Commit 8840583a authored by Marko Mäkelä's avatar Marko Mäkelä

MDEV-27909 InnoDB: Failing assertion: state == TRX_STATE_NOT_STARTED ... on DDL

The fix in commit 6e390a62 (MDEV-26772)
was a step to the right direction, but implemented incorrectly.
When an InnoDB persistent statistics table cannot be locked immediately,
we must not let row_mysql_handle_errors() to roll back the transaction.

lock_table_for_trx(): Add the parameter no_wait (default false)
for an immediate return of DB_LOCK_WAIT in case of a conflict.

ha_innobase::delete_table(), ha_innobase::rename_table():
Pass no_wait=true to lock_table_for_trx() when needed,
instead of temporarily setting THDVAR(thd, lock_wait_timeout) to 0.
parent 065f995e
...@@ -13547,29 +13547,26 @@ int ha_innobase::delete_table(const char *name) ...@@ -13547,29 +13547,26 @@ int ha_innobase::delete_table(const char *name)
dict_sys.unfreeze(); dict_sys.unfreeze();
} }
auto &timeout= THDVAR(thd, lock_wait_timeout); const bool skip_wait{table->name.is_temporary()};
const auto save_timeout= timeout;
if (table->name.is_temporary())
timeout= 0;
if (table_stats && index_stats && if (table_stats && index_stats &&
!strcmp(table_stats->name.m_name, TABLE_STATS_NAME) && !strcmp(table_stats->name.m_name, TABLE_STATS_NAME) &&
!strcmp(index_stats->name.m_name, INDEX_STATS_NAME) && !strcmp(index_stats->name.m_name, INDEX_STATS_NAME) &&
!(err= lock_table_for_trx(table_stats, trx, LOCK_X))) !(err= lock_table_for_trx(table_stats, trx, LOCK_X, skip_wait)))
err= lock_table_for_trx(index_stats, trx, LOCK_X); err= lock_table_for_trx(index_stats, trx, LOCK_X, skip_wait);
if (err != DB_SUCCESS && !timeout) if (err != DB_SUCCESS && skip_wait)
{ {
/* We may skip deleting statistics if we cannot lock the tables, /* We may skip deleting statistics if we cannot lock the tables,
when the table carries a temporary name. */ when the table carries a temporary name. */
ut_ad(err == DB_LOCK_WAIT);
ut_ad(trx->error_state == DB_SUCCESS);
err= DB_SUCCESS; err= DB_SUCCESS;
dict_table_close(table_stats, false, thd, mdl_table); dict_table_close(table_stats, false, thd, mdl_table);
dict_table_close(index_stats, false, thd, mdl_index); dict_table_close(index_stats, false, thd, mdl_index);
table_stats= nullptr; table_stats= nullptr;
index_stats= nullptr; index_stats= nullptr;
} }
timeout= save_timeout;
} }
if (err == DB_SUCCESS) if (err == DB_SUCCESS)
...@@ -14075,17 +14072,15 @@ ha_innobase::rename_table( ...@@ -14075,17 +14072,15 @@ ha_innobase::rename_table(
if (error == DB_SUCCESS && table_stats && index_stats if (error == DB_SUCCESS && table_stats && index_stats
&& !strcmp(table_stats->name.m_name, TABLE_STATS_NAME) && !strcmp(table_stats->name.m_name, TABLE_STATS_NAME)
&& !strcmp(index_stats->name.m_name, INDEX_STATS_NAME)) { && !strcmp(index_stats->name.m_name, INDEX_STATS_NAME)) {
auto &timeout = THDVAR(thd, lock_wait_timeout); error = lock_table_for_trx(table_stats, trx, LOCK_X,
const auto save_timeout = timeout; from_temp);
if (from_temp) {
timeout = 0;
}
error = lock_table_for_trx(table_stats, trx, LOCK_X);
if (error == DB_SUCCESS) { if (error == DB_SUCCESS) {
error = lock_table_for_trx(index_stats, trx, error = lock_table_for_trx(index_stats, trx,
LOCK_X); LOCK_X, from_temp);
} }
if (error != DB_SUCCESS && from_temp) { if (error != DB_SUCCESS && from_temp) {
ut_ad(error == DB_LOCK_WAIT);
ut_ad(trx->error_state == DB_SUCCESS);
error = DB_SUCCESS; error = DB_SUCCESS;
/* We may skip renaming statistics if /* We may skip renaming statistics if
we cannot lock the tables, when the we cannot lock the tables, when the
...@@ -14098,7 +14093,6 @@ ha_innobase::rename_table( ...@@ -14098,7 +14093,6 @@ ha_innobase::rename_table(
table_stats = nullptr; table_stats = nullptr;
index_stats = nullptr; index_stats = nullptr;
} }
timeout = save_timeout;
} }
} }
......
...@@ -394,15 +394,13 @@ lock_table( ...@@ -394,15 +394,13 @@ lock_table(
void lock_table_resurrect(dict_table_t *table, trx_t *trx, lock_mode mode); void lock_table_resurrect(dict_table_t *table, trx_t *trx, lock_mode mode);
/** Sets a lock on a table based on the given mode. /** Sets a lock on a table based on the given mode.
@param[in] table table to lock @param table table to lock
@param[in,out] trx transaction @param trx transaction
@param[in] mode LOCK_X or LOCK_S @param mode LOCK_X or LOCK_S
@return error code or DB_SUCCESS. */ @param no_wait whether to skip handling DB_LOCK_WAIT
dberr_t @return error code */
lock_table_for_trx( dberr_t lock_table_for_trx(dict_table_t *table, trx_t *trx, lock_mode mode,
dict_table_t* table, bool no_wait= false)
trx_t* trx,
enum lock_mode mode)
MY_ATTRIBUTE((nonnull, warn_unused_result)); MY_ATTRIBUTE((nonnull, warn_unused_result));
/** Exclusively lock the data dictionary tables. /** Exclusively lock the data dictionary tables.
...@@ -915,10 +913,8 @@ class lock_sys_t ...@@ -915,10 +913,8 @@ class lock_sys_t
@param page whether to discard also from lock_sys.prdt_hash */ @param page whether to discard also from lock_sys.prdt_hash */
void prdt_page_free_from_discard(const page_id_t id, bool all= false); void prdt_page_free_from_discard(const page_id_t id, bool all= false);
#ifdef WITH_WSREP
/** Cancel possible lock waiting for a transaction */ /** Cancel possible lock waiting for a transaction */
static void cancel_lock_wait_for_trx(trx_t *trx); static void cancel_lock_wait_for_trx(trx_t *trx);
#endif /* WITH_WSREP */
}; };
/** The lock system */ /** The lock system */
......
...@@ -3627,52 +3627,50 @@ static void lock_table_dequeue(lock_t *in_lock, bool owns_wait_mutex) ...@@ -3627,52 +3627,50 @@ static void lock_table_dequeue(lock_t *in_lock, bool owns_wait_mutex)
} }
} }
/** Sets a lock on a table based on the given mode.
@param[in] table table to lock
@param[in,out] trx transaction
@param[in] mode LOCK_X or LOCK_S
@return error code or DB_SUCCESS. */
dberr_t
lock_table_for_trx(
dict_table_t* table,
trx_t* trx,
enum lock_mode mode)
{
mem_heap_t* heap;
que_thr_t* thr;
dberr_t err;
sel_node_t* node;
heap = mem_heap_create(512);
node = sel_node_create(heap);
thr = pars_complete_graph_for_exec(node, trx, heap, NULL);
thr->graph->state = QUE_FORK_ACTIVE;
/* We use the select query graph as the dummy graph needed
in the lock module call */
thr = static_cast<que_thr_t*>( /** Sets a lock on a table based on the given mode.
que_fork_get_first_thr( @param table table to lock
static_cast<que_fork_t*>(que_node_get_parent(thr)))); @param trx transaction
@param mode LOCK_X or LOCK_S
@param no_wait whether to skip handling DB_LOCK_WAIT
@return error code */
dberr_t lock_table_for_trx(dict_table_t *table, trx_t *trx, lock_mode mode,
bool no_wait)
{
mem_heap_t *heap= mem_heap_create(512);
sel_node_t *node= sel_node_create(heap);
que_thr_t *thr= pars_complete_graph_for_exec(node, trx, heap, nullptr);
thr->graph->state= QUE_FORK_ACTIVE;
thr= static_cast<que_thr_t*>
(que_fork_get_first_thr(static_cast<que_fork_t*>
(que_node_get_parent(thr))));
run_again: run_again:
thr->run_node = thr; thr->run_node= thr;
thr->prev_node = thr->common.parent; thr->prev_node= thr->common.parent;
dberr_t err= lock_table(table, mode, thr);
err = lock_table(table, mode, thr);
trx->error_state = err; switch (err) {
case DB_SUCCESS:
if (UNIV_UNLIKELY(err != DB_SUCCESS)) { break;
if (row_mysql_handle_errors(&err, trx, thr, NULL)) { case DB_LOCK_WAIT:
goto run_again; if (no_wait)
} {
} lock_sys.cancel_lock_wait_for_trx(trx);
break;
}
/* fall through */
default:
trx->error_state= err;
if (row_mysql_handle_errors(&err, trx, thr, nullptr))
goto run_again;
}
que_graph_free(thr->graph); que_graph_free(thr->graph);
trx->op_info = ""; trx->op_info= "";
return(err); return err;
} }
/** Exclusively lock the data dictionary tables. /** Exclusively lock the data dictionary tables.
...@@ -5639,8 +5637,7 @@ static void lock_cancel_waiting_and_release(lock_t *lock) ...@@ -5639,8 +5637,7 @@ static void lock_cancel_waiting_and_release(lock_t *lock)
lock_wait_end(trx); lock_wait_end(trx);
trx->mutex_unlock(); trx->mutex_unlock();
} }
#ifdef WITH_WSREP
TRANSACTIONAL_TARGET
void lock_sys_t::cancel_lock_wait_for_trx(trx_t *trx) void lock_sys_t::cancel_lock_wait_for_trx(trx_t *trx)
{ {
lock_sys.wr_lock(SRW_LOCK_CALL); lock_sys.wr_lock(SRW_LOCK_CALL);
...@@ -5654,7 +5651,6 @@ void lock_sys_t::cancel_lock_wait_for_trx(trx_t *trx) ...@@ -5654,7 +5651,6 @@ void lock_sys_t::cancel_lock_wait_for_trx(trx_t *trx)
lock_sys.wr_unlock(); lock_sys.wr_unlock();
mysql_mutex_unlock(&lock_sys.wait_mutex); mysql_mutex_unlock(&lock_sys.wait_mutex);
} }
#endif /* WITH_WSREP */
/** Cancel a waiting lock request. /** Cancel a waiting lock request.
@tparam check_victim whether to check for DB_DEADLOCK @tparam check_victim whether to check for DB_DEADLOCK
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment