From 41e368f22db02e5c907aa40ae8c5c8e34ded3db9 Mon Sep 17 00:00:00 2001 From: Alexey Botchkov Date: Thu, 15 Apr 2021 11:52:22 +0400 Subject: [PATCH] MDEV-25149 JSON_TABLE: Inconsistency in implicit data type conversion. Only return the error if field->store produced errors, not warnings. --- mysql-test/suite/json/r/json_table.result | 51 +++++++++++++++- .../suite/json/r/json_table_mysql.result | 60 +++++++++++++++---- mysql-test/suite/json/t/json_table.test | 17 ++++++ mysql-test/suite/json/t/json_table_mysql.test | 6 +- sql/json_table.cc | 45 ++++++++------ 5 files changed, 146 insertions(+), 33 deletions(-) diff --git a/mysql-test/suite/json/r/json_table.result b/mysql-test/suite/json/r/json_table.result index 3ef87b3e24b..d0a9021dd70 100644 --- a/mysql-test/suite/json/r/json_table.result +++ b/mysql-test/suite/json/r/json_table.result @@ -360,10 +360,12 @@ json_table( '[{"a":"asd"}, {"a":123}, {"a":[]}, {"a":{}} ]', '$[*]' intcol int path '$.a' default '1234' on empty default '5678' on error) ) as tt; id intcol -1 5678 +1 0 2 123 3 5678 4 5678 +Warnings: +Warning 1366 Incorrect integer value: 'asd' for column ``.`(temporary)`.`intcol` at row 1 SELECT COUNT(*) FROM JSON_TABLE('[1, 2]', '$[*]' COLUMNS( I INT PATH '$')) tt; COUNT(*) 2 @@ -588,9 +590,11 @@ Error 4177 Can't store an array or an object in the scalar column 'a' of JSON_TA # MDEV-JSON_TABLE: CREATE TABLE ignores NULL ON ERROR (implicit or explicit) and fails. # CREATE TABLE t1 AS SELECT * FROM JSON_TABLE('{"x":1}', '$' COLUMNS(f DATE PATH '$.*')) AS jt; +Warnings: +Warning 1265 Data truncated for column 'f' at row 1 SELECT * FROM t1; f -NULL +0000-00-00 DROP TABLE t1; # # MDEV-25254: JSON_TABLE: Inconsistent name resolution with right joins @@ -907,5 +911,48 @@ a 2 DEALLOCATE PREPARE stmt1; # +# MDEV-25149 JSON_TABLE: Inconsistency in implicit data type conversion. +# +select * from json_table( '[{"a":"asd"}, {"a":123}, {"a":[]}, {"a":{}} ]', '$[*]' +columns ( id for ordinality, +intcol int path '$.a' default '1234' on empty default '5678' on error) +) as tt; +id intcol +1 0 +2 123 +3 5678 +4 5678 +Warnings: +Warning 1366 Incorrect integer value: 'asd' for column ``.`(temporary)`.`intcol` at row 1 +# +# MDEV-25377 JSON_TABLE: Wrong value with implicit conversion. +# +select * from json_table('{"a":"foo", "b":1, "c":1000}', '$.*' columns(converted tinyint path '$', original text path '$')) as jt; +converted original +0 foo +1 1 +127 1000 +Warnings: +Warning 1366 Incorrect integer value: 'foo' for column ``.`(temporary)`.`converted` at row 1 +Warning 1264 Out of range value for column 'converted' at row 3 +select * from json_table('{"a":"foo", "b":1, "c":1000}', '$.*' columns(converted tinyint path '$', original text path '$')) as jt order by converted; +converted original +0 foo +1 1 +127 1000 +Warnings: +Warning 1366 Incorrect integer value: 'foo' for column ``.`(temporary)`.`converted` at row 1 +Warning 1264 Out of range value for column 'converted' at row 1 +Warning 1366 Incorrect integer value: 'foo' for column ``.`(temporary)`.`converted` at row 1 +Warning 1264 Out of range value for column 'converted' at row 3 +select * from json_table('{"a":"foo", "b":1, "c":1000}', '$.*' columns(converted tinyint path '$', original text path '$')) as jt order by original; +converted original +1 1 +127 1000 +0 foo +Warnings: +Warning 1264 Out of range value for column 'converted' at row 2 +Warning 1366 Incorrect integer value: 'foo' for column ``.`(temporary)`.`converted` at row 3 +# # End of 10.6 tests # diff --git a/mysql-test/suite/json/r/json_table_mysql.result b/mysql-test/suite/json/r/json_table_mysql.result index 31acd80f011..ec21f18523f 100644 --- a/mysql-test/suite/json/r/json_table_mysql.result +++ b/mysql-test/suite/json/r/json_table_mysql.result @@ -59,7 +59,10 @@ id jpath_i jpath_r jsn_path jexst 2 2 2 2 0 3 33 33.3 {"x":33} 1 4 0 0.33 0.33 0 -5 66 0 asd 0 +5 0 0 asd 0 +Warnings: +Warning 1366 Incorrect integer value: 'asd' for column ``.`(temporary)`.`jpath_i` at row 5 +Warning 1366 Incorrect double value: 'asd' for column ``.`(temporary)`.`jpath_r` at row 5 select * from json_table( '[{"x":"3"},{"a":2},{"b":1},{"a":0},{"a":[1,2]}]', @@ -396,7 +399,10 @@ v CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIE DROP VIEW v; SELECT * FROM JSON_TABLE('"asdf"', '$' COLUMNS (a INT PATH '$' ERROR ON ERROR)) AS jt; -ERROR HY000: Can't store an array or an object in the scalar column 'a' of JSON_TABLE 'jt'. +a +0 +Warnings: +Warning 1366 Incorrect integer value: 'asdf' for column ``.`(temporary)`.`a` at row 1 SELECT * FROM JSON_TABLE('[{"a":1},{"a":2}]', '$' COLUMNS (a INT PATH '$[*].a' ERROR ON ERROR)) AS jt; @@ -407,11 +413,16 @@ JSON_TABLE('[{"a":1},{"a":2}]', ERROR HY000: Can't store multiple matches of the path in the column 'a' of JSON_TABLE 'jt'. SELECT * FROM JSON_TABLE('123.456', '$' COLUMNS (a DECIMAL(2,1) PATH '$' ERROR ON ERROR)) AS jt; -ERROR HY000: Can't store an array or an object in the scalar column 'a' of JSON_TABLE 'jt'. +a +9.9 +Warnings: +Warning 1264 Out of range value for column 'a' at row 1 SELECT * FROM JSON_TABLE('123.456', '$' COLUMNS (a DECIMAL(2,1) PATH '$')) AS jt; a -NULL +9.9 +Warnings: +Warning 1264 Out of range value for column 'a' at row 1 SELECT * FROM JSON_TABLE('{"a":1,"b":{"a":1,"b":{"a":1,"b":{"a":1,"b":{"a":1,"b":{"a":1,"b":{"a":1,"b":{"a":1,"b":{"a":1,"b":{"a":1,"b":{"a":1,"b":{"a":1,"b":{"a":1,"b":{"a":1,"b":{"a":1,"b":{"a":1,"b":{"a":1,"b":{"a":1,"b":{}}}}}}}}}}}}}}}}}}}', '$' COLUMNS (i0 INT PATH '$.a', @@ -525,7 +536,13 @@ i INT PATH '$', f FLOAT PATH '$', d DECIMAL PATH '$')) AS jt; tm dt i f d -NULL NULL NULL 0 0 +00:00:00 0000-00-00 0 0 0 +Warnings: +Warning 1265 Data truncated for column 'tm' at row 1 +Warning 1265 Data truncated for column 'dt' at row 1 +Warning 1366 Incorrect integer value: 'asdf' for column ``.`(temporary)`.`i` at row 1 +Warning 1366 Incorrect double value: 'asdf' for column ``.`(temporary)`.`f` at row 1 +Warning 1366 Incorrect decimal value: 'asdf' for column ``.`(temporary)`.`d` at row 1 SELECT * FROM JSON_TABLE('{}', '$' COLUMNS (x INT PATH '$.x' DEFAULT NULL ON EMPTY)) jt; ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'NULL ON EMPTY)) jt' at line 2 @@ -739,6 +756,8 @@ FROM JSON_TABLE('{"a":"1993-01-01"}', AS jt; jp 0000-00-00 +Warnings: +Warning 1265 Data truncated for column 'jp' at row 1 # # Bug#25532429: INVALID JSON ERROR NOT THROWN WITH EMPTY TABLES JOIN # @@ -763,6 +782,8 @@ JSON_TABLE('["3.14159"]', ) AS alias2; col18 3.1415 +Warnings: +Warning 1265 Data truncated for column 'col18' at row 1 #Truncated space doesn't trigger ON ERROR SELECT * FROM JSON_TABLE('["3.14159 "]', @@ -805,12 +826,17 @@ JSON_TABLE('[3.14159]', '$[*]' COLUMNS (col18 DECIMAL(3,3) PATH '$') ) AS alias2; col18 -NULL +0.999 +Warnings: +Warning 1264 Out of range value for column 'col18' at row 1 SELECT * FROM JSON_TABLE('[3.14159]', '$[*]' COLUMNS (col18 DECIMAL(3,3) PATH '$' ERROR ON ERROR) ) AS alias2; -ERROR HY000: Can't store an array or an object in the scalar column 'col18' of JSON_TABLE 'alias2'. +col18 +0.999 +Warnings: +Warning 1264 Out of range value for column 'col18' at row 1 SELECT * FROM JSON_TABLE('[0.9]', '$[*]' COLUMNS (col18 DECIMAL(3,3) PATH '$') @@ -825,6 +851,9 @@ DEFAULT "3.14159" ON ERROR) col18 0.000 0.000 +Warnings: +Warning 1366 Incorrect decimal value: 'asdf' for column ``.`(temporary)`.`col18` at row 1 +Warning 1366 Incorrect decimal value: 'ghjk' for column ``.`(temporary)`.`col18` at row 2 CREATE TABLE t1(jd JSON); INSERT INTO t1 VALUES('["asdf"]'),('["ghjk"]'); SELECT * FROM t1, @@ -835,6 +864,9 @@ DEFAULT "3.14159" ON ERROR) jd col18 ["asdf"] 0.000 ["ghjk"] 0.000 +Warnings: +Warning 1366 Incorrect decimal value: 'asdf' for column ``.`(temporary)`.`col18` at row 1 +Warning 1366 Incorrect decimal value: 'ghjk' for column ``.`(temporary)`.`col18` at row 1 DROP TABLE t1; # # Bug#25540027: SIG 11 IN FIND_FIELD_IN_TABLE | SQL/SQL_BASE.CC @@ -1316,13 +1348,17 @@ id SELECT id FROM JSON_TABLE('[{"id":"9223372036854775808"}]', '$[*]' COLUMNS (id BIGINT PATH '$.id')) AS json; id -NULL +9223372036854775807 +Warnings: +Warning 1264 Out of range value for column 'id' at row 1 # Here the JSON value is a NUMERIC value, and we thus know if the value # is signed or unsigned. SELECT id FROM JSON_TABLE('[{"id":9223372036854775808}]', '$[*]' COLUMNS (id BIGINT PATH '$.id')) AS json; id -NULL +9223372036854775807 +Warnings: +Warning 1264 Out of range value for column 'id' at row 1 # If we tell the JSON table column to be unsigned, we get to store the # full value correctly. SELECT id FROM JSON_TABLE('[{"id":"9223372036854775808"}]', '$[*]' COLUMNS @@ -1524,11 +1560,15 @@ b VARCHAR(3) PATH '$.b' DEFAULT '"ERR"' ON ERROR, c DATE PATH '$.c' DEFAULT '"2001-01-01"' ON ERROR, d DECIMAL PATH '$.c' DEFAULT '999' ON ERROR) ) AS jt; +Warnings: +Warning 1265 Data truncated for column 'b' at row 2 +Warning 1264 Out of range value for column 'a' at row 3 +Warning 1265 Data truncated for column 'd' at row 4 SELECT * FROM t ORDER BY id; id a b c d 1 1 abc NULL NULL 2 2 abc NULL NULL -3 111 xyz NULL NULL +3 127 xyz NULL NULL 4 NULL NULL 2012-00-00 12 DROP TABLE t; # diff --git a/mysql-test/suite/json/t/json_table.test b/mysql-test/suite/json/t/json_table.test index b8b16a750d1..f250fcbf58a 100644 --- a/mysql-test/suite/json/t/json_table.test +++ b/mysql-test/suite/json/t/json_table.test @@ -797,6 +797,23 @@ SELECT * FROM JSON_TABLE (CONVERT('[1,2]' USING koi8u), '$[*]' COLUMNS(a CHAR(8) EXECUTE stmt1; DEALLOCATE PREPARE stmt1; +--echo # +--echo # MDEV-25149 JSON_TABLE: Inconsistency in implicit data type conversion. +--echo # +select * from json_table( '[{"a":"asd"}, {"a":123}, {"a":[]}, {"a":{}} ]', '$[*]' + columns ( id for ordinality, + intcol int path '$.a' default '1234' on empty default '5678' on error) + ) as tt; + +--echo # +--echo # MDEV-25377 JSON_TABLE: Wrong value with implicit conversion. +--echo # +select * from json_table('{"a":"foo", "b":1, "c":1000}', '$.*' columns(converted tinyint path '$', original text path '$')) as jt; + +select * from json_table('{"a":"foo", "b":1, "c":1000}', '$.*' columns(converted tinyint path '$', original text path '$')) as jt order by converted; + +select * from json_table('{"a":"foo", "b":1, "c":1000}', '$.*' columns(converted tinyint path '$', original text path '$')) as jt order by original; + --echo # --echo # End of 10.6 tests --echo # diff --git a/mysql-test/suite/json/t/json_table_mysql.test b/mysql-test/suite/json/t/json_table_mysql.test index eebc4ba11ee..aaf123c6f7c 100644 --- a/mysql-test/suite/json/t/json_table_mysql.test +++ b/mysql-test/suite/json/t/json_table_mysql.test @@ -327,7 +327,7 @@ SELECT * FROM v; SHOW CREATE VIEW v; DROP VIEW v; ---error ER_JSON_TABLE_SCALAR_EXPECTED +#--error ER_JSON_TABLE_SCALAR_EXPECTED SELECT * FROM JSON_TABLE('"asdf"', '$' COLUMNS (a INT PATH '$' ERROR ON ERROR)) AS jt; --error ER_JSON_TABLE_MULTIPLE_MATCHES @@ -339,7 +339,7 @@ SELECT * FROM SELECT * FROM JSON_TABLE('[{"a":1},{"a":2}]', '$' COLUMNS (a JSON PATH '$[*].a' ERROR ON ERROR)) AS jt; ---error ER_JSON_TABLE_SCALAR_EXPECTED +#--error ER_JSON_TABLE_SCALAR_EXPECTED SELECT * FROM JSON_TABLE('123.456', '$' COLUMNS (a DECIMAL(2,1) PATH '$' ERROR ON ERROR)) AS jt; SELECT * FROM @@ -729,7 +729,7 @@ SELECT * FROM ) AS alias2; #--error ER_JT_VALUE_OUT_OF_RANGE ---error ER_JSON_TABLE_SCALAR_EXPECTED +# --error ER_JSON_TABLE_SCALAR_EXPECTED SELECT * FROM JSON_TABLE('[3.14159]', '$[*]' COLUMNS (col18 DECIMAL(3,3) PATH '$' ERROR ON ERROR) diff --git a/sql/json_table.cc b/sql/json_table.cc index 39eb415aaed..68a62aa6762 100644 --- a/sql/json_table.cc +++ b/sql/json_table.cc @@ -176,7 +176,7 @@ class ha_json_table: public handler String *m_js; // The JSON document we're reading String m_tmps; // Buffer for the above - int fill_column_values(uchar * buf, uchar *pos); + int fill_column_values(THD *thd, uchar * buf, uchar *pos); public: ha_json_table(TABLE_SHARE *share_arg, Table_function_json_table *jt): @@ -351,27 +351,29 @@ int ha_json_table::rnd_init(bool scan) for JSON's null, true, and false. */ -static int store_json_in_field(Field *f, const json_engine_t *je) +static void store_json_in_field(Field *f, const json_engine_t *je) { switch (je->value_type) { case JSON_VALUE_NULL: f->set_null(); - return 0; + return; case JSON_VALUE_TRUE: case JSON_VALUE_FALSE: { Item_result rt= f->result_type(); if (rt == INT_RESULT || rt == DECIMAL_RESULT || rt == REAL_RESULT) - return f->store(je->value_type == JSON_VALUE_TRUE, false); + { + f->store(je->value_type == JSON_VALUE_TRUE, false); + return; + } break; } default: break; }; - - return f->store((const char *) je->value, (uint32) je->value_len, je->s.cs); + f->store((const char *) je->value, (uint32) je->value_len, je->s.cs); } @@ -389,9 +391,6 @@ bool Json_table_nested_path::check_error(const char *str) int ha_json_table::rnd_next(uchar *buf) { - int res; - enum_check_fields cf_orig; - if (!m_js) return HA_ERR_END_OF_FILE; @@ -417,11 +416,7 @@ int ha_json_table::rnd_next(uchar *buf) Step 2: Read values for all columns (the columns refer to nested paths they are in). */ - cf_orig= table->in_use->count_cuted_fields; - table->in_use->count_cuted_fields= CHECK_FIELD_EXPRESSION; - res= fill_column_values(buf, NULL); - table->in_use->count_cuted_fields= cf_orig; - return res ? HA_ERR_JSON_TABLE : 0; + return fill_column_values(table->in_use, buf, NULL) ? HA_ERR_JSON_TABLE : 0; } @@ -436,15 +431,21 @@ int ha_json_table::rnd_next(uchar *buf) ha_json_table::position() for description) */ -int ha_json_table::fill_column_values(uchar * buf, uchar *pos) +int ha_json_table::fill_column_values(THD *thd, uchar * buf, uchar *pos) { MY_BITMAP *orig_map= dbug_tmp_use_all_columns(table, &table->write_set); int error= 0; + Counting_error_handler er_handler; Field **f= table->field; Json_table_column *jc; List_iterator_fast jc_i(m_jt->m_columns); my_ptrdiff_t ptrdiff= buf - table->record[0]; Abort_on_warning_instant_set ao_set(table->in_use, FALSE); + enum_check_fields cf_orig= table->in_use->count_cuted_fields; + + table->in_use->count_cuted_fields= CHECK_FIELD_ERROR_FOR_NULL; + + thd->push_internal_handler(&er_handler); while (!error && (jc= jc_i++)) { @@ -540,11 +541,17 @@ int ha_json_table::fill_column_values(uchar * buf, uchar *pos) } else { - if (!json_value_scalar(&je) || - store_json_in_field(*f, &je)) + if (!(error= !json_value_scalar(&je))) + { + store_json_in_field(*f, &je); + error= er_handler.errors; + } + + if (error) { error= jc->m_on_error.respond(jc, *f, ER_JSON_TABLE_SCALAR_EXPECTED); + er_handler.errors= 0; } else { @@ -577,13 +584,15 @@ cont_loop: } dbug_tmp_restore_column_map(&table->write_set, orig_map); + thd->pop_internal_handler(); + thd->count_cuted_fields= cf_orig; return error; } int ha_json_table::rnd_pos(uchar * buf, uchar *pos) { - return fill_column_values(buf, pos) ? HA_ERR_JSON_TABLE : 0; + return fill_column_values(table->in_use, buf, pos) ? HA_ERR_JSON_TABLE : 0; }