Commit 2e7cb4d0 authored by marko@hundin.mysql.fi's avatar marko@hundin.mysql.fi

InnoDB: Make intermediate COMMITs in ALTER TABLE more robust (Bug #6633)

parent 19dab118
...@@ -463,13 +463,17 @@ lock_rec_hash( ...@@ -463,13 +463,17 @@ lock_rec_hash(
ulint space, /* in: space */ ulint space, /* in: space */
ulint page_no);/* in: page number */ ulint page_no);/* in: page number */
/************************************************************************* /*************************************************************************
Gets the table covered by an IX table lock. */ Gets the table covered by an IX or IS table lock, if there are no
other locks on the table. */
dict_table_t* dict_table_t*
lock_get_ix_table( lock_get_table(
/*==============*/ /*===========*/
/* out: the table covered by the lock */ /* out: the table covered by the lock,
lock_t* lock); /* in: table lock */ or NULL if it is not an IX or IS table lock,
or there are other locks on the table */
lock_t* lock, /* in: lock */
ulint* mode); /* out: lock mode of table */
/************************************************************************* /*************************************************************************
Checks that a transaction id is sensible, i.e., not in the future. */ Checks that a transaction id is sensible, i.e., not in the future. */
......
...@@ -177,10 +177,12 @@ row_lock_table_for_mysql( ...@@ -177,10 +177,12 @@ row_lock_table_for_mysql(
/* out: error code or DB_SUCCESS */ /* out: error code or DB_SUCCESS */
row_prebuilt_t* prebuilt, /* in: prebuilt struct in the MySQL row_prebuilt_t* prebuilt, /* in: prebuilt struct in the MySQL
table handle */ table handle */
dict_table_t* table); /* in: table to LOCK_IX, or NULL dict_table_t* table, /* in: table to lock, or NULL
if prebuilt->table should be if prebuilt->table should be
locked as LOCK_TABLE_EXP | locked as LOCK_TABLE_EXP |
prebuilt->select_lock_type */ prebuilt->select_lock_type */
ulint mode); /* in: lock mode of table */
/************************************************************************* /*************************************************************************
Does an insert for MySQL. */ Does an insert for MySQL. */
......
...@@ -395,19 +395,6 @@ lock_rec_get_nth_bit( ...@@ -395,19 +395,6 @@ lock_rec_get_nth_bit(
return(ut_bit_get_nth(b, bit_index)); return(ut_bit_get_nth(b, bit_index));
} }
/*************************************************************************
Gets the table covered by an IX table lock. */
dict_table_t*
lock_get_ix_table(
/*==============*/
/* out: the table covered by the lock */
lock_t* lock) /* in: table lock */
{
ut_a(lock->type_mode == (LOCK_TABLE | LOCK_IX));
return(lock->un_member.tab_lock.table);
}
/*************************************************************************/ /*************************************************************************/
#define lock_mutex_enter_kernel() mutex_enter(&kernel_mutex) #define lock_mutex_enter_kernel() mutex_enter(&kernel_mutex)
...@@ -614,6 +601,45 @@ lock_get_wait( ...@@ -614,6 +601,45 @@ lock_get_wait(
return(FALSE); return(FALSE);
} }
/*************************************************************************
Gets the table covered by an IX or IS table lock, if there are no
other locks on the table. */
dict_table_t*
lock_get_table(
/*===========*/
/* out: the table covered by the lock,
or NULL if it is not an IX or IS table lock,
or there are other locks on the table */
lock_t* lock, /* in: lock */
ulint* mode) /* out: lock mode of table */
{
dict_table_t* table;
ulint lock_mode;
table = NULL;
*mode = LOCK_NONE;
if (lock_get_type(lock) != LOCK_TABLE) {
return(table);
}
lock_mode = lock_get_mode(lock);
switch (lock_mode) {
case LOCK_IS:
case LOCK_IX:
*mode = lock_mode;
table = lock->un_member.tab_lock.table;
if (UT_LIST_GET_LEN(table->locks) != 1 ||
UT_LIST_GET_FIRST(table->locks) != lock) {
/* We only support the case when
there is only one lock on this table. */
table = NULL;
}
}
return(table);
}
/************************************************************************* /*************************************************************************
Sets the wait flag of a lock and the back pointer in trx to lock. */ Sets the wait flag of a lock and the back pointer in trx to lock. */
UNIV_INLINE UNIV_INLINE
......
...@@ -782,10 +782,11 @@ row_lock_table_for_mysql( ...@@ -782,10 +782,11 @@ row_lock_table_for_mysql(
/* out: error code or DB_SUCCESS */ /* out: error code or DB_SUCCESS */
row_prebuilt_t* prebuilt, /* in: prebuilt struct in the MySQL row_prebuilt_t* prebuilt, /* in: prebuilt struct in the MySQL
table handle */ table handle */
dict_table_t* table) /* in: table to LOCK_IX, or NULL dict_table_t* table, /* in: table to lock, or NULL
if prebuilt->table should be if prebuilt->table should be
locked as LOCK_TABLE_EXP | locked as LOCK_TABLE_EXP |
prebuilt->select_lock_type */ prebuilt->select_lock_type */
ulint mode) /* in: lock mode of table */
{ {
trx_t* trx = prebuilt->trx; trx_t* trx = prebuilt->trx;
que_thr_t* thr; que_thr_t* thr;
...@@ -819,7 +820,7 @@ run_again: ...@@ -819,7 +820,7 @@ run_again:
trx_start_if_not_started(trx); trx_start_if_not_started(trx);
if (table) { if (table) {
err = lock_table(0, table, LOCK_IX, thr); err = lock_table(0, table, mode, thr);
} else { } else {
err = lock_table(LOCK_TABLE_EXP, prebuilt->table, err = lock_table(LOCK_TABLE_EXP, prebuilt->table,
prebuilt->select_lock_type, thr); prebuilt->select_lock_type, thr);
......
...@@ -2324,20 +2324,55 @@ ha_innobase::write_row( ...@@ -2324,20 +2324,55 @@ ha_innobase::write_row(
position in the source table need not be adjusted after the position in the source table need not be adjusted after the
intermediate COMMIT, since writes by other transactions are intermediate COMMIT, since writes by other transactions are
being blocked by a MySQL table lock TL_WRITE_ALLOW_READ. */ being blocked by a MySQL table lock TL_WRITE_ALLOW_READ. */
ut_a(prebuilt->trx->mysql_n_tables_locked == 2);
ut_a(UT_LIST_GET_LEN(prebuilt->trx->trx_locks) >= 2); dict_table_t* table;
dict_table_t* table = lock_get_ix_table( ibool mode;
UT_LIST_GET_FIRST(prebuilt->trx->trx_locks));
num_write_row = 0; num_write_row = 0;
/* Commit the transaction. This will release the table /* Commit the transaction. This will release the table
locks, so they have to be acquired again. */ locks, so they have to be acquired again. */
innobase_commit(user_thd, prebuilt->trx); switch (prebuilt->trx->mysql_n_tables_locked) {
/* Note that this transaction is still active. */ case 1:
user_thd->transaction.all.innodb_active_trans = 1; /* Altering to InnoDB format */
/* Re-acquire the IX table lock on the source table. */ innobase_commit(user_thd, prebuilt->trx);
row_lock_table_for_mysql(prebuilt, table); /* Note that this transaction is still active. */
/* We will need an IX lock on the destination table. */ user_thd->transaction.all.innodb_active_trans = 1;
prebuilt->sql_stat_start = TRUE; /* We will need an IX lock on the destination table. */
prebuilt->sql_stat_start = TRUE;
break;
case 2:
/* Altering an InnoDB table */
ut_a(UT_LIST_GET_LEN(prebuilt->trx->trx_locks) >= 2);
table = lock_get_table(
UT_LIST_GET_FIRST(prebuilt->trx->trx_locks),
&mode);
if (!table) {
goto no_commit;
}
/* Commit the transaction. This will release the table
locks, so they have to be acquired again. */
innobase_commit(user_thd, prebuilt->trx);
/* Note that this transaction is still active. */
user_thd->transaction.all.innodb_active_trans = 1;
/* Re-acquire the table lock on the source table. */
row_lock_table_for_mysql(prebuilt, table, mode);
/* We will need an IX lock on the destination table. */
prebuilt->sql_stat_start = TRUE;
break;
default:
no_commit:
/* Unknown situation: do nothing (no commit) */
/*
ut_print_timestamp(stderr);
fprintf(stderr,
" InnoDB error: ALTER TABLE is holding lock"
" on %lu tables!\n",
prebuilt->trx->mysql_n_tables_locked);
*/
break;
}
} }
num_write_row++; num_write_row++;
...@@ -5015,7 +5050,8 @@ ha_innobase::external_lock( ...@@ -5015,7 +5050,8 @@ ha_innobase::external_lock(
if (thd->in_lock_tables && if (thd->in_lock_tables &&
thd->variables.innodb_table_locks) { thd->variables.innodb_table_locks) {
ulint error; ulint error;
error = row_lock_table_for_mysql(prebuilt, 0); error = row_lock_table_for_mysql(prebuilt,
NULL, LOCK_TABLE_EXP);
if (error != DB_SUCCESS) { if (error != DB_SUCCESS) {
error = convert_error_code_to_mysql( error = convert_error_code_to_mysql(
......
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