1
0
mirror of https://github.com/MariaDB/server.git synced 2025-11-02 02:53:04 +03:00

Merge 10.10 into 10.11

This commit is contained in:
Marko Mäkelä
2023-06-07 15:01:43 +03:00
110 changed files with 3619 additions and 1711 deletions

2
.github/CODEOWNERS vendored
View File

@@ -1,2 +0,0 @@
/debian @ottok

View File

@@ -42,7 +42,7 @@ variables:
CMAKE_FLAGS: "-DWITH_SSL=system -DPLUGIN_COLUMNSTORE=NO -DPLUGIN_ROCKSDB=NO -DPLUGIN_S3=NO -DPLUGIN_MROONGA=NO -DPLUGIN_CONNECT=NO -DPLUGIN_MROONGA=NO -DPLUGIN_TOKUDB=NO -DPLUGIN_PERFSCHEMA=NO -DWITH_WSREP=OFF"
# Major version dictates which branches share the same ccache. E.g. 10.6-abc
# and 10.6-xyz will have the same cache.
MARIADB_MAJOR_VERSION: "10.8"
MARIADB_MAJOR_VERSION: "10.9"
# NOTE! Currently ccache is only used on the Centos8 build. As each job has
# sufficiently different environments they are unable to benefit from each
# other's ccaches. As each build generates about 1 GB of ccache, having

View File

@@ -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()

35
debian/additions/innotop/innotop vendored Normal file → Executable file
View File

