diff --git a/mysql-test/main/max_session_mem_used.result b/mysql-test/main/max_session_mem_used.result index d2ce765999b..20a88d7d811 100644 --- a/mysql-test/main/max_session_mem_used.result +++ b/mysql-test/main/max_session_mem_used.result @@ -46,3 +46,85 @@ REPAIR LOCAL TABLE t1; DROP TABLE t1; SET max_session_mem_used=default; # End of 10.6 tests +# +# MDEV-37489: SIGSEGV in get_param_default_value | store_schema_params +# +CREATE OR REPLACE PROCEDURE p0 (x INT DEFAULT func()) +BEGIN +SELECT x; +END; +// +SET SESSION max_session_mem_used=8192; +CALL p0(); +ERROR HY000: The MariaDB server is running with the --max-session-mem-used=8192 option so it cannot execute this statement +SET @@max_session_mem_used=DEFAULT; +CALL p0(); +ERROR 42000: FUNCTION test.func does not exist +SELECT * FROM information_schema.PARAMETERS where specific_name = 'p0'; +SPECIFIC_CATALOG SPECIFIC_SCHEMA SPECIFIC_NAME ORDINAL_POSITION PARAMETER_MODE PARAMETER_NAME DATA_TYPE CHARACTER_MAXIMUM_LENGTH CHARACTER_OCTET_LENGTH NUMERIC_PRECISION NUMERIC_SCALE DATETIME_PRECISION CHARACTER_SET_NAME COLLATION_NAME DTD_IDENTIFIER ROUTINE_TYPE +def test p0 1 IN x int NULL NULL 10 0 NULL NULL NULL int(11) PROCEDURE +# with func() defined +CREATE FUNCTION func(x INT DEFAULT 10) RETURNS INT +BEGIN +RETURN x; +END; +// +CREATE OR REPLACE PROCEDURE p0 (x INT DEFAULT func()) +BEGIN +SELECT x; +END; +// +SET SESSION max_session_mem_used=8192; +CALL p0(); +ERROR HY000: The MariaDB server is running with the --max-session-mem-used=8192 option so it cannot execute this statement +SET @@max_session_mem_used=DEFAULT; +CALL p0(); +x +10 +SELECT * FROM information_schema.PARAMETERS where specific_name = 'p0'; +SPECIFIC_CATALOG SPECIFIC_SCHEMA SPECIFIC_NAME ORDINAL_POSITION PARAMETER_MODE PARAMETER_NAME DATA_TYPE CHARACTER_MAXIMUM_LENGTH CHARACTER_OCTET_LENGTH NUMERIC_PRECISION NUMERIC_SCALE DATETIME_PRECISION CHARACTER_SET_NAME COLLATION_NAME DTD_IDENTIFIER ROUTINE_TYPE +def test p0 1 IN x int NULL NULL 10 0 NULL NULL NULL int(11) PROCEDURE +# with multiple functions +CREATE FUNCTION func2(x INT DEFAULT 10) RETURNS INT +BEGIN +RETURN x; +END; +// +CREATE OR REPLACE PROCEDURE p0 (x INT DEFAULT func(), y INT DEFAULT func2()) +BEGIN +SELECT x, y; +END; +// +SET SESSION max_session_mem_used=8192; +CALL p0(); +ERROR HY000: The MariaDB server is running with the --max-session-mem-used=8192 option so it cannot execute this statement +SET @@max_session_mem_used=DEFAULT; +CALL p0(); +x y +10 10 +SELECT * FROM information_schema.PARAMETERS where specific_name = 'p0'; +SPECIFIC_CATALOG SPECIFIC_SCHEMA SPECIFIC_NAME ORDINAL_POSITION PARAMETER_MODE PARAMETER_NAME DATA_TYPE CHARACTER_MAXIMUM_LENGTH CHARACTER_OCTET_LENGTH NUMERIC_PRECISION NUMERIC_SCALE DATETIME_PRECISION CHARACTER_SET_NAME COLLATION_NAME DTD_IDENTIFIER ROUTINE_TYPE +def test p0 1 IN x int NULL NULL 10 0 NULL NULL NULL int(11) PROCEDURE +def test p0 2 IN y int NULL NULL 10 0 NULL NULL NULL int(11) PROCEDURE +# with function and constant default param +CREATE OR REPLACE PROCEDURE p0 (x INT DEFAULT func(), y INT DEFAULT func2(), z INT DEFAULT 10) +BEGIN +SELECT x, y, z; +END; +// +SET SESSION max_session_mem_used=8192; +CALL p0(); +ERROR HY000: The MariaDB server is running with the --max-session-mem-used=8192 option so it cannot execute this statement +SET @@max_session_mem_used=DEFAULT; +CALL p0(); +x y z +10 10 10 +SELECT * FROM information_schema.PARAMETERS where specific_name = 'p0'; +SPECIFIC_CATALOG SPECIFIC_SCHEMA SPECIFIC_NAME ORDINAL_POSITION PARAMETER_MODE PARAMETER_NAME DATA_TYPE CHARACTER_MAXIMUM_LENGTH CHARACTER_OCTET_LENGTH NUMERIC_PRECISION NUMERIC_SCALE DATETIME_PRECISION CHARACTER_SET_NAME COLLATION_NAME DTD_IDENTIFIER ROUTINE_TYPE +def test p0 1 IN x int NULL NULL 10 0 NULL NULL NULL int(11) PROCEDURE +def test p0 2 IN y int NULL NULL 10 0 NULL NULL NULL int(11) PROCEDURE +def test p0 3 IN z int NULL NULL 10 0 NULL NULL NULL int(11) PROCEDURE +DROP PROCEDURE p0; +DROP FUNCTION func; +DROP FUNCTION func2; +# End of 11.8 tests diff --git a/mysql-test/main/max_session_mem_used.test b/mysql-test/main/max_session_mem_used.test index 353fdf11916..ba270470c3b 100644 --- a/mysql-test/main/max_session_mem_used.test +++ b/mysql-test/main/max_session_mem_used.test @@ -83,3 +83,97 @@ DROP TABLE t1; SET max_session_mem_used=default; --echo # End of 10.6 tests + +--echo # +--echo # MDEV-37489: SIGSEGV in get_param_default_value | store_schema_params +--echo # + +--DELIMITER // +CREATE OR REPLACE PROCEDURE p0 (x INT DEFAULT func()) +BEGIN + SELECT x; +END; +// +--DELIMITER ; + +SET SESSION max_session_mem_used=8192; +--ERROR ER_OPTION_PREVENTS_STATEMENT +CALL p0(); + +SET @@max_session_mem_used=DEFAULT; +--ERROR ER_SP_DOES_NOT_EXIST +CALL p0(); + +SELECT * FROM information_schema.PARAMETERS where specific_name = 'p0'; + +--echo # with func() defined +--DELIMITER // +CREATE FUNCTION func(x INT DEFAULT 10) RETURNS INT +BEGIN + RETURN x; +END; +// + +CREATE OR REPLACE PROCEDURE p0 (x INT DEFAULT func()) +BEGIN + SELECT x; +END; +// +--DELIMITER ; + +SET SESSION max_session_mem_used=8192; +--ERROR ER_OPTION_PREVENTS_STATEMENT +CALL p0(); + +SET @@max_session_mem_used=DEFAULT; +CALL p0(); + +SELECT * FROM information_schema.PARAMETERS where specific_name = 'p0'; + +--echo # with multiple functions +--DELIMITER // +CREATE FUNCTION func2(x INT DEFAULT 10) RETURNS INT +BEGIN + RETURN x; +END; +// + +CREATE OR REPLACE PROCEDURE p0 (x INT DEFAULT func(), y INT DEFAULT func2()) +BEGIN + SELECT x, y; +END; +// +--DELIMITER ; + +SET SESSION max_session_mem_used=8192; +--ERROR ER_OPTION_PREVENTS_STATEMENT +CALL p0(); + +SET @@max_session_mem_used=DEFAULT; +CALL p0(); + +SELECT * FROM information_schema.PARAMETERS where specific_name = 'p0'; + +--echo # with function and constant default param +--DELIMITER // +CREATE OR REPLACE PROCEDURE p0 (x INT DEFAULT func(), y INT DEFAULT func2(), z INT DEFAULT 10) +BEGIN + SELECT x, y, z; +END; +// +--DELIMITER ; + +SET SESSION max_session_mem_used=8192; +--ERROR ER_OPTION_PREVENTS_STATEMENT +CALL p0(); + +SET @@max_session_mem_used=DEFAULT; +CALL p0(); + +SELECT * FROM information_schema.PARAMETERS where specific_name = 'p0'; + +DROP PROCEDURE p0; +DROP FUNCTION func; +DROP FUNCTION func2; + +--echo # End of 11.8 tests diff --git a/mysql-test/main/mysqld--help.result b/mysql-test/main/mysqld--help.result index ca455b9ce36..5f97793197e 100644 --- a/mysql-test/main/mysqld--help.result +++ b/mysql-test/main/mysqld--help.result @@ -775,8 +775,9 @@ The following specify which files/extra groups are read (specified before remain Number of seconds to wait for a block to be written to a connection before aborting the write --new-mode=name Used to introduce new behavior to existing MariaDB - versions. Any combination of: FIX_DISK_TMPTABLE_COSTS, or - ALL to set all combinations + versions. Any combination of: FIX_DISK_TMPTABLE_COSTS, + FIX_INDEX_STATS_FOR_ALL_NULLS, or ALL to set all + combinations --note-verbosity=name Verbosity level for note-warnings given to the user. See also @@sql_notes. Any combination of: basic, diff --git a/mysql-test/main/null_aware_cardinality.result b/mysql-test/main/null_aware_cardinality.result new file mode 100644 index 00000000000..fca07a63455 --- /dev/null +++ b/mysql-test/main/null_aware_cardinality.result @@ -0,0 +1,158 @@ +SET @session_start_value = @@new_mode; +# Small driving table +CREATE TABLE t1 (a INT, b INT); +INSERT INTO t1 VALUES (1, 1), (2, 2000),(3,300); +ANALYZE TABLE t1 PERSISTENT FOR ALL; +Table Op Msg_type Msg_text +test.t1 analyze status Engine-independent statistics collected +test.t1 analyze status OK +# Table that will be accessed by an index lookup (`ref` access) +CREATE TABLE t2 (a INT, b INT, KEY key_b(b)); +# All t11.b values are NULL +INSERT INTO t2 SELECT seq/100, NULL FROM seq_1_to_1000; +ANALYZE TABLE t2 PERSISTENT FOR ALL; +Table Op Msg_type Msg_text +test.t2 analyze status Engine-independent statistics collected +test.t2 analyze status Table is already up to date +SET @@new_mode = "FIX_INDEX_STATS_FOR_ALL_NULLS"; +# NULL-rejecting equality t1.b = t2.b will not return any matches +# because all values of t2.b are NULL. So "rows" = 1 for t2 where 1 is +# a special value meaning "very few" rows +EXPLAIN EXTENDED SELECT * FROM t1 JOIN t2 ON t1.a = t2.a AND t1.b = t2.b; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using where +1 SIMPLE t2 ref key_b key_b 5 test.t1.b 1 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t2`.`a` AS `a`,`test`.`t2`.`b` AS `b` from `test`.`t1` join `test`.`t2` where `test`.`t2`.`a` = `test`.`t1`.`a` and `test`.`t2`.`b` = `test`.`t1`.`b` +# However, rows estimation for not NULL-rejecting conditions +# must not be affected ("rows" > 1 is expected) +EXPLAIN EXTENDED SELECT * FROM t1 JOIN t2 ON t1.a = t2.a AND t1.b <=> t2.b; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 +1 SIMPLE t2 ref key_b key_b 5 test.t1.b 11 100.00 Using index condition; Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t2`.`a` AS `a`,`test`.`t2`.`b` AS `b` from `test`.`t1` join `test`.`t2` where `test`.`t2`.`a` = `test`.`t1`.`a` and `test`.`t1`.`b` <=> `test`.`t2`.`b` +# Insert some non-NULL values and re-collect the stats +INSERT INTO t2 SELECT 1, 1 FROM seq_1_to_100; +ANALYZE TABLE t2 PERSISTENT FOR COLUMNS (b) INDEXES (key_b); +Table Op Msg_type Msg_text +test.t2 analyze status Engine-independent statistics collected +test.t2 analyze status OK +EXPLAIN EXTENDED SELECT * FROM t1 JOIN t2 ON t1.a = t2.a AND t1.b = t2.b; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using where +1 SIMPLE t2 ref key_b key_b 5 test.t1.b 100 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t2`.`a` AS `a`,`test`.`t2`.`b` AS `b` from `test`.`t1` join `test`.`t2` where `test`.`t2`.`a` = `test`.`t1`.`a` and `test`.`t2`.`b` = `test`.`t1`.`b` +# Test composite index for two columns. Key prefix is used for access +CREATE TABLE t3 (a INT, b INT, KEY key_ab(a,b)); +# All t3.b values are NULL +INSERT INTO t3 SELECT seq/100, NULL FROM seq_1_to_1000; +ANALYZE TABLE t3 PERSISTENT FOR COLUMNS(b) INDEXES(key_ab); +Table Op Msg_type Msg_text +test.t3 analyze status Engine-independent statistics collected +test.t3 analyze status Table is already up to date +# NULL-rejecting equality t1.b = t3.b, same as above. +# "rows" must be estimated to 1 +EXPLAIN EXTENDED SELECT * FROM t1 JOIN t3 ON t1.a = t3.a AND t1.b = t3.b; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using where +1 SIMPLE t3 ref key_ab key_ab 10 test.t1.a,test.t1.b 1 100.00 Using index +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t3`.`a` AS `a`,`test`.`t3`.`b` AS `b` from `test`.`t1` join `test`.`t3` where `test`.`t3`.`a` = `test`.`t1`.`a` and `test`.`t3`.`b` = `test`.`t1`.`b` +# Rows estimation for not NULL-rejecting conditions are not affected +# ("rows" > 1 is expected) +EXPLAIN EXTENDED SELECT * FROM t1 JOIN t3 ON t1.a = t3.a; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using where +1 SIMPLE t3 ref key_ab key_ab 5 test.t1.a 90 100.00 Using index +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t3`.`a` AS `a`,`test`.`t3`.`b` AS `b` from `test`.`t1` join `test`.`t3` where `test`.`t3`.`a` = `test`.`t1`.`a` +EXPLAIN EXTENDED SELECT * FROM t1 JOIN t3 ON t1.a = t3.a AND t1.b <=> t3.b; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using where +1 SIMPLE t3 ref key_ab key_ab 10 test.t1.a,test.t1.b 11 100.00 Using where; Using index +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t3`.`a` AS `a`,`test`.`t3`.`b` AS `b` from `test`.`t1` join `test`.`t3` where `test`.`t3`.`a` = `test`.`t1`.`a` and `test`.`t1`.`b` <=> `test`.`t3`.`b` +EXPLAIN EXTENDED SELECT * FROM t1 JOIN t3 ON t1.a = t3.a AND t3.b is NULL; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using where +1 SIMPLE t3 ref key_ab key_ab 10 test.t1.a,const 11 100.00 Using where; Using index +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t3`.`a` AS `a`,`test`.`t3`.`b` AS `b` from `test`.`t1` join `test`.`t3` where `test`.`t3`.`a` = `test`.`t1`.`a` and `test`.`t3`.`b` is null +# In the old mode (null-aware estimation is not enabled), "rows" > 1 +SET @@new_mode = ""; +EXPLAIN EXTENDED SELECT * FROM t1 JOIN t2 ON t1.a = t2.a AND t1.b = t2.b; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using where +1 SIMPLE t2 ref key_b key_b 5 test.t1.b 100 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t2`.`a` AS `a`,`test`.`t2`.`b` AS `b` from `test`.`t1` join `test`.`t2` where `test`.`t2`.`a` = `test`.`t1`.`a` and `test`.`t2`.`b` = `test`.`t1`.`b` +# Insert some non-NULL values and re-collect the stats +INSERT INTO t3 SELECT 1, 1 FROM seq_1_to_100; +ANALYZE TABLE t3 PERSISTENT FOR COLUMNS (b) INDEXES (key_ab); +Table Op Msg_type Msg_text +test.t3 analyze status Engine-independent statistics collected +test.t3 analyze status OK +SET @@new_mode = "FIX_INDEX_STATS_FOR_ALL_NULLS"; +EXPLAIN EXTENDED SELECT * FROM t1 JOIN t3 ON t1.a = t3.a AND t1.b = t3.b; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using where +1 SIMPLE t3 ref key_ab key_ab 10 test.t1.a,test.t1.b 100 100.00 Using index +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t3`.`a` AS `a`,`test`.`t3`.`b` AS `b` from `test`.`t1` join `test`.`t3` where `test`.`t3`.`a` = `test`.`t1`.`a` and `test`.`t3`.`b` = `test`.`t1`.`b` +# Test composite index for 3 columns. Key prefix is used for access +CREATE TABLE t4 (a INT, b INT, c INT, KEY key_abc(a,b,c)); +# All t3.b values are NULL +INSERT INTO t4 SELECT seq/10, NULL, seq/10 FROM seq_1_to_1000; +ANALYZE TABLE t4 PERSISTENT FOR COLUMNS(b) INDEXES(key_abc); +Table Op Msg_type Msg_text +test.t4 analyze status Engine-independent statistics collected +test.t4 analyze status Table is already up to date +# NULL-rejecting equality t1.b = t3.b, same as above. +# "rows" must be estimated to 1 +EXPLAIN EXTENDED SELECT * FROM t1 JOIN t4 ON t1.a = t4.a AND t1.b = t4.b; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using where +1 SIMPLE t4 ref key_abc key_abc 10 test.t1.a,test.t1.b 1 100.00 Using index +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t4`.`a` AS `a`,`test`.`t4`.`b` AS `b`,`test`.`t4`.`c` AS `c` from `test`.`t1` join `test`.`t4` where `test`.`t4`.`a` = `test`.`t1`.`a` and `test`.`t4`.`b` = `test`.`t1`.`b` +EXPLAIN EXTENDED SELECT * FROM t1 JOIN t4 ON t1.a = t4.a AND t1.b = t4.b and t1.b = t4.c; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using where +1 SIMPLE t4 ref key_abc key_abc 15 test.t1.a,test.t1.b,test.t1.b 1 100.00 Using index +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t4`.`a` AS `a`,`test`.`t4`.`b` AS `b`,`test`.`t4`.`c` AS `c` from `test`.`t1` join `test`.`t4` where `test`.`t4`.`a` = `test`.`t1`.`a` and `test`.`t4`.`b` = `test`.`t1`.`b` and `test`.`t4`.`c` = `test`.`t1`.`b` +# "rows" expected to be > 1 +EXPLAIN EXTENDED SELECT * FROM t1 JOIN t4 ON t1.a = t4.a; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using where +1 SIMPLE t4 ref key_abc key_abc 5 test.t1.a 9 100.00 Using index +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t4`.`a` AS `a`,`test`.`t4`.`b` AS `b`,`test`.`t4`.`c` AS `c` from `test`.`t1` join `test`.`t4` where `test`.`t4`.`a` = `test`.`t1`.`a` +EXPLAIN EXTENDED SELECT * FROM t1 JOIN t4 ON t1.a = t4.a AND t1.b <=> t4.c; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using where +1 SIMPLE t4 ref key_abc key_abc 5 test.t1.a 9 100.00 Using where; Using index +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t4`.`a` AS `a`,`test`.`t4`.`b` AS `b`,`test`.`t4`.`c` AS `c` from `test`.`t1` join `test`.`t4` where `test`.`t4`.`a` = `test`.`t1`.`a` and `test`.`t1`.`b` <=> `test`.`t4`.`c` +DROP TABLE t1, t2, t3, t4; +# Test for partially covered column +CREATE TABLE t1 (a VARCHAR(10)); +INSERT INTO t1 SELECT seq FROM seq_1_to_10; +CREATE TABLE t2 ( +a VARCHAR(10), +b VARCHAR(10), +INDEX i1(a, b(5)) +); +INSERT INTO t2 SELECT seq, NULL FROM seq_1_to_1000; +ANALYZE TABLE t2 PERSISTENT FOR COLUMNS (b) INDEXES (i1); +Table Op Msg_type Msg_text +test.t2 analyze status Engine-independent statistics collected +test.t2 analyze status Table is already up to date +EXPLAIN SELECT * FROM t1, t2 WHERE t2.a=t1.a AND t2.b=t1.a; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 10 Using where +1 SIMPLE t2 ref i1 i1 66 test.t1.a,test.t1.a 1 Using where +SET @@new_mode = @session_start_value; +DROP TABLE t1, t2; diff --git a/mysql-test/main/null_aware_cardinality.test b/mysql-test/main/null_aware_cardinality.test new file mode 100644 index 00000000000..eead918367a --- /dev/null +++ b/mysql-test/main/null_aware_cardinality.test @@ -0,0 +1,103 @@ +--source include/have_sequence.inc + +SET @session_start_value = @@new_mode; + +--echo # Small driving table +CREATE TABLE t1 (a INT, b INT); +INSERT INTO t1 VALUES (1, 1), (2, 2000),(3,300); + +ANALYZE TABLE t1 PERSISTENT FOR ALL; + +--echo # Table that will be accessed by an index lookup (`ref` access) +CREATE TABLE t2 (a INT, b INT, KEY key_b(b)); +--echo # All t11.b values are NULL +INSERT INTO t2 SELECT seq/100, NULL FROM seq_1_to_1000; + +ANALYZE TABLE t2 PERSISTENT FOR ALL; + +SET @@new_mode = "FIX_INDEX_STATS_FOR_ALL_NULLS"; + +--echo # NULL-rejecting equality t1.b = t2.b will not return any matches +--echo # because all values of t2.b are NULL. So "rows" = 1 for t2 where 1 is +--echo # a special value meaning "very few" rows +EXPLAIN EXTENDED SELECT * FROM t1 JOIN t2 ON t1.a = t2.a AND t1.b = t2.b; + +--echo # However, rows estimation for not NULL-rejecting conditions +--echo # must not be affected ("rows" > 1 is expected) +EXPLAIN EXTENDED SELECT * FROM t1 JOIN t2 ON t1.a = t2.a AND t1.b <=> t2.b; + +--echo # Insert some non-NULL values and re-collect the stats +INSERT INTO t2 SELECT 1, 1 FROM seq_1_to_100; + +ANALYZE TABLE t2 PERSISTENT FOR COLUMNS (b) INDEXES (key_b); + +EXPLAIN EXTENDED SELECT * FROM t1 JOIN t2 ON t1.a = t2.a AND t1.b = t2.b; + +--echo # Test composite index for two columns. Key prefix is used for access +CREATE TABLE t3 (a INT, b INT, KEY key_ab(a,b)); +--echo # All t3.b values are NULL +INSERT INTO t3 SELECT seq/100, NULL FROM seq_1_to_1000; + +ANALYZE TABLE t3 PERSISTENT FOR COLUMNS(b) INDEXES(key_ab); + +--echo # NULL-rejecting equality t1.b = t3.b, same as above. +--echo # "rows" must be estimated to 1 +EXPLAIN EXTENDED SELECT * FROM t1 JOIN t3 ON t1.a = t3.a AND t1.b = t3.b; + +--echo # Rows estimation for not NULL-rejecting conditions are not affected +--echo # ("rows" > 1 is expected) +EXPLAIN EXTENDED SELECT * FROM t1 JOIN t3 ON t1.a = t3.a; + +EXPLAIN EXTENDED SELECT * FROM t1 JOIN t3 ON t1.a = t3.a AND t1.b <=> t3.b; + +EXPLAIN EXTENDED SELECT * FROM t1 JOIN t3 ON t1.a = t3.a AND t3.b is NULL; + +--echo # In the old mode (null-aware estimation is not enabled), "rows" > 1 +SET @@new_mode = ""; +EXPLAIN EXTENDED SELECT * FROM t1 JOIN t2 ON t1.a = t2.a AND t1.b = t2.b; + +--echo # Insert some non-NULL values and re-collect the stats +INSERT INTO t3 SELECT 1, 1 FROM seq_1_to_100; + +ANALYZE TABLE t3 PERSISTENT FOR COLUMNS (b) INDEXES (key_ab); + +SET @@new_mode = "FIX_INDEX_STATS_FOR_ALL_NULLS"; +EXPLAIN EXTENDED SELECT * FROM t1 JOIN t3 ON t1.a = t3.a AND t1.b = t3.b; + +--echo # Test composite index for 3 columns. Key prefix is used for access +CREATE TABLE t4 (a INT, b INT, c INT, KEY key_abc(a,b,c)); + +--echo # All t3.b values are NULL +INSERT INTO t4 SELECT seq/10, NULL, seq/10 FROM seq_1_to_1000; + +ANALYZE TABLE t4 PERSISTENT FOR COLUMNS(b) INDEXES(key_abc); + +--echo # NULL-rejecting equality t1.b = t3.b, same as above. +--echo # "rows" must be estimated to 1 +EXPLAIN EXTENDED SELECT * FROM t1 JOIN t4 ON t1.a = t4.a AND t1.b = t4.b; + +EXPLAIN EXTENDED SELECT * FROM t1 JOIN t4 ON t1.a = t4.a AND t1.b = t4.b and t1.b = t4.c; + +--echo # "rows" expected to be > 1 +EXPLAIN EXTENDED SELECT * FROM t1 JOIN t4 ON t1.a = t4.a; + +EXPLAIN EXTENDED SELECT * FROM t1 JOIN t4 ON t1.a = t4.a AND t1.b <=> t4.c; + +DROP TABLE t1, t2, t3, t4; + +--echo # Test for partially covered column +CREATE TABLE t1 (a VARCHAR(10)); +INSERT INTO t1 SELECT seq FROM seq_1_to_10; + +CREATE TABLE t2 ( + a VARCHAR(10), + b VARCHAR(10), + INDEX i1(a, b(5)) +); +INSERT INTO t2 SELECT seq, NULL FROM seq_1_to_1000; +ANALYZE TABLE t2 PERSISTENT FOR COLUMNS (b) INDEXES (i1); + +EXPLAIN SELECT * FROM t1, t2 WHERE t2.a=t1.a AND t2.b=t1.a; + +SET @@new_mode = @session_start_value; +DROP TABLE t1, t2; \ No newline at end of file diff --git a/mysql-test/main/sp-default-param.result b/mysql-test/main/sp-default-param.result index 9a6867a467d..7cef0fe9916 100644 --- a/mysql-test/main/sp-default-param.result +++ b/mysql-test/main/sp-default-param.result @@ -130,85 +130,4 @@ SET p2 = p2 + 1; END; DELIMITER ;$$ ERROR 42000: This version of MariaDB doesn't yet support 'IN sparam1 DEFAULT , OUT spparam2 ' -# -# MDEV-37489: SIGSEGV in get_param_default_value | store_schema_params -# -CREATE OR REPLACE PROCEDURE p0 (x INT DEFAULT func()) -BEGIN -SELECT x; -END; -// -SET SESSION max_session_mem_used=8192; -CALL p0(); -ERROR HY000: The MariaDB server is running with the --max-session-mem-used=8192 option so it cannot execute this statement -SET @@max_session_mem_used=DEFAULT; -CALL p0(); -ERROR 42000: FUNCTION test.func does not exist -SELECT * FROM information_schema.PARAMETERS where specific_name = 'p0'; -SPECIFIC_CATALOG SPECIFIC_SCHEMA SPECIFIC_NAME ORDINAL_POSITION PARAMETER_MODE PARAMETER_NAME DATA_TYPE CHARACTER_MAXIMUM_LENGTH CHARACTER_OCTET_LENGTH NUMERIC_PRECISION NUMERIC_SCALE DATETIME_PRECISION CHARACTER_SET_NAME COLLATION_NAME DTD_IDENTIFIER ROUTINE_TYPE -def test p0 1 IN x int NULL NULL 10 0 NULL NULL NULL int(11) PROCEDURE -# with func() defined -CREATE FUNCTION func(x INT DEFAULT 10) RETURNS INT -BEGIN -RETURN x; -END; -// -CREATE OR REPLACE PROCEDURE p0 (x INT DEFAULT func()) -BEGIN -SELECT x; -END; -// -SET SESSION max_session_mem_used=8192; -CALL p0(); -ERROR HY000: The MariaDB server is running with the --max-session-mem-used=8192 option so it cannot execute this statement -SET @@max_session_mem_used=DEFAULT; -CALL p0(); -x -10 -SELECT * FROM information_schema.PARAMETERS where specific_name = 'p0'; -SPECIFIC_CATALOG SPECIFIC_SCHEMA SPECIFIC_NAME ORDINAL_POSITION PARAMETER_MODE PARAMETER_NAME DATA_TYPE CHARACTER_MAXIMUM_LENGTH CHARACTER_OCTET_LENGTH NUMERIC_PRECISION NUMERIC_SCALE DATETIME_PRECISION CHARACTER_SET_NAME COLLATION_NAME DTD_IDENTIFIER ROUTINE_TYPE -def test p0 1 IN x int NULL NULL 10 0 NULL NULL NULL int(11) PROCEDURE -# with multiple functions -CREATE FUNCTION func2(x INT DEFAULT 10) RETURNS INT -BEGIN -RETURN x; -END; -// -CREATE OR REPLACE PROCEDURE p0 (x INT DEFAULT func(), y INT DEFAULT func2()) -BEGIN -SELECT x, y; -END; -// -SET SESSION max_session_mem_used=8192; -CALL p0(); -ERROR HY000: The MariaDB server is running with the --max-session-mem-used=8192 option so it cannot execute this statement -SET @@max_session_mem_used=DEFAULT; -CALL p0(); -x y -10 10 -SELECT * FROM information_schema.PARAMETERS where specific_name = 'p0'; -SPECIFIC_CATALOG SPECIFIC_SCHEMA SPECIFIC_NAME ORDINAL_POSITION PARAMETER_MODE PARAMETER_NAME DATA_TYPE CHARACTER_MAXIMUM_LENGTH CHARACTER_OCTET_LENGTH NUMERIC_PRECISION NUMERIC_SCALE DATETIME_PRECISION CHARACTER_SET_NAME COLLATION_NAME DTD_IDENTIFIER ROUTINE_TYPE -def test p0 1 IN x int NULL NULL 10 0 NULL NULL NULL int(11) PROCEDURE -def test p0 2 IN y int NULL NULL 10 0 NULL NULL NULL int(11) PROCEDURE -# with function and constant default param -CREATE OR REPLACE PROCEDURE p0 (x INT DEFAULT func(), y INT DEFAULT func2(), z INT DEFAULT 10) -BEGIN -SELECT x, y, z; -END; -// -SET SESSION max_session_mem_used=8192; -CALL p0(); -ERROR HY000: The MariaDB server is running with the --max-session-mem-used=8192 option so it cannot execute this statement -SET @@max_session_mem_used=DEFAULT; -CALL p0(); -x y z -10 10 10 -SELECT * FROM information_schema.PARAMETERS where specific_name = 'p0'; -SPECIFIC_CATALOG SPECIFIC_SCHEMA SPECIFIC_NAME ORDINAL_POSITION PARAMETER_MODE PARAMETER_NAME DATA_TYPE CHARACTER_MAXIMUM_LENGTH CHARACTER_OCTET_LENGTH NUMERIC_PRECISION NUMERIC_SCALE DATETIME_PRECISION CHARACTER_SET_NAME COLLATION_NAME DTD_IDENTIFIER ROUTINE_TYPE -def test p0 1 IN x int NULL NULL 10 0 NULL NULL NULL int(11) PROCEDURE -def test p0 2 IN y int NULL NULL 10 0 NULL NULL NULL int(11) PROCEDURE -def test p0 3 IN z int NULL NULL 10 0 NULL NULL NULL int(11) PROCEDURE -DROP PROCEDURE p0; -DROP FUNCTION func; -DROP FUNCTION func2; # End of 11.8 tests diff --git a/mysql-test/main/sp-default-param.test b/mysql-test/main/sp-default-param.test index f615ee793e6..cc30b6d78f6 100644 --- a/mysql-test/main/sp-default-param.test +++ b/mysql-test/main/sp-default-param.test @@ -139,96 +139,4 @@ BEGIN END; DELIMITER ;$$ ---echo # ---echo # MDEV-37489: SIGSEGV in get_param_default_value | store_schema_params ---echo # - ---DELIMITER // -CREATE OR REPLACE PROCEDURE p0 (x INT DEFAULT func()) -BEGIN - SELECT x; -END; -// ---DELIMITER ; - -SET SESSION max_session_mem_used=8192; ---ERROR ER_OPTION_PREVENTS_STATEMENT -CALL p0(); - -SET @@max_session_mem_used=DEFAULT; ---ERROR ER_SP_DOES_NOT_EXIST -CALL p0(); - -SELECT * FROM information_schema.PARAMETERS where specific_name = 'p0'; - ---echo # with func() defined ---DELIMITER // -CREATE FUNCTION func(x INT DEFAULT 10) RETURNS INT -BEGIN - RETURN x; -END; -// - -CREATE OR REPLACE PROCEDURE p0 (x INT DEFAULT func()) -BEGIN - SELECT x; -END; -// ---DELIMITER ; - -SET SESSION max_session_mem_used=8192; ---ERROR ER_OPTION_PREVENTS_STATEMENT -CALL p0(); - -SET @@max_session_mem_used=DEFAULT; -CALL p0(); - -SELECT * FROM information_schema.PARAMETERS where specific_name = 'p0'; - ---echo # with multiple functions ---DELIMITER // -CREATE FUNCTION func2(x INT DEFAULT 10) RETURNS INT -BEGIN - RETURN x; -END; -// - -CREATE OR REPLACE PROCEDURE p0 (x INT DEFAULT func(), y INT DEFAULT func2()) -BEGIN - SELECT x, y; -END; -// ---DELIMITER ; - -SET SESSION max_session_mem_used=8192; ---ERROR ER_OPTION_PREVENTS_STATEMENT -CALL p0(); - -SET @@max_session_mem_used=DEFAULT; -CALL p0(); - -SELECT * FROM information_schema.PARAMETERS where specific_name = 'p0'; - ---echo # with function and constant default param ---DELIMITER // -CREATE OR REPLACE PROCEDURE p0 (x INT DEFAULT func(), y INT DEFAULT func2(), z INT DEFAULT 10) -BEGIN - SELECT x, y, z; -END; -// ---DELIMITER ; - -SET SESSION max_session_mem_used=8192; ---ERROR ER_OPTION_PREVENTS_STATEMENT -CALL p0(); - -SET @@max_session_mem_used=DEFAULT; -CALL p0(); - -SELECT * FROM information_schema.PARAMETERS where specific_name = 'p0'; - -DROP PROCEDURE p0; -DROP FUNCTION func; -DROP FUNCTION func2; - --echo # End of 11.8 tests diff --git a/mysql-test/suite/compat/oracle/r/sp-default-param.result b/mysql-test/suite/compat/oracle/r/sp-default-param.result index 7cbe7b18612..dc3374c9274 100644 --- a/mysql-test/suite/compat/oracle/r/sp-default-param.result +++ b/mysql-test/suite/compat/oracle/r/sp-default-param.result @@ -222,91 +222,4 @@ SET p2 = p2 + 1; END; DELIMITER ;$$ ERROR 42000: This version of MariaDB doesn't yet support 'sparam1 IN DEFAULT , spparam2 OUT ' -# -# MDEV-37489: SIGSEGV in get_param_default_value | store_schema_params -# -CREATE OR REPLACE PROCEDURE p0 (x INT DEFAULT func()) -AS -BEGIN -SELECT x; -END; -// -SET SESSION max_session_mem_used=8192; -CALL p0(); -ERROR HY000: The MariaDB server is running with the --max-session-mem-used=8192 option so it cannot execute this statement -SET @@max_session_mem_used=DEFAULT; -CALL p0(); -ERROR 42000: FUNCTION test.func does not exist -SELECT * FROM information_schema.PARAMETERS where specific_name = 'p0'; -SPECIFIC_CATALOG SPECIFIC_SCHEMA SPECIFIC_NAME ORDINAL_POSITION PARAMETER_MODE PARAMETER_NAME DATA_TYPE CHARACTER_MAXIMUM_LENGTH CHARACTER_OCTET_LENGTH NUMERIC_PRECISION NUMERIC_SCALE DATETIME_PRECISION CHARACTER_SET_NAME COLLATION_NAME DTD_IDENTIFIER ROUTINE_TYPE -def test p0 1 IN x int NULL NULL 10 0 NULL NULL NULL int(11) PROCEDURE -# with func() defined -CREATE FUNCTION func(x INT DEFAULT 10) RETURN INT -AS -BEGIN -RETURN x; -END; -// -CREATE OR REPLACE PROCEDURE p0 (x INT DEFAULT func()) -AS -BEGIN -SELECT x; -END; -// -SET SESSION max_session_mem_used=8192; -CALL p0(); -ERROR HY000: The MariaDB server is running with the --max-session-mem-used=8192 option so it cannot execute this statement -SET @@max_session_mem_used=DEFAULT; -CALL p0(); -x -10 -SELECT * FROM information_schema.PARAMETERS where specific_name = 'p0'; -SPECIFIC_CATALOG SPECIFIC_SCHEMA SPECIFIC_NAME ORDINAL_POSITION PARAMETER_MODE PARAMETER_NAME DATA_TYPE CHARACTER_MAXIMUM_LENGTH CHARACTER_OCTET_LENGTH NUMERIC_PRECISION NUMERIC_SCALE DATETIME_PRECISION CHARACTER_SET_NAME COLLATION_NAME DTD_IDENTIFIER ROUTINE_TYPE -def test p0 1 IN x int NULL NULL 10 0 NULL NULL NULL int(11) PROCEDURE -# with multiple functions -CREATE FUNCTION func2(x INT DEFAULT 10) RETURN INT -AS -BEGIN -RETURN x; -END; -// -CREATE OR REPLACE PROCEDURE p0 (x INT DEFAULT func(), y INT DEFAULT func2()) -AS -BEGIN -SELECT x, y; -END; -// -SET SESSION max_session_mem_used=8192; -CALL p0(); -ERROR HY000: The MariaDB server is running with the --max-session-mem-used=8192 option so it cannot execute this statement -SET @@max_session_mem_used=DEFAULT; -CALL p0(); -x y -10 10 -SELECT * FROM information_schema.PARAMETERS where specific_name = 'p0'; -SPECIFIC_CATALOG SPECIFIC_SCHEMA SPECIFIC_NAME ORDINAL_POSITION PARAMETER_MODE PARAMETER_NAME DATA_TYPE CHARACTER_MAXIMUM_LENGTH CHARACTER_OCTET_LENGTH NUMERIC_PRECISION NUMERIC_SCALE DATETIME_PRECISION CHARACTER_SET_NAME COLLATION_NAME DTD_IDENTIFIER ROUTINE_TYPE -def test p0 1 IN x int NULL NULL 10 0 NULL NULL NULL int(11) PROCEDURE -def test p0 2 IN y int NULL NULL 10 0 NULL NULL NULL int(11) PROCEDURE -# with function and constant default param -CREATE OR REPLACE PROCEDURE p0 (x INT DEFAULT func(), y INT DEFAULT func2(), z INT DEFAULT 10) -AS -BEGIN -SELECT x, y, z; -END; -// -SET SESSION max_session_mem_used=8192; -CALL p0(); -ERROR HY000: The MariaDB server is running with the --max-session-mem-used=8192 option so it cannot execute this statement -SET @@max_session_mem_used=DEFAULT; -CALL p0(); -x y z -10 10 10 -SELECT * FROM information_schema.PARAMETERS where specific_name = 'p0'; -SPECIFIC_CATALOG SPECIFIC_SCHEMA SPECIFIC_NAME ORDINAL_POSITION PARAMETER_MODE PARAMETER_NAME DATA_TYPE CHARACTER_MAXIMUM_LENGTH CHARACTER_OCTET_LENGTH NUMERIC_PRECISION NUMERIC_SCALE DATETIME_PRECISION CHARACTER_SET_NAME COLLATION_NAME DTD_IDENTIFIER ROUTINE_TYPE -def test p0 1 IN x int NULL NULL 10 0 NULL NULL NULL int(11) PROCEDURE -def test p0 2 IN y int NULL NULL 10 0 NULL NULL NULL int(11) PROCEDURE -def test p0 3 IN z int NULL NULL 10 0 NULL NULL NULL int(11) PROCEDURE -DROP PROCEDURE p0; -DROP FUNCTION func; -DROP FUNCTION func2; # End of 11.8 tests diff --git a/mysql-test/suite/compat/oracle/t/sp-default-param.test b/mysql-test/suite/compat/oracle/t/sp-default-param.test index 45d02b9cdc9..a57828d7164 100644 --- a/mysql-test/suite/compat/oracle/t/sp-default-param.test +++ b/mysql-test/suite/compat/oracle/t/sp-default-param.test @@ -244,102 +244,4 @@ BEGIN END; DELIMITER ;$$ ---echo # ---echo # MDEV-37489: SIGSEGV in get_param_default_value | store_schema_params ---echo # - ---DELIMITER // -CREATE OR REPLACE PROCEDURE p0 (x INT DEFAULT func()) -AS -BEGIN - SELECT x; -END; -// ---DELIMITER ; - -SET SESSION max_session_mem_used=8192; ---ERROR ER_OPTION_PREVENTS_STATEMENT -CALL p0(); - -SET @@max_session_mem_used=DEFAULT; ---ERROR ER_SP_DOES_NOT_EXIST -CALL p0(); - -SELECT * FROM information_schema.PARAMETERS where specific_name = 'p0'; - ---echo # with func() defined ---DELIMITER // -CREATE FUNCTION func(x INT DEFAULT 10) RETURN INT -AS -BEGIN - RETURN x; -END; -// - -CREATE OR REPLACE PROCEDURE p0 (x INT DEFAULT func()) -AS -BEGIN - SELECT x; -END; -// ---DELIMITER ; - -SET SESSION max_session_mem_used=8192; ---ERROR ER_OPTION_PREVENTS_STATEMENT -CALL p0(); - -SET @@max_session_mem_used=DEFAULT; -CALL p0(); - -SELECT * FROM information_schema.PARAMETERS where specific_name = 'p0'; - ---echo # with multiple functions ---DELIMITER // -CREATE FUNCTION func2(x INT DEFAULT 10) RETURN INT -AS -BEGIN - RETURN x; -END; -// - -CREATE OR REPLACE PROCEDURE p0 (x INT DEFAULT func(), y INT DEFAULT func2()) -AS -BEGIN - SELECT x, y; -END; -// ---DELIMITER ; - -SET SESSION max_session_mem_used=8192; ---ERROR ER_OPTION_PREVENTS_STATEMENT -CALL p0(); - -SET @@max_session_mem_used=DEFAULT; -CALL p0(); - -SELECT * FROM information_schema.PARAMETERS where specific_name = 'p0'; - ---echo # with function and constant default param ---DELIMITER // -CREATE OR REPLACE PROCEDURE p0 (x INT DEFAULT func(), y INT DEFAULT func2(), z INT DEFAULT 10) -AS -BEGIN - SELECT x, y, z; -END; -// ---DELIMITER ; - -SET SESSION max_session_mem_used=8192; ---ERROR ER_OPTION_PREVENTS_STATEMENT -CALL p0(); - -SET @@max_session_mem_used=DEFAULT; -CALL p0(); - -SELECT * FROM information_schema.PARAMETERS where specific_name = 'p0'; - -DROP PROCEDURE p0; -DROP FUNCTION func; -DROP FUNCTION func2; - --echo # End of 11.8 tests diff --git a/mysql-test/suite/galera/r/MDEV-37857.result b/mysql-test/suite/galera/r/MDEV-37857.result index 243a4b4b727..33497c50da6 100644 --- a/mysql-test/suite/galera/r/MDEV-37857.result +++ b/mysql-test/suite/galera/r/MDEV-37857.result @@ -5,7 +5,7 @@ drop view if exists t1; connection node_2; SELECT @@character_set_server, @@collation_server; @@character_set_server @@collation_server -latin1 latin1_swedish_ci +utf8mb4 utf8mb4_uca1400_ai_ci SELECT @@character_set_client, @@collation_connection; @@character_set_client @@collation_connection latin1 latin1_swedish_ci @@ -13,7 +13,7 @@ connection node_1; SET NAMES latin1 COLLATE latin1_bin; SELECT @@character_set_server, @@collation_server; @@character_set_server @@collation_server -latin1 latin1_swedish_ci +utf8mb4 utf8mb4_uca1400_ai_ci SELECT @@character_set_client, @@collation_connection; @@character_set_client @@collation_connection latin1 latin1_bin @@ -27,7 +27,7 @@ SHOW CREATE TABLE t1; Table Create Table t1 CREATE TABLE `t1` ( `a` int(11) DEFAULT NULL -) ENGINE=InnoDB DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci connection node_2; SHOW CREATE VIEW v1; View Create View character_set_client collation_connection @@ -36,7 +36,7 @@ SHOW CREATE TABLE t1; Table Create Table t1 CREATE TABLE `t1` ( `a` int(11) DEFAULT NULL -) ENGINE=InnoDB DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci connection node_1; DROP VIEW v1; DROP TABLE t1; diff --git a/mysql-test/suite/galera/r/galera_create_function.result b/mysql-test/suite/galera/r/galera_create_function.result index d01989c0603..67eb582ee06 100644 --- a/mysql-test/suite/galera/r/galera_create_function.result +++ b/mysql-test/suite/galera/r/galera_create_function.result @@ -37,7 +37,7 @@ Function sql_mode Create Function character_set_client collation_connection Data f1 STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION CREATE DEFINER=`user1`@`%` FUNCTION `f1`(param INTEGER) RETURNS varchar(200) CHARSET utf8mb4 COLLATE utf8mb4_uca1400_ai_ci MODIFIES SQL DATA COMMENT 'f1_comment' -RETURN 'abc' utf8mb4 utf8mb4_uca1400_ai_ci utf8mb4_uca1400_ai_ci +RETURN 'abc' latin1 latin1_swedish_ci utf8mb4_uca1400_ai_ci connection node_1; SHOW CREATE FUNCTION f2; Function sql_mode Create Function character_set_client collation_connection Database Collation @@ -53,7 +53,7 @@ f2 STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_ NO SQL DETERMINISTIC SQL SECURITY INVOKER -RETURN 123 utf8mb4 utf8mb4_uca1400_ai_ci utf8mb4_uca1400_ai_ci +RETURN 123 latin1 latin1_swedish_ci utf8mb4_uca1400_ai_ci SELECT f1(1) = 'abc'; f1(1) = 'abc' 1 diff --git a/mysql-test/suite/galera/r/galera_create_procedure.result b/mysql-test/suite/galera/r/galera_create_procedure.result index c6606519405..f011a33b4a3 100644 --- a/mysql-test/suite/galera/r/galera_create_procedure.result +++ b/mysql-test/suite/galera/r/galera_create_procedure.result @@ -36,7 +36,7 @@ Procedure sql_mode Create Procedure character_set_client collation_connection Da p1 STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION CREATE DEFINER=`user1`@`%` PROCEDURE `p1`(IN param1 INTEGER, OUT param2 INTEGER, INOUT param3 INTEGER) MODIFIES SQL DATA COMMENT 'p1_comment' -INSERT INTO t1 VALUES (1) utf8mb4 utf8mb4_uca1400_ai_ci utf8mb4_uca1400_ai_ci +INSERT INTO t1 VALUES (1) latin1 latin1_swedish_ci utf8mb4_uca1400_ai_ci connection node_1; SHOW CREATE PROCEDURE p2; Procedure sql_mode Create Procedure character_set_client collation_connection Database Collation @@ -52,7 +52,7 @@ p2 STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_ NO SQL DETERMINISTIC SQL SECURITY INVOKER -BEGIN END utf8mb4 utf8mb4_uca1400_ai_ci utf8mb4_uca1400_ai_ci +BEGIN END latin1 latin1_swedish_ci utf8mb4_uca1400_ai_ci CALL p1(@a, @b, @c); CALL p2('abc'); connection node_1; diff --git a/mysql-test/suite/galera/r/galera_partitioned_tables.result b/mysql-test/suite/galera/r/galera_partitioned_tables.result index 4d856fefa5a..ccfc9dacc2a 100644 --- a/mysql-test/suite/galera/r/galera_partitioned_tables.result +++ b/mysql-test/suite/galera/r/galera_partitioned_tables.result @@ -59,10 +59,10 @@ t2_v2 CREATE TABLE `t2_v2` ( PARTITIONS 2 SHOW CREATE VIEW x1; View Create View character_set_client collation_connection -x1 CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `x1` AS select `t1_v2`.`v1` AS `v1`,`t1_v2`.`v2` AS `v2`,`t1_v2`.`v3` AS `v3` from `t1_v2` utf8mb4 utf8mb4_uca1400_ai_ci +x1 CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `x1` AS select `t1_v2`.`v1` AS `v1`,`t1_v2`.`v2` AS `v2`,`t1_v2`.`v3` AS `v3` from `t1_v2` latin1 latin1_swedish_ci SHOW CREATE VIEW x2; View Create View character_set_client collation_connection -x2 CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `x2` AS select `t2_v2`.`v1` AS `v1`,`t2_v2`.`v2` AS `v2`,`t2_v2`.`v3` AS `v3` from `t2_v2` utf8mb4 utf8mb4_uca1400_ai_ci +x2 CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `x2` AS select `t2_v2`.`v1` AS `v1`,`t2_v2`.`v2` AS `v2`,`t2_v2`.`v3` AS `v3` from `t2_v2` latin1 latin1_swedish_ci SELECT * FROM t1_v2; v1 v2 v3 SELECT * FROM t2_v2; @@ -148,7 +148,7 @@ t2 CREATE TABLE `t2` ( PARTITIONS 2 SHOW CREATE VIEW x1; View Create View character_set_client collation_connection -x1 CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `x1` AS select `t1_v2`.`v1` AS `v1`,`t1_v2`.`v2` AS `v2` from `t1_v2` utf8mb4 utf8mb4_uca1400_ai_ci +x1 CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `x1` AS select `t1_v2`.`v1` AS `v1`,`t1_v2`.`v2` AS `v2` from `t1_v2` latin1 latin1_swedish_ci SELECT * FROM t1_v2; v1 v2 SELECT * FROM t2; diff --git a/mysql-test/suite/galera/r/galera_sync_wait_show.result b/mysql-test/suite/galera/r/galera_sync_wait_show.result index 864b045f4b1..78d92c5aded 100644 --- a/mysql-test/suite/galera/r/galera_sync_wait_show.result +++ b/mysql-test/suite/galera/r/galera_sync_wait_show.result @@ -15,7 +15,7 @@ connection node_2; SHOW CREATE PROCEDURE p1; Procedure sql_mode Create Procedure character_set_client collation_connection Database Collation p1 STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION CREATE DEFINER=`root`@`localhost` PROCEDURE `p1`() -SELECT 1 FROM DUAL utf8mb4 utf8mb4_uca1400_ai_ci utf8mb4_uca1400_ai_ci +SELECT 1 FROM DUAL latin1 latin1_swedish_ci utf8mb4_uca1400_ai_ci DROP PROCEDURE p1; connection node_1; CREATE PROCEDURE p1 () SELECT 1 FROM DUAL; @@ -30,7 +30,7 @@ connection node_2; SHOW CREATE FUNCTION f1; Function sql_mode Create Function character_set_client collation_connection Database Collation f1 STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION CREATE DEFINER=`root`@`localhost` FUNCTION `f1`() RETURNS int(11) -RETURN 123 utf8mb4 utf8mb4_uca1400_ai_ci utf8mb4_uca1400_ai_ci +RETURN 123 latin1 latin1_swedish_ci utf8mb4_uca1400_ai_ci DROP FUNCTION f1; connection node_1; CREATE FUNCTION f1 () RETURNS INTEGER RETURN 123; @@ -45,12 +45,12 @@ CREATE TRIGGER tr1 BEFORE INSERT ON t1 FOR EACH ROW SET NEW.f1 = 'a'; connection node_2; SHOW CREATE TRIGGER tr1; Trigger sql_mode SQL Original Statement character_set_client collation_connection Database Collation Created -tr1 STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION CREATE DEFINER=`root`@`localhost` TRIGGER tr1 BEFORE INSERT ON t1 FOR EACH ROW SET NEW.f1 = 'a' utf8mb4 utf8mb4_uca1400_ai_ci utf8mb4_uca1400_ai_ci # +tr1 STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION CREATE DEFINER=`root`@`localhost` TRIGGER tr1 BEFORE INSERT ON t1 FOR EACH ROW SET NEW.f1 = 'a' latin1 latin1_swedish_ci utf8mb4_uca1400_ai_ci # DROP TABLE t1; connection node_1; CREATE EVENT event1 ON SCHEDULE AT '2038-01-01 23:59:59' DO SELECT 1; connection node_2; SHOW CREATE EVENT event1; Event sql_mode time_zone Create Event character_set_client collation_connection Database Collation -event1 STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION SYSTEM CREATE DEFINER=`root`@`localhost` EVENT `event1` ON SCHEDULE AT '2038-01-01 23:59:59' ON COMPLETION NOT PRESERVE DISABLE ON SLAVE DO SELECT 1 utf8mb4 utf8mb4_uca1400_ai_ci utf8mb4_uca1400_ai_ci +event1 STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION SYSTEM CREATE DEFINER=`root`@`localhost` EVENT `event1` ON SCHEDULE AT '2038-01-01 23:59:59' ON COMPLETION NOT PRESERVE DISABLE ON SLAVE DO SELECT 1 latin1 latin1_swedish_ci utf8mb4_uca1400_ai_ci DROP EVENT event1; diff --git a/mysql-test/suite/galera/r/view.result b/mysql-test/suite/galera/r/view.result index 4d369b0b691..45d5b422f3f 100644 --- a/mysql-test/suite/galera/r/view.result +++ b/mysql-test/suite/galera/r/view.result @@ -22,16 +22,16 @@ connection node_2; USE test; SHOW CREATE VIEW v1; View Create View character_set_client collation_connection -v1 CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v1` AS select `t1`.`i` AS `i` from `t1` utf8mb4 utf8mb4_uca1400_ai_ci +v1 CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v1` AS select `t1`.`i` AS `i` from `t1` latin1 latin1_swedish_ci SHOW CREATE VIEW v2; View Create View character_set_client collation_connection -v2 CREATE ALGORITHM=MERGE DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v2` AS select `t1`.`i` AS `i` from `t1` utf8mb4 utf8mb4_uca1400_ai_ci +v2 CREATE ALGORITHM=MERGE DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v2` AS select `t1`.`i` AS `i` from `t1` latin1 latin1_swedish_ci SHOW CREATE VIEW v3; View Create View character_set_client collation_connection -v3 CREATE ALGORITHM=TEMPTABLE DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v3` AS select `t1`.`i` AS `i` from `t1` utf8mb4 utf8mb4_uca1400_ai_ci +v3 CREATE ALGORITHM=TEMPTABLE DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v3` AS select `t1`.`i` AS `i` from `t1` latin1 latin1_swedish_ci SHOW CREATE VIEW v4; View Create View character_set_client collation_connection -v4 CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v4` AS select `t1`.`i` AS `i` from `t1` utf8mb4 utf8mb4_uca1400_ai_ci +v4 CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v4` AS select `t1`.`i` AS `i` from `t1` latin1 latin1_swedish_ci # On node_1 connection node_1; ALTER ALGORITHM=MERGE VIEW v1 AS SELECT * FROM t1; @@ -42,16 +42,16 @@ ALTER ALGORITHM=TEMPTABLE DEFINER=CURRENT_USER VIEW v4 AS SELECT * FROM t1; connection node_2; SHOW CREATE VIEW v1; View Create View character_set_client collation_connection -v1 CREATE ALGORITHM=MERGE DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v1` AS select `t1`.`i` AS `i` from `t1` utf8mb4 utf8mb4_uca1400_ai_ci +v1 CREATE ALGORITHM=MERGE DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v1` AS select `t1`.`i` AS `i` from `t1` latin1 latin1_swedish_ci SHOW CREATE VIEW v2; View Create View character_set_client collation_connection -v2 CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v2` AS select `t1`.`i` AS `i` from `t1` utf8mb4 utf8mb4_uca1400_ai_ci +v2 CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v2` AS select `t1`.`i` AS `i` from `t1` latin1 latin1_swedish_ci SHOW CREATE VIEW v3; View Create View character_set_client collation_connection -v3 CREATE ALGORITHM=TEMPTABLE DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v3` AS select `t1`.`i` AS `i` from `t1` utf8mb4 utf8mb4_uca1400_ai_ci +v3 CREATE ALGORITHM=TEMPTABLE DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v3` AS select `t1`.`i` AS `i` from `t1` latin1 latin1_swedish_ci SHOW CREATE VIEW v4; View Create View character_set_client collation_connection -v4 CREATE ALGORITHM=TEMPTABLE DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v4` AS select `t1`.`i` AS `i` from `t1` utf8mb4 utf8mb4_uca1400_ai_ci +v4 CREATE ALGORITHM=TEMPTABLE DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v4` AS select `t1`.`i` AS `i` from `t1` latin1 latin1_swedish_ci # Cleanup DROP VIEW v1, v2, v3, v4; DROP TABLE t1; diff --git a/mysql-test/suite/json/r/json_table.result b/mysql-test/suite/json/r/json_table.result index 6780cfd384f..cef56670120 100644 --- a/mysql-test/suite/json/r/json_table.result +++ b/mysql-test/suite/json/r/json_table.result @@ -1088,13 +1088,8 @@ show create view v; View Create View character_set_client collation_connection v CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v` AS select `T`.`col1` AS `col1` from JSON_TABLE('{"a": "b"}', '$' COLUMNS (`col1` varchar(32) PATH '$.fooo' DEFAULT 'asdf' ON EMPTY)) `T` latin1 latin1_swedish_ci drop view v; -# # End of 10.6 tests # -# -# Start of 10.9 tests -# -# # MDEV-27743 Remove Lex::charset # SELECT collation(name) @@ -1214,10 +1209,8 @@ name ENUM('Laptop') CHARACTER SET BINARY PATH '$.name') ) AS 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 'ENUM('Laptop') CHARACTER SET BINARY PATH '$.name') ) AS jt' at line 6 -# # End of 10.9 tests # -# # MDEV-27898 CREATE VIEW AS SELECT FROM JSON_TABLE column requires global privileges # # Beginning of 10.11 tests @@ -1383,7 +1376,5 @@ Laptop black 20000.00 2 Jacket brown 5000.00 1 Jeans blue 5000.00 2 drop table t1; -# # End of 11.0 tests -# ALTER DATABASE test CHARACTER SET utf8mb4 COLLATE utf8mb4_uca1400_ai_ci; diff --git a/mysql-test/suite/json/t/json_table.test b/mysql-test/suite/json/t/json_table.test index 8342f75830d..01370cbc2e9 100644 --- a/mysql-test/suite/json/t/json_table.test +++ b/mysql-test/suite/json/t/json_table.test @@ -920,13 +920,7 @@ show create view v; drop view v; ---echo # --echo # End of 10.6 tests ---echo # - ---echo # ---echo # Start of 10.9 tests ---echo # --echo # --echo # MDEV-27743 Remove Lex::charset @@ -1041,9 +1035,7 @@ COLUMNS name ENUM('Laptop') CHARACTER SET BINARY PATH '$.name') ) AS jt; ---echo # --echo # End of 10.9 tests ---echo # --echo # --echo # MDEV-27898 CREATE VIEW AS SELECT FROM JSON_TABLE column requires global privileges @@ -1198,8 +1190,6 @@ select * from t1; drop table t1; ---echo # --echo # End of 11.0 tests ---echo # --source include/test_db_charset_restore.inc diff --git a/mysql-test/suite/sys_vars/r/new_mode.result b/mysql-test/suite/sys_vars/r/new_mode.result index cb4c66186d0..ffb71325e58 100644 --- a/mysql-test/suite/sys_vars/r/new_mode.result +++ b/mysql-test/suite/sys_vars/r/new_mode.result @@ -32,7 +32,7 @@ ERROR 42000: Variable 'new_mode' can't be set to the value of 'TEST_WARNING3' SET @@session.new_mode = "ALL"; select @@session.new_mode; @@session.new_mode -FIX_DISK_TMPTABLE_COSTS +FIX_DISK_TMPTABLE_COSTS,FIX_INDEX_STATS_FOR_ALL_NULLS SET @@global.new_mode = NULL; ERROR 42000: Variable 'new_mode' can't be set to the value of 'NULL' SET @@global.new_mode = ''; diff --git a/mysql-test/suite/sys_vars/r/sysvars_server_embedded.result b/mysql-test/suite/sys_vars/r/sysvars_server_embedded.result index d0db9345d52..dce6fbcfb4b 100644 --- a/mysql-test/suite/sys_vars/r/sysvars_server_embedded.result +++ b/mysql-test/suite/sys_vars/r/sysvars_server_embedded.result @@ -2399,7 +2399,7 @@ VARIABLE_COMMENT Used to introduce new behavior to existing MariaDB versions NUMERIC_MIN_VALUE NULL NUMERIC_MAX_VALUE NULL NUMERIC_BLOCK_SIZE NULL -ENUM_VALUE_LIST FIX_DISK_TMPTABLE_COSTS +ENUM_VALUE_LIST FIX_DISK_TMPTABLE_COSTS,FIX_INDEX_STATS_FOR_ALL_NULLS READ_ONLY NO COMMAND_LINE_ARGUMENT REQUIRED VARIABLE_NAME NOTE_VERBOSITY diff --git a/mysql-test/suite/sys_vars/r/sysvars_server_notembedded.result b/mysql-test/suite/sys_vars/r/sysvars_server_notembedded.result index b71e63ad28f..ef501a5b217 100644 --- a/mysql-test/suite/sys_vars/r/sysvars_server_notembedded.result +++ b/mysql-test/suite/sys_vars/r/sysvars_server_notembedded.result @@ -2609,7 +2609,7 @@ VARIABLE_COMMENT Used to introduce new behavior to existing MariaDB versions NUMERIC_MIN_VALUE NULL NUMERIC_MAX_VALUE NULL NUMERIC_BLOCK_SIZE NULL -ENUM_VALUE_LIST FIX_DISK_TMPTABLE_COSTS +ENUM_VALUE_LIST FIX_DISK_TMPTABLE_COSTS,FIX_INDEX_STATS_FOR_ALL_NULLS READ_ONLY NO COMMAND_LINE_ARGUMENT REQUIRED VARIABLE_NAME NOTE_VERBOSITY diff --git a/sql/sql_class.h b/sql/sql_class.h index 47e6b85e2a7..37ce08512ff 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -222,8 +222,9 @@ void old_mode_deprecated_warnings(ulonglong v); See sys_vars.cc /new_mode_all_names */ -#define NEW_MODE_FIX_DISK_TMPTABLE_COSTS (1 << 0) -#define NEW_MODE_MAX 1 +#define NEW_MODE_FIX_DISK_TMPTABLE_COSTS (1ULL << 0) +#define NEW_MODE_FIX_INDEX_STATS_FOR_ALL_NULLS (1ULL << 1) +#define NEW_MODE_MAX 2 /* Definitions above that have transitioned from new behaviour to default */ diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 1468c6cee05..170c0226ea4 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -8777,7 +8777,26 @@ best_access_path(JOIN *join, ulong key_flags; uint key_parts; key_part_map found_part= 0; - /* key parts which won't have NULL in lookup tuple */ + + /* + Bitmap indicating which key parts are used with NULL-rejecting + conditions. + + A bit is set to 1 for a key part if it's used with a + NULL-rejecting condition (i.e., the condition will never be + satisfied when the indexed column contains NULL). A bit is 0 if + the key part is used with a non-NULL-rejecting condition (i.e., + the condition can be satisfied even when the indexed column + contains NULL, e.g., is NULL or <=>). + + Example: for condition + t1.keypart1 = t2.col1 AND t1.keypart2 <=> t2.col2 AND + t1.keypart3 = t2.col3 + the notnull_part bitmap will be 101 (binary), because: + - keypart1: '=' is NULL-rejecting (bit 1) + - keypart2: '<=>' is NOT NULL-rejecting (bit 0) + - keypart3: '=' is NULL-rejecting (bit 1) + */ key_part_map notnull_part=0; table_map found_ref= 0; uint key= keyuse->key; @@ -9032,7 +9051,8 @@ best_access_path(JOIN *join, } else { - if (!(records= keyinfo->actual_rec_per_key(key_parts-1))) + if (!(records= + keyinfo->rec_per_key_null_aware(key_parts-1, notnull_part))) { /* Prefer longer keys */ trace_access_idx.add("rec_per_key_stats_missing", true); records= @@ -9164,7 +9184,9 @@ best_access_path(JOIN *join, else { /* Check if we have statistic about the distribution */ - if ((records= keyinfo->actual_rec_per_key(max_key_part-1))) + if ((records= + keyinfo->rec_per_key_null_aware(max_key_part-1, + notnull_part))) { /* Fix for the case where the index statistics is too @@ -13505,6 +13527,7 @@ static bool create_hj_key_for_table(JOIN *join, JOIN_TAB *join_tab, keyinfo->algorithm= HA_KEY_ALG_UNDEF; keyinfo->flags= HA_GENERATED_KEY; keyinfo->is_statistics_from_stat_tables= FALSE; + keyinfo->all_nulls_key_parts= 0; keyinfo->name.str= "$hj"; keyinfo->name.length= 3; keyinfo->rec_per_key= thd->calloc(key_parts); @@ -22494,6 +22517,7 @@ bool Create_tmp_table::finalize(THD *thd, keyinfo->collected_stats= NULL; keyinfo->algorithm= HA_KEY_ALG_UNDEF; keyinfo->is_statistics_from_stat_tables= FALSE; + keyinfo->all_nulls_key_parts= 0; keyinfo->name= group_key; keyinfo->comment.str= 0; ORDER *cur_group= m_group; @@ -22615,6 +22639,7 @@ bool Create_tmp_table::finalize(THD *thd, keyinfo->name= distinct_key; keyinfo->algorithm= HA_KEY_ALG_UNDEF; keyinfo->is_statistics_from_stat_tables= FALSE; + keyinfo->all_nulls_key_parts= 0; keyinfo->read_stats= NULL; keyinfo->collected_stats= NULL; diff --git a/sql/sql_statistics.cc b/sql/sql_statistics.cc index c07399cd26e..a7b769b7f16 100644 --- a/sql/sql_statistics.cc +++ b/sql/sql_statistics.cc @@ -3122,7 +3122,10 @@ read_statistics_for_table(THD *thd, TABLE *table, found|= index_stat.get_stat_values(index_statistics); } if (found) + { new_stats_cb->stats_available|= TABLE_STAT_INDEX; + index_statistics->mark_stats_as_read(); + } key_part_map ext_key_part_map= key_info->ext_key_part_map; if (key_info->user_defined_key_parts != key_info->ext_key_parts && @@ -4156,11 +4159,35 @@ void set_statistics_for_table(THD *thd, TABLE *table) for (key_info= table->key_info, key_info_end= key_info+table->s->keys; key_info < key_info_end; key_info++) { + key_info->all_nulls_key_parts= 0; key_info->is_statistics_from_stat_tables= - (check_eits_preferred(thd) && - table->stats_is_read && - key_info->read_stats->avg_frequency_is_inited() && - key_info->read_stats->get_avg_frequency(0) > 0.5); + (check_eits_preferred(thd) && + table->stats_is_read && + key_info->read_stats->avg_frequency_is_inited() && + key_info->read_stats->has_stats(thd)); + + // Fill out `all_nulls_key_parts` bitmap + if (TEST_NEW_MODE_FLAG(thd, NEW_MODE_FIX_INDEX_STATS_FOR_ALL_NULLS) && + key_info->is_statistics_from_stat_tables) + { + for (uint part_idx= 0; part_idx < key_info->usable_key_parts; part_idx++) + { + Field *field= + table->field[key_info->key_part[part_idx].field->field_index]; + if (!field->read_stats) + { + // No column statistics available + continue; + } + + // Check if all values in this column are NULL according to statistics + double nulls_ratio= field->read_stats->get_nulls_ratio(); + if (nulls_ratio == 1.0) + { + key_info->all_nulls_key_parts |= (1 << part_idx); + } + } + } } } diff --git a/sql/sql_statistics.h b/sql/sql_statistics.h index 77d3ddb7514..a984692ebe7 100644 --- a/sql/sql_statistics.h +++ b/sql/sql_statistics.h @@ -603,10 +603,24 @@ private: k-component prefixes among them */ ulonglong *avg_frequency; + bool stats_were_read; public: + void init_avg_frequency(ulonglong *ptr) + { + avg_frequency= ptr; + stats_were_read= false; + } - void init_avg_frequency(ulonglong *ptr) { avg_frequency= ptr; } + void mark_stats_as_read() { stats_were_read= true; } + + bool has_stats(THD *thd) const + { + if (TEST_NEW_MODE_FLAG(thd, NEW_MODE_FIX_INDEX_STATS_FOR_ALL_NULLS)) + return stats_were_read; + else + return get_avg_frequency(0) > 0.5; + } bool avg_frequency_is_inited() { return avg_frequency != NULL; } diff --git a/sql/structs.h b/sql/structs.h index ac0510bed5d..6d57fc60c64 100644 --- a/sql/structs.h +++ b/sql/structs.h @@ -173,7 +173,17 @@ typedef struct st_key { engine_option_value *option_list; ha_index_option_struct *option_struct; /* structure with parsed options */ - double actual_rec_per_key(uint i) const; + /* + Bitmap of key parts where all values are NULL (nulls_ratio == 1.0). + Bit N set means key part N has all NULLs in the corresponding column. + Used for NULL-aware cardinality estimation. + It is computed based on EITS data, otherwise it is 0. + */ + key_part_map all_nulls_key_parts; + + double actual_rec_per_key(uint last_key_part_in_prefix) const; + double rec_per_key_null_aware(uint last_key_part_in_prefix, + key_part_map notnull_part) const; } KEY; diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc index 5d8301283dc..53ade7785bf 100644 --- a/sql/sys_vars.cc +++ b/sql/sys_vars.cc @@ -4113,6 +4113,7 @@ static Sys_var_set Sys_old_behavior( static const char *new_mode_all_names[]= { "FIX_DISK_TMPTABLE_COSTS", + "FIX_INDEX_STATS_FOR_ALL_NULLS", "TEST_WARNING1", // Default from here, See NEW_MODE_MAX "TEST_WARNING2", 0 @@ -4120,8 +4121,8 @@ static const char *new_mode_all_names[]= static int new_mode_hidden_names[] = { - 1, // TEST_WARNING1 - 2, // TEST_WARNING2 + 2, // TEST_WARNING1 + 3, // TEST_WARNING2 -1 // End of list }; diff --git a/sql/table.cc b/sql/table.cc index 21819eb8bb5..12536079229 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -8701,6 +8701,7 @@ bool TABLE::add_tmp_key(uint key, uint key_parts, bzero(keyinfo->rec_per_key, sizeof(ulong)*key_parts); keyinfo->read_stats= NULL; keyinfo->collected_stats= NULL; + keyinfo->all_nulls_key_parts= 0; for (i= 0; i < key_parts; i++) { @@ -10436,12 +10437,68 @@ uint TABLE_SHARE::actual_n_key_parts(THD *thd) } -double KEY::actual_rec_per_key(uint i) const +/** + Get records-per-key estimate for an index prefix. + + Returns average number of records per key value for the given index prefix. + Prefers engine-independent statistics (EITS) if available and falls back + to engine-dependent statistics otherwise. + + @param last_key_part_in_prefix Index of the last key part + in the prefix (0-based) + + @return Estimated records per key value: + - 0.0 if no statistics available + - avg_frequency from EITS if available + - rec_per_key from engine statistics if EITS is not available +*/ +double KEY::actual_rec_per_key(uint last_key_part_in_prefix) const { - if (rec_per_key == 0) - return 0; - return (is_statistics_from_stat_tables ? - read_stats->get_avg_frequency(i) : (double) rec_per_key[i]); + if (is_statistics_from_stat_tables) + { + // Use engine-independent statistics (EITS) + return read_stats->get_avg_frequency(last_key_part_in_prefix); + } + // Fall back to engine-dependent statistics if EITS is not available + return rec_per_key ? (double) rec_per_key[last_key_part_in_prefix] : 0.0; +} + + +/** + Get records-per-key estimate for an index prefix with NULL-aware optimization. + + Returns average number of records per key value for the given index prefix. + When EITS statistics show avg_frequency == 0 (typically all NULL values) and + the query uses NULL-rejecting conditions (e.g., =), returns 1.0 to indicate + high selectivity since NULL = NULL never matches. + + @param last_key_part_in_prefix Index of the last key part + in the prefix (0-based) + @param notnull_part Bitmap indicating which key parts have NULL-rejecting + conditions (bit N set means key part N uses =, not <=>) + + @return Estimated records per key value: + - 0.0 if no statistics available + - avg_frequency from EITS if available + - 1.0 if all values are NULL with NULL-rejecting condition + - rec_per_key from engine statistics if EITS is not available +*/ +double KEY::rec_per_key_null_aware(uint last_key_part_in_prefix, + key_part_map notnull_part) const +{ + if (notnull_part & all_nulls_key_parts) + { + /* + For NULL-rejecting conditions like `t1.key_col = t2.col`, we know + there will be no matches (since NULL = NULL is never true). + If at least one NULL-rejecting condition is present, and all + corresponding key part values are NULL, return number of records 1.0 + (highly selective), indicating no expected matches. + */ + return 1.0; + } + + return actual_rec_per_key(last_key_part_in_prefix); } /* diff --git a/storage/spider/mysql-test/spider/r/timestamp,usual_handler.rdiff b/storage/spider/mysql-test/spider/r/timestamp,usual_handler.rdiff index ceb998819a7..427db2383ef 100644 --- a/storage/spider/mysql-test/spider/r/timestamp,usual_handler.rdiff +++ b/storage/spider/mysql-test/spider/r/timestamp,usual_handler.rdiff @@ -9,7 +9,7 @@ SELECT argument FROM mysql.general_log WHERE command_type != 'Execute' AND argument LIKE '%select %' SELECT col_a, col_dt, col_ts, unix_timestamp(col_ts) FROM tbl_a ORDER BY col_a; col_a col_dt col_ts unix_timestamp(col_ts) -@@ -144,7 +144,7 @@ col_a col_dt col_ts unix_timestamp(col_ts) +@@ -144,7 +144,7 @@ connection child2_1; SELECT argument FROM mysql.general_log WHERE command_type != 'Execute' AND argument LIKE '%select %'; argument @@ -18,7 +18,7 @@ SELECT argument FROM mysql.general_log WHERE command_type != 'Execute' AND argument LIKE '%select %' SELECT col_a, col_dt, col_ts, unix_timestamp(col_ts) FROM tbl_a ORDER BY col_a; col_a col_dt col_ts unix_timestamp(col_ts) -@@ -170,7 +170,7 @@ col_a col_dt col_ts unix_timestamp(col_ts) +@@ -170,7 +170,7 @@ connection child2_1; SELECT argument FROM mysql.general_log WHERE command_type != 'Execute' AND argument LIKE '%select %'; argument @@ -27,7 +27,7 @@ SELECT argument FROM mysql.general_log WHERE command_type != 'Execute' AND argument LIKE '%select %' SELECT col_a, col_dt, col_ts, unix_timestamp(col_ts) FROM tbl_a ORDER BY col_a; col_a col_dt col_ts unix_timestamp(col_ts) -@@ -198,7 +198,7 @@ connection child2_1; +@@ -198,7 +198,7 @@ SELECT argument FROM mysql.general_log WHERE command_type != 'Execute' AND argument LIKE '%select %'; argument select `col_a`,`col_dt`,`col_ts` from `ts_test_remote`.`tbl_a` for update @@ -36,7 +36,7 @@ SELECT argument FROM mysql.general_log WHERE command_type != 'Execute' AND argument LIKE '%select %' SELECT col_a, col_dt, col_ts, unix_timestamp(col_ts) FROM tbl_a ORDER BY col_a; col_a col_dt col_ts unix_timestamp(col_ts) -@@ -256,13 +256,13 @@ col_a col_dt col_ts unix_timestamp(col_ts) +@@ -256,13 +256,13 @@ connection child2_1; SELECT argument FROM mysql.general_log WHERE command_type != 'Execute' AND argument LIKE '%select %'; argument @@ -57,7 +57,7 @@ SELECT argument FROM mysql.general_log WHERE command_type != 'Execute' AND argument LIKE '%select %' SELECT col_a, col_dt, col_ts, unix_timestamp(col_ts) FROM tbl_a ORDER BY col_a; col_a col_dt col_ts unix_timestamp(col_ts) -@@ -343,13 +343,13 @@ col_a col_dt col_ts unix_timestamp(col_ts) +@@ -343,13 +343,13 @@ connection child2_1; SELECT argument FROM mysql.general_log WHERE command_type != 'Execute' AND argument LIKE '%select %'; argument @@ -78,7 +78,7 @@ SELECT argument FROM mysql.general_log WHERE command_type != 'Execute' AND argument LIKE '%select %' SELECT col_a, col_dt, col_ts, unix_timestamp(col_ts) FROM tbl_a ORDER BY col_a; col_a col_dt col_ts unix_timestamp(col_ts) -@@ -396,11 +396,11 @@ TIMESTAMP('2018-06-25', '10:43:21') +@@ -396,11 +396,11 @@ connection child2_1; SELECT argument FROM mysql.general_log WHERE command_type != 'Execute' AND argument LIKE '%select %'; argument