mirror of
https://github.com/MariaDB/server.git
synced 2026-01-06 05:22:24 +03:00
Merge various replication-related patches into MariaDB 5.3:
- MWL#116 Group commit - MWL#136 Enhancements for START TRANSACTION WITH CONSISTENT SNAPSHOT - MWL#47 Annotate_rows_log_event - MWL#163 innodb_release_locks_early - Percona patch enhancing row-based replication for tables with no primary key
This commit is contained in:
223
sql/handler.cc
223
sql/handler.cc
@@ -79,6 +79,8 @@ TYPELIB tx_isolation_typelib= {array_elements(tx_isolation_names)-1,"",
|
||||
static TYPELIB known_extensions= {0,"known_exts", NULL, NULL};
|
||||
uint known_extensions_id= 0;
|
||||
|
||||
static int commit_one_phase_2(THD *thd, bool all, THD_TRANS *trans,
|
||||
bool is_real_trans);
|
||||
|
||||
|
||||
static plugin_ref ha_default_plugin(THD *thd)
|
||||
@@ -1077,7 +1079,7 @@ ha_check_and_coalesce_trx_read_only(THD *thd, Ha_trx_info *ha_list,
|
||||
*/
|
||||
int ha_commit_trans(THD *thd, bool all)
|
||||
{
|
||||
int error= 0, cookie= 0;
|
||||
int error= 0, cookie;
|
||||
/*
|
||||
'all' means that this is either an explicit commit issued by
|
||||
user, or an implicit commit issued by a DDL.
|
||||
@@ -1092,7 +1094,8 @@ int ha_commit_trans(THD *thd, bool all)
|
||||
*/
|
||||
bool is_real_trans= all || thd->transaction.all.ha_list == 0;
|
||||
Ha_trx_info *ha_info= trans->ha_list;
|
||||
my_xid xid= thd->transaction.xid_state.xid.get_my_xid();
|
||||
bool need_prepare_ordered, need_commit_ordered;
|
||||
my_xid xid;
|
||||
DBUG_ENTER("ha_commit_trans");
|
||||
|
||||
/* Just a random warning to test warnings pushed during autocommit. */
|
||||
@@ -1131,89 +1134,114 @@ int ha_commit_trans(THD *thd, bool all)
|
||||
DBUG_RETURN(2);
|
||||
}
|
||||
#ifdef USING_TRANSACTIONS
|
||||
if (ha_info)
|
||||
if (!ha_info)
|
||||
{
|
||||
uint rw_ha_count;
|
||||
bool rw_trans;
|
||||
|
||||
DBUG_EXECUTE_IF("crash_commit_before", DBUG_SUICIDE(););
|
||||
|
||||
/* Close all cursors that can not survive COMMIT */
|
||||
if (is_real_trans) /* not a statement commit */
|
||||
thd->stmt_map.close_transient_cursors();
|
||||
|
||||
rw_ha_count= ha_check_and_coalesce_trx_read_only(thd, ha_info, all);
|
||||
/* rw_trans is TRUE when we in a transaction changing data */
|
||||
rw_trans= is_real_trans && (rw_ha_count > 0);
|
||||
|
||||
if (rw_trans &&
|
||||
wait_if_global_read_lock(thd, 0, 0))
|
||||
{
|
||||
ha_rollback_trans(thd, all);
|
||||
DBUG_RETURN(1);
|
||||
}
|
||||
|
||||
if (rw_trans &&
|
||||
opt_readonly &&
|
||||
!(thd->security_ctx->master_access & SUPER_ACL) &&
|
||||
!thd->slave_thread)
|
||||
{
|
||||
my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--read-only");
|
||||
ha_rollback_trans(thd, all);
|
||||
error= 1;
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (!trans->no_2pc && (rw_ha_count > 1))
|
||||
{
|
||||
for (; ha_info && !error; ha_info= ha_info->next())
|
||||
{
|
||||
int err;
|
||||
handlerton *ht= ha_info->ht();
|
||||
/*
|
||||
Do not call two-phase commit if this particular
|
||||
transaction is read-only. This allows for simpler
|
||||
implementation in engines that are always read-only.
|
||||
*/
|
||||
if (! ha_info->is_trx_read_write())
|
||||
continue;
|
||||
/*
|
||||
Sic: we know that prepare() is not NULL since otherwise
|
||||
trans->no_2pc would have been set.
|
||||
*/
|
||||
if ((err= ht->prepare(ht, thd, all)))
|
||||
{
|
||||
my_error(ER_ERROR_DURING_COMMIT, MYF(0), err);
|
||||
error= 1;
|
||||
}
|
||||
status_var_increment(thd->status_var.ha_prepare_count);
|
||||
}
|
||||
DBUG_EXECUTE_IF("crash_commit_after_prepare", DBUG_SUICIDE(););
|
||||
if (error || (is_real_trans && xid &&
|
||||
(error= !(cookie= tc_log->log_xid(thd, xid)))))
|
||||
{
|
||||
ha_rollback_trans(thd, all);
|
||||
error= 1;
|
||||
goto end;
|
||||
}
|
||||
DBUG_EXECUTE_IF("crash_commit_after_log", DBUG_SUICIDE(););
|
||||
}
|
||||
error=ha_commit_one_phase(thd, all) ? (cookie ? 2 : 1) : 0;
|
||||
DBUG_EXECUTE_IF("crash_commit_before_unlog", DBUG_SUICIDE(););
|
||||
if (cookie)
|
||||
if(tc_log->unlog(cookie, xid))
|
||||
{
|
||||
error= 2;
|
||||
goto end;
|
||||
}
|
||||
DBUG_EXECUTE_IF("crash_commit_after", DBUG_SUICIDE(););
|
||||
end:
|
||||
if (rw_trans)
|
||||
start_waiting_global_read_lock(thd);
|
||||
/* Free resources and perform other cleanup even for 'empty' transactions. */
|
||||
if (is_real_trans)
|
||||
thd->transaction.cleanup();
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
/* Free resources and perform other cleanup even for 'empty' transactions. */
|
||||
else if (is_real_trans)
|
||||
thd->transaction.cleanup();
|
||||
|
||||
DBUG_EXECUTE_IF("crash_commit_before", DBUG_SUICIDE(););
|
||||
|
||||
/* Close all cursors that can not survive COMMIT */
|
||||
if (is_real_trans) /* not a statement commit */
|
||||
thd->stmt_map.close_transient_cursors();
|
||||
|
||||
uint rw_ha_count= ha_check_and_coalesce_trx_read_only(thd, ha_info, all);
|
||||
/* rw_trans is TRUE when we in a transaction changing data */
|
||||
bool rw_trans= is_real_trans && (rw_ha_count > 0);
|
||||
|
||||
if (rw_trans &&
|
||||
wait_if_global_read_lock(thd, 0, 0))
|
||||
{
|
||||
ha_rollback_trans(thd, all);
|
||||
DBUG_RETURN(1);
|
||||
}
|
||||
|
||||
if (rw_trans &&
|
||||
opt_readonly &&
|
||||
!(thd->security_ctx->master_access & SUPER_ACL) &&
|
||||
!thd->slave_thread)
|
||||
{
|
||||
my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--read-only");
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (trans->no_2pc || (rw_ha_count <= 1))
|
||||
{
|
||||
error= ha_commit_one_phase(thd, all);
|
||||
DBUG_EXECUTE_IF("crash_commit_after", DBUG_SUICIDE(););
|
||||
goto end;
|
||||
}
|
||||
|
||||
need_prepare_ordered= FALSE;
|
||||
need_commit_ordered= FALSE;
|
||||
xid= thd->transaction.xid_state.xid.get_my_xid();
|
||||
|
||||
for (Ha_trx_info *hi= ha_info; hi; hi= hi->next())
|
||||
{
|
||||
int err;
|
||||
handlerton *ht= hi->ht();
|
||||
/*
|
||||
Do not call two-phase commit if this particular
|
||||
transaction is read-only. This allows for simpler
|
||||
implementation in engines that are always read-only.
|
||||
*/
|
||||
if (! hi->is_trx_read_write())
|
||||
continue;
|
||||
/*
|
||||
Sic: we know that prepare() is not NULL since otherwise
|
||||
trans->no_2pc would have been set.
|
||||
*/
|
||||
err= ht->prepare(ht, thd, all);
|
||||
status_var_increment(thd->status_var.ha_prepare_count);
|
||||
if (err)
|
||||
my_error(ER_ERROR_DURING_COMMIT, MYF(0), err);
|
||||
|
||||
if (err)
|
||||
goto err;
|
||||
|
||||
need_prepare_ordered|= (ht->prepare_ordered != NULL);
|
||||
need_commit_ordered|= (ht->commit_ordered != NULL);
|
||||
}
|
||||
DBUG_EXECUTE_IF("crash_commit_after_prepare", DBUG_SUICIDE(););
|
||||
|
||||
if (!is_real_trans)
|
||||
{
|
||||
error= commit_one_phase_2(thd, all, trans, is_real_trans);
|
||||
DBUG_EXECUTE_IF("crash_commit_after", DBUG_SUICIDE(););
|
||||
goto end;
|
||||
}
|
||||
|
||||
cookie= tc_log->log_and_order(thd, xid, all, need_prepare_ordered,
|
||||
need_commit_ordered);
|
||||
if (!cookie)
|
||||
goto err;
|
||||
|
||||
DBUG_EXECUTE_IF("crash_commit_after_log", DBUG_SUICIDE(););
|
||||
|
||||
error= commit_one_phase_2(thd, all, trans, is_real_trans) ? 2 : 0;
|
||||
DBUG_EXECUTE_IF("crash_commit_after", DBUG_SUICIDE(););
|
||||
|
||||
DBUG_EXECUTE_IF("crash_commit_before_unlog", DBUG_SUICIDE(););
|
||||
if (tc_log->unlog(cookie, xid))
|
||||
{
|
||||
error= 2; /* Error during commit */
|
||||
goto end;
|
||||
}
|
||||
|
||||
DBUG_EXECUTE_IF("crash_commit_after", DBUG_SUICIDE(););
|
||||
goto end;
|
||||
|
||||
/* Come here if error and we need to rollback. */
|
||||
err:
|
||||
error= 1; /* Transaction was rolled back */
|
||||
ha_rollback_trans(thd, all);
|
||||
|
||||
end:
|
||||
if (rw_trans)
|
||||
start_waiting_global_read_lock(thd);
|
||||
#endif /* USING_TRANSACTIONS */
|
||||
DBUG_RETURN(error);
|
||||
}
|
||||
@@ -1224,7 +1252,6 @@ end:
|
||||
*/
|
||||
int ha_commit_one_phase(THD *thd, bool all)
|
||||
{
|
||||
int error=0;
|
||||
THD_TRANS *trans=all ? &thd->transaction.all : &thd->transaction.stmt;
|
||||
/*
|
||||
"real" is a nick name for a transaction for which a commit will
|
||||
@@ -1234,8 +1261,16 @@ int ha_commit_one_phase(THD *thd, bool all)
|
||||
enclosing 'all' transaction is rolled back.
|
||||
*/
|
||||
bool is_real_trans=all || thd->transaction.all.ha_list == 0;
|
||||
Ha_trx_info *ha_info= trans->ha_list, *ha_info_next;
|
||||
DBUG_ENTER("ha_commit_one_phase");
|
||||
DBUG_RETURN(commit_one_phase_2(thd, all, trans, is_real_trans));
|
||||
}
|
||||
|
||||
static int
|
||||
commit_one_phase_2(THD *thd, bool all, THD_TRANS *trans, bool is_real_trans)
|
||||
{
|
||||
int error= 0;
|
||||
Ha_trx_info *ha_info= trans->ha_list, *ha_info_next;
|
||||
DBUG_ENTER("commit_one_phase_2");
|
||||
#ifdef USING_TRANSACTIONS
|
||||
if (ha_info)
|
||||
{
|
||||
@@ -1852,7 +1887,16 @@ int ha_start_consistent_snapshot(THD *thd)
|
||||
{
|
||||
bool warn= true;
|
||||
|
||||
/*
|
||||
Holding the LOCK_commit_ordered mutex ensures that we get the same
|
||||
snapshot for all engines (including the binary log). This allows us
|
||||
among other things to do backups with
|
||||
START TRANSACTION WITH CONSISTENT SNAPSHOT and
|
||||
have a consistent binlog position.
|
||||
*/
|
||||
pthread_mutex_lock(&LOCK_commit_ordered);
|
||||
plugin_foreach(thd, snapshot_handlerton, MYSQL_STORAGE_ENGINE_PLUGIN, &warn);
|
||||
pthread_mutex_unlock(&LOCK_commit_ordered);
|
||||
|
||||
/*
|
||||
Same idea as when one wants to CREATE TABLE in one engine which does not
|
||||
@@ -4622,7 +4666,8 @@ static bool check_table_binlog_row_based(THD *thd, TABLE *table)
|
||||
|
||||
/** @brief
|
||||
Write table maps for all (manually or automatically) locked tables
|
||||
to the binary log.
|
||||
to the binary log. Also, if binlog_annotate_rows_events is ON,
|
||||
write Annotate_rows event before the first table map.
|
||||
|
||||
SYNOPSIS
|
||||
write_locked_table_maps()
|
||||
@@ -4659,6 +4704,9 @@ static int write_locked_table_maps(THD *thd)
|
||||
locks[0]= thd->extra_lock;
|
||||
locks[1]= thd->lock;
|
||||
locks[2]= thd->locked_tables;
|
||||
my_bool with_annotate= thd->variables.binlog_annotate_rows_events &&
|
||||
thd->query() && thd->query_length();
|
||||
|
||||
for (uint i= 0 ; i < sizeof(locks)/sizeof(*locks) ; ++i )
|
||||
{
|
||||
MYSQL_LOCK const *const lock= locks[i];
|
||||
@@ -4676,7 +4724,8 @@ static int write_locked_table_maps(THD *thd)
|
||||
check_table_binlog_row_based(thd, table))
|
||||
{
|
||||
int const has_trans= table->file->has_transactions();
|
||||
int const error= thd->binlog_write_table_map(table, has_trans);
|
||||
int const error= thd->binlog_write_table_map(table, has_trans,
|
||||
&with_annotate);
|
||||
/*
|
||||
If an error occurs, it is the responsibility of the caller to
|
||||
roll back the transaction.
|
||||
|
||||
Reference in New Issue
Block a user