Skip to content

Commit

Permalink
MDEV-10865 COLLATE keyword doesn't work in PREPARE query
Browse files Browse the repository at this point in the history
Fixing applying the COLLATE clause to a parameter caused an error error:
  COLLATION '...' is not valid for CHARACTER SET 'binary'

Fix:

- Changing the collation derivation for a non-prepared Item_param
  to DERIVATION_IGNORABLE.

- Allowing to apply any COLLATE clause to expressions with DERIVATION_IGNORABLE.
  This includes:
    1. A non-prepared Item_param
    2. An explicit NULL
    3. Expressions derived from #1 and #2

  For example:
    SELECT ? COLLATE utf8mb_unicode_ci;
    SELECT NULL COLLATE utf8mb_unicode_ci;
    SELECT CONCAT(?) COLLATE utf8mb_unicode_ci;
    SELECT CONCAT(NULL) COLLATE utf8mb_unicode_ci

- Additional change: preserving the collation of an expression when
  the expression gets assigned to a PS parameter and evaluates to SQL NULL.
  Before this change, the collation of the parameter was erroneously set
  to &my_charset_binary.

- Additional change: removing the multiplication to mbmaxlen from the
  fix_char_length_ulonglong() argument, because the multiplication already
  happens inside fix_char_length_ulonglong().
  This fixes a too large column size created for a COLLATE clause.
  • Loading branch information
