Merge 10.4 into 10.5

This commit is contained in:
Marko Mäkelä 2020-07-21 17:33:16 +03:00
commit 4ec032b492
24 changed files with 641 additions and 176 deletions

View File

@ -1929,3 +1929,66 @@ KEY a_idx(a(1))
INSERT INTO t VALUES (1, 'something in the air'); INSERT INTO t VALUES (1, 'something in the air');
ALTER TABLE t MODIFY a text CHARSET utf8mb4; ALTER TABLE t MODIFY a text CHARSET utf8mb4;
DROP TABLE t; DROP TABLE t;
#
# MDEV-22899: Assertion `field->col->is_binary() || field->prefix_len % field->col->mbmaxlen == 0' failed in dict_index_add_to_cache
#
CREATE TABLE t1 (
a text CHARACTER SET utf8 DEFAULT NULL,
KEY a_key (a(1))
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
INSERT INTO t1 VALUES ();
ALTER TABLE t1 MODIFY a text DEFAULT NULL;
DROP TABLE t1;
CREATE TABLE t1 (
a text CHARACTER SET utf8 DEFAULT NULL,
b int,
KEY a_key (b, a(1))
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
INSERT INTO t1 VALUES ();
ALTER TABLE t1 MODIFY a text DEFAULT NULL;
DROP TABLE t1;
CREATE TABLE t1 (
a char(200) CHARACTER SET utf8 DEFAULT NULL,
KEY a_key (a(1))
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
INSERT INTO t1 VALUES ();
ALTER TABLE t1 MODIFY a text DEFAULT NULL;
DROP TABLE t1;
CREATE TABLE t1 (
a char(200) CHARACTER SET utf8 DEFAULT NULL,
b int,
KEY a_key (b, a(1))
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
INSERT INTO t1 VALUES ();
ALTER TABLE t1 MODIFY a text DEFAULT NULL;
DROP TABLE t1;
CREATE TABLE t1 (
a varchar(200) CHARACTER SET utf8 DEFAULT NULL,
KEY a_key (a(1))
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
INSERT INTO t1 VALUES ();
ALTER TABLE t1 MODIFY a text DEFAULT NULL;
DROP TABLE t1;
CREATE TABLE t1 (
a varchar(200) CHARACTER SET utf8 DEFAULT NULL,
b int,
KEY a_key (b, a(1))
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
INSERT INTO t1 VALUES ();
ALTER TABLE t1 MODIFY a text DEFAULT NULL;
DROP TABLE t1;
CREATE TABLE t1 (
a varchar(2000) CHARACTER SET utf8 DEFAULT NULL,
KEY a_key (a(1))
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
INSERT INTO t1 VALUES ();
ALTER TABLE t1 MODIFY a text DEFAULT NULL;
DROP TABLE t1;
CREATE TABLE t1 (
a varchar(2000) CHARACTER SET utf8 DEFAULT NULL,
b int,
KEY a_key (b, a(1))
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
INSERT INTO t1 VALUES ();
ALTER TABLE t1 MODIFY a text DEFAULT NULL;
DROP TABLE t1;

View File

@ -730,3 +730,76 @@ INSERT INTO t VALUES (1, 'something in the air');
ALTER TABLE t MODIFY a text CHARSET utf8mb4; ALTER TABLE t MODIFY a text CHARSET utf8mb4;
DROP TABLE t; DROP TABLE t;
--echo #
--echo # MDEV-22899: Assertion `field->col->is_binary() || field->prefix_len % field->col->mbmaxlen == 0' failed in dict_index_add_to_cache
--echo #
CREATE TABLE t1 (
a text CHARACTER SET utf8 DEFAULT NULL,
KEY a_key (a(1))
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
INSERT INTO t1 VALUES ();
ALTER TABLE t1 MODIFY a text DEFAULT NULL;
DROP TABLE t1;
CREATE TABLE t1 (
a text CHARACTER SET utf8 DEFAULT NULL,
b int,
KEY a_key (b, a(1))
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
INSERT INTO t1 VALUES ();
ALTER TABLE t1 MODIFY a text DEFAULT NULL;
DROP TABLE t1;
CREATE TABLE t1 (
a char(200) CHARACTER SET utf8 DEFAULT NULL,
KEY a_key (a(1))
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
INSERT INTO t1 VALUES ();
ALTER TABLE t1 MODIFY a text DEFAULT NULL;
DROP TABLE t1;
CREATE TABLE t1 (
a char(200) CHARACTER SET utf8 DEFAULT NULL,
b int,
KEY a_key (b, a(1))
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
INSERT INTO t1 VALUES ();
ALTER TABLE t1 MODIFY a text DEFAULT NULL;
DROP TABLE t1;
CREATE TABLE t1 (
a varchar(200) CHARACTER SET utf8 DEFAULT NULL,
KEY a_key (a(1))
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
INSERT INTO t1 VALUES ();
ALTER TABLE t1 MODIFY a text DEFAULT NULL;
DROP TABLE t1;
CREATE TABLE t1 (
a varchar(200) CHARACTER SET utf8 DEFAULT NULL,
b int,
KEY a_key (b, a(1))
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
INSERT INTO t1 VALUES ();
ALTER TABLE t1 MODIFY a text DEFAULT NULL;
DROP TABLE t1;
CREATE TABLE t1 (
a varchar(2000) CHARACTER SET utf8 DEFAULT NULL,
KEY a_key (a(1))
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
INSERT INTO t1 VALUES ();
ALTER TABLE t1 MODIFY a text DEFAULT NULL;
DROP TABLE t1;
CREATE TABLE t1 (
a varchar(2000) CHARACTER SET utf8 DEFAULT NULL,
b int,
KEY a_key (b, a(1))
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
INSERT INTO t1 VALUES ();
ALTER TABLE t1 MODIFY a text DEFAULT NULL;
DROP TABLE t1;

View File

@ -0,0 +1,40 @@
include/master-slave.inc
[connection master]
#
# MDEV-21953: deadlock between BACKUP STAGE BLOCK_COMMIT and parallel
# replication
#
connection master;
CREATE TABLE t1 (a INT PRIMARY KEY) ENGINE = innodb;
connection slave;
include/stop_slave.inc
SET @old_parallel_threads= @@GLOBAL.slave_parallel_threads;
SET @old_parallel_mode = @@GLOBAL.slave_parallel_mode;
SET @@global.slave_parallel_threads= 2;
SET @@global.slave_parallel_mode = 'optimistic';
connection master;
INSERT INTO t1 VALUES (1);
INSERT INTO t1 VALUES (2);
connect aux_slave,127.0.0.1,root,,test,$SLAVE_MYPORT,;
BEGIN;
INSERT INTO t1 VALUES (1);
connection slave;
include/start_slave.inc
connection aux_slave;
connect backup_slave,127.0.0.1,root,,test,$SLAVE_MYPORT,;
BACKUP STAGE START;
BACKUP STAGE BLOCK_COMMIT;
connection aux_slave;
ROLLBACK;
connection backup_slave;
BACKUP STAGE END;
connection slave;
include/diff_tables.inc [master:t1,slave:t1]
connection slave;
include/stop_slave.inc
SET @@global.slave_parallel_threads= @old_parallel_threads;
SET @@global.slave_parallel_mode = @old_parallel_mode;
include/start_slave.inc
connection server_1;
DROP TABLE t1;
include/rpl_end.inc

View File

@ -0,0 +1,75 @@
--source include/have_innodb.inc
# The test is not format specific, MIXED is required to optimize testing time
--source include/have_binlog_format_mixed.inc
--source include/master-slave.inc
--echo #
--echo # MDEV-21953: deadlock between BACKUP STAGE BLOCK_COMMIT and parallel
--echo # replication
--echo #
--connection master
CREATE TABLE t1 (a INT PRIMARY KEY) ENGINE = innodb;
--sync_slave_with_master
--source include/stop_slave.inc
SET @old_parallel_threads= @@GLOBAL.slave_parallel_threads;
SET @old_parallel_mode = @@GLOBAL.slave_parallel_mode;
SET @@global.slave_parallel_threads= 2;
SET @@global.slave_parallel_mode = 'optimistic';
--connection master
INSERT INTO t1 VALUES (1);
INSERT INTO t1 VALUES (2);
--save_master_pos
# The plot:
# Block the 1st of two workers and, at waiting-for-prior-commit by the 2nd,
# issue BACKUP commands.
# BLOCK_COMMIT may hang so it is --send.
# Release the 1st worker to observe a deadlock unless its fixed.
--connect (aux_slave,127.0.0.1,root,,test,$SLAVE_MYPORT,)
BEGIN;
# block the 1st worker and wait for the 2nd ready to commit
INSERT INTO t1 VALUES (1);
--connection slave
--source include/start_slave.inc
--connection aux_slave
--let $wait_condition= SELECT COUNT(*) > 0 FROM information_schema.processlist WHERE state = "Waiting for prior transaction to commit"
--source include/wait_condition.inc
# While the 1st worker is locked out run backup
--connect (backup_slave,127.0.0.1,root,,test,$SLAVE_MYPORT,)
BACKUP STAGE START;
--send BACKUP STAGE BLOCK_COMMIT
# release the 1st work
--connection aux_slave
--sleep 1
ROLLBACK;
--connection backup_slave
--reap
BACKUP STAGE END;
--connection slave
--sync_with_master
--let $diff_tables= master:t1,slave:t1
--source include/diff_tables.inc
# Clean up.
--connection slave
--source include/stop_slave.inc
SET @@global.slave_parallel_threads= @old_parallel_threads;
SET @@global.slave_parallel_mode = @old_parallel_mode;
--source include/start_slave.inc
--connection server_1
DROP TABLE t1;
--source include/rpl_end.inc

View File

@ -0,0 +1,12 @@
#
# MDEV-16929 Assertion ... in close_thread_tables upon killing connection
# running SHOW on sequence
#
CREATE SEQUENCE s ENGINE=InnoDB;
RENAME TABLE s TO s1;
connect con1,localhost,root,,test;
SHOW CREATE SEQUENCE s1;
connection default;
KILL thread_id;
connection default;
drop sequence s1;

View File

@ -0,0 +1,20 @@
--source include/have_innodb.inc
--echo #
--echo # MDEV-16929 Assertion ... in close_thread_tables upon killing connection
--echo # running SHOW on sequence
--echo #
CREATE SEQUENCE s ENGINE=InnoDB;
RENAME TABLE s TO s1;
--connect (con1,localhost,root,,test)
--let $conid= `SELECT CONNECTION_ID()`
--send
SHOW CREATE SEQUENCE s1;
--connection default
--replace_result $conid thread_id
--eval KILL $conid
# Cleanup
--connection default
drop sequence s1;

View File

@ -319,3 +319,34 @@ create or replace table t1 (f point, key(f)) with system versioning engine=myisa
update t1 set f = null where f = 'foo'; update t1 set f = null where f = 'foo';
ERROR 22003: Cannot get geometry object from data you send to the GEOMETRY field ERROR 22003: Cannot get geometry object from data you send to the GEOMETRY field
drop table t1; drop table t1;
#
# MDEV-22061 InnoDB: Assertion of missing row in sec index row_start upon REPLACE on a system-versioned table
#
create or replace table t1 (
a int,
b int,
row_start bigint(20) unsigned generated always as row start,
row_end bigint(20) unsigned generated always as row end,
unique key (b,row_end),
key (row_start),
period for system_time (row_start,row_end)
) engine=innodb with system versioning;
insert into t1 (a, b) values (1, 2);
replace into t1 (a, b) values (3, 2);
replace into t1 (a, b) values (4, 2);
drop table t1;
#
# MDEV-20661 Virtual fields are not recalculated on system fields value assignment
#
create table t1 (
a int,
row_start SYS_DATATYPE as row start invisible,
row_end SYS_DATATYPE as row end invisible,
period for system_time (row_start, row_end),
v1 bigint unsigned as (a ^ row_start) unique,
v2 bigint unsigned as (a ^ row_end) unique
) engine=innodb with system versioning;
insert into t1 (a) values (1), (2);
update ignore t1 set a= 3;
delete history from t1;
drop table t1;

View File

@ -245,4 +245,45 @@ update t1 set f = null where f = 'foo';
# cleanup # cleanup
drop table t1; drop table t1;
--echo #
--echo # MDEV-22061 InnoDB: Assertion of missing row in sec index row_start upon REPLACE on a system-versioned table
--echo #
create or replace table t1 (
a int,
b int,
row_start bigint(20) unsigned generated always as row start,
row_end bigint(20) unsigned generated always as row end,
unique key (b,row_end),
key (row_start),
period for system_time (row_start,row_end)
) engine=innodb with system versioning;
insert into t1 (a, b) values (1, 2);
replace into t1 (a, b) values (3, 2);
replace into t1 (a, b) values (4, 2);
# cleanup
drop table t1;
--echo #
--echo # MDEV-20661 Virtual fields are not recalculated on system fields value assignment
--echo #
replace_result $sys_datatype_expl SYS_DATATYPE;
eval create table t1 (
a int,
row_start $sys_datatype_expl as row start invisible,
row_end $sys_datatype_expl as row end invisible,
period for system_time (row_start, row_end),
v1 bigint unsigned as (a ^ row_start) unique,
v2 bigint unsigned as (a ^ row_end) unique
) engine=innodb with system versioning;
insert into t1 (a) values (1), (2);
update ignore t1 set a= 3;
delete history from t1;
# cleanup
drop table t1;
source suite/versioning/common_finish.inc; source suite/versioning/common_finish.inc;

View File

@ -188,7 +188,7 @@ private:
static int commit_one_phase_2(THD *thd, bool all, THD_TRANS *trans, static int commit_one_phase_2(THD *thd, bool all, THD_TRANS *trans,
bool is_real_trans); bool is_real_trans, bool rw_trans);
static plugin_ref ha_default_plugin(THD *thd) static plugin_ref ha_default_plugin(THD *thd)
@ -1621,39 +1621,9 @@ int ha_commit_trans(THD *thd, bool all)
/* rw_trans is TRUE when we in a transaction changing data */ /* rw_trans is TRUE when we in a transaction changing data */
bool rw_trans= is_real_trans && bool rw_trans= is_real_trans &&
(rw_ha_count > (thd->is_current_stmt_binlog_disabled()?0U:1U)); (rw_ha_count > (thd->is_current_stmt_binlog_disabled()?0U:1U));
MDL_request mdl_request;
mdl_request.ticket= 0;
DBUG_PRINT("info", ("is_real_trans: %d rw_trans: %d rw_ha_count: %d", DBUG_PRINT("info", ("is_real_trans: %d rw_trans: %d rw_ha_count: %d",
is_real_trans, rw_trans, rw_ha_count)); is_real_trans, rw_trans, rw_ha_count));
/*
We need to test maria_hton because of plugin_innodb.test that changes
the plugin table to innodb and thus plugin_load will call
mysql_close_tables() which calls trans_commit_trans() with maria_hton = 0
*/
if (rw_trans)
{
/*
Acquire a metadata lock which will ensure that COMMIT is blocked
by an active FLUSH TABLES WITH READ LOCK (and vice versa:
COMMIT in progress blocks FTWRL).
We allow the owner of FTWRL to COMMIT; we assume that it knows
what it does.
*/
MDL_REQUEST_INIT(&mdl_request, MDL_key::BACKUP, "", "", MDL_BACKUP_COMMIT,
MDL_EXPLICIT);
if (!WSREP(thd) &&
thd->mdl_context.acquire_lock(&mdl_request,
thd->variables.lock_wait_timeout))
{
ha_rollback_trans(thd, all);
DBUG_RETURN(1);
}
DEBUG_SYNC(thd, "ha_commit_trans_after_acquire_commit_lock");
}
if (rw_trans && if (rw_trans &&
opt_readonly && opt_readonly &&
!(thd->security_ctx->master_access & PRIV_IGNORE_READ_ONLY) && !(thd->security_ctx->master_access & PRIV_IGNORE_READ_ONLY) &&
@ -1693,7 +1663,7 @@ int ha_commit_trans(THD *thd, bool all)
// Here, the call will not commit inside InnoDB. It is only working // Here, the call will not commit inside InnoDB. It is only working
// around closing thd->transaction.stmt open by TR_table::open(). // around closing thd->transaction.stmt open by TR_table::open().
if (all) if (all)
commit_one_phase_2(thd, false, &thd->transaction->stmt, false); commit_one_phase_2(thd, false, &thd->transaction->stmt, false, false);
} }
} }
#endif #endif
@ -1713,7 +1683,7 @@ int ha_commit_trans(THD *thd, bool all)
goto wsrep_err; goto wsrep_err;
} }
#endif /* WITH_WSREP */ #endif /* WITH_WSREP */
error= ha_commit_one_phase(thd, all); error= ha_commit_one_phase(thd, all, rw_trans);
#ifdef WITH_WSREP #ifdef WITH_WSREP
// Here in case of error we must return 2 for inconsistency // Here in case of error we must return 2 for inconsistency
if (run_wsrep_hooks && !error) if (run_wsrep_hooks && !error)
@ -1750,7 +1720,7 @@ int ha_commit_trans(THD *thd, bool all)
if (!is_real_trans) if (!is_real_trans)
{ {
error= commit_one_phase_2(thd, all, trans, is_real_trans); error= commit_one_phase_2(thd, all, trans, is_real_trans, rw_trans);
goto done; goto done;
} }
@ -1784,7 +1754,7 @@ int ha_commit_trans(THD *thd, bool all)
DEBUG_SYNC(thd, "ha_commit_trans_after_log_and_order"); DEBUG_SYNC(thd, "ha_commit_trans_after_log_and_order");
DBUG_EXECUTE_IF("crash_commit_after_log", DBUG_SUICIDE();); DBUG_EXECUTE_IF("crash_commit_after_log", DBUG_SUICIDE(););
error= commit_one_phase_2(thd, all, trans, is_real_trans) ? 2 : 0; error= commit_one_phase_2(thd, all, trans, is_real_trans, rw_trans) ? 2 : 0;
#ifdef WITH_WSREP #ifdef WITH_WSREP
if (run_wsrep_hooks && if (run_wsrep_hooks &&
(error || (error = wsrep_after_commit(thd, all)))) (error || (error = wsrep_after_commit(thd, all))))
@ -1854,16 +1824,6 @@ err:
thd->rgi_slave->is_parallel_exec); thd->rgi_slave->is_parallel_exec);
} }
end: end:
if (mdl_request.ticket)
{
/*
We do not always immediately release transactional locks
after ha_commit_trans() (see uses of ha_enable_transaction()),
thus we release the commit blocker lock as soon as it's
not needed.
*/
thd->mdl_context.release_lock(mdl_request.ticket);
}
#ifdef WITH_WSREP #ifdef WITH_WSREP
if (wsrep_is_active(thd) && is_real_trans && !error && if (wsrep_is_active(thd) && is_real_trans && !error &&
(rw_ha_count == 0 || all) && (rw_ha_count == 0 || all) &&
@ -1879,6 +1839,7 @@ end:
/** /**
@note @note
This function does not care about global read lock. A caller should. This function does not care about global read lock. A caller should.
However backup locks are handled in commit_one_phase_2.
@param[in] all Is set in case of explicit commit @param[in] all Is set in case of explicit commit
(COMMIT statement), or implicit commit (COMMIT statement), or implicit commit
@ -1887,7 +1848,7 @@ end:
autocommit=1. autocommit=1.
*/ */
int ha_commit_one_phase(THD *thd, bool all) int ha_commit_one_phase(THD *thd, bool all, bool rw_trans)
{ {
THD_TRANS *trans=all ? &thd->transaction->all : &thd->transaction->stmt; THD_TRANS *trans=all ? &thd->transaction->all : &thd->transaction->stmt;
/* /*
@ -1913,20 +1874,48 @@ int ha_commit_one_phase(THD *thd, bool all)
if ((res= thd->wait_for_prior_commit())) if ((res= thd->wait_for_prior_commit()))
DBUG_RETURN(res); DBUG_RETURN(res);
} }
res= commit_one_phase_2(thd, all, trans, is_real_trans); res= commit_one_phase_2(thd, all, trans, is_real_trans, rw_trans);
DBUG_RETURN(res); DBUG_RETURN(res);
} }
static int static int
commit_one_phase_2(THD *thd, bool all, THD_TRANS *trans, bool is_real_trans) commit_one_phase_2(THD *thd, bool all, THD_TRANS *trans, bool is_real_trans,
bool rw_trans)
{ {
int error= 0; int error= 0;
uint count= 0; uint count= 0;
Ha_trx_info *ha_info= trans->ha_list, *ha_info_next; Ha_trx_info *ha_info= trans->ha_list, *ha_info_next;
MDL_request mdl_request;
mdl_request.ticket= 0;
DBUG_ENTER("commit_one_phase_2"); DBUG_ENTER("commit_one_phase_2");
if (is_real_trans) if (is_real_trans)
DEBUG_SYNC(thd, "commit_one_phase_2"); DEBUG_SYNC(thd, "commit_one_phase_2");
if (rw_trans)
{
/*
Acquire a metadata lock which will ensure that COMMIT is blocked
by an active FLUSH TABLES WITH READ LOCK (and vice versa:
COMMIT in progress blocks FTWRL).
We allow the owner of FTWRL to COMMIT; we assume that it knows
what it does.
*/
MDL_REQUEST_INIT(&mdl_request, MDL_key::BACKUP, "", "", MDL_BACKUP_COMMIT,
MDL_EXPLICIT);
if (!WSREP(thd) &&
thd->mdl_context.acquire_lock(&mdl_request,
thd->variables.lock_wait_timeout))
{
my_error(ER_ERROR_DURING_COMMIT, MYF(0), 1);
ha_rollback_trans(thd, all);
DBUG_RETURN(1);
}
DEBUG_SYNC(thd, "ha_commit_trans_after_acquire_commit_lock");
}
if (ha_info) if (ha_info)
{ {
for (; ha_info; ha_info= ha_info_next) for (; ha_info; ha_info= ha_info_next)
@ -1955,6 +1944,17 @@ commit_one_phase_2(THD *thd, bool all, THD_TRANS *trans, bool is_real_trans)
#endif #endif
} }
} }
if (mdl_request.ticket)
{
/*
We do not always immediately release transactional locks
after ha_commit_trans() (see uses of ha_enable_transaction()),
thus we release the commit blocker lock as soon as it's
not needed.
*/
thd->mdl_context.release_lock(mdl_request.ticket);
}
/* Free resources and perform other cleanup even for 'empty' transactions. */ /* Free resources and perform other cleanup even for 'empty' transactions. */
if (is_real_trans) if (is_real_trans)
{ {

View File

@ -5176,7 +5176,7 @@ int ha_change_key_cache(KEY_CACHE *old_key_cache, KEY_CACHE *new_key_cache);
/* transactions: interface to handlerton functions */ /* transactions: interface to handlerton functions */
int ha_start_consistent_snapshot(THD *thd); int ha_start_consistent_snapshot(THD *thd);
int ha_commit_or_rollback_by_xid(XID *xid, bool commit); int ha_commit_or_rollback_by_xid(XID *xid, bool commit);
int ha_commit_one_phase(THD *thd, bool all); int ha_commit_one_phase(THD *thd, bool all, bool rw_trans);
int ha_commit_trans(THD *thd, bool all); int ha_commit_trans(THD *thd, bool all);
int ha_rollback_trans(THD *thd, bool all); int ha_rollback_trans(THD *thd, bool all);
int ha_prepare(THD *thd); int ha_prepare(THD *thd);

View File

@ -124,6 +124,8 @@ public:
*/ */
enum enum_mdl_type { enum enum_mdl_type {
/* This means that the MDL_request is not initialized */
MDL_NOT_INITIALIZED= -1,
/* /*
An intention exclusive metadata lock (IX). Used only for scoped locks. An intention exclusive metadata lock (IX). Used only for scoped locks.
Owner of this type of lock can acquire upgradable exclusive locks on Owner of this type of lock can acquire upgradable exclusive locks on
@ -599,12 +601,13 @@ public:
*/ */
MDL_request& operator=(const MDL_request &) MDL_request& operator=(const MDL_request &)
{ {
type= MDL_NOT_INITIALIZED;
ticket= NULL; ticket= NULL;
/* Do nothing, in particular, don't try to copy the key. */ /* Do nothing, in particular, don't try to copy the key. */
return *this; return *this;
} }
/* Another piece of ugliness for TABLE_LIST constructor */ /* Another piece of ugliness for TABLE_LIST constructor */
MDL_request() {} MDL_request(): type(MDL_NOT_INITIALIZED), ticket(NULL) {}
MDL_request(const MDL_request *rhs) MDL_request(const MDL_request *rhs)
:type(rhs->type), :type(rhs->type),

View File

@ -1053,10 +1053,10 @@ handle_rpl_parallel_thread(void *arg)
server_threads.insert(thd); server_threads.insert(thd);
set_current_thd(thd); set_current_thd(thd);
pthread_detach_this_thread(); pthread_detach_this_thread();
thd->store_globals();
thd->init_for_queries(); thd->init_for_queries();
thd->variables.binlog_annotate_row_events= 0; thd->variables.binlog_annotate_row_events= 0;
init_thr_lock(); init_thr_lock();
thd->store_globals();
thd->system_thread= SYSTEM_THREAD_SLAVE_SQL; thd->system_thread= SYSTEM_THREAD_SLAVE_SQL;
thd->security_ctx->skip_grants(); thd->security_ctx->skip_grants();
thd->variables.max_allowed_packet= slave_max_allowed_packet; thd->variables.max_allowed_packet= slave_max_allowed_packet;

View File

@ -1717,6 +1717,10 @@ int vers_insert_history_row(TABLE *table)
if (row_start->cmp(row_start->ptr, row_end->ptr) >= 0) if (row_start->cmp(row_start->ptr, row_end->ptr) >= 0)
return 0; return 0;
if (table->vfield &&
table->update_virtual_fields(table->file, VCOL_UPDATE_FOR_READ))
return HA_ERR_GENERIC;
return table->file->ha_write_row(table->record[0]); return table->file->ha_write_row(table->record[0]);
} }

View File

@ -1,5 +1,6 @@
/* /*
Copyright (c) 2017, MariaDB Corporation, Alibaba Corporation Copyright (c) 2017, MariaDB Corporation, Alibaba Corporation
Copyrgiht (c) 2020, MariaDB Corporation.
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -483,6 +484,10 @@ int SEQUENCE::read_initial_values(TABLE *table)
if (mdl_lock_used) if (mdl_lock_used)
thd->mdl_context.release_lock(mdl_request.ticket); thd->mdl_context.release_lock(mdl_request.ticket);
write_unlock(table); write_unlock(table);
if (!has_active_transaction && !thd->transaction->stmt.is_empty() &&
!thd->in_sub_stmt)
trans_commit_stmt(thd);
DBUG_RETURN(HA_ERR_LOCK_WAIT_TIMEOUT); DBUG_RETURN(HA_ERR_LOCK_WAIT_TIMEOUT);
} }
DBUG_ASSERT(table->reginfo.lock_type == TL_READ); DBUG_ASSERT(table->reginfo.lock_type == TL_READ);

View File

@ -8809,29 +8809,24 @@ void TABLE::vers_update_fields()
bitmap_set_bit(write_set, vers_start_field()->field_index); bitmap_set_bit(write_set, vers_start_field()->field_index);
bitmap_set_bit(write_set, vers_end_field()->field_index); bitmap_set_bit(write_set, vers_end_field()->field_index);
if (versioned(VERS_TIMESTAMP)) if (!vers_write)
{ {
if (!vers_write) file->column_bitmaps_signal();
{ return;
file->column_bitmaps_signal();
return;
}
if (vers_start_field()->store_timestamp(in_use->query_start(),
in_use->query_start_sec_part()))
DBUG_ASSERT(0);
} }
else
if (versioned(VERS_TIMESTAMP) &&
vers_start_field()->store_timestamp(in_use->query_start(),
in_use->query_start_sec_part()))
{ {
if (!vers_write) DBUG_ASSERT(0);
{
file->column_bitmaps_signal();
return;
}
} }
vers_end_field()->set_max(); vers_end_field()->set_max();
bitmap_set_bit(read_set, vers_end_field()->field_index); bitmap_set_bit(read_set, vers_end_field()->field_index);
file->column_bitmaps_signal(); file->column_bitmaps_signal();
if (vfield)
update_virtual_fields(file, VCOL_UPDATE_FOR_READ);
} }

View File

@ -689,7 +689,8 @@ bool trans_xa_commit(THD *thd)
{ {
DEBUG_SYNC(thd, "trans_xa_commit_after_acquire_commit_lock"); DEBUG_SYNC(thd, "trans_xa_commit_after_acquire_commit_lock");
if ((res= MY_TEST(ha_commit_one_phase(thd, 1)))) res= MY_TEST(ha_commit_one_phase(thd, 1, 1));
if (res)
my_error(ER_XAER_RMERR, MYF(0)); my_error(ER_XAER_RMERR, MYF(0));
else else
{ {

View File

@ -21150,39 +21150,49 @@ ha_innobase::can_convert_varstring(const Field_varstring* field,
return true; return true;
} }
bool static bool is_part_of_a_key(const Field_blob *field)
ha_innobase::can_convert_blob(const Field_blob* field,
const Column_definition& new_type) const
{ {
if (new_type.type_handler() != field->type_handler()) { const TABLE_SHARE *s= field->table->s;
return false;
}
if (!new_type.compression_method() != !field->compression_method()) { for (uint i= 0; i < s->keys; i++)
return false; {
} const KEY &key= s->key_info[i];
for (uint j= 0; j < key.user_defined_key_parts; j++)
{
const KEY_PART_INFO &info= key.key_part[j];
if (info.field->field_index == field->field_index)
return true;
}
}
if (new_type.pack_length != field->pack_length()) { return false;
return false; }
}
if (new_type.charset != field->charset()) { bool ha_innobase::can_convert_blob(const Field_blob *field,
Charset field_cs(field->charset()); const Column_definition &new_type) const
if (!field_cs.encoding_allows_reinterpret_as( {
new_type.charset)) { if (new_type.type_handler() != field->type_handler())
return false; return false;
}
if (!field_cs.eq_collation_specific_names(new_type.charset)) { if (!new_type.compression_method() != !field->compression_method())
bool is_part_of_a_key return false;
= !field->part_of_key.is_clear_all();
return !is_part_of_a_key;
}
return true; if (new_type.pack_length != field->pack_length())
} return false;
return true; if (new_type.charset != field->charset())
{
Charset field_cs(field->charset());
if (!field_cs.encoding_allows_reinterpret_as(new_type.charset))
return false;
if (!field_cs.eq_collation_specific_names(new_type.charset))
return !is_part_of_a_key(field);
return true;
}
return true;
} }
Compare_keys ha_innobase::compare_key_parts( Compare_keys ha_innobase::compare_key_parts(
@ -21808,3 +21818,70 @@ ib_push_frm_error(
break; break;
} }
} }
/** Writes 8 bytes to nth tuple field
@param[in] tuple where to write
@param[in] nth index in tuple
@param[in] data what to write
@param[in] buf field data buffer */
static void set_tuple_col_8(dtuple_t *tuple, int col, uint64_t data, byte *buf)
{
dfield_t *dfield= dtuple_get_nth_field(tuple, col);
ut_ad(dfield->type.len == 8);
if (dfield->len == UNIV_SQL_NULL)
{
dfield_set_data(dfield, buf, 8);
}
ut_ad(dfield->len == dfield->type.len && dfield->data);
mach_write_to_8(dfield->data, data);
}
void ins_node_t::vers_update_end(row_prebuilt_t *prebuilt, bool history_row)
{
ut_ad(prebuilt->ins_node == this);
trx_t *trx= prebuilt->trx;
#ifndef DBUG_OFF
ut_ad(table->vers_start != table->vers_end);
const mysql_row_templ_t *t= prebuilt->get_template_by_col(table->vers_end);
ut_ad(t);
ut_ad(t->mysql_col_len == 8);
#endif
if (history_row)
{
set_tuple_col_8(row, table->vers_end, trx->id, vers_end_buf);
}
else /* ROW_INS_VERSIONED */
{
set_tuple_col_8(row, table->vers_end, TRX_ID_MAX, vers_end_buf);
#ifndef DBUG_OFF
t= prebuilt->get_template_by_col(table->vers_start);
ut_ad(t);
ut_ad(t->mysql_col_len == 8);
#endif
set_tuple_col_8(row, table->vers_start, trx->id, vers_start_buf);
}
dict_index_t *clust_index= dict_table_get_first_index(table);
THD *thd= trx->mysql_thd;
TABLE *mysql_table= prebuilt->m_mysql_table;
mem_heap_t *local_heap= NULL;
for (ulint col_no= 0; col_no < dict_table_get_n_v_cols(table); col_no++)
{
const dict_v_col_t *v_col= dict_table_get_nth_v_col(table, col_no);
for (ulint i= 0; i < unsigned(v_col->num_base); i++)
{
dict_col_t *base_col= v_col->base_col[i];
if (base_col->ind == table->vers_end)
{
innobase_get_computed_value(row, v_col, clust_index, &local_heap,
table->heap, NULL, thd, mysql_table,
mysql_table->record[0], NULL, NULL, NULL);
}
}
}
if (local_heap)
{
mem_heap_free(local_heap);
}
}

View File

@ -163,6 +163,8 @@ row_ins_step(
#define INS_NODE_INSERT_ENTRIES 3 /* index entries should be built and #define INS_NODE_INSERT_ENTRIES 3 /* index entries should be built and
inserted */ inserted */
struct row_prebuilt_t;
/** Insert node structure */ /** Insert node structure */
struct ins_node_t struct ins_node_t
{ {
@ -203,6 +205,7 @@ struct ins_node_t
entry_list and sys fields are stored here; entry_list and sys fields are stored here;
if this is NULL, entry list should be created if this is NULL, entry list should be created
and buffers for sys fields in row allocated */ and buffers for sys fields in row allocated */
void vers_update_end(row_prebuilt_t *prebuilt, bool history_row);
}; };
/** Create an insert object. /** Create an insert object.

View File

@ -354,7 +354,32 @@ struct upd_t{
fields[n_fields++] = field; fields[n_fields++] = field;
} }
/** Determine if the given field_no is modified. void remove_element(ulint i)
{
ut_ad(n_fields > 0);
ut_ad(i < n_fields);
while (i < n_fields - 1)
{
fields[i]= fields[i + 1];
i++;
}
n_fields--;
}
bool remove(const ulint field_no)
{
for (ulint i= 0; i < n_fields; ++i)
{
if (field_no == fields[i].field_no)
{
remove_element(i);
return true;
}
}
return false;
}
/** Determine if the given field_no is modified.
@return true if modified, false otherwise. */ @return true if modified, false otherwise. */
bool is_modified(uint16_t field_no) const bool is_modified(uint16_t field_no) const
{ {
@ -494,25 +519,25 @@ private:
make_versioned_delete(). make_versioned_delete().
@param[in] trx transaction @param[in] trx transaction
@param[in] vers_sys_idx table->row_start or table->row_end */ @param[in] vers_sys_idx table->row_start or table->row_end */
void make_versioned_helper(const trx_t* trx, ulint idx); void vers_update_fields(const trx_t *trx, ulint idx);
public: public:
/** Also set row_start = CURRENT_TIMESTAMP/trx->id /** Also set row_start = CURRENT_TIMESTAMP/trx->id
@param[in] trx transaction */ @param[in] trx transaction */
void make_versioned_update(const trx_t* trx) void vers_make_update(const trx_t *trx)
{ {
make_versioned_helper(trx, table->vers_start); vers_update_fields(trx, table->vers_start);
} }
/** Only set row_end = CURRENT_TIMESTAMP/trx->id. /** Only set row_end = CURRENT_TIMESTAMP/trx->id.
Do not touch other fields at all. Do not touch other fields at all.
@param[in] trx transaction */ @param[in] trx transaction */
void make_versioned_delete(const trx_t* trx) void vers_make_delete(const trx_t *trx)
{ {
update->n_fields = 0; update->n_fields = 0;
is_delete = VERSIONED_DELETE; is_delete = VERSIONED_DELETE;
make_versioned_helper(trx, table->vers_end); vers_update_fields(trx, table->vers_end);
} }
}; };
#define UPD_NODE_MAGIC_N 1579975 #define UPD_NODE_MAGIC_N 1579975

View File

@ -1331,23 +1331,6 @@ row_mysql_get_table_status(
return(err); return(err);
} }
/** Writes 8 bytes to nth tuple field
@param[in] tuple where to write
@param[in] nth index in tuple
@param[in] data what to write
@param[in] buf field data buffer */
static
void
set_tuple_col_8(dtuple_t* tuple, int col, uint64_t data, byte* buf) {
dfield_t* dfield = dtuple_get_nth_field(tuple, col);
ut_ad(dfield->type.len == 8);
if (dfield->len == UNIV_SQL_NULL) {
dfield_set_data(dfield, buf, 8);
}
ut_ad(dfield->len == dfield->type.len && dfield->data);
mach_write_to_8(dfield->data, data);
}
/** Does an insert for MySQL. /** Does an insert for MySQL.
@param[in] mysql_rec row in the MySQL format @param[in] mysql_rec row in the MySQL format
@param[in,out] prebuilt prebuilt struct in MySQL handle @param[in,out] prebuilt prebuilt struct in MySQL handle
@ -1415,29 +1398,8 @@ row_insert_for_mysql(
&blob_heap); &blob_heap);
if (ins_mode != ROW_INS_NORMAL) { if (ins_mode != ROW_INS_NORMAL) {
#ifndef DBUG_OFF node->vers_update_end(prebuilt, ins_mode == ROW_INS_HISTORICAL);
ut_ad(table->vers_start != table->vers_end); }
const mysql_row_templ_t* t
= prebuilt->get_template_by_col(table->vers_end);
ut_ad(t);
ut_ad(t->mysql_col_len == 8);
#endif
if (ins_mode == ROW_INS_HISTORICAL) {
set_tuple_col_8(node->row, table->vers_end, trx->id,
node->vers_end_buf);
} else /* ROW_INS_VERSIONED */ {
set_tuple_col_8(node->row, table->vers_end, TRX_ID_MAX,
node->vers_end_buf);
#ifndef DBUG_OFF
t = prebuilt->get_template_by_col(table->vers_start);
ut_ad(t);
ut_ad(t->mysql_col_len == 8);
#endif
set_tuple_col_8(node->row, table->vers_start, trx->id,
node->vers_start_buf);
}
}
savept = trx_savept_take(trx); savept = trx_savept_take(trx);
@ -1871,10 +1833,10 @@ row_update_for_mysql(row_prebuilt_t* prebuilt)
if (prebuilt->versioned_write) { if (prebuilt->versioned_write) {
if (node->is_delete == VERSIONED_DELETE) { if (node->is_delete == VERSIONED_DELETE) {
node->make_versioned_delete(trx); node->vers_make_delete(trx);
} else if (node->update->affects_versioned()) { } else if (node->update->affects_versioned()) {
node->make_versioned_update(trx); node->vers_make_update(trx);
} }
} }
for (;;) { for (;;) {
@ -2230,14 +2192,14 @@ row_update_cascade_for_mysql(
if (table->versioned()) { if (table->versioned()) {
if (node->is_delete == PLAIN_DELETE) { if (node->is_delete == PLAIN_DELETE) {
node->make_versioned_delete(trx); node->vers_make_delete(trx);
} else if (node->update->affects_versioned()) { } else if (node->update->affects_versioned()) {
dberr_t err = row_update_vers_insert(thr, node); dberr_t err = row_update_vers_insert(thr, node);
if (err != DB_SUCCESS) { if (err != DB_SUCCESS) {
return err; return err;
} }
node->make_versioned_update(trx); node->vers_make_update(trx);
} }
} }
for (;;) { for (;;) {

View File

@ -3173,34 +3173,59 @@ Supposed to be called only by make_versioned_update() and
make_versioned_delete(). make_versioned_delete().
@param[in] trx transaction @param[in] trx transaction
@param[in] vers_sys_idx table->row_start or table->row_end */ @param[in] vers_sys_idx table->row_start or table->row_end */
void upd_node_t::make_versioned_helper(const trx_t* trx, ulint idx) void upd_node_t::vers_update_fields(const trx_t *trx, ulint idx)
{ {
ut_ad(in_mysql_interface); // otherwise needs to recalculate ut_ad(in_mysql_interface); // otherwise needs to recalculate node->cmpl_info
// node->cmpl_info ut_ad(idx == table->vers_start || idx == table->vers_end);
ut_ad(idx == table->vers_start || idx == table->vers_end);
dict_index_t* clust_index = dict_table_get_first_index(table); dict_index_t *clust_index= dict_table_get_first_index(table);
const dict_col_t *col= dict_table_get_nth_col(table, idx);
ulint field_no= dict_col_get_clust_pos(col, clust_index);
upd_field_t *ufield;
/* row_create_update_node_for_mysql() pre-allocated this much. for (ulint i= 0; i < update->n_fields; ++i)
At least one PK column always remains unchanged. */ {
ut_ad(update->n_fields < ulint(table->n_cols + table->n_v_cols)); if (update->fields[i].field_no == field_no)
{
ufield= &update->fields[i];
goto skip_append;
}
}
update->n_fields++; /* row_create_update_node_for_mysql() pre-allocated this much.
upd_field_t* ufield = upd_get_nth_field(update, update->n_fields - 1); At least one PK column always remains unchanged. */
const dict_col_t* col = dict_table_get_nth_col(table, idx); ut_ad(update->n_fields < ulint(table->n_cols + table->n_v_cols));
upd_field_set_field_no(ufield, static_cast<uint16_t>( update->n_fields++;
dict_col_get_clust_pos( ufield= upd_get_nth_field(update, update->n_fields - 1);
col, clust_index)), upd_field_set_field_no(ufield, static_cast<uint16_t>(field_no), clust_index);
clust_index);
char* where = reinterpret_cast<char*>(update->vers_sys_value); skip_append:
if (col->vers_native()) { char *where= reinterpret_cast<char *>(update->vers_sys_value);
mach_write_to_8(where, trx->id); if (col->vers_native())
} else { mach_write_to_8(where, trx->id);
thd_get_query_start_data(trx->mysql_thd, where); else
} thd_get_query_start_data(trx->mysql_thd, where);
dfield_set_data(&ufield->new_val, update->vers_sys_value, col->len); dfield_set_data(&ufield->new_val, update->vers_sys_value, col->len);
for (ulint col_no= 0; col_no < dict_table_get_n_v_cols(table); col_no++)
{
const dict_v_col_t *v_col= dict_table_get_nth_v_col(table, col_no);
if (!v_col->m_col.ord_part)
continue;
for (ulint i= 0; i < unsigned(v_col->num_base); i++)
{
dict_col_t *base_col= v_col->base_col[i];
if (base_col->ind == col->ind)
{
/* Virtual column depends on system field value
which we updated above. Remove it from update
vector, so it is recalculated in
row_upd_store_v_row() (see !update branch). */
update->remove(v_col->v_pos);
break;
}
}
}
} }

View File

@ -1272,7 +1272,8 @@ trx_update_mod_tables_timestamp(
dict_table_t* table = it->first; dict_table_t* table = it->first;
table->update_time = now; table->update_time = now;
#ifdef UNIV_DEBUG #ifdef UNIV_DEBUG
if (preserve_tables || table->get_ref_count()) { if (preserve_tables || table->get_ref_count()
|| UT_LIST_GET_LEN(table->locks)) {
/* do not evict when committing DDL operations /* do not evict when committing DDL operations
or if some other transaction is holding the or if some other transaction is holding the
table handle */ table handle */
@ -1281,7 +1282,11 @@ trx_update_mod_tables_timestamp(
/* recheck while holding the mutex that blocks /* recheck while holding the mutex that blocks
table->acquire() */ table->acquire() */
mutex_enter(&dict_sys.mutex); mutex_enter(&dict_sys.mutex);
if (!table->get_ref_count()) { mutex_enter(&lock_sys.mutex);
const bool do_evict = !table->get_ref_count()
&& !UT_LIST_GET_LEN(table->locks);
mutex_exit(&lock_sys.mutex);
if (do_evict) {
dict_sys.remove(table, true); dict_sys.remove(table, true);
} }
mutex_exit(&dict_sys.mutex); mutex_exit(&dict_sys.mutex);

View File

@ -2947,6 +2947,10 @@ static void reset_thd_trn(THD *thd, MARIA_HA *first_table)
DBUG_VOID_RETURN; DBUG_VOID_RETURN;
} }
bool ha_maria::has_active_transaction(THD *thd)
{
return (maria_hton && THD_TRN);
}
/** /**
Performs an implicit commit of the Maria transaction and creates a new Performs an implicit commit of the Maria transaction and creates a new

View File

@ -158,6 +158,7 @@ public:
{ {
return file; return file;
} }
static bool has_active_transaction(THD *thd);
static int implicit_commit(THD *thd, bool new_trn); static int implicit_commit(THD *thd, bool new_trn);
/** /**
* Multi Range Read interface * Multi Range Read interface