mirror of
				https://github.com/MariaDB/server.git
				synced 2025-11-03 20:36:16 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			7399 lines
		
	
	
	
		
			192 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			7399 lines
		
	
	
	
		
			192 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/* Copyright (c) 2000, 2015, Oracle and/or its affiliates.
 | 
						|
   Copyright (c) 2009, 2022, MariaDB
 | 
						|
 | 
						|
   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 numerical functions
 | 
						|
*/
 | 
						|
 | 
						|
#ifdef USE_PRAGMA_IMPLEMENTATION
 | 
						|
#pragma implementation				// gcc: Class implementation
 | 
						|
#endif
 | 
						|
 | 
						|
#include "sql_plugin.h"
 | 
						|
#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 "slave.h"				// for wait_for_master_pos
 | 
						|
#include "sql_show.h"                           // append_identifier
 | 
						|
#include "strfunc.h"                            // find_type
 | 
						|
#include "sql_parse.h"                          // is_update_query
 | 
						|
#include "sql_acl.h"                            // EXECUTE_ACL
 | 
						|
#include "mysqld.h"                             // LOCK_short_uuid_generator
 | 
						|
#include "rpl_mi.h"
 | 
						|
#include "sql_time.h"
 | 
						|
#include <m_ctype.h>
 | 
						|
#include <hash.h>
 | 
						|
#include <time.h>
 | 
						|
#include <ft_global.h>
 | 
						|
#include <my_bit.h>
 | 
						|
 | 
						|
#include "sp_head.h"
 | 
						|
#include "sp_rcontext.h"
 | 
						|
#include "sp.h"
 | 
						|
#include "set_var.h"
 | 
						|
#include "debug_sync.h"
 | 
						|
#include "sql_base.h"
 | 
						|
#include "sql_cte.h"
 | 
						|
#ifdef WITH_WSREP
 | 
						|
#include "mysql/service_wsrep.h"
 | 
						|
#endif /* WITH_WSREP */
 | 
						|
 | 
						|
#ifdef NO_EMBEDDED_ACCESS_CHECKS
 | 
						|
#define sp_restore_security_context(A,B) while (0) {}
 | 
						|
#endif
 | 
						|
 | 
						|
bool check_reserved_words(const LEX_CSTRING *name)
 | 
						|
{
 | 
						|
  if (lex_string_eq(name, STRING_WITH_LEN("GLOBAL")) ||
 | 
						|
      lex_string_eq(name, STRING_WITH_LEN("LOCAL")) ||
 | 
						|
      lex_string_eq(name, STRING_WITH_LEN("SESSION")))
 | 
						|
    return TRUE;
 | 
						|
  return FALSE;
 | 
						|
}
 | 
						|
 | 
						|
void pause_execution(THD *thd, double timeout);
 | 
						|
static int do_pause(THD *thd, Interruptible_wait *timed_cond,
 | 
						|
                    mysql_cond_t *cond, double timeout);
 | 
						|
 | 
						|
/**
 | 
						|
   Test if the sum of arguments overflows the ulonglong range.
 | 
						|
*/
 | 
						|
