/* Copyright (c) 2003, 2012, Oracle and/or its affiliates. All rights reserved.
Copyright ( c ) 2009 , 2020 , 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 */
/* Some useful string utility functions used by the MySQL server */
#include "mariadb.h"
#include "sql_priv.h"
#include "unireg.h"
#include "strfunc.h"
#include "sql_class.h"
#include "typelib.h" // TYPELIB
#include "m_ctype.h" // my_charset_latin1
#include "mysqld.h" // system_charset_info
/*
Return bitmap for strings used in a set
SYNOPSIS
find_set ( )
lib Strings in set
str Strings of set - strings separated by ' , '
err_pos If error , set to point to start of wrong set string
err_len If error , set to the length of wrong set string
set_warning Set to 1 if some string in set couldn ' t be used
NOTE
We delete all end space from str before comparison
RETURN
bitmap of all sets found in x .
set_warning is set to 1 if there was any sets that couldn ' t be set
*/
static const char field_separator=',' ;
ulonglong find_set(const TYPELIB *lib,
const char *str, size_t length, CHARSET_INFO *cs,
char **err_pos, uint *err_len, bool *set_warning)
{
CHARSET_INFO *strip= cs ? cs : &my_charset_latin1;
const char *end= str + strip->lengthsp(str, length);
ulonglong found= 0 ;
*err_pos= 0 ; // No error yet
*err_len= 0 ;
if (str != end)
{
const char *start= str;
for (;;)
{
const char *pos= start;
uint var_len;
int mblen= 1 ;
if (cs && cs->mbminlen > 1 )
{
for ( ; pos < end; pos+= mblen)
{
my_wc_t wc;
if ((mblen= cs->mb_wc(&wc, (const uchar *) pos,
(const uchar *) end)) < 1 )
mblen= 1 ; // Not to hang on a wrong multibyte sequence
else if (wc == (my_wc_t) field_separator)
break ;
}
}
else
for (; pos != end && *pos != field_separator; pos++) ;
var_len= (uint) (pos - start);
uint find= cs ? find_type2(lib, start, var_len, 0 , cs) :
find_type(lib, start, var_len, (bool ) 0 );
if (unlikely(!find))
{
if (*err_len == 0 )
{
// report the first error with length > 0
*err_pos= (char *) start;
*err_len= var_len;
*set_warning= 1 ;
}
}
else if (find <= sizeof (longlong) * 8 )
found|= 1 ULL << (find - 1 );
if (pos >= end)
break ;
start= pos + mblen;
}
}
return found;
}
/*
Function to find a string in a TYPELIB
( similar to find_type ( ) of mysys / typelib . c )
SYNOPSIS
find_type ( )
lib TYPELIB ( struct of pointer to values + count )
find String to find
length Length of string to find
part_match Allow part matching of value
RETURN
0 error
> 0 position in TYPELIB - > type_names + 1
*/
uint find_type(const TYPELIB *lib, const char *find, size_t length,
bool part_match)
{
uint found_count=0 , found_pos=0 ;
const char *end= find+length;
const char *i;
const char *j;
for (uint pos=0 ; (j=lib->type_names[pos++]) ; )
{
for (i=find ; i != end &&
my_toupper(system_charset_info,*i) ==
my_toupper(system_charset_info,*j) ; i++, j++) ;
if (i == end)
{
if (! *j)
return (pos);
found_count++;
found_pos= pos;
}
}
return (found_count == 1 && part_match ? found_pos : 0 );
}
/*
Find a string in a list of strings according to collation
SYNOPSIS
find_type2 ( )
lib TYPELIB ( struct of pointer to values + count )
x String to find
length String length
part_match Allow prefix matching of typelib .
cs Character set + collation to use for comparison
NOTES
RETURN
0 No matching value
* part_match will be set if there was multiple prefix matches .
> 0 Offset + 1 in typelib for matched string . * part_match has random value
*/
uint find_type2(const TYPELIB *typelib, const char *x,
size_t length, my_bool *part_match, CHARSET_INFO *cs)
{
int found_count= 0 , found_pos= 0 ;
const char *j;
DBUG_ENTER("find_type2" );
DBUG_PRINT("enter" ,("x: '%.*s' lib: %p" , (int )length, x, typelib));
if (part_match)
*part_match= 0 ;
if (!typelib->count)
{
DBUG_PRINT("exit" ,("no count" ));
DBUG_RETURN(0 );
}
for (uint pos=0 ; (j=typelib->type_names[pos]) ; pos++)
{
if (!cs->strnncoll(j, typelib->type_lengths[pos], x, length, part_match))
{
if (!part_match)
DBUG_RETURN(pos+1 );
found_count++;
found_pos= pos;
}
}
if (!part_match)
DBUG_RETURN(0 );
*part_match= found_count > 0 ;
DBUG_RETURN(found_count == 1 ? found_pos+1 : 0 );
} /* find_type2 */
/*
Un - hex all elements in a typelib
SYNOPSIS
unhex_type2 ( )
interval TYPELIB ( struct of pointer to values + lengths + count )
NOTES
RETURN
N / A
*/
void unhex_type2(TYPELIB *interval)
{
for (uint pos= 0 ; pos < interval->count; pos++)
{
char *from, *to;
for (from= to= (char *) interval->type_names[pos]; *from; )
{
/*
Note , hexchar_to_int ( * from + + ) doesn ' t work
one some compilers , e . g . IRIX . Looks like a compiler
bug in inline functions in combination with arguments
that have a side effect . So , let ' s use from [ 0 ] and from [ 1 ]
and increment ' from ' by two later .
*/
*to++= (char ) (hexchar_to_int(from[0 ]) << 4 ) +
hexchar_to_int(from[1 ]);
from+= 2 ;
}
interval->type_lengths[pos] /= 2 ;
}
}
/*
Check if the first word in a string is one of the ones in TYPELIB
SYNOPSIS
check_word ( )
cs Character set
lib TYPELIB
val String to check
end End of input
end_of_word points to the first character after the matched string
NOTES
works for multi - byte characters
- matches the longest prefix , whether it ' s letters , digits or punctuation
( e . g . see month names for Japanese and Vietnamese )
- cs is utf8mb4_general_ci , because that ' s the charset of strings in sql_locale . cc
If multiple entries match as prefixes of the same length , the result is
considered ambiguous ( returns 0 ) unless one of them is an exact match .
RETURN
0 No matching value
> 0 lib - > type_names [ # - 1 ] matched
end_of_word will point to separator character / end in ' val '
*/
uint check_word(CHARSET_INFO *cs, TYPELIB *lib, const char *val,
const char *end, const char **end_of_word)
{
uint prefix_match= 0 , prefix_len= 0 , exact= 0 ;
for (uint i=0 ; i < lib->count; i++)
{
const char *s= lib->type_names[i], *e= s + lib->type_lengths[i], *v= val;
my_wc_t wcv_prev;
int lv_prev=0 ;
while (v < end && s < e)
{
my_wc_t wcs, wcv;
int ls, lv;
if ((ls= cs->mb_wc(&wcs, (const uchar*) s, (const uchar*) e)) <= 0 ||
(lv= cs->mb_wc(&wcv, (const uchar*) v, (const uchar*) end)) <= 0 )
return 0 ; // invalid character
if (cs->strnncoll(s, ls, v, lv))
break ;
lv_prev= lv;
wcv_prev= wcv;
s+= ls;
v+= lv;
}
if (lv_prev == 1 && my_isspace(cs, wcv_prev))
v--; // don't include an endspace
else if (lv_prev > 1 || (lv_prev == 1 && my_isalnum(cs, wcv_prev)))
{
my_wc_t wcv;
int lv= cs->mb_wc(&wcv, (const uchar*) v, (const uchar*) end);
if (lv > 1 || (lv == 1 && my_isalnum(cs, wcv)))
continue ; // won't break mid-word
}
// prefix match of the name
if ((uint) (v - val) > prefix_len || s >= e)
{
exact= s >= e;
prefix_match= i + 1 ;
prefix_len= (uint) (v - val);
}
else if (prefix_len && (uint) (v - val) == prefix_len && !exact)
prefix_match= 0 ; // ambiguous
}
if (prefix_match)
*end_of_word= val + prefix_len;
return prefix_match;
}
/*
Converts a string between character sets
SYNOPSIS
strconvert ( )
from_cs source character set
from source , a null terminated string
to destination buffer
to_length destination buffer length
NOTES
' to ' is always terminated with a ' \ 0 ' character .
If there is no enough space to convert whole string ,
only prefix is converted , and terminated with ' \ 0 ' .
RETURN VALUES
result string length
*/
uint strconvert(CHARSET_INFO *from_cs, const char *from, size_t from_length,
CHARSET_INFO *to_cs, char *to, size_t to_length, uint *errors)
{
int cnvres;
my_wc_t wc;
char *to_start= to;
uchar *to_end= (uchar*) to + to_length - 1 ;
const uchar *from_end= (const uchar*) from + from_length;
my_charset_conv_mb_wc mb_wc= from_cs->cset->mb_wc;
my_charset_conv_wc_mb wc_mb= to_cs->cset->wc_mb;
uint error_count= 0 ;
while (1 )
{
if ((cnvres= (*mb_wc)(from_cs, &wc,
(uchar*) from, from_end)) > 0 )
{
if (!wc)
break ;
from+= cnvres;
}
else if (cnvres == MY_CS_ILSEQ)
{
error_count++;
from++;
wc= '?' ;
}
else
break ; // Impossible char.
outp:
if ((cnvres= (*wc_mb)(to_cs, wc, (uchar*) to, to_end)) > 0 )
to+= cnvres;
else if (cnvres == MY_CS_ILUNI && wc != '?' )
{
error_count++;
wc= '?' ;
goto outp;
}
else
break ;
}
*to= '\0' ;
*errors= error_count;
return (uint32) (to - to_start);
}
/*
Searches for a LEX_STRING in an LEX_STRING array .
SYNOPSIS
find_string_in_array ( )
heap The array
needle The string to search for
NOTE
The last LEX_STRING in the array should have str member set to NULL
RETURN VALUES
- 1 Not found
> = 0 Ordinal position
*/
int find_string_in_array(LEX_CSTRING * const haystack, LEX_CSTRING * const needle,
CHARSET_INFO * const cs)
{
const LEX_CSTRING *pos;
for (pos= haystack; pos->str; pos++)
if (!cs->strnncollsp(pos->str, pos->length,
needle->str, needle->length))
{
return (int )(pos - haystack);
}
return -1 ;
}
const char *set_to_string(THD *thd, LEX_CSTRING *result, ulonglong set,
const char *lib[])
{
char buff[STRING_BUFFER_USUAL_SIZE*8 ];
String tmp(buff, sizeof (buff), &my_charset_latin1);
LEX_CSTRING unused;
if (!result)
result= &unused;
tmp.length(0 );
for (uint i= 0 ; set; i++, set >>= 1 )
if (set & 1 ) {
tmp.append(lib[i], strlen(lib[i]));
tmp.append(',' );
}
if (tmp.length())
{
result->str= thd->strmake(tmp.ptr(), tmp.length()-1 );
result->length= tmp.length()-1 ;
}
else
{
result->str= const_cast <char *>("" );
result->length= 0 ;
}
return result->str;
}
const char *flagset_to_string(THD *thd, LEX_CSTRING *result, ulonglong set,
const char *lib[])
{
char buff[STRING_BUFFER_USUAL_SIZE*8 ];
String tmp(buff, sizeof (buff), &my_charset_latin1);
LEX_CSTRING unused;
if (!result) result= &unused;
tmp.length(0 );
// note that the last element is always "default", and it's ignored below
for (uint i= 0 ; lib[i+1 ]; i++, set >>= 1 )
{
tmp.append(lib[i], strlen(lib[i]));
if (set & 1 )
tmp.append(STRING_WITH_LEN("=on," ));
else
tmp.append(STRING_WITH_LEN("=off," ));
}
result->str= thd->strmake(tmp.ptr(), tmp.length()-1 );
result->length= tmp.length()-1 ;
return result->str;
}
Messung V0.5 in Prozent C=95 H=82 G=88
¤ Dauer der Verarbeitung: 0.15 Sekunden
(vorverarbeitet am 2026-10-08)
¤
*© Formatika GbR, Deutschland