Skip to content

Commit 08c7ab4

Browse files
committed
MDEV-24176 Server crashes after insert in the table with virtual
column generated using date_format() and if() vcol_info->expr is allocated on expr_arena at parsing stage. Since expr item is allocated on expr_arena all its containee items must be allocated on expr_arena too. Otherwise fix_session_expr() will encounter prematurely freed item. When table is reopened from cache vcol_info contains stale expression. We refresh expression via TABLE::vcol_fix_exprs() but first we must prepare a proper context (Vcol_expr_context) which meets some requirements: 1. As noted above expr update must be done on expr_arena as there may be new items created. It was a bug in fix_session_expr_for_read() and was just not reproduced because of no second refix. Now refix is done for more cases so it does reproduce. Tests affected: vcol.binlog 2. Also name resolution context must be narrowed to the single table. Tested by: vcol.update main.default vcol.vcol_syntax gcol.gcol_bugfixes 3. sql_mode must be clean and not fail expr update. sql_mode such as MODE_NO_BACKSLASH_ESCAPES, MODE_NO_ZERO_IN_DATE, etc must not affect vcol expression update. If the table was created successfully any further evaluation must not fail. Tests affected: main.func_like Reviewed by: Sergei Golubchik <serg@mariadb.org>
1 parent c02ebf3 commit 08c7ab4

File tree

10 files changed

+342
-87
lines changed

10 files changed

+342
-87
lines changed

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

Lines changed: 92 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,97 @@ create table t1 (a int, v_a int generated always as (a));
9494
update t1 as x set a = 1;
9595
alter table t1 force;
9696
drop table t1;
97+
create table t1 (
98+
id int not null auto_increment primary key,
99+
order_date_time datetime not null,
100+
order_date date generated always as (convert(order_date_time, date)),
101+
language_id binary(16) null
102+
);
103+
update t1 as tx set order_date= null;
104+
alter table t1 modify column language_id binary(16) not null;
105+
drop table t1;
97106
#
98-
# End of 10.2 tests
107+
# MDEV-24176 Server crashes after insert in the table with virtual column generated using date_format() and if()
99108
#
109+
create table t1 (d1 date not null, d2 date not null,
110+
gd text as (concat(d1,if(d1 <> d2, date_format(d2, 'to %y-%m-%d '), ''))) );
111+
insert into t1(d1,d2) values
112+
('2020-09-01','2020-09-01'),('2020-05-01','2020-09-01');
113+
select * from t1;
114+
d1 d2 gd
115+
2020-09-01 2020-09-01 2020-09-01
116+
2020-05-01 2020-09-01 2020-05-01to 20-09-01
117+
drop table t1;
118+
# MDEV-25772 (duplicate) and LOCK TABLES case
119+
create table t1 (d1 datetime , v_d1 tinyint(1) as (d1 < curdate()));
120+
insert into t1 (d1) values ('2021-09-11 08:38:23'), ('2021-09-01 08:38:23');
121+
lock tables t1 write;
122+
select * from t1 where v_d1=1;
123+
d1 v_d1
124+
2021-09-11 08:38:23 1
125+
2021-09-01 08:38:23 1
126+
select * from t1;
127+
d1 v_d1
128+
2021-09-11 08:38:23 1
129+
2021-09-01 08:38:23 1
130+
unlock tables;
131+
drop table t1;
132+
# MDEV-26432 (duplicate)
133+
create table t1 (v2 int, v1 int as ((user() like 'x'))) ;
134+
select 1 from t1 where v1=1 ;
135+
1
136+
select * from t1;
137+
v2 v1
138+
drop table t1;
139+
create table t1 (v2 int as ( user () like 'x'));
140+
select 1 from t1 order by v2 ;
141+
1
142+
alter table t1 add i int;
143+
drop table t1;
144+
# MDEV-26437 (duplicate)
145+
create table v0 (v2 int not null,
146+
v1 bigint as (case 'x' when current_user() then v2 end));
147+
select v2 as v3 from v0 where v1 like 'x' escape 'x';
148+
v3
149+
insert into v0 (v2) values (-128);
150+
drop table v0;
151+
create table t1 (vi int as (case 'x' when current_user() then 1 end));
152+
select 1 from t1 where vi=1;
153+
1
154+
show create table t1;
155+
Table Create Table
156+
t1 CREATE TABLE `t1` (
157+
`vi` int(11) GENERATED ALWAYS AS (case 'x' when current_user() then 1 end) VIRTUAL
158+
) ENGINE=MyISAM DEFAULT CHARSET=latin1
159+
drop table t1;
160+
create table t1 (vi int as (case 'x' when current_user() then 1 end));
161+
select 1 from t1 where vi=1;
162+
1
163+
select 1 from t1 where vi=1;
164+
1
165+
drop table t1;
166+
# MDEV-28092 (duplicate)
167+
create table t1 (b timestamp, a int as (1 in (dayofmonth (b between 'x' and current_user) = b)));
168+
insert into t1(b) values ('2022-03-17 14:55:37');
169+
select 1 from t1 x natural join t1;
170+
1
171+
1
172+
Warnings:
173+
Warning 1292 Incorrect datetime value: 'x'
174+
Warning 1292 Incorrect datetime value: 'root@localhost'
175+
Warning 1292 Incorrect datetime value: 'x'
176+
Warning 1292 Incorrect datetime value: 'root@localhost'
177+
drop table t1;
178+
# MDEV-28089 (duplicate)
179+
create table t1 (a int , b date as (1 in ('x' ,(database () = 'x' is null) ))) ;
180+
select b from t1;
181+
b
182+
select a from t1 order by 'x' = b;
183+
a
184+
drop table t1;
185+
create table t1 (a int , b date as (1 in ('x' ,(database ()) ))) ;
186+
select b from t1;
187+
b
188+
select a from t1 order by 'x' = b;
189+
a
190+
drop table t1;

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

