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
parent 5b0e3b13
...@@ -105,3 +105,65 @@ a b ...@@ -105,3 +105,65 @@ a b
8 28 8 28
9 29 9 29
drop table t1; 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;
...@@ -48,3 +48,34 @@ disable_info; ...@@ -48,3 +48,34 @@ disable_info;
select * from t1; select * from t1;
drop table 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;
...@@ -362,7 +362,6 @@ bool check_merge_table_access(THD *thd, char *db, ...@@ -362,7 +362,6 @@ bool check_merge_table_access(THD *thd, char *db,
TABLE_LIST *table_list); TABLE_LIST *table_list);
int multi_update_precheck(THD *thd, TABLE_LIST *tables); int multi_update_precheck(THD *thd, TABLE_LIST *tables);
int multi_delete_precheck(THD *thd, TABLE_LIST *tables, uint *table_count); 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 update_precheck(THD *thd, TABLE_LIST *tables);
int delete_precheck(THD *thd, TABLE_LIST *tables); int delete_precheck(THD *thd, TABLE_LIST *tables);
int insert_precheck(THD *thd, TABLE_LIST *tables); int insert_precheck(THD *thd, TABLE_LIST *tables);
......
...@@ -1238,6 +1238,16 @@ class select_insert :public select_result_interceptor { ...@@ -1238,6 +1238,16 @@ class select_insert :public select_result_interceptor {
bzero((char*) &info,sizeof(info)); bzero((char*) &info,sizeof(info));
info.handle_duplicates=duplic; 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(); ~select_insert();
int prepare(List<Item> &list, SELECT_LEX_UNIT *u); int prepare(List<Item> &list, SELECT_LEX_UNIT *u);
bool send_data(List<Item> &items); bool send_data(List<Item> &items);
......
...@@ -197,15 +197,6 @@ int mysql_insert(THD *thd,TABLE_LIST *table_list, ...@@ -197,15 +197,6 @@ int mysql_insert(THD *thd,TABLE_LIST *table_list,
thd->used_tables=0; thd->used_tables=0;
values= its++; 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, if (mysql_prepare_insert(thd, table_list, insert_table_list, table,
fields, values, update_fields, fields, values, update_fields,
update_values, duplic)) update_values, duplic))
...@@ -448,14 +439,24 @@ int mysql_prepare_insert(THD *thd, TABLE_LIST *table_list, ...@@ -448,14 +439,24 @@ int mysql_prepare_insert(THD *thd, TABLE_LIST *table_list,
enum_duplicates duplic) enum_duplicates duplic)
{ {
DBUG_ENTER("mysql_prepare_insert"); 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_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 && (duplic == DUP_UPDATE &&
(setup_fields(thd, 0, insert_table_list, update_fields, 1, 0, 0) || (setup_fields(thd, 0, insert_table_list, update_fields, 1, 0, 0) ||
setup_fields(thd, 0, insert_table_list, update_values, 1, 0, 0)))) setup_fields(thd, 0, insert_table_list, update_values, 1, 0, 0))))
DBUG_RETURN(-1); 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)) table_list->db, table_list->real_name))
{ {
my_error(ER_UPDATE_TABLE_USED, MYF(0), 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) ...@@ -550,8 +551,10 @@ int write_record(TABLE *table,COPY_INFO *info)
that matches, is updated. If update causes a conflict again, that matches, is updated. If update causes a conflict again,
an error is returned an error is returned
*/ */
DBUG_ASSERT(table->insert_values != NULL);
store_record(table,insert_values); store_record(table,insert_values);
restore_record(table,record[1]); 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)) if (fill_record(*info->update_fields, *info->update_values, 0))
goto err; goto err;
if ((error=table->file->update_row(table->record[1],table->record[0]))) if ((error=table->file->update_row(table->record[1],table->record[0])))
......
...@@ -123,6 +123,7 @@ void lex_start(THD *thd, uchar *buf,uint length) ...@@ -123,6 +123,7 @@ void lex_start(THD *thd, uchar *buf,uint length)
lex->unit.thd= thd; lex->unit.thd= thd;
lex->select_lex.init_query(); lex->select_lex.init_query();
lex->value_list.empty(); lex->value_list.empty();
lex->update_list.empty();
lex->param_list.empty(); lex->param_list.empty();
lex->unit.next= lex->unit.master= lex->unit.next= lex->unit.master=
lex->unit.link_next= lex->unit.return_to= 0; lex->unit.link_next= lex->unit.return_to= 0;
......
...@@ -587,7 +587,7 @@ typedef struct st_lex ...@@ -587,7 +587,7 @@ typedef struct st_lex
List<LEX_COLUMN> columns; List<LEX_COLUMN> columns;
List<Key> key_list; List<Key> key_list;
List<create_field> create_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<List_item> many_values;
List<set_var_base> var_list; List<set_var_base> var_list;
List<Item_param> param_list; List<Item_param> param_list;
......
...@@ -2717,7 +2717,7 @@ unsent_create_error: ...@@ -2717,7 +2717,7 @@ unsent_create_error:
if ((res= insert_precheck(thd, tables))) if ((res= insert_precheck(thd, tables)))
break; break;
res = mysql_insert(thd,tables,lex->field_list,lex->many_values, 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); lex->duplicates);
if (thd->net.report_error) if (thd->net.report_error)
res= -1; res= -1;
...@@ -2727,7 +2727,7 @@ unsent_create_error: ...@@ -2727,7 +2727,7 @@ unsent_create_error:
case SQLCOM_INSERT_SELECT: case SQLCOM_INSERT_SELECT:
{ {
TABLE_LIST *first_local_table= (TABLE_LIST *) select_lex->table_list.first; 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; break;
/* Fix lock for first table */ /* Fix lock for first table */
...@@ -2749,11 +2749,16 @@ unsent_create_error: ...@@ -2749,11 +2749,16 @@ unsent_create_error:
select_lex->options |= OPTION_BUFFER_RESULT; select_lex->options |= OPTION_BUFFER_RESULT;
} }
if (!(res= open_and_lock_tables(thd, tables)) && 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, (result= new select_insert(tables->table, &lex->field_list,
&lex->update_list, &lex->value_list,
lex->duplicates))) lex->duplicates)))
{ {
TABLE *table= tables->table;
/* Skip first table, which is the table we are inserting in */ /* Skip first table, which is the table we are inserting in */
lex->select_lex.table_list.first= (byte*) first_local_table->next; lex->select_lex.table_list.first= (byte*) first_local_table->next;
/* /*
...@@ -2766,6 +2771,7 @@ unsent_create_error: ...@@ -2766,6 +2771,7 @@ unsent_create_error:
lex->select_lex.table_list.first= (byte*) first_local_table; lex->select_lex.table_list.first= (byte*) first_local_table;
lex->select_lex.resolve_mode= SELECT_LEX::INSERT_MODE; lex->select_lex.resolve_mode= SELECT_LEX::INSERT_MODE;
delete result; delete result;
table->insert_values= 0;
if (thd->net.report_error) if (thd->net.report_error)
res= -1; res= -1;
} }
...@@ -5304,33 +5310,6 @@ int multi_delete_precheck(THD *thd, TABLE_LIST *tables, uint *table_count) ...@@ -5304,33 +5310,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 simple UPDATE query pre-check
...@@ -5402,6 +5381,10 @@ int insert_precheck(THD *thd, TABLE_LIST *tables) ...@@ -5402,6 +5381,10 @@ int insert_precheck(THD *thd, TABLE_LIST *tables)
LEX *lex= thd->lex; LEX *lex= thd->lex;
DBUG_ENTER("insert_precheck"); DBUG_ENTER("insert_precheck");
/*
Check that we have modify privileges for the first table and
select privileges for the rest
*/
ulong privilege= INSERT_ACL | ulong privilege= INSERT_ACL |
(lex->duplicates == DUP_REPLACE ? DELETE_ACL : 0) | (lex->duplicates == DUP_REPLACE ? DELETE_ACL : 0) |
(lex->duplicates == DUP_UPDATE ? UPDATE_ACL : 0); (lex->duplicates == DUP_UPDATE ? UPDATE_ACL : 0);
...@@ -5409,7 +5392,7 @@ int insert_precheck(THD *thd, TABLE_LIST *tables) ...@@ -5409,7 +5392,7 @@ int insert_precheck(THD *thd, TABLE_LIST *tables)
if (check_one_table_access(thd, privilege, tables)) if (check_one_table_access(thd, privilege, tables))
DBUG_RETURN(1); 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)); my_error(ER_WRONG_VALUE_COUNT, MYF(0));
DBUG_RETURN(-1); DBUG_RETURN(-1);
......
...@@ -1343,7 +1343,7 @@ static int mysql_test_insert_select(Prepared_statement *stmt, ...@@ -1343,7 +1343,7 @@ static int mysql_test_insert_select(Prepared_statement *stmt,
{ {
int res; int res;
LEX *lex= stmt->lex; LEX *lex= stmt->lex;
if ((res= insert_select_precheck(stmt->thd, tables))) if ((res= insert_precheck(stmt->thd, tables)))
return res; return res;
TABLE_LIST *first_local_table= TABLE_LIST *first_local_table=
(TABLE_LIST *)lex->select_lex.table_list.first; (TABLE_LIST *)lex->select_lex.table_list.first;
......
...@@ -4161,21 +4161,8 @@ expr_or_default: ...@@ -4161,21 +4161,8 @@ expr_or_default:
opt_insert_update: opt_insert_update:
/* empty */ /* empty */
| ON DUPLICATE_SYM | ON DUPLICATE_SYM { Lex->duplicates= DUP_UPDATE; }
{ KEY_SYM UPDATE_SYM insert_update_list
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
; ;
/* Update rows in a table */ /* Update rows in a table */
...@@ -4211,14 +4198,26 @@ update: ...@@ -4211,14 +4198,26 @@ update:
; ;
update_list: 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; YYABORT;
} };
| simple_ident equal expr_or_default
insert_update_list:
insert_update_list ',' insert_update_elem
| insert_update_elem;
insert_update_elem:
simple_ident equal expr_or_default
{ {
if (add_item_to_list(YYTHD, $1) || add_value_to_list(YYTHD, $3)) LEX *lex= Lex;
if (lex->update_list.push_back($1) ||
lex->value_list.push_back($3))
YYABORT; YYABORT;
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment