diff options
author | Igor Babaev <igor@askmonty.org> | 2019-05-08 00:08:09 -0700 |
---|---|---|
committer | Igor Babaev <igor@askmonty.org> | 2019-05-08 09:45:24 -0700 |
commit | 09aa5d3f6988e92f869adfd13a925bfead6f040d (patch) | |
tree | 6e487e5d025573d3d638be0d07b52cb6210609f0 /sql/sql_tvc.cc | |
parent | 9d805004d8e7913a98d25142d22627a4a6e39063 (diff) | |
download | mariadb-git-09aa5d3f6988e92f869adfd13a925bfead6f040d.tar.gz |
MDEV-17894 Assertion `(thd->lex)->current_select' failed in MYSQLparse(),
query with VALUES()
A table value constructor can be used in all contexts where a select
can be used. In particular an ORDER BY clause or a LIMIT clause or both
of them can be attached to a table value constructor to produce a new
query. Unfortunately execution of such queries was not supported.
This patch fixes the problem.
Diffstat (limited to 'sql/sql_tvc.cc')
-rw-r--r-- | sql/sql_tvc.cc | 232 |
1 files changed, 199 insertions, 33 deletions
diff --git a/sql/sql_tvc.cc b/sql/sql_tvc.cc index a05673f2a6c..c9b55fe210e 100644 --- a/sql/sql_tvc.cc +++ b/sql/sql_tvc.cc @@ -263,6 +263,35 @@ bool table_value_constr::prepare(THD *thd, SELECT_LEX *sl, if (result && result->prepare(sl->item_list, unit_arg)) DBUG_RETURN(true); + /* + setup_order() for a TVC is not called when the following is true + (thd->lex->context_analysis_only & CONTEXT_ANALYSIS_ONLY_VIEW) + */ + + thd->where="order clause"; + ORDER *order= sl->order_list.first; + for (; order; order=order->next) + { + Item *order_item= *order->item; + if (order_item->type() == Item::INT_ITEM && order_item->basic_const_item()) + { + uint count= 0; + if (order->counter_used) + count= order->counter; // counter was once resolved + else + count= (uint) order_item->val_int(); + if (!count || count > first_elem->elements) + { + my_error(ER_BAD_FIELD_ERROR, MYF(0), + order_item->full_name(), thd->where); + DBUG_RETURN(true); + } + order->in_field_list= 1; + order->counter= count; + order->counter_used= 1; + } + } + select_lex->in_tvc= false; DBUG_RETURN(false); } @@ -344,6 +373,7 @@ bool table_value_constr::exec(SELECT_LEX *sl) DBUG_ENTER("table_value_constr::exec"); List_iterator_fast<List_item> li(lists_of_values); List_item *elem; + ha_rows send_records= 0; if (select_options & SELECT_DESCRIBE) DBUG_RETURN(false); @@ -357,7 +387,13 @@ bool table_value_constr::exec(SELECT_LEX *sl) while ((elem= li++)) { - result->send_data(*elem); + if (send_records >= sl->master_unit()->select_limit_cnt) + break; + int rc= result->send_data(*elem); + if (!rc) + send_records++; + else if (rc > 0) + DBUG_RETURN(true); } if (result->send_eof()) @@ -436,6 +472,12 @@ void table_value_constr::print(THD *thd, String *str, print_list_item(str, list, query_type); } + if (select_lex->order_list.elements) + { + str->append(STRING_WITH_LEN(" order by ")); + select_lex->print_order(str, select_lex->order_list.first, query_type); + } + select_lex->print_limit(thd, str, query_type); } @@ -533,7 +575,8 @@ static bool create_tvc_name(THD *thd, st_select_lex *parent_select, char buff[6]; alias->length= my_snprintf(buff, sizeof(buff), - "tvc_%u", parent_select->curr_tvc_name); + "tvc_%u", + parent_select ? parent_select->curr_tvc_name : 0); alias->str= thd->strmake(buff, alias->length); if (!alias->str) return true; @@ -542,19 +585,57 @@ static bool create_tvc_name(THD *thd, st_select_lex *parent_select, } -bool Item_subselect::wrap_tvc_in_derived_table(THD *thd, - st_select_lex *tvc_sl) +/** + @brief + Check whether TVC used in unit is to be wrapped into select + + @details + TVC used in unit that contains more than one members is to be wrapped + into select if it is tailed with ORDER BY ... LIMIT n [OFFSET m] + + @retval + true if TVC is to be wrapped + false otherwise +*/ + +bool table_value_constr::to_be_wrapped_as_with_tail() +{ + return select_lex->master_unit()->first_select()->next_select() && + select_lex->order_list.elements && select_lex->explicit_limit; +} + + +/** + @brief + Wrap table value constructor into a select + + @param thd The context handler + @param tvc_sl The TVC to wrap + @parent_select The parent select if tvc_sl used in a subquery + + @details + The function wraps the TVC tvc_sl into a select: + the function transforms the TVC of the form VALUES (v1), ... (vn) into + the select of the form + SELECT * FROM (VALUES (v1), ... (vn)) tvc_x + + @retval pointer to the result of of the transformation if successful + NULL - otherwise +*/ + +static +st_select_lex *wrap_tvc(THD *thd, st_select_lex *tvc_sl, + st_select_lex *parent_select) { LEX *lex= thd->lex; - /* SELECT_LEX object where the transformation is performed */ - SELECT_LEX *parent_select= lex->current_select; + select_result *save_result= thd->lex->result; uint8 save_derived_tables= lex->derived_tables; + thd->lex->result= NULL; Query_arena backup; Query_arena *arena= thd->activate_stmt_arena_if_needed(&backup); - /* - Create SELECT_LEX of the subquery SQ used in the result of transformation + Create SELECT_LEX of the select used in the result of transformation */ lex->current_select= tvc_sl; if (mysql_new_select(lex, 0, NULL)) @@ -562,15 +643,15 @@ bool Item_subselect::wrap_tvc_in_derived_table(THD *thd, mysql_init_select(lex); /* Create item list as '*' for the subquery SQ */ Item *item; - SELECT_LEX *sq_select; // select for IN subquery; - sq_select= lex->current_select; - sq_select->linkage= tvc_sl->linkage; - sq_select->parsing_place= SELECT_LIST; - item= new (thd->mem_root) Item_field(thd, &sq_select->context, + SELECT_LEX *wrapper_sl; + wrapper_sl= lex->current_select; + wrapper_sl->linkage= tvc_sl->linkage; + wrapper_sl->parsing_place= SELECT_LIST; + item= new (thd->mem_root) Item_field(thd, &wrapper_sl->context, NULL, NULL, &star_clex_str); if (item == NULL || add_item_to_list(thd, item)) goto err; - (sq_select->with_wild)++; + (wrapper_sl->with_wild)++; /* Exclude SELECT with TVC */ tvc_sl->exclude(); @@ -585,11 +666,11 @@ bool Item_subselect::wrap_tvc_in_derived_table(THD *thd, derived_unit= tvc_select->master_unit(); tvc_select->linkage= DERIVED_TABLE_TYPE; - lex->current_select= sq_select; + lex->current_select= wrapper_sl; /* Create the name of the wrapping derived table and - add it to the FROM list of the subquery SQ + add it to the FROM list of the wrapper */ Table_ident *ti; LEX_CSTRING alias; @@ -598,35 +679,120 @@ bool Item_subselect::wrap_tvc_in_derived_table(THD *thd, create_tvc_name(thd, parent_select, &alias)) goto err; if (!(derived_tab= - sq_select->add_table_to_list(thd, - ti, &alias, 0, - TL_READ, MDL_SHARED_READ))) + wrapper_sl->add_table_to_list(thd, + ti, &alias, 0, + TL_READ, MDL_SHARED_READ))) goto err; - sq_select->add_joined_table(derived_tab); - sq_select->add_where_field(derived_unit->first_select()); - sq_select->context.table_list= sq_select->table_list.first; - sq_select->context.first_name_resolution_table= sq_select->table_list.first; - sq_select->table_list.first->derived_type= DTYPE_TABLE | DTYPE_MATERIALIZE; + wrapper_sl->add_joined_table(derived_tab); + wrapper_sl->add_where_field(derived_unit->first_select()); + wrapper_sl->context.table_list= wrapper_sl->table_list.first; + wrapper_sl->context.first_name_resolution_table= wrapper_sl->table_list.first; + wrapper_sl->table_list.first->derived_type= DTYPE_TABLE | DTYPE_MATERIALIZE; lex->derived_tables|= DERIVED_SUBQUERY; - sq_select->where= 0; - sq_select->set_braces(false); + wrapper_sl->where= 0; + wrapper_sl->set_braces(false); derived_unit->set_with_clause(0); - if (engine->engine_type() == subselect_engine::SINGLE_SELECT_ENGINE) - ((subselect_single_select_engine *) engine)->change_select(sq_select); - if (arena) thd->restore_active_arena(arena, &backup); - lex->current_select= sq_select; - return false; + thd->lex->result= save_result; + return wrapper_sl; err: if (arena) thd->restore_active_arena(arena, &backup); + thd->lex->result= save_result; lex->derived_tables= save_derived_tables; - lex->current_select= parent_select; - return true; + return 0; +} + + +/** + @brief + Wrap TVC with ORDER BY ... LIMIT tail into a select + + @param thd The context handler + @param tvc_sl The TVC to wrap + + @details + The function wraps the TVC tvc_sl into a select: + the function transforms the TVC with tail of the form + VALUES (v1), ... (vn) ORDER BY ... LIMIT n [OFFSET m] + into the select with the same tail of the form + SELECT * FROM (VALUES (v1), ... (vn)) tvc_x + ORDER BY ... LIMIT n [OFFSET m] + + @retval pointer to the result of of the transformation if successful + NULL - otherwise +*/ + +st_select_lex *wrap_tvc_with_tail(THD *thd, st_select_lex *tvc_sl) +{ + st_select_lex *wrapper_sl= wrap_tvc(thd, tvc_sl, NULL); + if (!wrapper_sl) + return NULL; + + wrapper_sl->order_list= tvc_sl->order_list; + wrapper_sl->select_limit= tvc_sl->select_limit; + wrapper_sl->offset_limit= tvc_sl->offset_limit; + wrapper_sl->braces= tvc_sl->braces; + wrapper_sl->explicit_limit= tvc_sl->explicit_limit; + tvc_sl->order_list.empty(); + tvc_sl->select_limit= NULL; + tvc_sl->offset_limit= NULL; + tvc_sl->braces= 0; + tvc_sl->explicit_limit= false; + if (tvc_sl->select_number == 1) + { + tvc_sl->select_number= wrapper_sl->select_number; + wrapper_sl->select_number= 1; + } + if (tvc_sl->master_unit()->union_distinct == tvc_sl) + { + wrapper_sl->master_unit()->union_distinct= wrapper_sl; + } + thd->lex->current_select= wrapper_sl; + return wrapper_sl; +} + + +/** + @brief + Wrap TVC in a subselect into a select + + @param thd The context handler + @param tvc_sl The TVC to wrap + + @details + The function wraps the TVC tvc_sl used in a subselect into a select + the function transforms the TVC of the form VALUES (v1), ... (vn) + into the select the form + SELECT * FROM (VALUES (v1), ... (vn)) tvc_x + and replaces the subselect with the result of the transformation. + + @retval false if successfull + true otherwise +*/ + +bool Item_subselect::wrap_tvc_into_select(THD *thd, st_select_lex *tvc_sl) +{ + LEX *lex= thd->lex; + /* SELECT_LEX object where the transformation is performed */ + SELECT_LEX *parent_select= lex->current_select; + SELECT_LEX *wrapper_sl= wrap_tvc(thd, tvc_sl, parent_select); + if (wrapper_sl) + { + if (engine->engine_type() == subselect_engine::SINGLE_SELECT_ENGINE) + ((subselect_single_select_engine *) engine)->change_select(wrapper_sl); + lex->current_select= wrapper_sl; + return false; + } + else + { + lex->current_select= parent_select; + return true; + } } |