static inline bool test_if_sum_overflows_ull(ulonglong arg1, ulonglong arg2)
 | 
						|
{
 | 
						|
  return ULonglong::test_if_sum_overflows_ull(arg1, arg2);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Allocate memory for arguments using tmp_args or thd->alloc().
 | 
						|
  @retval false  - success
 | 
						|
  @retval true   - error (arg_count is set to 0 for conveniece)
 | 
						|
*/
 | 
						|
bool Item_args::alloc_arguments(THD *thd, uint count)
 | 
						|
{
 | 
						|
  if (count <= 2)
 | 
						|
  {
 | 
						|
    args= tmp_arg;
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
  if ((args= (Item**) thd->alloc(sizeof(Item*) * count)) == NULL)
 | 
						|
  {
 | 
						|
    arg_count= 0;
 | 
						|
    return true;
 | 
						|
  }
 | 
						|
  return false;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void Item_args::set_arguments(THD *thd, List<Item> &list)
 | 
						|
{
 | 
						|
  if (alloc_arguments(thd, list.elements))
 | 
						|
    return;
 | 
						|
  List_iterator_fast<Item> li(list);
 | 
						|
  Item *item;
 | 
						|
  for (arg_count= 0; (item= li++); )
 | 
						|
    args[arg_count++]= item;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
Item_args::Item_args(THD *thd, const Item_args *other)
 | 
						|
  :arg_count(other->arg_count)
 | 
						|
{
 | 
						|
  if (arg_count <= 2)
 | 
						|
  {
 | 
						|
    args= tmp_arg;
 | 
						|
  }
 | 
						|
  else if (!(args= (Item**) thd->alloc(sizeof(Item*) * arg_count)))
 | 
						|
  {
 | 
						|
    arg_count= 0;
 | 
						|
    return;
 | 
						|
  }
 | 
						|
  if (arg_count)
 | 
						|
    memcpy(args, other->args, sizeof(Item*) * arg_count);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void Item_func::wrong_param_count_error(const LEX_CSTRING &schema_name,
 | 
						|
                                        const LEX_CSTRING &func_name)
 | 
						|
{
 | 
						|
  DBUG_ASSERT(schema_name.length);
 | 
						|
  Database_qualified_name qname(schema_name, func_name);
 | 
						|
  my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0),
 | 
						|
           ErrConvDQName(&qname).ptr());
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void Item_func::sync_with_sum_func_and_with_field(List<Item> &list)
 | 
						|
{
 | 
						|
  List_iterator_fast<Item> li(list);
 | 
						|
  Item *item;
 | 
						|
  while ((item= li++))
 | 
						|
    with_flags|= item->with_flags;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool Item_func::check_argument_types_like_args0() const
 | 
						|
{
 | 
						|
  if (arg_count < 2)
 | 
						|
    return false;
 | 
						|
  uint cols= args[0]->cols();
 | 
						|
  bool is_scalar= args[0]->type_handler()->is_scalar_type();
 | 
						|
  for (uint i= 1; i < arg_count; i++)
 | 
						|
  {
 | 
						|
    if (is_scalar != args[i]->type_handler()->is_scalar_type())
 | 
						|
    {
 | 
						|
      my_error(ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION, MYF(0),
 | 
						|
               args[0]->type_handler()->name().ptr(),
 | 
						|
               args[i]->type_handler()->name().ptr(), func_name());
 | 
						|
      return true;
 | 
						|
    }
 | 
						|
    if (args[i]->check_cols(cols))
 | 
						|
      return true;
 | 
						|
  }
 | 
						|
  return false;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool Item_func::check_argument_types_or_binary(const Type_handler *handler,
 | 
						|
                                               uint start, uint end) const
 | 
						|
{
 | 
						|
  for (uint i= start; i < end ; i++)
 | 
						|
  {
 | 
						|
    DBUG_ASSERT(i < arg_count);
 | 
						|
    if (args[i]->check_type_or_binary(func_name_cstring(), handler))
 | 
						|
      return true;
 | 
						|
  }
 | 
						|
  return false;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool Item_func::check_argument_types_traditional_scalar(uint start,
 | 
						|
                                                        uint end) const
 | 
						|
{
 | 
						|
  for (uint i= start; i < end ; i++)
 | 
						|
  {
 | 
						|
    DBUG_ASSERT(i < arg_count);
 | 
						|
    if (args[i]->check_type_traditional_scalar(func_name_cstring()))
 | 
						|
      return true;
 | 
						|
  }
 | 
						|
  return false;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool Item_func::check_argument_types_can_return_int(uint start,
 | 
						|
                                                    uint end) const
 | 
						|
{
 | 
						|
  for (uint i= start; i < end ; i++)
 | 
						|
  {
 | 
						|
    DBUG_ASSERT(i < arg_count);
 | 
						|
    if (args[i]->check_type_can_return_int(func_name_cstring()))
 | 
						|
      return true;
 | 
						|
  }
 | 
						|
  return false;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool Item_func::check_argument_types_can_return_real(uint start,
 | 
						|
                                                     uint end) const
 | 
						|
{
 | 
						|
  for (uint i= start; i < end ; i++)
 | 
						|
  {
 | 
						|
    DBUG_ASSERT(i < arg_count);
 | 
						|
    if (args[i]->check_type_can_return_real(func_name_cstring()))
 | 
						|
      return true;
 | 
						|
  }
 | 
						|
  return false;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool Item_func::check_argument_types_can_return_text(uint start,
 | 
						|
                                                     uint end) const
 | 
						|
{
 | 
						|
  for (uint i= start; i < end ; i++)
 | 
						|
  {
 | 
						|
    DBUG_ASSERT(i < arg_count);
 | 
						|
    if (args[i]->check_type_can_return_text(func_name_cstring()))
 | 
						|
      return true;
 | 
						|
  }
 | 
						|
  return false;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool Item_func::check_argument_types_can_return_str(uint start,
 | 
						|
                                                    uint end) const
 | 
						|
{
 | 
						|
  for (uint i= start; i < end ; i++)
 | 
						|
  {
 | 
						|
    DBUG_ASSERT(i < arg_count);
 | 
						|
    if (args[i]->check_type_can_return_str(func_name_cstring()))
 | 
						|
      return true;
 | 
						|
  }
 | 
						|
  return false;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool Item_func::check_argument_types_can_return_date(uint start,
 | 
						|
                                                     uint end) const
 | 
						|
{
 | 
						|
  for (uint i= start; i < end ; i++)
 | 
						|
  {
 | 
						|
    DBUG_ASSERT(i < arg_count);
 | 
						|
    if (args[i]->check_type_can_return_date(func_name_cstring()))
 | 
						|
      return true;
 | 
						|
  }
 | 
						|
  return false;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool Item_func::check_argument_types_can_return_time(uint start,
 | 
						|
                                                     uint end) const
 | 
						|
{
 | 
						|
  for (uint i= start; i < end ; i++)
 | 
						|
  {
 | 
						|
    DBUG_ASSERT(i < arg_count);
 | 
						|
    if (args[i]->check_type_can_return_time(func_name_cstring()))
 | 
						|
      return true;
 | 
						|
  }
 | 
						|
  return false;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool Item_func::check_argument_types_scalar(uint start, uint end) const
 | 
						|
{
 | 
						|
  for (uint i= start; i < end; i++)
 | 
						|
  {
 | 
						|
    DBUG_ASSERT(i < arg_count);
 | 
						|
    if (args[i]->check_type_scalar(func_name_cstring()))
 | 
						|
      return true;
 | 
						|
  }
 | 
						|
  return false;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
  Resolve references to table column for a function and its argument
 | 
						|
 | 
						|
  SYNOPSIS:
 | 
						|
  fix_fields()
 | 
						|
  thd		Thread object
 | 
						|
  ref		Pointer to where this object is used.  This reference
 | 
						|
		is used if we want to replace this object with another
 | 
						|
		one (for example in the summary functions).
 | 
						|
 | 
						|
  DESCRIPTION
 | 
						|
    Call fix_fields() for all arguments to the function.  The main intention
 | 
						|
    is to allow all Item_field() objects to setup pointers to the table fields.
 | 
						|
 | 
						|
    Sets as a side effect the following class variables:
 | 
						|
      maybe_null        Set if any argument may return NULL
 | 
						|
      with_sum_func     Set if any of the arguments contains a sum function
 | 
						|
      with_window_func()  Set if any of the arguments contain a window function
 | 
						|
      with_field        Set if any of the arguments contains or is a field
 | 
						|
      used_tables_cache Set to union of the tables used by arguments
 | 
						|
 | 
						|
      str_value.charset If this is a string function, set this to the
 | 
						|
			character set for the first argument.
 | 
						|
			If any argument is binary, this is set to binary
 | 
						|
 | 
						|
   If for any item any of the defaults are wrong, then this can
 | 
						|
   be fixed in the fix_length_and_dec() function that is called
 | 
						|
   after this one or by writing a specialized fix_fields() for the
 | 
						|
   item.
 | 
						|
 | 
						|
  RETURN VALUES
 | 
						|
  FALSE	ok
 | 
						|
  TRUE	Got error.  Stored with my_error().
 | 
						|
*/
 | 
						|
 | 
						|
bool
 | 
						|
Item_func::fix_fields(THD *thd, Item **ref)
 | 
						|
{
 | 
						|
  DBUG_ASSERT(fixed() == 0);
 | 
						|
  Item **arg,**arg_end;
 | 
						|
  uchar buff[STACK_BUFF_ALLOC];			// Max argument in function
 | 
						|
 | 
						|
  /*
 | 
						|
    The Used_tables_and_const_cache of "this" was initialized by
 | 
						|
    the constructor, or by Item_func::cleanup().
 | 
						|
  */
 | 
						|
  DBUG_ASSERT(used_tables_cache == 0);
 | 
						|
  DBUG_ASSERT(const_item_cache == true);
 | 
						|
 | 
						|
  not_null_tables_cache= 0;
 | 
						|
 | 
						|
  /*
 | 
						|
    Use stack limit of STACK_MIN_SIZE * 2 since
 | 
						|
    on some platforms a recursive call to fix_fields
 | 
						|
    requires more than STACK_MIN_SIZE bytes (e.g. for
 | 
						|
    MIPS, it takes about 22kB to make one recursive
 | 
						|
    call to Item_func::fix_fields())
 | 
						|
  */
 | 
						|
  if (check_stack_overrun(thd, STACK_MIN_SIZE * 2, buff))
 | 
						|
    return TRUE;				// Fatal error if flag is set!
 | 
						|
  if (arg_count)
 | 
						|
  {						// Print purify happy
 | 
						|
    for (arg=args, arg_end=args+arg_count; arg != arg_end ; arg++)
 | 
						|
    {
 | 
						|
      Item *item;
 | 
						|
      /*
 | 
						|
	We can't yet set item to *arg as fix_fields may change *arg
 | 
						|
	We shouldn't call fix_fields() twice, so check 'fixed' field first
 | 
						|
      */
 | 
						|
      if ((*arg)->fix_fields_if_needed(thd, arg))
 | 
						|
      {
 | 
						|
        cleanup();
 | 
						|
	return TRUE;				/* purecov: inspected */
 | 
						|
      }
 | 
						|
      item= *arg;
 | 
						|
 | 
						|
      base_flags|= item->base_flags & item_base_t::MAYBE_NULL;
 | 
						|
      with_flags|= item->with_flags;
 | 
						|
      used_tables_and_const_cache_join(item);
 | 
						|
      not_null_tables_cache|= item->not_null_tables();
 | 
						|
    }
 | 
						|
  }
 | 
						|
  if (check_arguments())
 | 
						|
  {
 | 
						|
    cleanup();
 | 
						|
    return true;
 | 
						|
  }
 | 
						|
  if (fix_length_and_dec(thd))
 | 
						|
  {
 | 
						|
    cleanup();
 | 
						|
    return TRUE;
 | 
						|
  }
 | 
						|
  base_flags|= item_base_t::FIXED;
 | 
						|
 | 
						|
  return FALSE;
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
Item_func::quick_fix_field()
 | 
						|
{
 | 
						|
  Item **arg,**arg_end;
 | 
						|
  if (arg_count)
 | 
						|
  {
 | 
						|
    for (arg=args, arg_end=args+arg_count; arg != arg_end ; arg++)
 | 
						|
    {
 | 
						|
      if (!(*arg)->fixed())
 | 
						|
        (*arg)->quick_fix_field();
 | 
						|
    }
 | 
						|
  }
 | 
						|
  base_flags|= item_base_t::FIXED;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool
 | 
						|
Item_func::eval_not_null_tables(void *opt_arg)
 | 
						|
{
 | 
						|
  Item **arg,**arg_end;
 | 
						|
  not_null_tables_cache= 0;
 | 
						|
  if (arg_count)
 | 
						|
  {		
 | 
						|
    for (arg=args, arg_end=args+arg_count; arg != arg_end ; arg++)
 | 
						|
    {
 | 
						|
      not_null_tables_cache|= (*arg)->not_null_tables();
 | 
						|
    }
 | 
						|
  }
 | 
						|
  return FALSE;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool
 | 
						|
Item_func::find_not_null_fields(table_map allowed)
 | 
						|
{
 | 
						|
  if (~allowed & used_tables())
 | 
						|
    return false;
 | 
						|
 | 
						|
  Item **arg,**arg_end;
 | 
						|
  if (arg_count)
 | 
						|
  {
 | 
						|
    for (arg=args, arg_end=args+arg_count; arg != arg_end ; arg++)
 | 
						|
    {
 | 
						|
      if (!(*arg)->find_not_null_fields(allowed))
 | 
						|
        continue;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  return false;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void Item_func::fix_after_pullout(st_select_lex *new_parent, Item **ref,
 | 
						|
                                  bool merge)
 | 
						|
{
 | 
						|
  Item **arg,**arg_end;
 | 
						|
 | 
						|
  used_tables_and_const_cache_init();
 | 
						|
  not_null_tables_cache= 0;
 | 
						|
 | 
						|
  if (arg_count)
 | 
						|
  {
 | 
						|
    for (arg=args, arg_end=args+arg_count; arg != arg_end ; arg++)
 | 
						|
    {
 | 
						|
      (*arg)->fix_after_pullout(new_parent, arg, merge);
 | 
						|
      Item *item= *arg;
 | 
						|
 | 
						|
      used_tables_and_const_cache_join(item);
 | 
						|
      not_null_tables_cache|= item->not_null_tables();
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void Item_func::traverse_cond(Cond_traverser traverser,
 | 
						|
                              void *argument, traverse_order order)
 | 
						|
{
 | 
						|
  if (arg_count)
 | 
						|
  {
 | 
						|
    Item **arg,**arg_end;
 | 
						|
 | 
						|
    switch (order) {
 | 
						|
    case(PREFIX):
 | 
						|
      (*traverser)(this, argument);
 | 
						|
      for (arg= args, arg_end= args+arg_count; arg != arg_end; arg++)
 | 
						|
      {
 | 
						|
	(*arg)->traverse_cond(traverser, argument, order);
 | 
						|
      }
 | 
						|
      break;
 | 
						|
    case (POSTFIX):
 | 
						|
      for (arg= args, arg_end= args+arg_count; arg != arg_end; arg++)
 | 
						|
      {
 | 
						|
	(*arg)->traverse_cond(traverser, argument, order);
 | 
						|
      }
 | 
						|
      (*traverser)(this, argument);
 | 
						|
    }
 | 
						|
  }
 | 
						|
  else
 | 
						|
    (*traverser)(this, argument);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool Item_args::transform_args(THD *thd, Item_transformer transformer, uchar *arg)
 | 
						|
{
 | 
						|
  for (uint i= 0; i < arg_count; i++)
 | 
						|
  {
 | 
						|
    Item *new_item= args[i]->transform(thd, transformer, arg);
 | 
						|
    if (!new_item)
 | 
						|
      return true;
 | 
						|
    /*
 | 
						|
      THD::change_item_tree() should be called only if the tree was
 | 
						|
      really transformed, i.e. when a new item has been created.
 | 
						|
      Otherwise we'll be allocating a lot of unnecessary memory for
 | 
						|
      change records at each execution.
 | 
						|
    */
 | 
						|
    if (args[i] != new_item)
 | 
						|
      thd->change_item_tree(&args[i], new_item);
 | 
						|
  }
 | 
						|
  return false;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Transform an Item_func object with a transformer callback function.
 | 
						|
 | 
						|
    The function recursively applies the transform method to each
 | 
						|
    argument of the Item_func node.
 | 
						|
    If the call of the method for an argument item returns a new item
 | 
						|
    the old item is substituted for a new one.
 | 
						|
    After this the transformer is applied to the root node
 | 
						|
    of the Item_func object. 
 | 
						|
  @param transformer   the transformer callback function to be applied to
 | 
						|
                       the nodes of the tree of the object
 | 
						|
  @param argument      parameter to be passed to the transformer
 | 
						|
 | 
						|
  @return
 | 
						|
    Item returned as the result of transformation of the root node
 | 
						|
*/
 | 
						|
 | 
						|
Item *Item_func::transform(THD *thd, Item_transformer transformer, uchar *argument)
 | 
						|
{
 | 
						|
  DBUG_ASSERT(!thd->stmt_arena->is_stmt_prepare());
 | 
						|
  if (transform_args(thd, transformer, argument))
 | 
						|
    return 0;
 | 
						|
  return (this->*transformer)(thd, argument);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Compile Item_func object with a processor and a transformer
 | 
						|
  callback functions.
 | 
						|
 | 
						|
    First the function applies the analyzer to the root node of
 | 
						|
    the Item_func object. Then if the analyzer succeeds (returns TRUE)
 | 
						|
    the function recursively applies the compile method to each argument
 | 
						|
    of the Item_func node.
 | 
						|
    If the call of the method for an argument item returns a new item
 | 
						|
    the old item is substituted for a new one.
 | 
						|
    After this the transformer is applied to the root node
 | 
						|
    of the Item_func object. 
 | 
						|
    The compile function is not called if the analyzer returns NULL
 | 
						|
    in the parameter arg_p. 
 | 
						|
 | 
						|
  @param analyzer      the analyzer callback function to be applied to the
 | 
						|
                       nodes of the tree of the object
 | 
						|
  @param[in,out] arg_p parameter to be passed to the processor
 | 
						|
  @param transformer   the transformer callback function to be applied to the
 | 
						|
                       nodes of the tree of the object
 | 
						|
  @param arg_t         parameter to be passed to the transformer
 | 
						|
 | 
						|
  @return
 | 
						|
    Item returned as the result of transformation of the root node
 | 
						|
*/
 | 
						|
 | 
						|
Item *Item_func::compile(THD *thd, Item_analyzer analyzer, uchar **arg_p,
 | 
						|
                         Item_transformer transformer, uchar *arg_t)
 | 
						|
{
 | 
						|
  if (!(this->*analyzer)(arg_p))
 | 
						|
    return 0;
 | 
						|
  if (*arg_p && arg_count)
 | 
						|
  {
 | 
						|
    Item **arg,**arg_end;
 | 
						|
    for (arg= args, arg_end= args+arg_count; arg != arg_end; arg++)
 | 
						|
    {
 | 
						|
      /* 
 | 
						|
        The same parameter value of arg_p must be passed
 | 
						|
        to analyze any argument of the condition formula.
 | 
						|
      */
 | 
						|
      uchar *arg_v= *arg_p;
 | 
						|
      Item *new_item= (*arg)->compile(thd, analyzer, &arg_v, transformer,
 | 
						|
                                      arg_t);
 | 
						|
      if (new_item && *arg != new_item)
 | 
						|
        thd->change_item_tree(arg, new_item);
 | 
						|
    }
 | 
						|
  }
 | 
						|
  return (this->*transformer)(thd, arg_t);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void Item_args::propagate_equal_fields(THD *thd,
 | 
						|
                                       const Item::Context &ctx,
 | 
						|
                                       COND_EQUAL *cond)
 | 
						|
{
 | 
						|
  uint i;
 | 
						|
  for (i= 0; i < arg_count; i++)
 | 
						|
    args[i]->propagate_equal_fields_and_change_item_tree(thd, ctx, cond,
 | 
						|
                                                         &args[i]);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
Sql_mode_dependency Item_args::value_depends_on_sql_mode_bit_or() const
 | 
						|
{
 | 
						|
  Sql_mode_dependency res;
 | 
						|
  for (uint i= 0; i < arg_count; i++)
 | 
						|
    res|= args[i]->value_depends_on_sql_mode();
 | 
						|
  return res;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  See comments in Item_cond::split_sum_func()
 | 
						|
*/
 | 
						|
 | 
						|
void Item_func::split_sum_func(THD *thd,  Ref_ptr_array ref_pointer_array,
 | 
						|
                               List<Item> &fields, uint flags)
 | 
						|
{
 | 
						|
  Item **arg, **arg_end;
 | 
						|
  DBUG_ENTER("Item_func::split_sum_func");
 | 
						|
 | 
						|
  for (arg= args, arg_end= args+arg_count; arg != arg_end ; arg++)
 | 
						|
    (*arg)->split_sum_func2(thd, ref_pointer_array, fields, arg,
 | 
						|
                            flags | SPLIT_SUM_SKIP_REGISTERED);
 | 
						|
  DBUG_VOID_RETURN;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
table_map Item_func::not_null_tables() const
 | 
						|
{
 | 
						|
  return not_null_tables_cache;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void Item_func::print(String *str, enum_query_type query_type)
 | 
						|
{
 | 
						|
  str->append(func_name_cstring());
 | 
						|
  print_args_parenthesized(str, query_type);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void Item_func::print_args(String *str, uint from,
 | 
						|
                           enum_query_type query_type) const
 | 
						|
{
 | 
						|
  for (uint i=from ; i < arg_count ; i++)
 | 
						|
  {
 | 
						|
    if (i != from)
 | 
						|
      str->append(',');
 | 
						|
    args[i]->print(str, query_type);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void Item_func::print_op(String *str, enum_query_type query_type)
 | 
						|
{
 | 
						|
  for (uint i=0 ; i < arg_count-1 ; i++)
 | 
						|
  {
 | 
						|
    args[i]->print_parenthesised(str, query_type, precedence());
 | 
						|
    str->append(' ');
 | 
						|
    str->append(func_name_cstring());
 | 
						|
    str->append(' ');
 | 
						|
  }
 | 
						|
  args[arg_count-1]->print_parenthesised(str, query_type, higher_precedence());
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool Item_func::eq(const Item *item, bool binary_cmp) const
 | 
						|
{
 | 
						|
  /* Assume we don't have rtti */
 | 
						|
  if (this == item)
 | 
						|
    return 1;
 | 
						|
  /*
 | 
						|
    Ensure that we are comparing two functions and that the function
 | 
						|
    is deterministic.
 | 
						|
  */
 | 
						|
  if (item->type() != FUNC_ITEM || (used_tables() & RAND_TABLE_BIT))
 | 
						|
    return 0;
 | 
						|
  Item_func *item_func=(Item_func*) item;
 | 
						|
  Item_func::Functype func_type;
 | 
						|
  if ((func_type= functype()) != item_func->functype() ||
 | 
						|
      arg_count != item_func->arg_count ||
 | 
						|
      (func_type != Item_func::FUNC_SP &&
 | 
						|
       func_name() != item_func->func_name()) ||
 | 
						|
      (func_type == Item_func::FUNC_SP &&
 | 
						|
       my_strcasecmp(system_charset_info, func_name(), item_func->func_name())))
 | 
						|
    return 0;
 | 
						|
  return Item_args::eq(item_func, binary_cmp);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
bool Item_func::is_expensive_processor(uchar *arg)
 | 
						|
{
 | 
						|
  return is_expensive();
 | 
						|
}
 | 
						|
*/
 | 
						|
 | 
						|
 | 
						|
bool Item_hybrid_func::fix_attributes(Item **items, uint nitems)
 | 
						|
{
 | 
						|
  bool rc= Item_hybrid_func::type_handler()->
 | 
						|
             Item_hybrid_func_fix_attributes(current_thd,
 | 
						|
                                             func_name_cstring(), this, this,
 | 
						|
                                             items, nitems);
 | 
						|
  DBUG_ASSERT(!rc || current_thd->is_error());
 | 
						|
  return rc;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
String *Item_real_func::val_str(String *str)
 | 
						|
{
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
  double nr= val_real();
 | 
						|
  if (null_value)
 | 
						|
    return 0; /* purecov: inspected */
 | 
						|
  str->set_real(nr, decimals, collation.collation);
 | 
						|
  return str;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
my_decimal *Item_real_func::val_decimal(my_decimal *decimal_value)
 | 
						|
{
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
  double nr= val_real();
 | 
						|
  if (null_value)
 | 
						|
    return 0; /* purecov: inspected */
 | 
						|
  double2my_decimal(E_DEC_FATAL_ERROR, nr, decimal_value);
 | 
						|
  return decimal_value;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
#ifdef HAVE_DLOPEN
 | 
						|
void Item_udf_func::fix_num_length_and_dec()
 | 
						|
{
 | 
						|
  uint fl_length= 0;
 | 
						|
  decimals=0;
 | 
						|
  for (uint i=0 ; i < arg_count ; i++)
 | 
						|
  {
 | 
						|
    set_if_bigger(decimals,args[i]->decimals);
 | 
						|
    set_if_bigger(fl_length, args[i]->max_length);
 | 
						|
  }
 | 
						|
  max_length=float_length(decimals);
 | 
						|
  if (fl_length > max_length)
 | 
						|
  {
 | 
						|
    decimals= NOT_FIXED_DEC;
 | 
						|
    max_length= float_length(NOT_FIXED_DEC);
 | 
						|
  }
 | 
						|
}
 | 
						|
#endif
 | 
						|
 | 
						|
 | 
						|
void Item_func::signal_divide_by_null()
 | 
						|
{
 | 
						|
  THD *thd= current_thd;
 | 
						|
  if (thd->variables.sql_mode & MODE_ERROR_FOR_DIVISION_BY_ZERO)
 | 
						|
    push_warning(thd, Sql_condition::WARN_LEVEL_WARN, ER_DIVISION_BY_ZERO,
 | 
						|
                 ER_THD(thd, ER_DIVISION_BY_ZERO));
 | 
						|
  null_value= 1;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
Item *Item_func::get_tmp_table_item(THD *thd)
 | 
						|
{
 | 
						|
  if (!with_sum_func() && !const_item())
 | 
						|
  {
 | 
						|
    auto item_field= new (thd->mem_root) Item_field(thd, result_field);
 | 
						|
    if (item_field)
 | 
						|
      item_field->set_refers_to_temp_table();
 | 
						|
    return item_field;
 | 
						|
  }
 | 
						|
  return copy_or_same(thd);
 | 
						|
}
 | 
						|
 | 
						|
double Item_int_func::val_real()
 | 
						|
{
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
 | 
						|
  return unsigned_flag ? (double) ((ulonglong) val_int()) : (double) val_int();
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
String *Item_int_func::val_str(String *str)
 | 
						|
{
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
  longlong nr=val_int();
 | 
						|
  if (null_value)
 | 
						|
    return 0;
 | 
						|
  str->set_int(nr, unsigned_flag, collation.collation);
 | 
						|
  return str;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool Item_func_connection_id::fix_length_and_dec(THD *thd)
 | 
						|
{
 | 
						|
  if (Item_long_func::fix_length_and_dec(thd))
 | 
						|
    return TRUE;
 | 
						|
  max_length= 10;
 | 
						|
  return FALSE;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool Item_func_connection_id::fix_fields(THD *thd, Item **ref)
 | 
						|
{
 | 
						|
  if (Item_int_func::fix_fields(thd, ref))
 | 
						|
    return TRUE;
 | 
						|
  thd->used|= THD::THREAD_SPECIFIC_USED;
 | 
						|
  value= thd->variables.pseudo_thread_id;
 | 
						|
  return FALSE;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool Item_num_op::fix_type_handler(const Type_aggregator *aggregator)
 | 
						|
{
 | 
						|
  DBUG_ASSERT(arg_count == 2);
 | 
						|
  const Type_handler *h0= args[0]->cast_to_int_type_handler();
 | 
						|
  const Type_handler *h1= args[1]->cast_to_int_type_handler();
 | 
						|
  if (!aggregate_for_num_op(aggregator, h0, h1))
 | 
						|
    return false;
 | 
						|
  my_error(ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION, MYF(0),
 | 
						|
           h0->name().ptr(), h1->name().ptr(), func_name());
 | 
						|
  return true;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool Item_func_plus::fix_length_and_dec(THD *thd)
 | 
						|
{
 | 
						|
  DBUG_ENTER("Item_func_plus::fix_length_and_dec");
 | 
						|
  DBUG_PRINT("info", ("name %s", func_name()));
 | 
						|
  const Type_aggregator *aggregator= &type_handler_data->m_type_aggregator_for_plus;
 | 
						|
  DBUG_EXECUTE_IF("num_op", aggregator= &type_handler_data->m_type_aggregator_for_result;);
 | 
						|
  DBUG_ASSERT(aggregator->is_commutative());
 | 
						|
  if (fix_type_handler(aggregator))
 | 
						|
    DBUG_RETURN(TRUE);
 | 
						|
  if (Item_func_plus::type_handler()->Item_func_plus_fix_length_and_dec(this))
 | 
						|
    DBUG_RETURN(TRUE);
 | 
						|
  DBUG_PRINT("info", ("Type: %s", type_handler()->name().ptr()));
 | 
						|
  DBUG_RETURN(FALSE);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
String *Item_func_hybrid_field_type::val_str_from_int_op(String *str)
 | 
						|
{
 | 
						|
  longlong nr= int_op();
 | 
						|
  if (null_value)
 | 
						|
    return 0; /* purecov: inspected */
 | 
						|
  str->set_int(nr, unsigned_flag, collation.collation);
 | 
						|
  return str;
 | 
						|
}
 | 
						|
 | 
						|
double Item_func_hybrid_field_type::val_real_from_int_op()
 | 
						|
{
 | 
						|
  longlong result= int_op();
 | 
						|
  return unsigned_flag ? (double) ((ulonglong) result) : (double) result;
 | 
						|
}
 | 
						|
 | 
						|
my_decimal *
 | 
						|
Item_func_hybrid_field_type::val_decimal_from_int_op(my_decimal *dec)
 | 
						|
{
 | 
						|
  longlong result= int_op();
 | 
						|
  if (null_value)
 | 
						|
    return NULL;
 | 
						|
  int2my_decimal(E_DEC_FATAL_ERROR, result, unsigned_flag, dec);
 | 
						|
  return dec;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
String *Item_func_hybrid_field_type::val_str_from_real_op(String *str)
 | 
						|
{
 | 
						|
  double nr= real_op();
 | 
						|
  if (null_value)
 | 
						|
    return 0; /* purecov: inspected */
 | 
						|
  str->set_real(nr, decimals, collation.collation);
 | 
						|
  return str;
 | 
						|
}
 | 
						|
 | 
						|
longlong Item_func_hybrid_field_type::val_int_from_real_op()
 | 
						|
{
 | 
						|
  return Converter_double_to_longlong(real_op(), unsigned_flag).result();
 | 
						|
}
 | 
						|
 | 
						|
my_decimal *
 | 
						|
Item_func_hybrid_field_type::val_decimal_from_real_op(my_decimal *dec)
 | 
						|
{
 | 
						|
  double result= (double) real_op();
 | 
						|
  if (null_value)
 | 
						|
    return NULL;
 | 
						|
  double2my_decimal(E_DEC_FATAL_ERROR, result, dec);
 | 
						|
  return dec;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
String *Item_func_hybrid_field_type::val_str_from_date_op(String *str)
 | 
						|
{
 | 
						|
  MYSQL_TIME ltime;
 | 
						|
  if (date_op_with_null_check(current_thd, <ime) ||
 | 
						|
      (null_value= str->alloc(MAX_DATE_STRING_REP_LENGTH)))
 | 
						|
    return (String *) 0;
 | 
						|
  str->length(my_TIME_to_str(<ime, const_cast<char*>(str->ptr()), decimals));
 | 
						|
  str->set_charset(&my_charset_bin);
 | 
						|
  DBUG_ASSERT(!null_value);
 | 
						|
  return str;
 | 
						|
}
 | 
						|
 | 
						|
double Item_func_hybrid_field_type::val_real_from_date_op()
 | 
						|
{
 | 
						|
  MYSQL_TIME ltime;
 | 
						|
  if (date_op_with_null_check(current_thd, <ime))
 | 
						|
    return 0;
 | 
						|
  return TIME_to_double(<ime);
 | 
						|
}
 | 
						|
 | 
						|
longlong Item_func_hybrid_field_type::val_int_from_date_op()
 | 
						|
{
 | 
						|
  MYSQL_TIME ltime;
 | 
						|
  if (date_op_with_null_check(current_thd, <ime))
 | 
						|
    return 0;
 | 
						|
  return TIME_to_ulonglong(<ime);
 | 
						|
}
 | 
						|
 | 
						|
my_decimal *
 | 
						|
Item_func_hybrid_field_type::val_decimal_from_date_op(my_decimal *dec)
 | 
						|
{
 | 
						|
  MYSQL_TIME ltime;
 | 
						|
  if (date_op_with_null_check(current_thd, <ime))
 | 
						|
  {
 | 
						|
    my_decimal_set_zero(dec);
 | 
						|
    return 0;
 | 
						|
  }
 | 
						|
  return date2my_decimal(<ime, dec);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
String *Item_func_hybrid_field_type::val_str_from_time_op(String *str)
 | 
						|
{
 | 
						|
  MYSQL_TIME ltime;
 | 
						|
  if (time_op_with_null_check(current_thd, <ime) ||
 | 
						|
      (null_value= my_TIME_to_str(<ime, str, decimals)))
 | 
						|
    return NULL;
 | 
						|
  return str;
 | 
						|
}
 | 
						|
 | 
						|
double Item_func_hybrid_field_type::val_real_from_time_op()
 | 
						|
{
 | 
						|
  MYSQL_TIME ltime;
 | 
						|
  return time_op_with_null_check(current_thd, <ime) ? 0 :
 | 
						|
         TIME_to_double(<ime);
 | 
						|
}
 | 
						|
 | 
						|
longlong Item_func_hybrid_field_type::val_int_from_time_op()
 | 
						|
{
 | 
						|
  MYSQL_TIME ltime;
 | 
						|
  return time_op_with_null_check(current_thd, <ime) ? 0 :
 | 
						|
         TIME_to_ulonglong(<ime);
 | 
						|
}
 | 
						|
 | 
						|
my_decimal *
 | 
						|
Item_func_hybrid_field_type::val_decimal_from_time_op(my_decimal *dec)
 | 
						|
{
 | 
						|
  MYSQL_TIME ltime;
 | 
						|
  if (time_op_with_null_check(current_thd, <ime))
 | 
						|
  {
 | 
						|
    my_decimal_set_zero(dec);
 | 
						|
    return 0;
 | 
						|
  }
 | 
						|
  return date2my_decimal(<ime, dec);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
double Item_func_hybrid_field_type::val_real_from_str_op()
 | 
						|
{
 | 
						|
  String *res= str_op_with_null_check(&str_value);
 | 
						|
  return res ? double_from_string_with_check(res) : 0.0;
 | 
						|
}
 | 
						|
 | 
						|
longlong Item_func_hybrid_field_type::val_int_from_str_op()
 | 
						|
{
 | 
						|
  String *res= str_op_with_null_check(&str_value);
 | 
						|
  return res ? longlong_from_string_with_check(res) : 0;
 | 
						|
}
 | 
						|
 | 
						|
my_decimal *
 | 
						|
Item_func_hybrid_field_type::val_decimal_from_str_op(my_decimal *decimal_value)
 | 
						|
{
 | 
						|
  String *res= str_op_with_null_check(&str_value);
 | 
						|
  return res ? decimal_from_string_with_check(decimal_value, res) : 0;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void Item_func_signed::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 signed)"));
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void Item_func_unsigned::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 unsigned)"));
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
my_decimal *Item_decimal_typecast::val_decimal(my_decimal *dec)
 | 
						|
{
 | 
						|
  VDec tmp(args[0]);
 | 
						|
  bool sign;
 | 
						|
  uint precision;
 | 
						|
 | 
						|
  if ((null_value= tmp.is_null()))
 | 
						|
    return NULL;
 | 
						|
  tmp.round_to(dec, decimals, HALF_UP);
 | 
						|
  sign= dec->sign();
 | 
						|
  if (unsigned_flag)
 | 
						|
  {
 | 
						|
    if (sign)
 | 
						|
    {
 | 
						|
      my_decimal_set_zero(dec);
 | 
						|
      goto err;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  precision= my_decimal_length_to_precision(max_length,
 | 
						|
                                            decimals, unsigned_flag);
 | 
						|
  if (precision - decimals < (uint) my_decimal_intg(dec))
 | 
						|
  {
 | 
						|
    max_my_decimal(dec, precision, decimals);
 | 
						|
    dec->sign(sign);
 | 
						|
    goto err;
 | 
						|
  }
 | 
						|
  return dec;
 | 
						|
 | 
						|
err:
 | 
						|
  THD *thd= current_thd;
 | 
						|
  push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
 | 
						|
                      ER_WARN_DATA_OUT_OF_RANGE,
 | 
						|
                      ER_THD(thd, ER_WARN_DATA_OUT_OF_RANGE),
 | 
						|
                      name.str,
 | 
						|
                      thd->get_stmt_da()->current_row_for_warning());
 | 
						|
  return dec;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void Item_decimal_typecast::print(String *str, enum_query_type query_type)
 | 
						|
{
 | 
						|
  char len_buf[20*3 + 1];
 | 
						|
  char *end;
 | 
						|
 | 
						|
  uint precision= my_decimal_length_to_precision(max_length, decimals,
 | 
						|
                                                 unsigned_flag);
 | 
						|
  str->append(STRING_WITH_LEN("cast("));
 | 
						|
  args[0]->print(str, query_type);
 | 
						|
  str->append(STRING_WITH_LEN(" as decimal("));
 | 
						|
 | 
						|
  end=int10_to_str(precision, len_buf,10);
 | 
						|
  str->append(len_buf, (uint32) (end - len_buf));
 | 
						|
 | 
						|
  str->append(',');
 | 
						|
 | 
						|
  end=int10_to_str(decimals, len_buf,10);
 | 
						|
  str->append(len_buf, (uint32) (end - len_buf));
 | 
						|
 | 
						|
  str->append(')');
 | 
						|
  str->append(')');
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
double Item_real_typecast::val_real_with_truncate(double max_value)
 | 
						|
{
 | 
						|
  int error;
 | 
						|
  double tmp= args[0]->val_real();
 | 
						|
  if ((null_value= args[0]->null_value))
 | 
						|
    return 0.0;
 | 
						|
 | 
						|
  if (unlikely((error= truncate_double(&tmp, max_length, decimals,
 | 
						|
                                       false/*unsigned_flag*/, max_value))))
 | 
						|
  {
 | 
						|
    /*
 | 
						|
      We don't want automatic escalation from a warning to an error
 | 
						|
      in this scenario:
 | 
						|
        INSERT INTO t1 (float_field) VALUES (CAST(1e100 AS FLOAT));
 | 
						|
      The above statement should work even in the strict mode.
 | 
						|
      So let's use a note rather than a warning.
 | 
						|
    */
 | 
						|
    THD *thd= current_thd;
 | 
						|
    push_warning_printf(thd,
 | 
						|
                        Sql_condition::WARN_LEVEL_NOTE,
 | 
						|
                        ER_WARN_DATA_OUT_OF_RANGE,
 | 
						|
                        ER_THD(thd, ER_WARN_DATA_OUT_OF_RANGE),
 | 
						|
                        name.str, (ulong) 1);
 | 
						|
    if (error < 0)
 | 
						|
    {
 | 
						|
      null_value= 1;                            // Illegal value
 | 
						|
      tmp= 0.0;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  return tmp;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void Item_real_typecast::print(String *str, enum_query_type query_type)
 | 
						|
{
 | 
						|
  char len_buf[20*3 + 1];
 | 
						|
  char *end;
 | 
						|
  Name name= type_handler()->name();
 | 
						|
 | 
						|
  str->append(STRING_WITH_LEN("cast("));
 | 
						|
  args[0]->print(str, query_type);
 | 
						|
  str->append(STRING_WITH_LEN(" as "));
 | 
						|
  str->append(name.ptr(), name.length());
 | 
						|
  if (decimals != NOT_FIXED_DEC)
 | 
						|
  {
 | 
						|
    str->append('(');
 | 
						|
    end= int10_to_str(max_length, len_buf,10);
 | 
						|
    str->append(len_buf, (uint32) (end - len_buf));
 | 
						|
    str->append(',');
 | 
						|
    end= int10_to_str(decimals, len_buf,10);
 | 
						|
    str->append(len_buf, (uint32) (end - len_buf));
 | 
						|
    str->append(')');
 | 
						|
  }
 | 
						|
  str->append(')');
 | 
						|
}
 | 
						|
 | 
						|
double Item_func_plus::real_op()
 | 
						|
{
 | 
						|
  double value= args[0]->val_real() + args[1]->val_real();
 | 
						|
  if ((null_value=args[0]->null_value || args[1]->null_value))
 | 
						|
    return 0.0;
 | 
						|
  return check_float_overflow(value);
 | 
						|
}
 | 
						|
 | 
						|
#if defined(__powerpc64__) && GCC_VERSION >= 6003 && GCC_VERSION <= 10002
 | 
						|
#pragma GCC push_options
 | 
						|
#pragma GCC optimize ("no-expensive-optimizations")
 | 
						|
#endif
 | 
						|
 | 
						|
longlong Item_func_plus::int_op()
 | 
						|
{
 | 
						|
  longlong val0= args[0]->val_int();
 | 
						|
  longlong val1= args[1]->val_int();
 | 
						|
  bool     res_unsigned= FALSE;
 | 
						|
  longlong res;
 | 
						|
 | 
						|
  if ((null_value= args[0]->null_value || args[1]->null_value))
 | 
						|
    return 0;
 | 
						|
  /*
 | 
						|
    First check whether the result can be represented as a
 | 
						|
    (bool unsigned_flag, longlong value) pair, then check if it is compatible
 | 
						|
    with this Item's unsigned_flag by calling check_integer_overflow().
 | 
						|
  */
 | 
						|
  if (args[0]->unsigned_flag)
 | 
						|
  {
 | 
						|
    if (args[1]->unsigned_flag || val1 >= 0)
 | 
						|
    {
 | 
						|
      if (test_if_sum_overflows_ull((ulonglong) val0, (ulonglong) val1))
 | 
						|
        goto err;
 | 
						|
      res_unsigned= TRUE;
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
      /* val1 is negative */
 | 
						|
      if ((ulonglong) val0 > (ulonglong) LONGLONG_MAX)
 | 
						|
        res_unsigned= TRUE;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  else
 | 
						|
  {
 | 
						|
    if (args[1]->unsigned_flag)
 | 
						|
    {
 | 
						|
      if (val0 >= 0)
 | 
						|
      {
 | 
						|
        if (test_if_sum_overflows_ull((ulonglong) val0, (ulonglong) val1))
 | 
						|
          goto err;
 | 
						|
        res_unsigned= TRUE;
 | 
						|
      }
 | 
						|
      else
 | 
						|
      {
 | 
						|
        if ((ulonglong) val1 > (ulonglong) LONGLONG_MAX)
 | 
						|
          res_unsigned= TRUE;
 | 
						|
      }
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
      if (val0 >=0 && val1 >= 0)
 | 
						|
        res_unsigned= TRUE;
 | 
						|
      else if (val0 < 0 && val1 < 0 && val0 < (LONGLONG_MIN - val1))
 | 
						|
        goto err;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  if (res_unsigned)
 | 
						|
    res= (longlong) ((ulonglong) val0 + (ulonglong) val1);
 | 
						|
  else
 | 
						|
    res= val0 + val1;
 | 
						|
 | 
						|
  return check_integer_overflow(res, res_unsigned);
 | 
						|
 | 
						|
err:
 | 
						|
  return raise_integer_overflow();
 | 
						|
}
 | 
						|
 | 
						|
#if defined(__powerpc64__) && GCC_VERSION >= 6003 && GCC_VERSION <= 10002
 | 
						|
#pragma GCC pop_options
 | 
						|
#endif
 | 
						|
 | 
						|
/**
 | 
						|
  Calculate plus of two decimals.
 | 
						|
 | 
						|
  @param decimal_value	Buffer that can be used to store result
 | 
						|
 | 
						|
  @retval
 | 
						|
    0  Value was NULL;  In this case null_value is set
 | 
						|
  @retval
 | 
						|
    \# Value of operation as a decimal
 | 
						|
*/
 | 
						|
 | 
						|
my_decimal *Item_func_plus::decimal_op(my_decimal *decimal_value)
 | 
						|
{
 | 
						|
  VDec2_lazy val(args[0], args[1]);
 | 
						|
  if (!(null_value= (val.has_null() ||
 | 
						|
                     check_decimal_overflow(my_decimal_add(E_DEC_FATAL_ERROR &
 | 
						|
                                                           ~E_DEC_OVERFLOW,
 | 
						|
                                                           decimal_value,
 | 
						|
                                                           val.m_a.ptr(),
 | 
						|
                                                           val.m_b.ptr())) > 3)))
 | 
						|
    return decimal_value;
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Set precision of results for additive operations (+ and -)
 | 
						|
*/
 | 
						|
void Item_func_additive_op::result_precision()
 | 
						|
{
 | 
						|
  decimals= MY_MAX(args[0]->decimal_scale(), args[1]->decimal_scale());
 | 
						|
  int arg1_int= args[0]->decimal_precision() - args[0]->decimal_scale();
 | 
						|
  int arg2_int= args[1]->decimal_precision() - args[1]->decimal_scale();
 | 
						|
  int precision= MY_MAX(arg1_int, arg2_int) + 1 + decimals;
 | 
						|
 | 
						|
  DBUG_ASSERT(arg1_int >= 0);
 | 
						|
  DBUG_ASSERT(arg2_int >= 0);
 | 
						|
 | 
						|
  max_length= my_decimal_precision_to_length_no_truncation(precision, decimals,
 | 
						|
                                                           unsigned_flag);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  The following function is here to allow the user to force
 | 
						|
  subtraction of UNSIGNED BIGINT to return negative values.
 | 
						|
*/
 | 
						|
void Item_func_minus::fix_unsigned_flag()
 | 
						|
{
 | 
						|
  if (unsigned_flag &&
 | 
						|
      (current_thd->variables.sql_mode & MODE_NO_UNSIGNED_SUBTRACTION))
 | 
						|
  {
 | 
						|
    unsigned_flag=0;
 | 
						|
    set_handler(Item_func_minus::type_handler()->type_handler_signed());
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool Item_func_minus::fix_length_and_dec(THD *thd)
 | 
						|
{
 | 
						|
  DBUG_ENTER("Item_func_minus::fix_length_and_dec");
 | 
						|
  DBUG_PRINT("info", ("name %s", func_name()));
 | 
						|
  const Type_aggregator *aggregator= &type_handler_data->m_type_aggregator_for_minus;
 | 
						|
  DBUG_EXECUTE_IF("num_op", aggregator= &type_handler_data->m_type_aggregator_non_commutative_test;);
 | 
						|
  DBUG_ASSERT(!aggregator->is_commutative());
 | 
						|
  if (fix_type_handler(aggregator))
 | 
						|
    DBUG_RETURN(TRUE);
 | 
						|
  if (Item_func_minus::type_handler()->Item_func_minus_fix_length_and_dec(this))
 | 
						|
    DBUG_RETURN(TRUE);
 | 
						|
  DBUG_PRINT("info", ("Type: %s", type_handler()->name().ptr()));
 | 
						|
  m_depends_on_sql_mode_no_unsigned_subtraction= unsigned_flag;
 | 
						|
  fix_unsigned_flag();
 | 
						|
  DBUG_RETURN(FALSE);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
Sql_mode_dependency Item_func_minus::value_depends_on_sql_mode() const
 | 
						|
{
 | 
						|
  Sql_mode_dependency dep= Item_func_additive_op::value_depends_on_sql_mode();
 | 
						|
  if (m_depends_on_sql_mode_no_unsigned_subtraction)
 | 
						|
    dep|= Sql_mode_dependency(0, MODE_NO_UNSIGNED_SUBTRACTION);
 | 
						|
  return dep;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
double Item_func_minus::real_op()
 | 
						|
{
 | 
						|
  double value= args[0]->val_real() - args[1]->val_real();
 | 
						|
  if ((null_value=args[0]->null_value || args[1]->null_value))
 | 
						|
    return 0.0;
 | 
						|
  return check_float_overflow(value);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
#if defined(__powerpc64__) && GCC_VERSION >= 6003 && GCC_VERSION <= 10002
 | 
						|
#pragma GCC push_options
 | 
						|
#pragma GCC optimize ("no-expensive-optimizations")
 | 
						|
#endif
 | 
						|
 | 
						|
longlong Item_func_minus::int_op()
 | 
						|
{
 | 
						|
  longlong val0= args[0]->val_int();
 | 
						|
  longlong val1= args[1]->val_int();
 | 
						|
  bool     res_unsigned= FALSE;
 | 
						|
  longlong res;
 | 
						|
 | 
						|
  if ((null_value= args[0]->null_value || args[1]->null_value))
 | 
						|
    return 0;
 | 
						|
 | 
						|
  /*
 | 
						|
    First check whether the result can be represented as a
 | 
						|
    (bool unsigned_flag, longlong value) pair, then check if it is compatible
 | 
						|
    with this Item's unsigned_flag by calling check_integer_overflow().
 | 
						|
  */
 | 
						|
  if (args[0]->unsigned_flag)
 | 
						|
  {
 | 
						|
    if (args[1]->unsigned_flag)
 | 
						|
    {
 | 
						|
      if ((ulonglong) val0 >= (ulonglong) val1)
 | 
						|
        res_unsigned= TRUE;
 | 
						|
      else if ((ulonglong)val1 - (ulonglong)val0 > (ulonglong)LONGLONG_MAX)
 | 
						|
        goto err;
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
      if (val1 >= 0)
 | 
						|
      {
 | 
						|
        if ((ulonglong) val0 > (ulonglong) val1)
 | 
						|
          res_unsigned= TRUE;
 | 
						|
      }
 | 
						|
      else
 | 
						|
      {
 | 
						|
        if (test_if_sum_overflows_ull((ulonglong) val0, (ulonglong) -val1))
 | 
						|
          goto err;
 | 
						|
        res_unsigned= TRUE;
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
  else
 | 
						|
  {
 | 
						|
    if (args[1]->unsigned_flag)
 | 
						|
    {
 | 
						|
      if (((ulonglong) val0 - (ulonglong) LONGLONG_MIN) < (ulonglong) val1)
 | 
						|
        goto err;
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
      if (val0 > 0 && val1 < 0)
 | 
						|
        res_unsigned= TRUE;
 | 
						|
      else if (val0 < 0 && val1 > 0 && val0 < (LONGLONG_MIN + val1))
 | 
						|
        goto err;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  if (res_unsigned)
 | 
						|
    res= (longlong) ((ulonglong) val0 - (ulonglong) val1);
 | 
						|
  else
 | 
						|
    res= val0 - val1;
 | 
						|
 | 
						|
  return check_integer_overflow(res, res_unsigned);
 | 
						|
 | 
						|
err:
 | 
						|
  return raise_integer_overflow();
 | 
						|
}
 | 
						|
 | 
						|
#if defined(__powerpc64__) && GCC_VERSION >= 6003 && GCC_VERSION <= 10002
 | 
						|
#pragma GCC pop_options
 | 
						|
#endif
 | 
						|
 | 
						|
/**
 | 
						|
  See Item_func_plus::decimal_op for comments.
 | 
						|
*/
 | 
						|
 | 
						|
my_decimal *Item_func_minus::decimal_op(my_decimal *decimal_value)
 | 
						|
{
 | 
						|
  VDec2_lazy val(args[0], args[1]);
 | 
						|
  if (!(null_value= (val.has_null() ||
 | 
						|
                     check_decimal_overflow(my_decimal_sub(E_DEC_FATAL_ERROR &
 | 
						|
                                                           ~E_DEC_OVERFLOW,
 | 
						|
                                                           decimal_value,
 | 
						|
                                                           val.m_a.ptr(),
 | 
						|
                                                           val.m_b.ptr())) > 3)))
 | 
						|
    return decimal_value;
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
double Item_func_mul::real_op()
 | 
						|
{
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
  double value= args[0]->val_real() * args[1]->val_real();
 | 
						|
  if ((null_value=args[0]->null_value || args[1]->null_value))
 | 
						|
    return 0.0;
 | 
						|
  return check_float_overflow(value);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
longlong Item_func_mul::int_op()
 | 
						|
{
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
  /*
 | 
						|
    Since we also have to take the unsigned_flag for a and b into account,
 | 
						|
    it is easier to first work with absolute values and set the
 | 
						|
    correct sign later.
 | 
						|
  */
 | 
						|
  Longlong_hybrid_null ha= args[0]->to_longlong_hybrid_null();
 | 
						|
  Longlong_hybrid_null hb= args[1]->to_longlong_hybrid_null();
 | 
						|
 | 
						|
  if ((null_value= ha.is_null() || hb.is_null()))
 | 
						|
    return 0;
 | 
						|
 | 
						|
  ULonglong_null ures= ULonglong_null::ullmul(ha.abs(), hb.abs());
 | 
						|
  if (ures.is_null())
 | 
						|
    return raise_integer_overflow();
 | 
						|
 | 
						|
  return check_integer_overflow(ULonglong_hybrid(ures.value(),
 | 
						|
                                                 ha.neg() != hb.neg()));
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/** See Item_func_plus::decimal_op for comments. */
 | 
						|
 | 
						|
my_decimal *Item_func_mul::decimal_op(my_decimal *decimal_value)
 | 
						|
{
 | 
						|
  VDec2_lazy val(args[0], args[1]);
 | 
						|
  if (!(null_value= (val.has_null() ||
 | 
						|
                     check_decimal_overflow(my_decimal_mul(E_DEC_FATAL_ERROR &
 | 
						|
                                                           ~E_DEC_OVERFLOW,
 | 
						|
                                                           decimal_value,
 | 
						|
                                                           val.m_a.ptr(),
 | 
						|
                                                           val.m_b.ptr())) > 3)))
 | 
						|
    return decimal_value;
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void Item_func_mul::result_precision()
 | 
						|
{
 | 
						|
  decimals= MY_MIN(args[0]->decimal_scale() + args[1]->decimal_scale(),
 | 
						|
                   DECIMAL_MAX_SCALE);
 | 
						|
  uint est_prec = args[0]->decimal_precision() + args[1]->decimal_precision();
 | 
						|
  uint precision= MY_MIN(est_prec, DECIMAL_MAX_PRECISION);
 | 
						|
  max_length= my_decimal_precision_to_length_no_truncation(precision, decimals,
 | 
						|
                                                           unsigned_flag);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool Item_func_mul::fix_length_and_dec(THD *thd)
 | 
						|
{
 | 
						|
  DBUG_ENTER("Item_func_mul::fix_length_and_dec");
 | 
						|
  DBUG_PRINT("info", ("name %s", func_name()));
 | 
						|
  const Type_aggregator *aggregator= &type_handler_data->m_type_aggregator_for_mul;
 | 
						|
  DBUG_EXECUTE_IF("num_op", aggregator= &type_handler_data->m_type_aggregator_for_result;);
 | 
						|
  DBUG_ASSERT(aggregator->is_commutative());
 | 
						|
  if (fix_type_handler(aggregator))
 | 
						|
    DBUG_RETURN(TRUE);
 | 
						|
  if (Item_func_mul::type_handler()->Item_func_mul_fix_length_and_dec(this))
 | 
						|
    DBUG_RETURN(TRUE);
 | 
						|
  DBUG_PRINT("info", ("Type: %s", type_handler()->name().ptr()));
 | 
						|
  DBUG_RETURN(FALSE);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
double Item_func_div::real_op()
 | 
						|
{
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
  double value= args[0]->val_real();
 | 
						|
  double val2= args[1]->val_real();
 | 
						|
  if ((null_value= args[0]->null_value || args[1]->null_value))
 | 
						|
    return 0.0;
 | 
						|
  if (val2 == 0.0)
 | 
						|
  {
 | 
						|
    signal_divide_by_null();
 | 
						|
    return 0.0;
 | 
						|
  }
 | 
						|
  return check_float_overflow(value/val2);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
my_decimal *Item_func_div::decimal_op(my_decimal *decimal_value)
 | 
						|
{
 | 
						|
  int err;
 | 
						|
  VDec2_lazy val(args[0], args[1]);
 | 
						|
  if ((null_value= val.has_null()))
 | 
						|
    return 0;
 | 
						|
  if ((err= check_decimal_overflow(my_decimal_div(E_DEC_FATAL_ERROR &
 | 
						|
                                                  ~E_DEC_OVERFLOW &
 | 
						|
                                                  ~E_DEC_DIV_ZERO,
 | 
						|
                                                  decimal_value,
 | 
						|
                                                  val.m_a.ptr(), val.m_b.ptr(),
 | 
						|
                                                  prec_increment))) > 3)
 | 
						|
  {
 | 
						|
    if (err == E_DEC_DIV_ZERO)
 | 
						|
      signal_divide_by_null();
 | 
						|
    null_value= 1;
 | 
						|
    return 0;
 | 
						|
  }
 | 
						|
  return decimal_value;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void Item_func_div::result_precision()
 | 
						|
{
 | 
						|
  /*
 | 
						|
    We need to add args[1]->divisor_precision_increment(),
 | 
						|
    to properly handle the cases like this:
 | 
						|
      SELECT 5.05 / 0.014; -> 360.714286
 | 
						|
    i.e. when the divisor has a zero integer part
 | 
						|
    and non-zero digits appear only after the decimal point.
 | 
						|
    Precision in this example is calculated as
 | 
						|
      args[0]->decimal_precision()           +  // 3
 | 
						|
      args[1]->divisor_precision_increment() +  // 3
 | 
						|
      prec_increment                            // 4
 | 
						|
    which gives 10 decimals digits.
 | 
						|
  */
 | 
						|
  uint precision=MY_MIN(args[0]->decimal_precision() + 
 | 
						|
                     args[1]->divisor_precision_increment() + prec_increment,
 | 
						|
                     DECIMAL_MAX_PRECISION);
 | 
						|
  decimals= MY_MIN(args[0]->decimal_scale() + prec_increment, DECIMAL_MAX_SCALE);
 | 
						|
  max_length= my_decimal_precision_to_length_no_truncation(precision, decimals,
 | 
						|
                                                           unsigned_flag);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void Item_func_div::fix_length_and_dec_double(void)
 | 
						|
{
 | 
						|
  Item_num_op::fix_length_and_dec_double();
 | 
						|
  decimals= MY_MAX(args[0]->decimals, args[1]->decimals) + prec_increment;
 | 
						|
  set_if_smaller(decimals, NOT_FIXED_DEC);
 | 
						|
  uint tmp= float_length(decimals);
 | 
						|
  if (decimals == NOT_FIXED_DEC)
 | 
						|
    max_length= tmp;
 | 
						|
  else
 | 
						|
  {
 | 
						|
    max_length=args[0]->max_length - args[0]->decimals + decimals;
 | 
						|
    set_if_smaller(max_length, tmp);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void Item_func_div::fix_length_and_dec_int(void)
 | 
						|
{
 | 
						|
  set_handler(&type_handler_newdecimal);
 | 
						|
  DBUG_PRINT("info", ("Type changed: %s", type_handler()->name().ptr()));
 | 
						|
  Item_num_op::fix_length_and_dec_decimal();
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool Item_func_div::fix_length_and_dec(THD *thd)
 | 
						|
{
 | 
						|
  DBUG_ENTER("Item_func_div::fix_length_and_dec");
 | 
						|
  DBUG_PRINT("info", ("name %s", func_name()));
 | 
						|
  prec_increment= thd->variables.div_precincrement;
 | 
						|
  set_maybe_null(); // division by zero
 | 
						|
 | 
						|
  const Type_aggregator *aggregator= &type_handler_data->m_type_aggregator_for_div;
 | 
						|
  DBUG_EXECUTE_IF("num_op", aggregator= &type_handler_data->m_type_aggregator_non_commutative_test;);
 | 
						|
  DBUG_ASSERT(!aggregator->is_commutative());
 | 
						|
  if (fix_type_handler(aggregator))
 | 
						|
    DBUG_RETURN(TRUE);
 | 
						|
  if (Item_func_div::type_handler()->Item_func_div_fix_length_and_dec(this))
 | 
						|
    DBUG_RETURN(TRUE);
 | 
						|
  DBUG_PRINT("info", ("Type: %s", type_handler()->name().ptr()));
 | 
						|
  DBUG_RETURN(FALSE);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* Integer division */
 | 
						|
longlong Item_func_int_div::val_int()
 | 
						|
{
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
 | 
						|
  /*
 | 
						|
    Perform division using DECIMAL math if either of the operands has a
 | 
						|
    non-integer type
 | 
						|
  */
 | 
						|
  if (args[0]->result_type() != INT_RESULT ||
 | 
						|
      args[1]->result_type() != INT_RESULT)
 | 
						|
  {
 | 
						|
    VDec2_lazy val(args[0], args[1]);
 | 
						|
    if ((null_value= val.has_null()))
 | 
						|
      return 0;
 | 
						|
 | 
						|
    int err;
 | 
						|
    my_decimal tmp;
 | 
						|
    if ((err= my_decimal_div(E_DEC_FATAL_ERROR & ~E_DEC_DIV_ZERO, &tmp,
 | 
						|
                             val.m_a.ptr(), val.m_b.ptr(), 0)) > 3)
 | 
						|
    {
 | 
						|
      if (err == E_DEC_DIV_ZERO)
 | 
						|
        signal_divide_by_null();
 | 
						|
      return 0;
 | 
						|
    }
 | 
						|
 | 
						|
    my_decimal truncated;
 | 
						|
    if (tmp.round_to(&truncated, 0, TRUNCATE))
 | 
						|
      DBUG_ASSERT(false);
 | 
						|
 | 
						|
    longlong res;
 | 
						|
    if (my_decimal2int(E_DEC_FATAL_ERROR, &truncated, unsigned_flag, &res) &
 | 
						|
        E_DEC_OVERFLOW)
 | 
						|
      raise_integer_overflow();
 | 
						|
    return res;
 | 
						|
  }
 | 
						|
 | 
						|
  Longlong_hybrid val0= args[0]->to_longlong_hybrid();
 | 
						|
  Longlong_hybrid val1= args[1]->to_longlong_hybrid();
 | 
						|
  if ((null_value= (args[0]->null_value || args[1]->null_value)))
 | 
						|
    return 0;
 | 
						|
  if (val1 == 0)
 | 
						|
  {
 | 
						|
    signal_divide_by_null();
 | 
						|
    return 0;
 | 
						|
  }
 | 
						|
 | 
						|
  return check_integer_overflow(ULonglong_hybrid(val0.abs() / val1.abs(),
 | 
						|
                                                 val0.neg() != val1.neg()));
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool Item_func_int_div::fix_length_and_dec(THD *thd)
 | 
						|
{
 | 
						|
  uint32 prec= args[0]->decimal_int_part();
 | 
						|
  set_if_smaller(prec, MY_INT64_NUM_DECIMAL_DIGITS);
 | 
						|
  fix_char_length(prec);
 | 
						|
  set_maybe_null();
 | 
						|
  unsigned_flag=args[0]->unsigned_flag | args[1]->unsigned_flag;
 | 
						|
  return false;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
longlong Item_func_mod::int_op()
 | 
						|
{
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
  Longlong_hybrid val0= args[0]->to_longlong_hybrid();
 | 
						|
  Longlong_hybrid val1= args[1]->to_longlong_hybrid();
 | 
						|
 | 
						|
  if ((null_value= args[0]->null_value || args[1]->null_value))
 | 
						|
    return 0; /* purecov: inspected */
 | 
						|
  if (val1 == 0)
 | 
						|
  {
 | 
						|
    signal_divide_by_null();
 | 
						|
    return 0;
 | 
						|
  }
 | 
						|
 | 
						|
  /*
 | 
						|
    '%' is calculated by integer division internally. Since dividing
 | 
						|
    LONGLONG_MIN by -1 generates SIGFPE, we calculate using unsigned values and
 | 
						|
    then adjust the sign appropriately.
 | 
						|
  */
 | 
						|
  return check_integer_overflow(ULonglong_hybrid(val0.abs() % val1.abs(),
 | 
						|
                                                 val0.neg()));
 | 
						|
}
 | 
						|
 | 
						|
double Item_func_mod::real_op()
 | 
						|
{
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
  double value= args[0]->val_real();
 | 
						|
  double val2=  args[1]->val_real();
 | 
						|
  if ((null_value= args[0]->null_value || args[1]->null_value))
 | 
						|
    return 0.0; /* purecov: inspected */
 | 
						|
  if (val2 == 0.0)
 | 
						|
  {
 | 
						|
    signal_divide_by_null();
 | 
						|
    return 0.0;
 | 
						|
  }
 | 
						|
  return fmod(value,val2);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
my_decimal *Item_func_mod::decimal_op(my_decimal *decimal_value)
 | 
						|
{
 | 
						|
  VDec2_lazy val(args[0], args[1]);
 | 
						|
  if ((null_value= val.has_null()))
 | 
						|
    return 0;
 | 
						|
  switch (my_decimal_mod(E_DEC_FATAL_ERROR & ~E_DEC_DIV_ZERO, decimal_value,
 | 
						|
                         val.m_a.ptr(), val.m_b.ptr())) {
 | 
						|
  case E_DEC_TRUNCATED:
 | 
						|
  case E_DEC_OK:
 | 
						|
    return decimal_value;
 | 
						|
  case E_DEC_DIV_ZERO:
 | 
						|
    signal_divide_by_null();
 | 
						|
    /* fall through */
 | 
						|
  default:
 | 
						|
    null_value= 1;
 | 
						|
    return 0;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void Item_func_mod::result_precision()
 | 
						|
{
 | 
						|
  unsigned_flag= args[0]->unsigned_flag;
 | 
						|
  decimals= MY_MAX(args[0]->decimal_scale(), args[1]->decimal_scale());
 | 
						|
  uint prec= MY_MAX(args[0]->decimal_precision(), args[1]->decimal_precision());
 | 
						|
  fix_char_length(my_decimal_precision_to_length_no_truncation(prec, decimals,
 | 
						|
                                                               unsigned_flag));
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool Item_func_mod::fix_length_and_dec(THD *thd)
 | 
						|
{
 | 
						|
  DBUG_ENTER("Item_func_mod::fix_length_and_dec");
 | 
						|
  DBUG_PRINT("info", ("name %s", func_name()));
 | 
						|
  set_maybe_null(); // division by zero
 | 
						|
  const Type_aggregator *aggregator= &type_handler_data->m_type_aggregator_for_mod;
 | 
						|
  DBUG_EXECUTE_IF("num_op", aggregator= &type_handler_data->m_type_aggregator_non_commutative_test;);
 | 
						|
  DBUG_ASSERT(!aggregator->is_commutative());
 | 
						|
  if (fix_type_handler(aggregator))
 | 
						|
    DBUG_RETURN(TRUE);
 | 
						|
  if (Item_func_mod::type_handler()->Item_func_mod_fix_length_and_dec(this))
 | 
						|
    DBUG_RETURN(TRUE);
 | 
						|
  DBUG_PRINT("info", ("Type: %s", type_handler()->name().ptr()));
 | 
						|
  DBUG_RETURN(FALSE);
 | 
						|
}
 | 
						|
 | 
						|
static void calc_hash_for_unique(ulong &nr1, ulong &nr2, String *str)
 | 
						|
{
 | 
						|
  CHARSET_INFO *cs;
 | 
						|
  uchar l[4];
 | 
						|
  int4store(l, str->length());
 | 
						|
  cs= str->charset();
 | 
						|
  cs->hash_sort(l, sizeof(l), &nr1, &nr2);
 | 
						|
  cs= str->charset();
 | 
						|
  cs->hash_sort((uchar *)str->ptr(), str->length(), &nr1, &nr2);
 | 
						|
}
 | 
						|
 | 
						|
longlong  Item_func_hash_mariadb_100403::val_int()
 | 
						|
{
 | 
						|
  DBUG_EXECUTE_IF("same_long_unique_hash", return 9;);
 | 
						|
  unsigned_flag= true;
 | 
						|
  ulong nr1= 1,nr2= 4;
 | 
						|
  String * str;
 | 
						|
  for(uint i= 0;i<arg_count;i++)
 | 
						|
  {
 | 
						|
    str = args[i]->val_str();
 | 
						|
    if(args[i]->null_value)
 | 
						|
    {
 | 
						|
      null_value= 1;
 | 
						|
      return 0;
 | 
						|
    }
 | 
						|
   calc_hash_for_unique(nr1, nr2, str);
 | 
						|
  }
 | 
						|
  null_value= 0;
 | 
						|
  return   (longlong)nr1;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
longlong  Item_func_hash::val_int()
 | 
						|
{
 | 
						|
  DBUG_EXECUTE_IF("same_long_unique_hash", return 9;);
 | 
						|
  unsigned_flag= true;
 | 
						|
  Hasher hasher;
 | 
						|
  for(uint i= 0;i<arg_count;i++)
 | 
						|
  {
 | 
						|
    if (args[i]->hash_not_null(&hasher))
 | 
						|
    {
 | 
						|
      null_value= 1;
 | 
						|
      return 0;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  null_value= 0;
 | 
						|
  return (longlong) hasher.finalize();
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool Item_func_hash::fix_length_and_dec(THD *thd)
 | 
						|
{
 | 
						|
  decimals= 0;
 | 
						|
  max_length= 8;
 | 
						|
  return false;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
 | 
						|
double Item_func_neg::real_op()
 | 
						|
{
 | 
						|
  double value= args[0]->val_real();
 | 
						|
  null_value= args[0]->null_value;
 | 
						|
  return -value;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
longlong Item_func_neg::int_op()
 | 
						|
{
 | 
						|
  longlong value= args[0]->val_int();
 | 
						|
  if ((null_value= args[0]->null_value))
 | 
						|
    return 0;
 | 
						|
  if (args[0]->unsigned_flag &&
 | 
						|
      (ulonglong) value > (ulonglong) LONGLONG_MAX + 1)
 | 
						|
    return raise_integer_overflow();
 | 
						|
 | 
						|
  if (value == LONGLONG_MIN)
 | 
						|
  {
 | 
						|
    if (args[0]->unsigned_flag != unsigned_flag)
 | 
						|
      /* negation of LONGLONG_MIN is LONGLONG_MIN. */
 | 
						|
      return LONGLONG_MIN; 
 | 
						|
    else
 | 
						|
      return raise_integer_overflow();
 | 
						|
  }
 | 
						|
 | 
						|
  return check_integer_overflow(-value, !args[0]->unsigned_flag && value < 0);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
my_decimal *Item_func_neg::decimal_op(my_decimal *decimal_value)
 | 
						|
{
 | 
						|
  VDec value(args[0]);
 | 
						|
  if (!(null_value= value.is_null()))
 | 
						|
  {
 | 
						|
    my_decimal2decimal(value.ptr(), decimal_value);
 | 
						|
    my_decimal_neg(decimal_value);
 | 
						|
    return decimal_value;
 | 
						|
  }
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void Item_func_neg::fix_length_and_dec_int()
 | 
						|
{
 | 
						|
  max_length= args[0]->max_length + 1;
 | 
						|
  set_handler(type_handler_long_or_longlong());
 | 
						|
 | 
						|
  /*
 | 
						|
    If this is in integer context keep the context as integer if possible
 | 
						|
    (This is how multiplication and other integer functions works)
 | 
						|
    Use val() to get value as arg_type doesn't mean that item is
 | 
						|
    Item_int or Item_float due to existence of Item_param.
 | 
						|
  */
 | 
						|
  if (args[0]->const_item() && !args[0]->is_expensive())
 | 
						|
  {
 | 
						|
    longlong val= args[0]->val_int();
 | 
						|
    if ((ulonglong) val >= (ulonglong) LONGLONG_MIN &&
 | 
						|
        ((ulonglong) val != (ulonglong) LONGLONG_MIN ||
 | 
						|
         !args[0]->is_of_type(CONST_ITEM, INT_RESULT)))
 | 
						|
    {
 | 
						|
      /*
 | 
						|
        Ensure that result is converted to DECIMAL, as longlong can't hold
 | 
						|
        the negated number
 | 
						|
      */
 | 
						|
      set_handler(&type_handler_newdecimal);
 | 
						|
      DBUG_PRINT("info", ("Type changed: DECIMAL_RESULT"));
 | 
						|
    }
 | 
						|
  }
 | 
						|
  unsigned_flag= false;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void Item_func_neg::fix_length_and_dec_double()
 | 
						|
{
 | 
						|
  set_handler(&type_handler_double);
 | 
						|
  decimals= args[0]->decimals; // Preserve NOT_FIXED_DEC
 | 
						|
  max_length= args[0]->max_length + 1;
 | 
						|
  // Limit length with something reasonable
 | 
						|
  uint32 mlen= type_handler()->max_display_length(this);
 | 
						|
  set_if_smaller(max_length, mlen);
 | 
						|
  unsigned_flag= false;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void Item_func_neg::fix_length_and_dec_decimal()
 | 
						|
{
 | 
						|
  set_handler(&type_handler_newdecimal);
 | 
						|
  decimals= args[0]->decimal_scale(); // Do not preserve NOT_FIXED_DEC
 | 
						|
  max_length= args[0]->max_length + 1;
 | 
						|
  unsigned_flag= false;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool Item_func_neg::fix_length_and_dec(THD *thd)
 | 
						|
{
 | 
						|
  DBUG_ENTER("Item_func_neg::fix_length_and_dec");
 | 
						|
  DBUG_PRINT("info", ("name %s", func_name()));
 | 
						|
  if (args[0]->cast_to_int_type_handler()->
 | 
						|
      Item_func_neg_fix_length_and_dec(this))
 | 
						|
    DBUG_RETURN(TRUE);
 | 
						|
  DBUG_PRINT("info", ("Type: %s", type_handler()->name().ptr()));
 | 
						|
  DBUG_RETURN(FALSE);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
double Item_func_abs::real_op()
 | 
						|
{
 | 
						|
  double value= args[0]->val_real();
 | 
						|
  null_value= args[0]->null_value;
 | 
						|
  return fabs(value);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
longlong Item_func_abs::int_op()
 | 
						|
{
 | 
						|
  longlong value= args[0]->val_int();
 | 
						|
  if ((null_value= args[0]->null_value))
 | 
						|
    return 0;
 | 
						|
  if (unsigned_flag)
 | 
						|
    return value;
 | 
						|
  /* -LONGLONG_MIN = LONGLONG_MAX + 1 => outside of signed longlong range */
 | 
						|
  if (value == LONGLONG_MIN)
 | 
						|
    return raise_integer_overflow();
 | 
						|
  return (value >= 0) ? value : -value;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
my_decimal *Item_func_abs::decimal_op(my_decimal *decimal_value)
 | 
						|
{
 | 
						|
  VDec value(args[0]);
 | 
						|
  if (!(null_value= value.is_null()))
 | 
						|
  {
 | 
						|
    my_decimal2decimal(value.ptr(), decimal_value);
 | 
						|
    if (decimal_value->sign())
 | 
						|
      my_decimal_neg(decimal_value);
 | 
						|
    return decimal_value;
 | 
						|
  }
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
void Item_func_abs::fix_length_and_dec_int()
 | 
						|
{
 | 
						|
  max_length= args[0]->max_length;
 | 
						|
  unsigned_flag= args[0]->unsigned_flag;
 | 
						|
  set_handler(type_handler_long_or_longlong());
 | 
						|
}
 | 
						|
 | 
						|
void Item_func_abs::fix_length_and_dec_sint_ge0()
 | 
						|
{
 | 
						|
  /*
 | 
						|
    We're converting slong_ge0 to slong/slonglong.
 | 
						|
    Add one character for the sign into max_length.
 | 
						|
  */
 | 
						|
  max_length= args[0]->decimal_precision() + 1/*sign*/;
 | 
						|
  DBUG_ASSERT(!args[0]->unsigned_flag);
 | 
						|
  unsigned_flag= false;
 | 
						|
  set_handler(type_handler_long_or_longlong());
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void Item_func_abs::fix_length_and_dec_double()
 | 
						|
{
 | 
						|
  set_handler(&type_handler_double);
 | 
						|
  decimals= args[0]->decimals; // Preserve NOT_FIXED_DEC
 | 
						|
  max_length= float_length(decimals);
 | 
						|
  unsigned_flag= args[0]->unsigned_flag;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void Item_func_abs::fix_length_and_dec_decimal()
 | 
						|
{
 | 
						|
  set_handler(&type_handler_newdecimal);
 | 
						|
  decimals= args[0]->decimal_scale(); // Do not preserve NOT_FIXED_DEC
 | 
						|
  max_length= args[0]->max_length;
 | 
						|
  unsigned_flag= args[0]->unsigned_flag;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool Item_func_abs::fix_length_and_dec(THD *thd)
 | 
						|
{
 | 
						|
  DBUG_ENTER("Item_func_abs::fix_length_and_dec");
 | 
						|
  DBUG_PRINT("info", ("name %s", func_name()));
 | 
						|
  if (args[0]->cast_to_int_type_handler()->
 | 
						|
      Item_func_abs_fix_length_and_dec(this))
 | 
						|
    DBUG_RETURN(TRUE);
 | 
						|
  DBUG_PRINT("info", ("Type: %s", type_handler()->name().ptr()));
 | 
						|
  DBUG_RETURN(FALSE);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/** Gateway to natural LOG function. */
 | 
						|
double Item_func_ln::val_real()
 | 
						|
{
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
  double value= args[0]->val_real();
 | 
						|
  if ((null_value= args[0]->null_value))
 | 
						|
    return 0.0;
 | 
						|
  if (value <= 0.0)
 | 
						|
  {
 | 
						|
    signal_divide_by_null();
 | 
						|
    return 0.0;
 | 
						|
  }
 | 
						|
  return log(value);
 | 
						|
}
 | 
						|
 | 
						|
/** 
 | 
						|
  Extended but so slower LOG function.
 | 
						|
 | 
						|
  We have to check if all values are > zero and first one is not one
 | 
						|
  as these are the cases then result is not a number.
 | 
						|
*/ 
 | 
						|
double Item_func_log::val_real()
 | 
						|
{
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
  double value= args[0]->val_real();
 | 
						|
  if ((null_value= args[0]->null_value))
 | 
						|
    return 0.0;
 | 
						|
  if (value <= 0.0)
 | 
						|
  {
 | 
						|
    signal_divide_by_null();
 | 
						|
    return 0.0;
 | 
						|
  }
 | 
						|
  if (arg_count == 2)
 | 
						|
  {
 | 
						|
    double value2= args[1]->val_real();
 | 
						|
    if ((null_value= args[1]->null_value))
 | 
						|
      return 0.0;
 | 
						|
    if (value2 <= 0.0 || value == 1.0)
 | 
						|
    {
 | 
						|
      signal_divide_by_null();
 | 
						|
      return 0.0;
 | 
						|
    }
 | 
						|
    return log(value2) / log(value);
 | 
						|
  }
 | 
						|
  return log(value);
 | 
						|
}
 | 
						|
 | 
						|
double Item_func_log2::val_real()
 | 
						|
{
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
  double value= args[0]->val_real();
 | 
						|
 | 
						|
  if ((null_value=args[0]->null_value))
 | 
						|
    return 0.0;
 | 
						|
  if (value <= 0.0)
 | 
						|
  {
 | 
						|
    signal_divide_by_null();
 | 
						|
    return 0.0;
 | 
						|
  }
 | 
						|
  return log(value) / M_LN2;
 | 
						|
}
 | 
						|
 | 
						|
double Item_func_log10::val_real()
 | 
						|
{
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
  double value= args[0]->val_real();
 | 
						|
  if ((null_value= args[0]->null_value))
 | 
						|
    return 0.0;
 | 
						|
  if (value <= 0.0)
 | 
						|
  {
 | 
						|
    signal_divide_by_null();
 | 
						|
    return 0.0;
 | 
						|
  }
 | 
						|
  return log10(value);
 | 
						|
}
 | 
						|
 | 
						|
double Item_func_exp::val_real()
 | 
						|
{
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
  double value= args[0]->val_real();
 | 
						|
  if ((null_value=args[0]->null_value))
 | 
						|
    return 0.0; /* purecov: inspected */
 | 
						|
  return check_float_overflow(exp(value));
 | 
						|
}
 | 
						|
 | 
						|
double Item_func_sqrt::val_real()
 | 
						|
{
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
  double value= args[0]->val_real();
 | 
						|
  if ((null_value=(args[0]->null_value || value < 0)))
 | 
						|
    return 0.0; /* purecov: inspected */
 | 
						|
  return sqrt(value);
 | 
						|
}
 | 
						|
 | 
						|
double Item_func_pow::val_real()
 | 
						|
{
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
  double value= args[0]->val_real();
 | 
						|
  double val2= args[1]->val_real();
 | 
						|
  if ((null_value=(args[0]->null_value || args[1]->null_value)))
 | 
						|
    return 0.0; /* purecov: inspected */
 | 
						|
  return check_float_overflow(pow(value,val2));
 | 
						|
}
 | 
						|
 | 
						|
// Trigonometric functions
 | 
						|
 | 
						|
double Item_func_acos::val_real()
 | 
						|
{
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
  /* One can use this to defer SELECT processing. */
 | 
						|
  DEBUG_SYNC(current_thd, "before_acos_function");
 | 
						|
  // the volatile's for BUG #2338 to calm optimizer down (because of gcc's bug)
 | 
						|
  volatile double value= args[0]->val_real();
 | 
						|
  if ((null_value=(args[0]->null_value || (value < -1.0 || value > 1.0))))
 | 
						|
    return 0.0;
 | 
						|
  return acos(value);
 | 
						|
}
 | 
						|
 | 
						|
double Item_func_asin::val_real()
 | 
						|
{
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
  // the volatile's for BUG #2338 to calm optimizer down (because of gcc's bug)
 | 
						|
  volatile double value= args[0]->val_real();
 | 
						|
  if ((null_value=(args[0]->null_value || (value < -1.0 || value > 1.0))))
 | 
						|
    return 0.0;
 | 
						|
  return asin(value);
 | 
						|
}
 | 
						|
 | 
						|
double Item_func_atan::val_real()
 | 
						|
{
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
  double value= args[0]->val_real();
 | 
						|
  if ((null_value=args[0]->null_value))
 | 
						|
    return 0.0;
 | 
						|
  if (arg_count == 2)
 | 
						|
  {
 | 
						|
    double val2= args[1]->val_real();
 | 
						|
    if ((null_value=args[1]->null_value))
 | 
						|
      return 0.0;
 | 
						|
    return check_float_overflow(atan2(value,val2));
 | 
						|
  }
 | 
						|
  return atan(value);
 | 
						|
}
 | 
						|
 | 
						|
double Item_func_cos::val_real()
 | 
						|
{
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
  double value= args[0]->val_real();
 | 
						|
  if ((null_value=args[0]->null_value))
 | 
						|
    return 0.0;
 | 
						|
  return cos(value);
 | 
						|
}
 | 
						|
 | 
						|
double Item_func_sin::val_real()
 | 
						|
{
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
  double value= args[0]->val_real();
 | 
						|
  if ((null_value=args[0]->null_value))
 | 
						|
    return 0.0;
 | 
						|
  return sin(value);
 | 
						|
}
 | 
						|
 | 
						|
double Item_func_tan::val_real()
 | 
						|
{
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
  double value= args[0]->val_real();
 | 
						|
  if ((null_value=args[0]->null_value))
 | 
						|
    return 0.0;
 | 
						|
  return check_float_overflow(tan(value));
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
double Item_func_cot::val_real()
 | 
						|
{
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
  double value= args[0]->val_real();
 | 
						|
  if ((null_value=args[0]->null_value))
 | 
						|
    return 0.0;
 | 
						|
  return check_float_overflow(1.0 / tan(value));
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
// Shift-functions, same as << and >> in C/C++
 | 
						|
 | 
						|
 | 
						|
class Func_handler_shift_left_int_to_ulonglong:
 | 
						|
        public Item_handled_func::Handler_ulonglong
 | 
						|
{
 | 
						|
public:
 | 
						|
  Longlong_null to_longlong_null(Item_handled_func *item) const override
 | 
						|
  {
 | 
						|
    DBUG_ASSERT(item->fixed());
 | 
						|
    return item->arguments()[0]->to_longlong_null() <<
 | 
						|
           item->arguments()[1]->to_longlong_null();
 | 
						|
  }
 | 
						|
};
 | 
						|
 | 
						|
 | 
						|
class Func_handler_shift_left_decimal_to_ulonglong:
 | 
						|
        public Item_handled_func::Handler_ulonglong
 | 
						|
{
 | 
						|
public:
 | 
						|
  Longlong_null to_longlong_null(Item_handled_func *item) const override
 | 
						|
  {
 | 
						|
    DBUG_ASSERT(item->fixed());
 | 
						|
    return VDec(item->arguments()[0]).to_xlonglong_null() <<
 | 
						|
           item->arguments()[1]->to_longlong_null();
 | 
						|
  }
 | 
						|
};
 | 
						|
 | 
						|
 | 
						|
bool Item_func_shift_left::fix_length_and_dec(THD *thd)
 | 
						|
{
 | 
						|
  static Func_handler_shift_left_int_to_ulonglong ha_int_to_ull;
 | 
						|
  static Func_handler_shift_left_decimal_to_ulonglong ha_dec_to_ull;
 | 
						|
  return fix_length_and_dec_op1_std(&ha_int_to_ull, &ha_dec_to_ull);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
class Func_handler_shift_right_int_to_ulonglong:
 | 
						|
        public Item_handled_func::Handler_ulonglong
 | 
						|
{
 | 
						|
public:
 | 
						|
  Longlong_null to_longlong_null(Item_handled_func *item) const override
 | 
						|
  {
 | 
						|
    DBUG_ASSERT(item->fixed());
 | 
						|
    return item->arguments()[0]->to_longlong_null() >>
 | 
						|
           item->arguments()[1]->to_longlong_null();
 | 
						|
  }
 | 
						|
};
 | 
						|
 | 
						|
 | 
						|
class Func_handler_shift_right_decimal_to_ulonglong:
 | 
						|
        public Item_handled_func::Handler_ulonglong
 | 
						|
{
 | 
						|
public:
 | 
						|
  Longlong_null to_longlong_null(Item_handled_func *item) const override
 | 
						|
  {
 | 
						|
    DBUG_ASSERT(item->fixed());
 | 
						|
    return VDec(item->arguments()[0]).to_xlonglong_null() >>
 | 
						|
           item->arguments()[1]->to_longlong_null();
 | 
						|
  }
 | 
						|
};
 | 
						|
 | 
						|
 | 
						|
bool Item_func_shift_right::fix_length_and_dec(THD *thd)
 | 
						|
{
 | 
						|
  static Func_handler_shift_right_int_to_ulonglong ha_int_to_ull;
 | 
						|
  static Func_handler_shift_right_decimal_to_ulonglong ha_dec_to_ull;
 | 
						|
  return fix_length_and_dec_op1_std(&ha_int_to_ull, &ha_dec_to_ull);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
class Func_handler_bit_neg_int_to_ulonglong:
 | 
						|
        public Item_handled_func::Handler_ulonglong
 | 
						|
{
 | 
						|
public:
 | 
						|
  Longlong_null to_longlong_null(Item_handled_func *item) const override
 | 
						|
  {
 | 
						|
    DBUG_ASSERT(item->fixed());
 | 
						|
    return ~ item->arguments()[0]->to_longlong_null();
 | 
						|
  }
 | 
						|
};
 | 
						|
 | 
						|
 | 
						|
class Func_handler_bit_neg_decimal_to_ulonglong:
 | 
						|
        public Item_handled_func::Handler_ulonglong
 | 
						|
{
 | 
						|
public:
 | 
						|
  Longlong_null to_longlong_null(Item_handled_func *item) const override
 | 
						|
  {
 | 
						|
    DBUG_ASSERT(item->fixed());
 | 
						|
    return ~ VDec(item->arguments()[0]).to_xlonglong_null();
 | 
						|
  }
 | 
						|
};
 | 
						|
 | 
						|
 | 
						|
bool Item_func_bit_neg::fix_length_and_dec(THD *thd)
 | 
						|
{
 | 
						|
  static Func_handler_bit_neg_int_to_ulonglong ha_int_to_ull;
 | 
						|
  static Func_handler_bit_neg_decimal_to_ulonglong ha_dec_to_ull;
 | 
						|
  return fix_length_and_dec_op1_std(&ha_int_to_ull, &ha_dec_to_ull);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
// Conversion functions
 | 
						|
 | 
						|
void Item_func_int_val::fix_length_and_dec_int_or_decimal()
 | 
						|
{
 | 
						|
  DBUG_ASSERT(args[0]->cmp_type() == DECIMAL_RESULT);
 | 
						|
  DBUG_ASSERT(args[0]->max_length <= DECIMAL_MAX_STR_LENGTH);
 | 
						|
  /*
 | 
						|
    FLOOR() for negative numbers can increase length:   floor(-9.9) -> -10
 | 
						|
    CEILING() for positive numbers can increase length:  ceil(9.9)  -> 10
 | 
						|
  */
 | 
						|
  decimal_round_mode mode= round_mode();
 | 
						|
  uint length_increase= args[0]->decimals > 0 &&
 | 
						|
                        (mode == CEILING ||
 | 
						|
                         (mode == FLOOR && !args[0]->unsigned_flag)) ? 1 : 0;
 | 
						|
  uint precision= args[0]->decimal_int_part() + length_increase;
 | 
						|
  set_if_bigger(precision, 1);
 | 
						|
 | 
						|
  /*
 | 
						|
    The BIGINT data type can store:
 | 
						|
    UNSIGNED BIGINT: 0..18446744073709551615                     - up to 19 digits
 | 
						|
      SIGNED BIGINT:   -9223372036854775808..9223372036854775807 - up to 18 digits
 | 
						|
 | 
						|
    The INT data type can store:
 | 
						|
        UNSIGNED INT:  0..4294967295          - up to 9 digits
 | 
						|
          SIGNED INT: -2147483648..2147483647 - up to 9 digits
 | 
						|
  */
 | 
						|
  if (precision > 18)
 | 
						|
  {
 | 
						|
    unsigned_flag= args[0]->unsigned_flag;
 | 
						|
    fix_char_length(
 | 
						|
      my_decimal_precision_to_length_no_truncation(precision, 0,
 | 
						|
                                                   unsigned_flag));
 | 
						|
    set_handler(&type_handler_newdecimal);
 | 
						|
  }
 | 
						|
  else
 | 
						|
  {
 | 
						|
    uint sign_length= (unsigned_flag= args[0]->unsigned_flag) ? 0 : 1;
 | 
						|
    fix_char_length(precision + sign_length);
 | 
						|
    if (precision > 9)
 | 
						|
    {
 | 
						|
      if (unsigned_flag)
 | 
						|
        set_handler(&type_handler_ulonglong);
 | 
						|
      else
 | 
						|
        set_handler(&type_handler_slonglong);
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
      if (unsigned_flag)
 | 
						|
        set_handler(&type_handler_ulong);
 | 
						|
      else
 | 
						|
        set_handler(&type_handler_slong);
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void Item_func_int_val::fix_length_and_dec_double()
 | 
						|
{
 | 
						|
  set_handler(&type_handler_double);
 | 
						|
  max_length= float_length(0);
 | 
						|
  decimals= 0;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool Item_func_int_val::fix_length_and_dec(THD *thd)
 | 
						|
{
 | 
						|
  DBUG_ENTER("Item_func_int_val::fix_length_and_dec");
 | 
						|
  DBUG_PRINT("info", ("name %s", func_name()));
 | 
						|
  /*
 | 
						|
    We don't want to translate ENUM/SET to CHAR here.
 | 
						|
    So let's call real_type_handler(), not type_handler().
 | 
						|
  */
 | 
						|
  if (args[0]->real_type_handler()->Item_func_int_val_fix_length_and_dec(this))
 | 
						|
    DBUG_RETURN(TRUE);
 | 
						|
  DBUG_PRINT("info", ("Type: %s", real_type_handler()->name().ptr()));
 | 
						|
  DBUG_RETURN(FALSE);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool Item_func_int_val::native_op(THD *thd, Native *to)
 | 
						|
{
 | 
						|
  // TODO: turn Item_func_int_val into Item_handled_func eventually.
 | 
						|
  if (type_handler()->mysql_timestamp_type() == MYSQL_TIMESTAMP_TIME)
 | 
						|
    return Time(thd, this).to_native(to, decimals);
 | 
						|
  DBUG_ASSERT(0);
 | 
						|
  return true;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
longlong Item_func_ceiling::int_op()
 | 
						|
{
 | 
						|
  switch (args[0]->result_type()) {
 | 
						|
  case STRING_RESULT: // hex hybrid
 | 
						|
  case INT_RESULT:
 | 
						|
    return val_int_from_item(args[0]);
 | 
						|
  case DECIMAL_RESULT:
 | 
						|
    return VDec_op(this).to_longlong(unsigned_flag);
 | 
						|
  default:
 | 
						|
    break;
 | 
						|
  }
 | 
						|
  return (longlong) Item_func_ceiling::real_op();
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
double Item_func_ceiling::real_op()
 | 
						|
{
 | 
						|
  /*
 | 
						|
    the volatile's for BUG #3051 to calm optimizer down (because of gcc's
 | 
						|
    bug)
 | 
						|
  */
 | 
						|
  volatile double value= args[0]->val_real();
 | 
						|
  null_value= args[0]->null_value;
 | 
						|
  return ceil(value);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
my_decimal *Item_func_ceiling::decimal_op(my_decimal *decimal_value)
 | 
						|
{
 | 
						|
  VDec value(args[0]);
 | 
						|
  if (!(null_value= (value.is_null() ||
 | 
						|
                     value.round_to(decimal_value, 0, CEILING) > 1)))
 | 
						|
    return decimal_value;
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool Item_func_ceiling::date_op(THD *thd, MYSQL_TIME *to,
 | 
						|
                                date_mode_t fuzzydate)
 | 
						|
{
 | 
						|
  Datetime::Options opt(thd, TIME_FRAC_TRUNCATE);
 | 
						|
  Datetime *tm= new (to) Datetime(thd, args[0], opt);
 | 
						|
  tm->ceiling(thd);
 | 
						|
  null_value= !tm->is_valid_datetime();
 | 
						|
  DBUG_ASSERT(maybe_null() || !null_value);
 | 
						|
  return null_value;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool Item_func_ceiling::time_op(THD *thd, MYSQL_TIME *to)
 | 
						|
{
 | 
						|
  static const Time::Options_for_round opt;
 | 
						|
  Time *tm= new (to) Time(thd, args[0], opt);
 | 
						|
  tm->ceiling();
 | 
						|
  null_value= !tm->is_valid_time();
 | 
						|
  DBUG_ASSERT(maybe_null() || !null_value);
 | 
						|
  return null_value;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
longlong Item_func_floor::int_op()
 | 
						|
{
 | 
						|
  switch (args[0]->result_type()) {
 | 
						|
  case STRING_RESULT: // hex hybrid
 | 
						|
  case INT_RESULT:
 | 
						|
    return val_int_from_item(args[0]);
 | 
						|
  case DECIMAL_RESULT:
 | 
						|
  {
 | 
						|
    my_decimal dec_buf, *dec;
 | 
						|
    return (!(dec= Item_func_floor::decimal_op(&dec_buf))) ? 0 :
 | 
						|
           dec->to_longlong(unsigned_flag);
 | 
						|
  }
 | 
						|
  default:
 | 
						|
    break;
 | 
						|
  }
 | 
						|
  return (longlong) Item_func_floor::real_op();
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
double Item_func_floor::real_op()
 | 
						|
{
 | 
						|
  /*
 | 
						|
    the volatile's for BUG #3051 to calm optimizer down (because of gcc's
 | 
						|
    bug)
 | 
						|
  */
 | 
						|
  volatile double value= args[0]->val_real();
 | 
						|
  null_value= args[0]->null_value;
 | 
						|
  return floor(value);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
my_decimal *Item_func_floor::decimal_op(my_decimal *decimal_value)
 | 
						|
{
 | 
						|
  VDec value(args[0]);
 | 
						|
  if (!(null_value= (value.is_null() ||
 | 
						|
                     value.round_to(decimal_value, 0, FLOOR) > 1)))
 | 
						|
    return decimal_value;
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool Item_func_floor::date_op(THD *thd, MYSQL_TIME *to, date_mode_t fuzzydate)
 | 
						|
{
 | 
						|
  // DATETIME is not negative, so FLOOR means just truncation
 | 
						|
  Datetime::Options opt(thd, TIME_FRAC_TRUNCATE);
 | 
						|
  Datetime *tm= new (to) Datetime(thd, args[0], opt, 0);
 | 
						|
  null_value= !tm->is_valid_datetime();
 | 
						|
  DBUG_ASSERT(maybe_null() || !null_value);
 | 
						|
  return null_value;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool Item_func_floor::time_op(THD *thd, MYSQL_TIME *to)
 | 
						|
{
 | 
						|
  static const Time::Options_for_round opt;
 | 
						|
  Time *tm= new (to) Time(thd, args[0], opt);
 | 
						|
  tm->floor();
 | 
						|
  null_value= !tm->is_valid_time();
 | 
						|
  DBUG_ASSERT(maybe_null() || !null_value);
 | 
						|
  return null_value;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void Item_func_round::fix_length_and_dec_decimal(uint decimals_to_set)
 | 
						|
{
 | 
						|
  int decimals_delta= args[0]->decimals - decimals_to_set;
 | 
						|
  int length_increase= (decimals_delta <= 0 || truncate) ? 0 : 1;
 | 
						|
  int precision= args[0]->decimal_precision() + length_increase -
 | 
						|
                                                decimals_delta;
 | 
						|
  DBUG_ASSERT(decimals_to_set <= DECIMAL_MAX_SCALE);
 | 
						|
  set_handler(&type_handler_newdecimal);
 | 
						|
  unsigned_flag= args[0]->unsigned_flag;
 | 
						|
  decimals= decimals_to_set;
 | 
						|
  if (!precision)
 | 
						|
    precision= 1; // DECIMAL(0,0) -> DECIMAL(1,0)
 | 
						|
  max_length= my_decimal_precision_to_length_no_truncation(precision,
 | 
						|
                                                           decimals,
 | 
						|
                                                           unsigned_flag);
 | 
						|
}
 | 
						|
 | 
						|
void Item_func_round::fix_length_and_dec_double(uint decimals_to_set)
 | 
						|
{
 | 
						|
  set_handler(&type_handler_double);
 | 
						|
  unsigned_flag= args[0]->unsigned_flag;
 | 
						|
  decimals= decimals_to_set;
 | 
						|
  max_length= float_length(decimals_to_set);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void Item_func_round::fix_arg_decimal()
 | 
						|
{
 | 
						|
  if (args[1]->const_item())
 | 
						|
  {
 | 
						|
    Longlong_hybrid dec= args[1]->to_longlong_hybrid();
 | 
						|
    if (args[1]->null_value)
 | 
						|
      fix_length_and_dec_double(NOT_FIXED_DEC);
 | 
						|
    else
 | 
						|
      fix_length_and_dec_decimal(dec.to_uint(DECIMAL_MAX_SCALE));
 | 
						|
  }
 | 
						|
  else
 | 
						|
  {
 | 
						|
    set_handler(&type_handler_newdecimal);
 | 
						|
    unsigned_flag= args[0]->unsigned_flag;
 | 
						|
    decimals= args[0]->decimals;
 | 
						|
    max_length= args[0]->max_length;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void Item_func_round::fix_arg_double()
 | 
						|
{
 | 
						|
  if (args[1]->const_item())
 | 
						|
  {
 | 
						|
    Longlong_hybrid dec= args[1]->to_longlong_hybrid();
 | 
						|
    fix_length_and_dec_double(args[1]->null_value ? NOT_FIXED_DEC :
 | 
						|
                              dec.to_uint(NOT_FIXED_DEC));
 | 
						|
  }
 | 
						|
  else
 | 
						|
    fix_length_and_dec_double(args[0]->decimals);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void Item_func_round::fix_arg_temporal(const Type_handler *h,
 | 
						|
                                       uint int_part_length)
 | 
						|
{
 | 
						|
  set_handler(h);
 | 
						|
  if (args[1]->can_eval_in_optimize())
 | 
						|
  {
 | 
						|
    Longlong_hybrid_null dec= args[1]->to_longlong_hybrid_null();
 | 
						|
    fix_attributes_temporal(int_part_length,
 | 
						|
                            dec.is_null() ? args[0]->decimals :
 | 
						|
                            dec.to_uint(TIME_SECOND_PART_DIGITS));
 | 
						|
  }
 | 
						|
  else
 | 
						|
    fix_attributes_temporal(int_part_length, args[0]->decimals);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void Item_func_round::fix_arg_time()
 | 
						|
{
 | 
						|
  fix_arg_temporal(&type_handler_time2, MIN_TIME_WIDTH);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void Item_func_round::fix_arg_datetime()
 | 
						|
{
 | 
						|
  /*
 | 
						|
    Day increment operations are not supported for '0000-00-00',
 | 
						|
    see get_date_from_daynr() for details. Therefore, expressions like
 | 
						|
      ROUND('0000-00-00 23:59:59.999999')
 | 
						|
    return NULL.
 | 
						|
  */
 | 
						|
  if (!truncate)
 | 
						|
    set_maybe_null();
 | 
						|
  fix_arg_temporal(&type_handler_datetime2, MAX_DATETIME_WIDTH);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool Item_func_round::test_if_length_can_increase()
 | 
						|
{
 | 
						|
  if (truncate)
 | 
						|
    return false;
 | 
						|
  if (args[1]->can_eval_in_optimize())
 | 
						|
  {
 | 
						|
    // Length can increase in some cases: e.g. ROUND(9,-1) -> 10.
 | 
						|
    Longlong_hybrid val1= args[1]->to_longlong_hybrid();
 | 
						|
    return !args[1]->null_value && val1.neg();
 | 
						|
  }
 | 
						|
  return true; // ROUND(x,n), where n is not a constant.
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Calculate data type and attributes for INT-alike input.
 | 
						|
 | 
						|
  @param [IN] preferred - The preferred data type handler for simple cases
 | 
						|
                          such as ROUND(x) and TRUNCATE(x,0), when the input
 | 
						|
                          is short enough to fit into an integer type
 | 
						|
                          (without extending to DECIMAL).
 | 
						|
                          - If `preferred` is not NULL, then the code tries
 | 
						|
                            to preserve the given data type handler and
 | 
						|
                            the data type attributes `preferred_attrs`.
 | 
						|
                          - If `preferred` is NULL, then the code fully
 | 
						|
                            calculates attributes using
 | 
						|
                            args[0]->decimal_precision() and chooses between
 | 
						|
                            INT and BIGINT, depending on attributes.
 | 
						|
  @param [IN] preferred_attrs - The preferred data type attributes for
 | 
						|
                                simple cases.
 | 
						|
*/
 | 
						|
void Item_func_round::fix_arg_int(const Type_handler *preferred,
 | 
						|
                                  const Type_std_attributes *preferred_attrs,
 | 
						|
                                  bool use_decimal_on_length_increase)
 | 
						|
{
 | 
						|
  DBUG_ASSERT(args[0]->decimals == 0);
 | 
						|
 | 
						|
  Type_std_attributes::set(preferred_attrs);
 | 
						|
  if (!test_if_length_can_increase())
 | 
						|
  {
 | 
						|
    // Preserve the exact data type and attributes
 | 
						|
    set_handler(preferred);
 | 
						|
  }
 | 
						|
  else
 | 
						|
  {
 | 
						|
    max_length++;
 | 
						|
    if (use_decimal_on_length_increase)
 | 
						|
      set_handler(&type_handler_newdecimal);
 | 
						|
    else
 | 
						|
      set_handler(type_handler_long_or_longlong());
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void Item_func_round::fix_arg_slong_ge0()
 | 
						|
{
 | 
						|
  DBUG_ASSERT(!args[0]->unsigned_flag);
 | 
						|
  DBUG_ASSERT(args[0]->decimals == 0);
 | 
						|
  Type_std_attributes::set(args[0]);
 | 
						|
  /*
 | 
						|
    We're converting the data type from slong_ge0 to slong/slonglong.
 | 
						|
    Add one character for the sign,
 | 
						|
    to change max_length notation from "max_length digits" to
 | 
						|
    "max_length-1 digits and the sign".
 | 
						|
  */
 | 
						|
  max_length+= 1/*sign*/ + test_if_length_can_increase();
 | 
						|
  set_handler(type_handler_long_or_longlong());
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void Item_func_round::fix_arg_hex_hybrid()
 | 
						|
{
 | 
						|
  DBUG_ASSERT(args[0]->decimals == 0);
 | 
						|
  DBUG_ASSERT(args[0]->decimal_precision() < DECIMAL_LONGLONG_DIGITS);
 | 
						|
  DBUG_ASSERT(args[0]->unsigned_flag); // no needs to add sign length
 | 
						|
  bool length_can_increase= test_if_length_can_increase();
 | 
						|
  max_length= args[0]->decimal_precision() + MY_TEST(length_can_increase);
 | 
						|
  unsigned_flag= true;
 | 
						|
  decimals= 0;
 | 
						|
  if (length_can_increase && args[0]->max_length >= 8)
 | 
						|
    set_handler(&type_handler_newdecimal);
 | 
						|
  else
 | 
						|
    set_handler(type_handler_long_or_longlong());
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
double my_double_round(double value, longlong dec_value, bool dec_unsigned,
 | 
						|
                       bool truncate)
 | 
						|
{
 | 
						|
  double tmp;
 | 
						|
  const Longlong_hybrid dec(dec_value, dec_unsigned);
 | 
						|
  const ulonglong abs_dec= dec.abs();
 | 
						|
  /*
 | 
						|
    tmp2 is here to avoid return the value with 80 bit precision
 | 
						|
    This will fix that the test round(0.1,1) = round(0.1,1) is true
 | 
						|
    Tagging with volatile is no guarantee, it may still be optimized away...
 | 
						|
  */
 | 
						|
  volatile double tmp2;
 | 
						|
 | 
						|
  tmp=(abs_dec < array_elements(log_10) ?
 | 
						|
       log_10[abs_dec] : pow(10.0,(double) abs_dec));
 | 
						|
 | 
						|
  // Pre-compute these, to avoid optimizing away e.g. 'floor(v/tmp) * tmp'.
 | 
						|
  volatile double value_div_tmp= value / tmp;
 | 
						|
  volatile double value_mul_tmp= value * tmp;
 | 
						|
 | 
						|
  if (!dec.neg() && std::isinf(tmp)) // "dec" is a too large positive number
 | 
						|
    return value;
 | 
						|
 | 
						|
  if (dec.neg() && std::isinf(tmp)) // "dec" is a too small negative number
 | 
						|
    return 0.0;
 | 
						|
 | 
						|
  // Handle "dec" with a reasonably small absolute value
 | 
						|
  if (!dec.neg() && std::isinf(value_mul_tmp))
 | 
						|
    tmp2= value;
 | 
						|
  else if (truncate)
 | 
						|
  {
 | 
						|
    if (value >= 0.0)
 | 
						|
      tmp2= dec.neg() ? floor(value_div_tmp) * tmp : floor(value_mul_tmp) / tmp;
 | 
						|
    else
 | 
						|
      tmp2= dec.neg() ? ceil(value_div_tmp) * tmp : ceil(value_mul_tmp) / tmp;
 | 
						|
  }
 | 
						|
  else
 | 
						|
    tmp2=dec.neg() ? rint(value_div_tmp) * tmp : rint(value_mul_tmp) / tmp;
 | 
						|
 | 
						|
  return tmp2;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
double Item_func_round::real_op()
 | 
						|
{
 | 
						|
  double value= args[0]->val_real();
 | 
						|
 | 
						|
  if (!(null_value= args[0]->null_value))
 | 
						|
  {
 | 
						|
    longlong dec= args[1]->val_int();
 | 
						|
    if (!(null_value= args[1]->null_value))
 | 
						|
      return my_double_round(value, dec, args[1]->unsigned_flag, truncate);
 | 
						|
  }
 | 
						|
  return 0.0;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
  Rounds a given value to a power of 10 specified as the 'to' argument,
 | 
						|
  avoiding overflows when the value is close to the ulonglong range boundary.
 | 
						|
*/
 | 
						|
 | 
						|
static inline ulonglong my_unsigned_round(ulonglong value, ulonglong to)
 | 
						|
{
 | 
						|
  ulonglong tmp= value / to * to;
 | 
						|
  return (value - tmp < (to >> 1)) ? tmp : tmp + to;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
longlong Item_func_round::int_op()
 | 
						|
{
 | 
						|
  longlong value= args[0]->val_int();
 | 
						|
  longlong dec= args[1]->val_int();
 | 
						|
  decimals= 0;
 | 
						|
  ulonglong abs_dec;
 | 
						|
  if ((null_value= args[0]->null_value || args[1]->null_value))
 | 
						|
    return 0;
 | 
						|
  if ((dec >= 0) || args[1]->unsigned_flag)
 | 
						|
    return value; // integer have not digits after point
 | 
						|
 | 
						|
  abs_dec= Longlong(dec).abs(); // Avoid undefined behavior
 | 
						|
  longlong tmp;
 | 
						|
  
 | 
						|
  if(abs_dec >= array_elements(log_10_int))
 | 
						|
    return 0;
 | 
						|
  
 | 
						|
  tmp= log_10_int[abs_dec];
 | 
						|
  
 | 
						|
  if (truncate)
 | 
						|
    value= (unsigned_flag) ?
 | 
						|
      ((ulonglong) value / tmp) * tmp : (value / tmp) * tmp;
 | 
						|
  else
 | 
						|
    value= (unsigned_flag || value >= 0) ?
 | 
						|
      my_unsigned_round((ulonglong) value, tmp) :
 | 
						|
      -(longlong) my_unsigned_round((ulonglong) -value, tmp);
 | 
						|
  return value;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
my_decimal *Item_func_round::decimal_op(my_decimal *decimal_value)
 | 
						|
{
 | 
						|
  VDec value(args[0]);
 | 
						|
  longlong dec= args[1]->val_int();
 | 
						|
  if (dec >= 0 || args[1]->unsigned_flag)
 | 
						|
    dec= MY_MIN((ulonglong) dec, decimals);
 | 
						|
  else if (dec < INT_MIN)
 | 
						|
    dec= INT_MIN;
 | 
						|
    
 | 
						|
  if (!(null_value= (value.is_null() || args[1]->null_value ||
 | 
						|
                     value.round_to(decimal_value, (int) dec,
 | 
						|
                                    truncate ? TRUNCATE : HALF_UP) > 1)))
 | 
						|
    return decimal_value;
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool Item_func_round::time_op(THD *thd, MYSQL_TIME *to)
 | 
						|
{
 | 
						|
  DBUG_ASSERT(args[0]->type_handler()->mysql_timestamp_type() ==
 | 
						|
              MYSQL_TIMESTAMP_TIME);
 | 
						|
  Time::Options_for_round opt(truncate ? TIME_FRAC_TRUNCATE : TIME_FRAC_ROUND);
 | 
						|
  Longlong_hybrid_null dec= args[1]->to_longlong_hybrid_null();
 | 
						|
  Time *tm= new (to) Time(thd, args[0], opt,
 | 
						|
                          dec.to_uint(TIME_SECOND_PART_DIGITS));
 | 
						|
  null_value= !tm->is_valid_time() || dec.is_null();
 | 
						|
  DBUG_ASSERT(maybe_null() || !null_value);
 | 
						|
  return null_value;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool Item_func_round::date_op(THD *thd, MYSQL_TIME *to, date_mode_t fuzzydate)
 | 
						|
{
 | 
						|
  DBUG_ASSERT(args[0]->type_handler()->mysql_timestamp_type() ==
 | 
						|
              MYSQL_TIMESTAMP_DATETIME);
 | 
						|
  Datetime::Options opt(thd, truncate ? TIME_FRAC_TRUNCATE : TIME_FRAC_ROUND);
 | 
						|
  Longlong_hybrid_null dec= args[1]->to_longlong_hybrid_null();
 | 
						|
  Datetime *tm= new (to) Datetime(thd, args[0], opt,
 | 
						|
                                  dec.to_uint(TIME_SECOND_PART_DIGITS));
 | 
						|
  null_value= !tm->is_valid_datetime() || dec.is_null();
 | 
						|
  DBUG_ASSERT(maybe_null() || !null_value);
 | 
						|
  return null_value;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool Item_func_round::native_op(THD *thd, Native *to)
 | 
						|
{
 | 
						|
  // TODO: turn Item_func_round into Item_handled_func eventually.
 | 
						|
  if (type_handler()->mysql_timestamp_type() == MYSQL_TIMESTAMP_TIME)
 | 
						|
    return Time(thd, this).to_native(to, decimals);
 | 
						|
  DBUG_ASSERT(0);
 | 
						|
  return true;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void Item_func_rand::seed_random(Item *arg)
 | 
						|
{
 | 
						|
  /*
 | 
						|
    TODO: do not do reinit 'rand' for every execute of PS/SP if
 | 
						|
    args[0] is a constant.
 | 
						|
  */
 | 
						|
  uint32 tmp= (uint32) arg->val_int();
 | 
						|
#ifdef WITH_WSREP
 | 
						|
  if (WSREP_ON)
 | 
						|
  {
 | 
						|
    THD *thd= current_thd;
 | 
						|
    if (WSREP(thd))
 | 
						|
    {
 | 
						|
      if (wsrep_thd_is_applying(thd))
 | 
						|
        tmp= thd->wsrep_rand;
 | 
						|
      else
 | 
						|
        thd->wsrep_rand= tmp;
 | 
						|
    }
 | 
						|
  }
 | 
						|
#endif /* WITH_WSREP */
 | 
						|
 | 
						|
  my_rnd_init(rand, (uint32) (tmp*0x10001L+55555555L),
 | 
						|
             (uint32) (tmp*0x10000001L));
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool Item_func_rand::fix_fields(THD *thd,Item **ref)
 | 
						|
{
 | 
						|
  if (Item_real_func::fix_fields(thd, ref))
 | 
						|
    return TRUE;
 | 
						|
  used_tables_cache|= RAND_TABLE_BIT;
 | 
						|
  if (arg_count)
 | 
						|
  {					// Only use argument once in query
 | 
						|
    /*
 | 
						|
      Allocate rand structure once: we must use thd->stmt_arena
 | 
						|
      to create rand in proper mem_root if it's a prepared statement or
 | 
						|
      stored procedure.
 | 
						|
 | 
						|
      No need to send a Rand log event if seed was given eg: RAND(seed),
 | 
						|
      as it will be replicated in the query as such.
 | 
						|
    */
 | 
						|
    DBUG_ASSERT((!rand &&
 | 
						|
                 (thd->active_stmt_arena_to_use()->
 | 
						|
                    is_stmt_prepare_or_first_stmt_execute() ||
 | 
						|
                  thd->active_stmt_arena_to_use()->
 | 
						|
                    is_conventional() ||
 | 
						|
                  thd->active_stmt_arena_to_use()->state ==
 | 
						|
                    Query_arena::STMT_SP_QUERY_ARGUMENTS
 | 
						|
                 )
 | 
						|
                ) || rand);
 | 
						|
    if (!rand && !(rand= (struct my_rnd_struct*)
 | 
						|
                   thd->active_stmt_arena_to_use()->alloc(sizeof(*rand))))
 | 
						|
      return TRUE;
 | 
						|
  }
 | 
						|
  else
 | 
						|
  {
 | 
						|
    /*
 | 
						|
      Save the seed only the first time RAND() is used in the query
 | 
						|
      Once events are forwarded rather than recreated,
 | 
						|
      the following can be skipped if inside the slave thread
 | 
						|
    */
 | 
						|
    if (!(thd->used & THD::RAND_USED))
 | 
						|
    {
 | 
						|
      thd->used|= THD::RAND_USED;
 | 
						|
      thd->rand_saved_seed1= thd->rand.seed1;
 | 
						|
      thd->rand_saved_seed2= thd->rand.seed2;
 | 
						|
    }
 | 
						|
    rand= &thd->rand;
 | 
						|
  }
 | 
						|
  return FALSE;
 | 
						|
}
 | 
						|
 | 
						|
void Item_func_rand::update_used_tables()
 | 
						|
{
 | 
						|
  Item_real_func::update_used_tables();
 | 
						|
  used_tables_cache|= RAND_TABLE_BIT;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
double Item_func_rand::val_real()
 | 
						|
{
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
  if (arg_count)
 | 
						|
  {
 | 
						|
    if (!args[0]->const_item())
 | 
						|
      seed_random(args[0]);
 | 
						|
    else if (first_eval)
 | 
						|
    {
 | 
						|
      /*
 | 
						|
        Constantness of args[0] may be set during JOIN::optimize(), if arg[0]
 | 
						|
        is a field item of "constant" table. Thus, we have to evaluate
 | 
						|
        seed_random() for constant arg there but not at the fix_fields method.
 | 
						|
      */
 | 
						|
      first_eval= FALSE;
 | 
						|
      seed_random(args[0]);
 | 
						|
    }
 | 
						|
  }
 | 
						|
  return my_rnd(rand);
 | 
						|
}
 | 
						|
 | 
						|
longlong Item_func_sign::val_int()
 | 
						|
{
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
  double value= args[0]->val_real();
 | 
						|
  null_value=args[0]->null_value;
 | 
						|
  return value < 0.0 ? -1 : (value > 0 ? 1 : 0);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
double Item_func_units::val_real()
 | 
						|
{
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
  double value= args[0]->val_real();
 | 
						|
  if ((null_value=args[0]->null_value))
 | 
						|
    return 0;
 | 
						|
  return check_float_overflow(value * mul + add);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool Item_func_min_max::fix_attributes(Item **items, uint nitems)
 | 
						|
{
 | 
						|
  bool rc= Item_func_min_max::type_handler()->
 | 
						|
             Item_func_min_max_fix_attributes(current_thd, this, items, nitems);
 | 
						|
  DBUG_ASSERT(!rc || current_thd->is_error());
 | 
						|
  return rc;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
  Compare item arguments using DATETIME/DATE/TIME representation.
 | 
						|
 | 
						|
  DESCRIPTION
 | 
						|
    Compare item arguments as DATETIME values and return the index of the
 | 
						|
    least/greatest argument in the arguments array.
 | 
						|
    The correct DATE/DATETIME value of the found argument is
 | 
						|
    stored to the value pointer, if latter is provided.
 | 
						|
 | 
						|
  RETURN
 | 
						|
   1	If one of arguments is NULL or there was a execution error
 | 
						|
   0    Otherwise
 | 
						|
*/
 | 
						|
 | 
						|
bool Item_func_min_max::get_date_native(THD *thd, MYSQL_TIME *ltime,
 | 
						|
                                        date_mode_t fuzzydate)
 | 
						|
{
 | 
						|
  longlong UNINIT_VAR(min_max);
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
 | 
						|
  for (uint i=0; i < arg_count ; i++)
 | 
						|
  {
 | 
						|
    longlong res= args[i]->val_datetime_packed(thd);
 | 
						|
 | 
						|
    /* Check if we need to stop (because of error or KILL) and stop the loop */
 | 
						|
    if (unlikely(args[i]->null_value))
 | 
						|
      return (null_value= 1);
 | 
						|
 | 
						|
    if (i == 0 || (res < min_max ? cmp_sign : -cmp_sign) > 0)
 | 
						|
      min_max= res;
 | 
						|
  }
 | 
						|
  unpack_time(min_max, ltime, mysql_timestamp_type());
 | 
						|
 | 
						|
  if (!(fuzzydate & TIME_TIME_ONLY) &&
 | 
						|
      unlikely((null_value= check_date_with_warn(thd, ltime, fuzzydate,
 | 
						|
                                         MYSQL_TIMESTAMP_ERROR))))
 | 
						|
    return true;
 | 
						|
 | 
						|
  return (null_value= 0);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool Item_func_min_max::get_time_native(THD *thd, MYSQL_TIME *ltime)
 | 
						|
{
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
 | 
						|
  Time value(thd, args[0], Time::Options(thd), decimals);
 | 
						|
  if (!value.is_valid_time())
 | 
						|
    return (null_value= true);
 | 
						|
 | 
						|
  for (uint i= 1; i < arg_count ; i++)
 | 
						|
  {
 | 
						|
    Time tmp(thd, args[i], Time::Options(thd), decimals);
 | 
						|
    if (!tmp.is_valid_time())
 | 
						|
      return (null_value= true);
 | 
						|
 | 
						|
    int cmp= value.cmp(&tmp);
 | 
						|
    if ((cmp_sign < 0 ? cmp : -cmp) < 0)
 | 
						|
      value= tmp;
 | 
						|
  }
 | 
						|
  value.copy_to_mysql_time(ltime);
 | 
						|
  return (null_value= 0);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
String *Item_func_min_max::val_str_native(String *str)
 | 
						|
{
 | 
						|
  String *UNINIT_VAR(res);
 | 
						|
  for (uint i=0; i < arg_count ; i++)
 | 
						|
  {
 | 
						|
    if (i == 0)
 | 
						|
      res=args[i]->val_str(str);
 | 
						|
    else
 | 
						|
    {
 | 
						|
      String *res2= args[i]->val_str(&tmp_value);
 | 
						|
      if (res2)
 | 
						|
      {
 | 
						|
        int cmp= sortcmp(res,res2,collation.collation);
 | 
						|
        if ((cmp_sign < 0 ? cmp : -cmp) < 0)
 | 
						|
        {
 | 
						|
          str->copy(*res2);
 | 
						|
          res= str;
 | 
						|
        }
 | 
						|
      }
 | 
						|
    }
 | 
						|
    if ((null_value= args[i]->null_value))
 | 
						|
      return 0;
 | 
						|
  }
 | 
						|
  res->set_charset(collation.collation);
 | 
						|
  return res;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
double Item_func_min_max::val_real_native()
 | 
						|
{
 | 
						|
  double value=0.0;
 | 
						|
  for (uint i=0; i < arg_count ; i++)
 | 
						|
  {
 | 
						|
    if (i == 0)
 | 
						|
      value= args[i]->val_real();
 | 
						|
    else
 | 
						|
    {
 | 
						|
      double tmp= args[i]->val_real();
 | 
						|
      if (!args[i]->null_value && (tmp < value ? cmp_sign : -cmp_sign) > 0)
 | 
						|
	value=tmp;
 | 
						|
    }
 | 
						|
    if ((null_value= args[i]->null_value))
 | 
						|
      return 0;
 | 
						|
  }
 | 
						|
  return value;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
longlong Item_func_min_max::val_int_native()
 | 
						|
{
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
  longlong value=0;
 | 
						|
  for (uint i=0; i < arg_count ; i++)
 | 
						|
  {
 | 
						|
    if (i == 0)
 | 
						|
      value=args[i]->val_int();
 | 
						|
    else
 | 
						|
    {
 | 
						|
      longlong tmp=args[i]->val_int();
 | 
						|
      if (!args[i]->null_value && (tmp < value ? cmp_sign : -cmp_sign) > 0)
 | 
						|
	value=tmp;
 | 
						|
    }
 | 
						|
    if ((null_value= args[i]->null_value))
 | 
						|
      return 0;
 | 
						|
  }
 | 
						|
  return value;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
longlong Item_func_min_max::val_uint_native()
 | 
						|
{
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
  ulonglong value= 0;
 | 
						|
  for (uint i=0; i < arg_count ; i++)
 | 
						|
  {
 | 
						|
    if (i == 0)
 | 
						|
      value= (ulonglong) args[i]->val_int();
 | 
						|
    else
 | 
						|
    {
 | 
						|
      ulonglong tmp= (ulonglong) args[i]->val_int();
 | 
						|
      if (!args[i]->null_value && (tmp < value ? cmp_sign : -cmp_sign) > 0)
 | 
						|
	value= tmp;
 | 
						|
    }
 | 
						|
    if ((null_value= args[i]->null_value))
 | 
						|
      return 0;
 | 
						|
  }
 | 
						|
  return (longlong) value;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
my_decimal *Item_func_min_max::val_decimal_native(my_decimal *dec)
 | 
						|
{
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
  my_decimal tmp_buf, *tmp, *UNINIT_VAR(res);
 | 
						|
 | 
						|
  for (uint i=0; i < arg_count ; i++)
 | 
						|
  {
 | 
						|
    if (i == 0)
 | 
						|
      res= args[i]->val_decimal(dec);
 | 
						|
    else
 | 
						|
    {
 | 
						|
      tmp= args[i]->val_decimal(&tmp_buf);      // Zero if NULL
 | 
						|
      if (tmp && (my_decimal_cmp(tmp, res) * cmp_sign) < 0)
 | 
						|
      {
 | 
						|
        if (tmp == &tmp_buf)
 | 
						|
        {
 | 
						|
          /* Move value out of tmp_buf as this will be reused on next loop */
 | 
						|
          my_decimal2decimal(tmp, dec);
 | 
						|
          res= dec;
 | 
						|
        }
 | 
						|
        else
 | 
						|
          res= tmp;
 | 
						|
      }
 | 
						|
    }
 | 
						|
    if ((null_value= args[i]->null_value))
 | 
						|
    {
 | 
						|
      res= 0;
 | 
						|
      break;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  return res;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool Item_func_min_max::val_native(THD *thd, Native *native)
 | 
						|
{
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
  const Type_handler *handler= Item_hybrid_func::type_handler();
 | 
						|
  NativeBuffer<STRING_BUFFER_USUAL_SIZE> cur;
 | 
						|
  for (uint i= 0; i < arg_count; i++)
 | 
						|
  {
 | 
						|
    if (val_native_with_conversion_from_item(thd, args[i],
 | 
						|
                                             i == 0 ? native : &cur,
 | 
						|
                                             handler))
 | 
						|
      return true;
 | 
						|
    if (i > 0)
 | 
						|
    {
 | 
						|
      int cmp= handler->cmp_native(*native, cur);
 | 
						|
      if ((cmp_sign < 0 ? cmp : -cmp) < 0 && native->copy(cur))
 | 
						|
        return null_value= true;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  return null_value= false;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
longlong Item_func_bit_length::val_int()
 | 
						|
{
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
  String *res= args[0]->val_str(&value);
 | 
						|
  return (null_value= !res) ? 0 : (longlong) res->length() * 8;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
longlong Item_func_octet_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;
 | 
						|
  return (longlong) res->length();
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
longlong Item_func_char_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;
 | 
						|
  return (longlong) res->numchars();
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool Item_func_coercibility::fix_length_and_dec(THD *thd)
 | 
						|
{
 | 
						|
  max_length=10;
 | 
						|
  base_flags&= ~item_base_t::MAYBE_NULL;
 | 
						|
  /*
 | 
						|
    Since this is a const item which doesn't use tables (see used_tables()),
 | 
						|
    we don't want to access the function arguments during execution.
 | 
						|
    That's why we store the derivation here during the preparation phase
 | 
						|
    and only return it later at the execution phase
 | 
						|
  */
 | 
						|
  DBUG_ASSERT(args[0]->fixed());
 | 
						|
  m_cached_collation_derivation= (longlong) args[0]->collation.derivation;
 | 
						|
  return false;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
longlong Item_func_coercibility::val_int()
 | 
						|
{
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
  null_value= 0;
 | 
						|
  return m_cached_collation_derivation;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
longlong Item_func_locate::val_int()
 | 
						|
{
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
  String *a=args[0]->val_str(&value1);
 | 
						|
  String *b=args[1]->val_str(&value2);
 | 
						|
  if (!a || !b)
 | 
						|
  {
 | 
						|
    null_value=1;
 | 
						|
    return 0; /* purecov: inspected */
 | 
						|
  }
 | 
						|
  null_value=0;
 | 
						|
  /* must be longlong to avoid truncation */
 | 
						|
  longlong start=  0; 
 | 
						|
  longlong start0= 0;
 | 
						|
  my_match_t match;
 | 
						|
 | 
						|
  if (arg_count == 3)
 | 
						|
  {
 | 
						|
    start0= start= args[2]->val_int();
 | 
						|
 | 
						|
    if ((start <= 0) || (start > a->length()))
 | 
						|
      return 0;
 | 
						|
    start0--; start--;
 | 
						|
 | 
						|
    /* start is now sufficiently valid to pass to charpos function */
 | 
						|
    start= a->charpos((int) start);
 | 
						|
 | 
						|
    if (start + b->length() > a->length())
 | 
						|
      return 0;
 | 
						|
  }
 | 
						|
 | 
						|
  if (!b->length())				// Found empty string at start
 | 
						|
    return start + 1;
 | 
						|
  
 | 
						|
  if (!cmp_collation.collation->instr(a->ptr() + start,
 | 
						|
                                      (uint) (a->length() - start),
 | 
						|
                                      b->ptr(), b->length(),
 | 
						|
                                      &match, 1))
 | 
						|
    return 0;
 | 
						|
  return (longlong) match.mb_len + start0 + 1;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void Item_func_locate::print(String *str, enum_query_type query_type)
 | 
						|
{
 | 
						|
  str->append(STRING_WITH_LEN("locate("));
 | 
						|
  args[1]->print(str, query_type);
 | 
						|
  str->append(',');
 | 
						|
  args[0]->print(str, query_type);
 | 
						|
  if (arg_count == 3)
 | 
						|
  {
 | 
						|
    str->append(',');
 | 
						|
    args[2]->print(str, query_type);
 | 
						|
  }
 | 
						|
  str->append(')');
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
longlong Item_func_field::val_int()
 | 
						|
{
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
 | 
						|
  if (cmp_type == STRING_RESULT)
 | 
						|
  {
 | 
						|
    String *field;
 | 
						|
    if (!(field= args[0]->val_str(&value)))
 | 
						|
      return 0;
 | 
						|
    for (uint i=1 ; i < arg_count ; i++)
 | 
						|
    {
 | 
						|
      String *tmp_value=args[i]->val_str(&tmp);
 | 
						|
      if (tmp_value && !sortcmp(field,tmp_value,cmp_collation.collation))
 | 
						|
        return (longlong) (i);
 | 
						|
    }
 | 
						|
  }
 | 
						|
  else if (cmp_type == INT_RESULT)
 | 
						|
  {
 | 
						|
    longlong val= args[0]->val_int();
 | 
						|
    if (args[0]->null_value)
 | 
						|
      return 0;
 | 
						|
    for (uint i=1; i < arg_count ; i++)
 | 
						|
    {
 | 
						|
      if (val == args[i]->val_int() && !args[i]->null_value)
 | 
						|
        return (longlong) (i);
 | 
						|
    }
 | 
						|
  }
 | 
						|
  else if (cmp_type == DECIMAL_RESULT)
 | 
						|
  {
 | 
						|
    VDec dec(args[0]);
 | 
						|
    if (dec.is_null())
 | 
						|
      return 0;
 | 
						|
    my_decimal dec_arg_buf;
 | 
						|
    for (uint i=1; i < arg_count; i++)
 | 
						|
    {
 | 
						|
      my_decimal *dec_arg= args[i]->val_decimal(&dec_arg_buf);
 | 
						|
      if (!args[i]->null_value && !dec.cmp(dec_arg))
 | 
						|
        return (longlong) (i);
 | 
						|
    }
 | 
						|
  }
 | 
						|
  else
 | 
						|
  {
 | 
						|
    double val= args[0]->val_real();
 | 
						|
    if (args[0]->null_value)
 | 
						|
      return 0;
 | 
						|
    for (uint i=1; i < arg_count ; i++)
 | 
						|
    {
 | 
						|
      if (val == args[i]->val_real() && !args[i]->null_value)
 | 
						|
        return (longlong) (i);
 | 
						|
    }
 | 
						|
  }
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool Item_func_field::fix_length_and_dec(THD *thd)
 | 
						|
{
 | 
						|
  base_flags&= ~item_base_t::MAYBE_NULL;
 | 
						|
  max_length=3;
 | 
						|
  cmp_type= args[0]->result_type();
 | 
						|
  for (uint i=1; i < arg_count ; i++)
 | 
						|
    cmp_type= item_cmp_type(cmp_type, args[i]->result_type());
 | 
						|
  if (cmp_type == STRING_RESULT)
 | 
						|
    return agg_arg_charsets_for_comparison(cmp_collation, args, arg_count);
 | 
						|
  return FALSE;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
longlong Item_func_ascii::val_int()
 | 
						|
{
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
  String *res=args[0]->val_str(&value);
 | 
						|
  if (!res)
 | 
						|
  {
 | 
						|
    null_value=1;
 | 
						|
    return 0;
 | 
						|
  }
 | 
						|
  null_value=0;
 | 
						|
  return (longlong) (res->length() ? (uchar) (*res)[0] : (uchar) 0);
 | 
						|
}
 | 
						|
 | 
						|
longlong Item_func_ord::val_int()
 | 
						|
{
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
  String *res=args[0]->val_str(&value);
 | 
						|
  if (!res)
 | 
						|
  {
 | 
						|
    null_value=1;
 | 
						|
    return 0;
 | 
						|
  }
 | 
						|
  null_value=0;
 | 
						|
  if (!res->length()) return 0;
 | 
						|
#ifdef USE_MB
 | 
						|
  if (res->use_mb())
 | 
						|
  {
 | 
						|
    const char *str=res->ptr();
 | 
						|
    uint32 n=0, l=my_ismbchar(res->charset(),str,str+res->length());
 | 
						|
    if (!l)
 | 
						|
      return (longlong)((uchar) *str);
 | 
						|
    while (l--)
 | 
						|
      n=(n<<8)|(uint32)((uchar) *str++);
 | 
						|
    return (longlong) n;
 | 
						|
  }
 | 
						|
#endif
 | 
						|
  return (longlong) ((uchar) (*res)[0]);
 | 
						|
}
 | 
						|
 | 
						|
	/* Search after a string in a string of strings separated by ',' */
 | 
						|
	/* Returns number of found type >= 1 or 0 if not found */
 | 
						|
	/* This optimizes searching in enums to bit testing! */
 | 
						|
 | 
						|
bool Item_func_find_in_set::fix_length_and_dec(THD *thd)
 | 
						|
{
 | 
						|
  decimals=0;
 | 
						|
  max_length=3;					// 1-999
 | 
						|
  if (args[0]->const_item() && args[1]->type() == FIELD_ITEM)
 | 
						|
  {
 | 
						|
    Field *field= ((Item_field*) args[1])->field;
 | 
						|
    if (field->real_type() == MYSQL_TYPE_SET)
 | 
						|
    {
 | 
						|
      String *find=args[0]->val_str(&value);
 | 
						|
      if (find)
 | 
						|
      {
 | 
						|
        // find is not NULL pointer so args[0] is not a null-value
 | 
						|
        DBUG_ASSERT(!args[0]->null_value);
 | 
						|
	enum_value= find_type(((Field_enum*) field)->typelib,find->ptr(),
 | 
						|
			      find->length(), 0);
 | 
						|
	enum_bit=0;
 | 
						|
	if (enum_value)
 | 
						|
	  enum_bit= 1ULL << (enum_value-1);
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
  return agg_arg_charsets_for_comparison(cmp_collation, args, 2);
 | 
						|
}
 | 
						|
 | 
						|
static const char separator=',';
 | 
						|
 | 
						|
longlong Item_func_find_in_set::val_int()
 | 
						|
{
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
  if (enum_value)
 | 
						|
  {
 | 
						|
    // enum_value is set iff args[0]->const_item() in fix_length_and_dec().
 | 
						|
    DBUG_ASSERT(args[0]->const_item());
 | 
						|
 | 
						|
    ulonglong tmp= (ulonglong) args[1]->val_int();
 | 
						|
    null_value= args[1]->null_value;
 | 
						|
    /* 
 | 
						|
      No need to check args[0]->null_value since enum_value is set iff
 | 
						|
      args[0] is a non-null const item. Note: no DBUG_ASSERT on
 | 
						|
      args[0]->null_value here because args[0] may have been replaced
 | 
						|
      by an Item_cache on which val_int() has not been called. See
 | 
						|
      BUG#11766317
 | 
						|
    */
 | 
						|
    if (!null_value)
 | 
						|
    {
 | 
						|
      if (tmp & enum_bit)
 | 
						|
        return enum_value;
 | 
						|
    }
 | 
						|
    return 0L;
 | 
						|
  }
 | 
						|
 | 
						|
  String *find=args[0]->val_str(&value);
 | 
						|
  String *buffer=args[1]->val_str(&value2);
 | 
						|
  if (!find || !buffer)
 | 
						|
  {
 | 
						|
    null_value=1;
 | 
						|
    return 0; /* purecov: inspected */
 | 
						|
  }
 | 
						|
  null_value=0;
 | 
						|
 | 
						|
  if ((int) (buffer->length() - find->length()) >= 0)
 | 
						|
  {
 | 
						|
    my_wc_t wc= 0;
 | 
						|
    CHARSET_INFO *cs= cmp_collation.collation;
 | 
						|
    const char *str_begin= buffer->ptr();
 | 
						|
    const char *str_end= buffer->ptr();
 | 
						|
    const char *real_end= str_end+buffer->length();
 | 
						|
    const char *find_str= find->ptr();
 | 
						|
    uint find_str_len= find->length();
 | 
						|
    int position= 0;
 | 
						|
    while (1)
 | 
						|
    {
 | 
						|
      int symbol_len;
 | 
						|
      if ((symbol_len= cs->mb_wc(&wc, (uchar*) str_end,
 | 
						|
                                 (uchar*) real_end)) > 0)
 | 
						|
      {
 | 
						|
        const char *substr_end= str_end + symbol_len;
 | 
						|
        bool is_last_item= (substr_end == real_end);
 | 
						|
        bool is_separator= (wc == (my_wc_t) separator);
 | 
						|
        if (is_separator || is_last_item)
 | 
						|
        {
 | 
						|
          position++;
 | 
						|
          if (is_last_item && !is_separator)
 | 
						|
            str_end= substr_end;
 | 
						|
          if (!cs->strnncoll(str_begin, (uint) (str_end - str_begin),
 | 
						|
                             find_str, find_str_len))
 | 
						|
            return (longlong) position;
 | 
						|
          else
 | 
						|
            str_begin= substr_end;
 | 
						|
        }
 | 
						|
        str_end= substr_end;
 | 
						|
      }
 | 
						|
      else if (str_end - str_begin == 0 &&
 | 
						|
               find_str_len == 0 &&
 | 
						|
               wc == (my_wc_t) separator)
 | 
						|
        return (longlong) ++position;
 | 
						|
      else
 | 
						|
        return 0;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
class Func_handler_bit_count_int_to_slong:
 | 
						|
        public Item_handled_func::Handler_slong2
 | 
						|
{
 | 
						|
public:
 | 
						|
  Longlong_null to_longlong_null(Item_handled_func *item) const override
 | 
						|
  {
 | 
						|
    DBUG_ASSERT(item->fixed());
 | 
						|
    return item->arguments()[0]->to_longlong_null().bit_count();
 | 
						|
  }
 | 
						|
};
 | 
						|
 | 
						|
 | 
						|
class Func_handler_bit_count_decimal_to_slong:
 | 
						|
        public Item_handled_func::Handler_slong2
 | 
						|
{
 | 
						|
public:
 | 
						|
  Longlong_null to_longlong_null(Item_handled_func *item) const override
 | 
						|
  {
 | 
						|
    DBUG_ASSERT(item->fixed());
 | 
						|
    return VDec(item->arguments()[0]).to_xlonglong_null().bit_count();
 | 
						|
  }
 | 
						|
};
 | 
						|
 | 
						|
 | 
						|
bool Item_func_bit_count::fix_length_and_dec(THD *thd)
 | 
						|
{
 | 
						|
  static Func_handler_bit_count_int_to_slong ha_int_to_slong;
 | 
						|
  static Func_handler_bit_count_decimal_to_slong ha_dec_to_slong;
 | 
						|
  set_func_handler(args[0]->cmp_type() == INT_RESULT ?
 | 
						|
                   (const Handler *) &ha_int_to_slong :
 | 
						|
                   (const Handler *) &ha_dec_to_slong);
 | 
						|
  return m_func_handler->fix_length_and_dec(this);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/****************************************************************************
 | 
						|
** Functions to handle dynamic loadable functions
 | 
						|
** Original source by: Alexis Mikhailov <root@medinf.chuvashia.su>
 | 
						|
** Rewritten by monty.
 | 
						|
****************************************************************************/
 | 
						|
 | 
						|
#ifdef HAVE_DLOPEN
 | 
						|
 | 
						|
void udf_handler::cleanup()
 | 
						|
{
 | 
						|
  if (!not_original)
 | 
						|
  {
 | 
						|
    if (initialized)
 | 
						|
    {
 | 
						|
      if (u_d->func_deinit != NULL)
 | 
						|
      {
 | 
						|
        Udf_func_deinit deinit= u_d->func_deinit;
 | 
						|
        (*deinit)(&initid);
 | 
						|
      }
 | 
						|
      free_udf(u_d);
 | 
						|
      initialized= FALSE;
 | 
						|
    }
 | 
						|
    if (buffers)				// Because of bug in ecc
 | 
						|
      delete [] buffers;
 | 
						|
    buffers= 0;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool
 | 
						|
udf_handler::fix_fields(THD *thd, Item_func_or_sum *func,
 | 
						|
			uint arg_count, Item **arguments)
 | 
						|
{
 | 
						|
  uchar buff[STACK_BUFF_ALLOC];			// Max argument in function
 | 
						|
  DBUG_ENTER("Item_udf_func::fix_fields");
 | 
						|
 | 
						|
  if (check_stack_overrun(thd, STACK_MIN_SIZE, buff))
 | 
						|
    DBUG_RETURN(TRUE);				// Fatal error flag is set!
 | 
						|
 | 
						|
  udf_func *tmp_udf=find_udf(u_d->name.str,u_d->name.length,1);
 | 
						|
 | 
						|
  if (!tmp_udf)
 | 
						|
  {
 | 
						|
    my_error(ER_CANT_FIND_UDF, MYF(0), u_d->name.str);
 | 
						|
    DBUG_RETURN(TRUE);
 | 
						|
  }
 | 
						|
  u_d=tmp_udf;
 | 
						|
  args=arguments;
 | 
						|
 | 
						|
  /* Fix all arguments */
 | 
						|
  func->base_flags&= ~item_base_t::MAYBE_NULL;
 | 
						|
  func->used_tables_and_const_cache_init();
 | 
						|
 | 
						|
  if ((f_args.arg_count=arg_count))
 | 
						|
  {
 | 
						|
    if (!(f_args.arg_type= (Item_result*)
 | 
						|
	  thd->alloc(f_args.arg_count*sizeof(Item_result))))
 | 
						|
 | 
						|
    {
 | 
						|
    err_exit:
 | 
						|
      free_udf(u_d);
 | 
						|
      DBUG_RETURN(TRUE);
 | 
						|
    }
 | 
						|
    uint i;
 | 
						|
    Item **arg,**arg_end;
 | 
						|
    for (i=0, arg=arguments, arg_end=arguments+arg_count;
 | 
						|
	 arg != arg_end ;
 | 
						|
	 arg++,i++)
 | 
						|
    {
 | 
						|
      if ((*arg)->fix_fields_if_needed_for_scalar(thd, arg))
 | 
						|
	DBUG_RETURN(true);
 | 
						|
      // we can't assign 'item' before, because fix_fields() can change arg
 | 
						|
      Item *item= *arg;
 | 
						|
      /*
 | 
						|
	TODO: We should think about this. It is not always
 | 
						|
	right way just to set an UDF result to return my_charset_bin
 | 
						|
	if one argument has binary sorting order.
 | 
						|
	The result collation should be calculated according to arguments
 | 
						|
	derivations in some cases and should not in other cases.
 | 
						|
	Moreover, some arguments can represent a numeric input
 | 
						|
	which doesn't effect the result character set and collation.
 | 
						|
	There is no a general rule for UDF. Everything depends on
 | 
						|
        the particular user defined function.
 | 
						|
      */
 | 
						|
      if (item->collation.collation->state & MY_CS_BINSORT)
 | 
						|
	func->collation.set(&my_charset_bin);
 | 
						|
      func->base_flags|= item->base_flags & item_base_t::MAYBE_NULL;
 | 
						|
      func->with_flags|= item->with_flags;
 | 
						|
      func->used_tables_and_const_cache_join(item);
 | 
						|
      f_args.arg_type[i]=item->result_type();
 | 
						|
    }
 | 
						|
    buffers=new (thd->mem_root) String[arg_count];
 | 
						|
    if (!buffers ||
 | 
						|
        !multi_alloc_root(thd->mem_root,
 | 
						|
                          &f_args.args,              arg_count * sizeof(char *),
 | 
						|
                          &f_args.lengths,           arg_count * sizeof(long),
 | 
						|
                          &f_args.maybe_null,        arg_count * sizeof(char),
 | 
						|
                          &num_buffer,               arg_count * sizeof(double),
 | 
						|
                          &f_args.attributes,        arg_count * sizeof(char *),
 | 
						|
                          &f_args.attribute_lengths, arg_count * sizeof(long),
 | 
						|
                          NullS))
 | 
						|
      goto err_exit;
 | 
						|
  }
 | 
						|
  if (func->fix_length_and_dec(thd))
 | 
						|
    DBUG_RETURN(TRUE);
 | 
						|
  initid.max_length=func->max_length;
 | 
						|
  initid.maybe_null=func->maybe_null();
 | 
						|
  initid.const_item=func->const_item_cache;
 | 
						|
  initid.decimals=func->decimals;
 | 
						|
  initid.ptr=0;
 | 
						|
  not_original=0;
 | 
						|
  for (uint i1= 0 ; i1 < arg_count ; i1++)
 | 
						|
    buffers[i1].set_thread_specific();
 | 
						|
 | 
						|
  if (u_d->func_init)
 | 
						|
  {
 | 
						|
    char init_msg_buff[MYSQL_ERRMSG_SIZE];
 | 
						|
    char *to=num_buffer;
 | 
						|
    for (uint i=0; i < arg_count; i++)
 | 
						|
    {
 | 
						|
      /*
 | 
						|
       For a constant argument i, args->args[i] points to the argument value. 
 | 
						|
       For non-constant, args->args[i] is NULL.
 | 
						|
      */
 | 
						|
      f_args.args[i]= NULL;         /* Non-const unless updated below. */
 | 
						|
 | 
						|
      f_args.lengths[i]= arguments[i]->max_length;
 | 
						|
      f_args.maybe_null[i]= (char) arguments[i]->maybe_null();
 | 
						|
      f_args.attributes[i]= arguments[i]->name.str;
 | 
						|
      f_args.attribute_lengths[i]= (ulong)arguments[i]->name.length;
 | 
						|
 | 
						|
      if (arguments[i]->const_item())
 | 
						|
      {
 | 
						|
        switch (arguments[i]->result_type()) {
 | 
						|
        case STRING_RESULT:
 | 
						|
        case DECIMAL_RESULT:
 | 
						|
        {
 | 
						|
          String *res= arguments[i]->val_str(&buffers[i]);
 | 
						|
          if (arguments[i]->null_value)
 | 
						|
            continue;
 | 
						|
          f_args.args[i]= (char*) res->c_ptr_safe();
 | 
						|
          f_args.lengths[i]= res->length();
 | 
						|
          break;
 | 
						|
        }
 | 
						|
        case INT_RESULT:
 | 
						|
          *((longlong*) to)= arguments[i]->val_int();
 | 
						|
          if (arguments[i]->null_value)
 | 
						|
            continue;
 | 
						|
          f_args.args[i]= to;
 | 
						|
          to+= ALIGN_SIZE(sizeof(longlong));
 | 
						|
          break;
 | 
						|
        case REAL_RESULT:
 | 
						|
          *((double*) to)= arguments[i]->val_real();
 | 
						|
          if (arguments[i]->null_value)
 | 
						|
            continue;
 | 
						|
          f_args.args[i]= to;
 | 
						|
          to+= ALIGN_SIZE(sizeof(double));
 | 
						|
          break;
 | 
						|
        case ROW_RESULT:
 | 
						|
        case TIME_RESULT:
 | 
						|
          DBUG_ASSERT(0);          // This case should never be chosen
 | 
						|
          break;
 | 
						|
        }
 | 
						|
      }
 | 
						|
    }
 | 
						|
    Udf_func_init init= u_d->func_init;
 | 
						|
    if (unlikely((error=(uchar) init(&initid, &f_args, init_msg_buff))))
 | 
						|
    {
 | 
						|
      my_error(ER_CANT_INITIALIZE_UDF, MYF(0),
 | 
						|
               u_d->name.str, init_msg_buff);
 | 
						|
      goto err_exit;
 | 
						|
    }
 | 
						|
    func->max_length=MY_MIN(initid.max_length,MAX_BLOB_WIDTH);
 | 
						|
    func->set_maybe_null(initid.maybe_null);
 | 
						|
    /*
 | 
						|
      The above call for init() can reset initid.const_item to "false",
 | 
						|
      e.g. when the UDF function wants to be non-deterministic.
 | 
						|
      See sequence_init() in udf_example.cc.
 | 
						|
    */
 | 
						|
    func->const_item_cache= initid.const_item;
 | 
						|
    func->decimals=MY_MIN(initid.decimals,NOT_FIXED_DEC);
 | 
						|
  }
 | 
						|
  initialized=1;
 | 
						|
  if (unlikely(error))
 | 
						|
  {
 | 
						|
    my_error(ER_CANT_INITIALIZE_UDF, MYF(0),
 | 
						|
             u_d->name.str, ER_THD(thd, ER_UNKNOWN_ERROR));
 | 
						|
    DBUG_RETURN(TRUE);
 | 
						|
  }
 | 
						|
  DBUG_RETURN(FALSE);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool udf_handler::get_arguments()
 | 
						|
{
 | 
						|
  if (unlikely(error))
 | 
						|
    return 1;					// Got an error earlier
 | 
						|
  char *to= num_buffer;
 | 
						|
  uint str_count=0;
 | 
						|
  for (uint i=0; i < f_args.arg_count; i++)
 | 
						|
  {
 | 
						|
    f_args.args[i]=0;
 | 
						|
    switch (f_args.arg_type[i]) {
 | 
						|
    case STRING_RESULT:
 | 
						|
    case DECIMAL_RESULT:
 | 
						|
      {
 | 
						|
	String *res=args[i]->val_str(&buffers[str_count++]);
 | 
						|
	if (!(args[i]->null_value))
 | 
						|
	{
 | 
						|
	  f_args.args[i]=    (char*) res->ptr();
 | 
						|
	  f_args.lengths[i]= res->length();
 | 
						|
	}
 | 
						|
	else
 | 
						|
	{
 | 
						|
	  f_args.lengths[i]= 0;
 | 
						|
	}
 | 
						|
	break;
 | 
						|
      }
 | 
						|
    case INT_RESULT:
 | 
						|
      *((longlong*) to) = args[i]->val_int();
 | 
						|
      if (!args[i]->null_value)
 | 
						|
      {
 | 
						|
	f_args.args[i]=to;
 | 
						|
	to+= ALIGN_SIZE(sizeof(longlong));
 | 
						|
      }
 | 
						|
      break;
 | 
						|
    case REAL_RESULT:
 | 
						|
      *((double*) to)= args[i]->val_real();
 | 
						|
      if (!args[i]->null_value)
 | 
						|
      {
 | 
						|
	f_args.args[i]=to;
 | 
						|
	to+= ALIGN_SIZE(sizeof(double));
 | 
						|
      }
 | 
						|
      break;
 | 
						|
    case ROW_RESULT:
 | 
						|
    case TIME_RESULT:
 | 
						|
      DBUG_ASSERT(0);              // This case should never be chosen
 | 
						|
      break;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  @return
 | 
						|
    (String*)NULL in case of NULL values
 | 
						|
*/
 | 
						|
String *udf_handler::val_str(String *str,String *save_str)
 | 
						|
{
 | 
						|
  uchar is_null_tmp=0;
 | 
						|
  ulong res_length;
 | 
						|
  DBUG_ENTER("udf_handler::val_str");
 | 
						|
 | 
						|
  if (get_arguments())
 | 
						|
    DBUG_RETURN(0);
 | 
						|
  char * (*func)(UDF_INIT *, UDF_ARGS *, char *, ulong *, uchar *, uchar *)=
 | 
						|
    (char* (*)(UDF_INIT *, UDF_ARGS *, char *, ulong *, uchar *, uchar *))
 | 
						|
    u_d->func;
 | 
						|
 | 
						|
  if ((res_length=str->alloced_length()) < MAX_FIELD_WIDTH)
 | 
						|
  {						// This happens VERY seldom
 | 
						|
    if (str->alloc(MAX_FIELD_WIDTH))
 | 
						|
    {
 | 
						|
      error=1;
 | 
						|
      DBUG_RETURN(0);
 | 
						|
    }
 | 
						|
  }
 | 
						|
  char *res=func(&initid, &f_args, (char*) str->ptr(), &res_length,
 | 
						|
		 &is_null_tmp, &error);
 | 
						|
  DBUG_PRINT("info", ("udf func returned, res_length: %lu", res_length));
 | 
						|
  if (is_null_tmp || !res || unlikely(error))	// The !res is for safety
 | 
						|
  {
 | 
						|
    DBUG_PRINT("info", ("Null or error"));
 | 
						|
    DBUG_RETURN(0);
 | 
						|
  }
 | 
						|
  if (res == str->ptr())
 | 
						|
  {
 | 
						|
    str->length(res_length);
 | 
						|
    DBUG_PRINT("exit", ("str: %*.s", (int) str->length(), str->ptr()));
 | 
						|
    DBUG_RETURN(str);
 | 
						|
  }
 | 
						|
  save_str->set(res, res_length, str->charset());
 | 
						|
  DBUG_PRINT("exit", ("save_str: %s", save_str->ptr()));
 | 
						|
  DBUG_RETURN(save_str);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
  For the moment, UDF functions are returning DECIMAL values as strings
 | 
						|
*/
 | 
						|
 | 
						|
my_decimal *udf_handler::val_decimal(my_bool *null_value, my_decimal *dec_buf)
 | 
						|
{
 | 
						|
  char buf[DECIMAL_MAX_STR_LENGTH+1], *end;
 | 
						|
  ulong res_length= DECIMAL_MAX_STR_LENGTH;
 | 
						|
 | 
						|
  if (get_arguments())
 | 
						|
  {
 | 
						|
    *null_value=1;
 | 
						|
    return 0;
 | 
						|
  }
 | 
						|
  char *(*func)(UDF_INIT *, UDF_ARGS *, char *, ulong *, uchar *, uchar *)=
 | 
						|
    (char* (*)(UDF_INIT *, UDF_ARGS *, char *, ulong *, uchar *, uchar *))
 | 
						|
    u_d->func;
 | 
						|
 | 
						|
  char *res= func(&initid, &f_args, buf, &res_length, &is_null, &error);
 | 
						|
  if (is_null || unlikely(error))
 | 
						|
  {
 | 
						|
    *null_value= 1;
 | 
						|
    return 0;
 | 
						|
  }
 | 
						|
  end= res+ res_length;
 | 
						|
  str2my_decimal(E_DEC_FATAL_ERROR, res, dec_buf, &end);
 | 
						|
  return dec_buf;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void Item_udf_func::cleanup()
 | 
						|
{
 | 
						|
  udf.cleanup();
 | 
						|
  Item_func::cleanup();
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void Item_udf_func::print(String *str, enum_query_type query_type)
 | 
						|
{
 | 
						|
  str->append(func_name_cstring());
 | 
						|
  str->append('(');
 | 
						|
  for (uint i=0 ; i < arg_count ; i++)
 | 
						|
  {
 | 
						|
    if (i != 0)
 | 
						|
      str->append(',');
 | 
						|
    args[i]->print_item_w_name(str, query_type);
 | 
						|
  }
 | 
						|
  str->append(')');
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
double Item_func_udf_float::val_real()
 | 
						|
{
 | 
						|
  double res;
 | 
						|
  my_bool tmp_null_value;
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
  DBUG_ENTER("Item_func_udf_float::val");
 | 
						|
  DBUG_PRINT("info",("result_type: %d  arg_count: %d",
 | 
						|
		     args[0]->result_type(), arg_count));
 | 
						|
  res= udf.val(&tmp_null_value);
 | 
						|
  null_value= tmp_null_value;
 | 
						|
  DBUG_RETURN(res);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
String *Item_func_udf_float::val_str(String *str)
 | 
						|
{
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
  double nr= val_real();
 | 
						|
  if (null_value)
 | 
						|
    return 0;					/* purecov: inspected */
 | 
						|
  str->set_real(nr,decimals,&my_charset_bin);
 | 
						|
  return str;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
longlong Item_func_udf_int::val_int()
 | 
						|
{
 | 
						|
  longlong res;
 | 
						|
  my_bool tmp_null_value;
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
  DBUG_ENTER("Item_func_udf_int::val_int");
 | 
						|
  res= udf.val_int(&tmp_null_value);
 | 
						|
  null_value= tmp_null_value;
 | 
						|
  DBUG_RETURN(res);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
String *Item_func_udf_int::val_str(String *str)
 | 
						|
{
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
  longlong nr=val_int();
 | 
						|
  if (null_value)
 | 
						|
    return 0;
 | 
						|
  str->set_int(nr, unsigned_flag, &my_charset_bin);
 | 
						|
  return str;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
my_decimal *Item_func_udf_decimal::val_decimal(my_decimal *dec_buf)
 | 
						|
{
 | 
						|
  my_decimal *res;
 | 
						|
  my_bool tmp_null_value;
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
  DBUG_ENTER("Item_func_udf_decimal::val_decimal");
 | 
						|
  DBUG_PRINT("info",("result_type: %d  arg_count: %d",
 | 
						|
                     args[0]->result_type(), arg_count));
 | 
						|
 | 
						|
  res= udf.val_decimal(&tmp_null_value, dec_buf);
 | 
						|
  null_value= tmp_null_value;
 | 
						|
  DBUG_RETURN(res);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* Default max_length is max argument length */
 | 
						|
 | 
						|
bool Item_func_udf_str::fix_length_and_dec(THD *thd)
 | 
						|
{
 | 
						|
  DBUG_ENTER("Item_func_udf_str::fix_length_and_dec");
 | 
						|
  max_length=0;
 | 
						|
  for (uint i = 0; i < arg_count; i++)
 | 
						|
    set_if_bigger(max_length,args[i]->max_length);
 | 
						|
  DBUG_RETURN(FALSE);
 | 
						|
}
 | 
						|
 | 
						|
String *Item_func_udf_str::val_str(String *str)
 | 
						|
{
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
  String *res=udf.val_str(str,&str_value);
 | 
						|
  null_value = !res;
 | 
						|
  return res;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  @note
 | 
						|
  This has to come last in the udf_handler methods, or C for AIX
 | 
						|
  version 6.0.0.0 fails to compile with debugging enabled. (Yes, really.)
 | 
						|
*/
 | 
						|
 | 
						|
udf_handler::~udf_handler()
 | 
						|
{
 | 
						|
  /* Everything should be properly cleaned up by this moment. */
 | 
						|
  DBUG_ASSERT(not_original || !(initialized || buffers));
 | 
						|
}
 | 
						|
 | 
						|
#else
 | 
						|
bool udf_handler::get_arguments() { return 0; }
 | 
						|
#endif /* HAVE_DLOPEN */
 | 
						|
 | 
						|
 | 
						|
longlong Item_master_pos_wait::val_int()
 | 
						|
{
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
  THD* thd = current_thd;
 | 
						|
  String *log_name = args[0]->val_str(&value);
 | 
						|
  int event_count= 0;
 | 
						|
  DBUG_ENTER("Item_master_pos_wait::val_int");
 | 
						|
 | 
						|
  null_value=0;
 | 
						|
  if (thd->slave_thread || !log_name || !log_name->length())
 | 
						|
  {
 | 
						|
    null_value = 1;
 | 
						|
    DBUG_RETURN(0);
 | 
						|
  }
 | 
						|
#ifdef HAVE_REPLICATION
 | 
						|
  longlong pos = (ulong)args[1]->val_int();
 | 
						|
  longlong timeout = (arg_count>=3) ? args[2]->val_int() : 0 ;
 | 
						|
  String connection_name_buff;
 | 
						|
  LEX_CSTRING connection_name;
 | 
						|
  Master_info *mi= NULL;
 | 
						|
  if (arg_count >= 4)
 | 
						|
  {
 | 
						|
    String *con;
 | 
						|
    if (!(con= args[3]->val_str(&connection_name_buff)))
 | 
						|
      goto err;
 | 
						|
 | 
						|
    connection_name.str= con->ptr();
 | 
						|
    connection_name.length= con->length();
 | 
						|
    if (check_master_connection_name(&connection_name))
 | 
						|
    {
 | 
						|
      my_error(ER_WRONG_ARGUMENTS, MYF(ME_WARNING),
 | 
						|
               "MASTER_CONNECTION_NAME");
 | 
						|
      goto err;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  else
 | 
						|
    connection_name= thd->variables.default_master_connection;
 | 
						|
 | 
						|
  if (!(mi= get_master_info(&connection_name, Sql_condition::WARN_LEVEL_WARN)))
 | 
						|
    goto err;
 | 
						|
 | 
						|
  if ((event_count = mi->rli.wait_for_pos(thd, log_name, pos, timeout)) == -2)
 | 
						|
  {
 | 
						|
    null_value = 1;
 | 
						|
    event_count=0;
 | 
						|
  }
 | 
						|
  mi->release();
 | 
						|
#endif
 | 
						|
  DBUG_PRINT("exit", ("event_count: %d  null_value: %d", event_count,
 | 
						|
                      (int) null_value));
 | 
						|
  DBUG_RETURN(event_count);
 | 
						|
 | 
						|
#ifdef HAVE_REPLICATION
 | 
						|
err:
 | 
						|
  {
 | 
						|
    null_value = 1;
 | 
						|
    DBUG_RETURN(0);
 | 
						|
  }
 | 
						|
#endif
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
longlong Item_master_gtid_wait::val_int()
 | 
						|
{
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
  longlong result= 0;
 | 
						|
  String *gtid_pos __attribute__((unused)) = args[0]->val_str(&value);
 | 
						|
  DBUG_ENTER("Item_master_gtid_wait::val_int");
 | 
						|
 | 
						|
  if (args[0]->null_value)
 | 
						|
  {
 | 
						|
    null_value= 1;
 | 
						|
    DBUG_RETURN(0);
 | 
						|
  }
 | 
						|
 | 
						|
  null_value=0;
 | 
						|
#ifdef HAVE_REPLICATION
 | 
						|
  THD* thd= current_thd;
 | 
						|
  longlong timeout_us;
 | 
						|
 | 
						|
  if (arg_count==2 && !args[1]->null_value)
 | 
						|
    timeout_us= (longlong)(1e6*args[1]->val_real());
 | 
						|
  else
 | 
						|
    timeout_us= (longlong)-1;
 | 
						|
 | 
						|
  result= rpl_global_gtid_waiting.wait_for_pos(thd, gtid_pos, timeout_us);
 | 
						|
#else
 | 
						|
  null_value= 0;
 | 
						|
#endif /* REPLICATION */
 | 
						|
  DBUG_RETURN(result);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Enables a session to wait on a condition until a timeout or a network
 | 
						|
  disconnect occurs.
 | 
						|
 | 
						|
  @remark The connection is polled every m_interrupt_interval nanoseconds.
 | 
						|
*/
 | 
						|
 | 
						|
class Interruptible_wait
 | 
						|
{
 | 
						|
  THD *m_thd;
 | 
						|
  struct timespec m_abs_timeout;
 | 
						|
  static const ulonglong m_interrupt_interval;
 | 
						|
 | 
						|
  public:
 | 
						|
    Interruptible_wait(THD *thd)
 | 
						|
    : m_thd(thd) {}
 | 
						|
 | 
						|
    ~Interruptible_wait() = default;
 | 
						|
 | 
						|
  public:
 | 
						|
    /**
 | 
						|
      Set the absolute timeout.
 | 
						|
 | 
						|
      @param timeout The amount of time in nanoseconds to wait
 | 
						|
    */
 | 
						|
    void set_timeout(ulonglong timeout)
 | 
						|
    {
 | 
						|
      /*
 | 
						|
        Calculate the absolute system time at the start so it can
 | 
						|
        be controlled in slices. It relies on the fact that once
 | 
						|
        the absolute time passes, the timed wait call will fail
 | 
						|
        automatically with a timeout error.
 | 
						|
      */
 | 
						|
      set_timespec_nsec(m_abs_timeout, timeout);
 | 
						|
    }
 | 
						|
 | 
						|
    /** The timed wait. */
 | 
						|
    int wait(mysql_cond_t *, mysql_mutex_t *);
 | 
						|
};
 | 
						|
 | 
						|
 | 
						|
/** Time to wait before polling the connection status. */
 | 
						|
const ulonglong Interruptible_wait::m_interrupt_interval= 5 * 1000000000ULL;
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Wait for a given condition to be signaled.
 | 
						|
 | 
						|
  @param cond   The condition variable to wait on.
 | 
						|
  @param mutex  The associated mutex.
 | 
						|
 | 
						|
  @remark The absolute timeout is preserved across calls.
 | 
						|
 | 
						|
  @retval return value from mysql_cond_timedwait
 | 
						|
*/
 | 
						|
 | 
						|
int Interruptible_wait::wait(mysql_cond_t *cond, mysql_mutex_t *mutex)
 | 
						|
{
 | 
						|
  int error;
 | 
						|
  struct timespec timeout;
 | 
						|
 | 
						|
  while (1)
 | 
						|
  {
 | 
						|
    /* Wait for a fixed interval. */
 | 
						|
    set_timespec_nsec(timeout, m_interrupt_interval);
 | 
						|
 | 
						|
    /* But only if not past the absolute timeout. */
 | 
						|
    if (cmp_timespec(timeout, m_abs_timeout) > 0)
 | 
						|
      timeout= m_abs_timeout;
 | 
						|
 | 
						|
    error= mysql_cond_timedwait(cond, mutex, &timeout);
 | 
						|
    if (m_thd->check_killed())
 | 
						|
      break;
 | 
						|
    if (error == ETIMEDOUT || error == ETIME)
 | 
						|
    {
 | 
						|
      /* Return error if timed out or connection is broken. */
 | 
						|
      if (!cmp_timespec(timeout, m_abs_timeout) || !m_thd->is_connected())
 | 
						|
        break;
 | 
						|
    }
 | 
						|
    /* Otherwise, propagate status to the caller. */
 | 
						|
    else
 | 
						|
      break;
 | 
						|
  }
 | 
						|
 | 
						|
  return error;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  For locks with EXPLICIT duration, MDL returns a new ticket
 | 
						|
  every time a lock is granted. This allows to implement recursive
 | 
						|
  locks without extra allocation or additional data structures, such
 | 
						|
  as below. However, if there are too many tickets in the same
 | 
						|
  MDL_context, MDL_context::find_ticket() is getting too slow,
 | 
						|
  since it's using a linear search.
 | 
						|
  This is why a separate structure is allocated for a user
 | 
						|
  level lock, and before requesting a new lock from MDL,
 | 
						|
  GET_LOCK() checks thd->ull_hash if such lock is already granted,
 | 
						|
  and if so, simply increments a reference counter.
 | 
						|
*/
 | 
						|
 | 
						|
class User_level_lock
 | 
						|
{
 | 
						|
public:
 | 
						|
  MDL_ticket *lock;
 | 
						|
  int refs;
 | 
						|
};
 | 
						|
 | 
						|
 | 
						|
/** Extract a hash key from User_level_lock. */
 | 
						|
 | 
						|
const uchar *ull_get_key(const void *ptr, size_t *length, my_bool)
 | 
						|
{
 | 
						|
  User_level_lock *ull = (User_level_lock*) ptr;
 | 
						|
  MDL_key *key = ull->lock->get_key();
 | 
						|
  *length= key->length();
 | 
						|
  return key->ptr();
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Release all user level locks for this THD.
 | 
						|
*/
 | 
						|
 | 
						|
void mysql_ull_cleanup(THD *thd)
 | 
						|
{
 | 
						|
  User_level_lock *ull;
 | 
						|
  DBUG_ENTER("mysql_ull_cleanup");
 | 
						|
 | 
						|
  for (uint i= 0; i < thd->ull_hash.records; i++)
 | 
						|
  {
 | 
						|
    ull = (User_level_lock*) my_hash_element(&thd->ull_hash, i);
 | 
						|
    thd->mdl_context.release_lock(ull->lock);
 | 
						|
    my_free(ull);
 | 
						|
  }
 | 
						|
 | 
						|
  my_hash_free(&thd->ull_hash);
 | 
						|
 | 
						|
  DBUG_VOID_RETURN;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Set explicit duration for metadata locks corresponding to
 | 
						|
  user level locks to protect them from being released at the end
 | 
						|
  of transaction.
 | 
						|
*/
 | 
						|
 | 
						|
void mysql_ull_set_explicit_lock_duration(THD *thd)
 | 
						|
{
 | 
						|
  User_level_lock *ull;
 | 
						|
  DBUG_ENTER("mysql_ull_set_explicit_lock_duration");
 | 
						|
 | 
						|
  for (uint i= 0; i < thd->ull_hash.records; i++)
 | 
						|
  {
 | 
						|
    ull= (User_level_lock*) my_hash_element(&thd->ull_hash, i);
 | 
						|
    thd->mdl_context.set_lock_duration(ull->lock, MDL_EXPLICIT);
 | 
						|
  }
 | 
						|
  DBUG_VOID_RETURN;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  When MDL detects a lock wait timeout, it pushes
 | 
						|
  an error into the statement diagnostics area.
 | 
						|
  For GET_LOCK(), lock wait timeout is not an error,
 | 
						|
  but a special return value (0).
 | 
						|
  Similarly, killing get_lock wait is not an error either,
 | 
						|
  but a return value NULL.
 | 
						|
  Capture and suppress lock wait timeouts and kills.
 | 
						|
*/
 | 
						|
 | 
						|
class Lock_wait_timeout_handler: public Internal_error_handler
 | 
						|
{
 | 
						|
public:
 | 
						|
  Lock_wait_timeout_handler() :m_lock_wait_timeout(false) {}
 | 
						|
 | 
						|
  bool m_lock_wait_timeout;
 | 
						|
 | 
						|
  bool handle_condition(THD * /* thd */, uint sql_errno,
 | 
						|
                        const char * /* sqlstate */,
 | 
						|
                        Sql_condition::enum_warning_level* /* level */,
 | 
						|
                        const char *message,
 | 
						|
                        Sql_condition ** /* cond_hdl */) override;
 | 
						|
};
 | 
						|
 | 
						|
bool
 | 
						|
Lock_wait_timeout_handler::
 | 
						|
handle_condition(THD *thd, uint sql_errno,
 | 
						|
                 const char * /* sqlstate */,
 | 
						|
                 Sql_condition::enum_warning_level* /* level */,
 | 
						|
                 const char *message,
 | 
						|
                 Sql_condition ** /* cond_hdl */)
 | 
						|
{
 | 
						|
  if (sql_errno == ER_LOCK_WAIT_TIMEOUT)
 | 
						|
  {
 | 
						|
    m_lock_wait_timeout= true;
 | 
						|
    return true;                                /* condition handled */
 | 
						|
  }
 | 
						|
  if (thd->is_killed())
 | 
						|
    return true;
 | 
						|
 | 
						|
  return false;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
static int ull_name_ok(String *name)
 | 
						|
{
 | 
						|
  if (!name || !name->length())
 | 
						|
    return 0;
 | 
						|
 | 
						|
  if (name->length() > NAME_LEN)
 | 
						|
  {
 | 
						|
    my_error(ER_TOO_LONG_IDENT, MYF(0), name->c_ptr_safe());
 | 
						|
    return 0;
 | 
						|
  }
 | 
						|
  return 1;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Get a user level lock.
 | 
						|
 | 
						|
  @retval
 | 
						|
    1    : Got lock
 | 
						|
  @retval
 | 
						|
    0    : Timeout
 | 
						|
  @retval
 | 
						|
    NULL : Error
 | 
						|
*/
 | 
						|
 | 
						|
longlong Item_func_get_lock::val_int()
 | 
						|
{
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
  String *res= args[0]->val_str(&value);
 | 
						|
  double timeout= args[1]->val_real();
 | 
						|
  THD *thd= current_thd;
 | 
						|
  User_level_lock *ull;
 | 
						|
  DBUG_ENTER("Item_func_get_lock::val_int");
 | 
						|
 | 
						|
  null_value= 1;
 | 
						|
  /*
 | 
						|
    In slave thread no need to get locks, everything is serialized. Anyway
 | 
						|
    there is no way to make GET_LOCK() work on slave like it did on master
 | 
						|
    (i.e. make it return exactly the same value) because we don't have the
 | 
						|
    same other concurrent threads environment. No matter what we return here,
 | 
						|
    it's not guaranteed to be same as on master.
 | 
						|
  */
 | 
						|
  if (thd->slave_thread)
 | 
						|
  {
 | 
						|
    null_value= 0;
 | 
						|
    DBUG_RETURN(1);
 | 
						|
  }
 | 
						|
 | 
						|
  if (args[1]->null_value ||
 | 
						|
      (!args[1]->unsigned_flag && ((longlong) timeout < 0)))
 | 
						|
  {
 | 
						|
    char buf[22];
 | 
						|
    if (args[1]->null_value)
 | 
						|
      strmov(buf, "NULL");
 | 
						|
    else
 | 
						|
      llstr(((longlong) timeout), buf);
 | 
						|
    push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
 | 
						|
                        ER_WRONG_VALUE_FOR_TYPE, ER(ER_WRONG_VALUE_FOR_TYPE),
 | 
						|
                        "timeout", buf, "get_lock");
 | 
						|
    null_value= 1;
 | 
						|
    DBUG_RETURN(0);
 | 
						|
  }
 | 
						|
 | 
						|
  if (!ull_name_ok(res))
 | 
						|
    DBUG_RETURN(0);
 | 
						|
  DBUG_PRINT("enter", ("lock: %.*s", res->length(), res->ptr()));
 | 
						|
  /* HASH entries are of type User_level_lock. */
 | 
						|
  if (! my_hash_inited(&thd->ull_hash) &&
 | 
						|
        my_hash_init(key_memory_User_level_lock, &thd->ull_hash,
 | 
						|
                     &my_charset_bin, 16 /* small hash */, 0, 0, ull_get_key,
 | 
						|
                     NULL, 0))
 | 
						|
  {
 | 
						|
    DBUG_RETURN(0);
 | 
						|
  }
 | 
						|
 | 
						|
  MDL_request ull_request;
 | 
						|
  MDL_REQUEST_INIT(&ull_request, MDL_key::USER_LOCK, res->c_ptr_safe(), "",
 | 
						|
                   MDL_SHARED_NO_WRITE, MDL_EXPLICIT);
 | 
						|
  MDL_key *ull_key= &ull_request.key;
 | 
						|
 | 
						|
 | 
						|
  if ((ull= (User_level_lock*)
 | 
						|
       my_hash_search(&thd->ull_hash, ull_key->ptr(), ull_key->length())))
 | 
						|
  {
 | 
						|
    /* Recursive lock */
 | 
						|
    ull->refs++;
 | 
						|
    null_value= 0;
 | 
						|
    DBUG_PRINT("info", ("recursive lock, ref-count: %d", (int) ull->refs));
 | 
						|
    DBUG_RETURN(1);
 | 
						|
  }
 | 
						|
 | 
						|
  Lock_wait_timeout_handler lock_wait_timeout_handler;
 | 
						|
  thd->push_internal_handler(&lock_wait_timeout_handler);
 | 
						|
  bool error= thd->mdl_context.acquire_lock(&ull_request, timeout);
 | 
						|
  (void) thd->pop_internal_handler();
 | 
						|
  if (unlikely(error))
 | 
						|
  {
 | 
						|
    if (lock_wait_timeout_handler.m_lock_wait_timeout)
 | 
						|
      null_value= 0;
 | 
						|
    DBUG_RETURN(0);
 | 
						|
  }
 | 
						|
 | 
						|
  ull= (User_level_lock*) my_malloc(key_memory_User_level_lock,
 | 
						|
                                    sizeof(User_level_lock),
 | 
						|
                                    MYF(MY_WME|MY_THREAD_SPECIFIC));
 | 
						|
  if (ull == NULL)
 | 
						|
  {
 | 
						|
    thd->mdl_context.release_lock(ull_request.ticket);
 | 
						|
    DBUG_RETURN(0);
 | 
						|
  }
 | 
						|
 | 
						|
  ull->lock= ull_request.ticket;
 | 
						|
  ull->refs= 1;
 | 
						|
 | 
						|
  if (my_hash_insert(&thd->ull_hash, (uchar*) ull))
 | 
						|
  {
 | 
						|
    thd->mdl_context.release_lock(ull->lock);
 | 
						|
    my_free(ull);
 | 
						|
    DBUG_RETURN(0);
 | 
						|
  }
 | 
						|
  null_value= 0;
 | 
						|
 | 
						|
  DBUG_RETURN(1);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Release all user level locks.
 | 
						|
  @return
 | 
						|
    - N if N-lock released
 | 
						|
    - 0 if lock wasn't held
 | 
						|
*/
 | 
						|
longlong Item_func_release_all_locks::val_int()
 | 
						|
{
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
  THD *thd= current_thd;
 | 
						|
  ulong num_unlocked= 0;
 | 
						|
  DBUG_ENTER("Item_func_release_all_locks::val_int");
 | 
						|
  for (size_t i= 0; i < thd->ull_hash.records; i++)
 | 
						|
  {
 | 
						|
    auto ull= (User_level_lock *) my_hash_element(&thd->ull_hash, i);
 | 
						|
    thd->mdl_context.release_lock(ull->lock);
 | 
						|
    num_unlocked+= ull->refs;
 | 
						|
    my_free(ull);
 | 
						|
  }
 | 
						|
  my_hash_free(&thd->ull_hash);
 | 
						|
  DBUG_RETURN(num_unlocked);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Release a user level lock.
 | 
						|
  @return
 | 
						|
    - 1 if lock released
 | 
						|
    - 0 if lock wasn't held
 | 
						|
    - (SQL) NULL if no such lock
 | 
						|
*/
 | 
						|
 | 
						|
longlong Item_func_release_lock::val_int()
 | 
						|
{
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
  String *res= args[0]->val_str(&value);
 | 
						|
  THD *thd= current_thd;
 | 
						|
  DBUG_ENTER("Item_func_release_lock::val_int");
 | 
						|
  null_value= 1;
 | 
						|
 | 
						|
  if (!ull_name_ok(res))
 | 
						|
    DBUG_RETURN(0);
 | 
						|
 | 
						|
  DBUG_PRINT("enter", ("lock: %.*s", res->length(), res->ptr()));
 | 
						|
 | 
						|
  MDL_key ull_key;
 | 
						|
  ull_key.mdl_key_init(MDL_key::USER_LOCK, res->c_ptr_safe(), "");
 | 
						|
 | 
						|
  User_level_lock *ull;
 | 
						|
 | 
						|
  if (!my_hash_inited(&thd->ull_hash) ||
 | 
						|
      !(ull=
 | 
						|
        (User_level_lock*) my_hash_search(&thd->ull_hash,
 | 
						|
                                          ull_key.ptr(), ull_key.length())))
 | 
						|
  {
 | 
						|
    null_value= thd->mdl_context.get_lock_owner(&ull_key) == 0;
 | 
						|
    DBUG_RETURN(0);
 | 
						|
  }
 | 
						|
  DBUG_PRINT("info", ("ref count: %d", (int) ull->refs));
 | 
						|
  null_value= 0;
 | 
						|
  if (--ull->refs == 0)
 | 
						|
  {
 | 
						|
    my_hash_delete(&thd->ull_hash, (uchar*) ull);
 | 
						|
    thd->mdl_context.release_lock(ull->lock);
 | 
						|
    my_free(ull);
 | 
						|
  }
 | 
						|
  DBUG_RETURN(1);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Check a user level lock.
 | 
						|
 | 
						|
  Sets null_value=TRUE on error.
 | 
						|
 | 
						|
  @retval
 | 
						|
    1		Available
 | 
						|
  @retval
 | 
						|
    0		Already taken, or error
 | 
						|
*/
 | 
						|
 | 
						|
longlong Item_func_is_free_lock::val_int()
 | 
						|
{
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
  String *res= args[0]->val_str(&value);
 | 
						|
  THD *thd= current_thd;
 | 
						|
  null_value= 1;
 | 
						|
 | 
						|
  if (!ull_name_ok(res))
 | 
						|
    return 0;
 | 
						|
 | 
						|
  MDL_key ull_key;
 | 
						|
  ull_key.mdl_key_init(MDL_key::USER_LOCK, res->c_ptr_safe(), "");
 | 
						|
 | 
						|
  null_value= 0;
 | 
						|
  return thd->mdl_context.get_lock_owner(&ull_key) == 0;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
longlong Item_func_is_used_lock::val_int()
 | 
						|
{
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
  String *res= args[0]->val_str(&value);
 | 
						|
  THD *thd= current_thd;
 | 
						|
  null_value= 1;
 | 
						|
 | 
						|
  if (!ull_name_ok(res))
 | 
						|
    return 0;
 | 
						|
 | 
						|
  MDL_key ull_key;
 | 
						|
  ull_key.mdl_key_init(MDL_key::USER_LOCK, res->c_ptr_safe(), "");
 | 
						|
  ulong thread_id = thd->mdl_context.get_lock_owner(&ull_key);
 | 
						|
  if (thread_id == 0)
 | 
						|
    return 0;
 | 
						|
 | 
						|
  null_value= 0;
 | 
						|
  return thread_id;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
longlong Item_func_last_insert_id::val_int()
 | 
						|
{
 | 
						|
  THD *thd= current_thd;
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
  if (arg_count)
 | 
						|
  {
 | 
						|
    longlong value= args[0]->val_int();
 | 
						|
    null_value= args[0]->null_value;
 | 
						|
    /*
 | 
						|
      LAST_INSERT_ID(X) must affect the client's mysql_insert_id() as
 | 
						|
      documented in the manual. We don't want to touch
 | 
						|
      first_successful_insert_id_in_cur_stmt because it would make
 | 
						|
      LAST_INSERT_ID(X) take precedence over an generated auto_increment
 | 
						|
      value for this row.
 | 
						|
    */
 | 
						|
    thd->arg_of_last_insert_id_function= TRUE;
 | 
						|
    thd->first_successful_insert_id_in_prev_stmt= value;
 | 
						|
    return value;
 | 
						|
  }
 | 
						|
  return
 | 
						|
    static_cast<longlong>(thd->read_first_successful_insert_id_in_prev_stmt());
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool Item_func_last_insert_id::fix_fields(THD *thd, Item **ref)
 | 
						|
{
 | 
						|
  thd->lex->uncacheable(UNCACHEABLE_SIDEEFFECT);
 | 
						|
  return Item_int_func::fix_fields(thd, ref);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* This function is just used to test speed of different functions */
 | 
						|
 | 
						|
longlong Item_func_benchmark::val_int()
 | 
						|
{
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
  char buff[MAX_FIELD_WIDTH];
 | 
						|
  String tmp(buff,sizeof(buff), &my_charset_bin);
 | 
						|
  my_decimal tmp_decimal;
 | 
						|
  THD *thd= current_thd;
 | 
						|
  ulonglong loop_count;
 | 
						|
 | 
						|
  loop_count= (ulonglong) args[0]->val_int();
 | 
						|
 | 
						|
  if (args[0]->null_value ||
 | 
						|
      (!args[0]->unsigned_flag && (((longlong) loop_count) < 0)))
 | 
						|
  {
 | 
						|
    if (!args[0]->null_value)
 | 
						|
    {
 | 
						|
      char buff[22];
 | 
						|
      llstr(((longlong) loop_count), buff);
 | 
						|
      push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
 | 
						|
                          ER_WRONG_VALUE_FOR_TYPE,
 | 
						|
                          ER_THD(thd, ER_WRONG_VALUE_FOR_TYPE),
 | 
						|
                          "count", buff, "benchmark");
 | 
						|
    }
 | 
						|
 | 
						|
    null_value= 1;
 | 
						|
    return 0;
 | 
						|
  }
 | 
						|
 | 
						|
  null_value=0;
 | 
						|
  for (ulonglong loop=0 ; loop < loop_count && !thd->killed; loop++)
 | 
						|
  {
 | 
						|
    switch (args[1]->result_type()) {
 | 
						|
    case REAL_RESULT:
 | 
						|
      (void) args[1]->val_real();
 | 
						|
      break;
 | 
						|
    case INT_RESULT:
 | 
						|
      (void) args[1]->val_int();
 | 
						|
      break;
 | 
						|
    case STRING_RESULT:
 | 
						|
      (void) args[1]->val_str(&tmp);
 | 
						|
      break;
 | 
						|
    case DECIMAL_RESULT:
 | 
						|
      (void) args[1]->val_decimal(&tmp_decimal);
 | 
						|
      break;
 | 
						|
    case ROW_RESULT:
 | 
						|
    case TIME_RESULT:
 | 
						|
      DBUG_ASSERT(0);              // This case should never be chosen
 | 
						|
      return 0;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void Item_func_benchmark::print(String *str, enum_query_type query_type)
 | 
						|
{
 | 
						|
  str->append(STRING_WITH_LEN("benchmark("));
 | 
						|
  args[0]->print(str, query_type);
 | 
						|
  str->append(',');
 | 
						|
  args[1]->print(str, query_type);
 | 
						|
  str->append(')');
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
mysql_mutex_t LOCK_item_func_sleep;
 | 
						|
 | 
						|
#ifdef HAVE_PSI_INTERFACE
 | 
						|
static PSI_mutex_key key_LOCK_item_func_sleep;
 | 
						|
 | 
						|
static PSI_mutex_info item_func_sleep_mutexes[]=
 | 
						|
{
 | 
						|
  { &key_LOCK_item_func_sleep, "LOCK_item_func_sleep", PSI_FLAG_GLOBAL}
 | 
						|
};
 | 
						|
 | 
						|
 | 
						|
static void init_item_func_sleep_psi_keys(void)
 | 
						|
{
 | 
						|
  const char* category= "sql";
 | 
						|
  int count;
 | 
						|
 | 
						|
  if (PSI_server == NULL)
 | 
						|
    return;
 | 
						|
 | 
						|
  count= array_elements(item_func_sleep_mutexes);
 | 
						|
  PSI_server->register_mutex(category, item_func_sleep_mutexes, count);
 | 
						|
}
 | 
						|
#endif
 | 
						|
 | 
						|
static bool item_func_sleep_inited= 0;
 | 
						|
 | 
						|
 | 
						|
void item_func_sleep_init(void)
 | 
						|
{
 | 
						|
#ifdef HAVE_PSI_INTERFACE
 | 
						|
  init_item_func_sleep_psi_keys();
 | 
						|
#endif
 | 
						|
 | 
						|
  mysql_mutex_init(key_LOCK_item_func_sleep, &LOCK_item_func_sleep, MY_MUTEX_INIT_SLOW);
 | 
						|
  item_func_sleep_inited= 1;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void item_func_sleep_free(void)
 | 
						|
{
 | 
						|
  if (item_func_sleep_inited)
 | 
						|
  {
 | 
						|
    item_func_sleep_inited= 0;
 | 
						|
    mysql_mutex_destroy(&LOCK_item_func_sleep);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/** This function is just used to create tests with time gaps. */
 | 
						|
 | 
						|
longlong Item_func_sleep::val_int()
 | 
						|
{
 | 
						|
  THD *thd= current_thd;
 | 
						|
  Interruptible_wait timed_cond(thd);
 | 
						|
  mysql_cond_t cond;
 | 
						|
  double timeout;
 | 
						|
  int error;
 | 
						|
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
 | 
						|
  timeout= args[0]->val_real();
 | 
						|
  /*
 | 
						|
    On 64-bit OSX mysql_cond_timedwait() waits forever
 | 
						|
    if passed abstime time has already been exceeded by 
 | 
						|
    the system time.
 | 
						|
    When given a very short timeout (< 10 mcs) just return 
 | 
						|
    immediately.
 | 
						|
    We assume that the lines between this test and the call 
 | 
						|
    to mysql_cond_timedwait() will be executed in less than 0.00001 sec.
 | 
						|
  */
 | 
						|
  if (timeout < 0.00001)
 | 
						|
    return 0;
 | 
						|
 | 
						|
  error= do_pause(thd, &timed_cond, &cond, timeout);
 | 
						|
 | 
						|
#ifdef ENABLED_DEBUG_SYNC
 | 
						|
  DBUG_EXECUTE_IF("sleep_inject_query_done_debug_sync", {
 | 
						|
      debug_sync_set_action
 | 
						|
        (thd, STRING_WITH_LEN("dispatch_command_end SIGNAL query_done"));
 | 
						|
    };);
 | 
						|
#endif
 | 
						|
 | 
						|
  return MY_TEST(!error);                  // Return 1 killed
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool Item_func_user_var::check_vcol_func_processor(void *arg)
 | 
						|
{
 | 
						|
  return mark_unsupported_function("@", name.str, arg, VCOL_NON_DETERMINISTIC);
 | 
						|
}
 | 
						|
 | 
						|
#define extra_size sizeof(double)
 | 
						|
 | 
						|
user_var_entry *get_variable(HASH *hash, LEX_CSTRING *name,
 | 
						|
                             bool create_if_not_exists)
 | 
						|
{
 | 
						|
  user_var_entry *entry;
 | 
						|
 | 
						|
  if (!(entry = (user_var_entry*) my_hash_search(hash, (uchar*) name->str,
 | 
						|
                                                 name->length)) &&
 | 
						|
      create_if_not_exists)
 | 
						|
  {
 | 
						|
    size_t size=ALIGN_SIZE(sizeof(user_var_entry))+name->length+1+extra_size;
 | 
						|
    if (!my_hash_inited(hash))
 | 
						|
      return 0;
 | 
						|
    if (!(entry = (user_var_entry*) my_malloc(key_memory_user_var_entry, size,
 | 
						|
                                              MYF(MY_WME | ME_FATAL |
 | 
						|
                                                  MY_THREAD_SPECIFIC))))
 | 
						|
      return 0;
 | 
						|
    entry->name.str=(char*) entry+ ALIGN_SIZE(sizeof(user_var_entry))+
 | 
						|
      extra_size;
 | 
						|
    entry->name.length=name->length;
 | 
						|
    entry->value=0;
 | 
						|
    entry->length=0;
 | 
						|
    entry->update_query_id=0;
 | 
						|
    entry->set_charset(NULL);
 | 
						|
    /*
 | 
						|
      If we are here, we were called from a SET or a query which sets a
 | 
						|
      variable. Imagine it is this:
 | 
						|
      INSERT INTO t SELECT @a:=10, @a:=@a+1.
 | 
						|
      Then when we have a Item_func_get_user_var (because of the @a+1) so we
 | 
						|
      think we have to write the value of @a to the binlog. But before that,
 | 
						|
      we have a Item_func_set_user_var to create @a (@a:=10), in this we mark
 | 
						|
      the variable as "already logged" (line below) so that it won't be logged
 | 
						|
      by Item_func_get_user_var (because that's not necessary).
 | 
						|
    */
 | 
						|
    entry->used_query_id=current_thd->query_id;
 | 
						|
    entry->set_handler(&type_handler_long_blob);
 | 
						|
    memcpy((char*) entry->name.str, name->str, name->length+1);
 | 
						|
    if (my_hash_insert(hash,(uchar*) entry))
 | 
						|
    {
 | 
						|
      my_free(entry);
 | 
						|
      return 0;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  return entry;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void Item_func_set_user_var::cleanup()
 | 
						|
{
 | 
						|
  Item_func::cleanup();
 | 
						|
  m_var_entry= NULL;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool Item_func_set_user_var::set_entry(THD *thd, bool create_if_not_exists)
 | 
						|
{
 | 
						|
  if (m_var_entry && thd->thread_id == entry_thread_id)
 | 
						|
    goto end; // update entry->update_query_id for PS
 | 
						|
  if (!(m_var_entry= get_variable(&thd->user_vars, &name, create_if_not_exists)))
 | 
						|
  {
 | 
						|
    entry_thread_id= 0;
 | 
						|
    return TRUE;
 | 
						|
  }
 | 
						|
  entry_thread_id= thd->thread_id;
 | 
						|
  /* 
 | 
						|
     Remember the last query which updated it, this way a query can later know
 | 
						|
     if this variable is a constant item in the query (it is if update_query_id
 | 
						|
     is different from query_id).
 | 
						|
  */
 | 
						|
end:
 | 
						|
  m_var_entry->update_query_id= thd->query_id;
 | 
						|
  return FALSE;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
  When a user variable is updated (in a SET command or a query like
 | 
						|
  SELECT @a:= ).
 | 
						|
*/
 | 
						|
 | 
						|
bool Item_func_set_user_var::fix_fields(THD *thd, Item **ref)
 | 
						|
{
 | 
						|
  DBUG_ASSERT(fixed() == 0);
 | 
						|
  /* fix_fields will call Item_func_set_user_var::fix_length_and_dec */
 | 
						|
  if (Item_func::fix_fields(thd, ref) || set_entry(thd, TRUE))
 | 
						|
    return TRUE;
 | 
						|
  /*
 | 
						|
    As it is wrong and confusing to associate any 
 | 
						|
    character set with NULL, @a should be latin2
 | 
						|
    after this query sequence:
 | 
						|
 | 
						|
      SET @a=_latin2'string';
 | 
						|
      SET @a=NULL;
 | 
						|
 | 
						|
    I.e. the second query should not change the charset
 | 
						|
    to the current default value, but should keep the 
 | 
						|
    original value assigned during the first query.
 | 
						|
    In order to do it, we don't copy charset
 | 
						|
    from the argument if the argument is NULL
 | 
						|
    and the variable has previously been initialized.
 | 
						|
  */
 | 
						|
  null_item= (args[0]->type() == NULL_ITEM);
 | 
						|
  if (!m_var_entry->charset() || !null_item)
 | 
						|
    m_var_entry->set_charset(args[0]->collation.derivation == DERIVATION_NUMERIC ?
 | 
						|
                             &my_charset_numeric : args[0]->collation.collation);
 | 
						|
  collation.set(m_var_entry->charset(),
 | 
						|
                args[0]->collation.derivation == DERIVATION_NUMERIC ?
 | 
						|
                DERIVATION_NUMERIC : DERIVATION_IMPLICIT);
 | 
						|
  switch (args[0]->result_type()) {
 | 
						|
  case STRING_RESULT:
 | 
						|
  case TIME_RESULT:
 | 
						|
    if (args[0]->field_type() == MYSQL_TYPE_GEOMETRY)
 | 
						|
      set_handler(args[0]->type_handler());
 | 
						|
    else
 | 
						|
      set_handler(type_handler_long_blob.
 | 
						|
                  type_handler_adjusted_to_max_octet_length(max_length,
 | 
						|
                                                            collation.collation));
 | 
						|
    break;
 | 
						|
  case REAL_RESULT:
 | 
						|
    set_handler(&type_handler_double);
 | 
						|
    break;
 | 
						|
  case INT_RESULT:
 | 
						|
    set_handler(Type_handler::type_handler_long_or_longlong(max_char_length(),
 | 
						|
                                                            unsigned_flag));
 | 
						|
    break;
 | 
						|
  case DECIMAL_RESULT:
 | 
						|
    set_handler(&type_handler_newdecimal);
 | 
						|
    break;
 | 
						|
  case ROW_RESULT:
 | 
						|
    DBUG_ASSERT(0);
 | 
						|
    set_handler(&type_handler_row);
 | 
						|
    break;
 | 
						|
  }
 | 
						|
  if (thd->lex->current_select)
 | 
						|
  {
 | 
						|
    /*
 | 
						|
      When this function is used in a derived table/view force the derived
 | 
						|
      table to be materialized to preserve possible side-effect of setting a
 | 
						|
      user variable.
 | 
						|
    */
 | 
						|
    SELECT_LEX_UNIT *unit= thd->lex->current_select->master_unit();
 | 
						|
    TABLE_LIST *derived;
 | 
						|
    for (derived= unit->derived;
 | 
						|
         derived;
 | 
						|
         derived= unit->derived)
 | 
						|
    {
 | 
						|
      derived->set_materialized_derived();
 | 
						|
      derived->prohibit_cond_pushdown= true;
 | 
						|
      if (unit->with_element && unit->with_element->is_recursive)
 | 
						|
        break;
 | 
						|
      unit= derived->select_lex->master_unit();
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  return FALSE;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool
 | 
						|
Item_func_set_user_var::fix_length_and_dec(THD *thd)
 | 
						|
{
 | 
						|
  base_flags|= (args[0]->base_flags & item_base_t::MAYBE_NULL);
 | 
						|
  decimals=args[0]->decimals;
 | 
						|
  if (args[0]->collation.derivation == DERIVATION_NUMERIC)
 | 
						|
  {
 | 
						|
    collation.set(DERIVATION_NUMERIC);
 | 
						|
    uint sign_length= args[0]->type_handler() == &type_handler_slong_ge0 ? 1: 0;
 | 
						|
    fix_length_and_charset(args[0]->max_char_length() + sign_length,
 | 
						|
                           &my_charset_numeric);
 | 
						|
  }
 | 
						|
  else
 | 
						|
  {
 | 
						|
    collation.set(DERIVATION_IMPLICIT);
 | 
						|
    fix_length_and_charset(args[0]->max_char_length(),
 | 
						|
                           args[0]->collation.collation);
 | 
						|
  }
 | 
						|
  unsigned_flag= args[0]->unsigned_flag;
 | 
						|
  return FALSE;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
  Mark field in read_map
 | 
						|
 | 
						|
  NOTES
 | 
						|
    This is used by filesort to register used fields in a a temporary
 | 
						|
    column read set or to register used fields in a view
 | 
						|
*/
 | 
						|
 | 
						|
bool Item_func_set_user_var::register_field_in_read_map(void *arg)
 | 
						|
{
 | 
						|
  if (result_field)
 | 
						|
  {
 | 
						|
    TABLE *table= (TABLE *) arg;
 | 
						|
    if (result_field->table == table || !table)
 | 
						|
      bitmap_set_bit(result_field->table->read_set, result_field->field_index);
 | 
						|
    if (result_field->vcol_info)
 | 
						|
      return result_field->vcol_info->
 | 
						|
               expr->walk(&Item::register_field_in_read_map, 1, arg);
 | 
						|
  }
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
  Mark field in bitmap supplied as *arg
 | 
						|
 | 
						|
*/
 | 
						|
 | 
						|
bool Item_func_set_user_var::register_field_in_bitmap(void *arg)
 | 
						|
{
 | 
						|
  MY_BITMAP *bitmap = (MY_BITMAP *) arg;
 | 
						|
  DBUG_ASSERT(bitmap);
 | 
						|
  if (result_field)
 | 
						|
  {
 | 
						|
    if (!bitmap)
 | 
						|
      return 1;
 | 
						|
    bitmap_set_bit(bitmap, result_field->field_index);
 | 
						|
  }
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Set value to user variable.
 | 
						|
 | 
						|
  @param entry          pointer to structure representing variable
 | 
						|
  @param set_null       should we set NULL value ?
 | 
						|
  @param ptr            pointer to buffer with new value
 | 
						|
  @param length         length of new value
 | 
						|
  @param type           type of new value
 | 
						|
  @param cs             charset info for new value
 | 
						|
  @param dv             derivation for new value
 | 
						|
  @param unsigned_arg   indicates if a value of type INT_RESULT is unsigned
 | 
						|
 | 
						|
  @note Sets error and fatal error if allocation fails.
 | 
						|
 | 
						|
  @retval
 | 
						|
    false   success
 | 
						|
  @retval
 | 
						|
    true    failure
 | 
						|
*/
 | 
						|
 | 
						|
bool
 | 
						|
update_hash(user_var_entry *entry, bool set_null, void *ptr, size_t length,
 | 
						|
            const Type_handler *th, CHARSET_INFO *cs)
 | 
						|
{
 | 
						|
  entry->set_handler(th);
 | 
						|
  if (set_null)
 | 
						|
  {
 | 
						|
    char *pos= (char*) entry+ ALIGN_SIZE(sizeof(user_var_entry));
 | 
						|
    if (entry->value && entry->value != pos)
 | 
						|
      my_free(entry->value);
 | 
						|
    entry->value= 0;
 | 
						|
    entry->length= 0;
 | 
						|
  }
 | 
						|
  else
 | 
						|
  {
 | 
						|
    if (th->result_type() == STRING_RESULT)
 | 
						|
      length++;					// Store strings with end \0
 | 
						|
    if (length <= extra_size)
 | 
						|
    {
 | 
						|
      /* Save value in value struct */
 | 
						|
      char *pos= (char*) entry+ ALIGN_SIZE(sizeof(user_var_entry));
 | 
						|
      if (entry->value != pos)
 | 
						|
      {
 | 
						|
	if (entry->value)
 | 
						|
	  my_free(entry->value);
 | 
						|
	entry->value=pos;
 | 
						|
      }
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
      /* Allocate variable */
 | 
						|
      if (entry->length != length)
 | 
						|
      {
 | 
						|
	char *pos= (char*) entry+ ALIGN_SIZE(sizeof(user_var_entry));
 | 
						|
	if (entry->value == pos)
 | 
						|
	  entry->value=0;
 | 
						|
        entry->value= (char*) my_realloc(key_memory_user_var_entry_value,
 | 
						|
                                         entry->value, length,
 | 
						|
                                         MYF(MY_ALLOW_ZERO_PTR | MY_WME |
 | 
						|
                                             ME_FATAL | MY_THREAD_SPECIFIC));
 | 
						|
        if (!entry->value)
 | 
						|
	  return 1;
 | 
						|
      }
 | 
						|
    }
 | 
						|
    if (th->result_type() == STRING_RESULT)
 | 
						|
    {
 | 
						|
      length--;					// Fix length change above
 | 
						|
      entry->value[length]= 0;			// Store end \0
 | 
						|
    }
 | 
						|
    if (length)
 | 
						|
      memmove(entry->value, ptr, length);
 | 
						|
    if (th->result_type() == DECIMAL_RESULT)
 | 
						|
      ((my_decimal*)entry->value)->fix_buffer_pointer();
 | 
						|
    entry->length= length;
 | 
						|
    entry->set_charset(cs);
 | 
						|
  }
 | 
						|
#ifdef USER_VAR_TRACKING
 | 
						|
#ifndef EMBEDDED_LIBRARY
 | 
						|
  THD *thd= current_thd;
 | 
						|
  thd->session_tracker.user_variables.mark_as_changed(thd, entry);
 | 
						|
#endif
 | 
						|
#endif // USER_VAR_TRACKING
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool
 | 
						|
Item_func_set_user_var::update_hash(void *ptr, size_t length,
 | 
						|
                                    const Type_handler *th,
 | 
						|
                                    CHARSET_INFO *cs)
 | 
						|
{
 | 
						|
  /*
 | 
						|
    If we set a variable explicitly to NULL then keep the old
 | 
						|
    result type of the variable
 | 
						|
  */
 | 
						|
  if (args[0]->type() == Item::FIELD_ITEM)
 | 
						|
  {
 | 
						|
    /* args[0]->null_value may be outdated */
 | 
						|
    null_value= ((Item_field*)args[0])->field->is_null();
 | 
						|
  }
 | 
						|
  else
 | 
						|
    null_value= args[0]->null_value;
 | 
						|
  if (null_value && null_item)
 | 
						|
    th= m_var_entry->type_handler(); // Don't change type of item
 | 
						|
  if (::update_hash(m_var_entry, null_value, ptr, length, th, cs))
 | 
						|
  {
 | 
						|
    null_value= 1;
 | 
						|
    return 1;
 | 
						|
  }
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/** Get the value of a variable as a double. */
 | 
						|
 | 
						|
double user_var_entry::val_real(bool *null_value)
 | 
						|
{
 | 
						|
  if ((*null_value= (value == 0)))
 | 
						|
    return 0.0;
 | 
						|
 | 
						|
  switch (type_handler()->result_type()) {
 | 
						|
  case REAL_RESULT:
 | 
						|
    return *(double*) value;
 | 
						|
  case INT_RESULT:
 | 
						|
    return (double) *(longlong*) value;
 | 
						|
  case DECIMAL_RESULT:
 | 
						|
    return ((my_decimal *)value)->to_double();
 | 
						|
  case STRING_RESULT:
 | 
						|
    return my_atof(value);                      // This is null terminated
 | 
						|
  case ROW_RESULT:
 | 
						|
  case TIME_RESULT:
 | 
						|
    DBUG_ASSERT(0);				// Impossible
 | 
						|
    break;
 | 
						|
  }
 | 
						|
  return 0.0;					// Impossible
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/** Get the value of a variable as an integer. */
 | 
						|
 | 
						|
longlong user_var_entry::val_int(bool *null_value) const
 | 
						|
{
 | 
						|
  if ((*null_value= (value == 0)))
 | 
						|
    return 0;
 | 
						|
 | 
						|
  switch (type_handler()->result_type()) {
 | 
						|
  case REAL_RESULT:
 | 
						|
    return (longlong) *(double*) value;
 | 
						|
  case INT_RESULT:
 | 
						|
    return *(longlong*) value;
 | 
						|
  case DECIMAL_RESULT:
 | 
						|
    return ((my_decimal *)value)->to_longlong(false);
 | 
						|
  case STRING_RESULT:
 | 
						|
  {
 | 
						|
    int error;
 | 
						|
    return my_strtoll10(value, (char**) 0, &error);// String is null terminated
 | 
						|
  }
 | 
						|
  case ROW_RESULT:
 | 
						|
  case TIME_RESULT:
 | 
						|
    DBUG_ASSERT(0);				// Impossible
 | 
						|
    break;
 | 
						|
  }
 | 
						|
  return 0;					// Impossible
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/** Get the value of a variable as a string. */
 | 
						|
 | 
						|
String *user_var_entry::val_str(bool *null_value, String *str,
 | 
						|
                                uint decimals) const
 | 
						|
{
 | 
						|
  if ((*null_value= (value == 0)))
 | 
						|
    return (String*) 0;
 | 
						|
 | 
						|
  switch (type_handler()->result_type()) {
 | 
						|
  case REAL_RESULT:
 | 
						|
    str->set_real(*(double*) value, decimals, charset());
 | 
						|
    break;
 | 
						|
  case INT_RESULT:
 | 
						|
    if (!type_handler()->is_unsigned())
 | 
						|
      str->set(*(longlong*) value, charset());
 | 
						|
    else
 | 
						|
      str->set(*(ulonglong*) value, charset());
 | 
						|
    break;
 | 
						|
  case DECIMAL_RESULT:
 | 
						|
    str_set_decimal((my_decimal *) value, str, charset());
 | 
						|
    break;
 | 
						|
  case STRING_RESULT:
 | 
						|
    if (str->copy(value, length, charset()))
 | 
						|
      str= 0;					// EOM error
 | 
						|
    break;
 | 
						|
  case ROW_RESULT:
 | 
						|
  case TIME_RESULT:
 | 
						|
    DBUG_ASSERT(0);				// Impossible
 | 
						|
    break;
 | 
						|
  }
 | 
						|
  return(str);
 | 
						|
}
 | 
						|
 | 
						|
/** Get the value of a variable as a decimal. */
 | 
						|
 | 
						|
my_decimal *user_var_entry::val_decimal(bool *null_value, my_decimal *val)
 | 
						|
{
 | 
						|
  if ((*null_value= (value == 0)))
 | 
						|
    return 0;
 | 
						|
 | 
						|
  switch (type_handler()->result_type()) {
 | 
						|
  case REAL_RESULT:
 | 
						|
    double2my_decimal(E_DEC_FATAL_ERROR, *(double*) value, val);
 | 
						|
    break;
 | 
						|
  case INT_RESULT:
 | 
						|
    int2my_decimal(E_DEC_FATAL_ERROR, *(longlong*) value, 0, val);
 | 
						|
    break;
 | 
						|
  case DECIMAL_RESULT:
 | 
						|
    my_decimal2decimal((my_decimal *) value, val);
 | 
						|
    break;
 | 
						|
  case STRING_RESULT:
 | 
						|
    str2my_decimal(E_DEC_FATAL_ERROR, value, length, charset(), val);
 | 
						|
    break;
 | 
						|
  case ROW_RESULT:
 | 
						|
  case TIME_RESULT:
 | 
						|
    DBUG_ASSERT(0);				// Impossible
 | 
						|
    break;
 | 
						|
  }
 | 
						|
  return(val);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  This functions is invoked on SET \@variable or
 | 
						|
  \@variable:= expression.
 | 
						|
 | 
						|
  Evaluate (and check expression), store results.
 | 
						|
 | 
						|
  @note
 | 
						|
    For now it always return OK. All problem with value evaluating
 | 
						|
    will be caught by thd->is_error() check in sql_set_variables().
 | 
						|
 | 
						|
  @retval
 | 
						|
    FALSE OK.
 | 
						|
*/
 | 
						|
 | 
						|
bool
 | 
						|
Item_func_set_user_var::check(bool use_result_field)
 | 
						|
{
 | 
						|
  DBUG_ENTER("Item_func_set_user_var::check");
 | 
						|
  if (use_result_field && !result_field)
 | 
						|
    use_result_field= FALSE;
 | 
						|
 | 
						|
  switch (result_type()) {
 | 
						|
  case REAL_RESULT:
 | 
						|
  {
 | 
						|
    save_result.vreal= use_result_field ? result_field->val_real() :
 | 
						|
                        args[0]->val_real();
 | 
						|
    break;
 | 
						|
  }
 | 
						|
  case INT_RESULT:
 | 
						|
  {
 | 
						|
    save_result.vint= use_result_field ? result_field->val_int() :
 | 
						|
                       args[0]->val_int();
 | 
						|
    unsigned_flag= (use_result_field ?
 | 
						|
                    ((Field_num*)result_field)->unsigned_flag:
 | 
						|
                    args[0]->unsigned_flag);
 | 
						|
    break;
 | 
						|
  }
 | 
						|
  case STRING_RESULT:
 | 
						|
  {
 | 
						|
    save_result.vstr= use_result_field ? result_field->val_str(&value) :
 | 
						|
                       args[0]->val_str(&value);
 | 
						|
    break;
 | 
						|
  }
 | 
						|
  case DECIMAL_RESULT:
 | 
						|
  {
 | 
						|
    save_result.vdec= use_result_field ?
 | 
						|
                       result_field->val_decimal(&decimal_buff) :
 | 
						|
                       args[0]->val_decimal(&decimal_buff);
 | 
						|
    break;
 | 
						|
  }
 | 
						|
  case ROW_RESULT:
 | 
						|
  case TIME_RESULT:
 | 
						|
    DBUG_ASSERT(0);                // This case should never be chosen
 | 
						|
    break;
 | 
						|
  }
 | 
						|
  DBUG_RETURN(FALSE);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  @brief Evaluate and store item's result.
 | 
						|
  This function is invoked on "SELECT ... INTO @var ...".
 | 
						|
  
 | 
						|
  @param    item    An item to get value from.
 | 
						|
*/
 | 
						|
 | 
						|
void Item_func_set_user_var::save_item_result(Item *item)
 | 
						|
{
 | 
						|
  DBUG_ENTER("Item_func_set_user_var::save_item_result");
 | 
						|
 | 
						|
  switch (args[0]->result_type()) {
 | 
						|
  case REAL_RESULT:
 | 
						|
    save_result.vreal= item->val_result();
 | 
						|
    break;
 | 
						|
  case INT_RESULT:
 | 
						|
    save_result.vint= item->val_int_result();
 | 
						|
    unsigned_flag= item->unsigned_flag;
 | 
						|
    break;
 | 
						|
  case STRING_RESULT:
 | 
						|
    save_result.vstr= item->str_result(&value);
 | 
						|
    break;
 | 
						|
  case DECIMAL_RESULT:
 | 
						|
    save_result.vdec= item->val_decimal_result(&decimal_buff);
 | 
						|
    break;
 | 
						|
  case ROW_RESULT:
 | 
						|
  case TIME_RESULT:
 | 
						|
    DBUG_ASSERT(0);                // This case should never be chosen
 | 
						|
    break;
 | 
						|
  }
 | 
						|
  DBUG_VOID_RETURN;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  This functions is invoked on
 | 
						|
  SET \@variable or \@variable:= expression.
 | 
						|
 | 
						|
  @note
 | 
						|
    We have to store the expression as such in the variable, independent of
 | 
						|
    the value method used by the user
 | 
						|
 | 
						|
  @retval
 | 
						|
    0	OK
 | 
						|
  @retval
 | 
						|
    1	EOM Error
 | 
						|
 | 
						|
*/
 | 
						|
 | 
						|
bool
 | 
						|
Item_func_set_user_var::update()
 | 
						|
{
 | 
						|
  bool res= 0;
 | 
						|
  DBUG_ENTER("Item_func_set_user_var::update");
 | 
						|
 | 
						|
  switch (result_type()) {
 | 
						|
  case REAL_RESULT:
 | 
						|
  {
 | 
						|
    res= update_hash((void*) &save_result.vreal,sizeof(save_result.vreal),
 | 
						|
		     &type_handler_double, &my_charset_numeric);
 | 
						|
    break;
 | 
						|
  }
 | 
						|
  case INT_RESULT:
 | 
						|
  {
 | 
						|
    res= update_hash((void*) &save_result.vint, sizeof(save_result.vint),
 | 
						|
                     unsigned_flag ? (Type_handler *) &type_handler_ulonglong :
 | 
						|
                                     (Type_handler *) &type_handler_slonglong,
 | 
						|
                     &my_charset_numeric);
 | 
						|
    break;
 | 
						|
  }
 | 
						|
  case STRING_RESULT:
 | 
						|
  {
 | 
						|
    if (!save_result.vstr)					// Null value
 | 
						|
      res= update_hash((void*) 0, 0, &type_handler_long_blob, &my_charset_bin);
 | 
						|
    else
 | 
						|
      res= update_hash((void*) save_result.vstr->ptr(),
 | 
						|
                       save_result.vstr->length(),
 | 
						|
                       field_type() == MYSQL_TYPE_GEOMETRY ?
 | 
						|
                       type_handler() : &type_handler_long_blob,
 | 
						|
                       save_result.vstr->charset());
 | 
						|
    break;
 | 
						|
  }
 | 
						|
  case DECIMAL_RESULT:
 | 
						|
  {
 | 
						|
    if (!save_result.vdec)					// Null value
 | 
						|
      res= update_hash((void*) 0, 0, &type_handler_newdecimal, &my_charset_bin);
 | 
						|
    else
 | 
						|
      res= update_hash((void*) save_result.vdec,
 | 
						|
                       sizeof(my_decimal), &type_handler_newdecimal,
 | 
						|
                       &my_charset_numeric);
 | 
						|
    break;
 | 
						|
  }
 | 
						|
  case ROW_RESULT:
 | 
						|
  case TIME_RESULT:
 | 
						|
    DBUG_ASSERT(0);                // This case should never be chosen
 | 
						|
    break;
 | 
						|
  }
 | 
						|
  DBUG_RETURN(res);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
double Item_func_set_user_var::val_real()
 | 
						|
{
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
  check(0);
 | 
						|
  update();					// Store expression
 | 
						|
  return m_var_entry->val_real(&null_value);
 | 
						|
}
 | 
						|
 | 
						|
longlong Item_func_set_user_var::val_int()
 | 
						|
{
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
  check(0);
 | 
						|
  update();					// Store expression
 | 
						|
  return m_var_entry->val_int(&null_value);
 | 
						|
}
 | 
						|
 | 
						|
String *Item_func_set_user_var::val_str(String *str)
 | 
						|
{
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
  check(0);
 | 
						|
  update();					// Store expression
 | 
						|
  return m_var_entry->val_str(&null_value, str, decimals);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
my_decimal *Item_func_set_user_var::val_decimal(my_decimal *val)
 | 
						|
{
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
  check(0);
 | 
						|
  update();					// Store expression
 | 
						|
  return m_var_entry->val_decimal(&null_value, val);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
double Item_func_set_user_var::val_result()
 | 
						|
{
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
  check(TRUE);
 | 
						|
  update();					// Store expression
 | 
						|
  return m_var_entry->val_real(&null_value);
 | 
						|
}
 | 
						|
 | 
						|
longlong Item_func_set_user_var::val_int_result()
 | 
						|
{
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
  check(TRUE);
 | 
						|
  update();					// Store expression
 | 
						|
  return m_var_entry->val_int(&null_value);
 | 
						|
}
 | 
						|
 | 
						|
bool Item_func_set_user_var::val_bool_result()
 | 
						|
{
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
  check(TRUE);
 | 
						|
  update();					// Store expression
 | 
						|
  return m_var_entry->val_int(&null_value) != 0;
 | 
						|
}
 | 
						|
 | 
						|
String *Item_func_set_user_var::str_result(String *str)
 | 
						|
{
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
  check(TRUE);
 | 
						|
  update();					// Store expression
 | 
						|
  return m_var_entry->val_str(&null_value, str, decimals);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
my_decimal *Item_func_set_user_var::val_decimal_result(my_decimal *val)
 | 
						|
{
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
  check(TRUE);
 | 
						|
  update();					// Store expression
 | 
						|
  return m_var_entry->val_decimal(&null_value, val);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool Item_func_set_user_var::is_null_result()
 | 
						|
{
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
  check(TRUE);
 | 
						|
  update();					// Store expression
 | 
						|
  return is_null();
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void Item_func_set_user_var::print(String *str, enum_query_type query_type)
 | 
						|
{
 | 
						|
  str->append(STRING_WITH_LEN("@"));
 | 
						|
  str->append(&name);
 | 
						|
  str->append(STRING_WITH_LEN(":="));
 | 
						|
  args[0]->print_parenthesised(str, query_type, precedence());
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void Item_func_set_user_var::print_as_stmt(String *str,
 | 
						|
                                           enum_query_type query_type)
 | 
						|
{
 | 
						|
  str->append(STRING_WITH_LEN("set @"));
 | 
						|
  str->append(&name);
 | 
						|
  str->append(STRING_WITH_LEN(":="));
 | 
						|
  args[0]->print_parenthesised(str, query_type, precedence());
 | 
						|
}
 | 
						|
 | 
						|
bool Item_func_set_user_var::send(Protocol *protocol, st_value *buffer)
 | 
						|
{
 | 
						|
  if (result_field)
 | 
						|
  {
 | 
						|
    check(1);
 | 
						|
    update();
 | 
						|
    return protocol->store(result_field);
 | 
						|
  }
 | 
						|
  return Item::send(protocol, buffer);
 | 
						|
}
 | 
						|
 | 
						|
void Item_func_set_user_var::make_send_field(THD *thd, Send_field *tmp_field)
 | 
						|
{
 | 
						|
  if (result_field)
 | 
						|
  {
 | 
						|
    result_field->make_send_field(tmp_field);
 | 
						|
    DBUG_ASSERT(tmp_field->table_name.str != 0);
 | 
						|
    if (Item::name.str)
 | 
						|
      tmp_field->col_name= Item::name;          // Use user supplied name
 | 
						|
  }
 | 
						|
  else
 | 
						|
    Item::make_send_field(thd, tmp_field);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
  Save the value of a user variable into a field
 | 
						|
 | 
						|
  SYNOPSIS
 | 
						|
    save_in_field()
 | 
						|
      field           target field to save the value to
 | 
						|
      no_conversion   flag indicating whether conversions are allowed
 | 
						|
 | 
						|
  DESCRIPTION
 | 
						|
    Save the function value into a field and update the user variable
 | 
						|
    accordingly. If a result field is defined and the target field doesn't
 | 
						|
    coincide with it then the value from the result field will be used as
 | 
						|
    the new value of the user variable.
 | 
						|
 | 
						|
    The reason to have this method rather than simply using the result
 | 
						|
    field in the val_xxx() methods is that the value from the result field
 | 
						|
    not always can be used when the result field is defined.
 | 
						|
    Let's consider the following cases:
 | 
						|
    1) when filling a tmp table the result field is defined but the value of it
 | 
						|
    is undefined because it has to be produced yet. Thus we can't use it.
 | 
						|
    2) on execution of an INSERT ... SELECT statement the save_in_field()
 | 
						|
    function will be called to fill the data in the new record. If the SELECT
 | 
						|
    part uses a tmp table then the result field is defined and should be
 | 
						|
    used in order to get the correct result.
 | 
						|
 | 
						|
    The difference between the SET_USER_VAR function and regular functions
 | 
						|
    like CONCAT is that the Item_func objects for the regular functions are
 | 
						|
    replaced by Item_field objects after the values of these functions have
 | 
						|
    been stored in a tmp table. Yet an object of the Item_field class cannot
 | 
						|
    be used to update a user variable.
 | 
						|
    Due to this we have to handle the result field in a special way here and
 | 
						|
    in the Item_func_set_user_var::send() function.
 | 
						|
 | 
						|
  RETURN VALUES
 | 
						|
    FALSE       Ok
 | 
						|
    TRUE        Error
 | 
						|
*/
 | 
						|
 | 
						|
int Item_func_set_user_var::save_in_field(Field *field, bool no_conversions,
 | 
						|
                                          bool can_use_result_field)
 | 
						|
{
 | 
						|
  bool use_result_field= (!can_use_result_field ? 0 :
 | 
						|
                          (result_field && result_field != field));
 | 
						|
  int error;
 | 
						|
 | 
						|
  /* Update the value of the user variable */
 | 
						|
  check(use_result_field);
 | 
						|
  update();
 | 
						|
 | 
						|
  if (result_type() == STRING_RESULT ||
 | 
						|
      (result_type() == REAL_RESULT &&
 | 
						|
       field->result_type() == STRING_RESULT))
 | 
						|
  {
 | 
						|
    String *result;
 | 
						|
    CHARSET_INFO *cs= collation.collation;
 | 
						|
    char buff[MAX_FIELD_WIDTH];		// Alloc buffer for small columns
 | 
						|
    str_value.set_buffer_if_not_allocated(buff, sizeof(buff), cs);
 | 
						|
    result= m_var_entry->val_str(&null_value, &str_value, decimals);
 | 
						|
 | 
						|
    if (null_value)
 | 
						|
    {
 | 
						|
      str_value.set_buffer_if_not_allocated(0, 0, cs);
 | 
						|
      return set_field_to_null_with_conversions(field, no_conversions);
 | 
						|
    }
 | 
						|
 | 
						|
    /* NOTE: If null_value == FALSE, "result" must be not NULL.  */
 | 
						|
 | 
						|
    field->set_notnull();
 | 
						|
    error=field->store(result->ptr(),result->length(),cs);
 | 
						|
    str_value.set_buffer_if_not_allocated(0, 0, cs);
 | 
						|
  }
 | 
						|
  else if (result_type() == REAL_RESULT)
 | 
						|
  {
 | 
						|
    double nr= m_var_entry->val_real(&null_value);
 | 
						|
    if (null_value)
 | 
						|
      return set_field_to_null(field);
 | 
						|
    field->set_notnull();
 | 
						|
    error=field->store(nr);
 | 
						|
  }
 | 
						|
  else if (result_type() == DECIMAL_RESULT)
 | 
						|
  {
 | 
						|
    my_decimal decimal_value;
 | 
						|
    my_decimal *val= m_var_entry->val_decimal(&null_value, &decimal_value);
 | 
						|
    if (null_value)
 | 
						|
      return set_field_to_null(field);
 | 
						|
    field->set_notnull();
 | 
						|
    error=field->store_decimal(val);
 | 
						|
  }
 | 
						|
  else
 | 
						|
  {
 | 
						|
    longlong nr= m_var_entry->val_int(&null_value);
 | 
						|
    if (null_value)
 | 
						|
      return set_field_to_null_with_conversions(field, no_conversions);
 | 
						|
    field->set_notnull();
 | 
						|
    error=field->store(nr, unsigned_flag);
 | 
						|
  }
 | 
						|
  return error;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
String *
 | 
						|
Item_func_get_user_var::val_str(String *str)
 | 
						|
{
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
  DBUG_ENTER("Item_func_get_user_var::val_str");
 | 
						|
  if (!m_var_entry)
 | 
						|
    DBUG_RETURN((String*) 0);			// No such variable
 | 
						|
  DBUG_RETURN(m_var_entry->val_str(&null_value, str, decimals));
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
double Item_func_get_user_var::val_real()
 | 
						|
{
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
  if (!m_var_entry)
 | 
						|
    return 0.0;					// No such variable
 | 
						|
  return (m_var_entry->val_real(&null_value));
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
my_decimal *Item_func_get_user_var::val_decimal(my_decimal *dec)
 | 
						|
{
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
  if (!m_var_entry)
 | 
						|
    return 0;
 | 
						|
  return m_var_entry->val_decimal(&null_value, dec);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
longlong Item_func_get_user_var::val_int()
 | 
						|
{
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
  if (!m_var_entry)
 | 
						|
    return 0;				// No such variable
 | 
						|
  return (m_var_entry->val_int(&null_value));
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Get variable by name and, if necessary, put the record of variable 
 | 
						|
  use into the binary log.
 | 
						|
 | 
						|
  When a user variable is invoked from an update query (INSERT, UPDATE etc),
 | 
						|
  stores this variable and its value in thd->user_var_events, so that it can be
 | 
						|
  written to the binlog (will be written just before the query is written, see
 | 
						|
  log.cc).
 | 
						|
 | 
						|
  @param      thd        Current thread
 | 
						|
  @param      name       Variable name
 | 
						|
  @param[out] out_entry  variable structure or NULL. The pointer is set
 | 
						|
                         regardless of whether function succeeded or not.
 | 
						|
 | 
						|
  @retval
 | 
						|
    0  OK
 | 
						|
  @retval
 | 
						|
    1  Failed to put appropriate record into binary log
 | 
						|
 | 
						|
*/
 | 
						|
 | 
						|
static int
 | 
						|
get_var_with_binlog(THD *thd, enum_sql_command sql_command,
 | 
						|
                    LEX_CSTRING *name, user_var_entry **out_entry)
 | 
						|
{
 | 
						|
  BINLOG_USER_VAR_EVENT *user_var_event;
 | 
						|
  user_var_entry *var_entry;
 | 
						|
  var_entry= get_variable(&thd->user_vars, name, 0);
 | 
						|
 | 
						|
  /*
 | 
						|
    Any reference to user-defined variable which is done from stored
 | 
						|
    function or trigger affects their execution and the execution of the
 | 
						|
    calling statement. We must log all such variables even if they are 
 | 
						|
    not involved in table-updating statements.
 | 
						|
  */
 | 
						|
  if (!(opt_bin_log && 
 | 
						|
       (is_update_query(sql_command) || thd->in_sub_stmt)))
 | 
						|
  {
 | 
						|
    *out_entry= var_entry;
 | 
						|
    return 0;
 | 
						|
  }
 | 
						|
 | 
						|
  if (!var_entry)
 | 
						|
  {
 | 
						|
    /*
 | 
						|
      If the variable does not exist, it's NULL, but we want to create it so
 | 
						|
      that it gets into the binlog (if it didn't, the slave could be
 | 
						|
      influenced by a variable of the same name previously set by another
 | 
						|
      thread).
 | 
						|
      We create it like if it had been explicitly set with SET before.
 | 
						|
      The 'new' mimics what sql_yacc.yy does when 'SET @a=10;'.
 | 
						|
      sql_set_variables() is what is called from 'case SQLCOM_SET_OPTION'
 | 
						|
      in dispatch_command()). Instead of building a one-element list to pass to
 | 
						|
      sql_set_variables(), we could instead manually call check() and update();
 | 
						|
      this would save memory and time; but calling sql_set_variables() makes
 | 
						|
      one unique place to maintain (sql_set_variables()). 
 | 
						|
 | 
						|
      Manipulation with lex is necessary since free_underlaid_joins
 | 
						|
      is going to release memory belonging to the main query.
 | 
						|
    */
 | 
						|
 | 
						|
    List<set_var_base> tmp_var_list;
 | 
						|
    LEX *sav_lex= thd->lex, lex_tmp;
 | 
						|
    thd->lex= &lex_tmp;
 | 
						|
    lex_start(thd);
 | 
						|
    tmp_var_list.push_back(new (thd->mem_root)
 | 
						|
                           set_var_user(new (thd->mem_root)
 | 
						|
                                        Item_func_set_user_var(thd, name,
 | 
						|
                                                               new (thd->mem_root) Item_null(thd))),
 | 
						|
                           thd->mem_root);
 | 
						|
    /* Create the variable if the above allocations succeeded */
 | 
						|
    if (unlikely(thd->is_fatal_error) ||
 | 
						|
        unlikely(sql_set_variables(thd, &tmp_var_list, false)))
 | 
						|
    {
 | 
						|
      thd->lex= sav_lex;
 | 
						|
      goto err;
 | 
						|
    }
 | 
						|
    thd->lex= sav_lex;
 | 
						|
    if (unlikely(!(var_entry= get_variable(&thd->user_vars, name, 0))))
 | 
						|
      goto err;
 | 
						|
  }
 | 
						|
  else if (var_entry->used_query_id == thd->query_id ||
 | 
						|
           mysql_bin_log.is_query_in_union(thd, var_entry->used_query_id))
 | 
						|
  {
 | 
						|
    /* 
 | 
						|
       If this variable was already stored in user_var_events by this query
 | 
						|
       (because it's used in more than one place in the query), don't store
 | 
						|
       it.
 | 
						|
    */
 | 
						|
    *out_entry= var_entry;
 | 
						|
    return 0;
 | 
						|
  }
 | 
						|
 | 
						|
  size_t size;
 | 
						|
  /*
 | 
						|
    First we need to store value of var_entry, when the next situation
 | 
						|
    appears:
 | 
						|
    > set @a:=1;
 | 
						|
    > insert into t1 values (@a), (@a:=@a+1), (@a:=@a+1);
 | 
						|
    We have to write to binlog value @a= 1.
 | 
						|
 | 
						|
    We allocate the user_var_event on user_var_events_alloc pool, not on
 | 
						|
    the this-statement-execution pool because in SPs user_var_event objects 
 | 
						|
    may need to be valid after current [SP] statement execution pool is
 | 
						|
    destroyed.
 | 
						|
  */
 | 
						|
  size= ALIGN_SIZE(sizeof(BINLOG_USER_VAR_EVENT)) + var_entry->length;
 | 
						|
  if (unlikely(!(user_var_event= (BINLOG_USER_VAR_EVENT *)
 | 
						|
                 alloc_root(thd->user_var_events_alloc, size))))
 | 
						|
    goto err;
 | 
						|
 | 
						|
  user_var_event->value= (char*) user_var_event +
 | 
						|
    ALIGN_SIZE(sizeof(BINLOG_USER_VAR_EVENT));
 | 
						|
  user_var_event->user_var_event= var_entry;
 | 
						|
  user_var_event->th= var_entry->type_handler();
 | 
						|
  user_var_event->charset_number= var_entry->charset()->number;
 | 
						|
  if (!var_entry->value)
 | 
						|
  {
 | 
						|
    /* NULL value*/
 | 
						|
    user_var_event->length= 0;
 | 
						|
    user_var_event->value= 0;
 | 
						|
  }
 | 
						|
  else
 | 
						|
  {
 | 
						|
    user_var_event->length= var_entry->length;
 | 
						|
    memcpy(user_var_event->value, var_entry->value,
 | 
						|
           var_entry->length);
 | 
						|
  }
 | 
						|
  /* Mark that this variable has been used by this query */
 | 
						|
  var_entry->used_query_id= thd->query_id;
 | 
						|
  if (insert_dynamic(&thd->user_var_events, (uchar*) &user_var_event))
 | 
						|
    goto err;
 | 
						|
 | 
						|
  *out_entry= var_entry;
 | 
						|
  return 0;
 | 
						|
 | 
						|
err:
 | 
						|
  *out_entry= var_entry;
 | 
						|
  return 1;
 | 
						|
}
 | 
						|
 | 
						|
bool Item_func_get_user_var::fix_length_and_dec(THD *thd)
 | 
						|
{
 | 
						|
  int error;
 | 
						|
  set_maybe_null();
 | 
						|
  decimals=NOT_FIXED_DEC;
 | 
						|
  max_length=MAX_BLOB_WIDTH;
 | 
						|
 | 
						|
  error= get_var_with_binlog(thd, thd->lex->sql_command, &name, &m_var_entry);
 | 
						|
 | 
						|
  /*
 | 
						|
    If the variable didn't exist it has been created as a STRING-type.
 | 
						|
    'm_var_entry' is NULL only if there occurred an error during the call to
 | 
						|
    get_var_with_binlog.
 | 
						|
  */
 | 
						|
  if (likely(!error && m_var_entry))
 | 
						|
  {
 | 
						|
    unsigned_flag= m_var_entry->type_handler()->is_unsigned();
 | 
						|
    max_length= (uint32)m_var_entry->length;
 | 
						|
    switch (m_var_entry->type_handler()->result_type()) {
 | 
						|
    case REAL_RESULT:
 | 
						|
      collation.set(&my_charset_numeric, DERIVATION_NUMERIC);
 | 
						|
      fix_char_length(DBL_DIG + 8);
 | 
						|
      set_handler(&type_handler_double);
 | 
						|
      break;
 | 
						|
    case INT_RESULT:
 | 
						|
      collation.set(&my_charset_numeric, DERIVATION_NUMERIC);
 | 
						|
      fix_char_length(MAX_BIGINT_WIDTH);
 | 
						|
      decimals=0;
 | 
						|
      if (unsigned_flag)
 | 
						|
        set_handler(&type_handler_ulonglong);
 | 
						|
      else
 | 
						|
        set_handler(&type_handler_slonglong);
 | 
						|
      break;
 | 
						|
    case STRING_RESULT:
 | 
						|
      collation.set(m_var_entry->charset(), DERIVATION_IMPLICIT);
 | 
						|
      max_length= MAX_BLOB_WIDTH - 1;
 | 
						|
      set_handler(&type_handler_long_blob);
 | 
						|
      if (m_var_entry->type_handler()->field_type() == MYSQL_TYPE_GEOMETRY)
 | 
						|
        set_handler(m_var_entry->type_handler());
 | 
						|
      break;
 | 
						|
    case DECIMAL_RESULT:
 | 
						|
      collation.set(&my_charset_numeric, DERIVATION_NUMERIC);
 | 
						|
      fix_char_length(DECIMAL_MAX_STR_LENGTH);
 | 
						|
      decimals= DECIMAL_MAX_SCALE;
 | 
						|
      set_handler(&type_handler_newdecimal);
 | 
						|
      break;
 | 
						|
    case ROW_RESULT:                            // Keep compiler happy
 | 
						|
    case TIME_RESULT:
 | 
						|
      DBUG_ASSERT(0);                // This case should never be chosen
 | 
						|
      break;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  else
 | 
						|
  {
 | 
						|
    collation.set(&my_charset_bin, DERIVATION_IMPLICIT);
 | 
						|
    null_value= 1;
 | 
						|
    set_handler(&type_handler_long_blob);
 | 
						|
    max_length= MAX_BLOB_WIDTH;
 | 
						|
  }
 | 
						|
  return false;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool Item_func_get_user_var::const_item() const
 | 
						|
{
 | 
						|
  return (!m_var_entry ||
 | 
						|
          current_thd->query_id != m_var_entry->update_query_id);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void Item_func_get_user_var::print(String *str, enum_query_type query_type)
 | 
						|
{
 | 
						|
  str->append(STRING_WITH_LEN("@"));
 | 
						|
  append_identifier(current_thd, str, &name);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool Item_func_get_user_var::eq(const Item *item, bool binary_cmp) const
 | 
						|
{
 | 
						|
  /* Assume we don't have rtti */
 | 
						|
  if (this == item)
 | 
						|
    return 1;					// Same item is same.
 | 
						|
  /* Check if other type is also a get_user_var() object */
 | 
						|
  if (item->type() != FUNC_ITEM ||
 | 
						|
      ((Item_func*) item)->functype() != functype())
 | 
						|
    return 0;
 | 
						|
  Item_func_get_user_var *other=(Item_func_get_user_var*) item;
 | 
						|
  return (name.length == other->name.length &&
 | 
						|
	  !memcmp(name.str, other->name.str, name.length));
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool Item_func_get_user_var::set_value(THD *thd,
 | 
						|
                                       sp_rcontext * /*ctx*/, Item **it)
 | 
						|
{
 | 
						|
  LEX_CSTRING tmp_name= get_name();
 | 
						|
  Item_func_set_user_var *suv= new (thd->mem_root) Item_func_set_user_var(thd, &tmp_name, *it);
 | 
						|
  /*
 | 
						|
    Item_func_set_user_var is not fixed after construction, call
 | 
						|
    fix_fields().
 | 
						|
  */
 | 
						|
  return (!suv || suv->fix_fields(thd, it) || suv->check(0) || suv->update());
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool Item_user_var_as_out_param::fix_fields(THD *thd, Item **ref)
 | 
						|
{
 | 
						|
  DBUG_ASSERT(!fixed());
 | 
						|
  DBUG_ASSERT(thd->lex->exchange);
 | 
						|
  if (!(entry= get_variable(&thd->user_vars, &org_name, 1)))
 | 
						|
    return TRUE;
 | 
						|
  entry->set_handler(&type_handler_long_blob);
 | 
						|
  /*
 | 
						|
    Let us set the same collation which is used for loading
 | 
						|
    of fields in LOAD DATA INFILE.
 | 
						|
    (Since Item_user_var_as_out_param is used only there).
 | 
						|
  */
 | 
						|
  entry->set_charset(thd->lex->exchange->cs ?
 | 
						|
                     thd->lex->exchange->cs :
 | 
						|
                     thd->variables.collation_database);
 | 
						|
  entry->update_query_id= thd->query_id;
 | 
						|
  return FALSE;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void Item_user_var_as_out_param::set_null_value(CHARSET_INFO* cs)
 | 
						|
{
 | 
						|
  ::update_hash(entry, TRUE, 0, 0, &type_handler_long_blob, cs);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void Item_user_var_as_out_param::set_value(const char *str, uint length,
 | 
						|
                                           CHARSET_INFO* cs)
 | 
						|
{
 | 
						|
  ::update_hash(entry, FALSE, (void*)str, length, &type_handler_long_blob, cs);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
double Item_user_var_as_out_param::val_real()
 | 
						|
{
 | 
						|
  DBUG_ASSERT(0);
 | 
						|
  return 0.0;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
longlong Item_user_var_as_out_param::val_int()
 | 
						|
{
 | 
						|
  DBUG_ASSERT(0);
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
String* Item_user_var_as_out_param::val_str(String *str)
 | 
						|
{
 | 
						|
  DBUG_ASSERT(0);
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
my_decimal* Item_user_var_as_out_param::val_decimal(my_decimal *decimal_buffer)
 | 
						|
{
 | 
						|
  DBUG_ASSERT(0);
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool Item_user_var_as_out_param::get_date(THD *thd, MYSQL_TIME *ltime,
 | 
						|
                                          date_mode_t fuzzydate)
 | 
						|
{
 | 
						|
  DBUG_ASSERT(0);
 | 
						|
  return true;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void Item_user_var_as_out_param::load_data_print_for_log_event(THD *thd,
 | 
						|
                                                               String *str)
 | 
						|
                                                               const
 | 
						|
{
 | 
						|
  str->append('@');
 | 
						|
  append_identifier(thd, str, &org_name);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
Item_func_get_system_var::
 | 
						|
Item_func_get_system_var(THD *thd, sys_var *var_arg, enum_var_type var_type_arg,
 | 
						|
                       LEX_CSTRING *component_arg, const char *name_arg,
 | 
						|
                       size_t name_len_arg):
 | 
						|
  Item_func(thd), var(var_arg), var_type(var_type_arg),
 | 
						|
  orig_var_type(var_type_arg), component(*component_arg), cache_present(0)
 | 
						|
{
 | 
						|
  /* set_name() will allocate the name */
 | 
						|
  set_name(thd, name_arg, (uint) name_len_arg, system_charset_info);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool Item_func_get_system_var::is_written_to_binlog()
 | 
						|
{
 | 
						|
  return var->is_written_to_binlog(var_type);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void Item_func_get_system_var::update_null_value()
 | 
						|
{
 | 
						|
  THD *thd= current_thd;
 | 
						|
  int save_no_errors= thd->no_errors;
 | 
						|
  thd->no_errors= TRUE;
 | 
						|
  type_handler()->Item_update_null_value(this);
 | 
						|
  thd->no_errors= save_no_errors;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool Item_func_get_system_var::fix_length_and_dec(THD *thd)
 | 
						|
{
 | 
						|
  const char *cptr;
 | 
						|
  set_maybe_null();
 | 
						|
  max_length= 0;
 | 
						|
 | 
						|
  if (var->check_type(var_type))
 | 
						|
  {
 | 
						|
    if (var_type != OPT_DEFAULT)
 | 
						|
    {
 | 
						|
      my_error(ER_INCORRECT_GLOBAL_LOCAL_VAR, MYF(0),
 | 
						|
               var->name.str, var_type == OPT_GLOBAL ? "SESSION" : "GLOBAL");
 | 
						|
      return TRUE;
 | 
						|
    }
 | 
						|
    /* As there was no local variable, return the global value */
 | 
						|
    var_type= OPT_GLOBAL;
 | 
						|
  }
 | 
						|
 | 
						|
  switch (var->show_type())
 | 
						|
  {
 | 
						|
    case SHOW_HA_ROWS:
 | 
						|
    case SHOW_UINT:
 | 
						|
    case SHOW_ULONG:
 | 
						|
    case SHOW_ULONGLONG:
 | 
						|
      unsigned_flag= TRUE;
 | 
						|
      /* fall through */
 | 
						|
    case SHOW_SINT:
 | 
						|
    case SHOW_SLONG:
 | 
						|
    case SHOW_SLONGLONG:
 | 
						|
      collation= DTCollation_numeric();
 | 
						|
      fix_char_length(MY_INT64_NUM_DECIMAL_DIGITS);
 | 
						|
      decimals=0;
 | 
						|
      break;
 | 
						|
    case SHOW_CHAR:
 | 
						|
    case SHOW_CHAR_PTR:
 | 
						|
      mysql_mutex_lock(&LOCK_global_system_variables);
 | 
						|
      cptr= var->show_type() == SHOW_CHAR ?
 | 
						|
          reinterpret_cast<const char*>(var->value_ptr(thd, var_type,
 | 
						|
                                                       &component)) :
 | 
						|
          *reinterpret_cast<const char* const*>(var->value_ptr(thd,
 | 
						|
                                                               var_type,
 | 
						|
                                                               &component));
 | 
						|
      if (cptr)
 | 
						|
        max_length= (uint32) system_charset_info->numchars(cptr,
 | 
						|
                                                           cptr + strlen(cptr));
 | 
						|
      mysql_mutex_unlock(&LOCK_global_system_variables);
 | 
						|
      collation.set(system_charset_info, DERIVATION_SYSCONST);
 | 
						|
      max_length*= system_charset_info->mbmaxlen;
 | 
						|
      decimals=NOT_FIXED_DEC;
 | 
						|
      break;
 | 
						|
    case SHOW_LEX_STRING:
 | 
						|
      {
 | 
						|
        mysql_mutex_lock(&LOCK_global_system_variables);
 | 
						|
        const LEX_STRING *ls=
 | 
						|
                reinterpret_cast<const LEX_STRING*>(var->value_ptr(current_thd,
 | 
						|
                                                                   var_type,
 | 
						|
                                                                   &component));
 | 
						|
        max_length= (uint32) system_charset_info->numchars(ls->str,
 | 
						|
                                                           ls->str + ls->length);
 | 
						|
        mysql_mutex_unlock(&LOCK_global_system_variables);
 | 
						|
        collation.set(system_charset_info, DERIVATION_SYSCONST);
 | 
						|
        max_length*= system_charset_info->mbmaxlen;
 | 
						|
        decimals=NOT_FIXED_DEC;
 | 
						|
      }
 | 
						|
      break;
 | 
						|
    case SHOW_BOOL:
 | 
						|
    case SHOW_MY_BOOL:
 | 
						|
      collation= DTCollation_numeric();
 | 
						|
      fix_char_length(1);
 | 
						|
      decimals=0;
 | 
						|
      break;
 | 
						|
    case SHOW_DOUBLE:
 | 
						|
      decimals= 6;
 | 
						|
      collation= DTCollation_numeric();
 | 
						|
      fix_char_length(DBL_DIG + 6);
 | 
						|
      break;
 | 
						|
    default:
 | 
						|
      my_error(ER_VAR_CANT_BE_READ, MYF(0), var->name.str);
 | 
						|
      break;
 | 
						|
  }
 | 
						|
  return FALSE;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void Item_func_get_system_var::print(String *str, enum_query_type query_type)
 | 
						|
{
 | 
						|
  if (name.length)
 | 
						|
    str->append(&name);
 | 
						|
  else
 | 
						|
  {
 | 
						|
    str->append(STRING_WITH_LEN("@@"));
 | 
						|
    if (component.length)
 | 
						|
    {
 | 
						|
      str->append(&component);
 | 
						|
      str->append('.');
 | 
						|
    }
 | 
						|
    else if (var_type == SHOW_OPT_GLOBAL && var->scope() != sys_var::GLOBAL)
 | 
						|
    {
 | 
						|
      str->append(STRING_WITH_LEN("global."));
 | 
						|
    }
 | 
						|
    str->append(&var->name);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
bool Item_func_get_system_var::check_vcol_func_processor(void *arg)
 | 
						|
{
 | 
						|
  return mark_unsupported_function("@@", var->name.str, arg, VCOL_SESSION_FUNC);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
const Type_handler *Item_func_get_system_var::type_handler() const
 | 
						|
{
 | 
						|
  switch (var->show_type())
 | 
						|
  {
 | 
						|
    case SHOW_BOOL:
 | 
						|
    case SHOW_MY_BOOL:
 | 
						|
    case SHOW_SINT:
 | 
						|
    case SHOW_SLONG:
 | 
						|
    case SHOW_SLONGLONG:
 | 
						|
      return &type_handler_slonglong;
 | 
						|
    case SHOW_UINT:
 | 
						|
    case SHOW_ULONG:
 | 
						|
    case SHOW_ULONGLONG:
 | 
						|
    case SHOW_HA_ROWS:
 | 
						|
      return &type_handler_ulonglong;
 | 
						|
    case SHOW_CHAR: 
 | 
						|
    case SHOW_CHAR_PTR: 
 | 
						|
    case SHOW_LEX_STRING:
 | 
						|
      return &type_handler_varchar;
 | 
						|
    case SHOW_DOUBLE:
 | 
						|
      return &type_handler_double;
 | 
						|
    default:
 | 
						|
      my_error(ER_VAR_CANT_BE_READ, MYF(0), var->name.str);
 | 
						|
      return &type_handler_varchar;              // keep the compiler happy
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
longlong Item_func_get_system_var::val_int()
 | 
						|
{
 | 
						|
  THD *thd= current_thd;
 | 
						|
 | 
						|
  DBUG_EXECUTE_IF("simulate_non_gtid_aware_master",
 | 
						|
                  {
 | 
						|
                    if (0 == strcmp("gtid_domain_id", var->name.str))
 | 
						|
                    {
 | 
						|
                      my_error(ER_VAR_CANT_BE_READ, MYF(0), var->name.str);
 | 
						|
                      return 0;
 | 
						|
                    }
 | 
						|
                  });
 | 
						|
  if (cache_present && thd->query_id == used_query_id)
 | 
						|
  {
 | 
						|
    if (cache_present & GET_SYS_VAR_CACHE_LONG)
 | 
						|
    {
 | 
						|
      null_value= cached_null_value;
 | 
						|
      return cached_llval;
 | 
						|
    } 
 | 
						|
    else if (cache_present & GET_SYS_VAR_CACHE_DOUBLE)
 | 
						|
    {
 | 
						|
      null_value= cached_null_value;
 | 
						|
      cached_llval= (longlong) cached_dval;
 | 
						|
      cache_present|= GET_SYS_VAR_CACHE_LONG;
 | 
						|
      return cached_llval;
 | 
						|
    }
 | 
						|
    else if (cache_present & GET_SYS_VAR_CACHE_STRING)
 | 
						|
    {
 | 
						|
      null_value= cached_null_value;
 | 
						|
      if (!null_value)
 | 
						|
        cached_llval= longlong_from_string_with_check(&cached_strval);
 | 
						|
      else
 | 
						|
        cached_llval= 0;
 | 
						|
      cache_present|= GET_SYS_VAR_CACHE_LONG;
 | 
						|
      return cached_llval;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  cached_llval= var->val_int(&null_value, thd, var_type, &component);
 | 
						|
  cache_present |= GET_SYS_VAR_CACHE_LONG;
 | 
						|
  used_query_id= thd->query_id;
 | 
						|
  cached_null_value= null_value;
 | 
						|
  return cached_llval;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
String* Item_func_get_system_var::val_str(String* str)
 | 
						|
{
 | 
						|
  THD *thd= current_thd;
 | 
						|
 | 
						|
  if (cache_present && thd->query_id == used_query_id)
 | 
						|
  {
 | 
						|
    if (cache_present & GET_SYS_VAR_CACHE_STRING)
 | 
						|
    {
 | 
						|
      null_value= cached_null_value;
 | 
						|
      return null_value ? NULL : &cached_strval;
 | 
						|
    }
 | 
						|
    else if (cache_present & GET_SYS_VAR_CACHE_LONG)
 | 
						|
    {
 | 
						|
      null_value= cached_null_value;
 | 
						|
      if (!null_value)
 | 
						|
        cached_strval.set (cached_llval, collation.collation);
 | 
						|
      cache_present|= GET_SYS_VAR_CACHE_STRING;
 | 
						|
      return null_value ? NULL : &cached_strval;
 | 
						|
    }
 | 
						|
    else if (cache_present & GET_SYS_VAR_CACHE_DOUBLE)
 | 
						|
    {
 | 
						|
      null_value= cached_null_value;
 | 
						|
      if (!null_value)
 | 
						|
        cached_strval.set_real (cached_dval, decimals, collation.collation);
 | 
						|
      cache_present|= GET_SYS_VAR_CACHE_STRING;
 | 
						|
      return null_value ? NULL : &cached_strval;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  str= var->val_str(&cached_strval, thd, var_type, &component);
 | 
						|
  cache_present|= GET_SYS_VAR_CACHE_STRING;
 | 
						|
  used_query_id= thd->query_id;
 | 
						|
  cached_null_value= null_value= !str;
 | 
						|
  return str;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
double Item_func_get_system_var::val_real()
 | 
						|
{
 | 
						|
  THD *thd= current_thd;
 | 
						|
 | 
						|
  if (cache_present && thd->query_id == used_query_id)
 | 
						|
  {
 | 
						|
    if (cache_present & GET_SYS_VAR_CACHE_DOUBLE)
 | 
						|
    {
 | 
						|
      null_value= cached_null_value;
 | 
						|
      return cached_dval;
 | 
						|
    }
 | 
						|
    else if (cache_present & GET_SYS_VAR_CACHE_LONG)
 | 
						|
    {
 | 
						|
      null_value= cached_null_value;
 | 
						|
      cached_dval= (double)cached_llval;
 | 
						|
      cache_present|= GET_SYS_VAR_CACHE_DOUBLE;
 | 
						|
      return cached_dval;
 | 
						|
    }
 | 
						|
    else if (cache_present & GET_SYS_VAR_CACHE_STRING)
 | 
						|
    {
 | 
						|
      null_value= cached_null_value;
 | 
						|
      if (!null_value)
 | 
						|
        cached_dval= double_from_string_with_check(&cached_strval);
 | 
						|
      else
 | 
						|
        cached_dval= 0;
 | 
						|
      cache_present|= GET_SYS_VAR_CACHE_DOUBLE;
 | 
						|
      return cached_dval;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  cached_dval= var->val_real(&null_value, thd, var_type, &component);
 | 
						|
  cache_present |= GET_SYS_VAR_CACHE_DOUBLE;
 | 
						|
  used_query_id= thd->query_id;
 | 
						|
  cached_null_value= null_value;
 | 
						|
  return cached_dval;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool Item_func_get_system_var::eq(const Item *item, bool binary_cmp) const
 | 
						|
{
 | 
						|
  /* Assume we don't have rtti */
 | 
						|
  if (this == item)
 | 
						|
    return 1;					// Same item is same.
 | 
						|
  /* Check if other type is also a get_user_var() object */
 | 
						|
  if (item->type() != FUNC_ITEM ||
 | 
						|
      ((Item_func*) item)->functype() != functype())
 | 
						|
    return 0;
 | 
						|
  Item_func_get_system_var *other=(Item_func_get_system_var*) item;
 | 
						|
  return (var == other->var && var_type == other->var_type);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void Item_func_get_system_var::cleanup()
 | 
						|
{
 | 
						|
  Item_func::cleanup();
 | 
						|
  cache_present= 0;
 | 
						|
  var_type= orig_var_type;
 | 
						|
  cached_strval.free();
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
   @retval
 | 
						|
   0 ok
 | 
						|
   1 OOM error
 | 
						|
*/
 | 
						|
 | 
						|
bool Item_func_match::init_search(THD *thd, bool no_order)
 | 
						|
{
 | 
						|
  DBUG_ENTER("Item_func_match::init_search");
 | 
						|
 | 
						|
  if (!table->file->is_open())
 | 
						|
    DBUG_RETURN(0);
 | 
						|
 | 
						|
  /* Check if init_search() has been called before */
 | 
						|
  if (ft_handler)
 | 
						|
  {
 | 
						|
    if (join_key)
 | 
						|
      table->file->ft_handler= ft_handler;
 | 
						|
    DBUG_RETURN(0);
 | 
						|
  }
 | 
						|
 | 
						|
  if (key == NO_SUCH_KEY)
 | 
						|
  {
 | 
						|
    List<Item> fields;
 | 
						|
    fields.push_back(new (thd->mem_root)
 | 
						|
                     Item_string(thd, " ", 1, cmp_collation.collation),
 | 
						|
                     thd->mem_root);
 | 
						|
    for (uint i= 1; i < arg_count; i++)
 | 
						|
      fields.push_back(args[i]);
 | 
						|
    concat_ws= new (thd->mem_root) Item_func_concat_ws(thd, fields);
 | 
						|
    if (unlikely(thd->is_fatal_error))
 | 
						|
      DBUG_RETURN(1);                           // OOM in new or push_back
 | 
						|
    /*
 | 
						|
      Above function used only to get value and do not need fix_fields for it:
 | 
						|
      Item_string - basic constant
 | 
						|
      fields - fix_fields() was already called for this arguments
 | 
						|
      Item_func_concat_ws - do not need fix_fields() to produce value
 | 
						|
    */
 | 
						|
    concat_ws->quick_fix_field();
 | 
						|
  }
 | 
						|
 | 
						|
  if (master)
 | 
						|
  {
 | 
						|
    join_key= master->join_key= join_key | master->join_key;
 | 
						|
    if (master->init_search(thd, no_order))
 | 
						|
      DBUG_RETURN(1);
 | 
						|
    ft_handler= master->ft_handler;
 | 
						|
    join_key= master->join_key;
 | 
						|
    DBUG_RETURN(0);
 | 
						|
  }
 | 
						|
 | 
						|
  String *ft_tmp= 0;
 | 
						|
 | 
						|
  // MATCH ... AGAINST (NULL) is meaningless, but possible
 | 
						|
  if (!(ft_tmp=key_item()->val_str(&value)))
 | 
						|
  {
 | 
						|
    ft_tmp= &value;
 | 
						|
    value.set("", 0, cmp_collation.collation);
 | 
						|
  }
 | 
						|
 | 
						|
  if (ft_tmp->charset() != cmp_collation.collation)
 | 
						|
  {
 | 
						|
    uint dummy_errors;
 | 
						|
    if (search_value.copy(ft_tmp->ptr(), ft_tmp->length(), ft_tmp->charset(),
 | 
						|
                          cmp_collation.collation, &dummy_errors))
 | 
						|
      DBUG_RETURN(1);
 | 
						|
    ft_tmp= &search_value;
 | 
						|
  }
 | 
						|
 | 
						|
  if (join_key && !no_order)
 | 
						|
    match_flags|=FT_SORTED;
 | 
						|
 | 
						|
  if (key != NO_SUCH_KEY)
 | 
						|
    THD_STAGE_INFO(table->in_use, stage_fulltext_initialization);
 | 
						|
 | 
						|
  ft_handler= table->file->ft_init_ext(match_flags, key, ft_tmp);
 | 
						|
 | 
						|
  if (!ft_handler)
 | 
						|
    DBUG_RETURN(1);
 | 
						|
  if (join_key)
 | 
						|
    table->file->ft_handler=ft_handler;
 | 
						|
 | 
						|
  DBUG_RETURN(0);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool Item_func_match::fix_fields(THD *thd, Item **ref)
 | 
						|
{
 | 
						|
  DBUG_ASSERT(fixed() == 0);
 | 
						|
  Item *UNINIT_VAR(item);                        // Safe as arg_count is > 1
 | 
						|
 | 
						|
  status_var_increment(thd->status_var.feature_fulltext);
 | 
						|
 | 
						|
  set_maybe_null();
 | 
						|
  join_key=0;
 | 
						|
 | 
						|
  /*
 | 
						|
    const_item is assumed in quite a bit of places, so it would be difficult
 | 
						|
    to remove;  If it would ever to be removed, this should include
 | 
						|
    modifications to find_best and auto_close as complement to auto_init code
 | 
						|
    above.
 | 
						|
   */
 | 
						|
  if (Item_func::fix_fields(thd, ref) ||
 | 
						|
      !args[0]->const_during_execution())
 | 
						|
  {
 | 
						|
    my_error(ER_WRONG_ARGUMENTS,MYF(0),"AGAINST");
 | 
						|
    return TRUE;
 | 
						|
  }
 | 
						|
 | 
						|
  bool allows_multi_table_search= true;
 | 
						|
  const_item_cache=0;
 | 
						|
  table= 0;
 | 
						|
  for (uint i=1 ; i < arg_count ; i++)
 | 
						|
  {
 | 
						|
 | 
						|
    item= args[i]->real_item();
 | 
						|
    /*
 | 
						|
      When running in PS mode, some Item_field's can already be replaced
 | 
						|
      to Item_func_conv_charset during PREPARE time. This is possible
 | 
						|
      in case of "MATCH (f1,..,fN) AGAINST (... IN BOOLEAN MODE)"
 | 
						|
      when running without any fulltext indexes and when fields f1..fN
 | 
						|
      have different character sets.
 | 
						|
      So we check for FIELD_ITEM only during prepare time and in non-PS mode,
 | 
						|
      and do not check in PS execute time.
 | 
						|
    */
 | 
						|
    if (!thd->stmt_arena->is_stmt_execute() &&
 | 
						|
        item->type() != Item::FIELD_ITEM)
 | 
						|
    {
 | 
						|
      my_error(ER_WRONG_ARGUMENTS, MYF(0), "MATCH");
 | 
						|
      return TRUE;
 | 
						|
    }
 | 
						|
    /*
 | 
						|
      During the prepare-time execution of fix_fields() of a PS query some
 | 
						|
      Item_fields's could have been already replaced to Item_func_conv_charset
 | 
						|
      (by the call for agg_arg_charsets_for_comparison below()).
 | 
						|
      But agg_arg_charsets_for_comparison() is written in a way that
 | 
						|
      at least *one* of the Item_field's is not replaced.
 | 
						|
      This makes sure that "table" gets initialized during PS execution time.
 | 
						|
    */
 | 
						|
    if (item->type() == Item::FIELD_ITEM)
 | 
						|
      table= ((Item_field *)item)->field->table;
 | 
						|
 | 
						|
    allows_multi_table_search &= allows_search_on_non_indexed_columns(table);
 | 
						|
  }
 | 
						|
 | 
						|
  /*
 | 
						|
    Check that all columns come from the same table.
 | 
						|
    We've already checked that columns in MATCH are fields so
 | 
						|
    PARAM_TABLE_BIT can only appear from AGAINST argument.
 | 
						|
  */
 | 
						|
  if ((used_tables_cache & ~PARAM_TABLE_BIT) != item->used_tables())
 | 
						|
    key=NO_SUCH_KEY;
 | 
						|
 | 
						|
  if (key == NO_SUCH_KEY && !allows_multi_table_search)
 | 
						|
  {
 | 
						|
    my_error(ER_WRONG_ARGUMENTS,MYF(0),"MATCH");
 | 
						|
    return TRUE;
 | 
						|
  }
 | 
						|
  if (!(table->file->ha_table_flags() & HA_CAN_FULLTEXT))
 | 
						|
  {
 | 
						|
    my_error(ER_TABLE_CANT_HANDLE_FT, MYF(0), table->file->table_type());
 | 
						|
    return 1;
 | 
						|
  }
 | 
						|
  table->fulltext_searched=1;
 | 
						|
  return agg_arg_charsets_for_comparison(cmp_collation, args+1, arg_count-1);
 | 
						|
}
 | 
						|
 | 
						|
bool Item_func_match::fix_index()
 | 
						|
{
 | 
						|
  Item_field *item;
 | 
						|
  uint ft_to_key[MAX_KEY], ft_cnt[MAX_KEY], fts=0, keynr;
 | 
						|
  uint max_cnt=0, mkeys=0, i;
 | 
						|
 | 
						|
  /*
 | 
						|
    We will skip execution if the item is not fixed
 | 
						|
    with fix_field
 | 
						|
  */
 | 
						|
  if (!fixed())
 | 
						|
    return false;
 | 
						|
 | 
						|
  if (key == NO_SUCH_KEY)
 | 
						|
    return 0;
 | 
						|
  
 | 
						|
  if (!table) 
 | 
						|
    goto err;
 | 
						|
 | 
						|
  for (keynr=0 ; keynr < table->s->keys ; keynr++)
 | 
						|
  {
 | 
						|
    if ((table->key_info[keynr].flags & HA_FULLTEXT) &&
 | 
						|
        (match_flags & FT_BOOL ?
 | 
						|
         table->keys_in_use_for_query.is_set(keynr) :
 | 
						|
         table->s->usable_indexes(table->in_use).is_set(keynr)))
 | 
						|
    {
 | 
						|
      ft_to_key[fts]=keynr;
 | 
						|
      ft_cnt[fts]=0;
 | 
						|
      fts++;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  if (!fts)
 | 
						|
    goto err;
 | 
						|
 | 
						|
  for (i=1; i < arg_count; i++)
 | 
						|
  {
 | 
						|
    Item *real_item= args[i]->real_item();
 | 
						|
    if (real_item->type() != FIELD_ITEM)
 | 
						|
      goto err;
 | 
						|
    item=(Item_field*)real_item;
 | 
						|
    for (keynr=0 ; keynr < fts ; keynr++)
 | 
						|
    {
 | 
						|
      KEY *ft_key=&table->key_info[ft_to_key[keynr]];
 | 
						|
      uint key_parts=ft_key->user_defined_key_parts;
 | 
						|
 | 
						|
      for (uint part=0 ; part < key_parts ; part++)
 | 
						|
      {
 | 
						|
	if (item->field->eq(ft_key->key_part[part].field))
 | 
						|
	  ft_cnt[keynr]++;
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  for (keynr=0 ; keynr < fts ; keynr++)
 | 
						|
  {
 | 
						|
    if (ft_cnt[keynr] > max_cnt)
 | 
						|
    {
 | 
						|
      mkeys=0;
 | 
						|
      max_cnt=ft_cnt[mkeys]=ft_cnt[keynr];
 | 
						|
      ft_to_key[mkeys]=ft_to_key[keynr];
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
    if (max_cnt && ft_cnt[keynr] == max_cnt)
 | 
						|
    {
 | 
						|
      mkeys++;
 | 
						|
      ft_cnt[mkeys]=ft_cnt[keynr];
 | 
						|
      ft_to_key[mkeys]=ft_to_key[keynr];
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  for (keynr=0 ; keynr <= mkeys ; keynr++)
 | 
						|
  {
 | 
						|
    // partial keys doesn't work
 | 
						|
    if (max_cnt < arg_count-1 ||
 | 
						|
        max_cnt < table->key_info[ft_to_key[keynr]].user_defined_key_parts)
 | 
						|
      continue;
 | 
						|
 | 
						|
    key=ft_to_key[keynr];
 | 
						|
 | 
						|
    return 0;
 | 
						|
  }
 | 
						|
 | 
						|
err:
 | 
						|
  if (allows_search_on_non_indexed_columns(table))
 | 
						|
  {
 | 
						|
    key=NO_SUCH_KEY;
 | 
						|
    return 0;
 | 
						|
  }
 | 
						|
  my_message(ER_FT_MATCHING_KEY_NOT_FOUND,
 | 
						|
             ER(ER_FT_MATCHING_KEY_NOT_FOUND), MYF(0));
 | 
						|
  return 1;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool Item_func_match::eq(const Item *item, bool binary_cmp) const
 | 
						|
{
 | 
						|
  if (item->type() != FUNC_ITEM ||
 | 
						|
      ((Item_func*)item)->functype() != FT_FUNC ||
 | 
						|
      match_flags != ((Item_func_match*)item)->match_flags)
 | 
						|
    return 0;
 | 
						|
 | 
						|
  Item_func_match *ifm=(Item_func_match*) item;
 | 
						|
 | 
						|
  if (key == ifm->key && table == ifm->table &&
 | 
						|
      key_item()->eq(ifm->key_item(), binary_cmp))
 | 
						|
    return 1;
 | 
						|
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
double Item_func_match::val_real()
 | 
						|
{
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
  DBUG_ENTER("Item_func_match::val");
 | 
						|
  if (ft_handler == NULL)
 | 
						|
    DBUG_RETURN(-1.0);
 | 
						|
 | 
						|
  if (key != NO_SUCH_KEY && table->null_row) /* NULL row from an outer join */
 | 
						|
    DBUG_RETURN(0.0);
 | 
						|
 | 
						|
  if (join_key)
 | 
						|
  {
 | 
						|
    if (table->file->ft_handler)
 | 
						|
      DBUG_RETURN(ft_handler->please->get_relevance(ft_handler));
 | 
						|
    join_key=0;
 | 
						|
  }
 | 
						|
 | 
						|
  if (key == NO_SUCH_KEY)
 | 
						|
  {
 | 
						|
    String *a= concat_ws->val_str(&value);
 | 
						|
    if ((null_value= (a == 0)) || !a->length())
 | 
						|
      DBUG_RETURN(0);
 | 
						|
    DBUG_RETURN(ft_handler->please->find_relevance(ft_handler,
 | 
						|
				      (uchar *)a->ptr(), a->length()));
 | 
						|
  }
 | 
						|
  DBUG_RETURN(ft_handler->please->find_relevance(ft_handler,
 | 
						|
                                                 table->record[0], 0));
 | 
						|
}
 | 
						|
 | 
						|
void Item_func_match::print(String *str, enum_query_type query_type)
 | 
						|
{
 | 
						|
  str->append(STRING_WITH_LEN("(match "));
 | 
						|
  print_args(str, 1, query_type);
 | 
						|
  str->append(STRING_WITH_LEN(" against ("));
 | 
						|
  args[0]->print(str, query_type);
 | 
						|
  if (match_flags & FT_BOOL)
 | 
						|
    str->append(STRING_WITH_LEN(" in boolean mode"));
 | 
						|
  else if (match_flags & FT_EXPAND)
 | 
						|
    str->append(STRING_WITH_LEN(" with query expansion"));
 | 
						|
  str->append(STRING_WITH_LEN("))"));
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
class Func_handler_bit_xor_int_to_ulonglong:
 | 
						|
        public Item_handled_func::Handler_ulonglong
 | 
						|
{
 | 
						|
public:
 | 
						|
  Longlong_null to_longlong_null(Item_handled_func *item) const override
 | 
						|
  {
 | 
						|
    DBUG_ASSERT(item->fixed());
 | 
						|
    return item->arguments()[0]->to_longlong_null() ^
 | 
						|
           item->arguments()[1]->to_longlong_null();
 | 
						|
  }
 | 
						|
};
 | 
						|
 | 
						|
 | 
						|
class Func_handler_bit_xor_dec_to_ulonglong:
 | 
						|
        public Item_handled_func::Handler_ulonglong
 | 
						|
{
 | 
						|
public:
 | 
						|
  Longlong_null to_longlong_null(Item_handled_func *item) const override
 | 
						|
  {
 | 
						|
    DBUG_ASSERT(item->fixed());
 | 
						|
    return VDec(item->arguments()[0]).to_xlonglong_null() ^
 | 
						|
           VDec(item->arguments()[1]).to_xlonglong_null();
 | 
						|
  }
 | 
						|
};
 | 
						|
 | 
						|
 | 
						|
bool Item_func_bit_xor::fix_length_and_dec(THD *thd)
 | 
						|
{
 | 
						|
  static const Func_handler_bit_xor_int_to_ulonglong ha_int_to_ull;
 | 
						|
  static const Func_handler_bit_xor_dec_to_ulonglong ha_dec_to_ull;
 | 
						|
  return fix_length_and_dec_op2_std(&ha_int_to_ull, &ha_dec_to_ull);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/***************************************************************************
 | 
						|
  System variables
 | 
						|
****************************************************************************/
 | 
						|
 | 
						|
/**
 | 
						|
  Return value of an system variable base[.name] as a constant item.
 | 
						|
 | 
						|
  @param thd			Thread handler
 | 
						|
  @param var_type		global / session
 | 
						|
  @param name		        Name of base or system variable
 | 
						|
  @param component		Component.
 | 
						|
 | 
						|
  @note
 | 
						|
    If component.str = 0 then the variable name is in 'name'
 | 
						|
 | 
						|
  @return
 | 
						|
    - 0  : error
 | 
						|
    - #  : constant item
 | 
						|
*/
 | 
						|
 | 
						|
 | 
						|
Item *get_system_var(THD *thd, enum_var_type var_type,
 | 
						|
                     const LEX_CSTRING *name,
 | 
						|
		     const LEX_CSTRING *component)
 | 
						|
{
 | 
						|
  sys_var *var;
 | 
						|
  LEX_CSTRING base_name, component_name;
 | 
						|
 | 
						|
  if (component->str)
 | 
						|
  {
 | 
						|
    base_name= *component;
 | 
						|
    component_name= *name;
 | 
						|
  }
 | 
						|
  else
 | 
						|
  {
 | 
						|
    base_name= *name;
 | 
						|
    component_name= *component;			// Empty string
 | 
						|
  }
 | 
						|
 | 
						|
  if (!(var= find_sys_var(thd, base_name.str, base_name.length)))
 | 
						|
    return 0;
 | 
						|
  if (component->str)
 | 
						|
  {
 | 
						|
    if (!var->is_struct())
 | 
						|
    {
 | 
						|
      my_error(ER_VARIABLE_IS_NOT_STRUCT, MYF(0), base_name.str);
 | 
						|
      return 0;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  thd->lex->uncacheable(UNCACHEABLE_SIDEEFFECT);
 | 
						|
 | 
						|
  set_if_smaller(component_name.length, MAX_SYS_VAR_LENGTH);
 | 
						|
 | 
						|
  return new (thd->mem_root) Item_func_get_system_var(thd, var, var_type,
 | 
						|
                                                      &component_name,
 | 
						|
                                                      NULL, 0);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
longlong Item_func_row_count::val_int()
 | 
						|
{
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
  THD *thd= current_thd;
 | 
						|
 | 
						|
  return thd->get_row_count_func();
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
Item_func_sp::Item_func_sp(THD *thd, Name_resolution_context *context_arg,
 | 
						|
                           sp_name *name, const Sp_handler *sph):
 | 
						|
  Item_func(thd), Item_sp(thd, context_arg, name), m_handler(sph)
 | 
						|
{
 | 
						|
  set_maybe_null();
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
Item_func_sp::Item_func_sp(THD *thd, Name_resolution_context *context_arg,
 | 
						|
                           sp_name *name_arg, const Sp_handler *sph,
 | 
						|
                           List<Item> &list):
 | 
						|
  Item_func(thd, list), Item_sp(thd, context_arg, name_arg), m_handler(sph)
 | 
						|
{
 | 
						|
  set_maybe_null();
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void
 | 
						|
Item_func_sp::cleanup()
 | 
						|
{
 | 
						|
  Item_sp::cleanup();
 | 
						|
  Item_func::cleanup();
 | 
						|
}
 | 
						|
 | 
						|
LEX_CSTRING
 | 
						|
Item_func_sp::func_name_cstring() const
 | 
						|
{
 | 
						|
  return Item_sp::func_name_cstring(current_thd,
 | 
						|
                                    m_handler == &sp_handler_package_function);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void my_missing_function_error(const LEX_CSTRING &token, const char *func_name)
 | 
						|
{
 | 
						|
  if (token.length && is_lex_native_function (&token))
 | 
						|
    my_error(ER_FUNC_INEXISTENT_NAME_COLLISION, MYF(0), func_name);
 | 
						|
  else
 | 
						|
    my_error(ER_SP_DOES_NOT_EXIST, MYF(0), "FUNCTION", func_name);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  @note
 | 
						|
  Deterministic stored procedures are considered inexpensive.
 | 
						|
  Consequently such procedures may be evaluated during optimization,
 | 
						|
  if they are constant (checked by the optimizer).
 | 
						|
*/
 | 
						|
 | 
						|
bool Item_func_sp::is_expensive()
 | 
						|
{
 | 
						|
  return !m_sp->detistic() ||
 | 
						|
          current_thd->locked_tables_mode < LTM_LOCK_TABLES;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  @brief Initialize local members with values from the Field interface.
 | 
						|
 | 
						|
  @note called from Item::fix_fields.
 | 
						|
*/
 | 
						|
 | 
						|
bool Item_func_sp::fix_length_and_dec(THD *thd)
 | 
						|
{
 | 
						|
  DBUG_ENTER("Item_func_sp::fix_length_and_dec");
 | 
						|
 | 
						|
  DBUG_ASSERT(sp_result_field);
 | 
						|
  Type_std_attributes::set(sp_result_field->type_std_attributes());
 | 
						|
  // There is a bug in the line below. See MDEV-11292 for details.
 | 
						|
  collation.derivation= DERIVATION_COERCIBLE;
 | 
						|
  set_maybe_null();
 | 
						|
 | 
						|
  DBUG_RETURN(FALSE);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool
 | 
						|
Item_func_sp::execute()
 | 
						|
{
 | 
						|
  /* Execute function and store the return value in the field. */
 | 
						|
  return Item_sp::execute(current_thd, &null_value, args, arg_count);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void
 | 
						|
Item_func_sp::make_send_field(THD *thd, Send_field *tmp_field)
 | 
						|
{
 | 
						|
  DBUG_ENTER("Item_func_sp::make_send_field");
 | 
						|
  DBUG_ASSERT(sp_result_field);
 | 
						|
  sp_result_field->make_send_field(tmp_field);
 | 
						|
  if (name.str)
 | 
						|
  {
 | 
						|
    DBUG_ASSERT(name.length == strlen(name.str));
 | 
						|
    tmp_field->col_name= name;
 | 
						|
  }
 | 
						|
  DBUG_VOID_RETURN;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
const Type_handler *Item_func_sp::type_handler() const
 | 
						|
{
 | 
						|
  DBUG_ENTER("Item_func_sp::type_handler");
 | 
						|
  DBUG_PRINT("info", ("m_sp = %p", (void *) m_sp));
 | 
						|
  DBUG_ASSERT(sp_result_field);
 | 
						|
  // This converts ENUM/SET to STRING
 | 
						|
  const Type_handler *handler= sp_result_field->type_handler();
 | 
						|
  DBUG_RETURN(handler->type_handler_for_item_field());
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
longlong Item_func_found_rows::val_int()
 | 
						|
{
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
  return current_thd->found_rows();
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
longlong Item_func_oracle_sql_rowcount::val_int()
 | 
						|
{
 | 
						|
  DBUG_ASSERT(fixed());
 | 
						|
  THD *thd= current_thd;
 | 
						|
  /*
 | 
						|
    In case when a query like this:
 | 
						|
      INSERT a INTO @va FROM t1;
 | 
						|
    returns multiple rows, SQL%ROWCOUNT should report 1 rather than -1.
 | 
						|
  */
 | 
						|
  longlong rows= thd->get_row_count_func();
 | 
						|
  return rows != -1 ? rows :                   // ROW_COUNT()
 | 
						|
                      thd->found_rows();       // FOUND_ROWS()
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
longlong Item_func_sqlcode::val_int()
 | 
						|
{
 | 
						|
  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++))
 | 
						|
    return err->get_sql_errno();
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool
 | 
						|
Item_func_sp::fix_fields(THD *thd, Item **ref)
 | 
						|
{
 | 
						|
  bool res;
 | 
						|
  DBUG_ENTER("Item_func_sp::fix_fields");
 | 
						|
  DBUG_ASSERT(fixed() == 0);
 | 
						|
  sp_head *sp= m_handler->sp_find_routine(thd, m_name, true);
 | 
						|
 | 
						|
  /* 
 | 
						|
    Checking privileges to execute the function while creating view and
 | 
						|
    executing the function of select.
 | 
						|
   */
 | 
						|
  if (!(thd->lex->context_analysis_only & CONTEXT_ANALYSIS_ONLY_VIEW) ||
 | 
						|
      (thd->lex->sql_command == SQLCOM_CREATE_VIEW))
 | 
						|
  {
 | 
						|
    Security_context *save_security_ctx= thd->security_ctx;
 | 
						|
    if (context && context->security_ctx)
 | 
						|
      thd->security_ctx= context->security_ctx;
 | 
						|
 | 
						|
    /*
 | 
						|
      If the routine is not found, let's still check EXECUTE_ACL to decide
 | 
						|
      whether to return "Access denied" or "Routine does not exist".
 | 
						|
    */
 | 
						|
    res= sp ? sp->check_execute_access(thd) :
 | 
						|
              check_routine_access(thd, EXECUTE_ACL, &m_name->m_db,
 | 
						|
                                   &m_name->m_name,
 | 
						|
                                   &sp_handler_function, false);
 | 
						|
    thd->security_ctx= save_security_ctx;
 | 
						|
 | 
						|
    if (res)
 | 
						|
    {
 | 
						|
      process_error(thd);
 | 
						|
      DBUG_RETURN(res);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
 | 
						|
  /* Custom aggregates are transformed into an Item_sum_sp. We can not do this
 | 
						|
     earlier as we have no way of knowing what kind of Item we should create
 | 
						|
     when parsing the query.
 | 
						|
 | 
						|
     TODO(cvicentiu): See if this limitation can be lifted.
 | 
						|
  */
 | 
						|
 | 
						|
  DBUG_ASSERT(m_sp == NULL);
 | 
						|
  if (!(m_sp= sp))
 | 
						|
  {
 | 
						|
    my_missing_function_error(m_name->m_name, ErrConvDQName(m_name).ptr());
 | 
						|
    process_error(thd);
 | 
						|
    DBUG_RETURN(TRUE);
 | 
						|
  }
 | 
						|
 | 
						|
  /*
 | 
						|
    We must call init_result_field before Item_func::fix_fields()
 | 
						|
    to make m_sp and result_field members available to fix_length_and_dec(),
 | 
						|
    which is called from Item_func::fix_fields().
 | 
						|
  */
 | 
						|
  res= init_result_field(thd, max_length, maybe_null(), &null_value, &name);
 | 
						|
 | 
						|
  if (res)
 | 
						|
    DBUG_RETURN(TRUE);
 | 
						|
 | 
						|
  if (m_sp->agg_type() == GROUP_AGGREGATE)
 | 
						|
  {
 | 
						|
    Item_sum_sp *item_sp;
 | 
						|
    Query_arena *arena, backup;
 | 
						|
    arena= thd->activate_stmt_arena_if_needed(&backup);
 | 
						|
 | 
						|
    if (arg_count)
 | 
						|
    {
 | 
						|
      List<Item> list;
 | 
						|
      for (uint i= 0; i < arg_count; i++)
 | 
						|
        list.push_back(args[i]);
 | 
						|
      item_sp= new (thd->mem_root) Item_sum_sp(thd, context, m_name, sp, list);
 | 
						|
    }
 | 
						|
    else
 | 
						|
      item_sp= new (thd->mem_root) Item_sum_sp(thd, context, m_name, sp);
 | 
						|
 | 
						|
    if (arena)
 | 
						|
      thd->restore_active_arena(arena, &backup);
 | 
						|
    if (!item_sp)
 | 
						|
      DBUG_RETURN(TRUE);
 | 
						|
    *ref= item_sp;
 | 
						|
    item_sp->name= name;
 | 
						|
    bool err= item_sp->fix_fields(thd, ref);
 | 
						|
    if (err)
 | 
						|
      DBUG_RETURN(TRUE);
 | 
						|
 | 
						|
    DBUG_RETURN(FALSE);
 | 
						|
  }
 | 
						|
 | 
						|
  res= Item_func::fix_fields(thd, ref);
 | 
						|
 | 
						|
  if (res)
 | 
						|
    DBUG_RETURN(TRUE);
 | 
						|
 | 
						|
  if (thd->lex->is_view_context_analysis())
 | 
						|
  {
 | 
						|
    /*
 | 
						|
      Here we check privileges of the stored routine only during view
 | 
						|
      creation, in order to validate the view.  A runtime check is
 | 
						|
      performed in Item_func_sp::execute(), and this method is not
 | 
						|
      called during context analysis.  Notice, that during view
 | 
						|
      creation we do not infer into stored routine bodies and do not
 | 
						|
      check privileges of its statements, which would probably be a
 | 
						|
      good idea especially if the view has SQL SECURITY DEFINER and
 | 
						|
      the used stored procedure has SQL SECURITY DEFINER.
 | 
						|
    */
 | 
						|
    res= sp_check_access(thd);
 | 
						|
#ifndef NO_EMBEDDED_ACCESS_CHECKS
 | 
						|
    /*
 | 
						|
      Try to set and restore the security context to see whether it's valid
 | 
						|
    */
 | 
						|
    Security_context *save_secutiry_ctx;
 | 
						|
    res= set_routine_security_ctx(thd, m_sp, &save_secutiry_ctx);
 | 
						|
    if (!res)
 | 
						|
      m_sp->m_security_ctx.restore_security_context(thd, save_secutiry_ctx);
 | 
						|
    
 | 
						|
#endif /* ! NO_EMBEDDED_ACCESS_CHECKS */
 | 
						|
  }
 | 
						|
 | 
						|
  if (!m_sp->detistic())
 | 
						|
  {
 | 
						|
    used_tables_cache |= RAND_TABLE_BIT;
 | 
						|
    const_item_cache= FALSE;
 | 
						|
  }
 | 
						|
 | 
						|
  DBUG_RETURN(res);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void Item_func_sp::update_used_tables()
 | 
						|
{
 | 
						|
  Item_func::update_used_tables();
 | 
						|
 | 
						|
  if (!m_sp->detistic())
 | 
						|
  {
 | 
						|
    used_tables_cache |= RAND_TABLE_BIT;
 | 
						|
    const_item_cache= FALSE;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
bool Item_func_sp::check_vcol_func_processor(void *arg)
 | 
						|
{
 | 
						|
  return mark_unsupported_function(func_name(), "()", arg, VCOL_IMPOSSIBLE);
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
  uuid_short handling.
 | 
						|
 | 
						|
  The short uuid is defined as a longlong that contains the following bytes:
 | 
						|
 | 
						|
  Bytes  Comment
 | 
						|
  1      Server_id & 255
 | 
						|
  4      Startup time of server in seconds
 | 
						|
  3      Incrementor
 | 
						|
 | 
						|
  This means that an uuid is guaranteed to be unique
 | 
						|
  even in a replication environment if the following holds:
 | 
						|
 | 
						|
  - The last byte of the server id is unique
 | 
						|
  - If you between two shutdown of the server don't get more than
 | 
						|
    an average of 2^24 = 16M calls to uuid_short() per second.
 | 
						|
*/
 | 
						|
 | 
						|
ulonglong uuid_value;
 | 
						|
 | 
						|
void uuid_short_init()
 | 
						|
{
 | 
						|
  uuid_value= ((((ulonglong) global_system_variables.server_id) << 56) +
 | 
						|
               (((ulonglong) server_start_time) << 24));
 | 
						|
}
 | 
						|
 | 
						|
ulonglong server_uuid_value()
 | 
						|
{
 | 
						|
  ulonglong val;
 | 
						|
  mysql_mutex_lock(&LOCK_short_uuid_generator);
 | 
						|
  val= uuid_value++;
 | 
						|
  mysql_mutex_unlock(&LOCK_short_uuid_generator);
 | 
						|
  return val;
 | 
						|
}
 | 
						|
 | 
						|
longlong Item_func_uuid_short::val_int()
 | 
						|
{
 | 
						|
  return (longlong) server_uuid_value();
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Last_value - return last argument.
 | 
						|
*/
 | 
						|
 | 
						|
void Item_func_last_value::evaluate_sideeffects()
 | 
						|
{
 | 
						|
  DBUG_ASSERT(fixed() && arg_count > 0);
 | 
						|
  for (uint i= 0; i < arg_count-1 ; i++)
 | 
						|
    args[i]->val_int();
 | 
						|
}
 | 
						|
 | 
						|
String *Item_func_last_value::val_str(String *str)
 | 
						|
{
 | 
						|
  String *tmp;
 | 
						|
  evaluate_sideeffects();
 | 
						|
  tmp= last_value->val_str(str);
 | 
						|
  null_value= last_value->null_value;
 | 
						|
  return tmp;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool Item_func_last_value::val_native(THD *thd, Native *to)
 | 
						|
{
 | 
						|
  evaluate_sideeffects();
 | 
						|
  return val_native_from_item(thd, last_value, to);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
longlong Item_func_last_value::val_int()
 | 
						|
{
 | 
						|
  longlong tmp;
 | 
						|
  evaluate_sideeffects();
 | 
						|
  tmp= last_value->val_int();
 | 
						|
  null_value= last_value->null_value;
 | 
						|
  return tmp;
 | 
						|
}
 | 
						|
 | 
						|
double Item_func_last_value::val_real()
 | 
						|
{
 | 
						|
  double tmp;
 | 
						|
  evaluate_sideeffects();
 | 
						|
  tmp= last_value->val_real();
 | 
						|
  null_value= last_value->null_value;
 | 
						|
  return tmp;
 | 
						|
}
 | 
						|
 | 
						|
my_decimal *Item_func_last_value::val_decimal(my_decimal *decimal_value)
 | 
						|
{
 | 
						|
  my_decimal *tmp;
 | 
						|
  evaluate_sideeffects();
 | 
						|
  tmp= last_value->val_decimal(decimal_value);
 | 
						|
  null_value= last_value->null_value;
 | 
						|
  return tmp;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool Item_func_last_value::get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate)
 | 
						|
{
 | 
						|
  evaluate_sideeffects();
 | 
						|
  bool tmp= last_value->get_date(thd, ltime, fuzzydate);
 | 
						|
  null_value= last_value->null_value;
 | 
						|
  return tmp;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool Item_func_last_value::fix_length_and_dec(THD *thd)
 | 
						|
{
 | 
						|
  last_value=          args[arg_count -1];
 | 
						|
  Type_std_attributes::set(last_value);
 | 
						|
  set_maybe_null(last_value->maybe_null());
 | 
						|
  return FALSE;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void Cursor_ref::print_func(String *str, const LEX_CSTRING &func_name)
 | 
						|
{
 | 
						|
  append_identifier(current_thd, str, &m_cursor_name);
 | 
						|
  str->append(func_name);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
sp_cursor *Cursor_ref::get_open_cursor_or_error()
 | 
						|
{
 | 
						|
  THD *thd= current_thd;
 | 
						|
  sp_cursor *c= thd->spcont->get_cursor(m_cursor_offset);
 | 
						|
  DBUG_ASSERT(c);
 | 
						|
  if (!c/*safety*/ || !c->is_open())
 | 
						|
  {
 | 
						|
    my_message(ER_SP_CURSOR_NOT_OPEN, ER_THD(thd, ER_SP_CURSOR_NOT_OPEN),
 | 
						|
               MYF(0));
 | 
						|
    return NULL;
 | 
						|
  }
 | 
						|
  return c;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool Item_func_cursor_isopen::val_bool()
 | 
						|
{
 | 
						|
  sp_cursor *c= current_thd->spcont->get_cursor(m_cursor_offset);
 | 
						|
  DBUG_ASSERT(c != NULL);
 | 
						|
  return c ? c->is_open() : 0;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool Item_func_cursor_found::val_bool()
 | 
						|
{
 | 
						|
  sp_cursor *c= get_open_cursor_or_error();
 | 
						|
  return !(null_value= (!c || c->fetch_count() == 0)) && c->found();
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool Item_func_cursor_notfound::val_bool()
 | 
						|
{
 | 
						|
  sp_cursor *c= get_open_cursor_or_error();
 | 
						|
  return !(null_value= (!c || c->fetch_count() == 0)) && !c->found();
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
longlong Item_func_cursor_rowcount::val_int()
 | 
						|
{
 | 
						|
  sp_cursor *c= get_open_cursor_or_error();
 | 
						|
  return !(null_value= !c) ? c->row_count() : 0;
 | 
						|
}
 | 
						|
 | 
						|
/*****************************************************************************
 | 
						|
  SEQUENCE functions
 | 
						|
*****************************************************************************/
 | 
						|
bool Item_func_nextval::check_access_and_fix_fields(THD *thd, Item **ref,
 | 
						|
                                                    privilege_t want_access)
 | 
						|
{
 | 
						|
  table_list->sequence= false;
 | 
						|
  bool error= check_single_table_access(thd, want_access, table_list, false);
 | 
						|
  table_list->sequence= true;
 | 
						|
  if (error && table_list->belong_to_view)
 | 
						|
    table_list->replace_view_error_with_generic(thd);
 | 
						|
  return error || Item_longlong_func::fix_fields(thd, ref);
 | 
						|
}
 | 
						|
 | 
						|
longlong Item_func_nextval::val_int()
 | 
						|
{
 | 
						|
  longlong value;
 | 
						|
  int error;
 | 
						|
  const char *key;
 | 
						|
  uint length= get_table_def_key(table_list, &key);
 | 
						|
  THD *thd;
 | 
						|
  SEQUENCE_LAST_VALUE *entry;
 | 
						|
  char buff[80];
 | 
						|
  String key_buff(buff,sizeof(buff), &my_charset_bin);
 | 
						|
  DBUG_ENTER("Item_func_nextval::val_int");
 | 
						|
  update_table();
 | 
						|
  DBUG_ASSERT(table && table->s->sequence);
 | 
						|
  thd= table->in_use;
 | 
						|
 | 
						|
  if (thd->count_cuted_fields == CHECK_FIELD_EXPRESSION)
 | 
						|
  {
 | 
						|
    /* Alter table checking if function works */
 | 
						|
    null_value= 0;
 | 
						|
    DBUG_RETURN(0);
 | 
						|
  }
 | 
						|
 | 
						|
  if (table->s->tmp_table != NO_TMP_TABLE)
 | 
						|
  {
 | 
						|
    /*
 | 
						|
      Temporary tables has an extra \0 at end to distinguish it from
 | 
						|
      normal tables
 | 
						|
    */
 | 
						|
    key_buff.copy(key, length, &my_charset_bin);
 | 
						|
    key_buff.append((char) 0);
 | 
						|
    key= key_buff.ptr();
 | 
						|
    length++;
 | 
						|
  }
 | 
						|
 | 
						|
  if (!(entry= ((SEQUENCE_LAST_VALUE*)
 | 
						|
                my_hash_search(&thd->sequences, (uchar*) key, length))))
 | 
						|
  {
 | 
						|
    if (!(key= (char*) my_memdup(PSI_INSTRUMENT_ME, key, length, MYF(MY_WME))) ||
 | 
						|
        !(entry= new SEQUENCE_LAST_VALUE((uchar*) key, length)))
 | 
						|
    {
 | 
						|
      /* EOM, error given */
 | 
						|
      my_free((char*) key);
 | 
						|
      delete entry;
 | 
						|
      null_value= 1;
 | 
						|
      DBUG_RETURN(0);
 | 
						|
    }
 | 
						|
    if (my_hash_insert(&thd->sequences, (uchar*) entry))
 | 
						|
    {
 | 
						|
      /* EOM, error given */
 | 
						|
      delete entry;
 | 
						|
      null_value= 1;
 | 
						|
      DBUG_RETURN(0);
 | 
						|
    }
 | 
						|
  }
 | 
						|
  entry->null_value= null_value= 0;
 | 
						|
  value= table->s->sequence->next_value(table, 0, &error);
 | 
						|
  entry->value= value;
 | 
						|
  entry->set_version(table);
 | 
						|
 | 
						|
  if (unlikely(error))                          // Warning already printed
 | 
						|
    entry->null_value= null_value= 1;           // For not strict mode
 | 
						|
  DBUG_RETURN(value);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool Item_func_nextval::print_table_list_identifier(THD *thd, String *to) const
 | 
						|
{
 | 
						|
  if (table_list->db.str && table_list->db.str[0])
 | 
						|
  {
 | 
						|
    if (append_identifier_opt_casedn(thd, to, table_list->db,
 | 
						|
                                     lower_case_table_names) ||
 | 
						|
        to->append('.'))
 | 
						|
      return true;
 | 
						|
  }
 | 
						|
  return append_identifier_opt_casedn(thd, to, table_list->table_name,
 | 
						|
                                      lower_case_table_names);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* Print for nextval and lastval */
 | 
						|
 | 
						|
void Item_func_nextval::print(String *str, enum_query_type query_type)
 | 
						|
{
 | 
						|
  THD *thd= current_thd;                        // Don't trust 'table'
 | 
						|
 | 
						|
  str->append(func_name_cstring());
 | 
						|
  str->append('(');
 | 
						|
 | 
						|
  /*
 | 
						|
    for next_val we assume that table_list has been updated to contain
 | 
						|
    the current db.
 | 
						|
  */
 | 
						|
  print_table_list_identifier(thd, str);
 | 
						|
 | 
						|
  str->append(')');
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* Return last used value for sequence or NULL if sequence hasn't been used */
 | 
						|
 | 
						|
longlong Item_func_lastval::val_int()
 | 
						|
{
 | 
						|
  const char *key;
 | 
						|
  SEQUENCE_LAST_VALUE *entry;
 | 
						|
  uint length= get_table_def_key(table_list, &key);
 | 
						|
  THD *thd;
 | 
						|
  char buff[80];
 | 
						|
  String key_buff(buff,sizeof(buff), &my_charset_bin);
 | 
						|
  DBUG_ENTER("Item_func_lastval::val_int");
 | 
						|
  update_table();
 | 
						|
  thd= table->in_use;
 | 
						|
 | 
						|
  if (table->s->tmp_table != NO_TMP_TABLE)
 | 
						|
  {
 | 
						|
    /*
 | 
						|
      Temporary tables has an extra \0 at end to distinguish it from
 | 
						|
      normal tables
 | 
						|
    */
 | 
						|
    key_buff.copy(key, length, &my_charset_bin);
 | 
						|
    key_buff.append((char) 0);
 | 
						|
    key= key_buff.ptr();
 | 
						|
    length++;
 | 
						|
  }
 | 
						|
 | 
						|
  if (!(entry= ((SEQUENCE_LAST_VALUE*)
 | 
						|
                my_hash_search(&thd->sequences, (uchar*) key, length))))
 | 
						|
  {
 | 
						|
    /* Sequence not used */
 | 
						|
    null_value= 1;
 | 
						|
    DBUG_RETURN(0);
 | 
						|
  }
 | 
						|
  if (entry->check_version(table))
 | 
						|
  {
 | 
						|
    /* Table droped and re-created, remove current version */
 | 
						|
    my_hash_delete(&thd->sequences, (uchar*) entry);
 | 
						|
    null_value= 1;
 | 
						|
    DBUG_RETURN(0);
 | 
						|
  }    
 | 
						|
 | 
						|
  null_value= entry->null_value;
 | 
						|
  DBUG_RETURN(entry->value);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
  Sets next value to be returned from sequences
 | 
						|
 | 
						|
  SELECT setval(foo, 42, 0);           Next nextval will return 43
 | 
						|
  SELECT setval(foo, 42, 0, true);     Same as above
 | 
						|
  SELECT setval(foo, 42, 0, false);    Next nextval will return 42
 | 
						|
*/
 | 
						|
 | 
						|
longlong Item_func_setval::val_int()
 | 
						|
{
 | 
						|
  longlong value;
 | 
						|
  int error;
 | 
						|
  THD *thd;
 | 
						|
  DBUG_ENTER("Item_func_setval::val_int");
 | 
						|
 | 
						|
  update_table();
 | 
						|
  DBUG_ASSERT(table && table->s->sequence);
 | 
						|
  thd= table->in_use;
 | 
						|
 | 
						|
  if (unlikely(thd->count_cuted_fields == CHECK_FIELD_EXPRESSION))
 | 
						|
  {
 | 
						|
    /* Alter table checking if function works */
 | 
						|
    null_value= 0;
 | 
						|
    DBUG_RETURN(0);
 | 
						|
  }
 | 
						|
 | 
						|
  value= nextval;
 | 
						|
  error= table->s->sequence->set_value(table, nextval, round, is_used);
 | 
						|
  if (unlikely(error))
 | 
						|
  {
 | 
						|
    null_value= 1;
 | 
						|
    value= 0;
 | 
						|
  }
 | 
						|
  DBUG_RETURN(value);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* Print for setval */
 | 
						|
 | 
						|
void Item_func_setval::print(String *str, enum_query_type query_type)
 | 
						|
{
 | 
						|
  THD *thd= current_thd;                        // Don't trust 'table'
 | 
						|
 | 
						|
  str->append(func_name_cstring());
 | 
						|
  str->append('(');
 | 
						|
 | 
						|
  /*
 | 
						|
    for next_val we assume that table_list has been updated to contain
 | 
						|
    the current db.
 | 
						|
  */
 | 
						|
  print_table_list_identifier(thd, str);
 | 
						|
 | 
						|
  str->append(',');
 | 
						|
  str->append_longlong(nextval);
 | 
						|
  str->append(',');
 | 
						|
  str->append_longlong(is_used);
 | 
						|
  str->append(',');
 | 
						|
  str->append_ulonglong(round);
 | 
						|
  str->append(')');
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
  Return how many row combinations has accepted so far + 1
 | 
						|
 | 
						|
  The + 1 is to ensure that, for example, 'WHERE ROWNUM <=1' returns one row
 | 
						|
*/
 | 
						|
 | 
						|
longlong Item_func_rownum::val_int()
 | 
						|
{
 | 
						|
  if (!accepted_rows)
 | 
						|
  {
 | 
						|
    /*
 | 
						|
      Rownum is not properly set up. Probably used in wrong context when
 | 
						|
      it should not be used. In this case returning 0 is probably the best
 | 
						|
      solution.
 | 
						|
    */
 | 
						|
    return 0;
 | 
						|
  }
 | 
						|
  return (longlong) *accepted_rows+1;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
Item_func_rownum::Item_func_rownum(THD *thd):
 | 
						|
  Item_longlong_func(thd),accepted_rows(0)
 | 
						|
{
 | 
						|
  /*
 | 
						|
    Remember the select context.
 | 
						|
    Add the function to the list fix_after_optimize in the select context
 | 
						|
    so that we can easily initializef all rownum functions with the pointers
 | 
						|
    to the row counters.
 | 
						|
  */
 | 
						|
  select= thd->lex->current_select;
 | 
						|
  select->fix_after_optimize.push_back(this, thd->mem_root);
 | 
						|
 | 
						|
  /*
 | 
						|
    Mark that query is using rownum() and ensure that this select is
 | 
						|
    not merged with other selects
 | 
						|
  */
 | 
						|
  select->with_rownum= 1;
 | 
						|
  thd->lex->with_rownum= 1;
 | 
						|
  thd->lex->uncacheable(UNCACHEABLE_RAND);
 | 
						|
  with_flags= with_flags | item_with_t::ROWNUM_FUNC;
 | 
						|
 | 
						|
  /* If this command changes data, mark it as unsafe for statement logging */
 | 
						|
  if (sql_command_flags[thd->lex->sql_command] &
 | 
						|
      (CF_UPDATES_DATA | CF_DELETES_DATA))
 | 
						|
    thd->lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_SYSTEM_FUNCTION);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
  Store a reference to the variable that contains number of accepted rows
 | 
						|
*/
 | 
						|
 | 
						|
void Item_func_rownum::fix_after_optimize(THD *thd)
 | 
						|
{
 | 
						|
  accepted_rows= &select->join->accepted_rows;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
  Inform all ROWNUM() function where the number of rows are stored
 | 
						|
*/
 | 
						|
 | 
						|
void fix_rownum_pointers(THD *thd, SELECT_LEX *select_lex, ha_rows *ptr)
 | 
						|
{
 | 
						|
  List_iterator<Item> li(select_lex->fix_after_optimize);
 | 
						|
  while (Item *item= li++)
 | 
						|
  {
 | 
						|
    if (item->type() == Item::FUNC_ITEM &&
 | 
						|
        ((Item_func*) item)->functype() == Item_func::ROWNUM_FUNC)
 | 
						|
      ((Item_func_rownum*) item)->store_pointer_to_row_counter(ptr);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
static int do_pause(THD *thd, Interruptible_wait *timed_cond, mysql_cond_t *cond, double timeout)
 | 
						|
{
 | 
						|
  int error= 0;
 | 
						|
  timed_cond->set_timeout((ulonglong) (timeout * 1000000000.0));
 | 
						|
 | 
						|
  mysql_cond_init(key_item_func_sleep_cond, cond, NULL);
 | 
						|
  mysql_mutex_lock(&LOCK_item_func_sleep);
 | 
						|
 | 
						|
  THD_STAGE_INFO(thd, stage_user_sleep);
 | 
						|
  thd->mysys_var->current_mutex= &LOCK_item_func_sleep;
 | 
						|
  thd->mysys_var->current_cond=  cond;
 | 
						|
 | 
						|
  thd_wait_begin(thd, THD_WAIT_SLEEP);
 | 
						|
  while (!thd->killed)
 | 
						|
  {
 | 
						|
    error= timed_cond->wait(cond, &LOCK_item_func_sleep);
 | 
						|
    if (error == ETIMEDOUT || error == ETIME)
 | 
						|
      break;
 | 
						|
    error= 0;
 | 
						|
  }
 | 
						|
  thd_wait_end(thd);
 | 
						|
  mysql_mutex_unlock(&LOCK_item_func_sleep);
 | 
						|
  mysql_mutex_lock(&thd->mysys_var->mutex);
 | 
						|
  thd->mysys_var->current_mutex= 0;
 | 
						|
  thd->mysys_var->current_cond=  0;
 | 
						|
  mysql_mutex_unlock(&thd->mysys_var->mutex);
 | 
						|
 | 
						|
  mysql_cond_destroy(cond);
 | 
						|
 | 
						|
  return error;
 | 
						|
}
 | 
						|
 | 
						|
void pause_execution(THD *thd, double timeout)
 | 
						|
{
 | 
						|
  Interruptible_wait timed_cond(thd);
 | 
						|
  mysql_cond_t cond;
 | 
						|
 | 
						|
  do_pause(thd, &timed_cond, &cond, timeout);
 | 
						|
}
 |