Commit f83e2ecc authored by Sergei Petrunia's avatar Sergei Petrunia

MDEV-24953: 10.5.9 crashes with large IN() list

The problem was in and_all_keys(), the code of MDEV-9759 which calculates
the new tree weight:

First, it didn't take into account the case when

(next->next_key_part=tmp) == NULL

and dereferenced a NULL pointer when getting tmp->weight.

Second, "if (param->alloced_sel_args > SEL_ARG::MAX_SEL_ARGS) break"
could leave the loop with incorrect value of weight.

Fixed by introducing SEL_ARG::update_weight_locally() and calling it
at the end of the function. This allows to avoid caring about all the
above cases.
parent f1590615
...@@ -159,7 +159,6 @@ left(@json, 2500) ...@@ -159,7 +159,6 @@ left(@json, 2500)
] ]
] ]
## Repeat the above with a bit higher max_weight: ## Repeat the above with a bit higher max_weight:
set @tmp9750_weight=@@optimizer_max_sel_arg_weight;
set optimizer_max_sel_arg_weight=120; set optimizer_max_sel_arg_weight=120;
explain select * from t1 where explain select * from t1 where
kp1 in (1,2,3,4,5,6,7,8,9,10) and kp1 in (1,2,3,4,5,6,7,8,9,10) and
...@@ -225,3 +224,18 @@ SELECT * ...@@ -225,3 +224,18 @@ SELECT *
FROM mysql.help_relation ignore index (help_topic_id) FROM mysql.help_relation ignore index (help_topic_id)
WHERE (help_topic_id = 8 OR help_keyword_id = 0) AND help_keyword_id != 2 AND help_topic_id >= 1900; WHERE (help_topic_id = 8 OR help_keyword_id = 0) AND help_keyword_id != 2 AND help_topic_id >= 1900;
help_topic_id help_keyword_id help_topic_id help_keyword_id
#
# MDEV-24953: 10.5.9 crashes with large IN() list
#
CREATE TABLE t1 (
notification_type_id smallint(4) unsigned NOT NULL DEFAULT 0,
item_id int(10) unsigned NOT NULL DEFAULT 0,
item_parent_id int(10) unsigned NOT NULL DEFAULT 0,
user_id int(10) unsigned NOT NULL DEFAULT 0,
PRIMARY KEY (notification_type_id,item_id,item_parent_id,user_id)
);
insert into t1 values (1,1,1,1), (2,2,2,2), (3,3,3,3);
# Run crashing query
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 range PRIMARY PRIMARY 2 NULL 3 Using where
drop table t1;
...@@ -82,7 +82,6 @@ set @json= json_detailed(json_extract(@trace, '$**.setup_range_conditions')); ...@@ -82,7 +82,6 @@ set @json= json_detailed(json_extract(@trace, '$**.setup_range_conditions'));
select left(@json, 2500); select left(@json, 2500);
--echo ## Repeat the above with a bit higher max_weight: --echo ## Repeat the above with a bit higher max_weight:
set @tmp9750_weight=@@optimizer_max_sel_arg_weight;
set optimizer_max_sel_arg_weight=120; set optimizer_max_sel_arg_weight=120;
explain select * from t1 where explain select * from t1 where
kp1 in (1,2,3,4,5,6,7,8,9,10) and kp1 in (1,2,3,4,5,6,7,8,9,10) and
...@@ -110,3 +109,34 @@ SELECT * ...@@ -110,3 +109,34 @@ SELECT *
FROM mysql.help_relation ignore index (help_topic_id) FROM mysql.help_relation ignore index (help_topic_id)
WHERE (help_topic_id = 8 OR help_keyword_id = 0) AND help_keyword_id != 2 AND help_topic_id >= 1900; WHERE (help_topic_id = 8 OR help_keyword_id = 0) AND help_keyword_id != 2 AND help_topic_id >= 1900;
--echo #
--echo # MDEV-24953: 10.5.9 crashes with large IN() list
--echo #
--source include/have_sequence.inc
CREATE TABLE t1 (
notification_type_id smallint(4) unsigned NOT NULL DEFAULT 0,
item_id int(10) unsigned NOT NULL DEFAULT 0,
item_parent_id int(10) unsigned NOT NULL DEFAULT 0,
user_id int(10) unsigned NOT NULL DEFAULT 0,
PRIMARY KEY (notification_type_id,item_id,item_parent_id,user_id)
);
insert into t1 values (1,1,1,1), (2,2,2,2), (3,3,3,3);
let $consts=`select group_concat(concat("'",seq,"'")) from seq_1_to_4642`;
--echo # Run crashing query
--disable_query_log
eval
explain
DELETE FROM t1
WHERE
notification_type_id IN (3, 4, 5, 6, 23)
AND
user_id = '5044'
AND
item_parent_id IN ($consts)
;
--enable_query_log
drop table t1;
...@@ -9800,7 +9800,6 @@ and_all_keys(RANGE_OPT_PARAM *param, SEL_ARG *key1, SEL_ARG *key2, ...@@ -9800,7 +9800,6 @@ and_all_keys(RANGE_OPT_PARAM *param, SEL_ARG *key1, SEL_ARG *key2,
key1->right= key1->left= &null_element; key1->right= key1->left= &null_element;
key1->next= key1->prev= 0; key1->next= key1->prev= 0;
} }
uint new_weight= 0;
for (next=key1->first(); next ; next=next->next) for (next=key1->first(); next ; next=next->next)
{ {
...@@ -9813,22 +9812,21 @@ and_all_keys(RANGE_OPT_PARAM *param, SEL_ARG *key1, SEL_ARG *key2, ...@@ -9813,22 +9812,21 @@ and_all_keys(RANGE_OPT_PARAM *param, SEL_ARG *key1, SEL_ARG *key2,
continue; continue;
} }
next->next_key_part=tmp; next->next_key_part=tmp;
new_weight += 1 + tmp->weight;
if (use_count) if (use_count)
next->increment_use_count(use_count); next->increment_use_count(use_count);
if (param->alloced_sel_args > SEL_ARG::MAX_SEL_ARGS) if (param->alloced_sel_args > SEL_ARG::MAX_SEL_ARGS)
break; break;
} }
else else
{
new_weight += 1 + key2->weight;
next->next_key_part=key2; next->next_key_part=key2;
} }
}
if (!key1) if (!key1)
return &null_element; // Impossible ranges return &null_element; // Impossible ranges
key1->use_count++; key1->use_count++;
key1->weight= new_weight;
/* Re-compute the result tree's weight. */
key1->update_weight_locally();
key1->max_part_no= MY_MAX(key2->max_part_no, key2->part+1); key1->max_part_no= MY_MAX(key2->max_part_no, key2->part+1);
return key1; return key1;
} }
...@@ -9992,6 +9990,30 @@ get_range(SEL_ARG **e1,SEL_ARG **e2,SEL_ARG *root1) ...@@ -9992,6 +9990,30 @@ get_range(SEL_ARG **e1,SEL_ARG **e2,SEL_ARG *root1)
return 0; return 0;
} }
/*
@brief
Update the tree weight.
@detail
Utility function to be called on a SEL_ARG tree root after doing local
modifications concerning changes at this key part.
Assumes that the weight of the graphs connected via next_key_part is
up to dayte.
*/
void SEL_ARG::update_weight_locally()
{
uint new_weight= 0;
const SEL_ARG *sl;
for (sl= first(); sl ; sl= sl->next)
{
new_weight++;
if (sl->next_key_part)
new_weight += sl->next_key_part->weight;
}
weight= new_weight;
}
#ifndef DBUG_OFF #ifndef DBUG_OFF
/* /*
Verify SEL_TREE's weight. Verify SEL_TREE's weight.
...@@ -10728,17 +10750,7 @@ key_or(RANGE_OPT_PARAM *param, SEL_ARG *key1,SEL_ARG *key2) ...@@ -10728,17 +10750,7 @@ key_or(RANGE_OPT_PARAM *param, SEL_ARG *key1,SEL_ARG *key2)
key1->use_count++; key1->use_count++;
/* Re-compute the result tree's weight. */ /* Re-compute the result tree's weight. */
{ key1->update_weight_locally();
uint new_weight= 0;
const SEL_ARG *sl;
for (sl= key1->first(); sl ; sl= sl->next)
{
new_weight++;
if (sl->next_key_part)
new_weight += sl->next_key_part->weight;
}
key1->weight= new_weight;
}
key1->max_part_no= max_part_no; key1->max_part_no= max_part_no;
return key1; return key1;
......
...@@ -316,6 +316,8 @@ class SEL_ARG :public Sql_alloc ...@@ -316,6 +316,8 @@ class SEL_ARG :public Sql_alloc
*/ */
uint weight; uint weight;
enum { MAX_WEIGHT = 32000 }; enum { MAX_WEIGHT = 32000 };
void update_weight_locally();
#ifndef DBUG_OFF #ifndef DBUG_OFF
uint verify_weight(); uint verify_weight();
#endif #endif
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment