/* Copyright (c) 2011, 2024, Oracle and/or its affiliates. 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 designed to work 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 either included with the program or referenced in the documentation. Without limiting anything contained in the foregoing, this file, which is part of C Driver for MySQL (Connector/C), is also subject to the Universal FOSS Exception, version 1.0, a copy of which can be found at http://oss.oracle.com/licenses/universal-foss-exception. 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 mysys/my_compare.cc */ #include #include #include "my_base.h" #include "my_byteorder.h" #include "my_compare.h" #include "my_inttypes.h" #include "my_macros.h" #include "myisampack.h" #include "mysql/strings/m_ctype.h" #define CMP_NUM(a, b) (((a) < (b)) ? -1 : ((a) == (b)) ? 0 : 1) int ha_compare_text(const CHARSET_INFO *charset_info, const uchar *a, uint a_length, const uchar *b, uint b_length, bool part_key) { if (!part_key) return charset_info->coll->strnncollsp(charset_info, a, a_length, b, b_length); return charset_info->coll->strnncoll(charset_info, a, a_length, b, b_length, part_key); } static int compare_bin(const uchar *a, uint a_length, const uchar *b, uint b_length, bool part_key, bool skip_end_space) { const uint length = std::min(a_length, b_length); const uchar *end = a + length; int flag; while (a < end) if ((flag = (int)*a++ - (int)*b++)) return flag; if (part_key && b_length < a_length) return 0; if (skip_end_space && a_length != b_length) { int swap = 1; /* We are using space compression. We have to check if longer key has next character < ' ', in which case it's less than the shorter key that has an implicit space afterwards. This code is identical to the one in strings/ctype-simple.c:my_strnncollsp_simple */ if (a_length < b_length) { /* put shorter key in a */ a_length = b_length; a = b; swap = -1; /* swap sign of result */ } for (end = a + a_length - length; a < end; a++) { if (*a != ' ') return (*a < ' ') ? -swap : swap; } return 0; } return (int)(a_length - b_length); } /* Compare two keys SYNOPSIS ha_key_cmp() keyseg Array of key segments of key to compare a First key to compare, in format from _mi_pack_key() This is normally key specified by user b Second key to compare. This is always from a row key_length Length of key to compare. This can be shorter than a to just compare sub keys next_flag How keys should be compared If bit SEARCH_FIND is not set the keys includes the row position and this should also be compared diff_pos OUT Number of first keypart where values differ, counting from one. diff_pos[1] OUT (b + diff_pos[1]) points to first value in tuple b that is different from corresponding value in tuple a. EXAMPLES Example1: if the function is called for tuples ('aaa','bbb') and ('eee','fff'), then diff_pos[0] = 1 (as 'aaa' != 'eee') diff_pos[1] = 0 (offset from beginning of tuple b to 'eee' keypart). Example2: if the index function is called for tuples ('aaa','bbb') and ('aaa','fff'), diff_pos[0] = 2 (as 'aaa' != 'eee') diff_pos[1] = 3 (offset from beginning of tuple b to 'fff' keypart, here we assume that first key part is CHAR(3) NOT NULL) NOTES Number-keys can't be splited RETURN VALUES <0 If a < b 0 If a == b >0 If a > b */ #define FCMP(A, B) ((int)(A) - (int)(B)) int ha_key_cmp(const HA_KEYSEG *keyseg, const uchar *a, const uchar *b, uint key_length, uint nextflag, uint *diff_pos) { int flag; int16 s_1, s_2; int32 l_1, l_2; uint32 u_1, u_2; float f_1, f_2; double d_1, d_2; uint next_key_length; const uchar *orig_b = b; *diff_pos = 0; for (; (int)key_length > 0; key_length = next_key_length, keyseg++) { const uchar *end; const uint piks = !(keyseg->flag & HA_NO_SORT); (*diff_pos)++; diff_pos[1] = (uint)(b - orig_b); /* Handle NULL part */ if (keyseg->null_bit) { key_length--; if (*a != *b && piks) { flag = (int)*a - (int)*b; return ((keyseg->flag & HA_REVERSE_SORT) ? -flag : flag); } b++; if (!*a++) /* If key was NULL */ { if (nextflag == (SEARCH_FIND | SEARCH_UPDATE)) nextflag = SEARCH_SAME; /* Allow duplicate keys */ else if (nextflag & SEARCH_NULL_ARE_NOT_EQUAL) { /* This is only used from mi_check() to calculate cardinality. It can't be used when searching for a key as this would cause compare of (a,b) and (b,a) to return the same value. */ return -1; } next_key_length = key_length; continue; /* To next key part */ } } end = a + std::min(keyseg->length, key_length); next_key_length = key_length - keyseg->length; switch ((enum ha_base_keytype)keyseg->type) { case HA_KEYTYPE_TEXT: /* Ascii; Key is converted */ if (keyseg->flag & HA_SPACE_PACK) { uint pack_length; const int a_length = get_key_length(&a); const int b_length = get_key_pack_length(&b, &pack_length); next_key_length = key_length - b_length - pack_length; if (piks && (flag = ha_compare_text( keyseg->charset, a, a_length, b, b_length, (bool)((nextflag & SEARCH_PREFIX) && next_key_length <= 0)))) return ((keyseg->flag & HA_REVERSE_SORT) ? -flag : flag); a += a_length; b += b_length; break; } else { const uint length = (uint)(end - a), a_length = length, b_length = length; if (piks && (flag = ha_compare_text( keyseg->charset, a, a_length, b, b_length, (bool)((nextflag & SEARCH_PREFIX) && next_key_length <= 0)))) return ((keyseg->flag & HA_REVERSE_SORT) ? -flag : flag); a = end; b += length; } break; case HA_KEYTYPE_BINARY: case HA_KEYTYPE_BIT: if (keyseg->flag & HA_SPACE_PACK) { uint pack_length; const int a_length = get_key_length(&a); const int b_length = get_key_pack_length(&b, &pack_length); next_key_length = key_length - b_length - pack_length; if (piks && (flag = compare_bin(a, a_length, b, b_length, (bool)((nextflag & SEARCH_PREFIX) && next_key_length <= 0), true))) return ((keyseg->flag & HA_REVERSE_SORT) ? -flag : flag); a += a_length; b += b_length; break; } else { const uint length = keyseg->length; if (piks && (flag = compare_bin(a, length, b, length, (bool)((nextflag & SEARCH_PREFIX) && next_key_length <= 0), false))) return ((keyseg->flag & HA_REVERSE_SORT) ? -flag : flag); a += length; b += length; } break; case HA_KEYTYPE_VARTEXT1: case HA_KEYTYPE_VARTEXT2: { uint pack_length; const int a_length = get_key_length(&a); const int b_length = get_key_pack_length(&b, &pack_length); next_key_length = key_length - b_length - pack_length; if (piks && (flag = ha_compare_text( keyseg->charset, a, a_length, b, b_length, (bool)((nextflag & SEARCH_PREFIX) && next_key_length <= 0)))) return ((keyseg->flag & HA_REVERSE_SORT) ? -flag : flag); a += a_length; b += b_length; break; } case HA_KEYTYPE_VARBINARY1: case HA_KEYTYPE_VARBINARY2: { uint pack_length; const int a_length = get_key_length(&a); const int b_length = get_key_pack_length(&b, &pack_length); next_key_length = key_length - b_length - pack_length; if (piks && (flag = compare_bin(a, a_length, b, b_length, (bool)((nextflag & SEARCH_PREFIX) && next_key_length <= 0), false))) return ((keyseg->flag & HA_REVERSE_SORT) ? -flag : flag); a += a_length; b += b_length; } break; case HA_KEYTYPE_INT8: { const int i_1 = static_cast(*a); const int i_2 = static_cast(*b); if (piks && (flag = CMP_NUM(i_1, i_2))) return ((keyseg->flag & HA_REVERSE_SORT) ? -flag : flag); a = end; b++; break; } case HA_KEYTYPE_SHORT_INT: s_1 = mi_sint2korr(a); s_2 = mi_sint2korr(b); if (piks && (flag = CMP_NUM(s_1, s_2))) return ((keyseg->flag & HA_REVERSE_SORT) ? -flag : flag); a = end; b += 2; /* sizeof(short int); */ break; case HA_KEYTYPE_USHORT_INT: { uint16 us_1, us_2; us_1 = mi_sint2korr(a); us_2 = mi_sint2korr(b); if (piks && (flag = CMP_NUM(us_1, us_2))) return ((keyseg->flag & HA_REVERSE_SORT) ? -flag : flag); a = end; b += 2; /* sizeof(short int); */ break; } case HA_KEYTYPE_LONG_INT: l_1 = mi_sint4korr(a); l_2 = mi_sint4korr(b); if (piks && (flag = CMP_NUM(l_1, l_2))) return ((keyseg->flag & HA_REVERSE_SORT) ? -flag : flag); a = end; b += 4; /* sizeof(long int); */ break; case HA_KEYTYPE_ULONG_INT: u_1 = mi_sint4korr(a); u_2 = mi_sint4korr(b); if (piks && (flag = CMP_NUM(u_1, u_2))) return ((keyseg->flag & HA_REVERSE_SORT) ? -flag : flag); a = end; b += 4; /* sizeof(long int); */ break; case HA_KEYTYPE_INT24: l_1 = mi_sint3korr(a); l_2 = mi_sint3korr(b); if (piks && (flag = CMP_NUM(l_1, l_2))) return ((keyseg->flag & HA_REVERSE_SORT) ? -flag : flag); a = end; b += 3; break; case HA_KEYTYPE_UINT24: l_1 = mi_uint3korr(a); l_2 = mi_uint3korr(b); if (piks && (flag = CMP_NUM(l_1, l_2))) return ((keyseg->flag & HA_REVERSE_SORT) ? -flag : flag); a = end; b += 3; break; case HA_KEYTYPE_FLOAT: f_1 = mi_float4get(a); f_2 = mi_float4get(b); /* The following may give a compiler warning about floating point comparison not being safe, but this is ok in this context as we are bascily doing sorting */ if (piks && (flag = CMP_NUM(f_1, f_2))) return ((keyseg->flag & HA_REVERSE_SORT) ? -flag : flag); a = end; b += 4; /* sizeof(float); */ break; case HA_KEYTYPE_DOUBLE: d_1 = mi_float8get(a); d_2 = mi_float8get(b); /* The following may give a compiler warning about floating point comparison not being safe, but this is ok in this context as we are bascily doing sorting */ if (piks && (flag = CMP_NUM(d_1, d_2))) return ((keyseg->flag & HA_REVERSE_SORT) ? -flag : flag); a = end; b += 8; /* sizeof(double); */ break; case HA_KEYTYPE_NUM: /* Numeric key */ { int swap_flag = 0; int alength, blength; if (keyseg->flag & HA_REVERSE_SORT) { std::swap(a, b); swap_flag = 1; /* Remember swap of a & b */ end = a + (int)(end - b); } if (keyseg->flag & HA_SPACE_PACK) { alength = *a++; blength = *b++; end = a + alength; next_key_length = key_length - blength - 1; } else { alength = (int)(end - a); blength = keyseg->length; /* remove pre space from keys */ for (; alength && *a == ' '; a++, alength--) ; for (; blength && *b == ' '; b++, blength--) ; } if (piks) { if (*a == '-') { if (*b != '-') return -1; a++; b++; std::swap(a, b); std::swap(alength, blength); swap_flag = 1 - swap_flag; alength--; blength--; end = a + alength; } else if (*b == '-') return 1; while (alength && (*a == '+' || *a == '0')) { a++; alength--; } while (blength && (*b == '+' || *b == '0')) { b++; blength--; } if (alength != blength) return (alength < blength) ? -1 : 1; while (a < end) if (*a++ != *b++) return ((int)a[-1] - (int)b[-1]); } else { b += (end - a); a = end; } if (swap_flag) /* Restore pointers */ std::swap(a, b); break; } case HA_KEYTYPE_LONGLONG: { longlong ll_a, ll_b; ll_a = mi_sint8korr(a); ll_b = mi_sint8korr(b); if (piks && (flag = CMP_NUM(ll_a, ll_b))) return ((keyseg->flag & HA_REVERSE_SORT) ? -flag : flag); a = end; b += 8; break; } case HA_KEYTYPE_ULONGLONG: { ulonglong ll_a, ll_b; ll_a = mi_uint8korr(a); ll_b = mi_uint8korr(b); if (piks && (flag = CMP_NUM(ll_a, ll_b))) return ((keyseg->flag & HA_REVERSE_SORT) ? -flag : flag); a = end; b += 8; break; } case HA_KEYTYPE_END: /* Ready */ goto end; /* diff_pos is incremented */ } } (*diff_pos)++; end: if (!(nextflag & SEARCH_FIND)) { uint i; if (nextflag & (SEARCH_NO_FIND | SEARCH_LAST)) /* Find record after key */ return (nextflag & (SEARCH_BIGGER | SEARCH_LAST)) ? -1 : 1; flag = 0; for (i = keyseg->length; i-- > 0;) { if (*a++ != *b++) { flag = FCMP(a[-1], b[-1]); break; } } if (nextflag & SEARCH_SAME) return (flag); /* read same */ if (nextflag & SEARCH_BIGGER) return (flag <= 0 ? -1 : 1); /* read next */ return (flag < 0 ? -1 : 1); /* read previous */ } return 0; } /* ha_key_cmp */