Skip to content

Commit f1f9284

Browse files
committed
MDEV-34046 Parameterized PS converts error to warning, causes
replication problems DELETE HISTORY did not process parameterized PS properly as the history expression was checked on prepare stage when the parameters was not yet substituted. In that case check_units() succeeded as there is no invalid type: Item_param has type_handler_null which is inherited from string type and this is valid type for history expression. The warning was thrown when the expression was evaluated for comparison on delete execution (when the parameter was already substituted). The fix postpones check_units() until the first PS execution. We have to postpone where conditions processing until the first execution and update select_lex.where on every execution as it is reset to the state after prepare.
1 parent bff9b1e commit f1f9284

File tree

6 files changed

+357
-5
lines changed

6 files changed

+357
-5
lines changed

mysql-test/suite/versioning/r/delete_history.result

Lines changed: 213 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,3 +251,216 @@ t CREATE TABLE `t` (
251251
PARTITIONS 2
252252
drop table t;
253253
# End of 10.9 tests
254+
#
255+
# MDEV-34046 Parameterized PS converts error to warning, causes replication problems
256+
#
257+
create table t (a int) with system versioning;
258+
set timestamp= unix_timestamp('2000-01-01 00:00:00');
259+
insert into t values (1), (100);
260+
delete history from t before system_time @@timestamp;
261+
ERROR HY000: Illegal parameter data type double for operation 'FOR SYSTEM_TIME'
262+
execute immediate "delete history from t before system_time @@timestamp";
263+
ERROR HY000: Illegal parameter data type double for operation 'FOR SYSTEM_TIME'
264+
execute immediate "delete history from t before system_time ?" using @@timestamp;
265+
ERROR HY000: Illegal parameter data type double for operation 'FOR SYSTEM_TIME'
266+
set @ts1= '2000-01-01 00:00:01';
267+
set timestamp= unix_timestamp(@ts1);
268+
update t set a= a + 1 where a < 100;
269+
set @ts2= '2000-01-01 00:00:02';
270+
set timestamp= unix_timestamp(@ts2);
271+
update t set a= a + 1 where a < 100;
272+
set @ts3= '2000-01-01 00:00:03';
273+
set timestamp= unix_timestamp(@ts3);
274+
select *, row_start, row_end from t for system_time all order by a;
275+
a row_start row_end
276+
1 2000-01-01 00:00:00.000000 2000-01-01 00:00:01.000000
277+
2 2000-01-01 00:00:01.000000 2000-01-01 00:00:02.000000
278+
3 2000-01-01 00:00:02.000000 2038-01-19 03:14:07.999999
279+
100 2000-01-01 00:00:00.000000 2038-01-19 03:14:07.999999
280+
execute immediate "delete history from t before system_time @ts1";
281+
select *, row_start, row_end from t for system_time all order by a;
282+
a row_start row_end
283+
1 2000-01-01 00:00:00.000000 2000-01-01 00:00:01.000000
284+
2 2000-01-01 00:00:01.000000 2000-01-01 00:00:02.000000
285+
3 2000-01-01 00:00:02.000000 2038-01-19 03:14:07.999999
286+
100 2000-01-01 00:00:00.000000 2038-01-19 03:14:07.999999
287+
execute immediate "delete history from t before system_time @ts2";
288+
select *, row_start, row_end from t for system_time all order by a;
289+
a row_start row_end
290+
2 2000-01-01 00:00:01.000000 2000-01-01 00:00:02.000000
291+
3 2000-01-01 00:00:02.000000 2038-01-19 03:14:07.999999
292+
100 2000-01-01 00:00:00.000000 2038-01-19 03:14:07.999999
293+
execute immediate "delete history from t before system_time ?" using @ts3;
294+
select *, row_start, row_end from t for system_time all order by a;
295+
a row_start row_end
296+
3 2000-01-01 00:00:02.000000 2038-01-19 03:14:07.999999
297+
100 2000-01-01 00:00:00.000000 2038-01-19 03:14:07.999999
298+
execute immediate "delete history from t before system_time ?" using @ts3;
299+
select *, row_start, row_end from t for system_time all order by a;
300+
a row_start row_end
301+
3 2000-01-01 00:00:02.000000 2038-01-19 03:14:07.999999
302+
100 2000-01-01 00:00:00.000000 2038-01-19 03:14:07.999999
303+
update t set a= a + 1 where a < 100;
304+
set @ts4= '2000-01-01 00:00:04';
305+
set timestamp= unix_timestamp(@ts4);
306+
update t set a= a + 1 where a < 100;
307+
select *, row_start, row_end from t for system_time all order by a;
308+
a row_start row_end
309+
3 2000-01-01 00:00:02.000000 2000-01-01 00:00:03.000000
310+
4 2000-01-01 00:00:03.000000 2000-01-01 00:00:04.000000
311+
5 2000-01-01 00:00:04.000000 2038-01-19 03:14:07.999999
312+
100 2000-01-01 00:00:00.000000 2038-01-19 03:14:07.999999
313+
execute immediate "delete history from t before system_time ?" using '2000-01-01 00:00:04';
314+
select *, row_start, row_end from t for system_time all order by a;
315+
a row_start row_end
316+
4 2000-01-01 00:00:03.000000 2000-01-01 00:00:04.000000
317+
5 2000-01-01 00:00:04.000000 2038-01-19 03:14:07.999999
318+
100 2000-01-01 00:00:00.000000 2038-01-19 03:14:07.999999
319+
set @ts5= '2000-01-01 00:00:05';
320+
set timestamp= unix_timestamp(@ts5);
321+
update t set a= a + 1 where a < 100;
322+
set @ts6= '2000-01-01 00:00:06';
323+
set timestamp= unix_timestamp(@ts6);
324+
update t set a= a + 1 where a < 100;
325+
set @ts7= '2000-01-01 00:00:07';
326+
set timestamp= unix_timestamp(@ts7);
327+
update t set a= a + 1 where a < 100;
328+
select *, row_start, row_end from t for system_time all order by a;
329+
a row_start row_end
330+
4 2000-01-01 00:00:03.000000 2000-01-01 00:00:04.000000
331+
5 2000-01-01 00:00:04.000000 2000-01-01 00:00:05.000000
332+
6 2000-01-01 00:00:05.000000 2000-01-01 00:00:06.000000
333+
7 2000-01-01 00:00:06.000000 2000-01-01 00:00:07.000000
334+
8 2000-01-01 00:00:07.000000 2038-01-19 03:14:07.999999
335+
100 2000-01-01 00:00:00.000000 2038-01-19 03:14:07.999999
336+
prepare stmt from 'delete history from t before system_time ?';
337+
execute stmt using @ts4;
338+
select *, row_start, row_end from t for system_time all order by a;
339+
a row_start row_end
340+
4 2000-01-01 00:00:03.000000 2000-01-01 00:00:04.000000
341+
5 2000-01-01 00:00:04.000000 2000-01-01 00:00:05.000000
342+
6 2000-01-01 00:00:05.000000 2000-01-01 00:00:06.000000
343+
7 2000-01-01 00:00:06.000000 2000-01-01 00:00:07.000000
344+
8 2000-01-01 00:00:07.000000 2038-01-19 03:14:07.999999
345+
100 2000-01-01 00:00:00.000000 2038-01-19 03:14:07.999999
346+
execute stmt using @ts5;
347+
select *, row_start, row_end from t for system_time all order by a;
348+
a row_start row_end
349+
5 2000-01-01 00:00:04.000000 2000-01-01 00:00:05.000000
350+
6 2000-01-01 00:00:05.000000 2000-01-01 00:00:06.000000
351+
7 2000-01-01 00:00:06.000000 2000-01-01 00:00:07.000000
352+
8 2000-01-01 00:00:07.000000 2038-01-19 03:14:07.999999
353+
100 2000-01-01 00:00:00.000000 2038-01-19 03:14:07.999999
354+
execute stmt using @ts6;
355+
select *, row_start, row_end from t for system_time all order by a;
356+
a row_start row_end
357+
6 2000-01-01 00:00:05.000000 2000-01-01 00:00:06.000000
358+
7 2000-01-01 00:00:06.000000 2000-01-01 00:00:07.000000
359+
8 2000-01-01 00:00:07.000000 2038-01-19 03:14:07.999999
360+
100 2000-01-01 00:00:00.000000 2038-01-19 03:14:07.999999
361+
execute stmt using '2000-01-01 00:00:06';
362+
select *, row_start, row_end from t for system_time all order by a;
363+
a row_start row_end
364+
6 2000-01-01 00:00:05.000000 2000-01-01 00:00:06.000000
365+
7 2000-01-01 00:00:06.000000 2000-01-01 00:00:07.000000
366+
8 2000-01-01 00:00:07.000000 2038-01-19 03:14:07.999999
367+
100 2000-01-01 00:00:00.000000 2038-01-19 03:14:07.999999
368+
execute stmt using '2000-01-01 00:00:06.000001';
369+
select *, row_start, row_end from t for system_time all order by a;
370+
a row_start row_end
371+
7 2000-01-01 00:00:06.000000 2000-01-01 00:00:07.000000
372+
8 2000-01-01 00:00:07.000000 2038-01-19 03:14:07.999999
373+
100 2000-01-01 00:00:00.000000 2038-01-19 03:14:07.999999
374+
set @ts8= '2000-01-01 00:00:08';
375+
set timestamp= unix_timestamp(@ts8);
376+
delete from t;
377+
select *, row_start, row_end from t for system_time all order by a;
378+
a row_start row_end
379+
7 2000-01-01 00:00:06.000000 2000-01-01 00:00:07.000000
380+
8 2000-01-01 00:00:07.000000 2000-01-01 00:00:08.000000
381+
100 2000-01-01 00:00:00.000000 2000-01-01 00:00:08.000000
382+
execute immediate "delete history from t before system_time from_unixtime(?)" using @@timestamp;
383+
select *, row_start, row_end from t for system_time all order by a;
384+
a row_start row_end
385+
8 2000-01-01 00:00:07.000000 2000-01-01 00:00:08.000000
386+
100 2000-01-01 00:00:00.000000 2000-01-01 00:00:08.000000
387+
execute stmt using '2020-01-01';
388+
select *, row_start, row_end from t for system_time all order by a;
389+
a row_start row_end
390+
drop prepare stmt;
391+
set timestamp= unix_timestamp('2000-01-01 00:00:00');
392+
insert into t values (1), (100);
393+
set @ts1= '2000-01-01 00:00:01';
394+
set timestamp= unix_timestamp(@ts1);
395+
update t set a= a + 1 where a < 100;
396+
set timestamp= @@timestamp + 1;
397+
set @ts2= @@timestamp;
398+
update t set a= a + 1 where a < 100;
399+
set timestamp= @@timestamp + 1;
400+
update t set a= a + 1 where a < 100;
401+
prepare stmt from 'delete history from t before system_time from_unixtime(? + ?)';
402+
select *, row_start, row_end from t for system_time all order by a;
403+
a row_start row_end
404+
1 2000-01-01 00:00:00.000000 2000-01-01 00:00:01.000000
405+
2 2000-01-01 00:00:01.000000 2000-01-01 00:00:02.000000
406+
3 2000-01-01 00:00:02.000000 2000-01-01 00:00:03.000000
407+
4 2000-01-01 00:00:03.000000 2038-01-19 03:14:07.999999
408+
100 2000-01-01 00:00:00.000000 2038-01-19 03:14:07.999999
409+
execute stmt using @ts1, 0;
410+
Warnings:
411+
Warning 1292 Truncated incorrect DOUBLE value: '2000-01-01 00:00:01'
412+
Warning 1292 Truncated incorrect DOUBLE value: '2000-01-01 00:00:01'
413+
Warning 1292 Truncated incorrect DOUBLE value: '2000-01-01 00:00:01'
414+
Warning 1292 Truncated incorrect DOUBLE value: '2000-01-01 00:00:01'
415+
Warning 1292 Truncated incorrect DOUBLE value: '2000-01-01 00:00:01'
416+
select *, row_start, row_end from t for system_time all order by a;
417+
a row_start row_end
418+
1 2000-01-01 00:00:00.000000 2000-01-01 00:00:01.000000
419+
2 2000-01-01 00:00:01.000000 2000-01-01 00:00:02.000000
420+
3 2000-01-01 00:00:02.000000 2000-01-01 00:00:03.000000
421+
4 2000-01-01 00:00:03.000000 2038-01-19 03:14:07.999999
422+
100 2000-01-01 00:00:00.000000 2038-01-19 03:14:07.999999
423+
execute stmt using @ts2, 0;
424+
select *, row_start, row_end from t for system_time all order by a;
425+
a row_start row_end
426+
2 2000-01-01 00:00:01.000000 2000-01-01 00:00:02.000000
427+
3 2000-01-01 00:00:02.000000 2000-01-01 00:00:03.000000
428+
4 2000-01-01 00:00:03.000000 2038-01-19 03:14:07.999999
429+
100 2000-01-01 00:00:00.000000 2038-01-19 03:14:07.999999
430+
execute stmt using @@timestamp, NULL;
431+
select *, row_start, row_end from t for system_time all order by a;
432+
a row_start row_end
433+
2 2000-01-01 00:00:01.000000 2000-01-01 00:00:02.000000
434+
3 2000-01-01 00:00:02.000000 2000-01-01 00:00:03.000000
435+
4 2000-01-01 00:00:03.000000 2038-01-19 03:14:07.999999
436+
100 2000-01-01 00:00:00.000000 2038-01-19 03:14:07.999999
437+
execute stmt using NULL, NULL;
438+
select *, row_start, row_end from t for system_time all order by a;
439+
a row_start row_end
440+
2 2000-01-01 00:00:01.000000 2000-01-01 00:00:02.000000
441+
3 2000-01-01 00:00:02.000000 2000-01-01 00:00:03.000000
442+
4 2000-01-01 00:00:03.000000 2038-01-19 03:14:07.999999
443+
100 2000-01-01 00:00:00.000000 2038-01-19 03:14:07.999999
444+
execute stmt using @ts2, 1;
445+
select *, row_start, row_end from t for system_time all order by a;
446+
a row_start row_end
447+
3 2000-01-01 00:00:02.000000 2000-01-01 00:00:03.000000
448+
4 2000-01-01 00:00:03.000000 2038-01-19 03:14:07.999999
449+
100 2000-01-01 00:00:00.000000 2038-01-19 03:14:07.999999
450+
execute stmt using @ts2, @ts2;
451+
select *, row_start, row_end from t for system_time all order by a;
452+
a row_start row_end
453+
4 2000-01-01 00:00:03.000000 2038-01-19 03:14:07.999999
454+
100 2000-01-01 00:00:00.000000 2038-01-19 03:14:07.999999
455+
delete from t;
456+
select *, row_start, row_end from t for system_time all order by a;
457+
a row_start row_end
458+
4 2000-01-01 00:00:03.000000 2000-01-01 00:00:03.000000
459+
100 2000-01-01 00:00:00.000000 2000-01-01 00:00:03.000000
460+
execute stmt using @ts2, @ts2;
461+
select *, row_start, row_end from t for system_time all order by a;
462+
a row_start row_end
463+
drop prepare stmt;
464+
drop table t;
465+
set timestamp= default;
466+
# End of 10.11 tests

mysql-test/suite/versioning/t/delete_history.test

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,4 +256,102 @@ drop table t;
256256

257257
--echo # End of 10.9 tests
258258

259+
--echo #
260+
--echo # MDEV-34046 Parameterized PS converts error to warning, causes replication problems
261+
--echo #
262+
create table t (a int) with system versioning;
263+
set timestamp= unix_timestamp('2000-01-01 00:00:00');
264+
insert into t values (1), (100);
265+
266+
--error ER_ILLEGAL_PARAMETER_DATA_TYPE_FOR_OPERATION
267+
delete history from t before system_time @@timestamp;
268+
--error ER_ILLEGAL_PARAMETER_DATA_TYPE_FOR_OPERATION
269+
execute immediate "delete history from t before system_time @@timestamp";
270+
--error ER_ILLEGAL_PARAMETER_DATA_TYPE_FOR_OPERATION
271+
execute immediate "delete history from t before system_time ?" using @@timestamp;
272+
273+
set @ts1= '2000-01-01 00:00:01'; set timestamp= unix_timestamp(@ts1);
274+
update t set a= a + 1 where a < 100;
275+
set @ts2= '2000-01-01 00:00:02'; set timestamp= unix_timestamp(@ts2);
276+
update t set a= a + 1 where a < 100;
277+
set @ts3= '2000-01-01 00:00:03'; set timestamp= unix_timestamp(@ts3);
278+
select *, row_start, row_end from t for system_time all order by a;
279+
execute immediate "delete history from t before system_time @ts1";
280+
select *, row_start, row_end from t for system_time all order by a;
281+
execute immediate "delete history from t before system_time @ts2";
282+
select *, row_start, row_end from t for system_time all order by a;
283+
execute immediate "delete history from t before system_time ?" using @ts3;
284+
select *, row_start, row_end from t for system_time all order by a;
285+
execute immediate "delete history from t before system_time ?" using @ts3;
286+
select *, row_start, row_end from t for system_time all order by a;
287+
update t set a= a + 1 where a < 100;
288+
set @ts4= '2000-01-01 00:00:04'; set timestamp= unix_timestamp(@ts4);
289+
update t set a= a + 1 where a < 100;
290+
select *, row_start, row_end from t for system_time all order by a;
291+
execute immediate "delete history from t before system_time ?" using '2000-01-01 00:00:04';
292+
select *, row_start, row_end from t for system_time all order by a;
293+
set @ts5= '2000-01-01 00:00:05'; set timestamp= unix_timestamp(@ts5);
294+
update t set a= a + 1 where a < 100;
295+
set @ts6= '2000-01-01 00:00:06'; set timestamp= unix_timestamp(@ts6);
296+
update t set a= a + 1 where a < 100;
297+
set @ts7= '2000-01-01 00:00:07'; set timestamp= unix_timestamp(@ts7);
298+
update t set a= a + 1 where a < 100;
299+
select *, row_start, row_end from t for system_time all order by a;
300+
prepare stmt from 'delete history from t before system_time ?';
301+
execute stmt using @ts4;
302+
select *, row_start, row_end from t for system_time all order by a;
303+
execute stmt using @ts5;
304+
select *, row_start, row_end from t for system_time all order by a;
305+
execute stmt using @ts6;
306+
select *, row_start, row_end from t for system_time all order by a;
307+
execute stmt using '2000-01-01 00:00:06';
308+
select *, row_start, row_end from t for system_time all order by a;
309+
execute stmt using '2000-01-01 00:00:06.000001';
310+
select *, row_start, row_end from t for system_time all order by a;
311+
set @ts8= '2000-01-01 00:00:08'; set timestamp= unix_timestamp(@ts8);
312+
delete from t;
313+
select *, row_start, row_end from t for system_time all order by a;
314+
execute immediate "delete history from t before system_time from_unixtime(?)" using @@timestamp;
315+
select *, row_start, row_end from t for system_time all order by a;
316+
execute stmt using '2020-01-01';
317+
select *, row_start, row_end from t for system_time all order by a;
318+
drop prepare stmt;
319+
320+
# Check expression
321+
set timestamp= unix_timestamp('2000-01-01 00:00:00');
322+
insert into t values (1), (100);
323+
set @ts1= '2000-01-01 00:00:01'; set timestamp= unix_timestamp(@ts1);
324+
update t set a= a + 1 where a < 100;
325+
set timestamp= @@timestamp + 1;
326+
set @ts2= @@timestamp;
327+
update t set a= a + 1 where a < 100;
328+
set timestamp= @@timestamp + 1;
329+
update t set a= a + 1 where a < 100;
330+
331+
prepare stmt from 'delete history from t before system_time from_unixtime(? + ?)';
332+
select *, row_start, row_end from t for system_time all order by a;
333+
execute stmt using @ts1, 0;
334+
select *, row_start, row_end from t for system_time all order by a;
335+
execute stmt using @ts2, 0;
336+
select *, row_start, row_end from t for system_time all order by a;
337+
execute stmt using @@timestamp, NULL;
338+
select *, row_start, row_end from t for system_time all order by a;
339+
execute stmt using NULL, NULL;
340+
select *, row_start, row_end from t for system_time all order by a;
341+
execute stmt using @ts2, 1;
342+
select *, row_start, row_end from t for system_time all order by a;
343+
execute stmt using @ts2, @ts2;
344+
select *, row_start, row_end from t for system_time all order by a;
345+
delete from t;
346+
select *, row_start, row_end from t for system_time all order by a;
347+
execute stmt using @ts2, @ts2;
348+
select *, row_start, row_end from t for system_time all order by a;
349+
350+
drop prepare stmt;
351+
352+
drop table t;
353+
set timestamp= default;
354+
355+
--echo # End of 10.11 tests
356+
259357
--source suite/versioning/common_finish.inc

sql/sql_delete.cc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -393,7 +393,10 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
393393
}
394394

