github.com/dolthub/go-mysql-server@v0.18.0/enginetest/queries/json_scripts.go (about)

     1  // Copyright 2021 Dolthub, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package queries
    16  
    17  import (
    18  	querypb "github.com/dolthub/vitess/go/vt/proto/query"
    19  
    20  	"github.com/dolthub/go-mysql-server/sql"
    21  	"github.com/dolthub/go-mysql-server/sql/types"
    22  )
    23  
    24  var JsonScripts = []ScriptTest{
    25  	{
    26  		Name: "json_value",
    27  		SetUpScript: []string{
    28  			"CREATE TABLE xy (x bigint primary key, y JSON)",
    29  			`INSERT INTO xy VALUES (0, CAST('["a", "b"]' AS JSON)), (1, CAST('["a", "b", "c", "d"]' AS JSON));`,
    30  			`INSERT INTO xy VALUES (2, CAST('{"a": [{"b": 1}, {"c": 2}]}' AS JSON)), (3, CAST('{"a": {"b": ["c","d"]}}' AS JSON)), (4,NULL);`,
    31  		},
    32  		Assertions: []ScriptTestAssertion{
    33  			{
    34  				Query: `select json_value(y, '$.a', 'json') from xy`,
    35  				Expected: []sql.Row{
    36  					{nil},
    37  					{nil},
    38  					{types.MustJSON("[{\"b\": 1}, {\"c\": 2}]")},
    39  					{types.MustJSON("{\"b\": [\"c\",\"d\"]}")},
    40  					{nil},
    41  				},
    42  			},
    43  			{
    44  				Query: `select json_value(y, '$.a[0].b', 'signed') from xy where x = 2`,
    45  				Expected: []sql.Row{
    46  					{int64(1)},
    47  				},
    48  			},
    49  			{
    50  				Query: `select json_value(y, '$.a[0].b') from xy where x = 2`,
    51  				Expected: []sql.Row{
    52  					{"1"},
    53  				},
    54  			},
    55  			//{
    56  			//	Query: `select json_value(y, '$.a.b', 'signed') from xy where x = 2`,
    57  			//	Expected: []sql.Row{
    58  			//		{nil},
    59  			//	},
    60  			//},
    61  		},
    62  	},
    63  	{
    64  		Name: "json_length",
    65  		SetUpScript: []string{
    66  			"CREATE TABLE xy (x bigint primary key, y JSON)",
    67  			`INSERT INTO xy VALUES (0, CAST('["a", "b"]' AS JSON)), (1, CAST('["a", "b", "c", "d"]' AS JSON));`,
    68  			`INSERT INTO xy VALUES (2, CAST('{"a": [{"b": 1}, {"c": 2}]}' AS JSON)), (3, CAST('{"a": {"b": ["c","d"]}}' AS JSON)), (4,NULL);`,
    69  		},
    70  		Assertions: []ScriptTestAssertion{
    71  			{
    72  				Query: `select json_length(y) from xy`,
    73  				Expected: []sql.Row{
    74  					{2},
    75  					{4},
    76  					{1},
    77  					{1},
    78  					{nil},
    79  				},
    80  			},
    81  			{
    82  				Query:          `select json_length(json_extract(x, "$.a")) from xy`,
    83  				ExpectedErrStr: "failed to extract from expression 'xy.x'; object is not map",
    84  			},
    85  			{
    86  				Query: `select json_length(json_extract(y, "$.a")) from xy`,
    87  				Expected: []sql.Row{
    88  					{nil},
    89  					{nil},
    90  					{2},
    91  					{1},
    92  					{nil},
    93  				},
    94  			},
    95  			{
    96  				Query: `select json_length(json_extract(y, "$.a.b")) from xy where x = 3`,
    97  				Expected: []sql.Row{
    98  					{2},
    99  				},
   100  			},
   101  			{
   102  				Query: `select json_length(y, "$.a.b") from xy where x = 3`,
   103  				Expected: []sql.Row{
   104  					{2},
   105  				},
   106  			},
   107  			{
   108  				Query: `select json_length(y, "$.a[0].b") from xy where x = 2`,
   109  				Expected: []sql.Row{
   110  					{1},
   111  				},
   112  			},
   113  		},
   114  	},
   115  	{
   116  		// https://github.com/dolthub/go-mysql-server/issues/1855",
   117  		Name: "JSON_ARRAY properly handles CHAR bind vars",
   118  		SetUpScript: []string{
   119  			"CREATE TABLE `users` (`id` bigint unsigned AUTO_INCREMENT,`name` longtext,`languages` JSON, PRIMARY KEY (`id`))",
   120  			`INSERT INTO users (name, languages) VALUES ('Tom', CAST('["ZH", "EN"]' AS JSON));`,
   121  		},
   122  		Assertions: []ScriptTestAssertion{
   123  			{
   124  				Query: `SELECT * FROM users WHERE JSON_CONTAINS (languages, JSON_ARRAY(?)) ORDER BY users.id LIMIT 1`,
   125  				// CHAR bind vars are converted to VAR_BINARY on the wire path
   126  				Bindings: map[string]*querypb.BindVariable{
   127  					"v1": {Type: querypb.Type_VARBINARY, Value: []byte("ZH")},
   128  				},
   129  				Expected: []sql.Row{{uint64(1), "Tom", types.JSONDocument{Val: []interface{}{"ZH", "EN"}}}},
   130  			},
   131  		},
   132  	},
   133  	{
   134  		Name: "JSON_ARRAYAGG on one column",
   135  		SetUpScript: []string{
   136  			"create table t (o_id int primary key)",
   137  			"INSERT INTO t VALUES (1),(2)",
   138  		},
   139  		Assertions: []ScriptTestAssertion{
   140  			{
   141  				Query: "SELECT JSON_ARRAYAGG(o_id) FROM (SELECT * FROM t ORDER BY o_id) as sub",
   142  				Expected: []sql.Row{
   143  					{
   144  						types.MustJSON(`[1,2]`),
   145  					},
   146  				},
   147  			},
   148  		},
   149  	},
   150  	{
   151  		Name: "Simple JSON_ARRAYAGG on two columns",
   152  		SetUpScript: []string{
   153  			"create table t (o_id int primary key, attribute longtext)",
   154  			"INSERT INTO t VALUES (1, 'color'), (2, 'fabric')",
   155  		},
   156  		Assertions: []ScriptTestAssertion{
   157  			{
   158  				Query: "SELECT JSON_ARRAYAGG(o_id), JSON_ARRAYAGG(`attribute`) FROM (SELECT * FROM t ORDER BY o_id) as sub;",
   159  				Expected: []sql.Row{
   160  					{
   161  						types.MustJSON(`[1,2]`),
   162  						types.MustJSON(`["color","fabric"]`),
   163  					},
   164  				},
   165  			},
   166  		},
   167  	},
   168  	{
   169  		Name: "JSON_ARRAYAGG on column with string values w/ groupby",
   170  		SetUpScript: []string{
   171  			"create table t (o_id int primary key, c0 int, attribute longtext, value longtext)",
   172  			"INSERT INTO t VALUES (1, 2, 'color', 'red'), (2, 2, 'fabric', 'silk')",
   173  		},
   174  		Assertions: []ScriptTestAssertion{
   175  			{
   176  				Query: "SELECT c0, JSON_ARRAYAGG(`attribute`) FROM (SELECT * FROM t ORDER BY o_id) as sub GROUP BY c0",
   177  				Expected: []sql.Row{
   178  					{
   179  						2,
   180  						types.MustJSON(`["color","fabric"]`),
   181  					},
   182  				},
   183  			},
   184  			{
   185  				Query: "SELECT c0, JSON_ARRAYAGG(value) FROM (SELECT * FROM t ORDER BY o_id) as sub GROUP BY c0",
   186  				Expected: []sql.Row{
   187  					{
   188  						2,
   189  						types.MustJSON(`["red","silk"]`),
   190  					},
   191  				},
   192  			},
   193  		},
   194  	},
   195  	{
   196  		Name: "JSON_ARRAYAGG on column with int values w/ groupby",
   197  		SetUpScript: []string{
   198  			"create table t2 (o_id int primary key, val int)",
   199  			"INSERT INTO t2 VALUES (1,1), (2,1), (3,1)",
   200  		},
   201  		Assertions: []ScriptTestAssertion{
   202  			{
   203  				Query: "SELECT val, JSON_ARRAYAGG(o_id) FROM (SELECT * FROM t2 ORDER BY o_id) AS sub GROUP BY val",
   204  				Expected: []sql.Row{
   205  					{
   206  						1,
   207  						types.MustJSON(`[1,2,3]`),
   208  					},
   209  				},
   210  			},
   211  		},
   212  	},
   213  	{
   214  		Name: "JSON_ARRAYAGG on unknown column throws error",
   215  		SetUpScript: []string{
   216  			"create table t2 (o_id int primary key, val int)",
   217  			"INSERT INTO t2 VALUES (1,1), (2,2), (3,3)",
   218  		},
   219  		Assertions: []ScriptTestAssertion{
   220  			{
   221  				Query:       "SELECT o_id, JSON_ARRAYAGG(val2) FROM t2 GROUP BY o_id",
   222  				ExpectedErr: sql.ErrColumnNotFound,
   223  			},
   224  		},
   225  	},
   226  	{
   227  		Name: "JSON_ARRAYAGG on column with no rows returns NULL",
   228  		SetUpScript: []string{
   229  			"create table t2 (o_id int primary key)",
   230  		},
   231  		Assertions: []ScriptTestAssertion{
   232  			{
   233  				Query: "SELECT JSON_ARRAYAGG(o_id) FROM t2",
   234  				Expected: []sql.Row{
   235  					{
   236  						types.MustJSON(`[]`),
   237  					},
   238  				},
   239  			},
   240  		},
   241  	},
   242  	{
   243  		Name: "JSON_ARRAYAGG on row with 1 value, 1 null is fine",
   244  		SetUpScript: []string{
   245  			"create table x(pk int primary key, c1 int)",
   246  			"INSERT INTO x VALUES (1,NULL)",
   247  		},
   248  		Assertions: []ScriptTestAssertion{
   249  			{
   250  				Query: "SELECT pk, JSON_ARRAYAGG(c1) FROM x GROUP BY pk",
   251  				Expected: []sql.Row{
   252  					{1, types.MustJSON(`[null]`)},
   253  				},
   254  			},
   255  			{
   256  				Query: "SELECT JSON_ARRAYAGG(c1) FROM x",
   257  				Expected: []sql.Row{
   258  					{types.MustJSON(`[null]`)},
   259  				},
   260  			},
   261  		},
   262  	},
   263  	{
   264  		Name: "JSON_ARRAYAGG and group by use the same field.",
   265  		SetUpScript: []string{
   266  			"create table x(pk int primary key, c1 int)",
   267  			"INSERT INTO x VALUES (1, 1)",
   268  			"INSERT INTO x VALUES (2, 1)",
   269  			"INSERT INTO x VALUES (3, 3)",
   270  			"INSERT INTO x VALUES (4, 3)",
   271  			"INSERT INTO x VALUES (5, 5)",
   272  		},
   273  		Assertions: []ScriptTestAssertion{
   274  			{
   275  				Query: "SELECT JSON_ARRAYAGG(pk) FROM (SELECT * FROM x ORDER BY pk) as sub GROUP BY c1",
   276  				Expected: []sql.Row{
   277  					{types.MustJSON(`[1,2]`)},
   278  					{types.MustJSON(`[3,4]`)},
   279  					{types.MustJSON(`[5]`)},
   280  				},
   281  			},
   282  		},
   283  	},
   284  	{
   285  		Name: "JSON_ARRAGG with simple and nested json objects.",
   286  		SetUpScript: []string{
   287  			"create table j(pk int primary key, field JSON)",
   288  			`INSERT INTO j VALUES(1, '{"key1": {"key": "value"}}')`,
   289  			`INSERT INTO j VALUES(2, '{"key1": "value1", "key2": "value2"}')`,
   290  			`INSERT INTO j VALUES(3, '{"key1": {"key": [2,3]}}')`,
   291  			`INSERT INTO j VALUES(4, '["a", 1]')`,
   292  		},
   293  		Assertions: []ScriptTestAssertion{
   294  			{
   295  				Query: "SELECT pk, JSON_ARRAYAGG(field) FROM (SELECT * FROM j ORDER BY pk) as sub GROUP BY field ORDER BY pk",
   296  				Expected: []sql.Row{
   297  					{1, types.MustJSON(`[{"key1": {"key": "value"}}]`)},
   298  					{2, types.MustJSON(`[{"key1": "value1", "key2": "value2"}]`)},
   299  					{3, types.MustJSON(`[{"key1":{"key":[2,3]}}]`)},
   300  					{4, types.MustJSON(`[["a",1]]`)},
   301  				},
   302  			},
   303  		},
   304  	},
   305  	{
   306  		Name: "Simple JSON_OBJECTAGG with GROUP BY",
   307  		SetUpScript: []string{
   308  			"create table t2 (o_id int primary key, val int)",
   309  			"INSERT INTO t2 VALUES (1,1), (2,1), (3,1)",
   310  		},
   311  		Assertions: []ScriptTestAssertion{
   312  			{
   313  				Query: "SELECT JSON_OBJECTAGG(val, o_id) FROM (SELECT * FROM t2 ORDER BY o_id) as sub GROUP BY val",
   314  				Expected: []sql.Row{
   315  					{types.MustJSON(`{"1": 3}`)},
   316  				},
   317  			},
   318  		},
   319  	},
   320  	{
   321  		Name: "More complex JSON_OBJECTAGG WITH GROUP BY",
   322  		SetUpScript: []string{
   323  			"create table t (o_id int primary key, c0 int, attribute longtext, value longtext)",
   324  			"INSERT INTO t VALUES (1, 2, 'color', 'red'), (2, 2, 'fabric', 'silk')",
   325  			"INSERT INTO t VALUES (3, 3, 'color', 'green'), (4, 3, 'shape', 'square')",
   326  		},
   327  		Assertions: []ScriptTestAssertion{
   328  			{
   329  				Query: "SELECT c0, JSON_OBJECTAGG(`attribute`, value) FROM (SELECT * FROM t ORDER BY o_id) as sub GROUP BY c0",
   330  				Expected: []sql.Row{
   331  					{2, types.MustJSON(`{"color": "red", "fabric": "silk"}`)},
   332  					{3, types.MustJSON(`{"color": "green", "shape": "square"}`)},
   333  				},
   334  			},
   335  			{
   336  				Query: `SELECT c0, JSON_OBJECTAGG(c0, value) FROM (SELECT * FROM t ORDER BY o_id) as sub GROUP BY c0`,
   337  				Expected: []sql.Row{
   338  					{2, types.MustJSON(`{"2": "silk"}`)},
   339  					{3, types.MustJSON(`{"3": "square"}`)},
   340  				},
   341  			},
   342  		},
   343  	},
   344  	{
   345  		Name: "3 column table that uses JSON_OBJECTAGG without groupby",
   346  		SetUpScript: []string{
   347  			"create table t (o_id int primary key, c0 int, attribute longtext, value longtext)",
   348  			"INSERT INTO t VALUES (1, 2, 'color', 'red'), (2, 2, 'fabric', 'silk')",
   349  			"INSERT INTO t VALUES (3, 3, 'color', 'green'), (4, 3, 'shape', 'square')",
   350  		},
   351  		Assertions: []ScriptTestAssertion{
   352  			{
   353  				Query: `select JSON_OBJECTAGG(c0, value) from (SELECT * FROM t ORDER BY o_id) as sub`,
   354  				Expected: []sql.Row{
   355  					{types.MustJSON(`{"2": "silk", "3": "square"}`)},
   356  				},
   357  			},
   358  			{
   359  				Query: "select JSON_OBJECTAGG(`attribute`, value) from (SELECT * FROM t ORDER BY o_id) as sub",
   360  				Expected: []sql.Row{
   361  					{types.MustJSON(`{"color": "green", "fabric": "silk", "shape": "square"}`)},
   362  				},
   363  			},
   364  		},
   365  	},
   366  	{
   367  		Name: "JSON_OBJECTAGG and null values",
   368  		SetUpScript: []string{
   369  			`create table test (pk int primary key, val longtext)`,
   370  			`insert into test values (1, NULL)`,
   371  		},
   372  		Assertions: []ScriptTestAssertion{
   373  			{
   374  				Query: `SELECT JSON_OBJECTAGG(pk, val) from test`,
   375  				Expected: []sql.Row{
   376  					{types.MustJSON(`{"1": null}`)},
   377  				},
   378  			},
   379  		},
   380  	},
   381  	{
   382  		Name: "JSON_OBJECTAGG and nested json values",
   383  		SetUpScript: []string{
   384  			"create table j(pk int primary key, c0 int, val JSON)",
   385  			`INSERT INTO j VALUES(1, 1, '{"key1": "value1", "key2": "value2"}')`,
   386  			`INSERT INTO j VALUES(2, 1, '{"key1": {"key": [2,3]}}')`,
   387  			`INSERT INTO j VALUES(3, 2, '["a", 1]')`,
   388  		},
   389  		Assertions: []ScriptTestAssertion{
   390  			{
   391  				Query: `SELECT JSON_OBJECTAGG(c0, val) from (SELECT * FROM j ORDER BY pk) as sub`,
   392  				Expected: []sql.Row{
   393  					{types.MustJSON(`{"1": {"key1": {"key": [2, 3]}}, "2": ["a", 1]}`)},
   394  				},
   395  			},
   396  		},
   397  	},
   398  	{
   399  		Name: "JSON_OBJECTAGG correctly returns null when no rows are present",
   400  		SetUpScript: []string{
   401  			`create table test (pk int primary key, val longtext)`,
   402  		},
   403  		Assertions: []ScriptTestAssertion{
   404  			{
   405  				Query: `SELECT JSON_OBJECTAGG(pk, val) from test`,
   406  				Expected: []sql.Row{
   407  					{nil},
   408  				},
   409  			},
   410  		},
   411  	},
   412  	{
   413  		Name: "JSON_OBJECTAGG handles errors appropriately",
   414  		SetUpScript: []string{
   415  			`create table test (pk int primary key, c0 int, val longtext)`,
   416  			`insert into test values (1, 1, NULL)`,
   417  			`insert into test values (2, NULL, 1)`, // NULL keys are not allowed in JSON_OBJECTAGG
   418  		},
   419  		Assertions: []ScriptTestAssertion{
   420  			{
   421  				Query:       `SELECT JSON_OBJECTAGG(c0, notval) from test`,
   422  				ExpectedErr: sql.ErrColumnNotFound,
   423  			},
   424  			{
   425  				Query:       `SELECT JSON_OBJECTAGG(notpk, val) from test`,
   426  				ExpectedErr: sql.ErrColumnNotFound,
   427  			},
   428  			{
   429  				Query:       `SELECT JSON_OBJECTAGG(c0, val) from nottest`,
   430  				ExpectedErr: sql.ErrTableNotFound,
   431  			},
   432  			{
   433  				Query:       `SELECT JSON_OBJECTAGG(c0, val, 'badarg') from test`,
   434  				ExpectedErr: sql.ErrInvalidArgumentNumber,
   435  			},
   436  			{
   437  				Query:       `SELECT JSON_OBJECTAGG(c0, val, badarg) from test`,
   438  				ExpectedErr: sql.ErrColumnNotFound,
   439  			},
   440  			{
   441  				Query:       `SELECT JSON_OBJECTAGG(c0) from test`,
   442  				ExpectedErr: sql.ErrInvalidArgumentNumber,
   443  			},
   444  			{
   445  				Query:       `SELECT JSON_OBJECTAGG(c0, val) from test`,
   446  				ExpectedErr: sql.ErrJSONObjectAggNullKey,
   447  			},
   448  		},
   449  	},
   450  	{
   451  		Name: "JSON -> and ->> operator support",
   452  		SetUpScript: []string{
   453  			"create table t (pk int primary key, col1 JSON, col2 JSON);",
   454  			`insert into t values (1, JSON_OBJECT('key1', 1, 'key2', '"abc"'), JSON_ARRAY(3,10,5,17,"z"));`,
   455  			`insert into t values (2, JSON_OBJECT('key1', 100, 'key2', '"ghi"'), JSON_ARRAY(3,10,5,17,JSON_ARRAY(22,"y",66)));`,
   456  			`CREATE TABLE t2 (i INT PRIMARY KEY, j JSON);`,
   457  			`INSERT INTO t2 VALUES (0, '{"a": "123", "outer": {"inner": 456}}');`,
   458  		},
   459  		Assertions: []ScriptTestAssertion{
   460  			{
   461  				Query:    `select col1->'$.key1' from t;`,
   462  				Expected: []sql.Row{{types.MustJSON("1")}, {types.MustJSON("100")}},
   463  			},
   464  			{
   465  				Query:    `select col1->>'$.key2' from t;`,
   466  				Expected: []sql.Row{{"abc"}, {"ghi"}},
   467  			},
   468  			{
   469  				Query:    `select pk, col1 from t where col1->'$.key1' = 1;`,
   470  				Expected: []sql.Row{{1, types.MustJSON(`{"key1":1, "key2":"\"abc\""}`)}},
   471  			},
   472  			{
   473  				Query:    `select pk, col1 from t where col1->>'$.key2' = 'abc';`,
   474  				Expected: []sql.Row{{1, types.MustJSON(`{"key1":1, "key2":"\"abc\""}`)}},
   475  			},
   476  			{
   477  				Query:    `select * from t where col1->>'$.key2' = 'def';`,
   478  				Expected: []sql.Row{},
   479  			},
   480  			{
   481  				Query:    `SELECT col2->"$[3]", col2->>"$[3]" FROM t;`,
   482  				Expected: []sql.Row{{types.MustJSON("17"), "17"}, {types.MustJSON("17"), "17"}},
   483  			},
   484  			{
   485  				Query:    `SELECT col2->"$[4]", col2->>"$[4]" FROM t where pk=1;`,
   486  				Expected: []sql.Row{{types.MustJSON("\"z\""), "z"}},
   487  			},
   488  			{
   489  				// TODO: JSON_Extract doesn't seem able to handle a JSON path expression that references a nested array
   490  				//       This errors with "object is not Slice"
   491  				Skip:     true,
   492  				Query:    `SELECT col2->>"$[3]", col2->>"$[4][0]" FROM t;`,
   493  				Expected: []sql.Row{{17, 44}, {17, "y"}},
   494  			},
   495  			{
   496  				Query:    `SELECT k->"$.inner" from (SELECT j->"$.outer" AS k FROM t2) sq;`,
   497  				Expected: []sql.Row{{types.MustJSON("456")}},
   498  			},
   499  		},
   500  	},
   501  	// from https://dev.mysql.com/doc/refman/8.0/en/json.html#json-converting-between-types:~:text=information%20and%20examples.-,Comparison%20and%20Ordering%20of%20JSON%20Values,-JSON%20values%20can
   502  	{
   503  		Name: "json is ordered correctly",
   504  		SetUpScript: []string{
   505  			"create table t (pk int primary key, col1 json);",
   506  			"insert into t values (1, null);",
   507  			"insert into t values (2, '{}');",
   508  			"insert into t values (3, (select json_extract('{\"a\": null}', '$.a')));",
   509  			"insert into t values (4, 0);",
   510  		},
   511  		Assertions: []ScriptTestAssertion{
   512  			{
   513  				Query: "select * from t order by col1 asc;",
   514  				Expected: []sql.Row{
   515  					{1, nil},
   516  					{3, types.MustJSON("null")},
   517  					{4, types.MustJSON("0")},
   518  					{2, types.MustJSON("{}")},
   519  				},
   520  			},
   521  			{
   522  				Query: "select * from t order by col1 desc;",
   523  				Expected: []sql.Row{
   524  					{2, types.MustJSON("{}")},
   525  					{4, types.MustJSON("0")},
   526  					{3, types.MustJSON("null")},
   527  					{1, nil},
   528  				},
   529  			},
   530  		},
   531  	},
   532  	{
   533  		// https://github.com/dolthub/dolt/issues/4499
   534  		Name: "json is formatted correctly",
   535  		SetUpScript: []string{
   536  			"create table t (pk int primary key, col1 json);",
   537  
   538  			// formatted json
   539  			`insert into t values (1, '{"a": 1, "b": 2}');`,
   540  			// unordered keys with correct spacing
   541  			`insert into t values (2, '{"b": 2, "a": 1}');`,
   542  			// ordered keys with no spacing
   543  			`insert into t values (3, '{"a":1,"b":2}');`,
   544  			// unordered keys with no spacing
   545  			`insert into t values (4, '{"b":2,"a":1}');`,
   546  			// unordered keys with extra spacing
   547  			`insert into t values (5, '{ "b": 2 , "a" : 1 }');`,
   548  
   549  			// ordered keys with arrays of primitives without spaces
   550  			`insert into t values (6, '{"a":[1,2,3],"b":[4,5,6]}');`,
   551  			// unordered keys with arrays of primitives without spaces
   552  			`insert into t values (7, '{"b":[4,5,6],"a":[1,2,3]}');`,
   553  			// ordered keys with arrays of primitives with extra spaces
   554  			`insert into t values (8, '{ "a" : [ 1 , 2 , 3 ] , "b" : [ 4 , 5 , 6 ] }');`,
   555  			// unordered keys with arrays of primitives with extra spaces
   556  			`insert into t values (9, '{ "b" : [ 4 , 5 , 6 ] , "a" : [ 1 , 2 , 3 ] }');`,
   557  
   558  			// ordered keys with arrays of objects without spaces
   559  			`insert into t values (10, '{"a":[{"a":1},{"b":2}],"b":[{"c":3},{"d":4}]}');`,
   560  			// ordered keys with arrays of objects with extra spaces
   561  			`insert into t values (11, '{ "a" : [ { "a" : 1 } , { "b" : 2 } ] , "b" : [ { "c" : 3 } , { "d" : 4 } ] }');`,
   562  			// unordered keys with arrays of objects without spaces
   563  			`insert into t values (12, '{"b":[{"c":3},{"d":4}],"a":[{"a":1},{"b":2}]}');`,
   564  			// unordered keys with arrays of objects with extra spaces
   565  			`insert into t values (13, '{ "b" : [ { "c" : 3 } , { "d" : 4 } ] , "a" : [ { "a" : 1 } , { "b" : 2 } ] }');`,
   566  
   567  			// formatted json with special characters
   568  			`insert into t values (14, '[{"a":"<>&"}]');`,
   569  		},
   570  		Assertions: []ScriptTestAssertion{
   571  			{
   572  				Query: "select pk, cast(col1 as char) from t order by pk asc;",
   573  				Expected: []sql.Row{
   574  					{1, `{"a": 1, "b": 2}`},
   575  					{2, `{"a": 1, "b": 2}`},
   576  					{3, `{"a": 1, "b": 2}`},
   577  					{4, `{"a": 1, "b": 2}`},
   578  					{5, `{"a": 1, "b": 2}`},
   579  					{6, `{"a": [1, 2, 3], "b": [4, 5, 6]}`},
   580  					{7, `{"a": [1, 2, 3], "b": [4, 5, 6]}`},
   581  					{8, `{"a": [1, 2, 3], "b": [4, 5, 6]}`},
   582  					{9, `{"a": [1, 2, 3], "b": [4, 5, 6]}`},
   583  					{10, `{"a": [{"a": 1}, {"b": 2}], "b": [{"c": 3}, {"d": 4}]}`},
   584  					{11, `{"a": [{"a": 1}, {"b": 2}], "b": [{"c": 3}, {"d": 4}]}`},
   585  					{12, `{"a": [{"a": 1}, {"b": 2}], "b": [{"c": 3}, {"d": 4}]}`},
   586  					{13, `{"a": [{"a": 1}, {"b": 2}], "b": [{"c": 3}, {"d": 4}]}`},
   587  					{14, `[{"a": "<>&"}]`},
   588  				},
   589  			},
   590  		},
   591  	},
   592  	{
   593  		Name: "json_extract returns missing keys as sql null and handles json null literals correctly",
   594  		SetUpScript: []string{
   595  			"create table t (pk int primary key, col1 json);",
   596  			"insert into t values (1, '{\"items\": {\"1\": 1, \"2\": 2}}');",
   597  			"insert into t values (2, null);",
   598  			"insert into t values (3, '{}');",
   599  			"insert into t values (4, '{\"items\": null}');",
   600  			"insert into t values (5, (select json_extract('{\"a\": null}', '$.a')));",
   601  		},
   602  		Assertions: []ScriptTestAssertion{
   603  			{
   604  				Query: "select pk, json_extract(col1, '$.items') from t order by pk;",
   605  				Expected: []sql.Row{
   606  					{1, types.MustJSON("{\"1\":1,\"2\":2}")},
   607  					{2, nil},
   608  					{3, nil},
   609  					{4, types.MustJSON("null")},
   610  					{5, nil},
   611  				},
   612  			},
   613  			{
   614  				Query: "select pk, json_extract(col1, '$') from t order by pk;",
   615  				Expected: []sql.Row{
   616  					{1, types.MustJSON("{\"items\": {\"1\": 1, \"2\": 2}}")},
   617  					{2, nil},
   618  					{3, types.MustJSON("{}")},
   619  					{4, types.MustJSON("{\"items\": null}")},
   620  					{5, types.MustJSON("null")},
   621  				},
   622  			},
   623  			{
   624  				Query: "select pk, json_extract(col1, '$.items') is null from t order by pk;",
   625  				Expected: []sql.Row{
   626  					{1, false},
   627  					{2, true},
   628  					{3, true},
   629  					{4, false},
   630  					{5, true},
   631  				},
   632  			},
   633  			{
   634  				Query: "select pk, json_extract(col1, '$.items') <> null from t order by pk;",
   635  				Expected: []sql.Row{
   636  					{1, nil},
   637  					{2, nil},
   638  					{3, nil},
   639  					{4, nil},
   640  					{5, nil},
   641  				},
   642  			},
   643  			{
   644  				Query: "select pk, json_extract(col1, '$.items.*') from t order by pk;",
   645  				Expected: []sql.Row{
   646  					{1, types.MustJSON("[1, 2]")},
   647  					{2, nil},
   648  					{3, nil},
   649  					{4, types.MustJSON("null")},
   650  					{5, nil},
   651  				},
   652  			},
   653  			{
   654  				Query:    "select pk from t where json_extract(col1, '$.items') is null;",
   655  				Expected: []sql.Row{{2}, {3}, {5}},
   656  			},
   657  			{
   658  				Query:    "select pk from t where json_extract(col1, '$.items') <> null;",
   659  				Expected: []sql.Row{},
   660  			},
   661  		},
   662  	},
   663  	{
   664  		Name: "json_contains_path returns true if the path exists",
   665  		SetUpScript: []string{
   666  			`create table t (pk int primary key, col1 json);`,
   667  			`insert into t values (1, '{"a": 1}');`,
   668  			`insert into t values (2, '{"a": 1, "b": 2, "c": {"d": 4}}');`,
   669  			`insert into t values (3, '{"w": 1, "x": 2, "c": {"d": 4}}');`,
   670  			`insert into t values (4, '{}');`,
   671  			`insert into t values (5, null);`,
   672  		},
   673  
   674  		Assertions: []ScriptTestAssertion{
   675  			{
   676  				Query: "select pk, json_contains_path(col1, 'one', '$.a') from t order by pk;",
   677  				Expected: []sql.Row{
   678  					{1, true},
   679  					{2, true},
   680  					{3, false},
   681  					{4, false},
   682  					{5, nil},
   683  				},
   684  			},
   685  			{
   686  				Query: "select pk, json_contains_path(col1, 'one', '$.a', '$.x', '$.c.d') from t order by pk;",
   687  				Expected: []sql.Row{
   688  					{1, true},
   689  					{2, true},
   690  					{3, true},
   691  					{4, false},
   692  					{5, nil},
   693  				},
   694  			},
   695  			{
   696  				Query: "select pk, json_contains_path(col1, 'all', '$.a', '$.x') from t order by pk;",
   697  				Expected: []sql.Row{
   698  					{1, false},
   699  					{2, false},
   700  					{3, false},
   701  					{4, false},
   702  					{5, nil},
   703  				},
   704  			},
   705  			{
   706  				Query: "select pk, json_contains_path(col1, 'all', '$.c.d', '$.x') from t order by pk;",
   707  				Expected: []sql.Row{
   708  					{1, false},
   709  					{2, false},
   710  					{3, true},
   711  					{4, false},
   712  					{5, nil},
   713  				},
   714  			},
   715  			{
   716  				Query:          "select pk, json_contains_path(col1, 'other', '$.c.d', '$.x') from t order by pk;",
   717  				ExpectedErrStr: "The oneOrAll argument to json_contains_path may take these values: 'one' or 'all'",
   718  			},
   719  		},
   720  	},
   721  	{
   722  		Name: "json type value compared with number type value",
   723  		Assertions: []ScriptTestAssertion{
   724  			{
   725  				Query:    "SELECT JSON_EXTRACT('0.4', '$')",
   726  				Expected: []sql.Row{{types.MustJSON(`0.4`)}},
   727  			},
   728  			{
   729  				Query:    "SELECT JSON_EXTRACT('0.4', '$') > 0;",
   730  				Expected: []sql.Row{{true}},
   731  			},
   732  			{
   733  				Query:    "SELECT JSON_EXTRACT('0.4', '$') <= 0;",
   734  				Expected: []sql.Row{{false}},
   735  			}, {
   736  				Query:    "SELECT JSON_EXTRACT('0.4', '$') = 0;",
   737  				Expected: []sql.Row{{false}},
   738  			},
   739  			{
   740  				Query:    "SELECT JSON_EXTRACT('0.4', '$') = 0.4;",
   741  				Expected: []sql.Row{{true}},
   742  			},
   743  		},
   744  	},
   745  	{
   746  		Name: "json bools",
   747  		SetUpScript: []string{
   748  			"create table t (j json);",
   749  			"insert into t values ('{\"x\": true}'), ('{\"x\": false}');",
   750  		},
   751  		Assertions: []ScriptTestAssertion{
   752  			{
   753  				Query: "select j->'$.x' = true from t;",
   754  				Expected: []sql.Row{
   755  					{true},
   756  					{false},
   757  				},
   758  			},
   759  		},
   760  	},
   761  }