Commit 14b71228 authored by unknown's avatar unknown

ha_innodb.cc:

  Let InnoDB use a consistent read when it initializes the auto-inc counter for a table: this will eliminate spurious deadlocks, but will ignore an UPDATE if that happens at the same time that we init the auto-inc counter; this has to be documented; this path also fixes most of Bug #11633, but not all: if ::external_lock() is not called on the table in SHOW TABLE STATUS, that might cause a crash if someone simultaneously DROPs the table


sql/ha_innodb.cc:
  Let InnoDB use a consistent read when it initializes the auto-inc counter for a table: this will eliminate spurious deadlocks, but will ignore an UPDATE if that happens at the same time that we init the auto-inc counter; this has to be documented; this path also fixes most of Bug #11633, but not all: if ::external_lock() is not called on the table in SHOW TABLE STATUS, that might cause a crash if someone simultaneously DROPs the table
parent c3abc8f0
......@@ -1496,8 +1496,8 @@ innobase_start_trx_and_assign_read_view(
/*********************************************************************
Commits a transaction in an InnoDB database or marks an SQL statement
ended. */
static int
static
int
innobase_commit(
/*============*/
/* out: 0 */
......@@ -5991,6 +5991,7 @@ ha_innobase::external_lock(
reads. */
prebuilt->select_lock_type = LOCK_S;
prebuilt->stored_select_lock_type = LOCK_S;
}
/* Starting from 4.1.9, no InnoDB table lock is taken in LOCK
......@@ -6030,7 +6031,6 @@ ha_innobase::external_lock(
trx->n_mysql_tables_in_use--;
prebuilt->mysql_has_locked = FALSE;
/* If the MySQL lock count drops to zero we know that the current SQL
statement has ended */
......@@ -6563,12 +6563,14 @@ the value of the auto-inc counter. */
int
ha_innobase::innobase_read_and_init_auto_inc(
/*=========================================*/
/* out: 0 or error code: deadlock or
lock wait timeout */
/* out: 0 or error code: deadlock or lock wait
timeout */
longlong* ret) /* out: auto-inc value */
{
row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt;
longlong auto_inc;
ulint old_select_lock_type;
ibool trx_was_not_started = FALSE;
int error;
ut_a(prebuilt);
......@@ -6576,6 +6578,10 @@ ha_innobase::innobase_read_and_init_auto_inc(
(trx_t*) current_thd->ha_data[innobase_hton.slot]);
ut_a(prebuilt->table);
if (prebuilt->trx->conc_state == TRX_NOT_STARTED) {
trx_was_not_started = TRUE;
}
/* In case MySQL calls this in the middle of a SELECT query, release
possible adaptive hash latch to avoid deadlocks of threads */
......@@ -6587,7 +6593,9 @@ ha_innobase::innobase_read_and_init_auto_inc(
/* Already initialized */
*ret = auto_inc;
return(0);
error = 0;
goto func_exit_early;
}
error = row_lock_table_autoinc_for_mysql(prebuilt);
......@@ -6595,7 +6603,7 @@ ha_innobase::innobase_read_and_init_auto_inc(
if (error != DB_SUCCESS) {
error = convert_error_code_to_mysql(error, user_thd);
goto func_exit;
goto func_exit_early;
}
/* Check again if someone has initialized the counter meanwhile */
......@@ -6604,30 +6612,37 @@ ha_innobase::innobase_read_and_init_auto_inc(
if (auto_inc != 0) {
*ret = auto_inc;
return(0);
error = 0;
goto func_exit_early;
}
(void) extra(HA_EXTRA_KEYREAD);
index_init(table->s->next_number_index);
/* We use an exclusive lock when we read the max key value from the
auto-increment column index. This is because then build_template will
advise InnoDB to fetch all columns. In SHOW TABLE STATUS the query
id of the auto-increment column is not changed, and previously InnoDB
did not fetch it, causing SHOW TABLE STATUS to show wrong values
for the autoinc column. */
prebuilt->select_lock_type = LOCK_X;
/* Starting from 5.0.9, we use a consistent read to read the auto-inc
column maximum value. This eliminates the spurious deadlocks caused
by the row X-lock that we previously used. Note the following flaw
in our algorithm: if some other user meanwhile UPDATEs the auto-inc
column, our consistent read will not return the largest value. We
accept this flaw, since the deadlocks were a bigger trouble. */
/* Play safe and also give in another way the hint to fetch
all columns in the key: */
/* Fetch all the columns in the key */
prebuilt->hint_need_to_fetch_extra_cols = ROW_RETRIEVE_ALL_COLS;
prebuilt->trx->mysql_n_tables_locked += 1;
old_select_lock_type = prebuilt->select_lock_type;
prebuilt->select_lock_type = LOCK_NONE;
/* Eliminate an InnoDB error print that happens when we try to SELECT
from a table when no table has been locked in ::external_lock(). */
prebuilt->trx->n_mysql_tables_in_use++;
error = index_last(table->record[1]);
prebuilt->trx->n_mysql_tables_in_use--;
prebuilt->select_lock_type = old_select_lock_type;
if (error) {
if (error == HA_ERR_END_OF_FILE) {
/* The table was empty, initialize to 1 */
......@@ -6635,7 +6650,10 @@ ha_innobase::innobase_read_and_init_auto_inc(
error = 0;
} else {
/* Deadlock or a lock wait timeout */
/* This should not happen in a consistent read */
fprintf(stderr,
"InnoDB: Error: consistent read of auto-inc column returned %lu\n",
(ulong)error);
auto_inc = -1;
goto func_exit;
......@@ -6655,7 +6673,18 @@ ha_innobase::innobase_read_and_init_auto_inc(
*ret = auto_inc;
return(error);
func_exit_early:
/* Since MySQL does not seem to call autocommit after SHOW TABLE
STATUS (even if we would register the trx here), we must commit our
transaction here if it was started here. This is to eliminate a
dangling transaction. */
if (trx_was_not_started) {
innobase_commit_low(prebuilt->trx);
}
return(error);
}
/***********************************************************************
......
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