diff options
author | Oleksandr Byelkin <sanja@mariadb.com> | 2021-07-23 11:14:13 +0200 |
---|---|---|
committer | Oleksandr Byelkin <sanja@mariadb.com> | 2021-09-27 11:00:51 +0200 |
commit | 3690c549c6e72646ba74f6b4c83813ee4ac3aea4 (patch) | |
tree | e619941de38c42f99b6377e8af00a78700bcfbb1 | |
parent | 1a62c878970fea6d2013c432bbd5aae30dbaca89 (diff) | |
download | mariadb-git-3690c549c6e72646ba74f6b4c83813ee4ac3aea4.tar.gz |
MDEV-24454 Crash at change_item_treebb-10.2-MDEV-24454
Use in_sum_func (and so nest_level) only in LEX to which SELECT lex belong to
Reduce usage of current_select (because it does not always point on the correct
SELECT_LEX, for example with prepare.
Change context for all classes inherited from Item_ident (was only for Item_field) in case of pushing down it to HAVING.
Now name resolution context have to have SELECT_LEX reference if the context is present.
Fixed feedback plugin stack usage.
-rw-r--r-- | mysql-test/r/view.result | 43 | ||||
-rw-r--r-- | mysql-test/suite/plugins/r/feedback_plugin_load.result | 16 | ||||
-rw-r--r-- | mysql-test/suite/plugins/t/feedback_plugin_load.test | 7 | ||||
-rw-r--r-- | mysql-test/t/view.test | 49 | ||||
-rw-r--r-- | plugin/feedback/feedback.cc | 12 | ||||
-rw-r--r-- | sql/item.cc | 104 | ||||
-rw-r--r-- | sql/item.h | 1 | ||||
-rw-r--r-- | sql/item_subselect.cc | 3 | ||||
-rw-r--r-- | sql/item_sum.cc | 8 | ||||
-rw-r--r-- | sql/sql_base.cc | 1 | ||||
-rw-r--r-- | sql/sql_prepare.cc | 2 |
11 files changed, 202 insertions, 44 deletions
diff --git a/mysql-test/r/view.result b/mysql-test/r/view.result index ef4f0a48534..bae415c17ea 100644 --- a/mysql-test/r/view.result +++ b/mysql-test/r/view.result @@ -6833,5 +6833,48 @@ sum(z) DROP TABLE t1; DROP VIEW v1; # +# MDEV-24454: Crash at change_item_tree +# +CREATE TABLE t1(f0 INT); +CREATE VIEW v1 AS +SELECT +f0 AS f1 +FROM t1; +CREATE VIEW v2 AS +SELECT +(SELECT GROUP_CONCAT(v1.f1 SEPARATOR ', ') +FROM v1 n) AS f2, +GROUP_CONCAT('' SEPARATOR ', ') AS f3 +FROM v1; +CREATE VIEW v3 AS +SELECT 1 as f4 FROM v2; +CREATE PROCEDURE p1() +SELECT * FROM v3; +CALL p1(); +f4 +1 +CALL p1(); +f4 +1 +drop procedure p1; +drop view v1,v2,v3; +drop table t1; +# +# MDEV-25631: Crash in st_select_lex::mark_as_dependent with +# VIEW, aggregate and subquery +# +CREATE TABLE t1 (i1 int); +insert into t1 values (1),(2),(3); +CREATE VIEW v1 AS +SELECT t1.i1 FROM (t1 a JOIN t1 ON (t1.i1 = (SELECT t1.i1 FROM t1 b))); +SELECT 1 FROM (SELECT count(((SELECT i1 FROM v1))) FROM v1) dt ; +ERROR 21000: Subquery returns more than 1 row +delete from t1 where i1 > 1; +SELECT 1 FROM (SELECT count(((SELECT i1 FROM v1))) FROM v1) dt ; +1 +1 +drop view v1; +drop table t1; +# # End of 10.2 tests # diff --git a/mysql-test/suite/plugins/r/feedback_plugin_load.result b/mysql-test/suite/plugins/r/feedback_plugin_load.result index 843cd15ac94..8b2a561b9f0 100644 --- a/mysql-test/suite/plugins/r/feedback_plugin_load.result +++ b/mysql-test/suite/plugins/r/feedback_plugin_load.result @@ -24,3 +24,19 @@ VARIABLE_VALUE>0 VARIABLE_NAME 1 Collation used latin1_swedish_ci 1 Collation used utf8_bin 1 Collation used utf8_general_ci +prepare stmt from "SELECT VARIABLE_VALUE>0, VARIABLE_NAME FROM INFORMATION_SCHEMA.FEEDBACK WHERE VARIABLE_NAME LIKE 'Collation used %' ORDER BY VARIABLE_NAME"; +execute stmt; +VARIABLE_VALUE>0 VARIABLE_NAME +1 Collation used binary +1 Collation used latin1_bin +1 Collation used latin1_swedish_ci +1 Collation used utf8_bin +1 Collation used utf8_general_ci +execute stmt; +VARIABLE_VALUE>0 VARIABLE_NAME +1 Collation used binary +1 Collation used latin1_bin +1 Collation used latin1_swedish_ci +1 Collation used utf8_bin +1 Collation used utf8_general_ci +deallocate prepare stmt; diff --git a/mysql-test/suite/plugins/t/feedback_plugin_load.test b/mysql-test/suite/plugins/t/feedback_plugin_load.test index c0546cef0f9..9fbe523e5f3 100644 --- a/mysql-test/suite/plugins/t/feedback_plugin_load.test +++ b/mysql-test/suite/plugins/t/feedback_plugin_load.test @@ -42,3 +42,10 @@ if (`SELECT VERSION() LIKE '%embedded%'`) SELECT VARIABLE_VALUE>0, VARIABLE_NAME FROM INFORMATION_SCHEMA.FEEDBACK WHERE VARIABLE_NAME LIKE 'Collation used %' ORDER BY VARIABLE_NAME; + +prepare stmt from "SELECT VARIABLE_VALUE>0, VARIABLE_NAME FROM INFORMATION_SCHEMA.FEEDBACK WHERE VARIABLE_NAME LIKE 'Collation used %' ORDER BY VARIABLE_NAME"; + +execute stmt; +execute stmt; + +deallocate prepare stmt; diff --git a/mysql-test/t/view.test b/mysql-test/t/view.test index 8cb00f7a6f4..128fa853e10 100644 --- a/mysql-test/t/view.test +++ b/mysql-test/t/view.test @@ -6560,5 +6560,54 @@ DROP TABLE t1; DROP VIEW v1; --echo # +--echo # MDEV-24454: Crash at change_item_tree +--echo # + +CREATE TABLE t1(f0 INT); + +CREATE VIEW v1 AS +SELECT + f0 AS f1 +FROM t1; + +CREATE VIEW v2 AS +SELECT + (SELECT GROUP_CONCAT(v1.f1 SEPARATOR ', ') + FROM v1 n) AS f2, + GROUP_CONCAT('' SEPARATOR ', ') AS f3 +FROM v1; + +CREATE VIEW v3 AS +SELECT 1 as f4 FROM v2; + +CREATE PROCEDURE p1() + SELECT * FROM v3; + +CALL p1(); +CALL p1(); + +drop procedure p1; +drop view v1,v2,v3; +drop table t1; + +--echo # +--echo # MDEV-25631: Crash in st_select_lex::mark_as_dependent with +--echo # VIEW, aggregate and subquery +--echo # + +CREATE TABLE t1 (i1 int); +insert into t1 values (1),(2),(3); #not important +CREATE VIEW v1 AS + SELECT t1.i1 FROM (t1 a JOIN t1 ON (t1.i1 = (SELECT t1.i1 FROM t1 b))); + +--error ER_SUBQUERY_NO_1_ROW +SELECT 1 FROM (SELECT count(((SELECT i1 FROM v1))) FROM v1) dt ; +delete from t1 where i1 > 1; +SELECT 1 FROM (SELECT count(((SELECT i1 FROM v1))) FROM v1) dt ; + +drop view v1; +drop table t1; + +--echo # --echo # End of 10.2 tests --echo # diff --git a/plugin/feedback/feedback.cc b/plugin/feedback/feedback.cc index c76828efa2b..3f6cc9dddbc 100644 --- a/plugin/feedback/feedback.cc +++ b/plugin/feedback/feedback.cc @@ -92,16 +92,18 @@ static COND * const OOM= (COND*)1; static COND* make_cond(THD *thd, TABLE_LIST *tables, LEX_STRING *filter) { Item_cond_or *res= NULL; - Name_resolution_context nrc; + /* A reference to this context will be stored in Item_field */ + Name_resolution_context *nrc= new (thd->mem_root) Name_resolution_context; const char *db= tables->db, *table= tables->alias, *field= tables->table->field[0]->field_name; CHARSET_INFO *cs= &my_charset_latin1; - if (!filter->str) + if (!filter->str || !nrc) return 0; - nrc.init(); - nrc.resolve_in_table_list_only(tables); + nrc->init(); + nrc->resolve_in_table_list_only(tables); + nrc->select_lex= tables->select_lex; res= new (thd->mem_root) Item_cond_or(thd); if (!res) @@ -109,7 +111,7 @@ static COND* make_cond(THD *thd, TABLE_LIST *tables, LEX_STRING *filter) for (; filter->str; filter++) { - Item_field *fld= new (thd->mem_root) Item_field(thd, &nrc, db, table, + Item_field *fld= new (thd->mem_root) Item_field(thd, nrc, db, table, field); Item_string *pattern= new (thd->mem_root) Item_string(thd, filter->str, (uint) filter->length, cs); diff --git a/sql/item.cc b/sql/item.cc index 96e118e6365..2a7c620b864 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -61,11 +61,12 @@ bool cmp_items(Item *a, Item *b) /** Set max_sum_func_level if it is needed */ -inline void set_max_sum_func_level(THD *thd, SELECT_LEX *select) +inline void set_max_sum_func_level(SELECT_LEX *select) { - if (thd->lex->in_sum_func && - thd->lex->in_sum_func->nest_level >= select->nest_level) - set_if_bigger(thd->lex->in_sum_func->max_sum_func_level, + LEX *lex_s= select->parent_lex; + if (lex_s->in_sum_func && + lex_s->in_sum_func->nest_level >= select->nest_level) + set_if_bigger(lex_s->in_sum_func->max_sum_func_level, select->nest_level - 1); } @@ -780,6 +781,7 @@ Item_ident::Item_ident(THD *thd, Name_resolution_context *context_arg, { name = (char*) field_name_arg; name_length= name ? strlen(name) : 0; + DBUG_ASSERT(!context || context->select_lex); } @@ -794,6 +796,7 @@ Item_ident::Item_ident(THD *thd, TABLE_LIST *view_arg, const char *field_name_ar { name = (char*) field_name_arg; name_length= name ? strlen(name) : 0; + DBUG_ASSERT(!context || context->select_lex); } @@ -815,7 +818,9 @@ Item_ident::Item_ident(THD *thd, Item_ident *item) cached_table(item->cached_table), depended_from(item->depended_from), can_be_depended(item->can_be_depended) -{} +{ + DBUG_ASSERT(!context || context->select_lex); +} void Item_ident::cleanup() { @@ -5117,7 +5122,14 @@ Item_field::fix_outer_field(THD *thd, Field **from_field, Item **reference) */ Name_resolution_context *last_checked_context= context; Item **ref= (Item **) not_found_item; - SELECT_LEX *current_sel= thd->lex->current_select; + /* + There are cases when name resolution context is absent (when we are not + doing name resolution), but here the name resolution context should + be present because we are doing name resolution + */ + DBUG_ASSERT(context); + SELECT_LEX *current_sel= context->select_lex; + LEX *lex_s= context->select_lex->parent_lex; Name_resolution_context *outer_context= 0; SELECT_LEX *select= 0; /* Currently derived tables cannot be correlated */ @@ -5218,18 +5230,18 @@ Item_field::fix_outer_field(THD *thd, Field **from_field, Item **reference) return -1; thd->change_item_tree(reference, rf); select->inner_refs_list.push_back(rf, thd->mem_root); - rf->in_sum_func= thd->lex->in_sum_func; + rf->in_sum_func= lex_s->in_sum_func; } /* A reference is resolved to a nest level that's outer or the same as the nest level of the enclosing set function : adjust the value of max_arg_level for the function if it's needed. */ - if (thd->lex->in_sum_func && - thd->lex->in_sum_func->nest_level >= select->nest_level) + if (lex_s->in_sum_func && + lex_s->in_sum_func->nest_level >= select->nest_level) { Item::Type ref_type= (*reference)->type(); - set_if_bigger(thd->lex->in_sum_func->max_arg_level, + set_if_bigger(lex_s->in_sum_func->max_arg_level, select->nest_level); set_field(*from_field); fixed= 1; @@ -5250,10 +5262,10 @@ Item_field::fix_outer_field(THD *thd, Field **from_field, Item **reference) ((ref_type == REF_ITEM || ref_type == FIELD_ITEM) ? (Item_ident*) (*reference) : 0), false); - if (thd->lex->in_sum_func && - thd->lex->in_sum_func->nest_level >= select->nest_level) + if (lex_s->in_sum_func && + lex_s->in_sum_func->nest_level >= select->nest_level) { - set_if_bigger(thd->lex->in_sum_func->max_arg_level, + set_if_bigger(lex_s->in_sum_func->max_arg_level, select->nest_level); } /* @@ -5345,7 +5357,7 @@ Item_field::fix_outer_field(THD *thd, Field **from_field, Item **reference) { outer_context->select_lex->inner_refs_list.push_back((Item_outer_ref*)rf, thd->mem_root); - ((Item_outer_ref*)rf)->in_sum_func= thd->lex->in_sum_func; + ((Item_outer_ref*)rf)->in_sum_func= lex_s->in_sum_func; } thd->change_item_tree(reference, rf); /* @@ -5360,7 +5372,7 @@ Item_field::fix_outer_field(THD *thd, Field **from_field, Item **reference) We can not "move" aggregate function in the place where its arguments are not defined. */ - set_max_sum_func_level(thd, select); + set_max_sum_func_level(select); mark_as_dependent(thd, last_checked_context->select_lex, context->select_lex, rf, rf, false); @@ -5373,7 +5385,7 @@ Item_field::fix_outer_field(THD *thd, Field **from_field, Item **reference) We can not "move" aggregate function in the place where its arguments are not defined. */ - set_max_sum_func_level(thd, select); + set_max_sum_func_level(select); mark_as_dependent(thd, last_checked_context->select_lex, context->select_lex, this, (Item_ident*)*reference, false); @@ -5450,7 +5462,20 @@ bool Item_field::fix_fields(THD *thd, Item **reference) DBUG_ASSERT(fixed == 0); Field *from_field= (Field *)not_found_field; bool outer_fixed= false; - SELECT_LEX *select= thd->lex->current_select; + SELECT_LEX *select; + LEX *lex_s; + if (context) + { + select= context->select_lex; + lex_s= context->select_lex->parent_lex; + } + else + { + // No real name resolution, used somewhere in SP + DBUG_ASSERT(field); + select= NULL; + lex_s= NULL; + } if (!field) // If field is not checked { @@ -5511,7 +5536,7 @@ bool Item_field::fix_fields(THD *thd, Item **reference) We can not "move" aggregate function in the place where its arguments are not defined. */ - set_max_sum_func_level(thd, select); + set_max_sum_func_level(select); set_field(new_field); depended_from= (*((Item_field**)res))->depended_from; return 0; @@ -5540,7 +5565,7 @@ bool Item_field::fix_fields(THD *thd, Item **reference) We can not "move" aggregate function in the place where its arguments are not defined. */ - set_max_sum_func_level(thd, select); + set_max_sum_func_level(select); return FALSE; } } @@ -5577,10 +5602,11 @@ bool Item_field::fix_fields(THD *thd, Item **reference) goto mark_non_agg_field; } - if (thd->lex->in_sum_func && - thd->lex->in_sum_func->nest_level == + if (lex_s && + lex_s->in_sum_func && + lex_s->in_sum_func->nest_level == select->nest_level) - set_if_bigger(thd->lex->in_sum_func->max_arg_level, + set_if_bigger(lex_s->in_sum_func->max_arg_level, select->nest_level); /* if it is not expression from merged VIEW we will set this field. @@ -5646,8 +5672,9 @@ bool Item_field::fix_fields(THD *thd, Item **reference) if (field->vcol_info) fix_session_vcol_expr_for_read(thd, field, field->vcol_info); if (thd->variables.sql_mode & MODE_ONLY_FULL_GROUP_BY && - !outer_fixed && !thd->lex->in_sum_func && + !outer_fixed && select && + !lex_s->in_sum_func && select->cur_pos_in_select_list != UNDEF_POS && select->join) { @@ -5682,13 +5709,13 @@ mark_non_agg_field: */ select_lex= context->select_lex; } - if (!thd->lex->in_sum_func) + if (!lex_s || !lex_s->in_sum_func) select_lex->set_non_agg_field_used(true); else { if (outer_fixed) - thd->lex->in_sum_func->outer_fields.push_back(this, thd->mem_root); - else if (thd->lex->in_sum_func->nest_level != + lex_s->in_sum_func->outer_fields.push_back(this, thd->mem_root); + else if (lex_s->in_sum_func->nest_level != select->nest_level) select_lex->set_non_agg_field_used(true); } @@ -7181,6 +7208,12 @@ Item *get_field_item_for_having(THD *thd, Item *item, st_select_lex *sel) return NULL; } +Item *Item_ident::derived_field_transformer_for_having(THD *thd, uchar *arg) +{ + st_select_lex *sel= (st_select_lex *)arg; + context= &sel->context; + return this; +} Item *Item_field::derived_field_transformer_for_having(THD *thd, uchar *arg) { @@ -7200,12 +7233,13 @@ Item *Item_field::derived_field_transformer_for_having(THD *thd, uchar *arg) Item *Item_direct_view_ref::derived_field_transformer_for_having(THD *thd, uchar *arg) { + st_select_lex *sel= (st_select_lex *)arg; + context= &sel->context; if ((*ref)->marker & SUBSTITUTION_FL) { this->marker|= SUBSTITUTION_FL; return this; } - st_select_lex *sel= (st_select_lex *)arg; table_map tab_map= sel->master_unit()->derived->table->map; if ((item_equal && !(item_equal->used_tables() & tab_map)) || !item_equal) @@ -7501,7 +7535,9 @@ bool Item_ref::fix_fields(THD *thd, Item **reference) { enum_parsing_place place= NO_MATTER; DBUG_ASSERT(fixed == 0); - SELECT_LEX *current_sel= thd->lex->current_select; + + SELECT_LEX *current_sel= context->select_lex; + LEX *lex_s= context->select_lex->parent_lex; if (set_properties_only) { @@ -7662,10 +7698,10 @@ bool Item_ref::fix_fields(THD *thd, Item **reference) the nest level of the enclosing set function : adjust the value of max_arg_level for the function if it's needed. */ - if (thd->lex->in_sum_func && - thd->lex->in_sum_func->nest_level >= + if (lex_s->in_sum_func && + lex_s->in_sum_func->nest_level >= last_checked_context->select_lex->nest_level) - set_if_bigger(thd->lex->in_sum_func->max_arg_level, + set_if_bigger(lex_s->in_sum_func->max_arg_level, last_checked_context->select_lex->nest_level); return FALSE; } @@ -7685,10 +7721,10 @@ bool Item_ref::fix_fields(THD *thd, Item **reference) the nest level of the enclosing set function : adjust the value of max_arg_level for the function if it's needed. */ - if (thd->lex->in_sum_func && - thd->lex->in_sum_func->nest_level >= + if (lex_s->in_sum_func && + lex_s->in_sum_func->nest_level >= last_checked_context->select_lex->nest_level) - set_if_bigger(thd->lex->in_sum_func->max_arg_level, + set_if_bigger(lex_s->in_sum_func->max_arg_level, last_checked_context->select_lex->nest_level); } } diff --git a/sql/item.h b/sql/item.h index 8be9a7fe417..35700129243 100644 --- a/sql/item.h +++ b/sql/item.h @@ -2630,6 +2630,7 @@ public: Collect outer references */ virtual bool collect_outer_ref_processor(void *arg); + Item *derived_field_transformer_for_having(THD *thd, uchar *arg); friend bool insert_fields(THD *thd, Name_resolution_context *context, const char *db_name, const char *table_name, List_iterator<Item> *it, diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index 25621dfe104..3f9a760ce09 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -5190,8 +5190,9 @@ bool subselect_hash_sj_engine::make_semi_join_conds() NULL, TL_READ); tmp_table_ref->table= tmp_table; - context= new Name_resolution_context; + context= new (thd->mem_root) Name_resolution_context; context->init(); + context->select_lex= item_in->unit->first_select(); context->first_name_resolution_table= context->last_name_resolution_table= tmp_table_ref; semi_join_conds_context= context; diff --git a/sql/item_sum.cc b/sql/item_sum.cc index dd65f04a652..f20be3b5226 100644 --- a/sql/item_sum.cc +++ b/sql/item_sum.cc @@ -68,6 +68,7 @@ size_t Item_sum::ram_limitation(THD *thd) bool Item_sum::init_sum_func_check(THD *thd) { SELECT_LEX *curr_sel= thd->lex->current_select; + LEX *lex_s= (curr_sel ? curr_sel->parent_lex : thd->lex); if (curr_sel && !curr_sel->name_visibility_map) { for (SELECT_LEX *sl= curr_sel; sl; sl= sl->context.outer_select()) @@ -82,9 +83,9 @@ bool Item_sum::init_sum_func_check(THD *thd) return TRUE; } /* Set a reference to the nesting set function if there is any */ - in_sum_func= thd->lex->in_sum_func; + in_sum_func= lex_s->in_sum_func; /* Save a pointer to object to be used in items for nested set functions */ - thd->lex->in_sum_func= this; + lex_s->in_sum_func= this; nest_level= thd->lex->current_select->nest_level; ref_by= 0; aggr_level= -1; @@ -151,6 +152,7 @@ bool Item_sum::init_sum_func_check(THD *thd) bool Item_sum::check_sum_func(THD *thd, Item **ref) { SELECT_LEX *curr_sel= thd->lex->current_select; + LEX *lex_s= curr_sel->parent_lex; nesting_map allow_sum_func= (thd->lex->allow_sum_func & curr_sel->name_visibility_map); bool invalid= FALSE; @@ -310,7 +312,7 @@ bool Item_sum::check_sum_func(THD *thd, Item **ref) } aggr_sel->set_agg_func_used(true); update_used_tables(); - thd->lex->in_sum_func= in_sum_func; + lex_s->in_sum_func= in_sum_func; return FALSE; } diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 9a66b27a454..5173df260d5 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -6440,6 +6440,7 @@ set_new_item_local_context(THD *thd, Item_ident *item, TABLE_LIST *table_ref) if (!(context= new (thd->mem_root) Name_resolution_context)) return TRUE; context->init(); + context->select_lex= table_ref->select_lex; context->first_name_resolution_table= context->last_name_resolution_table= table_ref; item->context= context; diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index 624bcb90cd5..9339cb925e5 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -3060,6 +3060,7 @@ void reinit_stmt_before_use(THD *thd, LEX *lex) } for (; sl; sl= sl->next_select_in_list()) { + sl->parent_lex->in_sum_func= NULL; if (sl->changed_elements & TOUCHED_SEL_COND) { /* remove option which was put by mysql_explain_union() */ @@ -3190,7 +3191,6 @@ void reinit_stmt_before_use(THD *thd, LEX *lex) lex->result->set_thd(thd); } lex->allow_sum_func= 0; - lex->in_sum_func= NULL; DBUG_VOID_RETURN; } |