diff --git a/mysql-test/r/insert_select.result b/mysql-test/r/insert_select.result index 082b7098a1a..69eb64f08ea 100644 --- a/mysql-test/r/insert_select.result +++ b/mysql-test/r/insert_select.result @@ -634,3 +634,18 @@ ff1 ff2 1 2 2 1 drop table t1, t2; +create table t1 (a int unique); +create table t2 (a int, b int); +insert into t1 values (1),(2); +insert into t2 values (1,2); +select * from t1; +a +1 +2 +insert into t1 select t2.a from t2 on duplicate key update a= a + t2.b; +select * from t1; +a +2 +3 +drop table t1; +drop table t2; diff --git a/mysql-test/t/insert_select.test b/mysql-test/t/insert_select.test index 613c97dc4f1..7402940fa52 100644 --- a/mysql-test/t/insert_select.test +++ b/mysql-test/t/insert_select.test @@ -175,3 +175,17 @@ insert into t1 values (1),(1),(2); insert into t2(ff1) select f1 from t1 on duplicate key update ff2=ff2+1; select * from t2; drop table t1, t2; +# +# BUGS #9728 - 'Decreased functionality in "on duplicate key update"' +# #8147 - 'a column proclaimed ambigous in INSERT ... SELECT .. ON +# DUPLICATE' +# +create table t1 (a int unique); +create table t2 (a int, b int); +insert into t1 values (1),(2); +insert into t2 values (1,2); +select * from t1; +insert into t1 select t2.a from t2 on duplicate key update a= a + t2.b; +select * from t1; +drop table t1; +drop table t2; diff --git a/sql/item.cc b/sql/item.cc index c96794ff482..9c5bf499d11 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -65,6 +65,7 @@ Item::Item(): place == IN_HAVING) thd->lex->current_select->select_n_having_items++; } + item_flags= 0; } /* @@ -83,7 +84,8 @@ Item::Item(THD *thd, Item *item): unsigned_flag(item->unsigned_flag), with_sum_func(item->with_sum_func), fixed(item->fixed), - collation(item->collation) + collation(item->collation), + item_flags(item->item_flags) { next= thd->free_list; // Put in free list thd->free_list= this; diff --git a/sql/item.h b/sql/item.h index 8de2adeb730..d9a40cbdfb6 100644 --- a/sql/item.h +++ b/sql/item.h @@ -107,6 +107,11 @@ public: typedef bool (Item::*Item_processor)(byte *arg); +/* + See comments for sql_yacc.yy: insert_update_elem rule + */ +#define MY_ITEM_PREFER_1ST_TABLE 1 + class Item { Item(const Item &); /* Prevent use of these */ void operator=(Item &); @@ -142,6 +147,7 @@ public: my_bool with_sum_func; my_bool fixed; /* If item fixed with fix_fields */ DTCollation collation; + uint8 item_flags; /* Flags on how item should be processed */ // alloc & destruct is done as start of select using sql_alloc Item(); @@ -327,6 +333,11 @@ public: cleanup(); delete this; } + virtual bool set_flags_processor(byte *args) + { + this->item_flags|= *((uint8*)args); + return true; + } }; diff --git a/sql/sql_base.cc b/sql/sql_base.cc index e3c0efb83b8..84c03bee917 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -2086,7 +2086,8 @@ find_field_in_tables(THD *thd, Item_ident *item, TABLE_LIST *tables, return (Field*) 0; } bool allow_rowid= tables && !tables->next; // Only one table - for (; tables ; tables=tables->next) + uint table_idx= 0; + for (; tables ; tables=tables->next, table_idx++) { if (!tables->table) { @@ -2114,7 +2115,9 @@ find_field_in_tables(THD *thd, Item_ident *item, TABLE_LIST *tables, name,thd->where); return (Field*) 0; } - found=field; + found= field; + if (table_idx == 0 && item->item_flags & MY_ITEM_PREFER_1ST_TABLE) + break; } } if (found) diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 4d339fc6a0e..233104c9a90 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -2856,17 +2856,14 @@ unsent_create_error: if ((res= open_and_lock_tables(thd, tables))) break; - TABLE *table= tables->table; /* Skip first table, which is the table we are inserting in */ select_lex->table_list.first= (byte*) first_local_table->next; - tables= (TABLE_LIST *) select_lex->table_list.first; - first_local_table->next= 0; if (!(res= mysql_prepare_insert(thd, tables, first_local_table, - table, lex->field_list, 0, + tables->table, lex->field_list, 0, lex->update_list, lex->value_list, lex->duplicates)) && - (result= new select_insert(table, &lex->field_list, + (result= new select_insert(tables->table, &lex->field_list, &lex->update_list, &lex->value_list, lex->duplicates, lex->ignore))) { @@ -2879,7 +2876,7 @@ unsent_create_error: /* revert changes for SP */ lex->select_lex.resolve_mode= SELECT_LEX::INSERT_MODE; delete result; - table->insert_values= 0; + tables->table->insert_values= 0; if (thd->net.report_error) res= -1; } diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index bc21649fe54..460234de156 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -4239,9 +4239,24 @@ insert_update_elem: simple_ident equal expr_or_default { LEX *lex= Lex; + uint8 tmp= MY_ITEM_PREFER_1ST_TABLE; if (lex->update_list.push_back($1) || lex->value_list.push_back($3)) YYABORT; + /* + INSERT INTO a1(a) SELECT b1.a FROM b1 ON DUPLICATE KEY + UPDATE a= a + b1.b + + Set MY_ITEM_PREFER_1ST_TABLE flag to $1 and $3 items + to prevent find_field_in_tables() doing further item searching + if it finds item occurence in first table in insert_table_list. + This allows to avoid ambiguity in resolving 'a' field in + example above. + */ + $1->walk(&Item::set_flags_processor, + (byte *) &tmp); + $3->walk(&Item::set_flags_processor, + (byte *) &tmp); }; opt_low_priority: