用于EagleEye3.0 规则集漏报和误报测试的示例项目,项目收集于github和gitee
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

5033 lines
155 KiB

5 months ago
/*
Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved.
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 also distributed 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 included with MySQL.
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 sql/item_strfunc.cc
@brief
This file defines all string Items (e.g. CONCAT).
*/
#include "sql/item_strfunc.h"
#include <stdio.h>
#include <string.h>
#include <zconf.h>
#include <zlib.h>
#include <algorithm>
#include <atomic>
#include <cmath> // std::isfinite
#include <memory>
#include <ostream>
#include <string>
#include <utility>
#include "base64.h" // base64_encode_max_arg_length
#include "decimal.h"
#include "m_string.h"
#include "my_aes.h" // MY_AES_IV_SIZE
#include "my_byteorder.h"
#include "my_compiler.h"
#include "my_dbug.h"
#include "my_dir.h" // For my_stat
#include "my_io.h"
#include "my_macros.h"
#include "my_md5.h" // MD5_HASH_SIZE
#include "my_md5_size.h"
#include "my_rnd.h" // my_rand_buffer
#include "my_sqlcommand.h"
#include "my_sys.h"
#include "my_systime.h"
#include "myisampack.h"
#include "mysql/psi/mysql_file.h"
#include "mysql/psi/mysql_mutex.h"
#include "mysql/service_mysql_password_policy.h"
#include "mysqld_error.h"
#include "mysys_err.h"
#include "password.h" // my_make_scrambled_password
#include "sha1.h" // SHA1_HASH_SIZE
#include "sha2.h"
#include "sql/auth/auth_acls.h"
#include "sql/auth/auth_common.h" // check_password_policy
#include "sql/auth/sql_security_ctx.h"
#include "sql/current_thd.h" // current_thd
#include "sql/dd/dd_event.h" // dd::get_old_interval_type
#include "sql/dd/dd_table.h" // is_encrypted
#include "sql/dd/info_schema/metadata.h" // dd::info_schema::get_I_S_view...
#include "sql/dd/info_schema/table_stats.h"
#include "sql/dd/info_schema/tablespace_stats.h"
#include "sql/dd/properties.h" // dd::Properties
#include "sql/dd/string_type.h"
#include "sql/dd/types/event.h" // dd::Event::enum_interval_field
#include "sql/dd_sql_view.h" // push_view_warning_or_error
#include "sql/derror.h" // ER_THD
#include "sql/error_handler.h" // Internal_error_handler
#include "sql/events.h" // Events::reconstruct_interval_expression
#include "sql/handler.h"
#include "sql/my_decimal.h"
#include "sql/mysqld.h" // binary_keyword etc
#include "sql/resourcegroups/resource_group_mgr.h" // num_vcpus
#include "sql/rpl_gtid.h"
#include "sql/sql_base.h"
#include "sql/sql_class.h" // THD
#include "sql/sql_error.h"
#include "sql/sql_lex.h"
#include "sql/sql_locale.h" // my_locale_by_name
#include "sql/sql_show.h" // grant_types
#include "sql/strfunc.h" // hexchar_to_int
#include "sql/system_variables.h"
#include "sql/table.h"
#include "sql/val_int_compare.h" // Integer_value
#include "template_utils.h"
#include "typelib.h"
using std::max;
using std::min;
/*
For the Items which have only val_str_ascii() method
and don't have their own "native" val_str(),
we provide a "wrapper" method to convert from ASCII
to Item character set when it's necessary.
Conversion happens only in case of "tricky" Item character set (e.g. UCS2).
Normally conversion does not happen, and val_str_ascii() is immediately
returned instead.
*/
String *Item_str_func::val_str_from_val_str_ascii(String *str, String *str2) {
DBUG_ASSERT(fixed == 1);
if (!(collation.collation->state & MY_CS_NONASCII)) {
String *res = val_str_ascii(str);
if (res) res->set_charset(collation.collation);
return res;
}
DBUG_ASSERT(str != str2);
uint errors;
String *res = val_str_ascii(str);
if (!res) return 0;
if ((null_value = str2->copy(res->ptr(), res->length(), &my_charset_latin1,
collation.collation, &errors)))
return 0;
return str2;
}
bool Item_str_func::fix_fields(THD *thd, Item **ref) {
bool res = Item_func::fix_fields(thd, ref);
/*
In Item_str_func::check_well_formed_result() we may set null_value
flag on the same condition as in test() below.
*/
maybe_null = (maybe_null || thd->is_strict_mode());
return res;
}
my_decimal *Item_str_func::val_decimal(my_decimal *decimal_value) {
DBUG_ASSERT(fixed == 1);
char buff[64];
String *res, tmp(buff, sizeof(buff), &my_charset_bin);
res = val_str(&tmp);
if (!res) return 0;
(void)str2my_decimal(E_DEC_FATAL_ERROR, res->ptr(), res->length(),
res->charset(), decimal_value);
return decimal_value;
}
double Item_str_func::val_real() {
DBUG_ASSERT(fixed == 1);
int err_not_used;
const char *end_not_used;
char buff[64];
String *res, tmp(buff, sizeof(buff), &my_charset_bin);
res = val_str(&tmp);
return res ? my_strntod(res->charset(), res->ptr(), res->length(),
&end_not_used, &err_not_used)
: 0.0;
}
longlong Item_str_func::val_int() {
DBUG_ASSERT(fixed == 1);
int err;
char buff[22];
String *res, tmp(buff, sizeof(buff), &my_charset_bin);
res = val_str(&tmp);
return (res ? my_strntoll(res->charset(), res->ptr(), res->length(), 10, NULL,
&err)
: (longlong)0);
}
String *Item_func_md5::val_str_ascii(String *str) {
DBUG_ASSERT(fixed == 1);
String *sptr = args[0]->val_str(str);
str->set_charset(&my_charset_bin);
if (sptr) {
uchar digest[MD5_HASH_SIZE] = {0};
null_value = 0;
int retval = compute_md5_hash((char *)digest, sptr->ptr(), sptr->length());
if (retval == 1) {
push_warning_printf(current_thd, Sql_condition::SL_WARNING,
ER_SSL_FIPS_MODE_ERROR,
ER_THD(current_thd, ER_SSL_FIPS_MODE_ERROR),
"FIPS mode ON/STRICT: MD5 digest is not supported.");
}
if (str->alloc(32)) // Ensure that memory is free
{
null_value = 1;
return 0;
}
array_to_hex(str->ptr(), digest, MD5_HASH_SIZE);
str->length((uint)32);
return str;
}
null_value = 1;
return 0;
}
/*
The MD5()/SHA() functions treat their parameter as being a case sensitive.
Thus we set binary collation on it so different instances of MD5() will be
compared properly.
*/
static CHARSET_INFO *get_checksum_charset(const char *csname) {
CHARSET_INFO *cs = get_charset_by_csname(csname, MY_CS_BINSORT, MYF(0));
if (!cs) {
// Charset has no binary collation: use my_charset_bin.
cs = &my_charset_bin;
}
return cs;
}
bool Item_func_md5::resolve_type(THD *) {
CHARSET_INFO *cs = get_checksum_charset(args[0]->collation.collation->csname);
args[0]->collation.set(cs, DERIVATION_COERCIBLE);
set_data_type_string(32, default_charset());
return false;
}
String *Item_func_sha::val_str_ascii(String *str) {
DBUG_ASSERT(fixed == 1);
String *sptr = args[0]->val_str(str);
str->set_charset(&my_charset_bin);
if (sptr) /* If we got value different from NULL */
{
/* Temporary buffer to store 160bit digest */
uint8 digest[SHA1_HASH_SIZE];
compute_sha1_hash(digest, sptr->ptr(), sptr->length());
/* Ensure that memory is free */
if (!(str->alloc(SHA1_HASH_SIZE * 2))) {
array_to_hex(str->ptr(), digest, SHA1_HASH_SIZE);
str->length((uint)SHA1_HASH_SIZE * 2);
null_value = 0;
return str;
}
}
null_value = 1;
return 0;
}
bool Item_func_sha::resolve_type(THD *) {
CHARSET_INFO *cs = get_checksum_charset(args[0]->collation.collation->csname);
args[0]->collation.set(cs, DERIVATION_COERCIBLE);
// size of hex representation of hash
set_data_type_string(SHA1_HASH_SIZE * 2, default_charset());
return false;
}
/*
SHA2(str, hash_length)
The second argument indicates the desired bit length of the
result, which must have a value of 224, 256, 384, 512, or 0
(which is equivalent to 256).
*/
String *Item_func_sha2::val_str_ascii(String *str) {
DBUG_ASSERT(fixed == 1);
#if defined(HAVE_OPENSSL)
unsigned char digest_buf[SHA512_DIGEST_LENGTH];
uint digest_length = 0;
String *input_string = args[0]->val_str(str);
str->set_charset(&my_charset_bin);
if (input_string == NULL) {
null_value = true;
return (String *)NULL;
}
null_value = args[0]->null_value;
if (null_value) return NULL;
const unsigned char *input_ptr =
pointer_cast<const unsigned char *>(input_string->ptr());
size_t input_len = input_string->length();
longlong hash_length = args[1]->val_int();
null_value = args[1]->null_value;
// Give error message in switch below.
if (null_value) hash_length = -1;
switch (hash_length) {
#ifndef OPENSSL_NO_SHA512
case 512:
digest_length = SHA512_DIGEST_LENGTH;
(void)SHA_EVP512(input_ptr, input_len, digest_buf);
break;
case 384:
digest_length = SHA384_DIGEST_LENGTH;
(void)SHA_EVP384(input_ptr, input_len, digest_buf);
break;
#endif
#ifndef OPENSSL_NO_SHA256
case 224:
digest_length = SHA224_DIGEST_LENGTH;
(void)SHA_EVP224(input_ptr, input_len, digest_buf);
break;
case 256:
case 0: // SHA-256 is the default
digest_length = SHA256_DIGEST_LENGTH;
(void)SHA_EVP256(input_ptr, input_len, digest_buf);
break;
#endif
default:
// For const values we have already warned in resolve_type().
if (!args[1]->const_item())
push_warning_printf(
current_thd, Sql_condition::SL_WARNING,
ER_WRONG_PARAMETERS_TO_NATIVE_FCT,
ER_THD(current_thd, ER_WRONG_PARAMETERS_TO_NATIVE_FCT), "sha2");
null_value = true;
return NULL;
}
/*
Since we're subverting the usual String methods, we must make sure that
the destination has space for the bytes we're about to write.
*/
str->mem_realloc(digest_length * 2 + 1); /* Each byte as two nybbles */
/* Convert the large number to a string-hex representation. */
array_to_hex(str->ptr(), digest_buf, digest_length);
/* We poked raw bytes in. We must inform the the String of its length. */
str->length(digest_length * 2); /* Each byte as two nybbles */
null_value = false;
return str;
#else
push_warning_printf(
current_thd, Sql_condition::SL_WARNING, ER_FEATURE_DISABLED,
ER_THD(current_thd, ER_FEATURE_DISABLED), "sha2", "--with-ssl");
null_value = true;
return (String *)NULL;
#endif /* defined(HAVE_OPENSSL) */
}
bool Item_func_sha2::resolve_type(THD *thd) {
maybe_null = true;
#if defined(HAVE_OPENSSL)
longlong sha_variant;
if (args[1]->const_item()) {
sha_variant = args[1]->val_int();
// Give error message in switch below.
if (args[1]->null_value) sha_variant = -1;
} else {
sha_variant = 512;
}
switch (sha_variant) {
#ifndef OPENSSL_NO_SHA512
case 512:
set_data_type_string(SHA512_DIGEST_LENGTH * 2, default_charset());
break;
case 384:
set_data_type_string(SHA384_DIGEST_LENGTH * 2, default_charset());
break;
#endif
#ifndef OPENSSL_NO_SHA256
case 256:
case 0: // SHA-256 is the default
set_data_type_string(SHA256_DIGEST_LENGTH * 2, default_charset());
break;
#endif
case 224:
set_data_type_string(SHA224_DIGEST_LENGTH * 2, default_charset());
break;
default:
set_data_type_string(SHA256_DIGEST_LENGTH * 2, default_charset());
push_warning_printf(
thd, Sql_condition::SL_WARNING, ER_WRONG_PARAMETERS_TO_NATIVE_FCT,
ER_THD(thd, ER_WRONG_PARAMETERS_TO_NATIVE_FCT), "sha2");
}
CHARSET_INFO *cs = get_checksum_charset(args[0]->collation.collation->csname);
args[0]->collation.set(cs, DERIVATION_COERCIBLE);
#else
push_warning_printf(thd, Sql_condition::SL_WARNING, ER_FEATURE_DISABLED,
ER_THD(thd, ER_FEATURE_DISABLED), "sha2", "--with-ssl");
#endif /* defined(HAVE_OPENSSL) */
return false;
}
/* Implementation of AES encryption routines */
/** helper class to process an IV argument to aes_encrypt/aes_decrypt */
class iv_argument {
char iv_buff[MY_AES_IV_SIZE + 1]; // +1 to cater for the terminating NULL
String tmp_iv_value;
public:
iv_argument() : tmp_iv_value(iv_buff, sizeof(iv_buff), system_charset_info) {}
/**
Validate the arguments and retrieve the IV value.
Processes a 3d optional IV argument to an Item_func function.
Contains all the necessary stack buffers etc.
@param aes_opmode the encryption mode
@param arg_count number of parameters passed to the function
@param args array of arguments passed to the function
@param func_name the name of the function (for errors)
@param thd the current thread (for errors)
@param [out] error_generated set to true if error was generated.
@return a pointer to the retrived validated IV or NULL
*/
const unsigned char *retrieve_iv_ptr(enum my_aes_opmode aes_opmode,
uint arg_count, Item **args,
const char *func_name, THD *thd,
bool *error_generated) {
const unsigned char *iv_str = NULL;
*error_generated = false;
if (my_aes_needs_iv(aes_opmode)) {
/* we only enforce the need for IV */
if (arg_count == 3) {
String *iv = args[2]->val_str(&tmp_iv_value);
if (!iv || iv->length() < MY_AES_IV_SIZE) {
my_error(ER_AES_INVALID_IV, MYF(0), func_name,
(long long)MY_AES_IV_SIZE);
*error_generated = true;
return NULL;
}
iv_str = (unsigned char *)iv->ptr();
} else {
my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), func_name);
*error_generated = true;
return NULL;
}
} else {
if (arg_count == 3) {
push_warning_printf(thd, Sql_condition::SL_WARNING, WARN_OPTION_IGNORED,
ER_THD(thd, WARN_OPTION_IGNORED), "IV");
}
}
return iv_str;
}
};
bool Item_func_aes_encrypt::itemize(Parse_context *pc, Item **res) {
if (skip_itemize(res)) return false;
if (super::itemize(pc, res)) return true;
/* Unsafe for SBR since result depends on a session variable */
pc->thd->lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_SYSTEM_FUNCTION);
/* Not safe to cache either */
pc->thd->lex->set_uncacheable(pc->select, UNCACHEABLE_SIDEEFFECT);
return false;
}
String *Item_func_aes_encrypt::val_str(String *str) {
DBUG_ASSERT(fixed == 1);
char key_buff[80];
String tmp_key_value(key_buff, sizeof(key_buff), system_charset_info);
String *sptr, *key;
int aes_length;
THD *thd = current_thd;
ulong aes_opmode;
iv_argument iv_arg;
DBUG_TRACE;
sptr = args[0]->val_str(str); // String to encrypt
key = args[1]->val_str(&tmp_key_value); // key
aes_opmode = thd->variables.my_aes_mode;
DBUG_ASSERT(aes_opmode <= MY_AES_END);
if (sptr && key) // we need both arguments to be not NULL
{
const unsigned char *iv_str =
iv_arg.retrieve_iv_ptr((enum my_aes_opmode)aes_opmode, arg_count, args,
func_name(), thd, &null_value);
if (null_value) return NULL;
// Calculate result length
aes_length =
my_aes_get_size(sptr->length(), (enum my_aes_opmode)aes_opmode);
tmp_value.set_charset(&my_charset_bin);
if (!tmp_value.alloc(aes_length)) // Ensure that memory is free
{
// finally encrypt directly to allocated buffer.
if (my_aes_encrypt((unsigned char *)sptr->ptr(), sptr->length(),
(unsigned char *)tmp_value.ptr(),
(unsigned char *)key->ptr(), key->length(),
(enum my_aes_opmode)aes_opmode,
iv_str) == aes_length) {
// We got the expected result length
tmp_value.length(static_cast<size_t>(aes_length));
return &tmp_value;
}
}
}
null_value = 1;
return 0;
}
bool Item_func_aes_encrypt::resolve_type(THD *thd) {
ulong aes_opmode = thd->variables.my_aes_mode;
DBUG_ASSERT(aes_opmode <= MY_AES_END);
set_data_type_string((uint)my_aes_get_size(args[0]->max_length,
(enum my_aes_opmode)aes_opmode));
return false;
}
bool Item_func_aes_decrypt::itemize(Parse_context *pc, Item **res) {
if (skip_itemize(res)) return false;
if (super::itemize(pc, res)) return true;
/* Unsafe for SBR since result depends on a session variable */
pc->thd->lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_SYSTEM_FUNCTION);
/* Not safe to cache either */
pc->thd->lex->set_uncacheable(pc->select, UNCACHEABLE_SIDEEFFECT);
return false;
}
String *Item_func_aes_decrypt::val_str(String *str) {
DBUG_ASSERT(fixed == 1);
char key_buff[80];
String tmp_key_value(key_buff, sizeof(key_buff), system_charset_info);
String *sptr, *key;
THD *thd = current_thd;
ulong aes_opmode;
iv_argument iv_arg;
DBUG_TRACE;
sptr = args[0]->val_str(str); // String to decrypt
key = args[1]->val_str(&tmp_key_value); // Key
aes_opmode = thd->variables.my_aes_mode;
DBUG_ASSERT(aes_opmode <= MY_AES_END);
if (sptr && key) // Need to have both arguments not NULL
{
const unsigned char *iv_str =
iv_arg.retrieve_iv_ptr((enum my_aes_opmode)aes_opmode, arg_count, args,
func_name(), thd, &null_value);
if (null_value) return NULL;
str_value.set_charset(&my_charset_bin);
if (!str_value.alloc(sptr->length())) // Ensure that memory is free
{
// finally decrypt directly to allocated buffer.
int length;
length = my_aes_decrypt((unsigned char *)sptr->ptr(), sptr->length(),
(unsigned char *)str_value.ptr(),
(unsigned char *)key->ptr(), key->length(),
(enum my_aes_opmode)aes_opmode, iv_str);
if (length >= 0) // if we got correct data data
{
str_value.length((uint)length);
return &str_value;
}
}
}
// Bad parameters. No memory or bad data will all go here
null_value = 1;
return 0;
}
bool Item_func_aes_decrypt::resolve_type(THD *) {
set_data_type_string(args[0]->max_char_length());
maybe_null = true;
return false;
}
bool Item_func_random_bytes::itemize(Parse_context *pc, Item **res) {
if (skip_itemize(res)) return false;
if (super::itemize(pc, res)) return true;
/* it is unsafe for SBR since it uses crypto random from the ssl library */
pc->thd->lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_SYSTEM_FUNCTION);
/* Not safe to cache either */
pc->thd->lex->set_uncacheable(pc->select, UNCACHEABLE_RAND);
return false;
}
/*
Artificially limited to 1k to avoid excessive memory usage.
The SSL lib supports up to INT_MAX.
*/
const ulonglong Item_func_random_bytes::MAX_RANDOM_BYTES_BUFFER = 1024ULL;
bool Item_func_random_bytes::resolve_type(THD *) {
set_data_type_string(MAX_RANDOM_BYTES_BUFFER, &my_charset_bin);
return false;
}
String *Item_func_random_bytes::val_str(String *) {
DBUG_ASSERT(fixed == 1);
ulonglong n_bytes = args[0]->val_uint();
null_value = args[0]->null_value;
if (null_value) return NULL;
str_value.set_charset(&my_charset_bin);
if (n_bytes == 0 || n_bytes > MAX_RANDOM_BYTES_BUFFER) {
my_error(ER_DATA_OUT_OF_RANGE, MYF(0), "length", func_name());
null_value = true;
return NULL;
}
if (str_value.alloc(n_bytes)) {
my_error(ER_OUTOFMEMORY, n_bytes);
null_value = true;
return NULL;
}
str_value.set_charset(&my_charset_bin);
if (my_rand_buffer((unsigned char *)str_value.ptr(), n_bytes)) {
my_error(ER_ERROR_WHEN_EXECUTING_COMMAND, MYF(0), func_name(),
"SSL library can't generate random bytes");
null_value = true;
return NULL;
}
str_value.length(n_bytes);
return &str_value;
}
bool Item_func_to_base64::resolve_type(THD *thd) {
maybe_null = args[0]->maybe_null;
collation.set(default_charset(), DERIVATION_COERCIBLE, MY_REPERTOIRE_ASCII);
if (args[0]->max_length > (uint)base64_encode_max_arg_length()) {
maybe_null = true;
set_data_type_string((ulonglong)base64_encode_max_arg_length());
} else {
uint64 length = base64_needed_encoded_length((uint64)args[0]->max_length);
DBUG_ASSERT(length > 0);
set_data_type_string((ulonglong)length - 1);
maybe_null = (maybe_null || max_length > thd->variables.max_allowed_packet);
}
return false;
}
String *Item_func_to_base64::val_str_ascii(String *str) {
String *res = args[0]->val_str(str);
bool too_long = false;
uint64 length;
if (!res || res->length() > (uint)base64_encode_max_arg_length() ||
(too_long =
((length = base64_needed_encoded_length((uint64)res->length())) >
current_thd->variables.max_allowed_packet)) ||
tmp_value.alloc((uint)length)) {
null_value = 1; // NULL input, too long input, or OOM.
if (too_long) {
return push_packet_overflow_warning(current_thd, func_name());
}
return 0;
}
base64_encode(res->ptr(), (int)res->length(), tmp_value.ptr());
DBUG_ASSERT(length > 0);
tmp_value.length((uint)length - 1); // Without trailing '\0'
null_value = 0;
return &tmp_value;
}
bool Item_func_from_base64::resolve_type(THD *) {
if (args[0]->max_length > (uint)base64_decode_max_arg_length()) {
set_data_type_string(ulonglong(base64_decode_max_arg_length()));
} else {
uint64 length = base64_needed_decoded_length((uint64)args[0]->max_length);
set_data_type_string(ulonglong(length));
}
maybe_null = true; // Can be NULL, e.g. in case of badly formed input string
return false;
}
String *Item_func_from_base64::val_str(String *str) {
String *res = args[0]->val_str_ascii(str);
bool too_long = false;
int64 length;
const char *end_ptr;
if (!res || res->length() > (uint)base64_decode_max_arg_length() ||
(too_long = ((uint64)(length = base64_needed_decoded_length(
(uint64)res->length())) >
current_thd->variables.max_allowed_packet)) ||
tmp_value.alloc((uint)length) ||
(length = base64_decode(res->ptr(), (uint64)res->length(),
tmp_value.ptr(), &end_ptr, 0)) < 0 ||
end_ptr < res->ptr() + res->length()) {
null_value = 1; // NULL input, too long input, OOM, or badly formed input
if (too_long) {
return push_packet_overflow_warning(current_thd, func_name());
}
return 0;
}
tmp_value.length((uint)length);
null_value = 0;
return &tmp_value;
}
namespace {
/**
Because it's not possible to disentangle the state of the parser from the
THD, we have to destructively modify the current THD object in order to
parse. This class backs up and restores members that are modified in
Item_func_statement_digest::val_str_ascii. It also sports its own
Query_arena and LEX objects, which are used during parsing.
*/
class Thd_parse_modifier {
public:
Thd_parse_modifier(THD *thd, uchar *token_buffer)
: m_thd(thd),
m_arena(&m_mem_root, Query_arena::STMT_REGULAR_EXECUTION),
m_backed_up_lex(thd->lex),
m_saved_parser_state(thd->m_parser_state),
m_saved_digest(thd->m_digest),
m_cs(thd->variables.character_set_client) {
thd->m_digest = &m_digest_state;
m_digest_state.reset(token_buffer, get_max_digest_length());
m_arena.set_query_arena(*thd);
thd->lex = &m_lex;
lex_start(thd);
}
~Thd_parse_modifier() {
lex_end(&m_lex);
m_thd->lex = m_backed_up_lex;
m_thd->set_query_arena(m_arena);
m_thd->m_parser_state = m_saved_parser_state;
m_thd->m_digest = m_saved_digest;
m_thd->variables.character_set_client = m_cs;
m_thd->update_charset();
}
private:
THD *m_thd;
MEM_ROOT m_mem_root;
Query_arena m_arena;
LEX *m_backed_up_lex;
LEX m_lex;
sql_digest_state m_digest_state;
Parser_state *m_saved_parser_state;
sql_digest_state *m_saved_digest;
const CHARSET_INFO *m_cs;
};
/**
Error handler that wraps parse error messages, removes details and silences
warnings.
We don't want statement_digest() to raise warnings about deprecated syntax
or semantic problems. This is likely not interesting to the
caller. Therefore this handler issues a blanket silencing of all warnings.
The reason we want to anonymize parse errors is to avoid leaking information
in error messages that may be unintentionally visible to users of an
application. For instance an application may in error insert an expression
instead of a string:
SELECT statement_digest( (SELECT * FROM( SELECT user() ) t) );
The parser would normally raise an error saying:
You have an error in your SQL syntax; /.../ near 'root@localhost'
thus leaking data from the `user` table. Therefore, the errors are in this
not disclosed.
*/
class Parse_error_anonymizer : public Internal_error_handler {
public:
Parse_error_anonymizer(THD *thd, Item *arg) : m_thd(thd), m_arg(arg) {
thd->push_internal_handler(this);
}
bool handle_condition(THD *, uint, const char *,
Sql_condition::enum_severity_level *level,
const char *message) override {
// Silence warnings.
if (*level == Sql_condition::SL_WARNING) return true;
// We pretend we're not here if already inside a call to handle_condition().
if (is_handling) return false;
is_handling = true;
if (m_arg->basic_const_item())
// Ok, it's a literal, we can print the whole error message.
my_error(ER_PARSE_ERROR_IN_DIGEST_FN, MYF(0), message);
else
// The argument is an expression, potentially from malicious use, let's
// not disclose anything.
my_error(ER_UNDISCLOSED_PARSE_ERROR_IN_DIGEST_FN, MYF(0));
is_handling = false;
return true;
}
~Parse_error_anonymizer() override { m_thd->pop_internal_handler(); }
private:
THD *m_thd;
Item *m_arg;
/// This avoids infinte recursion through my_error().
bool is_handling = false;
};
/**
Parses a string and fills the token buffer.
The parser function THD::sql_parser() is called directly instead of
parse_sql(), as the latter assumes that it is called with the intent to record
the statement in performance_schema and later execute it, neither of which is
called for here. In fact we hardly need the parser to calculate a digest,
since it is calculated from the token stream. There are only some corner cases
where `NULL` is sometimes a literal and sometimes an operator, as in
`IS NULL`, `IS NOT NULL`.
@param thd Session object used by the parser.
@param statement_expr The expression that evaluates to something that
can be parsed. Needed for error messages in case we don't want to disclose
what it evaluates to.
@param statement_string The non-NULL string resulting from evaluating
statement_expr. The caller is preferred to do this as this function doesn't
deal with NULL values.
@retval true Error.
@retval false All went well, the digest information is in THD::m_digest.
*/
bool parse(THD *thd, Item *statement_expr, String *statement_string) {
// The lexer can't handle non-zero-length strings starting with NUL and we
// can't return NULL for them because this function is declared
// nonnullable.
if (statement_string->length() > 0 && (*statement_string)[0] == '\0')
statement_string->length(0);
const CHARSET_INFO *cs = statement_string->charset();
thd->variables.character_set_client = cs;
thd->update_charset();
Parser_state ps;
// The lexer needs null-terminated strings, despite boasting the below
// interface. Hence the use of c_ptr_safe().
if (ps.init(thd, statement_string->c_ptr_safe(), statement_string->length()))
return true;
ps.m_lip.multi_statements = false;
ps.m_lip.m_digest = thd->m_digest;
ps.m_lip.m_digest->m_digest_storage.m_charset_number = cs->number;
thd->m_parser_state = &ps;
{
Parse_error_anonymizer pea(thd, statement_expr);
if (thd->sql_parser()) {
return true;
}
}
return false;
}
} // namespace
bool Item_func_statement_digest::resolve_type(THD *thd) {
set_data_type_string(DIGEST_HASH_TO_STRING_LENGTH, default_charset());
m_token_buffer = static_cast<uchar *>(thd->alloc(get_max_digest_length()));
if (m_token_buffer == nullptr) return true;
return false;
}
/**
Implementation of the STATEMENT_DIGEST() native function.
@param buf A String object that we can write to.
@return The same string object, or nullptr in case of error or null return.
*/
String *Item_func_statement_digest::val_str_ascii(String *buf) {
DBUG_TRACE;
String *statement_string = args[0]->val_str(buf);
// This function is non-nullable, meaning it doesn't return NULL, unless the
// argument is NULL.
if (statement_string == nullptr) return null_return_str();
null_value = false;
uchar digest[DIGEST_HASH_SIZE];
{
THD *thd = current_thd;
Thd_parse_modifier thd_mod(thd, m_token_buffer);
if (parse(thd, args[0], statement_string)) return error_str();
compute_digest_hash(&thd->m_digest->m_digest_storage, digest);
}
if (buf->reserve(DIGEST_HASH_TO_STRING_LENGTH)) return error_str();
buf->length(DIGEST_HASH_TO_STRING_LENGTH);
DIGEST_HASH_TO_STRING(digest, buf->c_ptr_quick());
return buf;
}
bool Item_func_statement_digest_text::resolve_type(THD *thd) {
set_data_type_string(MAX_BLOB_WIDTH, args[0]->collation);
m_token_buffer = static_cast<uchar *>(thd->alloc(get_max_digest_length()));
if (m_token_buffer == nullptr) return true;
return false;
}
String *Item_func_statement_digest_text::val_str(String *buf) {
DBUG_TRACE;
String *statement_string = args[0]->val_str(buf);
// This function is non-nullable, meaning it doesn't return NULL, unless the
// argument is NULL.
if (statement_string == nullptr) return null_return_str();
null_value = false;
THD *thd = current_thd;
Thd_parse_modifier thd_mod(thd, m_token_buffer);
if (parse(thd, args[0], statement_string)) return error_str();
compute_digest_text(&thd->m_digest->m_digest_storage, buf);
return buf;
}
/**
Concatenate args with the following premises:
If only one arg (which is ok), return value of arg;
*/
String *Item_func_concat::val_str(String *str) {
DBUG_ASSERT(fixed == 1);
String *res;
THD *thd = current_thd;
null_value = false;
tmp_value.length(0);
for (uint i = 0; i < arg_count; ++i) {
if (!(res = args[i]->val_str(str))) {
if (thd->is_error()) return error_str();
DBUG_ASSERT(maybe_null);
null_value = true;
return nullptr;
}
if (res->length() + tmp_value.length() >
thd->variables.max_allowed_packet) {
return push_packet_overflow_warning(thd, func_name());
}
if (tmp_value.append(*res)) return error_str();
}
res = &tmp_value;
res->set_charset(collation.collation);
return res;
}
bool Item_func_concat::resolve_type(THD *thd) {
ulonglong char_length = 0;
if (agg_arg_charsets_for_string_result(collation, args, arg_count))
return true;
for (uint i = 0; i < arg_count; i++)
char_length += args[i]->max_char_length();
set_data_type_string(char_length);
maybe_null = (maybe_null || max_length > thd->variables.max_allowed_packet);
return false;
}
/**
concat with separator. First arg is the separator
concat_ws takes at least two arguments.
*/
String *Item_func_concat_ws::val_str(String *str) {
DBUG_ASSERT(fixed == 1);
char tmp_str_buff[10];
String tmp_sep_str(tmp_str_buff, sizeof(tmp_str_buff), default_charset_info);
String *sep_str, *res = nullptr, *res2;
uint i;
THD *thd = current_thd;
null_value = false;
if (!(sep_str = args[0]->val_str(&tmp_sep_str))) return error_str();
tmp_value.length(0);
// Skip until non-null argument is found.
// If not, return the empty string
for (i = 1; i < arg_count; i++)
if ((res = args[i]->val_str(str))) {
break;
}
if (i == arg_count) return make_empty_result();
if (tmp_value.append(*res)) return error_str();
for (i++; i < arg_count; i++) {
if (!(res2 = args[i]->val_str(str))) continue; // Skip NULL
if (tmp_value.length() + sep_str->length() + res2->length() >
thd->variables.max_allowed_packet) {
return push_packet_overflow_warning(thd, func_name());
}
if (tmp_value.append(*sep_str)) return error_str();
if (tmp_value.append(*res2)) return error_str();
}
res = &tmp_value;
res->set_charset(collation.collation);
return res;
}
bool Item_func_concat_ws::resolve_type(THD *thd) {
ulonglong char_length;
if (agg_arg_charsets_for_string_result(collation, args, arg_count))
return true;
DBUG_ASSERT(arg_count >= 2);
char_length = (ulonglong)args[0]->max_char_length() * (arg_count - 2);
for (uint i = 1; i < arg_count; i++)
char_length += args[i]->max_char_length();
set_data_type_string(char_length);
maybe_null = (maybe_null || max_length > thd->variables.max_allowed_packet);
return false;
}
String *Item_func_reverse::val_str(String *str) {
DBUG_ASSERT(fixed == 1);
String *res = args[0]->val_str(str);
const char *ptr, *end;
char *tmp;
if ((null_value = args[0]->null_value)) return 0;
/* An empty string is a special case as the string pointer may be null */
if (!res->length()) return make_empty_result();
if (tmp_value.alloced_length() < res->length() &&
tmp_value.mem_realloc(res->length())) {
null_value = 1;
return 0;
}
tmp_value.length(res->length());
tmp_value.set_charset(res->charset());
ptr = res->ptr();
end = ptr + res->length();
tmp = tmp_value.ptr() + tmp_value.length();
if (use_mb(res->charset())) {
uint32 l;
while (ptr < end) {
if ((l = my_ismbchar(res->charset(), ptr, end))) {
tmp -= l;
DBUG_ASSERT(tmp >= tmp_value.ptr());
memcpy(tmp, ptr, l);
ptr += l;
} else
*--tmp = *ptr++;
}
} else {
while (ptr < end) *--tmp = *ptr++;
}
return &tmp_value;
}
bool Item_func_reverse::resolve_type(THD *) {
if (agg_arg_charsets_for_string_result(collation, args, 1)) return true;
DBUG_ASSERT(collation.collation != NULL);
set_data_type_string(args[0]->max_char_length());
return false;
}
/**
Replace all occurences of string2 in string1 with string3.
*/
String *Item_func_replace::val_str(String *str) {
DBUG_ASSERT(fixed == 1);
String *res1 = args[0]->val_str(str);
if ((null_value = args[0]->null_value)) return nullptr;
String *res2 = args[1]->val_str(&tmp_value);
if ((null_value = args[1]->null_value)) return nullptr;
String *res3 = args[2]->val_str(&tmp_value2);
if ((null_value = args[2]->null_value)) return nullptr;
res1->set_charset(collation.collation);
if (res1->length() == 0 || res2->length() == 0) return res1;
const bool binary_cmp =
((res1->charset()->state & MY_CS_BINSORT) || !use_mb(res1->charset()));
if (binary_cmp && res1->strstr(*res2) < 0) return res1;
tmp_value_res.length(0);
tmp_value_res.set_charset(collation.collation);
String *result = &tmp_value_res;
THD *thd = current_thd;
const unsigned long max_size = thd->variables.max_allowed_packet;
const char *search = res2->ptr();
const size_t from_length = res2->length();
const char *search_end = search + from_length;
const size_t to_length = res3->length();
const char *ptr = res1->ptr();
const char *strend = res1->ptr() + res1->length();
while (ptr < strend) {
if (ptr + from_length <= strend && std::equal(search, search_end, ptr)) {
if (to_length > from_length &&
result->length() + (to_length - from_length) + (strend - ptr) >
max_size) {
return push_packet_overflow_warning(thd, func_name());
}
if (result->append(*res3)) return error_str();
ptr += from_length;
} else {
bool err = false;
uint32 l = use_mb(res1->charset())
? my_ismbchar(res1->charset(), ptr, strend)
: 0;
if (l != 0)
while (l-- > 0) err |= result->append(*ptr++);
else
err = result->append(*ptr++);
if (err) return error_str();
}
}
return result;
}
bool Item_func_replace::resolve_type(THD *thd) {
ulonglong char_length = args[0]->max_char_length();
int diff = (int)(args[2]->max_char_length() - args[1]->max_char_length());
if (diff > 0 && args[1]->max_char_length()) { // Calculate of maxreplaces
ulonglong max_substrs = char_length / args[1]->max_char_length();
char_length += max_substrs * (uint)diff;
}
if (agg_arg_charsets_for_string_result_with_comparison(collation, args, 3))
return true;
set_data_type_string(char_length);
maybe_null = (maybe_null || max_length > thd->variables.max_allowed_packet);
return false;
}
String *Item_func_insert::val_str(String *str) {
DBUG_ASSERT(fixed == 1);
String *res, *res2;
longlong start, length, orig_len; /* must be longlong to avoid truncation */
null_value = 0;
res = args[0]->val_str(str);
res2 = args[3]->val_str(&tmp_value);
start = args[1]->val_int();
length = args[2]->val_int();
if (args[0]->null_value || args[1]->null_value || args[2]->null_value ||
args[3]->null_value) {
DBUG_ASSERT(maybe_null);
return error_str(); /* purecov: inspected */
}
orig_len = static_cast<longlong>(res->length());
if ((start < 1) || (start > orig_len))
return res; // Wrong param; skip insert
--start; // Internal start from '0'
if ((length < 0) || (length > orig_len)) length = orig_len;
/*
There is one exception not handled (intentionaly) by the character set
aggregation code. If one string is strong side and is binary, and
another one is weak side and is a multi-byte character string,
then we need to operate on the second string in terms on bytes when
calling ::numchars() and ::charpos(), rather than in terms of characters.
Lets substitute its character set to binary.
*/
if (collation.collation == &my_charset_bin) {
res->set_charset(&my_charset_bin);
res2->set_charset(&my_charset_bin);
}
/* start and length are now sufficiently valid to pass to charpos function */
start = res->charpos((int)start);
length = res->charpos((int)length, (uint32)start);
/* Re-testing with corrected params */
if (start > orig_len)
return res; /* purecov: inspected */ // Wrong param; skip insert
if (length > orig_len - start) length = orig_len - start;
if ((ulonglong)(orig_len - length + res2->length()) >
(ulonglong)current_thd->variables.max_allowed_packet) {
return push_packet_overflow_warning(current_thd, func_name());
}
if (res->uses_buffer_owned_by(str)) {
if (tmp_value_res.alloc(orig_len) || tmp_value_res.copy(*res))
return error_str();
res = &tmp_value_res;
} else
res = copy_if_not_alloced(str, res, orig_len);
res->replace((uint32)start, (uint32)length, *res2);
return res;
}
bool Item_func_insert::resolve_type(THD *thd) {
// Handle character set for args[0] and args[3].
if (agg_arg_charsets_for_string_result(collation, args, 2, 3)) return true;
ulonglong length = ulonglong{args[0]->max_char_length()} +
ulonglong{args[3]->max_char_length()};
set_data_type_string(length);
maybe_null = (maybe_null || max_length > thd->variables.max_allowed_packet);
return false;
}
String *Item_str_conv::val_str(String *str) {
DBUG_ASSERT(fixed == 1);
String *res;
if (!(res = args[0]->val_str(str))) {
null_value = 1; /* purecov: inspected */
return 0; /* purecov: inspected */
}
null_value = 0;
if (multiply == 1) {
size_t len;
if (res->uses_buffer_owned_by(str)) {
if (tmp_value.copy(*res)) return error_str();
res = &tmp_value;
} else
res = copy_if_not_alloced(str, res, res->length());
len = converter(collation.collation, res->ptr(), res->length(), res->ptr(),
res->length());
DBUG_ASSERT(len <= res->length());
res->length(len);
} else {
size_t len = res->length() * multiply;
tmp_value.alloc(len);
tmp_value.set_charset(collation.collation);
len = converter(collation.collation, res->ptr(), res->length(),
tmp_value.ptr(), len);
tmp_value.length(len);
res = &tmp_value;
}
return res;
}
bool Item_func_lower::resolve_type(THD *) {
if (agg_arg_charsets_for_string_result(collation, args, 1)) return true;
DBUG_ASSERT(collation.collation != NULL);
multiply = collation.collation->casedn_multiply;
converter = collation.collation->cset->casedn;
set_data_type_string(args[0]->max_char_length() * multiply);
return false;
}
bool Item_func_upper::resolve_type(THD *) {
if (agg_arg_charsets_for_string_result(collation, args, 1)) return true;
DBUG_ASSERT(collation.collation != NULL);
multiply = collation.collation->caseup_multiply;
converter = collation.collation->cset->caseup;
set_data_type_string(args[0]->max_char_length() * multiply);
return false;
}
String *Item_func_left::val_str(String *str) {
DBUG_ASSERT(fixed == 1);
String *res = args[0]->val_str(str);
/* must be longlong to avoid truncation */
longlong length = args[1]->val_int();
size_t char_pos;
if ((null_value = (args[0]->null_value || args[1]->null_value))) return 0;
/* if "unsigned_flag" is set, we have a *huge* positive number. */
if ((length <= 0) && (!args[1]->unsigned_flag)) return make_empty_result();
if ((res->length() <= (ulonglong)length) ||
(res->length() <= (char_pos = res->charpos((int)length))))
return res;
tmp_value.set(*res, 0, char_pos);
return &tmp_value;
}
void Item_str_func::left_right_max_length() {
uint32 char_length = args[0]->max_char_length();
if (args[1]->const_item()) {
longlong length = args[1]->val_int();
if (args[1]->null_value) goto end;
Integer_value length_val(length, args[1]->unsigned_flag);
if (length_val.is_negative())
char_length = 0;
else if (length_val <= Integer_value(INT_MAX32, false))
char_length = std::min(char_length, static_cast<uint32>(length));
}
end:
set_data_type_string(char_length);
}
String *Item_str_func::push_packet_overflow_warning(THD *thd,
const char *func) {
push_warning_printf(thd, Sql_condition::SL_WARNING,
ER_WARN_ALLOWED_PACKET_OVERFLOWED,
ER_THD(thd, ER_WARN_ALLOWED_PACKET_OVERFLOWED), func,
thd->variables.max_allowed_packet);
DBUG_ASSERT(maybe_null);
return error_str();
}
bool Item_func_left::resolve_type(THD *) {
if (agg_arg_charsets_for_string_result(collation, args, 1)) return true;
DBUG_ASSERT(collation.collation != NULL);
left_right_max_length();
return false;
}
String *Item_func_right::val_str(String *str) {
DBUG_ASSERT(fixed == 1);
String *res = args[0]->val_str(str);
/* must be longlong to avoid truncation */
longlong length = args[1]->val_int();
if ((null_value = (args[0]->null_value || args[1]->null_value)))
return 0; /* purecov: inspected */
/* if "unsigned_flag" is set, we have a *huge* positive number. */
if ((length <= 0) && (!args[1]->unsigned_flag))
return make_empty_result(); /* purecov: inspected */
if (res->length() <= (ulonglong)length) return res; /* purecov: inspected */
size_t start = res->numchars();
if (start <= (uint)length) return res;
start = res->charpos(start - (uint)length);
tmp_value.set(*res, start, res->length() - start);
return &tmp_value;
}
bool Item_func_right::resolve_type(THD *) {
if (agg_arg_charsets_for_string_result(collation, args, 1)) return true;
DBUG_ASSERT(collation.collation != NULL);
left_right_max_length();
return false;
}
String *Item_func_substr::val_str(String *str) {
DBUG_ASSERT(fixed == 1);
String *res = args[0]->val_str(str);
/* must be longlong to avoid truncation */
longlong start = args[1]->val_int();
/* Assumes that the maximum length of a String is < INT_MAX32. */
/* Limit so that code sees out-of-bound value properly. */
longlong length = arg_count == 3 ? args[2]->val_int() : INT_MAX32;
longlong tmp_length;
if ((null_value = (args[0]->null_value || args[1]->null_value ||
(arg_count == 3 && args[2]->null_value))))
return 0; /* purecov: inspected */
/* Negative or zero length, will return empty string. */
if ((arg_count == 3) && (length <= 0) &&
(length == 0 || !args[2]->unsigned_flag))
return make_empty_result();
/* Assumes that the maximum length of a String is < INT_MAX32. */
/* Set here so that rest of code sees out-of-bound value as such. */
if ((length <= 0) || (length > INT_MAX32)) length = INT_MAX32;
/* if "unsigned_flag" is set, we have a *huge* positive number. */
/* Assumes that the maximum length of a String is < INT_MAX32. */
if ((!args[1]->unsigned_flag && (start < INT_MIN32 || start > INT_MAX32)) ||
(args[1]->unsigned_flag && ((ulonglong)start > INT_MAX32)))
return make_empty_result();
start = ((start < 0) ? res->numchars() + start : start - 1);
start = res->charpos((int)start);
if ((start < 0) || (start + 1 > static_cast<longlong>(res->length())))
return make_empty_result();
length = res->charpos((int)length, (uint32)start);
tmp_length = static_cast<longlong>(res->length()) - start;
length = min(length, tmp_length);
if (!start && (longlong)res->length() == length) return res;
tmp_value.set(*res, (uint32)start, (uint32)length);
return &tmp_value;
}
bool Item_func_substr::resolve_type(THD *) {
uint32 max_char_length = args[0]->max_char_length();
if (agg_arg_charsets_for_string_result(collation, args, 1)) return true;
DBUG_ASSERT(collation.collation != NULL);
if (args[1]->const_item()) {
longlong start = args[1]->val_int();
if (args[1]->null_value) goto end;
Integer_value start_val(start, args[1]->unsigned_flag);
if (Integer_value(INT_MIN32, false) < start_val &&
start_val <= Integer_value(INT_MAX32, false)) {
if (start < 0)
max_char_length = static_cast<uint32>(-start) > max_char_length
? 0
: static_cast<uint>(-start);
else
max_char_length -= min(static_cast<uint32>(start - 1), max_char_length);
}
}
if (arg_count == 3 && args[2]->const_item()) {
longlong length = args[2]->val_int();
if (args[2]->null_value) goto end;
Integer_value length_val(length, args[2]->unsigned_flag);
if (length_val.is_negative())
max_char_length = 0;
else if (length_val <= Integer_value(INT_MAX, false))
max_char_length = std::min(max_char_length, static_cast<uint32>(length));
}
end:
set_data_type_string(max_char_length);
return false;
}
bool Item_func_substr_index::resolve_type(THD *) {
if (agg_arg_charsets_for_string_result_with_comparison(collation, args, 2))
return true;
set_data_type_string(args[0]->max_char_length());
return false;
}
String *Item_func_substr_index::val_str(String *str) {
DBUG_ASSERT(fixed == 1);
char buff[MAX_FIELD_WIDTH];
String tmp(buff, sizeof(buff), system_charset_info);
String *res = args[0]->val_str(str);
String *delimiter = args[1]->val_str(&tmp);
const longlong count = args[2]->val_int();
int offset;
if (args[0]->null_value || args[1]->null_value ||
args[2]->null_value) { // string and/or delim are null
null_value = 1;
return 0;
}
null_value = 0;
size_t delimiter_length = delimiter->length();
if (!res->length() || !delimiter_length || !count)
return make_empty_result(); // Wrong parameters
res->set_charset(collation.collation);
Integer_value count_val(count, args[2]->unsigned_flag);
// Assumes that the maximum length of a String < INT_MAX32
if (Integer_value(INT_MAX32, false) < count_val ||
count_val < Integer_value(INT_MIN32, false))
return res;
if (use_mb(res->charset())) {
const char *ptr = res->ptr();
const char *strend = ptr + res->length();
const char *end = strend - delimiter_length + 1;
const char *search = delimiter->ptr();
const char *search_end = search + delimiter_length;
longlong nnn = 0;
longlong ccc = count;
// A single pass for positive, two passes for negative count.
for (int pass = (count_val.is_negative() ? 0 : 1); pass < 2; ++pass) {
while (ptr < end) {
if (*ptr == *search) {
const char *i = ptr + 1;
const char *j = search + 1;
while (j != search_end) {
if (*i++ != *j++) goto skip;
}
if (pass == 0)
++nnn;
else if (--ccc == 0)
break;
ptr += delimiter_length;
continue;
}
skip:
ptr += std::max(1U, my_ismbchar(res->charset(), ptr, strend));
} /* either not found or got total number when count<0 */
if (pass == 0) /* count < 0 */
{
ccc += nnn + 1;
if (ccc <= 0) return res; /* not found, return original string */
ptr = res->ptr();
} else {
if (ccc != 0) return res; /* Not found, return original string */
if (count_val.is_negative()) /* return right part */
{
ptr += delimiter_length;
tmp_value.set(*res, (ptr - res->ptr()), (strend - ptr));
} else /* return left part */
{
tmp_value.set(*res, 0, (ptr - res->ptr()));
}
}
}
} else {
if (count_val.is_negative()) {
/*
Negative index, start counting at the end
*/
longlong count_ll = count;
for (offset = res->length(); offset;) {
/*
this call will result in finding the position pointing to one
address space less than where the found substring is located
in res
*/
if ((offset = res->strrstr(*delimiter, offset)) < 0)
return res; // Didn't find, return org string
/*
At this point, we've searched for the substring
the number of times as supplied by the index value
*/
if (++count_ll == 0) {
offset += delimiter_length;
tmp_value.set(*res, offset, res->length() - offset);
break;
}
}
if (count_ll != 0) return res; // Didn't find, return org string
} else { // start counting from the beginning
ulonglong count_ull = count_val.val_unsigned();
for (offset = 0;; offset += delimiter_length) {
if ((offset = res->strstr(*delimiter, offset)) < 0)
return res; // Didn't find, return org string
if (--count_ull == 0) {
tmp_value.set(*res, 0, offset);
break;
}
}
}
}
return (&tmp_value);
}
/*
** The trim functions are extension to ANSI SQL because they trim substrings
** They ltrim() and rtrim() functions are optimized for 1 byte strings
** They also return the original string if possible, else they return
** a substring that points at the original string.
*/
String *Item_func_trim::val_str(String *str) {
DBUG_ASSERT(fixed == 1);
String *res = args[0]->val_str(str);
if ((null_value = args[0]->null_value)) return NULL;
char buff[MAX_FIELD_WIDTH];
String tmp(buff, sizeof(buff), system_charset_info);
const String *remove_str = &remove; // Default value.
if (arg_count == 2) {
remove_str = args[1]->val_str(&tmp);
if ((null_value = args[1]->null_value)) return NULL;
}
const size_t remove_length = remove_str->length();
if (remove_length == 0 || remove_length > res->length()) return res;
const char *ptr = res->ptr();
const char *end = ptr + res->length();
const char *const r_ptr = remove_str->ptr();
if (use_mb(res->charset())) {
if (m_trim_leading) {
while (ptr + remove_length <= end) {
uint num_bytes = 0;
while (num_bytes < remove_length) {
uint len;
if ((len = my_ismbchar(res->charset(), ptr + num_bytes, end)))
num_bytes += len;
else
++num_bytes;
}
if (num_bytes != remove_length) break;
if (memcmp(ptr, r_ptr, remove_length)) break;
ptr += remove_length;
}
}
if (m_trim_trailing) {
// Optimize a common case, removing 0x20
if (remove_length == 1) {
const char *save_ptr = ptr;
const char *new_end = ptr;
const char chr = (*remove_str)[0];
while (ptr < end) {
uint32 l;
if ((l = my_ismbchar(res->charset(), ptr, end))) {
ptr += l;
new_end = ptr;
} else if (*ptr++ != chr)
new_end = ptr;
}
end = new_end;
ptr = save_ptr;
} else {
bool found;
const char *save_ptr = ptr;
do {
found = false;
while (ptr + remove_length < end) {
uint32 l;
if ((l = my_ismbchar(res->charset(), ptr, end)))
ptr += l;
else
++ptr;
}
if (ptr + remove_length == end &&
!memcmp(ptr, r_ptr, remove_length)) {
end -= remove_length;
found = true;
}
ptr = save_ptr;
} while (found);
}
}
} else {
if (m_trim_leading) {
while (ptr + remove_length <= end && !memcmp(ptr, r_ptr, remove_length))
ptr += remove_length;
}
if (m_trim_trailing) {
while (ptr + remove_length <= end &&
!memcmp(end - remove_length, r_ptr, remove_length))
end -= remove_length;
}
}
if (ptr == res->ptr() && end == ptr + res->length()) return res;
tmp_value.set(*res, static_cast<uint>(ptr - res->ptr()),
static_cast<uint>(end - ptr));
return &tmp_value;
}
bool Item_func_trim::resolve_type(THD *) {
if (arg_count == 1) {
if (agg_arg_charsets_for_string_result(collation, args, 1)) return true;
DBUG_ASSERT(collation.collation != NULL);
remove.set_charset(collation.collation);
remove.set_ascii(" ", 1);
} else {
// Handle character set for args[1] and args[0].
// Note that we pass args[1] as the first item, and args[0] as the second.
if (agg_arg_charsets_for_string_result_with_comparison(collation, &args[1],
2, -1))
return true;
}
set_data_type_string(args[0]->max_char_length());
return false;
}
/*
We need a separate function for print(), in order to do correct printing.
The function func_name() is also used e.g. by Item_func::eq() to
distinguish between different functions, and we do not want
trim(leading) to match trim(trailing) for eq()
*/
static const char *trim_func_name(Item_func_trim::TRIM_MODE mode) {
switch (mode) {
case Item_func_trim::TRIM_BOTH_DEFAULT:
case Item_func_trim::TRIM_BOTH:
case Item_func_trim::TRIM_LEADING:
case Item_func_trim::TRIM_TRAILING:
return "trim";
case Item_func_trim::TRIM_LTRIM:
return "ltrim";
case Item_func_trim::TRIM_RTRIM:
return "rtrim";
}
return NULL;
}
void Item_func_trim::print(const THD *thd, String *str,
enum_query_type query_type) const {
str->append(trim_func_name(m_trim_mode));
str->append('(');
const char *mode_name;
switch (m_trim_mode) {
case TRIM_BOTH:
mode_name = "both ";
break;
case TRIM_LEADING:
mode_name = "leading ";
break;
case TRIM_TRAILING:
mode_name = "trailing ";
break;
default:
mode_name = NULL;
break;
}
if (mode_name) {
str->append(mode_name);
}
if (arg_count == 2) {
args[1]->print(thd, str, query_type);
str->append(STRING_WITH_LEN(" from "));
}
args[0]->print(thd, str, query_type);
str->append(')');
}
Item *Item_func_sysconst::safe_charset_converter(THD *thd,
const CHARSET_INFO *tocs) {
uint conv_errors;
String tmp, cstr, *ostr = val_str(&tmp);
if (null_value) {
Item *null_item = new Item_null(fully_qualified_func_name());
null_item->collation.set(tocs);
return null_item;
}
cstr.copy(ostr->ptr(), ostr->length(), ostr->charset(), tocs, &conv_errors);
if (conv_errors != 0) return nullptr;
char *ptr = thd->strmake(cstr.ptr(), cstr.length());
if (ptr == nullptr) return nullptr;
auto conv = new Item_static_string_func(fully_qualified_func_name(), ptr,
cstr.length(), cstr.charset(),
collation.derivation);
if (conv == nullptr) return nullptr;
conv->mark_result_as_const();
return conv;
}
bool Item_func_database::itemize(Parse_context *pc, Item **res) {
if (skip_itemize(res)) return false;
if (super::itemize(pc, res)) return true;
pc->thd->lex->safe_to_cache_query = 0;
return false;
}
String *Item_func_database::val_str(String *str) {
DBUG_ASSERT(fixed == 1);
THD *thd = current_thd;
if (thd->db().str == NULL) {
null_value = 1;
return 0;
} else
str->copy(thd->db().str, thd->db().length, system_charset_info);
return str;
}
/**
@note USER() is replicated correctly if binlog_format=ROW or (as of
BUG#28086) binlog_format=MIXED, but is incorrectly replicated to ''
if binlog_format=STATEMENT.
*/
bool Item_func_user::init(const char *user, const char *host) {
DBUG_ASSERT(fixed == 1);
DBUG_ASSERT(host != 0);
// For system threads (e.g. replication SQL thread) user may be empty
if (user) {
const CHARSET_INFO *cs = str_value.charset();
size_t res_length = (strlen(user) + strlen(host) + 2) * cs->mbmaxlen;
if (str_value.alloc((uint)res_length)) {
null_value = 1;
return true;
}
res_length = cs->cset->snprintf(cs, str_value.ptr(), res_length, "%s@%s",
user, host);
str_value.length((uint)res_length);
str_value.mark_as_const();
}
return false;
}
bool Item_func_user::itemize(Parse_context *pc, Item **res) {
if (skip_itemize(res)) return false;
if (super::itemize(pc, res)) return true;
LEX *lex = pc->thd->lex;
lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_SYSTEM_FUNCTION);
lex->safe_to_cache_query = 0;
return false;
}
bool Item_func_user::fix_fields(THD *thd, Item **ref) {
return (Item_func_sysconst::fix_fields(thd, ref) ||
init(thd->m_main_security_ctx.user().str,
thd->m_main_security_ctx.host_or_ip().str));
}
bool Item_func_current_user::itemize(Parse_context *pc, Item **res) {
if (skip_itemize(res)) return false;
if (super::itemize(pc, res)) return true;
context = pc->thd->lex->current_context();
return false;
}
bool Item_func_current_user::fix_fields(THD *thd, Item **ref) {
if (Item_func_sysconst::fix_fields(thd, ref)) return true;
Security_context *ctx =
(context->security_ctx ? context->security_ctx : thd->security_context());
return init(ctx->priv_user().str, ctx->priv_host().str);
}
bool Item_func_soundex::resolve_type(THD *) {
uint32 char_length = args[0]->max_char_length();
if (agg_arg_charsets_for_string_result(collation, args, 1)) return true;
DBUG_ASSERT(collation.collation != NULL);
set_if_bigger(char_length, 4);
set_data_type_string(char_length);
tmp_value.set_charset(collation.collation);
return false;
}
/**
If alpha, map input letter to soundex code.
If not alpha and remove_garbage is set then skip to next char
else return 0
*/
static int soundex_toupper(int ch) {
return (ch >= 'a' && ch <= 'z') ? ch - 'a' + 'A' : ch;
}
/* ABCDEFGHIJKLMNOPQRSTUVWXYZ */
/* :::::::::::::::::::::::::: */
static const char *soundex_map = "01230120022455012623010202";
static char get_scode(int wc) {
int ch = soundex_toupper(wc);
if (ch < 'A' || ch > 'Z') {
// Thread extended alfa (country spec)
return '0'; // as vokal
}
return (soundex_map[ch - 'A']);
}
static bool my_uni_isalpha(int wc) {
/*
Return true for all Basic Latin letters: a..z A..Z.
Return true for all Unicode characters with code higher than U+00C0:
- characters between 'z' and U+00C0 are controls and punctuations.
- "U+00C0 LATIN CAPITAL LETTER A WITH GRAVE" is the first letter after 'z'.
*/
return (wc >= 'a' && wc <= 'z') || (wc >= 'A' && wc <= 'Z') || (wc >= 0xC0);
}
String *Item_func_soundex::val_str(String *str) {
DBUG_ASSERT(fixed == 1);
String *res = args[0]->val_str(str);
char last_ch, ch;
const CHARSET_INFO *cs = collation.collation;
my_wc_t wc;
uint nchars;
int rc;
if ((null_value = args[0]->null_value)) return 0; /* purecov: inspected */
if (tmp_value.alloc(
max(res->length(), static_cast<size_t>(4 * cs->mbminlen))))
return str; /* purecov: inspected */
char *to = tmp_value.ptr();
char *to_end = to + tmp_value.alloced_length();
const char *from = res->ptr(), *end = from + res->length();
for (;;) /* Skip pre-space */
{
if ((rc = cs->cset->mb_wc(cs, &wc, pointer_cast<const uchar *>(from),
pointer_cast<const uchar *>(end))) <= 0)
return make_empty_result(); /* EOL or invalid byte sequence */
if (rc == 1 && cs->ctype) {
/* Single byte letter found */
if (my_isalpha(cs, *from)) {
last_ch = get_scode(*from); // Code of the first letter
*to++ = soundex_toupper(*from++); // Copy first letter
break;
}
from++;
} else {
from += rc;
if (my_uni_isalpha(wc)) {
/* Multibyte letter found */
wc = soundex_toupper(wc);
last_ch = get_scode(wc); // Code of the first letter
if ((rc = cs->cset->wc_mb(cs, wc, pointer_cast<uchar *>(to),
pointer_cast<uchar *>(to_end))) <= 0) {
/* Extra safety - should not really happen */
DBUG_ASSERT(false);
return make_empty_result();
}
to += rc;
break;
}
}
}
/*
last_ch is now set to the first 'double-letter' check.
loop on input letters until end of input
*/
for (nchars = 1;;) {
if ((rc = cs->cset->mb_wc(cs, &wc, pointer_cast<const uchar *>(from),
pointer_cast<const uchar *>(end))) <= 0)
break; /* EOL or invalid byte sequence */
if (rc == 1 && cs->ctype) {
if (!my_isalpha(cs, *from++)) continue;
} else {
from += rc;
if (!my_uni_isalpha(wc)) continue;
}
ch = get_scode(wc);
if ((ch != '0') && (ch != last_ch)) // if not skipped or double
{
// letter, copy to output
if ((rc = cs->cset->wc_mb(cs, (my_wc_t)ch, (uchar *)to,
(uchar *)to_end)) <= 0) {
// Extra safety - should not really happen
DBUG_ASSERT(false);
break;
}
to += rc;
nchars++;
last_ch = ch; // save code of last input letter
} // for next double-letter check
}
/* Pad up to 4 characters with DIGIT ZERO, if the string is shorter */
if (nchars < 4) {
uint nbytes = (4 - nchars) * cs->mbminlen;
cs->cset->fill(cs, to, nbytes, '0');
to += nbytes;
}
tmp_value.length((uint)(to - tmp_value.ptr()));
return &tmp_value;
}
/**
Change a number to format '3,333,333,333.000'.
This should be 'internationalized' sometimes.
*/
const int FORMAT_MAX_DECIMALS = 30;
MY_LOCALE *Item_func_format::get_locale(Item *) {
DBUG_ASSERT(arg_count == 3);
THD *thd = current_thd;
String tmp, *locale_name = args[2]->val_str_ascii(&tmp);
MY_LOCALE *lc;
if (!locale_name || !(lc = my_locale_by_name(thd, locale_name->ptr(),
locale_name->length()))) {
push_warning_printf(thd, Sql_condition::SL_WARNING, ER_UNKNOWN_LOCALE,
ER_THD(thd, ER_UNKNOWN_LOCALE),
locale_name ? locale_name->c_ptr_safe() : "NULL");
lc = &my_locale_en_US;
}
return lc;
}
bool Item_func_format::resolve_type(THD *) {
uint32 char_length = args[0]->max_char_length();
uint32 max_sep_count = (char_length / 3) + (decimals ? 1 : 0) + /*sign*/ 1;
set_data_type_string(char_length + max_sep_count + decimals,
default_charset());
if (arg_count == 3)
locale = args[2]->basic_const_item() ? get_locale(args[2]) : NULL;
else
locale = &my_locale_en_US; /* Two arguments */
return reject_geometry_args(arg_count, args, this);
}
/**
@todo
This needs to be fixed for multi-byte character set where numbers
are stored in more than one byte
*/
String *Item_func_format::val_str_ascii(String *str) {
size_t str_length;
/* Number of decimal digits */
int dec;
/* Number of characters used to represent the decimals, including '.' */
uint32 dec_length;
MY_LOCALE *lc;
DBUG_ASSERT(fixed == 1);
dec = (int)args[1]->val_int();
if (args[1]->null_value) {
null_value = 1;
return NULL;
}
lc = locale ? locale : get_locale(args[2]);
dec = set_zone(dec, 0, FORMAT_MAX_DECIMALS);
dec_length = dec ? dec + 1 : 0;
null_value = 0;
if (args[0]->result_type() == DECIMAL_RESULT ||
args[0]->result_type() == INT_RESULT) {
my_decimal dec_val, rnd_dec, *res;
res = args[0]->val_decimal(&dec_val);
if ((null_value = args[0]->null_value)) return 0; /* purecov: inspected */
my_decimal_round(E_DEC_FATAL_ERROR, res, dec, false, &rnd_dec);
my_decimal2string(E_DEC_FATAL_ERROR, &rnd_dec, 0, 0, 0, str);
str_length = str->length();
} else {
double nr = args[0]->val_real();
if ((null_value = args[0]->null_value)) return 0; /* purecov: inspected */
nr = my_double_round(nr, (longlong)dec, false, false);
str->set_real(nr, dec, &my_charset_numeric);
if (!std::isfinite(nr)) return str;
str_length = str->length();
}
/* We need this test to handle 'nan' and short values */
if (lc->grouping[0] > 0 && str_length >= dec_length + 1 + lc->grouping[0]) {
/* We need space for ',' between each group of digits as well. */
char buf[2 * FLOATING_POINT_BUFFER + 2] = {0};
int count;
const char *grouping = lc->grouping;
char sign_length = *str->ptr() == '-' ? 1 : 0;
const char *src = str->ptr() + str_length - dec_length - 1;
const char *src_begin = str->ptr() + sign_length;
char *dst = buf + 2 * FLOATING_POINT_BUFFER;
char *start_dst = dst;
/* Put the fractional part */
if (dec) {
dst -= (dec + 1);
*dst = lc->decimal_point;
memcpy(dst + 1, src + 2, dec);
}
/* Put the integer part with grouping */
for (count = *grouping; src >= src_begin; count--) {
/*
When *grouping==0x80 (which means "end of grouping")
count will be initialized to -1 and
we'll never get into this "if" anymore.
*/
if (count == 0) {
*--dst = lc->thousand_sep;
if (grouping[1]) grouping++;
count = *grouping;
}
DBUG_ASSERT(dst > buf);
*--dst = *src--;
}
if (sign_length) /* Put '-' */
*--dst = *str->ptr();
/* Put the rest of the integer part without grouping */
size_t result_length = start_dst - dst;
str->copy(dst, result_length, &my_charset_latin1);
} else if (dec_length && lc->decimal_point != '.') {
/*
For short values without thousands (<1000)
replace decimal point to localized value.
*/
DBUG_ASSERT(dec_length <= str_length);
(*str)[str_length - dec_length] = lc->decimal_point;
}
return str;
}
void Item_func_format::print(const THD *thd, String *str,
enum_query_type query_type) const {
str->append(STRING_WITH_LEN("format("));
args[0]->print(thd, str, query_type);
str->append(',');
args[1]->print(thd, str, query_type);
if (arg_count > 2) {
str->append(',');
args[2]->print(thd, str, query_type);
}
str->append(')');
}
bool Item_func_elt::resolve_type(THD *) {
uint32 char_length = 0;
decimals = 0;
if (agg_arg_charsets_for_string_result(collation, args + 1, arg_count - 1))
return true;
for (uint i = 1; i < arg_count; i++) {
set_if_bigger(char_length, args[i]->max_char_length());
set_if_bigger(decimals, args[i]->decimals);
}
set_data_type_string(char_length);
maybe_null = true; // NULL if wrong first arg
return false;
}
double Item_func_elt::val_real() {
DBUG_ASSERT(fixed == 1);
uint tmp;
null_value = 1;
if ((tmp = (uint)args[0]->val_int()) == 0 || args[0]->null_value ||
tmp >= arg_count)
return 0.0;
double result = args[tmp]->val_real();
null_value = args[tmp]->null_value;
return result;
}
longlong Item_func_elt::val_int() {
DBUG_ASSERT(fixed == 1);
uint tmp;
null_value = 1;
if ((tmp = (uint)args[0]->val_int()) == 0 || args[0]->null_value ||
tmp >= arg_count)
return 0;
longlong result = args[tmp]->val_int();
null_value = args[tmp]->null_value;
return result;
}
String *Item_func_elt::val_str(String *str) {
DBUG_ASSERT(fixed == 1);
uint tmp;
null_value = 1;
if ((tmp = (uint)args[0]->val_int()) == 0 || args[0]->null_value ||
tmp >= arg_count)
return NULL;
String *result = args[tmp]->val_str(str);
if (result) result->set_charset(collation.collation);
null_value = args[tmp]->null_value;
return result;
}
bool Item_func_make_set::itemize(Parse_context *pc, Item **res) {
if (skip_itemize(res)) return false;
/*
We have to itemize() the "item" before the super::itemize() call there since
this reflects the "natural" order of former semantic action code execution
in the original parser:
*/
return item->itemize(pc, &item) || super::itemize(pc, res);
}
void Item_func_make_set::split_sum_func(THD *thd, Ref_item_array ref_item_array,
List<Item> &fields) {
item->split_sum_func2(thd, ref_item_array, fields, &item, true);
Item_str_func::split_sum_func(thd, ref_item_array, fields);
}
bool Item_func_make_set::resolve_type(THD *) {
uint32 char_length = arg_count - 1; /* Separators */
if (agg_arg_charsets_for_string_result(collation, args, arg_count))
return true;
for (uint i = 0; i < arg_count; i++)
char_length += args[i]->max_char_length();
set_data_type_string(char_length);
used_tables_cache |= item->used_tables();
not_null_tables_cache &= item->not_null_tables();
add_accum_properties(item);
return false;
}
void Item_func_make_set::update_used_tables() {
Item_func::update_used_tables();
item->update_used_tables();
used_tables_cache |= item->used_tables();
add_accum_properties(item);
}
String *Item_func_make_set::val_str(String *str) {
DBUG_ASSERT(fixed == 1);
ulonglong bits;
bool first_found = 0;
Item **ptr = args;
String *result = NULL;
bits = item->val_int();
if ((null_value = item->null_value)) return NULL;
if (arg_count < 64) bits &= ((ulonglong)1 << arg_count) - 1;
for (; bits; bits >>= 1, ptr++) {
if (bits & 1) {
String *res = (*ptr)->val_str(str);
if (res) // Skip nulls
{
if (!first_found) { // First argument
first_found = 1;
if (res != str)
result = res; // Use original string
else {
if (tmp_str.copy(*res)) // Don't use 'str'
return make_empty_result();
result = &tmp_str;
}
} else {
if (result != &tmp_str) { // Copy data to tmp_str
if (tmp_str.alloc((result != NULL ? result->length() : 0) +
res->length() + 1) ||
tmp_str.copy(*result))
return make_empty_result();
result = &tmp_str;
}
if (tmp_str.append(STRING_WITH_LEN(","), &my_charset_bin) ||
tmp_str.append(*res))
return make_empty_result();
}
}
}
}
if (result == NULL) return make_empty_result();
return result;
}
Item *Item_func_make_set::transform(Item_transformer transformer, uchar *arg) {
Item *new_item = item->transform(transformer, arg);
if (!new_item) return 0;
/*
THD::change_item_tree() should be called only if the tree was
really transformed, i.e. when a new item has been created.
Otherwise we'll be allocating a lot of unnecessary memory for
change records at each execution.
*/
if (item != new_item) current_thd->change_item_tree(&item, new_item);
return Item_str_func::transform(transformer, arg);
}
void Item_func_make_set::print(const THD *thd, String *str,
enum_query_type query_type) const {
str->append(STRING_WITH_LEN("make_set("));
item->print(thd, str, query_type);
if (arg_count) {
str->append(',');
print_args(thd, str, 0, query_type);
}
str->append(')');
}
String *Item_func_char::val_str(String *str) {
DBUG_ASSERT(fixed == 1);
null_value = false;
str->length(0);
str->set_charset(collation.collation);
for (uint i = 0; i < arg_count; i++) {
int32 num = (int32)args[i]->val_int();
if (!args[i]->null_value) {
char tmp[4];
if (num & 0xFF000000L) {
mi_int4store(tmp, num);
str->append(tmp, 4, &my_charset_bin);
} else if (num & 0xFF0000L) {
mi_int3store(tmp, num);
str->append(tmp, 3, &my_charset_bin);
} else if (num & 0xFF00L) {
mi_int2store(tmp, num);
str->append(tmp, 2, &my_charset_bin);
} else {
tmp[0] = (char)num;
str->append(tmp, 1, &my_charset_bin);
}
}
}
str->mem_realloc(str->length()); // Add end 0 (for Purify)
String *res = check_well_formed_result(str,
false, // send warning
true); // truncate
if (!res) null_value = true;
return res;
}
inline String *alloc_buffer(String *res, String *str, String *tmp_value,
size_t length) {
if (res->alloced_length() < length) {
if (str->alloced_length() >= length) {
(void)str->copy(*res);
str->length(length);
return str;
}
if (tmp_value->alloc(length)) return 0;
(void)tmp_value->copy(*res);
tmp_value->length(length);
return tmp_value;
}
res->length(length);
return res;
}
bool Item_func_repeat::resolve_type(THD *thd) {
if (agg_arg_charsets_for_string_result(collation, args, 1)) return true;
DBUG_ASSERT(collation.collation != NULL);
if (args[1]->const_item()) {
/* must be longlong to avoid truncation */
longlong count = args[1]->val_int();
if (args[1]->null_value) goto end;
// If count is less than 1, returns an empty string.
Integer_value count_val(count, args[1]->unsigned_flag);
if (count_val.is_negative()) count = 0;
unsigned long long count_ull = static_cast<unsigned long long>(count);
/* Assumes that the maximum length of a String is < INT_MAX32. */
/* Set here so that rest of code sees out-of-bound value as such. */
if (count_ull > INT_MAX32) count_ull = INT_MAX32;
ulonglong char_length =
static_cast<ulonglong>(args[0]->max_char_length()) * count_ull;
set_data_type_string(char_length);
maybe_null = (maybe_null || max_length > thd->variables.max_allowed_packet);
return false;
}
end:
set_data_type_string(uint32(MAX_BLOB_WIDTH));
maybe_null = true;
return false;
}
/**
Item_func_repeat::str is carefully written to avoid reallocs
as much as possible at the cost of a local buffer
*/
String *Item_func_repeat::val_str(String *str) {
DBUG_ASSERT(fixed == 1);
size_t length, tot_length;
char *to;
/* must be longlong to avoid truncation */
longlong count = args[1]->val_int();
String *res = args[0]->val_str(str);
if (args[0]->null_value || args[1]->null_value)
return error_str(); // string and/or delim are null
null_value = false;
if (count <= 0 && (count == 0 || !args[1]->unsigned_flag))
return make_empty_result();
// Avoid looping, concatenating the empty string.
if (res->length() == 0) return res;
/* Assumes that the maximum length of a String is < INT_MAX32. */
/* Bounds check on count: If this is triggered, we will error. */
if ((ulonglong)count > INT_MAX32) count = INT_MAX32;
if (count == 1) // To avoid reallocs
return res;
length = res->length();
// Safe length check
if (length > current_thd->variables.max_allowed_packet / (uint)count) {
return push_packet_overflow_warning(current_thd, func_name());
}
tot_length = length * (uint)count;
if (res->uses_buffer_owned_by(str)) {
if (tmp_value.alloc(tot_length) || tmp_value.copy(*res)) return error_str();
tmp_value.length(tot_length);
res = &tmp_value;
} else if (!(res = alloc_buffer(res, str, &tmp_value, tot_length)))
return error_str();
to = res->ptr() + length;
while (--count) {
memcpy(to, res->ptr(), length);
to += length;
}
return res;
}
bool Item_func_space::resolve_type(THD *thd) {
collation.set(default_charset(), DERIVATION_COERCIBLE, MY_REPERTOIRE_ASCII);
if (args[0]->const_item()) {
/* must be longlong to avoid truncation */
longlong count = args[0]->val_int();
if (args[0]->null_value) goto end;
/*
Assumes that the maximum length of a String is < INT_MAX32.
Set here so that rest of code sees out-of-bound value as such.
*/
if (count > INT_MAX32) count = INT_MAX32;
set_data_type_string(ulonglong(count));
maybe_null = (maybe_null || max_length > thd->variables.max_allowed_packet);
return false;
}
end:
set_data_type_string(uint32(MAX_BLOB_WIDTH));
maybe_null = true;
return false;
}
String *Item_func_space::val_str(String *str) {
uint tot_length;
longlong count = args[0]->val_int();
const CHARSET_INFO *cs = collation.collation;
if (args[0]->null_value) return error_str(); // string and/or delim are null
null_value = 0;
if (count <= 0 && (count == 0 || !args[0]->unsigned_flag))
return make_empty_result();
/*
Assumes that the maximum length of a String is < INT_MAX32.
Bounds check on count: If this is triggered, we will error.
*/
if ((ulonglong)count > INT_MAX32) count = INT_MAX32;
// Safe length check
tot_length = (uint)count * cs->mbminlen;
if (tot_length > current_thd->variables.max_allowed_packet) {
return push_packet_overflow_warning(current_thd, func_name());
}
if (str->alloc(tot_length)) return error_str();
str->length(tot_length);
str->set_charset(cs);
cs->cset->fill(cs, str->ptr(), tot_length, ' ');
return str;
}
bool Item_func_rpad::resolve_type(THD *thd) {
// Handle character set for args[0] and args[2].
if (agg_arg_charsets_for_string_result(collation, &args[0], 2, 2))
return true;
if (args[1]->const_item()) {
ulonglong char_length = args[1]->val_uint();
if (args[1]->null_value) goto end;
DBUG_ASSERT(collation.collation->mbmaxlen > 0);
/* Assumes that the maximum length of a String is < INT_MAX32. */
/* Set here so that rest of code sees out-of-bound value as such. */
if (char_length > INT_MAX32) char_length = INT_MAX32;
set_data_type_string(char_length);
maybe_null = (maybe_null || max_length > thd->variables.max_allowed_packet);
return false;
}
end:
set_data_type_string(uint32(MAX_BLOB_WIDTH));
maybe_null = true;
return false;
}
String *Item_func_rpad::val_str(String *str) {
DBUG_ASSERT(fixed == 1);
char *to;
/* must be longlong to avoid truncation */
longlong count = args[1]->val_int();
/* Avoid modifying this string as it may affect args[0] */
String *res = args[0]->val_str(str);
String *rpad = args[2]->val_str(&rpad_str);
if ((null_value =
(args[0]->null_value || args[1]->null_value || args[2]->null_value)))
return nullptr;
if ((count < 0) && !args[1]->unsigned_flag) {
return null_return_str();
}
if (!res || !rpad) {
/* purecov: begin deadcode */
DBUG_ASSERT(false);
null_value = true;
return nullptr;
/* purecov: end */
}
/* Assumes that the maximum length of a String is < INT_MAX32. */
/* Set here so that rest of code sees out-of-bound value as such. */
if ((ulonglong)count > INT_MAX32) count = INT_MAX32;
/*
There is one exception not handled (intentionaly) by the character set
aggregation code. If one string is strong side and is binary, and
another one is weak side and is a multi-byte character string,
then we need to operate on the second string in terms on bytes when
calling ::numchars() and ::charpos(), rather than in terms of characters.
Lets substitute its character set to binary.
*/
if (collation.collation == &my_charset_bin) {
res->set_charset(&my_charset_bin);
rpad->set_charset(&my_charset_bin);
}
if (use_mb(rpad->charset())) {
// This will chop off any trailing illegal characters from rpad.
String *well_formed_pad =
args[2]->check_well_formed_result(rpad,
false, // send warning
true); // truncate
if (!well_formed_pad) {
DBUG_ASSERT(current_thd->is_error());
return error_str();
}
}
const size_t res_char_length = res->numchars();
if (count <=
static_cast<longlong>(res_char_length)) { // String to pad is big enough
int res_charpos = res->charpos((int)count);
if (tmp_value.alloc(res_charpos)) return nullptr;
(void)tmp_value.copy(*res);
tmp_value.length(res_charpos); // Shorten result if longer
return &tmp_value;
}
const size_t pad_char_length = rpad->numchars();
// Must be ulonglong to avoid overflow
const ulonglong byte_count = count * collation.collation->mbmaxlen;
if (byte_count > current_thd->variables.max_allowed_packet) {
return push_packet_overflow_warning(current_thd, func_name());
}
if (!pad_char_length) return make_empty_result();
/* Must be done before alloc_buffer */
const size_t res_byte_length = res->length();
/*
alloc_buffer() doesn't modify 'res' because 'res' is guaranteed too short
at this stage.
*/
if (!(res = alloc_buffer(res, str, &tmp_value,
static_cast<size_t>(byte_count)))) {
return error_str();
}
to = res->ptr() + res_byte_length;
const char *ptr_pad = rpad->ptr();
const size_t pad_byte_length = rpad->length();
count -= res_char_length;
for (; (uint32)count > pad_char_length; count -= pad_char_length) {
memcpy(to, ptr_pad, pad_byte_length);
to += pad_byte_length;
}
if (count) {
const size_t pad_charpos = rpad->charpos((int)count);
memcpy(to, ptr_pad, pad_charpos);
to += pad_charpos;
}
res->length((uint)(to - res->ptr()));
return (res);
}
bool Item_func_lpad::resolve_type(THD *thd) {
// Handle character set for args[0] and args[2].
if (agg_arg_charsets_for_string_result(collation, &args[0], 2, 2))
return true;
if (args[1]->const_item()) {
ulonglong char_length = args[1]->val_uint();
if (args[1]->null_value) goto end;
DBUG_ASSERT(collation.collation->mbmaxlen > 0);
/* Assumes that the maximum length of a String is < INT_MAX32. */
/* Set here so that rest of code sees out-of-bound value as such. */
if (char_length > INT_MAX32) char_length = INT_MAX32;
set_data_type_string(char_length);
maybe_null = (maybe_null || max_length > thd->variables.max_allowed_packet);
return false;
}
end:
set_data_type_string(uint32(MAX_BLOB_WIDTH));
maybe_null = true;
return false;
}
bool Item_func_uuid_to_bin::resolve_type(THD *) {
set_data_type_string(uint32(binary_log::Uuid::BYTE_LENGTH), &my_charset_bin);
maybe_null = true;
return false;
}
String *Item_func_uuid_to_bin::val_str(String *str) {
DBUG_ASSERT(fixed && (arg_count == 1 || arg_count == 2));
null_value = true;
String *res = args[0]->val_str(str);
if (!res || args[0]->null_value) return NULL;
if (binary_log::Uuid::parse(res->ptr(), res->length(), m_bin_buf)) goto err;
/*
If there is a second argument which is true, it means
that the uuid is version 1 which has the time-low part at the beginning
of the uuid. So in order to make it index-friendly the time-low
will be swapped with the time-high and the time-mid groups.
Time-high has length 4, time-mid and time-low have length 2.
(time-low)-(time-mid)-(time-high) => (time-high)-(time-mid)-(time-low)
*/
if (arg_count == 2 && args[1]->val_bool()) {
std::swap_ranges(&m_bin_buf[4], &m_bin_buf[4] + 2, &m_bin_buf[6]);
std::swap_ranges(&m_bin_buf[0], &m_bin_buf[0] + 4, &m_bin_buf[4]);
}
null_value = false;
str->set(reinterpret_cast<char *>(m_bin_buf), binary_log::Uuid::BYTE_LENGTH,
&my_charset_bin);
return str;
err:
ErrConvString err(res);
my_error(ER_WRONG_VALUE_FOR_TYPE, MYF(0), "string", err.ptr(), func_name());
return NULL;
}
bool Item_func_bin_to_uuid::resolve_type(THD *) {
set_data_type_string(uint32(binary_log::Uuid::TEXT_LENGTH),
default_charset());
maybe_null = true;
return false;
}
String *Item_func_bin_to_uuid::val_str_ascii(String *str) {
DBUG_ASSERT(fixed && (arg_count == 1 || arg_count == 2));
null_value = true;
String *res = args[0]->val_str(str);
if (!res || args[0]->null_value) return NULL;
if (res->length() != binary_log::Uuid::BYTE_LENGTH) goto err;
/*
If there is a second argument which is true,
the time-mid and time-high parts of uuid needs to be replaced
by time-low as they were previously shuffled to become index-friendly.
Time-high has length 4, time-mid and time-low have length 2.
(time-high)-(time-mid)-(time-low) => (time-low)-(time-mid)-(time-high)
*/
if (arg_count == 2 && args[1]->val_bool()) {
uchar rearranged[binary_log::Uuid::BYTE_LENGTH];
// The first 4 bytes are restored to "time-low".
std::copy_n(&res->ptr()[4], 4, &rearranged[0]);
// Bytes starting with 4th will be restored to "time-mid".
std::copy_n(&res->ptr()[2], 2, &rearranged[4]);
// Bytes starting with 6th will be restored to "time-high".
std::copy_n(&res->ptr()[0], 2, &rearranged[6]);
// The last 8 bytes were not changed so we just copy them.
std::copy_n(&res->ptr()[8], 8, &rearranged[8]);
binary_log::Uuid::to_string(rearranged, m_text_buf);
} else
binary_log::Uuid::to_string(reinterpret_cast<const uchar *>(res->ptr()),
m_text_buf);
null_value = false;
str->set(m_text_buf, binary_log::Uuid::TEXT_LENGTH, default_charset());
return str;
err:
ErrConvString err(res);
my_error(ER_WRONG_VALUE_FOR_TYPE, MYF(0), "string", err.ptr(), func_name());
return NULL;
}
longlong Item_func_is_uuid::val_int() {
DBUG_ASSERT(fixed && arg_count == 1);
null_value = true;
String buffer;
String *arg_str = args[0]->val_str(&buffer);
if (!arg_str) return 0;
null_value = false;
return binary_log::Uuid::is_valid(arg_str->ptr(), arg_str->length());
}
String *Item_func_lpad::val_str(String *str) {
DBUG_ASSERT(fixed == 1);
size_t res_char_length, pad_char_length;
/* must be longlong to avoid truncation */
longlong count = args[1]->val_int();
size_t byte_count;
/* Avoid modifying this string as it may affect args[0] */
String *res = args[0]->val_str(&tmp_value);
String *pad = args[2]->val_str(&lpad_str);
if ((null_value =
(args[0]->null_value || args[1]->null_value || args[2]->null_value)))
return nullptr;
if (count < 0 && !args[1]->unsigned_flag) {
return null_return_str();
}
if (!res || !pad) {
/* purecov: begin deadcode */
DBUG_ASSERT(false);
null_value = true;
return nullptr;
/* purecov: end */
}
/* Assumes that the maximum length of a String is < INT_MAX32. */
/* Set here so that rest of code sees out-of-bound value as such. */
if ((ulonglong)count > INT_MAX32) count = INT_MAX32;
/*
There is one exception not handled (intentionaly) by the character set
aggregation code. If one string is strong side and is binary, and
another one is weak side and is a multi-byte character string,
then we need to operate on the second string in terms on bytes when
calling ::numchars() and ::charpos(), rather than in terms of characters.
Lets substitute its character set to binary.
*/
if (collation.collation == &my_charset_bin) {
res->set_charset(&my_charset_bin);
pad->set_charset(&my_charset_bin);
}
if (use_mb(pad->charset())) {
// This will chop off any trailing illegal characters from pad.
String *well_formed_pad =
args[2]->check_well_formed_result(pad,
false, // send warning
true); // truncate
if (!well_formed_pad) {
DBUG_ASSERT(current_thd->is_error());
return error_str();
}
}
res_char_length = res->numchars();
if (count <= static_cast<longlong>(res_char_length)) {
int res_charpos = res->charpos((int)count);
if (tmp_value.alloc(res_charpos)) return nullptr;
(void)tmp_value.copy(*res);
tmp_value.length(res_charpos); // Shorten result if longer
return &tmp_value;
}
pad_char_length = pad->numchars();
byte_count = count * collation.collation->mbmaxlen;
if (byte_count > current_thd->variables.max_allowed_packet) {
return push_packet_overflow_warning(current_thd, func_name());
}
if (!pad_char_length) return make_empty_result();
if (str->alloc(byte_count)) {
my_error(ER_OOM, MYF(0));
return error_str();
}
str->length(0);
str->set_charset(collation.collation);
count -= res_char_length;
while (count >= static_cast<longlong>(pad_char_length)) {
str->append(*pad);
count -= pad_char_length;
}
if (count > 0)
str->append(pad->ptr(), pad->charpos((int)count), collation.collation);
str->append(*res);
null_value = 0;
return str;
}
bool Item_func_conv::resolve_type(THD *) {
set_data_type_string(uint32(64), default_charset());
maybe_null = true;
return reject_geometry_args(arg_count, args, this);
}
String *Item_func_conv::val_str(String *str) {
DBUG_ASSERT(fixed == 1);
String *res = args[0]->val_str(str);
const char *endptr;
char ans[65], *ptr;
longlong dec;
int from_base = (int)args[1]->val_int();
int to_base = (int)args[2]->val_int();
int err;
// Note that abs(INT_MIN) is undefined.
if (args[0]->null_value || args[1]->null_value || args[2]->null_value ||
from_base == INT_MIN || to_base == INT_MIN || abs(to_base) > 36 ||
abs(to_base) < 2 || abs(from_base) > 36 || abs(from_base) < 2 ||
!(res->length())) {
null_value = 1;
return NULL;
}
null_value = 0;
unsigned_flag = !(from_base < 0);
if (args[0]->data_type() == MYSQL_TYPE_BIT ||
args[0]->type() == VARBIN_ITEM) {
/*
Special case: The string representation of BIT doesn't resemble the
decimal representation, so we shouldn't change it to string and then to
decimal.
The same is true for hexadecimal and bit literals.
*/
dec = args[0]->val_int();
} else {
if (from_base < 0)
dec = my_strntoll(res->charset(), res->ptr(), res->length(), -from_base,
&endptr, &err);
else
dec = (longlong)my_strntoull(res->charset(), res->ptr(), res->length(),
from_base, &endptr, &err);
if (err) {
/*
If we got an overflow from my_strntoull, and the input was negative,
then return 0 rather than ~0
This is in order to be consistent with
CAST(<large negative value>, unsigned)
which returns zero.
*/
if (from_base > 0) {
my_decimal res_as_dec;
res_as_dec.sign(false);
str2my_decimal(E_DEC_OK, res->ptr(), res->length(), res->charset(),
&res_as_dec);
if (res_as_dec.sign()) dec = 0;
}
ErrConvString err(res);
push_warning_printf(
current_thd, Sql_condition::SL_WARNING, ER_TRUNCATED_WRONG_VALUE,
ER_THD(current_thd, ER_TRUNCATED_WRONG_VALUE), "DECIMAL", err.ptr());
}
}
if (!(ptr = longlong2str(dec, ans, to_base)) ||
str->copy(ans, (uint32)(ptr - ans), default_charset())) {
null_value = 1;
return NULL;
}
return str;
}
String *Item_func_conv_charset::val_str(String *str) {
DBUG_ASSERT(fixed == 1);
if (use_cached_value) return null_value ? 0 : &str_value;
String *arg = args[0]->val_str(str);
uint dummy_errors;
if (!arg) {
null_value = 1;
return 0;
}
null_value = tmp_value.copy(arg->ptr(), arg->length(), arg->charset(),
conv_charset, &dummy_errors);
return null_value ? 0
: check_well_formed_result(&tmp_value,
false, // send warning
true); // truncate
}
bool Item_func_conv_charset::resolve_type(THD *) {
collation.set(conv_charset, DERIVATION_IMPLICIT);
set_data_type_string(args[0]->max_char_length());
return false;
}
void Item_func_conv_charset::print(const THD *thd, String *str,
enum_query_type query_type) const {
str->append(STRING_WITH_LEN("convert("));
args[0]->print(thd, str, query_type);
str->append(STRING_WITH_LEN(" using "));
str->append(conv_charset->csname);
str->append(')');
}
bool Item_func_set_collation::itemize(Parse_context *pc, Item **res) {
if (skip_itemize(res)) return false;
THD *thd = pc->thd;
args[1] = new (pc->mem_root) Item_string(
collation_string.str, collation_string.length, thd->charset());
if (args[1] == NULL) return true;
return super::itemize(pc, res);
}
String *Item_func_set_collation::val_str(String *str) {
DBUG_ASSERT(fixed == 1);
str = args[0]->val_str(str);
if ((null_value = args[0]->null_value)) return 0;
str->set_charset(collation.collation);
return str;
}
bool Item_func_set_collation::resolve_type(THD *) {
CHARSET_INFO *set_collation;
const char *colname;
String tmp, *str = args[1]->val_str(&tmp);
colname = str->c_ptr();
if (colname == binary_keyword)
set_collation = get_charset_by_csname(args[0]->collation.collation->csname,
MY_CS_BINSORT, MYF(0));
else {
if (!(set_collation = mysqld_collation_get_by_name(colname))) return true;
}
if (!set_collation ||
(!my_charset_same(args[0]->collation.collation, set_collation) &&
args[0]->collation.derivation != DERIVATION_NUMERIC)) {
my_error(ER_COLLATION_CHARSET_MISMATCH, MYF(0), colname,
args[0]->collation.collation->csname);
return true;
}
collation.set(set_collation, DERIVATION_EXPLICIT,
args[0]->collation.repertoire);
set_data_type_string(args[0]->max_char_length());
return false;
}
bool Item_func_set_collation::eq(const Item *item, bool binary_cmp) const {
/* Assume we don't have rtti */
if (this == item) return true;
if (item->type() != FUNC_ITEM) return false;
const Item_func *item_func = down_cast<const Item_func *>(item);
if (arg_count != item_func->arg_count || functype() != item_func->functype())
return false;
const Item_func_set_collation *item_func_sc =
down_cast<const Item_func_set_collation *>(item);
if (collation.collation != item_func_sc->collation.collation) return false;
for (uint i = 0; i < arg_count; i++)
if (!args[i]->eq(item_func_sc->args[i], binary_cmp)) return false;
return true;
}
void Item_func_set_collation::print(const THD *thd, String *str,
enum_query_type query_type) const {
str->append('(');
args[0]->print(thd, str, query_type);
str->append(STRING_WITH_LEN(" collate "));
DBUG_ASSERT(args[1]->basic_const_item() &&
args[1]->type() == Item::STRING_ITEM);
String tmp;
args[1]->val_str(&tmp)->print(str);
str->append(')');
}
String *Item_func_charset::val_str(String *str) {
DBUG_ASSERT(fixed == 1);
uint dummy_errors;
const CHARSET_INFO *cs = args[0]->charset_for_protocol();
null_value = 0;
str->copy(cs->csname, strlen(cs->csname), &my_charset_latin1,
collation.collation, &dummy_errors);
return str;
}
String *Item_func_collation::val_str(String *str) {
DBUG_ASSERT(fixed == 1);
uint dummy_errors;
const CHARSET_INFO *cs = args[0]->charset_for_protocol();
null_value = 0;
str->copy(cs->name, strlen(cs->name), &my_charset_latin1, collation.collation,
&dummy_errors);
return str;
}
bool Item_func_weight_string::itemize(Parse_context *pc, Item **res) {
if (skip_itemize(res)) return false;
if (as_binary) {
if (args[0]->itemize(pc, &args[0])) return true;
args[0] = new (pc->mem_root)
Item_typecast_char(args[0], num_codepoints, &my_charset_bin);
if (args[0] == NULL) return true;
}
return super::itemize(pc, res);
}
void Item_func_weight_string::print(const THD *thd, String *str,
enum_query_type query_type) const {
str->append(func_name());
str->append('(');
args[0]->print(thd, str, query_type);
if (num_codepoints && !as_binary) {
str->append(" as char");
str->append_parenthesized(num_codepoints);
}
str->append(')');
}
bool Item_func_weight_string::resolve_type(THD *) {
const CHARSET_INFO *cs = args[0]->collation.collation;
collation.set(&my_charset_bin, args[0]->collation.derivation);
flags = my_strxfrm_flag_normalize(flags);
field = args[0]->type() == FIELD_ITEM && args[0]->is_temporal()
? ((Item_field *)(args[0]))->field
: (Field *)NULL;
/*
Use result_length if it was given explicitly in constructor,
otherwise calculate max_length using argument's max_length
and "num_codepoints".
FIXME: Shouldn't this use strxfrm_multiply instead of, or in
addition to, mbmaxlen? Do we take into account things like
UCA level separators at all?
*/
set_data_type_string(
field ? field->pack_length()
: result_length ? result_length
: cs->mbmaxlen * max(args[0]->max_char_length(),
num_codepoints));
maybe_null = true;
return false;
}
bool Item_func_weight_string::eq(const Item *item, bool binary_cmp) const {
if (this == item) return true;
if (item->type() != FUNC_ITEM) return false;
const Item_func *func_item = down_cast<const Item_func *>(item);
if (functype() != func_item->functype() ||
strcmp(func_name(), func_item->func_name()) != 0)
return false;
const Item_func_weight_string *wstr =
down_cast<const Item_func_weight_string *>(item);
if (num_codepoints != wstr->num_codepoints || flags != wstr->flags)
return false;
if (!args[0]->eq(wstr->args[0], binary_cmp)) return false;
return true;
}
/* Return a weight_string according to collation */
String *Item_func_weight_string::val_str(String *str) {
String *input = nullptr;
const CHARSET_INFO *cs = args[0]->collation.collation;
size_t output_buf_size, output_length;
bool rounded_up = false;
DBUG_ASSERT(fixed == 1);
if (args[0]->result_type() != STRING_RESULT ||
!(input = args[0]->val_str(str)))
return error_str();
/*
Use result_length if it was given in constructor
explicitly, otherwise calculate result length
from argument and "num_codepoints".
*/
output_buf_size =
field ? field->pack_length()
: result_length
? result_length
: cs->coll->strnxfrmlen(
cs, cs->mbmaxlen *
max<size_t>(input->length(), num_codepoints));
/*
my_strnxfrm() with an odd number of bytes is illegal for some collations;
ask for one more and then truncate ourselves instead.
*/
if ((output_buf_size % 2) == 1) {
++output_buf_size;
rounded_up = true;
}
if (output_buf_size > current_thd->variables.max_allowed_packet) {
return push_packet_overflow_warning(current_thd, func_name());
}
if (tmp_value.alloc(output_buf_size)) return error_str();
if (field) {
output_length = field->pack_length();
field->make_sort_key((uchar *)tmp_value.ptr(), output_buf_size);
} else {
size_t input_length = input->length();
size_t used_num_codepoints = num_codepoints;
if (num_codepoints) {
// Truncate the string to the requested number of code points.
input_length =
std::min(input_length, cs->cset->charpos(cs, input->ptr(),
input->ptr() + input_length,
num_codepoints));
} else {
/*
Give in exactly the right number of code points, so that we
do not get any excess trailing space from PAD SPACE collations.
*/
used_num_codepoints =
cs->cset->numchars(cs, input->ptr(), input->ptr() + input_length);
}
output_length = cs->coll->strnxfrm(
cs, (uchar *)tmp_value.ptr(), output_buf_size, used_num_codepoints,
(const uchar *)input->ptr(), input_length, flags);
}
DBUG_ASSERT(output_length <= output_buf_size);
if (rounded_up && output_length == output_buf_size) --output_length;
tmp_value.length(output_length);
null_value = 0;
return &tmp_value;
}
String *Item_func_hex::val_str_ascii(String *str) {
String *res;
DBUG_ASSERT(fixed == 1);
if (args[0]->result_type() != STRING_RESULT) {
/* Return hex of signed longlong value */
longlong dec = args[0]->val_int();
if ((null_value = args[0]->null_value)) return 0;
char ans[65], *ptr;
if (!(ptr = longlong2str(dec, ans, 16)) ||
str->copy(ans, (uint32)(ptr - ans), &my_charset_numeric))
return make_empty_result(); // End of memory
return str;
}
/* Convert given string to a hex string, character by character */
res = args[0]->val_str(str);
if (!res || tmp_value.alloc(res->length() * 2 + 1)) {
null_value = 1;
return 0;
}
null_value = 0;
tmp_value.length(res->length() * 2);
tmp_value.set_charset(&my_charset_latin1);
octet2hex(tmp_value.ptr(), res->ptr(), res->length());
return &tmp_value;
}
/** Convert given hex string to a binary string. */
String *Item_func_unhex::val_str(String *str) {
const char *from, *end;
char *to;
String *res;
size_t length;
null_value = true;
DBUG_ASSERT(fixed == 1);
res = args[0]->val_str(str);
// For a NULL input value return NULL without any warning
if (args[0]->null_value) return NULL;
if (!res || tmp_value.alloc(length = (1 + res->length()) / 2)) goto err;
from = res->ptr();
tmp_value.length(length);
to = tmp_value.ptr();
if (res->length() % 2) {
int hex_char = hexchar_to_int(*from++);
if (hex_char == -1) goto err;
*to++ = static_cast<char>(hex_char);
}
for (end = res->ptr() + res->length(); from < end; from += 2, to++) {
int hex_char = hexchar_to_int(from[0]);
if (hex_char == -1) goto err;
*to = static_cast<char>(hex_char << 4);
hex_char = hexchar_to_int(from[1]);
if (hex_char == -1) goto err;
*to |= hex_char;
}
null_value = false;
return &tmp_value;
err:
char buf[256];
String err(buf, sizeof(buf), system_charset_info);
err.length(0);
args[0]->print(current_thd, &err, QT_NO_DATA_EXPANSION);
push_warning_printf(current_thd, Sql_condition::SL_WARNING,
ER_WRONG_VALUE_FOR_TYPE,
ER_THD(current_thd, ER_WRONG_VALUE_FOR_TYPE), "string",
err.c_ptr_safe(), func_name());
return NULL;
}
#ifndef DBUG_OFF
String *Item_func_like_range::val_str(String *str) {
DBUG_ASSERT(fixed == 1);
longlong nbytes = args[1]->val_int();
String *res = args[0]->val_str(str);
size_t min_len, max_len;
const CHARSET_INFO *cs = collation.collation;
if (!res || args[0]->null_value || args[1]->null_value || nbytes < 0 ||
nbytes > MAX_BLOB_WIDTH || min_str.alloc(nbytes) || max_str.alloc(nbytes))
goto err;
null_value = 0;
if (cs->coll->like_range(cs, res->ptr(), res->length(), '\\', '_', '%',
nbytes, min_str.ptr(), max_str.ptr(), &min_len,
&max_len))
goto err;
min_str.set_charset(collation.collation);
max_str.set_charset(collation.collation);
min_str.length(min_len);
max_str.length(max_len);
return is_min ? &min_str : &max_str;
err:
null_value = 1;
return 0;
}
#endif
bool Item_typecast_char::eq(const Item *item, bool binary_cmp) const {
if (this == item) return true;
if (item->type() != FUNC_ITEM) return false;
const Item_func *func_item = down_cast<const Item_func *>(item);
if (functype() != func_item->functype() ||
strcmp(func_name(), func_item->func_name()))
return false;
const Item_typecast_char *cast = down_cast<const Item_typecast_char *>(item);
if (cast_length != cast->cast_length || cast_cs != cast->cast_cs)
return false;
if (!args[0]->eq(cast->args[0], binary_cmp)) return false;
return true;
}
void Item_typecast_char::print(const THD *thd, String *str,
enum_query_type query_type) const {
str->append(STRING_WITH_LEN("cast("));
args[0]->print(thd, str, query_type);
str->append(STRING_WITH_LEN(" as char"));
if (cast_length >= 0) str->append_parenthesized(cast_length);
if (cast_cs) {
str->append(STRING_WITH_LEN(" charset "));
str->append(cast_cs->csname);
}
str->append(')');
}
String *Item_typecast_char::val_str(String *str) {
DBUG_ASSERT(fixed);
THD *thd = current_thd;
uint32 length;
if (cast_length >= 0 &&
static_cast<ulonglong>(cast_length) > thd->variables.max_allowed_packet) {
return push_packet_overflow_warning(
thd, cast_cs == &my_charset_bin ? "cast_as_binary" : func_name());
}
String *res = args[0]->val_str(str);
if (res == nullptr) {
null_value = true;
return nullptr;
}
/*
Convert character set if differ
If it is a literal string, we must also take a copy.
*/
if (charset_conversion || res->alloced_length() == 0) {
uint dummy_err;
if (tmp_value.copy(res->ptr(), res->length(), from_cs, cast_cs, &dummy_err))
return error_str();
res = &tmp_value;
}
res->set_charset(cast_cs);
/*
Cut the tail if cast with length
and the result is longer than cast length, e.g.
CAST('string' AS CHAR(1))
*/
if (cast_length >= 0) {
if (res->length() > (length = (uint32)res->charpos(
cast_length))) { // Safe even if const arg
char char_type[40];
snprintf(char_type, sizeof(char_type), "%s(%lu)",
cast_cs == &my_charset_bin ? "BINARY" : "CHAR", (ulong)length);
if (!res->alloced_length()) { // Don't change const str
DBUG_ASSERT(res != &tmp_value);
tmp_value = *res; // Not malloced string
res = &tmp_value;
}
ErrConvString err(res);
push_warning_printf(
thd, Sql_condition::SL_WARNING, ER_TRUNCATED_WRONG_VALUE,
ER_THD(thd, ER_TRUNCATED_WRONG_VALUE), char_type, err.ptr());
res->length(length);
} else if (cast_cs == &my_charset_bin &&
res->length() < static_cast<ulonglong>(cast_length)) {
if (res->alloced_length() < static_cast<ulonglong>(cast_length)) {
if (res == &tmp_value) {
if (tmp_value.reserve(cast_length - res->length()))
return error_str();
} else {
if (tmp_value.reserve(cast_length)) return error_str();
tmp_value.copy(*res);
res = &tmp_value;
}
}
memset(res->ptr() + res->length(), 0, cast_length - res->length());
res->length(cast_length);
}
}
null_value = false;
return res;
}
bool Item_typecast_char::resolve_type(THD *thd) {
/*
If we convert between two ASCII compatible character sets and the
argument repertoire is MY_REPERTOIRE_ASCII then from_cs is set to cast_cs.
This allows just to take over the args[0]->val_str() result
and thus avoid unnecessary character set conversion.
*/
from_cs = args[0]->collation.repertoire == MY_REPERTOIRE_ASCII &&
my_charset_is_ascii_based(cast_cs) &&
my_charset_is_ascii_based(args[0]->collation.collation)
? cast_cs
: args[0]->collation.collation;
collation.set(cast_cs, DERIVATION_IMPLICIT);
set_data_type_string((uint32)(cast_length >= 0
? cast_length
: cast_cs == &my_charset_bin
? args[0]->max_length
: args[0]->max_char_length()));
maybe_null = (maybe_null || max_length > thd->variables.max_allowed_packet);
/*
We always force character set conversion if cast_cs
is a multi-byte character set. It garantees that the
result of CAST is a well-formed string.
For single-byte character sets we allow just to copy from the argument.
A single-byte character sets string is always well-formed.
*/
charset_conversion =
(cast_cs->mbmaxlen > 1) ||
(!my_charset_same(from_cs, cast_cs) && from_cs != &my_charset_bin &&
cast_cs != &my_charset_bin);
return false;
}
bool Item_load_file::itemize(Parse_context *pc, Item **res) {
if (skip_itemize(res)) return false;
if (super::itemize(pc, res)) return true;
pc->thd->lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_SYSTEM_FUNCTION);
pc->thd->lex->set_uncacheable(pc->select, UNCACHEABLE_SIDEEFFECT);
return false;
}
#include <fcntl.h>
#include <limits.h>
#include <stdlib.h>
#include <sys/stat.h>
String *Item_load_file::val_str(String *str) {
DBUG_ASSERT(fixed == 1);
String *file_name;
File file;
MY_STAT stat_info;
char path[FN_REFLEN];
uchar buf[4096];
if (!(file_name = args[0]->val_str(str)) ||
!(current_thd->security_context()->check_access(FILE_ACL))) {
DBUG_ASSERT(maybe_null);
return error_str();
}
(void)fn_format(path, file_name->c_ptr_safe(), mysql_real_data_home, "",
MY_RELATIVE_PATH | MY_UNPACK_FILENAME);
/* Read only allowed from within dir specified by secure_file_priv */
if (!is_secure_file_path(path)) {
DBUG_ASSERT(maybe_null);
return error_str();
}
if ((file = mysql_file_open(key_file_loadfile, file_name->ptr(), O_RDONLY,
MYF(0))) < 0) {
DBUG_ASSERT(maybe_null);
return error_str();
}
if (mysql_file_fstat(file, &stat_info) != 0) {
mysql_file_close(file, MYF(0));
DBUG_ASSERT(maybe_null);
return error_str();
}
if (!MY_S_ISREG(stat_info.st_mode)) {
my_error(ER_TEXTFILE_NOT_READABLE, MYF(0), file_name->c_ptr());
mysql_file_close(file, MYF(0));
DBUG_ASSERT(maybe_null);
return error_str();
}
tmp_value.length(0);
for (;;) {
int ret = mysql_file_read(file, buf, sizeof(buf), MYF(0));
if (ret == -1) {
mysql_file_close(file, MYF(0));
DBUG_ASSERT(maybe_null);
return error_str();
}
if (ret == 0) {
// EOF.
break;
}
tmp_value.append(pointer_cast<char *>(buf), ret);
if (tmp_value.length() > current_thd->variables.max_allowed_packet) {
mysql_file_close(file, MYF(0));
return push_packet_overflow_warning(current_thd, func_name());
}
}
mysql_file_close(file, MYF(0));
null_value = 0;
return &tmp_value;
}
String *Item_func_export_set::val_str(String *str) {
DBUG_ASSERT(fixed == 1);
String yes_buf, no_buf, sep_buf;
const ulonglong the_set = (ulonglong)args[0]->val_int();
const String *yes = args[1]->val_str(&yes_buf);
const String *no = args[2]->val_str(&no_buf);
const String *sep = NULL;
ulonglong num_set_values = 64;
str->length(0);
str->set_charset(collation.collation);
/* Check if some argument is a NULL value */
if (args[0]->null_value || args[1]->null_value || args[2]->null_value)
return error_str();
/*
Arg count can only be 3, 4 or 5 here. This is guaranteed from the
grammar for EXPORT_SET()
*/
switch (arg_count) {
case 5:
num_set_values = static_cast<ulonglong>(args[4]->val_int());
if (num_set_values > 64) num_set_values = 64;
if (args[4]->null_value) return error_str();
/* Fall through */
case 4:
if (!(sep = args[3]->val_str(&sep_buf))) // Only true if NULL
return error_str();
break;
case 3: {
/* errors is not checked - assume "," can always be converted */
uint errors;
sep_buf.copy(STRING_WITH_LEN(","), &my_charset_bin, collation.collation,
&errors);
sep = &sep_buf;
} break;
default:
DBUG_ASSERT(0); // cannot happen
}
null_value = false;
const ulonglong max_allowed_packet =
current_thd->variables.max_allowed_packet;
const ulonglong num_separators = num_set_values > 0 ? num_set_values - 1 : 0;
const ulonglong max_total_length =
num_set_values * max(yes->length(), no->length()) +
num_separators * sep->length();
if (unlikely(max_total_length > max_allowed_packet)) {
return push_packet_overflow_warning(current_thd, func_name());
}
uint ix;
ulonglong mask;
for (ix = 0, mask = 0x1; ix < num_set_values; ++ix, mask = (mask << 1)) {
if (the_set & mask)
str->append(*yes);
else
str->append(*no);
if (ix != num_separators) str->append(*sep);
}
if (str->ptr() == NULL) return make_empty_result();
return str;
}
bool Item_func_export_set::resolve_type(THD *thd) {
uint32 length = max(args[1]->max_char_length(), args[2]->max_char_length());
uint32 sep_length = (arg_count > 3 ? args[3]->max_char_length() : 1);
if (agg_arg_charsets_for_string_result(collation, args + 1,
min(4U, arg_count) - 1))
return true;
set_data_type_string(length * 64U + sep_length * 63U);
maybe_null = (maybe_null || max_length > thd->variables.max_allowed_packet);
return false;
}
bool Item_func_quote::resolve_type(THD *thd) {
/*
Since QUOTE may add escapes to potentially all the characters in its
argument, we need to compute the maximum by multiplying the argument's
maximum character length with 2, and then add 2 for the surrounding
single quotes added by QUOTE. NULLs print as NULL without single quotes
so their maximum length is 4.
*/
ulonglong max_result_length = std::max<ulonglong>(
4, static_cast<ulonglong>(args[0]->max_char_length()) * 2U + 2U);
collation.set(args[0]->collation);
set_data_type_string(max_result_length);
maybe_null = (maybe_null || max_length > thd->variables.max_allowed_packet);
return false;
}
#define get_esc_bit(mask, num) (1 & (*((mask) + ((num) >> 3))) >> ((num)&7))
/**
QUOTE() function returns argument string in single quotes suitable for
using in a SQL statement.
Adds a \\ before all characters that needs to be escaped in a SQL string.
We also escape '^Z' (END-OF-FILE in windows) to avoid probelms when
running commands from a file in windows.
This function is very useful when you want to generate SQL statements.
@note
QUOTE(NULL) returns the string 'NULL' (4 letters, without quotes).
@retval
str Quoted string
@retval
NULL Out of memory.
*/
String *Item_func_quote::val_str(String *str) {
DBUG_ASSERT(fixed == 1);
/*
Bit mask that has 1 for set for the position of the following characters:
0, \, ' and ^Z
*/
static uchar escmask[32] = {0x01, 0x00, 0x00, 0x04, 0x80, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
char *to;
const char *from, *end, *start;
String *arg = args[0]->val_str(str);
size_t arg_length, new_length;
if (!arg) // Null argument
{
/* Return the string 'NULL' */
str->copy(STRING_WITH_LEN("NULL"), collation.collation);
null_value = 0;
return str;
}
arg_length = arg->length();
if (collation.collation->mbmaxlen == 1) {
new_length = arg_length + 2; /* for beginning and ending ' signs */
for (from = arg->ptr(), end = from + arg_length; from < end; from++)
new_length += get_esc_bit(escmask, (uchar)*from);
} else {
new_length = (arg_length * 2) + /* For string characters */
(2 * collation.collation->mbmaxlen); /* For quotes */
}
if (tmp_value.alloc(new_length)) goto null;
if (collation.collation->mbmaxlen > 1) {
const CHARSET_INFO *cs = collation.collation;
int mblen;
uchar *to_end;
to = tmp_value.ptr();
to_end = (uchar *)to + new_length;
/* Put leading quote */
if ((mblen = cs->cset->wc_mb(cs, '\'', (uchar *)to, to_end)) <= 0)
goto null;
to += mblen;
for (start = arg->ptr(), end = start + arg_length; start < end;) {
my_wc_t wc;
bool escape;
if ((mblen = cs->cset->mb_wc(cs, &wc, pointer_cast<const uchar *>(start),
pointer_cast<const uchar *>(end))) <= 0)
goto null;
start += mblen;
switch (wc) {
case 0:
escape = 1;
wc = '0';
break;
case '\032':
escape = 1;
wc = 'Z';
break;
case '\'':
escape = 1;
break;
case '\\':
escape = 1;
break;
default:
escape = 0;
break;
}
if (escape) {
if ((mblen = cs->cset->wc_mb(cs, '\\', (uchar *)to, to_end)) <= 0)
goto null;
to += mblen;
}
if ((mblen = cs->cset->wc_mb(cs, wc, (uchar *)to, to_end)) <= 0)
goto null;
to += mblen;
}
/* Put trailing quote */
if ((mblen = cs->cset->wc_mb(cs, '\'', (uchar *)to, to_end)) <= 0)
goto null;
to += mblen;
new_length = to - tmp_value.ptr();
goto ret;
}
/*
We replace characters from the end to the beginning
*/
to = tmp_value.ptr() + new_length - 1;
*to-- = '\'';
for (start = arg->ptr(), end = start + arg_length; end-- != start; to--) {
/*
We can't use the bitmask here as we want to replace \O and ^Z with 0
and Z
*/
switch (*end) {
case 0:
*to-- = '0';
*to = '\\';
break;
case '\032':
*to-- = 'Z';
*to = '\\';
break;
case '\'':
case '\\':
*to-- = *end;
*to = '\\';
break;
default:
*to = *end;
break;
}
}
*to = '\'';
ret:
if (new_length > current_thd->variables.max_allowed_packet) {
return push_packet_overflow_warning(current_thd, func_name());
}
tmp_value.length(new_length);
tmp_value.set_charset(collation.collation);
null_value = 0;
return &tmp_value;
null:
null_value = 1;
return 0;
}
/**
@returns The length that the compressed string args[0] had before
being compressed.
@note This function is supposed to handle this case:
SELECT UNCOMPRESSED_LENGTH(COMPRESS(<some string>))
However, in mysql tradition, the input argument can be *anything*.
We return NULL if evaluation of the input argument returns NULL.
If the input string does not look like something produced by
Item_func_compress::val_str, we issue a warning and return 0.
*/
longlong Item_func_uncompressed_length::val_int() {
DBUG_ASSERT(fixed == 1);
String *res = args[0]->val_str(&value);
if ((null_value = args[0]->null_value)) return 0;
if (!res || res->is_empty()) return 0;
/*
If length is <= 4 bytes, data is corrupt. This is the best we can do
to detect garbage input without decompressing it.
*/
if (res->length() <= 4) {
push_warning(current_thd, Sql_condition::SL_WARNING, ER_ZLIB_Z_DATA_ERROR,
ER_THD(current_thd, ER_ZLIB_Z_DATA_ERROR));
return 0;
}
/*
res->ptr() using is safe because we have tested that string is at least
5 bytes long.
res->c_ptr() is not used because:
- we do not need \0 terminated string to get first 4 bytes
- c_ptr() tests simbol after string end (uninitialiozed memory) which
confuse valgrind
*/
return uint4korr(res->ptr()) & 0x3FFFFFFF;
}
longlong Item_func_crc32::val_int() {
DBUG_ASSERT(fixed == 1);
String *res = args[0]->val_str(&value);
if (!res) {
null_value = 1;
return 0; /* purecov: inspected */
}
null_value = 0;
return (longlong)crc32(0L, (uchar *)res->ptr(), res->length());
}
String *Item_func_compress::val_str(String *str) {
int err = Z_OK, code;
ulong new_size;
String *res;
Byte *body;
char *last_char;
DBUG_ASSERT(fixed == 1);
if (!(res = args[0]->val_str(str))) {
null_value = 1;
return 0;
}
null_value = 0;
if (res->is_empty()) return res;
/*
Citation from zlib.h (comment for compress function):
Compresses the source buffer into the destination buffer. sourceLen is
the byte length of the source buffer. Upon entry, destLen is the total
size of the destination buffer, which must be at least 0.1% larger than
sourceLen plus 12 bytes.
We assume here that the buffer can't grow more than .25 %.
*/
new_size = res->length() + res->length() / 5 + 12;
// Check new_size overflow: new_size <= res->length()
if (((new_size + 5) <= res->length()) ||
buffer.mem_realloc(new_size + 4 + 1)) {
null_value = 1;
return 0;
}
body = ((Byte *)buffer.ptr()) + 4;
// As far as we have checked res->is_empty() we can use ptr()
if ((err = compress(body, &new_size, (const Bytef *)res->ptr(),
res->length())) != Z_OK) {
code = err == Z_MEM_ERROR ? ER_ZLIB_Z_MEM_ERROR : ER_ZLIB_Z_BUF_ERROR;
push_warning(current_thd, Sql_condition::SL_WARNING, code,
err == Z_MEM_ERROR ? ER_THD(current_thd, ER_ZLIB_Z_MEM_ERROR)
: ER_THD(current_thd, ER_ZLIB_Z_BUF_ERROR));
null_value = 1;
return 0;
}
int4store(buffer.ptr(), res->length() & 0x3FFFFFFF);
/* This is to ensure that things works for CHAR fields, which trim ' ': */
last_char = ((char *)body) + new_size - 1;
if (*last_char == ' ') {
*++last_char = '.';
new_size++;
}
buffer.length(new_size + 4);
return &buffer;
}
String *Item_func_uncompress::val_str(String *str) {
DBUG_ASSERT(fixed == 1);
String *res = args[0]->val_str(str);
ulong new_size;
int err;
uint code;
if (!res) goto err;
null_value = 0;
if (res->is_empty()) return res;
/* If length is less than 4 bytes, data is corrupt */
if (res->length() <= 4) {
push_warning(current_thd, Sql_condition::SL_WARNING, ER_ZLIB_Z_DATA_ERROR,
ER_THD(current_thd, ER_ZLIB_Z_DATA_ERROR));
goto err;
}
/* Size of uncompressed data is stored as first 4 bytes of field */
new_size = uint4korr(res->ptr()) & 0x3FFFFFFF;
if (new_size > current_thd->variables.max_allowed_packet) {
push_warning_printf(
current_thd, Sql_condition::SL_WARNING, ER_TOO_BIG_FOR_UNCOMPRESS,
ER_THD(current_thd, ER_TOO_BIG_FOR_UNCOMPRESS),
static_cast<int>(current_thd->variables.max_allowed_packet));
goto err;
}
if (buffer.mem_realloc((uint32)new_size)) goto err;
if ((err = uncompress(pointer_cast<Byte *>(buffer.ptr()), &new_size,
pointer_cast<const Bytef *>(res->ptr()) + 4,
res->length() - 4)) == Z_OK) {
buffer.length((uint32)new_size);
return &buffer;
}
code = ((err == Z_BUF_ERROR) ? ER_ZLIB_Z_BUF_ERROR
: ((err == Z_MEM_ERROR) ? ER_ZLIB_Z_MEM_ERROR
: ER_ZLIB_Z_DATA_ERROR));
push_warning(current_thd, Sql_condition::SL_WARNING, code,
err == Z_BUF_ERROR
? ER_THD(current_thd, ER_ZLIB_Z_BUF_ERROR)
: (err == Z_MEM_ERROR)
? ER_THD(current_thd, ER_ZLIB_Z_MEM_ERROR)
: ER_THD(current_thd, ER_ZLIB_Z_DATA_ERROR));
err:
null_value = 1;
return 0;
}
/*
UUID, as in
DCE 1.1: Remote Procedure Call,
Open Group Technical Standard Document Number C706, October 1997,
(supersedes C309 DCE: Remote Procedure Call 8/1994,
which was basis for ISO/IEC 11578:1996 specification)
*/
static struct rand_struct uuid_rand;
static uint nanoseq;
static ulonglong uuid_time = 0;
static char clock_seq_and_node_str[] = "-0000-000000000000";
/**
number of 100-nanosecond intervals between
1582-10-15 00:00:00.00 and 1970-01-01 00:00:00.00.
*/
#define UUID_TIME_OFFSET ((ulonglong)141427 * 24 * 60 * 60 * 1000 * 1000 * 10)
#define UUID_VERSION 0x1000
#define UUID_VARIANT 0x8000
static void tohex(char *to, uint from, uint len) {
to += len;
while (len--) {
*--to = _dig_vec_lower[from & 15];
from >>= 4;
}
}
static void set_clock_seq_str() {
uint16 clock_seq = ((uint)(my_rnd(&uuid_rand) * 16383)) | UUID_VARIANT;
tohex(clock_seq_and_node_str + 1, clock_seq, 4);
nanoseq = 0;
}
bool Item_func_uuid::resolve_type(THD *) {
collation.set(system_charset_info, DERIVATION_COERCIBLE, MY_REPERTOIRE_ASCII);
set_data_type_string(uint32(UUID_LENGTH));
return false;
}
bool Item_func_uuid::itemize(Parse_context *pc, Item **res) {
if (skip_itemize(res)) return false;
if (super::itemize(pc, res)) return true;
pc->thd->lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_SYSTEM_FUNCTION);
pc->thd->lex->safe_to_cache_query = 0;
return false;
}
String *mysql_generate_uuid(String *str) {
char *s;
THD *thd = current_thd;
mysql_mutex_lock(&LOCK_uuid_generator);
if (!uuid_time) /* first UUID() call. initializing data */
{
ulong tmp = sql_rnd_with_mutex();
uchar mac[6];
int i;
if (my_gethwaddr(mac)) {
/* purecov: begin inspected */
/*
generating random "hardware addr"
and because specs explicitly specify that it should NOT correlate
with a clock_seq value (initialized random below), we use a separate
randominit() here
*/
randominit(&uuid_rand, tmp + (ulong)thd,
tmp + (ulong)atomic_global_query_id);
for (i = 0; i < (int)sizeof(mac); i++)
mac[i] = (uchar)(my_rnd(&uuid_rand) * 255);
/* purecov: end */
}
s = clock_seq_and_node_str + sizeof(clock_seq_and_node_str) - 1;
for (i = sizeof(mac) - 1; i >= 0; i--) {
*--s = _dig_vec_lower[mac[i] & 15];
*--s = _dig_vec_lower[mac[i] >> 4];
}
randominit(&uuid_rand, tmp + (ulong)server_start_time,
tmp + (ulong)thd->status_var.bytes_sent);
set_clock_seq_str();
}
ulonglong tv = my_getsystime() + UUID_TIME_OFFSET + nanoseq;
if (likely(tv > uuid_time)) {
/*
Current time is ahead of last timestamp, as it should be.
If we "borrowed time", give it back, just as long as we
stay ahead of the previous timestamp.
*/
if (nanoseq) {
DBUG_ASSERT((tv > uuid_time) && (nanoseq > 0));
/*
-1 so we won't make tv= uuid_time for nanoseq >= (tv - uuid_time)
*/
ulong delta = min<ulong>(nanoseq, (ulong)(tv - uuid_time - 1));
tv -= delta;
nanoseq -= delta;
}
} else {
if (unlikely(tv == uuid_time)) {
/*
For low-res system clocks. If several requests for UUIDs
end up on the same tick, we add a nano-second to make them
different.
( current_timestamp + nanoseq * calls_in_this_period )
may end up > next_timestamp; this is OK. Nonetheless, we'll
try to unwind nanoseq when we get a chance to.
If nanoseq overflows, we'll start over with a new numberspace
(so the if() below is needed so we can avoid the ++tv and thus
match the follow-up if() if nanoseq overflows!).
*/
if (likely(++nanoseq)) ++tv;
}
if (unlikely(tv <= uuid_time)) {
/*
If the admin changes the system clock (or due to Daylight
Saving Time), the system clock may be turned *back* so we
go through a period once more for which we already gave out
UUIDs. To avoid duplicate UUIDs despite potentially identical
times, we make a new random component.
We also come here if the nanoseq "borrowing" overflows.
In either case, we throw away any nanoseq borrowing since it's
irrelevant in the new numberspace.
*/
set_clock_seq_str();
tv = my_getsystime() + UUID_TIME_OFFSET;
nanoseq = 0;
DBUG_PRINT("uuid", ("making new numberspace"));
}
}
uuid_time = tv;
mysql_mutex_unlock(&LOCK_uuid_generator);
uint32 time_low = (uint32)(tv & 0xFFFFFFFF);
uint16 time_mid = (uint16)((tv >> 32) & 0xFFFF);
uint16 time_hi_and_version = (uint16)((tv >> 48) | UUID_VERSION);
str->mem_realloc(UUID_LENGTH + 1);
str->length(UUID_LENGTH);
str->set_charset(system_charset_info);
s = str->ptr();
s[8] = s[13] = '-';
tohex(s, time_low, 8);
tohex(s + 9, time_mid, 4);
tohex(s + 14, time_hi_and_version, 4);
my_stpcpy(s + 18, clock_seq_and_node_str);
DBUG_EXECUTE_IF("force_fake_uuid",
my_stpcpy(s, "a2d00942-b69c-11e4-a696-0020ff6fcbe6"););
return str;
}
String *Item_func_uuid::val_str(String *str) {
DBUG_ASSERT(fixed == 1);
return mysql_generate_uuid(str);
}
bool Item_func_gtid_subtract::resolve_type(THD *) {
maybe_null = args[0]->maybe_null || args[1]->maybe_null;
collation.set(default_charset(), DERIVATION_COERCIBLE, MY_REPERTOIRE_ASCII);
/*
In the worst case, the string grows after subtraction. This
happens when a GTID in args[0] is split by a GTID in args[1],
e.g., UUID:1-6 minus UUID:3-4 becomes UUID:1-2,5-6. The worst
case is UUID:1-100 minus UUID:9, where the two characters ":9" in
args[1] yield the five characters "-8,10" in the result.
*/
set_data_type_string(
args[0]->max_length +
max<ulonglong>(args[1]->max_length - binary_log::Uuid::TEXT_LENGTH, 0) *
5 / 2);
return false;
}
String *Item_func_gtid_subtract::val_str_ascii(String *str) {
DBUG_TRACE;
String *str1, *str2;
const char *charp1, *charp2;
enum_return_status status;
/*
We must execute args[*]->val_str_ascii() before checking
args[*]->null_value to ensure that them are updated when
this function is executed inside a stored procedure.
*/
if ((str1 = args[0]->val_str_ascii(&buf1)) != NULL &&
(charp1 = str1->c_ptr_safe()) != NULL &&
(str2 = args[1]->val_str_ascii(&buf2)) != NULL &&
(charp2 = str2->c_ptr_safe()) != NULL && !args[0]->null_value &&
!args[1]->null_value) {
Sid_map sid_map(NULL /*no rwlock*/);
// compute sets while holding locks
Gtid_set set1(&sid_map, charp1, &status);
if (status == RETURN_STATUS_OK) {
Gtid_set set2(&sid_map, charp2, &status);
size_t length;
// subtract, save result, return result
if (status == RETURN_STATUS_OK) {
set1.remove_gtid_set(&set2);
if (!str->mem_realloc((length = set1.get_string_length()) + 1)) {
null_value = false;
set1.to_string(str->ptr());
str->length(length);
return str;
}
}
}
}
null_value = true;
return NULL;
}
/**
@brief
This function prepares string with list of column privileges.
This is required for IS implementation which uses views on DD tables.
In older non-DD model, we use to get this string using get_column_grant().
With new IS implementation using DD, we can't call get_column_grant().
The following UDF implementation solves the problem,
Syntax:
string get_dd_column_privileges(schema_name,
table_name,
field_name);
*/
String *Item_func_get_dd_column_privileges::val_str(String *str) {
DBUG_TRACE;
std::ostringstream oss("");
//
// Retrieve required values to form column type string.
//
// Read schema_name, table_name, field_name
String schema_name;
String *schema_name_ptr;
String table_name;
String *table_name_ptr = nullptr;
String field_name;
String *field_name_ptr = nullptr;
if ((schema_name_ptr = args[0]->val_str(&schema_name)) != nullptr &&
(table_name_ptr = args[1]->val_str(&table_name)) != nullptr &&
(field_name_ptr = args[2]->val_str(&field_name)) != nullptr) {
if (!is_infoschema_db(schema_name_ptr->c_ptr_safe())) {
//
// Get privileges
//
THD *thd = current_thd;
GRANT_INFO grant_info;
fill_effective_table_privileges(thd, &grant_info,
schema_name_ptr->c_ptr_safe(),
table_name_ptr->c_ptr_safe());
// Get column grants
uint col_access;
col_access =
get_column_grant(thd, &grant_info, schema_name_ptr->c_ptr_safe(),
table_name_ptr->c_ptr_safe(),
field_name_ptr->c_ptr_safe()) &
COL_ACLS;
// Prepare user readable output string with column access grants
for (uint bitnr = 0; col_access; col_access >>= 1, bitnr++) {
if (col_access & 1) {
if (oss.str().length()) oss << ',';
oss << grant_types.type_names[bitnr];
}
}
} // INFORMATION SCHEMA Tables have SELECT privileges.
else
oss << "select";
}
str->copy(oss.str().c_str(), oss.str().length(), system_charset_info);
return str;
}
/**
@brief
This function prepares string representing create_options for table.
This is required for IS implementation which uses views on DD tables.
In older non-DD model, FRM file had only user options specified in
CREATE TABLE statement.
With new IS implementation using DD, all internal option values are
also stored in options field.
So, this UDF filters internal options from user defined options
Syntax:
string get_dd_create_options(dd.table.options)
The arguments accept values from options from 'tables' DD table,
as shown above.
*/
String *Item_func_get_dd_create_options::val_str(String *str) {
DBUG_TRACE;
// Read tables.options
String option;
String *option_ptr;
std::ostringstream oss("");
if ((option_ptr = args[0]->val_str(&option)) == nullptr) {
str->copy(oss.str().c_str(), oss.str().length(), system_charset_info);
return str;
}
// Read required values from properties
std::unique_ptr<dd::Properties> p(
dd::Properties::parse_properties(option_ptr->c_ptr_safe()));
// Warn if the property string is corrupt.
if (!p.get()) {
LogErr(WARNING_LEVEL, ER_WARN_PROPERTY_STRING_PARSE_FAILED,
option_ptr->c_ptr_safe());
if (DBUG_EVALUATE_IF("continue_on_property_string_parse_failure", 0, 1))
DBUG_ASSERT(false);
str->copy(oss.str().c_str(), oss.str().length(), system_charset_info);
return str;
}
// Read used_flags
uint opt_value = 0;
char option_buff[350], *ptr;
ptr = option_buff;
if (p->exists("max_rows")) {
p->get("max_rows", &opt_value);
if (opt_value != 0) {
ptr = my_stpcpy(ptr, " max_rows=");
ptr = longlong10_to_str(opt_value, ptr, 10);
}
}
if (p->exists("min_rows")) {
p->get("min_rows", &opt_value);
if (opt_value != 0) {
ptr = my_stpcpy(ptr, " min_rows=");
ptr = longlong10_to_str(opt_value, ptr, 10);
}
}
if (p->exists("avg_row_length")) {
p->get("avg_row_length", &opt_value);
if (opt_value != 0) {
ptr = my_stpcpy(ptr, " avg_row_length=");
ptr = longlong10_to_str(opt_value, ptr, 10);
}
}
if (p->exists("row_type")) {
p->get("row_type", &opt_value);
ptr = strxmov(ptr, " row_format=", ha_row_type[(uint)opt_value], NullS);
}
if (p->exists("stats_sample_pages")) {
p->get("stats_sample_pages", &opt_value);
if (opt_value != 0) {
ptr = my_stpcpy(ptr, " stats_sample_pages=");
ptr = longlong10_to_str(opt_value, ptr, 10);
}
}
if (p->exists("stats_auto_recalc")) {
p->get("stats_auto_recalc", &opt_value);
enum_stats_auto_recalc sar = (enum_stats_auto_recalc)opt_value;
if (sar == HA_STATS_AUTO_RECALC_ON)
ptr = my_stpcpy(ptr, " stats_auto_recalc=1");
else if (sar == HA_STATS_AUTO_RECALC_OFF)
ptr = my_stpcpy(ptr, " stats_auto_recalc=0");
}
if (p->exists("key_block_size")) p->get("key_block_size", &opt_value);
if (opt_value != 0) {
ptr = my_stpcpy(ptr, " KEY_BLOCK_SIZE=");
ptr = longlong10_to_str(opt_value, ptr, 10);
}
if (p->exists("compress")) {
dd::String_type opt_value;
p->get("compress", &opt_value);
if (!opt_value.empty()) {
if (opt_value.size() > 7) opt_value.erase(7, dd::String_type::npos);
ptr = my_stpcpy(ptr, " COMPRESSION=\"");
ptr = my_stpcpy(ptr, opt_value.c_str());
ptr = my_stpcpy(ptr, "\"");
}
}
// Print ENCRYPTION clause.
dd::String_type encrypt_type;
if (p->exists("encrypt_type")) {
p->get("encrypt_type", &encrypt_type);
} else {
encrypt_type = "N";
}
// Show ENCRYPTION clause only if we have a encrypted table
// OR if schema encryption default is different from table encryption.
bool is_schema_encrypted = args[2]->val_int();
bool encryption_request_type = is_encrypted(encrypt_type);
if (encryption_request_type ||
(is_schema_encrypted != encryption_request_type)) {
ptr = my_stpcpy(ptr, " ENCRYPTION=\'");
ptr = my_stpcpy(ptr, encrypt_type.c_str());
ptr = my_stpcpy(ptr, "\'");
}
if (p->exists("stats_persistent")) {
p->get("stats_persistent", &opt_value);
if (opt_value)
ptr = my_stpcpy(ptr, " stats_persistent=1");
else
ptr = my_stpcpy(ptr, " stats_persistent=0");
}
if (p->exists("pack_keys")) {
p->get("pack_keys", &opt_value);
if (opt_value)
ptr = my_stpcpy(ptr, " pack_keys=1");
else
ptr = my_stpcpy(ptr, " pack_keys=0");
}
if (p->exists("checksum")) {
p->get("checksum", &opt_value);
if (opt_value) ptr = my_stpcpy(ptr, " checksum=1");
}
if (p->exists("delay_key_write")) {
p->get("delay_key_write", &opt_value);
if (opt_value) ptr = my_stpcpy(ptr, " delay_key_write=1");
}
bool is_partitioned = args[1]->val_int();
if (is_partitioned) ptr = my_stpcpy(ptr, " partitioned");
if (p->exists("secondary_engine")) {
dd::String_type opt_value;
p->get("secondary_engine", &opt_value);
if (!opt_value.empty()) {
ptr = my_stpcpy(ptr, " SECONDARY_ENGINE=\"");
ptr = my_stpcpy(ptr, opt_value.c_str());
ptr = my_stpcpy(ptr, "\"");
}
}
if (ptr == option_buff)
oss << "";
else
oss << option_buff + 1;
str->copy(oss.str().c_str(), oss.str().length(), system_charset_info);
return str;
}
String *Item_func_internal_get_comment_or_error::val_str(String *str) {
DBUG_TRACE;
null_value = false;
// Read arguements
String schema;
String view;
String table_type;
String options;
String *schema_ptr = args[0]->val_str(&schema);
String *view_ptr = args[1]->val_str(&view);
String *table_type_ptr = args[2]->val_str(&table_type);
String *options_ptr = args[3]->val_str(&options);
String comment;
String *comment_ptr = args[4]->val_str(&comment);
if (table_type_ptr == nullptr || schema_ptr == nullptr ||
view_ptr == nullptr || comment_ptr == nullptr) {
null_value = true;
return nullptr;
}
THD *thd = current_thd;
std::ostringstream oss("");
DBUG_EXECUTE_IF("fetch_system_view_definition", {
dd::String_type definition;
if (dd::info_schema::get_I_S_view_definition(
dd::String_type(schema_ptr->c_ptr_safe()),
dd::String_type(view_ptr->c_ptr_safe()), &definition) == false) {
str->copy(definition.c_str(), definition.length(), system_charset_info);
return str;
}
});
if (options_ptr != nullptr &&
strcmp(table_type_ptr->c_ptr_safe(), "VIEW") == 0) {
bool is_view_valid = true;
std::unique_ptr<dd::Properties> view_options(
dd::Properties::parse_properties(options_ptr->c_ptr_safe()));
// Warn if the property string is corrupt.
if (!view_options.get()) {
LogErr(WARNING_LEVEL, ER_WARN_PROPERTY_STRING_PARSE_FAILED,
options_ptr->c_ptr_safe());
DBUG_ASSERT(false);
return nullptr;
}
if (view_options->get("view_valid", &is_view_valid)) return nullptr;
if (is_view_valid == false && thd->lex->sql_command != SQLCOM_SHOW_TABLES) {
oss << push_view_warning_or_error(current_thd, schema_ptr->c_ptr_safe(),
view_ptr->c_ptr_safe());
} else
oss << "VIEW";
} else if (!thd->lex->m_IS_table_stats.error().empty()) {
/*
There could be error generated due to INTERNAL_*() UDF calls
in I_S query. If there was a error found, we show that as
part of COMMENT field.
*/
oss << thd->lex->m_IS_table_stats.error();
} else {
oss << comment_ptr->c_ptr_safe();
}
str->copy(oss.str().c_str(), oss.str().length(), system_charset_info);
return str;
}
/*
The function return 'default' in case the dd::Properties string passed as
the argument 'str' does not contain 'nodegroup_id' key stored OR even
when the option string is empty.
*/
String *Item_func_get_partition_nodegroup::val_str(String *str) {
DBUG_TRACE;
null_value = false;
String options;
String *options_ptr = args[0]->val_str(&options);
std::ostringstream oss("");
// If we have a option string.
if (options_ptr != nullptr) {
// Prepare dd::Properties
std::unique_ptr<dd::Properties> view_options(
dd::Properties::parse_properties(options_ptr->c_ptr_safe()));
// Warn if the property string is corrupt.
if (!view_options.get()) {
LogErr(WARNING_LEVEL, ER_WARN_PROPERTY_STRING_PARSE_FAILED,
options_ptr->c_ptr_safe());
DBUG_ASSERT(false);
str->copy(oss.str().c_str(), oss.str().length(), system_charset_info);
return str;
}
if (view_options->exists("nodegroup_id")) {
uint32 value;
// Fetch nodegroup id.
view_options->get("nodegroup_id", &value);
oss << value;
} else
oss << "default";
} else
oss << "default";
// Copy the value to output string.
str->copy(oss.str().c_str(), oss.str().length(), system_charset_info);
return str;
}
String *Item_func_internal_tablespace_type::val_str(String *str) {
DBUG_TRACE;
dd::String_type result;
THD *thd = current_thd;
retrieve_tablespace_statistics(thd, args, &null_value);
if (null_value == false) {
thd->lex->m_IS_tablespace_stats.get_stat(
dd::info_schema::enum_tablespace_stats_type::TS_TYPE, &result);
str->copy(result.c_str(), result.length(), system_charset_info);
return str;
}
return nullptr;
}
String *Item_func_internal_tablespace_logfile_group_name::val_str(String *str) {
DBUG_TRACE;
dd::String_type result;
THD *thd = current_thd;
retrieve_tablespace_statistics(thd, args, &null_value);
if (null_value == false) {
thd->lex->m_IS_tablespace_stats.get_stat(
dd::info_schema::enum_tablespace_stats_type::TS_LOGFILE_GROUP_NAME,
&result);
if (result.length())
str->copy(result.c_str(), result.length(), system_charset_info);
else {
null_value = true;
return nullptr;
}
return str;
}
return nullptr;
}
String *Item_func_internal_tablespace_status::val_str(String *str) {
DBUG_TRACE;
dd::String_type result;
THD *thd = current_thd;
retrieve_tablespace_statistics(thd, args, &null_value);
if (null_value == false) {
thd->lex->m_IS_tablespace_stats.get_stat(
dd::info_schema::enum_tablespace_stats_type::TS_STATUS, &result);
str->copy(result.c_str(), result.length(), system_charset_info);
return str;
}
return nullptr;
}
String *Item_func_internal_tablespace_row_format::val_str(String *str) {
DBUG_TRACE;
dd::String_type result;
THD *thd = current_thd;
retrieve_tablespace_statistics(thd, args, &null_value);
if (null_value == false) {
thd->lex->m_IS_tablespace_stats.get_stat(
dd::info_schema::enum_tablespace_stats_type::TS_ROW_FORMAT, &result);
if (result.length())
str->copy(result.c_str(), result.length(), system_charset_info);
else {
null_value = true;
return nullptr;
}
return str;
}
return nullptr;
}
String *Item_func_internal_tablespace_extra::val_str(String *str) {
DBUG_TRACE;
dd::String_type result;
THD *thd = current_thd;
retrieve_tablespace_statistics(thd, args, &null_value);
if (null_value == false) {
thd->lex->m_IS_tablespace_stats.get_stat(
dd::info_schema::enum_tablespace_stats_type::TS_EXTRA, &result);
if (result.length())
str->copy(result.c_str(), result.length(), system_charset_info);
else {
null_value = true;
return nullptr;
}
return str;
}
return nullptr;
}
/**
@brief
This function prepares string representing se_private_data for tablespace.
This is required for IS implementation which uses views on DD tablespace.
Syntax:
string get_dd_tablespace_private_data(dd.tablespace.se_private_data)
The arguments accept values from se_private_data from 'tablespace'
DD table.
*/
String *Item_func_get_dd_tablespace_private_data::val_str(String *str) {
DBUG_TRACE;
// Read tablespaces.se_private_data
String option;
String *option_ptr;
std::ostringstream oss("");
if ((option_ptr = args[0]->val_str(&option)) == nullptr) {
str->copy(oss.str().c_str(), oss.str().length(), system_charset_info);
return str;
}
// Read required values from properties
std::unique_ptr<dd::Properties> p(
dd::Properties::parse_properties(option_ptr->c_ptr_safe()));
// Warn if the property string is corrupt.
if (!p.get()) {
LogErr(WARNING_LEVEL, ER_WARN_PROPERTY_STRING_PARSE_FAILED,
option_ptr->c_ptr_safe());
DBUG_ASSERT(false);
str->copy(oss.str().c_str(), oss.str().length(), system_charset_info);
return str;
}
// Read used_flags
uint opt_value = 0;
char option_buff[350], *ptr;
ptr = option_buff;
if (strcmp(args[1]->val_str(&option)->ptr(), "id") == 0) {
if (p->exists("id")) {
p->get("id", &opt_value);
ptr = longlong10_to_str(opt_value, ptr, 10);
}
}
if (strcmp(args[1]->val_str(&option)->ptr(), "flags") == 0) {
if (p->exists("flags")) {
p->get("flags", &opt_value);
ptr = longlong10_to_str(opt_value, ptr, 10);
}
}
if (ptr == option_buff)
oss << "";
else
oss << option_buff;
str->copy(oss.str().c_str(), oss.str().length(), system_charset_info);
return str;
}
/**
@brief
This function prepares string representing se_private_data for index.
This is required for IS implementation which uses views on DD indexes.
Syntax:
string get_dd_index_private_data(dd.indexes.se_private_data)
The arguments accept values from se_private_data from 'indexes'
DD table.
*/
String *Item_func_get_dd_index_private_data::val_str(String *str) {
DBUG_TRACE;
// Read indexes.se_private_data
String option;
String *option_ptr;
std::ostringstream oss("");
if ((option_ptr = args[0]->val_str(&option)) == nullptr) {
str->copy(oss.str().c_str(), oss.str().length(), system_charset_info);
return str;
}
// Read required values from properties
std::unique_ptr<dd::Properties> p(
dd::Properties::parse_properties(option_ptr->c_ptr_safe()));
// Warn if the property string is corrupt.
if (!p.get()) {
LogErr(WARNING_LEVEL, ER_WARN_PROPERTY_STRING_PARSE_FAILED,
option_ptr->c_ptr_safe());
DBUG_ASSERT(false);
str->copy(oss.str().c_str(), oss.str().length(), system_charset_info);
return str;
}
// Read used_flags
uint opt_value = 0;
char option_buff[350], *ptr;
ptr = option_buff;
if (strcmp(args[1]->val_str(&option)->ptr(), "id") == 0) {
if (p->exists("id")) {
p->get("id", &opt_value);
ptr = longlong10_to_str(opt_value, ptr, 10);
}
}
if (strcmp(args[1]->val_str(&option)->ptr(), "root") == 0) {
if (p->exists("root")) {
p->get("root", &opt_value);
ptr = longlong10_to_str(opt_value, ptr, 10);
}
}
if (strcmp(args[1]->val_str(&option)->ptr(), "trx_id") == 0) {
if (p->exists("trx_id")) {
p->get("trx_id", &opt_value);
ptr = longlong10_to_str(opt_value, ptr, 10);
}
}
if (ptr == option_buff)
oss << "";
else
oss << option_buff;
str->copy(oss.str().c_str(), oss.str().length(), system_charset_info);
return str;
}
/**
Get collation by name, send error to client on failure.
@param name Collation name
@param name_cs Character set of the name string
@return
@retval NULL on error
@retval Pointter to CHARSET_INFO with the given name on success
*/
CHARSET_INFO *mysqld_collation_get_by_name(const char *name,
CHARSET_INFO *name_cs) {
CHARSET_INFO *cs;
MY_CHARSET_LOADER loader;
char error[1024];
my_charset_loader_init_mysys(&loader);
if (!(cs = my_collation_get_by_name(&loader, name, MYF(0)))) {
ErrConvString err(name, name_cs);
my_error(ER_UNKNOWN_COLLATION, MYF(0), err.ptr());
if (loader.errcode) {
snprintf(error, sizeof(error) - 1, EE(loader.errcode), loader.errarg);
push_warning_printf(current_thd, Sql_condition::SL_WARNING,
ER_UNKNOWN_COLLATION, "%s", error);
}
}
return cs;
}
String *Item_func_convert_cpu_id_mask::val_str(String *str) {
DBUG_TRACE;
null_value = false;
String cpu_mask;
String *cpu_mask_str = args[0]->val_str(&cpu_mask);
if (cpu_mask_str == nullptr || cpu_mask_str->length() == 0) {
null_value = true;
return nullptr;
}
std::ostringstream oss("");
cpu_mask_str->set_charset(&my_charset_bin);
int bit_start = -1, bit_end = -1;
int start_pos = cpu_mask_str->length() - 1;
bool first = true;
for (int i = start_pos; i >= 0; i--) {
if (cpu_mask_str->ptr()[i] == '1')
bit_start == -1 ? (bit_start = bit_end = start_pos - i) : bit_end++;
else if (bit_start != -1) {
if (first)
first = false;
else
oss << ",";
if (bit_start == bit_end)
oss << bit_start;
else
oss << bit_start << "-" << bit_end;
bit_start = bit_end = -1;
}
}
if (oss.str().length() == 0)
oss << "0-"
<< resourcegroups::Resource_group_mgr::instance()->num_vcpus() - 1;
str->copy(oss.str().c_str(), oss.str().length(), &my_charset_bin);
return str;
}
void Item_func_current_role::cleanup() {
if (value_cache_set) {
value_cache.set((char *)NULL, 0, system_charset_info);
value_cache_set = false;
}
}
String *Item_func_current_role::val_str(String *) {
set_current_role(current_thd);
return &value_cache;
}
void Item_func_current_role::set_current_role(THD *thd) {
if (!value_cache_set) {
func_current_role(thd, &value_cache);
value_cache_set = true;
}
}
/**
@brief Constructs and caches the graphml string
Called once per query and the result cached inside value_cache
@param thd The current session
@retval false success
@retval true failure
*/
bool Item_func_roles_graphml::calculate_graphml(THD *thd) {
Security_context *sctx = thd->security_context();
if (sctx &&
(sctx->has_global_grant(STRING_WITH_LEN("ROLE_ADMIN")).first ||
sctx->check_access(SUPER_ACL, "", false)) &&
!skip_grant_tables())
roles_graphml(thd, &value_cache);
else
value_cache.set_ascii(
STRING_WITH_LEN("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
"<graphml />"));
value_cache_set = true;
return false;
}
String *Item_func_roles_graphml::val_str(String *) {
calculate_graphml(current_thd);
return &value_cache;
}
void Item_func_roles_graphml::cleanup() {
if (value_cache_set) {
value_cache.set((char *)NULL, 0, system_charset_info);
value_cache_set = false;
}
}
/**
@brief
This function prepares string representing value stored at key
supplied.
This is required for upgrade to parse encryption key value.
Syntax:
string get_dd_property_key_value(dd.table.options, key)
*/
String *Item_func_get_dd_property_key_value::val_str(String *str) {
DBUG_TRACE;
// Read tables.options
String properties;
String key;
std::ostringstream oss("");
null_value = false;
String *properties_ptr = args[0]->val_str(&properties);
String *key_ptr = args[1]->val_str(&key);
if (key_ptr == nullptr || properties_ptr == nullptr) {
null_value = true;
return nullptr;
}
// Read required values from properties
std::unique_ptr<dd::Properties> p(
dd::Properties::parse_properties(properties_ptr->c_ptr_safe()));
// Warn if the property string is corrupt.
if (!p.get()) {
LogErr(WARNING_LEVEL, ER_WARN_PROPERTY_STRING_PARSE_FAILED,
properties_ptr->c_ptr_safe());
str->copy(oss.str().c_str(), oss.str().length(), system_charset_info);
null_value = true;
return str;
}
// Read the value at key
dd::String_type keyname(key_ptr->c_ptr_safe(), strlen(key_ptr->c_ptr_safe()));
dd::String_type value;
if (p->exists(keyname)) {
p->get(keyname, &value);
oss << value;
} else {
null_value = true;
return nullptr;
}
str->copy(oss.str().c_str(), oss.str().length(), system_charset_info);
return str;
}
/**
@brief
This function removes a key value from given property string.
This is required during upgrade to encryption key value.
Syntax:
string remove_dd_property_key(dd.table.options, key)
The function either returns the string after removing the key OR
returns the original property string if key is not found.
*/
String *Item_func_remove_dd_property_key::val_str(String *str) {
DBUG_TRACE;
// Read tables.options
String properties;
String key;
std::ostringstream oss("");
null_value = false;
String *properties_ptr = args[0]->val_str(&properties);
String *key_ptr = args[1]->val_str(&key);
if (key_ptr == nullptr || properties_ptr == nullptr) {
null_value = true;
return nullptr;
}
// Read required values from properties
std::unique_ptr<dd::Properties> p(
dd::Properties::parse_properties(properties_ptr->c_ptr_safe()));
// Warn if the property string is corrupt.
if (!p.get()) {
LogErr(WARNING_LEVEL, ER_WARN_PROPERTY_STRING_PARSE_FAILED,
properties_ptr->c_ptr_safe());
str->copy(oss.str().c_str(), oss.str().length(), system_charset_info);
return str;
}
// Read the value at key
dd::String_type keyname(key_ptr->c_ptr_safe(), strlen(key_ptr->c_ptr_safe()));
(void)p->remove(keyname);
oss << p->raw_string();
str->copy(oss.str().c_str(), oss.str().length(), system_charset_info);
return str;
}
/*
This function converts interval expression to the interval value specified
by the user at time of creating events.
@param str pointer to String whose output is filled with user supplied
interval value at time of event creation.
@return returns a pointer to the string with the converted interval value.
*/
String *Item_func_convert_interval_to_user_interval::val_str(String *str) {
if (!args[0]->is_null() && !args[1]->is_null()) {
longlong event_interval_val = args[0]->val_int();
interval_type event_interval_unit = dd::get_old_interval_type(
(dd::Event::enum_interval_field)args[1]->val_int());
str->length(0);
Events::reconstruct_interval_expression(str, event_interval_unit,
event_interval_val);
null_value = false;
return str;
}
null_value = true;
return nullptr;
}
/**
@brief
This function prepares string representing EXTRA column for I_S.COLUMNS.
@param str A String object that we can write to.
Syntax:
string internal_get_dd_column_extra(dd.table.options)
@return returns a pointer to the string containing column options.
*/
String *Item_func_internal_get_dd_column_extra::val_str(String *str) {
DBUG_TRACE;
std::ostringstream oss("");
null_value = false;
// Create UPDATE_OPTION. This can be null.
String update_option;
String *update_option_ptr = args[3]->val_str(&update_option);
// Create COLUMNS.OPTIONS. This can not be null.
String properties;
String *properties_ptr = args[5]->val_str(&properties);
// Stop if any of required argument is not supplied.
if (args[0]->is_null() || args[1]->is_null() || args[2]->is_null() ||
args[4]->is_null()) {
null_value = true;
return nullptr;
}
bool is_not_generated_column = args[0]->val_int();
bool is_virtual = args[1]->val_int();
bool is_auto_increment = args[2]->val_int();
bool has_update_option = update_option_ptr != nullptr;
bool is_default_option = args[4]->val_int();
if (is_not_generated_column) {
if (is_default_option) oss << "DEFAULT_GENERATED";
if (has_update_option) {
if (oss.str().length()) oss << " ";
oss << "on update " << update_option_ptr->c_ptr_safe();
}
if (is_auto_increment) {
if (oss.str().length()) oss << " ";
oss << "auto_increment";
}
} else {
oss << (is_virtual ? "VIRTUAL GENERATED" : "STORED GENERATED");
}
// Print the column property 'NOT SECONDARY'.
if (properties_ptr != nullptr) {
// Read required values from properties
std::unique_ptr<dd::Properties> p(
dd::Properties::parse_properties(properties_ptr->c_ptr_safe()));
// Warn if the property string is corrupt.
if (!p.get()) {
LogErr(WARNING_LEVEL, ER_WARN_PROPERTY_STRING_PARSE_FAILED,
properties_ptr->c_ptr_safe());
str->copy(oss.str().c_str(), oss.str().length(), system_charset_info);
return str;
}
if (p->exists("not_secondary")) {
if (oss.str().length()) oss << " ";
oss << "NOT SECONDARY";
}
}
str->copy(oss.str().c_str(), oss.str().length(), system_charset_info);
return str;
}