diff --git a/cmake/cpack_rpm.cmake b/cmake/cpack_rpm.cmake index 4811eec8f8c..701f913dee2 100644 --- a/cmake/cpack_rpm.cmake +++ b/cmake/cpack_rpm.cmake @@ -297,7 +297,7 @@ ELSEIF(RPM MATCHES "sles") ENDIF() # MDEV-24629, we need it outside of ELSIFs -IF(RPM MATCHES "fedora3[234]") +IF(RPM MATCHES "fedora") ALTERNATIVE_NAME("common" "mariadb-connector-c-config" ${MARIADB_CONNECTOR_C_VERSION}-1) ENDIF() diff --git a/extra/mariabackup/xtrabackup.cc b/extra/mariabackup/xtrabackup.cc index 180616f37e9..515a8bd49c6 100644 --- a/extra/mariabackup/xtrabackup.cc +++ b/extra/mariabackup/xtrabackup.cc @@ -847,27 +847,49 @@ void mdl_lock_all() // Convert non-null terminated filename to space name +// Note that in 10.6 the filename may be an undo file name static std::string filename_to_spacename(const void *filename, size_t len) { - // null- terminate filename - char *f = (char *)malloc(len + 1); - ut_a(f); - memcpy(f, filename, len); - f[len] = 0; - for (size_t i = 0; i < len; i++) - if (f[i] == '\\') - f[i] = '/'; - char *p = strrchr(f, '.'); - ut_a(p); - *p = 0; - char *table = strrchr(f, '/'); - ut_a(table); - *table = 0; - char *db = strrchr(f, '/'); - *table = '/'; - std::string s(db ? db+1 : f); - free(f); - return s; + char f[FN_REFLEN]; + char *p= 0, *table, *db; + DBUG_ASSERT(len < FN_REFLEN); + + strmake(f, (const char*) filename, len); + +#ifdef _WIN32 + for (size_t i = 0; i < len; i++) + { + if (f[i] == '\\') + f[i] = '/'; + } +#endif + + /* Remove extension, if exists */ + if (!(p= strrchr(f, '.'))) + goto err; + *p= 0; + + /* Find table name */ + if (!(table= strrchr(f, '/'))) + goto err; + *table = 0; + + /* Find database name */ + db= strrchr(f, '/'); + *table = '/'; + if (!db) + goto err; + { + std::string s(db+1); + return s; + } + +err: + /* Not a database/table. Return original (converted) name */ + if (p) + *p= '.'; // Restore removed extension + std::string s(f); + return s; } /** Report an operation to create, delete, or rename a file during backup. diff --git a/include/m_string.h b/include/m_string.h index 046dc39d13a..6a645b20a7f 100644 --- a/include/m_string.h +++ b/include/m_string.h @@ -249,14 +249,15 @@ static inline void lex_string_set3(LEX_CSTRING *lex_str, const char *c_str, */ static inline int safe_strcpy(char *dst, size_t dst_size, const char *src) { - memset(dst, '\0', dst_size); - strncpy(dst, src, dst_size - 1); - /* - If the first condition is true, we are guaranteed to have src length - >= (dst_size - 1), hence safe to access src[dst_size - 1]. - */ - if (dst[dst_size - 2] != '\0' && src[dst_size - 1] != '\0') - return 1; /* Truncation of src. */ + DBUG_ASSERT(dst_size > 0); + /* Note, strncpy will zerofill end of dst if src shorter than dst_size */ + strncpy(dst, src, dst_size); + if (dst[dst_size-1]) + { + /* Ensure string is zero terminated */ + dst[dst_size-1]= 0; + return 1; + } return 0; } diff --git a/include/myisammrg.h b/include/myisammrg.h index 1d7efbe74d6..b3bca218a44 100644 --- a/include/myisammrg.h +++ b/include/myisammrg.h @@ -71,6 +71,7 @@ typedef struct st_myrg_info ulong cache_size; uint merge_insert_method; uint tables,options,reclength,keys; + uint key_parts; my_bool cache_in_use; /* If MERGE children attached to parent. See top comment in ha_myisammrg.cc */ my_bool children_attached; diff --git a/mysql-test/lib/My/File/Path.pm b/mysql-test/lib/My/File/Path.pm index d60027c909e..fd3cf6dd61c 100644 --- a/mysql-test/lib/My/File/Path.pm +++ b/mysql-test/lib/My/File/Path.pm @@ -34,7 +34,7 @@ use strict; use Exporter; use base "Exporter"; -our @EXPORT= qw /rmtree mkpath copytree/; +our @EXPORT= qw /rmtree mkpath copytree make_readonly/; use File::Find; use File::Copy; @@ -184,6 +184,10 @@ sub copytree { # Only copy plain files next unless -f "$from_dir/$_"; copy("$from_dir/$_", "$to_dir/$_"); + if (!$use_umask) + { + chmod(0666, "$to_dir/$_"); + } } closedir(DIR); @@ -193,4 +197,29 @@ sub copytree { } } + +sub make_readonly { + my ($dir) = @_; + + die "Usage: make_readonly(])" + unless @_ == 1; + + opendir(DIR, "$dir") + or croak("Can't find $dir$!"); + for(readdir(DIR)) { + + next if "$_" eq "." or "$_" eq ".."; + + if ( -d "$dir/$_" ) + { + make_readonly("$dir/$_"); + next; + } + + # Only copy plain files + next unless -f "$dir/$_"; + chmod 0444, "$dir/$_"; + } + closedir(DIR); +} 1; diff --git a/mysql-test/lib/My/SafeProcess/Base.pm b/mysql-test/lib/My/SafeProcess/Base.pm index 818e6e34e11..1cd01cb0ca9 100644 --- a/mysql-test/lib/My/SafeProcess/Base.pm +++ b/mysql-test/lib/My/SafeProcess/Base.pm @@ -40,7 +40,7 @@ our @EXPORT= qw(create_process); # Retry a couple of times if fork returns EAGAIN # sub _safe_fork { - my $retries= 5; + my $retries= 100; my $pid; FORK: diff --git a/mysql-test/main/distinct.result b/mysql-test/main/distinct.result index fa9f0259a0f..ac693421ba2 100644 --- a/mysql-test/main/distinct.result +++ b/mysql-test/main/distinct.result @@ -1157,3 +1157,28 @@ explain select * from t1 limit 0 offset 10; id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE NULL NULL NULL NULL NULL NULL NULL Zero limit drop table t1, t2; +# +# MDEV-28285 Unexpected result when combining DISTINCT, subselect +# and LIMIT +# +create table t1 (a int primary key); +create table t2 (a int primary key, b int not null); +insert into t1 select seq from seq_1_to_10; +insert into t2 select seq,seq from seq_1_to_10; +select distinct a from t1 where t1.a=1 and t1.a in (select a from t2 where t2.b in (1,2)); +a +1 +explain select distinct a from t1 where t1.a=1 and t1.a in (select a+0 from t2 where t2.b in (1,2)) limit 10,10; +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t1 const PRIMARY PRIMARY 4 const 1 Using index; Using temporary +1 PRIMARY eq_ref distinct_key distinct_key 8 func 1 +2 MATERIALIZED t2 ALL NULL NULL NULL NULL 10 Using where +select distinct a from t1 where t1.a=1 and t1.a in (select a+0 from t2 where t2.b in (1,2)) limit 10,10; +a +select distinct a from t1 where t1.a=1 and t1.a in (select a+0 from t2 where t2.b in (1,2)) limit 0,1; +a +1 +drop table t1,t2; +# +# end of 10.5 tests +# diff --git a/mysql-test/main/distinct.test b/mysql-test/main/distinct.test index 893e2dcc9a7..9aa3b2921aa 100644 --- a/mysql-test/main/distinct.test +++ b/mysql-test/main/distinct.test @@ -892,3 +892,24 @@ explain select * from t1 limit 0; explain select * from t1 limit 0 offset 10; drop table t1, t2; + +--echo # +--echo # MDEV-28285 Unexpected result when combining DISTINCT, subselect +--echo # and LIMIT +--echo # + +create table t1 (a int primary key); +create table t2 (a int primary key, b int not null); + +insert into t1 select seq from seq_1_to_10; +insert into t2 select seq,seq from seq_1_to_10; + +select distinct a from t1 where t1.a=1 and t1.a in (select a from t2 where t2.b in (1,2)); +explain select distinct a from t1 where t1.a=1 and t1.a in (select a+0 from t2 where t2.b in (1,2)) limit 10,10; +select distinct a from t1 where t1.a=1 and t1.a in (select a+0 from t2 where t2.b in (1,2)) limit 10,10; +select distinct a from t1 where t1.a=1 and t1.a in (select a+0 from t2 where t2.b in (1,2)) limit 0,1; +drop table t1,t2; + +--echo # +--echo # end of 10.5 tests +--echo # diff --git a/mysql-test/main/group_min_max.result b/mysql-test/main/group_min_max.result index 706a4132614..055bd266e15 100644 --- a/mysql-test/main/group_min_max.result +++ b/mysql-test/main/group_min_max.result @@ -4095,6 +4095,116 @@ MIN(pk) a 5 10 DROP TABLE t1; # +# MDEV-6768 Wrong result with agregate with join with no resultset +# +create table t1 +( +PARENT_ID int(10) unsigned NOT NULL AUTO_INCREMENT, +PARENT_FIELD VARCHAR(10), +PRIMARY KEY (PARENT_ID) +) engine=innodb; +create table t2 +( +CHILD_ID INT NOT NULL AUTO_INCREMENT, +PARENT_ID INT NOT NULL, +CHILD_FIELD varchar(10), +PRIMARY KEY (CHILD_ID) +)engine=innodb; +INSERT INTO t1 (PARENT_FIELD) +SELECT 'AAAA'; +INSERT INTO t2 (PARENT_ID, CHILD_FIELD) +SELECT 1, 'BBBB'; +explain select +t1.PARENT_ID, +min(CHILD_FIELD) +from t1 straight_join t2 +where t1.PARENT_ID = 1 +and t1.PARENT_ID = t2.PARENT_ID +and t2.CHILD_FIELD = "ZZZZ"; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 const PRIMARY PRIMARY 4 const 1 Using index +1 SIMPLE t2 ALL NULL NULL NULL NULL 1 Using where +select +t1.PARENT_ID, +min(CHILD_FIELD) +from t1 straight_join t2 +where t1.PARENT_ID = 1 +and t1.PARENT_ID = t2.PARENT_ID +and t2.CHILD_FIELD = "ZZZZ"; +PARENT_ID min(CHILD_FIELD) +NULL NULL +select +1, +min(CHILD_FIELD) +from t1 straight_join t2 +where t1.PARENT_ID = 1 +and t1.PARENT_ID = t2.PARENT_ID +and t2.CHILD_FIELD = "ZZZZ"; +1 min(CHILD_FIELD) +1 NULL +select +IFNULL(t1.PARENT_ID,1), +min(CHILD_FIELD) +from t1 straight_join t2 +where t1.PARENT_ID = 1 +and t1.PARENT_ID = t2.PARENT_ID +and t2.CHILD_FIELD = "ZZZZ"; +IFNULL(t1.PARENT_ID,1) min(CHILD_FIELD) +1 NULL +# Check that things works with MyISAM (which has different explain) +alter table t1 engine=myisam; +alter table t2 engine=myisam; +explain select +t1.PARENT_ID, +min(CHILD_FIELD) +from t1 straight_join t2 +where t1.PARENT_ID = 1 +and t1.PARENT_ID = t2.PARENT_ID +and t2.CHILD_FIELD = "ZZZZ"; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL Impossible WHERE noticed after reading const tables +select +t1.PARENT_ID, +min(CHILD_FIELD) +from t1 straight_join t2 +where t1.PARENT_ID = 1 +and t1.PARENT_ID = t2.PARENT_ID +and t2.CHILD_FIELD = "ZZZZ"; +PARENT_ID min(CHILD_FIELD) +NULL NULL +drop table t1,t2; +# Check that things works if sub queries are re-executed +create table t1 (a int primary key, b int); +create table t2 (a int primary key, b int); +create table t3 (a int primary key, b int); +insert into t1 values (1,1),(2,2),(3,3); +insert into t2 values (1,1),(2,2),(3,3); +insert into t3 values (1,1),(3,3); +explain +select *, +(select +CONCAT('t2:', IFNULL(t2.a, 't2a-null'), ';', +'min_t3_b:', IFNULL(min(t3.b), 't3b-null')) +from t2,t3 +where t2.a=1 and t1.b = t3.a) as s1 +from t1; +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t1 ALL NULL NULL NULL NULL 3 +2 DEPENDENT SUBQUERY t2 const PRIMARY PRIMARY 4 const 1 Using index +2 DEPENDENT SUBQUERY t3 eq_ref PRIMARY PRIMARY 4 test.t1.b 1 +select *, +(select +CONCAT('t2:', IFNULL(t2.a, 't2a-null'), ';', +'min_t3_b:', IFNULL(min(t3.b), 't3b-null')) +from t2,t3 +where t2.a=1 and t1.b = t3.a) as s1 +from t1; +a b s1 +1 1 t2:1;min_t3_b:1 +2 2 t2:t2a-null;min_t3_b:t3b-null +3 3 t2:1;min_t3_b:3 +drop table t1,t2,t3; +# # End of 10.5 tests # # diff --git a/mysql-test/main/group_min_max.test b/mysql-test/main/group_min_max.test index 1bc334dd3da..9d056473537 100644 --- a/mysql-test/main/group_min_max.test +++ b/mysql-test/main/group_min_max.test @@ -1749,6 +1749,116 @@ SELECT MIN(pk), a FROM t1 WHERE pk <> 1 GROUP BY a; DROP TABLE t1; +--echo # +--echo # MDEV-6768 Wrong result with agregate with join with no resultset +--echo # + +create table t1 +( + PARENT_ID int(10) unsigned NOT NULL AUTO_INCREMENT, + PARENT_FIELD VARCHAR(10), + PRIMARY KEY (PARENT_ID) +) engine=innodb; + +create table t2 +( + CHILD_ID INT NOT NULL AUTO_INCREMENT, + PARENT_ID INT NOT NULL, + CHILD_FIELD varchar(10), + PRIMARY KEY (CHILD_ID) +)engine=innodb; + +INSERT INTO t1 (PARENT_FIELD) +SELECT 'AAAA'; + +INSERT INTO t2 (PARENT_ID, CHILD_FIELD) +SELECT 1, 'BBBB'; + +explain select + t1.PARENT_ID, + min(CHILD_FIELD) + from t1 straight_join t2 + where t1.PARENT_ID = 1 + and t1.PARENT_ID = t2.PARENT_ID + and t2.CHILD_FIELD = "ZZZZ"; + +select + t1.PARENT_ID, + min(CHILD_FIELD) + from t1 straight_join t2 + where t1.PARENT_ID = 1 + and t1.PARENT_ID = t2.PARENT_ID + and t2.CHILD_FIELD = "ZZZZ"; + +select + 1, + min(CHILD_FIELD) + from t1 straight_join t2 + where t1.PARENT_ID = 1 + and t1.PARENT_ID = t2.PARENT_ID + and t2.CHILD_FIELD = "ZZZZ"; + +select + IFNULL(t1.PARENT_ID,1), + min(CHILD_FIELD) + from t1 straight_join t2 + where t1.PARENT_ID = 1 + and t1.PARENT_ID = t2.PARENT_ID + and t2.CHILD_FIELD = "ZZZZ"; + + +--echo # Check that things works with MyISAM (which has different explain) + +alter table t1 engine=myisam; +alter table t2 engine=myisam; + +explain select + t1.PARENT_ID, + min(CHILD_FIELD) + from t1 straight_join t2 + where t1.PARENT_ID = 1 + and t1.PARENT_ID = t2.PARENT_ID + and t2.CHILD_FIELD = "ZZZZ"; + +select + t1.PARENT_ID, + min(CHILD_FIELD) + from t1 straight_join t2 + where t1.PARENT_ID = 1 + and t1.PARENT_ID = t2.PARENT_ID + and t2.CHILD_FIELD = "ZZZZ"; + +drop table t1,t2; + +--echo # Check that things works if sub queries are re-executed + +create table t1 (a int primary key, b int); +create table t2 (a int primary key, b int); +create table t3 (a int primary key, b int); + +insert into t1 values (1,1),(2,2),(3,3); +insert into t2 values (1,1),(2,2),(3,3); +insert into t3 values (1,1),(3,3); + +explain +select *, + (select + CONCAT('t2:', IFNULL(t2.a, 't2a-null'), ';', + 'min_t3_b:', IFNULL(min(t3.b), 't3b-null')) + from t2,t3 + where t2.a=1 and t1.b = t3.a) as s1 +from t1; + +select *, + (select + CONCAT('t2:', IFNULL(t2.a, 't2a-null'), ';', + 'min_t3_b:', IFNULL(min(t3.b), 't3b-null')) + from t2,t3 + where t2.a=1 and t1.b = t3.a) as s1 +from t1; + +drop table t1,t2,t3; + --echo # --echo # End of 10.5 tests --echo # diff --git a/mysql-test/main/merge.result b/mysql-test/main/merge.result index 230fcf48e9d..e7330b7e54b 100644 --- a/mysql-test/main/merge.result +++ b/mysql-test/main/merge.result @@ -3902,6 +3902,18 @@ DROP TABLE t1; DROP TABLE m1; set global default_storage_engine=@save_default_storage_engine; # +# MDEV-31083 ASAN use-after-poison in myrg_attach_children +# +CREATE TABLE t1 (f TEXT, FULLTEXT (f)) ENGINE=MyISAM; +INSERT INTO t1 VALUES ('foo'),('bar'); +CREATE TABLE mrg (f TEXT) ENGINE=MERGE, UNION(t1); +SELECT * FROM mrg; +f +foo +bar +DROP TABLE mrg, t1; +End of 10.5 tests +# # End of 10.0 tests # # diff --git a/mysql-test/main/merge.test b/mysql-test/main/merge.test index 0485f3ed1c3..76903e27ae9 100644 --- a/mysql-test/main/merge.test +++ b/mysql-test/main/merge.test @@ -2859,6 +2859,18 @@ set global default_storage_engine=@save_default_storage_engine; # gone so execution of other tests won't be affected by their presence. --source include/wait_until_count_sessions.inc +--echo # +--echo # MDEV-31083 ASAN use-after-poison in myrg_attach_children +--echo # + +CREATE TABLE t1 (f TEXT, FULLTEXT (f)) ENGINE=MyISAM; +INSERT INTO t1 VALUES ('foo'),('bar'); +CREATE TABLE mrg (f TEXT) ENGINE=MERGE, UNION(t1); +SELECT * FROM mrg; +DROP TABLE mrg, t1; + +--echo End of 10.5 tests + --echo # --echo # End of 10.0 tests --echo # diff --git a/mysql-test/main/selectivity.result b/mysql-test/main/selectivity.result index bbe7948b1b3..3b2aabaffd9 100644 --- a/mysql-test/main/selectivity.result +++ b/mysql-test/main/selectivity.result @@ -1824,7 +1824,6 @@ test.t1 analyze status Table is already up to date test.t2 analyze status Engine-independent statistics collected test.t2 analyze status Table is already up to date set optimizer_switch='exists_to_in=off'; -set optimizer_use_condition_selectivity=2; SELECT * FROM t1 WHERE EXISTS (SELECT * FROM t1 A INNER JOIN t2 ON t2.a = A.id @@ -1849,18 +1848,39 @@ id a 17 17 18 18 19 19 -explain SELECT * FROM t1 +set statement optimizer_use_condition_selectivity=2 for explain SELECT * FROM t1 WHERE EXISTS (SELECT * FROM t1 A INNER JOIN t2 ON t2.a = A.id WHERE A.a=t1.a AND t2.b < 20); id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY t1 ALL NULL NULL NULL NULL 100 Using where -2 DEPENDENT SUBQUERY A ref PRIMARY,a a 5 test.t1.a 1 -2 DEPENDENT SUBQUERY t2 ref|filter a,b a|b 5|5 test.A.id 1 (10%) Using where; Using rowid filter -EXPLAIN SELECT * FROM t1 A, t1 B WHERE A.a = B.a and A.id = 65; +3 DEPENDENT SUBQUERY A ref PRIMARY,a a 5 test.t1.a 1 +3 DEPENDENT SUBQUERY t2 ref|filter a,b a|b 5|5 test.A.id 1 (10%) Using where; Using rowid filter +set statement optimizer_use_condition_selectivity=4 for explain SELECT * FROM t1 +WHERE +EXISTS (SELECT * FROM t1 A INNER JOIN t2 ON t2.a = A.id +WHERE A.a=t1.a AND t2.b < 20); id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE A const PRIMARY,a PRIMARY 4 const 1 -1 SIMPLE B ref a a 5 const 1 +1 PRIMARY t1 ALL NULL NULL NULL NULL 100 Using where +3 DEPENDENT SUBQUERY A ref PRIMARY,a a 5 test.t1.a 1 +3 DEPENDENT SUBQUERY t2 ref|filter a,b a|b 5|5 test.A.id 1 (10%) Using where; Using rowid filter +set @query="EXPLAIN SELECT * FROM t1 A, t1 B WHERE A.a = B.a and A.id = 65"; +set statement optimizer_use_condition_selectivity=2 for explain SELECT * FROM t1 +WHERE +EXISTS (SELECT * FROM t1 A INNER JOIN t2 ON t2.a = A.id +WHERE A.a=t1.a AND t2.b < 20); +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t1 ALL NULL NULL NULL NULL 100 Using where +3 DEPENDENT SUBQUERY A ref PRIMARY,a a 5 test.t1.a 1 +3 DEPENDENT SUBQUERY t2 ref|filter a,b a|b 5|5 test.A.id 1 (10%) Using where; Using rowid filter +set statement optimizer_use_condition_selectivity=4 for explain SELECT * FROM t1 +WHERE +EXISTS (SELECT * FROM t1 A INNER JOIN t2 ON t2.a = A.id +WHERE A.a=t1.a AND t2.b < 20); +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t1 ALL NULL NULL NULL NULL 100 Using where +3 DEPENDENT SUBQUERY A ref PRIMARY,a a 5 test.t1.a 1 +3 DEPENDENT SUBQUERY t2 ref|filter a,b a|b 5|5 test.A.id 1 (10%) Using where; Using rowid filter explain SELECT * FROM t1 WHERE EXISTS (SELECT * FROM t1 A INNER JOIN t2 ON t2.a = A.id @@ -1870,7 +1890,6 @@ id select_type table type possible_keys key key_len ref rows Extra 2 DEPENDENT SUBQUERY A ref PRIMARY,a a 5 test.t1.a 1 2 DEPENDENT SUBQUERY t2 ref|filter a,b a|b 5|5 test.A.id 1 (10%) Using where; Using rowid filter set optimizer_switch= @save_optimizer_switch; -set optimizer_use_condition_selectivity= @save_optimizer_use_condition_selectivity; drop table t1,t2; # # MDEV-21495: Conditional jump or move depends on uninitialised value in sel_arg_range_seq_next diff --git a/mysql-test/main/selectivity.test b/mysql-test/main/selectivity.test index df3850d74b7..9f21bea442a 100644 --- a/mysql-test/main/selectivity.test +++ b/mysql-test/main/selectivity.test @@ -1236,13 +1236,10 @@ set optimizer_use_condition_selectivity= @@optimizer_use_condition_selectivity; drop table t1,t2,t3; - --echo # --echo # MDEV-20519: Query plan regression with optimizer_use_condition_selectivity=4 --echo # - - create table t1 (id int, a int, PRIMARY KEY(id), key(a)); insert into t1 select seq,seq from seq_1_to_100; @@ -1252,7 +1249,6 @@ insert into t2 select seq,seq,seq from seq_1_to_100; analyze table t1,t2 persistent for all; set optimizer_switch='exists_to_in=off'; -set optimizer_use_condition_selectivity=2; let $query= SELECT * FROM t1 WHERE @@ -1260,14 +1256,16 @@ let $query= SELECT * FROM t1 WHERE A.a=t1.a AND t2.b < 20); eval $query; -eval explain $query; +eval set statement optimizer_use_condition_selectivity=2 for explain $query; +eval set statement optimizer_use_condition_selectivity=4 for explain $query; -EXPLAIN SELECT * FROM t1 A, t1 B WHERE A.a = B.a and A.id = 65; +set @query="EXPLAIN SELECT * FROM t1 A, t1 B WHERE A.a = B.a and A.id = 65"; +eval set statement optimizer_use_condition_selectivity=2 for explain $query; +eval set statement optimizer_use_condition_selectivity=4 for explain $query; eval explain $query; set optimizer_switch= @save_optimizer_switch; -set optimizer_use_condition_selectivity= @save_optimizer_use_condition_selectivity; drop table t1,t2; --echo # diff --git a/mysql-test/main/selectivity_innodb.result b/mysql-test/main/selectivity_innodb.result index f35d8b66f8f..cdb609793c8 100644 --- a/mysql-test/main/selectivity_innodb.result +++ b/mysql-test/main/selectivity_innodb.result @@ -1836,7 +1836,6 @@ test.t1 analyze status OK test.t2 analyze status Engine-independent statistics collected test.t2 analyze status OK set optimizer_switch='exists_to_in=off'; -set optimizer_use_condition_selectivity=2; SELECT * FROM t1 WHERE EXISTS (SELECT * FROM t1 A INNER JOIN t2 ON t2.a = A.id @@ -1861,18 +1860,39 @@ id a 17 17 18 18 19 19 -explain SELECT * FROM t1 +set statement optimizer_use_condition_selectivity=2 for explain SELECT * FROM t1 WHERE EXISTS (SELECT * FROM t1 A INNER JOIN t2 ON t2.a = A.id WHERE A.a=t1.a AND t2.b < 20); id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY t1 index NULL a 5 NULL 100 Using where; Using index -2 DEPENDENT SUBQUERY A ref PRIMARY,a a 5 test.t1.a 1 Using index -2 DEPENDENT SUBQUERY t2 ref|filter a,b a|b 5|5 test.A.id 1 (19%) Using where; Using rowid filter -EXPLAIN SELECT * FROM t1 A, t1 B WHERE A.a = B.a and A.id = 65; +3 DEPENDENT SUBQUERY A ref PRIMARY,a a 5 test.t1.a 1 Using index +3 DEPENDENT SUBQUERY t2 ref|filter a,b a|b 5|5 test.A.id 1 (19%) Using where; Using rowid filter +set statement optimizer_use_condition_selectivity=4 for explain SELECT * FROM t1 +WHERE +EXISTS (SELECT * FROM t1 A INNER JOIN t2 ON t2.a = A.id +WHERE A.a=t1.a AND t2.b < 20); id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE A const PRIMARY,a PRIMARY 4 const 1 -1 SIMPLE B ref a a 5 const 1 Using index +1 PRIMARY t1 index NULL a 5 NULL 100 Using where; Using index +3 DEPENDENT SUBQUERY A ref PRIMARY,a a 5 test.t1.a 1 Using index +3 DEPENDENT SUBQUERY t2 ref|filter a,b a|b 5|5 test.A.id 1 (19%) Using where; Using rowid filter +set @query="EXPLAIN SELECT * FROM t1 A, t1 B WHERE A.a = B.a and A.id = 65"; +set statement optimizer_use_condition_selectivity=2 for explain SELECT * FROM t1 +WHERE +EXISTS (SELECT * FROM t1 A INNER JOIN t2 ON t2.a = A.id +WHERE A.a=t1.a AND t2.b < 20); +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t1 index NULL a 5 NULL 100 Using where; Using index +3 DEPENDENT SUBQUERY A ref PRIMARY,a a 5 test.t1.a 1 Using index +3 DEPENDENT SUBQUERY t2 ref|filter a,b a|b 5|5 test.A.id 1 (19%) Using where; Using rowid filter +set statement optimizer_use_condition_selectivity=4 for explain SELECT * FROM t1 +WHERE +EXISTS (SELECT * FROM t1 A INNER JOIN t2 ON t2.a = A.id +WHERE A.a=t1.a AND t2.b < 20); +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t1 index NULL a 5 NULL 100 Using where; Using index +3 DEPENDENT SUBQUERY A ref PRIMARY,a a 5 test.t1.a 1 Using index +3 DEPENDENT SUBQUERY t2 ref|filter a,b a|b 5|5 test.A.id 1 (19%) Using where; Using rowid filter explain SELECT * FROM t1 WHERE EXISTS (SELECT * FROM t1 A INNER JOIN t2 ON t2.a = A.id @@ -1882,7 +1902,6 @@ id select_type table type possible_keys key key_len ref rows Extra 2 DEPENDENT SUBQUERY A ref PRIMARY,a a 5 test.t1.a 1 Using index 2 DEPENDENT SUBQUERY t2 ref|filter a,b a|b 5|5 test.A.id 1 (19%) Using where; Using rowid filter set optimizer_switch= @save_optimizer_switch; -set optimizer_use_condition_selectivity= @save_optimizer_use_condition_selectivity; drop table t1,t2; # # MDEV-21495: Conditional jump or move depends on uninitialised value in sel_arg_range_seq_next diff --git a/mysql-test/main/type_timestamp.result b/mysql-test/main/type_timestamp.result index 275d35ac476..0d54875df46 100644 --- a/mysql-test/main/type_timestamp.result +++ b/mysql-test/main/type_timestamp.result @@ -1230,6 +1230,8 @@ SELECT * FROM t1 HAVING MIN(t1.c1) >= ALL(SELECT 'a' UNION SELECT 'r'); c1 Warnings: Warning 1292 Truncated incorrect datetime value: 'r' +SELECT * FROM t1 HAVING MIN(t1.c1) > 0; +c1 DROP TABLE t1; CREATE TABLE t1 (c1 timestamp); INSERT INTO t1 VALUES ('2010-01-01 00:00:00'); diff --git a/mysql-test/main/type_timestamp.test b/mysql-test/main/type_timestamp.test index bff33c030b5..14e865df918 100644 --- a/mysql-test/main/type_timestamp.test +++ b/mysql-test/main/type_timestamp.test @@ -810,6 +810,7 @@ DROP TABLE t1; CREATE TABLE t1 (c1 timestamp); SELECT MIN(t1.c1) AS k1 FROM t1 HAVING (k1 >= ALL(SELECT 'a' UNION SELECT 'r')); SELECT * FROM t1 HAVING MIN(t1.c1) >= ALL(SELECT 'a' UNION SELECT 'r'); +SELECT * FROM t1 HAVING MIN(t1.c1) > 0; DROP TABLE t1; CREATE TABLE t1 (c1 timestamp); diff --git a/mysql-test/mariadb-test-run.pl b/mysql-test/mariadb-test-run.pl index 4fc4d9f050e..9d554998953 100755 --- a/mysql-test/mariadb-test-run.pl +++ b/mysql-test/mariadb-test-run.pl @@ -408,8 +408,11 @@ sub main { mark_time_used('collect'); - mysql_install_db(default_mysqld(), "$opt_vardir/install.db") unless using_extern(); - + if (!using_extern()) + { + mysql_install_db(default_mysqld(), "$opt_vardir/install.db"); + make_readonly("$opt_vardir/install.db"); + } if ($opt_dry_run) { for (@$tests) { diff --git a/plugin/type_mysql_timestamp/CMakeLists.txt b/plugin/type_mysql_timestamp/CMakeLists.txt new file mode 100644 index 00000000000..ca7bf1e7704 --- /dev/null +++ b/plugin/type_mysql_timestamp/CMakeLists.txt @@ -0,0 +1,17 @@ +# Copyright (c) 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 +# the Free Software Foundation; version 2 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA + +MYSQL_ADD_PLUGIN(type_mysql_timestamp plugin.cc RECOMPILE_FOR_EMBEDDED + MODULE_ONLY COMPONENT Test) diff --git a/plugin/type_mysql_timestamp/mysql-test/type_mysql_timestamp/suite.opt b/plugin/type_mysql_timestamp/mysql-test/type_mysql_timestamp/suite.opt new file mode 100644 index 00000000000..e9e2a99b589 --- /dev/null +++ b/plugin/type_mysql_timestamp/mysql-test/type_mysql_timestamp/suite.opt @@ -0,0 +1 @@ +--plugin-load-add=$TYPE_MYSQL_TIMESTAMP_SO diff --git a/plugin/type_mysql_timestamp/mysql-test/type_mysql_timestamp/suite.pm b/plugin/type_mysql_timestamp/mysql-test/type_mysql_timestamp/suite.pm new file mode 100644 index 00000000000..cbb8f1b097f --- /dev/null +++ b/plugin/type_mysql_timestamp/mysql-test/type_mysql_timestamp/suite.pm @@ -0,0 +1,10 @@ +package My::Suite::Type_test; + +@ISA = qw(My::Suite); + +return "No TYPE_TEST plugin" unless $ENV{TYPE_MYSQL_TIMESTAMP_SO}; + +sub is_default { 1 } + +bless { }; + diff --git a/plugin/type_mysql_timestamp/mysql-test/type_mysql_timestamp/type_mysql_timestamp.result b/plugin/type_mysql_timestamp/mysql-test/type_mysql_timestamp/type_mysql_timestamp.result new file mode 100644 index 00000000000..009b3a7c47c --- /dev/null +++ b/plugin/type_mysql_timestamp/mysql-test/type_mysql_timestamp/type_mysql_timestamp.result @@ -0,0 +1,45 @@ +# +# MDEV-30483 After upgrade to 10.6 from Mysql 5.7 seeing "InnoDB: Column last_update in table mysql.innodb_table_stats is BINARY(4) NOT NULL but should be INT UNSIGNED NOT NULL" +# +SELECT +PLUGIN_NAME, +PLUGIN_VERSION, +PLUGIN_STATUS, +PLUGIN_TYPE, +PLUGIN_AUTHOR, +PLUGIN_DESCRIPTION, +PLUGIN_LICENSE, +PLUGIN_MATURITY, +PLUGIN_AUTH_VERSION +FROM INFORMATION_SCHEMA.PLUGINS +WHERE PLUGIN_TYPE='DATA TYPE' + AND PLUGIN_NAME LIKE 'type_mysql_timestamp'; +PLUGIN_NAME type_mysql_timestamp +PLUGIN_VERSION 1.0 +PLUGIN_STATUS ACTIVE +PLUGIN_TYPE DATA TYPE +PLUGIN_AUTHOR MariaDB Corporation +PLUGIN_DESCRIPTION Data type TYPE_MYSQL_TIMESTAMP +PLUGIN_LICENSE GPL +PLUGIN_MATURITY Experimental +PLUGIN_AUTH_VERSION 1.0 +CREATE TABLE t1 (a TYPE_MYSQL_TIMESTAMP); +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` type_mysql_timestamp NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp() +) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci +DROP TABLE t1; +CREATE TABLE t1 (a TIMESTAMP); +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` timestamp NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp() +) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci +ALTER TABLE t1 MODIFY a TYPE_MYSQL_TIMESTAMP; +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` type_mysql_timestamp NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp() +) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci +DROP TABLE t1; diff --git a/plugin/type_mysql_timestamp/mysql-test/type_mysql_timestamp/type_mysql_timestamp.test b/plugin/type_mysql_timestamp/mysql-test/type_mysql_timestamp/type_mysql_timestamp.test new file mode 100644 index 00000000000..a7aaa5a3e4c --- /dev/null +++ b/plugin/type_mysql_timestamp/mysql-test/type_mysql_timestamp/type_mysql_timestamp.test @@ -0,0 +1,31 @@ +--source include/have_innodb.inc + +--echo # +--echo # MDEV-30483 After upgrade to 10.6 from Mysql 5.7 seeing "InnoDB: Column last_update in table mysql.innodb_table_stats is BINARY(4) NOT NULL but should be INT UNSIGNED NOT NULL" +--echo # + +--vertical_results +SELECT + PLUGIN_NAME, + PLUGIN_VERSION, + PLUGIN_STATUS, + PLUGIN_TYPE, + PLUGIN_AUTHOR, + PLUGIN_DESCRIPTION, + PLUGIN_LICENSE, + PLUGIN_MATURITY, + PLUGIN_AUTH_VERSION +FROM INFORMATION_SCHEMA.PLUGINS + WHERE PLUGIN_TYPE='DATA TYPE' + AND PLUGIN_NAME LIKE 'type_mysql_timestamp'; +--horizontal_results + +CREATE TABLE t1 (a TYPE_MYSQL_TIMESTAMP); +SHOW CREATE TABLE t1; +DROP TABLE t1; + +CREATE TABLE t1 (a TIMESTAMP); +SHOW CREATE TABLE t1; +ALTER TABLE t1 MODIFY a TYPE_MYSQL_TIMESTAMP; +SHOW CREATE TABLE t1; +DROP TABLE t1; diff --git a/plugin/type_mysql_timestamp/mysql-test/type_mysql_timestamp/type_mysql_timestamp_stat_tables.result b/plugin/type_mysql_timestamp/mysql-test/type_mysql_timestamp/type_mysql_timestamp_stat_tables.result new file mode 100644 index 00000000000..e48f29c9a26 --- /dev/null +++ b/plugin/type_mysql_timestamp/mysql-test/type_mysql_timestamp/type_mysql_timestamp_stat_tables.result @@ -0,0 +1,73 @@ +# +# MDEV-30483 After upgrade to 10.6 from Mysql 5.7 seeing "InnoDB: Column last_update in table mysql.innodb_table_stats is BINARY(4) NOT NULL but should be INT UNSIGNED NOT NULL" +# +SET @@global.innodb_stats_persistent=0; +SHOW CREATE TABLE mysql.innodb_table_stats; +Table Create Table +innodb_table_stats CREATE TABLE `innodb_table_stats` ( + `database_name` varchar(64) NOT NULL, + `table_name` varchar(199) NOT NULL, + `last_update` timestamp NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(), + `n_rows` bigint(20) unsigned NOT NULL, + `clustered_index_size` bigint(20) unsigned NOT NULL, + `sum_of_other_index_sizes` bigint(20) unsigned NOT NULL, + PRIMARY KEY (`database_name`,`table_name`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_bin STATS_PERSISTENT=0 +ALTER TABLE mysql.innodb_table_stats MODIFY last_update TYPE_MYSQL_TIMESTAMP; +SHOW CREATE TABLE mysql.innodb_table_stats; +Table Create Table +innodb_table_stats CREATE TABLE `innodb_table_stats` ( + `database_name` varchar(64) NOT NULL, + `table_name` varchar(199) NOT NULL, + `last_update` type_mysql_timestamp NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(), + `n_rows` bigint(20) unsigned NOT NULL, + `clustered_index_size` bigint(20) unsigned NOT NULL, + `sum_of_other_index_sizes` bigint(20) unsigned NOT NULL, + PRIMARY KEY (`database_name`,`table_name`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_bin STATS_PERSISTENT=0 +ALTER TABLE mysql.innodb_index_stats MODIFY last_update TYPE_MYSQL_TIMESTAMP; +SHOW CREATE TABLE mysql.innodb_index_stats; +Table Create Table +innodb_index_stats CREATE TABLE `innodb_index_stats` ( + `database_name` varchar(64) NOT NULL, + `table_name` varchar(199) NOT NULL, + `index_name` varchar(64) NOT NULL, + `last_update` type_mysql_timestamp NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(), + `stat_name` varchar(64) NOT NULL, + `stat_value` bigint(20) unsigned NOT NULL, + `sample_size` bigint(20) unsigned DEFAULT NULL, + `stat_description` varchar(1024) NOT NULL, + PRIMARY KEY (`database_name`,`table_name`,`index_name`,`stat_name`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_bin STATS_PERSISTENT=0 +SET @@global.innodb_stats_persistent=1; +CREATE TABLE t1 (a INT, KEY(a)) ENGINE=InnoDB; +INSERT INTO t1 VALUES (10); +DROP TABLE t1; +SET @@global.innodb_stats_persistent=0; +ALTER TABLE mysql.innodb_table_stats MODIFY last_update TIMESTAMP; +SHOW CREATE TABLE mysql.innodb_table_stats; +Table Create Table +innodb_table_stats CREATE TABLE `innodb_table_stats` ( + `database_name` varchar(64) NOT NULL, + `table_name` varchar(199) NOT NULL, + `last_update` timestamp NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(), + `n_rows` bigint(20) unsigned NOT NULL, + `clustered_index_size` bigint(20) unsigned NOT NULL, + `sum_of_other_index_sizes` bigint(20) unsigned NOT NULL, + PRIMARY KEY (`database_name`,`table_name`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_bin STATS_PERSISTENT=0 +ALTER TABLE mysql.innodb_index_stats MODIFY last_update TIMESTAMP; +SHOW CREATE TABLE mysql.innodb_index_stats; +Table Create Table +innodb_index_stats CREATE TABLE `innodb_index_stats` ( + `database_name` varchar(64) NOT NULL, + `table_name` varchar(199) NOT NULL, + `index_name` varchar(64) NOT NULL, + `last_update` timestamp NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(), + `stat_name` varchar(64) NOT NULL, + `stat_value` bigint(20) unsigned NOT NULL, + `sample_size` bigint(20) unsigned DEFAULT NULL, + `stat_description` varchar(1024) NOT NULL, + PRIMARY KEY (`database_name`,`table_name`,`index_name`,`stat_name`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_bin STATS_PERSISTENT=0 +SET @@global.innodb_stats_persistent=1; diff --git a/plugin/type_mysql_timestamp/mysql-test/type_mysql_timestamp/type_mysql_timestamp_stat_tables.test b/plugin/type_mysql_timestamp/mysql-test/type_mysql_timestamp/type_mysql_timestamp_stat_tables.test new file mode 100644 index 00000000000..d22c94c6f82 --- /dev/null +++ b/plugin/type_mysql_timestamp/mysql-test/type_mysql_timestamp/type_mysql_timestamp_stat_tables.test @@ -0,0 +1,24 @@ +--source include/have_innodb.inc + +--echo # +--echo # MDEV-30483 After upgrade to 10.6 from Mysql 5.7 seeing "InnoDB: Column last_update in table mysql.innodb_table_stats is BINARY(4) NOT NULL but should be INT UNSIGNED NOT NULL" +--echo # + +SET @@global.innodb_stats_persistent=0; +SHOW CREATE TABLE mysql.innodb_table_stats; +ALTER TABLE mysql.innodb_table_stats MODIFY last_update TYPE_MYSQL_TIMESTAMP; +SHOW CREATE TABLE mysql.innodb_table_stats; +ALTER TABLE mysql.innodb_index_stats MODIFY last_update TYPE_MYSQL_TIMESTAMP; +SHOW CREATE TABLE mysql.innodb_index_stats; +SET @@global.innodb_stats_persistent=1; + +CREATE TABLE t1 (a INT, KEY(a)) ENGINE=InnoDB; +INSERT INTO t1 VALUES (10); +DROP TABLE t1; + +SET @@global.innodb_stats_persistent=0; +ALTER TABLE mysql.innodb_table_stats MODIFY last_update TIMESTAMP; +SHOW CREATE TABLE mysql.innodb_table_stats; +ALTER TABLE mysql.innodb_index_stats MODIFY last_update TIMESTAMP; +SHOW CREATE TABLE mysql.innodb_index_stats; +SET @@global.innodb_stats_persistent=1; diff --git a/plugin/type_mysql_timestamp/plugin.cc b/plugin/type_mysql_timestamp/plugin.cc new file mode 100644 index 00000000000..fd6ad896aa7 --- /dev/null +++ b/plugin/type_mysql_timestamp/plugin.cc @@ -0,0 +1,177 @@ +/* + Copyright (c) 2023, 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 + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include +#include +#include +#include "sql_type.h" + + +class Type_collection_local: public Type_collection +{ +protected: + const Type_handler *aggregate_common(const Type_handler *h1, + const Type_handler *h2) const; +public: + const Type_handler *handler_by_name(const LEX_CSTRING &name) const override + { + return NULL; + } + + const Type_handler *aggregate_for_result(const Type_handler *h1, + const Type_handler *h2) + const override + { + return aggregate_common(h1, h2); + } + + const Type_handler *aggregate_for_comparison(const Type_handler *h1, + const Type_handler *h2) + const override + { + return aggregate_common(h1, h2); + } + + const Type_handler *aggregate_for_min_max(const Type_handler *h1, + const Type_handler *h2) + const override + { + return aggregate_common(h1, h2); + } + + const Type_handler *aggregate_for_num_op(const Type_handler *h1, + const Type_handler *h2) + const override + { + return aggregate_common(h1, h2); + } +}; + + +static Type_collection_local type_collection_local; + + +/* + A more MySQL compatible Field: + it does not set the UNSIGNED_FLAG. + This is how MySQL's Field_timestampf works. +*/ +class Field_mysql_timestampf :public Field_timestampf +{ +public: + Field_mysql_timestampf(const LEX_CSTRING &name, + const Record_addr &addr, + enum utype unireg_check_arg, + TABLE_SHARE *share, decimal_digits_t dec_arg) + :Field_timestampf(addr.ptr(), addr.null_ptr(), addr.null_bit(), + unireg_check_arg, &name, share, dec_arg) + { + flags&= ~UNSIGNED_FLAG; // MySQL compatibility + } + void sql_type(String &str) const override + { + sql_type_opt_dec_comment(str, + Field_mysql_timestampf::type_handler()->name(), + dec, type_version_mysql56()); + } + const Type_handler *type_handler() const override; +}; + + +class Type_handler_mysql_timestamp2: public Type_handler_timestamp2 +{ +public: + const Type_collection *type_collection() const override + { + return &type_collection_local; + } + Field *make_table_field_from_def(TABLE_SHARE *share, MEM_ROOT *root, + const LEX_CSTRING *name, + const Record_addr &rec, const Bit_addr &bit, + const Column_definition_attributes *attr, + uint32 flags) const override + { + return new (root) + Field_mysql_timestampf(*name, rec, attr->unireg_check, share, + attr->temporal_dec(MAX_DATETIME_WIDTH)); + } + void Column_definition_implicit_upgrade(Column_definition *c) const override + { + /* + Suppress the automatic upgrade depending on opt_mysql56_temporal_format, + derived from Type_handler_timestamp_common. + */ + } +}; + + +static Type_handler_mysql_timestamp2 type_handler_mysql_timestamp2; + + +const Type_handler *Field_mysql_timestampf::type_handler() const +{ + return &type_handler_mysql_timestamp2; +} + + +const Type_handler * +Type_collection_local::aggregate_common(const Type_handler *h1, + const Type_handler *h2) const +{ + if (h1 == h2) + return h1; + + static const Type_aggregator::Pair agg[]= + { + { + &type_handler_timestamp2, + &type_handler_mysql_timestamp2, + &type_handler_mysql_timestamp2 + }, + {NULL,NULL,NULL} + }; + + return Type_aggregator::find_handler_in_array(agg, h1, h2, true); +} + + +static struct st_mariadb_data_type plugin_descriptor_type_mysql_timestamp= +{ + MariaDB_DATA_TYPE_INTERFACE_VERSION, + &type_handler_mysql_timestamp2 +}; + + + +/*************************************************************************/ + +maria_declare_plugin(type_mysql_timestamp) +{ + MariaDB_DATA_TYPE_PLUGIN, // the plugin type (see include/mysql/plugin.h) + &plugin_descriptor_type_mysql_timestamp, // pointer to type-specific plugin descriptor + "type_mysql_timestamp", // plugin name + "MariaDB Corporation", // plugin author + "Data type TYPE_MYSQL_TIMESTAMP", // the plugin description + PLUGIN_LICENSE_GPL, // the plugin license (see include/mysql/plugin.h) + 0, // Pointer to plugin initialization function + 0, // Pointer to plugin deinitialization function + 0x0100, // Numeric version 0xAABB means AA.BB version + NULL, // Status variables + NULL, // System variables + "1.0", // String version representation + MariaDB_PLUGIN_MATURITY_EXPERIMENTAL // Maturity(see include/mysql/plugin.h)*/ +} +maria_declare_plugin_end; diff --git a/sql/field.h b/sql/field.h index 642456b9774..e57a93b6562 100644 --- a/sql/field.h +++ b/sql/field.h @@ -3354,7 +3354,7 @@ public: /** TIMESTAMP(0..6) - MySQL56 version */ -class Field_timestampf final :public Field_timestamp_with_dec { +class Field_timestampf :public Field_timestamp_with_dec { void store_TIMEVAL(const timeval &tv) override; public: Field_timestampf(uchar *ptr_arg, diff --git a/sql/item_func.h b/sql/item_func.h index 6e714814526..6df3b98276b 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -373,7 +373,7 @@ public: { for (uint i= 0; i < arg_count; i++) { - args[i]->no_rows_in_result(); + args[i]->restore_to_before_no_rows_in_result(); } } void convert_const_compared_to_int_field(THD *thd); diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 32392ab882e..d8f16405351 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -1746,6 +1746,11 @@ static void close_connections(void) (void) unlink(mysqld_unix_port); } } + /* + The following is needed to the threads stuck in + setup_connection_thread_globals() + to continue. + */ listen_sockets.free_memory(); mysql_mutex_unlock(&LOCK_start_thread); @@ -2031,6 +2036,7 @@ static void clean_up(bool print_message) end_ssl(); #ifndef EMBEDDED_LIBRARY vio_end(); + listen_sockets.free_memory(); #endif /*!EMBEDDED_LIBRARY*/ #if defined(ENABLED_DEBUG_SYNC) /* End the debug sync facility. See debug_sync.cc. */ diff --git a/sql/sql_limit.h b/sql/sql_limit.h index 41308bc12db..335aff9d215 100644 --- a/sql/sql_limit.h +++ b/sql/sql_limit.h @@ -61,6 +61,15 @@ class Select_limit_counters with_ties= false; } + /* Send the first row, still honoring offset_limit_cnt */ + void send_first_row() + { + /* Guard against overflow */ + if ((select_limit_cnt= offset_limit_cnt +1 ) == 0) + select_limit_cnt= offset_limit_cnt; + // with_ties= false; Remove // on merge to 10.6 + } + bool is_unlimited() const { return select_limit_cnt == HA_POS_ERROR; } /* diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 2bb28e09ac2..363356eb385 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -149,10 +149,10 @@ static void update_depend_map_for_order(JOIN *join, ORDER *order); static ORDER *remove_const(JOIN *join,ORDER *first_order,COND *cond, bool change_list, bool *simple_order); static int return_zero_rows(JOIN *join, select_result *res, - List &tables, - List &fields, bool send_row, + List *tables, + List *fields, bool send_row, ulonglong select_options, const char *info, - Item *having, List &all_fields); + Item *having, List *all_fields); static COND *build_equal_items(JOIN *join, COND *cond, COND_EQUAL *inherited, List *join_list, @@ -1299,11 +1299,40 @@ int SELECT_LEX::vers_setup_conds(THD *thd, TABLE_LIST *tables) DBUG_RETURN(0); } + /***************************************************************************** Check fields, find best join, do the select and output fields. mysql_select assumes that all tables are already opened *****************************************************************************/ +/* + Check if we have a field reference. If yes, we have to use + mixed_implicit_grouping. +*/ + +static bool check_list_for_field(List *items) +{ + List_iterator_fast select_it(*items); + Item *select_el; + + while ((select_el= select_it++)) + { + if (select_el->with_field()) + return true; + } + return false; +} + +static bool check_list_for_field(ORDER *order) +{ + for (; order; order= order->next) + { + if (order->item[0]->with_field()) + return true; + } + return false; +} + /** Prepare of whole select (including sub queries in future). @@ -1385,53 +1414,44 @@ JOIN::prepare(TABLE_LIST *tables_init, COND *conds_init, uint og_num, DBUG_RETURN(-1); /* - TRUE if the SELECT list mixes elements with and without grouping, - and there is no GROUP BY clause. Mixing non-aggregated fields with - aggregate functions in the SELECT list is a MySQL extenstion that - is allowed only if the ONLY_FULL_GROUP_BY sql mode is not set. + mixed_implicit_grouping will be set to TRUE if the SELECT list + mixes elements with and without grouping, and there is no GROUP BY + clause. + Mixing non-aggregated fields with aggregate functions in the + SELECT list or HAVING is a MySQL extension that is allowed only if + the ONLY_FULL_GROUP_BY sql mode is not set. */ mixed_implicit_grouping= false; if ((~thd->variables.sql_mode & MODE_ONLY_FULL_GROUP_BY) && select_lex->with_sum_func && !group_list) { - List_iterator_fast select_it(fields_list); - Item *select_el; /* Element of the SELECT clause, can be an expression. */ - bool found_field_elem= false; - bool found_sum_func_elem= false; - - while ((select_el= select_it++)) + if (check_list_for_field(&fields_list) || + check_list_for_field(order)) { - if (select_el->with_sum_func()) - found_sum_func_elem= true; - if (select_el->with_field()) - found_field_elem= true; - if (found_sum_func_elem && found_field_elem) + List_iterator_fast li(select_lex->leaf_tables); + + mixed_implicit_grouping= true; // mark for future + + while (TABLE_LIST *tbl= li++) { - mixed_implicit_grouping= true; - break; + /* + If the query uses implicit grouping where the select list + contains both aggregate functions and non-aggregate fields, + any non-aggregated field may produce a NULL value. Set all + fields of each table as nullable before semantic analysis to + take into account this change of nullability. + + Note: this loop doesn't touch tables inside merged + semi-joins, because subquery-to-semijoin conversion has not + been done yet. This is intended. + */ + if (tbl->table) + tbl->table->maybe_null= 1; } } } - table_count= select_lex->leaf_tables.elements; - TABLE_LIST *tbl; - List_iterator_fast li(select_lex->leaf_tables); - while ((tbl= li++)) - { - /* - If the query uses implicit grouping where the select list contains both - aggregate functions and non-aggregate fields, any non-aggregated field - may produce a NULL value. Set all fields of each table as nullable before - semantic analysis to take into account this change of nullability. - - Note: this loop doesn't touch tables inside merged semi-joins, because - subquery-to-semijoin conversion has not been done yet. This is intended. - */ - if (mixed_implicit_grouping && tbl->table) - tbl->table->maybe_null= 1; - } - uint real_og_num= og_num; if (skip_order_by && select_lex != select_lex->master_unit()->global_parameters()) @@ -1444,14 +1464,14 @@ JOIN::prepare(TABLE_LIST *tables_init, COND *conds_init, uint og_num, DBUG_RETURN(-1); ref_ptrs= ref_ptr_array_slice(0); - + enum_parsing_place save_place= thd->lex->current_select->context_analysis_place; thd->lex->current_select->context_analysis_place= SELECT_LIST; { List_iterator_fast it(select_lex->leaf_tables); - while ((tbl= it++)) + while (TABLE_LIST *tbl= it++) { if (tbl->table_function && tbl->table_function->setup(thd, tbl, select_lex_arg)) @@ -4069,7 +4089,7 @@ bool JOIN::make_aggr_tables_info() set_items_ref_array(items0); if (join_tab) join_tab[exec_join_tab_cnt() + aggr_tables - 1].next_select= - setup_end_select_func(this, NULL); + setup_end_select_func(this); group= has_group_by; DBUG_RETURN(false); @@ -4464,13 +4484,7 @@ JOIN::reinit() } } - /* Reset of sum functions */ - if (sum_funcs) - { - Item_sum *func, **func_ptr= sum_funcs; - while ((func= *(func_ptr++))) - func->clear(); - } + clear_sum_funcs(); if (no_rows_in_result_called) { @@ -4753,12 +4767,12 @@ void JOIN::exec_inner() } else { - (void) return_zero_rows(this, result, select_lex->leaf_tables, - *columns_list, + (void) return_zero_rows(this, result, &select_lex->leaf_tables, + columns_list, send_row_on_empty_set(), select_options, zero_result_cause, - having ? having : tmp_having, all_fields); + having ? having : tmp_having, &all_fields); DBUG_VOID_RETURN; } } @@ -15301,10 +15315,36 @@ ORDER *simple_remove_const(ORDER *order, COND *where) } +/* + Set all fields in the table to have a null value + + @param tables Table list +*/ + +static void make_tables_null_complemented(List *tables) +{ + List_iterator ti(*tables); + TABLE_LIST *table; + while ((table= ti++)) + { + /* + Don't touch semi-join materialization tables, as the a join_free() + call may have freed them (and HAVING clause can't have references to + them anyway). + */ + if (!table->is_jtbm()) + { + TABLE *tbl= table->table; + mark_as_null_row(tbl); // Set fields to NULL + } + } +} + + static int -return_zero_rows(JOIN *join, select_result *result, List &tables, - List &fields, bool send_row, ulonglong select_options, - const char *info, Item *having, List &all_fields) +return_zero_rows(JOIN *join, select_result *result, List *tables, + List *fields, bool send_row, ulonglong select_options, + const char *info, Item *having, List *all_fields) { DBUG_ENTER("return_zero_rows"); @@ -15320,24 +15360,15 @@ return_zero_rows(JOIN *join, select_result *result, List &tables, Set all tables to have NULL row. This is needed as we will be evaluating HAVING condition. */ - List_iterator ti(tables); - TABLE_LIST *table; - while ((table= ti++)) - { - /* - Don't touch semi-join materialization tables, as the above join_free() - call has freed them (and HAVING clause can't have references to them - anyway). - */ - if (!table->is_jtbm()) - mark_as_null_row(table->table); // All fields are NULL - } - List_iterator_fast it(all_fields); + make_tables_null_complemented(tables); + + List_iterator_fast it(*all_fields); Item *item; /* Inform all items (especially aggregating) to calculate HAVING correctly, also we will need it for sending results. */ + join->no_rows_in_result_called= 1; while ((item= it++)) item->no_rows_in_result(); if (having && having->val_int() == 0) @@ -15351,12 +15382,12 @@ return_zero_rows(JOIN *join, select_result *result, List &tables, join->thd->limit_found_rows= 0; } - if (!(result->send_result_set_metadata(fields, + if (!(result->send_result_set_metadata(*fields, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))) { bool send_error= FALSE; if (send_row) - send_error= result->send_data_with_check(fields, join->unit, 0) > 0; + send_error= result->send_data_with_check(*fields, join->unit, 0) > 0; if (likely(!send_error)) result->send_eof(); // Should be safe } @@ -15372,49 +15403,42 @@ return_zero_rows(JOIN *join, select_result *result, List &tables, } /** - used only in JOIN::clear (always) and in do_select() - (if there where no matching rows) + Reset table rows to contain a null-complement row (all fields are null) + + Used only in JOIN::clear() and in do_select() if there where no matching rows. @param join JOIN - @param cleared_tables If not null, clear also const tables and mark all - cleared tables in the map. cleared_tables is only - set when called from do_select() when there is a - group function and there where no matching rows. + @param cleared_tables Used to mark all cleared tables in the map. Needed for + unclear_tables() to know which tables to restore to + their original state. */ static void clear_tables(JOIN *join, table_map *cleared_tables) { - /* - must clear only the non-const tables as const tables are not re-calculated. - */ + DBUG_ASSERT(cleared_tables); for (uint i= 0 ; i < join->table_count ; i++) { TABLE *table= join->table[i]; if (table->null_row) continue; // Nothing more to do - if (!(table->map & join->const_table_map) || cleared_tables) + (*cleared_tables)|= (((table_map) 1) << i); + if (table->s->null_bytes) { - if (cleared_tables) - { - (*cleared_tables)|= (((table_map) 1) << i); - if (table->s->null_bytes) - { - /* - Remember null bits for the record so that we can restore the - original const record in unclear_tables() - */ - memcpy(table->record[1], table->null_flags, table->s->null_bytes); - } - } - mark_as_null_row(table); // All fields are NULL + /* + Remember null bits for the record so that we can restore the + original const record in unclear_tables() + */ + memcpy(table->record[1], table->null_flags, table->s->null_bytes); } + mark_as_null_row(table); // All fields are NULL } } /** Reverse null marking for tables and restore null bits. + This return the tables to the state of before clear_tables(). We have to do this because the tables may be re-used in a sub query and the subquery will assume that the const tables contains the original @@ -21043,9 +21067,9 @@ void set_postjoin_aggr_write_func(JOIN_TAB *tab) end_select function to use. This function can't fail. */ -Next_select_func setup_end_select_func(JOIN *join, JOIN_TAB *tab) +Next_select_func setup_end_select_func(JOIN *join) { - TMP_TABLE_PARAM *tmp_tbl= tab ? tab->tmp_table_param : &join->tmp_table_param; + TMP_TABLE_PARAM *tmp_tbl= &join->tmp_table_param; /* Choose method for presenting result to user. Use end_send_group @@ -21115,7 +21139,7 @@ do_select(JOIN *join, Procedure *procedure) join->duplicate_rows= join->send_records=0; if (join->only_const_tables() && !join->need_tmp) { - Next_select_func end_select= setup_end_select_func(join, NULL); + Next_select_func end_select= setup_end_select_func(join); /* HAVING will be checked after processing aggregate functions, @@ -21590,6 +21614,7 @@ sub_select(JOIN *join,JOIN_TAB *join_tab,bool end_of_records) } } + /* Restore state if mark_as_null_row() have been called */ if (join_tab->last_inner) { JOIN_TAB *last_inner_tab= join_tab->last_inner; @@ -23002,11 +23027,18 @@ end_send_group(JOIN *join, JOIN_TAB *join_tab, bool end_of_records) { int idx= -1; enum_nested_loop_state ok_code= NESTED_LOOP_OK; + /* + join_tab can be 0 in the case all tables are const tables and we did not + need a temporary table to store the result. + In this case we use the original given fields, which is stored in + join->fields. + */ List *fields= join_tab ? (join_tab-1)->fields : join->fields; DBUG_ENTER("end_send_group"); if (!join->items3.is_null() && !join->set_group_rpa) { + /* Move ref_pointer_array to points to items3 */ join->set_group_rpa= true; join->set_items_ref_array(join->items3); } @@ -23014,10 +23046,12 @@ end_send_group(JOIN *join, JOIN_TAB *join_tab, bool end_of_records) if (!join->first_record || end_of_records || (idx=test_if_group_changed(join->group_fields)) >= 0) { + if (!join->group_sent && (join->first_record || (end_of_records && !join->group && !join->group_optimized_away))) { + table_map cleared_tables= (table_map) 0; if (join->procedure) join->procedure->end_group(); /* Test if there was a group change. */ @@ -23042,11 +23076,13 @@ end_send_group(JOIN *join, JOIN_TAB *join_tab, bool end_of_records) /* Reset all sum functions on group change. */ if (!join->first_record) { - List_iterator_fast it(*join->fields); - Item *item; /* No matching rows for group function */ - join->clear(); + List_iterator_fast it(*fields); + Item *item; + join->no_rows_in_result_called= 1; + + join->clear(&cleared_tables); while ((item= it++)) item->no_rows_in_result(); } @@ -23074,7 +23110,14 @@ end_send_group(JOIN *join, JOIN_TAB *join_tab, bool end_of_records) if (join->rollup_send_data((uint) (idx+1))) error= 1; } - } + if (join->no_rows_in_result_called) + { + /* Restore null tables to original state */ + join->no_rows_in_result_called= 0; + if (cleared_tables) + unclear_tables(join, &cleared_tables); + } + } if (unlikely(error > 0)) DBUG_RETURN(NESTED_LOOP_ERROR); /* purecov: inspected */ if (end_of_records) @@ -23391,6 +23434,7 @@ end_write_group(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)), { if (join->first_record || (end_of_records && !join->group)) { + table_map cleared_tables= (table_map) 0; if (join->procedure) join->procedure->end_group(); int send_group_parts= join->send_group_parts; @@ -23399,7 +23443,7 @@ end_write_group(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)), if (!join->first_record) { /* No matching rows for group function */ - join->clear(); + join->clear(&cleared_tables); } copy_sum_funcs(join->sum_funcs, join->sum_funcs_end[send_group_parts]); @@ -23422,6 +23466,8 @@ end_write_group(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)), DBUG_RETURN(NESTED_LOOP_ERROR); } } + if (cleared_tables) + unclear_tables(join, &cleared_tables); if (end_of_records) goto end; } @@ -25153,7 +25199,7 @@ JOIN_TAB::remove_duplicates() !(join->select_options & OPTION_FOUND_ROWS)) { // only const items with no OPTION_FOUND_ROWS - join->unit->lim.set_single_row(); // Only send first row + join->unit->lim.send_first_row(); // Only send first row my_free(sortorder); DBUG_RETURN(false); } @@ -27557,11 +27603,8 @@ int JOIN::rollup_write_data(uint idx, TMP_TABLE_PARAM *tmp_table_param_arg, (end_send_group/end_write_group) */ -void JOIN::clear() +void inline JOIN::clear_sum_funcs() { - clear_tables(this, 0); - copy_fields(&tmp_table_param); - if (sum_funcs) { Item_sum *func, **func_ptr= sum_funcs; @@ -27571,6 +27614,22 @@ void JOIN::clear() } +/* + Prepare for returning 'empty row' when there is no matching row. + + - Mark all tables with mark_as_null_row() + - Make a copy of of all simple SELECT items + - Reset all sum functions to NULL or 0. +*/ + +void JOIN::clear(table_map *cleared_tables) +{ + clear_tables(this, cleared_tables); + copy_fields(&tmp_table_param); + clear_sum_funcs(); +} + + /** Print an EXPLAIN line with all NULLs and given message in the 'Extra' column diff --git a/sql/sql_select.h b/sql/sql_select.h index d5ca921d8d5..1dc16cb8b74 100644 --- a/sql/sql_select.h +++ b/sql/sql_select.h @@ -227,7 +227,7 @@ enum sj_strategy_enum typedef enum_nested_loop_state (*Next_select_func)(JOIN *, struct st_join_table *, bool); -Next_select_func setup_end_select_func(JOIN *join, JOIN_TAB *tab); +Next_select_func setup_end_select_func(JOIN *join); int rr_sequential(READ_RECORD *info); int read_record_func_for_rr_and_unpack(READ_RECORD *info); Item *remove_pushed_top_conjuncts(THD *thd, Item *cond); @@ -1701,7 +1701,8 @@ public: void join_free(); /** Cleanup this JOIN, possibly for reuse */ void cleanup(bool full); - void clear(); + void clear(table_map *cleared_tables); + void inline clear_sum_funcs(); bool send_row_on_empty_set() { return (do_send_rows && implicit_grouping && !group_optimized_away && diff --git a/sql/sql_type.h b/sql/sql_type.h index 52c17d61d2e..8ebdb38db49 100644 --- a/sql/sql_type.h +++ b/sql/sql_type.h @@ -7574,8 +7574,9 @@ extern Named_type_handler type_handler_time; extern Named_type_handler type_handler_time2; extern Named_type_handler type_handler_datetime; extern Named_type_handler type_handler_datetime2; -extern Named_type_handler type_handler_timestamp; -extern Named_type_handler type_handler_timestamp2; + +extern MYSQL_PLUGIN_IMPORT Named_type_handler type_handler_timestamp; +extern MYSQL_PLUGIN_IMPORT Named_type_handler type_handler_timestamp2; extern Type_handler_interval_DDhhmmssff type_handler_interval_DDhhmmssff; diff --git a/sql/table.h b/sql/table.h index 2cbe00e6910..e75905d4998 100644 --- a/sql/table.h +++ b/sql/table.h @@ -3366,10 +3366,16 @@ inline void mark_as_null_row(TABLE *table) bfill(table->null_flags,table->s->null_bytes,255); } +/* + Restore table to state before mark_as_null_row() call. + This assumes that the caller has restored table->null_flags, + as is done in unclear_tables(). +*/ + inline void unmark_as_null_row(TABLE *table) { - table->null_row=0; - table->status= STATUS_NO_RECORD; + table->null_row= 0; + table->status&= ~STATUS_NULL_ROW; } bool is_simple_order(ORDER *order); diff --git a/storage/innobase/dict/dict0stats.cc b/storage/innobase/dict/dict0stats.cc index 39f5943d5a4..2e8d87dad9e 100644 --- a/storage/innobase/dict/dict0stats.cc +++ b/storage/innobase/dict/dict0stats.cc @@ -204,7 +204,17 @@ static const dict_table_schema_t table_stats_schema = { {"database_name", DATA_VARMYSQL, DATA_NOT_NULL, 192}, {"table_name", DATA_VARMYSQL, DATA_NOT_NULL, 597}, - {"last_update", DATA_INT, DATA_NOT_NULL | DATA_UNSIGNED, 4}, + /* + Don't check the DATA_UNSIGNED flag in last_update. + It presents if the server is running in a pure MariaDB installation, + because MariaDB's Field_timestampf::flags has UNSIGNED_FLAG. + But DATA_UNSIGNED misses when the server starts on a MySQL-5.7 directory + (during a migration), because MySQL's Field_timestampf::flags does not + have UNSIGNED_FLAG. + This is fine not to check DATA_UNSIGNED, because Field_timestampf + in both MariaDB and MySQL support only non-negative time_t values. + */ + {"last_update", DATA_INT, DATA_NOT_NULL, 4}, {"n_rows", DATA_INT, DATA_NOT_NULL | DATA_UNSIGNED, 8}, {"clustered_index_size", DATA_INT, DATA_NOT_NULL | DATA_UNSIGNED, 8}, {"sum_of_other_index_sizes", DATA_INT, DATA_NOT_NULL | DATA_UNSIGNED, 8}, @@ -218,7 +228,11 @@ static const dict_table_schema_t index_stats_schema = {"database_name", DATA_VARMYSQL, DATA_NOT_NULL, 192}, {"table_name", DATA_VARMYSQL, DATA_NOT_NULL, 597}, {"index_name", DATA_VARMYSQL, DATA_NOT_NULL, 192}, - {"last_update", DATA_INT, DATA_NOT_NULL | DATA_UNSIGNED, 4}, + /* + Don't check the DATA_UNSIGNED flag in last_update. + See comments about last_update in table_stats_schema above. + */ + {"last_update", DATA_INT, DATA_NOT_NULL, 4}, {"stat_name", DATA_VARMYSQL, DATA_NOT_NULL, 64*3}, {"stat_value", DATA_INT, DATA_NOT_NULL | DATA_UNSIGNED, 8}, {"sample_size", DATA_INT, DATA_UNSIGNED, 8}, diff --git a/storage/innobase/fsp/fsp0fsp.cc b/storage/innobase/fsp/fsp0fsp.cc index c6044b201fe..48415f2a2dc 100644 --- a/storage/innobase/fsp/fsp0fsp.cc +++ b/storage/innobase/fsp/fsp0fsp.cc @@ -1284,23 +1284,20 @@ static dberr_t fsp_free_page(fil_space_t *space, page_no_t offset, mtr_t *mtr) + header->page.frame, frag_n_used - 1); } + mtr->free(*space, static_cast(offset)); + xdes_set_free(*xdes, descr, offset % FSP_EXTENT_SIZE, mtr); + ut_ad(err == DB_SUCCESS); + if (!xdes_get_n_used(descr)) { /* The extent has become free: move it to another list */ err = flst_remove(header, FSP_HEADER_OFFSET + FSP_FREE_FRAG, xdes, xoffset, mtr); - if (UNIV_UNLIKELY(err != DB_SUCCESS)) { - return err; - } - err = fsp_free_extent(space, offset, mtr); - if (UNIV_UNLIKELY(err != DB_SUCCESS)) { - return err; + if (err == DB_SUCCESS) { + err = fsp_free_extent(space, offset, mtr); } } - mtr->free(*space, static_cast(offset)); - xdes_set_free(*xdes, descr, offset % FSP_EXTENT_SIZE, mtr); - - return DB_SUCCESS; + return err; } /** @return Number of segment inodes which fit on a single page */ diff --git a/storage/innobase/trx/trx0purge.cc b/storage/innobase/trx/trx0purge.cc index 58bb6ab02a2..21e0491d6dd 100644 --- a/storage/innobase/trx/trx0purge.cc +++ b/storage/innobase/trx/trx0purge.cc @@ -399,12 +399,13 @@ static void trx_purge_free_segment(buf_block_t *block, mtr_t &mtr) } /** Remove unnecessary history data from a rollback segment. -@param[in,out] rseg rollback segment -@param[in] limit truncate anything before this +@param rseg rollback segment +@param limit truncate anything before this +@param all whether everything can be truncated @return error code */ static dberr_t -trx_purge_truncate_rseg_history(trx_rseg_t& rseg, - const purge_sys_t::iterator& limit) +trx_purge_truncate_rseg_history(trx_rseg_t &rseg, + const purge_sys_t::iterator &limit, bool all) { fil_addr_t hdr_addr; mtr_t mtr; @@ -447,6 +448,9 @@ loop: goto func_exit; } + if (!all) + goto func_exit; + fil_addr_t prev_hdr_addr= flst_get_prev_addr(b->page.frame + hdr_addr.boffset + TRX_UNDO_HISTORY_NODE); @@ -486,9 +490,9 @@ loop: if (undo->hdr_page_no == hdr_addr.page) goto found_cached; ut_ad("inconsistent undo logs" == 0); - break; - found_cached: - UT_LIST_REMOVE(rseg.undo_cached, undo); + if (false) + found_cached: + UT_LIST_REMOVE(rseg.undo_cached, undo); static_assert(FIL_NULL == 0xffffffff, ""); if (UNIV_UNLIKELY(mach_read_from_4(TRX_RSEG + TRX_RSEG_FORMAT + rseg_hdr->page.frame))) @@ -584,9 +588,11 @@ TRANSACTIONAL_TARGET static void trx_purge_truncate_history() { ut_ad(rseg.is_persistent()); rseg.latch.wr_lock(SRW_LOCK_CALL); - if (!rseg.is_referenced() && rseg.needs_purge <= head.trx_no) - if (dberr_t e= trx_purge_truncate_rseg_history(rseg, head)) - err= e; + if (dberr_t e= + trx_purge_truncate_rseg_history(rseg, head, + !rseg.is_referenced() && + rseg.needs_purge <= head.trx_no)) + err= e; rseg.latch.wr_unlock(); } @@ -664,7 +670,8 @@ not_free: } ut_ad(rseg.curr_size > cached); - if (rseg.curr_size > cached + 1) + if (rseg.curr_size > cached + 1 && + (rseg.history_size || srv_fast_shutdown || srv_undo_sources)) goto not_free; rseg.latch.rd_unlock(); diff --git a/storage/myisam/mi_open.c b/storage/myisam/mi_open.c index b80c2b69f16..8b82a71ff7c 100644 --- a/storage/myisam/mi_open.c +++ b/storage/myisam/mi_open.c @@ -518,6 +518,7 @@ MI_INFO *mi_open(const char *name, int mode, uint open_flags) share->kfile=kfile; share->this_process=(ulong) getpid(); share->last_process= share->state.process; + share->base.base_key_parts= base_key_parts; share->base.key_parts=key_parts; share->base.all_key_parts=key_parts+unique_key_parts; if (!(share->last_version=share->state.version)) diff --git a/storage/myisam/myisamdef.h b/storage/myisam/myisamdef.h index c90d989c975..f84ad6fa184 100644 --- a/storage/myisam/myisamdef.h +++ b/storage/myisam/myisamdef.h @@ -132,7 +132,7 @@ typedef struct st_mi_base_info uint extra_alloc_bytes; uint extra_alloc_procent; /* The following are from the header */ - uint key_parts, all_key_parts; + uint key_parts, all_key_parts, base_key_parts; } MI_BASE_INFO; diff --git a/storage/myisammrg/myrg_open.c b/storage/myisammrg/myrg_open.c index d9ea4b754f2..4a983684394 100644 --- a/storage/myisammrg/myrg_open.c +++ b/storage/myisammrg/myrg_open.c @@ -432,17 +432,20 @@ int myrg_attach_children(MYRG_INFO *m_info, int handle_locking, first_child= FALSE; m_info->reclength= myisam->s->base.reclength; min_keys= myisam->s->base.keys; - key_parts= myisam->s->base.key_parts; + key_parts= myisam->s->base.base_key_parts; if (*need_compat_check && m_info->rec_per_key_part) { my_free(m_info->rec_per_key_part); m_info->rec_per_key_part= NULL; } - if (!m_info->rec_per_key_part) + if (!m_info->rec_per_key_part || m_info->key_parts != key_parts) { - if(!(m_info->rec_per_key_part= (ulong*) - my_malloc(rg_key_memory_MYRG_INFO, - key_parts * sizeof(long), MYF(MY_WME)))) + m_info->key_parts= key_parts; + /* The +1 is because by my_realloc() don't allow zero length */ + if (!(m_info->rec_per_key_part= (ulong*) + my_realloc(rg_key_memory_MYRG_INFO, m_info->rec_per_key_part, + key_parts * sizeof(long) +1, + MYF(MY_WME | MY_ALLOW_ZERO_PTR | MY_FREE_ON_ERROR)))) goto err; /* purecov: inspected */ errpos= 1; } @@ -457,7 +460,8 @@ int myrg_attach_children(MYRG_INFO *m_info, int handle_locking, myisam->open_flag|= HA_OPEN_MERGE_TABLE; /* Check table definition match. */ - if (m_info->reclength != myisam->s->base.reclength) + if (m_info->reclength != myisam->s->base.reclength || + key_parts != myisam->s->base.base_key_parts) { DBUG_PRINT("error", ("definition mismatch table: '%s' repair: %d", myisam->filename, diff --git a/storage/spider/mysql-test/spider/bugfix/t/mdev_30370.test b/storage/spider/mysql-test/spider/bugfix/t/mdev_30370.test index 788ea2323f7..99e56ab062a 100644 --- a/storage/spider/mysql-test/spider/bugfix/t/mdev_30370.test +++ b/storage/spider/mysql-test/spider/bugfix/t/mdev_30370.test @@ -2,4 +2,7 @@ --echo # MDEV-30370 mariadbd hangs when running with --wsrep-recover and --plugin-load-add=ha_spider.so --echo # ---exec $MYSQLD_BOOTSTRAP_CMD --wsrep-recover --plugin-load-add=ha_spider.so +let $MYSQLD_DATADIR=$MYSQLTEST_VARDIR/mdev_30370; +--mkdir $MYSQLD_DATADIR +--exec $MYSQLD_BOOTSTRAP_CMD --wsrep-recover --plugin-load-add=ha_spider.so --datadir=$MYSQLD_DATADIR +--rmdir $MYSQLD_DATADIR