1
0
mirror of https://github.com/MariaDB/server.git synced 2026-10-23 18:49:29 +03:00

Add detection of partial matches for strnncoll

This is achived by changing the 'is_prefix' parameter to strncoll
from a my_bool to my_bool*. This parameter is null if not specified by
the caller. If the is_prefix parameter is set to point to a my_bool
variable, this variable will be set to 1 if the second argument is
a prefix of the first.

This is needed by TO_DATE to be able to detect partial uniqiue matches of
months etc.  find_type() did this for latin1 strings, but we did not
do this for utf8mb4 strings.
This patch fixes this incompatibility.
This commit is contained in:
Monty
2026-01-13 19:47:07 +02:00
committed by Sergei Golubchik
parent bd74c8cf7c
commit 782b103925
16 changed files with 159 additions and 42 deletions

View File

@@ -557,7 +557,7 @@ struct my_collation_handler_st
my_bool (*init)(struct charset_info_st *, MY_CHARSET_LOADER *);
/* Collation routines */
int (*strnncoll)(CHARSET_INFO *,
const uchar *, size_t, const uchar *, size_t, my_bool);
const uchar *, size_t, const uchar *, size_t, my_bool*);
int (*strnncollsp)(CHARSET_INFO *,
const uchar *, size_t, const uchar *, size_t);
/*
@@ -716,6 +716,7 @@ struct my_charset_handler_st
size_t (*numchars)(CHARSET_INFO *, const char *b, const char *e);
size_t (*charpos)(CHARSET_INFO *, const char *b, const char *e,
size_t pos);
/* Length without trailing space */
size_t (*lengthsp)(CHARSET_INFO *, const char *ptr, size_t length);
size_t (*numcells)(CHARSET_INFO *, const char *b, const char *e);
@@ -1146,22 +1147,23 @@ struct charset_info_st
}
int strnncoll(const LEX_CSTRING a, const LEX_CSTRING b,
my_bool b_is_prefix= FALSE) const
my_bool *b_is_prefix= 0) const
{
DBUG_ASSERT(is_valid_string(a));
DBUG_ASSERT(is_valid_string(b));
return (coll->strnncoll)(this,
(const uchar *) a.str, a.length,
(const uchar *) b.str, b.length, b_is_prefix);
(const uchar *) b.str, b.length,
b_is_prefix);
}
int strnncoll(const uchar *a, size_t alen,
const uchar *b, size_t blen, my_bool b_is_prefix= FALSE) const
const uchar *b, size_t blen, my_bool *b_is_prefix= 0) const
{
return (coll->strnncoll)(this, a, alen, b, blen, b_is_prefix);
}
int strnncoll(const char *a, size_t alen,
const char *b, size_t blen, my_bool b_is_prefix= FALSE) const
const char *b, size_t blen, my_bool *b_is_prefix= 0) const
{
return (coll->strnncoll)(this,
(const uchar *) a, alen,
@@ -1478,7 +1480,7 @@ static inline int
my_ci_strnncoll(CHARSET_INFO *ci,
const uchar *a, size_t alen,
const uchar *b, size_t blen,
my_bool b_is_prefix)
my_bool *b_is_prefix)
{
return (ci->coll->strnncoll)(ci, a, alen, b, blen, b_is_prefix);
}
@@ -1635,7 +1637,7 @@ const uint16 *my_cs_contraction2_weight(CHARSET_INFO *cs, my_wc_t wc1,
/* declarations for simple charsets */
extern int my_strnncoll_simple(CHARSET_INFO *, const uchar *, size_t,
const uchar *, size_t, my_bool);
const uchar *, size_t, my_bool*);
extern int my_strnncollsp_simple(CHARSET_INFO *, const uchar *, size_t,
const uchar *, size_t);

View File

@@ -140,7 +140,7 @@ static inline int ha_compare_char_varying(CHARSET_INFO *charset_info,
b, b_length);
return charset_info->coll->strnncoll(charset_info,
a, a_length,
b, b_length, TRUE/*prefix*/);
b, b_length, &b_is_prefix);
}
@@ -190,7 +190,7 @@ static inline int ha_compare_char_fixed(CHARSET_INFO *charset_info,
MY_STRNNCOLLSP_NCHARS_EMULATE_TRIMMED_TRAILING_SPACES);
return charset_info->coll->strnncoll(charset_info,
a, a_length,
b, b_length, TRUE/*prefix*/);
b, b_length, &b_is_prefix);
}
@@ -218,10 +218,11 @@ static inline int ha_compare_word_prefix(CHARSET_INFO *charset_info,
const uchar *a, size_t a_length,
const uchar *b, size_t b_length)
{
my_bool b_is_prefix;
return charset_info->coll->strnncoll(charset_info,
a, a_length,
b, b_length,
TRUE/*b_is_prefix*/);
&b_is_prefix);
}

View File

@@ -618,5 +618,10 @@ Warnings:
Note 1003 select `test`.`t1`.`c1` AS `c1` from `test`.`t1` where concat(`test`.`t1`.`c1`) = 'ä' and concat(`test`.`t1`.`c1`) like 'ae'
DROP TABLE IF EXISTS t1;
#
# MDEV-38608 TO_DATE: Server crash in my_uca_strnncoll_multilevel_generic
#
CREATE TABLE t1 (f ENUM('foo','bar')) COLLATE uca1400_ai_cs;
DROP TABLE t1;
#
# End of 10.0 tests
#

View File

@@ -47,6 +47,13 @@ SET NAMES utf8mb4 COLLATE uca1400_ai_ci;
--source include/ctype_like_cond_propagation.inc
--source include/ctype_like_cond_propagation_utf8_german.inc
--echo #
--echo # MDEV-38608 TO_DATE: Server crash in my_uca_strnncoll_multilevel_generic
--echo #
CREATE TABLE t1 (f ENUM('foo','bar')) COLLATE uca1400_ai_cs;
DROP TABLE t1;
--echo #
--echo # End of 10.0 tests
--echo #

View File

@@ -9441,10 +9441,11 @@ bool is_secure_file_path(char *path)
}
else
{
my_bool use_prefix;
if (files_charset_info->strnncoll(buff2, strlen(buff2),
opt_secure_file_priv,
opt_secure_file_priv_len,
TRUE))
&use_prefix))
return FALSE;
}
return TRUE;

View File

@@ -2383,6 +2383,7 @@ static bool is_mysql_datadir_path(const char *path)
convert_dirname(mysql_data_dir, mysql_unpacked_real_data_home, NullS);
size_t mysql_data_home_len= dirname_length(mysql_data_dir);
size_t path_len = dirname_length(path_dir);
my_bool part_matched;
if (path_len < mysql_data_home_len)
return true;
@@ -2393,7 +2394,7 @@ static bool is_mysql_datadir_path(const char *path)
return(files_charset_info->strnncoll((uchar *) path_dir, path_len,
(uchar *) mysql_data_dir,
mysql_data_home_len,
TRUE));
&part_matched));
}
/*********************************************************************//**

View File

@@ -258,9 +258,10 @@ inline bool cmp_dfield_dfield_eq_prefix(const dfield_t *dfield1,
uint cs_num= dtype_get_charset_coll(type->prtype);
CHARSET_INFO *cs= get_charset(cs_num, MYF(MY_WME));
my_bool use_prefix;
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);
dfield_get_len(dfield2), &use_prefix);
}

View File

@@ -259,6 +259,11 @@ Simple scanner routines
-----------------------
scan() - to skip leading spaces in the given string.
Used when a string value is inserted into a numeric field.
the last parameter is one of:
MY_SEQ_INTTAIL ; Ignore '.' and a set of '0'
MY_SEQ_SPACES ; Skip over pre-space
MY_SEQ_NOSPACES; ; Skip over non space characters

View File

@@ -83,11 +83,23 @@ my_coll_init_8bit_bin(struct charset_info_st *cs,
static int my_strnncoll_binary(CHARSET_INFO * cs __attribute__((unused)),
const uchar *s, size_t slen,
const uchar *t, size_t tlen,
my_bool t_is_prefix)
my_bool *t_is_prefix)
{
size_t len=MY_MIN(slen,tlen);
size_t len= MY_MIN(slen,tlen);
int cmp= len ? memcmp(s, t, len) : 0;
return cmp ? cmp : (int)((t_is_prefix ? len : slen) - tlen);
if (!t_is_prefix)
{
if (cmp)
return cmp;
return (int) (slen - tlen);
}
if (cmp)
{
*t_is_prefix= 0;
return cmp;
}
return !(*t_is_prefix= tlen <= slen);
}
@@ -144,11 +156,23 @@ static int my_strnncollsp_nchars_binary(CHARSET_INFO * cs __attribute__((unused)
static int my_strnncoll_8bit_bin(CHARSET_INFO * cs __attribute__((unused)),
const uchar *s, size_t slen,
const uchar *t, size_t tlen,
my_bool t_is_prefix)
my_bool *t_is_prefix)
{
size_t len=MY_MIN(slen,tlen);
size_t len= MY_MIN(slen,tlen);
int cmp= len ? memcmp(s, t, len) : 0;
return cmp ? cmp : (int)((t_is_prefix ? len : slen) - tlen);
if (!t_is_prefix)
{
if (cmp)
return cmp;
return (int) (slen - tlen);
}
if (cmp)
{
*t_is_prefix= 0;
return cmp;
}
return !(*t_is_prefix= tlen <= slen);
}
@@ -232,7 +256,7 @@ static int my_strnncollsp_8bit_nopad_bin(CHARSET_INFO * cs
const uchar *a, size_t a_length,
const uchar *b, size_t b_length)
{
return my_strnncoll_8bit_bin(cs, a, a_length, b, b_length, FALSE);
return my_strnncoll_8bit_bin(cs, a, a_length, b, b_length, 0);
}

View File

@@ -218,14 +218,20 @@ while (1) \
static int my_strnncoll_czech(CHARSET_INFO *cs __attribute__((unused)),
const uchar *s1, size_t len1,
const uchar *s2, size_t len2,
my_bool s2_is_prefix)
my_bool *s2_is_prefix)
{
int v1, v2;
const uchar *p1, * p2, * store1, * store2;
int pass1 = 0, pass2 = 0;
if (s2_is_prefix && len1 > len2)
len1=len2;
if (s2_is_prefix)
{
*s2_is_prefix= 0;
if (len1 > len2)
{
*s2_is_prefix= 1;
len1= len2;
}
}
p1 = s1; p2 = s2;
store1 = s1; store2 = s2;
@@ -236,7 +242,11 @@ static int my_strnncoll_czech(CHARSET_INFO *cs __attribute__((unused)),
NEXT_CMP_VALUE(s1, p1, store1, pass1, v1, (int)len1);
NEXT_CMP_VALUE(s2, p2, store2, pass2, v2, (int)len2);
if ((diff = v1 - v2))
{
if (s2_is_prefix)
*s2_is_prefix= 0;
return diff;
}
}
while (v1);
return 0;

View File

@@ -589,11 +589,13 @@ static const uchar combo2map[]={
static int my_strnncoll_latin1_de(CHARSET_INFO *cs __attribute__((unused)),
const uchar *a, size_t a_length,
const uchar *b, size_t b_length,
my_bool b_is_prefix)
my_bool *b_is_prefix)
{
const uchar *a_end= a + a_length;
const uchar *b_end= b + b_length;
uchar a_char, a_extend= 0, b_char, b_extend= 0;
if (b_is_prefix)
*b_is_prefix= 0;
while ((a < a_end || a_extend) && (b < b_end || b_extend))
{
@@ -622,7 +624,7 @@ static int my_strnncoll_latin1_de(CHARSET_INFO *cs __attribute__((unused)),
A simple test of string lengths won't work -- we test to see
which string ran out first
*/
return ((a < a_end || a_extend) ? (b_is_prefix ? 0 : 1) :
return ((a < a_end || a_extend) ? (b_is_prefix ? (*b_is_prefix= 1, 0) : 1) :
(b < b_end || b_extend) ? -1 : 0);
}

View File

@@ -142,22 +142,40 @@ my_strnxfrm_simple_nopad(CHARSET_INFO * cs,
int my_strnncoll_simple(CHARSET_INFO * cs, const uchar *s, size_t slen,
const uchar *t, size_t tlen,
my_bool t_is_prefix)
my_bool *t_is_prefix)
{
size_t len = ( slen > tlen ) ? tlen : slen;
const uchar *map= cs->sort_order;
if (t_is_prefix && slen > tlen)
slen=tlen;
my_bool dummy;
if (t_is_prefix)
{
*t_is_prefix= 0;
if (slen > tlen)
{
*t_is_prefix= 1;
slen= tlen;
}
}
else
t_is_prefix= &dummy;
while (len--)
{
if (map[*s++] != map[*t++])
{
*t_is_prefix= 0;
return ((int) map[s[-1]] - (int) map[t[-1]]);
}
}
/*
We can't use (slen - tlen) here as the result may be outside of the
precision of a signed int
*/
return slen > tlen ? 1 : slen < tlen ? -1 : 0 ;
if (tlen == slen)
return 0;
*t_is_prefix= 0;
return slen > tlen ? 1 : -1;
}

View File

@@ -516,14 +516,21 @@ static
int my_strnncoll_tis620(CHARSET_INFO *cs __attribute__((unused)),
const uchar *s1, size_t len1,
const uchar *s2, size_t len2,
my_bool s2_is_prefix)
my_bool *s2_is_prefix)
{
uchar buf[80] ;
uchar *tc1, *tc2;
int i;
if (s2_is_prefix && len1 > len2)
len1= len2;
if (s2_is_prefix)
{
*s2_is_prefix= 0;
if (len1 > len2)
{
*s2_is_prefix= 1;
len1= len2;
}
}
tc1= buf;
if ((len1 + len2 +2) > (int) sizeof(buf))
@@ -538,6 +545,8 @@ int my_strnncoll_tis620(CHARSET_INFO *cs __attribute__((unused)),
i= strcmp((char*)tc1, (char*)tc2);
if (tc1 != buf)
my_free(tc1);
if (i && s2_is_prefix)
*s2_is_prefix= 0;
return i;
}

View File

@@ -89,7 +89,7 @@ MY_FUNCTION_NAME(strnncoll_onelevel)(CHARSET_INFO *cs,
const MY_UCA_WEIGHT_LEVEL *level,
const uchar *s, size_t slen,
const uchar *t, size_t tlen,
my_bool t_is_prefix)
my_bool *t_is_prefix)
{
my_uca_scanner sscanner;
my_uca_scanner tscanner;
@@ -119,7 +119,12 @@ MY_FUNCTION_NAME(strnncoll_onelevel)(CHARSET_INFO *cs,
t_res= MY_FUNCTION_NAME(scanner_next)(&tscanner, &param);
} while ( s_res == t_res && s_res >0);
return (t_is_prefix && t_res < 0) ? 0 : (s_res - t_res);
if (t_is_prefix)
{
if ((*t_is_prefix= s_res != t_res && t_res < 0))
return 0;
}
return (s_res - t_res);
}
@@ -130,7 +135,7 @@ static int
MY_FUNCTION_NAME(strnncoll)(CHARSET_INFO *cs,
const uchar *s, size_t slen,
const uchar *t, size_t tlen,
my_bool t_is_prefix)
my_bool *t_is_prefix)
{
return MY_FUNCTION_NAME(strnncoll_onelevel)(cs, &cs->uca->level[0],
s, slen, t, tlen, t_is_prefix);
@@ -139,14 +144,19 @@ MY_FUNCTION_NAME(strnncoll)(CHARSET_INFO *cs,
/*
Multi-level, PAD SPACE.
Note that t_is_prefix is set from the last call to strnncoll_onelevel
*/
static int
MY_FUNCTION_NAME(strnncoll_multilevel)(CHARSET_INFO *cs,
const uchar *s, size_t slen,
const uchar *t, size_t tlen,
my_bool t_is_prefix)
my_bool *t_is_prefix)
{
uint i, level_flags= cs->levels_for_order;
if (t_is_prefix && *t_is_prefix)
*t_is_prefix= 0;
for (i= 0; level_flags; i++, level_flags>>= 1)
{
int ret;

View File

@@ -452,15 +452,22 @@ static const struct wordvalue doubles[] = {
static int my_strnncoll_win1250ch(CHARSET_INFO *cs __attribute__((unused)),
const uchar *s1, size_t len1,
const uchar *s2, size_t len2,
my_bool s2_is_prefix)
my_bool *s2_is_prefix)
{
int v1, v2;
const uchar *p1, * p2;
int pass1 = 0, pass2 = 0;
int diff;
if (s2_is_prefix && len1 > len2)
len1=len2;
if (s2_is_prefix)
{
*s2_is_prefix= 0;
if (len1 > len2)
{
*s2_is_prefix= 1;
len1= len2;
}
}
p1 = s1; p2 = s2;
@@ -469,7 +476,11 @@ static int my_strnncoll_win1250ch(CHARSET_INFO *cs __attribute__((unused)),
NEXT_CMP_VALUE(s1, p1, pass1, v1, (int)len1);
NEXT_CMP_VALUE(s2, p2, pass2, v2, (int)len2);
if ((diff = v1 - v2))
{
if (s2_is_prefix)
*s2_is_prefix= 0;
return diff;
}
} while (v1);
return 0;
}

View File

@@ -218,16 +218,19 @@ bad:
@param b - the right string
@param b_length - the length of the right string
@param b_is_prefix - if the caller wants to check if "b" is a prefix of "a"
*b_is_prefix is set to 1 if it was a prefix match
@return - the comparison result
*/
static int
MY_FUNCTION_NAME(strnncoll)(CHARSET_INFO *cs __attribute__((unused)),
const uchar *a, size_t a_length,
const uchar *b, size_t b_length,
my_bool b_is_prefix)
my_bool *b_is_prefix)
{
const uchar *a_end= a + a_length;
const uchar *b_end= b + b_length;
if (b_is_prefix)
*b_is_prefix= 0;
for ( ; ; )
{
int a_weight, b_weight, res;
@@ -271,7 +274,14 @@ MY_FUNCTION_NAME(strnncoll)(CHARSET_INFO *cs __attribute__((unused)),
return b_wlen ? -1 : 0;
if (!b_wlen)
return b_is_prefix ? 0 : +1;
{
if (b_is_prefix)
{
*b_is_prefix= 1; /* Prefix match */
return 0;
}
return +1;
}
if ((res= (a_weight - b_weight)))
return res;