summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarko Mäkelä <marko.makela@mariadb.com>2020-12-02 18:29:49 +0200
committerMarko Mäkelä <marko.makela@mariadb.com>2020-12-02 18:29:49 +0200
commit6a1e655cb0d400d8ed44fbfd22151b1d3e7f22d8 (patch)
tree3915bd814fa0ea7c0219b258a1fb458846a5b0bd
parente76e12886fd37094d95ca40b8cce45ca3ef28021 (diff)
parent24ec8eaf66ecd864529309c5edcb8dd6b685f2aa (diff)
downloadmariadb-git-6a1e655cb0d400d8ed44fbfd22151b1d3e7f22d8.tar.gz
Merge 10.4 into 10.5
-rw-r--r--extra/mariabackup/backup_copy.cc32
-rw-r--r--extra/mariabackup/backup_copy.h3
-rw-r--r--extra/mariabackup/backup_debug.h32
-rw-r--r--extra/mariabackup/encryption_plugin.cc1
-rw-r--r--extra/mariabackup/fil_cur.cc57
-rw-r--r--extra/mariabackup/fil_cur.h17
-rw-r--r--extra/mariabackup/write_filt.cc27
-rw-r--r--extra/mariabackup/write_filt.h8
-rw-r--r--extra/mariabackup/xtrabackup.cc417
-rw-r--r--extra/mariabackup/xtrabackup.h28
-rw-r--r--mysql-test/lib/mtr_report.pm10
-rw-r--r--mysql-test/main/auto_increment_ranges_innodb.result48
-rw-r--r--mysql-test/main/auto_increment_ranges_innodb.test59
-rw-r--r--mysql-test/main/derived_cond_pushdown.result41
-rw-r--r--mysql-test/main/derived_cond_pushdown.test28
-rw-r--r--mysql-test/main/information_schema.result9
-rw-r--r--mysql-test/main/information_schema.test10
-rw-r--r--mysql-test/main/lock_view.test1
-rw-r--r--mysql-test/main/mysqldump-system.test4
-rw-r--r--mysql-test/main/partition_innodb.result2
-rw-r--r--mysql-test/main/sp.result19
-rw-r--r--mysql-test/main/sp.test23
-rw-r--r--mysql-test/main/subselect4.result35
-rw-r--r--mysql-test/main/subselect4.test39
-rw-r--r--mysql-test/main/xa.result28
-rw-r--r--mysql-test/main/xa.test42
-rw-r--r--mysql-test/suite/binlog/r/binlog_mysqlbinlog_suppress_O_TMPFILE.result21
-rw-r--r--mysql-test/suite/binlog/t/binlog_mysqlbinlog_suppress_O_TMPFILE.test51
-rw-r--r--mysql-test/suite/galera/disabled.def4
-rw-r--r--mysql-test/suite/galera/r/galera#505.result1
-rw-r--r--mysql-test/suite/galera/r/galera_as_slave_replay.result95
-rw-r--r--mysql-test/suite/galera/r/galera_defaults.result5
-rw-r--r--mysql-test/suite/galera/r/galera_toi_alter_auto_increment.result2
-rw-r--r--mysql-test/suite/galera/t/MDEV-16509.test1
-rw-r--r--mysql-test/suite/galera/t/galera#505.test5
-rw-r--r--mysql-test/suite/galera/t/galera_as_slave_replay.cnf11
-rw-r--r--mysql-test/suite/galera/t/galera_as_slave_replay.test200
-rw-r--r--mysql-test/suite/galera/t/galera_defaults.cnf7
-rw-r--r--mysql-test/suite/galera/t/galera_defaults.test11
-rw-r--r--mysql-test/suite/galera/t/galera_fk_cascade_delete.test8
-rw-r--r--mysql-test/suite/galera/t/galera_rsu_simple.test3
-rw-r--r--mysql-test/suite/galera/t/galera_toi_alter_auto_increment.test4
-rw-r--r--mysql-test/suite/galera/t/galera_var_ignore_apply_errors.test26
-rw-r--r--mysql-test/suite/galera_3nodes/r/galera_parallel_apply_3nodes.result26
-rw-r--r--mysql-test/suite/galera_3nodes/t/galera_parallel_apply_3nodes.test59
-rw-r--r--mysql-test/suite/innodb/r/foreign_key.result1
-rw-r--r--mysql-test/suite/innodb/r/instant_alter_crash.result30
-rw-r--r--mysql-test/suite/innodb/t/foreign_key.test1
-rw-r--r--mysql-test/suite/innodb/t/instant_alter_crash.test28
-rw-r--r--mysql-test/suite/maria/create.opt1
-rw-r--r--mysql-test/suite/mariabackup/include/corrupt-page.pl146
-rw-r--r--mysql-test/suite/mariabackup/incremental_ddl_during_backup.test2
-rw-r--r--mysql-test/suite/mariabackup/log_page_corruption.opt1
-rw-r--r--mysql-test/suite/mariabackup/log_page_corruption.result145
-rw-r--r--mysql-test/suite/mariabackup/log_page_corruption.test426
-rw-r--r--mysql-test/suite/roles/show_grants.result15
-rw-r--r--mysql-test/suite/roles/show_grants.test13
-rw-r--r--mysql-test/suite/wsrep/include/check_galera_version.inc12
-rw-r--r--mysys/my_mess.c2
-rw-r--r--plugin/handler_socket/handlersocket/database.cpp4
-rw-r--r--plugin/type_inet/sql_type_inet.h2
-rw-r--r--scripts/wsrep_sst_rsync.sh8
-rw-r--r--sql/ha_partition.cc2
-rw-r--r--sql/ha_partition.h20
-rw-r--r--sql/handler.cc9
-rw-r--r--sql/handler.h15
-rw-r--r--sql/item.cc8
-rw-r--r--sql/item.h7
-rw-r--r--sql/item_cmpfunc.cc11
-rw-r--r--sql/item_subselect.cc3
-rw-r--r--sql/item_subselect.h8
-rw-r--r--sql/log_event_server.cc9
-rw-r--r--sql/mdl.cc6
-rw-r--r--sql/mdl.h2
-rw-r--r--sql/opt_subselect.cc8
-rw-r--r--sql/rpl_gtid.cc6
-rw-r--r--sql/rpl_injector.cc56
-rw-r--r--sql/rpl_rli.cc14
-rw-r--r--sql/sp.cc6
-rw-r--r--sql/sp_head.cc8
-rw-r--r--sql/sql_acl.cc2
-rw-r--r--sql/sql_admin.cc20
-rw-r--r--sql/sql_base.cc5
-rw-r--r--sql/sql_class.cc4
-rw-r--r--sql/sql_class.h7
-rw-r--r--sql/sql_delete.cc4
-rw-r--r--sql/sql_derived.cc19
-rw-r--r--sql/sql_insert.cc15
-rw-r--r--sql/sql_parse.cc30
-rw-r--r--sql/sql_prepare.cc2
-rw-r--r--sql/sql_select.cc4
-rw-r--r--sql/sql_select.h11
-rw-r--r--sql/sql_show.cc21
-rw-r--r--sql/sql_statistics.cc4
-rw-r--r--sql/sql_table.cc4
-rw-r--r--sql/sql_tvc.cc10
-rw-r--r--sql/sql_type.cc44
-rw-r--r--sql/sql_type.h45
-rw-r--r--sql/sql_type_geom.h4
-rw-r--r--sql/sql_union.cc1
-rw-r--r--sql/sys_vars.cc2
-rw-r--r--sql/table.cc4
-rw-r--r--sql/transaction.cc2
-rw-r--r--sql/unireg.cc4
-rw-r--r--sql/wsrep_client_service.cc2
-rw-r--r--sql/wsrep_high_priority_service.cc6
-rw-r--r--sql/wsrep_mysqld.cc2
-rw-r--r--sql/wsrep_schema.cc2
-rw-r--r--sql/wsrep_server_service.cc2
-rw-r--r--sql/wsrep_storage_service.cc4
-rw-r--r--sql/xa.cc10
-rw-r--r--storage/innobase/dict/dict0dict.cc1
-rw-r--r--storage/innobase/dict/dict0stats.cc2
-rw-r--r--storage/innobase/handler/handler0alter.cc2
-rw-r--r--storage/innobase/include/row0log.h4
-rw-r--r--storage/innobase/include/row0log.ic2
-rw-r--r--storage/innobase/mtr/mtr0mtr.cc4
-rw-r--r--storage/innobase/row/row0log.cc5
-rw-r--r--storage/innobase/row/row0uins.cc7
-rw-r--r--storage/innobase/row/row0umod.cc2
-rw-r--r--storage/mroonga/ha_mroonga.hpp5
-rw-r--r--storage/oqgraph/CMakeLists.txt4
-rw-r--r--storage/rocksdb/mysql-test/rocksdb/t/checkpoint.test2
123 files changed, 2481 insertions, 450 deletions
diff --git a/extra/mariabackup/backup_copy.cc b/extra/mariabackup/backup_copy.cc
index 711f4aa666f..1972bceacb7 100644
--- a/extra/mariabackup/backup_copy.cc
+++ b/extra/mariabackup/backup_copy.cc
@@ -863,21 +863,14 @@ datafile_rsync_backup(const char *filepath, bool save_to_list, FILE *f)
return(true);
}
-
-static
-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)
{
ds_file_t *dstfile = NULL;
MY_STAT stat; /* unused for now */
- char *buf = 0;
- int buf_len;
const char *action;
memset(&stat, 0, sizeof(stat));
- buf_len = vasprintf(&buf, fmt, ap);
-
stat.st_size = buf_len;
stat.st_mtime = my_time(0);
@@ -901,7 +894,6 @@ backup_file_vprintf(const char *filename, const char *fmt, va_list ap)
/* close */
msg(" ...done");
- free(buf);
if (ds_close(dstfile)) {
goto error_close;
@@ -910,7 +902,6 @@ backup_file_vprintf(const char *filename, const char *fmt, va_list ap)
return(true);
error:
- free(buf);
if (dstfile != NULL) {
ds_close(dstfile);
}
@@ -918,8 +909,21 @@ error:
error_close:
msg("Error: backup file failed.");
return(false); /*ERROR*/
-}
+ return true;
+};
+
+static
+bool
+backup_file_vprintf(const char *filename, const char *fmt, va_list ap)
+{
+ char *buf = 0;
+ int buf_len;
+ buf_len = vasprintf(&buf, fmt, ap);
+ bool result = backup_file_print_buf(filename, buf, buf_len);
+ free(buf);
+ return result;
+}
bool
backup_file_printf(const char *filename, const char *fmt, ...)
@@ -1441,7 +1445,7 @@ out:
return(ret);
}
-void backup_fix_ddl(void);
+void backup_fix_ddl(CorruptedPages &);
lsn_t get_current_lsn(MYSQL *connection)
{
@@ -1466,7 +1470,7 @@ 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()
+bool backup_start(CorruptedPages &corrupted_pages)
{
if (!opt_no_lock) {
if (opt_safe_slave_backup) {
@@ -1501,7 +1505,7 @@ bool backup_start()
msg("Waiting for log copy thread to read lsn %llu", (ulonglong)server_lsn_after_lock);
backup_wait_for_lsn(server_lsn_after_lock);
- backup_fix_ddl();
+ backup_fix_ddl(corrupted_pages);
// 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
diff --git a/extra/mariabackup/backup_copy.h b/extra/mariabackup/backup_copy.h
index 7c886719f37..62b2b1bc232 100644
--- a/extra/mariabackup/backup_copy.h
+++ b/extra/mariabackup/backup_copy.h
@@ -33,7 +33,7 @@ copy_file(ds_ctxt_t *datasink,
uint thread_n);
/** Start --backup */
-bool backup_start();
+bool backup_start(CorruptedPages &corrupted_pages);
/** Release resources after backup_start() */
void backup_release();
/** Finish after backup_start() and backup_release() */
@@ -51,5 +51,6 @@ 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_debug.h b/extra/mariabackup/backup_debug.h
new file mode 100644
index 00000000000..cefbc287361
--- /dev/null
+++ b/extra/mariabackup/backup_debug.h
@@ -0,0 +1,32 @@
+#pragma once
+#include "my_dbug.h"
+#ifndef DBUG_OFF
+extern char *dbug_mariabackup_get_val(const char *event, const char *key);
+/*
+In debug mode, execute SQL statement that was passed via environment.
+To use this facility, you need to
+
+1. Add code DBUG_EXECUTE_MARIABACKUP_EVENT("my_event_name", key););
+ to the code. key is usually a table name
+2. Set environment variable my_event_name_$key SQL statement you want to execute
+ when event occurs, in DBUG_EXECUTE_IF from above.
+ In mtr , you can set environment via 'let' statement (do not use $ as the first char
+ for the variable)
+3. start mariabackup with --dbug=+d,debug_mariabackup_events
+*/
+extern void dbug_mariabackup_event(
+ const char *event,const char *key);
+#define DBUG_MARIABACKUP_EVENT(A, B) \
+ DBUG_EXECUTE_IF("mariabackup_events", \
+ dbug_mariabackup_event(A,B););
+#define DBUG_EXECUTE_FOR_KEY(EVENT, KEY, CODE) \
+ DBUG_EXECUTE_IF("mariabackup_inject_code", {\
+ char *dbug_val = dbug_mariabackup_get_val(EVENT, KEY); \
+ if (dbug_val && *dbug_val) CODE \
+ })
+#else
+#define DBUG_MARIABACKUP_EVENT(A,B)
+#define DBUG_MARIABACKUP_EVENT_LOCK(A,B)
+#define DBUG_EXECUTE_FOR_KEY(EVENT, KEY, CODE)
+#endif
+
diff --git a/extra/mariabackup/encryption_plugin.cc b/extra/mariabackup/encryption_plugin.cc
index a3242078293..dbaa67e1324 100644
--- a/extra/mariabackup/encryption_plugin.cc
+++ b/extra/mariabackup/encryption_plugin.cc
@@ -18,7 +18,6 @@
#include <mysql.h>
#include <xtrabackup.h>
#include <encryption_plugin.h>
-#include <backup_copy.h>
#include <sql_plugin.h>
#include <sstream>
#include <vector>
diff --git a/extra/mariabackup/fil_cur.cc b/extra/mariabackup/fil_cur.cc
index 4f9e493b347..1364b337bec 100644
--- a/extra/mariabackup/fil_cur.cc
+++ b/extra/mariabackup/fil_cur.cc
@@ -35,6 +35,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA
#include "common.h"
#include "read_filt.h"
#include "xtrabackup.h"
+#include "backup_debug.h"
/* Size of read buffer in pages (640 pages = 10M for 16K sized pages) */
#define XB_FIL_CUR_PAGES 640
@@ -351,19 +352,18 @@ static bool page_is_corrupted(const byte *page, ulint page_no,
return buf_page_is_corrupted(true, page, space->flags);
}
-/************************************************************************
-Reads and verifies the next block of pages from the source
+/** Reads and verifies the next block of pages from the source
file. Positions the cursor after the last read non-corrupted page.
-
+@param[in,out] cursor source file cursor
+@param[out] corrupted_pages adds corrupted pages if
+opt_log_innodb_page_corruption is set
@return XB_FIL_CUR_SUCCESS if some have been read successfully, XB_FIL_CUR_EOF
if there are no more pages to read and XB_FIL_CUR_ERROR on error. */
-xb_fil_cur_result_t
-xb_fil_cur_read(
-/*============*/
- xb_fil_cur_t* cursor) /*!< in/out: source file cursor */
+xb_fil_cur_result_t xb_fil_cur_read(xb_fil_cur_t* cursor,
+ CorruptedPages &corrupted_pages)
{
byte* page;
- ulint i;
+ unsigned i;
ulint npages;
ulint retry_count;
xb_fil_cur_result_t ret;
@@ -417,7 +417,7 @@ read_retry:
cursor->buf_read = 0;
cursor->buf_npages = 0;
cursor->buf_offset = offset;
- cursor->buf_page_no = (ulint)(offset / page_size);
+ cursor->buf_page_no = static_cast<unsigned>(offset / page_size);
if (os_file_read(IORequestRead, cursor->file, cursor->buf, offset,
(ulint) to_read) != DB_SUCCESS) {
@@ -428,26 +428,47 @@ read_retry:
partially written pages */
for (page = cursor->buf, i = 0; i < npages;
page += page_size, i++) {
- ulint page_no = cursor->buf_page_no + i;
+ unsigned page_no = cursor->buf_page_no + i;
if (page_is_corrupted(page, page_no, cursor, space)){
retry_count--;
if (retry_count == 0) {
+ const char *ignore_corruption_warn = opt_log_innodb_page_corruption ?
+ " WARNING!!! The corruption is ignored due to"
+ " log-innodb-page-corruption option, the backup can contain"
+ " corrupted data." : "";
msg(cursor->thread_n,
"Error: failed to read page after "
"10 retries. File %s seems to be "
- "corrupted.", cursor->abs_path);
- ret = XB_FIL_CUR_ERROR;
+ "corrupted.%s", cursor->abs_path, ignore_corruption_warn);
ut_print_buf(stderr, page, page_size);
- break;
+ if (opt_log_innodb_page_corruption) {
+ corrupted_pages.add_page(cursor->node->name, cursor->node->space->id,
+ page_no);
+ retry_count = 1;
+ }
+ else {
+ ret = XB_FIL_CUR_ERROR;
+ break;
+ }
+ }
+ else {
+ msg(cursor->thread_n, "Database page corruption detected at page "
+ UINT32PF ", retrying...",
+ page_no);
+ os_thread_sleep(100000);
+ goto read_retry;
}
- msg(cursor->thread_n, "Database page corruption detected at page "
- ULINTPF ", retrying...",
- page_no);
- os_thread_sleep(100000);
- goto read_retry;
}
+ DBUG_EXECUTE_FOR_KEY("add_corrupted_page_for", cursor->node->space->name,
+ {
+ unsigned corrupted_page_no =
+ static_cast<unsigned>(strtoul(dbug_val, NULL, 10));
+ if (page_no == corrupted_page_no)
+ corrupted_pages.add_page(cursor->node->name, cursor->node->space->id,
+ corrupted_page_no);
+ });
cursor->buf_read += page_size;
cursor->buf_npages++;
}
diff --git a/extra/mariabackup/fil_cur.h b/extra/mariabackup/fil_cur.h
index 70e4888ba63..0027b7768e9 100644
--- a/extra/mariabackup/fil_cur.h
+++ b/extra/mariabackup/fil_cur.h
@@ -29,6 +29,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA
#include "read_filt.h"
#include "srv0start.h"
#include "srv0srv.h"
+#include "xtrabackup.h"
struct xb_fil_cur_t {
pfs_os_file_t file; /*!< source file handle */
@@ -52,7 +53,7 @@ struct xb_fil_cur_t {
last cursor read */
ib_int64_t buf_offset; /*!< file offset of the first page in
buffer */
- ulint buf_page_no; /*!< number of the first page in
+ unsigned buf_page_no; /*!< number of the first page in
buffer */
uint thread_n; /*!< thread number for diagnostics */
ulint space_id; /*!< ID of tablespace */
@@ -88,17 +89,15 @@ xb_fil_cur_open(
uint thread_n, /*!< thread number for diagnostics */
ulonglong max_file_size = ULLONG_MAX);
-/************************************************************************
-Reads and verifies the next block of pages from the source
+/** Reads and verifies the next block of pages from the source
file. Positions the cursor after the last read non-corrupted page.
-
+@param[in,out] cursor source file cursor
+@param[out] corrupted_pages adds corrupted pages if
+opt_log_innodb_page_corruption is set
@return XB_FIL_CUR_SUCCESS if some have been read successfully, XB_FIL_CUR_EOF
if there are no more pages to read and XB_FIL_CUR_ERROR on error. */
-xb_fil_cur_result_t
-xb_fil_cur_read(
-/*============*/
- xb_fil_cur_t* cursor); /*!< in/out: source file cursor */
-
+xb_fil_cur_result_t xb_fil_cur_read(xb_fil_cur_t *cursor,
+ CorruptedPages &corrupted_pages);
/************************************************************************
Close the source file cursor opened with xb_fil_cur_open() and its
associated read filter. */
diff --git a/extra/mariabackup/write_filt.cc b/extra/mariabackup/write_filt.cc
index d8910699d16..8339286e1df 100644
--- a/extra/mariabackup/write_filt.cc
+++ b/extra/mariabackup/write_filt.cc
@@ -32,7 +32,7 @@ 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,
- xb_fil_cur_t *cursor);
+ xb_fil_cur_t *cursor, CorruptedPages *corrupted_pages);
static my_bool wf_wt_process(xb_write_filt_ctxt_t *ctxt, ds_file_t *dstfile);
xb_write_filt_t wf_write_through = {
@@ -45,7 +45,7 @@ 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,
- xb_fil_cur_t *cursor);
+ xb_fil_cur_t *cursor, CorruptedPages *corrupted_pages);
static my_bool wf_incremental_process(xb_write_filt_ctxt_t *ctxt,
ds_file_t *dstfile);
static my_bool wf_incremental_finalize(xb_write_filt_ctxt_t *ctxt,
@@ -65,11 +65,11 @@ 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,
- xb_fil_cur_t *cursor)
+ xb_fil_cur_t *cursor, CorruptedPages *corrupted_pages)
{
char meta_name[FN_REFLEN];
xb_wf_incremental_ctxt_t *cp =
- &(ctxt->u.wf_incremental_ctxt);
+ &(ctxt->wf_incremental_ctxt);
ctxt->cursor = cursor;
@@ -100,7 +100,9 @@ wf_incremental_init(xb_write_filt_ctxt_t *ctxt, char *dst_name,
strcat(dst_name, ".delta");
mach_write_to_4(cp->delta_buf, 0x78747261UL); /*"xtra"*/
+
cp->npages = 1;
+ cp->corrupted_pages = corrupted_pages;
return(TRUE);
}
@@ -112,19 +114,20 @@ Run the next batch of pages through incremental page write filter.
static my_bool
wf_incremental_process(xb_write_filt_ctxt_t *ctxt, ds_file_t *dstfile)
{
- ulint i;
+ unsigned i;
xb_fil_cur_t *cursor = ctxt->cursor;
byte *page;
const ulint page_size = cursor->page_size;
- xb_wf_incremental_ctxt_t *cp = &(ctxt->u.wf_incremental_ctxt);
+ xb_wf_incremental_ctxt_t *cp = &(ctxt->wf_incremental_ctxt);
for (i = 0, page = cursor->buf; i < cursor->buf_npages;
i++, page += page_size) {
- if (incremental_lsn >= mach_read_from_8(page + FIL_PAGE_LSN)) {
-
+ if ((!cp->corrupted_pages ||
+ !cp->corrupted_pages->contains(cursor->node->space->id,
+ cursor->buf_page_no + i)) &&
+ incremental_lsn >= mach_read_from_8(page + FIL_PAGE_LSN))
continue;
- }
/* updated page */
if (cp->npages == page_size / 4) {
@@ -161,7 +164,7 @@ wf_incremental_finalize(xb_write_filt_ctxt_t *ctxt, ds_file_t *dstfile)
{
xb_fil_cur_t *cursor = ctxt->cursor;
const ulint page_size = cursor->page_size;
- xb_wf_incremental_ctxt_t *cp = &(ctxt->u.wf_incremental_ctxt);
+ xb_wf_incremental_ctxt_t *cp = &(ctxt->wf_incremental_ctxt);
if (cp->npages != page_size / 4) {
mach_write_to_4(cp->delta_buf + cp->npages * 4, 0xFFFFFFFFUL);
@@ -183,7 +186,7 @@ Free the incremental page write filter's buffer. */
static void
wf_incremental_deinit(xb_write_filt_ctxt_t *ctxt)
{
- xb_wf_incremental_ctxt_t *cp = &(ctxt->u.wf_incremental_ctxt);
+ xb_wf_incremental_ctxt_t *cp = &(ctxt->wf_incremental_ctxt);
my_large_free(cp->delta_buf, cp->delta_buf_size);
}
@@ -193,7 +196,7 @@ 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)),
- xb_fil_cur_t *cursor)
+ xb_fil_cur_t *cursor, CorruptedPages *)
{
ctxt->cursor = cursor;
diff --git a/extra/mariabackup/write_filt.h b/extra/mariabackup/write_filt.h
index febf25f2a8a..6c3ef24291f 100644
--- a/extra/mariabackup/write_filt.h
+++ b/extra/mariabackup/write_filt.h
@@ -27,26 +27,26 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA
#include "fil_cur.h"
#include "datasink.h"
+#include "xtrabackup.h"
/* Incremental page filter context */
typedef struct {
ulint delta_buf_size;
byte *delta_buf;
ulint npages;
+ CorruptedPages *corrupted_pages;
} xb_wf_incremental_ctxt_t;
/* Page filter context used as an opaque structure by callers */
typedef struct {
xb_fil_cur_t *cursor;
- union {
- xb_wf_incremental_ctxt_t wf_incremental_ctxt;
- } u;
+ xb_wf_incremental_ctxt_t wf_incremental_ctxt;
} xb_write_filt_ctxt_t;
typedef struct {
my_bool (*init)(xb_write_filt_ctxt_t *ctxt, char *dst_name,
- xb_fil_cur_t *cursor);
+ 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);
void (*deinit)(xb_write_filt_ctxt_t *);
diff --git a/extra/mariabackup/xtrabackup.cc b/extra/mariabackup/xtrabackup.cc
index be865b0fea0..d44e212d58a 100644
--- a/extra/mariabackup/xtrabackup.cc
+++ b/extra/mariabackup/xtrabackup.cc
@@ -77,6 +77,7 @@ Street, Fifth Floor, Boston, MA 02110-1335 USA
#include <list>
#include <sstream>
#include <set>
+#include <fstream>
#include <mysql.h>
#define G_PTR uchar*
@@ -104,6 +105,9 @@ Street, Fifth Floor, Boston, MA 02110-1335 USA
#include <log.h>
#include <derror.h>
#include <thr_timer.h>
+#include "backup_debug.h"
+
+#define MB_CORRUPTED_PAGES_FILE "innodb_corrupted_pages"
int sys_var_init();
@@ -287,6 +291,7 @@ my_bool opt_noversioncheck = FALSE;
my_bool opt_no_backup_locks = FALSE;
my_bool opt_decompress = FALSE;
my_bool opt_remove_original;
+my_bool opt_log_innodb_page_corruption;
my_bool opt_lock_ddl_per_table = FALSE;
static my_bool opt_check_privileges;
@@ -349,6 +354,212 @@ struct ddl_tracker_t {
static ddl_tracker_t ddl_tracker;
+// Convert non-null terminated filename to space name
+std::string filename_to_spacename(const byte *filename, size_t len);
+
+CorruptedPages::CorruptedPages() { ut_a(!pthread_mutex_init(&m_mutex, NULL)); }
+
+CorruptedPages::~CorruptedPages() { ut_a(!pthread_mutex_destroy(&m_mutex)); }
+
+void CorruptedPages::add_page_no_lock(const char *space_name, ulint space_id,
+ unsigned page_no,
+ bool convert_space_name)
+{
+ space_info_t &space_info = m_spaces[space_id];
+ if (space_info.space_name.empty())
+ space_info.space_name=
+ convert_space_name
+ ? filename_to_spacename(reinterpret_cast<const byte *>(space_name),
+ strlen(space_name))
+ : space_name;
+ (void)space_info.pages.insert(page_no);
+}
+
+void CorruptedPages::add_page(const char *file_name, ulint space_id,
+ unsigned page_no)
+{
+ ut_a(!pthread_mutex_lock(&m_mutex));
+ add_page_no_lock(file_name, space_id, page_no, true);
+ ut_a(!pthread_mutex_unlock(&m_mutex));
+}
+
+bool CorruptedPages::contains(ulint space_id, unsigned page_no) const
+{
+ bool result = false;
+ ut_a(!pthread_mutex_lock(&m_mutex));
+ container_t::const_iterator space_it= m_spaces.find(space_id);
+ if (space_it != m_spaces.end())
+ result = space_it->second.pages.count(page_no);
+ ut_a(!pthread_mutex_unlock(&m_mutex));
+ return result;
+}
+
+void CorruptedPages::drop_space(ulint space_id)
+{
+ ut_a(!pthread_mutex_lock(&m_mutex));
+ m_spaces.erase(space_id);
+ ut_a(!pthread_mutex_unlock(&m_mutex));
+}
+
+void CorruptedPages::rename_space(ulint space_id, const std::string &new_name)
+{
+ ut_a(!pthread_mutex_lock(&m_mutex));
+ container_t::iterator space_it = m_spaces.find(space_id);
+ if (space_it != m_spaces.end())
+ space_it->second.space_name = new_name;
+ ut_a(!pthread_mutex_unlock(&m_mutex));
+}
+
+bool CorruptedPages::print_to_file(const char *filename) const
+{
+ std::ostringstream out;
+ ut_a(!pthread_mutex_lock(&m_mutex));
+ if (!m_spaces.size())
+ {
+ ut_a(!pthread_mutex_unlock(&m_mutex));
+ return true;
+ }
+ for (container_t::const_iterator space_it=
+ m_spaces.begin();
+ space_it != m_spaces.end(); ++space_it)
+ {
+ out << space_it->second.space_name << " " << space_it->first << "\n";
+ bool first_page_no= true;
+ for (std::set<unsigned>::const_iterator page_it=
+ space_it->second.pages.begin();
+ page_it != space_it->second.pages.end(); ++page_it)
+ if (first_page_no)
+ {
+ out << *page_it;
+ first_page_no= false;
+ }
+ else
+ out << " " << *page_it;
+ out << "\n";
+ }
+ ut_a(!pthread_mutex_unlock(&m_mutex));
+ if (xtrabackup_backup)
+ return backup_file_print_buf(filename, out.str().c_str(),
+ static_cast<int>(out.str().size()));
+ std::ofstream outfile;
+ outfile.open(filename);
+ if (!outfile.is_open())
+ die("Can't open %s, error number: %d, error message: %s", filename, errno,
+ strerror(errno));
+ outfile << out.str();
+ return true;
+}
+
+void CorruptedPages::read_from_file(const char *file_name)
+{
+ MY_STAT mystat;
+ if (!my_stat(file_name, &mystat, MYF(0)))
+ return;
+ std::ifstream infile;
+ infile.open(file_name);
+ if (!infile.is_open())
+ die("Can't open %s, error number: %d, error message: %s", file_name, errno,
+ strerror(errno));
+ std::string line;
+ std::string space_name;
+ ulint space_id;
+ ulint line_number= 0;
+ while (std::getline(infile, line))
+ {
+ ++line_number;
+ std::istringstream iss(line);
+ if (line_number & 1) {
+ if (!(iss >> space_name))
+ die("Can't parse space name from corrupted pages file at "
+ "line " ULINTPF,
+ line_number);
+ if (!(iss >> space_id))
+ die("Can't parse space id from corrupted pages file at line " ULINTPF,
+ line_number);
+ }
+ else
+ {
+ std::istringstream iss(line);
+ unsigned page_no;
+ while ((iss >> page_no))
+ add_page_no_lock(space_name.c_str(), space_id, page_no, false);
+ if (!iss.eof())
+ die("Corrupted pages file parse error on line number " ULINTPF,
+ line_number);
+ }
+ }
+}
+
+bool CorruptedPages::empty() const
+{
+ ut_a(!pthread_mutex_lock(&m_mutex));
+ bool result= !m_spaces.size();
+ ut_a(!pthread_mutex_unlock(&m_mutex));
+ return result;
+}
+
+static void xb_load_single_table_tablespace(const std::string &space_name,
+ bool set_size);
+static void xb_data_files_close();
+static fil_space_t* fil_space_get_by_name(const char* name);
+
+void CorruptedPages::zero_out_free_pages()
+{
+ container_t non_free_pages;
+ byte *zero_page=
+ static_cast<byte *>(aligned_malloc(srv_page_size, srv_page_size));
+ memset(zero_page, 0, srv_page_size);
+
+ ut_a(!pthread_mutex_lock(&m_mutex));
+ for (container_t::const_iterator space_it= m_spaces.begin();
+ space_it != m_spaces.end(); ++space_it)
+ {
+ ulint space_id = space_it->first;
+ const std::string &space_name = space_it->second.space_name;
+ // There is no need to close tablespaces explixitly as they will be closed
+ // in innodb_shutdown().
+ xb_load_single_table_tablespace(space_name, false);
+ fil_space_t *space = fil_space_t::get(space_id);
+ if (!space)
+ die("Can't find space object for space name %s to check corrupted page",
+ space_name.c_str());
+ for (std::set<unsigned>::const_iterator page_it=
+ space_it->second.pages.begin();
+ page_it != space_it->second.pages.end(); ++page_it)
+ {
+ bool is_free= fseg_page_is_free(space, *page_it);
+ if (!is_free) {
+ space_info_t &space_info = non_free_pages[space_id];
+ space_info.pages.insert(*page_it);
+ if (space_info.space_name.empty())
+ space_info.space_name = space_name;
+ msg("Error: corrupted page " UINT32PF
+ " of tablespace %s can not be fixed",
+ *page_it, space_name.c_str());
+ }
+ else
+ {
+ space->reacquire();
+ auto err= space
+ ->io(IORequest(IORequest::PUNCH_RANGE),
+ *page_it * srv_page_size, srv_page_size, zero_page)
+ .err;
+ if (err != DB_SUCCESS)
+ die("Can't zero out corrupted page " UINT32PF " of tablespace %s",
+ *page_it, space_name.c_str());
+ msg("Corrupted page " UINT32PF
+ " of tablespace %s was successfuly fixed.",
+ *page_it, space_name.c_str());
+ }
+ }
+ space->flush();
+ space->release();
+ }
+ m_spaces.swap(non_free_pages);
+ ut_a(!pthread_mutex_unlock(&m_mutex));
+ aligned_free(zero_page);
+}
+
/* Simple datasink creation tracking...add datasinks in the reverse order you
want them destroyed. */
#define XTRABACKUP_MAX_DATASINKS 10
@@ -362,11 +573,12 @@ xtrabackup_add_datasink(ds_ctxt_t *ds)
datasinks[actual_datasinks] = ds; actual_datasinks++;
}
-
-typedef void (*process_single_tablespace_func_t)(const char *dirname, const char *filname, bool is_remote);
+typedef void (*process_single_tablespace_func_t)(const char *dirname,
+ const char *filname,
+ bool is_remote,
+ bool skip_node_page0);
static dberr_t enumerate_ibd_files(process_single_tablespace_func_t callback);
-
/* ======== Datafiles iterator ======== */
struct datafiles_iter_t {
fil_space_t *space;
@@ -690,6 +902,7 @@ typedef struct {
uint *count;
pthread_mutex_t* count_mutex;
os_thread_id_t id;
+ CorruptedPages *corrupted_pages;
} data_thread_ctxt_t;
/* ======== for option and variables ======== */
@@ -791,7 +1004,8 @@ enum options_xtrabackup
OPT_ROCKSDB_DATADIR,
OPT_BACKUP_ROCKSDB,
OPT_XTRA_CHECK_PRIVILEGES,
- OPT_XTRA_MYSQLD_ARGS
+ OPT_XTRA_MYSQLD_ARGS,
+ OPT_XB_IGNORE_INNODB_PAGE_CORRUPTION
};
struct my_option xb_client_options[]= {
@@ -1182,6 +1396,17 @@ struct my_option xb_client_options[]= {
" uses old (pre-4.1.1) protocol.",
&opt_secure_auth, &opt_secure_auth, 0, GET_BOOL, NO_ARG, 1, 0, 0, 0, 0,
0},
+
+ {"log-innodb-page-corruption", OPT_XB_IGNORE_INNODB_PAGE_CORRUPTION,
+ "Continue backup if innodb corrupted pages are found. The pages are "
+ "logged in " MB_CORRUPTED_PAGES_FILE
+ " and backup is finished with error. "
+ "--prepare will try to fix corrupted pages. If " MB_CORRUPTED_PAGES_FILE
+ " exists after --prepare in base backup directory, backup still contains "
+ "corrupted pages and can not be considered as consistent.",
+ &opt_log_innodb_page_corruption, &opt_log_innodb_page_corruption, 0,
+ GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
+
#define MYSQL_CLIENT
#include "sslopt-longopts.h"
#undef MYSQL_CLIENT
@@ -1474,7 +1699,8 @@ debug_sync_point(const char *name)
static std::set<std::string> tables_for_export;
-static void append_export_table(const char *dbname, const char *tablename, bool is_remote)
+static void append_export_table(const char *dbname, const char *tablename,
+ bool is_remote, bool skip_node_page0)
{
if(dbname && tablename && !is_remote)
{
@@ -2473,7 +2699,8 @@ 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,
const char *dest_name,
- const xb_write_filt_t &write_filter)
+ const xb_write_filt_t &write_filter,
+ CorruptedPages &corrupted_pages)
{
char dst_name[FN_REFLEN];
ds_file_t *dstfile = NULL;
@@ -2538,7 +2765,8 @@ 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(&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;
}
@@ -2558,7 +2786,8 @@ static my_bool xtrabackup_copy_datafile(fil_node_t *node, uint thread_n,
}
/* The main copy loop */
- while ((res = xb_fil_cur_read(&cursor)) == XB_FIL_CUR_SUCCESS) {
+ while ((res = xb_fil_cur_read(&cursor, corrupted_pages)) ==
+ XB_FIL_CUR_SUCCESS) {
if (!write_filter.process(&write_filt_ctxt, dstfile)) {
goto error;
}
@@ -2836,6 +3065,21 @@ static os_thread_ret_t DECLARE_THREAD(io_watching_thread)(void*)
}
#ifndef DBUG_OFF
+char *dbug_mariabackup_get_val(const char *event, const char *key)
+{
+ char envvar[FN_REFLEN];
+ if (key) {
+ snprintf(envvar, sizeof(envvar), "%s_%s", event, key);
+ char *slash = strchr(envvar, '/');
+ if (slash)
+ *slash = '_';
+ } else {
+ strncpy(envvar, event, sizeof envvar - 1);
+ envvar[sizeof envvar - 1] = '\0';
+ }
+ return getenv(envvar);
+}
+
/*
In debug mode, execute SQL statement that was passed via environment.
To use this facility, you need to
@@ -2848,35 +3092,15 @@ To use this facility, you need to
for the variable)
3. start mariabackup with --dbug=+d,debug_mariabackup_events
*/
-static void dbug_mariabackup_event(const char *event,const char *key)
+void dbug_mariabackup_event(const char *event,const char *key)
{
- char envvar[FN_REFLEN];
- if (key) {
- snprintf(envvar, sizeof(envvar), "%s_%s", event, key);
- char *slash = strchr(envvar, '/');
- if (slash)
- *slash = '_';
- } else {
- strncpy(envvar, event, sizeof envvar - 1);
- envvar[sizeof envvar - 1] = '\0';
- }
- char *sql = getenv(envvar);
- if (sql) {
+ char *sql = dbug_mariabackup_get_val(event, key);
+ if (sql && *sql) {
msg("dbug_mariabackup_event : executing '%s'", sql);
xb_mysql_query(mysql_connection, sql, false, true);
}
-
}
-#define DBUG_MARIABACKUP_EVENT(A, B) DBUG_EXECUTE_IF("mariabackup_events", dbug_mariabackup_event(A,B););
-#define DBUG_MB_INJECT_CODE(EVENT, KEY, CODE) \
- DBUG_EXECUTE_IF("mariabackup_inject_code", {\
- char *env = getenv(EVENT); \
- if (env && !strcmp(env, KEY)) { CODE } \
- })
-#else
-#define DBUG_MARIABACKUP_EVENT(A,B)
-#define DBUG_MB_INJECT_CODE(EVENT, KEY, CODE)
-#endif
+#endif // DBUG_OFF
/**************************************************************************
Datafiles copying thread.*/
@@ -2889,6 +3113,7 @@ DECLARE_THREAD(data_copy_thread_func)(
data_thread_ctxt_t *ctxt = (data_thread_ctxt_t *) arg;
uint num = ctxt->num;
fil_node_t* node;
+ ut_ad(ctxt->corrupted_pages);
/*
Initialize mysys thread-specific memory so we can
@@ -2900,11 +3125,12 @@ DECLARE_THREAD(data_copy_thread_func)(
while ((node = datafiles_iter_next(ctxt->it)) != NULL) {
DBUG_MARIABACKUP_EVENT("before_copy", node->space->name);
- DBUG_MB_INJECT_CODE("wait_innodb_redo_before_copy", node->space->name,
+ DBUG_EXECUTE_FOR_KEY("wait_innodb_redo_before_copy", node->space->name,
backup_wait_for_lsn(get_current_lsn(mysql_connection)););
/* copy the datafile */
if (xtrabackup_copy_datafile(node, num, NULL,
- xtrabackup_incremental ? wf_incremental : wf_write_through))
+ xtrabackup_incremental ? wf_incremental : wf_write_through,
+ *ctxt->corrupted_pages))
die("failed to copy datafile.");
DBUG_MARIABACKUP_EVENT("after_copy", node->space->name);
@@ -3041,15 +3267,24 @@ xb_new_datafile(const char *name, bool is_remote)
}
-static
-void
-xb_load_single_table_tablespace(
- const char *dirname,
- const char *filname,
- bool is_remote)
+/** Load tablespace.
+
+@param[in] dirname directory name of the tablespace to open
+@param[in] filname file name of the tablespece to open
+@param[in] is_remote true if tablespace file is .isl
+@param[in] skip_node_page0 true if we don't need to read node page 0. Otherwise
+node page0 will be read, and it's size and free pages limit
+will be set from page 0, what is neccessary for checking and fixing corrupted
+pages.
+*/
+static void xb_load_single_table_tablespace(const char *dirname,
+ const char *filname,
+ bool is_remote,
+ bool skip_node_page0)
{
ut_ad(srv_operation == SRV_OPERATION_BACKUP
- || srv_operation == SRV_OPERATION_RESTORE_DELTA);
+ || srv_operation == SRV_OPERATION_RESTORE_DELTA
+ || srv_operation == SRV_OPERATION_RESTORE);
/* Ignore .isl files on XtraBackup recovery. All tablespaces must be
local. */
if (is_remote && srv_operation == SRV_OPERATION_RESTORE_DELTA) {
@@ -3102,8 +3337,8 @@ xb_load_single_table_tablespace(
FIL_TYPE_TABLESPACE, NULL/* TODO: crypt_data */);
ut_a(space != NULL);
-
- space->add(file->filepath(), file->detach(), 0, false, false);
+ space->add(file->filepath(),
+ skip_node_page0 ? file->detach() : pfs_os_file_t(), 0, false, false);
mutex_enter(&fil_system.mutex);
space->read_page0();
mutex_exit(&fil_system.mutex);
@@ -3123,6 +3358,28 @@ xb_load_single_table_tablespace(
ut_free(name);
}
+static void xb_load_single_table_tablespace(const std::string &space_name,
+ bool skip_node_page0)
+{
+ std::string name(space_name);
+ bool is_remote= access((name + ".ibd").c_str(), R_OK) != 0;
+ const char *extension= is_remote ? ".isl" : ".ibd";
+ name.append(extension);
+ char buf[FN_REFLEN];
+ strncpy(buf, name.c_str(), sizeof buf - 1);
+ buf[sizeof buf - 1]= '\0';
+ const char *dbname= buf;
+ char *p= strchr(buf, '/');
+ if (p == 0)
+ die("Unexpected tablespace %s filename %s", space_name.c_str(),
+ name.c_str());
+ ut_a(p);
+ *p= 0;
+ const char *tablename= p + 1;
+ xb_load_single_table_tablespace(dbname, tablename, is_remote,
+ skip_node_page0);
+}
+
/** Scan the database directories under the MySQL datadir, looking for
.ibd files and determining the space id in each of them.
@return DB_SUCCESS or error number */
@@ -3164,7 +3421,7 @@ static dberr_t enumerate_ibd_files(process_single_tablespace_func_t callback)
bool is_ibd = !is_isl && ends_with(dbinfo.name,".ibd");
if (is_isl || is_ibd) {
- (*callback)(NULL, dbinfo.name, is_isl);
+ (*callback)(NULL, dbinfo.name, is_isl, false);
}
}
@@ -3221,7 +3478,7 @@ static dberr_t enumerate_ibd_files(process_single_tablespace_func_t callback)
if (strlen(fileinfo.name) > 4) {
bool is_isl= false;
if (ends_with(fileinfo.name, ".ibd") || ((is_isl = ends_with(fileinfo.name, ".isl"))))
- (*callback)(dbinfo.name, fileinfo.name, is_isl);
+ (*callback)(dbinfo.name, fileinfo.name, is_isl, false);
}
}
@@ -3935,6 +4192,7 @@ static bool xtrabackup_backup_func()
uint i;
uint count;
pthread_mutex_t count_mutex;
+ CorruptedPages corrupted_pages;
data_thread_ctxt_t *data_threads;
pthread_mutex_init(&backup_mutex, NULL);
pthread_cond_init(&scanned_lsn_cond, NULL);
@@ -4201,6 +4459,7 @@ fail_before_log_copying_thread_start:
data_threads[i].num = i+1;
data_threads[i].count = &count;
data_threads[i].count_mutex = &count_mutex;
+ data_threads[i].corrupted_pages = &corrupted_pages;
data_threads[i].id = os_thread_create(data_copy_thread_func,
data_threads + i);
}
@@ -4221,7 +4480,7 @@ fail_before_log_copying_thread_start:
datafiles_iter_free(it);
}
- bool ok = backup_start();
+ bool ok = backup_start(corrupted_pages);
if (ok) {
ok = xtrabackup_backup_low();
@@ -4238,6 +4497,9 @@ fail_before_log_copying_thread_start:
}
}
+ if (opt_log_innodb_page_corruption)
+ ok = corrupted_pages.print_to_file(MB_CORRUPTED_PAGES_FILE);
+
if (!ok) {
goto fail;
}
@@ -4265,7 +4527,13 @@ fail_before_log_copying_thread_start:
log_file_op = NULL;
pthread_mutex_destroy(&backup_mutex);
pthread_cond_destroy(&scanned_lsn_cond);
- return(true);
+ if (opt_log_innodb_page_corruption && !corrupted_pages.empty()) {
+ msg("Error: corrupted innodb pages are found and logged to "
+ MB_CORRUPTED_PAGES_FILE " file");
+ return false;
+ }
+ else
+ return(true);
}
@@ -4287,7 +4555,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(void)
+void backup_fix_ddl(CorruptedPages &corrupted_pages)
{
std::set<std::string> new_tables;
std::set<std::string> dropped_tables;
@@ -4309,6 +4577,7 @@ void backup_fix_ddl(void)
if (ddl_tracker.drops.find(id) != ddl_tracker.drops.end()) {
dropped_tables.insert(name);
+ corrupted_pages.drop_space(id);
continue;
}
@@ -4320,6 +4589,8 @@ void backup_fix_ddl(void)
const std::string new_name = ddl_tracker.id_to_name[id];
if (new_name != name) {
renamed_tables[name] = new_name;
+ if (opt_log_innodb_page_corruption)
+ corrupted_pages.rename_space(id, new_name);
}
}
@@ -4339,6 +4610,8 @@ void backup_fix_ddl(void)
if (ddl_tracker.drops.find(id) == ddl_tracker.drops.end()) {
dropped_tables.erase(name);
new_tables.insert(name);
+ if (opt_log_innodb_page_corruption)
+ corrupted_pages.drop_space(id);
}
}
@@ -4386,23 +4659,7 @@ void backup_fix_ddl(void)
const char *space_name = iter->c_str();
if (check_if_skip_table(space_name))
continue;
- std::string name(*iter);
- bool is_remote = access((name + ".ibd").c_str(), R_OK) != 0;
- const char *extension = is_remote ? ".isl" : ".ibd";
- name.append(extension);
- char buf[FN_REFLEN];
- strncpy(buf, name.c_str(), sizeof buf - 1);
- buf[sizeof buf - 1] = '\0';
- const char *dbname = buf;
- char *p = strchr(buf, '/');
- if (p == 0) {
- msg("Unexpected tablespace %s filename %s", space_name, name.c_str());
- ut_a(0);
- }
- ut_a(p);
- *p = 0;
- const char *tablename = p + 1;
- xb_load_single_table_tablespace(dbname, tablename, is_remote);
+ xb_load_single_table_tablespace(*iter, false);
}
it = datafiles_iter_new();
@@ -4415,7 +4672,8 @@ void backup_fix_ddl(void)
continue;
std::string dest_name(node->space->name);
dest_name.append(".new");
- xtrabackup_copy_datafile(node, 0, dest_name.c_str(), wf_write_through);
+ xtrabackup_copy_datafile(node, 0, dest_name.c_str(), wf_write_through,
+ corrupted_pages);
}
datafiles_iter_free(it);
@@ -5341,6 +5599,7 @@ static ibool prepare_handle_del_files(const char *datadir, const char *db, const
@return whether the operation succeeded */
static bool xtrabackup_prepare_func(char** argv)
{
+ CorruptedPages corrupted_pages;
char metadata_path[FN_REFLEN];
/* cd to target-dir */
@@ -5506,6 +5765,30 @@ static bool xtrabackup_prepare_func(char** argv)
ut_ad(!fil_system.freeze_space_list);
+ corrupted_pages.read_from_file(MB_CORRUPTED_PAGES_FILE);
+ if (xtrabackup_incremental)
+ {
+ char inc_filename[FN_REFLEN];
+ sprintf(inc_filename, "%s/%s", xtrabackup_incremental_dir,
+ MB_CORRUPTED_PAGES_FILE);
+ corrupted_pages.read_from_file(inc_filename);
+ }
+ if (!corrupted_pages.empty())
+ corrupted_pages.zero_out_free_pages();
+ if (corrupted_pages.empty())
+ {
+ if (!xtrabackup_incremental && unlink(MB_CORRUPTED_PAGES_FILE) &&
+ errno != ENOENT)
+ {
+ char errbuf[MYSYS_STRERROR_SIZE];
+ my_strerror(errbuf, sizeof(errbuf), errno);
+ die("Error: unlink %s failed: %s", MB_CORRUPTED_PAGES_FILE,
+ errbuf);
+ }
+ }
+ else
+ corrupted_pages.print_to_file(MB_CORRUPTED_PAGES_FILE);
+
if (ok) {
msg("Last binlog file %s, position %lld",
trx_sys.recovered_binlog_filename,
@@ -5564,7 +5847,7 @@ static bool xtrabackup_prepare_func(char** argv)
error_cleanup:
xb_filters_free();
- return ok && !ib::error::was_logged();
+ return ok && !ib::error::was_logged() && corrupted_pages.empty();
}
/**************************************************************************
diff --git a/extra/mariabackup/xtrabackup.h b/extra/mariabackup/xtrabackup.h
index f18d79aea55..6376849430c 100644
--- a/extra/mariabackup/xtrabackup.h
+++ b/extra/mariabackup/xtrabackup.h
@@ -25,6 +25,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA
#include "datasink.h"
#include "xbstream.h"
#include "changed_page_bitmap.h"
+#include <set>
struct xb_delta_info_t
{
@@ -36,6 +37,32 @@ struct xb_delta_info_t
ulint space_id;
};
+class CorruptedPages
+{
+public:
+ CorruptedPages();
+ ~CorruptedPages();
+ void add_page(const char *file_name, ulint space_id, unsigned page_no);
+ bool contains(ulint space_id, unsigned page_no) const;
+ void drop_space(ulint space_id);
+ void rename_space(ulint space_id, const std::string &new_name);
+ bool print_to_file(const char *file_name) const;
+ void read_from_file(const char *file_name);
+ bool empty() const;
+ void zero_out_free_pages();
+
+private:
+ void add_page_no_lock(const char *space_name, ulint space_id,
+ unsigned page_no, bool convert_space_name);
+ struct space_info_t {
+ std::string space_name;
+ std::set<unsigned> pages;
+ };
+ typedef std::map<ulint, space_info_t> container_t;
+ mutable pthread_mutex_t m_mutex;
+ container_t m_spaces;
+};
+
/* value of the --incremental option */
extern lsn_t incremental_lsn;
@@ -111,6 +138,7 @@ extern my_bool opt_remove_original;
extern my_bool opt_extended_validation;
extern my_bool opt_encrypted_backup;
extern my_bool opt_lock_ddl_per_table;
+extern my_bool opt_log_innodb_page_corruption;
extern char *opt_incremental_history_name;
extern char *opt_incremental_history_uuid;
diff --git a/mysql-test/lib/mtr_report.pm b/mysql-test/lib/mtr_report.pm
index 32250802815..473b21441e2 100644
--- a/mysql-test/lib/mtr_report.pm
+++ b/mysql-test/lib/mtr_report.pm
@@ -514,6 +514,10 @@ sub mtr_report_stats ($$$$) {
# if a test case has to be retried it should have the result MTR_RES_FAILED in jUnit XML
if ($test->{'result'} eq "MTR_RES_FAILED" || $test->{'retries'} > 0) {
my $logcontents = $test->{'logfile-failed'} || $test->{'logfile'};
+ # remove any double ] that would end the cdata
+ $logcontents =~ s/]]/\x{fffd}/g;
+ # replace wide characters that aren't allowed in XML 1.0
+ $logcontents =~ s/[\x00-\x08\x0B\x0C\x0E-\x1F]/\x{fffd}/g;
$xml_report .= qq(>\n\t\t\t<failure message="" type="MTR_RES_FAILED">\n<![CDATA[$logcontents]]>\n\t\t\t</failure>\n\t\t</testcase>\n);
} elsif ($test->{'result'} eq "MTR_RES_SKIPPED" && $test->{'disable'}) {
@@ -530,9 +534,9 @@ sub mtr_report_stats ($$$$) {
# save to file
my $xml_file = $::opt_xml_report;
- open XML_FILE, ">", $xml_file or die "Cannot create file $xml_file: $!";
- print XML_FILE $xml_report;
- close XML_FILE;
+ open (my $XML_UFILE, '>:encoding(UTF-8)', $xml_file) or die 'Cannot create file $xml_file: $!';
+ print $XML_UFILE $xml_report;
+ close $XML_UFILE or warn "File close failed!";
}
if (@$extra_warnings)
diff --git a/mysql-test/main/auto_increment_ranges_innodb.result b/mysql-test/main/auto_increment_ranges_innodb.result
index 61eccc6f944..7800099200f 100644
--- a/mysql-test/main/auto_increment_ranges_innodb.result
+++ b/mysql-test/main/auto_increment_ranges_innodb.result
@@ -289,3 +289,51 @@ pk f
5 a
6 <===
drop table t1;
+#
+# MDEV-21842: auto_increment does not increment with compound primary
+# key on partitioned table
+#
+create or replace table `t` (
+`id` bigint(20) unsigned not null auto_increment,
+`a` int(10) not null ,
+`dt` date not null,
+primary key (`id`, `dt`) ,
+unique key (`a`, `dt`)
+)
+partition by range columns(`dt`)
+(
+partition `p202002` values less than ('2020-03-01'),
+partition `P202003` values less than ('2020-04-01')
+);
+connect con1, localhost, root,,;
+connect con2, localhost, root,,;
+connection con1;
+start transaction;
+insert into t (a, dt) values (1, '2020-02-29');
+connection con2;
+start transaction;
+insert into t (a, dt) values (1, '2020-02-29');
+connection con1;
+insert into t (a, dt) values (2, '2020-02-29');
+select auto_increment from information_schema.tables where table_name='t';
+auto_increment
+4
+commit;
+connection con2;
+ERROR 23000: Duplicate entry '1-2020-02-29' for key 'a'
+connection con1;
+select auto_increment from information_schema.tables where table_name='t';
+auto_increment
+4
+insert into t (a, dt) values (3, '2020-02-29');
+insert into t (a, dt) values (4, '2020-02-29');
+disconnect con1;
+disconnect con2;
+connection default;
+select * from t;
+id a dt
+1 1 2020-02-29
+3 2 2020-02-29
+4 3 2020-02-29
+5 4 2020-02-29
+drop table t;
diff --git a/mysql-test/main/auto_increment_ranges_innodb.test b/mysql-test/main/auto_increment_ranges_innodb.test
index 016ca16bd91..92d377eb147 100644
--- a/mysql-test/main/auto_increment_ranges_innodb.test
+++ b/mysql-test/main/auto_increment_ranges_innodb.test
@@ -18,3 +18,62 @@ select * from t1;
drop table t1;
--let $datadir=`select @@datadir`
--remove_file $datadir/test/load.data
+
+--echo #
+--echo # MDEV-21842: auto_increment does not increment with compound primary
+--echo # key on partitioned table
+--echo #
+
+create or replace table `t` (
+ `id` bigint(20) unsigned not null auto_increment,
+ `a` int(10) not null ,
+ `dt` date not null,
+ primary key (`id`, `dt`) ,
+ unique key (`a`, `dt`)
+)
+ partition by range columns(`dt`)
+(
+ partition `p202002` values less than ('2020-03-01'),
+ partition `P202003` values less than ('2020-04-01')
+);
+
+connect (con1, localhost, root,,);
+connect (con2, localhost, root,,);
+
+--connection con1
+start transaction;
+insert into t (a, dt) values (1, '2020-02-29');
+
+--connection con2
+start transaction;
+let $conn2_id= `SELECT CONNECTION_ID()`;
+send insert into t (a, dt) values (1, '2020-02-29');
+
+--connection con1
+# Ensure that the above insert via conn2 increments next_auto_inc_val
+# before the following insert via conn1 starts.
+let $wait_condition=select 1 from Information_schema.INNODB_TRX
+ where trx_mysql_thread_id = $conn2_id and trx_state = 'LOCK WAIT'
+ and trx_query = "insert into t (a, dt) values (1, '2020-02-29')";
+--source include/wait_condition.inc
+
+insert into t (a, dt) values (2, '2020-02-29');
+select auto_increment from information_schema.tables where table_name='t';
+commit;
+
+--connection con2
+--error ER_DUP_ENTRY
+reap;
+
+--connection con1
+select auto_increment from information_schema.tables where table_name='t';
+insert into t (a, dt) values (3, '2020-02-29');
+insert into t (a, dt) values (4, '2020-02-29');
+
+disconnect con1;
+disconnect con2;
+
+--connection default
+select * from t;
+drop table t;
+
diff --git a/mysql-test/main/derived_cond_pushdown.result b/mysql-test/main/derived_cond_pushdown.result
index 33e8eab968b..6ea4528e641 100644
--- a/mysql-test/main/derived_cond_pushdown.result
+++ b/mysql-test/main/derived_cond_pushdown.result
@@ -10566,6 +10566,47 @@ a
abc
DROP VIEW v1;
DROP TABLE t1;
+#
+# MDEV-19179: pushdown into UNION of aggregation selects whose
+# corresponding columns have different names
+#
+create table t1 (a int);
+insert into t1 values (3), (7), (1);
+select *
+from (select min(a) as x from t1 union all select max(a) as y from t1) t
+where x>0;
+x
+1
+7
+explain extended select *
+from (select min(a) as x from t1 union all select max(a) as y from t1) t
+where x>0;
+id select_type table type possible_keys key key_len ref rows filtered Extra
+1 PRIMARY <derived2> ALL NULL NULL NULL NULL 6 100.00 Using where
+2 DERIVED t1 ALL NULL NULL NULL NULL 3 100.00
+3 UNION t1 ALL NULL NULL NULL NULL 3 100.00
+Warnings:
+Note 1003 /* select#1 */ select `t`.`x` AS `x` from (/* select#2 */ select min(`test`.`t1`.`a`) AS `x` from `test`.`t1` having `x` > 0 union all /* select#3 */ select max(`test`.`t1`.`a`) AS `x` from `test`.`t1` having `x` > 0) `t` where `t`.`x` > 0
+prepare stmt from "select *
+from (select min(a) as x from t1 union all select max(a) as y from t1) t
+where x>0";
+execute stmt;
+x
+1
+7
+execute stmt;
+x
+1
+7
+deallocate prepare stmt;
+create view v1(m) as
+select min(a) as x from t1 union all select max(a) as y from t1;
+select * from v1 where m > 0;
+m
+1
+7
+drop view v1;
+drop table t1;
# End of 10.2 tests
#
# MDEV-14579: pushdown conditions into materialized views/derived tables
diff --git a/mysql-test/main/derived_cond_pushdown.test b/mysql-test/main/derived_cond_pushdown.test
index b2f97029ede..7667cd44ed2 100644
--- a/mysql-test/main/derived_cond_pushdown.test
+++ b/mysql-test/main/derived_cond_pushdown.test
@@ -2185,6 +2185,34 @@ SELECT * FROM v1 WHERE IF( a REGEXP 'def', 'foo', a ) IN ('abc', 'foobar');
DROP VIEW v1;
DROP TABLE t1;
+--echo #
+--echo # MDEV-19179: pushdown into UNION of aggregation selects whose
+--echo # corresponding columns have different names
+--echo #
+
+create table t1 (a int);
+insert into t1 values (3), (7), (1);
+
+let $q=
+select *
+from (select min(a) as x from t1 union all select max(a) as y from t1) t
+where x>0;
+
+eval $q;
+eval explain extended $q;
+
+eval prepare stmt from "$q";
+execute stmt;
+execute stmt;
+deallocate prepare stmt;
+
+create view v1(m) as
+select min(a) as x from t1 union all select max(a) as y from t1;
+select * from v1 where m > 0;
+
+drop view v1;
+drop table t1;
+
--echo # End of 10.2 tests
--echo #
diff --git a/mysql-test/main/information_schema.result b/mysql-test/main/information_schema.result
index 9f36ba35a22..627fb9e4569 100644
--- a/mysql-test/main/information_schema.result
+++ b/mysql-test/main/information_schema.result
@@ -2219,8 +2219,6 @@ SCHEMA_NAME
# End of 10.1 tests
#
#
-# Start of 10.2 Test
-#
# MDEV-14836: Assertion `m_status == DA_ERROR' failed in
# Diagnostics_area::sql_errno upon query from I_S with LIMIT ROWS EXAMINED
#
@@ -2305,5 +2303,12 @@ mysql global_priv Priv json_valid(`Priv`) def mysql
test t a `i` > 0 def test
drop table t;
#
+# MDEV-24230 subquery on information_schema fails with error message
+#
+create table t1 (n int);
+create table t2 (n int);
+insert into t1 set n = (select table_rows from information_schema.tables where table_name='t2');
+drop table t1, t2;
+#
# End of 10.3 tests
#
diff --git a/mysql-test/main/information_schema.test b/mysql-test/main/information_schema.test
index 9c77606b9d4..ca49091ef76 100644
--- a/mysql-test/main/information_schema.test
+++ b/mysql-test/main/information_schema.test
@@ -1924,8 +1924,6 @@ SELECT SCHEMA_NAME from information_schema.schemata where schema_name=REPEAT('a'
--echo #
--echo #
---echo # Start of 10.2 Test
---echo #
--echo # MDEV-14836: Assertion `m_status == DA_ERROR' failed in
--echo # Diagnostics_area::sql_errno upon query from I_S with LIMIT ROWS EXAMINED
--echo #
@@ -1999,5 +1997,13 @@ from information_schema.TABLE_CONSTRAINTS tc
drop table t;
--echo #
+--echo # MDEV-24230 subquery on information_schema fails with error message
+--echo #
+create table t1 (n int);
+create table t2 (n int);
+insert into t1 set n = (select table_rows from information_schema.tables where table_name='t2');
+drop table t1, t2;
+
+--echo #
--echo # End of 10.3 tests
--echo #
diff --git a/mysql-test/main/lock_view.test b/mysql-test/main/lock_view.test
index dd8809ab89d..4b1adac5be1 100644
--- a/mysql-test/main/lock_view.test
+++ b/mysql-test/main/lock_view.test
@@ -1,4 +1,5 @@
source include/not_embedded.inc;
+source include/have_perfschema.inc;
#
# LOCK TABLES and privileges on views
#
diff --git a/mysql-test/main/mysqldump-system.test b/mysql-test/main/mysqldump-system.test
index 9aaa5fff6f9..c1965410167 100644
--- a/mysql-test/main/mysqldump-system.test
+++ b/mysql-test/main/mysqldump-system.test
@@ -3,6 +3,10 @@
--source include/have_udf.inc
--source include/platform.inc
+if (!$AUTH_SOCKET_SO) {
+ --skip Need auth socket plugin
+}
+
--echo #
--echo # MDEV-23630: mysqldump to logically dump system tables
--echo #
diff --git a/mysql-test/main/partition_innodb.result b/mysql-test/main/partition_innodb.result
index e212dadd06a..67829fd46d9 100644
--- a/mysql-test/main/partition_innodb.result
+++ b/mysql-test/main/partition_innodb.result
@@ -1087,10 +1087,10 @@ INSERT INTO t1 VALUES ();
SELECT * FROM t1;
a
-1
-1
3
4
6
+7
DROP TABLE t1;
#
# End of 10.3 tests
diff --git a/mysql-test/main/sp.result b/mysql-test/main/sp.result
index d5e722ff1bd..17b0af92a40 100644
--- a/mysql-test/main/sp.result
+++ b/mysql-test/main/sp.result
@@ -8448,8 +8448,25 @@ ERROR 22007: Incorrect integer value: 'y' for column ``.``.`a` at row 1
DROP TABLE t1;
SET sql_mode=DEFAULT;
#
-# Start of 10.3 tests
+# MDEV-24220: error when opening a table for the second call of SP
#
+CREATE TABLE t1 (a INT, b INT);
+INSERT INTO t1 VALUES (1,1),(2,2);
+CREATE VIEW v1 AS SELECT MAX(a) as f FROM t1;
+CREATE PROCEDURE p1()
+BEGIN
+SELECT * FROM v1;
+END $
+CALL p1;
+f
+2
+ALTER TABLE t1 DROP a;
+CALL p1;
+ERROR HY000: View 'test.v1' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them
+DROP PROCEDURE p1;
+DROP VIEW v1;
+DROP TABLE t1;
+#End of 10.2 tests
#
# MDEV-12007 Allow ROW variables as a cursor FETCH target
#
diff --git a/mysql-test/main/sp.test b/mysql-test/main/sp.test
index c6c00ca8d91..bf3a70b6283 100644
--- a/mysql-test/main/sp.test
+++ b/mysql-test/main/sp.test
@@ -9991,9 +9991,30 @@ DROP TABLE t1;
SET sql_mode=DEFAULT;
--echo #
---echo # Start of 10.3 tests
+--echo # MDEV-24220: error when opening a table for the second call of SP
--echo #
+CREATE TABLE t1 (a INT, b INT);
+INSERT INTO t1 VALUES (1,1),(2,2);
+CREATE VIEW v1 AS SELECT MAX(a) as f FROM t1;
+--delimiter $
+CREATE PROCEDURE p1()
+BEGIN
+ SELECT * FROM v1;
+END $
+--delimiter ;
+
+CALL p1;
+ALTER TABLE t1 DROP a;
+-- error ER_VIEW_INVALID
+CALL p1;
+
+DROP PROCEDURE p1;
+DROP VIEW v1;
+DROP TABLE t1;
+
+--echo #End of 10.2 tests
+
--echo #
--echo # MDEV-12007 Allow ROW variables as a cursor FETCH target
--echo #
diff --git a/mysql-test/main/subselect4.result b/mysql-test/main/subselect4.result
index 00f61e9433b..44b879ae33b 100644
--- a/mysql-test/main/subselect4.result
+++ b/mysql-test/main/subselect4.result
@@ -2719,6 +2719,41 @@ SET join_cache_level= @save_join_cache_level;
DROP TABLE t1,t2,t3,t4;
# End of 10.2 tests
#
+# MDEV-21265: IN predicate conversion to IN subquery should be allowed for a broader set of datatype comparison
+#
+CREATE TABLE t1(a VARCHAR(50) collate utf8_general_ci, b INT);
+INSERT INTO t1 VALUES ('abc',1), ('def', 2), ('ghi', 3), ('jkl', 4), ('mno', 5);
+CREATE TABLE t2(a VARCHAR(50) collate utf8mb4_general_ci, b INT);
+INSERT INTO t2 VALUES ('abc',1), ('def', 2), ('ghi', 3), ('jkl', 4), ('mno', 5);
+set @save_in_predicate_conversion_threshold= @@in_predicate_conversion_threshold;
+set in_predicate_conversion_threshold=2;
+set names 'utf8mb4';
+#
+# IN predicate to IN subquery is not allowed as materialization is not allowed
+# The character set on the inner side is not equal to or a proper subset of the outer side
+#
+EXPLAIN
+SELECT * FROM t1 WHERE (t1.a,t1.b) IN (('abx',1),('def',2), ('abc', 3));
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 ALL NULL NULL NULL NULL 5 Using where
+set names 'utf8';
+#
+# IN predicate to IN subquery is performed as materialization is llowed
+# The character set on the inner side is a proper subset of the outer side
+#
+# this test in 10.5 has only 2 rows in the IN predicate
+EXPLAIN
+SELECT * FROM t2 WHERE (t2.a,t2.b) IN (('abc',1), ('def', 2));
+id select_type table type possible_keys key key_len ref rows Extra
+1 PRIMARY t2 ALL NULL NULL NULL NULL 5
+1 PRIMARY <subquery2> eq_ref distinct_key distinct_key 16 func,func 1 Using where
+2 MATERIALIZED <derived3> ALL NULL NULL NULL NULL 2
+3 DERIVED NULL NULL NULL NULL NULL NULL NULL No tables used
+set names default;
+set @@in_predicate_conversion_threshold= @save_in_predicate_conversion_threshold;
+DROP TABLE t1,t2;
+# End of 10.3 tests
+#
# MDEV-19134: EXISTS() slower if ORDER BY is defined
#
create table t0 (a int);
diff --git a/mysql-test/main/subselect4.test b/mysql-test/main/subselect4.test
index c9b05c4f015..d46bb846a9e 100644
--- a/mysql-test/main/subselect4.test
+++ b/mysql-test/main/subselect4.test
@@ -2240,6 +2240,45 @@ DROP TABLE t1,t2,t3,t4;
--echo # End of 10.2 tests
--echo #
+--echo # MDEV-21265: IN predicate conversion to IN subquery should be allowed for a broader set of datatype comparison
+--echo #
+
+CREATE TABLE t1(a VARCHAR(50) collate utf8_general_ci, b INT);
+INSERT INTO t1 VALUES ('abc',1), ('def', 2), ('ghi', 3), ('jkl', 4), ('mno', 5);
+
+CREATE TABLE t2(a VARCHAR(50) collate utf8mb4_general_ci, b INT);
+INSERT INTO t2 VALUES ('abc',1), ('def', 2), ('ghi', 3), ('jkl', 4), ('mno', 5);
+
+set @save_in_predicate_conversion_threshold= @@in_predicate_conversion_threshold;
+set in_predicate_conversion_threshold=2;
+
+set names 'utf8mb4';
+--echo #
+--echo # IN predicate to IN subquery is not allowed as materialization is not allowed
+--echo # The character set on the inner side is not equal to or a proper subset of the outer side
+--echo #
+
+EXPLAIN
+SELECT * FROM t1 WHERE (t1.a,t1.b) IN (('abx',1),('def',2), ('abc', 3));
+
+set names 'utf8';
+--echo #
+--echo # IN predicate to IN subquery is performed as materialization is llowed
+--echo # The character set on the inner side is a proper subset of the outer side
+--echo #
+
+--echo # this test in 10.5 has only 2 rows in the IN predicate
+
+EXPLAIN
+SELECT * FROM t2 WHERE (t2.a,t2.b) IN (('abc',1), ('def', 2));
+
+set names default;
+set @@in_predicate_conversion_threshold= @save_in_predicate_conversion_threshold;
+DROP TABLE t1,t2;
+
+--echo # End of 10.3 tests
+
+--echo #
--echo # MDEV-19134: EXISTS() slower if ORDER BY is defined
--echo #
create table t0 (a int);
diff --git a/mysql-test/main/xa.result b/mysql-test/main/xa.result
index 2fe8316b65b..46c0c8551a1 100644
--- a/mysql-test/main/xa.result
+++ b/mysql-test/main/xa.result
@@ -297,7 +297,7 @@ DROP TABLE t1;
#
# Bug#12352846 - TRANS_XA_START(THD*):
# ASSERTION THD->TRANSACTION.XID_STATE.XID.IS_NULL()
-# FAILED
+# FAILED
#
CREATE TABLE t1 (a INT) ENGINE=InnoDB;
CREATE TABLE t2 (a INT) ENGINE=InnoDB;
@@ -349,6 +349,32 @@ XA END 'xid1';
XA ROLLBACK 'xid1';
DROP TABLE t1, t2, t3;
#
+# MDEV 15532 XA: Assertion `!log->same_pk' failed in
+# row_log_table_apply_delete
+#
+CREATE TABLE t1 (a INT) ENGINE=InnoDB;
+INSERT INTO t1 VALUES (1),(2);
+connect con1,localhost,root,,test;
+XA START 'xid';
+UPDATE t1 SET a = 5;
+connection default;
+SET innodb_lock_wait_timeout= 2, lock_wait_timeout= 2;
+ALTER TABLE non_existing_table1;
+ERROR 42S02: Table 'test.non_existing_table1' doesn't exist
+ALTER TABLE t1 FORCE;;
+connection con1;
+ALTER TABLE non_existing_table2;
+ERROR XAE07: XAER_RMFAIL: The command cannot be executed when global transaction is in the ACTIVE state
+DELETE FROM t1 LIMIT 1;
+connection default;
+ERROR HY000: Lock wait timeout exceeded; try restarting transaction
+connection con1;
+XA END 'xid';
+XA ROLLBACK 'xid';
+DROP TABLE t1;
+disconnect con1;
+connection default;
+#
# MDEV-21766 - Forbid XID with empty 'gtrid'
#
CREATE TABLE t1(a INT) ENGINE=InnoDB;
diff --git a/mysql-test/main/xa.test b/mysql-test/main/xa.test
index 07183feda76..f5a5cc96197 100644
--- a/mysql-test/main/xa.test
+++ b/mysql-test/main/xa.test
@@ -392,7 +392,7 @@ DROP TABLE t1;
--echo #
--echo # Bug#12352846 - TRANS_XA_START(THD*):
--echo # ASSERTION THD->TRANSACTION.XID_STATE.XID.IS_NULL()
---echo # FAILED
+--echo # FAILED
--echo #
CREATE TABLE t1 (a INT) ENGINE=InnoDB;
@@ -449,7 +449,7 @@ CREATE TABLE t1 (pk INT PRIMARY KEY) ENGINE=InnoDB;
CREATE TABLE t2 (pk INT PRIMARY KEY) ENGINE=InnoDB;
INSERT INTO t2 VALUES (1),(2);
CREATE TABLE t3 (i INT) ENGINE=InnoDB;
-
+
XA BEGIN 'xid1';
REPLACE INTO t1 SELECT * FROM t2;
@@ -478,6 +478,44 @@ XA END 'xid1';
XA ROLLBACK 'xid1';
DROP TABLE t1, t2, t3;
+--echo #
+--echo # MDEV 15532 XA: Assertion `!log->same_pk' failed in
+--echo # row_log_table_apply_delete
+--echo #
+
+CREATE TABLE t1 (a INT) ENGINE=InnoDB;
+INSERT INTO t1 VALUES (1),(2);
+
+--connect (con1,localhost,root,,test)
+
+XA START 'xid';
+UPDATE t1 SET a = 5;
+
+--connection default
+SET innodb_lock_wait_timeout= 2, lock_wait_timeout= 2;
+
+--error ER_NO_SUCH_TABLE
+ALTER TABLE non_existing_table1;
+
+--send ALTER TABLE t1 FORCE;
+
+--connection con1
+--error ER_XAER_RMFAIL
+
+ALTER TABLE non_existing_table2;
+DELETE FROM t1 LIMIT 1;
+
+--connection default
+--error ER_LOCK_WAIT_TIMEOUT
+--reap
+
+# Cleanup
+--connection con1
+XA END 'xid';
+XA ROLLBACK 'xid';
+DROP TABLE t1;
+--disconnect con1
+connection default;
--echo #
--echo # MDEV-21766 - Forbid XID with empty 'gtrid'
diff --git a/mysql-test/suite/binlog/r/binlog_mysqlbinlog_suppress_O_TMPFILE.result b/mysql-test/suite/binlog/r/binlog_mysqlbinlog_suppress_O_TMPFILE.result
new file mode 100644
index 00000000000..0154495f502
--- /dev/null
+++ b/mysql-test/suite/binlog/r/binlog_mysqlbinlog_suppress_O_TMPFILE.result
@@ -0,0 +1,21 @@
+RESET MASTER;
+CREATE TABLE t(f text);
+INSERT INTO t VALUES (repeat('x',4096));
+INSERT INTO t SELECT * FROM t;
+INSERT INTO t SELECT * FROM t;
+INSERT INTO t SELECT * FROM t;
+INSERT INTO t SELECT * FROM t;
+INSERT INTO t SELECT * FROM t;
+INSERT INTO t SELECT * FROM t;
+INSERT INTO t SELECT * FROM t;
+INSERT INTO t SELECT * FROM t;
+INSERT INTO t SELECT * FROM t;
+SELECT COUNT(*) FROM t;
+COUNT(*)
+512
+FLUSH LOGS;
+DROP TABLE t;
+# 512- Rows must be present
+include/assert.inc [Table t should have 512 rows.]
+DROP TABLE t;
+RESET MASTER;
diff --git a/mysql-test/suite/binlog/t/binlog_mysqlbinlog_suppress_O_TMPFILE.test b/mysql-test/suite/binlog/t/binlog_mysqlbinlog_suppress_O_TMPFILE.test
new file mode 100644
index 00000000000..c97269a8ad8
--- /dev/null
+++ b/mysql-test/suite/binlog/t/binlog_mysqlbinlog_suppress_O_TMPFILE.test
@@ -0,0 +1,51 @@
+# ==== Purpose ====
+#
+# Suppress the following informational note that gets printed to standard
+# error when O_TMPFILE flag is not supported by underlying operating system.
+#
+# Note: ../client/mysqlbinlog: O_TMPFILE is not supported on /tmp (disabling
+# future attempts)
+#
+# Step 1: Generate a binarylog file with a size greater than 1MB.
+# Step 2: Use mysqlbinlog tool to generate sql file and redirect the standard
+# error to standard output (2>&1)
+# Step 3: Source the generated sql file as inpurt to mysql client, observe no
+# syntax error is reported.
+#
+# ==== References ====
+#
+# MDEV-23846: O_TMPFILE error in mysqlbinlog stream output breaks restore
+#
+--source include/have_binlog_format_row.inc
+
+RESET MASTER;
+CREATE TABLE t(f text);
+INSERT INTO t VALUES (repeat('x',4096));
+INSERT INTO t SELECT * FROM t;
+INSERT INTO t SELECT * FROM t;
+INSERT INTO t SELECT * FROM t;
+INSERT INTO t SELECT * FROM t;
+INSERT INTO t SELECT * FROM t;
+INSERT INTO t SELECT * FROM t;
+INSERT INTO t SELECT * FROM t;
+INSERT INTO t SELECT * FROM t;
+INSERT INTO t SELECT * FROM t;
+SELECT COUNT(*) FROM t;
+FLUSH LOGS;
+let $MYSQLD_DATADIR= `select @@datadir`;
+
+--exec $MYSQL_BINLOG $MYSQLD_DATADIR/master-bin.000001 > $MYSQLTEST_VARDIR/tmp/mysqlbinlog_base64.sql 2>&1
+#
+# Clear database and restore from binlog
+#
+DROP TABLE t;
+
+--exec $MYSQL test < $MYSQLTEST_VARDIR/tmp/mysqlbinlog_base64.sql
+
+--echo # 512- Rows must be present
+--let $assert_cond= COUNT(*) = 512 FROM t
+--let $assert_text= Table t should have 512 rows.
+--source include/assert.inc
+
+DROP TABLE t;
+RESET MASTER;
diff --git a/mysql-test/suite/galera/disabled.def b/mysql-test/suite/galera/disabled.def
index d940c702d54..046feac5566 100644
--- a/mysql-test/suite/galera/disabled.def
+++ b/mysql-test/suite/galera/disabled.def
@@ -12,14 +12,11 @@
GCF-1081 : MDEV-18283 Galera test failure on galera.GCF-1081
GCF-939 : MDEV-21520 galera.GCF-939
-MDEV-16509 : MDEV-21523 galera.MDEV-16509
MDEV-20225 : MDEV-20886 galera.MDEV-20225
MW-286 : MDEV-18464 Killing thread can cause mutex deadlock if done concurrently with Galera/replication victim kill
MW-328A : MDEV-22666 galera.MW-328A MTR failed: "Semaphore wait has lasted > 600 seconds" and do not release port 16002
-MW-328A : MDEV-22666?
MW-328B : MDEV-22666 galera.MW-328A MTR failed: "Semaphore wait has lasted > 600 seconds" and do not release port 16002
MW-329 : MDEV-19962 Galera test failure on MW-329
-galera.galera_defaults : MDEV-21494 Galera test sporadic failure on galera.galera_defaults
galera_as_slave_replication_bundle : MDEV-15785 OPTION_GTID_BEGIN is set in Gtid_log_event::do_apply_event()
galera_bf_abort_group_commit : MDEV-18282 Galera test failure on galera.galera_bf_abort_group_commit
galera_binlog_stmt_autoinc : MDEV-19959 Galera test failure on galera_binlog_stmt_autoinc
@@ -44,7 +41,6 @@ galera_var_reject_queries : assertion in inline_mysql_socket_send
galera_var_replicate_myisam_on : MDEV-24062 Galera test failure on galera_var_replicate_myisam_on
galera_var_retry_autocommit: MDEV-18181 Galera test failure on galera.galera_var_retry_autocommit
galera_wan : MDEV-17259 Test failure on galera.galera_wan
-lp1376747-4 : MDEV-21911 Galera test failure on lp1376747-4
partition : MDEV-19958 Galera test failure on galera.partition
query_cache: MDEV-15805 Test failure on galera.query_cache
sql_log_bin : MDEV-21491 galera.sql_log_bin
diff --git a/mysql-test/suite/galera/r/galera#505.result b/mysql-test/suite/galera/r/galera#505.result
index bc7eb3b9ed4..d5e78c570b5 100644
--- a/mysql-test/suite/galera/r/galera#505.result
+++ b/mysql-test/suite/galera/r/galera#505.result
@@ -2,6 +2,7 @@ connection node_2;
connection node_1;
connection node_1;
SET SESSION wsrep_sync_wait=0;
+# Correct Galera library found
SET SESSION wsrep_sync_wait=DEFAULT;
SET GLOBAL wsrep_provider_options = 'pc.weight=3';
SHOW GLOBAL VARIABLES LIKE 'wsrep_provider_options';
diff --git a/mysql-test/suite/galera/r/galera_as_slave_replay.result b/mysql-test/suite/galera/r/galera_as_slave_replay.result
new file mode 100644
index 00000000000..760617be5f7
--- /dev/null
+++ b/mysql-test/suite/galera/r/galera_as_slave_replay.result
@@ -0,0 +1,95 @@
+connect node_2a, 127.0.0.1, root, , test, $NODE_MYPORT_2;
+connection node_2a;
+connection node_1;
+RESET MASTER;
+connection node_2a;
+START SLAVE;
+connection node_1;
+CREATE TABLE t1 (f1 INTEGER PRIMARY KEY, f2 CHAR(1)) engine=innodb;
+INSERT INTO t1 VALUES (1, 'a');
+INSERT INTO t1 VALUES (3, 'a');
+set binlog_format=STATEMENT;
+SET AUTOCOMMIT=ON;
+START TRANSACTION;
+SELECT * FROM t1 FOR UPDATE;
+f1 f2
+1 a
+3 a
+UPDATE t1 SET f2 = 'c' WHERE f1 > 1;
+connection node_2a;
+SET SESSION wsrep_sync_wait = 0;
+connect node_3, 127.0.0.1, root, , test, $NODE_MYPORT_3;
+connection node_3;
+SET SESSION wsrep_sync_wait = 0;
+connection node_2a;
+SET GLOBAL wsrep_provider_options = 'dbug=d,commit_monitor_enter_sync';
+SET GLOBAL debug_dbug = "d,sync.wsrep_apply_cb";
+connection node_3;
+INSERT INTO test.t1 VALUES (2, 'b');
+connection node_1;
+COMMIT;
+connection node_2a;
+SET SESSION wsrep_on = 0;
+SET SESSION wsrep_on = 1;
+SET GLOBAL debug_dbug = "";
+SET DEBUG_SYNC = "now SIGNAL signal.wsrep_apply_cb";
+connection node_2a;
+SET GLOBAL wsrep_provider_options = 'dbug=';
+SET GLOBAL wsrep_provider_options = 'signal=commit_monitor_enter_sync';
+connection node_1;
+SELECT COUNT(*) = 1 FROM t1 WHERE f2 = 'a';
+COUNT(*) = 1
+1
+SELECT COUNT(*) = 1 FROM t1 WHERE f2 = 'c';
+COUNT(*) = 1
+1
+SELECT * FROM t1;
+f1 f2
+1 a
+3 c
+connection node_2a;
+set session wsrep_sync_wait=15;
+set session wsrep_sync_wait=0;
+wsrep_local_replays
+1
+SELECT * FROM t1;
+f1 f2
+1 a
+2 b
+3 c
+SET DEBUG_SYNC = "RESET";
+#
+# test phase with real abort
+#
+connection node_1;
+set binlog_format=ROW;
+insert into t1 values (4, 'd');
+SET AUTOCOMMIT=ON;
+START TRANSACTION;
+UPDATE t1 SET f2 = 'd' WHERE f1 = 3;
+connection node_2a;
+SET GLOBAL wsrep_provider_options = 'dbug=d,commit_monitor_enter_sync';
+SET GLOBAL debug_dbug = "d,sync.wsrep_apply_cb";
+connection node_3;
+UPDATE test.t1 SET f2 = 'e' WHERE f1 = 3;
+connection node_1;
+COMMIT;
+connection node_2a;
+SET GLOBAL debug_dbug = "";
+SET DEBUG_SYNC = "now SIGNAL signal.wsrep_apply_cb";
+connection node_2a;
+SET GLOBAL wsrep_provider_options = 'dbug=';
+SET GLOBAL wsrep_provider_options = 'signal=commit_monitor_enter_sync';
+SET DEBUG_SYNC = "RESET";
+connection node_2a;
+set session wsrep_sync_wait=15;
+SELECT COUNT(*) = 1 FROM test.t1 WHERE f2 = 'e';
+COUNT(*) = 1
+1
+set session wsrep_sync_wait=0;
+STOP SLAVE;
+RESET SLAVE;
+DROP TABLE t1;
+connection node_1;
+DROP TABLE t1;
+RESET MASTER;
diff --git a/mysql-test/suite/galera/r/galera_defaults.result b/mysql-test/suite/galera/r/galera_defaults.result
index 6dd5258ff6d..9a5c1e54b06 100644
--- a/mysql-test/suite/galera/r/galera_defaults.result
+++ b/mysql-test/suite/galera/r/galera_defaults.result
@@ -1,7 +1,8 @@
connection node_2;
connection node_1;
-SELECT COUNT(*) `expect 48` FROM INFORMATION_SCHEMA.GLOBAL_VARIABLES WHERE VARIABLE_NAME LIKE 'wsrep_%';
-expect 48
+# Correct Galera library found
+SELECT COUNT(*) `expect 49` FROM INFORMATION_SCHEMA.GLOBAL_VARIABLES WHERE VARIABLE_NAME LIKE 'wsrep_%';
+expect 49
49
SELECT VARIABLE_NAME, VARIABLE_VALUE
FROM INFORMATION_SCHEMA.GLOBAL_VARIABLES
diff --git a/mysql-test/suite/galera/r/galera_toi_alter_auto_increment.result b/mysql-test/suite/galera/r/galera_toi_alter_auto_increment.result
index a23b0523140..a9923ba4211 100644
--- a/mysql-test/suite/galera/r/galera_toi_alter_auto_increment.result
+++ b/mysql-test/suite/galera/r/galera_toi_alter_auto_increment.result
@@ -1,7 +1,7 @@
connection node_2;
connection node_1;
connection node_1;
-CREATE TABLE ten (f1 INTEGER) ENGINE=InnoDB;
+CREATE TABLE ten (f1 INTEGER NOT NULL PRIMARY KEY) ENGINE=InnoDB;
INSERT INTO ten VALUES (1),(2),(3),(4),(5),(6),(7),(8),(9),(10);
CREATE TABLE t1 (f1 INTEGER AUTO_INCREMENT PRIMARY KEY, f2 INTEGER) ENGINE=InnoDB;
INSERT INTO t1 (f2) SELECT 1 FROM ten;
diff --git a/mysql-test/suite/galera/t/MDEV-16509.test b/mysql-test/suite/galera/t/MDEV-16509.test
index da79cdc7d4c..eecc0e5e4e6 100644
--- a/mysql-test/suite/galera/t/MDEV-16509.test
+++ b/mysql-test/suite/galera/t/MDEV-16509.test
@@ -3,6 +3,7 @@
#
--source include/galera_cluster.inc
+--source include/have_debug.inc
--source include/have_debug_sync.inc
diff --git a/mysql-test/suite/galera/t/galera#505.test b/mysql-test/suite/galera/t/galera#505.test
index 785b1411596..67ec1045119 100644
--- a/mysql-test/suite/galera/t/galera#505.test
+++ b/mysql-test/suite/galera/t/galera#505.test
@@ -5,12 +5,9 @@
--connection node_1
SET SESSION wsrep_sync_wait=0;
---disable_result_log
---disable_query_log
--let $galera_version=25.3.24
source ../../wsrep/include/check_galera_version.inc;
---enable_result_log
---enable_query_log
+
SET SESSION wsrep_sync_wait=DEFAULT;
# Convert "... pc.weight = N; ..." to "N; ..."
diff --git a/mysql-test/suite/galera/t/galera_as_slave_replay.cnf b/mysql-test/suite/galera/t/galera_as_slave_replay.cnf
new file mode 100644
index 00000000000..b1f9d7e9cbd
--- /dev/null
+++ b/mysql-test/suite/galera/t/galera_as_slave_replay.cnf
@@ -0,0 +1,11 @@
+!include ../galera_2nodes_as_slave.cnf
+
+[mysqld]
+binlog-format=row
+
+[mysqld.1]
+wsrep_restart_slave=1
+
+[mysqld.2]
+wsrep_restart_slave=1
+
diff --git a/mysql-test/suite/galera/t/galera_as_slave_replay.test b/mysql-test/suite/galera/t/galera_as_slave_replay.test
new file mode 100644
index 00000000000..93f95349e6d
--- /dev/null
+++ b/mysql-test/suite/galera/t/galera_as_slave_replay.test
@@ -0,0 +1,200 @@
+#
+# This test tests the operation of transaction replay for async replication slave.
+# If a potentially conflicting galera transaction arrives at
+# just the right time during the commit and has lock conflict with async replication transaction
+# applied by slave SQL thread, then the async replication transaction should either abort
+# or rollback and replay (depending on the nature of lock conflict).
+#
+
+--source include/have_innodb.inc
+--source include/have_debug.inc
+--source include/have_debug_sync.inc
+--source include/galera_have_debug_sync.inc
+
+--connect node_2a, 127.0.0.1, root, , test, $NODE_MYPORT_2
+
+--connection node_2a
+--source include/galera_cluster.inc
+#--source suite/galera/include/galera_have_debug_sync.inc
+
+#
+# node 1 is native MariaDB server operating as async replication master
+#
+--connection node_1
+RESET MASTER;
+
+--connection node_2a
+#
+# count the number of wsrep replay's done in the node
+#
+--let $wsrep_local_replays_old = `SELECT VARIABLE_VALUE FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_local_replays'`
+
+
+#
+# nodes 2 and 3 form a galera cluster, node 2 operates as slave for native MariaDB naster in node 1
+#
+--disable_query_log
+--eval CHANGE MASTER TO MASTER_HOST='127.0.0.1', MASTER_USER='root', MASTER_PORT=$NODE_MYPORT_1;
+--enable_query_log
+START SLAVE;
+
+--connection node_1
+CREATE TABLE t1 (f1 INTEGER PRIMARY KEY, f2 CHAR(1)) engine=innodb;
+INSERT INTO t1 VALUES (1, 'a');
+INSERT INTO t1 VALUES (3, 'a');
+
+#
+# use statement format replication to cause a false positive conflict with async replication transaction
+# and galera replication. The conflict will be on GAP lock, and slave SQL thread should rollback
+# and replay
+#
+set binlog_format=STATEMENT;
+
+SET AUTOCOMMIT=ON;
+START TRANSACTION;
+
+SELECT * FROM t1 FOR UPDATE;
+UPDATE t1 SET f2 = 'c' WHERE f1 > 1;
+
+--connection node_2a
+# wait for create table and inserts to be replicated from master
+SET SESSION wsrep_sync_wait = 0;
+--let $wait_condition = SELECT COUNT(*) = 2 FROM test.t1;
+--source include/wait_condition.inc
+
+# wait for create table and inserts to be replicated in cluster
+--connect node_3, 127.0.0.1, root, , test, $NODE_MYPORT_3
+--connection node_3
+SET SESSION wsrep_sync_wait = 0;
+--let $wait_condition = SELECT COUNT(*) = 2 FROM test.t1;
+--source include/wait_condition.inc
+
+--connection node_2a
+# Block the future commit of async replication
+--let $galera_sync_point = commit_monitor_enter_sync
+--source include/galera_set_sync_point.inc
+
+# block also the applier before applying begins
+SET GLOBAL debug_dbug = "d,sync.wsrep_apply_cb";
+
+#
+# now inject a conflicting insert from node 3, it will replicate with
+# earlier seqno (than async transaction) and pause before applying in node 2
+#
+--connection node_3
+INSERT INTO test.t1 VALUES (2, 'b');
+
+#
+# send the update from master, this will succeed here, beceuase of async replication.
+# async replication will apply this in node 2 and pause before commit phase,
+--connection node_1
+--error 0
+COMMIT;
+
+# Wait until async slave commit is blocked in node_2
+--connection node_2a
+--source include/galera_wait_sync_point.inc
+
+#
+# release the applier
+# note: have to clear wsrep_apply_cb sync point first, as async replication will go for replay
+# and as this sync point, after BF applier is released to progress
+#
+SET GLOBAL debug_dbug = "";
+SET DEBUG_SYNC = "now SIGNAL signal.wsrep_apply_cb";
+
+# Unblock the async slave commit
+--connection node_2a
+--source include/galera_clear_sync_point.inc
+--source include/galera_signal_sync_point.inc
+
+--connection node_1
+
+SELECT COUNT(*) = 1 FROM t1 WHERE f2 = 'a';
+SELECT COUNT(*) = 1 FROM t1 WHERE f2 = 'c';
+SELECT * FROM t1;
+
+--connection node_2a
+
+# wsrep_local_replays has increased by 1
+set session wsrep_sync_wait=15;
+--let $wsrep_local_replays_new = `SELECT VARIABLE_VALUE FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_local_replays'`
+set session wsrep_sync_wait=0;
+
+--disable_query_log
+--eval SELECT $wsrep_local_replays_new - $wsrep_local_replays_old = 1 AS wsrep_local_replays;
+--enable_query_log
+
+#
+# replaying of async transaction should be effective, and row 3 having 'c' in f2
+#
+SELECT * FROM t1;
+SET DEBUG_SYNC = "RESET";
+
+#********************************************************************************
+# test phase 2
+#********************************************************************************
+
+--echo #
+--echo # test phase with real abort
+--echo #
+
+--connection node_1
+
+set binlog_format=ROW;
+
+insert into t1 values (4, 'd');
+
+SET AUTOCOMMIT=ON;
+START TRANSACTION;
+
+UPDATE t1 SET f2 = 'd' WHERE f1 = 3;
+
+--connection node_2a
+# wait for the last insert to be replicated from master
+--let $wait_condition = SELECT COUNT(*) = 4 FROM test.t1;
+--source include/wait_condition.inc
+
+# Block the commit
+--let $galera_sync_point = commit_monitor_enter_sync
+--source include/galera_set_sync_point.inc
+
+# block applier
+SET GLOBAL debug_dbug = "d,sync.wsrep_apply_cb";
+
+# Inject a conflicting update from node 3
+--connection node_3
+UPDATE test.t1 SET f2 = 'e' WHERE f1 = 3;
+
+# send the update from master
+--connection node_1
+--error 0
+COMMIT;
+
+--connection node_2a
+
+# release the applier
+SET GLOBAL debug_dbug = "";
+SET DEBUG_SYNC = "now SIGNAL signal.wsrep_apply_cb";
+
+
+# Unblock the async slave commit
+--connection node_2a
+--source include/galera_clear_sync_point.inc
+--source include/galera_signal_sync_point.inc
+SET DEBUG_SYNC = "RESET";
+
+--connection node_2a
+
+set session wsrep_sync_wait=15;
+SELECT COUNT(*) = 1 FROM test.t1 WHERE f2 = 'e';
+set session wsrep_sync_wait=0;
+
+STOP SLAVE;
+RESET SLAVE;
+
+DROP TABLE t1;
+
+--connection node_1
+DROP TABLE t1;
+RESET MASTER;
diff --git a/mysql-test/suite/galera/t/galera_defaults.cnf b/mysql-test/suite/galera/t/galera_defaults.cnf
new file mode 100644
index 00000000000..fb143baf30f
--- /dev/null
+++ b/mysql-test/suite/galera/t/galera_defaults.cnf
@@ -0,0 +1,7 @@
+!include ../galera_2nodes.cnf
+
+[mysqld.1]
+wsrep_provider_options='base_port=@mysqld.1.#galera_port;gmcast.segment=1'
+
+[mysqld.2]
+wsrep_provider_options='base_port=@mysqld.2.#galera_port;gmcast.segment=1'
diff --git a/mysql-test/suite/galera/t/galera_defaults.test b/mysql-test/suite/galera/t/galera_defaults.test
index 3f8be268135..28e6f0cce38 100644
--- a/mysql-test/suite/galera/t/galera_defaults.test
+++ b/mysql-test/suite/galera/t/galera_defaults.test
@@ -1,25 +1,24 @@
#
# The purpose of this test is to preserve the current state of the following:
# * SHOW VARIABLES LIKE 'wsrep%'
-# * wsrep_provider_options
# * The names of the Galera status variables
#
+# Note that wsrep_provider_options contains paths and other non-deterministic parts
+#
# This way, if there is any change, inadvertent or not, the test will fail and the
# developer and QA will be alerted.
#
--source include/galera_cluster.inc
---source include/have_innodb.inc
+--source include/force_restart.inc
# Make sure that the test is operating on the right version of galera library.
---disable_query_log
---let $galera_version=25.3.20
+--let $galera_version=26.4.6
source ../wsrep/include/check_galera_version.inc;
---enable_query_log
# Global Variables
-SELECT COUNT(*) `expect 48` FROM INFORMATION_SCHEMA.GLOBAL_VARIABLES WHERE VARIABLE_NAME LIKE 'wsrep_%';
+SELECT COUNT(*) `expect 49` FROM INFORMATION_SCHEMA.GLOBAL_VARIABLES WHERE VARIABLE_NAME LIKE 'wsrep_%';
SELECT VARIABLE_NAME, VARIABLE_VALUE
FROM INFORMATION_SCHEMA.GLOBAL_VARIABLES
diff --git a/mysql-test/suite/galera/t/galera_fk_cascade_delete.test b/mysql-test/suite/galera/t/galera_fk_cascade_delete.test
index 6f0de0a1f4a..a3e0dbcf36f 100644
--- a/mysql-test/suite/galera/t/galera_fk_cascade_delete.test
+++ b/mysql-test/suite/galera/t/galera_fk_cascade_delete.test
@@ -40,11 +40,19 @@ set wsrep_sync_wait=0;
--let $wait_condition = SELECT COUNT(*) = 2 FROM child;
--source include/wait_condition.inc
+--let $wait_condition = SELECT COUNT(*) = 2 FROM parent;
+--source include/wait_condition.inc
+--let $wait_condition = SELECT COUNT(*) = 2 FROM grandparent;
+--source include/wait_condition.inc
DELETE FROM grandparent WHERE id = 1;
--connection node_1
--let $wait_condition = SELECT COUNT(*) = 1 FROM child;
--source include/wait_condition.inc
+--let $wait_condition = SELECT COUNT(*) = 1 FROM parent;
+--source include/wait_condition.inc
+--let $wait_condition = SELECT COUNT(*) = 1 FROM grandparent;
+--source include/wait_condition.inc
SELECT COUNT(*), COUNT(*) = 0 FROM parent WHERE grandparent_id = 1;
SELECT COUNT(*), COUNT(*) = 0 FROM child WHERE parent_id = 1;
diff --git a/mysql-test/suite/galera/t/galera_rsu_simple.test b/mysql-test/suite/galera/t/galera_rsu_simple.test
index 5841dbd8006..aa6f25b6db6 100644
--- a/mysql-test/suite/galera/t/galera_rsu_simple.test
+++ b/mysql-test/suite/galera/t/galera_rsu_simple.test
@@ -8,6 +8,9 @@
CREATE TABLE t1 (f1 INTEGER PRIMARY KEY) Engine=InnoDB;
--connection node_2
+--let $wait_condition = SELECT COUNT(*) = 1 FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 't1'
+--source include/wait_condition.inc
+
SET SESSION wsrep_OSU_method = "RSU";
ALTER TABLE t1 ADD COLUMN f2 INTEGER;
SELECT COUNT(*) = 2 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 't1';
diff --git a/mysql-test/suite/galera/t/galera_toi_alter_auto_increment.test b/mysql-test/suite/galera/t/galera_toi_alter_auto_increment.test
index 641d2101c80..793e87cb53e 100644
--- a/mysql-test/suite/galera/t/galera_toi_alter_auto_increment.test
+++ b/mysql-test/suite/galera/t/galera_toi_alter_auto_increment.test
@@ -7,7 +7,7 @@
--source include/have_innodb.inc
--connection node_1
-CREATE TABLE ten (f1 INTEGER) ENGINE=InnoDB;
+CREATE TABLE ten (f1 INTEGER NOT NULL PRIMARY KEY) ENGINE=InnoDB;
INSERT INTO ten VALUES (1),(2),(3),(4),(5),(6),(7),(8),(9),(10);
CREATE TABLE t1 (f1 INTEGER AUTO_INCREMENT PRIMARY KEY, f2 INTEGER) ENGINE=InnoDB;
@@ -83,6 +83,8 @@ SET GLOBAL auto_increment_offset = 1;
CREATE TABLE t1 (f1 INTEGER AUTO_INCREMENT PRIMARY KEY, f2 INTEGER) ENGINE=InnoDB;
--connection node_2a
+--let $wait_condition = SELECT COUNT(*) = 1 FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 't1'
+--source include/wait_condition.inc
ALTER TABLE t1 AUTO_INCREMENT=100;
diff --git a/mysql-test/suite/galera/t/galera_var_ignore_apply_errors.test b/mysql-test/suite/galera/t/galera_var_ignore_apply_errors.test
index 5232d4236e6..0f8efad5163 100644
--- a/mysql-test/suite/galera/t/galera_var_ignore_apply_errors.test
+++ b/mysql-test/suite/galera/t/galera_var_ignore_apply_errors.test
@@ -75,6 +75,9 @@ DELETE FROM t1 WHERE f1 = 1;
--connection node_1
SELECT COUNT(*) as expect_0 FROM t1;
--connection node_2
+--source include/galera_wait_ready.inc
+--let $wait_condition = SELECT VARIABLE_VALUE = 2 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_size';
+--source include/wait_condition.inc
SELECT COUNT(*) as expect_0 FROM t1;
DROP TABLE t1;
@@ -96,6 +99,9 @@ COMMIT;
--connection node_1
SELECT COUNT(*) as expect_1 FROM t1;
--connection node_2
+--source include/galera_wait_ready.inc
+--let $wait_condition = SELECT VARIABLE_VALUE = 2 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_size';
+--source include/wait_condition.inc
SELECT COUNT(*) as expect_1 FROM t1;
DROP TABLE t1;
@@ -120,6 +126,11 @@ DELETE FROM t1;
SELECT COUNT(*) as expect_0 FROM t1;
--connection node_2
+--source include/galera_wait_ready.inc
+--let $wait_condition = SELECT VARIABLE_VALUE = 2 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_size';
+--source include/wait_condition.inc
+--let $wait_condition = SELECT VARIABLE_VALUE = 'Primary' FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_status';
+--source include/wait_condition.inc
SELECT VARIABLE_VALUE = 'Primary' FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_status';
SELECT COUNT(*) as expect_0 FROM t1;
DROP TABLE t1;
@@ -154,6 +165,11 @@ SET AUTOCOMMIT=ON;
SELECT COUNT(*) as expect_0 FROM t1;
--connection node_2
+--source include/galera_wait_ready.inc
+--let $wait_condition = SELECT VARIABLE_VALUE = 2 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_size';
+--source include/wait_condition.inc
+--let $wait_condition = SELECT VARIABLE_VALUE = 'Primary' FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_status';
+--source include/wait_condition.inc
SELECT VARIABLE_VALUE = 'Primary' FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_status';
SELECT COUNT(*) as expect_0 FROM t1;
DROP TABLE t1;
@@ -183,6 +199,11 @@ DELETE t1, t2 FROM t1 JOIN t2 WHERE t1.f1 = t2.f1;
SELECT COUNT(*) as expect_0 FROM t1;
--connection node_2
+--source include/galera_wait_ready.inc
+--let $wait_condition = SELECT VARIABLE_VALUE = 2 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_size';
+--source include/wait_condition.inc
+--let $wait_condition = SELECT VARIABLE_VALUE = 'Primary' FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_status';
+--source include/wait_condition.inc
SELECT VARIABLE_VALUE = 'Primary' FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_status';
SELECT COUNT(*) as expect_0 FROM t1;
DROP TABLE t1,t2;
@@ -212,6 +233,11 @@ SELECT COUNT(*) as expect_0 FROM parent;
SELECT COUNT(*) as expect_0 FROM child;
--connection node_2
+--source include/galera_wait_ready.inc
+--let $wait_condition = SELECT VARIABLE_VALUE = 2 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_size';
+--source include/wait_condition.inc
+--let $wait_condition = SELECT VARIABLE_VALUE = 'Primary' FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_status';
+--source include/wait_condition.inc
SELECT VARIABLE_VALUE = 'Primary' FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_status';
SELECT COUNT(*) as expect_0 FROM parent;
SELECT COUNT(*) as expect_0 FROM child;
diff --git a/mysql-test/suite/galera_3nodes/r/galera_parallel_apply_3nodes.result b/mysql-test/suite/galera_3nodes/r/galera_parallel_apply_3nodes.result
index 2acfe56bede..087f9e7e279 100644
--- a/mysql-test/suite/galera_3nodes/r/galera_parallel_apply_3nodes.result
+++ b/mysql-test/suite/galera_3nodes/r/galera_parallel_apply_3nodes.result
@@ -1,27 +1,35 @@
connection node_2;
connection node_1;
+connect node_3, 127.0.0.1, root, , test, $NODE_MYPORT_3;
+connect node_1_ctrl, 127.0.0.1, root, , test, $NODE_MYPORT_1;
CREATE TABLE t1 (f1 INTEGER PRIMARY KEY) ENGINE=InnoDB;
INSERT INTO t1 VALUES (1);
connection node_3;
SET GLOBAL wsrep_slave_threads = 2;
+connection node_1_ctrl;
+SET SESSION wsrep_sync_wait=0;
connection node_1;
+SET DEBUG_SYNC = 'wsrep_before_certification SIGNAL before_cert WAIT_FOR continue';
UPDATE t1 SET f1 = f1 + 10;;
+connection node_1_ctrl;
+SET DEBUG_SYNC = 'now WAIT_FOR before_cert';
+SET GLOBAL debug_dbug = '+d,sync.wsrep_retry_autocommit';
connection node_2;
UPDATE t1 SET f1 = f1 + 100;;
+connection node_1_ctrl;
+SET DEBUG_SYNC = 'now WAIT_FOR wsrep_retry_autocommit_reached';
+SET GLOBAL debug_dbug = NULL;
+SET DEBUG_SYNC = 'now SIGNAL wsrep_retry_autocommit_continue';
connection node_1;
-SELECT COUNT(*) FROM t1;
-COUNT(*)
-1
connection node_2;
-SELECT COUNT(*) FROM t1;
-COUNT(*)
-1
connection node_3;
-SELECT COUNT(*) FROM t1;
-COUNT(*)
+SELECT f1 = 111 FROM t1;
+f1 = 111
1
SELECT COUNT(*) IN (1, 2) FROM INFORMATION_SCHEMA.PROCESSLIST WHERE USER = 'system user' AND STATE LIKE '%committed%';
COUNT(*) IN (1, 2)
1
-SET GLOBAL wsrep_slave_threads = 1;;
+SET GLOBAL wsrep_slave_threads = DEFAULT;
DROP TABLE t1;
+connection node_1;
+SET DEBUG_SYNC= 'RESET';
diff --git a/mysql-test/suite/galera_3nodes/t/galera_parallel_apply_3nodes.test b/mysql-test/suite/galera_3nodes/t/galera_parallel_apply_3nodes.test
index f1168e59193..84629c96c65 100644
--- a/mysql-test/suite/galera_3nodes/t/galera_parallel_apply_3nodes.test
+++ b/mysql-test/suite/galera_3nodes/t/galera_parallel_apply_3nodes.test
@@ -5,46 +5,71 @@
--source include/galera_cluster.inc
--source include/have_innodb.inc
+--source include/have_debug.inc
+--source include/have_debug_sync.inc
---let $galera_connection_name = node_3
---let $galera_server_number = 3
---source include/galera_connect.inc
+--connect node_3, 127.0.0.1, root, , test, $NODE_MYPORT_3
+--connect node_1_ctrl, 127.0.0.1, root, , test, $NODE_MYPORT_1
CREATE TABLE t1 (f1 INTEGER PRIMARY KEY) ENGINE=InnoDB;
INSERT INTO t1 VALUES (1);
+--let $wsrep_last_committed_before = `SELECT VARIABLE_VALUE FROM INFORMATION_SCHEMA.SESSION_STATUS WHERE VARIABLE_NAME = 'wsrep_last_committed'`
+
--connection node_3
---let $wsrep_slave_threads_orig = `SELECT @@wsrep_slave_threads`
SET GLOBAL wsrep_slave_threads = 2;
+--connection node_1_ctrl
+SET SESSION wsrep_sync_wait=0;
+
+#
+# We will make the following UPDATE depend on the UPDATE below
+#
--connection node_1
+SET DEBUG_SYNC = 'wsrep_before_certification SIGNAL before_cert WAIT_FOR continue';
--send UPDATE t1 SET f1 = f1 + 10;
+--connection node_1_ctrl
+SET DEBUG_SYNC = 'now WAIT_FOR before_cert';
+SET GLOBAL debug_dbug = '+d,sync.wsrep_retry_autocommit';
+
--connection node_2
--send UPDATE t1 SET f1 = f1 + 100;
---connection node_1
#
-# Note that test is not deterministic. We have following cases possible
-# (1) Both updates are certified locally and then executed by the applier
-# (2) Certification of update in node_1 fails because applier has started
-# update from node_2
-# (3) Certification of update in node_2 fails because applier has started
-# update from node_1
+# Let's wait for the first UPDATE the be BF aborted
+#
+--connection node_1_ctrl
+SET DEBUG_SYNC = 'now WAIT_FOR wsrep_retry_autocommit_reached';
+
+#
+# and make sure the second has committed
#
---error 0,ER_LOCK_DEADLOCK
+--let $wait_condition = SELECT VARIABLE_VALUE > $wsrep_last_committed_before FROM INFORMATION_SCHEMA.SESSION_STATUS WHERE VARIABLE_NAME = 'wsrep_last_committed'
+--source include/wait_condition.inc
+
+#
+# now release the first UPDATE.
+#
+SET GLOBAL debug_dbug = NULL;
+SET DEBUG_SYNC = 'now SIGNAL wsrep_retry_autocommit_continue';
+
+#
+# Both UPDATEs should succeed.
+#
+--connection node_1
--reap
-SELECT COUNT(*) FROM t1;
--connection node_2
---error 0,ER_LOCK_DEADLOCK
--reap
-SELECT COUNT(*) FROM t1;
--connection node_3
-SELECT COUNT(*) FROM t1;
+SELECT f1 = 111 FROM t1;
SELECT COUNT(*) IN (1, 2) FROM INFORMATION_SCHEMA.PROCESSLIST WHERE USER = 'system user' AND STATE LIKE '%committed%';
---eval SET GLOBAL wsrep_slave_threads = $wsrep_slave_threads_orig;
+SET GLOBAL wsrep_slave_threads = DEFAULT;
DROP TABLE t1;
+
+--connection node_1
+SET DEBUG_SYNC= 'RESET';
diff --git a/mysql-test/suite/innodb/r/foreign_key.result b/mysql-test/suite/innodb/r/foreign_key.result
index cd8b2d82a06..399e35b698f 100644
--- a/mysql-test/suite/innodb/r/foreign_key.result
+++ b/mysql-test/suite/innodb/r/foreign_key.result
@@ -409,6 +409,7 @@ CREATE TABLE x AS SELECT * FROM t1;
ERROR XAE07: XAER_RMFAIL: The command cannot be executed when global transaction is in the ACTIVE state
connect con1,localhost,root,,test;
SET foreign_key_checks= OFF, innodb_lock_wait_timeout= 1;
+SET lock_wait_timeout=5;
ALTER TABLE t1 ADD FOREIGN KEY f (a) REFERENCES t1 (pk), LOCK=EXCLUSIVE;
ERROR HY000: Lock wait timeout exceeded; try restarting transaction
disconnect con1;
diff --git a/mysql-test/suite/innodb/r/instant_alter_crash.result b/mysql-test/suite/innodb/r/instant_alter_crash.result
index ef0e310492a..a2c388fa103 100644
--- a/mysql-test/suite/innodb/r/instant_alter_crash.result
+++ b/mysql-test/suite/innodb/r/instant_alter_crash.result
@@ -74,7 +74,6 @@ DELETE FROM t1;
# Kill the server
disconnect ddl;
# restart
-SET @saved_frequency= @@GLOBAL.innodb_purge_rseg_truncate_frequency;
SET GLOBAL innodb_purge_rseg_truncate_frequency=1;
FOUND 3 /\[Note\] InnoDB: Rolled back recovered transaction / in mysqld.1.err
SELECT * FROM t1;
@@ -138,6 +137,23 @@ header=0x080008030000 (id=0x000000000000000100)
UNLOCK TABLES;
DELETE FROM t2;
InnoDB 0 transactions not purged
+#
+# MDEV-24323 Crash on recovery after kill during instant ADD COLUMN
+#
+connect ddl, localhost, root;
+CREATE TABLE t3(id INT PRIMARY KEY, c2 INT, v2 INT AS(c2) VIRTUAL, UNIQUE(v2))
+ENGINE=InnoDB;
+INSERT INTO t3 SET id=1,c2=1;
+SET DEBUG_SYNC='innodb_alter_inplace_before_commit SIGNAL ddl WAIT_FOR ever';
+ALTER TABLE t3 ADD COLUMN c3 TEXT NOT NULL DEFAULT 'sic transit gloria mundi';
+connection default;
+SET DEBUG_SYNC='now WAIT_FOR ddl';
+SET GLOBAL innodb_flush_log_at_trx_commit=1;
+SET debug_dbug='+d,dict_sys_mutex_avoid';
+INSERT INTO t1 VALUES(0,0);
+# Kill the server
+disconnect ddl;
+# restart
SHOW CREATE TABLE t1;
Table Create Table
t1 CREATE TABLE `t1` (
@@ -154,6 +170,14 @@ t2 CREATE TABLE `t2` (
PRIMARY KEY (`id`),
UNIQUE KEY `c2` (`c2`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 ROW_FORMAT=REDUNDANT
-DROP TABLE t1,t2;
+SHOW CREATE TABLE t3;
+Table Create Table
+t3 CREATE TABLE `t3` (
+ `id` int(11) NOT NULL,
+ `c2` int(11) DEFAULT NULL,
+ `v2` int(11) GENERATED ALWAYS AS (`c2`) VIRTUAL,
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `v2` (`v2`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1
+DROP TABLE t1,t2,t3;
db.opt
-SET GLOBAL innodb_purge_rseg_truncate_frequency=@saved_frequency;
diff --git a/mysql-test/suite/innodb/t/foreign_key.test b/mysql-test/suite/innodb/t/foreign_key.test
index ebde222ee69..94d736e97d4 100644
--- a/mysql-test/suite/innodb/t/foreign_key.test
+++ b/mysql-test/suite/innodb/t/foreign_key.test
@@ -414,6 +414,7 @@ INSERT INTO t1 VALUES (1,2);
CREATE TABLE x AS SELECT * FROM t1;
--connect (con1,localhost,root,,test)
SET foreign_key_checks= OFF, innodb_lock_wait_timeout= 1;
+SET lock_wait_timeout=5;
--error ER_LOCK_WAIT_TIMEOUT
ALTER TABLE t1 ADD FOREIGN KEY f (a) REFERENCES t1 (pk), LOCK=EXCLUSIVE;# Cleanup
--disconnect con1
diff --git a/mysql-test/suite/innodb/t/instant_alter_crash.test b/mysql-test/suite/innodb/t/instant_alter_crash.test
index f9b18aa589f..43db8f619f3 100644
--- a/mysql-test/suite/innodb/t/instant_alter_crash.test
+++ b/mysql-test/suite/innodb/t/instant_alter_crash.test
@@ -91,7 +91,6 @@ DELETE FROM t1;
disconnect ddl;
--source include/start_mysqld.inc
-SET @saved_frequency= @@GLOBAL.innodb_purge_rseg_truncate_frequency;
SET GLOBAL innodb_purge_rseg_truncate_frequency=1;
let SEARCH_FILE= $MYSQLTEST_VARDIR/log/mysqld.1.err;
@@ -177,11 +176,32 @@ UNLOCK TABLES;
DELETE FROM t2;
--source include/wait_all_purged.inc
+--echo #
+--echo # MDEV-24323 Crash on recovery after kill during instant ADD COLUMN
+--echo #
+connect ddl, localhost, root;
+CREATE TABLE t3(id INT PRIMARY KEY, c2 INT, v2 INT AS(c2) VIRTUAL, UNIQUE(v2))
+ENGINE=InnoDB;
+INSERT INTO t3 SET id=1,c2=1;
+
+SET DEBUG_SYNC='innodb_alter_inplace_before_commit SIGNAL ddl WAIT_FOR ever';
+--send
+ALTER TABLE t3 ADD COLUMN c3 TEXT NOT NULL DEFAULT 'sic transit gloria mundi';
+
+connection default;
+SET DEBUG_SYNC='now WAIT_FOR ddl';
+SET GLOBAL innodb_flush_log_at_trx_commit=1;
+SET debug_dbug='+d,dict_sys_mutex_avoid';
+INSERT INTO t1 VALUES(0,0);
+
+--source include/kill_mysqld.inc
+disconnect ddl;
+--source include/start_mysqld.inc
+
SHOW CREATE TABLE t1;
SHOW CREATE TABLE t2;
-DROP TABLE t1,t2;
+SHOW CREATE TABLE t3;
+DROP TABLE t1,t2,t3;
--remove_files_wildcard $MYSQLD_DATADIR/test #sql*.frm
--list_files $MYSQLD_DATADIR/test
-
-SET GLOBAL innodb_purge_rseg_truncate_frequency=@saved_frequency;
diff --git a/mysql-test/suite/maria/create.opt b/mysql-test/suite/maria/create.opt
new file mode 100644
index 00000000000..b1392bfd485
--- /dev/null
+++ b/mysql-test/suite/maria/create.opt
@@ -0,0 +1 @@
+--symbolic-links=1
diff --git a/mysql-test/suite/mariabackup/include/corrupt-page.pl b/mysql-test/suite/mariabackup/include/corrupt-page.pl
new file mode 100644
index 00000000000..d5c75dbde55
--- /dev/null
+++ b/mysql-test/suite/mariabackup/include/corrupt-page.pl
@@ -0,0 +1,146 @@
+use strict;
+use warnings;
+use Fcntl qw(:DEFAULT :seek);
+do "$ENV{MTR_SUITE_DIR}/../innodb/include/crc32.pl";
+
+sub corrupt_space_page_id {
+ my $file_name = shift;
+ my @pages_to_corrupt = @_;
+
+ my $page_size = $ENV{INNODB_PAGE_SIZE};
+
+ sysopen my $ibd_file, $file_name, O_RDWR || die "Cannot open $file_name\n";
+ sysread($ibd_file, $_, 38) || die "Cannot read $file_name\n";
+ my $space = unpack("x[34]N", $_);
+ foreach my $page_no (@pages_to_corrupt) {
+ $space += 10; # generate wrong space id
+ sysseek($ibd_file, $page_size * $page_no, SEEK_SET)
+ || die "Cannot seek $file_name\n";
+
+ my $head = pack("Nx[18]", $page_no + 10); # generate wrong page number
+ my $body = chr(0) x ($page_size - 38 - 8);
+
+ # Calculate innodb_checksum_algorithm=crc32 for the unencrypted page.
+ # The following bytes are excluded:
+ # bytes 0..3 (the checksum is stored there)
+ # bytes 26..37 (encryption key version, post-encryption checksum, tablespace id)
+ # bytes $page_size-8..$page_size-1 (checksum, LSB of FIL_PAGE_LSN)
+ my $polynomial = 0x82f63b78; # CRC-32C
+ my $ck = mycrc32($head, 0, $polynomial) ^ mycrc32($body, 0, $polynomial);
+
+ my $page= pack("N",$ck).$head.pack("NNN",1,$ck,$space).$body.pack("Nx[4]",$ck);
+ die unless syswrite($ibd_file, $page, $page_size) == $page_size;
+ }
+ close $ibd_file;
+}
+
+sub extend_space {
+ my $file_name = shift;
+ my $n_pages = shift;
+
+ my $page_size = $ENV{INNODB_PAGE_SIZE};
+ my $page;
+
+ sysopen my $ibd_file, $file_name, O_RDWR || die "Cannot open $file_name\n";
+ sysread($ibd_file, $page, $page_size)
+ || die "Cannot read $file_name\n";
+ my $size = unpack("N", substr($page, 46, 4));
+ my $packed_new_size = pack("N", $size + $n_pages);
+ substr($page, 46, 4, $packed_new_size);
+
+ my $head = substr($page, 4, 22);
+ my $body = substr($page, 38, $page_size - 38 - 8);
+ my $polynomial = 0x82f63b78; # CRC-32C
+ my $ck = mycrc32($head, 0, $polynomial) ^ mycrc32($body, 0, $polynomial);
+ my $packed_ck = pack("N", $ck);
+ substr($page, 0, 4, $packed_ck);
+ substr($page, $page_size - 8, 4, $packed_ck);
+
+ sysseek($ibd_file, 0, SEEK_SET)
+ || die "Cannot seek $file_name\n";
+ die unless syswrite($ibd_file, $page, $page_size) == $page_size;
+
+ sysseek($ibd_file, 0, SEEK_END)
+ || die "Cannot seek $file_name\n";
+ my $pages_size = $page_size*$n_pages;
+ my $pages = chr(0) x $pages_size;
+ die unless syswrite($ibd_file, $pages, $pages_size) == $pages_size;
+ close $ibd_file;
+ return $size;
+}
+
+sub die_if_page_is_not_zero {
+ my $file_name = shift;
+ my @pages_to_check = @_;
+
+ no locale;
+ my $page_size = $ENV{INNODB_PAGE_SIZE};
+ my $zero_page = chr(0) x $page_size;
+ sysopen my $ibd_file, $file_name, O_RDWR || die "Cannot open $file_name\n";
+ foreach my $page_no_to_check (@pages_to_check) {
+ sysseek($ibd_file, $page_size*$page_no_to_check, SEEK_SET) ||
+ die "Cannot seek $file_name\n";
+ sysread($ibd_file, my $read_page, $page_size) ||
+ die "Cannot read $file_name\n";
+ die "The page $page_no_to_check is not zero-filed in $file_name"
+ if ($read_page cmp $zero_page);
+ }
+ close $ibd_file;
+}
+
+sub print_corrupted_pages_file {
+ my $file_in = shift;
+ my $file_out = shift;
+ open my $fh, '<', $file_in || die $!;
+ my $line_number = 0;
+ my $space = {};
+ my @spaces;
+ while (my $line = <$fh>) {
+ ++$line_number;
+ if ($line_number & 1) {
+ my ($name, $id) = split(/ /, $line);
+ $space->{name} = $name;
+ }
+ else {
+ $space->{pages} = $line;
+ push (@spaces, $space);
+ $space = {};
+ }
+ }
+ close $fh;
+ my @sorted_spaces = sort { $a->{name} cmp $b->{name} } @spaces;
+ open $fh, '>', $file_out || die $!;
+ foreach my $space (@sorted_spaces) {
+ print $fh $space->{name};
+ print $fh "\n";
+ print $fh $space->{pages};
+ }
+ close $fh;
+}
+
+sub append_corrupted_pages {
+ my $file_name = shift;
+ my $space_name = shift;
+ my $pages = shift;
+ open my $fh, '<', $file_name || die $!;
+ my $line_number = 0;
+ my $space_line;
+ while (my $line = <$fh>) {
+ ++$line_number;
+ if ($line_number & 1) {
+ my ($name, $id) = split(/ /, $line);
+ if ($name eq $space_name) {
+ $space_line = $line;
+ last;
+ }
+ }
+ }
+ close $fh;
+ if (not defined $space_line) {
+ die "Can't find requested space $space_name in file $file_name";
+ }
+ open $fh, '>>', $file_name || die $!;
+ print $fh $space_line;
+ print $fh "$pages\n";
+ close $fh;
+}
diff --git a/mysql-test/suite/mariabackup/incremental_ddl_during_backup.test b/mysql-test/suite/mariabackup/incremental_ddl_during_backup.test
index b1ab17a6d8f..ebdb2137523 100644
--- a/mysql-test/suite/mariabackup/incremental_ddl_during_backup.test
+++ b/mysql-test/suite/mariabackup/incremental_ddl_during_backup.test
@@ -22,7 +22,7 @@ INSERT into t1 values(1);
--let after_copy_test_t2=DROP TABLE test.t2
--let after_copy_test_t3=CREATE INDEX a_i ON test.t3(i);
--let before_copy_test_t10=DROP TABLE test.t10
---let wait_innodb_redo_before_copy=test/t10
+--let wait_innodb_redo_before_copy_test_t10 = 1
# mariabackup should crash with assertion if MDEV-24026 is not fixed
exec $XTRABACKUP --defaults-file=$MYSQLTEST_VARDIR/my.cnf --backup --target-dir=$incremental_dir --incremental-basedir=$basedir --dbug=+d,mariabackup_events,mariabackup_inject_code;
diff --git a/mysql-test/suite/mariabackup/log_page_corruption.opt b/mysql-test/suite/mariabackup/log_page_corruption.opt
new file mode 100644
index 00000000000..c44c611ed60
--- /dev/null
+++ b/mysql-test/suite/mariabackup/log_page_corruption.opt
@@ -0,0 +1 @@
+--innodb-checksum-algorithm=crc32
diff --git a/mysql-test/suite/mariabackup/log_page_corruption.result b/mysql-test/suite/mariabackup/log_page_corruption.result
new file mode 100644
index 00000000000..be29ea435b6
--- /dev/null
+++ b/mysql-test/suite/mariabackup/log_page_corruption.result
@@ -0,0 +1,145 @@
+########
+# Test for generating "innodb_corrupted_pages" file during full and
+# incremental backup, including DDL processing
+###
+
+CREATE TABLE t1_corrupted(c INT) ENGINE INNODB;
+CREATE TABLE t2_corrupted(c INT) ENGINE INNODB;
+CREATE TABLE t3(c INT) ENGINE INNODB;
+CREATE TABLE t5_corrupted_to_rename(c INT) ENGINE INNODB;
+CREATE TABLE t6_corrupted_to_drop(c INT) ENGINE INNODB;
+CREATE TABLE t7_corrupted_to_alter(c INT) ENGINE INNODB;
+CREATE TABLE t1_inc_corrupted(c INT) ENGINE INNODB;
+CREATE TABLE t2_inc_corrupted(c INT) ENGINE INNODB;
+CREATE TABLE t3_inc(c INT) ENGINE INNODB;
+CREATE TABLE t5_inc_corrupted_to_rename(c INT) ENGINE INNODB;
+CREATE TABLE t6_inc_corrupted_to_drop(c INT) ENGINE INNODB;
+CREATE TABLE t7_inc_corrupted_to_alter(c INT) ENGINE INNODB;
+INSERT INTO t1_corrupted VALUES (3), (4), (5), (6), (7), (8), (9);
+INSERT INTO t2_corrupted VALUES (3), (4), (5), (6), (7), (8), (9);
+INSERT INTO t3 VALUES (3), (4), (5), (6), (7), (8), (9);
+INSERT INTO t5_corrupted_to_rename VALUES (3), (4), (5), (6), (7), (8), (9);
+INSERT INTO t6_corrupted_to_drop VALUES (3), (4), (5), (6), (7), (8), (9);
+INSERT INTO t7_corrupted_to_alter VALUES (3), (4), (5), (6), (7), (8), (9);
+# Corrupt tables
+# restart
+# Backup must fail due to page corruption
+FOUND 1 /Database page corruption detected.*/ in backup.log
+# "innodb_corrupted_pages" file must not exist
+# Backup must fail, but "innodb_corrupted_pages" file must be created due to --log-innodb-page-corruption option
+FOUND 1 /Database page corruption detected.*/ in backup.log
+--- "innodb_corrupted_pages" file content: ---
+test/t1_corrupted
+6 8 9
+test/t2_corrupted
+7 8 10
+test/t4_corrupted_new
+1
+test/t5_corrupted_to_rename_renamed
+6
+test/t7_corrupted_to_alter
+3
+------
+INSERT INTO t1_inc_corrupted VALUES (3), (4), (5), (6), (7), (8), (9);
+INSERT INTO t2_inc_corrupted VALUES (3), (4), (5), (6), (7), (8), (9);
+INSERT INTO t3_inc VALUES (3), (4), (5), (6), (7), (8), (9);
+# restart
+# Backup must fail, but "innodb_corrupted_pages" file must be created due to --log-innodb-page-corruption option
+--- "innodb_corrupted_pages" file content: ---
+test/t1_corrupted
+6 8 9
+test/t1_inc_corrupted
+6 8 9
+test/t2_corrupted
+7 8 10
+test/t2_inc_corrupted
+7 8 10
+test/t4_inc_corrupted_new
+1
+test/t5_corrupted_to_rename_renamed
+6
+test/t5_inc_corrupted_to_rename_renamed
+6
+test/t7_inc_corrupted_to_alter
+3
+------
+# Check if corrupted pages were copied to delta files, and non-corrupted pages are not copied.
+DROP TABLE t1_corrupted;
+DROP TABLE t2_corrupted;
+DROP TABLE t4_corrupted_new;
+DROP TABLE t5_corrupted_to_rename_renamed;
+DROP TABLE t7_corrupted_to_alter;
+DROP TABLE t1_inc_corrupted;
+DROP TABLE t2_inc_corrupted;
+DROP TABLE t4_inc_corrupted_new;
+DROP TABLE t5_inc_corrupted_to_rename_renamed;
+DROP TABLE t7_inc_corrupted_to_alter;
+
+########
+# Test for --prepare with "innodb_corrupted_pages" file
+###
+
+# Extend some tablespace and corrupt extended pages for full backup
+# restart
+# Full backup with --log-innodb-page-corruption
+--- "innodb_corrupted_pages" file content: ---
+test/t3
+6 8
+------
+# Extend some tablespace and corrupt extended pages for incremental backup
+# restart
+# Incremental backup --log-innodb-page-corruption
+--- "innodb_corrupted_pages" file content: ---
+test/t3
+6 8
+test/t3_inc
+6 8
+------
+# Full backup prepare
+# "innodb_corrupted_pages" file must not exist after successful prepare
+FOUND 1 /was successfuly fixed.*/ in backup.log
+# Check that fixed pages are zero-filled
+# Incremental backup prepare
+# "innodb_corrupted_pages" file must not exist after successful prepare
+# do not remove "innodb_corrupted_pages" in incremental dir
+FOUND 1 /was successfuly fixed.*/ in backup.log
+# Check that fixed pages are zero-filled
+# shutdown server
+# remove datadir
+# xtrabackup move back
+# restart
+SELECT * FROM t3;
+c
+3
+4
+5
+6
+7
+8
+9
+SELECT * FROM t3_inc;
+c
+3
+4
+5
+6
+7
+8
+9
+# Test the case when not all corrupted pages are fixed
+
+# Add some fake corrupted pages
+# Full backup prepare
+FOUND 1 /Error: corrupted page.*/ in backup.log
+--- "innodb_corrupted_pages" file content: ---
+test/t3
+3
+------
+# Incremental backup prepare
+FOUND 1 /Error: corrupted page.*/ in backup.log
+--- "innodb_corrupted_pages" file content: ---
+test/t3
+3
+------
+DROP TABLE t3;
+DROP TABLE t3_inc;
diff --git a/mysql-test/suite/mariabackup/log_page_corruption.test b/mysql-test/suite/mariabackup/log_page_corruption.test
new file mode 100644
index 00000000000..e9419687288
--- /dev/null
+++ b/mysql-test/suite/mariabackup/log_page_corruption.test
@@ -0,0 +1,426 @@
+--source include/have_debug.inc
+
+--echo ########
+--echo # Test for generating "innodb_corrupted_pages" file during full and
+--echo # incremental backup, including DDL processing
+--echo ###
+--echo
+
+CREATE TABLE t1_corrupted(c INT) ENGINE INNODB;
+CREATE TABLE t2_corrupted(c INT) ENGINE INNODB;
+CREATE TABLE t3(c INT) ENGINE INNODB;
+CREATE TABLE t5_corrupted_to_rename(c INT) ENGINE INNODB;
+CREATE TABLE t6_corrupted_to_drop(c INT) ENGINE INNODB;
+CREATE TABLE t7_corrupted_to_alter(c INT) ENGINE INNODB;
+
+CREATE TABLE t1_inc_corrupted(c INT) ENGINE INNODB;
+CREATE TABLE t2_inc_corrupted(c INT) ENGINE INNODB;
+CREATE TABLE t3_inc(c INT) ENGINE INNODB;
+CREATE TABLE t5_inc_corrupted_to_rename(c INT) ENGINE INNODB;
+CREATE TABLE t6_inc_corrupted_to_drop(c INT) ENGINE INNODB;
+CREATE TABLE t7_inc_corrupted_to_alter(c INT) ENGINE INNODB;
+
+# Fill tables with several pages
+INSERT INTO t1_corrupted VALUES (3), (4), (5), (6), (7), (8), (9);
+INSERT INTO t2_corrupted VALUES (3), (4), (5), (6), (7), (8), (9);
+INSERT INTO t3 VALUES (3), (4), (5), (6), (7), (8), (9);
+INSERT INTO t5_corrupted_to_rename VALUES (3), (4), (5), (6), (7), (8), (9);
+INSERT INTO t6_corrupted_to_drop VALUES (3), (4), (5), (6), (7), (8), (9);
+INSERT INTO t7_corrupted_to_alter VALUES (3), (4), (5), (6), (7), (8), (9);
+
+--let MYSQLD_DATADIR=`select @@datadir`
+--let INNODB_PAGE_SIZE=`select @@innodb_page_size`
+
+--source include/shutdown_mysqld.inc
+--echo # Corrupt tables
+perl;
+do "$ENV{MTR_SUITE_DIR}/include/corrupt-page.pl";
+my $schema = "$ENV{MYSQLD_DATADIR}/test";
+
+my $last_page_no = extend_space("$schema/t1_corrupted.ibd", 4);
+corrupt_space_page_id("$schema/t1_corrupted.ibd",
+ $last_page_no, $last_page_no + 2, $last_page_no + 3);
+
+$last_page_no = extend_space("$schema/t2_corrupted.ibd", 5);
+corrupt_space_page_id("$schema/t2_corrupted.ibd",
+ $last_page_no + 1, $last_page_no + 2, $last_page_no + 4);
+
+$last_page_no = extend_space("$schema/t5_corrupted_to_rename.ibd", 1);
+corrupt_space_page_id("$schema/t5_corrupted_to_rename.ibd", $last_page_no);
+
+$last_page_no = extend_space("$schema/t6_corrupted_to_drop.ibd", );
+corrupt_space_page_id("$schema/t6_corrupted_to_drop.ibd", $last_page_no);
+EOF
+--source include/start_mysqld.inc
+
+--let targetdir=$MYSQLTEST_VARDIR/tmp/backup
+--let $backuplog=$MYSQLTEST_VARDIR/tmp/backup.log
+--let corrupted_pages_file = $targetdir/innodb_corrupted_pages
+--let corrupted_pages_file_filt = $MYSQLTEST_VARDIR/tmp/innodb_corrupted_pages_filt
+--let perl_result_file=$MYSQLTEST_VARDIR/tmp/perl_result
+
+--echo # Backup must fail due to page corruption
+--disable_result_log
+--error 1
+exec $XTRABACKUP --defaults-file=$MYSQLTEST_VARDIR/my.cnf --backup --target-dir=$targetdir > $backuplog;
+--enable_result_log
+
+--let SEARCH_PATTERN=Database page corruption detected.*
+--let SEARCH_FILE=$backuplog
+--source include/search_pattern_in_file.inc
+--echo # "innodb_corrupted_pages" file must not exist
+--error 1
+--file_exists $corrupted_pages_file
+--rmdir $targetdir
+
+--let after_load_tablespaces=CREATE TABLE test.t4_corrupted_new ENGINE=INNODB SELECT UUID() from test.seq_1_to_10
+--let add_corrupted_page_for_test_t4_corrupted_new=1
+--let after_copy_test_t5_corrupted_to_rename=RENAME TABLE test.t5_corrupted_to_rename TO test.t5_corrupted_to_rename_renamed
+--let after_copy_test_t6_corrupted_to_drop=DROP TABLE test.t6_corrupted_to_drop
+--let after_copy_test_t7_corrupted_to_alter=ALTER TABLE test.t7_corrupted_to_alter ADD COLUMN (d INT)
+--let add_corrupted_page_for_test_t7_corrupted_to_alter=3
+
+--echo # Backup must fail, but "innodb_corrupted_pages" file must be created due to --log-innodb-page-corruption option
+--disable_result_log
+--error 1
+--exec $XTRABACKUP --defaults-file=$MYSQLTEST_VARDIR/my.cnf --backup --log-innodb-page-corruption --target-dir=$targetdir --dbug=+d,mariabackup_events,mariabackup_inject_code > $backuplog
+--enable_result_log
+
+--let SEARCH_PATTERN=Database page corruption detected.*
+--let SEARCH_FILE=$backuplog
+--source include/search_pattern_in_file.inc
+--echo --- "innodb_corrupted_pages" file content: ---
+perl;
+do "$ENV{MTR_SUITE_DIR}/include/corrupt-page.pl";
+print_corrupted_pages_file($ENV{corrupted_pages_file},
+ $ENV{corrupted_pages_file_filt});
+EOF
+--cat_file $corrupted_pages_file_filt
+--echo ------
+--let after_load_tablespaces=
+--let add_corrupted_page_for_test_t4_corrupted_new=
+--let after_copy_test_t5_corrupted_to_rename=
+--let after_copy_test_t6_corrupted_to_drop=
+--let after_copy_test_t7_corrupted_to_alter=
+--let add_corrupted_page_for_test_t7_corrupted_to_alter=
+# Fill tables for incremental backup with several pages
+INSERT INTO t1_inc_corrupted VALUES (3), (4), (5), (6), (7), (8), (9);
+INSERT INTO t2_inc_corrupted VALUES (3), (4), (5), (6), (7), (8), (9);
+INSERT INTO t3_inc VALUES (3), (4), (5), (6), (7), (8), (9);
+
+--source include/shutdown_mysqld.inc
+perl;
+do "$ENV{MTR_SUITE_DIR}/include/corrupt-page.pl";
+my $schema="$ENV{MYSQLD_DATADIR}/test";
+
+open(my $fh, '>', $ENV{perl_result_file}) or die $!;
+
+my $last_page_no = extend_space("$schema/t1_inc_corrupted.ibd", 4);
+corrupt_space_page_id("$schema/t1_inc_corrupted.ibd",
+ $last_page_no, $last_page_no + 2, $last_page_no + 3);
+print $fh "$last_page_no\n";
+
+$last_page_no = extend_space("$schema/t2_inc_corrupted.ibd", 5);
+corrupt_space_page_id("$schema/t2_inc_corrupted.ibd",
+ $last_page_no + 1, $last_page_no + 2, $last_page_no + 4);
+print $fh "$last_page_no\n";
+
+$last_page_no = extend_space("$schema/t5_inc_corrupted_to_rename.ibd", 1);
+corrupt_space_page_id("$schema/t5_inc_corrupted_to_rename.ibd", $last_page_no);
+print $fh "$last_page_no\n";
+
+$last_page_no = extend_space("$schema/t6_inc_corrupted_to_drop.ibd", );
+corrupt_space_page_id("$schema/t6_inc_corrupted_to_drop.ibd", $last_page_no);
+
+close $fh;
+EOF
+--source include/start_mysqld.inc
+
+--let incdir=$MYSQLTEST_VARDIR/tmp/backup_inc
+
+--let after_load_tablespaces=CREATE TABLE test.t4_inc_corrupted_new ENGINE=INNODB SELECT UUID() from test.seq_1_to_10
+--let add_corrupted_page_for_test_t4_inc_corrupted_new=1
+--let after_copy_test_t5_inc_corrupted_to_rename=RENAME TABLE test.t5_inc_corrupted_to_rename TO test.t5_inc_corrupted_to_rename_renamed
+--let after_copy_test_t6_inc_corrupted_to_drop=DROP TABLE test.t6_inc_corrupted_to_drop
+--let after_copy_test_t7_inc_corrupted_to_alter=ALTER TABLE test.t7_inc_corrupted_to_alter ADD COLUMN (d INT)
+--let add_corrupted_page_for_test_t7_inc_corrupted_to_alter=3
+
+--echo # Backup must fail, but "innodb_corrupted_pages" file must be created due to --log-innodb-page-corruption option
+--disable_result_log
+--error 1
+--exec $XTRABACKUP --defaults-file=$MYSQLTEST_VARDIR/my.cnf --backup --log-innodb-page-corruption --target-dir=$incdir --incremental-basedir=$targetdir --dbug=+d,mariabackup_events,mariabackup_inject_code > $backuplog
+--disable_result_log
+
+--let after_load_tablespaces=
+--let add_corrupted_page_for_test_t4_inc_corrupted_new=
+--let after_copy_test_t5_inc_corrupted_to_rename=
+--let after_copy_test_t6_inc_corrupted_to_drop=
+--let after_copy_test_t7_inc_corrupted_to_alter=
+--let add_corrupted_page_for_test_t7_inc_corrupted_to_alter=
+
+--let SEARCH_PATTERN=Database page corruption detected.*
+--let SEARCH_FILE=$backuplog
+--source include/search_pattern_in_file.inc
+--let corrupted_pages_file = $incdir/innodb_corrupted_pages
+--echo --- "innodb_corrupted_pages" file content: ---
+perl;
+do "$ENV{MTR_SUITE_DIR}/include/corrupt-page.pl";
+print_corrupted_pages_file($ENV{corrupted_pages_file},
+ $ENV{corrupted_pages_file_filt});
+EOF
+--cat_file $corrupted_pages_file_filt
+--echo ------
+
+--echo # Check if corrupted pages were copied to delta files, and non-corrupted pages are not copied.
+perl;
+use strict;
+use warnings;
+my $schema = "$ENV{incdir}/test";
+
+open(my $fh, '<', $ENV{perl_result_file}) or die $!;
+
+my $last_page_no = <$fh>;
+die_if_no_pages("$schema/t1_corrupted.ibd.delta",
+ $last_page_no, $last_page_no + 2, $last_page_no + 3);
+
+$last_page_no = <$fh>;
+die_if_no_pages("$schema/t2_corrupted.ibd.delta",
+ $last_page_no + 1, $last_page_no + 2, $last_page_no + 4);
+
+$last_page_no = <$fh>;
+die_if_no_pages("$schema/t5_corrupted_to_rename_renamed.ibd.delta",
+ $last_page_no);
+
+close $fh;
+
+die_if_not_empty("$schema/t3.ibd.delta");
+
+sub read_first_page_from_delta {
+ my $file_name = shift;
+ my $pages_count = shift;
+
+ open my $file, '<:raw', $file_name || die "Cannot open $file_name\n";
+ read $file, my $buffer, $pages_count*4 || die "Cannot read $file_name\n";
+ close $file;
+
+ return unpack("N[$pages_count]", $buffer);
+}
+
+sub die_if_no_pages {
+ my $file_name = shift;
+ my @check_pages = @_;
+ my @read_pages =
+ read_first_page_from_delta($file_name, scalar(@check_pages) + 1);
+ for (my $i = 1; $i < @check_pages + 1; ++$i) {
+ my $check_page_no = $check_pages[$i - 1];
+ die "Corrupted page $check_page_no was not copied to $file_name."
+ if ($i >= @read_pages || $read_pages[$i] != $check_page_no);
+ }
+}
+
+sub die_if_not_empty {
+ my $file_name = shift;
+ my ($magic, $full) = read_first_page_from_delta($file_name, 2);
+ die "Delta $file_name must be empty."
+ if ($full != 0xFFFFFFFF);
+}
+EOF
+--rmdir $incdir
+--rmdir $targetdir
+
+DROP TABLE t1_corrupted;
+DROP TABLE t2_corrupted;
+DROP TABLE t4_corrupted_new;
+DROP TABLE t5_corrupted_to_rename_renamed;
+DROP TABLE t7_corrupted_to_alter;
+DROP TABLE t1_inc_corrupted;
+DROP TABLE t2_inc_corrupted;
+DROP TABLE t4_inc_corrupted_new;
+DROP TABLE t5_inc_corrupted_to_rename_renamed;
+DROP TABLE t7_inc_corrupted_to_alter;
+
+--echo
+--echo ########
+--echo # Test for --prepare with "innodb_corrupted_pages" file
+--echo ###
+--echo
+
+--echo # Extend some tablespace and corrupt extended pages for full backup
+--source include/shutdown_mysqld.inc
+perl;
+do "$ENV{MTR_SUITE_DIR}/include/corrupt-page.pl";
+my $schema="$ENV{MYSQLD_DATADIR}/test";
+my $last_page_no = extend_space("$schema/t3.ibd", 3);
+corrupt_space_page_id("$schema/t3.ibd", $last_page_no, $last_page_no + 2);
+open(my $fh, '>', $ENV{perl_result_file}) or die $!;
+print $fh "$last_page_no\n";
+close $fh;
+EOF
+--source include/start_mysqld.inc
+
+--echo # Full backup with --log-innodb-page-corruption
+--disable_result_log
+--error 1
+--exec $XTRABACKUP --defaults-file=$MYSQLTEST_VARDIR/my.cnf --backup --log-innodb-page-corruption --target-dir=$targetdir
+--enable_result_log
+--let corrupted_pages_file = $targetdir/innodb_corrupted_pages
+--echo --- "innodb_corrupted_pages" file content: ---
+perl;
+do "$ENV{MTR_SUITE_DIR}/include/corrupt-page.pl";
+print_corrupted_pages_file($ENV{corrupted_pages_file},
+ $ENV{corrupted_pages_file_filt});
+EOF
+--cat_file $corrupted_pages_file_filt
+--echo ------
+
+--echo # Extend some tablespace and corrupt extended pages for incremental backup
+--source include/shutdown_mysqld.inc
+perl;
+do "$ENV{MTR_SUITE_DIR}/include/corrupt-page.pl";
+my $schema="$ENV{MYSQLD_DATADIR}/test";
+my $last_page_no = extend_space("$schema/t3_inc.ibd", 3);
+corrupt_space_page_id("$schema/t3_inc.ibd", $last_page_no, $last_page_no + 2);
+open(my $fh, '>>', $ENV{perl_result_file}) or die $!;
+print $fh "$last_page_no";
+close $fh;
+EOF
+--source include/start_mysqld.inc
+
+--echo # Incremental backup --log-innodb-page-corruption
+--disable_result_log
+--error 1
+--exec $XTRABACKUP --defaults-file=$MYSQLTEST_VARDIR/my.cnf --backup --log-innodb-page-corruption --target-dir=$incdir --incremental-basedir=$targetdir --dbug=+d,mariabackup_events,mariabackup_inject_code > $backuplog
+--disable_result_log
+--let corrupted_pages_file = $incdir/innodb_corrupted_pages
+--echo --- "innodb_corrupted_pages" file content: ---
+perl;
+do "$ENV{MTR_SUITE_DIR}/include/corrupt-page.pl";
+print_corrupted_pages_file($ENV{corrupted_pages_file},
+ $ENV{corrupted_pages_file_filt});
+EOF
+--cat_file $corrupted_pages_file_filt
+--echo ------
+
+--let targetdir2=$targetdir-2
+--let incdir2=$incdir-2
+perl;
+use lib "lib";
+use My::Handles { suppress_init_messages => 1 };
+use My::File::Path;
+copytree($ENV{'targetdir'}, $ENV{'targetdir2'});
+copytree($ENV{'incdir'}, $ENV{'incdir2'});
+EOF
+
+--echo # Full backup prepare
+--disable_result_log
+exec $XTRABACKUP --prepare --target-dir=$targetdir > $backuplog;
+--enable_result_log
+
+--echo # "innodb_corrupted_pages" file must not exist after successful prepare
+--error 1
+--file_exists $targetdir/innodb_corrupted_pages
+--let SEARCH_PATTERN=was successfuly fixed.*
+--let SEARCH_FILE=$backuplog
+--source include/search_pattern_in_file.inc
+
+--echo # Check that fixed pages are zero-filled
+perl;
+do "$ENV{MTR_SUITE_DIR}/include/corrupt-page.pl";
+open(my $fh, '<', $ENV{perl_result_file}) or die $!;
+my $last_page_no = <$fh>;
+close $fh;
+my $schema = "$ENV{targetdir}/test";
+die_if_page_is_not_zero("$schema/t3.ibd", $last_page_no, $last_page_no + 2);
+EOF
+
+--echo # Incremental backup prepare
+--disable_result_log
+exec $XTRABACKUP --prepare --target-dir=$targetdir --incremental-dir=$incdir > $backuplog;
+--enable_result_log
+
+--echo # "innodb_corrupted_pages" file must not exist after successful prepare
+--error 1
+--file_exists $targetdir/innodb_corrupted_pages
+--echo # do not remove "innodb_corrupted_pages" in incremental dir
+--file_exists $incdir/innodb_corrupted_pages
+--let SEARCH_PATTERN=was successfuly fixed.*
+--let SEARCH_FILE=$backuplog
+--source include/search_pattern_in_file.inc
+
+--echo # Check that fixed pages are zero-filled
+perl;
+do "$ENV{MTR_SUITE_DIR}/include/corrupt-page.pl";
+open(my $fh, '<', $ENV{perl_result_file}) or die $!;
+my $last_page_no_full = <$fh>;
+my $last_page_no_inc = <$fh>;
+close $fh;
+my $schema = "$ENV{targetdir}/test";
+die_if_page_is_not_zero("$schema/t3.ibd",
+ $last_page_no_full, $last_page_no_full + 2);
+die_if_page_is_not_zero("$schema/t3_inc.ibd",
+ $last_page_no_inc, $last_page_no_inc + 2);
+EOF
+
+--source include/restart_and_restore.inc
+
+SELECT * FROM t3;
+SELECT * FROM t3_inc;
+
+--echo # Test the case when not all corrupted pages are fixed
+--echo
+--echo # Add some fake corrupted pages
+perl;
+do "$ENV{MTR_SUITE_DIR}/include/corrupt-page.pl";
+append_corrupted_pages(
+ "$ENV{targetdir2}/innodb_corrupted_pages", 'test/t3', '3 4');
+append_corrupted_pages(
+ "$ENV{incdir2}/innodb_corrupted_pages", 'test/t3_inc', '4 5');
+EOF
+
+--echo # Full backup prepare
+--disable_result_log
+--error 1
+exec $XTRABACKUP --prepare --target-dir=$targetdir2 > $backuplog;
+--enable_result_log
+
+--let SEARCH_PATTERN=Error: corrupted page.*
+--let SEARCH_FILE=$backuplog
+--source include/search_pattern_in_file.inc
+--let corrupted_pages_file = $targetdir2/innodb_corrupted_pages
+--echo --- "innodb_corrupted_pages" file content: ---
+perl;
+do "$ENV{MTR_SUITE_DIR}/include/corrupt-page.pl";
+print_corrupted_pages_file($ENV{corrupted_pages_file},
+ $ENV{corrupted_pages_file_filt});
+EOF
+--cat_file $corrupted_pages_file_filt
+--echo ------
+
+--echo # Incremental backup prepare
+--disable_result_log
+--error 1
+exec $XTRABACKUP --prepare --target-dir=$targetdir2 --incremental-dir=$incdir2 > $backuplog;
+--enable_result_log
+
+--let SEARCH_PATTERN=Error: corrupted page.*
+--let SEARCH_FILE=$backuplog
+--source include/search_pattern_in_file.inc
+--let corrupted_pages_file = $targetdir2/innodb_corrupted_pages
+--echo --- "innodb_corrupted_pages" file content: ---
+perl;
+do "$ENV{MTR_SUITE_DIR}/include/corrupt-page.pl";
+print_corrupted_pages_file($ENV{corrupted_pages_file},
+ $ENV{corrupted_pages_file_filt});
+EOF
+--cat_file $corrupted_pages_file_filt
+--echo ------
+
+DROP TABLE t3;
+DROP TABLE t3_inc;
+--remove_file $backuplog
+--remove_file $perl_result_file
+--remove_file $corrupted_pages_file_filt
+--rmdir $targetdir
+--rmdir $targetdir2
+--rmdir $incdir
+--rmdir $incdir2
diff --git a/mysql-test/suite/roles/show_grants.result b/mysql-test/suite/roles/show_grants.result
index 1867133525d..7ae499a9cfc 100644
--- a/mysql-test/suite/roles/show_grants.result
+++ b/mysql-test/suite/roles/show_grants.result
@@ -147,3 +147,18 @@ drop role test_role2;
delete from mysql.roles_mapping where Role='test_role1';
delete from mysql.roles_mapping where Role='test_role2';
flush privileges;
+#
+# MDEV-24289: show grants missing with grant option
+#
+create role anel;
+GRANT SELECT, UPDATE, DELETE, ALTER ON *.* TO 'anel';
+SHOW GRANTS for 'anel';
+Grants for anel
+GRANT SELECT, UPDATE, DELETE, ALTER ON *.* TO `anel`
+create role MariaDB_admin;
+GRANT SELECT, UPDATE, DELETE, ALTER ON *.* TO 'MariaDB_admin' WITH GRANT OPTION;
+SHOW GRANTS for 'MariaDB_admin';
+Grants for MariaDB_admin
+GRANT SELECT, UPDATE, DELETE, ALTER ON *.* TO `MariaDB_admin` WITH GRANT OPTION
+drop role MariaDB_admin;
+drop role anel;
diff --git a/mysql-test/suite/roles/show_grants.test b/mysql-test/suite/roles/show_grants.test
index 9c15d8b8b2b..fc2165ac53b 100644
--- a/mysql-test/suite/roles/show_grants.test
+++ b/mysql-test/suite/roles/show_grants.test
@@ -88,3 +88,16 @@ drop role test_role2;
delete from mysql.roles_mapping where Role='test_role1';
delete from mysql.roles_mapping where Role='test_role2';
flush privileges;
+
+--echo #
+--echo # MDEV-24289: show grants missing with grant option
+--echo #
+create role anel;
+GRANT SELECT, UPDATE, DELETE, ALTER ON *.* TO 'anel';
+SHOW GRANTS for 'anel';
+
+create role MariaDB_admin;
+GRANT SELECT, UPDATE, DELETE, ALTER ON *.* TO 'MariaDB_admin' WITH GRANT OPTION;
+SHOW GRANTS for 'MariaDB_admin';
+drop role MariaDB_admin;
+drop role anel;
diff --git a/mysql-test/suite/wsrep/include/check_galera_version.inc b/mysql-test/suite/wsrep/include/check_galera_version.inc
index 32d01197f94..7a58e657f40 100644
--- a/mysql-test/suite/wsrep/include/check_galera_version.inc
+++ b/mysql-test/suite/wsrep/include/check_galera_version.inc
@@ -21,23 +21,27 @@ SELECT CAST(REGEXP_REPLACE(@GALERA_VERSION,'^(\\d+)\\.(\\d+)\\.(\\d+).*','\\3')
# Actual
SELECT VARIABLE_VALUE INTO @ACTUAL_GALERA_VERSION FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE 'wsrep_provider_version';
+SELECT CAST(REGEXP_REPLACE(@ACTUAL_GALERA_VERSION,'^(\\d+)\\.(\\d+).*','\\1') AS UNSIGNED) INTO @ACTUAL_GALERA_MAJOR_VERSION;
SELECT CAST(REGEXP_REPLACE(@ACTUAL_GALERA_VERSION,'^[\\d\\.]*(\\d+)\\.\\d+.*','\\1') AS UNSIGNED) INTO @ACTUAL_GALERA_MINOR_VERSION;
SELECT CAST(REGEXP_REPLACE(@ACTUAL_GALERA_VERSION,'^[\\d\\.]*\\.(\\d+).*','\\1') AS UNSIGNED) INTO @ACTUAL_GALERA_RELEASE_VERSION;
+--enable_query_log
# For testing
#SELECT @GALERA_MAJOR_VERSION;
#SELECT @GALERA_MINOR_VERSION;
#SELECT @GALERA_RELEASE_VERSION;
-#SELECT @ACTUAL_GALERA_VERSION;
+#SELECT @ACTUAL_GALERA_MAJOR_VERSION;
#SELECT @ACTUAL_GALERA_MINOR_VERSION;
#SELECT @ACTUAL_GALERA_RELEASE_VERSION;
-if (!`SELECT (@ACTUAL_GALERA_MINOR_VERSION > @GALERA_MINOR_VERSION) OR
- (@ACTUAL_GALERA_MINOR_VERSION = @GALERA_MINOR_VERSION AND
+if (!`SELECT (@ACTUAL_GALERA_MAJOR_VERSION >= @GALERA_MAJOR_VERSION AND @ACTUAL_GALERA_MINOR_VERSION > @GALERA_MINOR_VERSION) OR
+ (@ACTUAL_GALERA_MAJOR_VERSION = @GALERA_MAJOR_VERSION AND
+ @ACTUAL_GALERA_MINOR_VERSION = @GALERA_MINOR_VERSION AND
@ACTUAL_GALERA_RELEASE_VERSION >= @GALERA_RELEASE_VERSION)
`)
{
skip Test requires Galera library version >= $galera_version;
}
---enable_query_log
+--echo # Correct Galera library found
+
diff --git a/mysys/my_mess.c b/mysys/my_mess.c
index 7bc4c038cb6..c9a1aee64b6 100644
--- a/mysys/my_mess.c
+++ b/mysys/my_mess.c
@@ -21,6 +21,8 @@ void my_message_stderr(uint error __attribute__((unused)),
DBUG_ENTER("my_message_stderr");
DBUG_PRINT("enter",("message: %s",str));
(void) fflush(stdout);
+ if (MyFlags & (ME_NOTE | ME_ERROR_LOG_ONLY))
+ DBUG_VOID_RETURN;
if (MyFlags & ME_BELL)
(void) fputc('\007', stderr);
if (my_progname)
diff --git a/plugin/handler_socket/handlersocket/database.cpp b/plugin/handler_socket/handlersocket/database.cpp
index 91b095cb655..937b1177ae4 100644
--- a/plugin/handler_socket/handlersocket/database.cpp
+++ b/plugin/handler_socket/handlersocket/database.cpp
@@ -471,9 +471,7 @@ dbcontext::close_tables_if()
unlock_tables_if();
DENA_VERBOSE(100, fprintf(stderr, "HNDSOCK close tables\n"));
close_thread_tables(thd);
- #if MYSQL_VERSION_ID >= 50505
- thd->mdl_context.release_transactional_locks();
- #endif
+ thd->mdl_context.release_transactional_locks(thd);
if (!table_vec.empty()) {
statistic_increment(close_tables_count, &LOCK_status);
table_vec.clear();
diff --git a/plugin/type_inet/sql_type_inet.h b/plugin/type_inet/sql_type_inet.h
index 8c42431ccaa..03153af6ddc 100644
--- a/plugin/type_inet/sql_type_inet.h
+++ b/plugin/type_inet/sql_type_inet.h
@@ -653,7 +653,7 @@ public:
}
bool
subquery_type_allows_materialization(const Item *inner,
- const Item *outer) const override
+ const Item *outer, bool) const override
{
/*
Example:
diff --git a/scripts/wsrep_sst_rsync.sh b/scripts/wsrep_sst_rsync.sh
index d42cf014009..809d3e81e16 100644
--- a/scripts/wsrep_sst_rsync.sh
+++ b/scripts/wsrep_sst_rsync.sh
@@ -401,6 +401,14 @@ then
MODULE="rsync_sst"
RSYNC_PID="$WSREP_SST_OPT_DATA/$MODULE.pid"
+ # give some time for lingering rsync from previous SST to complete
+ check_round=0
+ while check_pid $RSYNC_PID && [ $check_round -lt 10 ]
+ do
+ wsrep_log_info "lingering rsync daemon found at startup, waiting for it to exit"
+ check_round=$(( check_round + 1 ))
+ sleep 1
+ done
if check_pid $RSYNC_PID
then
diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc
index 7f00a194698..470f59fe15f 100644
--- a/sql/ha_partition.cc
+++ b/sql/ha_partition.cc
@@ -4443,7 +4443,7 @@ int ha_partition::write_row(const uchar * buf)
DBUG_ASSERT(!m_file[part_id]->row_logging);
error= m_file[part_id]->ha_write_row(buf);
- if (have_auto_increment && !table->s->next_number_keypart)
+ if (!error && have_auto_increment && !table->s->next_number_keypart)
set_auto_increment_if_higher(table->next_number_field);
exit:
diff --git a/sql/ha_partition.h b/sql/ha_partition.h
index 8d0557f4ae4..3c3e394d59f 100644
--- a/sql/ha_partition.h
+++ b/sql/ha_partition.h
@@ -92,7 +92,6 @@ public:
bool auto_inc_initialized;
mysql_mutex_t auto_inc_mutex; /**< protecting auto_inc val */
ulonglong next_auto_inc_val; /**< first non reserved value */
- ulonglong prev_auto_inc_val; /**< stored next_auto_inc_val */
/**
Hash of partition names. Initialized in the first ha_partition::open()
for the table_share. After that it is read-only, i.e. no locking required.
@@ -104,7 +103,6 @@ public:
Partition_share()
: auto_inc_initialized(false),
next_auto_inc_val(0),
- prev_auto_inc_val(0),
partition_name_hash_initialized(false),
partition_names(NULL)
{
@@ -429,24 +427,6 @@ private:
MY_BITMAP m_locked_partitions;
/** Stores shared auto_increment etc. */
Partition_share *part_share;
- /** Fix spurious -Werror=overloaded-virtual in GCC 9 */
- virtual void restore_auto_increment(ulonglong prev_insert_id) override
- {
- handler::restore_auto_increment(prev_insert_id);
- }
- /** Store and restore next_auto_inc_val over duplicate key errors. */
- void store_auto_increment() override
- {
- DBUG_ASSERT(part_share);
- part_share->prev_auto_inc_val= part_share->next_auto_inc_val;
- handler::store_auto_increment();
- }
- void restore_auto_increment() override
- {
- DBUG_ASSERT(part_share);
- part_share->next_auto_inc_val= part_share->prev_auto_inc_val;
- handler::restore_auto_increment();
- }
void sum_copy_info(handler *file);
void sum_copy_infos();
void reset_copy_info() override;
diff --git a/sql/handler.cc b/sql/handler.cc
index 01825c13da6..314fe7acb8c 100644
--- a/sql/handler.cc
+++ b/sql/handler.cc
@@ -3514,7 +3514,6 @@ int handler::update_auto_increment()
THD *thd= table->in_use;
struct system_variables *variables= &thd->variables;
int result=0, tmp;
- enum enum_check_fields save_count_cuted_fields;
DBUG_ENTER("handler::update_auto_increment");
/*
@@ -3656,10 +3655,10 @@ int handler::update_auto_increment()
nr, append ? nb_reserved_values : 0));
/* Store field without warning (Warning will be printed by insert) */
- save_count_cuted_fields= thd->count_cuted_fields;
- thd->count_cuted_fields= CHECK_FIELD_IGNORE;
- tmp= table->next_number_field->store((longlong)nr, TRUE);
- thd->count_cuted_fields= save_count_cuted_fields;
+ {
+ Check_level_instant_set check_level_save(thd, CHECK_FIELD_IGNORE);
+ tmp= table->next_number_field->store((longlong)nr, TRUE);
+ }
if (unlikely(tmp)) // Out of range value in store
{
diff --git a/sql/handler.h b/sql/handler.h
index 41da458aa2a..4e1e3f0413f 100644
--- a/sql/handler.h
+++ b/sql/handler.h
@@ -3278,9 +3278,6 @@ private:
*/
Handler_share **ha_share;
- /** Stores next_insert_id for handling duplicate key errors. */
- ulonglong m_prev_insert_id;
-
public:
handler(handlerton *ht_arg, TABLE_SHARE *share_arg)
:table_share(share_arg), table(0),
@@ -3308,7 +3305,7 @@ public:
m_psi_numrows(0),
m_psi_locker(NULL),
row_logging(0), row_logging_init(0),
- m_lock_type(F_UNLCK), ha_share(NULL), m_prev_insert_id(0)
+ m_lock_type(F_UNLCK), ha_share(NULL)
{
DBUG_PRINT("info",
("handler created F_UNLCK %d F_RDLCK %d F_WRLCK %d",
@@ -4028,16 +4025,6 @@ public:
insert_id_for_cur_row;
}
- /** Store and restore next_insert_id over duplicate key errors. */
- virtual void store_auto_increment()
- {
- m_prev_insert_id= next_insert_id;
- }
- virtual void restore_auto_increment()
- {
- restore_auto_increment(m_prev_insert_id);
- }
-
virtual void update_create_info(HA_CREATE_INFO *create_info) {}
int check_old_types();
virtual int assign_to_keycache(THD* thd, HA_CHECK_OPT* check_opt)
diff --git a/sql/item.cc b/sql/item.cc
index dc90b9fe1b3..2bf53dc885b 100644
--- a/sql/item.cc
+++ b/sql/item.cc
@@ -1442,16 +1442,12 @@ int Item::save_in_field_no_warnings(Field *field, bool no_conversions)
int res;
TABLE *table= field->table;
THD *thd= table->in_use;
- enum_check_fields tmp= thd->count_cuted_fields;
- my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->write_set);
+ Check_level_instant_set check_level_save(thd, CHECK_FIELD_IGNORE);
Sql_mode_save sms(thd);
thd->variables.sql_mode&= ~(MODE_NO_ZERO_IN_DATE | MODE_NO_ZERO_DATE);
thd->variables.sql_mode|= MODE_INVALID_DATES;
- thd->count_cuted_fields= CHECK_FIELD_IGNORE;
-
+ my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->write_set);
res= save_in_field(field, no_conversions);
-
- thd->count_cuted_fields= tmp;
dbug_tmp_restore_column_map(table->write_set, old_map);
return res;
}
diff --git a/sql/item.h b/sql/item.h
index fb480b4c578..677112e448c 100644
--- a/sql/item.h
+++ b/sql/item.h
@@ -973,6 +973,13 @@ public:
void set_name_no_truncate(THD *thd, const char *str, uint length,
CHARSET_INFO *cs);
void init_make_send_field(Send_field *tmp_field, const Type_handler *h);
+ void share_name_with(const Item *item)
+ {
+ name= item->name;
+ common_flags= static_cast<uint8>
+ ((common_flags & ~IS_AUTO_GENERATED_NAME) |
+ (item->common_flags & IS_AUTO_GENERATED_NAME));
+ }
virtual void cleanup();
virtual void make_send_field(THD *thd, Send_field *field);
diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc
index 83eb605f463..bfd415344ef 100644
--- a/sql/item_cmpfunc.cc
+++ b/sql/item_cmpfunc.cc
@@ -321,19 +321,18 @@ static bool convert_const_to_int(THD *thd, Item_field *field_item,
if ((*item)->const_item() && !(*item)->is_expensive())
{
TABLE *table= field->table;
- sql_mode_t orig_sql_mode= thd->variables.sql_mode;
- enum_check_fields orig_count_cuted_fields= thd->count_cuted_fields;
+ Sql_mode_save sql_mode(thd);
+ Check_level_instant_set check_level_save(thd, CHECK_FIELD_IGNORE);
my_bitmap_map *old_maps[2] = { NULL, NULL };
ulonglong UNINIT_VAR(orig_field_val); /* original field value if valid */
/* table->read_set may not be set if we come here from a CREATE TABLE */
if (table && table->read_set)
- dbug_tmp_use_all_columns(table, old_maps,
+ dbug_tmp_use_all_columns(table, old_maps,
table->read_set, table->write_set);
/* For comparison purposes allow invalid dates like 2000-01-32 */
- thd->variables.sql_mode= (orig_sql_mode & ~MODE_NO_ZERO_DATE) |
+ thd->variables.sql_mode= (thd->variables.sql_mode & ~MODE_NO_ZERO_DATE) |
MODE_INVALID_DATES;
- thd->count_cuted_fields= CHECK_FIELD_IGNORE;
/*
Store the value of the field/constant because the call to save_in_field
@@ -370,8 +369,6 @@ static bool convert_const_to_int(THD *thd, Item_field *field_item,
/* orig_field_val must be a valid value that can be restored back. */
DBUG_ASSERT(!result);
}
- thd->variables.sql_mode= orig_sql_mode;
- thd->count_cuted_fields= orig_count_cuted_fields;
if (table && table->read_set)
dbug_tmp_restore_column_maps(table->read_set, table->write_set, old_maps);
}
diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc
index cbde9599073..7916bcb45a9 100644
--- a/sql/item_subselect.cc
+++ b/sql/item_subselect.cc
@@ -1520,7 +1520,8 @@ Item_in_subselect::Item_in_subselect(THD *thd, Item * left_exp,
pushed_cond_guards(NULL), do_not_convert_to_sj(FALSE), is_jtbm_merged(FALSE),
is_jtbm_const_tab(FALSE), is_flattenable_semijoin(FALSE),
is_registered_semijoin(FALSE),
- upper_item(0)
+ upper_item(0),
+ converted_from_in_predicate(FALSE)
{
DBUG_ENTER("Item_in_subselect::Item_in_subselect");
DBUG_PRINT("info", ("in_strategy: %u", (uint)in_strategy));
diff --git a/sql/item_subselect.h b/sql/item_subselect.h
index ec4398b9a76..a346def72f1 100644
--- a/sql/item_subselect.h
+++ b/sql/item_subselect.h
@@ -613,12 +613,18 @@ public:
Item_func_not_all *upper_item; // point on NOT/NOP before ALL/SOME subquery
+ /*
+ SET to TRUE if IN subquery is converted from an IN predicate
+ */
+ bool converted_from_in_predicate;
+
Item_in_subselect(THD *thd_arg, Item * left_expr, st_select_lex *select_lex);
Item_in_subselect(THD *thd_arg):
Item_exists_subselect(thd_arg), left_expr_cache(0), first_execution(TRUE),
in_strategy(SUBS_NOT_TRANSFORMED),
pushed_cond_guards(NULL), func(NULL), do_not_convert_to_sj(FALSE),
- is_jtbm_merged(FALSE), is_jtbm_const_tab(FALSE), upper_item(0) {}
+ is_jtbm_merged(FALSE), is_jtbm_const_tab(FALSE), upper_item(0),
+ converted_from_in_predicate(FALSE) {}
void cleanup() override;
subs_type substype() override { return IN_SUBS; }
void reset() override
diff --git a/sql/log_event_server.cc b/sql/log_event_server.cc
index dd7e166059a..aa3e48ee873 100644
--- a/sql/log_event_server.cc
+++ b/sql/log_event_server.cc
@@ -2988,10 +2988,10 @@ error:
if (thd->transaction_rollback_request)
{
trans_rollback_implicit(thd);
- thd->mdl_context.release_transactional_locks();
+ thd->release_transactional_locks();
}
else if (! thd->in_multi_stmt_transaction_mode())
- thd->mdl_context.release_transactional_locks();
+ thd->release_transactional_locks();
else
thd->mdl_context.release_statement_locks();
@@ -4039,7 +4039,7 @@ int Xid_log_event::do_commit()
{
bool res;
res= trans_commit(thd); /* Automatically rolls back on error. */
- thd->mdl_context.release_transactional_locks();
+ thd->release_transactional_locks();
return res;
}
#endif
@@ -4088,10 +4088,7 @@ int XA_prepare_log_event::do_commit()
res= trans_xa_prepare(thd);
}
else
- {
res= trans_xa_commit(thd);
- thd->mdl_context.release_transactional_locks();
- }
return res;
}
diff --git a/sql/mdl.cc b/sql/mdl.cc
index 4772dc017f9..5f5e5c3ce9a 100644
--- a/sql/mdl.cc
+++ b/sql/mdl.cc
@@ -3039,15 +3039,17 @@ void MDL_context::rollback_to_savepoint(const MDL_savepoint &mdl_savepoint)
implementation of COMMIT (implicit or explicit) and ROLLBACK.
*/
-void MDL_context::release_transactional_locks()
+void MDL_context::release_transactional_locks(THD *thd)
{
DBUG_ENTER("MDL_context::release_transactional_locks");
+ /* Fail if there are active transactions */
+ DBUG_ASSERT(!(thd->server_status &
+ (SERVER_STATUS_IN_TRANS | SERVER_STATUS_IN_TRANS_READONLY)));
release_locks_stored_before(MDL_STATEMENT, NULL);
release_locks_stored_before(MDL_TRANSACTION, NULL);
DBUG_VOID_RETURN;
}
-
void MDL_context::release_statement_locks()
{
DBUG_ENTER("MDL_context::release_transactional_locks");
diff --git a/sql/mdl.h b/sql/mdl.h
index f6b7154fba0..9dbf9aa7f4f 100644
--- a/sql/mdl.h
+++ b/sql/mdl.h
@@ -921,7 +921,7 @@ public:
void set_lock_duration(MDL_ticket *mdl_ticket, enum_mdl_duration duration);
void release_statement_locks();
- void release_transactional_locks();
+ void release_transactional_locks(THD *thd);
void release_explicit_locks();
void rollback_to_savepoint(const MDL_savepoint &mdl_savepoint);
diff --git a/sql/opt_subselect.cc b/sql/opt_subselect.cc
index 41dcf6442a1..0a87d9ccd2f 100644
--- a/sql/opt_subselect.cc
+++ b/sql/opt_subselect.cc
@@ -872,6 +872,7 @@ bool subquery_types_allow_materialization(THD* thd, Item_in_subselect *in_subs)
bool all_are_fields= TRUE;
uint32 total_key_length = 0;
+ bool converted_from_in_predicate= in_subs->converted_from_in_predicate;
for (uint i= 0; i < elements; i++)
{
Item *outer= left_exp->element_index(i);
@@ -879,8 +880,11 @@ bool subquery_types_allow_materialization(THD* thd, Item_in_subselect *in_subs)
all_are_fields &= (outer->real_item()->type() == Item::FIELD_ITEM &&
inner->real_item()->type() == Item::FIELD_ITEM);
total_key_length += inner->max_length;
- if (!inner->type_handler()->subquery_type_allows_materialization(inner,
- outer))
+ if (!inner->
+ type_handler()->
+ subquery_type_allows_materialization(inner,
+ outer,
+ converted_from_in_predicate))
{
trace_transform.add("possible", false);
trace_transform.add("cause", "types mismatch");
diff --git a/sql/rpl_gtid.cc b/sql/rpl_gtid.cc
index af0c4747d2b..bc06188af34 100644
--- a/sql/rpl_gtid.cc
+++ b/sql/rpl_gtid.cc
@@ -445,7 +445,7 @@ rpl_slave_state::truncate_state_table(THD *thd)
close_thread_tables(thd);
ha_commit_trans(thd, TRUE);
}
- thd->mdl_context.release_transactional_locks();
+ thd->release_transactional_locks();
}
return err;
}
@@ -736,7 +736,7 @@ end:
if (in_transaction)
thd->mdl_context.release_statement_locks();
else
- thd->mdl_context.release_transactional_locks();
+ thd->release_transactional_locks();
}
thd->lex->restore_backup_query_tables_list(&lex_backup);
thd->variables.option_bits= thd_saved_option;
@@ -991,7 +991,7 @@ end:
ha_rollback_trans(thd, FALSE);
}
close_thread_tables(thd);
- thd->mdl_context.release_transactional_locks();
+ thd->release_transactional_locks();
thd->lex->restore_backup_query_tables_list(&lex_backup);
if (err)
diff --git a/sql/rpl_injector.cc b/sql/rpl_injector.cc
index 0726697211b..2319f69d67c 100644
--- a/sql/rpl_injector.cc
+++ b/sql/rpl_injector.cc
@@ -69,34 +69,34 @@ injector::transaction::~transaction()
*/
int injector::transaction::commit()
{
- DBUG_ENTER("injector::transaction::commit()");
- int error= m_thd->binlog_flush_pending_rows_event(true);
- /*
- Cluster replication does not preserve statement or
- transaction boundaries of the master. Instead, a new
- transaction on replication slave is started when a new GCI
- (global checkpoint identifier) is issued, and is committed
- when the last event of the check point has been received and
- processed. This ensures consistency of each cluster in
- cluster replication, and there is no requirement for stronger
- consistency: MySQL replication is asynchronous with other
- engines as well.
-
- A practical consequence of that is that row level replication
- stream passed through the injector thread never contains
- COMMIT events.
- Here we should preserve the server invariant that there is no
- outstanding statement transaction when the normal transaction
- is committed by committing the statement transaction
- explicitly.
- */
- trans_commit_stmt(m_thd);
- if (!trans_commit(m_thd))
- {
- close_thread_tables(m_thd);
- m_thd->mdl_context.release_transactional_locks();
- }
- DBUG_RETURN(error);
+ DBUG_ENTER("injector::transaction::commit()");
+ int error= m_thd->binlog_flush_pending_rows_event(true);
+ /*
+ Cluster replication does not preserve statement or
+ transaction boundaries of the master. Instead, a new
+ transaction on replication slave is started when a new GCI
+ (global checkpoint identifier) is issued, and is committed
+ when the last event of the check point has been received and
+ processed. This ensures consistency of each cluster in
+ cluster replication, and there is no requirement for stronger
+ consistency: MySQL replication is asynchronous with other
+ engines as well.
+
+ A practical consequence of that is that row level replication
+ stream passed through the injector thread never contains
+ COMMIT events.
+ Here we should preserve the server invariant that there is no
+ outstanding statement transaction when the normal transaction
+ is committed by committing the statement transaction
+ explicitly.
+ */
+ trans_commit_stmt(m_thd);
+ if (!trans_commit(m_thd))
+ {
+ close_thread_tables(m_thd);
+ m_thd->release_transactional_locks();
+ }
+ DBUG_RETURN(error);
}
diff --git a/sql/rpl_rli.cc b/sql/rpl_rli.cc
index b7127d86fcd..8b61a3a708b 100644
--- a/sql/rpl_rli.cc
+++ b/sql/rpl_rli.cc
@@ -1677,7 +1677,7 @@ end:
{
*out_hton= table->s->db_type();
close_thread_tables(thd);
- thd->mdl_context.release_transactional_locks();
+ thd->release_transactional_locks();
}
return err;
}
@@ -1704,7 +1704,7 @@ scan_all_gtid_slave_pos_table(THD *thd, int (*cb)(THD *, LEX_CSTRING *, void *),
{
my_error(ER_FILE_NOT_FOUND, MYF(0), path, my_errno);
close_thread_tables(thd);
- thd->mdl_context.release_transactional_locks();
+ thd->release_transactional_locks();
return 1;
}
else
@@ -1717,7 +1717,7 @@ scan_all_gtid_slave_pos_table(THD *thd, int (*cb)(THD *, LEX_CSTRING *, void *),
err= ha_discover_table_names(thd, &MYSQL_SCHEMA_NAME, dirp, &tl, false);
my_dirend(dirp);
close_thread_tables(thd);
- thd->mdl_context.release_transactional_locks();
+ thd->release_transactional_locks();
if (err)
return err;
@@ -2003,7 +2003,7 @@ end:
ha_commit_trans(thd, FALSE);
ha_commit_trans(thd, TRUE);
close_thread_tables(thd);
- thd->mdl_context.release_transactional_locks();
+ thd->release_transactional_locks();
}
return err;
@@ -2292,7 +2292,7 @@ void rpl_group_info::cleanup_context(THD *thd, bool error)
if (thd->transaction->xid_state.is_explicit_XA())
xa_trans_force_rollback(thd);
- thd->mdl_context.release_transactional_locks();
+ thd->release_transactional_locks();
if (thd == rli->sql_driver_thd)
{
@@ -2406,10 +2406,10 @@ void rpl_group_info::slave_close_thread_tables(THD *thd)
if (thd->transaction_rollback_request)
{
trans_rollback_implicit(thd);
- thd->mdl_context.release_transactional_locks();
+ thd->release_transactional_locks();
}
else if (! thd->in_multi_stmt_transaction_mode())
- thd->mdl_context.release_transactional_locks();
+ thd->release_transactional_locks();
else
thd->mdl_context.release_statement_locks();
diff --git a/sql/sp.cc b/sql/sp.cc
index 3737bd11740..abd89dde499 100644
--- a/sql/sp.cc
+++ b/sql/sp.cc
@@ -1205,8 +1205,6 @@ Sp_handler::sp_create_routine(THD *thd, const sp_head *sp) const
CHARSET_INFO *db_cs= get_default_db_collation(thd, sp->m_db.str);
- enum_check_fields saved_count_cuted_fields;
-
bool store_failed= FALSE;
DBUG_ENTER("sp_create_routine");
DBUG_PRINT("enter", ("type: %s name: %.*s",
@@ -1240,8 +1238,7 @@ Sp_handler::sp_create_routine(THD *thd, const sp_head *sp) const
/* Reset sql_mode during data dictionary operations. */
thd->variables.sql_mode= 0;
- saved_count_cuted_fields= thd->count_cuted_fields;
- thd->count_cuted_fields= CHECK_FIELD_WARN;
+ Check_level_instant_set check_level_save(thd, CHECK_FIELD_WARN);
if (!(table= open_proc_table_for_update(thd)))
{
@@ -1500,7 +1497,6 @@ log:
ret= FALSE;
done:
- thd->count_cuted_fields= saved_count_cuted_fields;
thd->variables.sql_mode= saved_mode;
DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
DBUG_RETURN(ret);
diff --git a/sql/sp_head.cc b/sql/sp_head.cc
index 9e5b5bee0f2..513e7207b7e 100644
--- a/sql/sp_head.cc
+++ b/sql/sp_head.cc
@@ -2390,10 +2390,10 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
if (thd->transaction_rollback_request)
{
trans_rollback_implicit(thd);
- thd->mdl_context.release_transactional_locks();
+ thd->release_transactional_locks();
}
else if (! thd->in_multi_stmt_transaction_mode())
- thd->mdl_context.release_transactional_locks();
+ thd->release_transactional_locks();
else
thd->mdl_context.release_statement_locks();
}
@@ -3518,10 +3518,10 @@ sp_lex_keeper::reset_lex_and_exec_core(THD *thd, uint *nextp,
if (thd->transaction_rollback_request)
{
trans_rollback_implicit(thd);
- thd->mdl_context.release_transactional_locks();
+ thd->release_transactional_locks();
}
else if (! thd->in_multi_stmt_transaction_mode())
- thd->mdl_context.release_transactional_locks();
+ thd->release_transactional_locks();
else
thd->mdl_context.release_statement_locks();
}
diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc
index bad39cfc1d1..25e8aa42c7c 100644
--- a/sql/sql_acl.cc
+++ b/sql/sql_acl.cc
@@ -9463,6 +9463,8 @@ static bool show_global_privileges(THD *thd, ACL_USER_BASE *acl_entry,
add_user_parameters(thd, &global, (ACL_USER *)acl_entry,
(want_access & GRANT_ACL));
+ else if (want_access & GRANT_ACL)
+ global.append(STRING_WITH_LEN(" WITH GRANT OPTION"));
protocol->prepare_for_resend();
protocol->store(global.ptr(),global.length(),global.charset());
if (protocol->write())
diff --git a/sql/sql_admin.cc b/sql/sql_admin.cc
index 2d6f891f56f..7c1f8596ece 100644
--- a/sql/sql_admin.cc
+++ b/sql/sql_admin.cc
@@ -42,7 +42,7 @@ static bool admin_recreate_table(THD *thd, TABLE_LIST *table_list)
trans_rollback_stmt(thd);
trans_rollback(thd);
close_thread_tables(thd);
- thd->mdl_context.release_transactional_locks();
+ thd->release_transactional_locks();
/*
table_list->table has been closed and freed. Do not reference
@@ -115,7 +115,7 @@ static int prepare_for_repair(THD *thd, TABLE_LIST *table_list,
acquire the exclusive lock to satisfy MDL asserts and avoid
deadlocks.
*/
- thd->mdl_context.release_transactional_locks();
+ thd->release_transactional_locks();
/*
Attempt to do full-blown table open in mysql_admin_table() has failed.
Let us try to open at least a .FRM for this table.
@@ -278,7 +278,7 @@ end:
}
/* In case of a temporary table there will be no metadata lock. */
if (unlikely(error) && has_mdl_lock)
- thd->mdl_context.release_transactional_locks();
+ thd->release_transactional_locks();
DBUG_RETURN(error);
}
@@ -607,7 +607,7 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
trans_rollback(thd);
close_thread_tables(thd);
table->table= NULL;
- thd->mdl_context.release_transactional_locks();
+ thd->release_transactional_locks();
MDL_REQUEST_INIT(&table->mdl_request, MDL_key::TABLE, table->db.str,
table->table_name.str, MDL_SHARED_NO_READ_WRITE,
MDL_TRANSACTION);
@@ -668,7 +668,7 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
trans_rollback_stmt(thd);
trans_rollback(thd);
close_thread_tables(thd);
- thd->mdl_context.release_transactional_locks();
+ thd->release_transactional_locks();
DBUG_PRINT("admin", ("simple error, admin next table"));
continue;
case -1: // error, message could be written to net
@@ -735,7 +735,7 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
trans_commit_stmt(thd);
trans_commit(thd);
close_thread_tables(thd);
- thd->mdl_context.release_transactional_locks();
+ thd->release_transactional_locks();
lex->reset_query_tables_list(FALSE);
/*
Restore Query_tables_list::sql_command value to make statement
@@ -868,7 +868,7 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
thd->open_options|= extra_open_options;
close_thread_tables(thd);
table->table= NULL;
- thd->mdl_context.release_transactional_locks();
+ thd->release_transactional_locks();
MDL_REQUEST_INIT(&table->mdl_request, MDL_key::TABLE, table->db.str,
table->table_name.str, MDL_SHARED_NO_READ_WRITE,
MDL_TRANSACTION);
@@ -1099,7 +1099,7 @@ send_result_message:
trans_commit_stmt(thd);
trans_commit(thd);
close_thread_tables(thd);
- thd->mdl_context.release_transactional_locks();
+ thd->release_transactional_locks();
/* Clear references to TABLE and MDL_ticket after releasing them. */
table->mdl_request.ticket= NULL;
@@ -1258,7 +1258,7 @@ send_result_message:
goto err;
}
close_thread_tables(thd);
- thd->mdl_context.release_transactional_locks();
+ thd->release_transactional_locks();
/*
If it is CHECK TABLE v1, v2, v3, and v1, v2, v3 are views, we will run
@@ -1296,7 +1296,7 @@ err:
table->table= 0;
}
close_thread_tables(thd); // Shouldn't be needed
- thd->mdl_context.release_transactional_locks();
+ thd->release_transactional_locks();
thd->resume_subsequent_commits(suspended_wfc);
DBUG_RETURN(TRUE);
}
diff --git a/sql/sql_base.cc b/sql/sql_base.cc
index dec5bd91f36..d02d130c084 100644
--- a/sql/sql_base.cc
+++ b/sql/sql_base.cc
@@ -9040,9 +9040,12 @@ void
close_mysql_tables(THD *thd)
{
if (! thd->in_sub_stmt)
+ {
trans_commit_stmt(thd);
+ trans_commit(thd);
+ }
close_thread_tables(thd);
- thd->mdl_context.release_transactional_locks();
+ thd->release_transactional_locks();
}
/*
diff --git a/sql/sql_class.cc b/sql/sql_class.cc
index a4fd26cf7d8..a0c23a08b12 100644
--- a/sql/sql_class.cc
+++ b/sql/sql_class.cc
@@ -1565,7 +1565,7 @@ void THD::cleanup(void)
and left the mode a few lines above), there will be outstanding
metadata locks. Release them.
*/
- mdl_context.release_transactional_locks();
+ mdl_context.release_transactional_locks(this);
backup_end(this);
backup_unlock(this);
@@ -4919,7 +4919,7 @@ void destroy_background_thd(MYSQL_THD thd)
void reset_thd(MYSQL_THD thd)
{
close_thread_tables(thd);
- thd->mdl_context.release_transactional_locks();
+ thd->release_transactional_locks();
thd->free_items();
free_root(thd->mem_root, MYF(MY_KEEP_PREALLOC));
}
diff --git a/sql/sql_class.h b/sql/sql_class.h
index e3e3b18472b..1da82edc061 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -4730,6 +4730,13 @@ public:
locked_tables_mode= mode_arg;
}
void leave_locked_tables_mode();
+ /* Relesae transactional locks if there are no active transactions */
+ void release_transactional_locks()
+ {
+ if (!(server_status &
+ (SERVER_STATUS_IN_TRANS | SERVER_STATUS_IN_TRANS_READONLY)))
+ mdl_context.release_transactional_locks(this);
+ }
int decide_logging_format(TABLE_LIST *tables);
/*
In Some cases when decide_logging_format is called it does not have all
diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc
index 7586b2831ae..01f67476688 100644
--- a/sql/sql_delete.cc
+++ b/sql/sql_delete.cc
@@ -248,7 +248,7 @@ int update_portion_of_time(THD *thd, TABLE *table,
uint dst_fieldno= lcond ? table->s->period.end_fieldno
: table->s->period.start_fieldno;
- table->file->store_auto_increment();
+ ulonglong prev_insert_id= table->file->next_insert_id;
store_record(table, record[1]);
if (likely(!res))
res= src->save_in_field(table->field[dst_fieldno], true);
@@ -264,7 +264,7 @@ int update_portion_of_time(THD *thd, TABLE *table,
TRG_ACTION_AFTER, true);
restore_record(table, record[1]);
if (res)
- table->file->restore_auto_increment();
+ table->file->restore_auto_increment(prev_insert_id);
if (likely(!res) && lcond && rcond)
res= table->period_make_insert(period_conds.end.item,
diff --git a/sql/sql_derived.cc b/sql/sql_derived.cc
index 428e7b1d261..a9155f361b5 100644
--- a/sql/sql_derived.cc
+++ b/sql/sql_derived.cc
@@ -1401,7 +1401,8 @@ bool pushdown_cond_for_derived(THD *thd, Item *cond, TABLE_LIST *derived)
DBUG_RETURN(false);
st_select_lex_unit *unit= derived->get_unit();
- st_select_lex *sl= unit->first_select();
+ st_select_lex *first_sl= unit->first_select();
+ st_select_lex *sl= first_sl;
if (derived->prohibit_cond_pushdown)
DBUG_RETURN(false);
@@ -1460,6 +1461,20 @@ bool pushdown_cond_for_derived(THD *thd, Item *cond, TABLE_LIST *derived)
if (!extracted_cond_copy)
continue;
+ /*
+ Rename the columns of all non-first selects of a union to be compatible
+ by names with the columns of the first select. It will allow to use copies
+ of the same expression pushed into having clauses of different selects.
+ */
+ if (sl != first_sl)
+ {
+ DBUG_ASSERT(sl->item_list.elements == first_sl->item_list.elements);
+ List_iterator_fast<Item> it(sl->item_list);
+ List_iterator_fast<Item> nm_it(unit->types);
+ while (Item *item= it++)
+ item->share_name_with(nm_it++);
+ }
+
/* Collect fields that are used in the GROUP BY of sl */
if (sl->have_window_funcs())
{
@@ -1480,7 +1495,7 @@ bool pushdown_cond_for_derived(THD *thd, Item *cond, TABLE_LIST *derived)
&remaining_cond,
&Item::derived_field_transformer_for_where,
(uchar *) sl);
-
+
if (!remaining_cond)
continue;
/*
diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc
index 7833059438e..1e456a724e7 100644
--- a/sql/sql_insert.cc
+++ b/sql/sql_insert.cc
@@ -1769,7 +1769,7 @@ int write_record(THD *thd, TABLE *table, COPY_INFO *info, select_result *sink)
int error, trg_error= 0;
char *key=0;
MY_BITMAP *save_read_set, *save_write_set;
- table->file->store_auto_increment();
+ ulonglong prev_insert_id= table->file->next_insert_id;
ulonglong insert_id_for_cur_row= 0;
ulonglong prev_insert_id_for_cur_row= 0;
DBUG_ENTER("write_record");
@@ -1918,7 +1918,7 @@ int write_record(THD *thd, TABLE *table, COPY_INFO *info, select_result *sink)
if (res == VIEW_CHECK_ERROR)
goto before_trg_err;
- table->file->restore_auto_increment();
+ table->file->restore_auto_increment(prev_insert_id);
info->touched++;
if (different_records)
{
@@ -2110,7 +2110,7 @@ int write_record(THD *thd, TABLE *table, COPY_INFO *info, select_result *sink)
if (!(thd->variables.old_behavior &
OLD_MODE_NO_DUP_KEY_WARNINGS_WITH_IGNORE))
table->file->print_error(error, MYF(ME_WARNING));
- table->file->restore_auto_increment();
+ table->file->restore_auto_increment(prev_insert_id);
goto after_trg_or_ignored_err;
}
@@ -2143,7 +2143,7 @@ err:
table->file->print_error(error,MYF(0));
before_trg_err:
- table->file->restore_auto_increment();
+ table->file->restore_auto_increment(prev_insert_id);
if (key)
my_safe_afree(key, table->s->max_unique_length);
table->column_bitmaps_set(save_read_set, save_write_set);
@@ -2297,7 +2297,7 @@ public:
if (table)
{
close_thread_tables(&thd);
- thd.mdl_context.release_transactional_locks();
+ thd.mdl_context.release_transactional_locks(&thd);
}
mysql_mutex_destroy(&mutex);
mysql_cond_destroy(&cond);
@@ -3130,7 +3130,7 @@ pthread_handler_t handle_delayed_insert(void *arg)
if (thd->mdl_context.clone_ticket(&di->grl_protection) ||
thd->mdl_context.clone_ticket(&di->table_list.mdl_request))
{
- thd->mdl_context.release_transactional_locks();
+ thd->release_transactional_locks();
di->handler_thread_initialized= TRUE;
goto err;
}
@@ -3356,7 +3356,7 @@ pthread_handler_t handle_delayed_insert(void *arg)
thd->set_killed(KILL_CONNECTION_HARD); // If error
close_thread_tables(thd); // Free the table
- thd->mdl_context.release_transactional_locks();
+ thd->release_transactional_locks();
mysql_cond_broadcast(&di->cond_client); // Safety
mysql_mutex_lock(&LOCK_delayed_create); // Because of delayed_get_table
@@ -4971,6 +4971,7 @@ bool select_create::send_eof()
WSREP_ERROR("Appending table key for CTAS failed: %s, %d",
(wsrep_thd_query(thd)) ?
wsrep_thd_query(thd) : "void", rcode);
+ abort_result_set();
DBUG_RETURN(true);
}
/* If commit fails, we should be able to reset the OK status. */
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index a364097931e..f09d7140d3b 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -2088,7 +2088,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
locks.
*/
trans_rollback_implicit(thd);
- thd->mdl_context.release_transactional_locks();
+ thd->release_transactional_locks();
}
thd->cleanup_after_query();
@@ -2153,7 +2153,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
ulonglong options= (ulonglong) (uchar) packet[0];
if (trans_commit_implicit(thd))
break;
- thd->mdl_context.release_transactional_locks();
+ thd->release_transactional_locks();
if (check_global_access(thd,RELOAD_ACL))
break;
general_log_print(thd, command, NullS);
@@ -2188,7 +2188,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
if (trans_commit_implicit(thd))
break;
close_thread_tables(thd);
- thd->mdl_context.release_transactional_locks();
+ thd->release_transactional_locks();
my_ok(thd);
break;
}
@@ -3031,7 +3031,7 @@ err:
/* Close tables and release metadata locks. */
close_thread_tables(thd);
DBUG_ASSERT(!thd->locked_tables_mode);
- thd->mdl_context.release_transactional_locks();
+ thd->release_transactional_locks();
return TRUE;
}
@@ -3776,7 +3776,7 @@ mysql_execute_command(THD *thd)
/* Commit the normal transaction if one is active. */
bool commit_failed= trans_commit_implicit(thd);
/* Release metadata locks acquired in this transaction. */
- thd->mdl_context.release_transactional_locks();
+ thd->release_transactional_locks();
if (commit_failed)
{
WSREP_DEBUG("implicit commit failed, MDL released: %lld",
@@ -5087,7 +5087,7 @@ mysql_execute_command(THD *thd)
res= trans_commit_implicit(thd);
if (thd->locked_tables_list.unlock_locked_tables(thd))
res= 1;
- thd->mdl_context.release_transactional_locks();
+ thd->release_transactional_locks();
thd->variables.option_bits&= ~(OPTION_TABLE_LOCK);
thd->reset_binlog_for_next_statement();
}
@@ -5104,7 +5104,7 @@ mysql_execute_command(THD *thd)
if (thd->locked_tables_list.unlock_locked_tables(thd))
res= 1;
/* Release transactional metadata locks. */
- thd->mdl_context.release_transactional_locks();
+ thd->release_transactional_locks();
if (res)
goto error;
@@ -5620,7 +5620,7 @@ mysql_execute_command(THD *thd)
DBUG_PRINT("info", ("Executing SQLCOM_BEGIN thd: %p", thd));
if (trans_begin(thd, lex->start_transaction_opt))
{
- thd->mdl_context.release_transactional_locks();
+ thd->release_transactional_locks();
WSREP_DEBUG("BEGIN failed, MDL released: %lld",
(longlong) thd->thread_id);
WSREP_DEBUG("stmt_da, sql_errno: %d", (thd->get_stmt_da()->is_error()) ? thd->get_stmt_da()->sql_errno() : 0);
@@ -5639,7 +5639,7 @@ mysql_execute_command(THD *thd)
(thd->variables.completion_type == 2 &&
lex->tx_release != TVL_NO));
bool commit_failed= trans_commit(thd);
- thd->mdl_context.release_transactional_locks();
+ thd->release_transactional_locks();
if (commit_failed)
{
WSREP_DEBUG("COMMIT failed, MDL released: %lld",
@@ -5677,7 +5677,7 @@ mysql_execute_command(THD *thd)
(thd->variables.completion_type == 2 &&
lex->tx_release != TVL_NO));
bool rollback_failed= trans_rollback(thd);
- thd->mdl_context.release_transactional_locks();
+ thd->release_transactional_locks();
if (rollback_failed)
{
@@ -5864,7 +5864,6 @@ mysql_execute_command(THD *thd)
case SQLCOM_XA_COMMIT:
{
bool commit_failed= trans_xa_commit(thd);
- thd->mdl_context.release_transactional_locks();
if (commit_failed)
{
WSREP_DEBUG("XA commit failed, MDL released: %lld",
@@ -5882,7 +5881,6 @@ mysql_execute_command(THD *thd)
case SQLCOM_XA_ROLLBACK:
{
bool rollback_failed= trans_xa_rollback(thd);
- thd->mdl_context.release_transactional_locks();
if (rollback_failed)
{
WSREP_DEBUG("XA rollback failed, MDL released: %lld",
@@ -6093,7 +6091,7 @@ finish:
*/
THD_STAGE_INFO(thd, stage_rollback_implicit);
trans_rollback_implicit(thd);
- thd->mdl_context.release_transactional_locks();
+ thd->release_transactional_locks();
}
else if (stmt_causes_implicit_commit(thd, CF_IMPLICIT_COMMIT_END))
{
@@ -6107,7 +6105,7 @@ finish:
/* Commit the normal transaction if one is active. */
trans_commit_implicit(thd);
thd->get_stmt_da()->set_overwrite_status(false);
- thd->mdl_context.release_transactional_locks();
+ thd->release_transactional_locks();
}
}
else if (! thd->in_sub_stmt && ! thd->in_multi_stmt_transaction_mode())
@@ -6122,7 +6120,7 @@ finish:
- If in autocommit mode, or outside a transactional context,
automatically release metadata locks of the current statement.
*/
- thd->mdl_context.release_transactional_locks();
+ thd->release_transactional_locks();
}
else if (! thd->in_sub_stmt)
{
@@ -6147,7 +6145,7 @@ finish:
{
WSREP_DEBUG("Forcing release of transactional locks for thd: %lld",
(longlong) thd->thread_id);
- thd->mdl_context.release_transactional_locks();
+ thd->release_transactional_locks();
}
/*
diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc
index 8f609ac0b7d..7dd025f03e4 100644
--- a/sql/sql_prepare.cc
+++ b/sql/sql_prepare.cc
@@ -4289,7 +4289,7 @@ bool Prepared_statement::prepare(const char *packet, uint packet_len)
if (thd->transaction_rollback_request)
{
trans_rollback_implicit(thd);
- thd->mdl_context.release_transactional_locks();
+ thd->release_transactional_locks();
}
/* Preserve CHANGE MASTER attributes */
diff --git a/sql/sql_select.cc b/sql/sql_select.cc
index a2cc265ec5b..808d49d4067 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -24256,8 +24256,7 @@ cmp_buffer_with_ref(THD *thd, TABLE *table, TABLE_REF *tab_ref)
bool
cp_buffer_from_ref(THD *thd, TABLE *table, TABLE_REF *ref)
{
- enum enum_check_fields save_count_cuted_fields= thd->count_cuted_fields;
- thd->count_cuted_fields= CHECK_FIELD_IGNORE;
+ Check_level_instant_set check_level_save(thd, CHECK_FIELD_IGNORE);
my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->write_set);
bool result= 0;
@@ -24269,7 +24268,6 @@ cp_buffer_from_ref(THD *thd, TABLE *table, TABLE_REF *ref)
break;
}
}
- thd->count_cuted_fields= save_count_cuted_fields;
dbug_tmp_restore_column_map(table->write_set, old_map);
return result;
}
diff --git a/sql/sql_select.h b/sql/sql_select.h
index bb9ecf88df0..fad008ebcb9 100644
--- a/sql/sql_select.h
+++ b/sql/sql_select.h
@@ -1902,18 +1902,11 @@ public:
{
enum store_key_result result;
THD *thd= to_field->table->in_use;
- enum_check_fields saved_count_cuted_fields= thd->count_cuted_fields;
- sql_mode_t orig_sql_mode= thd->variables.sql_mode;
+ Check_level_instant_set check_level_save(thd, CHECK_FIELD_IGNORE);
+ Sql_mode_save sql_mode(thd);
thd->variables.sql_mode&= ~(MODE_NO_ZERO_IN_DATE | MODE_NO_ZERO_DATE);
thd->variables.sql_mode|= MODE_INVALID_DATES;
-
- thd->count_cuted_fields= CHECK_FIELD_IGNORE;
-
result= copy_inner();
-
- thd->count_cuted_fields= saved_count_cuted_fields;
- thd->variables.sql_mode= orig_sql_mode;
-
return result;
}
diff --git a/sql/sql_show.cc b/sql/sql_show.cc
index 0f6200ff237..ca3e2aaa97e 100644
--- a/sql/sql_show.cc
+++ b/sql/sql_show.cc
@@ -3671,7 +3671,6 @@ static bool show_status_array(THD *thd, const char *wild,
char name_buffer[NAME_CHAR_LEN];
int len;
SHOW_VAR tmp, *var;
- enum_check_fields save_count_cuted_fields= thd->count_cuted_fields;
bool res= FALSE;
CHARSET_INFO *charset= system_charset_info;
DBUG_ENTER("show_status_array");
@@ -3794,7 +3793,6 @@ static bool show_status_array(THD *thd, const char *wild,
}
}
end:
- thd->count_cuted_fields= save_count_cuted_fields;
DBUG_RETURN(res);
}
@@ -4521,8 +4519,7 @@ fill_schema_table_by_open(THD *thd, MEM_ROOT *mem_root,
Open_tables_backup *open_tables_state_backup,
bool can_deadlock)
{
- Query_arena i_s_arena(mem_root,
- Query_arena::STMT_CONVENTIONAL_EXECUTION),
+ Query_arena i_s_arena(mem_root, Query_arena::STMT_CONVENTIONAL_EXECUTION),
backup_arena, *old_arena;
LEX *old_lex= thd->lex, temp_lex, *lex;
LEX_CSTRING db_name, table_name;
@@ -5037,12 +5034,9 @@ end:
class Warnings_only_error_handler : public Internal_error_handler
{
public:
- bool handle_condition(THD *thd,
- uint sql_errno,
- const char* sqlstate,
+ bool handle_condition(THD *thd, uint sql_errno, const char* sqlstate,
Sql_condition::enum_warning_level *level,
- const char* msg,
- Sql_condition ** cond_hdl)
+ const char* msg, Sql_condition ** cond_hdl)
{
if (sql_errno == ER_TRG_NO_DEFINER || sql_errno == ER_TRG_NO_CREATION_CTX)
return true;
@@ -8493,13 +8487,6 @@ static int optimize_schema_tables_memory_usage(TABLE_LIST *table_list)
DBUG_ASSERT(table->s->keys == 0);
DBUG_ASSERT(table->s->uniques == 0);
- // XXX HACK HACK HACK: in a stored function, RETURN (SELECT ...)
- // enables warnings (in THD::sp_eval_expr) for the whole val_xxx/store pair,
- // while the intention is to warn only for store(). Until this is
- // fixed let's avoid data truncation warnings in I_S->fill_table()
- if (thd->count_cuted_fields == CHECK_FIELD_IGNORE)
- {
-
uchar *cur= table->field[0]->ptr;
/* first recinfo could be a NULL bitmap, not an actual Field */
from_recinfo= to_recinfo= p->start_recinfo + (cur != table->record[0]);
@@ -8533,7 +8520,6 @@ static int optimize_schema_tables_memory_usage(TABLE_LIST *table_list)
to_recinfo++;
}
p->recinfo= to_recinfo;
- } // XXX end of HACK HACK HACK
// TODO switch from Aria to Memory if all blobs were optimized away?
if (instantiate_tmp_table(table, p->keyinfo, p->start_recinfo, &p->recinfo,
@@ -8696,6 +8682,7 @@ bool get_schema_tables_result(JOIN *join,
}
Switch_to_definer_security_ctx backup_ctx(thd, table_list);
+ Check_level_instant_set check_level_save(thd, CHECK_FIELD_IGNORE);
if (table_list->schema_table->fill_table(thd, table_list, cond))
{
result= 1;
diff --git a/sql/sql_statistics.cc b/sql/sql_statistics.cc
index 2636299e330..7b600bd45c4 100644
--- a/sql/sql_statistics.cc
+++ b/sql/sql_statistics.cc
@@ -2899,7 +2899,6 @@ int read_statistics_for_table(THD *thd, TABLE *table, TABLE_LIST *stat_tables)
Field **field_ptr;
KEY *key_info, *key_info_end;
TABLE_SHARE *table_share= table->s;
- enum_check_fields old_check_level= thd->count_cuted_fields;
DBUG_ENTER("read_statistics_for_table");
DEBUG_SYNC(thd, "statistics_mem_alloc_start1");
@@ -2915,7 +2914,7 @@ int read_statistics_for_table(THD *thd, TABLE *table, TABLE_LIST *stat_tables)
}
/* Don't write warnings for internal field conversions */
- thd->count_cuted_fields= CHECK_FIELD_IGNORE;
+ Check_level_instant_set check_level_save(thd, CHECK_FIELD_IGNORE);
/* Read statistics from the statistical table table_stats */
Table_statistics *read_stats= table_share->stats_cb.table_stats;
@@ -2997,7 +2996,6 @@ int read_statistics_for_table(THD *thd, TABLE *table, TABLE_LIST *stat_tables)
}
}
- thd->count_cuted_fields= old_check_level;
table_share->stats_cb.end_stats_load();
DBUG_RETURN(0);
}
diff --git a/sql/sql_table.cc b/sql/sql_table.cc
index c2ba9bcadfb..b8c51f05f77 100644
--- a/sql/sql_table.cc
+++ b/sql/sql_table.cc
@@ -10636,16 +10636,14 @@ do_continue:;
if (use_inplace)
{
table->s->frm_image= &frm;
- enum_check_fields save_count_cuted_fields= thd->count_cuted_fields;
/*
Set the truncated column values of thd as warning
for alter table.
*/
- thd->count_cuted_fields = CHECK_FIELD_WARN;
+ Check_level_instant_set check_level_save(thd, CHECK_FIELD_WARN);
int res= mysql_inplace_alter_table(thd, table_list, table, &altered_table,
&ha_alter_info,
&target_mdl_request, &alter_ctx);
- thd->count_cuted_fields= save_count_cuted_fields;
my_free(const_cast<uchar*>(frm.str));
if (res)
diff --git a/sql/sql_tvc.cc b/sql/sql_tvc.cc
index df774a5d8dd..5f732d474f8 100644
--- a/sql/sql_tvc.cc
+++ b/sql/sql_tvc.cc
@@ -1,4 +1,4 @@
-/* Copyright (c) 2017, MariaDB
+/* Copyright (c) 2017, 2020, MariaDB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -861,7 +861,8 @@ static bool cmp_row_types(Item* item1, Item* item2)
Item *inner= item1->element_index(i);
Item *outer= item2->element_index(i);
if (!inner->type_handler()->subquery_type_allows_materialization(inner,
- outer))
+ outer,
+ true))
return true;
}
return false;
@@ -941,8 +942,8 @@ Item *Item_func_in::in_predicate_to_in_subs_transformer(THD *thd,
trace_conv.add("reason", "non-constant element in the IN-list");
return this;
}
-
- if (cmp_row_types(args[0], args[i]))
+
+ if (cmp_row_types(args[i], args[0]))
{
trace_conv.add("done", false);
trace_conv.add("reason", "type mismatch");
@@ -1027,6 +1028,7 @@ Item *Item_func_in::in_predicate_to_in_subs_transformer(THD *thd,
if (!(in_subs=
new (thd->mem_root) Item_in_subselect(thd, args[0], sq_select)))
goto err;
+ in_subs->converted_from_in_predicate= TRUE;
sq= in_subs;
if (negated)
sq= negate_expression(thd, in_subs);
diff --git a/sql/sql_type.cc b/sql/sql_type.cc
index 00b9c71cc37..6c5f72c2ce2 100644
--- a/sql/sql_type.cc
+++ b/sql/sql_type.cc
@@ -20,6 +20,7 @@
#include "sql_const.h"
#include "sql_class.h"
#include "sql_time.h"
+#include "sql_string.h"
#include "item.h"
#include "log.h"
#include "tztime.h"
@@ -7077,7 +7078,8 @@ uint Type_handler_timestamp_common::Item_decimal_precision(const Item *item) con
bool Type_handler_real_result::
subquery_type_allows_materialization(const Item *inner,
- const Item *outer) const
+ const Item *outer,
+ bool is_in_predicate) const
{
DBUG_ASSERT(inner->cmp_type() == REAL_RESULT);
return outer->cmp_type() == REAL_RESULT;
@@ -7086,7 +7088,8 @@ bool Type_handler_real_result::
bool Type_handler_int_result::
subquery_type_allows_materialization(const Item *inner,
- const Item *outer) const
+ const Item *outer,
+ bool is_in_predicate) const
{
DBUG_ASSERT(inner->cmp_type() == INT_RESULT);
return outer->cmp_type() == INT_RESULT;
@@ -7095,7 +7098,8 @@ bool Type_handler_int_result::
bool Type_handler_decimal_result::
subquery_type_allows_materialization(const Item *inner,
- const Item *outer) const
+ const Item *outer,
+ bool is_in_predicate) const
{
DBUG_ASSERT(inner->cmp_type() == DECIMAL_RESULT);
return outer->cmp_type() == DECIMAL_RESULT;
@@ -7104,23 +7108,37 @@ bool Type_handler_decimal_result::
bool Type_handler_string_result::
subquery_type_allows_materialization(const Item *inner,
- const Item *outer) const
+ const Item *outer,
+ bool is_in_predicate) const
{
DBUG_ASSERT(inner->cmp_type() == STRING_RESULT);
- return outer->cmp_type() == STRING_RESULT &&
- outer->collation.collation == inner->collation.collation &&
- /*
- Materialization also is unable to work when create_tmp_table() will
- create a blob column because item->max_length is too big.
- The following test is copied from varstring_type_handler().
- */
- !inner->too_big_for_varchar();
+ if (outer->cmp_type() == STRING_RESULT &&
+ /*
+ Materialization also is unable to work when create_tmp_table() will
+ create a blob column because item->max_length is too big.
+ The following test is copied from varstring_type_handler().
+ */
+ !inner->too_big_for_varchar())
+ {
+ if (outer->collation.collation == inner->collation.collation)
+ return true;
+ if (is_in_predicate)
+ {
+ Charset inner_col(inner->collation.collation);
+ if (inner_col.encoding_allows_reinterpret_as(outer->
+ collation.collation) &&
+ inner_col.eq_collation_specific_names(outer->collation.collation))
+ return true;
+ }
+ }
+ return false;
}
bool Type_handler_temporal_result::
subquery_type_allows_materialization(const Item *inner,
- const Item *outer) const
+ const Item *outer,
+ bool is_in_predicate) const
{
DBUG_ASSERT(inner->cmp_type() == TIME_RESULT);
return mysql_timestamp_type() ==
diff --git a/sql/sql_type.h b/sql/sql_type.h
index db4f67e343f..271e74a9762 100644
--- a/sql/sql_type.h
+++ b/sql/sql_type.h
@@ -4049,9 +4049,21 @@ public:
Item *target_expr, Item *target_value,
Item_bool_func2 *source,
Item *source_expr, Item *source_const) const= 0;
+
+ /*
+ @brief
+ Check if an IN subquery allows materialization or not
+ @param
+ inner expression on the inner side of the IN subquery
+ outer expression on the outer side of the IN subquery
+ is_in_predicate SET to true if IN subquery was converted from an
+ IN predicate or we are checking if materialization
+ strategy can be used for an IN predicate
+ */
virtual bool
subquery_type_allows_materialization(const Item *inner,
- const Item *outer) const= 0;
+ const Item *outer,
+ bool is_in_predicate) const= 0;
/**
Make a simple constant replacement item for a constant "src",
so the new item can futher be used for comparison with "cmp", e.g.:
@@ -4311,8 +4323,8 @@ public:
DBUG_ASSERT(0);
return 0;
}
- bool subquery_type_allows_materialization(const Item *inner,
- const Item *outer) const override
+ bool subquery_type_allows_materialization(const Item *, const Item *,
+ bool) const override
{
DBUG_ASSERT(0);
return false;
@@ -4714,7 +4726,8 @@ public:
int stored_field_cmp_to_item(THD *thd, Field *field, Item *item)
const override;
bool subquery_type_allows_materialization(const Item *inner,
- const Item *outer)
+ const Item *outer,
+ bool is_in_predicate)
const override;
void make_sort_key_part(uchar *to, Item *item,
const SORT_FIELD_ATTR *sort_field,
@@ -4819,7 +4832,9 @@ public:
return item_val.is_null() ? 0 : my_decimal(field).cmp(item_val.ptr());
}
bool subquery_type_allows_materialization(const Item *inner,
- const Item *outer) const override;
+ const Item *outer,
+ bool is_in_predicate)
+ const override;
Field *make_schema_field(MEM_ROOT *root,
TABLE *table,
const Record_addr &addr,
@@ -5073,7 +5088,9 @@ public:
const Type_handler *type_handler_for_comparison() const override;
int stored_field_cmp_to_item(THD *thd, Field *field, Item *item) const override;
bool subquery_type_allows_materialization(const Item *inner,
- const Item *outer) const override;
+ const Item *outer,
+ bool is_in_predicate)
+ const override;
Field *make_num_distinct_aggregator_field(MEM_ROOT *, const Item *) const override;
Field *make_table_field(MEM_ROOT *root,
const LEX_CSTRING *name,
@@ -5214,7 +5231,9 @@ public:
Item *source_expr, Item *source_const)
const override;
bool subquery_type_allows_materialization(const Item *inner,
- const Item *outer) const override;
+ const Item *outer,
+ bool is_in_predicate)
+ const override;
bool Item_func_min_max_fix_attributes(THD *thd, Item_func_min_max *func,
Item **items, uint nitems)
const override;
@@ -5352,7 +5371,9 @@ public:
Item *source_expr, Item *source_const) const
override;
bool subquery_type_allows_materialization(const Item *inner,
- const Item *outer) const override;
+ const Item *outer,
+ bool is_in_predicate)
+ const override;
Item *make_const_item_for_comparison(THD *, Item *src, const Item *cmp) const
override;
Item_cache *Item_get_cache(THD *thd, const Item *item) const override;
@@ -6983,8 +7004,8 @@ public:
{
return blob_type_handler(item);
}
- bool subquery_type_allows_materialization(const Item *inner,
- const Item *outer) const override
+ bool subquery_type_allows_materialization(const Item *, const Item *, bool)
+ const override
{
return false; // Materialization does not work with BLOB columns
}
@@ -7114,7 +7135,7 @@ public:
{
return MYSQL_TYPE_BLOB_COMPRESSED;
}
- ulong KEY_pack_flags(uint column_nr) const override
+ ulong KEY_pack_flags(uint) const override
{
DBUG_ASSERT(0);
return 0;
@@ -7125,7 +7146,7 @@ public:
Field *make_conversion_table_field(MEM_ROOT *root,
TABLE *table, uint metadata,
const Field *target) const override;
- enum_dynamic_column_type dyncol_type(const Type_all_attributes *attr)
+ enum_dynamic_column_type dyncol_type(const Type_all_attributes *)
const override
{
DBUG_ASSERT(0);
diff --git a/sql/sql_type_geom.h b/sql/sql_type_geom.h
index 74290e47afe..a2933114751 100644
--- a/sql/sql_type_geom.h
+++ b/sql/sql_type_geom.h
@@ -67,8 +67,8 @@ public:
geometry_type() == th->geometry_type();
}
bool type_can_have_key_part() const override { return true; }
- bool subquery_type_allows_materialization(const Item *inner,
- const Item *outer) const override
+ bool subquery_type_allows_materialization(const Item *, const Item *, bool)
+ const override
{
return false; // Materialization does not work with GEOMETRY columns
}
diff --git a/sql/sql_union.cc b/sql/sql_union.cc
index 9e51bb43a74..6da1175e709 100644
--- a/sql/sql_union.cc
+++ b/sql/sql_union.cc
@@ -2747,6 +2747,7 @@ bool st_select_lex::cleanup()
delete join;
join= 0;
}
+ leaf_tables.empty();
for (SELECT_LEX_UNIT *lex_unit= first_inner_unit(); lex_unit ;
lex_unit= lex_unit->next_unit())
{
diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc
index 64040243df0..d0eff0adada 100644
--- a/sql/sys_vars.cc
+++ b/sql/sys_vars.cc
@@ -4325,7 +4325,7 @@ static bool fix_autocommit(sys_var *self, THD *thd, enum_var_type type)
if (trans_commit_stmt(thd) || trans_commit(thd))
{
thd->variables.option_bits&= ~OPTION_AUTOCOMMIT;
- thd->mdl_context.release_transactional_locks();
+ thd->release_transactional_locks();
WSREP_DEBUG("autocommit, MDL TRX lock released: %lld",
(longlong) thd->thread_id);
return true;
diff --git a/sql/table.cc b/sql/table.cc
index c48a6fed89a..6cd2b1690cf 100644
--- a/sql/table.cc
+++ b/sql/table.cc
@@ -8741,7 +8741,7 @@ int TABLE::period_make_insert(Item *src, Field *dst)
{
THD *thd= in_use;
- file->store_auto_increment();
+ ulonglong prev_insert_id= file->next_insert_id;
store_record(this, record[1]);
int res= src->save_in_field(dst, true);
@@ -8761,7 +8761,7 @@ int TABLE::period_make_insert(Item *src, Field *dst)
restore_record(this, record[1]);
if (res)
- file->restore_auto_increment();
+ file->restore_auto_increment(prev_insert_id);
return res;
}
diff --git a/sql/transaction.cc b/sql/transaction.cc
index 421aa1cfc51..8bd58419ec9 100644
--- a/sql/transaction.cc
+++ b/sql/transaction.cc
@@ -151,7 +151,7 @@ bool trans_begin(THD *thd, uint flags)
Release transactional metadata locks only after the
transaction has been committed.
*/
- thd->mdl_context.release_transactional_locks();
+ thd->release_transactional_locks();
// The RO/RW options are mutually exclusive.
DBUG_ASSERT(!((flags & MYSQL_START_TRANS_OPT_READ_ONLY) &&
diff --git a/sql/unireg.cc b/sql/unireg.cc
index ecd5bb8d430..bffe51b6c49 100644
--- a/sql/unireg.cc
+++ b/sql/unireg.cc
@@ -1151,7 +1151,6 @@ static bool make_empty_rec(THD *thd, uchar *buff, uint table_options,
TABLE table;
TABLE_SHARE share;
Create_field *field;
- enum_check_fields old_count_cuted_fields= thd->count_cuted_fields;
DBUG_ENTER("make_empty_rec");
/* We need a table to generate columns for default values */
@@ -1170,7 +1169,7 @@ static bool make_empty_rec(THD *thd, uchar *buff, uint table_options,
null_pos= buff;
List_iterator<Create_field> it(create_fields);
- thd->count_cuted_fields= CHECK_FIELD_WARN; // To find wrong default values
+ Check_level_instant_set check_level_save(thd, CHECK_FIELD_WARN);
while ((field=it++))
{
Record_addr addr(buff + field->offset + data_offset,
@@ -1217,6 +1216,5 @@ static bool make_empty_rec(THD *thd, uchar *buff, uint table_options,
*(null_pos + null_count / 8)|= ~(((uchar) 1 << (null_count & 7)) - 1);
err:
- thd->count_cuted_fields= old_count_cuted_fields;
DBUG_RETURN(error);
} /* make_empty_rec */
diff --git a/sql/wsrep_client_service.cc b/sql/wsrep_client_service.cc
index c01c256e872..24e1e198507 100644
--- a/sql/wsrep_client_service.cc
+++ b/sql/wsrep_client_service.cc
@@ -345,7 +345,7 @@ int Wsrep_client_service::bf_rollback()
{
m_thd->global_read_lock.unlock_global_read_lock(m_thd);
}
- m_thd->mdl_context.release_transactional_locks();
+ m_thd->release_transactional_locks();
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 5961a9574eb..3116315b9c7 100644
--- a/sql/wsrep_high_priority_service.cc
+++ b/sql/wsrep_high_priority_service.cc
@@ -290,7 +290,7 @@ int Wsrep_high_priority_service::append_fragment_and_commit(
ret= ret || trans_commit(m_thd);
ret= ret || (m_thd->wsrep_cs().after_applying(), 0);
- m_thd->mdl_context.release_transactional_locks();
+ m_thd->release_transactional_locks();
free_root(m_thd->mem_root, MYF(MY_KEEP_PREALLOC));
@@ -332,7 +332,7 @@ int Wsrep_high_priority_service::commit(const wsrep::ws_handle& ws_handle,
m_rgi->cleanup_context(thd, 0);
}
- m_thd->mdl_context.release_transactional_locks();
+ m_thd->release_transactional_locks();
thd_proc_info(thd, "wsrep applier committed");
@@ -378,7 +378,7 @@ int Wsrep_high_priority_service::rollback(const wsrep::ws_handle& ws_handle,
assert(ws_handle == wsrep::ws_handle());
}
int ret= (trans_rollback_stmt(m_thd) || trans_rollback(m_thd));
- m_thd->mdl_context.release_transactional_locks();
+ m_thd->release_transactional_locks();
m_thd->mdl_context.release_explicit_locks();
free_root(m_thd->mem_root, MYF(MY_KEEP_PREALLOC));
diff --git a/sql/wsrep_mysqld.cc b/sql/wsrep_mysqld.cc
index 1dfeb782bbe..284df936160 100644
--- a/sql/wsrep_mysqld.cc
+++ b/sql/wsrep_mysqld.cc
@@ -1267,7 +1267,7 @@ wsrep_append_fk_parent_table(THD* thd, TABLE_LIST* tables, wsrep::key_array* key
if (!WSREP(thd) || !WSREP_CLIENT(thd)) return;
TABLE_LIST *table;
- thd->mdl_context.release_transactional_locks();
+ thd->release_transactional_locks();
uint counter;
MDL_savepoint mdl_savepoint= thd->mdl_context.mdl_savepoint();
diff --git a/sql/wsrep_schema.cc b/sql/wsrep_schema.cc
index 9f568226079..ed6f3ebc881 100644
--- a/sql/wsrep_schema.cc
+++ b/sql/wsrep_schema.cc
@@ -873,7 +873,7 @@ Wsrep_view Wsrep_schema::restore_view(THD* thd, const Wsrep_id& own_id) const {
close_thread_tables(thd);
}
}
- thd->mdl_context.release_transactional_locks();
+ thd->release_transactional_locks();
thd->variables.wsrep_sync_wait= wsrep_sync_wait_saved;
diff --git a/sql/wsrep_server_service.cc b/sql/wsrep_server_service.cc
index 7ba744b4d3c..32b585871d4 100644
--- a/sql/wsrep_server_service.cc
+++ b/sql/wsrep_server_service.cc
@@ -248,7 +248,7 @@ void Wsrep_server_service::log_view(
WSREP_WARN("Failed to commit transaction for store view");
}
}
- applier->m_thd->mdl_context.release_transactional_locks();
+ applier->m_thd->release_transactional_locks();
}
/*
diff --git a/sql/wsrep_storage_service.cc b/sql/wsrep_storage_service.cc
index 2ad817fe25a..4885fd9f7e6 100644
--- a/sql/wsrep_storage_service.cc
+++ b/sql/wsrep_storage_service.cc
@@ -176,7 +176,7 @@ int Wsrep_storage_service::commit(const wsrep::ws_handle& ws_handle,
trans_rollback(m_thd);
}
m_thd->wsrep_cs().after_applying();
- m_thd->mdl_context.release_transactional_locks();
+ m_thd->release_transactional_locks();
DBUG_RETURN(ret);
}
@@ -191,7 +191,7 @@ int Wsrep_storage_service::rollback(const wsrep::ws_handle& ws_handle,
ws_handle, ws_meta, false) ||
trans_rollback(m_thd));
m_thd->wsrep_cs().after_applying();
- m_thd->mdl_context.release_transactional_locks();
+ m_thd->release_transactional_locks();
DBUG_RETURN(ret);
}
diff --git a/sql/xa.cc b/sql/xa.cc
index 15833377fb6..e0defcb92ed 100644
--- a/sql/xa.cc
+++ b/sql/xa.cc
@@ -404,6 +404,7 @@ bool xa_trans_force_rollback(THD *thd)
xid_cache_delete(thd, &thd->transaction->xid_state);
trans_track_end_trx(thd);
+ thd->mdl_context.release_transactional_locks(thd);
return rc;
}
@@ -554,11 +555,13 @@ bool trans_xa_prepare(THD *thd)
/**
Commit and terminate the a XA transaction.
+ Transactional locks are released if transaction ended
@param thd Current thread
@retval FALSE Success
@retval TRUE Failure
+
*/
bool trans_xa_commit(THD *thd)
@@ -718,6 +721,8 @@ bool trans_xa_commit(THD *thd)
xid_cache_delete(thd, &xid_state);
trans_track_end_trx(thd);
+ thd->mdl_context.release_transactional_locks(thd);
+
/* The transaction should be marked as complete in P_S. */
DBUG_ASSERT(thd->m_transaction_psi == NULL || res);
DBUG_RETURN(res);
@@ -726,6 +731,7 @@ bool trans_xa_commit(THD *thd)
/**
Roll back and terminate a XA transaction.
+ Transactional locks are released if transaction ended
@param thd Current thread
@@ -844,6 +850,10 @@ bool trans_xa_detach(THD *thd)
thd->transaction->all.ha_list= 0;
thd->transaction->all.no_2pc= 0;
thd->m_transaction_psi= 0;
+ thd->server_status&= ~(SERVER_STATUS_IN_TRANS |
+ SERVER_STATUS_IN_TRANS_READONLY);
+ thd->mdl_context.release_transactional_locks(thd);
+
return false;
}
diff --git a/storage/innobase/dict/dict0dict.cc b/storage/innobase/dict/dict0dict.cc
index 168636cc965..f613162fb04 100644
--- a/storage/innobase/dict/dict0dict.cc
+++ b/storage/innobase/dict/dict0dict.cc
@@ -2193,6 +2193,7 @@ dict_index_remove_from_cache_low(
if (index->online_log) {
ut_ad(index->online_status == ONLINE_INDEX_CREATION);
row_log_free(index->online_log);
+ index->online_log = NULL;
}
/* Remove the index from the list of indexes of the table */
diff --git a/storage/innobase/dict/dict0stats.cc b/storage/innobase/dict/dict0stats.cc
index 07d5230b3f7..d6fbb9b7937 100644
--- a/storage/innobase/dict/dict0stats.cc
+++ b/storage/innobase/dict/dict0stats.cc
@@ -1981,7 +1981,7 @@ static index_stats_t dict_stats_analyze_index(dict_index_t* index)
since it will be faster and will give better results. */
if (root_level == 0
- || N_SAMPLE_PAGES(index) * n_uniq > index->stat_n_leaf_pages) {
+ || N_SAMPLE_PAGES(index) * n_uniq > result.n_leaf_pages) {
if (root_level == 0) {
DEBUG_PRINTF(" %s(): just one page,"
diff --git a/storage/innobase/handler/handler0alter.cc b/storage/innobase/handler/handler0alter.cc
index 710a3787e09..363cf50aa90 100644
--- a/storage/innobase/handler/handler0alter.cc
+++ b/storage/innobase/handler/handler0alter.cc
@@ -6957,6 +6957,7 @@ error_handling_drop_uncached:
if (ok && a == 1) {
row_log_free(
index->online_log);
+ index->online_log = NULL;
ok = false;
});
@@ -8533,6 +8534,7 @@ innobase_online_rebuild_log_free(
== ONLINE_INDEX_CREATION);
clust_index->online_status = ONLINE_INDEX_COMPLETE;
row_log_free(clust_index->online_log);
+ clust_index->online_log = NULL;
DEBUG_SYNC_C("innodb_online_rebuild_log_free_aborted");
}
diff --git a/storage/innobase/include/row0log.h b/storage/innobase/include/row0log.h
index 63fd877691c..5ec4b9c1103 100644
--- a/storage/innobase/include/row0log.h
+++ b/storage/innobase/include/row0log.h
@@ -1,7 +1,7 @@
/*****************************************************************************
Copyright (c) 2011, 2016, Oracle and/or its affiliates. All Rights Reserved.
-Copyright (c) 2017, MariaDB Corporation.
+Copyright (c) 2017, 2020, MariaDB Corporation.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
@@ -69,7 +69,7 @@ Free the row log for an index that was being created online. */
void
row_log_free(
/*=========*/
- row_log_t*& log) /*!< in,own: row log */
+ row_log_t* log) /*!< in,own: row log */
MY_ATTRIBUTE((nonnull));
/******************************************************//**
diff --git a/storage/innobase/include/row0log.ic b/storage/innobase/include/row0log.ic
index ba7eb7b025c..44d17bbcdf1 100644
--- a/storage/innobase/include/row0log.ic
+++ b/storage/innobase/include/row0log.ic
@@ -1,6 +1,7 @@
/*****************************************************************************
Copyright (c) 2011, 2015, Oracle and/or its affiliates. All Rights Reserved.
+Copyright (c) 2020, MariaDB Corporation.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
@@ -38,6 +39,7 @@ row_log_abort_sec(
ut_ad(!dict_index_is_clust(index));
dict_index_set_online_status(index, ONLINE_INDEX_ABORTED);
row_log_free(index->online_log);
+ index->online_log = NULL;
}
/******************************************************//**
diff --git a/storage/innobase/mtr/mtr0mtr.cc b/storage/innobase/mtr/mtr0mtr.cc
index a6f74bf927f..4c22f05c4ba 100644
--- a/storage/innobase/mtr/mtr0mtr.cc
+++ b/storage/innobase/mtr/mtr0mtr.cc
@@ -893,7 +893,7 @@ inline std::pair<lsn_t,bool> mtr_t::finish_write(ulint len)
return std::make_pair(start_lsn, flush);
}
-/** Find out whether a block was X-latched by the mini-transaction */
+/** Find out whether a block was not X-latched by the mini-transaction */
struct FindBlockX
{
const buf_block_t &block;
@@ -903,7 +903,7 @@ struct FindBlockX
/** @return whether the block was not found x-latched */
bool operator()(const mtr_memo_slot_t *slot) const
{
- return slot->object != &block || slot->type == MTR_MEMO_PAGE_X_FIX;
+ return slot->object != &block || slot->type != MTR_MEMO_PAGE_X_FIX;
}
};
diff --git a/storage/innobase/row/row0log.cc b/storage/innobase/row/row0log.cc
index 02429aac23b..4a98ac24185 100644
--- a/storage/innobase/row/row0log.cc
+++ b/storage/innobase/row/row0log.cc
@@ -3237,7 +3237,6 @@ row_log_allocate(
}
dict_index_set_online_status(index, ONLINE_INDEX_CREATION);
- index->online_log = log;
if (log_tmp_is_encrypted()) {
log->crypt_head_size = log->crypt_tail_size = srv_sort_buf_size;
@@ -3252,6 +3251,7 @@ row_log_allocate(
}
}
+ index->online_log = log;
/* While we might be holding an exclusive data dictionary lock
here, in row_log_abort_sec() we will not always be holding it. Use
atomic operations in both cases. */
@@ -3265,7 +3265,7 @@ Free the row log for an index that was being created online. */
void
row_log_free(
/*=========*/
- row_log_t*& log) /*!< in,own: row log */
+ row_log_t* log) /*!< in,own: row log */
{
MONITOR_ATOMIC_DEC(MONITOR_ONLINE_CREATE_INDEX);
@@ -3285,7 +3285,6 @@ row_log_free(
mutex_free(&log->mutex);
ut_free(log);
- log = NULL;
}
/******************************************************//**
diff --git a/storage/innobase/row/row0uins.cc b/storage/innobase/row/row0uins.cc
index 114d83c8564..0ce136c5906 100644
--- a/storage/innobase/row/row0uins.cc
+++ b/storage/innobase/row/row0uins.cc
@@ -426,6 +426,13 @@ close_table:
node->heap);
} else {
node->ref = &trx_undo_metadata;
+ if (!row_undo_search_clust_to_pcur(node)) {
+ /* An error probably occurred during
+ an insert into the clustered index,
+ after we wrote the undo log record. */
+ goto close_table;
+ }
+ return true;
}
if (!row_undo_search_clust_to_pcur(node)) {
diff --git a/storage/innobase/row/row0umod.cc b/storage/innobase/row/row0umod.cc
index f710f54d0c1..e52ca5815f3 100644
--- a/storage/innobase/row/row0umod.cc
+++ b/storage/innobase/row/row0umod.cc
@@ -1313,7 +1313,7 @@ close_table:
}
/* Extract indexed virtual columns from undo log */
- if (node->table->n_v_cols) {
+ if (node->ref != &trx_undo_metadata && node->table->n_v_cols) {
row_upd_replace_vcol(node->row, node->table,
node->update, false, node->undo_row,
(node->cmpl_info & UPD_NODE_NO_ORD_CHANGE)
diff --git a/storage/mroonga/ha_mroonga.hpp b/storage/mroonga/ha_mroonga.hpp
index f129ab74aa2..66767899e21 100644
--- a/storage/mroonga/ha_mroonga.hpp
+++ b/storage/mroonga/ha_mroonga.hpp
@@ -572,11 +572,6 @@ public:
void set_next_insert_id(ulonglong id);
void get_auto_increment(ulonglong offset, ulonglong increment, ulonglong nb_desired_values,
ulonglong *first_value, ulonglong *nb_reserved_values) mrn_override;
- /** Fix spurious -Werror=overloaded-virtual in GCC 9 */
- void restore_auto_increment() mrn_override
- {
- handler::restore_auto_increment();
- }
void restore_auto_increment(ulonglong prev_insert_id) mrn_override;
void release_auto_increment() mrn_override;
int check_for_upgrade(HA_CHECK_OPT *check_opt) mrn_override;
diff --git a/storage/oqgraph/CMakeLists.txt b/storage/oqgraph/CMakeLists.txt
index 68582e2802d..901acc6a8ca 100644
--- a/storage/oqgraph/CMakeLists.txt
+++ b/storage/oqgraph/CMakeLists.txt
@@ -20,6 +20,10 @@ INCLUDE_DIRECTORIES(${Judy_INCLUDE_DIR})
SET(OQGRAPH_OK 1)
ENDMACRO()
+IF(PLUGIN_OQGRAPH STREQUAL "NO")
+ RETURN()
+ENDIF()
+
IF(NOT DEFINED OQGRAPH_OK)
CHECK_OQGRAPH()
IF (NOT OQGRAPH_OK)
diff --git a/storage/rocksdb/mysql-test/rocksdb/t/checkpoint.test b/storage/rocksdb/mysql-test/rocksdb/t/checkpoint.test
index e5de6246f60..68fe02bbd86 100644
--- a/storage/rocksdb/mysql-test/rocksdb/t/checkpoint.test
+++ b/storage/rocksdb/mysql-test/rocksdb/t/checkpoint.test
@@ -87,7 +87,7 @@ let $checkpoint = $MYSQL_TMP_DIR/already-existing-directory;
--mkdir $checkpoint
let $succeeds = 0;
--source set_checkpoint.inc
---exec rm -rf $checkpoint
+rmdir $checkpoint;
--disable_result_log
truncate table t1;