@@ -20,6 +20,9 @@
# Street, Fifth Floor, Boston, MA 02110-1335 USA
use strict;
use warnings;
use utf8;
use feature ':5.16';
use warnings FATAL => 'all';
our $VERSION = '1.11.4';
@@ -265,7 +268,7 @@ sub get_dbh {
$dbh->do($sql);
MKDEBUG && _d('Enabling charset for STDOUT');
if ( $charset eq 'utf8' ) {
binmode(STDOUT, ':utf8')
binmode(STDOUT, ':encoding(UTF-8)')
or die "Can't binmode(STDOUT, ':utf8'): $OS_ERROR";
}
else {
@@ -612,6 +615,9 @@ sub ts_to_string {
sub parse_innodb_timestamp {
my $text = shift;
if ( ! defined $text ) {
return (0, 0, 0, 0, 0, 0);
}
my ( $y, $m, $d, $h, $i, $s )
= $text =~ m/^(\d\d)(\d\d)(\d\d) +(\d+):(\d+):(\d+)$/;
die("Can't get timestamp from $text\n") unless $y;
@@ -803,7 +809,8 @@ sub parse_fk_transaction_error {
# TODO: write some tests for this
sub parse_innodb_record_dump {
my ( $dump, $complete, $debug ) = @_;
return undef unless $dump;
# Use bare return as recommend in page 199 of PBP
return unless $dump;
my $result = {};
@@ -6769,6 +6776,9 @@ sub set_precision {
my ( $num, $precision ) = @_;
$num = 0 unless defined $num;
$precision = $config{num_digits}->{val} if !defined $precision;
if ( $num eq "" ) {
$num = int(0);
}
sprintf("%.${precision}f", $num);
}
@@ -6777,6 +6787,9 @@ sub set_precision {
sub percent {
my ( $num ) = @_;
$num = 0 unless defined $num;
if ( $num eq "" ) {
$num = int(0);
}
my $digits = $config{num_digits}->{val};
return sprintf("%.${digits}f", $num * 100)
. ($config{show_percent}->{val} ? '%' : '');
@@ -6841,7 +6854,7 @@ sub make_color_func {
push @criteria,
"( defined \$set->{$spec->{col}} && \$set->{$spec->{col}} $spec->{op} $val ) { return '$spec->{color}'; }";
}
return undef unless @criteria;
return unless @criteria;
my $sub = eval 'sub { my ( $set ) = @_; if ' . join(" elsif ", @criteria) . '}';
die if $EVAL_ERROR;
return $sub;
@@ -7521,10 +7534,10 @@ sub choose_connections {
sub do_stmt {
my ( $cxn, $stmt_name, @args ) = @_;
return undef if $file;
return if $file;
# Test if the cxn should not even be tried
return undef if $dbhs{$cxn}
return if $dbhs{$cxn}
&& $dbhs{$cxn}->{failed}
&& ( !$dbhs{$cxn}->{dbh} || !$dbhs{$cxn}->{dbh}->{Active} || $dbhs{$cxn}->{mode} eq $config{mode}->{val} );
@@ -7596,10 +7609,10 @@ sub handle_cxn_error {
sub do_query {
my ( $cxn, $query ) = @_;
return undef if $file;
return if $file;
# Test if the cxn should not even be tried
return undef if $dbhs{$cxn}
return if $dbhs{$cxn}
&& $dbhs{$cxn}->{failed}
&& ( !$dbhs{$cxn}->{dbh} || !$dbhs{$cxn}->{dbh}->{Active} || $dbhs{$cxn}->{mode} eq $config{mode}->{val} );
@@ -7781,7 +7794,7 @@ sub compile_select_stmt {
sub compile_filter {
my ( $text ) = @_;
my ( $sub, $err );
eval "\$sub = sub { my \$set = shift; $text }";
eval { $sub = sub { my $set = shift; $text } };
if ( $EVAL_ERROR ) {
$EVAL_ERROR =~ s/at \(eval.*$//;
$sub = sub { return $EVAL_ERROR };
@@ -8013,7 +8026,7 @@ sub load_config_plugins {
# First, find a list of all plugins that exist on disk, and get information about them.
my $dir = $config{plugin_dir}->{val};
foreach my $p_file ( <$dir/*.pm> ) {
foreach my $p_file (glob($dir."/*.pm")) {
my ($package, $desc);
eval {
open my $p_in, "<", $p_file or die $OS_ERROR;
@@ -9192,7 +9205,7 @@ sub switch_var_set {
# edit_stmt_sleep_times {{{3
sub edit_stmt_sleep_times {
$clear_screen_sub->();
my $stmt = prompt_list('Specify a statement', '', sub { return sort keys %stmt_maker_for });
my $stmt = prompt_list('Specify a statement', '', sub { my @tmparray = sort keys %stmt_maker_for; return @tmparray });
return unless $stmt && exists $stmt_maker_for{$stmt};
$clear_screen_sub->();
my $curr_val = $stmt_sleep_time_for{$stmt} || 0;
@@ -9843,7 +9856,7 @@ sub get_slave_status {
sub is_func {
my ( $word ) = @_;
return defined(&$word)
|| eval "my \$x= sub { $word }; 1"
|| eval { my $x = sub { $word }; 1 }
|| $EVAL_ERROR !~ m/^Bareword/;
}

View File

@@ -86,7 +86,7 @@ sanity_checks() {
datadir=`mariadbd_get_param datadir`
# As preset blocksize of GNU df is 1024 then available bytes is $df_available_blocks * 1024
# 4096 blocks is then lower than 4 MB
df_available_blocks=`LC_ALL=C BLOCKSIZE= df --output=avail "$datadir" | tail -n 1`
df_available_blocks="$(LC_ALL=C BLOCKSIZE='' df --output=avail "$datadir" | tail -n 1)"
if [ "$df_available_blocks" -lt "4096" ]; then
log_failure_msg "$0: ERROR: The partition with $datadir is too full!"
echo "ERROR: The partition with $datadir is too full!" | $ERR_LOGGER

View File

@@ -223,14 +223,23 @@ then
mkdir -Z $mysql_datadir
fi
# As preset blocksize of GNU df is 1024 then available bytes is $df_available_blocks * 1024
# 4096 blocks is then lower than 4 MB
df_available_blocks=`LC_ALL=C BLOCKSIZE= df --output=avail "$datadir" | tail -n 1`
if [ "$df_available_blocks" -lt "4096" ]
# Check if MariaDB datadir is available if not fails.
# There should be symlink or directory available or something will fail.
if [ -d "$mysql_datadir" ] || [ -L "$mysql_datadir" ]
then
# As preset blocksize of GNU df is 1024 then available bytes is $df_available_blocks * 1024
# 4096 blocks is then lower than 4 MB
df_available_blocks="$(LC_ALL=C BLOCKSIZE='' df --output=avail "$mysql_datadir" | tail -n 1)"
if [ "$df_available_blocks" -lt "4096" ]
then
echo "ERROR: There's not enough space in $mysql_datadir/" 1>&2
db_stop
exit 1
fi
else
echo "ERROR: There's no directory or symlink available: $mysql_datadir/" 1>&2
db_stop
exit 1
fi
# Since the home directory was created before putting the user into

View File

@@ -847,26 +847,48 @@ 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;
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] = '/';
char *p = strrchr(f, '.');
ut_a(p);
*p = 0;
char *table = strrchr(f, '/');
ut_a(table);
}
#endif
/* Remove extension, if exists */
if (!(p= strrchr(f, '.')))
goto err;
*p= 0;
/* Find table name */
if (!(table= strrchr(f, '/')))
goto err;
*table = 0;
char *db = strrchr(f, '/');
/* Find database name */
db= strrchr(f, '/');
*table = '/';
std::string s(db ? db+1 : f);
free(f);
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;
}
@@ -3146,7 +3168,7 @@ static bool xtrabackup_copy_logfile()
if (log_sys.buf[recv_sys.offset] <= 1)
break;
if (recv_sys.parse_mtr(STORE_NO) == recv_sys_t::OK)
if (recv_sys.parse_mtr<false>(false) == recv_sys_t::OK)
{
do
{
@@ -3156,7 +3178,7 @@ static bool xtrabackup_copy_logfile()
sequence_offset));
*seq= 1;
}
while ((r= recv_sys.parse_mtr(STORE_NO)) == recv_sys_t::OK);
while ((r= recv_sys.parse_mtr<false>(false)) == recv_sys_t::OK);
if (ds_write(dst_log_file, log_sys.buf + start_offset,
recv_sys.offset - start_offset))

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -57,6 +57,7 @@ extern struct wsrep_service_st {
my_bool (*wsrep_on_func)(const MYSQL_THD thd);
bool (*wsrep_prepare_key_for_innodb_func)(MYSQL_THD thd, const unsigned char*, size_t, const unsigned char*, size_t, struct wsrep_buf*, size_t*);
void (*wsrep_thd_LOCK_func)(const MYSQL_THD thd);
int (*wsrep_thd_TRYLOCK_func)(const MYSQL_THD thd);
void (*wsrep_thd_UNLOCK_func)(const MYSQL_THD thd);
const char * (*wsrep_thd_query_func)(const MYSQL_THD thd);
int (*wsrep_thd_retry_counter_func)(const MYSQL_THD thd);
@@ -89,7 +90,6 @@ extern struct wsrep_service_st {
ulong (*wsrep_OSU_method_get_func)(const MYSQL_THD thd);
my_bool (*wsrep_thd_has_ignored_error_func)(const MYSQL_THD thd);
void (*wsrep_thd_set_ignored_error_func)(MYSQL_THD thd, my_bool val);
bool (*wsrep_thd_set_wsrep_aborter_func)(MYSQL_THD bf_thd, MYSQL_THD thd);
void (*wsrep_report_bf_lock_wait_func)(const MYSQL_THD thd,
unsigned long long trx_id);
void (*wsrep_thd_kill_LOCK_func)(const MYSQL_THD thd);
@@ -111,6 +111,7 @@ extern struct wsrep_service_st {
#define wsrep_on(thd) (thd) && WSREP_ON && wsrep_service->wsrep_on_func(thd)
#define wsrep_prepare_key_for_innodb(A,B,C,D,E,F,G) wsrep_service->wsrep_prepare_key_for_innodb_func(A,B,C,D,E,F,G)
#define wsrep_thd_LOCK(T) wsrep_service->wsrep_thd_LOCK_func(T)
#define wsrep_thd_TRYLOCK(T) wsrep_service->wsrep_thd_TRYLOCK_func(T)
#define wsrep_thd_UNLOCK(T) wsrep_service->wsrep_thd_UNLOCK_func(T)
#define wsrep_thd_kill_LOCK(T) wsrep_service->wsrep_thd_kill_LOCK_func(T)
#define wsrep_thd_kill_UNLOCK(T) wsrep_service->wsrep_thd_kill_UNLOCK_func(T)
@@ -141,7 +142,6 @@ extern struct wsrep_service_st {
#define wsrep_OSU_method_get(T) wsrep_service->wsrep_OSU_method_get_func(T)
#define wsrep_thd_has_ignored_error(T) wsrep_service->wsrep_thd_has_ignored_error_func(T)
#define wsrep_thd_set_ignored_error(T,V) wsrep_service->wsrep_thd_set_ignored_error_func(T,V)
#define wsrep_thd_set_wsrep_aborter(T) wsrep_service->wsrep_thd_set_wsrep_aborter_func(T1, T2)
#define wsrep_report_bf_lock_wait(T,I) wsrep_service->wsrep_report_bf_lock_wait(T,I)
#define wsrep_thd_set_PA_unsafe(T) wsrep_service->wsrep_thd_set_PA_unsafe_func(T)
#else
@@ -175,6 +175,8 @@ void wsrep_set_data_home_dir(const char *data_dir);
extern "C" my_bool wsrep_on(const MYSQL_THD thd);
/* Lock thd wsrep lock */
extern "C" void wsrep_thd_LOCK(const MYSQL_THD thd);
/* Try thd wsrep lock. Return non-zero if lock could not be taken. */
extern "C" int wsrep_thd_TRYLOCK(const MYSQL_THD thd);
/* Unlock thd wsrep lock */
extern "C" void wsrep_thd_UNLOCK(const MYSQL_THD thd);
@@ -197,8 +199,6 @@ extern "C" my_bool wsrep_thd_is_local(const MYSQL_THD thd);
/* Return true if thd is in high priority mode */
/* todo: rename to is_high_priority() */
extern "C" my_bool wsrep_thd_is_applying(const MYSQL_THD thd);
/* set wsrep_aborter for the target THD */
extern "C" bool wsrep_thd_set_wsrep_aborter(MYSQL_THD bf_thd, MYSQL_THD victim_thd);
/* Return true if thd is in TOI mode */
extern "C" my_bool wsrep_thd_is_toi(const MYSQL_THD thd);
/* Return true if thd is in replicating TOI mode */
@@ -249,7 +249,6 @@ extern "C" my_bool wsrep_thd_is_applying(const MYSQL_THD thd);
extern "C" ulong wsrep_OSU_method_get(const MYSQL_THD thd);
extern "C" my_bool wsrep_thd_has_ignored_error(const MYSQL_THD thd);
extern "C" void wsrep_thd_set_ignored_error(MYSQL_THD thd, my_bool val);
extern "C" bool wsrep_thd_set_wsrep_aborter(MYSQL_THD bf_thd, MYSQL_THD victim_thd);
extern "C" void wsrep_report_bf_lock_wait(const THD *thd,
unsigned long long trx_id);
/* declare parallel applying unsafety for the THD */

View File

@@ -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(<dir>])"
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;

View File

@@ -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:

View File

@@ -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 <subquery2> 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
#

View File

@@ -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 #

View File

@@ -2578,5 +2578,29 @@ SELECT JSON_EXTRACT('{ "my-key": 1 }', '$.my-key');
JSON_EXTRACT('{ "my-key": 1 }', '$.my-key')
1
#
# MDEV-23187: Assorted assertion failures in json_find_path with certain collations
#
SET @save_collation_connection= @@collation_connection;
SET @json='{ "A": [ [{"k":"v"},[1]],true],"B": {"C": 1} }';
SELECT JSON_VALUE(@json,'$.A[last-1][last-1].key1');
JSON_VALUE(@json,'$.A[last-1][last-1].key1')
NULL
SET @json='{ "A": [ [{"k":"v"},[1]],true],"B": {"C": 1} }';
SET collation_connection='ucs2_bin';
SELECT JSON_VALUE(@json,'$.A[last-1][last-1].key1');
JSON_VALUE(@json,'$.A[last-1][last-1].key1')
NULL
SET @json='{ "A": [ [{"k":"v"},[15]],true],"B": {"C": 1} }';
SET sql_mode=0,character_set_connection=utf32;
SELECT JSON_VALUE(@json,'$.A[last-1][last-1].key1');
JSON_VALUE(@json,'$.A[last-1][last-1].key1')
NULL
SET @json='{ "A": [ [{"k":"v"},[15]],true],"B": {"C": 1} }';
SET sql_mode=0,character_set_connection=utf32;
SELECT JSON_VALUE(@json,'$.A[last-1][last-1].key1');
JSON_VALUE(@json,'$.A[last-1][last-1].key1')
NULL
SET @@collation_connection= @save_collation_connection;
#
# End of 10.9 Test
#

View File

@@ -1772,6 +1772,32 @@ DROP TABLE t1;
SELECT JSON_EXTRACT('{ "my-key": 1 }', '$."my-key"');
SELECT JSON_EXTRACT('{ "my-key": 1 }', '$.my-key');
--echo #
--echo # MDEV-23187: Assorted assertion failures in json_find_path with certain collations
--echo #
SET @save_collation_connection= @@collation_connection;
SET @json='{ "A": [ [{"k":"v"},[1]],true],"B": {"C": 1} }';
SELECT JSON_VALUE(@json,'$.A[last-1][last-1].key1');
SET @json='{ "A": [ [{"k":"v"},[1]],true],"B": {"C": 1} }';
SET collation_connection='ucs2_bin';
SELECT JSON_VALUE(@json,'$.A[last-1][last-1].key1');
SET @json='{ "A": [ [{"k":"v"},[15]],true],"B": {"C": 1} }';
SET sql_mode=0,character_set_connection=utf32;
SELECT JSON_VALUE(@json,'$.A[last-1][last-1].key1');
SET @json='{ "A": [ [{"k":"v"},[15]],true],"B": {"C": 1} }';
SET sql_mode=0,character_set_connection=utf32;
SELECT JSON_VALUE(@json,'$.A[last-1][last-1].key1');
SET @@collation_connection= @save_collation_connection;
--echo #
--echo # End of 10.9 Test
--echo #

View File

@@ -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
#
#

View File

@@ -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 #

View File

@@ -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
#
#

View File

@@ -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 #

View File

@@ -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

View File

@@ -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 #

View File

@@ -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

View File

@@ -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');

View File

@@ -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);

View File

@@ -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) {

View File

@@ -27,3 +27,5 @@ galera_bf_kill_debug : timeout after 900 seconds
galera_ssl_upgrade : [Warning] Failed to load slave replication state from table mysql.gtid_slave_pos: 130: Incorrect file format 'gtid_slave_pos'
galera_parallel_simple : timeout related to wsrep_sync_wait
galera_insert_bulk : MDEV-30536 no expected deadlock in galera_insert_bulk test
MDEV-27713 : test is using get_lock(), which is now rejected in cluster
galera_bf_abort_group_commit : MDEV-30855 PR to remove the test exists

View File

@@ -0,0 +1,21 @@
connection node_2;
connection node_1;
connect node_1a, 127.0.0.1, root, , test, $NODE_MYPORT_1;
connect node_1b, 127.0.0.1, root, , test, $NODE_MYPORT_1;
set wsrep_sync_wait = 0;
CREATE TABLE t1(a int not null primary key auto_increment, b int) engine=InnoDB;
INSERT INTO t1 VALUES (1,2);
connection node_1a;
BEGIN;
UPDATE t1 SET b=3 WHERE a=1;
connection node_1;
set debug_sync='wsrep_kill_before_awake_no_mutex SIGNAL before_kill WAIT_FOR continue';
connection node_1b;
set debug_sync= 'now WAIT_FOR before_kill';
connection node_2;
UPDATE t1 SET b=7 WHERE a=1;
connection node_1b;
set debug_sync= 'now SIGNAL continue';
connection node_1;
DROP TABLE t1;
SET DEBUG_SYNC= 'RESET';

View File

@@ -82,6 +82,7 @@ connect node_1a, 127.0.0.1, root, , test, $NODE_MYPORT_1;
LOCK TABLE t2 WRITE;
connection node_1;
CREATE TABLE t1 AS SELECT * FROM t2;;
connection node_1a;
connection node_2;
SELECT COUNT(*) = 5 FROM t2;
COUNT(*) = 5

View File

@@ -134,6 +134,3 @@ connection node_1;
call mtr.add_suppression("Error in Log_event::read_log_event():.*");
CALL mtr.add_suppression("conflict state 7 after post commit");
CALL mtr.add_suppression("Skipped GCache ring buffer recovery");
connection node_2;
call mtr.add_suppression("Error in Log_event::read_log_event():.*");
CALL mtr.add_suppression("Skipped GCache ring buffer recovery");

View File

@@ -0,0 +1,27 @@
connection node_2;
connection node_1;
connect node_1_kill, 127.0.0.1, root, , test, $NODE_MYPORT_1;
connect node_1_ctrl, 127.0.0.1, root, , test, $NODE_MYPORT_1;
SET SESSION wsrep_sync_wait = 0;
connect node_1_follower, 127.0.0.1, root, , test, $NODE_MYPORT_1;
SET SESSION wsrep_sync_wait = 0;
connection node_1;
CREATE TABLE t1 (f1 INT PRIMARY KEY) ENGINE=InnoDB;
SET SESSION DEBUG_SYNC = "commit_before_enqueue SIGNAL leader_before_enqueue_reached WAIT_FOR leader_before_enqueue_continue";
INSERT INTO t1 VALUES (1);
connection node_1_ctrl;
SET DEBUG_SYNC = "now WAIT_FOR leader_before_enqueue_reached";
connection node_1_follower;
INSERT INTO t1 VALUES (2);;
connection node_1_ctrl;
connection node_1_kill;
# Execute KILL QUERY for group commit follower
SET DEBUG_SYNC = "now SIGNAL leader_before_enqueue_continue";
connection node_1_follower;
connection node_1;
SELECT * FROM t1;
f1
1
2
SET DEBUG_SYNC = "RESET";
DROP TABLE t1;

View File

@@ -36,7 +36,10 @@ SET DEBUG_SYNC = 'now SIGNAL wsrep_retry_autocommit_continue';
connection node_1;
SELECT COUNT(*) FROM t1;
COUNT(*)
1
connection node_1;
SELECT COUNT(*) FROM t1;
COUNT(*)
0
SET DEBUG_SYNC = 'RESET';
SET GLOBAL debug_dbug = NULL;
DROP TABLE t1;

View File

@@ -0,0 +1,41 @@
--source include/galera_cluster.inc
--source include/have_innodb.inc
--source include/have_debug_sync.inc
--source include/galera_have_debug_sync.inc
--connect node_1a, 127.0.0.1, root, , test, $NODE_MYPORT_1
--connect node_1b, 127.0.0.1, root, , test, $NODE_MYPORT_1
set wsrep_sync_wait = 0;
CREATE TABLE t1(a int not null primary key auto_increment, b int) engine=InnoDB;
INSERT INTO t1 VALUES (1,2);
--connection node_1a
--let $victim_id = `SELECT CONNECTION_ID()`
BEGIN;
UPDATE t1 SET b=3 WHERE a=1;
--connection node_1
set debug_sync='wsrep_kill_before_awake_no_mutex SIGNAL before_kill WAIT_FOR continue';
--disable_query_log
--disable_result_log
--send_eval KILL CONNECTION $victim_id
--enable_result_log
--enable_query_log
--connection node_1b
set debug_sync= 'now WAIT_FOR before_kill';
--connection node_2
UPDATE t1 SET b=7 WHERE a=1;
--connection node_1b
--let $wait_condition = SELECT COUNT(*) = 1 FROM INFORMATION_SCHEMA.PROCESSLIST WHERE User = 'system user' AND State LIKE 'Update_rows_log_event%';
--source include/wait_condition.inc
set debug_sync= 'now SIGNAL continue';
--connection node_1
--reap
DROP TABLE t1;
SET DEBUG_SYNC= 'RESET';

View File

@@ -113,6 +113,10 @@ LOCK TABLE t2 WRITE;
--connection node_1
--send CREATE TABLE t1 AS SELECT * FROM t2;
--connection node_1a
--let $wait_condition = SELECT COUNT(*) = 1 FROM information_schema.processlist WHERE STATE LIKE 'Waiting for table metadata lock%'
--source include/wait_condition.inc
--connection node_2
SELECT COUNT(*) = 5 FROM t2;
CREATE TABLE t1 AS SELECT * FROM t2;
@@ -121,7 +125,7 @@ CREATE TABLE t1 AS SELECT * FROM t2;
UNLOCK TABLES;
--connection node_1
--error ER_TABLE_EXISTS_ERROR,ER_LOCK_DEADLOCK
--error ER_TABLE_EXISTS_ERROR,ER_QUERY_INTERRUPTED
--reap
DROP TABLE t1, t2;

View File

@@ -0,0 +1,5 @@
!include ../galera_2nodes.cnf
[mysqld]
log-bin
log-slave-updates

View File

@@ -0,0 +1,69 @@
#
# Verify that transaction which has reached group commit queue
# cannot be killed. If the kill succeeds, assertion for
# wsrep transaction state will fail.
#
# If the bug is present, i.e. wsrep transaction gets killed during
# group commit wait, this test is enough to reproduce the crash
# most of the time.
#
--source include/have_innodb.inc
--source include/have_debug_sync.inc
--source include/galera_cluster.inc
# Connection for KILL commands
--connect node_1_kill, 127.0.0.1, root, , test, $NODE_MYPORT_1
# Connection for sync point control
--connect node_1_ctrl, 127.0.0.1, root, , test, $NODE_MYPORT_1
SET SESSION wsrep_sync_wait = 0;
# Connection for group commit follower
--connect node_1_follower, 127.0.0.1, root, , test, $NODE_MYPORT_1
# Need to disable sync wait to reach commit queue when leader
# is blocked.
SET SESSION wsrep_sync_wait = 0;
--let $follower_id = `SELECT CONNECTION_ID()`
--connection node_1
CREATE TABLE t1 (f1 INT PRIMARY KEY) ENGINE=InnoDB;
SET SESSION DEBUG_SYNC = "commit_before_enqueue SIGNAL leader_before_enqueue_reached WAIT_FOR leader_before_enqueue_continue";
--send INSERT INTO t1 VALUES (1)
--connection node_1_ctrl
SET DEBUG_SYNC = "now WAIT_FOR leader_before_enqueue_reached";
--connection node_1_follower
# SET SESSION DEBUG_SYNC = "group_commit_waiting_for_prior SIGNAL follower_waiting_for_prior_reached WAIT_FOR follower_waiting_for_prior_continue";
--send INSERT INTO t1 VALUES (2);
--connection node_1_ctrl
# TODO: Is it possible to use sync points to enforce group commit to happen?
# The leader will hold commit monitor in commit_before_enqueue sync point,
# which prevents the follower to reach the group commit wait state.
# We now sleep and expect the follower to reach group commit, but this
# may cause false negatives.
--sleep 1
--connection node_1_kill
--echo # Execute KILL QUERY for group commit follower
--disable_query_log
--disable_result_log
# Because it is currently impossible to verify that the
# follower has reached group commit queue, the KILL may
# sometimes return success.
--error 0,ER_KILL_DENIED_ERROR
--eval KILL QUERY $follower_id
--enable_result_log
--enable_query_log
SET DEBUG_SYNC = "now SIGNAL leader_before_enqueue_continue";
--connection node_1_follower
--reap
--connection node_1
--reap
SELECT * FROM t1;
SET DEBUG_SYNC = "RESET";
DROP TABLE t1;

View File

@@ -64,6 +64,7 @@ SELECT COUNT(*) FROM t1;
SET DEBUG_SYNC = 'now SIGNAL wsrep_retry_autocommit_continue';
--connection node_1
--error 0,ER_LOCK_DEADLOCK
--reap
SELECT COUNT(*) FROM t1;

View File

@@ -1,10 +1,9 @@
set global innodb_monitor_disable = All;
select name, if(enabled,'enabled','disabled') status
from information_schema.innodb_metrics;
name status
metadata_table_handles_opened disabled
lock_deadlocks disabled
lock_timeouts disabled
lock_deadlocks enabled
lock_timeouts enabled
lock_rec_lock_waits disabled
lock_table_lock_waits disabled
lock_rec_lock_requests disabled
@@ -14,30 +13,30 @@ lock_rec_locks disabled
lock_table_lock_created disabled
lock_table_lock_removed disabled
lock_table_locks disabled
lock_row_lock_current_waits disabled
lock_row_lock_time disabled
lock_row_lock_time_max disabled
lock_row_lock_waits disabled
lock_row_lock_time_avg disabled
buffer_pool_size disabled
buffer_pool_reads disabled
buffer_pool_read_requests disabled
buffer_pool_write_requests disabled
buffer_pool_wait_free disabled
buffer_pool_read_ahead disabled
buffer_pool_read_ahead_evicted disabled
buffer_pool_pages_total disabled
buffer_pool_pages_misc disabled
buffer_pool_pages_data disabled
buffer_pool_bytes_data disabled
buffer_pool_pages_dirty disabled
buffer_pool_bytes_dirty disabled
buffer_pool_pages_free disabled
buffer_pages_created disabled
buffer_pages_written disabled
buffer_pages_read disabled
buffer_data_reads disabled
buffer_data_written disabled
lock_row_lock_current_waits enabled
lock_row_lock_time enabled
lock_row_lock_time_max enabled
lock_row_lock_waits enabled
lock_row_lock_time_avg enabled
buffer_pool_size enabled
buffer_pool_reads enabled
buffer_pool_read_requests enabled
buffer_pool_write_requests enabled
buffer_pool_wait_free enabled
buffer_pool_read_ahead enabled
buffer_pool_read_ahead_evicted enabled
buffer_pool_pages_total enabled
buffer_pool_pages_misc enabled
buffer_pool_pages_data enabled
buffer_pool_bytes_data enabled
buffer_pool_pages_dirty enabled
buffer_pool_bytes_dirty enabled
buffer_pool_pages_free enabled
buffer_pages_created enabled
buffer_pages_written enabled
buffer_pages_read enabled
buffer_data_reads enabled
buffer_data_written enabled
buffer_flush_batch_scanned disabled
buffer_flush_batch_num_scan disabled
buffer_flush_batch_scanned_per_call disabled
@@ -70,8 +69,8 @@ buffer_flush_background_pages disabled
buffer_LRU_batch_scanned disabled
buffer_LRU_batch_num_scan disabled
buffer_LRU_batch_scanned_per_call disabled
buffer_LRU_batch_flush_total_pages disabled
buffer_LRU_batch_evict_total_pages disabled
buffer_LRU_batch_flush_total_pages enabled
buffer_LRU_batch_evict_total_pages enabled
buffer_LRU_single_flush_failure_count disabled
buffer_LRU_get_free_search disabled
buffer_LRU_search_scanned disabled
@@ -112,21 +111,21 @@ buffer_page_written_blob disabled
buffer_page_written_zblob disabled
buffer_page_written_zblob2 disabled
buffer_page_written_other disabled
os_data_reads disabled
os_data_writes disabled
os_data_fsyncs disabled
os_pending_reads disabled
os_pending_writes disabled
os_log_bytes_written disabled
os_data_reads enabled
os_data_writes enabled
os_data_fsyncs enabled
os_pending_reads enabled
os_pending_writes enabled
os_log_bytes_written enabled
trx_rw_commits disabled
trx_ro_commits disabled
trx_nl_ro_commits disabled
trx_commits_insert_update disabled
trx_rollbacks disabled
trx_rollbacks_savepoint disabled
trx_rseg_history_len disabled
trx_rseg_history_len enabled
trx_undo_slots_used disabled
trx_undo_slots_cached disabled
trx_undo_slots_cached enabled
trx_rseg_current_size disabled
purge_del_mark_records disabled
purge_upd_exist_or_extern_records disabled
@@ -142,9 +141,9 @@ log_lsn_current disabled
log_lsn_checkpoint_age disabled
log_lsn_buf_pool_oldest disabled
log_max_modified_age_async disabled
log_waits disabled
log_write_requests disabled
log_writes disabled
log_waits enabled
log_write_requests enabled
log_writes enabled
compress_pages_compressed disabled
compress_pages_decompressed disabled
compression_pad_increments disabled
@@ -162,34 +161,34 @@ index_page_merge_successful disabled
index_page_reorg_attempts disabled
index_page_reorg_successful disabled
index_page_discards disabled
adaptive_hash_searches disabled
adaptive_hash_searches_btree disabled
adaptive_hash_searches enabled
adaptive_hash_searches_btree enabled
adaptive_hash_pages_added disabled
adaptive_hash_pages_removed disabled
adaptive_hash_rows_added disabled
adaptive_hash_rows_removed disabled
adaptive_hash_rows_deleted_no_hash_entry disabled
adaptive_hash_rows_updated disabled
file_num_open_files disabled
ibuf_merges_insert disabled
ibuf_merges_delete_mark disabled
ibuf_merges_delete disabled
ibuf_merges_discard_insert disabled
ibuf_merges_discard_delete_mark disabled
ibuf_merges_discard_delete disabled
ibuf_merges disabled
ibuf_size disabled
file_num_open_files enabled
ibuf_merges_insert enabled
ibuf_merges_delete_mark enabled
ibuf_merges_delete enabled
ibuf_merges_discard_insert enabled
ibuf_merges_discard_delete_mark enabled
ibuf_merges_discard_delete enabled
ibuf_merges enabled
ibuf_size enabled
innodb_master_thread_sleeps disabled
innodb_activity_count disabled
innodb_activity_count enabled
innodb_master_active_loops disabled
innodb_master_idle_loops disabled
innodb_log_flush_usec disabled
innodb_dict_lru_usec disabled
innodb_dict_lru_count_active disabled
innodb_dict_lru_count_idle disabled
innodb_dblwr_writes disabled
innodb_dblwr_pages_written disabled
innodb_page_size disabled
innodb_dblwr_writes enabled
innodb_dblwr_pages_written enabled
innodb_page_size enabled
ddl_background_drop_indexes disabled
ddl_online_create_index disabled
ddl_pending_alter_table disabled
@@ -199,6 +198,9 @@ icp_attempts disabled
icp_no_match disabled
icp_out_of_range disabled
icp_match disabled
set global innodb_monitor_disable = All;
select name from information_schema.innodb_metrics where enabled;
name
set global innodb_monitor_enable = all;
select name from information_schema.innodb_metrics where not enabled;
name

View File

@@ -26,4 +26,60 @@ UPDATE mysql.innodb_table_stats SET last_update=NULL WHERE table_name='t1';
XA END 'test';
XA ROLLBACK 'test';
DROP TABLE t1;
#
# 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"
#
#
# Testing a non-default format: Field_timestamp0 - UINT4 based
#
SET @@global.mysql56_temporal_format=0;
ALTER TABLE mysql.innodb_table_stats MODIFY last_update TIMESTAMP NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp();
ALTER TABLE mysql.innodb_index_stats MODIFY last_update TIMESTAMP NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp();
SHOW COLUMNS FROM mysql.innodb_table_stats LIKE 'last_update';
Field Type Null Key Default Extra
last_update timestamp /* mariadb-5.3 */ NO current_timestamp() on update current_timestamp()
SHOW COLUMNS FROM mysql.innodb_index_stats LIKE 'last_update';
Field Type Null Key Default Extra
last_update timestamp /* mariadb-5.3 */ NO current_timestamp() on update current_timestamp()
CREATE TABLE t1 (a INT PRIMARY KEY) ENGINE=InnoDB STATS_PERSISTENT=1;
SELECT TIMESTAMPDIFF(DAY,last_update,now())<=1 FROM mysql.innodb_table_stats
WHERE database_name='test' AND table_name='t1';
TIMESTAMPDIFF(DAY,last_update,now())<=1
1
SELECT TIMESTAMPDIFF(DAY,last_update,now())<=1 FROM mysql.innodb_index_stats
WHERE database_name='test' AND table_name='t1' AND stat_name='size';
TIMESTAMPDIFF(DAY,last_update,now())<=1
1
DROP TABLE t1;
#
# Now as the table t1 is dropped, expect no statistics
#
SELECT * FROM mysql.innodb_table_stats
WHERE database_name='test' AND table_name='t1';
database_name table_name last_update n_rows clustered_index_size sum_of_other_index_sizes
SELECT * FROM mysql.innodb_index_stats
WHERE database_name='test' AND table_name='t1' AND stat_name='size';
database_name table_name index_name last_update stat_name stat_value sample_size stat_description
#
# Testing with the default format: Field_timestampf - BINARY(4) based with the UNSIGNED_FLAG
#
SET @@global.mysql56_temporal_format=1;
ALTER TABLE mysql.innodb_table_stats MODIFY last_update TIMESTAMP NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp();
ALTER TABLE mysql.innodb_index_stats MODIFY last_update TIMESTAMP NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp();
SHOW COLUMNS FROM mysql.innodb_table_stats LIKE 'last_update';
Field Type Null Key Default Extra
last_update timestamp NO current_timestamp() on update current_timestamp()
SHOW COLUMNS FROM mysql.innodb_index_stats LIKE 'last_update';
Field Type Null Key Default Extra
last_update timestamp NO current_timestamp() on update current_timestamp()
CREATE TABLE t1 (a INT PRIMARY KEY) ENGINE=InnoDB STATS_PERSISTENT=1;
SELECT TIMESTAMPDIFF(DAY,last_update,now())<=1 FROM mysql.innodb_table_stats
WHERE database_name='test' AND table_name='t1';
TIMESTAMPDIFF(DAY,last_update,now())<=1
1
SELECT TIMESTAMPDIFF(DAY,last_update,now())<=1 FROM mysql.innodb_index_stats
WHERE database_name='test' AND table_name='t1' AND stat_name='size';
TIMESTAMPDIFF(DAY,last_update,now())<=1
1
DROP TABLE t1;
# End of 10.6 tests

View File

@@ -5,12 +5,14 @@
# sys_vars.innodb_monitor_enable_basic
--source include/have_innodb.inc
set global innodb_monitor_disable = All;
# Test turn on/off the monitor counter with "all" option
# By default, they will be off.
select name, if(enabled,'enabled','disabled') status
from information_schema.innodb_metrics;
set global innodb_monitor_disable = All;
select name from information_schema.innodb_metrics where enabled;
# Turn on all monitor counters
set global innodb_monitor_enable = all;

View File

@@ -28,4 +28,57 @@ XA END 'test';
XA ROLLBACK 'test';
DROP TABLE t1;
--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 #
# The following tests demonstrate that these columns:
# - innodb_table_stats.last_update
# - innodb_index_stats.last_update
# have sane values close to NOW(), rather than any garbage,
# with all TIMESTAMP formats.
--echo #
--echo # Testing a non-default format: Field_timestamp0 - UINT4 based
--echo #
SET @@global.mysql56_temporal_format=0;
ALTER TABLE mysql.innodb_table_stats MODIFY last_update TIMESTAMP NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp();
ALTER TABLE mysql.innodb_index_stats MODIFY last_update TIMESTAMP NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp();
SHOW COLUMNS FROM mysql.innodb_table_stats LIKE 'last_update';
SHOW COLUMNS FROM mysql.innodb_index_stats LIKE 'last_update';
CREATE TABLE t1 (a INT PRIMARY KEY) ENGINE=InnoDB STATS_PERSISTENT=1;
SELECT TIMESTAMPDIFF(DAY,last_update,now())<=1 FROM mysql.innodb_table_stats
WHERE database_name='test' AND table_name='t1';
SELECT TIMESTAMPDIFF(DAY,last_update,now())<=1 FROM mysql.innodb_index_stats
WHERE database_name='test' AND table_name='t1' AND stat_name='size';
DROP TABLE t1;
--echo #
--echo # Now as the table t1 is dropped, expect no statistics
--echo #
SELECT * FROM mysql.innodb_table_stats
WHERE database_name='test' AND table_name='t1';
SELECT * FROM mysql.innodb_index_stats
WHERE database_name='test' AND table_name='t1' AND stat_name='size';
--echo #
--echo # Testing with the default format: Field_timestampf - BINARY(4) based with the UNSIGNED_FLAG
--echo #
SET @@global.mysql56_temporal_format=1;
ALTER TABLE mysql.innodb_table_stats MODIFY last_update TIMESTAMP NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp();
ALTER TABLE mysql.innodb_index_stats MODIFY last_update TIMESTAMP NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp();
SHOW COLUMNS FROM mysql.innodb_table_stats LIKE 'last_update';
SHOW COLUMNS FROM mysql.innodb_index_stats LIKE 'last_update';
CREATE TABLE t1 (a INT PRIMARY KEY) ENGINE=InnoDB STATS_PERSISTENT=1;
SELECT TIMESTAMPDIFF(DAY,last_update,now())<=1 FROM mysql.innodb_table_stats
WHERE database_name='test' AND table_name='t1';
SELECT TIMESTAMPDIFF(DAY,last_update,now())<=1 FROM mysql.innodb_index_stats
WHERE database_name='test' AND table_name='t1' AND stat_name='size';
DROP TABLE t1;
--echo # End of 10.6 tests

View File

@@ -0,0 +1,26 @@
CREATE TABLE t1(f1 INT, f2 INT, INDEX(f1))ENGINE=InnoDB
PARTITION BY LIST(f1) (
PARTITION p1 VALUES in (1, 2, 3),
PARTITION p2 VALUES in (4, 5, 6));
INSERT INTO t1 VALUES(1, 1), (1, 1), (6, 1);
connect con1,localhost,root,,,;
START TRANSACTION WITH CONSISTENT SNAPSHOT;
connect con2,localhost,root,,,;
SET DEBUG_SYNC="innodb_rollback_inplace_alter_table SIGNAL default_resume WAIT_FOR alter_resume";
ALTER TABLE t1 ADD UNIQUE INDEX(f1);
connection default;
set DEBUG_SYNC="now WAIT_FOR default_resume";
SET DEBUG_SYNC="innodb_row_update_for_mysql_begin SIGNAL alter_resume WAIT_FOR alter_finish";
DELETE FROM t1;
connection con2;
ERROR 23000: Duplicate entry '1' for key 'f1_2'
SET DEBUG_SYNC="now SIGNAL alter_finish";
connection default;
connection con1;
commit;
connection default;
disconnect con1;
disconnect con2;
InnoDB 0 transactions not purged
drop table t1;
SET DEBUG_SYNC=reset;

View File

@@ -0,0 +1 @@
--innodb_purge_threads=1

View File

@@ -0,0 +1,37 @@
--source include/have_innodb.inc
--source include/have_partition.inc
--source include/have_debug.inc
--source include/have_debug_sync.inc
CREATE TABLE t1(f1 INT, f2 INT, INDEX(f1))ENGINE=InnoDB
PARTITION BY LIST(f1) (
PARTITION p1 VALUES in (1, 2, 3),
PARTITION p2 VALUES in (4, 5, 6));
INSERT INTO t1 VALUES(1, 1), (1, 1), (6, 1);
connect(con1,localhost,root,,,);
START TRANSACTION WITH CONSISTENT SNAPSHOT;
connect(con2,localhost,root,,,);
SET DEBUG_SYNC="innodb_rollback_inplace_alter_table SIGNAL default_resume WAIT_FOR alter_resume";
send ALTER TABLE t1 ADD UNIQUE INDEX(f1);
connection default;
set DEBUG_SYNC="now WAIT_FOR default_resume";
SET DEBUG_SYNC="innodb_row_update_for_mysql_begin SIGNAL alter_resume WAIT_FOR alter_finish";
send DELETE FROM t1;
connection con2;
--error ER_DUP_ENTRY
reap;
SET DEBUG_SYNC="now SIGNAL alter_finish";
connection default;
reap;
connection con1;
commit;
connection default;
disconnect con1;
disconnect con2;
--source ../../innodb/include/wait_all_purged.inc
drop table t1;
SET DEBUG_SYNC=reset;

View File

@@ -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)

View File

@@ -0,0 +1 @@
--plugin-load-add=$TYPE_MYSQL_TIMESTAMP_SO

View File

@@ -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 { };

View File

@@ -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 NULL DEFAULT NULL
) 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 NULL DEFAULT NULL
) 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 NULL DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci
DROP TABLE t1;

View File

@@ -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;

View File

@@ -0,0 +1,108 @@
#
# 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 NOT NULL DEFAULT current_timestamp() ON UPDATE current_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 NOT NULL DEFAULT current_timestamp() ON UPDATE current_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 NOT NULL DEFAULT current_timestamp() ON UPDATE current_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 NOT NULL DEFAULT current_timestamp() ON UPDATE current_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;
#
# Testing MySQL-5.6-alike Field_timestampf: BINARY(4) based, without UNSIGNED_FLAG
#
ALTER TABLE mysql.innodb_table_stats MODIFY last_update TYPE_MYSQL_TIMESTAMP NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp();
ALTER TABLE mysql.innodb_index_stats MODIFY last_update TYPE_MYSQL_TIMESTAMP NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp();
SHOW COLUMNS FROM mysql.innodb_table_stats LIKE 'last_update';
Field Type Null Key Default Extra
last_update type_mysql_timestamp NO current_timestamp() on update current_timestamp()
SHOW COLUMNS FROM mysql.innodb_index_stats LIKE 'last_update';
Field Type Null Key Default Extra
last_update type_mysql_timestamp NO current_timestamp() on update current_timestamp()
CREATE TABLE t1 (a INT PRIMARY KEY) ENGINE=InnoDB STATS_PERSISTENT=1;
SELECT TIMESTAMPDIFF(DAY,last_update,now())<=1 FROM mysql.innodb_table_stats
WHERE database_name='test' AND table_name='t1';
TIMESTAMPDIFF(DAY,last_update,now())<=1
1
SELECT TIMESTAMPDIFF(DAY,last_update,now())<=1 FROM mysql.innodb_index_stats
WHERE database_name='test' AND table_name='t1' AND stat_name='size';
TIMESTAMPDIFF(DAY,last_update,now())<=1
1
DROP TABLE t1;
#
# Now as the table t1 is dropped, expect no statistics
#
SELECT * FROM mysql.innodb_table_stats
WHERE database_name='test' AND table_name='t1';
database_name table_name last_update n_rows clustered_index_size sum_of_other_index_sizes
SELECT * FROM mysql.innodb_index_stats
WHERE database_name='test' AND table_name='t1' AND stat_name='size';
database_name table_name index_name last_update stat_name stat_value sample_size stat_description
#
# Restore the structure of the tables
#
ALTER TABLE mysql.innodb_table_stats MODIFY last_update TIMESTAMP NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp();
ALTER TABLE mysql.innodb_index_stats MODIFY last_update TIMESTAMP NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp();

View File

@@ -0,0 +1,62 @@
--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 NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp();
SHOW CREATE TABLE mysql.innodb_table_stats;
ALTER TABLE mysql.innodb_index_stats MODIFY last_update TYPE_MYSQL_TIMESTAMP NOT NULL DEFAULT current_timestamp() ON UPDATE current_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 NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp();
SHOW CREATE TABLE mysql.innodb_table_stats;
ALTER TABLE mysql.innodb_index_stats MODIFY last_update TIMESTAMP NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp();
SHOW CREATE TABLE mysql.innodb_index_stats;
SET @@global.innodb_stats_persistent=1;
# The following test demonstrate that these columns:
# - innodb_table_stats.last_update
# - innodb_index_stats.last_update
# have sane values close to NOW(), rather than any garbage,
# with MySQL-alike Field_timestampf
--echo #
--echo # Testing MySQL-5.6-alike Field_timestampf: BINARY(4) based, without UNSIGNED_FLAG
--echo #
ALTER TABLE mysql.innodb_table_stats MODIFY last_update TYPE_MYSQL_TIMESTAMP NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp();
ALTER TABLE mysql.innodb_index_stats MODIFY last_update TYPE_MYSQL_TIMESTAMP NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp();
SHOW COLUMNS FROM mysql.innodb_table_stats LIKE 'last_update';
SHOW COLUMNS FROM mysql.innodb_index_stats LIKE 'last_update';
CREATE TABLE t1 (a INT PRIMARY KEY) ENGINE=InnoDB STATS_PERSISTENT=1;
SELECT TIMESTAMPDIFF(DAY,last_update,now())<=1 FROM mysql.innodb_table_stats
WHERE database_name='test' AND table_name='t1';
SELECT TIMESTAMPDIFF(DAY,last_update,now())<=1 FROM mysql.innodb_index_stats
WHERE database_name='test' AND table_name='t1' AND stat_name='size';
DROP TABLE t1;
--echo #
--echo # Now as the table t1 is dropped, expect no statistics
--echo #
SELECT * FROM mysql.innodb_table_stats
WHERE database_name='test' AND table_name='t1';
SELECT * FROM mysql.innodb_index_stats
WHERE database_name='test' AND table_name='t1' AND stat_name='size';
--echo #
--echo # Restore the structure of the tables
--echo #
ALTER TABLE mysql.innodb_table_stats MODIFY last_update TIMESTAMP NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp();
ALTER TABLE mysql.innodb_index_stats MODIFY last_update TIMESTAMP NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp();

View File

@@ -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 <my_global.h>
#include <sql_class.h>
#include <mysql/plugin_data_type.h>
#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;

View File

@@ -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,

View File

@@ -7972,6 +7972,9 @@ Compare_keys handler::compare_key_parts(const Field &old_field,
concurrent accesses. And it's an overkill to take LOCK_plugin and
iterate the whole installed_htons[] array every time.
@note Object victim_thd is not guaranteed to exist after this
function returns.
@param bf_thd brute force THD asking for the abort
@param victim_thd victim THD to be aborted
@@ -7985,6 +7988,8 @@ int ha_abort_transaction(THD *bf_thd, THD *victim_thd, my_bool signal)
if (!WSREP(bf_thd) &&
!(bf_thd->variables.wsrep_OSU_method == WSREP_OSU_RSU &&
wsrep_thd_is_toi(bf_thd))) {
mysql_mutex_unlock(&victim_thd->LOCK_thd_data);
mysql_mutex_unlock(&victim_thd->LOCK_thd_kill);
DBUG_RETURN(0);
}
@@ -7996,6 +8001,8 @@ int ha_abort_transaction(THD *bf_thd, THD *victim_thd, my_bool signal)
else
{
WSREP_WARN("Cannot abort InnoDB transaction");
mysql_mutex_unlock(&victim_thd->LOCK_thd_data);
mysql_mutex_unlock(&victim_thd->LOCK_thd_kill);
}
DBUG_RETURN(0);

View File

@@ -45,6 +45,7 @@
#include "sql_sequence.h"
#include "mem_root_array.h"
#include <utility> // pair
#include <my_attribute.h> /* __attribute__ */
class Alter_info;
class Virtual_column_info;
@@ -1460,9 +1461,9 @@ struct handlerton
const char *query, uint query_length,
const char *db, const char *table_name);
void (*abort_transaction)(handlerton *hton, THD *bf_thd,
THD *victim_thd, my_bool signal);
int (*set_checkpoint)(handlerton *hton, const XID* xid);
void (*abort_transaction)(handlerton *hton, THD *bf_thd, THD *victim_thd,
my_bool signal) __attribute__((nonnull));
int (*set_checkpoint)(handlerton *hton, const XID *xid);
int (*get_checkpoint)(handlerton *hton, XID* xid);
/**
Check if the version of the table matches the version in the .frm

View File

@@ -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);

View File

@@ -1747,6 +1747,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);
@@ -2032,6 +2037,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. */

View File

@@ -32,6 +32,11 @@ extern "C" void wsrep_thd_LOCK(const THD *thd)
mysql_mutex_lock(&thd->LOCK_thd_data);
}
extern "C" int wsrep_thd_TRYLOCK(const THD *thd)
{
return mysql_mutex_trylock(&thd->LOCK_thd_data);
}
extern "C" void wsrep_thd_UNLOCK(const THD *thd)
{
mysql_mutex_unlock(&thd->LOCK_thd_data);
@@ -196,6 +201,7 @@ extern "C" void wsrep_handle_SR_rollback(THD *bf_thd,
/* Note: do not store/reset globals before wsrep_bf_abort() call
to avoid losing BF thd context. */
mysql_mutex_lock(&victim_thd->LOCK_thd_data);
if (!(bf_thd && bf_thd != victim_thd))
{
DEBUG_SYNC(victim_thd, "wsrep_before_SR_rollback");
@@ -208,6 +214,7 @@ extern "C" void wsrep_handle_SR_rollback(THD *bf_thd,
{
wsrep_thd_self_abort(victim_thd);
}
mysql_mutex_unlock(&victim_thd->LOCK_thd_data);
if (bf_thd)
{
wsrep_store_threadvars(bf_thd);
@@ -218,7 +225,7 @@ extern "C" my_bool wsrep_thd_bf_abort(THD *bf_thd, THD *victim_thd,
my_bool signal)
{
mysql_mutex_assert_owner(&victim_thd->LOCK_thd_kill);
mysql_mutex_assert_not_owner(&victim_thd->LOCK_thd_data);
mysql_mutex_assert_owner(&victim_thd->LOCK_thd_data);
my_bool ret= wsrep_bf_abort(bf_thd, victim_thd);
/*
Send awake signal if victim was BF aborted or does not
@@ -227,19 +234,8 @@ extern "C" my_bool wsrep_thd_bf_abort(THD *bf_thd, THD *victim_thd,
*/
if ((ret || !wsrep_on(victim_thd)) && signal)
{
mysql_mutex_lock(&victim_thd->LOCK_thd_data);
if (victim_thd->wsrep_aborter && victim_thd->wsrep_aborter != bf_thd->thread_id)
{
WSREP_DEBUG("victim is killed already by %llu, skipping awake",
victim_thd->wsrep_aborter);
mysql_mutex_unlock(&victim_thd->LOCK_thd_data);
return false;
}
victim_thd->wsrep_aborter= bf_thd->thread_id;
victim_thd->awake_no_mutex(KILL_QUERY_HARD);
mysql_mutex_unlock(&victim_thd->LOCK_thd_data);
} else {
WSREP_DEBUG("wsrep_thd_bf_abort skipped awake, signal %d", signal);
}
@@ -368,25 +364,6 @@ extern "C" ulong wsrep_OSU_method_get(const MYSQL_THD thd)
return(global_system_variables.wsrep_OSU_method);
}
extern "C" bool wsrep_thd_set_wsrep_aborter(THD *bf_thd, THD *victim_thd)
{
mysql_mutex_assert_owner(&victim_thd->LOCK_thd_data);
if (!bf_thd)
{
victim_thd->wsrep_aborter= 0;
WSREP_DEBUG("wsrep_thd_set_wsrep_aborter resetting wsrep_aborter");
return false;
}
if (victim_thd->wsrep_aborter && victim_thd->wsrep_aborter != bf_thd->thread_id)
{
return true;
}
victim_thd->wsrep_aborter= bf_thd->thread_id;
WSREP_DEBUG("wsrep_thd_set_wsrep_aborter setting wsrep_aborter %u",
victim_thd->wsrep_aborter);
return false;
}
extern "C" void wsrep_report_bf_lock_wait(const THD *thd,
unsigned long long trx_id)
{

View File

@@ -1293,6 +1293,11 @@ void THD::init()
wsrep_affected_rows = 0;
m_wsrep_next_trx_id = WSREP_UNDEFINED_TRX_ID;
wsrep_aborter = 0;
wsrep_abort_by_kill = NOT_KILLED;
wsrep_abort_by_kill_err = 0;
#ifndef DBUG_OFF
wsrep_killed_state = 0;
#endif /* DBUG_OFF */
wsrep_desynced_backup_stage= false;
#endif /* WITH_WSREP */
@@ -1641,6 +1646,13 @@ void THD::reset_for_reuse()
#endif
#ifdef WITH_WSREP
wsrep_free_status(this);
wsrep_cs().reset_error();
wsrep_aborter= 0;
wsrep_abort_by_kill= NOT_KILLED;
wsrep_abort_by_kill_err= 0;
#ifndef DBUG_OFF
wsrep_killed_state= 0;
#endif /* DBUG_OFF */
#endif /* WITH_WSREP */
}
@@ -1897,7 +1909,9 @@ void THD::awake_no_mutex(killed_state state_to_set)
}
/* Interrupt target waiting inside a storage engine. */
if (state_to_set != NOT_KILLED && !wsrep_is_bf_aborted(this))
if (state_to_set != NOT_KILLED &&
IF_WSREP(!wsrep_is_bf_aborted(this) && wsrep_abort_by_kill == NOT_KILLED,
true))
ha_kill_query(this, thd_kill_level(this));
abort_current_cond_wait(false);
@@ -2125,6 +2139,17 @@ void THD::reset_killed()
mysql_mutex_unlock(&LOCK_thd_kill);
}
#ifdef WITH_WSREP
if (WSREP_NNULL(this))
{
if (wsrep_abort_by_kill != NOT_KILLED)
{
mysql_mutex_assert_not_owner(&LOCK_thd_kill);
mysql_mutex_lock(&LOCK_thd_kill);
wsrep_abort_by_kill= NOT_KILLED;
wsrep_abort_by_kill_err= 0;
mysql_mutex_unlock(&LOCK_thd_kill);
}
}
mysql_mutex_assert_not_owner(&LOCK_thd_data);
mysql_mutex_lock(&LOCK_thd_data);
wsrep_aborter= 0;

View File

@@ -5382,7 +5382,14 @@ public:
bool wsrep_ignore_table;
/* thread who has started kill for this THD protected by LOCK_thd_data*/
my_thread_id wsrep_aborter;
/* Kill signal used, if thread was killed by manual KILL. Protected by
LOCK_thd_kill. */
std::atomic<killed_state> wsrep_abort_by_kill;
/* */
struct err_info* wsrep_abort_by_kill_err;
#ifndef DBUG_OFF
int wsrep_killed_state;
#endif /* DBUG_OFF */
/* true if BF abort is observed in do_command() right after reading
client's packet, and if the client has sent PS execute command. */
bool wsrep_delayed_BF_abort;

View File

@@ -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; }
/*

View File

@@ -9229,23 +9229,20 @@ kill_one_thread(THD *thd, my_thread_id id, killed_state kill_signal, killed_type
thd->security_ctx->user_matches(tmp->security_ctx))
#endif /* WITH_WSREP */
{
#ifdef WITH_WSREP
DEBUG_SYNC(thd, "before_awake_no_mutex");
if (tmp->wsrep_aborter && tmp->wsrep_aborter != thd->thread_id)
{
/* victim is in hit list already, bail out */
WSREP_DEBUG("victim %lld has wsrep aborter: %lu, skipping awake()",
id, tmp->wsrep_aborter);
error= 0;
#ifdef WITH_WSREP
if (WSREP(tmp))
{
error = wsrep_kill_thd(thd, tmp, kill_signal);
}
else
#endif /* WITH_WSREP */
{
WSREP_DEBUG("kill_one_thread victim: %lld wsrep_aborter %lu"
" by signal %d",
id, tmp->wsrep_aborter, kill_signal);
#endif /* WITH_WSREP */
tmp->awake_no_mutex(kill_signal);
error= 0;
#ifdef WITH_WSREP
}
#endif /* WITH_WSREP */
}
}
else
@@ -9368,18 +9365,6 @@ static
void sql_kill(THD *thd, my_thread_id id, killed_state state, killed_type type)
{
uint error;
#ifdef WITH_WSREP
if (WSREP(thd))
{
WSREP_DEBUG("sql_kill called");
if (thd->wsrep_applier)
{
WSREP_DEBUG("KILL in applying, bailing out here");
return;
}
WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL)
}
#endif /* WITH_WSREP */
if (likely(!(error= kill_one_thread(thd, id, state, type))))
{
if (!thd->killed)
@@ -9389,11 +9374,6 @@ void sql_kill(THD *thd, my_thread_id id, killed_state state, killed_type type)
}
else
my_error(error, MYF(0), id);
#ifdef WITH_WSREP
return;
wsrep_error_label:
my_error(ER_KILL_DENIED_ERROR, MYF(0), (long long) thd->thread_id);
#endif /* WITH_WSREP */
}
@@ -9402,18 +9382,6 @@ sql_kill_user(THD *thd, LEX_USER *user, killed_state state)
{
uint error;
ha_rows rows;
#ifdef WITH_WSREP
if (WSREP(thd))
{
WSREP_DEBUG("sql_kill_user called");
if (thd->wsrep_applier)
{
WSREP_DEBUG("KILL in applying, bailing out here");
return;
}
WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL)
}
#endif /* WITH_WSREP */
switch (error= kill_threads_for_user(thd, user, state, &rows))
{
case 0:
@@ -9429,11 +9397,6 @@ sql_kill_user(THD *thd, LEX_USER *user, killed_state state)
default:
my_error(error, MYF(0));
}
#ifdef WITH_WSREP
return;
wsrep_error_label:
my_error(ER_KILL_DENIED_ERROR, MYF(0), (long long) thd->thread_id);
#endif /* WITH_WSREP */
}

View File

@@ -151,6 +151,7 @@ static struct wsrep_service_st wsrep_handler = {
wsrep_on,
wsrep_prepare_key_for_innodb,
wsrep_thd_LOCK,
wsrep_thd_TRYLOCK,
wsrep_thd_UNLOCK,
wsrep_thd_query,
wsrep_thd_retry_counter,
@@ -179,7 +180,6 @@ static struct wsrep_service_st wsrep_handler = {
wsrep_OSU_method_get,
wsrep_thd_has_ignored_error,
wsrep_thd_set_ignored_error,
wsrep_thd_set_wsrep_aborter,
wsrep_report_bf_lock_wait,
wsrep_thd_kill_LOCK,
wsrep_thd_kill_UNLOCK,

View File

@@ -150,10 +150,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<TABLE_LIST> &tables,
List<Item> &fields, bool send_row,
List<TABLE_LIST> *tables,
List<Item> *fields, bool send_row,
ulonglong select_options, const char *info,
Item *having, List<Item> &all_fields);
Item *having, List<Item> *all_fields);
static COND *build_equal_items(JOIN *join, COND *cond,
COND_EQUAL *inherited,
List<TABLE_LIST> *join_list,
@@ -1301,11 +1301,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<Item> *items)
{
List_iterator_fast <Item> 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).
@@ -1387,52 +1416,43 @@ 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 <Item> 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)
{
mixed_implicit_grouping= true;
break;
}
}
}
table_count= select_lex->leaf_tables.elements;
TABLE_LIST *tbl;
List_iterator_fast<TABLE_LIST> li(select_lex->leaf_tables);
while ((tbl= li++))
mixed_implicit_grouping= true; // mark for future
while (TABLE_LIST *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.
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.
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)
if (tbl->table)
tbl->table->maybe_null= 1;
}
}
}
table_count= select_lex->leaf_tables.elements;
uint real_og_num= og_num;
if (skip_order_by &&
@@ -1453,7 +1473,7 @@ JOIN::prepare(TABLE_LIST *tables_init, COND *conds_init, uint og_num,
{
List_iterator_fast<TABLE_LIST> 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))
@@ -4074,7 +4094,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);
@@ -4469,13 +4489,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)
{
@@ -4758,12 +4772,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;
}
}
@@ -15641,10 +15655,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<TABLE_LIST> *tables)
{
List_iterator<TABLE_LIST> 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<TABLE_LIST> &tables,
List<Item> &fields, bool send_row, ulonglong select_options,
const char *info, Item *having, List<Item> &all_fields)
return_zero_rows(JOIN *join, select_result *result, List<TABLE_LIST> *tables,
List<Item> *fields, bool send_row, ulonglong select_options,
const char *info, Item *having, List<Item> *all_fields)
{
DBUG_ENTER("return_zero_rows");
@@ -15660,24 +15700,15 @@ return_zero_rows(JOIN *join, select_result *result, List<TABLE_LIST> &tables,
Set all tables to have NULL row. This is needed as we will be evaluating
HAVING condition.
*/
List_iterator<TABLE_LIST> 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<Item> it(all_fields);
make_tables_null_complemented(tables);
List_iterator_fast<Item> 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)
@@ -15691,12 +15722,12 @@ return_zero_rows(JOIN *join, select_result *result, List<TABLE_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
}
@@ -15712,31 +15743,25 @@ return_zero_rows(JOIN *join, select_result *result, List<TABLE_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)
{
if (cleared_tables)
{
(*cleared_tables)|= (((table_map) 1) << i);
if (table->s->null_bytes)
{
@@ -15746,15 +15771,14 @@ static void clear_tables(JOIN *join, table_map *cleared_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
@@ -21493,9 +21517,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
@@ -21565,7 +21589,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,
@@ -22040,6 +22064,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;
@@ -23452,11 +23477,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<Item> *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);
}
@@ -23464,10 +23496,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. */
@@ -23492,11 +23526,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<Item> it(*join->fields);
Item *item;
/* No matching rows for group function */
join->clear();
List_iterator_fast<Item> it(*fields);
Item *item;
join->no_rows_in_result_called= 1;
join->clear(&cleared_tables);
while ((item= it++))
item->no_rows_in_result();
}
@@ -23524,6 +23560,13 @@ 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 */
@@ -23841,6 +23884,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;
@@ -23849,7 +23893,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]);
@@ -23872,6 +23916,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;
}
@@ -25603,7 +25649,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);
}
@@ -28007,11 +28053,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;
@@ -28021,6 +28064,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

View File

@@ -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);
@@ -1722,7 +1722,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 &&

View File

@@ -7574,8 +7574,9 @@ extern Named_type_handler<Type_handler_time> type_handler_time;
extern Named_type_handler<Type_handler_time2> type_handler_time2;
extern Named_type_handler<Type_handler_datetime> type_handler_datetime;
extern Named_type_handler<Type_handler_datetime2> type_handler_datetime2;
extern Named_type_handler<Type_handler_timestamp> type_handler_timestamp;
extern Named_type_handler<Type_handler_timestamp2> type_handler_timestamp2;
extern MYSQL_PLUGIN_IMPORT Named_type_handler<Type_handler_timestamp> type_handler_timestamp;
extern MYSQL_PLUGIN_IMPORT Named_type_handler<Type_handler_timestamp2> type_handler_timestamp2;
extern Type_handler_interval_DDhhmmssff type_handler_interval_DDhhmmssff;

View File

@@ -3376,10 +3376,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);