Lines changed: 82 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,88 @@ update t1 as x set a = 1;
7777
alter table t1 force;
7878
drop table t1;
7979

80+
create table t1 (
81+
id int not null auto_increment primary key,
82+
order_date_time datetime not null,
83+
order_date date generated always as (convert(order_date_time, date)),
84+
language_id binary(16) null
85+
);
86+
87+
update t1 as tx set order_date= null;
88+
alter table t1 modify column language_id binary(16) not null;
89+
# Cleanup
90+
drop table t1;
8091

8192
--echo #
82-
--echo # End of 10.2 tests
93+
--echo # MDEV-24176 Server crashes after insert in the table with virtual column generated using date_format() and if()
8394
--echo #
95+
create table t1 (d1 date not null, d2 date not null,
96+
gd text as (concat(d1,if(d1 <> d2, date_format(d2, 'to %y-%m-%d '), ''))) );
97+
98+
insert into t1(d1,d2) values
99+
('2020-09-01','2020-09-01'),('2020-05-01','2020-09-01');
100+
select * from t1;
101+
102+
drop table t1;
103+
104+
--echo # MDEV-25772 (duplicate) and LOCK TABLES case
105+
create table t1 (d1 datetime , v_d1 tinyint(1) as (d1 < curdate()));
106+
insert into t1 (d1) values ('2021-09-11 08:38:23'), ('2021-09-01 08:38:23');
107+
108+
lock tables t1 write;
109+
select * from t1 where v_d1=1;
110+
select * from t1;
111+
unlock tables;
112+
113+
drop table t1;
114+
115+
--echo # MDEV-26432 (duplicate)
116+
create table t1 (v2 int, v1 int as ((user() like 'x'))) ;
117+
select 1 from t1 where v1=1 ;
118+
select * from t1;
119+
120+
drop table t1;
121+
122+
create table t1 (v2 int as ( user () like 'x'));
123+
select 1 from t1 order by v2 ;
124+
alter table t1 add i int;
125+
drop table t1;
126+
127+
--echo # MDEV-26437 (duplicate)
128+
create table v0 (v2 int not null,
129+
v1 bigint as (case 'x' when current_user() then v2 end));
130+
131+
select v2 as v3 from v0 where v1 like 'x' escape 'x';
132+
insert into v0 (v2) values (-128);
133+
134+
drop table v0;
135+
136+
create table t1 (vi int as (case 'x' when current_user() then 1 end));
137+
select 1 from t1 where vi=1;
138+
show create table t1;
139+
140+
drop table t1;
141+
142+
create table t1 (vi int as (case 'x' when current_user() then 1 end));
143+
select 1 from t1 where vi=1;
144+
select 1 from t1 where vi=1;
145+
146+
drop table t1;
147+
148+
--echo # MDEV-28092 (duplicate)
149+
create table t1 (b timestamp, a int as (1 in (dayofmonth (b between 'x' and current_user) = b)));
150+
insert into t1(b) values ('2022-03-17 14:55:37');
151+
152+
select 1 from t1 x natural join t1;
153+
drop table t1;
154+
155+
--echo # MDEV-28089 (duplicate)
156+
create table t1 (a int , b date as (1 in ('x' ,(database () = 'x' is null) ))) ;
157+
select b from t1;
158+
select a from t1 order by 'x' = b;
159+
drop table t1;
160+
161+
create table t1 (a int , b date as (1 in ('x' ,(database ()) ))) ;
162+
select b from t1;
163+
select a from t1 order by 'x' = b;
164+
drop table t1;

