--echo # Test of the JSON functions used in SQL statements --echo # ---------------------------------------------------------------------- SET NAMES utf8; CREATE TABLE t1 (pk int NOT NULL PRIMARY KEY AUTO_INCREMENT, i INT, j JSON); SHOW CREATE TABLE t1; INSERT INTO t1(i, j) VALUES (0, NULL); INSERT INTO t1(i, j) VALUES (1, '{"a": 2}'); INSERT INTO t1(i, j) VALUES (2, '[1,2]'); INSERT INTO t1(i, j) VALUES (3, '{"a":"b", "c":"d","ab":"abc", "bc": ["x", "y"]}'); INSERT INTO t1(i, j) VALUES (4, '["here", ["I", "am"], "!!!"]'); INSERT INTO t1(i, j) VALUES (5, '"scalar string"'); INSERT INTO t1(i, j) VALUES (6, 'true'); INSERT INTO t1(i, j) VALUES (7, 'false'); INSERT INTO t1(i, j) VALUES (8, 'null'); INSERT INTO t1(i, j) VALUES (9, '-1'); INSERT INTO t1(i, j) VALUES (10, CAST(CAST(1 AS UNSIGNED) AS JSON)); INSERT INTO t1(i, j) VALUES (11, '32767'); INSERT INTO t1(i, j) VALUES (12, '32768'); INSERT INTO t1(i, j) VALUES (13, '-32768'); INSERT INTO t1(i, j) VALUES (14, '-32769'); INSERT INTO t1(i, j) VALUES (15, '2147483647'); INSERT INTO t1(i, j) VALUES (16, '2147483648'); INSERT INTO t1(i, j) VALUES (17, '-2147483648'); INSERT INTO t1(i, j) VALUES (18, '-2147483649'); INSERT INTO t1(i, j) VALUES (19, '18446744073709551615'); INSERT INTO t1(i, j) VALUES (20, '18446744073709551616'); INSERT INTO t1(i, j) VALUES (21, '3.14'); INSERT INTO t1(i, j) VALUES (22, '{}'); INSERT INTO t1(i, j) VALUES (23, '[]'); INSERT INTO t1(i, j) VALUES (24, CAST(CAST('2015-01-15 23:24:25' AS DATETIME) AS JSON)); INSERT INTO t1(i, j) VALUES (25, CAST(CAST('23:24:25' AS TIME) AS JSON)); INSERT INTO t1(i, j) VALUES (26, CAST(CAST('2015-01-15' AS DATE) AS JSON)); INSERT INTO t1(i, j) VALUES (27, CAST(TIMESTAMP'2015-01-15 23:24:25' AS JSON)); INSERT INTO t1(i, j) VALUES (28, CAST(ST_GeomFromText('POINT(1 1)') AS JSON)); # auto-convert to utf8mb4 INSERT INTO t1(i, j) VALUES (29, CAST('[]' AS CHAR CHARACTER SET 'ascii')); INSERT INTO t1(i, j) VALUES (30, CAST(x'cafe' AS JSON)); INSERT INTO t1(i, j) VALUES (31, CAST(x'cafebabe' AS JSON)); --echo # --echo # Test of JSON comparator. --echo # SELECT i, (j = '"scalar string"') AS c1, (j = 'scalar string') AS c2, (j = CAST('"scalar string"' AS JSON)) AS c3, (j = CAST(CAST(j AS CHAR CHARACTER SET 'utf8mb4') AS JSON)) AS c4, (j = CAST(NULL AS JSON)) AS c5, (j = NULL) AS c6, (j <=> NULL) AS c7, (j <=> CAST(NULL AS JSON)) AS c8, (j IN (-1, 2, 32768, 3.14)) AS c9, (j IN (CAST('[1, 2]' AS JSON), CAST('{}' AS JSON), CAST(3.14 AS JSON))) AS c10, (j = (SELECT j FROM t1 WHERE j = CAST('null' AS JSON))) AS c11, (j = (SELECT j FROM t1 WHERE j IS NULL)) AS c12, (j = (SELECT j FROM t1 WHERE 1<>1)) AS c13, (j = DATE'2015-01-15') AS c14, (j = TIME'23:24:25') AS c15, (j = TIMESTAMP'2015-01-15 23:24:25') AS c16, (j = CURRENT_TIMESTAMP) AS c17, (j = ST_GeomFromText('POINT(1 1)')) AS c18, (JSON_EXTRACT(j, '$.a') = 2) AS c19 FROM t1 ORDER BY i; SELECT i FROM t1 WHERE j = CAST(CAST(j AS CHAR CHARACTER SET 'utf8') AS JSON) ORDER BY i; SELECT CAST(NULL AS UNSIGNED) = CAST(NULL AS JSON); SELECT CAST(NULL AS JSON) = CAST(NULL AS JSON); SELECT CAST(NULL AS JSON) = NULL; SELECT CAST(1 AS JSON) = NULL; SELECT CAST('true' AS JSON) = 1; SELECT CAST('true' AS JSON) = true; SELECT a.i, b.i, a.j < b.j, a.j = b.j, a.j > b.j, a.j <> b.j, a.j <=> b.j FROM t1 a, t1 b ORDER BY a.i, b.i; --echo # Verify that the index on the int column is not used when --echo # comparing the int column to a JSON column. The two columns --echo # should be compared using the JSON comparator. CREATE TABLE t2(i int, j json); CREATE INDEX t2_i ON t2(i); INSERT INTO t2 values (1, CAST(1 AS JSON)); INSERT INTO t2 values (1, CAST('"1"' AS JSON)); ANALYZE TABLE t2; let $query=SELECT * FROM t2 where i = j; eval EXPLAIN $query; eval $query; DROP TABLE t2; # Create a table full of JSON numeric scalars to verify that the JSON # comparator returns the expected result when comparing all # combinations of those values. # # The values should be inserted in ascending order. The table has a # rank column that tells how the comparator is expected to order the # JSON values. If two rows have the same rank, the comparator is # expected to say that the JSON values on the two rows are equal. If a # row has a lower rank than another, the JSON value in that row is # expected to be smaller than the JSON value in the other row. CREATE TABLE numbers(id INT NOT NULL AUTO_INCREMENT, rank INT, j JSON, PRIMARY KEY(id)); INSERT INTO numbers(rank, j) VALUES (1, '-1e100'), (2, '-1e65'), # smallest DECIMAL (negative with 65 digits) (3, CAST(-99999999999999999999999999999999999999999999999999999999999999999 AS JSON)), (4, CAST(-9223372036854776001 AS JSON)), (5, CAST(-9223372036854776000 AS JSON)), # closest DOUBLE approximation of the smallest SIGNED BIGINT (5 /* same rank as previous */, '-9.223372036854776e18'), (6, CAST(-9223372036854775999 AS JSON)), (7, CAST(-9223372036854775809 AS JSON)), # smallest SIGNED BIGINT - 1 (8, CAST(-9223372036854775808 AS JSON)), # smallest SIGNED BIGINT (9, CAST(-9223372036854775807 AS JSON)), # smallest SIGNED BIGINT + 1 (10, '-1e-50'), # close to zero, fits in a DECIMAL (11, '-1.2345678901234e-71'), # has to be truncated to fit in a DECIMAL (12, CAST(-0.000000000000000000000000000000000000000000000000000000000000000000000012 AS JSON)), (12 /* same rank as previous */, '-1.2e-71'), (13, '-1.0345678901234e-71'), # has to be truncated to fit in a DECIMAL (14, '-1e-100'), # too close to zero to fit in a DECIMAL (15, '0'), (15 /* same rank as previous */, '0.0'), (15 /* same rank as previous */, '-0.0'), (15 /* same rank as previous */, CAST(0.0 AS JSON)), (15 /* same rank as previous */, CAST(CAST(-0.0e0 AS DECIMAL) AS JSON)), (16, '1e-100'), # too close to zero to fit in a DECIMAL (17, '1.0345678901234e-71'), # has to be truncated to fit in a DECIMAL (18, CAST(0.000000000000000000000000000000000000000000000000000000000000000000000012 AS JSON)), (18 /* same rank as previous */, '1.2e-71'), (19, '1.2345678901234e-71'), # has to be truncated to fit in a DECIMAL (20, '1e-50'), # close to zero, fits in a DECIMAL (21, CAST(9223372036854775806 AS JSON)), # largest SIGNED BIGINT - 1 (22, CAST(9223372036854775807 AS JSON)), # largest SIGNED BIGINT (23, CAST(9223372036854775808 AS JSON)), # largest SIGNED BIGINT + 1 (24, CAST(9223372036854775999 AS JSON)), # closest DOUBLE approximation of the largest SIGNED BIGINT (25, '9.223372036854776e18'), (25 /* same rank as previous */, CAST(9223372036854776000 AS JSON)), (26, CAST(9223372036854776001 AS JSON)), (27, CAST(18446744073709551614 AS JSON)), # largest UNSIGNED BIGINT - 1 (28, CAST(18446744073709551615 AS JSON)), # largest UNSIGNED BIGINT (29, CAST(18446744073709551616 AS JSON)), # largest UNSIGNED BIGINT + 1 # Gets converted to the closest DOUBLE approximation of UNSIGNED BIGINT + 1 # by the JSON parser (30, '18446744073709551616'), # biggest DECIMAL (65 digits) (31, CAST(99999999999999999999999999999999999999999999999999999999999999999 AS JSON)), (32, CAST('1e65' AS JSON)), (33, CAST('1e100' AS JSON)); SELECT *, JSON_TYPE(j) FROM numbers ORDER BY id; # Now compare every combination of scalars in the table using <, =, >, # <> and <=>, and cross-check the results against the ranks. The query # returns the rows where the comparison returned an unexpected result. # If all is well, the query returns no rows. SELECT a.j, b.j, a.j < b.j, a.j = b.j, a.j > b.j, a.j <=> b.j FROM numbers a, numbers b WHERE ((a.j < b.j) <> (a.rank < b.rank)) OR ((a.j = b.j) <> (a.rank = b.rank)) OR ((a.j > b.j) <> (a.rank > b.rank)) OR ((a.j <=> b.j) <> (a.rank <=> b.rank)); DROP TABLE numbers; # Verify handling of errors during evaluation of the arguments to the # comparator, both in the left argument and in the right argument. CREATE TABLE t(txt TEXT); INSERT INTO t VALUES (''); --error ER_INVALID_JSON_TEXT_IN_PARAM SELECT COUNT(*) FROM t WHERE JSON_EXTRACT(txt, '$') = 5; --error ER_INVALID_JSON_TEXT_IN_PARAM SELECT COUNT(*) FROM t WHERE 5 = JSON_EXTRACT(txt, '$'); DROP TABLE t; --echo # --echo # WL#8539 - Ordering of scalar JSON values --echo # # Create some timestamps. CREATE TABLE timestamps (ts TIMESTAMP(6)); INSERT INTO timestamps VALUES ('2000-01-01 00:00:00'), ('2000-01-01 00:00:00.01'), ('2000-01-01 00:00:00.001'), ('2000-01-01 00:00:00.002'), ('2000-01-01 00:00:00.02'), ('2000-01-01 23:59:59.999999'), ('2000-01-02 00:00:00'), ('2000-02-01 00:00:00'), ('2010-12-02 01:00:00'), ('2010-12-02 01:02:00'), ('2010-12-02 01:02:03'), ('2010-12-02 02:01:00'), ('1970-01-02 00:00:01'), ('1970-01-02 00:00:01.000001'); SELECT * FROM timestamps ORDER BY CAST(ts AS JSON); # Create datetimes that correspond to the above timestamps, and add some values # that are outside the accepted range of the timestamp data type. CREATE TABLE datetimes (dt DATETIME(6)); INSERT INTO datetimes SELECT ts FROM timestamps; INSERT INTO datetimes VALUES ('1960-01-02 03:04:05'), ('1960-01-02 03:04:06'), ('1000-01-01 00:00:00'), ('9999-12-31 23:59:59.999999'); SELECT * FROM datetimes ORDER BY CAST(dt AS JSON); # Create some times using the time component of the above datetimes. Also add # some times that go outside of the 0-24 range of the time component of # datetime. CREATE TABLE times (t TIME(6)); INSERT INTO times SELECT DISTINCT TIME(dt) FROM datetimes; INSERT INTO times VALUES ('-838:59:59'), ('838:59:59'), ('-00:00:00.000001'), ('-00:00:00'), ('24:00:00'), ('-12:00:00'), ('-24:00:00'); SELECT * FROM times ORDER BY CAST(t AS JSON); # Create dates using the date component of the above datetimes. CREATE TABLE dates(d DATE); INSERT INTO dates SELECT DISTINCT DATE(dt) FROM datetimes; # Create some signed integers. CREATE TABLE signed_integers(i BIGINT); INSERT INTO signed_integers VALUES (0), (1), (2), (3), (4), (5), (10), (11), (12), (20), (21), (22), (99), (100), (101), (999), (1000), (1001), (9223372036854775806), (9223372036854775807); INSERT INTO signed_integers SELECT -i FROM signed_integers; INSERT INTO signed_integers VALUES (-9223372036854775808); SELECT * FROM signed_integers ORDER BY CAST(i AS JSON); # Create some unsigned integers. CREATE TABLE unsigned_integers(i BIGINT UNSIGNED); INSERT INTO unsigned_integers SELECT i FROM signed_integers where i >= 0; INSERT INTO unsigned_integers VALUES (9223372036854775808), (18446744073709551614), (18446744073709551615); SELECT * FROM unsigned_integers ORDER BY CAST(i AS JSON); # Create some decimals. CREATE TABLE decimals (d DECIMAL(25,3)); INSERT INTO decimals SELECT i FROM signed_integers; INSERT INTO decimals SELECT i FROM unsigned_integers; INSERT INTO decimals VALUES (9223372036854776000), (-9223372036854776000), (9223372036854776001), (-9223372036854776001), (3.13), (3.14), (3.15), (-3.13), (-3.14), (-3.15), (3.131), (3.141), (3.151), (-3.131), (-3.141), (-3.151), (3.129), (3.139), (3.149), (-3.129), (-3.139), (-3.149), (0.1), (0.01), (0.001), (-0.1), (-0.01), (-0.001); SELECT * FROM decimals ORDER BY CAST(d AS JSON); # Create some doubles. CREATE TABLE doubles (d DOUBLE); INSERT INTO doubles SELECT d FROM decimals; INSERT INTO doubles VALUES (1.5E-200), (1.5E200), (-1.5E-200), (-1.5E200), (-1E-323), (-1E-322), (-1E-321), (1E-323), (1E-322), (1E-321), (-1E308), (-1E307), (-1E306), (1E308), (1E307), (1E306); SELECT * FROM doubles ORDER BY CAST(d AS JSON); # Now convert all of the above values to JSON. CREATE TABLE t(id INT PRIMARY KEY AUTO_INCREMENT, j JSON); INSERT INTO t(j) SELECT CAST(ts AS JSON) FROM timestamps ORDER BY ts; INSERT INTO t(j) SELECT CAST(dt AS JSON) FROM datetimes ORDER BY dt; INSERT INTO t(j) SELECT CAST(t AS JSON) FROM times ORDER BY t; INSERT INTO t(j) SELECT CAST(d AS JSON) FROM dates ORDER BY d; INSERT INTO t(j) SELECT CAST(i AS JSON) FROM signed_integers ORDER BY i; INSERT INTO t(j) SELECT CAST(i AS JSON) FROM unsigned_integers ORDER BY i; INSERT INTO t(j) SELECT CAST(d AS JSON) FROM decimals ORDER BY d; INSERT INTO t(j) SELECT CAST(d AS JSON) FROM doubles ORDER BY d; # Insert some more JSON values. INSERT INTO t(j) VALUES (NULL), (NULL), ('true'), ('false'), ('null'), ('"abc"'), ('""'), ('"abcd"'), ('"bc"'), ('"abc\\u0000\\u0000"'), ('"abc\\u0000"'), ('0.0'), ('-0.0'), ('9223372036854776000'), ('1.0e-1'), ('1.0e-2'), (CAST(0.000000000000001 AS JSON)), (CAST(0.00000000000000115 AS JSON)), (CAST(0.0000000000000001 AS JSON)), (CAST(0.000000000000000116 AS JSON)), (CAST(0.0 AS JSON)), (CAST(-999999999999999999999999999999999999999999999999999999999999999999999999999999999 AS JSON)), (CAST(-999999999999999999999999999999999999999999999999999999999999999999999999999999998 AS JSON)), (CAST(-999999999999999999999999999999999999999999999999999999999999999999999999999999997 AS JSON)), (CAST(999999999999999999999999999999999999999999999999999999999999999999999999999999997 AS JSON)), (CAST(999999999999999999999999999999999999999999999999999999999999999999999999999999998 AS JSON)), (CAST(999999999999999999999999999999999999999999999999999999999999999999999999999999999 AS JSON)), (CAST(-1E81 AS JSON)), (CAST(-9.99E80 AS JSON)), (CAST(9.99E80 AS JSON)), (CAST(1E81 AS JSON)), (JSON_ARRAY('an array')), (JSON_ARRAY('another array')), (JSON_OBJECT('an', 'object')), (JSON_OBJECT('another', 'object')), (CAST(ST_GeomFromText('POINT(0 0)') AS JSON)), (CAST(ST_GeomFromText('POINT(0 1)') AS JSON)), (CAST(CAST('1234abcd' AS BINARY) AS JSON)); # Now order the table on the JSON column. SELECT j, JSON_TYPE(j) AS tp FROM t ORDER BY j, id; SELECT j, JSON_TYPE(j) AS tp FROM t ORDER BY j DESC, id; # Ordering on a JSON expression should give the same result. SELECT JSON_EXTRACT(j, '$') AS je, JSON_TYPE(j) AS tp FROM t ORDER BY je, id; # Set max_sort_length as small as possible and order again. Since we'll now just # look at a prefix of the values, distinct values with a common prefix may order # as equal. SET @@max_sort_length=4; SELECT j, JSON_TYPE(j) AS tp FROM t ORDER BY j, id; SET @@max_sort_length=default; # GROUP BY uses a temporary for grouping, GROUP BY WITH ROLLUP uses filesort to # do the grouping. ANALYZE TABLE t; EXPLAIN SELECT j, COUNT(*) FROM t GROUP BY j ORDER BY j; EXPLAIN SELECT j, COUNT(*) FROM t GROUP BY j WITH ROLLUP; # Grouping produces indeterminate results based on the order of evaluation. For example, # either '2' or '2.0' could be the name of the group # either '20' or '20.000' could be the name of the group # either '-0.1' or '-0.100' could be the name of the group # either '9223372036854775807.000' or '9223372036854775807' could be the name of the group # These issues are resolved by removing trailing zeros from decimals and replacing -0 with 0. # This also replaces .000010 with 010 and .000001 with 001 [because \s is not supported]. --replace_regex /1\.0e\-1/0.1/ /1\.0e\-2/0.01/ /0\.010/0.01/ /\.000// /\.0// /0\.100/0.1/ /\-0/0/ /3\.130/3.13/ /3\.140/3.14/ /3\.150/3.15/ /9\.223372036854776e18/9223372036854776000/ SELECT j, COUNT(*) FROM t GROUP BY j ORDER BY j; --replace_regex /1\.0e\-1/0.1/ /1\.0e\-2/0.01/ /0\.010/0.01/ /\.000// /\.0// /0\.100/0.1/ /\-0/0/ /3\.130/3.13/ /3\.140/3.14/ /3\.150/3.15/ /9\.223372036854776e18/9223372036854776000/ SELECT JSON_EXTRACT(j, '$') AS je, COUNT(*) FROM t GROUP BY je ORDER BY je; --replace_regex /1\.0e\-1/0.1/ /1\.0e\-2/0.01/ /0\.010/0.01/ /\.000// /\.0// /0\.100/0.1/ /\-0/0/ /3\.130/3.13/ /3\.140/3.14/ /3\.150/3.15/ /9\.223372036854776e18/9223372036854776000/ SELECT j, COUNT(*) FROM t GROUP BY j WITH ROLLUP; --replace_regex /1\.0e\-1/0.1/ /1\.0e\-2/0.01/ /0\.010/0.01/ /\.000// /\.0// /0\.100/0.1/ /\-0/0/ /3\.130/3.13/ /3\.140/3.14/ /3\.150/3.15/ /9\.223372036854776e18/9223372036854776000/ SELECT JSON_EXTRACT(j, '$') AS je, COUNT(*) FROM t GROUP BY je WITH ROLLUP; DROP TABLE t, timestamps, datetimes, times, dates, signed_integers, unsigned_integers, decimals, doubles; # Test ordering of a not nullable column. CREATE TABLE t(j JSON NOT NULL); INSERT INTO t VALUES ('1'), ('2'), ('10'), ('"1"'), ('"2"'), ('"10"'), ('true'), ('false'), ('null'); SELECT j FROM t ORDER BY j; SELECT j FROM t ORDER BY JSON_EXTRACT(j, '$'); SELECT JSON_EXTRACT(j, '$') FROM t ORDER BY 1; # Ordering on (j+1) will convert to a numeric type. SELECT j FROM t ORDER BY j+1, JSON_TYPE(j); DROP TABLE t; CREATE TABLE t(vc varchar(10)); INSERT INTO t VALUES ('["abc"]'), ('[1'); --error ER_INVALID_JSON_TEXT_IN_PARAM SELECT * FROM t ORDER BY CAST(vc AS JSON); --error ER_INVALID_JSON_TEXT_IN_PARAM SELECT * FROM t ORDER BY JSON_EXTRACT(vc, '$[0]'); --error ER_INVALID_JSON_TEXT_IN_PARAM SELECT CAST(vc AS JSON) AS j FROM t ORDER BY j; --error ER_INVALID_JSON_TEXT_IN_PARAM SELECT JSON_EXTRACT(vc, '$[0]') AS j FROM t ORDER BY j; --error ER_INVALID_JSON_TEXT_IN_PARAM SELECT CAST(vc AS JSON) FROM t ORDER BY 1; --error ER_INVALID_JSON_TEXT_IN_PARAM SELECT JSON_EXTRACT(vc, '$[0]') FROM t ORDER BY 1; DROP TABLE t; --echo # --echo # Internal ordering of arrays and objects. Ordered by cardinality. --echo # CREATE TABLE t(i int, j json); INSERT INTO t VALUES (1, '{}'), (2, '{"a":1}'), (3, '{"ab":2}'), (4, '{"a":1,"b":2}'), (5, '{"c":3,"d":4}'), (6, '{"a":1,"b":2,"c":3,"d":4}'); INSERT INTO t VALUES (1, '[]'), (2, '[1]'), (3, '[2]'), (4, '[1,2]'), (5, '[2,1]'), (6, '[1,2,3]'), (7, '[1,2,3,4]'), (8, '[4,3,2,1]'), (9, '[1,2,3,4,5]'); INSERT INTO t SELECT i+100, j FROM t; SELECT * FROM t ORDER BY j, i; SELECT * FROM t ORDER BY j DESC, i; # GROUP BY knows how to distinguish the arrays and the objects, even # if they have the same cardinality. # Group by produces indeterminate results based on the order the items are evaluated # SELECT j, COUNT(*) FROM t GROUP BY j ORDER BY j; # GROUP BY WITH ROLLUP, on the other hand, doesn't know how to # distinguish them, and produces confusing results for arrays/objects. # GROUP BY WITH ROLLUP is only useful on scalar results for now. SELECT j, COUNT(*) FROM t GROUP BY j WITH ROLLUP; DROP TABLE t; --echo # Test NULLs sorting. CREATE TABLE t(i int, j json); INSERT INTO t(i) VALUES (1),(2),(3),(2),(1); SELECT * FROM t ORDER BY j, i; SELECT * FROM t ORDER BY j DESC, i; SELECT i, JSON_EXTRACT(j, '$') AS je FROM t ORDER BY je, i; SELECT i, JSON_EXTRACT(j, '$') AS je FROM t ORDER BY je DESC, i; INSERT INTO t(i, j) VALUES (1, '1'); SELECT * FROM t ORDER BY j, i; SELECT * FROM t ORDER BY j DESC, i; SELECT i, JSON_EXTRACT(j, '$') AS je FROM t ORDER BY je, i; SELECT i, JSON_EXTRACT(j, '$') AS je FROM t ORDER BY je DESC, i; DROP TABLE t; # Merging of sort results should not get confused if one of the sort columns is # a JSON column. CREATE TABLE t(vc TEXT, j JSON); INSERT INTO t (vc) VALUES ('a'), ('b'), ('c'); INSERT INTO t SELECT * FROM t; INSERT INTO t SELECT * FROM t; INSERT INTO t SELECT * FROM t; INSERT INTO t SELECT * FROM t; INSERT INTO t SELECT * FROM t; INSERT INTO t SELECT * FROM t; INSERT INTO t SELECT * FROM t; SELECT * FROM t ORDER BY vc, j; DROP TABLE t; --echo # ---------------------------------------------------------------------- --echo # Test of JSON_VALID function. --echo # ---------------------------------------------------------------------- --echo --echo # Table - Json string column - utf-8, NULL --echo Note: 'utf8' is a subset of internal 'utf8mb4' --echo create table utf8_t (c varchar(20)) CHARACTER SET 'utf8'; insert into utf8_t values (NULL); -- echo # Expect NULL: select JSON_VALID(c) from utf8_t; delete from utf8_t; --echo --echo # Table - Json string column - utf-8, valid insert into utf8_t values ('[123]'); select JSON_VALID(c) from utf8_t; delete from utf8_t; --echo --echo # Table - Json string column - utf-8, non-utf8 insert into utf8_t values ('[123'); --echo expect 0 (false) select JSON_VALID(c) from utf8_t; delete from utf8_t; --echo --echo # Table - Try to extract JSON from TIMESTAMP column ALTER TABLE utf8_t ADD d TIMESTAMP; --echo # Should give false; not string or JSON type --echo # and we do not convert automatically from TIMESTAMP to JSON insert into utf8_t values (NULL, '2014-11-25 18:00'); select JSON_VALID(d) from utf8_t; --echo # Explicit cast to a character data type --echo # allows MySQL to parse this is a JSON text --echo # The string isn't a legal JSON document, tho, so not valid. select JSON_VALID(CAST(d as CHAR)) from utf8_t; --echo # Should give true select JSON_VALID(CONCAT( CONCAT('"', CAST(d as CHAR)), '"')) from utf8_t; delete from utf8_t; drop table utf8_t; --echo --echo # Table - JSON type; should give true by definition create table json_t(t json); insert into json_t values ('[123]'); select json_VALID(t) from json_t; --echo --echo # Function result - JSON select JSON_VALID( JSON_ARRAY(t, t) ) from json_t; drop table json_t; --echo # ---------------------------------------------------------------------- --echo # Test of JSON_LENGTH function. --echo # ---------------------------------------------------------------------- create table utf8_mj_length (a int, c varchar(20)) CHARACTER SET 'utf8'; insert into utf8_mj_length values( 1, null ); insert into utf8_mj_length values( 2, '1' ); insert into utf8_mj_length values( 3, 'abc' ); insert into utf8_mj_length values( 4, '"abc"' ); insert into utf8_mj_length values ( 5, 'true' ); insert into utf8_mj_length values ( 6, 'false' ); insert into utf8_mj_length values ( 7, 'null' ); select a, c, json_length( c ) from utf8_mj_length where a = 1; select a, c, json_length( c ) from utf8_mj_length where a = 2; --echo --echo # invalid json text --error ER_INVALID_JSON_TEXT_IN_PARAM select a, c, json_length( c ) from utf8_mj_length where a = 3; select a, c, json_length( c ) from utf8_mj_length where a = 4; select a, c, json_length( c ) from utf8_mj_length where a = 5; select a, c, json_length( c ) from utf8_mj_length where a = 6; select a, c, json_length( c ) from utf8_mj_length where a = 7; create table json_mj_length( a int, b json ); insert into json_mj_length values( 1, NULL ); select a, b, json_length( b ) from json_mj_length where a = 1; # json_length() with vacuous path expressions set names 'ascii'; --echo --echo # path auto-converted to a utf8 string from ascii --echo select a, c, json_length( c, '$' ) from utf8_mj_length where a = 2; set names 'utf8'; select a, c, json_length( c, '$' ) from utf8_mj_length where a = 1; select a, c, json_length( c, '$' ) from utf8_mj_length where a = 2; --echo --echo # invalid json text --error ER_INVALID_JSON_TEXT_IN_PARAM select a, c, json_length( c, '$' ) from utf8_mj_length where a = 3; select a, c, json_length( c, '$' ) from utf8_mj_length where a = 4; select a, c, json_length( c, '$' ) from utf8_mj_length where a = 5; select a, c, json_length( c, '$' ) from utf8_mj_length where a = 6; select a, c, json_length( c, '$' ) from utf8_mj_length where a = 7; select a, b, json_length( b, '$' ) from json_mj_length where a = 1; drop table utf8_mj_length; drop table json_mj_length; # different paths for each row CREATE TABLE json_remove_t(j JSON, p TEXT); INSERT INTO json_remove_t(p) VALUES ('$.a'), ('$.b'), ('$.c'); UPDATE json_remove_t SET j = '{"a":1,"b":2,"c":3}'; SELECT j, p, json_remove(j, p) FROM json_remove_t ORDER BY p; DROP TABLE json_remove_t; CREATE TABLE json_merge_t(i INT, j JSON); INSERT INTO json_merge_t VALUES (0, NULL), (1, 'true'), (2, '5'), (3, '[1,2]'), (4, '{"a":["x", "y"]}'), (5, '{"a":"b","c":"d"}'); SELECT t1.j, t2.j, JSON_MERGE_PRESERVE(t1.j, t2.j) AS m1, JSON_MERGE_PRESERVE(t2.j, t1.j) AS m2 FROM json_merge_t t1, json_merge_t t2 ORDER BY t1.i, t2.i; DROP TABLE json_merge_t; create table keys1(i int, j json); insert into keys1 select i, j from t1; DROP TABLE t1; # example from the wl7909 spec create table rawOrders( orderID int, doc json ); insert into rawOrders values ( 1, '100' ), ( 2, '{ "id": 2, "quantity": 200 }' ); create table orders( orderID int, quantity int unsigned ); INSERT INTO orders( orderID, quantity ) SELECT r.orderID, CASE( JSON_TYPE( r.doc ) ) WHEN "INTEGER" THEN CAST( r.doc AS UNSIGNED INT ) WHEN "OBJECT" THEN CAST( JSON_EXTRACT( r.doc, '$.quantity' ) AS UNSIGNED INT ) ELSE NULL END FROM rawOrders r; select * from rawOrders order by orderID; select * from orders order by orderID; drop table rawOrders; drop table orders; # the value here isn't important, but it should be stable select charset(json_type('{}')); --echo # ---------------------------------------------------------------------- --echo # Test of CAST( AS JSON) --echo # ---------------------------------------------------------------------- create table t1(dati datetime, da date, tim time, ts timestamp, y year, -- ti tinyint, tiu tinyint unsigned, si smallint, siu smallint unsigned, mi mediumint, miu mediumint unsigned, i int, iu int unsigned, bi bigint, biu bigint unsigned, boo boolean, -- dc decimal(5,2), n numeric(5,2), -- f float, d double, bitt bit(10), blb blob, bin binary(10), en enum('a','b','c'), se set('a','b','c'), -- ge geometry, po point, ls linestring, py polygon, js json ); insert into t1 values('2014-11-25 18:00', '2014-11-25', '18:00:59', '2014-11-25 18:00', '1999', -- 127, 255, 32767, 65535, 8388607, 16777215, -- 3 bytes 2147483647, 4294967295, -- 4 bytes 9223372036854775807, 18446744073709551615, true, -- 3.14, 3.14, -- 3.14, 3.14, b'10101', '10101abcde', '10101abcde', 'b', 'a,c', -- ST_GeomFromText('POINT(1 1)'), ST_GeomFromText('POINT(1 1)'), ST_GeomFromText('LINESTRING(0 0,1 1,2 2)'), ST_GeomFromText('POLYGON((0 0,10 0,10 10,0 10,0 0), (5 5,7 5,7 7,5 7, 5 5))'), '[123]' ); select json_type(cast(dati as json)) from t1; select json_type(cast(da as json)) from t1; select json_type(cast(tim as json)) from t1; select json_type(cast(ts as json)) from t1; select json_type(cast(y as json)) from t1; select json_type(cast(ti as json)) from t1; select json_type(cast(tiu as json)) from t1; select json_type(cast(si as json)) from t1; select json_type(cast(siu as json)) from t1; select json_type(cast(mi as json)) from t1; select json_type(cast(miu as json)) from t1; select json_type(cast(i as json)) from t1; select json_type(cast(iu as json)) from t1; select json_type(cast(bi as json)) from t1; select json_type(cast(biu as json)) from t1; select json_type(cast(boo as json)) from t1; # INTEGER (not enough info) select json_type(cast(dc as json)) from t1; # select json_type(cast(n as json)) from t1; select json_type(cast(f as json)) from t1; select json_type(cast(d as json)) from t1; select json_type(cast(bitt as json)) from t1; select json_type(cast(blb as json)) from t1; select json_type(cast(bin as json)) from t1; select json_type(cast(en as json)) from t1; select json_type(cast(se as json)) from t1; select json_type(cast(ge as json)) from t1; select json_type(cast(po as json)) from t1; select json_type(cast(ls as json)) from t1; select json_type(cast(py as json)) from t1; select json_type(cast(js as json)) from t1; # # same, but now show the printable value: # select cast(dati as json) from t1; select cast(da as json) from t1; select cast(tim as json) from t1; select cast(ts as json) from t1; select cast(y as json) from t1; select cast(ti as json) from t1; select cast(tiu as json) from t1; select cast(si as json) from t1; select cast(siu as json) from t1; select cast(mi as json) from t1; select cast(miu as json) from t1; select cast(i as json) from t1; select cast(iu as json) from t1; select cast(bi as json) from t1; select cast(biu as json) from t1; select cast(boo as json) from t1; # INTEGER (not enough info) select cast(dc as json) from t1; # select cast(n as json) from t1; select cast(f as json) from t1; select cast(d as json) from t1; select cast(bitt as json) from t1; select cast(blb as json) from t1; select cast(bin as json) from t1; select cast(en as json) from t1; select cast(se as json) from t1; select cast(ge as json) from t1; select cast(po as json) from t1; select cast(ls as json) from t1; select cast(py as json) from t1; select cast(js as json) from t1; --echo # --echo # Bug#21442878 INCORRECT RETURN STATUS FROM --echo # ITEM_JSON_TYPECAST::VAL_JSON() ON PARSE ERRORS --echo # --error ER_INVALID_TYPE_FOR_JSON select json_extract(en, '$') from t1; drop table t1; create table t1 ( c1 varchar(200) character set 'latin1', c2 varchar(200) character set 'utf8' ); insert into t1 values ('[1,2]', # legal json, but not utf-8 '[1,2 '); # illegal json, but utf-8 # convert latin1 to UTF-8 select cast(c1 as json) from t1; --error ER_INVALID_JSON_TEXT_IN_PARAM select cast(c2 as json) from t1; --error ER_INVALID_JSON_TEXT_IN_PARAM select cast(c2 as json) is null from t1; drop table t1; # Two distinct but related bugs detected by Knut 2015-02-05 caused NULL for y here: create table t2(x int); insert into t2 values (1), (2); select x, cast(y as json) from (select x, cast(x as json) as y from t2) s order by x; select x, cast(y as json) from (select x, cast(cast(x as json) as char charset utf8) as y from t2) s order by x; drop table t2; --echo # ---------------------------------------------------------------------- --echo # Test of CAST(