2014-02-26 19:11:54 +01:00
|
|
|
/*****************************************************************************
|
|
|
|
|
2016-04-22 10:50:45 +02:00
|
|
|
Copyright (c) 1996, 2016, Oracle and/or its affiliates. All Rights Reserved.
|
MDEV-11831 Make InnoDB mini-transaction memo checks stricter
InnoDB keeps track of buffer-fixed buf_block_t or acquired rw_lock_t
within a mini-transaction. There are some memo_contains assertions
in the code that document when certain blocks or rw_locks must be held.
But, these assertions only check the mini-transaction memo, not the fact
whether the rw_lock_t are actually being held by the caller.
btr_pcur_store_position(): Remove #ifdef, and assert that the block
is always buffer-fixed.
rtr_pcur_getnext_from_path(), rtr_pcur_open_low(),
ibuf_rec_get_page_no_func(), ibuf_rec_get_space_func(),
ibuf_rec_get_info_func(), ibuf_rec_get_op_type_func(),
ibuf_build_entry_from_ibuf_rec_func(), ibuf_rec_get_volume_func(),
ibuf_get_merge_page_nos_func(), ibuf_get_volume_buffered_count_func()
ibuf_get_entry_counter_low_func(), page_set_ssn_id(),
row_vers_old_has_index_entry(), row_vers_build_for_consistent_read(),
row_vers_build_for_semi_consistent_read(),
trx_undo_prev_version_build():
Make use of mtr_memo_contains_page_flagged().
mtr_t::memo_contains(): Take a const memo. Assert rw_lock_own().
FindPage, FlaggedCheck: Assert rw_lock_own_flagged().
2017-01-18 12:53:35 +02:00
|
|
|
Copyright (c) 2016, 2017, 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.,
|
|
|
|
51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA
|
|
|
|
|
|
|
|
*****************************************************************************/
|
|
|
|
|
|
|
|
/**************************************************//**
|
|
|
|
@file btr/btr0pcur.cc
|
|
|
|
The index tree persistent cursor
|
|
|
|
|
|
|
|
Created 2/23/1996 Heikki Tuuri
|
|
|
|
*******************************************************/
|
|
|
|
|
|
|
|
#include "btr0pcur.h"
|
|
|
|
#include "ut0byte.h"
|
|
|
|
#include "rem0cmp.h"
|
|
|
|
#include "trx0trx.h"
|
|
|
|
|
|
|
|
/**************************************************************//**
|
|
|
|
Allocates memory for a persistent cursor object and initializes the cursor.
|
2016-08-12 11:17:45 +03:00
|
|
|
@return own: persistent cursor */
|
2014-02-26 19:11:54 +01:00
|
|
|
btr_pcur_t*
|
|
|
|
btr_pcur_create_for_mysql(void)
|
|
|
|
/*============================*/
|
|
|
|
{
|
|
|
|
btr_pcur_t* pcur;
|
2016-08-12 11:17:45 +03:00
|
|
|
DBUG_ENTER("btr_pcur_create_for_mysql");
|
2014-02-26 19:11:54 +01:00
|
|
|
|
2016-08-12 11:17:45 +03:00
|
|
|
pcur = (btr_pcur_t*) ut_malloc_nokey(sizeof(btr_pcur_t));
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
pcur->btr_cur.index = NULL;
|
|
|
|
btr_pcur_init(pcur);
|
|
|
|
|
2016-08-12 11:17:45 +03:00
|
|
|
DBUG_PRINT("btr_pcur_create_for_mysql", ("pcur: %p", pcur));
|
|
|
|
DBUG_RETURN(pcur);
|
2014-02-26 19:11:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**************************************************************//**
|
|
|
|
Resets a persistent cursor object, freeing ::old_rec_buf if it is
|
|
|
|
allocated and resetting the other members to their initial values. */
|
|
|
|
void
|
|
|
|
btr_pcur_reset(
|
|
|
|
/*===========*/
|
|
|
|
btr_pcur_t* cursor) /*!< in, out: persistent cursor */
|
|
|
|
{
|
2016-08-12 11:17:45 +03:00
|
|
|
btr_pcur_free(cursor);
|
|
|
|
cursor->old_rec_buf = NULL;
|
2014-02-26 19:11:54 +01:00
|
|
|
cursor->btr_cur.index = NULL;
|
|
|
|
cursor->btr_cur.page_cur.rec = NULL;
|
|
|
|
cursor->old_rec = NULL;
|
|
|
|
cursor->old_n_fields = 0;
|
2016-08-12 11:17:45 +03:00
|
|
|
cursor->old_stored = false;
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
cursor->latch_mode = BTR_NO_LATCHES;
|
|
|
|
cursor->pos_state = BTR_PCUR_NOT_POSITIONED;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**************************************************************//**
|
|
|
|
Frees the memory for a persistent cursor object. */
|
|
|
|
void
|
|
|
|
btr_pcur_free_for_mysql(
|
|
|
|
/*====================*/
|
|
|
|
btr_pcur_t* cursor) /*!< in, own: persistent cursor */
|
|
|
|
{
|
2016-08-12 11:17:45 +03:00
|
|
|
DBUG_ENTER("btr_pcur_free_for_mysql");
|
|
|
|
DBUG_PRINT("btr_pcur_free_for_mysql", ("pcur: %p", cursor));
|
|
|
|
|
|
|
|
btr_pcur_free(cursor);
|
|
|
|
ut_free(cursor);
|
|
|
|
DBUG_VOID_RETURN;
|
2014-02-26 19:11:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**************************************************************//**
|
|
|
|
The position of the cursor is stored by taking an initial segment of the
|
|
|
|
record the cursor is positioned on, before, or after, and copying it to the
|
|
|
|
cursor data structure, or just setting a flag if the cursor id before the
|
|
|
|
first in an EMPTY tree, or after the last in an EMPTY tree. NOTE that the
|
|
|
|
page where the cursor is positioned must not be empty if the index tree is
|
|
|
|
not totally empty! */
|
|
|
|
void
|
|
|
|
btr_pcur_store_position(
|
|
|
|
/*====================*/
|
|
|
|
btr_pcur_t* cursor, /*!< in: persistent cursor */
|
|
|
|
mtr_t* mtr) /*!< in: mtr */
|
|
|
|
{
|
|
|
|
page_cur_t* page_cursor;
|
|
|
|
buf_block_t* block;
|
|
|
|
rec_t* rec;
|
|
|
|
dict_index_t* index;
|
|
|
|
page_t* page;
|
|
|
|
ulint offs;
|
|
|
|
|
2014-02-26 19:23:04 +01:00
|
|
|
ut_ad(cursor->pos_state == BTR_PCUR_IS_POSITIONED);
|
2014-02-26 19:11:54 +01:00
|
|
|
ut_ad(cursor->latch_mode != BTR_NO_LATCHES);
|
|
|
|
|
|
|
|
block = btr_pcur_get_block(cursor);
|
|
|
|
index = btr_cur_get_index(btr_pcur_get_btr_cur(cursor));
|
|
|
|
|
|
|
|
page_cursor = btr_pcur_get_page_cur(cursor);
|
|
|
|
|
|
|
|
rec = page_cur_get_rec(page_cursor);
|
|
|
|
page = page_align(rec);
|
|
|
|
offs = page_offset(rec);
|
|
|
|
|
MDEV-11831 Make InnoDB mini-transaction memo checks stricter
InnoDB keeps track of buffer-fixed buf_block_t or acquired rw_lock_t
within a mini-transaction. There are some memo_contains assertions
in the code that document when certain blocks or rw_locks must be held.
But, these assertions only check the mini-transaction memo, not the fact
whether the rw_lock_t are actually being held by the caller.
btr_pcur_store_position(): Remove #ifdef, and assert that the block
is always buffer-fixed.
rtr_pcur_getnext_from_path(), rtr_pcur_open_low(),
ibuf_rec_get_page_no_func(), ibuf_rec_get_space_func(),
ibuf_rec_get_info_func(), ibuf_rec_get_op_type_func(),
ibuf_build_entry_from_ibuf_rec_func(), ibuf_rec_get_volume_func(),
ibuf_get_merge_page_nos_func(), ibuf_get_volume_buffered_count_func()
ibuf_get_entry_counter_low_func(), page_set_ssn_id(),
row_vers_old_has_index_entry(), row_vers_build_for_consistent_read(),
row_vers_build_for_semi_consistent_read(),
trx_undo_prev_version_build():
Make use of mtr_memo_contains_page_flagged().
mtr_t::memo_contains(): Take a const memo. Assert rw_lock_own().
FindPage, FlaggedCheck: Assert rw_lock_own_flagged().
2017-01-18 12:53:35 +02:00
|
|
|
ut_ad(block->page.buf_fix_count);
|
|
|
|
/* For spatial index, when we do positioning on parent
|
|
|
|
buffer if necessary, it might not hold latches, but the
|
|
|
|
tree must be locked to prevent change on the page */
|
|
|
|
ut_ad(mtr_memo_contains_flagged(mtr, block,
|
|
|
|
MTR_MEMO_PAGE_S_FIX
|
|
|
|
| MTR_MEMO_PAGE_X_FIX)
|
|
|
|
|| (dict_index_is_spatial(index)
|
|
|
|
&& mtr_memo_contains_flagged(
|
|
|
|
mtr, dict_index_get_lock(index),
|
|
|
|
MTR_MEMO_X_LOCK | MTR_MEMO_SX_LOCK)));
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
if (page_is_empty(page)) {
|
|
|
|
/* It must be an empty index tree; NOTE that in this case
|
|
|
|
we do not store the modify_clock, but always do a search
|
|
|
|
if we restore the cursor position */
|
|
|
|
|
|
|
|
ut_a(btr_page_get_next(page, mtr) == FIL_NULL);
|
|
|
|
ut_a(btr_page_get_prev(page, mtr) == FIL_NULL);
|
|
|
|
ut_ad(page_is_leaf(page));
|
|
|
|
ut_ad(page_get_page_no(page) == index->page);
|
|
|
|
|
2016-08-12 11:17:45 +03:00
|
|
|
cursor->old_stored = true;
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
if (page_rec_is_supremum_low(offs)) {
|
|
|
|
|
|
|
|
cursor->rel_pos = BTR_PCUR_AFTER_LAST_IN_TREE;
|
|
|
|
} else {
|
|
|
|
cursor->rel_pos = BTR_PCUR_BEFORE_FIRST_IN_TREE;
|
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (page_rec_is_supremum_low(offs)) {
|
|
|
|
|
|
|
|
rec = page_rec_get_prev(rec);
|
|
|
|
|
|
|
|
cursor->rel_pos = BTR_PCUR_AFTER;
|
|
|
|
|
|
|
|
} else if (page_rec_is_infimum_low(offs)) {
|
|
|
|
|
|
|
|
rec = page_rec_get_next(rec);
|
|
|
|
|
|
|
|
cursor->rel_pos = BTR_PCUR_BEFORE;
|
|
|
|
} else {
|
|
|
|
cursor->rel_pos = BTR_PCUR_ON;
|
|
|
|
}
|
|
|
|
|
2016-08-12 11:17:45 +03:00
|
|
|
cursor->old_stored = true;
|
2014-02-26 19:11:54 +01:00
|
|
|
cursor->old_rec = dict_index_copy_rec_order_prefix(
|
|
|
|
index, rec, &cursor->old_n_fields,
|
|
|
|
&cursor->old_rec_buf, &cursor->buf_size);
|
|
|
|
|
|
|
|
cursor->block_when_stored = block;
|
2016-08-12 11:17:45 +03:00
|
|
|
|
|
|
|
/* Function try to check if block is S/X latch. */
|
2014-02-26 19:11:54 +01:00
|
|
|
cursor->modify_clock = buf_block_get_modify_clock(block);
|
2016-08-12 11:17:45 +03:00
|
|
|
cursor->withdraw_clock = buf_withdraw_clock;
|
2014-02-26 19:11:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**************************************************************//**
|
|
|
|
Copies the stored position of a pcur to another pcur. */
|
|
|
|
void
|
|
|
|
btr_pcur_copy_stored_position(
|
|
|
|
/*==========================*/
|
|
|
|
btr_pcur_t* pcur_receive, /*!< in: pcur which will receive the
|
|
|
|
position info */
|
|
|
|
btr_pcur_t* pcur_donate) /*!< in: pcur from which the info is
|
|
|
|
copied */
|
|
|
|
{
|
2016-08-12 11:17:45 +03:00
|
|
|
ut_free(pcur_receive->old_rec_buf);
|
2014-02-26 19:11:54 +01:00
|
|
|
ut_memcpy(pcur_receive, pcur_donate, sizeof(btr_pcur_t));
|
|
|
|
|
|
|
|
if (pcur_donate->old_rec_buf) {
|
|
|
|
|
|
|
|
pcur_receive->old_rec_buf = (byte*)
|
2016-08-12 11:17:45 +03:00
|
|
|
ut_malloc_nokey(pcur_donate->buf_size);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
ut_memcpy(pcur_receive->old_rec_buf, pcur_donate->old_rec_buf,
|
|
|
|
pcur_donate->buf_size);
|
|
|
|
pcur_receive->old_rec = pcur_receive->old_rec_buf
|
|
|
|
+ (pcur_donate->old_rec - pcur_donate->old_rec_buf);
|
|
|
|
}
|
|
|
|
|
|
|
|
pcur_receive->old_n_fields = pcur_donate->old_n_fields;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**************************************************************//**
|
|
|
|
Restores the stored position of a persistent cursor bufferfixing the page and
|
|
|
|
obtaining the specified latches. If the cursor position was saved when the
|
|
|
|
(1) cursor was positioned on a user record: this function restores the position
|
|
|
|
to the last record LESS OR EQUAL to the stored record;
|
|
|
|
(2) cursor was positioned on a page infimum record: restores the position to
|
|
|
|
the last record LESS than the user record which was the successor of the page
|
|
|
|
infimum;
|
|
|
|
(3) cursor was positioned on the page supremum: restores to the first record
|
|
|
|
GREATER than the user record which was the predecessor of the supremum.
|
|
|
|
(4) cursor was positioned before the first or after the last in an empty tree:
|
|
|
|
restores to before first or after the last in the tree.
|
|
|
|
@return TRUE if the cursor position was stored when it was on a user
|
|
|
|
record and it can be restored on a user record whose ordering fields
|
|
|
|
are identical to the ones of the original user record */
|
|
|
|
ibool
|
|
|
|
btr_pcur_restore_position_func(
|
|
|
|
/*===========================*/
|
|
|
|
ulint latch_mode, /*!< in: BTR_SEARCH_LEAF, ... */
|
|
|
|
btr_pcur_t* cursor, /*!< in: detached persistent cursor */
|
|
|
|
const char* file, /*!< in: file name */
|
2017-03-01 08:27:39 +02:00
|
|
|
unsigned line, /*!< in: line where called */
|
2014-02-26 19:11:54 +01:00
|
|
|
mtr_t* mtr) /*!< in: mtr */
|
|
|
|
{
|
|
|
|
dict_index_t* index;
|
|
|
|
dtuple_t* tuple;
|
2016-08-12 11:17:45 +03:00
|
|
|
page_cur_mode_t mode;
|
|
|
|
page_cur_mode_t old_mode;
|
2014-02-26 19:11:54 +01:00
|
|
|
mem_heap_t* heap;
|
|
|
|
|
2016-08-12 11:17:45 +03:00
|
|
|
ut_ad(mtr->is_active());
|
|
|
|
//ut_ad(cursor->old_stored);
|
2014-02-26 19:23:04 +01:00
|
|
|
ut_ad(cursor->pos_state == BTR_PCUR_WAS_POSITIONED
|
|
|
|
|| cursor->pos_state == BTR_PCUR_IS_POSITIONED);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
index = btr_cur_get_index(btr_pcur_get_btr_cur(cursor));
|
|
|
|
|
|
|
|
if (UNIV_UNLIKELY
|
|
|
|
(cursor->rel_pos == BTR_PCUR_AFTER_LAST_IN_TREE
|
|
|
|
|| cursor->rel_pos == BTR_PCUR_BEFORE_FIRST_IN_TREE)) {
|
2016-08-12 11:17:45 +03:00
|
|
|
dberr_t err = DB_SUCCESS;
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
/* In these cases we do not try an optimistic restoration,
|
|
|
|
but always do a search */
|
|
|
|
|
2016-08-12 11:17:45 +03:00
|
|
|
err = btr_cur_open_at_index_side(
|
2014-02-26 19:11:54 +01:00
|
|
|
cursor->rel_pos == BTR_PCUR_BEFORE_FIRST_IN_TREE,
|
|
|
|
index, latch_mode,
|
|
|
|
btr_pcur_get_btr_cur(cursor), 0, mtr);
|
|
|
|
|
2016-08-12 11:17:45 +03:00
|
|
|
if (err != DB_SUCCESS) {
|
|
|
|
ib::warn() << " Error code: " << err
|
|
|
|
<< " btr_pcur_restore_position_func "
|
|
|
|
<< " called from file: "
|
|
|
|
<< file << " line: " << line
|
|
|
|
<< " table: " << index->table->name
|
|
|
|
<< " index: " << index->name;
|
|
|
|
}
|
|
|
|
|
|
|
|
cursor->latch_mode =
|
|
|
|
BTR_LATCH_MODE_WITHOUT_INTENTION(latch_mode);
|
2014-02-26 19:11:54 +01:00
|
|
|
cursor->pos_state = BTR_PCUR_IS_POSITIONED;
|
|
|
|
cursor->block_when_stored = btr_pcur_get_block(cursor);
|
|
|
|
|
|
|
|
return(FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
ut_a(cursor->old_rec);
|
|
|
|
ut_a(cursor->old_n_fields);
|
|
|
|
|
2016-12-05 21:04:30 +02:00
|
|
|
switch (latch_mode) {
|
|
|
|
case BTR_SEARCH_LEAF:
|
|
|
|
case BTR_MODIFY_LEAF:
|
|
|
|
case BTR_SEARCH_PREV:
|
|
|
|
case BTR_MODIFY_PREV:
|
2014-02-26 19:23:04 +01:00
|
|
|
/* Try optimistic restoration. */
|
2014-02-26 19:11:54 +01:00
|
|
|
|
2016-08-12 11:17:45 +03:00
|
|
|
if (!buf_pool_is_obsolete(cursor->withdraw_clock)
|
|
|
|
&& btr_cur_optimistic_latch_leaves(
|
|
|
|
cursor->block_when_stored, cursor->modify_clock,
|
|
|
|
&latch_mode, btr_pcur_get_btr_cur(cursor),
|
|
|
|
file, line, mtr)) {
|
|
|
|
|
2014-02-26 19:11:54 +01:00
|
|
|
cursor->pos_state = BTR_PCUR_IS_POSITIONED;
|
2014-02-26 19:23:04 +01:00
|
|
|
cursor->latch_mode = latch_mode;
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
buf_block_dbg_add_level(
|
|
|
|
btr_pcur_get_block(cursor),
|
|
|
|
dict_index_is_ibuf(index)
|
|
|
|
? SYNC_IBUF_TREE_NODE : SYNC_TREE_NODE);
|
|
|
|
|
|
|
|
if (cursor->rel_pos == BTR_PCUR_ON) {
|
|
|
|
#ifdef UNIV_DEBUG
|
|
|
|
const rec_t* rec;
|
|
|
|
const ulint* offsets1;
|
|
|
|
const ulint* offsets2;
|
|
|
|
rec = btr_pcur_get_rec(cursor);
|
|
|
|
|
|
|
|
heap = mem_heap_create(256);
|
|
|
|
offsets1 = rec_get_offsets(
|
|
|
|
cursor->old_rec, index, NULL,
|
|
|
|
cursor->old_n_fields, &heap);
|
|
|
|
offsets2 = rec_get_offsets(
|
|
|
|
rec, index, NULL,
|
|
|
|
cursor->old_n_fields, &heap);
|
|
|
|
|
|
|
|
ut_ad(!cmp_rec_rec(cursor->old_rec,
|
|
|
|
rec, offsets1, offsets2,
|
|
|
|
index));
|
|
|
|
mem_heap_free(heap);
|
|
|
|
#endif /* UNIV_DEBUG */
|
|
|
|
return(TRUE);
|
|
|
|
}
|
2014-02-26 19:23:04 +01:00
|
|
|
/* This is the same record as stored,
|
|
|
|
may need to be adjusted for BTR_PCUR_BEFORE/AFTER,
|
|
|
|
depending on search mode and direction. */
|
|
|
|
if (btr_pcur_is_on_user_rec(cursor)) {
|
|
|
|
cursor->pos_state
|
|
|
|
= BTR_PCUR_IS_POSITIONED_OPTIMISTIC;
|
|
|
|
}
|
2014-02-26 19:11:54 +01:00
|
|
|
return(FALSE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If optimistic restoration did not succeed, open the cursor anew */
|
|
|
|
|
|
|
|
heap = mem_heap_create(256);
|
|
|
|
|
|
|
|
tuple = dict_index_build_data_tuple(index, cursor->old_rec,
|
|
|
|
cursor->old_n_fields, heap);
|
|
|
|
|
|
|
|
/* Save the old search mode of the cursor */
|
|
|
|
old_mode = cursor->search_mode;
|
|
|
|
|
|
|
|
switch (cursor->rel_pos) {
|
|
|
|
case BTR_PCUR_ON:
|
|
|
|
mode = PAGE_CUR_LE;
|
|
|
|
break;
|
|
|
|
case BTR_PCUR_AFTER:
|
|
|
|
mode = PAGE_CUR_G;
|
|
|
|
break;
|
|
|
|
case BTR_PCUR_BEFORE:
|
|
|
|
mode = PAGE_CUR_L;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
ut_error;
|
2016-08-12 11:17:45 +03:00
|
|
|
mode = PAGE_CUR_UNSUPP;
|
2014-02-26 19:11:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
btr_pcur_open_with_no_init_func(index, tuple, mode, latch_mode,
|
|
|
|
cursor, 0, file, line, mtr);
|
|
|
|
|
|
|
|
/* Restore the old search mode */
|
|
|
|
cursor->search_mode = old_mode;
|
|
|
|
|
2016-08-12 11:17:45 +03:00
|
|
|
ut_ad(cursor->rel_pos == BTR_PCUR_ON
|
|
|
|
|| cursor->rel_pos == BTR_PCUR_BEFORE
|
|
|
|
|| cursor->rel_pos == BTR_PCUR_AFTER);
|
|
|
|
if (cursor->rel_pos == BTR_PCUR_ON
|
|
|
|
&& btr_pcur_is_on_user_rec(cursor)
|
|
|
|
&& !cmp_dtuple_rec(tuple, btr_pcur_get_rec(cursor),
|
|
|
|
rec_get_offsets(btr_pcur_get_rec(cursor),
|
|
|
|
index, NULL, ULINT_UNDEFINED, &heap))) {
|
|
|
|
|
|
|
|
/* We have to store the NEW value for the modify clock,
|
|
|
|
since the cursor can now be on a different page!
|
|
|
|
But we can retain the value of old_rec */
|
|
|
|
|
|
|
|
cursor->block_when_stored = btr_pcur_get_block(cursor);
|
|
|
|
cursor->modify_clock = buf_block_get_modify_clock(
|
|
|
|
cursor->block_when_stored);
|
|
|
|
cursor->old_stored = true;
|
|
|
|
cursor->withdraw_clock = buf_withdraw_clock;
|
|
|
|
|
|
|
|
mem_heap_free(heap);
|
|
|
|
|
|
|
|
return(TRUE);
|
2014-02-26 19:11:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
mem_heap_free(heap);
|
|
|
|
|
|
|
|
/* We have to store new position information, modify_clock etc.,
|
|
|
|
to the cursor because it can now be on a different page, the record
|
|
|
|
under it may have been removed, etc. */
|
|
|
|
|
|
|
|
btr_pcur_store_position(cursor, mtr);
|
|
|
|
|
|
|
|
return(FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*********************************************************//**
|
|
|
|
Moves the persistent cursor to the first record on the next page. Releases the
|
|
|
|
latch on the current page, and bufferunfixes it. Note that there must not be
|
|
|
|
modifications on the current page, as then the x-latch can be released only in
|
|
|
|
mtr_commit. */
|
|
|
|
void
|
|
|
|
btr_pcur_move_to_next_page(
|
|
|
|
/*=======================*/
|
|
|
|
btr_pcur_t* cursor, /*!< in: persistent cursor; must be on the
|
|
|
|
last record of the current page */
|
|
|
|
mtr_t* mtr) /*!< in: mtr */
|
|
|
|
{
|
|
|
|
ulint next_page_no;
|
|
|
|
page_t* page;
|
|
|
|
buf_block_t* next_block;
|
|
|
|
page_t* next_page;
|
2016-08-12 11:17:45 +03:00
|
|
|
ulint mode;
|
2014-02-26 19:11:54 +01:00
|
|
|
|
2014-02-26 19:23:04 +01:00
|
|
|
ut_ad(cursor->pos_state == BTR_PCUR_IS_POSITIONED);
|
2014-02-26 19:11:54 +01:00
|
|
|
ut_ad(cursor->latch_mode != BTR_NO_LATCHES);
|
|
|
|
ut_ad(btr_pcur_is_after_last_on_page(cursor));
|
|
|
|
|
2016-08-12 11:17:45 +03:00
|
|
|
cursor->old_stored = false;
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
page = btr_pcur_get_page(cursor);
|
|
|
|
next_page_no = btr_page_get_next(page, mtr);
|
|
|
|
|
|
|
|
ut_ad(next_page_no != FIL_NULL);
|
|
|
|
|
2016-08-12 11:17:45 +03:00
|
|
|
mode = cursor->latch_mode;
|
|
|
|
switch (mode) {
|
|
|
|
case BTR_SEARCH_TREE:
|
|
|
|
mode = BTR_SEARCH_LEAF;
|
|
|
|
break;
|
|
|
|
case BTR_MODIFY_TREE:
|
|
|
|
mode = BTR_MODIFY_LEAF;
|
|
|
|
}
|
|
|
|
|
|
|
|
buf_block_t* block = btr_pcur_get_block(cursor);
|
|
|
|
|
|
|
|
next_block = btr_block_get(
|
|
|
|
page_id_t(block->page.id.space(), next_page_no),
|
|
|
|
block->page.size, mode,
|
|
|
|
btr_pcur_get_btr_cur(cursor)->index, mtr);
|
|
|
|
|
2014-02-26 19:11:54 +01:00
|
|
|
next_page = buf_block_get_frame(next_block);
|
|
|
|
#ifdef UNIV_BTR_DEBUG
|
|
|
|
ut_a(page_is_comp(next_page) == page_is_comp(page));
|
|
|
|
ut_a(btr_page_get_prev(next_page, mtr)
|
2016-08-12 11:17:45 +03:00
|
|
|
== btr_pcur_get_block(cursor)->page.id.page_no());
|
2014-02-26 19:11:54 +01:00
|
|
|
#endif /* UNIV_BTR_DEBUG */
|
|
|
|
|
2016-08-12 11:17:45 +03:00
|
|
|
btr_leaf_page_release(btr_pcur_get_block(cursor), mode, mtr);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
page_cur_set_before_first(next_block, btr_pcur_get_page_cur(cursor));
|
|
|
|
|
2016-08-12 11:17:45 +03:00
|
|
|
ut_d(page_check_dir(next_page));
|
2014-02-26 19:11:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*********************************************************//**
|
|
|
|
Moves the persistent cursor backward if it is on the first record of the page.
|
|
|
|
Commits mtr. Note that to prevent a possible deadlock, the operation
|
|
|
|
first stores the position of the cursor, commits mtr, acquires the necessary
|
|
|
|
latches and restores the cursor position again before returning. The
|
|
|
|
alphabetical position of the cursor is guaranteed to be sensible on
|
|
|
|
return, but it may happen that the cursor is not positioned on the last
|
|
|
|
record of any page, because the structure of the tree may have changed
|
|
|
|
during the time when the cursor had no latches. */
|
2015-05-26 10:01:12 +03:00
|
|
|
static
|
2014-02-26 19:11:54 +01:00
|
|
|
void
|
|
|
|
btr_pcur_move_backward_from_page(
|
|
|
|
/*=============================*/
|
|
|
|
btr_pcur_t* cursor, /*!< in: persistent cursor, must be on the first
|
|
|
|
record of the current page */
|
|
|
|
mtr_t* mtr) /*!< in: mtr */
|
|
|
|
{
|
|
|
|
ulint prev_page_no;
|
|
|
|
page_t* page;
|
|
|
|
buf_block_t* prev_block;
|
|
|
|
ulint latch_mode;
|
|
|
|
ulint latch_mode2;
|
|
|
|
|
|
|
|
ut_ad(cursor->latch_mode != BTR_NO_LATCHES);
|
|
|
|
ut_ad(btr_pcur_is_before_first_on_page(cursor));
|
|
|
|
ut_ad(!btr_pcur_is_before_first_in_tree(cursor, mtr));
|
|
|
|
|
|
|
|
latch_mode = cursor->latch_mode;
|
|
|
|
|
|
|
|
if (latch_mode == BTR_SEARCH_LEAF) {
|
|
|
|
|
|
|
|
latch_mode2 = BTR_SEARCH_PREV;
|
|
|
|
|
|
|
|
} else if (latch_mode == BTR_MODIFY_LEAF) {
|
|
|
|
|
|
|
|
latch_mode2 = BTR_MODIFY_PREV;
|
|
|
|
} else {
|
|
|
|
latch_mode2 = 0; /* To eliminate compiler warning */
|
|
|
|
ut_error;
|
|
|
|
}
|
|
|
|
|
|
|
|
btr_pcur_store_position(cursor, mtr);
|
|
|
|
|
|
|
|
mtr_commit(mtr);
|
|
|
|
|
2016-08-12 11:17:45 +03:00
|
|
|
mtr_start(mtr);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
btr_pcur_restore_position(latch_mode2, cursor, mtr);
|
|
|
|
|
|
|
|
page = btr_pcur_get_page(cursor);
|
|
|
|
|
|
|
|
prev_page_no = btr_page_get_prev(page, mtr);
|
|
|
|
|
2016-12-05 21:04:30 +02:00
|
|
|
if (prev_page_no == FIL_NULL) {
|
|
|
|
} else if (btr_pcur_is_before_first_on_page(cursor)) {
|
2014-02-26 19:11:54 +01:00
|
|
|
|
2016-12-05 21:04:30 +02:00
|
|
|
prev_block = btr_pcur_get_btr_cur(cursor)->left_block;
|
2014-02-26 19:11:54 +01:00
|
|
|
|
2016-12-05 21:04:30 +02:00
|
|
|
btr_leaf_page_release(btr_pcur_get_block(cursor),
|
|
|
|
latch_mode, mtr);
|
2016-08-12 11:17:45 +03:00
|
|
|
|
2016-12-05 21:04:30 +02:00
|
|
|
page_cur_set_after_last(prev_block,
|
2014-02-26 19:11:54 +01:00
|
|
|
btr_pcur_get_page_cur(cursor));
|
2016-12-05 21:04:30 +02:00
|
|
|
} else {
|
2014-02-26 19:11:54 +01:00
|
|
|
|
2016-12-05 21:04:30 +02:00
|
|
|
/* The repositioned cursor did not end on an infimum
|
|
|
|
record on a page. Cursor repositioning acquired a latch
|
|
|
|
also on the previous page, but we do not need the latch:
|
|
|
|
release it. */
|
2014-02-26 19:11:54 +01:00
|
|
|
|
2016-12-05 21:04:30 +02:00
|
|
|
prev_block = btr_pcur_get_btr_cur(cursor)->left_block;
|
2014-02-26 19:11:54 +01:00
|
|
|
|
2016-12-05 21:04:30 +02:00
|
|
|
btr_leaf_page_release(prev_block, latch_mode, mtr);
|
2014-02-26 19:11:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
cursor->latch_mode = latch_mode;
|
2016-08-12 11:17:45 +03:00
|
|
|
cursor->old_stored = false;
|
2014-02-26 19:11:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*********************************************************//**
|
|
|
|
Moves the persistent cursor to the previous record in the tree. If no records
|
|
|
|
are left, the cursor stays 'before first in tree'.
|
2016-08-12 11:17:45 +03:00
|
|
|
@return TRUE if the cursor was not before first in tree */
|
2014-02-26 19:11:54 +01:00
|
|
|
ibool
|
|
|
|
btr_pcur_move_to_prev(
|
|
|
|
/*==================*/
|
|
|
|
btr_pcur_t* cursor, /*!< in: persistent cursor; NOTE that the
|
|
|
|
function may release the page latch */
|
|
|
|
mtr_t* mtr) /*!< in: mtr */
|
|
|
|
{
|
|
|
|
ut_ad(cursor->pos_state == BTR_PCUR_IS_POSITIONED);
|
|
|
|
ut_ad(cursor->latch_mode != BTR_NO_LATCHES);
|
|
|
|
|
2016-08-12 11:17:45 +03:00
|
|
|
cursor->old_stored = false;
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
if (btr_pcur_is_before_first_on_page(cursor)) {
|
|
|
|
|
|
|
|
if (btr_pcur_is_before_first_in_tree(cursor, mtr)) {
|
|
|
|
|
|
|
|
return(FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
btr_pcur_move_backward_from_page(cursor, mtr);
|
|
|
|
|
|
|
|
return(TRUE);
|
|
|
|
}
|
|
|
|
|
|
|
|
btr_pcur_move_to_prev_on_page(cursor);
|
|
|
|
|
|
|
|
return(TRUE);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**************************************************************//**
|
|
|
|
If mode is PAGE_CUR_G or PAGE_CUR_GE, opens a persistent cursor on the first
|
|
|
|
user record satisfying the search condition, in the case PAGE_CUR_L or
|
|
|
|
PAGE_CUR_LE, on the last user record. If no such user record exists, then
|
|
|
|
in the first case sets the cursor after last in tree, and in the latter case
|
|
|
|
before first in tree. The latching mode must be BTR_SEARCH_LEAF or
|
|
|
|
BTR_MODIFY_LEAF. */
|
|
|
|
void
|
|
|
|
btr_pcur_open_on_user_rec_func(
|
|
|
|
/*===========================*/
|
|
|
|
dict_index_t* index, /*!< in: index */
|
|
|
|
const dtuple_t* tuple, /*!< in: tuple on which search done */
|
2016-08-12 11:17:45 +03:00
|
|
|
page_cur_mode_t mode, /*!< in: PAGE_CUR_L, ... */
|
2014-02-26 19:11:54 +01:00
|
|
|
ulint latch_mode, /*!< in: BTR_SEARCH_LEAF or
|
|
|
|
BTR_MODIFY_LEAF */
|
|
|
|
btr_pcur_t* cursor, /*!< in: memory buffer for persistent
|
|
|
|
cursor */
|
|
|
|
const char* file, /*!< in: file name */
|
2017-03-01 08:27:39 +02:00
|
|
|
unsigned line, /*!< in: line where called */
|
2014-02-26 19:11:54 +01:00
|
|
|
mtr_t* mtr) /*!< in: mtr */
|
|
|
|
{
|
|
|
|
btr_pcur_open_low(index, 0, tuple, mode, latch_mode, cursor,
|
MDEV-6076 Persistent AUTO_INCREMENT for InnoDB
This should be functionally equivalent to WL#6204 in MySQL 8.0.0, with
the notable difference that the file format changes are limited to
repurposing a previously unused data field in B-tree pages.
For persistent InnoDB tables, write the last used AUTO_INCREMENT
value to the root page of the clustered index, in the previously
unused (0) PAGE_MAX_TRX_ID field, now aliased as PAGE_ROOT_AUTO_INC.
Unlike some other previously unused InnoDB data fields, this one was
actually always zero-initialized, at least since MySQL 3.23.49.
The writes to PAGE_ROOT_AUTO_INC are protected by SX or X latch on the
root page. The SX latch will allow concurrent read access to the root
page. (The field PAGE_ROOT_AUTO_INC will only be read on the
first-time call to ha_innobase::open() from the SQL layer. The
PAGE_ROOT_AUTO_INC can only be updated when executing SQL, so
read/write races are not possible.)
During INSERT, the PAGE_ROOT_AUTO_INC is updated by the low-level
function btr_cur_search_to_nth_level(), adding no extra page
access. [Adaptive hash index lookup will be disabled during INSERT.]
If some rare UPDATE modifies an AUTO_INCREMENT column, the
PAGE_ROOT_AUTO_INC will be adjusted in a separate mini-transaction in
ha_innobase::update_row().
When a page is reorganized, we have to preserve the PAGE_ROOT_AUTO_INC
field.
During ALTER TABLE, the initial AUTO_INCREMENT value will be copied
from the table. ALGORITHM=COPY and online log apply in LOCK=NONE will
update PAGE_ROOT_AUTO_INC in real time.
innodb_col_no(): Determine the dict_table_t::cols[] element index
corresponding to a Field of a non-virtual column.
(The MySQL 5.7 implementation of virtual columns breaks the 1:1
relationship between Field::field_index and dict_table_t::cols[].
Virtual columns are omitted from dict_table_t::cols[]. Therefore,
we must translate the field_index of AUTO_INCREMENT columns into
an index of dict_table_t::cols[].)
Upgrade from old data files:
By default, the AUTO_INCREMENT sequence in old data files would appear
to be reset, because PAGE_MAX_TRX_ID or PAGE_ROOT_AUTO_INC would contain
the value 0 in each clustered index page. In new data files,
PAGE_ROOT_AUTO_INC can only be 0 if the table is empty or does not contain
any AUTO_INCREMENT column.
For backward compatibility, we use the old method of
SELECT MAX(auto_increment_column) for initializing the sequence.
btr_read_autoinc(): Read the AUTO_INCREMENT sequence from a new-format
data file.
btr_read_autoinc_with_fallback(): A variant of btr_read_autoinc()
that will resort to reading MAX(auto_increment_column) for data files
that did not use AUTO_INCREMENT yet. It was manually tested that during
the execution of innodb.autoinc_persist the compatibility logic is
not activated (for new files, PAGE_ROOT_AUTO_INC is never 0 in nonempty
clustered index root pages).
initialize_auto_increment(): Replaces
ha_innobase::innobase_initialize_autoinc(). This initializes
the AUTO_INCREMENT metadata. Only called from ha_innobase::open().
ha_innobase::info_low(): Do not try to lazily initialize
dict_table_t::autoinc. It must already have been initialized by
ha_innobase::open() or ha_innobase::create().
Note: The adjustments to class ha_innopart were not tested, because
the source code (native InnoDB partitioning) is not being compiled.
2016-12-14 19:56:39 +02:00
|
|
|
file, line, 0, mtr);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
if ((mode == PAGE_CUR_GE) || (mode == PAGE_CUR_G)) {
|
|
|
|
|
|
|
|
if (btr_pcur_is_after_last_on_page(cursor)) {
|
|
|
|
|
|
|
|
btr_pcur_move_to_next_user_rec(cursor, mtr);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
ut_ad((mode == PAGE_CUR_LE) || (mode == PAGE_CUR_L));
|
|
|
|
|
|
|
|
/* Not implemented yet */
|
|
|
|
|
|
|
|
ut_error;
|
|
|
|
}
|
|
|
|
}
|