From 6718d3bc3241f72e07504133371cf3813d2e6fe9 Mon Sep 17 00:00:00 2001 From: Vlad Lesin Date: Fri, 25 May 2018 22:16:04 +0400 Subject: [PATCH 1/9] MDEV-21082: isnan/isinf compilation errors, isfinite warnings on MacOS The fix consists of three commits backported from 10.3: 1) Cleanup isnan() portability checks (cherry picked from commit 7ffd7fe9627d1f750a3712aebb4503e5ae8aea8e) 2) Cleanup isinf() portability checks Original problem reported by Wlad: re-compilation of 10.3 on top of 10.2 build would cache undefined HAVE_ISINF from 10.2, whereas it is expected to be 1 in 10.3. std::isinf() seem to be available on all supported platforms. (cherry picked from commit bc469a0bdf85400f7a63834f5b7af1a513dcdec9) 3) Use std::isfinite in C++ code This is addition to parent revision fixing build failures. (cherry picked from commit 54999f4e75f42baca484ae436b382ca8817df1dd) --- cmake/os/Windows.cmake | 1 - config.h.cmake | 2 -- configure.cmake | 11 ----------- include/my_global.h | 20 +------------------- sql/field.cc | 6 +++--- sql/item_func.cc | 6 +++--- sql/item_func.h | 2 +- sql/item_strfunc.cc | 2 +- sql/item_sum.cc | 4 ++-- storage/heap/hp_hash.c | 2 -- storage/innobase/gis/gis0geo.cc | 2 +- storage/innobase/gis/gis0rtree.cc | 2 +- storage/maria/ma_key.c | 2 -- storage/maria/ma_sp_key.c | 2 -- storage/myisam/mi_key.c | 2 -- storage/myisam/sp_key.c | 2 -- 16 files changed, 13 insertions(+), 55 deletions(-) diff --git a/cmake/os/Windows.cmake b/cmake/os/Windows.cmake index ca6d13e1c1b..1eae92924a6 100644 --- a/cmake/os/Windows.cmake +++ b/cmake/os/Windows.cmake @@ -225,7 +225,6 @@ CHECK_SYMBOL_REPLACEMENT(S_IROTH _S_IREAD sys/stat.h) CHECK_SYMBOL_REPLACEMENT(S_IFIFO _S_IFIFO sys/stat.h) CHECK_SYMBOL_REPLACEMENT(SIGQUIT SIGTERM signal.h) CHECK_SYMBOL_REPLACEMENT(SIGPIPE SIGINT signal.h) -CHECK_SYMBOL_REPLACEMENT(isnan _isnan "math.h;float.h") CHECK_SYMBOL_REPLACEMENT(finite _finite "math;float.h") CHECK_FUNCTION_REPLACEMENT(popen _popen) CHECK_FUNCTION_REPLACEMENT(pclose _pclose) diff --git a/config.h.cmake b/config.h.cmake index ae1431c8abe..6e4af65c40e 100644 --- a/config.h.cmake +++ b/config.h.cmake @@ -163,7 +163,6 @@ #cmakedefine HAVE_IN_ADDR_T 1 #cmakedefine HAVE_INITGROUPS 1 #cmakedefine HAVE_ISNAN 1 -#cmakedefine HAVE_ISINF 1 #cmakedefine HAVE_LARGE_PAGE_OPTION 1 #cmakedefine HAVE_LDIV 1 #cmakedefine HAVE_LRAND48 1 @@ -423,7 +422,6 @@ #cmakedefine mode_t @mode_t@ #cmakedefine SIGQUIT @SIGQUIT@ #cmakedefine SIGPIPE @SIGPIPE@ -#cmakedefine isnan @isnan@ #cmakedefine finite @finite@ #cmakedefine popen @popen@ #cmakedefine pclose @pclose@ diff --git a/configure.cmake b/configure.cmake index 8677aec44f1..f94fb3642a5 100644 --- a/configure.cmake +++ b/configure.cmake @@ -475,19 +475,8 @@ ELSE() CHECK_SYMBOL_EXISTS(finite "ieeefp.h" HAVE_FINITE) ENDIF() CHECK_SYMBOL_EXISTS(log2 math.h HAVE_LOG2) -CHECK_SYMBOL_EXISTS(isnan math.h HAVE_ISNAN) CHECK_SYMBOL_EXISTS(rint math.h HAVE_RINT) -# isinf() prototype not found on Solaris -CHECK_CXX_SOURCE_COMPILES( -"#include -int main() { - isinf(0.0); - return 0; -}" HAVE_ISINF) - - - # # Test for endianess # diff --git a/include/my_global.h b/include/my_global.h index 4ce4671c571..8103f82a7e2 100644 --- a/include/my_global.h +++ b/include/my_global.h @@ -811,26 +811,8 @@ inline unsigned long long my_double2ulonglong(double d) #define SIZE_T_MAX (~((size_t) 0)) #endif -#ifndef isfinite -#ifdef HAVE_FINITE -#define isfinite(x) finite(x) -#else -#define finite(x) (1.0 / fabs(x) > 0.0) -#endif /* HAVE_FINITE */ -#elif (__cplusplus >= 201103L) +#ifdef __cplusplus #include -static inline bool isfinite(double x) { return std::isfinite(x); } -#endif /* isfinite */ - -#ifndef HAVE_ISNAN -#define isnan(x) ((x) != (x)) -#endif -#define my_isnan(x) isnan(x) - -#ifdef HAVE_ISINF -#define my_isinf(X) isinf(X) -#else /* !HAVE_ISINF */ -#define my_isinf(X) (!finite(X) && !isnan(X)) #endif /* Define missing math constants. */ diff --git a/sql/field.cc b/sql/field.cc index a23004ebd96..09e82acb009 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -2904,7 +2904,7 @@ int Field_decimal::store(double nr) return 1; } - if (!isfinite(nr)) // Handle infinity as special case + if (!std::isfinite(nr)) // Handle infinity as special case { overflow(nr < 0.0); return 1; @@ -4821,7 +4821,7 @@ int truncate_double(double *nr, uint field_length, uint dec, int error= 0; double res= *nr; - if (isnan(res)) + if (std::isnan(res)) { *nr= 0; return -1; @@ -4843,7 +4843,7 @@ int truncate_double(double *nr, uint field_length, uint dec, max_value-= 1.0 / log_10[dec]; /* Check for infinity so we don't get NaN in calculations */ - if (!my_isinf(res)) + if (!std::isinf(res)) { double tmp= rint((res - floor(res)) * log_10[dec]) / log_10[dec]; res= floor(res) + tmp; diff --git a/sql/item_func.cc b/sql/item_func.cc index 7719e0faa87..6ca74a79b9c 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -2553,12 +2553,12 @@ double my_double_round(double value, longlong dec, bool dec_unsigned, volatile double value_div_tmp= value / tmp; volatile double value_mul_tmp= value * tmp; - if (!dec_negative && my_isinf(tmp)) // "dec" is too large positive number + if (!dec_negative && std::isinf(tmp)) // "dec" is too large positive number return value; - if (dec_negative && my_isinf(tmp)) + if (dec_negative && std::isinf(tmp)) tmp2= 0.0; - else if (!dec_negative && my_isinf(value_mul_tmp)) + else if (!dec_negative && std::isinf(value_mul_tmp)) tmp2= value; else if (truncate) { diff --git a/sql/item_func.h b/sql/item_func.h index 1193daea106..496109b0e24 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -241,7 +241,7 @@ public: */ inline double check_float_overflow(double value) { - return isfinite(value) ? value : raise_float_overflow(); + return std::isfinite(value) ? value : raise_float_overflow(); } /** Throw an error if the input BIGINT value represented by the diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc index dcfbd272809..8738af7ac56 100644 --- a/sql/item_strfunc.cc +++ b/sql/item_strfunc.cc @@ -2655,7 +2655,7 @@ String *Item_func_format::val_str_ascii(String *str) return 0; /* purecov: inspected */ nr= my_double_round(nr, (longlong) dec, FALSE, FALSE); str->set_real(nr, dec, &my_charset_numeric); - if (!isfinite(nr)) + if (!std::isfinite(nr)) return str; str_length=str->length(); } diff --git a/sql/item_sum.cc b/sql/item_sum.cc index 5bed0d32009..06c01c58948 100644 --- a/sql/item_sum.cc +++ b/sql/item_sum.cc @@ -1808,7 +1808,7 @@ double Item_sum_std::val_real() { DBUG_ASSERT(fixed == 1); double nr= Item_sum_variance::val_real(); - if (isnan(nr)) + if (std::isnan(nr)) { /* variance_fp_recurrence_next() can overflow in some cases and return "nan": @@ -1820,7 +1820,7 @@ double Item_sum_std::val_real() null_value= true; // Convert "nan" to NULL return 0; } - if (my_isinf(nr)) + if (std::isinf(nr)) return DBL_MAX; DBUG_ASSERT(nr >= 0.0); return sqrt(nr); diff --git a/storage/heap/hp_hash.c b/storage/heap/hp_hash.c index 848a9881b20..f0238d06010 100644 --- a/storage/heap/hp_hash.c +++ b/storage/heap/hp_hash.c @@ -778,7 +778,6 @@ uint hp_rb_make_key(HP_KEYDEF *keydef, uchar *key, uchar *pos= (uchar*) rec + seg->start; DBUG_ASSERT(seg->type != HA_KEYTYPE_BIT); -#ifdef HAVE_ISNAN if (seg->type == HA_KEYTYPE_FLOAT) { float nr; @@ -802,7 +801,6 @@ uint hp_rb_make_key(HP_KEYDEF *keydef, uchar *key, continue; } } -#endif pos+= length; while (length--) { diff --git a/storage/innobase/gis/gis0geo.cc b/storage/innobase/gis/gis0geo.cc index bd601c2e19e..cad3877d3e9 100644 --- a/storage/innobase/gis/gis0geo.cc +++ b/storage/innobase/gis/gis0geo.cc @@ -367,7 +367,7 @@ mbr_join_square( /* Check if finite (not infinity or NaN), so we don't get NaN in calculations */ - if (!isfinite(square)) { + if (!std::isfinite(square)) { return DBL_MAX; } diff --git a/storage/innobase/gis/gis0rtree.cc b/storage/innobase/gis/gis0rtree.cc index a9c30ae2a38..ce1749820c3 100644 --- a/storage/innobase/gis/gis0rtree.cc +++ b/storage/innobase/gis/gis0rtree.cc @@ -1969,7 +1969,7 @@ rtr_estimate_n_rows_in_range( mtr_commit(&mtr); mem_heap_free(heap); - if (!isfinite(area)) { + if (!std::isfinite(area)) { return(HA_POS_ERROR); } diff --git a/storage/maria/ma_key.c b/storage/maria/ma_key.c index c9db47dd384..9e804a1e9dc 100644 --- a/storage/maria/ma_key.c +++ b/storage/maria/ma_key.c @@ -279,7 +279,6 @@ MARIA_KEY *_ma_make_key(MARIA_HA *info, MARIA_KEY *int_key, uint keynr, } else if (keyseg->flag & HA_SWAP_KEY) { /* Numerical column */ -#ifdef HAVE_ISNAN if (type == HA_KEYTYPE_FLOAT) { float nr; @@ -303,7 +302,6 @@ MARIA_KEY *_ma_make_key(MARIA_HA *info, MARIA_KEY *int_key, uint keynr, continue; } } -#endif pos+=length; while (length--) { diff --git a/storage/maria/ma_sp_key.c b/storage/maria/ma_sp_key.c index 0dc7fe1fe46..1a9abc989ed 100644 --- a/storage/maria/ma_sp_key.c +++ b/storage/maria/ma_sp_key.c @@ -77,7 +77,6 @@ MARIA_KEY *_ma_sp_make_key(MARIA_HA *info, MARIA_KEY *ret_key, uint keynr, DBUG_ASSERT(keyseg->type == HA_KEYTYPE_DOUBLE); val= mbr[start / sizeof (double)]; -#ifdef HAVE_ISNAN if (isnan(val)) { bzero(key, length); @@ -85,7 +84,6 @@ MARIA_KEY *_ma_sp_make_key(MARIA_HA *info, MARIA_KEY *ret_key, uint keynr, len+= length; continue; } -#endif if (keyseg->flag & HA_SWAP_KEY) { diff --git a/storage/myisam/mi_key.c b/storage/myisam/mi_key.c index c81bc674685..9247fae9e3c 100644 --- a/storage/myisam/mi_key.c +++ b/storage/myisam/mi_key.c @@ -150,7 +150,6 @@ uint _mi_make_key(register MI_INFO *info, uint keynr, uchar *key, } else if (keyseg->flag & HA_SWAP_KEY) { /* Numerical column */ -#ifdef HAVE_ISNAN if (type == HA_KEYTYPE_FLOAT) { float nr; @@ -174,7 +173,6 @@ uint _mi_make_key(register MI_INFO *info, uint keynr, uchar *key, continue; } } -#endif pos+=length; while (length--) { diff --git a/storage/myisam/sp_key.c b/storage/myisam/sp_key.c index c3aeb7553f2..4c6ef75934e 100644 --- a/storage/myisam/sp_key.c +++ b/storage/myisam/sp_key.c @@ -66,7 +66,6 @@ uint sp_make_key(register MI_INFO *info, uint keynr, uchar *key, DBUG_ASSERT(keyseg->type == HA_KEYTYPE_DOUBLE); val= mbr[start / sizeof (double)]; -#ifdef HAVE_ISNAN if (isnan(val)) { bzero(key, length); @@ -74,7 +73,6 @@ uint sp_make_key(register MI_INFO *info, uint keynr, uchar *key, len+= length; continue; } -#endif if (keyseg->flag & HA_SWAP_KEY) { From a14544260c33dcdb057d2f62c4aab33cb09ebcb1 Mon Sep 17 00:00:00 2001 From: Eugene Kosov Date: Fri, 15 Nov 2019 21:49:04 +0700 Subject: [PATCH 2/9] MDEV-21045 AddressSanitizer: use-after-poison in mem_heap_dup / row_log_table_get_pk_col row_log_table_get_pk_col(): read instant field value from instant alter table when it's required. --- .../suite/innodb/r/instant_alter_debug.result | 18 +++++++++++++ .../suite/innodb/t/instant_alter_debug.test | 26 +++++++++++++++++++ storage/innobase/row/row0log.cc | 4 +++ 3 files changed, 48 insertions(+) diff --git a/mysql-test/suite/innodb/r/instant_alter_debug.result b/mysql-test/suite/innodb/r/instant_alter_debug.result index 62df17e066f..49365b6e9be 100644 --- a/mysql-test/suite/innodb/r/instant_alter_debug.result +++ b/mysql-test/suite/innodb/r/instant_alter_debug.result @@ -264,3 +264,21 @@ a b vb 5 NULL NULL DROP TABLE t1; SET GLOBAL innodb_purge_rseg_truncate_frequency = @save_frequency; +# +# MDEV-21045 AddressSanitizer: use-after-poison in mem_heap_dup / row_log_table_get_pk_col +# +CREATE TABLE t1 (a TEXT) ENGINE = InnoDB ROW_FORMAT=REDUNDANT; +INSERT INTO t1 (a) VALUES ('foo'); +ALTER TABLE t1 ADD COLUMN b INT DEFAULT 0,algorithm=instant; +connect con2,localhost,root,,test; +SET DEBUG_SYNC='innodb_inplace_alter_table_enter SIGNAL onlinealter WAIT_FOR update'; +ALTER TABLE t1 ADD PRIMARY KEY (b); +connection default; +SET DEBUG_SYNC='now WAIT_FOR onlinealter'; +UPDATE t1 SET b = 1; +SET DEBUG_SYNC='now SIGNAL update'; +connection con2; +connection default; +SET DEBUG_SYNC='RESET'; +disconnect con2; +DROP TABLE t1; diff --git a/mysql-test/suite/innodb/t/instant_alter_debug.test b/mysql-test/suite/innodb/t/instant_alter_debug.test index 5b8624a3186..cec7a05725b 100644 --- a/mysql-test/suite/innodb/t/instant_alter_debug.test +++ b/mysql-test/suite/innodb/t/instant_alter_debug.test @@ -293,3 +293,29 @@ SELECT * FROM t1; DROP TABLE t1; SET GLOBAL innodb_purge_rseg_truncate_frequency = @save_frequency; + +--echo # +--echo # MDEV-21045 AddressSanitizer: use-after-poison in mem_heap_dup / row_log_table_get_pk_col +--echo # +CREATE TABLE t1 (a TEXT) ENGINE = InnoDB ROW_FORMAT=REDUNDANT; +INSERT INTO t1 (a) VALUES ('foo'); + +ALTER TABLE t1 ADD COLUMN b INT DEFAULT 0,algorithm=instant; + +--connect (con2,localhost,root,,test) +SET DEBUG_SYNC='innodb_inplace_alter_table_enter SIGNAL onlinealter WAIT_FOR update'; +--send +ALTER TABLE t1 ADD PRIMARY KEY (b); + +--connection default +SET DEBUG_SYNC='now WAIT_FOR onlinealter'; +UPDATE t1 SET b = 1; +SET DEBUG_SYNC='now SIGNAL update'; + +--connection con2 +--reap + +--connection default +SET DEBUG_SYNC='RESET'; +--disconnect con2 +DROP TABLE t1; diff --git a/storage/innobase/row/row0log.cc b/storage/innobase/row/row0log.cc index 9b09b61e119..e2f6450e38d 100644 --- a/storage/innobase/row/row0log.cc +++ b/storage/innobase/row/row0log.cc @@ -1164,6 +1164,10 @@ row_log_table_get_pk_col( field = rec_get_nth_field(rec, offsets, i, &len); + if (len == UNIV_SQL_DEFAULT) { + field = log->instant_field_value(i, &len); + } + if (len == UNIV_SQL_NULL) { if (!log->allow_not_null) { return(DB_INVALID_NULL); From cb6d7c3ee3e3e90ed46d3d1d58be6bac12e5f784 Mon Sep 17 00:00:00 2001 From: Seth Shelnutt Date: Mon, 4 Nov 2019 16:17:59 -0500 Subject: [PATCH 3/9] MDEV-20972: `or` alterative operator breaking windows build Closes #1405 --- sql/item_subselect.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index 86c607fb894..79dcf8ecfe8 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -123,7 +123,7 @@ void Item_subselect::init(st_select_lex *select_lex, NO_MATTER : outer_select->parsing_place); if (unit->is_unit_op() && - (unit->first_select()->next_select() or unit->fake_select_lex)) + (unit->first_select()->next_select() || unit->fake_select_lex)) engine= new subselect_union_engine(unit, result, this); else engine= new subselect_single_select_engine(select_lex, result, this); From 0076dce2c89248b6c0252ec4385879194f9aadbf Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Fri, 22 Nov 2019 14:29:03 +0300 Subject: [PATCH 4/9] MDEV-18727 improve DML operation of System Versioning MDEV-18957 UPDATE with LIMIT clause is wrong for versioned partitioned tables UPDATE, DELETE: replace linear search of current/historical records with vers_setup_conds(). Additional DML cases in view.test --- mysql-test/suite/versioning/r/delete.result | 6 +- ...{truncate.result => delete_history.result} | 0 .../suite/versioning/r/partition.result | 17 ++++ mysql-test/suite/versioning/r/view.result | 78 +++++++++++++++++-- mysql-test/suite/versioning/t/delete.test | 10 +-- .../t/{truncate.test => delete_history.test} | 0 mysql-test/suite/versioning/t/partition.test | 14 ++++ mysql-test/suite/versioning/t/view.test | 47 +++++++++-- sql/ha_partition.cc | 2 +- sql/mysqld.h | 3 +- sql/sql_delete.cc | 65 ++++++---------- sql/sql_derived.cc | 10 +++ sql/sql_parse.cc | 7 +- sql/sql_select.cc | 47 +++++++++-- sql/sql_union.cc | 16 +++- sql/sql_update.cc | 28 ++++--- sql/sql_yacc.yy | 2 +- sql/table.cc | 2 + sql/table.h | 4 + 19 files changed, 271 insertions(+), 87 deletions(-) rename mysql-test/suite/versioning/r/{truncate.result => delete_history.result} (100%) rename mysql-test/suite/versioning/t/{truncate.test => delete_history.test} (100%) diff --git a/mysql-test/suite/versioning/r/delete.result b/mysql-test/suite/versioning/r/delete.result index 77b7fc80286..26ade83acd7 100644 --- a/mysql-test/suite/versioning/r/delete.result +++ b/mysql-test/suite/versioning/r/delete.result @@ -1,3 +1,4 @@ +# Basic + delete from view create or replace table t1( XNo int unsigned, sys_start SYS_DATATYPE as row start invisible, @@ -44,6 +45,7 @@ XNo_vt1 5 drop view vt1; drop table t1; +# Check sys_start, sys_end create or replace table t1( x int, sys_start SYS_DATATYPE as row start invisible, @@ -59,6 +61,7 @@ select x = 1 as A, sys_start = @sys_start as B, sys_end > sys_start as C from t1 A B C 1 1 1 drop table t1; +# Multi-delete create or replace table t1( x int, y int, @@ -103,9 +106,6 @@ t2_x_all 14 drop table t1; drop table t2; -# Basic + delete from view -# Check sys_start, sys_end -# Multi-delete # Update + delete create or replace table t1 (x int) with system versioning; insert into t1 values (1); diff --git a/mysql-test/suite/versioning/r/truncate.result b/mysql-test/suite/versioning/r/delete_history.result similarity index 100% rename from mysql-test/suite/versioning/r/truncate.result rename to mysql-test/suite/versioning/r/delete_history.result diff --git a/mysql-test/suite/versioning/r/partition.result b/mysql-test/suite/versioning/r/partition.result index 2163ebeb1a0..315413fbd7d 100644 --- a/mysql-test/suite/versioning/r/partition.result +++ b/mysql-test/suite/versioning/r/partition.result @@ -566,3 +566,20 @@ execute immediate 'select * from t1 for update'; pk drop view v1; drop tables t, t1, t2, t3, t4; +# +# MDEV-18957 UPDATE with LIMIT clause is wrong for versioned partitioned tables +# +create or replace table t1 ( +x int, +a varchar(255) +) with system versioning partition by system_time (partition p1 history, partition pn current); +insert into t1 (x) values (1), (2), (3), (4); +update t1 set a= 'foo' limit 3; +update t1 set a= 'bar' limit 4; +select * from t1; +x a +1 bar +2 bar +3 bar +4 bar +drop table t1; diff --git a/mysql-test/suite/versioning/r/view.result b/mysql-test/suite/versioning/r/view.result index 850eba32c0d..8b23e87d6a4 100644 --- a/mysql-test/suite/versioning/r/view.result +++ b/mysql-test/suite/versioning/r/view.result @@ -64,13 +64,13 @@ select * from vt1; x 1 2 -# VIEW with parameters [#151] +# VIEW with parameters [tempesta-tech/mariadb#151] create or replace table t1 (x int) with system versioning; create or replace view vt1(c) as select x from t1; show create view vt1; View Create View character_set_client collation_connection vt1 CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `vt1` AS select `t1`.`x` AS `c` from `t1` latin1 latin1_swedish_ci -# VIEW over JOIN of versioned tables [#153] +# VIEW over JOIN of versioned tables [tempesta-tech/mariadb#153] create or replace table t1 (a int) with system versioning; create or replace table t2 (b int) with system versioning; insert into t1 values (1); @@ -82,7 +82,7 @@ a b create or replace view vt12 as select * from t1 for system_time as of timestamp ('0-0-0') cross join t2; select * from vt12; a b -# VIEW improvements [#183] +# VIEW improvements [tempesta-tech/mariadb#183] create or replace table t3 (x int); create or replace view vt1 as select * from t1, t2, t3; show create view vt1; @@ -96,12 +96,12 @@ create or replace view vt1 as select a, t2.row_end as endo from t3, t1, t2; show create view vt1; View Create View character_set_client collation_connection vt1 CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `vt1` AS select `t1`.`a` AS `a`,`t2`.`row_end` AS `endo` from ((`t3` join `t1`) join `t2`) latin1 latin1_swedish_ci -# VIEW over UNION [#269] +# VIEW over UNION [tempesta-tech/mariadb#269] create or replace view vt1 as select * from t1 union select * from t1; select * from vt1; a 1 -# VIEW over UNION with non-versioned [#393] +# VIEW over UNION with non-versioned [tempesta-tech/mariadb#393] create or replace table t2 (a int); create or replace view vt1 as select * from t1 union select * from t2; select * from vt1; @@ -123,10 +123,10 @@ drop tables t1, t2; # # MDEV-15146 SQLError[4122]: View is not system versioned # -create table t1 (a int) with system versioning; +create or replace table t1 (a int) with system versioning; insert t1 values (1),(2); set @a=now(6); -create view v1 as select * from t1; +create or replace view v1 as select * from t1; delete from t1; select * from v1; a @@ -149,3 +149,67 @@ View Create View character_set_client collation_connection v1 CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v1` AS select `t1`.`i` AS `i` from `t1` FOR SYSTEM_TIME AS OF current_timestamp() - interval 6 second latin1 latin1_swedish_ci drop view v1, vt1, vt12; drop tables t1, t3; +# +# MDEV-18727 improve DML operation of System Versioning +# +create or replace table t1 ( +x int, +row_start SYS_DATATYPE as row start invisible, +row_end SYS_DATATYPE as row end invisible, +period for system_time (row_start, row_end) +) with system versioning; +insert into t1 values (1), (2); +create or replace view v1 as select * from t1 where x > 1; +update v1 set x= x + 1; +select *, check_row(row_start, row_end) from t1 for system_time all order by x; +x check_row(row_start, row_end) +1 CURRENT ROW +2 HISTORICAL ROW +3 CURRENT ROW +insert v1 values (4); +select *, check_row(row_start, row_end) from t1 for system_time all order by x; +x check_row(row_start, row_end) +1 CURRENT ROW +2 HISTORICAL ROW +3 CURRENT ROW +4 CURRENT ROW +delete from v1 where x < 4; +select *, check_row(row_start, row_end) from t1 for system_time all order by x; +x check_row(row_start, row_end) +1 CURRENT ROW +2 HISTORICAL ROW +3 HISTORICAL ROW +4 CURRENT ROW +# multi-update +create or replace table t2 like t1; +insert into t2 values (1), (2); +create or replace view v2 as select * from t2 where x > 1; +update v1, v2 set v1.x= v1.x + 1, v2.x= v2.x + 1 where v1.x = v2.x + 2; +select *, check_row(row_start, row_end) from t1 for system_time all order by x; +x check_row(row_start, row_end) +1 CURRENT ROW +2 HISTORICAL ROW +3 HISTORICAL ROW +4 HISTORICAL ROW +5 CURRENT ROW +select *, check_row(row_start, row_end) from t2 for system_time all order by x; +x check_row(row_start, row_end) +1 CURRENT ROW +2 HISTORICAL ROW +3 CURRENT ROW +# multi-delete +delete v1, v2 from v1 join v2 where v1.x = v2.x + 2; +select *, check_row(row_start, row_end) from t1 for system_time all order by x; +x check_row(row_start, row_end) +1 CURRENT ROW +2 HISTORICAL ROW +3 HISTORICAL ROW +4 HISTORICAL ROW +5 HISTORICAL ROW +select *, check_row(row_start, row_end) from t2 for system_time all order by x; +x check_row(row_start, row_end) +1 CURRENT ROW +2 HISTORICAL ROW +3 HISTORICAL ROW +drop view v1, v2; +drop tables t1, t2; diff --git a/mysql-test/suite/versioning/t/delete.test b/mysql-test/suite/versioning/t/delete.test index b9045898bb0..4f1ba4b1d8e 100644 --- a/mysql-test/suite/versioning/t/delete.test +++ b/mysql-test/suite/versioning/t/delete.test @@ -1,6 +1,7 @@ source suite/versioning/engines.inc; source suite/versioning/common.inc; +--echo # Basic + delete from view replace_result $sys_datatype_expl SYS_DATATYPE; eval create or replace table t1( XNo int unsigned, @@ -31,7 +32,7 @@ select XNo as XNo_vt1 from vt1; drop view vt1; drop table t1; - +--echo # Check sys_start, sys_end replace_result $sys_datatype_expl SYS_DATATYPE; eval create or replace table t1( x int, @@ -47,6 +48,7 @@ select * from t1; select x = 1 as A, sys_start = @sys_start as B, sys_end > sys_start as C from t1 for system_time all; drop table t1; +--echo # Multi-delete replace_result $sys_datatype_expl SYS_DATATYPE; eval create or replace table t1( x int, @@ -69,12 +71,6 @@ select x as t2_x_all from t2 for system_time all; drop table t1; drop table t2; ---echo # Basic + delete from view - ---echo # Check sys_start, sys_end - ---echo # Multi-delete - --echo # Update + delete create or replace table t1 (x int) with system versioning; insert into t1 values (1); diff --git a/mysql-test/suite/versioning/t/truncate.test b/mysql-test/suite/versioning/t/delete_history.test similarity index 100% rename from mysql-test/suite/versioning/t/truncate.test rename to mysql-test/suite/versioning/t/delete_history.test diff --git a/mysql-test/suite/versioning/t/partition.test b/mysql-test/suite/versioning/t/partition.test index eca322d9ef4..ce8c2e5ec1a 100644 --- a/mysql-test/suite/versioning/t/partition.test +++ b/mysql-test/suite/versioning/t/partition.test @@ -517,4 +517,18 @@ execute immediate 'select * from t1 for update'; drop view v1; drop tables t, t1, t2, t3, t4; +--echo # +--echo # MDEV-18957 UPDATE with LIMIT clause is wrong for versioned partitioned tables +--echo # +create or replace table t1 ( + x int, + a varchar(255) +) with system versioning partition by system_time (partition p1 history, partition pn current); + +insert into t1 (x) values (1), (2), (3), (4); +update t1 set a= 'foo' limit 3; +update t1 set a= 'bar' limit 4; +select * from t1; +drop table t1; + --source suite/versioning/common_finish.inc diff --git a/mysql-test/suite/versioning/t/view.test b/mysql-test/suite/versioning/t/view.test index 5a03a50f1d3..c05fbfd3866 100644 --- a/mysql-test/suite/versioning/t/view.test +++ b/mysql-test/suite/versioning/t/view.test @@ -52,13 +52,13 @@ prepare stmt from @tmp; execute stmt; drop prepare stmt; select * from vt1; ---echo # VIEW with parameters [#151] +--echo # VIEW with parameters [tempesta-tech/mariadb#151] create or replace table t1 (x int) with system versioning; create or replace view vt1(c) as select x from t1; --replace_result 18446744073709551615 MAX_RESULT "TIMESTAMP'2038-01-19 03:14:07.999999'" MAX_RESULT show create view vt1; ---echo # VIEW over JOIN of versioned tables [#153] +--echo # VIEW over JOIN of versioned tables [tempesta-tech/mariadb#153] create or replace table t1 (a int) with system versioning; create or replace table t2 (b int) with system versioning; insert into t1 values (1); @@ -68,7 +68,7 @@ select * from vt12; create or replace view vt12 as select * from t1 for system_time as of timestamp ('0-0-0') cross join t2; select * from vt12; ---echo # VIEW improvements [#183] +--echo # VIEW improvements [tempesta-tech/mariadb#183] create or replace table t3 (x int); create or replace view vt1 as select * from t1, t2, t3; --replace_result 18446744073709551615 MAX_RESULT "TIMESTAMP'2038-01-19 03:14:07.999999'" MAX_RESULT @@ -80,11 +80,11 @@ create or replace view vt1 as select a, t2.row_end as endo from t3, t1, t2; --replace_result 18446744073709551615 MAX_RESULT "TIMESTAMP'2038-01-19 03:14:07.999999'" MAX_RESULT show create view vt1; ---echo # VIEW over UNION [#269] +--echo # VIEW over UNION [tempesta-tech/mariadb#269] create or replace view vt1 as select * from t1 union select * from t1; select * from vt1; ---echo # VIEW over UNION with non-versioned [#393] +--echo # VIEW over UNION with non-versioned [tempesta-tech/mariadb#393] create or replace table t2 (a int); create or replace view vt1 as select * from t1 union select * from t2; select * from vt1; @@ -104,10 +104,10 @@ drop tables t1, t2; --echo # --echo # MDEV-15146 SQLError[4122]: View is not system versioned --echo # -create table t1 (a int) with system versioning; +create or replace table t1 (a int) with system versioning; insert t1 values (1),(2); set @a=now(6); -create view v1 as select * from t1; +create or replace view v1 as select * from t1; delete from t1; select * from v1; select * from v1 for system_time as of @a; @@ -124,4 +124,37 @@ show create view v1; drop view v1, vt1, vt12; drop tables t1, t3; +--echo # +--echo # MDEV-18727 improve DML operation of System Versioning +--echo # +--replace_result $sys_datatype_expl SYS_DATATYPE +eval create or replace table t1 ( + x int, + row_start $sys_datatype_expl as row start invisible, + row_end $sys_datatype_expl as row end invisible, + period for system_time (row_start, row_end) +) with system versioning; +insert into t1 values (1), (2); +create or replace view v1 as select * from t1 where x > 1; +update v1 set x= x + 1; +select *, check_row(row_start, row_end) from t1 for system_time all order by x; +insert v1 values (4); +select *, check_row(row_start, row_end) from t1 for system_time all order by x; +delete from v1 where x < 4; +select *, check_row(row_start, row_end) from t1 for system_time all order by x; +--echo # multi-update +create or replace table t2 like t1; +insert into t2 values (1), (2); +create or replace view v2 as select * from t2 where x > 1; +update v1, v2 set v1.x= v1.x + 1, v2.x= v2.x + 1 where v1.x = v2.x + 2; +select *, check_row(row_start, row_end) from t1 for system_time all order by x; +select *, check_row(row_start, row_end) from t2 for system_time all order by x; +--echo # multi-delete +delete v1, v2 from v1 join v2 where v1.x = v2.x + 2; +select *, check_row(row_start, row_end) from t1 for system_time all order by x; +select *, check_row(row_start, row_end) from t2 for system_time all order by x; + +drop view v1, v2; +drop tables t1, t2; + --source suite/versioning/common_finish.inc diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc index 232111d5a98..380fc48e915 100644 --- a/sql/ha_partition.cc +++ b/sql/ha_partition.cc @@ -4538,7 +4538,7 @@ int ha_partition::delete_row(const uchar *buf) or last historical partition, but DELETE HISTORY can delete from any historical partition. So, skip the check in this case. */ - if (!thd->lex->vers_conditions.is_set()) // if not DELETE HISTORY + if (!thd->lex->vers_conditions.delete_history) { uint32 part_id; error= get_part_for_buf(buf, m_rec0, m_part_info, &part_id); diff --git a/sql/mysqld.h b/sql/mysqld.h index 11871155355..dc0641502ce 100644 --- a/sql/mysqld.h +++ b/sql/mysqld.h @@ -189,7 +189,8 @@ enum vers_system_time_t SYSTEM_TIME_AS_OF, SYSTEM_TIME_FROM_TO, SYSTEM_TIME_BETWEEN, - SYSTEM_TIME_BEFORE, + SYSTEM_TIME_BEFORE, // used for DELETE HISTORY ... BEFORE + SYSTEM_TIME_HISTORY, // used for DELETE HISTORY SYSTEM_TIME_ALL }; diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index e3d17174b8b..4245c843968 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -225,19 +225,11 @@ bool Update_plan::save_explain_data_intern(MEM_ROOT *mem_root, static bool record_should_be_deleted(THD *thd, TABLE *table, SQL_SELECT *sel, Explain_delete *explain, bool truncate_history) { - bool check_delete= true; - - if (table->versioned()) - { - bool historical= !table->vers_end_field()->is_max(); - check_delete= truncate_history ? historical : !historical; - } - explain->tracker.on_record_read(); thd->inc_examined_row_count(1); if (table->vfield) (void) table->update_virtual_fields(table->file, VCOL_UPDATE_FOR_DELETE); - if (check_delete && (!sel || sel->skip_record(thd) > 0)) + if (!sel || sel->skip_record(thd) > 0) { explain->tracker.on_record_after_where(); return true; @@ -305,29 +297,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, THD_STAGE_INFO(thd, stage_init_update); - bool delete_history= table_list->vers_conditions.is_set(); - if (delete_history) - { - if (table_list->is_view_or_derived()) - { - my_error(ER_IT_IS_A_VIEW, MYF(0), table_list->table_name.str); - DBUG_RETURN(true); - } - - DBUG_ASSERT(table_list->table); - - DBUG_ASSERT(!conds || thd->stmt_arena->is_stmt_execute()); - - // conds could be cached from previous SP call - if (!conds) - { - if (select_lex->vers_setup_conds(thd, table_list)) - DBUG_RETURN(TRUE); - - conds= table_list->on_expr; - table_list->on_expr= NULL; - } - } + const bool delete_history= table_list->vers_conditions.delete_history; if (thd->lex->handle_list_of_derived(table_list, DT_MERGE_FOR_INSERT)) DBUG_RETURN(TRUE); @@ -940,16 +910,36 @@ int mysql_prepare_delete(THD *thd, TABLE_LIST *table_list, select_lex->leaf_tables, FALSE, DELETE_ACL, SELECT_ACL, TRUE)) DBUG_RETURN(TRUE); - if (table_list->vers_conditions.is_set()) + + if (table_list->vers_conditions.is_set() && table_list->is_view_or_derived()) { - if (table_list->is_view()) + my_error(ER_IT_IS_A_VIEW, MYF(0), table_list->table_name.str); + DBUG_RETURN(true); + } + +/* 10.4: + if (table_list->has_period()) + { + if (table_list->is_view_or_derived()) { my_error(ER_IT_IS_A_VIEW, MYF(0), table_list->table_name.str); DBUG_RETURN(true); } - if (select_lex->vers_setup_conds(thd, table_list)) + + if (select_lex->period_setup_conds(thd, table_list)) DBUG_RETURN(true); } +*/ + + DBUG_ASSERT(table_list->table); + // conds could be cached from previous SP call + DBUG_ASSERT(!table_list->vers_conditions.is_set() || + !*conds || thd->stmt_arena->is_stmt_execute()); + if (select_lex->vers_setup_conds(thd, table_list)) + DBUG_RETURN(TRUE); + + *conds= select_lex->where; + if ((wild_num && setup_wild(thd, table_list, field_list, NULL, wild_num, &select_lex->hidden_bit_fields)) || setup_fields(thd, Ref_ptr_array(), @@ -1238,11 +1228,6 @@ int multi_delete::send_data(List &values) if (table->status & (STATUS_NULL_ROW | STATUS_DELETED)) continue; - if (table->versioned() && !table->vers_end_field()->is_max()) - { - continue; - } - table->file->position(table->record[0]); found++; diff --git a/sql/sql_derived.cc b/sql/sql_derived.cc index 44ea6a18663..44595746614 100644 --- a/sql/sql_derived.cc +++ b/sql/sql_derived.cc @@ -689,7 +689,17 @@ bool mysql_derived_prepare(THD *thd, LEX *lex, TABLE_LIST *derived) !(derived->is_multitable() && (thd->lex->sql_command == SQLCOM_UPDATE_MULTI || thd->lex->sql_command == SQLCOM_DELETE_MULTI)))) + { + /* + System versioned tables may still require to get versioning conditions + (when updating view). See vers_setup_conds(). + */ + if (!unit->prepared && + derived->table->versioned() && + (res= unit->prepare(derived, derived->derived_result, 0))) + goto exit; DBUG_RETURN(FALSE); + } /* prevent name resolving out of derived table */ for (SELECT_LEX *sl= first_select; sl; sl= sl->next_select()) diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index a8e66d2a230..861d50e8872 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -4710,8 +4710,10 @@ mysql_execute_command(THD *thd) { result= new (thd->mem_root) multi_delete(thd, aux_tables, lex->table_count); - if (unlikely(result)) + if (likely(result)) { + if (unlikely(select_lex->vers_setup_conds(thd, aux_tables))) + goto multi_delete_error; res= mysql_select(thd, select_lex->get_table_list(), select_lex->with_wild, @@ -4733,6 +4735,7 @@ mysql_execute_command(THD *thd) if (lex->describe || lex->analyze_stmt) res= thd->lex->explain->send_explain(thd); } + multi_delete_error: delete result; } } @@ -9483,7 +9486,7 @@ bool update_precheck(THD *thd, TABLE_LIST *tables) bool delete_precheck(THD *thd, TABLE_LIST *tables) { DBUG_ENTER("delete_precheck"); - if (tables->vers_conditions.is_set()) + if (tables->vers_conditions.delete_history) { if (check_one_table_access(thd, DELETE_HISTORY_ACL, tables)) DBUG_RETURN(TRUE); diff --git a/sql/sql_select.cc b/sql/sql_select.cc index c7501e4fd1d..46e21822cc4 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -677,6 +677,7 @@ bool vers_select_conds_t::init_from_sysvar(THD *thd) { vers_asof_timestamp_t &in= thd->variables.vers_asof_timestamp; type= (vers_system_time_t) in.type; + delete_history= false; start.unit= VERS_TIMESTAMP; if (type != SYSTEM_TIME_UNSPECIFIED && type != SYSTEM_TIME_ALL) { @@ -709,6 +710,7 @@ void vers_select_conds_t::print(String *str, enum_query_type query_type) const end.print(str, query_type, STRING_WITH_LEN(" AND ")); break; case SYSTEM_TIME_BEFORE: + case SYSTEM_TIME_HISTORY: DBUG_ASSERT(0); break; case SYSTEM_TIME_ALL: @@ -776,9 +778,22 @@ int SELECT_LEX::vers_setup_conds(THD *thd, TABLE_LIST *tables) } } + bool is_select= false; + switch (thd->lex->sql_command) + { + case SQLCOM_SELECT: + case SQLCOM_INSERT_SELECT: + case SQLCOM_REPLACE_SELECT: + case SQLCOM_DELETE_MULTI: + case SQLCOM_UPDATE_MULTI: + is_select= true; + default: + break; + } + for (table= tables; table; table= table->next_local) { - if (!table->table || !table->table->versioned()) + if (!table->table || table->is_view() || !table->table->versioned()) continue; vers_select_conds_t &vers_conditions= table->vers_conditions; @@ -808,7 +823,7 @@ int SELECT_LEX::vers_setup_conds(THD *thd, TABLE_LIST *tables) } // propagate system_time from sysvar - if (!vers_conditions.is_set()) + if (!vers_conditions.is_set() && is_select) { if (vers_conditions.init_from_sysvar(thd)) DBUG_RETURN(-1); @@ -834,7 +849,7 @@ int SELECT_LEX::vers_setup_conds(THD *thd, TABLE_LIST *tables) bool timestamps_only= table->table->versioned(VERS_TIMESTAMP); - if (vers_conditions.is_set()) + if (vers_conditions.is_set() && vers_conditions.type != SYSTEM_TIME_HISTORY) { thd->where= "FOR SYSTEM_TIME"; /* TODO: do resolve fix_length_and_dec(), fix_fields(). This requires @@ -861,10 +876,14 @@ int SELECT_LEX::vers_setup_conds(THD *thd, TABLE_LIST *tables) switch (vers_conditions.type) { case SYSTEM_TIME_UNSPECIFIED: + case SYSTEM_TIME_HISTORY: thd->variables.time_zone->gmt_sec_to_TIME(&max_time, TIMESTAMP_MAX_VALUE); max_time.second_part= TIME_MAX_SECOND_PART; curr= newx Item_datetime_literal(thd, &max_time, TIME_SECOND_PART_DIGITS); - cond1= newx Item_func_eq(thd, row_end, curr); + if (vers_conditions.type == SYSTEM_TIME_UNSPECIFIED) + cond1= newx Item_func_eq(thd, row_end, curr); + else + cond1= newx Item_func_lt(thd, row_end, curr); break; case SYSTEM_TIME_AS_OF: cond1= newx Item_func_le(thd, row_start, point_in_time1); @@ -896,8 +915,12 @@ int SELECT_LEX::vers_setup_conds(THD *thd, TABLE_LIST *tables) switch (vers_conditions.type) { case SYSTEM_TIME_UNSPECIFIED: + case SYSTEM_TIME_HISTORY: curr= newx Item_int(thd, ULONGLONG_MAX); - cond1= newx Item_func_eq(thd, row_end, curr); + if (vers_conditions.type == SYSTEM_TIME_UNSPECIFIED) + cond1= newx Item_func_eq(thd, row_end, curr); + else + cond1= newx Item_func_lt(thd, row_end, curr); break; case SYSTEM_TIME_AS_OF: trx_id0= vers_conditions.start.unit == VERS_TIMESTAMP @@ -938,7 +961,19 @@ int SELECT_LEX::vers_setup_conds(THD *thd, TABLE_LIST *tables) { cond1= and_items(thd, cond2, cond1); cond1= and_items(thd, cond3, cond1); - table->on_expr= and_items(thd, table->on_expr, cond1); + if (is_select) + table->on_expr= and_items(thd, table->on_expr, cond1); + else + { + if (join) + { + where= and_items(thd, join->conds, cond1); + join->conds= where; + } + else + where= and_items(thd, where, cond1); + table->where= and_items(thd, table->where, cond1); + } } table->vers_conditions.type= SYSTEM_TIME_ALL; diff --git a/sql/sql_union.cc b/sql/sql_union.cc index c119f1e0116..b71b62b35ed 100644 --- a/sql/sql_union.cc +++ b/sql/sql_union.cc @@ -976,9 +976,21 @@ bool st_select_lex_unit::prepare(TABLE_LIST *derived_arg, if (sl->tvc->prepare(thd, sl, tmp_result, this)) goto err; } - else if (prepare_join(thd, first_sl, tmp_result, additional_options, + else + { + if (prepare_join(thd, first_sl, tmp_result, additional_options, is_union_select)) - goto err; + goto err; + + if (derived_arg && derived_arg->table && + derived_arg->derived_type == VIEW_ALGORITHM_MERGE && + derived_arg->table->versioned()) + { + /* Got versioning conditions (see vers_setup_conds()), need to update + derived_arg. */ + derived_arg->where= first_sl->where; + } + } types= first_sl->item_list; goto cont; } diff --git a/sql/sql_update.cc b/sql/sql_update.cc index 32adc4420b3..39d28bfbe50 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -878,11 +878,6 @@ update_begin: THD_STAGE_INFO(thd, stage_updating); while (!(error=info.read_record()) && !thd->killed) { - if (table->versioned() && !table->vers_end_field()->is_max()) - { - continue; - } - explain->tracker.on_record_read(); thd->inc_examined_row_count(1); if (!select || select->skip_record(thd) > 0) @@ -1266,6 +1261,21 @@ bool mysql_prepare_update(THD *thd, TABLE_LIST *table_list, thd->lex->allow_sum_func.clear_all(); +/* 10.4: + if (table_list->has_period() && + select_lex->period_setup_conds(thd, table_list)) + DBUG_RETURN(true); +*/ + + DBUG_ASSERT(table_list->table); + // conds could be cached from previous SP call + DBUG_ASSERT(!table_list->vers_conditions.is_set() || + !*conds || thd->stmt_arena->is_stmt_execute()); + if (select_lex->vers_setup_conds(thd, table_list)) + DBUG_RETURN(TRUE); + + *conds= select_lex->where; + /* We do not call DT_MERGE_FOR_INSERT because it has no sense for simple (not multi-) update @@ -1786,6 +1796,9 @@ bool mysql_multi_update(THD *thd, TABLE_LIST *table_list, List *fields, thd->abort_on_warning= !ignore && thd->is_strict_mode(); List total_list; + if (select_lex->vers_setup_conds(thd, table_list)) + DBUG_RETURN(1); + res= mysql_select(thd, table_list, select_lex->with_wild, total_list, conds, select_lex->order_list.elements, @@ -2345,11 +2358,6 @@ int multi_update::send_data(List ¬_used_values) if (table->status & (STATUS_NULL_ROW | STATUS_UPDATED)) continue; - if (table->versioned() && !table->vers_end_field()->is_max()) - { - continue; - } - if (table == table_to_update) { /* diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 235be9b0f89..bddbec26d67 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -13785,7 +13785,7 @@ delete: opt_delete_system_time: /* empty */ { - Lex->vers_conditions.init(SYSTEM_TIME_ALL); + Lex->vers_conditions.init(SYSTEM_TIME_HISTORY); } | BEFORE_SYM SYSTEM_TIME_SYM history_point { diff --git a/sql/table.cc b/sql/table.cc index 278423ec0c2..e008e6a3ded 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -9172,6 +9172,8 @@ bool vers_select_conds_t::eq(const vers_select_conds_t &conds) const return true; case SYSTEM_TIME_BEFORE: break; + case SYSTEM_TIME_HISTORY: + break; case SYSTEM_TIME_AS_OF: return start.eq(conds.start); case SYSTEM_TIME_FROM_TO: diff --git a/sql/table.h b/sql/table.h index bec0fdd10ba..1dda70ae0da 100644 --- a/sql/table.h +++ b/sql/table.h @@ -1863,6 +1863,7 @@ struct vers_select_conds_t { vers_system_time_t type; bool used:1; + bool delete_history:1; Vers_history_point start; Vers_history_point end; @@ -1870,6 +1871,7 @@ struct vers_select_conds_t { type= SYSTEM_TIME_UNSPECIFIED; used= false; + delete_history= false; start.empty(); end.empty(); } @@ -1880,6 +1882,8 @@ struct vers_select_conds_t { type= _type; used= false; + delete_history= (type == SYSTEM_TIME_HISTORY || + type == SYSTEM_TIME_BEFORE); start= _start; end= _end; } From 1d5f6a007339ce78471c125a9a5d5a73a5c664ff Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Fri, 22 Nov 2019 14:30:13 +0300 Subject: [PATCH 5/9] MDEV-21049 Segfault in create federatedx table with empty hostname Use my_localhost instead of NULL for share->hostname. --- mysql-test/suite/federated/federatedx.result | 16 +++++++++++++++ mysql-test/suite/federated/federatedx.test | 21 ++++++++++++++++++++ storage/federatedx/ha_federatedx.cc | 7 +++---- 3 files changed, 40 insertions(+), 4 deletions(-) diff --git a/mysql-test/suite/federated/federatedx.result b/mysql-test/suite/federated/federatedx.result index 84dcb0d0a8c..8345f56dba9 100644 --- a/mysql-test/suite/federated/federatedx.result +++ b/mysql-test/suite/federated/federatedx.result @@ -2283,6 +2283,22 @@ connection default; connection master; CREATE TABLE t1 (a INT) ENGINE=FEDERATED CONNECTION='mysql://@127.0.0.1:SLAVE_PORT/federated/t1'; ERROR HY000: Can't create federated table. Foreign data src error: database: 'federated' username: '' hostname: '127.0.0.1' +# +# MDEV-21049 Segfault in create federatedx table with empty hostname +# +connection master; +CREATE TABLE federated.t1 (x int) ENGINE=FEDERATED +CONNECTION='mysql://root@:SLAVE_PORT/federated/t1'; +ERROR HY000: Can't create federated table. Foreign data src error: database: 'federated' username: 'root' hostname: 'localhost' +connection slave; +CREATE TABLE federated.t1(x int); +connection master; +CREATE TABLE federated.t1 (x int) ENGINE=FEDERATED +CONNECTION='mysql://root@:SLAVE_PORT/federated/t1'; +DROP TABLE federated.t1; +connection slave; +DROP TABLE federated.t1; +connection default; connection master; DROP TABLE IF EXISTS federated.t1; DROP DATABASE IF EXISTS federated; diff --git a/mysql-test/suite/federated/federatedx.test b/mysql-test/suite/federated/federatedx.test index 29d1eaddc26..fcc0178c024 100644 --- a/mysql-test/suite/federated/federatedx.test +++ b/mysql-test/suite/federated/federatedx.test @@ -2010,4 +2010,25 @@ connection master; --error ER_CANT_CREATE_FEDERATED_TABLE eval CREATE TABLE t1 (a INT) ENGINE=FEDERATED CONNECTION='mysql://@127.0.0.1:$SLAVE_MYPORT/federated/t1'; +--echo # +--echo # MDEV-21049 Segfault in create federatedx table with empty hostname +--echo # +connection master; +--replace_result $SLAVE_MYPORT SLAVE_PORT +--error ER_CANT_CREATE_FEDERATED_TABLE +eval CREATE TABLE federated.t1 (x int) ENGINE=FEDERATED + CONNECTION='mysql://root@:$SLAVE_MYPORT/federated/t1'; + +connection slave; +CREATE TABLE federated.t1(x int); +connection master; +--replace_result $SLAVE_MYPORT SLAVE_PORT +eval CREATE TABLE federated.t1 (x int) ENGINE=FEDERATED + CONNECTION='mysql://root@:$SLAVE_MYPORT/federated/t1'; + +DROP TABLE federated.t1; +connection slave; +DROP TABLE federated.t1; +connection default; + source include/federated_cleanup.inc; diff --git a/storage/federatedx/ha_federatedx.cc b/storage/federatedx/ha_federatedx.cc index 74d547cb674..4a717eead20 100644 --- a/storage/federatedx/ha_federatedx.cc +++ b/storage/federatedx/ha_federatedx.cc @@ -798,12 +798,12 @@ static int parse_url(MEM_ROOT *mem_root, FEDERATEDX_SHARE *share, goto error; if (share->hostname[0] == '\0') - share->hostname= NULL; + share->hostname= strdup_root(mem_root, my_localhost); } if (!share->port) { - if (!share->hostname || strcmp(share->hostname, my_localhost) == 0) + if (0 == strcmp(share->hostname, my_localhost)) share->socket= (char *) MYSQL_UNIX_ADDR; else share->port= MYSQL_PORT; @@ -3385,8 +3385,7 @@ int ha_federatedx::create(const char *name, TABLE *table_arg, goto error; /* loopback socket connections hang due to LOCK_open mutex */ - if ((!tmp_share.hostname || !strcmp(tmp_share.hostname,my_localhost)) && - !tmp_share.port) + if (0 == strcmp(tmp_share.hostname, my_localhost) && !tmp_share.port) goto error; /* From f95288211ce1023e0d268229fbe5febbf0b2edd3 Mon Sep 17 00:00:00 2001 From: Igor Babaev Date: Fri, 22 Nov 2019 19:11:58 -0800 Subject: [PATCH 6/9] MDEV-19919 Assertion `!prebuilt->index->is_primary()' failed in row_search_idx_cond_check For a single table query with ORDER BY and several sargable range conditions the optimizer may choose an execution plan that employs a rowid filter. In this case it is important to build the filter before calling the function JOIN_TAB::sort_table() that creates sort index for the result set, because when this is index created the filter has to be already filled. After the sort index has been created the filter must be deactivated. If not to do this the innodb function row_search_idx_cond_check() is getting confused when it has to read rows from the created sort index by using ha_rnd_pos(). The order of actions mentioned above is needed also when processing a join query if sorting is performed for the first non constant table in the chosen execution plan. --- mysql-test/main/rowid_filter_innodb.result | 35 ++++++++++++++++++++++ mysql-test/main/rowid_filter_innodb.test | 33 ++++++++++++++++++++ sql/sql_select.cc | 8 +++-- 3 files changed, 74 insertions(+), 2 deletions(-) diff --git a/mysql-test/main/rowid_filter_innodb.result b/mysql-test/main/rowid_filter_innodb.result index 390c7834a0a..37e32f0291a 100644 --- a/mysql-test/main/rowid_filter_innodb.result +++ b/mysql-test/main/rowid_filter_innodb.result @@ -2210,3 +2210,38 @@ a b drop table t1; set optimizer_switch=@save_optimizer_switch; SET SESSION STORAGE_ENGINE=DEFAULT; +# +# MDEV-19919: use of rowid filter for innodb table + ORDER BY +# +SET @stats.save= @@innodb_stats_persistent; +SET GLOBAL innodb_stats_persistent= ON; +CREATE TABLE t1 ( +a INT, +b VARCHAR(10), +c VARCHAR(1024), +KEY (b), +KEY (c) +) ENGINE=InnoDB; +INSERT INTO t1 VALUES +(1,'w','z'), (1,'X','o'), (1,'q','c'), (5,'w','c'), (2,'j','m'), +(2,'Q','s'), (9,'e','J'), (2,'p','W'), (9,'o','F'), (2,'g','S'), +(1,'Y','a'), (NULL,'Y','p'), (NULL,'s','x'), (NULL,'i','S'), +(1,'l','q'), (7,'r','e'), (4,'b','h'), (NULL,'E','c'), +(NULL,'M','a'), (3,'e','X'), (NULL,'p','r'), (9,'e','i'), +(3,'g','x'), (2,'h','y'); +ANALYZE TABLE t1; +Table Op Msg_type Msg_text +test.t1 analyze status Engine-independent statistics collected +test.t1 analyze status OK +EXPLAIN EXTENDED +SELECT a FROM t1 WHERE c < 'k' AND b > 't' ORDER BY a; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 range|filter b,c b|c 13|1027 NULL 5 (42%) 41.67 Using index condition; Using where; Using filesort; Using rowid filter +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`c` < 'k' and `test`.`t1`.`b` > 't' order by `test`.`t1`.`a` +SELECT a FROM t1 WHERE c < 'k' AND b > 't' ORDER BY a; +a +1 +5 +DROP TABLE t1; +SET GLOBAL innodb_stats_persistent= @stats.save; diff --git a/mysql-test/main/rowid_filter_innodb.test b/mysql-test/main/rowid_filter_innodb.test index 240cd928d06..4a6c4316aa8 100644 --- a/mysql-test/main/rowid_filter_innodb.test +++ b/mysql-test/main/rowid_filter_innodb.test @@ -96,3 +96,36 @@ drop table t1; set optimizer_switch=@save_optimizer_switch; SET SESSION STORAGE_ENGINE=DEFAULT; + +--echo # +--echo # MDEV-19919: use of rowid filter for innodb table + ORDER BY +--echo # + +SET @stats.save= @@innodb_stats_persistent; +SET GLOBAL innodb_stats_persistent= ON; + +CREATE TABLE t1 ( + a INT, + b VARCHAR(10), + c VARCHAR(1024), + KEY (b), + KEY (c) +) ENGINE=InnoDB; + +INSERT INTO t1 VALUES + (1,'w','z'), (1,'X','o'), (1,'q','c'), (5,'w','c'), (2,'j','m'), + (2,'Q','s'), (9,'e','J'), (2,'p','W'), (9,'o','F'), (2,'g','S'), + (1,'Y','a'), (NULL,'Y','p'), (NULL,'s','x'), (NULL,'i','S'), + (1,'l','q'), (7,'r','e'), (4,'b','h'), (NULL,'E','c'), + (NULL,'M','a'), (3,'e','X'), (NULL,'p','r'), (9,'e','i'), + (3,'g','x'), (2,'h','y'); + +ANALYZE TABLE t1; + +EXPLAIN EXTENDED +SELECT a FROM t1 WHERE c < 'k' AND b > 't' ORDER BY a; + +SELECT a FROM t1 WHERE c < 'k' AND b > 't' ORDER BY a; + +DROP TABLE t1; +SET GLOBAL innodb_stats_persistent= @stats.save; diff --git a/sql/sql_select.cc b/sql/sql_select.cc index a05c2f81b10..1ee2a175579 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -21101,11 +21101,12 @@ int join_init_read_record(JOIN_TAB *tab) */ if (tab->distinct && tab->remove_duplicates()) // Remove duplicates. return 1; - if (tab->filesort && tab->sort_table()) // Sort table. - return 1; tab->build_range_rowid_filter_if_needed(); + if (tab->filesort && tab->sort_table()) // Sort table. + return 1; + DBUG_EXECUTE_IF("kill_join_init_read_record", tab->join->thd->set_killed(KILL_QUERY);); if (tab->select && tab->select->quick && tab->select->quick->reset()) @@ -21165,6 +21166,9 @@ JOIN_TAB::sort_table() JOIN::ordered_index_order_by : JOIN::ordered_index_group_by)); rc= create_sort_index(join->thd, join, this, NULL); + /* Disactivate rowid filter if it was used when creating sort index */ + if (rowid_filter) + table->file->rowid_filter_is_active= false; return (rc != 0); } From 4111a53079da9850c630ce30eec7f8a38744eacd Mon Sep 17 00:00:00 2001 From: seppo Date: Mon, 25 Nov 2019 11:19:33 +0200 Subject: [PATCH 7/9] MDEV-21096 async slave crash with gtid_log_pos table access (#1413) The original crash happened when async replication IO thread was updating mysql.gtid_slave_pos table. Operations on this table should remain node local, but it appears that protection (THD::wsrep_ignore_table flag) to prevent wsrep replication for this table mas missing for innodb write_row() and update_row(). It was somewhat difficult to reproduce the issue, because mtr seems to create the affected table mysql.gtid_log_pos as of Aria engine type, and Aria engine operations will not be replicated anyhow. It looks, though, that in release installation, mysql.gtid_slave_pos table is of InnoDB engine. It was possible to trigger somewhat related problem by running test galera.galera_as_slave_gtid with configuration: gtid_pos_auto_engines=InnoDB. However, this test mode, causes earlier crash when replication background thread creates aditional table: mysql.gtid_slave_pos_InnoDB, and this table create triggered wsrep TOI replication, which also failed for assertion. Actually, async replication IO and background threads should not replicate anything to cluster. This pull request contains new test galera.galera_as_slave_gtid_auto_engine, which basically just runs galera.galera_as_slave_gtid with configuration of gtid_pos_auto_engines=InnoDB. Test galera.galera_as_slave_gtid is also modified for better code reuse. Actual fix for MDEV-21096 is in storage/innobase/handler/ha_innodb.cc, where THD::wsrep_ignore_table flag is now honored before wsrep key population. There is additional fix in sql/service_wsrep.cc where async replication IO and background threads are marked as non-local. This fences these threads out of wsrep replication altogether. Note that this change, actually makes the use of THD::wsrep_ignore-table redundant. We may want to refactor THD::wsrep_ignore_table out in the future, if there is no other use case for it in sight. --- .../r/galera_as_slave_gtid_auto_engine.result | 40 +++++++++ .../suite/galera/t/galera_as_slave_gtid.inc | 86 +++++++++++++++++++ .../suite/galera/t/galera_as_slave_gtid.test | 78 +---------------- .../t/galera_as_slave_gtid_auto_engine.cnf | 8 ++ .../t/galera_as_slave_gtid_auto_engine.test | 14 +++ sql/service_wsrep.cc | 12 ++- storage/innobase/handler/ha_innodb.cc | 4 +- 7 files changed, 163 insertions(+), 79 deletions(-) create mode 100644 mysql-test/suite/galera/r/galera_as_slave_gtid_auto_engine.result create mode 100644 mysql-test/suite/galera/t/galera_as_slave_gtid.inc create mode 100644 mysql-test/suite/galera/t/galera_as_slave_gtid_auto_engine.cnf create mode 100644 mysql-test/suite/galera/t/galera_as_slave_gtid_auto_engine.test diff --git a/mysql-test/suite/galera/r/galera_as_slave_gtid_auto_engine.result b/mysql-test/suite/galera/r/galera_as_slave_gtid_auto_engine.result new file mode 100644 index 00000000000..6c84c1ecd31 --- /dev/null +++ b/mysql-test/suite/galera/r/galera_as_slave_gtid_auto_engine.result @@ -0,0 +1,40 @@ +connection node_2; +connection node_1; +connect node_3, 127.0.0.1, root, , test, $NODE_MYPORT_3; +connection node_2; +START SLAVE; +connection node_3; +CREATE TABLE t1 (f1 INTEGER PRIMARY KEY) ENGINE=InnoDB; +INSERT INTO t1 VALUES(1); +SELECT LENGTH(@@global.gtid_binlog_state) > 1; +LENGTH(@@global.gtid_binlog_state) > 1 +1 +connection node_2; +gtid_binlog_state_equal +1 +connection node_1; +SELECT COUNT(*) = 1 FROM t1; +COUNT(*) = 1 +1 +gtid_binlog_state_equal +1 +connection node_3; +DROP TABLE t1; +connection node_1; +connection node_2; +STOP SLAVE; +RESET SLAVE ALL; +#cleanup +connection node_1; +set global wsrep_on=OFF; +reset master; +set global wsrep_on=ON; +connection node_2; +set global wsrep_on=OFF; +reset master; +set global wsrep_on=ON; +connection node_3; +reset master; +connection node_2; +DROP TABLE mysql.gtid_slave_pos_InnoDB; +CALL mtr.add_suppression("The automatically created table"); diff --git a/mysql-test/suite/galera/t/galera_as_slave_gtid.inc b/mysql-test/suite/galera/t/galera_as_slave_gtid.inc new file mode 100644 index 00000000000..f5222b4322b --- /dev/null +++ b/mysql-test/suite/galera/t/galera_as_slave_gtid.inc @@ -0,0 +1,86 @@ +# +# Test Galera as a slave to a MariaDB master using GTIDs +# +# suite/galera/galera_2nodes_as_slave.cnf describes the setup of the nodes +# suite/galera/t/galera_as_slave_gtid.cnf has the GTID options +# +# In addition to performing DDL and DML, we check that the gtid of the master is preserved inside the cluster +# + +--source include/have_innodb.inc +--source include/galera_cluster.inc + +# As node #3 is not a Galera node, and galera_cluster.inc does not open connetion to it +# we open the node_3 connection here +--connect node_3, 127.0.0.1, root, , test, $NODE_MYPORT_3 + +--connection node_2 +--disable_query_log +--eval CHANGE MASTER TO MASTER_HOST='127.0.0.1', MASTER_USER='root', MASTER_PORT=$NODE_MYPORT_3; +--enable_query_log +START SLAVE; + +--connection node_3 +CREATE TABLE t1 (f1 INTEGER PRIMARY KEY) ENGINE=InnoDB; +INSERT INTO t1 VALUES(1); + +SELECT LENGTH(@@global.gtid_binlog_state) > 1; +--let $gtid_binlog_state_node1 = `SELECT @@global.gtid_binlog_state;` + +--connection node_2 +--let $wait_condition = SELECT COUNT(*) = 1 FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 't1'; +--source include/wait_condition.inc + +--let $wait_condition = SELECT COUNT(*) = 1 FROM t1; +--source include/wait_condition.inc + +--disable_query_log + +--eval SELECT '$gtid_binlog_state_node1' = @@global.gtid_binlog_state AS gtid_binlog_state_equal; +#--eval SELECT GTID_SUBSET('$gtid_executed_node1', @@global.gtid_executed) AS gtid_executed_equal; + +--enable_query_log + +--connection node_1 +SELECT COUNT(*) = 1 FROM t1; + +--disable_query_log +--eval SELECT '$gtid_binlog_state_node1' = @@global.gtid_binlog_state AS gtid_binlog_state_equal; +#--eval SELECT GTID_SUBSET('$gtid_executed_node1', @@global.gtid_executed) AS gtid_executed_equal; +--enable_query_log + +--connection node_3 +DROP TABLE t1; + +# +# Unfortunately without the sleep below the following statement fails with "query returned no rows", which +# is difficult to understand given that it is an aggregate query. A "query execution was interrupted" +# warning is also reported by MTR, which is also weird. +# + +--sleep 1 + +--connection node_1 +--let $wait_condition = SELECT COUNT(*) = 0 FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 't1'; +--source include/wait_condition.inc + +--connection node_2 +--let $wait_condition = SELECT COUNT(*) = 0 FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 't1'; +--source include/wait_condition.inc + +STOP SLAVE; +RESET SLAVE ALL; + +--echo #cleanup +--connection node_1 +set global wsrep_on=OFF; +reset master; +set global wsrep_on=ON; + +--connection node_2 +set global wsrep_on=OFF; +reset master; +set global wsrep_on=ON; + +--connection node_3 +reset master; diff --git a/mysql-test/suite/galera/t/galera_as_slave_gtid.test b/mysql-test/suite/galera/t/galera_as_slave_gtid.test index f5222b4322b..c5f45031050 100644 --- a/mysql-test/suite/galera/t/galera_as_slave_gtid.test +++ b/mysql-test/suite/galera/t/galera_as_slave_gtid.test @@ -7,80 +7,4 @@ # In addition to performing DDL and DML, we check that the gtid of the master is preserved inside the cluster # ---source include/have_innodb.inc ---source include/galera_cluster.inc - -# As node #3 is not a Galera node, and galera_cluster.inc does not open connetion to it -# we open the node_3 connection here ---connect node_3, 127.0.0.1, root, , test, $NODE_MYPORT_3 - ---connection node_2 ---disable_query_log ---eval CHANGE MASTER TO MASTER_HOST='127.0.0.1', MASTER_USER='root', MASTER_PORT=$NODE_MYPORT_3; ---enable_query_log -START SLAVE; - ---connection node_3 -CREATE TABLE t1 (f1 INTEGER PRIMARY KEY) ENGINE=InnoDB; -INSERT INTO t1 VALUES(1); - -SELECT LENGTH(@@global.gtid_binlog_state) > 1; ---let $gtid_binlog_state_node1 = `SELECT @@global.gtid_binlog_state;` - ---connection node_2 ---let $wait_condition = SELECT COUNT(*) = 1 FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 't1'; ---source include/wait_condition.inc - ---let $wait_condition = SELECT COUNT(*) = 1 FROM t1; ---source include/wait_condition.inc - ---disable_query_log - ---eval SELECT '$gtid_binlog_state_node1' = @@global.gtid_binlog_state AS gtid_binlog_state_equal; -#--eval SELECT GTID_SUBSET('$gtid_executed_node1', @@global.gtid_executed) AS gtid_executed_equal; - ---enable_query_log - ---connection node_1 -SELECT COUNT(*) = 1 FROM t1; - ---disable_query_log ---eval SELECT '$gtid_binlog_state_node1' = @@global.gtid_binlog_state AS gtid_binlog_state_equal; -#--eval SELECT GTID_SUBSET('$gtid_executed_node1', @@global.gtid_executed) AS gtid_executed_equal; ---enable_query_log - ---connection node_3 -DROP TABLE t1; - -# -# Unfortunately without the sleep below the following statement fails with "query returned no rows", which -# is difficult to understand given that it is an aggregate query. A "query execution was interrupted" -# warning is also reported by MTR, which is also weird. -# - ---sleep 1 - ---connection node_1 ---let $wait_condition = SELECT COUNT(*) = 0 FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 't1'; ---source include/wait_condition.inc - ---connection node_2 ---let $wait_condition = SELECT COUNT(*) = 0 FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 't1'; ---source include/wait_condition.inc - -STOP SLAVE; -RESET SLAVE ALL; - ---echo #cleanup ---connection node_1 -set global wsrep_on=OFF; -reset master; -set global wsrep_on=ON; - ---connection node_2 -set global wsrep_on=OFF; -reset master; -set global wsrep_on=ON; - ---connection node_3 -reset master; +--source galera_as_slave_gtid.inc diff --git a/mysql-test/suite/galera/t/galera_as_slave_gtid_auto_engine.cnf b/mysql-test/suite/galera/t/galera_as_slave_gtid_auto_engine.cnf new file mode 100644 index 00000000000..adcba9b6069 --- /dev/null +++ b/mysql-test/suite/galera/t/galera_as_slave_gtid_auto_engine.cnf @@ -0,0 +1,8 @@ +!include ../galera_2nodes_as_slave.cnf + +[mysqld] +log-bin=mysqld-bin +log-slave-updates +binlog-format=ROW + +gtid_pos_auto_engines=InnoDB \ No newline at end of file diff --git a/mysql-test/suite/galera/t/galera_as_slave_gtid_auto_engine.test b/mysql-test/suite/galera/t/galera_as_slave_gtid_auto_engine.test new file mode 100644 index 00000000000..990dd35f40e --- /dev/null +++ b/mysql-test/suite/galera/t/galera_as_slave_gtid_auto_engine.test @@ -0,0 +1,14 @@ +# +# Test Galera as a slave to a MariaDB master using GTIDs +# +# suite/galera/galera_2nodes_as_slave.cnf describes the setup of the nodes +# suite/galera/t/galera_as_slave_gtid.cnf has the GTID options +# +# In addition to performing DDL and DML, we check that the gtid of the master is preserved inside the cluster +# + +--source galera_as_slave_gtid.inc + +--connection node_2 +DROP TABLE mysql.gtid_slave_pos_InnoDB; +CALL mtr.add_suppression("The automatically created table"); \ No newline at end of file diff --git a/sql/service_wsrep.cc b/sql/service_wsrep.cc index 35bc1b83029..5526c343d69 100644 --- a/sql/service_wsrep.cc +++ b/sql/service_wsrep.cc @@ -112,7 +112,17 @@ extern "C" my_bool wsrep_get_debug() extern "C" my_bool wsrep_thd_is_local(const THD *thd) { - return thd->wsrep_cs().mode() == wsrep::client_state::m_local; + /* + async replication IO and background threads have nothing to replicate in the cluster, + marking them as non-local here to prevent write set population and replication + + async replication SQL thread, applies client transactions from mariadb master + and will be replicated into cluster + */ + return ( + thd->system_thread != SYSTEM_THREAD_SLAVE_BACKGROUND && + thd->system_thread != SYSTEM_THREAD_SLAVE_IO && + thd->wsrep_cs().mode() == wsrep::client_state::m_local); } extern "C" my_bool wsrep_thd_is_applying(const THD *thd) diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index 836ea5f603e..e6cef3a57c6 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -8199,6 +8199,7 @@ report_error: if (!error_result && wsrep_on(m_user_thd) && wsrep_thd_is_local(m_user_thd) + && !wsrep_thd_ignore_table(m_user_thd) && !wsrep_consistency_check(m_user_thd) && (thd_sql_command(m_user_thd) != SQLCOM_CREATE_TABLE) && (thd_sql_command(m_user_thd) != SQLCOM_LOAD || @@ -8909,7 +8910,8 @@ func_exit: #ifdef WITH_WSREP if (error == DB_SUCCESS && wsrep_on(m_user_thd) - && wsrep_thd_is_local(m_user_thd)) { + && wsrep_thd_is_local(m_user_thd) + && !wsrep_thd_ignore_table(m_user_thd)) { DBUG_PRINT("wsrep", ("update row key")); From 33f55789d354b0fccf5234027dc0bd66cbd0c539 Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Mon, 25 Nov 2019 16:01:43 +0300 Subject: [PATCH 8/9] MDEV-18727 improve DML operation of System Versioning (10.4) UPDATE, DELETE: replace linear search of current/historical records with vers_setup_conds(). Additional DML cases in view.test --- mysql-test/suite/versioning/r/delete.result | 6 +- ...{truncate.result => delete_history.result} | 0 .../suite/versioning/r/partition.result | 17 ++++ mysql-test/suite/versioning/r/view.result | 78 +++++++++++++++++-- mysql-test/suite/versioning/t/delete.test | 10 +-- .../t/{truncate.test => delete_history.test} | 0 mysql-test/suite/versioning/t/partition.test | 14 ++++ mysql-test/suite/versioning/t/view.test | 47 +++++++++-- sql/ha_partition.cc | 2 +- sql/mysqld.h | 3 +- sql/sql_delete.cc | 65 ++++------------ sql/sql_derived.cc | 10 +++ sql/sql_lex.h | 2 +- sql/sql_parse.cc | 7 +- sql/sql_select.cc | 60 +++++++++++--- sql/sql_union.cc | 16 +++- sql/sql_update.cc | 29 ++++--- sql/sql_yacc.yy | 2 +- sql/table.cc | 2 + sql/table.h | 4 + 20 files changed, 267 insertions(+), 107 deletions(-) rename mysql-test/suite/versioning/r/{truncate.result => delete_history.result} (100%) rename mysql-test/suite/versioning/t/{truncate.test => delete_history.test} (100%) diff --git a/mysql-test/suite/versioning/r/delete.result b/mysql-test/suite/versioning/r/delete.result index 77b7fc80286..26ade83acd7 100644 --- a/mysql-test/suite/versioning/r/delete.result +++ b/mysql-test/suite/versioning/r/delete.result @@ -1,3 +1,4 @@ +# Basic + delete from view create or replace table t1( XNo int unsigned, sys_start SYS_DATATYPE as row start invisible, @@ -44,6 +45,7 @@ XNo_vt1 5 drop view vt1; drop table t1; +# Check sys_start, sys_end create or replace table t1( x int, sys_start SYS_DATATYPE as row start invisible, @@ -59,6 +61,7 @@ select x = 1 as A, sys_start = @sys_start as B, sys_end > sys_start as C from t1 A B C 1 1 1 drop table t1; +# Multi-delete create or replace table t1( x int, y int, @@ -103,9 +106,6 @@ t2_x_all 14 drop table t1; drop table t2; -# Basic + delete from view -# Check sys_start, sys_end -# Multi-delete # Update + delete create or replace table t1 (x int) with system versioning; insert into t1 values (1); diff --git a/mysql-test/suite/versioning/r/truncate.result b/mysql-test/suite/versioning/r/delete_history.result similarity index 100% rename from mysql-test/suite/versioning/r/truncate.result rename to mysql-test/suite/versioning/r/delete_history.result diff --git a/mysql-test/suite/versioning/r/partition.result b/mysql-test/suite/versioning/r/partition.result index 690617551de..76268af9c3c 100644 --- a/mysql-test/suite/versioning/r/partition.result +++ b/mysql-test/suite/versioning/r/partition.result @@ -574,3 +574,20 @@ execute immediate 'select * from t1 for update'; pk drop view v1; drop tables t, t1, t2, t3, t4; +# +# MDEV-18957 UPDATE with LIMIT clause is wrong for versioned partitioned tables +# +create or replace table t1 ( +x int, +a varchar(255) +) with system versioning partition by system_time (partition p1 history, partition pn current); +insert into t1 (x) values (1), (2), (3), (4); +update t1 set a= 'foo' limit 3; +update t1 set a= 'bar' limit 4; +select * from t1; +x a +1 bar +2 bar +3 bar +4 bar +drop table t1; diff --git a/mysql-test/suite/versioning/r/view.result b/mysql-test/suite/versioning/r/view.result index 850eba32c0d..8b23e87d6a4 100644 --- a/mysql-test/suite/versioning/r/view.result +++ b/mysql-test/suite/versioning/r/view.result @@ -64,13 +64,13 @@ select * from vt1; x 1 2 -# VIEW with parameters [#151] +# VIEW with parameters [tempesta-tech/mariadb#151] create or replace table t1 (x int) with system versioning; create or replace view vt1(c) as select x from t1; show create view vt1; View Create View character_set_client collation_connection vt1 CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `vt1` AS select `t1`.`x` AS `c` from `t1` latin1 latin1_swedish_ci -# VIEW over JOIN of versioned tables [#153] +# VIEW over JOIN of versioned tables [tempesta-tech/mariadb#153] create or replace table t1 (a int) with system versioning; create or replace table t2 (b int) with system versioning; insert into t1 values (1); @@ -82,7 +82,7 @@ a b create or replace view vt12 as select * from t1 for system_time as of timestamp ('0-0-0') cross join t2; select * from vt12; a b -# VIEW improvements [#183] +# VIEW improvements [tempesta-tech/mariadb#183] create or replace table t3 (x int); create or replace view vt1 as select * from t1, t2, t3; show create view vt1; @@ -96,12 +96,12 @@ create or replace view vt1 as select a, t2.row_end as endo from t3, t1, t2; show create view vt1; View Create View character_set_client collation_connection vt1 CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `vt1` AS select `t1`.`a` AS `a`,`t2`.`row_end` AS `endo` from ((`t3` join `t1`) join `t2`) latin1 latin1_swedish_ci -# VIEW over UNION [#269] +# VIEW over UNION [tempesta-tech/mariadb#269] create or replace view vt1 as select * from t1 union select * from t1; select * from vt1; a 1 -# VIEW over UNION with non-versioned [#393] +# VIEW over UNION with non-versioned [tempesta-tech/mariadb#393] create or replace table t2 (a int); create or replace view vt1 as select * from t1 union select * from t2; select * from vt1; @@ -123,10 +123,10 @@ drop tables t1, t2; # # MDEV-15146 SQLError[4122]: View is not system versioned # -create table t1 (a int) with system versioning; +create or replace table t1 (a int) with system versioning; insert t1 values (1),(2); set @a=now(6); -create view v1 as select * from t1; +create or replace view v1 as select * from t1; delete from t1; select * from v1; a @@ -149,3 +149,67 @@ View Create View character_set_client collation_connection v1 CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v1` AS select `t1`.`i` AS `i` from `t1` FOR SYSTEM_TIME AS OF current_timestamp() - interval 6 second latin1 latin1_swedish_ci drop view v1, vt1, vt12; drop tables t1, t3; +# +# MDEV-18727 improve DML operation of System Versioning +# +create or replace table t1 ( +x int, +row_start SYS_DATATYPE as row start invisible, +row_end SYS_DATATYPE as row end invisible, +period for system_time (row_start, row_end) +) with system versioning; +insert into t1 values (1), (2); +create or replace view v1 as select * from t1 where x > 1; +update v1 set x= x + 1; +select *, check_row(row_start, row_end) from t1 for system_time all order by x; +x check_row(row_start, row_end) +1 CURRENT ROW +2 HISTORICAL ROW +3 CURRENT ROW +insert v1 values (4); +select *, check_row(row_start, row_end) from t1 for system_time all order by x; +x check_row(row_start, row_end) +1 CURRENT ROW +2 HISTORICAL ROW +3 CURRENT ROW +4 CURRENT ROW +delete from v1 where x < 4; +select *, check_row(row_start, row_end) from t1 for system_time all order by x; +x check_row(row_start, row_end) +1 CURRENT ROW +2 HISTORICAL ROW +3 HISTORICAL ROW +4 CURRENT ROW +# multi-update +create or replace table t2 like t1; +insert into t2 values (1), (2); +create or replace view v2 as select * from t2 where x > 1; +update v1, v2 set v1.x= v1.x + 1, v2.x= v2.x + 1 where v1.x = v2.x + 2; +select *, check_row(row_start, row_end) from t1 for system_time all order by x; +x check_row(row_start, row_end) +1 CURRENT ROW +2 HISTORICAL ROW +3 HISTORICAL ROW +4 HISTORICAL ROW +5 CURRENT ROW +select *, check_row(row_start, row_end) from t2 for system_time all order by x; +x check_row(row_start, row_end) +1 CURRENT ROW +2 HISTORICAL ROW +3 CURRENT ROW +# multi-delete +delete v1, v2 from v1 join v2 where v1.x = v2.x + 2; +select *, check_row(row_start, row_end) from t1 for system_time all order by x; +x check_row(row_start, row_end) +1 CURRENT ROW +2 HISTORICAL ROW +3 HISTORICAL ROW +4 HISTORICAL ROW +5 HISTORICAL ROW +select *, check_row(row_start, row_end) from t2 for system_time all order by x; +x check_row(row_start, row_end) +1 CURRENT ROW +2 HISTORICAL ROW +3 HISTORICAL ROW +drop view v1, v2; +drop tables t1, t2; diff --git a/mysql-test/suite/versioning/t/delete.test b/mysql-test/suite/versioning/t/delete.test index b9045898bb0..4f1ba4b1d8e 100644 --- a/mysql-test/suite/versioning/t/delete.test +++ b/mysql-test/suite/versioning/t/delete.test @@ -1,6 +1,7 @@ source suite/versioning/engines.inc; source suite/versioning/common.inc; +--echo # Basic + delete from view replace_result $sys_datatype_expl SYS_DATATYPE; eval create or replace table t1( XNo int unsigned, @@ -31,7 +32,7 @@ select XNo as XNo_vt1 from vt1; drop view vt1; drop table t1; - +--echo # Check sys_start, sys_end replace_result $sys_datatype_expl SYS_DATATYPE; eval create or replace table t1( x int, @@ -47,6 +48,7 @@ select * from t1; select x = 1 as A, sys_start = @sys_start as B, sys_end > sys_start as C from t1 for system_time all; drop table t1; +--echo # Multi-delete replace_result $sys_datatype_expl SYS_DATATYPE; eval create or replace table t1( x int, @@ -69,12 +71,6 @@ select x as t2_x_all from t2 for system_time all; drop table t1; drop table t2; ---echo # Basic + delete from view - ---echo # Check sys_start, sys_end - ---echo # Multi-delete - --echo # Update + delete create or replace table t1 (x int) with system versioning; insert into t1 values (1); diff --git a/mysql-test/suite/versioning/t/truncate.test b/mysql-test/suite/versioning/t/delete_history.test similarity index 100% rename from mysql-test/suite/versioning/t/truncate.test rename to mysql-test/suite/versioning/t/delete_history.test diff --git a/mysql-test/suite/versioning/t/partition.test b/mysql-test/suite/versioning/t/partition.test index e3acf53686e..bf6dd18118e 100644 --- a/mysql-test/suite/versioning/t/partition.test +++ b/mysql-test/suite/versioning/t/partition.test @@ -508,4 +508,18 @@ execute immediate 'select * from t1 for update'; drop view v1; drop tables t, t1, t2, t3, t4; +--echo # +--echo # MDEV-18957 UPDATE with LIMIT clause is wrong for versioned partitioned tables +--echo # +create or replace table t1 ( + x int, + a varchar(255) +) with system versioning partition by system_time (partition p1 history, partition pn current); + +insert into t1 (x) values (1), (2), (3), (4); +update t1 set a= 'foo' limit 3; +update t1 set a= 'bar' limit 4; +select * from t1; +drop table t1; + --source suite/versioning/common_finish.inc diff --git a/mysql-test/suite/versioning/t/view.test b/mysql-test/suite/versioning/t/view.test index 5a03a50f1d3..c05fbfd3866 100644 --- a/mysql-test/suite/versioning/t/view.test +++ b/mysql-test/suite/versioning/t/view.test @@ -52,13 +52,13 @@ prepare stmt from @tmp; execute stmt; drop prepare stmt; select * from vt1; ---echo # VIEW with parameters [#151] +--echo # VIEW with parameters [tempesta-tech/mariadb#151] create or replace table t1 (x int) with system versioning; create or replace view vt1(c) as select x from t1; --replace_result 18446744073709551615 MAX_RESULT "TIMESTAMP'2038-01-19 03:14:07.999999'" MAX_RESULT show create view vt1; ---echo # VIEW over JOIN of versioned tables [#153] +--echo # VIEW over JOIN of versioned tables [tempesta-tech/mariadb#153] create or replace table t1 (a int) with system versioning; create or replace table t2 (b int) with system versioning; insert into t1 values (1); @@ -68,7 +68,7 @@ select * from vt12; create or replace view vt12 as select * from t1 for system_time as of timestamp ('0-0-0') cross join t2; select * from vt12; ---echo # VIEW improvements [#183] +--echo # VIEW improvements [tempesta-tech/mariadb#183] create or replace table t3 (x int); create or replace view vt1 as select * from t1, t2, t3; --replace_result 18446744073709551615 MAX_RESULT "TIMESTAMP'2038-01-19 03:14:07.999999'" MAX_RESULT @@ -80,11 +80,11 @@ create or replace view vt1 as select a, t2.row_end as endo from t3, t1, t2; --replace_result 18446744073709551615 MAX_RESULT "TIMESTAMP'2038-01-19 03:14:07.999999'" MAX_RESULT show create view vt1; ---echo # VIEW over UNION [#269] +--echo # VIEW over UNION [tempesta-tech/mariadb#269] create or replace view vt1 as select * from t1 union select * from t1; select * from vt1; ---echo # VIEW over UNION with non-versioned [#393] +--echo # VIEW over UNION with non-versioned [tempesta-tech/mariadb#393] create or replace table t2 (a int); create or replace view vt1 as select * from t1 union select * from t2; select * from vt1; @@ -104,10 +104,10 @@ drop tables t1, t2; --echo # --echo # MDEV-15146 SQLError[4122]: View is not system versioned --echo # -create table t1 (a int) with system versioning; +create or replace table t1 (a int) with system versioning; insert t1 values (1),(2); set @a=now(6); -create view v1 as select * from t1; +create or replace view v1 as select * from t1; delete from t1; select * from v1; select * from v1 for system_time as of @a; @@ -124,4 +124,37 @@ show create view v1; drop view v1, vt1, vt12; drop tables t1, t3; +--echo # +--echo # MDEV-18727 improve DML operation of System Versioning +--echo # +--replace_result $sys_datatype_expl SYS_DATATYPE +eval create or replace table t1 ( + x int, + row_start $sys_datatype_expl as row start invisible, + row_end $sys_datatype_expl as row end invisible, + period for system_time (row_start, row_end) +) with system versioning; +insert into t1 values (1), (2); +create or replace view v1 as select * from t1 where x > 1; +update v1 set x= x + 1; +select *, check_row(row_start, row_end) from t1 for system_time all order by x; +insert v1 values (4); +select *, check_row(row_start, row_end) from t1 for system_time all order by x; +delete from v1 where x < 4; +select *, check_row(row_start, row_end) from t1 for system_time all order by x; +--echo # multi-update +create or replace table t2 like t1; +insert into t2 values (1), (2); +create or replace view v2 as select * from t2 where x > 1; +update v1, v2 set v1.x= v1.x + 1, v2.x= v2.x + 1 where v1.x = v2.x + 2; +select *, check_row(row_start, row_end) from t1 for system_time all order by x; +select *, check_row(row_start, row_end) from t2 for system_time all order by x; +--echo # multi-delete +delete v1, v2 from v1 join v2 where v1.x = v2.x + 2; +select *, check_row(row_start, row_end) from t1 for system_time all order by x; +select *, check_row(row_start, row_end) from t2 for system_time all order by x; + +drop view v1, v2; +drop tables t1, t2; + --source suite/versioning/common_finish.inc diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc index 9ca6408007e..8dd808065ce 100644 --- a/sql/ha_partition.cc +++ b/sql/ha_partition.cc @@ -4540,7 +4540,7 @@ int ha_partition::delete_row(const uchar *buf) or last historical partition, but DELETE HISTORY can delete from any historical partition. So, skip the check in this case. */ - if (!thd->lex->vers_conditions.is_set()) // if not DELETE HISTORY + if (!thd->lex->vers_conditions.delete_history) { uint32 part_id; error= get_part_for_buf(buf, m_rec0, m_part_info, &part_id); diff --git a/sql/mysqld.h b/sql/mysqld.h index ad19ce13db8..44db6c6216b 100644 --- a/sql/mysqld.h +++ b/sql/mysqld.h @@ -187,7 +187,8 @@ enum vers_system_time_t SYSTEM_TIME_AS_OF, SYSTEM_TIME_FROM_TO, SYSTEM_TIME_BETWEEN, - SYSTEM_TIME_BEFORE, + SYSTEM_TIME_BEFORE, // used for DELETE HISTORY ... BEFORE + SYSTEM_TIME_HISTORY, // used for DELETE HISTORY SYSTEM_TIME_ALL }; diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index c4bc32262e8..622b24292f3 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -225,19 +225,11 @@ bool Update_plan::save_explain_data_intern(MEM_ROOT *mem_root, static bool record_should_be_deleted(THD *thd, TABLE *table, SQL_SELECT *sel, Explain_delete *explain, bool truncate_history) { - bool check_delete= true; - - if (table->versioned()) - { - bool historical= !table->vers_end_field()->is_max(); - check_delete= truncate_history ? historical : !historical; - } - explain->tracker.on_record_read(); thd->inc_examined_row_count(1); if (table->vfield) (void) table->update_virtual_fields(table->file, VCOL_UPDATE_FOR_DELETE); - if (check_delete && (!sel || sel->skip_record(thd) > 0)) + if (!sel || sel->skip_record(thd) > 0) { explain->tracker.on_record_after_where(); return true; @@ -351,30 +343,8 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, THD_STAGE_INFO(thd, stage_init_update); - bool delete_history= table_list->vers_conditions.is_set(); - if (delete_history) - { - DBUG_ASSERT(!table_list->period_conditions.is_set()); - - if (table_list->is_view_or_derived()) - { - my_error(ER_IT_IS_A_VIEW, MYF(0), table_list->table_name.str); - DBUG_RETURN(true); - } - - DBUG_ASSERT(table_list->table); - DBUG_ASSERT(!conds || thd->stmt_arena->is_stmt_execute()); - - // conds could be cached from previous SP call - if (!conds) - { - if (select_lex->vers_setup_conds(thd, table_list)) - DBUG_RETURN(TRUE); - - conds= table_list->on_expr; - table_list->on_expr= NULL; - } - } + const bool delete_history= table_list->vers_conditions.delete_history; + DBUG_ASSERT(!(delete_history && table_list->period_conditions.is_set())); if (thd->lex->handle_list_of_derived(table_list, DT_MERGE_FOR_INSERT)) DBUG_RETURN(TRUE); @@ -1036,15 +1006,11 @@ int mysql_prepare_delete(THD *thd, TABLE_LIST *table_list, select_lex->leaf_tables, FALSE, DELETE_ACL, SELECT_ACL, TRUE)) DBUG_RETURN(TRUE); - if (table_list->vers_conditions.is_set()) + + if (table_list->vers_conditions.is_set() && table_list->is_view_or_derived()) { - if (table_list->is_view()) - { - my_error(ER_IT_IS_A_VIEW, MYF(0), table_list->table_name.str); - DBUG_RETURN(true); - } - if (select_lex->vers_setup_conds(thd, table_list)) - DBUG_RETURN(true); + my_error(ER_IT_IS_A_VIEW, MYF(0), table_list->table_name.str); + DBUG_RETURN(true); } if (table_list->has_period()) @@ -1055,11 +1021,19 @@ int mysql_prepare_delete(THD *thd, TABLE_LIST *table_list, DBUG_RETURN(true); } - *conds= select_lex->period_setup_conds(thd, table_list, *conds); - if (!*conds) + if (select_lex->period_setup_conds(thd, table_list)) DBUG_RETURN(true); } + DBUG_ASSERT(table_list->table); + // conds could be cached from previous SP call + DBUG_ASSERT(!table_list->vers_conditions.is_set() || + !*conds || thd->stmt_arena->is_stmt_execute()); + if (select_lex->vers_setup_conds(thd, table_list)) + DBUG_RETURN(TRUE); + + *conds= select_lex->where; + if ((wild_num && setup_wild(thd, table_list, field_list, NULL, wild_num, &select_lex->hidden_bit_fields)) || setup_fields(thd, Ref_ptr_array(), @@ -1356,11 +1330,6 @@ int multi_delete::send_data(List &values) if (table->status & (STATUS_NULL_ROW | STATUS_DELETED)) continue; - if (table->versioned() && !table->vers_end_field()->is_max()) - { - continue; - } - table->file->position(table->record[0]); found++; diff --git a/sql/sql_derived.cc b/sql/sql_derived.cc index 06e82263524..06a3e8a108f 100644 --- a/sql/sql_derived.cc +++ b/sql/sql_derived.cc @@ -723,7 +723,17 @@ bool mysql_derived_prepare(THD *thd, LEX *lex, TABLE_LIST *derived) !(derived->is_multitable() && (thd->lex->sql_command == SQLCOM_UPDATE_MULTI || thd->lex->sql_command == SQLCOM_DELETE_MULTI)))) + { + /* + System versioned tables may still require to get versioning conditions + (when updating view). See vers_setup_conds(). + */ + if (!unit->prepared && + derived->table->versioned() && + (res= unit->prepare(derived, derived->derived_result, 0))) + goto exit; DBUG_RETURN(FALSE); + } /* prevent name resolving out of derived table */ for (SELECT_LEX *sl= first_select; sl; sl= sl->next_select()) diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 159c05fb98f..9d22eb1a8cc 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -1291,7 +1291,7 @@ public: /* push new Item_field into item_list */ bool vers_push_field(THD *thd, TABLE_LIST *table, const LEX_CSTRING field_name); - Item* period_setup_conds(THD *thd, TABLE_LIST *table, Item *where); + int period_setup_conds(THD *thd, TABLE_LIST *table); void init_query(); void init_select(); st_select_lex_unit* master_unit() { return (st_select_lex_unit*) master; } diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 516813a0264..d89cae39330 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -4778,8 +4778,10 @@ mysql_execute_command(THD *thd) { result= new (thd->mem_root) multi_delete(thd, aux_tables, lex->table_count); - if (unlikely(result)) + if (likely(result)) { + if (unlikely(select_lex->vers_setup_conds(thd, aux_tables))) + goto multi_delete_error; res= mysql_select(thd, select_lex->get_table_list(), select_lex->with_wild, @@ -4801,6 +4803,7 @@ mysql_execute_command(THD *thd) if (lex->describe || lex->analyze_stmt) res= thd->lex->explain->send_explain(thd); } + multi_delete_error: delete result; } } @@ -9595,7 +9598,7 @@ bool update_precheck(THD *thd, TABLE_LIST *tables) bool delete_precheck(THD *thd, TABLE_LIST *tables) { DBUG_ENTER("delete_precheck"); - if (tables->vers_conditions.is_set()) + if (tables->vers_conditions.delete_history) { if (check_one_table_access(thd, DELETE_HISTORY_ACL, tables)) DBUG_RETURN(TRUE); diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 1ee2a175579..9022252c34c 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -715,6 +715,7 @@ bool vers_select_conds_t::init_from_sysvar(THD *thd) { vers_asof_timestamp_t &in= thd->variables.vers_asof_timestamp; type= (vers_system_time_t) in.type; + delete_history= false; start.unit= VERS_TIMESTAMP; if (type != SYSTEM_TIME_UNSPECIFIED && type != SYSTEM_TIME_ALL) { @@ -747,6 +748,7 @@ void vers_select_conds_t::print(String *str, enum_query_type query_type) const end.print(str, query_type, STRING_WITH_LEN(" AND ")); break; case SYSTEM_TIME_BEFORE: + case SYSTEM_TIME_HISTORY: DBUG_ASSERT(0); break; case SYSTEM_TIME_ALL: @@ -782,10 +784,15 @@ Item* period_get_condition(THD *thd, TABLE_LIST *table, SELECT_LEX *select, switch (conds->type) { case SYSTEM_TIME_UNSPECIFIED: + case SYSTEM_TIME_HISTORY: thd->variables.time_zone->gmt_sec_to_TIME(&max_time, TIMESTAMP_MAX_VALUE); max_time.second_part= TIME_MAX_SECOND_PART; curr= newx Item_datetime_literal(thd, &max_time, TIME_SECOND_PART_DIGITS); - cond1= newx Item_func_eq(thd, conds->field_end, curr); + if (conds->type == SYSTEM_TIME_UNSPECIFIED) + cond1= newx Item_func_eq(thd, conds->field_end, curr); + else + cond1= newx Item_func_lt(thd, conds->field_end, curr); + break; break; case SYSTEM_TIME_AS_OF: cond1= newx Item_func_le(thd, conds->field_start, conds->start.item); @@ -829,8 +836,13 @@ Item* period_get_condition(THD *thd, TABLE_LIST *table, SELECT_LEX *select, switch (conds->type) { case SYSTEM_TIME_UNSPECIFIED: + case SYSTEM_TIME_HISTORY: curr= newx Item_int(thd, ULONGLONG_MAX); - cond1= newx Item_func_eq(thd, conds->field_end, curr); + if (conds->type == SYSTEM_TIME_UNSPECIFIED) + cond1= newx Item_func_eq(thd, conds->field_end, curr); + else + cond1= newx Item_func_lt(thd, conds->field_end, curr); + break; DBUG_ASSERT(!conds->start.item); DBUG_ASSERT(!conds->end.item); break; @@ -873,12 +885,12 @@ bool skip_setup_conds(THD *thd) || thd->lex->is_view_context_analysis(); } -Item* SELECT_LEX::period_setup_conds(THD *thd, TABLE_LIST *tables, Item *where) +int SELECT_LEX::period_setup_conds(THD *thd, TABLE_LIST *tables) { DBUG_ENTER("SELECT_LEX::period_setup_conds"); if (skip_setup_conds(thd)) - DBUG_RETURN(where); + DBUG_RETURN(0); Query_arena backup; Query_arena *arena= thd->activate_stmt_arena_if_needed(&backup); @@ -896,19 +908,19 @@ Item* SELECT_LEX::period_setup_conds(THD *thd, TABLE_LIST *tables, Item *where) my_error(ER_PERIOD_NOT_FOUND, MYF(0), conds.name.str); if (arena) thd->restore_active_arena(arena, &backup); - DBUG_RETURN(NULL); + DBUG_RETURN(-1); } conds.period= &table->table->s->period; result= and_items(thd, result, period_get_condition(thd, table, this, &conds, true)); } - result= and_items(thd, where, result); + where= and_items(thd, where, result); if (arena) thd->restore_active_arena(arena, &backup); - DBUG_RETURN(result); + DBUG_RETURN(0); } int SELECT_LEX::vers_setup_conds(THD *thd, TABLE_LIST *tables) @@ -960,9 +972,22 @@ int SELECT_LEX::vers_setup_conds(THD *thd, TABLE_LIST *tables) } } + bool is_select= false; + switch (thd->lex->sql_command) + { + case SQLCOM_SELECT: + case SQLCOM_INSERT_SELECT: + case SQLCOM_REPLACE_SELECT: + case SQLCOM_DELETE_MULTI: + case SQLCOM_UPDATE_MULTI: + is_select= true; + default: + break; + } + for (TABLE_LIST *table= tables; table; table= table->next_local) { - if (!table->table || !table->table->versioned()) + if (!table->table || table->is_view() || !table->table->versioned()) continue; vers_select_conds_t &vers_conditions= table->vers_conditions; @@ -992,7 +1017,7 @@ int SELECT_LEX::vers_setup_conds(THD *thd, TABLE_LIST *tables) } // propagate system_time from sysvar - if (!vers_conditions.is_set()) + if (!vers_conditions.is_set() && is_select) { if (vers_conditions.init_from_sysvar(thd)) DBUG_RETURN(-1); @@ -1008,7 +1033,7 @@ int SELECT_LEX::vers_setup_conds(THD *thd, TABLE_LIST *tables) bool timestamps_only= table->table->versioned(VERS_TIMESTAMP); - if (vers_conditions.is_set()) + if (vers_conditions.is_set() && vers_conditions.type != SYSTEM_TIME_HISTORY) { thd->where= "FOR SYSTEM_TIME"; /* TODO: do resolve fix_length_and_dec(), fix_fields(). This requires @@ -1028,10 +1053,21 @@ int SELECT_LEX::vers_setup_conds(THD *thd, TABLE_LIST *tables) vers_conditions.period = &table->table->s->vers; Item *cond= period_get_condition(thd, table, this, &vers_conditions, timestamps_only); - if (cond) + if (is_select) table->on_expr= and_items(thd, table->on_expr, cond); - table->vers_conditions.type= SYSTEM_TIME_ALL; + else + { + if (join) + { + where= and_items(thd, join->conds, cond); + join->conds= where; + } + else + where= and_items(thd, where, cond); + table->where= and_items(thd, table->where, cond); + } + table->vers_conditions.type= SYSTEM_TIME_ALL; } // for (table= tables; ...) DBUG_RETURN(0); diff --git a/sql/sql_union.cc b/sql/sql_union.cc index da25fa774d0..5ff88e02f5d 100644 --- a/sql/sql_union.cc +++ b/sql/sql_union.cc @@ -977,9 +977,21 @@ bool st_select_lex_unit::prepare(TABLE_LIST *derived_arg, if (sl->tvc->prepare(thd, sl, tmp_result, this)) goto err; } - else if (prepare_join(thd, first_sl, tmp_result, additional_options, + else + { + if (prepare_join(thd, first_sl, tmp_result, additional_options, is_union_select)) - goto err; + goto err; + + if (derived_arg && derived_arg->table && + derived_arg->derived_type == VIEW_ALGORITHM_MERGE && + derived_arg->table->versioned()) + { + /* Got versioning conditions (see vers_setup_conds()), need to update + derived_arg. */ + derived_arg->where= first_sl->where; + } + } types= first_sl->item_list; goto cont; } diff --git a/sql/sql_update.cc b/sql/sql_update.cc index f5bb298fdba..590a4f8bae1 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -960,11 +960,6 @@ update_begin: THD_STAGE_INFO(thd, stage_updating); while (!(error=info.read_record()) && !thd->killed) { - if (table->versioned() && !table->vers_end_field()->is_max()) - { - continue; - } - explain->tracker.on_record_read(); thd->inc_examined_row_count(1); if (!select || select->skip_record(thd) > 0) @@ -1373,12 +1368,18 @@ bool mysql_prepare_update(THD *thd, TABLE_LIST *table_list, thd->lex->allow_sum_func.clear_all(); - if (table_list->has_period()) - { - *conds= select_lex->period_setup_conds(thd, table_list, *conds); - if (!*conds) + if (table_list->has_period() && + select_lex->period_setup_conds(thd, table_list)) DBUG_RETURN(true); - } + + DBUG_ASSERT(table_list->table); + // conds could be cached from previous SP call + DBUG_ASSERT(!table_list->vers_conditions.is_set() || + !*conds || thd->stmt_arena->is_stmt_execute()); + if (select_lex->vers_setup_conds(thd, table_list)) + DBUG_RETURN(TRUE); + + *conds= select_lex->where; /* We do not call DT_MERGE_FOR_INSERT because it has no sense for simple @@ -1900,6 +1901,9 @@ bool mysql_multi_update(THD *thd, TABLE_LIST *table_list, List *fields, thd->abort_on_warning= !ignore && thd->is_strict_mode(); List total_list; + if (select_lex->vers_setup_conds(thd, table_list)) + DBUG_RETURN(1); + res= mysql_select(thd, table_list, select_lex->with_wild, total_list, conds, select_lex->order_list.elements, @@ -2459,11 +2463,6 @@ int multi_update::send_data(List ¬_used_values) if (table->status & (STATUS_NULL_ROW | STATUS_UPDATED)) continue; - if (table->versioned() && !table->vers_end_field()->is_max()) - { - continue; - } - if (table == table_to_update) { /* diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 3fa2c85f160..476196f38ea 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -13866,7 +13866,7 @@ delete: opt_delete_system_time: /* empty */ { - Lex->vers_conditions.init(SYSTEM_TIME_ALL); + Lex->vers_conditions.init(SYSTEM_TIME_HISTORY); } | BEFORE_SYM SYSTEM_TIME_SYM history_point { diff --git a/sql/table.cc b/sql/table.cc index 0a7d2c9547a..055f02ef267 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -9511,6 +9511,8 @@ bool vers_select_conds_t::eq(const vers_select_conds_t &conds) const return true; case SYSTEM_TIME_BEFORE: break; + case SYSTEM_TIME_HISTORY: + break; case SYSTEM_TIME_AS_OF: return start.eq(conds.start); case SYSTEM_TIME_FROM_TO: diff --git a/sql/table.h b/sql/table.h index 700c5f036fd..ae46b192854 100644 --- a/sql/table.h +++ b/sql/table.h @@ -1930,6 +1930,7 @@ struct vers_select_conds_t { vers_system_time_t type; bool used:1; + bool delete_history:1; Vers_history_point start; Vers_history_point end; Lex_ident name; @@ -1943,6 +1944,7 @@ struct vers_select_conds_t { type= SYSTEM_TIME_UNSPECIFIED; used= false; + delete_history= false; start.empty(); end.empty(); } @@ -1954,6 +1956,8 @@ struct vers_select_conds_t { type= _type; used= false; + delete_history= (type == SYSTEM_TIME_HISTORY || + type == SYSTEM_TIME_BEFORE); start= _start; end= _end; name= _name; From f9ceb0a67ffb20631c936a7e8e8776c000d677ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Mon, 25 Nov 2019 15:22:21 +0200 Subject: [PATCH 9/9] MDEV-20190 Instant operation fails when add column and collation change on non-indexed column We must relax too strict debug assertions. For latin1_swedish_ci, mtype=DATA_CHAR or mtype=DATA_VARCHAR will be used instead of mtype=DATA_MYSQL or mtype=DATA_VARMYSQL. Likewise, some changes of dtype_get_charset_coll() do not affect the data type encoding, but only any indexes that are defined on the column. Charset::same_encoding(): Check whether two charset-collations have the same character set encoding. dict_col_t::same_encoding(): Check whether two character columns have the same character set encoding. dict_col_t::same_type(): Check whether two columns have a compatible data type encoding. dict_col_t::same_format(), dict_table_t::instant_column(): Do not compare mtype or the charset-collation of prtype directly. Rely on dict_col_t::same_type() instead. dtype_get_charset_coll(): Narrow the return type to uint16_t. This is a refined version of a fix that was developed by Thirunarayanan Balathandayuthapani. --- .../suite/innodb/r/instant_alter_bugs.result | 33 +++++++++++++ .../suite/innodb/t/instant_alter_bugs.test | 25 ++++++++++ sql/sql_string.h | 6 ++- storage/innobase/dict/dict0mem.cc | 9 ++++ storage/innobase/handler/handler0alter.cc | 3 +- storage/innobase/include/data0type.h | 17 +++---- storage/innobase/include/data0type.ic | 12 ----- storage/innobase/include/dict0mem.h | 47 ++++++++++++++++++- 8 files changed, 129 insertions(+), 23 deletions(-) diff --git a/mysql-test/suite/innodb/r/instant_alter_bugs.result b/mysql-test/suite/innodb/r/instant_alter_bugs.result index 19262246c9b..95efacf294b 100644 --- a/mysql-test/suite/innodb/r/instant_alter_bugs.result +++ b/mysql-test/suite/innodb/r/instant_alter_bugs.result @@ -324,4 +324,37 @@ InnoDB 0 transactions not purged SELECT * FROM t1; a DROP TABLE t1; +# +# MDEV-20190 Instant operation fails when add column and collation +# change on non-indexed column +# +CREATE TABLE t1 (a CHAR)ENGINE=INNODB; +ALTER TABLE t1 DEFAULT COLLATE= latin1_general_cs; +ALTER TABLE t1 ADD COLUMN b INT NOT NULL, MODIFY a CHAR, ALGORITHM=INSTANT; +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` char(1) COLLATE latin1_general_cs DEFAULT NULL, + `b` int(11) NOT NULL +) ENGINE=InnoDB DEFAULT CHARSET=latin1 COLLATE=latin1_general_cs +DROP TABLE t1; +CREATE TABLE t1 (a CHAR NOT NULL) ENGINE=InnoDB ROW_FORMAT=REDUNDANT; +ALTER TABLE t1 DEFAULT COLLATE = latin1_general_cs; +ALTER TABLE t1 MODIFY a CHAR, ALGORITHM=INSTANT; +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` char(1) COLLATE latin1_general_cs DEFAULT NULL +) ENGINE=InnoDB DEFAULT CHARSET=latin1 COLLATE=latin1_general_cs ROW_FORMAT=REDUNDANT +DROP TABLE t1; +CREATE TABLE t1 (a CHAR NOT NULL) CHARSET latin2 COLLATE latin2_bin +ENGINE=InnoDB ROW_FORMAT=REDUNDANT; +ALTER TABLE t1 DEFAULT COLLATE = latin2_general_ci; +ALTER TABLE t1 MODIFY a CHAR, ALGORITHM=INSTANT; +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` char(1) DEFAULT NULL +) ENGINE=InnoDB DEFAULT CHARSET=latin2 ROW_FORMAT=REDUNDANT +DROP TABLE t1; SET GLOBAL innodb_purge_rseg_truncate_frequency=@save_frequency; diff --git a/mysql-test/suite/innodb/t/instant_alter_bugs.test b/mysql-test/suite/innodb/t/instant_alter_bugs.test index 090a4aef787..ac93089e60e 100644 --- a/mysql-test/suite/innodb/t/instant_alter_bugs.test +++ b/mysql-test/suite/innodb/t/instant_alter_bugs.test @@ -348,4 +348,29 @@ ALTER TABLE t1 DROP b, DROP c, DROP d, DROP e; --source include/wait_all_purged.inc SELECT * FROM t1; DROP TABLE t1; + +--echo # +--echo # MDEV-20190 Instant operation fails when add column and collation +--echo # change on non-indexed column +--echo # + +CREATE TABLE t1 (a CHAR)ENGINE=INNODB; +ALTER TABLE t1 DEFAULT COLLATE= latin1_general_cs; +ALTER TABLE t1 ADD COLUMN b INT NOT NULL, MODIFY a CHAR, ALGORITHM=INSTANT; +SHOW CREATE TABLE t1; +DROP TABLE t1; + +CREATE TABLE t1 (a CHAR NOT NULL) ENGINE=InnoDB ROW_FORMAT=REDUNDANT; +ALTER TABLE t1 DEFAULT COLLATE = latin1_general_cs; +ALTER TABLE t1 MODIFY a CHAR, ALGORITHM=INSTANT; +SHOW CREATE TABLE t1; +DROP TABLE t1; + +CREATE TABLE t1 (a CHAR NOT NULL) CHARSET latin2 COLLATE latin2_bin +ENGINE=InnoDB ROW_FORMAT=REDUNDANT; +ALTER TABLE t1 DEFAULT COLLATE = latin2_general_ci; +ALTER TABLE t1 MODIFY a CHAR, ALGORITHM=INSTANT; +SHOW CREATE TABLE t1; +DROP TABLE t1; + SET GLOBAL innodb_purge_rseg_truncate_frequency=@save_frequency; diff --git a/sql/sql_string.h b/sql/sql_string.h index 605480461f1..aad5314a973 100644 --- a/sql/sql_string.h +++ b/sql/sql_string.h @@ -3,7 +3,7 @@ /* Copyright (c) 2000, 2013, Oracle and/or its affiliates. - Copyright (c) 2008, 2018, MariaDB Corporation. + Copyright (c) 2008, 2019, MariaDB Corporation. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -164,6 +164,10 @@ public: { swap_variables(CHARSET_INFO*, m_charset, other.m_charset); } + bool same_encoding(const Charset &other) const + { + return !strcmp(m_charset->csname, other.m_charset->csname); + } /* Collation name without the character set name. For example, in case of "latin1_swedish_ci", diff --git a/storage/innobase/dict/dict0mem.cc b/storage/innobase/dict/dict0mem.cc index 9e67f19e8b3..7d3f33d1ae2 100644 --- a/storage/innobase/dict/dict0mem.cc +++ b/storage/innobase/dict/dict0mem.cc @@ -37,6 +37,7 @@ Created 1/8/1996 Heikki Tuuri #include "lock0lock.h" #include "sync0sync.h" #include "row0row.h" +#include "sql_string.h" #include #define DICT_HEAP_SIZE 100 /*!< initial memory heap size when @@ -115,6 +116,14 @@ operator<<( return(s << ut_get_name(NULL, table_name.m_name)); } +bool dict_col_t::same_encoding(uint16_t a, uint16_t b) +{ + if (const CHARSET_INFO *acs= get_charset(a, MYF(MY_WME))) + if (const CHARSET_INFO *bcs= get_charset(b, MYF(MY_WME))) + return Charset(acs).same_encoding(bcs); + return false; +} + /**********************************************************************//** Creates a table memory object. @return own: table object */ diff --git a/storage/innobase/handler/handler0alter.cc b/storage/innobase/handler/handler0alter.cc index 7bb226b9a2e..d7b332e4dee 100644 --- a/storage/innobase/handler/handler0alter.cc +++ b/storage/innobase/handler/handler0alter.cc @@ -539,8 +539,9 @@ inline bool dict_table_t::instant_column(const dict_table_t& table, c.def_val = o->def_val; DBUG_ASSERT(!((c.prtype ^ o->prtype) & ~(DATA_NOT_NULL | DATA_VERSIONED + | CHAR_COLL_MASK << 16 | DATA_LONG_TRUE_VARCHAR))); - DBUG_ASSERT(c.mtype == o->mtype); + DBUG_ASSERT(c.same_type(*o)); DBUG_ASSERT(c.len >= o->len); if (o->vers_sys_start()) { diff --git a/storage/innobase/include/data0type.h b/storage/innobase/include/data0type.h index 7168c104b1c..0e496085113 100644 --- a/storage/innobase/include/data0type.h +++ b/storage/innobase/include/data0type.h @@ -334,14 +334,15 @@ dtype_get_mblen( multi-byte character */ ulint* mbmaxlen); /*!< out: maximum length of a multi-byte character */ -/*********************************************************************//** -Gets the MySQL charset-collation code for MySQL string types. -@return MySQL charset-collation code */ -UNIV_INLINE -ulint -dtype_get_charset_coll( -/*===================*/ - ulint prtype);/*!< in: precise data type */ +/** +Get the charset-collation code for string types. +@param prtype InnoDB precise type +@return charset-collation code */ +inline uint16_t dtype_get_charset_coll(ulint prtype) +{ + return static_cast(prtype >> 16) & CHAR_COLL_MASK; +} + /** Form a precise type from the < 4.1.2 format precise type plus the charset-collation code. @param[in] old_prtype MySQL type code and the flags diff --git a/storage/innobase/include/data0type.ic b/storage/innobase/include/data0type.ic index f2c499716ce..037a71a9345 100644 --- a/storage/innobase/include/data0type.ic +++ b/storage/innobase/include/data0type.ic @@ -27,18 +27,6 @@ Created 1/16/1996 Heikki Tuuri #include "mach0data.h" #include "ha_prototypes.h" -/*********************************************************************//** -Gets the MySQL charset-collation code for MySQL string types. -@return MySQL charset-collation code */ -UNIV_INLINE -ulint -dtype_get_charset_coll( -/*===================*/ - ulint prtype) /*!< in: precise data type */ -{ - return((prtype >> 16) & CHAR_COLL_MASK); -} - /*********************************************************************//** Determines if a MySQL string type is a subset of UTF-8. This function may return false negatives, in case further character-set collation diff --git a/storage/innobase/include/dict0mem.h b/storage/innobase/include/dict0mem.h index 5e1fa0033f7..6a0cb54c88f 100644 --- a/storage/innobase/include/dict0mem.h +++ b/storage/innobase/include/dict0mem.h @@ -674,18 +674,63 @@ public: def_val.data = NULL; } + /** @return whether two columns have compatible data type encoding */ + bool same_type(const dict_col_t &other) const + { + if (mtype != other.mtype) + { + /* For latin1_swedish_ci, DATA_CHAR and DATA_VARCHAR + will be used instead of DATA_MYSQL and DATA_VARMYSQL. + As long as mtype,prtype are being written to InnoDB + data dictionary tables, we cannot simplify this. */ + switch (mtype) { + default: + return false; + case DATA_VARCHAR: + if (other.mtype != DATA_VARMYSQL) + return false; + goto check_encoding; + case DATA_VARMYSQL: + if (other.mtype != DATA_VARCHAR) + return false; + goto check_encoding; + case DATA_CHAR: + if (other.mtype != DATA_MYSQL) + return false; + goto check_encoding; + case DATA_MYSQL: + if (other.mtype != DATA_CHAR) + return false; + goto check_encoding; + } + } + else if (dtype_is_string_type(mtype)) + { + check_encoding: + const uint16_t cset= dtype_get_charset_coll(prtype); + const uint16_t ocset= dtype_get_charset_coll(other.prtype); + return cset == ocset || dict_col_t::same_encoding(cset, ocset); + } + + return true; + } + + /** @return whether two collations codes have the same character encoding */ + static bool same_encoding(uint16_t a, uint16_t b); + /** Determine if the columns have the same format except for is_nullable() and is_versioned(). @param[in] other column to compare to @return whether the columns have the same format */ bool same_format(const dict_col_t& other) const { - return mtype == other.mtype + return same_type(other) && len >= other.len && mbminlen == other.mbminlen && mbmaxlen == other.mbmaxlen && !((prtype ^ other.prtype) & ~(DATA_NOT_NULL | DATA_VERSIONED + | CHAR_COLL_MASK << 16 | DATA_LONG_TRUE_VARCHAR)); } };