summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarko Mäkelä <marko.makela@mariadb.com>2023-04-26 18:31:50 +0300
committerMarko Mäkelä <marko.makela@mariadb.com>2023-04-26 18:31:50 +0300
commit52f6f364d9b0f8d5660abac9016ceac4477ec7ce (patch)
tree4f9604f560ac9325c1a9e83a8b59ca1121e8f585
parentb0ecf4693dae307bbe700f340c8565a96c14715b (diff)
parentce6616aa284eda7615678e1ccd558172da8de15b (diff)
downloadmariadb-git-52f6f364d9b0f8d5660abac9016ceac4477ec7ce.tar.gz
Merge 10.10 into 10.11
-rw-r--r--client/mysqltest.cc1
-rwxr-xr-xdebian/autobake-deb.sh17
-rw-r--r--debian/control1
-rw-r--r--extra/mariabackup/backup_copy.cc234
-rw-r--r--extra/mariabackup/backup_copy.h19
-rw-r--r--extra/mariabackup/backup_mysql.cc40
-rw-r--r--extra/mariabackup/backup_mysql.h15
-rw-r--r--extra/mariabackup/datasink.h29
-rw-r--r--extra/mariabackup/write_filt.cc14
-rw-r--r--extra/mariabackup/write_filt.h3
-rw-r--r--extra/mariabackup/xtrabackup.cc250
-rw-r--r--extra/mariabackup/xtrabackup.h11
-rw-r--r--include/my_alloca.h6
-rw-r--r--mysql-test/include/sql_mode_pad_char_to_full_length.inc31
-rw-r--r--mysql-test/lib/My/CoreDump.pm33
-rw-r--r--mysql-test/main/bootstrap.result45
-rw-r--r--mysql-test/main/bootstrap.test108
-rw-r--r--mysql-test/main/ctype_uca_partitions.result40
-rw-r--r--mysql-test/main/ctype_uca_partitions.test32
-rw-r--r--mysql-test/main/derived.result23
-rw-r--r--mysql-test/main/derived.test30
-rw-r--r--mysql-test/main/group_min_max.result12
-rw-r--r--mysql-test/main/group_min_max.test11
-rw-r--r--mysql-test/main/mysql_tzinfo_to_sql_symlink.result42
-rw-r--r--mysql-test/main/opt_trace.result395
-rw-r--r--mysql-test/main/opt_trace.test19
-rw-r--r--mysql-test/main/parser.result28
-rw-r--r--mysql-test/main/parser.test17
-rw-r--r--mysql-test/main/sql_mode_pad_char_to_full_length.result94
-rw-r--r--mysql-test/main/sql_mode_pad_char_to_full_length.test19
-rw-r--r--mysql-test/main/update.result29
-rw-r--r--mysql-test/main/update.test23
-rwxr-xr-xmysql-test/mariadb-test-run.pl30
-rw-r--r--mysql-test/suite/binlog/t/binlog_truncate_multi_engine.inc8
-rw-r--r--mysql-test/suite/galera/r/MDEV-30804.result11
-rw-r--r--mysql-test/suite/galera/r/MDEV-30955.result26
-rw-r--r--mysql-test/suite/galera/r/galera_var_replicate_aria_on.result5
-rw-r--r--mysql-test/suite/galera/r/mdev-26175.result24
-rw-r--r--mysql-test/suite/galera/t/MDEV-30804.cnf7
-rw-r--r--mysql-test/suite/galera/t/MDEV-30804.test21
-rw-r--r--mysql-test/suite/galera/t/MDEV-30955.test70
-rw-r--r--mysql-test/suite/galera/t/galera_sequences.test1
-rw-r--r--mysql-test/suite/galera/t/galera_var_replicate_aria_on.test8
-rw-r--r--mysql-test/suite/galera/t/mdev-26175.test27
-rw-r--r--mysql-test/suite/galera_sr/r/MDEV-30862.result11
-rw-r--r--mysql-test/suite/galera_sr/t/MDEV-30862.test24
-rw-r--r--mysql-test/suite/innodb/r/default_row_format_alter.result20
-rw-r--r--mysql-test/suite/innodb/r/innodb_skip_innodb_is_tables.result4
-rw-r--r--mysql-test/suite/innodb/r/sql_mode_pad_char_to_full_length.result51
-rw-r--r--mysql-test/suite/innodb/t/default_row_format_alter.test17
-rw-r--r--mysql-test/suite/innodb/t/sql_mode_pad_char_to_full_length.test18
-rw-r--r--mysql-test/suite/innodb_fts/r/concurrent_insert.result2
-rw-r--r--mysql-test/suite/innodb_fts/r/sync.result16
-rw-r--r--mysql-test/suite/innodb_fts/r/sync_block.result83
-rw-r--r--mysql-test/suite/innodb_fts/t/concurrent_insert.test2
-rw-r--r--mysql-test/suite/innodb_fts/t/sync.test4
-rw-r--r--mysql-test/suite/innodb_fts/t/sync_block.test124
-rw-r--r--mysql-test/suite/mariabackup/aria_log_dir_path.result41
-rw-r--r--mysql-test/suite/mariabackup/aria_log_dir_path.test105
-rw-r--r--mysql-test/suite/mariabackup/aria_log_dir_path_rel.result41
-rw-r--r--mysql-test/suite/mariabackup/aria_log_dir_path_rel.test4
-rw-r--r--mysql-test/suite/rpl/r/rpl_xa_prepare_gtid_fail.result51
-rw-r--r--mysql-test/suite/rpl/t/rpl_xa_prepare_gtid_fail.test106
-rw-r--r--mysql-test/suite/sys_vars/r/character_set_client_basic.result11
-rw-r--r--mysql-test/suite/sys_vars/r/character_set_connection_basic.result16
-rw-r--r--mysql-test/suite/sys_vars/r/character_set_results_basic.resultbin16427 -> 16895 bytes
-rw-r--r--mysql-test/suite/sys_vars/r/character_set_server_basic.result16
-rw-r--r--mysql-test/suite/sys_vars/t/character_set_client_basic.test9
-rw-r--r--mysql-test/suite/sys_vars/t/character_set_connection_basic.test13
-rw-r--r--mysql-test/suite/sys_vars/t/character_set_results_basic.test12
-rw-r--r--mysql-test/suite/sys_vars/t/character_set_server_basic.test12
-rw-r--r--scripts/wsrep_sst_mariabackup.sh3
-rw-r--r--sql/handler.cc29
-rw-r--r--sql/log.cc2
-rw-r--r--sql/log_event.h4
-rw-r--r--sql/log_event_server.cc64
-rw-r--r--sql/mysqld.cc5
-rw-r--r--sql/opt_range.cc49
-rw-r--r--sql/sql_derived.cc3
-rw-r--r--sql/sql_parse.cc2
-rw-r--r--sql/sql_select.cc4
-rw-r--r--sql/sql_table.cc13
-rw-r--r--sql/sql_yacc.yy56
-rw-r--r--sql/sys_vars.cc22
-rw-r--r--sql/tztime.cc4
-rw-r--r--sql/wsrep_client_service.cc2
-rw-r--r--sql/wsrep_high_priority_service.cc2
-rw-r--r--storage/connect/catalog.h4
-rw-r--r--storage/connect/ha_connect.cc5
-rw-r--r--storage/connect/reldef.cpp26
-rw-r--r--storage/connect/tabbson.cpp47
-rw-r--r--storage/connect/tabdos.cpp61
-rw-r--r--storage/connect/tabext.cpp57
-rw-r--r--storage/connect/tabfmt.cpp38
-rw-r--r--storage/connect/tabjdbc.cpp45
-rw-r--r--storage/connect/tabjson.cpp54
-rw-r--r--storage/connect/value.cpp8
-rw-r--r--storage/connect/value.h2
-rw-r--r--storage/innobase/btr/btr0btr.cc2
-rw-r--r--storage/innobase/buf/buf0buf.cc4
-rw-r--r--storage/innobase/buf/buf0dblwr.cc1
-rw-r--r--storage/innobase/buf/buf0dump.cc2
-rw-r--r--storage/innobase/buf/buf0flu.cc91
-rw-r--r--storage/innobase/buf/buf0lru.cc6
-rw-r--r--storage/innobase/buf/buf0rea.cc2
-rw-r--r--storage/innobase/dict/drop.cc7
-rw-r--r--storage/innobase/fil/fil0fil.cc52
-rw-r--r--storage/innobase/fsp/fsp0space.cc24
-rw-r--r--storage/innobase/fsp/fsp0sysspace.cc10
-rw-r--r--storage/innobase/fts/fts0fts.cc325
-rw-r--r--storage/innobase/fts/fts0opt.cc56
-rw-r--r--storage/innobase/handler/ha_innodb.cc5
-rw-r--r--storage/innobase/handler/handler0alter.cc8
-rw-r--r--storage/innobase/ibuf/ibuf0ibuf.cc4
-rw-r--r--storage/innobase/include/buf0dblwr.h5
-rw-r--r--storage/innobase/include/fil0fil.h1
-rw-r--r--storage/innobase/include/fts0fts.h9
-rw-r--r--storage/innobase/include/fts0types.h32
-rw-r--r--storage/innobase/include/log0log.h9
-rw-r--r--storage/innobase/include/mtr0mtr.h14
-rw-r--r--storage/innobase/include/os0file.h10
-rw-r--r--storage/innobase/include/trx0rseg.h3
-rw-r--r--storage/innobase/include/trx0undo.h10
-rw-r--r--storage/innobase/log/log0log.cc10
-rw-r--r--storage/innobase/log/log0recv.cc20
-rw-r--r--storage/innobase/mtr/mtr0mtr.cc12
-rw-r--r--storage/innobase/os/os0file.cc21
-rw-r--r--storage/innobase/rem/rem0rec.cc4
-rw-r--r--storage/innobase/row/row0merge.cc5
-rw-r--r--storage/innobase/row/row0mysql.cc1
-rw-r--r--storage/innobase/row/row0purge.cc9
-rw-r--r--storage/innobase/row/row0quiesce.cc2
-rw-r--r--storage/innobase/row/row0uins.cc9
-rw-r--r--storage/innobase/srv/srv0mon.cc34
-rw-r--r--storage/innobase/srv/srv0srv.cc7
-rw-r--r--storage/innobase/srv/srv0start.cc8
-rw-r--r--storage/innobase/trx/trx0purge.cc242
-rw-r--r--storage/innobase/trx/trx0rec.cc10
-rw-r--r--storage/innobase/trx/trx0rseg.cc3
-rw-r--r--storage/innobase/trx/trx0trx.cc27
-rw-r--r--storage/innobase/trx/trx0undo.cc120
-rw-r--r--storage/rocksdb/build_rocksdb.cmake2
-rw-r--r--storage/rocksdb/mysql-test/rocksdb/r/innodb_i_s_tables_disabled.result4
-rw-r--r--storage/spider/mysql-test/spider/bugfix/r/mdev_29644.result41
-rw-r--r--storage/spider/mysql-test/spider/bugfix/r/self_reference_multi.result21
-rw-r--r--storage/spider/mysql-test/spider/bugfix/t/mdev_29644.cnf3
-rw-r--r--storage/spider/mysql-test/spider/bugfix/t/mdev_29644.test56
-rw-r--r--storage/spider/mysql-test/spider/bugfix/t/self_reference_multi.test29
-rw-r--r--storage/spider/spd_db_mysql.cc104
-rw-r--r--storage/spider/spd_db_mysql.h4
150 files changed, 3693 insertions, 1140 deletions
diff --git a/client/mysqltest.cc b/client/mysqltest.cc
index be76e34b315..dc6022096bc 100644
--- a/client/mysqltest.cc
+++ b/client/mysqltest.cc
@@ -5262,6 +5262,7 @@ void do_shutdown_server(struct st_command *command)
if (!timeout || wait_until_dead(pid, timeout < 5 ? 5 : timeout))
{
(void) my_kill(pid, SIGKILL);
+ wait_until_dead(pid, 5);
}
}
DBUG_VOID_RETURN;
diff --git a/debian/autobake-deb.sh b/debian/autobake-deb.sh
index be013fb9b0f..869016fe5b2 100755
--- a/debian/autobake-deb.sh
+++ b/debian/autobake-deb.sh
@@ -196,34 +196,39 @@ dch -b -D "${LSBNAME}" -v "${VERSION}" "Automatic build with ${LOGSTRING}." --co
echo "Creating package version ${VERSION} ... "
-BUILDPACKAGE_DPKGCMD=""
+BUILDPACKAGE_DPKGCMD=()
+
+# Fakeroot test
+if fakeroot true; then
+ BUILDPACKAGE_DPKGCMD+=( "fakeroot" "--" )
+fi
# Use eatmydata is available to build faster with less I/O, skipping fsync()
# during the entire build process (safe because a build can always be restarted)
if which eatmydata > /dev/null
then
- BUILDPACKAGE_DPKGCMD="eatmydata"
+ BUILDPACKAGE_DPKGCMD+=("eatmydata")
fi
-BUILDPACKAGE_DPKGCMD+="dpkg-buildpackage"
+BUILDPACKAGE_DPKGCMD+=("dpkg-buildpackage")
# Using dpkg-buildpackage args
# -us Allow unsigned sources
# -uc Allow unsigned changes
# -I Tar ignore
-BUILDPACKAGE_DPKG_ARGS=(-us -uc -I)
+BUILDPACKAGE_DPKGCMD+=(-us -uc -I)
# There can be also extra flags that are appended to args
if [ -n "$BUILDPACKAGE_FLAGS" ]
then
read -ra BUILDPACKAGE_TMP_ARGS <<< "$BUILDPACKAGE_FLAGS"
- BUILDPACKAGE_DPKG_ARGS=("${BUILDPACKAGE_DPKG_ARGS[@]} ${BUILDPACKAGE_TMP_ARGS[@]}")
+ BUILDPACKAGE_DPKGCMD+=( "${BUILDPACKAGE_TMP_ARGS[@]}" )
fi
# Build the package
# Pass -I so that .git and other unnecessary temporary and source control files
# will be ignored by dpkg-source when creating the tar.gz source package.
-fakeroot -- "${BUILDPACKAGE_DPKGCMD}" "${BUILDPACKAGE_DPKG_ARGS[@]}"
+"${BUILDPACKAGE_DPKGCMD[@]}"
# If the step above fails due to missing dependencies, you can manually run
# sudo mk-build-deps debian/control -r -i
diff --git a/debian/control b/debian/control
index 0ca6cfd5969..98ffb0731bc 100644
--- a/debian/control
+++ b/debian/control
@@ -808,6 +808,7 @@ Depends: libxml2,
unixodbc,
${misc:Depends},
${shlibs:Depends}
+Recommends: curl
Breaks: mariadb-connect-engine-10.0,
mariadb-connect-engine-10.1,
mariadb-connect-engine-10.2,
diff --git a/extra/mariabackup/backup_copy.cc b/extra/mariabackup/backup_copy.cc
index 093cffd6df7..dbf12cedd68 100644
--- a/extra/mariabackup/backup_copy.cc
+++ b/extra/mariabackup/backup_copy.cc
@@ -79,9 +79,8 @@ bool binlog_locked;
static void rocksdb_create_checkpoint();
static bool has_rocksdb_plugin();
-static void copy_or_move_dir(const char *from, const char *to, bool copy, bool allow_hardlinks);
-static void rocksdb_backup_checkpoint();
-static void rocksdb_copy_back();
+static void rocksdb_backup_checkpoint(ds_ctxt *ds_data);
+static void rocksdb_copy_back(ds_ctxt *ds_data);
static bool is_abs_path(const char *path)
{
@@ -136,7 +135,9 @@ struct datadir_thread_ctxt_t {
bool ret;
};
-static bool backup_files_from_datadir(const char *dir_path);
+static bool backup_files_from_datadir(ds_ctxt_t *ds_data,
+ const char *dir_path,
+ const char *prefix);
/************************************************************************
Retirn true if character if file separator */
@@ -807,7 +808,7 @@ if passes the rules for partial backup.
@return true if file backed up or skipped successfully. */
static
bool
-datafile_copy_backup(const char *filepath, uint thread_n)
+datafile_copy_backup(ds_ctxt *ds_data, const char *filepath, uint thread_n)
{
const char *ext_list[] = {"frm", "isl", "MYD", "MYI", "MAD", "MAI",
"MRG", "TRG", "TRN", "ARM", "ARZ", "CSM", "CSV", "opt", "par",
@@ -828,7 +829,7 @@ datafile_copy_backup(const char *filepath, uint thread_n)
}
if (filename_matches(filepath, ext_list)) {
- return copy_file(ds_data, filepath, filepath, thread_n);
+ return ds_data->copy_file(filepath, filepath, thread_n);
}
return(true);
@@ -869,7 +870,8 @@ datafile_rsync_backup(const char *filepath, bool save_to_list, FILE *f)
return(true);
}
-bool backup_file_print_buf(const char *filename, const char *buf, int buf_len)
+bool ds_ctxt_t::backup_file_print_buf(const char *filename,
+ const char *buf, int buf_len)
{
ds_file_t *dstfile = NULL;
MY_STAT stat; /* unused for now */
@@ -880,7 +882,7 @@ bool backup_file_print_buf(const char *filename, const char *buf, int buf_len)
stat.st_size = buf_len;
stat.st_mtime = my_time(0);
- dstfile = ds_open(ds_data, filename, &stat);
+ dstfile = ds_open(this, filename, &stat);
if (dstfile == NULL) {
msg("error: Can't open the destination stream for %s",
filename);
@@ -919,9 +921,9 @@ error_close:
return true;
};
-static
bool
-backup_file_vprintf(const char *filename, const char *fmt, va_list ap)
+ds_ctxt_t::backup_file_vprintf(const char *filename,
+ const char *fmt, va_list ap)
{
char *buf = 0;
int buf_len;
@@ -932,7 +934,7 @@ backup_file_vprintf(const char *filename, const char *fmt, va_list ap)
}
bool
-backup_file_printf(const char *filename, const char *fmt, ...)
+ds_ctxt_t::backup_file_printf(const char *filename, const char *fmt, ...)
{
bool result;
va_list ap;
@@ -1000,16 +1002,15 @@ run_data_threads(datadir_iter_t *it, void (*func)(datadir_thread_ctxt_t *ctxt),
Copy file for backup/restore.
@return true in case of success. */
bool
-copy_file(ds_ctxt_t *datasink,
- const char *src_file_path,
- const char *dst_file_path,
- uint thread_n)
+ds_ctxt_t::copy_file(const char *src_file_path,
+ const char *dst_file_path,
+ uint thread_n)
{
char dst_name[FN_REFLEN];
ds_file_t *dstfile = NULL;
datafile_cur_t cursor;
xb_fil_cur_result_t res;
- DBUG_ASSERT(datasink->datasink->remove);
+ DBUG_ASSERT(datasink->remove);
const char *dst_path =
(xtrabackup_copy_back || xtrabackup_move_back)?
dst_file_path : trim_dotslash(dst_file_path);
@@ -1020,7 +1021,7 @@ copy_file(ds_ctxt_t *datasink,
strncpy(dst_name, cursor.rel_path, sizeof(dst_name));
- dstfile = ds_open(datasink, dst_path, &cursor.statinfo);
+ dstfile = ds_open(this, dst_path, &cursor.statinfo);
if (dstfile == NULL) {
msg(thread_n,"error: "
"cannot open the destination stream for %s", dst_name);
@@ -1053,7 +1054,7 @@ copy_file(ds_ctxt_t *datasink,
error:
datafile_close(&cursor);
if (dstfile != NULL) {
- datasink->datasink->remove(dstfile->path);
+ datasink->remove(dstfile->path);
ds_close(dstfile);
}
@@ -1067,12 +1068,10 @@ error_close:
Try to move file by renaming it. If source and destination are on
different devices fall back to copy and unlink.
@return true in case of success. */
-static
bool
-move_file(ds_ctxt_t *datasink,
- const char *src_file_path,
- const char *dst_file_path,
- const char *dst_dir, uint thread_n)
+ds_ctxt_t::move_file(const char *src_file_path,
+ const char *dst_file_path,
+ const char *dst_dir, uint thread_n)
{
char errbuf[MYSYS_STRERROR_SIZE];
char dst_file_path_abs[FN_REFLEN];
@@ -1099,7 +1098,7 @@ move_file(ds_ctxt_t *datasink,
if (my_rename(src_file_path, dst_file_path_abs, MYF(0)) != 0) {
if (my_errno == EXDEV) {
/* Fallback to copy/unlink */
- if(!copy_file(datasink, src_file_path,
+ if(!copy_file(src_file_path,
dst_file_path, thread_n))
return false;
msg(thread_n,"Removing %s", src_file_path);
@@ -1178,13 +1177,13 @@ Copy or move file depending on current mode.
@return true in case of success. */
static
bool
-copy_or_move_file(const char *src_file_path,
+copy_or_move_file(ds_ctxt *datasink0, const char *src_file_path,
const char *dst_file_path,
const char *dst_dir,
uint thread_n,
bool copy = xtrabackup_copy_back)
{
- ds_ctxt_t *datasink = ds_data; /* copy to datadir by default */
+ ds_ctxt_t *datasink = datasink0; /* copy to datadir by default */
char filedir[FN_REFLEN];
size_t filedir_len;
bool ret;
@@ -1232,13 +1231,13 @@ copy_or_move_file(const char *src_file_path,
}
ret = (copy ?
- copy_file(datasink, src_file_path, dst_file_path, thread_n) :
- move_file(datasink, src_file_path, dst_file_path,
+ datasink->copy_file(src_file_path, dst_file_path, thread_n) :
+ datasink->move_file(src_file_path, dst_file_path,
dst_dir, thread_n));
cleanup:
- if (datasink != ds_data) {
+ if (datasink != datasink0) {
ds_destroy(datasink);
}
@@ -1250,7 +1249,7 @@ cleanup:
static
bool
-backup_files(const char *from, bool prep_mode)
+backup_files(ds_ctxt *ds_data, const char *from, bool prep_mode)
{
char rsync_tmpfile_name[FN_REFLEN];
FILE *rsync_tmpfile = NULL;
@@ -1288,7 +1287,7 @@ backup_files(const char *from, bool prep_mode)
ret = datafile_rsync_backup(node.filepath,
!prep_mode, rsync_tmpfile);
} else {
- ret = datafile_copy_backup(node.filepath, 1);
+ ret = datafile_copy_backup(ds_data, node.filepath, 1);
}
if (!ret) {
msg("Failed to copy file %s", node.filepath);
@@ -1299,7 +1298,7 @@ backup_files(const char *from, bool prep_mode)
char path[FN_REFLEN];
snprintf(path, sizeof(path),
"%s/db.opt", node.filepath);
- if (!(ret = backup_file_printf(
+ if (!(ret = ds_data->backup_file_printf(
trim_dotslash(path), "%s", ""))) {
msg("Failed to create file %s", path);
goto out;
@@ -1388,7 +1387,6 @@ out:
return(ret);
}
-void backup_fix_ddl(CorruptedPages &);
lsn_t get_current_lsn(MYSQL *connection)
{
@@ -1413,7 +1411,8 @@ lsn_t get_current_lsn(MYSQL *connection)
lsn_t server_lsn_after_lock;
extern void backup_wait_for_lsn(lsn_t lsn);
/** Start --backup */
-bool backup_start(CorruptedPages &corrupted_pages)
+bool backup_start(ds_ctxt *ds_data, ds_ctxt *ds_meta,
+ CorruptedPages &corrupted_pages)
{
if (!opt_no_lock) {
if (opt_safe_slave_backup) {
@@ -1422,7 +1421,7 @@ bool backup_start(CorruptedPages &corrupted_pages)
}
}
- if (!backup_files(fil_path_to_mysql_datadir, true)) {
+ if (!backup_files(ds_data, fil_path_to_mysql_datadir, true)) {
return(false);
}
@@ -1434,11 +1433,15 @@ bool backup_start(CorruptedPages &corrupted_pages)
server_lsn_after_lock = get_current_lsn(mysql_connection);
}
- if (!backup_files(fil_path_to_mysql_datadir, false)) {
+ if (!backup_files(ds_data, fil_path_to_mysql_datadir, false)) {
return(false);
}
- if (!backup_files_from_datadir(fil_path_to_mysql_datadir)) {
+ if (!backup_files_from_datadir(ds_data, fil_path_to_mysql_datadir,
+ "aws-kms-key") ||
+ !backup_files_from_datadir(ds_data,
+ aria_log_dir_path,
+ "aria_log")) {
return false;
}
@@ -1455,7 +1458,7 @@ bool backup_start(CorruptedPages &corrupted_pages)
my_sleep(milliseconds*1000UL);
});
- backup_fix_ddl(corrupted_pages);
+ corrupted_pages.backup_fix_ddl(ds_data, ds_meta);
// There is no need to stop slave thread before coping non-Innodb data when
// --no-lock option is used because --no-lock option requires that no DDL or
@@ -1471,7 +1474,7 @@ bool backup_start(CorruptedPages &corrupted_pages)
if (opt_slave_info) {
lock_binlog_maybe(mysql_connection);
- if (!write_slave_info(mysql_connection)) {
+ if (!write_slave_info(ds_data, mysql_connection)) {
return(false);
}
}
@@ -1483,7 +1486,7 @@ bool backup_start(CorruptedPages &corrupted_pages)
avoid that is to have a single process, i.e. merge innobackupex and
xtrabackup. */
if (opt_galera_info) {
- if (!write_galera_info(mysql_connection)) {
+ if (!write_galera_info(ds_data, mysql_connection)) {
return(false);
}
}
@@ -1491,7 +1494,7 @@ bool backup_start(CorruptedPages &corrupted_pages)
if (opt_binlog_info == BINLOG_INFO_ON) {
lock_binlog_maybe(mysql_connection);
- write_binlog_info(mysql_connection);
+ write_binlog_info(ds_data, mysql_connection);
}
if (!opt_no_lock) {
@@ -1528,20 +1531,20 @@ void backup_release()
static const char *default_buffer_pool_file = "ib_buffer_pool";
/** Finish after backup_start() and backup_release() */
-bool backup_finish()
+bool backup_finish(ds_ctxt *ds_data)
{
/* Copy buffer pool dump or LRU dump */
if (!opt_rsync && opt_galera_info) {
if (buffer_pool_filename && file_exists(buffer_pool_filename)) {
- copy_file(ds_data, buffer_pool_filename, default_buffer_pool_file, 0);
+ ds_data->copy_file(buffer_pool_filename, default_buffer_pool_file, 0);
}
if (file_exists("ib_lru_dump")) {
- copy_file(ds_data, "ib_lru_dump", "ib_lru_dump", 0);
+ ds_data->copy_file("ib_lru_dump", "ib_lru_dump", 0);
}
}
if (has_rocksdb_plugin()) {
- rocksdb_backup_checkpoint();
+ rocksdb_backup_checkpoint(ds_data);
}
msg("Backup created in directory '%s'", xtrabackup_target_dir);
@@ -1553,11 +1556,11 @@ bool backup_finish()
mysql_slave_position);
}
- if (!write_backup_config_file()) {
+ if (!write_backup_config_file(ds_data)) {
return(false);
}
- if (!write_xtrabackup_info(mysql_connection, XTRABACKUP_INFO,
+ if (!write_xtrabackup_info(ds_data, mysql_connection, XTRABACKUP_INFO,
opt_history != 0, true)) {
return(false);
}
@@ -1624,6 +1627,7 @@ ibx_copy_incremental_over_full()
bool ret = true;
char path[FN_REFLEN];
int i;
+ ds_ctxt *ds_data= NULL;
DBUG_ASSERT(!opt_galera_info);
datadir_node_init(&node);
@@ -1651,15 +1655,20 @@ ibx_copy_incremental_over_full()
unlink(node.filepath_rel);
}
- if (!(ret = copy_file(ds_data, node.filepath,
- node.filepath_rel, 1))) {
+ if (!(ret = ds_data->copy_file(node.filepath,
+ node.filepath_rel, 1))) {
msg("Failed to copy file %s",
node.filepath);
goto cleanup;
}
}
- if (!(ret = backup_files_from_datadir(xtrabackup_incremental_dir)))
+ if (!(ret = backup_files_from_datadir(ds_data,
+ xtrabackup_incremental_dir,
+ "aws-kms-key")) ||
+ !(ret = backup_files_from_datadir(ds_data,
+ xtrabackup_incremental_dir,
+ "aria_log")))
goto cleanup;
/* copy supplementary files */
@@ -1674,7 +1683,7 @@ ibx_copy_incremental_over_full()
if (file_exists(sup_files[i])) {
unlink(sup_files[i]);
}
- copy_file(ds_data, path, sup_files[i], 0);
+ ds_data->copy_file(path, sup_files[i], 0);
}
}
@@ -1688,7 +1697,7 @@ ibx_copy_incremental_over_full()
if (my_mkdir(ROCKSDB_BACKUP_DIR, 0777, MYF(0))) {
die("my_mkdir failed for " ROCKSDB_BACKUP_DIR);
}
- copy_or_move_dir(path, ROCKSDB_BACKUP_DIR, true, true);
+ ds_data->copy_or_move_dir(path, ROCKSDB_BACKUP_DIR, true, true);
}
ibx_incremental_drop_databases(xtrabackup_target_dir,
xtrabackup_incremental_dir);
@@ -1774,6 +1783,39 @@ public:
}
};
+
+static inline bool
+is_aria_log_dir_file(const datadir_node_t &node)
+{
+ return starts_with(node.filepath_rel, "aria_log");
+}
+
+
+bool
+copy_back_aria_logs(const char *dstdir)
+{
+ std::unique_ptr<ds_ctxt_t, void (&)(ds_ctxt_t*)>
+ ds_ctxt_aria_log_dir_path(ds_create(dstdir, DS_TYPE_LOCAL), ds_destroy);
+
+ datadir_node_t node;
+ datadir_node_init(&node);
+ datadir_iter_t *it = datadir_iter_new(".", false);
+
+ while (datadir_iter_next(it, &node))
+ {
+ if (!is_aria_log_dir_file(node))
+ continue;
+ if (!copy_or_move_file(ds_ctxt_aria_log_dir_path.get(),
+ node.filepath, node.filepath_rel,
+ dstdir, 1))
+ return false;
+ }
+ datadir_node_free(&node);
+ datadir_iter_free(it);
+ return true;
+}
+
+
bool
copy_back()
{
@@ -1820,6 +1862,13 @@ copy_back()
return(false);
}
+ Copy_back_dst_dir aria_log_dir_path_dst;
+ const char *aria_log_dir_path_abs= aria_log_dir_path_dst.make(aria_log_dir_path);
+ if (aria_log_dir_path && *aria_log_dir_path
+ && !directory_exists(aria_log_dir_path_abs, true)) {
+ return false;
+ }
+
/* cd to backup directory */
if (my_setwd(xtrabackup_target_dir, MYF(MY_WME)))
{
@@ -1827,6 +1876,9 @@ copy_back()
return(false);
}
+ if (!copy_back_aria_logs(aria_log_dir_path_abs))
+ return false;
+
/* parse data file path */
if (!innobase_data_file_path) {
@@ -1848,7 +1900,7 @@ copy_back()
dst_dir = dst_dir_buf.make(srv_undo_dir);
- ds_data = ds_create(dst_dir, DS_TYPE_LOCAL);
+ ds_ctxt *ds_tmp = ds_create(dst_dir, DS_TYPE_LOCAL);
for (uint i = 1; i <= TRX_SYS_MAX_UNDO_SPACES; i++) {
char filename[20];
@@ -1856,14 +1908,14 @@ copy_back()
if (!file_exists(filename)) {
break;
}
- if (!(ret = copy_or_move_file(filename, filename,
+ if (!(ret = copy_or_move_file(ds_tmp, filename, filename,
dst_dir, 1))) {
goto cleanup;
}
}
- ds_destroy(ds_data);
- ds_data = NULL;
+ ds_destroy(ds_tmp);
+ ds_tmp = NULL;
/* copy redo logs */
@@ -1871,34 +1923,34 @@ copy_back()
/* --backup generates a single ib_logfile0, which we must copy. */
- ds_data = ds_create(dst_dir, DS_TYPE_LOCAL);
- if (!(ret = copy_or_move_file(LOG_FILE_NAME, LOG_FILE_NAME,
+ ds_tmp = ds_create(dst_dir, DS_TYPE_LOCAL);
+ if (!(ret = copy_or_move_file(ds_tmp, LOG_FILE_NAME, LOG_FILE_NAME,
dst_dir, 1))) {
goto cleanup;
}
- ds_destroy(ds_data);
+ ds_destroy(ds_tmp);
/* copy innodb system tablespace(s) */
dst_dir = dst_dir_buf.make(innobase_data_home_dir);
- ds_data = ds_create(dst_dir, DS_TYPE_LOCAL);
+ ds_tmp = ds_create(dst_dir, DS_TYPE_LOCAL);
for (Tablespace::const_iterator iter(srv_sys_space.begin()),
end(srv_sys_space.end());
iter != end;
++iter) {
const char *filepath = iter->filepath();
- if (!(ret = copy_or_move_file(base_name(filepath), filepath,
- dst_dir, 1))) {
+ if (!(ret = copy_or_move_file(ds_tmp, base_name(filepath),
+ filepath, dst_dir, 1))) {
goto cleanup;
}
}
- ds_destroy(ds_data);
+ ds_destroy(ds_tmp);
/* copy the rest of tablespaces */
- ds_data = ds_create(mysql_data_home, DS_TYPE_LOCAL);
+ ds_tmp = ds_create(mysql_data_home, DS_TYPE_LOCAL);
it = datadir_iter_new(".", false);
@@ -1913,6 +1965,10 @@ copy_back()
char c_tmp;
int i_tmp;
+ /* Skip aria log files */
+ if (is_aria_log_dir_file(node))
+ continue;
+
if (strstr(node.filepath,"/" ROCKSDB_BACKUP_DIR "/")
#ifdef _WIN32
|| strstr(node.filepath,"\\" ROCKSDB_BACKUP_DIR "\\")
@@ -1978,7 +2034,7 @@ copy_back()
}
}
- if (!(ret = copy_or_move_file(node.filepath, node.filepath_rel,
+ if (!(ret = copy_or_move_file(ds_tmp, node.filepath, node.filepath_rel,
mysql_data_home, 1))) {
goto cleanup;
}
@@ -1990,12 +2046,12 @@ copy_back()
if (file_exists(default_buffer_pool_file) &&
innobase_buffer_pool_filename) {
- copy_or_move_file(default_buffer_pool_file,
+ copy_or_move_file(ds_tmp, default_buffer_pool_file,
innobase_buffer_pool_filename,
mysql_data_home, 0);
}
- rocksdb_copy_back();
+ rocksdb_copy_back(ds_tmp);
cleanup:
if (it != NULL) {
@@ -2004,11 +2060,11 @@ cleanup:
datadir_node_free(&node);
- if (ds_data != NULL) {
- ds_destroy(ds_data);
+ if (ds_tmp != NULL) {
+ ds_destroy(ds_tmp);
}
- ds_data = NULL;
+ ds_tmp = NULL;
return(ret);
}
@@ -2108,7 +2164,7 @@ decrypt_decompress()
}
/* copy the rest of tablespaces */
- ds_data = ds_create(".", DS_TYPE_LOCAL);
+ ds_ctxt *ds_tmp = ds_create(".", DS_TYPE_LOCAL);
it = datadir_iter_new(".", false);
@@ -2121,11 +2177,11 @@ decrypt_decompress()
datadir_iter_free(it);
}
- if (ds_data != NULL) {
- ds_destroy(ds_data);
+ if (ds_tmp != NULL) {
+ ds_destroy(ds_tmp);
}
- ds_data = NULL;
+ ds_tmp = NULL;
return(ret);
}
@@ -2135,7 +2191,9 @@ decrypt_decompress()
Do not copy the Innodb files (ibdata1, redo log files),
as this is done in a separate step.
*/
-static bool backup_files_from_datadir(const char *dir_path)
+static bool backup_files_from_datadir(ds_ctxt_t *ds_data,
+ const char *dir_path,
+ const char *prefix)
{
os_file_dir_t dir = os_file_opendir(dir_path);
if (dir == IF_WIN(INVALID_HANDLE_VALUE, nullptr)) return false;
@@ -2158,8 +2216,7 @@ static bool backup_files_from_datadir(const char *dir_path)
if (!pname)
pname = info.name;
- if (!starts_with(pname, "aws-kms-key") &&
- !starts_with(pname, "aria_log"))
+ if (!starts_with(pname, prefix))
/* For ES exchange the above line with the following code:
(!xtrabackup_prepare || !xtrabackup_incremental_dir ||
!starts_with(pname, "aria_log")))
@@ -2172,7 +2229,7 @@ static bool backup_files_from_datadir(const char *dir_path)
std::string full_path(dir_path);
full_path.append(1, '/').append(info.name);
- if (!(ret = copy_file(ds_data, full_path.c_str() , info.name, 1)))
+ if (!(ret = ds_data->copy_file(full_path.c_str() , info.name, 1)))
break;
}
os_file_closedir(dir);
@@ -2222,13 +2279,14 @@ static char *trim_trailing_dir_sep(char *path)
Create a file hardlink.
@return true on success, false on error.
*/
-static bool make_hardlink(const char *from_path, const char *to_path)
+bool
+ds_ctxt_t::make_hardlink(const char *from_path, const char *to_path)
{
DBUG_EXECUTE_IF("no_hardlinks", return false;);
char to_path_full[FN_REFLEN];
if (!is_abs_path(to_path))
{
- fn_format(to_path_full, to_path, ds_data->root, "", MYF(MY_RELATIVE_PATH));
+ fn_format(to_path_full, to_path, root, "", MYF(MY_RELATIVE_PATH));
}
else
{
@@ -2249,7 +2307,9 @@ static bool make_hardlink(const char *from_path, const char *to_path)
Has optimization that allows to use hardlinks when possible
(source and destination are directories on the same device)
*/
-static void copy_or_move_dir(const char *from, const char *to, bool do_copy, bool allow_hardlinks)
+void
+ds_ctxt_t::copy_or_move_dir(const char *from, const char *to,
+ bool do_copy, bool allow_hardlinks)
{
datadir_node_t node;
datadir_node_init(&node);
@@ -2277,8 +2337,8 @@ static void copy_or_move_dir(const char *from, const char *to, bool do_copy, boo
if (!rc)
{
rc = (do_copy ?
- copy_file(ds_data, from_path, to_path, 1) :
- move_file(ds_data, from_path, node.filepath_rel,
+ copy_file(from_path, to_path, 1) :
+ move_file(from_path, node.filepath_rel,
to, 1));
}
if (!rc)
@@ -2375,7 +2435,7 @@ static void rocksdb_create_checkpoint()
remove temp.checkpoint directory (in server's datadir)
and release user level lock acquired inside rocksdb_create_checkpoint().
*/
-static void rocksdb_backup_checkpoint()
+static void rocksdb_backup_checkpoint(ds_ctxt *ds_data)
{
msg("Backing up rocksdb files.");
char rocksdb_backup_dir[FN_REFLEN];
@@ -2387,7 +2447,7 @@ static void rocksdb_backup_checkpoint()
die("Can't create rocksdb backup directory %s", rocksdb_backup_dir);
}
}
- copy_or_move_dir(rocksdb_checkpoint_dir, ROCKSDB_BACKUP_DIR, true, backup_to_directory);
+ ds_data->copy_or_move_dir(rocksdb_checkpoint_dir, ROCKSDB_BACKUP_DIR, true, backup_to_directory);
rocksdb_remove_checkpoint_directory();
rocksdb_unlock_checkpoint();
}
@@ -2395,7 +2455,7 @@ static void rocksdb_backup_checkpoint()
/*
Copies #rocksdb directory to the $rockdb_data_dir, on copy-back
*/
-static void rocksdb_copy_back() {
+static void rocksdb_copy_back(ds_ctxt *ds_data) {
if (access(ROCKSDB_BACKUP_DIR, 0))
return;
char rocksdb_home_dir[FN_REFLEN];
@@ -2407,5 +2467,5 @@ static void rocksdb_copy_back() {
xb_rocksdb_datadir?trim_dotslash(xb_rocksdb_datadir): ROCKSDB_BACKUP_DIR);
}
mkdirp(rocksdb_home_dir, 0777, MYF(0));
- copy_or_move_dir(ROCKSDB_BACKUP_DIR, rocksdb_home_dir, xtrabackup_copy_back, xtrabackup_copy_back);
+ ds_data->copy_or_move_dir(ROCKSDB_BACKUP_DIR, rocksdb_home_dir, xtrabackup_copy_back, xtrabackup_copy_back);
}
diff --git a/extra/mariabackup/backup_copy.h b/extra/mariabackup/backup_copy.h
index 62b2b1bc232..b4a323f2e89 100644
--- a/extra/mariabackup/backup_copy.h
+++ b/extra/mariabackup/backup_copy.h
@@ -14,30 +14,18 @@
extern bool binlog_locked;
-bool
-backup_file_printf(const char *filename, const char *fmt, ...)
- ATTRIBUTE_FORMAT(printf, 2, 0);
-
/************************************************************************
Return true if first and second arguments are the same path. */
bool
equal_paths(const char *first, const char *second);
-/************************************************************************
-Copy file for backup/restore.
-@return true in case of success. */
-bool
-copy_file(ds_ctxt_t *datasink,
- const char *src_file_path,
- const char *dst_file_path,
- uint thread_n);
-
/** Start --backup */
-bool backup_start(CorruptedPages &corrupted_pages);
+bool backup_start(ds_ctxt *ds_data, ds_ctxt *ds_meta,
+ CorruptedPages &corrupted_pages);
/** Release resources after backup_start() */
void backup_release();
/** Finish after backup_start() and backup_release() */
-bool backup_finish();
+bool backup_finish(ds_ctxt *ds_data);
bool
apply_log_finish();
bool
@@ -51,6 +39,5 @@ directory_exists(const char *dir, bool create);
lsn_t
get_current_lsn(MYSQL *connection);
-bool backup_file_print_buf(const char *filename, const char *buf, int buf_len);
#endif
diff --git a/extra/mariabackup/backup_mysql.cc b/extra/mariabackup/backup_mysql.cc
index 69448586b33..cf8a50517a8 100644
--- a/extra/mariabackup/backup_mysql.cc
+++ b/extra/mariabackup/backup_mysql.cc
@@ -327,6 +327,7 @@ bool get_mysql_vars(MYSQL *connection)
char *innodb_undo_directory_var= NULL;
char *innodb_page_size_var= NULL;
char *innodb_undo_tablespaces_var= NULL;
+ char *aria_log_dir_path_var= NULL;
char *page_zip_level_var= NULL;
char *ignore_db_dirs= NULL;
char *endptr;
@@ -355,6 +356,7 @@ bool get_mysql_vars(MYSQL *connection)
{"innodb_undo_tablespaces", &innodb_undo_tablespaces_var},
{"innodb_compression_level", &page_zip_level_var},
{"ignore_db_dirs", &ignore_db_dirs},
+ {"aria_log_dir_path", &aria_log_dir_path_var},
{NULL, NULL}};
read_mysql_variables(connection, "SHOW VARIABLES", mysql_vars, true);
@@ -482,6 +484,12 @@ bool get_mysql_vars(MYSQL *connection)
ut_ad(*endptr == 0);
}
+ if (aria_log_dir_path_var)
+ {
+ aria_log_dir_path= my_strdup(PSI_NOT_INSTRUMENTED,
+ aria_log_dir_path_var, MYF(MY_FAE));
+ }
+
if (page_zip_level_var != NULL)
{
page_zip_level= static_cast<uint>(strtoul(page_zip_level_var, &endptr,
@@ -1322,7 +1330,7 @@ variable.
@returns true on success
*/
bool
-write_slave_info(MYSQL *connection)
+write_slave_info(ds_ctxt *datasink, MYSQL *connection)
{
String sql, comment;
@@ -1339,7 +1347,8 @@ write_slave_info(MYSQL *connection)
}
mysql_slave_position= strdup(comment.c_ptr());
- return backup_file_print_buf(XTRABACKUP_SLAVE_INFO, sql.ptr(), sql.length());
+ return datasink->backup_file_print_buf(XTRABACKUP_SLAVE_INFO,
+ sql.ptr(), sql.length());
}
@@ -1347,7 +1356,7 @@ write_slave_info(MYSQL *connection)
Retrieves MySQL Galera and
saves it in a file. It also prints it to stdout. */
bool
-write_galera_info(MYSQL *connection)
+write_galera_info(ds_ctxt *datasink, MYSQL *connection)
{
char *state_uuid = NULL, *state_uuid55 = NULL;
char *last_committed = NULL, *last_committed55 = NULL;
@@ -1379,12 +1388,12 @@ write_galera_info(MYSQL *connection)
goto cleanup;
}
- result = backup_file_printf(XTRABACKUP_GALERA_INFO,
+ result = datasink->backup_file_printf(XTRABACKUP_GALERA_INFO,
"%s:%s\n", state_uuid ? state_uuid : state_uuid55,
last_committed ? last_committed : last_committed55);
if (result)
{
- write_current_binlog_file(connection);
+ write_current_binlog_file(datasink, connection);
}
cleanup:
@@ -1398,7 +1407,7 @@ cleanup:
Flush and copy the current binary log file into the backup,
if GTID is enabled */
bool
-write_current_binlog_file(MYSQL *connection)
+write_current_binlog_file(ds_ctxt *datasink, MYSQL *connection)
{
char *executed_gtid_set = NULL;
char *gtid_binlog_state = NULL;
@@ -1468,7 +1477,7 @@ write_current_binlog_file(MYSQL *connection)
snprintf(filepath, sizeof(filepath), "%s%c%s",
log_bin_dir, FN_LIBCHAR, log_bin_file);
- result = copy_file(ds_data, filepath, log_bin_file, 0);
+ result = datasink->copy_file(filepath, log_bin_file, 0);
}
cleanup:
@@ -1484,7 +1493,7 @@ cleanup:
Retrieves MySQL binlog position and
saves it in a file. It also prints it to stdout. */
bool
-write_binlog_info(MYSQL *connection)
+write_binlog_info(ds_ctxt *datasink, MYSQL *connection)
{
char *filename = NULL;
char *position = NULL;
@@ -1529,14 +1538,14 @@ write_binlog_info(MYSQL *connection)
"filename '%s', position '%s', "
"GTID of the last change '%s'",
filename, position, gtid) != -1);
- result = backup_file_printf(XTRABACKUP_BINLOG_INFO,
+ result = datasink->backup_file_printf(XTRABACKUP_BINLOG_INFO,
"%s\t%s\t%s\n", filename, position,
gtid);
} else {
ut_a(asprintf(&mysql_binlog_position,
"filename '%s', position '%s'",
filename, position) != -1);
- result = backup_file_printf(XTRABACKUP_BINLOG_INFO,
+ result = datasink->backup_file_printf(XTRABACKUP_BINLOG_INFO,
"%s\t%s\n", filename, position);
}
@@ -1576,8 +1585,9 @@ mysql.mariabackup_history and writes a new history record to the
table containing all the history info particular to the just completed
backup. */
bool
-write_xtrabackup_info(MYSQL *connection, const char * filename, bool history,
- bool stream)
+write_xtrabackup_info(ds_ctxt *datasink,
+ MYSQL *connection, const char * filename, bool history,
+ bool stream)
{
bool result = true;
@@ -1653,7 +1663,7 @@ write_xtrabackup_info(MYSQL *connection, const char * filename, bool history,
}
if (stream) {
- backup_file_printf(filename, "%s", buf);
+ datasink->backup_file_printf(filename, "%s", buf);
} else {
fp = fopen(filename, "w");
if (!fp) {
@@ -1772,9 +1782,9 @@ static std::string make_local_paths(const char *data_file_path)
return buf.str();
}
-bool write_backup_config_file()
+bool write_backup_config_file(ds_ctxt *datasink)
{
- int rc= backup_file_printf("backup-my.cnf",
+ int rc= datasink->backup_file_printf("backup-my.cnf",
"# This options file was generated by innobackupex.\n\n"
"# The server\n"
"[mysqld]\n"
diff --git a/extra/mariabackup/backup_mysql.h b/extra/mariabackup/backup_mysql.h
index eb4ed42c048..4b08da0b939 100644
--- a/extra/mariabackup/backup_mysql.h
+++ b/extra/mariabackup/backup_mysql.h
@@ -58,17 +58,18 @@ void
unlock_all(MYSQL *connection);
bool
-write_current_binlog_file(MYSQL *connection);
+write_current_binlog_file(ds_ctxt *datasink, MYSQL *connection);
bool
-write_binlog_info(MYSQL *connection);
+write_binlog_info(ds_ctxt *datasink, MYSQL *connection);
bool
-write_xtrabackup_info(MYSQL *connection, const char * filename, bool history,
- bool stream);
+write_xtrabackup_info(ds_ctxt *datasink,
+ MYSQL *connection, const char * filename, bool history,
+ bool stream);
bool
-write_backup_config_file();
+write_backup_config_file(ds_ctxt *datasink);
bool
lock_binlog_maybe(MYSQL *connection);
@@ -80,10 +81,10 @@ bool
wait_for_safe_slave(MYSQL *connection);
bool
-write_galera_info(MYSQL *connection);
+write_galera_info(ds_ctxt *datasink, MYSQL *connection);
bool
-write_slave_info(MYSQL *connection);
+write_slave_info(ds_ctxt *datasink, MYSQL *connection);
#endif
diff --git a/extra/mariabackup/datasink.h b/extra/mariabackup/datasink.h
index 4bede4ec9e7..57468e0c9c7 100644
--- a/extra/mariabackup/datasink.h
+++ b/extra/mariabackup/datasink.h
@@ -37,6 +37,35 @@ typedef struct ds_ctxt {
char *root;
void *ptr;
struct ds_ctxt *pipe_ctxt;
+ /*
+ Copy file for backup/restore.
+ @return true in case of success.
+ */
+ bool copy_file(const char *src_file_path,
+ const char *dst_file_path,
+ uint thread_n);
+
+ bool move_file(const char *src_file_path,
+ const char *dst_file_path,
+ const char *dst_dir,
+ uint thread_n);
+
+ bool make_hardlink(const char *from_path, const char *to_path);
+
+ void copy_or_move_dir(const char *from, const char *to,
+ bool do_copy, bool allow_hardlinks);
+
+ bool backup_file_vprintf(const char *filename,
+ const char *fmt, va_list ap);
+
+ bool backup_file_print_buf(const char *filename,
+ const char *buf,
+ int buf_len);
+
+ bool backup_file_printf(const char *filename,
+ const char *fmt, ...)
+ ATTRIBUTE_FORMAT(printf, 2, 0);
+
} ds_ctxt_t;
typedef struct {
diff --git a/extra/mariabackup/write_filt.cc b/extra/mariabackup/write_filt.cc
index 086d4078e31..052cea26ef6 100644
--- a/extra/mariabackup/write_filt.cc
+++ b/extra/mariabackup/write_filt.cc
@@ -31,7 +31,8 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA
/************************************************************************
Write-through page write filter. */
-static my_bool wf_wt_init(xb_write_filt_ctxt_t *ctxt, char *dst_name,
+static my_bool wf_wt_init(ds_ctxt *ds_meta,
+ xb_write_filt_ctxt_t *ctxt, char *dst_name,
xb_fil_cur_t *cursor, CorruptedPages *corrupted_pages);
static my_bool wf_wt_process(xb_write_filt_ctxt_t *ctxt, ds_file_t *dstfile);
@@ -44,7 +45,8 @@ xb_write_filt_t wf_write_through = {
/************************************************************************
Incremental page write filter. */
-static my_bool wf_incremental_init(xb_write_filt_ctxt_t *ctxt, char *dst_name,
+static my_bool wf_incremental_init(ds_ctxt *ds_meta,
+ xb_write_filt_ctxt_t *ctxt, char *dst_name,
xb_fil_cur_t *cursor, CorruptedPages *corrupted_pages);
static my_bool wf_incremental_process(xb_write_filt_ctxt_t *ctxt,
ds_file_t *dstfile);
@@ -64,7 +66,8 @@ Initialize incremental page write filter.
@return TRUE on success, FALSE on error. */
static my_bool
-wf_incremental_init(xb_write_filt_ctxt_t *ctxt, char *dst_name,
+wf_incremental_init(ds_ctxt *ds_meta,
+ xb_write_filt_ctxt_t *ctxt, char *dst_name,
xb_fil_cur_t *cursor, CorruptedPages *corrupted_pages)
{
char meta_name[FN_REFLEN];
@@ -88,7 +91,7 @@ wf_incremental_init(xb_write_filt_ctxt_t *ctxt, char *dst_name,
XB_DELTA_INFO_SUFFIX);
const xb_delta_info_t info(cursor->page_size, cursor->zip_size,
cursor->space_id);
- if (!xb_write_delta_metadata(meta_name, &info)) {
+ if (!xb_write_delta_metadata(ds_meta, meta_name, &info)) {
msg(cursor->thread_n,"Error: "
"failed to write meta info for %s",
cursor->rel_path);
@@ -207,7 +210,8 @@ Initialize the write-through page write filter.
@return TRUE on success, FALSE on error. */
static my_bool
-wf_wt_init(xb_write_filt_ctxt_t *ctxt, char *dst_name __attribute__((unused)),
+wf_wt_init(ds_ctxt *ds_meta __attribute__((unused)),
+ xb_write_filt_ctxt_t *ctxt, char *dst_name __attribute__((unused)),
xb_fil_cur_t *cursor, CorruptedPages *)
{
ctxt->cursor = cursor;
diff --git a/extra/mariabackup/write_filt.h b/extra/mariabackup/write_filt.h
index 6c3ef24291f..a0ce0778a7f 100644
--- a/extra/mariabackup/write_filt.h
+++ b/extra/mariabackup/write_filt.h
@@ -45,7 +45,8 @@ typedef struct {
typedef struct {
- my_bool (*init)(xb_write_filt_ctxt_t *ctxt, char *dst_name,
+ my_bool (*init)(ds_ctxt *ds_meta,
+ xb_write_filt_ctxt_t *ctxt, char *dst_name,
xb_fil_cur_t *cursor, CorruptedPages *corrupted_pages);
my_bool (*process)(xb_write_filt_ctxt_t *ctxt, ds_file_t *dstfile);
my_bool (*finalize)(xb_write_filt_ctxt_t *, ds_file_t *dstfile);
diff --git a/extra/mariabackup/xtrabackup.cc b/extra/mariabackup/xtrabackup.cc
index 2549a9ce470..587d572fe91 100644
--- a/extra/mariabackup/xtrabackup.cc
+++ b/extra/mariabackup/xtrabackup.cc
@@ -117,6 +117,12 @@ Street, Fifth Floor, Boston, MA 02110-1335 USA
#define MB_CORRUPTED_PAGES_FILE "innodb_corrupted_pages"
+// disable server's systemd notification code
+extern "C" {
+int sd_notify() { return 0; }
+int sd_notifyf() { return 0; }
+}
+
int sys_var_init();
/* === xtrabackup specific options === */
@@ -256,6 +262,7 @@ static char* innobase_ignored_opt;
char* innobase_data_home_dir;
char* innobase_data_file_path;
+char *aria_log_dir_path;
my_bool xtrabackup_incremental_force_scan = FALSE;
@@ -273,10 +280,66 @@ char *xb_plugin_dir;
char *xb_plugin_load;
my_bool xb_close_files;
-/* Datasinks */
-ds_ctxt_t *ds_data = NULL;
-ds_ctxt_t *ds_meta = NULL;
-ds_ctxt_t *ds_redo = NULL;
+
+class Datasink_free_list
+{
+protected:
+ /*
+ Simple datasink creation tracking...
+ add datasinks in the reverse order you want them destroyed.
+ */
+#define XTRABACKUP_MAX_DATASINKS 10
+ ds_ctxt_t *m_datasinks_to_destroy[XTRABACKUP_MAX_DATASINKS];
+ uint m_actual_datasinks_to_destroy;
+public:
+ Datasink_free_list()
+ :m_actual_datasinks_to_destroy(0)
+ { }
+
+ void add_datasink_to_destroy(ds_ctxt_t *ds)
+ {
+ xb_ad(m_actual_datasinks_to_destroy < XTRABACKUP_MAX_DATASINKS);
+ m_datasinks_to_destroy[m_actual_datasinks_to_destroy] = ds;
+ m_actual_datasinks_to_destroy++;
+ }
+
+ /*
+ Destroy datasinks.
+ Destruction is done in the specific order to not violate their order in the
+ pipeline so that each datasink is able to flush data down the pipeline.
+ */
+ void destroy()
+ {
+ for (uint i= m_actual_datasinks_to_destroy; i > 0; i--)
+ {
+ ds_destroy(m_datasinks_to_destroy[i - 1]);
+ m_datasinks_to_destroy[i - 1] = NULL;
+ }
+ }
+};
+
+
+class Backup_datasinks: public Datasink_free_list
+{
+public:
+ ds_ctxt_t *m_data;
+ ds_ctxt_t *m_meta;
+ ds_ctxt_t *m_redo;
+
+ Backup_datasinks()
+ :m_data(NULL),
+ m_meta(NULL),
+ m_redo(NULL)
+ { }
+ void init();
+ void destroy()
+ {
+ Datasink_free_list::destroy();
+ *this= Backup_datasinks();
+ }
+ bool backup_low();
+};
+
static bool innobackupex_mode = false;
@@ -469,7 +532,8 @@ void CorruptedPages::rename_space(uint32_t space_id,
ut_a(!pthread_mutex_unlock(&m_mutex));
}
-bool CorruptedPages::print_to_file(const char *filename) const
+bool CorruptedPages::print_to_file(ds_ctxt *ds_data,
+ const char *filename) const
{
std::ostringstream out;
ut_a(!pthread_mutex_lock(&m_mutex));
@@ -497,8 +561,8 @@ bool CorruptedPages::print_to_file(const char *filename) const
out << "\n";
}
ut_a(!pthread_mutex_unlock(&m_mutex));
- if (xtrabackup_backup)
- return backup_file_print_buf(filename, out.str().c_str(),
+ if (ds_data)
+ return ds_data->backup_file_print_buf(filename, out.str().c_str(),
static_cast<int>(out.str().size()));
std::ofstream outfile;
outfile.open(filename);
@@ -620,19 +684,6 @@ void CorruptedPages::zero_out_free_pages()
aligned_free(zero_page);
}
-/* Simple datasink creation tracking...add datasinks in the reverse order you
-want them destroyed. */
-#define XTRABACKUP_MAX_DATASINKS 10
-static ds_ctxt_t *datasinks[XTRABACKUP_MAX_DATASINKS];
-static uint actual_datasinks = 0;
-static inline
-void
-xtrabackup_add_datasink(ds_ctxt_t *ds)
-{
- xb_ad(actual_datasinks < XTRABACKUP_MAX_DATASINKS);
- datasinks[actual_datasinks] = ds; actual_datasinks++;
-}
-
typedef void (*process_single_tablespace_func_t)(const char *dirname,
const char *filname,
bool is_remote,
@@ -973,6 +1024,7 @@ typedef struct {
uint *count;
pthread_mutex_t* count_mutex;
CorruptedPages *corrupted_pages;
+ Backup_datasinks *datasinks;
} data_thread_ctxt_t;
/* ======== for option and variables ======== */
@@ -1078,7 +1130,8 @@ enum options_xtrabackup
OPT_XTRA_CHECK_PRIVILEGES,
OPT_XTRA_MYSQLD_ARGS,
OPT_XB_IGNORE_INNODB_PAGE_CORRUPTION,
- OPT_INNODB_FORCE_RECOVERY
+ OPT_INNODB_FORCE_RECOVERY,
+ OPT_ARIA_LOG_DIR_PATH
};
struct my_option xb_client_options[]= {
@@ -1671,6 +1724,11 @@ struct my_option xb_server_options[] =
&xb_plugin_dir, &xb_plugin_dir,
0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
+ {"aria_log_dir_path", OPT_ARIA_LOG_DIR_PATH,
+ "Path to individual files and their sizes.",
+ &aria_log_dir_path, &aria_log_dir_path,
+ 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+
{"open_files_limit", OPT_OPEN_FILES_LIMIT, "the maximum number of file "
"descriptors to reserve with setrlimit().",
(G_PTR*) &xb_open_files_limit, (G_PTR*) &xb_open_files_limit, 0, GET_ULONG,
@@ -1983,6 +2041,10 @@ xb_get_one_option(const struct my_option *opt,
}
break;
+ case OPT_ARIA_LOG_DIR_PATH:
+ ADD_PRINT_PARAM_OPT(aria_log_dir_path);
+ break;
+
case OPT_XTRA_TARGET_DIR:
strmake(xtrabackup_real_target_dir,argument, sizeof(xtrabackup_real_target_dir)-1);
xtrabackup_target_dir= xtrabackup_real_target_dir;
@@ -2547,7 +2609,8 @@ xb_read_delta_metadata(const char *filepath, xb_delta_info_t *info)
Write meta info for an incremental delta.
@return TRUE on success, FALSE on failure. */
my_bool
-xb_write_delta_metadata(const char *filename, const xb_delta_info_t *info)
+xb_write_delta_metadata(ds_ctxt *ds_meta,
+ const char *filename, const xb_delta_info_t *info)
{
ds_file_t *f;
char buf[64];
@@ -2878,7 +2941,9 @@ xb_get_copy_action(const char *dflt)
for full backup, pages filter for incremental backup, etc.
@return FALSE on success and TRUE on error */
-static my_bool xtrabackup_copy_datafile(fil_node_t *node, uint thread_n,
+static my_bool xtrabackup_copy_datafile(ds_ctxt *ds_data,
+ ds_ctxt *ds_meta,
+ fil_node_t *node, uint thread_n,
const char *dest_name,
const xb_write_filt_t &write_filter,
CorruptedPages &corrupted_pages)
@@ -2936,7 +3001,7 @@ static my_bool xtrabackup_copy_datafile(fil_node_t *node, uint thread_n,
ut_a(write_filter.process != NULL);
if (write_filter.init != NULL &&
- !write_filter.init(&write_filt_ctxt, dst_name, &cursor,
+ !write_filter.init(ds_meta, &write_filt_ctxt, dst_name, &cursor,
opt_log_innodb_page_corruption ? &corrupted_pages : NULL)) {
msg (thread_n, "mariabackup: error: failed to initialize page write filter.");
goto error;
@@ -3273,7 +3338,8 @@ static void data_copy_thread_func(data_thread_ctxt_t *ctxt) /* thread context */
node->space->name(),
backup_wait_for_lsn(get_current_lsn(mysql_connection)););
/* copy the datafile */
- if (xtrabackup_copy_datafile(node, num, NULL,
+ if (xtrabackup_copy_datafile(ctxt->datasinks->m_data,
+ ctxt->datasinks->m_meta, node, num, NULL,
xtrabackup_incremental ? wf_incremental : wf_write_through,
*ctxt->corrupted_pages))
die("failed to copy datafile.");
@@ -3296,22 +3362,21 @@ Otherwise (i.e. when streaming in the 'tar' format) we need 2 separate datasinks
for the data stream (and don't allow parallel data copying) and for metainfo
files (including ib_logfile0). The second datasink writes to temporary
files first, and then streams them in a serialized way when closed. */
-static void
-xtrabackup_init_datasinks(void)
+void Backup_datasinks::init()
{
/* Start building out the pipelines from the terminus back */
if (xtrabackup_stream) {
/* All streaming goes to stdout */
- ds_data = ds_meta = ds_redo = ds_create(xtrabackup_target_dir,
- DS_TYPE_STDOUT);
+ m_data = m_meta = m_redo = ds_create(xtrabackup_target_dir,
+ DS_TYPE_STDOUT);
} else {
/* Local filesystem */
- ds_data = ds_meta = ds_redo = ds_create(xtrabackup_target_dir,
- DS_TYPE_LOCAL);
+ m_data = m_meta = m_redo = ds_create(xtrabackup_target_dir,
+ DS_TYPE_LOCAL);
}
/* Track it for destruction */
- xtrabackup_add_datasink(ds_data);
+ add_datasink_to_destroy(m_data);
/* Stream formatting */
if (xtrabackup_stream) {
@@ -3320,66 +3385,50 @@ xtrabackup_init_datasinks(void)
ut_a(xtrabackup_stream_fmt == XB_STREAM_FMT_XBSTREAM);
ds = ds_create(xtrabackup_target_dir, DS_TYPE_XBSTREAM);
- xtrabackup_add_datasink(ds);
+ add_datasink_to_destroy(ds);
- ds_set_pipe(ds, ds_data);
- ds_data = ds;
+ ds_set_pipe(ds, m_data);
+ m_data = ds;
- ds_redo = ds_meta = ds_data;
+ m_redo = m_meta = m_data;
}
- /* Compression for ds_data and ds_redo */
+ /* Compression for m_data and m_redo */
if (xtrabackup_compress) {
ds_ctxt_t *ds;
/* Use a 1 MB buffer for compressed output stream */
ds = ds_create(xtrabackup_target_dir, DS_TYPE_BUFFER);
ds_buffer_set_size(ds, 1024 * 1024);
- xtrabackup_add_datasink(ds);
- ds_set_pipe(ds, ds_data);
- if (ds_data != ds_redo) {
- ds_data = ds;
+ add_datasink_to_destroy(ds);
+ ds_set_pipe(ds, m_data);
+ if (m_data != m_redo) {
+ m_data = ds;
ds = ds_create(xtrabackup_target_dir, DS_TYPE_BUFFER);
ds_buffer_set_size(ds, 1024 * 1024);
- xtrabackup_add_datasink(ds);
- ds_set_pipe(ds, ds_redo);
- ds_redo = ds;
+ add_datasink_to_destroy(ds);
+ ds_set_pipe(ds, m_redo);
+ m_redo = ds;
} else {
- ds_redo = ds_data = ds;
+ m_redo = m_data = ds;
}
ds = ds_create(xtrabackup_target_dir, DS_TYPE_COMPRESS);
- xtrabackup_add_datasink(ds);
- ds_set_pipe(ds, ds_data);
- if (ds_data != ds_redo) {
- ds_data = ds;
+ add_datasink_to_destroy(ds);
+ ds_set_pipe(ds, m_data);
+ if (m_data != m_redo) {
+ m_data = ds;
ds = ds_create(xtrabackup_target_dir, DS_TYPE_COMPRESS);
- xtrabackup_add_datasink(ds);
- ds_set_pipe(ds, ds_redo);
- ds_redo = ds;
+ add_datasink_to_destroy(ds);
+ ds_set_pipe(ds, m_redo);
+ m_redo = ds;
} else {
- ds_redo = ds_data = ds;
+ m_redo = m_data = ds;
}
}
}
-/************************************************************************
-Destroy datasinks.
-
-Destruction is done in the specific order to not violate their order in the
-pipeline so that each datasink is able to flush data down the pipeline. */
-static void xtrabackup_destroy_datasinks(void)
-{
- for (uint i = actual_datasinks; i > 0; i--) {
- ds_destroy(datasinks[i-1]);
- datasinks[i-1] = NULL;
- }
- ds_data = NULL;
- ds_meta = NULL;
- ds_redo = NULL;
-}
-
#define SRV_MAX_N_PENDING_SYNC_IOS 100
/** Initialize the tablespace cache subsystem. */
@@ -3493,21 +3542,20 @@ static void xb_load_single_table_tablespace(const char *dirname,
bool is_empty_file = file->exists() && file->is_empty_file();
if (err == DB_SUCCESS && file->space_id() != SRV_TMP_SPACE_ID) {
+ mysql_mutex_lock(&fil_system.mutex);
space = fil_space_t::create(
file->space_id(), file->flags(),
FIL_TYPE_TABLESPACE, nullptr/* TODO: crypt_data */,
FIL_ENCRYPTION_DEFAULT,
file->handle() != OS_FILE_CLOSED);
-
- ut_a(space != NULL);
+ ut_ad(space);
fil_node_t* node= space->add(
file->filepath(),
skip_node_page0 ? file->detach() : pfs_os_file_t(),
0, false, false);
node->deferred= defer;
- mysql_mutex_lock(&fil_system.mutex);
if (!space->read_page0())
- err= DB_CANNOT_OPEN_FILE;
+ err = DB_CANNOT_OPEN_FILE;
mysql_mutex_unlock(&fil_system.mutex);
if (srv_operation == SRV_OPERATION_RESTORE_DELTA
@@ -4410,7 +4458,7 @@ static void stop_backup_threads()
/** Implement the core of --backup
@return whether the operation succeeded */
-static bool xtrabackup_backup_low()
+bool Backup_datasinks::backup_low()
{
mysql_mutex_lock(&recv_sys.mutex);
ut_ad(!metadata_to_lsn);
@@ -4476,7 +4524,7 @@ static bool xtrabackup_backup_low()
}
metadata_last_lsn = recv_sys.lsn;
- if (!xtrabackup_stream_metadata(ds_meta)) {
+ if (!xtrabackup_stream_metadata(m_meta)) {
msg("Error: failed to stream metadata.");
return false;
}
@@ -4492,7 +4540,8 @@ static bool xtrabackup_backup_low()
}
sprintf(filename, "%s/%s", xtrabackup_extra_lsndir,
XTRABACKUP_INFO);
- if (!write_xtrabackup_info(mysql_connection, filename, false, false)) {
+ if (!write_xtrabackup_info(m_data,
+ mysql_connection, filename, false, false)) {
msg("Error: failed to write info "
"to '%s'.", filename);
return false;
@@ -4512,6 +4561,7 @@ static bool xtrabackup_backup_func()
pthread_mutex_t count_mutex;
CorruptedPages corrupted_pages;
data_thread_ctxt_t *data_threads;
+ Backup_datasinks backup_datasinks;
pthread_cond_init(&scanned_lsn_cond, NULL);
#ifdef USE_POSIX_FADVISE
@@ -4637,7 +4687,7 @@ fail:
goto fail;
}
- xtrabackup_init_datasinks();
+ backup_datasinks.init();
if (!select_history()) {
goto fail;
@@ -4645,7 +4695,7 @@ fail:
/* open the log file */
memset(&stat_info, 0, sizeof(MY_STAT));
- dst_log_file = ds_open(ds_redo, LOG_FILE_NAME, &stat_info);
+ dst_log_file = ds_open(backup_datasinks.m_redo, LOG_FILE_NAME, &stat_info);
if (dst_log_file == NULL) {
msg("Error: failed to open the target stream for '%s'.",
LOG_FILE_NAME);
@@ -4732,6 +4782,7 @@ fail:
data_threads[i].count = &count;
data_threads[i].count_mutex = &count_mutex;
data_threads[i].corrupted_pages = &corrupted_pages;
+ data_threads[i].datasinks= &backup_datasinks;
std::thread(data_copy_thread_func, data_threads + i).detach();
}
@@ -4749,10 +4800,13 @@ fail:
pthread_mutex_destroy(&count_mutex);
free(data_threads);
- bool ok = backup_start(corrupted_pages);
+ DBUG_ASSERT(backup_datasinks.m_data);
+ DBUG_ASSERT(backup_datasinks.m_meta);
+ bool ok = backup_start(backup_datasinks.m_data,
+ backup_datasinks.m_meta, corrupted_pages);
if (ok) {
- ok = xtrabackup_backup_low();
+ ok = backup_datasinks.backup_low();
backup_release();
@@ -4760,12 +4814,13 @@ fail:
pthread_join(dbug_alter_thread, nullptr););
if (ok) {
- backup_finish();
+ backup_finish(backup_datasinks.m_data);
}
}
if (opt_log_innodb_page_corruption)
- ok = corrupted_pages.print_to_file(MB_CORRUPTED_PAGES_FILE);
+ ok = corrupted_pages.print_to_file(backup_datasinks.m_data,
+ MB_CORRUPTED_PAGES_FILE);
if (!ok) {
goto fail;
@@ -4774,7 +4829,7 @@ fail:
if (changed_page_bitmap) {
xb_page_bitmap_deinit(changed_page_bitmap);
}
- xtrabackup_destroy_datasinks();
+ backup_datasinks.destroy();
msg("Redo log (from LSN " LSN_PF " to " LSN_PF ") was copied.",
log_sys.next_checkpoint_lsn, recv_sys.lsn);
@@ -4822,7 +4877,7 @@ FTWRL. This ensures consistent backup in presence of DDL.
It is the responsibility of the prepare phase to deal with .new, .ren, and .del
files.
*/
-void backup_fix_ddl(CorruptedPages &corrupted_pages)
+void CorruptedPages::backup_fix_ddl(ds_ctxt *ds_data, ds_ctxt *ds_meta)
{
std::set<std::string> dropped_tables;
std::map<std::string, std::string> renamed_tables;
@@ -4844,7 +4899,7 @@ void backup_fix_ddl(CorruptedPages &corrupted_pages)
if (ddl_tracker.drops.find(id) != ddl_tracker.drops.end()) {
dropped_tables.insert(name);
- corrupted_pages.drop_space(id);
+ drop_space(id);
continue;
}
@@ -4857,7 +4912,7 @@ void backup_fix_ddl(CorruptedPages &corrupted_pages)
if (new_name != name) {
renamed_tables[name] = new_name;
if (opt_log_innodb_page_corruption)
- corrupted_pages.rename_space(id, new_name);
+ rename_space(id, new_name);
}
}
@@ -4880,7 +4935,7 @@ void backup_fix_ddl(CorruptedPages &corrupted_pages)
dropped_tables.erase(name);
new_tables[id] = name;
if (opt_log_innodb_page_corruption)
- corrupted_pages.drop_space(id);
+ drop_space(id);
}
}
@@ -4889,7 +4944,8 @@ void backup_fix_ddl(CorruptedPages &corrupted_pages)
iter != renamed_tables.end(); ++iter) {
const std::string old_name = iter->first;
std::string new_name = iter->second;
- backup_file_printf((old_name + ".ren").c_str(), "%s", new_name.c_str());
+ DBUG_ASSERT(ds_data);
+ ds_data->backup_file_printf((old_name + ".ren").c_str(), "%s", new_name.c_str());
}
// Mark tablespaces for drop
@@ -4897,7 +4953,7 @@ void backup_fix_ddl(CorruptedPages &corrupted_pages)
iter != dropped_tables.end();
iter++) {
const std::string name(*iter);
- backup_file_printf((name + ".del").c_str(), "%s", "");
+ ds_data->backup_file_printf((name + ".del").c_str(), "%s", "");
}
// Load and copy new tables.
@@ -4972,8 +5028,9 @@ void backup_fix_ddl(CorruptedPages &corrupted_pages)
node->name, strlen(node->name));
dest_name.append(".new");
- xtrabackup_copy_datafile(node, 0, dest_name.c_str(), wf_write_through,
- corrupted_pages);
+ xtrabackup_copy_datafile(ds_data, ds_meta,
+ node, 0, dest_name.c_str(),
+ wf_write_through, *this);
}
}
@@ -5216,9 +5273,12 @@ exit:
ut_ad(fil_space_t::zip_size(flags) == info.zip_size);
ut_ad(fil_space_t::physical_size(flags) == info.page_size);
- if (fil_space_t::create(info.space_id, flags,
- FIL_TYPE_TABLESPACE, 0, FIL_ENCRYPTION_DEFAULT,
- true)) {
+ mysql_mutex_lock(&fil_system.mutex);
+ fil_space_t* space = fil_space_t::create(info.space_id, flags,
+ FIL_TYPE_TABLESPACE, 0,
+ FIL_ENCRYPTION_DEFAULT, true);
+ mysql_mutex_unlock(&fil_system.mutex);
+ if (space) {
*success = xb_space_create_file(real_name, info.space_id,
flags, &file);
} else {
@@ -6035,7 +6095,7 @@ error:
}
}
else
- corrupted_pages.print_to_file(MB_CORRUPTED_PAGES_FILE);
+ corrupted_pages.print_to_file(NULL, MB_CORRUPTED_PAGES_FILE);
if (ok) {
msg("Last binlog file %s, position %lld",
diff --git a/extra/mariabackup/xtrabackup.h b/extra/mariabackup/xtrabackup.h
index d2a5e31645b..53784a3fb56 100644
--- a/extra/mariabackup/xtrabackup.h
+++ b/extra/mariabackup/xtrabackup.h
@@ -49,11 +49,13 @@ public:
bool contains(page_id_t page_id) const;
void drop_space(uint32_t space_id);
void rename_space(uint32_t space_id, const std::string &new_name);
- bool print_to_file(const char *file_name) const;
+ bool print_to_file(ds_ctxt *ds_data, const char *file_name) const;
void read_from_file(const char *file_name);
bool empty() const;
void zero_out_free_pages();
+ void backup_fix_ddl(ds_ctxt *ds_data, ds_ctxt *ds_meta);
+
private:
void add_page_no_lock(const char *space_name, page_id_t page_id,
bool convert_space_name);
@@ -66,6 +68,7 @@ private:
container_t m_spaces;
};
+
/* value of the --incremental option */
extern lsn_t incremental_lsn;
@@ -74,13 +77,12 @@ extern char *xtrabackup_incremental_dir;
extern char *xtrabackup_incremental_basedir;
extern char *innobase_data_home_dir;
extern char *innobase_buffer_pool_filename;
+extern char *aria_log_dir_path;
extern char *xb_plugin_dir;
extern char *xb_rocksdb_datadir;
extern my_bool xb_backup_rocksdb;
extern uint opt_protocol;
-extern ds_ctxt_t *ds_meta;
-extern ds_ctxt_t *ds_data;
extern xb_page_bitmap *changed_page_bitmap;
@@ -177,7 +179,8 @@ extern ulong opt_binlog_info;
extern ulong xtrabackup_innodb_force_recovery;
void xtrabackup_io_throttling(void);
-my_bool xb_write_delta_metadata(const char *filename,
+my_bool xb_write_delta_metadata(ds_ctxt *ds_meta,
+ const char *filename,
const xb_delta_info_t *info);
/************************************************************************
diff --git a/include/my_alloca.h b/include/my_alloca.h
index 761c2adb890..85fa64e9558 100644
--- a/include/my_alloca.h
+++ b/include/my_alloca.h
@@ -32,7 +32,10 @@
#endif
#endif
-#if defined(HAVE_ALLOCA)
+#if defined(_AIX) && !defined(__GNUC__) && !defined(_AIX43)
+#pragma alloca
+#endif /* _AIX */
+
/*
If the GCC/LLVM compiler from the MinGW is used,
alloca may not be defined when using the MSVC CRT:
@@ -40,6 +43,5 @@
#if defined(__GNUC__) && !defined(HAVE_ALLOCA_H) && !defined(alloca)
#define alloca __builtin_alloca
#endif /* GNUC */
-#endif
#endif /* MY_ALLOCA_INCLUDED */
diff --git a/mysql-test/include/sql_mode_pad_char_to_full_length.inc b/mysql-test/include/sql_mode_pad_char_to_full_length.inc
new file mode 100644
index 00000000000..df03c4dbc28
--- /dev/null
+++ b/mysql-test/include/sql_mode_pad_char_to_full_length.inc
@@ -0,0 +1,31 @@
+--echo #
+--echo # MDEV-28190 sql_mode makes MDEV-371 virtual column expressions nondeterministic
+--echo #
+
+CREATE TABLE t1 (a INT,b CHAR(20));
+SHOW CREATE TABLE t1;
+CREATE UNIQUE INDEX bi USING HASH ON t1 (b);
+INSERT INTO t1 VALUES (0,0);
+SET sql_mode='pad_char_to_full_length';
+DELETE FROM t1;
+DROP TABLE t1;
+
+
+SET sql_mode='';
+CREATE TABLE t1 (a INT,b CHAR(20));
+SHOW CREATE TABLE t1;
+CREATE UNIQUE INDEX bi USING HASH ON t1 (b);
+SET sql_mode='pad_char_to_full_length';
+INSERT INTO t1 VALUES (0,0);
+DELETE FROM t1;
+DROP TABLE t1;
+
+
+SET sql_mode='';
+CREATE OR REPLACE TABLE t1 (a CHAR(20),b CHAR(20));
+SHOW CREATE TABLE t1;
+CREATE UNIQUE INDEX bi USING HASH ON t1 (b);
+INSERT INTO t1 VALUES (0,0);
+SET sql_mode='pad_char_to_full_length';
+DELETE FROM t1;
+DROP TABLE t1;
diff --git a/mysql-test/lib/My/CoreDump.pm b/mysql-test/lib/My/CoreDump.pm
index 05b6edf1385..be6d21146d1 100644
--- a/mysql-test/lib/My/CoreDump.pm
+++ b/mysql-test/lib/My/CoreDump.pm
@@ -310,16 +310,8 @@ sub cdb_check {
`cdb -? 2>&1`;
if ($? >> 8)
{
- print "Cannot find cdb. Please Install Debugging tools for Windows\n";
- print "from http://www.microsoft.com/whdc/devtools/debugging/";
- if($ENV{'ProgramW6432'})
- {
- print "install64bit.mspx (native x64 version)\n";
- }
- else
- {
- print "installx86.mspx\n";
- }
+ print "Cannot find the cdb debugger. Please install Debugging tools for Windows\n";
+ print "and set PATH environment variable to include location of cdb.exe";
}
}
@@ -328,25 +320,6 @@ sub _cdb {
my ($core_name, $format)= @_;
print "\nTrying 'cdb' to get a backtrace\n";
return unless -f $core_name;
-
- # Try to set environment for debugging tools for Windows
- if ($ENV{'PATH'} !~ /Debugging Tools/)
- {
- if ($ENV{'ProgramW6432'})
- {
- # On x64 computer
- $ENV{'PATH'}.= ";".$ENV{'ProgramW6432'}."\\Debugging Tools For Windows (x64)";
- }
- else
- {
- # On x86 computer. Newest versions of Debugging tools are installed in the
- # directory with (x86) suffix, older versions did not have this suffix.
- $ENV{'PATH'}.= ";".$ENV{'ProgramFiles'}."\\Debugging Tools For Windows (x86)";
- $ENV{'PATH'}.= ";".$ENV{'ProgramFiles'}."\\Debugging Tools For Windows";
- }
- }
-
-
# Read module list, find out the name of executable and
# build symbol path (required by cdb if executable was built on
# different machine)
@@ -384,7 +357,7 @@ sub _cdb {
if (!$ENV{'_NT_SYMBOL_PATH'})
{
my $windir= $ENV{'windir'};
- my $symbol_cache= substr($windir ,0, index($windir,'\\'))."\\cdb_symbols";
+ my $symbol_cache= substr($windir ,0, index($windir,'\\'))."\\symbols";
print "OS debug symbols will be downloaded and stored in $symbol_cache.\n";
print "You can control the location of symbol cache with _NT_SYMBOL_PATH\n";
diff --git a/mysql-test/main/bootstrap.result b/mysql-test/main/bootstrap.result
index fd9b4726cb6..cfff13136d6 100644
--- a/mysql-test/main/bootstrap.result
+++ b/mysql-test/main/bootstrap.result
@@ -1,15 +1,28 @@
-drop table if exists t1;
+#
+# test mysqld in bootstrap mode
+#
+#
+# Check that --bootstrap reads from stdin
+#
# Kill the server
# restart
drop table t1;
+#
+# Check that --bootstrap of file with SQL error returns error
+#
# Kill the server
# restart
drop table t1;
ERROR 42S02: Unknown table 'test.t1'
+#
+# Bootstrap with a large thd->net.max_packet
+#
# Kill the server
# restart
drop table t1;
-End of 5.1 tests
+#
+# End of 5.1 tests
+#
#
# Bug #11766306: 59393: HAVE_INNODB=YES WHEN MYSQLD
# STARTED WITH --SKIP-INNODB
@@ -18,9 +31,23 @@ SELECT 'bug' as '' FROM INFORMATION_SCHEMA.ENGINES WHERE engine='innodb'
and SUPPORT='YES';
# Kill the server
+#
+# MDEV-13063 Server crashes in intern_plugin_lock or assertion `plugin_ptr->ref_count == 1' fails in plugin_init
+#
+#
+# MDEV-19349 mysql_install_db: segfault at tmp_file_prefix check
+#
# restart
-End of 5.5 tests
+#
+# End of 5.5 tests
+#
+#
+# Check that --bootstrap can install and uninstall plugins
+#
# Kill the server
+#
+# Check that installed plugins are *not* automatically loaded in --bootstrap
+#
# restart
flush tables;
show create table t1;
@@ -35,6 +62,18 @@ EXAMPLE ha_example.so
truncate table mysql.plugin;
# Kill the server
#
+# MDEV-9969 mysql_install_db error processing ignore_db_dirs.
+#
+#
+# MDEV-13397 MariaDB upgrade fail when using default_time_zone
+#
+#
+# MDEV-30818 invalid ssl prevents bootstrap
+#
+#
+# End of 10.3 tests
+#
+#
# MDEV-28782 mariadb-tzinfo-to-sql to work in bootstrap mode
#
#
diff --git a/mysql-test/main/bootstrap.test b/mysql-test/main/bootstrap.test
index f7bddf23408..d7cc8e36d56 100644
--- a/mysql-test/main/bootstrap.test
+++ b/mysql-test/main/bootstrap.test
@@ -1,17 +1,20 @@
+--echo #
+--echo # test mysqld in bootstrap mode
+--echo #
--source include/not_embedded.inc
-#
-# test mysqld in bootstrap mode
-#
---disable_warnings
-drop table if exists t1;
---enable_warnings
+--source include/have_example_plugin.inc
+
+--let test_bootstrap=$MYSQLTEST_VARDIR/tmp/test_bootstrap.sql
+--write_file $test_bootstrap
+use test;
+EOF
# Add the datadir to the bootstrap command
let $MYSQLD_DATADIR= `select @@datadir`;
let $MYSQLD_BOOTSTRAP_CMD= $MYSQLD_BOOTSTRAP_CMD --datadir=$MYSQLD_DATADIR --tmpdir=$MYSQL_TMP_DIR --default-storage-engine=MyISAM --loose-skip-innodb --plugin-maturity=unknown;
-#
-# Check that --bootstrap reads from stdin
-#
+--echo #
+--echo # Check that --bootstrap reads from stdin
+--echo #
--write_file $MYSQLTEST_VARDIR/tmp/bootstrap_test.sql
use test;
CREATE TABLE t1(a int);
@@ -21,9 +24,9 @@ EOF
--source include/start_mysqld.inc
drop table t1;
remove_file $MYSQLTEST_VARDIR/tmp/bootstrap_test.sql;
-#
-# Check that --bootstrap of file with SQL error returns error
-#
+--echo #
+--echo # Check that --bootstrap of file with SQL error returns error
+--echo #
--write_file $MYSQLTEST_VARDIR/tmp/bootstrap_error.sql
use test;
CREATE TABLE t1;
@@ -37,9 +40,9 @@ EOF
drop table t1;
remove_file $MYSQLTEST_VARDIR/tmp/bootstrap_error.sql;
-#
-# Bootstrap with a large thd->net.max_packet
-#
+--echo #
+--echo # Bootstrap with a large thd->net.max_packet
+--echo #
--disable_query_log
create table t1 select 2 as a, concat(repeat('MySQL', @@max_allowed_packet/10), ';') as b;
eval select * into outfile '$MYSQLTEST_VARDIR/tmp/long_query.sql' from t1;
@@ -51,7 +54,9 @@ remove_file $MYSQLTEST_VARDIR/tmp/long_query.sql;
--source include/start_mysqld.inc
drop table t1;
---echo End of 5.1 tests
+--echo #
+--echo # End of 5.1 tests
+--echo #
--echo #
--echo # Bug #11766306: 59393: HAVE_INNODB=YES WHEN MYSQLD
@@ -63,29 +68,25 @@ SELECT 'bug' as '' FROM INFORMATION_SCHEMA.ENGINES WHERE engine='innodb'
and SUPPORT='YES';
--source include/kill_mysqld.inc
-#
-# MDEV-13063 Server crashes in intern_plugin_lock or assertion `plugin_ptr->ref_count == 1' fails in plugin_init
-#
+--echo #
+--echo # MDEV-13063 Server crashes in intern_plugin_lock or assertion `plugin_ptr->ref_count == 1' fails in plugin_init
+--echo #
--error 1
--exec $MYSQLD_BOOTSTRAP_CMD --myisam_recover_options=NONE
-#
-# MDEV-19349 mysql_install_db: segfault at tmp_file_prefix check
-#
---write_file $MYSQLTEST_VARDIR/tmp/1
-use test;
-EOF
---exec $MYSQLD_BOOTSTRAP_CMD < $MYSQLTEST_VARDIR/tmp/1 >> $MYSQLTEST_VARDIR/tmp/bootstrap.log 2>&1
---remove_file $MYSQLTEST_VARDIR/tmp/1
+--echo #
+--echo # MDEV-19349 mysql_install_db: segfault at tmp_file_prefix check
+--echo #
+--exec $MYSQLD_BOOTSTRAP_CMD < $test_bootstrap >> $MYSQLTEST_VARDIR/tmp/bootstrap.log 2>&1
--source include/start_mysqld.inc
---echo End of 5.5 tests
+--echo #
+--echo # End of 5.5 tests
+--echo #
---source include/not_windows_embedded.inc
---source include/have_example_plugin.inc
-#
-# Check that --bootstrap can install and uninstall plugins
-#
+--echo #
+--echo # Check that --bootstrap can install and uninstall plugins
+--echo #
let $PLUGIN_DIR=`select @@plugin_dir`;
--source include/kill_mysqld.inc
--write_file $MYSQLTEST_VARDIR/tmp/install_plugin.sql
@@ -95,9 +96,9 @@ EOF
--exec $MYSQLD_BOOTSTRAP_CMD --plugin-dir=$PLUGIN_DIR < $MYSQLTEST_VARDIR/tmp/install_plugin.sql >> $MYSQLTEST_VARDIR/tmp/bootstrap.log 2>&1
--remove_file $MYSQLTEST_VARDIR/tmp/install_plugin.sql
-#
-# Check that installed plugins are *not* automatically loaded in --bootstrap
-#
+--echo #
+--echo # Check that installed plugins are *not* automatically loaded in --bootstrap
+--echo #
--write_file $MYSQLTEST_VARDIR/tmp/bootstrap_plugins.sql
SET SQL_MODE="";
use test;
@@ -113,25 +114,25 @@ drop table t1;
select * from mysql.plugin;
truncate table mysql.plugin;
-
-#
-# MDEV-9969 mysql_install_db error processing ignore_db_dirs.
-#
---write_file $MYSQLTEST_VARDIR/tmp/bootstrap_9969.sql
-use test;
-EOF
--source include/kill_mysqld.inc
---exec $MYSQLD_BOOTSTRAP_CMD --ignore-db-dirs='some_dir' --ignore-db-dirs='some_dir' < $MYSQLTEST_VARDIR/tmp/bootstrap_9969.sql >> $MYSQLTEST_VARDIR/tmp/bootstrap.log 2>&1
---remove_file $MYSQLTEST_VARDIR/tmp/bootstrap_9969.sql
+--echo #
+--echo # MDEV-9969 mysql_install_db error processing ignore_db_dirs.
+--echo #
+--exec $MYSQLD_BOOTSTRAP_CMD --ignore-db-dirs='some_dir' --ignore-db-dirs='some_dir' < $test_bootstrap >> $MYSQLTEST_VARDIR/tmp/bootstrap.log 2>&1
-#
-# MDEV-13397 MariaDB upgrade fail when using default_time_zone
-#
---write_file $MYSQLTEST_VARDIR/tmp/bootstrap_9969.sql
-use test;
-EOF
---exec $MYSQLD_BOOTSTRAP_CMD --default-time-zone=Europe/Moscow < $MYSQLTEST_VARDIR/tmp/bootstrap_9969.sql >> $MYSQLTEST_VARDIR/tmp/bootstrap.log 2>&1
---remove_file $MYSQLTEST_VARDIR/tmp/bootstrap_9969.sql
+--echo #
+--echo # MDEV-13397 MariaDB upgrade fail when using default_time_zone
+--echo #
+--exec $MYSQLD_BOOTSTRAP_CMD --default-time-zone=Europe/Moscow < $test_bootstrap >> $MYSQLTEST_VARDIR/tmp/bootstrap.log 2>&1
+
+--echo #
+--echo # MDEV-30818 invalid ssl prevents bootstrap
+--echo #
+--exec $MYSQLD_BOOTSTRAP_CMD --ssl-ca=/dev/nonexistent < $test_bootstrap >> $MYSQLTEST_VARDIR/tmp/bootstrap.log 2>&1
+
+--echo #
+--echo # End of 10.3 tests
+--echo #
--echo #
--echo # MDEV-28782 mariadb-tzinfo-to-sql to work in bootstrap mode
@@ -160,3 +161,4 @@ EOF
# restore
--source include/start_mysqld.inc
+--remove_file $test_bootstrap
diff --git a/mysql-test/main/ctype_uca_partitions.result b/mysql-test/main/ctype_uca_partitions.result
index 677b68e2387..486e848e191 100644
--- a/mysql-test/main/ctype_uca_partitions.result
+++ b/mysql-test/main/ctype_uca_partitions.result
@@ -84,3 +84,43 @@ O
P
Y
DROP TABLE t1;
+#
+# Start of 10.4 tests
+#
+#
+# MDEV-30072 Wrong ORDER BY for a partitioned prefix key + NOPAD
+#
+SET NAMES utf8mb4;
+CREATE TABLE t1
+(
+id INT,
+data VARCHAR(20),
+KEY data_id (data,id)
+) COLLATE utf8mb3_unicode_nopad_ci ENGINE=MyISAM
+PARTITION BY RANGE COLUMNS (id)
+(
+PARTITION p10 VALUES LESS THAN (20),
+PARTITION p20 VALUES LESS THAN MAXVALUE
+);
+INSERT INTO t1 VALUES (30, 'ss '), (10, 'ß ');
+SELECT id FROM t1 WHERE data='ss ' ORDER BY id;
+id
+10
+30
+SELECT id FROM t1 WHERE data='ss ' ORDER BY id DESC;
+id
+30
+10
+ALTER TABLE t1 DROP KEY data_id, ADD KEY data_id2(data(10),id);
+SELECT id FROM t1 WHERE data='ss ' ORDER BY id;
+id
+10
+30
+SELECT id FROM t1 WHERE data='ss ' ORDER BY id DESC;
+id
+30
+10
+DROP TABLE t1;
+#
+# End of 10.4 tests
+#
diff --git a/mysql-test/main/ctype_uca_partitions.test b/mysql-test/main/ctype_uca_partitions.test
index 5734bb52008..81f1a091574 100644
--- a/mysql-test/main/ctype_uca_partitions.test
+++ b/mysql-test/main/ctype_uca_partitions.test
@@ -36,3 +36,35 @@ SELECT * FROM t1 PARTITION (p0) ORDER BY c1;
SELECT * FROM t1 PARTITION (p1) ORDER BY c1;
SELECT * FROM t1 PARTITION (p2) ORDER BY c1;
DROP TABLE t1;
+
+--echo #
+--echo # Start of 10.4 tests
+--echo #
+
+--echo #
+--echo # MDEV-30072 Wrong ORDER BY for a partitioned prefix key + NOPAD
+--echo #
+
+SET NAMES utf8mb4;
+CREATE TABLE t1
+(
+ id INT,
+ data VARCHAR(20),
+ KEY data_id (data,id)
+) COLLATE utf8mb3_unicode_nopad_ci ENGINE=MyISAM
+PARTITION BY RANGE COLUMNS (id)
+(
+ PARTITION p10 VALUES LESS THAN (20),
+ PARTITION p20 VALUES LESS THAN MAXVALUE
+);
+INSERT INTO t1 VALUES (30, 'ss '), (10, 'ß ');
+SELECT id FROM t1 WHERE data='ss ' ORDER BY id;
+SELECT id FROM t1 WHERE data='ss ' ORDER BY id DESC;
+ALTER TABLE t1 DROP KEY data_id, ADD KEY data_id2(data(10),id);
+SELECT id FROM t1 WHERE data='ss ' ORDER BY id;
+SELECT id FROM t1 WHERE data='ss ' ORDER BY id DESC;
+DROP TABLE t1;
+
+--echo #
+--echo # End of 10.4 tests
+--echo #
diff --git a/mysql-test/main/derived.result b/mysql-test/main/derived.result
index 268b111cd77..aad5e510473 100644
--- a/mysql-test/main/derived.result
+++ b/mysql-test/main/derived.result
@@ -1331,5 +1331,28 @@ a b
DROP VIEW v1;
DROP TABLE t1;
#
+# MDEV-28616: derived table over union with order by clause that
+# contains subquery with unresolvable column reference
+#
+SELECT 1 FROM (
+SELECT 1 UNION SELECT 2 ORDER BY (SELECT 1 FROM DUAL WHERE xxx = 0)
+) dt;
+ERROR 42S22: Unknown column 'xxx' in 'where clause'
+create table t1 (a int, b int);
+insert into t1 values (3,8), (7,2), (1,4), (5,9);
+create table t2 (a int, b int);
+insert into t2 values (9,1), (7,3), (2,6);
+create table t3 (c int, d int);
+insert into t3 values (7,8), (1,2), (3,8);
+select * from
+(
+select a,b from t1 where t1.a > 3
+union
+select a,b from t2 where t2.b < 6
+order by (a - b / (select a + max(c) from t3 where d = x))
+) dt;
+ERROR 42S22: Unknown column 'x' in 'where clause'
+drop table t1,t2,t3;
+#
# End of 10.3 tests
#
diff --git a/mysql-test/main/derived.test b/mysql-test/main/derived.test
index b256637ce26..5e1f102d69e 100644
--- a/mysql-test/main/derived.test
+++ b/mysql-test/main/derived.test
@@ -1145,5 +1145,35 @@ DROP VIEW v1;
DROP TABLE t1;
--echo #
+--echo # MDEV-28616: derived table over union with order by clause that
+--echo # contains subquery with unresolvable column reference
+--echo #
+
+--error ER_BAD_FIELD_ERROR
+SELECT 1 FROM (
+ SELECT 1 UNION SELECT 2 ORDER BY (SELECT 1 FROM DUAL WHERE xxx = 0)
+) dt;
+
+create table t1 (a int, b int);
+insert into t1 values (3,8), (7,2), (1,4), (5,9);
+
+create table t2 (a int, b int);
+insert into t2 values (9,1), (7,3), (2,6);
+
+create table t3 (c int, d int);
+insert into t3 values (7,8), (1,2), (3,8);
+
+--error ER_BAD_FIELD_ERROR
+select * from
+(
+ select a,b from t1 where t1.a > 3
+ union
+ select a,b from t2 where t2.b < 6
+ order by (a - b / (select a + max(c) from t3 where d = x))
+) dt;
+
+drop table t1,t2,t3;
+
+--echo #
--echo # End of 10.3 tests
--echo #
diff --git a/mysql-test/main/group_min_max.result b/mysql-test/main/group_min_max.result
index 2b8b10b29b9..706a4132614 100644
--- a/mysql-test/main/group_min_max.result
+++ b/mysql-test/main/group_min_max.result
@@ -4083,6 +4083,18 @@ MIN(pk)
1
DROP TABLE t1, t2;
#
+# MDEV-30605 Wrong result while using index for group-by
+#
+CREATE TABLE t1 (pk INT primary key, a int, key(a)) engine=innodb;
+INSERT INTO t1 VALUES (1,-1),(2,8),(3,5),(4,-1),(5,10), (6,-1);
+SELECT MIN(pk), a FROM t1 WHERE pk <> 1 GROUP BY a;
+MIN(pk) a
+4 -1
+3 5
+2 8
+5 10
+DROP TABLE t1;
+#
# End of 10.5 tests
#
#
diff --git a/mysql-test/main/group_min_max.test b/mysql-test/main/group_min_max.test
index 0fa91e4d72b..1bc334dd3da 100644
--- a/mysql-test/main/group_min_max.test
+++ b/mysql-test/main/group_min_max.test
@@ -1739,6 +1739,17 @@ SELECT MIN(pk) FROM t1, t2;
DROP TABLE t1, t2;
--echo #
+--echo # MDEV-30605 Wrong result while using index for group-by
+--echo #
+
+CREATE TABLE t1 (pk INT primary key, a int, key(a)) engine=innodb;
+INSERT INTO t1 VALUES (1,-1),(2,8),(3,5),(4,-1),(5,10), (6,-1);
+
+SELECT MIN(pk), a FROM t1 WHERE pk <> 1 GROUP BY a;
+
+DROP TABLE t1;
+
+--echo #
--echo # End of 10.5 tests
--echo #
diff --git a/mysql-test/main/mysql_tzinfo_to_sql_symlink.result b/mysql-test/main/mysql_tzinfo_to_sql_symlink.result
index b6b35e44988..97548768a2d 100644
--- a/mysql-test/main/mysql_tzinfo_to_sql_symlink.result
+++ b/mysql-test/main/mysql_tzinfo_to_sql_symlink.result
@@ -10,13 +10,13 @@ CREATE TABLE time_zone_leap_second LIKE mysql.time_zone_leap_second;
set @wsrep_is_on=(select coalesce(sum(SESSION_VALUE='ON'), 0) from information_schema.SYSTEM_VARIABLES WHERE VARIABLE_NAME='wsrep_on');
SET STATEMENT SQL_MODE='' FOR SELECT concat('%', GROUP_CONCAT(OPTION), '%') INTO @replicate_opt FROM (SELECT DISTINCT concat('REPLICATE_', UPPER(ENGINE)) AS OPTION FROM information_schema.TABLES WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME IN ('time_zone', 'time_zone_name', 'time_zone_transition', 'time_zone_transition_type', 'time_zone_leap_second') AND ENGINE in ('MyISAM', 'Aria')) AS o ORDER BY OPTION DESC;
set @wsrep_cannot_replicate_tz=@wsrep_is_on AND (select coalesce(sum(GLOBAL_VALUE NOT LIKE @replicate_opt), 0) from information_schema.SYSTEM_VARIABLES WHERE VARIABLE_NAME='wsrep_mode');
-execute immediate if(@wsrep_cannot_replicate_tz, "select ENGINE into @time_zone_engine from information_schema.TABLES where TABLE_SCHEMA=DATABASE() and TABLE_NAME='time_zone'", 'do 0');
+execute immediate if(@wsrep_cannot_replicate_tz, 'select ENGINE into @time_zone_engine from information_schema.TABLES where TABLE_SCHEMA=DATABASE() and TABLE_NAME=''time_zone''', 'do 0');
execute immediate if(@wsrep_cannot_replicate_tz, 'ALTER TABLE time_zone ENGINE=InnoDB', 'do 0');
-execute immediate if(@wsrep_cannot_replicate_tz, "select ENGINE into @time_zone_name_engine from information_schema.TABLES where TABLE_SCHEMA=DATABASE() and TABLE_NAME='time_zone_name'", 'do 0');
+execute immediate if(@wsrep_cannot_replicate_tz, 'select ENGINE into @time_zone_name_engine from information_schema.TABLES where TABLE_SCHEMA=DATABASE() and TABLE_NAME=''time_zone_name''', 'do 0');
execute immediate if(@wsrep_cannot_replicate_tz, 'ALTER TABLE time_zone_name ENGINE=InnoDB', 'do 0');
-execute immediate if(@wsrep_cannot_replicate_tz, "select ENGINE into @time_zone_transition_engine from information_schema.TABLES where TABLE_SCHEMA=DATABASE() and TABLE_NAME='time_zone_transition'", 'do 0');
+execute immediate if(@wsrep_cannot_replicate_tz, 'select ENGINE into @time_zone_transition_engine from information_schema.TABLES where TABLE_SCHEMA=DATABASE() and TABLE_NAME=''time_zone_transition''', 'do 0');
execute immediate if(@wsrep_cannot_replicate_tz, 'ALTER TABLE time_zone_transition ENGINE=InnoDB', 'do 0');
-execute immediate if(@wsrep_cannot_replicate_tz, "select ENGINE into @time_zone_transition_type_engine from information_schema.TABLES where TABLE_SCHEMA=DATABASE() and TABLE_NAME='time_zone_transition_type'", 'do 0');
+execute immediate if(@wsrep_cannot_replicate_tz, 'select ENGINE into @time_zone_transition_type_engine from information_schema.TABLES where TABLE_SCHEMA=DATABASE() and TABLE_NAME=''time_zone_transition_type''', 'do 0');
execute immediate if(@wsrep_cannot_replicate_tz, 'ALTER TABLE time_zone_transition_type ENGINE=InnoDB', 'do 0');
TRUNCATE TABLE time_zone;
TRUNCATE TABLE time_zone_name;
@@ -59,13 +59,13 @@ execute immediate if(@wsrep_cannot_replicate_tz, concat('ALTER TABLE time_zone_t
set @wsrep_is_on=(select coalesce(sum(SESSION_VALUE='ON'), 0) from information_schema.SYSTEM_VARIABLES WHERE VARIABLE_NAME='wsrep_on');
SET STATEMENT SQL_MODE='' FOR SELECT concat('%', GROUP_CONCAT(OPTION), '%') INTO @replicate_opt FROM (SELECT DISTINCT concat('REPLICATE_', UPPER(ENGINE)) AS OPTION FROM information_schema.TABLES WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME IN ('time_zone', 'time_zone_name', 'time_zone_transition', 'time_zone_transition_type', 'time_zone_leap_second') AND ENGINE in ('MyISAM', 'Aria')) AS o ORDER BY OPTION DESC;
set @wsrep_cannot_replicate_tz=@wsrep_is_on AND (select coalesce(sum(GLOBAL_VALUE NOT LIKE @replicate_opt), 0) from information_schema.SYSTEM_VARIABLES WHERE VARIABLE_NAME='wsrep_mode');
-execute immediate if(@wsrep_cannot_replicate_tz, "select ENGINE into @time_zone_engine from information_schema.TABLES where TABLE_SCHEMA=DATABASE() and TABLE_NAME='time_zone'", 'do 0');
+execute immediate if(@wsrep_cannot_replicate_tz, 'select ENGINE into @time_zone_engine from information_schema.TABLES where TABLE_SCHEMA=DATABASE() and TABLE_NAME=''time_zone''', 'do 0');
execute immediate if(@wsrep_cannot_replicate_tz, 'ALTER TABLE time_zone ENGINE=InnoDB', 'do 0');
-execute immediate if(@wsrep_cannot_replicate_tz, "select ENGINE into @time_zone_name_engine from information_schema.TABLES where TABLE_SCHEMA=DATABASE() and TABLE_NAME='time_zone_name'", 'do 0');
+execute immediate if(@wsrep_cannot_replicate_tz, 'select ENGINE into @time_zone_name_engine from information_schema.TABLES where TABLE_SCHEMA=DATABASE() and TABLE_NAME=''time_zone_name''', 'do 0');
execute immediate if(@wsrep_cannot_replicate_tz, 'ALTER TABLE time_zone_name ENGINE=InnoDB', 'do 0');
-execute immediate if(@wsrep_cannot_replicate_tz, "select ENGINE into @time_zone_transition_engine from information_schema.TABLES where TABLE_SCHEMA=DATABASE() and TABLE_NAME='time_zone_transition'", 'do 0');
+execute immediate if(@wsrep_cannot_replicate_tz, 'select ENGINE into @time_zone_transition_engine from information_schema.TABLES where TABLE_SCHEMA=DATABASE() and TABLE_NAME=''time_zone_transition''', 'do 0');
execute immediate if(@wsrep_cannot_replicate_tz, 'ALTER TABLE time_zone_transition ENGINE=InnoDB', 'do 0');
-execute immediate if(@wsrep_cannot_replicate_tz, "select ENGINE into @time_zone_transition_type_engine from information_schema.TABLES where TABLE_SCHEMA=DATABASE() and TABLE_NAME='time_zone_transition_type'", 'do 0');
+execute immediate if(@wsrep_cannot_replicate_tz, 'select ENGINE into @time_zone_transition_type_engine from information_schema.TABLES where TABLE_SCHEMA=DATABASE() and TABLE_NAME=''time_zone_transition_type''', 'do 0');
execute immediate if(@wsrep_cannot_replicate_tz, 'ALTER TABLE time_zone_transition_type ENGINE=InnoDB', 'do 0');
TRUNCATE TABLE time_zone;
TRUNCATE TABLE time_zone_name;
@@ -191,13 +191,13 @@ TRUNCATE TABLE time_zone_leap_second;
set @wsrep_is_on=(select coalesce(sum(SESSION_VALUE='ON'), 0) from information_schema.SYSTEM_VARIABLES WHERE VARIABLE_NAME='wsrep_on');
SET STATEMENT SQL_MODE='' FOR SELECT concat('%', GROUP_CONCAT(OPTION), '%') INTO @replicate_opt FROM (SELECT DISTINCT concat('REPLICATE_', UPPER(ENGINE)) AS OPTION FROM information_schema.TABLES WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME IN ('time_zone', 'time_zone_name', 'time_zone_transition', 'time_zone_transition_type', 'time_zone_leap_second') AND ENGINE in ('MyISAM', 'Aria')) AS o ORDER BY OPTION DESC;
set @wsrep_cannot_replicate_tz=@wsrep_is_on AND (select coalesce(sum(GLOBAL_VALUE NOT LIKE @replicate_opt), 0) from information_schema.SYSTEM_VARIABLES WHERE VARIABLE_NAME='wsrep_mode');
-execute immediate if(@wsrep_cannot_replicate_tz, "select ENGINE into @time_zone_engine from information_schema.TABLES where TABLE_SCHEMA=DATABASE() and TABLE_NAME='time_zone'", 'do 0');
+execute immediate if(@wsrep_cannot_replicate_tz, 'select ENGINE into @time_zone_engine from information_schema.TABLES where TABLE_SCHEMA=DATABASE() and TABLE_NAME=''time_zone''', 'do 0');
execute immediate if(@wsrep_cannot_replicate_tz, 'ALTER TABLE time_zone ENGINE=InnoDB', 'do 0');
-execute immediate if(@wsrep_cannot_replicate_tz, "select ENGINE into @time_zone_name_engine from information_schema.TABLES where TABLE_SCHEMA=DATABASE() and TABLE_NAME='time_zone_name'", 'do 0');
+execute immediate if(@wsrep_cannot_replicate_tz, 'select ENGINE into @time_zone_name_engine from information_schema.TABLES where TABLE_SCHEMA=DATABASE() and TABLE_NAME=''time_zone_name''', 'do 0');
execute immediate if(@wsrep_cannot_replicate_tz, 'ALTER TABLE time_zone_name ENGINE=InnoDB', 'do 0');
-execute immediate if(@wsrep_cannot_replicate_tz, "select ENGINE into @time_zone_transition_engine from information_schema.TABLES where TABLE_SCHEMA=DATABASE() and TABLE_NAME='time_zone_transition'", 'do 0');
+execute immediate if(@wsrep_cannot_replicate_tz, 'select ENGINE into @time_zone_transition_engine from information_schema.TABLES where TABLE_SCHEMA=DATABASE() and TABLE_NAME=''time_zone_transition''', 'do 0');
execute immediate if(@wsrep_cannot_replicate_tz, 'ALTER TABLE time_zone_transition ENGINE=InnoDB', 'do 0');
-execute immediate if(@wsrep_cannot_replicate_tz, "select ENGINE into @time_zone_transition_type_engine from information_schema.TABLES where TABLE_SCHEMA=DATABASE() and TABLE_NAME='time_zone_transition_type'", 'do 0');
+execute immediate if(@wsrep_cannot_replicate_tz, 'select ENGINE into @time_zone_transition_type_engine from information_schema.TABLES where TABLE_SCHEMA=DATABASE() and TABLE_NAME=''time_zone_transition_type''', 'do 0');
execute immediate if(@wsrep_cannot_replicate_tz, 'ALTER TABLE time_zone_transition_type ENGINE=InnoDB', 'do 0');
/*M!100602 execute immediate if(@wsrep_cannot_replicate_tz, 'start transaction', 'LOCK TABLES time_zone WRITE,
time_zone_leap_second WRITE,
@@ -313,20 +313,20 @@ TRUNCATE TABLE time_zone_leap_second;
set @wsrep_is_on=(select coalesce(sum(SESSION_VALUE='ON'), 0) from information_schema.SYSTEM_VARIABLES WHERE VARIABLE_NAME='wsrep_on');
SET STATEMENT SQL_MODE='' FOR SELECT concat('%', GROUP_CONCAT(OPTION), '%') INTO @replicate_opt FROM (SELECT DISTINCT concat('REPLICATE_', UPPER(ENGINE)) AS OPTION FROM information_schema.TABLES WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME IN ('time_zone', 'time_zone_name', 'time_zone_transition', 'time_zone_transition_type', 'time_zone_leap_second') AND ENGINE in ('MyISAM', 'Aria')) AS o ORDER BY OPTION DESC;
set @wsrep_cannot_replicate_tz=@wsrep_is_on AND (select coalesce(sum(GLOBAL_VALUE NOT LIKE @replicate_opt), 0) from information_schema.SYSTEM_VARIABLES WHERE VARIABLE_NAME='wsrep_mode');
-execute immediate if(@wsrep_cannot_replicate_tz, "select ENGINE into @time_zone_engine from information_schema.TABLES where TABLE_SCHEMA=DATABASE() and TABLE_NAME='time_zone'", 'do 0');
+execute immediate if(@wsrep_cannot_replicate_tz, 'select ENGINE into @time_zone_engine from information_schema.TABLES where TABLE_SCHEMA=DATABASE() and TABLE_NAME=''time_zone''', 'do 0');
execute immediate if(@wsrep_cannot_replicate_tz, 'ALTER TABLE time_zone ENGINE=InnoDB', 'do 0');
-execute immediate if(@wsrep_cannot_replicate_tz, "select ENGINE into @time_zone_name_engine from information_schema.TABLES where TABLE_SCHEMA=DATABASE() and TABLE_NAME='time_zone_name'", 'do 0');
+execute immediate if(@wsrep_cannot_replicate_tz, 'select ENGINE into @time_zone_name_engine from information_schema.TABLES where TABLE_SCHEMA=DATABASE() and TABLE_NAME=''time_zone_name''', 'do 0');
execute immediate if(@wsrep_cannot_replicate_tz, 'ALTER TABLE time_zone_name ENGINE=InnoDB', 'do 0');
-execute immediate if(@wsrep_cannot_replicate_tz, "select ENGINE into @time_zone_transition_engine from information_schema.TABLES where TABLE_SCHEMA=DATABASE() and TABLE_NAME='time_zone_transition'", 'do 0');
+execute immediate if(@wsrep_cannot_replicate_tz, 'select ENGINE into @time_zone_transition_engine from information_schema.TABLES where TABLE_SCHEMA=DATABASE() and TABLE_NAME=''time_zone_transition''', 'do 0');
execute immediate if(@wsrep_cannot_replicate_tz, 'ALTER TABLE time_zone_transition ENGINE=InnoDB', 'do 0');
-execute immediate if(@wsrep_cannot_replicate_tz, "select ENGINE into @time_zone_transition_type_engine from information_schema.TABLES where TABLE_SCHEMA=DATABASE() and TABLE_NAME='time_zone_transition_type'", 'do 0');
+execute immediate if(@wsrep_cannot_replicate_tz, 'select ENGINE into @time_zone_transition_type_engine from information_schema.TABLES where TABLE_SCHEMA=DATABASE() and TABLE_NAME=''time_zone_transition_type''', 'do 0');
execute immediate if(@wsrep_cannot_replicate_tz, 'ALTER TABLE time_zone_transition_type ENGINE=InnoDB', 'do 0');
/*M!100602 execute immediate if(@wsrep_cannot_replicate_tz, 'start transaction', 'LOCK TABLES time_zone WRITE,
time_zone_leap_second WRITE,
time_zone_name WRITE,
time_zone_transition WRITE,
time_zone_transition_type WRITE')*/;
-execute immediate if(@wsrep_cannot_replicate_tz, "select ENGINE into @time_zone_leap_second_engine from information_schema.TABLES where TABLE_SCHEMA=DATABASE() and TABLE_NAME='time_zone_leap_second'", 'do 0');
+execute immediate if(@wsrep_cannot_replicate_tz, 'select ENGINE into @time_zone_leap_second_engine from information_schema.TABLES where TABLE_SCHEMA=DATABASE() and TABLE_NAME=''time_zone_leap_second''', 'do 0');
execute immediate if(@wsrep_cannot_replicate_tz, 'ALTER TABLE time_zone_leap_second ENGINE=InnoDB', 'do 0');
TRUNCATE TABLE time_zone_leap_second;
execute immediate if(@wsrep_cannot_replicate_tz, concat('ALTER TABLE time_zone_leap_second ENGINE=', @time_zone_leap_second_engine), 'do 0');
@@ -497,13 +497,13 @@ set sql_mode=default;
set @wsrep_is_on=(select coalesce(sum(SESSION_VALUE='ON'), 0) from information_schema.SYSTEM_VARIABLES WHERE VARIABLE_NAME='wsrep_on');
SET STATEMENT SQL_MODE='' FOR SELECT concat('%', GROUP_CONCAT(OPTION), '%') INTO @replicate_opt FROM (SELECT DISTINCT concat('REPLICATE_', UPPER(ENGINE)) AS OPTION FROM information_schema.TABLES WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME IN ('time_zone', 'time_zone_name', 'time_zone_transition', 'time_zone_transition_type', 'time_zone_leap_second') AND ENGINE in ('MyISAM', 'Aria')) AS o ORDER BY OPTION DESC;
set @wsrep_cannot_replicate_tz=@wsrep_is_on AND (select coalesce(sum(GLOBAL_VALUE NOT LIKE @replicate_opt), 0) from information_schema.SYSTEM_VARIABLES WHERE VARIABLE_NAME='wsrep_mode');
-execute immediate if(@wsrep_cannot_replicate_tz, "select ENGINE into @time_zone_engine from information_schema.TABLES where TABLE_SCHEMA=DATABASE() and TABLE_NAME='time_zone'", 'do 0');
+execute immediate if(@wsrep_cannot_replicate_tz, 'select ENGINE into @time_zone_engine from information_schema.TABLES where TABLE_SCHEMA=DATABASE() and TABLE_NAME=''time_zone''', 'do 0');
execute immediate if(@wsrep_cannot_replicate_tz, 'ALTER TABLE time_zone ENGINE=InnoDB', 'do 0');
-execute immediate if(@wsrep_cannot_replicate_tz, "select ENGINE into @time_zone_name_engine from information_schema.TABLES where TABLE_SCHEMA=DATABASE() and TABLE_NAME='time_zone_name'", 'do 0');
+execute immediate if(@wsrep_cannot_replicate_tz, 'select ENGINE into @time_zone_name_engine from information_schema.TABLES where TABLE_SCHEMA=DATABASE() and TABLE_NAME=''time_zone_name''', 'do 0');
execute immediate if(@wsrep_cannot_replicate_tz, 'ALTER TABLE time_zone_name ENGINE=InnoDB', 'do 0');
-execute immediate if(@wsrep_cannot_replicate_tz, "select ENGINE into @time_zone_transition_engine from information_schema.TABLES where TABLE_SCHEMA=DATABASE() and TABLE_NAME='time_zone_transition'", 'do 0');
+execute immediate if(@wsrep_cannot_replicate_tz, 'select ENGINE into @time_zone_transition_engine from information_schema.TABLES where TABLE_SCHEMA=DATABASE() and TABLE_NAME=''time_zone_transition''', 'do 0');
execute immediate if(@wsrep_cannot_replicate_tz, 'ALTER TABLE time_zone_transition ENGINE=InnoDB', 'do 0');
-execute immediate if(@wsrep_cannot_replicate_tz, "select ENGINE into @time_zone_transition_type_engine from information_schema.TABLES where TABLE_SCHEMA=DATABASE() and TABLE_NAME='time_zone_transition_type'", 'do 0');
+execute immediate if(@wsrep_cannot_replicate_tz, 'select ENGINE into @time_zone_transition_type_engine from information_schema.TABLES where TABLE_SCHEMA=DATABASE() and TABLE_NAME=''time_zone_transition_type''', 'do 0');
execute immediate if(@wsrep_cannot_replicate_tz, 'ALTER TABLE time_zone_transition_type ENGINE=InnoDB', 'do 0');
TRUNCATE TABLE time_zone;
TRUNCATE TABLE time_zone_name;
diff --git a/mysql-test/main/opt_trace.result b/mysql-test/main/opt_trace.result
index 83bc03fe0f9..70038a5f605 100644
--- a/mysql-test/main/opt_trace.result
+++ b/mysql-test/main/opt_trace.result
@@ -9948,6 +9948,401 @@ SELECT a FROM t1 WHERE (a,b) in (SELECT @c,@d);
a
DROP TABLE t1;
#
+# MDEV-31085: multi-update using view with optimizer trace enabled
+#
+SET SESSION optimizer_trace = 'enabled=on';
+CREATE TABLE t (a int, b int);
+CREATE VIEW v AS SELECT 1 AS c UNION SELECT 2 AS c;
+INSERT INTO t VALUES (0,4),(5,6);
+UPDATE t, v SET t.b = t.a, t.a = v.c WHERE v.c < t.a;
+SELECT * FROM information_schema.optimizer_trace;
+QUERY TRACE MISSING_BYTES_BEYOND_MAX_MEM_SIZE INSUFFICIENT_PRIVILEGES
+UPDATE t, v SET t.b = t.a, t.a = v.c WHERE v.c < t.a {
+ "steps": [
+ {
+ "view": {
+ "table": "v",
+ "select_id": 2,
+ "algorithm": "materialized"
+ }
+ },
+ {
+ "join_preparation": {
+ "select_id": 2,
+ "steps": [
+ {
+ "expanded_query": "/* select#2 */ select 1 AS c"
+ }
+ ]
+ }
+ },
+ {
+ "join_preparation": {
+ "select_id": 3,
+ "steps": [
+ {
+ "expanded_query": "/* select#3 */ select 2 AS c"
+ }
+ ]
+ }
+ },
+ {
+ "join_preparation": {
+ "select_id": 1,
+ "steps": [
+ {
+ "expanded_query": "/* select#1 */ update t join v set t.b = t.a,t.a = v.c where v.c < t.a"
+ }
+ ]
+ }
+ },
+ {
+ "join_optimization": {
+ "select_id": 1,
+ "steps": [
+ {
+ "condition_processing": {
+ "condition": "WHERE",
+ "original_condition": "v.c < t.a",
+ "steps": [
+ {
+ "transformation": "equality_propagation",
+ "resulting_condition": "v.c < t.a"
+ },
+ {
+ "transformation": "constant_propagation",
+ "resulting_condition": "v.c < t.a"
+ },
+ {
+ "transformation": "trivial_condition_removal",
+ "resulting_condition": "v.c < t.a"
+ }
+ ]
+ }
+ },
+ {
+ "join_optimization": {
+ "select_id": 2,
+ "steps": []
+ }
+ },
+ {
+ "join_optimization": {
+ "select_id": 3,
+ "steps": []
+ }
+ },
+ {
+ "table_dependencies": [
+ {
+ "table": "t",
+ "row_may_be_null": false,
+ "map_bit": 0,
+ "depends_on_map_bits": []
+ },
+ {
+ "table": "<derived2>",
+ "row_may_be_null": false,
+ "map_bit": 1,
+ "depends_on_map_bits": []
+ }
+ ]
+ },
+ {
+ "ref_optimizer_key_uses": []
+ },
+ {
+ "rows_estimation": [
+ {
+ "table": "t",
+ "table_scan": {
+ "rows": 2,
+ "cost": 2.004394531
+ }
+ },
+ {
+ "table": "<derived2>",
+ "table_scan": {
+ "rows": 2,
+ "cost": 2
+ }
+ }
+ ]
+ },
+ {
+ "considered_execution_plans": [
+ {
+ "plan_prefix": [],
+ "get_costs_for_tables": [
+ {
+ "best_access_path": {
+ "table": "t",
+ "considered_access_paths": [
+ {
+ "access_type": "scan",
+ "resulting_rows": 2,
+ "cost": 2.004394531,
+ "chosen": true
+ }
+ ],
+ "chosen_access_method": {
+ "type": "scan",
+ "records": 2,
+ "cost": 2.004394531,
+ "uses_join_buffering": false
+ }
+ }
+ },
+ {
+ "best_access_path": {
+ "table": "<derived2>",
+ "considered_access_paths": [
+ {
+ "access_type": "scan",
+ "resulting_rows": 2,
+ "cost": 2,
+ "chosen": true
+ }
+ ],
+ "chosen_access_method": {
+ "type": "scan",
+ "records": 2,
+ "cost": 2,
+ "uses_join_buffering": false
+ }
+ }
+ }
+ ]
+ },
+ {
+ "plan_prefix": [],
+ "table": "t",
+ "rows_for_plan": 2,
+ "cost_for_plan": 2.404394531,
+ "rest_of_plan": [
+ {
+ "plan_prefix": ["t"],
+ "get_costs_for_tables": [
+ {
+ "best_access_path": {
+ "table": "<derived2>",
+ "considered_access_paths": [
+ {
+ "access_type": "scan",
+ "resulting_rows": 2,
+ "cost": 2,
+ "chosen": true
+ }
+ ],
+ "chosen_access_method": {
+ "type": "scan",
+ "records": 2,
+ "cost": 2,
+ "uses_join_buffering": true
+ }
+ }
+ }
+ ]
+ },
+ {
+ "plan_prefix": ["t"],
+ "table": "<derived2>",
+ "rows_for_plan": 4,
+ "cost_for_plan": 5.204394531
+ }
+ ]
+ },
+ {
+ "plan_prefix": [],
+ "table": "<derived2>",
+ "rows_for_plan": 2,
+ "cost_for_plan": 2.4,
+ "rest_of_plan": [
+ {
+ "plan_prefix": ["<derived2>"],
+ "get_costs_for_tables": [
+ {
+ "best_access_path": {
+ "table": "t",
+ "considered_access_paths": [
+ {
+ "access_type": "scan",
+ "resulting_rows": 2,
+ "cost": 2.004394531,
+ "chosen": true
+ }
+ ],
+ "chosen_access_method": {
+ "type": "scan",
+ "records": 2,
+ "cost": 2.004394531,
+ "uses_join_buffering": true
+ }
+ }
+ }
+ ]
+ },
+ {
+ "plan_prefix": ["<derived2>"],
+ "table": "t",
+ "rows_for_plan": 4,
+ "cost_for_plan": 5.204394531,
+ "pruned_by_cost": true,
+ "current_cost": 5.204394531,
+ "best_cost": 5.204394531
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "best_join_order": ["t", "<derived2>"]
+ },
+ {
+ "substitute_best_equal": {
+ "condition": "WHERE",
+ "resulting_condition": "v.c < t.a"
+ }
+ },
+ {
+ "attaching_conditions_to_tables": {
+ "attached_conditions_computation": [],
+ "attached_conditions_summary": [
+ {
+ "table": "t",
+ "attached": null
+ },
+ {
+ "table": "<derived2>",
+ "attached": "v.c < t.a"
+ }
+ ]
+ }
+ }
+ ]
+ }
+ },
+ {
+ "join_execution": {
+ "select_id": 1,
+ "steps": [
+ {
+ "join_execution": {
+ "select_id": 2,
+ "steps": []
+ }
+ },
+ {
+ "join_execution": {
+ "select_id": 3,
+ "steps": []
+ }
+ },
+ {
+ "join_preparation": {
+ "select_id": "fake",
+ "steps": [
+ {
+ "expanded_query": "select c AS c from dual"
+ }
+ ]
+ }
+ },
+ {
+ "join_optimization": {
+ "select_id": "fake",
+ "steps": [
+ {
+ "table_dependencies": [
+ {
+ "table": "union",
+ "row_may_be_null": false,
+ "map_bit": 0,
+ "depends_on_map_bits": []
+ }
+ ]
+ },
+ {
+ "rows_estimation": [
+ {
+ "table": "union",
+ "table_scan": {
+ "rows": 2,
+ "cost": 10.1
+ }
+ }
+ ]
+ },
+ {
+ "considered_execution_plans": [
+ {
+ "plan_prefix": [],
+ "get_costs_for_tables": [
+ {
+ "best_access_path": {
+ "table": "union",
+ "considered_access_paths": [
+ {
+ "access_type": "scan",
+ "resulting_rows": 2,
+ "cost": 10.1,
+ "chosen": true
+ }
+ ],
+ "chosen_access_method": {
+ "type": "scan",
+ "records": 2,
+ "cost": 10.1,
+ "uses_join_buffering": false
+ }
+ }
+ }
+ ]
+ },
+ {
+ "plan_prefix": [],
+ "table": "union",
+ "rows_for_plan": 2,
+ "cost_for_plan": 10.5
+ }
+ ]
+ },
+ {
+ "best_join_order": ["union"]
+ },
+ {
+ "attaching_conditions_to_tables": {
+ "attached_conditions_computation": [],
+ "attached_conditions_summary": [
+ {
+ "table": "union",
+ "attached": null
+ }
+ ]
+ }
+ }
+ ]
+ }
+ },
+ {
+ "join_execution": {
+ "select_id": "fake",
+ "steps": []
+ }
+ }
+ ]
+ }
+ }
+ ]
+} 0 0
+SELECT * FROM t;
+a b
+0 4
+1 5
+SET optimizer_trace=DEFAULT;
+DROP VIEW v;
+DROP TABLE t;
+#
# End of 10.4 tests
#
set optimizer_trace='enabled=on';
diff --git a/mysql-test/main/opt_trace.test b/mysql-test/main/opt_trace.test
index d07afb2dfce..0cacc4a60ea 100644
--- a/mysql-test/main/opt_trace.test
+++ b/mysql-test/main/opt_trace.test
@@ -683,6 +683,25 @@ SELECT a FROM t1 WHERE (a,b) in (SELECT @c,@d);
DROP TABLE t1;
--echo #
+--echo # MDEV-31085: multi-update using view with optimizer trace enabled
+--echo #
+
+SET SESSION optimizer_trace = 'enabled=on';
+
+CREATE TABLE t (a int, b int);
+CREATE VIEW v AS SELECT 1 AS c UNION SELECT 2 AS c;
+INSERT INTO t VALUES (0,4),(5,6);
+UPDATE t, v SET t.b = t.a, t.a = v.c WHERE v.c < t.a;
+SELECT * FROM information_schema.optimizer_trace;
+
+SELECT * FROM t;
+
+SET optimizer_trace=DEFAULT;
+
+DROP VIEW v;
+DROP TABLE t;
+
+--echo #
--echo # End of 10.4 tests
--echo #
diff --git a/mysql-test/main/parser.result b/mysql-test/main/parser.result
index 50be9dc49c8..89732e20b40 100644
--- a/mysql-test/main/parser.result
+++ b/mysql-test/main/parser.result
@@ -1910,12 +1910,32 @@ SET @@sql_mode=@save_sql_mode;
#
# MDEV-30151 parse error 1=2 not between/in
#
-select 1=2 not in (3,4);
-1=2 not in (3,4)
+SELECT 1=2 NOT IN (3,4);
+1=2 NOT IN (3,4)
1
-select 1=2 not between 3 and 4;
-1=2 not between 3 and 4
+SELECT 1=2 NOT BETWEEN 3 AND 4;
+1=2 NOT BETWEEN 3 AND 4
1
+CREATE TABLE t1 ( f INT AS ( 1 IN ( 2 NOT BETWEEN 3 AND 4 ) ) );
+SHOW CREATE TABLE t1;
+Table Create Table
+t1 CREATE TABLE `t1` (
+ `f` int(11) GENERATED ALWAYS AS (1 = 2 not between 3 and 4) VIRTUAL
+) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci
+DROP TABLE t1;
+CREATE TABLE t1 ( f INT, CHECK ( 1 IN ( 2 NOT BETWEEN 3 AND 4 ) ) );
+SHOW CREATE TABLE t1;
+Table Create Table
+t1 CREATE TABLE `t1` (
+ `f` int(11) DEFAULT NULL,
+ CONSTRAINT `CONSTRAINT_1` CHECK (1 = 2 not between 3 and 4)
+) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci
+DROP TABLE t1;
+CREATE VIEW v1 AS SELECT 1 IN ( 2 NOT BETWEEN 3 AND 4 );
+SHOW CREATE VIEW v1;
+View Create View character_set_client collation_connection
+v1 CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v1` AS select 1 = 2 not between 3 and 4 AS `1 IN ( 2 NOT BETWEEN 3 AND 4 )` latin1 latin1_swedish_ci
+DROP VIEW v1;
#
# End of 10.3 tests
#
diff --git a/mysql-test/main/parser.test b/mysql-test/main/parser.test
index 48cda68b416..dcad30b36b8 100644
--- a/mysql-test/main/parser.test
+++ b/mysql-test/main/parser.test
@@ -1687,8 +1687,21 @@ SET @@sql_mode=@save_sql_mode;
--echo #
--echo # MDEV-30151 parse error 1=2 not between/in
--echo #
-select 1=2 not in (3,4);
-select 1=2 not between 3 and 4;
+
+SELECT 1=2 NOT IN (3,4);
+SELECT 1=2 NOT BETWEEN 3 AND 4;
+
+CREATE TABLE t1 ( f INT AS ( 1 IN ( 2 NOT BETWEEN 3 AND 4 ) ) );
+SHOW CREATE TABLE t1;
+DROP TABLE t1;
+
+CREATE TABLE t1 ( f INT, CHECK ( 1 IN ( 2 NOT BETWEEN 3 AND 4 ) ) );
+SHOW CREATE TABLE t1;
+DROP TABLE t1;
+
+CREATE VIEW v1 AS SELECT 1 IN ( 2 NOT BETWEEN 3 AND 4 );
+SHOW CREATE VIEW v1;
+DROP VIEW v1;
--echo #
--echo # End of 10.3 tests
diff --git a/mysql-test/main/sql_mode_pad_char_to_full_length.result b/mysql-test/main/sql_mode_pad_char_to_full_length.result
new file mode 100644
index 00000000000..6f68aade613
--- /dev/null
+++ b/mysql-test/main/sql_mode_pad_char_to_full_length.result
@@ -0,0 +1,94 @@
+#
+# Start of 10.4 tests
+#
+#
+# MDEV-28190 sql_mode makes MDEV-371 virtual column expressions nondeterministic
+#
+SET default_storage_engine=MyISAM;
+#
+# MDEV-28190 sql_mode makes MDEV-371 virtual column expressions nondeterministic
+#
+CREATE TABLE t1 (a INT,b CHAR(20));
+SHOW CREATE TABLE t1;
+Table Create Table
+t1 CREATE TABLE `t1` (
+ `a` int(11) DEFAULT NULL,
+ `b` char(20) DEFAULT NULL
+) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci
+CREATE UNIQUE INDEX bi USING HASH ON t1 (b);
+INSERT INTO t1 VALUES (0,0);
+SET sql_mode='pad_char_to_full_length';
+DELETE FROM t1;
+DROP TABLE t1;
+SET sql_mode='';
+CREATE TABLE t1 (a INT,b CHAR(20));
+SHOW CREATE TABLE t1;
+Table Create Table
+t1 CREATE TABLE `t1` (
+ `a` int(11) DEFAULT NULL,
+ `b` char(20) DEFAULT NULL
+) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci
+CREATE UNIQUE INDEX bi USING HASH ON t1 (b);
+SET sql_mode='pad_char_to_full_length';
+INSERT INTO t1 VALUES (0,0);
+DELETE FROM t1;
+DROP TABLE t1;
+SET sql_mode='';
+CREATE OR REPLACE TABLE t1 (a CHAR(20),b CHAR(20));
+SHOW CREATE TABLE t1;
+Table Create Table
+t1 CREATE TABLE `t1` (
+ `a` char(20) DEFAULT NULL,
+ `b` char(20) DEFAULT NULL
+) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci
+CREATE UNIQUE INDEX bi USING HASH ON t1 (b);
+INSERT INTO t1 VALUES (0,0);
+SET sql_mode='pad_char_to_full_length';
+DELETE FROM t1;
+DROP TABLE t1;
+SET default_storage_engine=MEMORY;
+#
+# MDEV-28190 sql_mode makes MDEV-371 virtual column expressions nondeterministic
+#
+CREATE TABLE t1 (a INT,b CHAR(20));
+SHOW CREATE TABLE t1;
+Table Create Table
+t1 CREATE TABLE `t1` (
+ `a` int(11) DEFAULT NULL,
+ `b` char(20) DEFAULT NULL
+) ENGINE=MEMORY DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci
+CREATE UNIQUE INDEX bi USING HASH ON t1 (b);
+INSERT INTO t1 VALUES (0,0);
+SET sql_mode='pad_char_to_full_length';
+DELETE FROM t1;
+DROP TABLE t1;
+SET sql_mode='';
+CREATE TABLE t1 (a INT,b CHAR(20));
+SHOW CREATE TABLE t1;
+Table Create Table
+t1 CREATE TABLE `t1` (
+ `a` int(11) DEFAULT NULL,
+ `b` char(20) DEFAULT NULL
+) ENGINE=MEMORY DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci
+CREATE UNIQUE INDEX bi USING HASH ON t1 (b);
+SET sql_mode='pad_char_to_full_length';
+INSERT INTO t1 VALUES (0,0);
+DELETE FROM t1;
+DROP TABLE t1;
+SET sql_mode='';
+CREATE OR REPLACE TABLE t1 (a CHAR(20),b CHAR(20));
+SHOW CREATE TABLE t1;
+Table Create Table
+t1 CREATE TABLE `t1` (
+ `a` char(20) DEFAULT NULL,
+ `b` char(20) DEFAULT NULL
+) ENGINE=MEMORY DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci
+CREATE UNIQUE INDEX bi USING HASH ON t1 (b);
+INSERT INTO t1 VALUES (0,0);
+SET sql_mode='pad_char_to_full_length';
+DELETE FROM t1;
+DROP TABLE t1;
+SET default_storage_engine=DEFAULT;
+#
+# End of 10.4 tests
+#
diff --git a/mysql-test/main/sql_mode_pad_char_to_full_length.test b/mysql-test/main/sql_mode_pad_char_to_full_length.test
new file mode 100644
index 00000000000..4d492bc1b70
--- /dev/null
+++ b/mysql-test/main/sql_mode_pad_char_to_full_length.test
@@ -0,0 +1,19 @@
+--echo #
+--echo # Start of 10.4 tests
+--echo #
+
+--echo #
+--echo # MDEV-28190 sql_mode makes MDEV-371 virtual column expressions nondeterministic
+--echo #
+
+SET default_storage_engine=MyISAM;
+--source include/sql_mode_pad_char_to_full_length.inc
+
+SET default_storage_engine=MEMORY;
+--source include/sql_mode_pad_char_to_full_length.inc
+
+SET default_storage_engine=DEFAULT;
+
+--echo #
+--echo # End of 10.4 tests
+--echo #
diff --git a/mysql-test/main/update.result b/mysql-test/main/update.result
index f5edf1c6be3..b9b80fb48de 100644
--- a/mysql-test/main/update.result
+++ b/mysql-test/main/update.result
@@ -734,3 +734,32 @@ UPDATE t1,t2 SET t1.i1 = -39 WHERE t2.d1 <> t1.i1 AND t2.d1 = t1.d2;
ERROR 22007: Incorrect datetime value: '19' for column `test`.`t1`.`i1` at row 1
DROP TABLE t1,t2;
# End of MariaDB 10.2 tests
+#
+# MDEV-20773: UPDATE with LIKE predicate over non-indexed column
+# of VARCHAR type
+#
+create table t1 (a1 varchar(30), a2 varchar(30) collate utf8_bin);
+insert into t1 values
+('aa','zzz'), ('b','xxaa'), ('ccc','yyy'), ('ddd','xxb');
+analyze table t1 persistent for all;
+Table Op Msg_type Msg_text
+test.t1 analyze status Engine-independent statistics collected
+test.t1 analyze status OK
+explain extended
+update t1 set a1 = 'u'
+ where a2 like 'xx%' and exists(select 1 from t1 where t1.a1 < 'c');
+id select_type table type possible_keys key key_len ref rows filtered Extra
+1 PRIMARY t1 ALL NULL NULL NULL NULL 4 50.00 Using where
+2 SUBQUERY t1 ALL NULL NULL NULL NULL 4 50.00 Using where
+Warnings:
+Note 1003 /* select#1 */ update `test`.`t1` set `test`.`t1`.`a1` = 'u' where `test`.`t1`.`a2` like 'xx%'
+update t1 set a1 = 'u'
+ where a2 like 'xx%' and exists(select 1 from t1 where t1.a1 < 'c');
+select * from t1;
+a1 a2
+aa zzz
+u xxaa
+ccc yyy
+u xxb
+drop table t1;
+# End of MariaDB 10.4 tests
diff --git a/mysql-test/main/update.test b/mysql-test/main/update.test
index 8a6949447ee..147d69d50c9 100644
--- a/mysql-test/main/update.test
+++ b/mysql-test/main/update.test
@@ -676,3 +676,26 @@ UPDATE t1,t2 SET t1.i1 = -39 WHERE t2.d1 <> t1.i1 AND t2.d1 = t1.d2;
DROP TABLE t1,t2;
--echo # End of MariaDB 10.2 tests
+
+--echo #
+--echo # MDEV-20773: UPDATE with LIKE predicate over non-indexed column
+--echo # of VARCHAR type
+--echo #
+
+create table t1 (a1 varchar(30), a2 varchar(30) collate utf8_bin);
+insert into t1 values
+ ('aa','zzz'), ('b','xxaa'), ('ccc','yyy'), ('ddd','xxb');
+analyze table t1 persistent for all;
+
+explain extended
+update t1 set a1 = 'u'
+ where a2 like 'xx%' and exists(select 1 from t1 where t1.a1 < 'c');
+
+update t1 set a1 = 'u'
+ where a2 like 'xx%' and exists(select 1 from t1 where t1.a1 < 'c');
+
+select * from t1;
+
+drop table t1;
+
+--echo # End of MariaDB 10.4 tests
diff --git a/mysql-test/mariadb-test-run.pl b/mysql-test/mariadb-test-run.pl
index b9e338a4b8d..b4801b3835a 100755
--- a/mysql-test/mariadb-test-run.pl
+++ b/mysql-test/mariadb-test-run.pl
@@ -145,7 +145,6 @@ my $opt_start_exit;
my $start_only;
my $file_wsrep_provider;
my $num_saved_cores= 0; # Number of core files saved in vardir/log/ so far.
-my $test_name_for_report;
our @global_suppressions;
@@ -402,6 +401,11 @@ sub main {
mtr_report("Collecting tests...");
my $tests= collect_test_cases($opt_reorder, $opt_suites, \@opt_cases, \@opt_skip_test_list);
+ if (@$tests == 0) {
+ mtr_report("No tests to run...");
+ exit 0;
+ }
+
mark_time_used('collect');
mysql_install_db(default_mysqld(), "$opt_vardir/install.db") unless using_extern();
@@ -516,13 +520,13 @@ sub main {
}
if ( not @$completed ) {
- if ($test_name_for_report)
- {
- my $tinfo = My::Test->new(name => $test_name_for_report);
- $tinfo->{result}= 'MTR_RES_FAILED';
- $tinfo->{comment}=' ';
- mtr_report_test($tinfo);
- }
+ my $test_name= mtr_grab_file($path_testlog);
+ $test_name =~ s/^CURRENT_TEST:\s//;
+ chomp($test_name);
+ my $tinfo = My::Test->new(name => $test_name);
+ $tinfo->{result}= 'MTR_RES_FAILED';
+ $tinfo->{comment}=' ';
+ mtr_report_test($tinfo);
mtr_error("Test suite aborted");
}
@@ -3741,8 +3745,8 @@ sub resfile_report_test ($) {
sub run_testcase ($$) {
my ($tinfo, $server_socket)= @_;
my $print_freq=20;
- $test_name_for_report= $tinfo->{name};
- mtr_verbose("Running test:", $test_name_for_report);
+
+ mtr_verbose("Running test:", $tinfo->{name});
$ENV{'MTR_TEST_NAME'} = $tinfo->{name};
resfile_report_test($tinfo) if $opt_resfile;
@@ -5131,10 +5135,12 @@ sub mysqld_start ($$) {
if (!$rc)
{
# Report failure about the last test case before exit
- my $tinfo = My::Test->new(name => $test_name_for_report);
+ my $test_name= mtr_grab_file($path_current_testlog);
+ $test_name =~ s/^CURRENT_TEST:\s//;
+ my $tinfo = My::Test->new(name => $test_name);
$tinfo->{result}= 'MTR_RES_FAILED';
$tinfo->{failures}= 1;
- $tinfo->{logfile}=get_log_from_proc($mysqld->{'proc'}, $test_name_for_report);
+ $tinfo->{logfile}=get_log_from_proc($mysqld->{'proc'}, $tinfo->{name});
report_option('verbose', 1);
mtr_report_test($tinfo);
}
diff --git a/mysql-test/suite/binlog/t/binlog_truncate_multi_engine.inc b/mysql-test/suite/binlog/t/binlog_truncate_multi_engine.inc
index 52ce4741eaa..f3801070851 100644
--- a/mysql-test/suite/binlog/t/binlog_truncate_multi_engine.inc
+++ b/mysql-test/suite/binlog/t/binlog_truncate_multi_engine.inc
@@ -20,7 +20,9 @@ connect(con1,localhost,root,,);
--source include/show_binary_logs.inc
INSERT INTO t1 VALUES (1, REPEAT("x", 1));
INSERT INTO t2 VALUES (1, REPEAT("x", 1));
-if (`SELECT $case = "B"`)
+--let $is_case_B=`SELECT $case = "B"`
+
+if ($is_case_B)
{
--write_file $MYSQLTEST_VARDIR/tmp/mysqld.1.expect
wait-binlog_truncate_multi_engine.test
@@ -39,12 +41,12 @@ if (`SELECT $debug_sync_action != ""`)
send COMMIT;
--connection default
-if (`SELECT $case = "B"`)
+if ($is_case_B)
{
--source include/wait_until_disconnected.inc
--source include/start_mysqld.inc
}
-if (`SELECT $case != "B"`)
+if (!$is_case_B)
{
SET DEBUG_SYNC= "now WAIT_FOR con1_ready";
--echo List of binary logs after rotation
diff --git a/mysql-test/suite/galera/r/MDEV-30804.result b/mysql-test/suite/galera/r/MDEV-30804.result
new file mode 100644
index 00000000000..2bf323d19f8
--- /dev/null
+++ b/mysql-test/suite/galera/r/MDEV-30804.result
@@ -0,0 +1,11 @@
+connection node_2;
+connection node_1;
+CREATE TABLE t (a INT) ENGINE=Aria;
+CREATE TABLE t1 (a INT) ENGINE=InnoDB;
+START TRANSACTION;
+INSERT INTO t VALUES ('1');
+INSERT INTO t1 VALUES ('1');
+COMMIT;
+ERROR HY000: Transactional commit not supported by involved engine(s)
+DROP TABLE t;
+DROP TABLE t1;
diff --git a/mysql-test/suite/galera/r/MDEV-30955.result b/mysql-test/suite/galera/r/MDEV-30955.result
new file mode 100644
index 00000000000..2a090cb58bc
--- /dev/null
+++ b/mysql-test/suite/galera/r/MDEV-30955.result
@@ -0,0 +1,26 @@
+connection node_2;
+connection node_1;
+CREATE TABLE t (a CHAR(1) KEY);
+START TRANSACTION;
+HANDLER t OPEN;
+disconnect node_1;
+connect node_1, 127.0.0.1, root, , test, $NODE_MYPORT_1;
+DROP TABLE t;
+BACKUP STAGE START;
+START TRANSACTION;
+disconnect node_1;
+connect node_1, 127.0.0.1, root, , test, $NODE_MYPORT_1;
+connection node_1;
+CREATE TABLE t1 (f1 INTEGER PRIMARY KEY);
+CREATE TABLE t2 (f1 INTEGER PRIMARY KEY);
+START TRANSACTION;
+INSERT INTO t1 VALUES(1);
+HANDLER t2 OPEN;
+connection node_2;
+INSERT INTO t1 VALUES(1);
+connect node_1a, 127.0.0.1, root, , test, $NODE_MYPORT_1;
+connection node_1a;
+connection node_1;
+COMMIT;
+ERROR 40001: Deadlock found when trying to get lock; try restarting transaction
+DROP TABLE t1,t2;
diff --git a/mysql-test/suite/galera/r/galera_var_replicate_aria_on.result b/mysql-test/suite/galera/r/galera_var_replicate_aria_on.result
index 39fd748314c..91c07ba6681 100644
--- a/mysql-test/suite/galera/r/galera_var_replicate_aria_on.result
+++ b/mysql-test/suite/galera/r/galera_var_replicate_aria_on.result
@@ -89,11 +89,8 @@ connection node_1;
SET GLOBAL wsrep_sync_wait=15;
CREATE TABLE t1 (f1 INTEGER NOT NULL PRIMARY KEY) ENGINE=Aria;
CREATE TABLE t2 (f1 INTEGER NOT NULL PRIMARY KEY) ENGINE=InnoDB;
-SET AUTOCOMMIT=OFF;
-START TRANSACTION;
INSERT INTO t1 VALUES (1);
INSERT INTO t2 VALUES (1);
-COMMIT;
connection node_2;
SET GLOBAL wsrep_sync_wait=15;
SELECT COUNT(*) AS EXPECT_1 FROM t1;
@@ -103,6 +100,7 @@ SELECT COUNT(*) AS EXPECT_1 FROM t2;
EXPECT_1
1
connection node_1;
+SET AUTOCOMMIT=OFF;
START TRANSACTION;
INSERT INTO t1 VALUES (2);
INSERT INTO t2 VALUES (2);
@@ -129,6 +127,7 @@ INSERT INTO t1 VALUES (1);
ERROR 23000: Duplicate entry '1' for key 'PRIMARY'
connection node_1;
COMMIT;
+ERROR HY000: Transactional commit not supported by involved engine(s)
DROP TABLE t1,t2;
connection node_1;
CREATE TABLE t1 (i INT NOT NULL PRIMARY KEY) ENGINE=INNODB;
diff --git a/mysql-test/suite/galera/r/mdev-26175.result b/mysql-test/suite/galera/r/mdev-26175.result
new file mode 100644
index 00000000000..f84244fe916
--- /dev/null
+++ b/mysql-test/suite/galera/r/mdev-26175.result
@@ -0,0 +1,24 @@
+connection node_2;
+connection node_1;
+connection node_1;
+SET sql_mode="no_zero_date";
+SET GLOBAL wsrep_max_ws_rows=1;
+CREATE TABLE t2 (a INT);
+CREATE TABLE t1 (a INT NOT NULL PRIMARY KEY) ENGINE=InnoDB;
+CREATE TRIGGER tgr BEFORE INSERT ON t1 FOR EACH ROW INSERT INTO t2 VALUES (0);
+INSERT INTO t1 VALUES (0),(1);
+ERROR HY000: wsrep_max_ws_rows exceeded
+SELECT * FROM t1;
+a
+SELECT * FROM t2;
+a
+connection node_2;
+SELECT * FROM t1;
+a
+SELECT * FROM t2;
+a
+connection node_1;
+SET sql_mode=DEFAULT;
+SET GLOBAL wsrep_max_ws_rows=DEFAULT;
+DROP TRIGGER tgr;
+DROP TABLE t1, t2;
diff --git a/mysql-test/suite/galera/t/MDEV-30804.cnf b/mysql-test/suite/galera/t/MDEV-30804.cnf
new file mode 100644
index 00000000000..9dbd81f758d
--- /dev/null
+++ b/mysql-test/suite/galera/t/MDEV-30804.cnf
@@ -0,0 +1,7 @@
+!include ../galera_2nodes.cnf
+
+[mysqld.1]
+log-bin
+
+[mysqld.2]
+log-bin
diff --git a/mysql-test/suite/galera/t/MDEV-30804.test b/mysql-test/suite/galera/t/MDEV-30804.test
new file mode 100644
index 00000000000..561953a0578
--- /dev/null
+++ b/mysql-test/suite/galera/t/MDEV-30804.test
@@ -0,0 +1,21 @@
+#
+# Test that transaction requiring two-phase commit and involving
+# storage engines not supporting it rolls back with a message.
+#
+
+--source include/galera_cluster.inc
+--source include/have_innodb.inc
+--source include/have_aria.inc
+
+CREATE TABLE t (a INT) ENGINE=Aria;
+CREATE TABLE t1 (a INT) ENGINE=InnoDB;
+
+START TRANSACTION;
+INSERT INTO t VALUES ('1');
+INSERT INTO t1 VALUES ('1');
+
+--error ER_ERROR_DURING_COMMIT
+COMMIT;
+
+DROP TABLE t;
+DROP TABLE t1;
diff --git a/mysql-test/suite/galera/t/MDEV-30955.test b/mysql-test/suite/galera/t/MDEV-30955.test
new file mode 100644
index 00000000000..18577120e83
--- /dev/null
+++ b/mysql-test/suite/galera/t/MDEV-30955.test
@@ -0,0 +1,70 @@
+#
+# MDEV-30955
+# Assertion `thd->mdl_context.is_lock_owner(MDL_key::TABLE,
+# table->s->db.str, table->s->table_name.str, MDL_SHARED)'
+# failed in close_thread_table()
+#
+
+--source include/galera_cluster.inc
+
+#
+# Test 1: Assertion thd->mdl_context.is_lock_owner()
+# failed in close_thread_table()
+#
+CREATE TABLE t (a CHAR(1) KEY);
+START TRANSACTION;
+HANDLER t OPEN;
+
+#
+# If bug is present the transaction will be aborted
+# through Wsrep_client_service::bf_rollback() and
+# release explicit locks too early. Later, during
+# THD::cleanup(), table t will be closed and the
+# THD is expected to be owner of the MDL lock that
+# was just released.
+#
+--disconnect node_1
+
+--connect node_1, 127.0.0.1, root, , test, $NODE_MYPORT_1
+DROP TABLE t;
+
+
+#
+# Test 2: Similar issue reproduces also with BACKUP STAGE locks.
+# See comments in MDEV-25037
+#
+
+BACKUP STAGE START;
+START TRANSACTION;
+--disconnect node_1
+--connect node_1, 127.0.0.1, root, , test, $NODE_MYPORT_1
+
+
+#
+# Test 3: Assertion `!thd->mdl_context.has_locks()' failed
+# in do_command()
+#
+
+--connection node_1
+CREATE TABLE t1 (f1 INTEGER PRIMARY KEY);
+CREATE TABLE t2 (f1 INTEGER PRIMARY KEY);
+
+--let $bf_count = `SELECT VARIABLE_VALUE FROM INFORMATION_SCHEMA.global_status WHERE VARIABLE_NAME = 'wsrep_local_bf_aborts'`
+
+START TRANSACTION;
+INSERT INTO t1 VALUES(1);
+HANDLER t2 OPEN;
+
+--connection node_2
+INSERT INTO t1 VALUES(1);
+
+--connect node_1a, 127.0.0.1, root, , test, $NODE_MYPORT_1
+--connection node_1a
+--let $wait_condition = SELECT VARIABLE_VALUE = $bf_count + 1 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_local_bf_aborts'
+--source include/wait_condition.inc
+
+--connection node_1
+--error ER_LOCK_DEADLOCK
+COMMIT;
+
+DROP TABLE t1,t2;
diff --git a/mysql-test/suite/galera/t/galera_sequences.test b/mysql-test/suite/galera/t/galera_sequences.test
index faa3b46d2a7..613823d83e9 100644
--- a/mysql-test/suite/galera/t/galera_sequences.test
+++ b/mysql-test/suite/galera/t/galera_sequences.test
@@ -1,4 +1,5 @@
--source include/galera_cluster.inc
+--source include/have_innodb.inc
#
# MDEV-19353 : Alter Sequence do not replicate to another nodes with in Galera Cluster
diff --git a/mysql-test/suite/galera/t/galera_var_replicate_aria_on.test b/mysql-test/suite/galera/t/galera_var_replicate_aria_on.test
index c3bc53ee17f..ac9a79e6196 100644
--- a/mysql-test/suite/galera/t/galera_var_replicate_aria_on.test
+++ b/mysql-test/suite/galera/t/galera_var_replicate_aria_on.test
@@ -85,18 +85,15 @@ SELECT * FROM t1;
DROP TABLE t1;
#
-# Transaction
+# Preparation for next tests
#
--connection node_1
SET GLOBAL wsrep_sync_wait=15;
CREATE TABLE t1 (f1 INTEGER NOT NULL PRIMARY KEY) ENGINE=Aria;
CREATE TABLE t2 (f1 INTEGER NOT NULL PRIMARY KEY) ENGINE=InnoDB;
-SET AUTOCOMMIT=OFF;
-START TRANSACTION;
INSERT INTO t1 VALUES (1);
INSERT INTO t2 VALUES (1);
-COMMIT;
--connection node_2
SET GLOBAL wsrep_sync_wait=15;
@@ -108,6 +105,7 @@ SELECT COUNT(*) AS EXPECT_1 FROM t2;
#
--connection node_1
+SET AUTOCOMMIT=OFF;
START TRANSACTION;
INSERT INTO t1 VALUES (2);
INSERT INTO t2 VALUES (2);
@@ -138,6 +136,8 @@ INSERT INTO t2 VALUES (1);
INSERT INTO t1 VALUES (1);
--connection node_1
+
+--error ER_ERROR_DURING_COMMIT
COMMIT;
DROP TABLE t1,t2;
diff --git a/mysql-test/suite/galera/t/mdev-26175.test b/mysql-test/suite/galera/t/mdev-26175.test
new file mode 100644
index 00000000000..1a3f1153e03
--- /dev/null
+++ b/mysql-test/suite/galera/t/mdev-26175.test
@@ -0,0 +1,27 @@
+--source include/galera_cluster.inc
+--source include/have_innodb.inc
+
+#
+# MDEV-26175 : Assertion `! thd->in_sub_stmt' failed in bool trans_rollback_stmt(THD*)
+#
+--connection node_1
+SET sql_mode="no_zero_date";
+SET GLOBAL wsrep_max_ws_rows=1;
+CREATE TABLE t2 (a INT);
+CREATE TABLE t1 (a INT NOT NULL PRIMARY KEY) ENGINE=InnoDB;
+CREATE TRIGGER tgr BEFORE INSERT ON t1 FOR EACH ROW INSERT INTO t2 VALUES (0);
+
+--error ER_ERROR_DURING_COMMIT
+INSERT INTO t1 VALUES (0),(1);
+SELECT * FROM t1;
+SELECT * FROM t2;
+
+--connection node_2
+SELECT * FROM t1;
+SELECT * FROM t2;
+
+--connection node_1
+SET sql_mode=DEFAULT;
+SET GLOBAL wsrep_max_ws_rows=DEFAULT;
+DROP TRIGGER tgr;
+DROP TABLE t1, t2;
diff --git a/mysql-test/suite/galera_sr/r/MDEV-30862.result b/mysql-test/suite/galera_sr/r/MDEV-30862.result
new file mode 100644
index 00000000000..43da77f24df
--- /dev/null
+++ b/mysql-test/suite/galera_sr/r/MDEV-30862.result
@@ -0,0 +1,11 @@
+connection node_2;
+connection node_1;
+SET autocommit=0;
+SET SESSION wsrep_trx_fragment_size=1;
+CREATE TABLE t2 SELECT seq FROM seq_1_to_50;
+ERROR 42000: CREATE TABLE AS SELECT is not supported with streaming replication
+CREATE TABLE t1 (f1 INT NOT NULL AUTO_INCREMENT PRIMARY KEY);
+INSERT INTO t1 VALUES(DEFAULT);
+CREATE TABLE t2 SELECT * FROM t1;
+ERROR 42000: CREATE TABLE AS SELECT is not supported with streaming replication
+DROP TABLE t1;
diff --git a/mysql-test/suite/galera_sr/t/MDEV-30862.test b/mysql-test/suite/galera_sr/t/MDEV-30862.test
new file mode 100644
index 00000000000..6be77b4d71b
--- /dev/null
+++ b/mysql-test/suite/galera_sr/t/MDEV-30862.test
@@ -0,0 +1,24 @@
+#
+# MDEV-30862 Assertion `mode_ == m_high_priority' failed in
+# void wsrep::client_state::after_applying()
+#
+
+--source include/galera_cluster.inc
+--source include/have_sequence.inc
+
+SET autocommit=0;
+SET SESSION wsrep_trx_fragment_size=1;
+--error ER_NOT_ALLOWED_COMMAND
+CREATE TABLE t2 SELECT seq FROM seq_1_to_50;
+
+
+#
+# Same test without using seq
+#
+CREATE TABLE t1 (f1 INT NOT NULL AUTO_INCREMENT PRIMARY KEY);
+INSERT INTO t1 VALUES(DEFAULT);
+--error ER_NOT_ALLOWED_COMMAND
+CREATE TABLE t2 SELECT * FROM t1;
+
+
+DROP TABLE t1;
diff --git a/mysql-test/suite/innodb/r/default_row_format_alter.result b/mysql-test/suite/innodb/r/default_row_format_alter.result
index 42cbab8a5f2..33936b59003 100644
--- a/mysql-test/suite/innodb/r/default_row_format_alter.result
+++ b/mysql-test/suite/innodb/r/default_row_format_alter.result
@@ -129,5 +129,25 @@ SELECT ROW_FORMAT FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME='t1';
ROW_FORMAT
Dynamic
DROP TABLE t1;
+#
+# MDEV-31025 Redundant table alter fails when fixed column
+# stored externally
+#
+set @old_sql_mode = @@sql_mode;
+SET @@sql_mode='';
+CREATE TABLE t1(pk INT,c CHAR(255),c2 CHAR(255),c3 CHAR(255),
+c4 char(255), c5 char(255), c6 char(255),
+c7 char(255), c8 char(255), primary key(pk)
+)Engine=InnoDB character set utf32 ROW_FORMAT=REDUNDANT;
+INSERT INTO t1(pk, c) VALUES (1, repeat('a', 255));
+ALTER TABLE t1 FORCE;
+CHECK TABLE t1;
+Table Op Msg_type Msg_text
+test.t1 check status OK
+SELECT LENGTH(c) FROM t1;
+LENGTH(c)
+1020
+DROP TABLE t1;
+set @@sql_mode = @old_sql_mode;
# End of 10.4 tests
SET GLOBAL innodb_default_row_format = @row_format;
diff --git a/mysql-test/suite/innodb/r/innodb_skip_innodb_is_tables.result b/mysql-test/suite/innodb/r/innodb_skip_innodb_is_tables.result
index e919f0dee0b..d948472e965 100644
--- a/mysql-test/suite/innodb/r/innodb_skip_innodb_is_tables.result
+++ b/mysql-test/suite/innodb/r/innodb_skip_innodb_is_tables.result
@@ -159,8 +159,8 @@ trx_commits_insert_update transaction 0 NULL NULL NULL 0 NULL NULL NULL NULL NUL
trx_rollbacks transaction 0 NULL NULL NULL 0 NULL NULL NULL NULL NULL NULL NULL 0 counter Number of transactions rolled back
trx_rollbacks_savepoint transaction 0 NULL NULL NULL 0 NULL NULL NULL NULL NULL NULL NULL 0 counter Number of transactions rolled back to savepoint
trx_rseg_history_len transaction 0 NULL NULL NULL 0 NULL NULL NULL NULL NULL NULL NULL 0 value Length of the TRX_RSEG_HISTORY list
-trx_undo_slots_used transaction 0 NULL NULL NULL 0 NULL NULL NULL NULL NULL NULL NULL 0 counter Number of undo slots used
-trx_undo_slots_cached transaction 0 NULL NULL NULL 0 NULL NULL NULL NULL NULL NULL NULL 0 counter Number of undo slots cached
+trx_undo_slots_used transaction 0 NULL NULL NULL 0 NULL NULL NULL NULL NULL NULL NULL 0 value Number of undo slots used
+trx_undo_slots_cached transaction 0 NULL NULL NULL 0 NULL NULL NULL NULL NULL NULL NULL 0 value Number of undo slots cached
trx_rseg_current_size transaction 0 NULL NULL NULL 0 NULL NULL NULL NULL NULL NULL NULL 0 value Current rollback segment size in pages
purge_del_mark_records purge 0 NULL NULL NULL 0 NULL NULL NULL NULL NULL NULL NULL 0 counter Number of delete-marked rows purged
purge_upd_exist_or_extern_records purge 0 NULL NULL NULL 0 NULL NULL NULL NULL NULL NULL NULL 0 counter Number of purges on updates of existing records and updates on delete marked record with externally stored field
diff --git a/mysql-test/suite/innodb/r/sql_mode_pad_char_to_full_length.result b/mysql-test/suite/innodb/r/sql_mode_pad_char_to_full_length.result
new file mode 100644
index 00000000000..09c1cf57497
--- /dev/null
+++ b/mysql-test/suite/innodb/r/sql_mode_pad_char_to_full_length.result
@@ -0,0 +1,51 @@
+SET default_storage_engine=InnoDB;
+#
+# Start of 10.4 tests
+#
+#
+# MDEV-28190 sql_mode makes MDEV-371 virtual column expressions nondeterministic
+#
+#
+# MDEV-28190 sql_mode makes MDEV-371 virtual column expressions nondeterministic
+#
+CREATE TABLE t1 (a INT,b CHAR(20));
+SHOW CREATE TABLE t1;
+Table Create Table
+t1 CREATE TABLE `t1` (
+ `a` int(11) DEFAULT NULL,
+ `b` char(20) DEFAULT NULL
+) ENGINE=InnoDB DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci
+CREATE UNIQUE INDEX bi USING HASH ON t1 (b);
+INSERT INTO t1 VALUES (0,0);
+SET sql_mode='pad_char_to_full_length';
+DELETE FROM t1;
+DROP TABLE t1;
+SET sql_mode='';
+CREATE TABLE t1 (a INT,b CHAR(20));
+SHOW CREATE TABLE t1;
+Table Create Table
+t1 CREATE TABLE `t1` (
+ `a` int(11) DEFAULT NULL,
+ `b` char(20) DEFAULT NULL
+) ENGINE=InnoDB DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci
+CREATE UNIQUE INDEX bi USING HASH ON t1 (b);
+SET sql_mode='pad_char_to_full_length';
+INSERT INTO t1 VALUES (0,0);
+DELETE FROM t1;
+DROP TABLE t1;
+SET sql_mode='';
+CREATE OR REPLACE TABLE t1 (a CHAR(20),b CHAR(20));
+SHOW CREATE TABLE t1;
+Table Create Table
+t1 CREATE TABLE `t1` (
+ `a` char(20) DEFAULT NULL,
+ `b` char(20) DEFAULT NULL
+) ENGINE=InnoDB DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci
+CREATE UNIQUE INDEX bi USING HASH ON t1 (b);
+INSERT INTO t1 VALUES (0,0);
+SET sql_mode='pad_char_to_full_length';
+DELETE FROM t1;
+DROP TABLE t1;
+#
+# End of 10.4 tests
+#
diff --git a/mysql-test/suite/innodb/t/default_row_format_alter.test b/mysql-test/suite/innodb/t/default_row_format_alter.test
index f5dd246efb5..5f2170454f3 100644
--- a/mysql-test/suite/innodb/t/default_row_format_alter.test
+++ b/mysql-test/suite/innodb/t/default_row_format_alter.test
@@ -150,6 +150,23 @@ ALTER TABLE t1 DROP b;
SELECT ROW_FORMAT FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME='t1';
DROP TABLE t1;
+--echo #
+--echo # MDEV-31025 Redundant table alter fails when fixed column
+--echo # stored externally
+--echo #
+set @old_sql_mode = @@sql_mode;
+SET @@sql_mode='';
+CREATE TABLE t1(pk INT,c CHAR(255),c2 CHAR(255),c3 CHAR(255),
+ c4 char(255), c5 char(255), c6 char(255),
+ c7 char(255), c8 char(255), primary key(pk)
+ )Engine=InnoDB character set utf32 ROW_FORMAT=REDUNDANT;
+INSERT INTO t1(pk, c) VALUES (1, repeat('a', 255));
+ALTER TABLE t1 FORCE;
+CHECK TABLE t1;
+SELECT LENGTH(c) FROM t1;
+DROP TABLE t1;
+set @@sql_mode = @old_sql_mode;
+
--echo # End of 10.4 tests
SET GLOBAL innodb_default_row_format = @row_format;
diff --git a/mysql-test/suite/innodb/t/sql_mode_pad_char_to_full_length.test b/mysql-test/suite/innodb/t/sql_mode_pad_char_to_full_length.test
new file mode 100644
index 00000000000..ba286c744d9
--- /dev/null
+++ b/mysql-test/suite/innodb/t/sql_mode_pad_char_to_full_length.test
@@ -0,0 +1,18 @@
+--source include/have_innodb.inc
+
+SET default_storage_engine=InnoDB;
+
+--echo #
+--echo # Start of 10.4 tests
+--echo #
+
+--echo #
+--echo # MDEV-28190 sql_mode makes MDEV-371 virtual column expressions nondeterministic
+--echo #
+
+--source include/sql_mode_pad_char_to_full_length.inc
+
+
+--echo #
+--echo # End of 10.4 tests
+--echo #
diff --git a/mysql-test/suite/innodb_fts/r/concurrent_insert.result b/mysql-test/suite/innodb_fts/r/concurrent_insert.result
index 2335982816b..bc47511b046 100644
--- a/mysql-test/suite/innodb_fts/r/concurrent_insert.result
+++ b/mysql-test/suite/innodb_fts/r/concurrent_insert.result
@@ -19,7 +19,7 @@ INSERT INTO t2 VALUES('mariadb');
connection default;
SET @saved_dbug = @@GLOBAL.debug_dbug;
SET GLOBAL debug_dbug ='+d,fts_instrument_sync_request,ib_optimize_wq_hang';
-SET DEBUG_SYNC= 'fts_sync_end
+SET DEBUG_SYNC= 'fts_instrument_sync_request
SIGNAL drop_index_start WAIT_FOR sync_op';
INSERT INTO t1 VALUES('Keyword');
connect con1,localhost,root,,,;
diff --git a/mysql-test/suite/innodb_fts/r/sync.result b/mysql-test/suite/innodb_fts/r/sync.result
index 74a5d2f13fb..928efffdb21 100644
--- a/mysql-test/suite/innodb_fts/r/sync.result
+++ b/mysql-test/suite/innodb_fts/r/sync.result
@@ -11,19 +11,19 @@ INSERT INTO t1(title) VALUES('database');
connection con1;
SET @old_dbug = @@SESSION.debug_dbug;
SET debug_dbug = '+d,fts_instrument_sync_debug';
-SET DEBUG_SYNC= 'fts_sync_end SIGNAL written WAIT_FOR selected';
+SET DEBUG_SYNC= 'fts_write_node SIGNAL written WAIT_FOR selected';
INSERT INTO t1(title) VALUES('mysql database');
connection default;
SET DEBUG_SYNC= 'now WAIT_FOR written';
SET GLOBAL innodb_ft_aux_table="test/t1";
SELECT * FROM INFORMATION_SCHEMA.INNODB_FT_INDEX_CACHE;
WORD FIRST_DOC_ID LAST_DOC_ID DOC_COUNT DOC_ID POSITION
-SELECT * FROM INFORMATION_SCHEMA.INNODB_FT_INDEX_TABLE;
-WORD FIRST_DOC_ID LAST_DOC_ID DOC_COUNT DOC_ID POSITION
database 2 3 2 2 0
database 2 3 2 3 6
mysql 1 3 2 1 0
mysql 1 3 2 3 0
+SELECT * FROM INFORMATION_SCHEMA.INNODB_FT_INDEX_TABLE;
+WORD FIRST_DOC_ID LAST_DOC_ID DOC_COUNT DOC_ID POSITION
SET GLOBAL innodb_ft_aux_table=default;
SELECT * FROM t1 WHERE MATCH(title) AGAINST('mysql database');
FTS_DOC_ID title
@@ -59,7 +59,7 @@ INSERT INTO t1(title) VALUES('mysql');
INSERT INTO t1(title) VALUES('database');
connection con1;
SET debug_dbug = '+d,fts_instrument_sync_debug';
-SET DEBUG_SYNC= 'fts_sync_end SIGNAL written WAIT_FOR inserted';
+SET DEBUG_SYNC= 'fts_write_node SIGNAL written WAIT_FOR inserted';
INSERT INTO t1(title) VALUES('mysql database');
connection default;
SET DEBUG_SYNC= 'now WAIT_FOR written';
@@ -70,14 +70,14 @@ SET debug_dbug = @old_dbug;
SET GLOBAL innodb_ft_aux_table="test/t1";
SELECT * FROM INFORMATION_SCHEMA.INNODB_FT_INDEX_CACHE;
WORD FIRST_DOC_ID LAST_DOC_ID DOC_COUNT DOC_ID POSITION
-database 4 4 1 4 6
-mysql 4 4 1 4 0
SELECT * FROM INFORMATION_SCHEMA.INNODB_FT_INDEX_TABLE;
WORD FIRST_DOC_ID LAST_DOC_ID DOC_COUNT DOC_ID POSITION
database 2 3 2 2 0
database 2 3 2 3 6
-mysql 1 3 2 1 0
-mysql 1 3 2 3 0
+database 4 4 1 4 6
+mysql 1 4 3 1 0
+mysql 1 4 3 3 0
+mysql 1 4 3 4 0
SET GLOBAL innodb_ft_aux_table=default;
SELECT * FROM t1 WHERE MATCH(title) AGAINST('mysql database');
FTS_DOC_ID title
diff --git a/mysql-test/suite/innodb_fts/r/sync_block.result b/mysql-test/suite/innodb_fts/r/sync_block.result
new file mode 100644
index 00000000000..65bee127e80
--- /dev/null
+++ b/mysql-test/suite/innodb_fts/r/sync_block.result
@@ -0,0 +1,83 @@
+SET @old_log_output = @@global.log_output;
+SET @old_slow_query_log = @@global.slow_query_log;
+SET @old_general_log = @@global.general_log;
+SET @old_long_query_time = @@global.long_query_time;
+SET @old_debug = @@global.debug_dbug;
+SET GLOBAL log_output = 'TABLE';
+SET GLOBAL general_log = 1;
+SET GLOBAL slow_query_log = 1;
+SET GLOBAL long_query_time = 1;
+connect con1,localhost,root,,;
+connect con2,localhost,root,,;
+connection default;
+# Case 1: Sync blocks DML(insert) on the same table.
+CREATE TABLE t1 (
+FTS_DOC_ID BIGINT UNSIGNED AUTO_INCREMENT NOT NULL PRIMARY KEY,
+title VARCHAR(200),
+FULLTEXT(title)
+) ENGINE = InnoDB;
+connection con1;
+SET GLOBAL debug_dbug='+d,fts_instrument_sync_debug,fts_instrument_sync_sleep';
+SET DEBUG_SYNC= 'fts_sync_begin SIGNAL begin WAIT_FOR continue';
+INSERT INTO t1(title) VALUES('mysql database');
+connection con2;
+SET DEBUG_SYNC= 'now WAIT_FOR begin';
+SELECT * FROM t1 WHERE MATCH(title) AGAINST('mysql database');
+connection default;
+SET DEBUG_SYNC= 'now SIGNAL continue';
+connection con1;
+/* connection con1 */ INSERT INTO t1(title) VALUES('mysql database');
+connection con2;
+/* conneciton con2 */ SELECT * FROM t1 WHERE MATCH(title) AGAINST('mysql database');
+FTS_DOC_ID title
+connection default;
+# make con1 & con2 show up in mysql.slow_log
+SELECT SLEEP(2);
+SLEEP(2)
+0
+# slow log results should only contain INSERT INTO t1.
+SELECT sql_text FROM mysql.slow_log WHERE query_time >= '00:00:02';
+sql_text
+INSERT INTO t1(title) VALUES('mysql database')
+SET GLOBAL debug_dbug = @old_debug;
+TRUNCATE TABLE mysql.slow_log;
+DROP TABLE t1;
+# Case 2: Sync blocks DML(insert) on other tables.
+CREATE TABLE t1 (
+FTS_DOC_ID BIGINT UNSIGNED AUTO_INCREMENT NOT NULL PRIMARY KEY,
+title VARCHAR(200),
+FULLTEXT(title)
+) ENGINE = InnoDB;
+CREATE TABLE t2(id INT);
+connection con1;
+SET GLOBAL debug_dbug='+d,fts_instrument_sync_request,fts_instrument_sync_sleep';
+SET DEBUG_SYNC= 'fts_instrument_sync_request SIGNAL begin WAIT_FOR continue';
+INSERT INTO t1(title) VALUES('mysql database');
+connection con2;
+SET DEBUG_SYNC= 'now WAIT_FOR begin';
+INSERT INTO t2 VALUES(1);
+connection default;
+SET DEBUG_SYNC= 'now SIGNAL continue';
+connection con1;
+/* connection con1 */ INSERT INTO t1(title) VALUES('mysql database');
+connection con2;
+/* conneciton con2 */ INSERT INTO t2 VALUES(1);
+connection default;
+SET DEBUG_SYNC = 'RESET';
+# make con1 & con2 show up in mysql.slow_log
+SELECT SLEEP(2);
+SLEEP(2)
+0
+# slow log results should be empty here.
+SELECT sql_text FROM mysql.slow_log WHERE query_time >= '00:00:02';
+sql_text
+SET GLOBAL debug_dbug = @old_debug;
+TRUNCATE TABLE mysql.slow_log;
+DROP TABLE t1,t2;
+disconnect con1;
+disconnect con2;
+# Restore slow log settings.
+SET GLOBAL log_output = @old_log_output;
+SET GLOBAL general_log = @old_general_log;
+SET GLOBAL slow_query_log = @old_slow_query_log;
+SET GLOBAL long_query_time = @old_long_query_time;
diff --git a/mysql-test/suite/innodb_fts/t/concurrent_insert.test b/mysql-test/suite/innodb_fts/t/concurrent_insert.test
index b6991f6e503..9b4d9517b1a 100644
--- a/mysql-test/suite/innodb_fts/t/concurrent_insert.test
+++ b/mysql-test/suite/innodb_fts/t/concurrent_insert.test
@@ -31,7 +31,7 @@ INSERT INTO t2 VALUES('mariadb');
connection default;
SET @saved_dbug = @@GLOBAL.debug_dbug;
SET GLOBAL debug_dbug ='+d,fts_instrument_sync_request,ib_optimize_wq_hang';
-SET DEBUG_SYNC= 'fts_sync_end
+SET DEBUG_SYNC= 'fts_instrument_sync_request
SIGNAL drop_index_start WAIT_FOR sync_op';
send INSERT INTO t1 VALUES('Keyword');
diff --git a/mysql-test/suite/innodb_fts/t/sync.test b/mysql-test/suite/innodb_fts/t/sync.test
index 7c5c835f2ee..168309a5c92 100644
--- a/mysql-test/suite/innodb_fts/t/sync.test
+++ b/mysql-test/suite/innodb_fts/t/sync.test
@@ -27,7 +27,7 @@ connection con1;
SET @old_dbug = @@SESSION.debug_dbug;
SET debug_dbug = '+d,fts_instrument_sync_debug';
-SET DEBUG_SYNC= 'fts_sync_end SIGNAL written WAIT_FOR selected';
+SET DEBUG_SYNC= 'fts_write_node SIGNAL written WAIT_FOR selected';
send INSERT INTO t1(title) VALUES('mysql database');
@@ -74,7 +74,7 @@ connection con1;
SET debug_dbug = '+d,fts_instrument_sync_debug';
-SET DEBUG_SYNC= 'fts_sync_end SIGNAL written WAIT_FOR inserted';
+SET DEBUG_SYNC= 'fts_write_node SIGNAL written WAIT_FOR inserted';
send INSERT INTO t1(title) VALUES('mysql database');
diff --git a/mysql-test/suite/innodb_fts/t/sync_block.test b/mysql-test/suite/innodb_fts/t/sync_block.test
new file mode 100644
index 00000000000..895d2ba8a59
--- /dev/null
+++ b/mysql-test/suite/innodb_fts/t/sync_block.test
@@ -0,0 +1,124 @@
+#
+# BUG#22516559 MYSQL INSTANCE STALLS WHEN SYNCING FTS INDEX
+#
+
+--source include/have_innodb.inc
+--source include/have_debug.inc
+--source include/have_debug_sync.inc
+--source include/have_log_bin.inc
+--source include/count_sessions.inc
+
+SET @old_log_output = @@global.log_output;
+SET @old_slow_query_log = @@global.slow_query_log;
+SET @old_general_log = @@global.general_log;
+SET @old_long_query_time = @@global.long_query_time;
+SET @old_debug = @@global.debug_dbug;
+
+SET GLOBAL log_output = 'TABLE';
+SET GLOBAL general_log = 1;
+SET GLOBAL slow_query_log = 1;
+SET GLOBAL long_query_time = 1;
+
+connect (con1,localhost,root,,);
+connect (con2,localhost,root,,);
+connection default;
+
+--echo # Case 1: Sync blocks DML(insert) on the same table.
+CREATE TABLE t1 (
+ FTS_DOC_ID BIGINT UNSIGNED AUTO_INCREMENT NOT NULL PRIMARY KEY,
+ title VARCHAR(200),
+ FULLTEXT(title)
+) ENGINE = InnoDB;
+
+connection con1;
+
+SET GLOBAL debug_dbug='+d,fts_instrument_sync_debug,fts_instrument_sync_sleep';
+
+SET DEBUG_SYNC= 'fts_sync_begin SIGNAL begin WAIT_FOR continue';
+
+send INSERT INTO t1(title) VALUES('mysql database');
+
+connection con2;
+
+SET DEBUG_SYNC= 'now WAIT_FOR begin';
+
+send SELECT * FROM t1 WHERE MATCH(title) AGAINST('mysql database');
+
+connection default;
+SET DEBUG_SYNC= 'now SIGNAL continue';
+
+connection con1;
+--echo /* connection con1 */ INSERT INTO t1(title) VALUES('mysql database');
+--reap
+
+connection con2;
+--echo /* conneciton con2 */ SELECT * FROM t1 WHERE MATCH(title) AGAINST('mysql database');
+--reap
+
+connection default;
+-- echo # make con1 & con2 show up in mysql.slow_log
+SELECT SLEEP(2);
+-- echo # slow log results should only contain INSERT INTO t1.
+SELECT sql_text FROM mysql.slow_log WHERE query_time >= '00:00:02';
+
+SET GLOBAL debug_dbug = @old_debug;
+TRUNCATE TABLE mysql.slow_log;
+
+DROP TABLE t1;
+
+--echo # Case 2: Sync blocks DML(insert) on other tables.
+CREATE TABLE t1 (
+ FTS_DOC_ID BIGINT UNSIGNED AUTO_INCREMENT NOT NULL PRIMARY KEY,
+ title VARCHAR(200),
+ FULLTEXT(title)
+) ENGINE = InnoDB;
+
+CREATE TABLE t2(id INT);
+
+connection con1;
+
+SET GLOBAL debug_dbug='+d,fts_instrument_sync_request,fts_instrument_sync_sleep';
+
+SET DEBUG_SYNC= 'fts_instrument_sync_request SIGNAL begin WAIT_FOR continue';
+
+send INSERT INTO t1(title) VALUES('mysql database');
+
+connection con2;
+
+SET DEBUG_SYNC= 'now WAIT_FOR begin';
+
+send INSERT INTO t2 VALUES(1);
+
+connection default;
+SET DEBUG_SYNC= 'now SIGNAL continue';
+
+connection con1;
+--echo /* connection con1 */ INSERT INTO t1(title) VALUES('mysql database');
+--reap
+
+connection con2;
+--echo /* conneciton con2 */ INSERT INTO t2 VALUES(1);
+--reap
+
+connection default;
+SET DEBUG_SYNC = 'RESET';
+-- echo # make con1 & con2 show up in mysql.slow_log
+SELECT SLEEP(2);
+-- echo # slow log results should be empty here.
+SELECT sql_text FROM mysql.slow_log WHERE query_time >= '00:00:02';
+
+SET GLOBAL debug_dbug = @old_debug;
+TRUNCATE TABLE mysql.slow_log;
+
+DROP TABLE t1,t2;
+
+disconnect con1;
+disconnect con2;
+
+--source include/wait_until_count_sessions.inc
+
+-- echo # Restore slow log settings.
+SET GLOBAL log_output = @old_log_output;
+SET GLOBAL general_log = @old_general_log;
+SET GLOBAL slow_query_log = @old_slow_query_log;
+SET GLOBAL long_query_time = @old_long_query_time;
diff --git a/mysql-test/suite/mariabackup/aria_log_dir_path.result b/mysql-test/suite/mariabackup/aria_log_dir_path.result
new file mode 100644
index 00000000000..1a877321bbe
--- /dev/null
+++ b/mysql-test/suite/mariabackup/aria_log_dir_path.result
@@ -0,0 +1,41 @@
+#
+# MDEV-30968 mariadb-backup does not copy Aria logs if aria_log_dir_path is used
+#
+# Restart mariadbd with the test specific parameters
+# restart: --aria-log-file-size=8388608 --aria-log-purge-type=external --loose-aria-log-dir-path=MYSQLTEST_VARDIR/tmp/backup_aria_log_dir_path
+# Create and populate an Aria table (and Aria logs)
+CREATE TABLE t1 (id INT, txt LONGTEXT) ENGINE=Aria;
+BEGIN NOT ATOMIC
+FOR id IN 0..9 DO
+INSERT INTO test.t1 (id, txt) VALUES (id, REPEAT(id,1024*1024));
+END FOR;
+END;
+$$
+# Testing aria log files before --backup
+SET @@global.aria_checkpoint_interval=DEFAULT /*Force checkpoint*/;
+SHOW ENGINE aria logs;
+Type Name Status
+Aria aria_log.00000001 free
+Aria aria_log.00000002 in use
+# mariadb-backup --backup
+# mariadb-backup --prepare
+# shutdown server
+# remove datadir
+# remove aria-log-dir-path
+# mariadb-backup --copy-back
+# with parameters: --defaults-file=MYSQLTEST_VARDIR/my.cnf --copy-back --datadir=MYSQLTEST_VARDIR/mysqld.1/data/ --target-dir=MYSQLTEST_VARDIR/tmp/backup --parallel=2 --throttle=1 --aria-log-dir-path=MYSQLTEST_VARDIR/tmp/backup_aria_log_dir_path
+# starting server
+# restart: --aria-log-file-size=8388608 --aria-log-purge-type=external --loose-aria-log-dir-path=MYSQLTEST_VARDIR/tmp/backup_aria_log_dir_path
+# Check that the table is there after --copy-back
+SELECT COUNT(*) from t1;
+COUNT(*)
+10
+DROP TABLE t1;
+# Testing aria log files after --copy-back
+SET @@global.aria_checkpoint_interval=DEFAULT /*Force checkpoint*/;
+SHOW ENGINE aria logs;
+Type Name Status
+Aria aria_log.00000001 free
+Aria aria_log.00000002 in use
+# Restarting mariadbd with default parameters
+# restart
diff --git a/mysql-test/suite/mariabackup/aria_log_dir_path.test b/mysql-test/suite/mariabackup/aria_log_dir_path.test
new file mode 100644
index 00000000000..0178cd4eae5
--- /dev/null
+++ b/mysql-test/suite/mariabackup/aria_log_dir_path.test
@@ -0,0 +1,105 @@
+--source include/have_maria.inc
+
+--echo #
+--echo # MDEV-30968 mariadb-backup does not copy Aria logs if aria_log_dir_path is used
+--echo #
+
+--let $datadir=`SELECT @@datadir`
+--let $targetdir=$MYSQLTEST_VARDIR/tmp/backup
+
+if ($ARIA_LOGDIR_MARIADB == '')
+{
+ --let $ARIA_LOGDIR_MARIADB=$MYSQLTEST_VARDIR/tmp/backup_aria_log_dir_path
+}
+
+if ($ARIA_LOGDIR_FS == '')
+{
+ --let $ARIA_LOGDIR_FS=$MYSQLTEST_VARDIR/tmp/backup_aria_log_dir_path
+}
+
+--let $server_parameters=--aria-log-file-size=8388608 --aria-log-purge-type=external --loose-aria-log-dir-path=$ARIA_LOGDIR_MARIADB
+
+
+--echo # Restart mariadbd with the test specific parameters
+--mkdir $ARIA_LOGDIR_FS
+--let $restart_parameters=$server_parameters
+--source include/restart_mysqld.inc
+
+
+--echo # Create and populate an Aria table (and Aria logs)
+CREATE TABLE t1 (id INT, txt LONGTEXT) ENGINE=Aria;
+DELIMITER $$;
+BEGIN NOT ATOMIC
+ FOR id IN 0..9 DO
+ INSERT INTO test.t1 (id, txt) VALUES (id, REPEAT(id,1024*1024));
+ END FOR;
+END;
+$$
+DELIMITER ;$$
+
+
+--echo # Testing aria log files before --backup
+SET @@global.aria_checkpoint_interval=DEFAULT /*Force checkpoint*/;
+--file_exists $ARIA_LOGDIR_FS/aria_log_control
+--file_exists $ARIA_LOGDIR_FS/aria_log.00000001
+--file_exists $ARIA_LOGDIR_FS/aria_log.00000002
+--error 1
+--file_exists $ARIA_LOGDIR_FS/aria_log.00000003
+--replace_regex /Size +[0-9]+ ; .+aria_log/aria_log/
+SHOW ENGINE aria logs;
+
+
+--echo # mariadb-backup --backup
+--disable_result_log
+--mkdir $targetdir
+--exec $XTRABACKUP --defaults-file=$MYSQLTEST_VARDIR/my.cnf --backup --target-dir=$targetdir
+--enable_result_log
+
+
+--echo # mariadb-backup --prepare
+--disable_result_log
+--exec $XTRABACKUP --prepare --target-dir=$targetdir
+--enable_result_log
+
+
+--echo # shutdown server
+--disable_result_log
+--source include/shutdown_mysqld.inc
+--echo # remove datadir
+--rmdir $datadir
+--echo # remove aria-log-dir-path
+--rmdir $ARIA_LOGDIR_FS
+
+--echo # mariadb-backup --copy-back
+--let $mariadb_backup_parameters=--defaults-file=$MYSQLTEST_VARDIR/my.cnf --copy-back --datadir=$datadir --target-dir=$targetdir --parallel=2 --throttle=1 --aria-log-dir-path=$ARIA_LOGDIR_MARIADB
+--replace_result $MYSQL_TEST_DIR MYSQL_TEST_DIR $MYSQLTEST_VARDIR MYSQLTEST_VARDIR
+--exec echo "# with parameters: $mariadb_backup_parameters"
+--exec $XTRABACKUP $mariadb_backup_parameters
+
+--echo # starting server
+--let $restart_parameters=$server_parameters
+--source include/start_mysqld.inc
+--enable_result_log
+--rmdir $targetdir
+
+
+--echo # Check that the table is there after --copy-back
+SELECT COUNT(*) from t1;
+DROP TABLE t1;
+
+
+--echo # Testing aria log files after --copy-back
+SET @@global.aria_checkpoint_interval=DEFAULT /*Force checkpoint*/;
+--file_exists $ARIA_LOGDIR_FS/aria_log_control
+--file_exists $ARIA_LOGDIR_FS/aria_log.00000001
+--file_exists $ARIA_LOGDIR_FS/aria_log.00000002
+--error 1
+--file_exists $ARIA_LOGDIR_FS/aria_log.00000003
+--replace_regex /Size +[0-9]+ ; .+aria_log/aria_log/
+SHOW ENGINE aria logs;
+
+
+--echo # Restarting mariadbd with default parameters
+--let $restart_parameters=
+--source include/restart_mysqld.inc
+--rmdir $ARIA_LOGDIR_FS
diff --git a/mysql-test/suite/mariabackup/aria_log_dir_path_rel.result b/mysql-test/suite/mariabackup/aria_log_dir_path_rel.result
new file mode 100644
index 00000000000..7fef26096e0
--- /dev/null
+++ b/mysql-test/suite/mariabackup/aria_log_dir_path_rel.result
@@ -0,0 +1,41 @@
+#
+# MDEV-30968 mariadb-backup does not copy Aria logs if aria_log_dir_path is used
+#
+# Restart mariadbd with the test specific parameters
+# restart: --aria-log-file-size=8388608 --aria-log-purge-type=external --loose-aria-log-dir-path=../../tmp/backup_aria_log_dir_path_rel
+# Create and populate an Aria table (and Aria logs)
+CREATE TABLE t1 (id INT, txt LONGTEXT) ENGINE=Aria;
+BEGIN NOT ATOMIC
+FOR id IN 0..9 DO
+INSERT INTO test.t1 (id, txt) VALUES (id, REPEAT(id,1024*1024));
+END FOR;
+END;
+$$
+# Testing aria log files before --backup
+SET @@global.aria_checkpoint_interval=DEFAULT /*Force checkpoint*/;
+SHOW ENGINE aria logs;
+Type Name Status
+Aria aria_log.00000001 free
+Aria aria_log.00000002 in use
+# mariadb-backup --backup
+# mariadb-backup --prepare
+# shutdown server
+# remove datadir
+# remove aria-log-dir-path
+# mariadb-backup --copy-back
+# with parameters: --defaults-file=MYSQLTEST_VARDIR/my.cnf --copy-back --datadir=MYSQLTEST_VARDIR/mysqld.1/data/ --target-dir=MYSQLTEST_VARDIR/tmp/backup --parallel=2 --throttle=1 --aria-log-dir-path=../../tmp/backup_aria_log_dir_path_rel
+# starting server
+# restart: --aria-log-file-size=8388608 --aria-log-purge-type=external --loose-aria-log-dir-path=../../tmp/backup_aria_log_dir_path_rel
+# Check that the table is there after --copy-back
+SELECT COUNT(*) from t1;
+COUNT(*)
+10
+DROP TABLE t1;
+# Testing aria log files after --copy-back
+SET @@global.aria_checkpoint_interval=DEFAULT /*Force checkpoint*/;
+SHOW ENGINE aria logs;
+Type Name Status
+Aria aria_log.00000001 free
+Aria aria_log.00000002 in use
+# Restarting mariadbd with default parameters
+# restart
diff --git a/mysql-test/suite/mariabackup/aria_log_dir_path_rel.test b/mysql-test/suite/mariabackup/aria_log_dir_path_rel.test
new file mode 100644
index 00000000000..c8169959929
--- /dev/null
+++ b/mysql-test/suite/mariabackup/aria_log_dir_path_rel.test
@@ -0,0 +1,4 @@
+--let $ARIA_LOGDIR_MARIADB=../../tmp/backup_aria_log_dir_path_rel
+--let $ARIA_LOGDIR_FS=$MYSQLTEST_VARDIR/tmp/backup_aria_log_dir_path_rel
+
+--source aria_log_dir_path.test
diff --git a/mysql-test/suite/rpl/r/rpl_xa_prepare_gtid_fail.result b/mysql-test/suite/rpl/r/rpl_xa_prepare_gtid_fail.result
new file mode 100644
index 00000000000..f3fecbda349
--- /dev/null
+++ b/mysql-test/suite/rpl/r/rpl_xa_prepare_gtid_fail.result
@@ -0,0 +1,51 @@
+include/master-slave.inc
+[connection master]
+connection slave;
+include/stop_slave.inc
+change master to master_use_gtid=slave_pos;
+set @@global.slave_parallel_threads= 4;
+set @@global.slave_parallel_mode= optimistic;
+set @@global.gtid_strict_mode=ON;
+set sql_log_bin= 0;
+alter table mysql.gtid_slave_pos engine=innodb;
+call mtr.add_suppression("Deadlock found.*");
+set sql_log_bin= 1;
+include/start_slave.inc
+connection master;
+create table t1 (a int primary key, b int) engine=innodb;
+insert t1 values (1,1);
+include/save_master_gtid.inc
+connection slave;
+include/sync_with_master_gtid.inc
+include/stop_slave.inc
+set @@global.innodb_lock_wait_timeout= 1;
+connection master;
+set @@session.gtid_seq_no=100;
+xa start '1';
+update t1 set b=b+10 where a=1;
+xa end '1';
+xa prepare '1';
+xa commit '1';
+include/save_master_gtid.inc
+connection slave;
+connection slave1;
+BEGIN;
+SELECT * FROM mysql.gtid_slave_pos WHERE seq_no=100 FOR UPDATE;
+domain_id sub_id server_id seq_no
+connection slave;
+include/start_slave.inc
+include/wait_for_slave_sql_error.inc [errno=1942,1213]
+connection slave1;
+ROLLBACK;
+# Cleanup
+connection master;
+drop table t1;
+connection slave;
+include/stop_slave.inc
+set @@global.gtid_slave_pos= "0-1-100";
+set @@global.slave_parallel_threads= 0;
+set @@global.gtid_strict_mode= 0;
+set @@global.innodb_lock_wait_timeout= 50;
+include/start_slave.inc
+include/rpl_end.inc
+# End of rpl_xa_prepare_gtid_fail.test
diff --git a/mysql-test/suite/rpl/t/rpl_xa_prepare_gtid_fail.test b/mysql-test/suite/rpl/t/rpl_xa_prepare_gtid_fail.test
new file mode 100644
index 00000000000..8042b355754
--- /dev/null
+++ b/mysql-test/suite/rpl/t/rpl_xa_prepare_gtid_fail.test
@@ -0,0 +1,106 @@
+#
+# When handling the replication of an XA PREPARE, the commit phase is
+# bifurcated. First, the prepare is handled by the relevant storage engines.
+# Then second,the GTID slave state is updated as a separate autocommit
+# transaction. If the second stage fails, i.e. we are unable to update the
+# GTID slave state, then the slave should immediately quit in error, without
+# retry.
+#
+# This tests validates the above behavior by simulating a deadlock on the
+# GTID slave state table during the second part of XA PREPARE's commit, to
+# ensure that the appropriate error is reported and the transaction was never
+# retried.
+#
+#
+# References
+# MDEV-31038: Parallel Replication Breaks if XA PREPARE Fails Updating Slave
+# GTID State
+#
+source include/master-slave.inc;
+source include/have_binlog_format_row.inc;
+source include/have_innodb.inc;
+
+--connection slave
+--source include/stop_slave.inc
+
+--let $save_par_thds= `SELECT @@global.slave_parallel_threads`
+--let $save_strict_mode= `SELECT @@global.gtid_strict_mode`
+--let $save_innodb_lock_wait_timeout= `SELECT @@global.innodb_lock_wait_timeout`
+
+change master to master_use_gtid=slave_pos;
+set @@global.slave_parallel_threads= 4;
+set @@global.slave_parallel_mode= optimistic;
+set @@global.gtid_strict_mode=ON;
+
+set sql_log_bin= 0;
+alter table mysql.gtid_slave_pos engine=innodb;
+call mtr.add_suppression("Deadlock found.*");
+set sql_log_bin= 1;
+--source include/start_slave.inc
+
+--connection master
+let $datadir= `select @@datadir`;
+create table t1 (a int primary key, b int) engine=innodb;
+insert t1 values (1,1);
+--source include/save_master_gtid.inc
+
+--connection slave
+--source include/sync_with_master_gtid.inc
+--source include/stop_slave.inc
+set @@global.innodb_lock_wait_timeout= 1;
+
+--let $retried_tx_initial= query_get_value(SHOW ALL SLAVES STATUS, Retried_transactions, 1)
+
+--connection master
+--let $gtid_domain_id=`SELECT @@GLOBAL.gtid_domain_id`
+--let $gtid_server_id=`SELECT @@GLOBAL.server_id`
+--let $xap_seq_no=100
+--eval set @@session.gtid_seq_no=$xap_seq_no
+xa start '1';
+update t1 set b=b+10 where a=1;
+xa end '1';
+xa prepare '1';
+--let $new_gtid= `SELECT @@global.gtid_binlog_pos`
+xa commit '1';
+--source include/save_master_gtid.inc
+
+
+--connection slave
+
+#--eval set statement sql_log_bin=0 for insert into mysql.gtid_slave_pos values ($gtid_domain_id, 5, $gtid_server_id, $xap_seq_no)
+
+--connection slave1
+BEGIN;
+--eval SELECT * FROM mysql.gtid_slave_pos WHERE seq_no=$xap_seq_no FOR UPDATE
+
+--connection slave
+--source include/start_slave.inc
+
+--let $slave_sql_errno= 1942,1213
+--source include/wait_for_slave_sql_error.inc
+
+--let $retried_tx_test= query_get_value(SHOW ALL SLAVES STATUS, Retried_transactions, 1)
+if ($retried_tx_initial != $retried_tx_test)
+{
+ --echo Transaction was retried when a failed XA PREPARE slave GTID update should lead to immediate slave stop without retry
+ --die Transaction was retried when a failed XA PREPARE slave GTID update should lead to immediate slave stop without retry
+}
+
+--connection slave1
+ROLLBACK;
+
+--echo # Cleanup
+
+--connection master
+drop table t1;
+
+--connection slave
+--source include/stop_slave.inc
+--eval set @@global.gtid_slave_pos= "$new_gtid"
+--eval set @@global.slave_parallel_threads= $save_par_thds
+--eval set @@global.gtid_strict_mode= $save_strict_mode
+--eval set @@global.innodb_lock_wait_timeout= $save_innodb_lock_wait_timeout
+--source include/start_slave.inc
+
+--source include/rpl_end.inc
+--echo # End of rpl_xa_prepare_gtid_fail.test
diff --git a/mysql-test/suite/sys_vars/r/character_set_client_basic.result b/mysql-test/suite/sys_vars/r/character_set_client_basic.result
index d62d88027c7..14c8bcfd058 100644
--- a/mysql-test/suite/sys_vars/r/character_set_client_basic.result
+++ b/mysql-test/suite/sys_vars/r/character_set_client_basic.result
@@ -500,5 +500,16 @@ res
#
SET GLOBAL character_set_client=2;
ERROR 42000: Unknown character set: '2'
+#
+# MDEV-31018 Replica of 10.3, 10.4, <10.5.19 and <10.6.12 to 10.11 will not work when using non-default charset
+#
+SET @@pseudo_slave_mode=1;
+SET character_set_client=2/*latin2_czech_cs*/;
+SHOW VARIABLES LIKE 'character_set_client';
+Variable_name Value
+character_set_client latin2
+SET @@pseudo_slave_mode=0;
+Warnings:
+Warning 1231 Slave applier execution mode not active, statement ineffective.
SET @@global.character_set_client = @global_start_value;
SET @@session.character_set_client = @session_start_value;
diff --git a/mysql-test/suite/sys_vars/r/character_set_connection_basic.result b/mysql-test/suite/sys_vars/r/character_set_connection_basic.result
index e356d62ed2b..bbb4f4e5f75 100644
--- a/mysql-test/suite/sys_vars/r/character_set_connection_basic.result
+++ b/mysql-test/suite/sys_vars/r/character_set_connection_basic.result
@@ -494,5 +494,21 @@ SELECT @@session.character_set_connection =
WHERE VARIABLE_NAME='character_set_connection') AS res;
res
1
+#
+# MDEV-31018 Replica of 10.3, 10.4, <10.5.19 and <10.6.12 to 10.11 will not work when using non-default charset
+#
+SET character_set_connection=2/*latin2_czech_cs*/;
+ERROR 42000: Unknown character set: '2'
+SET @@pseudo_slave_mode=1;
+SET character_set_connection=2/*latin2_czech_cs*/;
+SHOW VARIABLES LIKE 'character_set_connection';
+Variable_name Value
+character_set_connection latin2
+SHOW VARIABLES LIKE 'collation_connection';
+Variable_name Value
+collation_connection latin2_general_ci
+SET @@pseudo_slave_mode=0;
+Warnings:
+Warning 1231 Slave applier execution mode not active, statement ineffective.
SET @@global.character_set_connection = @global_start_value;
SET @@global.character_set_client = @save_character_set_client;
diff --git a/mysql-test/suite/sys_vars/r/character_set_results_basic.result b/mysql-test/suite/sys_vars/r/character_set_results_basic.result
index d1c6a52ba17..1346c8b42ca 100644
--- a/mysql-test/suite/sys_vars/r/character_set_results_basic.result
+++ b/mysql-test/suite/sys_vars/r/character_set_results_basic.result
Binary files differ
diff --git a/mysql-test/suite/sys_vars/r/character_set_server_basic.result b/mysql-test/suite/sys_vars/r/character_set_server_basic.result
index e0a13c729fe..7445be6aef1 100644
--- a/mysql-test/suite/sys_vars/r/character_set_server_basic.result
+++ b/mysql-test/suite/sys_vars/r/character_set_server_basic.result
@@ -486,5 +486,21 @@ SELECT @@session.character_set_server =
WHERE VARIABLE_NAME='character_set_server') AS res;
res
1
+#
+# MDEV-31018 Replica of 10.3, 10.4, <10.5.19 and <10.6.12 to 10.11 will not work when using non-default charset
+#
+SET character_set_server=2/*latin2_czech_cs*/;
+ERROR 42000: Unknown character set: '2'
+SET @@pseudo_slave_mode=1;
+SET character_set_server=2/*latin2_czech_cs*/;
+SHOW VARIABLES LIKE 'character_set_server';
+Variable_name Value
+character_set_server latin2
+SHOW VARIABLES LIKE 'collation_server';
+Variable_name Value
+collation_server latin2_general_ci
+SET @@pseudo_slave_mode=0;
+Warnings:
+Warning 1231 Slave applier execution mode not active, statement ineffective.
SET @@global.character_set_server = @global_start_value;
SET @@session.character_set_server = @session_start_value;
diff --git a/mysql-test/suite/sys_vars/t/character_set_client_basic.test b/mysql-test/suite/sys_vars/t/character_set_client_basic.test
index 09f758a0316..44c0d240a9d 100644
--- a/mysql-test/suite/sys_vars/t/character_set_client_basic.test
+++ b/mysql-test/suite/sys_vars/t/character_set_client_basic.test
@@ -350,6 +350,15 @@ SELECT @@session.character_set_client =
--error ER_UNKNOWN_CHARACTER_SET
SET GLOBAL character_set_client=2;
+--echo #
+--echo # MDEV-31018 Replica of 10.3, 10.4, <10.5.19 and <10.6.12 to 10.11 will not work when using non-default charset
+--echo #
+
+SET @@pseudo_slave_mode=1;
+SET character_set_client=2/*latin2_czech_cs*/;
+SHOW VARIABLES LIKE 'character_set_client';
+SET @@pseudo_slave_mode=0;
+
####################################
# Restore initial value #
####################################
diff --git a/mysql-test/suite/sys_vars/t/character_set_connection_basic.test b/mysql-test/suite/sys_vars/t/character_set_connection_basic.test
index 3d9094fca04..46bdfcb52e2 100644
--- a/mysql-test/suite/sys_vars/t/character_set_connection_basic.test
+++ b/mysql-test/suite/sys_vars/t/character_set_connection_basic.test
@@ -275,6 +275,19 @@ SELECT @@session.character_set_connection =
(SELECT VARIABLE_VALUE FROM INFORMATION_SCHEMA.SESSION_VARIABLES
WHERE VARIABLE_NAME='character_set_connection') AS res;
+--echo #
+--echo # MDEV-31018 Replica of 10.3, 10.4, <10.5.19 and <10.6.12 to 10.11 will not work when using non-default charset
+--echo #
+
+--error ER_UNKNOWN_CHARACTER_SET
+SET character_set_connection=2/*latin2_czech_cs*/;
+SET @@pseudo_slave_mode=1;
+SET character_set_connection=2/*latin2_czech_cs*/;
+SHOW VARIABLES LIKE 'character_set_connection';
+SHOW VARIABLES LIKE 'collation_connection';
+SET @@pseudo_slave_mode=0;
+
+
####################################
# Restore initial value #
####################################
diff --git a/mysql-test/suite/sys_vars/t/character_set_results_basic.test b/mysql-test/suite/sys_vars/t/character_set_results_basic.test
index 617332b9780..41090be6657 100644
--- a/mysql-test/suite/sys_vars/t/character_set_results_basic.test
+++ b/mysql-test/suite/sys_vars/t/character_set_results_basic.test
@@ -272,6 +272,18 @@ SELECT @@session.character_set_results =
(SELECT VARIABLE_VALUE FROM INFORMATION_SCHEMA.SESSION_VARIABLES
WHERE VARIABLE_NAME='character_set_results') AS res;
+--echo #
+--echo # MDEV-31018 Replica of 10.3, 10.4, <10.5.19 and <10.6.12 to 10.11 will not work when using non-default charset
+--echo #
+
+--error ER_UNKNOWN_CHARACTER_SET
+SET character_set_results=2/*latin2_czech_cs*/;
+SET @@pseudo_slave_mode=1;
+SET character_set_results=2;
+SHOW VARIABLES LIKE 'character_set_results';
+SET @@pseudo_slave_mode=0;
+
+
####################################
# Restore initial value #
####################################
diff --git a/mysql-test/suite/sys_vars/t/character_set_server_basic.test b/mysql-test/suite/sys_vars/t/character_set_server_basic.test
index b2f4788fdae..929dbfaf143 100644
--- a/mysql-test/suite/sys_vars/t/character_set_server_basic.test
+++ b/mysql-test/suite/sys_vars/t/character_set_server_basic.test
@@ -266,6 +266,18 @@ SELECT @@session.character_set_server =
(SELECT VARIABLE_VALUE FROM INFORMATION_SCHEMA.SESSION_VARIABLES
WHERE VARIABLE_NAME='character_set_server') AS res;
+--echo #
+--echo # MDEV-31018 Replica of 10.3, 10.4, <10.5.19 and <10.6.12 to 10.11 will not work when using non-default charset
+--echo #
+
+--error ER_UNKNOWN_CHARACTER_SET
+SET character_set_server=2/*latin2_czech_cs*/;
+SET @@pseudo_slave_mode=1;
+SET character_set_server=2/*latin2_czech_cs*/;
+SHOW VARIABLES LIKE 'character_set_server';
+SHOW VARIABLES LIKE 'collation_server';
+SET @@pseudo_slave_mode=0;
+
####################################
# Restore initial value #
####################################
diff --git a/scripts/wsrep_sst_mariabackup.sh b/scripts/wsrep_sst_mariabackup.sh
index 6ec975f5a59..d6334052f24 100644
--- a/scripts/wsrep_sst_mariabackup.sh
+++ b/scripts/wsrep_sst_mariabackup.sh
@@ -340,6 +340,9 @@ get_transfer()
"Use workaround for socat $SOCAT_VERSION bug"
fi
fi
+ if check_for_version "$SOCAT_VERSION" '1.7.4'; then
+ tcmd="$tcmd,no-sni=1"
+ fi
fi
if [ "${sockopt#*,dhparam=}" = "$sockopt" ]; then
diff --git a/sql/handler.cc b/sql/handler.cc
index 95802a80208..1b5212d22de 100644
--- a/sql/handler.cc
+++ b/sql/handler.cc
@@ -1822,7 +1822,19 @@ int ha_commit_trans(THD *thd, bool all)
ordering is normally done. Commit ordering must be done here.
*/
if (run_wsrep_hooks)
- error= wsrep_before_commit(thd, all);
+ {
+ // This commit involves more than one storage engine and requires
+ // two phases, but some engines don't support it.
+ // Issue a message to the client and roll back the transaction.
+ if (trans->no_2pc && rw_ha_count > 1)
+ {
+ my_message(ER_ERROR_DURING_COMMIT, "Transactional commit not supported "
+ "by involved engine(s)", MYF(0));
+ error= 1;
+ }
+ else
+ error= wsrep_before_commit(thd, all);
+ }
if (error)
{
ha_rollback_trans(thd, FALSE);
@@ -2160,8 +2172,11 @@ int ha_rollback_trans(THD *thd, bool all)
rollback without signalling following transactions. And in release
builds, we explicitly do the signalling before rolling back.
*/
- DBUG_ASSERT(!(thd->rgi_slave && thd->rgi_slave->did_mark_start_commit) ||
- thd->transaction->xid_state.is_explicit_XA());
+ DBUG_ASSERT(
+ !(thd->rgi_slave && thd->rgi_slave->did_mark_start_commit) ||
+ (thd->transaction->xid_state.is_explicit_XA() ||
+ (thd->rgi_slave->gtid_ev_flags2 & Gtid_log_event::FL_PREPARED_XA)));
+
if (thd->rgi_slave && thd->rgi_slave->did_mark_start_commit)
thd->rgi_slave->unmark_start_commit();
}
@@ -7198,7 +7213,13 @@ static int wsrep_after_row(THD *thd)
thd->wsrep_affected_rows > wsrep_max_ws_rows &&
wsrep_thd_is_local(thd))
{
- trans_rollback_stmt(thd) || trans_rollback(thd);
+ /*
+ If we are inside stored function or trigger we should not commit or
+ rollback current statement transaction. See comment in ha_commit_trans()
+ call for more information.
+ */
+ if (!thd->in_sub_stmt)
+ trans_rollback_stmt(thd) || trans_rollback(thd);
my_message(ER_ERROR_DURING_COMMIT, "wsrep_max_ws_rows exceeded", MYF(0));
DBUG_RETURN(ER_ERROR_DURING_COMMIT);
}
diff --git a/sql/log.cc b/sql/log.cc
index 48e5145210c..052ca1d5275 100644
--- a/sql/log.cc
+++ b/sql/log.cc
@@ -418,7 +418,7 @@ private:
Rows_log_event *m_pending;
/*
- Bit flags for what has been writting to cache. Used to
+ Bit flags for what has been writing to cache. Used to
discard logs without any data changes.
see enum_logged_status;
*/
diff --git a/sql/log_event.h b/sql/log_event.h
index 0b1503a5b03..b8e10610bf2 100644
--- a/sql/log_event.h
+++ b/sql/log_event.h
@@ -730,7 +730,7 @@ enum Log_event_type
/*
- Bit flags for what has been writting to cache. Used to
+ Bit flags for what has been writing to cache. Used to
discard logs with table map events but not row events and
nothing else important. This is stored by cache.
*/
@@ -3113,7 +3113,7 @@ private:
virtual int do_commit()= 0;
virtual int do_apply_event(rpl_group_info *rgi);
int do_record_gtid(THD *thd, rpl_group_info *rgi, bool in_trans,
- void **out_hton);
+ void **out_hton, bool force_err= false);
enum_skip_reason do_shall_skip(rpl_group_info *rgi);
virtual const char* get_query()= 0;
#endif
diff --git a/sql/log_event_server.cc b/sql/log_event_server.cc
index a0126d0e338..e37e59dcc39 100644
--- a/sql/log_event_server.cc
+++ b/sql/log_event_server.cc
@@ -153,6 +153,30 @@ is_parallel_retry_error(rpl_group_info *rgi, int err)
return has_temporary_error(rgi->thd);
}
+/**
+ Accumulate a Diagnostics_area's errors and warnings into an output buffer
+
+ @param errbuf The output buffer to write error messages
+ @param errbuf_size The size of the output buffer
+ @param da The Diagnostics_area to check for errors
+*/
+static void inline aggregate_da_errors(char *errbuf, size_t errbuf_size,
+ Diagnostics_area *da)
+{
+ const char *errbuf_end= errbuf + errbuf_size;
+ char *slider;
+ Diagnostics_area::Sql_condition_iterator it= da->sql_conditions();
+ const Sql_condition *err;
+ size_t len;
+ for (err= it++, slider= errbuf; err && slider < errbuf_end - 1;
+ slider += len, err= it++)
+ {
+ len= my_snprintf(slider, errbuf_end - slider,
+ " %s, Error_code: %d;", err->get_message_text(),
+ err->get_sql_errno());
+ }
+}
+
/**
Error reporting facility for Rows_log_event::do_apply_event
@@ -173,13 +197,8 @@ static void inline slave_rows_error_report(enum loglevel level, int ha_error,
const char *log_name, my_off_t pos)
{
const char *handler_error= (ha_error ? HA_ERR(ha_error) : NULL);
- char buff[MAX_SLAVE_ERRMSG], *slider;
- const char *buff_end= buff + sizeof(buff);
- size_t len;
- Diagnostics_area::Sql_condition_iterator it=
- thd->get_stmt_da()->sql_conditions();
+ char buff[MAX_SLAVE_ERRMSG];
Relay_log_info const *rli= rgi->rli;
- const Sql_condition *err;
buff[0]= 0;
int errcode= thd->is_error() ? thd->get_stmt_da()->sql_errno() : 0;
@@ -192,13 +211,7 @@ static void inline slave_rows_error_report(enum loglevel level, int ha_error,
if (is_parallel_retry_error(rgi, errcode))
return;
- for (err= it++, slider= buff; err && slider < buff_end - 1;
- slider += len, err= it++)
- {
- len= my_snprintf(slider, buff_end - slider,
- " %s, Error_code: %d;", err->get_message_text(),
- err->get_sql_errno());
- }
+ aggregate_da_errors(buff, sizeof(buff), thd->get_stmt_da());
if (ha_error != 0 && !thd->killed)
rli->report(level, errcode, rgi->gtid_info(),
@@ -4284,7 +4297,8 @@ bool slave_execute_deferred_events(THD *thd)
#if defined(HAVE_REPLICATION)
int Xid_apply_log_event::do_record_gtid(THD *thd, rpl_group_info *rgi,
- bool in_trans, void **out_hton)
+ bool in_trans, void **out_hton,
+ bool force_err)
{
int err= 0;
Relay_log_info const *rli= rgi->rli;
@@ -4299,14 +4313,26 @@ int Xid_apply_log_event::do_record_gtid(THD *thd, rpl_group_info *rgi,
int ec= thd->get_stmt_da()->sql_errno();
/*
Do not report an error if this is really a kill due to a deadlock.
- In this case, the transaction will be re-tried instead.
+ In this case, the transaction will be re-tried instead. Unless force_err
+ is set, as in the case of XA PREPARE, as the GTID state is updated as a
+ separate transaction, and if that fails, we should not retry but exit in
+ error immediately.
*/
- if (!is_parallel_retry_error(rgi, ec))
+ if (!is_parallel_retry_error(rgi, ec) || force_err)
+ {
+ char buff[MAX_SLAVE_ERRMSG];
+ buff[0]= 0;
+ aggregate_da_errors(buff, sizeof(buff), thd->get_stmt_da());
+
+ if (force_err)
+ thd->clear_error();
+
rli->report(ERROR_LEVEL, ER_CANNOT_UPDATE_GTID_STATE, rgi->gtid_info(),
"Error during XID COMMIT: failed to update GTID state in "
- "%s.%s: %d: %s",
+ "%s.%s: %d: %s the event's master log %s, end_log_pos %llu",
"mysql", rpl_gtid_slave_state_table_name.str, ec,
- thd->get_stmt_da()->message());
+ buff, RPL_LOG_NAME, log_pos);
+ }
thd->is_slave_error= 1;
}
@@ -4380,7 +4406,7 @@ int Xid_apply_log_event::do_apply_event(rpl_group_info *rgi)
{
DBUG_ASSERT(!thd->transaction->xid_state.is_explicit_XA());
- if ((err= do_record_gtid(thd, rgi, false, &hton)))
+ if ((err= do_record_gtid(thd, rgi, false, &hton, true)))
return err;
}
diff --git a/sql/mysqld.cc b/sql/mysqld.cc
index 23f34dd0d84..302c3db88f1 100644
--- a/sql/mysqld.cc
+++ b/sql/mysqld.cc
@@ -4699,7 +4699,10 @@ static void init_ssl()
{
sql_print_error("Failed to setup SSL");
sql_print_error("SSL error: %s", sslGetErrString(error));
- unireg_abort(1);
+ if (!opt_bootstrap)
+ unireg_abort(1);
+ opt_use_ssl = 0;
+ have_ssl= SHOW_OPTION_DISABLED;
}
else
ssl_acceptor_stats.init();
diff --git a/sql/opt_range.cc b/sql/opt_range.cc
index 86bd5663623..a32d4cfe24e 100644
--- a/sql/opt_range.cc
+++ b/sql/opt_range.cc
@@ -461,7 +461,7 @@ void print_range_for_non_indexed_field(String *out, Field *field,
static void print_min_range_operator(String *out, const ha_rkey_function flag);
static void print_max_range_operator(String *out, const ha_rkey_function flag);
-static bool is_field_an_unique_index(RANGE_OPT_PARAM *param, Field *field);
+static bool is_field_an_unique_index(Field *field);
/*
SEL_IMERGE is a list of possible ways to do index merge, i.e. it is
@@ -3561,7 +3561,10 @@ bool calculate_cond_selectivity_for_table(THD *thd, TABLE *table, Item **cond)
}
else
{
+ enum_check_fields save_count_cuted_fields= thd->count_cuted_fields;
+ thd->count_cuted_fields= CHECK_FIELD_IGNORE;
rows= records_in_column_ranges(&param, idx, key);
+ thd->count_cuted_fields= save_count_cuted_fields;
if (rows != DBL_MAX)
{
key->field->cond_selectivity= rows/table_records;
@@ -7759,8 +7762,13 @@ SEL_TREE *Item_func_ne::get_func_mm_tree(RANGE_OPT_PARAM *param,
If this condition is a "col1<>...", where there is a UNIQUE KEY(col1),
do not construct a SEL_TREE from it. A condition that excludes just one
row in the table is not selective (unless there are only a few rows)
+
+ Note: this logic must be in sync with code in
+ check_group_min_max_predicates(). That function walks an Item* condition
+ and checks if the range optimizer would produce an equivalent range for
+ it.
*/
- if (is_field_an_unique_index(param, field))
+ if (param->using_real_indexes && is_field_an_unique_index(field))
DBUG_RETURN(NULL);
DBUG_RETURN(get_ne_mm_tree(param, field, value, value));
}
@@ -7872,7 +7880,7 @@ SEL_TREE *Item_func_in::get_func_mm_tree(RANGE_OPT_PARAM *param,
- if there are a lot of constants, the overhead of building and
processing enormous range list is not worth it.
*/
- if (is_field_an_unique_index(param, field))
+ if (param->using_real_indexes && is_field_an_unique_index(field))
DBUG_RETURN(0);
/* Get a SEL_TREE for "(-inf|NULL) < X < c_0" interval. */
@@ -8581,24 +8589,18 @@ SEL_TREE *Item_equal::get_mm_tree(RANGE_OPT_PARAM *param, Item **cond_ptr)
In the future we could also add "almost unique" indexes where any value is
present only in a few rows (but necessarily exactly one row)
*/
-static bool is_field_an_unique_index(RANGE_OPT_PARAM *param, Field *field)
+static bool is_field_an_unique_index(Field *field)
{
DBUG_ENTER("is_field_an_unique_index");
-
- // The check for using_real_indexes is there because of the heuristics
- // this function is used for.
- if (param->using_real_indexes)
+ key_map::Iterator it(field->key_start);
+ uint key_no;
+ while ((key_no= it++) != key_map::Iterator::BITMAP_END)
{
- key_map::Iterator it(field->key_start);
- uint key_no;
- while ((key_no= it++) != key_map::Iterator::BITMAP_END)
+ KEY *key_info= &field->table->key_info[key_no];
+ if (key_info->user_defined_key_parts == 1 &&
+ (key_info->flags & HA_NOSAME))
{
- KEY *key_info= &field->table->key_info[key_no];
- if (key_info->user_defined_key_parts == 1 &&
- (key_info->flags & HA_NOSAME))
- {
- DBUG_RETURN(true);
- }
+ DBUG_RETURN(true);
}
}
DBUG_RETURN(false);
@@ -13537,7 +13539,7 @@ cost_group_min_max(TABLE* table, KEY *index_info, uint used_key_parts,
- (C between const_i and const_j)
- C IS NULL
- C IS NOT NULL
- - C != const
+ - C != const (unless C is the primary key)
SA4. If Q has a GROUP BY clause, there are no other aggregate functions
except MIN and MAX. For queries with DISTINCT, aggregate functions
are allowed.
@@ -14431,6 +14433,17 @@ check_group_min_max_predicates(Item *cond, Item_field *min_max_arg_item,
if (!simple_pred(pred, args, &inv))
DBUG_RETURN(FALSE);
+ /*
+ Follow the logic in Item_func_ne::get_func_mm_tree(): condition
+ in form "tbl.primary_key <> const" is not used to produce intervals.
+
+ If the condition doesn't have an equivalent interval, this means we
+ fail LooseScan's condition SA3. Return FALSE to indicate this.
+ */
+ if (pred_type == Item_func::NE_FUNC &&
+ is_field_an_unique_index(min_max_arg_item->field))
+ DBUG_RETURN(FALSE);
+
if (args[0] && args[1]) // this is a binary function or BETWEEN
{
DBUG_ASSERT(pred->fixed_type_handler());
diff --git a/sql/sql_derived.cc b/sql/sql_derived.cc
index 22503106350..e5362ffe0a9 100644
--- a/sql/sql_derived.cc
+++ b/sql/sql_derived.cc
@@ -803,6 +803,9 @@ bool mysql_derived_prepare(THD *thd, LEX *lex, TABLE_LIST *derived)
cursor->outer_join|= JOIN_TYPE_OUTER;
}
}
+ // Prevent it for possible ORDER BY clause
+ if (unit->fake_select_lex)
+ unit->fake_select_lex->context.outer_context= 0;
if (unlikely(thd->trace_started()))
{
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index 55c743edeea..3b6f66b5ae7 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -1336,7 +1336,7 @@ dispatch_command_return do_command(THD *thd, bool blocking)
in wsrep_before_command().
*/
WSREP_LOG_THD(thd, "enter found BF aborted");
- DBUG_ASSERT(!thd->mdl_context.has_locks());
+ DBUG_ASSERT(!thd->mdl_context.has_transactional_locks());
DBUG_ASSERT(!thd->get_stmt_da()->is_set());
/* We let COM_QUIT and COM_STMT_CLOSE to execute even if wsrep aborted. */
if (command == COM_STMT_EXECUTE)
diff --git a/sql/sql_select.cc b/sql/sql_select.cc
index 6bf350f9f10..80625d7bbcc 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -29219,7 +29219,7 @@ void st_select_lex::print_item_list(THD *thd, String *str,
outer_select() can not be used here because it is for name resolution
and will return NULL at any end of name resolution chain (view/derived)
*/
- bool top_level= (get_master()->get_master() == 0);
+ bool top_level= (get_master() == &thd->lex->unit);
List_iterator_fast<Item> it(item_list);
Item *item;
while ((item= it++))
@@ -29326,7 +29326,7 @@ void st_select_lex::print(THD *thd, String *str, enum_query_type query_type)
return;
}
- bool top_level= (get_master()->get_master() == 0);
+ bool top_level= (get_master() == &thd->lex->unit);
enum explainable_cmd_type sel_type= SELECT_CMD;
if (top_level)
sel_type= get_explainable_cmd_type(thd);
diff --git a/sql/sql_table.cc b/sql/sql_table.cc
index bc9856b4310..a510de15027 100644
--- a/sql/sql_table.cc
+++ b/sql/sql_table.cc
@@ -12298,6 +12298,19 @@ bool Sql_cmd_create_table_like::execute(THD *thd)
}
#endif
+#ifdef WITH_WSREP
+ if (select_lex->item_list.elements && // With SELECT
+ WSREP(thd) && thd->variables.wsrep_trx_fragment_size > 0)
+ {
+ my_message(
+ ER_NOT_ALLOWED_COMMAND,
+ "CREATE TABLE AS SELECT is not supported with streaming replication",
+ MYF(0));
+ res= 1;
+ goto end_with_restore_list;
+ }
+#endif /* WITH_WSREP */
+
if (select_lex->item_list.elements || select_lex->tvc) // With select or TVC
{
select_result *result;
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index 0fed9186dd2..c4c685d0bf2 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -345,9 +345,9 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
*/
%ifdef MARIADB
-%expect 82
+%expect 64
%else
-%expect 83
+%expect 65
%endif
/*
@@ -1178,7 +1178,8 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
%left PREC_BELOW_NOT
-%nonassoc LOW_PRIORITY_NOT
+/* The precendence of boolean NOT is in fact here. See the comment below. */
+
%left '=' EQUAL_SYM GE '>' LE '<' NE
%nonassoc IS
%right BETWEEN_SYM
@@ -1190,6 +1191,24 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
%left '*' '/' '%' DIV_SYM MOD_SYM
%left '^'
%left MYSQL_CONCAT_SYM
+/*
+ Boolean negation has a special branch in "expr" starting with NOT_SYM.
+ The precedence of logical negation is determined by the grammar itself
+ (without using Bison terminal symbol precedence) in this order
+ - Boolean factor (i.e. logical AND)
+ - Boolean NOT
+ - Boolean test (such as '=', IS NULL, IS TRUE)
+
+ But we also need a precedence for NOT_SYM in other contexts,
+ to shift (without reduce) in these cases:
+ predicate <here> NOT IN ...
+ predicate <here> NOT BETWEEN ...
+ predicate <here> NOT LIKE ...
+ predicate <here> NOT REGEXP ...
+ If the precedence of NOT_SYM was low, it would reduce immediately
+ after scanning "predicate" and then produce a syntax error on "NOT".
+*/
+%nonassoc NOT_SYM
%nonassoc NEG '~' NOT2_SYM BINARY
%nonassoc COLLATE_SYM
%nonassoc SUBQUERY_AS_EXPR
@@ -1478,6 +1497,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
literal insert_ident order_ident temporal_literal
simple_ident expr sum_expr in_sum_expr
variable variable_aux
+ boolean_test
predicate bit_expr parenthesized_expr
table_wild simple_expr column_default_non_parenthesized_expr udf_expr
primary_expr string_factor_expr mysql_concatenation_expr
@@ -9137,79 +9157,83 @@ expr:
MYSQL_YYABORT;
}
}
- | NOT_SYM expr %prec LOW_PRIORITY_NOT
+ | NOT_SYM expr
{
$$= negate_expression(thd, $2);
if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
- | expr IS TRUE_SYM %prec IS
+ | boolean_test %prec PREC_BELOW_NOT
+ ;
+
+boolean_test:
+ boolean_test IS TRUE_SYM %prec IS
{
$$= new (thd->mem_root) Item_func_istrue(thd, $1);
if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
- | expr IS not TRUE_SYM %prec IS
+ | boolean_test IS not TRUE_SYM %prec IS
{
$$= new (thd->mem_root) Item_func_isnottrue(thd, $1);
if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
- | expr IS FALSE_SYM %prec IS
+ | boolean_test IS FALSE_SYM %prec IS
{
$$= new (thd->mem_root) Item_func_isfalse(thd, $1);
if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
- | expr IS not FALSE_SYM %prec IS
+ | boolean_test IS not FALSE_SYM %prec IS
{
$$= new (thd->mem_root) Item_func_isnotfalse(thd, $1);
if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
- | expr IS UNKNOWN_SYM %prec IS
+ | boolean_test IS UNKNOWN_SYM %prec IS
{
$$= new (thd->mem_root) Item_func_isnull(thd, $1);
if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
- | expr IS not UNKNOWN_SYM %prec IS
+ | boolean_test IS not UNKNOWN_SYM %prec IS
{
$$= new (thd->mem_root) Item_func_isnotnull(thd, $1);
if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
- | expr IS NULL_SYM %prec PREC_BELOW_NOT
+ | boolean_test IS NULL_SYM %prec IS
{
$$= new (thd->mem_root) Item_func_isnull(thd, $1);
if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
- | expr IS not NULL_SYM %prec IS
+ | boolean_test IS not NULL_SYM %prec IS
{
$$= new (thd->mem_root) Item_func_isnotnull(thd, $1);
if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
- | expr EQUAL_SYM predicate %prec EQUAL_SYM
+ | boolean_test EQUAL_SYM predicate %prec EQUAL_SYM
{
$$= new (thd->mem_root) Item_func_equal(thd, $1, $3);
if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
- | expr comp_op predicate %prec '='
+ | boolean_test comp_op predicate %prec '='
{
$$= (*$2)(0)->create(thd, $1, $3);
if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
- | expr comp_op all_or_any '(' subselect ')' %prec '='
+ | boolean_test comp_op all_or_any '(' subselect ')' %prec '='
{
$$= all_any_subquery_creator(thd, $1, $2, $3, $5);
if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
- | predicate
+ | predicate %prec BETWEEN_SYM
;
predicate:
diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc
index b1d2b4919f2..47138046ab4 100644
--- a/sql/sys_vars.cc
+++ b/sql/sys_vars.cc
@@ -792,12 +792,26 @@ static bool check_charset(sys_var *self, THD *thd, set_var *var)
{
int csno= (int)var->value->val_int();
CHARSET_INFO *cs;
- if (!(var->save_result.ptr= cs= get_charset(csno, MYF(0))) ||
- !(cs->state & MY_CS_PRIMARY))
+ if ((var->save_result.ptr= cs= get_charset(csno, MYF(0))))
{
- my_error(ER_UNKNOWN_CHARACTER_SET, MYF(0), llstr(csno, buff));
- return true;
+ /*
+ Backward compatibility: pre MDEV-30824 servers
+ can write non-default collation IDs to binary log:
+ SET character_set_client=83; -- utf8mb3_bin
+ Convert a non-default collation to the compiled default collation,
+ e.g. utf8mb3_bin to utf8mb3_general_ci, but only if
+ - THD is a slave thread or
+ - is processing a mysqlbinlog output.
+ */
+ if ((cs->state & MY_CS_PRIMARY) ||
+ ((thd->variables.pseudo_slave_mode || thd->slave_thread) &&
+ (var->save_result.ptr=
+ Lex_exact_charset_opt_extended_collate(cs, true).
+ find_default_collation())))
+ return false;
}
+ my_error(ER_UNKNOWN_CHARACTER_SET, MYF(0), llstr(csno, buff));
+ return true;
}
return false;
}
diff --git a/sql/tztime.cc b/sql/tztime.cc
index 8e1a8805d96..8ddb9f0e30e 100644
--- a/sql/tztime.cc
+++ b/sql/tztime.cc
@@ -2433,9 +2433,9 @@ print_tz_as_sql(const char* tz_name, const TIME_ZONE_INFO *sp)
#define SAVE_ENGINE(e) \
- "\"select ENGINE into @" e "_engine" \
+ "'select ENGINE into @" e "_engine" \
" from information_schema.TABLES" \
- " where TABLE_SCHEMA=DATABASE() and TABLE_NAME='" e "'\""
+ " where TABLE_SCHEMA=DATABASE() and TABLE_NAME=''" e "'''"
/*
Print info about leap seconds in time zone as SQL statements
diff --git a/sql/wsrep_client_service.cc b/sql/wsrep_client_service.cc
index 628a408b49b..d3b4a18195b 100644
--- a/sql/wsrep_client_service.cc
+++ b/sql/wsrep_client_service.cc
@@ -374,8 +374,6 @@ int Wsrep_client_service::bf_rollback()
m_thd->global_read_lock.unlock_global_read_lock(m_thd);
}
m_thd->release_transactional_locks();
- mysql_ull_cleanup(m_thd);
- m_thd->mdl_context.release_explicit_locks();
}
DBUG_RETURN(ret);
diff --git a/sql/wsrep_high_priority_service.cc b/sql/wsrep_high_priority_service.cc
index d9988914c4d..53ef20f3e78 100644
--- a/sql/wsrep_high_priority_service.cc
+++ b/sql/wsrep_high_priority_service.cc
@@ -391,8 +391,6 @@ int Wsrep_high_priority_service::rollback(const wsrep::ws_handle& ws_handle,
m_thd->killed);
m_thd->release_transactional_locks();
- mysql_ull_cleanup(m_thd);
- m_thd->mdl_context.release_explicit_locks();
free_root(m_thd->mem_root, MYF(MY_KEEP_PREALLOC));
diff --git a/storage/connect/catalog.h b/storage/connect/catalog.h
index 2649a50cf76..a46615f5d6e 100644
--- a/storage/connect/catalog.h
+++ b/storage/connect/catalog.h
@@ -39,9 +39,9 @@ typedef struct _colinfo {
PCSZ Name;
int Type;
int Offset;
- int Length;
+ unsigned Length;
int Key;
- int Precision;
+ unsigned Precision;
int Scale;
int Opt;
int Freq;
diff --git a/storage/connect/ha_connect.cc b/storage/connect/ha_connect.cc
index 23d3c7c1058..3327ba63598 100644
--- a/storage/connect/ha_connect.cc
+++ b/storage/connect/ha_connect.cc
@@ -1614,10 +1614,7 @@ void *ha_connect::GetColumnOption(PGLOBAL g, void *field, PCOLINFO pcf)
pcf->Scale= 0;
pcf->Opt= (fop) ? (int)fop->opt : 0;
- if (fp->field_length >= 0)
- pcf->Length= fp->field_length;
- else
- pcf->Length= 256; // BLOB?
+ pcf->Length= fp->field_length;
pcf->Precision= pcf->Length;
diff --git a/storage/connect/reldef.cpp b/storage/connect/reldef.cpp
index 114071b35fb..9af9faa333a 100644
--- a/storage/connect/reldef.cpp
+++ b/storage/connect/reldef.cpp
@@ -91,11 +91,11 @@ PQRYRES OEMColumns(PGLOBAL g, PTOS topt, char* tab, char* db, bool info)
/* directories are used (to make this even remotely secure). */
/*********************************************************************/
if (check_valid_path(module, strlen(module))) {
- strcpy(g->Message, "Module cannot contain a path");
+ safe_strcpy(g->Message, sizeof(g->Message), "Module cannot contain a path");
return NULL;
}
else if (strlen(subtype)+1+3 >= sizeof(getname)) {
- strcpy(g->Message, "Subtype string too long");
+ safe_strcpy(g->Message, sizeof(g->Message), "Subtype string too long");
return NULL;
}
else
@@ -118,7 +118,8 @@ PQRYRES OEMColumns(PGLOBAL g, PTOS topt, char* tab, char* db, bool info)
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS, NULL, rc, 0,
(LPTSTR)buf, sizeof(buf), NULL);
- strcat(strcat(g->Message, ": "), buf);
+ safe_strcat(g->Message, sizeof(g->Message), ": ");
+ safe_strcat(g->Message, sizeof(g->Message), buf);
return NULL;
} // endif hDll
@@ -281,7 +282,7 @@ char *RELDEF::GetStringCatInfo(PGLOBAL g, PCSZ what, PCSZ sdef)
if (IsFileType(GetTypeID(ftype))) {
name= Hc->GetPartName();
sval= (char*)PlugSubAlloc(g, NULL, strlen(name) + 12);
- strcat(strcpy(sval, name), ".");
+ snprintf(sval, strlen(name) + 12, "%s.", name);
n= strlen(sval);
// Fold ftype to lower case
@@ -623,12 +624,11 @@ PTABDEF OEMDEF::GetXdef(PGLOBAL g)
/* directories are used (to make this even remotely secure). */
/*********************************************************************/
if (check_valid_path(Module, strlen(Module))) {
- strcpy(g->Message, "Module cannot contain a path");
+ safe_strcpy(g->Message, sizeof(g->Message), "Module cannot contain a path");
return NULL;
} else
// PlugSetPath(soname, Module, GetPluginDir()); // Crashes on Fedora
- strncat(strcpy(soname, GetPluginDir()), Module,
- sizeof(soname) - strlen(soname) - 1);
+ snprintf(soname, sizeof(soname), "%s%s", GetPluginDir(), Module);
#if defined(_WIN32)
// Is the DLL already loaded?
@@ -642,7 +642,8 @@ PTABDEF OEMDEF::GetXdef(PGLOBAL g)
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS, NULL, rc, 0,
(LPTSTR)buf, sizeof(buf), NULL);
- strcat(strcat(g->Message, ": "), buf);
+ safe_strcat(g->Message, sizeof(g->Message), ": ");
+ safe_strcat(g->Message, sizeof(g->Message), buf);
return NULL;
} // endif hDll
@@ -662,7 +663,8 @@ PTABDEF OEMDEF::GetXdef(PGLOBAL g)
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS, NULL, rc, 0,
(LPTSTR)buf, sizeof(buf), NULL);
- strcat(strcat(g->Message, ": "), buf);
+ safe_strcat(g->Message, sizeof(g->Message), ": ");
+ safe_strcat(g->Message, sizeof(g->Message), buf);
FreeLibrary((HMODULE)Hdll);
return NULL;
} // endif getdef
@@ -811,7 +813,7 @@ PTDB OEMDEF::GetTable(PGLOBAL g, MODE mode)
else
txfp = new(g) ZLBFAM(defp);
#else // !GZ_SUPPORT
- strcpy(g->Message, "Compress not supported");
+ safe_strcpy(g->Message, sizeof(g->Message), "Compress not supported");
return NULL;
#endif // !GZ_SUPPORT
} else if (rfm == RECFM_VAR) {
@@ -834,7 +836,7 @@ PTDB OEMDEF::GetTable(PGLOBAL g, MODE mode)
else
txfp = new(g) VCTFAM((PVCTDEF)defp);
#else // !VCT_SUPPORT
- strcpy(g->Message, "VCT no more supported");
+ safe_strcpy(g->Message, sizeof(g->Message), "VCT no more supported");
return NULL;
#endif // !VCT_SUPPORT
} // endif's
@@ -925,7 +927,7 @@ int COLDEF::Define(PGLOBAL g, void *, PCOLINFO cfp, int poff)
return -1;
} // endswitch
- strcpy(F.Type, GetFormatType(Buf_Type));
+ safe_strcpy(F.Type, sizeof(F.Type), GetFormatType(Buf_Type));
F.Length = cfp->Length;
F.Prec = cfp->Scale;
Offset = (cfp->Offset < 0) ? poff : cfp->Offset;
diff --git a/storage/connect/tabbson.cpp b/storage/connect/tabbson.cpp
index 4cbb0e44e19..dfaf2284a72 100644
--- a/storage/connect/tabbson.cpp
+++ b/storage/connect/tabbson.cpp
@@ -39,6 +39,7 @@
#include "checklvl.h"
#include "resource.h"
#include "mycat.h" // for FNC_COL
+#include "m_string.h"
/***********************************************************************/
/* This should be an option. */
@@ -80,7 +81,7 @@ PQRYRES BSONColumns(PGLOBAL g, PCSZ db, PCSZ dsn, PTOS topt, bool info)
} // endif info
if (GetIntegerTableOption(g, topt, "Multiple", 0)) {
- strcpy(g->Message, "Cannot find column definition for multiple table");
+ safe_strcpy(g->Message, sizeof(g->Message), "Cannot find column definition for multiple table");
return NULL;
} // endif Multiple
@@ -206,7 +207,7 @@ int BSONDISC::GetColumns(PGLOBAL g, PCSZ db, PCSZ dsn, PTOS topt)
tdp->Uri = (dsn && *dsn ? dsn : NULL);
if (!tdp->Fn && !tdp->Uri) {
- strcpy(g->Message, MSG(MISSING_FNAME));
+ safe_strcpy(g->Message, sizeof(g->Message), MSG(MISSING_FNAME));
return 0;
} else
topt->subtype = NULL;
@@ -318,7 +319,7 @@ int BSONDISC::GetColumns(PGLOBAL g, PCSZ db, PCSZ dsn, PTOS topt)
switch (tjnp->ReadDB(g)) {
case RC_EF:
- strcpy(g->Message, "Void json table");
+ safe_strcpy(g->Message, sizeof(g->Message), "Void json table");
case RC_FX:
goto err;
default:
@@ -328,7 +329,7 @@ int BSONDISC::GetColumns(PGLOBAL g, PCSZ db, PCSZ dsn, PTOS topt)
} // endif pretty
if (!(row = (jsp) ? bp->GetObject(jsp) : NULL)) {
- strcpy(g->Message, "Can only retrieve columns from object rows");
+ safe_strcpy(g->Message, sizeof(g->Message), "Can only retrieve columns from object rows");
goto err;
} // endif row
@@ -405,7 +406,7 @@ bool BSONDISC::Find(PGLOBAL g, PBVAL jvp, PCSZ key, int j)
if (jvp && !bp->IsJson(jvp)) {
if (JsonAllPath() && !fmt[bf])
- strcat(fmt, colname);
+ safe_strcat(fmt, sizeof(fmt), colname);
jcol.Type = (JTYP)jvp->Type;
@@ -439,7 +440,7 @@ bool BSONDISC::Find(PGLOBAL g, PBVAL jvp, PCSZ key, int j)
jcol.Cbn = true;
} else if (j < lvl && !Stringified(strfy, colname)) {
if (!fmt[bf])
- strcat(fmt, colname);
+ safe_strcat(fmt, sizeof(fmt), colname);
p = fmt + strlen(fmt);
jsp = jvp;
@@ -510,11 +511,11 @@ bool BSONDISC::Find(PGLOBAL g, PBVAL jvp, PCSZ key, int j)
} else if (lvl >= 0) {
if (Stringified(strfy, colname)) {
if (!fmt[bf])
- strcat(fmt, colname);
+ safe_strcat(fmt, sizeof(fmt), colname);
- strcat(fmt, ".*");
+ safe_strcat(fmt, sizeof(fmt), ".*");
} else if (JsonAllPath() && !fmt[bf])
- strcat(fmt, colname);
+ safe_strcat(fmt, sizeof(fmt), colname);
jcol.Type = TYPE_STRG;
jcol.Len = sz;
@@ -961,7 +962,7 @@ PVAL BCUTIL::ExpandArray(PGLOBAL g, PBVAL arp, int n)
} // endif ars
if (!(bvp = GetArrayValue(arp, (nodes[n].Rx = nodes[n].Nx)))) {
- strcpy(g->Message, "Logical error expanding array");
+ safe_strcpy(g->Message, sizeof(g->Message), "Logical error expanding array");
throw 666;
} // endif jvp
@@ -1146,7 +1147,7 @@ PBVAL BCUTIL::GetRow(PGLOBAL g)
} else if (row->Type == TYPE_JAR) {
AddArrayValue(row, (nwr = NewVal(type)));
} else {
- strcpy(g->Message, "Wrong type when writing new row");
+ safe_strcpy(g->Message, sizeof(g->Message), "Wrong type when writing new row");
nwr = NULL;
} // endif's
@@ -1255,7 +1256,7 @@ PTDB BSONDEF::GetTable(PGLOBAL g, MODE m)
// Allocate the parse work memory
G = PlugInit(NULL, (size_t)Lrecl * (Pretty < 0 ? 3 : 5));
} else {
- strcpy(g->Message, "LRECL is not defined");
+ safe_strcpy(g->Message, sizeof(g->Message), "LRECL is not defined");
return NULL;
} // endif Lrecl
@@ -1295,7 +1296,7 @@ PTDB BSONDEF::GetTable(PGLOBAL g, MODE m)
} else if (m == MODE_INSERT) {
txfp = new(g) ZIPFAM(this);
} else {
- strcpy(g->Message, "UPDATE/DELETE not supported for ZIP");
+ safe_strcpy(g->Message, sizeof(g->Message), "UPDATE/DELETE not supported for ZIP");
return NULL;
} // endif's m
#else // !ZIP_SUPPORT
@@ -1325,10 +1326,10 @@ PTDB BSONDEF::GetTable(PGLOBAL g, MODE m)
if (m == MODE_READ || m == MODE_ANY || m == MODE_ALTER) {
txfp = new(g) UNZFAM(this);
} else if (m == MODE_INSERT) {
- strcpy(g->Message, "INSERT supported only for zipped JSON when pretty=0");
+ safe_strcpy(g->Message, sizeof(g->Message), "INSERT supported only for zipped JSON when pretty=0");
return NULL;
} else {
- strcpy(g->Message, "UPDATE/DELETE not supported for ZIP");
+ safe_strcpy(g->Message, sizeof(g->Message), "UPDATE/DELETE not supported for ZIP");
return NULL;
} // endif's m
#else // !ZIP_SUPPORT
@@ -1661,7 +1662,7 @@ bool TDBBSN::PrepareWriting(PGLOBAL g)
strcat(s, ",");
if ((signed)strlen(s) > Lrecl) {
- strncpy(To_Line, s, Lrecl);
+ safe_strcpy(To_Line, Lrecl, s);
snprintf(g->Message, sizeof(g->Message), "Line truncated (lrecl=%d)", Lrecl);
return PushWarning(g, this);
} else
@@ -1764,7 +1765,7 @@ bool BSONCOL::CheckExpand(PGLOBAL g, int i, PSZ nm, bool b)
Xpd = true; // Expandable object
Nodes[i].Op = OP_EXP;
} else if (b) {
- strcpy(g->Message, "Cannot expand more than one branch");
+ safe_strcpy(g->Message, sizeof(g->Message), "Cannot expand more than one branch");
return true;
} // endif Xcol
@@ -1975,7 +1976,7 @@ bool BSONCOL::ParseJpath(PGLOBAL g)
if (SetArrayOptions(g, p, i, Nodes[i - 1].Key))
return true;
else if (Xpd && Tbp->Mode == MODE_DELETE) {
- strcpy(g->Message, "Cannot delete expanded columns");
+ safe_strcpy(g->Message, sizeof(g->Message), "Cannot delete expanded columns");
return true;
} // endif Xpd
@@ -2098,7 +2099,7 @@ void BSONCOL::ReadColumn(PGLOBAL g)
void BSONCOL::WriteColumn(PGLOBAL g)
{
if (Xpd && Tbp->Pretty < 2) {
- strcpy(g->Message, "Cannot write expanded column when Pretty is not 2");
+ safe_strcpy(g->Message, sizeof(g->Message), "Cannot write expanded column when Pretty is not 2");
throw 666;
} // endif Xpd
@@ -2128,7 +2129,7 @@ void BSONCOL::WriteColumn(PGLOBAL g)
char *s = Value->GetCharValue();
if (!(jsp = Cp->ParseJson(g, s, strlen(s)))) {
- strcpy(g->Message, s);
+ safe_strcpy(g->Message, sizeof(g->Message), s);
throw 666;
} // endif jsp
@@ -2314,7 +2315,7 @@ int TDBBSON::MakeDocument(PGLOBAL g)
if (!a && *p && *p != '[' && !IsNum(p)) {
// obj is a key
if (jsp->Type != TYPE_JOB) {
- strcpy(g->Message, "Table path does not match the json file");
+ safe_strcpy(g->Message, sizeof(g->Message), "Table path does not match the json file");
return RC_FX;
} // endif Type
@@ -2340,7 +2341,7 @@ int TDBBSON::MakeDocument(PGLOBAL g)
} // endif p
if (jsp->Type != TYPE_JAR) {
- strcpy(g->Message, "Table path does not match the json file");
+ safe_strcpy(g->Message, sizeof(g->Message), "Table path does not match the json file");
return RC_FX;
} // endif Type
@@ -2434,7 +2435,7 @@ void TDBBSON::ResetSize(void)
int TDBBSON::MakeIndex(PGLOBAL g, PIXDEF pxdf, bool)
{
if (pxdf) {
- strcpy(g->Message, "JSON not indexable when pretty = 2");
+ safe_strcpy(g->Message, sizeof(g->Message), "JSON not indexable when pretty = 2");
return RC_FX;
} else
return RC_OK;
diff --git a/storage/connect/tabdos.cpp b/storage/connect/tabdos.cpp
index 62eecb5e69e..0fdc182f6df 100644
--- a/storage/connect/tabdos.cpp
+++ b/storage/connect/tabdos.cpp
@@ -62,6 +62,7 @@
#include "tabmul.h"
#include "array.h"
#include "blkfil.h"
+#include "m_string.h"
/***********************************************************************/
/* DB static variables. */
@@ -258,7 +259,7 @@ bool DOSDEF::DeleteIndexFile(PGLOBAL g, PIXDEF pxdf)
sep = GetBoolCatInfo("SepIndex", false);
if (!sep && pxdf) {
- strcpy(g->Message, MSG(NO_RECOV_SPACE));
+ safe_strcpy(g->Message, sizeof(g->Message), MSG(NO_RECOV_SPACE));
return true;
} // endif sep
@@ -293,7 +294,8 @@ bool DOSDEF::DeleteIndexFile(PGLOBAL g, PIXDEF pxdf)
for (; pxdf; pxdf = pxdf->GetNext()) {
_splitpath(Ofn, drive, direc, fname, NULL);
- strcat(strcat(fname, "_"), pxdf->GetName());
+ safe_strcat(fname, sizeof(fname), "_");
+ safe_strcat(fname, sizeof(fname), pxdf->GetName());
_makepath(filename, drive, direc, fname, ftype);
PlugSetPath(filename, filename, GetPath());
#if defined(_WIN32)
@@ -312,7 +314,7 @@ bool DOSDEF::DeleteIndexFile(PGLOBAL g, PIXDEF pxdf)
} else { // !sep
// Drop all indexes, delete the common file
PlugSetPath(filename, Ofn, GetPath());
- strcat(PlugRemoveType(filename, filename), ftype);
+ safe_strcat(PlugRemoveType(filename, filename), sizeof(filename), ftype);
#if defined(_WIN32)
if (!DeleteFile(filename))
rc = (GetLastError() != ERROR_FILE_NOT_FOUND);
@@ -365,7 +367,7 @@ PTDB DOSDEF::GetTable(PGLOBAL g, MODE mode)
if (mode == MODE_READ || mode == MODE_ANY || mode == MODE_ALTER) {
txfp = new(g) UZDFAM(this);
} else {
- strcpy(g->Message, "Zipped DBF tables are read only");
+ safe_strcpy(g->Message, sizeof(g->Message), "Zipped DBF tables are read only");
return NULL;
} // endif's mode
@@ -386,7 +388,7 @@ PTDB DOSDEF::GetTable(PGLOBAL g, MODE mode)
} else if (mode == MODE_INSERT) {
txfp = new(g) ZIPFAM(this);
} else {
- strcpy(g->Message, "UPDATE/DELETE not supported for ZIP");
+ safe_strcpy(g->Message, sizeof(g->Message), "UPDATE/DELETE not supported for ZIP");
return NULL;
} // endif's mode
@@ -397,7 +399,7 @@ PTDB DOSDEF::GetTable(PGLOBAL g, MODE mode)
} else if (mode == MODE_INSERT) {
txfp = new(g) ZPXFAM(this);
} else {
- strcpy(g->Message, "UPDATE/DELETE not supported for ZIP");
+ safe_strcpy(g->Message, sizeof(g->Message), "UPDATE/DELETE not supported for ZIP");
return NULL;
} // endif's mode
@@ -655,7 +657,7 @@ int TDBDOS::MakeBlockValues(PGLOBAL g)
if ((nrec = defp->GetElemt()) < 2) {
if (!To_Def->Partitioned()) {
// This may be wrong to do in some cases
- strcpy(g->Message, MSG(TABLE_NOT_OPT));
+ safe_strcpy(g->Message, sizeof(g->Message), MSG(TABLE_NOT_OPT));
return RC_INFO; // Not to be optimized
} else
return RC_OK;
@@ -675,7 +677,7 @@ int TDBDOS::MakeBlockValues(PGLOBAL g)
if ((block = (int)((MaxSize + (int)nrec - 1) / (int)nrec)) < 2) {
// This may be wrong to do in some cases
defp->RemoveOptValues(g);
- strcpy(g->Message, MSG(TABLE_NOT_OPT));
+ safe_strcpy(g->Message, sizeof(g->Message), MSG(TABLE_NOT_OPT));
return RC_INFO; // Not to be optimized
} // endif block
@@ -758,7 +760,7 @@ int TDBDOS::MakeBlockValues(PGLOBAL g)
// No optimised columns. Still useful for blocked variable tables.
if (!colp && defp->Recfm != RECFM_VAR) {
- strcpy(g->Message, "No optimised columns");
+ safe_strcpy(g->Message, sizeof(g->Message), "No optimised columns");
return RC_INFO;
} // endif colp
@@ -788,7 +790,8 @@ int TDBDOS::MakeBlockValues(PGLOBAL g)
/*********************************************************************/
char *p = (char *)PlugSubAlloc(g, NULL, 24 + strlen(Name));
- dup->Step = strcat(strcpy(p, MSG(OPTIMIZING)), Name);
+ snprintf(p, 24 + strlen(Name), "%s%s", MSG(OPTIMIZING), Name);
+ dup->Step = p;
dup->ProgMax = GetProgMax(g);
dup->ProgCur = 0;
#endif // SOCKET_MODE || THREAD
@@ -805,7 +808,7 @@ int TDBDOS::MakeBlockValues(PGLOBAL g)
} else {
if (++curnum >= nrec) {
if (++curblk >= block) {
- strcpy(g->Message, MSG(BAD_BLK_ESTIM));
+ safe_strcpy(g->Message, sizeof(g->Message), MSG(BAD_BLK_ESTIM));
goto err;
} else
curnum = 0;
@@ -833,7 +836,7 @@ int TDBDOS::MakeBlockValues(PGLOBAL g)
#if defined(PROG_INFO)
if (!dup->Step) {
- strcpy(g->Message, MSG(OPT_CANCELLED));
+ safe_strcpy(g->Message, sizeof(g->Message), MSG(OPT_CANCELLED));
goto err;
} else
dup->ProgCur = GetProgCur();
@@ -913,7 +916,8 @@ bool TDBDOS::SaveBlockValues(PGLOBAL g)
if (!(opfile = fopen(filename, "wb"))) {
snprintf(g->Message, sizeof(g->Message), MSG(OPEN_MODE_ERROR),
"wb", (int)errno, filename);
- strcat(strcat(g->Message, ": "), strerror(errno));
+ safe_strcat(g->Message, sizeof(g->Message), ": ");
+ safe_strcat(g->Message, sizeof(g->Message), strerror(errno));
if (trace(1))
htrc("%s\n", g->Message);
@@ -1230,7 +1234,8 @@ bool TDBDOS::GetDistinctColumnValues(PGLOBAL g, int nrec)
/* Initialize progress information */
/*********************************************************************/
p = (char *)PlugSubAlloc(g, NULL, 48 + strlen(Name));
- dup->Step = strcat(strcpy(p, MSG(GET_DIST_VALS)), Name);
+ snprintf(p, 48 + strlen(Name), "%s%s", MSG(GET_DIST_VALS), Name);
+ dup->Step = p;
dup->ProgMax = GetProgMax(g);
dup->ProgCur = 0;
@@ -1242,12 +1247,12 @@ bool TDBDOS::GetDistinctColumnValues(PGLOBAL g, int nrec)
#if defined(SOCKET_MODE)
if (SendProgress(dup)) {
- strcpy(g->Message, MSG(OPT_CANCELLED));
+ safe_strcpy(g->Message, sizeof(g->Message), MSG(OPT_CANCELLED));
return true;
} else
#elif defined(THREAD)
if (!dup->Step) {
- strcpy(g->Message, MSG(OPT_CANCELLED));
+ safe_strcpy(g->Message, sizeof(g->Message), MSG(OPT_CANCELLED));
return true;
} else
#endif // THREAD
@@ -1528,7 +1533,7 @@ PBF TDBDOS::CheckBlockFilari(PGLOBAL g, PXOB *arg, int op, bool *cnv)
} else if (n == 8 || n == 14) {
if (n == 8 && ctype != TYPE_LIST) {
// Should never happen
- strcpy(g->Message, "Block opt: bad constant");
+ safe_strcpy(g->Message, sizeof(g->Message), "Block opt: bad constant");
throw 99;
} // endif Conv
@@ -1686,7 +1691,7 @@ int TDBDOS::MakeIndex(PGLOBAL g, PIXDEF pxdf, bool add)
// Are we are called from CreateTable or CreateIndex?
if (pxdf) {
if (!add && dfp->GetIndx()) {
- strcpy(g->Message, MSG(INDX_EXIST_YET));
+ safe_strcpy(g->Message, sizeof(g->Message), MSG(INDX_EXIST_YET));
return RC_FX;
} // endif To_Indx
@@ -1798,7 +1803,7 @@ int TDBDOS::MakeIndex(PGLOBAL g, PIXDEF pxdf, bool add)
htrc("Exception %d: %s\n", n, g->Message);
rc = RC_FX;
} catch (const char *msg) {
- strcpy(g->Message, msg);
+ safe_strcpy(g->Message, sizeof(g->Message), msg);
rc = RC_FX;
} // end catch
@@ -1832,7 +1837,7 @@ bool TDBDOS::InitialyzeIndex(PGLOBAL g, volatile PIXDEF xdp, bool sorted)
PKPDEF kdp;
if (!xdp && !(xdp = To_Xdp)) {
- strcpy(g->Message, "NULL dynamic index");
+ safe_strcpy(g->Message, sizeof(g->Message), "NULL dynamic index");
return true;
} else
dynamic = To_Filter && xdp->IsUnique() && xdp->IsDynamic();
@@ -1921,7 +1926,7 @@ bool TDBDOS::InitialyzeIndex(PGLOBAL g, volatile PIXDEF xdp, bool sorted)
htrc("Exception %d: %s\n", n, g->Message);
brc = true;
} catch (const char *msg) {
- strcpy(g->Message, msg);
+ safe_strcpy(g->Message, sizeof(g->Message), msg);
brc = true;
} // end catch
@@ -2682,38 +2687,38 @@ void DOSCOL::WriteColumn(PGLOBAL g)
if (Ldz || Nod || Dcm >= 0) {
switch (Buf_Type) {
case TYPE_SHORT:
- strcpy(fmt, (Ldz) ? "%0*hd" : "%*.hd");
+ safe_strcpy(fmt, sizeof(fmt), (Ldz) ? "%0*hd" : "%*.hd");
i = 0;
if (Nod)
for (; i < Dcm; i++)
- strcat(fmt, "0");
+ safe_strcat(fmt, sizeof(fmt), "0");
len = sprintf(Buf, fmt, field - i, Value->GetShortValue());
break;
case TYPE_INT:
- strcpy(fmt, (Ldz) ? "%0*d" : "%*.d");
+ safe_strcpy(fmt, sizeof(fmt), (Ldz) ? "%0*d" : "%*.d");
i = 0;
if (Nod)
for (; i < Dcm; i++)
- strcat(fmt, "0");
+ safe_strcat(fmt,sizeof(fmt), "0");
len = sprintf(Buf, fmt, field - i, Value->GetIntValue());
break;
case TYPE_TINY:
- strcpy(fmt, (Ldz) ? "%0*d" : "%*.d");
+ safe_strcpy(fmt, sizeof(fmt), (Ldz) ? "%0*d" : "%*.d");
i = 0;
if (Nod)
for (; i < Dcm; i++)
- strcat(fmt, "0");
+ safe_strcat(fmt, sizeof(fmt), "0");
len = sprintf(Buf, fmt, field - i, Value->GetTinyValue());
break;
case TYPE_DOUBLE:
case TYPE_DECIM:
- strcpy(fmt, (Ldz) ? "%0*.*lf" : "%*.*lf");
+ safe_strcpy(fmt, sizeof(fmt), (Ldz) ? "%0*.*lf" : "%*.*lf");
len = field + ((Nod && Dcm) ? 1 : 0);
snprintf(Buf, len + 1, fmt, len, Dcm, Value->GetFloatValue());
len = strlen(Buf);
diff --git a/storage/connect/tabext.cpp b/storage/connect/tabext.cpp
index 96a9f70e4a3..f558cb04f4d 100644
--- a/storage/connect/tabext.cpp
+++ b/storage/connect/tabext.cpp
@@ -65,7 +65,7 @@ int CONDFIL::Init(PGLOBAL g, PHC hc)
while (alt) {
if (!(p = strchr(alt, '='))) {
- strcpy(g->Message, "Invalid alias list");
+ safe_strcpy(g->Message, sizeof(g->Message), "Invalid alias list");
rc = RC_FX;
break;
} // endif !p
@@ -126,7 +126,7 @@ EXTDEF::EXTDEF(void)
bool EXTDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff)
{
if (g->Createas) {
- strcpy(g->Message,
+ safe_strcpy(g->Message, sizeof(g->Message),
"Multiple-table UPDATE/DELETE commands are not supported");
return true;
} // endif multi
@@ -349,7 +349,7 @@ bool TDBEXT::MakeSrcdef(PGLOBAL g)
int n_placeholders = count_placeholders(Srcdef);
if (n_placeholders < 0)
{
- strcpy(g->Message, "MakeSQL: Wrong place holders specification");
+ safe_strcpy(g->Message, sizeof(g->Message), "MakeSQL: Wrong place holders specification");
return true;
}
@@ -372,7 +372,7 @@ bool TDBEXT::MakeSrcdef(PGLOBAL g)
Query = new(g)STRING(g, strlen(Srcdef) + strlen(fil1) + strlen(fil2));
Query->SetLength(sprintf(Query->GetStr(), Srcdef, fil2, fil1));
} else {
- strcpy(g->Message, "MakeSQL: Wrong place holders specification");
+ safe_strcpy(g->Message, sizeof(g->Message), "MakeSQL: Wrong place holders specification");
return true;
} // endif's ph
@@ -466,7 +466,7 @@ bool TDBEXT::MakeSQL(PGLOBAL g, bool cnt)
if (Quote) {
// Tabname can have both database and table identifiers, we need to parse
- if (res= strstr(buf, "."))
+ if ((res= strstr(buf, ".")))
{
// Parse schema
my_len= res - buf + 1;
@@ -513,7 +513,7 @@ bool TDBEXT::MakeSQL(PGLOBAL g, bool cnt)
len += ((Mode == MODE_READX) ? 256 : 1);
if (Query->IsTruncated()) {
- strcpy(g->Message, "MakeSQL: Out of memory");
+ safe_strcpy(g->Message, sizeof(g->Message), "MakeSQL: Out of memory");
return true;
} else
Query->Resize(len);
@@ -574,6 +574,7 @@ bool TDBEXT::MakeCommand(PGLOBAL g)
bool qtd = Quoted > 0;
char q = qtd ? *Quote : ' ';
int i = 0, k = 0;
+ size_t stmt_sz = 0;
// Make a lower case copy of the originale query and change
// back ticks to the data source identifier quoting character
@@ -585,26 +586,30 @@ bool TDBEXT::MakeCommand(PGLOBAL g)
p[7] = 0; // Remove where clause
Qrystr[(p - qrystr) + 7] = 0;
body = To_CondFil->Body;
- stmt = (char*)PlugSubAlloc(g, NULL, strlen(qrystr)
- + strlen(body) + 64);
+ stmt_sz = strlen(qrystr) + strlen(body) + 64;
} else
- stmt = (char*)PlugSubAlloc(g, NULL, strlen(Qrystr) + 64);
+ stmt_sz = strlen(Qrystr) + 64;
+ stmt = (char*)PlugSubAlloc(g, NULL, stmt_sz);
// Check whether the table name is equal to a keyword
// If so, it must be quoted in the original query
- strlwr(strcat(strcat(strcpy(name, " "), Name), " "));
+ snprintf(name, sizeof(name), " %s ", Name);
+ strlwr(name);
if (strstr(" update delete low_priority ignore quick from ", name)) {
if (Quote) {
- strlwr(strcat(strcat(strcpy(name, Quote), Name), Quote));
+ snprintf(name, sizeof(name), "%s%s%s", Quote, Name, Quote);
+ strlwr(name);
k += 2;
} else {
- strcpy(g->Message, "Quoted must be specified");
+ safe_strcpy(g->Message, sizeof(g->Message), "Quoted must be specified");
return true;
} // endif Quote
- } else
- strlwr(strcpy(name, Name)); // Not a keyword
+ } else {
+ safe_strcpy(name, sizeof(name), Name); // Not a keyword
+ strlwr(name);
+ }
if ((p = strstr(qrystr, name))) {
for (i = 0; i < p - qrystr; i++)
@@ -618,21 +623,29 @@ bool TDBEXT::MakeCommand(PGLOBAL g)
schmp = Schema;
if (qtd && *(p - 1) == ' ') {
- if (schmp)
- strcat(strcat(stmt, schmp), ".");
+ if (schmp) {
+ safe_strcat(stmt, stmt_sz, schmp);
+ safe_strcat(stmt, stmt_sz, ".");
+ }
- strcat(strcat(strcat(stmt, Quote), TableName), Quote);
+ safe_strcat(stmt, stmt_sz, Quote);
+ safe_strcat(stmt, stmt_sz, TableName);
+ safe_strcat(stmt, stmt_sz, Quote);
} else {
if (schmp) {
if (qtd && *(p - 1) != ' ') {
stmt[i - 1] = 0;
- strcat(strcat(strcat(stmt, schmp), "."), Quote);
- } else
- strcat(strcat(stmt, schmp), ".");
+ safe_strcat(stmt, stmt_sz, schmp);
+ safe_strcat(stmt, stmt_sz, ".");
+ safe_strcat(stmt, stmt_sz, Quote);
+ } else {
+ safe_strcat(stmt, stmt_sz, schmp);
+ safe_strcat(stmt, stmt_sz, ".");
+ }
} // endif schmp
- strcat(stmt, TableName);
+ safe_strcat(stmt, stmt_sz, TableName);
} // endif's
i = (int)strlen(stmt);
@@ -644,7 +657,7 @@ bool TDBEXT::MakeCommand(PGLOBAL g)
RemoveConst(g, stmt);
if (body)
- strcat(stmt, body);
+ safe_strcat(stmt, stmt_sz, body);
} else {
snprintf(g->Message, sizeof(g->Message), "Cannot use this %s command",
diff --git a/storage/connect/tabfmt.cpp b/storage/connect/tabfmt.cpp
index 7edffc638fa..037a465af13 100644
--- a/storage/connect/tabfmt.cpp
+++ b/storage/connect/tabfmt.cpp
@@ -62,6 +62,7 @@
#define NO_FUNC
#include "plgcnx.h" // For DB types
#include "resource.h"
+#include "m_string.h"
/***********************************************************************/
/* This should be an option. */
@@ -137,7 +138,7 @@ PQRYRES CSVColumns(PGLOBAL g, PCSZ dp, PTOS topt, bool info)
? strchr(tdp->Entry, '*') || strchr(tdp->Entry, '?')
: GetBooleanTableOption(g, topt, "Mulentries", false);
#else // !ZIP_SUPPORT
- strcpy(g->Message, "ZIP not supported by this version");
+ safe_strcpy(g->Message, sizeof(g->Message), "ZIP not supported by this version");
return NULL;
#endif // !ZIP_SUPPORT
} // endif // Zipped
@@ -145,7 +146,7 @@ PQRYRES CSVColumns(PGLOBAL g, PCSZ dp, PTOS topt, bool info)
fn = tdp->Fn = GetStringTableOption(g, topt, "Filename", NULL);
if (!tdp->Fn) {
- strcpy(g->Message, MSG(MISSING_FNAME));
+ safe_strcpy(g->Message, sizeof(g->Message), MSG(MISSING_FNAME));
return NULL;
} // endif Fn
@@ -472,7 +473,7 @@ bool CSVDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff)
if (Catfunc == FNC_NO)
for (PCOLDEF cdp = To_Cols; cdp; cdp = cdp->GetNext())
if (cdp->GetOffset() < 1 && !cdp->IsSpecial()) {
- strcpy(g->Message, MSG(BAD_OFFSET_VAL));
+ safe_strcpy(g->Message, sizeof(g->Message), MSG(BAD_OFFSET_VAL));
return true;
} // endif Offset
@@ -528,11 +529,11 @@ PTDB CSVDEF::GetTable(PGLOBAL g, MODE mode)
} else if (mode == MODE_INSERT) {
txfp = new(g) ZIPFAM(this);
} else {
- strcpy(g->Message, "UPDATE/DELETE not supported for ZIP");
+ safe_strcpy(g->Message, sizeof(g->Message), "UPDATE/DELETE not supported for ZIP");
return NULL;
} // endif's mode
#else // !ZIP_SUPPORT
- strcpy(g->Message, "ZIP not supported");
+ safe_strcpy(g->Message, sizeof(g->Message), "ZIP not supported");
return NULL;
#endif // !ZIP_SUPPORT
} else if (map) {
@@ -546,7 +547,7 @@ PTDB CSVDEF::GetTable(PGLOBAL g, MODE mode)
txfp = new(g) ZLBFAM(this);
#else // !GZ_SUPPORT
- strcpy(g->Message, "Compress not supported");
+ safe_strcpy(g->Message, sizeof(g->Message), "Compress not supported");
return NULL;
#endif // !GZ_SUPPORT
} else
@@ -878,7 +879,7 @@ bool TDBCSV::SkipHeader(PGLOBAL g)
if (q)
To_Line[strlen(To_Line)] = Qot;
- strcat(To_Line, cdp->GetName());
+ safe_strcat(To_Line, Lrecl, cdp->GetName());
if (q)
To_Line[strlen(To_Line)] = Qot;
@@ -1048,14 +1049,16 @@ bool TDBCSV::PrepareWriting(PGLOBAL g)
for (i = 0; i < Fields; i++) {
if (i)
- strcat(To_Line, sep);
+ safe_strcat(To_Line, Lrecl, sep);
if (Field[i]) {
if (!strlen(Field[i])) {
// Generally null fields are not quoted
- if (Quoted > 2)
+ if (Quoted > 2) {
// Except if explicitly required
- strcat(strcat(To_Line, qot), qot);
+ safe_strcat(To_Line, Lrecl, qot);
+ safe_strcat(To_Line, Lrecl, qot);
+ }
} else if (Qot && (strchr(Field[i], Sep) || *Field[i] == Qot
|| Quoted > 1 || (Quoted == 1 && !Fldtyp[i]))) {
@@ -1074,12 +1077,15 @@ bool TDBCSV::PrepareWriting(PGLOBAL g)
To_Line[k++] = Qot;
To_Line[k] = '\0';
- } else
- strcat(strcat(strcat(To_Line, qot), Field[i]), qot);
+ } else {
+ safe_strcat(To_Line, Lrecl, qot);
+ safe_strcat(To_Line, Lrecl, Field[i]);
+ safe_strcat(To_Line, Lrecl, qot);
+ }
}
else
- strcat(To_Line, Field[i]);
+ safe_strcat(To_Line, Lrecl, Field[i]);
}
} // endfor i
@@ -1156,7 +1162,7 @@ int TDBCSV::CheckWrite(PGLOBAL g)
} // endif
}
if ((nlen += n) > maxlen) {
- strcpy(g->Message, MSG(LINE_TOO_LONG));
+ safe_strcpy(g->Message, sizeof(g->Message), MSG(LINE_TOO_LONG));
return -1;
} // endif nlen
@@ -1266,7 +1272,7 @@ bool TDBFMT::OpenDB(PGLOBAL g)
} // endif n
FldFormat[i] = (PSZ)PlugSubAlloc(g, NULL, n + 5);
- strcpy(FldFormat[i], pfm);
+ safe_strcpy(FldFormat[i], n + 5, pfm);
if (!strcmp(pfm + n, "%m")) {
// This is a field that can be missing. Flag it so it can
@@ -1276,7 +1282,7 @@ bool TDBFMT::OpenDB(PGLOBAL g)
} else if (i+1 < Fields && strcmp(pfm + n, "%n")) {
// There are trailing characters after the field contents
// add a marker for the next field start position.
- strcat(FldFormat[i], "%n");
+ safe_strcat(FldFormat[i], n + 5, "%n");
FmtTest[i] = 1;
} // endif's
diff --git a/storage/connect/tabjdbc.cpp b/storage/connect/tabjdbc.cpp
index 46fb7695a51..0242832b02f 100644
--- a/storage/connect/tabjdbc.cpp
+++ b/storage/connect/tabjdbc.cpp
@@ -277,7 +277,7 @@ PTDB JDBCDEF::GetTable(PGLOBAL g, MODE m)
if (Multiple == 1)
tdbp = new(g)TDBMUL(tdbp);
else if (Multiple == 2)
- strcpy(g->Message, "NO_JDBC_MUL");
+ safe_strcpy(g->Message, sizeof(g->Message), "NO_JDBC_MUL");
} // endswitch Catfunc
@@ -386,7 +386,7 @@ bool TDBJDBC::MakeInsert(PGLOBAL g)
for (colp = Columns; colp; colp = colp->GetNext())
if (colp->IsSpecial()) {
- strcpy(g->Message, "No JDBC special columns");
+ safe_strcpy(g->Message, sizeof(g->Message), "No JDBC special columns");
return true;
} else {
// Column name can be encoded in UTF-8
@@ -460,7 +460,7 @@ bool TDBJDBC::MakeInsert(PGLOBAL g)
} // endfor colp
if ((Query->Append(") VALUES ("))) {
- strcpy(g->Message, "MakeInsert: Out of memory");
+ safe_strcpy(g->Message, sizeof(g->Message), "MakeInsert: Out of memory");
return true;
} else // in case prepared statement fails
pos = Query->GetLength();
@@ -470,7 +470,7 @@ bool TDBJDBC::MakeInsert(PGLOBAL g)
Query->Append("?,");
if (Query->IsTruncated()) {
- strcpy(g->Message, "MakeInsert: Out of memory");
+ safe_strcpy(g->Message, sizeof(g->Message), "MakeInsert: Out of memory");
return true;
} else
Query->RepLast(')');
@@ -532,12 +532,15 @@ int TDBJDBC::Cardinality(PGLOBAL g)
// Table name can be encoded in UTF-8
Decode(TableName, tbn, sizeof(tbn));
- strcpy(qry, "SELECT COUNT(*) FROM ");
+ safe_strcpy(qry, sizeof(qry), "SELECT COUNT(*) FROM ");
- if (Quote)
- strcat(strcat(strcat(qry, Quote), tbn), Quote);
+ if (Quote) {
+ safe_strcat(qry, sizeof(qry), Quote);
+ safe_strcat(qry, sizeof(qry), tbn);
+ safe_strcat(qry, sizeof(qry), Quote);
+ }
else
- strcat(qry, tbn);
+ safe_strcat(qry, sizeof(qry), tbn);
// Allocate a Count(*) column (must not use the default constructor)
Cnp = new(g)JDBCCOL;
@@ -656,7 +659,7 @@ bool TDBJDBC::OpenDB(PGLOBAL g)
if ((Qrp = Jcp->AllocateResult(g, this)))
Memory = 2; // Must be filled
else {
- strcpy(g->Message, "Result set memory allocation failed");
+ safe_strcpy(g->Message, sizeof(g->Message), "Result set memory allocation failed");
return true;
} // endif n
@@ -683,7 +686,7 @@ bool TDBJDBC::OpenDB(PGLOBAL g)
#if 0
if (!(rc = MakeInsert(g))) {
if (Nparm != Jcp->PrepareSQL(Query->GetStr())) {
- strcpy(g->Message, MSG(PARM_CNT_MISS));
+ safe_strcpy(g->Message, sizeof(g->Message), MSG(PARM_CNT_MISS));
rc = true;
} else
rc = BindParameters(g);
@@ -735,12 +738,12 @@ bool TDBJDBC::SetRecpos(PGLOBAL g, int recpos)
CurNum = recpos;
Fpos = recpos;
} else {
- strcpy(g->Message, "Scrolling out of row set NIY");
+ safe_strcpy(g->Message, sizeof(g->Message), "Scrolling out of row set NIY");
return true;
} // endif recpos
} else {
- strcpy(g->Message, "This action requires a scrollable cursor");
+ safe_strcpy(g->Message, sizeof(g->Message), "This action requires a scrollable cursor");
return true;
} // endif's
@@ -786,7 +789,7 @@ bool TDBJDBC::ReadKey(PGLOBAL g, OPVAL op, const key_range *kr)
if (To_CondFil)
if (Query->Append(" AND ") || Query->Append(To_CondFil->Body)) {
- strcpy(g->Message, "Readkey: Out of memory");
+ safe_strcpy(g->Message, sizeof(g->Message), "Readkey: Out of memory");
return true;
} // endif Append
@@ -919,7 +922,7 @@ int TDBJDBC::WriteDB(PGLOBAL g)
} // endfor colp
if (unlikely(Query->IsTruncated())) {
- strcpy(g->Message, "WriteDB: Out of memory");
+ safe_strcpy(g->Message, sizeof(g->Message), "WriteDB: Out of memory");
return RC_FX;
} // endif Query
@@ -1112,13 +1115,13 @@ PCMD TDBXJDC::MakeCMD(PGLOBAL g)
(To_CondFil->Op == OP_EQ || To_CondFil->Op == OP_IN)) {
xcmd = To_CondFil->Cmds;
} else
- strcpy(g->Message, "Invalid command specification filter");
+ safe_strcpy(g->Message, sizeof(g->Message), "Invalid command specification filter");
} else
- strcpy(g->Message, "No command column in select list");
+ safe_strcpy(g->Message, sizeof(g->Message), "No command column in select list");
} else if (!Srcdef)
- strcpy(g->Message, "No Srcdef default command");
+ safe_strcpy(g->Message, sizeof(g->Message), "No Srcdef default command");
else
xcmd = new(g) CMD(g, Srcdef);
@@ -1149,7 +1152,7 @@ bool TDBXJDC::OpenDB(PGLOBAL g)
this, Tdb_No, Use, Mode);
if (Use == USE_OPEN) {
- strcpy(g->Message, "Multiple execution is not allowed");
+ safe_strcpy(g->Message, sizeof(g->Message), "Multiple execution is not allowed");
return true;
} // endif use
@@ -1171,7 +1174,7 @@ bool TDBXJDC::OpenDB(PGLOBAL g)
Use = USE_OPEN; // Do it now in case we are recursively called
if (Mode != MODE_READ && Mode != MODE_READX) {
- strcpy(g->Message, "No INSERT/DELETE/UPDATE of XJDBC tables");
+ safe_strcpy(g->Message, sizeof(g->Message), "No INSERT/DELETE/UPDATE of XJDBC tables");
return true;
} // endif Mode
@@ -1224,7 +1227,7 @@ int TDBXJDC::ReadDB(PGLOBAL g)
/***********************************************************************/
int TDBXJDC::WriteDB(PGLOBAL g)
{
- strcpy(g->Message, "Execsrc tables are read only");
+ safe_strcpy(g->Message, sizeof(g->Message), "Execsrc tables are read only");
return RC_FX;
} // end of DeleteDB
@@ -1233,7 +1236,7 @@ int TDBXJDC::WriteDB(PGLOBAL g)
/***********************************************************************/
int TDBXJDC::DeleteDB(PGLOBAL g, int irc)
{
- strcpy(g->Message, "NO_XJDBC_DELETE");
+ safe_strcpy(g->Message, sizeof(g->Message), "NO_XJDBC_DELETE");
return RC_FX;
} // end of DeleteDB
diff --git a/storage/connect/tabjson.cpp b/storage/connect/tabjson.cpp
index 968253ec8d1..27bdfee5f35 100644
--- a/storage/connect/tabjson.cpp
+++ b/storage/connect/tabjson.cpp
@@ -85,7 +85,7 @@ PQRYRES JSONColumns(PGLOBAL g, PCSZ db, PCSZ dsn, PTOS topt, bool info)
} // endif info
if (GetIntegerTableOption(g, topt, "Multiple", 0)) {
- strcpy(g->Message, "Cannot find column definition for multiple table");
+ safe_strcpy(g->Message, sizeof(g->Message), "Cannot find column definition for multiple table");
return NULL;
} // endif Multiple
@@ -212,7 +212,7 @@ int JSONDISC::GetColumns(PGLOBAL g, PCSZ db, PCSZ dsn, PTOS topt)
tdp->Uri = (dsn && *dsn ? dsn : NULL);
if (!tdp->Fn && !tdp->Uri) {
- strcpy(g->Message, MSG(MISSING_FNAME));
+ safe_strcpy(g->Message, sizeof(g->Message), MSG(MISSING_FNAME));
return 0;
} else
topt->subtype = NULL;
@@ -320,7 +320,7 @@ int JSONDISC::GetColumns(PGLOBAL g, PCSZ db, PCSZ dsn, PTOS topt)
switch (tjnp->ReadDB(g)) {
case RC_EF:
- strcpy(g->Message, "Void json table");
+ safe_strcpy(g->Message, sizeof(g->Message), "Void json table");
case RC_FX:
goto err;
default:
@@ -333,7 +333,7 @@ int JSONDISC::GetColumns(PGLOBAL g, PCSZ db, PCSZ dsn, PTOS topt)
} // endif pretty
if (!(row = (jsp) ? jsp->GetObject() : NULL)) {
- strcpy(g->Message, "Can only retrieve columns from object rows");
+ safe_strcpy(g->Message, sizeof(g->Message), "Can only retrieve columns from object rows");
goto err;
} // endif row
@@ -417,7 +417,7 @@ bool JSONDISC::Find(PGLOBAL g, PJVAL jvp, PCSZ key, int j)
if (jvp && jvp->DataType != TYPE_JSON) {
if (JsonAllPath() && !fmt[bf])
- strcat(fmt, colname);
+ safe_strcat(fmt, sizeof(fmt), colname);
jcol.Type = jvp->DataType;
@@ -450,7 +450,7 @@ bool JSONDISC::Find(PGLOBAL g, PJVAL jvp, PCSZ key, int j)
jcol.Cbn = true;
} else if (j < lvl && !Stringified(strfy, colname)) {
if (!fmt[bf])
- strcat(fmt, colname);
+ safe_strcat(fmt, sizeof(fmt), colname);
p = fmt + strlen(fmt);
jsp = jvp->GetJson();
@@ -520,11 +520,11 @@ bool JSONDISC::Find(PGLOBAL g, PJVAL jvp, PCSZ key, int j)
} else if (lvl >= 0) {
if (Stringified(strfy, colname)) {
if (!fmt[bf])
- strcat(fmt, colname);
+ safe_strcat(fmt, sizeof(fmt), colname);
- strcat(fmt, ".*");
+ safe_strcat(fmt, sizeof(fmt), ".*");
} else if (JsonAllPath() && !fmt[bf])
- strcat(fmt, colname);
+ safe_strcat(fmt, sizeof(fmt), colname);
jcol.Type = TYPE_STRG;
jcol.Len = sz;
@@ -735,7 +735,7 @@ PTDB JSONDEF::GetTable(PGLOBAL g, MODE m)
} else if (m == MODE_INSERT) {
txfp = new(g) ZIPFAM(this);
} else {
- strcpy(g->Message, "UPDATE/DELETE not supported for ZIP");
+ safe_strcpy(g->Message, sizeof(g->Message), "UPDATE/DELETE not supported for ZIP");
return NULL;
} // endif's m
#else // !ZIP_SUPPORT
@@ -775,7 +775,7 @@ PTDB JSONDEF::GetTable(PGLOBAL g, MODE m)
#endif // 0
((TDBJSN*)tdbp)->G = PlugInit(NULL, (size_t)Lrecl * (Pretty >= 0 ? 12 : 4));
} else {
- strcpy(g->Message, "LRECL is not defined");
+ safe_strcpy(g->Message, sizeof(g->Message), "LRECL is not defined");
return NULL;
} // endif Lrecl
@@ -785,10 +785,10 @@ PTDB JSONDEF::GetTable(PGLOBAL g, MODE m)
if (m == MODE_READ || m == MODE_ANY || m == MODE_ALTER) {
txfp = new(g) UNZFAM(this);
} else if (m == MODE_INSERT) {
- strcpy(g->Message, "INSERT supported only for zipped JSON when pretty=0");
+ safe_strcpy(g->Message, sizeof(g->Message), "INSERT supported only for zipped JSON when pretty=0");
return NULL;
} else {
- strcpy(g->Message, "UPDATE/DELETE not supported for ZIP");
+ safe_strcpy(g->Message, sizeof(g->Message), "UPDATE/DELETE not supported for ZIP");
return NULL;
} // endif's m
#else // !ZIP_SUPPORT
@@ -1144,7 +1144,7 @@ int TDBJSN::ReadDB(PGLOBAL g) {
M = 1;
rc = RC_OK;
} else if (Pretty != 1 || strcmp(To_Line, "]")) {
- strcpy(g->Message, G->Message);
+ safe_strcpy(g->Message, sizeof(g->Message), G->Message);
rc = RC_FX;
} else
rc = RC_EF;
@@ -1257,7 +1257,7 @@ bool TDBJSN::PrepareWriting(PGLOBAL g)
strcat(s, ",");
if ((signed)strlen(s) > Lrecl) {
- strncpy(To_Line, s, Lrecl);
+ safe_strcpy(To_Line, Lrecl, s);
snprintf(g->Message, sizeof(g->Message), "Line truncated (lrecl=%d)", Lrecl);
return PushWarning(g, this);
} else
@@ -1359,7 +1359,7 @@ bool JSONCOL::CheckExpand(PGLOBAL g, int i, PSZ nm, bool b)
Xpd = true; // Expandable object
Nodes[i].Op = OP_EXP;
} else if (b) {
- strcpy(g->Message, "Cannot expand more than one branch");
+ safe_strcpy(g->Message, sizeof(g->Message), "Cannot expand more than one branch");
return true;
} // endif Xcol
@@ -1570,7 +1570,7 @@ bool JSONCOL::ParseJpath(PGLOBAL g)
if (SetArrayOptions(g, p, i, Nodes[i - 1].Key))
return true;
else if (Xpd && Tjp->Mode == MODE_DELETE) {
- strcpy(g->Message, "Cannot delete expanded columns");
+ safe_strcpy(g->Message, sizeof(g->Message), "Cannot delete expanded columns");
return true;
} // endif Xpd
@@ -1674,7 +1674,7 @@ PSZ JSONCOL::GetJpath(PGLOBAL g, bool proj)
PVAL JSONCOL::MakeJson(PGLOBAL g, PJSON jsp, int n)
{
if (Value->IsTypeNum()) {
- strcpy(g->Message, "Cannot make Json for a numeric column");
+ safe_strcpy(g->Message, sizeof(g->Message), "Cannot make Json for a numeric column");
if (!Warned) {
PushWarning(g, Tjp);
@@ -1689,10 +1689,10 @@ PVAL JSONCOL::MakeJson(PGLOBAL g, PJSON jsp, int n)
ulong len = Tjp->Lrecl ? Tjp->Lrecl : 500;
PBSON bsp = JbinAlloc(g, NULL, len, jsp);
- strcat(bsp->Msg, " column");
+ safe_strcat(bsp->Msg, sizeof(bsp->Msg), " column");
((BINVAL*)Value)->SetBinValue(bsp, sizeof(BSON));
} else {
- strcpy(g->Message, "Column size too small");
+ safe_strcpy(g->Message, sizeof(g->Message), "Column size too small");
Value->SetValue_char(NULL, 0);
} // endif Clen
#endif // 0
@@ -1934,7 +1934,7 @@ PVAL JSONCOL::ExpandArray(PGLOBAL g, PJAR arp, int n)
} // endif ars
if (!(jvp = arp->GetArrayValue((Nodes[n].Rx = Nodes[n].Nx)))) {
- strcpy(g->Message, "Logical error expanding array");
+ safe_strcpy(g->Message, sizeof(g->Message), "Logical error expanding array");
throw 666;
} // endif jvp
@@ -2122,7 +2122,7 @@ PJSON JSONCOL::GetRow(PGLOBAL g)
((PJAR)row)->AddArrayValue(G, new(G) JVALUE(nwr));
((PJAR)row)->InitArray(G);
} else {
- strcpy(g->Message, "Wrong type when writing new row");
+ safe_strcpy(g->Message, sizeof(g->Message), "Wrong type when writing new row");
nwr = NULL;
} // endif's
@@ -2143,7 +2143,7 @@ PJSON JSONCOL::GetRow(PGLOBAL g)
void JSONCOL::WriteColumn(PGLOBAL g)
{
if (Xpd && Tjp->Pretty < 2) {
- strcpy(g->Message, "Cannot write expanded column when Pretty is not 2");
+ safe_strcpy(g->Message, sizeof(g->Message), "Cannot write expanded column when Pretty is not 2");
throw 666;
} // endif Xpd
@@ -2179,7 +2179,7 @@ void JSONCOL::WriteColumn(PGLOBAL g)
if (s && *s) {
if (!(jsp = ParseJson(G, s, strlen(s)))) {
- strcpy(g->Message, s);
+ safe_strcpy(g->Message, sizeof(g->Message), s);
throw 666;
} // endif jsp
@@ -2362,7 +2362,7 @@ int TDBJSON::MakeDocument(PGLOBAL g)
if (!a && *p && *p != '[' && !IsNum(p)) {
// obj is a key
if (jsp->GetType() != TYPE_JOB) {
- strcpy(g->Message, "Table path does not match the json file");
+ safe_strcpy(g->Message, sizeof(g->Message), "Table path does not match the json file");
return RC_FX;
} // endif Type
@@ -2388,7 +2388,7 @@ int TDBJSON::MakeDocument(PGLOBAL g)
} // endif p
if (jsp->GetType() != TYPE_JAR) {
- strcpy(g->Message, "Table path does not match the json file");
+ safe_strcpy(g->Message, sizeof(g->Message), "Table path does not match the json file");
return RC_FX;
} // endif Type
@@ -2483,7 +2483,7 @@ void TDBJSON::ResetSize(void)
int TDBJSON::MakeIndex(PGLOBAL g, PIXDEF pxdf, bool)
{
if (pxdf) {
- strcpy(g->Message, "JSON not indexable when pretty = 2");
+ safe_strcpy(g->Message, sizeof(g->Message), "JSON not indexable when pretty = 2");
return RC_FX;
} else
return RC_OK;
diff --git a/storage/connect/value.cpp b/storage/connect/value.cpp
index 498ec71a87f..7265b2ed0ca 100644
--- a/storage/connect/value.cpp
+++ b/storage/connect/value.cpp
@@ -163,9 +163,9 @@ PCSZ GetTypeName(int type)
/***********************************************************************/
/* GetTypeSize: returns the PlugDB internal type size. */
/***********************************************************************/
-int GetTypeSize(int type, int len)
- {
- switch (type) {
+unsigned GetTypeSize(int type, unsigned len)
+{
+ switch (type) {
case TYPE_DECIM:
case TYPE_BIN:
case TYPE_STRING: len = len * sizeof(char); break;
@@ -176,7 +176,7 @@ int GetTypeSize(int type, int len)
case TYPE_DOUBLE: len = sizeof(double); break;
case TYPE_TINY: len = sizeof(char); break;
case TYPE_PCHAR: len = sizeof(char*); break;
- default: len = -1;
+ default: len = 0;
} // endswitch type
return len;
diff --git a/storage/connect/value.h b/storage/connect/value.h
index a0d947347c3..7eb0dec29f2 100644
--- a/storage/connect/value.h
+++ b/storage/connect/value.h
@@ -41,7 +41,7 @@ typedef struct _datpar *PDTP; // For DTVAL
/***********************************************************************/
// Exported functions
DllExport PCSZ GetTypeName(int);
-DllExport int GetTypeSize(int, int);
+DllExport unsigned GetTypeSize(int, unsigned);
#ifdef ODBC_SUPPORT
/* This function is exported for use in OEM table type DLLs */
DllExport int TranslateSQLType(int stp, int prec,
diff --git a/storage/innobase/btr/btr0btr.cc b/storage/innobase/btr/btr0btr.cc
index 4d576ff7a8b..8b66f3d3871 100644
--- a/storage/innobase/btr/btr0btr.cc
+++ b/storage/innobase/btr/btr0btr.cc
@@ -4757,7 +4757,7 @@ n_field_mismatch:
len -= BTR_EXTERN_FIELD_REF_SIZE;
ulint extern_len = mach_read_from_4(
data + len + BTR_EXTERN_LEN + 4);
- if (fixed_size == extern_len) {
+ if (fixed_size == extern_len + len) {
goto next_field;
}
}
diff --git a/storage/innobase/buf/buf0buf.cc b/storage/innobase/buf/buf0buf.cc
index c9c32292024..922c64c1d58 100644
--- a/storage/innobase/buf/buf0buf.cc
+++ b/storage/innobase/buf/buf0buf.cc
@@ -1311,11 +1311,11 @@ buf_tmp_buffer_t *buf_pool_t::io_buf_t::reserve()
for (buf_tmp_buffer_t *s= slots, *e= slots + n_slots; s != e; s++)
if (s->acquire())
return s;
- os_aio_wait_until_no_pending_writes();
+ os_aio_wait_until_no_pending_writes(true);
for (buf_tmp_buffer_t *s= slots, *e= slots + n_slots; s != e; s++)
if (s->acquire())
return s;
- os_aio_wait_until_no_pending_reads();
+ os_aio_wait_until_no_pending_reads(true);
}
}
diff --git a/storage/innobase/buf/buf0dblwr.cc b/storage/innobase/buf/buf0dblwr.cc
index c5de7a41808..1260145ed1c 100644
--- a/storage/innobase/buf/buf0dblwr.cc
+++ b/storage/innobase/buf/buf0dblwr.cc
@@ -596,7 +596,6 @@ bool buf_dblwr_t::flush_buffered_writes(const ulint size)
const bool multi_batch= block1 + static_cast<uint32_t>(size) != block2 &&
old_first_free > size;
flushing_buffered_writes= 1 + multi_batch;
- pages_submitted+= old_first_free;
/* Now safe to release the mutex. */
mysql_mutex_unlock(&mutex);
#ifdef UNIV_DEBUG
diff --git a/storage/innobase/buf/buf0dump.cc b/storage/innobase/buf/buf0dump.cc
index 63a28aa1a37..bb705ec5651 100644
--- a/storage/innobase/buf/buf0dump.cc
+++ b/storage/innobase/buf/buf0dump.cc
@@ -651,7 +651,7 @@ buf_load()
ut_free(dump);
if (i == dump_n) {
- os_aio_wait_until_no_pending_reads();
+ os_aio_wait_until_no_pending_reads(true);
}
ut_sprintf_timestamp(now);
diff --git a/storage/innobase/buf/buf0flu.cc b/storage/innobase/buf/buf0flu.cc
index b702d79df8b..acfef5daad2 100644
--- a/storage/innobase/buf/buf0flu.cc
+++ b/storage/innobase/buf/buf0flu.cc
@@ -113,7 +113,12 @@ void buf_pool_t::page_cleaner_wakeup(bool for_LRU)
{
ut_d(buf_flush_validate_skip());
if (!page_cleaner_idle())
+ {
+ if (for_LRU)
+ /* Ensure that the page cleaner is not in a timed wait. */
+ pthread_cond_signal(&do_flush_list);
return;
+ }
double dirty_pct= double(UT_LIST_GET_LEN(buf_pool.flush_list)) * 100.0 /
double(UT_LIST_GET_LEN(buf_pool.LRU) + UT_LIST_GET_LEN(buf_pool.free));
double pct_lwm= srv_max_dirty_pages_pct_lwm;
@@ -209,7 +214,7 @@ void buf_flush_remove_pages(uint32_t id)
if (!deferred)
break;
- os_aio_wait_until_no_pending_writes();
+ os_aio_wait_until_no_pending_writes(true);
}
}
@@ -1216,6 +1221,11 @@ static void buf_flush_LRU_list_batch(ulint max, bool evict,
++n->evicted;
/* fall through */
case 1:
+ if (UNIV_LIKELY(scanned & 31))
+ continue;
+ mysql_mutex_unlock(&buf_pool.mutex);
+ reacquire_mutex:
+ mysql_mutex_lock(&buf_pool.mutex);
continue;
}
@@ -1251,32 +1261,37 @@ static void buf_flush_LRU_list_batch(ulint max, bool evict,
auto p= buf_flush_space(space_id);
space= p.first;
last_space_id= space_id;
+ if (!space)
+ {
+ mysql_mutex_lock(&buf_pool.mutex);
+ goto no_space;
+ }
mysql_mutex_lock(&buf_pool.mutex);
- if (p.second)
- buf_pool.stat.n_pages_written+= p.second;
+ buf_pool.stat.n_pages_written+= p.second;
}
else
+ {
ut_ad(!space);
+ goto no_space;
+ }
}
else if (space->is_stopping())
{
space->release();
space= nullptr;
- }
-
- if (!space)
- {
+ no_space:
mysql_mutex_lock(&buf_pool.flush_list_mutex);
buf_flush_discard_page(bpage);
+ continue;
}
- else if (neighbors && space->is_rotational())
+
+ if (neighbors && space->is_rotational())
{
mysql_mutex_unlock(&buf_pool.mutex);
n->flushed+= buf_flush_try_neighbors(space, page_id, bpage,
neighbors == 1,
do_evict, n->flushed, max);
-reacquire_mutex:
- mysql_mutex_lock(&buf_pool.mutex);
+ goto reacquire_mutex;
}
else if (n->flushed >= max && !recv_recovery_is_on())
{
@@ -1643,7 +1658,7 @@ done:
space->release();
if (space->purpose == FIL_TYPE_IMPORT)
- os_aio_wait_until_no_pending_writes();
+ os_aio_wait_until_no_pending_writes(true);
else
buf_dblwr.flush_buffered_writes();
@@ -1961,7 +1976,7 @@ static void buf_flush_wait(lsn_t lsn)
break;
}
mysql_mutex_unlock(&buf_pool.flush_list_mutex);
- os_aio_wait_until_no_pending_writes();
+ os_aio_wait_until_no_pending_writes(false);
mysql_mutex_lock(&buf_pool.flush_list_mutex);
}
}
@@ -1996,16 +2011,20 @@ ATTRIBUTE_COLD void buf_flush_wait_flushed(lsn_t sync_lsn)
MONITOR_FLUSH_SYNC_COUNT,
MONITOR_FLUSH_SYNC_PAGES, n_pages);
}
- os_aio_wait_until_no_pending_writes();
+ os_aio_wait_until_no_pending_writes(false);
mysql_mutex_lock(&buf_pool.flush_list_mutex);
}
while (buf_pool.get_oldest_modification(sync_lsn) < sync_lsn);
}
else
#endif
+ {
+ thd_wait_begin(nullptr, THD_WAIT_DISKIO);
+ tpool::tpool_wait_begin();
buf_flush_wait(sync_lsn);
-
- thd_wait_end(nullptr);
+ tpool::tpool_wait_end();
+ thd_wait_end(nullptr);
+ }
}
mysql_mutex_unlock(&buf_pool.flush_list_mutex);
@@ -2164,10 +2183,12 @@ Based on various factors it decides if there is a need to do flushing.
@return number of pages recommended to be flushed
@param last_pages_in number of pages flushed in previous batch
@param oldest_lsn buf_pool.get_oldest_modification(0)
+@param pct_lwm innodb_max_dirty_pages_pct_lwm, or 0 to ignore it
@param dirty_blocks UT_LIST_GET_LEN(buf_pool.flush_list)
@param dirty_pct 100*flush_list.count / (LRU.count + free.count) */
static ulint page_cleaner_flush_pages_recommendation(ulint last_pages_in,
lsn_t oldest_lsn,
+ double pct_lwm,
ulint dirty_blocks,
double dirty_pct)
{
@@ -2237,11 +2258,17 @@ func_exit:
sum_pages = 0;
}
- const ulint pct_for_dirty = srv_max_dirty_pages_pct_lwm == 0
- ? (dirty_pct >= max_pct ? 100 : 0)
- : static_cast<ulint>
- (max_pct > 0.0 ? dirty_pct / max_pct : dirty_pct);
- ulint pct_total = std::max(pct_for_dirty, pct_for_lsn);
+ MONITOR_SET(MONITOR_FLUSH_PCT_FOR_LSN, pct_for_lsn);
+
+ double total_ratio;
+ if (pct_lwm == 0.0 || max_pct == 0.0) {
+ total_ratio = 1;
+ } else {
+ total_ratio = std::max(double(pct_for_lsn) / 100,
+ (dirty_pct / max_pct));
+ }
+
+ MONITOR_SET(MONITOR_FLUSH_PCT_FOR_DIRTY, ulint(total_ratio * 100));
/* Estimate pages to be flushed for the lsn progress */
lsn_t target_lsn = oldest_lsn
@@ -2267,7 +2294,7 @@ func_exit:
pages_for_lsn = 1;
}
- n_pages = (ulint(double(srv_io_capacity) * double(pct_total) / 100.0)
+ n_pages = (ulint(double(srv_io_capacity) * total_ratio)
+ avg_page_rate + pages_for_lsn) / 3;
if (n_pages > srv_max_io_capacity) {
@@ -2280,8 +2307,6 @@ func_exit:
MONITOR_SET(MONITOR_FLUSH_AVG_PAGE_RATE, avg_page_rate);
MONITOR_SET(MONITOR_FLUSH_LSN_AVG_RATE, lsn_avg_rate);
- MONITOR_SET(MONITOR_FLUSH_PCT_FOR_DIRTY, pct_for_dirty);
- MONITOR_SET(MONITOR_FLUSH_PCT_FOR_LSN, pct_for_lsn);
goto func_exit;
}
@@ -2394,7 +2419,7 @@ static void buf_flush_page_cleaner()
soft_lsn_limit= lsn_limit;
}
- bool idle_flush= false;
+ double pct_lwm= 0.0;
ulint n_flushed= 0, n;
if (UNIV_UNLIKELY(soft_lsn_limit != 0))
@@ -2413,7 +2438,7 @@ static void buf_flush_page_cleaner()
mysql_mutex_unlock(&buf_pool.mutex);
last_pages+= n;
- if (!idle_flush)
+ if (pct_lwm == 0.0)
goto end_of_batch;
/* when idle flushing kicks in page_cleaner is marked active.
@@ -2431,7 +2456,8 @@ static void buf_flush_page_cleaner()
guaranteed to be nonempty, and it is a subset of buf_pool.LRU. */
const double dirty_pct= double(dirty_blocks) * 100.0 /
double(UT_LIST_GET_LEN(buf_pool.LRU) + UT_LIST_GET_LEN(buf_pool.free));
- if (srv_max_dirty_pages_pct_lwm != 0.0)
+ pct_lwm= srv_max_dirty_pages_pct_lwm;
+ if (pct_lwm != 0.0)
{
const ulint activity_count= srv_get_activity_count();
if (activity_count != last_activity_count)
@@ -2448,13 +2474,16 @@ static void buf_flush_page_cleaner()
- there are no pending reads but there are dirty pages to flush */
buf_pool.update_last_activity_count(activity_count);
mysql_mutex_unlock(&buf_pool.flush_list_mutex);
- idle_flush= true;
goto idle_flush;
}
else
+ {
maybe_unemployed:
- if (dirty_pct < srv_max_dirty_pages_pct_lwm)
+ const bool below{dirty_pct < pct_lwm};
+ pct_lwm= 0.0;
+ if (below)
goto possibly_unemployed;
+ }
}
else if (dirty_pct < srv_max_buf_pool_modified_pct)
possibly_unemployed:
@@ -2485,6 +2514,7 @@ static void buf_flush_page_cleaner()
}
else if ((n= page_cleaner_flush_pages_recommendation(last_pages,
oldest_lsn,
+ pct_lwm,
dirty_blocks,
dirty_pct)) != 0)
{
@@ -2515,7 +2545,7 @@ static void buf_flush_page_cleaner()
mysql_mutex_lock(&buf_pool.flush_list_mutex);
buf_flush_wait_LRU_batch_end();
mysql_mutex_unlock(&buf_pool.flush_list_mutex);
- os_aio_wait_until_no_pending_writes();
+ os_aio_wait_until_no_pending_writes(false);
}
mysql_mutex_lock(&buf_pool.flush_list_mutex);
@@ -2565,7 +2595,7 @@ ATTRIBUTE_COLD void buf_flush_buffer_pool()
{
mysql_mutex_unlock(&buf_pool.flush_list_mutex);
buf_flush_list(srv_max_io_capacity);
- os_aio_wait_until_no_pending_writes();
+ os_aio_wait_until_no_pending_writes(false);
mysql_mutex_lock(&buf_pool.flush_list_mutex);
service_manager_extend_timeout(INNODB_EXTEND_TIMEOUT_INTERVAL,
"Waiting to flush " ULINTPF " pages",
@@ -2573,7 +2603,6 @@ ATTRIBUTE_COLD void buf_flush_buffer_pool()
}
mysql_mutex_unlock(&buf_pool.flush_list_mutex);
- ut_ad(!os_aio_pending_writes());
ut_ad(!os_aio_pending_reads());
}
diff --git a/storage/innobase/buf/buf0lru.cc b/storage/innobase/buf/buf0lru.cc
index 24527f2815c..19219ea026b 100644
--- a/storage/innobase/buf/buf0lru.cc
+++ b/storage/innobase/buf/buf0lru.cc
@@ -448,15 +448,15 @@ got_block:
mysql_mutex_unlock(&buf_pool.mutex);
mysql_mutex_lock(&buf_pool.flush_list_mutex);
const auto n_flush = buf_pool.n_flush();
+ if (!buf_pool.try_LRU_scan) {
+ buf_pool.page_cleaner_wakeup(true);
+ }
mysql_mutex_unlock(&buf_pool.flush_list_mutex);
mysql_mutex_lock(&buf_pool.mutex);
if (!n_flush) {
goto not_found;
}
if (!buf_pool.try_LRU_scan) {
- mysql_mutex_lock(&buf_pool.flush_list_mutex);
- buf_pool.page_cleaner_wakeup(true);
- mysql_mutex_unlock(&buf_pool.flush_list_mutex);
my_cond_wait(&buf_pool.done_free,
&buf_pool.mutex.m_mutex);
}
diff --git a/storage/innobase/buf/buf0rea.cc b/storage/innobase/buf/buf0rea.cc
index 8631a03644f..39ecd5de27f 100644
--- a/storage/innobase/buf/buf0rea.cc
+++ b/storage/innobase/buf/buf0rea.cc
@@ -685,7 +685,7 @@ void buf_read_recv_pages(uint32_t space_id, st_::span<uint32_t> page_nos)
}
if (os_aio_pending_reads() >= limit) {
- os_aio_wait_until_no_pending_reads();
+ os_aio_wait_until_no_pending_reads(false);
}
space->reacquire();
diff --git a/storage/innobase/dict/drop.cc b/storage/innobase/dict/drop.cc
index 9013841ba5e..35b732c53b6 100644
--- a/storage/innobase/dict/drop.cc
+++ b/storage/innobase/dict/drop.cc
@@ -68,6 +68,7 @@ before transaction commit and must be rolled back explicitly are as follows:
#include "dict0defrag_bg.h"
#include "btr0defragment.h"
+#include "ibuf0ibuf.h"
#include "lock0lock.h"
#include "que0que.h"
@@ -237,6 +238,8 @@ void trx_t::commit(std::vector<pfs_os_file_t> &deleted)
commit_persist();
if (dict_operation)
{
+ std::vector<uint32_t> space_ids;
+ space_ids.reserve(mod_tables.size());
ut_ad(dict_sys.locked());
lock_sys.wr_lock(SRW_LOCK_CALL);
mutex_lock();
@@ -271,6 +274,7 @@ void trx_t::commit(std::vector<pfs_os_file_t> &deleted)
dict_sys.remove(table);
if (const auto id= space ? space->id : 0)
{
+ space_ids.emplace_back(id);
pfs_os_file_t d= fil_delete_tablespace(id);
if (d != OS_FILE_CLOSED)
deleted.emplace_back(d);
@@ -283,6 +287,9 @@ void trx_t::commit(std::vector<pfs_os_file_t> &deleted)
mysql_mutex_lock(&lock_sys.wait_mutex);
lock_sys.deadlock_check();
mysql_mutex_unlock(&lock_sys.wait_mutex);
+
+ for (const auto id : space_ids)
+ ibuf_delete_for_discarded_space(id);
}
commit_cleanup();
}
diff --git a/storage/innobase/fil/fil0fil.cc b/storage/innobase/fil/fil0fil.cc
index 24319bcb8a6..be313140225 100644
--- a/storage/innobase/fil/fil0fil.cc
+++ b/storage/innobase/fil/fil0fil.cc
@@ -45,7 +45,6 @@ Created 10/25/1995 Heikki Tuuri
#include "srv0start.h"
#include "trx0purge.h"
#include "buf0lru.h"
-#include "ibuf0ibuf.h"
#include "buf0flu.h"
#include "log.h"
#ifdef __linux__
@@ -295,6 +294,8 @@ fil_node_t* fil_space_t::add(const char* name, pfs_os_file_t handle,
uint32_t size, bool is_raw, bool atomic_write,
uint32_t max_pages)
{
+ mysql_mutex_assert_owner(&fil_system.mutex);
+
fil_node_t* node;
ut_ad(name != NULL);
@@ -319,7 +320,6 @@ fil_node_t* fil_space_t::add(const char* name, pfs_os_file_t handle,
node->atomic_write = atomic_write;
- mysql_mutex_lock(&fil_system.mutex);
this->size += size;
UT_LIST_ADD_LAST(chain, node);
if (node->is_open()) {
@@ -330,7 +330,6 @@ fil_node_t* fil_space_t::add(const char* name, pfs_os_file_t handle,
release();
}
}
- mysql_mutex_unlock(&fil_system.mutex);
return node;
}
@@ -804,8 +803,17 @@ pfs_os_file_t fil_system_t::detach(fil_space_t *space, bool detach_handle)
space_list_t::iterator s= space_list_t::iterator(space);
if (space_list_last_opened == space)
{
- space_list_t::iterator prev= s;
- space_list_last_opened= &*--prev;
+ if (s == space_list.begin())
+ {
+ ut_ad(srv_operation > SRV_OPERATION_EXPORT_RESTORED ||
+ srv_shutdown_state > SRV_SHUTDOWN_NONE);
+ space_list_last_opened= nullptr;
+ }
+ else
+ {
+ space_list_t::iterator prev= s;
+ space_list_last_opened= &*--prev;
+ }
}
space_list.erase(s);
}
@@ -939,6 +947,7 @@ fil_space_t *fil_space_t::create(uint32_t id, uint32_t flags,
{
fil_space_t* space;
+ mysql_mutex_assert_owner(&fil_system.mutex);
ut_ad(fil_system.is_initialised());
ut_ad(fil_space_t::is_valid_flags(flags & ~FSP_FLAGS_MEM_MASK, id));
ut_ad(srv_page_size == UNIV_PAGE_SIZE_ORIG || flags != 0);
@@ -971,8 +980,6 @@ fil_space_t *fil_space_t::create(uint32_t id, uint32_t flags,
space->latch.SRW_LOCK_INIT(fil_space_latch_key);
- mysql_mutex_lock(&fil_system.mutex);
-
if (const fil_space_t *old_space = fil_space_get_by_id(id)) {
ib::error() << "Trying to add tablespace with id " << id
<< " to the cache, but tablespace '"
@@ -980,7 +987,6 @@ fil_space_t *fil_space_t::create(uint32_t id, uint32_t flags,
? old_space->chain.start->name
: "")
<< "' already exists in the cache!";
- mysql_mutex_unlock(&fil_system.mutex);
space->~fil_space_t();
ut_free(space);
return(NULL);
@@ -1027,12 +1033,12 @@ fil_space_t *fil_space_t::create(uint32_t id, uint32_t flags,
if (rotate) {
fil_system.default_encrypt_tables.push_back(*space);
space->is_in_default_encrypt = true;
- }
- mysql_mutex_unlock(&fil_system.mutex);
-
- if (rotate && srv_n_fil_crypt_threads_started) {
- fil_crypt_threads_signal();
+ if (srv_n_fil_crypt_threads_started) {
+ mysql_mutex_unlock(&fil_system.mutex);
+ fil_crypt_threads_signal();
+ mysql_mutex_lock(&fil_system.mutex);
+ }
}
return(space);
@@ -1315,9 +1321,9 @@ void fil_system_t::close()
void fil_system_t::add_opened_last_to_space_list(fil_space_t *space)
{
if (UNIV_LIKELY(space_list_last_opened != nullptr))
- space_list.insert(space_list_t::iterator(space_list_last_opened), *space);
+ space_list.insert(++space_list_t::iterator(space_list_last_opened), *space);
else
- space_list.push_back(*space);
+ space_list.push_front(*space);
space_list_last_opened= space;
}
@@ -1629,13 +1635,10 @@ pfs_os_file_t fil_delete_tablespace(uint32_t id)
mtr_t mtr;
mtr.start();
mtr.log_file_op(FILE_DELETE, id, space->chain.start->name);
- handle= space->chain.start->handle;
- mtr.commit_file(*space, nullptr);
-
+ mtr.commit_file(*space, nullptr, &handle);
fil_space_free_low(space);
}
- ibuf_delete_for_discarded_space(id);
return handle;
}
@@ -1945,16 +1948,20 @@ err_exit:
DBUG_EXECUTE_IF("checkpoint_after_file_create",
log_make_checkpoint(););
+ mysql_mutex_lock(&fil_system.mutex);
if (fil_space_t* space = fil_space_t::create(space_id, flags,
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));
mtr.start();
mtr.set_named_space(space);
ut_a(fsp_header_init(space, size, &mtr) == DB_SUCCESS);
mtr.commit();
return space;
+ } else {
+ mysql_mutex_unlock(&fil_system.mutex);
}
if (space_name.data()) {
@@ -2214,8 +2221,10 @@ skip_validate:
first_page)
: NULL;
+ mysql_mutex_lock(&fil_system.mutex);
space = fil_space_t::create(id, flags, purpose, crypt_data);
if (!space) {
+ mysql_mutex_unlock(&fil_system.mutex);
goto error;
}
@@ -2225,6 +2234,7 @@ skip_validate:
space->add(
df_remote.is_open() ? df_remote.filepath() :
df_default.filepath(), OS_FILE_CLOSED, 0, false, true);
+ mysql_mutex_unlock(&fil_system.mutex);
if (must_validate && !srv_read_only_mode) {
df_remote.close();
@@ -2510,10 +2520,13 @@ tablespace_check:
return FIL_LOAD_INVALID;
}
+ mysql_mutex_lock(&fil_system.mutex);
+
space = fil_space_t::create(
space_id, flags, FIL_TYPE_TABLESPACE, crypt_data);
if (space == NULL) {
+ mysql_mutex_unlock(&fil_system.mutex);
return(FIL_LOAD_INVALID);
}
@@ -2525,6 +2538,7 @@ tablespace_check:
let fil_node_open() do that task. */
space->add(file.filepath(), OS_FILE_CLOSED, 0, false, false);
+ mysql_mutex_unlock(&fil_system.mutex);
return(FIL_LOAD_OK);
}
diff --git a/storage/innobase/fsp/fsp0space.cc b/storage/innobase/fsp/fsp0space.cc
index 6bdf9fcc4d8..c2152b08590 100644
--- a/storage/innobase/fsp/fsp0space.cc
+++ b/storage/innobase/fsp/fsp0space.cc
@@ -88,25 +88,25 @@ Tablespace::open_or_create(bool is_temp)
ut_ad(!m_files.empty());
for (iterator it = begin(); it != end(); ++it) {
-
if (it->m_exists) {
err = it->open_or_create(
m_ignore_read_only
? false : srv_read_only_mode);
+ if (err != DB_SUCCESS) {
+ return err;
+ }
} else {
err = it->open_or_create(
m_ignore_read_only
? false : srv_read_only_mode);
- /* Set the correct open flags now that we have
- successfully created the file. */
- if (err == DB_SUCCESS) {
- file_found(*it);
+ if (err != DB_SUCCESS) {
+ return err;
}
- }
- if (err != DB_SUCCESS) {
- break;
+ /* Set the correct open flags now that we have
+ successfully created the file. */
+ file_found(*it);
}
/* We can close the handle now and open the tablespace
@@ -130,20 +130,22 @@ Tablespace::open_or_create(bool is_temp)
fsp_flags = FSP_FLAGS_PAGE_SSIZE();
}
+ mysql_mutex_lock(&fil_system.mutex);
space = fil_space_t::create(
m_space_id, fsp_flags,
is_temp
? FIL_TYPE_TEMPORARY : FIL_TYPE_TABLESPACE,
NULL);
if (!space) {
+ mysql_mutex_unlock(&fil_system.mutex);
return DB_ERROR;
}
+ } else {
+ mysql_mutex_lock(&fil_system.mutex);
}
-
- ut_a(fil_validate());
-
space->add(it->m_filepath, OS_FILE_CLOSED, it->m_size,
false, true);
+ mysql_mutex_unlock(&fil_system.mutex);
}
return(err);
diff --git a/storage/innobase/fsp/fsp0sysspace.cc b/storage/innobase/fsp/fsp0sysspace.cc
index 09eea04b655..9aef6f389ef 100644
--- a/storage/innobase/fsp/fsp0sysspace.cc
+++ b/storage/innobase/fsp/fsp0sysspace.cc
@@ -920,6 +920,7 @@ SysTablespace::open_or_create(
/* Close the curent handles, add space and file info to the
fil_system cache and the Data Dictionary, and re-open them
in file_system cache so that they stay open until shutdown. */
+ mysql_mutex_lock(&fil_system.mutex);
ulint node_counter = 0;
for (files_t::iterator it = begin; it != end; ++it) {
it->close();
@@ -933,7 +934,8 @@ SysTablespace::open_or_create(
FIL_TYPE_TEMPORARY, NULL);
ut_ad(space == fil_system.temp_space);
if (!space) {
- return DB_ERROR;
+ err = DB_ERROR;
+ break;
}
ut_ad(!space->is_compressed());
ut_ad(space->full_crc32());
@@ -944,12 +946,11 @@ SysTablespace::open_or_create(
FIL_TYPE_TABLESPACE, NULL);
ut_ad(space == fil_system.sys_space);
if (!space) {
- return DB_ERROR;
+ err = DB_ERROR;
+ break;
}
}
- ut_a(fil_validate());
-
uint32_t max_size = (++node_counter == m_files.size()
? (m_last_file_size_max == 0
? UINT32_MAX
@@ -960,6 +961,7 @@ SysTablespace::open_or_create(
it->m_type != SRV_NOT_RAW, true, max_size);
}
+ mysql_mutex_unlock(&fil_system.mutex);
return(err);
}
diff --git a/storage/innobase/fts/fts0fts.cc b/storage/innobase/fts/fts0fts.cc
index 8623e653991..d2b90542ec6 100644
--- a/storage/innobase/fts/fts0fts.cc
+++ b/storage/innobase/fts/fts0fts.cc
@@ -38,22 +38,6 @@ Full Text Search interface
#include "dict0stats.h"
#include "btr0pcur.h"
-/** The SYNC state of the cache. There is one instance of this struct
-associated with each ADD thread. */
-struct fts_sync_t {
- /** Transaction used for SYNCing the cache to disk */
- trx_t *trx;
- /** Table with FTS index(es) */
- dict_table_t *table;
- /** Max size in bytes of the cache */
- ulint max_cache_size;
- /** The doc id at which the cache was noted as being
- full, we use this to set the upper_limit field */
- doc_id_t max_doc_id;
- /** SYNC start time; only used if fts_enable_diag_print */
- time_t start_time;
-};
-
static const ulint FTS_MAX_ID_LEN = 32;
/** Column name from the FTS config table */
@@ -201,8 +185,15 @@ struct fts_tokenize_param_t {
/** Run SYNC on the table, i.e., write out data from the cache to the
FTS auxiliary INDEX table and clear the cache at the end.
@param[in,out] sync sync state
+@param[in] unlock_cache whether unlock cache lock when write node
+@param[in] wait whether wait when a sync is in progress
@return DB_SUCCESS if all OK */
-static dberr_t fts_sync(fts_sync_t *sync);
+static
+dberr_t
+fts_sync(
+ fts_sync_t* sync,
+ bool unlock_cache,
+ bool wait);
/****************************************************************//**
Release all resources help by the words rb tree e.g., the node ilist. */
@@ -275,6 +266,7 @@ fts_cache_destroy(fts_cache_t* cache)
mysql_mutex_destroy(&cache->init_lock);
mysql_mutex_destroy(&cache->deleted_lock);
mysql_mutex_destroy(&cache->doc_id_lock);
+ pthread_cond_destroy(&cache->sync->cond);
if (cache->stopword_info.cached_stopword) {
rbt_free(cache->stopword_info.cached_stopword);
@@ -574,6 +566,7 @@ fts_index_cache_init(
for (i = 0; i < FTS_NUM_AUX_INDEX; ++i) {
ut_a(index_cache->ins_graph[i] == NULL);
+ ut_a(index_cache->sel_graph[i] == NULL);
}
}
@@ -643,6 +636,7 @@ fts_cache_create(
mem_heap_zalloc(heap, sizeof(fts_sync_t)));
cache->sync->table = table;
+ pthread_cond_init(&cache->sync->cond, nullptr);
/* Create the index cache vector that will hold the inverted indexes. */
cache->indexes = ib_vector_create(
@@ -968,6 +962,10 @@ fts_cache_index_cache_create(
mem_heap_zalloc(static_cast<mem_heap_t*>(
cache->self_heap->arg), n_bytes));
+ index_cache->sel_graph = static_cast<que_t**>(
+ mem_heap_zalloc(static_cast<mem_heap_t*>(
+ cache->self_heap->arg), n_bytes));
+
fts_index_cache_init(cache->sync_heap, index_cache);
if (cache->get_docs) {
@@ -1041,6 +1039,13 @@ fts_cache_clear(
index_cache->ins_graph[j] = NULL;
}
+
+ if (index_cache->sel_graph[j] != NULL) {
+
+ que_graph_free(index_cache->sel_graph[j]);
+
+ index_cache->sel_graph[j] = NULL;
+ }
}
index_cache->doc_stats = NULL;
@@ -1333,7 +1338,8 @@ fts_cache_add_doc(
ib_vector_last(word->nodes));
}
- if (!fts_node || fts_node->ilist_size > FTS_ILIST_MAX_SIZE
+ if (fts_node == NULL || fts_node->synced
+ || fts_node->ilist_size > FTS_ILIST_MAX_SIZE
|| doc_id < fts_node->last_doc_id) {
fts_node = static_cast<fts_node_t*>(
@@ -3320,7 +3326,7 @@ fts_add_doc_from_tuple(
if (cache->total_size > fts_max_cache_size / 5
|| fts_need_sync) {
- fts_sync(cache->sync);
+ fts_sync(cache->sync, true, false);
}
mtr_start(&mtr);
@@ -3356,7 +3362,7 @@ fts_add_doc_by_id(
dict_index_t* fts_id_index;
ibool is_id_cluster;
fts_cache_t* cache = ftt->table->fts->cache;
- bool need_sync= false;
+
ut_ad(cache->get_docs);
/* If Doc ID has been supplied by the user, then the table
@@ -3496,32 +3502,44 @@ fts_add_doc_by_id(
get_doc->index_cache,
doc_id, doc.tokens);
- /** FTS cache sync should happen
- frequently. Because user thread
- shouldn't hold the cache lock for
- longer time. So cache should sync
- whenever cache size exceeds 512 KB */
- need_sync =
- cache->total_size > 512*1024;
+ bool need_sync = !cache->sync->in_progress
+ && (fts_need_sync
+ || (cache->total_size
+ - cache->total_size_at_sync)
+ > fts_max_cache_size / 10);
+ if (need_sync) {
+ cache->total_size_at_sync =
+ cache->total_size;
+ }
mysql_mutex_unlock(&table->fts->cache->lock);
DBUG_EXECUTE_IF(
"fts_instrument_sync",
- fts_sync_table(table);
+ fts_optimize_request_sync_table(table);
+ mysql_mutex_lock(&cache->lock);
+ if (cache->sync->in_progress)
+ my_cond_wait(
+ &cache->sync->cond,
+ &cache->lock.m_mutex);
+ mysql_mutex_unlock(&cache->lock);
);
DBUG_EXECUTE_IF(
"fts_instrument_sync_debug",
- fts_sync(cache->sync);
+ fts_sync(cache->sync, true, true);
);
DEBUG_SYNC_C("fts_instrument_sync_request");
DBUG_EXECUTE_IF(
"fts_instrument_sync_request",
- need_sync= true;
+ fts_optimize_request_sync_table(table);
);
+ if (need_sync) {
+ fts_optimize_request_sync_table(table);
+ }
+
mtr_start(&mtr);
if (i < num_idx - 1) {
@@ -3547,10 +3565,6 @@ func_exit:
ut_free(pcur.old_rec_buf);
mem_heap_free(heap);
-
- if (need_sync) {
- fts_sync_table(table);
- }
}
@@ -3910,13 +3924,15 @@ static MY_ATTRIBUTE((nonnull, warn_unused_result))
dberr_t
fts_sync_write_words(
trx_t* trx,
- fts_index_cache_t* index_cache)
+ fts_index_cache_t* index_cache,
+ bool unlock_cache)
{
fts_table_t fts_table;
ulint n_nodes = 0;
ulint n_words = 0;
const ib_rbt_node_t* rbt_node;
dberr_t error = DB_SUCCESS;
+ ibool print_error = FALSE;
dict_table_t* table = index_cache->index->table;
FTS_INIT_INDEX_TABLE(
@@ -3947,36 +3963,53 @@ fts_sync_write_words(
fts_table.suffix = fts_get_suffix(selected);
+ /* We iterate over all the nodes even if there was an error */
for (i = 0; i < ib_vector_size(word->nodes); ++i) {
fts_node_t* fts_node = static_cast<fts_node_t*>(
ib_vector_get(word->nodes, i));
- error = fts_write_node(
- trx, &index_cache->ins_graph[selected],
- &fts_table, &word->text, fts_node);
+ if (fts_node->synced) {
+ continue;
+ } else {
+ fts_node->synced = true;
+ }
+
+ /*FIXME: we need to handle the error properly. */
+ if (error == DB_SUCCESS) {
+ if (unlock_cache) {
+ mysql_mutex_unlock(
+ &table->fts->cache->lock);
+ }
+
+ error = fts_write_node(
+ trx,
+ &index_cache->ins_graph[selected],
+ &fts_table, &word->text, fts_node);
- DEBUG_SYNC_C("fts_write_node");
- DBUG_EXECUTE_IF("fts_write_node_crash",
+ DEBUG_SYNC_C("fts_write_node");
+ DBUG_EXECUTE_IF("fts_write_node_crash",
DBUG_SUICIDE(););
- DBUG_EXECUTE_IF("fts_instrument_sync_sleep",
+ DBUG_EXECUTE_IF(
+ "fts_instrument_sync_sleep",
std::this_thread::sleep_for(
std::chrono::seconds(1)););
- if (error != DB_SUCCESS) {
- goto err_exit;
+ if (unlock_cache) {
+ mysql_mutex_lock(
+ &table->fts->cache->lock);
+ }
}
}
n_nodes += ib_vector_size(word->nodes);
- if (UNIV_UNLIKELY(error != DB_SUCCESS)) {
-err_exit:
+ if (UNIV_UNLIKELY(error != DB_SUCCESS) && !print_error) {
ib::error() << "(" << error << ") writing"
" word node to FTS auxiliary index table "
<< table->name;
- break;
+ print_error = TRUE;
}
}
@@ -4035,44 +4068,58 @@ fts_sync_index(
ut_ad(rbt_validate(index_cache->words));
- return(fts_sync_write_words(trx, index_cache));
+ return(fts_sync_write_words(trx, index_cache, sync->unlock_cache));
}
-/** Rollback a sync operation
-@param[in,out] sync sync state */
+/** Check if index cache has been synced completely
+@param[in,out] index_cache index cache
+@return true if index is synced, otherwise false. */
static
-void
-fts_sync_rollback(
- fts_sync_t* sync)
+bool
+fts_sync_index_check(
+ fts_index_cache_t* index_cache)
{
- trx_t* trx = sync->trx;
- fts_cache_t* cache = sync->table->fts->cache;
-
- for (ulint i = 0; i < ib_vector_size(cache->indexes); ++i) {
- ulint j;
- fts_index_cache_t* index_cache;
-
- index_cache = static_cast<fts_index_cache_t*>(
- ib_vector_get(cache->indexes, i));
+ const ib_rbt_node_t* rbt_node;
- for (j = 0; fts_index_selector[j].value; ++j) {
+ for (rbt_node = rbt_first(index_cache->words);
+ rbt_node != NULL;
+ rbt_node = rbt_next(index_cache->words, rbt_node)) {
- if (index_cache->ins_graph[j] != NULL) {
+ fts_tokenizer_word_t* word;
+ word = rbt_value(fts_tokenizer_word_t, rbt_node);
- que_graph_free(index_cache->ins_graph[j]);
+ fts_node_t* fts_node;
+ fts_node = static_cast<fts_node_t*>(ib_vector_last(word->nodes));
- index_cache->ins_graph[j] = NULL;
- }
+ if (!fts_node->synced) {
+ return(false);
}
}
- mysql_mutex_unlock(&cache->lock);
+ return(true);
+}
- fts_sql_rollback(trx);
+/** Reset synced flag in index cache when rollback
+@param[in,out] index_cache index cache */
+static
+void
+fts_sync_index_reset(
+ fts_index_cache_t* index_cache)
+{
+ const ib_rbt_node_t* rbt_node;
- /* Avoid assertion in trx_t::free(). */
- trx->dict_operation_lock_mode = false;
- trx->free();
+ for (rbt_node = rbt_first(index_cache->words);
+ rbt_node != NULL;
+ rbt_node = rbt_next(index_cache->words, rbt_node)) {
+
+ fts_tokenizer_word_t* word;
+ word = rbt_value(fts_tokenizer_word_t, rbt_node);
+
+ fts_node_t* fts_node;
+ fts_node = static_cast<fts_node_t*>(ib_vector_last(word->nodes));
+
+ fts_node->synced = false;
+ }
}
/** Commit the SYNC, change state of processed doc ids etc.
@@ -4105,20 +4152,19 @@ fts_sync_commit(
sync, cache->deleted_doc_ids);
}
+ /* We need to do this within the deleted lock since fts_delete() can
+ attempt to add a deleted doc id to the cache deleted id array. */
+ fts_cache_clear(cache);
+ DEBUG_SYNC_C("fts_deleted_doc_ids_clear");
+ fts_cache_init(cache);
+ mysql_mutex_unlock(&cache->lock);
+
if (UNIV_LIKELY(error == DB_SUCCESS)) {
- /* We need to do this within the deleted lock
- since fts_delete() can attempt to add a deleted
- doc id to the cache deleted id array. */
- fts_cache_clear(cache);
- DEBUG_SYNC_C("fts_deleted_doc_ids_clear");
- fts_cache_init(cache);
- mysql_mutex_unlock(&cache->lock);
fts_sql_commit(trx);
} else {
+ fts_sql_rollback(trx);
ib::error() << "(" << error << ") during SYNC of "
"table " << sync->table->name;
- fts_sync_rollback(sync);
- return error;
}
if (UNIV_UNLIKELY(fts_enable_diag_print) && elapsed_time) {
@@ -4138,13 +4184,66 @@ fts_sync_commit(
return(error);
}
+/** Rollback a sync operation
+@param[in,out] sync sync state */
+static
+void
+fts_sync_rollback(
+ fts_sync_t* sync)
+{
+ trx_t* trx = sync->trx;
+ fts_cache_t* cache = sync->table->fts->cache;
+
+ for (ulint i = 0; i < ib_vector_size(cache->indexes); ++i) {
+ ulint j;
+ fts_index_cache_t* index_cache;
+
+ index_cache = static_cast<fts_index_cache_t*>(
+ ib_vector_get(cache->indexes, i));
+
+ /* Reset synced flag so nodes will not be skipped
+ in the next sync, see fts_sync_write_words(). */
+ fts_sync_index_reset(index_cache);
+
+ for (j = 0; fts_index_selector[j].value; ++j) {
+
+ if (index_cache->ins_graph[j] != NULL) {
+
+ que_graph_free(index_cache->ins_graph[j]);
+
+ index_cache->ins_graph[j] = NULL;
+ }
+
+ if (index_cache->sel_graph[j] != NULL) {
+
+ que_graph_free(index_cache->sel_graph[j]);
+
+ index_cache->sel_graph[j] = NULL;
+ }
+ }
+ }
+
+ mysql_mutex_unlock(&cache->lock);
+
+ fts_sql_rollback(trx);
+
+ /* Avoid assertion in trx_t::free(). */
+ trx->dict_operation_lock_mode = false;
+ trx->free();
+}
+
/** Run SYNC on the table, i.e., write out data from the cache to the
FTS auxiliary INDEX table and clear the cache at the end.
@param[in,out] sync sync state
@param[in] unlock_cache whether unlock cache lock when write node
@param[in] wait whether wait when a sync is in progress
@return DB_SUCCESS if all OK */
-static dberr_t fts_sync(fts_sync_t *sync)
+static
+dberr_t
+fts_sync(
+ fts_sync_t* sync,
+ bool unlock_cache,
+ bool wait)
{
if (srv_read_only_mode) {
return DB_READ_ONLY;
@@ -4155,13 +4254,33 @@ static dberr_t fts_sync(fts_sync_t *sync)
fts_cache_t* cache = sync->table->fts->cache;
mysql_mutex_lock(&cache->lock);
+
+ /* Check if cache is being synced.
+ Note: we release cache lock in fts_sync_write_words() to
+ avoid long wait for the lock by other threads. */
+ if (sync->in_progress) {
+ if (!wait) {
+ mysql_mutex_unlock(&cache->lock);
+ return(DB_SUCCESS);
+ }
+ do {
+ my_cond_wait(&sync->cond, &cache->lock.m_mutex);
+ } while (sync->in_progress);
+ }
+
+ sync->unlock_cache = unlock_cache;
+ sync->in_progress = true;
+
DEBUG_SYNC_C("fts_sync_begin");
fts_sync_begin(sync);
+begin_sync:
const size_t fts_cache_size= fts_max_cache_size;
if (cache->total_size > fts_cache_size) {
/* Avoid the case: sync never finish when
insert/update keeps comming. */
+ ut_ad(sync->unlock_cache);
+ sync->unlock_cache = false;
ib::warn() << "Total InnoDB FTS size "
<< cache->total_size << " for the table "
<< cache->sync->table->name
@@ -4185,23 +4304,52 @@ static dberr_t fts_sync(fts_sync_t *sync)
error = fts_sync_index(sync, index_cache);
if (error != DB_SUCCESS) {
- goto err_exit;
+ goto end_sync;
+ }
+
+ if (!sync->unlock_cache
+ && cache->total_size < fts_max_cache_size) {
+ /* Reset the unlock cache if the value
+ is less than innodb_ft_cache_size */
+ sync->unlock_cache = true;
}
}
DBUG_EXECUTE_IF("fts_instrument_sync_interrupted",
+ sync->interrupted = true;
error = DB_INTERRUPTED;
- goto err_exit;
+ goto end_sync;
);
- if (error == DB_SUCCESS) {
+ /* Make sure all the caches are synced. */
+ for (i = 0; i < ib_vector_size(cache->indexes); ++i) {
+ fts_index_cache_t* index_cache;
+
+ index_cache = static_cast<fts_index_cache_t*>(
+ ib_vector_get(cache->indexes, i));
+
+ if (index_cache->index->to_be_dropped
+ || fts_sync_index_check(index_cache)) {
+ continue;
+ }
+
+ goto begin_sync;
+ }
+
+end_sync:
+ if (error == DB_SUCCESS && !sync->interrupted) {
error = fts_sync_commit(sync);
} else {
-err_exit:
fts_sync_rollback(sync);
- return error;
}
+ mysql_mutex_lock(&cache->lock);
+ ut_ad(sync->in_progress);
+ sync->interrupted = false;
+ sync->in_progress = false;
+ pthread_cond_broadcast(&sync->cond);
+ mysql_mutex_unlock(&cache->lock);
+
/* We need to check whether an optimize is required, for that
we make copies of the two variables that control the trigger. These
variables can change behind our back and we don't want to hold the
@@ -4213,7 +4361,6 @@ err_exit:
mysql_mutex_unlock(&cache->deleted_lock);
- DEBUG_SYNC_C("fts_sync_end");
return(error);
}
@@ -4222,12 +4369,12 @@ FTS auxiliary INDEX table and clear the cache at the end.
@param[in,out] table fts table
@param[in] wait whether wait for existing sync to finish
@return DB_SUCCESS on success, error code on failure. */
-dberr_t fts_sync_table(dict_table_t* table)
+dberr_t fts_sync_table(dict_table_t* table, bool wait)
{
ut_ad(table->fts);
return table->space && !table->corrupted && table->fts->cache
- ? fts_sync(table->fts->cache->sync)
+ ? fts_sync(table->fts->cache->sync, !wait, wait)
: DB_SUCCESS;
}
diff --git a/storage/innobase/fts/fts0opt.cc b/storage/innobase/fts/fts0opt.cc
index 7c40a25e6e7..fe31767d901 100644
--- a/storage/innobase/fts/fts0opt.cc
+++ b/storage/innobase/fts/fts0opt.cc
@@ -83,8 +83,9 @@ enum fts_msg_type_t {
FTS_MSG_ADD_TABLE, /*!< Add table to the optimize thread's
work queue */
- FTS_MSG_DEL_TABLE /*!< Remove a table from the optimize
+ FTS_MSG_DEL_TABLE, /*!< Remove a table from the optimize
threads work queue */
+ FTS_MSG_SYNC_TABLE /*!< Sync fts cache of a table */
};
/** Compressed list of words that have been read from FTS INDEX
@@ -2624,6 +2625,36 @@ fts_optimize_remove_table(
mysql_mutex_unlock(&fts_optimize_wq->mutex);
}
+/** Send sync fts cache for the table.
+@param[in] table table to sync */
+void
+fts_optimize_request_sync_table(
+ dict_table_t* table)
+{
+ /* if the optimize system not yet initialized, return */
+ if (!fts_optimize_wq) {
+ return;
+ }
+
+ mysql_mutex_lock(&fts_optimize_wq->mutex);
+
+ /* FTS optimizer thread is already exited */
+ if (fts_opt_start_shutdown) {
+ ib::info() << "Try to sync table " << table->name
+ << " after FTS optimize thread exiting.";
+ } else if (table->fts->sync_message) {
+ /* If the table already has SYNC message in
+ fts_optimize_wq queue then ignore it */
+ } else {
+ add_msg(fts_optimize_create_msg(FTS_MSG_SYNC_TABLE, table));
+ table->fts->sync_message = true;
+ DBUG_EXECUTE_IF("fts_optimize_wq_count_check",
+ DBUG_ASSERT(fts_optimize_wq->length <= 1000););
+ }
+
+ mysql_mutex_unlock(&fts_optimize_wq->mutex);
+}
+
/** Add a table to fts_slots if it doesn't already exist. */
static bool fts_optimize_new_table(dict_table_t* table)
{
@@ -2765,8 +2796,7 @@ static void fts_optimize_sync_table(dict_table_t *table,
if (sync_table->fts && sync_table->fts->cache && sync_table->is_accessible())
{
- fts_sync_table(sync_table);
-
+ fts_sync_table(sync_table, false);
if (process_message)
{
mysql_mutex_lock(&fts_optimize_wq->mutex);
@@ -2866,6 +2896,24 @@ retry_later:
--n_tables;
}
break;
+
+ case FTS_MSG_SYNC_TABLE:
+ if (UNIV_UNLIKELY(wsrep_sst_disable_writes)) {
+ add_msg(msg);
+ goto retry_later;
+ }
+
+ DBUG_EXECUTE_IF(
+ "fts_instrument_msg_sync_sleep",
+ std::this_thread::sleep_for(
+ std::chrono::milliseconds(
+ 300)););
+
+ fts_optimize_sync_table(
+ static_cast<dict_table_t*>(msg->ptr),
+ true);
+ break;
+
default:
ut_error;
}
@@ -2998,7 +3046,7 @@ void fts_sync_during_ddl(dict_table_t* table)
if (!sync_message)
return;
- fts_sync_table(table);
+ fts_sync_table(table, false);
mysql_mutex_lock(&fts_optimize_wq->mutex);
table->fts->sync_message = false;
diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc
index cc5e4197703..342c51d2fe5 100644
--- a/storage/innobase/handler/ha_innodb.cc
+++ b/storage/innobase/handler/ha_innodb.cc
@@ -1518,6 +1518,7 @@ static void innodb_drop_database(handlerton*, char *path)
dfield_set_data(&dfield, namebuf, len);
dict_index_copy_types(&tuple, sys_index, 1);
std::vector<pfs_os_file_t> to_close;
+ std::vector<uint32_t> space_ids;
mtr_t mtr;
mtr.start();
pcur.btr_cur.page_cur.index = sys_index;
@@ -1561,6 +1562,7 @@ static void innodb_drop_database(handlerton*, char *path)
ut_ad("corrupted SYS_TABLES.SPACE" == 0);
else if (uint32_t space_id= mach_read_from_4(s))
{
+ space_ids.emplace_back(space_id);
pfs_os_file_t detached= fil_delete_tablespace(space_id);
if (detached != OS_FILE_CLOSED)
to_close.emplace_back(detached);
@@ -1570,6 +1572,9 @@ static void innodb_drop_database(handlerton*, char *path)
mtr.commit();
for (pfs_os_file_t detached : to_close)
os_file_close(detached);
+ for (const auto id : space_ids)
+ ibuf_delete_for_discarded_space(id);
+
/* Any changes must be persisted before we return. */
log_write_up_to(mtr.commit_lsn(), true);
}
diff --git a/storage/innobase/handler/handler0alter.cc b/storage/innobase/handler/handler0alter.cc
index 5825b4ae78a..20decb897ba 100644
--- a/storage/innobase/handler/handler0alter.cc
+++ b/storage/innobase/handler/handler0alter.cc
@@ -11604,8 +11604,12 @@ foreign_fail:
ut_d(dict_table_check_for_dup_indexes(
ctx->new_table, CHECK_ABORTED_OK));
- ut_ad(!ctx->new_table->fts
- || fts_check_cached_index(ctx->new_table));
+#ifdef UNIV_DEBUG
+ if (!(ctx->new_table->fts != NULL
+ && ctx->new_table->fts->cache->sync->in_progress)) {
+ ut_a(fts_check_cached_index(ctx->new_table));
+ }
+#endif
}
unlock_and_close_files(deleted, trx);
diff --git a/storage/innobase/ibuf/ibuf0ibuf.cc b/storage/innobase/ibuf/ibuf0ibuf.cc
index e988a685678..13039e4d07f 100644
--- a/storage/innobase/ibuf/ibuf0ibuf.cc
+++ b/storage/innobase/ibuf/ibuf0ibuf.cc
@@ -3495,6 +3495,10 @@ ibuf_insert(
ulint zip_size,
que_thr_t* thr)
{
+ if (!index->is_committed()) {
+ return false;
+ }
+
dberr_t err;
ulint entry_size;
ibool no_counter;
diff --git a/storage/innobase/include/buf0dblwr.h b/storage/innobase/include/buf0dblwr.h
index 92b840d2f4c..9932b0e521f 100644
--- a/storage/innobase/include/buf0dblwr.h
+++ b/storage/innobase/include/buf0dblwr.h
@@ -66,8 +66,6 @@ class buf_dblwr_t
bool batch_running;
/** number of expected flush_buffered_writes_completed() calls */
unsigned flushing_buffered_writes;
- /** pages submitted to flush_buffered_writes() */
- ulint pages_submitted;
/** number of flush_buffered_writes_completed() calls */
ulint writes_completed;
/** number of pages written by flush_buffered_writes_completed() */
@@ -94,9 +92,6 @@ public:
/** Acquire the mutex */
void lock() { mysql_mutex_lock(&mutex); }
- /** @return the number of submitted page writes */
- ulint submitted() const
- { mysql_mutex_assert_owner(&mutex); return pages_submitted; }
/** @return the number of completed batches */
ulint batches() const
{ mysql_mutex_assert_owner(&mutex); return writes_completed; }
diff --git a/storage/innobase/include/fil0fil.h b/storage/innobase/include/fil0fil.h
index 008cb3d1261..73e80d77b56 100644
--- a/storage/innobase/include/fil0fil.h
+++ b/storage/innobase/include/fil0fil.h
@@ -1482,6 +1482,7 @@ public:
if (space_list_last_opened == space)
{
+ ut_ad(s != space_list.begin());
space_list_t::iterator prev= s;
space_list_last_opened= &*--prev;
}
diff --git a/storage/innobase/include/fts0fts.h b/storage/innobase/include/fts0fts.h
index 720fe7f25b9..c0151b44063 100644
--- a/storage/innobase/include/fts0fts.h
+++ b/storage/innobase/include/fts0fts.h
@@ -648,6 +648,12 @@ fts_optimize_remove_table(
void
fts_optimize_shutdown();
+/** Send sync fts cache for the table.
+@param[in] table table to sync */
+void
+fts_optimize_request_sync_table(
+ dict_table_t* table);
+
/**********************************************************************//**
Take a FTS savepoint. */
void
@@ -702,8 +708,9 @@ fts_savepoint_rollback_last_stmt(
/** Run SYNC on the table, i.e., write out data from the cache to the
FTS auxiliary INDEX table and clear the cache at the end.
@param[in,out] table fts table
+@param[in] wait whether to wait for existing sync to finish
@return DB_SUCCESS on success, error code on failure. */
-dberr_t fts_sync_table(dict_table_t* table);
+dberr_t fts_sync_table(dict_table_t* table, bool wait = true);
/****************************************************************//**
Create an FTS index cache. */
diff --git a/storage/innobase/include/fts0types.h b/storage/innobase/include/fts0types.h
index 04e99d595c5..fb278d543c4 100644
--- a/storage/innobase/include/fts0types.h
+++ b/storage/innobase/include/fts0types.h
@@ -75,6 +75,7 @@ struct fts_index_cache_t {
que_t** ins_graph; /*!< Insert query graphs */
+ que_t** sel_graph; /*!< Select query graphs */
CHARSET_INFO* charset; /*!< charset */
};
@@ -86,7 +87,35 @@ struct fts_stopword_t {
CHARSET_INFO* charset; /*!< charset for stopword */
};
-struct fts_sync_t;
+/** The SYNC state of the cache. There is one instance of this struct
+associated with each ADD thread. */
+struct fts_sync_t {
+ trx_t* trx; /*!< The transaction used for SYNCing
+ the cache to disk */
+ dict_table_t* table; /*!< Table with FTS index(es) */
+ ulint max_cache_size; /*!< Max size in bytes of the cache */
+ ibool cache_full; /*!< flag, when true it indicates that
+ we need to sync the cache to disk */
+ ulint lower_index; /*!< the start index of the doc id
+ vector from where to start adding
+ documents to the FTS cache */
+ ulint upper_index; /*!< max index of the doc id vector to
+ add to the FTS cache */
+ ibool interrupted; /*!< TRUE if SYNC was interrupted */
+ doc_id_t min_doc_id; /*!< The smallest doc id added to the
+ cache. It should equal to
+ doc_ids[lower_index] */
+ doc_id_t max_doc_id; /*!< The doc id at which the cache was
+ noted as being full, we use this to
+ set the upper_limit field */
+ time_t start_time; /*!< SYNC start time; only used if
+ fts_enable_diag_print */
+ bool in_progress; /*!< flag whether sync is in progress.*/
+ bool unlock_cache; /*!< flag whether unlock cache when
+ write fts node */
+ /** condition variable for in_progress; used with table->fts->cache->lock */
+ pthread_cond_t cond;
+};
/** The cache for the FTS system. It is a memory-based inverted index
that new entries are added to, until it grows over the configured maximum
@@ -175,6 +204,7 @@ struct fts_node_t {
ulint ilist_size_alloc;
/*!< Allocated size of ilist in
bytes */
+ bool synced; /*!< flag whether the node is synced */
};
/** A tokenizer word. Contains information about one word. */
diff --git a/storage/innobase/include/log0log.h b/storage/innobase/include/log0log.h
index 58a2ee3468c..c77f13b9f5c 100644
--- a/storage/innobase/include/log0log.h
+++ b/storage/innobase/include/log0log.h
@@ -514,11 +514,10 @@ public:
/** Redo log system */
extern log_t log_sys;
-inline void log_free_check()
-{
- if (log_sys.check_flush_or_checkpoint())
- log_check_margins();
-}
+/** Wait for a log checkpoint if needed.
+NOTE that this function may only be called while not holding
+any synchronization objects except dict_sys.latch. */
+void log_free_check();
/** Release the latches that protect log resizing. */
void log_resize_release();
diff --git a/storage/innobase/include/mtr0mtr.h b/storage/innobase/include/mtr0mtr.h
index b8df6d9f63e..4d7b77b4638 100644
--- a/storage/innobase/include/mtr0mtr.h
+++ b/storage/innobase/include/mtr0mtr.h
@@ -93,10 +93,16 @@ struct mtr_t {
ATTRIBUTE_COLD void commit_shrink(fil_space_t &space);
/** Commit a mini-transaction that is deleting or renaming a file.
- @param space tablespace that is being renamed or deleted
- @param name new file name (nullptr=the file will be deleted)
+ @param space tablespace that is being renamed or deleted
+ @param name new file name (nullptr=the file will be deleted)
+ @param detached_handle if detached_handle != nullptr and if space is detached
+ during the function execution the file handle if its
+ node will be set to OS_FILE_CLOSED, and the previous
+ value of the file handle will be assigned to the
+ address, pointed by detached_handle.
@return whether the operation succeeded */
- ATTRIBUTE_COLD bool commit_file(fil_space_t &space, const char *name);
+ ATTRIBUTE_COLD bool commit_file(fil_space_t &space, const char *name,
+ pfs_os_file_t *detached_handle= nullptr);
/** Commit a mini-transaction that did not modify any pages,
but generated some redo log on a higher level, such as
@@ -335,7 +341,7 @@ public:
{
mtr_memo_slot_t &slot= m_memo[savepoint];
ut_ad(slot.type <= MTR_MEMO_BUF_FIX);
- ut_ad(type <= MTR_MEMO_BUF_FIX);
+ ut_ad(type < MTR_MEMO_S_LOCK);
slot.type= type;
}
diff --git a/storage/innobase/include/os0file.h b/storage/innobase/include/os0file.h
index 764071cfab9..13f9d3de3f8 100644
--- a/storage/innobase/include/os0file.h
+++ b/storage/innobase/include/os0file.h
@@ -1056,11 +1056,13 @@ size_t os_aio_pending_reads_approx();
/** @return number of pending writes */
size_t os_aio_pending_writes();
-/** Wait until there are no pending asynchronous writes. */
-void os_aio_wait_until_no_pending_writes();
+/** Wait until there are no pending asynchronous writes.
+@param declare whether the wait will be declared in tpool */
+void os_aio_wait_until_no_pending_writes(bool declare);
-/** Wait until all pending asynchronous reads have completed. */
-void os_aio_wait_until_no_pending_reads();
+/** Wait until all pending asynchronous reads have completed.
+@param declare whether the wait will be declared in tpool */
+void os_aio_wait_until_no_pending_reads(bool declare);
/** Prints info of the aio arrays.
@param[in/out] file file where to print */
diff --git a/storage/innobase/include/trx0rseg.h b/storage/innobase/include/trx0rseg.h
index 6e0eb547eb8..43e0c290d63 100644
--- a/storage/innobase/include/trx0rseg.h
+++ b/storage/innobase/include/trx0rseg.h
@@ -129,7 +129,8 @@ public:
#endif
}
/** @return whether the segment is marked for undo truncation */
- bool skip_allocation() const { return ref_load() & SKIP; }
+ bool skip_allocation() const
+ { return ref.load(std::memory_order_acquire) & SKIP; }
/** Increment the reference count */
void acquire()
{ ut_d(auto r=) ref.fetch_add(REF); ut_ad(!(r & SKIP)); }
diff --git a/storage/innobase/include/trx0undo.h b/storage/innobase/include/trx0undo.h
index 3474a903f6c..670fe00c25b 100644
--- a/storage/innobase/include/trx0undo.h
+++ b/storage/innobase/include/trx0undo.h
@@ -203,16 +203,18 @@ trx_undo_assign(trx_t* trx, dberr_t* err, mtr_t* mtr)
MY_ATTRIBUTE((nonnull));
/** Assign an undo log for a transaction.
A new undo log is created or a cached undo log reused.
+@tparam is_temp whether this is temporary undo log
@param[in,out] trx transaction
@param[in] rseg rollback segment
@param[out] undo the undo log
-@param[out] err error code
@param[in,out] mtr mini-transaction
+@param[out] err error code
@return the undo log block
-@retval NULL on error */
+@retval nullptr on error */
+template<bool is_temp>
buf_block_t*
-trx_undo_assign_low(trx_t* trx, trx_rseg_t* rseg, trx_undo_t** undo,
- dberr_t* err, mtr_t* mtr)
+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.
diff --git a/storage/innobase/log/log0log.cc b/storage/innobase/log/log0log.cc
index ff5508d489d..3e4c7029db4 100644
--- a/storage/innobase/log/log0log.cc
+++ b/storage/innobase/log/log0log.cc
@@ -1044,6 +1044,16 @@ ATTRIBUTE_COLD void log_check_margins()
while (log_sys.check_flush_or_checkpoint());
}
+/** Wait for a log checkpoint if needed.
+NOTE that this function may only be called while not holding
+any synchronization objects except dict_sys.latch. */
+void log_free_check()
+{
+ ut_ad(!lock_sys.is_writer());
+ if (log_sys.check_flush_or_checkpoint())
+ log_check_margins();
+}
+
extern void buf_resize_shutdown();
/** Make a checkpoint at the latest lsn on shutdown. */
diff --git a/storage/innobase/log/log0recv.cc b/storage/innobase/log/log0recv.cc
index 88b6bae963f..37a496725fc 100644
--- a/storage/innobase/log/log0recv.cc
+++ b/storage/innobase/log/log0recv.cc
@@ -775,9 +775,10 @@ retry:
if (!os_file_status(name->c_str(), &exists, &ftype) || !exists)
goto processed;
}
- create(it, *name, static_cast<uint32_t>
- (1U << FSP_FLAGS_FCRC32_POS_MARKER |
- FSP_FLAGS_FCRC32_PAGE_SSIZE()), nullptr, 0);
+ if (create(it, *name, static_cast<uint32_t>
+ (1U << FSP_FLAGS_FCRC32_POS_MARKER |
+ FSP_FLAGS_FCRC32_PAGE_SSIZE()), nullptr, 0))
+ mysql_mutex_unlock(&fil_system.mutex);
}
}
else
@@ -806,7 +807,7 @@ processed:
@param flags FSP_SPACE_FLAGS
@param crypt_data encryption metadata
@param size tablespace size in pages
- @return tablespace
+ @return tablespace; the caller must release fil_system.mutex
@retval nullptr if crypt_data is invalid */
static fil_space_t *create(const recv_spaces_t::const_iterator &it,
const std::string &name, uint32_t flags,
@@ -818,6 +819,7 @@ processed:
ut_free(crypt_data);
return nullptr;
}
+ mysql_mutex_lock(&fil_system.mutex);
fil_space_t *space= fil_space_t::create(it->first, flags,
FIL_TYPE_TABLESPACE, crypt_data);
ut_ad(space);
@@ -878,12 +880,13 @@ processed:
space->free_limit= fsp_header_get_field(page, FSP_FREE_LIMIT);
space->free_len= flst_get_len(FSP_HEADER_OFFSET + FSP_FREE + page);
fil_node_t *node= UT_LIST_GET_FIRST(space->chain);
+ mysql_mutex_unlock(&fil_system.mutex);
if (!space->acquire())
- {
+ {
free_space:
fil_space_free(it->first, false);
goto next_item;
- }
+ }
if (os_file_write(IORequestWrite, node->name, node->handle,
page, 0, fil_space_t::physical_size(flags)) !=
DB_SUCCESS)
@@ -953,6 +956,7 @@ bool recv_sys_t::recover_deferred(recv_sys_t::map::iterator &p,
space->free_len= flst_get_len(FSP_HEADER_OFFSET + FSP_FREE + page);
fil_node_t *node= UT_LIST_GET_FIRST(space->chain);
node->deferred= true;
+ mysql_mutex_unlock(&fil_system.mutex);
if (!space->acquire())
goto release_and_fail;
fil_names_dirty(space);
@@ -976,8 +980,10 @@ bool recv_sys_t::recover_deferred(recv_sys_t::map::iterator &p,
uint32_t(file_size / fil_space_t::physical_size(flags));
if (n_pages > size)
{
+ mysql_mutex_lock(&fil_system.mutex);
space->size= node->size= n_pages;
space->set_committed_size();
+ mysql_mutex_unlock(&fil_system.mutex);
goto size_set;
}
}
@@ -3582,7 +3588,7 @@ next_free_block:
else
{
mysql_mutex_unlock(&mutex);
- os_aio_wait_until_no_pending_reads();
+ os_aio_wait_until_no_pending_reads(false);
mysql_mutex_lock(&mutex);
ut_ad(pages.empty());
}
diff --git a/storage/innobase/mtr/mtr0mtr.cc b/storage/innobase/mtr/mtr0mtr.cc
index b866460feb5..fb08511126a 100644
--- a/storage/innobase/mtr/mtr0mtr.cc
+++ b/storage/innobase/mtr/mtr0mtr.cc
@@ -597,8 +597,14 @@ void mtr_t::commit_shrink(fil_space_t &space)
/** Commit a mini-transaction that is deleting or renaming a file.
@param space tablespace that is being renamed or deleted
@param name new file name (nullptr=the file will be deleted)
+@param detached_handle if detached_handle != nullptr and if space is detached
+ during the function execution the file handle if its
+ node will be set to OS_FILE_CLOSED, and the previous
+ value of the file handle will be assigned to the
+ address, pointed by detached_handle.
@return whether the operation succeeded */
-bool mtr_t::commit_file(fil_space_t &space, const char *name)
+bool mtr_t::commit_file(fil_space_t &space, const char *name,
+ pfs_os_file_t *detached_handle)
{
ut_ad(is_active());
ut_ad(!is_inside_ibuf());
@@ -687,7 +693,9 @@ bool mtr_t::commit_file(fil_space_t &space, const char *name)
ut_ad(!space.referenced());
ut_ad(space.is_stopping());
- fil_system.detach(&space, true);
+ pfs_os_file_t handle = fil_system.detach(&space, true);
+ if (detached_handle)
+ *detached_handle = handle;
mysql_mutex_unlock(&fil_system.mutex);
success= true;
diff --git a/storage/innobase/os/os0file.cc b/storage/innobase/os/os0file.cc
index 0274af2ed9f..1354de3cef2 100644
--- a/storage/innobase/os/os0file.cc
+++ b/storage/innobase/os/os0file.cc
@@ -3453,9 +3453,8 @@ static void io_callback(tpool::aiocb *cb)
else
{
ut_ad(write_slots->contains(cb));
- const IORequest req{request};
+ fil_aio_callback(request);
write_slots->release(cb);
- fil_aio_callback(req);
}
}
@@ -3699,9 +3698,9 @@ void os_aio_free()
}
/** Wait until there are no pending asynchronous writes. */
-static void os_aio_wait_until_no_pending_writes_low()
+static void os_aio_wait_until_no_pending_writes_low(bool declare)
{
- bool notify_wait = write_slots->pending_io_count() > 0;
+ const bool notify_wait= declare && write_slots->pending_io_count();
if (notify_wait)
tpool::tpool_wait_begin();
@@ -3712,10 +3711,11 @@ static void os_aio_wait_until_no_pending_writes_low()
tpool::tpool_wait_end();
}
-/** Wait until there are no pending asynchronous writes. */
-void os_aio_wait_until_no_pending_writes()
+/** Wait until there are no pending asynchronous writes.
+@param declare whether the wait will be declared in tpool */
+void os_aio_wait_until_no_pending_writes(bool declare)
{
- os_aio_wait_until_no_pending_writes_low();
+ os_aio_wait_until_no_pending_writes_low(declare);
buf_dblwr.wait_flush_buffered_writes();
}
@@ -3743,10 +3743,11 @@ size_t os_aio_pending_writes()
return pending;
}
-/** Wait until all pending asynchronous reads have completed. */
-void os_aio_wait_until_no_pending_reads()
+/** Wait until all pending asynchronous reads have completed.
+@param declare whether the wait will be declared in tpool */
+void os_aio_wait_until_no_pending_reads(bool declare)
{
- const auto notify_wait= read_slots->pending_io_count();
+ const bool notify_wait= declare && read_slots->pending_io_count();
if (notify_wait)
tpool::tpool_wait_begin();
diff --git a/storage/innobase/rem/rem0rec.cc b/storage/innobase/rem/rem0rec.cc
index d743ef9aa5c..766f5000a0d 100644
--- a/storage/innobase/rem/rem0rec.cc
+++ b/storage/innobase/rem/rem0rec.cc
@@ -242,9 +242,9 @@ enum rec_leaf_format {
REC_LEAF_INSTANT
};
-#if defined __GNUC__ && !defined __clang__ && __GNUC__ < 11
+#if defined __GNUC__ && !defined __clang__ && __GNUC__ < 12
# pragma GCC diagnostic push
-# pragma GCC diagnostic ignored "-Wconversion" /* GCC 5 to 10 need this */
+# pragma GCC diagnostic ignored "-Wconversion" /* GCC 5 to 11 need this */
#endif
/** Determine the offset to each field in a leaf-page record
in ROW_FORMAT=COMPACT,DYNAMIC,COMPRESSED.
diff --git a/storage/innobase/row/row0merge.cc b/storage/innobase/row/row0merge.cc
index cfeac48ac52..42c8c633b8d 100644
--- a/storage/innobase/row/row0merge.cc
+++ b/storage/innobase/row/row0merge.cc
@@ -756,11 +756,6 @@ error:
row_field, field, col->len,
old_table->space->zip_size(),
conv_heap);
- } else {
- /* Field length mismatch should not
- happen when rebuilding redundant row
- format table. */
- ut_ad(index->table->not_redundant());
}
}
}
diff --git a/storage/innobase/row/row0mysql.cc b/storage/innobase/row/row0mysql.cc
index 549d2745223..51cd5b87a24 100644
--- a/storage/innobase/row/row0mysql.cc
+++ b/storage/innobase/row/row0mysql.cc
@@ -2470,6 +2470,7 @@ rollback:
if (fts_exist)
purge_sys.resume_FTS();
+ ibuf_delete_for_discarded_space(space_id);
buf_flush_remove_pages(space_id);
trx->op_info= "";
return err;
diff --git a/storage/innobase/row/row0purge.cc b/storage/innobase/row/row0purge.cc
index 65d26e0a733..cfb42ec46b2 100644
--- a/storage/innobase/row/row0purge.cc
+++ b/storage/innobase/row/row0purge.cc
@@ -162,8 +162,9 @@ close_and_exit:
ut_ad("corrupted SYS_INDEXES record" == 0);
}
- if (const uint32_t space_id = dict_drop_index_tree(
- &node->pcur, nullptr, &mtr)) {
+ const uint32_t space_id = dict_drop_index_tree(
+ &node->pcur, nullptr, &mtr);
+ if (space_id) {
if (table) {
if (table->get_ref_count() == 0) {
dict_sys.remove(table);
@@ -184,6 +185,10 @@ close_and_exit:
table = nullptr;
}
+ if (space_id) {
+ ibuf_delete_for_discarded_space(space_id);
+ }
+
purge_sys.check_stop_SYS();
mtr.start();
index->set_modified(mtr);
diff --git a/storage/innobase/row/row0quiesce.cc b/storage/innobase/row/row0quiesce.cc
index a4d634f2d14..eadb30bfcfa 100644
--- a/storage/innobase/row/row0quiesce.cc
+++ b/storage/innobase/row/row0quiesce.cc
@@ -553,7 +553,7 @@ row_quiesce_table_start(
if (!trx_is_interrupted(trx)) {
/* Ensure that all asynchronous IO is completed. */
- os_aio_wait_until_no_pending_writes();
+ os_aio_wait_until_no_pending_writes(true);
table->space->flush<false>();
if (row_quiesce_write_cfg(table, trx->mysql_thd)
diff --git a/storage/innobase/row/row0uins.cc b/storage/innobase/row/row0uins.cc
index 50196e78092..ff1a34b46c3 100644
--- a/storage/innobase/row/row0uins.cc
+++ b/storage/innobase/row/row0uins.cc
@@ -147,8 +147,9 @@ restart:
pfs_os_file_t d = OS_FILE_CLOSED;
- if (const uint32_t space_id = dict_drop_index_tree(
- &node->pcur, node->trx, &mtr)) {
+ const uint32_t space_id = dict_drop_index_tree(
+ &node->pcur, node->trx, &mtr);
+ if (space_id) {
if (table) {
lock_release_on_rollback(node->trx,
table);
@@ -187,6 +188,10 @@ restart:
os_file_close(d);
}
+ if (space_id) {
+ ibuf_delete_for_discarded_space(space_id);
+ }
+
mtr.start();
ut_a(node->pcur.restore_position(
BTR_MODIFY_LEAF, &mtr) == btr_pcur_t::SAME_ALL);
diff --git a/storage/innobase/srv/srv0mon.cc b/storage/innobase/srv/srv0mon.cc
index d0f96ece141..d47a57ff8ec 100644
--- a/storage/innobase/srv/srv0mon.cc
+++ b/storage/innobase/srv/srv0mon.cc
@@ -674,16 +674,18 @@ 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_DEFAULT_ON),
+ MONITOR_EXISTING | MONITOR_DISPLAY_CURRENT),
MONITOR_DEFAULT_START, MONITOR_RSEG_HISTORY_LEN},
{"trx_undo_slots_used", "transaction", "Number of undo slots used",
- MONITOR_NONE,
+ static_cast<monitor_type_t>(
+ MONITOR_EXISTING | MONITOR_DISPLAY_CURRENT),
MONITOR_DEFAULT_START, MONITOR_NUM_UNDO_SLOT_USED},
{"trx_undo_slots_cached", "transaction",
"Number of undo slots cached",
- MONITOR_NONE,
+ static_cast<monitor_type_t>(
+ MONITOR_EXISTING | MONITOR_DISPLAY_CURRENT | MONITOR_DEFAULT_ON),
MONITOR_DEFAULT_START, MONITOR_NUM_UNDO_SLOT_CACHED},
{"trx_rseg_current_size", "transaction",
@@ -1289,6 +1291,24 @@ TPOOL_SUPPRESS_TSAN static ulint srv_mon_get_rseg_size()
return size;
}
+/** @return number of used undo log slots */
+TPOOL_SUPPRESS_TSAN static ulint srv_mon_get_rseg_used()
+{
+ ulint size= 0;
+ for (const auto &rseg : trx_sys.rseg_array)
+ size+= UT_LIST_GET_LEN(rseg.undo_list);
+ return size;
+}
+
+/** @return number of cached undo log slots */
+TPOOL_SUPPRESS_TSAN static ulint srv_mon_get_rseg_cached()
+{
+ ulint size= 0;
+ for (const auto &rseg : trx_sys.rseg_array)
+ size+= UT_LIST_GET_LEN(rseg.undo_cached);
+ return size;
+}
+
/****************************************************************//**
This function consolidates some existing server counters used
by "system status variables". These existing system variables do not have
@@ -1526,7 +1546,12 @@ srv_mon_process_existing_counter(
case MONITOR_RSEG_CUR_SIZE:
value = srv_mon_get_rseg_size();
break;
-
+ case MONITOR_NUM_UNDO_SLOT_USED:
+ value = srv_mon_get_rseg_used();
+ break;
+ case MONITOR_NUM_UNDO_SLOT_CACHED:
+ value = srv_mon_get_rseg_cached();
+ break;
case MONITOR_OVLD_N_FILE_OPENED:
value = fil_system.n_open;
break;
@@ -1637,7 +1662,6 @@ srv_mon_process_existing_counter(
case MONITOR_TIMEOUT:
value = lock_sys.timeouts;
break;
-
default:
ut_error;
}
diff --git a/storage/innobase/srv/srv0srv.cc b/storage/innobase/srv/srv0srv.cc
index c5ccb7ee43b..08a18768324 100644
--- a/storage/innobase/srv/srv0srv.cc
+++ b/storage/innobase/srv/srv0srv.cc
@@ -911,12 +911,13 @@ srv_export_innodb_status(void)
export_vars.innodb_data_writes = os_n_file_writes;
buf_dblwr.lock();
- ulint dblwr = buf_dblwr.submitted();
- export_vars.innodb_dblwr_pages_written = buf_dblwr.written();
+ ulint dblwr = buf_dblwr.written();
+ export_vars.innodb_dblwr_pages_written = dblwr;
export_vars.innodb_dblwr_writes = buf_dblwr.batches();
buf_dblwr.unlock();
- export_vars.innodb_data_written = srv_stats.data_written + dblwr;
+ export_vars.innodb_data_written = srv_stats.data_written
+ + (dblwr << srv_page_size_shift);
export_vars.innodb_buffer_pool_bytes_data =
buf_pool.stat.LRU_bytes
diff --git a/storage/innobase/srv/srv0start.cc b/storage/innobase/srv/srv0start.cc
index 4a938f0eb03..081731c4a1f 100644
--- a/storage/innobase/srv/srv0start.cc
+++ b/storage/innobase/srv/srv0start.cc
@@ -692,14 +692,12 @@ err_exit:
fil_set_max_space_id_if_bigger(space_id);
+ mysql_mutex_lock(&fil_system.mutex);
fil_space_t *space= fil_space_t::create(space_id, fsp_flags,
FIL_TYPE_TABLESPACE, nullptr,
FIL_ENCRYPTION_DEFAULT, true);
- ut_a(fil_validate());
- ut_a(space);
-
+ ut_ad(space);
fil_node_t *file= space->add(name, fh, 0, false, true);
- mysql_mutex_lock(&fil_system.mutex);
if (create)
{
@@ -1098,10 +1096,10 @@ same_size:
ut_ad(flushed_lsn == log_sys.get_lsn());
ut_ad(!os_aio_pending_reads());
- ut_ad(!os_aio_pending_writes());
ut_d(mysql_mutex_lock(&buf_pool.flush_list_mutex));
ut_ad(!buf_pool.get_oldest_modification(0));
ut_d(mysql_mutex_unlock(&buf_pool.flush_list_mutex));
+ ut_d(os_aio_wait_until_no_pending_writes(false));
DBUG_RETURN(flushed_lsn);
}
diff --git a/storage/innobase/trx/trx0purge.cc b/storage/innobase/trx/trx0purge.cc
index 867126adc0e..d3ee678a43e 100644
--- a/storage/innobase/trx/trx0purge.cc
+++ b/storage/innobase/trx/trx0purge.cc
@@ -275,13 +275,11 @@ trx_purge_add_undo_to_history(const trx_t* trx, trx_undo_t*& undo, mtr_t* mtr)
if (undo->state != TRX_UNDO_CACHED) {
/* The undo log segment will not be reused */
ut_a(undo->id < TRX_RSEG_N_SLOTS);
- compile_time_assert(FIL_NULL == 0xffffffff);
+ static_assert(FIL_NULL == 0xffffffff, "");
mtr->memset(rseg_header,
TRX_RSEG + TRX_RSEG_UNDO_SLOTS
+ undo->id * TRX_RSEG_SLOT_SIZE, 4, 0xff);
- MONITOR_DEC(MONITOR_NUM_UNDO_SLOT_USED);
-
uint32_t hist_size = mach_read_from_4(
TRX_RSEG_HISTORY_SIZE + TRX_RSEG
+ rseg_header->page.frame);
@@ -365,7 +363,6 @@ trx_purge_add_undo_to_history(const trx_t* trx, trx_undo_t*& undo, mtr_t* mtr)
if (undo->state == TRX_UNDO_CACHED) {
UT_LIST_ADD_FIRST(rseg->undo_cached, undo);
- MONITOR_INC(MONITOR_NUM_UNDO_SLOT_CACHED);
} else {
ut_ad(undo->state == TRX_UNDO_TO_PURGE);
ut_free(undo);
@@ -387,161 +384,168 @@ static dberr_t trx_purge_remove_log_hdr(buf_block_t *rseg, buf_block_t* log,
uint16_t(offset + TRX_UNDO_HISTORY_NODE), mtr);
}
-MY_ATTRIBUTE((nonnull, warn_unused_result))
-/** Free an undo log segment, and remove the header from the history list.
-@param[in,out] mtr mini-transaction
-@param[in,out] rseg rollback segment
-@param[in] hdr_addr file address of log_hdr
-@return error code */
-static dberr_t
-trx_purge_free_segment(mtr_t &mtr, trx_rseg_t* rseg, fil_addr_t hdr_addr)
+/** Free an undo log segment.
+@param block rollback segment header page
+@param mtr mini-transaction */
+static void trx_purge_free_segment(buf_block_t *block, mtr_t &mtr)
{
- mtr.commit();
- log_free_check();
- mtr.start();
-
- const page_id_t hdr_page_id{rseg->space->id, hdr_addr.page};
- dberr_t err;
- buf_block_t *rseg_hdr= rseg->get(&mtr, &err);
- if (!rseg_hdr)
- return err;
- buf_block_t *block= buf_page_get_gen(hdr_page_id, 0, RW_X_LATCH,
- nullptr, BUF_GET_POSSIBLY_FREED,
- &mtr, &err);
- if (!block)
- return err;
-
- const uint32_t seg_size=
- flst_get_len(TRX_UNDO_SEG_HDR + TRX_UNDO_PAGE_LIST + block->page.frame);
-
- err= trx_purge_remove_log_hdr(rseg_hdr, block, hdr_addr.boffset, &mtr);
- if (UNIV_UNLIKELY(err != DB_SUCCESS))
- return err;
-
- ut_ad(rseg->curr_size >= seg_size);
- rseg->curr_size-= seg_size;
- rseg->history_size--;
-
- byte *hist= TRX_RSEG + TRX_RSEG_HISTORY_SIZE + rseg_hdr->page.frame;
- ut_ad(mach_read_from_4(hist) >= seg_size);
- mtr.write<4>(*rseg_hdr, hist, mach_read_from_4(hist) - seg_size);
-
while (!fseg_free_step_not_header(TRX_UNDO_SEG_HDR + TRX_UNDO_FSEG_HEADER +
block->page.frame, &mtr))
{
block->fix();
+ const page_id_t id{block->page.id()};
mtr.commit();
/* NOTE: If the server is killed after the log that was produced
up to this point was written, and before the log from the mtr.commit()
in our caller is written, then the pages belonging to the
undo log will become unaccessible garbage.
- This does not matters when using multiple innodb_undo_tablespaces;
+ This does not matter when using multiple innodb_undo_tablespaces;
innodb_undo_log_truncate=ON will be able to reclaim the space. */
log_free_check();
mtr.start();
block->page.lock.x_lock();
- mtr.memo_push(block, MTR_MEMO_PAGE_X_MODIFY);
+ if (UNIV_UNLIKELY(block->page.id() != id))
+ {
+ block->unfix();
+ block->page.lock.x_unlock();
+ block= buf_page_get_gen(id, 0, RW_X_LATCH, nullptr, BUF_GET, &mtr);
+ if (!block)
+ return;
+ }
+ else
+ mtr.memo_push(block, MTR_MEMO_PAGE_X_MODIFY);
}
while (!fseg_free_step(TRX_UNDO_SEG_HDR + TRX_UNDO_FSEG_HEADER +
block->page.frame, &mtr));
- return DB_SUCCESS;
}
/** Remove unnecessary history data from a rollback segment.
@param[in,out] rseg rollback segment
@param[in] limit truncate anything before this
@return error code */
-static
-dberr_t
-trx_purge_truncate_rseg_history(
- trx_rseg_t& rseg,
- const purge_sys_t::iterator& limit)
+static dberr_t
+trx_purge_truncate_rseg_history(trx_rseg_t& rseg,
+ const purge_sys_t::iterator& limit)
{
- fil_addr_t hdr_addr;
- mtr_t mtr;
+ fil_addr_t hdr_addr;
+ mtr_t mtr;
- mtr.start();
+ log_free_check();
+ mtr.start();
- dberr_t err;
- buf_block_t* rseg_hdr = rseg.get(&mtr, &err);
- if (!rseg_hdr) {
- goto func_exit;
- }
+ dberr_t err;
+reget:
+ buf_block_t *rseg_hdr= rseg.get(&mtr, &err);
+ if (!rseg_hdr)
+ {
+func_exit:
+ mtr.commit();
+ return err;
+ }
- hdr_addr = flst_get_last(TRX_RSEG + TRX_RSEG_HISTORY
- + rseg_hdr->page.frame);
- hdr_addr.boffset = static_cast<uint16_t>(hdr_addr.boffset
- - TRX_UNDO_HISTORY_NODE);
+ hdr_addr= flst_get_last(TRX_RSEG + TRX_RSEG_HISTORY + rseg_hdr->page.frame);
+ hdr_addr.boffset= static_cast<uint16_t>(hdr_addr.boffset -
+ TRX_UNDO_HISTORY_NODE);
loop:
- if (hdr_addr.page == FIL_NULL) {
-func_exit:
- mtr.commit();
- return err;
- }
+ if (hdr_addr.page == FIL_NULL)
+ goto func_exit;
- buf_block_t* block = buf_page_get_gen(page_id_t(rseg.space->id,
- hdr_addr.page),
- 0, RW_X_LATCH, nullptr,
- BUF_GET_POSSIBLY_FREED,
- &mtr, &err);
- if (!block) {
- goto func_exit;
- }
+ buf_block_t *b=
+ buf_page_get_gen(page_id_t(rseg.space->id, hdr_addr.page),
+ 0, RW_X_LATCH, nullptr, BUF_GET_POSSIBLY_FREED,
+ &mtr, &err);
+ if (!b)
+ goto func_exit;
- const trx_id_t undo_trx_no = mach_read_from_8(
- block->page.frame + hdr_addr.boffset + TRX_UNDO_TRX_NO);
+ const trx_id_t undo_trx_no=
+ mach_read_from_8(b->page.frame + hdr_addr.boffset + TRX_UNDO_TRX_NO);
- if (undo_trx_no >= limit.trx_no) {
- if (undo_trx_no == limit.trx_no) {
- err = trx_undo_truncate_start(
- &rseg, hdr_addr.page,
- hdr_addr.boffset, limit.undo_no);
- }
+ if (undo_trx_no >= limit.trx_no)
+ {
+ if (undo_trx_no == limit.trx_no)
+ err = trx_undo_truncate_start(&rseg, hdr_addr.page,
+ hdr_addr.boffset, limit.undo_no);
+ goto func_exit;
+ }
- goto func_exit;
- }
+ fil_addr_t prev_hdr_addr=
+ flst_get_prev_addr(b->page.frame + hdr_addr.boffset +
+ TRX_UNDO_HISTORY_NODE);
+ prev_hdr_addr.boffset= static_cast<uint16_t>(prev_hdr_addr.boffset -
+ TRX_UNDO_HISTORY_NODE);
+ err= trx_purge_remove_log_hdr(rseg_hdr, b, hdr_addr.boffset, &mtr);
+ if (UNIV_UNLIKELY(err != DB_SUCCESS))
+ goto func_exit;
- fil_addr_t prev_hdr_addr = flst_get_prev_addr(
- block->page.frame + hdr_addr.boffset + TRX_UNDO_HISTORY_NODE);
- prev_hdr_addr.boffset = static_cast<uint16_t>(prev_hdr_addr.boffset
- - TRX_UNDO_HISTORY_NODE);
-
- if (!rseg.is_referenced()
- && rseg.needs_purge <= (purge_sys.head.trx_no
- ? purge_sys.head.trx_no
- : purge_sys.tail.trx_no)
- && mach_read_from_2(TRX_UNDO_SEG_HDR + TRX_UNDO_STATE
- + block->page.frame)
- == TRX_UNDO_TO_PURGE
- && !mach_read_from_2(block->page.frame + hdr_addr.boffset
- + TRX_UNDO_NEXT_LOG)) {
- /* We can free the whole log segment.
- This will call trx_purge_remove_log_hdr(). */
- err = trx_purge_free_segment(mtr, &rseg, hdr_addr);
- } else {
- /* Remove the log hdr from the rseg history. */
- rseg.history_size--;
- err = trx_purge_remove_log_hdr(rseg_hdr, block,
- hdr_addr.boffset, &mtr);
- }
+ rseg_hdr->fix();
- mtr.commit();
- if (err != DB_SUCCESS) {
- return err;
- }
- mtr.start();
+ if (mach_read_from_2(b->page.frame + hdr_addr.boffset + TRX_UNDO_NEXT_LOG) ||
+ rseg.is_referenced() ||
+ rseg.needs_purge > (purge_sys.head.trx_no
+ ? purge_sys.head.trx_no
+ : purge_sys.tail.trx_no))
+ /* We cannot free the entire undo page. */;
+ else
+ {
+ const uint32_t seg_size=
+ flst_get_len(TRX_UNDO_SEG_HDR + TRX_UNDO_PAGE_LIST + b->page.frame);
+ switch (mach_read_from_2(TRX_UNDO_SEG_HDR + TRX_UNDO_STATE +
+ b->page.frame)) {
+ case TRX_UNDO_TO_PURGE:
+ {
+ byte *hist= TRX_RSEG + TRX_RSEG_HISTORY_SIZE + rseg_hdr->page.frame;
+ ut_ad(mach_read_from_4(hist) >= seg_size);
+ mtr.write<4>(*rseg_hdr, hist, mach_read_from_4(hist) - seg_size);
+ }
+ free_segment:
+ ut_ad(rseg.curr_size >= seg_size);
+ rseg.curr_size-= seg_size;
+ trx_purge_free_segment(b, mtr);
+ break;
+ case TRX_UNDO_CACHED:
+ /* rseg.undo_cached must point to this page */
+ trx_undo_t *undo= UT_LIST_GET_FIRST(rseg.undo_cached);
+ for (; undo; undo= UT_LIST_GET_NEXT(undo_list, undo))
+ if (undo->hdr_page_no == hdr_addr.page)
+ goto found_cached;
+ ut_ad("inconsistent undo logs" == 0);
+ break;
+ found_cached:
+ UT_LIST_REMOVE(rseg.undo_cached, undo);
+ static_assert(FIL_NULL == 0xffffffff, "");
+ if (UNIV_UNLIKELY(mach_read_from_4(TRX_RSEG + TRX_RSEG_FORMAT +
+ rseg_hdr->page.frame)))
+ trx_rseg_format_upgrade(rseg_hdr, &mtr);
+ mtr.memset(rseg_hdr, TRX_RSEG + TRX_RSEG_UNDO_SLOTS +
+ undo->id * TRX_RSEG_SLOT_SIZE, 4, 0xff);
+ ut_free(undo);
+ mtr.write<8,mtr_t::MAYBE_NOP>(*rseg_hdr, TRX_RSEG + TRX_RSEG_MAX_TRX_ID +
+ rseg_hdr->page.frame,
+ trx_sys.get_max_trx_id() - 1);
+ goto free_segment;
+ }
+ }
- hdr_addr = prev_hdr_addr;
+ hdr_addr= prev_hdr_addr;
- rseg_hdr = rseg.get(&mtr, &err);
- if (!rseg_hdr) {
- goto func_exit;
- }
+ mtr.commit();
+ ut_ad(rseg.history_size > 0);
+ rseg.history_size--;
+ log_free_check();
+ mtr.start();
+ rseg_hdr->page.lock.x_lock();
+ if (UNIV_UNLIKELY(rseg_hdr->page.id() != rseg.page_id()))
+ {
+ rseg_hdr->unfix();
+ rseg_hdr->page.lock.x_unlock();
+ goto reget;
+ }
+ mtr.memo_push(rseg_hdr, MTR_MEMO_PAGE_X_MODIFY);
- goto loop;
+ goto loop;
}
/** Cleanse purge queue to remove the rseg that reside in undo-tablespace
diff --git a/storage/innobase/trx/trx0rec.cc b/storage/innobase/trx/trx0rec.cc
index e70516a2d2d..c1a7b08b717 100644
--- a/storage/innobase/trx/trx0rec.cc
+++ b/storage/innobase/trx/trx0rec.cc
@@ -1875,26 +1875,28 @@ trx_undo_report_row_operation(
}
mtr_t mtr;
+ dberr_t err;
mtr.start();
trx_undo_t** pundo;
trx_rseg_t* rseg;
const bool is_temp = index->table->is_temporary();
+ buf_block_t* undo_block;
if (is_temp) {
mtr.set_log_mode(MTR_LOG_NO_REDO);
-
rseg = trx->get_temp_rseg();
pundo = &trx->rsegs.m_noredo.undo;
+ undo_block = trx_undo_assign_low<true>(trx, rseg, pundo,
+ &mtr, &err);
} else {
ut_ad(!trx->read_only);
ut_ad(trx->id);
pundo = &trx->rsegs.m_redo.undo;
rseg = trx->rsegs.m_redo.rseg;
+ undo_block = trx_undo_assign_low<false>(trx, rseg, pundo,
+ &mtr, &err);
}
- dberr_t err;
- buf_block_t* undo_block = trx_undo_assign_low(trx, rseg, pundo,
- &err, &mtr);
trx_undo_t* undo = *pundo;
ut_ad((err == DB_SUCCESS) == (undo_block != NULL));
if (UNIV_UNLIKELY(undo_block == NULL)) {
diff --git a/storage/innobase/trx/trx0rseg.cc b/storage/innobase/trx/trx0rseg.cc
index 3fada7d34aa..8d1a381ce22 100644
--- a/storage/innobase/trx/trx0rseg.cc
+++ b/storage/innobase/trx/trx0rseg.cc
@@ -395,7 +395,6 @@ void trx_rseg_t::reinit(uint32_t page)
{
next= UT_LIST_GET_NEXT(undo_list, undo);
UT_LIST_REMOVE(undo_cached, undo);
- MONITOR_DEC(MONITOR_NUM_UNDO_SLOT_CACHED);
ut_free(undo);
}
@@ -404,6 +403,7 @@ void trx_rseg_t::reinit(uint32_t page)
last_commit_and_offset= 0;
last_page_no= FIL_NULL;
curr_size= 1;
+ ref.store(0, std::memory_order_release);
}
/** Read the undo log lists.
@@ -428,7 +428,6 @@ static dberr_t trx_undo_lists_init(trx_rseg_t *rseg,
if (is_undo_empty)
is_undo_empty= !undo->size || undo->state == TRX_UNDO_CACHED;
rseg->curr_size+= undo->size;
- MONITOR_INC(MONITOR_NUM_UNDO_SLOT_USED);
}
}
diff --git a/storage/innobase/trx/trx0trx.cc b/storage/innobase/trx/trx0trx.cc
index bc41a535dbe..3a04fd4b71c 100644
--- a/storage/innobase/trx/trx0trx.cc
+++ b/storage/innobase/trx/trx0trx.cc
@@ -816,28 +816,17 @@ static void trx_assign_rseg_low(trx_t *trx)
static Atomic_counter<unsigned> rseg_slot;
unsigned slot = rseg_slot++ % TRX_SYS_N_RSEGS;
ut_d(if (trx_rseg_n_slots_debug) slot = 0);
+ ut_d(const auto start_scan_slot = slot);
+ ut_d(bool look_for_rollover = false);
trx_rseg_t* rseg;
-#ifdef UNIV_DEBUG
- ulint start_scan_slot = slot;
- bool look_for_rollover = false;
-#endif /* UNIV_DEBUG */
-
bool allocated;
do {
for (;;) {
rseg = &trx_sys.rseg_array[slot];
-
-#ifdef UNIV_DEBUG
- /* Ensure that we are not revisiting the same
- slot that we have already inspected. */
- if (look_for_rollover) {
- ut_ad(start_scan_slot != slot);
- }
- look_for_rollover = true;
-#endif /* UNIV_DEBUG */
-
+ ut_ad(!look_for_rollover || start_scan_slot != slot);
+ ut_d(look_for_rollover = true);
ut_d(if (!trx_rseg_n_slots_debug))
slot = (slot + 1) % TRX_SYS_N_RSEGS;
@@ -1038,7 +1027,13 @@ trx_write_serialisation_history(
mtr_t temp_mtr;
temp_mtr.start();
temp_mtr.set_log_mode(MTR_LOG_NO_REDO);
- trx_undo_set_state_at_finish(undo, &temp_mtr);
+ buf_block_t* block= buf_page_get(page_id_t(SRV_TMP_SPACE_ID,
+ undo->hdr_page_no),
+ 0, RW_X_LATCH, mtr);
+ ut_a(block);
+ temp_mtr.write<2>(*block, TRX_UNDO_SEG_HDR + TRX_UNDO_STATE
+ + block->page.frame, TRX_UNDO_TO_PURGE);
+ undo->state = TRX_UNDO_TO_PURGE;
temp_mtr.commit();
}
diff --git a/storage/innobase/trx/trx0undo.cc b/storage/innobase/trx/trx0undo.cc
index 33b1f93ff65..20434d9fb9c 100644
--- a/storage/innobase/trx/trx0undo.cc
+++ b/storage/innobase/trx/trx0undo.cc
@@ -535,8 +535,6 @@ trx_undo_seg_create(fil_space_t *space, buf_block_t *rseg_hdr, ulint *id,
+ slot_no * TRX_RSEG_SLOT_SIZE + rseg_hdr->page.frame,
block->page.id().page_no());
- MONITOR_INC(MONITOR_NUM_UNDO_SLOT_USED);
-
*err = DB_SUCCESS;
return block;
}
@@ -996,7 +994,6 @@ static void trx_undo_seg_free(const trx_undo_t *undo)
static_assert(FIL_NULL == 0xffffffff, "compatibility");
mtr.memset(rseg_header, TRX_RSEG + TRX_RSEG_UNDO_SLOTS +
undo->id * TRX_RSEG_SLOT_SIZE, 4, 0xff);
- MONITOR_DEC(MONITOR_NUM_UNDO_SLOT_USED);
}
}
@@ -1155,7 +1152,6 @@ corrupted_type:
UT_LIST_ADD_LAST(rseg->undo_list, undo);
} else {
UT_LIST_ADD_LAST(rseg->undo_cached, undo);
- MONITOR_INC(MONITOR_NUM_UNDO_SLOT_CACHED);
}
mtr.commit();
@@ -1294,27 +1290,25 @@ trx_undo_create(trx_t* trx, trx_rseg_t* rseg, trx_undo_t** undo,
@param[in,out] rseg rollback segment
@param[out] pundo the undo log memory object
@param[in,out] mtr mini-transaction
+@param[out] err error code
@return the undo log block
@retval NULL if none cached */
static
buf_block_t*
trx_undo_reuse_cached(trx_t* trx, trx_rseg_t* rseg, trx_undo_t** pundo,
- mtr_t* mtr)
+ mtr_t* mtr, dberr_t *err)
{
- if (rseg->is_persistent()) {
- ut_ad(rseg->is_referenced());
- if (rseg->needs_purge <= trx->id) {
- /* trx_purge_truncate_history() compares
- rseg->needs_purge <= head.trx_no
- so we need to compensate for that.
- The rseg->needs_purge after crash
- recovery would be at least trx->id + 1,
- because that is the minimum possible value
- assigned by trx_serialise() on commit. */
- rseg->needs_purge = trx->id + 1;
- }
- } else {
- ut_ad(!rseg->is_referenced());
+ ut_ad(rseg->is_persistent());
+ ut_ad(rseg->is_referenced());
+ if (rseg->needs_purge <= trx->id) {
+ /* trx_purge_truncate_history() compares
+ rseg->needs_purge <= head.trx_no
+ so we need to compensate for that.
+ The rseg->needs_purge after crash
+ recovery would be at least trx->id + 1,
+ because that is the minimum possible value
+ assigned by trx_serialise() on commit. */
+ rseg->needs_purge = trx->id + 1;
}
trx_undo_t* undo = UT_LIST_GET_FIRST(rseg->undo_cached);
@@ -1325,15 +1319,15 @@ trx_undo_reuse_cached(trx_t* trx, trx_rseg_t* rseg, trx_undo_t** pundo,
ut_ad(undo->size == 1);
ut_ad(undo->id < TRX_RSEG_N_SLOTS);
- buf_block_t* block = buf_page_get(page_id_t(undo->rseg->space->id,
- undo->hdr_page_no),
- 0, RW_X_LATCH, mtr);
+ buf_block_t* block = buf_page_get_gen(page_id_t(undo->rseg->space->id,
+ undo->hdr_page_no),
+ 0, RW_X_LATCH, nullptr, BUF_GET,
+ mtr, err);
if (!block) {
return NULL;
}
UT_LIST_REMOVE(rseg->undo_cached, undo);
- MONITOR_DEC(MONITOR_NUM_UNDO_SLOT_CACHED);
*pundo = undo;
@@ -1379,11 +1373,12 @@ trx_undo_assign(trx_t* trx, dberr_t* err, mtr_t* mtr)
BUF_GET, mtr, err);
}
+ *err = DB_SUCCESS;
trx_rseg_t* rseg = trx->rsegs.m_redo.rseg;
rseg->latch.wr_lock(SRW_LOCK_CALL);
buf_block_t* block = trx_undo_reuse_cached(
- trx, rseg, &trx->rsegs.m_redo.undo, mtr);
+ trx, rseg, &trx->rsegs.m_redo.undo, mtr, err);
if (!block) {
block = trx_undo_create(trx, rseg, &trx->rsegs.m_redo.undo,
@@ -1392,8 +1387,6 @@ trx_undo_assign(trx_t* trx, dberr_t* err, mtr_t* mtr)
if (!block) {
goto func_exit;
}
- } else {
- *err = DB_SUCCESS;
}
UT_LIST_ADD_FIRST(rseg->undo_list, trx->rsegs.m_redo.undo);
@@ -1405,18 +1398,20 @@ func_exit:
/** Assign an undo log for a transaction.
A new undo log is created or a cached undo log reused.
+@tparam is_temp whether this is temporary undo log
@param[in,out] trx transaction
@param[in] rseg rollback segment
@param[out] undo the undo log
-@param[out] err error code
@param[in,out] mtr mini-transaction
+@param[out] err error code
@return the undo log block
-@retval NULL on error */
+@retval nullptr on error */
+template<bool is_temp>
buf_block_t*
-trx_undo_assign_low(trx_t* trx, trx_rseg_t* rseg, trx_undo_t** undo,
- dberr_t* err, mtr_t* mtr)
+trx_undo_assign_low(trx_t *trx, trx_rseg_t *rseg, trx_undo_t **undo,
+ mtr_t *mtr, dberr_t *err)
{
- ut_d(const bool is_temp = rseg == trx->rsegs.m_noredo.rseg);
+ ut_ad(is_temp == (rseg == trx->rsegs.m_noredo.rseg));
ut_ad(is_temp || rseg == trx->rsegs.m_redo.rseg);
ut_ad(undo == (is_temp
? &trx->rsegs.m_noredo.undo
@@ -1436,19 +1431,24 @@ trx_undo_assign_low(trx_t* trx, trx_rseg_t* rseg, trx_undo_t** undo,
*err = DB_TOO_MANY_CONCURRENT_TRXS; return NULL;
);
+ *err = DB_SUCCESS;
rseg->latch.wr_lock(SRW_LOCK_CALL);
- buf_block_t* block = trx_undo_reuse_cached(trx, rseg, undo, mtr);
-
- if (!block) {
- block = trx_undo_create(trx, rseg, undo, err, mtr);
- ut_ad(!block == (*err != DB_SUCCESS));
- if (!block) {
- goto func_exit;
- }
+ buf_block_t* block;
+ if (is_temp) {
+ ut_ad(!UT_LIST_GET_LEN(rseg->undo_cached));
} else {
- *err = DB_SUCCESS;
+ block = trx_undo_reuse_cached(trx, rseg, undo, mtr, err);
+ if (block) {
+ goto got_block;
+ }
+ }
+ block = trx_undo_create(trx, rseg, undo, err, mtr);
+ ut_ad(!block == (*err != DB_SUCCESS));
+ if (!block) {
+ goto func_exit;
}
+got_block:
UT_LIST_ADD_FIRST(rseg->undo_list, *undo);
func_exit:
@@ -1456,6 +1456,13 @@ func_exit:
return block;
}
+template buf_block_t*
+trx_undo_assign_low<false>(trx_t *trx, trx_rseg_t *rseg, trx_undo_t **undo,
+ mtr_t *mtr, dberr_t *err);
+template buf_block_t*
+trx_undo_assign_low<true>(trx_t *trx, trx_rseg_t *rseg, trx_undo_t **undo,
+ mtr_t *mtr, dberr_t *err);
+
/******************************************************************//**
Sets the state of the undo log segment at a transaction finish.
@return undo log segment header page, x-latched */
@@ -1466,6 +1473,7 @@ trx_undo_set_state_at_finish(
mtr_t* mtr) /*!< in: mtr */
{
ut_ad(undo->id < TRX_RSEG_N_SLOTS);
+ ut_ad(undo->rseg->is_persistent());
buf_block_t *block=
buf_page_get(page_id_t(undo->rseg->space->id, undo->hdr_page_no), 0,
@@ -1537,29 +1545,19 @@ the data can be discarded.
@param undo temporary undo log */
void trx_undo_commit_cleanup(trx_undo_t *undo)
{
- trx_rseg_t* rseg = undo->rseg;
- ut_ad(rseg->space == fil_system.temp_space);
-
- rseg->latch.wr_lock(SRW_LOCK_CALL);
-
- UT_LIST_REMOVE(rseg->undo_list, undo);
-
- if (undo->state == TRX_UNDO_CACHED) {
- UT_LIST_ADD_FIRST(rseg->undo_cached, undo);
- MONITOR_INC(MONITOR_NUM_UNDO_SLOT_CACHED);
- undo = nullptr;
- } else {
- ut_ad(undo->state == TRX_UNDO_TO_PURGE);
-
- /* Delete first the undo log segment in the file */
- trx_undo_seg_free(undo);
+ trx_rseg_t *rseg= undo->rseg;
+ ut_ad(rseg->space == fil_system.temp_space);
+ rseg->latch.wr_lock(SRW_LOCK_CALL);
- ut_ad(rseg->curr_size > undo->size);
- rseg->curr_size -= undo->size;
- }
+ UT_LIST_REMOVE(rseg->undo_list, undo);
+ ut_ad(undo->state == TRX_UNDO_TO_PURGE);
+ /* Delete first the undo log segment in the file */
+ trx_undo_seg_free(undo);
+ ut_ad(rseg->curr_size > undo->size);
+ rseg->curr_size-= undo->size;
- rseg->latch.wr_unlock();
- ut_free(undo);
+ rseg->latch.wr_unlock();
+ ut_free(undo);
}
/** At shutdown, frees the undo logs of a transaction. */
diff --git a/storage/rocksdb/build_rocksdb.cmake b/storage/rocksdb/build_rocksdb.cmake
index 096185af08f..27a8616cfda 100644
--- a/storage/rocksdb/build_rocksdb.cmake
+++ b/storage/rocksdb/build_rocksdb.cmake
@@ -134,8 +134,8 @@ option(WITH_FALLOCATE "build with fallocate" ON)
if(WITH_FALLOCATE AND UNIX)
include(CheckCSourceCompiles)
CHECK_C_SOURCE_COMPILES("
+#define _GNU_SOURCE
#include <fcntl.h>
-#include <linux/falloc.h>
int main() {
int fd = open(\"/dev/null\", 0);
fallocate(fd, FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE, 0, 1024);
diff --git a/storage/rocksdb/mysql-test/rocksdb/r/innodb_i_s_tables_disabled.result b/storage/rocksdb/mysql-test/rocksdb/r/innodb_i_s_tables_disabled.result
index df4c8ee424c..e4ea0bc9787 100644
--- a/storage/rocksdb/mysql-test/rocksdb/r/innodb_i_s_tables_disabled.result
+++ b/storage/rocksdb/mysql-test/rocksdb/r/innodb_i_s_tables_disabled.result
@@ -141,8 +141,8 @@ trx_commits_insert_update transaction 0 NULL NULL NULL 0 NULL NULL NULL NULL NUL
trx_rollbacks transaction 0 NULL NULL NULL 0 NULL NULL NULL NULL NULL NULL NULL 0 counter Number of transactions rolled back
trx_rollbacks_savepoint transaction 0 NULL NULL NULL 0 NULL NULL NULL NULL NULL NULL NULL 0 counter Number of transactions rolled back to savepoint
trx_rseg_history_len transaction 0 NULL NULL NULL 0 NULL NULL NULL NULL NULL NULL NULL 0 value Length of the TRX_RSEG_HISTORY list
-trx_undo_slots_used transaction 0 NULL NULL NULL 0 NULL NULL NULL NULL NULL NULL NULL 0 counter Number of undo slots used
-trx_undo_slots_cached transaction 0 NULL NULL NULL 0 NULL NULL NULL NULL NULL NULL NULL 0 counter Number of undo slots cached
+trx_undo_slots_used transaction 0 NULL NULL NULL 0 NULL NULL NULL NULL NULL NULL NULL 0 value Number of undo slots used
+trx_undo_slots_cached transaction 0 NULL NULL NULL 0 NULL NULL NULL NULL NULL NULL NULL 0 value Number of undo slots cached
trx_rseg_current_size transaction 0 NULL NULL NULL 0 NULL NULL NULL NULL NULL NULL NULL 0 value Current rollback segment size in pages
purge_del_mark_records purge 0 NULL NULL NULL 0 NULL NULL NULL NULL NULL NULL NULL 0 counter Number of delete-marked rows purged
purge_upd_exist_or_extern_records purge 0 NULL NULL NULL 0 NULL NULL NULL NULL NULL NULL NULL 0 counter Number of purges on updates of existing records and updates on delete marked record with externally stored field
diff --git a/storage/spider/mysql-test/spider/bugfix/r/mdev_29644.result b/storage/spider/mysql-test/spider/bugfix/r/mdev_29644.result
new file mode 100644
index 00000000000..b52cecc5bb7
--- /dev/null
+++ b/storage/spider/mysql-test/spider/bugfix/r/mdev_29644.result
@@ -0,0 +1,41 @@
+#
+# MDEV-29644 a potential bug of null pointer dereference in spider_db_mbase::print_warnings()
+#
+for master_1
+for child2
+child2_1
+child2_2
+child2_3
+for child3
+connection child2_1;
+CREATE DATABASE auto_test_remote;
+USE auto_test_remote;
+CREATE TABLE tbl_a (
+a CHAR(5)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+SET GLOBAL sql_mode='';
+connection master_1;
+CREATE DATABASE auto_test_local;
+USE auto_test_local;
+CREATE TABLE tbl_a (
+a CHAR(255)
+) ENGINE=Spider DEFAULT CHARSET=utf8 COMMENT='table "tbl_a", srv "s_2_1"';
+SET sql_mode='';
+INSERT INTO tbl_a VALUES ("this will be truncated");
+NOT FOUND /\[WARN SPIDER RESULT\].* Warning 1265 Data truncated for column 'a' at row 1.*/ in mysqld.1.1.err
+SET GLOBAL spider_log_result_errors=4;
+INSERT INTO tbl_a VALUES ("this will be truncated");
+FOUND 1 /\[WARN SPIDER RESULT\].* Warning 1265 Data truncated for column 'a' at row 1.*/ in mysqld.1.1.err
+connection master_1;
+SET GLOBAL spider_log_result_errors=DEFAULT;
+SET sql_mode=DEFAULT;
+DROP DATABASE IF EXISTS auto_test_local;
+connection child2_1;
+SET GLOBAL sql_mode=DEFAULT;
+DROP DATABASE IF EXISTS auto_test_remote;
+for master_1
+for child2
+child2_1
+child2_2
+child2_3
+for child3
diff --git a/storage/spider/mysql-test/spider/bugfix/r/self_reference_multi.result b/storage/spider/mysql-test/spider/bugfix/r/self_reference_multi.result
new file mode 100644
index 00000000000..8ddf428b4ea
--- /dev/null
+++ b/storage/spider/mysql-test/spider/bugfix/r/self_reference_multi.result
@@ -0,0 +1,21 @@
+for master_1
+for child2
+for child3
+
+MDEV-6268 SPIDER table with no COMMENT clause causes queries to wait forever
+
+CREATE SERVER srv FOREIGN DATA WRAPPER MYSQL OPTIONS (SOCKET "$MASTER_1_MYSOCK", DATABASE 'test',user 'root');
+create table t2 (c int);
+create table t1 (c int) ENGINE=Spider COMMENT='WRAPPER "mysql", srv "srv",TABLE "t2"';
+create table t0 (c int) ENGINE=Spider COMMENT='WRAPPER "mysql", srv "srv",TABLE "t1"';
+alter table t2 ENGINE=Spider COMMENT='WRAPPER "mysql", srv "srv",TABLE "t0"';
+select * from t0;
+ERROR HY000: An infinite loop is detected when opening table test.t0
+select * from t1;
+ERROR HY000: An infinite loop is detected when opening table test.t1
+select * from t2;
+ERROR HY000: An infinite loop is detected when opening table test.t2
+drop table t0, t1, t2;
+for master_1
+for child2
+for child3
diff --git a/storage/spider/mysql-test/spider/bugfix/t/mdev_29644.cnf b/storage/spider/mysql-test/spider/bugfix/t/mdev_29644.cnf
new file mode 100644
index 00000000000..05dfd8a0bce
--- /dev/null
+++ b/storage/spider/mysql-test/spider/bugfix/t/mdev_29644.cnf
@@ -0,0 +1,3 @@
+!include include/default_mysqld.cnf
+!include ../my_1_1.cnf
+!include ../my_2_1.cnf
diff --git a/storage/spider/mysql-test/spider/bugfix/t/mdev_29644.test b/storage/spider/mysql-test/spider/bugfix/t/mdev_29644.test
new file mode 100644
index 00000000000..3a8fbb251e1
--- /dev/null
+++ b/storage/spider/mysql-test/spider/bugfix/t/mdev_29644.test
@@ -0,0 +1,56 @@
+--echo #
+--echo # MDEV-29644 a potential bug of null pointer dereference in spider_db_mbase::print_warnings()
+--echo #
+
+# The test case below does not cause the potential null pointer dereference.
+# It is just for checking spider_db_mbase::fetch_and_print_warnings() works.
+
+--disable_query_log
+--disable_result_log
+--source ../../t/test_init.inc
+--enable_result_log
+--enable_query_log
+
+--connection child2_1
+CREATE DATABASE auto_test_remote;
+USE auto_test_remote;
+eval CREATE TABLE tbl_a (
+ a CHAR(5)
+) $CHILD2_1_ENGINE $CHILD2_1_CHARSET;
+
+SET GLOBAL sql_mode='';
+
+--connection master_1
+CREATE DATABASE auto_test_local;
+USE auto_test_local;
+eval CREATE TABLE tbl_a (
+ a CHAR(255)
+) $MASTER_1_ENGINE $MASTER_1_CHARSET COMMENT='table "tbl_a", srv "s_2_1"';
+
+SET sql_mode='';
+
+let SEARCH_FILE= $MYSQLTEST_VARDIR/log/mysqld.1.1.err;
+let SEARCH_PATTERN= \[WARN SPIDER RESULT\].* Warning 1265 Data truncated for column 'a' at row 1.*;
+
+INSERT INTO tbl_a VALUES ("this will be truncated");
+--source include/search_pattern_in_file.inc # should not find
+
+SET GLOBAL spider_log_result_errors=4;
+
+INSERT INTO tbl_a VALUES ("this will be truncated");
+--source include/search_pattern_in_file.inc # should find
+
+--connection master_1
+SET GLOBAL spider_log_result_errors=DEFAULT;
+SET sql_mode=DEFAULT;
+DROP DATABASE IF EXISTS auto_test_local;
+
+--connection child2_1
+SET GLOBAL sql_mode=DEFAULT;
+DROP DATABASE IF EXISTS auto_test_remote;
+
+--disable_query_log
+--disable_result_log
+--source ../t/test_deinit.inc
+--enable_query_log
+--enable_result_log
diff --git a/storage/spider/mysql-test/spider/bugfix/t/self_reference_multi.test b/storage/spider/mysql-test/spider/bugfix/t/self_reference_multi.test
new file mode 100644
index 00000000000..8b6f070d167
--- /dev/null
+++ b/storage/spider/mysql-test/spider/bugfix/t/self_reference_multi.test
@@ -0,0 +1,29 @@
+--disable_query_log
+--disable_result_log
+--source ../../t/test_init.inc
+--enable_result_log
+--enable_query_log
+
+--echo
+--echo MDEV-6268 SPIDER table with no COMMENT clause causes queries to wait forever
+--echo
+
+--replace_regex /SOCKET ".*"/SOCKET "$MASTER_1_MYSOCK"/
+eval CREATE SERVER srv FOREIGN DATA WRAPPER MYSQL OPTIONS (SOCKET "$MASTER_1_MYSOCK", DATABASE 'test',user 'root');
+create table t2 (c int);
+create table t1 (c int) ENGINE=Spider COMMENT='WRAPPER "mysql", srv "srv",TABLE "t2"';
+create table t0 (c int) ENGINE=Spider COMMENT='WRAPPER "mysql", srv "srv",TABLE "t1"';
+alter table t2 ENGINE=Spider COMMENT='WRAPPER "mysql", srv "srv",TABLE "t0"';
+--error 12719
+select * from t0;
+--error 12719
+select * from t1;
+--error 12719
+select * from t2;
+drop table t0, t1, t2;
+
+--disable_query_log
+--disable_result_log
+--source ../../t/test_deinit.inc
+--enable_result_log
+--enable_query_log
diff --git a/storage/spider/spd_db_mysql.cc b/storage/spider/spd_db_mysql.cc
index 78236243bf2..303d3ad3e00 100644
--- a/storage/spider/spd_db_mysql.cc
+++ b/storage/spider/spd_db_mysql.cc
@@ -2141,7 +2141,7 @@ int spider_db_mbase::exec_query(
db_conn->affected_rows, db_conn->insert_id,
db_conn->server_status, db_conn->warning_count);
if (spider_param_log_result_errors() >= 3)
- print_warnings(l_time);
+ fetch_and_print_warnings(l_time);
} else if (log_result_errors >= 4)
{
time_t cur_time = (time_t) time((time_t*) 0);
@@ -2223,77 +2223,33 @@ bool spider_db_mbase::is_xa_nota_error(
DBUG_RETURN(xa_nota);
}
-int spider_db_mbase::print_warnings(
- struct tm *l_time
-) {
- int error_num = 0;
- DBUG_ENTER("spider_db_mbase::print_warnings");
- DBUG_PRINT("info",("spider this=%p", this));
- if (db_conn->status == MYSQL_STATUS_READY)
- {
- if (
- !(db_conn->server_status & SERVER_MORE_RESULTS_EXISTS) &&
- db_conn->warning_count
- ) {
- if (
- spider_param_dry_access() ||
- !mysql_real_query(db_conn, SPIDER_SQL_SHOW_WARNINGS_STR,
- SPIDER_SQL_SHOW_WARNINGS_LEN)
- ) {
- MYSQL_RES *res = NULL;
- MYSQL_ROW row = NULL;
- uint num_fields;
- if (
- spider_param_dry_access() ||
- !(res = mysql_store_result(db_conn)) ||
- !(row = mysql_fetch_row(res))
- ) {
- if (mysql_errno(db_conn))
- {
- if (res)
- mysql_free_result(res);
- DBUG_RETURN(0);
- }
- /* no record is ok */
- }
- num_fields = mysql_num_fields(res);
- if (num_fields != 3)
- {
- mysql_free_result(res);
- DBUG_RETURN(0);
- }
- if (l_time)
- {
- while (row)
- {
- fprintf(stderr, "%04d%02d%02d %02d:%02d:%02d [WARN SPIDER RESULT] "
- "from [%s] %ld to %ld: %s %s %s\n",
+void spider_db_mbase::fetch_and_print_warnings(struct tm *l_time)
+{
+ DBUG_ENTER("spider_db_mbase::fetch_and_print_warnings");
+
+ if (spider_param_dry_access() || db_conn->status != MYSQL_STATUS_READY ||
+ db_conn->server_status & SERVER_MORE_RESULTS_EXISTS)
+ DBUG_VOID_RETURN;
+
+ if (mysql_real_query(db_conn, SPIDER_SQL_SHOW_WARNINGS_STR,
+ SPIDER_SQL_SHOW_WARNINGS_LEN))
+ DBUG_VOID_RETURN;
+
+ MYSQL_RES *res= mysql_store_result(db_conn);
+ if (!res)
+ DBUG_VOID_RETURN;
+
+ if (mysql_num_fields(res) == 3)
+ while (MYSQL_ROW row= mysql_fetch_row(res))
+ fprintf(stderr,
+ "%04d%02d%02d %02d:%02d:%02d [WARN SPIDER RESULT] from [%s] %ld "
+ "to %ld: %s %s %s\n",
l_time->tm_year + 1900, l_time->tm_mon + 1, l_time->tm_mday,
- l_time->tm_hour, l_time->tm_min, l_time->tm_sec,
- conn->tgt_host, (ulong) db_conn->thread_id,
- (ulong) current_thd->thread_id, row[0], row[1], row[2]);
- row = mysql_fetch_row(res);
- }
- } else {
- while (row)
- {
- DBUG_PRINT("info",("spider row[0]=%s", row[0]));
- DBUG_PRINT("info",("spider row[1]=%s", row[1]));
- DBUG_PRINT("info",("spider row[2]=%s", row[2]));
- longlong res_num =
- (longlong) my_strtoll10(row[1], (char**) NULL, &error_num);
- DBUG_PRINT("info",("spider res_num=%lld", res_num));
- my_printf_error((int) res_num, row[2], MYF(0));
- error_num = (int) res_num;
- row = mysql_fetch_row(res);
- }
- }
- if (res)
- mysql_free_result(res);
- }
- }
- }
- DBUG_RETURN(error_num);
+ l_time->tm_hour, l_time->tm_min, l_time->tm_sec, conn->tgt_host,
+ (ulong) db_conn->thread_id, (ulong) current_thd->thread_id,
+ row[0], row[1], row[2]);
+ mysql_free_result(res);
+ DBUG_VOID_RETURN;
}
spider_db_result *spider_db_mbase::store_result(
@@ -13851,9 +13807,11 @@ int spider_mbase_handler::show_table_status(
DBUG_RETURN(error_num);
}
}
- if ((error_num = ((spider_db_mbase *) conn->db_conn)->print_warnings(NULL)))
{
- DBUG_RETURN(error_num);
+ time_t cur_time = (time_t) time((time_t*) 0);
+ struct tm lt;
+ struct tm *l_time = localtime_r(&cur_time, &lt);
+ ((spider_db_mbase *) conn->db_conn)->fetch_and_print_warnings(l_time);
}
if (share->static_records_for_status != -1)
{
diff --git a/storage/spider/spd_db_mysql.h b/storage/spider/spd_db_mysql.h
index 5cfd3e693d5..725b08044b3 100644
--- a/storage/spider/spd_db_mysql.h
+++ b/storage/spider/spd_db_mysql.h
@@ -434,9 +434,7 @@ public:
bool is_xa_nota_error(
int error_num
);
- int print_warnings(
- struct tm *l_time
- );
+ void fetch_and_print_warnings(struct tm *l_time);
spider_db_result *store_result(
spider_db_result_buffer **spider_res_buf,
st_spider_db_request_key *request_key,