mirror of
https://github.com/MariaDB/server.git
synced 2025-07-30 16:24:05 +03:00
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.
This commit is contained in:
@ -159,7 +159,6 @@ left(@json, 2500)
|
||||
]
|
||||
]
|
||||
## Repeat the above with a bit higher max_weight:
|
||||
set @tmp9750_weight=@@optimizer_max_sel_arg_weight;
|
||||
set optimizer_max_sel_arg_weight=120;
|
||||
explain select * from t1 where
|
||||
kp1 in (1,2,3,4,5,6,7,8,9,10) and
|
||||
@ -225,3 +224,18 @@ SELECT *
|
||||
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;
|
||||
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'));
|
||||
select left(@json, 2500);
|
||||
|
||||
--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;
|
||||
explain select * from t1 where
|
||||
kp1 in (1,2,3,4,5,6,7,8,9,10) and
|
||||
@ -110,3 +109,34 @@ SELECT *
|
||||
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;
|
||||
|
||||
--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,
|
||||
key1->right= key1->left= &null_element;
|
||||
key1->next= key1->prev= 0;
|
||||
}
|
||||
uint new_weight= 0;
|
||||
|
||||
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,
|
||||
continue;
|
||||
}
|
||||
next->next_key_part=tmp;
|
||||
new_weight += 1 + tmp->weight;
|
||||
if (use_count)
|
||||
next->increment_use_count(use_count);
|
||||
if (param->alloced_sel_args > SEL_ARG::MAX_SEL_ARGS)
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
new_weight += 1 + key2->weight;
|
||||
next->next_key_part=key2;
|
||||
}
|
||||
}
|
||||
if (!key1)
|
||||
return &null_element; // Impossible ranges
|
||||
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);
|
||||
return key1;
|
||||
}
|
||||
@ -9992,6 +9990,30 @@ get_range(SEL_ARG **e1,SEL_ARG **e2,SEL_ARG *root1)
|
||||
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
|
||||
/*
|
||||
Verify SEL_TREE's weight.
|
||||
@ -10728,17 +10750,7 @@ end:
|
||||
key1->use_count++;
|
||||
|
||||
/* Re-compute the result tree's weight. */
|
||||
{
|
||||
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->update_weight_locally();
|
||||
|
||||
key1->max_part_no= max_part_no;
|
||||
return key1;
|
||||
|
@ -316,6 +316,8 @@ public:
|
||||
*/
|
||||
uint weight;
|
||||
enum { MAX_WEIGHT = 32000 };
|
||||
|
||||
void update_weight_locally();
|
||||
#ifndef DBUG_OFF
|
||||
uint verify_weight();
|
||||
#endif
|
||||
|
Reference in New Issue
Block a user