MDEV-29114 Pruning depends on current timestamp for partition by SYSTEM_TIME

Wrong "fastpath" boundary conditions. The right boundary is open, the
left boundary is closed. The condition range_value[loc_hist_id - 1] as
left boundary must be checked with <= operator. The same with the
right boundary range_value[loc_hist_id] as the correct way is to check
it with > operator, the right boundary check was rewritten for clear
understanding:

  ts < range_value[loc_hist_id]

The below code confirms closed endpoint type for left boundary:

      if (range_value[loc_hist_id] <= ts)
        min_hist_id= loc_hist_id + 1;

Also the endpoint type (closed for left, open for right) is confirmed
by vers_set_hist_part() for DML:

  else if (vers_info->interval.is_set() &&
           vers_info->hist_part->range_value <= thd->query_start())

and here (right boundary, rewritten for clarity):

      if (thd->query_start() < next->range_value)
      {
        error= false;
        break;
      }
This commit is contained in:
Aleksey Midenkov 2025-12-23 13:42:45 +03:00
commit dfd28e5324
4 changed files with 115 additions and 3 deletions

View file

@ -3524,6 +3524,69 @@ alter table `t1` partition by system_time interval 7 year ;
ERROR 22003: TIMESTAMP value is out of range in 'INTERVAL'
drop table t1;
#
# MDEV-29114 ALTER puts row into different partition for system_time
# interval partitioning
#
# CREATE case
set timestamp= unix_timestamp('2000-01-01 00:00:00');
create or replace table t1 (x int) with system versioning
partition by system_time interval 1 hour (
partition p0 history, partition p1 history, partition pn current);
set timestamp= unix_timestamp('2000-01-01 00:00:00');
insert t1 values (0);
set timestamp= unix_timestamp('2000-01-01 00:10:00');
update t1 set x= 1;
set timestamp= unix_timestamp('2000-01-01 01:00:00');
update t1 set x= 2;
set timestamp= unix_timestamp('2000-01-01 01:30:00');
update t1 set x= 3;
# CREATE result: row 1 got into p1
select *, row_start, row_end from t1 partition (p0);
x row_start row_end
0 2000-01-01 00:00:00.000000 2000-01-01 00:10:00.000000
select *, row_start, row_end from t1 partition (p1);
x row_start row_end
1 2000-01-01 00:10:00.000000 2000-01-01 01:00:00.000000
2 2000-01-01 01:00:00.000000 2000-01-01 01:30:00.000000
flush tables;
# For CREATE pruning is affected by current timestamp, but SELECT works
# in any case since row 1 got into p1
set timestamp= unix_timestamp('2000-01-01 00:00:00');
explain partitions select * from t1 for system_time as of '2000-01-01 00:59:59';
id select_type table partitions type possible_keys key key_len ref rows Extra
1 SIMPLE t1 p0,p1,pn ALL NULL NULL NULL NULL 4 Using where
select * from t1 for system_time as of '2000-01-01 00:59:59';
x
1
set timestamp= unix_timestamp('2020-01-01 00:00:00');
explain partitions select * from t1 for system_time as of '2000-01-01 00:59:59';
id select_type table partitions type possible_keys key key_len ref rows Extra
1 SIMPLE t1 p0,p1,pn ALL NULL NULL NULL NULL 4 Using where
select * from t1 for system_time as of '2000-01-01 00:59:59';
x
1
# ALTER case
set timestamp= unix_timestamp('2000-01-01 00:00:00');
alter table t1
partition by system_time interval 1 hour (
partition p0 history, partition p1 history, partition pn current);
# ALTER result: row 1 got into p1
Select *, row_start, row_end from t1 partition (p0);
x row_start row_end
0 2000-01-01 00:00:00.000000 2000-01-01 00:10:00.000000
Select *, row_start, row_end from t1 partition (p1);
x row_start row_end
1 2000-01-01 00:10:00.000000 2000-01-01 01:00:00.000000
2 2000-01-01 01:00:00.000000 2000-01-01 01:30:00.000000
flush tables;
Explain partitions select * from t1 for system_time as of '2000-01-01 00:59:59';
id select_type table partitions type possible_keys key key_len ref rows Extra
1 SIMPLE t1 p0,p1,pn ALL NULL NULL NULL NULL 4 Using where
Select * from t1 for system_time as of '2000-01-01 00:59:59';
x
1
drop table t1;
#
# End of 10.11 tests
#
set global innodb_stats_persistent= @save_persistent;

