302 lines
10 KiB
Plaintext
302 lines
10 KiB
Plaintext
/*****************************************************************************
|
|
|
|
Copyright (c) 1994, 2019, Oracle and/or its affiliates. All Rights Reserved.
|
|
|
|
This program is free software; you can redistribute it and/or modify it under
|
|
the terms of the GNU General Public License, version 2.0, as published by the
|
|
Free Software Foundation.
|
|
|
|
This program is also distributed with certain software (including but not
|
|
limited to OpenSSL) that is licensed under separate terms, as designated in a
|
|
particular file or component or in included license documentation. The authors
|
|
of MySQL hereby grant you an additional permission to link the program and
|
|
your derivative works with the separately licensed software that they have
|
|
included with MySQL.
|
|
|
|
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, version 2.0,
|
|
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 St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
|
|
*****************************************************************************/
|
|
|
|
/** @file include/page0cur.ic
|
|
The page cursor
|
|
|
|
Created 10/4/1994 Heikki Tuuri
|
|
*************************************************************************/
|
|
|
|
#include "buf0types.h"
|
|
#include "page0page.h"
|
|
|
|
#ifdef UNIV_DEBUG
|
|
#ifndef UNIV_HOTBACKUP
|
|
#include "rem0cmp.h"
|
|
#endif /* !UNIV_HOTBACKUP */
|
|
|
|
/** Gets pointer to the page frame where the cursor is positioned.
|
|
@return page */
|
|
UNIV_INLINE
|
|
page_t *page_cur_get_page(page_cur_t *cur) /*!< in: page cursor */
|
|
{
|
|
ut_ad(cur);
|
|
ut_ad(page_align(cur->rec) == cur->block->frame);
|
|
|
|
return (page_align(cur->rec));
|
|
}
|
|
|
|
/** Gets pointer to the buffer block where the cursor is positioned.
|
|
@return page */
|
|
UNIV_INLINE
|
|
buf_block_t *page_cur_get_block(page_cur_t *cur) /*!< in: page cursor */
|
|
{
|
|
ut_ad(cur);
|
|
ut_ad(page_align(cur->rec) == cur->block->frame);
|
|
return (cur->block);
|
|
}
|
|
|
|
/** Gets pointer to the page frame where the cursor is positioned.
|
|
@return page */
|
|
UNIV_INLINE
|
|
page_zip_des_t *page_cur_get_page_zip(page_cur_t *cur) /*!< in: page cursor */
|
|
{
|
|
return (buf_block_get_page_zip(page_cur_get_block(cur)));
|
|
}
|
|
|
|
/** Gets the record where the cursor is positioned.
|
|
@return record */
|
|
UNIV_INLINE
|
|
rec_t *page_cur_get_rec(page_cur_t *cur) /*!< in: page cursor */
|
|
{
|
|
ut_ad(cur);
|
|
ut_ad(page_align(cur->rec) == cur->block->frame);
|
|
|
|
return (cur->rec);
|
|
}
|
|
#endif /* UNIV_DEBUG */
|
|
|
|
/** Sets the cursor object to point before the first user record
|
|
on the page. */
|
|
UNIV_INLINE
|
|
void page_cur_set_before_first(const buf_block_t *block, /*!< in: index page */
|
|
page_cur_t *cur) /*!< in: cursor */
|
|
{
|
|
cur->block = (buf_block_t *)block;
|
|
cur->rec = page_get_infimum_rec(buf_block_get_frame(cur->block));
|
|
}
|
|
|
|
/** Sets the cursor object to point after the last user record on
|
|
the page. */
|
|
UNIV_INLINE
|
|
void page_cur_set_after_last(const buf_block_t *block, /*!< in: index page */
|
|
page_cur_t *cur) /*!< in: cursor */
|
|
{
|
|
cur->block = (buf_block_t *)block;
|
|
cur->rec = page_get_supremum_rec(buf_block_get_frame(cur->block));
|
|
}
|
|
|
|
/** Returns TRUE if the cursor is before first user record on page.
|
|
@return true if at start */
|
|
UNIV_INLINE
|
|
ibool page_cur_is_before_first(const page_cur_t *cur) /*!< in: cursor */
|
|
{
|
|
ut_ad(cur);
|
|
ut_ad(page_align(cur->rec) == cur->block->frame);
|
|
return (page_rec_is_infimum(cur->rec));
|
|
}
|
|
|
|
/** Returns TRUE if the cursor is after last user record.
|
|
@return true if at end */
|
|
UNIV_INLINE
|
|
ibool page_cur_is_after_last(const page_cur_t *cur) /*!< in: cursor */
|
|
{
|
|
ut_ad(cur);
|
|
ut_ad(page_align(cur->rec) == cur->block->frame);
|
|
return (page_rec_is_supremum(cur->rec));
|
|
}
|
|
|
|
/** Positions the cursor on the given record. */
|
|
UNIV_INLINE
|
|
void page_cur_position(const rec_t *rec, /*!< in: record on a page */
|
|
const buf_block_t *block, /*!< in: buffer block
|
|
containing the record */
|
|
page_cur_t *cur) /*!< out: page cursor */
|
|
{
|
|
ut_ad(rec && block && cur);
|
|
ut_ad(page_align(rec) == block->frame);
|
|
|
|
cur->rec = (rec_t *)rec;
|
|
cur->block = (buf_block_t *)block;
|
|
}
|
|
|
|
/** Moves the cursor to the next record on page. */
|
|
UNIV_INLINE
|
|
void page_cur_move_to_next(
|
|
page_cur_t *cur) /*!< in/out: cursor; must not be after last */
|
|
{
|
|
ut_ad(!page_cur_is_after_last(cur));
|
|
|
|
cur->rec = page_rec_get_next(cur->rec);
|
|
}
|
|
|
|
/** Moves the cursor to the previous record on page. */
|
|
UNIV_INLINE
|
|
void page_cur_move_to_prev(
|
|
page_cur_t *cur) /*!< in/out: page cursor, not before first */
|
|
{
|
|
ut_ad(!page_cur_is_before_first(cur));
|
|
|
|
cur->rec = page_rec_get_prev(cur->rec);
|
|
}
|
|
|
|
#ifndef UNIV_HOTBACKUP
|
|
/** Search the right position for a page cursor.
|
|
@param[in] block buffer block
|
|
@param[in] index index tree
|
|
@param[in] tuple data tuple
|
|
@param[in] mode PAGE_CUR_L, PAGE_CUR_LE, PAGE_CUR_G, or PAGE_CUR_GE
|
|
@param[out] cursor page cursor
|
|
@return number of matched fields on the left */
|
|
UNIV_INLINE
|
|
ulint page_cur_search(const buf_block_t *block, const dict_index_t *index,
|
|
const dtuple_t *tuple, page_cur_mode_t mode,
|
|
page_cur_t *cursor) {
|
|
ulint low_match = 0;
|
|
ulint up_match = 0;
|
|
|
|
ut_ad(dtuple_check_typed(tuple));
|
|
|
|
page_cur_search_with_match(block, index, tuple, mode, &up_match, &low_match,
|
|
cursor, NULL);
|
|
return (low_match);
|
|
}
|
|
|
|
/** Search the right position for a page cursor.
|
|
@param[in] block buffer block
|
|
@param[in] index index tree
|
|
@param[in] tuple data tuple
|
|
@param[out] cursor page cursor
|
|
@return number of matched fields on the left */
|
|
UNIV_INLINE
|
|
ulint page_cur_search(const buf_block_t *block, const dict_index_t *index,
|
|
const dtuple_t *tuple, page_cur_t *cursor) {
|
|
return (page_cur_search(block, index, tuple, PAGE_CUR_LE, cursor));
|
|
}
|
|
|
|
/** Inserts a record next to page cursor. Returns pointer to inserted record if
|
|
succeed, i.e., enough space available, NULL otherwise. The cursor stays at
|
|
the same logical position, but the physical position may change if it is
|
|
pointing to a compressed page that was reorganized.
|
|
|
|
IMPORTANT: The caller will have to update IBUF_BITMAP_FREE
|
|
if this is a compressed leaf page in a secondary index.
|
|
This has to be done either within the same mini-transaction,
|
|
or by invoking ibuf_reset_free_bits() before mtr_commit().
|
|
|
|
@return pointer to record if succeed, NULL otherwise */
|
|
UNIV_INLINE
|
|
rec_t *page_cur_tuple_insert(
|
|
page_cur_t *cursor, /*!< in/out: a page cursor */
|
|
const dtuple_t *tuple, /*!< in: pointer to a data tuple */
|
|
dict_index_t *index, /*!< in: record descriptor */
|
|
ulint **offsets, /*!< out: offsets on *rec */
|
|
mem_heap_t **heap, /*!< in/out: pointer to memory heap, or NULL */
|
|
ulint n_ext, /*!< in: number of externally stored columns */
|
|
mtr_t *mtr, /*!< in: mini-transaction handle, or NULL */
|
|
bool use_cache)
|
|
/*!< in: if true, then use record cache to
|
|
hold the tuple converted record. */
|
|
{
|
|
rec_t *rec;
|
|
rec_t *insert_rec;
|
|
|
|
ulint size = rec_get_converted_size(index, tuple, n_ext);
|
|
|
|
if (!*heap) {
|
|
*heap = mem_heap_create(
|
|
size + (4 + REC_OFFS_HEADER_SIZE + dtuple_get_n_fields(tuple)) *
|
|
sizeof **offsets);
|
|
}
|
|
|
|
rec = rec_convert_dtuple_to_rec((byte *)mem_heap_alloc(*heap, size), index,
|
|
tuple, n_ext);
|
|
|
|
*offsets = rec_get_offsets(rec, index, *offsets, ULINT_UNDEFINED, heap);
|
|
|
|
ut_ad(cmp_dtuple_rec(tuple, rec, index, *offsets) == 0);
|
|
|
|
if (buf_block_get_page_zip(cursor->block)) {
|
|
insert_rec = page_cur_insert_rec_zip(cursor, index, rec, *offsets, mtr);
|
|
} else {
|
|
insert_rec =
|
|
page_cur_insert_rec_low(cursor->rec, index, rec, *offsets, mtr);
|
|
}
|
|
|
|
#ifdef UNIV_DEBUG
|
|
if (insert_rec != nullptr) {
|
|
if (cmp_dtuple_rec(tuple, insert_rec, index, *offsets) != 0) {
|
|
std::cerr << "tuple=" << rec_printer(tuple).str() << std::endl;
|
|
std::cerr << "rec=" << rec_printer(insert_rec, *offsets).str()
|
|
<< std::endl;
|
|
}
|
|
}
|
|
ut_ad(!insert_rec || !cmp_dtuple_rec(tuple, insert_rec, index, *offsets));
|
|
#endif /* UNIV_DEBUG */
|
|
|
|
return (insert_rec);
|
|
}
|
|
|
|
/** Insert a record next to page cursor. Record is directly copied to
|
|
the page from tuple without creating intermediate copy of the record.
|
|
|
|
@param[in,out] cursor a page cursor
|
|
@param[in] tuple pointer to a data tuple
|
|
@param[in] index record descriptor
|
|
@param[in] n_ext number of externally stored columns
|
|
@param[in] mtr mini-transaction handle, or NULL
|
|
|
|
@return pointer to record if succeed, NULL otherwise */
|
|
UNIV_INLINE
|
|
rec_t *page_cur_tuple_direct_insert(page_cur_t *cursor, const dtuple_t *tuple,
|
|
dict_index_t *index, ulint n_ext,
|
|
mtr_t *mtr) {
|
|
rec_t *rec;
|
|
|
|
ut_ad(index->table->is_intrinsic());
|
|
|
|
rec = page_cur_direct_insert_rec_low(cursor->rec, index, tuple, n_ext, mtr);
|
|
|
|
return (rec);
|
|
}
|
|
#endif /* !UNIV_HOTBACKUP */
|
|
|
|
/** Inserts a record next to page cursor. Returns pointer to inserted record if
|
|
succeed, i.e., enough space available, NULL otherwise. The cursor stays at
|
|
the same logical position, but the physical position may change if it is
|
|
pointing to a compressed page that was reorganized.
|
|
|
|
IMPORTANT: The caller will have to update IBUF_BITMAP_FREE
|
|
if this is a compressed leaf page in a secondary index.
|
|
This has to be done either within the same mini-transaction,
|
|
or by invoking ibuf_reset_free_bits() before mtr_commit().
|
|
|
|
@return pointer to record if succeed, NULL otherwise */
|
|
UNIV_INLINE
|
|
rec_t *page_cur_rec_insert(
|
|
page_cur_t *cursor, /*!< in/out: a page cursor */
|
|
const rec_t *rec, /*!< in: record to insert */
|
|
dict_index_t *index, /*!< in: record descriptor */
|
|
ulint *offsets, /*!< in/out: rec_get_offsets(rec, index) */
|
|
mtr_t *mtr) /*!< in: mini-transaction handle, or NULL */
|
|
{
|
|
if (buf_block_get_page_zip(cursor->block)) {
|
|
return (page_cur_insert_rec_zip(cursor, index, rec, offsets, mtr));
|
|
} else {
|
|
return (page_cur_insert_rec_low(cursor->rec, index, rec, offsets, mtr));
|
|
}
|
|
}
|