diff options
author | unknown <acurtis@pcgem.rdg.cyberkinetica.com> | 2004-12-13 12:26:28 +0000 |
---|---|---|
committer | unknown <acurtis@pcgem.rdg.cyberkinetica.com> | 2004-12-13 12:26:28 +0000 |
commit | 46364ddb1978802ad9bf5418738b03d0cfe8bd61 (patch) | |
tree | 74dc7fe4fb76d0fd7e5bf58eca907c162b9d3b30 | |
parent | 0a3590f6d08342512deecfe15e91424eb01c0be4 (diff) | |
download | mariadb-git-46364ddb1978802ad9bf5418738b03d0cfe8bd61.tar.gz |
WL#2274 - INSERT..SELECT..UPDATE
UPDATE clause conflicts with SELECT for use of item_list field.
Alter UPDATE clause to use new lex field update_list
Tests included
mysql-test/r/insert_update.result:
WL#2274
New tests for INSERT..SELECT..UPDATE
mysql-test/t/insert_update.test:
WL#2274
New tests for INSERT..SELECT..UPDATE
sql/mysql_priv.h:
Remove function - insert_select_precheck()
sql/sql_class.h:
WL#2274
New constructor for class select_insert
sql/sql_insert.cc:
WL#2274
Move code into mysql_prepare_insert
Add checks as param values may be NULL
sql/sql_lex.cc:
WL#2274
initialize lex->update_list
sql/sql_lex.h:
WL#2274
New field in LEX: update_list
sql/sql_parse.cc:
WL#2274
INSERT..UPDATE clause now populates lex->update_list
Remove redundant function: insert_select_precheck()
sql/sql_prepare.cc:
WL#2274
invoke insert_precheck() instead of insert_select_precheck()
sql/sql_yacc.yy:
WL#2274
Enable INSERT..SELECT..UPDATE syntax
New rule - insert_update_list, to populate lex->update_list
-rw-r--r-- | mysql-test/r/insert_update.result | 62 | ||||
-rw-r--r-- | mysql-test/t/insert_update.test | 31 | ||||
-rw-r--r-- | sql/mysql_priv.h | 1 | ||||
-rw-r--r-- | sql/sql_class.h | 10 | ||||
-rw-r--r-- | sql/sql_insert.cc | 27 | ||||
-rw-r--r-- | sql/sql_lex.cc | 1 | ||||
-rw-r--r-- | sql/sql_lex.h | 2 | ||||
-rw-r--r-- | sql/sql_parse.cc | 45 | ||||
-rw-r--r-- | sql/sql_prepare.cc | 2 | ||||
-rw-r--r-- | sql/sql_yacc.yy | 45 |
10 files changed, 157 insertions, 69 deletions
diff --git a/mysql-test/r/insert_update.result b/mysql-test/r/insert_update.result index 303d7186015..753dc2cd749 100644 --- a/mysql-test/r/insert_update.result +++ b/mysql-test/r/insert_update.result @@ -105,3 +105,65 @@ a b 8 28 9 29 drop table t1; +CREATE TABLE t1 (a INT, b INT, c INT, UNIQUE (A), UNIQUE(B)); +INSERT t1 VALUES (1,2,10), (3,4,20); +INSERT t1 SELECT 5,6,30 FROM DUAL ON DUPLICATE KEY UPDATE c=c+100; +SELECT * FROM t1; +a b c +1 2 10 +3 4 20 +5 6 30 +INSERT t1 SELECT 5,7,40 FROM DUAL ON DUPLICATE KEY UPDATE c=c+100; +SELECT * FROM t1; +a b c +1 2 10 +3 4 20 +5 6 130 +INSERT t1 SELECT 8,4,50 FROM DUAL ON DUPLICATE KEY UPDATE c=c+1000; +SELECT * FROM t1; +a b c +1 2 10 +3 4 1020 +5 6 130 +INSERT t1 SELECT 1,4,60 FROM DUAL ON DUPLICATE KEY UPDATE c=c+10000; +SELECT * FROM t1; +a b c +1 2 10010 +3 4 1020 +5 6 130 +INSERT t1 SELECT 1,9,70 FROM DUAL ON DUPLICATE KEY UPDATE c=c+100000, b=4; +ERROR 23000: Duplicate entry '4' for key 2 +SELECT * FROM t1; +a b c +1 2 10010 +3 4 1020 +5 6 130 +TRUNCATE TABLE t1; +INSERT t1 VALUES (1,2,10), (3,4,20); +CREATE TABLE t2 (x INT, y INT, z INT, d INT); +INSERT t2 VALUES (5,6,30,1), (7,4,40,1), (8,9,60,1); +INSERT t2 VALUES (2,1,11,2), (7,4,40,2); +INSERT t1 SELECT x,y,z FROM t2 WHERE d=1 ON DUPLICATE KEY UPDATE c=c+100; +SELECT * FROM t1; +a b c +1 2 10 +3 4 120 +5 6 30 +8 9 60 +INSERT t1 SET a=5 ON DUPLICATE KEY UPDATE b=0; +SELECT * FROM t1; +a b c +1 2 10 +3 4 120 +5 0 30 +8 9 60 +INSERT t1 SELECT x,y,z FROM t2 WHERE d=2 ON DUPLICATE KEY UPDATE c=c+VALUES(a); +SELECT *, VALUES(a) FROM t1; +a b c VALUES(a) +1 2 10 NULL +3 4 127 NULL +5 0 30 NULL +8 9 60 NULL +2 1 11 NULL +DROP TABLE t1; +DROP TABLE t2; diff --git a/mysql-test/t/insert_update.test b/mysql-test/t/insert_update.test index fc54ce88f8a..182baa641da 100644 --- a/mysql-test/t/insert_update.test +++ b/mysql-test/t/insert_update.test @@ -48,3 +48,34 @@ disable_info; select * from t1; drop table t1; + +# WorkLog #2274 - enable INSERT .. SELECT .. UPDATE syntax +# Same tests as beginning of this test except that insert source +# is a result from a select statement +# +CREATE TABLE t1 (a INT, b INT, c INT, UNIQUE (A), UNIQUE(B)); +INSERT t1 VALUES (1,2,10), (3,4,20); +INSERT t1 SELECT 5,6,30 FROM DUAL ON DUPLICATE KEY UPDATE c=c+100; +SELECT * FROM t1; +INSERT t1 SELECT 5,7,40 FROM DUAL ON DUPLICATE KEY UPDATE c=c+100; +SELECT * FROM t1; +INSERT t1 SELECT 8,4,50 FROM DUAL ON DUPLICATE KEY UPDATE c=c+1000; +SELECT * FROM t1; +INSERT t1 SELECT 1,4,60 FROM DUAL ON DUPLICATE KEY UPDATE c=c+10000; +SELECT * FROM t1; +-- error 1062 +INSERT t1 SELECT 1,9,70 FROM DUAL ON DUPLICATE KEY UPDATE c=c+100000, b=4; +SELECT * FROM t1; +TRUNCATE TABLE t1; +INSERT t1 VALUES (1,2,10), (3,4,20); +CREATE TABLE t2 (x INT, y INT, z INT, d INT); +INSERT t2 VALUES (5,6,30,1), (7,4,40,1), (8,9,60,1); +INSERT t2 VALUES (2,1,11,2), (7,4,40,2); +INSERT t1 SELECT x,y,z FROM t2 WHERE d=1 ON DUPLICATE KEY UPDATE c=c+100; +SELECT * FROM t1; +INSERT t1 SET a=5 ON DUPLICATE KEY UPDATE b=0; +SELECT * FROM t1; +INSERT t1 SELECT x,y,z FROM t2 WHERE d=2 ON DUPLICATE KEY UPDATE c=c+VALUES(a); +SELECT *, VALUES(a) FROM t1; +DROP TABLE t1; +DROP TABLE t2; diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 8350122c4e2..c90935f4cf9 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -362,7 +362,6 @@ bool check_merge_table_access(THD *thd, char *db, TABLE_LIST *table_list); int multi_update_precheck(THD *thd, TABLE_LIST *tables); int multi_delete_precheck(THD *thd, TABLE_LIST *tables, uint *table_count); -int insert_select_precheck(THD *thd, TABLE_LIST *tables); int update_precheck(THD *thd, TABLE_LIST *tables); int delete_precheck(THD *thd, TABLE_LIST *tables); int insert_precheck(THD *thd, TABLE_LIST *tables); diff --git a/sql/sql_class.h b/sql/sql_class.h index 419c087afbc..169835f3324 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -1238,6 +1238,16 @@ class select_insert :public select_result_interceptor { bzero((char*) &info,sizeof(info)); info.handle_duplicates=duplic; } + select_insert(TABLE *table_par, List<Item> *fields_par, + List<Item> *update_fields, List<Item> *update_values, + enum_duplicates duplic) + :table(table_par), fields(fields_par), last_insert_id(0) + { + bzero((char*) &info,sizeof(info)); + info.handle_duplicates=duplic; + info.update_fields= update_fields; + info.update_values= update_values; + } ~select_insert(); int prepare(List<Item> &list, SELECT_LEX_UNIT *u); bool send_data(List<Item> &items); diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 768acb0cf9e..ce64890523a 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -197,15 +197,6 @@ int mysql_insert(THD *thd,TABLE_LIST *table_list, thd->used_tables=0; values= its++; - if (duplic == DUP_UPDATE && !table->insert_values) - { - /* it should be allocated before Item::fix_fields() */ - table->insert_values= - (byte *)alloc_root(thd->mem_root, table->rec_buff_length); - if (!table->insert_values) - goto abort; - } - if (mysql_prepare_insert(thd, table_list, insert_table_list, table, fields, values, update_fields, update_values, duplic)) @@ -448,14 +439,24 @@ int mysql_prepare_insert(THD *thd, TABLE_LIST *table_list, enum_duplicates duplic) { DBUG_ENTER("mysql_prepare_insert"); - if (check_insert_fields(thd, table, fields, *values, 1) || + if (duplic == DUP_UPDATE && !table->insert_values) + { + /* it should be allocated before Item::fix_fields() */ + table->insert_values= + (byte *)alloc_root(thd->mem_root, table->rec_buff_length); + if (!table->insert_values) + DBUG_RETURN(-1); + } + if ((values && check_insert_fields(thd, table, fields, *values, 1)) || setup_tables(insert_table_list) || - setup_fields(thd, 0, insert_table_list, *values, 0, 0, 0) || + (values && setup_fields(thd, 0, insert_table_list, *values, 0, 0, 0)) || (duplic == DUP_UPDATE && (setup_fields(thd, 0, insert_table_list, update_fields, 1, 0, 0) || setup_fields(thd, 0, insert_table_list, update_values, 1, 0, 0)))) DBUG_RETURN(-1); - if (find_real_table_in_list(table_list->next, + if ((thd->lex->sql_command==SQLCOM_INSERT || + thd->lex->sql_command==SQLCOM_REPLACE) && + 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); @@ -550,8 +551,10 @@ int write_record(TABLE *table,COPY_INFO *info) that matches, is updated. If update causes a conflict again, an error is returned */ + DBUG_ASSERT(table->insert_values != NULL); store_record(table,insert_values); restore_record(table,record[1]); + DBUG_ASSERT(info->update_fields->elements==info->update_values->elements); if (fill_record(*info->update_fields, *info->update_values, 0)) goto err; if ((error=table->file->update_row(table->record[1],table->record[0]))) diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 1d9afcc94a4..d2ac0df1472 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -123,6 +123,7 @@ void lex_start(THD *thd, uchar *buf,uint length) lex->unit.thd= thd; lex->select_lex.init_query(); lex->value_list.empty(); + lex->update_list.empty(); lex->param_list.empty(); lex->unit.next= lex->unit.master= lex->unit.link_next= lex->unit.return_to= 0; diff --git a/sql/sql_lex.h b/sql/sql_lex.h index b055a022eb4..8421be7e735 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -587,7 +587,7 @@ typedef struct st_lex List<LEX_COLUMN> columns; List<Key> key_list; List<create_field> create_list; - List<Item> *insert_list,field_list,value_list; + List<Item> *insert_list,field_list,value_list,update_list; List<List_item> many_values; List<set_var_base> var_list; List<Item_param> param_list; diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index e73c3d95b42..8dc1339993e 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -2717,7 +2717,7 @@ unsent_create_error: if ((res= insert_precheck(thd, tables))) break; res = mysql_insert(thd,tables,lex->field_list,lex->many_values, - select_lex->item_list, lex->value_list, + lex->update_list, lex->value_list, lex->duplicates); if (thd->net.report_error) res= -1; @@ -2727,7 +2727,7 @@ unsent_create_error: case SQLCOM_INSERT_SELECT: { TABLE_LIST *first_local_table= (TABLE_LIST *) select_lex->table_list.first; - if ((res= insert_select_precheck(thd, tables))) + if ((res= insert_precheck(thd, tables))) break; /* Fix lock for first table */ @@ -2749,11 +2749,16 @@ unsent_create_error: select_lex->options |= OPTION_BUFFER_RESULT; } - if (!(res= open_and_lock_tables(thd, tables)) && + !(res= mysql_prepare_insert(thd, tables, first_local_table, + tables->table, lex->field_list, 0, + lex->update_list, lex->value_list, + lex->duplicates)) && (result= new select_insert(tables->table, &lex->field_list, + &lex->update_list, &lex->value_list, lex->duplicates))) { + TABLE *table= tables->table; /* Skip first table, which is the table we are inserting in */ lex->select_lex.table_list.first= (byte*) first_local_table->next; /* @@ -2766,6 +2771,7 @@ unsent_create_error: lex->select_lex.table_list.first= (byte*) first_local_table; lex->select_lex.resolve_mode= SELECT_LEX::INSERT_MODE; delete result; + table->insert_values= 0; if (thd->net.report_error) res= -1; } @@ -5305,33 +5311,6 @@ int multi_delete_precheck(THD *thd, TABLE_LIST *tables, uint *table_count) /* - INSERT ... SELECT query pre-check - - SYNOPSIS - insert_delete_precheck() - thd Thread handler - tables Global table list - - RETURN VALUE - 0 OK - 1 Error (message is sent to user) - -1 Error (message is not sent to user) -*/ - -int insert_select_precheck(THD *thd, TABLE_LIST *tables) -{ - DBUG_ENTER("insert_select_precheck"); - /* - Check that we have modify privileges for the first table and - select privileges for the rest - */ - ulong privilege= (thd->lex->duplicates == DUP_REPLACE ? - INSERT_ACL | DELETE_ACL : INSERT_ACL); - DBUG_RETURN(check_one_table_access(thd, privilege, tables) ? 1 : 0); -} - - -/* simple UPDATE query pre-check SYNOPSIS @@ -5402,6 +5381,10 @@ int insert_precheck(THD *thd, TABLE_LIST *tables) LEX *lex= thd->lex; DBUG_ENTER("insert_precheck"); + /* + Check that we have modify privileges for the first table and + select privileges for the rest + */ ulong privilege= INSERT_ACL | (lex->duplicates == DUP_REPLACE ? DELETE_ACL : 0) | (lex->duplicates == DUP_UPDATE ? UPDATE_ACL : 0); @@ -5409,7 +5392,7 @@ int insert_precheck(THD *thd, TABLE_LIST *tables) if (check_one_table_access(thd, privilege, tables)) DBUG_RETURN(1); - if (lex->select_lex.item_list.elements != lex->value_list.elements) + if (lex->update_list.elements != lex->value_list.elements) { my_error(ER_WRONG_VALUE_COUNT, MYF(0)); DBUG_RETURN(-1); diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index bcb9d18d827..f4a96d751cd 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -1343,7 +1343,7 @@ static int mysql_test_insert_select(Prepared_statement *stmt, { int res; LEX *lex= stmt->lex; - if ((res= insert_select_precheck(stmt->thd, tables))) + if ((res= insert_precheck(stmt->thd, tables))) return res; TABLE_LIST *first_local_table= (TABLE_LIST *)lex->select_lex.table_list.first; diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 1b629e72ecc..a09694ee1e6 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -4161,21 +4161,8 @@ expr_or_default: opt_insert_update: /* empty */ - | ON DUPLICATE_SYM - { - LEX *lex= Lex; - /* - For simplicity, let's forget about INSERT ... SELECT ... UPDATE - for a moment. - */ - if (lex->sql_command != SQLCOM_INSERT) - { - yyerror(ER(ER_SYNTAX_ERROR)); - YYABORT; - } - lex->duplicates= DUP_UPDATE; - } - KEY_SYM UPDATE_SYM update_list + | ON DUPLICATE_SYM { Lex->duplicates= DUP_UPDATE; } + KEY_SYM UPDATE_SYM insert_update_list ; /* Update rows in a table */ @@ -4211,16 +4198,28 @@ update: ; update_list: - update_list ',' simple_ident equal expr_or_default + update_list ',' update_elem + | update_elem; + +update_elem: + simple_ident equal expr_or_default { - if (add_item_to_list(YYTHD, $3) || add_value_to_list(YYTHD, $5)) + if (add_item_to_list(YYTHD, $1) || add_value_to_list(YYTHD, $3)) YYABORT; - } - | simple_ident equal expr_or_default - { - if (add_item_to_list(YYTHD, $1) || add_value_to_list(YYTHD, $3)) - YYABORT; - }; + }; + +insert_update_list: + insert_update_list ',' insert_update_elem + | insert_update_elem; + +insert_update_elem: + simple_ident equal expr_or_default + { + LEX *lex= Lex; + if (lex->update_list.push_back($1) || + lex->value_list.push_back($3)) + YYABORT; + }; opt_low_priority: /* empty */ { $$= YYTHD->update_lock_default; } |