View File

@@ -56,6 +56,11 @@ my_bool wsrep_on(const THD *)
void wsrep_thd_LOCK(const THD *)
{ }
int wsrep_thd_TRYLOCK(const THD *)
{
return 0;
}
void wsrep_thd_UNLOCK(const THD *)
{ }
@@ -154,8 +159,6 @@ void wsrep_thd_set_ignored_error(THD*, my_bool)
{ }
ulong wsrep_OSU_method_get(const THD*)
{ return 0;}
bool wsrep_thd_set_wsrep_aborter(THD*, THD*)
{ return 0;}
void wsrep_report_bf_lock_wait(const THD*,
unsigned long long)

View File

@@ -510,6 +510,7 @@ int Wsrep_high_priority_service::log_dummy_write_set(const wsrep::ws_handle& ws_
m_thd->wait_for_prior_commit();
}
WSREP_DEBUG("checkpointing dummy write set %lld", ws_meta.seqno().get());
wsrep_set_SE_checkpoint(ws_meta.gtid(), wsrep_gtid_server.gtid());
if (!WSREP_EMULATE_BINLOG(m_thd))

View File

@@ -52,6 +52,7 @@
#include "log_event.h"
#include "sql_connect.h"
#include "thread_cache.h"
#include "debug_sync.h"
#include <sstream>
@@ -3107,6 +3108,20 @@ void wsrep_handle_mdl_conflict(MDL_context *requestor_ctx,
request_thd, granted_thd);
ticket->wsrep_report(wsrep_debug);
DEBUG_SYNC(request_thd, "before_wsrep_thd_abort");
DBUG_EXECUTE_IF("sync.before_wsrep_thd_abort", {
const char act[]= "now "
"SIGNAL sync.before_wsrep_thd_abort_reached "
"WAIT_FOR signal.before_wsrep_thd_abort";
DBUG_ASSERT(!debug_sync_set_action(request_thd, STRING_WITH_LEN(act)));
};);
/* Here we will call wsrep_abort_transaction so we should hold
THD::LOCK_thd_data to protect victim from concurrent usage
and THD::LOCK_thd_kill to protect from disconnect or delete.
*/
mysql_mutex_lock(&granted_thd->LOCK_thd_kill);
mysql_mutex_lock(&granted_thd->LOCK_thd_data);
if (wsrep_thd_is_toi(granted_thd) ||
@@ -3118,13 +3133,11 @@ void wsrep_handle_mdl_conflict(MDL_context *requestor_ctx,
wsrep_thd_query(request_thd));
THD_STAGE_INFO(request_thd, stage_waiting_isolation);
ticket->wsrep_report(wsrep_debug);
mysql_mutex_unlock(&granted_thd->LOCK_thd_data);
}
else if (wsrep_thd_is_SR(granted_thd) && !wsrep_thd_is_SR(request_thd))
{
WSREP_MDL_LOG(INFO, "MDL conflict, DDL vs SR",
schema, schema_len, request_thd, granted_thd);
mysql_mutex_unlock(&granted_thd->LOCK_thd_data);
WSREP_DEBUG("wsrep_handle_mdl_conflict DDL vs SR for %s",
wsrep_thd_query(request_thd));
THD_STAGE_INFO(request_thd, stage_waiting_isolation);
@@ -3136,6 +3149,7 @@ void wsrep_handle_mdl_conflict(MDL_context *requestor_ctx,
request_thd, granted_thd);
ticket->wsrep_report(true);
mysql_mutex_unlock(&granted_thd->LOCK_thd_data);
mysql_mutex_unlock(&granted_thd->LOCK_thd_kill);
unireg_abort(1);
}
}
@@ -3146,7 +3160,6 @@ void wsrep_handle_mdl_conflict(MDL_context *requestor_ctx,
wsrep_thd_query(request_thd));
THD_STAGE_INFO(request_thd, stage_waiting_ddl);
ticket->wsrep_report(wsrep_debug);
mysql_mutex_unlock(&granted_thd->LOCK_thd_data);
if (granted_thd->current_backup_stage != BACKUP_FINISHED &&
wsrep_check_mode(WSREP_MODE_BF_MARIABACKUP))
{
@@ -3160,7 +3173,6 @@ void wsrep_handle_mdl_conflict(MDL_context *requestor_ctx,
wsrep_thd_query(request_thd));
THD_STAGE_INFO(request_thd, stage_waiting_isolation);
ticket->wsrep_report(wsrep_debug);
mysql_mutex_unlock(&granted_thd->LOCK_thd_data);
wsrep_abort_thd(request_thd, granted_thd, 1);
}
else
@@ -3174,7 +3186,6 @@ void wsrep_handle_mdl_conflict(MDL_context *requestor_ctx,
if (granted_thd->wsrep_trx().active())
{
mysql_mutex_unlock(&granted_thd->LOCK_thd_data);
wsrep_abort_thd(request_thd, granted_thd, 1);
}
else
@@ -3183,10 +3194,9 @@ void wsrep_handle_mdl_conflict(MDL_context *requestor_ctx,
Granted_thd is likely executing with wsrep_on=0. If the requesting
thd is BF, BF abort and wait.
*/
mysql_mutex_unlock(&granted_thd->LOCK_thd_data);
if (wsrep_thd_is_BF(request_thd, FALSE))
{
granted_thd->awake_no_mutex(KILL_QUERY_HARD);
ha_abort_transaction(request_thd, granted_thd, TRUE);
}
else
@@ -3195,10 +3205,14 @@ void wsrep_handle_mdl_conflict(MDL_context *requestor_ctx,
schema, schema_len,
request_thd, granted_thd);
ticket->wsrep_report(true);
mysql_mutex_unlock(&granted_thd->LOCK_thd_data);
mysql_mutex_unlock(&granted_thd->LOCK_thd_kill);
unireg_abort(1);
}
}
}
mysql_mutex_unlock(&granted_thd->LOCK_thd_data);
mysql_mutex_unlock(&granted_thd->LOCK_thd_kill);
}
else
{
@@ -3210,13 +3224,17 @@ void wsrep_handle_mdl_conflict(MDL_context *requestor_ctx,
static bool abort_replicated(THD *thd)
{
bool ret_code= false;
mysql_mutex_lock(&thd->LOCK_thd_kill);
mysql_mutex_lock(&thd->LOCK_thd_data);
if (thd->wsrep_trx().state() == wsrep::transaction::s_committing)
{
WSREP_DEBUG("aborting replicated trx: %llu", (ulonglong)(thd->real_id));
(void)wsrep_abort_thd(thd, thd, TRUE);
wsrep_abort_thd(thd, thd, TRUE);
ret_code= true;
}
mysql_mutex_unlock(&thd->LOCK_thd_data);
mysql_mutex_unlock(&thd->LOCK_thd_kill);
return ret_code;
}

View File

@@ -148,9 +148,13 @@ void Wsrep_server_service::release_high_priority_service(wsrep::high_priority_se
wsrep_delete_threadvars();
}
void Wsrep_server_service::background_rollback(wsrep::client_state& client_state)
void Wsrep_server_service::background_rollback(
wsrep::unique_lock<wsrep::mutex> &lock WSREP_UNUSED,
wsrep::client_state &client_state)
{
Wsrep_client_state& cs= static_cast<Wsrep_client_state&>(client_state);
DBUG_ASSERT(lock.owns_lock());
Wsrep_client_state &cs= static_cast<Wsrep_client_state &>(client_state);
mysql_mutex_assert_owner(&cs.thd()->LOCK_thd_data);
wsrep_fire_rollbacker(cs.thd());
}

View File

@@ -46,7 +46,8 @@ public:
void release_high_priority_service(wsrep::high_priority_service*);
void background_rollback(wsrep::client_state&);
void background_rollback(wsrep::unique_lock<wsrep::mutex> &,
wsrep::client_state &);
void bootstrap();
void log_message(enum wsrep::log::level, const char*);

View File

@@ -307,48 +307,9 @@ void wsrep_fire_rollbacker(THD *thd)
}
}
int wsrep_abort_thd(THD *bf_thd,
THD *victim_thd,
my_bool signal)
static bool wsrep_bf_abort_low(THD *bf_thd, THD *victim_thd)
{
DBUG_ENTER("wsrep_abort_thd");
mysql_mutex_lock(&victim_thd->LOCK_thd_data);
/* Note that when you use RSU node is desynced from cluster, thus WSREP(thd)
might not be true.
*/
if ((WSREP_NNULL(bf_thd) ||
((WSREP_ON || bf_thd->variables.wsrep_OSU_method == WSREP_OSU_RSU) &&
wsrep_thd_is_toi(bf_thd))) &&
!wsrep_thd_is_aborting(victim_thd))
{
WSREP_DEBUG("wsrep_abort_thd, by: %llu, victim: %llu",
(long long)bf_thd->real_id, (long long)victim_thd->real_id);
mysql_mutex_unlock(&victim_thd->LOCK_thd_data);
ha_abort_transaction(bf_thd, victim_thd, signal);
DBUG_RETURN(1);
}
else
{
WSREP_DEBUG("wsrep_abort_thd not effective: bf %llu victim %llu "
"wsrep %d wsrep_on %d RSU %d TOI %d aborting %d",
(long long)bf_thd->real_id, (long long)victim_thd->real_id,
WSREP_NNULL(bf_thd), WSREP_ON,
bf_thd->variables.wsrep_OSU_method == WSREP_OSU_RSU,
wsrep_thd_is_toi(bf_thd),
wsrep_thd_is_aborting(victim_thd));
}
mysql_mutex_unlock(&victim_thd->LOCK_thd_data);
DBUG_RETURN(1);
}
bool wsrep_bf_abort(THD* bf_thd, THD* victim_thd)
{
WSREP_LOG_THD(bf_thd, "BF aborter before");
WSREP_LOG_THD(victim_thd, "victim before");
mysql_mutex_assert_owner(&victim_thd->LOCK_thd_data);
#ifdef ENABLED_DEBUG_SYNC
DBUG_EXECUTE_IF("sync.wsrep_bf_abort",
@@ -362,6 +323,85 @@ bool wsrep_bf_abort(THD* bf_thd, THD* victim_thd)
};);
#endif
wsrep::seqno bf_seqno(bf_thd->wsrep_trx().ws_meta().seqno());
bool ret;
{
/* Adopt the lock, it is being held by the caller. */
Wsrep_mutex wsm{&victim_thd->LOCK_thd_data};
wsrep::unique_lock<wsrep::mutex> lock{wsm, std::adopt_lock};
if (wsrep_thd_is_toi(bf_thd))
{
ret= victim_thd->wsrep_cs().total_order_bf_abort(lock, bf_seqno);
}
else
{
DBUG_ASSERT(WSREP(victim_thd) ? victim_thd->wsrep_trx().active() : 1);
ret= victim_thd->wsrep_cs().bf_abort(lock, bf_seqno);
}
if (ret)
{
/* BF abort should be allowed only once by wsrep-lib.*/
DBUG_ASSERT(victim_thd->wsrep_aborter == 0);
victim_thd->wsrep_aborter= bf_thd->thread_id;
wsrep_bf_aborts_counter++;
}
lock.release(); /* No unlock at the end of the scope. */
}
/* Sanity check for wsrep-lib calls to return with LOCK_thd_data held. */
mysql_mutex_assert_owner(&victim_thd->LOCK_thd_data);
return ret;
}
void wsrep_abort_thd(THD *bf_thd,
THD *victim_thd,
my_bool signal)
{
DBUG_ENTER("wsrep_abort_thd");
mysql_mutex_assert_owner(&victim_thd->LOCK_thd_kill);
mysql_mutex_assert_owner(&victim_thd->LOCK_thd_data);
/* Note that when you use RSU node is desynced from cluster, thus WSREP(thd)
might not be true.
*/
if ((WSREP(bf_thd)
|| ((WSREP_ON || bf_thd->variables.wsrep_OSU_method == WSREP_OSU_RSU)
&& wsrep_thd_is_toi(bf_thd))
|| bf_thd->lex->sql_command == SQLCOM_KILL)
&& !wsrep_thd_is_aborting(victim_thd) &&
wsrep_bf_abort_low(bf_thd, victim_thd) &&
!victim_thd->wsrep_cs().is_rollbacker_active())
{
WSREP_DEBUG("wsrep_abort_thd, by: %llu, victim: %llu",
(long long)bf_thd->real_id, (long long)victim_thd->real_id);
victim_thd->awake_no_mutex(KILL_QUERY_HARD);
ha_abort_transaction(bf_thd, victim_thd, signal);
}
else
{
WSREP_DEBUG("wsrep_abort_thd not effective: bf %llu victim %llu "
"wsrep %d wsrep_on %d RSU %d TOI %d aborting %d",
(long long)bf_thd->real_id, (long long)victim_thd->real_id,
WSREP_NNULL(bf_thd), WSREP_ON,
bf_thd->variables.wsrep_OSU_method == WSREP_OSU_RSU,
wsrep_thd_is_toi(bf_thd),
wsrep_thd_is_aborting(victim_thd));
}
DBUG_VOID_RETURN;
}
bool wsrep_bf_abort(THD* bf_thd, THD* victim_thd)
{
WSREP_LOG_THD(bf_thd, "BF aborter before");
WSREP_LOG_THD(victim_thd, "victim before");
mysql_mutex_assert_owner(&victim_thd->LOCK_thd_data);
if (WSREP(victim_thd) && !victim_thd->wsrep_trx().active())
{
WSREP_DEBUG("wsrep_bf_abort, BF abort for non active transaction."
@@ -384,30 +424,84 @@ bool wsrep_bf_abort(THD* bf_thd, THD* victim_thd)
wsrep_check_mode(WSREP_MODE_BF_MARIABACKUP))
{
WSREP_DEBUG("killing connection for non wsrep session");
mysql_mutex_lock(&victim_thd->LOCK_thd_data);
victim_thd->awake_no_mutex(KILL_CONNECTION);
mysql_mutex_unlock(&victim_thd->LOCK_thd_data);
}
return false;
}
bool ret;
wsrep::seqno bf_seqno(bf_thd->wsrep_trx().ws_meta().seqno());
return wsrep_bf_abort_low(bf_thd, victim_thd);
}
if (wsrep_thd_is_toi(bf_thd))
uint wsrep_kill_thd(THD *thd, THD *victim_thd, killed_state kill_signal)
{
DBUG_ENTER("wsrep_kill_thd");
DBUG_ASSERT(WSREP(victim_thd));
mysql_mutex_assert_owner(&victim_thd->LOCK_thd_kill);
mysql_mutex_assert_owner(&victim_thd->LOCK_thd_data);
using trans= wsrep::transaction;
auto trx_state= victim_thd->wsrep_trx().state();
#ifndef DBUG_OFF
victim_thd->wsrep_killed_state= trx_state;
#endif /* DBUG_OFF */
/*
Already killed or in commit codepath. Mark the victim as killed,
the killed status will be restored in wsrep_after_commit() and
will be processed after the commit is over. In case of multiple
KILLs happened on commit codepath, the last one will be effective.
*/
if (victim_thd->wsrep_abort_by_kill ||
trx_state == trans::s_preparing ||
trx_state == trans::s_committing ||
trx_state == trans::s_ordered_commit)
{
ret= victim_thd->wsrep_cs().total_order_bf_abort(bf_seqno);
victim_thd->wsrep_abort_by_kill= kill_signal;
DBUG_RETURN(0);
}
else
/*
Mark killed victim_thd with kill_signal so that awake_no_mutex does
not dive into storage engine. We use ha_abort_transaction()
to do the storage engine part for wsrep THDs.
*/
DEBUG_SYNC(thd, "wsrep_kill_before_awake_no_mutex");
victim_thd->wsrep_abort_by_kill= kill_signal;
victim_thd->awake_no_mutex(kill_signal);
/* ha_abort_transaction() releases tmp->LOCK_thd_kill, so tmp
is not safe to access anymore. */
ha_abort_transaction(thd, victim_thd, 1);
DBUG_RETURN(0);
}
void wsrep_backup_kill_for_commit(THD *thd)
{
DBUG_ASSERT(WSREP(thd));
mysql_mutex_assert_owner(&thd->LOCK_thd_kill);
DBUG_ASSERT(thd->killed != NOT_KILLED);
mysql_mutex_lock(&thd->LOCK_thd_data);
/* If the transaction will roll back, keep the killed state.
For must replay, the replay will happen in different THD context
which is high priority and cannot be killed. The owning thread will
pick the killed state in after statement processing. */
if (thd->wsrep_trx().state() != wsrep::transaction::s_cert_failed &&
thd->wsrep_trx().state() != wsrep::transaction::s_must_abort &&
thd->wsrep_trx().state() != wsrep::transaction::s_aborting &&
thd->wsrep_trx().state() != wsrep::transaction::s_must_replay)
{
DBUG_ASSERT(WSREP(victim_thd) ? victim_thd->wsrep_trx().active() : 1);
ret= victim_thd->wsrep_cs().bf_abort(bf_seqno);
thd->wsrep_abort_by_kill= thd->killed;
thd->wsrep_abort_by_kill_err= thd->killed_err;
thd->killed= NOT_KILLED;
thd->killed_err= 0;
}
if (ret)
{
wsrep_bf_aborts_counter++;
}
return ret;
mysql_mutex_unlock(&thd->LOCK_thd_data);
}
void wsrep_restore_kill_after_commit(THD *thd)
{
DBUG_ASSERT(WSREP(thd));
mysql_mutex_assert_owner(&thd->LOCK_thd_kill);
thd->killed= thd->wsrep_abort_by_kill;
thd->killed_err= thd->wsrep_abort_by_kill_err;
thd->wsrep_abort_by_kill= NOT_KILLED;
thd->wsrep_abort_by_kill_err= 0;
}
int wsrep_create_threadvars()