View file

@ -2768,6 +2768,52 @@ partition by system_time interval 1 month (
alter table `t1` partition by system_time interval 7 year ;
drop table t1;
--echo #
--echo # MDEV-29114 ALTER puts row into different partition for system_time
--echo # interval partitioning
--echo #
--echo # CREATE case
set timestamp= unix_timestamp('2000-01-01 00:00:00');
create or replace table t1 (x int) with system versioning
partition by system_time interval 1 hour (
partition p0 history, partition p1 history, partition pn current);
set timestamp= unix_timestamp('2000-01-01 00:00:00');
insert t1 values (0);
set timestamp= unix_timestamp('2000-01-01 00:10:00');
update t1 set x= 1;
set timestamp= unix_timestamp('2000-01-01 01:00:00');
update t1 set x= 2;
set timestamp= unix_timestamp('2000-01-01 01:30:00');
update t1 set x= 3;
--echo # CREATE result: row 1 got into p1
select *, row_start, row_end from t1 partition (p0);
select *, row_start, row_end from t1 partition (p1);
flush tables;
--echo # For CREATE pruning is affected by current timestamp, but SELECT works
--echo # in any case since row 1 got into p1
set timestamp= unix_timestamp('2000-01-01 00:00:00');
explain partitions select * from t1 for system_time as of '2000-01-01 00:59:59';
select * from t1 for system_time as of '2000-01-01 00:59:59';
set timestamp= unix_timestamp('2020-01-01 00:00:00');
explain partitions select * from t1 for system_time as of '2000-01-01 00:59:59';
select * from t1 for system_time as of '2000-01-01 00:59:59';
--echo # ALTER case
set timestamp= unix_timestamp('2000-01-01 00:00:00');
alter table t1
partition by system_time interval 1 hour (
partition p0 history, partition p1 history, partition pn current);
--echo # ALTER result: row 1 got into p1
Select *, row_start, row_end from t1 partition (p0);
Select *, row_start, row_end from t1 partition (p1);
flush tables;
Explain partitions select * from t1 for system_time as of '2000-01-01 00:59:59';
Select * from t1 for system_time as of '2000-01-01 00:59:59';
drop table t1;
--echo #
--echo # End of 10.11 tests
--echo #

View file

@ -831,6 +831,7 @@ bool partition_info::vers_set_hist_part(THD *thd, uint *create_count)
return 0;
}
else if (vers_info->interval.is_set() &&
/* Left boundary is closed */
vers_info->hist_part->range_value <= thd->query_start())
{
partition_element *next= NULL;
@ -842,7 +843,8 @@ bool partition_info::vers_set_hist_part(THD *thd, uint *create_count)
while ((next= it++) != vers_info->now_part)
{
vers_info->hist_part= next;
if (next->range_value > thd->query_start())
/* Right boundary is open */
if (thd->query_start() < next->range_value)
{
error= false;
break;

View file

@ -3511,13 +3511,14 @@ int vers_get_partition_id(partition_info *part_info, uint32 *part_id,
goto done; // fastpath
ts= row_end->get_timestamp(&unused);
if ((loc_hist_id == 0 || range_value[loc_hist_id - 1] < ts) &&
(loc_hist_id == max_hist_id || range_value[loc_hist_id] >= ts))
if ((loc_hist_id == 0 || range_value[loc_hist_id - 1] <= ts) &&
(loc_hist_id == max_hist_id || ts < range_value[loc_hist_id]))
goto done; // fastpath
while (max_hist_id > min_hist_id)
{
loc_hist_id= (max_hist_id + min_hist_id) / 2;
/* Left boundary is closed */
if (range_value[loc_hist_id] <= ts)
min_hist_id= loc_hist_id + 1;
else