summaryrefslogtreecommitdiff
path: root/storage/innobase/include/rem0cmp.h
blob: 3a30f5a92f3ea6451d71e1b715d1f74e608c9e6d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
/*****************************************************************************

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);
}