diff options
author | Igor Babaev <igor@askmonty.org> | 2011-07-17 00:52:07 -0700 |
---|---|---|
committer | Igor Babaev <igor@askmonty.org> | 2011-07-17 00:52:07 -0700 |
commit | cc0195d6a1e603c3ebef91af16a1e1c33dff4a2e (patch) | |
tree | 346adf4088219487e1667e680ffb0ac891042097 | |
parent | c1b6eb149056f39969b2bbcbfe5d07eb3e1fcd44 (diff) | |
parent | d37465a9cc458ab215105de22875ce0a64c0efc2 (diff) | |
download | mariadb-git-cc0195d6a1e603c3ebef91af16a1e1c33dff4a2e.tar.gz |
Merge with the latest 5.3 code.
-rw-r--r-- | mysql-test/r/derived_view.result | 38 | ||||
-rw-r--r-- | mysql-test/t/derived_view.test | 42 | ||||
-rw-r--r-- | sql/item_subselect.cc | 3 | ||||
-rw-r--r-- | sql/item_subselect.h | 5 | ||||
-rw-r--r-- | sql/opt_subselect.cc | 127 | ||||
-rw-r--r-- | sql/sql_base.cc | 15 | ||||
-rw-r--r-- | sql/sql_class.cc | 1 | ||||
-rw-r--r-- | sql/sql_class.h | 2 | ||||
-rw-r--r-- | sql/sql_delete.cc | 27 | ||||
-rw-r--r-- | sql/sql_derived.cc | 131 | ||||
-rw-r--r-- | sql/sql_insert.cc | 2 | ||||
-rw-r--r-- | sql/sql_lex.cc | 62 | ||||
-rw-r--r-- | sql/sql_lex.h | 11 | ||||
-rw-r--r-- | sql/sql_prepare.cc | 13 | ||||
-rw-r--r-- | sql/sql_select.cc | 2 | ||||
-rw-r--r-- | sql/sql_select.h | 7 | ||||
-rw-r--r-- | sql/sql_update.cc | 21 |
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> ¬_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) |