395395
if (delete_history)
396+
{
397+
DBUG_ASSERT(conds);
396398
table->vers_write= false;
399+
}
397400

398401
if (returning)
399402
(void) result->prepare(returning->item_list, NULL);

sql/sql_select.cc

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1279,10 +1279,25 @@ int SELECT_LEX::vers_setup_conds(THD *thd, TABLE_LIST *tables)
12791279
}
12801280

12811281
if (vers_conditions.type == SYSTEM_TIME_ALL)
1282+
{
1283+
if (vers_conditions.has_param)
1284+
{
1285+
/*
1286+
Parameter substitution (set_params_from_actual_params()) works
1287+
on existing items so we don't have to reevaluate table->where
1288+
(in update_this below), we just update SELECT_LEX WHERE expression
1289+
from existing table conditions.
1290+
*/
1291+
DBUG_ASSERT(vers_conditions.delete_history);
1292+
DBUG_ASSERT(thd->stmt_arena->is_stmt_execute());
1293+
where= and_items(thd, where, table->where);
1294+
}
12821295
continue;
1296+
}
12831297
}
12841298

12851299
bool timestamps_only= table->table->versioned(VERS_TIMESTAMP);
1300+
bool update_this= update_conds;
12861301

12871302
if (vers_conditions.is_set() && vers_conditions.type != SYSTEM_TIME_HISTORY)
12881303
{
@@ -1299,9 +1314,23 @@ int SELECT_LEX::vers_setup_conds(THD *thd, TABLE_LIST *tables)
12991314
my_error(ER_VERS_ENGINE_UNSUPPORTED, MYF(0), table->table_name.str);
13001315
DBUG_RETURN(-1);
13011316
}
1317+
if (vers_conditions.has_param)
1318+
{
1319+
/*
1320+
PS parameter in history expression requires processing at execution
1321+
stage when parameters has values substituted. So at prepare continue
1322+
the loop, but at execution enter update_this. The second execution
1323+
is skipped on vers_conditions.type == SYSTEM_TIME_ALL condition.
1324+
*/
1325+
DBUG_ASSERT(vers_conditions.delete_history);
1326+
if (thd->stmt_arena->is_stmt_prepare())
1327+
continue;
1328+
DBUG_ASSERT(thd->stmt_arena->is_stmt_execute());
1329+
update_this= true;
1330+
}
13021331
}
13031332

1304-
if (update_conds)
1333+
if (update_this)
13051334
{
13061335
vers_conditions.period = &table->table->s->vers;
13071336
Item *cond= period_get_condition(thd, table, this, &vers_conditions,

0 commit comments

Comments
 (0)