mirror of
https://github.com/MariaDB/server.git
synced 2025-07-29 05:21:33 +03:00
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().
This commit is contained in:
@ -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;
|
|||||||
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;
|
|||||||
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 @@ public:
|
|||||||
#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 @@ public:
|
|||||||
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 @@ public:
|
|||||||
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)
|
|||||||
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)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
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)
|
|||||||
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()
|
|||||||
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()
|
|||||||
|
|
||||||
/* 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()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)
|
|||||||
{
|
{
|
||||||
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();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
Reference in New Issue
Block a user