View File

@@ -88,10 +88,39 @@ bool wsrep_create_appliers(long threads, bool mutex_protected=false);
void wsrep_create_rollbacker();
bool wsrep_bf_abort(THD* bf_thd, THD* victim_thd);
int wsrep_abort_thd(THD *bf_thd,
/*
Abort transaction for victim_thd. This function is called from
MDL BF abort codepath.
*/
void wsrep_abort_thd(THD *bf_thd,
THD *victim_thd,
my_bool signal) __attribute__((nonnull(1,2)));
/**
Kill wsrep connection with kill_signal. Object thd is not
guaranteed to exist anymore when this function returns.
Asserts that the caller holds victim_thd->LOCK_thd_kill,
victim_thd->LOCK_thd_data.
@param thd THD object for connection that executes the KILL.
@param victim_thd THD object for connection to be killed.
@param kill_signal Kill signal.
@return Zero if the kill was successful, otherwise non-zero error code.
*/
uint wsrep_kill_thd(THD *thd, THD *victim_thd, killed_state kill_signal);
/*
Backup kill status for commit.
*/
void wsrep_backup_kill_for_commit(THD *);
/*
Restore KILL status after commit.
*/
void wsrep_restore_kill_after_commit(THD *);
/*
Helper methods to deal with thread local storage.
The purpose of these methods is to hide the details of thread

View File

@@ -256,6 +256,11 @@ static inline int wsrep_before_prepare(THD* thd, bool all)
thd->wsrep_trx().ws_meta().gtid(),
wsrep_gtid_server.gtid());
}
mysql_mutex_lock(&thd->LOCK_thd_kill);
if (thd->killed) wsrep_backup_kill_for_commit(thd);
mysql_mutex_unlock(&thd->LOCK_thd_kill);
DBUG_RETURN(ret);
}
@@ -325,6 +330,11 @@ static inline int wsrep_before_commit(THD* thd, bool all)
wsrep_gtid_server.gtid());
wsrep_register_for_group_commit(thd);
}
mysql_mutex_lock(&thd->LOCK_thd_kill);
if (thd->killed) wsrep_backup_kill_for_commit(thd);
mysql_mutex_unlock(&thd->LOCK_thd_kill);
DBUG_RETURN(ret);
}
@@ -343,7 +353,8 @@ static inline int wsrep_before_commit(THD* thd, bool all)
static inline int wsrep_ordered_commit(THD* thd, bool all)
{
DBUG_ENTER("wsrep_ordered_commit");
WSREP_DEBUG("wsrep_ordered_commit: %d", wsrep_is_real(thd, all));
WSREP_DEBUG("wsrep_ordered_commit: %d %lld", wsrep_is_real(thd, all),
(long long) wsrep_thd_trx_seqno(thd));
DBUG_ASSERT(wsrep_run_commit_hook(thd, all));
DBUG_RETURN(thd->wsrep_cs().ordered_commit());
}
@@ -451,10 +462,18 @@ int wsrep_after_statement(THD* thd)
wsrep::to_c_string(thd->wsrep_cs().state()),
wsrep::to_c_string(thd->wsrep_cs().mode()),
wsrep::to_c_string(thd->wsrep_cs().transaction().state()));
DBUG_RETURN((thd->wsrep_cs().state() != wsrep::client_state::s_none &&
int ret= ((thd->wsrep_cs().state() != wsrep::client_state::s_none &&
thd->wsrep_cs().mode() == Wsrep_client_state::m_local) &&
!thd->internal_transaction() ?
thd->wsrep_cs().after_statement() : 0);
if (wsrep_is_active(thd))
{
mysql_mutex_lock(&thd->LOCK_thd_kill);
wsrep_restore_kill_after_commit(thd);
mysql_mutex_unlock(&thd->LOCK_thd_kill);
}
DBUG_RETURN(ret);
}
static inline void wsrep_after_apply(THD* thd)

View File

@@ -2140,6 +2140,8 @@ void buf_page_free(fil_space_t *space, uint32_t page, mtr_t *mtr)
}
block->page.lock.x_lock();
if (block->page.is_ibuf_exist())
ibuf_merge_or_delete_for_page(nullptr, page_id, block->page.zip_size());
#ifdef BTR_CUR_HASH_ADAPT
if (block->index)
btr_search_drop_page_hash_index(block, false);

View File

@@ -372,7 +372,7 @@ void buf_dblwr_t::recover()
const uint32_t space_id= page_get_space_id(page);
const page_id_t page_id(space_id, page_no);
if (recv_sys.lsn < lsn)
if (recv_sys.scanned_lsn < lsn)
{
ib::info() << "Ignoring a doublewrite copy of page " << page_id
<< " with future log sequence number " << lsn;

View File

@@ -966,12 +966,20 @@ uint32_t fil_space_t::flush_freed(bool writable)
mysql_mutex_assert_not_owner(&buf_pool.flush_list_mutex);
mysql_mutex_assert_not_owner(&buf_pool.mutex);
for (;;)
{
freed_range_mutex.lock();
if (freed_ranges.empty() || log_sys.get_flushed_lsn() < get_last_freed_lsn())
if (freed_ranges.empty())
{
freed_range_mutex.unlock();
return 0;
}
const lsn_t flush_lsn= last_freed_lsn;
if (log_sys.get_flushed_lsn() >= flush_lsn)
break;
freed_range_mutex.unlock();
log_write_up_to(flush_lsn, true);
}
const unsigned physical{physical_size()};
@@ -2430,6 +2438,7 @@ static void buf_flush_page_cleaner()
else if (buf_pool.ran_out())
{
buf_pool.page_cleaner_set_idle(false);
buf_pool.get_oldest_modification(0);
mysql_mutex_unlock(&buf_pool.flush_list_mutex);
n= srv_max_io_capacity;
mysql_mutex_lock(&buf_pool.mutex);
@@ -2583,6 +2592,7 @@ ATTRIBUTE_COLD void buf_flush_page_cleaner_init()
/** Flush the buffer pool on shutdown. */
ATTRIBUTE_COLD void buf_flush_buffer_pool()
{
ut_ad(!os_aio_pending_reads());
ut_ad(!buf_page_cleaner_is_active);
ut_ad(!buf_flush_sync_lsn);

View File

@@ -1086,7 +1086,11 @@ static bool buf_LRU_block_remove_hashed(buf_page_t *bpage, const page_id_t id,
ut_a(!zip || !bpage->oldest_modification());
ut_ad(bpage->zip_size());
/* Skip consistency checks if the page was freed.
In recovery, we could get a sole FREE_PAGE record
and nothing else, for a ROW_FORMAT=COMPRESSED page.
Its contents would be garbage. */
if (!bpage->is_freed())
switch (fil_page_get_type(page)) {
case FIL_PAGE_TYPE_ALLOCATED:
case FIL_PAGE_INODE:
@@ -1217,6 +1221,7 @@ void buf_pool_t::corrupted_evict(buf_page_t *bpage, uint32_t state)
buf_pool_t::hash_chain &chain= buf_pool.page_hash.cell_get(id.fold());
page_hash_latch &hash_lock= buf_pool.page_hash.lock_get(chain);
recv_sys.free_corrupted_page(id);
mysql_mutex_lock(&mutex);
hash_lock.lock();
@@ -1241,8 +1246,6 @@ void buf_pool_t::corrupted_evict(buf_page_t *bpage, uint32_t state)
buf_LRU_block_free_hashed_page(reinterpret_cast<buf_block_t*>(bpage));
mysql_mutex_unlock(&mutex);
recv_sys.free_corrupted_page(id);
}
/** Update buf_pool.LRU_old_ratio.

View File

@@ -265,9 +265,6 @@ buf_read_page_low(
buf_page_t* bpage;
if (buf_dblwr.is_inside(page_id)) {
ib::error() << "Trying to read doublewrite buffer page "
<< page_id;
ut_ad(0);
space->release();
return DB_PAGE_CORRUPTED;
}
@@ -525,7 +522,7 @@ buf_read_ahead_linear(const page_id_t page_id, ulint zip_size, bool ibuf)
/* We will check that almost all pages in the area have been accessed
in the desired order. */
const bool descending= page_id == low;
const bool descending= page_id != low;
if (!descending && page_id != high_1)
/* This is not a border page of the area */
@@ -555,7 +552,7 @@ fail:
uint32_t{buf_pool.read_ahead_area});
page_id_t new_low= low, new_high_1= high_1;
unsigned prev_accessed= 0;
for (page_id_t i= low; i != high_1; ++i)
for (page_id_t i= low; i <= high_1; ++i)
{
buf_pool_t::hash_chain &chain= buf_pool.page_hash.cell_get(i.fold());
transactional_shared_lock_guard<page_hash_latch> g
@@ -583,12 +580,21 @@ failed:
if (prev == FIL_NULL || next == FIL_NULL)
goto fail;
page_id_t id= page_id;
if (descending && next - 1 == page_id.page_no())
id.set_page_no(prev);
else if (!descending && prev + 1 == page_id.page_no())
id.set_page_no(next);
if (descending)
{
if (id == high_1)
++id;
else if (next - 1 != page_id.page_no())
goto fail;
else
goto fail; /* Successor or predecessor not in the right order */
id.set_page_no(prev);
}
else
{
if (prev + 1 != page_id.page_no())
goto fail;
id.set_page_no(next);
}
new_low= id - (id.page_no() % buf_read_ahead_area);
new_high_1= new_low + (buf_read_ahead_area - 1);
@@ -620,7 +626,7 @@ failed:
/* If we got this far, read-ahead can be sensible: do it */
count= 0;
for (ulint ibuf_mode= ibuf ? BUF_READ_IBUF_PAGES_ONLY : BUF_READ_ANY_PAGE;
new_low != new_high_1; ++new_low)
new_low <= new_high_1; ++new_low)
{
if (ibuf_bitmap_page(new_low, zip_size))
continue;
@@ -649,60 +655,35 @@ failed:
return count;
}
/** @return whether a page has been freed */
inline bool fil_space_t::is_freed(uint32_t page)
/** Schedule a page for recovery.
@param space tablespace
@param page_id page identifier
@param recs log records
@param init page initialization, or nullptr if the page needs to be read */
void buf_read_recover(fil_space_t *space, const page_id_t page_id,
page_recv_t &recs, recv_init *init)
{
std::lock_guard<std::mutex> freed_lock(freed_range_mutex);
return freed_ranges.contains(page);
}
/** Issues read requests for pages which recovery wants to read in.
@param space_id tablespace identifier
@param page_nos page numbers to read, in ascending order */
void buf_read_recv_pages(uint32_t space_id, st_::span<uint32_t> page_nos)
{
fil_space_t* space = fil_space_t::get(space_id);
if (!space) {
/* The tablespace is missing or unreadable: do nothing */
return;
}
const ulint zip_size = space->zip_size();
for (ulint i = 0; i < page_nos.size(); i++) {
/* Ignore if the page already present in freed ranges. */
if (space->is_freed(page_nos[i])) {
continue;
}
const page_id_t cur_page_id(space_id, page_nos[i]);
ulint limit = 0;
for (ulint j = 0; j < buf_pool.n_chunks; j++) {
limit += buf_pool.chunks[j].size / 2;
}
if (os_aio_pending_reads() >= limit) {
os_aio_wait_until_no_pending_reads(false);
}
ut_ad(space->id == page_id.space());
space->reacquire();
switch (buf_read_page_low(space, false, BUF_READ_ANY_PAGE,
cur_page_id, zip_size, true)) {
case DB_SUCCESS: case DB_SUCCESS_LOCKED_REC:
break;
default:
const ulint zip_size= space->zip_size();
if (init)
{
if (buf_page_t *bpage= buf_page_init_for_read(BUF_READ_ANY_PAGE, page_id,
zip_size, true))
{
ut_ad(bpage->in_file());
os_fake_read(IORequest{bpage, (buf_tmp_buffer_t*) &recs,
UT_LIST_GET_FIRST(space->chain),
IORequest::READ_ASYNC}, ptrdiff_t(init));
}
}
else if (dberr_t err= buf_read_page_low(space, false, BUF_READ_ANY_PAGE,
page_id, zip_size, true))
{
if (err != DB_SUCCESS_LOCKED_REC)
sql_print_error("InnoDB: Recovery failed to read page "
UINT32PF " from %s",
cur_page_id.page_no(),
space->chain.start->name);
page_id.page_no(), space->chain.start->name);
}
}
DBUG_PRINT("ib_buf", ("recovery read (%zu pages) for %s",
page_nos.size(), space->chain.start->name));
space->release();
}

View File

@@ -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},

View File

@@ -447,7 +447,9 @@ static bool fil_node_open_file(fil_node_t *node)
}
}
return fil_node_open_file_low(node);
/* The node can be opened beween releasing and acquiring fil_system.mutex
in the above code */
return node->is_open() || fil_node_open_file_low(node);
}
/** Close the file handle. */
@@ -1953,8 +1955,8 @@ err_exit:
FIL_TYPE_TABLESPACE,
crypt_data, mode, true)) {
fil_node_t* node = space->add(path, file, size, false, true);
mysql_mutex_unlock(&fil_system.mutex);
IF_WIN(node->find_metadata(), node->find_metadata(file, true));
mysql_mutex_unlock(&fil_system.mutex);
mtr.start();
mtr.set_named_space(space);
ut_a(fsp_header_init(space, size, &mtr) == DB_SUCCESS);
@@ -2775,38 +2777,41 @@ func_exit:
#include <tpool.h>
/** Callback for AIO completion */
void fil_aio_callback(const IORequest &request)
void IORequest::write_complete() const
{
ut_ad(fil_validate_skip());
ut_ad(request.node);
ut_ad(node);
ut_ad(is_write());
if (!request.bpage)
if (!bpage)
{
ut_ad(!srv_read_only_mode);
if (request.type == IORequest::DBLWR_BATCH)
buf_dblwr.flush_buffered_writes_completed(request);
if (type == IORequest::DBLWR_BATCH)
buf_dblwr.flush_buffered_writes_completed(*this);
else
ut_ad(request.type == IORequest::WRITE_ASYNC);
write_completed:
request.node->complete_write();
}
else if (request.is_write())
{
buf_page_write_complete(request);
goto write_completed;
ut_ad(type == IORequest::WRITE_ASYNC);
}
else
{
ut_ad(request.is_read());
buf_page_write_complete(*this);
node->complete_write();
node->space->release();
}
void IORequest::read_complete() const
{
ut_ad(fil_validate_skip());
ut_ad(node);
ut_ad(is_read());
ut_ad(bpage);
/* IMPORTANT: since i/o handling for reads will read also the insert
buffer in fil_system.sys_space, we have to be very careful not to
introduce deadlocks. We never close fil_system.sys_space data
files and never issue asynchronous reads of change buffer pages. */
const page_id_t id(request.bpage->id());
introduce deadlocks. We never close fil_system.sys_space data files
and never issue asynchronous reads of change buffer pages. */
const page_id_t id(bpage->id());
if (dberr_t err= request.bpage->read_complete(*request.node))
if (dberr_t err= bpage->read_complete(*node))
{
if (recv_recovery_is_on() && !srv_force_recovery)
{
@@ -2817,11 +2822,10 @@ write_completed:
if (err != DB_FAIL)
ib::error() << "Failed to read page " << id.page_no()
<< " from file '" << request.node->name << "': " << err;
}
<< " from file '" << node->name << "': " << err;
}
request.node->space->release();
node->space->release();
}
/** Flush to disk the writes in file spaces of the given type

View File

@@ -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<uint32_t>(offset));
xdes_set_free<true>(*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;
}
if (err == DB_SUCCESS) {
err = fsp_free_extent(space, offset, mtr);
if (UNIV_UNLIKELY(err != DB_SUCCESS)) {
}
}
return err;
}
}
mtr->free(*space, static_cast<uint32_t>(offset));
xdes_set_free<true>(*xdes, descr, offset % FSP_EXTENT_SIZE, mtr);
return DB_SUCCESS;
}
/** @return Number of segment inodes which fit on a single page */

View File

@@ -945,7 +945,8 @@ static SHOW_VAR innodb_status_variables[]= {
{"buffer_pool_read_ahead", &buf_pool.stat.n_ra_pages_read, SHOW_SIZE_T},
{"buffer_pool_read_ahead_evicted",
&buf_pool.stat.n_ra_pages_evicted, SHOW_SIZE_T},
{"buffer_pool_read_requests", &buf_pool.stat.n_page_gets, SHOW_SIZE_T},
{"buffer_pool_read_requests",
&export_vars.innodb_buffer_pool_read_requests, SHOW_SIZE_T},
{"buffer_pool_reads", &buf_pool.stat.n_pages_read, SHOW_SIZE_T},
{"buffer_pool_wait_free", &buf_pool.stat.LRU_waits, SHOW_SIZE_T},
{"buffer_pool_write_requests", &buf_pool.flush_list_requests, SHOW_SIZE_T},
@@ -1917,8 +1918,9 @@ static void innodb_disable_internal_writes(bool disable)
sst_enable_innodb_writes();
}
static void wsrep_abort_transaction(handlerton*, THD *, THD *, my_bool);
static int innobase_wsrep_set_checkpoint(handlerton* hton, const XID* xid);
static void wsrep_abort_transaction(handlerton *, THD *, THD *, my_bool)
__attribute__((nonnull));
static int innobase_wsrep_set_checkpoint(handlerton *hton, const XID *xid);
static int innobase_wsrep_get_checkpoint(handlerton* hton, XID* xid);
#endif /* WITH_WSREP */
@@ -18622,36 +18624,45 @@ void lock_wait_wsrep_kill(trx_t *bf_trx, ulong thd_id, trx_id_t trx_id)
wsrep_thd_client_mode_str(vthd),
wsrep_thd_transaction_state_str(vthd),
wsrep_thd_query(vthd));
/* Mark transaction as a victim for Galera abort */
vtrx->lock.set_wsrep_victim();
if (!wsrep_thd_set_wsrep_aborter(bf_thd, vthd))
aborting= true;
else
WSREP_DEBUG("kill transaction skipped due to wsrep_aborter set");
}
}
mysql_mutex_unlock(&lock_sys.wait_mutex);
vtrx->mutex_unlock();
}
wsrep_thd_UNLOCK(vthd);
if (aborting)
DEBUG_SYNC(bf_thd, "before_wsrep_thd_abort");
if (aborting && wsrep_thd_bf_abort(bf_thd, vthd, true))
{
/* Need to grab mutexes again to ensure that the trx is still in
right state. */
lock_sys.wr_lock(SRW_LOCK_CALL);
mysql_mutex_lock(&lock_sys.wait_mutex);
vtrx->mutex_lock();
/* if victim is waiting for some other lock, we have to cancel
that waiting
*/
lock_sys.cancel_lock_wait_for_trx(vtrx);
DEBUG_SYNC(bf_thd, "before_wsrep_thd_abort");
if (!wsrep_thd_bf_abort(bf_thd, vthd, true))
if (vtrx->id == trx_id)
{
switch (vtrx->state) {
default:
break;
case TRX_STATE_ACTIVE:
case TRX_STATE_PREPARED:
lock_sys.cancel_lock_wait_for_wsrep_bf_abort(vtrx);
}
}
lock_sys.wr_unlock();
mysql_mutex_unlock(&lock_sys.wait_mutex);
vtrx->mutex_unlock();
}
else
{
wsrep_thd_LOCK(vthd);
wsrep_thd_set_wsrep_aborter(NULL, vthd);
wsrep_thd_UNLOCK(vthd);
WSREP_DEBUG("wsrep_thd_bf_abort has failed, victim %lu will survive",
thd_get_thread_id(vthd));
}
}
wsrep_thd_UNLOCK(vthd);
wsrep_thd_kill_UNLOCK(vthd);
}
}
@@ -18659,67 +18670,49 @@ void lock_wait_wsrep_kill(trx_t *bf_trx, ulong thd_id, trx_id_t trx_id)
/** This function forces the victim transaction to abort. Aborting the
transaction does NOT end it, it still has to be rolled back.
The caller must lock LOCK_thd_kill and LOCK_thd_data.
@param bf_thd brute force THD asking for the abort
@param victim_thd victim THD to be aborted
@return 0 victim was aborted
@return -1 victim thread was aborted (no transaction)
*/
static
void
wsrep_abort_transaction(
handlerton*,
THD *bf_thd,
THD *victim_thd,
static void wsrep_abort_transaction(handlerton *, THD *bf_thd, THD *victim_thd,
my_bool signal)
{
DBUG_ENTER("wsrep_abort_transaction");
ut_ad(bf_thd);
ut_ad(victim_thd);
wsrep_thd_kill_LOCK(victim_thd);
wsrep_thd_LOCK(victim_thd);
trx_t* victim_trx= thd_to_trx(victim_thd);
wsrep_thd_UNLOCK(victim_thd);
trx_t *victim_trx= thd_to_trx(victim_thd);
WSREP_DEBUG("abort transaction: BF: %s victim: %s victim conf: %s",
wsrep_thd_query(bf_thd),
wsrep_thd_query(victim_thd),
wsrep_thd_query(bf_thd), wsrep_thd_query(victim_thd),
wsrep_thd_transaction_state_str(victim_thd));
if (victim_trx) {
victim_trx->lock.set_wsrep_victim();
wsrep_thd_LOCK(victim_thd);
bool aborting= !wsrep_thd_set_wsrep_aborter(bf_thd, victim_thd);
wsrep_thd_UNLOCK(victim_thd);
if (aborting) {
DEBUG_SYNC(bf_thd, "before_wsrep_thd_abort");
DBUG_EXECUTE_IF("sync.before_wsrep_thd_abort",
if (!victim_trx)
{
const char act[]=
"now "
"SIGNAL sync.before_wsrep_thd_abort_reached "
"WAIT_FOR signal.before_wsrep_thd_abort";
DBUG_ASSERT(!debug_sync_set_action(bf_thd,
STRING_WITH_LEN(act)));
};);
wsrep_thd_bf_abort(bf_thd, victim_thd, signal);
}
} else {
DBUG_EXECUTE_IF("sync.before_wsrep_thd_abort",
{
const char act[]=
"now "
"SIGNAL sync.before_wsrep_thd_abort_reached "
"WAIT_FOR signal.before_wsrep_thd_abort";
DBUG_ASSERT(!debug_sync_set_action(bf_thd,
STRING_WITH_LEN(act)));
};);
wsrep_thd_bf_abort(bf_thd, victim_thd, signal);
WSREP_DEBUG("abort transaction: victim did not exist");
DBUG_VOID_RETURN;
}
wsrep_thd_kill_UNLOCK(victim_thd);
lock_sys.wr_lock(SRW_LOCK_CALL);
mysql_mutex_lock(&lock_sys.wait_mutex);
victim_trx->mutex_lock();
switch (victim_trx->state) {
default:
break;
case TRX_STATE_ACTIVE:
case TRX_STATE_PREPARED:
/* Cancel lock wait if the victim is waiting for a lock in InnoDB.
The transaction which is blocked somewhere else (e.g. waiting
for next command or MDL) has been interrupted by THD::awake_no_mutex()
on server level before calling this function. */
lock_sys.cancel_lock_wait_for_wsrep_bf_abort(victim_trx);
}
lock_sys.wr_unlock();
mysql_mutex_unlock(&lock_sys.wait_mutex);
victim_trx->mutex_unlock();
DBUG_VOID_RETURN;
}

View File

@@ -2365,6 +2365,7 @@ tablespace_deleted:
}
const ulint zip_size = s->zip_size(), size = s->size;
s->x_lock();
s->release();
mtr_t mtr;
@@ -2382,13 +2383,17 @@ tablespace_deleted:
|| !page_is_leaf(block->page.frame);
mtr.commit();
if (err == DB_TABLESPACE_DELETED) {
s->x_unlock();
goto tablespace_deleted;
}
if (!remove) {
s->x_unlock();
continue;
}
}
s->x_unlock();
if (srv_shutdown_state == SRV_SHUTDOWN_NONE
|| srv_fast_shutdown) {
continue;
@@ -2417,7 +2422,7 @@ tablespace_deleted:
/* Prevent an infinite loop, by removing entries from
the change buffer in the case the bitmap bits were
wrongly clear even though buffered changes exist. */
ibuf_delete_recs(page_id_t(space_ids[i], page_nos[i]));
ibuf_delete_recs(page_id_t(space_id, page_nos[i]));
}
}
@@ -4195,25 +4200,26 @@ dberr_t ibuf_merge_or_delete_for_page(buf_block_t *block,
ibuf_mtr_commit(&mtr);
if (bitmap_bits
&& DB_SUCCESS
if (!bitmap_bits) {
done:
/* No changes are buffered for this page. */
space->release();
return DB_SUCCESS;
}
if (!block
|| DB_SUCCESS
== fseg_page_is_allocated(space, page_id.page_no())) {
ibuf_mtr_start(&mtr);
mtr.set_named_space(space);
ibuf_reset_bitmap(block, page_id, zip_size, &mtr);
ibuf_mtr_commit(&mtr);
bitmap_bits = 0;
if (!block
|| btr_page_get_index_id(block->page.frame)
!= DICT_IBUF_ID_MIN + IBUF_SPACE_ID) {
ibuf_delete_recs(page_id);
}
}
if (!bitmap_bits) {
/* No changes are buffered for this page. */
space->release();
return DB_SUCCESS;
goto done;
}
}

View File

@@ -75,8 +75,7 @@ struct buf_pool_info_t
ulint flush_list_len; /*!< Length of buf_pool.flush_list */
ulint n_pend_unzip; /*!< buf_pool.n_pend_unzip, pages
pending decompress */
ulint n_pend_reads; /*!< buf_pool.n_pend_reads, pages
pending read */
ulint n_pend_reads; /*!< os_aio_pending_reads() */
ulint n_pending_flush_lru; /*!< Pages pending flush in LRU */
ulint n_pending_flush_list; /*!< Pages pending flush in FLUSH
LIST */

View File

@@ -102,10 +102,13 @@ which could result in a deadlock if the OS does not support asynchronous io.
ulint
buf_read_ahead_linear(const page_id_t page_id, ulint zip_size, bool ibuf);
/** Issue read requests for pages that need to be recovered.
@param space_id tablespace identifier
@param page_nos page numbers to read, in ascending order */
void buf_read_recv_pages(uint32_t space_id, st_::span<uint32_t> page_nos);
/** Schedule a page for recovery.
@param space tablespace
@param page_id page identifier
@param recs log records
@param init page initialization, or nullptr if the page needs to be read */
void buf_read_recover(fil_space_t *space, const page_id_t page_id,
page_recv_t &recs, recv_init *init);
/** @name Modes used in read-ahead @{ */
/** read only pages belonging to the insert buffer tree */

View File

@@ -638,8 +638,6 @@ public:
/** Close all tablespace files at shutdown */
static void close_all();
/** @return last_freed_lsn */
lsn_t get_last_freed_lsn() { return last_freed_lsn; }
/** Update last_freed_lsn */
void update_last_freed_lsn(lsn_t lsn) { last_freed_lsn= lsn; }

View File

@@ -955,6 +955,10 @@ public:
/** Cancel possible lock waiting for a transaction */
static void cancel_lock_wait_for_trx(trx_t *trx);
#ifdef WITH_WSREP
/** Cancel lock waiting for a wsrep BF abort. */
static void cancel_lock_wait_for_wsrep_bf_abort(trx_t *trx);
#endif /* WITH_WSREP */
};
/** The lock system */

