Commit 487da702 authored by osku's avatar osku

Add support for lock waits in the SQL parser.

parent efd7af58
......@@ -1193,7 +1193,7 @@ dict_create_or_check_foreign_constraint_tables(void)
"CREATE UNIQUE CLUSTERED INDEX ID_IND ON SYS_FOREIGN_COLS (ID, POS);\n"
"COMMIT WORK;\n"
"END;\n"
, trx);
, FALSE, trx);
if (error != DB_SUCCESS) {
fprintf(stderr, "InnoDB: error %lu in creation\n",
......@@ -1242,7 +1242,7 @@ dict_foreign_eval_sql(
ulint error;
FILE* ef = dict_foreign_err_file;
error = que_eval_sql(info, sql, trx);
error = que_eval_sql(info, sql, FALSE, trx);
if (error == DB_DUPLICATE_KEY) {
mutex_enter(&dict_foreign_err_mutex);
......
......@@ -143,14 +143,12 @@ que_thr_stop_for_mysql(
/*===================*/
que_thr_t* thr); /* in: query thread */
/**************************************************************************
Runs query threads. Note that the individual query thread which is run
within this function may change if, e.g., the OS thread executing this
function uses a threshold amount of resources. */
Run a query thread. Handles lock waits. */
void
que_run_threads(
/*============*/
que_thr_t* thr); /* in: query thread which is run initially */
que_thr_t* thr); /* in: query thread */
/**************************************************************************
After signal handling is finished, returns control to a query graph error
handling routine. (Currently, just returns the control to the root of the
......@@ -163,19 +161,6 @@ que_fork_error_handle(
que_t* fork); /* in: query graph which was run before signal
handling started, NULL not allowed */
/**************************************************************************
Handles an SQL error noticed during query thread execution. At the moment,
does nothing! */
void
que_thr_handle_error(
/*=================*/
que_thr_t* thr, /* in: query thread */
ulint err_no, /* in: error number */
byte* err_str,/* in, own: error string or NULL; NOTE: the
function will take care of freeing of the
string! */
ulint err_len);/* in: error string length */
/**************************************************************************
Moves a suspended query thread to the QUE_THR_RUNNING state and releases
a single worker thread to execute it. This function should be used to end
the wait state of a query thread waiting for a lock or a stored procedure
......@@ -337,9 +322,14 @@ Evaluate the given SQL */
ulint
que_eval_sql(
/*=========*/
pars_info_t* info, /* out: error code or DB_SUCCESS */
const char* sql, /* in: info struct, or NULL */
/* out: error code or DB_SUCCESS */
pars_info_t* info, /* in: info struct, or NULL */
const char* sql, /* in: SQL string */
ibool reserve_dict_mutex,
/* in: if TRUE, acquire/release
dict_sys->mutex around call to pars_sql. */
trx_t* trx); /* in: trx */
/* Query graph query thread node: the fields are protected by the kernel
mutex with the exceptions named below */
......
......@@ -715,27 +715,6 @@ que_graph_try_free(
return(FALSE);
}
/**************************************************************************
Handles an SQL error noticed during query thread execution. Currently,
does nothing! */
void
que_thr_handle_error(
/*=================*/
que_thr_t* thr __attribute__((unused)),
/* in: query thread */
ulint err_no __attribute__((unused)),
/* in: error number */
byte* err_str __attribute__((unused)),
/* in, own: error string or NULL; NOTE: the
function will take care of freeing of the
string! */
ulint err_len __attribute__((unused)))
/* in: error string length */
{
/* Does nothing */
}
/********************************************************************
Performs an execution step on a thr node. */
static
......@@ -813,11 +792,10 @@ que_thr_move_to_run_state(
Decrements the query thread reference counts in the query graph and the
transaction. May start signal handling, e.g., a rollback.
*** NOTE ***:
This and que_thr_stop_for_mysql are
the only functions where the reference count can be decremented and
this function may only be called from inside que_run_threads or
que_thr_check_if_switch! These restrictions exist to make the rollback code
easier to maintain. */
This and que_thr_stop_for_mysql are the only functions where the reference
count can be decremented and this function may only be called from inside
que_run_threads or que_thr_check_if_switch! These restrictions exist to make
the rollback code easier to maintain. */
static
void
que_thr_dec_refer_count(
......@@ -836,7 +814,7 @@ que_thr_dec_refer_count(
ibool stopped;
fork = thr->common.parent;
trx = thr->graph->trx;
trx = thr_get_trx(thr);
sess = trx->sess;
mutex_enter(&kernel_mutex);
......@@ -856,6 +834,12 @@ que_thr_dec_refer_count(
stderr); */
if (next_thr && *next_thr == NULL) {
/* Normally srv_suspend_mysql_thread resets
the state to DB_SUCCESS before waiting, but
in this case we have to do it here,
otherwise nobody does it. */
trx->error_state = DB_SUCCESS;
*next_thr = thr;
} else {
ut_a(0);
......@@ -1200,7 +1184,10 @@ que_thr_step(
trx_t* trx;
ulint type;
trx = thr_get_trx(thr);
ut_ad(thr->state == QUE_THR_RUNNING);
ut_a(trx->error_state == DB_SUCCESS);
thr->resource++;
......@@ -1236,7 +1223,6 @@ que_thr_step(
threads doing updating or inserting at the moment! */
if (thr->prev_node == que_node_get_parent(node)) {
trx = thr_get_trx(thr);
trx->last_sql_stat_start.least_undo_no
= trx->undo_no;
}
......@@ -1298,24 +1284,28 @@ que_thr_step(
old_thr->prev_node = node;
}
if (thr) {
ut_a(thr_get_trx(thr)->error_state == DB_SUCCESS);
}
return(thr);
}
/**************************************************************************
Runs query threads. Note that the individual query thread which is run
within this function may change if, e.g., the OS thread executing this
function uses a threshold amount of resources. */
Run a query thread until it finishes or encounters e.g. a lock wait. */
static
void
que_run_threads(
/*============*/
que_thr_t* thr) /* in: query thread which is run initially */
que_run_threads_low(
/*================*/
que_thr_t* thr) /* in: query thread */
{
que_thr_t* next_thr;
ulint cumul_resource;
ulint loop_count;
ut_ad(thr->state == QUE_THR_RUNNING);
ut_a(thr_get_trx(thr)->error_state == DB_SUCCESS);
#ifdef UNIV_SYNC_DEBUG
ut_ad(!mutex_own(&kernel_mutex));
#endif /* UNIV_SYNC_DEBUG */
......@@ -1340,10 +1330,15 @@ que_run_threads(
next_thr = que_thr_step(thr);
/*-------------------------*/
ut_a(!next_thr || (thr_get_trx(next_thr)->error_state == DB_SUCCESS));
loop_count++;
if (next_thr != thr) {
ut_a(next_thr == NULL);
/* This can change next_thr to a non-NULL value if there was
a lock wait that already completed. */
que_thr_dec_refer_count(thr, &next_thr);
if (next_thr == NULL) {
......@@ -1359,20 +1354,89 @@ que_run_threads(
goto loop;
}
/**************************************************************************
Run a query thread. Handles lock waits. */
void
que_run_threads(
/*============*/
que_thr_t* thr) /* in: query thread */
{
loop:
ut_a(thr_get_trx(thr)->error_state == DB_SUCCESS);
que_run_threads_low(thr);
mutex_enter(&kernel_mutex);
switch (thr->state) {
case QUE_THR_RUNNING:
/* There probably was a lock wait, but it already ended
before we came here: continue running thr */
mutex_exit(&kernel_mutex);
goto loop;
case QUE_THR_LOCK_WAIT:
mutex_exit(&kernel_mutex);
/* The ..._mysql_... function works also for InnoDB's
internal threads. Let us wait that the lock wait ends. */
srv_suspend_mysql_thread(thr);
if (thr_get_trx(thr)->error_state != DB_SUCCESS) {
/* thr was chosen as a deadlock victim or there was
a lock wait timeout */
que_thr_dec_refer_count(thr, NULL);
return;
}
goto loop;
case QUE_THR_COMPLETED:
case QUE_THR_COMMAND_WAIT:
/* Do nothing */
break;
default:
ut_error;
}
mutex_exit(&kernel_mutex);
}
/*************************************************************************
Evaluate the given SQL */
Evaluate the given SQL. */
ulint
que_eval_sql(
/*=========*/
pars_info_t* info, /* out: error code or DB_SUCCESS */
const char* sql, /* in: info struct, or NULL */
/* out: error code or DB_SUCCESS */
pars_info_t* info, /* in: info struct, or NULL */
const char* sql, /* in: SQL string */
ibool reserve_dict_mutex,
/* in: if TRUE, acquire/release
dict_sys->mutex around call to pars_sql. */
trx_t* trx) /* in: trx */
{
que_thr_t* thr;
que_t* graph;
ut_a(trx->error_state == DB_SUCCESS);
if (reserve_dict_mutex) {
mutex_enter(&dict_sys->mutex);
}
graph = pars_sql(info, sql);
if (reserve_dict_mutex) {
mutex_exit(&dict_sys->mutex);
}
ut_a(graph);
graph->trx = trx;
......
......@@ -2517,7 +2517,7 @@ do not allow the discard. We also reserve the data dictionary latch. */
" WHERE TABLE_ID = old_id;\n"
"COMMIT WORK;\n"
"END;\n"
, trx);
, FALSE, trx);
if (err != DB_SUCCESS) {
trx->error_state = DB_SUCCESS;
......@@ -2913,7 +2913,7 @@ do not allow the TRUNCATE. We also reserve the data dictionary latch. */
" WHERE TABLE_ID = :old_id;\n"
"COMMIT WORK;\n"
"END;\n"
, trx);
, FALSE, trx);
if (err != DB_SUCCESS) {
trx->error_state = DB_SUCCESS;
......@@ -3234,7 +3234,7 @@ fputs(" InnoDB: You are trying to drop table ", stderr);
"DELETE FROM SYS_TABLES WHERE ID = table_id;\n"
"COMMIT WORK;\n"
"END;\n"
, trx);
, FALSE, trx);
if (err != DB_SUCCESS) {
ut_a(err == DB_OUT_OF_FILE_SPACE);
......@@ -3443,7 +3443,7 @@ row_delete_constraint_low(
"DELETE FROM SYS_FOREIGN_COLS WHERE ID = :id;\n"
"DELETE FROM SYS_FOREIGN WHERE ID = :id;\n"
"END;\n"
, trx));
, FALSE, trx));
}
/********************************************************************
......@@ -3609,7 +3609,7 @@ row_rename_table_for_mysql(
"UPDATE SYS_TABLES SET NAME = :new_table_name\n"
" WHERE NAME = :old_table_name;\n"
"END;\n"
, trx);
, FALSE, trx);
if (err != DB_SUCCESS) {
......@@ -3683,7 +3683,7 @@ row_rename_table_for_mysql(
"WHERE REF_NAME = :old_table_name\n"
" AND TO_BINARY(REF_NAME) = TO_BINARY(:old_table_name);\n"
"END;\n"
, trx);
, FALSE, trx);
} else if (n_constraints_to_drop > 0) {
/* Drop some constraints of tmp tables. */
......
......@@ -1920,9 +1920,8 @@ row_sel_step(
err = lock_table(0, table_node->table,
i_lock_mode, thr);
if (err != DB_SUCCESS) {
thr_get_trx(thr)->error_state = err;
que_thr_handle_error(thr, DB_ERROR,
NULL, 0);
return(NULL);
}
......@@ -1958,17 +1957,8 @@ row_sel_step(
thr->graph->last_sel_node = node;
if (err == DB_SUCCESS) {
/* Ok: do nothing */
} else if (err == DB_LOCK_WAIT) {
return(NULL);
} else {
/* SQL error detected */
fprintf(stderr, "SQL error %lu\n", (ulong) err);
que_thr_handle_error(thr, DB_ERROR, NULL, 0);
if (err != DB_SUCCESS) {
thr_get_trx(thr)->error_state = err;
return(NULL);
}
......@@ -2029,7 +2019,7 @@ fetch_step(
fprintf(stderr,
"InnoDB: Error: fetch called on a closed cursor\n");
que_thr_handle_error(thr, DB_ERROR, NULL, 0);
thr_get_trx(thr)->error_state = DB_ERROR;
return(NULL);
}
......
......@@ -1984,12 +1984,7 @@ row_upd_step(
error_handling:
trx->error_state = err;
if (err == DB_SUCCESS) {
/* Ok: do nothing */
} else if (err == DB_LOCK_WAIT) {
return(NULL);
} else {
if (err != DB_SUCCESS) {
return(NULL);
}
......
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