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