Merge 10.2 into 10.3

This commit is contained in:
Marko Mäkelä 2021-07-27 10:47:17 +03:00
commit f50eb0d398
35 changed files with 402 additions and 268 deletions

View file

@ -1,5 +1,5 @@
/* Copyright (c) 2000, 2013, Oracle and/or its affiliates.
Copyright (c) 2010, 2019, MariaDB Corporation.
Copyright (c) 2010, 2021, 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
@ -283,7 +283,8 @@ extern int my_umask_dir,
extern my_bool my_use_symdir;
extern ulong my_default_record_cache_size;
extern my_bool my_disable_locking, my_disable_async_io,
extern MYSQL_PLUGIN_IMPORT my_bool my_disable_locking;
extern my_bool my_disable_async_io,
my_disable_flush_key_blocks, my_disable_symlinks;
extern my_bool my_disable_sync, my_disable_copystat_in_redel;
extern char wild_many,wild_one,wild_prefix;

View file

@ -4565,6 +4565,28 @@ drop procedure sp1;
drop procedure sp2;
drop table t1;
#
# MDEV-26202: Recursive CTE used indirectly twice
# (fixed by the patch forMDEV-26025)
#
with recursive
rcte as ( SELECT 1 AS a
UNION ALL
SELECT cast(a + 1 as unsigned int) FROM rcte WHERE a < 3),
cte1 AS (SELECT a FROM rcte),
cte2 AS (SELECT a FROM cte1),
cte3 AS ( SELECT a FROM cte2)
SELECT * FROM cte2, cte3;
a a
1 1
2 1
3 1
1 2
2 2
3 2
1 3
2 3
3 3
#
# End of 10.2 tests
#
#

View file

@ -2919,6 +2919,20 @@ drop procedure sp2;
drop table t1;
--echo #
--echo # MDEV-26202: Recursive CTE used indirectly twice
--echo # (fixed by the patch forMDEV-26025)
--echo #
with recursive
rcte as ( SELECT 1 AS a
UNION ALL
SELECT cast(a + 1 as unsigned int) FROM rcte WHERE a < 3),
cte1 AS (SELECT a FROM rcte),
cte2 AS (SELECT a FROM cte1),
cte3 AS ( SELECT a FROM cte2)
SELECT * FROM cte2, cte3;
--echo #
--echo # End of 10.2 tests
--echo #

View file

@ -2,6 +2,9 @@
# MySQL >= 5.0
#
# The test can take hours with valgrind
--source include/not_valgrind.inc
# Save the initial number of concurrent sessions
--source include/count_sessions.inc

View file

@ -4,6 +4,8 @@
# MySQL >= 5.0
#
# The test can take hours with valgrind
--source include/not_valgrind.inc
# Save the initial number of concurrent sessions
--source include/count_sessions.inc

View file

@ -18,6 +18,9 @@
# - with annotated events, default checksums and minimal binlog row image
#
# The test can take very long time with valgrind
--source include/not_valgrind.inc
--source include/have_partition.inc
--source encryption_algorithms.inc
--source include/have_innodb.inc

View file

@ -1,6 +1,9 @@
-- source include/have_innodb.inc
-- source include/have_file_key_management_plugin.inc
# The test can take very long time with valgrind
--source include/not_valgrind.inc
create table innodb_normal(c1 bigint not null, b char(200)) engine=innodb;
show warnings;
create table innodb_compact(c1 bigint not null, b char(200)) engine=innodb row_format=compact encrypted=yes encryption_key_id=1;

View file

@ -48,12 +48,6 @@ while ($cnt)
{
real_sleep 1;
dec $cnt;
if ($cnt == 200)
{
--disable_query_log
set global innodb_encrypt_tables = on;
--enable_query_log
}
}
}
if (!$success)
@ -84,12 +78,6 @@ while ($cnt)
{
real_sleep 1;
dec $cnt;
if ($cnt == 200)
{
--disable_query_log
set global innodb_encrypt_tables = off;
--enable_query_log
}
}
}
if (!$success)
@ -119,12 +107,6 @@ while ($cnt)
{
real_sleep 1;
dec $cnt;
if ($cnt == 200)
{
--disable_query_log
set global innodb_encrypt_tables=on;
--enable_query_log
}
}
}
if (!$success)

View file

@ -0,0 +1,52 @@
CREATE TABLE parent(parent_id int not null AUTO_INCREMENT PRIMARY KEY,
parent_name varchar(80)) ENGINE=InnoDB;
CREATE TABLE child(child_id int not null AUTO_INCREMENT PRIMARY KEY,
child_name varchar(80),
child_parent_id int not null,
CONSTRAINT `fk_child_parent`
FOREIGN KEY (child_parent_id) REFERENCES parent (parent_id)
ON DELETE CASCADE
ON UPDATE CASCADE) ENGINE=InnoDB;
INSERT INTO parent VALUES (1, 'first'),(2,'second'),(3,'foo'),(4,'tmp');
INSERT INTO child VALUES (NULL,'first_child',1);
INSERT INTO child VALUES (NULL,'second_child',1);
INSERT INTO child VALUES (NULL,'first_child2',2);
INSERT INTO child VALUES (NULL,'first_child3',2);
INSERT INTO child VALUES (NULL,'first_child4',3);
BEGIN;
UPDATE parent SET parent_name = 'bar' WHERE parent_id = 2;
connect node_1a, 127.0.0.1, root, , test, $NODE_MYPORT_1;
SET SESSION innodb_lock_wait_timeout=2;
UPDATE child SET child_parent_id = 5 where child_parent_id = 2;
ERROR HY000: Lock wait timeout exceeded; try restarting transaction
connection node_1;
COMMIT;
SELECT * FROM parent;
parent_id parent_name
1 first
2 bar
3 foo
4 tmp
SELECT * FROM child;
child_id child_name child_parent_id
1 first_child 1
3 second_child 1
5 first_child2 2
7 first_child3 2
9 first_child4 3
connection node_2;
SELECT * FROM parent;
parent_id parent_name
1 first
2 bar
3 foo
4 tmp
SELECT * FROM child;
child_id child_name child_parent_id
1 first_child 1
3 second_child 1
5 first_child2 2
7 first_child3 2
9 first_child4 3
DROP TABLE child, parent;
disconnect node_1a;

View file

@ -0,0 +1,40 @@
--source include/galera_cluster.inc
CREATE TABLE parent(parent_id int not null AUTO_INCREMENT PRIMARY KEY,
parent_name varchar(80)) ENGINE=InnoDB;
CREATE TABLE child(child_id int not null AUTO_INCREMENT PRIMARY KEY,
child_name varchar(80),
child_parent_id int not null,
CONSTRAINT `fk_child_parent`
FOREIGN KEY (child_parent_id) REFERENCES parent (parent_id)
ON DELETE CASCADE
ON UPDATE CASCADE) ENGINE=InnoDB;
INSERT INTO parent VALUES (1, 'first'),(2,'second'),(3,'foo'),(4,'tmp');
INSERT INTO child VALUES (NULL,'first_child',1);
INSERT INTO child VALUES (NULL,'second_child',1);
INSERT INTO child VALUES (NULL,'first_child2',2);
INSERT INTO child VALUES (NULL,'first_child3',2);
INSERT INTO child VALUES (NULL,'first_child4',3);
BEGIN;
UPDATE parent SET parent_name = 'bar' WHERE parent_id = 2;
--connect node_1a, 127.0.0.1, root, , test, $NODE_MYPORT_1
SET SESSION innodb_lock_wait_timeout=2;
--error ER_LOCK_WAIT_TIMEOUT
UPDATE child SET child_parent_id = 5 where child_parent_id = 2;
--connection node_1
COMMIT;
SELECT * FROM parent;
SELECT * FROM child;
--connection node_2
SELECT * FROM parent;
SELECT * FROM child;
DROP TABLE child, parent;
--disconnect node_1a

View file

@ -809,15 +809,18 @@ generated_email_id int as (email_id),
PRIMARY KEY (id),
KEY mautic_generated_sent_date_email_id (generated_email_id),
FOREIGN KEY (email_id) REFERENCES emails (id) ON DELETE SET NULL
ON UPDATE CASCADE
) ENGINE=InnoDB;
CREATE TABLE emails_metadata (
email_id int,
PRIMARY KEY (email_id),
CONSTRAINT FK FOREIGN KEY (email_id) REFERENCES emails (id) ON DELETE CASCADE
ON UPDATE CASCADE
) ENGINE=InnoDB;
INSERT INTO emails VALUES (1);
INSERT INTO email_stats (id, email_id, date_sent) VALUES (1,1,'Jan');
INSERT INTO emails_metadata VALUES (1);
UPDATE emails SET id=2;
DELETE FROM emails;
DROP TABLE email_stats;
DROP TABLE emails_metadata;

View file

@ -670,6 +670,7 @@ CREATE TABLE email_stats (
PRIMARY KEY (id),
KEY mautic_generated_sent_date_email_id (generated_email_id),
FOREIGN KEY (email_id) REFERENCES emails (id) ON DELETE SET NULL
ON UPDATE CASCADE
) ENGINE=InnoDB;
@ -677,6 +678,7 @@ CREATE TABLE emails_metadata (
email_id int,
PRIMARY KEY (email_id),
CONSTRAINT FK FOREIGN KEY (email_id) REFERENCES emails (id) ON DELETE CASCADE
ON UPDATE CASCADE
) ENGINE=InnoDB;
@ -684,6 +686,7 @@ INSERT INTO emails VALUES (1);
INSERT INTO email_stats (id, email_id, date_sent) VALUES (1,1,'Jan');
INSERT INTO emails_metadata VALUES (1);
UPDATE emails SET id=2;
DELETE FROM emails;
DROP TABLE email_stats;

View file

@ -1,6 +1,8 @@
-- source include/have_innodb.inc
-- source include/have_innodb_lz4.inc
-- source include/not_embedded.inc
# The test can take very long time with valgrind
--source include/not_valgrind.inc
# lz4
set global innodb_compression_algorithm = 2;

View file

@ -114,7 +114,7 @@ qsort_t my_qsort(void *base_ptr, size_t count, size_t size, qsort_cmp cmp)
stack[0].low=stack[0].high=0;
#endif
pivot = (char *) my_alloca((int) size);
ptr_cmp= size == sizeof(char*) && !((low - (char*) 0)& (sizeof(char*)-1));
ptr_cmp= size == sizeof(char*) && (intptr_t)low % sizeof(char*) == 0;
/* The following loop sorts elements between high and low */
do

