/* Copyright (c) 2000, 2015, Oracle and/or its affiliates.
Copyright ( c ) 2009 , 2026 , MariaDB plc .
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
*/
#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 convenience )
*/
bool Item_args::alloc_arguments(THD *thd, uint count)
{
if (count <= 2 )
{
args= tmp_arg;
return false ;
}
if ((args= thd->alloc<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= thd->alloc<Item*>(arg_count)))
{
arg_count= 0 ;
return ;
}
if (arg_count)
memcpy(args, other->args, sizeof (Item*) * arg_count);
}
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_bool(uint start,
uint end) const
{
const LEX_CSTRING fname= func_name_cstring();
for (uint i= start; i < end ; i++)
{
DBUG_ASSERT(i < arg_count);
if (args[i]->check_type_can_return_bool(fname))
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 ;
}
/**
@ brief
Update function ' s nullability based on nullness of its arguments
@ details
Functions like ` IFNULL ` and ` COALESCE ` decide nullability of their
result after checking all the arguments . If any of the argument
is NOT NULL , function ' s result is also set to NOT NULL .
Note : Nullability determined here may be reset by type handlers in
` Item_hybrid_func_fix_attributes ( ) ` , if the first non - null argument
cannot be safely converted to target data type .
E . g . Type_handler_inet6 does :
IFNULL ( inet6_not_null_expr , ' foo ' ) - > INET6 NULL
IFNULL ( inet6_not_null_expr , ' : : 1 ' ) - > INET6 NOT NULL
*/
void Item_func::update_nullability_post_fix_fields()
{
if (!maybe_null())
return ;
for (uint i= 0 ; i < arg_count; i++)
{
if (!args[i]->maybe_null())
{
base_flags &= ~item_base_t::MAYBE_NULL;
break ;
}
}
}
/*
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 22 kB 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;
if (type_handler()->is_complex())
{
with_flags|= item_with_t::COMPLEX_DATA_TYPE;
thd->stmt_arena->with_flags_bit_or_for_complex_data_types|= with_flags;
}
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, const Eq_config &config) 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 &&
!Lex_ident_routine(func_name_cstring()).
streq(item_func->func_name_cstring())))
return 0 ;
return Item_args::eq(item_func, config);
}
/*
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
double Item_func::signal_log_domain_error(double value)
{
THD *thd= current_thd;
if (thd->variables.sql_mode & MODE_ERROR_FOR_DIVISION_BY_ZERO)
push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
ER_STD_INVALID_ARGUMENT,
ER_THD(thd, ER_STD_INVALID_ARGUMENT),
ErrConvDouble(value).ptr(), func_name());
null_value= 1 ;
return 0 .0 ;
}
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;
}
#ifdef _M_ARM64
/* MSVC on ARM incorrectly optimizes the code in val_real_from_int_op() */
#pragma optimize("" , off)
#endif
double Item_func_hybrid_field_type::val_real_from_int_op()
{
longlong result= int_op();
return unsigned_flag ? (double ) ((ulonglong) result) : (double ) result;
}
#ifdef _M_ARM64
#pragma optimize("" , on)
#endif
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 ;
}
longlong Item_func_hybrid_field_type::val_int_from_hex_hybrid_str_op()
{
String *res= str_op_with_null_check(&str_value);
return res ? longlong_from_hex_hybrid(res->ptr(), res->length()) : 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 ( 1 e100 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(my_hasher_st *hasher, String *str)
{
CHARSET_INFO *cs;
uchar l[4 ];
int4store(l, str->length());
cs= str->charset();
cs->hash_sort(hasher, l, sizeof (l));
cs= str->charset();
cs->hash_sort(hasher, (uchar *)str->ptr(), str->length());
}
longlong Item_func_hash_mariadb_100403::val_int()
{
DBUG_EXECUTE_IF("same_long_unique_hash" , return 9 ;);
unsigned_flag= true ;
my_hasher_st hasher= my_hasher_mysql5x();
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(&hasher, str);
}
null_value= 0 ;
return (longlong)hasher.m_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
/*
Clamp the argument width so that adding one character for the sign keeps
the result within the double display length . The argument must be
clamped before the sign is added , otherwise an argument whose max_length
is already the uint32 maximum , as with LONGTEXT and LONGBLOB , overflows
to zero and produces a zero width result .
*/
const uint32 mlen= type_handler()->max_display_length(this );
max_length= MY_MIN(args[0 ]->max_length, mlen - 1 ) + 1 ;
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 )
return signal_log_domain_error(value);
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 base= args[0 ]->val_real();
if ((null_value= args[0 ]->null_value))
return 0 .0 ;
if (arg_count == 2 )
{
double value= args[1 ]->val_real();
if ((null_value= args[1 ]->null_value))
return 0 .0 ;
if (base <= 0 .0 || base == 1 .0 )
return signal_log_domain_error(base);
if (value <= 0 .0 )
return signal_log_domain_error(value);
return log(value) / log(base);
}
if (base <= 0 .0 )
return signal_log_domain_error(base);
return log(base);
}
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 )
return signal_log_domain_error(value);
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 )
return signal_log_domain_error(value);
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*0 x10001L+55555555 L),
(uint32) (tmp*0 x10000001L));
}
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;
thd->lex->safe_to_cache_query= 0 ;
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= thd->active_stmt_arena_to_use()->alloc<my_rnd_struct>(1 )))
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()))
{
null_value= args[2 ]->is_null();
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= 1 ULL << (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 0 L;
}
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= thd->alloc<Item_result>(f_args.arg_count)))
{
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->max_length= func->type_handler()->max_display_length(func);
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)))
{
sql_print_information("Could not get master info for %s" , connection_name.str);
goto err;
}
if (mi->binlog_storage_engine)
{
my_error(ER_NOT_AVAILABLE_WITH_ENGINE_BINLOG, MYF(0 ), "master_pos_wait()" );
mi->release();
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)(1 e6*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 * 1000000000 ULL;
/**
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;
const 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 );
}
const MDL_key ull_key(MDL_key::USER_LOCK, res->c_ptr_safe(), "" );
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);
MDL_ticket *ticket= thd->mdl_context.MDL_ACQUIRE_LOCK_BY_KEY(
&ull_key, MDL_SHARED_NO_WRITE, MDL_EXPLICIT, timeout);
(void ) thd->pop_internal_handler();
if (unlikely(!ticket))
{
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(ticket);
DBUG_RETURN(0 );
}
ull->lock= 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*) alloc_root(current_thd->user_vars_root(),
size)))
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))
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_COERCIBLE);
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(args[0 ]->type_handler());
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_COERCIBLE);
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 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,
arg, WALK_SUBQUERY);
}
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);
if (with_complex_data_types())
{
/*
There are no user variable methods in Type_handler yet .
Also all INT - alike expresions do not preserve the exact data type on
a user variable assignment : they all get converted into BIGINT .
Let ' s disallow complex data types with side effects for now ,
to avoid side effect resources leaking , e . g . m_statement_cursors
elements in case of SYS_REFCURSOR .
*/
my_error(ER_ILLEGAL_PARAMETER_DATA_TYPE_FOR_OPERATION, MYF(0 ),
args[0 ]->type_handler()->name().lex_cstring().str,
"SET user_variable" );
DBUG_RETURN(true );
}
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_USERVAR);
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_USERVAR);
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, const Eq_config &config) 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,
Lex_ident_column::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:
{
CHARSET_INFO *cs= system_charset_info_for_i_s;
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));
uint char_length= cptr ?
(uint32) cs->numchars(cptr, cptr + strlen(cptr)) : 0 ;
mysql_mutex_unlock(&LOCK_global_system_variables);
collation.set(cs, DERIVATION_SYSCONST);
fix_char_length(char_length);
decimals=NOT_FIXED_DEC;
break ;
}
case SHOW_LEX_STRING:
{
CHARSET_INFO *cs= system_charset_info_for_i_s;
mysql_mutex_lock(&LOCK_global_system_variables);
const LEX_STRING *ls=
reinterpret_cast <const LEX_STRING*>(var->value_ptr(current_thd,
var_type,
&component));
uint char_length= (uint32) cs->numchars(ls->str, ls->str + ls->length);
mysql_mutex_unlock(&LOCK_global_system_variables);
collation.set(cs, DERIVATION_SYSCONST);
fix_char_length(char_length);
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, const Eq_config &config) 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].algorithm == HA_KEY_ALG_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, const Eq_config &config) 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(), config))
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),
m_filter(NULL)
{
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),
m_filter(NULL)
{
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
Item_func_sp::print(String *str, enum_query_type query_type)
{
Item_func::print(str, query_type);
if (m_filter)
{
str->append(STRING_WITH_LEN(" FILTER(WHERE " ));
m_filter->print(str, query_type);
str->append(')' );
}
}
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 );
}
Query_arena *arena, backup;
/*
Allocation an instance of Item_func_sp used for initialization of
sp_result_field taken place inside the method init_result_field ( ) is done
on sp_head ' s mem_root since Item_sp also allocated on this memory root .
Switching to SP / PS memory root is done explicitly before calling the method
init_result_field ( ) instead doing that inside init_result_field ( )
since for the case when rollup aggregate function is handled
( @ see Item_sum_sp : : copy_or_same , @ see JOIN : : rollup_make_fields )
the runtime arena used for operations , so switching to SP / PS arena for this
case would result in assertion failure on second execution of the same
prepared statement because the memory root be already marked as read only .
*/
arena= thd->activate_stmt_arena_if_needed(&backup);
/*
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 (arena)
thd->restore_active_arena(arena, &backup);
if (res)
DBUG_RETURN(TRUE );
if (m_filter && m_sp->agg_type() != GROUP_AGGREGATE)
{
my_error(ER_WRONG_USAGE, MYF(0 ), "FILTER" , "non-aggregate function" );
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;
if (m_filter)
{
item_sp->set_filter(m_filter);
}
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 = 16 M 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(THD *thd)
{
DBUG_ASSERT(fixed() && arg_count > 0 );
for (uint i= 0 ; i < arg_count-1 ; i++)
{
args[i]->val_int();
if (with_complex_data_types())
args[i]->expr_event_handler(thd ? thd : current_thd,
expr_event_t::DESTRUCT_ROUTINE_ARG);
}
}
String *Item_func_last_value::val_str(String *str)
{
String *tmp;
evaluate_sideeffects(nullptr);
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(thd);
return val_native_from_item(thd, last_value, to);
}
longlong Item_func_last_value::val_int()
{
longlong tmp;
evaluate_sideeffects(nullptr);
tmp= last_value->val_int();
null_value= last_value->null_value;
return tmp;
}
double Item_func_last_value::val_real()
{
double tmp;
evaluate_sideeffects(nullptr);
tmp= last_value->val_real();
null_value= last_value->null_value;
return tmp;
}
Type_ref_null Item_func_last_value::val_ref(THD *thd)
{
evaluate_sideeffects(thd);
return last_value->val_ref(thd);
}
my_decimal *Item_func_last_value::val_decimal(my_decimal *decimal_value)
{
my_decimal *tmp;
evaluate_sideeffects(nullptr);
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(thd);
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);
}
bool Item_func_cursor_isopen::val_bool()
{
sp_cursor *c= Sp_rcontext_handler::get_cursor(m_thd, *this );
return c ? c->is_open() : 0 ;
}
bool Item_func_cursor_found::val_bool()
{
sp_cursor *c= Sp_rcontext_handler::get_open_cursor_or_error(m_thd, *this );
return !(null_value= (!c || c->fetch_count() == 0 )) && c->found();
}
bool Item_func_cursor_notfound::val_bool()
{
sp_cursor *c= Sp_rcontext_handler::get_open_cursor_or_error(m_thd, *this );
return !(null_value= (!c || c->fetch_count() == 0 )) && !c->found();
}
longlong Item_func_cursor_rowcount::val_int()
{
sp_cursor *c= Sp_rcontext_handler::get_open_cursor_or_error(m_thd, *this );
return !(null_value= !c) ? c->row_count() : 0 ;
}
/*****************************************************************************
SEQUENCE functions
*****************************************************************************/
bool Item_func_nextval::check_access(THD *thd, 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;
}
longlong Item_func_nextval::val_int()
{
longlong value;
int error;
const char *key;
DBUG_ENTER("Item_func_nextval::val_int" );
if (table_list->is_pure_alias())
{
my_error(ER_NOT_SEQUENCE2, MYF(0 ), table_list->alias.str);
DBUG_RETURN(0 );
}
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);
update_table();
DBUG_ASSERT(table);
DBUG_ASSERT(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;
DBUG_ENTER("Item_func_lastval::val_int" );
if (table_list->is_pure_alias())
{
my_error(ER_NOT_SEQUENCE2, MYF(0 ), table_list->alias.str);
DBUG_RETURN(0 );
}
uint length= get_table_def_key(table_list, &key);
THD *thd;
char buff[80 ];
String key_buff(buff,sizeof (buff), &my_charset_bin);
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" );
if (table_list->is_pure_alias())
{
my_error(ER_NOT_SEQUENCE2, MYF(0 ), table_list->alias.str);
DBUG_RETURN(0 );
}
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 );
}
/*
Truncate nextval according to the value type of the sequence if
out of bounds . If truncation happens i . e . nextval is out of
bounds for the value type , return null immediately .
*/
value= table->s->sequence->truncate_value(nextval);
if (value != nextval.value())
{
null_value= 1 ;
value= 0 ;
DBUG_RETURN(0 );
}
unsigned_flag= table->s->sequence->is_unsigned;
error= table->s->sequence->set_value(table, value, 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(',' );
if (nextval.is_unsigned())
str->append_ulonglong(nextval.value());
else
str->append_longlong(nextval.value());
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 initialize 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);
}
Messung V0.5 in Prozent C=96 H=93 G=94
¤ Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.0.370Bemerkung:
(vorverarbeitet am 2026-10-08)
¤
*Bot Zugriff