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.
|
2021-01-04 15:30:34 +02:00
|
|
|
Copyright (c) 2017, 2021, 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-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 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(
|
|
|
|
/*===========================*/
|
|
|
|
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
|
|
|
/*********************************************************************//**
|
|
|
|
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
|
|
|
|
MDEV-515 Reduce InnoDB undo logging for insert into empty table
We implement an idea that was suggested by Michael 'Monty' Widenius
in October 2017: When InnoDB is inserting into an empty table or partition,
we can write a single undo log record TRX_UNDO_EMPTY, which will cause
ROLLBACK to clear the table.
For this to work, the insert into an empty table or partition must be
covered by an exclusive table lock that will be held until the transaction
has been committed or rolled back, or the INSERT operation has been
rolled back (and the table is empty again), in lock_table_x_unlock().
Clustered index records that are covered by the TRX_UNDO_EMPTY record
will carry DB_TRX_ID=0 and DB_ROLL_PTR=1<<55, and thus they cannot
be distinguished from what MDEV-12288 leaves behind after purging the
history of row-logged operations.
Concurrent non-locking reads must be adjusted: If the read view was
created before the INSERT into an empty table, then we must continue
to imagine that the table is empty, and not try to read any records.
If the read view was created after the INSERT was committed, then
all records must be visible normally. To implement this, we introduce
the field dict_table_t::bulk_trx_id.
This special handling only applies to the very first INSERT statement
of a transaction for the empty table or partition. If a subsequent
statement in the transaction is modifying the initially empty table again,
we must enable row-level undo logging, so that we will be able to
roll back to the start of the statement in case of an error (such as
duplicate key).
INSERT IGNORE will continue to use row-level logging and locking, because
implementing it would require the ability to roll back the latest row.
Since the undo log that we write only allows us to roll back the entire
statement, we cannot support INSERT IGNORE. We will introduce a
handler::extra() parameter HA_EXTRA_IGNORE_INSERT to indicate to storage
engines that INSERT IGNORE is being executed.
In many test cases, we add an extra record to the table, so that during
the 'interesting' part of the test, row-level locking and logging will
be used.
Replicas will continue to use row-level logging and locking until
MDEV-24622 has been addressed. Likewise, this optimization will be
disabled in Galera cluster until MDEV-24623 enables it.
dict_table_t::bulk_trx_id: The latest active or committed transaction
that initiated an insert into an empty table or partition.
Protected by exclusive table lock and a clustered index leaf page latch.
ins_node_t::bulk_insert: Whether bulk insert was initiated.
trx_t::mod_tables: Use C++11 style accessors (emplace instead of insert).
Unlike earlier, this collection will cover also temporary tables.
trx_mod_table_time_t: Add start_bulk_insert(), end_bulk_insert(),
is_bulk_insert(), was_bulk_insert().
trx_undo_report_row_operation(): Before accessing any undo log pages,
invoke trx->mod_tables.emplace() in order to determine whether undo
logging was disabled, or whether this is the first INSERT and we are
supposed to write a TRX_UNDO_EMPTY record.
row_ins_clust_index_entry_low(): If we are inserting into an empty
clustered index leaf page, set the ins_node_t::bulk_insert flag for
the subsequent trx_undo_report_row_operation() call.
lock_rec_insert_check_and_lock(), lock_prdt_insert_check_and_lock():
Remove the redundant parameter 'flags' that can be checked in the caller.
btr_cur_ins_lock_and_undo(): Simplify the logic. Correctly write
DB_TRX_ID,DB_ROLL_PTR after invoking trx_undo_report_row_operation().
trx_mark_sql_stat_end(), ha_innobase::extra(HA_EXTRA_IGNORE_INSERT),
ha_innobase::external_lock(): Invoke trx_t::end_bulk_insert() so that
the next statement will not be covered by table-level undo logging.
ReadView::changes_visible(trx_id_t) const: New accessor for the case
where the trx_id_t is not read from a potentially corrupted index page
but directly from the memory. In this case, we can skip a sanity check.
row_sel(), row_sel_try_search_shortcut(), row_search_mvcc():
row_sel_try_search_shortcut_for_mysql(),
row_merge_read_clustered_index(): Check dict_table_t::bulk_trx_id.
row_sel_clust_sees(): Replaces lock_clust_rec_cons_read_sees().
lock_sec_rec_cons_read_sees(): Replaced with lower-level code.
btr_root_page_init(): Refactored from btr_create().
dict_index_t::clear(), dict_table_t::clear(): Empty an index or table,
for the ROLLBACK of an INSERT operation.
ROW_T_EMPTY, ROW_OP_EMPTY: Note a concurrent ROLLBACK of an INSERT
into an empty table.
This is joint work with Thirunarayanan Balathandayuthapani,
who created a working prototype.
Thanks to Matthias Leich for extensive testing.
2021-01-25 18:41:27 +02:00
|
|
|
/** Create a table X lock object for a resurrected TRX_UNDO_EMPTY transaction.
|
|
|
|
@param table table to be X-locked
|
|
|
|
@param trx transaction */
|
|
|
|
void lock_table_x_resurrect(dict_table_t *table, trx_t *trx);
|
|
|
|
|
|
|
|
/** Release a table X lock after rolling back an insert into an empty table
|
|
|
|
(which was covered by a TRX_UNDO_EMPTY record).
|
|
|
|
@param table table to be X-unlocked
|
|
|
|
@param trx transaction */
|
|
|
|
void lock_table_x_unlock(dict_table_t *table, trx_t *trx);
|
|
|
|
|
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
|
|
|
|
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
|
MDEV-24671: Replace lock_wait_timeout_task with mysql_cond_timedwait()
lock_wait(): Replaces lock_wait_suspend_thread(). Wait for the lock to
be granted or the transaction to be killed using mysql_cond_timedwait()
or mysql_cond_wait().
lock_wait_end(): Replaces que_thr_end_lock_wait() and
lock_wait_release_thread_if_suspended().
lock_wait_timeout_task: Remove. The operating system kernel will
resume the mysql_cond_timedwait() in lock_wait(). An added benefit
is that innodb_lock_wait_timeout no longer has a 'jitter' of 1 second,
which was caused by this wake-up task waking up only once per second,
and then waking up any threads for which the timeout (which was only
measured in seconds) was exceeded.
innobase_kill_query(): Set trx->error_state=DB_INTERRUPTED,
so that a call trx_is_interrupted(trx) in lock_wait() can be avoided.
We will protect things more consistently with lock_sys.wait_mutex,
which will be moved below lock_sys.mutex in the latching order.
trx_lock_t::cond: Condition variable for !wait_lock, used with
lock_sys.wait_mutex.
srv_slot_t: Remove. Replaced by trx_lock_t::cond,
lock_grant_after_reset(): Merged to to lock_grant().
lock_rec_get_index_name(): Remove.
lock_sys_t: Introduce wait_pending, wait_count, wait_time, wait_time_max
that are protected by wait_mutex.
trx_lock_t::que_state: Remove.
que_thr_state_t: Remove QUE_THR_COMMAND_WAIT, QUE_THR_LOCK_WAIT.
que_thr_t: Remove is_active, start_running(), stop_no_error().
que_fork_t::n_active_thrs, trx_lock_t::n_active_thrs: Remove.
2021-01-26 16:39:56 +02:00
|
|
|
@param[in] now current my_hrtime_coarse() */
|
|
|
|
void lock_trx_print_wait_and_mvcc_state(FILE *file, const trx_t *trx,
|
|
|
|
my_hrtime_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 */
|
|
|
|
|
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 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 */
|
|
|
|
|
|
|
|
/*******************************************************************//**
|
|
|
|
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 */
|
|
|
|
|
MDEV-24671: Replace lock_wait_timeout_task with mysql_cond_timedwait()
lock_wait(): Replaces lock_wait_suspend_thread(). Wait for the lock to
be granted or the transaction to be killed using mysql_cond_timedwait()
or mysql_cond_wait().
lock_wait_end(): Replaces que_thr_end_lock_wait() and
lock_wait_release_thread_if_suspended().
lock_wait_timeout_task: Remove. The operating system kernel will
resume the mysql_cond_timedwait() in lock_wait(). An added benefit
is that innodb_lock_wait_timeout no longer has a 'jitter' of 1 second,
which was caused by this wake-up task waking up only once per second,
and then waking up any threads for which the timeout (which was only
measured in seconds) was exceeded.
innobase_kill_query(): Set trx->error_state=DB_INTERRUPTED,
so that a call trx_is_interrupted(trx) in lock_wait() can be avoided.
We will protect things more consistently with lock_sys.wait_mutex,
which will be moved below lock_sys.mutex in the latching order.
trx_lock_t::cond: Condition variable for !wait_lock, used with
lock_sys.wait_mutex.
srv_slot_t: Remove. Replaced by trx_lock_t::cond,
lock_grant_after_reset(): Merged to to lock_grant().
lock_rec_get_index_name(): Remove.
lock_sys_t: Introduce wait_pending, wait_count, wait_time, wait_time_max
that are protected by wait_mutex.
trx_lock_t::que_state: Remove.
que_thr_state_t: Remove QUE_THR_COMMAND_WAIT, QUE_THR_LOCK_WAIT.
que_thr_t: Remove is_active, start_running(), stop_no_error().
que_fork_t::n_active_thrs, trx_lock_t::n_active_thrs: Remove.
2021-01-26 16:39:56 +02:00
|
|
|
/** Wait for a lock to be released.
|
|
|
|
@retval DB_DEADLOCK if this transaction was chosen as the deadlock victim
|
|
|
|
@retval DB_INTERRUPTED if the execution was interrupted by the user
|
|
|
|
@retval DB_LOCK_WAIT_TIMEOUT if the lock wait timed out
|
|
|
|
@retval DB_SUCCESS if the lock was granted */
|
|
|
|
dberr_t lock_wait(que_thr_t *thr);
|
2014-02-26 19:11:54 +01:00
|
|
|
/*********************************************************************//**
|
|
|
|
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
|
|
|
};
|
|
|
|
|
|
|
|
/** The lock system struct */
|
2018-02-22 20:46:42 +04:00
|
|
|
class lock_sys_t
|
|
|
|
{
|
|
|
|
bool m_initialised;
|
|
|
|
|
2020-12-04 16:18:04 +02:00
|
|
|
/** mutex proteting the locks */
|
|
|
|
MY_ALIGNED(CACHE_LINE_SIZE) mysql_mutex_t mutex;
|
2020-12-04 19:02:58 +02:00
|
|
|
public:
|
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;
|
2021-02-04 16:38:07 +02:00
|
|
|
/** number of deadlocks detected; protected by mutex */
|
|
|
|
ulint deadlocks;
|
2016-08-12 11:17:45 +03:00
|
|
|
|
MDEV-24671: Replace lock_wait_timeout_task with mysql_cond_timedwait()
lock_wait(): Replaces lock_wait_suspend_thread(). Wait for the lock to
be granted or the transaction to be killed using mysql_cond_timedwait()
or mysql_cond_wait().
lock_wait_end(): Replaces que_thr_end_lock_wait() and
lock_wait_release_thread_if_suspended().
lock_wait_timeout_task: Remove. The operating system kernel will
resume the mysql_cond_timedwait() in lock_wait(). An added benefit
is that innodb_lock_wait_timeout no longer has a 'jitter' of 1 second,
which was caused by this wake-up task waking up only once per second,
and then waking up any threads for which the timeout (which was only
measured in seconds) was exceeded.
innobase_kill_query(): Set trx->error_state=DB_INTERRUPTED,
so that a call trx_is_interrupted(trx) in lock_wait() can be avoided.
We will protect things more consistently with lock_sys.wait_mutex,
which will be moved below lock_sys.mutex in the latching order.
trx_lock_t::cond: Condition variable for !wait_lock, used with
lock_sys.wait_mutex.
srv_slot_t: Remove. Replaced by trx_lock_t::cond,
lock_grant_after_reset(): Merged to to lock_grant().
lock_rec_get_index_name(): Remove.
lock_sys_t: Introduce wait_pending, wait_count, wait_time, wait_time_max
that are protected by wait_mutex.
trx_lock_t::que_state: Remove.
que_thr_state_t: Remove QUE_THR_COMMAND_WAIT, QUE_THR_LOCK_WAIT.
que_thr_t: Remove is_active, start_running(), stop_no_error().
que_fork_t::n_active_thrs, trx_lock_t::n_active_thrs: Remove.
2021-01-26 16:39:56 +02:00
|
|
|
/** mutex covering lock waits; @see trx_lock_t::wait_lock */
|
2020-12-04 16:18:04 +02:00
|
|
|
MY_ALIGNED(CACHE_LINE_SIZE) mysql_mutex_t wait_mutex;
|
MDEV-24671: Replace lock_wait_timeout_task with mysql_cond_timedwait()
lock_wait(): Replaces lock_wait_suspend_thread(). Wait for the lock to
be granted or the transaction to be killed using mysql_cond_timedwait()
or mysql_cond_wait().
lock_wait_end(): Replaces que_thr_end_lock_wait() and
lock_wait_release_thread_if_suspended().
lock_wait_timeout_task: Remove. The operating system kernel will
resume the mysql_cond_timedwait() in lock_wait(). An added benefit
is that innodb_lock_wait_timeout no longer has a 'jitter' of 1 second,
which was caused by this wake-up task waking up only once per second,
and then waking up any threads for which the timeout (which was only
measured in seconds) was exceeded.
innobase_kill_query(): Set trx->error_state=DB_INTERRUPTED,
so that a call trx_is_interrupted(trx) in lock_wait() can be avoided.
We will protect things more consistently with lock_sys.wait_mutex,
which will be moved below lock_sys.mutex in the latching order.
trx_lock_t::cond: Condition variable for !wait_lock, used with
lock_sys.wait_mutex.
srv_slot_t: Remove. Replaced by trx_lock_t::cond,
lock_grant_after_reset(): Merged to to lock_grant().
lock_rec_get_index_name(): Remove.
lock_sys_t: Introduce wait_pending, wait_count, wait_time, wait_time_max
that are protected by wait_mutex.
trx_lock_t::que_state: Remove.
que_thr_state_t: Remove QUE_THR_COMMAND_WAIT, QUE_THR_LOCK_WAIT.
que_thr_t: Remove is_active, start_running(), stop_no_error().
que_fork_t::n_active_thrs, trx_lock_t::n_active_thrs: Remove.
2021-01-26 16:39:56 +02:00
|
|
|
private:
|
|
|
|
/** Pending number of lock waits; protected by wait_mutex */
|
|
|
|
ulint wait_pending;
|
|
|
|
/** Cumulative number of lock waits; protected by wait_mutex */
|
|
|
|
ulint wait_count;
|
|
|
|
/** Cumulative wait time; protected by wait_mutex */
|
|
|
|
ulint wait_time;
|
|
|
|
/** Longest wait time; protected by wait_mutex */
|
|
|
|
ulint wait_time_max;
|
|
|
|
public:
|
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; }
|
|
|
|
|
2020-12-04 19:02:58 +02:00
|
|
|
#ifdef HAVE_PSI_MUTEX_INTERFACE
|
|
|
|
/** Try to acquire lock_sys.mutex */
|
|
|
|
ATTRIBUTE_NOINLINE int mutex_trylock();
|
|
|
|
/** Acquire lock_sys.mutex */
|
|
|
|
ATTRIBUTE_NOINLINE void mutex_lock();
|
|
|
|
/** Release lock_sys.mutex */
|
|
|
|
ATTRIBUTE_NOINLINE void mutex_unlock();
|
|
|
|
#else
|
|
|
|
/** Try to acquire lock_sys.mutex */
|
|
|
|
int mutex_trylock() { return mysql_mutex_trylock(&mutex); }
|
|
|
|
/** Aqcuire lock_sys.mutex */
|
2020-12-15 17:45:19 +02:00
|
|
|
void mutex_lock() { mysql_mutex_lock(&mutex); }
|
2020-12-04 19:02:58 +02:00
|
|
|
/** Release lock_sys.mutex */
|
|
|
|
void mutex_unlock() { mysql_mutex_unlock(&mutex); }
|
|
|
|
#endif
|
|
|
|
/** Assert that mutex_lock() has been invoked */
|
|
|
|
void mutex_assert_locked() const { mysql_mutex_assert_owner(&mutex); }
|
|
|
|
/** Assert that mutex_lock() has not been invoked */
|
|
|
|
void mutex_assert_unlocked() const { mysql_mutex_assert_not_owner(&mutex); }
|
|
|
|
|
|
|
|
/** Wait for a lock to be granted */
|
|
|
|
void wait_lock(lock_t **lock, mysql_cond_t *cond)
|
|
|
|
{ while (*lock) mysql_cond_wait(cond, &mutex); }
|
2018-02-22 20:46:42 +04:00
|
|
|
|
|
|
|
/**
|
|
|
|
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
|
|
|
|
MDEV-24671: Replace lock_wait_timeout_task with mysql_cond_timedwait()
lock_wait(): Replaces lock_wait_suspend_thread(). Wait for the lock to
be granted or the transaction to be killed using mysql_cond_timedwait()
or mysql_cond_wait().
lock_wait_end(): Replaces que_thr_end_lock_wait() and
lock_wait_release_thread_if_suspended().
lock_wait_timeout_task: Remove. The operating system kernel will
resume the mysql_cond_timedwait() in lock_wait(). An added benefit
is that innodb_lock_wait_timeout no longer has a 'jitter' of 1 second,
which was caused by this wake-up task waking up only once per second,
and then waking up any threads for which the timeout (which was only
measured in seconds) was exceeded.
innobase_kill_query(): Set trx->error_state=DB_INTERRUPTED,
so that a call trx_is_interrupted(trx) in lock_wait() can be avoided.
We will protect things more consistently with lock_sys.wait_mutex,
which will be moved below lock_sys.mutex in the latching order.
trx_lock_t::cond: Condition variable for !wait_lock, used with
lock_sys.wait_mutex.
srv_slot_t: Remove. Replaced by trx_lock_t::cond,
lock_grant_after_reset(): Merged to to lock_grant().
lock_rec_get_index_name(): Remove.
lock_sys_t: Introduce wait_pending, wait_count, wait_time, wait_time_max
that are protected by wait_mutex.
trx_lock_t::que_state: Remove.
que_thr_state_t: Remove QUE_THR_COMMAND_WAIT, QUE_THR_LOCK_WAIT.
que_thr_t: Remove is_active, start_running(), stop_no_error().
que_fork_t::n_active_thrs, trx_lock_t::n_active_thrs: Remove.
2021-01-26 16:39:56 +02:00
|
|
|
|
|
|
|
/** Note that a record lock wait started */
|
|
|
|
inline void wait_start();
|
|
|
|
|
|
|
|
/** Note that a record lock wait resumed */
|
|
|
|
inline void wait_resume(THD *thd, my_hrtime_t start, my_hrtime_t now);
|
|
|
|
|
|
|
|
/** @return pending number of lock waits */
|
|
|
|
ulint get_wait_pending() const { return wait_pending; }
|
|
|
|
/** @return cumulative number of lock waits */
|
|
|
|
ulint get_wait_cumulative() const { return wait_count; }
|
|
|
|
/** Cumulative wait time; protected by wait_mutex */
|
|
|
|
ulint get_wait_time_cumulative() const { return wait_time; }
|
|
|
|
/** Longest wait time; protected by wait_mutex */
|
|
|
|
ulint get_wait_time_max() const { return wait_time_max; }
|
|
|
|
|
2020-09-11 15:55:30 +03:00
|
|
|
/** @return the hash value for a page address */
|
|
|
|
ulint hash(const page_id_t id) const
|
2020-12-04 16:18:04 +02:00
|
|
|
{ mysql_mutex_assert_owner(&mutex); return rec_hash.calc_hash(id.fold()); }
|
2020-09-11 15:55:30 +03:00
|
|
|
|
|
|
|
/** 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
|
|
|
};
|
|
|
|
|
2021-02-04 16:38:07 +02:00
|
|
|
/** The lock system */
|
|
|
|
extern lock_sys_t lock_sys;
|
|
|
|
|
|
|
|
/** lock_sys.mutex guard */
|
|
|
|
struct LockMutexGuard
|
|
|
|
{
|
|
|
|
LockMutexGuard() { lock_sys.mutex_lock(); }
|
|
|
|
~LockMutexGuard() { lock_sys.mutex_unlock(); }
|
|
|
|
};
|
|
|
|
|
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!
|
|
|
|
@return created lock */
|
|
|
|
UNIV_INLINE
|
|
|
|
lock_t*
|
|
|
|
lock_rec_create(
|
|
|
|
/*============*/
|
|
|
|
#ifdef WITH_WSREP
|
|
|
|
lock_t* c_lock, /*!< conflicting lock */
|
|
|
|
que_thr_t* thr, /*!< thread owning trx */
|
|
|
|
#endif
|
2021-01-26 16:28:02 +02:00
|
|
|
unsigned type_mode,/*!< in: lock mode and wait flag */
|
2018-03-11 23:34:23 +02:00
|
|
|
const buf_block_t* block, /*!< in: buffer block containing
|
|
|
|
the record */
|
|
|
|
ulint heap_no,/*!< in: heap number of the record */
|
|
|
|
dict_index_t* index, /*!< in: index of record */
|
|
|
|
trx_t* trx, /*!< in,out: transaction */
|
|
|
|
bool caller_owns_trx_mutex);
|
|
|
|
/*!< in: true if caller owns
|
|
|
|
trx mutex */
|
|
|
|
|
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.
|
2021-01-26 16:28:02 +02:00
|
|
|
@param[in] type_mode lock mode and wait flag
|
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
|
|
|
|
@return created lock */
|
|
|
|
lock_t*
|
|
|
|
lock_rec_create_low(
|
|
|
|
#ifdef WITH_WSREP
|
|
|
|
lock_t* c_lock, /*!< conflicting lock */
|
|
|
|
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,
|
|
|
|
bool holds_trx_mutex);
|
|
|
|
/** 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
|
2021-02-04 16:38:07 +02:00
|
|
|
@retval DB_DEADLOCK if this transaction was chosen as the victim */
|
2018-03-11 23:34:23 +02:00
|
|
|
dberr_t
|
|
|
|
lock_rec_enqueue_waiting(
|
|
|
|
#ifdef WITH_WSREP
|
|
|
|
lock_t* c_lock, /*!< conflicting lock */
|
|
|
|
#endif
|
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 */
|
|
|
|
|
MDEV-24671: Replace lock_wait_timeout_task with mysql_cond_timedwait()
lock_wait(): Replaces lock_wait_suspend_thread(). Wait for the lock to
be granted or the transaction to be killed using mysql_cond_timedwait()
or mysql_cond_wait().
lock_wait_end(): Replaces que_thr_end_lock_wait() and
lock_wait_release_thread_if_suspended().
lock_wait_timeout_task: Remove. The operating system kernel will
resume the mysql_cond_timedwait() in lock_wait(). An added benefit
is that innodb_lock_wait_timeout no longer has a 'jitter' of 1 second,
which was caused by this wake-up task waking up only once per second,
and then waking up any threads for which the timeout (which was only
measured in seconds) was exceeded.
innobase_kill_query(): Set trx->error_state=DB_INTERRUPTED,
so that a call trx_is_interrupted(trx) in lock_wait() can be avoided.
We will protect things more consistently with lock_sys.wait_mutex,
which will be moved below lock_sys.mutex in the latching order.
trx_lock_t::cond: Condition variable for !wait_lock, used with
lock_sys.wait_mutex.
srv_slot_t: Remove. Replaced by trx_lock_t::cond,
lock_grant_after_reset(): Merged to to lock_grant().
lock_rec_get_index_name(): Remove.
lock_sys_t: Introduce wait_pending, wait_count, wait_time, wait_time_max
that are protected by wait_mutex.
trx_lock_t::que_state: Remove.
que_thr_state_t: Remove QUE_THR_COMMAND_WAIT, QUE_THR_LOCK_WAIT.
que_thr_t: Remove is_active, start_running(), stop_no_error().
que_fork_t::n_active_thrs, trx_lock_t::n_active_thrs: Remove.
2021-01-26 16:39:56 +02:00
|
|
|
/** Cancel a waiting lock request and release possibly waiting transactions */
|
|
|
|
void lock_cancel_waiting_and_release(lock_t *lock);
|
2016-09-06 09:43:16 +03:00
|
|
|
|
2014-02-26 19:11:54 +01:00
|
|
|
#include "lock0lock.ic"
|
|
|
|
|
|
|
|
#endif
|