summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIgor Babaev <igor@askmonty.org>2011-06-13 19:03:03 -0700
committerIgor Babaev <igor@askmonty.org>2011-06-13 19:03:03 -0700
commit56eb6d7e69ecce856e2d54e2404157407cb7203b (patch)
treeed1868d2b843805abf292c87b538e4a7983ae9a4
parentab411f8f1c2e84082623c038eb024c15c58745b5 (diff)
downloadmariadb-git-56eb6d7e69ecce856e2d54e2404157407cb7203b.tar.gz
Fixed LP bug #794890.
Changed the code that processing of multi-updates and multi-deletes with multitable views at the prepare stage. A proper solution would be: never to perform any transformations of views before and at the prepare stage. Yet it would require re-engineering of the code that checks privileges and updatability of views. Ultimately this re-engineering has to be done to provide a clean solution for INSERT/UPDATE/DELETE statements that use views. Fixed a valgrind problem in the function TABLE::use_index.
-rw-r--r--mysql-test/r/derived_view.result25
-rw-r--r--mysql-test/t/derived_view.test24
-rw-r--r--sql/sql_base.cc15
-rw-r--r--sql/sql_class.cc1
-rw-r--r--sql/sql_class.h2
-rw-r--r--sql/sql_delete.cc23
-rw-r--r--sql/sql_derived.cc84
-rw-r--r--sql/sql_lex.cc30
-rw-r--r--sql/sql_lex.h3
-rw-r--r--sql/sql_prepare.cc13
-rw-r--r--sql/sql_update.cc8
-rw-r--r--sql/table.cc2
12 files changed, 176 insertions, 54 deletions
diff --git a/mysql-test/r/derived_view.result b/mysql-test/r/derived_view.result
index 5e3fc1e8cf1..2b0ef412460 100644
--- a/mysql-test/r/derived_view.result
+++ b/mysql-test/r/derived_view.result
@@ -589,3 +589,28 @@ f1 f1
224 224
DROP VIEW v1;
DROP TABLE t1,t2;
+#
+# LP bug #794890: abort failure on multi-update with view
+#
+CREATE TABLE t1 (a int);
+INSERT INTO t1 VALUES (20), (7);
+CREATE TABLE t2 (a int);
+INSERT INTO t2 VALUES (7), (9), (7);
+CREATE ALGORITHM=TEMPTABLE VIEW v1 AS SELECT a FROM t1;
+CREATE VIEW v2 AS SELECT t2.a FROM t2, v1 WHERE t2.a=t2.a;
+UPDATE v2 SET a = 2;
+SELECT * FROM t2;
+a
+2
+2
+2
+UPDATE t1,v2 SET t1.a = 3;
+SELECT * FROM t1;
+a
+3
+3
+DELETE t1 FROM t1,v2;
+SELECT * FROM t1;
+a
+DROP VIEW v1,v2;
+DROP TABLE t1,t2;
diff --git a/mysql-test/t/derived_view.test b/mysql-test/t/derived_view.test
index 93c2eb48fcd..e5d5b0f797d 100644
--- a/mysql-test/t/derived_view.test
+++ b/mysql-test/t/derived_view.test
@@ -235,3 +235,27 @@ SELECT * FROM v1 JOIN t2 ON v1.f1 = t2.f1;
DROP VIEW v1;
DROP TABLE t1,t2;
+
+--echo #
+--echo # LP bug #794890: abort failure on multi-update with view
+--echo #
+
+CREATE TABLE t1 (a int);
+INSERT INTO t1 VALUES (20), (7);
+CREATE TABLE t2 (a int);
+INSERT INTO t2 VALUES (7), (9), (7);
+
+CREATE ALGORITHM=TEMPTABLE VIEW v1 AS SELECT a FROM t1;
+
+CREATE VIEW v2 AS SELECT t2.a FROM t2, v1 WHERE t2.a=t2.a;
+UPDATE v2 SET a = 2;
+SELECT * FROM t2;
+
+UPDATE t1,v2 SET t1.a = 3;
+SELECT * FROM t1;
+
+DELETE t1 FROM t1,v2;
+SELECT * FROM t1;
+
+DROP VIEW v1,v2;
+DROP TABLE t1,t2;
diff --git a/sql/sql_base.cc b/sql/sql_base.cc
index 136ae9cb9f0..dd1c89a8fb4 100644
--- a/sql/sql_base.cc
+++ b/sql/sql_base.cc
@@ -7789,9 +7789,18 @@ bool setup_tables(THD *thd, Name_resolution_context *context,
if (select_lex->first_cond_optimization)
{
leaves.empty();
- select_lex->leaf_tables_exec.empty();
- make_leaves_list(leaves, tables, full_table_list, first_select_table);
-
+ if (!select_lex->is_prep_leaf_list_saved)
+ {
+ make_leaves_list(leaves, tables, full_table_list, first_select_table);
+ select_lex->leaf_tables_exec.empty();
+ }
+ else
+ {
+ List_iterator_fast <TABLE_LIST> ti(select_lex->leaf_tables_prep);
+ while ((table_list= ti++))
+ leaves.push_back(table_list);
+ }
+
while ((table_list= ti++))
{
TABLE *table= table_list->table;
diff --git a/sql/sql_class.cc b/sql/sql_class.cc
index 925a39afa33..d8aece52c53 100644
--- a/sql/sql_class.cc
+++ b/sql/sql_class.cc
@@ -814,6 +814,7 @@ THD::THD()
memset(&invoker_user, 0, sizeof(invoker_user));
memset(&invoker_host, 0, sizeof(invoker_host));
prepare_derived_at_open= FALSE;
+ save_prep_leaf_list= FALSE;
}
diff --git a/sql/sql_class.h b/sql/sql_class.h
index aec7839455e..152d65e9d9e 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -1571,6 +1571,8 @@ public:
bool prepare_derived_at_open;
+ bool save_prep_leaf_list;
+
#ifndef MYSQL_CLIENT
int binlog_setup_trx_data();
diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc
index 6ace7ba8086..7f3ed2e705f 100644
--- a/sql/sql_delete.cc
+++ b/sql/sql_delete.cc
@@ -61,8 +61,9 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
if (open_and_lock_tables(thd, table_list))
DBUG_RETURN(TRUE);
- if (mysql_handle_list_of_derived(thd->lex, table_list, DT_MERGE_FOR_INSERT) ||
- mysql_handle_list_of_derived(thd->lex, table_list, DT_PREPARE))
+ if (mysql_handle_list_of_derived(thd->lex, table_list, DT_MERGE_FOR_INSERT))
+ DBUG_RETURN(TRUE);
+ if (mysql_handle_list_of_derived(thd->lex, table_list, DT_PREPARE))
DBUG_RETURN(TRUE);
if (!table_list->updatable)
@@ -550,7 +551,7 @@ int mysql_prepare_delete(THD *thd, TABLE_LIST *table_list, Item **conds)
fix_inner_refs(thd, all_fields, select_lex, select_lex->ref_pointer_array))
DBUG_RETURN(TRUE);
- select_lex->fix_prepare_information(thd, conds, &fake_conds);
+ select_lex->fix_prepare_information(thd, conds, &fake_conds);
DBUG_RETURN(FALSE);
}
@@ -586,10 +587,11 @@ int mysql_multi_delete_prepare(THD *thd)
TABLE_LIST *target_tbl;
DBUG_ENTER("mysql_multi_delete_prepare");
- TABLE_LIST *tables= lex->query_tables;
- if (mysql_handle_derived(lex, DT_INIT) ||
- mysql_handle_list_of_derived(lex, tables, DT_MERGE_FOR_INSERT) ||
- mysql_handle_list_of_derived(lex, tables, DT_PREPARE))
+ if (mysql_handle_derived(lex, DT_INIT))
+ DBUG_RETURN(TRUE);
+ if (mysql_handle_derived(lex, DT_MERGE_FOR_INSERT))
+ DBUG_RETURN(TRUE);
+ if (mysql_handle_derived(lex, DT_PREPARE))
DBUG_RETURN(TRUE);
/*
setup_tables() need for VIEWs. JOIN::prepare() will not do it second
@@ -616,7 +618,8 @@ int mysql_multi_delete_prepare(THD *thd)
target_tbl= target_tbl->next_local)
{
- if (!(target_tbl->table= target_tbl->correspondent_table->table))
+ target_tbl->table= target_tbl->correspondent_table->table;
+ if (target_tbl->correspondent_table->is_multitable())
{
my_error(ER_VIEW_DELETE_MERGE_VIEW, MYF(0),
target_tbl->correspondent_table->view_db.str,
@@ -651,6 +654,10 @@ int mysql_multi_delete_prepare(THD *thd)
with further calls to unique_table
*/
lex->select_lex.exclude_from_table_unique_test= FALSE;
+
+ if (lex->select_lex.save_prep_leaf_tables(thd))
+ DBUG_RETURN(TRUE);
+
DBUG_RETURN(FALSE);
}
diff --git a/sql/sql_derived.cc b/sql/sql_derived.cc
index 9bfa49c5f2d..7474566b184 100644
--- a/sql/sql_derived.cc
+++ b/sql/sql_derived.cc
@@ -85,15 +85,18 @@ mysql_handle_derived(LEX *lex, uint phases)
cursor && !res;
cursor= cursor->next_local)
{
+ if (!cursor->is_view_or_derived() && phases == DT_MERGE_FOR_INSERT)
+ continue;
uint8 allowed_phases= (cursor->is_merged_derived() ? DT_PHASES_MERGE :
- DT_PHASES_MATERIALIZE);
+ DT_PHASES_MATERIALIZE | DT_MERGE_FOR_INSERT);
/*
Skip derived tables to which the phase isn't applicable.
TODO: mark derived at the parse time, later set it's type
(merged or materialized)
*/
if ((phase_flag != DT_PREPARE && !(allowed_phases & phase_flag)) ||
- (cursor->merged_for_insert && phase_flag != DT_REINIT))
+ (cursor->merged_for_insert && phase_flag != DT_REINIT &&
+ phase_flag != DT_PREPARE))
continue;
res= (*processors[phase])(lex->thd, lex, cursor);
}
@@ -345,41 +348,43 @@ bool mysql_derived_merge(THD *thd, LEX *lex, TABLE_LIST *derived)
arena= thd->activate_stmt_arena_if_needed(&backup); // For easier test
derived->merged= TRUE;
- /*
- Check whether there is enough free bits in table map to merge subquery.
- If not - materialize it. This check isn't cached so when there is a big
- and small subqueries, and the bigger one can't be merged it wouldn't
- block the smaller one.
- */
- if (parent_lex->get_free_table_map(&map, &tablenr))
- {
- /* There is no enough table bits, fall back to materialization. */
- derived->change_refs_to_fields();
- derived->set_materialized_derived();
- goto exit_merge;
- }
- if (dt_select->leaf_tables.elements + tablenr > MAX_TABLES)
+ if (!derived->merged_for_insert)
{
- /* There is no enough table bits, fall back to materialization. */
- derived->change_refs_to_fields();
- derived->set_materialized_derived();
- goto exit_merge;
- }
+ /*
+ Check whether there is enough free bits in table map to merge subquery.
+ If not - materialize it. This check isn't cached so when there is a big
+ and small subqueries, and the bigger one can't be merged it wouldn't
+ block the smaller one.
+ */
+ if (parent_lex->get_free_table_map(&map, &tablenr))
+ {
+ /* There is no enough table bits, fall back to materialization. */
+ derived->change_refs_to_fields();
+ derived->set_materialized_derived();
+ goto exit_merge;
+ }
- if (dt_select->options & OPTION_SCHEMA_TABLE)
- parent_lex->options |= OPTION_SCHEMA_TABLE;
+ if (dt_select->leaf_tables.elements + tablenr > MAX_TABLES)
+ {
+ /* There is no enough table bits, fall back to materialization. */
+ derived->change_refs_to_fields();
+ derived->set_materialized_derived();
+ goto exit_merge;
+ }
- parent_lex->cond_count+= dt_select->cond_count;
+ if (dt_select->options & OPTION_SCHEMA_TABLE)
+ parent_lex->options |= OPTION_SCHEMA_TABLE;
- if (!derived->get_unit()->prepared)
- {
- dt_select->leaf_tables.empty();
- make_leaves_list(dt_select->leaf_tables, derived, TRUE, 0);
- }
+ parent_lex->cond_count+= dt_select->cond_count;
- if (!derived->merged_for_insert)
- { derived->nested_join= (NESTED_JOIN*) thd->calloc(sizeof(NESTED_JOIN));
+ if (!derived->get_unit()->prepared)
+ {
+ dt_select->leaf_tables.empty();
+ make_leaves_list(dt_select->leaf_tables, derived, TRUE, 0);
+ }
+
+ derived->nested_join= (NESTED_JOIN*) thd->calloc(sizeof(NESTED_JOIN));
if (!derived->nested_join)
{
res= TRUE;
@@ -467,14 +472,16 @@ bool mysql_derived_merge_for_insert(THD *thd, LEX *lex, TABLE_LIST *derived)
if (derived->merged_for_insert)
return FALSE;
- /* It's a target view for an INSERT, create field translation only. */
- if (!derived->updatable || derived->is_materialized_derived())
+ if (derived->is_materialized_derived())
{
- bool res= derived->create_field_translation(thd);
+ bool res= mysql_derived_prepare(thd, lex, derived);
+ derived->select_lex->leaf_tables.push_back(derived);
return res;
}
if (!derived->is_multitable())
{
+ if (!derived->updatable)
+ return derived->create_field_translation(thd);
TABLE_LIST *tl=((TABLE_LIST*)dt_select->table_list.first);
TABLE *table= tl->table;
/* preserve old map & tablenr. */
@@ -504,6 +511,9 @@ bool mysql_derived_merge_for_insert(THD *thd, LEX *lex, TABLE_LIST *derived)
}
else
{
+ if (thd->lex->sql_command == SQLCOM_UPDATE_MULTI ||
+ thd->lex->sql_command != SQLCOM_DELETE_MULTI)
+ thd->save_prep_leaf_list= TRUE;
if (!derived->merged_for_insert && mysql_derived_merge(thd, lex, derived))
return TRUE;
}
@@ -609,7 +619,11 @@ bool mysql_derived_prepare(THD *thd, LEX *lex, TABLE_LIST *derived)
bool res= FALSE;
// Skip already prepared views/DT
- if (!unit || unit->prepared || derived->merged_for_insert)
+ if (!unit || unit->prepared ||
+ (derived->merged_for_insert &&
+ !(derived->is_multitable() &&
+ (thd->lex->sql_command == SQLCOM_UPDATE_MULTI ||
+ thd->lex->sql_command == SQLCOM_DELETE_MULTI))))
DBUG_RETURN(FALSE);
Query_arena *arena= thd->stmt_arena, backup;
diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc
index d3613d0d660..27562238734 100644
--- a/sql/sql_lex.cc
+++ b/sql/sql_lex.cc
@@ -1604,6 +1604,7 @@ void st_select_lex::init_query()
top_join_list.empty();
join_list= &top_join_list;
embedding= 0;
+ leaf_tables_prep.empty();
leaf_tables.empty();
item_list.empty();
join= 0;
@@ -1672,6 +1673,7 @@ void st_select_lex::init_select()
cond_value= having_value= Item::COND_UNDEF;
inner_refs_list.empty();
full_group_by_flag= 0;
+ is_prep_leaf_list_saved= FALSE;
insert_tables= 0;
merged_into= 0;
}
@@ -3585,6 +3587,7 @@ void SELECT_LEX::mark_const_derived(bool empty)
}
}
+
bool st_select_lex::save_leaf_tables(THD *thd)
{
Query_arena *arena= thd->stmt_arena, backup;
@@ -3609,6 +3612,33 @@ bool st_select_lex::save_leaf_tables(THD *thd)
}
+bool st_select_lex::save_prep_leaf_tables(THD *thd)
+{
+ if (!thd->save_prep_leaf_list)
+ return 0;
+
+ Query_arena *arena= thd->stmt_arena, backup;
+ if (arena->is_conventional())
+ arena= 0;
+ else
+ thd->set_n_backup_active_arena(arena, &backup);
+
+ List_iterator_fast<TABLE_LIST> li(leaf_tables);
+ TABLE_LIST *table;
+ while ((table= li++))
+ {
+ if (leaf_tables_prep.push_back(table))
+ return 1;
+ }
+ thd->lex->select_lex.is_prep_leaf_list_saved= TRUE;
+ thd->save_prep_leaf_list= FALSE;
+ if (arena)
+ thd->restore_active_arena(arena, &backup);
+
+ return 0;
+}
+
+
/**
A routine used by the parser to decide whether we are specifying a full
partitioning or if only partitions to add or to split.
diff --git a/sql/sql_lex.h b/sql/sql_lex.h
index 5a8b5a5f361..57eeb963b89 100644
--- a/sql/sql_lex.h
+++ b/sql/sql_lex.h
@@ -639,6 +639,8 @@ public:
*/
List<TABLE_LIST> leaf_tables;
List<TABLE_LIST> leaf_tables_exec;
+ List<TABLE_LIST> leaf_tables_prep;
+ bool is_prep_leaf_list_saved;
uint insert_tables;
st_select_lex *merged_into; /* select which this select is merged into */
/* (not 0 only for views/derived tables) */
@@ -899,6 +901,7 @@ public:
void mark_const_derived(bool empty);
bool save_leaf_tables(THD *thd);
+ bool save_prep_leaf_tables(THD *thd);
private:
/* current index hint kind. used in filling up index_hints */
diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc
index ece4b8a1014..5b5ed004006 100644
--- a/sql/sql_prepare.cc
+++ b/sql/sql_prepare.cc
@@ -1269,8 +1269,9 @@ static int mysql_test_update(Prepared_statement *stmt,
thd->fill_derived_tables() is false here for sure (because it is
preparation of PS, so we even do not check it).
*/
- if (table_list->handle_derived(thd->lex, DT_MERGE_FOR_INSERT) ||
- table_list->handle_derived(thd->lex, DT_PREPARE))
+ if (table_list->handle_derived(thd->lex, DT_MERGE_FOR_INSERT))
+ goto error;
+ if (table_list->handle_derived(thd->lex, DT_PREPARE))
goto error;
if (!table_list->updatable)
@@ -1340,9 +1341,11 @@ static bool mysql_test_delete(Prepared_statement *stmt,
open_tables(thd, &table_list, &table_count, 0))
goto error;
- if (mysql_handle_derived(thd->lex, DT_INIT) ||
- mysql_handle_list_of_derived(thd->lex, table_list, DT_MERGE_FOR_INSERT) ||
- mysql_handle_list_of_derived(thd->lex, table_list, DT_PREPARE))
+ if (mysql_handle_derived(thd->lex, DT_INIT))
+ goto error;
+ if (mysql_handle_derived(thd->lex, DT_MERGE_FOR_INSERT))
+ goto error;
+ if (mysql_handle_derived(thd->lex, DT_PREPARE))
goto error;
if (!table_list->updatable)
diff --git a/sql/sql_update.cc b/sql/sql_update.cc
index 4821fc2bd8f..0bfbcf2b23f 100644
--- a/sql/sql_update.cc
+++ b/sql/sql_update.cc
@@ -1038,8 +1038,9 @@ reopen_tables:
call in setup_tables()).
*/
//We need to merge for insert prior to prepare.
- if (mysql_handle_list_of_derived(lex, table_list, DT_MERGE_FOR_INSERT))
- DBUG_RETURN(1);
+ if (mysql_handle_derived(lex, DT_MERGE_FOR_INSERT))
+ DBUG_RETURN(TRUE);
+
if (mysql_handle_derived(lex, DT_PREPARE))
DBUG_RETURN(TRUE);
@@ -1237,6 +1238,9 @@ reopen_tables:
further check in multi_update::prepare whether to use record cache.
*/
lex->select_lex.exclude_from_table_unique_test= FALSE;
+
+ if (lex->select_lex.save_prep_leaf_tables(thd))
+ DBUG_RETURN(TRUE);
DBUG_RETURN (FALSE);
}
diff --git a/sql/table.cc b/sql/table.cc
index 8427ca6e112..512214b155e 100644
--- a/sql/table.cc
+++ b/sql/table.cc
@@ -5372,7 +5372,7 @@ void TABLE::use_index(int key_to_save)
DBUG_ASSERT(!created && key_to_save < (int)s->keys);
if (key_to_save >= 0)
/* Save the given key. */
- memcpy(key_info, key_info + key_to_save, sizeof(KEY));
+ memmove(key_info, key_info + key_to_save, sizeof(KEY));
else
/* Drop all keys; */
i= 0;