sql/event_db_repository.cc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -610,6 +610,8 @@ Event_db_repository::open_event_table(THD *thd, enum thr_lock_type lock_type,
610610

611611
*table= tables.table;
612612
tables.table->use_all_columns();
613+
/* NOTE: &tables pointer will be invalid after return */
614+
tables.table->pos_in_table_list= NULL;
613615

614616
if (table_intact.check(*table, &event_table_def))
615617
{

sql/field.cc

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2416,6 +2416,11 @@ int Field::set_default()
24162416
if (default_value)
24172417
{
24182418
Query_arena backup_arena;
2419+
/*
2420+
TODO: this may impose memory leak until table flush.
2421+
See comment in
2422+
TABLE::update_virtual_fields(handler *, enum_vcol_update_mode).
2423+
*/
24192424
table->in_use->set_n_backup_active_arena(table->expr_arena, &backup_arena);
24202425
int rc= default_value->expr->save_in_field(this, 0);
24212426
table->in_use->restore_active_arena(table->expr_arena, &backup_arena);

sql/field.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -616,9 +616,13 @@ class Virtual_column_info: public Sql_alloc
616616
{
617617
in_partitioning_expr= TRUE;
618618
}
619+
bool need_refix() const
620+
{
621+
return flags & VCOL_SESSION_FUNC;
622+
}
619623
bool fix_expr(THD *thd);
620624
bool fix_session_expr(THD *thd);
621-
bool fix_session_expr_for_read(THD *thd, Field *field);
625+
bool cleanup_session_expr();
622626
bool fix_and_check_expr(THD *thd, TABLE *table);
623627
inline bool is_equal(const Virtual_column_info* vcol) const;
624628
inline void print(String*);

sql/item.cc

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -835,7 +835,6 @@ bool Item_ident::remove_dependence_processor(void * arg)
835835
DBUG_RETURN(0);
836836
}
837837

838-
839838
bool Item_ident::collect_outer_ref_processor(void *param)
840839
{
841840
Collect_deps_prm *prm= (Collect_deps_prm *)param;
@@ -6376,9 +6375,6 @@ bool Item_field::fix_fields(THD *thd, Item **reference)
63766375
}
63776376
#endif
63786377
fixed= 1;
6379-
if (field->vcol_info &&
6380-
field->vcol_info->fix_session_expr_for_read(thd, field))
6381-
goto error;
63826378
if (thd->variables.sql_mode & MODE_ONLY_FULL_GROUP_BY &&
63836379
!outer_fixed && !thd->lex->in_sum_func &&
63846380
select &&
@@ -9500,12 +9496,6 @@ bool Item_default_value::fix_fields(THD *thd, Item **items)
95009496
uchar *newptr= (uchar*) thd->alloc(1+def_field->pack_length());
95019497
if (!newptr)
95029498
goto error;
9503-
/*
9504-
Even if DEFAULT() do not read tables fields, the default value
9505-
expression can do it.
9506-
*/
9507-
if (def_field->default_value->fix_session_expr_for_read(thd, def_field))
9508-
goto error;
95099499
if (should_mark_column(thd->column_usage))
95109500
def_field->default_value->expr->update_used_tables();
95119501
def_field->move_field(newptr+1, def_field->maybe_null() ? newptr : 0, 1);

sql/item.h

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2925,10 +2925,8 @@ class Item_ident :public Item_result_field
29252925
const char *table_name;
29262926
LEX_CSTRING field_name;
29272927
/*
2928-
NOTE: came from TABLE::alias_name_used and this is only a hint! It works
2929-
only in need_correct_ident() condition. On other cases it is FALSE even if
2930-
table_name is alias! It cannot be TRUE in these cases, lots of spaghetti
2931-
logic depends on that.
2928+
NOTE: came from TABLE::alias_name_used and this is only a hint!
2929+
See comment for TABLE::alias_name_used.
29322930
*/
29332931
bool alias_name_used; /* true if item was resolved against alias */
29342932
/*

sql/sql_base.cc

Lines changed: 7 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -749,6 +749,8 @@ void close_thread_tables(THD *thd)
749749
/* Table might be in use by some outer statement. */
750750
DBUG_PRINT("tcache", ("table: '%s' query_id: %lu",
751751
table->s->table_name.str, (ulong) table->query_id));
752+
if (thd->locked_tables_mode)
753+
table->vcol_cleanup_expr(thd);
752754
if (thd->locked_tables_mode <= LTM_LOCK_TABLES ||
753755
table->query_id == thd->query_id)
754756
{
@@ -884,6 +886,8 @@ void close_thread_table(THD *thd, TABLE **table_ptr)
884886
table->s->db.str,
885887
table->s->table_name.str,
886888
MDL_SHARED));
889+
890+
table->vcol_cleanup_expr(thd);
887891
table->mdl_ticket= NULL;
888892

889893
if (table->file)
@@ -1610,6 +1614,7 @@ bool open_table(THD *thd, TABLE_LIST *table_list, Open_table_context *ot_ctx)
16101614
MDL_ticket *mdl_ticket;
16111615
TABLE_SHARE *share;
16121616
uint gts_flags;
1617+
bool from_share= false;
16131618
#ifdef WITH_PARTITION_STORAGE_ENGINE
16141619
int part_names_error=0;
16151620
#endif
@@ -2021,6 +2026,7 @@ bool open_table(THD *thd, TABLE_LIST *table_list, Open_table_context *ot_ctx)
20212026

20222027
/* Add table to the share's used tables list. */
20232028
tc_add_table(thd, table);
2029+
from_share= true;
20242030
}
20252031

20262032
table->mdl_ticket= mdl_ticket;
@@ -2040,7 +2046,7 @@ bool open_table(THD *thd, TABLE_LIST *table_list, Open_table_context *ot_ctx)
20402046
table_list->updatable= 1; // It is not derived table nor non-updatable VIEW
20412047
table_list->table= table;
20422048

2043-
if (table->vcol_fix_exprs(thd))
2049+
if (!from_share && table->vcol_fix_expr(thd))
20442050
goto err_lock;
20452051

20462052
#ifdef WITH_PARTITION_STORAGE_ENGINE
@@ -5293,46 +5299,6 @@ static void mark_real_tables_as_free_for_reuse(TABLE_LIST *table_list)
52935299
}
52945300
}
52955301

