summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorunknown <bell@sanja.is.com.ua>2004-09-08 13:39:15 +0300
committerunknown <bell@sanja.is.com.ua>2004-09-08 13:39:15 +0300
commit6c2330407f99b0bab1db68ec38789fc10a3518ec (patch)
tree42e71f47652f97532ac4741a570233064060593d
parenteb72c28b9b8e7fe785e918cc1c366b4fce6fa8f6 (diff)
downloadmariadb-git-6c2330407f99b0bab1db68ec38789fc10a3518ec.tar.gz
check that table used in multi-update is unique added (BUG#5455)
mysql-test/r/multi_update.result: multi* unique updating table check mysql-test/t/multi_update.test: multi* unique updating table check sql/sql_lex.cc: new method to check table only in subqueries sql/sql_lex.h: new method to check table only in subqueries sql/sql_parse.cc: used new method to check table only in subqueries sql/sql_update.cc: check that table is unique added
-rw-r--r--mysql-test/r/multi_update.result7
-rw-r--r--mysql-test/t/multi_update.test11
-rw-r--r--sql/sql_lex.cc22
-rw-r--r--sql/sql_lex.h1
-rw-r--r--sql/sql_parse.cc23
-rw-r--r--sql/sql_update.cc26
6 files changed, 70 insertions, 20 deletions
diff --git a/mysql-test/r/multi_update.result b/mysql-test/r/multi_update.result
index 39ec9ff4eb9..780ceb36ad6 100644
--- a/mysql-test/r/multi_update.result
+++ b/mysql-test/r/multi_update.result
@@ -455,3 +455,10 @@ create table t3 (a int, primary key (a));
delete t1,t3 from t1,t2 where t1.a=t2.a and t2.a=(select t3.a from t3 where t1.a=t3.a);
ERROR 42S02: Unknown table 't3' in MULTI DELETE
drop table t1, t2, t3;
+create table t1 (col1 int);
+create table t2 (col1 int);
+update t1,t2 set t1.col1 = (select max(col1) from t1) where t1.col1 = t2.col1;
+ERROR HY000: You can't specify target table 't1' for update in FROM clause
+delete t1 from t1,t2 where t1.col1 < (select max(col1) from t1) and t1.col1 = t2.col1;
+ERROR HY000: You can't specify target table 't1' for update in FROM clause
+drop table t1,t2;
diff --git a/mysql-test/t/multi_update.test b/mysql-test/t/multi_update.test
index c2814606aa2..40e742679f8 100644
--- a/mysql-test/t/multi_update.test
+++ b/mysql-test/t/multi_update.test
@@ -417,3 +417,14 @@ create table t3 (a int, primary key (a));
-- error 1109
delete t1,t3 from t1,t2 where t1.a=t2.a and t2.a=(select t3.a from t3 where t1.a=t3.a);
drop table t1, t2, t3;
+
+#
+# multi* unique updating table check
+#
+create table t1 (col1 int);
+create table t2 (col1 int);
+-- error 1093
+update t1,t2 set t1.col1 = (select max(col1) from t1) where t1.col1 = t2.col1;
+-- error 1093
+delete t1 from t1,t2 where t1.col1 < (select max(col1) from t1) and t1.col1 = t2.col1;
+drop table t1,t2;
diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc
index fab047002ad..36af3003487 100644
--- a/sql/sql_lex.cc
+++ b/sql/sql_lex.cc
@@ -1548,6 +1548,7 @@ bool st_select_lex::setup_ref_array(THD *thd, uint order_group_num)
1 - found
0 - OK (table did not found)
*/
+
bool st_select_lex_unit::check_updateable(char *db, char *table)
{
for (SELECT_LEX *sl= first_select(); sl; sl= sl->next_select())
@@ -1559,7 +1560,7 @@ bool st_select_lex_unit::check_updateable(char *db, char *table)
/*
Find db.table which will be updated in this select and
- underlayed ones (except derived tables)
+ underlaying ones (except derived tables)
SYNOPSIS
st_select_lex::check_updateable()
@@ -1570,11 +1571,30 @@ bool st_select_lex_unit::check_updateable(char *db, char *table)
1 - found
0 - OK (table did not found)
*/
+
bool st_select_lex::check_updateable(char *db, char *table)
{
if (find_real_table_in_list(get_table_list(), db, table))
return 1;
+ return check_updateable_in_subqueries(db, table);
+}
+
+/*
+ Find db.table which will be updated in underlaying subqueries
+
+ SYNOPSIS
+ st_select_lex::check_updateable_in_subqueries()
+ db - data base name
+ table - real table name
+
+ RETURN
+ 1 - found
+ 0 - OK (table did not found)
+*/
+
+bool st_select_lex::check_updateable_in_subqueries(char *db, char *table)
+{
for (SELECT_LEX_UNIT *un= first_inner_unit();
un;
un= un->next_unit())
diff --git a/sql/sql_lex.h b/sql/sql_lex.h
index 7342902c086..dffe7bcb2b0 100644
--- a/sql/sql_lex.h
+++ b/sql/sql_lex.h
@@ -517,6 +517,7 @@ public:
}
bool setup_ref_array(THD *thd, uint order_group_num);
bool check_updateable(char *db, char *table);
+ bool check_updateable_in_subqueries(char *db, char *table);
void print(THD *thd, String *str);
static void print_order(String *str, ORDER *order);
void print_limit(THD *thd, String *str);
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index 39b94362269..dd57f37473c 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -2796,24 +2796,19 @@ unsent_create_error:
target_tbl;
target_tbl= target_tbl->next)
{
- target_tbl->table= target_tbl->table_list->table;
- /*
+ TABLE_LIST *orig= target_tbl->table_list;
+ target_tbl->table= orig->table;
+ /*
Multi-delete can't be constructed over-union => we always have
single SELECT on top and have to check underlaying SELECTs of it
*/
- for (SELECT_LEX_UNIT *un= lex->select_lex.first_inner_unit();
- un;
- un= un->next_unit())
+ if (lex->select_lex.check_updateable_in_subqueries(orig->db,
+ orig->real_name))
{
- if (un->first_select()->linkage != DERIVED_TABLE_TYPE &&
- un->check_updateable(target_tbl->table_list->db,
- target_tbl->table_list->real_name))
- {
- my_error(ER_UPDATE_TABLE_USED, MYF(0),
- target_tbl->table_list->real_name);
- res= -1;
- break;
- }
+ my_error(ER_UPDATE_TABLE_USED, MYF(0),
+ orig->real_name);
+ res= -1;
+ break;
}
}
diff --git a/sql/sql_update.cc b/sql/sql_update.cc
index db4edff4fa1..b6cd0d967e9 100644
--- a/sql/sql_update.cc
+++ b/sql/sql_update.cc
@@ -490,9 +490,8 @@ int mysql_multi_update(THD *thd,
table->grant.want_privilege= (UPDATE_ACL & ~table->grant.privilege);
}
- if (thd->lex->derived_tables)
+ /* Assign table map values to check updatability of derived tables */
{
- // Assign table map values to check updatability of derived tables
uint tablenr=0;
for (TABLE_LIST *table_list= update_list;
table_list;
@@ -501,11 +500,12 @@ int mysql_multi_update(THD *thd,
table_list->table->map= (table_map) 1 << tablenr;
}
}
+
if (setup_fields(thd, 0, update_list, *fields, 1, 0, 0))
DBUG_RETURN(-1);
- if (thd->lex->derived_tables)
+
+ /* Find tables used in items */
{
- // Find tables used in items
List_iterator_fast<Item> it(*fields);
Item *item;
while ((item= it++))
@@ -527,7 +527,23 @@ int mysql_multi_update(THD *thd,
if (table->timestamp_field &&
table->timestamp_field->query_id == thd->query_id)
table->timestamp_on_update_now= 0;
-
+
+ /* if table will be updated then check that it is unique */
+ if (table->map & item_tables)
+ {
+ /*
+ Multi-update can't be constructed over-union => we always have
+ single SELECT on top and have to check underlaying SELECTs of it
+ */
+ if (select_lex->check_updateable_in_subqueries(tl->db,
+ tl->real_name))
+ {
+ my_error(ER_UPDATE_TABLE_USED, MYF(0),
+ tl->real_name);
+ DBUG_RETURN(-1);
+ }
+ }
+
if (tl->derived)
derived_tables|= table->map;
}