mirror of
https://github.com/MariaDB/server.git
synced 2025-08-12 21:41:33 +02:00

Let us use implement a simple fixed-size allocator for the adaptive hash index, insted of complicating mem_heap_t or mem_block_info_t. MEM_HEAP_BTR_SEARCH: Remove. mem_block_info_t::free_block(), mem_heap_free_block_free(): Remove. mem_heap_free_top(), mem_heap_get_top(): Remove. btr_sea::partition::spare: Replaces mem_block_info_t::free_block. This keeps one spare block per adaptive hash index partition, to process an insert. We must not wait for buf_pool.mutex while holding any btr_sea::partition::latch. That is why we cache one block for future allocations. This is protected by a new btr_sea::partition::blocks_mutex in order to relieve pressure on btr_sea::partition::latch. btr_sea::partition::prepare_insert(): Replaces btr_search_check_free_space_in_heap(). btr_sea::partition::erase(): Replaces ha_search_and_delete_if_found(). btr_sea::partition::cleanup_after_erase(): Replaces the most part of ha_delete_hash_node(). Unlike the previous implementation, we will retain a spare block for prepare_insert(). This should reduce some contention on buf_pool.mutex. btr_search.n_parts: Replaces btr_ahi_parts. btr_search.enabled: Replaces btr_search_enabled. This must hold whenever buf_block_t::index is set while a thread is holding a btr_sea::partition::latch. dict_index_t::search_info: Remove pointer indirection, and use Atomic_relaxed or Atomic_counter for most fields. btr_search_guess_on_hash(): Let the caller ensure that latch_mode is BTR_MODIFY_LEAF or BTR_SEARCH_LEAF. Release btr_sea::partition::latch before buffer-fixing the block. The page latch that we already acquired is preventing buffer pool eviction. We must validate both block->index and block->page.state while holding part.latch in order to avoid race conditions with buffer page relocation or buf_pool_t::resize(). btr_search_check_guess(): Remove the constant parameter can_only_compare_to_cursor_rec=false. ahi_node: Replaces ha_node_t. This has been tested by running the regression test suite with the adaptive hash index enabled: ./mtr --mysqld=--loose-innodb-adaptive-hash-index=ON Reviewed by: Vladislav Lesin
266 lines
9.7 KiB
C
266 lines
9.7 KiB
C
/*****************************************************************************
|
|
|
|
Copyright (c) 1994, 2016, Oracle and/or its affiliates. All Rights Reserved.
|
|
Copyright (c) 2017, 2021, MariaDB Corporation.
|
|
|
|
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, Fifth Floor, Boston, MA 02110-1335 USA
|
|
|
|
*****************************************************************************/
|
|
|
|
/*******************************************************************//**
|
|
@file include/rem0cmp.h
|
|
Comparison services for records
|
|
|
|
Created 7/1/1994 Heikki Tuuri
|
|
************************************************************************/
|
|
|
|
#pragma once
|
|
|
|
#include "data0data.h"
|
|
#include "data0type.h"
|
|
#include "rem0types.h"
|
|
#include "page0types.h"
|
|
|
|
/*************************************************************//**
|
|
Returns TRUE if two columns are equal for comparison purposes.
|
|
@return TRUE if the columns are considered equal in comparisons */
|
|
ibool
|
|
cmp_cols_are_equal(
|
|
/*===============*/
|
|
const dict_col_t* col1, /*!< in: column 1 */
|
|
const dict_col_t* col2, /*!< in: column 2 */
|
|
ibool check_charsets);
|
|
/*!< in: whether to check charsets */
|
|
/** Compare two data fields.
|
|
@param mtype main type
|
|
@param prtype precise type
|
|
@param descending whether to use descending order
|
|
@param data1 data field
|
|
@param len1 length of data1 in bytes, or UNIV_SQL_NULL
|
|
@param data2 data field
|
|
@param len2 length of data2 in bytes, or UNIV_SQL_NULL
|
|
@return the comparison result of data1 and data2
|
|
@retval 0 if data1 is equal to data2
|
|
@retval negative if data1 is less than data2
|
|
@retval positive if data1 is greater than data2 */
|
|
int cmp_data(ulint mtype, ulint prtype, bool descending,
|
|
const byte *data1, size_t len1, const byte *data2, size_t len2)
|
|
noexcept
|
|
MY_ATTRIBUTE((warn_unused_result));
|
|
|
|
/** Compare two data fields.
|
|
@param dfield1 data field; must have type field set
|
|
@param dfield2 data field
|
|
@param descending whether to use descending order
|
|
@return the comparison result of dfield1 and dfield2
|
|
@retval 0 if dfield1 is equal to dfield2
|
|
@retval negative if dfield1 is less than dfield2
|
|
@retval positive if dfield1 is greater than dfield2 */
|
|
inline int cmp_dfield_dfield(const dfield_t *dfield1, const dfield_t *dfield2,
|
|
bool descending= false)
|
|
{
|
|
ut_ad(dfield_check_typed(dfield1));
|
|
const dtype_t *type= dfield_get_type(dfield1);
|
|
return cmp_data(type->mtype, type->prtype, descending,
|
|
static_cast<const byte*>(dfield_get_data(dfield1)),
|
|
dfield_get_len(dfield1),
|
|
static_cast<const byte*>(dfield_get_data(dfield2)),
|
|
dfield_get_len(dfield2));
|
|
}
|
|
|
|
#ifdef UNIV_DEBUG
|
|
/** Compare a GIS data tuple to a physical record.
|
|
@param[in] dtuple data tuple
|
|
@param[in] rec R-tree record
|
|
@param[in] mode compare mode
|
|
@retval negative if dtuple is less than rec */
|
|
int cmp_dtuple_rec_with_gis(const dtuple_t *dtuple, const rec_t *rec,
|
|
page_cur_mode_t mode)
|
|
MY_ATTRIBUTE((nonnull));
|
|
#endif
|
|
|
|
/** Compare two minimum bounding rectangles.
|
|
@return 1, 0, -1, if a is greater, equal, less than b, respectively */
|
|
inline int cmp_geometry_field(const void *a, const void *b)
|
|
{
|
|
const byte *mbr1= static_cast<const byte*>(a);
|
|
const byte *mbr2= static_cast<const byte*>(b);
|
|
|
|
static_assert(SPDIMS == 2, "compatibility");
|
|
static_assert(DATA_MBR_LEN == SPDIMS * 2 * sizeof(double), "compatibility");
|
|
|
|
/* Try to compare mbr left lower corner (xmin, ymin) */
|
|
double x1= mach_double_read(mbr1);
|
|
double x2= mach_double_read(mbr2);
|
|
if (x1 > x2)
|
|
return 1;
|
|
if (x1 < x2)
|
|
return -1;
|
|
|
|
x1= mach_double_read(mbr1 + sizeof(double) * SPDIMS);
|
|
x2= mach_double_read(mbr2 + sizeof(double) * SPDIMS);
|
|
|
|
if (x1 > x2)
|
|
return 1;
|
|
if (x1 < x2)
|
|
return -1;
|
|
|
|
/* left lower corner (xmin, ymin) overlaps, now right upper corner */
|
|
x1= mach_double_read(mbr1 + sizeof(double));
|
|
x2= mach_double_read(mbr2 + sizeof(double));
|
|
|
|
if (x1 > x2)
|
|
return 1;
|
|
if (x1 < x2)
|
|
return -1;
|
|
|
|
x1= mach_double_read(mbr1 + sizeof(double) * 2 + sizeof(double));
|
|
x2= mach_double_read(mbr2 + sizeof(double) * 2 + sizeof(double));
|
|
|
|
if (x1 > x2)
|
|
return 1;
|
|
if (x1 < x2)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/** Compare a data tuple to a physical record.
|
|
@param dtuple data tuple
|
|
@param rec B-tree index record
|
|
@param index B-tree index
|
|
@param offsets rec_get_offsets(rec,index)
|
|
@param n_cmp number of fields to compare
|
|
@param matched_fields number of completely matched fields
|
|
@return the comparison result of dtuple and rec
|
|
@retval 0 if dtuple is equal to rec
|
|
@retval negative if dtuple is less than rec
|
|
@retval positive if dtuple is greater than rec */
|
|
int cmp_dtuple_rec_with_match_low(const dtuple_t *dtuple, const rec_t *rec,
|
|
const dict_index_t *index,
|
|
const rec_offs *offsets,
|
|
ulint n_cmp, uint16_t *matched_fields)
|
|
MY_ATTRIBUTE((nonnull));
|
|
#define cmp_dtuple_rec_with_match(tuple,rec,index,offsets,fields) \
|
|
cmp_dtuple_rec_with_match_low( \
|
|
tuple,rec,index,offsets,dtuple_get_n_fields_cmp(tuple),fields)
|
|
/** Compare a data tuple to a physical record.
|
|
@see cmp_dtuple_rec_with_match
|
|
@param dtuple data tuple
|
|
@param rec index record
|
|
@param index index
|
|
@param offsets rec_get_offsets(rec, index)
|
|
@return the comparison result of dtuple and rec
|
|
@retval 0 if dtuple is equal to rec
|
|
@retval negative if dtuple is less than rec
|
|
@retval positive if dtuple is greater than rec */
|
|
inline int cmp_dtuple_rec(const dtuple_t *dtuple, const rec_t *rec,
|
|
const dict_index_t *index, const rec_offs *offsets)
|
|
{
|
|
uint16_t matched= 0;
|
|
return cmp_dtuple_rec_with_match(dtuple, rec, index, offsets, &matched);
|
|
}
|
|
|
|
/** Check if a dtuple is a prefix of a record.
|
|
@param dtuple data tuple
|
|
@param rec index record
|
|
@param index index
|
|
@param offsets rec_get_offsets(rec)
|
|
@return whether dtuple is a prefix of rec */
|
|
bool cmp_dtuple_is_prefix_of_rec(const dtuple_t *dtuple, const rec_t *rec,
|
|
const dict_index_t *index,
|
|
const rec_offs *offsets)
|
|
MY_ATTRIBUTE((nonnull, warn_unused_result));
|
|
|
|
/** Compare two physical records that contain the same number of columns,
|
|
none of which are stored externally.
|
|
@retval positive if rec1 (including non-ordering columns) is greater than rec2
|
|
@retval negative if rec1 (including non-ordering columns) is less than rec2
|
|
@retval 0 if rec1 is a duplicate of rec2 */
|
|
int
|
|
cmp_rec_rec_simple(
|
|
/*===============*/
|
|
const rec_t* rec1, /*!< in: physical record */
|
|
const rec_t* rec2, /*!< in: physical record */
|
|
const rec_offs* offsets1,/*!< in: rec_get_offsets(rec1, ...) */
|
|
const rec_offs* offsets2,/*!< in: rec_get_offsets(rec2, ...) */
|
|
const dict_index_t* index, /*!< in: data dictionary index */
|
|
struct TABLE* table) /*!< in: MySQL table, for reporting
|
|
duplicate key value if applicable,
|
|
or NULL */
|
|
MY_ATTRIBUTE((nonnull(1,2,3,4), warn_unused_result));
|
|
|
|
/** Compare two B-tree or R-tree records.
|
|
Only the common first fields are compared, and externally stored field
|
|
are treated as equal.
|
|
@param[in] rec1 record (possibly not on an index page)
|
|
@param[in] rec2 B-tree or R-tree record in an index page
|
|
@param[in] offsets1 rec_get_offsets(rec1, index)
|
|
@param[in] offsets2 rec_get_offsets(rec2, index)
|
|
@param[in] nulls_unequal true if this is for index cardinality
|
|
statistics estimation with
|
|
innodb_stats_method=nulls_unequal
|
|
or innodb_stats_method=nulls_ignored
|
|
@param[out] matched_fields number of completely matched fields
|
|
within the first field not completely matched
|
|
@retval 0 if rec1 is equal to rec2
|
|
@retval negative if rec1 is less than rec2
|
|
@retval positive if rec1 is greater than rec2 */
|
|
int
|
|
cmp_rec_rec(
|
|
const rec_t* rec1,
|
|
const rec_t* rec2,
|
|
const rec_offs* offsets1,
|
|
const rec_offs* offsets2,
|
|
const dict_index_t* index,
|
|
bool nulls_unequal = false,
|
|
ulint* matched_fields = NULL)
|
|
MY_ATTRIBUTE((nonnull(1,2,3,4,5)));
|
|
|
|
/** Compare two data fields.
|
|
@param dfield1 data field
|
|
@param dfield2 data field
|
|
@return the comparison result of dfield1 and dfield2
|
|
@retval true if dfield1 is equal to dfield2, or a prefix of dfield1
|
|
@retval false otherwise */
|
|
inline bool cmp_dfield_dfield_eq_prefix(const dfield_t *dfield1,
|
|
const dfield_t *dfield2)
|
|
{
|
|
ut_ad(dfield_check_typed(dfield1));
|
|
ut_ad(dfield_check_typed(dfield2));
|
|
const dtype_t *type= dfield_get_type(dfield1);
|
|
|
|
#ifdef UNIV_DEBUG
|
|
switch (type->prtype & DATA_MYSQL_TYPE_MASK) {
|
|
case MYSQL_TYPE_BIT:
|
|
case MYSQL_TYPE_STRING:
|
|
case MYSQL_TYPE_VAR_STRING:
|
|
case MYSQL_TYPE_TINY_BLOB:
|
|
case MYSQL_TYPE_MEDIUM_BLOB:
|
|
case MYSQL_TYPE_BLOB:
|
|
case MYSQL_TYPE_LONG_BLOB:
|
|
case MYSQL_TYPE_VARCHAR:
|
|
break;
|
|
default:
|
|
ut_error;
|
|
}
|
|
#endif /* UNIV_DEBUG */
|
|
|
|
uint cs_num= dtype_get_charset_coll(type->prtype);
|
|
CHARSET_INFO *cs= get_charset(cs_num, MYF(MY_WME));
|
|
ut_a(cs);
|
|
return !cs->strnncoll(static_cast<const uchar*>(dfield_get_data(dfield1)),
|
|
dfield_get_len(dfield1),
|
|
static_cast<const uchar*>(dfield_get_data(dfield2)),
|
|
dfield_get_len(dfield2), 1);
|
|
}
|