Loading innobase/include/row0mysql.h +14 −4 Original line number Diff line number Diff line Loading @@ -243,17 +243,27 @@ row_update_for_mysql( the MySQL format */ row_prebuilt_t* prebuilt); /* in: prebuilt struct in MySQL handle */ /************************************************************************* Does an unlock of a row for MySQL. */ This can only be used when srv_locks_unsafe_for_binlog is TRUE. Before calling this function we must use trx_reset_new_rec_lock_info() and trx_register_new_rec_lock() to store the information which new record locks really were set. This function removes a newly set lock under prebuilt->pcur, and also under prebuilt->clust_pcur. Currently, this is only used and tested in the case of an UPDATE or a DELETE statement, where the row lock is of the LOCK_X type. Thus, this implements a 'mini-rollback' that releases the latest record locks we set. */ int row_unlock_for_mysql( /*=================*/ /* out: error code or DB_SUCCESS */ row_prebuilt_t* prebuilt); /* in: prebuilt struct in MySQL row_prebuilt_t* prebuilt, /* in: prebuilt struct in MySQL handle */ ibool has_latches_on_recs);/* TRUE if called so that we have the latches on the records under pcur and clust_pcur, and we do not need to reposition the cursors. */ /************************************************************************* Creates an query graph node of 'update' type to be used in the MySQL interface. */ Loading innobase/include/trx0trx.h +41 −2 Original line number Diff line number Diff line Loading @@ -16,10 +16,39 @@ Created 3/26/1996 Heikki Tuuri #include "que0types.h" #include "mem0mem.h" #include "read0types.h" #include "dict0types.h" #include "trx0xa.h" extern ulint trx_n_mysql_transactions; /***************************************************************** Resets the new record lock info in a transaction struct. */ UNIV_INLINE void trx_reset_new_rec_lock_info( /*========================*/ trx_t* trx); /* in: transaction struct */ /***************************************************************** Registers that we have set a new record lock on an index. We only have space to store 2 indexes! If this is called to store more than 2 indexes after trx_reset_new_rec_lock_info(), then this function does nothing. */ UNIV_INLINE void trx_register_new_rec_lock( /*======================*/ trx_t* trx, /* in: transaction struct */ dict_index_t* index); /* in: trx sets a new record lock on this index */ /***************************************************************** Checks if trx has set a new record lock on an index. */ UNIV_INLINE ibool trx_new_rec_locks_contain( /*======================*/ /* out: TRUE if trx has set a new record lock on index */ trx_t* trx, /* in: transaction struct */ dict_index_t* index); /* in: index */ /************************************************************************ Releases the search latch if trx has reserved it. */ Loading Loading @@ -495,8 +524,18 @@ struct trx_struct{ lock_t* auto_inc_lock; /* possible auto-inc lock reserved by the transaction; note that it is also in the lock list trx_locks */ ibool trx_create_lock;/* this is TRUE if we have created a new lock for a record accessed */ dict_index_t* new_rec_locks[2];/* these are normally NULL; if srv_locks_unsafe_for_binlog is TRUE, in a cursor search, if we set a new record lock on an index, this is set to point to the index; this is used in releasing the locks under the cursors if we are performing an UPDATE and we determine after retrieving the row that it does not need to be locked; thus, these can be used to implement a 'mini-rollback' that releases the latest record locks */ UT_LIST_NODE_T(trx_t) trx_list; /* list of transactions */ UT_LIST_NODE_T(trx_t) Loading innobase/include/trx0trx.ic +56 −0 Original line number Diff line number Diff line Loading @@ -39,4 +39,60 @@ trx_start_if_not_started_low( } } /***************************************************************** Resets the new record lock info in a transaction struct. */ UNIV_INLINE void trx_reset_new_rec_lock_info( /*========================*/ trx_t* trx) /* in: transaction struct */ { trx->new_rec_locks[0] = NULL; trx->new_rec_locks[1] = NULL; } /***************************************************************** Registers that we have set a new record lock on an index. We only have space to store 2 indexes! If this is called to store more than 2 indexes after trx_reset_new_rec_lock_info(), then this function does nothing. */ UNIV_INLINE void trx_register_new_rec_lock( /*======================*/ trx_t* trx, /* in: transaction struct */ dict_index_t* index) /* in: trx sets a new record lock on this index */ { if (trx->new_rec_locks[0] == NULL) { trx->new_rec_locks[0] = index; return; } if (trx->new_rec_locks[0] == index) { return; } if (trx->new_rec_locks[1] != NULL) { return; } trx->new_rec_locks[1] = index; } /***************************************************************** Checks if trx has set a new record lock on an index. */ UNIV_INLINE ibool trx_new_rec_locks_contain( /*======================*/ /* out: TRUE if trx has set a new record lock on index */ trx_t* trx, /* in: transaction struct */ dict_index_t* index) /* in: index */ { return(trx->new_rec_locks[0] == index || trx->new_rec_locks[1] == index); } innobase/lock/lock0lock.c +32 −28 Original line number Diff line number Diff line Loading @@ -1766,9 +1766,6 @@ lock_rec_create( HASH_INSERT(lock_t, hash, lock_sys->rec_hash, lock_rec_fold(space, page_no), lock); /* Note that we have create a new lock */ trx->trx_create_lock = TRUE; if (type_mode & LOCK_WAIT) { lock_set_lock_and_trx_wait(lock, trx); Loading Loading @@ -1945,15 +1942,6 @@ lock_rec_add_to_queue( if (similar_lock && !somebody_waits && !(type_mode & LOCK_WAIT)) { /* If the nth bit of a record lock is already set then we do not set a new lock bit, otherwice we set */ if (lock_rec_get_nth_bit(similar_lock, heap_no)) { trx->trx_create_lock = FALSE; } else { trx->trx_create_lock = TRUE; } lock_rec_set_nth_bit(similar_lock, heap_no); return(similar_lock); Loading Loading @@ -2005,11 +1993,14 @@ lock_rec_lock_fast( lock = lock_rec_get_first_on_page(rec); trx = thr_get_trx(thr); trx->trx_create_lock = FALSE; if (lock == NULL) { if (!impl) { lock_rec_create(mode, rec, index, trx); if (srv_locks_unsafe_for_binlog) { trx_register_new_rec_lock(trx, index); } } return(TRUE); Loading @@ -2023,21 +2014,20 @@ lock_rec_lock_fast( if (lock->trx != trx || lock->type_mode != (mode | LOCK_REC) || lock_rec_get_n_bits(lock) <= heap_no) { return(FALSE); } if (!impl) { /* If the nth bit of the record lock is already set then we do not set a new lock bit, otherwise we do set */ /* If the nth bit of a record lock is already set then we do not set a new lock bit, otherwice we set */ if (lock_rec_get_nth_bit(lock, heap_no)) { trx->trx_create_lock = FALSE; } else { trx->trx_create_lock = TRUE; } if (!lock_rec_get_nth_bit(lock, heap_no)) { lock_rec_set_nth_bit(lock, heap_no); if (srv_locks_unsafe_for_binlog) { trx_register_new_rec_lock(trx, index); } } } return(TRUE); Loading Loading @@ -2093,12 +2083,19 @@ lock_rec_lock_slow( enough already granted on the record, we have to wait. */ err = lock_rec_enqueue_waiting(mode, rec, index, thr); if (srv_locks_unsafe_for_binlog) { trx_register_new_rec_lock(trx, index); } } else { if (!impl) { /* Set the requested lock on the record */ lock_rec_add_to_queue(LOCK_REC | mode, rec, index, trx); if (srv_locks_unsafe_for_binlog) { trx_register_new_rec_lock(trx, index); } } err = DB_SUCCESS; Loading Loading @@ -2436,8 +2433,15 @@ lock_rec_inherit_to_gap( lock = lock_rec_get_first(rec); /* If srv_locks_unsafe_for_binlog is TRUE, we do not want locks set by an UPDATE or a DELETE to be inherited as gap type locks. But we DO want S-locks set by a consistency constraint to be inherited also then. */ while (lock != NULL) { if (!lock_rec_get_insert_intention(lock)) { if (!lock_rec_get_insert_intention(lock) && !(srv_locks_unsafe_for_binlog && lock_get_mode(lock) == LOCK_X)) { lock_rec_add_to_queue(LOCK_REC | lock_get_mode(lock) | LOCK_GAP, Loading innobase/row/row0mysql.c +79 −24 Original line number Diff line number Diff line Loading @@ -1429,51 +1429,106 @@ row_update_for_mysql( } /************************************************************************* Does an unlock of a row for MySQL. */ This can only be used when srv_locks_unsafe_for_binlog is TRUE. Before calling this function we must use trx_reset_new_rec_lock_info() and trx_register_new_rec_lock() to store the information which new record locks really were set. This function removes a newly set lock under prebuilt->pcur, and also under prebuilt->clust_pcur. Currently, this is only used and tested in the case of an UPDATE or a DELETE statement, where the row lock is of the LOCK_X type. Thus, this implements a 'mini-rollback' that releases the latest record locks we set. */ int row_unlock_for_mysql( /*=================*/ /* out: error code or DB_SUCCESS */ row_prebuilt_t* prebuilt) /* in: prebuilt struct in MySQL row_prebuilt_t* prebuilt, /* in: prebuilt struct in MySQL handle */ ibool has_latches_on_recs)/* TRUE if called so that we have the latches on the records under pcur and clust_pcur, and we do not need to reposition the cursors. */ { rec_t* rec; btr_pcur_t* cur = prebuilt->pcur; dict_index_t* index; btr_pcur_t* pcur = prebuilt->pcur; btr_pcur_t* clust_pcur = prebuilt->clust_pcur; trx_t* trx = prebuilt->trx; rec_t* rec; mtr_t mtr; ut_ad(prebuilt && trx); ut_ad(trx->mysql_thread_id == os_thread_get_curr_id()); if (!srv_locks_unsafe_for_binlog) { fprintf(stderr, "InnoDB: Error: calling row_unlock_for_mysql though\n" "InnoDB: srv_locks_unsafe_for_binlog is FALSE.\n"); return(DB_SUCCESS); } trx->op_info = "unlock_row"; if (srv_locks_unsafe_for_binlog) { if (trx->trx_create_lock == TRUE) { index = btr_pcur_get_btr_cur(pcur)->index; if (index != NULL && trx_new_rec_locks_contain(trx, index)) { mtr_start(&mtr); /* Restore a cursor position and find a record */ btr_pcur_restore_position(BTR_SEARCH_LEAF, cur, &mtr); rec = btr_pcur_get_rec(cur); /* Restore the cursor position and find the record */ if (!has_latches_on_recs) { btr_pcur_restore_position(BTR_SEARCH_LEAF, pcur, &mtr); } rec = btr_pcur_get_rec(pcur); if (rec) { mutex_enter(&kernel_mutex); lock_rec_reset_and_release_wait(rec); } else { fputs("InnoDB: Error: " "Record for the lock not found\n", stderr); mem_analyze_corruption((byte*) trx); ut_error; } trx->trx_create_lock = FALSE; mutex_exit(&kernel_mutex); mtr_commit(&mtr); /* If the search was done through the clustered index, then we have not used clust_pcur at all, and we must NOT try to reset locks on clust_pcur. The values in clust_pcur may be garbage! */ if (index->type & DICT_CLUSTERED) { goto func_exit; } } index = btr_pcur_get_btr_cur(clust_pcur)->index; if (index != NULL && trx_new_rec_locks_contain(trx, index)) { mtr_start(&mtr); /* Restore the cursor position and find the record */ if (!has_latches_on_recs) { btr_pcur_restore_position(BTR_SEARCH_LEAF, clust_pcur, &mtr); } rec = btr_pcur_get_rec(clust_pcur); mutex_enter(&kernel_mutex); lock_rec_reset_and_release_wait(rec); mutex_exit(&kernel_mutex); mtr_commit(&mtr); } func_exit: trx->op_info = ""; return(DB_SUCCESS); Loading Loading
innobase/include/row0mysql.h +14 −4 Original line number Diff line number Diff line Loading @@ -243,17 +243,27 @@ row_update_for_mysql( the MySQL format */ row_prebuilt_t* prebuilt); /* in: prebuilt struct in MySQL handle */ /************************************************************************* Does an unlock of a row for MySQL. */ This can only be used when srv_locks_unsafe_for_binlog is TRUE. Before calling this function we must use trx_reset_new_rec_lock_info() and trx_register_new_rec_lock() to store the information which new record locks really were set. This function removes a newly set lock under prebuilt->pcur, and also under prebuilt->clust_pcur. Currently, this is only used and tested in the case of an UPDATE or a DELETE statement, where the row lock is of the LOCK_X type. Thus, this implements a 'mini-rollback' that releases the latest record locks we set. */ int row_unlock_for_mysql( /*=================*/ /* out: error code or DB_SUCCESS */ row_prebuilt_t* prebuilt); /* in: prebuilt struct in MySQL row_prebuilt_t* prebuilt, /* in: prebuilt struct in MySQL handle */ ibool has_latches_on_recs);/* TRUE if called so that we have the latches on the records under pcur and clust_pcur, and we do not need to reposition the cursors. */ /************************************************************************* Creates an query graph node of 'update' type to be used in the MySQL interface. */ Loading
innobase/include/trx0trx.h +41 −2 Original line number Diff line number Diff line Loading @@ -16,10 +16,39 @@ Created 3/26/1996 Heikki Tuuri #include "que0types.h" #include "mem0mem.h" #include "read0types.h" #include "dict0types.h" #include "trx0xa.h" extern ulint trx_n_mysql_transactions; /***************************************************************** Resets the new record lock info in a transaction struct. */ UNIV_INLINE void trx_reset_new_rec_lock_info( /*========================*/ trx_t* trx); /* in: transaction struct */ /***************************************************************** Registers that we have set a new record lock on an index. We only have space to store 2 indexes! If this is called to store more than 2 indexes after trx_reset_new_rec_lock_info(), then this function does nothing. */ UNIV_INLINE void trx_register_new_rec_lock( /*======================*/ trx_t* trx, /* in: transaction struct */ dict_index_t* index); /* in: trx sets a new record lock on this index */ /***************************************************************** Checks if trx has set a new record lock on an index. */ UNIV_INLINE ibool trx_new_rec_locks_contain( /*======================*/ /* out: TRUE if trx has set a new record lock on index */ trx_t* trx, /* in: transaction struct */ dict_index_t* index); /* in: index */ /************************************************************************ Releases the search latch if trx has reserved it. */ Loading Loading @@ -495,8 +524,18 @@ struct trx_struct{ lock_t* auto_inc_lock; /* possible auto-inc lock reserved by the transaction; note that it is also in the lock list trx_locks */ ibool trx_create_lock;/* this is TRUE if we have created a new lock for a record accessed */ dict_index_t* new_rec_locks[2];/* these are normally NULL; if srv_locks_unsafe_for_binlog is TRUE, in a cursor search, if we set a new record lock on an index, this is set to point to the index; this is used in releasing the locks under the cursors if we are performing an UPDATE and we determine after retrieving the row that it does not need to be locked; thus, these can be used to implement a 'mini-rollback' that releases the latest record locks */ UT_LIST_NODE_T(trx_t) trx_list; /* list of transactions */ UT_LIST_NODE_T(trx_t) Loading
innobase/include/trx0trx.ic +56 −0 Original line number Diff line number Diff line Loading @@ -39,4 +39,60 @@ trx_start_if_not_started_low( } } /***************************************************************** Resets the new record lock info in a transaction struct. */ UNIV_INLINE void trx_reset_new_rec_lock_info( /*========================*/ trx_t* trx) /* in: transaction struct */ { trx->new_rec_locks[0] = NULL; trx->new_rec_locks[1] = NULL; } /***************************************************************** Registers that we have set a new record lock on an index. We only have space to store 2 indexes! If this is called to store more than 2 indexes after trx_reset_new_rec_lock_info(), then this function does nothing. */ UNIV_INLINE void trx_register_new_rec_lock( /*======================*/ trx_t* trx, /* in: transaction struct */ dict_index_t* index) /* in: trx sets a new record lock on this index */ { if (trx->new_rec_locks[0] == NULL) { trx->new_rec_locks[0] = index; return; } if (trx->new_rec_locks[0] == index) { return; } if (trx->new_rec_locks[1] != NULL) { return; } trx->new_rec_locks[1] = index; } /***************************************************************** Checks if trx has set a new record lock on an index. */ UNIV_INLINE ibool trx_new_rec_locks_contain( /*======================*/ /* out: TRUE if trx has set a new record lock on index */ trx_t* trx, /* in: transaction struct */ dict_index_t* index) /* in: index */ { return(trx->new_rec_locks[0] == index || trx->new_rec_locks[1] == index); }
innobase/lock/lock0lock.c +32 −28 Original line number Diff line number Diff line Loading @@ -1766,9 +1766,6 @@ lock_rec_create( HASH_INSERT(lock_t, hash, lock_sys->rec_hash, lock_rec_fold(space, page_no), lock); /* Note that we have create a new lock */ trx->trx_create_lock = TRUE; if (type_mode & LOCK_WAIT) { lock_set_lock_and_trx_wait(lock, trx); Loading Loading @@ -1945,15 +1942,6 @@ lock_rec_add_to_queue( if (similar_lock && !somebody_waits && !(type_mode & LOCK_WAIT)) { /* If the nth bit of a record lock is already set then we do not set a new lock bit, otherwice we set */ if (lock_rec_get_nth_bit(similar_lock, heap_no)) { trx->trx_create_lock = FALSE; } else { trx->trx_create_lock = TRUE; } lock_rec_set_nth_bit(similar_lock, heap_no); return(similar_lock); Loading Loading @@ -2005,11 +1993,14 @@ lock_rec_lock_fast( lock = lock_rec_get_first_on_page(rec); trx = thr_get_trx(thr); trx->trx_create_lock = FALSE; if (lock == NULL) { if (!impl) { lock_rec_create(mode, rec, index, trx); if (srv_locks_unsafe_for_binlog) { trx_register_new_rec_lock(trx, index); } } return(TRUE); Loading @@ -2023,21 +2014,20 @@ lock_rec_lock_fast( if (lock->trx != trx || lock->type_mode != (mode | LOCK_REC) || lock_rec_get_n_bits(lock) <= heap_no) { return(FALSE); } if (!impl) { /* If the nth bit of the record lock is already set then we do not set a new lock bit, otherwise we do set */ /* If the nth bit of a record lock is already set then we do not set a new lock bit, otherwice we set */ if (lock_rec_get_nth_bit(lock, heap_no)) { trx->trx_create_lock = FALSE; } else { trx->trx_create_lock = TRUE; } if (!lock_rec_get_nth_bit(lock, heap_no)) { lock_rec_set_nth_bit(lock, heap_no); if (srv_locks_unsafe_for_binlog) { trx_register_new_rec_lock(trx, index); } } } return(TRUE); Loading Loading @@ -2093,12 +2083,19 @@ lock_rec_lock_slow( enough already granted on the record, we have to wait. */ err = lock_rec_enqueue_waiting(mode, rec, index, thr); if (srv_locks_unsafe_for_binlog) { trx_register_new_rec_lock(trx, index); } } else { if (!impl) { /* Set the requested lock on the record */ lock_rec_add_to_queue(LOCK_REC | mode, rec, index, trx); if (srv_locks_unsafe_for_binlog) { trx_register_new_rec_lock(trx, index); } } err = DB_SUCCESS; Loading Loading @@ -2436,8 +2433,15 @@ lock_rec_inherit_to_gap( lock = lock_rec_get_first(rec); /* If srv_locks_unsafe_for_binlog is TRUE, we do not want locks set by an UPDATE or a DELETE to be inherited as gap type locks. But we DO want S-locks set by a consistency constraint to be inherited also then. */ while (lock != NULL) { if (!lock_rec_get_insert_intention(lock)) { if (!lock_rec_get_insert_intention(lock) && !(srv_locks_unsafe_for_binlog && lock_get_mode(lock) == LOCK_X)) { lock_rec_add_to_queue(LOCK_REC | lock_get_mode(lock) | LOCK_GAP, Loading
innobase/row/row0mysql.c +79 −24 Original line number Diff line number Diff line Loading @@ -1429,51 +1429,106 @@ row_update_for_mysql( } /************************************************************************* Does an unlock of a row for MySQL. */ This can only be used when srv_locks_unsafe_for_binlog is TRUE. Before calling this function we must use trx_reset_new_rec_lock_info() and trx_register_new_rec_lock() to store the information which new record locks really were set. This function removes a newly set lock under prebuilt->pcur, and also under prebuilt->clust_pcur. Currently, this is only used and tested in the case of an UPDATE or a DELETE statement, where the row lock is of the LOCK_X type. Thus, this implements a 'mini-rollback' that releases the latest record locks we set. */ int row_unlock_for_mysql( /*=================*/ /* out: error code or DB_SUCCESS */ row_prebuilt_t* prebuilt) /* in: prebuilt struct in MySQL row_prebuilt_t* prebuilt, /* in: prebuilt struct in MySQL handle */ ibool has_latches_on_recs)/* TRUE if called so that we have the latches on the records under pcur and clust_pcur, and we do not need to reposition the cursors. */ { rec_t* rec; btr_pcur_t* cur = prebuilt->pcur; dict_index_t* index; btr_pcur_t* pcur = prebuilt->pcur; btr_pcur_t* clust_pcur = prebuilt->clust_pcur; trx_t* trx = prebuilt->trx; rec_t* rec; mtr_t mtr; ut_ad(prebuilt && trx); ut_ad(trx->mysql_thread_id == os_thread_get_curr_id()); if (!srv_locks_unsafe_for_binlog) { fprintf(stderr, "InnoDB: Error: calling row_unlock_for_mysql though\n" "InnoDB: srv_locks_unsafe_for_binlog is FALSE.\n"); return(DB_SUCCESS); } trx->op_info = "unlock_row"; if (srv_locks_unsafe_for_binlog) { if (trx->trx_create_lock == TRUE) { index = btr_pcur_get_btr_cur(pcur)->index; if (index != NULL && trx_new_rec_locks_contain(trx, index)) { mtr_start(&mtr); /* Restore a cursor position and find a record */ btr_pcur_restore_position(BTR_SEARCH_LEAF, cur, &mtr); rec = btr_pcur_get_rec(cur); /* Restore the cursor position and find the record */ if (!has_latches_on_recs) { btr_pcur_restore_position(BTR_SEARCH_LEAF, pcur, &mtr); } rec = btr_pcur_get_rec(pcur); if (rec) { mutex_enter(&kernel_mutex); lock_rec_reset_and_release_wait(rec); } else { fputs("InnoDB: Error: " "Record for the lock not found\n", stderr); mem_analyze_corruption((byte*) trx); ut_error; } trx->trx_create_lock = FALSE; mutex_exit(&kernel_mutex); mtr_commit(&mtr); /* If the search was done through the clustered index, then we have not used clust_pcur at all, and we must NOT try to reset locks on clust_pcur. The values in clust_pcur may be garbage! */ if (index->type & DICT_CLUSTERED) { goto func_exit; } } index = btr_pcur_get_btr_cur(clust_pcur)->index; if (index != NULL && trx_new_rec_locks_contain(trx, index)) { mtr_start(&mtr); /* Restore the cursor position and find the record */ if (!has_latches_on_recs) { btr_pcur_restore_position(BTR_SEARCH_LEAF, clust_pcur, &mtr); } rec = btr_pcur_get_rec(clust_pcur); mutex_enter(&kernel_mutex); lock_rec_reset_and_release_wait(rec); mutex_exit(&kernel_mutex); mtr_commit(&mtr); } func_exit: trx->op_info = ""; return(DB_SUCCESS); Loading