Quelle sql_tvc.cc
Sprache: C
/* Copyright (c) 2017, 2022, MariaDB
This program is free software ; you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation ; version 2 of the License .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with this program ; if not , write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
#include "mariadb.h"
#include "sql_list.h"
#include "sql_tvc.h"
#include "sql_class.h"
#include "opt_range.h"
#include "sql_select.h"
#include "sql_explain.h"
#include "sql_parse.h"
#include "sql_cte.h"
#include "my_json_writer.h"
/**
@ brief
Walk through all VALUES items .
@ param
@ param processor - the processor to call for each Item
@ param walk_subquery - if should dive into subquery items
@ param argument - the argument to pass recursively
@ retval
true on error
false on success
*/
bool table_value_constr::walk_values(Item_processor processor,
bool walk_subquery,
void *argument)
{
List_iterator_fast<List_item> list_item_it(lists_of_values);
while (List_item *list= list_item_it++)
{
List_iterator_fast<Item> item_it(*list);
while (Item *item= item_it++)
{
if (item->walk(&Item::unknown_splocal_processor, argument, 0 ))
return true ;
}
}
return false ;
}
/**
@ brief
Fix fields for TVC values
@ param
@ param thd The context of the statement
@ param li The iterator on the list of lists
@ details
Call fix_fields procedure for TVC values .
@ retval
true if an error was reported
false otherwise
*/
bool fix_fields_for_tvc(THD *thd, List_iterator_fast<List_item> &li)
{
DBUG_ENTER("fix_fields_for_tvc" );
List_item *lst;
li.rewind();
while ((lst= li++))
{
List_iterator<Item> it(*lst);
Item *item;
while ((item= it++))
{
/*
Some items have already been fixed .
For example Item_splocal items get fixed in
Item_splocal : : append_for_log ( ) , which is called from subst_spvars ( )
while replacing their values to NAME_CONST ( ) s .
So fix only those that have not been .
*/
if (item->fix_fields_if_needed_for_scalar(thd, it.ref()) ||
item->check_is_evaluable_expression_or_error())
DBUG_RETURN(true );
}
}
DBUG_RETURN(false );
}
/**
@ brief
Defines types of matrix columns elements where matrix rows are defined by
some lists of values .
@ param
@ param thd The context of the statement
@ param li The iterator on the list of lists
@ param holders The structure where types of matrix columns are stored
@ param first_list_el_count Count of the list values . It should be the same
for each list of lists elements . It contains
number of elements of the first list from list of
lists .
@ details
For each list list_a from list of lists the procedure gets its elements
types and aggregates them with the previous ones stored in holders . If
list_a is the first one in the list of lists its elements types are put in
holders . The errors can be reported when count of list_a elements is
different from the first_list_el_count . Also error can be reported when
aggregation can ' t be made .
@ retval
true if an error was reported
false otherwise
*/
bool join_type_handlers_for_tvc(THD *thd, List_iterator_fast<List_item> &li,
Type_holder *holders, uint first_list_el_count)
{
DBUG_ENTER("join_type_handlers_for_tvc" );
List_item *lst;
li.rewind();
bool first= true ;
while ((lst= li++))
{
List_iterator_fast<Item> it(*lst);
Item *item;
if (first_list_el_count != lst->elements)
{
my_message(ER_WRONG_NUMBER_OF_VALUES_IN_TVC,
ER_THD(thd, ER_WRONG_NUMBER_OF_VALUES_IN_TVC),
MYF(0 ));
DBUG_RETURN(true );
}
for (uint pos= 0 ; (item=it++); pos++)
{
const Type_handler *item_type_handler= item->real_type_handler();
if (first)
holders[pos].set_handler(item_type_handler);
else if (holders[pos].aggregate_for_result(item_type_handler))
{
my_error(ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION, MYF(0 ),
holders[pos].type_handler()->name().ptr(),
item_type_handler->name().ptr(),
"TABLE VALUE CONSTRUCTOR" );
DBUG_RETURN(true );
}
}
first= false ;
}
DBUG_RETURN(false );
}
/**
@ brief
Define attributes of matrix columns elements where matrix rows are defined
by some lists of values .
@ param
@ param thd The context of the statement
@ param li The iterator on the list of lists
@ param holders The structure where names of matrix columns are stored
@ param count_of_lists Count of list of lists elements
@ param first_list_el_count Count of the list values . It should be the same
for each list of lists elements . It contains
number of elements of the first list from list
of lists .
@ details
For each list list_a from list of lists the procedure gets its elements
attributes and aggregates them with the previous ones stored in holders .
The errors can be reported when aggregation can ' t be made .
@ retval
true if an error was reported
false otherwise
*/
bool get_type_attributes_for_tvc(THD *thd,
List_iterator_fast<List_item> &li,
Type_holder *holders, uint count_of_lists,
uint first_list_el_count)
{
DBUG_ENTER("get_type_attributes_for_tvc" );
List_item *lst;
li.rewind();
for (uint pos= 0 ; pos < first_list_el_count; pos++)
{
if (holders[pos].alloc_arguments(thd, count_of_lists))
DBUG_RETURN(true );
}
while ((lst= li++))
{
List_iterator_fast<Item> it(*lst);
Item *item;
for (uint holder_pos= 0 ; (item= it++); holder_pos++)
{
DBUG_ASSERT(item->fixed());
holders[holder_pos].add_argument(item);
}
}
for (uint pos= 0 ; pos < first_list_el_count; pos++)
{
if (holders[pos].aggregate_attributes(thd))
DBUG_RETURN(true );
}
DBUG_RETURN(false );
}
/**
@ brief
Prepare of TVC
@ param
@ param thd The context of the statement
@ param sl The select where this TVC is defined
@ param tmp_result Structure that contains the information
about where to send the result of the query
@ param unit_arg The union where sl is defined
@ details
Gets types and attributes of values of this TVC that will be used
for temporary table creation for this TVC . It creates Item_type_holders
for each element of the first list from list of lists ( VALUES from tvc ) ,
using its elements name , defined type and attribute .
@ retval
true if an error was reported
false otherwise
*/
bool table_value_constr::prepare(THD *thd, SELECT_LEX *sl,
select_result *tmp_result,
st_select_lex_unit *unit_arg)
{
DBUG_ENTER("table_value_constr::prepare" );
select_lex->in_tvc= true ;
List_iterator_fast<List_item> li(lists_of_values);
List_item *first_elem= li++;
uint cnt= first_elem->elements;
Type_holder *holders= type_holders;
if (cnt == 0 )
{
my_error(ER_EMPTY_ROW_IN_TVC, MYF(0 ));
DBUG_RETURN(true );
}
if (fix_fields_for_tvc(thd, li))
DBUG_RETURN(true );
if (!holders)
{
DBUG_ASSERT(thd->stmt_arena->is_stmt_prepare_or_first_stmt_execute() ||
thd->stmt_arena->is_conventional());
holders= type_holders=
new (thd->active_stmt_arena_to_use()->mem_root) Type_holder[cnt];
if (!holders ||
join_type_handlers_for_tvc(thd, li, holders, cnt) ||
get_type_attributes_for_tvc(thd, li, holders,
lists_of_values.elements, cnt))
DBUG_RETURN(true );
List_iterator_fast<Item> it(*first_elem);
Item *item;
Query_arena *arena, backup;
arena=thd->activate_stmt_arena_if_needed(&backup);
sl->item_list.empty();
for (uint pos= 0 ; (item= it++); pos++)
{
/* Error's in 'new' will be detected after loop */
Item_type_holder *new_holder= new (thd->mem_root)
Item_type_holder(thd, item, holders[pos].type_handler(),
&holders[pos]/*Type_all_attributes*/,
holders[pos].get_maybe_null());
sl->item_list.push_back(new_holder);
}
if (arena)
thd->restore_active_arena(arena, &backup);
if (unlikely(thd->is_fatal_error))
DBUG_RETURN(true ); // out of memory
}
result= tmp_result;
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= THD_WHERE::ORDER_CLAUSE;
ORDER *order= sl->order_list.first;
for (; order; order=order->next)
{
Item *order_item= *order->item;
if (order_item->is_order_clause_position())
{
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(thd));
DBUG_RETURN(true );
}
order->in_field_list= 1 ;
order->counter= count;
order->counter_used= 1 ;
}
}
select_lex->in_tvc= false ;
DBUG_RETURN(false );
}
/**
Save Query Plan Footprint
*/
int table_value_constr::save_explain_data_intern(THD *thd,
Explain_query *output)
{
const char *message= "No tables used" ;
DBUG_ENTER("table_value_constr::save_explain_data_intern" );
DBUG_PRINT("info" , ("Select %p, type %s, message %s" ,
select_lex, select_lex->type,
message));
DBUG_ASSERT(have_query_plan == QEP_AVAILABLE);
/* There should be no attempts to save query plans for merged selects */
DBUG_ASSERT(!select_lex->master_unit()->derived ||
select_lex->master_unit()->derived->is_materialized_derived() ||
select_lex->master_unit()->derived->is_with_table());
explain= new (output->mem_root) Explain_select(output->mem_root,
thd->lex->analyze_stmt);
if (!explain)
DBUG_RETURN(1 );
select_lex->set_explain_type(true );
explain->select_id= select_lex->select_number;
explain->select_type= select_lex->type;
explain->linkage= select_lex->get_linkage();
explain->using_temporary= false ;
explain->using_filesort= false ;
/* Setting explain->message means that all other members are invalid */
explain->message= message;
if (select_lex->master_unit()->derived)
explain->connection_type= Explain_node::EXPLAIN_NODE_DERIVED;
for (SELECT_LEX_UNIT *unit= select_lex->first_inner_unit();
unit;
unit= unit->next_unit())
{
explain->add_child(unit->first_select()->select_number);
}
output->add_node(explain);
if (select_lex->is_top_level_node())
output->query_plan_ready();
DBUG_RETURN(0 );
}
/**
Optimization of TVC
*/
bool table_value_constr::optimize(THD *thd)
{
create_explain_query_if_not_exists(thd->lex, thd->mem_root);
have_query_plan= QEP_AVAILABLE;
if (select_lex->select_number != FAKE_SELECT_LEX_ID &&
have_query_plan != QEP_NOT_PRESENT_YET &&
thd->lex->explain && // for "SET" command in SPs.
(!thd->lex->explain->get_select(select_lex->select_number)))
{
if (save_explain_data_intern(thd, thd->lex->explain))
return true ;
}
if (select_lex->optimize_unflattened_subqueries(true ))
return true ;
return false ;
}
/**
Execute of TVC
*/
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;
THD *cur_thd= sl->parent_lex->thd;
ha_rows send_records= 0 ;
int rc=0 ;
if (select_options & SELECT_DESCRIBE)
DBUG_RETURN(false );
if (result->send_result_set_metadata(sl->item_list,
Protocol::SEND_NUM_ROWS |
Protocol::SEND_EOF))
{
DBUG_RETURN(true );
}
fix_rownum_pointers(sl->parent_lex->thd, sl, &send_records);
while ((elem= li++))
{
cur_thd->get_stmt_da()->inc_current_row_for_warning();
if (send_records >= sl->master_unit()->lim.get_select_limit())
break ;
rc= result->send_data_with_check(*elem, sl->master_unit(), send_records);
if (!rc)
send_records++;
else if (rc > 0 )
DBUG_RETURN(true );
}
if (result->send_eof())
DBUG_RETURN(true );
DBUG_RETURN(false );
}
/**
@ brief
Print list
@ param str The reference on the string representation of the list
@ param list The list that needed to be print
@ param query_type The mode of printing
@ details
The method saves a string representation of list in the
string str .
*/
void print_list_item(String *str, List_item *list,
enum_query_type query_type)
{
bool is_first_elem= true ;
List_iterator_fast<Item> it(*list);
Item *item;
str->append('(' );
while ((item= it++))
{
if (is_first_elem)
is_first_elem= false ;
else
str->append(',' );
item->print(str, query_type);
}
str->append(')' );
}
/**
@ brief
Print this TVC
@ param thd The context of the statement
@ param str The reference on the string representation of this TVC
@ param query_type The mode of printing
@ details
The method saves a string representation of this TVC in the
string str .
*/
void table_value_constr::print(THD *thd, String *str,
enum_query_type query_type)
{
DBUG_ASSERT(thd);
str->append(STRING_WITH_LEN("values " ));
bool is_first_elem= true ;
List_iterator_fast<List_item> li(lists_of_values);
List_item *list;
while ((list= li++))
{
if (is_first_elem)
is_first_elem= false ;
else
str->append(',' );
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);
}
/**
@ brief
Create list of lists for TVC from the list of this IN predicate
@ param thd The context of the statement
@ param values TVC list of values
@ details
The method uses the list of values of this IN predicate to build
an equivalent list of values that can be used in TVC .
E . g . :
< value_list > = 5 , 2 , 7
< transformed_value_list > = ( 5 ) , ( 2 ) , ( 7 )
< value_list > = ( 5 , 2 ) , ( 7 , 1 )
< transformed_value_list > = ( 5 , 2 ) , ( 7 , 1 )
@ retval
false if the method succeeds
true otherwise
*/
bool Item_func_in::create_value_list_for_tvc(THD *thd,
List< List<Item> > *values)
{
bool is_list_of_rows= args[1 ]->type() == Item::ROW_ITEM;
for (uint i=1 ; i < arg_count; i++)
{
char col_name[8 ];
List<Item> *tvc_value;
if (!(tvc_value= new (thd->mem_root) List<Item>()))
return true ;
if (is_list_of_rows)
{
Item_row *row_list= (Item_row *)(args[i]);
if (!row_list)
return true ;
for (uint j=0 ; j < row_list->cols(); j++)
{
if (i == 1 )
{
snprintf(col_name, sizeof (col_name), "_col_%i" , j+1 );
row_list->element_index(j)->set_name(thd, col_name, strlen(col_name),
thd->charset());
}
if (tvc_value->push_back(row_list->element_index(j),
thd->mem_root))
return true ;
}
}
else
{
if (i == 1 )
{
snprintf(col_name, sizeof (col_name), "_col_%i" , 1 );
args[i]->set_name(thd, col_name, strlen(col_name), thd->charset());
}
if (tvc_value->push_back(args[i]))
return true ;
}
if (values->push_back(tvc_value, thd->mem_root))
return true ;
}
return false ;
}
/**
@ brief
Create name for the derived table defined by TVC
@ param thd The context of the statement
@ param parent_select The SELECT where derived table is used
@ param alias The returned created name
@ details
Create name for the derived table using current TVC number
for this parent_select stored in parent_select
@ retval
true if creation fails
false otherwise
*/
static bool create_tvc_name(THD *thd, st_select_lex *parent_select,
LEX_CSTRING *alias)
{
char buff[6 ];
alias->length= my_snprintf(buff, sizeof (buff),
"tvc_%u" ,
parent_select ? parent_select->curr_tvc_name : 0 );
alias->str= thd->strmake(buff, alias->length);
if (!alias->str)
return true ;
return false ;
}
/**
@ 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->limit_params.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 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_result *save_result= 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);
Item *item;
SELECT_LEX *wrapper_sl;
SELECT_LEX_UNIT *derived_unit;
/*
Create SELECT_LEX wrapper_sl of the select used in the result
of the transformation
*/
if (!(wrapper_sl= new (thd->mem_root) SELECT_LEX()))
goto err;
wrapper_sl->select_number= ++thd->lex->stmt_lex->current_select_number;
wrapper_sl->parent_lex= lex; /* Used in init_query. */
wrapper_sl->make_empty_select();
wrapper_sl->is_tvc_wrapper= true ;
wrapper_sl->nest_level= tvc_sl->nest_level;
wrapper_sl->parsing_place= tvc_sl->parsing_place;
wrapper_sl->distinct= tvc_sl->distinct;
wrapper_sl->set_linkage(tvc_sl->get_linkage());
wrapper_sl->exclude_from_table_unique_test=
tvc_sl->exclude_from_table_unique_test;
lex->current_select= wrapper_sl;
item= new (thd->mem_root) Item_field(thd, &wrapper_sl->context,
star_clex_str);
if (item == NULL || add_item_to_list(thd, item))
goto err;
(wrapper_sl->with_wild)++;
/* Include the newly created select into the global list of selects */
wrapper_sl->include_global((st_select_lex_node**)&lex->all_selects_list);
/* Substitute select node used of TVC for the newly created select */
tvc_sl->substitute_in_tree(wrapper_sl);
/*
Create a unit for the substituted select used for TVC and attach it
to the wrapper select wrapper_sl as the only unit . The created
unit is the unit for the derived table tvc_x of the transformation .
*/
if (!(derived_unit= new (thd->mem_root) SELECT_LEX_UNIT()))
goto err;
derived_unit->init_query();
derived_unit->thd= thd;
derived_unit->include_down(wrapper_sl);
derived_unit->distinct= tvc_sl->distinct;
/*
Attach the select used of TVC as the only slave to the unit for
the derived table tvc_x of the transformation
*/
derived_unit->attach_single(tvc_sl);
tvc_sl->set_linkage(DERIVED_TABLE_TYPE);
/*
Generate the name of the derived table created for TVC and
add it to the FROM list of the wrapping select
*/
Table_ident *ti;
LEX_CSTRING alias;
TABLE_LIST *derived_tab;
if (!(ti= new (thd->mem_root) Table_ident(derived_unit)) ||
create_tvc_name(thd, parent_select, &alias))
goto err;
if (!(derived_tab=
wrapper_sl->add_table_to_list(thd,
ti, &alias, 0 ,
TL_READ, MDL_SHARED_READ)))
goto err;
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;
if (arena)
thd->restore_active_arena(arena, &backup);
lex->result= save_result;
return wrapper_sl;
err:
if (arena)
thd->restore_active_arena(arena, &backup);
lex->result= save_result;
lex->derived_tables= save_derived_tables;
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 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->limit_params= tvc_sl->limit_params;
wrapper_sl->braces= tvc_sl->braces;
tvc_sl->order_list.empty();
tvc_sl->limit_params.clear();
tvc_sl->braces= 0 ;
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;
}
wrapper_sl->distinct= tvc_sl->distinct;
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 wrapping select if successful
0 otherwise
*/
st_select_lex *
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= parent_select;
return wrapper_sl;
}
/*
@ brief
Check whether the items are of comparable type or not
@ details
This check are done because materialization is not performed
if the left expr and right expr are of the same types .
@ see subquery_types_allow_materialization ( )
@ retval
0 comparable
1 not comparable
*/
static bool cmp_row_types(Item* item1, Item* item2)
{
uint n= item1->cols();
if (item2->check_cols(n))
return true ;
for (uint i=0 ; i < n; i++)
{
Item *inner= item1->element_index(i);
Item *outer= item2->element_index(i);
if (!inner->type_handler()->subquery_type_allows_materialization(inner,
outer,
true ))
return true ;
}
return false ;
}
/**
@ brief
Transform IN predicate into IN subquery
@ param thd The context of the statement
@ param arg Not used
@ details
The method transforms this IN predicate into in equivalent IN subquery :
< left_expr > IN ( < value_list > )
= >
< left_expr > IN ( SELECT * FROM ( VALUES < transformed_value_list > ) AS tvc_ # )
E . g . :
< value_list > = 5 , 2 , 7
< transformed_value_list > = ( 5 ) , ( 2 ) , ( 7 )
< value_list > = ( 5 , 2 ) , ( 7 , 1 )
< transformed_value_list > = ( 5 , 2 ) , ( 7 , 1 )
If the transformation succeeds the method returns the result IN subquery ,
otherwise this IN predicate is returned .
@ retval
pointer to the result of transformation if succeeded
pointer to this IN predicate otherwise
*/
Item *Item_func_in::in_predicate_to_in_subs_transformer(THD *thd,
uchar *arg)
{
if (!transform_into_subq)
return this ;
Json_writer_object trace_wrapper(thd);
Json_writer_object trace_conv(thd, "in_to_subquery_conversion" );
trace_conv.add("item" , this );
List<List_item> values;
LEX *lex= thd->lex;
/* SELECT_LEX object where the transformation is performed */
SELECT_LEX *parent_select= lex->current_select;
uint8 save_derived_tables= lex->derived_tables;
/*
Make sure that create_tmp_table will not fail due to too long keys .
Here the strategy would mainly use materialization , so we need to make
sure that the materialized table can be created .
The checks here are the same as in subquery_type_allows_materialization ( )
*/
uint32 length= max_length_of_left_expr();
if (!length || length > tmp_table_max_key_length() ||
args[0 ]->cols() > tmp_table_max_key_parts())
{
if (unlikely(trace_conv.trace_started()))
trace_conv.
add("done" , false ).
add("reason" , "key is too long" );
return this ;
}
for (uint i=1 ; i < arg_count; i++)
{
if (!args[i]->const_item())
{
if (unlikely(trace_conv.trace_started()))
trace_conv.
add("done" , false ).
add("reason" , "non-constant element in the IN-list" );
return this ;
}
if (cmp_row_types(args[i], args[0 ]))
{
if (unlikely(trace_conv.trace_started()))
trace_conv.
add("done" , false ).
add("reason" , "type mismatch" );
return this ;
}
}
Json_writer_array trace_nested_obj(thd, "conversion" );
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
*/
if (mysql_new_select(lex, 1 , NULL))
goto err;
lex->init_select();
/* 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->parsing_place= SELECT_LIST;
item= new (thd->mem_root) Item_field(thd, &sq_select->context,
star_clex_str);
if (item == NULL || add_item_to_list(thd, item))
goto err;
(sq_select->with_wild)++;
/*
Create derived table DT that will wrap TVC in the result of transformation
*/
SELECT_LEX *tvc_select; // select for tvc
SELECT_LEX_UNIT *derived_unit; // unit for tvc_select
if (mysql_new_select(lex, 1 , NULL))
goto err;
lex->init_select();
tvc_select= lex->current_select;
derived_unit= tvc_select->master_unit();
derived_unit->distinct= 1 ;
tvc_select->set_linkage(DERIVED_TABLE_TYPE);
tvc_select->distinct= 1 ;
/* Create TVC used in the transformation */
if (create_value_list_for_tvc(thd, &values))
goto err;
if (!(tvc_select->tvc=
new (thd->mem_root)
table_value_constr(values,
tvc_select,
tvc_select->options)))
goto err;
lex->current_select= sq_select;
/*
Create the name of the wrapping derived table and
add it to the FROM list of the subquery SQ
*/
Table_ident *ti;
LEX_CSTRING alias;
TABLE_LIST *derived_tab;
if (!(ti= new (thd->mem_root) Table_ident(derived_unit)) ||
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)))
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 |
DTYPE_IN_PREDICATE);
lex->derived_tables|= DERIVED_SUBQUERY;
sq_select->where= 0 ;
sq_select->set_braces(false );
derived_unit->set_with_clause(0 );
/* Create IN subquery predicate */
sq_select->parsing_place= parent_select->parsing_place;
Item_in_subselect *in_subs;
Item *sq;
if (!(in_subs=
new (thd->mem_root) Item_in_subselect(thd, args[0 ], sq_select)))
goto err;
in_subs->converted_from_in_predicate= TRUE ;
sq= in_subs;
if (negated)
sq= negate_expression(thd, in_subs);
else
in_subs->emb_on_expr_nest= emb_on_expr_nest;
if (arena)
thd->restore_active_arena(arena, &backup);
thd->lex->current_select= parent_select;
if (sq->fix_fields(thd, (Item **)&sq))
goto err;
parent_select->curr_tvc_name++;
return sq;
err:
if (arena)
thd->restore_active_arena(arena, &backup);
lex->derived_tables= save_derived_tables;
thd->lex->current_select= parent_select;
return NULL;
}
uint32 Item_func_in::max_length_of_left_expr()
{
uint n= args[0 ]->cols();
uint32 length= 0 ;
for (uint i=0 ; i < n; i++)
length+= args[0 ]->element_index(i)->max_length;
return length;
}
/**
@ brief
Check if this IN - predicate can be transformed in IN - subquery
with TVC
@ param thd The context of the statement
@ details
Compare the number of elements in the list of
values in this IN - predicate with the
in_subquery_conversion_threshold special variable
@ retval
true if transformation can be made
false otherwise
*/
bool Item_func_in::to_be_transformed_into_in_subq(THD *thd)
{
bool is_row_list= args[1 ]->type() == Item::ROW_ITEM;
uint values_count= arg_count-1 ;
if (is_row_list)
values_count*= ((Item_row *)(args[1 ]))->cols();
if (thd->variables.in_subquery_conversion_threshold == 0 ||
thd->variables.in_subquery_conversion_threshold > values_count)
return false ;
if (!(thd->lex->context_analysis_only & CONTEXT_ANALYSIS_ONLY_PREPARE))
return true ;
/* Occurence of '?' in IN list is checked only for PREPARE <stmt> commands */
for (uint i=1 ; i < arg_count; i++)
{
if (!is_row_list)
{
if (args[i]->type() == Item::PARAM_ITEM)
return false ;
}
else
{
Item_row *row_list= (Item_row *)(args[i]);
for (uint j=0 ; j < row_list->cols(); j++)
{
if (row_list->element_index(j)->type() == Item::PARAM_ITEM)
return false ;
}
}
}
return true ;
}
/**
@ brief
Transform IN predicates into IN subqueries in WHERE and ON expressions
@ param thd The context of the statement
@ details
For each IN predicate from AND parts of the WHERE condition and / or
ON expressions of the SELECT for this join the method performs
the intransformation into an equivalent IN subquery if it ' s needed .
@ retval
false always
*/
bool JOIN::transform_in_predicates_into_in_subq(THD *thd)
{
DBUG_ENTER("JOIN::transform_in_predicates_into_in_subq" );
if (!select_lex->in_funcs.elements)
DBUG_RETURN(false );
SELECT_LEX *save_current_select= thd->lex->current_select;
enum_parsing_place save_parsing_place= select_lex->parsing_place;
thd->lex->current_select= select_lex;
if (conds)
{
select_lex->parsing_place= IN_WHERE;
conds=
conds->top_level_transform(thd,
&Item::in_predicate_to_in_subs_transformer, 0 );
if (!conds)
DBUG_RETURN(true );
select_lex->where= conds;
}
if (join_list)
{
TABLE_LIST *table;
List_iterator<TABLE_LIST> li(*join_list);
select_lex->parsing_place= IN_ON;
while ((table= li++))
{
if (table->on_expr)
{
table->on_expr=
table->on_expr->top_level_transform(thd,
&Item::in_predicate_to_in_subs_transformer, 0 );
if (!table->on_expr)
DBUG_RETURN(true );
}
}
}
select_lex->in_funcs.empty();
select_lex->parsing_place= save_parsing_place;
thd->lex->current_select= save_current_select;
DBUG_RETURN(false );
}
Messung V0.5 in Prozent C=97 H=74 G=86
¤ Dauer der Verarbeitung: 0.17 Sekunden
(vorverarbeitet am 2026-10-08)
¤
*© Formatika GbR, Deutschland
2026-10-09
Neuigkeiten
Aktuelles
Motto des Tages
Open Source Software
Jenseits des Üblichen ....
Besucherstatistik
Statistik
#Sources=1126438
#Domains=1867298