mirror of
https://github.com/MariaDB/server.git
synced 2025-07-30 16:24:05 +03:00
Implementation of WL#2486 -
"Process NATURAL and USING joins according to SQL:2003". * Some of the main problems fixed by the patch: - in "select *" queries the * expanded correctly according to ANSI for arbitrary natural/using joins - natural/using joins are correctly transformed into JOIN ... ON for any number/nesting of the joins. - column references are correctly resolved against natural joins of any nesting and combined with arbitrary other joins. * This patch also contains a fix for name resolution of items inside the ON condition of JOIN ... ON - in this case items must be resolved only against the JOIN operands. To support such 'local' name resolution, the patch introduces a stack of name resolution contexts used at parse time. NOTICE: - This patch is not complete in the sense that - there are 2 test cases that still do not pass - one in join.test, one in select.test. Both are marked with a comment "TODO: WL#2486". - it does not include a new test specific for the task
This commit is contained in:
@ -106,12 +106,15 @@ static int check_insert_fields(THD *thd, TABLE_LIST *table_list,
|
||||
}
|
||||
else
|
||||
{ // Part field list
|
||||
Name_resolution_context *context= &thd->lex->select_lex.context;
|
||||
TABLE_LIST *save_next= table_list->next_local,
|
||||
*save_context= context->table_list;
|
||||
bool save_resolve_in_select_list=
|
||||
thd->lex->select_lex.context.resolve_in_select_list;
|
||||
SELECT_LEX *select_lex= &thd->lex->select_lex;
|
||||
Name_resolution_context *context= &select_lex->context;
|
||||
TABLE_LIST *save_next_local;
|
||||
TABLE_LIST *save_table_list;
|
||||
TABLE_LIST *save_first_name_resolution_table;
|
||||
TABLE_LIST *save_next_name_resolution_table;
|
||||
bool save_resolve_in_select_list;
|
||||
int res;
|
||||
|
||||
if (fields.elements != values.elements)
|
||||
{
|
||||
my_error(ER_WRONG_VALUE_COUNT_ON_ROW, MYF(0), 1L);
|
||||
@ -119,17 +122,39 @@ static int check_insert_fields(THD *thd, TABLE_LIST *table_list,
|
||||
}
|
||||
|
||||
thd->dupp_field=0;
|
||||
thd->lex->select_lex.no_wrap_view_item= TRUE;
|
||||
/* fields only from first table */
|
||||
select_lex->no_wrap_view_item= TRUE;
|
||||
|
||||
/* Save the state of the current name resolution context. */
|
||||
save_table_list= context->table_list;
|
||||
save_first_name_resolution_table= context->first_name_resolution_table;
|
||||
save_next_name_resolution_table= (context->first_name_resolution_table) ?
|
||||
context->first_name_resolution_table->
|
||||
next_name_resolution_table :
|
||||
NULL;
|
||||
save_resolve_in_select_list= context->resolve_in_select_list;
|
||||
save_next_local= table_list->next_local;
|
||||
|
||||
/*
|
||||
Perform name resolution only in the first table - 'table_list',
|
||||
which is the table that is inserted into.
|
||||
*/
|
||||
table_list->next_local= 0;
|
||||
context->resolve_in_table_list_only(table_list);
|
||||
res= setup_fields(thd, 0, fields, 1, 0, 0);
|
||||
table_list->next_local= save_next;
|
||||
|
||||
/* Restore the current context. */
|
||||
table_list->next_local= save_next_local;
|
||||
context->table_list= save_table_list;
|
||||
context->first_name_resolution_table= save_first_name_resolution_table;
|
||||
if (context->first_name_resolution_table)
|
||||
context->first_name_resolution_table->
|
||||
next_name_resolution_table= save_next_name_resolution_table;
|
||||
context->resolve_in_select_list= save_resolve_in_select_list;
|
||||
thd->lex->select_lex.no_wrap_view_item= FALSE;
|
||||
context->table_list= save_context;
|
||||
context->resolve_in_select_list= save_resolve_in_select_list;
|
||||
|
||||
if (res)
|
||||
return -1;
|
||||
|
||||
if (table_list->effective_algorithm == VIEW_ALGORITHM_MERGE)
|
||||
{
|
||||
/* it is join view => we need to find table for update */
|
||||
@ -254,9 +279,13 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
|
||||
ulonglong id;
|
||||
COPY_INFO info;
|
||||
TABLE *table= 0;
|
||||
TABLE_LIST *next_local;
|
||||
TABLE_LIST *save_table_list;
|
||||
TABLE_LIST *save_next_local;
|
||||
TABLE_LIST *save_first_name_resolution_table;
|
||||
TABLE_LIST *save_next_name_resolution_table;
|
||||
List_iterator_fast<List_item> its(values_list);
|
||||
List_item *values;
|
||||
Name_resolution_context *context;
|
||||
#ifndef EMBEDDED_LIBRARY
|
||||
char *query= thd->query;
|
||||
#endif
|
||||
@ -335,9 +364,23 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
|
||||
/* mysql_prepare_insert set table_list->table if it was not set */
|
||||
table= table_list->table;
|
||||
|
||||
next_local= table_list->next_local;
|
||||
context= &thd->lex->select_lex.context;
|
||||
/* Save the state of the current name resolution context. */
|
||||
save_table_list= context->table_list;
|
||||
save_first_name_resolution_table= context->first_name_resolution_table;
|
||||
save_next_name_resolution_table= (context->first_name_resolution_table) ?
|
||||
context->first_name_resolution_table->
|
||||
next_name_resolution_table :
|
||||
NULL;
|
||||
save_next_local= table_list->next_local;
|
||||
|
||||
/*
|
||||
Perform name resolution only in the first table - 'table_list',
|
||||
which is the table that is inserted into.
|
||||
*/
|
||||
table_list->next_local= 0;
|
||||
thd->lex->select_lex.context.resolve_in_table_list_only(table_list);
|
||||
context->resolve_in_table_list_only(table_list);
|
||||
|
||||
value_count= values->elements;
|
||||
while ((values= its++))
|
||||
{
|
||||
@ -351,7 +394,14 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
|
||||
goto abort;
|
||||
}
|
||||
its.rewind ();
|
||||
table_list->next_local= next_local;
|
||||
|
||||
/* Restore the current context. */
|
||||
table_list->next_local= save_next_local;
|
||||
context->first_name_resolution_table= save_first_name_resolution_table;
|
||||
if (context->first_name_resolution_table)
|
||||
context->first_name_resolution_table->
|
||||
next_name_resolution_table= save_next_name_resolution_table;
|
||||
|
||||
/*
|
||||
Fill in the given fields and dump it to the table file
|
||||
*/
|
||||
@ -707,6 +757,7 @@ static bool mysql_prepare_insert_check_table(THD *thd, TABLE_LIST *table_list,
|
||||
DBUG_ENTER("mysql_prepare_insert_check_table");
|
||||
|
||||
if (setup_tables(thd, &thd->lex->select_lex.context,
|
||||
&thd->lex->select_lex.top_join_list,
|
||||
table_list, where, &thd->lex->select_lex.leaf_tables,
|
||||
select_insert))
|
||||
DBUG_RETURN(TRUE);
|
||||
@ -761,10 +812,13 @@ bool mysql_prepare_insert(THD *thd, TABLE_LIST *table_list,
|
||||
COND **where, bool select_insert)
|
||||
{
|
||||
SELECT_LEX *select_lex= &thd->lex->select_lex;
|
||||
Name_resolution_context *context= &select_lex->context;
|
||||
TABLE_LIST *save_table_list;
|
||||
TABLE_LIST *save_next_local;
|
||||
TABLE_LIST *save_first_name_resolution_table;
|
||||
TABLE_LIST *save_next_name_resolution_table;
|
||||
bool save_resolve_in_select_list;
|
||||
bool insert_into_view= (table_list->view != 0);
|
||||
bool save_resolve_in_select_list;
|
||||
bool res= 0;
|
||||
DBUG_ENTER("mysql_prepare_insert");
|
||||
DBUG_PRINT("enter", ("table_list 0x%lx, table 0x%lx, view %d",
|
||||
@ -802,35 +856,57 @@ bool mysql_prepare_insert(THD *thd, TABLE_LIST *table_list,
|
||||
select_insert))
|
||||
DBUG_RETURN(TRUE);
|
||||
|
||||
save_table_list= select_lex->context.table_list;
|
||||
save_resolve_in_select_list= select_lex->context.resolve_in_select_list;
|
||||
save_next_local= table_list->next_local;
|
||||
/* Save the state of the current name resolution context. */
|
||||
save_table_list= context->table_list;
|
||||
/* Here first_name_resolution_table points to the first select table. */
|
||||
save_first_name_resolution_table= context->first_name_resolution_table;
|
||||
save_next_name_resolution_table= (context->first_name_resolution_table) ?
|
||||
context->first_name_resolution_table->
|
||||
next_name_resolution_table :
|
||||
NULL;
|
||||
save_resolve_in_select_list= context->resolve_in_select_list;
|
||||
save_next_local= table_list->next_local;
|
||||
|
||||
/*
|
||||
Perform name resolution only in the first table - 'table_list',
|
||||
which is the table that is inserted into.
|
||||
*/
|
||||
table_list->next_local= 0;
|
||||
select_lex->context.resolve_in_table_list_only(table_list);
|
||||
if ((values && check_insert_fields(thd, table_list, fields, *values,
|
||||
context->resolve_in_table_list_only(table_list);
|
||||
|
||||
/* Prepare the fields in the statement. */
|
||||
if ((values && check_insert_fields(thd, context->table_list, fields, *values,
|
||||
!insert_into_view)) ||
|
||||
(values && setup_fields(thd, 0, *values, 0, 0, 0)))
|
||||
res= TRUE;
|
||||
else if (duplic == DUP_UPDATE)
|
||||
{
|
||||
select_lex->no_wrap_view_item= TRUE;
|
||||
res= check_update_fields(thd, table_list, update_fields);
|
||||
res= check_update_fields(thd, context->table_list, update_fields);
|
||||
select_lex->no_wrap_view_item= FALSE;
|
||||
if (select_lex->group_list.elements == 0)
|
||||
{
|
||||
/*
|
||||
When we are not using GROUP BY we can refer to other tables in the
|
||||
ON DUPLICATE KEY part
|
||||
ON DUPLICATE KEY part.
|
||||
*/
|
||||
table_list->next_local= save_next_local;
|
||||
context->table_list->next_local= save_next_local;
|
||||
context->first_name_resolution_table->
|
||||
next_name_resolution_table= save_next_local;
|
||||
}
|
||||
if (!res)
|
||||
res= setup_fields(thd, 0, update_values, 1, 0, 0);
|
||||
}
|
||||
|
||||
/* Restore the current context. */
|
||||
table_list->next_local= save_next_local;
|
||||
select_lex->context.table_list= save_table_list;
|
||||
select_lex->context.resolve_in_select_list= save_resolve_in_select_list;
|
||||
context->table_list= save_table_list;
|
||||
context->first_name_resolution_table= save_first_name_resolution_table;
|
||||
if (context->first_name_resolution_table)
|
||||
context->first_name_resolution_table->
|
||||
next_name_resolution_table= save_next_name_resolution_table;
|
||||
context->resolve_in_select_list= save_resolve_in_select_list;
|
||||
|
||||
if (res)
|
||||
DBUG_RETURN(res);
|
||||
|
||||
|
Reference in New Issue
Block a user