mariadb/storage/innobase/include/rem0cmp.h
Marko Mäkelä 358921ce32 MDEV-26938 Support descending indexes internally in InnoDB
This is loosely based on the InnoDB changes in
mysql/mysql-server@97fd8b1b69
that I had developed in 2015 or 2016.

For each B-tree key field, we will allow a flag ASC/DESC to be associated.
When PRIMARY KEY fields are internally appended to secondary indexes,
the ASC/DESC attribute will be inherited, so that covering index scans
will work as expected.

Note: Until the subsequent commit, the DESC attribute will be ignored
(no HA_REVERSE_SORT flag will be written to .frm files).

dict_field_t::descending: A new flag to denote descending order.

cmp_data(), cmp_dfield_dfield(): Add a new parameter descending.

cmp_dtuple_rec(), cmp_dtuple_rec_with_match(): Add a parameter "index".

dtuple_coll_eq(): Replaces dtuple_coll_cmp().

cmp_dfield_dfield_eq_prefix(): Replaces cmp_dfield_dfield_like_prefix().

dict_index_t::is_btree(): Check whether the index is a regular
B-tree index (not SPATIAL, FULLTEXT, or the ibuf.index,
or a corrupted index.

btr_cur_search_to_nth_level_func(): Only attempt to use
the adaptive hash index if index->is_btree().
This function may also be invoked on ibuf.index, and
cmp_dtuple_rec_with_match_bytes() will no longer work on ibuf.index
because it assumes that the index and record fields exactly match.
The ibuf.index is a special variadic index tree.

Thanks to Thirunarayanan Balathandayuthapani for fixing some bugs:
MDEV-27439, MDEV-27374/MDEV-27445.
2022-01-26 18:43:05 +01:00

286 lines
10 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)
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, ulint *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.
@param[in] dtuple data tuple
@param[in] rec B-tree or R-tree index record
@param[in] index index tree
@param[in] offsets rec_get_offsets(rec)
@param[in,out] matched_fields number of completely matched fields
@param[in,out] matched_bytes number of matched bytes in the first
field that is not matched
@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_bytes(
const dtuple_t* dtuple,
const rec_t* rec,
const dict_index_t* index,
const rec_offs* offsets,
ulint* matched_fields,
ulint* matched_bytes)
MY_ATTRIBUTE((warn_unused_result));
/** 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)
{
ulint 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);
}