diff options
-rw-r--r-- | mysql-test/r/insert_update.result | 17 | ||||
-rw-r--r-- | mysql-test/t/insert_update.test | 23 | ||||
-rw-r--r-- | sql/item.h | 7 | ||||
-rw-r--r-- | sql/sql_base.cc | 17 | ||||
-rw-r--r-- | sql/sql_help.cc | 2 | ||||
-rw-r--r-- | sql/sql_insert.cc | 77 | ||||
-rw-r--r-- | sql/sql_prepare.cc | 21 | ||||
-rw-r--r-- | sql/sql_union.cc | 2 | ||||
-rw-r--r-- | sql/sql_yacc.yy | 7 |
9 files changed, 105 insertions, 68 deletions
diff --git a/mysql-test/r/insert_update.result b/mysql-test/r/insert_update.result index 6be37450835..f658ff06624 100644 --- a/mysql-test/r/insert_update.result +++ b/mysql-test/r/insert_update.result @@ -219,3 +219,20 @@ SELECT * FROM t1; a b 45 2 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; diff --git a/mysql-test/t/insert_update.test b/mysql-test/t/insert_update.test index b3813864464..4581cc7a875 100644 --- a/mysql-test/t/insert_update.test +++ b/mysql-test/t/insert_update.test @@ -139,3 +139,26 @@ INSERT INTO t1 VALUES (45, 1) ON DUPLICATE KEY UPDATE b = SELECT * FROM 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; diff --git a/sql/item.h b/sql/item.h index 62d6127f10b..e6be2f223da 100644 --- a/sql/item.h +++ b/sql/item.h @@ -325,10 +325,10 @@ private: TABLE_LIST *save_first_name_resolution_table; TABLE_LIST *save_next_name_resolution_table; bool save_resolve_in_select_list; + TABLE_LIST *save_next_local; public: Name_resolution_context_state() {} /* Remove gcc warning */ - TABLE_LIST *save_next_local; public: /* Save the state of a name resolution context. */ @@ -355,6 +355,11 @@ public: next_name_resolution_table= save_next_name_resolution_table; context->resolve_in_select_list= save_resolve_in_select_list; } + + TABLE_LIST *get_first_name_resolution_table() + { + return save_first_name_resolution_table; + } }; /*************************************************************************/ diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 0949d4aa331..304a16dc62e 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -4498,21 +4498,8 @@ bool setup_tables(THD *thd, Name_resolution_context *context, uint tablenr= 0; DBUG_ENTER("setup_tables"); - /* - Due to the various call paths that lead to setup_tables() it may happen - 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; - } - + DBUG_ASSERT ((select_insert && !tables->next_name_resolution_table) || !tables || + (context->table_list && context->first_name_resolution_table)); /* this is used for INSERT ... SELECT. For select we setup tables except first (and its underlying tables) diff --git a/sql/sql_help.cc b/sql/sql_help.cc index 78349a6ef0d..ba7f1a534ea 100644 --- a/sql/sql_help.cc +++ b/sql/sql_help.cc @@ -656,6 +656,8 @@ bool mysqld_help(THD *thd, const char *mask) Init tables and fields to be usable from items 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, &thd->lex->select_lex.top_join_list, tables, 0, &leaves, FALSE); diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index fb59aeea8e7..75caba84716 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -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", (ulong)table_list, (ulong)table, (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 @@ -998,44 +1000,40 @@ bool mysql_prepare_insert(THD *thd, TABLE_LIST *table_list, select_insert)) DBUG_RETURN(TRUE); - /* Save the state of the current name resolution context. */ - ctx_state.save_state(context, table_list); - - /* - 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); /* Prepare the fields in the statement. */ - if (values && - !(res= check_insert_fields(thd, context->table_list, fields, *values, - !insert_into_view, &map) || - setup_fields(thd, 0, *values, 0, 0, 0)) && - duplic == DUP_UPDATE) + if (values) { - select_lex->no_wrap_view_item= TRUE; - res= check_update_fields(thd, context->table_list, update_fields, &map); - select_lex->no_wrap_view_item= FALSE; + /* 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. */ + ctx_state.save_state(context, table_list); + /* - 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) + 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); + + if (!(res= check_insert_fields(thd, context->table_list, fields, *values, + !insert_into_view, &map) || + setup_fields(thd, 0, *values, 0, 0, 0)) + && duplic == DUP_UPDATE) { - 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; + select_lex->no_wrap_view_item= TRUE; + res= check_update_fields(thd, context->table_list, update_fields, &map); + select_lex->no_wrap_view_item= FALSE; } + + /* Restore the current context. */ + ctx_state.restore_state(context, table_list); + if (!res) res= setup_fields(thd, 0, update_values, 1, 0, 0); } - /* Restore the current context. */ - ctx_state.restore_state(context, table_list); - if (res) DBUG_RETURN(res); @@ -2355,7 +2353,6 @@ select_insert::prepare(List<Item> &values, SELECT_LEX_UNIT *u) 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_state ctx_state; @@ -2371,16 +2368,20 @@ select_insert::prepare(List<Item> &values, SELECT_LEX_UNIT *u) *info.update_fields, &map); lex->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 + When we are not using GROUP BY and there are no ungrouped aggregate functions + 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) - { - 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; - } + DBUG_ASSERT (!table_list->next_name_resolution_table); + if (lex->select_lex.group_list.elements == 0 && + !lex->select_lex.with_sum_func) + /* + We must make a single context out of the two separate name resolution contexts : + 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); /* Restore the current context. */ diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index 002b1d52331..3b46d613c93 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -1571,21 +1571,16 @@ error: static bool mysql_insert_select_prepare_tester(THD *thd) { - TABLE_LIST *first; - bool res; 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 */ - first_select->table_list.first= (byte*)(first= - ((TABLE_LIST*)first_select-> - table_list.first)->next_local); - res= 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; + first_select->table_list.first= (byte *) second_table; + thd->lex->select_lex.context.table_list= + thd->lex->select_lex.context.first_name_resolution_table= second_table; + + return mysql_insert_select_prepare(thd); } diff --git a/sql/sql_union.cc b/sql/sql_union.cc index 8b7dde2f818..0632d88875e 100644 --- a/sql/sql_union.cc +++ b/sql/sql_union.cc @@ -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, (byte **) &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; order; order=order->next) diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 92640ea58d6..15682385cfe 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -4188,8 +4188,13 @@ select_into: | select_from into; 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 + { + Select->context.table_list= + Select->context.first_name_resolution_table= + (TABLE_LIST *) Select->table_list.first; + } | FROM DUAL_SYM where_clause opt_limit_clause /* oracle compatibility: oracle always requires FROM clause, and DUAL is system table without fields. |