5296-
bool TABLE::vcol_fix_exprs(THD *thd)
5297-
{
5298-
if (pos_in_table_list->placeholder() || !s->vcols_need_refixing ||
5299-
pos_in_table_list->lock_type < TL_WRITE_ALLOW_WRITE)
5300-
return false;
5301-
5302-
DBUG_ASSERT(pos_in_table_list != thd->lex->first_not_own_table());
5303-
5304-
bool result= true;
5305-
Security_context *save_security_ctx= thd->security_ctx;
5306-
Query_arena *stmt_backup= thd->stmt_arena;
5307-
if (thd->stmt_arena->is_conventional())
5308-
thd->stmt_arena= expr_arena;
5309-
5310-
if (pos_in_table_list->security_ctx)
5311-
thd->security_ctx= pos_in_table_list->security_ctx;
5312-
5313-
5314-
for (Field **vf= vfield; vf && *vf; vf++)
5315-
if ((*vf)->vcol_info->fix_session_expr(thd))
5316-
goto end;
5317-
5318-
for (Field **df= default_field; df && *df; df++)
5319-
if ((*df)->default_value &&
5320-
(*df)->default_value->fix_session_expr(thd))
5321-
goto end;
5322-
5323-
for (Virtual_column_info **cc= check_constraints; cc && *cc; cc++)
5324-
if ((*cc)->fix_session_expr(thd))
5325-
goto end;
5326-
5327-
result= false;
5328-
5329-
end:
5330-
thd->security_ctx= save_security_ctx;
5331-
thd->stmt_arena= stmt_backup;
5332-
5333-
return result;
5334-
}
5335-
53365302

53375303
/**
53385304
Lock all tables in a list.

0 commit comments

Comments
 (0)