Commit aebb1038 authored by Sergei Golubchik's avatar Sergei Golubchik

bugfix: multi-UPDATE, vcols, const tables

multi-update was setting up read_set/vcol_set in
multi_update::initialize_tables() that is invoked after
the optimizer (JOIN::optimize_inner()). But some rows - if they're from
const tables - will be read already in the optimizer, and these rows
will not have all necessary column/vcol values.

* multi_update::initialize_tables() uses results from the optimizer
  and cannot be moved to be called earlier.
* multi_update::prepare() is called before the optimizer, but
  it cannot set up read_set/vcol_set, because the optimizer
  might reset them (see SELECT_LEX::update_used_tables()).

As a fix I've added a new method, select_result::prepare_to_read_rows(),
it's called from inside the optimizer just before make_join_statistics().
parent 0e401bf7
...@@ -13,7 +13,6 @@ insert into t1 values (3, 3), (7, 7); ...@@ -13,7 +13,6 @@ insert into t1 values (3, 3), (7, 7);
delete t1 from t1 where a = 3; delete t1 from t1 where a = 3;
select * from t1; select * from t1;
a b a b
3 3
7 7 7 7
drop table t1; drop table t1;
connection slave; connection slave;
......
...@@ -43,3 +43,14 @@ select * from t1; ...@@ -43,3 +43,14 @@ select * from t1;
a b c a b c
b 2 b b 2 b
drop table t1; drop table t1;
create table t (a int primary key, b int, c int as (b), index (c));
insert t (a,b) values (9,0);
create table t2 select * from t;
update t, t2 set t.b=10 where t.a=t2.a;
check table t;
Table Op Msg_type Msg_text
test.t check status OK
select * from t;
a b c
9 10 10
drop table t, t2;
...@@ -55,3 +55,13 @@ replace t1 set a = 'a',b =1; ...@@ -55,3 +55,13 @@ replace t1 set a = 'a',b =1;
insert t1 (a,b) values ('a', 1) on duplicate key update a='b', b=2; insert t1 (a,b) values ('a', 1) on duplicate key update a='b', b=2;
select * from t1; select * from t1;
drop table t1; drop table t1;
#
# multi-UPDATE and const tables
#
create table t (a int primary key, b int, c int as (b), index (c));
insert t (a,b) values (9,0);
create table t2 select * from t;
update t, t2 set t.b=10 where t.a=t2.a;
check table t; select * from t;
drop table t, t2;
...@@ -4455,6 +4455,9 @@ class select_result :public select_result_sink ...@@ -4455,6 +4455,9 @@ class select_result :public select_result_sink
#endif #endif
virtual void update_used_tables() {} virtual void update_used_tables() {}
/* this method is called just before the first row of the table can be read */
virtual void prepare_to_read_rows() {}
void reset_offset_limit() void reset_offset_limit()
{ {
unit->offset_limit_cnt= 0; unit->offset_limit_cnt= 0;
...@@ -5301,11 +5304,9 @@ class multi_delete :public select_result_interceptor ...@@ -5301,11 +5304,9 @@ class multi_delete :public select_result_interceptor
int do_deletes(); int do_deletes();
int do_table_deletes(TABLE *table, SORT_INFO *sort_info, bool ignore); int do_table_deletes(TABLE *table, SORT_INFO *sort_info, bool ignore);
bool send_eof(); bool send_eof();
inline ha_rows num_deleted() inline ha_rows num_deleted() const { return deleted; }
{
return deleted;
}
virtual void abort_result_set(); virtual void abort_result_set();
void prepare_to_read_rows();
}; };
...@@ -5349,16 +5350,11 @@ class multi_update :public select_result_interceptor ...@@ -5349,16 +5350,11 @@ class multi_update :public select_result_interceptor
bool initialize_tables (JOIN *join); bool initialize_tables (JOIN *join);
int do_updates(); int do_updates();
bool send_eof(); bool send_eof();
inline ha_rows num_found() inline ha_rows num_found() const { return found; }
{ inline ha_rows num_updated() const { return updated; }
return found;
}
inline ha_rows num_updated()
{
return updated;
}
virtual void abort_result_set(); virtual void abort_result_set();
void update_used_tables(); void update_used_tables();
void prepare_to_read_rows();
}; };
class my_var : public Sql_alloc { class my_var : public Sql_alloc {
......
...@@ -924,6 +924,15 @@ multi_delete::prepare(List<Item> &values, SELECT_LEX_UNIT *u) ...@@ -924,6 +924,15 @@ multi_delete::prepare(List<Item> &values, SELECT_LEX_UNIT *u)
DBUG_RETURN(0); DBUG_RETURN(0);
} }
void multi_delete::prepare_to_read_rows()
{
/* see multi_update::prepare_to_read_rows() */
for (TABLE_LIST *walk= delete_tables; walk; walk= walk->next_local)
{
TABLE_LIST *tbl= walk->correspondent_table->find_table_for_update();
tbl->table->mark_columns_needed_for_delete();
}
}
bool bool
multi_delete::initialize_tables(JOIN *join) multi_delete::initialize_tables(JOIN *join)
...@@ -953,7 +962,6 @@ multi_delete::initialize_tables(JOIN *join) ...@@ -953,7 +962,6 @@ multi_delete::initialize_tables(JOIN *join)
} }
} }
walk= delete_tables; walk= delete_tables;
for (JOIN_TAB *tab= first_linear_tab(join, WITHOUT_BUSH_ROOTS, for (JOIN_TAB *tab= first_linear_tab(join, WITHOUT_BUSH_ROOTS,
...@@ -977,7 +985,6 @@ multi_delete::initialize_tables(JOIN *join) ...@@ -977,7 +985,6 @@ multi_delete::initialize_tables(JOIN *join)
normal_tables= 1; normal_tables= 1;
tbl->prepare_triggers_for_delete_stmt_or_event(); tbl->prepare_triggers_for_delete_stmt_or_event();
tbl->prepare_for_position(); tbl->prepare_for_position();
tbl->mark_columns_needed_for_delete();
} }
else if ((tab->type != JT_SYSTEM && tab->type != JT_CONST) && else if ((tab->type != JT_SYSTEM && tab->type != JT_CONST) &&
walk == delete_tables) walk == delete_tables)
......
...@@ -4122,6 +4122,15 @@ void SELECT_LEX::update_used_tables() ...@@ -4122,6 +4122,15 @@ void SELECT_LEX::update_used_tables()
TABLE *tab= tl->table; TABLE *tab= tl->table;
tab->covering_keys= tab->s->keys_for_keyread; tab->covering_keys= tab->s->keys_for_keyread;
tab->covering_keys.intersect(tab->keys_in_use_for_query); tab->covering_keys.intersect(tab->keys_in_use_for_query);
/*
View/derived was merged. Need to recalculate read_set/vcol_set
bitmaps here. For example:
CREATE VIEW v1 AS SELECT f1,f2,f3 FROM t1;
SELECT f1 FROM v1;
Initially, the view definition will put all f1,f2,f3 in the
read_set for t1. But after the view is merged, only f1 should
be in the read_set.
*/
bitmap_clear_all(tab->read_set); bitmap_clear_all(tab->read_set);
if (tab->vcol_set) if (tab->vcol_set)
bitmap_clear_all(tab->vcol_set); bitmap_clear_all(tab->vcol_set);
......
...@@ -1466,6 +1466,7 @@ JOIN::optimize_inner() ...@@ -1466,6 +1466,7 @@ JOIN::optimize_inner()
/* Calculate how to do the join */ /* Calculate how to do the join */
THD_STAGE_INFO(thd, stage_statistics); THD_STAGE_INFO(thd, stage_statistics);
result->prepare_to_read_rows();
if (make_join_statistics(this, select_lex->leaf_tables, &keyuse) || if (make_join_statistics(this, select_lex->leaf_tables, &keyuse) ||
thd->is_fatal_error) thd->is_fatal_error)
{ {
......
...@@ -1800,6 +1800,21 @@ void multi_update::update_used_tables() ...@@ -1800,6 +1800,21 @@ void multi_update::update_used_tables()
} }
} }
void multi_update::prepare_to_read_rows()
{
/*
update column maps now. it cannot be done in ::prepare() before the
optimizer, because the optimize might reset them (in
SELECT_LEX::update_used_tables()), it cannot be done in
::initialize_tables() after the optimizer, because the optimizer
might read rows from const tables
*/
for (TABLE_LIST *tl= update_tables; tl; tl= tl->next_local)
tl->table->mark_columns_needed_for_update();
}
/* /*
Check if table is safe to update on fly Check if table is safe to update on fly
...@@ -1916,12 +1931,10 @@ multi_update::initialize_tables(JOIN *join) ...@@ -1916,12 +1931,10 @@ multi_update::initialize_tables(JOIN *join)
{ {
if (safe_update_on_fly(thd, join->join_tab, table_ref, all_tables)) if (safe_update_on_fly(thd, join->join_tab, table_ref, all_tables))
{ {
table->mark_columns_needed_for_update();
table_to_update= table; // Update table on the fly table_to_update= table; // Update table on the fly
continue; continue;
} }
} }
table->mark_columns_needed_for_update();
table->prepare_for_position(); table->prepare_for_position();
/* /*
......
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