Commit a2a42f4e authored by Alexander Barkov's avatar Alexander Barkov

MDEV-23408 Wrong result upon query from I_S and further Assertion `!alias_arg...

MDEV-23408 Wrong result upon query from I_S and further Assertion `!alias_arg || strlen(alias_arg->str) == alias_arg->length' failed with certain connection charset

There were two independent problems which lead to the crash
and to the non-relevant records returned in I_S queries:

- The code in the I_S implementation was not secure
  about values with 0x00 bytes.
  It's fixed by using check_db_name() and check_table_name()
  inside make_table_name_list(), and by adding the test for
  0x00 inside check_table_name().

- The code in Item_string::print() did not convert
  strings without introducers when restoring
  the CREATE VIEW statement from an Item tree.
  This made wrong literals inside the "query" line in the view FRM file
  in cases when the VIEW parse time
  character_set_client!=character_set_connection.
  That's fixed by adding a proper conversion.

  This change also fixed a similar problem in SHOW PROCEDURE CODE -
  the literals were displayed in wrong character set in SP instructions
  in cases when the SP parse time
  character_set_client!=character_set_connection.
parent bbae2d39
...@@ -3000,5 +3000,38 @@ DROP TABLE t1; ...@@ -3000,5 +3000,38 @@ DROP TABLE t1;
# #
SET STORAGE_ENGINE=Default; SET STORAGE_ENGINE=Default;
# #
# MDEV-23408 Wrong result upon query from I_S and further Assertion `!alias_arg || strlen(alias_arg->str) == alias_arg->length' failed with certain connection charset
#
SET NAMES utf8;
SET SESSION character_set_connection= utf16le;
CREATE TABLE kv (v BLOB);
CREATE TABLE t (a INT);
CREATE VIEW v AS SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 't1';
LOAD DATA INFILE 'MYSQLD_DATADIR/test/v.frm' REPLACE INTO TABLE kv;
SELECT * FROM kv WHERE v LIKE _binary'query=%';
v
query=select `information_schema`.`TABLES`.`TABLE_CATALOG` AS `TABLE_CATALOG`,`information_schema`.`TABLES`.`TABLE_SCHEMA` AS `TABLE_SCHEMA`,`information_schema`.`TABLES`.`TABLE_NAME` AS `TABLE_NAME`,`information_schema`.`TABLES`.`TABLE_TYPE` AS `TABLE_TYPE`,`information_schema`.`TABLES`.`ENGINE` AS `ENGINE`,`information_schema`.`TABLES`.`VERSION` AS `VERSION`,`information_schema`.`TABLES`.`ROW_FORMAT` AS `ROW_FORMAT`,`information_schema`.`TABLES`.`TABLE_ROWS` AS `TABLE_ROWS`,`information_schema`.`TABLES`.`AVG_ROW_LENGTH` AS `AVG_ROW_LENGTH`,`information_schema`.`TABLES`.`DATA_LENGTH` AS `DATA_LENGTH`,`information_schema`.`TABLES`.`MAX_DATA_LENGTH` AS `MAX_DATA_LENGTH`,`information_schema`.`TABLES`.`INDEX_LENGTH` AS `INDEX_LENGTH`,`information_schema`.`TABLES`.`DATA_FREE` AS `DATA_FREE`,`information_schema`.`TABLES`.`AUTO_INCREMENT` AS `AUTO_INCREMENT`,`information_schema`.`TABLES`.`CREATE_TIME` AS `CREATE_TIME`,`information_schema`.`TABLES`.`UPDATE_TIME` AS `UPDATE_TIME`,`information_schema`.`TABLES`.`CHECK_TIME` AS `CHECK_TIME`,`information_schema`.`TABLES`.`TABLE_COLLATION` AS `TABLE_COLLATION`,`information_schema`.`TABLES`.`CHECKSUM` AS `CHECKSUM`,`information_schema`.`TABLES`.`CREATE_OPTIONS` AS `CREATE_OPTIONS`,`information_schema`.`TABLES`.`TABLE_COMMENT` AS `TABLE_COMMENT` from `INFORMATION_SCHEMA`.`TABLES` where `information_schema`.`TABLES`.`TABLE_NAME` = 't1'
TRUNCATE TABLE kv;
SELECT * FROM v;
TABLE_CATALOG TABLE_SCHEMA TABLE_NAME TABLE_TYPE ENGINE VERSION ROW_FORMAT TABLE_ROWS AVG_ROW_LENGTH DATA_LENGTH MAX_DATA_LENGTH INDEX_LENGTH DATA_FREE AUTO_INCREMENT CREATE_TIME UPDATE_TIME CHECK_TIME TABLE_COLLATION CHECKSUM CREATE_OPTIONS TABLE_COMMENT
LOCK TABLE t WRITE;
UNLOCK TABLES;
DROP VIEW v;
DROP TABLE t;
DROP TABLE kv;
CREATE TABLE t (a INT);
SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME=CONCAT('t',0x00,'1');
TABLE_CATALOG TABLE_SCHEMA TABLE_NAME TABLE_TYPE ENGINE VERSION ROW_FORMAT TABLE_ROWS AVG_ROW_LENGTH DATA_LENGTH MAX_DATA_LENGTH INDEX_LENGTH DATA_FREE AUTO_INCREMENT CREATE_TIME UPDATE_TIME CHECK_TIME TABLE_COLLATION CHECKSUM CREATE_OPTIONS TABLE_COMMENT
LOCK TABLE t WRITE;
UNLOCK TABLES;
DROP TABLE t;
CREATE TABLE t (a INT);
SELECT TABLE_NAME, HEX(TABLE_NAME) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME=CONCAT('t',0x00,'1');
TABLE_NAME HEX(TABLE_NAME)
SELECT TABLE_NAME, TABLE_SCHEMA, HEX(TABLE_NAME) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA=CONCAT('test',0x00,'1');
TABLE_NAME TABLE_SCHEMA HEX(TABLE_NAME)
DROP TABLE t;
SET NAMES utf8;
#
# End of 10.2 tests # End of 10.2 tests
# #
...@@ -11239,5 +11239,22 @@ DROP TABLE t1; ...@@ -11239,5 +11239,22 @@ DROP TABLE t1;
# #
SET STORAGE_ENGINE=Default; SET STORAGE_ENGINE=Default;
# #
# MDEV-23408 Wrong result upon query from I_S and further Assertion `!alias_arg || strlen(alias_arg->str) == alias_arg->length' failed with certain connection charset
#
SET NAMES utf8;
SET SESSION character_set_connection=latin1;
CREATE VIEW v1 AS SELECT 'ä' AS c1;
SELECT c1, HEX(c1) FROM v1;
c1 HEX(c1)
ä E4
CREATE TABLE kv (v BLOB);
LOAD DATA INFILE 'MYSQLD_DATADIR/test/v1.frm' REPLACE INTO TABLE kv;
SELECT * FROM kv WHERE v LIKE _binary'query=%';
v
query=select 'ä' AS `c1`
DROP TABLE kv;
DROP VIEW v1;
SET NAMES utf8;
#
# End of 10.2 tests # End of 10.2 tests
# #
...@@ -2210,5 +2210,25 @@ SELECT * FROM v LIMIT ROWS EXAMINED 9; ...@@ -2210,5 +2210,25 @@ SELECT * FROM v LIMIT ROWS EXAMINED 9;
ERROR HY000: Sort aborted: ERROR HY000: Sort aborted:
DROP VIEW v; DROP VIEW v;
# #
# MDEV-23408 Wrong result upon query from I_S and further Assertion `!alias_arg || strlen(alias_arg->str) == alias_arg->length' failed with certain connection charset
#
CREATE TABLE t (a INT);
SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME=CONCAT('t',0x00,'1');
TABLE_CATALOG TABLE_SCHEMA TABLE_NAME TABLE_TYPE ENGINE VERSION ROW_FORMAT TABLE_ROWS AVG_ROW_LENGTH DATA_LENGTH MAX_DATA_LENGTH INDEX_LENGTH DATA_FREE AUTO_INCREMENT CREATE_TIME UPDATE_TIME CHECK_TIME TABLE_COLLATION CHECKSUM CREATE_OPTIONS TABLE_COMMENT
SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA=CONCAT('test',0x00,'1');
TABLE_CATALOG TABLE_SCHEMA TABLE_NAME TABLE_TYPE ENGINE VERSION ROW_FORMAT TABLE_ROWS AVG_ROW_LENGTH DATA_LENGTH MAX_DATA_LENGTH INDEX_LENGTH DATA_FREE AUTO_INCREMENT CREATE_TIME UPDATE_TIME CHECK_TIME TABLE_COLLATION CHECKSUM CREATE_OPTIONS TABLE_COMMENT
DROP TABLE t;
CREATE TABLE `a/~.b` (a INT);
SELECT TABLE_SCHEMA, TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME='a/~.b';
TABLE_SCHEMA TABLE_NAME
test a/~.b
DROP TABLE `a/~.b`;
CREATE DATABASE `a/~.b`;
CREATE TABLE `a/~.b`.t1 (a INT);
SELECT TABLE_SCHEMA, TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA='a/~.b';
TABLE_SCHEMA TABLE_NAME
a/~.b t1
DROP DATABASE `a/~.b`;
#
# End of 10.2 Test # End of 10.2 Test
# #
...@@ -971,3 +971,27 @@ Pos Instruction ...@@ -971,3 +971,27 @@ Pos Instruction
DROP PROCEDURE testp_bug11763507; DROP PROCEDURE testp_bug11763507;
DROP FUNCTION testf_bug11763507; DROP FUNCTION testf_bug11763507;
#END OF BUG#11763507 test. #END OF BUG#11763507 test.
#
# MDEV-23408 Wrong result upon query from I_S and further Assertion `!alias_arg || strlen(alias_arg->str) == alias_arg->length' failed with certain connection charset
#
SET NAMES utf8;
SET SESSION character_set_connection=latin1;
CREATE PROCEDURE p1()
BEGIN
DECLARE a VARCHAR(10) CHARACTER SET utf8;
SET a='ä';
SELECT a, 'ä' AS b;
END;
$$
SHOW PROCEDURE CODE p1;
Pos Instruction
0 set a@0 NULL
1 set a@0 'ä'
2 stmt 0 "SELECT a, 'ä' AS b"
CALL p1;
a b
ä ä
DROP PROCEDURE p1;
#
# End of 10.2 tests
#
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
-- source include/have_utf32.inc -- source include/have_utf32.inc
-- source include/have_utf8mb4.inc -- source include/have_utf8mb4.inc
let $MYSQLD_DATADIR= `select @@datadir`;
SET TIME_ZONE='+03:00'; SET TIME_ZONE='+03:00';
...@@ -810,6 +811,42 @@ let $coll='utf16le_nopad_bin'; ...@@ -810,6 +811,42 @@ let $coll='utf16le_nopad_bin';
let $coll_pad='utf16le_bin'; let $coll_pad='utf16le_bin';
--source include/ctype_pad_all_engines.inc --source include/ctype_pad_all_engines.inc
--echo #
--echo # MDEV-23408 Wrong result upon query from I_S and further Assertion `!alias_arg || strlen(alias_arg->str) == alias_arg->length' failed with certain connection charset
--echo #
SET NAMES utf8;
SET SESSION character_set_connection= utf16le;
CREATE TABLE kv (v BLOB);
CREATE TABLE t (a INT);
CREATE VIEW v AS SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 't1';
--replace_result $MYSQLD_DATADIR MYSQLD_DATADIR
eval LOAD DATA INFILE '$MYSQLD_DATADIR/test/v.frm' REPLACE INTO TABLE kv;
SELECT * FROM kv WHERE v LIKE _binary'query=%';
TRUNCATE TABLE kv;
SELECT * FROM v;
LOCK TABLE t WRITE;
UNLOCK TABLES;
DROP VIEW v;
DROP TABLE t;
DROP TABLE kv;
CREATE TABLE t (a INT);
SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME=CONCAT('t',0x00,'1');
LOCK TABLE t WRITE;
UNLOCK TABLES;
DROP TABLE t;
CREATE TABLE t (a INT);
SELECT TABLE_NAME, HEX(TABLE_NAME) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME=CONCAT('t',0x00,'1');
SELECT TABLE_NAME, TABLE_SCHEMA, HEX(TABLE_NAME) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA=CONCAT('test',0x00,'1');
DROP TABLE t;
SET NAMES utf8;
--echo # --echo #
--echo # End of 10.2 tests --echo # End of 10.2 tests
--echo # --echo #
...@@ -2,6 +2,8 @@ ...@@ -2,6 +2,8 @@
# Tests with the utf8 character set # Tests with the utf8 character set
# #
let $MYSQLD_DATADIR= `select @@datadir`;
let collation=utf8_unicode_ci; let collation=utf8_unicode_ci;
--source include/have_collation.inc --source include/have_collation.inc
SET TIME_ZONE='+03:00'; SET TIME_ZONE='+03:00';
...@@ -2165,6 +2167,22 @@ let $coll='utf8_nopad_bin'; ...@@ -2165,6 +2167,22 @@ let $coll='utf8_nopad_bin';
let $coll_pad='utf8_bin'; let $coll_pad='utf8_bin';
--source include/ctype_pad_all_engines.inc --source include/ctype_pad_all_engines.inc
--echo #
--echo # MDEV-23408 Wrong result upon query from I_S and further Assertion `!alias_arg || strlen(alias_arg->str) == alias_arg->length' failed with certain connection charset
--echo #
SET NAMES utf8;
SET SESSION character_set_connection=latin1;
CREATE VIEW v1 AS SELECT 'ä' AS c1;
SELECT c1, HEX(c1) FROM v1;
CREATE TABLE kv (v BLOB);
--replace_result $MYSQLD_DATADIR MYSQLD_DATADIR
eval LOAD DATA INFILE '$MYSQLD_DATADIR/test/v1.frm' REPLACE INTO TABLE kv;
SELECT * FROM kv WHERE v LIKE _binary'query=%';
DROP TABLE kv;
DROP VIEW v1;
SET NAMES utf8;
--echo # --echo #
--echo # End of 10.2 tests --echo # End of 10.2 tests
--echo # --echo #
...@@ -1934,6 +1934,27 @@ SELECT * FROM v LIMIT ROWS EXAMINED 9; ...@@ -1934,6 +1934,27 @@ SELECT * FROM v LIMIT ROWS EXAMINED 9;
DROP VIEW v; DROP VIEW v;
--echo #
--echo # MDEV-23408 Wrong result upon query from I_S and further Assertion `!alias_arg || strlen(alias_arg->str) == alias_arg->length' failed with certain connection charset
--echo #
# Expect empty sets if requested TABLE_NAME or TABLE_SCHEMA with zero bytes
CREATE TABLE t (a INT);
SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME=CONCAT('t',0x00,'1');
SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA=CONCAT('test',0x00,'1');
DROP TABLE t;
# Make sure check_table_name() does not reject special characters
CREATE TABLE `a/~.b` (a INT);
SELECT TABLE_SCHEMA, TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME='a/~.b';
DROP TABLE `a/~.b`;
# Make sure check_db_name() does not reject special characters
CREATE DATABASE `a/~.b`;
CREATE TABLE `a/~.b`.t1 (a INT);
SELECT TABLE_SCHEMA, TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA='a/~.b';
DROP DATABASE `a/~.b`;
--echo # --echo #
--echo # End of 10.2 Test --echo # End of 10.2 Test
--echo # --echo #
...@@ -735,3 +735,27 @@ DROP PROCEDURE testp_bug11763507; ...@@ -735,3 +735,27 @@ DROP PROCEDURE testp_bug11763507;
DROP FUNCTION testf_bug11763507; DROP FUNCTION testf_bug11763507;
--echo #END OF BUG#11763507 test. --echo #END OF BUG#11763507 test.
--echo #
--echo # MDEV-23408 Wrong result upon query from I_S and further Assertion `!alias_arg || strlen(alias_arg->str) == alias_arg->length' failed with certain connection charset
--echo #
SET NAMES utf8;
SET SESSION character_set_connection=latin1;
DELIMITER $$;
CREATE PROCEDURE p1()
BEGIN
DECLARE a VARCHAR(10) CHARACTER SET utf8;
SET a='ä';
SELECT a, 'ä' AS b;
END;
$$
DELIMITER ;$$
SHOW PROCEDURE CODE p1;
CALL p1;
DROP PROCEDURE p1;
--echo #
--echo # End of 10.2 tests
--echo #
...@@ -3338,9 +3338,49 @@ void Item_string::print(String *str, enum_query_type query_type) ...@@ -3338,9 +3338,49 @@ void Item_string::print(String *str, enum_query_type query_type)
} }
else else
{ {
// Caller wants a result in the charset of str_value. /*
We're restoring a parse-able statement from an Item tree.
Make sure to revert character set conversions that previously
happened in the parser when Item_string was created.
*/
if (print_introducer)
{
/*
Print the string as is, without conversion:
Strings with introducers are not converted in the parser.
*/
str_value.print(str); str_value.print(str);
} }
else
{
/*
Print the string with conversion.
Strings without introducers are converted in the parser,
from character_set_client to character_set_connection.
When restoring a CREATE VIEW statement,
- str_value.charsets() contains parse time character_set_connection
- str->charset() contains parse time character_set_client
So we convert the string back from parse-time character_set_connection
to parse time character_set_client.
In some cases, e.g. SHOW PROCEDURE CODE, it's also possible
that str->charset() is "utf8mb3" instead of parse time
character_set_client. In these cases we convert
here from the parse-time character_set_connection to utf8mb3.
QQ: perhaps the code behind SHOW PROCEDURE CODE should
also request the result in the parse-time character_set_client
(like the code restoring CREATE VIEW statements does),
rather than in utf8mb3:
- utf8mb3 does not work well with non-BMP characters (e.g. emoji).
- Simply changing utf8mb3 to utf8mb4 will not fully help:
some character sets have unassigned characters,
they get lost during during cs->utf8mb4->cs round trip.
*/
str_value.print_with_conversion(str, str->charset());
}
}
str->append('\''); str->append('\'');
} }
......
...@@ -4227,7 +4227,9 @@ make_table_name_list(THD *thd, Dynamic_array<LEX_STRING*> *table_names, ...@@ -4227,7 +4227,9 @@ make_table_name_list(THD *thd, Dynamic_array<LEX_STRING*> *table_names,
if (!lookup_field_vals->wild_table_value && if (!lookup_field_vals->wild_table_value &&
lookup_field_vals->table_value.str) lookup_field_vals->table_value.str)
{ {
if (lookup_field_vals->table_value.length > NAME_LEN) if (check_table_name(lookup_field_vals->table_value.str,
lookup_field_vals->table_value.length,
false))
{ {
/* /*
Impossible value for a table name, Impossible value for a table name,
...@@ -4264,6 +4266,9 @@ make_table_name_list(THD *thd, Dynamic_array<LEX_STRING*> *table_names, ...@@ -4264,6 +4266,9 @@ make_table_name_list(THD *thd, Dynamic_array<LEX_STRING*> *table_names,
return (schema_tables_add(thd, table_names, return (schema_tables_add(thd, table_names,
lookup_field_vals->table_value.str)); lookup_field_vals->table_value.str));
if (check_db_name((LEX_STRING*)db_name))
return 0; // Impossible TABLE_SCHEMA name
find_files_result res= find_files(thd, table_names, db_name, path, find_files_result res= find_files(thd, table_names, db_name, path,
&lookup_field_vals->table_value); &lookup_field_vals->table_value);
if (res != FIND_FILES_OK) if (res != FIND_FILES_OK)
......
...@@ -4183,6 +4183,21 @@ bool check_table_name(const char *name, size_t length, bool check_for_path_chars ...@@ -4183,6 +4183,21 @@ bool check_table_name(const char *name, size_t length, bool check_for_path_chars
if (check_for_path_chars && if (check_for_path_chars &&
(*name == '/' || *name == '\\' || *name == '~' || *name == FN_EXTCHAR)) (*name == '/' || *name == '\\' || *name == '~' || *name == FN_EXTCHAR))
return 1; return 1;
/*
We don't allow zero byte in table/schema names:
- Some code still uses NULL-terminated strings.
Zero bytes will confuse this code.
- There is a little practical use of zero bytes in names anyway.
Note, if the string passed as "name" comes here
from the parser as an identifier, it does not contain zero bytes,
as the parser rejects zero bytes in identifiers.
But "name" can also come here from queries like this:
SELECT * FROM I_S.TABLES WHERE TABLE_NAME='str';
In this case "name" is a general string expression
and it can have any arbitrary bytes, including zero bytes.
*/
if (*name == 0x00)
return 1;
name++; name++;
name_length++; name_length++;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment