Commit 34139685 authored by Rex's avatar Rex Committed by Sergei Golubchik

MDEV-31466 Add optional correlation column list for derived tables

Extend derived table syntax to support column name assignment.
(subquery expression) [as|=] ident [comma separated column name list].
Prior to this patch, the optional comma separated column name list is
not supported.

Processing within the unit of the subquery expression will use
original column names, outside the unit will use the new names.

For example, in the query

select a1, a2 from
  (select c1, c2, c3 from t1 where c2 > 0) as dt (a1, a2, a3)
where a2 > 10;

we see the second column of the derived table dt being used both within,
(where c2 > 0), and outside, (where a2 > 10), the specification.
Both conditions apply to t1.c2.

When multiple unit preparations are required, such as when being used within
a prepared statement or procedure, original column names are needed for
correct resolution. Original names are reset within mysql_derived_reinit().

Item_holder items, used for result tables in both TVC and union preparations
are renamed before use within st_select_lex_unit::prepare().

During wildcard expansion, if column names are present, items names are
set directly after creation.

Reviewed by Igor Babaev (igor@mariadb.com)
parent 41c6b844
This diff is collapsed.
This diff is collapsed.
......@@ -12288,3 +12288,6 @@ WARN_SORTING_ON_TRUNCATED_LENGTH
ger "%llu Werte waren länger als max_sort_length. Sie wurden anhand der ersten %lu Bytes verglichen"
rus "%llu значений были длиннее, чем max_sort_length. Их сортировка проведена по первым %lu байтам"
swe "%llu värden var längre än max_sort_length=%lu"
ER_INCORRECT_COLUMN_NAME_COUNT
eng "Incorrect column name count for derived table"
chi "派生表的列名计数不正确"
......@@ -8635,6 +8635,10 @@ insert_fields(THD *thd, Name_resolution_context *context,
*/
field_iterator.set(tables);
List_iterator_fast<Lex_ident_sys> ni;
if (tables->column_names)
ni.init(*tables->column_names);
for (; !field_iterator.end_of_fields(); field_iterator.next())
{
/*
......@@ -8650,6 +8654,9 @@ insert_fields(THD *thd, Name_resolution_context *context,
if (!(item= field_iterator.create_item(thd)))
DBUG_RETURN(TRUE);
if (tables->column_names)
lex_string_set(&item->name, (ni++)->str);
/* cache the table for the Item_fields inserted by expanding stars */
if (item->type() == Item::FIELD_ITEM && tables->cacheable_table)
((Item_field *)item)->cached_table= tables;
......
......@@ -1652,10 +1652,14 @@ void With_clause::print(THD *thd, String *str, enum_query_type query_type)
}
static void list_strlex_print(THD *thd, String *str, List<Lex_ident_sys> *list)
void list_strlex_print(THD *thd, String *str, List<Lex_ident_sys> *list,
bool bracketed)
{
List_iterator_fast<Lex_ident_sys> li(*list);
bool first= TRUE;
if (bracketed)
str->append('(');
while(Lex_ident_sys *col_name= li++)
{
if (first)
......@@ -1664,6 +1668,8 @@ static void list_strlex_print(THD *thd, String *str, List<Lex_ident_sys> *list)
str->append(',');
append_identifier(thd, str, col_name);
}
if (bracketed)
str->append(')');
}
......@@ -1684,12 +1690,7 @@ void With_element::print(THD *thd, String *str, enum_query_type query_type)
{
str->append(get_name());
if (column_list.elements)
{
List_iterator_fast<Lex_ident_sys> li(column_list);
str->append('(');
list_strlex_print(thd, str, &column_list);
str->append(')');
}
list_strlex_print(thd, str, &column_list, true);
str->append(STRING_WITH_LEN(" as ("));
spec->print(str, query_type);
str->append(')');
......
......@@ -550,4 +550,7 @@ void st_select_lex::set_with_clause(With_clause *with_clause)
with_clause->set_owner(master_unit());
}
void list_strlex_print(THD *thd, String *str, List<Lex_ident_sys> *list,
bool bracketed= false);
#endif /* SQL_CTE_INCLUDED */
......@@ -1354,6 +1354,9 @@ bool mysql_derived_reinit(THD *thd, LEX *lex, TABLE_LIST *derived)
derived->get_unit()));
st_select_lex_unit *unit= derived->get_unit();
if (derived->original_names_are_saved)
unit->first_select()->set_item_list_names(derived->original_names);
// reset item names to that saved after wildcard expansion in JOIN::prepare
for(st_select_lex *sl= unit->first_select(); sl; sl= sl->next_select())
sl->restore_item_list_names();
......
......@@ -3606,6 +3606,42 @@ List<Item>* st_select_lex::get_item_list()
}
/**
@brief
Replace the name of each item in the item_list with a new name.
@param
new_names pointer to a List of Lex_ident_sys from which replacement
names are taken.
@details
This is used in derived tables to optionally set the names in the item_list.
Usually called in unit::prepare().
@retval
true: an error occurred
false: success
*/
bool st_select_lex::set_item_list_names(List<Lex_ident_sys> *new_names)
{
if (item_list.elements != new_names->elements)
{
my_error(ER_INCORRECT_COLUMN_NAME_COUNT, MYF(0));
return true;
}
List_iterator<Lex_ident_sys> it(*new_names);
List_iterator_fast<Item> li(item_list);
Item *item;
while ((item= li++))
lex_string_set( &item->name, (it++)->str);
return false;
}
uint st_select_lex::get_cardinality_of_ref_ptrs_slice(uint order_group_num_arg)
{
if (!((options & SELECT_DISTINCT) && !group_list.elements))
......@@ -10745,7 +10781,8 @@ SELECT_LEX *LEX::parsed_TVC_end()
TABLE_LIST *LEX::parsed_derived_table(SELECT_LEX_UNIT *unit,
int for_system_time,
LEX_CSTRING *alias)
LEX_CSTRING *alias,
List<Lex_ident_sys> *column_names)
{
TABLE_LIST *res;
derived_tables|= DERIVED_SUBQUERY;
......@@ -10766,6 +10803,15 @@ TABLE_LIST *LEX::parsed_derived_table(SELECT_LEX_UNIT *unit,
{
res->vers_conditions= vers_conditions;
}
if (column_names && column_names->elements > 0)
{
res->column_names= column_names;
// pre-allocate space to save item_list names
res->original_names= new (thd->mem_root) List<Lex_ident_sys>;
for (uint i= 0; i < column_names->elements; i++)
res->original_names->push_back( new Lex_ident_sys );
}
return res;
}
......
......@@ -805,6 +805,8 @@ class st_select_lex_unit: public st_select_lex_node {
bool is_view:1;
bool describe:1; /* union exec() called for EXPLAIN */
bool columns_are_renamed:1;
inline bool rename_item_list(TABLE_LIST *derived_arg);
inline void rename_types_list(List<Lex_ident_sys> *new_names);
protected:
/* This is bool, not bit, as it's used and set in many places */
......@@ -1324,7 +1326,7 @@ class st_select_lex: public st_select_lex_node
List<Item>* get_item_list();
bool save_item_list_names(THD *thd);
void restore_item_list_names();
bool set_item_list_names( List<Lex_ident_sys> *overwrite );
ulong get_table_join_options();
void set_lock_for_tables(thr_lock_type lock_type, bool for_update,
bool skip_locks);
......@@ -4736,7 +4738,8 @@ struct LEX: public Query_tables_list
SELECT_LEX *parsed_TVC_end();
TABLE_LIST *parsed_derived_table(SELECT_LEX_UNIT *unit,
int for_system_time,
LEX_CSTRING *alias);
LEX_CSTRING *alias,
List<Lex_ident_sys> *column_names= nullptr);
bool parsed_create_view(SELECT_LEX_UNIT *unit, int check);
bool select_finalize(st_select_lex_unit *expr);
bool select_finalize(st_select_lex_unit *expr, Lex_select_lock l);
......
......@@ -31179,6 +31179,9 @@ void TABLE_LIST::print(THD *thd, table_map eliminated_tables, String *str,
str->append(' ');
append_identifier_opt_casedn(thd, str, alias,
lower_case_table_names == 1);
if (column_names && (column_names->elements > 0))
list_strlex_print(thd, str, column_names, true);
}
if (index_hints)
......@@ -1368,6 +1368,27 @@ static select_handler *find_unit_handler(THD *thd,
}
inline bool st_select_lex_unit::rename_item_list(TABLE_LIST *derived_arg)
{
if (derived_arg->save_original_names(first_select()))
return true;
if (first_select()->set_item_list_names(derived_arg->column_names))
return true;
return false;
}
inline void st_select_lex_unit::rename_types_list(List<Lex_ident_sys> *newnames)
{
List_iterator<Lex_ident_sys> it(*newnames);
List_iterator_fast<Item> li(types);
Item *item;
while ((item= li++))
lex_string_set( &item->name, (it++)->str);
}
bool st_select_lex_unit::prepare(TABLE_LIST *derived_arg,
select_result *sel_result,
ulonglong additional_options)
......@@ -1748,6 +1769,14 @@ bool st_select_lex_unit::prepare(TABLE_LIST *derived_arg,
}
}
/*
We need to rename tvc BEFORE Item_holder pushed into result table
below in join_union_item_types().
*/
if (first_select()->tvc && derived_arg && derived_arg->column_names)
if (rename_item_list(derived_arg))
goto err;
// In case of a non-recursive UNION, join data types for all UNION parts.
if (!is_recursive && join_union_item_types(thd, types, union_part_count))
goto err;
......@@ -1940,6 +1969,11 @@ bool st_select_lex_unit::prepare(TABLE_LIST *derived_arg,
global_parameters()->order_list.first, // order
false, NULL, NULL, NULL, fake_select_lex, this);
}
/*
Rename types used in result table for union.
*/
if (derived_arg && derived_arg->column_names)
rename_types_list(derived_arg->column_names);
if (!thd->lex->is_view_context_analysis())
pushdown_unit= find_unit_handler(thd, this);
......@@ -1950,6 +1984,12 @@ bool st_select_lex_unit::prepare(TABLE_LIST *derived_arg,
}
}
if (derived_arg && derived_arg->column_names)
{
if (rename_item_list(derived_arg))
goto err;
}
thd->lex->current_select= lex_select_save;
DBUG_RETURN(saved_error || thd->is_fatal_error);
......
......@@ -1775,8 +1775,8 @@ rule:
repair analyze opt_with_admin opt_with_admin_option
analyze_table_list analyze_table_elem_spec
opt_persistent_stat_clause persistent_stat_spec
persistent_column_stat_spec persistent_index_stat_spec
table_column_list table_index_list table_index_name
persistent_index_stat_spec
table_index_list table_index_name
check start checksum opt_returning
field_list field_list_item kill key_def constraint_def
keycache_list keycache_list_or_parts assign_to_keycache
......@@ -1924,8 +1924,10 @@ rule:
%type <ident_sys_list>
comma_separated_ident_list
opt_with_column_list
with_column_list
derived_column_list
opt_column_name_list
persistent_column_stat_spec
opt_cycle
%type <vers_range_unit> opt_history_unit
......@@ -12135,11 +12137,17 @@ table_primary_ident:
}
;
derived_column_list:
opt_column_name_list
;
table_primary_derived:
subquery
opt_for_system_time_clause table_alias_clause
opt_for_system_time_clause
table_alias_clause
derived_column_list
{
if (!($$= Lex->parsed_derived_table($1->master_unit(), $2, $3)))
if (!($$= Lex->parsed_derived_table($1->master_unit(), $2, $3, $4)))
MYSQL_YYABORT;
}
%ifdef ORACLE
......@@ -15537,10 +15545,13 @@ with_list:
| with_list ',' with_list_element
;
with_column_list:
opt_column_name_list
;
with_list_element:
with_element_head
opt_with_column_list
with_column_list
AS '(' query_expression ')' opt_cycle
{
LEX *lex= thd->lex;
......@@ -15578,21 +15589,16 @@ opt_cycle:
}
;
opt_with_column_list:
opt_column_name_list:
/* empty */
{
if (($$= new (thd->mem_root) List<Lex_ident_sys>) == NULL)
MYSQL_YYABORT;
}
| '(' with_column_list ')'
| '(' comma_separated_ident_list ')'
{ $$= $2; }
;
with_column_list:
comma_separated_ident_list
;
ident_sys_alloc:
ident_cli
{
......
......@@ -10335,6 +10335,50 @@ bool TABLE_LIST::is_the_same_definition(THD* thd, TABLE_SHARE *s)
}
/*
@brief
Save original names of derived table.
@param
derived pointer to a select_lex containing the names to be saved.
@details
This is used in derived tables to optionally set the names of the resultant
columns. Called before first st_select_lex::set_item_list_names().
@retval
true on failure, false otherwise
*/
bool TABLE_LIST::save_original_names(st_select_lex *derived)
{
if (unlikely(derived->with_wild))
return false;
if (original_names_are_saved)
return false;
// these elements allocated in LEX::parsed_derived_table
if (original_names->elements != derived->item_list.elements)
{
my_error(ER_INCORRECT_COLUMN_NAME_COUNT, MYF(0));
return true;
}
List_iterator_fast<Lex_ident_sys> overwrite_iterator(*original_names);
Lex_ident_sys *original_name;
List_iterator_fast<Item> item_list_iterator(derived->item_list);
Item *item_list_element;
while ((item_list_element= item_list_iterator++) &&
(original_name= overwrite_iterator++))
lex_string_set( original_name, item_list_element->name.str);
original_names_are_saved= true;
return false;
}
uint TABLE_SHARE::actual_n_key_parts(THD *thd)
{
return use_ext_keys &&
......
......@@ -2329,6 +2329,7 @@ struct vers_select_conds_t
struct LEX;
class Index_hint;
class Lex_ident_sys;
/*
@struct TABLE_CHAIN
......@@ -2595,9 +2596,18 @@ struct TABLE_LIST
@note Inside views, a subquery in the @c FROM clause is not allowed.
@note Do not use this field to separate views/base tables/anonymous
derived tables. Use TABLE_LIST::is_anonymous_derived_table().
@note _column_names_ below are associated with these derived tables
SELECT * FROM (SELECT a FROM t1) b (list of column names)
@note _original_names_ below are used to save *item_list.name in the
select_lex for multiple executions
*/
st_select_lex_unit *derived; /* SELECT_LEX_UNIT of derived table */
With_element *with; /* With element defining this table (if any) */
List<Lex_ident_sys> *column_names; /* list of correlation column names */
List<Lex_ident_sys> *original_names;/* list of original column names */
bool original_names_are_saved:1;
bool save_original_names(st_select_lex *derived);
/* Bitmap of the defining with element */
table_map with_internal_reference_map;
TABLE_LIST * next_with_rec_ref;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment