mirror of
				https://github.com/MariaDB/server.git
				synced 2025-11-03 20:36:16 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			6260 lines
		
	
	
	
		
			165 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			6260 lines
		
	
	
	
		
			165 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/*
 | 
						|
   Copyright (c) 2000, 2017, Oracle and/or its affiliates.
 | 
						|
   Copyright (c) 2009, 2022, MariaDB Corporation.
 | 
						|
 | 
						|
   This program is free software; you can redistribute it and/or modify
 | 
						|
   it under the terms of the GNU General Public License as published by
 | 
						|
   the Free Software Foundation; version 2 of the License.
 | 
						|
 | 
						|
   This program is distributed in the hope that it will be useful,
 | 
						|
   but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
						|
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
						|
   GNU General Public License for more details.
 | 
						|
 | 
						|
   You should have received a copy of the GNU General Public License
 | 
						|
   along with this program; if not, write to the Free Software
 | 
						|
   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335  USA
 | 
						|
*/
 | 
						|
 | 
						|
/**
 | 
						|
  @file
 | 
						|
 | 
						|
  @brief
 | 
						|
  This file defines all string functions
 | 
						|
 | 
						|
  @warning
 | 
						|
    Some string functions don't always put and end-null on a String.
 | 
						|
    (This shouldn't be needed)
 | 
						|
*/
 | 
						|
 | 
						|
#ifdef USE_PRAGMA_IMPLEMENTATION
 | 
						|
#pragma implementation				// gcc: Class implementation
 | 
						|
#endif
 | 
						|
 | 
						|
#include "mariadb.h"                          // HAVE_*
 | 
						|
 | 
						|
#include "sql_priv.h"
 | 
						|
/*
 | 
						|
  It is necessary to include set_var.h instead of item.h because there
 | 
						|
  are dependencies on include order for set_var.h and item.h. This
 | 
						|
  will be resolved later.
 | 
						|
*/
 | 
						|
#include "sql_class.h"                          // set_var.h: THD
 | 
						|
#include "set_var.h"
 | 
						|
#include "sql_base.h"
 | 
						|
#include "sql_time.h"
 | 
						|
#include "des_key_file.h"       // st_des_keyschedule, st_des_keyblock
 | 
						|
#include "password.h"           // my_make_scrambled_password,
 | 
						|
                                // my_make_scrambled_password_323
 | 
						|
#include <m_ctype.h>
 | 
						|
#include <my_md5.h>
 | 
						|
C_MODE_START
 | 
						|
#include "../mysys/my_static.h"			// For soundex_map
 | 
						|
C_MODE_END
 | 
						|
#include "sql_show.h"                           // append_identifier
 | 
						|
#include <sql_repl.h>
 | 
						|
#include "sql_statistics.h"
 | 
						|
#include "strfunc.h"
 | 
						|
 | 
						|
#ifdef HAVE_hkdf
 | 
						|
#include <openssl/kdf.h>
 | 
						|
#endif
 | 
						|
 | 
						|
/* fmtlib include (https://fmt.dev/). */
 | 
						|
#define FMT_HEADER_ONLY 1
 | 
						|
#include "fmt/args.h"
 | 
						|
 | 
						|
size_t username_char_length= USERNAME_CHAR_LENGTH;
 | 
						|
 | 
						|
/*
 | 
						|
  Calculate max length of string from length argument to LEFT and RIGHT
 | 
						|
*/
 | 
						|
 | 
						|
