Commit d37465a9 authored by Igor Babaev's avatar Igor Babaev

Fixed LP bug #794901.

Also:
1. simplified the code of the function mysql_derived_merge_for_insert.
2. moved merge of views/dt for multi-update/delete to the prepare stage.
3. the list of the references to the candidates for semi-join now is
   allocated in the statement memory.
parent 6e541385
...@@ -1003,3 +1003,16 @@ Warnings: ...@@ -1003,3 +1003,16 @@ Warnings:
Note 1003 select `test`.`t2`.`a` AS `a`,0 AS `a`,0 AS `b` from `test`.`t1` left join `test`.`t2` on((0 <> 0)) where <expr_cache><0>(<in_optimizer>(0,<exists>(select 0 from `test`.`t1` where (<cache>(0) = 0)))) Note 1003 select `test`.`t2`.`a` AS `a`,0 AS `a`,0 AS `b` from `test`.`t1` left join `test`.`t2` on((0 <> 0)) where <expr_cache><0>(<in_optimizer>(0,<exists>(select 0 from `test`.`t1` where (<cache>(0) = 0))))
DROP VIEW v1; DROP VIEW v1;
DROP TABLE t1,t2; DROP TABLE t1,t2;
#
# 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;
...@@ -582,3 +582,21 @@ SELECT * FROM t2 RIGHT JOIN v1 AS t ON t.a != 0 ...@@ -582,3 +582,21 @@ SELECT * FROM t2 RIGHT JOIN v1 AS t ON t.a != 0
DROP VIEW v1; DROP VIEW v1;
DROP TABLE t1,t2; DROP TABLE t1,t2;
--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;
...@@ -1146,7 +1146,8 @@ Item_in_subselect::Item_in_subselect(Item * left_exp, ...@@ -1146,7 +1146,8 @@ Item_in_subselect::Item_in_subselect(Item * left_exp,
st_select_lex *select_lex): st_select_lex *select_lex):
Item_exists_subselect(), left_expr_cache(0), first_execution(TRUE), Item_exists_subselect(), left_expr_cache(0), first_execution(TRUE),
optimizer(0), pushed_cond_guards(NULL), in_strategy(0), 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) upper_item(0)
{ {
DBUG_ENTER("Item_in_subselect::Item_in_subselect"); DBUG_ENTER("Item_in_subselect::Item_in_subselect");
......
...@@ -458,6 +458,11 @@ class Item_in_subselect :public Item_exists_subselect ...@@ -458,6 +458,11 @@ class Item_in_subselect :public Item_exists_subselect
*/ */
bool is_flattenable_semijoin; 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, 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. in case there is a need to locate it there and replace with something else.
......
This diff is collapsed.
...@@ -603,9 +603,11 @@ int mysql_multi_delete_prepare(THD *thd) ...@@ -603,9 +603,11 @@ int mysql_multi_delete_prepare(THD *thd)
&thd->lex->select_lex.top_join_list, &thd->lex->select_lex.top_join_list,
lex->query_tables, lex->query_tables,
lex->select_lex.leaf_tables, FALSE, lex->select_lex.leaf_tables, FALSE,
DELETE_ACL, SELECT_ACL, TRUE)) DELETE_ACL, SELECT_ACL, FALSE))
DBUG_RETURN(TRUE); 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 Multi-delete can't be constructed over-union => we always have
......
...@@ -346,10 +346,17 @@ bool mysql_derived_merge(THD *thd, LEX *lex, TABLE_LIST *derived) ...@@ -346,10 +346,17 @@ bool mysql_derived_merge(THD *thd, LEX *lex, TABLE_LIST *derived)
if (derived->merged) if (derived->merged)
return FALSE; 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 arena= thd->activate_stmt_arena_if_needed(&backup); // For easier test
derived->merged= TRUE; derived->merged= TRUE;
if (!derived->merged_for_insert) if (!derived->merged_for_insert ||
(derived->is_multitable() &&
(thd->lex->sql_command == SQLCOM_UPDATE_MULTI ||
thd->lex->sql_command == SQLCOM_DELETE_MULTI)))
{ {
/* /*
Check whether there is enough free bits in table map to merge subquery. Check whether there is enough free bits in table map to merge subquery.
...@@ -392,7 +399,7 @@ bool mysql_derived_merge(THD *thd, LEX *lex, TABLE_LIST *derived) ...@@ -392,7 +399,7 @@ bool mysql_derived_merge(THD *thd, LEX *lex, TABLE_LIST *derived)
} }
/* Merge derived table's subquery in the parent select. */ /* 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; res= TRUE;
goto exit_merge; goto exit_merge;
...@@ -468,8 +475,6 @@ bool mysql_derived_merge(THD *thd, LEX *lex, TABLE_LIST *derived) ...@@ -468,8 +475,6 @@ bool mysql_derived_merge(THD *thd, LEX *lex, TABLE_LIST *derived)
bool mysql_derived_merge_for_insert(THD *thd, LEX *lex, TABLE_LIST *derived) 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) if (derived->merged_for_insert)
return FALSE; return FALSE;
if (derived->is_materialized_derived()) if (derived->is_materialized_derived())
...@@ -482,43 +487,13 @@ bool mysql_derived_merge_for_insert(THD *thd, LEX *lex, TABLE_LIST *derived) ...@@ -482,43 +487,13 @@ bool mysql_derived_merge_for_insert(THD *thd, LEX *lex, TABLE_LIST *derived)
{ {
if (!derived->updatable) if (!derived->updatable)
return derived->create_field_translation(thd); return derived->create_field_translation(thd);
TABLE_LIST *tl=((TABLE_LIST*)dt_select->table_list.first); if (derived->merge_underlying_list)
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)
{ {
Query_arena *arena, backup; derived->table= derived->merge_underlying_list->table;
arena= thd->activate_stmt_arena_if_needed(&backup); // For easier test derived->schema_table= derived->merge_underlying_list->schema_table;
derived->select_lex->leaf_tables.push_back(tl); derived->merged_for_insert= TRUE;
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 (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;
}
derived->merged_for_insert= TRUE;
return FALSE; return FALSE;
} }
......
...@@ -135,7 +135,7 @@ bool check_view_single_update(List<Item> &fields, List<Item> *values, ...@@ -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. A buffer for the insert values was allocated for the merged view.
Use it. Use it.
*/ */
//tbl->table->insert_values= view->table->insert_values; tbl->table->insert_values= view->table->insert_values;
view->table= tbl->table; view->table= tbl->table;
*map= tables; *map= tables;
......
...@@ -1647,6 +1647,7 @@ void st_select_lex::init_select() ...@@ -1647,6 +1647,7 @@ void st_select_lex::init_select()
{ {
st_select_lex_node::init_select(); st_select_lex_node::init_select();
sj_nests.empty(); sj_nests.empty();
sj_subselects.empty();
group_list.empty(); group_list.empty();
type= db= 0; type= db= 0;
having= 0; having= 0;
...@@ -3235,12 +3236,6 @@ bool st_select_lex::get_free_table_map(table_map *map, uint *tablenr) ...@@ -3235,12 +3236,6 @@ bool st_select_lex::get_free_table_map(table_map *map, uint *tablenr)
*map= 0; *map= 0;
*tablenr= 0; *tablenr= 0;
TABLE_LIST *tl; TABLE_LIST *tl;
if (!join)
{
(*map)= 1<<1;
(*tablenr)++;
return FALSE;
}
List_iterator<TABLE_LIST> ti(leaf_tables); List_iterator<TABLE_LIST> ti(leaf_tables);
while ((tl= ti++)) while ((tl= ti++))
{ {
...@@ -3397,7 +3392,8 @@ void st_select_lex::remap_tables(TABLE_LIST *derived, table_map map, ...@@ -3397,7 +3392,8 @@ void st_select_lex::remap_tables(TABLE_LIST *derived, table_map map,
@return FALSE ok @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) uint table_no, table_map map)
{ {
derived->wrap_into_nested_join(subq_select->top_join_list); derived->wrap_into_nested_join(subq_select->top_join_list);
...@@ -3405,19 +3401,17 @@ bool SELECT_LEX::merge_subquery(TABLE_LIST *derived, SELECT_LEX *subq_select, ...@@ -3405,19 +3401,17 @@ bool SELECT_LEX::merge_subquery(TABLE_LIST *derived, SELECT_LEX *subq_select,
leaf_tables.concat(&subq_select->leaf_tables); leaf_tables.concat(&subq_select->leaf_tables);
ftfunc_list->concat(subq_select->ftfunc_list); ftfunc_list->concat(subq_select->ftfunc_list);
if (join) if (join ||
{ thd->lex->sql_command == SQLCOM_UPDATE_MULTI ||
Item_in_subselect **in_subq; thd->lex->sql_command == SQLCOM_DELETE_MULTI)
Item_in_subselect **in_subq_end; {
for (in_subq= subq_select->join->sj_subselects.front(), List_iterator_fast<Item_in_subselect> li(subq_select->sj_subselects);
in_subq_end= subq_select->join->sj_subselects.back(); Item_in_subselect *in_subq;
in_subq != in_subq_end; while ((in_subq= li++))
in_subq++)
{ {
join->sj_subselects.append(join->thd->mem_root, *in_subq); sj_subselects.push_back(in_subq);
DBUG_ASSERT((*in_subq)->emb_on_expr_nest != NULL); if (in_subq->emb_on_expr_nest == NO_JOIN_NEST)
if ((*in_subq)->emb_on_expr_nest == NO_JOIN_NEST) in_subq->emb_on_expr_nest= derived;
(*in_subq)->emb_on_expr_nest= derived;
} }
} }
/* /*
......
...@@ -637,6 +637,12 @@ class st_select_lex: public st_select_lex_node ...@@ -637,6 +637,12 @@ class st_select_lex: public st_select_lex_node
tables. Unlike 'next_local', this in this list views are *not* tables. Unlike 'next_local', this in this list views are *not*
leaves. Created in setup_tables() -> make_leaves_list(). 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;
List<TABLE_LIST> leaf_tables_exec; List<TABLE_LIST> leaf_tables_exec;
List<TABLE_LIST> leaf_tables_prep; List<TABLE_LIST> leaf_tables_prep;
...@@ -886,7 +892,7 @@ class st_select_lex: public st_select_lex_node ...@@ -886,7 +892,7 @@ class st_select_lex: public st_select_lex_node
void remove_table_from_list(TABLE_LIST *table); void remove_table_from_list(TABLE_LIST *table);
void remap_tables(TABLE_LIST *derived, table_map map, void remap_tables(TABLE_LIST *derived, table_map map,
uint tablenr, st_select_lex *parent_lex); 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); uint tablenr, table_map map);
inline bool is_mergeable() inline bool is_mergeable()
{ {
......
...@@ -13033,7 +13033,7 @@ void setup_tmp_table_column_bitmaps(TABLE *table, uchar *bitmaps) ...@@ -13033,7 +13033,7 @@ void setup_tmp_table_column_bitmaps(TABLE *table, uchar *bitmaps)
*/ */
TABLE * 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, ORDER *group, bool distinct, bool save_sum_fields,
ulonglong select_options, ha_rows rows_limit, ulonglong select_options, ha_rows rows_limit,
char *table_alias, bool do_not_open) char *table_alias, bool do_not_open)
......
...@@ -964,11 +964,6 @@ class JOIN :public Sql_alloc ...@@ -964,11 +964,6 @@ class JOIN :public Sql_alloc
bool optimized; ///< flag to avoid double optimization in EXPLAIN bool optimized; ///< flag to avoid double optimization in EXPLAIN
bool initialized; ///< flag to avoid double init_execution calls 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 Additional WHERE and HAVING predicates to be considered for IN=>EXISTS
subquery transformation of a JOIN object. subquery transformation of a JOIN object.
...@@ -998,7 +993,7 @@ class JOIN :public Sql_alloc ...@@ -998,7 +993,7 @@ class JOIN :public Sql_alloc
JOIN(THD *thd_arg, List<Item> &fields_arg, ulonglong select_options_arg, JOIN(THD *thd_arg, List<Item> &fields_arg, ulonglong select_options_arg,
select_result *result_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); init(thd_arg, fields_arg, select_options_arg, result_arg);
} }
......
...@@ -248,6 +248,7 @@ int mysql_update(THD *thd, ...@@ -248,6 +248,7 @@ int mysql_update(THD *thd,
DBUG_RETURN(1); DBUG_RETURN(1);
close_tables_for_reopen(thd, &table_list); close_tables_for_reopen(thd, &table_list);
} }
if (table_list->handle_derived(thd->lex, DT_MERGE_FOR_INSERT)) if (table_list->handle_derived(thd->lex, DT_MERGE_FOR_INSERT))
DBUG_RETURN(1); DBUG_RETURN(1);
if (table_list->handle_derived(thd->lex, DT_PREPARE)) if (table_list->handle_derived(thd->lex, DT_PREPARE))
...@@ -1037,10 +1038,10 @@ int mysql_multi_update_prepare(THD *thd) ...@@ -1037,10 +1038,10 @@ int mysql_multi_update_prepare(THD *thd)
second time, but this call will do nothing (there are check for second second time, but this call will do nothing (there are check for second
call in setup_tables()). call in setup_tables()).
*/ */
//We need to merge for insert prior to prepare. //We need to merge for insert prior to prepare.
if (mysql_handle_derived(lex, DT_MERGE_FOR_INSERT)) if (mysql_handle_derived(lex, DT_MERGE_FOR_INSERT))
DBUG_RETURN(TRUE); DBUG_RETURN(TRUE);
if (mysql_handle_derived(lex, DT_PREPARE)) if (mysql_handle_derived(lex, DT_PREPARE))
DBUG_RETURN(TRUE); DBUG_RETURN(TRUE);
...@@ -1048,7 +1049,10 @@ int mysql_multi_update_prepare(THD *thd) ...@@ -1048,7 +1049,10 @@ int mysql_multi_update_prepare(THD *thd)
&lex->select_lex.top_join_list, &lex->select_lex.top_join_list,
table_list, table_list,
lex->select_lex.leaf_tables, FALSE, 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); DBUG_RETURN(TRUE);
if (setup_fields_with_no_wrap(thd, 0, *fields, MARK_COLUMNS_WRITE, 0, 0)) if (setup_fields_with_no_wrap(thd, 0, *fields, MARK_COLUMNS_WRITE, 0, 0))
...@@ -1334,13 +1338,6 @@ int multi_update::prepare(List<Item> &not_used_values, ...@@ -1334,13 +1338,6 @@ int multi_update::prepare(List<Item> &not_used_values,
thd->cuted_fields=0L; thd->cuted_fields=0L;
thd_proc_info(thd, "updating main table"); 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); tables_to_update= get_table_map(fields);
if (!tables_to_update) if (!tables_to_update)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment