diff --git a/mysql-test/README b/mysql-test/README index 65e6186613a..10d64784ed4 100644 --- a/mysql-test/README +++ b/mysql-test/README @@ -2,8 +2,17 @@ This directory contains a test suite for mysql daemon. To run the currently existing test cases, simply execute ./mysql-test-run in this directory. It will fire up the newly built mysqld and test it. -If you want to run the test with a running MySQL server use the --external -option to mysql-test-run. +If you want to run a test with a running MySQL server use the --extern +option to mysql-test-run. Please note that in this mode the test suite +expects user to specify test names to run. Otherwise it falls back to the +normal "non-extern" behaviour. The reason is that some tests +could not run with external server. Here is the sample command +to test "alias" and "analyze" tests on external server: + +mysql-test-run --extern alias analyze + +To match your setup you might also need to provide --socket, --user and +other relevant options. Note that you do not have to have to do make install, and you could actually have a co-existing MySQL installation - the tests will not diff --git a/mysql-test/r/insert_select.result b/mysql-test/r/insert_select.result index 11384b0feff..026dae8381a 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/r/insert_update.result b/mysql-test/r/insert_update.result index 6d3aa941c8c..739beea6286 100644 --- a/mysql-test/r/insert_update.result +++ b/mysql-test/r/insert_update.result @@ -1,4 +1,4 @@ -DROP TABLE IF EXISTS t1; +DROP TABLE IF EXISTS t1, t2; CREATE TABLE t1 (a INT, b INT, c INT, UNIQUE (A), UNIQUE(B)); INSERT t1 VALUES (1,2,10), (3,4,20); INSERT t1 VALUES (5,6,30) ON DUPLICATE KEY UPDATE c=c+100; diff --git a/mysql-test/t/insert_select.test b/mysql-test/t/insert_select.test index 834561ed5f7..a6468c52645 100644 --- a/mysql-test/t/insert_select.test +++ b/mysql-test/t/insert_select.test @@ -173,3 +173,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/mysql-test/t/insert_update.test b/mysql-test/t/insert_update.test index f5857840588..7653fd8dd42 100644 --- a/mysql-test/t/insert_update.test +++ b/mysql-test/t/insert_update.test @@ -1,5 +1,5 @@ --disable_warnings -DROP TABLE IF EXISTS t1; +DROP TABLE IF EXISTS t1, t2; --enable_warnings CREATE TABLE t1 (a INT, b INT, c INT, UNIQUE (A), UNIQUE(B)); diff --git a/sql/item.cc b/sql/item.cc index ae000f60a58..d3888cef9d5 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -338,6 +338,7 @@ Item::Item(): place == IN_HAVING) thd->lex->current_select->select_n_having_items++; } + item_flags= 0; } /* @@ -358,7 +359,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 c912ad3f0a7..c8180b4932a 100644 --- a/sql/item.h +++ b/sql/item.h @@ -225,6 +225,11 @@ typedef Item* (Item::*Item_transformer) (byte *arg); typedef void (*Cond_traverser) (const Item *item, void *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 &); @@ -272,6 +277,7 @@ public: my_bool is_autogenerated_name; /* indicate was name of this Item autogenerated or set by user */ DTCollation collation; + uint8 item_flags; /* Flags on how item should be processed */ // alloc & destruct is done as start of select using sql_alloc Item(); @@ -584,6 +590,11 @@ public: cleanup(); delete this; } + virtual bool set_flags_processor(byte *args) + { + this->item_flags|= *((uint8*)args); + return false; + } virtual bool is_splocal() { return 0; } /* Needed for error checking */ }; diff --git a/sql/sql_base.cc b/sql/sql_base.cc index fee3bdfeb5f..e9af684767d 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -2626,7 +2626,6 @@ find_field_in_tables(THD *thd, Item_ident *item, TABLE_LIST *tables, uint length=(uint) strlen(name); char name_buff[NAME_LEN+1]; - if (item->cached_table) { /* @@ -2693,10 +2692,13 @@ find_field_in_tables(THD *thd, Item_ident *item, TABLE_LIST *tables, db= name_buff; } + bool search_global= item->item_flags & MY_ITEM_PREFER_1ST_TABLE; if (table_name && table_name[0]) { /* Qualified field */ - bool found_table=0; - for (; tables; tables= tables->next_local) + bool found_table=0; + uint table_idx= 0; + for (; tables; tables= search_global?tables->next_global:tables->next_local, + table_idx++) { /* TODO; Ensure that db and tables->db always points to something ! */ if (!my_strcasecmp(table_alias_charset, tables->alias, table_name) && @@ -2732,6 +2734,8 @@ find_field_in_tables(THD *thd, Item_ident *item, TABLE_LIST *tables, return (Field*) 0; } found=find; + if (table_idx == 0 && item->item_flags & MY_ITEM_PREFER_1ST_TABLE) + break; } } } @@ -2756,9 +2760,10 @@ find_field_in_tables(THD *thd, Item_ident *item, TABLE_LIST *tables, return (Field*) not_found_field; return (Field*) 0; } - bool allow_rowid= tables && !tables->next_local; // Only one table - for (; tables ; tables= tables->next_local) + uint table_idx= 0; + for (; tables ; tables= search_global?tables->next_global:tables->next_local, + table_idx++) { if (!tables->table && !tables->ancestor) { @@ -2793,7 +2798,9 @@ find_field_in_tables(THD *thd, Item_ident *item, TABLE_LIST *tables, my_error(ER_NON_UNIQ_ERROR, MYF(0), 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_yacc.yy b/sql/sql_yacc.yy index 1b9176e2744..360bc421965 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -6120,9 +6120,24 @@ insert_update_elem: simple_ident_nospvar 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: