2014-02-26 19:11:54 +01:00
|
|
|
/*****************************************************************************
|
|
|
|
|
2016-06-21 14:21:03 +02:00
|
|
|
Copyright (c) 1996, 2016, Oracle and/or its affiliates. All Rights Reserved.
|
2022-01-11 18:19:39 +03:00
|
|
|
Copyright (c) 2017, 2022, MariaDB Corporation.
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or modify it under
|
|
|
|
the terms of the GNU General Public License as published by the Free Software
|
|
|
|
Foundation; version 2 of the License.
|
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful, but WITHOUT
|
|
|
|
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
|
|
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License along with
|
|
|
|
this program; if not, write to the Free Software Foundation, Inc.,
|
2019-05-11 19:25:02 +03:00
|
|
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
*****************************************************************************/
|
|
|
|
|
|
|
|
/**************************************************//**
|
|
|
|
@file include/lock0lock.h
|
|
|
|
The transaction lock system
|
|
|
|
|
|
|
|
Created 5/7/1996 Heikki Tuuri
|
|
|
|
*******************************************************/
|
|
|
|
|
|
|
|
#ifndef lock0lock_h
|
|
|
|
#define lock0lock_h
|
|
|
|
|
|
|
|
#include "buf0types.h"
|
|
|
|
#include "trx0types.h"
|
|
|
|
#include "mtr0types.h"
|
|
|
|
#include "rem0types.h"
|
|
|
|
#include "que0types.h"
|
|
|
|
#include "lock0types.h"
|
|
|
|
#include "hash0hash.h"
|
|
|
|
#include "srv0srv.h"
|
|
|
|
#include "ut0vec.h"
|
2016-08-12 11:17:45 +03:00
|
|
|
#include "gis0rtree.h"
|
|
|
|
#include "lock0prdt.h"
|
2014-02-26 19:11:54 +01:00
|
|
|
|
2016-10-17 21:56:05 -04:00
|
|
|
/** Alternatives for innodb_lock_schedule_algorithm, which can be changed by
|
2016-10-23 13:36:26 -04:00
|
|
|
setting innodb_lock_schedule_algorithm. */
|
2016-10-17 21:56:05 -04:00
|
|
|
enum innodb_lock_schedule_algorithm_t {
|
2016-10-23 13:36:26 -04:00
|
|
|
/*!< First Come First Served */
|
|
|
|
INNODB_LOCK_SCHEDULE_ALGORITHM_FCFS,
|
|
|
|
/*!< Variance-Aware-Transaction-Scheduling */
|
|
|
|
INNODB_LOCK_SCHEDULE_ALGORITHM_VATS
|
2016-10-17 21:56:05 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
extern ulong innodb_lock_schedule_algorithm;
|
|
|
|
|
2016-08-12 11:17:45 +03:00
|
|
|
// Forward declaration
|
|
|
|
class ReadView;
|
2014-02-26 19:11:54 +01:00
|
|
|
|
2016-07-28 13:08:52 +08:00
|
|
|
/** The value of innodb_deadlock_detect */
|
|
|
|
extern my_bool innobase_deadlock_detect;
|
|
|
|
|
2014-02-26 19:11:54 +01:00
|
|
|
/*********************************************************************//**
|
|
|
|
Gets the size of a lock struct.
|
2016-08-12 11:17:45 +03:00
|
|
|
@return size in bytes */
|
2014-02-26 19:11:54 +01:00
|
|
|
ulint
|
|
|
|
lock_get_size(void);
|
|
|
|
/*===============*/
|
|
|
|
/*********************************************************************//**
|
|
|
|
Gets the heap_no of the smallest user record on a page.
|
2016-08-12 11:17:45 +03:00
|
|
|
@return heap_no of smallest user record, or PAGE_HEAP_NO_SUPREMUM */
|
2014-02-26 19:11:54 +01:00
|
|
|
UNIV_INLINE
|
|
|
|
ulint
|
|
|
|
lock_get_min_heap_no(
|
|
|
|
/*=================*/
|
|
|
|
const buf_block_t* block); /*!< in: buffer block */
|
|
|
|
/*************************************************************//**
|
|
|
|
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(
|
|
|
|
/*======================*/
|
|
|
|
const buf_block_t* block, /*!< in: old index page, now
|
|
|
|
reorganized */
|
|
|
|
const buf_block_t* oblock);/*!< in: copy of the old, not
|
|
|
|
reorganized page */
|
|
|
|
/*************************************************************//**
|
|
|
|
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(
|
|
|
|
/*===================*/
|
|
|
|
const buf_block_t* new_block, /*!< in: index page to move to */
|
|
|
|
const buf_block_t* block, /*!< in: index page */
|
|
|
|
const rec_t* rec); /*!< in: record on page: this
|
|
|
|
is the first record moved */
|
|
|
|
/*************************************************************//**
|
|
|
|
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(
|
|
|
|
/*=====================*/
|
|
|
|
const buf_block_t* new_block, /*!< in: index page to move to */
|
|
|
|
const buf_block_t* block, /*!< in: index page */
|
|
|
|
const rec_t* rec, /*!< in: record on page:
|
|
|
|
this is the first
|
|
|
|
record NOT copied */
|
|
|
|
const rec_t* old_end); /*!< in: old
|
|
|
|
previous-to-last
|
|
|
|
record on new_page
|
|
|
|
before the records
|
|
|
|
were copied */
|
|
|
|
/*************************************************************//**
|
|
|
|
Updates the lock table when a page is split to the right. */
|
|
|
|
void
|
|
|
|
lock_update_split_right(
|
|
|
|
/*====================*/
|
|
|
|
const buf_block_t* right_block, /*!< in: right page */
|
|
|
|
const buf_block_t* left_block); /*!< in: left page */
|
|
|
|
/*************************************************************//**
|
|
|
|
Updates the lock table when a page is merged to the right. */
|
|
|
|
void
|
|
|
|
lock_update_merge_right(
|
|
|
|
/*====================*/
|
|
|
|
const buf_block_t* right_block, /*!< in: right page to
|
|
|
|
which merged */
|
|
|
|
const rec_t* orig_succ, /*!< in: original
|
|
|
|
successor of infimum
|
|
|
|
on the right page
|
|
|
|
before merge */
|
|
|
|
const buf_block_t* left_block); /*!< in: merged index
|
|
|
|
page which will be
|
|
|
|
discarded */
|
|
|
|
/*************************************************************//**
|
|
|
|
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(
|
|
|
|
/*===================*/
|
|
|
|
const buf_block_t* block, /*!< in: index page to which copied */
|
|
|
|
const buf_block_t* root); /*!< in: root page */
|
|
|
|
/*************************************************************//**
|
|
|
|
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(
|
|
|
|
/*=========================*/
|
|
|
|
const buf_block_t* new_block, /*!< in: index page to
|
|
|
|
which copied */
|
|
|
|
const buf_block_t* block); /*!< in: index page;
|
|
|
|
NOT the root! */
|
|
|
|
/*************************************************************//**
|
|
|
|
Updates the lock table when a page is split to the left. */
|
|
|
|
void
|
|
|
|
lock_update_split_left(
|
|
|
|
/*===================*/
|
|
|
|
const buf_block_t* right_block, /*!< in: right page */
|
|
|
|
const buf_block_t* left_block); /*!< in: left page */
|
|
|
|
/*************************************************************//**
|
|
|
|
Updates the lock table when a page is merged to the left. */
|
|
|
|
void
|
|
|
|
lock_update_merge_left(
|
|
|
|
/*===================*/
|
|
|
|
const buf_block_t* left_block, /*!< in: left page to
|
|
|
|
which merged */
|
|
|
|
const rec_t* orig_pred, /*!< in: original predecessor
|
|
|
|
of supremum on the left page
|
|
|
|
before merge */
|
|
|
|
const buf_block_t* right_block); /*!< in: merged index page
|
|
|
|
which will be discarded */
|
|
|
|
/*************************************************************//**
|
2017-10-02 11:12:19 +03:00
|
|
|
Updates the lock table when a page is split and merged to
|
2014-08-06 15:28:58 +03:00
|
|
|
two pages. */
|
|
|
|
UNIV_INTERN
|
|
|
|
void
|
|
|
|
lock_update_split_and_merge(
|
|
|
|
const buf_block_t* left_block, /*!< in: left page to which merged */
|
|
|
|
const rec_t* orig_pred, /*!< in: original predecessor of
|
|
|
|
supremum on the left page before merge*/
|
|
|
|
const buf_block_t* right_block);/*!< in: right page from which merged */
|
|
|
|
/*************************************************************//**
|
2014-02-26 19:11:54 +01:00
|
|
|
Resets the original locks on heir and replaces them with gap type locks
|
|
|
|
inherited from rec. */
|
|
|
|
void
|
|
|
|
lock_rec_reset_and_inherit_gap_locks(
|
|
|
|
/*=================================*/
|
|
|
|
const buf_block_t* heir_block, /*!< in: block containing the
|
|
|
|
record which inherits */
|
|
|
|
const buf_block_t* block, /*!< in: block containing the
|
|
|
|
record from which inherited;
|
|
|
|
does NOT reset the locks on
|
|
|
|
this record */
|
|
|
|
ulint heir_heap_no, /*!< in: heap_no of the
|
|
|
|
inheriting record */
|
|
|
|
ulint heap_no); /*!< in: heap_no of the
|
|
|
|
donating record */
|
|
|
|
/*************************************************************//**
|
|
|
|
Updates the lock table when a page is discarded. */
|
|
|
|
void
|
|
|
|
lock_update_discard(
|
|
|
|
/*================*/
|
|
|
|
const buf_block_t* heir_block, /*!< in: index page
|
|
|
|
which will inherit the locks */
|
|
|
|
ulint heir_heap_no, /*!< in: heap_no of the record
|
|
|
|
which will inherit the locks */
|
|
|
|
const buf_block_t* block); /*!< in: index page
|
|
|
|
which will be discarded */
|
|
|
|
/*************************************************************//**
|
|
|
|
Updates the lock table when a new user record is inserted. */
|
|
|
|
void
|
|
|
|
lock_update_insert(
|
|
|
|
/*===============*/
|
|
|
|
const buf_block_t* block, /*!< in: buffer block containing rec */
|
|
|
|
const rec_t* rec); /*!< in: the inserted record */
|
|
|
|
/*************************************************************//**
|
|
|
|
Updates the lock table when a record is removed. */
|
|
|
|
void
|
|
|
|
lock_update_delete(
|
|
|
|
/*===============*/
|
|
|
|
const buf_block_t* block, /*!< in: buffer block containing rec */
|
|
|
|
const rec_t* rec); /*!< in: the record to be removed */
|
|
|
|
/*********************************************************************//**
|
|
|
|
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 in such an update moved, 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(
|
|
|
|
/*===========================*/
|
|
|
|
const buf_block_t* block, /*!< in: buffer block containing rec */
|
|
|
|
const 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 */
|
|
|
|
/*********************************************************************//**
|
|
|
|
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(
|
|
|
|
/*===============================*/
|
|
|
|
const buf_block_t* block, /*!< in: buffer block containing rec */
|
|
|
|
const rec_t* rec, /*!< in: record whose lock state
|
|
|
|
is restored */
|
|
|
|
const buf_block_t* donator);/*!< in: page (rec is not
|
|
|
|
necessarily on this page)
|
|
|
|
whose infimum stored the lock
|
|
|
|
state; lock bits are reset on
|
|
|
|
the infimum */
|
|
|
|
/*********************************************************************//**
|
|
|
|
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.
|
2018-03-13 11:07:34 +02:00
|
|
|
@return DB_SUCCESS, DB_LOCK_WAIT, or DB_DEADLOCK */
|
2014-02-26 19:11:54 +01:00
|
|
|
dberr_t
|
|
|
|
lock_rec_insert_check_and_lock(
|
|
|
|
/*===========================*/
|
|
|
|
ulint flags, /*!< in: if BTR_NO_LOCKING_FLAG bit is
|
|
|
|
set, does nothing */
|
|
|
|
const rec_t* rec, /*!< in: record after which to insert */
|
|
|
|
buf_block_t* block, /*!< in/out: buffer block of rec */
|
|
|
|
dict_index_t* index, /*!< in: index */
|
|
|
|
que_thr_t* thr, /*!< in: query thread */
|
|
|
|
mtr_t* mtr, /*!< in/out: mini-transaction */
|
2018-02-13 23:29:51 +03:00
|
|
|
bool* inherit)/*!< out: set to true if the new
|
2014-02-26 19:11:54 +01:00
|
|
|
inserted record maybe should inherit
|
|
|
|
LOCK_GAP type locks from the successor
|
|
|
|
record */
|
2016-09-06 09:43:16 +03:00
|
|
|
MY_ATTRIBUTE((warn_unused_result));
|
2016-08-12 11:17:45 +03:00
|
|
|
|
2014-02-26 19:11:54 +01:00
|
|
|
/*********************************************************************//**
|
|
|
|
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.
|
2018-03-13 11:07:34 +02:00
|
|
|
@return DB_SUCCESS, DB_LOCK_WAIT, or DB_DEADLOCK */
|
2014-02-26 19:11:54 +01:00
|
|
|
dberr_t
|
|
|
|
lock_clust_rec_modify_check_and_lock(
|
|
|
|
/*=================================*/
|
|
|
|
ulint flags, /*!< in: if BTR_NO_LOCKING_FLAG
|
|
|
|
bit is set, does nothing */
|
|
|
|
const buf_block_t* block, /*!< in: buffer block of rec */
|
|
|
|
const rec_t* rec, /*!< in: record which should be
|
|
|
|
modified */
|
|
|
|
dict_index_t* index, /*!< in: clustered index */
|
2020-04-28 10:46:51 +10:00
|
|
|
const rec_offs* offsets,/*!< in: rec_get_offsets(rec, index) */
|
2014-02-26 19:11:54 +01:00
|
|
|
que_thr_t* thr) /*!< in: query thread */
|
2016-09-06 09:43:16 +03:00
|
|
|
MY_ATTRIBUTE((warn_unused_result));
|
2014-02-26 19:11:54 +01:00
|
|
|
/*********************************************************************//**
|
|
|
|
Checks if locks of other transactions prevent an immediate modify
|
|
|
|
(delete mark or delete unmark) of a secondary index record.
|
2018-03-13 11:07:34 +02:00
|
|
|
@return DB_SUCCESS, DB_LOCK_WAIT, or DB_DEADLOCK */
|
2014-02-26 19:11:54 +01:00
|
|
|
dberr_t
|
|
|
|
lock_sec_rec_modify_check_and_lock(
|
|
|
|
/*===============================*/
|
|
|
|
ulint flags, /*!< in: if BTR_NO_LOCKING_FLAG
|
|
|
|
bit is set, does nothing */
|
|
|
|
buf_block_t* block, /*!< in/out: buffer block of rec */
|
|
|
|
const 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
|
|
|
|
(can be NULL if BTR_NO_LOCKING_FLAG) */
|
|
|
|
mtr_t* mtr) /*!< in/out: mini-transaction */
|
2016-09-06 09:43:16 +03:00
|
|
|
MY_ATTRIBUTE((warn_unused_result));
|
2014-02-26 19:11:54 +01:00
|
|
|
/*********************************************************************//**
|
|
|
|
Like lock_clust_rec_read_check_and_lock(), but reads a
|
|
|
|
secondary index record.
|
2018-03-13 11:07:34 +02:00
|
|
|
@return DB_SUCCESS, DB_SUCCESS_LOCKED_REC, DB_LOCK_WAIT, or DB_DEADLOCK */
|
2014-02-26 19:11:54 +01:00
|
|
|
dberr_t
|
|
|
|
lock_sec_rec_read_check_and_lock(
|
|
|
|
/*=============================*/
|
|
|
|
ulint flags, /*!< in: if BTR_NO_LOCKING_FLAG
|
|
|
|
bit is set, does nothing */
|
|
|
|
const buf_block_t* block, /*!< in: buffer block of rec */
|
|
|
|
const 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 */
|
2020-04-28 10:46:51 +10:00
|
|
|
const rec_offs* offsets,/*!< in: rec_get_offsets(rec, index) */
|
2016-08-12 11:17:45 +03:00
|
|
|
lock_mode mode, /*!< in: mode of the lock which
|
2014-02-26 19:11:54 +01:00
|
|
|
the read cursor should set on
|
|
|
|
records: LOCK_S or LOCK_X; the
|
|
|
|
latter is possible in
|
|
|
|
SELECT FOR UPDATE */
|
2020-03-10 20:05:17 +02:00
|
|
|
unsigned gap_mode,/*!< in: LOCK_ORDINARY, LOCK_GAP, or
|
2014-02-26 19:11:54 +01:00
|
|
|
LOCK_REC_NOT_GAP */
|
|
|
|
que_thr_t* thr); /*!< in: query thread */
|
|
|
|
/*********************************************************************//**
|
|
|
|
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.
|
2018-03-13 11:07:34 +02:00
|
|
|
@return DB_SUCCESS, DB_SUCCESS_LOCKED_REC, DB_LOCK_WAIT, or DB_DEADLOCK */
|
2014-02-26 19:11:54 +01:00
|
|
|
dberr_t
|
|
|
|
lock_clust_rec_read_check_and_lock(
|
|
|
|
/*===============================*/
|
|
|
|
ulint flags, /*!< in: if BTR_NO_LOCKING_FLAG
|
|
|
|
bit is set, does nothing */
|
|
|
|
const buf_block_t* block, /*!< in: buffer block of rec */
|
|
|
|
const 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 */
|
2020-04-28 10:46:51 +10:00
|
|
|
const rec_offs* offsets,/*!< in: rec_get_offsets(rec, index) */
|
2016-08-12 11:17:45 +03:00
|
|
|
lock_mode mode, /*!< in: mode of the lock which
|
2014-02-26 19:11:54 +01:00
|
|
|
the read cursor should set on
|
|
|
|
records: LOCK_S or LOCK_X; the
|
|
|
|
latter is possible in
|
|
|
|
SELECT FOR UPDATE */
|
2020-03-10 20:05:17 +02:00
|
|
|
unsigned gap_mode,/*!< in: LOCK_ORDINARY, LOCK_GAP, or
|
2014-02-26 19:11:54 +01:00
|
|
|
LOCK_REC_NOT_GAP */
|
|
|
|
que_thr_t* thr); /*!< in: query thread */
|
|
|
|
/*********************************************************************//**
|
|
|
|
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".
|
2018-03-13 11:07:34 +02:00
|
|
|
@return DB_SUCCESS, DB_LOCK_WAIT, or DB_DEADLOCK */
|
2014-02-26 19:11:54 +01:00
|
|
|
dberr_t
|
|
|
|
lock_clust_rec_read_check_and_lock_alt(
|
|
|
|
/*===================================*/
|
|
|
|
ulint flags, /*!< in: if BTR_NO_LOCKING_FLAG
|
|
|
|
bit is set, does nothing */
|
|
|
|
const buf_block_t* block, /*!< in: buffer block of rec */
|
|
|
|
const 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 */
|
2016-08-12 11:17:45 +03:00
|
|
|
lock_mode mode, /*!< in: mode of the lock which
|
2014-02-26 19:11:54 +01:00
|
|
|
the read cursor should set on
|
|
|
|
records: LOCK_S or LOCK_X; the
|
|
|
|
latter is possible in
|
|
|
|
SELECT FOR UPDATE */
|
2020-03-10 20:05:17 +02:00
|
|
|
unsigned gap_mode,/*!< in: LOCK_ORDINARY, LOCK_GAP, or
|
2014-02-26 19:11:54 +01:00
|
|
|
LOCK_REC_NOT_GAP */
|
|
|
|
que_thr_t* thr) /*!< in: query thread */
|
2016-09-06 09:43:16 +03:00
|
|
|
MY_ATTRIBUTE((warn_unused_result));
|
2014-02-26 19:11:54 +01:00
|
|
|
/*********************************************************************//**
|
|
|
|
Checks that a record is seen in a consistent read.
|
|
|
|
@return true if sees, or false if an earlier version of the record
|
|
|
|
should be retrieved */
|
|
|
|
bool
|
|
|
|
lock_clust_rec_cons_read_sees(
|
|
|
|
/*==========================*/
|
|
|
|
const rec_t* rec, /*!< in: user record which should be read or
|
|
|
|
passed over by a read cursor */
|
|
|
|
dict_index_t* index, /*!< in: clustered index */
|
2020-04-28 10:46:51 +10:00
|
|
|
const rec_offs* offsets,/*!< in: rec_get_offsets(rec, index) */
|
2016-08-12 11:17:45 +03:00
|
|
|
ReadView* view); /*!< in: consistent read view */
|
2014-02-26 19:11:54 +01:00
|
|
|
/*********************************************************************//**
|
|
|
|
Checks that a non-clustered index record is seen in a consistent read.
|
|
|
|
|
|
|
|
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.
|
|
|
|
|
|
|
|
@return true if certainly sees, or false if an earlier version of the
|
|
|
|
clustered index record might be needed */
|
|
|
|
bool
|
|
|
|
lock_sec_rec_cons_read_sees(
|
|
|
|
/*========================*/
|
2016-09-06 09:43:16 +03:00
|
|
|
const rec_t* rec, /*!< in: user record which
|
|
|
|
should be read or passed over
|
|
|
|
by a read cursor */
|
|
|
|
const dict_index_t* index, /*!< in: index */
|
|
|
|
const ReadView* view) /*!< in: consistent read view */
|
|
|
|
MY_ATTRIBUTE((warn_unused_result));
|
2014-02-26 19:11:54 +01:00
|
|
|
/*********************************************************************//**
|
|
|
|
Locks the specified database table in the mode given. If the lock cannot
|
|
|
|
be granted immediately, the query thread is put to wait.
|
2018-03-13 11:07:34 +02:00
|
|
|
@return DB_SUCCESS, DB_LOCK_WAIT, or DB_DEADLOCK */
|
2014-02-26 19:11:54 +01:00
|
|
|
dberr_t
|
|
|
|
lock_table(
|
|
|
|
/*=======*/
|
2020-03-10 20:05:17 +02:00
|
|
|
unsigned flags, /*!< in: if BTR_NO_LOCKING_FLAG bit is set,
|
2014-02-26 19:11:54 +01:00
|
|
|
does nothing */
|
|
|
|
dict_table_t* table, /*!< in/out: database table
|
|
|
|
in dictionary cache */
|
2016-08-12 11:17:45 +03:00
|
|
|
lock_mode mode, /*!< in: lock mode */
|
2014-02-26 19:11:54 +01:00
|
|
|
que_thr_t* thr) /*!< in: query thread */
|
2016-09-06 09:43:16 +03:00
|
|
|
MY_ATTRIBUTE((warn_unused_result));
|
2014-02-26 19:11:54 +01:00
|
|
|
/*********************************************************************//**
|
|
|
|
Creates a table IX lock object for a resurrected transaction. */
|
|
|
|
void
|
|
|
|
lock_table_ix_resurrect(
|
|
|
|
/*====================*/
|
|
|
|
dict_table_t* table, /*!< in/out: table */
|
|
|
|
trx_t* trx); /*!< in/out: transaction */
|
2016-09-06 09:43:16 +03:00
|
|
|
|
|
|
|
/** Sets a lock on a table based on the given mode.
|
|
|
|
@param[in] table table to lock
|
|
|
|
@param[in,out] trx transaction
|
|
|
|
@param[in] mode LOCK_X or LOCK_S
|
|
|
|
@return error code or DB_SUCCESS. */
|
|
|
|
dberr_t
|
|
|
|
lock_table_for_trx(
|
|
|
|
dict_table_t* table,
|
|
|
|
trx_t* trx,
|
|
|
|
enum lock_mode mode)
|
|
|
|
MY_ATTRIBUTE((nonnull, warn_unused_result));
|
|
|
|
|
2014-02-26 19:11:54 +01:00
|
|
|
/*************************************************************//**
|
|
|
|
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/out: transaction that has
|
|
|
|
set a record lock */
|
|
|
|
const buf_block_t* block, /*!< in: buffer block containing rec */
|
|
|
|
const rec_t* rec, /*!< in: record */
|
2016-08-12 11:17:45 +03:00
|
|
|
lock_mode lock_mode);/*!< in: LOCK_S or LOCK_X */
|
MDEV-15326: InnoDB: Failing assertion: !other_lock
MySQL 5.7.9 (and MariaDB 10.2.2) introduced a race condition
between InnoDB transaction commit and the conversion of implicit
locks into explicit ones.
The assertion failure can be triggered with a test that runs
3 concurrent single-statement transactions in a loop on a simple
table:
CREATE TABLE t (a INT PRIMARY KEY) ENGINE=InnoDB;
thread1: INSERT INTO t SET a=1;
thread2: DELETE FROM t;
thread3: SELECT * FROM t FOR UPDATE; -- or DELETE FROM t;
The failure scenarios are like the following:
(1) The INSERT statement is being committed, waiting for lock_sys->mutex.
(2) At the time of the failure, both the DELETE and SELECT transactions
are active but have not logged any changes yet.
(3) The transaction where the !other_lock assertion fails started
lock_rec_convert_impl_to_expl().
(4) After this point, the commit of the INSERT removed the transaction from
trx_sys->rw_trx_set, in trx_erase_lists().
(5) The other transaction consulted trx_sys->rw_trx_set and determined
that there is no implicit lock. Hence, it grabbed the lock.
(6) The !other_lock assertion fails in lock_rec_add_to_queue()
for the lock_rec_convert_impl_to_expl(), because the lock was 'stolen'.
This assertion failure looks genuine, because the INSERT transaction
is still active (trx->state=TRX_STATE_ACTIVE).
The problematic step (4) was introduced in
mysql/mysql-server@e27e0e0bb75b4d35e87059816f1cc370c09890ad
which fixed something related to MVCC (covered by the test
innodb.innodb-read-view). Basically, it reintroduced an error
that had been mentioned in an earlier commit
mysql/mysql-server@a17be6963fc0d9210fa0642d3985b7219cdaf0c5:
"The active transaction was removed from trx_sys->rw_trx_set prematurely."
Our fix goes along the following lines:
(a) Implicit locks will released by assigning
trx->state=TRX_STATE_COMMITTED_IN_MEMORY as the first step.
This transition will no longer be protected by lock_sys_t::mutex,
only by trx->mutex. This idea is by Sergey Vojtovich.
(b) We detach the transaction from trx_sys before starting to release
explicit locks.
(c) All callers of trx_rw_is_active() and trx_rw_is_active_low() must
recheck trx->state after acquiring trx->mutex.
(d) Before releasing any explicit locks, we will ensure that any activity
by other threads to convert implicit locks into explicit will have ceased,
by checking !trx_is_referenced(trx). There was a glitch
in this check when it was part of lock_trx_release_locks(); at the end
we would release trx->mutex and acquire lock_sys->mutex and trx->mutex,
and fail to recheck (trx_is_referenced() is protected by trx_t::mutex).
(e) Explicit locks can be released in batches (LOCK_RELEASE_INTERVAL=1000)
just like we did before.
trx_t::state: Document that the transition to COMMITTED is only
protected by trx_t::mutex, no longer by lock_sys_t::mutex.
trx_rw_is_active_low(), trx_rw_is_active(): Document that the transaction
state should be rechecked after acquiring trx_t::mutex.
trx_t::commit_state(): New function to change a transaction to committed
state, to release implicit locks.
trx_t::release_locks(): New function to release the explicit locks
after commit_state().
lock_trx_release_locks(): Move much of the logic to the caller
(which must invoke trx_t::commit_state() and trx_t::release_locks()
as needed), and assert that the transaction will have locks.
trx_get_trx_by_xid(): Make the parameter a pointer to const.
lock_rec_other_trx_holds_expl(): Recheck trx->state after acquiring
trx->mutex, and avoid a redundant lookup of the transaction.
lock_rec_queue_validate(): Recheck impl_trx->state while holding
impl_trx->mutex.
row_vers_impl_x_locked(), row_vers_impl_x_locked_low():
Document that the transaction state must be rechecked after
trx_mutex_enter().
trx_free_prepared(): Adjust for the changes to lock_trx_release_locks().
2019-09-03 12:31:37 +03:00
|
|
|
|
|
|
|
/** Release the explicit locks of a committing transaction,
|
|
|
|
and release possible other transactions waiting because of these locks. */
|
2019-09-05 15:57:39 +03:00
|
|
|
void lock_release(trx_t* trx);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
2021-10-18 12:49:10 +03:00
|
|
|
/** Release non-exclusive locks on XA PREPARE,
|
|
|
|
and release possible other transactions waiting because of these locks. */
|
|
|
|
void lock_release_on_prepare(trx_t *trx);
|
|
|
|
|
2016-08-12 11:17:45 +03:00
|
|
|
/*************************************************************//**
|
|
|
|
Get the lock hash table */
|
|
|
|
UNIV_INLINE
|
|
|
|
hash_table_t*
|
|
|
|
lock_hash_get(
|
|
|
|
/*==========*/
|
|
|
|
ulint mode); /*!< in: lock mode */
|
|
|
|
|
2014-02-26 19:11:54 +01:00
|
|
|
/**********************************************************************//**
|
|
|
|
Looks for a set bit in a record lock bitmap. Returns ULINT_UNDEFINED,
|
|
|
|
if none found.
|
|
|
|
@return bit index == heap number of the record, or ULINT_UNDEFINED if
|
|
|
|
none found */
|
|
|
|
ulint
|
|
|
|
lock_rec_find_set_bit(
|
|
|
|
/*==================*/
|
|
|
|
const lock_t* lock); /*!< in: record lock with at least one
|
|
|
|
bit set */
|
|
|
|
|
|
|
|
/*********************************************************************//**
|
|
|
|
Checks if a lock request lock1 has to wait for request lock2.
|
2018-02-16 22:15:51 +03:00
|
|
|
@return whether lock1 has to wait for lock2 to be removed */
|
2018-02-13 22:03:26 +03:00
|
|
|
bool
|
2014-02-26 19:11:54 +01:00
|
|
|
lock_has_to_wait(
|
|
|
|
/*=============*/
|
|
|
|
const lock_t* lock1, /*!< in: waiting lock */
|
|
|
|
const 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 */
|
|
|
|
/*********************************************************************//**
|
|
|
|
Reports that a transaction id is insensible, i.e., in the future. */
|
2020-06-04 10:24:10 +03:00
|
|
|
ATTRIBUTE_COLD
|
2014-02-26 19:11:54 +01:00
|
|
|
void
|
|
|
|
lock_report_trx_id_insanity(
|
|
|
|
/*========================*/
|
|
|
|
trx_id_t trx_id, /*!< in: trx id */
|
|
|
|
const rec_t* rec, /*!< in: user record */
|
|
|
|
dict_index_t* index, /*!< in: index */
|
2020-04-28 10:46:51 +10:00
|
|
|
const rec_offs* offsets, /*!< in: rec_get_offsets(rec, index) */
|
2017-12-22 16:15:41 +02:00
|
|
|
trx_id_t max_trx_id); /*!< in: trx_sys.get_max_trx_id() */
|
2014-02-26 19:11:54 +01:00
|
|
|
/*********************************************************************//**
|
|
|
|
Prints info of locks for all transactions.
|
|
|
|
@return FALSE if not able to obtain lock mutex and exits without
|
|
|
|
printing info */
|
|
|
|
ibool
|
|
|
|
lock_print_info_summary(
|
|
|
|
/*====================*/
|
|
|
|
FILE* file, /*!< in: file where to print */
|
|
|
|
ibool nowait) /*!< in: whether to wait for the lock mutex */
|
2016-09-06 09:43:16 +03:00
|
|
|
MY_ATTRIBUTE((warn_unused_result));
|
2016-08-12 11:17:45 +03:00
|
|
|
|
|
|
|
/** Prints transaction lock wait and MVCC state.
|
|
|
|
@param[in,out] file file where to print
|
2019-07-25 12:08:50 +03:00
|
|
|
@param[in] trx transaction
|
|
|
|
@param[in] now current time */
|
2016-08-12 11:17:45 +03:00
|
|
|
void
|
2019-07-25 12:08:50 +03:00
|
|
|
lock_trx_print_wait_and_mvcc_state(FILE* file, const trx_t* trx, time_t now);
|
2016-08-12 11:17:45 +03:00
|
|
|
|
2014-02-26 19:11:54 +01:00
|
|
|
/*********************************************************************//**
|
|
|
|
Prints info of locks for each transaction. This function assumes that the
|
|
|
|
caller holds the lock mutex and more importantly it will release the lock
|
|
|
|
mutex on behalf of the caller. (This should be fixed in the future). */
|
|
|
|
void
|
|
|
|
lock_print_info_all_transactions(
|
|
|
|
/*=============================*/
|
|
|
|
FILE* file); /*!< in: file where to print */
|
|
|
|
/*********************************************************************//**
|
|
|
|
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.
|
2018-02-22 20:46:42 +04:00
|
|
|
The caller must be holding lock_sys.mutex. */
|
2014-02-26 19:11:54 +01:00
|
|
|
ulint
|
|
|
|
lock_number_of_rows_locked(
|
|
|
|
/*=======================*/
|
|
|
|
const trx_lock_t* trx_lock) /*!< in: transaction locks */
|
2016-09-06 09:43:16 +03:00
|
|
|
MY_ATTRIBUTE((warn_unused_result));
|
2014-02-26 19:11:54 +01:00
|
|
|
|
2016-08-12 11:17:45 +03:00
|
|
|
/*********************************************************************//**
|
|
|
|
Return the number of table locks for a transaction.
|
2018-02-22 20:46:42 +04:00
|
|
|
The caller must be holding lock_sys.mutex. */
|
2016-08-12 11:17:45 +03:00
|
|
|
ulint
|
|
|
|
lock_number_of_tables_locked(
|
|
|
|
/*=========================*/
|
|
|
|
const trx_lock_t* trx_lock) /*!< in: transaction locks */
|
2016-09-06 09:43:16 +03:00
|
|
|
MY_ATTRIBUTE((warn_unused_result));
|
2016-08-12 11:17:45 +03:00
|
|
|
|
2014-02-26 19:11:54 +01:00
|
|
|
/*******************************************************************//**
|
|
|
|
Gets the type of a lock. Non-inline version for using outside of the
|
|
|
|
lock module.
|
2016-08-12 11:17:45 +03:00
|
|
|
@return LOCK_TABLE or LOCK_REC */
|
2014-02-26 19:11:54 +01:00
|
|
|
ulint
|
|
|
|
lock_get_type(
|
|
|
|
/*==========*/
|
|
|
|
const lock_t* lock); /*!< in: lock */
|
|
|
|
|
|
|
|
/*******************************************************************//**
|
|
|
|
Gets the id of the table on which the lock is.
|
2016-08-12 11:17:45 +03:00
|
|
|
@return id of the table */
|
2014-02-26 19:11:54 +01:00
|
|
|
table_id_t
|
|
|
|
lock_get_table_id(
|
|
|
|
/*==============*/
|
|
|
|
const lock_t* lock); /*!< in: lock */
|
|
|
|
|
2016-08-12 11:17:45 +03:00
|
|
|
/** Determine which table a lock is associated with.
|
|
|
|
@param[in] lock the lock
|
|
|
|
@return name of the table */
|
|
|
|
const table_name_t&
|
2014-02-26 19:11:54 +01:00
|
|
|
lock_get_table_name(
|
2016-08-12 11:17:45 +03:00
|
|
|
const lock_t* lock);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
/*******************************************************************//**
|
|
|
|
For a record lock, gets the index on which the lock is.
|
2016-08-12 11:17:45 +03:00
|
|
|
@return index */
|
2014-02-26 19:11:54 +01:00
|
|
|
const dict_index_t*
|
|
|
|
lock_rec_get_index(
|
|
|
|
/*===============*/
|
|
|
|
const lock_t* lock); /*!< in: lock */
|
|
|
|
|
|
|
|
/*******************************************************************//**
|
|
|
|
For a record lock, gets the name of the index on which the lock is.
|
|
|
|
The string should not be free()'d or modified.
|
2016-08-12 11:17:45 +03:00
|
|
|
@return name of the index */
|
2014-02-26 19:11:54 +01:00
|
|
|
const char*
|
|
|
|
lock_rec_get_index_name(
|
|
|
|
/*====================*/
|
|
|
|
const lock_t* lock); /*!< in: lock */
|
|
|
|
|
|
|
|
/*******************************************************************//**
|
|
|
|
Check if there are any locks (table or rec) against table.
|
2016-08-12 11:17:45 +03:00
|
|
|
@return TRUE if locks exist */
|
|
|
|
bool
|
2014-02-26 19:11:54 +01:00
|
|
|
lock_table_has_locks(
|
|
|
|
/*=================*/
|
|
|
|
const dict_table_t* table); /*!< in: check if there are any locks
|
|
|
|
held on records in this table or on the
|
|
|
|
table itself */
|
|
|
|
|
2019-11-13 18:14:44 +01:00
|
|
|
/** A task which wakes up threads whose lock wait may have lasted too long */
|
2019-10-29 22:37:12 +01:00
|
|
|
void lock_wait_timeout_task(void*);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
/********************************************************************//**
|
|
|
|
Releases a user OS thread waiting for a lock to be released, if the
|
|
|
|
thread is already suspended. */
|
|
|
|
void
|
|
|
|
lock_wait_release_thread_if_suspended(
|
|
|
|
/*==================================*/
|
|
|
|
que_thr_t* thr); /*!< in: query thread associated with the
|
|
|
|
user OS thread */
|
|
|
|
|
|
|
|
/***************************************************************//**
|
|
|
|
Puts a user OS thread to wait for a lock to be released. If an error
|
|
|
|
occurs during the wait trx->error_state associated with thr is
|
|
|
|
!= DB_SUCCESS when we return. DB_LOCK_WAIT_TIMEOUT and DB_DEADLOCK
|
|
|
|
are possible errors. DB_DEADLOCK is returned if selective deadlock
|
|
|
|
resolution chose this transaction as a victim. */
|
|
|
|
void
|
|
|
|
lock_wait_suspend_thread(
|
|
|
|
/*=====================*/
|
|
|
|
que_thr_t* thr); /*!< in: query thread associated with the
|
|
|
|
user OS thread */
|
|
|
|
/*********************************************************************//**
|
|
|
|
Unlocks AUTO_INC type locks that were possibly reserved by a trx. This
|
|
|
|
function should be called at the the end of an SQL statement, by the
|
|
|
|
connection thread that owns the transaction (trx->mysql_thd). */
|
|
|
|
void
|
|
|
|
lock_unlock_table_autoinc(
|
|
|
|
/*======================*/
|
|
|
|
trx_t* trx); /*!< in/out: transaction */
|
|
|
|
/*********************************************************************//**
|
|
|
|
Check whether the transaction has already been rolled back because it
|
|
|
|
was selected as a deadlock victim, or if it has to wait then cancel
|
|
|
|
the wait lock.
|
|
|
|
@return DB_DEADLOCK, DB_LOCK_WAIT or DB_SUCCESS */
|
|
|
|
dberr_t
|
|
|
|
lock_trx_handle_wait(
|
|
|
|
/*=================*/
|
2018-03-16 14:35:42 +02:00
|
|
|
trx_t* trx); /*!< in/out: trx lock state */
|
2014-02-26 19:11:54 +01:00
|
|
|
/*********************************************************************//**
|
|
|
|
Get the number of locks on a table.
|
|
|
|
@return number of locks */
|
|
|
|
ulint
|
|
|
|
lock_table_get_n_locks(
|
|
|
|
/*===================*/
|
2016-08-12 11:17:45 +03:00
|
|
|
const dict_table_t* table); /*!< in: table */
|
|
|
|
/*******************************************************************//**
|
|
|
|
Initialise the trx lock list. */
|
|
|
|
void
|
|
|
|
lock_trx_lock_list_init(
|
|
|
|
/*====================*/
|
|
|
|
trx_lock_list_t* lock_list); /*!< List to initialise */
|
|
|
|
|
2014-02-26 19:11:54 +01:00
|
|
|
/*********************************************************************//**
|
|
|
|
Checks that a transaction id is sensible, i.e., not in the future.
|
2016-08-12 11:17:45 +03:00
|
|
|
@return true if ok */
|
2014-02-26 19:11:54 +01:00
|
|
|
bool
|
|
|
|
lock_check_trx_id_sanity(
|
|
|
|
/*=====================*/
|
|
|
|
trx_id_t trx_id, /*!< in: trx id */
|
|
|
|
const rec_t* rec, /*!< in: user record */
|
|
|
|
dict_index_t* index, /*!< in: index */
|
2020-05-04 16:47:11 +02:00
|
|
|
const rec_offs* offsets); /*!< in: rec_get_offsets(rec, index) */
|
2017-12-27 20:07:20 +04:00
|
|
|
#ifdef UNIV_DEBUG
|
2014-02-26 19:11:54 +01:00
|
|
|
/*******************************************************************//**
|
|
|
|
Check if the transaction holds any locks on the sys tables
|
|
|
|
or its records.
|
2016-08-12 11:17:45 +03:00
|
|
|
@return the strongest lock found on any sys table or 0 for none */
|
2014-02-26 19:11:54 +01:00
|
|
|
const lock_t*
|
|
|
|
lock_trx_has_sys_table_locks(
|
|
|
|
/*=========================*/
|
|
|
|
const trx_t* trx) /*!< in: transaction to check */
|
2018-07-03 15:10:06 +03:00
|
|
|
MY_ATTRIBUTE((nonnull, warn_unused_result));
|
2014-02-26 19:11:54 +01:00
|
|
|
|
2018-07-03 15:10:06 +03:00
|
|
|
/** Check if the transaction holds an explicit exclusive lock on a record.
|
|
|
|
@param[in] trx transaction
|
|
|
|
@param[in] table table
|
|
|
|
@param[in] block leaf page
|
|
|
|
@param[in] heap_no heap number identifying the record
|
|
|
|
@return whether an explicit X-lock is held */
|
2014-02-26 19:11:54 +01:00
|
|
|
bool
|
2018-07-03 15:10:06 +03:00
|
|
|
lock_trx_has_expl_x_lock(
|
2014-02-26 19:11:54 +01:00
|
|
|
const trx_t* trx, /*!< in: transaction to check */
|
|
|
|
const dict_table_t* table, /*!< in: table to check */
|
|
|
|
const buf_block_t* block, /*!< in: buffer block of the record */
|
|
|
|
ulint heap_no)/*!< in: record heap number */
|
2018-07-03 15:10:06 +03:00
|
|
|
MY_ATTRIBUTE((nonnull, warn_unused_result));
|
2014-02-26 19:11:54 +01:00
|
|
|
#endif /* UNIV_DEBUG */
|
|
|
|
|
|
|
|
/** Lock operation struct */
|
|
|
|
struct lock_op_t{
|
|
|
|
dict_table_t* table; /*!< table to be locked */
|
2016-08-12 11:17:45 +03:00
|
|
|
lock_mode mode; /*!< lock mode */
|
2014-02-26 19:11:54 +01:00
|
|
|
};
|
|
|
|
|
2016-08-12 11:17:45 +03:00
|
|
|
typedef ib_mutex_t LockMutex;
|
|
|
|
|
2014-02-26 19:11:54 +01:00
|
|
|
/** The lock system struct */
|
2018-02-22 20:46:42 +04:00
|
|
|
class lock_sys_t
|
|
|
|
{
|
|
|
|
bool m_initialised;
|
|
|
|
|
|
|
|
public:
|
|
|
|
MY_ALIGNED(CACHE_LINE_SIZE)
|
2016-08-12 11:17:45 +03:00
|
|
|
LockMutex mutex; /*!< Mutex protecting the
|
2014-02-26 19:11:54 +01:00
|
|
|
locks */
|
2020-06-18 12:26:28 +03:00
|
|
|
/** record locks */
|
|
|
|
hash_table_t rec_hash;
|
|
|
|
/** predicate locks for SPATIAL INDEX */
|
|
|
|
hash_table_t prdt_hash;
|
|
|
|
/** page locks for SPATIAL INDEX */
|
|
|
|
hash_table_t prdt_page_hash;
|
2016-08-12 11:17:45 +03:00
|
|
|
|
2018-02-22 20:46:42 +04:00
|
|
|
MY_ALIGNED(CACHE_LINE_SIZE)
|
2016-08-12 11:17:45 +03:00
|
|
|
LockMutex wait_mutex; /*!< Mutex protecting the
|
2014-02-26 19:11:54 +01:00
|
|
|
next two fields */
|
|
|
|
srv_slot_t* waiting_threads; /*!< Array of user threads
|
|
|
|
suspended while waiting for
|
|
|
|
locks within InnoDB, protected
|
2018-02-22 20:46:42 +04:00
|
|
|
by the lock_sys.wait_mutex;
|
2017-02-17 10:32:21 +02:00
|
|
|
os_event_set() and
|
|
|
|
os_event_reset() on
|
|
|
|
waiting_threads[]->event
|
|
|
|
are protected by
|
|
|
|
trx_t::mutex */
|
2014-02-26 19:11:54 +01:00
|
|
|
srv_slot_t* last_slot; /*!< highest slot ever used
|
|
|
|
in the waiting_threads array,
|
|
|
|
protected by
|
2018-02-22 20:46:42 +04:00
|
|
|
lock_sys.wait_mutex */
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
ulint n_lock_max_wait_time; /*!< Max wait time */
|
|
|
|
|
2019-10-29 22:37:12 +01:00
|
|
|
std::unique_ptr<tpool::timer> timeout_timer; /*!< Thread pool timer task */
|
|
|
|
bool timeout_timer_active;
|
2018-02-22 20:46:42 +04:00
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
Constructor.
|
|
|
|
|
|
|
|
Some members may require late initialisation, thus we just mark object as
|
|
|
|
uninitialised. Real initialisation happens in create().
|
|
|
|
*/
|
|
|
|
lock_sys_t(): m_initialised(false) {}
|
|
|
|
|
|
|
|
|
|
|
|
bool is_initialised() { return m_initialised; }
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
Creates the lock system at database start.
|
|
|
|
|
|
|
|
@param[in] n_cells number of slots in lock hash table
|
|
|
|
*/
|
|
|
|
void create(ulint n_cells);
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
Resize the lock hash table.
|
|
|
|
|
|
|
|
@param[in] n_cells number of slots in lock hash table
|
|
|
|
*/
|
|
|
|
void resize(ulint n_cells);
|
|
|
|
|
|
|
|
|
|
|
|
/** Closes the lock system at database shutdown. */
|
|
|
|
void close();
|
2020-09-11 15:55:30 +03:00
|
|
|
|
|
|
|
/** @return the hash value for a page address */
|
|
|
|
ulint hash(const page_id_t id) const
|
|
|
|
{ ut_ad(mutex_own(&mutex)); return rec_hash.calc_hash(id.fold()); }
|
|
|
|
|
|
|
|
/** Get the first lock on a page.
|
|
|
|
@param lock_hash hash table to look at
|
|
|
|
@param id page number
|
|
|
|
@return first lock
|
|
|
|
@retval nullptr if none exists */
|
|
|
|
lock_t *get_first(const hash_table_t &lock_hash, const page_id_t id) const
|
|
|
|
{
|
|
|
|
ut_ad(&lock_hash == &rec_hash || &lock_hash == &prdt_hash ||
|
|
|
|
&lock_hash == &prdt_page_hash);
|
|
|
|
for (lock_t *lock= static_cast<lock_t*>
|
|
|
|
(HASH_GET_FIRST(&lock_hash, hash(id)));
|
|
|
|
lock; lock= static_cast<lock_t*>(HASH_GET_NEXT(hash, lock)))
|
|
|
|
if (lock->un_member.rec_lock.page_id == id)
|
|
|
|
return lock;
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Get the first record lock on a page.
|
|
|
|
@param id page number
|
|
|
|
@return first lock
|
|
|
|
@retval nullptr if none exists */
|
|
|
|
lock_t *get_first(const page_id_t id) const
|
|
|
|
{ return get_first(rec_hash, id); }
|
|
|
|
/** Get the first predicate lock on a SPATIAL INDEX page.
|
|
|
|
@param id page number
|
|
|
|
@return first lock
|
|
|
|
@retval nullptr if none exists */
|
|
|
|
lock_t *get_first_prdt(const page_id_t id) const
|
|
|
|
{ return get_first(prdt_hash, id); }
|
|
|
|
/** Get the first predicate lock on a SPATIAL INDEX page.
|
|
|
|
@param id page number
|
|
|
|
@return first lock
|
|
|
|
@retval nullptr if none exists */
|
|
|
|
lock_t *get_first_prdt_page(const page_id_t id) const
|
|
|
|
{ return get_first(prdt_page_hash, id); }
|
2014-02-26 19:11:54 +01:00
|
|
|
};
|
|
|
|
|
2018-03-11 23:34:23 +02:00
|
|
|
/*********************************************************************//**
|
|
|
|
Creates a new record lock and inserts it to the lock queue. Does NOT check
|
|
|
|
for deadlocks or lock compatibility!
|
2022-01-11 18:19:39 +03:00
|
|
|
@param[in] c_lock conflicting lock
|
|
|
|
@param[in] thr thread owning trx
|
|
|
|
@param[in] type_mode lock mode and wait flag, type is ignored and replaced by
|
|
|
|
LOCK_REC
|
|
|
|
@param[in] block buffer block containing the record
|
|
|
|
@param[in] heap_no heap number of the record
|
|
|
|
@param[in] index index of record
|
|
|
|
@param[in,out] trx transaction
|
|
|
|
@param[in] caller_owns_trx_mutex TRUE if caller owns trx mutex
|
|
|
|
@param[in] insert_before_waiting if true, inserts new B-tree record lock
|
|
|
|
just after the last non-waiting lock of the current transaction which is
|
|
|
|
located before the first waiting for the current transaction lock, otherwise
|
|
|
|
the lock is inserted at the end of the queue
|
2018-03-11 23:34:23 +02:00
|
|
|
@return created lock */
|
|
|
|
UNIV_INLINE
|
2022-01-11 18:19:39 +03:00
|
|
|
lock_t *lock_rec_create(lock_t *c_lock,
|
2018-03-11 23:34:23 +02:00
|
|
|
#ifdef WITH_WSREP
|
2022-01-11 18:19:39 +03:00
|
|
|
que_thr_t *thr,
|
2018-03-11 23:34:23 +02:00
|
|
|
#endif
|
2022-01-11 18:19:39 +03:00
|
|
|
unsigned type_mode, const buf_block_t *block,
|
|
|
|
ulint heap_no, dict_index_t *index, trx_t *trx,
|
|
|
|
bool caller_owns_trx_mutex,
|
|
|
|
bool insert_before_waiting= false);
|
2018-03-11 23:34:23 +02:00
|
|
|
|
2016-08-12 11:17:45 +03:00
|
|
|
/*************************************************************//**
|
|
|
|
Removes a record lock request, waiting or granted, from the queue. */
|
|
|
|
void
|
|
|
|
lock_rec_discard(
|
|
|
|
/*=============*/
|
|
|
|
lock_t* in_lock); /*!< in: record lock object: all
|
|
|
|
record locks which are contained
|
|
|
|
in this lock object are removed */
|
|
|
|
|
2018-03-11 23:34:23 +02:00
|
|
|
/** Create a new record lock and inserts it to the lock queue,
|
|
|
|
without checking for deadlocks or conflicts.
|
2022-01-11 18:19:39 +03:00
|
|
|
@param[in] c_lock conflicting lock
|
2018-03-11 23:34:23 +02:00
|
|
|
@param[in] type_mode lock mode and wait flag; type will be replaced
|
|
|
|
with LOCK_REC
|
2020-09-11 15:55:30 +03:00
|
|
|
@param[in] page_id index page number
|
2018-03-11 23:34:23 +02:00
|
|
|
@param[in] page R-tree index page, or NULL
|
|
|
|
@param[in] heap_no record heap number in the index page
|
|
|
|
@param[in] index the index tree
|
|
|
|
@param[in,out] trx transaction
|
|
|
|
@param[in] holds_trx_mutex whether the caller holds trx->mutex
|
2022-01-11 18:19:39 +03:00
|
|
|
@param[in] insert_before_waiting if true, inserts new B-tree record lock
|
|
|
|
just after the last non-waiting lock of the current transaction which is
|
|
|
|
located before the first waiting for the current transaction lock, otherwise
|
|
|
|
the lock is inserted at the end of the queue
|
2018-03-11 23:34:23 +02:00
|
|
|
@return created lock */
|
|
|
|
lock_t*
|
|
|
|
lock_rec_create_low(
|
2022-01-11 18:19:39 +03:00
|
|
|
lock_t* c_lock,
|
2018-03-11 23:34:23 +02:00
|
|
|
#ifdef WITH_WSREP
|
|
|
|
que_thr_t* thr, /*!< thread owning trx */
|
|
|
|
#endif
|
2020-03-10 20:05:17 +02:00
|
|
|
unsigned type_mode,
|
2020-09-11 15:55:30 +03:00
|
|
|
const page_id_t page_id,
|
2018-03-11 23:34:23 +02:00
|
|
|
const page_t* page,
|
|
|
|
ulint heap_no,
|
|
|
|
dict_index_t* index,
|
|
|
|
trx_t* trx,
|
2022-01-11 18:19:39 +03:00
|
|
|
bool holds_trx_mutex,
|
|
|
|
bool insert_before_waiting = false);
|
|
|
|
|
2018-03-11 23:34:23 +02:00
|
|
|
/** Enqueue a waiting request for a lock which cannot be granted immediately.
|
|
|
|
Check for deadlocks.
|
|
|
|
@param[in] type_mode the requested lock mode (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
|
|
|
|
@param[in] block leaf page in the index
|
|
|
|
@param[in] heap_no record heap number in the block
|
|
|
|
@param[in] index index tree
|
|
|
|
@param[in,out] thr query thread
|
|
|
|
@param[in] prdt minimum bounding box (spatial index)
|
|
|
|
@retval DB_LOCK_WAIT if the waiting lock was enqueued
|
|
|
|
@retval DB_DEADLOCK if this transaction was chosen as the victim
|
|
|
|
@retval DB_SUCCESS_LOCKED_REC if the other transaction was chosen as a victim
|
|
|
|
(or it happened to commit) */
|
|
|
|
dberr_t
|
|
|
|
lock_rec_enqueue_waiting(
|
|
|
|
lock_t* c_lock, /*!< conflicting lock */
|
2020-03-10 20:05:17 +02:00
|
|
|
unsigned type_mode,
|
2018-03-11 23:34:23 +02:00
|
|
|
const buf_block_t* block,
|
|
|
|
ulint heap_no,
|
|
|
|
dict_index_t* index,
|
|
|
|
que_thr_t* thr,
|
|
|
|
lock_prdt_t* prdt);
|
2016-08-12 11:17:45 +03:00
|
|
|
/*************************************************************//**
|
|
|
|
Moves the explicit locks on user records to another page if a record
|
|
|
|
list start is moved to another page. */
|
|
|
|
void
|
|
|
|
lock_rtr_move_rec_list(
|
|
|
|
/*===================*/
|
|
|
|
const buf_block_t* new_block, /*!< in: index page to
|
|
|
|
move to */
|
|
|
|
const buf_block_t* block, /*!< in: index page */
|
|
|
|
rtr_rec_move_t* rec_move, /*!< in: recording records
|
|
|
|
moved */
|
|
|
|
ulint num_move); /*!< in: num of rec to move */
|
|
|
|
|
|
|
|
/*************************************************************//**
|
|
|
|
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. */
|
|
|
|
void
|
|
|
|
lock_rec_free_all_from_discard_page(
|
|
|
|
/*================================*/
|
|
|
|
const buf_block_t* block); /*!< in: page to be discarded */
|
|
|
|
|
2014-02-26 19:11:54 +01:00
|
|
|
/** The lock system */
|
2018-02-22 20:46:42 +04:00
|
|
|
extern lock_sys_t lock_sys;
|
2014-02-26 19:11:54 +01:00
|
|
|
|
2018-02-22 20:46:42 +04:00
|
|
|
/** Test if lock_sys.mutex can be acquired without waiting. */
|
2016-08-12 11:17:45 +03:00
|
|
|
#define lock_mutex_enter_nowait() \
|
2018-02-22 20:46:42 +04:00
|
|
|
(lock_sys.mutex.trylock(__FILE__, __LINE__))
|
2014-02-26 19:11:54 +01:00
|
|
|
|
2018-02-22 20:46:42 +04:00
|
|
|
/** Test if lock_sys.mutex is owned. */
|
|
|
|
#define lock_mutex_own() (lock_sys.mutex.is_owned())
|
2014-02-26 19:11:54 +01:00
|
|
|
|
2018-02-22 20:46:42 +04:00
|
|
|
/** Acquire the lock_sys.mutex. */
|
2014-02-26 19:11:54 +01:00
|
|
|
#define lock_mutex_enter() do { \
|
2018-02-22 20:46:42 +04:00
|
|
|
mutex_enter(&lock_sys.mutex); \
|
2014-02-26 19:11:54 +01:00
|
|
|
} while (0)
|
|
|
|
|
2018-02-22 20:46:42 +04:00
|
|
|
/** Release the lock_sys.mutex. */
|
2014-02-26 19:11:54 +01:00
|
|
|
#define lock_mutex_exit() do { \
|
2018-02-22 20:46:42 +04:00
|
|
|
lock_sys.mutex.exit(); \
|
2014-02-26 19:11:54 +01:00
|
|
|
} while (0)
|
|
|
|
|
2018-02-22 20:46:42 +04:00
|
|
|
/** Test if lock_sys.wait_mutex is owned. */
|
|
|
|
#define lock_wait_mutex_own() (lock_sys.wait_mutex.is_owned())
|
2014-02-26 19:11:54 +01:00
|
|
|
|
2018-02-22 20:46:42 +04:00
|
|
|
/** Acquire the lock_sys.wait_mutex. */
|
2014-02-26 19:11:54 +01:00
|
|
|
#define lock_wait_mutex_enter() do { \
|
2018-02-22 20:46:42 +04:00
|
|
|
mutex_enter(&lock_sys.wait_mutex); \
|
2014-02-26 19:11:54 +01:00
|
|
|
} while (0)
|
|
|
|
|
2018-02-22 20:46:42 +04:00
|
|
|
/** Release the lock_sys.wait_mutex. */
|
2014-02-26 19:11:54 +01:00
|
|
|
#define lock_wait_mutex_exit() do { \
|
2018-02-22 20:46:42 +04:00
|
|
|
lock_sys.wait_mutex.exit(); \
|
2014-02-26 19:11:54 +01:00
|
|
|
} while (0)
|
|
|
|
|
2014-08-06 15:39:15 +03:00
|
|
|
#ifdef WITH_WSREP
|
|
|
|
/*********************************************************************//**
|
|
|
|
Cancels a waiting lock request and releases possible other transactions
|
|
|
|
waiting behind it. */
|
|
|
|
UNIV_INTERN
|
|
|
|
void
|
|
|
|
lock_cancel_waiting_and_release(
|
|
|
|
/*============================*/
|
|
|
|
lock_t* lock); /*!< in/out: waiting lock request */
|
2015-12-21 16:36:26 +02:00
|
|
|
|
|
|
|
/*******************************************************************//**
|
|
|
|
Get lock mode and table/index name
|
|
|
|
@return string containing lock info */
|
|
|
|
std::string
|
|
|
|
lock_get_info(
|
|
|
|
const lock_t*);
|
|
|
|
|
2014-08-06 15:39:15 +03:00
|
|
|
#endif /* WITH_WSREP */
|
2016-09-06 09:43:16 +03:00
|
|
|
|
2022-01-13 17:27:28 +01:00
|
|
|
#include "lock0lock.inl"
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
#endif
|