static uint32 max_length_for_string(Item *item, bool *neg)
 | 
						|
{
 | 
						|
  *neg= false;
 | 
						|
  ulonglong length= item->val_int();
 | 
						|
  if (item->null_value)
 | 
						|
    return 0;
 | 
						|
  if (length > (ulonglong) LONGLONG_MAX && !item->unsigned_flag)
 | 
						|
  {
 | 
						|
    *neg= true;
 | 
						|
    return 0; // Negative
 | 
						|
  }
 | 
						|
  if (length > (ulonglong) INT_MAX32)
 | 
						|
  {
 | 
						|
    /* Limit string length to maxium string length in MariaDB (2G) */
 | 
						|
    length= (ulonglong) INT_MAX32;
 | 
						|
  }
 | 
						|
  return (uint32) length;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
  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.
 | 
						|
 | 
						|
  No matter if conversion is needed or not needed,
 | 
						|
  the result is always returned in "str" (see MDEV-10306 why).
 | 
						|
 | 
						|
  @param [OUT] str          - Store the result here
 | 
						|
  @param [IN]  ascii_buffer - Use this temporary buffer to call val_str_ascii()
 | 
						|
*/
 | 
						|
String *Item_func::val_str_from_val_str_ascii(String *str, String *ascii_buffer)
 | 
						|
{
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
 | 
						|
  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 != ascii_buffer);
 | 
						|
  
 | 
						|
  uint errors;
 | 
						|
  String *res= val_str_ascii(ascii_buffer);
 | 
						|
  if (!res)
 | 
						|
    return 0;
 | 
						|
  
 | 
						|
  if ((null_value= str->copy(res->ptr(), res->length(),
 | 
						|
                             &my_charset_latin1, collation.collation,
 | 
						|
                             &errors)))
 | 
						|
    return 0;
 | 
						|
  
 | 
						|
  return str;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
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.
 | 
						|
  */
 | 
						|
  if (thd->is_strict_mode())
 | 
						|
    set_maybe_null();
 | 
						|
  return res;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
my_decimal *Item_str_func::val_decimal(my_decimal *decimal_value)
 | 
						|
{
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
  StringBuffer<64> tmp;
 | 
						|
  String *res= val_str(&tmp);
 | 
						|
  return res ? decimal_from_string_with_check(decimal_value, res) : 0;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
double Item_str_func::val_real()
 | 
						|
{
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
  StringBuffer<64> tmp;
 | 
						|
  String *res= val_str(&tmp);
 | 
						|
  return res ? double_from_string_with_check(res) : 0.0;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
longlong Item_str_func::val_int()
 | 
						|
{
 | 
						|
  DBUG_ASSERT(!is_cond());
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
  StringBuffer<22> tmp;
 | 
						|
  String *res= val_str(&tmp);
 | 
						|
  return res ? longlong_from_string_with_check(res) : 0;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
String *Item_func_md5::val_str_ascii(String *str)
 | 
						|
{
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
  String * sptr= args[0]->val_str(str);
 | 
						|
  if (sptr)
 | 
						|
  {
 | 
						|
    uchar digest[16];
 | 
						|
 | 
						|
    null_value=0;
 | 
						|
    compute_md5_hash(digest, (const char *) sptr->ptr(), sptr->length());
 | 
						|
    if (str->alloc(32))				// Ensure that memory is free
 | 
						|
    {
 | 
						|
      null_value=1;
 | 
						|
      return 0;
 | 
						|
    }
 | 
						|
    array_to_hex((char *) str->ptr(), digest, 16);
 | 
						|
    str->set_charset(&my_charset_numeric);
 | 
						|
    str->length((uint) 32);
 | 
						|
    return str;
 | 
						|
  }
 | 
						|
  null_value=1;
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
String *Item_func_sha::val_str_ascii(String *str)
 | 
						|
{
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
  String * sptr= args[0]->val_str(str);
 | 
						|
  if (sptr)  /* If we got value different from NULL */
 | 
						|
  {
 | 
						|
    /* Temporary buffer to store 160bit digest */
 | 
						|
    uint8 digest[MY_SHA1_HASH_SIZE];
 | 
						|
    my_sha1(digest, (const char *) sptr->ptr(), sptr->length());
 | 
						|
    /* Ensure that memory is free and we got result */
 | 
						|
    if (!str->alloc(MY_SHA1_HASH_SIZE*2))
 | 
						|
    {
 | 
						|
      array_to_hex((char *) str->ptr(), digest, MY_SHA1_HASH_SIZE);
 | 
						|
      str->set_charset(&my_charset_numeric);
 | 
						|
      str->length((uint)  MY_SHA1_HASH_SIZE*2);
 | 
						|
      null_value=0;
 | 
						|
      return str;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  null_value=1;
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
bool Item_func_sha::fix_length_and_dec(THD *thd)
 | 
						|
{
 | 
						|
  // size of hex representation of hash
 | 
						|
  fix_length_and_charset(MY_SHA1_HASH_SIZE * 2, default_charset());
 | 
						|
  return FALSE;
 | 
						|
}
 | 
						|
 | 
						|
String *Item_func_sha2::val_str_ascii(String *str)
 | 
						|
{
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
  unsigned char digest_buf[512/8]; // enough for SHA512
 | 
						|
  String *input_string;
 | 
						|
  const char *input_ptr;
 | 
						|
  size_t input_len;
 | 
						|
 | 
						|
  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 (String *) NULL;
 | 
						|
 | 
						|
  input_ptr= input_string->ptr();
 | 
						|
  input_len= input_string->length();
 | 
						|
 | 
						|
  longlong digest_length= args[1]->val_int();
 | 
						|
  switch (digest_length) {
 | 
						|
  case 512:
 | 
						|
    my_sha512(digest_buf, input_ptr, input_len);
 | 
						|
    break;
 | 
						|
  case 384:
 | 
						|
    my_sha384(digest_buf, input_ptr, input_len);
 | 
						|
    break;
 | 
						|
  case 224:
 | 
						|
    my_sha224(digest_buf, input_ptr, input_len);
 | 
						|
    break;
 | 
						|
  case 0: // SHA-256 is the default
 | 
						|
    digest_length= 256;
 | 
						|
    /* fall through */
 | 
						|
  case 256:
 | 
						|
    my_sha256(digest_buf, input_ptr, input_len);
 | 
						|
    break;
 | 
						|
  default:
 | 
						|
    if (!args[1]->const_item())
 | 
						|
    {
 | 
						|
      THD *thd= current_thd;
 | 
						|
      push_warning_printf(thd,
 | 
						|
                          Sql_condition::WARN_LEVEL_WARN,
 | 
						|
                          ER_WRONG_PARAMETERS_TO_NATIVE_FCT,
 | 
						|
                          ER_THD(thd, ER_WRONG_PARAMETERS_TO_NATIVE_FCT),
 | 
						|
                          "sha2");
 | 
						|
    }
 | 
						|
    null_value= TRUE;
 | 
						|
    return NULL;
 | 
						|
  }
 | 
						|
  digest_length/= 8; /* bits to bytes */
 | 
						|
 | 
						|
  /* 
 | 
						|
    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->alloc((uint) digest_length*2 + 1); /* Each byte as two nybbles */
 | 
						|
 | 
						|
  /* Convert the large number to a string-hex representation. */
 | 
						|
  array_to_hex((char *) str->ptr(), digest_buf, (uint)digest_length);
 | 
						|
 | 
						|
  /* We poked raw bytes in.  We must inform the the String of its length. */
 | 
						|
  str->length((uint) digest_length*2); /* Each byte as two nybbles */
 | 
						|
 | 
						|
  null_value= FALSE;
 | 
						|
  return str;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool Item_func_sha2::fix_length_and_dec(THD *thd)
 | 
						|
{
 | 
						|
  set_maybe_null();
 | 
						|
  max_length = 0;
 | 
						|
 | 
						|
  int sha_variant= (int)(args[1]->const_item() ? args[1]->val_int() : 512);
 | 
						|
 | 
						|
  switch (sha_variant) {
 | 
						|
  case 0: // SHA-256 is the default
 | 
						|
    sha_variant= 256;
 | 
						|
    /* fall through */
 | 
						|
  case 512:
 | 
						|
  case 384:
 | 
						|
  case 256:
 | 
						|
  case 224:
 | 
						|
    fix_length_and_charset(sha_variant/8 * 2, default_charset());
 | 
						|
    break;
 | 
						|
  default:
 | 
						|
    THD *thd= current_thd;
 | 
						|
    push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
 | 
						|
                        ER_WRONG_PARAMETERS_TO_NATIVE_FCT,
 | 
						|
                        ER_THD(thd, ER_WRONG_PARAMETERS_TO_NATIVE_FCT), "sha2");
 | 
						|
  }
 | 
						|
  return FALSE;
 | 
						|
}
 | 
						|
 | 
						|
const char *block_encryption_mode_values[]= {
 | 
						|
  "aes-128-ecb", "aes-192-ecb", "aes-256-ecb",
 | 
						|
  "aes-128-cbc", "aes-192-cbc", "aes-256-cbc",
 | 
						|
  "aes-128-ctr", "aes-192-ctr", "aes-256-ctr",
 | 
						|
  NullS };
 | 
						|
TYPELIB block_encryption_mode_typelib= {9, 0, block_encryption_mode_values, 0};
 | 
						|
static inline uint block_encryption_mode_to_key_length(ulong bem)
 | 
						|
{ return (bem % 3 + 2) * 64; }
 | 
						|
static inline my_aes_mode block_encryption_mode_to_aes_mode(ulong bem)
 | 
						|
{ return (my_aes_mode)(bem / 3); }
 | 
						|
 | 
						|
/* Implementation of AES encryption routines */
 | 
						|
int Item_aes_crypt::parse_mode()
 | 
						|
{
 | 
						|
  StringBuffer<80> buf;
 | 
						|
  String *ptr= args[3]->val_str_ascii(&buf);
 | 
						|
  ulong bem;
 | 
						|
 | 
						|
  if (ptr == NULL)
 | 
						|
    goto err;
 | 
						|
 | 
						|
  bem= find_type(&block_encryption_mode_typelib, ptr->ptr(), ptr->length(), 0);
 | 
						|
  if (!bem)
 | 
						|
    goto err;
 | 
						|
 | 
						|
  aes_key_length= block_encryption_mode_to_key_length(bem - 1);
 | 
						|
  aes_mode= block_encryption_mode_to_aes_mode(bem - 1);
 | 
						|
  return 0;
 | 
						|
err:
 | 
						|
  return 1;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void Item_aes_crypt::create_key(String *user_key, uchar *real_key)
 | 
						|
{
 | 
						|
  uchar *real_key_end= real_key + aes_key_length / 8;
 | 
						|
  uchar *ptr;
 | 
						|
  const char *sptr= user_key->ptr();
 | 
						|
  const char *key_end= sptr + user_key->length();
 | 
						|
 | 
						|
  bzero(real_key, aes_key_length / 8);
 | 
						|
 | 
						|
  for (ptr= real_key; sptr < key_end; ptr++, sptr++)
 | 
						|
  {
 | 
						|
    if (ptr == real_key_end)
 | 
						|
      ptr= real_key;
 | 
						|
    *ptr ^= (uchar) *sptr;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
String *Item_aes_crypt::val_str(String *str2)
 | 
						|
{
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
  StringBuffer<80> user_key_buf, iv_buf;
 | 
						|
  String *sptr= args[0]->val_str(&tmp_value);
 | 
						|
  String *user_key= args[1]->val_str(&user_key_buf);
 | 
						|
  String *iv= NULL;
 | 
						|
  uint32 aes_length;
 | 
						|
 | 
						|
  if (sptr && user_key) // we need both arguments to be not NULL
 | 
						|
  {
 | 
						|
    if (arg_count > 3 && (null_value= parse_mode()))
 | 
						|
      return 0;
 | 
						|
 | 
						|
    if (aes_mode != MY_AES_ECB)
 | 
						|
    {
 | 
						|
      if (arg_count > 2)
 | 
						|
        iv= args[2]->val_str(&iv_buf);
 | 
						|
      if ((null_value= (!iv || iv->length() < MY_AES_BLOCK_SIZE)))
 | 
						|
        return 0;
 | 
						|
    }
 | 
						|
 | 
						|
    aes_length=my_aes_get_size(aes_mode, sptr->length());
 | 
						|
 | 
						|
    if (!str2->alloc(aes_length))		// Ensure that memory is free
 | 
						|
    {
 | 
						|
      uchar *rkey= (uchar*)alloca(aes_key_length / 8);
 | 
						|
      create_key(user_key, rkey);
 | 
						|
 | 
						|
      if (!my_aes_crypt(aes_mode, what, (uchar*)sptr->ptr(), sptr->length(),
 | 
						|
                 (uchar*)str2->ptr(), &aes_length,
 | 
						|
                 rkey, aes_key_length / 8,
 | 
						|
                 iv ? (uchar*)iv->ptr() : 0, iv ? iv->length() : 0))
 | 
						|
      {
 | 
						|
        str2->length((uint) aes_length);
 | 
						|
        DBUG_ASSERT(collation.collation == &my_charset_bin);
 | 
						|
        str2->set_charset(&my_charset_bin);
 | 
						|
        null_value= 0;
 | 
						|
        return str2;
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
  null_value=1;
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
bool Item_aes_crypt::fix_fields(THD *thd, Item **ref)
 | 
						|
{
 | 
						|
  aes_key_length= block_encryption_mode_to_key_length(thd->variables.block_encryption_mode);
 | 
						|
  aes_mode= block_encryption_mode_to_aes_mode(thd->variables.block_encryption_mode);
 | 
						|
  return  Item_str_binary_checksum_func::fix_fields(thd, ref);
 | 
						|
}
 | 
						|
 | 
						|
bool Item_func_aes_encrypt::fix_length_and_dec(THD *thd)
 | 
						|
{
 | 
						|
  max_length=my_aes_get_size(MY_AES_ECB, args[0]->max_length);
 | 
						|
  return FALSE;
 | 
						|
}
 | 
						|
 | 
						|
bool Item_func_aes_decrypt::fix_length_and_dec(THD *thd)
 | 
						|
{
 | 
						|
  max_length=args[0]->max_length;
 | 
						|
  set_maybe_null();
 | 
						|
  return FALSE;
 | 
						|
}
 | 
						|
 | 
						|
bool Item_func_kdf::fix_length_and_dec(THD *thd)
 | 
						|
{
 | 
						|
  if (arg_count > 4 && args[4]->const_item())
 | 
						|
  {
 | 
						|
    if (((key_length= (uint)args[4]->val_int()) % 8) || key_length > 65536)
 | 
						|
      key_length= 0;
 | 
						|
  }
 | 
						|
  else if (arg_count <= 4)
 | 
						|
    key_length= block_encryption_mode_to_key_length(thd->variables.block_encryption_mode);
 | 
						|
  else
 | 
						|
    key_length= 0;
 | 
						|
  key_length/= 8;
 | 
						|
  max_length= key_length ? key_length : 256/8;
 | 
						|
  set_maybe_null();
 | 
						|
  return FALSE;
 | 
						|
}
 | 
						|
 | 
						|
static void invalid_argument_error(const char *func, const char *val)
 | 
						|
{
 | 
						|
  THD *thd= current_thd;
 | 
						|
  push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
 | 
						|
    ER_STD_INVALID_ARGUMENT, ER_THD(thd, ER_STD_INVALID_ARGUMENT),
 | 
						|
    val, func);
 | 
						|
}
 | 
						|
 | 
						|
String *Item_func_kdf::val_str(String *buf)
 | 
						|
{
 | 
						|
  // KDF(key_str, salt [, {info | iterations} [, kdf_name [, width ]]])
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
  StringBuffer<80> key_buf, salt_buf;
 | 
						|
  String *key= args[0]->val_str(&key_buf);
 | 
						|
  String *salt= args[1]->val_str(&salt_buf);
 | 
						|
  bool use_hkdf= false;
 | 
						|
  size_t klen= key_length;
 | 
						|
  bool ok= false;
 | 
						|
 | 
						|
  if (!key || !salt)
 | 
						|
    goto ret_null;
 | 
						|
 | 
						|
  if (arg_count > 3)
 | 
						|
  {
 | 
						|
    if (String *s= args[3]->val_str(buf))
 | 
						|
    {
 | 
						|
      if (strcasecmp(s->c_ptr(), "hkdf") == 0)
 | 
						|
        use_hkdf= true;
 | 
						|
      else if (strcasecmp(s->c_ptr(), "pbkdf2_hmac") != 0)
 | 
						|
      {
 | 
						|
        invalid_argument_error(func_name(), ErrConvStringQ(s).ptr());
 | 
						|
        goto ret_null;
 | 
						|
      }
 | 
						|
    }
 | 
						|
    else
 | 
						|
      goto ret_null;
 | 
						|
  }
 | 
						|
  if (!klen)
 | 
						|
  {
 | 
						|
    klen= args[4]->val_int();
 | 
						|
    if (!klen || klen % 8 || klen > 65536)
 | 
						|
    {
 | 
						|
      if (!args[4]->null_value)
 | 
						|
        invalid_argument_error(func_name(),
 | 
						|
          ErrConvInteger({(ssize_t)klen, args[4]->unsigned_flag}).ptr());
 | 
						|
      goto ret_null;
 | 
						|
    }
 | 
						|
    klen/= 8;
 | 
						|
  }
 | 
						|
  buf->reserve(klen);
 | 
						|
  buf->length(klen);
 | 
						|
 | 
						|
  if (use_hkdf)
 | 
						|
  {
 | 
						|
#ifdef HAVE_hkdf
 | 
						|
    StringBuffer<80> info_buf;
 | 
						|
    String *info= arg_count > 2 ? args[2]->val_str(&info_buf) : 0;
 | 
						|
 | 
						|
    EVP_PKEY_CTX *ctx= EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, NULL);
 | 
						|
    ok= EVP_PKEY_derive_init(ctx) > 0 &&
 | 
						|
        EVP_PKEY_CTX_set_hkdf_md(ctx, EVP_sha512()) > 0 &&
 | 
						|
        EVP_PKEY_CTX_set1_hkdf_key(ctx, (const uchar*)key->ptr(), key->length()) > 0 &&
 | 
						|
        EVP_PKEY_CTX_set1_hkdf_salt(ctx, (const uchar*)salt->ptr(), salt->length()) > 0 &&
 | 
						|
        (!info || EVP_PKEY_CTX_add1_hkdf_info(ctx, (const uchar*)info->ptr(), info->length()) > 0) &&
 | 
						|
        EVP_PKEY_derive(ctx, (uchar*)buf->ptr(), &klen) > 0;
 | 
						|
 | 
						|
    EVP_PKEY_CTX_free(ctx);
 | 
						|
#else
 | 
						|
    THD *thd= current_thd;
 | 
						|
    push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
 | 
						|
      ER_NOT_SUPPORTED_YET, ER_THD(thd, ER_NOT_SUPPORTED_YET),
 | 
						|
      "kdf(..., 'hkdf')");
 | 
						|
#endif
 | 
						|
  }
 | 
						|
  else
 | 
						|
  {
 | 
						|
    longlong iter= arg_count > 2 ? args[2]->val_int() : 1000;
 | 
						|
    if (iter <= 0)
 | 
						|
    {
 | 
						|
      if (!args[2]->null_value)
 | 
						|
        invalid_argument_error(func_name(),
 | 
						|
          ErrConvInteger({iter, args[2]->unsigned_flag}).ptr());
 | 
						|
    }
 | 
						|
    else
 | 
						|
      ok= PKCS5_PBKDF2_HMAC(key->ptr(), key->length(),
 | 
						|
                      (const uchar*)salt->ptr(), salt->length(), (int)iter,
 | 
						|
                      EVP_sha512(), (int)klen, (uchar*)buf->ptr());
 | 
						|
  }
 | 
						|
 | 
						|
  if (ok)
 | 
						|
  {
 | 
						|
    null_value= 0;
 | 
						|
    return buf;
 | 
						|
  }
 | 
						|
 | 
						|
ret_null:
 | 
						|
  null_value=1;
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
bool Item_func_to_base64::fix_length_and_dec(THD *thd)
 | 
						|
{
 | 
						|
  base_flags|= args[0]->base_flags & item_base_t::MAYBE_NULL;
 | 
						|
  collation.set(default_charset(), DERIVATION_COERCIBLE, MY_REPERTOIRE_ASCII);
 | 
						|
  if (args[0]->max_length > (uint) my_base64_encode_max_arg_length())
 | 
						|
  {
 | 
						|
    set_maybe_null();
 | 
						|
    fix_char_length_ulonglong((ulonglong) my_base64_encode_max_arg_length());
 | 
						|
  }
 | 
						|
  else
 | 
						|
  {
 | 
						|
    int length= my_base64_needed_encoded_length((int) args[0]->max_length);
 | 
						|
    DBUG_ASSERT(length > 0);
 | 
						|
    fix_char_length_ulonglong((ulonglong) length - 1);
 | 
						|
  }
 | 
						|
  return FALSE;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
String *Item_func_to_base64::val_str_ascii(String *str)
 | 
						|
{
 | 
						|
  String *res= args[0]->val_str(&tmp_value);
 | 
						|
  bool too_long= false;
 | 
						|
  int length;
 | 
						|
  if (!res ||
 | 
						|
      res->length() > (uint) my_base64_encode_max_arg_length() ||
 | 
						|
      (too_long=
 | 
						|
       ((uint) (length= my_base64_needed_encoded_length((int) res->length())) >
 | 
						|
        current_thd->variables.max_allowed_packet)) ||
 | 
						|
      str->alloc((uint) length))
 | 
						|
  {
 | 
						|
    null_value= 1; // NULL input, too long input, or OOM.
 | 
						|
    if (too_long)
 | 
						|
    {
 | 
						|
      THD *thd= current_thd;
 | 
						|
      push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
 | 
						|
                          ER_WARN_ALLOWED_PACKET_OVERFLOWED,
 | 
						|
                          ER_THD(thd, ER_WARN_ALLOWED_PACKET_OVERFLOWED),
 | 
						|
                          func_name(),
 | 
						|
                          thd->variables.max_allowed_packet);
 | 
						|
    }
 | 
						|
    return 0;
 | 
						|
  }
 | 
						|
  my_base64_encode(res->ptr(), (int) res->length(), (char*) str->ptr());
 | 
						|
  DBUG_ASSERT(length > 0);
 | 
						|
  str->length((uint) length - 1); // Without trailing '\0'
 | 
						|
  null_value= 0;
 | 
						|
  return str;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool Item_func_from_base64::fix_length_and_dec(THD *thd)
 | 
						|
{
 | 
						|
  if (args[0]->max_length > (uint) my_base64_decode_max_arg_length())
 | 
						|
  {
 | 
						|
    fix_char_length_ulonglong((ulonglong) my_base64_decode_max_arg_length());
 | 
						|
  }
 | 
						|
  else
 | 
						|
  {
 | 
						|
    int length= my_base64_needed_decoded_length((int) args[0]->max_length);
 | 
						|
    fix_char_length_ulonglong((ulonglong) length);
 | 
						|
  }
 | 
						|
  // Can be NULL, e.g. in case of badly formed input string
 | 
						|
  set_maybe_null();
 | 
						|
  return FALSE;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
String *Item_func_from_base64::val_str(String *str)
 | 
						|
{
 | 
						|
  String *res= args[0]->val_str_ascii(&tmp_value);
 | 
						|
  int length;
 | 
						|
  const char *end_ptr;
 | 
						|
 | 
						|
  if (!res)
 | 
						|
    goto err;
 | 
						|
 | 
						|
  if (res->length() > (uint) my_base64_decode_max_arg_length() ||
 | 
						|
      ((uint) (length= my_base64_needed_decoded_length((int) res->length())) >
 | 
						|
       current_thd->variables.max_allowed_packet))
 | 
						|
  {
 | 
						|
    THD *thd= current_thd;
 | 
						|
    push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
 | 
						|
                        ER_WARN_ALLOWED_PACKET_OVERFLOWED,
 | 
						|
                        ER_THD(thd, ER_WARN_ALLOWED_PACKET_OVERFLOWED),
 | 
						|
                        func_name(),
 | 
						|
                        thd->variables.max_allowed_packet);
 | 
						|
    goto err;
 | 
						|
  }
 | 
						|
 | 
						|
  if (str->alloc((uint) length))
 | 
						|
    goto err;
 | 
						|
 | 
						|
  if ((length= my_base64_decode(res->ptr(), (int) res->length(),
 | 
						|
                                (char *) str->ptr(), &end_ptr, 0)) < 0 ||
 | 
						|
      end_ptr < res->ptr() + res->length())
 | 
						|
  {
 | 
						|
    THD *thd= current_thd;
 | 
						|
    push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
 | 
						|
                        ER_BAD_BASE64_DATA, ER_THD(thd, ER_BAD_BASE64_DATA),
 | 
						|
                        (int) (end_ptr - res->ptr()));
 | 
						|
    goto err;
 | 
						|
  }
 | 
						|
 | 
						|
  str->length((uint) length);
 | 
						|
  null_value= 0;
 | 
						|
  return str;
 | 
						|
err:
 | 
						|
  null_value= 1; // NULL input, too long input, OOM, or badly formed input
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
///////////////////////////////////////////////////////////////////////////////
 | 
						|
 | 
						|
 | 
						|
const char *histogram_types[] =
 | 
						|
    {"SINGLE_PREC_HB", "DOUBLE_PREC_HB", "JSON_HB", 0};
 | 
						|
static TYPELIB histogram_types_typelib=
 | 
						|
  { array_elements(histogram_types),
 | 
						|
    "histogram_types",
 | 
						|
    histogram_types, NULL};
 | 
						|
const char *representation_by_type[]= {"%.3f", "%.5f"};
 | 
						|
 | 
						|
String *Item_func_decode_histogram::val_str(String *str)
 | 
						|
{
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
  char buff[STRING_BUFFER_USUAL_SIZE];
 | 
						|
  String *res, tmp(buff, sizeof(buff), &my_charset_bin);
 | 
						|
  int type;
 | 
						|
 | 
						|
  tmp.length(0);
 | 
						|
  if (!(res= args[0]->val_str(&tmp)) ||
 | 
						|
      (type= find_type(res->c_ptr_safe(),
 | 
						|
                       &histogram_types_typelib, MYF(0))) <= 0)
 | 
						|
  {
 | 
						|
    null_value= 1;
 | 
						|
    return 0;
 | 
						|
  }
 | 
						|
  type--;
 | 
						|
 | 
						|
  tmp.length(0);
 | 
						|
  if (!(res= args[1]->val_str(&tmp)))
 | 
						|
  {
 | 
						|
    null_value= 1;
 | 
						|
    return 0;
 | 
						|
  }
 | 
						|
 | 
						|
  if (type == JSON_HB)
 | 
						|
  {
 | 
						|
    // It's a JSON histogram. Return it as-is.
 | 
						|
    null_value= 0;
 | 
						|
    return res;
 | 
						|
  }
 | 
						|
 | 
						|
  if (type == DOUBLE_PREC_HB && res->length() % 2 != 0)
 | 
						|
    res->length(res->length() - 1); // one byte is unused
 | 
						|
 | 
						|
  double prev= 0.0;
 | 
						|
  uint i;
 | 
						|
  str->length(0);
 | 
						|
  char numbuf[32];
 | 
						|
  const uchar *p= (uchar*)res->c_ptr_safe();
 | 
						|
  for (i= 0; i < res->length(); i++)
 | 
						|
  {
 | 
						|
    double val;
 | 
						|
    switch (type)
 | 
						|
    {
 | 
						|
    case SINGLE_PREC_HB:
 | 
						|
      val= p[i] / ((double)((1 << 8) - 1));
 | 
						|
      break;
 | 
						|
    case DOUBLE_PREC_HB:
 | 
						|
      val= uint2korr(p + i) / ((double)((1 << 16) - 1));
 | 
						|
      i++;
 | 
						|
      break;
 | 
						|
    default:
 | 
						|
      val= 0;
 | 
						|
      DBUG_ASSERT(0);
 | 
						|
    }
 | 
						|
    /* show delta with previous value */
 | 
						|
    size_t size= my_snprintf(numbuf, sizeof(numbuf),
 | 
						|
                          representation_by_type[type], val - prev);
 | 
						|
    str->append(numbuf, size);
 | 
						|
    str->append(',');
 | 
						|
    prev= val;
 | 
						|
  }
 | 
						|
  /* show delta with max */
 | 
						|
  size_t size= my_snprintf(numbuf, sizeof(numbuf),
 | 
						|
                        representation_by_type[type], 1.0 - prev);
 | 
						|
  str->append(numbuf, size);
 | 
						|
 | 
						|
  null_value=0;
 | 
						|
  return str;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
///////////////////////////////////////////////////////////////////////////////
 | 
						|
 | 
						|
/*
 | 
						|
  Realloc the result buffer.
 | 
						|
  NOTE: We should be prudent in the initial allocation unit -- the
 | 
						|
  size of the arguments is a function of data distribution, which
 | 
						|
  can be any. Instead of overcommitting at the first row, we grow
 | 
						|
  the allocated amount by the factor of 2. This ensures that no
 | 
						|
  more than 25% of memory will be overcommitted on average.
 | 
						|
 | 
						|
  @param IN/OUT str    - the result string
 | 
						|
  @param IN     length - new total space required in "str"
 | 
						|
  @retval       false  - on success
 | 
						|
  @retval       true   - on error
 | 
						|
*/
 | 
						|
 | 
						|
bool Item_func_concat::realloc_result(String *str, uint length) const
 | 
						|
{
 | 
						|
  if (str->alloced_length() >= length)
 | 
						|
    return false; // Alloced space is big enough, nothing to do.
 | 
						|
 | 
						|
  if (str->alloced_length() == 0)
 | 
						|
    return str->alloc(length);
 | 
						|
 | 
						|
  /*
 | 
						|
    Item_func_concat::val_str() makes sure the result length does not grow
 | 
						|
    higher than max_allowed_packet. So "length" is limited to 1G here.
 | 
						|
    We can't say anything about the current value of str->alloced_length(),
 | 
						|
    as str was initially set by args[0]->val_str(str).
 | 
						|
    So multiplication by 2 can overflow, if args[0] for some reasons
 | 
						|
    did not limit the result to max_alloced_packet. But it's not harmful,
 | 
						|
    "str" will be reallocated exactly to "length" bytes in case of overflow.
 | 
						|
  */
 | 
						|
  uint new_length= MY_MAX(str->alloced_length() * 2, length);
 | 
						|
  return str->realloc(new_length);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  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());
 | 
						|
  THD *thd= current_thd;
 | 
						|
  String *res;
 | 
						|
 | 
						|
  null_value=0;
 | 
						|
  if (!(res= args[0]->val_str(str)))
 | 
						|
    goto null;
 | 
						|
 | 
						|
  if (res != str)
 | 
						|
    str->copy_or_move(res->ptr(), res->length(), res->charset());
 | 
						|
 | 
						|
  for (uint i= 1 ; i < arg_count ; i++)
 | 
						|
  {
 | 
						|
    if (!(res= args[i]->val_str(&tmp_value)) ||
 | 
						|
        append_value(thd, str, res))
 | 
						|
      goto null;
 | 
						|
  }
 | 
						|
 | 
						|
  str->set_charset(collation.collation);
 | 
						|
  return str;
 | 
						|
 | 
						|
null:
 | 
						|
  null_value= true;
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
String *Item_func_concat_operator_oracle::val_str(String *str)
 | 
						|
{
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
  THD *thd= current_thd;
 | 
						|
  String *res= NULL;
 | 
						|
  uint i;
 | 
						|
 | 
						|
  null_value=0;
 | 
						|
  // Search first non null argument
 | 
						|
  for (i= 0; i < arg_count; i++)
 | 
						|
  {
 | 
						|
    if ((res= args[i]->val_str(str)))
 | 
						|
      break;
 | 
						|
  }
 | 
						|
  if (!res)
 | 
						|
    goto null;
 | 
						|
 | 
						|
  if (res != str)
 | 
						|
    str->copy_or_move(res->ptr(), res->length(), res->charset());
 | 
						|
 | 
						|
  for (i++ ; i < arg_count ; i++)
 | 
						|
  {
 | 
						|
    if (!(res= args[i]->val_str(&tmp_value)) || res->length() == 0)
 | 
						|
     continue;
 | 
						|
    if (append_value(thd, str, res))
 | 
						|
      goto null;
 | 
						|
  }
 | 
						|
 | 
						|
  str->set_charset(collation.collation);
 | 
						|
  return str;
 | 
						|
 | 
						|
null:
 | 
						|
  null_value= true;
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool Item_func_concat::append_value(THD *thd, String *res, const String *app)
 | 
						|
{
 | 
						|
  uint concat_len;
 | 
						|
  if ((concat_len= res->length() + app->length()) >
 | 
						|
      thd->variables.max_allowed_packet)
 | 
						|
  {
 | 
						|
    push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
 | 
						|
                        ER_WARN_ALLOWED_PACKET_OVERFLOWED,
 | 
						|
                        ER(ER_WARN_ALLOWED_PACKET_OVERFLOWED), func_name(),
 | 
						|
                        thd->variables.max_allowed_packet);
 | 
						|
    return true;
 | 
						|
  }
 | 
						|
  DBUG_ASSERT(!res->uses_buffer_owned_by(app));
 | 
						|
  DBUG_ASSERT(!app->uses_buffer_owned_by(res));
 | 
						|
  return realloc_result(res, concat_len) || res->append(*app);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool Item_func_concat::fix_length_and_dec(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();
 | 
						|
 | 
						|
  fix_char_length_ulonglong(char_length);
 | 
						|
  return FALSE;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  @details
 | 
						|
  Function des_encrypt() by tonu@spam.ee & monty
 | 
						|
  Works only if compiled with OpenSSL library support.
 | 
						|
  @return
 | 
						|
    A binary string where first character is CHAR(128 | key-number).
 | 
						|
    If one uses a string key key_number is 127.
 | 
						|
    Encryption result is longer than original by formula:
 | 
						|
  @code new_length= org_length + (8-(org_length % 8))+1 @endcode
 | 
						|
*/
 | 
						|
bool Item_func_des_encrypt::fix_length_and_dec(THD *thd)
 | 
						|
{
 | 
						|
  set_maybe_null();
 | 
						|
  /* 9 = MAX ((8- (arg_len % 8)) + 1) */
 | 
						|
  max_length = args[0]->max_length + 9;
 | 
						|
  warn_deprecated<1010>(thd, func_name_cstring().str);
 | 
						|
  return FALSE;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
String *Item_func_des_encrypt::val_str(String *str)
 | 
						|
{
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
#if defined(HAVE_des) && !defined(EMBEDDED_LIBRARY)
 | 
						|
  uint code= ER_WRONG_PARAMETERS_TO_PROCEDURE;
 | 
						|
  DES_cblock ivec;
 | 
						|
  struct st_des_keyblock keyblock;
 | 
						|
  struct st_des_keyschedule keyschedule;
 | 
						|
  const char *append_str="********";
 | 
						|
  uint key_number, res_length, tail;
 | 
						|
  String *res= args[0]->val_str(&tmp_value);
 | 
						|
 | 
						|
  if ((null_value= args[0]->null_value))
 | 
						|
    return 0;                                   // ENCRYPT(NULL) == NULL
 | 
						|
  if ((res_length=res->length()) == 0)
 | 
						|
    return make_empty_result(str);
 | 
						|
  if (arg_count == 1)
 | 
						|
  {
 | 
						|
    /* Protect against someone doing FLUSH DES_KEY_FILE */
 | 
						|
    mysql_mutex_lock(&LOCK_des_key_file);
 | 
						|
    keyschedule= des_keyschedule[key_number=des_default_key];
 | 
						|
    mysql_mutex_unlock(&LOCK_des_key_file);
 | 
						|
  }
 | 
						|
  else if (args[1]->result_type() == INT_RESULT)
 | 
						|
  {
 | 
						|
    key_number= (uint) args[1]->val_int();
 | 
						|
    if (key_number > 9)
 | 
						|
      goto error;
 | 
						|
    mysql_mutex_lock(&LOCK_des_key_file);
 | 
						|
    keyschedule= des_keyschedule[key_number];
 | 
						|
    mysql_mutex_unlock(&LOCK_des_key_file);
 | 
						|
  }
 | 
						|
  else
 | 
						|
  {
 | 
						|
    String *keystr= args[1]->val_str(str);
 | 
						|
    if (!keystr)
 | 
						|
      goto error;
 | 
						|
    key_number=127;				// User key string
 | 
						|
 | 
						|
    /* We make good 24-byte (168 bit) key from given plaintext key with MD5 */
 | 
						|
    bzero((char*) &ivec,sizeof(ivec));
 | 
						|
    if (!EVP_BytesToKey(EVP_des_ede3_cbc(),EVP_md5(),NULL,
 | 
						|
		   (uchar*) keystr->ptr(), (int) keystr->length(),
 | 
						|
		   1, (uchar*) &keyblock,ivec))
 | 
						|
      goto error;
 | 
						|
    DES_set_key_unchecked(&keyblock.key1,&keyschedule.ks1);
 | 
						|
    DES_set_key_unchecked(&keyblock.key2,&keyschedule.ks2);
 | 
						|
    DES_set_key_unchecked(&keyblock.key3,&keyschedule.ks3);
 | 
						|
  }
 | 
						|
 | 
						|
  /*
 | 
						|
     The problem: DES algorithm requires original data to be in 8-bytes
 | 
						|
     chunks. Missing bytes get filled with '*'s and result of encryption
 | 
						|
     can be up to 8 bytes longer than original string. When decrypted,
 | 
						|
     we do not know the size of original string :(
 | 
						|
     We add one byte with value 0x1..0x8 as the last byte of the padded
 | 
						|
     string marking change of string length.
 | 
						|
  */
 | 
						|
 | 
						|
  tail= 8 - (res_length % 8);                   // 1..8 marking extra length
 | 
						|
  res_length+=tail;
 | 
						|
  if (tmp_arg.alloc(res_length))
 | 
						|
    goto error;
 | 
						|
  tmp_arg.length(0);
 | 
						|
  tmp_arg.append(res->ptr(), res->length());
 | 
						|
  code= ER_OUT_OF_RESOURCES;
 | 
						|
  if (tmp_arg.append(append_str, tail) || str->alloc(res_length+1))
 | 
						|
    goto error;
 | 
						|
  tmp_arg[res_length-1]=tail;                   // save extra length
 | 
						|
  str->length(res_length+1);
 | 
						|
  str->set_charset(&my_charset_bin);
 | 
						|
  (*str)[0]=(char) (128 | key_number);
 | 
						|
  // Real encryption
 | 
						|
  bzero((char*) &ivec,sizeof(ivec));
 | 
						|
  DES_ede3_cbc_encrypt((const uchar*) (tmp_arg.ptr()),
 | 
						|
		       (uchar*) (str->ptr()+1),
 | 
						|
		       res_length,
 | 
						|
		       &keyschedule.ks1,
 | 
						|
		       &keyschedule.ks2,
 | 
						|
		       &keyschedule.ks3,
 | 
						|
		       &ivec, TRUE);
 | 
						|
  return str;
 | 
						|
 | 
						|
error:
 | 
						|
  THD *thd= current_thd;
 | 
						|
  push_warning_printf(thd,Sql_condition::WARN_LEVEL_WARN,
 | 
						|
                      code, ER_THD(thd, code),
 | 
						|
                      "des_encrypt");
 | 
						|
#else
 | 
						|
  THD *thd= current_thd;
 | 
						|
  push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
 | 
						|
                      ER_FEATURE_DISABLED, ER_THD(thd, ER_FEATURE_DISABLED),
 | 
						|
                      "des_encrypt", "openssl des cipher (HAVE_des)");
 | 
						|
#endif /* defined(HAVE_des) && !defined(EMBEDDED_LIBRARY) */
 | 
						|
  null_value=1;
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool Item_func_des_decrypt::fix_length_and_dec(THD *thd)
 | 
						|
{
 | 
						|
  set_maybe_null();
 | 
						|
  /* 9 = MAX ((8- (arg_len % 8)) + 1) */
 | 
						|
  max_length= args[0]->max_length;
 | 
						|
  if (max_length >= 9U)
 | 
						|
    max_length-= 9U;
 | 
						|
  warn_deprecated<1010>(thd, func_name_cstring().str);
 | 
						|
  return FALSE;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
String *Item_func_des_decrypt::val_str(String *str)
 | 
						|
{
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
#if defined(HAVE_des) && !defined(EMBEDDED_LIBRARY)
 | 
						|
  uint code= ER_WRONG_PARAMETERS_TO_PROCEDURE;
 | 
						|
  DES_cblock ivec;
 | 
						|
  struct st_des_keyblock keyblock;
 | 
						|
  struct st_des_keyschedule keyschedule;
 | 
						|
  String *res= args[0]->val_str(&tmp_value);
 | 
						|
  uint length,tail;
 | 
						|
 | 
						|
  if ((null_value= args[0]->null_value))
 | 
						|
    return 0;
 | 
						|
  length= res->length();
 | 
						|
  if (length < 9 || (length % 8) != 1 || !((*res)[0] & 128))
 | 
						|
    return res;				// Skip decryption if not encrypted
 | 
						|
 | 
						|
  if (arg_count == 1)			// If automatic uncompression
 | 
						|
  {
 | 
						|
    uint key_number=(uint) (*res)[0] & 127;
 | 
						|
    // Check if automatic key and that we have privilege to uncompress using it
 | 
						|
    if (!(current_thd->security_ctx->master_access & PRIV_DES_DECRYPT_ONE_ARG) ||
 | 
						|
        key_number > 9)
 | 
						|
      goto error;
 | 
						|
 | 
						|
    mysql_mutex_lock(&LOCK_des_key_file);
 | 
						|
    keyschedule= des_keyschedule[key_number];
 | 
						|
    mysql_mutex_unlock(&LOCK_des_key_file);
 | 
						|
  }
 | 
						|
  else
 | 
						|
  {
 | 
						|
    // We make good 24-byte (168 bit) key from given plaintext key with MD5
 | 
						|
    String *keystr= args[1]->val_str(str);
 | 
						|
    if (!keystr)
 | 
						|
      goto error;
 | 
						|
 | 
						|
    bzero((char*) &ivec,sizeof(ivec));
 | 
						|
    if (!EVP_BytesToKey(EVP_des_ede3_cbc(),EVP_md5(),NULL,
 | 
						|
		   (uchar*) keystr->ptr(),(int) keystr->length(),
 | 
						|
		   1,(uchar*) &keyblock,ivec))
 | 
						|
      goto error;
 | 
						|
    // Here we set all 64-bit keys (56 effective) one by one
 | 
						|
    DES_set_key_unchecked(&keyblock.key1,&keyschedule.ks1);
 | 
						|
    DES_set_key_unchecked(&keyblock.key2,&keyschedule.ks2);
 | 
						|
    DES_set_key_unchecked(&keyblock.key3,&keyschedule.ks3);
 | 
						|
  }
 | 
						|
  code= ER_OUT_OF_RESOURCES;
 | 
						|
  if (str->alloc(length-1))
 | 
						|
    goto error;
 | 
						|
 | 
						|
  bzero((char*) &ivec,sizeof(ivec));
 | 
						|
  DES_ede3_cbc_encrypt((const uchar*) res->ptr()+1,
 | 
						|
		       (uchar*) (str->ptr()),
 | 
						|
		       length-1,
 | 
						|
		       &keyschedule.ks1,
 | 
						|
		       &keyschedule.ks2,
 | 
						|
		       &keyschedule.ks3,
 | 
						|
		       &ivec, FALSE);
 | 
						|
  /* Restore old length of key */
 | 
						|
  if ((tail=(uint) (uchar) (*str)[length-2]) > 8)
 | 
						|
    goto wrong_key;				     // Wrong key
 | 
						|
  str->length(length-1-tail);
 | 
						|
  str->set_charset(&my_charset_bin);
 | 
						|
  return str;
 | 
						|
 | 
						|
error:
 | 
						|
  {
 | 
						|
    THD *thd= current_thd;
 | 
						|
    push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
 | 
						|
                        code, ER_THD(thd, code),
 | 
						|
                        "des_decrypt");
 | 
						|
  }
 | 
						|
wrong_key:
 | 
						|
#else
 | 
						|
  {
 | 
						|
    THD *thd= current_thd;
 | 
						|
    push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
 | 
						|
                        ER_FEATURE_DISABLED, ER_THD(thd, ER_FEATURE_DISABLED),
 | 
						|
                        "des_decrypt", "openssl des cipher (HAVE_des)");
 | 
						|
  }
 | 
						|
#endif /* defined(HAVE_des) && !defined(EMBEDDED_LIBRARY) */
 | 
						|
  null_value=1;
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  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());
 | 
						|
  char tmp_str_buff[10];
 | 
						|
  String tmp_sep_str(tmp_str_buff, sizeof(tmp_str_buff),default_charset_info),
 | 
						|
         *sep_str, *res, *res2,*use_as_buff;
 | 
						|
  uint i;
 | 
						|
  bool is_const= 0;
 | 
						|
  THD *thd= 0;
 | 
						|
 | 
						|
  null_value=0;
 | 
						|
  if (!(sep_str= args[0]->val_str(&tmp_sep_str)))
 | 
						|
    goto null;
 | 
						|
 | 
						|
  use_as_buff= &tmp_value;
 | 
						|
  str->length(0);				// QQ; Should be removed
 | 
						|
  res=str;                                      // If 0 arg_count
 | 
						|
 | 
						|
  // 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)))
 | 
						|
    {
 | 
						|
      is_const= args[i]->const_item();
 | 
						|
      break;
 | 
						|
    }
 | 
						|
 | 
						|
  if (i ==  arg_count)
 | 
						|
    return make_empty_result(str);
 | 
						|
 | 
						|
  for (i++; i < arg_count ; i++)
 | 
						|
  {
 | 
						|
    if (!(res2= args[i]->val_str(use_as_buff)))
 | 
						|
      continue;					// Skip NULL
 | 
						|
 | 
						|
    if (!thd)
 | 
						|
      thd= current_thd;
 | 
						|
    if (res->length() + sep_str->length() + res2->length() >
 | 
						|
	thd->variables.max_allowed_packet)
 | 
						|
    {
 | 
						|
      push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
 | 
						|
			  ER_WARN_ALLOWED_PACKET_OVERFLOWED,
 | 
						|
			  ER_THD(thd, ER_WARN_ALLOWED_PACKET_OVERFLOWED),
 | 
						|
                          func_name(),
 | 
						|
			  thd->variables.max_allowed_packet);
 | 
						|
      goto null;
 | 
						|
    }
 | 
						|
    if (!is_const && res->alloced_length() >=
 | 
						|
	res->length() + sep_str->length() + res2->length())
 | 
						|
    {						// Use old buffer
 | 
						|
      res->append(*sep_str);			// res->length() > 0 always
 | 
						|
      res->append(*res2);
 | 
						|
    }
 | 
						|
    else if (str->alloced_length() >=
 | 
						|
	     res->length() + sep_str->length() + res2->length())
 | 
						|
    {
 | 
						|
      /* We have room in str;  We can't get any errors here */
 | 
						|
      if (str->ptr() == res2->ptr())
 | 
						|
      {						// This is quite uncommon!
 | 
						|
	str->replace(0,0,*sep_str);
 | 
						|
	str->replace(0,0,*res);
 | 
						|
      }
 | 
						|
      else
 | 
						|
      {
 | 
						|
	str->copy(*res);
 | 
						|
	str->append(*sep_str);
 | 
						|
	str->append(*res2);
 | 
						|
      }
 | 
						|
      res=str;
 | 
						|
      use_as_buff= &tmp_value;
 | 
						|
    }
 | 
						|
    else if (res == &tmp_value)
 | 
						|
    {
 | 
						|
      if (res->append(*sep_str) || res->append(*res2))
 | 
						|
	goto null; // Must be a blob
 | 
						|
    }
 | 
						|
    else if (res2 == &tmp_value)
 | 
						|
    {						// This can happen only 1 time
 | 
						|
      if (tmp_value.replace(0,0,*sep_str) || tmp_value.replace(0,0,*res))
 | 
						|
	goto null;
 | 
						|
      res= &tmp_value;
 | 
						|
      use_as_buff=str;				// Put next arg here
 | 
						|
    }
 | 
						|
    else if (tmp_value.is_alloced() && res2->ptr() >= tmp_value.ptr() &&
 | 
						|
	     res2->ptr() < tmp_value.ptr() + tmp_value.alloced_length())
 | 
						|
    {
 | 
						|
      /*
 | 
						|
	This happens really seldom:
 | 
						|
	In this case res2 is sub string of tmp_value.  We will
 | 
						|
	now work in place in tmp_value to set it to res | sep_str | res2
 | 
						|
      */
 | 
						|
      /* Chop the last characters in tmp_value that isn't in res2 */
 | 
						|
      tmp_value.length((uint32) (res2->ptr() - tmp_value.ptr()) +
 | 
						|
		       res2->length());
 | 
						|
      /* Place res2 at start of tmp_value, remove chars before res2 */
 | 
						|
      if (tmp_value.replace(0,(uint32) (res2->ptr() - tmp_value.ptr()),
 | 
						|
			    *res) ||
 | 
						|
	  tmp_value.replace(res->length(),0, *sep_str))
 | 
						|
	goto null;
 | 
						|
      res= &tmp_value;
 | 
						|
      use_as_buff=str;			// Put next arg here
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {						// Two big const strings
 | 
						|
      /*
 | 
						|
        NOTE: We should be prudent in the initial allocation unit -- the
 | 
						|
        size of the arguments is a function of data distribution, which can
 | 
						|
        be any. Instead of overcommitting at the first row, we grow the
 | 
						|
        allocated amount by the factor of 2. This ensures that no more than
 | 
						|
        25% of memory will be overcommitted on average.
 | 
						|
      */
 | 
						|
 | 
						|
      uint concat_len= res->length() + sep_str->length() + res2->length();
 | 
						|
 | 
						|
      if (tmp_value.alloced_length() < concat_len)
 | 
						|
      {
 | 
						|
        if (tmp_value.alloced_length() == 0)
 | 
						|
        {
 | 
						|
          if (tmp_value.alloc(concat_len))
 | 
						|
            goto null;
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
          uint new_len = MY_MAX(tmp_value.alloced_length() * 2, concat_len);
 | 
						|
 | 
						|
          if (tmp_value.alloc(new_len))
 | 
						|
            goto null;
 | 
						|
        }
 | 
						|
      }
 | 
						|
 | 
						|
      if (tmp_value.copy(*res) ||
 | 
						|
	  tmp_value.append(*sep_str) ||
 | 
						|
	  tmp_value.append(*res2))
 | 
						|
	goto null;
 | 
						|
      res= &tmp_value;
 | 
						|
      use_as_buff=str;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  res->set_charset(collation.collation);
 | 
						|
  return res;
 | 
						|
 | 
						|
null:
 | 
						|
  null_value=1;
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool Item_func_concat_ws::fix_length_and_dec(THD *thd)
 | 
						|
{
 | 
						|
  ulonglong char_length;
 | 
						|
 | 
						|
  if (agg_arg_charsets_for_string_result(collation, args, arg_count))
 | 
						|
    return TRUE;
 | 
						|
 | 
						|
  /*
 | 
						|
     arg_count cannot be less than 2,
 | 
						|
     it is done on parser level in sql_yacc.yy
 | 
						|
     so, (arg_count - 2) is safe here.
 | 
						|
  */
 | 
						|
  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();
 | 
						|
 | 
						|
  fix_char_length_ulonglong(char_length);
 | 
						|
  return FALSE;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
String *Item_func_reverse::val_str(String *str)
 | 
						|
{
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
  String *res= args[0]->val_str(&tmp_value);
 | 
						|
  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(str);
 | 
						|
  if (str->alloc(res->length()))
 | 
						|
  {
 | 
						|
    null_value= 1;
 | 
						|
    return 0;
 | 
						|
  }
 | 
						|
  str->length(res->length());
 | 
						|
  str->set_charset(res->charset());
 | 
						|
  ptr= res->ptr();
 | 
						|
  end= res->end();
 | 
						|
  tmp= (char *) str->end();
 | 
						|
#ifdef USE_MB
 | 
						|
  if (res->use_mb())
 | 
						|
  {
 | 
						|
    uint32 l;
 | 
						|
    while (ptr < end)
 | 
						|
    {
 | 
						|
      if ((l= my_ismbchar(res->charset(),ptr,end)))
 | 
						|
      {
 | 
						|
        tmp-= l;
 | 
						|
        DBUG_ASSERT(tmp >= str->ptr());
 | 
						|
        memcpy(tmp,ptr,l);
 | 
						|
        ptr+= l;
 | 
						|
      }
 | 
						|
      else
 | 
						|
        *--tmp= *ptr++;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  else
 | 
						|
#endif /* USE_MB */
 | 
						|
  {
 | 
						|
    while (ptr < end)
 | 
						|
      *--tmp= *ptr++;
 | 
						|
  }
 | 
						|
  return str;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool Item_func_reverse::fix_length_and_dec(THD *thd)
 | 
						|
{
 | 
						|
  if (agg_arg_charsets_for_string_result(collation, args, 1))
 | 
						|
    return TRUE;
 | 
						|
  DBUG_ASSERT(collation.collation != NULL);
 | 
						|
  fix_char_length(args[0]->max_char_length());
 | 
						|
  return FALSE;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Replace all occurrences of string2 in string1 with string3.
 | 
						|
 | 
						|
  Don't reallocate val_str() if not needed.
 | 
						|
 | 
						|
  @todo
 | 
						|
    Fix that this works with binary strings when using USE_MB 
 | 
						|
*/
 | 
						|
 | 
						|
String *Item_func_replace::val_str_internal(String *str, bool null_to_empty)
 | 
						|
{
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
  String *res,*res2,*res3;
 | 
						|
  int offset;
 | 
						|
  uint from_length,to_length;
 | 
						|
  bool alloced=0;
 | 
						|
#ifdef USE_MB
 | 
						|
  const char *ptr,*end,*strend,*search,*search_end;
 | 
						|
  uint32 l;
 | 
						|
  bool binary_cmp;
 | 
						|
#endif
 | 
						|
  THD *thd= 0;
 | 
						|
 | 
						|
  null_value=0;
 | 
						|
  res=args[0]->val_str(str);
 | 
						|
  if (args[0]->null_value)
 | 
						|
    goto null;
 | 
						|
  if (!(res2= args[1]->val_str_null_to_empty(&tmp_value, null_to_empty)))
 | 
						|
    goto null;
 | 
						|
  res->set_charset(collation.collation);
 | 
						|
 | 
						|
#ifdef USE_MB
 | 
						|
  binary_cmp = ((res->charset()->state & MY_CS_BINSORT) || !res->use_mb());
 | 
						|
#endif
 | 
						|
 | 
						|
  if (res2->length() == 0)
 | 
						|
    return res;
 | 
						|
#ifndef USE_MB
 | 
						|
  if ((offset=res->strstr(*res2)) < 0)
 | 
						|
    return res;
 | 
						|
#else
 | 
						|
  offset=0;
 | 
						|
  if (binary_cmp && (offset=res->strstr(*res2)) < 0)
 | 
						|
    return res;
 | 
						|
#endif
 | 
						|
  if (!(res3= args[2]->val_str_null_to_empty(&tmp_value2, null_to_empty)))
 | 
						|
    goto null;
 | 
						|
  from_length= res2->length();
 | 
						|
  to_length=   res3->length();
 | 
						|
 | 
						|
#ifdef USE_MB
 | 
						|
  if (!binary_cmp)
 | 
						|
  {
 | 
						|
    search=res2->ptr();
 | 
						|
    search_end=search+from_length;
 | 
						|
redo:
 | 
						|
    DBUG_ASSERT(res->ptr() || !offset);
 | 
						|
    ptr=res->ptr()+offset;
 | 
						|
    strend=res->ptr()+res->length();
 | 
						|
    /*
 | 
						|
      In some cases val_str() can return empty string
 | 
						|
      with ptr() == NULL and length() == 0.
 | 
						|
      Let's check strend to avoid overflow.
 | 
						|
    */
 | 
						|
    end= strend ? strend - from_length + 1 : NULL;
 | 
						|
    while (ptr < end)
 | 
						|
    {
 | 
						|
      if (*ptr == *search)
 | 
						|
      {
 | 
						|
        char *i,*j;
 | 
						|
        i=(char*) ptr+1; j=(char*) search+1;
 | 
						|
        while (j != search_end)
 | 
						|
          if (*i++ != *j++) goto skip;
 | 
						|
        offset= (int) (ptr-res->ptr());
 | 
						|
 | 
						|
        if (!thd)
 | 
						|
          thd= current_thd;
 | 
						|
 | 
						|
        if (res->length()-from_length + to_length >
 | 
						|
            thd->variables.max_allowed_packet)
 | 
						|
        {
 | 
						|
          push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
 | 
						|
                              ER_WARN_ALLOWED_PACKET_OVERFLOWED,
 | 
						|
                              ER_THD(thd, ER_WARN_ALLOWED_PACKET_OVERFLOWED),
 | 
						|
                              func_name(),
 | 
						|
                              thd->variables.max_allowed_packet);
 | 
						|
 | 
						|
          goto null;
 | 
						|
        }
 | 
						|
        if (!alloced)
 | 
						|
        {
 | 
						|
          alloced=1;
 | 
						|
          res=copy_if_not_alloced(str,res,res->length()+to_length);
 | 
						|
        }
 | 
						|
        res->replace((uint) offset,from_length,*res3);
 | 
						|
        offset+=(int) to_length;
 | 
						|
        goto redo;
 | 
						|
      }
 | 
						|
  skip:
 | 
						|
      if ((l=my_ismbchar(res->charset(), ptr,strend))) ptr+=l;
 | 
						|
      else ++ptr;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  else
 | 
						|
#endif /* USE_MB */
 | 
						|
  {
 | 
						|
    thd= current_thd;
 | 
						|
    do
 | 
						|
    {
 | 
						|
      if (res->length()-from_length + to_length >
 | 
						|
	  thd->variables.max_allowed_packet)
 | 
						|
      {
 | 
						|
	push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
 | 
						|
			    ER_WARN_ALLOWED_PACKET_OVERFLOWED,
 | 
						|
			    ER_THD(thd, ER_WARN_ALLOWED_PACKET_OVERFLOWED),
 | 
						|
                            func_name(),
 | 
						|
			    thd->variables.max_allowed_packet);
 | 
						|
        goto null;
 | 
						|
      }
 | 
						|
      if (!alloced)
 | 
						|
      {
 | 
						|
        alloced=1;
 | 
						|
        res=copy_if_not_alloced(str,res,res->length()+to_length);
 | 
						|
      }
 | 
						|
      res->replace((uint) offset,from_length,*res3);
 | 
						|
      offset+=(int) to_length;
 | 
						|
    }
 | 
						|
    while ((offset=res->strstr(*res2,(uint) offset)) >= 0);
 | 
						|
  }
 | 
						|
  if (null_to_empty && !res->length())
 | 
						|
    goto null;
 | 
						|
 | 
						|
  return res;
 | 
						|
 | 
						|
null:
 | 
						|
  null_value=1;
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool Item_func_replace::fix_length_and_dec(THD *thd)
 | 
						|
{
 | 
						|
  ulonglong char_length= (ulonglong) args[0]->max_char_length();
 | 
						|
  int diff=(int) (args[2]->max_char_length() - 1);
 | 
						|
  if (diff > 0)
 | 
						|
  {						// Calculate of maxreplaces
 | 
						|
    ulonglong max_substrs= char_length;
 | 
						|
    char_length+= max_substrs * (uint) diff;
 | 
						|
  }
 | 
						|
 | 
						|
  if (agg_arg_charsets_for_string_result_with_comparison(collation, args, 3))
 | 
						|
    return TRUE;
 | 
						|
  fix_char_length_ulonglong(char_length);
 | 
						|
  return FALSE;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
  this is done in the constructor to be in the same memroot as
 | 
						|
  the item itself
 | 
						|
*/
 | 
						|
Item_func_sformat::Item_func_sformat(THD *thd, List<Item> &list)
 | 
						|
  : Item_str_func(thd, list)
 | 
						|
{
 | 
						|
  val_arg= new (thd->mem_root) String[arg_count];
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool Item_func_sformat::fix_length_and_dec(THD *thd)
 | 
						|
{
 | 
						|
  if (!val_arg)
 | 
						|
    return TRUE;
 | 
						|
 | 
						|
  ulonglong char_length= 0;
 | 
						|
 | 
						|
  uint flags= MY_COLL_ALLOW_SUPERSET_CONV |
 | 
						|
              MY_COLL_ALLOW_COERCIBLE_CONV |
 | 
						|
              MY_COLL_ALLOW_NUMERIC_CONV;
 | 
						|
 | 
						|
  if (Type_std_attributes::agg_item_collations(collation, func_name_cstring(),
 | 
						|
        args, arg_count, flags, 1))
 | 
						|
    return TRUE;
 | 
						|
 | 
						|
  DTCollation c= collation;
 | 
						|
  if (c.collation->mbminlen > 1)
 | 
						|
    c.collation= &my_charset_utf8mb4_bin;
 | 
						|
 | 
						|
  for (uint i=0 ; i < arg_count ; i++)
 | 
						|
  {
 | 
						|
    if (args[i]->result_type() == STRING_RESULT &&
 | 
						|
        Type_std_attributes::agg_item_set_converter(c, func_name_cstring(),
 | 
						|
                                                    args+i, 1, flags, 1))
 | 
						|
      return TRUE;
 | 
						|
  }
 | 
						|
 | 
						|
  char_length= MAX_BLOB_WIDTH;
 | 
						|
  fix_char_length_ulonglong(char_length);
 | 
						|
  return FALSE;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
  allow fmt to take String arguments directly.
 | 
						|
  Inherit from string_view, so all string formatting works.
 | 
						|
  but {:p} doesn't, because it's not char*, not a pointer.
 | 
						|
*/
 | 
						|
namespace fmt {
 | 
						|
  template <> struct formatter<String>: formatter<string_view> {
 | 
						|
    template <typename FormatContext>
 | 
						|
    auto format(String c, FormatContext& ctx) const -> decltype(ctx.out()) {
 | 
						|
      string_view name = { c.ptr(), c.length() };
 | 
						|
      return formatter<string_view>::format(name, ctx);
 | 
						|
    };
 | 
						|
    /* needed below function for libfmt-7.1.3 compatibility, (not 9.1.0+) */
 | 
						|
    template <typename FormatContext>
 | 
						|
    auto format(String c, FormatContext& ctx) -> decltype(ctx.out()) {
 | 
						|
      string_view name = { c.ptr(), c.length() };
 | 
						|
      return formatter<string_view>::format(name, ctx);
 | 
						|
    };
 | 
						|
  };
 | 
						|
};
 | 
						|
 | 
						|
struct fmt_locale_comma : std::numpunct<char>
 | 
						|
{
 | 
						|
  char do_thousands_sep() const override { return ','; }
 | 
						|
  std::string do_grouping() const override { return "\3"; }
 | 
						|
};
 | 
						|
static std::locale fmt_locale(std::locale(), new fmt_locale_comma);
 | 
						|
 | 
						|
/*
 | 
						|
  SFORMAT(format_string, ...)
 | 
						|
  This function receives a formatting specification string and N parameters
 | 
						|
  (N >= 0), and it returns string formatted using the rules the user passed
 | 
						|
  in the specification. It uses fmtlib (https://fmt.dev/).
 | 
						|
*/
 | 
						|
String *Item_func_sformat::val_str(String *res)
 | 
						|
{
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
  using         ArgStore= fmt::dynamic_format_arg_store<fmt::format_context>;
 | 
						|
  String       *fmt_arg=  NULL;
 | 
						|
  String       *parg=     NULL;
 | 
						|
  ArgStore      arg_store;
 | 
						|
 | 
						|
  null_value= true;
 | 
						|
  if (!(fmt_arg= args[0]->val_str(res)))
 | 
						|
    return NULL;
 | 
						|
 | 
						|
  /* Creates the array of arguments for vformat */
 | 
						|
  for (uint carg= 1; carg < arg_count; carg++)
 | 
						|
  {
 | 
						|
    switch (args[carg]->result_type())
 | 
						|
    {
 | 
						|
    case INT_RESULT:
 | 
						|
      arg_store.push_back(args[carg]->val_int());
 | 
						|
      break;
 | 
						|
    case DECIMAL_RESULT: // TODO
 | 
						|
    case REAL_RESULT:
 | 
						|
      if (args[carg]->field_type() == MYSQL_TYPE_FLOAT)
 | 
						|
        arg_store.push_back((float)args[carg]->val_real());
 | 
						|
      else
 | 
						|
        arg_store.push_back(args[carg]->val_real());
 | 
						|
      break;
 | 
						|
    case STRING_RESULT:
 | 
						|
      if (!(parg= args[carg]->val_str(&val_arg[carg-1])))
 | 
						|
      {
 | 
						|
        return NULL;
 | 
						|
      }
 | 
						|
      arg_store.push_back(*parg);
 | 
						|
      break;
 | 
						|
    case TIME_RESULT: // TODO
 | 
						|
    case ROW_RESULT: // TODO
 | 
						|
    default:
 | 
						|
      DBUG_ASSERT(0);
 | 
						|
      return NULL;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  null_value= false;
 | 
						|
  /* Create the string output  */
 | 
						|
  try
 | 
						|
  {
 | 
						|
    auto text = fmt::vformat(fmt_locale, fmt_arg->c_ptr_safe(), arg_store);
 | 
						|
    res->length(0);
 | 
						|
    res->set_charset(collation.collation);
 | 
						|
    res->append(text.c_str(), text.size(), fmt_arg->charset());
 | 
						|
  }
 | 
						|
  catch (const fmt::format_error &ex)
 | 
						|
  {
 | 
						|
    THD *thd= current_thd;
 | 
						|
    push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
 | 
						|
                        WARN_SFORMAT_ERROR,
 | 
						|
                        ER_THD(thd, WARN_SFORMAT_ERROR), ex.what());
 | 
						|
    null_value= true;
 | 
						|
  }
 | 
						|
  return null_value ? NULL : res;
 | 
						|
}
 | 
						|
 | 
						|
#include"my_global.h"
 | 
						|
#include <openssl/rand.h>
 | 
						|
#include <openssl/err.h>
 | 
						|
 | 
						|
bool Item_func_random_bytes::fix_length_and_dec(THD *thd)
 | 
						|
{
 | 
						|
  set_maybe_null();
 | 
						|
  used_tables_cache|= RAND_TABLE_BIT;
 | 
						|
  if (args[0]->can_eval_in_optimize())
 | 
						|
  {
 | 
						|
    int32 v= (int32) args[0]->val_int();
 | 
						|
    max_length= MY_MAX(0, MY_MIN(v, MAX_RANDOM_BYTES));
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
  max_length= MAX_RANDOM_BYTES;
 | 
						|
  return false;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void Item_func_random_bytes::update_used_tables()
 | 
						|
{
 | 
						|
  Item_str_func::update_used_tables();
 | 
						|
  used_tables_cache|= RAND_TABLE_BIT;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
String *Item_func_random_bytes::val_str(String *str)
 | 
						|
{
 | 
						|
  longlong count= args[0]->val_int();
 | 
						|
 | 
						|
  if (args[0]->null_value)
 | 
						|
    goto err;
 | 
						|
  null_value= 0;
 | 
						|
 | 
						|
  if (count < 0 || count > MAX_RANDOM_BYTES)
 | 
						|
    goto err;
 | 
						|
 | 
						|
  if (count == 0)
 | 
						|
    return make_empty_result(str);
 | 
						|
 | 
						|
  if (str->alloc((uint) count))
 | 
						|
    goto err;
 | 
						|
 | 
						|
  str->length(count);
 | 
						|
  str->set_charset(&my_charset_bin);
 | 
						|
  if (my_random_bytes((unsigned char *) str->ptr(), (int32) count))
 | 
						|
  {
 | 
						|
    ulong ssl_err;
 | 
						|
    while ((ssl_err= ERR_get_error()))
 | 
						|
    {
 | 
						|
      char buf[256];
 | 
						|
      ERR_error_string_n(ssl_err, buf, sizeof(buf));
 | 
						|
      sql_print_warning("SSL error: %s", buf);
 | 
						|
    }
 | 
						|
    goto err;
 | 
						|
  }
 | 
						|
 | 
						|
  return str;
 | 
						|
 | 
						|
err:
 | 
						|
  null_value= 1;
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*********************************************************************/
 | 
						|
bool Item_func_regexp_replace::fix_length_and_dec(THD *thd)
 | 
						|
{
 | 
						|
  if (agg_arg_charsets_for_string_result_with_comparison(collation, args, 3))
 | 
						|
    return TRUE;
 | 
						|
  max_length= MAX_BLOB_WIDTH;
 | 
						|
  re.init(collation.collation, 0);
 | 
						|
  re.fix_owner(this, args[0], args[1]);
 | 
						|
  return FALSE;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
  Traverse through the replacement string and append to "str".
 | 
						|
  Sub-pattern references \0 .. \9 are recognized, which are replaced
 | 
						|
  to the chunks of the source string.
 | 
						|
*/
 | 
						|
bool Item_func_regexp_replace::append_replacement(String *str,
 | 
						|
                                                  const LEX_CSTRING *source,
 | 
						|
                                                  const LEX_CSTRING *replace)
 | 
						|
{
 | 
						|
  const char *beg= replace->str;
 | 
						|
  const char *end= beg + replace->length;
 | 
						|
  CHARSET_INFO *cs= re.library_charset();
 | 
						|
 | 
						|
  for ( ; ; )
 | 
						|
  {
 | 
						|
    my_wc_t wc;
 | 
						|
    int cnv, n;
 | 
						|
 | 
						|
    if ((cnv= cs->mb_wc(&wc, (const uchar *) beg,
 | 
						|
                             (const uchar *) end)) < 1)
 | 
						|
      break; /* End of line */
 | 
						|
    beg+= cnv;
 | 
						|
 | 
						|
    if (wc != '\\')
 | 
						|
    {
 | 
						|
      if (str->append(beg - cnv, cnv, cs))
 | 
						|
        return true;
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
 | 
						|
    if ((cnv= cs->mb_wc(&wc, (const uchar *) beg,
 | 
						|
                             (const uchar *) end)) < 1)
 | 
						|
      break; /* End of line */
 | 
						|
    beg+= cnv;
 | 
						|
 | 
						|
    if ((n= ((int) wc) - '0') >= 0 && n <= 9)
 | 
						|
    {
 | 
						|
      if (n < re.nsubpatterns())
 | 
						|
      {
 | 
						|
        /* A valid sub-pattern reference found */
 | 
						|
        size_t pbeg= re.subpattern_start(n), plength= re.subpattern_end(n) - pbeg;
 | 
						|
        if (str->append(source->str + pbeg, plength, cs))
 | 
						|
          return true;
 | 
						|
      }
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
      /*
 | 
						|
         A non-digit character following after '\'.
 | 
						|
         Just add the character itself.
 | 
						|
       */
 | 
						|
      if (str->append(beg - cnv, cnv, cs))
 | 
						|
        return false;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  return false;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
String *Item_func_regexp_replace::val_str_internal(String *str,
 | 
						|
                                                   bool null_to_empty)
 | 
						|
{
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
  char buff0[MAX_FIELD_WIDTH];
 | 
						|
  char buff2[MAX_FIELD_WIDTH];
 | 
						|
  String tmp0(buff0,sizeof(buff0),&my_charset_bin);
 | 
						|
  String tmp2(buff2,sizeof(buff2),&my_charset_bin);
 | 
						|
  String *source, *replace;
 | 
						|
  LEX_CSTRING src, rpl;
 | 
						|
  size_t startoffset= 0;
 | 
						|
 | 
						|
  source= args[0]->val_str(&tmp0);
 | 
						|
  if (!source)
 | 
						|
    goto err;
 | 
						|
  replace= args[2]->val_str_null_to_empty(&tmp2, null_to_empty);
 | 
						|
  if (!replace || re.recompile(args[1]))
 | 
						|
    goto err;
 | 
						|
  if (!(source= re.convert_if_needed(source, &re.subject_converter)) ||
 | 
						|
      !(replace= re.convert_if_needed(replace, &re.replace_converter)))
 | 
						|
    goto err;
 | 
						|
  null_value= false;
 | 
						|
 | 
						|
  source->get_value(&src);
 | 
						|
  replace->get_value(&rpl);
 | 
						|
 | 
						|
  str->length(0);
 | 
						|
  str->set_charset(collation.collation);
 | 
						|
 | 
						|
  for ( ; ; ) // Iterate through all matches
 | 
						|
  {
 | 
						|
 | 
						|
    if (re.exec(src.str, src.length, startoffset))
 | 
						|
      goto err;
 | 
						|
 | 
						|
    if (!re.match() || re.subpattern_length(0) == 0)
 | 
						|
    {
 | 
						|
      /* 
 | 
						|
        No match or an empty match.
 | 
						|
        Append the rest of the source string
 | 
						|
        starting from startoffset until the end of the source.
 | 
						|
      */
 | 
						|
      if (str->append(src.str + startoffset, src.length - startoffset,
 | 
						|
                      re.library_charset()))
 | 
						|
        goto err;
 | 
						|
      return str;
 | 
						|
    }
 | 
						|
 | 
						|
    /*
 | 
						|
      Append prefix, the part before the matching pattern.
 | 
						|
      starting from startoffset until the next match
 | 
						|
    */
 | 
						|
    if (str->append(src.str + startoffset,
 | 
						|
          re.subpattern_start(0) - startoffset, re.library_charset()))
 | 
						|
      goto err;
 | 
						|
 | 
						|
    // Append replacement
 | 
						|
    if (append_replacement(str, &src, &rpl))
 | 
						|
      goto err;
 | 
						|
 | 
						|
    // Set the new start point as the end of previous match
 | 
						|
    startoffset= re.subpattern_end(0);
 | 
						|
  }
 | 
						|
  return str;
 | 
						|
 | 
						|
err:
 | 
						|
  null_value= true;
 | 
						|
  return nullptr;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool Item_func_regexp_substr::fix_length_and_dec(THD *thd)
 | 
						|
{
 | 
						|
  if (agg_arg_charsets_for_string_result_with_comparison(collation, args, 2))
 | 
						|
    return TRUE;
 | 
						|
  fix_char_length(args[0]->max_char_length());
 | 
						|
  re.init(collation.collation, 0);
 | 
						|
  re.fix_owner(this, args[0], args[1]);
 | 
						|
  return FALSE;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
String *Item_func_regexp_substr::val_str(String *str)
 | 
						|
{
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
  char buff0[MAX_FIELD_WIDTH];
 | 
						|
  String tmp0(buff0,sizeof(buff0),&my_charset_bin);
 | 
						|
  String *source= args[0]->val_str(&tmp0);
 | 
						|
 | 
						|
  if ((null_value= (args[0]->null_value || re.recompile(args[1]))))
 | 
						|
    return (String *) 0;
 | 
						|
 | 
						|
  if (!(source= re.convert_if_needed(source, &re.subject_converter)))
 | 
						|
    goto err;
 | 
						|
 | 
						|
  str->length(0);
 | 
						|
  str->set_charset(collation.collation);
 | 
						|
 | 
						|
  if (re.exec(source->ptr(), source->length(), 0))
 | 
						|
    goto err;
 | 
						|
 | 
						|
  if (!re.match())
 | 
						|
    return str;
 | 
						|
 | 
						|
  if (str->append(source->ptr() + re.subpattern_start(0),
 | 
						|
                  re.subpattern_length(0), re.library_charset()))
 | 
						|
    goto err;
 | 
						|
 | 
						|
  return str;
 | 
						|
 | 
						|
err:
 | 
						|
  null_value= true;
 | 
						|
  return (String *) 0;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/************************************************************************/
 | 
						|
 | 
						|
 | 
						|
String *Item_func_insert::val_str(String *str)
 | 
						|
{
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
  String *res,*res2;
 | 
						|
  longlong start, length;  /* 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)
 | 
						|
    goto null; /* purecov: inspected */
 | 
						|
 | 
						|
  if ((start <= 0) || (start > res->length()))
 | 
						|
    return res;                                 // Wrong param; skip insert
 | 
						|
  if ((length < 0) || (length > res->length()))
 | 
						|
    length= res->length();
 | 
						|
  start--;
 | 
						|
 | 
						|
  /*
 | 
						|
    There is one exception not handled (intentionally) 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 + 1 > res->length()) // remember, start = args[1].val_int() - 1
 | 
						|
    return res; /* purecov: inspected */        // Wrong param; skip insert
 | 
						|
  if (length > res->length() - start)
 | 
						|
    length= res->length() - start;
 | 
						|
 | 
						|
  {
 | 
						|
    THD *thd= current_thd;
 | 
						|
    if ((ulonglong) (res->length() - length + res2->length()) >
 | 
						|
        (ulonglong) thd->variables.max_allowed_packet)
 | 
						|
    {
 | 
						|
      push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
 | 
						|
                          ER_WARN_ALLOWED_PACKET_OVERFLOWED,
 | 
						|
                          ER_THD(thd, ER_WARN_ALLOWED_PACKET_OVERFLOWED),
 | 
						|
                          func_name(), thd->variables.max_allowed_packet);
 | 
						|
      goto null;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  res=copy_if_not_alloced(str,res,res->length());
 | 
						|
  res->replace((uint32) start,(uint32) length,*res2);
 | 
						|
  return res;
 | 
						|
null:
 | 
						|
  null_value=1;
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool Item_func_insert::fix_length_and_dec(THD *thd)
 | 
						|
{
 | 
						|
  ulonglong char_length;
 | 
						|
 | 
						|
  // Handle character set for args[0] and args[3].
 | 
						|
  if (agg_arg_charsets_for_string_result(collation, args, 2, 3))
 | 
						|
    return TRUE;
 | 
						|
  if (collation.collation == &my_charset_bin)
 | 
						|
    char_length= (ulonglong) args[0]->max_length +
 | 
						|
                 (ulonglong) args[3]->max_length;
 | 
						|
  else
 | 
						|
    char_length= ((ulonglong) args[0]->max_char_length() +
 | 
						|
                  (ulonglong) args[3]->max_char_length());
 | 
						|
  fix_char_length_ulonglong(char_length);
 | 
						|
  return FALSE;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
String *Item_str_conv::val_str(String *str)
 | 
						|
{
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
  String *res= args[0]->val_str(&tmp_value);
 | 
						|
 | 
						|
  if (!res)
 | 
						|
  {
 | 
						|
  err:
 | 
						|
    null_value= true;
 | 
						|
    return nullptr;
 | 
						|
  }
 | 
						|
 | 
						|
  size_t alloced_length= res->length() * multiply, len;
 | 
						|
 | 
						|
  if (str->alloc((alloced_length)))
 | 
						|
    goto err;
 | 
						|
 | 
						|
  null_value= false;
 | 
						|
  len= converter(collation.collation, (char*) res->ptr(), res->length(),
 | 
						|
                                      (char*) str->ptr(), alloced_length);
 | 
						|
  DBUG_ASSERT(len <= alloced_length);
 | 
						|
  str->set_charset(collation.collation);
 | 
						|
  str->length(len);
 | 
						|
  return str;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool Item_func_lcase::fix_length_and_dec(THD *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;
 | 
						|
  fix_char_length_ulonglong((ulonglong) args[0]->max_char_length() * multiply);
 | 
						|
  return FALSE;
 | 
						|
}
 | 
						|
 | 
						|
bool Item_func_ucase::fix_length_and_dec(THD *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;
 | 
						|
  fix_char_length_ulonglong((ulonglong) args[0]->max_char_length() * multiply);
 | 
						|
  return FALSE;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool Item_func_left::hash_not_null(Hasher *hasher)
 | 
						|
{
 | 
						|
  StringBuffer<STRING_BUFFER_USUAL_SIZE> buf;
 | 
						|
  String *str= val_str(&buf);
 | 
						|
  DBUG_ASSERT((str == NULL) == null_value);
 | 
						|
  if (!str)
 | 
						|
    return true;
 | 
						|
  hasher->add(collation.collation, str->ptr(), str->length());
 | 
						|
  return false;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
String *Item_func_left::val_str(String *str)
 | 
						|
{
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
  String *res= args[0]->val_str(str);
 | 
						|
 | 
						|
  /* must be longlong to avoid truncation */
 | 
						|
  longlong length= args[1]->val_int();
 | 
						|
  uint 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(str);
 | 
						|
  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]->can_eval_in_optimize())
 | 
						|
  {
 | 
						|
    bool neg;
 | 
						|
    uint32 length= max_length_for_string(args[1], &neg);
 | 
						|
    set_if_smaller(char_length, length);
 | 
						|
  }
 | 
						|
  fix_char_length(char_length);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool Item_func_left::fix_length_and_dec(THD *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());
 | 
						|
  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(str); /* purecov: inspected */
 | 
						|
 | 
						|
  if (res->length() <= (ulonglong) length)
 | 
						|
    return res; /* purecov: inspected */
 | 
						|
 | 
						|
  uint 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::fix_length_and_dec(THD *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());
 | 
						|
  String *res  = args[0]->val_str(str);
 | 
						|
  /* must be longlong to avoid truncation */
 | 
						|
  longlong start= get_position();
 | 
						|
  /* 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(str);
 | 
						|
 | 
						|
  /* 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(str);
 | 
						|
 | 
						|
  start= ((start < 0) ? res->numchars() + start : start - 1);
 | 
						|
  start= res->charpos((int) start);
 | 
						|
  if ((start < 0) || ((uint) start + 1 > res->length()))
 | 
						|
    return make_empty_result(str);
 | 
						|
 | 
						|
  length= res->charpos((int) length, (uint32) start);
 | 
						|
  tmp_length= res->length() - start;
 | 
						|
  length= MY_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::fix_length_and_dec(THD *thd)
 | 
						|
{
 | 
						|
  max_length=args[0]->max_length;
 | 
						|
 | 
						|
  if (agg_arg_charsets_for_string_result(collation, args, 1))
 | 
						|
    return TRUE;
 | 
						|
  DBUG_ASSERT(collation.collation != NULL);
 | 
						|
  if (args[1]->const_item())
 | 
						|
  {
 | 
						|
    int32 start= (int32) get_position();
 | 
						|
    if (args[1]->null_value)
 | 
						|
      max_length= 0;
 | 
						|
    else if (start < 0)
 | 
						|
      max_length= ((uint)(-start) > max_length) ? 0 : (uint)(-start);
 | 
						|
    else
 | 
						|
      max_length-= MY_MIN((uint)(start - 1), max_length);
 | 
						|
  }
 | 
						|
  if (arg_count == 3 && args[2]->const_item())
 | 
						|
  {
 | 
						|
    longlong length= args[2]->val_int();
 | 
						|
    if (args[2]->null_value || (length <= 0 && !args[2]->unsigned_flag))
 | 
						|
      max_length=0; /* purecov: inspected */
 | 
						|
    else if (length < UINT32_MAX)
 | 
						|
      set_if_smaller(max_length, (uint32) length);
 | 
						|
  }
 | 
						|
  max_length*= collation.collation->mbmaxlen;
 | 
						|
  return FALSE;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool Item_func_substr_index::fix_length_and_dec(THD *thd)
 | 
						|
{
 | 
						|
  if (agg_arg_charsets_for_string_result_with_comparison(collation, args, 2))
 | 
						|
    return TRUE;
 | 
						|
  fix_char_length(args[0]->max_char_length());
 | 
						|
  return FALSE;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
String *Item_func_substr_index::val_str(String *str)
 | 
						|
{
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
  char buff[MAX_FIELD_WIDTH];
 | 
						|
  String tmp(buff,sizeof(buff),system_charset_info);
 | 
						|
  String *res= args[0]->val_str(&tmp_value);
 | 
						|
  String *delimiter= args[1]->val_str(&tmp);
 | 
						|
  int32 count= (int32) args[2]->val_int();
 | 
						|
  uint 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;
 | 
						|
  uint delimiter_length= delimiter->length();
 | 
						|
  if (!res->length() || !delimiter_length || !count)
 | 
						|
    return make_empty_result(str);      // Wrong parameters
 | 
						|
 | 
						|
  res->set_charset(collation.collation);
 | 
						|
 | 
						|
#ifdef USE_MB
 | 
						|
  if (res->use_mb())
 | 
						|
  {
 | 
						|
    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;
 | 
						|
    int32 n=0,c=count,pass;
 | 
						|
    uint32 l;
 | 
						|
    for (pass=(count>0);pass<2;++pass)
 | 
						|
    {
 | 
						|
      while (ptr < end)
 | 
						|
      {
 | 
						|
        if (*ptr == *search)
 | 
						|
        {
 | 
						|
	  char *i,*j;
 | 
						|
	  i=(char*) ptr+1; j=(char*) search+1;
 | 
						|
	  while (j != search_end)
 | 
						|
	    if (*i++ != *j++) goto skip;
 | 
						|
	  if (pass==0) ++n;
 | 
						|
	  else if (!--c) break;
 | 
						|
	  ptr+= delimiter_length;
 | 
						|
	  continue;
 | 
						|
	}
 | 
						|
    skip:
 | 
						|
        if ((l=my_ismbchar(res->charset(), ptr,strend))) ptr+=l;
 | 
						|
        else ++ptr;
 | 
						|
      } /* either not found or got total number when count<0 */
 | 
						|
      if (pass == 0) /* count<0 */
 | 
						|
      {
 | 
						|
        c+=n+1;
 | 
						|
        if (c<=0)
 | 
						|
        {
 | 
						|
          str->copy(res->ptr(), res->length(), collation.collation);
 | 
						|
          return str; // not found, return the original string
 | 
						|
        }
 | 
						|
        ptr=res->ptr();
 | 
						|
      }
 | 
						|
      else
 | 
						|
      {
 | 
						|
        if (c)
 | 
						|
        {
 | 
						|
          str->copy(res->ptr(), res->length(), collation.collation);
 | 
						|
          return str; // not found, return the original string
 | 
						|
        }
 | 
						|
        if (count>0) /* return left part */
 | 
						|
        {
 | 
						|
          str->copy(res->ptr(), (uint32) (ptr-res->ptr()), collation.collation);
 | 
						|
          return str;
 | 
						|
        }
 | 
						|
        else /* return right part */
 | 
						|
        {
 | 
						|
          ptr+= delimiter_length;
 | 
						|
          str->copy(res->ptr() + (ptr-res->ptr()), (uint32) (strend - ptr),
 | 
						|
                    collation.collation);
 | 
						|
          return str;
 | 
						|
        }
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
  else
 | 
						|
#endif /* USE_MB */
 | 
						|
  {
 | 
						|
    if (count > 0)
 | 
						|
    {					// start counting from the beginning
 | 
						|
      for (offset=0; ; offset+= delimiter_length)
 | 
						|
      {
 | 
						|
        if ((int) (offset= res->strstr(*delimiter, offset)) < 0)
 | 
						|
        {
 | 
						|
          str->copy(res->ptr(), res->length(), collation.collation);
 | 
						|
          return str; // not found, return the original string
 | 
						|
        }
 | 
						|
        if (!--count)
 | 
						|
        {
 | 
						|
          str->copy(res->ptr(), offset, collation.collation);
 | 
						|
          return str;
 | 
						|
        }
 | 
						|
      }
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
      /*
 | 
						|
        Negative index, start counting at the end
 | 
						|
      */
 | 
						|
      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 ((int) (offset= res->strrstr(*delimiter, offset)) < 0)
 | 
						|
        {
 | 
						|
          str->copy(res->ptr(), res->length(), collation.collation);
 | 
						|
          return str; // not found, return the original string
 | 
						|
        }
 | 
						|
        /*
 | 
						|
          At this point, we've searched for the substring
 | 
						|
          the number of times as supplied by the index value
 | 
						|
        */
 | 
						|
        if (!++count)
 | 
						|
        {
 | 
						|
          offset+= delimiter_length;
 | 
						|
          str->copy(res->ptr() + offset, res->length() - offset,
 | 
						|
                    collation.collation);
 | 
						|
          return str;
 | 
						|
        }
 | 
						|
      }
 | 
						|
      if (count)
 | 
						|
      {
 | 
						|
        str->copy(res->ptr(), res->length(), collation.collation);
 | 
						|
        return str; // not found, return the original string
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
  DBUG_ASSERT(0);
 | 
						|
  return NULL;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
** 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_ltrim::val_str(String *str)
 | 
						|
{
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
  char buff[MAX_FIELD_WIDTH], *ptr, *end;
 | 
						|
  String tmp(buff,sizeof(buff),system_charset_info);
 | 
						|
  String *res, *remove_str;
 | 
						|
  uint UNINIT_VAR(remove_length);
 | 
						|
 | 
						|
  res= args[0]->val_str(str);
 | 
						|
  if ((null_value=args[0]->null_value))
 | 
						|
    return 0;
 | 
						|
  remove_str= &remove;                          /* Default value. */
 | 
						|
  if (arg_count == 2)
 | 
						|
  {
 | 
						|
    remove_str= args[1]->val_str(&tmp);
 | 
						|
    if ((null_value= args[1]->null_value))
 | 
						|
      return 0;
 | 
						|
  }
 | 
						|
 | 
						|
  if ((remove_length= remove_str->length()) == 0 ||
 | 
						|
      remove_length > res->length())
 | 
						|
    return non_trimmed_value(res);
 | 
						|
 | 
						|
  ptr= (char*) res->ptr();
 | 
						|
  end= ptr+res->length();
 | 
						|
  if (remove_length == 1)
 | 
						|
  {
 | 
						|
    char chr=(*remove_str)[0];
 | 
						|
    while (ptr != end && *ptr == chr)
 | 
						|
      ptr++;
 | 
						|
  }
 | 
						|
  else
 | 
						|
  {
 | 
						|
    const char *r_ptr=remove_str->ptr();
 | 
						|
    end-=remove_length;
 | 
						|
    while (ptr <= end && !memcmp(ptr, r_ptr, remove_length))
 | 
						|
      ptr+=remove_length;
 | 
						|
    end+=remove_length;
 | 
						|
  }
 | 
						|
  if (ptr == res->ptr())
 | 
						|
    return non_trimmed_value(res);
 | 
						|
  return trimmed_value(res, (uint32) (ptr - res->ptr()), (uint32) (end - ptr));
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
String *Item_func_rtrim::val_str(String *str)
 | 
						|
{
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
  char buff[MAX_FIELD_WIDTH], *ptr, *end;
 | 
						|
  String tmp(buff, sizeof(buff), system_charset_info);
 | 
						|
  String *res, *remove_str;
 | 
						|
  uint UNINIT_VAR(remove_length);
 | 
						|
 | 
						|
  res= args[0]->val_str(str);
 | 
						|
  if ((null_value=args[0]->null_value))
 | 
						|
    return 0;
 | 
						|
  remove_str= &remove;                          /* Default value. */
 | 
						|
  if (arg_count == 2)
 | 
						|
  {
 | 
						|
    remove_str= args[1]->val_str(&tmp);
 | 
						|
    if ((null_value= args[1]->null_value))
 | 
						|
      return 0;
 | 
						|
  }
 | 
						|
 | 
						|
  if ((remove_length= remove_str->length()) == 0 ||
 | 
						|
      remove_length > res->length())
 | 
						|
    return non_trimmed_value(res);
 | 
						|
 | 
						|
  ptr= (char*) res->ptr();
 | 
						|
  end= ptr+res->length();
 | 
						|
#ifdef USE_MB
 | 
						|
  char *p=ptr;
 | 
						|
  uint32 l;
 | 
						|
#endif
 | 
						|
  if (remove_length == 1)
 | 
						|
  {
 | 
						|
    char chr=(*remove_str)[0];
 | 
						|
#ifdef USE_MB
 | 
						|
    if (collation.collation->use_mb())
 | 
						|
    {
 | 
						|
      while (ptr < end)
 | 
						|
      {
 | 
						|
	if ((l= my_ismbchar(collation.collation, ptr, end))) ptr+= l, p=ptr;
 | 
						|
	else ++ptr;
 | 
						|
      }
 | 
						|
      ptr=p;
 | 
						|
    }
 | 
						|
#endif
 | 
						|
    while (ptr != end  && end[-1] == chr)
 | 
						|
      end--;
 | 
						|
  }
 | 
						|
  else
 | 
						|
  {
 | 
						|
    const char *r_ptr=remove_str->ptr();
 | 
						|
#ifdef USE_MB
 | 
						|
    if (collation.collation->use_mb())
 | 
						|
    {
 | 
						|
  loop:
 | 
						|
      while (ptr + remove_length < end)
 | 
						|
      {
 | 
						|
	if ((l= my_ismbchar(collation.collation, ptr, end))) ptr+= l;
 | 
						|
	else ++ptr;
 | 
						|
      }
 | 
						|
      if (ptr + remove_length == end && !memcmp(ptr,r_ptr,remove_length))
 | 
						|
      {
 | 
						|
	end-=remove_length;
 | 
						|
	ptr=p;
 | 
						|
	goto loop;
 | 
						|
      }
 | 
						|
    }
 | 
						|
    else
 | 
						|
#endif /* USE_MB */
 | 
						|
    {
 | 
						|
      while (ptr + remove_length <= end &&
 | 
						|
	     !memcmp(end-remove_length, r_ptr, remove_length))
 | 
						|
	end-=remove_length;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  if (end == res->ptr()+res->length())
 | 
						|
    return non_trimmed_value(res);
 | 
						|
  return trimmed_value(res, 0, (uint32) (end - res->ptr()));
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
String *Item_func_trim::val_str(String *str)
 | 
						|
{
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
  char buff[MAX_FIELD_WIDTH], *ptr, *end;
 | 
						|
  const char *r_ptr;
 | 
						|
  String tmp(buff, sizeof(buff), system_charset_info);
 | 
						|
  String *res, *remove_str;
 | 
						|
  uint UNINIT_VAR(remove_length);
 | 
						|
 | 
						|
  res= args[0]->val_str(str);
 | 
						|
  if ((null_value=args[0]->null_value))
 | 
						|
    return 0;
 | 
						|
  remove_str= &remove;                          /* Default value. */
 | 
						|
  if (arg_count == 2)
 | 
						|
  {
 | 
						|
    remove_str= args[1]->val_str(&tmp);
 | 
						|
    if ((null_value= args[1]->null_value))
 | 
						|
      return 0;
 | 
						|
  }
 | 
						|
 | 
						|
  if ((remove_length= remove_str->length()) == 0 ||
 | 
						|
      remove_length > res->length())
 | 
						|
    return non_trimmed_value(res);
 | 
						|
 | 
						|
  ptr= (char*) res->ptr();
 | 
						|
  end= ptr+res->length();
 | 
						|
  r_ptr= remove_str->ptr();
 | 
						|
  while (ptr+remove_length <= end && !memcmp(ptr,r_ptr,remove_length))
 | 
						|
    ptr+=remove_length;
 | 
						|
#ifdef USE_MB
 | 
						|
  if (collation.collation->use_mb())
 | 
						|
  {
 | 
						|
    char *p=ptr;
 | 
						|
    uint32 l;
 | 
						|
 loop:
 | 
						|
    while (ptr + remove_length < end)
 | 
						|
    {
 | 
						|
      if ((l= my_ismbchar(collation.collation, ptr, end)))
 | 
						|
        ptr+= l;
 | 
						|
      else
 | 
						|
        ++ptr;
 | 
						|
    }
 | 
						|
    if (ptr + remove_length == end && !memcmp(ptr,r_ptr,remove_length))
 | 
						|
    {
 | 
						|
      end-=remove_length;
 | 
						|
      ptr=p;
 | 
						|
      goto loop;
 | 
						|
    }
 | 
						|
    ptr=p;
 | 
						|
  }
 | 
						|
  else
 | 
						|
#endif /* USE_MB */
 | 
						|
  {
 | 
						|
    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 non_trimmed_value(res);
 | 
						|
  return trimmed_value(res, (uint32) (ptr - res->ptr()), (uint32) (end - ptr));
 | 
						|
}
 | 
						|
 | 
						|
bool Item_func_trim::fix_length_and_dec(THD *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;
 | 
						|
  }
 | 
						|
  fix_char_length(args[0]->max_char_length());
 | 
						|
  return FALSE;
 | 
						|
}
 | 
						|
 | 
						|
void Item_func_trim::print(String *str, enum_query_type query_type)
 | 
						|
{
 | 
						|
  LEX_CSTRING suffix= {STRING_WITH_LEN("_oracle")};
 | 
						|
  if (arg_count == 1)
 | 
						|
  {
 | 
						|
    if (query_type & QT_FOR_FRM)
 | 
						|
    {
 | 
						|
      // 10.3 downgrade compatibility for FRM
 | 
						|
      str->append(func_name_cstring());
 | 
						|
      if (schema() == &oracle_schema_ref)
 | 
						|
        str->append(suffix);
 | 
						|
    }
 | 
						|
    else
 | 
						|
      print_sql_mode_qualified_name(str, query_type, func_name_cstring());
 | 
						|
    print_args_parenthesized(str, query_type);
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  if (query_type & QT_FOR_FRM)
 | 
						|
  {
 | 
						|
    // 10.3 downgrade compatibility for FRM
 | 
						|
    str->append(Item_func_trim::func_name_cstring());
 | 
						|
    if (schema() == &oracle_schema_ref)
 | 
						|
      str->append(suffix);
 | 
						|
  }
 | 
						|
  else
 | 
						|
    print_sql_mode_qualified_name(str, query_type, Item_func_trim::func_name_cstring());
 | 
						|
  str->append('(');
 | 
						|
  str->append(mode_name());
 | 
						|
  str->append(' ');
 | 
						|
  args[1]->print(str, query_type);
 | 
						|
  str->append(STRING_WITH_LEN(" from "));
 | 
						|
  args[0]->print(str, query_type);
 | 
						|
  str->append(')');
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
  RTRIM(expr)
 | 
						|
  TRIM(TRAILING ' ' FROM expr)
 | 
						|
  remove argument's soft dependency on PAD_CHAR_TO_FULL_LENGTH:
 | 
						|
*/
 | 
						|
Sql_mode_dependency Item_func_trim::value_depends_on_sql_mode() const
 | 
						|
{
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
  if (arg_count == 1) // RTRIM(expr)
 | 
						|
    return (args[0]->value_depends_on_sql_mode() &
 | 
						|
            Sql_mode_dependency(~0, ~MODE_PAD_CHAR_TO_FULL_LENGTH)).
 | 
						|
           soft_to_hard();
 | 
						|
  // TRIM(... FROM expr)
 | 
						|
  DBUG_ASSERT(arg_count == 2);
 | 
						|
  if (!args[1]->value_depends_on_sql_mode_const_item())
 | 
						|
    return Item_func::value_depends_on_sql_mode();
 | 
						|
  StringBuffer<64> trimstrbuf;
 | 
						|
  String *trimstr= args[1]->val_str(&trimstrbuf);
 | 
						|
  if (!trimstr)
 | 
						|
    return Sql_mode_dependency();                  // will return NULL
 | 
						|
  if (trimstr->length() == 0)
 | 
						|
    return Item_func::value_depends_on_sql_mode(); // will trim nothing
 | 
						|
  if (trimstr->lengthsp() != 0)
 | 
						|
    return Item_func::value_depends_on_sql_mode(); // will trim not only spaces
 | 
						|
  if (trimstr->length() > trimstr->charset()->mbminlen ||
 | 
						|
      trimstr->numchars() > 1)
 | 
						|
    return Item_func::value_depends_on_sql_mode(); // more than one space
 | 
						|
  // TRIM(TRAILING ' ' FROM expr)
 | 
						|
  return ((args[0]->value_depends_on_sql_mode() |
 | 
						|
           args[1]->value_depends_on_sql_mode()) &
 | 
						|
          Sql_mode_dependency(~0, ~MODE_PAD_CHAR_TO_FULL_LENGTH)).
 | 
						|
         soft_to_hard();
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* Item_func_password */
 | 
						|
 | 
						|
bool Item_func_password::fix_fields(THD *thd, Item **ref)
 | 
						|
{
 | 
						|
  if (deflt)
 | 
						|
    alg= (thd->variables.old_passwords ? OLD : NEW);
 | 
						|
  return Item_str_ascii_func::fix_fields(thd, ref);
 | 
						|
}
 | 
						|
 | 
						|
String *Item_func_password::val_str_ascii(String *str)
 | 
						|
{
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
  String *res= args[0]->val_str(str); 
 | 
						|
  switch (alg){
 | 
						|
  case NEW:
 | 
						|
    if (args[0]->null_value || res->length() == 0)
 | 
						|
      return make_empty_result(str);
 | 
						|
    my_make_scrambled_password(tmp_value, res->ptr(), res->length());
 | 
						|
    str->copy(tmp_value, SCRAMBLED_PASSWORD_CHAR_LENGTH, &my_charset_latin1);
 | 
						|
    break;
 | 
						|
  case OLD:
 | 
						|
    if ((null_value=args[0]->null_value))
 | 
						|
      return 0;
 | 
						|
    if (res->length() == 0)
 | 
						|
      return make_empty_result(str);
 | 
						|
    my_make_scrambled_password_323(tmp_value, res->ptr(), res->length());
 | 
						|
    str->copy(tmp_value, SCRAMBLED_PASSWORD_CHAR_LENGTH_323, &my_charset_latin1);
 | 
						|
    break;
 | 
						|
  default:
 | 
						|
    DBUG_ASSERT(0);
 | 
						|
  }
 | 
						|
  return str;
 | 
						|
}
 | 
						|
 | 
						|
char *Item_func_password::alloc(THD *thd, const char *password,
 | 
						|
                                size_t pass_len, enum PW_Alg al)
 | 
						|
{
 | 
						|
  char *buff= (char *) thd->alloc((al==NEW)?
 | 
						|
                                  SCRAMBLED_PASSWORD_CHAR_LENGTH + 1:
 | 
						|
                                  SCRAMBLED_PASSWORD_CHAR_LENGTH_323 + 1);
 | 
						|
  if (!buff)
 | 
						|
    return NULL;
 | 
						|
 | 
						|
  switch (al) {
 | 
						|
  case NEW:
 | 
						|
    my_make_scrambled_password(buff, password, pass_len);
 | 
						|
    break;
 | 
						|
  case OLD:
 | 
						|
    my_make_scrambled_password_323(buff, password, pass_len);
 | 
						|
    break;
 | 
						|
  default:
 | 
						|
    DBUG_ASSERT(0);
 | 
						|
  }
 | 
						|
  return buff;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
 | 
						|
#define bin_to_ascii(c) ((c)>=38?((c)-38+'a'):(c)>=12?((c)-12+'A'):(c)+'.')
 | 
						|
 | 
						|
String *Item_func_encrypt::val_str(String *str)
 | 
						|
{
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
 | 
						|
#ifdef HAVE_CRYPT
 | 
						|
  String *res  =args[0]->val_str(str);
 | 
						|
 | 
						|
  char salt[3],*salt_ptr;
 | 
						|
  if ((null_value=args[0]->null_value))
 | 
						|
    return 0;
 | 
						|
  if (res->length() == 0)
 | 
						|
    return make_empty_result(str);
 | 
						|
  if (arg_count == 1)
 | 
						|
  {					// generate random salt
 | 
						|
    time_t timestamp=current_thd->query_start();
 | 
						|
    salt[0] = bin_to_ascii( (ulong) timestamp & 0x3f);
 | 
						|
    salt[1] = bin_to_ascii(( (ulong) timestamp >> 5) & 0x3f);
 | 
						|
    salt[2] = 0;
 | 
						|
    salt_ptr=salt;
 | 
						|
  }
 | 
						|
  else
 | 
						|
  {					// obtain salt from the first two bytes
 | 
						|
    String *salt_str=args[1]->val_str(&tmp_value);
 | 
						|
    if ((null_value= (args[1]->null_value || salt_str->length() < 2)))
 | 
						|
      return 0;
 | 
						|
    salt_ptr= salt_str->c_ptr_safe();
 | 
						|
  }
 | 
						|
  mysql_mutex_lock(&LOCK_crypt);
 | 
						|
  char *tmp= crypt(res->c_ptr_safe(),salt_ptr);
 | 
						|
  if (!tmp)
 | 
						|
  {
 | 
						|
    mysql_mutex_unlock(&LOCK_crypt);
 | 
						|
    null_value= 1;
 | 
						|
    return 0;
 | 
						|
  }
 | 
						|
  str->set(tmp, (uint) strlen(tmp), &my_charset_bin);
 | 
						|
  str->copy();
 | 
						|
  mysql_mutex_unlock(&LOCK_crypt);
 | 
						|
  return str;
 | 
						|
#else
 | 
						|
  null_value=1;
 | 
						|
  return 0;
 | 
						|
#endif	/* HAVE_CRYPT */
 | 
						|
}
 | 
						|
 | 
						|
bool Item_func_encode::seed()
 | 
						|
{
 | 
						|
  char buf[80];
 | 
						|
  ulong rand_nr[2];
 | 
						|
  String *key, tmp(buf, sizeof(buf), system_charset_info);
 | 
						|
 | 
						|
  if (!(key= args[1]->val_str(&tmp)))
 | 
						|
    return TRUE;
 | 
						|
 | 
						|
  hash_password(rand_nr, key->ptr(), key->length());
 | 
						|
  sql_crypt.init(rand_nr);
 | 
						|
 | 
						|
  return FALSE;
 | 
						|
}
 | 
						|
 | 
						|
bool Item_func_encode::fix_length_and_dec(THD *thd)
 | 
						|
{
 | 
						|
  max_length=args[0]->max_length;
 | 
						|
  base_flags|= ((args[0]->base_flags | args[1]->base_flags) &
 | 
						|
                item_base_t::MAYBE_NULL);
 | 
						|
  collation.set(&my_charset_bin);
 | 
						|
  /* Precompute the seed state if the item is constant. */
 | 
						|
  seeded= args[1]->const_item() &&
 | 
						|
          (args[1]->result_type() == STRING_RESULT) && !seed();
 | 
						|
  return FALSE;
 | 
						|
}
 | 
						|
 | 
						|
String *Item_func_encode::val_str(String *str)
 | 
						|
{
 | 
						|
  String *res;
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
 | 
						|
  if (!(res=args[0]->val_str(str)))
 | 
						|
  {
 | 
						|
    null_value= 1;
 | 
						|
    return NULL;
 | 
						|
  }
 | 
						|
 | 
						|
  if (!seeded && seed())
 | 
						|
  {
 | 
						|
    null_value= 1;
 | 
						|
    return NULL;
 | 
						|
  }
 | 
						|
 | 
						|
  null_value= 0;
 | 
						|
  res= copy_if_not_alloced(str, res, res->length());
 | 
						|
  crypto_transform(res);
 | 
						|
  sql_crypt.reinit();
 | 
						|
 | 
						|
  return res;
 | 
						|
}
 | 
						|
 | 
						|
void Item_func_encode::crypto_transform(String *res)
 | 
						|
{
 | 
						|
  sql_crypt.encode((char*) res->ptr(),res->length());
 | 
						|
  res->set_charset(&my_charset_bin);
 | 
						|
}
 | 
						|
 | 
						|
void Item_func_decode::crypto_transform(String *res)
 | 
						|
{
 | 
						|
  sql_crypt.decode((char*) res->ptr(),res->length());
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
String *Item_func_database::val_str(String *str)
 | 
						|
{
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
  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);
 | 
						|
  null_value= 0;
 | 
						|
  return str;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
String *Item_func_sqlerrm::val_str(String *str)
 | 
						|
{
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
  DBUG_ASSERT(!null_value);
 | 
						|
  Diagnostics_area::Sql_condition_iterator it=
 | 
						|
    current_thd->get_stmt_da()->sql_conditions();
 | 
						|
  const Sql_condition *err;
 | 
						|
  if ((err= it++))
 | 
						|
  {
 | 
						|
    str->copy(err->get_message_text(), err->get_message_octet_length(),
 | 
						|
              system_charset_info);
 | 
						|
    return str;
 | 
						|
  }
 | 
						|
  str->copy(STRING_WITH_LEN("normal, successful completion"),
 | 
						|
            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());
 | 
						|
 | 
						|
  // For system threads (e.g. replication SQL thread) user may be empty
 | 
						|
  if (user)
 | 
						|
  {
 | 
						|
    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, (char*)str_value.ptr(), (uint) res_length,
 | 
						|
                                  "%s@%s", user, host);
 | 
						|
    str_value.length((uint) res_length);
 | 
						|
    str_value.mark_as_const();
 | 
						|
  }
 | 
						|
  return FALSE;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
Item *Item_func_sysconst::safe_charset_converter(THD *thd, CHARSET_INFO *tocs)
 | 
						|
{
 | 
						|
  /*
 | 
						|
   During view or prepared statement creation, the item should not
 | 
						|
   make use of const_charset_converter as it would imply substitution
 | 
						|
   with constant items which is not correct. Functions can have different
 | 
						|
   values during view creation and view execution based on context.
 | 
						|
 | 
						|
   Return the identical item during view creation and prepare.
 | 
						|
  */
 | 
						|
  if (thd->lex->is_ps_or_view_context_analysis())
 | 
						|
    return this;
 | 
						|
  return const_charset_converter(thd, tocs, true, fully_qualified_func_name());
 | 
						|
}
 | 
						|
 | 
						|
bool Item_func_sysconst::const_item() const
 | 
						|
{
 | 
						|
  if (current_thd->lex->is_ps_or_view_context_analysis())
 | 
						|
    return false;
 | 
						|
  return true;
 | 
						|
}
 | 
						|
 | 
						|
bool Item_func_user::fix_fields(THD *thd, Item **ref)
 | 
						|
{
 | 
						|
  return (Item_func_sysconst::fix_fields(thd, ref) ||
 | 
						|
          init(thd->main_security_ctx.user,
 | 
						|
               thd->main_security_ctx.host_or_ip));
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
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 && context->security_ctx
 | 
						|
                          ? context->security_ctx : thd->security_ctx;
 | 
						|
  return init(ctx->priv_user, ctx->priv_host);
 | 
						|
}
 | 
						|
 | 
						|
bool Item_func_current_role::fix_fields(THD *thd, Item **ref)
 | 
						|
{
 | 
						|
  if (Item_func_sysconst::fix_fields(thd, ref))
 | 
						|
    return 1;
 | 
						|
 | 
						|
  Security_context *ctx= context && context->security_ctx
 | 
						|
                          ? context->security_ctx : thd->security_ctx;
 | 
						|
  if (ctx->priv_role[0])
 | 
						|
  {
 | 
						|
    if (str_value.copy(ctx->priv_role, strlen(ctx->priv_role),
 | 
						|
                       system_charset_info))
 | 
						|
      return 1;
 | 
						|
    str_value.mark_as_const();
 | 
						|
    null_value= 0;
 | 
						|
    base_flags&= ~item_base_t::MAYBE_NULL;
 | 
						|
    return 0;
 | 
						|
  }
 | 
						|
  null_value= 1;
 | 
						|
  set_maybe_null();
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
bool Item_func_soundex::fix_length_and_dec(THD *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);
 | 
						|
  fix_char_length(char_length);
 | 
						|
  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;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
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());
 | 
						|
  String *res= args[0]->val_str(&tmp_value);
 | 
						|
  char last_ch,ch;
 | 
						|
  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 (str->alloc(MY_MAX(res->length(), 4 * cs->mbminlen)))
 | 
						|
    return &tmp_value; /* purecov: inspected */
 | 
						|
  str->set_charset(collation.collation);
 | 
						|
  char *to= (char *) str->ptr();
 | 
						|
  char *to_end= to + str->alloced_length();
 | 
						|
  char *from= (char *) res->ptr(), *end= from + res->length();
 | 
						|
  
 | 
						|
  for ( ; ; ) /* Skip pre-space */
 | 
						|
  {
 | 
						|
    if ((rc= cs->mb_wc(&wc, (uchar*) from, (uchar*) end)) <= 0)
 | 
						|
      return make_empty_result(str); /* EOL or invalid byte sequence */
 | 
						|
 | 
						|
    if (rc == 1 && cs->m_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->wc_mb(wc, (uchar*) to, (uchar*) to_end)) <= 0)
 | 
						|
        {
 | 
						|
          /* Extra safety - should not really happen */
 | 
						|
          DBUG_ASSERT(false);
 | 
						|
          return make_empty_result(str);
 | 
						|
        }
 | 
						|
        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->mb_wc(&wc, (uchar*) from, (uchar*) end)) <= 0)
 | 
						|
      break; /* EOL or invalid byte sequence */
 | 
						|
 | 
						|
    if (rc == 1 && cs->m_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->wc_mb((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->fill(to, nbytes, '0');
 | 
						|
    to+= nbytes;
 | 
						|
  }
 | 
						|
 | 
						|
  str->length((uint) (to - str->ptr()));
 | 
						|
  return str;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Change a number to format '3,333,333,333.000'.
 | 
						|
 | 
						|
  This should be 'internationalized' sometimes.
 | 
						|
*/
 | 
						|
 | 
						|
/*
 | 
						|
  The maximum supported decimal scale:
 | 
						|
  38 - starting from 10.2.1
 | 
						|
  30 - before 10.2.1
 | 
						|
*/
 | 
						|
const int FORMAT_MAX_DECIMALS= 38;
 | 
						|
 | 
						|
 | 
						|
bool Item_func_format::fix_length_and_dec(THD *thd)
 | 
						|
{
 | 
						|
  uint32 char_length= args[0]->type_handler()->Item_decimal_notation_int_digits(args[0]);
 | 
						|
  uint dec= FORMAT_MAX_DECIMALS;
 | 
						|
  /*
 | 
						|
    Format can require one more integer digit if rounding happens:
 | 
						|
      FORMAT(9.9,0) -> '10'
 | 
						|
    Set need_extra_digit_for_rounding to true by default
 | 
						|
    if args[0] has some decimals: if args[1] is not
 | 
						|
    a constant, then format can potentially reduce
 | 
						|
    the number of decimals and round to the next integer.
 | 
						|
  */
 | 
						|
  bool need_extra_digit_for_rounding= args[0]->decimals > 0;
 | 
						|
  if (args[1]->can_eval_in_optimize())
 | 
						|
  {
 | 
						|
    Longlong_hybrid tmp= args[1]->to_longlong_hybrid();
 | 
						|
    if (!args[1]->null_value)
 | 
						|
    {
 | 
						|
      dec= tmp.to_uint(FORMAT_MAX_DECIMALS);
 | 
						|
      need_extra_digit_for_rounding= (dec < args[0]->decimals);
 | 
						|
    }
 | 
						|
  }
 | 
						|
  /*
 | 
						|
    In case of a data type with zero integer digits, e.g. DECIMAL(4,4),
 | 
						|
    we'll print at least one integer digit.
 | 
						|
  */
 | 
						|
  if (need_extra_digit_for_rounding || !char_length)
 | 
						|
    char_length++;
 | 
						|
  uint32 max_sep_count= (char_length / 3) + (dec ? 1 : 0) + /*sign*/1;
 | 
						|
  collation.set(default_charset());
 | 
						|
  fix_char_length(char_length + max_sep_count + dec);
 | 
						|
  if (arg_count == 3)
 | 
						|
    locale= args[2]->basic_const_item() ? args[2]->locale_from_val_str() : NULL;
 | 
						|
  else
 | 
						|
    locale= &my_locale_en_US; /* Two arguments */
 | 
						|
  return FALSE;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  @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)
 | 
						|
{
 | 
						|
  uint32 str_length;
 | 
						|
  /* Number of decimal digits */
 | 
						|
  int dec;
 | 
						|
  /* Number of characters used to represent the decimals, including '.' */
 | 
						|
  uint32 dec_length;
 | 
						|
  const MY_LOCALE *lc;
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
 | 
						|
  dec= (int) args[1]->val_int();
 | 
						|
  if (args[1]->null_value)
 | 
						|
  {
 | 
						|
    null_value=1;
 | 
						|
    return NULL;
 | 
						|
  }
 | 
						|
 | 
						|
  lc= locale ? locale : args[2]->locale_from_val_str();
 | 
						|
 | 
						|
  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)
 | 
						|
  {
 | 
						|
    VDec res(args[0]);
 | 
						|
    if ((null_value= res.is_null()))
 | 
						|
      return 0; /* purecov: inspected */
 | 
						|
    res.to_string_round(str, dec);
 | 
						|
    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_fcvt(nr, dec);
 | 
						|
    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];
 | 
						|
    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 + sizeof(buf);
 | 
						|
    
 | 
						|
    /* 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 */
 | 
						|
    str->copy(dst, buf + sizeof(buf) - dst, &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);
 | 
						|
    ((char*) str->ptr())[str_length - dec_length]= lc->decimal_point;
 | 
						|
  }
 | 
						|
  return str;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool Item_func_elt::fix_length_and_dec(THD *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);
 | 
						|
  }
 | 
						|
  fix_char_length(char_length);
 | 
						|
  set_maybe_null(); // NULL if wrong first arg
 | 
						|
  return FALSE;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
double Item_func_elt::val_real()
 | 
						|
{
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
  uint tmp;
 | 
						|
  null_value=1;
 | 
						|
  if ((tmp=(uint) args[0]->val_int()) == 0 || 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());
 | 
						|
  uint tmp;
 | 
						|
  null_value=1;
 | 
						|
  if ((tmp=(uint) args[0]->val_int()) == 0 || 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());
 | 
						|
  uint tmp;
 | 
						|
  null_value=1;
 | 
						|
  if ((tmp=(uint) args[0]->val_int()) == 0 || 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::fix_length_and_dec(THD *thd)
 | 
						|
{
 | 
						|
  uint32 char_length= arg_count - 2; /* Separators */
 | 
						|
 | 
						|
  if (agg_arg_charsets_for_string_result(collation, args + 1, arg_count - 1))
 | 
						|
    return TRUE;
 | 
						|
  
 | 
						|
  for (uint i=1 ; i < arg_count ; i++)
 | 
						|
    char_length+= args[i]->max_char_length();
 | 
						|
  fix_char_length(char_length);
 | 
						|
  return FALSE;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
String *Item_func_make_set::val_str(String *str)
 | 
						|
{
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
  ulonglong bits;
 | 
						|
  bool first_found=0;
 | 
						|
  Item **ptr=args+1;
 | 
						|
  String *result= make_empty_result(str);
 | 
						|
 | 
						|
  bits=args[0]->val_int();
 | 
						|
  if ((null_value=args[0]->null_value))
 | 
						|
    return NULL;
 | 
						|
 | 
						|
  if (arg_count < 65)
 | 
						|
    bits &= ((ulonglong) 1 << (arg_count-1))-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(str);
 | 
						|
	    result= &tmp_str;
 | 
						|
	  }
 | 
						|
	}
 | 
						|
	else
 | 
						|
	{
 | 
						|
	  if (result != &tmp_str)
 | 
						|
	  {					// Copy data to tmp_str
 | 
						|
	    if (tmp_str.alloc(result->length()+res->length()+1) ||
 | 
						|
		tmp_str.copy(*result))
 | 
						|
              return make_empty_result(str);
 | 
						|
	    result= &tmp_str;
 | 
						|
	  }
 | 
						|
	  if (tmp_str.append(STRING_WITH_LEN(","), &my_charset_bin) || tmp_str.append(*res))
 | 
						|
            return make_empty_result(str);
 | 
						|
	}
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
  return result;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void Item_func_char::print(String *str, enum_query_type query_type)
 | 
						|
{
 | 
						|
  str->append(Item_func_char::func_name_cstring());
 | 
						|
  str->append('(');
 | 
						|
  print_args(str, 0, query_type);
 | 
						|
  if (collation.collation != &my_charset_bin)
 | 
						|
  {
 | 
						|
    str->append(STRING_WITH_LEN(" using "));
 | 
						|
    str->append(collation.collation->cs_name);
 | 
						|
  }
 | 
						|
  str->append(')');
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
String *Item_func_char::val_str(String *str)
 | 
						|
{
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
  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)
 | 
						|
      append_char(str, num);
 | 
						|
  }
 | 
						|
  str->realloc(str->length());			// Add end 0 (for Purify)
 | 
						|
  return check_well_formed_result(str);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void Item_func_char::append_char(String *str, int32 num)
 | 
						|
{
 | 
						|
  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);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
String *Item_func_chr::val_str(String *str)
 | 
						|
{
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
  str->length(0);
 | 
						|
  str->set_charset(collation.collation);
 | 
						|
  int32 num=(int32) args[0]->val_int();
 | 
						|
  if (!args[0]->null_value)
 | 
						|
    append_char(str, num);
 | 
						|
  else
 | 
						|
  {
 | 
						|
    null_value= 1;
 | 
						|
    return 0;
 | 
						|
  }
 | 
						|
  str->realloc(str->length());			// Add end 0 (for Purify)
 | 
						|
  return check_well_formed_result(str);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
inline String* alloc_buffer(String *res,String *str,String *tmp_value,
 | 
						|
			    ulong 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::fix_length_and_dec(THD *thd)
 | 
						|
{
 | 
						|
  if (agg_arg_charsets_for_string_result(collation, args, 1))
 | 
						|
    return TRUE;
 | 
						|
  DBUG_ASSERT(collation.collation != NULL);
 | 
						|
  if (args[1]->can_eval_in_optimize())
 | 
						|
  {
 | 
						|
    bool neg;
 | 
						|
    uint32 length= max_length_for_string(args[1], &neg);
 | 
						|
    ulonglong char_length= (ulonglong) args[0]->max_char_length() * length;
 | 
						|
    fix_char_length_ulonglong(char_length);
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
  max_length= MAX_BLOB_WIDTH;
 | 
						|
  set_maybe_null();
 | 
						|
  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());
 | 
						|
  uint 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)
 | 
						|
    goto err;				// string and/or delim are null
 | 
						|
  null_value= 0;
 | 
						|
 | 
						|
  if (count <= 0 && (count == 0 || !args[1]->unsigned_flag))
 | 
						|
    return make_empty_result(str);
 | 
						|
 | 
						|
  /* 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
 | 
						|
  {
 | 
						|
    THD *thd= current_thd;
 | 
						|
    if (length > thd->variables.max_allowed_packet / (uint) count)
 | 
						|
    {
 | 
						|
      push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
 | 
						|
                          ER_WARN_ALLOWED_PACKET_OVERFLOWED,
 | 
						|
                          ER_THD(thd, ER_WARN_ALLOWED_PACKET_OVERFLOWED),
 | 
						|
                          func_name(), thd->variables.max_allowed_packet);
 | 
						|
      goto err;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  tot_length= length*(uint) count;
 | 
						|
  if (!(res= alloc_buffer(res,str,&tmp_value,tot_length)))
 | 
						|
    goto err;
 | 
						|
 | 
						|
  to=(char*) res->ptr()+length;
 | 
						|
  while (--count)
 | 
						|
  {
 | 
						|
    memcpy(to,res->ptr(),length);
 | 
						|
    to+=length;
 | 
						|
  }
 | 
						|
  return (res);
 | 
						|
 | 
						|
err:
 | 
						|
  null_value=1;
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool Item_func_space::fix_length_and_dec(THD *thd)
 | 
						|
{
 | 
						|
  collation.set(default_charset(), DERIVATION_COERCIBLE, MY_REPERTOIRE_ASCII);
 | 
						|
  if (args[0]->can_eval_in_optimize())
 | 
						|
  {
 | 
						|
    bool neg;
 | 
						|
    fix_char_length_ulonglong(max_length_for_string(args[0], &neg));
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
  max_length= MAX_BLOB_WIDTH;
 | 
						|
  set_maybe_null();
 | 
						|
  return false;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
String *Item_func_space::val_str(String *str)
 | 
						|
{
 | 
						|
  uint tot_length;
 | 
						|
  longlong count= args[0]->val_int();
 | 
						|
  CHARSET_INFO *cs= collation.collation;
 | 
						|
 | 
						|
  if (args[0]->null_value)
 | 
						|
    goto err;				// string and/or delim are null
 | 
						|
  null_value= 0;
 | 
						|
 | 
						|
  if (count <= 0 && (count == 0 || !args[0]->unsigned_flag))
 | 
						|
    return make_empty_result(str);
 | 
						|
  /*
 | 
						|
   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;
 | 
						|
  {
 | 
						|
    THD *thd= current_thd;
 | 
						|
    if (tot_length > thd->variables.max_allowed_packet)
 | 
						|
    {
 | 
						|
      push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
 | 
						|
                          ER_WARN_ALLOWED_PACKET_OVERFLOWED,
 | 
						|
                          ER_THD(thd, ER_WARN_ALLOWED_PACKET_OVERFLOWED),
 | 
						|
                          func_name(),
 | 
						|
                          thd->variables.max_allowed_packet);
 | 
						|
      goto err;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  if (str->alloc(tot_length))
 | 
						|
    goto err;
 | 
						|
  str->length(tot_length);
 | 
						|
  str->set_charset(cs);
 | 
						|
  cs->fill((char*) str->ptr(), tot_length, ' ');
 | 
						|
  return str;
 | 
						|
 | 
						|
err:
 | 
						|
  null_value= 1;
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool Item_func_binlog_gtid_pos::fix_length_and_dec(THD *thd)
 | 
						|
{
 | 
						|
  collation.set(system_charset_info);
 | 
						|
  max_length= MAX_BLOB_WIDTH;
 | 
						|
  set_maybe_null();
 | 
						|
  return FALSE;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
String *Item_func_binlog_gtid_pos::val_str(String *str)
 | 
						|
{
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
#ifndef HAVE_REPLICATION
 | 
						|
  null_value= 0;
 | 
						|
  str->copy("", 0, system_charset_info);
 | 
						|
  return str;
 | 
						|
#else
 | 
						|
  String name_str, *name;
 | 
						|
  longlong pos;
 | 
						|
 | 
						|
  name= args[0]->val_str(&name_str);
 | 
						|
  pos= args[1]->val_int();
 | 
						|
 | 
						|
  if (args[0]->null_value || args[1]->null_value)
 | 
						|
    goto err;
 | 
						|
 | 
						|
  if (pos < 0 || pos > (longlong) UINT_MAX32)
 | 
						|
    goto err;
 | 
						|
 | 
						|
  if (gtid_state_from_binlog_pos(name->c_ptr_safe(), (uint32)pos, str))
 | 
						|
    goto err;
 | 
						|
  null_value= 0;
 | 
						|
  return str;
 | 
						|
 | 
						|
err:
 | 
						|
  null_value= 1;
 | 
						|
  return NULL;
 | 
						|
#endif  /* !HAVE_REPLICATION */
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
static String *default_pad_str(String *pad_str, CHARSET_INFO *collation)
 | 
						|
{
 | 
						|
  pad_str->set_charset(collation);
 | 
						|
  pad_str->length(0);
 | 
						|
  pad_str->append(" ", 1);
 | 
						|
  return pad_str;
 | 
						|
}
 | 
						|
 | 
						|
bool Item_func_pad::fix_length_and_dec(THD *thd)
 | 
						|
{
 | 
						|
  if (arg_count == 3)
 | 
						|
  {
 | 
						|
    String *str;
 | 
						|
    if (!args[2]->basic_const_item() || !(str= args[2]->val_str(&pad_str)) ||
 | 
						|
        !str->length())
 | 
						|
      set_maybe_null();
 | 
						|
    // Handle character set for args[0] and args[2].
 | 
						|
    if (agg_arg_charsets_for_string_result(collation, &args[0], 2, 2))
 | 
						|
      return TRUE;
 | 
						|
  }
 | 
						|
  else
 | 
						|
  {
 | 
						|
    if (agg_arg_charsets_for_string_result(collation, &args[0], 1, 1))
 | 
						|
      return TRUE;
 | 
						|
    default_pad_str(&pad_str, collation.collation);
 | 
						|
  }
 | 
						|
 | 
						|
  DBUG_ASSERT(collation.collation->mbmaxlen > 0);
 | 
						|
  if (args[1]->can_eval_in_optimize())
 | 
						|
  {
 | 
						|
    bool neg;
 | 
						|
    fix_char_length_ulonglong(max_length_for_string(args[1], &neg));
 | 
						|
    if (neg)
 | 
						|
      set_maybe_null();
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
  max_length= MAX_BLOB_WIDTH;
 | 
						|
  set_maybe_null();
 | 
						|
  return false;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
  PAD(expr,length,' ')
 | 
						|
  removes argument's soft dependency on PAD_CHAR_TO_FULL_LENGTH if the result
 | 
						|
  is longer than the argument's maximim possible length.
 | 
						|
*/
 | 
						|
Sql_mode_dependency Item_func_rpad::value_depends_on_sql_mode() const
 | 
						|
{
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
  DBUG_ASSERT(arg_count >= 2);
 | 
						|
  if (!args[1]->value_depends_on_sql_mode_const_item() ||
 | 
						|
          (arg_count == 3 && !args[2]->value_depends_on_sql_mode_const_item()))
 | 
						|
    return Item_func::value_depends_on_sql_mode();
 | 
						|
  Longlong_hybrid len= args[1]->to_longlong_hybrid();
 | 
						|
  if (args[1]->null_value || len.neg())
 | 
						|
    return Sql_mode_dependency();                  // will return NULL
 | 
						|
  if (len.abs() > 0 && len.abs() < args[0]->max_char_length())
 | 
						|
    return Item_func::value_depends_on_sql_mode();
 | 
						|
  StringBuffer<64> padstrbuf;
 | 
						|
  String *padstr= arg_count == 3 ? args[2]->val_str(&padstrbuf) :
 | 
						|
                               default_pad_str(&padstrbuf, collation.collation);
 | 
						|
  if (!padstr || !padstr->length())
 | 
						|
    return Sql_mode_dependency();                  // will return NULL
 | 
						|
  if (padstr->lengthsp() != 0)
 | 
						|
    return Item_func::value_depends_on_sql_mode(); // will pad not only spaces
 | 
						|
  // RPAD(expr, length, ' ')  -- with a long enough length
 | 
						|
  return ((args[0]->value_depends_on_sql_mode() |
 | 
						|
           args[1]->value_depends_on_sql_mode()) &
 | 
						|
          Sql_mode_dependency(~0, ~MODE_PAD_CHAR_TO_FULL_LENGTH)).
 | 
						|
         soft_to_hard();
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
 | 
						|
String *Item_func_rpad::val_str(String *str)
 | 
						|
{
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
  uint32 res_byte_length,res_char_length,pad_char_length,pad_byte_length;
 | 
						|
  char *to;
 | 
						|
  const char *ptr_pad;
 | 
						|
  /* must be longlong to avoid truncation */
 | 
						|
  longlong count= args[1]->val_int();
 | 
						|
  longlong byte_count;
 | 
						|
  String *res= args[0]->val_str(str);
 | 
						|
  String *rpad= arg_count == 2 ? &pad_str : args[2]->val_str(&pad_str);
 | 
						|
 | 
						|
  if (!res || args[1]->null_value || !rpad || 
 | 
						|
      ((count < 0) && !args[1]->unsigned_flag))
 | 
						|
    goto err;
 | 
						|
 | 
						|
  null_value=0;
 | 
						|
 | 
						|
  if (count == 0)
 | 
						|
    return make_empty_result(str);
 | 
						|
 | 
						|
  /* 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 (intentionally) 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 (count <= (res_char_length= res->numchars()))
 | 
						|
  {						// String to pad is big enough
 | 
						|
    res->length(res->charpos((int) count));	// Shorten result if longer
 | 
						|
    return (res);
 | 
						|
  }
 | 
						|
 | 
						|
  byte_count= count * collation.collation->mbmaxlen;
 | 
						|
  {
 | 
						|
    THD *thd= current_thd;
 | 
						|
    if ((ulonglong) byte_count > thd->variables.max_allowed_packet)
 | 
						|
    {
 | 
						|
      push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
 | 
						|
                          ER_WARN_ALLOWED_PACKET_OVERFLOWED,
 | 
						|
                          ER_THD(thd, ER_WARN_ALLOWED_PACKET_OVERFLOWED),
 | 
						|
                          func_name(), thd->variables.max_allowed_packet);
 | 
						|
      goto err;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  if (arg_count == 3)
 | 
						|
  {
 | 
						|
    if (args[2]->null_value || !(pad_char_length= rpad->numchars()))
 | 
						|
      goto err;
 | 
						|
  }
 | 
						|
  else
 | 
						|
    pad_char_length= 1; // Implicit space
 | 
						|
 | 
						|
  res_byte_length= res->length();	/* Must be done before alloc_buffer */
 | 
						|
  if (!(res= alloc_buffer(res,str,&tmp_value, (ulong) byte_count)))
 | 
						|
    goto err;
 | 
						|
 | 
						|
  to= (char*) res->ptr()+res_byte_length;
 | 
						|
  ptr_pad=rpad->ptr();
 | 
						|
  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)
 | 
						|
  {
 | 
						|
    pad_byte_length= rpad->charpos((int) count);
 | 
						|
    memcpy(to,ptr_pad,(size_t) pad_byte_length);
 | 
						|
    to+= pad_byte_length;
 | 
						|
  }
 | 
						|
  res->length((uint) (to- (char*) res->ptr()));
 | 
						|
  return (res);
 | 
						|
 | 
						|
 err:
 | 
						|
  null_value=1;
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
String *Item_func_lpad::val_str(String *str)
 | 
						|
{
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
  uint32 res_char_length,pad_char_length;
 | 
						|
  /* must be longlong to avoid truncation */
 | 
						|
  longlong count= args[1]->val_int();
 | 
						|
  longlong byte_count;
 | 
						|
  String *res= args[0]->val_str(&tmp_value);
 | 
						|
  String *pad= arg_count == 2 ? &pad_str : args[2]->val_str(&pad_str);
 | 
						|
 | 
						|
  if (!res || args[1]->null_value || !pad ||  
 | 
						|
      ((count < 0) && !args[1]->unsigned_flag))
 | 
						|
    goto err;  
 | 
						|
 | 
						|
  null_value=0;
 | 
						|
 | 
						|
  if (count == 0)
 | 
						|
    return make_empty_result(str);
 | 
						|
 | 
						|
  /* 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 (intentionally) 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);
 | 
						|
  }
 | 
						|
 | 
						|
  res_char_length= res->numchars();
 | 
						|
 | 
						|
  if (count <= res_char_length)
 | 
						|
  {
 | 
						|
    res->length(res->charpos((int) count));
 | 
						|
    return res;
 | 
						|
  }
 | 
						|
  
 | 
						|
  byte_count= count * collation.collation->mbmaxlen;
 | 
						|
  
 | 
						|
  {
 | 
						|
    THD *thd= current_thd;
 | 
						|
    if ((ulonglong) byte_count > thd->variables.max_allowed_packet)
 | 
						|
    {
 | 
						|
      push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
 | 
						|
                          ER_WARN_ALLOWED_PACKET_OVERFLOWED,
 | 
						|
                          ER_THD(thd, ER_WARN_ALLOWED_PACKET_OVERFLOWED),
 | 
						|
                          func_name(), thd->variables.max_allowed_packet);
 | 
						|
      goto err;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  if (str->alloc((uint32) byte_count))
 | 
						|
    goto err;
 | 
						|
 | 
						|
  if (arg_count == 3)
 | 
						|
  {
 | 
						|
    if (args[2]->null_value || !(pad_char_length= pad->numchars()))
 | 
						|
      goto err;
 | 
						|
  }
 | 
						|
  else
 | 
						|
    pad_char_length= 1; // Implicit space
 | 
						|
  
 | 
						|
  str->length(0);
 | 
						|
  str->set_charset(collation.collation);
 | 
						|
  count-= res_char_length;
 | 
						|
  while (count >= 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;
 | 
						|
 | 
						|
err:
 | 
						|
  null_value= 1;
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
String *Item_func_conv::val_str(String *str)
 | 
						|
{
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
  String *res= args[0]->val_str(str);
 | 
						|
  char *endptr,ans[66],*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) > 62 || abs(to_base) < 2 ||
 | 
						|
      abs(from_base) > 62 || abs(from_base) < 2 || !(res->length()))
 | 
						|
  {
 | 
						|
    null_value= 1;
 | 
						|
    return NULL;
 | 
						|
  }
 | 
						|
  null_value= 0;
 | 
						|
  unsigned_flag= !(from_base < 0);
 | 
						|
 | 
						|
  if (args[0]->field_type() == MYSQL_TYPE_BIT) 
 | 
						|
  {
 | 
						|
    /* 
 | 
						|
     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. 
 | 
						|
    */
 | 
						|
    dec= args[0]->val_int();
 | 
						|
  }
 | 
						|
  else
 | 
						|
  {
 | 
						|
    if (from_base < 0)
 | 
						|
      dec= res->charset()->strntoll(res->ptr(), res->length(),
 | 
						|
                                    -from_base, &endptr, &err);
 | 
						|
    else
 | 
						|
      dec= (longlong) res->charset()->strntoull(res->ptr(), res->length(),
 | 
						|
                                                from_base, &endptr, &err);
 | 
						|
  }
 | 
						|
 | 
						|
  uint dummy_errors;
 | 
						|
  if (!(ptr= longlong2str(dec, ans, to_base)) ||
 | 
						|
      (collation.collation->state & MY_CS_NONASCII) ?
 | 
						|
       str->copy(ans, (uint32)  (ptr - ans), &my_charset_latin1,
 | 
						|
                 collation.collation, &dummy_errors) :
 | 
						|
       str->copy(ans, (uint32) (ptr - ans), collation.collation))
 | 
						|
  {
 | 
						|
    null_value= 1;
 | 
						|
    return NULL;
 | 
						|
  }
 | 
						|
  return str;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
  This function is needed as Item_func_conc_charset stores cached values
 | 
						|
  in str_value.
 | 
						|
*/
 | 
						|
 | 
						|
int Item_func_conv_charset::save_in_field(Field *field, bool no_conversions)
 | 
						|
{
 | 
						|
  String *result;
 | 
						|
  CHARSET_INFO *cs= collation.collation;
 | 
						|
 | 
						|
  result= val_str(&str_value);
 | 
						|
  if (null_value)
 | 
						|
    return set_field_to_null_with_conversions(field, no_conversions);
 | 
						|
 | 
						|
  /* NOTE: If null_value == FALSE, "result" must be not NULL.  */
 | 
						|
  field->set_notnull();
 | 
						|
  int error= field->store(result->ptr(),result->length(),cs);
 | 
						|
  return error;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
String *Item_func_conv_charset::val_str(String *str)
 | 
						|
{
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
  if (use_cached_value)
 | 
						|
    return null_value ? 0 : &str_value;
 | 
						|
  String *arg= args[0]->val_str(&tmp_value);
 | 
						|
  String_copier_for_item copier(current_thd);
 | 
						|
  return ((null_value= args[0]->null_value ||
 | 
						|
                       copier.copy_with_warn(collation.collation, str,
 | 
						|
                                             arg->charset(), arg->ptr(),
 | 
						|
                                             arg->length(), arg->length()))) ?
 | 
						|
    0 : str;
 | 
						|
}
 | 
						|
 | 
						|
bool Item_func_conv_charset::fix_length_and_dec(THD *thd)
 | 
						|
{
 | 
						|
  DBUG_ASSERT(collation.derivation == DERIVATION_IMPLICIT);
 | 
						|
  fix_char_length(args[0]->max_char_length());
 | 
						|
  return FALSE;
 | 
						|
}
 | 
						|
 | 
						|
void Item_func_conv_charset::print(String *str, enum_query_type query_type)
 | 
						|
{
 | 
						|
  str->append(STRING_WITH_LEN("convert("));
 | 
						|
  args[0]->print(str, query_type);
 | 
						|
  str->append(STRING_WITH_LEN(" using "));
 | 
						|
  str->append(collation.collation->cs_name);
 | 
						|
  str->append(')');
 | 
						|
}
 | 
						|
 | 
						|
String *Item_func_set_collation::val_str(String *str)
 | 
						|
{
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
  str=args[0]->val_str(str);
 | 
						|
  if ((null_value=args[0]->null_value))
 | 
						|
    return 0;
 | 
						|
  /*
 | 
						|
    Let SCS be the character set of the source - args[0].
 | 
						|
    Let TCS be the character set of the target - i.e. the character set
 | 
						|
    of the collation specified in the COLLATE clause.
 | 
						|
 | 
						|
    It's OK to return SQL NULL if SCS is not equal to TCS.
 | 
						|
    This is possible on the explicit NULL or expressions derived from
 | 
						|
    the explicit NULL:
 | 
						|
      SELECT NULL COLLATE utf8mb4_general_ci;
 | 
						|
      SELECT COALESCE(NULL) COLLATE utf8mb4_general_ci;
 | 
						|
 | 
						|
    But for a non-NULL result SCS and TCS must be compatible:
 | 
						|
    1. Either SCS==TCS
 | 
						|
    2. Or SCS can be can be reinterpeted to TCS.
 | 
						|
       This scenario is possible when args[0] is numeric and TCS->mbmaxlen==1.
 | 
						|
 | 
						|
    If SCS and TCS are not compatible here, then something went wrong during
 | 
						|
    fix_fields(), e.g. an Item_func_conv_charset was not added two wrap args[0].
 | 
						|
  */
 | 
						|
  DBUG_ASSERT(my_charset_same(args[0]->collation.collation,
 | 
						|
                              collation.collation) ||
 | 
						|
              (args[0]->collation.repertoire == MY_REPERTOIRE_ASCII &&
 | 
						|
               !(collation.collation->state & MY_CS_NONASCII)));
 | 
						|
  str->set_charset(collation.collation);
 | 
						|
  return str;
 | 
						|
}
 | 
						|
 | 
						|
bool Item_func_set_collation::fix_length_and_dec(THD *thd)
 | 
						|
{
 | 
						|
  if (agg_arg_charsets_for_string_result(collation, args, 1) ||
 | 
						|
      collation.merge_collation(thd, thd->variables.character_set_collations,
 | 
						|
                                m_set_collation,
 | 
						|
                                args[0]->collation.repertoire,
 | 
						|
                                with_param() &&
 | 
						|
                                thd->lex->is_ps_or_view_context_analysis()))
 | 
						|
    return true;
 | 
						|
  ulonglong max_char_length= (ulonglong) args[0]->max_char_length();
 | 
						|
  fix_char_length_ulonglong(max_char_length);
 | 
						|
 | 
						|
  return FALSE;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool Item_func_set_collation::eq(const Item *item, bool binary_cmp) const
 | 
						|
{
 | 
						|
  return Item_func::eq(item, binary_cmp) &&
 | 
						|
         collation.collation == item->collation.collation;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void Item_func_set_collation::print(String *str, enum_query_type query_type)
 | 
						|
{
 | 
						|
  args[0]->print_parenthesised(str, query_type, precedence());
 | 
						|
  str->append(STRING_WITH_LEN(" collate "));
 | 
						|
  str->append(m_set_collation.collation_name_for_show());
 | 
						|
}
 | 
						|
 | 
						|
String *Item_func_charset::val_str(String *str)
 | 
						|
{
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
  uint dummy_errors;
 | 
						|
 | 
						|
  null_value= 0;
 | 
						|
  str->copy(m_cached_charset_info.str, m_cached_charset_info.length,
 | 
						|
	    &my_charset_latin1, collation.collation, &dummy_errors);
 | 
						|
  return str;
 | 
						|
}
 | 
						|
 | 
						|
String *Item_func_collation::val_str(String *str)
 | 
						|
{
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
  uint dummy_errors;
 | 
						|
  CHARSET_INFO *cs= args[0]->charset_for_protocol(); 
 | 
						|
 | 
						|
  null_value= 0;
 | 
						|
  str->copy(cs->coll_name.str, cs->coll_name.length, &my_charset_latin1,
 | 
						|
            collation.collation, &dummy_errors);
 | 
						|
  return str;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool Item_func_weight_string::fix_length_and_dec(THD *thd)
 | 
						|
{
 | 
						|
  CHARSET_INFO *cs= args[0]->collation.collation;
 | 
						|
  collation.set(&my_charset_bin, args[0]->collation.derivation);
 | 
						|
  weigth_flags= my_strxfrm_flag_normalize(cs, weigth_flags);
 | 
						|
  /* 
 | 
						|
    Use result_length if it was given explicitly in constructor,
 | 
						|
    otherwise calculate max_length using argument's max_length
 | 
						|
    and "nweights".
 | 
						|
  */
 | 
						|
  if (!(max_length= result_length))
 | 
						|
  {
 | 
						|
    size_t char_length;
 | 
						|
    char_length= ((cs->state & MY_CS_STRNXFRM_BAD_NWEIGHTS) || !nweights) ?
 | 
						|
                 args[0]->max_char_length() : nweights *
 | 
						|
                 my_count_bits_uint32(cs->levels_for_order);
 | 
						|
    max_length= (uint32) cs->strnxfrmlen(char_length * cs->mbmaxlen);
 | 
						|
  }
 | 
						|
  set_maybe_null();
 | 
						|
  return FALSE;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* Return a weight_string according to collation */
 | 
						|
String *Item_func_weight_string::val_str(String *str)
 | 
						|
{
 | 
						|
  String *res;
 | 
						|
  CHARSET_INFO *cs= args[0]->collation.collation;
 | 
						|
  size_t tmp_length, frm_length;
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
 | 
						|
  if (args[0]->result_type() != STRING_RESULT ||
 | 
						|
      !(res= args[0]->val_str(&tmp_value)))
 | 
						|
    goto nl;
 | 
						|
  
 | 
						|
  /*
 | 
						|
    Use result_length if it was given in constructor
 | 
						|
    explicitly, otherwise calculate result length
 | 
						|
    from argument and "nweights".
 | 
						|
  */
 | 
						|
  if (!(tmp_length= result_length))
 | 
						|
  {
 | 
						|
    size_t char_length;
 | 
						|
    if (cs->state & MY_CS_STRNXFRM_BAD_NWEIGHTS)
 | 
						|
    {
 | 
						|
      /*
 | 
						|
        latin2_czech_cs and cp1250_czech_cs do not support
 | 
						|
        the "nweights" limit in strnxfrm(). Use the full length.
 | 
						|
      */
 | 
						|
      char_length= res->length();
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
      /*
 | 
						|
        If we don't need to pad the result with spaces, then it should be
 | 
						|
        OK to calculate character length of the argument approximately:
 | 
						|
        "res->length() / cs->mbminlen" can return a number that is 
 | 
						|
        bigger than the real number of characters in the string, so
 | 
						|
        we'll allocate a little bit more memory but avoid calling
 | 
						|
        the slow res->numchars().
 | 
						|
        In case if we do need to pad with spaces, we call res->numchars()
 | 
						|
        to know the true number of characters.
 | 
						|
      */
 | 
						|
      if (!(char_length= nweights))
 | 
						|
        char_length= (weigth_flags & MY_STRXFRM_PAD_WITH_SPACE) ?
 | 
						|
                      res->numchars() : (res->length() / cs->mbminlen);
 | 
						|
    }
 | 
						|
    tmp_length= cs->strnxfrmlen(char_length * cs->mbmaxlen);
 | 
						|
  }
 | 
						|
 | 
						|
  {
 | 
						|
    THD *thd= current_thd;
 | 
						|
    if (tmp_length > current_thd->variables.max_allowed_packet)
 | 
						|
    {
 | 
						|
      push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
 | 
						|
                          ER_WARN_ALLOWED_PACKET_OVERFLOWED,
 | 
						|
                          ER_THD(thd, ER_WARN_ALLOWED_PACKET_OVERFLOWED),
 | 
						|
                          func_name(),
 | 
						|
                          thd->variables.max_allowed_packet);
 | 
						|
      goto nl;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  if (str->alloc(tmp_length))
 | 
						|
    goto nl;
 | 
						|
 | 
						|
  frm_length= cs->strnxfrm((char*) str->ptr(), tmp_length,
 | 
						|
                           nweights ? nweights : (uint) tmp_length,
 | 
						|
                           res->ptr(), res->length(),
 | 
						|
                           weigth_flags);
 | 
						|
  DBUG_ASSERT(frm_length <= tmp_length);
 | 
						|
 | 
						|
  str->set_charset(&my_charset_bin);
 | 
						|
  str->length(frm_length);
 | 
						|
  null_value= 0;
 | 
						|
  return str;
 | 
						|
 | 
						|
nl:
 | 
						|
  null_value= 1;
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void Item_func_weight_string::print(String *str, enum_query_type query_type)
 | 
						|
{
 | 
						|
  str->append(func_name_cstring());
 | 
						|
  str->append('(');
 | 
						|
  args[0]->print(str, query_type);
 | 
						|
  str->append(',');
 | 
						|
  str->append_ulonglong(result_length);
 | 
						|
  str->append(',');
 | 
						|
  str->append_ulonglong(nweights);
 | 
						|
  str->append(',');
 | 
						|
  str->append_ulonglong(weigth_flags);
 | 
						|
  str->append(')');
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
String *Item_func_hex::val_str_ascii_from_val_real(String *str)
 | 
						|
{
 | 
						|
  ulonglong dec;
 | 
						|
  double val= args[0]->val_real();
 | 
						|
  if ((null_value= args[0]->null_value))
 | 
						|
    return 0;
 | 
						|
  if ((val <= (double) LONGLONG_MIN) ||
 | 
						|
      (val >= (double) (ulonglong) ULONGLONG_MAX))
 | 
						|
    dec= ULONGLONG_MAX;
 | 
						|
  else if (val < 0)
 | 
						|
    dec= (ulonglong) (longlong) (val - 0.5);
 | 
						|
  else
 | 
						|
    dec= (ulonglong) (val + 0.5);
 | 
						|
  return str->set_hex(dec) ? make_empty_result(str) : str;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
String *Item_func_hex::val_str_ascii_from_val_str(String *str)
 | 
						|
{
 | 
						|
  DBUG_ASSERT(&tmp_value != str);
 | 
						|
  String *res= args[0]->val_str(&tmp_value);
 | 
						|
  DBUG_ASSERT(res != str);
 | 
						|
  if ((null_value= (res == NULL)))
 | 
						|
    return NULL;
 | 
						|
  return str->set_hex(res->ptr(), res->length()) ? make_empty_result(str) : str;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
String *Item_func_hex::val_str_ascii_from_val_int(String *str)
 | 
						|
{
 | 
						|
  ulonglong dec= (ulonglong) args[0]->val_int();
 | 
						|
  if ((null_value= args[0]->null_value))
 | 
						|
    return 0;
 | 
						|
  return str->set_hex(dec) ? make_empty_result(str) : str;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
  /** Convert given hex string to a binary string. */
 | 
						|
 | 
						|
String *Item_func_unhex::val_str(String *str)
 | 
						|
{
 | 
						|
  const char *from, *end;
 | 
						|
  char *to;
 | 
						|
  String *res;
 | 
						|
  uint length;
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
 | 
						|
  res= args[0]->val_str(&tmp_value);
 | 
						|
  if (!res || str->alloc(length= (1+res->length())/2))
 | 
						|
  {
 | 
						|
    null_value=1;
 | 
						|
    return 0;
 | 
						|
  }
 | 
						|
 | 
						|
  from= res->ptr();
 | 
						|
  null_value= 0;
 | 
						|
  str->set_charset(&my_charset_bin);
 | 
						|
  str->length(length);
 | 
						|
  to= (char*) str->ptr();
 | 
						|
  if (res->length() % 2)
 | 
						|
  {
 | 
						|
    int hex_char;
 | 
						|
    *to++= hex_char= hexchar_to_int(*from++);
 | 
						|
    if ((null_value= (hex_char == -1)))
 | 
						|
      return 0;
 | 
						|
  }
 | 
						|
  for (end=res->ptr()+res->length(); from < end ; from+=2, to++)
 | 
						|
  {
 | 
						|
    int hex_char1, hex_char2;
 | 
						|
    hex_char1= hexchar_to_int(from[0]);
 | 
						|
    hex_char2= hexchar_to_int(from[1]);
 | 
						|
    if ((null_value= (hex_char1 == -1 || hex_char2 == -1)))
 | 
						|
      return 0;
 | 
						|
    *to= (char) ((hex_char1 << 4) | hex_char2);
 | 
						|
  }
 | 
						|
  return str;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
#ifndef DBUG_OFF
 | 
						|
String *Item_func_like_range::val_str(String *str)
 | 
						|
{
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
  longlong nbytes= args[1]->val_int();
 | 
						|
  String *res= args[0]->val_str(str);
 | 
						|
  size_t min_len, max_len;
 | 
						|
  CHARSET_INFO *cs= collation.collation;
 | 
						|
 | 
						|
  if (!res || args[0]->null_value || args[1]->null_value ||
 | 
						|
      nbytes < 0 || nbytes > MAX_BLOB_WIDTH ||
 | 
						|
      min_str.alloc((size_t)nbytes) || max_str.alloc((size_t)nbytes))
 | 
						|
    goto err;
 | 
						|
  null_value=0;
 | 
						|
 | 
						|
  if (cs->like_range(res->ptr(), res->length(),
 | 
						|
                     '\\', '_', '%', (size_t)nbytes,
 | 
						|
                     (char*) min_str.ptr(), (char*) 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
 | 
						|
 | 
						|
 | 
						|
void Item_func_binary::print(String *str, enum_query_type query_type)
 | 
						|
{
 | 
						|
  str->append(STRING_WITH_LEN("cast("));
 | 
						|
  args[0]->print(str, query_type);
 | 
						|
  str->append(STRING_WITH_LEN(" as binary)"));
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
#include <my_dir.h>				// For my_stat
 | 
						|
 | 
						|
String *Item_load_file::val_str(String *str)
 | 
						|
{
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
  String *file_name;
 | 
						|
  File file;
 | 
						|
  MY_STAT stat_info;
 | 
						|
  char path[FN_REFLEN];
 | 
						|
  ulonglong file_size;
 | 
						|
  DBUG_ENTER("load_file");
 | 
						|
 | 
						|
  if (!(file_name= args[0]->val_str(str))
 | 
						|
#ifndef NO_EMBEDDED_ACCESS_CHECKS
 | 
						|
      || !(current_thd->security_ctx->master_access & FILE_ACL)
 | 
						|
#endif
 | 
						|
      )
 | 
						|
    goto err;
 | 
						|
 | 
						|
  (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))
 | 
						|
    goto err;
 | 
						|
 | 
						|
  if (!mysql_file_stat(key_file_loadfile, path, &stat_info, MYF(0)))
 | 
						|
    goto err;
 | 
						|
 | 
						|
  if (!(stat_info.st_mode & S_IROTH))
 | 
						|
  {
 | 
						|
    /* my_error(ER_TEXTFILE_NOT_READABLE, MYF(0), file_name->c_ptr()); */
 | 
						|
    goto err;
 | 
						|
  }
 | 
						|
  file_size= stat_info.st_size;
 | 
						|
 | 
						|
  {
 | 
						|
    THD *thd= current_thd;
 | 
						|
    if (file_size >= thd->variables.max_allowed_packet)
 | 
						|
    {
 | 
						|
      push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
 | 
						|
                          ER_WARN_ALLOWED_PACKET_OVERFLOWED,
 | 
						|
                          ER_THD(thd, ER_WARN_ALLOWED_PACKET_OVERFLOWED),
 | 
						|
                          func_name(), thd->variables.max_allowed_packet);
 | 
						|
      goto err;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  if (tmp_value.alloc((ulong)file_size))
 | 
						|
    goto err;
 | 
						|
  if ((file= mysql_file_open(key_file_loadfile,
 | 
						|
                             file_name->ptr(), O_RDONLY, MYF(0))) < 0)
 | 
						|
    goto err;
 | 
						|
  if (mysql_file_read(file, (uchar*) tmp_value.ptr(), (size_t)stat_info.st_size,
 | 
						|
                      MYF(MY_NABP)))
 | 
						|
  {
 | 
						|
    mysql_file_close(file, MYF(0));
 | 
						|
    goto err;
 | 
						|
  }
 | 
						|
  tmp_value.length((uint32)stat_info.st_size);
 | 
						|
  mysql_file_close(file, MYF(0));
 | 
						|
  null_value = 0;
 | 
						|
  DBUG_RETURN(&tmp_value);
 | 
						|
 | 
						|
err:
 | 
						|
  null_value = 1;
 | 
						|
  DBUG_RETURN(0);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
String* Item_func_export_set::val_str(String* str)
 | 
						|
{
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
  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;
 | 
						|
 | 
						|
  uint 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)
 | 
						|
  {
 | 
						|
    null_value= true;
 | 
						|
    return NULL;
 | 
						|
  }
 | 
						|
  /*
 | 
						|
    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 = (uint) args[4]->val_int();
 | 
						|
    if (num_set_values > 64)
 | 
						|
      num_set_values=64;
 | 
						|
    if (args[4]->null_value)
 | 
						|
    {
 | 
						|
      null_value= true;
 | 
						|
      return NULL;
 | 
						|
    }
 | 
						|
    /* Fall through */
 | 
						|
  case 4:
 | 
						|
    if (!(sep = args[3]->val_str(&sep_buf)))	// Only true if NULL
 | 
						|
    {
 | 
						|
      null_value= true;
 | 
						|
      return NULL;
 | 
						|
    }
 | 
						|
    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;
 | 
						|
 | 
						|
  THD *thd= current_thd;
 | 
						|
  const ulong max_allowed_packet= thd->variables.max_allowed_packet;
 | 
						|
  const uint num_separators= num_set_values > 0 ? num_set_values - 1 : 0;
 | 
						|
  const ulonglong max_total_length=
 | 
						|
    num_set_values * MY_MAX(yes->length(), no->length()) +
 | 
						|
    num_separators * sep->length();
 | 
						|
 | 
						|
  if (unlikely(max_total_length > max_allowed_packet))
 | 
						|
  {
 | 
						|
    push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
 | 
						|
                        ER_WARN_ALLOWED_PACKET_OVERFLOWED,
 | 
						|
                        ER_THD(thd, ER_WARN_ALLOWED_PACKET_OVERFLOWED),
 | 
						|
                        func_name(), max_allowed_packet);
 | 
						|
    null_value= true;
 | 
						|
    return NULL;
 | 
						|
  }
 | 
						|
 | 
						|
  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);
 | 
						|
  }
 | 
						|
  return str;
 | 
						|
}
 | 
						|
 | 
						|
bool Item_func_export_set::fix_length_and_dec(THD *thd)
 | 
						|
{
 | 
						|
  uint32 length= MY_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, MY_MIN(4, arg_count) - 1))
 | 
						|
    return TRUE;
 | 
						|
  fix_char_length(length * 64 + sep_length * 63);
 | 
						|
  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 problems 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());
 | 
						|
  /*
 | 
						|
    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
 | 
						|
  };
 | 
						|
 | 
						|
  ulong max_allowed_packet= current_thd->variables.max_allowed_packet;
 | 
						|
  char *from, *to, *end, *start;
 | 
						|
  String *arg= args[0]->val_str(&tmp_value);
 | 
						|
  uint 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= (char*) arg->ptr(), end= from + arg_length; from < end; from++)
 | 
						|
      new_length+= get_esc_bit(escmask, (uchar) *from);
 | 
						|
    if (new_length > max_allowed_packet)
 | 
						|
      goto toolong;
 | 
						|
  }
 | 
						|
  else
 | 
						|
  {
 | 
						|
    new_length= (arg_length * 2) +  /* For string characters */
 | 
						|
                (2 * collation.collation->mbmaxlen); /* For quotes */
 | 
						|
    set_if_smaller(new_length, max_allowed_packet);
 | 
						|
  }
 | 
						|
 | 
						|
  if (str->alloc(new_length))
 | 
						|
    goto null;
 | 
						|
 | 
						|
  if (collation.collation->mbmaxlen > 1)
 | 
						|
  {
 | 
						|
    CHARSET_INFO *cs= collation.collation;
 | 
						|
    int mblen;
 | 
						|
    uchar *to_end;
 | 
						|
    to= (char*) str->ptr();
 | 
						|
    to_end= (uchar*) to + new_length;
 | 
						|
 | 
						|
    /* Put leading quote */
 | 
						|
    if ((mblen= cs->wc_mb('\'', (uchar *) to, to_end)) <= 0)
 | 
						|
      goto toolong;
 | 
						|
    to+= mblen;
 | 
						|
 | 
						|
    for (start= (char*) arg->ptr(), end= start + arg_length; start < end; )
 | 
						|
    {
 | 
						|
      my_wc_t wc;
 | 
						|
      bool escape;
 | 
						|
      if ((mblen= cs->mb_wc(&wc, (uchar*) start, (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->wc_mb('\\', (uchar*) to, to_end)) <= 0)
 | 
						|
          goto toolong;
 | 
						|
        to+= mblen;
 | 
						|
      }
 | 
						|
      if ((mblen= cs->wc_mb(wc, (uchar*) to, to_end)) <= 0)
 | 
						|
        goto toolong;
 | 
						|
      to+= mblen;
 | 
						|
    }
 | 
						|
 | 
						|
    /* Put trailing quote */
 | 
						|
    if ((mblen= cs->wc_mb('\'', (uchar *) to, to_end)) <= 0)
 | 
						|
      goto toolong;
 | 
						|
    to+= mblen;
 | 
						|
    new_length= (uint)(to - str->ptr());
 | 
						|
    goto ret;
 | 
						|
  }
 | 
						|
 | 
						|
  /*
 | 
						|
    We replace characters from the end to the beginning
 | 
						|
  */
 | 
						|
  to= (char*) str->ptr() + new_length - 1;
 | 
						|
  *to--= '\'';
 | 
						|
  for (start= (char*) 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:
 | 
						|
  str->length(new_length);
 | 
						|
  str->set_charset(collation.collation);
 | 
						|
  null_value= 0;
 | 
						|
  return str;
 | 
						|
 | 
						|
toolong:
 | 
						|
  push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN,
 | 
						|
                      ER_WARN_ALLOWED_PACKET_OVERFLOWED,
 | 
						|
                      ER_THD(current_thd, ER_WARN_ALLOWED_PACKET_OVERFLOWED),
 | 
						|
                      func_name(), max_allowed_packet);
 | 
						|
null:
 | 
						|
  null_value= 1;
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
longlong Item_func_uncompressed_length::val_int()
 | 
						|
{
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
  String *res= args[0]->val_str(&value);
 | 
						|
  if (!res)
 | 
						|
  {
 | 
						|
    null_value=1;
 | 
						|
    return 0; /* purecov: inspected */
 | 
						|
  }
 | 
						|
  null_value=0;
 | 
						|
  if (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)
 | 
						|
  {
 | 
						|
    THD *thd= current_thd;
 | 
						|
    push_warning(thd, Sql_condition::WARN_LEVEL_WARN,
 | 
						|
                 ER_ZLIB_Z_DATA_ERROR,
 | 
						|
                 ER_THD(thd, ER_ZLIB_Z_DATA_ERROR));
 | 
						|
    null_value= 1;
 | 
						|
    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 (uninitialized memory) which
 | 
						|
        confuse valgrind
 | 
						|
  */
 | 
						|
  return uint4korr(res->ptr()) & 0x3FFFFFFF;
 | 
						|
}
 | 
						|
 | 
						|
longlong Item_func_crc32::val_int()
 | 
						|
{
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
  DBUG_ASSERT(arg_count == 1 || arg_count == 2);
 | 
						|
  String *res;
 | 
						|
  longlong crc;
 | 
						|
  if (arg_count > 1)
 | 
						|
  {
 | 
						|
    crc= args[0]->val_int();
 | 
						|
    null_value= args[0]->null_value;
 | 
						|
    if (null_value)
 | 
						|
      return 0;
 | 
						|
    res= args[1]->val_str(&value);
 | 
						|
  }
 | 
						|
  else
 | 
						|
  {
 | 
						|
    crc= 0;
 | 
						|
    null_value= 0;
 | 
						|
    res= args[0]->val_str(&value);
 | 
						|
  }
 | 
						|
 | 
						|
  if (!res)
 | 
						|
  {
 | 
						|
    null_value=1;
 | 
						|
    return 0; /* purecov: inspected */
 | 
						|
  }
 | 
						|
 | 
						|
  return static_cast<longlong>
 | 
						|
    (ulonglong{crc_func(uint32_t(crc), res->ptr(), res->length())});
 | 
						|
}
 | 
						|
 | 
						|
#ifdef HAVE_COMPRESS
 | 
						|
#include "zlib.h"
 | 
						|
 | 
						|
String *Item_func_compress::val_str(String *str)
 | 
						|
{
 | 
						|
  int err= Z_OK, code;
 | 
						|
  size_t new_size;
 | 
						|
  String *res;
 | 
						|
  Byte *body;
 | 
						|
  char *tmp, *last_char;
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
 | 
						|
  if (!(res= args[0]->val_str(&tmp_value)))
 | 
						|
  {
 | 
						|
    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 (((uint32) (new_size+5) <= res->length()) || 
 | 
						|
      str->alloc((uint32) new_size + 4 + 1))
 | 
						|
  {
 | 
						|
    null_value= 1;
 | 
						|
    return 0;
 | 
						|
  }
 | 
						|
 | 
						|
  body= ((Byte*)str->ptr()) + 4;
 | 
						|
 | 
						|
  // As far as we have checked res->is_empty() we can use ptr()
 | 
						|
  if ((err= my_compress_buffer(body, &new_size, (const uchar *)res->ptr(),
 | 
						|
                               res->length())) != Z_OK)
 | 
						|
  {
 | 
						|
    THD *thd= current_thd;
 | 
						|
    code= err==Z_MEM_ERROR ? ER_ZLIB_Z_MEM_ERROR : ER_ZLIB_Z_BUF_ERROR;
 | 
						|
    push_warning(thd, Sql_condition::WARN_LEVEL_WARN, code,
 | 
						|
                 ER_THD(thd, code));
 | 
						|
    null_value= 1;
 | 
						|
    return 0;
 | 
						|
  }
 | 
						|
 | 
						|
  tmp= (char*) str->ptr(); // int4store is a macro; avoid side effects
 | 
						|
  int4store(tmp, 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++;
 | 
						|
  }
 | 
						|
 | 
						|
  str->length((uint32)new_size + 4);
 | 
						|
  str->set_charset(&my_charset_bin);
 | 
						|
  return str;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
String *Item_func_uncompress::val_str(String *str)
 | 
						|
{
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
  String *res= args[0]->val_str(&tmp_value);
 | 
						|
  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)
 | 
						|
  {
 | 
						|
    THD *thd= current_thd;
 | 
						|
    push_warning(thd, Sql_condition::WARN_LEVEL_WARN,
 | 
						|
			ER_ZLIB_Z_DATA_ERROR,
 | 
						|
			ER_THD(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)
 | 
						|
  {
 | 
						|
    THD *thd= current_thd;
 | 
						|
    push_warning_printf(thd,Sql_condition::WARN_LEVEL_WARN,
 | 
						|
			ER_TOO_BIG_FOR_UNCOMPRESS,
 | 
						|
			ER_THD(thd, ER_TOO_BIG_FOR_UNCOMPRESS),
 | 
						|
                        static_cast<int>(thd->variables.
 | 
						|
                                         max_allowed_packet));
 | 
						|
    goto err;
 | 
						|
  }
 | 
						|
  if (str->alloc((uint32)new_size))
 | 
						|
    goto err;
 | 
						|
 | 
						|
  if ((err= uncompress((Byte*)str->ptr(), &new_size,
 | 
						|
		       ((const Bytef*)res->ptr())+4,res->length()-4)) == Z_OK)
 | 
						|
  {
 | 
						|
    str->length((uint32) new_size);
 | 
						|
    return str;
 | 
						|
  }
 | 
						|
 | 
						|
  code= ((err == Z_BUF_ERROR) ? ER_ZLIB_Z_BUF_ERROR :
 | 
						|
	 ((err == Z_MEM_ERROR) ? ER_ZLIB_Z_MEM_ERROR : ER_ZLIB_Z_DATA_ERROR));
 | 
						|
  {
 | 
						|
    THD *thd= current_thd;
 | 
						|
    push_warning(thd, Sql_condition::WARN_LEVEL_WARN, code, ER_THD(thd, code));
 | 
						|
  }
 | 
						|
 | 
						|
err:
 | 
						|
  null_value= 1;
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
#endif
 | 
						|
 | 
						|
 | 
						|
Item_func_dyncol_create::Item_func_dyncol_create(THD *thd, List<Item> &args,
 | 
						|
                                                 DYNCALL_CREATE_DEF *dfs):
 | 
						|
  Item_str_func(thd, args), defs(dfs), vals(0), keys_num(NULL), keys_str(NULL),
 | 
						|
  names(FALSE), force_names(FALSE)
 | 
						|
{
 | 
						|
  DBUG_ASSERT((args.elements & 0x1) == 0); // even number of arguments
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool Item_func_dyncol_create::fix_fields(THD *thd, Item **ref)
 | 
						|
{
 | 
						|
  uint i;
 | 
						|
  bool res= Item_func::fix_fields(thd, ref); // no need Item_str_func here
 | 
						|
  if (!res)
 | 
						|
  {
 | 
						|
    vals= (DYNAMIC_COLUMN_VALUE *) alloc_root(thd->mem_root,
 | 
						|
                                              sizeof(DYNAMIC_COLUMN_VALUE) *
 | 
						|
                                              (arg_count / 2));
 | 
						|
    for (i= 0;
 | 
						|
         i + 1 < arg_count && args[i]->result_type() == INT_RESULT;
 | 
						|
         i+= 2)
 | 
						|
      ;
 | 
						|
    if (i + 1 < arg_count)
 | 
						|
    {
 | 
						|
      names= TRUE;
 | 
						|
    }
 | 
						|
 | 
						|
    keys_num= (uint *) alloc_root(thd->mem_root,
 | 
						|
                               (sizeof(LEX_STRING) > sizeof(uint) ?
 | 
						|
                                sizeof(LEX_STRING) :
 | 
						|
                                sizeof(uint)) *
 | 
						|
                               (arg_count / 2));
 | 
						|
    keys_str= (LEX_STRING *) keys_num;
 | 
						|
    status_var_increment(thd->status_var.feature_dynamic_columns);
 | 
						|
  }
 | 
						|
  return res || vals == 0 || keys_num == 0;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool Item_func_dyncol_create::fix_length_and_dec(THD *thd)
 | 
						|
{
 | 
						|
  max_length= MAX_BLOB_WIDTH;
 | 
						|
  set_maybe_null();
 | 
						|
  collation.set(&my_charset_bin);
 | 
						|
  decimals= 0;
 | 
						|
  return FALSE;
 | 
						|
}
 | 
						|
 | 
						|
bool Item_func_dyncol_create::prepare_arguments(THD *thd, bool force_names_arg)
 | 
						|
{
 | 
						|
  char buff[STRING_BUFFER_USUAL_SIZE];
 | 
						|
  String *res, tmp(buff, sizeof(buff), &my_charset_bin);
 | 
						|
  uint column_count= (arg_count / 2);
 | 
						|
  uint i;
 | 
						|
  my_decimal dtmp, *dres;
 | 
						|
  force_names= force_names_arg;
 | 
						|
 | 
						|
  if (!(names || force_names))
 | 
						|
  {
 | 
						|
    for (i= 0; i < column_count; i++)
 | 
						|
    {
 | 
						|
      uint valpos= i * 2 + 1;
 | 
						|
      DYNAMIC_COLUMN_TYPE type= defs[i].type;
 | 
						|
      if (type == DYN_COL_NULL)
 | 
						|
        type= args[valpos]->type_handler()->dyncol_type(args[valpos]);
 | 
						|
      if (type == DYN_COL_STRING &&
 | 
						|
          args[valpos]->type() == Item::FUNC_ITEM &&
 | 
						|
          ((Item_func *)args[valpos])->functype() == DYNCOL_FUNC)
 | 
						|
      {
 | 
						|
        force_names= 1;
 | 
						|
        break;
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  /* get values */
 | 
						|
  for (i= 0; i < column_count; i++)
 | 
						|
  {
 | 
						|
    uint valpos= i * 2 + 1;
 | 
						|
    DYNAMIC_COLUMN_TYPE type= defs[i].type;
 | 
						|
    if (type == DYN_COL_NULL) // auto detect
 | 
						|
      type= args[valpos]->type_handler()->dyncol_type(args[valpos]);
 | 
						|
    if (type == DYN_COL_STRING &&
 | 
						|
        args[valpos]->type() == Item::FUNC_ITEM &&
 | 
						|
        ((Item_func *)args[valpos])->functype() == DYNCOL_FUNC)
 | 
						|
    {
 | 
						|
      DBUG_ASSERT(names || force_names);
 | 
						|
      type= DYN_COL_DYNCOL;
 | 
						|
    }
 | 
						|
    if (names || force_names)
 | 
						|
    {
 | 
						|
      res= args[i * 2]->val_str(&tmp);
 | 
						|
      if (res)
 | 
						|
      {
 | 
						|
        // guaranty UTF-8 string for names
 | 
						|
        if (my_charset_same(res->charset(), DYNCOL_UTF))
 | 
						|
        {
 | 
						|
          keys_str[i].length= res->length();
 | 
						|
          keys_str[i].str= thd->strmake(res->ptr(), res->length());
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
          uint strlen= res->length() * DYNCOL_UTF->mbmaxlen + 1;
 | 
						|
          uint dummy_errors;
 | 
						|
          if (char *str= (char *) thd->alloc(strlen))
 | 
						|
          {
 | 
						|
            keys_str[i].length=
 | 
						|
              copy_and_convert(str, strlen, DYNCOL_UTF,
 | 
						|
                               res->ptr(), res->length(), res->charset(),
 | 
						|
                               &dummy_errors);
 | 
						|
              keys_str[i].str= str;
 | 
						|
          }
 | 
						|
          else
 | 
						|
            keys_str[i].length= 0;
 | 
						|
 | 
						|
        }
 | 
						|
      }
 | 
						|
      else
 | 
						|
      {
 | 
						|
        keys_str[i].length= 0;
 | 
						|
        keys_str[i].str= NULL;
 | 
						|
      }
 | 
						|
    }
 | 
						|
    else
 | 
						|
      keys_num[i]= (uint) args[i * 2]->val_int();
 | 
						|
    if (args[i * 2]->null_value)
 | 
						|
    {
 | 
						|
      /* to make cleanup possible */
 | 
						|
      for (; i < column_count; i++)
 | 
						|
        vals[i].type= DYN_COL_NULL;
 | 
						|
      return 1;
 | 
						|
    }
 | 
						|
    vals[i].type= type;
 | 
						|
    switch (type) {
 | 
						|
    case DYN_COL_NULL:
 | 
						|
      DBUG_ASSERT(args[valpos]->field_type() == MYSQL_TYPE_NULL);
 | 
						|
      break;
 | 
						|
    case DYN_COL_INT:
 | 
						|
      vals[i].x.long_value= args[valpos]->val_int();
 | 
						|
      break;
 | 
						|
    case DYN_COL_UINT:
 | 
						|
      vals[i].x.ulong_value= args[valpos]->val_int();
 | 
						|
      break;
 | 
						|
    case DYN_COL_DOUBLE:
 | 
						|
      vals[i].x.double_value= args[valpos]->val_real();
 | 
						|
      break;
 | 
						|
    case DYN_COL_DYNCOL:
 | 
						|
    case DYN_COL_STRING:
 | 
						|
      res= args[valpos]->val_str(&tmp);
 | 
						|
      if (res && defs[i].cs)
 | 
						|
        res->set_charset(defs[i].cs);
 | 
						|
      if (res &&
 | 
						|
          (vals[i].x.string.value.str= thd->strmake(res->ptr(), res->length())))
 | 
						|
      {
 | 
						|
	vals[i].x.string.value.length= res->length();
 | 
						|
	vals[i].x.string.charset= res->charset();
 | 
						|
      }
 | 
						|
      else
 | 
						|
      {
 | 
						|
        args[valpos]->null_value= 1;            // In case of out of memory
 | 
						|
        vals[i].x.string.value.str= NULL;
 | 
						|
        vals[i].x.string.value.length= 0;         // just to be safe
 | 
						|
      }
 | 
						|
      break;
 | 
						|
    case DYN_COL_DECIMAL:
 | 
						|
      if ((dres= args[valpos]->val_decimal(&dtmp)))
 | 
						|
      {
 | 
						|
	mariadb_dyncol_prepare_decimal(&vals[i]);
 | 
						|
        DBUG_ASSERT(vals[i].x.decimal.value.len == dres->len);
 | 
						|
        vals[i].x.decimal.value.intg= dres->intg;
 | 
						|
        vals[i].x.decimal.value.frac= dres->frac;
 | 
						|
        vals[i].x.decimal.value.sign= dres->sign();
 | 
						|
        memcpy(vals[i].x.decimal.buffer, dres->buf,
 | 
						|
               sizeof(vals[i].x.decimal.buffer));
 | 
						|
      }
 | 
						|
      else
 | 
						|
      {
 | 
						|
	mariadb_dyncol_prepare_decimal(&vals[i]); // just to be safe
 | 
						|
        DBUG_ASSERT(args[valpos]->null_value);
 | 
						|
      }
 | 
						|
      break;
 | 
						|
    case DYN_COL_DATETIME:
 | 
						|
    case DYN_COL_DATE:
 | 
						|
      args[valpos]->get_date(thd, &vals[i].x.time_value,
 | 
						|
                             Datetime::Options(thd));
 | 
						|
      break;
 | 
						|
    case DYN_COL_TIME:
 | 
						|
      args[valpos]->get_time(thd, &vals[i].x.time_value);
 | 
						|
      break;
 | 
						|
    default:
 | 
						|
      DBUG_ASSERT(0);
 | 
						|
      vals[i].type= DYN_COL_NULL;
 | 
						|
    }
 | 
						|
    if (vals[i].type != DYN_COL_NULL && args[valpos]->null_value)
 | 
						|
    {
 | 
						|
      vals[i].type= DYN_COL_NULL;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  return FALSE;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
String *Item_func_dyncol_create::val_str(String *str)
 | 
						|
{
 | 
						|
  DYNAMIC_COLUMN col;
 | 
						|
  String *res;
 | 
						|
  uint column_count= (arg_count / 2);
 | 
						|
  enum enum_dyncol_func_result rc;
 | 
						|
  DBUG_ASSERT((arg_count & 0x1) == 0); // even number of arguments
 | 
						|
 | 
						|
  /* FIXME: add thd argument to Item::val_str() */
 | 
						|
  if (prepare_arguments(current_thd, FALSE))
 | 
						|
  {
 | 
						|
    res= NULL;
 | 
						|
    null_value= 1;
 | 
						|
  }
 | 
						|
  else
 | 
						|
  {
 | 
						|
    if ((rc= ((names || force_names) ?
 | 
						|
              mariadb_dyncol_create_many_named(&col, column_count, keys_str,
 | 
						|
                                               vals, TRUE) :
 | 
						|
              mariadb_dyncol_create_many_num(&col, column_count, keys_num,
 | 
						|
                                             vals, TRUE))))
 | 
						|
    {
 | 
						|
      dynamic_column_error_message(rc);
 | 
						|
      mariadb_dyncol_free(&col);
 | 
						|
      res= NULL;
 | 
						|
      null_value= TRUE;
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
      /* Move result from DYNAMIC_COLUMN to str_value */
 | 
						|
      char *ptr;
 | 
						|
      size_t length, alloc_length;
 | 
						|
      dynstr_reassociate(&col, &ptr, &length, &alloc_length);
 | 
						|
      str_value.reset(ptr, length, alloc_length, &my_charset_bin);
 | 
						|
      res= &str_value;
 | 
						|
      null_value= FALSE;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  return res;
 | 
						|
}
 | 
						|
 | 
						|
void Item_func_dyncol_create::print_arguments(String *str,
 | 
						|
                                              enum_query_type query_type)
 | 
						|
{
 | 
						|
  uint i;
 | 
						|
  uint column_count= (arg_count / 2);
 | 
						|
  for (i= 0; i < column_count; i++)
 | 
						|
  {
 | 
						|
    args[i*2]->print(str, query_type);
 | 
						|
    str->append(',');
 | 
						|
    args[i*2 + 1]->print(str, query_type);
 | 
						|
    switch (defs[i].type) {
 | 
						|
    case DYN_COL_NULL: // automatic type => write nothing
 | 
						|
      break;
 | 
						|
    case DYN_COL_INT:
 | 
						|
      str->append(STRING_WITH_LEN(" AS int"));
 | 
						|
      break;
 | 
						|
    case DYN_COL_UINT:
 | 
						|
      str->append(STRING_WITH_LEN(" AS unsigned int"));
 | 
						|
      break;
 | 
						|
    case DYN_COL_DOUBLE:
 | 
						|
      str->append(STRING_WITH_LEN(" AS double"));
 | 
						|
      break;
 | 
						|
    case DYN_COL_DYNCOL:
 | 
						|
    case DYN_COL_STRING:
 | 
						|
      str->append(STRING_WITH_LEN(" AS char"));
 | 
						|
      if (defs[i].cs)
 | 
						|
      {
 | 
						|
        str->append(STRING_WITH_LEN(" charset "));
 | 
						|
        str->append(defs[i].cs->cs_name);
 | 
						|
        if (Charset(defs[i].cs).can_have_collate_clause())
 | 
						|
        {
 | 
						|
          str->append(STRING_WITH_LEN(" collate "));
 | 
						|
          str->append(defs[i].cs->coll_name);
 | 
						|
        }
 | 
						|
        str->append(' ');
 | 
						|
      }
 | 
						|
      break;
 | 
						|
    case DYN_COL_DECIMAL:
 | 
						|
      str->append(STRING_WITH_LEN(" AS decimal"));
 | 
						|
      break;
 | 
						|
    case DYN_COL_DATETIME:
 | 
						|
      str->append(STRING_WITH_LEN(" AS datetime"));
 | 
						|
      break;
 | 
						|
    case DYN_COL_DATE:
 | 
						|
      str->append(STRING_WITH_LEN(" AS date"));
 | 
						|
      break;
 | 
						|
    case DYN_COL_TIME:
 | 
						|
      str->append(STRING_WITH_LEN(" AS time"));
 | 
						|
      break;
 | 
						|
    }
 | 
						|
    if (i < column_count - 1)
 | 
						|
      str->append(',');
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void Item_func_dyncol_create::print(String *str,
 | 
						|
                                    enum_query_type query_type)
 | 
						|
{
 | 
						|
  DBUG_ASSERT((arg_count & 0x1) == 0); // even number of arguments
 | 
						|
  str->append(STRING_WITH_LEN("column_create("));
 | 
						|
  print_arguments(str, query_type);
 | 
						|
  str->append(')');
 | 
						|
}
 | 
						|
 | 
						|
String *Item_func_dyncol_json::val_str(String *str)
 | 
						|
{
 | 
						|
  DYNAMIC_STRING json, col;
 | 
						|
  String *res;
 | 
						|
  enum enum_dyncol_func_result rc;
 | 
						|
 | 
						|
  res= args[0]->val_str(str);
 | 
						|
  if (args[0]->null_value)
 | 
						|
    goto null;
 | 
						|
 | 
						|
  col.str= (char *)res->ptr();
 | 
						|
  col.length= res->length();
 | 
						|
  if ((rc= mariadb_dyncol_json(&col, &json)))
 | 
						|
  {
 | 
						|
    dynamic_column_error_message(rc);
 | 
						|
    goto null;
 | 
						|
  }
 | 
						|
  bzero(&col, sizeof(col));
 | 
						|
  {
 | 
						|
    /* Move result from DYNAMIC_COLUMN to str */
 | 
						|
    char *ptr;
 | 
						|
    size_t length, alloc_length;
 | 
						|
    dynstr_reassociate(&json, &ptr, &length, &alloc_length);
 | 
						|
    str->reset(ptr, length, alloc_length, DYNCOL_UTF);
 | 
						|
    null_value= FALSE;
 | 
						|
  }
 | 
						|
  str->set_charset(DYNCOL_UTF);
 | 
						|
  return str;
 | 
						|
 | 
						|
null:
 | 
						|
  bzero(&col, sizeof(col));
 | 
						|
  null_value= TRUE;
 | 
						|
  return NULL;
 | 
						|
}
 | 
						|
 | 
						|
String *Item_func_dyncol_add::val_str(String *str)
 | 
						|
{
 | 
						|
  DYNAMIC_COLUMN col;
 | 
						|
  String *res;
 | 
						|
  uint column_count=  (arg_count / 2);
 | 
						|
  enum enum_dyncol_func_result rc;
 | 
						|
  DBUG_ASSERT((arg_count & 0x1) == 1); // odd number of arguments
 | 
						|
 | 
						|
  /* We store the packed data last */
 | 
						|
  res= args[arg_count - 1]->val_str(str);
 | 
						|
  if (args[arg_count - 1]->null_value ||
 | 
						|
      init_dynamic_string(&col, NULL, res->length() + STRING_BUFFER_USUAL_SIZE,
 | 
						|
                          STRING_BUFFER_USUAL_SIZE))
 | 
						|
    goto null;
 | 
						|
 | 
						|
  col.length= res->length();
 | 
						|
  memcpy(col.str, res->ptr(), col.length);
 | 
						|
 | 
						|
  /* FIXME: add thd argument to Item::val_str() */
 | 
						|
  if (prepare_arguments(current_thd, mariadb_dyncol_has_names(&col)))
 | 
						|
    goto null;
 | 
						|
 | 
						|
  if ((rc= ((names || force_names) ?
 | 
						|
            mariadb_dyncol_update_many_named(&col, column_count,
 | 
						|
                                             keys_str, vals) :
 | 
						|
            mariadb_dyncol_update_many_num(&col, column_count,
 | 
						|
                                           keys_num, vals))))
 | 
						|
  {
 | 
						|
    dynamic_column_error_message(rc);
 | 
						|
    mariadb_dyncol_free(&col);
 | 
						|
    goto null;
 | 
						|
  }
 | 
						|
 | 
						|
  {
 | 
						|
    /* Move result from DYNAMIC_COLUMN to str */
 | 
						|
    char *ptr;
 | 
						|
    size_t length, alloc_length;
 | 
						|
    dynstr_reassociate(&col, &ptr, &length, &alloc_length);
 | 
						|
    str->reset(ptr, length, alloc_length, &my_charset_bin);
 | 
						|
    null_value= FALSE;
 | 
						|
  }
 | 
						|
 | 
						|
  return str;
 | 
						|
 | 
						|
null:
 | 
						|
  null_value= TRUE;
 | 
						|
  return NULL;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void Item_func_dyncol_add::print(String *str,
 | 
						|
                                 enum_query_type query_type)
 | 
						|
{
 | 
						|
  DBUG_ASSERT((arg_count & 0x1) == 1); // odd number of arguments
 | 
						|
  str->append(STRING_WITH_LEN("column_add("));
 | 
						|
  args[arg_count - 1]->print(str, query_type);
 | 
						|
  str->append(',');
 | 
						|
  print_arguments(str, query_type);
 | 
						|
  str->append(')');
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Get value for a column stored in a dynamic column
 | 
						|
 | 
						|
  @notes
 | 
						|
  This function ensures that null_value is set correctly
 | 
						|
*/
 | 
						|
 | 
						|
bool Item_dyncol_get::get_dyn_value(THD *thd, DYNAMIC_COLUMN_VALUE *val,
 | 
						|
                                    String *tmp)
 | 
						|
{
 | 
						|
  DYNAMIC_COLUMN dyn_str;
 | 
						|
  String *res;
 | 
						|
  longlong num= 0;
 | 
						|
  LEX_STRING buf, *name= NULL;
 | 
						|
  char nmstrbuf[11];
 | 
						|
  String nmbuf(nmstrbuf, sizeof(nmstrbuf), system_charset_info);
 | 
						|
  enum enum_dyncol_func_result rc;
 | 
						|
 | 
						|
  if (args[1]->result_type() == INT_RESULT)
 | 
						|
    num= args[1]->val_int();
 | 
						|
  else
 | 
						|
  {
 | 
						|
    String *nm= args[1]->val_str(&nmbuf);
 | 
						|
    if (!nm || args[1]->null_value)
 | 
						|
    {
 | 
						|
      null_value= 1;
 | 
						|
      return 1;
 | 
						|
    }
 | 
						|
 | 
						|
    if (my_charset_same(nm->charset(), DYNCOL_UTF))
 | 
						|
    {
 | 
						|
      buf.str= (char *) nm->ptr();
 | 
						|
      buf.length= nm->length();
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
      uint strlen= nm->length() * DYNCOL_UTF->mbmaxlen + 1;
 | 
						|
      uint dummy_errors;
 | 
						|
      buf.str= (char *) thd->alloc(strlen);
 | 
						|
      if (buf.str)
 | 
						|
      {
 | 
						|
        buf.length=
 | 
						|
          copy_and_convert(buf.str, strlen, DYNCOL_UTF,
 | 
						|
                           nm->ptr(), nm->length(), nm->charset(),
 | 
						|
                           &dummy_errors);
 | 
						|
      }
 | 
						|
      else
 | 
						|
        buf.length= 0;
 | 
						|
    }
 | 
						|
    name= &buf;
 | 
						|
  }
 | 
						|
 | 
						|
 | 
						|
  if (args[1]->null_value || num < 0 || num > INT_MAX)
 | 
						|
  {
 | 
						|
    null_value= 1;
 | 
						|
    return 1;
 | 
						|
  }
 | 
						|
 | 
						|
  res= args[0]->val_str(tmp);
 | 
						|
  if (args[0]->null_value)
 | 
						|
  {
 | 
						|
    null_value= 1;
 | 
						|
    return 1;
 | 
						|
  }
 | 
						|
 | 
						|
  dyn_str.str=   (char*) res->ptr();
 | 
						|
  dyn_str.length= res->length();
 | 
						|
  if ((rc= ((name == NULL) ?
 | 
						|
            mariadb_dyncol_get_num(&dyn_str, (uint) num, val) :
 | 
						|
            mariadb_dyncol_get_named(&dyn_str, name, val))))
 | 
						|
  {
 | 
						|
    dynamic_column_error_message(rc);
 | 
						|
    null_value= 1;
 | 
						|
    return 1;
 | 
						|
  }
 | 
						|
 | 
						|
  null_value= 0;
 | 
						|
  return 0;                                     // ok
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
String *Item_dyncol_get::val_str(String *str_result)
 | 
						|
{
 | 
						|
  DYNAMIC_COLUMN_VALUE val;
 | 
						|
  char buff[STRING_BUFFER_USUAL_SIZE];
 | 
						|
  String tmp(buff, sizeof(buff), &my_charset_bin);
 | 
						|
 | 
						|
  if (get_dyn_value(current_thd, &val, &tmp))
 | 
						|
    return NULL;
 | 
						|
 | 
						|
  switch (val.type) {
 | 
						|
  case DYN_COL_NULL:
 | 
						|
    goto null;
 | 
						|
  case DYN_COL_INT:
 | 
						|
  case DYN_COL_UINT:
 | 
						|
    str_result->set_int(val.x.long_value, MY_TEST(val.type == DYN_COL_UINT),
 | 
						|
                       &my_charset_latin1);
 | 
						|
    break;
 | 
						|
  case DYN_COL_DOUBLE:
 | 
						|
    str_result->set_real(val.x.double_value, NOT_FIXED_DEC, &my_charset_latin1);
 | 
						|
    break;
 | 
						|
  case DYN_COL_DYNCOL:
 | 
						|
  case DYN_COL_STRING:
 | 
						|
    if ((char*) tmp.ptr() <= val.x.string.value.str &&
 | 
						|
        (char*) tmp.ptr() + tmp.length() >= val.x.string.value.str)
 | 
						|
    {
 | 
						|
      /* value is allocated in tmp buffer; We have to make a copy */
 | 
						|
      str_result->copy(val.x.string.value.str, val.x.string.value.length,
 | 
						|
                      val.x.string.charset);
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
      /*
 | 
						|
        It's safe to use the current value because it's either pointing
 | 
						|
        into a field or in a buffer for another item and this buffer
 | 
						|
        is not going to be deleted during expression evaluation
 | 
						|
      */
 | 
						|
      str_result->set(val.x.string.value.str, val.x.string.value.length,
 | 
						|
                      val.x.string.charset);
 | 
						|
    }
 | 
						|
    break;
 | 
						|
  case DYN_COL_DECIMAL:
 | 
						|
  {
 | 
						|
    int res;
 | 
						|
    int length= decimal_string_size(&val.x.decimal.value);
 | 
						|
    if (str_result->alloc(length))
 | 
						|
      goto null;
 | 
						|
    if ((res= decimal2string(&val.x.decimal.value, (char*) str_result->ptr(),
 | 
						|
                             &length, 0, 0, ' ')) != E_DEC_OK)
 | 
						|
    {
 | 
						|
      char buff[40];
 | 
						|
      int len= sizeof(buff);
 | 
						|
      DBUG_ASSERT(length < (int)sizeof(buff));
 | 
						|
      decimal2string(&val.x.decimal.value, buff, &len, 0, 0, ' ');
 | 
						|
      decimal_operation_results(res, buff, "CHAR");
 | 
						|
    }
 | 
						|
    str_result->set_charset(&my_charset_latin1);
 | 
						|
    str_result->length(length);
 | 
						|
    break;
 | 
						|
  }
 | 
						|
  case DYN_COL_DATETIME:
 | 
						|
  case DYN_COL_DATE:
 | 
						|
  case DYN_COL_TIME:
 | 
						|
  {
 | 
						|
    int length;
 | 
						|
    /*
 | 
						|
      We use AUTO_SEC_PART_DIGITS here to ensure that we do not loose
 | 
						|
      any microseconds from the data. This is safe to do as we are
 | 
						|
      asked to return the time argument as a string.
 | 
						|
    */
 | 
						|
    if (str_result->alloc(MAX_DATE_STRING_REP_LENGTH) ||
 | 
						|
        !(length= my_TIME_to_str(&val.x.time_value, (char*) str_result->ptr(),
 | 
						|
                                 AUTO_SEC_PART_DIGITS)))
 | 
						|
      goto null;
 | 
						|
    str_result->set_charset(&my_charset_latin1);
 | 
						|
    str_result->length(length);
 | 
						|
    break;
 | 
						|
  }
 | 
						|
  }
 | 
						|
  return str_result;
 | 
						|
 | 
						|
null:
 | 
						|
  null_value= TRUE;
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
longlong Item_dyncol_get::val_int()
 | 
						|
{
 | 
						|
  THD *thd= current_thd;
 | 
						|
  DYNAMIC_COLUMN_VALUE val;
 | 
						|
  char buff[STRING_BUFFER_USUAL_SIZE];
 | 
						|
  String tmp(buff, sizeof(buff), &my_charset_bin);
 | 
						|
 | 
						|
  if (get_dyn_value(thd, &val, &tmp))
 | 
						|
    return 0;
 | 
						|
 | 
						|
  switch (val.type) {
 | 
						|
  case DYN_COL_DYNCOL:
 | 
						|
  case DYN_COL_NULL:
 | 
						|
    goto null;
 | 
						|
  case DYN_COL_UINT:
 | 
						|
    unsigned_flag= 1;            // Make it possible for caller to detect sign
 | 
						|
    return val.x.long_value;
 | 
						|
  case DYN_COL_INT:
 | 
						|
    unsigned_flag= 0;            // Make it possible for caller to detect sign
 | 
						|
    return val.x.long_value;
 | 
						|
  case DYN_COL_DOUBLE:
 | 
						|
    return Converter_double_to_longlong_with_warn(thd, val.x.double_value,
 | 
						|
                                                  unsigned_flag).result();
 | 
						|
  case DYN_COL_STRING:
 | 
						|
  {
 | 
						|
    int error;
 | 
						|
    longlong num;
 | 
						|
    char *end= val.x.string.value.str + val.x.string.value.length, *org_end= end;
 | 
						|
 | 
						|
    num= my_strtoll10(val.x.string.value.str, &end, &error);
 | 
						|
    if (unlikely(end != org_end || error > 0))
 | 
						|
    {
 | 
						|
      push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
 | 
						|
                          ER_BAD_DATA,
 | 
						|
                          ER_THD(thd, ER_BAD_DATA),
 | 
						|
                          ErrConvString(val.x.string.value.str,
 | 
						|
                                        val.x.string.value.length,
 | 
						|
                                        val.x.string.charset).ptr(),
 | 
						|
                          unsigned_flag ? "UNSIGNED INT" : "INT");
 | 
						|
    }
 | 
						|
    unsigned_flag= error >= 0;
 | 
						|
    return num;
 | 
						|
  }
 | 
						|
  case DYN_COL_DECIMAL:
 | 
						|
  {
 | 
						|
    longlong num;
 | 
						|
    my_decimal2int(E_DEC_FATAL_ERROR, &val.x.decimal.value, unsigned_flag,
 | 
						|
                   &num);
 | 
						|
    return num;
 | 
						|
  }
 | 
						|
  case DYN_COL_DATETIME:
 | 
						|
  case DYN_COL_DATE:
 | 
						|
  case DYN_COL_TIME:
 | 
						|
    unsigned_flag= !val.x.time_value.neg;
 | 
						|
    if (unsigned_flag)
 | 
						|
      return TIME_to_ulonglong(&val.x.time_value);
 | 
						|
    else
 | 
						|
      return -(longlong)TIME_to_ulonglong(&val.x.time_value);
 | 
						|
  }
 | 
						|
 | 
						|
null:
 | 
						|
  null_value= TRUE;
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
double Item_dyncol_get::val_real()
 | 
						|
{
 | 
						|
  THD *thd= current_thd;
 | 
						|
  DYNAMIC_COLUMN_VALUE val;
 | 
						|
  char buff[STRING_BUFFER_USUAL_SIZE];
 | 
						|
  String tmp(buff, sizeof(buff), &my_charset_bin);
 | 
						|
 | 
						|
  if (get_dyn_value(thd, &val, &tmp))
 | 
						|
    return 0.0;
 | 
						|
 | 
						|
  switch (val.type) {
 | 
						|
  case DYN_COL_DYNCOL:
 | 
						|
  case DYN_COL_NULL:
 | 
						|
    goto null;
 | 
						|
  case DYN_COL_UINT:
 | 
						|
    return ulonglong2double(val.x.ulong_value);
 | 
						|
  case DYN_COL_INT:
 | 
						|
    return (double) val.x.long_value;
 | 
						|
  case DYN_COL_DOUBLE:
 | 
						|
    return (double) val.x.double_value;
 | 
						|
  case DYN_COL_STRING:
 | 
						|
  {
 | 
						|
    int error;
 | 
						|
    char *end;
 | 
						|
    double res= val.x.string.charset->strntod((char*) val.x.string.value.str,
 | 
						|
                                              val.x.string.value.length, &end, &error);
 | 
						|
 | 
						|
    if (end != (char*) val.x.string.value.str + val.x.string.value.length ||
 | 
						|
        error)
 | 
						|
    {
 | 
						|
      push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
 | 
						|
                          ER_BAD_DATA,
 | 
						|
                          ER_THD(thd, ER_BAD_DATA),
 | 
						|
                          ErrConvString(val.x.string.value.str,
 | 
						|
                                        val.x.string.value.length,
 | 
						|
                                        val.x.string.charset).ptr(),
 | 
						|
                          "DOUBLE");
 | 
						|
    }
 | 
						|
    return res;
 | 
						|
  }
 | 
						|
  case DYN_COL_DECIMAL:
 | 
						|
  {
 | 
						|
    double res;
 | 
						|
    /* This will always succeed */
 | 
						|
    decimal2double(&val.x.decimal.value, &res);
 | 
						|
    return res;
 | 
						|
  }
 | 
						|
  case DYN_COL_DATETIME:
 | 
						|
  case DYN_COL_DATE:
 | 
						|
  case DYN_COL_TIME:
 | 
						|
    return TIME_to_double(&val.x.time_value);
 | 
						|
  }
 | 
						|
 | 
						|
null:
 | 
						|
  null_value= TRUE;
 | 
						|
  return 0.0;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
my_decimal *Item_dyncol_get::val_decimal(my_decimal *decimal_value)
 | 
						|
{
 | 
						|
  THD *thd= current_thd;
 | 
						|
  DYNAMIC_COLUMN_VALUE val;
 | 
						|
  char buff[STRING_BUFFER_USUAL_SIZE];
 | 
						|
  String tmp(buff, sizeof(buff), &my_charset_bin);
 | 
						|
 | 
						|
  if (get_dyn_value(thd, &val, &tmp))
 | 
						|
    return NULL;
 | 
						|
 | 
						|
  switch (val.type) {
 | 
						|
  case DYN_COL_DYNCOL:
 | 
						|
  case DYN_COL_NULL:
 | 
						|
    goto null;
 | 
						|
  case DYN_COL_UINT:
 | 
						|
    int2my_decimal(E_DEC_FATAL_ERROR, val.x.long_value, TRUE, decimal_value);
 | 
						|
    break;
 | 
						|
  case DYN_COL_INT:
 | 
						|
    int2my_decimal(E_DEC_FATAL_ERROR, val.x.long_value, FALSE, decimal_value);
 | 
						|
    break;
 | 
						|
  case DYN_COL_DOUBLE:
 | 
						|
    double2my_decimal(E_DEC_FATAL_ERROR, val.x.double_value, decimal_value);
 | 
						|
    break;
 | 
						|
  case DYN_COL_STRING:
 | 
						|
  {
 | 
						|
    const char *end;
 | 
						|
    int rc;
 | 
						|
    rc= str2my_decimal(0, val.x.string.value.str, val.x.string.value.length,
 | 
						|
                       val.x.string.charset, decimal_value, &end);
 | 
						|
    if (rc != E_DEC_OK ||
 | 
						|
        end != val.x.string.value.str + val.x.string.value.length)
 | 
						|
    {
 | 
						|
      push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
 | 
						|
                          ER_BAD_DATA,
 | 
						|
                          ER_THD(thd, ER_BAD_DATA),
 | 
						|
                          ErrConvString(val.x.string.value.str,
 | 
						|
                                        val.x.string.value.length,
 | 
						|
                                        val.x.string.charset).ptr(),
 | 
						|
                          "DECIMAL");
 | 
						|
    }
 | 
						|
    break;
 | 
						|
  }
 | 
						|
  case DYN_COL_DECIMAL:
 | 
						|
    decimal2my_decimal(&val.x.decimal.value, decimal_value);
 | 
						|
    break;
 | 
						|
  case DYN_COL_DATETIME:
 | 
						|
  case DYN_COL_DATE:
 | 
						|
  case DYN_COL_TIME:
 | 
						|
    decimal_value= TIME_to_my_decimal(&val.x.time_value, decimal_value);
 | 
						|
    break;
 | 
						|
  }
 | 
						|
  return decimal_value;
 | 
						|
 | 
						|
null:
 | 
						|
  null_value= TRUE;
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool Item_dyncol_get::get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate)
 | 
						|
{
 | 
						|
  DYNAMIC_COLUMN_VALUE val;
 | 
						|
  char buff[STRING_BUFFER_USUAL_SIZE];
 | 
						|
  String tmp(buff, sizeof(buff), &my_charset_bin);
 | 
						|
  bool signed_value= 0;
 | 
						|
 | 
						|
  if (get_dyn_value(current_thd, &val, &tmp))
 | 
						|
    return 1;                                   // Error
 | 
						|
 | 
						|
  switch (val.type) {
 | 
						|
  case DYN_COL_DYNCOL:
 | 
						|
  case DYN_COL_NULL:
 | 
						|
    goto null;
 | 
						|
  case DYN_COL_INT:
 | 
						|
    signed_value= 1;                                  // For error message
 | 
						|
    /* fall through */
 | 
						|
  case DYN_COL_UINT:
 | 
						|
    if (signed_value || val.x.ulong_value <= LONGLONG_MAX)
 | 
						|
    {
 | 
						|
      longlong llval = (longlong)val.x.ulong_value;
 | 
						|
      if (int_to_datetime_with_warn(thd, Longlong_hybrid(llval, !signed_value),
 | 
						|
                                    ltime, fuzzydate, 0, 0 /* TODO */))
 | 
						|
        goto null;
 | 
						|
      return 0;
 | 
						|
    }
 | 
						|
    /* let double_to_datetime_with_warn() issue the warning message */
 | 
						|
    val.x.double_value= static_cast<double>(ULONGLONG_MAX);
 | 
						|
    /* fall through */
 | 
						|
  case DYN_COL_DOUBLE:
 | 
						|
    if (double_to_datetime_with_warn(thd, val.x.double_value, ltime, fuzzydate,
 | 
						|
                                     0, 0 /* TODO */))
 | 
						|
      goto null;
 | 
						|
    return 0;
 | 
						|
  case DYN_COL_DECIMAL:
 | 
						|
    if (decimal_to_datetime_with_warn(thd, (my_decimal*)&val.x.decimal.value,
 | 
						|
                                      ltime, fuzzydate, 0, 0 /* TODO */))
 | 
						|
      goto null;
 | 
						|
    return 0;
 | 
						|
  case DYN_COL_STRING:
 | 
						|
    if (str_to_datetime_with_warn(thd, &my_charset_numeric,
 | 
						|
                                  val.x.string.value.str,
 | 
						|
                                  val.x.string.value.length,
 | 
						|
                                  ltime, fuzzydate))
 | 
						|
      goto null;
 | 
						|
    return 0;
 | 
						|
  case DYN_COL_DATETIME:
 | 
						|
  case DYN_COL_DATE:
 | 
						|
  case DYN_COL_TIME:
 | 
						|
    *ltime= val.x.time_value;
 | 
						|
    return 0;
 | 
						|
  }
 | 
						|
 | 
						|
null:
 | 
						|
  null_value= TRUE;
 | 
						|
  return 1;
 | 
						|
}
 | 
						|
 | 
						|
void Item_dyncol_get::print(String *str, enum_query_type query_type)
 | 
						|
{
 | 
						|
  /*
 | 
						|
    Parent cast doesn't exist yet, only print dynamic column name. This happens
 | 
						|
    when called from create_func_cast() / wrong_precision_error().
 | 
						|
  */
 | 
						|
  if (!str->length())
 | 
						|
  {
 | 
						|
    args[1]->print(str, query_type);
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  /* see create_func_dyncol_get */
 | 
						|
  DBUG_ASSERT(str->length() >= 5);
 | 
						|
  DBUG_ASSERT(strncmp(str->ptr() + str->length() - 5, "cast(", 5) == 0);
 | 
						|
 | 
						|
  str->length(str->length() - 5);    // removing "cast("
 | 
						|
  str->append(STRING_WITH_LEN("column_get("));
 | 
						|
  args[0]->print(str, query_type);
 | 
						|
  str->append(',');
 | 
						|
  args[1]->print(str, query_type);
 | 
						|
  /* let the parent cast item add " as <type>)" */
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
String *Item_func_dyncol_list::val_str(String *str)
 | 
						|
{
 | 
						|
  uint i;
 | 
						|
  enum enum_dyncol_func_result rc;
 | 
						|
  LEX_STRING *names= 0;
 | 
						|
  uint count;
 | 
						|
  DYNAMIC_COLUMN col;
 | 
						|
  String *res= args[0]->val_str(str);
 | 
						|
 | 
						|
  if (args[0]->null_value)
 | 
						|
    goto null;
 | 
						|
  col.length= res->length();
 | 
						|
  /* We do not change the string, so could do this trick */
 | 
						|
  col.str= (char *)res->ptr();
 | 
						|
  if ((rc= mariadb_dyncol_list_named(&col, &count, &names)))
 | 
						|
  {
 | 
						|
    bzero(&col, sizeof(col));
 | 
						|
    dynamic_column_error_message(rc);
 | 
						|
    goto null;
 | 
						|
  }
 | 
						|
  bzero(&col, sizeof(col));
 | 
						|
 | 
						|
  /*
 | 
						|
    We estimate average name length as 10
 | 
						|
  */
 | 
						|
  if (str->alloc(count * 13))
 | 
						|
    goto null;
 | 
						|
 | 
						|
  str->length(0);
 | 
						|
  for (i= 0; i < count; i++)
 | 
						|
  {
 | 
						|
    append_identifier(current_thd, str, names[i].str, names[i].length);
 | 
						|
    if (i < count - 1)
 | 
						|
      str->qs_append(',');
 | 
						|
  }
 | 
						|
  null_value= FALSE;
 | 
						|
  if (names)
 | 
						|
    my_free(names);
 | 
						|
  str->set_charset(DYNCOL_UTF);
 | 
						|
  return str;
 | 
						|
 | 
						|
null:
 | 
						|
  null_value= TRUE;
 | 
						|
  if (names)
 | 
						|
    my_free(names);
 | 
						|
  return NULL;
 | 
						|
}
 | 
						|
 | 
						|
Item_temptable_rowid::Item_temptable_rowid(TABLE *table_arg)
 | 
						|
  : Item_str_func(table_arg->in_use), table(table_arg)
 | 
						|
{
 | 
						|
  max_length= table->file->ref_length;
 | 
						|
}
 | 
						|
 | 
						|
bool Item_temptable_rowid::fix_length_and_dec(THD *thd)
 | 
						|
{
 | 
						|
  used_tables_cache= table->map;
 | 
						|
  const_item_cache= false;
 | 
						|
  return FALSE;
 | 
						|
}
 | 
						|
 | 
						|
String *Item_temptable_rowid::val_str(String *str)
 | 
						|
{
 | 
						|
  if (!((null_value= table->null_row)))
 | 
						|
    table->file->position(table->record[0]);
 | 
						|
  str_value.set((char*)(table->file->ref), max_length, &my_charset_bin);
 | 
						|
  return &str_value;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Helper routine to encode length prefix
 | 
						|
  in natsort_encode_numeric_string().
 | 
						|
 | 
						|
  The idea is so that bigger input numbers correspond
 | 
						|
  lexicographically bigger output strings.
 | 
						|
 | 
						|
  Note, that in real use the number would typically
 | 
						|
  small, as it only computes variable *length prefixes*.
 | 
						|
 | 
						|
  @param[in] n - the number
 | 
						|
  @param[in] s - output string
 | 
						|
 | 
						|
  @return - length of encoding
 | 
						|
 | 
						|
  Here is how encoding works
 | 
						|
 | 
						|
  - n is from 0 to 8
 | 
						|
   Output string calculated as '0'+n (range '0' - '8')
 | 
						|
 | 
						|
  - n is from 9 to 17
 | 
						|
    Output calculated as concat('9', '0' + n -9)'
 | 
						|
    Output range: '90'-'98'
 | 
						|
 | 
						|
   -n is from 18 to 26
 | 
						|
    Output calculated as concat('99', '0' + n -18)'
 | 
						|
    Output range '990'-'998'
 | 
						|
 | 
						|
  - n is from 27 to SIZE_T_MAX
 | 
						|
    Output starts with '999',
 | 
						|
    then log10(n) is encoded as 2-digit decimal number
 | 
						|
    then the number itself is added.
 | 
						|
    Example : for 28 key is concat('999', '01' , '28')
 | 
						|
    i.e '9990128'
 | 
						|
 | 
						|
    Key length is 5 + ceil(log10(n))
 | 
						|
 | 
						|
   Output range is
 | 
						|
     (64bit)'9990128' - '9991918446744073709551615'
 | 
						|
     (32bit)'9990128' - '999094294967295'
 | 
						|
*/
 | 
						|
 | 
						|
/* Largest length of encoded string.*/
 | 
						|
static size_t natsort_encode_length_max(size_t n)
 | 
						|
{
 | 
						|
  return (n < 27) ? n/9+1 : 26;
 | 
						|
}
 | 
						|
 | 
						|
static void natsort_encode_length(size_t n, String* out)
 | 
						|
{
 | 
						|
  if (n < 27)
 | 
						|
  {
 | 
						|
    if (n >= 9)
 | 
						|
      out->fill(out->length() + n/9,'9');
 | 
						|
    out->append(char(n % 9 + '0'));
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  size_t log10n= 0;
 | 
						|
  for (size_t tmp= n / 10; tmp; tmp/= 10)
 | 
						|
    log10n++;
 | 
						|
  out->fill(out->length() + 3, '9');
 | 
						|
  out->append('0' + (char) (log10n / 10));
 | 
						|
  out->append('0' + (char) (log10n % 10));
 | 
						|
  out->append_ulonglong(n);
 | 
						|
}
 | 
						|
 | 
						|
enum class NATSORT_ERR
 | 
						|
{
 | 
						|
  SUCCESS= 0,
 | 
						|
  KEY_TOO_LARGE= 1,
 | 
						|
  ALLOC_ERROR= 2
 | 
						|
};
 | 
						|
 | 
						|
/*
 | 
						|
   Encode numeric string for natural sorting.
 | 
						|
 | 
						|
   @param[in] in - start of the numeric string
 | 
						|
   skipping leading zeros
 | 
						|
 | 
						|
   @param[in] n_digits - length of the string,
 | 
						|
   in characters, not counting leading zeros.
 | 
						|
 | 
						|
   @param[out] out - String to write to. The string should
 | 
						|
   have enough preallocated space to fit the encoded key.
 | 
						|
 | 
						|
   @return
 | 
						|
     NATSORT_ERR::SUCCESS  - success
 | 
						|
     NATSORT_ERR::KEY_TOO_LARGE  - out string does not have enough
 | 
						|
     space left to accomodate the key.
 | 
						|
 | 
						|
 | 
						|
   The resulting encoding of the numeric string is then
 | 
						|
 | 
						|
   CONCAT(natsort_encode_length(n_digits), in)
 | 
						|
*/
 | 
						|
static NATSORT_ERR natsort_encode_numeric_string(const char *in,
 | 
						|
                                                 size_t n_digits,
 | 
						|
                                                 String *out)
 | 
						|
{
 | 
						|
  DBUG_ASSERT(in);
 | 
						|
  DBUG_ASSERT(n_digits);
 | 
						|
 | 
						|
  if (out->length() + natsort_encode_length_max(n_digits - 1) + n_digits >
 | 
						|
      out->alloced_length())
 | 
						|
    return NATSORT_ERR::KEY_TOO_LARGE;
 | 
						|
 | 
						|
  natsort_encode_length(n_digits - 1, out);
 | 
						|
  out->append(in, n_digits);
 | 
						|
  return NATSORT_ERR::SUCCESS;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
  Calculate max size of the natsort key.
 | 
						|
 | 
						|
  A digit in string expands to 2 chars  length_prefix , and  the digit
 | 
						|
 | 
						|
  With even length L=2N, the largest key corresponds to input string
 | 
						|
  in form REPEAT(<digit><letter>,N) and the length of a key is
 | 
						|
  2N + N = 3N
 | 
						|
 | 
						|
  With odd input length L=2N+1, largest key is built by appending
 | 
						|
  a digit at the end, with key length 3N+2
 | 
						|
 | 
						|
*/
 | 
						|
static size_t natsort_max_key_size(size_t input_size)
 | 
						|
{
 | 
						|
  return input_size + (input_size + 1)/2 ;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Convert a string to natural sort key.
 | 
						|
  @param[in]   in - input string
 | 
						|
  @param[out]  out - output string
 | 
						|
  @param[in]   max_key_size - the maximum size of the output
 | 
						|
               key, in bytes.
 | 
						|
  @return NATSORT_ERR::SUCCESS - successful completion
 | 
						|
          NATSORT_ERR::ALLOC_ERROR - memory allocation error
 | 
						|
          NATSORT_ERR::KEY_TOO_LARGE - resulting key would exceed max_key_size
 | 
						|
*/
 | 
						|
static NATSORT_ERR to_natsort_key(const String *in, String *out,
 | 
						|
                                  size_t max_key_size)
 | 
						|
{
 | 
						|
  size_t n_digits= 0;
 | 
						|
  size_t n_lead_zeros= 0;
 | 
						|
  size_t num_start= 0;
 | 
						|
  size_t reserve_length= std::min(
 | 
						|
      natsort_max_key_size(in->length()) + MAX_BIGINT_WIDTH + 2, max_key_size);
 | 
						|
 | 
						|
  out->length(0);
 | 
						|
  out->set_charset(in->charset());
 | 
						|
 | 
						|
  if (out->alloc((uint32) reserve_length))
 | 
						|
    return NATSORT_ERR::ALLOC_ERROR;
 | 
						|
 | 
						|
  for (size_t pos= 0;; pos++)
 | 
						|
  {
 | 
						|
    char c= pos < in->length() ? (*in)[pos] : 0;
 | 
						|
    bool is_digit= (c >= '0' && c <= '9');
 | 
						|
    if (!is_digit && (n_digits || n_lead_zeros))
 | 
						|
    {
 | 
						|
      /* Handle end of digits run.*/
 | 
						|
      if (!n_digits)
 | 
						|
      {
 | 
						|
        /*We only have zeros.*/
 | 
						|
        n_lead_zeros--;
 | 
						|
        num_start= pos - 1;
 | 
						|
        n_digits= 1;
 | 
						|
      }
 | 
						|
      NATSORT_ERR err= natsort_encode_numeric_string(
 | 
						|
          in->ptr() + num_start, n_digits, out);
 | 
						|
      if (err != NATSORT_ERR::SUCCESS)
 | 
						|
        return err;
 | 
						|
 | 
						|
      /* Reset state.*/
 | 
						|
      n_digits= 0;
 | 
						|
      num_start= size_t(-1);
 | 
						|
      n_lead_zeros= 0;
 | 
						|
    }
 | 
						|
 | 
						|
    if (pos == in->length())
 | 
						|
      break;
 | 
						|
 | 
						|
    if (!is_digit)
 | 
						|
    {
 | 
						|
      if (out->length() == max_key_size)
 | 
						|
        return NATSORT_ERR::KEY_TOO_LARGE;
 | 
						|
      out->append(c);
 | 
						|
    }
 | 
						|
    else if (c == '0' && !n_digits)
 | 
						|
      n_lead_zeros++;
 | 
						|
    else if (!n_digits++)
 | 
						|
      num_start= pos;
 | 
						|
  }
 | 
						|
  return NATSORT_ERR::SUCCESS;
 | 
						|
}
 | 
						|
 | 
						|
String *Item_func_natural_sort_key::val_str(String *out)
 | 
						|
{
 | 
						|
  String *in= args[0]->val_str();
 | 
						|
  if (args[0]->null_value || !in)
 | 
						|
  {
 | 
						|
    null_value= true;
 | 
						|
    return nullptr;
 | 
						|
  }
 | 
						|
  NATSORT_ERR err= NATSORT_ERR::SUCCESS;
 | 
						|
  CHARSET_INFO *cs= in->charset();
 | 
						|
  ulong max_allowed_packet= current_thd->variables.max_allowed_packet;
 | 
						|
  uint errs;
 | 
						|
  String tmp;
 | 
						|
  /*
 | 
						|
    to_natsort_key() only support charsets where digits are represented by
 | 
						|
    a single byte in range 0x30-0x39. Almost everything is OK, just utf16/32
 | 
						|
    won't do. Full ASCII compatibility is not required, so that SJIS and SWE7
 | 
						|
    are fine.
 | 
						|
  */
 | 
						|
  if (cs->mbminlen != 1)
 | 
						|
  {
 | 
						|
    if (tmp.copy(in, &my_charset_utf8mb4_bin, &errs))
 | 
						|
      goto error_exit;
 | 
						|
    in= &tmp;
 | 
						|
  }
 | 
						|
 | 
						|
  err= to_natsort_key(in, out, max_allowed_packet / cs->mbminlen);
 | 
						|
 | 
						|
  if (err != NATSORT_ERR::SUCCESS)
 | 
						|
  {
 | 
						|
    if (err == NATSORT_ERR::KEY_TOO_LARGE)
 | 
						|
    {
 | 
						|
      push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN,
 | 
						|
                          ER_WARN_ALLOWED_PACKET_OVERFLOWED,
 | 
						|
                          ER(ER_WARN_ALLOWED_PACKET_OVERFLOWED), func_name(),
 | 
						|
                          max_allowed_packet);
 | 
						|
    }
 | 
						|
    goto error_exit;
 | 
						|
  }
 | 
						|
 | 
						|
  if (cs->mbminlen != 1)
 | 
						|
  {
 | 
						|
    /* output string is now utf8, convert to input charset.*/
 | 
						|
    if (tmp.copy(out, cs, &errs) || out->copy(tmp))
 | 
						|
      goto error_exit;
 | 
						|
  }
 | 
						|
  null_value= false;
 | 
						|
  return out;
 | 
						|
 | 
						|
error_exit:
 | 
						|
  null_value= true;
 | 
						|
  return nullptr;
 | 
						|
}
 | 
						|
 | 
						|
bool Item_func_natural_sort_key::fix_length_and_dec(THD *thd)
 | 
						|
{
 | 
						|
  if (agg_arg_charsets_for_string_result(collation, args, 1))
 | 
						|
    return true;
 | 
						|
  DBUG_ASSERT(collation.collation != NULL);
 | 
						|
  uint32 max_char_len=
 | 
						|
      (uint32) natsort_max_key_size(args[0]->max_char_length());
 | 
						|
  fix_char_length(max_char_len);
 | 
						|
 | 
						|
  set_maybe_null(args[0]->maybe_null() ||
 | 
						|
                 max_char_len * collation.collation->mbmaxlen >
 | 
						|
                     current_thd->variables.max_allowed_packet);
 | 
						|
  return false;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Disable use in stored virtual functions. Temporarily(?), until
 | 
						|
  the encoding is stable.
 | 
						|
*/
 | 
						|
bool Item_func_natural_sort_key::check_vcol_func_processor(void *arg)
 | 
						|
{
 | 
						|
  return mark_unsupported_function(func_name(), "()", arg,
 | 
						|
                                   VCOL_NON_DETERMINISTIC);
 | 
						|
}
 | 
						|
 | 
						|
String *Item_func_format_pico_time::val_str_ascii(String *)
 | 
						|
{
 | 
						|
  double time_val= args[0]->val_real();
 | 
						|
 | 
						|
  null_value= args[0]->null_value;
 | 
						|
  if (null_value)
 | 
						|
    return 0;
 | 
						|
 | 
						|
  constexpr ulonglong nano{1000};
 | 
						|
  constexpr ulonglong micro{1000 * nano};
 | 
						|
  constexpr ulonglong milli{1000 * micro};
 | 
						|
  constexpr ulonglong sec{1000 * milli};
 | 
						|
  constexpr ulonglong min{60 * sec};
 | 
						|
  constexpr ulonglong hour{60 * min};
 | 
						|
  constexpr ulonglong day{24 * hour};
 | 
						|
 | 
						|
  double time_abs= fabs(time_val);
 | 
						|
 | 
						|
  ulonglong divisor;
 | 
						|
  size_t len;
 | 
						|
  const char *unit;
 | 
						|
 | 
						|
  /* SI-approved time units. */
 | 
						|
  if (time_abs >= day)
 | 
						|
  {
 | 
						|
    divisor= day;
 | 
						|
    unit= "d";
 | 
						|
  }
 | 
						|
  else if (time_abs >= hour)
 | 
						|
  {
 | 
						|
    divisor= hour;
 | 
						|
    unit= "h";
 | 
						|
  }
 | 
						|
  else if (time_abs >= min)
 | 
						|
  {
 | 
						|
    divisor= min;
 | 
						|
    unit= "min";
 | 
						|
  }
 | 
						|
  else if (time_abs >= sec)
 | 
						|
  {
 | 
						|
    divisor= sec;
 | 
						|
    unit= "s";
 | 
						|
  }
 | 
						|
  else if (time_abs >= milli)
 | 
						|
  {
 | 
						|
    divisor= milli;
 | 
						|
    unit= "ms";
 | 
						|
  }
 | 
						|
  else if (time_abs >= micro)
 | 
						|
  {
 | 
						|
    divisor= micro;
 | 
						|
    unit= "us";
 | 
						|
  }
 | 
						|
  else if (time_abs >= nano)
 | 
						|
  {
 | 
						|
    divisor= nano;
 | 
						|
    unit= "ns";
 | 
						|
  }
 | 
						|
  else
 | 
						|
  {
 | 
						|
    divisor= 1;
 | 
						|
    unit= "ps";
 | 
						|
  }
 | 
						|
 | 
						|
  if (divisor == 1)
 | 
						|
    len= my_snprintf(m_value_buffer, sizeof(m_value_buffer), "%3d %s", (int)time_val, unit);
 | 
						|
  else
 | 
						|
  {
 | 
						|
    double value= time_val / divisor;
 | 
						|
    if (fabs(value) >= 100000.0)
 | 
						|
      len= snprintf(m_value_buffer, sizeof(m_value_buffer), "%4.2e %s", value, unit);
 | 
						|
    else
 | 
						|
      len= my_snprintf(m_value_buffer, sizeof(m_value_buffer), "%4.2f %s", value, unit);
 | 
						|
  }
 | 
						|
  m_value.length(len);
 | 
						|
  return &m_value;
 | 
						|
}
 | 
						|
 | 
						|
#ifdef WITH_WSREP
 | 
						|
#include "wsrep_mysqld.h"
 | 
						|
#include "wsrep_server_state.h"
 | 
						|
/* Format is %d-%d-%llu */
 | 
						|
#define WSREP_MAX_WSREP_SERVER_GTID_STR_LEN 10+1+10+1+20
 | 
						|
 | 
						|
String *Item_func_wsrep_last_written_gtid::val_str_ascii(String *str)
 | 
						|
{
 | 
						|
  if (gtid_str.alloc(WSREP_MAX_WSREP_SERVER_GTID_STR_LEN+1))
 | 
						|
  {
 | 
						|
    my_error(ER_OUTOFMEMORY, MYF(0), WSREP_MAX_WSREP_SERVER_GTID_STR_LEN);
 | 
						|
    null_value= TRUE;
 | 
						|
    return 0;
 | 
						|
  }
 | 
						|
 | 
						|
  ssize_t gtid_len= my_snprintf((char*)gtid_str.ptr(), 
 | 
						|
                                WSREP_MAX_WSREP_SERVER_GTID_STR_LEN+1,
 | 
						|
                                "%u-%u-%llu", wsrep_gtid_server.domain_id,
 | 
						|
                                wsrep_gtid_server.server_id,
 | 
						|
                                current_thd->wsrep_last_written_gtid_seqno);
 | 
						|
  if (gtid_len < 0)
 | 
						|
  {
 | 
						|
    my_error(ER_ERROR_WHEN_EXECUTING_COMMAND, MYF(0), func_name(),
 | 
						|
            "wsrep_gtid_print failed");
 | 
						|
    null_value= TRUE;
 | 
						|
    return 0;
 | 
						|
  }
 | 
						|
  gtid_str.length(gtid_len);
 | 
						|
  return >id_str;
 | 
						|
}
 | 
						|
 | 
						|
String *Item_func_wsrep_last_seen_gtid::val_str_ascii(String *str)
 | 
						|
{
 | 
						|
  if (gtid_str.alloc(WSREP_MAX_WSREP_SERVER_GTID_STR_LEN+1))
 | 
						|
  {
 | 
						|
    my_error(ER_OUTOFMEMORY, MYF(0), WSREP_MAX_WSREP_SERVER_GTID_STR_LEN);
 | 
						|
    null_value= TRUE;
 | 
						|
    return 0;
 | 
						|
  }
 | 
						|
  ssize_t gtid_len= my_snprintf((char*)gtid_str.ptr(), 
 | 
						|
                                WSREP_MAX_WSREP_SERVER_GTID_STR_LEN+1,
 | 
						|
                                "%u-%u-%llu", wsrep_gtid_server.domain_id,
 | 
						|
                                wsrep_gtid_server.server_id,
 | 
						|
                                wsrep_gtid_server.seqno());
 | 
						|
  if (gtid_len < 0)
 | 
						|
  {
 | 
						|
    my_error(ER_ERROR_WHEN_EXECUTING_COMMAND, MYF(0), func_name(),
 | 
						|
             "wsrep_gtid_print failed");
 | 
						|
    null_value= TRUE;
 | 
						|
    return 0;
 | 
						|
  }
 | 
						|
  gtid_str.length(gtid_len);
 | 
						|
  return >id_str;
 | 
						|
}
 | 
						|
 | 
						|
longlong Item_func_wsrep_sync_wait_upto::val_int()
 | 
						|
{
 | 
						|
  String *gtid_str __attribute__((unused)) = args[0]->val_str(&value);
 | 
						|
  null_value=0;
 | 
						|
  uint timeout;
 | 
						|
  rpl_gtid *gtid_list;
 | 
						|
  uint32 count;
 | 
						|
  int wait_gtid_ret= 0;
 | 
						|
  int ret= 1;
 | 
						|
 | 
						|
  if (args[0]->null_value)
 | 
						|
  {
 | 
						|
    my_error(ER_WRONG_ARGUMENTS, MYF(0), func_name());
 | 
						|
    null_value= TRUE;
 | 
						|
    return 0;
 | 
						|
  }
 | 
						|
 | 
						|
  if (arg_count==2 && !args[1]->null_value)
 | 
						|
    timeout= (uint)(args[1]->val_real());
 | 
						|
  else
 | 
						|
    timeout= (uint)-1;
 | 
						|
 | 
						|
  if (!(gtid_list= gtid_parse_string_to_list(gtid_str->ptr(), gtid_str->length(),
 | 
						|
                                             &count)))
 | 
						|
  {
 | 
						|
    my_error(ER_INCORRECT_GTID_STATE, MYF(0));
 | 
						|
    null_value= TRUE;
 | 
						|
    return 0;
 | 
						|
  }
 | 
						|
  if (count == 1)
 | 
						|
  {
 | 
						|
    if (wsrep_check_gtid_seqno(gtid_list[0].domain_id, gtid_list[0].server_id,
 | 
						|
                               gtid_list[0].seq_no))
 | 
						|
    {
 | 
						|
      wait_gtid_ret= wsrep_gtid_server.wait_gtid_upto(gtid_list[0].seq_no, timeout);
 | 
						|
      if ((wait_gtid_ret == ETIMEDOUT) || (wait_gtid_ret == ETIME))
 | 
						|
      {
 | 
						|
        my_error(ER_LOCK_WAIT_TIMEOUT, MYF(0));
 | 
						|
        ret= 0;
 | 
						|
      }
 | 
						|
      else if (wait_gtid_ret == ENOMEM)
 | 
						|
      {
 | 
						|
        my_error(ER_OUTOFMEMORY, MYF(0), sizeof(std::pair<uint64, mysql_cond_t*>));
 | 
						|
        ret= 0;
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
  else
 | 
						|
  { 
 | 
						|
    my_error(ER_WRONG_ARGUMENTS, MYF(0), func_name());
 | 
						|
    null_value= TRUE;
 | 
						|
    ret= 0;
 | 
						|
  }
 | 
						|
  my_free(gtid_list);
 | 
						|
  return ret;
 | 
						|
}
 | 
						|
 | 
						|
#endif /* WITH_WSREP */
 |