diff options
-rw-r--r-- | mysql-test/r/insert_update.result | 6 | ||||
-rw-r--r-- | mysql-test/t/insert_update.test | 14 | ||||
-rw-r--r-- | sql/sql_class.h | 14 | ||||
-rw-r--r-- | sql/sql_insert.cc | 36 | ||||
-rw-r--r-- | sql/sql_parse.cc | 3 |
5 files changed, 58 insertions, 15 deletions
diff --git a/mysql-test/r/insert_update.result b/mysql-test/r/insert_update.result index 150f4ef26c7..9e674cc4aae 100644 --- a/mysql-test/r/insert_update.result +++ b/mysql-test/r/insert_update.result @@ -191,3 +191,9 @@ ERROR 23000: Column 'a' in field list is ambiguous insert ignore into t1 select a from t1 on duplicate key update a=t1.a+1 ; ERROR 23000: Column 't1.a' in field list is ambiguous drop table t1; +CREATE TABLE t1 ( +a BIGINT(20) NOT NULL DEFAULT 0, +PRIMARY KEY (a) +) ENGINE=MyISAM; +INSERT INTO t1 ( a ) SELECT 0 ON DUPLICATE KEY UPDATE a = a + VALUES (a) ; +DROP TABLE t1; diff --git a/mysql-test/t/insert_update.test b/mysql-test/t/insert_update.test index d0e75f0fa2a..8038bd7bfe7 100644 --- a/mysql-test/t/insert_update.test +++ b/mysql-test/t/insert_update.test @@ -101,4 +101,18 @@ insert into t1 select a from t1 on duplicate key update a=a+1 ; insert ignore into t1 select a from t1 on duplicate key update a=t1.a+1 ; drop table t1; +# +# Bug#10109 - INSERT .. SELECT ... ON DUPLICATE KEY UPDATE fails +# Bogus "Duplicate columns" error message +# + +CREATE TABLE t1 ( + a BIGINT(20) NOT NULL DEFAULT 0, + PRIMARY KEY (a) +) ENGINE=MyISAM; + +INSERT INTO t1 ( a ) SELECT 0 ON DUPLICATE KEY UPDATE a = a + VALUES (a) ; + +DROP TABLE t1; + # End of 4.1 tests diff --git a/sql/sql_class.h b/sql/sql_class.h index b6bf0dcdc45..bc651b32d94 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -1236,19 +1236,27 @@ class select_insert :public select_result_interceptor { List<Item> *fields; ulonglong last_insert_id; COPY_INFO info; + TABLE_LIST *insert_table_list; + TABLE_LIST *dup_table_list; select_insert(TABLE *table_par, List<Item> *fields_par, enum_duplicates duplic, bool ignore) - :table(table_par), fields(fields_par), last_insert_id(0) + :table(table_par), fields(fields_par), last_insert_id(0), + insert_table_list(0), dup_table_list(0) { bzero((char*) &info,sizeof(info)); info.ignore= ignore; info.handle_duplicates=duplic; } - select_insert(TABLE *table_par, List<Item> *fields_par, + select_insert(TABLE *table_par, + TABLE_LIST *insert_table_list_par, + TABLE_LIST *dup_table_list_par, + List<Item> *fields_par, List<Item> *update_fields, List<Item> *update_values, enum_duplicates duplic, bool ignore) - :table(table_par), fields(fields_par), last_insert_id(0) + :table(table_par), fields(fields_par), last_insert_id(0), + insert_table_list(insert_table_list_par), + dup_table_list(dup_table_list_par) { bzero((char*) &info,sizeof(info)); info.ignore= ignore; diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 7d613ad6fbf..8c6fed26f8e 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -543,18 +543,22 @@ int mysql_prepare_insert(THD *thd, TABLE_LIST *table_list, if (!table->insert_values) DBUG_RETURN(-1); } - if ((values && check_insert_fields(thd, table, fields, *values)) || - setup_tables(insert_table_list) || - (values && setup_fields(thd, 0, insert_table_list, *values, 0, 0, 0)) || - (duplic == DUP_UPDATE && - (check_update_fields(thd, table, insert_table_list, update_fields) || - setup_fields(thd, 0, dup_table_list, update_values, 1, 0, 0)))) + if (setup_tables(insert_table_list)) DBUG_RETURN(-1); - if (values && find_real_table_in_list(table_list->next, table_list->db, - table_list->real_name)) + if (values) { - my_error(ER_UPDATE_TABLE_USED, MYF(0), table_list->real_name); - DBUG_RETURN(-1); + if (check_insert_fields(thd, table, fields, *values) || + setup_fields(thd, 0, insert_table_list, *values, 0, 0, 0) || + (duplic == DUP_UPDATE && + (check_update_fields(thd, table, insert_table_list, update_fields) || + setup_fields(thd, 0, dup_table_list, update_values, 1, 0, 0)))) + DBUG_RETURN(-1); + if (find_real_table_in_list(table_list->next, table_list->db, + table_list->real_name)) + { + my_error(ER_UPDATE_TABLE_USED, MYF(0), table_list->real_name); + DBUG_RETURN(-1); + } } if (duplic == DUP_UPDATE || duplic == DUP_REPLACE) table->file->extra(HA_EXTRA_RETRIEVE_PRIMARY_KEY); @@ -1601,6 +1605,7 @@ select_insert::prepare(List<Item> &values, SELECT_LEX_UNIT *u) int res; LEX *lex= thd->lex; SELECT_LEX *lex_current_select_save= lex->current_select; + bool lex_select_no_error= lex->select_lex.no_error; DBUG_ENTER("select_insert::prepare"); unit= u; @@ -1608,10 +1613,19 @@ select_insert::prepare(List<Item> &values, SELECT_LEX_UNIT *u) Since table in which we are going to insert is added to the first select, LEX::current_select should point to the first select while we are fixing fields from insert list. + Since these checks may cause the query to fail, we don't want the + error messages to be converted into warnings, must force no_error=0 */ lex->current_select= &lex->select_lex; - res= check_insert_fields(thd, table, *fields, values); + lex->select_lex.no_error= 0; + res= + check_insert_fields(thd, table, *fields, values) || + setup_fields(thd, 0, insert_table_list, values, 0, 0, 0) || + (info.handle_duplicates == DUP_UPDATE && + (check_update_fields(thd, table, insert_table_list, *info.update_fields) || + setup_fields(thd, 0, dup_table_list, *info.update_values, 1, 0, 0))); lex->current_select= lex_current_select_save; + lex->select_lex.no_error= lex_select_no_error; if (res) DBUG_RETURN(1); diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index c32cbff0f5e..9fb431df318 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -2877,7 +2877,8 @@ unsent_create_error: lex->field_list, 0, lex->update_list, lex->value_list, lex->duplicates)) && - (result= new select_insert(insert_table, &lex->field_list, + (result= new select_insert(insert_table, first_local_table, + &dup_tables, &lex->field_list, &lex->update_list, &lex->value_list, lex->duplicates, lex->ignore))) { |