mirror of
https://github.com/MariaDB/server.git
synced 2026-05-16 20:07:13 +02:00
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.
This commit is contained in:
parent
349a595bc4
commit
358921ce32
43 changed files with 797 additions and 644 deletions
|
|
@ -1,7 +1,7 @@
|
|||
/*****************************************************************************
|
||||
|
||||
Copyright (c) 1994, 2016, Oracle and/or its affiliates. All Rights Reserved.
|
||||
Copyright (c) 2017, 2020, MariaDB Corporation.
|
||||
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
|
||||
|
|
@ -24,8 +24,7 @@ Comparison services for records
|
|||
Created 7/1/1994 Heikki Tuuri
|
||||
************************************************************************/
|
||||
|
||||
#ifndef rem0cmp_h
|
||||
#define rem0cmp_h
|
||||
#pragma once
|
||||
|
||||
#include "data0data.h"
|
||||
#include "data0type.h"
|
||||
|
|
@ -43,39 +42,40 @@ cmp_cols_are_equal(
|
|||
ibool check_charsets);
|
||||
/*!< in: whether to check charsets */
|
||||
/** Compare two data fields.
|
||||
@param[in] mtype main type
|
||||
@param[in] prtype precise type
|
||||
@param[in] data1 data field
|
||||
@param[in] len1 length of data1 in bytes, or UNIV_SQL_NULL
|
||||
@param[in] data2 data field
|
||||
@param[in] len2 length of data2 in bytes, or UNIV_SQL_NULL
|
||||
@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_data(
|
||||
ulint mtype,
|
||||
ulint prtype,
|
||||
const byte* data1,
|
||||
ulint len1,
|
||||
const byte* data2,
|
||||
ulint len2)
|
||||
MY_ATTRIBUTE((warn_unused_result));
|
||||
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[in] dfield1 data field; must have type field set
|
||||
@param[in] dfield2 data field
|
||||
@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 */
|
||||
UNIV_INLINE
|
||||
int
|
||||
cmp_dfield_dfield(
|
||||
/*==============*/
|
||||
const dfield_t* dfield1,/*!< in: data field; must have type field set */
|
||||
const dfield_t* dfield2);/*!< in: data field */
|
||||
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.
|
||||
|
|
@ -103,15 +103,15 @@ inline int cmp_geometry_field(const void *a, const void *b)
|
|||
double x2= mach_double_read(mbr2);
|
||||
if (x1 > x2)
|
||||
return 1;
|
||||
if (x2 > x1)
|
||||
if (x1 < x2)
|
||||
return -1;
|
||||
|
||||
double y1= mach_double_read(mbr1 + sizeof(double) * SPDIMS);
|
||||
double y2= mach_double_read(mbr2 + sizeof(double) * SPDIMS);
|
||||
x1= mach_double_read(mbr1 + sizeof(double) * SPDIMS);
|
||||
x2= mach_double_read(mbr2 + sizeof(double) * SPDIMS);
|
||||
|
||||
if (y1 > y2)
|
||||
if (x1 > x2)
|
||||
return 1;
|
||||
if (y2 > y1)
|
||||
if (x1 < x2)
|
||||
return -1;
|
||||
|
||||
/* left lower corner (xmin, ymin) overlaps, now right upper corner */
|
||||
|
|
@ -120,41 +120,39 @@ inline int cmp_geometry_field(const void *a, const void *b)
|
|||
|
||||
if (x1 > x2)
|
||||
return 1;
|
||||
if (x2 > x1)
|
||||
if (x1 < x2)
|
||||
return -1;
|
||||
|
||||
y1= mach_double_read(mbr1 + sizeof(double) * 2 + sizeof(double));
|
||||
y2= mach_double_read(mbr2 + sizeof(double) * 2 + sizeof(double));
|
||||
x1= mach_double_read(mbr1 + sizeof(double) * 2 + sizeof(double));
|
||||
x2= mach_double_read(mbr2 + sizeof(double) * 2 + sizeof(double));
|
||||
|
||||
if (y1 > y2)
|
||||
if (x1 > x2)
|
||||
return 1;
|
||||
if (y2 > y1)
|
||||
if (x1 < x2)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Compare a data tuple to a physical record.
|
||||
@param[in] dtuple data tuple
|
||||
@param[in] rec B-tree record
|
||||
@param[in] offsets rec_get_offsets(rec)
|
||||
@param[in] n_cmp number of fields to compare
|
||||
@param[in,out] matched_fields number of completely matched fields
|
||||
@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 rec_offs* offsets,
|
||||
ulint n_cmp,
|
||||
ulint* matched_fields)
|
||||
MY_ATTRIBUTE((nonnull));
|
||||
#define cmp_dtuple_rec_with_match(tuple,rec,offsets,fields) \
|
||||
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,offsets,dtuple_get_n_fields_cmp(tuple),fields)
|
||||
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
|
||||
|
|
@ -178,28 +176,32 @@ cmp_dtuple_rec_with_match_bytes(
|
|||
MY_ATTRIBUTE((warn_unused_result));
|
||||
/** Compare a data tuple to a physical record.
|
||||
@see cmp_dtuple_rec_with_match
|
||||
@param[in] dtuple data tuple
|
||||
@param[in] rec B-tree record
|
||||
@param[in] offsets rec_get_offsets(rec)
|
||||
@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 */
|
||||
int
|
||||
cmp_dtuple_rec(
|
||||
const dtuple_t* dtuple,
|
||||
const rec_t* rec,
|
||||
const rec_offs* offsets);
|
||||
/**************************************************************//**
|
||||
Checks if a dtuple is a prefix of a record. The last field in dtuple
|
||||
is allowed to be a prefix of the corresponding field in the record.
|
||||
@return TRUE if prefix */
|
||||
ibool
|
||||
cmp_dtuple_is_prefix_of_rec(
|
||||
/*========================*/
|
||||
const dtuple_t* dtuple, /*!< in: data tuple */
|
||||
const rec_t* rec, /*!< in: physical record */
|
||||
const rec_offs* offsets);/*!< in: array returned by rec_get_offsets() */
|
||||
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
|
||||
|
|
@ -246,18 +248,39 @@ cmp_rec_rec(
|
|||
MY_ATTRIBUTE((nonnull(1,2,3,4,5)));
|
||||
|
||||
/** Compare two data fields.
|
||||
@param[in] dfield1 data field
|
||||
@param[in] dfield2 data field
|
||||
@param dfield1 data field
|
||||
@param dfield2 data field
|
||||
@return the comparison result of dfield1 and dfield2
|
||||
@retval 0 if dfield1 is equal to dfield2, or a prefix of dfield1
|
||||
@retval negative if dfield1 is less than dfield2
|
||||
@retval positive if dfield1 is greater than dfield2 */
|
||||
UNIV_INLINE
|
||||
int
|
||||
cmp_dfield_dfield_like_prefix(
|
||||
const dfield_t* dfield1,
|
||||
const dfield_t* 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);
|
||||
|
||||
#include "rem0cmp.ic"
|
||||
#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 */
|
||||
|
||||
#endif
|
||||
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);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue