summaryrefslogtreecommitdiff
path: root/storage/innobase/lock
diff options
context:
space:
mode:
authorGuilhem Bichot <guilhem@mysql.com>2009-08-04 13:25:19 +0200
committerGuilhem Bichot <guilhem@mysql.com>2009-08-04 13:25:19 +0200
commitb57e4dbd88671df86e2cf39aff5178976d710b64 (patch)
tree32be2bfec3ca062c65566c60ecf59b673d1f97e9 /storage/innobase/lock
parent1a0c2153a036296785dcdfa7b5f4974515616e11 (diff)
parent94efc1c6b084ed531b513e70fb66e7b7a1186b56 (diff)
downloadmariadb-git-b57e4dbd88671df86e2cf39aff5178976d710b64.tar.gz
Creation of mysql-trunk = {summit + "Innodb plugin replacing the builtin"}:
bzr branch mysql-5.1-performance-version mysql-trunk # Summit cd mysql-trunk bzr merge mysql-5.1-innodb_plugin # which is 5.1 + Innodb plugin bzr rm innobase # remove the builtin Next step: build, test fixes.
Diffstat (limited to 'storage/innobase/lock')
-rw-r--r--storage/innobase/lock/lock0iter.c90
-rw-r--r--storage/innobase/lock/lock0lock.c5189
2 files changed, 0 insertions, 5279 deletions
diff --git a/storage/innobase/lock/lock0iter.c b/storage/innobase/lock/lock0iter.c
deleted file mode 100644
index 0afa7019c86..00000000000
--- a/storage/innobase/lock/lock0iter.c
+++ /dev/null
@@ -1,90 +0,0 @@
-/******************************************************
-Lock queue iterator. Can iterate over table and record
-lock queues.
-
-(c) 2007 Innobase Oy
-
-Created July 16, 2007 Vasil Dimov
-*******************************************************/
-
-#define LOCK_MODULE_IMPLEMENTATION
-
-#include "univ.i"
-#include "lock0iter.h"
-#include "lock0lock.h"
-#include "lock0priv.h"
-#include "ut0dbg.h"
-#include "ut0lst.h"
-
-/***********************************************************************
-Initialize lock queue iterator so that it starts to iterate from
-"lock". bit_no specifies the record number within the heap where the
-record is stored. It can be undefined (ULINT_UNDEFINED) in two cases:
-1. If the lock is a table lock, thus we have a table lock queue;
-2. If the lock is a record lock and it is a wait lock. In this case
- bit_no is calculated in this function by using
- lock_rec_find_set_bit(). There is exactly one bit set in the bitmap
- of a wait lock. */
-
-void
-lock_queue_iterator_reset(
-/*======================*/
- lock_queue_iterator_t* iter, /* out: iterator */
- lock_t* lock, /* in: lock to start from */
- ulint bit_no) /* in: record number in the
- heap */
-{
- iter->current_lock = lock;
-
- if (bit_no != ULINT_UNDEFINED) {
-
- iter->bit_no = bit_no;
- } else {
-
- switch (lock_get_type(lock)) {
- case LOCK_TABLE:
- iter->bit_no = ULINT_UNDEFINED;
- break;
- case LOCK_REC:
- iter->bit_no = lock_rec_find_set_bit(lock);
- ut_a(iter->bit_no != ULINT_UNDEFINED);
- break;
- default:
- ut_error;
- }
- }
-}
-
-/***********************************************************************
-Gets the previous lock in the lock queue, returns NULL if there are no
-more locks (i.e. the current lock is the first one). The iterator is
-receded (if not-NULL is returned). */
-
-lock_t*
-lock_queue_iterator_get_prev(
-/*=========================*/
- /* out: previous lock or NULL */
- lock_queue_iterator_t* iter) /* in/out: iterator */
-{
- lock_t* prev_lock;
-
- switch (lock_get_type(iter->current_lock)) {
- case LOCK_REC:
- prev_lock = lock_rec_get_prev(
- iter->current_lock, iter->bit_no);
- break;
- case LOCK_TABLE:
- prev_lock = UT_LIST_GET_PREV(
- un_member.tab_lock.locks, iter->current_lock);
- break;
- default:
- ut_error;
- }
-
- if (prev_lock != NULL) {
-
- iter->current_lock = prev_lock;
- }
-
- return(prev_lock);
-}
diff --git a/storage/innobase/lock/lock0lock.c b/storage/innobase/lock/lock0lock.c
deleted file mode 100644
index 5afd19aa7e7..00000000000
--- a/storage/innobase/lock/lock0lock.c
+++ /dev/null
@@ -1,5189 +0,0 @@
-/******************************************************
-The transaction lock system
-
-(c) 1996 Innobase Oy
-
-Created 5/7/1996 Heikki Tuuri
-*******************************************************/
-
-#define LOCK_MODULE_IMPLEMENTATION
-
-#include "lock0lock.h"
-#include "lock0priv.h"
-
-#ifdef UNIV_NONINL
-#include "lock0lock.ic"
-#include "lock0priv.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
-
-/* Restricts the recursion depth of the search we will do in the waits-for
-graph of transactions */
-#define LOCK_MAX_DEPTH_IN_DEADLOCK_CHECK 200
-
-/* 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. */
-
-/* LOCK COMPATIBILITY MATRIX
- * IS IX S X AI
- * IS + + + - +
- * IX + + - - +
- * S + - + - -
- * X - - - - -
- * AI + + - - -
- *
- * Note that for rows, InnoDB only acquires S or X locks.
- * For tables, InnoDB normally acquires IS or IX locks.
- * S or X table locks are only acquired for LOCK TABLES.
- * Auto-increment (AI) locks are needed because of
- * statement-level MySQL binlog.
- * See also lock_mode_compatible().
- */
-
-#ifdef UNIV_DEBUG
-ibool lock_print_waits = FALSE;
-#endif /* UNIV_DEBUG */
-
-/* The lock system */
-lock_sys_t* lock_sys = NULL;
-
-/* 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 and we
- chose trx as a victim; FALSE if no deadlock, or
- there was a deadlock, but we chose other
- transaction(s) as victim(s) */
- lock_t* lock, /* in: lock the transaction is requesting */
- trx_t* trx); /* in: transaction */
-/************************************************************************
-Looks recursively for a deadlock. */
-static
-ulint
-lock_deadlock_recursive(
-/*====================*/
- /* out: 0 if no deadlock found,
- LOCK_VICTIM_IS_START if there was a deadlock
- and we chose 'start' as the victim,
- LOCK_VICTIM_IS_OTHER if a deadlock
- was found and we chose some other trx as a
- victim: we must do the search again in this
- last case because there may be another
- deadlock! */
- 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 LOCK_VICTIM_IS_START */
- ulint depth); /* in: recursion depth: if this exceeds
- LOCK_MAX_DEPTH_IN_DEADLOCK_CHECK, we
- return LOCK_VICTIM_IS_START */
-
-/*************************************************************************
-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: index */
- const ulint* offsets, /* in: rec_get_offsets(rec, index) */
- ibool has_kernel_mutex)/* in: TRUE if the caller owns the
- kernel mutex */
-{
- ibool is_ok = TRUE;
-
- ut_ad(rec_offs_validate(rec, index, offsets));
-
- 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 record\n",
- stderr);
- rec_print_new(stderr, rec, offsets);
- 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 */
- const ulint* offsets,/* in: rec_get_offsets(rec, 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));
- ut_ad(rec_offs_validate(rec, index, offsets));
-
- /* 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, offsets);
-
- return(read_view_sees_trx_id(view, trx_id));
-}
-
-/*************************************************************************
-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 */
- const 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;
- ibool ok = FALSE;
-
- ut_ad(table);
- ut_ad(trx);
-
- lock_mutex_enter_kernel();
-
- 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. */
- goto not_ok;
- }
-
- 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:
-not_ok:
- /* Other table locks than LOCK_IX are not allowed. */
- ok = FALSE;
- goto func_exit;
- }
- }
-
-func_exit:
- lock_mutex_exit_kernel();
-
- 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. */
-
-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. */
-
-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;
-
- ut_ad(mutex_own(&kernel_mutex));
- 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;
-
- ut_ad(mutex_own(&kernel_mutex));
-
- 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;
-
- ut_ad(mutex_own(&kernel_mutex));
-
- 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 */
-{
- ut_ad(mutex_own(&kernel_mutex));
- ut_ad(lock_get_type(lock) == LOCK_REC);
-
- if (page_rec_is_comp(rec)) {
- do {
- lock = lock_rec_get_next_on_page(lock);
- } while (lock && !lock_rec_get_nth_bit(
- lock, rec_get_heap_no(rec, TRUE)));
- } else {
- do {
- lock = lock_rec_get_next_on_page(lock);
- } while (lock && !lock_rec_get_nth_bit(
- lock, rec_get_heap_no(rec, FALSE)));
- }
-
- 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;
-
- ut_ad(mutex_own(&kernel_mutex));
-
- lock = lock_rec_get_first_on_page(rec);
- if (UNIV_LIKELY_NULL(lock)) {
- ulint heap_no = rec_get_heap_no(rec, page_rec_is_comp(rec));
-
- while (lock && !lock_rec_get_nth_bit(lock, heap_no)) {
- 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. */
-
-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;
-
- ut_ad(mutex_own(&kernel_mutex));
- 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;
-
- ut_ad(mutex_own(&kernel_mutex));
-
- /* 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;
-
- ut_ad(mutex_own(&kernel_mutex));
- 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);
-}
-
-#ifndef UNIV_HOTBACKUP
-/*************************************************************************
-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;
-
- ut_ad(mutex_own(&kernel_mutex));
- 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);
-}
-#endif /* !UNIV_HOTBACKUP */
-
-/*************************************************************************
-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;
-
- ut_ad(mutex_own(&kernel_mutex));
-
- 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;
-
- ut_ad(mutex_own(&kernel_mutex));
-
- heap_no = rec_get_heap_no(rec, page_rec_is_comp(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 */
- const ulint* offsets)/* in: rec_get_offsets(rec, index) */
-{
- page_t* page;
-
- ut_ad(mutex_own(&kernel_mutex));
- ut_ad(!(index->type & DICT_CLUSTERED));
- ut_ad(page_rec_is_user_rec(rec));
- ut_ad(rec_offs_validate(rec, index, offsets));
-
- 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, offsets, 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, offsets));
-}
-
-/*************************************************************************
-Return approximate number or record locks (bits set in the bitmap) for
-this transaction. Since delete-marked records may be removed, the
-record count will not be precise. */
-
-ulint
-lock_number_of_rows_locked(
-/*=======================*/
- trx_t* trx) /* in: transaction */
-{
- lock_t* lock;
- ulint n_records = 0;
- ulint n_bits;
- ulint n_bit;
-
- lock = UT_LIST_GET_FIRST(trx->trx_locks);
-
- while (lock) {
- if (lock_get_type(lock) == LOCK_REC) {
- n_bits = lock_rec_get_n_bits(lock);
-
- for (n_bit = 0; n_bit < n_bits; n_bit++) {
- if (lock_rec_get_nth_bit(lock, n_bit)) {
- n_records++;
- }
- }
- }
-
- lock = UT_LIST_GET_NEXT(trx_locks, lock);
- }
-
- return (n_records);
-}
-
-/*============== 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 */
- 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;
-
- ut_ad(mutex_own(&kernel_mutex));
-
- 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, page_is_comp(page));
-
- ut_ad(!!page_is_comp(page) == dict_table_is_comp(index->table));
-
- /* 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_dir_get_n_heap(page) + LOCK_PAGE_BITMAP_MARGIN;
- n_bytes = 1 + n_bits / 8;
-
- lock = mem_heap_alloc(trx->lock_heap, sizeof(lock_t) + n_bytes);
-
- 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;
-
- ut_ad(mutex_own(&kernel_mutex));
-
- /* 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, TRUE, index->table_name);
- fputs(".\n"
- "InnoDB: Submit a detailed bug report"
- " to http://bugs.mysql.com\n",
- 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, page_rec_is_comp(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));
-
-#ifdef UNIV_DEBUG
- 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, FALSE, index->name);
- }
-#endif /* UNIV_DEBUG */
-
- 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 */
- 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;
- ibool somebody_waits = FALSE;
-
- ut_ad(mutex_own(&kernel_mutex));
- 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;
-
- /* 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 (page_rec_is_supremum(rec)) {
- 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, page_rec_is_comp(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;
- trx_t* trx;
-
- ut_ad(mutex_own(&kernel_mutex));
- 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, page_rec_is_comp(rec));
-
- lock = lock_rec_get_first_on_page(rec);
-
- trx = thr_get_trx(thr);
-
- if (lock == NULL) {
- if (!impl) {
- lock_rec_create(mode, rec, index, trx);
- }
-
- return(TRUE);
- }
-
- if (lock_rec_get_next_on_page(lock)) {
-
- return(FALSE);
- }
-
- 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 (!lock_rec_get_nth_bit(lock, heap_no)) {
- 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;
-
- ut_ad(mutex_own(&kernel_mutex));
- 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;
-
- ut_ad(mutex_own(&kernel_mutex));
- 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;
-
- ut_ad(mutex_own(&kernel_mutex));
- 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 */
-{
- ut_ad(mutex_own(&kernel_mutex));
-
- 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;
- }
-
-#ifdef UNIV_DEBUG
- if (lock_print_waits) {
- fprintf(stderr, "Lock wait for trx %lu ends\n",
- (ulong) ut_dulint_get_low(lock->trx->id));
- }
-#endif /* UNIV_DEBUG */
-
- /* 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 */
-{
- ut_ad(mutex_own(&kernel_mutex));
- 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;
-
- ut_ad(mutex_own(&kernel_mutex));
- 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;
-
- ut_ad(mutex_own(&kernel_mutex));
- 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;
-
- ut_ad(mutex_own(&kernel_mutex));
-
- 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. */
-static
-void
-lock_rec_reset_and_release_wait(
-/*============================*/
- rec_t* rec) /* in: record whose locks bits should be reset */
-{
- lock_t* lock;
- ulint heap_no;
-
- ut_ad(mutex_own(&kernel_mutex));
-
- heap_no = rec_get_heap_no(rec, page_rec_is_comp(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;
-
- ut_ad(mutex_own(&kernel_mutex));
-
- lock = lock_rec_get_first(rec);
-
- /* If srv_locks_unsafe_for_binlog is TRUE or session is using
- READ COMMITTED isolation level, 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)
- && !((srv_locks_unsafe_for_binlog
- || lock->trx->isolation_level
- == TRX_ISO_READ_COMMITTED)
- && lock_get_mode(lock) == LOCK_X)) {
-
- 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;
-
- ut_ad(mutex_own(&kernel_mutex));
-
- 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 */
- ulint comp) /* in: nonzero=compact page format */
-{
- lock_t* lock;
- ulint heap_no;
- ulint type_mode;
-
- ut_ad(mutex_own(&kernel_mutex));
-
- heap_no = rec_get_heap_no(donator, comp);
-
- 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;
- rec_t* sup;
- ulint comp;
-
- lock_mutex_enter_kernel();
-
- lock = lock_rec_get_first_on_page(page);
-
- if (lock == NULL) {
- lock_mutex_exit_kernel();
-
- return;
- }
-
- heap = mem_heap_create(256);
-
- /* Copy first all the locks on the page to heap and reset the
- bitmaps in the original locks; chain the copies of the locks
- using the trx_locks field in them. */
-
- UT_LIST_INIT(old_locks);
-
- while (lock != NULL) {
-
- /* Make a copy of the lock */
- old_lock = lock_rec_copy(lock, heap);
-
- UT_LIST_ADD_LAST(trx_locks, old_locks, old_lock);
-
- /* Reset bitmap of lock */
- lock_rec_bitmap_reset(lock);
-
- if (lock_get_wait(lock)) {
- lock_reset_lock_and_trx_wait(lock);
- }
-
- lock = lock_rec_get_next_on_page(lock);
- }
-
- sup = page_get_supremum_rec(page);
-
- lock = UT_LIST_GET_FIRST(old_locks);
-
- comp = page_is_comp(page);
- ut_ad(comp == page_is_comp(old_page));
-
- while (lock) {
- /* NOTE: we copy also the locks set on the infimum and
- supremum 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 */
-
- page_cur_set_before_first(page, &cur1);
- page_cur_set_before_first(old_page, &cur2);
-
- /* Set locks according to old locks */
- for (;;) {
- ut_ad(comp || !memcmp(page_cur_get_rec(&cur1),
- page_cur_get_rec(&cur2),
- rec_get_data_size_old(
- page_cur_get_rec(
- &cur2))));
- old_heap_no = rec_get_heap_no(page_cur_get_rec(&cur2),
- comp);
-
- if (lock_rec_get_nth_bit(lock, old_heap_no)) {
-
- /* NOTE that the old lock bitmap could be too
- small for the new heap number! */
-
- lock_rec_add_to_queue(lock->type_mode,
- page_cur_get_rec(&cur1),
- lock->index, lock->trx);
-
- /* if ((page_cur_get_rec(&cur1) == sup)
- && lock_get_wait(lock)) {
- fprintf(stderr,
- "---\n--\n!!!Lock reorg: supr type %lu\n",
- lock->type_mode);
- } */
- }
-
- if (page_cur_get_rec(&cur1) == sup) {
-
- break;
- }
-
- page_cur_move_to_next(&cur1);
- page_cur_move_to_next(&cur2);
- }
-
- /* Remember that we chained old locks on the trx_locks field */
-
- lock = UT_LIST_GET_NEXT(trx_locks, lock);
- }
-
- lock_mutex_exit_kernel();
-
- mem_heap_free(heap);
-
-#if 0
- ut_ad(lock_rec_validate_page(buf_frame_get_space_id(page),
- buf_frame_get_page_no(page)));
-#endif
-}
-
-/*****************************************************************
-Moves the explicit locks on user records to another page if a record
-list end is moved to another page. */
-
-void
-lock_move_rec_list_end(
-/*===================*/
- page_t* new_page, /* in: index page to move to */
- page_t* page, /* in: index page */
- rec_t* rec) /* in: record on page: this is the
- first record moved */
-{
- lock_t* lock;
- page_cur_t cur1;
- page_cur_t cur2;
- ulint heap_no;
- rec_t* sup;
- ulint type_mode;
- ulint comp;
- ut_ad(page == buf_frame_align(rec));
-
- lock_mutex_enter_kernel();
-
- /* Note: when we move locks from record to record, waiting locks
- and possible granted gap type locks behind them are enqueued in
- the original order, because new elements are inserted to a hash
- table to the end of the hash chain, and lock_rec_add_to_queue
- does not reuse locks if there are waiters in the queue. */
-
- sup = page_get_supremum_rec(page);
-
- lock = lock_rec_get_first_on_page(page);
-
- comp = page_is_comp(page);
-
- while (lock != NULL) {
-
- page_cur_position(rec, &cur1);
-
- if (page_cur_is_before_first(&cur1)) {
- page_cur_move_to_next(&cur1);
- }
-
- page_cur_set_before_first(new_page, &cur2);
- page_cur_move_to_next(&cur2);
-
- /* Copy lock requests on user records to new page and
- reset the lock bits on the old */
-
- while (page_cur_get_rec(&cur1) != sup) {
- ut_ad(comp || !memcmp(page_cur_get_rec(&cur1),
- page_cur_get_rec(&cur2),
- rec_get_data_size_old(
- page_cur_get_rec(
- &cur2))));
- heap_no = rec_get_heap_no(page_cur_get_rec(&cur1),
- comp);
-
- if (lock_rec_get_nth_bit(lock, heap_no)) {
- 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);
- }
-
- lock_rec_add_to_queue(type_mode,
- page_cur_get_rec(&cur2),
- lock->index, lock->trx);
- }
-
- page_cur_move_to_next(&cur1);
- page_cur_move_to_next(&cur2);
- }
-
- lock = lock_rec_get_next_on_page(lock);
- }
-
- lock_mutex_exit_kernel();
-
-#if 0
- ut_ad(lock_rec_validate_page(buf_frame_get_space_id(page),
- buf_frame_get_page_no(page)));
- ut_ad(lock_rec_validate_page(buf_frame_get_space_id(new_page),
- buf_frame_get_page_no(new_page)));
-#endif
-}
-
-/*****************************************************************
-Moves the explicit locks on user records to another page if a record
-list start is moved to another page. */
-
-void
-lock_move_rec_list_start(
-/*=====================*/
- page_t* new_page, /* in: index page to move to */
- page_t* page, /* in: index page */
- rec_t* rec, /* in: record on page: this is the
- first record NOT copied */
- rec_t* old_end) /* in: old previous-to-last record on
- new_page before the records were copied */
-{
- lock_t* lock;
- page_cur_t cur1;
- page_cur_t cur2;
- ulint heap_no;
- ulint type_mode;
- ulint comp;
-
- ut_a(new_page);
-
- lock_mutex_enter_kernel();
-
- lock = lock_rec_get_first_on_page(page);
- comp = page_is_comp(page);
- ut_ad(comp == page_is_comp(new_page));
- ut_ad(page == buf_frame_align(rec));
-
- while (lock != NULL) {
-
- page_cur_set_before_first(page, &cur1);
- page_cur_move_to_next(&cur1);
-
- page_cur_position(old_end, &cur2);
- page_cur_move_to_next(&cur2);
-
- /* Copy lock requests on user records to new page and
- reset the lock bits on the old */
-
- while (page_cur_get_rec(&cur1) != rec) {
- ut_ad(comp || !memcmp(page_cur_get_rec(&cur1),
- page_cur_get_rec(&cur2),
- rec_get_data_size_old(
- page_cur_get_rec(
- &cur2))));
- heap_no = rec_get_heap_no(page_cur_get_rec(&cur1),
- comp);
-
- if (lock_rec_get_nth_bit(lock, heap_no)) {
- 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);
- }
-
- lock_rec_add_to_queue(type_mode,
- page_cur_get_rec(&cur2),
- lock->index, lock->trx);
- }
-
- page_cur_move_to_next(&cur1);
- page_cur_move_to_next(&cur2);
- }
-
- lock = lock_rec_get_next_on_page(lock);
- }
-
- lock_mutex_exit_kernel();
-#if 0
- ut_ad(lock_rec_validate_page(buf_frame_get_space_id(page),
- buf_frame_get_page_no(page)));
- ut_ad(lock_rec_validate_page(buf_frame_get_space_id(new_page),
- buf_frame_get_page_no(new_page)));
-#endif
-}
-
-/*****************************************************************
-Updates the lock table when a page is split to the right. */
-
-void
-lock_update_split_right(
-/*====================*/
- page_t* right_page, /* in: right page */
- page_t* left_page) /* in: left page */
-{
- ulint comp;
- lock_mutex_enter_kernel();
- comp = page_is_comp(left_page);
- ut_ad(comp == page_is_comp(right_page));
-
- /* Move the locks on the supremum of the left page to the supremum
- of the right page */
-
- lock_rec_move(page_get_supremum_rec(right_page),
- page_get_supremum_rec(left_page), comp);
-
- /* Inherit the locks to the supremum of left page from the successor
- of the infimum on right page */
-
- lock_rec_inherit_to_gap(page_get_supremum_rec(left_page),
- page_rec_get_next(
- page_get_infimum_rec(right_page)));
-
- lock_mutex_exit_kernel();
-}
-
-/*****************************************************************
-Updates the lock table when a page is merged to the right. */
-
-void
-lock_update_merge_right(
-/*====================*/
- rec_t* orig_succ, /* in: original successor of infimum
- on the right page before merge */
- page_t* left_page) /* in: merged index page which will be
- discarded */
-{
- lock_mutex_enter_kernel();
-
- /* Inherit the locks from the supremum of the left page to the
- original successor of infimum on the right page, to which the left
- page was merged */
-
- lock_rec_inherit_to_gap(orig_succ, page_get_supremum_rec(left_page));
-
- /* Reset the locks on the supremum of the left page, releasing
- waiting transactions */
-
- lock_rec_reset_and_release_wait(page_get_supremum_rec(left_page));
-
- lock_rec_free_all_from_discard_page(left_page);
-
- lock_mutex_exit_kernel();
-}
-
-/*****************************************************************
-Updates the lock table when the root page is copied to another in
-btr_root_raise_and_insert. Note that we leave lock structs on the
-root page, even though they do not make sense on other than leaf
-pages: the reason is that in a pessimistic update the infimum record
-of the root page will act as a dummy carrier of the locks of the record
-to be updated. */
-
-void
-lock_update_root_raise(
-/*===================*/
- page_t* new_page, /* in: index page to which copied */
- page_t* root) /* in: root page */
-{
- ulint comp;
- lock_mutex_enter_kernel();
- comp = page_is_comp(root);
- ut_ad(comp == page_is_comp(new_page));
-
- /* Move the locks on the supremum of the root to the supremum
- of new_page */
-
- lock_rec_move(page_get_supremum_rec(new_page),
- page_get_supremum_rec(root), comp);
- lock_mutex_exit_kernel();
-}
-
-/*****************************************************************
-Updates the lock table when a page is copied to another and the original page
-is removed from the chain of leaf pages, except if page is the root! */
-
-void
-lock_update_copy_and_discard(
-/*=========================*/
- page_t* new_page, /* in: index page to which copied */
- page_t* page) /* in: index page; NOT the root! */
-{
- ulint comp;
- lock_mutex_enter_kernel();
- comp = page_is_comp(page);
- ut_ad(comp == page_is_comp(new_page));
-
- /* Move the locks on the supremum of the old page to the supremum
- of new_page */
-
- lock_rec_move(page_get_supremum_rec(new_page),
- page_get_supremum_rec(page), comp);
- lock_rec_free_all_from_discard_page(page);
-
- lock_mutex_exit_kernel();
-}
-
-/*****************************************************************
-Updates the lock table when a page is split to the left. */
-
-void
-lock_update_split_left(
-/*===================*/
- page_t* right_page, /* in: right page */
- page_t* left_page) /* in: left page */
-{
- lock_mutex_enter_kernel();
-
- /* Inherit the locks to the supremum of the left page from the
- successor of the infimum on the right page */
-
- lock_rec_inherit_to_gap(page_get_supremum_rec(left_page),
- page_rec_get_next(
- page_get_infimum_rec(right_page)));
-
- lock_mutex_exit_kernel();
-}
-
-/*****************************************************************
-Updates the lock table when a page is merged to the left. */
-
-void
-lock_update_merge_left(
-/*===================*/
- page_t* left_page, /* in: left page to which merged */
- rec_t* orig_pred, /* in: original predecessor of supremum
- on the left page before merge */
- page_t* right_page) /* in: merged index page which will be
- discarded */
-{
- rec_t* left_next_rec;
- rec_t* left_supremum;
- ulint comp;
- lock_mutex_enter_kernel();
- comp = page_is_comp(left_page);
- ut_ad(comp == page_is_comp(right_page));
- ut_ad(left_page == buf_frame_align(orig_pred));
-
- left_next_rec = page_rec_get_next(orig_pred);
- left_supremum = page_get_supremum_rec(left_page);
-
- if (UNIV_LIKELY(left_next_rec != left_supremum)) {
-
- /* Inherit the locks on the supremum of the left page to the
- first record which was moved from the right page */
-
- lock_rec_inherit_to_gap(left_next_rec, left_supremum);
-
- /* Reset the locks on the supremum of the left page,
- releasing waiting transactions */
-
- lock_rec_reset_and_release_wait(left_supremum);
- }
-
- /* Move the locks from the supremum of right page to the supremum
- of the left page */
-
- lock_rec_move(left_supremum, page_get_supremum_rec(right_page), comp);
-
- lock_rec_free_all_from_discard_page(right_page);
-
- lock_mutex_exit_kernel();
-}
-
-/*****************************************************************
-Resets the original locks on heir and replaces them with gap type locks
-inherited from rec. */
-
-void
-lock_rec_reset_and_inherit_gap_locks(
-/*=================================*/
- rec_t* heir, /* in: heir record */
- rec_t* rec) /* in: record */
-{
- mutex_enter(&kernel_mutex);
-
- lock_rec_reset_and_release_wait(heir);
-
- lock_rec_inherit_to_gap(heir, rec);
-
- mutex_exit(&kernel_mutex);
-}
-
-/*****************************************************************
-Updates the lock table when a page is discarded. */
-
-void
-lock_update_discard(
-/*================*/
- rec_t* heir, /* in: record which will inherit the locks */
- page_t* page) /* in: index page which will be discarded */
-{
- rec_t* rec;
-
- lock_mutex_enter_kernel();
-
- if (NULL == lock_rec_get_first_on_page(page)) {
- /* No locks exist on page, nothing to do */
-
- lock_mutex_exit_kernel();
-
- return;
- }
-
- /* Inherit all the locks on the page to the record and reset all
- the locks on the page */
-
- rec = page_get_infimum_rec(page);
-
- for (;;) {
- lock_rec_inherit_to_gap(heir, rec);
-
- /* Reset the locks on rec, releasing waiting transactions */
-
- lock_rec_reset_and_release_wait(rec);
-
- if (page_rec_is_supremum(rec)) {
-
- break;
- }
-
- rec = page_rec_get_next(rec);
- }
-
- lock_rec_free_all_from_discard_page(page);
-
- lock_mutex_exit_kernel();
-}
-
-/*****************************************************************
-Updates the lock table when a new user record is inserted. */
-
-void
-lock_update_insert(
-/*===============*/
- rec_t* rec) /* in: the inserted record */
-{
- lock_mutex_enter_kernel();
-
- /* Inherit the gap-locking locks for rec, in gap mode, from the next
- record */
-
- lock_rec_inherit_to_gap_if_gap_lock(rec, page_rec_get_next(rec));
-
- lock_mutex_exit_kernel();
-}
-
-/*****************************************************************
-Updates the lock table when a record is removed. */
-
-void
-lock_update_delete(
-/*===============*/
- rec_t* rec) /* in: the record to be removed */
-{
- lock_mutex_enter_kernel();
-
- /* Let the next record inherit the locks from rec, in gap mode */
-
- lock_rec_inherit_to_gap(page_rec_get_next(rec), rec);
-
- /* Reset the lock bits on rec and release waiting transactions */
-
- lock_rec_reset_and_release_wait(rec);
-
- lock_mutex_exit_kernel();
-}
-
-/*************************************************************************
-Stores on the page infimum record the explicit locks of another record.
-This function is used to store the lock state of a record when it is
-updated and the size of the record changes in the update. The record
-is moved in such an update, perhaps to another page. The infimum record
-acts as a dummy carrier record, taking care of lock releases while the
-actual record is being moved. */
-
-void
-lock_rec_store_on_page_infimum(
-/*===========================*/
- page_t* page, /* in: page containing the record */
- rec_t* rec) /* in: record whose lock state is stored
- on the infimum record of the same page; lock
- bits are reset on the record */
-{
- ut_ad(page == buf_frame_align(rec));
-
- lock_mutex_enter_kernel();
-
- lock_rec_move(page_get_infimum_rec(page), rec, page_is_comp(page));
-
- lock_mutex_exit_kernel();
-}
-
-/*************************************************************************
-Restores the state of explicit lock requests on a single record, where the
-state was stored on the infimum of the page. */
-
-void
-lock_rec_restore_from_page_infimum(
-/*===============================*/
- rec_t* rec, /* in: record whose lock state is restored */
- page_t* page) /* in: page (rec is not necessarily on this page)
- whose infimum stored the lock state; lock bits are
- reset on the infimum */
-{
- ulint comp;
- lock_mutex_enter_kernel();
- comp = page_is_comp(page);
- ut_ad(!comp == !page_rec_is_comp(rec));
-
- lock_rec_move(rec, page_get_infimum_rec(page), comp);
-
- lock_mutex_exit_kernel();
-}
-
-/*=========== DEADLOCK CHECKING ======================================*/
-
-/************************************************************************
-Checks if a lock request results in a deadlock. */
-static
-ibool
-lock_deadlock_occurs(
-/*=================*/
- /* out: TRUE if a deadlock was detected and we
- chose trx as a victim; FALSE if no deadlock, or
- there was a deadlock, but we chose other
- transaction(s) as victim(s) */
- lock_t* lock, /* in: lock the transaction is requesting */
- trx_t* trx) /* in: transaction */
-{
- dict_table_t* table;
- dict_index_t* index;
- trx_t* mark_trx;
- ulint ret;
- ulint cost = 0;
-
- ut_ad(trx);
- ut_ad(lock);
- ut_ad(mutex_own(&kernel_mutex));
-retry:
- /* We check that adding this trx to the waits-for graph
- does not produce a cycle. First mark all active transactions
- with 0: */
-
- mark_trx = UT_LIST_GET_FIRST(trx_sys->trx_list);
-
- while (mark_trx) {
- mark_trx->deadlock_mark = 0;
- mark_trx = UT_LIST_GET_NEXT(trx_list, mark_trx);
- }
-
- ret = lock_deadlock_recursive(trx, trx, lock, &cost, 0);
-
- if (ret == LOCK_VICTIM_IS_OTHER) {
- /* We chose some other trx as a victim: retry if there still
- is a deadlock */
-
- goto retry;
- }
-
- if (ret == LOCK_VICTIM_IS_START) {
- if (lock_get_type(lock) & LOCK_TABLE) {
- table = lock->un_member.tab_lock.table;
- index = NULL;
- } else {
- index = lock->index;
- table = index->table;
- }
-
- lock_deadlock_found = TRUE;
-
- fputs("*** WE ROLL BACK TRANSACTION (2)\n",
- lock_latest_err_file);
-
- return(TRUE);
- }
-
- return(FALSE);
-}
-
-/************************************************************************
-Looks recursively for a deadlock. */
-static
-ulint
-lock_deadlock_recursive(
-/*====================*/
- /* out: 0 if no deadlock found,
- LOCK_VICTIM_IS_START if there was a deadlock
- and we chose 'start' as the victim,
- LOCK_VICTIM_IS_OTHER if a deadlock
- was found and we chose some other trx as a
- victim: we must do the search again in this
- last case because there may be another
- deadlock! */
- 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 LOCK_VICTIM_IS_START */
- ulint depth) /* in: recursion depth: if this exceeds
- LOCK_MAX_DEPTH_IN_DEADLOCK_CHECK, we
- return LOCK_VICTIM_IS_START */
-{
- lock_t* lock;
- ulint bit_no = ULINT_UNDEFINED;
- trx_t* lock_trx;
- ulint ret;
-
- ut_a(trx);
- ut_a(start);
- ut_a(wait_lock);
- ut_ad(mutex_own(&kernel_mutex));
-
- if (trx->deadlock_mark == 1) {
- /* We have already exhaustively searched the subtree starting
- from this trx */
-
- return(0);
- }
-
- *cost = *cost + 1;
-
- lock = wait_lock;
-
- if (lock_get_type(wait_lock) == LOCK_REC) {
-
- bit_no = lock_rec_find_set_bit(wait_lock);
-
- ut_a(bit_no != ULINT_UNDEFINED);
- }
-
- /* Look at the locks ahead of wait_lock in the lock queue */
-
- for (;;) {
- if (lock_get_type(lock) & LOCK_TABLE) {
-
- lock = UT_LIST_GET_PREV(un_member.tab_lock.locks,
- lock);
- } else {
- ut_ad(lock_get_type(lock) == LOCK_REC);
- ut_a(bit_no != ULINT_UNDEFINED);
-
- lock = lock_rec_get_prev(lock, bit_no);
- }
-
- if (lock == NULL) {
- /* We can mark this subtree as searched */
- trx->deadlock_mark = 1;
-
- return(FALSE);
- }
-
- if (lock_has_to_wait(wait_lock, lock)) {
-
- ibool too_far
- = depth > LOCK_MAX_DEPTH_IN_DEADLOCK_CHECK
- || *cost > LOCK_MAX_N_STEPS_IN_DEADLOCK_CHECK;
-
- lock_trx = lock->trx;
-
- if (lock_trx == start || too_far) {
-
- /* We came back to the recursion starting
- point: a deadlock detected; or we have
- searched the waits-for graph too long */
-
- FILE* ef = lock_latest_err_file;
-
- rewind(ef);
- ut_print_timestamp(ef);
-
- fputs("\n*** (1) TRANSACTION:\n", ef);
-
- trx_print(ef, wait_lock->trx, 3000);
-
- fputs("*** (1) WAITING FOR THIS LOCK"
- " TO BE GRANTED:\n", ef);
-
- if (lock_get_type(wait_lock) == LOCK_REC) {
- lock_rec_print(ef, wait_lock);
- } else {
- lock_table_print(ef, wait_lock);
- }
-
- fputs("*** (2) TRANSACTION:\n", ef);
-
- trx_print(ef, lock->trx, 3000);
-
- fputs("*** (2) HOLDS THE LOCK(S):\n", ef);
-
- if (lock_get_type(lock) == LOCK_REC) {
- lock_rec_print(ef, lock);
- } else {
- lock_table_print(ef, lock);
- }
-
- fputs("*** (2) WAITING FOR THIS LOCK"
- " TO BE GRANTED:\n", ef);
-
- if (lock_get_type(start->wait_lock)
- == LOCK_REC) {
- lock_rec_print(ef, start->wait_lock);
- } else {
- lock_table_print(ef, start->wait_lock);
- }
-#ifdef UNIV_DEBUG
- if (lock_print_waits) {
- fputs("Deadlock detected"
- " or too long search\n",
- stderr);
- }
-#endif /* UNIV_DEBUG */
- if (too_far) {
-
- fputs("TOO DEEP OR LONG SEARCH"
- " IN THE LOCK TABLE"
- " WAITS-FOR GRAPH\n", ef);
-
- return(LOCK_VICTIM_IS_START);
- }
-
- if (trx_weight_cmp(wait_lock->trx,
- start) >= 0) {
- /* Our recursion starting point
- transaction is 'smaller', let us
- choose 'start' as the victim and roll
- back it */
-
- return(LOCK_VICTIM_IS_START);
- }
-
- lock_deadlock_found = TRUE;
-
- /* Let us choose the transaction of wait_lock
- as a victim to try to avoid deadlocking our
- recursion starting point transaction */
-
- fputs("*** WE ROLL BACK TRANSACTION (1)\n",
- ef);
-
- wait_lock->trx->was_chosen_as_deadlock_victim
- = TRUE;
-
- lock_cancel_waiting_and_release(wait_lock);
-
- /* Since trx and wait_lock are no longer
- in the waits-for graph, we can return FALSE;
- note that our selective algorithm can choose
- several transactions as victims, but still
- we may end up rolling back also the recursion
- starting point transaction! */
-
- return(LOCK_VICTIM_IS_OTHER);
- }
-
- if (lock_trx->que_state == TRX_QUE_LOCK_WAIT) {
-
- /* Another trx ahead has requested lock in an
- incompatible mode, and is itself waiting for
- a lock */
-
- ret = lock_deadlock_recursive(
- start, lock_trx,
- lock_trx->wait_lock, cost, depth + 1);
- if (ret != 0) {
-
- return(ret);
- }
- }
- }
- }/* end of the 'for (;;)'-loop */
-}
-
-/*========================= TABLE LOCKS ==============================*/
-
-/*************************************************************************
-Creates a table lock object and adds it as the last in the lock queue
-of the table. Does NOT check for deadlocks or lock compatibility. */
-UNIV_INLINE
-lock_t*
-lock_table_create(
-/*==============*/
- /* out, own: new lock object */
- dict_table_t* table, /* in: database table in dictionary cache */
- ulint type_mode,/* in: lock mode possibly ORed with
- LOCK_WAIT */
- trx_t* trx) /* in: trx */
-{
- lock_t* lock;
-
- ut_ad(table && trx);
- ut_ad(mutex_own(&kernel_mutex));
-
- if ((type_mode & LOCK_MODE_MASK) == LOCK_AUTO_INC) {
- ++table->n_waiting_or_granted_auto_inc_locks;
- }
-
- if (type_mode == LOCK_AUTO_INC) {
- /* Only one trx can have the lock on the table
- at a time: we may use the memory preallocated
- to the table object */
-
- lock = table->auto_inc_lock;
-
- ut_a(trx->auto_inc_lock == NULL);
- trx->auto_inc_lock = lock;
- } else {
- lock = mem_heap_alloc(trx->lock_heap, sizeof(lock_t));
- }
-
- UT_LIST_ADD_LAST(trx_locks, trx->trx_locks, lock);
-
- lock->type_mode = type_mode | LOCK_TABLE;
- lock->trx = trx;
-
- lock->un_member.tab_lock.table = table;
-
- UT_LIST_ADD_LAST(un_member.tab_lock.locks, table->locks, lock);
-
- if (type_mode & LOCK_WAIT) {
-
- lock_set_lock_and_trx_wait(lock, trx);
- }
-
- return(lock);
-}
-
-/*****************************************************************
-Removes a table lock request from the queue and the trx list of locks;
-this is a low-level function which does NOT check if waiting requests
-can now be granted. */
-UNIV_INLINE
-void
-lock_table_remove_low(
-/*==================*/
- lock_t* lock) /* in: table lock */
-{
- dict_table_t* table;
- trx_t* trx;
-
- ut_ad(mutex_own(&kernel_mutex));
-
- table = lock->un_member.tab_lock.table;
- trx = lock->trx;
-
- if (lock == trx->auto_inc_lock) {
- trx->auto_inc_lock = NULL;
-
- ut_a(table->n_waiting_or_granted_auto_inc_locks > 0);
- --table->n_waiting_or_granted_auto_inc_locks;
- }
-
- UT_LIST_REMOVE(trx_locks, trx->trx_locks, lock);
- UT_LIST_REMOVE(un_member.tab_lock.locks, table->locks, lock);
-}
-
-/*************************************************************************
-Enqueues a waiting request for a table lock which cannot be granted
-immediately. Checks for deadlocks. */
-static
-ulint
-lock_table_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 mode, /* in: lock mode this transaction is
- requesting */
- dict_table_t* table, /* in: table */
- que_thr_t* thr) /* in: query thread */
-{
- lock_t* lock;
- trx_t* trx;
-
- ut_ad(mutex_own(&kernel_mutex));
-
- /* 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 table lock wait happens"
- " in a dictionary operation!\n"
- "InnoDB: Table name ", stderr);
- ut_print_name(stderr, trx, TRUE, table->name);
- fputs(".\n"
- "InnoDB: Submit a detailed bug report"
- " to http://bugs.mysql.com\n",
- stderr);
- }
-
- /* Enqueue the lock request that will wait to be granted */
-
- lock = lock_table_create(table, mode | LOCK_WAIT, 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_table_remove_low(lock);
-
- return(DB_DEADLOCK);
- }
-
- if (trx->wait_lock == NULL) {
- /* Deadlock resolution chose another transaction as a victim,
- and we accidentally got our lock granted! */
-
- 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));
-
- return(DB_LOCK_WAIT);
-}
-
-/*************************************************************************
-Checks if other transactions have an incompatible mode lock request in
-the lock queue. */
-UNIV_INLINE
-ibool
-lock_table_other_has_incompatible(
-/*==============================*/
- trx_t* trx, /* in: transaction, or NULL if all
- transactions should be included */
- ulint wait, /* in: LOCK_WAIT if also waiting locks are
- taken into account, or 0 if not */
- dict_table_t* table, /* in: table */
- ulint mode) /* in: lock mode */
-{
- lock_t* lock;
-
- ut_ad(mutex_own(&kernel_mutex));
-
- lock = UT_LIST_GET_LAST(table->locks);
-
- while (lock != NULL) {
-
- if ((lock->trx != trx)
- && (!lock_mode_compatible(lock_get_mode(lock), mode))
- && (wait || !(lock_get_wait(lock)))) {
-
- return(TRUE);
- }
-
- lock = UT_LIST_GET_PREV(un_member.tab_lock.locks, lock);
- }
-
- return(FALSE);
-}
-
-/*************************************************************************
-Locks the specified database table in the mode given. If the lock cannot
-be granted immediately, the query thread is put to wait. */
-
-ulint
-lock_table(
-/*=======*/
- /* out: DB_SUCCESS, DB_LOCK_WAIT,
- DB_DEADLOCK, or DB_QUE_THR_SUSPENDED */
- ulint flags, /* in: if BTR_NO_LOCKING_FLAG bit is set,
- does nothing */
- dict_table_t* table, /* in: database table in dictionary cache */
- ulint mode, /* in: lock mode */
- que_thr_t* thr) /* in: query thread */
-{
- trx_t* trx;
- ulint err;
-
- ut_ad(table && thr);
-
- if (flags & BTR_NO_LOCKING_FLAG) {
-
- return(DB_SUCCESS);
- }
-
- ut_a(flags == 0);
-
- trx = thr_get_trx(thr);
-
- lock_mutex_enter_kernel();
-
- /* Look for stronger locks the same trx already has on the table */
-
- if (lock_table_has(trx, table, mode)) {
-
- lock_mutex_exit_kernel();
-
- return(DB_SUCCESS);
- }
-
- /* We have to check if the new lock is compatible with any locks
- other transactions have in the table lock queue. */
-
- if (lock_table_other_has_incompatible(trx, LOCK_WAIT, table, mode)) {
-
- /* Another trx has a request on the table in an incompatible
- mode: this trx may have to wait */
-
- err = lock_table_enqueue_waiting(mode | flags, table, thr);
-
- lock_mutex_exit_kernel();
-
- return(err);
- }
-
- lock_table_create(table, mode | flags, trx);
-
- ut_a(!flags || mode == LOCK_S || mode == LOCK_X);
-
- lock_mutex_exit_kernel();
-
- return(DB_SUCCESS);
-}
-
-/*************************************************************************
-Checks if there are any locks set on the table. */
-
-ibool
-lock_is_on_table(
-/*=============*/
- /* out: TRUE if there are lock(s) */
- dict_table_t* table) /* in: database table in dictionary cache */
-{
- ibool ret;
-
- ut_ad(table);
-
- lock_mutex_enter_kernel();
-
- if (UT_LIST_GET_LAST(table->locks)) {
- ret = TRUE;
- } else {
- ret = FALSE;
- }
-
- lock_mutex_exit_kernel();
-
- return(ret);
-}
-
-/*************************************************************************
-Checks if a waiting table lock request still has to wait in a queue. */
-static
-ibool
-lock_table_has_to_wait_in_queue(
-/*============================*/
- /* out: TRUE if still has to wait */
- lock_t* wait_lock) /* in: waiting table lock */
-{
- dict_table_t* table;
- lock_t* lock;
-
- ut_ad(mutex_own(&kernel_mutex));
- ut_ad(lock_get_wait(wait_lock));
-
- table = wait_lock->un_member.tab_lock.table;
-
- lock = UT_LIST_GET_FIRST(table->locks);
-
- while (lock != wait_lock) {
-
- if (lock_has_to_wait(wait_lock, lock)) {
-
- return(TRUE);
- }
-
- lock = UT_LIST_GET_NEXT(un_member.tab_lock.locks, lock);
- }
-
- return(FALSE);
-}
-
-/*****************************************************************
-Removes a table 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. */
-static
-void
-lock_table_dequeue(
-/*===============*/
- lock_t* in_lock)/* in: table lock object; transactions waiting
- behind will get their lock requests granted, if
- they are now qualified to it */
-{
- lock_t* lock;
-
- ut_ad(mutex_own(&kernel_mutex));
- ut_a(lock_get_type(in_lock) == LOCK_TABLE);
-
- lock = UT_LIST_GET_NEXT(un_member.tab_lock.locks, in_lock);
-
- lock_table_remove_low(in_lock);
-
- /* Check if waiting locks in the queue can now be granted: grant
- locks if there are no conflicting locks ahead. */
-
- while (lock != NULL) {
-
- if (lock_get_wait(lock)
- && !lock_table_has_to_wait_in_queue(lock)) {
-
- /* Grant the lock */
- lock_grant(lock);
- }
-
- lock = UT_LIST_GET_NEXT(un_member.tab_lock.locks, lock);
- }
-}
-
-/*=========================== LOCK RELEASE ==============================*/
-
-/*****************************************************************
-Removes a granted record lock of a transaction from the queue and grants
-locks to other transactions waiting in the queue if they now are entitled
-to a lock. */
-
-void
-lock_rec_unlock(
-/*============*/
- trx_t* trx, /* in: transaction that has set a record
- lock */
- rec_t* rec, /* in: record */
- ulint lock_mode) /* in: LOCK_S or LOCK_X */
-{
- lock_t* lock;
- lock_t* release_lock = NULL;
- ulint heap_no;
-
- ut_ad(trx && rec);
-
- mutex_enter(&kernel_mutex);
-
- heap_no = rec_get_heap_no(rec, page_rec_is_comp(rec));
-
- lock = lock_rec_get_first(rec);
-
- /* Find the last lock with the same lock_mode and transaction
- from the record. */
-
- while (lock != NULL) {
- if (lock->trx == trx && lock_get_mode(lock) == lock_mode) {
- release_lock = lock;
- ut_a(!lock_get_wait(lock));
- }
-
- lock = lock_rec_get_next(rec, lock);
- }
-
- /* If a record lock is found, release the record lock */
-
- if (UNIV_LIKELY(release_lock != NULL)) {
- lock_rec_reset_nth_bit(release_lock, heap_no);
- } else {
- mutex_exit(&kernel_mutex);
- ut_print_timestamp(stderr);
- fprintf(stderr,
- " InnoDB: Error: unlock row could not"
- " find a %lu mode lock on the record\n",
- (ulong) lock_mode);
-
- return;
- }
-
- /* Check if we can now grant waiting lock requests */
-
- lock = lock_rec_get_first(rec);
-
- 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(rec, lock);
- }
-
- mutex_exit(&kernel_mutex);
-}
-
-/*************************************************************************
-Releases a table lock.
-Releases possible other transactions waiting for this lock. */
-
-void
-lock_table_unlock(
-/*==============*/
- lock_t* lock) /* in: lock */
-{
- mutex_enter(&kernel_mutex);
-
- lock_table_dequeue(lock);
-
- mutex_exit(&kernel_mutex);
-}
-
-/*************************************************************************
-Releases an auto-inc lock a transaction possibly has on a table.
-Releases possible other transactions waiting for this lock. */
-
-void
-lock_table_unlock_auto_inc(
-/*=======================*/
- trx_t* trx) /* in: transaction */
-{
- if (trx->auto_inc_lock) {
- mutex_enter(&kernel_mutex);
-
- lock_table_dequeue(trx->auto_inc_lock);
-
- mutex_exit(&kernel_mutex);
- }
-}
-
-/*************************************************************************
-Releases transaction locks, and releases possible other transactions waiting
-because of these locks. */
-
-void
-lock_release_off_kernel(
-/*====================*/
- trx_t* trx) /* in: transaction */
-{
- dict_table_t* table;
- ulint count;
- lock_t* lock;
-
- ut_ad(mutex_own(&kernel_mutex));
-
- lock = UT_LIST_GET_LAST(trx->trx_locks);
-
- count = 0;
-
- while (lock != NULL) {
-
- count++;
-
- if (lock_get_type(lock) == LOCK_REC) {
-
- lock_rec_dequeue_from_page(lock);
- } else {
- ut_ad(lock_get_type(lock) & LOCK_TABLE);
-
- if (lock_get_mode(lock) != LOCK_IS
- && 0 != ut_dulint_cmp(trx->undo_no,
- ut_dulint_zero)) {
-
- /* The trx may have modified the table. We
- block the use of the MySQL query cache for
- all currently active transactions. */
-
- table = lock->un_member.tab_lock.table;
-
- table->query_cache_inv_trx_id
- = trx_sys->max_trx_id;
- }
-
- lock_table_dequeue(lock);
- }
-
- if (count == LOCK_RELEASE_KERNEL_INTERVAL) {
- /* Release the kernel mutex for a while, so that we
- do not monopolize it */
-
- lock_mutex_exit_kernel();
-
- lock_mutex_enter_kernel();
-
- count = 0;
- }
-
- lock = UT_LIST_GET_LAST(trx->trx_locks);
- }
-
- mem_heap_empty(trx->lock_heap);
-
- ut_a(trx->auto_inc_lock == NULL);
-}
-
-/*************************************************************************
-Cancels a waiting lock request and releases possible other transactions
-waiting behind it. */
-
-void
-lock_cancel_waiting_and_release(
-/*============================*/
- lock_t* lock) /* in: waiting lock request */
-{
- ut_ad(mutex_own(&kernel_mutex));
-
- if (lock_get_type(lock) == LOCK_REC) {
-
- lock_rec_dequeue_from_page(lock);
- } else {
- ut_ad(lock_get_type(lock) & LOCK_TABLE);
-
- lock_table_dequeue(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);
-}
-
-/* True if a lock mode is S or X */
-#define IS_LOCK_S_OR_X(lock) \
- (lock_get_mode(lock) == LOCK_S \
- || lock_get_mode(lock) == LOCK_X)
-
-
-/*************************************************************************
-Removes locks of a transaction on a table to be dropped.
-If remove_also_table_sx_locks is TRUE then table-level S and X locks are
-also removed in addition to other table-level and record-level locks.
-No lock, that is going to be removed, is allowed to be a wait lock. */
-static
-void
-lock_remove_all_on_table_for_trx(
-/*=============================*/
- dict_table_t* table, /* in: table to be dropped */
- trx_t* trx, /* in: a transaction */
- ibool remove_also_table_sx_locks)/* in: also removes
- table S and X locks */
-{
- lock_t* lock;
- lock_t* prev_lock;
-
- ut_ad(mutex_own(&kernel_mutex));
-
- lock = UT_LIST_GET_LAST(trx->trx_locks);
-
- while (lock != NULL) {
- prev_lock = UT_LIST_GET_PREV(trx_locks, lock);
-
- if (lock_get_type(lock) == LOCK_REC
- && lock->index->table == table) {
- ut_a(!lock_get_wait(lock));
-
- lock_rec_discard(lock);
- } else if (lock_get_type(lock) & LOCK_TABLE
- && lock->un_member.tab_lock.table == table
- && (remove_also_table_sx_locks
- || !IS_LOCK_S_OR_X(lock))) {
-
- ut_a(!lock_get_wait(lock));
-
- lock_table_remove_low(lock);
- }
-
- lock = prev_lock;
- }
-}
-
-/*************************************************************************
-Removes locks on a table to be dropped or truncated.
-If remove_also_table_sx_locks is TRUE then table-level S and X locks are
-also removed in addition to other table-level and record-level locks.
-No lock, that is going to be removed, is allowed to be a wait lock. */
-
-void
-lock_remove_all_on_table(
-/*=====================*/
- dict_table_t* table, /* in: table to be dropped
- or truncated */
- ibool remove_also_table_sx_locks)/* in: also removes
- table S and X locks */
-{
- lock_t* lock;
- lock_t* prev_lock;
-
- mutex_enter(&kernel_mutex);
-
- lock = UT_LIST_GET_FIRST(table->locks);
-
- while (lock != NULL) {
-
- prev_lock = UT_LIST_GET_PREV(un_member.tab_lock.locks,
- lock);
-
- /* If we should remove all locks (remove_also_table_sx_locks
- is TRUE), or if the lock is not table-level S or X lock,
- then check we are not going to remove a wait lock. */
- if (remove_also_table_sx_locks
- || !(lock_get_type(lock) == LOCK_TABLE
- && IS_LOCK_S_OR_X(lock))) {
-
- ut_a(!lock_get_wait(lock));
- }
-
- lock_remove_all_on_table_for_trx(table, lock->trx,
- remove_also_table_sx_locks);
-
- if (prev_lock == NULL) {
- if (lock == UT_LIST_GET_FIRST(table->locks)) {
- /* lock was not removed, pick its successor */
- lock = UT_LIST_GET_NEXT(
- un_member.tab_lock.locks, lock);
- } else {
- /* lock was removed, pick the first one */
- lock = UT_LIST_GET_FIRST(table->locks);
- }
- } else if (UT_LIST_GET_NEXT(un_member.tab_lock.locks,
- prev_lock) != lock) {
- /* If lock was removed by
- lock_remove_all_on_table_for_trx() then pick the
- successor of prev_lock ... */
- lock = UT_LIST_GET_NEXT(
- un_member.tab_lock.locks, prev_lock);
- } else {
- /* ... otherwise pick the successor of lock. */
- lock = UT_LIST_GET_NEXT(
- un_member.tab_lock.locks, lock);
- }
- }
-
- mutex_exit(&kernel_mutex);
-}
-
-/*===================== VALIDATION AND DEBUGGING ====================*/
-
-/*************************************************************************
-Prints info of a table lock. */
-
-void
-lock_table_print(
-/*=============*/
- FILE* file, /* in: file where to print */
- lock_t* lock) /* in: table type lock */
-{
- ut_ad(mutex_own(&kernel_mutex));
- ut_a(lock_get_type(lock) == LOCK_TABLE);
-
- fputs("TABLE LOCK table ", file);
- ut_print_name(file, lock->trx, TRUE,
- lock->un_member.tab_lock.table->name);
- fprintf(file, " trx id %lu %lu",
- (ulong) (lock->trx)->id.high, (ulong) (lock->trx)->id.low);
-
- if (lock_get_mode(lock) == LOCK_S) {
- fputs(" lock mode S", file);
- } else if (lock_get_mode(lock) == LOCK_X) {
- fputs(" lock mode X", file);
- } else if (lock_get_mode(lock) == LOCK_IS) {
- fputs(" lock mode IS", file);
- } else if (lock_get_mode(lock) == LOCK_IX) {
- fputs(" lock mode IX", file);
- } else if (lock_get_mode(lock) == LOCK_AUTO_INC) {
- fputs(" lock mode AUTO-INC", file);
- } else {
- fprintf(file, " unknown lock mode %lu",
- (ulong) lock_get_mode(lock));
- }
-
- if (lock_get_wait(lock)) {
- fputs(" waiting", file);
- }
-
- putc('\n', file);
-}
-
-/*************************************************************************
-Prints info of a record lock. */
-
-void
-lock_rec_print(
-/*===========*/
- FILE* file, /* in: file where to print */
- lock_t* lock) /* in: record type lock */
-{
- page_t* page;
- ulint space;
- ulint page_no;
- ulint i;
- mtr_t mtr;
- mem_heap_t* heap = NULL;
- ulint offsets_[REC_OFFS_NORMAL_SIZE];
- ulint* offsets = offsets_;
- *offsets_ = (sizeof offsets_) / sizeof *offsets_;
-
- ut_ad(mutex_own(&kernel_mutex));
- ut_a(lock_get_type(lock) == LOCK_REC);
-
- space = lock->un_member.rec_lock.space;
- page_no = lock->un_member.rec_lock.page_no;
-
- fprintf(file, "RECORD LOCKS space id %lu page no %lu n bits %lu ",
- (ulong) space, (ulong) page_no,
- (ulong) lock_rec_get_n_bits(lock));
- dict_index_name_print(file, lock->trx, lock->index);
- fprintf(file, " trx id %lu %lu",
- (ulong) (lock->trx)->id.high,
- (ulong) (lock->trx)->id.low);
-
- if (lock_get_mode(lock) == LOCK_S) {
- fputs(" lock mode S", file);
- } else if (lock_get_mode(lock) == LOCK_X) {
- fputs(" lock_mode X", file);
- } else {
- ut_error;
- }
-
- if (lock_rec_get_gap(lock)) {
- fputs(" locks gap before rec", file);
- }
-
- if (lock_rec_get_rec_not_gap(lock)) {
- fputs(" locks rec but not gap", file);
- }
-
- if (lock_rec_get_insert_intention(lock)) {
- fputs(" insert intention", file);
- }
-
- if (lock_get_wait(lock)) {
- fputs(" waiting", file);
- }
-
- mtr_start(&mtr);
-
- putc('\n', file);
-
- /* If the page is not in the buffer pool, we cannot load it
- because we have the kernel mutex and ibuf operations would
- break the latching order */
-
- page = buf_page_get_gen(space, page_no, RW_NO_LATCH,
- NULL, BUF_GET_IF_IN_POOL,
- __FILE__, __LINE__, &mtr);
- if (page) {
- page = buf_page_get_nowait(space, page_no, RW_S_LATCH, &mtr);
-
- if (!page) {
- /* Let us try to get an X-latch. If the current thread
- is holding an X-latch on the page, we cannot get an
- S-latch. */
-
- page = buf_page_get_nowait(space, page_no, RW_X_LATCH,
- &mtr);
- }
- }
-
- if (page) {
-#ifdef UNIV_SYNC_DEBUG
- buf_page_dbg_add_level(page, SYNC_NO_ORDER_CHECK);
-#endif /* UNIV_SYNC_DEBUG */
- }
-
- for (i = 0; i < lock_rec_get_n_bits(lock); i++) {
-
- if (lock_rec_get_nth_bit(lock, i)) {
-
- fprintf(file, "Record lock, heap no %lu ", (ulong) i);
-
- if (page) {
- rec_t* rec
- = page_find_rec_with_heap_no(page, i);
- offsets = rec_get_offsets(
- rec, lock->index, offsets,
- ULINT_UNDEFINED, &heap);
- rec_print_new(file, rec, offsets);
- }
-
- putc('\n', file);
- }
- }
-
- mtr_commit(&mtr);
- if (UNIV_LIKELY_NULL(heap)) {
- mem_heap_free(heap);
- }
-}
-
-#ifndef UNIV_HOTBACKUP
-
-#ifdef UNIV_DEBUG
-/* Print the number of lock structs from lock_print_info_summary() only
-in non-production builds for performance reasons, see
-http://bugs.mysql.com/36942 */
-#define PRINT_NUM_OF_LOCK_STRUCTS
-#endif /* UNIV_DEBUG */
-
-#ifdef PRINT_NUM_OF_LOCK_STRUCTS
-/*************************************************************************
-Calculates the number of record lock structs in the record lock hash table. */
-static
-ulint
-lock_get_n_rec_locks(void)
-/*======================*/
-{
- lock_t* lock;
- ulint n_locks = 0;
- ulint i;
-
- ut_ad(mutex_own(&kernel_mutex));
-
- for (i = 0; i < hash_get_n_cells(lock_sys->rec_hash); i++) {
-
- lock = HASH_GET_FIRST(lock_sys->rec_hash, i);
-
- while (lock) {
- n_locks++;
-
- lock = HASH_GET_NEXT(hash, lock);
- }
- }
-
- return(n_locks);
-}
-#endif /* PRINT_NUM_OF_LOCK_STRUCTS */
-
-/*************************************************************************
-Prints info of locks for all transactions. */
-
-void
-lock_print_info_summary(
-/*====================*/
- FILE* file) /* in: file where to print */
-{
- /* We must protect the MySQL thd->query field with a MySQL mutex, and
- because the MySQL mutex must be reserved before the kernel_mutex of
- InnoDB, we call innobase_mysql_prepare_print_arbitrary_thd() here. */
-
- innobase_mysql_prepare_print_arbitrary_thd();
- lock_mutex_enter_kernel();
-
- if (lock_deadlock_found) {
- fputs("------------------------\n"
- "LATEST DETECTED DEADLOCK\n"
- "------------------------\n", file);
-
- ut_copy_file(file, lock_latest_err_file);
- }
-
- fputs("------------\n"
- "TRANSACTIONS\n"
- "------------\n", file);
-
- fprintf(file, "Trx id counter %lu %lu\n",
- (ulong) ut_dulint_get_high(trx_sys->max_trx_id),
- (ulong) ut_dulint_get_low(trx_sys->max_trx_id));
-
- fprintf(file,
- "Purge done for trx's n:o < %lu %lu undo n:o < %lu %lu\n",
- (ulong) ut_dulint_get_high(purge_sys->purge_trx_no),
- (ulong) ut_dulint_get_low(purge_sys->purge_trx_no),
- (ulong) ut_dulint_get_high(purge_sys->purge_undo_no),
- (ulong) ut_dulint_get_low(purge_sys->purge_undo_no));
-
- fprintf(file,
- "History list length %lu\n",
- (ulong) trx_sys->rseg_history_len);
-
-#ifdef PRINT_NUM_OF_LOCK_STRUCTS
- fprintf(file,
- "Total number of lock structs in row lock hash table %lu\n",
- (ulong) lock_get_n_rec_locks());
-#endif /* PRINT_NUM_OF_LOCK_STRUCTS */
-}
-
-/*************************************************************************
-Prints info of locks for each transaction. */
-
-void
-lock_print_info_all_transactions(
-/*=============================*/
- FILE* file) /* in: file where to print */
-{
- lock_t* lock;
- ulint space;
- ulint page_no;
- page_t* page;
- ibool load_page_first = TRUE;
- ulint nth_trx = 0;
- ulint nth_lock = 0;
- ulint i;
- mtr_t mtr;
- trx_t* trx;
-
- fprintf(file, "LIST OF TRANSACTIONS FOR EACH SESSION:\n");
-
- /* First print info on non-active transactions */
-
- trx = UT_LIST_GET_FIRST(trx_sys->mysql_trx_list);
-
- while (trx) {
- if (trx->conc_state == TRX_NOT_STARTED) {
- fputs("---", file);
- trx_print(file, trx, 600);
- }
-
- trx = UT_LIST_GET_NEXT(mysql_trx_list, trx);
- }
-
-loop:
- trx = UT_LIST_GET_FIRST(trx_sys->trx_list);
-
- i = 0;
-
- /* Since we temporarily release the kernel mutex when
- reading a database page in below, variable trx may be
- obsolete now and we must loop through the trx list to
- get probably the same trx, or some other trx. */
-
- while (trx && (i < nth_trx)) {
- trx = UT_LIST_GET_NEXT(trx_list, trx);
- i++;
- }
-
- if (trx == NULL) {
- lock_mutex_exit_kernel();
- innobase_mysql_end_print_arbitrary_thd();
-
- ut_ad(lock_validate());
-
- return;
- }
-
- if (nth_lock == 0) {
- fputs("---", file);
- trx_print(file, trx, 600);
-
- if (trx->read_view) {
- fprintf(file,
- "Trx read view will not see trx with"
- " id >= %lu %lu, sees < %lu %lu\n",
- (ulong) ut_dulint_get_high(
- trx->read_view->low_limit_id),
- (ulong) ut_dulint_get_low(
- trx->read_view->low_limit_id),
- (ulong) ut_dulint_get_high(
- trx->read_view->up_limit_id),
- (ulong) ut_dulint_get_low(
- trx->read_view->up_limit_id));
- }
-
- if (trx->que_state == TRX_QUE_LOCK_WAIT) {
- fprintf(file,
- "------- TRX HAS BEEN WAITING %lu SEC"
- " FOR THIS LOCK TO BE GRANTED:\n",
- (ulong) difftime(time(NULL),
- trx->wait_started));
-
- if (lock_get_type(trx->wait_lock) == LOCK_REC) {
- lock_rec_print(file, trx->wait_lock);
- } else {
- lock_table_print(file, trx->wait_lock);
- }
-
- fputs("------------------\n", file);
- }
- }
-
- if (!srv_print_innodb_lock_monitor) {
- nth_trx++;
- goto loop;
- }
-
- i = 0;
-
- /* Look at the note about the trx loop above why we loop here:
- lock may be an obsolete pointer now. */
-
- lock = UT_LIST_GET_FIRST(trx->trx_locks);
-
- while (lock && (i < nth_lock)) {
- lock = UT_LIST_GET_NEXT(trx_locks, lock);
- i++;
- }
-
- if (lock == NULL) {
- nth_trx++;
- nth_lock = 0;
-
- goto loop;
- }
-
- if (lock_get_type(lock) == LOCK_REC) {
- space = lock->un_member.rec_lock.space;
- page_no = lock->un_member.rec_lock.page_no;
-
- if (load_page_first) {
- lock_mutex_exit_kernel();
- innobase_mysql_end_print_arbitrary_thd();
-
- mtr_start(&mtr);
-
- page = buf_page_get_with_no_latch(
- space, page_no, &mtr);
-
- mtr_commit(&mtr);
-
- load_page_first = FALSE;
-
- innobase_mysql_prepare_print_arbitrary_thd();
- lock_mutex_enter_kernel();
-
- goto loop;
- }
-
- lock_rec_print(file, lock);
- } else {
- ut_ad(lock_get_type(lock) & LOCK_TABLE);
-
- lock_table_print(file, lock);
- }
-
- load_page_first = TRUE;
-
- nth_lock++;
-
- if (nth_lock >= 10) {
- fputs("10 LOCKS PRINTED FOR THIS TRX:"
- " SUPPRESSING FURTHER PRINTS\n",
- file);
-
- nth_trx++;
- nth_lock = 0;
-
- goto loop;
- }
-
- goto loop;
-}
-
-/*************************************************************************
-Validates the lock queue on a table. */
-
-ibool
-lock_table_queue_validate(
-/*======================*/
- /* out: TRUE if ok */
- dict_table_t* table) /* in: table */
-{
- lock_t* lock;
-
- ut_ad(mutex_own(&kernel_mutex));
-
- lock = UT_LIST_GET_FIRST(table->locks);
-
- while (lock) {
- ut_a(((lock->trx)->conc_state == TRX_ACTIVE)
- || ((lock->trx)->conc_state == TRX_PREPARED)
- || ((lock->trx)->conc_state == TRX_COMMITTED_IN_MEMORY));
-
- if (!lock_get_wait(lock)) {
-
- ut_a(!lock_table_other_has_incompatible(
- lock->trx, 0, table,
- lock_get_mode(lock)));
- } else {
-
- ut_a(lock_table_has_to_wait_in_queue(lock));
- }
-
- lock = UT_LIST_GET_NEXT(un_member.tab_lock.locks, lock);
- }
-
- return(TRUE);
-}
-
-/*************************************************************************
-Validates the lock queue on a single record. */
-
-ibool
-lock_rec_queue_validate(
-/*====================*/
- /* out: TRUE if ok */
- rec_t* rec, /* in: record to look at */
- dict_index_t* index, /* in: index, or NULL if not known */
- const ulint* offsets)/* in: rec_get_offsets(rec, index) */
-{
- trx_t* impl_trx;
- lock_t* lock;
-
- ut_a(rec);
- ut_ad(rec_offs_validate(rec, index, offsets));
- ut_ad(!page_rec_is_comp(rec) == !rec_offs_comp(offsets));
-
- lock_mutex_enter_kernel();
-
- if (!page_rec_is_user_rec(rec)) {
-
- lock = lock_rec_get_first(rec);
-
- while (lock) {
- switch(lock->trx->conc_state) {
- case TRX_ACTIVE:
- case TRX_PREPARED:
- case TRX_COMMITTED_IN_MEMORY:
- break;
- default:
- ut_error;
- }
-
- ut_a(trx_in_trx_list(lock->trx));
-
- if (lock_get_wait(lock)) {
- ut_a(lock_rec_has_to_wait_in_queue(lock));
- }
-
- if (index) {
- ut_a(lock->index == index);
- }
-
- lock = lock_rec_get_next(rec, lock);
- }
-
- lock_mutex_exit_kernel();
-
- return(TRUE);
- }
-
- if (index && (index->type & DICT_CLUSTERED)) {
-
- impl_trx = lock_clust_rec_some_has_impl(rec, index, offsets);
-
- if (impl_trx && lock_rec_other_has_expl_req(
- LOCK_S, 0, LOCK_WAIT, rec, impl_trx)) {
-
- ut_a(lock_rec_has_expl(LOCK_X | LOCK_REC_NOT_GAP,
- rec, impl_trx));
- }
- }
-
- if (index && !(index->type & DICT_CLUSTERED)) {
-
- /* The kernel mutex may get released temporarily in the
- next function call: we have to release lock table mutex
- to obey the latching order */
-
- impl_trx = lock_sec_rec_some_has_impl_off_kernel(
- rec, index, offsets);
-
- if (impl_trx && lock_rec_other_has_expl_req(
- LOCK_S, 0, LOCK_WAIT, rec, impl_trx)) {
-
- ut_a(lock_rec_has_expl(LOCK_X | LOCK_REC_NOT_GAP,
- rec, impl_trx));
- }
- }
-
- lock = lock_rec_get_first(rec);
-
- while (lock) {
- ut_a(lock->trx->conc_state == TRX_ACTIVE
- || lock->trx->conc_state == TRX_PREPARED
- || lock->trx->conc_state == TRX_COMMITTED_IN_MEMORY);
- ut_a(trx_in_trx_list(lock->trx));
-
- if (index) {
- ut_a(lock->index == index);
- }
-
- if (!lock_rec_get_gap(lock) && !lock_get_wait(lock)) {
-
- ulint mode;
-
- if (lock_get_mode(lock) == LOCK_S) {
- mode = LOCK_X;
- } else {
- mode = LOCK_S;
- }
- ut_a(!lock_rec_other_has_expl_req(
- mode, 0, 0, rec, lock->trx));
-
- } else if (lock_get_wait(lock) && !lock_rec_get_gap(lock)) {
-
- ut_a(lock_rec_has_to_wait_in_queue(lock));
- }
-
- lock = lock_rec_get_next(rec, lock);
- }
-
- lock_mutex_exit_kernel();
-
- return(TRUE);
-}
-
-/*************************************************************************
-Validates the record lock queues on a page. */
-
-ibool
-lock_rec_validate_page(
-/*===================*/
- /* out: TRUE if ok */
- ulint space, /* in: space id */
- ulint page_no)/* in: page number */
-{
- dict_index_t* index;
- page_t* page;
- lock_t* lock;
- rec_t* rec;
- ulint nth_lock = 0;
- ulint nth_bit = 0;
- ulint i;
- mtr_t mtr;
- mem_heap_t* heap = NULL;
- ulint offsets_[REC_OFFS_NORMAL_SIZE];
- ulint* offsets = offsets_;
- *offsets_ = (sizeof offsets_) / sizeof *offsets_;
-
- ut_ad(!mutex_own(&kernel_mutex));
-
- mtr_start(&mtr);
-
- page = buf_page_get(space, page_no, RW_X_LATCH, &mtr);
-#ifdef UNIV_SYNC_DEBUG
- buf_page_dbg_add_level(page, SYNC_NO_ORDER_CHECK);
-#endif /* UNIV_SYNC_DEBUG */
-
- lock_mutex_enter_kernel();
-loop:
- lock = lock_rec_get_first_on_page_addr(space, page_no);
-
- if (!lock) {
- goto function_exit;
- }
-
- for (i = 0; i < nth_lock; i++) {
-
- lock = lock_rec_get_next_on_page(lock);
-
- if (!lock) {
- goto function_exit;
- }
- }
-
- ut_a(trx_in_trx_list(lock->trx));
- ut_a(lock->trx->conc_state == TRX_ACTIVE
- || lock->trx->conc_state == TRX_PREPARED
- || lock->trx->conc_state == TRX_COMMITTED_IN_MEMORY);
-
- for (i = nth_bit; i < lock_rec_get_n_bits(lock); i++) {
-
- if (i == 1 || lock_rec_get_nth_bit(lock, i)) {
-
- index = lock->index;
- rec = page_find_rec_with_heap_no(page, i);
- offsets = rec_get_offsets(rec, index, offsets,
- ULINT_UNDEFINED, &heap);
-
- fprintf(stderr,
- "Validating %lu %lu\n",
- (ulong) space, (ulong) page_no);
-
- lock_mutex_exit_kernel();
-
- lock_rec_queue_validate(rec, index, offsets);
-
- lock_mutex_enter_kernel();
-
- nth_bit = i + 1;
-
- goto loop;
- }
- }
-
- nth_bit = 0;
- nth_lock++;
-
- goto loop;
-
-function_exit:
- lock_mutex_exit_kernel();
-
- mtr_commit(&mtr);
-
- if (UNIV_LIKELY_NULL(heap)) {
- mem_heap_free(heap);
- }
- return(TRUE);
-}
-
-/*************************************************************************
-Validates the lock system. */
-
-ibool
-lock_validate(void)
-/*===============*/
- /* out: TRUE if ok */
-{
- lock_t* lock;
- trx_t* trx;
- dulint limit;
- ulint space;
- ulint page_no;
- ulint i;
-
- lock_mutex_enter_kernel();
-
- trx = UT_LIST_GET_FIRST(trx_sys->trx_list);
-
- while (trx) {
- lock = UT_LIST_GET_FIRST(trx->trx_locks);
-
- while (lock) {
- if (lock_get_type(lock) & LOCK_TABLE) {
-
- lock_table_queue_validate(
- lock->un_member.tab_lock.table);
- }
-
- lock = UT_LIST_GET_NEXT(trx_locks, lock);
- }
-
- trx = UT_LIST_GET_NEXT(trx_list, trx);
- }
-
- for (i = 0; i < hash_get_n_cells(lock_sys->rec_hash); i++) {
-
- limit = ut_dulint_zero;
-
- for (;;) {
- lock = HASH_GET_FIRST(lock_sys->rec_hash, i);
-
- while (lock) {
- ut_a(trx_in_trx_list(lock->trx));
-
- space = lock->un_member.rec_lock.space;
- page_no = lock->un_member.rec_lock.page_no;
-
- if (ut_dulint_cmp(
- ut_dulint_create(space, page_no),
- limit) >= 0) {
- break;
- }
-
- lock = HASH_GET_NEXT(hash, lock);
- }
-
- if (!lock) {
-
- break;
- }
-
- lock_mutex_exit_kernel();
-
- lock_rec_validate_page(space, page_no);
-
- lock_mutex_enter_kernel();
-
- limit = ut_dulint_create(space, page_no + 1);
- }
- }
-
- lock_mutex_exit_kernel();
-
- return(TRUE);
-}
-#endif /* !UNIV_HOTBACKUP */
-/*============ RECORD LOCK CHECKS FOR ROW OPERATIONS ====================*/
-
-/*************************************************************************
-Checks if locks of other transactions prevent an immediate insert of
-a record. If they do, first tests if the query thread should anyway
-be suspended for some reason; if not, then puts the transaction and
-the query thread to the lock wait state and inserts a waiting request
-for a gap x-lock to the lock queue. */
-
-ulint
-lock_rec_insert_check_and_lock(
-/*===========================*/
- /* out: DB_SUCCESS, DB_LOCK_WAIT,
- DB_DEADLOCK, or DB_QUE_THR_SUSPENDED */
- ulint flags, /* in: if BTR_NO_LOCKING_FLAG bit is set,
- does nothing */
- rec_t* rec, /* in: record after which to insert */
- dict_index_t* index, /* in: index */
- que_thr_t* thr, /* in: query thread */
- ibool* inherit)/* out: set to TRUE if the new inserted
- record maybe should inherit LOCK_GAP type
- locks from the successor record */
-{
- rec_t* next_rec;
- trx_t* trx;
- lock_t* lock;
- ulint err;
-
- if (flags & BTR_NO_LOCKING_FLAG) {
-
- return(DB_SUCCESS);
- }
-
- ut_ad(rec);
-
- trx = thr_get_trx(thr);
- next_rec = page_rec_get_next(rec);
-
- *inherit = FALSE;
-
- lock_mutex_enter_kernel();
-
- ut_ad(lock_table_has(thr_get_trx(thr), index->table, LOCK_IX));
-
- lock = lock_rec_get_first(next_rec);
-
- if (lock == NULL) {
- /* We optimize CPU time usage in the simplest case */
-
- lock_mutex_exit_kernel();
-
- if (!(index->type & DICT_CLUSTERED)) {
-
- /* Update the page max trx id field */
- page_update_max_trx_id(buf_frame_align(rec),
- thr_get_trx(thr)->id);
- }
-
- return(DB_SUCCESS);
- }
-
- *inherit = TRUE;
-
- /* If another transaction has an explicit lock request which locks
- the gap, waiting or granted, on the successor, the insert has to wait.
-
- An exception is the case where the lock by the another transaction
- is a gap type lock which it placed to wait for its turn to insert. We
- do not consider that kind of a lock conflicting with our insert. This
- eliminates an unnecessary deadlock which resulted when 2 transactions
- had to wait for their insert. Both had waiting gap type lock requests
- on the successor, which produced an unnecessary deadlock. */
-
- if (lock_rec_other_has_conflicting(
- LOCK_X | LOCK_GAP | LOCK_INSERT_INTENTION, next_rec,
- trx)) {
-
- /* Note that we may get DB_SUCCESS also here! */
- err = lock_rec_enqueue_waiting(LOCK_X | LOCK_GAP
- | LOCK_INSERT_INTENTION,
- next_rec, index, thr);
- } else {
- err = DB_SUCCESS;
- }
-
- lock_mutex_exit_kernel();
-
- if (!(index->type & DICT_CLUSTERED) && (err == DB_SUCCESS)) {
-
- /* Update the page max trx id field */
- page_update_max_trx_id(buf_frame_align(rec),
- thr_get_trx(thr)->id);
- }
-
-#ifdef UNIV_DEBUG
- {
- mem_heap_t* heap = NULL;
- ulint offsets_[REC_OFFS_NORMAL_SIZE];
- const ulint* offsets;
- *offsets_ = (sizeof offsets_) / sizeof *offsets_;
-
- offsets = rec_get_offsets(next_rec, index, offsets_,
- ULINT_UNDEFINED, &heap);
- ut_ad(lock_rec_queue_validate(next_rec, index, offsets));
- if (UNIV_LIKELY_NULL(heap)) {
- mem_heap_free(heap);
- }
- }
-#endif /* UNIV_DEBUG */
-
- return(err);
-}
-
-/*************************************************************************
-If a transaction has an implicit x-lock on a record, but no explicit x-lock
-set on the record, sets one for it. NOTE that in the case of a secondary
-index, the kernel mutex may get temporarily released. */
-static
-void
-lock_rec_convert_impl_to_expl(
-/*==========================*/
- rec_t* rec, /* in: user record on page */
- dict_index_t* index, /* in: index of record */
- const ulint* offsets)/* in: rec_get_offsets(rec, index) */
-{
- trx_t* impl_trx;
-
- ut_ad(mutex_own(&kernel_mutex));
- ut_ad(page_rec_is_user_rec(rec));
- ut_ad(rec_offs_validate(rec, index, offsets));
- ut_ad(!page_rec_is_comp(rec) == !rec_offs_comp(offsets));
-
- if (index->type & DICT_CLUSTERED) {
- impl_trx = lock_clust_rec_some_has_impl(rec, index, offsets);
- } else {
- impl_trx = lock_sec_rec_some_has_impl_off_kernel(
- rec, index, offsets);
- }
-
- if (impl_trx) {
- /* If the transaction has no explicit x-lock set on the
- record, set one for it */
-
- if (!lock_rec_has_expl(LOCK_X | LOCK_REC_NOT_GAP, rec,
- impl_trx)) {
-
- lock_rec_add_to_queue(
- LOCK_REC | LOCK_X | LOCK_REC_NOT_GAP,
- rec, index, impl_trx);
- }
- }
-}
-
-/*************************************************************************
-Checks if locks of other transactions prevent an immediate modify (update,
-delete mark, or delete unmark) of a clustered index record. If they do,
-first tests if the query thread should anyway be suspended for some
-reason; if not, then puts the transaction and the query thread to the
-lock wait state and inserts a waiting request for a record x-lock to the
-lock queue. */
-
-ulint
-lock_clust_rec_modify_check_and_lock(
-/*=================================*/
- /* out: DB_SUCCESS, DB_LOCK_WAIT,
- DB_DEADLOCK, or DB_QUE_THR_SUSPENDED */
- ulint flags, /* in: if BTR_NO_LOCKING_FLAG bit is set,
- does nothing */
- rec_t* rec, /* in: record which should be modified */
- dict_index_t* index, /* in: clustered index */
- const ulint* offsets,/* in: rec_get_offsets(rec, index) */
- que_thr_t* thr) /* in: query thread */
-{
- ulint err;
-
- ut_ad(rec_offs_validate(rec, index, offsets));
- ut_ad(index->type & DICT_CLUSTERED);
-
- if (flags & BTR_NO_LOCKING_FLAG) {
-
- return(DB_SUCCESS);
- }
-
- lock_mutex_enter_kernel();
-
- ut_ad(lock_table_has(thr_get_trx(thr), index->table, LOCK_IX));
-
- /* If a transaction has no explicit x-lock set on the record, set one
- for it */
-
- lock_rec_convert_impl_to_expl(rec, index, offsets);
-
- err = lock_rec_lock(TRUE, LOCK_X | LOCK_REC_NOT_GAP, rec, index, thr);
-
- lock_mutex_exit_kernel();
-
- ut_ad(lock_rec_queue_validate(rec, index, offsets));
-
- return(err);
-}
-
-/*************************************************************************
-Checks if locks of other transactions prevent an immediate modify (delete
-mark or delete unmark) of a secondary index record. */
-
-ulint
-lock_sec_rec_modify_check_and_lock(
-/*===============================*/
- /* out: DB_SUCCESS, DB_LOCK_WAIT,
- DB_DEADLOCK, or DB_QUE_THR_SUSPENDED */
- ulint flags, /* in: if BTR_NO_LOCKING_FLAG bit is set,
- does nothing */
- rec_t* rec, /* in: record which should be modified;
- NOTE: as this is a secondary index, we
- always have to modify the clustered index
- record first: see the comment below */
- dict_index_t* index, /* in: secondary index */
- que_thr_t* thr) /* in: query thread */
-{
- ulint err;
-
- if (flags & BTR_NO_LOCKING_FLAG) {
-
- return(DB_SUCCESS);
- }
-
- ut_ad(!(index->type & DICT_CLUSTERED));
-
- /* Another transaction cannot have an implicit lock on the record,
- because when we come here, we already have modified the clustered
- index record, and this would not have been possible if another active
- transaction had modified this secondary index record. */
-
- lock_mutex_enter_kernel();
-
- ut_ad(lock_table_has(thr_get_trx(thr), index->table, LOCK_IX));
-
- err = lock_rec_lock(TRUE, LOCK_X | LOCK_REC_NOT_GAP, rec, index, thr);
-
- lock_mutex_exit_kernel();
-
-#ifdef UNIV_DEBUG
- {
- mem_heap_t* heap = NULL;
- ulint offsets_[REC_OFFS_NORMAL_SIZE];
- const ulint* offsets;
- *offsets_ = (sizeof offsets_) / sizeof *offsets_;
-
- offsets = rec_get_offsets(rec, index, offsets_,
- ULINT_UNDEFINED, &heap);
- ut_ad(lock_rec_queue_validate(rec, index, offsets));
- if (UNIV_LIKELY_NULL(heap)) {
- mem_heap_free(heap);
- }
- }
-#endif /* UNIV_DEBUG */
-
- if (err == DB_SUCCESS) {
- /* Update the page max trx id field */
-
- page_update_max_trx_id(buf_frame_align(rec),
- thr_get_trx(thr)->id);
- }
-
- return(err);
-}
-
-/*************************************************************************
-Like the counterpart for a clustered index below, but now we read a
-secondary index record. */
-
-ulint
-lock_sec_rec_read_check_and_lock(
-/*=============================*/
- /* out: DB_SUCCESS, DB_LOCK_WAIT,
- DB_DEADLOCK, or DB_QUE_THR_SUSPENDED */
- ulint flags, /* in: if BTR_NO_LOCKING_FLAG bit is set,
- does nothing */
- rec_t* rec, /* in: user record or page supremum record
- which should be read or passed over by a read
- cursor */
- dict_index_t* index, /* in: secondary index */
- const ulint* offsets,/* in: rec_get_offsets(rec, index) */
- ulint mode, /* in: mode of the lock which the read cursor
- should set on records: LOCK_S or LOCK_X; the
- latter is possible in SELECT FOR UPDATE */
- ulint gap_mode,/* in: LOCK_ORDINARY, LOCK_GAP, or
- LOCK_REC_NOT_GAP */
- que_thr_t* thr) /* in: query thread */
-{
- ulint err;
-
- ut_ad(!(index->type & DICT_CLUSTERED));
- ut_ad(page_rec_is_user_rec(rec) || page_rec_is_supremum(rec));
- ut_ad(rec_offs_validate(rec, index, offsets));
-
- if (flags & BTR_NO_LOCKING_FLAG) {
-
- return(DB_SUCCESS);
- }
-
- lock_mutex_enter_kernel();
-
- ut_ad(mode != LOCK_X
- || lock_table_has(thr_get_trx(thr), index->table, LOCK_IX));
- ut_ad(mode != LOCK_S
- || lock_table_has(thr_get_trx(thr), index->table, LOCK_IS));
-
- /* 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 a
- database recovery is running. */
-
- if (((ut_dulint_cmp(page_get_max_trx_id(buf_frame_align(rec)),
- trx_list_get_min_trx_id()) >= 0)
- || recv_recovery_is_on())
- && !page_rec_is_supremum(rec)) {
-
- lock_rec_convert_impl_to_expl(rec, index, offsets);
- }
-
- err = lock_rec_lock(FALSE, mode | gap_mode, rec, index, thr);
-
- lock_mutex_exit_kernel();
-
- ut_ad(lock_rec_queue_validate(rec, index, offsets));
-
- return(err);
-}
-
-/*************************************************************************
-Checks if locks of other transactions prevent an immediate read, or passing
-over by a read cursor, of a clustered index record. If they do, first tests
-if the query thread should anyway be suspended for some reason; if not, then
-puts the transaction and the query thread to the lock wait state and inserts a
-waiting request for a record lock to the lock queue. Sets the requested mode
-lock on the record. */
-
-ulint
-lock_clust_rec_read_check_and_lock(
-/*===============================*/
- /* out: DB_SUCCESS, DB_LOCK_WAIT,
- DB_DEADLOCK, or DB_QUE_THR_SUSPENDED */
- ulint flags, /* in: if BTR_NO_LOCKING_FLAG bit is set,
- does nothing */
- rec_t* rec, /* in: user record or page supremum record
- which should be read or passed over by a read
- cursor */
- dict_index_t* index, /* in: clustered index */
- const ulint* offsets,/* in: rec_get_offsets(rec, index) */
- ulint mode, /* in: mode of the lock which the read cursor
- should set on records: LOCK_S or LOCK_X; the
- latter is possible in SELECT FOR UPDATE */
- ulint gap_mode,/* in: LOCK_ORDINARY, LOCK_GAP, or
- LOCK_REC_NOT_GAP */
- que_thr_t* thr) /* in: query thread */
-{
- ulint err;
-
- ut_ad(index->type & DICT_CLUSTERED);
- ut_ad(page_rec_is_user_rec(rec) || page_rec_is_supremum(rec));
- ut_ad(gap_mode == LOCK_ORDINARY || gap_mode == LOCK_GAP
- || gap_mode == LOCK_REC_NOT_GAP);
- ut_ad(rec_offs_validate(rec, index, offsets));
-
- if (flags & BTR_NO_LOCKING_FLAG) {
-
- return(DB_SUCCESS);
- }
-
- lock_mutex_enter_kernel();
-
- ut_ad(mode != LOCK_X
- || lock_table_has(thr_get_trx(thr), index->table, LOCK_IX));
- ut_ad(mode != LOCK_S
- || lock_table_has(thr_get_trx(thr), index->table, LOCK_IS));
-
- if (!page_rec_is_supremum(rec)) {
-
- lock_rec_convert_impl_to_expl(rec, index, offsets);
- }
-
- err = lock_rec_lock(FALSE, mode | gap_mode, rec, index, thr);
-
- lock_mutex_exit_kernel();
-
- ut_ad(lock_rec_queue_validate(rec, index, offsets));
-
- return(err);
-}
-/*************************************************************************
-Checks if locks of other transactions prevent an immediate read, or passing
-over by a read cursor, of a clustered index record. If they do, first tests
-if the query thread should anyway be suspended for some reason; if not, then
-puts the transaction and the query thread to the lock wait state and inserts a
-waiting request for a record lock to the lock queue. Sets the requested mode
-lock on the record. This is an alternative version of
-lock_clust_rec_read_check_and_lock() that does not require the parameter
-"offsets". */
-
-ulint
-lock_clust_rec_read_check_and_lock_alt(
-/*===================================*/
- /* out: DB_SUCCESS, DB_LOCK_WAIT,
- DB_DEADLOCK, or DB_QUE_THR_SUSPENDED */
- ulint flags, /* in: if BTR_NO_LOCKING_FLAG bit is set,
- does nothing */
- rec_t* rec, /* in: user record or page supremum record
- which should be read or passed over by a read
- cursor */
- dict_index_t* index, /* in: clustered index */
- ulint mode, /* in: mode of the lock which the read cursor
- should set on records: LOCK_S or LOCK_X; the
- latter is possible in SELECT FOR UPDATE */
- ulint gap_mode,/* in: LOCK_ORDINARY, LOCK_GAP, or
- LOCK_REC_NOT_GAP */
- que_thr_t* thr) /* in: query thread */
-{
- mem_heap_t* tmp_heap = NULL;
- ulint offsets_[REC_OFFS_NORMAL_SIZE];
- ulint* offsets = offsets_;
- ulint ret;
- *offsets_ = (sizeof offsets_) / sizeof *offsets_;
-
- offsets = rec_get_offsets(rec, index, offsets,
- ULINT_UNDEFINED, &tmp_heap);
- ret = lock_clust_rec_read_check_and_lock(flags, rec, index,
- offsets, mode, gap_mode, thr);
- if (tmp_heap) {
- mem_heap_free(tmp_heap);
- }
- return(ret);
-}
-