Skip to content

Commit aebb103

Browse files
committed
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().
1 parent 0e401bf commit aebb103

File tree

8 files changed

+63
-17
lines changed

8 files changed

+63
-17
lines changed

mysql-test/suite/federated/federated_maybe_16324629.result

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ insert into t1 values (3, 3), (7, 7);
1313
delete t1 from t1 where a = 3;
1414
select * from t1;
1515
a b
16-
3 3
1716
7 7
1817
drop table t1;
1918
connection slave;

mysql-test/suite/vcol/r/update.result

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,3 +43,14 @@ select * from t1;
4343
a b c
4444
b 2 b
4545
drop table t1;
46+
create table t (a int primary key, b int, c int as (b), index (c));
47+
insert t (a,b) values (9,0);
48+
create table t2 select * from t;
49+
update t, t2 set t.b=10 where t.a=t2.a;
50+
check table t;
51+
Table Op Msg_type Msg_text
52+
test.t check status OK
53+
select * from t;
54+
a b c
55+
9 10 10
56+
drop table t, t2;

mysql-test/suite/vcol/t/update.test

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,3 +55,13 @@ replace t1 set a = 'a',b =1;
5555
insert t1 (a,b) values ('a', 1) on duplicate key update a='b', b=2;
5656
select * from t1;
5757
drop table t1;
58+
59+
#
60+
# multi-UPDATE and const tables
61+
#
62+
create table t (a int primary key, b int, c int as (b), index (c));
63+
insert t (a,b) values (9,0);
64+
create table t2 select * from t;
65+
update t, t2 set t.b=10 where t.a=t2.a;
66+
check table t; select * from t;
67+
drop table t, t2;

sql/sql_class.h

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4455,6 +4455,9 @@ class select_result :public select_result_sink
44554455
#endif
44564456
virtual void update_used_tables() {}
44574457

4458+
/* this method is called just before the first row of the table can be read */
4459+
virtual void prepare_to_read_rows() {}
4460+
44584461
void reset_offset_limit()
44594462
{
44604463
unit->offset_limit_cnt= 0;
@@ -5301,11 +5304,9 @@ class multi_delete :public select_result_interceptor
53015304
int do_deletes();
53025305
int do_table_deletes(TABLE *table, SORT_INFO *sort_info, bool ignore);
53035306
bool send_eof();
5304-
inline ha_rows num_deleted()
5305-
{
5306-
return deleted;
5307-
}
5307+
inline ha_rows num_deleted() const { return deleted; }
53085308
virtual void abort_result_set();
5309+
void prepare_to_read_rows();
53095310
};
53105311

53115312

@@ -5349,16 +5350,11 @@ class multi_update :public select_result_interceptor
53495350
bool initialize_tables (JOIN *join);
53505351
int do_updates();
53515352
bool send_eof();
5352-
inline ha_rows num_found()
5353-
{
5354-
return found;
5355-
}
5356-
inline ha_rows num_updated()
5357-
{
5358-
return updated;
5359-
}
5353+
inline ha_rows num_found() const { return found; }
5354+
inline ha_rows num_updated() const { return updated; }
53605355
virtual void abort_result_set();
53615356
void update_used_tables();
5357+
void prepare_to_read_rows();
53625358
};
53635359

53645360
class my_var : public Sql_alloc {

sql/sql_delete.cc

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -924,6 +924,15 @@ multi_delete::prepare(List<Item> &values, SELECT_LEX_UNIT *u)
924924
DBUG_RETURN(0);
925925
}
926926

927+
void multi_delete::prepare_to_read_rows()
928+
{
929+
/* see multi_update::prepare_to_read_rows() */
930+
for (TABLE_LIST *walk= delete_tables; walk; walk= walk->next_local)
931+
{
932+
TABLE_LIST *tbl= walk->correspondent_table->find_table_for_update();
933+
tbl->table->mark_columns_needed_for_delete();
934+
}
935+
}
927936

928937
bool
929938
multi_delete::initialize_tables(JOIN *join)
@@ -953,7 +962,6 @@ multi_delete::initialize_tables(JOIN *join)
953962
}
954963
}
955964

956-
957965
walk= delete_tables;
958966

959967
for (JOIN_TAB *tab= first_linear_tab(join, WITHOUT_BUSH_ROOTS,
@@ -977,7 +985,6 @@ multi_delete::initialize_tables(JOIN *join)
977985
normal_tables= 1;
978986
tbl->prepare_triggers_for_delete_stmt_or_event();
979987
tbl->prepare_for_position();
980-
tbl->mark_columns_needed_for_delete();
981988
}
982989
else if ((tab->type != JT_SYSTEM && tab->type != JT_CONST) &&
983990
walk == delete_tables)

sql/sql_lex.cc

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4122,6 +4122,15 @@ void SELECT_LEX::update_used_tables()
41224122
TABLE *tab= tl->table;
41234123
tab->covering_keys= tab->s->keys_for_keyread;
41244124
tab->covering_keys.intersect(tab->keys_in_use_for_query);
4125+
/*
4126+
View/derived was merged. Need to recalculate read_set/vcol_set
4127+
bitmaps here. For example:
4128+
CREATE VIEW v1 AS SELECT f1,f2,f3 FROM t1;
4129+
SELECT f1 FROM v1;
4130+
Initially, the view definition will put all f1,f2,f3 in the
4131+
read_set for t1. But after the view is merged, only f1 should
4132+
be in the read_set.
4133+
*/
41254134
bitmap_clear_all(tab->read_set);
41264135
if (tab->vcol_set)
41274136
bitmap_clear_all(tab->vcol_set);

sql/sql_select.cc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1466,6 +1466,7 @@ JOIN::optimize_inner()
14661466

14671467
/* Calculate how to do the join */
14681468
THD_STAGE_INFO(thd, stage_statistics);
1469+
result->prepare_to_read_rows();
14691470
if (make_join_statistics(this, select_lex->leaf_tables, &keyuse) ||
14701471
thd->is_fatal_error)
14711472
{

sql/sql_update.cc

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1800,6 +1800,21 @@ void multi_update::update_used_tables()
18001800
}
18011801
}
18021802

1803+
void multi_update::prepare_to_read_rows()
1804+
{
1805+
/*
1806+
update column maps now. it cannot be done in ::prepare() before the
1807+
optimizer, because the optimize might reset them (in
1808+
SELECT_LEX::update_used_tables()), it cannot be done in
1809+
::initialize_tables() after the optimizer, because the optimizer
1810+
might read rows from const tables
1811+
*/
1812+
1813+
for (TABLE_LIST *tl= update_tables; tl; tl= tl->next_local)
1814+
tl->table->mark_columns_needed_for_update();
1815+
}
1816+
1817+
18031818
/*
18041819
Check if table is safe to update on fly
18051820
@@ -1916,12 +1931,10 @@ multi_update::initialize_tables(JOIN *join)
19161931
{
19171932
if (safe_update_on_fly(thd, join->join_tab, table_ref, all_tables))
19181933
{
1919-
table->mark_columns_needed_for_update();
19201934
table_to_update= table; // Update table on the fly
19211935
continue;
19221936
}
19231937
}
1924-
table->mark_columns_needed_for_update();
19251938
table->prepare_for_position();
19261939

19271940
/*

0 commit comments

Comments
 (0)