View file

@ -1,5 +1,5 @@
// Copyright (c) 2014, Google Inc.
// Copyright (c) 2017, MariaDB Corporation.
// Copyright (c) 2017, 2021, MariaDB Corporation.
/**************************************************//**
@file btr/btr0scrub.cc
@ -830,20 +830,12 @@ btr_scrub_page(
/**************************************************************//**
Start iterating a space */
UNIV_INTERN
bool
btr_scrub_start_space(
/*===================*/
ulint space, /*!< in: space */
btr_scrub_t* scrub_data) /*!< in/out: scrub data */
bool btr_scrub_start_space(const fil_space_t &space, btr_scrub_t *scrub_data)
{
bool found;
scrub_data->space = space;
scrub_data->space = space.id;
scrub_data->current_table = NULL;
scrub_data->current_index = NULL;
const page_size_t page_size = fil_space_get_page_size(space, &found);
scrub_data->compressed = page_size.is_compressed();
scrub_data->compressed = FSP_FLAGS_GET_ZIP_SSIZE(space.flags) != 0;
scrub_data->scrubbing = check_scrub_setting(scrub_data);
return scrub_data->scrubbing;
}

View file

@ -1,6 +1,6 @@
/*****************************************************************************
Copyright (C) 2013, 2015, Google Inc. All Rights Reserved.
Copyright (c) 2014, 2020, MariaDB Corporation.
Copyright (c) 2014, 2021, 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
@ -1091,6 +1091,33 @@ struct rotate_thread_t {
}
};
/** Avoid the removal of the tablespace from
default_encrypt_list only when
1) Another active encryption thread working on tablespace
2) Eligible for tablespace key rotation
3) Tablespace is in flushing phase
@return true if tablespace should be removed from
default encrypt */
static bool fil_crypt_must_remove(const fil_space_t &space)
{
ut_ad(space.purpose == FIL_TYPE_TABLESPACE);
fil_space_crypt_t *crypt_data = space.crypt_data;
ut_ad(mutex_own(&fil_system.mutex));
const ulong encrypt_tables= srv_encrypt_tables;
if (!crypt_data)
return !encrypt_tables;
if (!crypt_data->is_key_found())
return true;
mutex_enter(&crypt_data->mutex);
const bool remove= (space.is_stopping() || crypt_data->not_encrypted()) &&
(!crypt_data->rotate_state.flushing &&
!encrypt_tables == !!crypt_data->min_key_version &&
!crypt_data->rotate_state.active_threads);
mutex_exit(&crypt_data->mutex);
return remove;
}
/***********************************************************************
Check if space needs rotation given a key_state
@param[in,out] state Key rotation state
@ -1172,7 +1199,7 @@ fil_crypt_space_needs_rotation(
key_state->rotate_key_age);
crypt_data->rotate_state.scrubbing.is_active =
btr_scrub_start_space(space->id, &state->scrub_data);
btr_scrub_start_space(*space, &state->scrub_data);
time_t diff = time(0) - crypt_data->rotate_state.scrubbing.
last_scrub_completed;
@ -1428,8 +1455,7 @@ inline fil_space_t *fil_system_t::default_encrypt_next(
If there is a change in innodb_encrypt_tables variables
value then don't remove the last processed tablespace
from the default encrypt list. */
if (released && (!recheck || space->crypt_data) &&
!encrypt == !srv_encrypt_tables)
if (released && !recheck && fil_crypt_must_remove(*space))
{
ut_a(!default_encrypt_tables.empty());
default_encrypt_tables.remove(*space);

View file

@ -4441,7 +4441,7 @@ innobase_commit_low(
if (trx_is_started(trx)) {
trx_commit_for_mysql(trx);
} else {
trx->will_lock = 0;
trx->will_lock = false;
#ifdef WITH_WSREP
trx->wsrep = false;
#endif /* WITH_WSREP */
@ -4795,7 +4795,7 @@ innobase_rollback_trx(
lock_unlock_table_autoinc(trx);
if (!trx->has_logged()) {
trx->will_lock = 0;
trx->will_lock = false;
#ifdef WITH_WSREP
trx->wsrep = false;
#endif
@ -8066,7 +8066,7 @@ ha_innobase::write_row(
ut_a(m_prebuilt->trx == trx);
if (!trx_is_started(trx)) {
++trx->will_lock;
trx->will_lock = true;
}
#ifdef WITH_WSREP
@ -8842,7 +8842,7 @@ ha_innobase::update_row(
ib_senderrf(ha_thd(), IB_LOG_LEVEL_WARN, ER_READ_ONLY_MODE);
DBUG_RETURN(HA_ERR_TABLE_READONLY);
} else if (!trx_is_started(trx)) {
++trx->will_lock;
trx->will_lock = true;
}
if (m_upd_buf == NULL) {
@ -9021,7 +9021,7 @@ ha_innobase::delete_row(
ib_senderrf(ha_thd(), IB_LOG_LEVEL_WARN, ER_READ_ONLY_MODE);
DBUG_RETURN(HA_ERR_TABLE_READONLY);
} else if (!trx_is_started(trx)) {
++trx->will_lock;
trx->will_lock = true;
}
if (!m_prebuilt->upd_node) {
@ -9899,7 +9899,7 @@ ha_innobase::ft_init()
them as regular read only transactions for now. */
if (!trx_is_started(trx)) {
++trx->will_lock;
trx->will_lock = true;
}
DBUG_RETURN(rnd_init(false));
@ -9965,7 +9965,7 @@ ha_innobase::ft_init_ext(
them as regular read only transactions for now. */
if (!trx_is_started(trx)) {
++trx->will_lock;
trx->will_lock = true;
}
dict_table_t* ft_table = m_prebuilt->table;
@ -12893,7 +12893,7 @@ create_table_info_t::allocate_trx()
{
m_trx = innobase_trx_allocate(m_thd);
m_trx->will_lock++;
m_trx->will_lock = true;
m_trx->ddl = true;
}
@ -13207,13 +13207,7 @@ inline int ha_innobase::delete_table(const char* name, enum_sql_command sqlcom)
ut_a(name_len < 1000);
/* Either the transaction is already flagged as a locking transaction
or it hasn't been started yet. */
ut_a(!trx_is_started(trx) || trx->will_lock > 0);
/* We are doing a DDL operation. */
++trx->will_lock;
trx->will_lock = true;
/* Drop the table in InnoDB */
@ -13390,14 +13384,7 @@ innobase_drop_database(
#endif /* _WIN32 */
trx_t* trx = innobase_trx_allocate(thd);
/* Either the transaction is already flagged as a locking transaction
or it hasn't been started yet. */
ut_a(!trx_is_started(trx) || trx->will_lock > 0);
/* We are doing a DDL operation. */
++trx->will_lock;
trx->will_lock = true;
ulint dummy;
@ -13441,7 +13428,7 @@ inline dberr_t innobase_rename_table(trx_t *trx, const char *from,
DEBUG_SYNC_C("innodb_rename_table_ready");
trx_start_if_not_started(trx, true);
ut_ad(trx->will_lock > 0);
ut_ad(trx->will_lock);
if (commit) {
/* Serialize data dictionary operations with dictionary mutex:
@ -13590,8 +13577,7 @@ int ha_innobase::truncate()
heap, ib_table->name.m_name, ib_table->id);
const char* name = mem_heap_strdup(heap, ib_table->name.m_name);
trx_t* trx = innobase_trx_allocate(m_user_thd);
++trx->will_lock;
trx->will_lock = true;
trx_set_dict_operation(trx, TRX_DICT_OP_TABLE);
row_mysql_lock_data_dictionary(trx);
dict_stats_wait_bg_to_stop_using_table(ib_table, trx);
@ -13676,9 +13662,7 @@ ha_innobase::rename_table(
}
trx_t* trx = innobase_trx_allocate(thd);
/* We are doing a DDL operation. */
++trx->will_lock;
trx->will_lock = true;
trx_set_dict_operation(trx, TRX_DICT_OP_INDEX);
dberr_t error = innobase_rename_table(trx, from, to, true);
@ -15673,7 +15657,7 @@ ha_innobase::start_stmt(
innobase_register_trx(ht, thd, trx);
if (!trx_is_started(trx)) {
++trx->will_lock;
trx->will_lock = true;
}
DBUG_RETURN(0);
@ -15900,7 +15884,7 @@ ha_innobase::external_lock(
&& (m_prebuilt->select_lock_type != LOCK_NONE
|| m_prebuilt->stored_select_lock_type != LOCK_NONE)) {
++trx->will_lock;
trx->will_lock = true;
}
DBUG_RETURN(0);
@ -15941,7 +15925,7 @@ ha_innobase::external_lock(
&& (m_prebuilt->select_lock_type != LOCK_NONE
|| m_prebuilt->stored_select_lock_type != LOCK_NONE)) {
++trx->will_lock;
trx->will_lock = true;
}
DBUG_RETURN(0);
@ -16623,7 +16607,7 @@ ha_innobase::store_lock(
&& (m_prebuilt->select_lock_type != LOCK_NONE
|| m_prebuilt->stored_select_lock_type != LOCK_NONE)) {
++trx->will_lock;
trx->will_lock = true;
}
return(to);

View file

@ -1369,7 +1369,7 @@ ha_innobase::check_if_supported_inplace_alter(
}
}
m_prebuilt->trx->will_lock++;
m_prebuilt->trx->will_lock = true;
/* When changing a NULL column to NOT NULL and specifying a
DEFAULT value, ensure that the DEFAULT expression is a constant.
@ -10131,7 +10131,6 @@ foreign_fail:
m_prebuilt = ctx->prebuilt;
}
trx_start_if_not_started(user_trx, true);
user_trx->will_lock++;
m_prebuilt->trx = user_trx;
}
DBUG_INJECT_CRASH("ib_commit_inplace_crash",

View file

@ -141,12 +141,7 @@ btr_scrub_skip_page(
/****************************************************************
Start iterating a space
* @return true if scrubbing is turned on */
UNIV_INTERN
bool
btr_scrub_start_space(
/*===================*/
ulint space, /*!< in: space */
btr_scrub_t* scrub_data); /*!< in/out: scrub data */
bool btr_scrub_start_space(const fil_space_t &space, btr_scrub_t *scrub_data);
/** Complete iterating a space.
@param[in,out] scrub_data scrub data */

View file

@ -1,7 +1,7 @@
/*****************************************************************************
Copyright (c) 2007, 2015, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2017, 2019, MariaDB Corporation.
Copyright (c) 2017, 2021, 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
@ -164,8 +164,8 @@ struct i_s_trx_row_t {
/*!< detailed_error in trx_t */
ulint trx_is_read_only;
/*!< trx_t::read_only */
ulint trx_is_autocommit_non_locking;
/*!< trx_is_autocommit_non_locking(trx)
bool trx_is_autocommit_non_locking;
/*!< trx:t::is_autocommit_non_locking()
*/
};

View file

@ -512,7 +512,7 @@ class rw_trx_hash_t
static void validate_element(trx_t *trx)
{
ut_ad(!trx->read_only || !trx->rsegs.m_redo.rseg);
ut_ad(!trx_is_autocommit_non_locking(trx));
ut_ad(!trx->is_autocommit_non_locking());
/* trx->state can be anything except TRX_STATE_NOT_STARTED */
mutex_enter(&trx->mutex);
ut_ad(trx_state_eq(trx, TRX_STATE_ACTIVE) ||

View file

@ -406,81 +406,6 @@ from innodb_lock_wait_timeout via trx_t::mysql_thd.
? thd_lock_wait_timeout((t)->mysql_thd) \
: 0)
/**
Determine if the transaction is a non-locking autocommit select
(implied read-only).
@param t transaction
@return true if non-locking autocommit select transaction. */
#define trx_is_autocommit_non_locking(t) \
((t)->auto_commit && (t)->will_lock == 0)
/**
Determine if the transaction is a non-locking autocommit select
with an explicit check for the read-only status.
@param t transaction
@return true if non-locking autocommit read-only transaction. */
#define trx_is_ac_nl_ro(t) \
((t)->read_only && trx_is_autocommit_non_locking((t)))
/**
Check transaction state */
#define check_trx_state(t) do { \
ut_ad(!trx_is_autocommit_non_locking((t))); \
switch ((t)->state) { \
case TRX_STATE_PREPARED: \
case TRX_STATE_PREPARED_RECOVERED: \
case TRX_STATE_ACTIVE: \
case TRX_STATE_COMMITTED_IN_MEMORY: \
continue; \
case TRX_STATE_NOT_STARTED: \
break; \
} \
ut_error; \
} while (0)
/** Check if transaction is free so that it can be re-initialized.
@param t transaction handle */
#define assert_trx_is_free(t) do { \
ut_ad(trx_state_eq((t), TRX_STATE_NOT_STARTED)); \
ut_ad(!(t)->id); \
ut_ad(!(t)->has_logged()); \
ut_ad(!(t)->is_referenced()); \
ut_ad(!(t)->is_wsrep()); \
ut_ad(!(t)->read_view.is_open()); \
ut_ad((t)->lock.wait_thr == NULL); \
ut_ad(UT_LIST_GET_LEN((t)->lock.trx_locks) == 0); \
ut_ad((t)->lock.table_locks.empty()); \
ut_ad(!(t)->autoinc_locks \
|| ib_vector_is_empty((t)->autoinc_locks)); \
ut_ad((t)->dict_operation == TRX_DICT_OP_NONE); \
} while(0)
#ifdef UNIV_DEBUG
/*******************************************************************//**
Assert that an autocommit non-locking select cannot be in the
rw_trx_hash and that it is a read-only transaction.
The transaction must have mysql_thd assigned. */
# define assert_trx_nonlocking_or_in_list(t) \
do { \
if (trx_is_autocommit_non_locking(t)) { \
trx_state_t t_state = (t)->state; \
ut_ad((t)->read_only); \
ut_ad(!(t)->is_recovered); \
ut_ad((t)->mysql_thd); \
ut_ad(t_state == TRX_STATE_NOT_STARTED \
|| t_state == TRX_STATE_ACTIVE); \
} else { \
check_trx_state(t); \
} \
} while (0)
#else /* UNIV_DEBUG */
/*******************************************************************//**
Assert that an autocommit non-locking slect cannot be in the
rw_trx_hash and that it is a read-only transaction.
The transaction must have mysql_thd assigned. */
# define assert_trx_nonlocking_or_in_list(trx) ((void)0)
#endif /* UNIV_DEBUG */
typedef std::vector<ib_lock_t*, ut_allocator<ib_lock_t*> > lock_list;
/*******************************************************************//**
@ -985,16 +910,15 @@ public:
/*------------------------------*/
bool read_only; /*!< true if transaction is flagged
as a READ-ONLY transaction.
if auto_commit && will_lock == 0
if auto_commit && !will_lock
then it will be handled as a
AC-NL-RO-SELECT (Auto Commit Non-Locking
Read Only Select). A read only
transaction will not be assigned an
UNDO log. */
bool auto_commit; /*!< true if it is an autocommit */
ib_uint32_t will_lock; /*!< Will acquire some locks. Increment
each time we determine that a lock will
be acquired by the MySQL layer. */
bool will_lock; /*!< set to inform trx_start_low() that
the transaction may acquire locks */
/*------------------------------*/
fts_trx_t* fts_trx; /*!< FTS information, or NULL if
transaction hasn't modified tables
@ -1116,10 +1040,28 @@ public:
void free();
void assert_freed() const
{
ut_ad(state == TRX_STATE_NOT_STARTED);
ut_ad(!id);
ut_ad(!has_logged());
ut_ad(!const_cast<trx_t*>(this)->is_referenced());
ut_ad(!is_wsrep());
ut_ad(!read_view.is_open());
ut_ad(!lock.wait_thr);
ut_ad(UT_LIST_GET_LEN(lock.trx_locks) == 0);
ut_ad(lock.table_locks.empty());
ut_ad(!autoinc_locks || ib_vector_is_empty(autoinc_locks));
ut_ad(dict_operation == TRX_DICT_OP_NONE);
}
/** @return whether this is a non-locking autocommit transaction */
bool is_autocommit_non_locking() const { return auto_commit && !will_lock; }
private:
/** Assign a rollback segment for modifying temporary tables.
@return the assigned rollback segment */
trx_rseg_t* assign_temp_rseg();
/** Assign a rollback segment for modifying temporary tables.
@return the assigned rollback segment */
trx_rseg_t *assign_temp_rseg();
};
/**

View file

@ -1,7 +1,7 @@
/*****************************************************************************
Copyright (c) 1996, 2015, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2016, 2019, MariaDB Corporation.
Copyright (c) 2016, 2021, 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
@ -49,11 +49,15 @@ trx_state_eq(
case TRX_STATE_PREPARED:
case TRX_STATE_PREPARED_RECOVERED:
case TRX_STATE_COMMITTED_IN_MEMORY:
ut_ad(!trx_is_autocommit_non_locking(trx));
ut_ad(!trx->is_autocommit_non_locking());
return(trx->state == state);
case TRX_STATE_ACTIVE:
assert_trx_nonlocking_or_in_list(trx);
if (trx->is_autocommit_non_locking()) {
ut_ad(!trx->is_recovered);
ut_ad(trx->read_only);
ut_ad(trx->mysql_thd);
}
return(state == trx->state);
case TRX_STATE_NOT_STARTED:

View file

@ -1309,6 +1309,19 @@ wsrep_print_wait_locks(
}
#endif /* WITH_WSREP */
#ifdef UNIV_DEBUG
/** Check transaction state */
static void check_trx_state(const trx_t *trx)
{
ut_ad(!trx->auto_commit || trx->will_lock);
const trx_state_t state= trx->state;
ut_ad(state == TRX_STATE_ACTIVE ||
state == TRX_STATE_PREPARED_RECOVERED ||
state == TRX_STATE_PREPARED ||
state == TRX_STATE_COMMITTED_IN_MEMORY);
}
#endif
/** Create a new record lock and inserts it to the lock queue,
without checking for deadlocks or conflicts.
@param[in] type_mode lock mode and wait flag; type will be replaced
@ -3449,8 +3462,8 @@ lock_table_create(
ut_ad(table && trx);
ut_ad(lock_mutex_own());
ut_ad(trx_mutex_own(trx));
check_trx_state(trx);
ut_ad(trx->is_recovered || trx->state == TRX_STATE_ACTIVE);
ut_ad(!trx->auto_commit || trx->will_lock);
if ((type_mode & LOCK_MODE_MASK) == LOCK_AUTO_INC) {
++table->n_waiting_or_granted_auto_inc_locks;
@ -4841,7 +4854,8 @@ lock_rec_queue_validate(
ut_ad(!index || lock->index == index);
trx_mutex_enter(lock->trx);
ut_ad(!trx_is_ac_nl_ro(lock->trx));
ut_ad(!lock->trx->read_only
|| !lock->trx->is_autocommit_non_locking());
ut_ad(trx_state_eq(lock->trx,
TRX_STATE_COMMITTED_IN_MEMORY)
|| !lock_get_wait(lock)
@ -4927,8 +4941,8 @@ func_exit:
for (lock = lock_rec_get_first(lock_sys.rec_hash, block, heap_no);
lock != NULL;
lock = lock_rec_get_next_const(heap_no, lock)) {
ut_ad(!trx_is_ac_nl_ro(lock->trx));
ut_ad(!lock->trx->read_only
|| !lock->trx->is_autocommit_non_locking());
ut_ad(!page_rec_is_metadata(rec));
if (index) {
@ -5018,7 +5032,8 @@ loop:
}
}
ut_ad(!trx_is_ac_nl_ro(lock->trx));
ut_ad(!lock->trx->read_only
|| !lock->trx->is_autocommit_non_locking());
/* Only validate the record queues when this thread is not
holding a space->latch. */
@ -5085,7 +5100,8 @@ lock_rec_validate(
ib_uint64_t current;
ut_ad(!trx_is_ac_nl_ro(lock->trx));
ut_ad(!lock->trx->read_only
|| !lock->trx->is_autocommit_non_locking());
ut_ad(lock_get_type(lock) == LOCK_REC);
current = ut_ull_create(
@ -6775,7 +6791,8 @@ DeadlockChecker::search()
ut_ad(m_start != NULL);
ut_ad(m_wait_lock != NULL);
check_trx_state(m_wait_lock->trx);
ut_ad(!m_wait_lock->trx->auto_commit || m_wait_lock->trx->will_lock);
ut_d(check_trx_state(m_wait_lock->trx));
ut_ad(m_mark_start <= s_lock_mark_counter);
/* Look at the locks ahead of wait_lock in the lock queue. */
@ -6935,7 +6952,8 @@ DeadlockChecker::check_and_resolve(const lock_t* lock, trx_t* trx)
{
ut_ad(lock_mutex_own());
ut_ad(trx_mutex_own(trx));
check_trx_state(trx);
ut_ad(trx->state == TRX_STATE_ACTIVE);
ut_ad(!trx->auto_commit || trx->will_lock);
ut_ad(!srv_read_only_mode);
if (!innobase_deadlock_detect) {

View file

@ -1112,6 +1112,10 @@ os_file_lock(
int fd,
const char* name)
{
if (my_disable_locking) {
return 0;
}
struct flock lk;
lk.l_type = F_WRLCK;

View file

@ -1,7 +1,7 @@
/*****************************************************************************
Copyright (c) 1996, 2015, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2018, 2019, MariaDB Corporation.
Copyright (c) 2018, 2021, 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
@ -235,7 +235,7 @@ void ReadView::open(trx_t *trx)
may get started, committed and purged meanwhile. It is acceptable as
well, since this view doesn't see it.
*/
if (trx_is_autocommit_non_locking(trx) && m_ids.empty() &&
if (trx->is_autocommit_non_locking() && m_ids.empty() &&
m_low_limit_id == trx_sys.get_max_trx_id())
goto reopen;

View file

@ -935,8 +935,8 @@ row_ins_foreign_fill_virtual(
upd_field = update->fields + n_diff;
upd_field->old_v_val = static_cast<dfield_t*>(
mem_heap_alloc(cascade->heap,
sizeof *upd_field->old_v_val));
mem_heap_alloc(update->heap,
sizeof *upd_field->old_v_val));
dfield_copy(upd_field->old_v_val, vfield);

View file

@ -52,6 +52,11 @@ Created 12/27/1996 Heikki Tuuri
#include <algorithm>
#include <mysql/plugin.h>
#include <mysql/service_wsrep.h>
#ifdef WITH_WSREP
#include "log.h"
#include "wsrep.h"
#endif /* WITH_WSREP */
/* What kind of latch and lock can we assume when the control comes to
-------------------------------------------------------------------
@ -2466,34 +2471,30 @@ row_upd_sec_index_entry(
err = DB_SUCCESS;
break;
case DB_LOCK_WAIT:
if (UNIV_UNLIKELY(wsrep_debug)) {
ib::warn() << "WSREP: sec index FK lock wait"
<< " index " << index->name
<< " table " << index->table->name
<< " query " << wsrep_thd_query(trx->mysql_thd);
}
break;
case DB_DEADLOCK:
if (UNIV_UNLIKELY(wsrep_debug)) {
ib::warn() << "WSREP: sec index FK check fail for deadlock"
<< " index " << index->name
<< " table " << index->table->name
<< " query " << wsrep_thd_query(trx->mysql_thd);
}
case DB_LOCK_WAIT_TIMEOUT:
WSREP_DEBUG("Foreign key check fail: "
"%s on table %s index %s query %s",
ut_strerr(err), index->name, index->table->name,
wsrep_thd_query(trx->mysql_thd));
break;
default:
ib::error() << "WSREP: referenced FK check fail: " << err
<< " index " << index->name
<< " table " << index->table->name
<< " query " << wsrep_thd_query(trx->mysql_thd);
WSREP_ERROR("Foreign key check fail: "
"%s on table %s index %s query %s",
ut_strerr(err), index->name, index->table->name,
wsrep_thd_query(trx->mysql_thd));
break;
}
}
#endif /* WITH_WSREP */
}
#ifdef WITH_WSREP
ut_ad(err == DB_SUCCESS || err == DB_LOCK_WAIT
|| err == DB_DEADLOCK || err == DB_LOCK_WAIT_TIMEOUT);
#else
ut_ad(err == DB_SUCCESS);
#endif
if (referenced) {
rec_offs* offsets = rec_get_offsets(
@ -2804,17 +2805,21 @@ check_fk:
case DB_NO_REFERENCED_ROW:
err = DB_SUCCESS;
break;
case DB_LOCK_WAIT:
case DB_DEADLOCK:
if (UNIV_UNLIKELY(wsrep_debug)) {
ib::warn() << "WSREP: sec index FK check fail for deadlock"
<< " index " << index->name
<< " table " << index->table->name;
}
case DB_LOCK_WAIT_TIMEOUT:
WSREP_DEBUG("Foreign key check fail: "
"%s on table %s index %s query %s",
ut_strerr(err), index->name, index->table->name,
wsrep_thd_query(trx->mysql_thd));
goto err_exit;
default:
ib::error() << "WSREP: referenced FK check fail: " << err
<< " index " << index->name
<< " table " << index->table->name;
WSREP_ERROR("Foreign key check fail: "
"%s on table %s index %s query %s",
ut_strerr(err), index->name, index->table->name,
wsrep_thd_query(trx->mysql_thd));
goto err_exit;
}
#endif /* WITH_WSREP */
@ -3031,18 +3036,19 @@ row_upd_del_mark_clust_rec(
case DB_NO_REFERENCED_ROW:
err = DB_SUCCESS;
break;
case DB_LOCK_WAIT:
case DB_DEADLOCK:
if (UNIV_UNLIKELY(wsrep_debug)) {
ib::warn() << "WSREP: sec index FK check fail for deadlock"
<< " index " << index->name
<< " table " << index->table->name;
}
case DB_LOCK_WAIT_TIMEOUT:
WSREP_DEBUG("Foreign key check fail: "
"%d on table %s index %s query %s",
err, index->name, index->table->name,
wsrep_thd_query(trx->mysql_thd));
break;
default:
ib::error() << "WSREP: referenced FK check fail: " << err
<< " index " << index->name
<< " table " << index->table->name;
WSREP_ERROR("Foreign key check fail: "
"%d on table %s index %s query %s",
err, index->name, index->table->name,
wsrep_thd_query(trx->mysql_thd));
break;
}
#endif /* WITH_WSREP */

View file

@ -578,7 +578,7 @@ thd_done:
row->trx_is_read_only = trx->read_only;
row->trx_is_autocommit_non_locking = trx_is_autocommit_non_locking(trx);
row->trx_is_autocommit_non_locking = trx->is_autocommit_non_locking();
return(TRUE);
}
@ -1233,7 +1233,24 @@ static void fetch_data_into_cache_low(trx_i_s_cache_t *cache, const trx_t *trx)
{
i_s_locks_row_t *requested_lock_row;
assert_trx_nonlocking_or_in_list(trx);
#ifdef UNIV_DEBUG
{
const trx_state_t state= trx->state;
if (trx->is_autocommit_non_locking())
{
ut_ad(trx->read_only);
ut_ad(!trx->is_recovered);
ut_ad(trx->mysql_thd);
ut_ad(state == TRX_STATE_NOT_STARTED || state == TRX_STATE_ACTIVE);
}
else
ut_ad(state == TRX_STATE_ACTIVE ||
state == TRX_STATE_PREPARED ||
state == TRX_STATE_PREPARED_RECOVERED ||
state == TRX_STATE_COMMITTED_IN_MEMORY);
}
#endif /* UNIV_DEBUG */
if (add_trx_relevant_locks_to_cache(cache, trx, &requested_lock_row))
{

View file

@ -108,12 +108,18 @@ trx_rollback_to_savepoint_low(
heap = mem_heap_create(512);
roll_node = roll_node_create(heap);
ut_ad(!trx->in_rollback);
if (savept != NULL) {
roll_node->savept = savept;
check_trx_state(trx);
ut_ad(trx->mysql_thd);
ut_ad(!trx->is_recovered);
ut_ad(trx->state == TRX_STATE_ACTIVE);
} else {
assert_trx_nonlocking_or_in_list(trx);
ut_d(trx_state_t state = trx->state);
ut_ad(state == TRX_STATE_ACTIVE
|| state == TRX_STATE_PREPARED
|| state == TRX_STATE_PREPARED_RECOVERED);
}
trx->error_state = DB_SUCCESS;
@ -219,7 +225,7 @@ dberr_t trx_rollback_for_mysql(trx_t* trx)
switch (trx->state) {
case TRX_STATE_NOT_STARTED:
trx->will_lock = 0;
trx->will_lock = false;
ut_ad(trx->mysql_thd);
#ifdef WITH_WSREP
trx->wsrep = false;
@ -228,13 +234,14 @@ dberr_t trx_rollback_for_mysql(trx_t* trx)
case TRX_STATE_ACTIVE:
ut_ad(trx->mysql_thd);
assert_trx_nonlocking_or_in_list(trx);
ut_ad(!trx->is_recovered);
ut_ad(!trx->is_autocommit_non_locking() || trx->read_only);
return(trx_rollback_for_mysql_low(trx));
case TRX_STATE_PREPARED:
case TRX_STATE_PREPARED_RECOVERED:
ut_ad(!trx_is_autocommit_non_locking(trx));
if (trx->rsegs.m_redo.undo) {
ut_ad(!trx->is_autocommit_non_locking());
if (trx->has_logged_persistent()) {
/* The XA ROLLBACK of a XA PREPARE transaction
will consist of multiple mini-transactions.
@ -272,7 +279,7 @@ dberr_t trx_rollback_for_mysql(trx_t* trx)
return(trx_rollback_for_mysql_low(trx));
case TRX_STATE_COMMITTED_IN_MEMORY:
check_trx_state(trx);
ut_ad(!trx->is_autocommit_non_locking());
break;
}
@ -301,7 +308,9 @@ trx_rollback_last_sql_stat_for_mysql(
return(DB_SUCCESS);
case TRX_STATE_ACTIVE:
assert_trx_nonlocking_or_in_list(trx);
ut_ad(trx->mysql_thd);
ut_ad(!trx->is_recovered);
ut_ad(!trx->is_autocommit_non_locking() || trx->read_only);
trx->op_info = "rollback of SQL statement";

View file

@ -140,7 +140,7 @@ trx_init(
trx->auto_commit = false;
trx->will_lock = 0;
trx->will_lock = false;
trx->ddl = false;
@ -345,13 +345,13 @@ trx_t *trx_create()
MEM_MAKE_DEFINED(trx, sizeof *trx);
#endif
assert_trx_is_free(trx);
trx->assert_freed();
mem_heap_t* heap;
ib_alloc_t* alloc;
/* We just got trx from pool, it should be non locking */
ut_ad(trx->will_lock == 0);
ut_ad(!trx->will_lock);
ut_ad(trx->state == TRX_STATE_NOT_STARTED);
ut_ad(!trx->rw_trx_hash_pins);
@ -395,7 +395,7 @@ void trx_t::free()
dict_operation= TRX_DICT_OP_NONE;
trx_sys.deregister_trx(this);
assert_trx_is_free(this);
assert_freed();
trx_sys.rw_trx_hash.put_pins(this);
mysql_thd= NULL;
@ -567,7 +567,7 @@ void trx_disconnect_prepared(trx_t *trx)
trx->is_recovered= true;
trx->mysql_thd= NULL;
/* todo/fixme: suggest to do it at innodb prepare */
trx->will_lock= 0;
trx->will_lock= false;
}
/****************************************************************//**
@ -917,11 +917,10 @@ void trx_t::remove_flush_observer()
/** Assign a rollback segment for modifying temporary tables.
@return the assigned rollback segment */
trx_rseg_t*
trx_t::assign_temp_rseg()
trx_rseg_t *trx_t::assign_temp_rseg()
{
ut_ad(!rsegs.m_noredo.rseg);
ut_ad(!trx_is_autocommit_non_locking(this));
ut_ad(!is_autocommit_non_locking());
compile_time_assert(ut_is_2pow(TRX_SYS_N_RSEGS));
/* Choose a temporary rollback segment between 0 and 127
@ -971,8 +970,8 @@ trx_start_low(
&& thd_trx_is_read_only(trx->mysql_thd));
if (!trx->auto_commit) {
++trx->will_lock;
} else if (trx->will_lock == 0) {
trx->will_lock = true;
} else if (!trx->will_lock) {
trx->read_only = true;
}
@ -1015,7 +1014,7 @@ trx_start_low(
trx_sys.register_rw(trx);
} else {
if (!trx_is_autocommit_non_locking(trx)) {
if (!trx->is_autocommit_non_locking()) {
/* If this is a read-only transaction that is writing
to a temporary table then it needs a transaction id
@ -1285,11 +1284,14 @@ trx_commit_in_memory(
trx->must_flush_log_later = false;
trx->read_view.close();
if (trx_is_autocommit_non_locking(trx)) {
if (trx->is_autocommit_non_locking()) {
ut_ad(trx->id == 0);
ut_ad(trx->read_only);
ut_ad(!trx->will_lock);
ut_a(!trx->is_recovered);
ut_ad(trx->rsegs.m_redo.rseg == NULL);
ut_ad(trx->mysql_thd);
ut_ad(trx->state == TRX_STATE_ACTIVE);
/* Note: We are asserting without holding the lock mutex. But
that is OK because this transaction is not waiting and cannot
@ -1305,8 +1307,6 @@ trx_commit_in_memory(
and it cannot be removed from the trx_list and freed
without first acquiring the trx_sys_t::mutex. */
ut_ad(trx_state_eq(trx, TRX_STATE_ACTIVE));
MONITOR_INC(MONITOR_TRX_NL_RO_COMMIT);
DBUG_LOG("trx", "Autocommit in memory: " << trx);
@ -1437,7 +1437,7 @@ trx_commit_in_memory(
trx->wsrep = false;
#endif
assert_trx_is_free(trx);
trx->assert_freed();
trx_init(trx);
@ -1452,8 +1452,6 @@ trx_commit_in_memory(
@param[in,out] mtr mini-transaction (NULL if no modifications) */
void trx_commit_low(trx_t* trx, mtr_t* mtr)
{
assert_trx_nonlocking_or_in_list(trx);
ut_ad(!trx_state_eq(trx, TRX_STATE_COMMITTED_IN_MEMORY));
ut_ad(!mtr || mtr->is_active());
ut_d(bool aborted = trx->in_rollback
&& trx->error_state == DB_DEADLOCK);
@ -1462,23 +1460,15 @@ void trx_commit_low(trx_t* trx, mtr_t* mtr)
/* undo_no is non-zero if we're doing the final commit. */
if (trx->fts_trx != NULL && trx->undo_no != 0) {
dberr_t error;
ut_a(!trx_is_autocommit_non_locking(trx));
error = fts_commit(trx);
ut_a(!trx->is_autocommit_non_locking());
/* FTS-FIXME: Temporarily tolerate DB_DUPLICATE_KEY
instead of dying. This is a possible scenario if there
is a crash between insert to DELETED table committing
and transaction committing. The fix would be able to
return error from this function */
if (error != DB_SUCCESS && error != DB_DUPLICATE_KEY) {
/* FTS-FIXME: once we can return values from this
function, we should do so and signal an error
instead of just dying. */
ut_error;
if (dberr_t error = fts_commit(trx)) {
ut_a(error == DB_DUPLICATE_KEY);
}
}
@ -1770,7 +1760,6 @@ trx_print_low(
/*!< in: mem_heap_get_size(trx->lock.lock_heap) */
{
ibool newline;
const char* op_info;
fprintf(f, "TRANSACTION " TRX_ID_FMT, trx_get_id_for_print(trx));
@ -1797,9 +1786,7 @@ trx_print_low(
fprintf(f, ", state %lu", (ulong) trx->state);
ut_ad(0);
state_ok:
/* prevent a race condition */
op_info = trx->op_info;
const char* op_info = trx->op_info;
if (*op_info) {
putc(' ', f);
@ -2270,7 +2257,7 @@ trx_start_internal_low(
/* Ensure it is not flagged as an auto-commit-non-locking
transaction. */
trx->will_lock = 1;
trx->will_lock = true;
trx->internal = true;
@ -2286,7 +2273,7 @@ trx_start_internal_read_only_low(
/* Ensure it is not flagged as an auto-commit-non-locking
transaction. */
trx->will_lock = 1;
trx->will_lock = true;
trx->internal = true;
@ -2307,13 +2294,7 @@ trx_start_for_ddl_low(
the data dictionary will be locked in crash recovery. */
trx_set_dict_operation(trx, op);
/* Ensure it is not flagged as an auto-commit-non-locking
transation. */
trx->will_lock = 1;
trx->ddl= true;
trx_start_internal_low(trx);
return;
@ -2340,7 +2321,7 @@ trx_set_rw_mode(
trx_t* trx) /*!< in/out: transaction that is RW */
{
ut_ad(trx->rsegs.m_redo.rseg == 0);
ut_ad(!trx_is_autocommit_non_locking(trx));
ut_ad(!trx->is_autocommit_non_locking());
ut_ad(!trx->read_only);
ut_ad(trx->id == 0);

View file

@ -1,3 +1,6 @@
# The test can take very long time with valgrind
--source include/not_valgrind.inc
--disable_warnings
DROP TABLE IF EXISTS rsb, rsb_graph;
--enable_warnings

View file

@ -7266,7 +7266,7 @@ int ha_tokudb::create(
// in the database directory, so automatic filename-based
// discover_table_names() doesn't work either. So, it must force .frm
// file to disk.
form->s->write_frm_image();
error= form->s->write_frm_image();
#endif
#if defined(TOKU_INCLUDE_OPTION_STRUCTS) && TOKU_INCLUDE_OPTION_STRUCTS
@ -7298,8 +7298,8 @@ int ha_tokudb::create(
#endif // defined(TOKU_INCLUDE_OPTION_STRUCTS) && TOKU_INCLUDE_OPTION_STRUCTS
const toku_compression_method compression_method =
row_format_to_toku_compression_method(row_format);
bool create_from_engine = (create_info->table_options & HA_OPTION_CREATE_FROM_ENGINE);
if (error) { goto cleanup; }
if (create_from_engine) {
// table already exists, nothing to do
error = 0;

View file

@ -0,0 +1,10 @@
#
# 10.2 Test
#
# MDEV-23786: Assertion `!is_set() || (m_status == DA_OK_BULK &&
# is_bulk_op())'failed for TokuDB engine CREATE TABLE
#
set default_storage_engine='tokudb';
CREATE TABLE _uppercase.t (a INT) ENGINE=TokuDB;
ERROR 42000: Unknown database '_uppercase'
# End of 10.2 Test

View file

@ -0,0 +1,14 @@
source include/have_tokudb.inc;
--echo #
--echo # 10.2 Test
--echo #
--echo # MDEV-23786: Assertion `!is_set() || (m_status == DA_OK_BULK &&
--echo # is_bulk_op())'failed for TokuDB engine CREATE TABLE
--echo #
set default_storage_engine='tokudb';
--error ER_BAD_DB_ERROR
CREATE TABLE _uppercase.t (a INT) ENGINE=TokuDB;
--echo # End of 10.2 Test