Loading innobase/include/lock0lock.h +24 −5 Original line number Diff line number Diff line Loading @@ -463,13 +463,32 @@ lock_rec_hash( ulint space, /* in: space */ ulint page_no);/* in: page number */ /************************************************************************* Gets the table covered by an IX table lock. */ Gets the source table of an ALTER TABLE transaction. The table must be covered by an IX or IS table lock. */ dict_table_t* lock_get_ix_table( /*==============*/ /* out: the table covered by the lock */ lock_t* lock); /* in: table lock */ lock_get_src_table( /*===============*/ /* out: the source table of transaction, if it is covered by an IX or IS table lock; dest if there is no source table, and NULL if the transaction is locking more than two tables or an inconsistency is found */ trx_t* trx, /* in: transaction */ dict_table_t* dest, /* in: destination of ALTER TABLE */ ulint* mode); /* out: lock mode of the source table */ /************************************************************************* Determine if the given table is exclusively "owned" by the given transaction, i.e., transaction holds LOCK_IX and possibly LOCK_AUTO_INC on the table. */ ibool lock_table_exclusive( /*=================*/ /* out: TRUE if table is only locked by trx, with LOCK_IX, and possibly LOCK_AUTO_INC */ dict_table_t* table, /* in: table */ trx_t* trx); /* in: transaction */ /************************************************************************* Checks that a transaction id is sensible, i.e., not in the future. */ Loading innobase/include/row0mysql.h +3 −1 Original line number Diff line number Diff line Loading @@ -177,10 +177,12 @@ row_lock_table_for_mysql( /* out: error code or DB_SUCCESS */ row_prebuilt_t* prebuilt, /* in: prebuilt struct in the MySQL 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 locked as LOCK_TABLE_EXP | prebuilt->select_lock_type */ ulint mode); /* in: lock mode of table */ /************************************************************************* Does an insert for MySQL. */ Loading innobase/lock/lock0lock.c +122 −13 Original line number Diff line number Diff line Loading @@ -395,19 +395,6 @@ lock_rec_get_nth_bit( 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) Loading Loading @@ -614,6 +601,128 @@ lock_get_wait( return(FALSE); } /************************************************************************* Gets the source table of an ALTER TABLE transaction. The table must be covered by an IX or IS table lock. */ dict_table_t* lock_get_src_table( /*===============*/ /* out: the source table of transaction, if it is covered by an IX or IS table lock; dest if there is no source table, and NULL if the transaction is locking more than two tables or an inconsistency is found */ trx_t* trx, /* in: transaction */ dict_table_t* dest, /* in: destination of ALTER TABLE */ ulint* mode) /* out: lock mode of the source table */ { dict_table_t* src; lock_t* lock; src = NULL; *mode = LOCK_NONE; for (lock = UT_LIST_GET_FIRST(trx->trx_locks); lock; lock = UT_LIST_GET_NEXT(trx_locks, lock)) { lock_table_t* tab_lock; ulint lock_mode; if (!(lock_get_type(lock) & LOCK_TABLE)) { /* We are only interested in table locks. */ continue; } tab_lock = &lock->un_member.tab_lock; if (dest == tab_lock->table) { /* We are not interested in the destination table. */ continue; } else if (!src) { /* This presumably is the source table. */ src = tab_lock->table; if (UT_LIST_GET_LEN(src->locks) != 1 || UT_LIST_GET_FIRST(src->locks) != lock) { /* We only support the case when there is only one lock on this table. */ return(NULL); } } else if (src != tab_lock->table) { /* The transaction is locking more than two tables (src and dest): abort */ return(NULL); } /* Check that the source table is locked by LOCK_IX or LOCK_IS. */ lock_mode = lock_get_mode(lock); switch (lock_mode) { case LOCK_IX: case LOCK_IS: if (*mode != LOCK_NONE && *mode != lock_mode) { /* There are multiple locks on src. */ return(NULL); } *mode = lock_mode; break; } } if (!src) { /* No source table lock found: flag the situation to caller */ src = dest; } return(src); } /************************************************************************* Determine if the given table is exclusively "owned" by the given transaction, i.e., transaction holds LOCK_IX and possibly LOCK_AUTO_INC on the table. */ ibool lock_is_table_exclusive( /*====================*/ /* out: TRUE if table is only locked by trx, with LOCK_IX, and possibly LOCK_AUTO_INC */ dict_table_t* table, /* in: table */ trx_t* trx) /* in: transaction */ { lock_t* lock; bool ok = FALSE; ut_ad(table && trx); for (lock = UT_LIST_GET_FIRST(table->locks); lock; lock = UT_LIST_GET_NEXT(locks, &lock->un_member.tab_lock)) { if (lock->trx != trx) { /* A lock on the table is held by some other transaction. */ return(FALSE); } if (!(lock_get_type(lock) & LOCK_TABLE)) { /* We are interested in table locks only. */ continue; } switch (lock_get_mode(lock)) { case LOCK_IX: ok = TRUE; break; case LOCK_AUTO_INC: /* It is allowed for trx to hold an auto_increment lock. */ break; default: /* Other table locks than LOCK_IX are not allowed. */ return(FALSE); } } return(ok); } /************************************************************************* Sets the wait flag of a lock and the back pointer in trx to lock. */ UNIV_INLINE Loading innobase/row/row0mysql.c +3 −2 Original line number Diff line number Diff line Loading @@ -782,10 +782,11 @@ row_lock_table_for_mysql( /* out: error code or DB_SUCCESS */ row_prebuilt_t* prebuilt, /* in: prebuilt struct in the MySQL 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 locked as LOCK_TABLE_EXP | prebuilt->select_lock_type */ ulint mode) /* in: lock mode of table */ { trx_t* trx = prebuilt->trx; que_thr_t* thr; Loading Loading @@ -819,7 +820,7 @@ row_lock_table_for_mysql( trx_start_if_not_started(trx); if (table) { err = lock_table(0, table, LOCK_IX, thr); err = lock_table(0, table, mode, thr); } else { err = lock_table(LOCK_TABLE_EXP, prebuilt->table, prebuilt->select_lock_type, thr); Loading sql/ha_innodb.cc +51 −12 Original line number Diff line number Diff line Loading @@ -2324,20 +2324,58 @@ ha_innobase::write_row( position in the source table need not be adjusted after the intermediate COMMIT, since writes by other transactions are 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 = lock_get_ix_table( UT_LIST_GET_FIRST(prebuilt->trx->trx_locks)); dict_table_t* src_table; ibool mode; num_write_row = 0; /* Commit the transaction. This will release the table locks, so they have to be acquired again. */ /* Altering an InnoDB table */ /* Get the source table. */ src_table = lock_get_src_table( prebuilt->trx, prebuilt->table, &mode); if (!src_table) { no_commit: /* Unknown situation: do not commit */ /* ut_print_timestamp(stderr); fprintf(stderr, " InnoDB error: ALTER TABLE is holding lock" " on %lu tables!\n", prebuilt->trx->mysql_n_tables_locked); */ ; } else if (src_table == prebuilt->table) { /* Source table is not in InnoDB format: no need to re-acquire locks on it. */ /* Altering to InnoDB format */ innobase_commit(user_thd, prebuilt->trx); /* Note that this transaction is still active. */ user_thd->transaction.all.innodb_active_trans = 1; /* Re-acquire the IX table lock on the source table. */ row_lock_table_for_mysql(prebuilt, table); /* We will need an IX lock on the destination table. */ prebuilt->sql_stat_start = TRUE; } else { /* Ensure that there are no other table locks than LOCK_IX and LOCK_AUTO_INC on the destination table. */ if (!lock_is_table_exclusive(prebuilt->table, prebuilt->trx)) { 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, src_table, mode); /* We will need an IX lock on the destination table. */ prebuilt->sql_stat_start = TRUE; } } num_write_row++; Loading Loading @@ -5015,7 +5053,8 @@ ha_innobase::external_lock( if (thd->in_lock_tables && thd->variables.innodb_table_locks) { 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) { error = convert_error_code_to_mysql( Loading Loading
innobase/include/lock0lock.h +24 −5 Original line number Diff line number Diff line Loading @@ -463,13 +463,32 @@ lock_rec_hash( ulint space, /* in: space */ ulint page_no);/* in: page number */ /************************************************************************* Gets the table covered by an IX table lock. */ Gets the source table of an ALTER TABLE transaction. The table must be covered by an IX or IS table lock. */ dict_table_t* lock_get_ix_table( /*==============*/ /* out: the table covered by the lock */ lock_t* lock); /* in: table lock */ lock_get_src_table( /*===============*/ /* out: the source table of transaction, if it is covered by an IX or IS table lock; dest if there is no source table, and NULL if the transaction is locking more than two tables or an inconsistency is found */ trx_t* trx, /* in: transaction */ dict_table_t* dest, /* in: destination of ALTER TABLE */ ulint* mode); /* out: lock mode of the source table */ /************************************************************************* Determine if the given table is exclusively "owned" by the given transaction, i.e., transaction holds LOCK_IX and possibly LOCK_AUTO_INC on the table. */ ibool lock_table_exclusive( /*=================*/ /* out: TRUE if table is only locked by trx, with LOCK_IX, and possibly LOCK_AUTO_INC */ dict_table_t* table, /* in: table */ trx_t* trx); /* in: transaction */ /************************************************************************* Checks that a transaction id is sensible, i.e., not in the future. */ Loading
innobase/include/row0mysql.h +3 −1 Original line number Diff line number Diff line Loading @@ -177,10 +177,12 @@ row_lock_table_for_mysql( /* out: error code or DB_SUCCESS */ row_prebuilt_t* prebuilt, /* in: prebuilt struct in the MySQL 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 locked as LOCK_TABLE_EXP | prebuilt->select_lock_type */ ulint mode); /* in: lock mode of table */ /************************************************************************* Does an insert for MySQL. */ Loading
innobase/lock/lock0lock.c +122 −13 Original line number Diff line number Diff line Loading @@ -395,19 +395,6 @@ lock_rec_get_nth_bit( 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) Loading Loading @@ -614,6 +601,128 @@ lock_get_wait( return(FALSE); } /************************************************************************* Gets the source table of an ALTER TABLE transaction. The table must be covered by an IX or IS table lock. */ dict_table_t* lock_get_src_table( /*===============*/ /* out: the source table of transaction, if it is covered by an IX or IS table lock; dest if there is no source table, and NULL if the transaction is locking more than two tables or an inconsistency is found */ trx_t* trx, /* in: transaction */ dict_table_t* dest, /* in: destination of ALTER TABLE */ ulint* mode) /* out: lock mode of the source table */ { dict_table_t* src; lock_t* lock; src = NULL; *mode = LOCK_NONE; for (lock = UT_LIST_GET_FIRST(trx->trx_locks); lock; lock = UT_LIST_GET_NEXT(trx_locks, lock)) { lock_table_t* tab_lock; ulint lock_mode; if (!(lock_get_type(lock) & LOCK_TABLE)) { /* We are only interested in table locks. */ continue; } tab_lock = &lock->un_member.tab_lock; if (dest == tab_lock->table) { /* We are not interested in the destination table. */ continue; } else if (!src) { /* This presumably is the source table. */ src = tab_lock->table; if (UT_LIST_GET_LEN(src->locks) != 1 || UT_LIST_GET_FIRST(src->locks) != lock) { /* We only support the case when there is only one lock on this table. */ return(NULL); } } else if (src != tab_lock->table) { /* The transaction is locking more than two tables (src and dest): abort */ return(NULL); } /* Check that the source table is locked by LOCK_IX or LOCK_IS. */ lock_mode = lock_get_mode(lock); switch (lock_mode) { case LOCK_IX: case LOCK_IS: if (*mode != LOCK_NONE && *mode != lock_mode) { /* There are multiple locks on src. */ return(NULL); } *mode = lock_mode; break; } } if (!src) { /* No source table lock found: flag the situation to caller */ src = dest; } return(src); } /************************************************************************* Determine if the given table is exclusively "owned" by the given transaction, i.e., transaction holds LOCK_IX and possibly LOCK_AUTO_INC on the table. */ ibool lock_is_table_exclusive( /*====================*/ /* out: TRUE if table is only locked by trx, with LOCK_IX, and possibly LOCK_AUTO_INC */ dict_table_t* table, /* in: table */ trx_t* trx) /* in: transaction */ { lock_t* lock; bool ok = FALSE; ut_ad(table && trx); for (lock = UT_LIST_GET_FIRST(table->locks); lock; lock = UT_LIST_GET_NEXT(locks, &lock->un_member.tab_lock)) { if (lock->trx != trx) { /* A lock on the table is held by some other transaction. */ return(FALSE); } if (!(lock_get_type(lock) & LOCK_TABLE)) { /* We are interested in table locks only. */ continue; } switch (lock_get_mode(lock)) { case LOCK_IX: ok = TRUE; break; case LOCK_AUTO_INC: /* It is allowed for trx to hold an auto_increment lock. */ break; default: /* Other table locks than LOCK_IX are not allowed. */ return(FALSE); } } return(ok); } /************************************************************************* Sets the wait flag of a lock and the back pointer in trx to lock. */ UNIV_INLINE Loading
innobase/row/row0mysql.c +3 −2 Original line number Diff line number Diff line Loading @@ -782,10 +782,11 @@ row_lock_table_for_mysql( /* out: error code or DB_SUCCESS */ row_prebuilt_t* prebuilt, /* in: prebuilt struct in the MySQL 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 locked as LOCK_TABLE_EXP | prebuilt->select_lock_type */ ulint mode) /* in: lock mode of table */ { trx_t* trx = prebuilt->trx; que_thr_t* thr; Loading Loading @@ -819,7 +820,7 @@ row_lock_table_for_mysql( trx_start_if_not_started(trx); if (table) { err = lock_table(0, table, LOCK_IX, thr); err = lock_table(0, table, mode, thr); } else { err = lock_table(LOCK_TABLE_EXP, prebuilt->table, prebuilt->select_lock_type, thr); Loading
sql/ha_innodb.cc +51 −12 Original line number Diff line number Diff line Loading @@ -2324,20 +2324,58 @@ ha_innobase::write_row( position in the source table need not be adjusted after the intermediate COMMIT, since writes by other transactions are 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 = lock_get_ix_table( UT_LIST_GET_FIRST(prebuilt->trx->trx_locks)); dict_table_t* src_table; ibool mode; num_write_row = 0; /* Commit the transaction. This will release the table locks, so they have to be acquired again. */ /* Altering an InnoDB table */ /* Get the source table. */ src_table = lock_get_src_table( prebuilt->trx, prebuilt->table, &mode); if (!src_table) { no_commit: /* Unknown situation: do not commit */ /* ut_print_timestamp(stderr); fprintf(stderr, " InnoDB error: ALTER TABLE is holding lock" " on %lu tables!\n", prebuilt->trx->mysql_n_tables_locked); */ ; } else if (src_table == prebuilt->table) { /* Source table is not in InnoDB format: no need to re-acquire locks on it. */ /* Altering to InnoDB format */ innobase_commit(user_thd, prebuilt->trx); /* Note that this transaction is still active. */ user_thd->transaction.all.innodb_active_trans = 1; /* Re-acquire the IX table lock on the source table. */ row_lock_table_for_mysql(prebuilt, table); /* We will need an IX lock on the destination table. */ prebuilt->sql_stat_start = TRUE; } else { /* Ensure that there are no other table locks than LOCK_IX and LOCK_AUTO_INC on the destination table. */ if (!lock_is_table_exclusive(prebuilt->table, prebuilt->trx)) { 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, src_table, mode); /* We will need an IX lock on the destination table. */ prebuilt->sql_stat_start = TRUE; } } num_write_row++; Loading Loading @@ -5015,7 +5053,8 @@ ha_innobase::external_lock( if (thd->in_lock_tables && thd->variables.innodb_table_locks) { 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) { error = convert_error_code_to_mysql( Loading