From 06a4193cc30d679773aa93eb5be48395964d79f8 Mon Sep 17 00:00:00 2001 From: Anel Husakovic Date: Wed, 4 May 2022 04:58:02 -0500 Subject: [PATCH] MDEV-28391: table_exists procedure fails when arguments contain escaped backticks as an quoted identifiers - When arguments to the procedure contain quote in the name, procedure fails with parsing error. The reason was because additional quoting is done when testing TEMPORARY table with the same name. - Reviewed by: --- .../suite/sysschema/r/pr_table_exists.result | 28 +++++++++++++++++++ .../suite/sysschema/t/pr_table_exists.test | 21 ++++++++++++++ .../sys_schema/procedures/table_exists.sql | 20 +++++++++++-- 3 files changed, 66 insertions(+), 3 deletions(-) diff --git a/mysql-test/suite/sysschema/r/pr_table_exists.result b/mysql-test/suite/sysschema/r/pr_table_exists.result index 65d7c20c61d..88917418f67 100644 --- a/mysql-test/suite/sysschema/r/pr_table_exists.result +++ b/mysql-test/suite/sysschema/r/pr_table_exists.result @@ -42,3 +42,31 @@ ERROR 22001: Data too long for column 'in_db' at row 1 CALL sys.table_exists('test', @identifier, @exists); ERROR 22001: Data too long for column 'in_table' at row 1 SET @identifier := NULL; +# +# MDEV-28391: table_exists procedure fails with +# Incorrect table name with backtick identifiers +# +CREATE TABLE `ab``c` (t1_id int PRIMARY KEY, t1_val varchar(10)); +CALL sys.table_exists('test', 'ab`c', @tbl_type); +SELECT @tbl_type; +@tbl_type +BASE TABLE +DROP TABLE `ab``c`; +CREATE TEMPORARY TABLE `ab``c` (t1_id int PRIMARY KEY, t1_val varchar(10)); +CALL sys.table_exists('test', 'ab`c', @tbl_type); +SELECT @tbl_type; +@tbl_type +TEMPORARY +DROP TABLE `ab``c`; +CREATE TABLE `ab``c` (t1_id int PRIMARY KEY, t1_val varchar(10)); +CREATE TEMPORARY TABLE `ab``c` (t1_id int PRIMARY KEY, t1_val varchar(10)); +CALL sys.table_exists('test', 'ab`c', @tbl_type); +SELECT @tbl_type; +@tbl_type +TEMPORARY +# We cannot send quoted identifer to the procedure, no table will be found +CALL sys.table_exists('test', '`ab``c`', @tbl_type); +SELECT @tbl_type; +@tbl_type + +DROP TABLE `ab``c`; diff --git a/mysql-test/suite/sysschema/t/pr_table_exists.test b/mysql-test/suite/sysschema/t/pr_table_exists.test index d0c538843b1..83e1dc0b9d9 100644 --- a/mysql-test/suite/sysschema/t/pr_table_exists.test +++ b/mysql-test/suite/sysschema/t/pr_table_exists.test @@ -46,3 +46,24 @@ CALL sys.table_exists(@identifier, 't1', @exists); CALL sys.table_exists('test', @identifier, @exists); SET @identifier := NULL; + +--echo # +--echo # MDEV-28391: table_exists procedure fails with +--echo # Incorrect table name with backtick identifiers +--echo # +CREATE TABLE `ab``c` (t1_id int PRIMARY KEY, t1_val varchar(10)); +CALL sys.table_exists('test', 'ab`c', @tbl_type); +SELECT @tbl_type; +DROP TABLE `ab``c`; +CREATE TEMPORARY TABLE `ab``c` (t1_id int PRIMARY KEY, t1_val varchar(10)); +CALL sys.table_exists('test', 'ab`c', @tbl_type); +SELECT @tbl_type; +DROP TABLE `ab``c`; +CREATE TABLE `ab``c` (t1_id int PRIMARY KEY, t1_val varchar(10)); +CREATE TEMPORARY TABLE `ab``c` (t1_id int PRIMARY KEY, t1_val varchar(10)); +CALL sys.table_exists('test', 'ab`c', @tbl_type); +SELECT @tbl_type; +--echo # We cannot send quoted identifer to the procedure, no table will be found +CALL sys.table_exists('test', '`ab``c`', @tbl_type); +SELECT @tbl_type; +DROP TABLE `ab``c`; \ No newline at end of file diff --git a/scripts/sys_schema/procedures/table_exists.sql b/scripts/sys_schema/procedures/table_exists.sql index 8a17e0e19f4..0f7640329e7 100644 --- a/scripts/sys_schema/procedures/table_exists.sql +++ b/scripts/sys_schema/procedures/table_exists.sql @@ -133,6 +133,8 @@ CREATE DEFINER='mariadb.sys'@'localhost' PROCEDURE table_exists ( CONTAINS SQL BEGIN DECLARE v_error BOOLEAN DEFAULT FALSE; + DECLARE db_quoted VARCHAR(64); + DECLARE table_quoted VARCHAR(64); DECLARE v_table_type VARCHAR(16) DEFAULT ''; DECLARE v_system_db BOOLEAN DEFAULT LOWER(in_db) IN ('information_schema', 'performance_schema'); @@ -140,19 +142,28 @@ BEGIN DECLARE CONTINUE HANDLER FOR 1146 SET v_error = TRUE; SET out_exists = ''; + SET db_quoted = sys.quote_identifier(in_db); + SET table_quoted = sys.quote_identifier(in_table); -- Verify whether the table name exists as a normal table IF (EXISTS(SELECT 1 FROM information_schema.TABLES WHERE TABLE_SCHEMA = in_db AND TABLE_NAME = in_table)) THEN -- Unfortunately the only way to determine whether there is also a temporary table is to try to create -- a temporary table with the same name. If it succeeds the table didn't exist as a temporary table. IF v_system_db = FALSE THEN - SET @sys.tmp.table_exists.SQL = CONCAT('CREATE TEMPORARY TABLE `', in_db, '`.`', in_table, '` (id INT PRIMARY KEY)'); + SET @sys.tmp.table_exists.SQL = CONCAT('CREATE TEMPORARY TABLE ', + db_quoted, + '.', + table_quoted, + '(id INT PRIMARY KEY)'); PREPARE stmt_create_table FROM @sys.tmp.table_exists.SQL; EXECUTE stmt_create_table; DEALLOCATE PREPARE stmt_create_table; -- The temporary table was created, i.e. it didn't exist. Remove it again so we don't leave garbage around. - SET @sys.tmp.table_exists.SQL = CONCAT('DROP TEMPORARY TABLE `', in_db, '`.`', in_table, '`'); + SET @sys.tmp.table_exists.SQL = CONCAT('DROP TEMPORARY TABLE ', + db_quoted, + '.', + table_quoted); PREPARE stmt_drop_table FROM @sys.tmp.table_exists.SQL; EXECUTE stmt_drop_table; DEALLOCATE PREPARE stmt_drop_table; @@ -174,7 +185,10 @@ BEGIN -- If it does it's possible to SELECT from the table without causing an error. -- If it does not exist even a PREPARE using the table will fail. IF v_system_db = FALSE THEN - SET @sys.tmp.table_exists.SQL = CONCAT('SELECT COUNT(*) FROM `', in_db, '`.`', in_table, '`'); + SET @sys.tmp.table_exists.SQL = CONCAT('SELECT COUNT(*) FROM ', + db_quoted, + '.', + table_quoted); PREPARE stmt_select FROM @sys.tmp.table_exists.SQL; IF (NOT v_error) THEN DEALLOCATE PREPARE stmt_select;