Bug #25831: Deficiencies in INSERT ... SELECT ... field name resolving.

 Several problems fixed: 
  1. There was a "catch-all" context initialization in setup_tables()
    that was causing the table that we insert into to be visible in the 
    SELECT part of an INSERT .. SELECT .. statement with no tables in
    its FROM clause. This was making sure all the under-initialized
    contexts in various parts of the code are not left uninitialized.
    Fixed by removing the "catch-all" statement and initializing the 
    context in the parser.
  2. Incomplete name resolution context when resolving the right-hand
    values in the ON DUPLICATE KEY UPDATE ... part of an INSERT ... SELECT ...
    caused columns from NATURAL JOIN/JOIN USING table references in the
    FROM clause of the select to be unavailable.
    Fixed by establishing a proper name resolution context.
  3. When setting up the special name resolution context for problem 2
    there was no check for cases where an aggregate function without a
    GROUP BY effectively takes the column from the SELECT part of an 
    INSERT ... SELECT unavailable for ON DUPLICATE KEY UPDATE.
    Fixed by checking for that condition when setting up the name 
    resolution context.
parent 4908cf07
...@@ -219,3 +219,20 @@ SELECT * FROM t1; ...@@ -219,3 +219,20 @@ SELECT * FROM t1;
a b a b
45 2 45 2
DROP TABLE t1; DROP TABLE t1;
CREATE TABLE t1 (i INT PRIMARY KEY, j INT);
INSERT INTO t1 SELECT 1, j;
ERROR 42S22: Unknown column 'j' in 'field list'
DROP TABLE t1;
CREATE TABLE t1 (i INT PRIMARY KEY, j INT);
CREATE TABLE t2 (a INT, b INT);
CREATE TABLE t3 (a INT, c INT);
INSERT INTO t1 SELECT 1, a FROM t2 NATURAL JOIN t3
ON DUPLICATE KEY UPDATE j= a;
DROP TABLE t1,t2,t3;
CREATE TABLE t1 (i INT PRIMARY KEY, j INT);
CREATE TABLE t2 (a INT);
INSERT INTO t1 VALUES (1, 1);
INSERT INTO t2 VALUES (1), (3);
INSERT INTO t1 SELECT 1, COUNT(*) FROM t2 ON DUPLICATE KEY UPDATE j= a;
ERROR 42S22: Unknown column 'a' in 'field list'
DROP TABLE t1,t2;
...@@ -139,3 +139,26 @@ INSERT INTO t1 VALUES (45, 1) ON DUPLICATE KEY UPDATE b = ...@@ -139,3 +139,26 @@ INSERT INTO t1 VALUES (45, 1) ON DUPLICATE KEY UPDATE b =
SELECT * FROM t1; SELECT * FROM t1;
DROP TABLE t1; DROP TABLE t1;
#
# Bug#25831: Deficiencies in INSERT ... SELECT ... field name resolving.
#
CREATE TABLE t1 (i INT PRIMARY KEY, j INT);
--error ER_BAD_FIELD_ERROR
INSERT INTO t1 SELECT 1, j;
DROP TABLE t1;
CREATE TABLE t1 (i INT PRIMARY KEY, j INT);
CREATE TABLE t2 (a INT, b INT);
CREATE TABLE t3 (a INT, c INT);
INSERT INTO t1 SELECT 1, a FROM t2 NATURAL JOIN t3
ON DUPLICATE KEY UPDATE j= a;
DROP TABLE t1,t2,t3;
CREATE TABLE t1 (i INT PRIMARY KEY, j INT);
CREATE TABLE t2 (a INT);
INSERT INTO t1 VALUES (1, 1);
INSERT INTO t2 VALUES (1), (3);
--error ER_BAD_FIELD_ERROR
INSERT INTO t1 SELECT 1, COUNT(*) FROM t2 ON DUPLICATE KEY UPDATE j= a;
DROP TABLE t1,t2;
...@@ -325,10 +325,10 @@ class Name_resolution_context_state ...@@ -325,10 +325,10 @@ class Name_resolution_context_state
TABLE_LIST *save_first_name_resolution_table; TABLE_LIST *save_first_name_resolution_table;
TABLE_LIST *save_next_name_resolution_table; TABLE_LIST *save_next_name_resolution_table;
bool save_resolve_in_select_list; bool save_resolve_in_select_list;
TABLE_LIST *save_next_local;
public: public:
Name_resolution_context_state() {} /* Remove gcc warning */ Name_resolution_context_state() {} /* Remove gcc warning */
TABLE_LIST *save_next_local;
public: public:
/* Save the state of a name resolution context. */ /* Save the state of a name resolution context. */
...@@ -355,6 +355,11 @@ class Name_resolution_context_state ...@@ -355,6 +355,11 @@ class Name_resolution_context_state
next_name_resolution_table= save_next_name_resolution_table; next_name_resolution_table= save_next_name_resolution_table;
context->resolve_in_select_list= save_resolve_in_select_list; context->resolve_in_select_list= save_resolve_in_select_list;
} }
TABLE_LIST *get_first_name_resolution_table()
{
return save_first_name_resolution_table;
}
}; };
/*************************************************************************/ /*************************************************************************/
......
...@@ -4498,21 +4498,8 @@ bool setup_tables(THD *thd, Name_resolution_context *context, ...@@ -4498,21 +4498,8 @@ bool setup_tables(THD *thd, Name_resolution_context *context,
uint tablenr= 0; uint tablenr= 0;
DBUG_ENTER("setup_tables"); DBUG_ENTER("setup_tables");
/* DBUG_ASSERT ((select_insert && !tables->next_name_resolution_table) || !tables ||
Due to the various call paths that lead to setup_tables() it may happen (context->table_list && context->first_name_resolution_table));
that context->table_list and context->first_name_resolution_table can be
NULL (this is typically done when creating TABLE_LISTs internally).
TODO:
Investigate all cases when this my happen, initialize the name resolution
context correctly in all those places, and remove the context reset below.
*/
if (!context->table_list || !context->first_name_resolution_table)
{
/* Test whether the context is in a consistent state. */
DBUG_ASSERT(!context->first_name_resolution_table && !context->table_list);
context->table_list= context->first_name_resolution_table= tables;
}
/* /*
this is used for INSERT ... SELECT. this is used for INSERT ... SELECT.
For select we setup tables except first (and its underlying tables) For select we setup tables except first (and its underlying tables)
......
...@@ -656,6 +656,8 @@ bool mysqld_help(THD *thd, const char *mask) ...@@ -656,6 +656,8 @@ bool mysqld_help(THD *thd, const char *mask)
Init tables and fields to be usable from items Init tables and fields to be usable from items
tables do not contain VIEWs => we can pass 0 as conds tables do not contain VIEWs => we can pass 0 as conds
*/ */
thd->lex->select_lex.context.table_list=
thd->lex->select_lex.context.first_name_resolution_table= &tables[0];
setup_tables(thd, &thd->lex->select_lex.context, setup_tables(thd, &thd->lex->select_lex.context,
&thd->lex->select_lex.top_join_list, &thd->lex->select_lex.top_join_list,
tables, 0, &leaves, FALSE); tables, 0, &leaves, FALSE);
......
...@@ -966,6 +966,8 @@ bool mysql_prepare_insert(THD *thd, TABLE_LIST *table_list, ...@@ -966,6 +966,8 @@ bool mysql_prepare_insert(THD *thd, TABLE_LIST *table_list,
DBUG_PRINT("enter", ("table_list 0x%lx, table 0x%lx, view %d", DBUG_PRINT("enter", ("table_list 0x%lx, table 0x%lx, view %d",
(ulong)table_list, (ulong)table, (ulong)table_list, (ulong)table,
(int)insert_into_view)); (int)insert_into_view));
/* INSERT should have a SELECT or VALUES clause */
DBUG_ASSERT (!select_insert || !values);
/* /*
For subqueries in VALUES() we should not see the table in which we are For subqueries in VALUES() we should not see the table in which we are
...@@ -998,6 +1000,13 @@ bool mysql_prepare_insert(THD *thd, TABLE_LIST *table_list, ...@@ -998,6 +1000,13 @@ bool mysql_prepare_insert(THD *thd, TABLE_LIST *table_list,
select_insert)) select_insert))
DBUG_RETURN(TRUE); DBUG_RETURN(TRUE);
/* Prepare the fields in the statement. */
if (values)
{
/* if we have INSERT ... VALUES () we cannot have a GROUP BY clause */
DBUG_ASSERT (!select_lex->group_list.elements);
/* Save the state of the current name resolution context. */ /* Save the state of the current name resolution context. */
ctx_state.save_state(context, table_list); ctx_state.save_state(context, table_list);
...@@ -1008,34 +1017,23 @@ bool mysql_prepare_insert(THD *thd, TABLE_LIST *table_list, ...@@ -1008,34 +1017,23 @@ bool mysql_prepare_insert(THD *thd, TABLE_LIST *table_list,
table_list->next_local= 0; table_list->next_local= 0;
context->resolve_in_table_list_only(table_list); context->resolve_in_table_list_only(table_list);
/* Prepare the fields in the statement. */ if (!(res= check_insert_fields(thd, context->table_list, fields, *values,
if (values &&
!(res= check_insert_fields(thd, context->table_list, fields, *values,
!insert_into_view, &map) || !insert_into_view, &map) ||
setup_fields(thd, 0, *values, 0, 0, 0)) && setup_fields(thd, 0, *values, 0, 0, 0))
duplic == DUP_UPDATE) && duplic == DUP_UPDATE)
{ {
select_lex->no_wrap_view_item= TRUE; select_lex->no_wrap_view_item= TRUE;
res= check_update_fields(thd, context->table_list, update_fields, &map); res= check_update_fields(thd, context->table_list, update_fields, &map);
select_lex->no_wrap_view_item= FALSE; select_lex->no_wrap_view_item= FALSE;
/*
When we are not using GROUP BY we can refer to other tables in the
ON DUPLICATE KEY part.
*/
if (select_lex->group_list.elements == 0)
{
context->table_list->next_local= ctx_state.save_next_local;
/* first_name_resolution_table was set by resolve_in_table_list_only() */
context->first_name_resolution_table->
next_name_resolution_table= ctx_state.save_next_local;
}
if (!res)
res= setup_fields(thd, 0, update_values, 1, 0, 0);
} }
/* Restore the current context. */ /* Restore the current context. */
ctx_state.restore_state(context, table_list); ctx_state.restore_state(context, table_list);
if (!res)
res= setup_fields(thd, 0, update_values, 1, 0, 0);
}
if (res) if (res)
DBUG_RETURN(res); DBUG_RETURN(res);
...@@ -2355,7 +2353,6 @@ select_insert::prepare(List<Item> &values, SELECT_LEX_UNIT *u) ...@@ -2355,7 +2353,6 @@ select_insert::prepare(List<Item> &values, SELECT_LEX_UNIT *u)
if (info.handle_duplicates == DUP_UPDATE) if (info.handle_duplicates == DUP_UPDATE)
{ {
/* Save the state of the current name resolution context. */
Name_resolution_context *context= &lex->select_lex.context; Name_resolution_context *context= &lex->select_lex.context;
Name_resolution_context_state ctx_state; Name_resolution_context_state ctx_state;
...@@ -2371,16 +2368,20 @@ select_insert::prepare(List<Item> &values, SELECT_LEX_UNIT *u) ...@@ -2371,16 +2368,20 @@ select_insert::prepare(List<Item> &values, SELECT_LEX_UNIT *u)
*info.update_fields, &map); *info.update_fields, &map);
lex->select_lex.no_wrap_view_item= FALSE; lex->select_lex.no_wrap_view_item= FALSE;
/* /*
When we are not using GROUP BY we can refer to other tables in the When we are not using GROUP BY and there are no ungrouped aggregate functions
ON DUPLICATE KEY part we can refer to other tables in the ON DUPLICATE KEY part.
We use next_name_resolution_table descructively, so check it first (views?)
*/ */
if (lex->select_lex.group_list.elements == 0) DBUG_ASSERT (!table_list->next_name_resolution_table);
{ if (lex->select_lex.group_list.elements == 0 &&
context->table_list->next_local= ctx_state.save_next_local; !lex->select_lex.with_sum_func)
/* first_name_resolution_table was set by resolve_in_table_list_only() */ /*
context->first_name_resolution_table-> We must make a single context out of the two separate name resolution contexts :
next_name_resolution_table= ctx_state.save_next_local; the INSERT table and the tables in the SELECT part of INSERT ... SELECT.
} To do that we must concatenate the two lists
*/
table_list->next_name_resolution_table= ctx_state.get_first_name_resolution_table();
res= res || setup_fields(thd, 0, *info.update_values, 1, 0, 0); res= res || setup_fields(thd, 0, *info.update_values, 1, 0, 0);
/* Restore the current context. */ /* Restore the current context. */
......
...@@ -1571,21 +1571,16 @@ static bool mysql_test_multidelete(Prepared_statement *stmt, ...@@ -1571,21 +1571,16 @@ static bool mysql_test_multidelete(Prepared_statement *stmt,
static bool mysql_insert_select_prepare_tester(THD *thd) static bool mysql_insert_select_prepare_tester(THD *thd)
{ {
TABLE_LIST *first;
bool res;
SELECT_LEX *first_select= &thd->lex->select_lex; SELECT_LEX *first_select= &thd->lex->select_lex;
TABLE_LIST *second_table= ((TABLE_LIST*)first_select->table_list.first)->
next_local;
/* Skip first table, which is the table we are inserting in */ /* Skip first table, which is the table we are inserting in */
first_select->table_list.first= (byte*)(first= first_select->table_list.first= (byte *) second_table;
((TABLE_LIST*)first_select-> thd->lex->select_lex.context.table_list=
table_list.first)->next_local); thd->lex->select_lex.context.first_name_resolution_table= second_table;
res= mysql_insert_select_prepare(thd);
/* return mysql_insert_select_prepare(thd);
insert/replace from SELECT give its SELECT_LEX for SELECT,
and item_list belong to SELECT
*/
thd->lex->select_lex.context.resolve_in_select_list= TRUE;
thd->lex->select_lex.context.table_list= first;
return res;
} }
......
...@@ -147,6 +147,8 @@ st_select_lex_unit::init_prepare_fake_select_lex(THD *thd) ...@@ -147,6 +147,8 @@ st_select_lex_unit::init_prepare_fake_select_lex(THD *thd)
fake_select_lex->table_list.link_in_list((byte *)&result_table_list, fake_select_lex->table_list.link_in_list((byte *)&result_table_list,
(byte **) (byte **)
&result_table_list.next_local); &result_table_list.next_local);
fake_select_lex->context.table_list= fake_select_lex->context.first_name_resolution_table=
fake_select_lex->get_table_list();
for (ORDER *order= (ORDER *)global_parameters->order_list.first; for (ORDER *order= (ORDER *)global_parameters->order_list.first;
order; order;
order=order->next) order=order->next)
......
...@@ -4190,6 +4190,11 @@ select_into: ...@@ -4190,6 +4190,11 @@ select_into:
select_from: select_from:
FROM join_table_list where_clause group_clause having_clause FROM join_table_list where_clause group_clause having_clause
opt_order_clause opt_limit_clause procedure_clause opt_order_clause opt_limit_clause procedure_clause
{
Select->context.table_list=
Select->context.first_name_resolution_table=
(TABLE_LIST *) Select->table_list.first;
}
| FROM DUAL_SYM where_clause opt_limit_clause | FROM DUAL_SYM where_clause opt_limit_clause
/* oracle compatibility: oracle always requires FROM clause, /* oracle compatibility: oracle always requires FROM clause,
and DUAL is system table without fields. and DUAL is system table without fields.
......
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