2022-03-29 11:59:18 +02:00
|
|
|
/* Copyright (c) 2017, 2022, MariaDB
|
2017-11-02 10:06:02 +01:00
|
|
|
|
|
|
|
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"
|
2017-06-29 14:32:17 +02:00
|
|
|
#include "sql_list.h"
|
|
|
|
#include "sql_tvc.h"
|
|
|
|
#include "sql_class.h"
|
2017-08-29 02:32:39 +02:00
|
|
|
#include "opt_range.h"
|
|
|
|
#include "sql_select.h"
|
|
|
|
#include "sql_explain.h"
|
|
|
|
#include "sql_parse.h"
|
2017-11-02 10:06:02 +01:00
|
|
|
#include "sql_cte.h"
|
2020-09-19 23:07:37 +02:00
|
|
|
#include "my_json_writer.h"
|
2017-09-04 22:29:58 +02:00
|
|
|
|
MDEV-14347 CREATE PROCEDURE returns no error when using an unknown variable
CREATE PROCEDURE did not detect unknown SP variables in assignments like this:
SET var=a_long_var_name_with_a_typo;
The error happened only during the SP execution time, and only of the control
flow reaches the erroneous statement.
Fixing most expressions to detect unknown identifiers.
This includes simple subqueries without tables:
- Query specification: SELECT list, WHERE,
HAVING (inside aggregate functions) clauses, e.g.
SET var= (SELECT unknown_ident+1);
SET var= (SELECT 1 WHERE unknown_identifier);
SET var= (SELECT 1 HAVING SUM(unknown_identifier);
- Table value constructor: VALUES clause, e.g.:
SET var= (VALUES(unknown_ident));
Note, in some more complex subquery cases unknown variables are still not detected
(this will be fixed separately):
- Derived tables:
SET a=(SELECT unknown_ident FROM (SELECT 1 AS alias) t1);
SET res=(SELECT * FROM t1 LEFT OUTER JOIN (SELECT unknown_ident) t2 USING (c1));
- CTE:
SET a=(WITH cte1 (a) AS (SELECT unknown_ident) SELECT * FROM cte1);
SET a=(WITH cte1 (a,b) AS (VALUES (unknown,2),(3,4)) SELECT * FROM cte1);
SET a=(WITH cte1 (a,b) AS (VALUES (1,2),(3,4)) SELECT unknown_ident FROM cte1);
- SELECT .. GROUP BY unknown_identifier
- SELECT .. ORDER BY unknown_identifier
- HAVING with an unknown identifier outside of any aggregate functions:
SELECT .. HAVING unknown_identifier;
2020-06-09 21:03:08 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
@brief
|
|
|
|
Walk through all VALUES items.
|
|
|
|
@param
|
|
|
|
@param processor - the processor to call for each Item
|
|
|
|
@param walk_qubquery - 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, false, argument))
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-09-04 22:29:58 +02:00
|
|
|
/**
|
|
|
|
@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++))
|
|
|
|
{
|
2021-01-26 00:21:52 +01:00
|
|
|
List_iterator<Item> it(*lst);
|
2017-09-04 22:29:58 +02:00
|
|
|
Item *item;
|
|
|
|
|
|
|
|
while ((item= it++))
|
|
|
|
{
|
2020-05-15 04:15:10 +02:00
|
|
|
/*
|
|
|
|
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.
|
|
|
|
*/
|
2021-01-26 00:21:52 +01:00
|
|
|
if (item->fix_fields_if_needed_for_scalar(thd, it.ref()) ||
|
2020-05-18 09:29:43 +02:00
|
|
|
item->check_is_evaluable_expression_or_error())
|
2017-09-04 22:29:58 +02:00
|
|
|
DBUG_RETURN(true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
DBUG_RETURN(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-06-29 14:32:17 +02:00
|
|
|
/**
|
2017-08-29 02:32:39 +02:00
|
|
|
@brief
|
|
|
|
Defines types of matrix columns elements where matrix rows are defined by
|
|
|
|
some lists of values.
|
|
|
|
|
|
|
|
@param
|
2017-11-02 05:42:26 +01:00
|
|
|
@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.
|
2017-08-29 02:32:39 +02:00
|
|
|
|
|
|
|
@details
|
2017-11-02 05:42:26 +01:00
|
|
|
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 whe
|
|
|
|
n aggregation can't be made.
|
2017-08-29 02:32:39 +02:00
|
|
|
|
|
|
|
@retval
|
|
|
|
true if an error was reported
|
|
|
|
false otherwise
|
2017-06-29 14:32:17 +02:00
|
|
|
*/
|
|
|
|
|
2017-09-04 22:29:58 +02:00
|
|
|
bool join_type_handlers_for_tvc(THD *thd, List_iterator_fast<List_item> &li,
|
2017-08-29 02:32:39 +02:00
|
|
|
Type_holder *holders, uint first_list_el_count)
|
2017-06-29 14:32:17 +02:00
|
|
|
{
|
2017-08-29 02:32:39 +02:00
|
|
|
DBUG_ENTER("join_type_handlers_for_tvc");
|
2017-06-29 14:32:17 +02:00
|
|
|
List_item *lst;
|
|
|
|
li.rewind();
|
|
|
|
bool first= true;
|
|
|
|
|
2017-09-04 22:29:58 +02:00
|
|
|
while ((lst= li++))
|
2017-06-29 14:32:17 +02:00
|
|
|
{
|
|
|
|
List_iterator_fast<Item> it(*lst);
|
|
|
|
Item *item;
|
|
|
|
|
2017-08-29 02:32:39 +02:00
|
|
|
if (first_list_el_count != lst->elements)
|
2017-06-29 14:32:17 +02:00
|
|
|
{
|
2017-08-29 16:58:32 +02:00
|
|
|
my_message(ER_WRONG_NUMBER_OF_VALUES_IN_TVC,
|
2017-09-04 22:29:58 +02:00
|
|
|
ER_THD(thd, ER_WRONG_NUMBER_OF_VALUES_IN_TVC),
|
2017-08-29 02:32:39 +02:00
|
|
|
MYF(0));
|
|
|
|
DBUG_RETURN(true);
|
2017-06-29 14:32:17 +02:00
|
|
|
}
|
|
|
|
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))
|
|
|
|
{
|
2017-08-29 02:32:39 +02:00
|
|
|
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);
|
2017-06-29 14:32:17 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
first= false;
|
|
|
|
}
|
2017-08-29 02:32:39 +02:00
|
|
|
DBUG_RETURN(false);
|
2017-06-29 14:32:17 +02:00
|
|
|
}
|
|
|
|
|
2017-08-29 02:32:39 +02:00
|
|
|
|
2017-06-29 14:32:17 +02:00
|
|
|
/**
|
2017-08-29 02:32:39 +02:00
|
|
|
@brief
|
2017-11-02 05:42:26 +01:00
|
|
|
Define attributes of matrix columns elements where matrix rows are defined
|
|
|
|
by some lists of values.
|
2017-08-29 02:32:39 +02:00
|
|
|
|
|
|
|
@param
|
2017-11-02 05:42:26 +01:00
|
|
|
@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.
|
2017-08-29 02:32:39 +02:00
|
|
|
|
|
|
|
@details
|
2017-11-02 05:42:26 +01:00
|
|
|
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.
|
2017-08-29 02:32:39 +02:00
|
|
|
The errors can be reported when aggregation can't be made.
|
|
|
|
|
|
|
|
@retval
|
|
|
|
true if an error was reported
|
|
|
|
false otherwise
|
2017-06-29 14:32:17 +02:00
|
|
|
*/
|
|
|
|
|
2017-09-04 22:29:58 +02:00
|
|
|
bool get_type_attributes_for_tvc(THD *thd,
|
2017-06-29 14:32:17 +02:00
|
|
|
List_iterator_fast<List_item> &li,
|
2017-08-29 02:32:39 +02:00
|
|
|
Type_holder *holders, uint count_of_lists,
|
|
|
|
uint first_list_el_count)
|
2017-06-29 14:32:17 +02:00
|
|
|
{
|
2017-08-29 02:32:39 +02:00
|
|
|
DBUG_ENTER("get_type_attributes_for_tvc");
|
2017-06-29 14:32:17 +02:00
|
|
|
List_item *lst;
|
|
|
|
li.rewind();
|
|
|
|
|
|
|
|
for (uint pos= 0; pos < first_list_el_count; pos++)
|
|
|
|
{
|
2017-09-04 22:29:58 +02:00
|
|
|
if (holders[pos].alloc_arguments(thd, count_of_lists))
|
2017-08-29 02:32:39 +02:00
|
|
|
DBUG_RETURN(true);
|
2017-06-29 14:32:17 +02:00
|
|
|
}
|
|
|
|
|
2017-09-04 22:29:58 +02:00
|
|
|
while ((lst= li++))
|
2017-06-29 14:32:17 +02:00
|
|
|
{
|
2017-08-29 02:32:39 +02:00
|
|
|
List_iterator_fast<Item> it(*lst);
|
|
|
|
Item *item;
|
|
|
|
for (uint holder_pos= 0 ; (item= it++); holder_pos++)
|
|
|
|
{
|
2020-08-14 18:51:10 +02:00
|
|
|
DBUG_ASSERT(item->fixed());
|
2017-08-29 02:32:39 +02:00
|
|
|
holders[holder_pos].add_argument(item);
|
|
|
|
}
|
2017-06-29 14:32:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
for (uint pos= 0; pos < first_list_el_count; pos++)
|
|
|
|
{
|
2017-09-04 22:29:58 +02:00
|
|
|
if (holders[pos].aggregate_attributes(thd))
|
2017-08-29 02:32:39 +02:00
|
|
|
DBUG_RETURN(true);
|
2017-06-29 14:32:17 +02:00
|
|
|
}
|
2017-08-29 02:32:39 +02:00
|
|
|
DBUG_RETURN(false);
|
2017-06-29 14:32:17 +02:00
|
|
|
}
|
|
|
|
|
2017-08-29 02:32:39 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
@brief
|
|
|
|
Prepare of TVC
|
|
|
|
|
|
|
|
@param
|
2017-09-04 22:29:58 +02:00
|
|
|
@param thd The context of the statement
|
2017-08-29 02:32:39 +02:00
|
|
|
@param sl The select where this TVC is defined
|
|
|
|
@param tmp_result Structure that contains the information
|
2017-09-04 22:29:58 +02:00
|
|
|
about where to send the result of the query
|
2017-08-29 02:32:39 +02:00
|
|
|
@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
|
|
|
|
*/
|
|
|
|
|
2017-09-04 22:29:58 +02:00
|
|
|
bool table_value_constr::prepare(THD *thd, SELECT_LEX *sl,
|
2017-08-29 02:32:39 +02:00
|
|
|
select_result *tmp_result,
|
|
|
|
st_select_lex_unit *unit_arg)
|
2017-06-29 14:32:17 +02:00
|
|
|
{
|
2017-08-29 02:32:39 +02:00
|
|
|
DBUG_ENTER("table_value_constr::prepare");
|
2017-10-28 20:54:18 +02:00
|
|
|
select_lex->in_tvc= true;
|
2017-06-29 14:32:17 +02:00
|
|
|
List_iterator_fast<List_item> li(lists_of_values);
|
|
|
|
|
|
|
|
List_item *first_elem= li++;
|
|
|
|
uint cnt= first_elem->elements;
|
2023-09-24 04:02:52 +02:00
|
|
|
Type_holder *holders= type_holders;
|
2017-06-29 14:32:17 +02:00
|
|
|
|
2018-04-27 01:38:56 +02:00
|
|
|
if (cnt == 0)
|
|
|
|
{
|
|
|
|
my_error(ER_EMPTY_ROW_IN_TVC, MYF(0));
|
|
|
|
DBUG_RETURN(true);
|
|
|
|
}
|
|
|
|
|
2017-09-04 22:29:58 +02:00
|
|
|
if (fix_fields_for_tvc(thd, li))
|
|
|
|
DBUG_RETURN(true);
|
|
|
|
|
2023-09-24 04:02:52 +02:00
|
|
|
if (!holders)
|
2017-06-29 14:32:17 +02:00
|
|
|
{
|
2023-11-24 10:28:31 +01:00
|
|
|
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];
|
2023-09-24 04:02:52 +02:00
|
|
|
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
|
2017-06-29 14:32:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
result= tmp_result;
|
|
|
|
|
2017-08-29 02:32:39 +02:00
|
|
|
if (result && result->prepare(sl->item_list, unit_arg))
|
|
|
|
DBUG_RETURN(true);
|
|
|
|
|
2019-05-08 09:08:09 +02:00
|
|
|
/*
|
|
|
|
setup_order() for a TVC is not called when the following is true
|
|
|
|
(thd->lex->context_analysis_only & CONTEXT_ANALYSIS_ONLY_VIEW)
|
|
|
|
*/
|
|
|
|
|
2024-04-26 18:13:31 +02:00
|
|
|
thd->where= THD_WHERE::ORDER_CLAUSE;
|
2019-05-08 09:08:09 +02:00
|
|
|
ORDER *order= sl->order_list.first;
|
|
|
|
for (; order; order=order->next)
|
|
|
|
{
|
|
|
|
Item *order_item= *order->item;
|
2019-05-19 20:55:37 +02:00
|
|
|
if (order_item->is_order_clause_position())
|
2019-05-08 09:08:09 +02:00
|
|
|
{
|
|
|
|
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),
|
2024-04-26 18:13:31 +02:00
|
|
|
order_item->full_name(), thd_where(thd));
|
2019-05-08 09:08:09 +02:00
|
|
|
DBUG_RETURN(true);
|
|
|
|
}
|
|
|
|
order->in_field_list= 1;
|
|
|
|
order->counter= count;
|
|
|
|
order->counter_used= 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-10-28 20:54:18 +02:00
|
|
|
select_lex->in_tvc= false;
|
2017-08-29 02:32:39 +02:00
|
|
|
DBUG_RETURN(false);
|
2017-06-29 14:32:17 +02:00
|
|
|
}
|
|
|
|
|
2017-08-29 02:32:39 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
Save Query Plan Footprint
|
|
|
|
*/
|
|
|
|
|
2017-09-04 22:29:58 +02:00
|
|
|
int table_value_constr::save_explain_data_intern(THD *thd,
|
2017-08-29 02:32:39 +02:00
|
|
|
Explain_query *output)
|
2017-06-29 14:32:17 +02:00
|
|
|
{
|
2017-08-29 02:32:39 +02:00
|
|
|
const char *message= "No tables used";
|
|
|
|
DBUG_ENTER("table_value_constr::save_explain_data_intern");
|
2017-11-02 10:31:28 +01:00
|
|
|
DBUG_PRINT("info", ("Select %p, type %s, message %s",
|
|
|
|
select_lex, select_lex->type,
|
2017-08-29 02:32:39 +02:00
|
|
|
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,
|
2017-09-04 22:29:58 +02:00
|
|
|
thd->lex->analyze_stmt);
|
2017-11-14 06:47:58 +01:00
|
|
|
if (!explain)
|
|
|
|
DBUG_RETURN(1);
|
|
|
|
|
2017-08-29 02:32:39 +02:00
|
|
|
select_lex->set_explain_type(true);
|
|
|
|
|
|
|
|
explain->select_id= select_lex->select_number;
|
|
|
|
explain->select_type= select_lex->type;
|
2018-05-22 19:08:39 +02:00
|
|
|
explain->linkage= select_lex->get_linkage();
|
2018-01-25 18:48:52 +01:00
|
|
|
explain->using_temporary= false;
|
|
|
|
explain->using_filesort= false;
|
2017-08-29 02:32:39 +02:00
|
|
|
/* 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;
|
|
|
|
|
2021-02-22 07:01:24 +01:00
|
|
|
for (SELECT_LEX_UNIT *unit= select_lex->first_inner_unit();
|
|
|
|
unit;
|
|
|
|
unit= unit->next_unit())
|
|
|
|
{
|
|
|
|
explain->add_child(unit->first_select()->select_number);
|
|
|
|
}
|
|
|
|
|
2017-08-29 02:32:39 +02:00
|
|
|
output->add_node(explain);
|
|
|
|
|
|
|
|
if (select_lex->is_top_level_node())
|
|
|
|
output->query_plan_ready();
|
|
|
|
|
|
|
|
DBUG_RETURN(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
Optimization of TVC
|
|
|
|
*/
|
|
|
|
|
2017-11-14 06:47:58 +01:00
|
|
|
bool table_value_constr::optimize(THD *thd)
|
2017-08-29 02:32:39 +02:00
|
|
|
{
|
2017-09-04 22:29:58 +02:00
|
|
|
create_explain_query_if_not_exists(thd->lex, thd->mem_root);
|
2017-08-29 02:32:39 +02:00
|
|
|
have_query_plan= QEP_AVAILABLE;
|
|
|
|
|
2021-04-14 23:19:13 +02:00
|
|
|
if (select_lex->select_number != FAKE_SELECT_LEX_ID &&
|
2017-08-29 02:32:39 +02:00
|
|
|
have_query_plan != QEP_NOT_PRESENT_YET &&
|
2017-09-04 22:29:58 +02:00
|
|
|
thd->lex->explain && // for "SET" command in SPs.
|
|
|
|
(!thd->lex->explain->get_select(select_lex->select_number)))
|
2017-08-29 02:32:39 +02:00
|
|
|
{
|
2021-02-22 07:01:24 +01:00
|
|
|
if (save_explain_data_intern(thd, thd->lex->explain))
|
|
|
|
return true;
|
2017-08-29 02:32:39 +02:00
|
|
|
}
|
2021-02-22 07:01:24 +01:00
|
|
|
|
|
|
|
if (select_lex->optimize_unflattened_subqueries(true))
|
|
|
|
return true;
|
|
|
|
|
|
|
|
return false;
|
2017-08-29 02:32:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
Execute of TVC
|
|
|
|
*/
|
|
|
|
|
|
|
|
bool table_value_constr::exec(SELECT_LEX *sl)
|
|
|
|
{
|
|
|
|
DBUG_ENTER("table_value_constr::exec");
|
2017-06-29 14:32:17 +02:00
|
|
|
List_iterator_fast<List_item> li(lists_of_values);
|
|
|
|
List_item *elem;
|
2022-01-23 09:12:41 +01:00
|
|
|
THD *cur_thd= sl->parent_lex->thd;
|
2019-05-08 09:08:09 +02:00
|
|
|
ha_rows send_records= 0;
|
2022-01-23 09:12:41 +01:00
|
|
|
int rc=0;
|
2017-06-29 14:32:17 +02:00
|
|
|
|
2017-08-29 02:32:39 +02:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2021-01-04 16:03:16 +01:00
|
|
|
fix_rownum_pointers(sl->parent_lex->thd, sl, &send_records);
|
|
|
|
|
2017-09-04 22:29:58 +02:00
|
|
|
while ((elem= li++))
|
2017-06-29 14:32:17 +02:00
|
|
|
{
|
2022-01-23 09:12:41 +01:00
|
|
|
cur_thd->get_stmt_da()->inc_current_row_for_warning();
|
2019-09-26 09:49:50 +02:00
|
|
|
if (send_records >= sl->master_unit()->lim.get_select_limit())
|
2019-05-08 09:08:09 +02:00
|
|
|
break;
|
2022-01-23 09:12:41 +01:00
|
|
|
rc= result->send_data_with_check(*elem, sl->master_unit(), send_records);
|
2019-05-08 09:08:09 +02:00
|
|
|
if (!rc)
|
|
|
|
send_records++;
|
|
|
|
else if (rc > 0)
|
|
|
|
DBUG_RETURN(true);
|
2017-06-29 14:32:17 +02:00
|
|
|
}
|
2017-08-29 02:32:39 +02:00
|
|
|
|
|
|
|
if (result->send_eof())
|
|
|
|
DBUG_RETURN(true);
|
|
|
|
|
|
|
|
DBUG_RETURN(false);
|
|
|
|
}
|
|
|
|
|
2017-09-04 22:29:58 +02:00
|
|
|
|
2017-08-29 02:32:39 +02:00
|
|
|
/**
|
|
|
|
@brief
|
2017-09-04 22:29:58 +02:00
|
|
|
Print list
|
2017-08-29 02:32:39 +02:00
|
|
|
|
2017-09-04 22:29:58 +02:00
|
|
|
@param str The reference on the string representation of the list
|
|
|
|
@param list The list that needed to be print
|
2017-08-29 02:32:39 +02:00
|
|
|
@param query_type The mode of printing
|
|
|
|
|
|
|
|
@details
|
2017-09-04 22:29:58 +02:00
|
|
|
The method saves a string representation of list in the
|
|
|
|
string str.
|
2017-08-29 02:32:39 +02:00
|
|
|
*/
|
|
|
|
|
2017-09-04 22:29:58 +02:00
|
|
|
void print_list_item(String *str, List_item *list,
|
|
|
|
enum_query_type query_type)
|
2017-08-29 02:32:39 +02:00
|
|
|
{
|
2017-09-04 22:29:58 +02:00
|
|
|
bool is_first_elem= true;
|
|
|
|
List_iterator_fast<Item> it(*list);
|
|
|
|
Item *item;
|
2017-08-29 02:32:39 +02:00
|
|
|
|
2017-09-04 22:29:58 +02:00
|
|
|
str->append('(');
|
|
|
|
|
|
|
|
while ((item= it++))
|
2017-08-29 02:32:39 +02:00
|
|
|
{
|
2017-09-04 22:29:58 +02:00
|
|
|
if (is_first_elem)
|
|
|
|
is_first_elem= false;
|
2017-08-29 02:32:39 +02:00
|
|
|
else
|
|
|
|
str->append(',');
|
|
|
|
|
2017-09-04 22:29:58 +02:00
|
|
|
item->print(str, query_type);
|
2017-08-29 02:32:39 +02:00
|
|
|
}
|
2017-09-04 22:29:58 +02:00
|
|
|
|
|
|
|
str->append(')');
|
2017-08-29 02:32:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
@brief
|
|
|
|
Print this TVC
|
|
|
|
|
2017-09-04 22:29:58 +02:00
|
|
|
@param thd The context of the statement
|
|
|
|
@param str The reference on the string representation of this TVC
|
2017-08-29 02:32:39 +02:00
|
|
|
@param query_type The mode of printing
|
|
|
|
|
|
|
|
@details
|
2017-09-04 22:29:58 +02:00
|
|
|
The method saves a string representation of this TVC in the
|
|
|
|
string str.
|
2017-08-29 02:32:39 +02:00
|
|
|
*/
|
|
|
|
|
2017-09-04 22:29:58 +02:00
|
|
|
void table_value_constr::print(THD *thd, String *str,
|
2017-08-29 02:32:39 +02:00
|
|
|
enum_query_type query_type)
|
|
|
|
{
|
2017-09-04 22:29:58 +02:00
|
|
|
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(',');
|
2017-08-29 02:32:39 +02:00
|
|
|
|
2017-09-04 22:29:58 +02:00
|
|
|
print_list_item(str, list, query_type);
|
|
|
|
}
|
2019-05-08 09:08:09 +02:00
|
|
|
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);
|
2017-08-29 02:32:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
@brief
|
2017-09-02 23:19:20 +02:00
|
|
|
Create list of lists for TVC from the list of this IN predicate
|
2017-08-29 02:32:39 +02:00
|
|
|
|
2017-09-02 23:19:20 +02:00
|
|
|
@param thd The context of the statement
|
|
|
|
@param values TVC list of values
|
2017-08-29 02:32:39 +02:00
|
|
|
|
|
|
|
@details
|
2017-09-02 23:19:20 +02:00
|
|
|
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.:
|
2017-08-29 02:32:39 +02:00
|
|
|
|
2017-09-02 23:19:20 +02:00
|
|
|
<value_list> = 5,2,7
|
|
|
|
<transformed_value_list> = (5),(2),(7)
|
2017-08-29 02:32:39 +02:00
|
|
|
|
2017-09-02 23:19:20 +02:00
|
|
|
<value_list> = (5,2),(7,1)
|
|
|
|
<transformed_value_list> = (5,2),(7,1)
|
2017-08-29 02:32:39 +02:00
|
|
|
|
|
|
|
@retval
|
2017-09-02 23:19:20 +02:00
|
|
|
false if the method succeeds
|
|
|
|
true otherwise
|
2017-08-29 02:32:39 +02:00
|
|
|
*/
|
|
|
|
|
2017-09-02 23:19:20 +02:00
|
|
|
bool Item_func_in::create_value_list_for_tvc(THD *thd,
|
|
|
|
List< List<Item> > *values)
|
2017-08-29 02:32:39 +02:00
|
|
|
{
|
2017-09-02 23:19:20 +02:00
|
|
|
bool is_list_of_rows= args[1]->type() == Item::ROW_ITEM;
|
2017-09-01 19:01:06 +02:00
|
|
|
|
2017-09-02 23:19:20 +02:00
|
|
|
for (uint i=1; i < arg_count; i++)
|
|
|
|
{
|
2018-10-15 00:20:25 +02:00
|
|
|
char col_name[8];
|
2017-09-02 23:19:20 +02:00
|
|
|
List<Item> *tvc_value;
|
|
|
|
if (!(tvc_value= new (thd->mem_root) List<Item>()))
|
|
|
|
return true;
|
|
|
|
|
|
|
|
if (is_list_of_rows)
|
|
|
|
{
|
2022-12-09 15:10:25 +01:00
|
|
|
Item_row *row_list= (Item_row *)(args[i]);
|
2021-10-20 14:24:31 +02:00
|
|
|
|
|
|
|
if (!row_list)
|
|
|
|
return true;
|
2017-09-02 23:19:20 +02:00
|
|
|
|
|
|
|
for (uint j=0; j < row_list->cols(); j++)
|
|
|
|
{
|
2018-10-15 00:20:25 +02:00
|
|
|
if (i == 1)
|
|
|
|
{
|
|
|
|
sprintf(col_name, "_col_%i", j+1);
|
|
|
|
row_list->element_index(j)->set_name(thd, col_name, strlen(col_name),
|
|
|
|
thd->charset());
|
|
|
|
}
|
2017-09-02 23:19:20 +02:00
|
|
|
if (tvc_value->push_back(row_list->element_index(j),
|
|
|
|
thd->mem_root))
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
2018-10-15 00:20:25 +02:00
|
|
|
else
|
|
|
|
{
|
|
|
|
if (i == 1)
|
|
|
|
{
|
|
|
|
sprintf(col_name, "_col_%i", 1);
|
|
|
|
args[i]->set_name(thd, col_name, strlen(col_name), thd->charset());
|
|
|
|
}
|
2022-12-09 15:10:25 +01:00
|
|
|
if (tvc_value->push_back(args[i]))
|
2018-10-15 00:20:25 +02:00
|
|
|
return true;
|
|
|
|
}
|
2017-09-02 23:19:20 +02:00
|
|
|
|
|
|
|
if (values->push_back(tvc_value, thd->mem_root))
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
2017-08-29 02:32:39 +02:00
|
|
|
|
|
|
|
|
2017-09-04 22:29:58 +02:00
|
|
|
/**
|
|
|
|
@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
|
|
|
|
*/
|
|
|
|
|
2017-09-02 23:19:20 +02:00
|
|
|
static bool create_tvc_name(THD *thd, st_select_lex *parent_select,
|
|
|
|
LEX_CSTRING *alias)
|
|
|
|
{
|
2017-08-29 02:32:39 +02:00
|
|
|
char buff[6];
|
2017-09-01 19:01:06 +02:00
|
|
|
|
2017-09-02 23:19:20 +02:00
|
|
|
alias->length= my_snprintf(buff, sizeof(buff),
|
2019-05-08 09:08:09 +02:00
|
|
|
"tvc_%u",
|
|
|
|
parent_select ? parent_select->curr_tvc_name : 0);
|
2017-09-02 23:19:20 +02:00
|
|
|
alias->str= thd->strmake(buff, alias->length);
|
|
|
|
if (!alias->str)
|
|
|
|
return true;
|
2017-09-01 19:01:06 +02:00
|
|
|
|
2017-09-02 23:19:20 +02:00
|
|
|
return false;
|
|
|
|
}
|
2017-09-01 19:01:06 +02:00
|
|
|
|
2017-09-04 22:29:58 +02:00
|
|
|
|
2019-05-08 09:08:09 +02:00
|
|
|
/**
|
|
|
|
@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()
|
|
|
|
{
|
2019-09-20 18:03:38 +02:00
|
|
|
return select_lex->master_unit()->first_select()->next_select() &&
|
2020-12-19 12:59:37 +01:00
|
|
|
select_lex->order_list.elements &&
|
|
|
|
select_lex->limit_params.explicit_limit;
|
2019-05-08 09:08:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
@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)
|
2017-10-28 20:54:18 +02:00
|
|
|
{
|
|
|
|
LEX *lex= thd->lex;
|
2021-03-01 18:40:33 +01:00
|
|
|
select_result *save_result= lex->result;
|
2017-10-28 20:54:18 +02:00
|
|
|
uint8 save_derived_tables= lex->derived_tables;
|
2019-05-08 09:08:09 +02:00
|
|
|
thd->lex->result= NULL;
|
2017-10-28 20:54:18 +02:00
|
|
|
|
|
|
|
Query_arena backup;
|
|
|
|
Query_arena *arena= thd->activate_stmt_arena_if_needed(&backup);
|
2021-02-24 22:51:47 +01:00
|
|
|
|
|
|
|
Item *item;
|
|
|
|
SELECT_LEX *wrapper_sl;
|
|
|
|
SELECT_LEX_UNIT *derived_unit;
|
|
|
|
|
2017-10-28 20:54:18 +02:00
|
|
|
/*
|
2021-02-24 22:51:47 +01:00
|
|
|
Create SELECT_LEX wrapper_sl of the select used in the result
|
|
|
|
of the transformation
|
2017-10-28 20:54:18 +02:00
|
|
|
*/
|
2021-02-24 22:51:47 +01:00
|
|
|
if (!(wrapper_sl= new (thd->mem_root) SELECT_LEX()))
|
2017-10-28 20:54:18 +02:00
|
|
|
goto err;
|
2021-02-24 22:51:47 +01:00
|
|
|
wrapper_sl->select_number= ++thd->lex->stmt_lex->current_select_number;
|
|
|
|
wrapper_sl->parent_lex= lex; /* Used in init_query. */
|
2022-05-04 16:26:43 +02:00
|
|
|
wrapper_sl->make_empty_select();
|
2023-02-27 19:51:22 +01:00
|
|
|
wrapper_sl->is_tvc_wrapper= true;
|
2021-02-24 22:51:47 +01:00
|
|
|
|
|
|
|
wrapper_sl->nest_level= tvc_sl->nest_level;
|
|
|
|
wrapper_sl->parsing_place= tvc_sl->parsing_place;
|
2022-05-04 16:26:43 +02:00
|
|
|
wrapper_sl->distinct= tvc_sl->distinct;
|
2019-05-19 20:55:37 +02:00
|
|
|
wrapper_sl->set_linkage(tvc_sl->get_linkage());
|
2021-03-04 07:48:39 +01:00
|
|
|
wrapper_sl->exclude_from_table_unique_test=
|
|
|
|
tvc_sl->exclude_from_table_unique_test;
|
2021-02-24 22:51:47 +01:00
|
|
|
|
|
|
|
lex->current_select= wrapper_sl;
|
2019-05-08 09:08:09 +02:00
|
|
|
item= new (thd->mem_root) Item_field(thd, &wrapper_sl->context,
|
2019-05-26 04:17:35 +02:00
|
|
|
star_clex_str);
|
2017-10-28 20:54:18 +02:00
|
|
|
if (item == NULL || add_item_to_list(thd, item))
|
|
|
|
goto err;
|
2019-05-08 09:08:09 +02:00
|
|
|
(wrapper_sl->with_wild)++;
|
2021-02-24 22:51:47 +01:00
|
|
|
|
|
|
|
/* 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);
|
|
|
|
|
2017-10-28 20:54:18 +02:00
|
|
|
/*
|
2021-02-24 22:51:47 +01:00
|
|
|
Create a unit for the substituted select used for TVC and attach it
|
|
|
|
to the the wrapper select wrapper_sl as the only unit. The created
|
|
|
|
unit is the unit for the derived table tvc_x of the transformation.
|
2017-10-28 20:54:18 +02:00
|
|
|
*/
|
2021-02-24 22:51:47 +01:00
|
|
|
if (!(derived_unit= new (thd->mem_root) SELECT_LEX_UNIT()))
|
2017-10-28 20:54:18 +02:00
|
|
|
goto err;
|
2021-02-24 22:51:47 +01:00
|
|
|
derived_unit->init_query();
|
|
|
|
derived_unit->thd= thd;
|
|
|
|
derived_unit->include_down(wrapper_sl);
|
2022-05-04 16:26:43 +02:00
|
|
|
derived_unit->distinct= tvc_sl->distinct;
|
2017-10-28 20:54:18 +02:00
|
|
|
|
2021-02-24 22:51:47 +01:00
|
|
|
/*
|
|
|
|
Attach the select used of TVC as the only slave to the unit for
|
|
|
|
the derived table tvc_x of the transformation
|
|
|
|
*/
|
2022-07-04 15:23:01 +02:00
|
|
|
derived_unit->attach_single(tvc_sl);
|
2021-02-25 03:16:10 +01:00
|
|
|
tvc_sl->set_linkage(DERIVED_TABLE_TYPE);
|
2017-10-28 20:54:18 +02:00
|
|
|
|
|
|
|
/*
|
2021-02-24 22:51:47 +01:00
|
|
|
Generate the name of the derived table created for TVC and
|
|
|
|
add it to the FROM list of the wrapping select
|
|
|
|
*/
|
2017-10-28 20:54:18 +02:00
|
|
|
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=
|
2019-05-08 09:08:09 +02:00
|
|
|
wrapper_sl->add_table_to_list(thd,
|
|
|
|
ti, &alias, 0,
|
|
|
|
TL_READ, MDL_SHARED_READ)))
|
2017-10-28 20:54:18 +02:00
|
|
|
goto err;
|
2019-05-08 09:08:09 +02:00
|
|
|
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;
|
2017-10-28 20:54:18 +02:00
|
|
|
lex->derived_tables|= DERIVED_SUBQUERY;
|
|
|
|
|
|
|
|
if (arena)
|
|
|
|
thd->restore_active_arena(arena, &backup);
|
2021-03-01 18:40:33 +01:00
|
|
|
lex->result= save_result;
|
2019-05-08 09:08:09 +02:00
|
|
|
return wrapper_sl;
|
2017-10-28 20:54:18 +02:00
|
|
|
|
|
|
|
err:
|
|
|
|
if (arena)
|
|
|
|
thd->restore_active_arena(arena, &backup);
|
2021-03-01 18:40:33 +01:00
|
|
|
lex->result= save_result;
|
2017-10-28 20:54:18 +02:00
|
|
|
lex->derived_tables= save_derived_tables;
|
2019-05-08 09:08:09 +02:00
|
|
|
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;
|
2020-12-19 12:59:37 +01:00
|
|
|
wrapper_sl->limit_params= tvc_sl->limit_params;
|
2019-05-08 09:08:09 +02:00
|
|
|
wrapper_sl->braces= tvc_sl->braces;
|
|
|
|
tvc_sl->order_list.empty();
|
2020-12-19 12:59:37 +01:00
|
|
|
tvc_sl->limit_params.clear();
|
2019-05-08 09:08:09 +02:00
|
|
|
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;
|
|
|
|
}
|
2019-08-24 15:42:35 +02:00
|
|
|
wrapper_sl->distinct= tvc_sl->distinct;
|
2019-05-08 09:08:09 +02:00
|
|
|
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.
|
|
|
|
|
2021-02-11 08:38:52 +01:00
|
|
|
@retval wrapping select if successful
|
|
|
|
0 otherwise
|
2019-05-08 09:08:09 +02:00
|
|
|
*/
|
|
|
|
|
2021-02-11 08:38:52 +01:00
|
|
|
st_select_lex *
|
|
|
|
Item_subselect::wrap_tvc_into_select(THD *thd, st_select_lex *tvc_sl)
|
2019-05-08 09:08:09 +02:00
|
|
|
{
|
|
|
|
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);
|
|
|
|
}
|
2021-03-01 18:40:33 +01:00
|
|
|
lex->current_select= parent_select;
|
|
|
|
return wrapper_sl;
|
2017-10-28 20:54:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-12-04 15:34:45 +01:00
|
|
|
/*
|
|
|
|
@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,
|
2020-11-27 17:36:54 +01:00
|
|
|
outer,
|
|
|
|
true))
|
2019-12-04 15:34:45 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-09-02 23:19:20 +02:00
|
|
|
/**
|
|
|
|
@brief
|
|
|
|
Transform IN predicate into IN subquery
|
2017-09-01 19:01:06 +02:00
|
|
|
|
2017-09-02 23:19:20 +02:00
|
|
|
@param thd The context of the statement
|
|
|
|
@param arg Not used
|
2017-09-01 19:01:06 +02:00
|
|
|
|
2017-09-02 23:19:20 +02:00
|
|
|
@details
|
|
|
|
The method transforms this IN predicate into in equivalent IN subquery:
|
2017-09-01 19:01:06 +02:00
|
|
|
|
2017-09-02 23:19:20 +02:00
|
|
|
<left_expr> IN (<value_list>)
|
|
|
|
=>
|
|
|
|
<left_expr> IN (SELECT * FROM (VALUES <transformed_value_list>) AS tvc_#)
|
2017-09-01 19:01:06 +02:00
|
|
|
|
2017-09-02 23:19:20 +02:00
|
|
|
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;
|
2022-03-29 11:59:18 +02:00
|
|
|
|
2020-09-19 23:07:37 +02:00
|
|
|
Json_writer_object trace_wrapper(thd);
|
|
|
|
Json_writer_object trace_conv(thd, "in_to_subquery_conversion");
|
|
|
|
trace_conv.add("item", this);
|
|
|
|
|
2017-09-02 23:19:20 +02:00
|
|
|
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;
|
2019-12-04 15:34:45 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
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())
|
2020-09-19 23:07:37 +02:00
|
|
|
{
|
2022-01-20 14:49:01 +01:00
|
|
|
if (unlikely(trace_conv.trace_started()))
|
|
|
|
trace_conv.
|
|
|
|
add("done", false).
|
|
|
|
add("reason", "key is too long");
|
2019-12-04 15:34:45 +01:00
|
|
|
return this;
|
2020-09-19 23:07:37 +02:00
|
|
|
}
|
|
|
|
|
2017-10-28 20:54:18 +02:00
|
|
|
for (uint i=1; i < arg_count; i++)
|
|
|
|
{
|
2020-09-19 23:07:37 +02:00
|
|
|
if (!args[i]->const_item())
|
|
|
|
{
|
2022-01-20 14:49:01 +01:00
|
|
|
if (unlikely(trace_conv.trace_started()))
|
|
|
|
trace_conv.
|
|
|
|
add("done", false).
|
|
|
|
add("reason", "non-constant element in the IN-list");
|
2017-10-28 20:54:18 +02:00
|
|
|
return this;
|
2020-09-19 23:07:37 +02:00
|
|
|
}
|
2020-12-02 17:29:49 +01:00
|
|
|
|
|
|
|
if (cmp_row_types(args[i], args[0]))
|
2020-09-19 23:07:37 +02:00
|
|
|
{
|
2022-01-20 14:49:01 +01:00
|
|
|
if (unlikely(trace_conv.trace_started()))
|
|
|
|
trace_conv.
|
|
|
|
add("done", false).
|
|
|
|
add("reason", "type mismatch");
|
2020-09-19 23:07:37 +02:00
|
|
|
return this;
|
|
|
|
}
|
2017-10-28 20:54:18 +02:00
|
|
|
}
|
2020-09-19 23:07:37 +02:00
|
|
|
Json_writer_array trace_nested_obj(thd, "conversion");
|
2017-09-02 23:19:20 +02:00
|
|
|
|
|
|
|
Query_arena backup;
|
|
|
|
Query_arena *arena= thd->activate_stmt_arena_if_needed(&backup);
|
2017-08-29 02:32:39 +02:00
|
|
|
|
|
|
|
/*
|
2017-09-02 23:19:20 +02:00
|
|
|
Create SELECT_LEX of the subquery SQ used in the result of transformation
|
2017-08-29 02:32:39 +02:00
|
|
|
*/
|
|
|
|
if (mysql_new_select(lex, 1, NULL))
|
|
|
|
goto err;
|
2022-10-21 17:16:05 +02:00
|
|
|
lex->init_select();
|
2017-09-02 23:19:20 +02:00
|
|
|
/* 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,
|
2019-05-26 04:17:35 +02:00
|
|
|
star_clex_str);
|
2017-09-02 23:19:20 +02:00
|
|
|
if (item == NULL || add_item_to_list(thd, item))
|
2017-08-29 02:32:39 +02:00
|
|
|
goto err;
|
2017-09-02 23:19:20 +02:00
|
|
|
(sq_select->with_wild)++;
|
2017-08-29 02:32:39 +02:00
|
|
|
/*
|
2017-09-02 23:19:20 +02:00
|
|
|
Create derived table DT that will wrap TVC in the result of transformation
|
2017-08-29 02:32:39 +02:00
|
|
|
*/
|
2017-09-02 23:19:20 +02:00
|
|
|
SELECT_LEX *tvc_select; // select for tvc
|
|
|
|
SELECT_LEX_UNIT *derived_unit; // unit for tvc_select
|
2017-08-29 02:32:39 +02:00
|
|
|
if (mysql_new_select(lex, 1, NULL))
|
|
|
|
goto err;
|
2022-10-21 17:16:05 +02:00
|
|
|
lex->init_select();
|
2017-09-02 23:19:20 +02:00
|
|
|
tvc_select= lex->current_select;
|
|
|
|
derived_unit= tvc_select->master_unit();
|
2022-05-04 16:26:43 +02:00
|
|
|
derived_unit->distinct= 1;
|
2018-05-22 19:08:39 +02:00
|
|
|
tvc_select->set_linkage(DERIVED_TABLE_TYPE);
|
2022-05-04 16:26:43 +02:00
|
|
|
tvc_select->distinct= 1;
|
2017-08-29 02:32:39 +02:00
|
|
|
|
2017-09-02 23:19:20 +02:00
|
|
|
/* Create TVC used in the transformation */
|
|
|
|
if (create_value_list_for_tvc(thd, &values))
|
|
|
|
goto err;
|
|
|
|
if (!(tvc_select->tvc=
|
2017-09-01 19:01:06 +02:00
|
|
|
new (thd->mem_root)
|
|
|
|
table_value_constr(values,
|
2017-09-02 23:19:20 +02:00
|
|
|
tvc_select,
|
|
|
|
tvc_select->options)))
|
2017-08-29 02:32:39 +02:00
|
|
|
goto err;
|
|
|
|
|
2017-09-02 23:19:20 +02:00
|
|
|
lex->current_select= sq_select;
|
2017-08-29 02:32:39 +02:00
|
|
|
|
2017-09-02 23:19:20 +02:00
|
|
|
/*
|
|
|
|
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))
|
2017-08-29 02:32:39 +02:00
|
|
|
goto err;
|
2017-09-02 23:19:20 +02:00
|
|
|
if (!(derived_tab=
|
|
|
|
sq_select->add_table_to_list(thd,
|
|
|
|
ti, &alias, 0,
|
|
|
|
TL_READ, MDL_SHARED_READ)))
|
2017-08-29 02:32:39 +02:00
|
|
|
goto err;
|
2017-09-02 23:19:20 +02:00
|
|
|
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;
|
2022-12-28 15:42:27 +01:00
|
|
|
sq_select->table_list.first->derived_type= (DTYPE_TABLE |
|
|
|
|
DTYPE_MATERIALIZE |
|
|
|
|
DTYPE_IN_PREDICATE);
|
2017-09-02 23:19:20 +02:00
|
|
|
lex->derived_tables|= DERIVED_SUBQUERY;
|
2017-08-29 02:32:39 +02:00
|
|
|
|
2017-09-02 23:19:20 +02:00
|
|
|
sq_select->where= 0;
|
|
|
|
sq_select->set_braces(false);
|
|
|
|
derived_unit->set_with_clause(0);
|
2017-08-29 02:32:39 +02:00
|
|
|
|
2017-09-02 23:19:20 +02:00
|
|
|
/* Create IN subquery predicate */
|
|
|
|
sq_select->parsing_place= parent_select->parsing_place;
|
|
|
|
Item_in_subselect *in_subs;
|
2017-11-05 20:59:19 +01:00
|
|
|
Item *sq;
|
2017-09-02 23:19:20 +02:00
|
|
|
if (!(in_subs=
|
|
|
|
new (thd->mem_root) Item_in_subselect(thd, args[0], sq_select)))
|
2017-09-01 19:01:06 +02:00
|
|
|
goto err;
|
2020-11-27 17:36:54 +01:00
|
|
|
in_subs->converted_from_in_predicate= TRUE;
|
2017-11-05 20:59:19 +01:00
|
|
|
sq= in_subs;
|
|
|
|
if (negated)
|
|
|
|
sq= negate_expression(thd, in_subs);
|
|
|
|
else
|
|
|
|
in_subs->emb_on_expr_nest= emb_on_expr_nest;
|
|
|
|
|
2017-09-01 19:01:06 +02:00
|
|
|
if (arena)
|
|
|
|
thd->restore_active_arena(arena, &backup);
|
2017-09-02 23:19:20 +02:00
|
|
|
thd->lex->current_select= parent_select;
|
2017-08-29 02:32:39 +02:00
|
|
|
|
2017-11-05 20:59:19 +01:00
|
|
|
if (sq->fix_fields(thd, (Item **)&sq))
|
2017-09-02 23:19:20 +02:00
|
|
|
goto err;
|
|
|
|
|
|
|
|
parent_select->curr_tvc_name++;
|
2020-09-19 23:07:37 +02:00
|
|
|
|
2017-11-05 20:59:19 +01:00
|
|
|
return sq;
|
2017-08-29 02:32:39 +02:00
|
|
|
|
2017-09-01 19:01:06 +02:00
|
|
|
err:
|
|
|
|
if (arena)
|
|
|
|
thd->restore_active_arena(arena, &backup);
|
2017-09-02 23:19:20 +02:00
|
|
|
lex->derived_tables= save_derived_tables;
|
|
|
|
thd->lex->current_select= parent_select;
|
2017-09-04 22:29:58 +02:00
|
|
|
return NULL;
|
2017-08-29 02:32:39 +02:00
|
|
|
}
|
|
|
|
|
2017-09-04 22:29:58 +02:00
|
|
|
|
2019-12-04 15:34:45 +01:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-08-29 02:32:39 +02:00
|
|
|
/**
|
|
|
|
@brief
|
2017-09-04 22:29:58 +02:00
|
|
|
Check if this IN-predicate can be transformed in IN-subquery
|
2017-08-29 02:32:39 +02:00
|
|
|
with TVC
|
|
|
|
|
|
|
|
@param thd The context of the statement
|
|
|
|
|
|
|
|
@details
|
2017-09-04 22:29:58 +02:00
|
|
|
Compare the number of elements in the list of
|
2017-08-29 02:32:39 +02:00
|
|
|
values in this IN-predicate with the
|
|
|
|
in_subquery_conversion_threshold special variable
|
|
|
|
|
|
|
|
@retval
|
|
|
|
true if transformation can be made
|
|
|
|
false otherwise
|
|
|
|
*/
|
|
|
|
|
2017-09-02 23:19:20 +02:00
|
|
|
bool Item_func_in::to_be_transformed_into_in_subq(THD *thd)
|
2017-08-29 02:32:39 +02:00
|
|
|
{
|
2022-03-25 19:04:56 +01:00
|
|
|
bool is_row_list= args[1]->type() == Item::ROW_ITEM;
|
2017-09-04 22:29:58 +02:00
|
|
|
uint values_count= arg_count-1;
|
2017-08-29 02:32:39 +02:00
|
|
|
|
2022-03-25 19:04:56 +01:00
|
|
|
if (is_row_list)
|
2017-09-04 22:29:58 +02:00
|
|
|
values_count*= ((Item_row *)(args[1]))->cols();
|
2017-08-29 02:32:39 +02:00
|
|
|
|
2019-09-01 15:28:11 +02:00
|
|
|
if (thd->variables.in_subquery_conversion_threshold == 0 ||
|
|
|
|
thd->variables.in_subquery_conversion_threshold > values_count)
|
2017-08-29 02:32:39 +02:00
|
|
|
return false;
|
|
|
|
|
2022-03-25 19:04:56 +01:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-08-29 02:32:39 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-09-04 22:29:58 +02:00
|
|
|
|
2017-08-29 02:32:39 +02:00
|
|
|
/**
|
|
|
|
@brief
|
2017-09-02 23:19:20 +02:00
|
|
|
Transform IN predicates into IN subqueries in WHERE and ON expressions
|
2017-08-29 02:32:39 +02:00
|
|
|
|
2017-09-02 23:19:20 +02:00
|
|
|
@param thd The context of the statement
|
2017-08-29 02:32:39 +02:00
|
|
|
|
|
|
|
@details
|
2017-09-02 23:19:20 +02:00
|
|
|
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 sunquery if it's needed.
|
|
|
|
|
|
|
|
@retval
|
|
|
|
false always
|
2017-08-29 02:32:39 +02:00
|
|
|
*/
|
|
|
|
|
2017-09-02 23:19:20 +02:00
|
|
|
bool JOIN::transform_in_predicates_into_in_subq(THD *thd)
|
2017-08-29 02:32:39 +02:00
|
|
|
{
|
2017-09-04 22:29:58 +02:00
|
|
|
DBUG_ENTER("JOIN::transform_in_predicates_into_in_subq");
|
2017-08-29 02:32:39 +02:00
|
|
|
if (!select_lex->in_funcs.elements)
|
2017-09-04 22:29:58 +02:00
|
|
|
DBUG_RETURN(false);
|
2017-08-29 02:32:39 +02:00
|
|
|
|
2017-09-02 23:19:20 +02:00
|
|
|
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;
|
2017-08-29 02:32:39 +02:00
|
|
|
if (conds)
|
|
|
|
{
|
|
|
|
select_lex->parsing_place= IN_WHERE;
|
|
|
|
conds=
|
2022-10-28 16:51:16 +02:00
|
|
|
conds->top_level_transform(thd,
|
|
|
|
&Item::in_predicate_to_in_subs_transformer, 0);
|
2017-09-04 22:29:58 +02:00
|
|
|
if (!conds)
|
|
|
|
DBUG_RETURN(true);
|
2017-08-29 02:32:39 +02:00
|
|
|
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=
|
2022-10-28 16:51:16 +02:00
|
|
|
table->on_expr->top_level_transform(thd,
|
|
|
|
&Item::in_predicate_to_in_subs_transformer, 0);
|
2017-09-04 22:29:58 +02:00
|
|
|
if (!table->on_expr)
|
|
|
|
DBUG_RETURN(true);
|
2017-08-29 02:32:39 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2017-09-02 23:19:20 +02:00
|
|
|
|
2017-09-01 19:01:06 +02:00
|
|
|
select_lex->in_funcs.empty();
|
2017-09-02 23:19:20 +02:00
|
|
|
select_lex->parsing_place= save_parsing_place;
|
|
|
|
thd->lex->current_select= save_current_select;
|
2017-09-04 22:29:58 +02:00
|
|
|
DBUG_RETURN(false);
|
2017-11-02 05:42:26 +01:00
|
|
|
}
|