abarkov committed Jul 4, 2024
1 parent c91ec6a commit f6989d1
Show file tree
Hide file tree
Showing 11 changed files with 626 additions and 49 deletions.
100 changes: 100 additions & 0 deletions mysql-test/main/ctype_collate_context.result
Original file line number Diff line number Diff line change
Expand Up @@ -3716,3 +3716,103 @@ DROP PROCEDURE p;
#
# End of 10.9 tests
#
#
# Start of 10.11 tests
#
#
# MDEV-10865 COLLATE keyword doesn't work in PREPARE query
#
SET NAMES utf8mb4;
#
# A context collation and an explicit NULL
#
SELECT NULL COLLATE uca1400_ai_ci;
ERROR HY000: 'NULL' is not allowed in this context
SELECT CONCAT(NULL) COLLATE uca1400_ai_ci;
ERROR HY000: 'NULL' is not allowed in this context
PREPARE stmt FROM 'CREATE TABLE t1 AS SELECT NULL COLLATE uca1400_ai_ci AS c1';
ERROR HY000: 'NULL' is not allowed in this context
PREPARE stmt FROM 'CREATE TABLE t1 AS SELECT CONCAT(NULL) COLLATE uca1400_ai_ci AS c1';
ERROR HY000: 'NULL' is not allowed in this context
#
# A context collation and a parameter bound to NULL
#
PREPARE stmt FROM 'CREATE TABLE t1 AS SELECT ? COLLATE uca1400_ai_ci AS c1';
EXECUTE stmt USING NULL;
ERROR HY000: 'NULL' is not allowed in this context
EXECUTE stmt USING CONCAT(NULL);
ERROR HY000: 'NULL' is not allowed in this context
EXECUTE stmt USING NULL COLLATE uca1400_ai_ci;
ERROR HY000: 'NULL' is not allowed in this context
EXECUTE stmt USING CONCAT(NULL) COLLATE uca1400_ai_ci;
ERROR HY000: 'NULL' is not allowed in this context
#
# A context collation and CONVERT(NULL USING ...)
#
EXECUTE stmt USING CONVERT(NULL USING utf8mb4);
SHOW CREATE TABLE t1;
Table Create Table
t1 CREATE TABLE `t1` (
`c1` char(0) CHARACTER SET utf8mb4 COLLATE utf8mb4_uca1400_ai_ci DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci
DROP TABLE t1;
EXECUTE stmt USING CONVERT(NULL USING utf8mb4) COLLATE uca1400_ai_ci;
SHOW CREATE TABLE t1;
Table Create Table
t1 CREATE TABLE `t1` (
`c1` char(0) CHARACTER SET utf8mb4 COLLATE utf8mb4_uca1400_ai_ci DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci
DROP TABLE t1;
EXECUTE stmt USING CONVERT(NULL USING binary);
ERROR 42000: COLLATION 'uca1400_ai_ci' is not valid for CHARACTER SET 'binary'
EXECUTE stmt USING CONVERT(NULL USING latin1);
ERROR 42000: COLLATION 'uca1400_ai_ci' is not valid for CHARACTER SET 'latin1'
#
# A context collation and an expression with a parameter
# whose character does not get resolved when bound to a not-NULL value
#
PREPARE stmt FROM 'CREATE TABLE t1 AS SELECT LEFT(NULL,?) COLLATE uca1400_ai_ci AS c1';
EXECUTE stmt USING NULL;
ERROR HY000: 'NULL' is not allowed in this context
#
# A context collation and an expression with a parameter
# whose character set gets resolved when bound to a not-NULL value
#
PREPARE stmt FROM 'CREATE TABLE t1 AS SELECT ? COLLATE uca1400_ai_ci AS c1';
EXECUTE stmt USING 1;
SHOW CREATE TABLE t1;
Table Create Table
t1 CREATE TABLE `t1` (
`c1` varchar(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_uca1400_ai_ci DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci
DROP TABLE t1;
PREPARE stmt FROM 'CREATE TABLE t1 AS SELECT ? COLLATE uca1400_ai_ci AS c1';
EXECUTE stmt USING _binary'test';
ERROR 42000: COLLATION 'uca1400_ai_ci' is not valid for CHARACTER SET 'binary'
PREPARE stmt FROM 'CREATE TABLE t1 AS SELECT ? COLLATE uca1400_ai_ci AS c1';
EXECUTE stmt USING 'test';
SHOW CREATE TABLE t1;
Table Create Table
t1 CREATE TABLE `t1` (
`c1` varchar(4) CHARACTER SET utf8mb4 COLLATE utf8mb4_uca1400_ai_ci DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci
DROP TABLE t1;
PREPARE stmt FROM 'CREATE TABLE t1 AS SELECT ? COLLATE uca1400_ai_ci AS c1';
EXECUTE stmt USING 'test' COLLATE utf8mb4_bin;
SHOW CREATE TABLE t1;
Table Create Table
t1 CREATE TABLE `t1` (
`c1` varchar(4) CHARACTER SET utf8mb4 COLLATE utf8mb4_uca1400_ai_ci DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci
DROP TABLE t1;
PREPARE stmt FROM 'CREATE TABLE t1 AS SELECT ? COLLATE uca1400_ai_ci AS c1';
EXECUTE stmt USING _latin1'test' COLLATE latin1_bin;
SHOW CREATE TABLE t1;
Table Create Table
t1 CREATE TABLE `t1` (
`c1` varchar(4) CHARACTER SET utf8mb4 COLLATE utf8mb4_uca1400_ai_ci DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci
DROP TABLE t1;
#
# End of 10.11 tests
#
100 changes: 100 additions & 0 deletions mysql-test/main/ctype_collate_context.test
Original file line number Diff line number Diff line change
Expand Up @@ -401,3 +401,103 @@ DROP PROCEDURE p;
--echo #
--echo # End of 10.9 tests
--echo #

--echo #
--echo # Start of 10.11 tests
--echo #

--echo #
--echo # MDEV-10865 COLLATE keyword doesn't work in PREPARE query
--echo #

SET NAMES utf8mb4;

--echo #
--echo # A context collation and an explicit NULL
--echo #

--error ER_NOT_ALLOWED_IN_THIS_CONTEXT
SELECT NULL COLLATE uca1400_ai_ci;
--error ER_NOT_ALLOWED_IN_THIS_CONTEXT
SELECT CONCAT(NULL) COLLATE uca1400_ai_ci;
--error ER_NOT_ALLOWED_IN_THIS_CONTEXT
PREPARE stmt FROM 'CREATE TABLE t1 AS SELECT NULL COLLATE uca1400_ai_ci AS c1';
--error ER_NOT_ALLOWED_IN_THIS_CONTEXT
PREPARE stmt FROM 'CREATE TABLE t1 AS SELECT CONCAT(NULL) COLLATE uca1400_ai_ci AS c1';


--echo #
--echo # A context collation and a parameter bound to NULL
--echo #

PREPARE stmt FROM 'CREATE TABLE t1 AS SELECT ? COLLATE uca1400_ai_ci AS c1';
--error ER_NOT_ALLOWED_IN_THIS_CONTEXT
EXECUTE stmt USING NULL;
--error ER_NOT_ALLOWED_IN_THIS_CONTEXT
EXECUTE stmt USING CONCAT(NULL);
--error ER_NOT_ALLOWED_IN_THIS_CONTEXT
EXECUTE stmt USING NULL COLLATE uca1400_ai_ci;
--error ER_NOT_ALLOWED_IN_THIS_CONTEXT
EXECUTE stmt USING CONCAT(NULL) COLLATE uca1400_ai_ci;

--echo #
--echo # A context collation and CONVERT(NULL USING ...)
--echo #

EXECUTE stmt USING CONVERT(NULL USING utf8mb4);
SHOW CREATE TABLE t1;
DROP TABLE t1;

EXECUTE stmt USING CONVERT(NULL USING utf8mb4) COLLATE uca1400_ai_ci;
SHOW CREATE TABLE t1;
DROP TABLE t1;

--error ER_COLLATION_CHARSET_MISMATCH
EXECUTE stmt USING CONVERT(NULL USING binary);

--error ER_COLLATION_CHARSET_MISMATCH
EXECUTE stmt USING CONVERT(NULL USING latin1);

--echo #
--echo # A context collation and an expression with a parameter
--echo # whose character does not get resolved when bound to a not-NULL value
--echo #

PREPARE stmt FROM 'CREATE TABLE t1 AS SELECT LEFT(NULL,?) COLLATE uca1400_ai_ci AS c1';
--error ER_NOT_ALLOWED_IN_THIS_CONTEXT
EXECUTE stmt USING NULL;


--echo #
--echo # A context collation and an expression with a parameter
--echo # whose character set gets resolved when bound to a not-NULL value
--echo #

PREPARE stmt FROM 'CREATE TABLE t1 AS SELECT ? COLLATE uca1400_ai_ci AS c1';
EXECUTE stmt USING 1;
SHOW CREATE TABLE t1;
DROP TABLE t1;

PREPARE stmt FROM 'CREATE TABLE t1 AS SELECT ? COLLATE uca1400_ai_ci AS c1';
--error ER_COLLATION_CHARSET_MISMATCH
EXECUTE stmt USING _binary'test';

PREPARE stmt FROM 'CREATE TABLE t1 AS SELECT ? COLLATE uca1400_ai_ci AS c1';
EXECUTE stmt USING 'test';
SHOW CREATE TABLE t1;
DROP TABLE t1;

PREPARE stmt FROM 'CREATE TABLE t1 AS SELECT ? COLLATE uca1400_ai_ci AS c1';
EXECUTE stmt USING 'test' COLLATE utf8mb4_bin;
SHOW CREATE TABLE t1;
DROP TABLE t1;

PREPARE stmt FROM 'CREATE TABLE t1 AS SELECT ? COLLATE uca1400_ai_ci AS c1';
EXECUTE stmt USING _latin1'test' COLLATE latin1_bin;
SHOW CREATE TABLE t1;
DROP TABLE t1;


--echo #
--echo # End of 10.11 tests
--echo #
85 changes: 85 additions & 0 deletions mysql-test/main/ctype_utf32.result
Original file line number Diff line number Diff line change
Expand Up @@ -3024,3 +3024,88 @@ HEX(DATE_FORMAT(TIME'11:22:33',@format))
#
# End of 10.4 tests
#
#
# Start of 10.11 tests
#
#
# MDEV-10865 COLLATE keyword doesn't work in PREPARE query
#
#
# The collation is not applicable to the PS parameter
#
SET NAMES utf8mb4;
CREATE TABLE t1 (
c1 varchar(500) COLLATE utf32_unicode_ci NOT NULL
);
INSERT INTO t1 VALUES ('jj');
PREPARE stmt FROM 'SELECT * FROM t1 WHERE c1 LIKE ? COLLATE utf32_unicode_ci';
EXECUTE stmt USING 'jj';
ERROR 42000: COLLATION 'utf32_unicode_ci' is not valid for CHARACTER SET 'utf8mb4'
DROP TABLE t1;
PREPARE stmt FROM 'CREATE TABLE t1 AS SELECT ? COLLATE utf32_unicode_ci AS c1';
EXECUTE stmt USING 'test';
ERROR 42000: COLLATION 'utf32_unicode_ci' is not valid for CHARACTER SET 'utf8mb4'
PREPARE stmt FROM 'CREATE TABLE t1 AS SELECT CONCAT(?) COLLATE utf32_unicode_ci AS c1';
EXECUTE stmt USING 'test';
ERROR 42000: COLLATION 'utf32_unicode_ci' is not valid for CHARACTER SET 'utf8mb4'
CREATE TABLE t1 AS SELECT NULL COLLATE utf32_unicode_ci AS c1;
SHOW CREATE TABLE t1;
Table Create Table
t1 CREATE TABLE `t1` (
`c1` char(0) CHARACTER SET utf32 COLLATE utf32_unicode_ci DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci
DROP TABLE t1;
CREATE TABLE t1 AS SELECT CONCAT(NULL) COLLATE utf32_unicode_ci AS c1;
SHOW CREATE TABLE t1;
Table Create Table
t1 CREATE TABLE `t1` (
`c1` char(0) CHARACTER SET utf32 COLLATE utf32_unicode_ci DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci
DROP TABLE t1;
#
# The collation is applicable to the PS parameter
#
SET NAMES utf8mb4, collation_connection=utf32_general_ci;
CREATE TABLE t1 (
c1 varchar(500) COLLATE utf32_unicode_ci NOT NULL
);
INSERT INTO t1 VALUES ('jj');
PREPARE stmt FROM 'SELECT * FROM t1 WHERE c1 LIKE ? COLLATE utf32_unicode_ci';
EXECUTE stmt USING 'jj';
c1
jj
DROP TABLE t1;
PREPARE stmt FROM 'CREATE TABLE t1 AS SELECT ? COLLATE utf32_unicode_ci AS c1';
EXECUTE stmt USING 'test';
SHOW CREATE TABLE t1;
Table Create Table
t1 CREATE TABLE `t1` (
`c1` varchar(4) CHARACTER SET utf32 COLLATE utf32_unicode_ci DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci
DROP TABLE t1;
PREPARE stmt FROM 'CREATE TABLE t1 AS SELECT CONCAT(?) COLLATE utf32_unicode_ci AS c1';
EXECUTE stmt USING 'test';
SHOW CREATE TABLE t1;
Table Create Table
t1 CREATE TABLE `t1` (
`c1` varchar(4) CHARACTER SET utf32 COLLATE utf32_unicode_ci DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci
DROP TABLE t1;
CREATE TABLE t1 AS SELECT NULL COLLATE utf32_unicode_ci AS c1;
SHOW CREATE TABLE t1;
Table Create Table
t1 CREATE TABLE `t1` (
`c1` char(0) CHARACTER SET utf32 COLLATE utf32_unicode_ci DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci
DROP TABLE t1;
CREATE TABLE t1 AS SELECT CONCAT(NULL) COLLATE utf32_unicode_ci AS c1;
SHOW CREATE TABLE t1;
Table Create Table
t1 CREATE TABLE `t1` (
`c1` char(0) CHARACTER SET utf32 COLLATE utf32_unicode_ci DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci
DROP TABLE t1;
SET NAMES utf8mb4;
#
# End of 10.11 tests
#
76 changes: 76 additions & 0 deletions mysql-test/main/ctype_utf32.test
Original file line number Diff line number Diff line change
Expand Up @@ -1167,4 +1167,80 @@ SELECT HEX(DATE_FORMAT(TIME'11:22:33',@format));
--echo # End of 10.4 tests
--echo #

--echo #
--echo # Start of 10.11 tests
--echo #

--echo #
--echo # MDEV-10865 COLLATE keyword doesn't work in PREPARE query
--echo #

--echo #
--echo # The collation is not applicable to the PS parameter
--echo #

SET NAMES utf8mb4;
CREATE TABLE t1 (
c1 varchar(500) COLLATE utf32_unicode_ci NOT NULL
);
INSERT INTO t1 VALUES ('jj');
PREPARE stmt FROM 'SELECT * FROM t1 WHERE c1 LIKE ? COLLATE utf32_unicode_ci';
--error ER_COLLATION_CHARSET_MISMATCH
EXECUTE stmt USING 'jj';
DROP TABLE t1;

PREPARE stmt FROM 'CREATE TABLE t1 AS SELECT ? COLLATE utf32_unicode_ci AS c1';
--error ER_COLLATION_CHARSET_MISMATCH
EXECUTE stmt USING 'test';

PREPARE stmt FROM 'CREATE TABLE t1 AS SELECT CONCAT(?) COLLATE utf32_unicode_ci AS c1';
--error ER_COLLATION_CHARSET_MISMATCH
EXECUTE stmt USING 'test';

CREATE TABLE t1 AS SELECT NULL COLLATE utf32_unicode_ci AS c1;
SHOW CREATE TABLE t1;
DROP TABLE t1;

CREATE TABLE t1 AS SELECT CONCAT(NULL) COLLATE utf32_unicode_ci AS c1;
SHOW CREATE TABLE t1;
DROP TABLE t1;


--echo #
--echo # The collation is applicable to the PS parameter
--echo #

SET NAMES utf8mb4, collation_connection=utf32_general_ci;
CREATE TABLE t1 (
c1 varchar(500) COLLATE utf32_unicode_ci NOT NULL
);
INSERT INTO t1 VALUES ('jj');
PREPARE stmt FROM 'SELECT * FROM t1 WHERE c1 LIKE ? COLLATE utf32_unicode_ci';
EXECUTE stmt USING 'jj';
DROP TABLE t1;

PREPARE stmt FROM 'CREATE TABLE t1 AS SELECT ? COLLATE utf32_unicode_ci AS c1';
EXECUTE stmt USING 'test';
SHOW CREATE TABLE t1;
DROP TABLE t1;

PREPARE stmt FROM 'CREATE TABLE t1 AS SELECT CONCAT(?) COLLATE utf32_unicode_ci AS c1';
EXECUTE stmt USING 'test';
SHOW CREATE TABLE t1;
DROP TABLE t1;

CREATE TABLE t1 AS SELECT NULL COLLATE utf32_unicode_ci AS c1;
SHOW CREATE TABLE t1;
DROP TABLE t1;

CREATE TABLE t1 AS SELECT CONCAT(NULL) COLLATE utf32_unicode_ci AS c1;
SHOW CREATE TABLE t1;
DROP TABLE t1;

SET NAMES utf8mb4;

--echo #
--echo # End of 10.11 tests
--echo #

--enable_service_connection
Loading

0 comments on commit f6989d1

Please sign in to comment.