lock0lock.c
上传用户:romrleung
上传日期:2022-05-23
资源大小:18897k
文件大小:131k
- /******************************************************
- The transaction lock system
- (c) 1996 Innobase Oy
- Created 5/7/1996 Heikki Tuuri
- *******************************************************/
- #include "lock0lock.h"
- #ifdef UNIV_NONINL
- #include "lock0lock.ic"
- #endif
- #include "usr0sess.h"
- #include "trx0purge.h"
- #include "dict0mem.h"
- #include "trx0sys.h"
- /* 2 function prototypes copied from ha_innodb.cc: */
- /*****************************************************************
- If you want to print a thd that is not associated with the current thread,
- you must call this function before reserving the InnoDB kernel_mutex, to
- protect MySQL from setting thd->query NULL. If you print a thd of the current
- thread, we know that MySQL cannot modify thd->query, and it is not necessary
- to call this. Call innobase_mysql_end_print_arbitrary_thd() after you release
- the kernel_mutex.
- NOTE that /mysql/innobase/lock/lock0lock.c must contain the prototype for this
- function! */
- void
- innobase_mysql_prepare_print_arbitrary_thd(void);
- /*============================================*/
- /*****************************************************************
- Relases the mutex reserved by innobase_mysql_prepare_print_arbitrary_thd().
- NOTE that /mysql/innobase/lock/lock0lock.c must contain the prototype for this
- function! */
- void
- innobase_mysql_end_print_arbitrary_thd(void);
- /*========================================*/
- /* Restricts the length of search we will do in the waits-for
- graph of transactions */
- #define LOCK_MAX_N_STEPS_IN_DEADLOCK_CHECK 1000000
- /* When releasing transaction locks, this specifies how often we release
- the kernel mutex for a moment to give also others access to it */
- #define LOCK_RELEASE_KERNEL_INTERVAL 1000
- /* Safety margin when creating a new record lock: this many extra records
- can be inserted to the page without need to create a lock with a bigger
- bitmap */
- #define LOCK_PAGE_BITMAP_MARGIN 64
- /* An explicit record lock affects both the record and the gap before it.
- An implicit x-lock does not affect the gap, it only locks the index
- record from read or update.
- If a transaction has modified or inserted an index record, then
- it owns an implicit x-lock on the record. On a secondary index record,
- a transaction has an implicit x-lock also if it has modified the
- clustered index record, the max trx id of the page where the secondary
- index record resides is >= trx id of the transaction (or database recovery
- is running), and there are no explicit non-gap lock requests on the
- secondary index record.
- This complicated definition for a secondary index comes from the
- implementation: we want to be able to determine if a secondary index
- record has an implicit x-lock, just by looking at the present clustered
- index record, not at the historical versions of the record. The
- complicated definition can be explained to the user so that there is
- nondeterminism in the access path when a query is answered: we may,
- or may not, access the clustered index record and thus may, or may not,
- bump into an x-lock set there.
- Different transaction can have conflicting locks set on the gap at the
- same time. The locks on the gap are purely inhibitive: an insert cannot
- be made, or a select cursor may have to wait if a different transaction
- has a conflicting lock on the gap. An x-lock on the gap does not give
- the right to insert into the gap.
- An explicit lock can be placed on a user record or the supremum record of
- a page. The locks on the supremum record are always thought to be of the gap
- type, though the gap bit is not set. When we perform an update of a record
- where the size of the record changes, we may temporarily store its explicit
- locks on the infimum record of the page, though the infimum otherwise never
- carries locks.
- A waiting record lock can also be of the gap type. A waiting lock request
- can be granted when there is no conflicting mode lock request by another
- transaction ahead of it in the explicit lock queue.
- In version 4.0.5 we added yet another explicit lock type: LOCK_REC_NOT_GAP.
- It only locks the record it is placed on, not the gap before the record.
- This lock type is necessary to emulate an Oracle-like READ COMMITTED isolation
- level.
- -------------------------------------------------------------------------
- RULE 1: If there is an implicit x-lock on a record, and there are non-gap
- -------
- lock requests waiting in the queue, then the transaction holding the implicit
- x-lock also has an explicit non-gap record x-lock. Therefore, as locks are
- released, we can grant locks to waiting lock requests purely by looking at
- the explicit lock requests in the queue.
- RULE 3: Different transactions cannot have conflicting granted non-gap locks
- -------
- on a record at the same time. However, they can have conflicting granted gap
- locks.
- RULE 4: If a there is a waiting lock request in a queue, no lock request,
- -------
- gap or not, can be inserted ahead of it in the queue. In record deletes
- and page splits new gap type locks can be created by the database manager
- for a transaction, and without rule 4, the waits-for graph of transactions
- might become cyclic without the database noticing it, as the deadlock check
- is only performed when a transaction itself requests a lock!
- -------------------------------------------------------------------------
- An insert is allowed to a gap if there are no explicit lock requests by
- other transactions on the next record. It does not matter if these lock
- requests are granted or waiting, gap bit set or not, with the exception
- that a gap type request set by another transaction to wait for
- its turn to do an insert is ignored. On the other hand, an
- implicit x-lock by another transaction does not prevent an insert, which
- allows for more concurrency when using an Oracle-style sequence number
- generator for the primary key with many transactions doing inserts
- concurrently.
- A modify of a record is allowed if the transaction has an x-lock on the
- record, or if other transactions do not have any non-gap lock requests on the
- record.
- A read of a single user record with a cursor is allowed if the transaction
- has a non-gap explicit, or an implicit lock on the record, or if the other
- transactions have no x-lock requests on the record. At a page supremum a
- read is always allowed.
- In summary, an implicit lock is seen as a granted x-lock only on the
- record, not on the gap. An explicit lock with no gap bit set is a lock
- both on the record and the gap. If the gap bit is set, the lock is only
- on the gap. Different transaction cannot own conflicting locks on the
- record at the same time, but they may own conflicting locks on the gap.
- Granted locks on a record give an access right to the record, but gap type
- locks just inhibit operations.
- NOTE: Finding out if some transaction has an implicit x-lock on a secondary
- index record can be cumbersome. We may have to look at previous versions of
- the corresponding clustered index record to find out if a delete marked
- secondary index record was delete marked by an active transaction, not by
- a committed one.
- FACT A: If a transaction has inserted a row, it can delete it any time
- without need to wait for locks.
- PROOF: The transaction has an implicit x-lock on every index record inserted
- for the row, and can thus modify each record without the need to wait. Q.E.D.
- FACT B: If a transaction has read some result set with a cursor, it can read
- it again, and retrieves the same result set, if it has not modified the
- result set in the meantime. Hence, there is no phantom problem. If the
- biggest record, in the alphabetical order, touched by the cursor is removed,
- a lock wait may occur, otherwise not.
- PROOF: When a read cursor proceeds, it sets an s-lock on each user record
- it passes, and a gap type s-lock on each page supremum. The cursor must
- wait until it has these locks granted. Then no other transaction can
- have a granted x-lock on any of the user records, and therefore cannot
- modify the user records. Neither can any other transaction insert into
- the gaps which were passed over by the cursor. Page splits and merges,
- and removal of obsolete versions of records do not affect this, because
- when a user record or a page supremum is removed, the next record inherits
- its locks as gap type locks, and therefore blocks inserts to the same gap.
- Also, if a page supremum is inserted, it inherits its locks from the successor
- record. When the cursor is positioned again at the start of the result set,
- the records it will touch on its course are either records it touched
- during the last pass or new inserted page supremums. It can immediately
- access all these records, and when it arrives at the biggest record, it
- notices that the result set is complete. If the biggest record was removed,
- lock wait can occur because the next record only inherits a gap type lock,
- and a wait may be needed. Q.E.D. */
- /* If an index record should be changed or a new inserted, we must check
- the lock on the record or the next. When a read cursor starts reading,
- we will set a record level s-lock on each record it passes, except on the
- initial record on which the cursor is positioned before we start to fetch
- records. Our index tree search has the convention that the B-tree
- cursor is positioned BEFORE the first possibly matching record in
- the search. Optimizations are possible here: if the record is searched
- on an equality condition to a unique key, we could actually set a special
- lock on the record, a lock which would not prevent any insert before
- this record. In the next key locking an x-lock set on a record also
- prevents inserts just before that record.
- There are special infimum and supremum records on each page.
- A supremum record can be locked by a read cursor. This records cannot be
- updated but the lock prevents insert of a user record to the end of
- the page.
- Next key locks will prevent the phantom problem where new rows
- could appear to SELECT result sets after the select operation has been
- performed. Prevention of phantoms ensures the serilizability of
- transactions.
- What should we check if an insert of a new record is wanted?
- Only the lock on the next record on the same page, because also the
- supremum record can carry a lock. An s-lock prevents insertion, but
- what about an x-lock? If it was set by a searched update, then there
- is implicitly an s-lock, too, and the insert should be prevented.
- What if our transaction owns an x-lock to the next record, but there is
- a waiting s-lock request on the next record? If this s-lock was placed
- by a read cursor moving in the ascending order in the index, we cannot
- do the insert immediately, because when we finally commit our transaction,
- the read cursor should see also the new inserted record. So we should
- move the read cursor backward from the the next record for it to pass over
- the new inserted record. This move backward may be too cumbersome to
- implement. If we in this situation just enqueue a second x-lock request
- for our transaction on the next record, then the deadlock mechanism
- notices a deadlock between our transaction and the s-lock request
- transaction. This seems to be an ok solution.
- We could have the convention that granted explicit record locks,
- lock the corresponding records from changing, and also lock the gaps
- before them from inserting. A waiting explicit lock request locks the gap
- before from inserting. Implicit record x-locks, which we derive from the
- transaction id in the clustered index record, only lock the record itself
- from modification, not the gap before it from inserting.
- How should we store update locks? If the search is done by a unique
- key, we could just modify the record trx id. Otherwise, we could put a record
- x-lock on the record. If the update changes ordering fields of the
- clustered index record, the inserted new record needs no record lock in
- lock table, the trx id is enough. The same holds for a secondary index
- record. Searched delete is similar to update.
- PROBLEM:
- What about waiting lock requests? If a transaction is waiting to make an
- update to a record which another modified, how does the other transaction
- know to send the end-lock-wait signal to the waiting transaction? If we have
- the convention that a transaction may wait for just one lock at a time, how
- do we preserve it if lock wait ends?
- PROBLEM:
- Checking the trx id label of a secondary index record. In the case of a
- modification, not an insert, is this necessary? A secondary index record
- is modified only by setting or resetting its deleted flag. A secondary index
- record contains fields to uniquely determine the corresponding clustered
- index record. A secondary index record is therefore only modified if we
- also modify the clustered index record, and the trx id checking is done
- on the clustered index record, before we come to modify the secondary index
- record. So, in the case of delete marking or unmarking a secondary index
- record, we do not have to care about trx ids, only the locks in the lock
- table must be checked. In the case of a select from a secondary index, the
- trx id is relevant, and in this case we may have to search the clustered
- index record.
- PROBLEM: How to update record locks when page is split or merged, or
- --------------------------------------------------------------------
- a record is deleted or updated?
- If the size of fields in a record changes, we perform the update by
- a delete followed by an insert. How can we retain the locks set or
- waiting on the record? Because a record lock is indexed in the bitmap
- by the heap number of the record, when we remove the record from the
- record list, it is possible still to keep the lock bits. If the page
- is reorganized, we could make a table of old and new heap numbers,
- and permute the bitmaps in the locks accordingly. We can add to the
- table a row telling where the updated record ended. If the update does
- not require a reorganization of the page, we can simply move the lock
- bits for the updated record to the position determined by its new heap
- number (we may have to allocate a new lock, if we run out of the bitmap
- in the old one).
- A more complicated case is the one where the reinsertion of the
- updated record is done pessimistically, because the structure of the
- tree may change.
- PROBLEM: If a supremum record is removed in a page merge, or a record
- ---------------------------------------------------------------------
- removed in a purge, what to do to the waiting lock requests? In a split to
- the right, we just move the lock requests to the new supremum. If a record
- is removed, we could move the waiting lock request to its inheritor, the
- next record in the index. But, the next record may already have lock
- requests on its own queue. A new deadlock check should be made then. Maybe
- it is easier just to release the waiting transactions. They can then enqueue
- new lock requests on appropriate records.
- PROBLEM: When a record is inserted, what locks should it inherit from the
- -------------------------------------------------------------------------
- upper neighbor? An insert of a new supremum record in a page split is
- always possible, but an insert of a new user record requires that the upper
- neighbor does not have any lock requests by other transactions, granted or
- waiting, in its lock queue. Solution: We can copy the locks as gap type
- locks, so that also the waiting locks are transformed to granted gap type
- locks on the inserted record. */
- ibool lock_print_waits = FALSE;
- /* The lock system */
- lock_sys_t* lock_sys = NULL;
- /* A table lock */
- typedef struct lock_table_struct lock_table_t;
- struct lock_table_struct{
- dict_table_t* table; /* database table in dictionary cache */
- UT_LIST_NODE_T(lock_t)
- locks; /* list of locks on the same table */
- };
- /* Record lock for a page */
- typedef struct lock_rec_struct lock_rec_t;
- struct lock_rec_struct{
- ulint space; /* space id */
- ulint page_no; /* page number */
- ulint n_bits; /* number of bits in the lock bitmap */
- /* NOTE: the lock bitmap is placed immediately
- after the lock struct */
- };
- /* Lock struct */
- struct lock_struct{
- trx_t* trx; /* transaction owning the lock */
- UT_LIST_NODE_T(lock_t)
- trx_locks; /* list of the locks of the
- transaction */
- ulint type_mode; /* lock type, mode, LOCK_GAP or
- LOCK_REC_NOT_GAP,
- LOCK_INSERT_INTENTION,
- wait flag, ORed */
- hash_node_t hash; /* hash chain node for a record lock */
- dict_index_t* index; /* index for a record lock */
- union {
- lock_table_t tab_lock;/* table lock */
- lock_rec_t rec_lock;/* record lock */
- } un_member;
- };
- /* We store info on the latest deadlock error to this buffer. InnoDB
- Monitor will then fetch it and print */
- ibool lock_deadlock_found = FALSE;
- FILE* lock_latest_err_file;
- /* Flags for recursive deadlock search */
- #define LOCK_VICTIM_IS_START 1
- #define LOCK_VICTIM_IS_OTHER 2
- /************************************************************************
- Checks if a lock request results in a deadlock. */
- static
- ibool
- lock_deadlock_occurs(
- /*=================*/
- /* out: TRUE if a deadlock was detected */
- lock_t* lock, /* in: lock the transaction is requesting */
- trx_t* trx); /* in: transaction */
- /************************************************************************
- Looks recursively for a deadlock. */
- static
- ibool
- lock_deadlock_recursive(
- /*====================*/
- /* out: TRUE if a deadlock was detected
- or the calculation took too long */
- trx_t* start, /* in: recursion starting point */
- trx_t* trx, /* in: a transaction waiting for a lock */
- lock_t* wait_lock, /* in: the lock trx is waiting to be granted */
- ulint* cost); /* in/out: number of calculation steps thus
- far: if this exceeds LOCK_MAX_N_STEPS_...
- we return TRUE */
- /*************************************************************************
- Gets the type of a lock. */
- UNIV_INLINE
- ulint
- lock_get_type(
- /*==========*/
- /* out: LOCK_TABLE or LOCK_REC */
- lock_t* lock) /* in: lock */
- {
- ut_ad(lock);
- return(lock->type_mode & LOCK_TYPE_MASK);
- }
- /*************************************************************************
- Gets the nth bit of a record lock. */
- UNIV_INLINE
- ibool
- lock_rec_get_nth_bit(
- /*=================*/
- /* out: TRUE if bit set */
- lock_t* lock, /* in: record lock */
- ulint i) /* in: index of the bit */
- {
- ulint byte_index;
- ulint bit_index;
- ulint b;
- ut_ad(lock);
- ut_ad(lock_get_type(lock) == LOCK_REC);
- if (i >= lock->un_member.rec_lock.n_bits) {
- return(FALSE);
- }
- byte_index = i / 8;
- bit_index = i % 8;
- b = (ulint)*((byte*)lock + sizeof(lock_t) + byte_index);
- return(ut_bit_get_nth(b, bit_index));
- }
- /*************************************************************************/
- #define lock_mutex_enter_kernel() mutex_enter(&kernel_mutex)
- #define lock_mutex_exit_kernel() mutex_exit(&kernel_mutex)
- /*************************************************************************
- Checks that a transaction id is sensible, i.e., not in the future. */
- ibool
- lock_check_trx_id_sanity(
- /*=====================*/
- /* out: TRUE if ok */
- dulint trx_id, /* in: trx id */
- rec_t* rec, /* in: user record */
- dict_index_t* index, /* in: clustered index */
- ibool has_kernel_mutex)/* in: TRUE if the caller owns the
- kernel mutex */
- {
- ibool is_ok = TRUE;
-
- if (!has_kernel_mutex) {
- mutex_enter(&kernel_mutex);
- }
- /* A sanity check: the trx_id in rec must be smaller than the global
- trx id counter */
- if (ut_dulint_cmp(trx_id, trx_sys->max_trx_id) >= 0) {
- ut_print_timestamp(stderr);
- fputs(" InnoDB: Error: transaction id associated"
- " with recordn",
- stderr);
- rec_print(stderr, rec);
- fputs("InnoDB: in ", stderr);
- dict_index_name_print(stderr, NULL, index);
- fprintf(stderr, "n"
- "InnoDB: is %lu %lu which is higher than the global trx id counter %lu %lu!n"
- "InnoDB: The table is corrupt. You have to do dump + drop + reimport.n",
- (ulong) ut_dulint_get_high(trx_id),
- (ulong) ut_dulint_get_low(trx_id),
- (ulong) ut_dulint_get_high(trx_sys->max_trx_id),
- (ulong) ut_dulint_get_low(trx_sys->max_trx_id));
- is_ok = FALSE;
- }
-
- if (!has_kernel_mutex) {
- mutex_exit(&kernel_mutex);
- }
- return(is_ok);
- }
- /*************************************************************************
- Checks that a record is seen in a consistent read. */
- ibool
- lock_clust_rec_cons_read_sees(
- /*==========================*/
- /* out: TRUE if sees, or FALSE if an earlier
- version of the record should be retrieved */
- rec_t* rec, /* in: user record which should be read or
- passed over by a read cursor */
- dict_index_t* index, /* in: clustered index */
- read_view_t* view) /* in: consistent read view */
- {
- dulint trx_id;
- ut_ad(index->type & DICT_CLUSTERED);
- ut_ad(page_rec_is_user_rec(rec));
- /* NOTE that we call this function while holding the search
- system latch. To obey the latching order we must NOT reserve the
- kernel mutex here! */
- trx_id = row_get_rec_trx_id(rec, index);
-
- if (read_view_sees_trx_id(view, trx_id)) {
- return(TRUE);
- }
- return(FALSE);
- }
- /*************************************************************************
- Checks that a non-clustered index record is seen in a consistent read. */
- ulint
- lock_sec_rec_cons_read_sees(
- /*========================*/
- /* out: TRUE if certainly sees, or FALSE if an
- earlier version of the clustered index record
- might be needed: NOTE that a non-clustered
- index page contains so little information on
- its modifications that also in the case FALSE,
- the present version of rec may be the right,
- but we must check this from the clustered
- index record */
- rec_t* rec, /* in: user record which should be read or
- passed over by a read cursor */
- dict_index_t* index, /* in: non-clustered index */
- read_view_t* view) /* in: consistent read view */
- {
- dulint max_trx_id;
-
- UT_NOT_USED(index);
-
- ut_ad(!(index->type & DICT_CLUSTERED));
- ut_ad(page_rec_is_user_rec(rec));
- /* NOTE that we might call this function while holding the search
- system latch. To obey the latching order we must NOT reserve the
- kernel mutex here! */
- if (recv_recovery_is_on()) {
- return(FALSE);
- }
- max_trx_id = page_get_max_trx_id(buf_frame_align(rec));
- if (ut_dulint_cmp(max_trx_id, view->up_limit_id) >= 0) {
- return(FALSE);
- }
- return(TRUE);
- }
- /*************************************************************************
- Creates the lock system at database start. */
- void
- lock_sys_create(
- /*============*/
- ulint n_cells) /* in: number of slots in lock hash table */
- {
- lock_sys = mem_alloc(sizeof(lock_sys_t));
- lock_sys->rec_hash = hash_create(n_cells);
- /* hash_create_mutexes(lock_sys->rec_hash, 2, SYNC_REC_LOCK); */
- lock_latest_err_file = os_file_create_tmpfile();
- ut_a(lock_latest_err_file);
- }
- /*************************************************************************
- Gets the size of a lock struct. */
- ulint
- lock_get_size(void)
- /*===============*/
- /* out: size in bytes */
- {
- return((ulint)sizeof(lock_t));
- }
- /*************************************************************************
- Gets the mode of a lock. */
- UNIV_INLINE
- ulint
- lock_get_mode(
- /*==========*/
- /* out: mode */
- lock_t* lock) /* in: lock */
- {
- ut_ad(lock);
- return(lock->type_mode & LOCK_MODE_MASK);
- }
- /*************************************************************************
- Gets the wait flag of a lock. */
- UNIV_INLINE
- ibool
- lock_get_wait(
- /*==========*/
- /* out: TRUE if waiting */
- lock_t* lock) /* in: lock */
- {
- ut_ad(lock);
- if (lock->type_mode & LOCK_WAIT) {
- return(TRUE);
- }
- 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
- void
- lock_set_lock_and_trx_wait(
- /*=======================*/
- lock_t* lock, /* in: lock */
- trx_t* trx) /* in: trx */
- {
- ut_ad(lock);
- ut_ad(trx->wait_lock == NULL);
-
- trx->wait_lock = lock;
- lock->type_mode = lock->type_mode | LOCK_WAIT;
- }
- /**************************************************************************
- The back pointer to a waiting lock request in the transaction is set to NULL
- and the wait bit in lock type_mode is reset. */
- UNIV_INLINE
- void
- lock_reset_lock_and_trx_wait(
- /*=========================*/
- lock_t* lock) /* in: record lock */
- {
- ut_ad((lock->trx)->wait_lock == lock);
- ut_ad(lock_get_wait(lock));
- /* Reset the back pointer in trx to this waiting lock request */
- (lock->trx)->wait_lock = NULL;
- lock->type_mode = lock->type_mode & ~LOCK_WAIT;
- }
- /*************************************************************************
- Gets the gap flag of a record lock. */
- UNIV_INLINE
- ibool
- lock_rec_get_gap(
- /*=============*/
- /* out: TRUE if gap flag set */
- lock_t* lock) /* in: record lock */
- {
- ut_ad(lock);
- ut_ad(lock_get_type(lock) == LOCK_REC);
- if (lock->type_mode & LOCK_GAP) {
- return(TRUE);
- }
- return(FALSE);
- }
- /*************************************************************************
- Gets the LOCK_REC_NOT_GAP flag of a record lock. */
- UNIV_INLINE
- ibool
- lock_rec_get_rec_not_gap(
- /*=====================*/
- /* out: TRUE if LOCK_REC_NOT_GAP flag set */
- lock_t* lock) /* in: record lock */
- {
- ut_ad(lock);
- ut_ad(lock_get_type(lock) == LOCK_REC);
- if (lock->type_mode & LOCK_REC_NOT_GAP) {
- return(TRUE);
- }
- return(FALSE);
- }
- /*************************************************************************
- Gets the waiting insert flag of a record lock. */
- UNIV_INLINE
- ibool
- lock_rec_get_insert_intention(
- /*==========================*/
- /* out: TRUE if gap flag set */
- lock_t* lock) /* in: record lock */
- {
- ut_ad(lock);
- ut_ad(lock_get_type(lock) == LOCK_REC);
- if (lock->type_mode & LOCK_INSERT_INTENTION) {
- return(TRUE);
- }
- return(FALSE);
- }
- /*************************************************************************
- Calculates if lock mode 1 is stronger or equal to lock mode 2. */
- UNIV_INLINE
- ibool
- lock_mode_stronger_or_eq(
- /*=====================*/
- /* out: TRUE if mode1 stronger or equal to mode2 */
- ulint mode1, /* in: lock mode */
- ulint mode2) /* in: lock mode */
- {
- ut_ad(mode1 == LOCK_X || mode1 == LOCK_S || mode1 == LOCK_IX
- || mode1 == LOCK_IS || mode1 == LOCK_AUTO_INC);
- ut_ad(mode2 == LOCK_X || mode2 == LOCK_S || mode2 == LOCK_IX
- || mode2 == LOCK_IS || mode2 == LOCK_AUTO_INC);
- if (mode1 == LOCK_X) {
- return(TRUE);
- } else if (mode1 == LOCK_AUTO_INC && mode2 == LOCK_AUTO_INC) {
- return(TRUE);
- } else if (mode1 == LOCK_S
- && (mode2 == LOCK_S || mode2 == LOCK_IS)) {
- return(TRUE);
- } else if (mode1 == LOCK_IS && mode2 == LOCK_IS) {
- return(TRUE);
- } else if (mode1 == LOCK_IX && (mode2 == LOCK_IX
- || mode2 == LOCK_IS)) {
- return(TRUE);
- }
- return(FALSE);
- }
- /*************************************************************************
- Calculates if lock mode 1 is compatible with lock mode 2. */
- UNIV_INLINE
- ibool
- lock_mode_compatible(
- /*=================*/
- /* out: TRUE if mode1 compatible with mode2 */
- ulint mode1, /* in: lock mode */
- ulint mode2) /* in: lock mode */
- {
- ut_ad(mode1 == LOCK_X || mode1 == LOCK_S || mode1 == LOCK_IX
- || mode1 == LOCK_IS || mode1 == LOCK_AUTO_INC);
- ut_ad(mode2 == LOCK_X || mode2 == LOCK_S || mode2 == LOCK_IX
- || mode2 == LOCK_IS || mode2 == LOCK_AUTO_INC);
- if (mode1 == LOCK_S && (mode2 == LOCK_IS || mode2 == LOCK_S)) {
- return(TRUE);
- } else if (mode1 == LOCK_X) {
- return(FALSE);
- } else if (mode1 == LOCK_AUTO_INC && (mode2 == LOCK_IS
- || mode2 == LOCK_IX)) {
- return(TRUE);
- } else if (mode1 == LOCK_IS && (mode2 == LOCK_IS
- || mode2 == LOCK_IX
- || mode2 == LOCK_AUTO_INC
- || mode2 == LOCK_S)) {
- return(TRUE);
- } else if (mode1 == LOCK_IX && (mode2 == LOCK_IS
- || mode2 == LOCK_AUTO_INC
- || mode2 == LOCK_IX)) {
- return(TRUE);
- }
- return(FALSE);
- }
- /*************************************************************************
- Checks if a lock request for a new lock has to wait for request lock2. */
- UNIV_INLINE
- ibool
- lock_rec_has_to_wait(
- /*=================*/
- /* out: TRUE if new lock has to wait for lock2 to be
- removed */
- trx_t* trx, /* in: trx of new lock */
- ulint type_mode,/* in: precise mode of the new lock to set:
- LOCK_S or LOCK_X, possibly ORed to
- LOCK_GAP or LOCK_REC_NOT_GAP, LOCK_INSERT_INTENTION */
- lock_t* lock2, /* in: another record lock; NOTE that it is assumed
- that this has a lock bit set on the same record as
- in the new lock we are setting */
- ibool lock_is_on_supremum) /* in: TRUE if we are setting the lock
- on the 'supremum' record of an index
- page: we know then that the lock request
- is really for a 'gap' type lock */
- {
- ut_ad(trx && lock2);
- ut_ad(lock_get_type(lock2) == LOCK_REC);
- if (trx != lock2->trx
- && !lock_mode_compatible(LOCK_MODE_MASK & type_mode,
- lock_get_mode(lock2))) {
- /* We have somewhat complex rules when gap type record locks
- cause waits */
- if ((lock_is_on_supremum || (type_mode & LOCK_GAP))
- && !(type_mode & LOCK_INSERT_INTENTION)) {
- /* Gap type locks without LOCK_INSERT_INTENTION flag
- do not need to wait for anything. This is because
- different users can have conflicting lock types
- on gaps. */
-
- return(FALSE);
- }
-
- if (!(type_mode & LOCK_INSERT_INTENTION)
- && lock_rec_get_gap(lock2)) {
- /* Record lock (LOCK_ORDINARY or LOCK_REC_NOT_GAP
- does not need to wait for a gap type lock */
- return(FALSE);
- }
- if ((type_mode & LOCK_GAP)
- && lock_rec_get_rec_not_gap(lock2)) {
-
- /* Lock on gap does not need to wait for
- a LOCK_REC_NOT_GAP type lock */
- return(FALSE);
- }
- if (lock_rec_get_insert_intention(lock2)) {
- /* No lock request needs to wait for an insert
- intention lock to be removed. This is ok since our
- rules allow conflicting locks on gaps. This eliminates
- a spurious deadlock caused by a next-key lock waiting
- for an insert intention lock; when the insert
- intention lock was granted, the insert deadlocked on
- the waiting next-key lock.
- Also, insert intention locks do not disturb each
- other. */
-
- return(FALSE);
- }
- return(TRUE);
- }
- return(FALSE);
- }
- /*************************************************************************
- Checks if a lock request lock1 has to wait for request lock2. */
- static
- ibool
- lock_has_to_wait(
- /*=============*/
- /* out: TRUE if lock1 has to wait for lock2 to be
- removed */
- lock_t* lock1, /* in: waiting lock */
- lock_t* lock2) /* in: another lock; NOTE that it is assumed that this
- has a lock bit set on the same record as in lock1 if
- the locks are record locks */
- {
- ut_ad(lock1 && lock2);
- if (lock1->trx != lock2->trx
- && !lock_mode_compatible(lock_get_mode(lock1),
- lock_get_mode(lock2))) {
- if (lock_get_type(lock1) == LOCK_REC) {
- ut_ad(lock_get_type(lock2) == LOCK_REC);
- /* If this lock request is for a supremum record
- then the second bit on the lock bitmap is set */
-
- return(lock_rec_has_to_wait(lock1->trx,
- lock1->type_mode, lock2,
- lock_rec_get_nth_bit(lock1,1)));
- }
- return(TRUE);
- }
- return(FALSE);
- }
- /*============== RECORD LOCK BASIC FUNCTIONS ============================*/
- /*************************************************************************
- Gets the number of bits in a record lock bitmap. */
- UNIV_INLINE
- ulint
- lock_rec_get_n_bits(
- /*================*/
- /* out: number of bits */
- lock_t* lock) /* in: record lock */
- {
- return(lock->un_member.rec_lock.n_bits);
- }
- /**************************************************************************
- Sets the nth bit of a record lock to TRUE. */
- UNIV_INLINE
- void
- lock_rec_set_nth_bit(
- /*==================*/
- lock_t* lock, /* in: record lock */
- ulint i) /* in: index of the bit */
- {
- ulint byte_index;
- ulint bit_index;
- byte* ptr;
- ulint b;
-
- ut_ad(lock);
- ut_ad(lock_get_type(lock) == LOCK_REC);
- ut_ad(i < lock->un_member.rec_lock.n_bits);
-
- byte_index = i / 8;
- bit_index = i % 8;
- ptr = (byte*)lock + sizeof(lock_t) + byte_index;
-
- b = (ulint)*ptr;
- b = ut_bit_set_nth(b, bit_index, TRUE);
- *ptr = (byte)b;
- }
- /**************************************************************************
- Looks for a set bit in a record lock bitmap. Returns ULINT_UNDEFINED,
- if none found. */
- static
- ulint
- lock_rec_find_set_bit(
- /*==================*/
- /* out: bit index == heap number of the record, or
- ULINT_UNDEFINED if none found */
- lock_t* lock) /* in: record lock with at least one bit set */
- {
- ulint i;
- for (i = 0; i < lock_rec_get_n_bits(lock); i++) {
- if (lock_rec_get_nth_bit(lock, i)) {
- return(i);
- }
- }
- return(ULINT_UNDEFINED);
- }
- /**************************************************************************
- Resets the nth bit of a record lock. */
- UNIV_INLINE
- void
- lock_rec_reset_nth_bit(
- /*===================*/
- lock_t* lock, /* in: record lock */
- ulint i) /* in: index of the bit which must be set to TRUE
- when this function is called */
- {
- ulint byte_index;
- ulint bit_index;
- byte* ptr;
- ulint b;
-
- ut_ad(lock);
- ut_ad(lock_get_type(lock) == LOCK_REC);
- ut_ad(i < lock->un_member.rec_lock.n_bits);
-
- byte_index = i / 8;
- bit_index = i % 8;
- ptr = (byte*)lock + sizeof(lock_t) + byte_index;
-
- b = (ulint)*ptr;
- b = ut_bit_set_nth(b, bit_index, FALSE);
- *ptr = (byte)b;
- }
- /*************************************************************************
- Gets the first or next record lock on a page. */
- UNIV_INLINE
- lock_t*
- lock_rec_get_next_on_page(
- /*======================*/
- /* out: next lock, NULL if none exists */
- lock_t* lock) /* in: a record lock */
- {
- ulint space;
- ulint page_no;
- #ifdef UNIV_SYNC_DEBUG
- ut_ad(mutex_own(&kernel_mutex));
- #endif /* UNIV_SYNC_DEBUG */
- ut_ad(lock_get_type(lock) == LOCK_REC);
- space = lock->un_member.rec_lock.space;
- page_no = lock->un_member.rec_lock.page_no;
-
- for (;;) {
- lock = HASH_GET_NEXT(hash, lock);
- if (!lock) {
- break;
- }
- if ((lock->un_member.rec_lock.space == space)
- && (lock->un_member.rec_lock.page_no == page_no)) {
- break;
- }
- }
-
- return(lock);
- }
- /*************************************************************************
- Gets the first record lock on a page, where the page is identified by its
- file address. */
- UNIV_INLINE
- lock_t*
- lock_rec_get_first_on_page_addr(
- /*============================*/
- /* out: first lock, NULL if none exists */
- ulint space, /* in: space */
- ulint page_no)/* in: page number */
- {
- lock_t* lock;
- #ifdef UNIV_SYNC_DEBUG
- ut_ad(mutex_own(&kernel_mutex));
- #endif /* UNIV_SYNC_DEBUG */
- lock = HASH_GET_FIRST(lock_sys->rec_hash,
- lock_rec_hash(space, page_no));
- while (lock) {
- if ((lock->un_member.rec_lock.space == space)
- && (lock->un_member.rec_lock.page_no == page_no)) {
- break;
- }
- lock = HASH_GET_NEXT(hash, lock);
- }
- return(lock);
- }
-
- /*************************************************************************
- Returns TRUE if there are explicit record locks on a page. */
- ibool
- lock_rec_expl_exist_on_page(
- /*========================*/
- /* out: TRUE if there are explicit record locks on
- the page */
- ulint space, /* in: space id */
- ulint page_no)/* in: page number */
- {
- ibool ret;
- mutex_enter(&kernel_mutex);
- if (lock_rec_get_first_on_page_addr(space, page_no)) {
- ret = TRUE;
- } else {
- ret = FALSE;
- }
- mutex_exit(&kernel_mutex);
-
- return(ret);
- }
- /*************************************************************************
- Gets the first record lock on a page, where the page is identified by a
- pointer to it. */
- UNIV_INLINE
- lock_t*
- lock_rec_get_first_on_page(
- /*=======================*/
- /* out: first lock, NULL if none exists */
- byte* ptr) /* in: pointer to somewhere on the page */
- {
- ulint hash;
- lock_t* lock;
- ulint space;
- ulint page_no;
- #ifdef UNIV_SYNC_DEBUG
- ut_ad(mutex_own(&kernel_mutex));
- #endif /* UNIV_SYNC_DEBUG */
-
- hash = buf_frame_get_lock_hash_val(ptr);
- lock = HASH_GET_FIRST(lock_sys->rec_hash, hash);
- while (lock) {
- space = buf_frame_get_space_id(ptr);
- page_no = buf_frame_get_page_no(ptr);
- if ((lock->un_member.rec_lock.space == space)
- && (lock->un_member.rec_lock.page_no == page_no)) {
- break;
- }
- lock = HASH_GET_NEXT(hash, lock);
- }
- return(lock);
- }
- /*************************************************************************
- Gets the next explicit lock request on a record. */
- UNIV_INLINE
- lock_t*
- lock_rec_get_next(
- /*==============*/
- /* out: next lock, NULL if none exists */
- rec_t* rec, /* in: record on a page */
- lock_t* lock) /* in: lock */
- {
- #ifdef UNIV_SYNC_DEBUG
- ut_ad(mutex_own(&kernel_mutex));
- #endif /* UNIV_SYNC_DEBUG */
- ut_ad(lock_get_type(lock) == LOCK_REC);
- for (;;) {
- lock = lock_rec_get_next_on_page(lock);
- if (lock == NULL) {
- return(NULL);
- }
- if (lock_rec_get_nth_bit(lock, rec_get_heap_no(rec))) {
- return(lock);
- }
- }
- }
- /*************************************************************************
- Gets the first explicit lock request on a record. */
- UNIV_INLINE
- lock_t*
- lock_rec_get_first(
- /*===============*/
- /* out: first lock, NULL if none exists */
- rec_t* rec) /* in: record on a page */
- {
- lock_t* lock;
- #ifdef UNIV_SYNC_DEBUG
- ut_ad(mutex_own(&kernel_mutex));
- #endif /* UNIV_SYNC_DEBUG */
- lock = lock_rec_get_first_on_page(rec);
- while (lock) {
- if (lock_rec_get_nth_bit(lock, rec_get_heap_no(rec))) {
- break;
- }
- lock = lock_rec_get_next_on_page(lock);
- }
- return(lock);
- }
- /*************************************************************************
- Resets the record lock bitmap to zero. NOTE: does not touch the wait_lock
- pointer in the transaction! This function is used in lock object creation
- and resetting. */
- static
- void
- lock_rec_bitmap_reset(
- /*==================*/
- lock_t* lock) /* in: record lock */
- {
- byte* ptr;
- ulint n_bytes;
- ulint i;
- ut_ad(lock_get_type(lock) == LOCK_REC);
- /* Reset to zero the bitmap which resides immediately after the lock
- struct */
- ptr = (byte*)lock + sizeof(lock_t);
- n_bytes = lock_rec_get_n_bits(lock) / 8;
- ut_ad((lock_rec_get_n_bits(lock) % 8) == 0);
-
- for (i = 0; i < n_bytes; i++) {
- *ptr = 0;
- ptr++;
- }
- }
- /*************************************************************************
- Copies a record lock to heap. */
- static
- lock_t*
- lock_rec_copy(
- /*==========*/
- /* out: copy of lock */
- lock_t* lock, /* in: record lock */
- mem_heap_t* heap) /* in: memory heap */
- {
- lock_t* dupl_lock;
- ulint size;
- ut_ad(lock_get_type(lock) == LOCK_REC);
- size = sizeof(lock_t) + lock_rec_get_n_bits(lock) / 8;
- dupl_lock = mem_heap_alloc(heap, size);
- ut_memcpy(dupl_lock, lock, size);
- return(dupl_lock);
- }
- /*************************************************************************
- Gets the previous record lock set on a record. */
- static
- lock_t*
- lock_rec_get_prev(
- /*==============*/
- /* out: previous lock on the same record, NULL if
- none exists */
- lock_t* in_lock,/* in: record lock */
- ulint heap_no)/* in: heap number of the record */
- {
- lock_t* lock;
- ulint space;
- ulint page_no;
- lock_t* found_lock = NULL;
- #ifdef UNIV_SYNC_DEBUG
- ut_ad(mutex_own(&kernel_mutex));
- #endif /* UNIV_SYNC_DEBUG */
- ut_ad(lock_get_type(in_lock) == LOCK_REC);
- space = in_lock->un_member.rec_lock.space;
- page_no = in_lock->un_member.rec_lock.page_no;
- lock = lock_rec_get_first_on_page_addr(space, page_no);
- for (;;) {
- ut_ad(lock);
-
- if (lock == in_lock) {
- return(found_lock);
- }
- if (lock_rec_get_nth_bit(lock, heap_no)) {
- found_lock = lock;
- }
- lock = lock_rec_get_next_on_page(lock);
- }
- }
- /*============= FUNCTIONS FOR ANALYZING TABLE LOCK QUEUE ================*/
- /*************************************************************************
- Checks if a transaction has the specified table lock, or stronger. */
- UNIV_INLINE
- lock_t*
- lock_table_has(
- /*===========*/
- /* out: lock or NULL */
- trx_t* trx, /* in: transaction */
- dict_table_t* table, /* in: table */
- ulint mode) /* in: lock mode */
- {
- lock_t* lock;
- #ifdef UNIV_SYNC_DEBUG
- ut_ad(mutex_own(&kernel_mutex));
- #endif /* UNIV_SYNC_DEBUG */
- /* Look for stronger locks the same trx already has on the table */
- lock = UT_LIST_GET_LAST(table->locks);
- while (lock != NULL) {
- if (lock->trx == trx
- && lock_mode_stronger_or_eq(lock_get_mode(lock), mode)) {
- /* The same trx already has locked the table in
- a mode stronger or equal to the mode given */
- ut_ad(!lock_get_wait(lock));
- return(lock);
- }
- lock = UT_LIST_GET_PREV(un_member.tab_lock.locks, lock);
- }
- return(NULL);
- }
-
- /*============= FUNCTIONS FOR ANALYZING RECORD LOCK QUEUE ================*/
- /*************************************************************************
- Checks if a transaction has a GRANTED explicit lock on rec stronger or equal
- to precise_mode. */
- UNIV_INLINE
- lock_t*
- lock_rec_has_expl(
- /*==============*/
- /* out: lock or NULL */
- ulint precise_mode,/* in: LOCK_S or LOCK_X possibly ORed to
- LOCK_GAP or LOCK_REC_NOT_GAP,
- for a supremum record we regard this always a gap
- type request */
- rec_t* rec, /* in: record */
- trx_t* trx) /* in: transaction */
- {
- lock_t* lock;
- #ifdef UNIV_SYNC_DEBUG
- ut_ad(mutex_own(&kernel_mutex));
- #endif /* UNIV_SYNC_DEBUG */
- ut_ad((precise_mode & LOCK_MODE_MASK) == LOCK_S
- || (precise_mode & LOCK_MODE_MASK) == LOCK_X);
- ut_ad(!(precise_mode & LOCK_INSERT_INTENTION));
-
- lock = lock_rec_get_first(rec);
- while (lock) {
- if (lock->trx == trx
- && lock_mode_stronger_or_eq(lock_get_mode(lock),
- precise_mode & LOCK_MODE_MASK)
- && !lock_get_wait(lock)
- && (!lock_rec_get_rec_not_gap(lock)
- || (precise_mode & LOCK_REC_NOT_GAP)
- || page_rec_is_supremum(rec))
- && (!lock_rec_get_gap(lock)
- || (precise_mode & LOCK_GAP)
- || page_rec_is_supremum(rec))
- && (!lock_rec_get_insert_intention(lock))) {
- return(lock);
- }
- lock = lock_rec_get_next(rec, lock);
- }
- return(NULL);
- }
-
- /*************************************************************************
- Checks if some other transaction has a lock request in the queue. */
- static
- lock_t*
- lock_rec_other_has_expl_req(
- /*========================*/
- /* out: lock or NULL */
- ulint mode, /* in: LOCK_S or LOCK_X */
- ulint gap, /* in: LOCK_GAP if also gap locks are taken
- into account, or 0 if not */
- ulint wait, /* in: LOCK_WAIT if also waiting locks are
- taken into account, or 0 if not */
- rec_t* rec, /* in: record to look at */
- trx_t* trx) /* in: transaction, or NULL if requests by all
- transactions are taken into account */
- {
- lock_t* lock;
-
- #ifdef UNIV_SYNC_DEBUG
- ut_ad(mutex_own(&kernel_mutex));
- #endif /* UNIV_SYNC_DEBUG */
- ut_ad(mode == LOCK_X || mode == LOCK_S);
- ut_ad(gap == 0 || gap == LOCK_GAP);
- ut_ad(wait == 0 || wait == LOCK_WAIT);
- lock = lock_rec_get_first(rec);
- while (lock) {
- if (lock->trx != trx
- && (gap ||
- !(lock_rec_get_gap(lock) || page_rec_is_supremum(rec)))
- && (wait || !lock_get_wait(lock))
- && lock_mode_stronger_or_eq(lock_get_mode(lock), mode)) {
- return(lock);
- }
- lock = lock_rec_get_next(rec, lock);
- }
- return(NULL);
- }
- /*************************************************************************
- Checks if some other transaction has a conflicting explicit lock request
- in the queue, so that we have to wait. */
- static
- lock_t*
- lock_rec_other_has_conflicting(
- /*===========================*/
- /* out: lock or NULL */
- ulint mode, /* in: LOCK_S or LOCK_X,
- possibly ORed to LOCK_GAP or LOC_REC_NOT_GAP,
- LOCK_INSERT_INTENTION */
- rec_t* rec, /* in: record to look at */
- trx_t* trx) /* in: our transaction */
- {
- lock_t* lock;
-
- #ifdef UNIV_SYNC_DEBUG
- ut_ad(mutex_own(&kernel_mutex));
- #endif /* UNIV_SYNC_DEBUG */
- lock = lock_rec_get_first(rec);
- while (lock) {
- if (lock_rec_has_to_wait(trx, mode, lock,
- page_rec_is_supremum(rec))) {
- return(lock);
- }
-
- lock = lock_rec_get_next(rec, lock);
- }
- return(NULL);
- }
- /*************************************************************************
- Looks for a suitable type record lock struct by the same trx on the same page.
- This can be used to save space when a new record lock should be set on a page:
- no new struct is needed, if a suitable old is found. */
- UNIV_INLINE
- lock_t*
- lock_rec_find_similar_on_page(
- /*==========================*/
- /* out: lock or NULL */
- ulint type_mode, /* in: lock type_mode field */
- rec_t* rec, /* in: record */
- trx_t* trx) /* in: transaction */
- {
- lock_t* lock;
- ulint heap_no;
- #ifdef UNIV_SYNC_DEBUG
- ut_ad(mutex_own(&kernel_mutex));
- #endif /* UNIV_SYNC_DEBUG */
- heap_no = rec_get_heap_no(rec);
-
- lock = lock_rec_get_first_on_page(rec);
- while (lock != NULL) {
- if (lock->trx == trx
- && lock->type_mode == type_mode
- && lock_rec_get_n_bits(lock) > heap_no) {
-
- return(lock);
- }
-
- lock = lock_rec_get_next_on_page(lock);
- }
- return(NULL);
- }
- /*************************************************************************
- Checks if some transaction has an implicit x-lock on a record in a secondary
- index. */
- trx_t*
- lock_sec_rec_some_has_impl_off_kernel(
- /*==================================*/
- /* out: transaction which has the x-lock, or
- NULL */
- rec_t* rec, /* in: user record */
- dict_index_t* index) /* in: secondary index */
- {
- page_t* page;
-
- #ifdef UNIV_SYNC_DEBUG
- ut_ad(mutex_own(&kernel_mutex));
- #endif /* UNIV_SYNC_DEBUG */
- ut_ad(!(index->type & DICT_CLUSTERED));
- ut_ad(page_rec_is_user_rec(rec));
- page = buf_frame_align(rec);
- /* Some transaction may have an implicit x-lock on the record only
- if the max trx id for the page >= min trx id for the trx list, or
- database recovery is running. We do not write the changes of a page
- max trx id to the log, and therefore during recovery, this value
- for a page may be incorrect. */
- if (!(ut_dulint_cmp(page_get_max_trx_id(page),
- trx_list_get_min_trx_id()) >= 0)
- && !recv_recovery_is_on()) {
- return(NULL);
- }
- /* Ok, in this case it is possible that some transaction has an
- implicit x-lock. We have to look in the clustered index. */
-
- if (!lock_check_trx_id_sanity(page_get_max_trx_id(page), rec, index,
- TRUE)) {
- buf_page_print(page);
-
- /* The page is corrupt: try to avoid a crash by returning
- NULL */
- return(NULL);
- }
- return(row_vers_impl_x_locked_off_kernel(rec, index));
- }
- /*============== RECORD LOCK CREATION AND QUEUE MANAGEMENT =============*/
- /*************************************************************************
- Creates a new record lock and inserts it to the lock queue. Does NOT check
- for deadlocks or lock compatibility! */
- static
- lock_t*
- lock_rec_create(
- /*============*/
- /* out: created lock, NULL if out of memory */
- ulint type_mode,/* in: lock mode and wait flag, type is
- ignored and replaced by LOCK_REC */
- rec_t* rec, /* in: record on page */
- dict_index_t* index, /* in: index of record */
- trx_t* trx) /* in: transaction */
- {
- page_t* page;
- lock_t* lock;
- ulint page_no;
- ulint heap_no;
- ulint space;
- ulint n_bits;
- ulint n_bytes;
-
- #ifdef UNIV_SYNC_DEBUG
- ut_ad(mutex_own(&kernel_mutex));
- #endif /* UNIV_SYNC_DEBUG */
- page = buf_frame_align(rec);
- space = buf_frame_get_space_id(page);
- page_no = buf_frame_get_page_no(page);
- heap_no = rec_get_heap_no(rec);
- /* If rec is the supremum record, then we reset the gap and
- LOCK_REC_NOT_GAP bits, as all locks on the supremum are
- automatically of the gap type */
- if (rec == page_get_supremum_rec(page)) {
- ut_ad(!(type_mode & LOCK_REC_NOT_GAP));
- type_mode = type_mode & ~(LOCK_GAP | LOCK_REC_NOT_GAP);
- }
- /* Make lock bitmap bigger by a safety margin */
- n_bits = page_header_get_field(page, PAGE_N_HEAP)
- + LOCK_PAGE_BITMAP_MARGIN;
- n_bytes = 1 + n_bits / 8;
- lock = mem_heap_alloc(trx->lock_heap, sizeof(lock_t) + n_bytes);
-
- if (lock == NULL) {
- return(NULL);
- }
- UT_LIST_ADD_LAST(trx_locks, trx->trx_locks, lock);
- lock->trx = trx;
- lock->type_mode = (type_mode & ~LOCK_TYPE_MASK) | LOCK_REC;
- lock->index = index;
-
- lock->un_member.rec_lock.space = space;
- lock->un_member.rec_lock.page_no = page_no;
- lock->un_member.rec_lock.n_bits = n_bytes * 8;
- /* Reset to zero the bitmap which resides immediately after the
- lock struct */
- lock_rec_bitmap_reset(lock);
- /* Set the bit corresponding to rec */
- lock_rec_set_nth_bit(lock, heap_no);
- HASH_INSERT(lock_t, hash, lock_sys->rec_hash,
- lock_rec_fold(space, page_no), lock);
- if (type_mode & LOCK_WAIT) {
- lock_set_lock_and_trx_wait(lock, trx);
- }
-
- return(lock);
- }
- /*************************************************************************
- Enqueues a waiting request for a lock which cannot be granted immediately.
- Checks for deadlocks. */
- static
- ulint
- lock_rec_enqueue_waiting(
- /*=====================*/
- /* out: DB_LOCK_WAIT, DB_DEADLOCK, or
- DB_QUE_THR_SUSPENDED, or DB_SUCCESS;
- DB_SUCCESS means that there was a deadlock,
- but another transaction was chosen as a
- victim, and we got the lock immediately:
- no need to wait then */
- ulint type_mode,/* in: lock mode this transaction is
- requesting: LOCK_S or LOCK_X, possibly ORed
- with LOCK_GAP or LOCK_REC_NOT_GAP, ORed
- with LOCK_INSERT_INTENTION if this waiting
- lock request is set when performing an
- insert of an index record */
- rec_t* rec, /* in: record */
- dict_index_t* index, /* in: index of record */
- que_thr_t* thr) /* in: query thread */
- {
- lock_t* lock;
- trx_t* trx;
-
- #ifdef UNIV_SYNC_DEBUG
- ut_ad(mutex_own(&kernel_mutex));
- #endif /* UNIV_SYNC_DEBUG */
- /* Test if there already is some other reason to suspend thread:
- we do not enqueue a lock request if the query thread should be
- stopped anyway */
- if (que_thr_stop(thr)) {
- ut_error;
- return(DB_QUE_THR_SUSPENDED);
- }
-
- trx = thr_get_trx(thr);
- if (trx->dict_operation) {
- ut_print_timestamp(stderr);
- fputs(
- " InnoDB: Error: a record lock wait happens in a dictionary operation!n"
- "InnoDB: Table name ", stderr);
- ut_print_name(stderr, trx, index->table_name);
- fputs(".n"
- "InnoDB: Submit a detailed bug report to http://bugs.mysql.comn",
- stderr);
- }
-
- /* Enqueue the lock request that will wait to be granted */
- lock = lock_rec_create(type_mode | LOCK_WAIT, rec, index, trx);
- /* Check if a deadlock occurs: if yes, remove the lock request and
- return an error code */
-
- if (lock_deadlock_occurs(lock, trx)) {
- lock_reset_lock_and_trx_wait(lock);
- lock_rec_reset_nth_bit(lock, rec_get_heap_no(rec));
- return(DB_DEADLOCK);
- }
- /* If there was a deadlock but we chose another transaction as a
- victim, it is possible that we already have the lock now granted! */
- if (trx->wait_lock == NULL) {
- return(DB_SUCCESS);
- }
- trx->que_state = TRX_QUE_LOCK_WAIT;
- trx->was_chosen_as_deadlock_victim = FALSE;
- trx->wait_started = time(NULL);
- ut_a(que_thr_stop(thr));
- if (lock_print_waits) {
- fprintf(stderr, "Lock wait for trx %lu in index ",
- (ulong) ut_dulint_get_low(trx->id));
- ut_print_name(stderr, trx, index->name);
- }
-
- return(DB_LOCK_WAIT);
- }
- /*************************************************************************
- Adds a record lock request in the record queue. The request is normally
- added as the last in the queue, but if there are no waiting lock requests
- on the record, and the request to be added is not a waiting request, we
- can reuse a suitable record lock object already existing on the same page,
- just setting the appropriate bit in its bitmap. This is a low-level function
- which does NOT check for deadlocks or lock compatibility! */
- static
- lock_t*
- lock_rec_add_to_queue(
- /*==================*/
- /* out: lock where the bit was set, NULL if out
- of memory */
- ulint type_mode,/* in: lock mode, wait, gap etc. flags;
- type is ignored and replaced by LOCK_REC */
- rec_t* rec, /* in: record on page */
- dict_index_t* index, /* in: index of record */
- trx_t* trx) /* in: transaction */
- {
- lock_t* lock;
- lock_t* similar_lock = NULL;
- ulint heap_no;
- page_t* page;
- ibool somebody_waits = FALSE;
-
- #ifdef UNIV_SYNC_DEBUG
- ut_ad(mutex_own(&kernel_mutex));
- #endif /* UNIV_SYNC_DEBUG */
- ut_ad((type_mode & (LOCK_WAIT | LOCK_GAP))
- || ((type_mode & LOCK_MODE_MASK) != LOCK_S)
- || !lock_rec_other_has_expl_req(LOCK_X, 0, LOCK_WAIT, rec, trx));
- ut_ad((type_mode & (LOCK_WAIT | LOCK_GAP))
- || ((type_mode & LOCK_MODE_MASK) != LOCK_X)
- || !lock_rec_other_has_expl_req(LOCK_S, 0, LOCK_WAIT, rec, trx));
- type_mode = type_mode | LOCK_REC;
- page = buf_frame_align(rec);
- /* If rec is the supremum record, then we can reset the gap bit, as
- all locks on the supremum are automatically of the gap type, and we
- try to avoid unnecessary memory consumption of a new record lock
- struct for a gap type lock */
- if (rec == page_get_supremum_rec(page)) {
- ut_ad(!(type_mode & LOCK_REC_NOT_GAP));
- /* There should never be LOCK_REC_NOT_GAP on a supremum
- record, but let us play safe */
-
- type_mode = type_mode & ~(LOCK_GAP | LOCK_REC_NOT_GAP);
- }
- /* Look for a waiting lock request on the same record or on a gap */
- heap_no = rec_get_heap_no(rec);
- lock = lock_rec_get_first_on_page(rec);
- while (lock != NULL) {
- if (lock_get_wait(lock)
- && (lock_rec_get_nth_bit(lock, heap_no))) {
- somebody_waits = TRUE;
- }
- lock = lock_rec_get_next_on_page(lock);
- }
- /* Look for a similar record lock on the same page: if one is found
- and there are no waiting lock requests, we can just set the bit */
- similar_lock = lock_rec_find_similar_on_page(type_mode, rec, trx);
- if (similar_lock && !somebody_waits && !(type_mode & LOCK_WAIT)) {
- lock_rec_set_nth_bit(similar_lock, heap_no);
- return(similar_lock);
- }
- return(lock_rec_create(type_mode, rec, index, trx));
- }
- /*************************************************************************
- This is a fast routine for locking a record in the most common cases:
- there are no explicit locks on the page, or there is just one lock, owned
- by this transaction, and of the right type_mode. This is a low-level function
- which does NOT look at implicit locks! Checks lock compatibility within
- explicit locks. This function sets a normal next-key lock, or in the case of
- a page supremum record, a gap type lock. */
- UNIV_INLINE
- ibool
- lock_rec_lock_fast(
- /*===============*/
- /* out: TRUE if locking succeeded */
- ibool impl, /* in: if TRUE, no lock is set if no wait
- is necessary: we assume that the caller will
- set an implicit lock */
- ulint mode, /* in: lock mode: LOCK_X or LOCK_S possibly
- ORed to either LOCK_GAP or LOCK_REC_NOT_GAP */
- rec_t* rec, /* in: record */
- dict_index_t* index, /* in: index of record */
- que_thr_t* thr) /* in: query thread */
- {
- lock_t* lock;
- ulint heap_no;
- #ifdef UNIV_SYNC_DEBUG
- ut_ad(mutex_own(&kernel_mutex));
- #endif /* UNIV_SYNC_DEBUG */
- ut_ad((LOCK_MODE_MASK & mode) != LOCK_S
- || lock_table_has(thr_get_trx(thr), index->table, LOCK_IS));
- ut_ad((LOCK_MODE_MASK & mode) != LOCK_X
- || lock_table_has(thr_get_trx(thr), index->table, LOCK_IX));
- ut_ad((LOCK_MODE_MASK & mode) == LOCK_S
- || (LOCK_MODE_MASK & mode) == LOCK_X);
- ut_ad(mode - (LOCK_MODE_MASK & mode) == LOCK_GAP
- || mode - (LOCK_MODE_MASK & mode) == 0
- || mode - (LOCK_MODE_MASK & mode) == LOCK_REC_NOT_GAP);
-
- heap_no = rec_get_heap_no(rec);
-
- lock = lock_rec_get_first_on_page(rec);
- if (lock == NULL) {
- if (!impl) {
- lock_rec_create(mode, rec, index, thr_get_trx(thr));
- }
-
- return(TRUE);
- }
-
- if (lock_rec_get_next_on_page(lock)) {
- return(FALSE);
- }
- if (lock->trx != thr_get_trx(thr)
- || lock->type_mode != (mode | LOCK_REC)
- || lock_rec_get_n_bits(lock) <= heap_no) {
- return(FALSE);
- }
- if (!impl) {
- lock_rec_set_nth_bit(lock, heap_no);
- }
- return(TRUE);
- }
- /*************************************************************************
- This is the general, and slower, routine for locking a record. This is a
- low-level function which does NOT look at implicit locks! Checks lock
- compatibility within explicit locks. This function sets a normal next-key
- lock, or in the case of a page supremum record, a gap type lock. */
- static
- ulint
- lock_rec_lock_slow(
- /*===============*/
- /* out: DB_SUCCESS, DB_LOCK_WAIT, or error
- code */
- ibool impl, /* in: if TRUE, no lock is set if no wait is
- necessary: we assume that the caller will set
- an implicit lock */
- ulint mode, /* in: lock mode: LOCK_X or LOCK_S possibly
- ORed to either LOCK_GAP or LOCK_REC_NOT_GAP */
- rec_t* rec, /* in: record */
- dict_index_t* index, /* in: index of record */
- que_thr_t* thr) /* in: query thread */
- {
- trx_t* trx;
- ulint err;
- #ifdef UNIV_SYNC_DEBUG
- ut_ad(mutex_own(&kernel_mutex));
- #endif /* UNIV_SYNC_DEBUG */
- ut_ad((LOCK_MODE_MASK & mode) != LOCK_S
- || lock_table_has(thr_get_trx(thr), index->table, LOCK_IS));
- ut_ad((LOCK_MODE_MASK & mode) != LOCK_X
- || lock_table_has(thr_get_trx(thr), index->table, LOCK_IX));
- ut_ad((LOCK_MODE_MASK & mode) == LOCK_S
- || (LOCK_MODE_MASK & mode) == LOCK_X);
- ut_ad(mode - (LOCK_MODE_MASK & mode) == LOCK_GAP
- || mode - (LOCK_MODE_MASK & mode) == 0
- || mode - (LOCK_MODE_MASK & mode) == LOCK_REC_NOT_GAP);
-
- trx = thr_get_trx(thr);
-
- if (lock_rec_has_expl(mode, rec, trx)) {
- /* The trx already has a strong enough lock on rec: do
- nothing */
- err = DB_SUCCESS;
- } else if (lock_rec_other_has_conflicting(mode, rec, trx)) {
- /* If another transaction has a non-gap conflicting request in
- the queue, as this transaction does not have a lock strong
- enough already granted on the record, we have to wait. */
-
- err = lock_rec_enqueue_waiting(mode, rec, index, thr);
- } else {
- if (!impl) {
- /* Set the requested lock on the record */
- lock_rec_add_to_queue(LOCK_REC | mode, rec, index,
- trx);
- }
- err = DB_SUCCESS;
- }
- return(err);
- }
- /*************************************************************************
- Tries to lock the specified record in the mode requested. If not immediately
- possible, enqueues a waiting lock request. This is a low-level function
- which does NOT look at implicit locks! Checks lock compatibility within
- explicit locks. This function sets a normal next-key lock, or in the case
- of a page supremum record, a gap type lock. */
- static
- ulint
- lock_rec_lock(
- /*==========*/
- /* out: DB_SUCCESS, DB_LOCK_WAIT, or error
- code */
- ibool impl, /* in: if TRUE, no lock is set if no wait is
- necessary: we assume that the caller will set
- an implicit lock */
- ulint mode, /* in: lock mode: LOCK_X or LOCK_S possibly
- ORed to either LOCK_GAP or LOCK_REC_NOT_GAP */
- rec_t* rec, /* in: record */
- dict_index_t* index, /* in: index of record */
- que_thr_t* thr) /* in: query thread */
- {
- ulint err;
- #ifdef UNIV_SYNC_DEBUG
- ut_ad(mutex_own(&kernel_mutex));
- #endif /* UNIV_SYNC_DEBUG */
- ut_ad((LOCK_MODE_MASK & mode) != LOCK_S
- || lock_table_has(thr_get_trx(thr), index->table, LOCK_IS));
- ut_ad((LOCK_MODE_MASK & mode) != LOCK_X
- || lock_table_has(thr_get_trx(thr), index->table, LOCK_IX));
- ut_ad((LOCK_MODE_MASK & mode) == LOCK_S
- || (LOCK_MODE_MASK & mode) == LOCK_X);
- ut_ad(mode - (LOCK_MODE_MASK & mode) == LOCK_GAP
- || mode - (LOCK_MODE_MASK & mode) == LOCK_REC_NOT_GAP
- || mode - (LOCK_MODE_MASK & mode) == 0);
-
- if (lock_rec_lock_fast(impl, mode, rec, index, thr)) {
- /* We try a simplified and faster subroutine for the most
- common cases */
- err = DB_SUCCESS;
- } else {
- err = lock_rec_lock_slow(impl, mode, rec, index, thr);
- }
- return(err);
- }
- /*************************************************************************
- Checks if a waiting record lock request still has to wait in a queue. */
- static
- ibool
- lock_rec_has_to_wait_in_queue(
- /*==========================*/
- /* out: TRUE if still has to wait */
- lock_t* wait_lock) /* in: waiting record lock */
- {
- lock_t* lock;
- ulint space;
- ulint page_no;
- ulint heap_no;
- #ifdef UNIV_SYNC_DEBUG
- ut_ad(mutex_own(&kernel_mutex));
- #endif /* UNIV_SYNC_DEBUG */
- ut_ad(lock_get_wait(wait_lock));
- ut_ad(lock_get_type(wait_lock) == LOCK_REC);
-
- space = wait_lock->un_member.rec_lock.space;
- page_no = wait_lock->un_member.rec_lock.page_no;
- heap_no = lock_rec_find_set_bit(wait_lock);
- lock = lock_rec_get_first_on_page_addr(space, page_no);
- while (lock != wait_lock) {
- if (lock_rec_get_nth_bit(lock, heap_no)
- && lock_has_to_wait(wait_lock, lock)) {
- return(TRUE);
- }
- lock = lock_rec_get_next_on_page(lock);
- }
- return(FALSE);
- }
- /*****************************************************************
- Grants a lock to a waiting lock request and releases the waiting
- transaction. */
- static
- void
- lock_grant(
- /*=======*/
- lock_t* lock) /* in: waiting lock request */
- {
- #ifdef UNIV_SYNC_DEBUG
- ut_ad(mutex_own(&kernel_mutex));
- #endif /* UNIV_SYNC_DEBUG */
- lock_reset_lock_and_trx_wait(lock);
- if (lock_get_mode(lock) == LOCK_AUTO_INC) {
- if (lock->trx->auto_inc_lock != NULL) {
- fprintf(stderr,
- "InnoDB: Error: trx already had an AUTO-INC lock!n");
- }
- /* Store pointer to lock to trx so that we know to
- release it at the end of the SQL statement */
- lock->trx->auto_inc_lock = lock;
- } else if (lock_get_type(lock) == LOCK_TABLE_EXP) {
- ut_a(lock_get_mode(lock) == LOCK_S
- || lock_get_mode(lock) == LOCK_X);
- }
- if (lock_print_waits) {
- fprintf(stderr, "Lock wait for trx %lu endsn",
- (ulong) ut_dulint_get_low(lock->trx->id));
- }
- /* If we are resolving a deadlock by choosing another transaction
- as a victim, then our original transaction may not be in the
- TRX_QUE_LOCK_WAIT state, and there is no need to end the lock wait
- for it */
-
- if (lock->trx->que_state == TRX_QUE_LOCK_WAIT) {
- trx_end_lock_wait(lock->trx);
- }
- }
- /*****************************************************************
- Cancels a waiting record lock request and releases the waiting transaction
- that requested it. NOTE: does NOT check if waiting lock requests behind this
- one can now be granted! */
- static
- void
- lock_rec_cancel(
- /*============*/
- lock_t* lock) /* in: waiting record lock request */
- {
- #ifdef UNIV_SYNC_DEBUG
- ut_ad(mutex_own(&kernel_mutex));
- #endif /* UNIV_SYNC_DEBUG */
- ut_ad(lock_get_type(lock) == LOCK_REC);
- /* Reset the bit (there can be only one set bit) in the lock bitmap */
- lock_rec_reset_nth_bit(lock, lock_rec_find_set_bit(lock));
- /* Reset the wait flag and the back pointer to lock in trx */
- lock_reset_lock_and_trx_wait(lock);
- /* The following function releases the trx from lock wait */
- trx_end_lock_wait(lock->trx);
- }
-
- /*****************************************************************
- Removes a record lock request, waiting or granted, from the queue and
- grants locks to other transactions in the queue if they now are entitled
- to a lock. NOTE: all record locks contained in in_lock are removed. */
- static
- void
- lock_rec_dequeue_from_page(
- /*=======================*/
- lock_t* in_lock)/* in: record lock object: all record locks which
- are contained in this lock object are removed;
- transactions waiting behind will get their lock
- requests granted, if they are now qualified to it */
- {
- ulint space;
- ulint page_no;
- lock_t* lock;
- trx_t* trx;
-
- #ifdef UNIV_SYNC_DEBUG
- ut_ad(mutex_own(&kernel_mutex));
- #endif /* UNIV_SYNC_DEBUG */
- ut_ad(lock_get_type(in_lock) == LOCK_REC);
- trx = in_lock->trx;
- space = in_lock->un_member.rec_lock.space;
- page_no = in_lock->un_member.rec_lock.page_no;
- HASH_DELETE(lock_t, hash, lock_sys->rec_hash,
- lock_rec_fold(space, page_no), in_lock);
- UT_LIST_REMOVE(trx_locks, trx->trx_locks, in_lock);
- /* Check if waiting locks in the queue can now be granted: grant
- locks if there are no conflicting locks ahead. */
- lock = lock_rec_get_first_on_page_addr(space, page_no);
- while (lock != NULL) {
- if (lock_get_wait(lock)
- && !lock_rec_has_to_wait_in_queue(lock)) {
- /* Grant the lock */
- lock_grant(lock);
- }
- lock = lock_rec_get_next_on_page(lock);
- }
- }
- /*****************************************************************
- Removes a record lock request, waiting or granted, from the queue. */
- static
- void
- lock_rec_discard(
- /*=============*/
- lock_t* in_lock)/* in: record lock object: all record locks which
- are contained in this lock object are removed */
- {
- ulint space;
- ulint page_no;
- trx_t* trx;
-
- #ifdef UNIV_SYNC_DEBUG
- ut_ad(mutex_own(&kernel_mutex));
- #endif /* UNIV_SYNC_DEBUG */
- ut_ad(lock_get_type(in_lock) == LOCK_REC);
- trx = in_lock->trx;
-
- space = in_lock->un_member.rec_lock.space;
- page_no = in_lock->un_member.rec_lock.page_no;
- HASH_DELETE(lock_t, hash, lock_sys->rec_hash,
- lock_rec_fold(space, page_no), in_lock);
- UT_LIST_REMOVE(trx_locks, trx->trx_locks, in_lock);
- }
- /*****************************************************************
- Removes record lock objects set on an index page which is discarded. This
- function does not move locks, or check for waiting locks, therefore the
- lock bitmaps must already be reset when this function is called. */
- static
- void
- lock_rec_free_all_from_discard_page(
- /*================================*/
- page_t* page) /* in: page to be discarded */
- {
- ulint space;
- ulint page_no;
- lock_t* lock;
- lock_t* next_lock;
-
- #ifdef UNIV_SYNC_DEBUG
- ut_ad(mutex_own(&kernel_mutex));
- #endif /* UNIV_SYNC_DEBUG */
-
- space = buf_frame_get_space_id(page);
- page_no = buf_frame_get_page_no(page);
- lock = lock_rec_get_first_on_page_addr(space, page_no);
- while (lock != NULL) {
- ut_ad(lock_rec_find_set_bit(lock) == ULINT_UNDEFINED);
- ut_ad(!lock_get_wait(lock));
- next_lock = lock_rec_get_next_on_page(lock);
-
- lock_rec_discard(lock);
-
- lock = next_lock;
- }
- }
- /*============= RECORD LOCK MOVING AND INHERITING ===================*/
- /*****************************************************************
- Resets the lock bits for a single record. Releases transactions waiting for
- lock requests here. */
- void
- lock_rec_reset_and_release_wait(
- /*============================*/
- rec_t* rec) /* in: record whose locks bits should be reset */
- {
- lock_t* lock;
- ulint heap_no;
-
- #ifdef UNIV_SYNC_DEBUG
- ut_ad(mutex_own(&kernel_mutex));
- #endif /* UNIV_SYNC_DEBUG */
- heap_no = rec_get_heap_no(rec);
-
- lock = lock_rec_get_first(rec);
- while (lock != NULL) {
- if (lock_get_wait(lock)) {
- lock_rec_cancel(lock);
- } else {
- lock_rec_reset_nth_bit(lock, heap_no);
- }
- lock = lock_rec_get_next(rec, lock);
- }
- }
- /*****************************************************************
- Makes a record to inherit the locks (except LOCK_INSERT_INTENTION type)
- of another record as gap type locks, but does not reset the lock bits of
- the other record. Also waiting lock requests on rec are inherited as
- GRANTED gap locks. */
- void
- lock_rec_inherit_to_gap(
- /*====================*/
- rec_t* heir, /* in: record which inherits */
- rec_t* rec) /* in: record from which inherited; does NOT reset
- the locks on this record */
- {
- lock_t* lock;
-
- #ifdef UNIV_SYNC_DEBUG
- ut_ad(mutex_own(&kernel_mutex));
- #endif /* UNIV_SYNC_DEBUG */
-
- lock = lock_rec_get_first(rec);
- while (lock != NULL) {
- if (!lock_rec_get_insert_intention(lock)) {
-
- lock_rec_add_to_queue(LOCK_REC | lock_get_mode(lock)
- | LOCK_GAP,
- heir, lock->index, lock->trx);
- }
-
- lock = lock_rec_get_next(rec, lock);
- }
- }
- /*****************************************************************
- Makes a record to inherit the gap locks (except LOCK_INSERT_INTENTION type)
- of another record as gap type locks, but does not reset the lock bits of the
- other record. Also waiting lock requests are inherited as GRANTED gap locks. */
- static
- void
- lock_rec_inherit_to_gap_if_gap_lock(
- /*================================*/
- rec_t* heir, /* in: record which inherits */
- rec_t* rec) /* in: record from which inherited; does NOT reset
- the locks on this record */
- {
- lock_t* lock;
-
- #ifdef UNIV_SYNC_DEBUG
- ut_ad(mutex_own(&kernel_mutex));
- #endif /* UNIV_SYNC_DEBUG */
-
- lock = lock_rec_get_first(rec);
- while (lock != NULL) {
- if (!lock_rec_get_insert_intention(lock)
- && (page_rec_is_supremum(rec)
- || !lock_rec_get_rec_not_gap(lock))) {
-
- lock_rec_add_to_queue(LOCK_REC | lock_get_mode(lock)
- | LOCK_GAP,
- heir, lock->index, lock->trx);
- }
- lock = lock_rec_get_next(rec, lock);
- }
- }
- /*****************************************************************
- Moves the locks of a record to another record and resets the lock bits of
- the donating record. */
- static
- void
- lock_rec_move(
- /*==========*/
- rec_t* receiver, /* in: record which gets locks; this record
- must have no lock requests on it! */
- rec_t* donator) /* in: record which gives locks */
- {
- lock_t* lock;
- ulint heap_no;
- ulint type_mode;
-
- #ifdef UNIV_SYNC_DEBUG
- ut_ad(mutex_own(&kernel_mutex));
- #endif /* UNIV_SYNC_DEBUG */
- heap_no = rec_get_heap_no(donator);
-
- lock = lock_rec_get_first(donator);
- ut_ad(lock_rec_get_first(receiver) == NULL);
- while (lock != NULL) {
- type_mode = lock->type_mode;
-
- lock_rec_reset_nth_bit(lock, heap_no);
- if (lock_get_wait(lock)) {
- lock_reset_lock_and_trx_wait(lock);
- }
- /* Note that we FIRST reset the bit, and then set the lock:
- the function works also if donator == receiver */
- lock_rec_add_to_queue(type_mode, receiver, lock->index,
- lock->trx);
- lock = lock_rec_get_next(donator, lock);
- }
- ut_ad(lock_rec_get_first(donator) == NULL);
- }
- /*****************************************************************
- Updates the lock table when we have reorganized a page. NOTE: we copy
- also the locks set on the infimum of the page; the infimum may carry
- locks if an update of a record is occurring on the page, and its locks
- were temporarily stored on the infimum. */
- void
- lock_move_reorganize_page(
- /*======================*/
- page_t* page, /* in: old index page, now reorganized */
- page_t* old_page) /* in: copy of the old, not reorganized page */
- {
- lock_t* lock;
- lock_t* old_lock;
- page_cur_t cur1;
- page_cur_t cur2;
- ulint old_heap_no;
- UT_LIST_BASE_NODE_T(lock_t) old_locks;
- mem_heap_t* heap = NULL;