From 5056c74e1271e511836538b763906ca469d9219d Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Tue, 14 Apr 2026 13:31:17 +0300 Subject: [PATCH 01/15] MDEV-25529 get_next_time() comment --- sql/event_data_objects.cc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sql/event_data_objects.cc b/sql/event_data_objects.cc index 4fdfad925a760..ac456c77c110f 100644 --- a/sql/event_data_objects.cc +++ b/sql/event_data_objects.cc @@ -689,6 +689,10 @@ add_interval(MYSQL_TIME *ltime, const Time_zone *time_zone, /* Computes the sum of a timestamp plus interval. + Computes the smallest next > time_now obtained by adding one or more + multiples of the given interval (i_value, i_type) to start, taking + into account time_zone conversions and DST ambiguities. + SYNOPSIS get_next_time() time_zone event time zone From bff0b7bc5824e5f4b47edab835b0c25a5bfc7072 Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Tue, 14 Apr 2026 13:31:17 +0300 Subject: [PATCH 02/15] MDEV-25529 converted COMBINE macro to interval2usec inline function --- sql/sql_time.cc | 8 ++------ sql/sql_time.h | 11 +++++++++++ 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/sql/sql_time.cc b/sql/sql_time.cc index a729c01212aac..ae8ec8734b630 100644 --- a/sql/sql_time.cc +++ b/sql/sql_time.cc @@ -24,6 +24,7 @@ #include +/* Daynumber from year 0 to 9999-12-31 */ #define MAX_DAY_NUMBER 3652424L /* Some functions to calculate dates */ @@ -920,11 +921,6 @@ void make_truncated_value_warning(THD *thd, } -/* Daynumber from year 0 to 9999-12-31 */ -#define COMBINE(X) \ - (((((X)->day * 24LL + (X)->hour) * 60LL + \ - (X)->minute) * 60LL + (X)->second)*1000000LL + \ - (X)->second_part) #define GET_PART(X, N) X % N ## LL; X/= N ## LL bool date_add_interval(THD *thd, MYSQL_TIME *ltime, interval_type int_type, @@ -964,7 +960,7 @@ bool date_add_interval(THD *thd, MYSQL_TIME *ltime, interval_type int_type, if (time_type != MYSQL_TIMESTAMP_TIME) ltime->day+= calc_daynr(ltime->year, ltime->month, 1) - 1; - usec= COMBINE(ltime) + sign*COMBINE(&interval); + usec= interval2usec(ltime) + sign * interval2usec(&interval); if (usec < 0) { diff --git a/sql/sql_time.h b/sql/sql_time.h index c918eb6d807ba..7ed507c2f4759 100644 --- a/sql/sql_time.h +++ b/sql/sql_time.h @@ -187,4 +187,15 @@ longlong pack_time(const MYSQL_TIME *my_time); void unpack_time(longlong packed, MYSQL_TIME *my_time, enum_mysql_timestamp_type ts_type); +template +inline longlong interval2sec(T x) +{ + return ((x->day * 24LL + x->hour) * 60LL + x->minute) * 60LL + x->second; +} + +template +inline longlong interval2usec(T x) +{ + return interval2sec(x) * 1000000LL + x->second_part; +} #endif /* SQL_TIME_INCLUDED */ From 1cd7884f5ca80bcb1702fd267bd4811a3bd5c15c Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Tue, 14 Apr 2026 13:31:17 +0300 Subject: [PATCH 03/15] MDEV-25529 TimestampString for printing timestamps --- include/my_time.h | 35 +++++++++++++++++++ .../suite/versioning/r/partition.result | 6 ++-- sql/partition_info.cc | 8 +++-- sql/share/errmsg-utf8.txt | 6 ++-- sql/sql_class.cc | 7 ++++ sql/sql_class.h | 24 +++++++++++++ sql/sql_type.cc | 8 +++++ sql/sql_type.h | 1 + 8 files changed, 86 insertions(+), 9 deletions(-) diff --git a/include/my_time.h b/include/my_time.h index 90a8885a293b3..c7d8d96882246 100644 --- a/include/my_time.h +++ b/include/my_time.h @@ -279,6 +279,41 @@ enum interval_type INTERVAL_MINUTE_MICROSECOND, INTERVAL_SECOND_MICROSECOND, INTERVAL_LAST }; +typedef struct my_timespec +{ + my_time_t sec; + ulong usec; +} my_timespec_t; + +inline +int cmp(my_timespec_t a, my_timespec_t b) +{ + return ((a.sec > b.sec || (a.sec == b.sec && a.usec > b.usec)) ? 1 : + ((a.sec < b.sec || (a.sec == b.sec && a.usec < b.usec)) ? -1 : 0)); +} + C_MODE_END +#ifdef __cplusplus +constexpr my_timespec_t MY_TIMESPEC_MIN= {MY_TIME_T_MIN, 0}; +constexpr my_timespec_t MY_TIMESPEC_MAX= {TIMESTAMP_MAX_VALUE, TIME_MAX_SECOND_PART}; + +inline +bool operator< (my_timespec_t a, my_timespec_t b) +{ + return cmp(a, b) < 0; +} + +inline +bool operator> (my_timespec_t a, my_timespec_t b) +{ + return cmp(a, b) > 0; +} + +inline +bool operator== (my_timespec_t a, my_timespec_t b) +{ + return (a.sec == b.sec) && (a.usec == b.usec); +} +#endif #endif /* _my_time_h_ */ diff --git a/mysql-test/suite/versioning/r/partition.result b/mysql-test/suite/versioning/r/partition.result index 2d408c170f4ce..67c53bca5bdd7 100644 --- a/mysql-test/suite/versioning/r/partition.result +++ b/mysql-test/suite/versioning/r/partition.result @@ -484,7 +484,7 @@ PARTITIONS 2 create or replace table t1 (i int) with system versioning partition by system_time interval 1 day starts '2000-01-01 00:00:01'; Warnings: -Warning 4164 `t1`: STARTS is later than query time, first history partition may exceed INTERVAL value +Warning 4164 `t1`: STARTS timestamp 2000-01-01 00:00:01 is later than query timestamp 2000-01-01 00:00:00, first history partition may exceed INTERVAL value # Test default STARTS rounding set timestamp= unix_timestamp('1999-12-15 13:33:33'); create or replace table t1 (i int) with system versioning @@ -556,7 +556,7 @@ set time_zone="+03:00"; create or replace table t1 (i int) with system versioning partition by system_time interval 1 day starts '2000-01-01 00:00:00'; Warnings: -Warning 4164 `t1`: STARTS is later than query time, first history partition may exceed INTERVAL value +Warning 4164 `t1`: STARTS timestamp 2000-01-01 00:00:00 is later than query timestamp 1999-12-15 16:33:33, first history partition may exceed INTERVAL value set timestamp= unix_timestamp('2000-01-01 00:00:00'); create or replace table t2 (i int) with system versioning partition by system_time interval 1 day; @@ -616,7 +616,7 @@ create or replace table t1 (i int) with system versioning partition by system_time interval 1 day starts '2000-01-03 00:00:00' partitions 3; Warnings: -Warning 4164 `t1`: STARTS is later than query time, first history partition may exceed INTERVAL value +Warning 4164 `t1`: STARTS timestamp 2000-01-03 00:00:00 is later than query timestamp 2000-01-01 00:00:00, first history partition may exceed INTERVAL value insert into t1 values (0); set timestamp= unix_timestamp('2000-01-01 00:00:01'); update t1 set i= i + 1; diff --git a/sql/partition_info.cc b/sql/partition_info.cc index fe2dd6f0895f0..907c1649406e5 100644 --- a/sql/partition_info.cc +++ b/sql/partition_info.cc @@ -2869,10 +2869,12 @@ bool partition_info::vers_set_interval(THD* thd, Item* interval, if (!table) { if (thd->query_start() < vers_info->interval.start) { + TimestampString str_interval(thd, vers_info->interval.start); + TimestampString str_query(thd, thd->query_start()); push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, - ER_PART_STARTS_BEYOND_INTERVAL, - ER_THD(thd, ER_PART_STARTS_BEYOND_INTERVAL), - table_name); + WARN_VERS_STARTS_BEYOND_INTERVAL, + ER_THD(thd, WARN_VERS_STARTS_BEYOND_INTERVAL), + table_name, str_interval.cstr(), str_query.cstr()); } } } diff --git a/sql/share/errmsg-utf8.txt b/sql/share/errmsg-utf8.txt index ad124f180c833..53ca57eeaa292 100644 --- a/sql/share/errmsg-utf8.txt +++ b/sql/share/errmsg-utf8.txt @@ -10676,9 +10676,9 @@ ER_UNKNOWN_OPERATOR spa "El operador no existe: '%-.128s'" ER_UNUSED_29 eng "You should never see it" -ER_PART_STARTS_BEYOND_INTERVAL - eng "%`s: STARTS is later than query time, first history partition may exceed INTERVAL value" - spa "%`s: STARTS es posterior al momento de consulta (query), la primera partición de historia puede exceder el valor INTERVAL" +WARN_VERS_STARTS_BEYOND_INTERVAL + eng "%`s: STARTS timestamp %s is later than query timestamp %s, first history partition may exceed INTERVAL value" + spa "%`s: STARTS %s es posterior al momento de consulta (query) %s, la primera partición de historia puede exceder el valor INTERVAL" ER_GALERA_REPLICATION_NOT_SUPPORTED eng "Galera replication not supported" spa "La replicación en Galera no está soportada" diff --git a/sql/sql_class.cc b/sql/sql_class.cc index a5155b9955dc3..58a16f54dfee5 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -8675,6 +8675,13 @@ bool THD::timestamp_to_TIME(MYSQL_TIME *ltime, my_time_t ts, } +bool THD::timestamp_to_string(String *str, uint dec, my_timespec_t ts) +{ + Temporal_hybrid th(this, ts); + return th.to_string(str, dec); +} + + void THD::my_ok_with_recreate_info(const Recreate_info &info, ulong warn_count) { diff --git a/sql/sql_class.h b/sql/sql_class.h index aa78312c3b6a5..800b8adc1e11d 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -4140,6 +4140,11 @@ class THD: public THD_count, /* this must be first */ const Type_handler *type_handler_for_datetime() const; bool timestamp_to_TIME(MYSQL_TIME *ltime, my_time_t ts, ulong sec_part, date_mode_t fuzzydate); + bool timestamp_to_string(String *str, uint dec, my_timespec_t ts); + bool timestamp_to_string(String *str, uint dec, my_time_t ts) + { + return timestamp_to_string(str, dec, {ts, 0}); + } inline my_time_t query_start() { return start_time; } inline ulong query_start_sec_part() { used|= QUERY_START_SEC_PART_USED; return start_time_sec_part; } @@ -8423,5 +8428,24 @@ class Write_log_with_flags } }; + +class TimestampString : public String +{ + THD *thd; + my_timespec_t ts; + +public: + TimestampString(THD *thd, my_timespec_t ts) : thd{thd}, ts{ts} {} + TimestampString(THD *thd, my_time_t ts) : thd{thd}, ts({ts, 0}) {} + + const char * cstr(uint dec= 0) + { + if (thd->timestamp_to_string(this, dec, ts)) + return "ERROR"; + else + return c_ptr_safe(); + } +}; + #endif /* MYSQL_SERVER */ #endif /* SQL_CLASS_INCLUDED */ diff --git a/sql/sql_type.cc b/sql/sql_type.cc index cc793009b71f5..61846dacc6ea2 100644 --- a/sql/sql_type.cc +++ b/sql/sql_type.cc @@ -438,6 +438,14 @@ Temporal_hybrid::Temporal_hybrid(THD *thd, Item *item, date_mode_t fuzzydate) } +Temporal_hybrid::Temporal_hybrid(THD *thd, my_timespec_t time) +{ + thd->variables.time_zone->gmt_sec_to_TIME(this, time.sec); + DBUG_ASSERT(time.usec < 1000000); + second_part= time.usec; +} + + uint Timestamp::binary_length_to_precision(uint length) { switch (length) { diff --git a/sql/sql_type.h b/sql/sql_type.h index 53970f3d23a54..3b93fad7cfbd2 100644 --- a/sql/sql_type.h +++ b/sql/sql_type.h @@ -1319,6 +1319,7 @@ class Temporal_hybrid: public Temporal else make_from_decimal(thd, warn, nr, mode); } + Temporal_hybrid(THD *thd, my_timespec_t time); // End of constuctors bool copy_valid_value_to_mysql_time(MYSQL_TIME *ltime) const From 9d3064e4a9d0e8928f174a2ac52ebd43fdffba1a Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Tue, 14 Apr 2026 13:31:17 +0300 Subject: [PATCH 04/15] MDEV-25529 cleanup for vers_set_starts() and starts_clause --- sql/partition_info.cc | 37 +++++++++++++++++++++++-------------- sql/partition_info.h | 4 ++++ 2 files changed, 27 insertions(+), 14 deletions(-) diff --git a/sql/partition_info.cc b/sql/partition_info.cc index 907c1649406e5..b8ebda64f472c 100644 --- a/sql/partition_info.cc +++ b/sql/partition_info.cc @@ -2840,6 +2840,7 @@ bool partition_info::vers_set_interval(THD* thd, Item* interval, /* 2. assign STARTS to interval.start */ if (starts) { + vers_info->starts_clause= true; if (starts->fix_fields_if_needed_for_scalar(thd, &starts)) return true; switch (starts->result_type()) @@ -2880,20 +2881,7 @@ bool partition_info::vers_set_interval(THD* thd, Item* interval, } else // calculate default STARTS depending on INTERVAL { - thd->variables.time_zone->gmt_sec_to_TIME(<ime, thd->query_start()); - if (vers_info->interval.step.second) - goto interval_set_starts; - ltime.second= 0; - if (vers_info->interval.step.minute) - goto interval_set_starts; - ltime.minute= 0; - if (vers_info->interval.step.hour) - goto interval_set_starts; - ltime.hour= 0; - -interval_set_starts: - vers_info->interval.start= TIME_to_timestamp(thd, <ime, &err); - if (err) + if (vers_set_starts(thd, thd->query_start())) goto interval_starts_error; } @@ -2905,6 +2893,27 @@ bool partition_info::vers_set_interval(THD* thd, Item* interval, } +bool partition_info::vers_set_starts(THD *thd, my_time_t ts) +{ + MYSQL_TIME ltime; + uint err; + thd->variables.time_zone->gmt_sec_to_TIME(<ime, ts); + if (vers_info->interval.step.second) + goto interval_set_starts; + ltime.second= 0; + if (vers_info->interval.step.minute) + goto interval_set_starts; + ltime.minute= 0; + if (vers_info->interval.step.hour) + goto interval_set_starts; + ltime.hour= 0; + +interval_set_starts: + vers_info->interval.start= TIME_to_timestamp(thd, <ime, &err); + return (bool) err; +} + + bool partition_info::vers_set_limit(ulonglong limit, bool auto_hist, const char *table_name) { diff --git a/sql/partition_info.h b/sql/partition_info.h index 3a8c3a3752435..58971b1708318 100644 --- a/sql/partition_info.h +++ b/sql/partition_info.h @@ -41,6 +41,7 @@ struct Vers_part_info : public Sql_alloc Vers_part_info() : limit(0), auto_hist(false), + starts_clause(false), now_part(NULL), hist_part(NULL) { @@ -50,6 +51,7 @@ struct Vers_part_info : public Sql_alloc interval(src.interval), limit(src.limit), auto_hist(src.auto_hist), + starts_clause(src.starts_clause), now_part(NULL), hist_part(NULL) { @@ -91,6 +93,7 @@ struct Vers_part_info : public Sql_alloc } interval; ulonglong limit; bool auto_hist; + bool starts_clause; partition_element *now_part; partition_element *hist_part; }; @@ -417,6 +420,7 @@ class partition_info : public DDL_LOG_STATE, public Sql_alloc bool vers_set_interval(THD *thd, Item *interval, interval_type int_type, Item *starts, bool auto_part, const char *table_name); + bool vers_set_starts(THD *thd, my_time_t ts); bool vers_set_limit(ulonglong limit, bool auto_part, const char *table_name); bool vers_set_hist_part(THD* thd, uint *create_count); bool vers_require_hist_part(THD *thd) const From c8da2a319f10e5c8d6a4538d10bd8c10c3e93c00 Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Tue, 14 Apr 2026 13:31:17 +0300 Subject: [PATCH 05/15] MDEV-25529 ALTER TABLE FORCE syntax improved Improves ALTER TABLE syntax when alter_list can be supplied alongside a partitioning expression, so that they can appear in any order. This is particularly useful for the FORCE clause when adding it to an existing command. Also improves handling of AUTO with FORCE, so that AUTO FORCE specified together provides more consistent syntax, which is used by this task in further commits. --- mysql-test/main/partition.result | 15 +++++++++++++++ mysql-test/main/partition.test | 17 +++++++++++++++++ sql/sql_yacc.yy | 4 ++++ 3 files changed, 36 insertions(+) diff --git a/mysql-test/main/partition.result b/mysql-test/main/partition.result index bfe5d07b1d99f..93ff867f39e63 100644 --- a/mysql-test/main/partition.result +++ b/mysql-test/main/partition.result @@ -2921,3 +2921,18 @@ DROP TABLE mdev20498; # # End of 10.6 tests # +# +# MDEV-25529 ALTER TABLE FORCE syntax improved +# +create table t1 (id int primary key); +alter table t1 force partition by hash(id) partitions 2; +alter table t1 force remove partitioning; +alter table t1 partition by hash(id) partitions 2 force; +alter table t1 remove partitioning force; +alter table t1 force partition by hash(id) partitions 2 force; +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'force' at line 1 +alter table t1 force remove partitioning force; +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'force' at line 1 +alter table t1 add column x int partition by hash(id) partitions 2; +alter table t1 remove partitioning add column y int; +drop table t1; diff --git a/mysql-test/main/partition.test b/mysql-test/main/partition.test index da0109913305a..0c0d2b30c3ad0 100644 --- a/mysql-test/main/partition.test +++ b/mysql-test/main/partition.test @@ -3144,3 +3144,20 @@ DROP TABLE mdev20498; --echo # --echo # End of 10.6 tests --echo # + +--echo # +--echo # MDEV-25529 ALTER TABLE FORCE syntax improved +--echo # +create table t1 (id int primary key); +alter table t1 force partition by hash(id) partitions 2; +alter table t1 force remove partitioning; +alter table t1 partition by hash(id) partitions 2 force; +alter table t1 remove partitioning force; +--error ER_PARSE_ERROR +alter table t1 force partition by hash(id) partitions 2 force; +--error ER_PARSE_ERROR +alter table t1 force remove partitioning force; +# Strange syntax, but it exists since 2005 (cd483c55) +alter table t1 add column x int partition by hash(id) partitions 2; +alter table t1 remove partitioning add column y int; +drop table t1; diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index eabd28ba35e63..c6a72e5e1d068 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -7361,8 +7361,12 @@ alter_commands: } | alter_list opt_partitioning + | partitioning + alter_list | alter_list remove_partitioning + | remove_partitioning + alter_list | remove_partitioning | partitioning /* From 5cc48ce68bd2cec649aedd3e78baedd4b57c2858 Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Tue, 14 Apr 2026 13:31:17 +0300 Subject: [PATCH 06/15] MDEV-25529 set_up_default_partitions() ER_OUT_OF_RESOURCES error --- sql/partition_info.cc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sql/partition_info.cc b/sql/partition_info.cc index b8ebda64f472c..d99750b7e5797 100644 --- a/sql/partition_info.cc +++ b/sql/partition_info.cc @@ -423,6 +423,7 @@ bool partition_info::set_up_default_partitions(THD *thd, handler *file, i= 0; do { + // FIXME (newbie): how is it freed? Explain in comment or remake via table->mem_root. partition_element *part_elem= new partition_element(); if (likely(part_elem != 0 && (!partitions.push_back(part_elem)))) @@ -442,7 +443,10 @@ bool partition_info::set_up_default_partitions(THD *thd, handler *file, } } else + { + my_error(ER_OUT_OF_RESOURCES, MYF(0)); goto end; + } } while (++i < num_parts); result= FALSE; end: From efdb7132204a33f5dec4f645a1a4a40466e470cb Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Tue, 14 Apr 2026 13:31:18 +0300 Subject: [PATCH 07/15] MDEV-25529 Auto-create: Pre-existing historical data is not partitioned as specified by ALTER Adds logic into prep_alter_part_table() for AUTO to check the history range (vers_get_history_range()) and based on (max_ts - min_ts) difference compute the number of created partitions and set STARTS value to round down min_ts value (vers_set_starts()) if it was not specified by user or if the user specified it incorrectly. In the latter case it will print warning about wrongly specified user value. In case of fast ALTER TABLE, f.ex. when partitioning already exists, the above logic is ignored unless FORCE clause is specified. When user specifies partition list explicitly the above logic is ignored even with FORCE clause. vers_get_history_range() detects if the index can be used for row_end min/max stats and if so it gets it with ha_index_first() and HA_READ_BEFORE_KEY (as it must ignore current data). Otherwise it does table scan to read the stats. There is test_mdev-25529 debug keyword to check the both and compare results. A warning is printed if the algorithm uses slow scan. Field_vers_trx_id::get_timestamp() is implemented for TRX_ID based versioning to get epoch value. It works in vers_get_history_range() but since partitioning is not enabled for TRX_ID versioning create temporary table fails with error, requiring timestamp-based system fields. This method will be useful when partitioning will be enabled for TRX_ID which is mostly performance problems to solve. Static key_cmp was renamed to key_eq to resolve compilation after key.h was included as key_cmp was already declared there. --- include/my_time.h | 2 +- .../suite/versioning/r/partition2.result | 443 ++++++++++++++++++ mysql-test/suite/versioning/t/partition2.test | 196 ++++++++ sql/field.cc | 24 + sql/field.h | 1 + sql/partition_info.cc | 5 +- sql/share/errmsg-utf8.txt | 4 + sql/sql_partition.cc | 63 ++- sql/sql_table.cc | 144 +++++- sql/table.h | 2 + 10 files changed, 879 insertions(+), 5 deletions(-) create mode 100644 mysql-test/suite/versioning/r/partition2.result create mode 100644 mysql-test/suite/versioning/t/partition2.test diff --git a/include/my_time.h b/include/my_time.h index c7d8d96882246..c1c5d76ed8405 100644 --- a/include/my_time.h +++ b/include/my_time.h @@ -285,7 +285,7 @@ typedef struct my_timespec ulong usec; } my_timespec_t; -inline +static inline int cmp(my_timespec_t a, my_timespec_t b) { return ((a.sec > b.sec || (a.sec == b.sec && a.usec > b.usec)) ? 1 : diff --git a/mysql-test/suite/versioning/r/partition2.result b/mysql-test/suite/versioning/r/partition2.result new file mode 100644 index 0000000000000..2e38e1964192b --- /dev/null +++ b/mysql-test/suite/versioning/r/partition2.result @@ -0,0 +1,443 @@ +# +# MDEV-25529 Auto-create: Pre-existing historical data is not partitioned as specified by ALTER +# +set @old_dbug= @@debug_dbug; +set debug_dbug= '+d,test_mdev-25529'; +create or replace table t1 (x int) with system versioning +partition by system_time limit 1; +alter table t1 partition by system_time limit 1 auto; +create or replace table t1 (x int, +row_start timestamp(6) as row start invisible, +row_end timestamp(6) as row end invisible, +period for system_time (row_start, row_end), +index (row_end, row_start), +index (row_start), +index (row_end, x), +index (x, row_end), +index (row_end)) with system versioning +partition by system_time limit 1000 partitions 6; +show create table t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `x` int(11) DEFAULT NULL, + `row_start` timestamp(6) GENERATED ALWAYS AS ROW START INVISIBLE, + `row_end` timestamp(6) GENERATED ALWAYS AS ROW END INVISIBLE, + KEY `row_end` (`row_end`,`row_start`), + KEY `row_start` (`row_start`), + KEY `row_end_2` (`row_end`,`x`), + KEY `x` (`x`,`row_end`), + KEY `row_end_3` (`row_end`), + PERIOD FOR SYSTEM_TIME (`row_start`, `row_end`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci WITH SYSTEM VERSIONING + PARTITION BY SYSTEM_TIME LIMIT 1000 +PARTITIONS 6 +set timestamp= unix_timestamp('2000-01-01 00:00:00'); +insert t1 values (0); +set timestamp= unix_timestamp('2000-01-01 00:10:00.22'); +update t1 set x= x + 1; +set timestamp= unix_timestamp('2000-01-01 01:00:00'); +update t1 set x= x + 1; +set timestamp= unix_timestamp('2000-01-01 01:30:00'); +update t1 set x= x + 1; +set timestamp= unix_timestamp('2000-01-01 02:00:00'); +update t1 set x= x + 1; +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.220000 +1 2000-01-01 00:10:00.220000 2000-01-01 01:00:00.000000 +2 2000-01-01 01:00:00.000000 2000-01-01 01:30:00.000000 +3 2000-01-01 01:30:00.000000 2000-01-01 02:00:00.000000 +select *, row_start, row_end from t1 partition (p1); +x row_start row_end +select *, row_start, row_end from t1 partition (p2); +x row_start row_end +select *, row_start, row_end from t1 partition (p3); +x row_start row_end +select *, row_start, row_end from t1 partition (p4); +x row_start row_end +select *, row_start, row_end from t1 partition (pn); +x row_start row_end +4 2000-01-01 02:00:00.000000 2038-01-19 06:14:07.999999 +set timestamp= default; +ALTER table t1 partition by system_time +interval 1 hour starts '2000-01-01 00:00:00'; +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.220000 +select *, row_start, row_end from t1 partition (p1); +x row_start row_end +1 2000-01-01 00:10:00.220000 2000-01-01 01:00:00.000000 +2 2000-01-01 01:00:00.000000 2000-01-01 01:30:00.000000 +select *, row_start, row_end from t1 partition (p2); +x row_start row_end +3 2000-01-01 01:30:00.000000 2000-01-01 02:00:00.000000 +select *, row_start, row_end from t1 partition (p3); +x row_start row_end +select *, row_start, row_end from t1 partition (p4); +x row_start row_end +select *, row_start, row_end from t1 partition (pn); +x row_start row_end +4 2000-01-01 02:00:00.000000 2038-01-19 06:14:07.999999 +alter TABLE t1 partition by system_time +interval 1 hour; +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.220000 +select *, row_start, row_end from t1 partition (p1); +x row_start row_end +1 2000-01-01 00:10:00.220000 2000-01-01 01:00:00.000000 +2 2000-01-01 01:00:00.000000 2000-01-01 01:30:00.000000 +select *, row_start, row_end from t1 partition (p2); +x row_start row_end +3 2000-01-01 01:30:00.000000 2000-01-01 02:00:00.000000 +select *, row_start, row_end from t1 partition (p3); +x row_start row_end +select *, row_start, row_end from t1 partition (p4); +x row_start row_end +select *, row_start, row_end from t1 partition (pn); +x row_start row_end +4 2000-01-01 02:00:00.000000 2038-01-19 06:14:07.999999 +set @@system_versioning_alter_history= keep; +alter table t1 remove partitioning; +select *, row_start, row_end from t1 for system_time all; +x row_start row_end +0 2000-01-01 00:00:00.000000 2000-01-01 00:10:00.220000 +1 2000-01-01 00:10:00.220000 2000-01-01 01:00:00.000000 +2 2000-01-01 01:00:00.000000 2000-01-01 01:30:00.000000 +3 2000-01-01 01:30:00.000000 2000-01-01 02:00:00.000000 +4 2000-01-01 02:00:00.000000 2038-01-19 06:14:07.999999 +alter table t1 PARTITION by system_time interval 1 hour auto; +show create table t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `x` int(11) DEFAULT NULL, + `row_start` timestamp(6) GENERATED ALWAYS AS ROW START INVISIBLE, + `row_end` timestamp(6) GENERATED ALWAYS AS ROW END INVISIBLE, + KEY `row_end` (`row_end`,`row_start`), + KEY `row_start` (`row_start`), + KEY `row_end_2` (`row_end`,`x`), + KEY `x` (`x`,`row_end`), + KEY `row_end_3` (`row_end`), + PERIOD FOR SYSTEM_TIME (`row_start`, `row_end`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci WITH SYSTEM VERSIONING + PARTITION BY SYSTEM_TIME INTERVAL 1 HOUR STARTS TIMESTAMP'2000-01-01 00:00:00' AUTO +PARTITIONS 3 +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.220000 +select *, row_start, row_end from t1 partition (p1); +x row_start row_end +1 2000-01-01 00:10:00.220000 2000-01-01 01:00:00.000000 +2 2000-01-01 01:00:00.000000 2000-01-01 01:30:00.000000 +3 2000-01-01 01:30:00.000000 2000-01-01 02:00:00.000000 +select *, row_start, row_end from t1 partition (pn); +x row_start row_end +4 2000-01-01 02:00:00.000000 2038-01-19 06:14:07.999999 +alter table t1 remove partitioning; +alter table t1 partition by SYSTEM_TIME interval 1 hour starts '2000-01-01 00:00:00' auto; +show create table t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `x` int(11) DEFAULT NULL, + `row_start` timestamp(6) GENERATED ALWAYS AS ROW START INVISIBLE, + `row_end` timestamp(6) GENERATED ALWAYS AS ROW END INVISIBLE, + KEY `row_end` (`row_end`,`row_start`), + KEY `row_start` (`row_start`), + KEY `row_end_2` (`row_end`,`x`), + KEY `x` (`x`,`row_end`), + KEY `row_end_3` (`row_end`), + PERIOD FOR SYSTEM_TIME (`row_start`, `row_end`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci WITH SYSTEM VERSIONING + PARTITION BY SYSTEM_TIME INTERVAL 1 HOUR STARTS TIMESTAMP'2000-01-01 00:00:00' AUTO +PARTITIONS 3 +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.220000 +select *, row_start, row_end from t1 partition (p1); +x row_start row_end +1 2000-01-01 00:10:00.220000 2000-01-01 01:00:00.000000 +2 2000-01-01 01:00:00.000000 2000-01-01 01:30:00.000000 +3 2000-01-01 01:30:00.000000 2000-01-01 02:00:00.000000 +select *, row_start, row_end from t1 partition (pn); +x row_start row_end +4 2000-01-01 02:00:00.000000 2038-01-19 06:14:07.999999 +set timestamp= unix_timestamp('2000-01-02 03:01:23.456'); +delete from t1; +alter table t1 remove partitioning; +alter table t1 partition by SYSTEM_TIME interval 1 hour auto; +show create table t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `x` int(11) DEFAULT NULL, + `row_start` timestamp(6) GENERATED ALWAYS AS ROW START INVISIBLE, + `row_end` timestamp(6) GENERATED ALWAYS AS ROW END INVISIBLE, + KEY `row_end` (`row_end`,`row_start`), + KEY `row_start` (`row_start`), + KEY `row_end_2` (`row_end`,`x`), + KEY `x` (`x`,`row_end`), + KEY `row_end_3` (`row_end`), + PERIOD FOR SYSTEM_TIME (`row_start`, `row_end`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci WITH SYSTEM VERSIONING + PARTITION BY SYSTEM_TIME INTERVAL 1 HOUR STARTS TIMESTAMP'2000-01-01 00:00:00' AUTO +PARTITIONS 29 +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.220000 +select *, row_start, row_end from t1 partition (p1); +x row_start row_end +1 2000-01-01 00:10:00.220000 2000-01-01 01:00:00.000000 +2 2000-01-01 01:00:00.000000 2000-01-01 01:30:00.000000 +select *, row_start, row_end from t1 partition (p2); +x row_start row_end +3 2000-01-01 01:30:00.000000 2000-01-01 02:00:00.000000 +select *, row_start, row_end from t1 partition (p27); +x row_start row_end +4 2000-01-01 02:00:00.000000 2000-01-02 03:01:23.456000 +select *, row_start, row_end from t1 partition (pn); +x row_start row_end +set timestamp= unix_timestamp('2000-01-02 03:01:23.456'); +delete from t1; +alter table t1 partition by system_time INTERVAL 20 minute auto force; +Warnings: +Warning 4193 `t1`: STARTS timestamp 2000-01-02 03:01:00 is above earliest history date 2000-01-01 00:10:00 and was set to 2000-01-01 00:10:00 +show create table t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `x` int(11) DEFAULT NULL, + `row_start` timestamp(6) GENERATED ALWAYS AS ROW START INVISIBLE, + `row_end` timestamp(6) GENERATED ALWAYS AS ROW END INVISIBLE, + KEY `row_end` (`row_end`,`row_start`), + KEY `row_start` (`row_start`), + KEY `row_end_2` (`row_end`,`x`), + KEY `x` (`x`,`row_end`), + KEY `row_end_3` (`row_end`), + PERIOD FOR SYSTEM_TIME (`row_start`, `row_end`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci WITH SYSTEM VERSIONING + PARTITION BY SYSTEM_TIME INTERVAL 20 MINUTE STARTS TIMESTAMP'2000-01-01 00:10:00' AUTO +PARTITIONS 82 +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.220000 +select *, row_start, row_end from t1 partition (p2); +x row_start row_end +1 2000-01-01 00:10:00.220000 2000-01-01 01:00:00.000000 +select *, row_start, row_end from t1 partition (p5); +x row_start row_end +3 2000-01-01 01:30:00.000000 2000-01-01 02:00:00.000000 +select *, row_start, row_end from t1 partition (pn); +x row_start row_end +alter table t1 remove partitioning; +alter table t1 partition by system_time interval 1 hour +starts '2002-01-01 00:00:00' auto; +Warnings: +Warning 4164 `t1`: STARTS timestamp 2002-01-01 00:00:00 is later than query timestamp 2000-01-02 03:01:23, first history partition may exceed INTERVAL value +Warning 4193 `t1`: STARTS timestamp 2002-01-01 00:00:00 is above earliest history date 2000-01-01 00:10:00 and was set to 2000-01-01 00:00:00 +# STARTS changed and WARN_VERS_WRONG_STARTS is here +alter table t1 force partition by system_time interval 1 hour +starts '2003-01-01 00:00:00' auto; +Warnings: +Warning 4193 `t1`: STARTS timestamp 2003-01-01 00:00:00 is above earliest history date 2000-01-01 00:10:00 and was set to 2000-01-01 00:00:00 +show create table t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `x` int(11) DEFAULT NULL, + `row_start` timestamp(6) GENERATED ALWAYS AS ROW START INVISIBLE, + `row_end` timestamp(6) GENERATED ALWAYS AS ROW END INVISIBLE, + KEY `row_end` (`row_end`,`row_start`), + KEY `row_start` (`row_start`), + KEY `row_end_2` (`row_end`,`x`), + KEY `x` (`x`,`row_end`), + KEY `row_end_3` (`row_end`), + PERIOD FOR SYSTEM_TIME (`row_start`, `row_end`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci WITH SYSTEM VERSIONING + PARTITION BY SYSTEM_TIME INTERVAL 1 HOUR STARTS TIMESTAMP'2000-01-01 00:00:00' AUTO +PARTITIONS 29 +alter table t1 partition by system_time interval 1 hour +starts '2004-01-01 00:00:00' auto force; +Warnings: +Warning 4164 `t1`: STARTS timestamp 2004-01-01 00:00:00 is later than query timestamp 2000-01-02 03:01:23, first history partition may exceed INTERVAL value +Warning 4193 `t1`: STARTS timestamp 2004-01-01 00:00:00 is above earliest history date 2000-01-01 00:10:00 and was set to 2000-01-01 00:00:00 +show create table t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `x` int(11) DEFAULT NULL, + `row_start` timestamp(6) GENERATED ALWAYS AS ROW START INVISIBLE, + `row_end` timestamp(6) GENERATED ALWAYS AS ROW END INVISIBLE, + KEY `row_end` (`row_end`,`row_start`), + KEY `row_start` (`row_start`), + KEY `row_end_2` (`row_end`,`x`), + KEY `x` (`x`,`row_end`), + KEY `row_end_3` (`row_end`), + PERIOD FOR SYSTEM_TIME (`row_start`, `row_end`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci WITH SYSTEM VERSIONING + PARTITION BY SYSTEM_TIME INTERVAL 1 HOUR STARTS TIMESTAMP'2000-01-01 00:00:00' AUTO +PARTITIONS 29 +# STARTS doesn't change without FORCE +alter table t1 partition by system_time interval 1 hour +starts '2005-01-01 00:00:00' auto; +Warnings: +Warning 4164 `t1`: STARTS timestamp 2005-01-01 00:00:00 is later than query timestamp 2000-01-02 03:01:23, first history partition may exceed INTERVAL value +show create table t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `x` int(11) DEFAULT NULL, + `row_start` timestamp(6) GENERATED ALWAYS AS ROW START INVISIBLE, + `row_end` timestamp(6) GENERATED ALWAYS AS ROW END INVISIBLE, + KEY `row_end` (`row_end`,`row_start`), + KEY `row_start` (`row_start`), + KEY `row_end_2` (`row_end`,`x`), + KEY `x` (`x`,`row_end`), + KEY `row_end_3` (`row_end`), + PERIOD FOR SYSTEM_TIME (`row_start`, `row_end`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci WITH SYSTEM VERSIONING + PARTITION BY SYSTEM_TIME INTERVAL 1 HOUR STARTS TIMESTAMP'2005-01-01 00:00:00' AUTO +PARTITIONS 29 +# min_ts == max_ts case, partitions decrease +delete history from t1; +set timestamp= unix_timestamp('2000-01-01 00:00:00'); +insert t1 values (77); +delete from t1; +alter table t1 partition by system_time interval 7 hour auto force; +show create table t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `x` int(11) DEFAULT NULL, + `row_start` timestamp(6) GENERATED ALWAYS AS ROW START INVISIBLE, + `row_end` timestamp(6) GENERATED ALWAYS AS ROW END INVISIBLE, + KEY `row_end` (`row_end`,`row_start`), + KEY `row_start` (`row_start`), + KEY `row_end_2` (`row_end`,`x`), + KEY `x` (`x`,`row_end`), + KEY `row_end_3` (`row_end`), + PERIOD FOR SYSTEM_TIME (`row_start`, `row_end`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci WITH SYSTEM VERSIONING + PARTITION BY SYSTEM_TIME INTERVAL 7 HOUR STARTS TIMESTAMP'2000-01-01 00:00:00' AUTO +PARTITIONS 2 +# Explicit partition list ignores AUTO FORCE +set timestamp= unix_timestamp('2000-01-03 00:00:00'); +insert t1 values (88); +delete from t1; +alter table t1 partition by system_time interval 8 hour auto force; +Warnings: +Warning 4193 `t1`: STARTS timestamp 2000-01-03 00:00:00 is above earliest history date 2000-01-01 00:00:00 and was set to 2000-01-01 00:00:00 +show create table t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `x` int(11) DEFAULT NULL, + `row_start` timestamp(6) GENERATED ALWAYS AS ROW START INVISIBLE, + `row_end` timestamp(6) GENERATED ALWAYS AS ROW END INVISIBLE, + KEY `row_end` (`row_end`,`row_start`), + KEY `row_start` (`row_start`), + KEY `row_end_2` (`row_end`,`x`), + KEY `x` (`x`,`row_end`), + KEY `row_end_3` (`row_end`), + PERIOD FOR SYSTEM_TIME (`row_start`, `row_end`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci WITH SYSTEM VERSIONING + PARTITION BY SYSTEM_TIME INTERVAL 8 HOUR STARTS TIMESTAMP'2000-01-01 00:00:00' AUTO +PARTITIONS 7 +alter table t1 partition by system_time interval 8 hour auto ( +partition p0 history, +partition p1 history, +partition pn current) force; +show create table t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `x` int(11) DEFAULT NULL, + `row_start` timestamp(6) GENERATED ALWAYS AS ROW START INVISIBLE, + `row_end` timestamp(6) GENERATED ALWAYS AS ROW END INVISIBLE, + KEY `row_end` (`row_end`,`row_start`), + KEY `row_start` (`row_start`), + KEY `row_end_2` (`row_end`,`x`), + KEY `x` (`x`,`row_end`), + KEY `row_end_3` (`row_end`), + PERIOD FOR SYSTEM_TIME (`row_start`, `row_end`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci WITH SYSTEM VERSIONING + PARTITION BY SYSTEM_TIME INTERVAL 8 HOUR STARTS TIMESTAMP'2000-01-03 00:00:00' AUTO +(PARTITION `p0` HISTORY ENGINE = MyISAM, + PARTITION `p1` HISTORY ENGINE = MyISAM, + PARTITION `pn` CURRENT ENGINE = MyISAM) +# Switch system_time -> hash +alter table t1 partition by hash(x) partitions 3; +show create table t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `x` int(11) DEFAULT NULL, + `row_start` timestamp(6) GENERATED ALWAYS AS ROW START INVISIBLE, + `row_end` timestamp(6) GENERATED ALWAYS AS ROW END INVISIBLE, + KEY `row_end` (`row_end`,`row_start`), + KEY `row_start` (`row_start`), + KEY `row_end_2` (`row_end`,`x`), + KEY `x` (`x`,`row_end`), + KEY `row_end_3` (`row_end`), + PERIOD FOR SYSTEM_TIME (`row_start`, `row_end`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci WITH SYSTEM VERSIONING + PARTITION BY HASH (`x`) +PARTITIONS 3 +# Switch hash -> system_time +alter table t1 partition by system_time interval 9 hour +starts '2001-01-01 00:00:00' auto force; +Warnings: +Warning 4164 `t1`: STARTS timestamp 2001-01-01 00:00:00 is later than query timestamp 2000-01-03 00:00:00, first history partition may exceed INTERVAL value +Warning 4193 `t1`: STARTS timestamp 2001-01-01 00:00:00 is above earliest history date 2000-01-01 00:00:00 and was set to 2000-01-01 00:00:00 +show create table t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `x` int(11) DEFAULT NULL, + `row_start` timestamp(6) GENERATED ALWAYS AS ROW START INVISIBLE, + `row_end` timestamp(6) GENERATED ALWAYS AS ROW END INVISIBLE, + KEY `row_end` (`row_end`,`row_start`), + KEY `row_start` (`row_start`), + KEY `row_end_2` (`row_end`,`x`), + KEY `x` (`x`,`row_end`), + KEY `row_end_3` (`row_end`), + PERIOD FOR SYSTEM_TIME (`row_start`, `row_end`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci WITH SYSTEM VERSIONING + PARTITION BY SYSTEM_TIME INTERVAL 9 HOUR STARTS TIMESTAMP'2000-01-01 00:00:00' AUTO +PARTITIONS 7 +# Slow scan warning +create or replace table t1 (x int, +row_start timestamp(6) as row start invisible, +row_end timestamp(6) as row end invisible, +period for system_time (row_start, row_end), +index (x, row_end, row_start), +index (row_start, row_end), +index (x, row_end)) with system versioning; +set timestamp= unix_timestamp('2000-01-01 00:00:00'); +insert t1 values (77); +delete from t1; +set timestamp= unix_timestamp('2000-01-03 00:00:00'); +insert t1 values (88); +delete from t1; +alter table t1 partition by system_time interval 10 hour auto; +Warnings: +Warning 4194 `t1`: ROW END index not found, using slow scan +show create table t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `x` int(11) DEFAULT NULL, + `row_start` timestamp(6) GENERATED ALWAYS AS ROW START INVISIBLE, + `row_end` timestamp(6) GENERATED ALWAYS AS ROW END INVISIBLE, + KEY `x` (`x`,`row_end`,`row_start`), + KEY `row_start` (`row_start`,`row_end`), + KEY `x_2` (`x`,`row_end`), + PERIOD FOR SYSTEM_TIME (`row_start`, `row_end`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci WITH SYSTEM VERSIONING + PARTITION BY SYSTEM_TIME INTERVAL 10 HOUR STARTS TIMESTAMP'2000-01-01 00:00:00' AUTO +PARTITIONS 6 +# TRX_ID versioning +create or replace table t1 (x int, +row_start bigint(20) unsigned as row start invisible, +row_end bigint(20) unsigned as row end invisible, +period for system_time (row_start, row_end), +index (row_end)) with system versioning engine innodb; +set timestamp= unix_timestamp('2000-01-01 00:00:00'); +insert t1 values (77); +delete from t1; +set timestamp= unix_timestamp('2000-01-03 00:00:00'); +insert t1 values (88); +delete from t1; +# Partitioning is not allowed for TRX_ID versioning +alter table t1 partition by system_time interval 11 hour auto; +ERROR HY000: `row_start` must be of type TIMESTAMP(6) for system-versioned table `t1` +drop table t1; +set debug_dbug= @old_dbug; diff --git a/mysql-test/suite/versioning/t/partition2.test b/mysql-test/suite/versioning/t/partition2.test new file mode 100644 index 0000000000000..5efb937d7ca13 --- /dev/null +++ b/mysql-test/suite/versioning/t/partition2.test @@ -0,0 +1,196 @@ +--source include/have_partition.inc +--source include/have_innodb.inc +--source include/maybe_debug.inc + +--echo # +--echo # MDEV-25529 Auto-create: Pre-existing historical data is not partitioned as specified by ALTER +--echo # + +# Test both index and scan, compare the results between them +if ($have_debug) +{ + set @old_dbug= @@debug_dbug; + set debug_dbug= '+d,test_mdev-25529'; +} + +create or replace table t1 (x int) with system versioning +partition by system_time limit 1; +alter table t1 partition by system_time limit 1 auto; + +create or replace table t1 (x int, + row_start timestamp(6) as row start invisible, + row_end timestamp(6) as row end invisible, + period for system_time (row_start, row_end), + index (row_end, row_start), + index (row_start), + index (row_end, x), + index (x, row_end), + index (row_end)) with system versioning +partition by system_time limit 1000 partitions 6; + +show create table t1; +set timestamp= unix_timestamp('2000-01-01 00:00:00'); +insert t1 values (0); +set timestamp= unix_timestamp('2000-01-01 00:10:00.22'); +update t1 set x= x + 1; +set timestamp= unix_timestamp('2000-01-01 01:00:00'); +update t1 set x= x + 1; +set timestamp= unix_timestamp('2000-01-01 01:30:00'); +update t1 set x= x + 1; +set timestamp= unix_timestamp('2000-01-01 02:00:00'); +update t1 set x= x + 1; + +select *, row_start, row_end from t1 partition (p0); +select *, row_start, row_end from t1 partition (p1); +select *, row_start, row_end from t1 partition (p2); +select *, row_start, row_end from t1 partition (p3); +select *, row_start, row_end from t1 partition (p4); +select *, row_start, row_end from t1 partition (pn); +set timestamp= default; + +ALTER table t1 partition by system_time +interval 1 hour starts '2000-01-01 00:00:00'; +select *, row_start, row_end from t1 partition (p0); +select *, row_start, row_end from t1 partition (p1); +select *, row_start, row_end from t1 partition (p2); +select *, row_start, row_end from t1 partition (p3); +select *, row_start, row_end from t1 partition (p4); +select *, row_start, row_end from t1 partition (pn); + +alter TABLE t1 partition by system_time +interval 1 hour; +select *, row_start, row_end from t1 partition (p0); +select *, row_start, row_end from t1 partition (p1); +select *, row_start, row_end from t1 partition (p2); +select *, row_start, row_end from t1 partition (p3); +select *, row_start, row_end from t1 partition (p4); +select *, row_start, row_end from t1 partition (pn); + +set @@system_versioning_alter_history= keep; +alter table t1 remove partitioning; +# First history: 2000-01-01 00:10:00.220000 +# Last history: 2000-01-01 02:00:00.00000021 +select *, row_start, row_end from t1 for system_time all; + +alter table t1 PARTITION by system_time interval 1 hour auto; + +show create table t1; +select *, row_start, row_end from t1 partition (p0); +select *, row_start, row_end from t1 partition (p1); +select *, row_start, row_end from t1 partition (pn); + +alter table t1 remove partitioning; +alter table t1 partition by SYSTEM_TIME interval 1 hour starts '2000-01-01 00:00:00' auto; + +show create table t1; +select *, row_start, row_end from t1 partition (p0); +select *, row_start, row_end from t1 partition (p1); +select *, row_start, row_end from t1 partition (pn); + +set timestamp= unix_timestamp('2000-01-02 03:01:23.456'); +delete from t1; +alter table t1 remove partitioning; +alter table t1 partition by SYSTEM_TIME interval 1 hour auto; + +show create table t1; +select *, row_start, row_end from t1 partition (p0); +select *, row_start, row_end from t1 partition (p1); +select *, row_start, row_end from t1 partition (p2); +select *, row_start, row_end from t1 partition (p27); +select *, row_start, row_end from t1 partition (pn); + +set timestamp= unix_timestamp('2000-01-02 03:01:23.456'); +delete from t1; +alter table t1 partition by system_time INTERVAL 20 minute auto force; +show create table t1; +select *, row_start, row_end from t1 partition (p0); +select *, row_start, row_end from t1 partition (p2); +select *, row_start, row_end from t1 partition (p5); +select *, row_start, row_end from t1 partition (pn); + +alter table t1 remove partitioning; +alter table t1 partition by system_time interval 1 hour +starts '2002-01-01 00:00:00' auto; + +--echo # STARTS changed and WARN_VERS_WRONG_STARTS is here +alter table t1 force partition by system_time interval 1 hour +starts '2003-01-01 00:00:00' auto; +show create table t1; + +alter table t1 partition by system_time interval 1 hour +starts '2004-01-01 00:00:00' auto force; +show create table t1; + +--echo # STARTS doesn't change without FORCE +alter table t1 partition by system_time interval 1 hour +starts '2005-01-01 00:00:00' auto; +show create table t1; + +--echo # min_ts == max_ts case, partitions decrease +delete history from t1; +set timestamp= unix_timestamp('2000-01-01 00:00:00'); +insert t1 values (77); +delete from t1; +alter table t1 partition by system_time interval 7 hour auto force; +show create table t1; + +--echo # Explicit partition list ignores AUTO FORCE +set timestamp= unix_timestamp('2000-01-03 00:00:00'); +insert t1 values (88); +delete from t1; +alter table t1 partition by system_time interval 8 hour auto force; +show create table t1; + +alter table t1 partition by system_time interval 8 hour auto ( + partition p0 history, + partition p1 history, + partition pn current) force; +show create table t1; + +--echo # Switch system_time -> hash +alter table t1 partition by hash(x) partitions 3; +show create table t1; +--echo # Switch hash -> system_time +alter table t1 partition by system_time interval 9 hour +starts '2001-01-01 00:00:00' auto force; +show create table t1; + +--echo # Slow scan warning +create or replace table t1 (x int, + row_start timestamp(6) as row start invisible, + row_end timestamp(6) as row end invisible, + period for system_time (row_start, row_end), + index (x, row_end, row_start), + index (row_start, row_end), + index (x, row_end)) with system versioning; +set timestamp= unix_timestamp('2000-01-01 00:00:00'); +insert t1 values (77); +delete from t1; +set timestamp= unix_timestamp('2000-01-03 00:00:00'); +insert t1 values (88); +delete from t1; +alter table t1 partition by system_time interval 10 hour auto; +show create table t1; + +--echo # TRX_ID versioning +create or replace table t1 (x int, + row_start bigint(20) unsigned as row start invisible, + row_end bigint(20) unsigned as row end invisible, + period for system_time (row_start, row_end), + index (row_end)) with system versioning engine innodb; +set timestamp= unix_timestamp('2000-01-01 00:00:00'); +insert t1 values (77); +delete from t1; +set timestamp= unix_timestamp('2000-01-03 00:00:00'); +insert t1 values (88); +delete from t1; +--echo # Partitioning is not allowed for TRX_ID versioning +--error ER_VERS_FIELD_WRONG_TYPE +alter table t1 partition by system_time interval 11 hour auto; + +drop table t1; + +if ($have_debug) +{ + set debug_dbug= @old_dbug; +} diff --git a/sql/field.cc b/sql/field.cc index 992429cb5b682..ed1a15958d87c 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -2372,6 +2372,30 @@ bool Field_vers_trx_id::get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate, ulong } +my_time_t Field_vers_trx_id::get_timestamp(const uchar *pos, ulong *sec_part) const +{ + ulonglong trx_id= uint8korr(pos); + THD *thd= get_thd(); + if (trx_id == ULONGLONG_MAX) + { + if (sec_part) + *sec_part= TIME_MAX_SECOND_PART; + return TIMESTAMP_MAX_VALUE; + } + TR_table trt(thd); + if (trt.query(trx_id)) + { + return trt[TR_table::FLD_COMMIT_TS]->get_timestamp(sec_part); + } + + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, + ER_VERS_NO_TRX_ID, ER_THD(thd, ER_VERS_NO_TRX_ID), + (longlong) trx_id); + + return -1; +} + + Field_str::Field_str(uchar *ptr_arg,uint32 len_arg, uchar *null_ptr_arg, uchar null_bit_arg, utype unireg_check_arg, const LEX_CSTRING *field_name_arg, diff --git a/sql/field.h b/sql/field.h index c31b38d78e623..2526437201318 100644 --- a/sql/field.h +++ b/sql/field.h @@ -2884,6 +2884,7 @@ class Field_vers_trx_id :public Field_longlong { { return get_date(ltime, fuzzydate, (ulonglong) val_int()); } + my_time_t get_timestamp(const uchar *pos, ulong *sec_part) const override; bool test_if_equality_guarantees_uniqueness(const Item *item) const override; Data_type_compatibility can_optimize_keypart_ref(const Item_bool_func *, const Item *) diff --git a/sql/partition_info.cc b/sql/partition_info.cc index d99750b7e5797..f16e017a40b23 100644 --- a/sql/partition_info.cc +++ b/sql/partition_info.cc @@ -2873,7 +2873,9 @@ bool partition_info::vers_set_interval(THD* thd, Item* interval, } if (!table) { - if (thd->query_start() < vers_info->interval.start) { + if (thd->query_start() < vers_info->interval.start && + !(auto_hist && (thd->lex->alter_info.flags & ALTER_RECREATE))) + { TimestampString str_interval(thd, vers_info->interval.start); TimestampString str_query(thd, thd->query_start()); push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, @@ -2897,6 +2899,7 @@ bool partition_info::vers_set_interval(THD* thd, Item* interval, } +/* Round down STARTS to units based on INTERVAL value */ bool partition_info::vers_set_starts(THD *thd, my_time_t ts) { MYSQL_TIME ltime; diff --git a/sql/share/errmsg-utf8.txt b/sql/share/errmsg-utf8.txt index 53ca57eeaa292..9c8ad3f275e84 100644 --- a/sql/share/errmsg-utf8.txt +++ b/sql/share/errmsg-utf8.txt @@ -10758,3 +10758,7 @@ ER_CM_OPTION_MISSING_REQUIREMENT eng "CHANGE MASTER TO option '%s=%s' is missing requirement %s" ER_SLAVE_STATEMENT_TIMEOUT 70100 eng "Slave log event execution was interrupted (slave_max_statement_time exceeded)" +WARN_VERS_WRONG_STARTS + eng "%`s: STARTS timestamp %s is above earliest history date %s and was set to %s" +WARN_VERS_SLOW_ROW_END + eng "%`s: ROW END index not found, using slow scan" diff --git a/sql/sql_partition.cc b/sql/sql_partition.cc index bda2282efd465..22b3f402cb394 100644 --- a/sql/sql_partition.cc +++ b/sql/sql_partition.cc @@ -6141,6 +6141,67 @@ the generated partition syntax in a correct manner. } } + if (table->versioned() && + (alter_info->partition_flags & ALTER_PARTITION_INFO) && + part_info->part_type == VERSIONING_PARTITION && + part_info->vers_info->auto_hist && + alt_part_info->use_default_partitions && + (!*fast_alter_table || (alter_info->flags & ALTER_RECREATE))) + { + if (*fast_alter_table) + *fast_alter_table= false; + my_timespec_t min_ts, max_ts; + Vers_part_info *vers_info= part_info->vers_info; + auto &interval= vers_info->interval; + if (table->vers_get_history_range(thd, min_ts, max_ts)) + DBUG_RETURN(true); + if (max_ts.sec > MY_TIME_T_MIN) /* there is history in the table */ + { + if (max_ts.usec) + { + max_ts.sec++; + max_ts.usec= 0; + } + DBUG_ASSERT(min_ts.sec <= max_ts.sec); + if (vers_info->starts_clause) + { + if (interval.start > min_ts.sec) + { + TimestampString str_interval(thd, interval.start); + if (part_info->vers_set_starts(thd, min_ts.sec)) + DBUG_RETURN(true); + TimestampString str_min_ts(thd, min_ts); + TimestampString str_interval2(thd, interval.start); + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, + WARN_VERS_WRONG_STARTS, ER(WARN_VERS_WRONG_STARTS), + table->s->table_name.str, + str_interval.cstr(), str_min_ts.cstr(), str_interval2.cstr()); + + } + } + else if (part_info->vers_set_starts(thd, min_ts.sec)) + DBUG_RETURN(true); + + part_info->use_default_num_partitions= false; + part_info->use_default_partitions= true; + part_info->default_partitions_setup= false; + part_info->partitions.empty(); + + my_time_t range= max_ts.sec - interval.start; + DBUG_ASSERT(range >= 0); + if (!range) + range++; + const longlong i_sec= interval2sec(&interval.step); + DBUG_ASSERT(i_sec > 0); + uint hist_parts= range / i_sec; + if (hist_parts * i_sec != range) + hist_parts++; + part_info->num_parts= hist_parts + 1; + } /* if (max_ts.sec > MY_TIME_T_MIN) */ + else + DBUG_ASSERT(max_ts == MY_TIMESPEC_MIN); /* no history in table */ + } /* if (need to get history range) */ + /* Set up partition default_engine_type either from the create_info or from the previus table @@ -6166,7 +6227,7 @@ the generated partition syntax in a correct manner. DBUG_ASSERT(create_info->db_type); create_info->db_type= partition_hton; } - } + } /* if (thd->work_part_info) */ } DBUG_RETURN(FALSE); err: diff --git a/sql/sql_table.cc b/sql/sql_table.cc index d73af1fedb401..ed0b3f2cde6d1 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -62,6 +62,7 @@ #include "rpl_mi.h" #include "rpl_rli.h" #include "log.h" +#include "key.h" #ifdef WITH_WSREP #include "wsrep_mysqld.h" @@ -2394,7 +2395,7 @@ void promote_first_timestamp_column(List *column_definitions) } } -static bool key_cmp(const Key_part_spec &a, const Key_part_spec &b) +static bool key_eq(const Key_part_spec &a, const Key_part_spec &b) { return a.length == b.length && a.asc == b.asc && !lex_string_cmp(system_charset_info, &a.field_name, &b.field_name); @@ -2438,7 +2439,7 @@ static void check_duplicate_key(THD *thd, const Key *key, const KEY *key_info, } if (std::equal(key->columns.begin(), key->columns.end(), k.columns.begin(), - key_cmp)) + key_eq)) { push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, ER_DUP_INDEX, ER_THD(thd, ER_DUP_INDEX), key_info->name.str); @@ -13285,3 +13286,142 @@ bool HA_CREATE_INFO:: } return false; } + + +#ifdef WITH_PARTITION_STORAGE_ENGINE +bool TABLE::vers_get_history_range(THD *thd, my_timespec_t &min_ts, + my_timespec_t &max_ts) +{ + DBUG_ASSERT(versioned()); + // Find best key + KEY *key= key_info, *best_key= NULL; + uint best_idx; + Field *end_field= vers_end_field(); + const field_index_t end_field_idx= end_field->field_index; + for (uint idx= 0; idx < s->keys; idx++, key++) + { + DBUG_ASSERT(key->key_part->fieldnr > 0); + if (key->key_part->fieldnr - 1 == end_field_idx && + (!best_key || best_key->key_length > key->key_length)) + { + best_key= key; + best_idx= idx; + } + } + + int error; + my_timespec_t ts; + min_ts= MY_TIMESPEC_MAX; + max_ts= MY_TIMESPEC_MIN; +#ifndef DBUG_OFF + my_timespec_t min_ts2, max_ts2; +#endif /* DBUG_OFF */ + MY_BITMAP *save_read_set= read_set; + MY_BITMAP *save_write_set= write_set; + DBUG_ASSERT(save_read_set != &tmp_set); + bitmap_clear_all(&tmp_set); + column_bitmaps_set(&tmp_set, &tmp_set); + bitmap_set_bit(read_set, end_field->field_index); + file->column_bitmaps_signal(); + DBUG_ASSERT(thd->mdl_context.is_lock_owner(MDL_key::TABLE, s->db.str, + s->table_name.str, MDL_SHARED_READ)); + if ((error= file->ha_external_lock(thd, F_RDLCK))) + goto end; + + if (best_key) + { + uchar key[MAX_KEY_LENGTH]; + KEY *best_key_info= key_info + best_idx; + end_field->set_max(); + KEY_PART_INFO *key_part= best_key_info->key_part; + const uint key_prefix_len= key_part[0].store_length; + key_copy(key, record[0], best_key_info, key_prefix_len); + + /* Get range from index */ + if ((error= file->ha_index_init(best_idx, true))) + goto end_unlock; + + if (!(error= file->ha_index_first(record[0]))) + { + min_ts.sec= end_field->get_timestamp(&min_ts.usec); + error= file->ha_index_read_map(record[0], (uchar*) key, (key_part_map) 1, + HA_READ_BEFORE_KEY); + if (!error) + { + max_ts.sec= end_field->get_timestamp(&max_ts.usec); + } + } + + file->ha_index_end(); + + if (error == HA_ERR_END_OF_FILE || error == HA_ERR_KEY_NOT_FOUND) + error= 0; + else if (error) + goto end_unlock; + +#ifndef DBUG_OFF + /* Test both index and scan, compare the results between them */ + min_ts2= min_ts; + max_ts2= max_ts; + if (DBUG_IF("test_mdev-25529")) + goto jump_scan; +#endif /* DBUG_OFF */ + } + else + { + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, + WARN_VERS_SLOW_ROW_END, ER(WARN_VERS_SLOW_ROW_END), + s->table_name.str); +#ifndef DBUG_OFF +jump_scan: +#endif /* DBUG_OFF */ + /* Get range by scan */ + if ((error= file->ha_rnd_init(1))) + goto end_unlock; + + /* can_continue_handler_scan() is only for heap (record changed protection) */ + while (!(error= file->can_continue_handler_scan()) && + !(error= file->ha_rnd_next(record[0]))) + { + ts.sec= end_field->get_timestamp(&ts.usec); + if (ts == MY_TIMESPEC_MAX) + continue; + if (ts < min_ts) + min_ts= ts; + if (ts > max_ts) + max_ts= ts; + } + + file->ha_rnd_end(); +#ifndef DBUG_OFF + if (best_key) + { + DBUG_ASSERT(DBUG_IF("test_mdev-25529")); + DBUG_ASSERT(min_ts == min_ts2); + DBUG_ASSERT(max_ts == max_ts2); + } +#endif /* DBUG_OFF */ + } + + if (error == HA_ERR_END_OF_FILE) + error= 0; + +end_unlock: + file->ha_external_unlock(thd); + +end: + if (error) + { + myf flags= 0; + + if (file->is_fatal_error(error, HA_CHECK_ALL)) + flags|= ME_FATAL; /* Other handler errors are fatal */ + + file->print_error(error, MYF(flags)); + } + + column_bitmaps_set(save_read_set, save_write_set); + file->column_bitmaps_signal(); + return error; +} +#endif /* WITH_PARTITION_STORAGE_ENGINE */ diff --git a/sql/table.h b/sql/table.h index e287ffb330abc..3c5f9f012dcf1 100644 --- a/sql/table.h +++ b/sql/table.h @@ -1851,6 +1851,8 @@ struct TABLE #ifdef WITH_PARTITION_STORAGE_ENGINE bool vers_switch_partition(THD *thd, TABLE_LIST *table_list, Open_table_context *ot_ctx); + bool vers_get_history_range(THD *thd, my_timespec_t &min_ts, + my_timespec_t &max_ts); #endif int update_generated_fields(); From 9c46f05b59b3e0304cfe9f51db89dbd5b78ec924 Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Tue, 14 Apr 2026 15:55:16 +0300 Subject: [PATCH 08/15] vers_calc_hist_parts() --- .../suite/versioning/r/partition2.result | 92 +++++++++++++++++-- mysql-test/suite/versioning/t/partition2.test | 25 +++++ sql/sql_partition.cc | 78 ++++++++++++++-- 3 files changed, 180 insertions(+), 15 deletions(-) diff --git a/mysql-test/suite/versioning/r/partition2.result b/mysql-test/suite/versioning/r/partition2.result index 2e38e1964192b..eabe688a4b852 100644 --- a/mysql-test/suite/versioning/r/partition2.result +++ b/mysql-test/suite/versioning/r/partition2.result @@ -1,3 +1,4 @@ +set @@session.time_zone='+00:00'; # # MDEV-25529 Auto-create: Pre-existing historical data is not partitioned as specified by ALTER # @@ -57,7 +58,7 @@ select *, row_start, row_end from t1 partition (p4); x row_start row_end select *, row_start, row_end from t1 partition (pn); x row_start row_end -4 2000-01-01 02:00:00.000000 2038-01-19 06:14:07.999999 +4 2000-01-01 02:00:00.000000 2038-01-19 03:14:07.999999 set timestamp= default; ALTER table t1 partition by system_time interval 1 hour starts '2000-01-01 00:00:00'; @@ -77,7 +78,7 @@ select *, row_start, row_end from t1 partition (p4); x row_start row_end select *, row_start, row_end from t1 partition (pn); x row_start row_end -4 2000-01-01 02:00:00.000000 2038-01-19 06:14:07.999999 +4 2000-01-01 02:00:00.000000 2038-01-19 03:14:07.999999 alter TABLE t1 partition by system_time interval 1 hour; select *, row_start, row_end from t1 partition (p0); @@ -96,7 +97,7 @@ select *, row_start, row_end from t1 partition (p4); x row_start row_end select *, row_start, row_end from t1 partition (pn); x row_start row_end -4 2000-01-01 02:00:00.000000 2038-01-19 06:14:07.999999 +4 2000-01-01 02:00:00.000000 2038-01-19 03:14:07.999999 set @@system_versioning_alter_history= keep; alter table t1 remove partitioning; select *, row_start, row_end from t1 for system_time all; @@ -105,7 +106,7 @@ x row_start row_end 1 2000-01-01 00:10:00.220000 2000-01-01 01:00:00.000000 2 2000-01-01 01:00:00.000000 2000-01-01 01:30:00.000000 3 2000-01-01 01:30:00.000000 2000-01-01 02:00:00.000000 -4 2000-01-01 02:00:00.000000 2038-01-19 06:14:07.999999 +4 2000-01-01 02:00:00.000000 2038-01-19 03:14:07.999999 alter table t1 PARTITION by system_time interval 1 hour auto; show create table t1; Table Create Table @@ -132,7 +133,7 @@ x row_start row_end 3 2000-01-01 01:30:00.000000 2000-01-01 02:00:00.000000 select *, row_start, row_end from t1 partition (pn); x row_start row_end -4 2000-01-01 02:00:00.000000 2038-01-19 06:14:07.999999 +4 2000-01-01 02:00:00.000000 2038-01-19 03:14:07.999999 alter table t1 remove partitioning; alter table t1 partition by SYSTEM_TIME interval 1 hour starts '2000-01-01 00:00:00' auto; show create table t1; @@ -160,7 +161,7 @@ x row_start row_end 3 2000-01-01 01:30:00.000000 2000-01-01 02:00:00.000000 select *, row_start, row_end from t1 partition (pn); x row_start row_end -4 2000-01-01 02:00:00.000000 2038-01-19 06:14:07.999999 +4 2000-01-01 02:00:00.000000 2038-01-19 03:14:07.999999 set timestamp= unix_timestamp('2000-01-02 03:01:23.456'); delete from t1; alter table t1 remove partitioning; @@ -394,6 +395,85 @@ t1 CREATE TABLE `t1` ( ) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci WITH SYSTEM VERSIONING PARTITION BY SYSTEM_TIME INTERVAL 9 HOUR STARTS TIMESTAMP'2000-01-01 00:00:00' AUTO PARTITIONS 7 +# Year/month interval +delete history from t1; +alter table t1 remove partitioning; +set timestamp= unix_timestamp('2000-01-01 00:00:00'); +insert t1 values (77); +delete from t1; +set timestamp= unix_timestamp('2003-10-01 00:00:00'); +insert t1 values (88); +delete from t1; +alter table t1 partition by system_time interval 11 month auto force; +show create table t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `x` int(11) DEFAULT NULL, + `row_start` timestamp(6) GENERATED ALWAYS AS ROW START INVISIBLE, + `row_end` timestamp(6) GENERATED ALWAYS AS ROW END INVISIBLE, + KEY `row_end` (`row_end`,`row_start`), + KEY `row_start` (`row_start`), + KEY `row_end_2` (`row_end`,`x`), + KEY `x` (`x`,`row_end`), + KEY `row_end_3` (`row_end`), + PERIOD FOR SYSTEM_TIME (`row_start`, `row_end`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci WITH SYSTEM VERSIONING + PARTITION BY SYSTEM_TIME INTERVAL 11 MONTH STARTS TIMESTAMP'2000-01-01 00:00:00' AUTO +PARTITIONS 6 +alter table t1 partition by system_time interval 1 year auto force; +Warnings: +Warning 4193 `t1`: STARTS timestamp 2003-10-01 00:00:00 is above earliest history date 2000-01-01 00:00:00 and was set to 2000-01-01 00:00:00 +show create table t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `x` int(11) DEFAULT NULL, + `row_start` timestamp(6) GENERATED ALWAYS AS ROW START INVISIBLE, + `row_end` timestamp(6) GENERATED ALWAYS AS ROW END INVISIBLE, + KEY `row_end` (`row_end`,`row_start`), + KEY `row_start` (`row_start`), + KEY `row_end_2` (`row_end`,`x`), + KEY `x` (`x`,`row_end`), + KEY `row_end_3` (`row_end`), + PERIOD FOR SYSTEM_TIME (`row_start`, `row_end`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci WITH SYSTEM VERSIONING + PARTITION BY SYSTEM_TIME INTERVAL 1 YEAR STARTS TIMESTAMP'2000-01-01 00:00:00' AUTO +PARTITIONS 5 +# No history +delete history from t1; +set timestamp= unix_timestamp('2000-01-01 00:00:00'); +alter table t1 partition by system_time interval 1 hour auto force; +show create table t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `x` int(11) DEFAULT NULL, + `row_start` timestamp(6) GENERATED ALWAYS AS ROW START INVISIBLE, + `row_end` timestamp(6) GENERATED ALWAYS AS ROW END INVISIBLE, + KEY `row_end` (`row_end`,`row_start`), + KEY `row_start` (`row_start`), + KEY `row_end_2` (`row_end`,`x`), + KEY `x` (`x`,`row_end`), + KEY `row_end_3` (`row_end`), + PERIOD FOR SYSTEM_TIME (`row_start`, `row_end`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci WITH SYSTEM VERSIONING + PARTITION BY SYSTEM_TIME INTERVAL 1 HOUR STARTS TIMESTAMP'2000-01-01 00:00:00' AUTO +PARTITIONS 5 +insert t1 values (99); +alter table t1 partition by system_time interval 1 hour auto force; +show create table t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `x` int(11) DEFAULT NULL, + `row_start` timestamp(6) GENERATED ALWAYS AS ROW START INVISIBLE, + `row_end` timestamp(6) GENERATED ALWAYS AS ROW END INVISIBLE, + KEY `row_end` (`row_end`,`row_start`), + KEY `row_start` (`row_start`), + KEY `row_end_2` (`row_end`,`x`), + KEY `x` (`x`,`row_end`), + KEY `row_end_3` (`row_end`), + PERIOD FOR SYSTEM_TIME (`row_start`, `row_end`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci WITH SYSTEM VERSIONING + PARTITION BY SYSTEM_TIME INTERVAL 1 HOUR STARTS TIMESTAMP'2000-01-01 00:00:00' AUTO +PARTITIONS 5 # Slow scan warning create or replace table t1 (x int, row_start timestamp(6) as row start invisible, diff --git a/mysql-test/suite/versioning/t/partition2.test b/mysql-test/suite/versioning/t/partition2.test index 5efb937d7ca13..fd436e97d59ed 100644 --- a/mysql-test/suite/versioning/t/partition2.test +++ b/mysql-test/suite/versioning/t/partition2.test @@ -2,6 +2,8 @@ --source include/have_innodb.inc --source include/maybe_debug.inc +set @@session.time_zone='+00:00'; + --echo # --echo # MDEV-25529 Auto-create: Pre-existing historical data is not partitioned as specified by ALTER --echo # @@ -155,6 +157,29 @@ alter table t1 partition by system_time interval 9 hour starts '2001-01-01 00:00:00' auto force; show create table t1; +--echo # Year/month interval +delete history from t1; +alter table t1 remove partitioning; +set timestamp= unix_timestamp('2000-01-01 00:00:00'); +insert t1 values (77); +delete from t1; +set timestamp= unix_timestamp('2003-10-01 00:00:00'); +insert t1 values (88); +delete from t1; +alter table t1 partition by system_time interval 11 month auto force; +show create table t1; +alter table t1 partition by system_time interval 1 year auto force; +show create table t1; + +--echo # No history +delete history from t1; +set timestamp= unix_timestamp('2000-01-01 00:00:00'); +alter table t1 partition by system_time interval 1 hour auto force; +show create table t1; +insert t1 values (99); +alter table t1 partition by system_time interval 1 hour auto force; +show create table t1; + --echo # Slow scan warning create or replace table t1 (x int, row_start timestamp(6) as row start invisible, diff --git a/sql/sql_partition.cc b/sql/sql_partition.cc index 22b3f402cb394..2eef99ae9c733 100644 --- a/sql/sql_partition.cc +++ b/sql/sql_partition.cc @@ -100,6 +100,9 @@ static int get_partition_id_linear_key_sub(partition_info *part_info, uint32 *pa static uint32 get_next_partition_via_walking(PARTITION_ITERATOR*); static void set_up_range_analysis_info(partition_info *part_info); static uint32 get_next_subpartition_via_walking(PARTITION_ITERATOR*); +static bool vers_calc_hist_parts(THD *thd, my_time_t end, + const Vers_part_info::interval_t &interval, + uint *hist_parts); #endif uint32 get_next_partition_id_range(PARTITION_ITERATOR* part_iter); @@ -6187,15 +6190,9 @@ the generated partition syntax in a correct manner. part_info->default_partitions_setup= false; part_info->partitions.empty(); - my_time_t range= max_ts.sec - interval.start; - DBUG_ASSERT(range >= 0); - if (!range) - range++; - const longlong i_sec= interval2sec(&interval.step); - DBUG_ASSERT(i_sec > 0); - uint hist_parts= range / i_sec; - if (hist_parts * i_sec != range) - hist_parts++; + uint hist_parts= 0; + if (vers_calc_hist_parts(thd, max_ts.sec, interval, &hist_parts)) + DBUG_RETURN(true); part_info->num_parts= hist_parts + 1; } /* if (max_ts.sec > MY_TIME_T_MIN) */ else @@ -6238,6 +6235,69 @@ the generated partition syntax in a correct manner. } +static bool vers_calc_hist_parts(THD *thd, my_time_t end, + const Vers_part_info::interval_t &interval, + uint *hist_parts) +{ + uint error= 0; + my_time_t start= interval.start; + DBUG_ASSERT(hist_parts != NULL); + *hist_parts= 0; + + if (end < start) + return false; + + if (!(interval.step.year || interval.step.month)) + { + my_time_t range= end - start; + if (!range) + range++; + + const longlong i_sec= interval2sec(&interval.step); + DBUG_ASSERT(i_sec > 0); + + *hist_parts= static_cast(range / i_sec); + if ((*hist_parts) * i_sec != range) + (*hist_parts)++; + if (*hist_parts >= MAX_PARTITIONS) + { + my_error(ER_TOO_MANY_PARTITIONS_ERROR, MYF(0)); + return true; + } + return false; + } + + MYSQL_TIME boundary; + thd->variables.time_zone->gmt_sec_to_TIME(&boundary, start); + + while (start < end) + { + if (*hist_parts >= MAX_PARTITIONS) + { + my_error(ER_TOO_MANY_PARTITIONS_ERROR, MYF(0)); + return true; + } + + if (date_add_interval(thd, &boundary, interval.type, interval.step)) + { + my_error(ER_DATA_OUT_OF_RANGE, MYF(0), "TIMESTAMP", "INTERVAL"); + return true; + } + + start= thd->variables.time_zone->TIME_to_gmt_sec(&boundary, &error); + if (start <= 0 || error) + { + my_error(ER_DATA_OUT_OF_RANGE, MYF(0), "TIMESTAMP", "INTERVAL"); + return true; + } + + (*hist_parts)++; + } + + return false; +} + + /* Change partitions, used to implement ALTER TABLE ADD/REORGANIZE/COALESCE partitions. This method is used to implement both single-phase and multi- From 7a43f68f2be457a8abf25e184ddb288c31d05c07 Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Tue, 14 Apr 2026 19:29:50 +0300 Subject: [PATCH 09/15] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- sql/sql_table.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sql/sql_table.cc b/sql/sql_table.cc index ed0b3f2cde6d1..9edf9c667da3b 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -13370,7 +13370,8 @@ bool TABLE::vers_get_history_range(THD *thd, my_timespec_t &min_ts, else { push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, - WARN_VERS_SLOW_ROW_END, ER(WARN_VERS_SLOW_ROW_END), + WARN_VERS_SLOW_ROW_END, + ER_THD(thd, WARN_VERS_SLOW_ROW_END), s->table_name.str); #ifndef DBUG_OFF jump_scan: From 68139bf55f2aeb3096d3595375561702dff5a296 Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Tue, 14 Apr 2026 19:30:18 +0300 Subject: [PATCH 10/15] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- sql/sql_partition.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sql/sql_partition.cc b/sql/sql_partition.cc index 2eef99ae9c733..da867d7104209 100644 --- a/sql/sql_partition.cc +++ b/sql/sql_partition.cc @@ -6176,7 +6176,7 @@ the generated partition syntax in a correct manner. TimestampString str_min_ts(thd, min_ts); TimestampString str_interval2(thd, interval.start); push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, - WARN_VERS_WRONG_STARTS, ER(WARN_VERS_WRONG_STARTS), + WARN_VERS_WRONG_STARTS, ER_THD(thd, WARN_VERS_WRONG_STARTS), table->s->table_name.str, str_interval.cstr(), str_min_ts.cstr(), str_interval2.cstr()); From 2415a302ae91e5ef40d82f3d057bd1506c394088 Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Tue, 14 Apr 2026 19:31:07 +0300 Subject: [PATCH 11/15] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- sql/sql_class.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sql/sql_class.h b/sql/sql_class.h index 800b8adc1e11d..3210c0321655c 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -8436,7 +8436,7 @@ class TimestampString : public String public: TimestampString(THD *thd, my_timespec_t ts) : thd{thd}, ts{ts} {} - TimestampString(THD *thd, my_time_t ts) : thd{thd}, ts({ts, 0}) {} + TimestampString(THD *thd, my_time_t sec) : thd{thd}, ts{sec, 0} {} const char * cstr(uint dec= 0) { From 54f1c9b4a90814da37dc0bb967348eafa5d13144 Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Tue, 14 Apr 2026 20:05:31 +0300 Subject: [PATCH 12/15] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- sql/sql_table.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 9edf9c667da3b..267825cf54bb8 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -13302,6 +13302,7 @@ bool TABLE::vers_get_history_range(THD *thd, my_timespec_t &min_ts, { DBUG_ASSERT(key->key_part->fieldnr > 0); if (key->key_part->fieldnr - 1 == end_field_idx && + !(key->key_part->key_part_flag & HA_REVERSE_SORT) && (!best_key || best_key->key_length > key->key_length)) { best_key= key; From 1201eaf394ceb20e6e6e985c9f82206111cf88f6 Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Tue, 14 Apr 2026 20:08:29 +0300 Subject: [PATCH 13/15] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- sql/share/errmsg-utf8.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sql/share/errmsg-utf8.txt b/sql/share/errmsg-utf8.txt index 9c8ad3f275e84..d4468e0c83cc3 100644 --- a/sql/share/errmsg-utf8.txt +++ b/sql/share/errmsg-utf8.txt @@ -10759,6 +10759,10 @@ ER_CM_OPTION_MISSING_REQUIREMENT ER_SLAVE_STATEMENT_TIMEOUT 70100 eng "Slave log event execution was interrupted (slave_max_statement_time exceeded)" WARN_VERS_WRONG_STARTS + chi "%`s: STARTS 时间戳 %s 晚于最早历史日期 %s,已被设置为 %s" eng "%`s: STARTS timestamp %s is above earliest history date %s and was set to %s" + spa "%`s: la marca de tiempo STARTS %s es posterior a la fecha histórica más temprana %s y se estableció en %s" WARN_VERS_SLOW_ROW_END + chi "%`s: 未找到 ROW END 索引,正在使用慢速扫描" eng "%`s: ROW END index not found, using slow scan" + spa "%`s: no se encontró el índice ROW END, usando escaneo lento" From 908878413382e0952b45a4a927dbcb0d67e56592 Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Tue, 14 Apr 2026 20:09:46 +0300 Subject: [PATCH 14/15] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- sql/sql_partition.cc | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sql/sql_partition.cc b/sql/sql_partition.cc index da867d7104209..ab74fc1d1079d 100644 --- a/sql/sql_partition.cc +++ b/sql/sql_partition.cc @@ -6172,7 +6172,10 @@ the generated partition syntax in a correct manner. { TimestampString str_interval(thd, interval.start); if (part_info->vers_set_starts(thd, min_ts.sec)) + { + my_error(ER_PART_WRONG_VALUE, MYF(0), "STARTS"); DBUG_RETURN(true); + } TimestampString str_min_ts(thd, min_ts); TimestampString str_interval2(thd, interval.start); push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, @@ -6183,7 +6186,10 @@ the generated partition syntax in a correct manner. } } else if (part_info->vers_set_starts(thd, min_ts.sec)) + { + my_error(ER_PART_WRONG_VALUE, MYF(0), "STARTS"); DBUG_RETURN(true); + } part_info->use_default_num_partitions= false; part_info->use_default_partitions= true; From 1a69ca2f5b7bd35e90b85360dc0f61714e279893 Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Tue, 14 Apr 2026 20:32:18 +0300 Subject: [PATCH 15/15] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- sql/sql_table.cc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 267825cf54bb8..7ff072a1a3fdd 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -13331,12 +13331,12 @@ bool TABLE::vers_get_history_range(THD *thd, my_timespec_t &min_ts, if (best_key) { - uchar key[MAX_KEY_LENGTH]; + uchar search_key[MAX_KEY_LENGTH]; KEY *best_key_info= key_info + best_idx; end_field->set_max(); KEY_PART_INFO *key_part= best_key_info->key_part; const uint key_prefix_len= key_part[0].store_length; - key_copy(key, record[0], best_key_info, key_prefix_len); + key_copy(search_key, record[0], best_key_info, key_prefix_len); /* Get range from index */ if ((error= file->ha_index_init(best_idx, true))) @@ -13345,8 +13345,8 @@ bool TABLE::vers_get_history_range(THD *thd, my_timespec_t &min_ts, if (!(error= file->ha_index_first(record[0]))) { min_ts.sec= end_field->get_timestamp(&min_ts.usec); - error= file->ha_index_read_map(record[0], (uchar*) key, (key_part_map) 1, - HA_READ_BEFORE_KEY); + error= file->ha_index_read_map(record[0], (uchar*) search_key, + (key_part_map) 1, HA_READ_BEFORE_KEY); if (!error) { max_ts.sec= end_field->get_timestamp(&max_ts.usec);