diff options
author | Oleg Smirnov <olernov@gmail.com> | 2022-07-27 21:25:34 +0700 |
---|---|---|
committer | Oleg Smirnov <olernov@gmail.com> | 2022-07-28 16:14:01 +0700 |
commit | 4c82cfb54eaf960562253b851721732cfa60c435 (patch) | |
tree | e776e0281ca6f7b782431aa6a0295b4917a5da8c | |
parent | 8494758e8e06aea5c8d4cddcff6c0a913bac4d23 (diff) | |
download | mariadb-git-bb-10.5-MDEV-24819.tar.gz |
MDEV-24819 Optimizer uses wrong parameter evaluating TRP_GROUP_MIN_MAXbb-10.5-MDEV-24819
When evaluating GROUP_MIN_MAX access (also known as Loose Index Scan)
the optimizer considers applying QUICK_RANGE_SELECT object to the
constructed TRP_GROUP_MIN_MAX object. To do that the optimizer
evaluates param->quick_rows[cur_index] which has been set before
while evaluating Index Merge access. But Loose Index Scan cannot be
combined with Index Merge access so evaluating param->quick_rows is not
valid here. The correct parameter to evaluate possibility of
QUICK_RANGE_SELECT is param->table->opt_range[cur_index].rows.
-rw-r--r-- | mysql-test/main/group_min_max.result | 31 | ||||
-rw-r--r-- | mysql-test/main/group_min_max.test | 23 | ||||
-rw-r--r-- | sql/opt_range.cc | 38 |
3 files changed, 74 insertions, 18 deletions
diff --git a/mysql-test/main/group_min_max.result b/mysql-test/main/group_min_max.result index e6a5bc0ab50..7b1482f7c95 100644 --- a/mysql-test/main/group_min_max.result +++ b/mysql-test/main/group_min_max.result @@ -4065,3 +4065,34 @@ SELECT DISTINCT owner_id FROM t1 WHERE foo = true GROUP BY owner_id HAVING (COUN owner_id 1 DROP TABLE t1; +# +# MDEV-24819 Wrong result or ASAN use-after-poison errors upon SELECT with GROUP BY +# +# Firstly test MyISAM engine: +CREATE TABLE t1 (a INT, b VARCHAR(1), KEY (a), KEY(b,a)) ENGINE=MyISAM; +INSERT INTO t1 VALUES (4,'n'),(1,'h'),(NULL,'w'); +# Make sure "Using index for group-by" is chosen: +EXPLAIN SELECT b, a FROM t1 WHERE b <> 'p' OR a = 4 GROUP BY b, a HAVING a <= 7; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 range a,b b 9 NULL 4 Using where; Using index for group-by +SELECT b, a FROM t1 WHERE b <> 'p' OR a = 4 GROUP BY b, a HAVING a <= 7; +b a +h 1 +n 4 +DROP TABLE t1; +# Test the same for InnoDB engine: +CREATE TABLE t1 (a INT, b VARCHAR(1), KEY (a), KEY(b,a)) ENGINE=InnoDB; +INSERT INTO t1 VALUES (4,'n'),(1,'h'),(NULL,'w'); +EXPLAIN SELECT b, a FROM t1 WHERE b <> 'p' OR a = 4 GROUP BY b, a HAVING a <= 7; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 range a,b b 9 NULL 4 Using where; Using index for group-by +SELECT b, a FROM t1 WHERE b <> 'p' OR a = 4 GROUP BY b, a HAVING a <= 7; +b a +h 1 +n 4 +DROP TABLE t1; +CREATE TABLE t1 (pk INT PRIMARY KEY, a INT, KEY(a)) ENGINE=InnoDB; +INSERT INTO t1 VALUES (1,3),(2,6),(3,9),(4,NULL),(5,NULL); +SELECT a, pk FROM t1 WHERE pk != 0 AND (a < 0 OR pk <= 0) GROUP BY a, pk; +a pk +DROP TABLE t1; diff --git a/mysql-test/main/group_min_max.test b/mysql-test/main/group_min_max.test index 3f7ef4ec9b8..62177e46848 100644 --- a/mysql-test/main/group_min_max.test +++ b/mysql-test/main/group_min_max.test @@ -1722,3 +1722,26 @@ EXPLAIN SELECT DISTINCT owner_id FROM t1 WHERE foo = true GROUP BY owner_id HAVING (COUNT(*) = 1); SELECT DISTINCT owner_id FROM t1 WHERE foo = true GROUP BY owner_id HAVING (COUNT(*) = 1); DROP TABLE t1; + + +--echo # +--echo # MDEV-24819 Wrong result or ASAN use-after-poison errors upon SELECT with GROUP BY +--echo # +--echo # Firstly test MyISAM engine: +CREATE TABLE t1 (a INT, b VARCHAR(1), KEY (a), KEY(b,a)) ENGINE=MyISAM; +INSERT INTO t1 VALUES (4,'n'),(1,'h'),(NULL,'w'); +--echo # Make sure "Using index for group-by" is chosen: +EXPLAIN SELECT b, a FROM t1 WHERE b <> 'p' OR a = 4 GROUP BY b, a HAVING a <= 7; +SELECT b, a FROM t1 WHERE b <> 'p' OR a = 4 GROUP BY b, a HAVING a <= 7; +DROP TABLE t1; +--echo # Test the same for InnoDB engine: +CREATE TABLE t1 (a INT, b VARCHAR(1), KEY (a), KEY(b,a)) ENGINE=InnoDB; +INSERT INTO t1 VALUES (4,'n'),(1,'h'),(NULL,'w'); +EXPLAIN SELECT b, a FROM t1 WHERE b <> 'p' OR a = 4 GROUP BY b, a HAVING a <= 7; +SELECT b, a FROM t1 WHERE b <> 'p' OR a = 4 GROUP BY b, a HAVING a <= 7; +DROP TABLE t1; + +CREATE TABLE t1 (pk INT PRIMARY KEY, a INT, KEY(a)) ENGINE=InnoDB; +INSERT INTO t1 VALUES (1,3),(2,6),(3,9),(4,NULL),(5,NULL); +SELECT a, pk FROM t1 WHERE pk != 0 AND (a < 0 OR pk <= 0) GROUP BY a, pk; +DROP TABLE t1;
\ No newline at end of file diff --git a/sql/opt_range.cc b/sql/opt_range.cc index e3287e1bbea..e9f49599756 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -13754,13 +13754,9 @@ get_best_group_min_max(PARAM *param, SEL_TREE *tree, double read_time) double best_read_cost= DBL_MAX; ha_rows best_records= 0; SEL_ARG *best_index_tree= NULL; - ha_rows best_quick_prefix_records= 0; + ha_rows best_quick_prefix_records= HA_POS_ERROR; uint best_param_idx= 0; - const uint pk= param->table->s->primary_key; - uint max_key_part; - SEL_ARG *cur_index_tree= NULL; - ha_rows cur_quick_prefix_records= 0; // We go through allowed indexes Json_writer_array trace_indexes(thd, "potential_group_range_indexes"); @@ -13789,6 +13785,9 @@ get_best_group_min_max(PARAM *param, SEL_TREE *tree, double read_time) uint cur_key_infix_len= 0; uchar cur_key_infix[MAX_KEY_LENGTH]; uint cur_used_key_parts; + ha_rows cur_quick_prefix_records= HA_POS_ERROR; + SEL_ARG *cur_index_tree= NULL; + uint max_key_part= 0; /* Check (B1) - if current index is covering. @@ -13846,7 +13845,6 @@ get_best_group_min_max(PARAM *param, SEL_TREE *tree, double read_time) trace_idx.add("covering", true); - max_key_part= 0; used_key_parts_map.clear_all(); /* @@ -14089,22 +14087,26 @@ get_best_group_min_max(PARAM *param, SEL_TREE *tree, double read_time) (first_non_infix_part - first_non_group_part) : 0; cur_used_key_parts= cur_group_key_parts + key_infix_parts; - /* Compute the cost of using this index. */ - if (tree) + /* + Consider using this index for constructing QUICK_RANGE_SELECT object + in TRP_GROUP_MIN_MAX. + Initially the index is considered not applicable: + */ + cur_quick_prefix_records= HA_POS_ERROR; + if (tree && ((cur_index_tree= tree->keys[cur_param_idx]) != nullptr)) { - if ((cur_index_tree= tree->keys[cur_param_idx])) + if (param->table->opt_range_keys.is_set(cur_index)) + /* Current index is applicable for QUICK_RANGE_SELECT object */ + cur_quick_prefix_records= param->table->opt_range[cur_index].rows; + if (unlikely(thd->trace_started())) { - cur_quick_prefix_records= param->quick_rows[cur_index]; - if (unlikely(cur_index_tree && thd->trace_started())) - { - Json_writer_array trace_range(thd, "ranges"); - trace_ranges(&trace_range, param, cur_param_idx, - cur_index_tree, cur_index_info->key_part); - } + Json_writer_array trace_range(thd, "ranges"); + trace_ranges(&trace_range, param, cur_param_idx, cur_index_tree, + cur_index_info->key_part); } - else - cur_quick_prefix_records= HA_POS_ERROR; } + + /* Compute the cost of using this index. */ cost_group_min_max(table, cur_index_info, cur_used_key_parts, cur_group_key_parts, tree, cur_index_tree, cur_quick_prefix_records, have_min, have_max, |