View File

@@ -38,9 +38,9 @@ Created 9/20/1997 Heikki Tuuri
#define recv_recovery_is_on() UNIV_UNLIKELY(recv_sys.recovery_on)
ATTRIBUTE_COLD MY_ATTRIBUTE((nonnull, warn_unused_result))
/** Apply any buffered redo log to a page that was just read from a data file.
@param[in,out] space tablespace
@param[in,out] bpage buffer pool page
/** Apply any buffered redo log to a page.
@param space tablespace
@param bpage buffer pool page
@return whether the page was recovered correctly */
bool recv_recover_page(fil_space_t* space, buf_page_t* bpage);
@@ -49,17 +49,6 @@ of first system tablespace page
@return error code or DB_SUCCESS */
dberr_t recv_recovery_from_checkpoint_start();
/** Whether to store redo log records in recv_sys.pages */
enum store_t {
/** Do not store redo log records. */
STORE_NO,
/** Store redo log records. */
STORE_YES,
/** Store redo log records if the tablespace exists. */
STORE_IF_EXISTS
};
/** Report an operation to create, delete, or rename a file during backup.
@param[in] space_id tablespace identifier
@param[in] type file operation redo log type
@@ -125,21 +114,15 @@ struct recv_dblwr_t
list pages;
};
/** the recovery state and buffered records for a page */
/** recv_sys.pages entry; protected by recv_sys.mutex */
struct page_recv_t
{
/** Recovery state; protected by recv_sys.mutex */
enum
{
/** not yet processed */
RECV_NOT_PROCESSED,
/** not processed; the page will be reinitialized */
RECV_WILL_NOT_READ,
/** page is being read */
RECV_BEING_READ,
/** log records are being applied on the page */
RECV_BEING_PROCESSED
} state= RECV_NOT_PROCESSED;
/** Recovery status: 0=not in progress, 1=log is being applied,
-1=log has been applied and the entry may be erased.
Transitions from 1 to -1 are NOT protected by recv_sys.mutex. */
Atomic_relaxed<int8_t> being_processed{0};
/** Whether reading the page will be skipped */
bool skip_read= false;
/** Latest written byte offset when applying the log records.
@see mtr_t::m_last_offset */
uint16_t last_offset= 1;
@@ -162,6 +145,9 @@ struct page_recv_t
head= recs;
tail= recs;
}
/** Remove the last records for the page
@param start_lsn start of the removed log */
ATTRIBUTE_COLD void rewind(lsn_t start_lsn);
/** @return the last log snippet */
const log_rec_t* last() const { return tail; }
@@ -180,8 +166,8 @@ struct page_recv_t
iterator begin() { return head; }
iterator end() { return NULL; }
bool empty() const { ut_ad(!head == !tail); return !head; }
/** Clear and free the records; @see recv_sys_t::alloc() */
inline void clear();
/** Clear and free the records; @see recv_sys_t::add() */
void clear();
} log;
/** Trim old log records for a page.
@@ -190,21 +176,27 @@ struct page_recv_t
inline bool trim(lsn_t start_lsn);
/** Ignore any earlier redo log records for this page. */
inline void will_not_read();
/** @return whether the log records for the page are being processed */
bool is_being_processed() const { return state == RECV_BEING_PROCESSED; }
};
/** A page initialization operation that was parsed from the redo log */
struct recv_init
{
/** log sequence number of the page initialization */
lsn_t lsn;
/** Whether btr_page_create() avoided a read of the page.
At the end of the last recovery batch, mark_ibuf_exist()
will mark pages for which this flag is set. */
bool created;
};
/** Recovery system data structure */
struct recv_sys_t
{
/** mutex protecting apply_log_recs and page_recv_t::state */
mysql_mutex_t mutex;
using init= recv_init;
/** mutex protecting this as well as some of page_recv_t */
alignas(CPU_LEVEL1_DCACHE_LINESIZE) mysql_mutex_t mutex;
private:
/** condition variable for
!apply_batch_on || pages.empty() || found_corrupt_log || found_corrupt_fs */
pthread_cond_t cond;
/** whether recv_apply_hashed_log_recs() is running */
bool apply_batch_on;
/** set when finding a corrupt log block or record, or there is a
log parsing buffer overflow */
bool found_corrupt_log;
@@ -226,6 +218,8 @@ public:
size_t offset;
/** log sequence number of the first non-parsed record */
lsn_t lsn;
/** log sequence number of the last parsed mini-transaction */
lsn_t scanned_lsn;
/** log sequence number at the end of the FILE_CHECKPOINT record, or 0 */
lsn_t file_checkpoint;
/** the time when progress was last reported */
@@ -238,6 +232,9 @@ public:
map pages;
private:
/** iterator to pages, used by parse() */
map::iterator pages_it;
/** Process a record that indicates that a tablespace size is being shrunk.
@param page_id first page that is not in the file
@param lsn log sequence number of the shrink operation */
@@ -257,30 +254,42 @@ public:
/** The contents of the doublewrite buffer */
recv_dblwr_t dblwr;
/** Last added LSN to pages, before switching to STORE_NO */
lsn_t last_stored_lsn= 0;
inline void read(os_offset_t offset, span<byte> buf);
inline size_t files_size();
void close_files() { files.clear(); files.shrink_to_fit(); }
/** Advance pages_it if it matches the iterator */
void pages_it_invalidate(const map::iterator &p)
{
mysql_mutex_assert_owner(&mutex);
if (pages_it == p)
pages_it++;
}
/** Invalidate pages_it if it points to the given tablespace */
void pages_it_invalidate(uint32_t space_id)
{
mysql_mutex_assert_owner(&mutex);
if (pages_it != pages.end() && pages_it->first.space() == space_id)
pages_it= pages.end();
}
private:
/** Attempt to initialize a page based on redo log records.
@param page_id page identifier
@param p iterator pointing to page_id
@param p iterator
@param mtr mini-transaction
@param b pre-allocated buffer pool block
@param init page initialization
@return the recovered block
@retval nullptr if the page cannot be initialized based on log records
@retval -1 if the page cannot be recovered due to corruption */
inline buf_block_t *recover_low(const page_id_t page_id, map::iterator &p,
mtr_t &mtr, buf_block_t *b);
inline buf_block_t *recover_low(const map::iterator &p, mtr_t &mtr,
buf_block_t *b, init &init);
/** Attempt to initialize a page based on redo log records.
@param page_id page identifier
@return the recovered block
@retval nullptr if the page cannot be initialized based on log records
@retval -1 if the page cannot be recovered due to corruption */
buf_block_t *recover_low(const page_id_t page_id);
ATTRIBUTE_COLD buf_block_t *recover_low(const page_id_t page_id);
/** All found log files (multiple ones are possible if we are upgrading
from before MariaDB Server 10.5.1) */
@@ -289,10 +298,27 @@ private:
/** Base node of the redo block list.
List elements are linked via buf_block_t::unzip_LRU. */
UT_LIST_BASE_NODE_T(buf_block_t) blocks;
/** Allocate a block from the buffer pool for recv_sys.pages */
ATTRIBUTE_COLD buf_block_t *add_block();
/** Wait for buffer pool to become available.
@param pages number of buffer pool pages needed */
ATTRIBUTE_COLD void wait_for_pool(size_t pages);
/** Free log for processed pages. */
void garbage_collect();
/** Apply a recovery batch.
@param space_id current tablespace identifier
@param space current tablespace
@param free_block spare buffer block
@param last_batch whether it is possible to write more redo log
@return whether the caller must provide a new free_block */
bool apply_batch(uint32_t space_id, fil_space_t *&space,
buf_block_t *&free_block, bool last_batch);
public:
/** Check whether the number of read redo log blocks exceeds the maximum.
@return whether the memory is exhausted */
inline bool is_memory_exhausted();
/** Apply buffered log to persistent data pages.
@param last_batch whether it is possible to write more redo log */
void apply(bool last_batch);
@@ -310,7 +336,7 @@ public:
/** Clean up after create() */
void close();
bool is_initialised() const { return last_stored_lsn != 0; }
bool is_initialised() const { return scanned_lsn != 0; }
/** Find the latest checkpoint.
@return error code or DB_SUCCESS */
@@ -321,60 +347,76 @@ public:
@param start_lsn start LSN of the mini-transaction
@param lsn @see mtr_t::commit_lsn()
@param l redo log snippet
@param len length of l, in bytes */
inline void add(map::iterator it, lsn_t start_lsn, lsn_t lsn,
@param len length of l, in bytes
@return whether we ran out of memory */
bool add(map::iterator it, lsn_t start_lsn, lsn_t lsn,
const byte *l, size_t len);
enum parse_mtr_result { OK, PREMATURE_EOF, GOT_EOF };
/** Parsing result */
enum parse_mtr_result {
/** a record was successfully parsed */
OK,
/** the log ended prematurely (need to read more) */
PREMATURE_EOF,
/** the end of the log was reached */
GOT_EOF,
/** parse<true>(l, false) ran out of memory */
GOT_OOM
};
private:
/** Parse and register one log_t::FORMAT_10_8 mini-transaction.
@param store whether to store the records
@param l log data source */
@tparam store whether to store the records
@param l log data source
@param if_exists if store: whether to check if the tablespace exists */
template<typename source,bool store>
inline parse_mtr_result parse(source &l, bool if_exists) noexcept;
/** Rewind a mini-transaction when parse() runs out of memory.
@param l log data source
@param begin start of the mini-transaction */
template<typename source>
inline parse_mtr_result parse(store_t store, source& l) noexcept;
ATTRIBUTE_COLD void rewind(source &l, source &begin) noexcept;
/** Report progress in terms of LSN or pages remaining */
ATTRIBUTE_COLD void report_progress() const;
public:
/** Parse and register one log_t::FORMAT_10_8 mini-transaction,
handling log_sys.is_pmem() buffer wrap-around.
@param store whether to store the records */
static parse_mtr_result parse_mtr(store_t store) noexcept;
@tparam store whether to store the records
@param if_exists if store: whether to check if the tablespace exists */
template<bool store>
static parse_mtr_result parse_mtr(bool if_exists) noexcept;
/** Parse and register one log_t::FORMAT_10_8 mini-transaction,
handling log_sys.is_pmem() buffer wrap-around.
@param store whether to store the records */
static parse_mtr_result parse_pmem(store_t store) noexcept
@tparam store whether to store the records
@param if_exists if store: whether to check if the tablespace exists */
template<bool store>
static parse_mtr_result parse_pmem(bool if_exists) noexcept
#ifdef HAVE_PMEM
;
#else
{ return parse_mtr(store); }
{ return parse_mtr<store>(if_exists); }
#endif
/** Erase log records for a page. */
void erase(map::iterator p);
/** Clear a fully processed set of stored redo log records. */
inline void clear();
void clear();
/** Determine whether redo log recovery progress should be reported.
@param time the current time
@return whether progress should be reported
(the last report was at least 15 seconds ago) */
bool report(time_t time)
{
if (time - progress_time < 15)
return false;
progress_time= time;
return true;
}
bool report(time_t time);
/** The alloc() memory alignment, in bytes */
static constexpr size_t ALIGNMENT= sizeof(size_t);
/** Allocate memory for log_rec_t
@param len allocation size, in bytes
@return pointer to len bytes of memory (never NULL) */
inline void *alloc(size_t len);
/** Free a redo log snippet.
@param data buffer returned by alloc() */
@param data buffer allocated in add() */
inline void free(const void *data);
/** Remove records for a corrupted page.
@@ -386,8 +428,6 @@ public:
ATTRIBUTE_COLD void set_corrupt_fs();
/** Flag log file corruption during recovery. */
ATTRIBUTE_COLD void set_corrupt_log();
/** Possibly finish a recovery batch. */
inline void maybe_finish_batch();
/** @return whether data file corruption was found */
bool is_corrupt_fs() const { return UNIV_UNLIKELY(found_corrupt_fs); }
@@ -405,12 +445,13 @@ public:
}
/** Try to recover a tablespace that was not readable earlier
@param p iterator, initially pointing to page_id_t{space_id,0};
the records will be freed and the iterator advanced
@param p iterator
@param name tablespace file name
@param free_block spare buffer block
@return whether recovery failed */
bool recover_deferred(map::iterator &p, const std::string &name,
@return recovered tablespace
@retval nullptr if recovery failed */
fil_space_t *recover_deferred(const map::iterator &p,
const std::string &name,
buf_block_t *&free_block);
};

View File

@@ -212,6 +212,10 @@ public:
bool is_LRU() const { return (type & (WRITE_LRU ^ WRITE_ASYNC)) != 0; }
bool is_async() const { return (type & (READ_SYNC ^ READ_ASYNC)) != 0; }
void write_complete() const;
void read_complete() const;
void fake_read_complete(os_offset_t offset) const;
/** If requested, free storage space associated with a section of the file.
@param off byte offset from the start (SEEK_SET)
@param len size of the hole in bytes
@@ -1040,6 +1044,11 @@ int os_aio_init();
Frees the asynchronous io system. */
void os_aio_free();
/** Submit a fake read request during crash recovery.
@param type fake read request
@param offset additional context */
void os_fake_read(const IORequest &type, os_offset_t offset);
/** Request a read or write.
@param type I/O request
@param buf buffer

View File

@@ -617,6 +617,8 @@ struct export_var_t{
#ifdef UNIV_DEBUG
ulint innodb_buffer_pool_pages_latched; /*!< Latched pages */
#endif /* UNIV_DEBUG */
/** buf_pool.stat.n_page_gets (a sharded counter) */
ulint innodb_buffer_pool_read_requests;
ulint innodb_checkpoint_age;
ulint innodb_checkpoint_max_age;
ulint innodb_data_pending_reads; /*!< Pending reads */

View File

@@ -1055,7 +1055,7 @@ public:
void close();
/** @return total number of active (non-prepared) transactions */
ulint any_active_transactions();
size_t any_active_transactions(size_t *prepared= nullptr);
/**

View File

@@ -216,14 +216,6 @@ buf_block_t*
trx_undo_assign_low(trx_t *trx, trx_rseg_t *rseg, trx_undo_t **undo,
mtr_t *mtr, dberr_t *err)
MY_ATTRIBUTE((nonnull, warn_unused_result));
/******************************************************************//**
Sets the state of the undo log segment at a transaction finish.
@return undo log segment header page, x-latched */
buf_block_t*
trx_undo_set_state_at_finish(
/*=========================*/
trx_undo_t* undo, /*!< in: undo log memory copy */
mtr_t* mtr); /*!< in: mtr */
/** Set the state of the undo log segment at a XA PREPARE or XA ROLLBACK.
@param[in,out] trx transaction

View File

@@ -5732,12 +5732,13 @@ static void lock_release_autoinc_locks(trx_t *trx)
}
/** Cancel a waiting lock request and release possibly waiting transactions */
template <bool from_deadlock= false>
template <bool from_deadlock= false, bool inner_trx_lock= true>
void lock_cancel_waiting_and_release(lock_t *lock)
{
lock_sys.assert_locked(*lock);
mysql_mutex_assert_owner(&lock_sys.wait_mutex);
trx_t *trx= lock->trx;
if (inner_trx_lock)
trx->mutex_lock();
ut_d(const auto trx_state= trx->state);
ut_ad(trx_state == TRX_STATE_COMMITTED_IN_MEMORY ||
@@ -5762,6 +5763,7 @@ void lock_cancel_waiting_and_release(lock_t *lock)
lock_wait_end<from_deadlock>(trx);
if (inner_trx_lock)
trx->mutex_unlock();
}
@@ -5779,6 +5781,19 @@ void lock_sys_t::cancel_lock_wait_for_trx(trx_t *trx)
mysql_mutex_unlock(&lock_sys.wait_mutex);
}
#ifdef WITH_WSREP
void lock_sys_t::cancel_lock_wait_for_wsrep_bf_abort(trx_t *trx)
{
lock_sys.assert_locked();
mysql_mutex_assert_owner(&lock_sys.wait_mutex);
ut_ad(trx->mutex_is_owner());
ut_ad(trx->state == TRX_STATE_ACTIVE || trx->state == TRX_STATE_PREPARED);
trx->lock.set_wsrep_victim();
if (lock_t *lock= trx->lock.wait_lock)
lock_cancel_waiting_and_release<false, false>(lock);
}
#endif /* WITH_WSREP */
/** Cancel a waiting lock request.
@tparam check_victim whether to check for DB_DEADLOCK
@param trx active transaction

File diff suppressed because it is too large Load Diff

View File

@@ -3427,15 +3427,12 @@ os_file_get_status(
return(ret);
}
extern void fil_aio_callback(const IORequest &request);
static void io_callback(tpool::aiocb *cb)
static void io_callback_errorcheck(const tpool::aiocb *cb)
{
const IORequest &request= *static_cast<const IORequest*>
(static_cast<const void*>(cb->m_userdata));
if (cb->m_err != DB_SUCCESS)
{
const IORequest &request= *static_cast<const IORequest*>
(static_cast<const void*>(cb->m_userdata));
ib::fatal() << "IO Error: " << cb->m_err << " during " <<
(request.is_async() ? "async " : "sync ") <<
(request.is_LRU() ? "lru " : "") <<
@@ -3443,19 +3440,36 @@ static void io_callback(tpool::aiocb *cb)
" of " << cb->m_len << " bytes, for file " << cb->m_fh << ", returned " <<
cb->m_ret_len;
}
/* Return cb back to cache*/
if (cb->m_opcode == tpool::aio_opcode::AIO_PREAD)
{
}
static void fake_io_callback(void *c)
{
tpool::aiocb *cb= static_cast<tpool::aiocb*>(c);
ut_ad(read_slots->contains(cb));
fil_aio_callback(request);
static_cast<const IORequest*>(static_cast<const void*>(cb->m_userdata))->
fake_read_complete(cb->m_offset);
read_slots->release(cb);
}
else
{
}
static void read_io_callback(void *c)
{
tpool::aiocb *cb= static_cast<tpool::aiocb*>(c);
ut_ad(cb->m_opcode == tpool::aio_opcode::AIO_PREAD);
io_callback_errorcheck(cb);
ut_ad(read_slots->contains(cb));
static_cast<const IORequest*>
(static_cast<const void*>(cb->m_userdata))->read_complete();
read_slots->release(cb);
}
static void write_io_callback(void *c)
{
tpool::aiocb *cb= static_cast<tpool::aiocb*>(c);
ut_ad(cb->m_opcode == tpool::aio_opcode::AIO_PWRITE);
ut_ad(write_slots->contains(cb));
fil_aio_callback(request);
static_cast<const IORequest*>
(static_cast<const void*>(cb->m_userdata))->write_complete();
write_slots->release(cb);
}
}
#ifdef LINUX_NATIVE_AIO
@@ -3758,6 +3772,28 @@ void os_aio_wait_until_no_pending_reads(bool declare)
tpool::tpool_wait_end();
}
/** Submit a fake read request during crash recovery.
@param type fake read request
@param offset additional context */
void os_fake_read(const IORequest &type, os_offset_t offset)
{
tpool::aiocb *cb= read_slots->acquire();
cb->m_group= read_slots->get_task_group();
cb->m_fh= type.node->handle.m_file;
cb->m_buffer= nullptr;
cb->m_len= 0;
cb->m_offset= offset;
cb->m_opcode= tpool::aio_opcode::AIO_PREAD;
new (cb->m_userdata) IORequest{type};
cb->m_internal_task.m_func= fake_io_callback;
cb->m_internal_task.m_arg= cb;
cb->m_internal_task.m_group= cb->m_group;
srv_thread_pool->submit_task(&cb->m_internal_task);
}
/** Request a read or write.
@param type I/O request
@param buf buffer
@@ -3803,23 +3839,32 @@ func_exit:
return err;
}
io_slots* slots;
tpool::callback_func callback;
tpool::aio_opcode opcode;
if (type.is_read()) {
++os_n_file_reads;
slots = read_slots;
callback = read_io_callback;
opcode = tpool::aio_opcode::AIO_PREAD;
} else {
++os_n_file_writes;
slots = write_slots;
callback = write_io_callback;
opcode = tpool::aio_opcode::AIO_PWRITE;
}
compile_time_assert(sizeof(IORequest) <= tpool::MAX_AIO_USERDATA_LEN);
io_slots* slots= type.is_read() ? read_slots : write_slots;
tpool::aiocb* cb = slots->acquire();
cb->m_buffer = buf;
cb->m_callback = (tpool::callback_func)io_callback;
cb->m_callback = callback;
cb->m_group = slots->get_task_group();
cb->m_fh = type.node->handle.m_file;
cb->m_len = (int)n;
cb->m_offset = offset;
cb->m_opcode = type.is_read() ? tpool::aio_opcode::AIO_PREAD : tpool::aio_opcode::AIO_PWRITE;
cb->m_opcode = opcode;
new (cb->m_userdata) IORequest{type};
if (srv_thread_pool->submit_io(cb)) {
@@ -3827,6 +3872,7 @@ func_exit:
os_file_handle_error(type.node->name, type.is_read()
? "aio read" : "aio write");
err = DB_IO_ERROR;
type.node->space->release();
}
goto func_exit;

View File

@@ -214,14 +214,14 @@ row_ins_sec_index_entry_by_modify(
made to the clustered index, and completed the
secondary index creation before we got here. In this
case, the change would already be there. The CREATE
INDEX should be waiting for a MySQL meta-data lock
upgrade at least until this INSERT or UPDATE
returns. After that point, set_committed(true)
would be invoked in commit_inplace_alter_table(). */
INDEX should be in wait_while_table_is_used() at least
until this INSERT or UPDATE returns. After that point,
set_committed(true) would be invoked in
commit_inplace_alter_table(). */
ut_a(update->n_fields == 0);
ut_a(!cursor->index()->is_committed());
ut_ad(!dict_index_is_online_ddl(cursor->index()));
return(DB_SUCCESS);
return cursor->index()->is_committed()
? DB_CORRUPTION : DB_SUCCESS;
}
if (mode == BTR_MODIFY_LEAF) {

View File

@@ -615,6 +615,8 @@ row_purge_del_mark(
const auto type= node->index->type;
if (type & (DICT_FTS | DICT_CORRUPT))
continue;
if (node->index->online_status > ONLINE_INDEX_CREATION)
continue;
if (UNIV_UNLIKELY(DICT_VIRTUAL & type) && !node->index->is_committed() &&
node->index->has_new_v_col())
continue;
@@ -767,6 +769,11 @@ row_purge_upd_exist_or_extern_func(
continue;
}
if (node->index->online_status
> ONLINE_INDEX_CREATION) {
continue;
}
if (row_upd_changes_ord_field_binary(node->index, node->update,
thr, NULL, NULL)) {
/* Build the older version of the index entry */

View File

@@ -674,7 +674,7 @@ static monitor_info_t innodb_counter_info[] =
{"trx_rseg_history_len", "transaction",
"Length of the TRX_RSEG_HISTORY list",
static_cast<monitor_type_t>(
MONITOR_EXISTING | MONITOR_DISPLAY_CURRENT),
MONITOR_EXISTING | MONITOR_DISPLAY_CURRENT | MONITOR_DEFAULT_ON),
MONITOR_DEFAULT_START, MONITOR_RSEG_HISTORY_LEN},
{"trx_undo_slots_used", "transaction", "Number of undo slots used",

Some files were not shown because too many files have changed in this diff Show More