Skip to content

Commit aa70eea

Browse files
committed
MDEV-36761: Put NULL-aware cardinality estimation under new_mode flag
The NULL-aware index statistics fix is now controlled by the FIX_INDEX_STATS_FOR_ALL_NULLS flag and disabled by default for preserving execution plan stability in stable versions. To enable: SET @@new_mode = 'FIX_INDEX_STATS_FOR_ALL_NULLS'; Or via command line: --new-mode=FIX_INDEX_STATS_FOR_ALL_NULLS Or in configuration file: [mysqld] new_mode=FIX_INDEX_STATS_FOR_ALL_NULLS `all_nulls_key_parts` bitmap is now calculated at set_statistics_for_table()
1 parent ebe27ec commit aa70eea

13 files changed

+94
-72
lines changed

mysql-test/main/mysqld--help.result

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -743,7 +743,9 @@ The following specify which files/extra groups are read (specified before remain
743743
Number of seconds to wait for a block to be written to a
744744
connection before aborting the write
745745
--new-mode=name Used to introduce new behavior to existing MariaDB
746-
versions. No currently supported values.
746+
versions. Any combination of:
747+
FIX_INDEX_STATS_FOR_ALL_NULLS
748+
Use 'ALL' to set all combinations.
747749
--note-verbosity=name
748750
Verbosity level for note-warnings given to the user. See
749751
also @@sql_notes.. Any combination of: basic,

mysql-test/main/null-aware-cardinality.result renamed to mysql-test/main/null_aware_cardinality.result

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
SET @session_start_value = @@new_mode;
12
# Small driving table
23
CREATE TABLE t1 (a INT, b INT);
34
INSERT INTO t1 VALUES (1, 1), (2, 2000),(3,300);
@@ -13,6 +14,7 @@ ANALYZE TABLE t2 PERSISTENT FOR ALL;
1314
Table Op Msg_type Msg_text
1415
test.t2 analyze status Engine-independent statistics collected
1516
test.t2 analyze status Table is already up to date
17+
SET @@new_mode = "FIX_INDEX_STATS_FOR_ALL_NULLS";
1618
# NULL-rejecting equality t1.b = t2.b will not return any matches
1719
# because all values of t2.b are NULL. So "rows" = 1 for t2 where 1 is
1820
# a special value meaning "very few" rows
@@ -30,10 +32,6 @@ id select_type table type possible_keys key key_len ref rows filtered Extra
3032
1 SIMPLE t2 ref key_b key_b 5 test.t1.b 11 100.00 Using index condition; Using where
3133
Warnings:
3234
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`
33-
ANALYZE SELECT * FROM t1 JOIN t2 ON t1.a = t2.a AND t1.b <=> t2.b;
34-
id select_type table type possible_keys key key_len ref rows r_rows filtered r_filtered Extra
35-
1 SIMPLE t1 ALL NULL NULL NULL NULL 3 3.00 100.00 100.00
36-
1 SIMPLE t2 ref key_b key_b 5 test.t1.b 11 0.00 100.00 100.00 Using index condition; Using where
3735
# Insert some non-NULL values and re-collect the stats
3836
INSERT INTO t2 SELECT 1, 1 FROM seq_1_to_100;
3937
ANALYZE TABLE t2 PERSISTENT FOR COLUMNS (b) INDEXES (key_b);
@@ -82,12 +80,21 @@ id select_type table type possible_keys key key_len ref rows filtered Extra
8280
1 SIMPLE t3 ref key_ab key_ab 10 test.t1.a,const 11 100.00 Using where; Using index
8381
Warnings:
8482
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
83+
# In the old mode (null-aware estimation is not enabled), "rows" > 1
84+
SET @@new_mode = "";
85+
EXPLAIN EXTENDED SELECT * FROM t1 JOIN t2 ON t1.a = t2.a AND t1.b = t2.b;
86+
id select_type table type possible_keys key key_len ref rows filtered Extra
87+
1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using where
88+
1 SIMPLE t2 ref key_b key_b 5 test.t1.b 100 100.00 Using where
89+
Warnings:
90+
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`
8591
# Insert some non-NULL values and re-collect the stats
8692
INSERT INTO t3 SELECT 1, 1 FROM seq_1_to_100;
8793
ANALYZE TABLE t3 PERSISTENT FOR COLUMNS (b) INDEXES (key_ab);
8894
Table Op Msg_type Msg_text
8995
test.t3 analyze status Engine-independent statistics collected
9096
test.t3 analyze status OK
97+
SET @@new_mode = "FIX_INDEX_STATS_FOR_ALL_NULLS";
9198
EXPLAIN EXTENDED SELECT * FROM t1 JOIN t3 ON t1.a = t3.a AND t1.b = t3.b;
9299
id select_type table type possible_keys key key_len ref rows filtered Extra
93100
1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using where
@@ -147,4 +154,5 @@ EXPLAIN SELECT * FROM t1, t2 WHERE t2.a=t1.a AND t2.b=t1.a;
147154
id select_type table type possible_keys key key_len ref rows Extra
148155
1 SIMPLE t1 ALL NULL NULL NULL NULL 10 Using where
149156
1 SIMPLE t2 ref i1 i1 21 test.t1.a,test.t1.a 1 Using where
157+
SET @@new_mode = @session_start_value;
150158
DROP TABLE t1, t2;

mysql-test/main/null-aware-cardinality.test renamed to mysql-test/main/null_aware_cardinality.test

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
--source include/have_sequence.inc
22

3+
SET @session_start_value = @@new_mode;
4+
35
--echo # Small driving table
46
CREATE TABLE t1 (a INT, b INT);
57
INSERT INTO t1 VALUES (1, 1), (2, 2000),(3,300);
@@ -13,6 +15,8 @@ INSERT INTO t2 SELECT seq/100, NULL FROM seq_1_to_1000;
1315

1416
ANALYZE TABLE t2 PERSISTENT FOR ALL;
1517

18+
SET @@new_mode = "FIX_INDEX_STATS_FOR_ALL_NULLS";
19+
1620
--echo # NULL-rejecting equality t1.b = t2.b will not return any matches
1721
--echo # because all values of t2.b are NULL. So "rows" = 1 for t2 where 1 is
1822
--echo # a special value meaning "very few" rows
@@ -22,8 +26,6 @@ EXPLAIN EXTENDED SELECT * FROM t1 JOIN t2 ON t1.a = t2.a AND t1.b = t2.b;
2226
--echo # must not be affected ("rows" > 1 is expected)
2327
EXPLAIN EXTENDED SELECT * FROM t1 JOIN t2 ON t1.a = t2.a AND t1.b <=> t2.b;
2428

25-
ANALYZE SELECT * FROM t1 JOIN t2 ON t1.a = t2.a AND t1.b <=> t2.b;
26-
2729
--echo # Insert some non-NULL values and re-collect the stats
2830
INSERT INTO t2 SELECT 1, 1 FROM seq_1_to_100;
2931

@@ -50,11 +52,16 @@ EXPLAIN EXTENDED SELECT * FROM t1 JOIN t3 ON t1.a = t3.a AND t1.b <=> t3.b;
5052

5153
EXPLAIN EXTENDED SELECT * FROM t1 JOIN t3 ON t1.a = t3.a AND t3.b is NULL;
5254

55+
--echo # In the old mode (null-aware estimation is not enabled), "rows" > 1
56+
SET @@new_mode = "";
57+
EXPLAIN EXTENDED SELECT * FROM t1 JOIN t2 ON t1.a = t2.a AND t1.b = t2.b;
58+
5359
--echo # Insert some non-NULL values and re-collect the stats
5460
INSERT INTO t3 SELECT 1, 1 FROM seq_1_to_100;
5561

5662
ANALYZE TABLE t3 PERSISTENT FOR COLUMNS (b) INDEXES (key_ab);
5763

64+
SET @@new_mode = "FIX_INDEX_STATS_FOR_ALL_NULLS";
5865
EXPLAIN EXTENDED SELECT * FROM t1 JOIN t3 ON t1.a = t3.a AND t1.b = t3.b;
5966

6067
--echo # Test composite index for 3 columns. Key prefix is used for access
@@ -92,4 +99,5 @@ ANALYZE TABLE t2 PERSISTENT FOR COLUMNS (b) INDEXES (i1);
9299

93100
EXPLAIN SELECT * FROM t1, t2 WHERE t2.a=t1.a AND t2.b=t1.a;
94101

102+
SET @@new_mode = @session_start_value;
95103
DROP TABLE t1, t2;

mysql-test/suite/sys_vars/r/new_mode.result

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ ERROR 42000: Variable 'new_mode' can't be set to the value of 'TEST_WARNING3'
2525
SET @@session.new_mode = "ALL";
2626
select @@session.new_mode;
2727
@@session.new_mode
28-
28+
FIX_INDEX_STATS_FOR_ALL_NULLS
2929
SET @@global.new_mode = NULL;
3030
ERROR 42000: Variable 'new_mode' can't be set to the value of 'NULL'
3131
SET @@global.new_mode = '';

mysql-test/suite/sys_vars/r/sysvars_server_embedded.result

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2319,7 +2319,7 @@ VARIABLE_COMMENT Used to introduce new behavior to existing MariaDB versions
23192319
NUMERIC_MIN_VALUE NULL
23202320
NUMERIC_MAX_VALUE NULL
23212321
NUMERIC_BLOCK_SIZE NULL
2322-
ENUM_VALUE_LIST
2322+
ENUM_VALUE_LIST FIX_INDEX_STATS_FOR_ALL_NULLS
23232323
READ_ONLY NO
23242324
COMMAND_LINE_ARGUMENT REQUIRED
23252325
VARIABLE_NAME NOTE_VERBOSITY

mysql-test/suite/sys_vars/r/sysvars_server_notembedded.result

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2529,7 +2529,7 @@ VARIABLE_COMMENT Used to introduce new behavior to existing MariaDB versions
25292529
NUMERIC_MIN_VALUE NULL
25302530
NUMERIC_MAX_VALUE NULL
25312531
NUMERIC_BLOCK_SIZE NULL
2532-
ENUM_VALUE_LIST
2532+
ENUM_VALUE_LIST FIX_INDEX_STATS_FOR_ALL_NULLS
25332533
READ_ONLY NO
25342534
COMMAND_LINE_ARGUMENT REQUIRED
25352535
VARIABLE_NAME NOTE_VERBOSITY

sql/sql_class.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,8 @@ void old_mode_deprecated_warnings(THD *thd, ulonglong v);
219219
See sys_vars.cc /new_mode_all_names
220220
*/
221221

222-
#define NEW_MODE_MAX 0
222+
#define NEW_MODE_FIX_INDEX_STATS_FOR_ALL_NULLS (1ULL << 0)
223+
#define NEW_MODE_MAX 1
223224

224225
/* Definitions above that have transitioned from new behaviour to default */
225226

sql/sql_select.cc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13450,6 +13450,7 @@ static bool create_hj_key_for_table(JOIN *join, JOIN_TAB *join_tab,
1345013450
keyinfo->algorithm= HA_KEY_ALG_UNDEF;
1345113451
keyinfo->flags= HA_GENERATED_KEY;
1345213452
keyinfo->is_statistics_from_stat_tables= FALSE;
13453+
keyinfo->all_nulls_key_parts= 0;
1345313454
keyinfo->name.str= "$hj";
1345413455
keyinfo->name.length= 3;
1345513456
keyinfo->rec_per_key= (ulong*) thd->calloc(sizeof(ulong)*key_parts);
@@ -22432,6 +22433,7 @@ bool Create_tmp_table::finalize(THD *thd,
2243222433
keyinfo->collected_stats= NULL;
2243322434
keyinfo->algorithm= HA_KEY_ALG_UNDEF;
2243422435
keyinfo->is_statistics_from_stat_tables= FALSE;
22436+
keyinfo->all_nulls_key_parts= 0;
2243522437
keyinfo->name= group_key;
2243622438
keyinfo->comment.str= 0;
2243722439
ORDER *cur_group= m_group;
@@ -22553,6 +22555,7 @@ bool Create_tmp_table::finalize(THD *thd,
2255322555
keyinfo->name= distinct_key;
2255422556
keyinfo->algorithm= HA_KEY_ALG_UNDEF;
2255522557
keyinfo->is_statistics_from_stat_tables= FALSE;
22558+
keyinfo->all_nulls_key_parts= 0;
2255622559
keyinfo->read_stats= NULL;
2255722560
keyinfo->collected_stats= NULL;
2255822561

sql/sql_statistics.cc

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4156,11 +4156,35 @@ void set_statistics_for_table(THD *thd, TABLE *table)
41564156
for (key_info= table->key_info, key_info_end= key_info+table->s->keys;
41574157
key_info < key_info_end; key_info++)
41584158
{
4159+
key_info->all_nulls_key_parts= 0;
41594160
key_info->is_statistics_from_stat_tables=
4160-
(check_eits_preferred(thd) &&
4161-
table->stats_is_read &&
4162-
key_info->read_stats->avg_frequency_is_inited() &&
4163-
key_info->read_stats->has_stats());
4161+
(check_eits_preferred(thd) &&
4162+
table->stats_is_read &&
4163+
key_info->read_stats->avg_frequency_is_inited() &&
4164+
key_info->read_stats->has_stats(thd));
4165+
4166+
// Fill out `all_nulls_key_parts` bitmap
4167+
if (TEST_NEW_MODE_FLAG(thd, NEW_MODE_FIX_INDEX_STATS_FOR_ALL_NULLS) &&
4168+
key_info->is_statistics_from_stat_tables)
4169+
{
4170+
for (uint part_idx= 0; part_idx < key_info->usable_key_parts; part_idx++)
4171+
{
4172+
Field *field=
4173+
table->field[key_info->key_part[part_idx].field->field_index];
4174+
if (!field->read_stats)
4175+
{
4176+
// No column statistics available
4177+
continue;
4178+
}
4179+
4180+
// Check if all values in this column are NULL according to statistics
4181+
double nulls_ratio= field->read_stats->get_nulls_ratio();
4182+
if (nulls_ratio == 1.0)
4183+
{
4184+
key_info->all_nulls_key_parts |= (1 << part_idx);
4185+
}
4186+
}
4187+
}
41644188
}
41654189
}
41664190

sql/sql_statistics.h

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -606,7 +606,6 @@ class Index_statistics
606606
bool stats_were_read;
607607

608608
public:
609-
610609
void init_avg_frequency(ulonglong *ptr)
611610
{
612611
avg_frequency= ptr;
@@ -615,7 +614,13 @@ class Index_statistics
615614

616615
void mark_stats_as_read() { stats_were_read= true; }
617616

618-
bool has_stats() const { return stats_were_read; }
617+
bool has_stats(THD *thd) const
618+
{
619+
if (TEST_NEW_MODE_FLAG(thd, NEW_MODE_FIX_INDEX_STATS_FOR_ALL_NULLS))
620+
return stats_were_read;
621+
else
622+
return get_avg_frequency(0) > 0.5;
623+
}
619624

620625
bool avg_frequency_is_inited() { return avg_frequency != NULL; }
621626

0 commit comments

Comments
 (0)