From 5056c74e1271e511836538b763906ca469d9219d Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Tue, 14 Apr 2026 13:31:17 +0300 Subject: [PATCH 1/8] 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 2/8] 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 574366076df4abffceef0f227fd8237cc77624e6 Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Tue, 14 Apr 2026 13:31:17 +0300 Subject: [PATCH 3/8] 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..b241842e319ec 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) == nullptr; +} + + 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..3210c0321655c 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 sec) : thd{thd}, ts{sec, 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 110252bb8ed98d6da3c7f95ac74085ecd9a9e4d6 Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Tue, 14 Apr 2026 13:31:17 +0300 Subject: [PATCH 4/8] MDEV-25529 cleanup for vers_set_starts() and starts_clause --- sql/partition_info.cc | 37 +++++++++++++++++++++++-------------- sql/partition_info.h | 5 +++++ 2 files changed, 28 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..7e32c8ecfabc3 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) { @@ -59,6 +61,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= src.now_part; hist_part= src.hist_part; return *this; @@ -91,6 +94,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 +421,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 454decd6889146615ebe4fba0d01a6dce85e87fe Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Tue, 14 Apr 2026 13:31:17 +0300 Subject: [PATCH 5/8] 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 f7e89528de7210224daeda0aa76e2bee702a5532 Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Tue, 14 Apr 2026 13:31:17 +0300 Subject: [PATCH 6/8] 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 219e9788a256d5537bbf82e42577a3fdadb5c3bf Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Tue, 14 Apr 2026 13:31:18 +0300 Subject: [PATCH 7/8] 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 | 439 ++++++++++++++++++ mysql-test/suite/versioning/t/partition2.test | 196 ++++++++ sql/field.cc | 26 ++ sql/field.h | 1 + sql/partition_info.cc | 5 +- sql/share/errmsg-utf8.txt | 8 + sql/sql_partition.cc | 72 ++- sql/sql_table.cc | 146 +++++- sql/table.h | 2 + 10 files changed, 892 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..418e750b7241e --- /dev/null +++ b/mysql-test/suite/versioning/r/partition2.result @@ -0,0 +1,439 @@ +# +# 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; +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; +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..0a6eb44e0f5e7 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -2372,6 +2372,32 @@ 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..d4468e0c83cc3 100644 --- a/sql/share/errmsg-utf8.txt +++ b/sql/share/errmsg-utf8.txt @@ -10758,3 +10758,11 @@ 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 + 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" diff --git a/sql/sql_partition.cc b/sql/sql_partition.cc index bda2282efd465..8420de1186f52 100644 --- a/sql/sql_partition.cc +++ b/sql/sql_partition.cc @@ -6141,6 +6141,76 @@ 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 */ + { + DBUG_ASSERT(max_ts.sec < TIMESTAMP_MAX_VALUE); + DBUG_ASSERT(min_ts.sec <= max_ts.sec); + if (max_ts.usec) + { + max_ts.sec++; + max_ts.usec= 0; + } + 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)) + { + my_error(ER_PART_WRONG_VALUE, MYF(0), + table->s->table_name.str, "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, + 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()); + + } + } + else if (part_info->vers_set_starts(thd, min_ts.sec)) + { + my_error(ER_PART_WRONG_VALUE, MYF(0), + table->s->table_name.str, "STARTS"); + 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 +6236,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..7ff072a1a3fdd 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,144 @@ 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 && + !(key->key_part->key_part_flag & HA_REVERSE_SORT) && + (!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 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(search_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*) search_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_THD(thd, 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 bb686dfbac8218ce23bad2c013ca33ae272860cd Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Tue, 14 Apr 2026 15:55:16 +0300 Subject: [PATCH 8/8] vers_calc_hist_parts() --- .../suite/versioning/r/partition2.result | 134 +++++++++++++++--- mysql-test/suite/versioning/t/partition2.test | 29 +++- sql/sql_partition.cc | 83 +++++++++-- 3 files changed, 216 insertions(+), 30 deletions(-) diff --git a/mysql-test/suite/versioning/r/partition2.result b/mysql-test/suite/versioning/r/partition2.result index 418e750b7241e..1b452a4c5c861 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 # @@ -14,7 +15,7 @@ index (row_end, row_start), index (row_start), index (row_end, x), index (x, row_end), -index (row_end)) with system versioning +index (row_end desc)) with system versioning partition by system_time limit 1000 partitions 6; show create table t1; Table Create Table @@ -26,7 +27,7 @@ t1 CREATE TABLE `t1` ( KEY `row_start` (`row_start`), KEY `row_end_2` (`row_end`,`x`), KEY `x` (`x`,`row_end`), - KEY `row_end_3` (`row_end`), + KEY `row_end_3` (`row_end` DESC), 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 @@ -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 @@ -117,7 +118,7 @@ t1 CREATE TABLE `t1` ( KEY `row_start` (`row_start`), KEY `row_end_2` (`row_end`,`x`), KEY `x` (`x`,`row_end`), - KEY `row_end_3` (`row_end`), + KEY `row_end_3` (`row_end` DESC), 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 @@ -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; @@ -145,7 +146,7 @@ t1 CREATE TABLE `t1` ( KEY `row_start` (`row_start`), KEY `row_end_2` (`row_end`,`x`), KEY `x` (`x`,`row_end`), - KEY `row_end_3` (`row_end`), + KEY `row_end_3` (`row_end` DESC), 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 @@ -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; @@ -175,7 +176,7 @@ t1 CREATE TABLE `t1` ( KEY `row_start` (`row_start`), KEY `row_end_2` (`row_end`,`x`), KEY `x` (`x`,`row_end`), - KEY `row_end_3` (`row_end`), + KEY `row_end_3` (`row_end` DESC), 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 @@ -208,7 +209,7 @@ t1 CREATE TABLE `t1` ( KEY `row_start` (`row_start`), KEY `row_end_2` (`row_end`,`x`), KEY `x` (`x`,`row_end`), - KEY `row_end_3` (`row_end`), + KEY `row_end_3` (`row_end` DESC), 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 @@ -245,7 +246,7 @@ t1 CREATE TABLE `t1` ( KEY `row_start` (`row_start`), KEY `row_end_2` (`row_end`,`x`), KEY `x` (`x`,`row_end`), - KEY `row_end_3` (`row_end`), + KEY `row_end_3` (`row_end` DESC), 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 @@ -265,7 +266,7 @@ t1 CREATE TABLE `t1` ( KEY `row_start` (`row_start`), KEY `row_end_2` (`row_end`,`x`), KEY `x` (`x`,`row_end`), - KEY `row_end_3` (`row_end`), + KEY `row_end_3` (`row_end` DESC), 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 @@ -285,7 +286,7 @@ t1 CREATE TABLE `t1` ( KEY `row_start` (`row_start`), KEY `row_end_2` (`row_end`,`x`), KEY `x` (`x`,`row_end`), - KEY `row_end_3` (`row_end`), + KEY `row_end_3` (`row_end` DESC), 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 @@ -306,7 +307,7 @@ t1 CREATE TABLE `t1` ( KEY `row_start` (`row_start`), KEY `row_end_2` (`row_end`,`x`), KEY `x` (`x`,`row_end`), - KEY `row_end_3` (`row_end`), + KEY `row_end_3` (`row_end` DESC), 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 @@ -326,7 +327,7 @@ t1 CREATE TABLE `t1` ( KEY `row_start` (`row_start`), KEY `row_end_2` (`row_end`,`x`), KEY `x` (`x`,`row_end`), - KEY `row_end_3` (`row_end`), + KEY `row_end_3` (`row_end` DESC), 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 @@ -345,7 +346,7 @@ t1 CREATE TABLE `t1` ( KEY `row_start` (`row_start`), KEY `row_end_2` (`row_end`,`x`), KEY `x` (`x`,`row_end`), - KEY `row_end_3` (`row_end`), + KEY `row_end_3` (`row_end` DESC), 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 @@ -364,7 +365,7 @@ t1 CREATE TABLE `t1` ( KEY `row_start` (`row_start`), KEY `row_end_2` (`row_end`,`x`), KEY `x` (`x`,`row_end`), - KEY `row_end_3` (`row_end`), + KEY `row_end_3` (`row_end` DESC), PERIOD FOR SYSTEM_TIME (`row_start`, `row_end`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci WITH SYSTEM VERSIONING PARTITION BY HASH (`x`) @@ -385,11 +386,104 @@ t1 CREATE TABLE `t1` ( KEY `row_start` (`row_start`), KEY `row_end_2` (`row_end`,`x`), KEY `x` (`x`,`row_end`), - KEY `row_end_3` (`row_end`), + KEY `row_end_3` (`row_end` DESC), 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 +# 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; +alter table t1 partition by system_time interval 1 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` DESC), + 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 MONTH STARTS TIMESTAMP'2000-01-01 00:00:00' AUTO +PARTITIONS 2 +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` DESC), + 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; +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` DESC), + 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` DESC), + 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` DESC), + 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..cdfab219c1b22 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 # @@ -25,7 +27,7 @@ create or replace table t1 (x int, index (row_start), index (row_end, x), index (x, row_end), - index (row_end)) with system versioning + index (row_end desc)) with system versioning partition by system_time limit 1000 partitions 6; show create table t1; @@ -155,6 +157,31 @@ 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; +alter table t1 partition by system_time interval 1 month auto force; +show create table 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 8420de1186f52..18edec87f98c9 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); @@ -6196,15 +6199,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 @@ -6247,6 +6244,74 @@ 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); + + if (end == start) + { + *hist_parts= 1; + return false; + } + + while (start < end) + { + 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 (error) + { + my_error(ER_DATA_OUT_OF_RANGE, MYF(0), "TIMESTAMP", "INTERVAL"); + return true; + } + + (*hist_parts)++; + if (*hist_parts >= MAX_PARTITIONS) + { + my_error(ER_TOO_MANY_PARTITIONS_ERROR, MYF(0)); + return true; + } + } + + return false; +} + + /* Change partitions, used to implement ALTER TABLE ADD/REORGANIZE/COALESCE partitions. This method is used to implement both single-phase and multi-