summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIgor Babaev <igor@askmonty.org>2011-07-17 00:52:07 -0700
committerIgor Babaev <igor@askmonty.org>2011-07-17 00:52:07 -0700
commitcc0195d6a1e603c3ebef91af16a1e1c33dff4a2e (patch)
tree346adf4088219487e1667e680ffb0ac891042097
parentc1b6eb149056f39969b2bbcbfe5d07eb3e1fcd44 (diff)
parentd37465a9cc458ab215105de22875ce0a64c0efc2 (diff)
downloadmariadb-git-cc0195d6a1e603c3ebef91af16a1e1c33dff4a2e.tar.gz
Merge with the latest 5.3 code.
-rw-r--r--mysql-test/r/derived_view.result38
-rw-r--r--mysql-test/t/derived_view.test42
-rw-r--r--sql/item_subselect.cc3
-rw-r--r--sql/item_subselect.h5
-rw-r--r--sql/opt_subselect.cc127
-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.cc27
-rw-r--r--sql/sql_derived.cc131
-rw-r--r--sql/sql_insert.cc2
-rw-r--r--sql/sql_lex.cc62
-rw-r--r--sql/sql_lex.h11
-rw-r--r--sql/sql_prepare.cc13
-rw-r--r--sql/sql_select.cc2
-rw-r--r--sql/sql_select.h7
-rw-r--r--sql/sql_update.cc21
17 files changed, 324 insertions, 185 deletions
diff --git a/mysql-test/r/derived_view.result b/mysql-test/r/derived_view.result
index 51444acdb3c..a28a6bcf53a 100644
--- a/mysql-test/r/derived_view.result
+++ b/mysql-test/r/derived_view.result
@@ -590,6 +590,31 @@ f1 f1
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;
+#
# LP bug #802023: MIN/MAX optimization
# for mergeable derived tables and views
#
@@ -1078,3 +1103,16 @@ Warnings:
Note 1003 select 6 AS `a`,5 AS `b` from `test`.`t1` join `test`.`t2` left join `test`.`t3` on((0 <> 0)) where (not(<in_optimizer>((6,5),<exists>(select 7,5 having (trigcond(((<cache>(6) = 7) or isnull(7))) and trigcond(((<cache>(5) = 5) or isnull(5))) and trigcond(<is_not_null_test>(7)) and trigcond(<is_not_null_test>(5)))))))
DROP VIEW v1;
DROP TABLE t1,t2,t3;
+#
+# LP bug #794901: insert into a multi-table view
+#
+CREATE TABLE t1 (a int);
+CREATE TABLE t2 (a int);
+CREATE TABLE t3 (a int);
+CREATE VIEW v1 AS SELECT t1.a FROM t1,t2;
+CREATE VIEW v2 AS SELECT a FROM t2 GROUP BY a;
+CREATE VIEW v3 AS SELECT v1.a FROM v1,v2;
+INSERT INTO v3(a) VALUES (1);
+ERROR HY000: The target table v3 of the INSERT is not insertable-into
+DROP VIEW v1,v2,v3;
+DROP TABLE t1,t2,t3;
diff --git a/mysql-test/t/derived_view.test b/mysql-test/t/derived_view.test
index 70bd10aef51..8e53a7056c3 100644
--- a/mysql-test/t/derived_view.test
+++ b/mysql-test/t/derived_view.test
@@ -237,6 +237,30 @@ 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;
+
+--echo #
--echo # LP bug #802023: MIN/MAX optimization
--echo # for mergeable derived tables and views
--echo #
@@ -626,3 +650,21 @@ SELECT t.a,t.b FROM t3 RIGHT JOIN (v1 AS t, t2) ON t2.b != 0
DROP VIEW v1;
DROP TABLE t1,t2,t3;
+
+--echo #
+--echo # LP bug #794901: insert into a multi-table view
+--echo #
+
+CREATE TABLE t1 (a int);
+CREATE TABLE t2 (a int);
+CREATE TABLE t3 (a int);
+
+CREATE VIEW v1 AS SELECT t1.a FROM t1,t2;
+CREATE VIEW v2 AS SELECT a FROM t2 GROUP BY a;
+CREATE VIEW v3 AS SELECT v1.a FROM v1,v2;
+
+-- error ER_NON_INSERTABLE_TABLE
+INSERT INTO v3(a) VALUES (1);
+
+DROP VIEW v1,v2,v3;
+DROP TABLE t1,t2,t3;
diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc
index 7ac273b68c7..4379483a519 100644
--- a/sql/item_subselect.cc
+++ b/sql/item_subselect.cc
@@ -1146,7 +1146,8 @@ Item_in_subselect::Item_in_subselect(Item * left_exp,
st_select_lex *select_lex):
Item_exists_subselect(), left_expr_cache(0), first_execution(TRUE),
optimizer(0), pushed_cond_guards(NULL), in_strategy(0),
- is_jtbm_merged(FALSE), is_flattenable_semijoin(FALSE),
+ is_jtbm_merged(FALSE), is_flattenable_semijoin(FALSE),
+ is_registered_semijoin(FALSE),
upper_item(0)
{
DBUG_ENTER("Item_in_subselect::Item_in_subselect");
diff --git a/sql/item_subselect.h b/sql/item_subselect.h
index 8a84d446208..a192bb48f5c 100644
--- a/sql/item_subselect.h
+++ b/sql/item_subselect.h
@@ -459,6 +459,11 @@ public:
bool is_flattenable_semijoin;
/*
+ TRUE<=>registered in the list of semijoins in outer select
+ */
+ bool is_registered_semijoin;
+
+ /*
Used to determine how this subselect item is represented in the item tree,
in case there is a need to locate it there and replace with something else.
Two options are possible:
diff --git a/sql/opt_subselect.cc b/sql/opt_subselect.cc
index 9fb392c20f4..d94e166b32f 100644
--- a/sql/opt_subselect.cc
+++ b/sql/opt_subselect.cc
@@ -177,8 +177,8 @@ bool subquery_types_allow_materialization(Item_in_subselect *in_subs);
static bool replace_where_subcondition(JOIN *join, Item **expr,
Item *old_cond, Item *new_cond,
bool do_fix_fields);
-static int subq_sj_candidate_cmp(Item_in_subselect* const *el1,
- Item_in_subselect* const *el2);
+static int subq_sj_candidate_cmp(Item_in_subselect* el1, Item_in_subselect* el2,
+ void *arg);
static bool convert_subq_to_sj(JOIN *parent_join, Item_in_subselect *subq_pred);
static bool convert_subq_to_jtbm(JOIN *parent_join,
Item_in_subselect *subq_pred, bool *remove);
@@ -357,8 +357,15 @@ int check_and_do_in_subquery_rewrites(JOIN *join)
in_subs->is_flattenable_semijoin= TRUE;
/* Register the subquery for further processing in flatten_subqueries() */
- select_lex->
- outer_select()->join->sj_subselects.append(thd->mem_root, in_subs);
+ if (!in_subs->is_registered_semijoin)
+ {
+ Query_arena *arena, backup;
+ arena= thd->activate_stmt_arena_if_needed(&backup);
+ select_lex->outer_select()->sj_subselects.push_back(in_subs);
+ if (arena)
+ thd->restore_active_arena(arena, &backup);
+ in_subs->is_registered_semijoin= TRUE;
+ }
}
else
{
@@ -405,7 +412,7 @@ int check_and_do_in_subquery_rewrites(JOIN *join)
condition also excludes multi-table update statements.
A note about prepared statements: we want the if-branch to be taken on
PREPARE and each EXECUTE. The rewrites are only done once, but we need
- join->sj_subselects list to be populated for every EXECUTE.
+ select_lex->sj_subselects list to be populated for every EXECUTE.
*/
if (optimizer_flag(thd, OPTIMIZER_SWITCH_MATERIALIZATION) && // 0
@@ -432,8 +439,15 @@ int check_and_do_in_subquery_rewrites(JOIN *join)
{
in_subs->emb_on_expr_nest= thd->thd_marker.emb_on_expr_nest;
in_subs->is_flattenable_semijoin= FALSE;
- select_lex->outer_select()->
- join->sj_subselects.append(thd->mem_root, in_subs);
+ if (!in_subs->is_registered_semijoin)
+ {
+ Query_arena *arena, backup;
+ arena= thd->activate_stmt_arena_if_needed(&backup);
+ select_lex->outer_select()->sj_subselects.push_back(in_subs);
+ if (arena)
+ thd->restore_active_arena(arena, &backup);
+ in_subs->is_registered_semijoin= TRUE;
+ }
}
}
@@ -731,21 +745,19 @@ bool check_for_outer_joins(List<TABLE_LIST> *join_list)
bool convert_join_subqueries_to_semijoins(JOIN *join)
{
Query_arena *arena, backup;
- Item_in_subselect **in_subq;
- Item_in_subselect **in_subq_end;
+ Item_in_subselect *in_subq;
THD *thd= join->thd;
List_iterator<TABLE_LIST> ti(join->select_lex->leaf_tables);
DBUG_ENTER("convert_join_subqueries_to_semijoins");
- if (join->sj_subselects.elements() == 0)
+ if (join->select_lex->sj_subselects.is_empty())
DBUG_RETURN(FALSE);
- for (in_subq= join->sj_subselects.front(),
- in_subq_end= join->sj_subselects.back();
- in_subq != in_subq_end;
- in_subq++)
+ List_iterator_fast<Item_in_subselect> li(join->select_lex->sj_subselects);
+
+ while ((in_subq= li++))
{
- SELECT_LEX *subq_sel= (*in_subq)->get_select_lex();
+ SELECT_LEX *subq_sel= in_subq->get_select_lex();
if (subq_sel->handle_derived(thd->lex, DT_OPTIMIZE))
DBUG_RETURN(1);
if (subq_sel->handle_derived(thd->lex, DT_MERGE))
@@ -753,13 +765,11 @@ bool convert_join_subqueries_to_semijoins(JOIN *join)
subq_sel->update_used_tables();
}
+ li.rewind();
/* First, convert child join's subqueries. We proceed bottom-up here */
- for (in_subq= join->sj_subselects.front(),
- in_subq_end= join->sj_subselects.back();
- in_subq != in_subq_end;
- in_subq++)
+ while ((in_subq= li++))
{
- st_select_lex *child_select= (*in_subq)->get_select_lex();
+ st_select_lex *child_select= in_subq->get_select_lex();
JOIN *child_join= child_select->join;
child_join->outer_tables = child_join->table_count;
@@ -773,9 +783,9 @@ bool convert_join_subqueries_to_semijoins(JOIN *join)
if (convert_join_subqueries_to_semijoins(child_join))
DBUG_RETURN(TRUE);
- (*in_subq)->sj_convert_priority=
- test((*in_subq)->emb_on_expr_nest != NO_JOIN_NEST) * MAX_TABLES * 2 +
- (*in_subq)->is_correlated * MAX_TABLES + child_join->outer_tables;
+ in_subq->sj_convert_priority=
+ test(in_subq->emb_on_expr_nest != NO_JOIN_NEST) * MAX_TABLES * 2 +
+ in_subq->is_correlated * MAX_TABLES + child_join->outer_tables;
}
// Temporary measure: disable semi-joins when they are together with outer
@@ -783,7 +793,7 @@ bool convert_join_subqueries_to_semijoins(JOIN *join)
#if 0
if (check_for_outer_joins(join->join_list))
{
- in_subq= join->sj_subselects.front();
+ in_subq= join->select_lex->sj_subselects.head();
arena= thd->activate_stmt_arena_if_needed(&backup);
goto skip_conversion;
}
@@ -795,41 +805,41 @@ bool convert_join_subqueries_to_semijoins(JOIN *join)
- prefer correlated subqueries over uncorrelated;
- prefer subqueries that have greater number of outer tables;
*/
- join->sj_subselects.sort(subq_sj_candidate_cmp);
+ bubble_sort<Item_in_subselect>(&join->select_lex->sj_subselects,
+ subq_sj_candidate_cmp, NULL);
// #tables-in-parent-query + #tables-in-subquery < MAX_TABLES
/* Replace all subqueries to be flattened with Item_int(1) */
arena= thd->activate_stmt_arena_if_needed(&backup);
- for (in_subq= join->sj_subselects.front();
- in_subq != in_subq_end;
- in_subq++)
+ li.rewind();
+ while ((in_subq= li++))
{
bool remove_item= TRUE;
/* Stop processing if we've reached a subquery that's attached to the ON clause */
- if ((*in_subq)->emb_on_expr_nest != NO_JOIN_NEST)
+ if (in_subq->emb_on_expr_nest != NO_JOIN_NEST)
break;
- if ((*in_subq)->is_flattenable_semijoin)
+ if (in_subq->is_flattenable_semijoin)
{
if (join->table_count +
- (*in_subq)->unit->first_select()->join->table_count >= MAX_TABLES)
+ in_subq->unit->first_select()->join->table_count >= MAX_TABLES)
break;
- if (convert_subq_to_sj(join, *in_subq))
+ if (convert_subq_to_sj(join, in_subq))
DBUG_RETURN(TRUE);
}
else
{
if (join->table_count + 1 >= MAX_TABLES)
break;
- if (convert_subq_to_jtbm(join, *in_subq, &remove_item))
+ if (convert_subq_to_jtbm(join, in_subq, &remove_item))
DBUG_RETURN(TRUE);
}
if (remove_item)
{
- Item **tree= ((*in_subq)->emb_on_expr_nest == NO_JOIN_NEST)?
- &join->conds : &((*in_subq)->emb_on_expr_nest->on_expr);
- Item *replace_me= (*in_subq)->original_item();
+ Item **tree= (in_subq->emb_on_expr_nest == NO_JOIN_NEST)?
+ &join->conds : &(in_subq->emb_on_expr_nest->on_expr);
+ Item *replace_me= in_subq->original_item();
if (replace_where_subcondition(join, tree, replace_me, new Item_int(1),
FALSE))
DBUG_RETURN(TRUE); /* purecov: inspected */
@@ -840,34 +850,34 @@ bool convert_join_subqueries_to_semijoins(JOIN *join)
3. Finalize (perform IN->EXISTS rewrite) the subqueries that we didn't
convert:
*/
- for (; in_subq!= in_subq_end; in_subq++)
+ while (in_subq)
{
- JOIN *child_join= (*in_subq)->unit->first_select()->join;
- (*in_subq)->changed= 0;
- (*in_subq)->fixed= 0;
+ JOIN *child_join= in_subq->unit->first_select()->join;
+ in_subq->changed= 0;
+ in_subq->fixed= 0;
SELECT_LEX *save_select_lex= thd->lex->current_select;
- thd->lex->current_select= (*in_subq)->unit->first_select();
+ thd->lex->current_select= in_subq->unit->first_select();
- bool res= (*in_subq)->select_transformer(child_join);
+ bool res= in_subq->select_transformer(child_join);
thd->lex->current_select= save_select_lex;
if (res)
DBUG_RETURN(TRUE);
- (*in_subq)->changed= 1;
- (*in_subq)->fixed= 1;
+ in_subq->changed= 1;
+ in_subq->fixed= 1;
- Item *substitute= (*in_subq)->substitution;
- bool do_fix_fields= !(*in_subq)->substitution->fixed;
- Item **tree= ((*in_subq)->emb_on_expr_nest == NO_JOIN_NEST)?
- &join->conds : &((*in_subq)->emb_on_expr_nest->on_expr);
- Item *replace_me= (*in_subq)->original_item();
+ Item *substitute= in_subq->substitution;
+ bool do_fix_fields= !in_subq->substitution->fixed;
+ Item **tree= (in_subq->emb_on_expr_nest == NO_JOIN_NEST)?
+ &join->conds : &(in_subq->emb_on_expr_nest->on_expr);
+ Item *replace_me= in_subq->original_item();
if (replace_where_subcondition(join, tree, replace_me, substitute,
do_fix_fields))
DBUG_RETURN(TRUE);
- (*in_subq)->substitution= NULL;
+ in_subq->substitution= NULL;
#if 0
/*
Don't do the following, because the simplify_join() call is after this
@@ -881,9 +891,9 @@ bool convert_join_subqueries_to_semijoins(JOIN *join)
*/
if (!thd->stmt_arena->is_conventional())
{
- tree= ((*in_subq)->emb_on_expr_nest == NO_JOIN_NEST)?
+ tree= (in_subq->emb_on_expr_nest == NO_JOIN_NEST)?
&join->select_lex->prep_where :
- &((*in_subq)->emb_on_expr_nest->prep_on_expr);
+ &(in_subq->emb_on_expr_nest->prep_on_expr);
if (replace_where_subcondition(join, tree, replace_me, substitute,
FALSE))
@@ -898,12 +908,13 @@ bool convert_join_subqueries_to_semijoins(JOIN *join)
re-run the test for materialization that was done in
check_and_do_in_subquery_rewrites.
*/
- (*in_subq)->in_strategy= SUBS_IN_TO_EXISTS;
+ in_subq->in_strategy= SUBS_IN_TO_EXISTS;
+ in_subq= li++;
}
if (arena)
thd->restore_active_arena(arena, &backup);
- join->sj_subselects.clear();
+ join->select_lex->sj_subselects.empty();
DBUG_RETURN(FALSE);
}
@@ -1004,11 +1015,11 @@ static bool replace_where_subcondition(JOIN *join, Item **expr,
return TRUE;
}
-static int subq_sj_candidate_cmp(Item_in_subselect* const *el1,
- Item_in_subselect* const *el2)
+static int subq_sj_candidate_cmp(Item_in_subselect* el1, Item_in_subselect* el2,
+ void *arg)
{
- return ((*el1)->sj_convert_priority < (*el2)->sj_convert_priority) ? 1 :
- ( ((*el1)->sj_convert_priority == (*el2)->sj_convert_priority)? 0 : -1);
+ return (el1->sj_convert_priority > el2->sj_convert_priority) ? 1 :
+ ( (el1->sj_convert_priority == el2->sj_convert_priority)? 0 : -1);
}
diff --git a/sql/sql_base.cc b/sql/sql_base.cc
index 5f5e1334148..7cf607b28d9 100644
--- a/sql/sql_base.cc
+++ b/sql/sql_base.cc
@@ -7794,9 +7794,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 0772ea56d17..9bb1f83b06d 100644
--- a/sql/sql_class.cc
+++ b/sql/sql_class.cc
@@ -817,6 +817,7 @@ THD::THD()
memset(&invoker_host, 0, sizeof(invoker_host));
prepare_derived_at_open= FALSE;
create_tmp_table_for_derived= FALSE;
+ save_prep_leaf_list= FALSE;
}
diff --git a/sql/sql_class.h b/sql/sql_class.h
index d304eb72d0e..d5a6f25b3cd 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -1601,6 +1601,8 @@ public:
*/
bool create_tmp_table_for_derived;
+ 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..becc1ada7ae 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
@@ -601,9 +603,11 @@ int mysql_multi_delete_prepare(THD *thd)
&thd->lex->select_lex.top_join_list,
lex->query_tables,
lex->select_lex.leaf_tables, FALSE,
- DELETE_ACL, SELECT_ACL, TRUE))
+ DELETE_ACL, SELECT_ACL, FALSE))
DBUG_RETURN(TRUE);
+ if (lex->select_lex.handle_derived(thd->lex, DT_MERGE))
+ DBUG_RETURN(TRUE);
/*
Multi-delete can't be constructed over-union => we always have
@@ -616,7 +620,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 +656,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 fb033fb27f6..e3de7d9c2dd 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);
}
@@ -343,43 +346,52 @@ bool mysql_derived_merge(THD *thd, LEX *lex, TABLE_LIST *derived)
if (derived->merged)
return FALSE;
+ if (thd->lex->sql_command == SQLCOM_UPDATE_MULTI ||
+ thd->lex->sql_command == SQLCOM_DELETE_MULTI)
+ thd->save_prep_leaf_list= TRUE;
+
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 ||
+ (derived->is_multitable() &&
+ (thd->lex->sql_command == SQLCOM_UPDATE_MULTI ||
+ thd->lex->sql_command == SQLCOM_DELETE_MULTI)))
{
- /* 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->get_unit()->prepared)
+ {
+ dt_select->leaf_tables.empty();
+ make_leaves_list(dt_select->leaf_tables, derived, TRUE, 0);
+ }
- if (!derived->merged_for_insert)
- { derived->nested_join= (NESTED_JOIN*) thd->calloc(sizeof(NESTED_JOIN));
+ derived->nested_join= (NESTED_JOIN*) thd->calloc(sizeof(NESTED_JOIN));
if (!derived->nested_join)
{
res= TRUE;
@@ -387,7 +399,7 @@ bool mysql_derived_merge(THD *thd, LEX *lex, TABLE_LIST *derived)
}
/* Merge derived table's subquery in the parent select. */
- if (parent_lex->merge_subquery(derived, dt_select, tablenr, map))
+ if (parent_lex->merge_subquery(thd, derived, dt_select, tablenr, map))
{
res= TRUE;
goto exit_merge;
@@ -463,52 +475,25 @@ exit_merge:
bool mysql_derived_merge_for_insert(THD *thd, LEX *lex, TABLE_LIST *derived)
{
- SELECT_LEX *dt_select= derived->get_single_select();
-
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())
{
- TABLE_LIST *tl=((TABLE_LIST*)dt_select->table_list.first);
- TABLE *table= tl->table;
- /* preserve old map & tablenr. */
- if (!derived->merged_for_insert && derived->table)
- table->set_table_map(derived->table->map, derived->table->tablenr);
-
- derived->table= table;
- derived->schema_table=
- ((TABLE_LIST*)dt_select->table_list.first)->schema_table;
- if (!derived->merged)
+ if (!derived->updatable)
+ return derived->create_field_translation(thd);
+ if (derived->merge_underlying_list)
{
- Query_arena *arena, backup;
- arena= thd->activate_stmt_arena_if_needed(&backup); // For easier test
- derived->select_lex->leaf_tables.push_back(tl);
- derived->nested_join= (NESTED_JOIN*) thd->calloc(sizeof(NESTED_JOIN));
- if (derived->nested_join)
- {
- derived->wrap_into_nested_join(tl->select_lex->top_join_list);
- derived->get_unit()->exclude_level();
- }
- if (arena)
- thd->restore_active_arena(arena, &backup);
- derived->merged= TRUE;
- if (!derived->nested_join)
- return TRUE;
- }
- }
- else
- {
- if (!derived->merged_for_insert && mysql_derived_merge(thd, lex, derived))
- return TRUE;
- }
- derived->merged_for_insert= TRUE;
-
+ derived->table= derived->merge_underlying_list->table;
+ derived->schema_table= derived->merge_underlying_list->schema_table;
+ derived->merged_for_insert= TRUE;
+ }
+ }
return FALSE;
}
@@ -609,7 +594,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_insert.cc b/sql/sql_insert.cc
index 72514d72dc1..05a30ab49a4 100644
--- a/sql/sql_insert.cc
+++ b/sql/sql_insert.cc
@@ -135,7 +135,7 @@ bool check_view_single_update(List<Item> &fields, List<Item> *values,
A buffer for the insert values was allocated for the merged view.
Use it.
*/
- //tbl->table->insert_values= view->table->insert_values;
+ tbl->table->insert_values= view->table->insert_values;
view->table= tbl->table;
*map= tables;
diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc
index c8b5a3439b0..5abb47acd6b 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;
@@ -1646,6 +1647,7 @@ void st_select_lex::init_select()
{
st_select_lex_node::init_select();
sj_nests.empty();
+ sj_subselects.empty();
group_list.empty();
type= db= 0;
having= 0;
@@ -1672,6 +1674,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;
}
@@ -3233,12 +3236,6 @@ bool st_select_lex::get_free_table_map(table_map *map, uint *tablenr)
*map= 0;
*tablenr= 0;
TABLE_LIST *tl;
- if (!join)
- {
- (*map)= 1<<1;
- (*tablenr)++;
- return FALSE;
- }
List_iterator<TABLE_LIST> ti(leaf_tables);
while ((tl= ti++))
{
@@ -3395,7 +3392,8 @@ void st_select_lex::remap_tables(TABLE_LIST *derived, table_map map,
@return FALSE ok
*/
-bool SELECT_LEX::merge_subquery(TABLE_LIST *derived, SELECT_LEX *subq_select,
+bool SELECT_LEX::merge_subquery(THD *thd, TABLE_LIST *derived,
+ SELECT_LEX *subq_select,
uint table_no, table_map map)
{
derived->wrap_into_nested_join(subq_select->top_join_list);
@@ -3403,19 +3401,17 @@ bool SELECT_LEX::merge_subquery(TABLE_LIST *derived, SELECT_LEX *subq_select,
leaf_tables.concat(&subq_select->leaf_tables);
ftfunc_list->concat(subq_select->ftfunc_list);
- if (join)
- {
- Item_in_subselect **in_subq;
- Item_in_subselect **in_subq_end;
- for (in_subq= subq_select->join->sj_subselects.front(),
- in_subq_end= subq_select->join->sj_subselects.back();
- in_subq != in_subq_end;
- in_subq++)
+ if (join ||
+ thd->lex->sql_command == SQLCOM_UPDATE_MULTI ||
+ thd->lex->sql_command == SQLCOM_DELETE_MULTI)
+ {
+ List_iterator_fast<Item_in_subselect> li(subq_select->sj_subselects);
+ Item_in_subselect *in_subq;
+ while ((in_subq= li++))
{
- join->sj_subselects.append(join->thd->mem_root, *in_subq);
- DBUG_ASSERT((*in_subq)->emb_on_expr_nest != NULL);
- if ((*in_subq)->emb_on_expr_nest == NO_JOIN_NEST)
- (*in_subq)->emb_on_expr_nest= derived;
+ sj_subselects.push_back(in_subq);
+ if (in_subq->emb_on_expr_nest == NO_JOIN_NEST)
+ in_subq->emb_on_expr_nest= derived;
}
}
/*
@@ -3628,6 +3624,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;
@@ -3652,6 +3649,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..14ab445a366 100644
--- a/sql/sql_lex.h
+++ b/sql/sql_lex.h
@@ -637,8 +637,16 @@ public:
tables. Unlike 'next_local', this in this list views are *not*
leaves. Created in setup_tables() -> make_leaves_list().
*/
+ /*
+ Subqueries that will need to be converted to semi-join nests, including
+ those converted to jtbm nests. The list is emptied when conversion is done.
+ */
+ List<Item_in_subselect> sj_subselects;
+
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) */
@@ -884,7 +892,7 @@ public:
void remove_table_from_list(TABLE_LIST *table);
void remap_tables(TABLE_LIST *derived, table_map map,
uint tablenr, st_select_lex *parent_lex);
- bool merge_subquery(TABLE_LIST *derived, st_select_lex *subq_lex,
+ bool merge_subquery(THD *thd, TABLE_LIST *derived, st_select_lex *subq_lex,
uint tablenr, table_map map);
inline bool is_mergeable()
{
@@ -899,6 +907,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_select.cc b/sql/sql_select.cc
index 4be9d534fa7..30060262b26 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -13055,7 +13055,7 @@ void setup_tmp_table_column_bitmaps(TABLE *table, uchar *bitmaps)
*/
TABLE *
-create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields,
+create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields,
ORDER *group, bool distinct, bool save_sum_fields,
ulonglong select_options, ha_rows rows_limit,
char *table_alias, bool do_not_open)
diff --git a/sql/sql_select.h b/sql/sql_select.h
index 07872cffa3e..54b83a3c379 100644
--- a/sql/sql_select.h
+++ b/sql/sql_select.h
@@ -966,11 +966,6 @@ public:
bool optimized; ///< flag to avoid double optimization in EXPLAIN
bool initialized; ///< flag to avoid double init_execution calls
- /*
- Subqueries that will need to be converted to semi-join nests, including
- those converted to jtbm nests. The list is emptied when conversion is done.
- */
- Array<Item_in_subselect> sj_subselects;
/*
Additional WHERE and HAVING predicates to be considered for IN=>EXISTS
subquery transformation of a JOIN object.
@@ -1000,7 +995,7 @@ public:
JOIN(THD *thd_arg, List<Item> &fields_arg, ulonglong select_options_arg,
select_result *result_arg)
- :fields_list(fields_arg), sj_subselects(thd_arg->mem_root, 4)
+ :fields_list(fields_arg)
{
init(thd_arg, fields_arg, select_options_arg, result_arg);
}
diff --git a/sql/sql_update.cc b/sql/sql_update.cc
index bd088857ff7..b516065eff7 100644
--- a/sql/sql_update.cc
+++ b/sql/sql_update.cc
@@ -248,6 +248,7 @@ int mysql_update(THD *thd,
DBUG_RETURN(1);
close_tables_for_reopen(thd, &table_list);
}
+
if (table_list->handle_derived(thd->lex, DT_MERGE_FOR_INSERT))
DBUG_RETURN(1);
if (table_list->handle_derived(thd->lex, DT_PREPARE))
@@ -1037,9 +1038,10 @@ reopen_tables:
second time, but this call will do nothing (there are check for second
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);
@@ -1047,7 +1049,10 @@ reopen_tables:
&lex->select_lex.top_join_list,
table_list,
lex->select_lex.leaf_tables, FALSE,
- UPDATE_ACL, SELECT_ACL, TRUE))
+ UPDATE_ACL, SELECT_ACL, FALSE))
+ DBUG_RETURN(TRUE);
+
+ if (lex->select_lex.handle_derived(thd->lex, DT_MERGE))
DBUG_RETURN(TRUE);
if (setup_fields_with_no_wrap(thd, 0, *fields, MARK_COLUMNS_WRITE, 0, 0))
@@ -1237,6 +1242,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);
}
@@ -1330,13 +1338,6 @@ int multi_update::prepare(List<Item> &not_used_values,
thd->cuted_fields=0L;
thd_proc_info(thd, "updating main table");
- SELECT_LEX *select_lex= lex_unit->first_select();
- if (select_lex->first_cond_optimization)
- {
- if (select_lex->handle_derived(thd->lex, DT_MERGE))
- DBUG_RETURN(TRUE);
- }
-
tables_to_update= get_table_map(fields);
if (!tables_to_update)