diff options
author | unknown <dlenev@brandersnatch.localdomain> | 2005-06-09 01:07:52 +0400 |
---|---|---|
committer | unknown <dlenev@brandersnatch.localdomain> | 2005-06-09 01:07:52 +0400 |
commit | 4e0bbc1b6eb6716a0774a1678be24b4a2463b8c9 (patch) | |
tree | 5f8aa229b96ac3ed689847d17b87a35fd44cff21 | |
parent | 0bc3c6221cbaae56ba444e06603c4afbf11f685e (diff) | |
download | mariadb-git-4e0bbc1b6eb6716a0774a1678be24b4a2463b8c9.tar.gz |
Fix for bug #11158 "Can't perform multi-delete in stored procedure".
In order to make multi-delete SP friendly we need to have all table
locks for the elements of main statement table list properly set
at the end of parsing.
Also performed small cleanup: We don't need relink_tables_for_multidelete()
any longer since the only case now when TABLE_LIST::correspondent_table
is non-zero are tables in auxilary table list of multi-delete and these
tables are handled specially in mysql_multi_delete_prepare().
mysql-test/r/sp-threads.result:
Added test case for bug #11158 "Can't perform multi-delete in stored
procedure".
mysql-test/t/sp-threads.test:
Added test case for bug #11158 "Can't perform multi-delete in stored
procedure".
sql/mysql_priv.h:
- Removed third argument from the declaration of multi_delete_precheck()
as nowdays we calculate number of tables in multi-delete from which
we are going to delete rows right at the end of statement parsing.
- Introduced definition of multi_delete_set_locks_and_link_aux_tables()
which is responsible for propagation of proper table locks from
multi-delete's auxilary table list to the main list and binding
corresponding tables in these two lists.
sql/sql_base.cc:
Removed relink_tables_for_multidelete() routine and its invocations.
We don't need them in 5.0 since the only case now when
TABLE_LIST::correspondent_table is non-zero are tables in auxilary table
list of multi-delete and these tables are handled specially in
mysql_multi_delete_prepare().
sql/sql_lex.h:
LEX::table_count
Added description of new role of this LEX member for multi-delete.
Now for this statement we store number of tables from which we should
delete records there.
sql/sql_parse.cc:
multi_delete_precheck():
Moved code which is responsible for iterating through auxilary table
list and binding its elements with corresponding elements of main
table list, and properly updating locks in it to separate function -
multi_delete_set_locks_and_link_aux_tables(). This is because in order
to make multi-delete SP friendly we need to have all locks set properly
at the end of statement parsing. So we are introducing new function
which will be called from parser.
We also calculate number of tables from which we are going to perform
deletions there and store this number for later usage in
LEX::table_count.
Also removed some no longer needed code.
sql/sql_prepare.cc:
mysql_test_multidelete():
Now multi_delete_precheck() takes only two arguments, so we don't
need to pass fake third parameter.
sql/sql_yacc.yy:
delete:
In order to make multi-delete SP friendly we need to have all table
locks for the elements of main statement table list properly set
at the end of parsing.
-rw-r--r-- | mysql-test/r/sp-threads.result | 15 | ||||
-rw-r--r-- | mysql-test/t/sp-threads.test | 26 | ||||
-rw-r--r-- | sql/mysql_priv.h | 3 | ||||
-rw-r--r-- | sql/sql_base.cc | 28 | ||||
-rw-r--r-- | sql/sql_lex.h | 7 | ||||
-rw-r--r-- | sql/sql_parse.cc | 49 | ||||
-rw-r--r-- | sql/sql_prepare.cc | 3 | ||||
-rw-r--r-- | sql/sql_yacc.yy | 9 |
8 files changed, 89 insertions, 51 deletions
diff --git a/mysql-test/r/sp-threads.result b/mysql-test/r/sp-threads.result index a081e520496..a9d50e6e697 100644 --- a/mysql-test/r/sp-threads.result +++ b/mysql-test/r/sp-threads.result @@ -40,3 +40,18 @@ Id User Host db Command Time State Info unlock tables; drop procedure bug9486; drop table t1, t2; +drop procedure if exists bug11158; +create procedure bug11158() delete t1 from t1, t2 where t1.id = t2.id; +create table t1 (id int, j int); +insert into t1 values (1, 1), (2, 2); +create table t2 (id int); +insert into t2 values (1); +call bug11158(); +select * from t1; +id j +2 2 +lock tables t2 read; +call bug11158(); +unlock tables; +drop procedure bug11158; +drop table t1, t2; diff --git a/mysql-test/t/sp-threads.test b/mysql-test/t/sp-threads.test index 608ac3e2ee7..8fec5d14bc1 100644 --- a/mysql-test/t/sp-threads.test +++ b/mysql-test/t/sp-threads.test @@ -84,6 +84,32 @@ reap; drop procedure bug9486; drop table t1, t2; +# +# BUG#11158: Can't perform multi-delete in stored procedure +# +--disable_warnings +drop procedure if exists bug11158; +--enable_warnings +create procedure bug11158() delete t1 from t1, t2 where t1.id = t2.id; +create table t1 (id int, j int); +insert into t1 values (1, 1), (2, 2); +create table t2 (id int); +insert into t2 values (1); +# Procedure should work and cause proper effect (delete only first row) +call bug11158(); +select * from t1; +# Also let us test that we obtain only read (and thus non exclusive) lock +# for table from which we are not going to delete rows. +connection con2root; +lock tables t2 read; +connection con1root; +call bug11158(); +connection con2root; +unlock tables; +connection con1root; +# Clean-up +drop procedure bug11158; +drop table t1, t2; # # BUG#NNNN: New bug synopsis diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index e8ec1b69959..58ac3f96833 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -480,7 +480,7 @@ bool check_merge_table_access(THD *thd, char *db, TABLE_LIST *table_list); bool check_some_routine_access(THD *thd, const char *db, const char *name, bool is_proc); bool multi_update_precheck(THD *thd, TABLE_LIST *tables); -bool multi_delete_precheck(THD *thd, TABLE_LIST *tables, uint *table_count); +bool multi_delete_precheck(THD *thd, TABLE_LIST *tables); bool mysql_multi_update_prepare(THD *thd); bool mysql_multi_delete_prepare(THD *thd); bool mysql_insert_select_prepare(THD *thd); @@ -575,6 +575,7 @@ void mysql_init_query(THD *thd, uchar *buf, uint length); bool mysql_new_select(LEX *lex, bool move_down); void create_select_for_variable(const char *var_name); void mysql_init_multi_delete(LEX *lex); +bool multi_delete_set_locks_and_link_aux_tables(LEX *lex); void init_max_user_conn(void); void init_update_queries(void); void free_max_user_conn(void); diff --git a/sql/sql_base.cc b/sql/sql_base.cc index b5df0be1073..a1887996d00 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -42,7 +42,6 @@ static my_bool open_new_frm(const char *path, const char *alias, uint db_stat, uint prgflag, uint ha_open_flags, TABLE *outparam, TABLE_LIST *table_desc, MEM_ROOT *mem_root); -static void relink_tables_for_multidelete(THD *thd); extern "C" byte *table_cache_key(const byte *record,uint *length, my_bool not_used __attribute__((unused))) @@ -2089,7 +2088,6 @@ bool open_and_lock_tables(THD *thd, TABLE_LIST *tables) (thd->fill_derived_tables() && mysql_handle_derived(thd->lex, &mysql_derived_filling))) DBUG_RETURN(TRUE); /* purecov: inspected */ - relink_tables_for_multidelete(thd); DBUG_RETURN(0); } @@ -2119,37 +2117,11 @@ bool open_normal_and_derived_tables(THD *thd, TABLE_LIST *tables) if (open_tables(thd, &tables, &counter) || mysql_handle_derived(thd->lex, &mysql_derived_prepare)) DBUG_RETURN(TRUE); /* purecov: inspected */ - relink_tables_for_multidelete(thd); // Not really needed, but DBUG_RETURN(0); } /* - Let us propagate pointers to open tables from global table list - to table lists for multi-delete -*/ - -static void relink_tables_for_multidelete(THD *thd) -{ - if (thd->lex->all_selects_list->next_select_in_list()) - { - for (SELECT_LEX *sl= thd->lex->all_selects_list; - sl; - sl= sl->next_select_in_list()) - { - for (TABLE_LIST *cursor= (TABLE_LIST *) sl->table_list.first; - cursor; - cursor=cursor->next_local) - { - if (cursor->correspondent_table) - cursor->table= cursor->correspondent_table->table; - } - } - } -} - - -/* Mark all real tables in the list as free for reuse. SYNOPSIS diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 5022392565c..f6ffc141444 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -750,7 +750,12 @@ typedef struct st_lex uint grant, grant_tot_col, which_columns; uint fk_delete_opt, fk_update_opt, fk_match_option; uint slave_thd_opt, start_transaction_opt; - uint table_count; /* used when usual update transformed in multiupdate */ + /* + In LEX representing update which were transformed to multi-update + stores total number of tables. For LEX representing multi-delete + holds number of tables from which we will delete records. + */ + uint table_count; uint8 describe; uint8 derived_tables; uint8 create_view_algorithm; diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 7d00cfa4c98..ba316e84c73 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -3291,10 +3291,9 @@ end_with_restore_list: DBUG_ASSERT(first_table == all_tables && first_table != 0); TABLE_LIST *aux_tables= (TABLE_LIST *)thd->lex->auxilliary_table_list.first; - uint table_count; multi_delete *result; - if ((res= multi_delete_precheck(thd, all_tables, &table_count))) + if ((res= multi_delete_precheck(thd, all_tables))) break; /* condition will be TRUE on SP re-excuting */ @@ -3311,7 +3310,7 @@ end_with_restore_list: goto error; if (!thd->is_fatal_error && (result= new multi_delete(thd,aux_tables, - table_count))) + lex->table_count))) { res= mysql_select(thd, &select_lex->ref_pointer_array, select_lex->get_table_list(), @@ -6799,23 +6798,19 @@ bool multi_update_precheck(THD *thd, TABLE_LIST *tables) multi_delete_precheck() thd Thread handler tables Global/local table list - table_count Pointer to table counter RETURN VALUE FALSE OK TRUE error */ -bool multi_delete_precheck(THD *thd, TABLE_LIST *tables, uint *table_count) +bool multi_delete_precheck(THD *thd, TABLE_LIST *tables) { SELECT_LEX *select_lex= &thd->lex->select_lex; TABLE_LIST *aux_tables= (TABLE_LIST *)thd->lex->auxilliary_table_list.first; - TABLE_LIST *target_tbl; DBUG_ENTER("multi_delete_precheck"); - *table_count= 0; - /* sql_yacc guarantees that tables and aux_tables are not zero */ DBUG_ASSERT(aux_tables != 0); if (check_db_used(thd, tables) || check_db_used(thd,aux_tables) || @@ -6828,9 +6823,35 @@ bool multi_delete_precheck(THD *thd, TABLE_LIST *tables, uint *table_count) ER(ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE), MYF(0)); DBUG_RETURN(TRUE); } - for (target_tbl= aux_tables; target_tbl; target_tbl= target_tbl->next_local) + DBUG_RETURN(FALSE); +} + + +/* + Link tables in auxilary table list of multi-delete with corresponding + elements in main table list, and set proper locks for them. + + SYNOPSIS + multi_delete_set_locks_and_link_aux_tables() + lex - pointer to LEX representing multi-delete + + RETURN VALUE + FALSE - success + TRUE - error +*/ + +bool multi_delete_set_locks_and_link_aux_tables(LEX *lex) +{ + TABLE_LIST *tables= (TABLE_LIST*)lex->select_lex.table_list.first; + TABLE_LIST *target_tbl; + DBUG_ENTER("multi_delete_set_locks_and_link_aux_tables"); + + lex->table_count= 0; + + for (target_tbl= (TABLE_LIST *)lex->auxilliary_table_list.first; + target_tbl; target_tbl= target_tbl->next_local) { - (*table_count)++; + lex->table_count++; /* All tables in aux_tables must be found in FROM PART */ TABLE_LIST *walk; for (walk= tables; walk; walk= walk->next_local) @@ -6848,14 +6869,6 @@ bool multi_delete_precheck(THD *thd, TABLE_LIST *tables, uint *table_count) } walk->lock_type= target_tbl->lock_type; target_tbl->correspondent_table= walk; // Remember corresponding table - - /* in case of subselects, we need to set lock_type in - * corresponding table in list of all tables */ - if (walk->correspondent_table) - { - target_tbl->correspondent_table= walk->correspondent_table; - walk->correspondent_table->lock_type= walk->lock_type; - } } DBUG_RETURN(FALSE); } diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index 1521b206e0d..759bf45da26 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -1448,8 +1448,7 @@ static int mysql_test_multidelete(Prepared_statement *stmt, if (add_item_to_list(stmt->thd, new Item_null())) return -1; - uint fake_counter; - if ((res= multi_delete_precheck(stmt->thd, tables, &fake_counter))) + if ((res= multi_delete_precheck(stmt->thd, tables))) return res; if ((res= select_like_stmt_test_with_open_n_lock(stmt, tables, &mysql_multi_delete_prepare, diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index e0529da9302..e4f6b5a8513 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -6132,10 +6132,17 @@ single_multi: | table_wild_list { mysql_init_multi_delete(Lex); } FROM join_table_list where_clause + { + if (multi_delete_set_locks_and_link_aux_tables(Lex)) + YYABORT; + } | FROM table_wild_list { mysql_init_multi_delete(Lex); } USING join_table_list where_clause - {} + { + if (multi_delete_set_locks_and_link_aux_tables(Lex)) + YYABORT; + } ; table_wild_list: |