diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-04 18:07:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-04 18:07:14 +0000 |
commit | a175314c3e5827eb193872241446f2f8f5c9d33c (patch) | |
tree | cd3d60ca99ae00829c52a6ca79150a5b6e62528b /sql/item_func.cc | |
parent | Initial commit. (diff) | |
download | mariadb-10.5-upstream/1%10.5.12.tar.xz mariadb-10.5-upstream/1%10.5.12.zip |
Adding upstream version 1:10.5.12.upstream/1%10.5.12upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | sql/item_func.cc | 7257 |
1 files changed, 7257 insertions, 0 deletions
diff --git a/sql/item_func.cc b/sql/item_func.cc new file mode 100644 index 00000000..60efc55d --- /dev/null +++ b/sql/item_func.cc @@ -0,0 +1,7257 @@ +/* Copyright (c) 2000, 2015, Oracle and/or its affiliates. + Copyright (c) 2009, 2021, MariaDB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */ + +/** + @file + + @brief + This file defines all numerical functions +*/ + +#ifdef USE_PRAGMA_IMPLEMENTATION +#pragma implementation // gcc: Class implementation +#endif + +#include "sql_plugin.h" +#include "sql_priv.h" +/* + It is necessary to include set_var.h instead of item.h because there + are dependencies on include order for set_var.h and item.h. This + will be resolved later. +*/ +#include "sql_class.h" // set_var.h: THD +#include "set_var.h" +#include "slave.h" // for wait_for_master_pos +#include "sql_show.h" // append_identifier +#include "strfunc.h" // find_type +#include "sql_parse.h" // is_update_query +#include "sql_acl.h" // EXECUTE_ACL +#include "mysqld.h" // LOCK_short_uuid_generator +#include "rpl_mi.h" +#include "sql_time.h" +#include <m_ctype.h> +#include <hash.h> +#include <time.h> +#include <ft_global.h> +#include <my_bit.h> + +#include "sp_head.h" +#include "sp_rcontext.h" +#include "sp.h" +#include "set_var.h" +#include "debug_sync.h" +#include "sql_base.h" +#include "sql_cte.h" + +#ifdef 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; +} + + +/** + Test if the sum of arguments overflows the ulonglong range. +*/ +static inline bool test_if_sum_overflows_ull(ulonglong arg1, ulonglong arg2) +{ + return ULONGLONG_MAX - arg1 < arg2; +} + + +/** + Allocate memory for arguments using tmp_args or thd->alloc(). + @retval false - success + @retval true - error (arg_count is set to 0 for conveniece) +*/ +bool Item_args::alloc_arguments(THD *thd, uint count) +{ + if (count <= 2) + { + args= tmp_arg; + return false; + } + if ((args= (Item**) thd->alloc(sizeof(Item*) * count)) == NULL) + { + arg_count= 0; + return true; + } + return false; +} + + +void Item_args::set_arguments(THD *thd, List<Item> &list) +{ + if (alloc_arguments(thd, list.elements)) + return; + List_iterator_fast<Item> li(list); + Item *item; + for (arg_count= 0; (item= li++); ) + args[arg_count++]= item; +} + + +Item_args::Item_args(THD *thd, const Item_args *other) + :arg_count(other->arg_count) +{ + if (arg_count <= 2) + { + args= tmp_arg; + } + else if (!(args= (Item**) thd->alloc(sizeof(Item*) * arg_count))) + { + arg_count= 0; + return; + } + if (arg_count) + memcpy(args, other->args, sizeof(Item*) * arg_count); +} + + +void Item_func::sync_with_sum_func_and_with_field(List<Item> &list) +{ + List_iterator_fast<Item> li(list); + Item *item; + while ((item= li++)) + { + join_with_sum_func(item); + with_window_func|= item->with_window_func; + with_field|= item->with_field; + with_param|= item->with_param; + } +} + + +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(), 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())) + 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())) + 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())) + 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())) + 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())) + 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())) + 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())) + 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())) + return true; + } + return false; +} + + +/* + Resolve references to table column for a function and its argument + + SYNOPSIS: + fix_fields() + thd Thread object + ref Pointer to where this object is used. This reference + is used if we want to replace this object with another + one (for example in the summary functions). + + DESCRIPTION + Call fix_fields() for all arguments to the function. The main intention + is to allow all Item_field() objects to setup pointers to the table fields. + + Sets as a side effect the following class variables: + maybe_null Set if any argument may return NULL + with_sum_func Set if any of the arguments contains a sum function + with_window_func Set if any of the arguments contain a window function + with_field Set if any of the arguments contains or is a field + used_tables_cache Set to union of the tables used by arguments + + str_value.charset If this is a string function, set this to the + character set for the first argument. + If any argument is binary, this is set to binary + + If for any item any of the defaults are wrong, then this can + be fixed in the fix_length_and_dec() function that is called + after this one or by writing a specialized fix_fields() for the + item. + + RETURN VALUES + FALSE ok + TRUE Got error. Stored with my_error(). +*/ + +bool +Item_func::fix_fields(THD *thd, Item **ref) +{ + DBUG_ASSERT(fixed == 0); + Item **arg,**arg_end; + uchar buff[STACK_BUFF_ALLOC]; // Max argument in function + + /* + The Used_tables_and_const_cache of "this" was initialized by + the constructor, or by Item_func::cleanup(). + */ + DBUG_ASSERT(used_tables_cache == 0); + DBUG_ASSERT(const_item_cache == true); + + not_null_tables_cache= 0; + + /* + Use stack limit of STACK_MIN_SIZE * 2 since + on some platforms a recursive call to fix_fields + requires more than STACK_MIN_SIZE bytes (e.g. for + MIPS, it takes about 22kB to make one recursive + call to Item_func::fix_fields()) + */ + if (check_stack_overrun(thd, STACK_MIN_SIZE * 2, buff)) + return TRUE; // Fatal error if flag is set! + if (arg_count) + { // Print purify happy + for (arg=args, arg_end=args+arg_count; arg != arg_end ; arg++) + { + Item *item; + /* + We can't yet set item to *arg as fix_fields may change *arg + We shouldn't call fix_fields() twice, so check 'fixed' field first + */ + if ((*arg)->fix_fields_if_needed(thd, arg)) + return TRUE; /* purecov: inspected */ + item= *arg; + + if (item->maybe_null) + maybe_null=1; + + join_with_sum_func(item); + with_param= with_param || item->with_param; + with_window_func= with_window_func || item->with_window_func; + with_field= with_field || item->with_field; + used_tables_and_const_cache_join(item); + not_null_tables_cache|= item->not_null_tables(); + m_with_subquery|= item->with_subquery(); + } + } + if (check_arguments()) + return true; + if (fix_length_and_dec()) + return TRUE; + fixed= 1; + 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)->is_fixed()) + (*arg)->quick_fix_field(); + } + } + fixed= 1; +} + + +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; + 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); +} + + +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()); + str->append('('); + print_args(str, 0, query_type); + str->append(')'); +} + + +void Item_func::print_args(String *str, uint from, enum_query_type query_type) +{ + 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()); + str->append(' '); + } + args[arg_count-1]->print_parenthesised(str, query_type, higher_precedence()); +} + + +bool Item_func::eq(const Item *item, bool binary_cmp) const +{ + /* Assume we don't have rtti */ + if (this == item) + return 1; + /* + Ensure that we are comparing two functions and that the function + is deterministic. + */ + if (item->type() != FUNC_ITEM || (used_tables() & RAND_TABLE_BIT)) + return 0; + Item_func *item_func=(Item_func*) item; + Item_func::Functype func_type; + if ((func_type= functype()) != item_func->functype() || + arg_count != item_func->arg_count || + (func_type != Item_func::FUNC_SP && + func_name() != item_func->func_name()) || + (func_type == Item_func::FUNC_SP && + my_strcasecmp(system_charset_info, func_name(), item_func->func_name()))) + return 0; + return Item_args::eq(item_func, binary_cmp); +} + + +/* +bool Item_func::is_expensive_processor(uchar *arg) +{ + return is_expensive(); +} +*/ + + +bool Item_hybrid_func::fix_attributes(Item **items, uint nitems) +{ + bool rc= Item_hybrid_func::type_handler()-> + Item_hybrid_func_fix_attributes(current_thd, + func_name(), this, this, + items, nitems); + DBUG_ASSERT(!rc || current_thd->is_error()); + return rc; +} + + +String *Item_real_func::val_str(String *str) +{ + DBUG_ASSERT(fixed == 1); + double nr= val_real(); + if (null_value) + return 0; /* purecov: inspected */ + str->set_real(nr, decimals, collation.collation); + return str; +} + + +my_decimal *Item_real_func::val_decimal(my_decimal *decimal_value) +{ + DBUG_ASSERT(fixed); + double nr= val_real(); + if (null_value) + return 0; /* purecov: inspected */ + double2my_decimal(E_DEC_FATAL_ERROR, nr, decimal_value); + return decimal_value; +} + + +#ifdef HAVE_DLOPEN +void Item_udf_func::fix_num_length_and_dec() +{ + uint fl_length= 0; + decimals=0; + for (uint i=0 ; i < arg_count ; i++) + { + set_if_bigger(decimals,args[i]->decimals); + set_if_bigger(fl_length, args[i]->max_length); + } + max_length=float_length(decimals); + if (fl_length > max_length) + { + decimals= NOT_FIXED_DEC; + max_length= float_length(NOT_FIXED_DEC); + } +} +#endif + + +void Item_func::signal_divide_by_null() +{ + THD *thd= current_thd; + if (thd->variables.sql_mode & MODE_ERROR_FOR_DIVISION_BY_ZERO) + push_warning(thd, Sql_condition::WARN_LEVEL_WARN, ER_DIVISION_BY_ZERO, + ER_THD(thd, ER_DIVISION_BY_ZERO)); + null_value= 1; +} + + +Item *Item_func::get_tmp_table_item(THD *thd) +{ + if (!Item_func::with_sum_func() && !const_item()) + return new (thd->mem_root) Item_temptable_field(thd, result_field); + return copy_or_same(thd); +} + +double Item_int_func::val_real() +{ + DBUG_ASSERT(fixed == 1); + + return unsigned_flag ? (double) ((ulonglong) val_int()) : (double) val_int(); +} + + +String *Item_int_func::val_str(String *str) +{ + DBUG_ASSERT(fixed == 1); + 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() +{ + if (Item_long_func::fix_length_and_dec()) + 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->thread_specific_used= TRUE; + 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(void) +{ + DBUG_ENTER("Item_func_plus::fix_length_and_dec"); + DBUG_PRINT("info", ("name %s", func_name())); + const Type_aggregator *aggregator= &type_handler_data->m_type_aggregator_for_plus; + DBUG_EXECUTE_IF("num_op", aggregator= &type_handler_data->m_type_aggregator_for_result;); + DBUG_ASSERT(aggregator->is_commutative()); + if (fix_type_handler(aggregator)) + DBUG_RETURN(TRUE); + if (Item_func_plus::type_handler()->Item_func_plus_fix_length_and_dec(this)) + DBUG_RETURN(TRUE); + DBUG_PRINT("info", ("Type: %s", type_handler()->name().ptr())); + DBUG_RETURN(FALSE); +} + + +String *Item_func_hybrid_field_type::val_str_from_int_op(String *str) +{ + longlong nr= int_op(); + if (null_value) + return 0; /* purecov: inspected */ + str->set_int(nr, unsigned_flag, collation.collation); + return str; +} + +double Item_func_hybrid_field_type::val_real_from_int_op() +{ + longlong result= int_op(); + return unsigned_flag ? (double) ((ulonglong) result) : (double) result; +} + +my_decimal * +Item_func_hybrid_field_type::val_decimal_from_int_op(my_decimal *dec) +{ + longlong result= int_op(); + if (null_value) + return NULL; + int2my_decimal(E_DEC_FATAL_ERROR, result, unsigned_flag, dec); + return dec; +} + + +String *Item_func_hybrid_field_type::val_str_from_real_op(String *str) +{ + double nr= real_op(); + if (null_value) + return 0; /* purecov: inspected */ + str->set_real(nr, decimals, collation.collation); + return str; +} + +longlong Item_func_hybrid_field_type::val_int_from_real_op() +{ + return Converter_double_to_longlong(real_op(), unsigned_flag).result(); +} + +my_decimal * +Item_func_hybrid_field_type::val_decimal_from_real_op(my_decimal *dec) +{ + double result= (double) real_op(); + if (null_value) + return NULL; + double2my_decimal(E_DEC_FATAL_ERROR, result, dec); + return dec; +} + + +String *Item_func_hybrid_field_type::val_str_from_date_op(String *str) +{ + MYSQL_TIME ltime; + if (date_op_with_null_check(current_thd, <ime) || + (null_value= str->alloc(MAX_DATE_STRING_REP_LENGTH))) + return (String *) 0; + str->length(my_TIME_to_str(<ime, const_cast<char*>(str->ptr()), decimals)); + str->set_charset(&my_charset_bin); + DBUG_ASSERT(!null_value); + return str; +} + +double Item_func_hybrid_field_type::val_real_from_date_op() +{ + MYSQL_TIME ltime; + if (date_op_with_null_check(current_thd, <ime)) + return 0; + return TIME_to_double(<ime); +} + +longlong Item_func_hybrid_field_type::val_int_from_date_op() +{ + MYSQL_TIME ltime; + if (date_op_with_null_check(current_thd, <ime)) + return 0; + return TIME_to_ulonglong(<ime); +} + +my_decimal * +Item_func_hybrid_field_type::val_decimal_from_date_op(my_decimal *dec) +{ + MYSQL_TIME ltime; + if (date_op_with_null_check(current_thd, <ime)) + { + my_decimal_set_zero(dec); + return 0; + } + return date2my_decimal(<ime, dec); +} + + +String *Item_func_hybrid_field_type::val_str_from_time_op(String *str) +{ + MYSQL_TIME ltime; + if (time_op_with_null_check(current_thd, <ime) || + (null_value= my_TIME_to_str(<ime, str, decimals))) + return NULL; + return str; +} + +double Item_func_hybrid_field_type::val_real_from_time_op() +{ + MYSQL_TIME ltime; + return time_op_with_null_check(current_thd, <ime) ? 0 : + TIME_to_double(<ime); +} + +longlong Item_func_hybrid_field_type::val_int_from_time_op() +{ + MYSQL_TIME ltime; + return time_op_with_null_check(current_thd, <ime) ? 0 : + TIME_to_ulonglong(<ime); +} + +my_decimal * +Item_func_hybrid_field_type::val_decimal_from_time_op(my_decimal *dec) +{ + MYSQL_TIME ltime; + if (time_op_with_null_check(current_thd, <ime)) + { + my_decimal_set_zero(dec); + return 0; + } + return date2my_decimal(<ime, dec); +} + + +double Item_func_hybrid_field_type::val_real_from_str_op() +{ + String *res= str_op_with_null_check(&str_value); + return res ? double_from_string_with_check(res) : 0.0; +} + +longlong Item_func_hybrid_field_type::val_int_from_str_op() +{ + String *res= str_op_with_null_check(&str_value); + return res ? longlong_from_string_with_check(res) : 0; +} + +my_decimal * +Item_func_hybrid_field_type::val_decimal_from_str_op(my_decimal *decimal_value) +{ + String *res= str_op_with_null_check(&str_value); + return res ? decimal_from_string_with_check(decimal_value, res) : 0; +} + + +void Item_func_signed::print(String *str, enum_query_type query_type) +{ + str->append(STRING_WITH_LEN("cast(")); + args[0]->print(str, query_type); + str->append(STRING_WITH_LEN(" as signed)")); + +} + + +void Item_func_unsigned::print(String *str, enum_query_type query_type) +{ + str->append(STRING_WITH_LEN("cast(")); + args[0]->print(str, query_type); + str->append(STRING_WITH_LEN(" as unsigned)")); + +} + + +my_decimal *Item_decimal_typecast::val_decimal(my_decimal *dec) +{ + VDec tmp(args[0]); + bool sign; + uint precision; + + if ((null_value= tmp.is_null())) + return NULL; + tmp.round_to(dec, decimals, HALF_UP); + sign= dec->sign(); + if (unsigned_flag) + { + if (sign) + { + my_decimal_set_zero(dec); + goto err; + } + } + precision= my_decimal_length_to_precision(max_length, + decimals, unsigned_flag); + if (precision - decimals < (uint) my_decimal_intg(dec)) + { + max_my_decimal(dec, precision, decimals); + dec->sign(sign); + goto err; + } + return dec; + +err: + THD *thd= current_thd; + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, + ER_WARN_DATA_OUT_OF_RANGE, + ER_THD(thd, ER_WARN_DATA_OUT_OF_RANGE), + name.str, 1L); + return dec; +} + + +void Item_decimal_typecast::print(String *str, enum_query_type query_type) +{ + char len_buf[20*3 + 1]; + char *end; + + uint precision= my_decimal_length_to_precision(max_length, decimals, + unsigned_flag); + str->append(STRING_WITH_LEN("cast(")); + args[0]->print(str, query_type); + str->append(STRING_WITH_LEN(" as decimal(")); + + end=int10_to_str(precision, len_buf,10); + str->append(len_buf, (uint32) (end - len_buf)); + + str->append(','); + + end=int10_to_str(decimals, len_buf,10); + str->append(len_buf, (uint32) (end - len_buf)); + + str->append(')'); + str->append(')'); +} + + +double Item_real_typecast::val_real_with_truncate(double max_value) +{ + int error; + double tmp= args[0]->val_real(); + if ((null_value= args[0]->null_value)) + return 0.0; + + if (unlikely((error= truncate_double(&tmp, max_length, decimals, + false/*unsigned_flag*/, max_value)))) + { + /* + We don't want automatic escalation from a warning to an error + in this scenario: + INSERT INTO t1 (float_field) VALUES (CAST(1e100 AS FLOAT)); + The above statement should work even in the strict mode. + So let's use a note rather than a warning. + */ + THD *thd= current_thd; + push_warning_printf(thd, + Sql_condition::WARN_LEVEL_NOTE, + ER_WARN_DATA_OUT_OF_RANGE, + ER_THD(thd, ER_WARN_DATA_OUT_OF_RANGE), + name.str, (ulong) 1); + if (error < 0) + { + null_value= 1; // Illegal value + tmp= 0.0; + } + } + return tmp; +} + + +void Item_real_typecast::print(String *str, enum_query_type query_type) +{ + char len_buf[20*3 + 1]; + char *end; + + str->append(STRING_WITH_LEN("cast(")); + args[0]->print(str, query_type); + str->append(STRING_WITH_LEN(" as ")); + str->append(type_handler()->name().ptr()); + 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; + } + } + +#ifndef WITH_UBSAN + res= val0 + val1; +#else + if (res_unsigned) + res= (longlong) ((ulonglong) val0 + (ulonglong) val1); + else + res= val0+val1; +#endif /* WITH_UBSAN */ + + 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() +{ + 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) + goto err; + res_unsigned= TRUE; + } + 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; + } + } +#ifndef WITH_UBSAN + res= val0 - val1; +#else + if (res_unsigned) + res= (longlong) ((ulonglong) val0 - (ulonglong) val1); + else + res= val0 - val1; +#endif /* WITH_UBSAN */ + + 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 == 1); + 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 == 1); + longlong a= args[0]->val_int(); + longlong b= args[1]->val_int(); + longlong res; + ulonglong res0, res1; + ulong a0, a1, b0, b1; + bool res_unsigned= FALSE; + bool a_negative= FALSE, b_negative= FALSE; + + 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(). + + Let a = a1 * 2^32 + a0 and b = b1 * 2^32 + b0. Then + a * b = (a1 * 2^32 + a0) * (b1 * 2^32 + b0) = a1 * b1 * 2^64 + + + (a1 * b0 + a0 * b1) * 2^32 + a0 * b0; + We can determine if the above sum overflows the ulonglong range by + sequentially checking the following conditions: + 1. If both a1 and b1 are non-zero. + 2. Otherwise, if (a1 * b0 + a0 * b1) is greater than ULONG_MAX. + 3. Otherwise, if (a1 * b0 + a0 * b1) * 2^32 + a0 * b0 is greater than + ULONGLONG_MAX. + + 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. + */ + if (!args[0]->unsigned_flag && a < 0) + { + a_negative= TRUE; + a= -a; + } + if (!args[1]->unsigned_flag && b < 0) + { + b_negative= TRUE; + b= -b; + } + + a0= 0xFFFFFFFFUL & a; + a1= ((ulonglong) a) >> 32; + b0= 0xFFFFFFFFUL & b; + b1= ((ulonglong) b) >> 32; + + if (a1 && b1) + goto err; + + res1= (ulonglong) a1 * b0 + (ulonglong) a0 * b1; + if (res1 > 0xFFFFFFFFUL) + goto err; + + res1= res1 << 32; + res0= (ulonglong) a0 * b0; + + if (test_if_sum_overflows_ull(res1, res0)) + goto err; + res= res1 + res0; + + if (a_negative != b_negative) + { + if ((ulonglong) res > (ulonglong) LONGLONG_MIN + 1) + goto err; + res= -res; + } + else + res_unsigned= TRUE; + + return check_integer_overflow(res, res_unsigned); + +err: + return raise_integer_overflow(); +} + + +/** 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(void) +{ + 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 == 1); + 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() +{ + DBUG_ENTER("Item_func_div::fix_length_and_dec"); + DBUG_PRINT("info", ("name %s", func_name())); + prec_increment= current_thd->variables.div_precincrement; + maybe_null= 1; // 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 == 1); + + /* + 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; + } + + bool res_negative= val0.neg() != val1.neg(); + ulonglong res= val0.abs() / val1.abs(); + if (res_negative) + { + if (res > (ulonglong) LONGLONG_MAX) + return raise_integer_overflow(); + res= (ulonglong) (-(longlong) res); + } + return check_integer_overflow(res, !res_negative); +} + + +bool Item_func_int_div::fix_length_and_dec() +{ + uint32 prec= args[0]->decimal_int_part(); + set_if_smaller(prec, MY_INT64_NUM_DECIMAL_DIGITS); + fix_char_length(prec); + maybe_null=1; + unsigned_flag=args[0]->unsigned_flag | args[1]->unsigned_flag; + return false; +} + + +longlong Item_func_mod::int_op() +{ + DBUG_ASSERT(fixed == 1); + 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. + */ + ulonglong res= val0.abs() % val1.abs(); + return check_integer_overflow(val0.neg() ? -(longlong) res : res, + !val0.neg()); +} + +double Item_func_mod::real_op() +{ + DBUG_ASSERT(fixed == 1); + 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() +{ + DBUG_ENTER("Item_func_mod::fix_length_and_dec"); + DBUG_PRINT("info", ("name %s", func_name())); + maybe_null= true; // division by zero + const Type_aggregator *aggregator= &type_handler_data->m_type_aggregator_for_mod; + DBUG_EXECUTE_IF("num_op", aggregator= &type_handler_data->m_type_aggregator_non_commutative_test;); + DBUG_ASSERT(!aggregator->is_commutative()); + if (fix_type_handler(aggregator)) + DBUG_RETURN(TRUE); + if (Item_func_mod::type_handler()->Item_func_mod_fix_length_and_dec(this)) + DBUG_RETURN(TRUE); + DBUG_PRINT("info", ("Type: %s", type_handler()->name().ptr())); + DBUG_RETURN(FALSE); +} + +static void calc_hash_for_unique(ulong &nr1, ulong &nr2, String *str) +{ + CHARSET_INFO *cs; + uchar l[4]; + int4store(l, str->length()); + cs= str->charset(); + cs->hash_sort(l, sizeof(l), &nr1, &nr2); + cs= str->charset(); + cs->hash_sort((uchar *)str->ptr(), str->length(), &nr1, &nr2); +} + +longlong Item_func_hash::val_int() +{ + DBUG_EXECUTE_IF("same_long_unique_hash", return 9;); + unsigned_flag= true; + ulong nr1= 1,nr2= 4; + String * str; + for(uint i= 0;i<arg_count;i++) + { + str = args[i]->val_str(); + if(args[i]->null_value) + { + null_value= 1; + return 0; + } + calc_hash_for_unique(nr1, nr2, str); + } + null_value= 0; + return (longlong)nr1; +} + + +bool Item_func_hash::fix_length_and_dec() +{ + 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()) + { + longlong val= args[0]->val_int(); + if ((ulonglong) val >= (ulonglong) LONGLONG_MIN && + ((ulonglong) val != (ulonglong) LONGLONG_MIN || + !args[0]->is_of_type(CONST_ITEM, INT_RESULT))) + { + /* + Ensure that result is converted to DECIMAL, as longlong can't hold + the negated number + */ + set_handler(&type_handler_newdecimal); + DBUG_PRINT("info", ("Type changed: DECIMAL_RESULT")); + } + } + unsigned_flag= false; +} + + +void Item_func_neg::fix_length_and_dec_double() +{ + set_handler(&type_handler_double); + decimals= args[0]->decimals; // Preserve NOT_FIXED_DEC + max_length= args[0]->max_length + 1; + // Limit length with something reasonable + uint32 mlen= type_handler()->max_display_length(this); + set_if_smaller(max_length, mlen); + unsigned_flag= false; +} + + +void Item_func_neg::fix_length_and_dec_decimal() +{ + set_handler(&type_handler_newdecimal); + decimals= args[0]->decimal_scale(); // Do not preserve NOT_FIXED_DEC + max_length= args[0]->max_length + 1; + unsigned_flag= false; +} + + +bool Item_func_neg::fix_length_and_dec() +{ + 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_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() +{ + 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 == 1); + double value= args[0]->val_real(); + if ((null_value= args[0]->null_value)) + return 0.0; + if (value <= 0.0) + { + signal_divide_by_null(); + return 0.0; + } + return log(value); +} + +/** + Extended but so slower LOG function. + + We have to check if all values are > zero and first one is not one + as these are the cases then result is not a number. +*/ +double Item_func_log::val_real() +{ + DBUG_ASSERT(fixed == 1); + double value= args[0]->val_real(); + if ((null_value= args[0]->null_value)) + return 0.0; + if (value <= 0.0) + { + signal_divide_by_null(); + return 0.0; + } + if (arg_count == 2) + { + double value2= args[1]->val_real(); + if ((null_value= args[1]->null_value)) + return 0.0; + if (value2 <= 0.0 || value == 1.0) + { + signal_divide_by_null(); + return 0.0; + } + return log(value2) / log(value); + } + return log(value); +} + +double Item_func_log2::val_real() +{ + DBUG_ASSERT(fixed == 1); + double value= args[0]->val_real(); + + if ((null_value=args[0]->null_value)) + return 0.0; + if (value <= 0.0) + { + signal_divide_by_null(); + return 0.0; + } + return log(value) / M_LN2; +} + +double Item_func_log10::val_real() +{ + DBUG_ASSERT(fixed == 1); + double value= args[0]->val_real(); + if ((null_value= args[0]->null_value)) + return 0.0; + if (value <= 0.0) + { + signal_divide_by_null(); + return 0.0; + } + return log10(value); +} + +double Item_func_exp::val_real() +{ + DBUG_ASSERT(fixed == 1); + 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 == 1); + 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 == 1); + 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 == 1); + /* 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 == 1); + // 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 == 1); + 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 == 1); + 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 == 1); + 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 == 1); + 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 == 1); + 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 + { + DBUG_ASSERT(item->is_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 + { + DBUG_ASSERT(item->is_fixed()); + return VDec(item->arguments()[0]).to_xlonglong_null() << + item->arguments()[1]->to_longlong_null(); + } +}; + + +bool Item_func_shift_left::fix_length_and_dec() +{ + 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 + { + DBUG_ASSERT(item->fixed == 1); + 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 + { + DBUG_ASSERT(item->is_fixed()); + return VDec(item->arguments()[0]).to_xlonglong_null() >> + item->arguments()[1]->to_longlong_null(); + } +}; + + +bool Item_func_shift_right::fix_length_and_dec() +{ + 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 + { + DBUG_ASSERT(item->is_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 + { + DBUG_ASSERT(item->is_fixed()); + return ~ VDec(item->arguments()[0]).to_xlonglong_null(); + } +}; + + +bool Item_func_bit_neg::fix_length_and_dec() +{ + 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() +{ + 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); +} + + +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= float_length(args[0]->decimals) + 1; + } +} + + +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]->const_item() && !args[1]->is_expensive()) + { + 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) + maybe_null= true; + 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]->const_item() && !args[1]->is_expensive()) + { + // 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_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, bool dec_unsigned, + bool truncate) +{ + double tmp; + bool dec_negative= (dec < 0) && !dec_unsigned; + ulonglong abs_dec= dec_negative ? -dec : dec; + /* + 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_negative && std::isinf(tmp)) // "dec" is too large positive number + return value; + + if (dec_negative && std::isinf(tmp)) + tmp2= 0.0; + else if (!dec_negative && std::isinf(value_mul_tmp)) + tmp2= value; + else if (truncate) + { + if (value >= 0.0) + tmp2= dec < 0 ? floor(value_div_tmp) * tmp : floor(value_mul_tmp) / tmp; + else + tmp2= dec < 0 ? ceil(value_div_tmp) * tmp : ceil(value_mul_tmp) / tmp; + } + else + tmp2=dec < 0 ? 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= -dec; + 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, (uint) 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; +} + + +void Item_func_rand::seed_random(Item *arg) +{ + /* + TODO: do not do reinit 'rand' for every execute of PS/SP if + args[0] is a constant. + */ + uint32 tmp= (uint32) arg->val_int(); +#ifdef WITH_WSREP + if (WSREP_ON) + { + THD *thd= current_thd; + if (WSREP(thd)) + { + if (wsrep_thd_is_applying(thd)) + tmp= thd->wsrep_rand; + else + thd->wsrep_rand= tmp; + } + } +#endif /* WITH_WSREP */ + + my_rnd_init(rand, (uint32) (tmp*0x10001L+55555555L), + (uint32) (tmp*0x10000001L)); +} + + +bool Item_func_rand::fix_fields(THD *thd,Item **ref) +{ + if (Item_real_func::fix_fields(thd, ref)) + return TRUE; + used_tables_cache|= RAND_TABLE_BIT; + if (arg_count) + { // Only use argument once in query + /* + Allocate rand structure once: we must use thd->stmt_arena + to create rand in proper mem_root if it's a prepared statement or + stored procedure. + + No need to send a Rand log event if seed was given eg: RAND(seed), + as it will be replicated in the query as such. + */ + if (!rand && !(rand= (struct my_rnd_struct*) + thd->stmt_arena->alloc(sizeof(*rand)))) + return TRUE; + } + else + { + /* + Save the seed only the first time RAND() is used in the query + Once events are forwarded rather than recreated, + the following can be skipped if inside the slave thread + */ + if (!thd->rand_used) + { + thd->rand_used= 1; + 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 == 1); + 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 == 1); + 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 == 1); + 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 == 1); + + 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 == 1); + + 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; + res2= args[i]->val_str(res == str ? &tmp_value : str); + if (res2) + { + int cmp= sortcmp(res,res2,collation.collation); + if ((cmp_sign < 0 ? cmp : -cmp) < 0) + res=res2; + } + } + 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)) + break; + } + return value; +} + + +longlong Item_func_min_max::val_int_native() +{ + DBUG_ASSERT(fixed == 1); + 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)) + break; + } + return value; +} + + +my_decimal *Item_func_min_max::val_decimal_native(my_decimal *dec) +{ + DBUG_ASSERT(fixed == 1); + 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 == 1); + 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 == 1); + 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 == 1); + 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 == 1); + String *res=args[0]->val_str(&value); + if (!res) + { + null_value=1; + return 0; /* purecov: inspected */ + } + null_value=0; + return (longlong) res->numchars(); +} + + +longlong Item_func_coercibility::val_int() +{ + DBUG_ASSERT(fixed == 1); + null_value= 0; + return (longlong) args[0]->collation.derivation; +} + + +longlong Item_func_locate::val_int() +{ + DBUG_ASSERT(fixed == 1); + String *a=args[0]->val_str(&value1); + String *b=args[1]->val_str(&value2); + if (!a || !b) + { + null_value=1; + return 0; /* purecov: inspected */ + } + null_value=0; + /* must be longlong to avoid truncation */ + longlong start= 0; + longlong start0= 0; + my_match_t match; + + if (arg_count == 3) + { + start0= start= args[2]->val_int(); + + if ((start <= 0) || (start > a->length())) + return 0; + start0--; start--; + + /* start is now sufficiently valid to pass to charpos function */ + start= a->charpos((int) start); + + if (start + b->length() > a->length()) + return 0; + } + + if (!b->length()) // Found empty string at start + return start + 1; + + if (!cmp_collation.collation->instr(a->ptr() + start, + (uint) (a->length() - start), + b->ptr(), b->length(), + &match, 1)) + return 0; + return (longlong) match.mb_len + start0 + 1; +} + + +void Item_func_locate::print(String *str, enum_query_type query_type) +{ + str->append(STRING_WITH_LEN("locate(")); + args[1]->print(str, query_type); + str->append(','); + args[0]->print(str, query_type); + if (arg_count == 3) + { + str->append(','); + args[2]->print(str, query_type); + } + str->append(')'); +} + + +longlong Item_func_field::val_int() +{ + DBUG_ASSERT(fixed == 1); + + 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() +{ + maybe_null=0; 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 == 1); + 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 == 1); + 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() +{ + decimals=0; + max_length=3; // 1-999 + if (args[0]->const_item() && args[1]->type() == FIELD_ITEM) + { + Field *field= ((Item_field*) args[1])->field; + if (field->real_type() == MYSQL_TYPE_SET) + { + String *find=args[0]->val_str(&value); + if (find) + { + // find is not NULL pointer so args[0] is not a null-value + DBUG_ASSERT(!args[0]->null_value); + enum_value= find_type(((Field_enum*) field)->typelib,find->ptr(), + find->length(), 0); + enum_bit=0; + if (enum_value) + enum_bit= 1ULL << (enum_value-1); + } + } + } + return agg_arg_charsets_for_comparison(cmp_collation, args, 2); +} + +static const char separator=','; + +longlong Item_func_find_in_set::val_int() +{ + DBUG_ASSERT(fixed == 1); + if (enum_value) + { + // enum_value is set iff args[0]->const_item() in fix_length_and_dec(). + DBUG_ASSERT(args[0]->const_item()); + + ulonglong tmp= (ulonglong) args[1]->val_int(); + null_value= args[1]->null_value; + /* + No need to check args[0]->null_value since enum_value is set iff + args[0] is a non-null const item. Note: no DBUG_ASSERT on + args[0]->null_value here because args[0] may have been replaced + by an Item_cache on which val_int() has not been called. See + BUG#11766317 + */ + if (!null_value) + { + if (tmp & enum_bit) + return enum_value; + } + return 0L; + } + + String *find=args[0]->val_str(&value); + String *buffer=args[1]->val_str(&value2); + if (!find || !buffer) + { + null_value=1; + return 0; /* purecov: inspected */ + } + null_value=0; + + if ((int) (buffer->length() - find->length()) >= 0) + { + my_wc_t wc= 0; + CHARSET_INFO *cs= cmp_collation.collation; + const char *str_begin= buffer->ptr(); + const char *str_end= buffer->ptr(); + const char *real_end= str_end+buffer->length(); + const char *find_str= find->ptr(); + uint find_str_len= find->length(); + int position= 0; + while (1) + { + int symbol_len; + if ((symbol_len= cs->mb_wc(&wc, (uchar*) str_end, + (uchar*) real_end)) > 0) + { + const char *substr_end= str_end + symbol_len; + bool is_last_item= (substr_end == real_end); + bool is_separator= (wc == (my_wc_t) separator); + if (is_separator || is_last_item) + { + position++; + if (is_last_item && !is_separator) + str_end= substr_end; + if (!cs->strnncoll(str_begin, (uint) (str_end - str_begin), + find_str, find_str_len)) + return (longlong) position; + else + str_begin= substr_end; + } + str_end= substr_end; + } + else if (str_end - str_begin == 0 && + find_str_len == 0 && + wc == (my_wc_t) separator) + return (longlong) ++position; + else + return 0; + } + } + return 0; +} + + +class Func_handler_bit_count_int_to_slong: + public Item_handled_func::Handler_slong2 +{ +public: + Longlong_null to_longlong_null(Item_handled_func *item) const + { + DBUG_ASSERT(item->is_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 + { + DBUG_ASSERT(item->is_fixed()); + return VDec(item->arguments()[0]).to_xlonglong_null().bit_count(); + } +}; + + +bool Item_func_bit_count::fix_length_and_dec() +{ + 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->maybe_null=0; + func->used_tables_and_const_cache_init(); + + if ((f_args.arg_count=arg_count)) + { + if (!(f_args.arg_type= (Item_result*) + thd->alloc(f_args.arg_count*sizeof(Item_result)))) + + { + free_udf(u_d); + DBUG_RETURN(TRUE); + } + uint i; + Item **arg,**arg_end; + With_sum_func_cache *with_sum_func_cache= func->get_with_sum_func_cache(); + 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); + if (item->maybe_null) + func->maybe_null=1; + if (with_sum_func_cache) + with_sum_func_cache->join_with_sum_func(item); + func->with_window_func= func->with_window_func || + item->with_window_func; + func->with_field= func->with_field || item->with_field; + func->with_param= func->with_param || item->with_param; + func->With_subquery_cache::join(item); + func->used_tables_and_const_cache_join(item); + f_args.arg_type[i]=item->result_type(); + } + if (!(buffers=new (thd->mem_root) String[arg_count]) || + !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)) + { + free_udf(u_d); + DBUG_RETURN(TRUE); + } + } + if (func->fix_length_and_dec()) + 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; + 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); + free_udf(u_d); + DBUG_RETURN(TRUE); + } + func->max_length=MY_MIN(initid.max_length,MAX_BLOB_WIDTH); + func->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()); + 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 == 1); + 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 == 1); + 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 == 1); + 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 == 1); + 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 == 1); + 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() +{ + 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 == 1); + 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 == 1); + THD* thd = current_thd; + String *log_name = args[0]->val_str(&value); + int event_count= 0; + DBUG_ENTER("Item_master_pos_wait::val_int"); + + null_value=0; + if (thd->slave_thread || !log_name || !log_name->length()) + { + null_value = 1; + DBUG_RETURN(0); + } +#ifdef HAVE_REPLICATION + longlong pos = (ulong)args[1]->val_int(); + longlong timeout = (arg_count>=3) ? args[2]->val_int() : 0 ; + String connection_name_buff; + LEX_CSTRING connection_name; + Master_info *mi= NULL; + if (arg_count >= 4) + { + String *con; + if (!(con= args[3]->val_str(&connection_name_buff))) + goto err; + + connection_name.str= con->ptr(); + connection_name.length= con->length(); + if (check_master_connection_name(&connection_name)) + { + my_error(ER_WRONG_ARGUMENTS, MYF(ME_WARNING), + "MASTER_CONNECTION_NAME"); + goto err; + } + } + else + connection_name= thd->variables.default_master_connection; + + if (!(mi= get_master_info(&connection_name, Sql_condition::WARN_LEVEL_WARN))) + goto err; + + if ((event_count = mi->rli.wait_for_pos(thd, log_name, pos, timeout)) == -2) + { + null_value = 1; + event_count=0; + } + mi->release(); +#endif + DBUG_PRINT("exit", ("event_count: %d null_value: %d", event_count, + (int) null_value)); + DBUG_RETURN(event_count); + +#ifdef HAVE_REPLICATION +err: + { + null_value = 1; + DBUG_RETURN(0); + } +#endif +} + + +longlong Item_master_gtid_wait::val_int() +{ + DBUG_ASSERT(fixed == 1); + longlong result= 0; + String *gtid_pos __attribute__((unused)) = args[0]->val_str(&value); + DBUG_ENTER("Item_master_gtid_wait::val_int"); + + if (args[0]->null_value) + { + null_value= 1; + DBUG_RETURN(0); + } + + null_value=0; +#ifdef HAVE_REPLICATION + THD* thd= current_thd; + longlong timeout_us; + + if (arg_count==2 && !args[1]->null_value) + timeout_us= (longlong)(1e6*args[1]->val_real()); + else + timeout_us= (longlong)-1; + + result= rpl_global_gtid_waiting.wait_for_pos(thd, gtid_pos, timeout_us); +#else + null_value= 0; +#endif /* REPLICATION */ + DBUG_RETURN(result); +} + + +/** + Enables a session to wait on a condition until a timeout or a network + disconnect occurs. + + @remark The connection is polled every m_interrupt_interval nanoseconds. +*/ + +class Interruptible_wait +{ + THD *m_thd; + struct timespec m_abs_timeout; + static const ulonglong m_interrupt_interval; + + public: + Interruptible_wait(THD *thd) + : m_thd(thd) {} + + ~Interruptible_wait() {} + + public: + /** + Set the absolute timeout. + + @param timeout The amount of time in nanoseconds to wait + */ + void set_timeout(ulonglong timeout) + { + /* + Calculate the absolute system time at the start so it can + be controlled in slices. It relies on the fact that once + the absolute time passes, the timed wait call will fail + automatically with a timeout error. + */ + set_timespec_nsec(m_abs_timeout, timeout); + } + + /** The timed wait. */ + int wait(mysql_cond_t *, mysql_mutex_t *); +}; + + +/** Time to wait before polling the connection status. */ +const ulonglong Interruptible_wait::m_interrupt_interval= 5 * 1000000000ULL; + + +/** + Wait for a given condition to be signaled. + + @param cond The condition variable to wait on. + @param mutex The associated mutex. + + @remark The absolute timeout is preserved across calls. + + @retval return value from mysql_cond_timedwait +*/ + +int Interruptible_wait::wait(mysql_cond_t *cond, mysql_mutex_t *mutex) +{ + int error; + struct timespec timeout; + + while (1) + { + /* Wait for a fixed interval. */ + set_timespec_nsec(timeout, m_interrupt_interval); + + /* But only if not past the absolute timeout. */ + if (cmp_timespec(timeout, m_abs_timeout) > 0) + timeout= m_abs_timeout; + + error= mysql_cond_timedwait(cond, mutex, &timeout); + if (m_thd->check_killed()) + break; + if (error == ETIMEDOUT || error == ETIME) + { + /* Return error if timed out or connection is broken. */ + if (!cmp_timespec(timeout, m_abs_timeout) || !m_thd->is_connected()) + break; + } + /* Otherwise, propagate status to the caller. */ + else + break; + } + + return error; +} + + +/** + For locks with EXPLICIT duration, MDL returns a new ticket + every time a lock is granted. This allows to implement recursive + locks without extra allocation or additional data structures, such + as below. However, if there are too many tickets in the same + MDL_context, MDL_context::find_ticket() is getting too slow, + since it's using a linear search. + This is why a separate structure is allocated for a user + level lock, and before requesting a new lock from MDL, + GET_LOCK() checks thd->ull_hash if such lock is already granted, + and if so, simply increments a reference counter. +*/ + +class User_level_lock +{ +public: + MDL_ticket *lock; + int refs; +}; + + +/** Extract a hash key from User_level_lock. */ + +uchar *ull_get_key(const uchar *ptr, size_t *length, + my_bool not_used __attribute__((unused))) +{ + User_level_lock *ull = (User_level_lock*) ptr; + MDL_key *key = ull->lock->get_key(); + *length= key->length(); + return (uchar*) 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 */); +}; + +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 == 1); + String *res= args[0]->val_str(&value); + double timeout= args[1]->val_real(); + THD *thd= current_thd; + User_level_lock *ull; + DBUG_ENTER("Item_func_get_lock::val_int"); + + null_value= 1; + /* + In slave thread no need to get locks, everything is serialized. Anyway + there is no way to make GET_LOCK() work on slave like it did on master + (i.e. make it return exactly the same value) because we don't have the + same other concurrent threads environment. No matter what we return here, + it's not guaranteed to be same as on master. + */ + if (thd->slave_thread) + { + null_value= 0; + DBUG_RETURN(1); + } + + if (args[1]->null_value || + (!args[1]->unsigned_flag && ((longlong) timeout < 0))) + { + char buf[22]; + if (args[1]->null_value) + strmov(buf, "NULL"); + else + llstr(((longlong) timeout), buf); + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, + ER_WRONG_VALUE_FOR_TYPE, ER(ER_WRONG_VALUE_FOR_TYPE), + "timeout", buf, "get_lock"); + null_value= 1; + DBUG_RETURN(0); + } + + if (!ull_name_ok(res)) + DBUG_RETURN(0); + DBUG_PRINT("enter", ("lock: %.*s", res->length(), res->ptr())); + /* HASH entries are of type User_level_lock. */ + if (! my_hash_inited(&thd->ull_hash) && + my_hash_init(key_memory_User_level_lock, &thd->ull_hash, + &my_charset_bin, 16 /* small hash */, 0, 0, ull_get_key, + NULL, 0)) + { + DBUG_RETURN(0); + } + + MDL_request ull_request; + MDL_REQUEST_INIT(&ull_request, MDL_key::USER_LOCK, res->c_ptr_safe(), "", + MDL_SHARED_NO_WRITE, MDL_EXPLICIT); + MDL_key *ull_key= &ull_request.key; + + + if ((ull= (User_level_lock*) + my_hash_search(&thd->ull_hash, ull_key->ptr(), ull_key->length()))) + { + /* Recursive lock */ + ull->refs++; + null_value= 0; + DBUG_PRINT("info", ("recursive lock, ref-count: %d", (int) ull->refs)); + DBUG_RETURN(1); + } + + Lock_wait_timeout_handler lock_wait_timeout_handler; + thd->push_internal_handler(&lock_wait_timeout_handler); + bool error= thd->mdl_context.acquire_lock(&ull_request, timeout); + (void) thd->pop_internal_handler(); + if (unlikely(error)) + { + if (lock_wait_timeout_handler.m_lock_wait_timeout) + null_value= 0; + DBUG_RETURN(0); + } + + ull= (User_level_lock*) my_malloc(key_memory_User_level_lock, + sizeof(User_level_lock), + MYF(MY_WME|MY_THREAD_SPECIFIC)); + if (ull == NULL) + { + thd->mdl_context.release_lock(ull_request.ticket); + DBUG_RETURN(0); + } + + ull->lock= ull_request.ticket; + ull->refs= 1; + + if (my_hash_insert(&thd->ull_hash, (uchar*) ull)) + { + thd->mdl_context.release_lock(ull->lock); + my_free(ull); + DBUG_RETURN(0); + } + null_value= 0; + + DBUG_RETURN(1); +} + + +/** + Release all user level locks. + @return + - N if N-lock released + - 0 if lock wasn't held +*/ +longlong Item_func_release_all_locks::val_int() +{ + DBUG_ASSERT(fixed == 1); + 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 == 1); + 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 == 1); + 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 == 1); + 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 == 1); + 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 == 1); + 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 == 1); + + 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; + + 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; + + error= 0; + 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); + + DBUG_EXECUTE_IF("sleep_inject_query_done_debug_sync", { + debug_sync_set_action + (thd, STRING_WITH_LEN("dispatch_command_end SIGNAL query_done")); + };); + + return MY_TEST(!error); // Return 1 killed +} + + +bool Item_func_user_var::check_vcol_func_processor(void *arg) +{ + return mark_unsupported_function("@", name.str, arg, VCOL_NON_DETERMINISTIC); +} + +#define extra_size sizeof(double) + +user_var_entry *get_variable(HASH *hash, LEX_CSTRING *name, + bool create_if_not_exists) +{ + user_var_entry *entry; + + if (!(entry = (user_var_entry*) my_hash_search(hash, (uchar*) name->str, + name->length)) && + create_if_not_exists) + { + size_t size=ALIGN_SIZE(sizeof(user_var_entry))+name->length+1+extra_size; + if (!my_hash_inited(hash)) + return 0; + if (!(entry = (user_var_entry*) my_malloc(key_memory_user_var_entry, size, + MYF(MY_WME | ME_FATAL | + MY_THREAD_SPECIFIC)))) + return 0; + entry->name.str=(char*) entry+ ALIGN_SIZE(sizeof(user_var_entry))+ + extra_size; + entry->name.length=name->length; + entry->value=0; + entry->length=0; + entry->update_query_id=0; + entry->set_charset(NULL); + entry->unsigned_flag= 0; + /* + 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->type=STRING_RESULT; + memcpy((char*) entry->name.str, name->str, name->length+1); + if (my_hash_insert(hash,(uchar*) entry)) + { + my_free(entry); + return 0; + } + } + return entry; +} + + +void Item_func_set_user_var::cleanup() +{ + Item_func::cleanup(); + m_var_entry= NULL; +} + + +bool Item_func_set_user_var::set_entry(THD *thd, bool create_if_not_exists) +{ + if (m_var_entry && thd->thread_id == entry_thread_id) + goto end; // update entry->update_query_id for PS + if (!(m_var_entry= get_variable(&thd->user_vars, &name, create_if_not_exists))) + { + entry_thread_id= 0; + return TRUE; + } + entry_thread_id= thd->thread_id; + /* + Remember the last query which updated it, this way a query can later know + if this variable is a constant item in the query (it is if update_query_id + is different from query_id). + */ +end: + m_var_entry->update_query_id= thd->query_id; + return FALSE; +} + + +/* + When a user variable is updated (in a SET command or a query like + SELECT @a:= ). +*/ + +bool Item_func_set_user_var::fix_fields(THD *thd, Item **ref) +{ + DBUG_ASSERT(fixed == 0); + /* fix_fields will call Item_func_set_user_var::fix_length_and_dec */ + if (Item_func::fix_fields(thd, ref) || set_entry(thd, TRUE)) + return TRUE; + /* + As it is wrong and confusing to associate any + character set with NULL, @a should be latin2 + after this query sequence: + + SET @a=_latin2'string'; + SET @a=NULL; + + I.e. the second query should not change the charset + to the current default value, but should keep the + original value assigned during the first query. + In order to do it, we don't copy charset + from the argument if the argument is NULL + and the variable has previously been initialized. + */ + null_item= (args[0]->type() == NULL_ITEM); + if (!m_var_entry->charset() || !null_item) + m_var_entry->set_charset(args[0]->collation.derivation == DERIVATION_NUMERIC ? + &my_charset_numeric : args[0]->collation.collation); + collation.set(m_var_entry->charset(), + args[0]->collation.derivation == DERIVATION_NUMERIC ? + DERIVATION_NUMERIC : DERIVATION_IMPLICIT); + switch (args[0]->result_type()) { + case STRING_RESULT: + case TIME_RESULT: + set_handler(type_handler_long_blob. + type_handler_adjusted_to_max_octet_length(max_length, + collation.collation)); + break; + case REAL_RESULT: + set_handler(&type_handler_double); + break; + case INT_RESULT: + set_handler(Type_handler::type_handler_long_or_longlong(max_char_length(), + unsigned_flag)); + break; + case DECIMAL_RESULT: + set_handler(&type_handler_newdecimal); + break; + case ROW_RESULT: + DBUG_ASSERT(0); + set_handler(&type_handler_row); + break; + } + if (thd->lex->current_select) + { + /* + When this function is used in a derived table/view force the derived + table to be materialized to preserve possible side-effect of setting a + user variable. + */ + SELECT_LEX_UNIT *unit= thd->lex->current_select->master_unit(); + TABLE_LIST *derived; + for (derived= unit->derived; + derived; + derived= unit->derived) + { + derived->set_materialized_derived(); + derived->prohibit_cond_pushdown= true; + if (unit->with_element && unit->with_element->is_recursive) + break; + unit= derived->select_lex->master_unit(); + } + } + + return FALSE; +} + + +bool +Item_func_set_user_var::fix_length_and_dec() +{ + maybe_null=args[0]->maybe_null; + decimals=args[0]->decimals; + if (args[0]->collation.derivation == DERIVATION_NUMERIC) + { + collation.set(DERIVATION_NUMERIC); + fix_length_and_charset(args[0]->max_char_length(), &my_charset_numeric); + } + else + { + collation.set(DERIVATION_IMPLICIT); + fix_length_and_charset(args[0]->max_char_length(), + args[0]->collation.collation); + } + unsigned_flag= args[0]->unsigned_flag; + return FALSE; +} + + +/* + Mark field in read_map + + NOTES + This is used by filesort to register used fields in a a temporary + column read set or to register used fields in a view +*/ + +bool Item_func_set_user_var::register_field_in_read_map(void *arg) +{ + if (result_field) + { + TABLE *table= (TABLE *) arg; + if (result_field->table == table || !table) + bitmap_set_bit(result_field->table->read_set, result_field->field_index); + if (result_field->vcol_info) + return result_field->vcol_info-> + expr->walk(&Item::register_field_in_read_map, 1, arg); + } + return 0; +} + +/* + Mark field in bitmap supplied as *arg + +*/ + +bool Item_func_set_user_var::register_field_in_bitmap(void *arg) +{ + MY_BITMAP *bitmap = (MY_BITMAP *) arg; + DBUG_ASSERT(bitmap); + if (result_field) + { + if (!bitmap) + return 1; + bitmap_set_bit(bitmap, result_field->field_index); + } + return 0; +} + +/** + Set value to user variable. + + @param entry pointer to structure representing variable + @param set_null should we set NULL value ? + @param ptr pointer to buffer with new value + @param length length of new value + @param type type of new value + @param cs charset info for new value + @param dv derivation for new value + @param unsigned_arg indicates if a value of type INT_RESULT is unsigned + + @note Sets error and fatal error if allocation fails. + + @retval + false success + @retval + true failure +*/ + +bool +update_hash(user_var_entry *entry, bool set_null, void *ptr, size_t length, + Item_result type, CHARSET_INFO *cs, + bool unsigned_arg) +{ + 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 (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 (type == STRING_RESULT) + { + length--; // Fix length change above + entry->value[length]= 0; // Store end \0 + } + if (length) + memmove(entry->value, ptr, length); + if (type == DECIMAL_RESULT) + ((my_decimal*)entry->value)->fix_buffer_pointer(); + entry->length= length; + entry->set_charset(cs); + entry->unsigned_flag= unsigned_arg; + } + entry->type=type; +#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, + Item_result res_type, + CHARSET_INFO *cs, + bool unsigned_arg) +{ + /* + 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) + res_type= m_var_entry->type; // Don't change type of item + if (::update_hash(m_var_entry, null_value, + ptr, length, res_type, cs, unsigned_arg)) + { + 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) { + 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) { + 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) { + case REAL_RESULT: + str->set_real(*(double*) value, decimals, charset()); + break; + case INT_RESULT: + if (!unsigned_flag) + 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) { + 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), + REAL_RESULT, &my_charset_numeric, 0); + break; + } + case INT_RESULT: + { + res= update_hash((void*) &save_result.vint, sizeof(save_result.vint), + INT_RESULT, &my_charset_numeric, unsigned_flag); + break; + } + case STRING_RESULT: + { + if (!save_result.vstr) // Null value + res= update_hash((void*) 0, 0, STRING_RESULT, &my_charset_bin, 0); + else + res= update_hash((void*) save_result.vstr->ptr(), + save_result.vstr->length(), STRING_RESULT, + save_result.vstr->charset(), 0); + break; + } + case DECIMAL_RESULT: + { + if (!save_result.vdec) // Null value + res= update_hash((void*) 0, 0, DECIMAL_RESULT, &my_charset_bin, 0); + else + res= update_hash((void*) save_result.vdec, + sizeof(my_decimal), DECIMAL_RESULT, + &my_charset_numeric, 0); + 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 == 1); + check(0); + update(); // Store expression + return m_var_entry->val_real(&null_value); +} + +longlong Item_func_set_user_var::val_int() +{ + DBUG_ASSERT(fixed == 1); + 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 == 1); + 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 == 1); + 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 == 1); + 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 == 1); + 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 == 1); + 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 == 1); + 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 == 1); + 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 == 1); + 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_quick(buff, sizeof(buff), cs); + result= m_var_entry->val_str(&null_value, &str_value, decimals); + + if (null_value) + { + str_value.set_quick(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_quick(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 == 1); + 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 == 1); + 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 == 1); + 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 == 1); + 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->type= var_entry->type; + user_var_event->charset_number= var_entry->charset()->number; + user_var_event->unsigned_flag= var_entry->unsigned_flag; + 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=current_thd; + int error; + maybe_null=1; + 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->unsigned_flag; + max_length= (uint32)m_var_entry->length; + switch (m_var_entry->type) { + case REAL_RESULT: + collation.set(&my_charset_numeric, DERIVATION_NUMERIC); + fix_char_length(DBL_DIG + 8); + set_handler(&type_handler_double); + break; + case INT_RESULT: + collation.set(&my_charset_numeric, DERIVATION_NUMERIC); + fix_char_length(MAX_BIGINT_WIDTH); + decimals=0; + if (unsigned_flag) + set_handler(&type_handler_ulonglong); + else + set_handler(&type_handler_slonglong); + break; + case STRING_RESULT: + collation.set(m_var_entry->charset(), DERIVATION_IMPLICIT); + max_length= MAX_BLOB_WIDTH - 1; + set_handler(&type_handler_long_blob); + break; + case DECIMAL_RESULT: + collation.set(&my_charset_numeric, DERIVATION_NUMERIC); + fix_char_length(DECIMAL_MAX_STR_LENGTH); + decimals= DECIMAL_MAX_SCALE; + set_handler(&type_handler_newdecimal); + break; + case ROW_RESULT: // Keep compiler happy + case TIME_RESULT: + DBUG_ASSERT(0); // This case should never be chosen + break; + } + } + else + { + collation.set(&my_charset_bin, DERIVATION_IMPLICIT); + null_value= 1; + set_handler(&type_handler_long_blob); + max_length= MAX_BLOB_WIDTH; + } + return false; +} + + +bool Item_func_get_user_var::const_item() const +{ + return (!m_var_entry || + current_thd->query_id != m_var_entry->update_query_id); +} + + +void Item_func_get_user_var::print(String *str, enum_query_type query_type) +{ + str->append(STRING_WITH_LEN("@")); + append_identifier(current_thd, str, &name); +} + + +bool Item_func_get_user_var::eq(const Item *item, bool binary_cmp) const +{ + /* Assume we don't have rtti */ + if (this == item) + return 1; // Same item is same. + /* Check if other type is also a get_user_var() object */ + if (item->type() != FUNC_ITEM || + ((Item_func*) item)->functype() != functype()) + return 0; + Item_func_get_user_var *other=(Item_func_get_user_var*) item; + return (name.length == other->name.length && + !memcmp(name.str, other->name.str, name.length)); +} + + +bool Item_func_get_user_var::set_value(THD *thd, + sp_rcontext * /*ctx*/, Item **it) +{ + LEX_CSTRING tmp_name= get_name(); + Item_func_set_user_var *suv= new (thd->mem_root) Item_func_set_user_var(thd, &tmp_name, *it); + /* + Item_func_set_user_var is not fixed after construction, call + fix_fields(). + */ + return (!suv || suv->fix_fields(thd, it) || suv->check(0) || suv->update()); +} + + +bool Item_user_var_as_out_param::fix_fields(THD *thd, Item **ref) +{ + DBUG_ASSERT(!is_fixed()); + DBUG_ASSERT(thd->lex->exchange); + if (!(entry= get_variable(&thd->user_vars, &org_name, 1))) + return TRUE; + entry->type= STRING_RESULT; + /* + 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, STRING_RESULT, cs, 0 /* unsigned_arg */); +} + + +void Item_user_var_as_out_param::set_value(const char *str, uint length, + CHARSET_INFO* cs) +{ + ::update_hash(entry, FALSE, (void*)str, length, STRING_RESULT, cs, + 0 /* unsigned_arg */); +} + + +double Item_user_var_as_out_param::val_real() +{ + DBUG_ASSERT(0); + return 0.0; +} + + +longlong Item_user_var_as_out_param::val_int() +{ + DBUG_ASSERT(0); + return 0; +} + + +String* Item_user_var_as_out_param::val_str(String *str) +{ + DBUG_ASSERT(0); + return 0; +} + + +my_decimal* Item_user_var_as_out_param::val_decimal(my_decimal *decimal_buffer) +{ + DBUG_ASSERT(0); + return 0; +} + + +bool Item_user_var_as_out_param::get_date(THD *thd, MYSQL_TIME *ltime, + date_mode_t fuzzydate) +{ + DBUG_ASSERT(0); + return true; +} + + +void Item_user_var_as_out_param::load_data_print_for_log_event(THD *thd, + String *str) + const +{ + str->append('@'); + append_identifier(thd, str, &org_name); +} + + +Item_func_get_system_var:: +Item_func_get_system_var(THD *thd, sys_var *var_arg, enum_var_type var_type_arg, + LEX_CSTRING *component_arg, const char *name_arg, + size_t name_len_arg): + Item_func(thd), var(var_arg), var_type(var_type_arg), + orig_var_type(var_type_arg), component(*component_arg), cache_present(0) +{ + /* set_name() will allocate the name */ + set_name(thd, name_arg, (uint) name_len_arg, system_charset_info); +} + + +bool Item_func_get_system_var::is_written_to_binlog() +{ + return var->is_written_to_binlog(var_type); +} + + +void Item_func_get_system_var::update_null_value() +{ + THD *thd= current_thd; + int save_no_errors= thd->no_errors; + thd->no_errors= TRUE; + type_handler()->Item_update_null_value(this); + thd->no_errors= save_no_errors; +} + + +bool Item_func_get_system_var::fix_length_and_dec() +{ + const char *cptr; + maybe_null= TRUE; + max_length= 0; + + if (var->check_type(var_type)) + { + if (var_type != OPT_DEFAULT) + { + my_error(ER_INCORRECT_GLOBAL_LOCAL_VAR, MYF(0), + var->name.str, var_type == OPT_GLOBAL ? "SESSION" : "GLOBAL"); + return TRUE; + } + /* As there was no local variable, return the global value */ + var_type= OPT_GLOBAL; + } + + switch (var->show_type()) + { + case SHOW_HA_ROWS: + case SHOW_UINT: + case SHOW_ULONG: + case SHOW_ULONGLONG: + unsigned_flag= TRUE; + /* fall through */ + case SHOW_SINT: + case SHOW_SLONG: + case SHOW_SLONGLONG: + collation= DTCollation_numeric(); + fix_char_length(MY_INT64_NUM_DECIMAL_DIGITS); + decimals=0; + break; + case SHOW_CHAR: + case SHOW_CHAR_PTR: + mysql_mutex_lock(&LOCK_global_system_variables); + cptr= var->show_type() == SHOW_CHAR ? + reinterpret_cast<const char*>(var->value_ptr(current_thd, var_type, + &component)) : + *reinterpret_cast<const char* const*>(var->value_ptr(current_thd, + var_type, + &component)); + if (cptr) + max_length= (uint32) system_charset_info->numchars(cptr, + cptr + strlen(cptr)); + mysql_mutex_unlock(&LOCK_global_system_variables); + collation.set(system_charset_info, DERIVATION_SYSCONST); + max_length*= system_charset_info->mbmaxlen; + decimals=NOT_FIXED_DEC; + break; + case SHOW_LEX_STRING: + { + mysql_mutex_lock(&LOCK_global_system_variables); + const LEX_STRING *ls= + reinterpret_cast<const LEX_STRING*>(var->value_ptr(current_thd, + var_type, + &component)); + max_length= (uint32) system_charset_info->numchars(ls->str, + ls->str + ls->length); + mysql_mutex_unlock(&LOCK_global_system_variables); + collation.set(system_charset_info, DERIVATION_SYSCONST); + max_length*= system_charset_info->mbmaxlen; + decimals=NOT_FIXED_DEC; + } + break; + case SHOW_BOOL: + case SHOW_MY_BOOL: + collation= DTCollation_numeric(); + fix_char_length(1); + decimals=0; + break; + case SHOW_DOUBLE: + decimals= 6; + collation= DTCollation_numeric(); + fix_char_length(DBL_DIG + 6); + break; + default: + my_error(ER_VAR_CANT_BE_READ, MYF(0), var->name.str); + break; + } + return FALSE; +} + + +void Item_func_get_system_var::print(String *str, enum_query_type query_type) +{ + if (name.length) + str->append(&name); + else + { + str->append(STRING_WITH_LEN("@@")); + if (component.length) + { + str->append(&component); + str->append('.'); + } + else if (var_type == SHOW_OPT_GLOBAL && var->scope() != sys_var::GLOBAL) + { + str->append(STRING_WITH_LEN("global.")); + } + str->append(&var->name); + } +} + +bool Item_func_get_system_var::check_vcol_func_processor(void *arg) +{ + return mark_unsupported_function("@@", var->name.str, arg, VCOL_SESSION_FUNC); +} + + +const Type_handler *Item_func_get_system_var::type_handler() const +{ + switch (var->show_type()) + { + case SHOW_BOOL: + case SHOW_MY_BOOL: + case SHOW_SINT: + case SHOW_SLONG: + case SHOW_SLONGLONG: + return &type_handler_slonglong; + case SHOW_UINT: + case SHOW_ULONG: + case SHOW_ULONGLONG: + case SHOW_HA_ROWS: + return &type_handler_ulonglong; + case SHOW_CHAR: + case SHOW_CHAR_PTR: + case SHOW_LEX_STRING: + return &type_handler_varchar; + case SHOW_DOUBLE: + return &type_handler_double; + default: + my_error(ER_VAR_CANT_BE_READ, MYF(0), var->name.str); + return &type_handler_varchar; // keep the compiler happy + } +} + + +longlong Item_func_get_system_var::val_int() +{ + THD *thd= current_thd; + + DBUG_EXECUTE_IF("simulate_non_gtid_aware_master", + { + if (0 == strcmp("gtid_domain_id", var->name.str)) + { + my_error(ER_VAR_CANT_BE_READ, MYF(0), var->name.str); + return 0; + } + }); + if (cache_present && thd->query_id == used_query_id) + { + if (cache_present & GET_SYS_VAR_CACHE_LONG) + { + null_value= cached_null_value; + return cached_llval; + } + else if (cache_present & GET_SYS_VAR_CACHE_DOUBLE) + { + null_value= cached_null_value; + cached_llval= (longlong) cached_dval; + cache_present|= GET_SYS_VAR_CACHE_LONG; + return cached_llval; + } + else if (cache_present & GET_SYS_VAR_CACHE_STRING) + { + null_value= cached_null_value; + if (!null_value) + cached_llval= longlong_from_string_with_check(&cached_strval); + else + cached_llval= 0; + cache_present|= GET_SYS_VAR_CACHE_LONG; + return cached_llval; + } + } + + cached_llval= var->val_int(&null_value, thd, var_type, &component); + cache_present |= GET_SYS_VAR_CACHE_LONG; + used_query_id= thd->query_id; + cached_null_value= null_value; + return cached_llval; +} + + +String* Item_func_get_system_var::val_str(String* str) +{ + THD *thd= current_thd; + + if (cache_present && thd->query_id == used_query_id) + { + if (cache_present & GET_SYS_VAR_CACHE_STRING) + { + null_value= cached_null_value; + return null_value ? NULL : &cached_strval; + } + else if (cache_present & GET_SYS_VAR_CACHE_LONG) + { + null_value= cached_null_value; + if (!null_value) + cached_strval.set (cached_llval, collation.collation); + cache_present|= GET_SYS_VAR_CACHE_STRING; + return null_value ? NULL : &cached_strval; + } + else if (cache_present & GET_SYS_VAR_CACHE_DOUBLE) + { + null_value= cached_null_value; + if (!null_value) + cached_strval.set_real (cached_dval, decimals, collation.collation); + cache_present|= GET_SYS_VAR_CACHE_STRING; + return null_value ? NULL : &cached_strval; + } + } + + str= var->val_str(&cached_strval, thd, var_type, &component); + cache_present|= GET_SYS_VAR_CACHE_STRING; + used_query_id= thd->query_id; + cached_null_value= null_value= !str; + return str; +} + + +double Item_func_get_system_var::val_real() +{ + THD *thd= current_thd; + + if (cache_present && thd->query_id == used_query_id) + { + if (cache_present & GET_SYS_VAR_CACHE_DOUBLE) + { + null_value= cached_null_value; + return cached_dval; + } + else if (cache_present & GET_SYS_VAR_CACHE_LONG) + { + null_value= cached_null_value; + cached_dval= (double)cached_llval; + cache_present|= GET_SYS_VAR_CACHE_DOUBLE; + return cached_dval; + } + else if (cache_present & GET_SYS_VAR_CACHE_STRING) + { + null_value= cached_null_value; + if (!null_value) + cached_dval= double_from_string_with_check(&cached_strval); + else + cached_dval= 0; + cache_present|= GET_SYS_VAR_CACHE_DOUBLE; + return cached_dval; + } + } + + cached_dval= var->val_real(&null_value, thd, var_type, &component); + cache_present |= GET_SYS_VAR_CACHE_DOUBLE; + used_query_id= thd->query_id; + cached_null_value= null_value; + return cached_dval; +} + + +bool Item_func_get_system_var::eq(const Item *item, bool binary_cmp) const +{ + /* Assume we don't have rtti */ + if (this == item) + return 1; // Same item is same. + /* Check if other type is also a get_user_var() object */ + if (item->type() != FUNC_ITEM || + ((Item_func*) item)->functype() != functype()) + return 0; + Item_func_get_system_var *other=(Item_func_get_system_var*) item; + return (var == other->var && var_type == other->var_type); +} + + +void Item_func_get_system_var::cleanup() +{ + Item_func::cleanup(); + cache_present= 0; + var_type= orig_var_type; + cached_strval.free(); +} + +/** + @retval + 0 ok + 1 OOM error +*/ + +bool Item_func_match::init_search(THD *thd, bool no_order) +{ + DBUG_ENTER("Item_func_match::init_search"); + + if (!table->file->is_open()) + DBUG_RETURN(0); + + /* Check if init_search() has been called before */ + if (ft_handler) + { + if (join_key) + table->file->ft_handler= ft_handler; + DBUG_RETURN(0); + } + + if (key == NO_SUCH_KEY) + { + List<Item> fields; + fields.push_back(new (thd->mem_root) + Item_string(thd, " ", 1, cmp_collation.collation), + thd->mem_root); + for (uint i= 1; i < arg_count; i++) + fields.push_back(args[i]); + concat_ws= new (thd->mem_root) Item_func_concat_ws(thd, fields); + if (unlikely(thd->is_fatal_error)) + DBUG_RETURN(1); // OOM in new or push_back + /* + Above function used only to get value and do not need fix_fields for it: + Item_string - basic constant + fields - fix_fields() was already called for this arguments + Item_func_concat_ws - do not need fix_fields() to produce value + */ + concat_ws->quick_fix_field(); + } + + if (master) + { + join_key= master->join_key= join_key | master->join_key; + if (master->init_search(thd, no_order)) + DBUG_RETURN(1); + ft_handler= master->ft_handler; + join_key= master->join_key; + DBUG_RETURN(0); + } + + String *ft_tmp= 0; + + // MATCH ... AGAINST (NULL) is meaningless, but possible + if (!(ft_tmp=key_item()->val_str(&value))) + { + ft_tmp= &value; + value.set("", 0, cmp_collation.collation); + } + + if (ft_tmp->charset() != cmp_collation.collation) + { + uint dummy_errors; + if (search_value.copy(ft_tmp->ptr(), ft_tmp->length(), ft_tmp->charset(), + cmp_collation.collation, &dummy_errors)) + DBUG_RETURN(1); + ft_tmp= &search_value; + } + + if (join_key && !no_order) + flags|=FT_SORTED; + + if (key != NO_SUCH_KEY) + THD_STAGE_INFO(table->in_use, stage_fulltext_initialization); + + ft_handler= table->file->ft_init_ext(flags, key, ft_tmp); + + 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); + + maybe_null=1; + 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]= args[i]->real_item(); + /* + When running in PS mode, some Item_field's can already be replaced + to Item_func_conv_charset during PREPARE time. This is possible + in case of "MATCH (f1,..,fN) AGAINST (... IN BOOLEAN MODE)" + when running without any fulltext indexes and when fields f1..fN + have different character sets. + So we check for FIELD_ITEM only during prepare time and in non-PS mode, + and do not check in PS execute time. + */ + if (!thd->stmt_arena->is_stmt_execute() && + item->type() != Item::FIELD_ITEM) + { + my_error(ER_WRONG_ARGUMENTS, MYF(0), "MATCH"); + return TRUE; + } + /* + During the prepare-time execution of fix_fields() of a PS query some + Item_fields's could have been already replaced to Item_func_conv_charset + (by the call for agg_arg_charsets_for_comparison below()). + But agg_arg_charsets_for_comparison() is written in a way that + at least *one* of the Item_field's is not replaced. + This makes sure that "table" gets initialized during PS execution time. + */ + if (item->type() == Item::FIELD_ITEM) + table= ((Item_field *)item)->field->table; + + allows_multi_table_search &= allows_search_on_non_indexed_columns(table); + } + + /* + Check that all columns come from the same table. + We've already checked that columns in MATCH are fields so + PARAM_TABLE_BIT can only appear from AGAINST argument. + */ + if ((used_tables_cache & ~PARAM_TABLE_BIT) != item->used_tables()) + key=NO_SUCH_KEY; + + if (key == NO_SUCH_KEY && !allows_multi_table_search) + { + my_error(ER_WRONG_ARGUMENTS,MYF(0),"MATCH"); + return TRUE; + } + if (!(table->file->ha_table_flags() & HA_CAN_FULLTEXT)) + { + my_error(ER_TABLE_CANT_HANDLE_FT, MYF(0), table->file->table_type()); + return 1; + } + table->fulltext_searched=1; + return agg_arg_charsets_for_comparison(cmp_collation, args+1, arg_count-1); +} + +bool Item_func_match::fix_index() +{ + Item_field *item; + uint ft_to_key[MAX_KEY], ft_cnt[MAX_KEY], fts=0, keynr; + uint max_cnt=0, mkeys=0, i; + + /* + We will skip execution if the item is not fixed + with fix_field + */ + if (!fixed) + return false; + + if (key == NO_SUCH_KEY) + return 0; + + if (!table) + goto err; + + for (keynr=0 ; keynr < table->s->keys ; keynr++) + { + if ((table->key_info[keynr].flags & HA_FULLTEXT) && + (flags & FT_BOOL ? table->keys_in_use_for_query.is_set(keynr) : + table->s->keys_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++) + { + if (args[i]->type() != FIELD_ITEM) + goto err; + item=(Item_field*)args[i]; + for (keynr=0 ; keynr < fts ; keynr++) + { + KEY *ft_key=&table->key_info[ft_to_key[keynr]]; + uint key_parts=ft_key->user_defined_key_parts; + + for (uint part=0 ; part < key_parts ; part++) + { + if (item->field->eq(ft_key->key_part[part].field)) + ft_cnt[keynr]++; + } + } + } + + for (keynr=0 ; keynr < fts ; keynr++) + { + if (ft_cnt[keynr] > max_cnt) + { + mkeys=0; + max_cnt=ft_cnt[mkeys]=ft_cnt[keynr]; + ft_to_key[mkeys]=ft_to_key[keynr]; + continue; + } + if (max_cnt && ft_cnt[keynr] == max_cnt) + { + mkeys++; + ft_cnt[mkeys]=ft_cnt[keynr]; + ft_to_key[mkeys]=ft_to_key[keynr]; + continue; + } + } + + for (keynr=0 ; keynr <= mkeys ; keynr++) + { + // partial keys doesn't work + if (max_cnt < arg_count-1 || + max_cnt < table->key_info[ft_to_key[keynr]].user_defined_key_parts) + continue; + + key=ft_to_key[keynr]; + + return 0; + } + +err: + if (allows_search_on_non_indexed_columns(table)) + { + key=NO_SUCH_KEY; + return 0; + } + my_message(ER_FT_MATCHING_KEY_NOT_FOUND, + ER(ER_FT_MATCHING_KEY_NOT_FOUND), MYF(0)); + return 1; +} + + +bool Item_func_match::eq(const Item *item, bool binary_cmp) const +{ + if (item->type() != FUNC_ITEM || + ((Item_func*)item)->functype() != FT_FUNC || + flags != ((Item_func_match*)item)->flags) + return 0; + + Item_func_match *ifm=(Item_func_match*) item; + + if (key == ifm->key && table == ifm->table && + key_item()->eq(ifm->key_item(), binary_cmp)) + return 1; + + return 0; +} + + +double Item_func_match::val_real() +{ + DBUG_ASSERT(fixed == 1); + 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 (flags & FT_BOOL) + str->append(STRING_WITH_LEN(" in boolean mode")); + else if (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 + { + DBUG_ASSERT(item->is_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 + { + DBUG_ASSERT(item->is_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() +{ + 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 == 1); + 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) +{ + maybe_null= 1; +} + + +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) +{ + maybe_null= 1; +} + + +void +Item_func_sp::cleanup() +{ + Item_sp::cleanup(); + Item_func::cleanup(); +} + +const char * +Item_func_sp::func_name() const +{ + THD *thd= current_thd; + return Item_sp::func_name(thd); +} + + +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() +{ + 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; + maybe_null= 1; + + 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 == 1); + return current_thd->found_rows(); +} + + +longlong Item_func_oracle_sql_rowcount::val_int() +{ + DBUG_ASSERT(fixed == 1); + THD *thd= current_thd; + /* + In case when a query like this: + INSERT a INTO @va FROM t1; + returns multiple rows, SQL%ROWCOUNT should report 1 rather than -1. + */ + longlong rows= thd->get_row_count_func(); + return rows != -1 ? rows : // ROW_COUNT() + thd->found_rows(); // FOUND_ROWS() +} + + +longlong Item_func_sqlcode::val_int() +{ + DBUG_ASSERT(fixed); + DBUG_ASSERT(!null_value); + Diagnostics_area::Sql_condition_iterator it= + current_thd->get_stmt_da()->sql_conditions(); + const Sql_condition *err; + if ((err= it++)) + return err->get_sql_errno(); + return 0; +} + + +bool +Item_func_sp::fix_fields(THD *thd, Item **ref) +{ + bool res; + DBUG_ENTER("Item_func_sp::fix_fields"); + DBUG_ASSERT(fixed == 0); + sp_head *sp= m_handler->sp_find_routine(thd, m_name, true); + + /* + Checking privileges to execute the function while creating view and + executing the function of select. + */ + if (!(thd->lex->context_analysis_only & CONTEXT_ANALYSIS_ONLY_VIEW) || + (thd->lex->sql_command == SQLCOM_CREATE_VIEW)) + { + Security_context *save_security_ctx= thd->security_ctx; + if (context && context->security_ctx) + thd->security_ctx= context->security_ctx; + + /* + If the routine is not found, let's still check EXECUTE_ACL to decide + whether to return "Access denied" or "Routine does not exist". + */ + res= sp ? sp->check_execute_access(thd) : + check_routine_access(thd, EXECUTE_ACL, &m_name->m_db, + &m_name->m_name, + &sp_handler_function, false); + thd->security_ctx= save_security_ctx; + + if (res) + { + process_error(thd); + DBUG_RETURN(res); + } + } + + + /* Custom aggregates are transformed into an Item_sum_sp. We can not do this + earlier as we have no way of knowing what kind of Item we should create + when parsing the query. + + TODO(cvicentiu): See if this limitation can be lifted. + */ + + DBUG_ASSERT(m_sp == NULL); + if (!(m_sp= sp)) + { + my_missing_function_error(m_name->m_name, ErrConvDQName(m_name).ptr()); + process_error(thd); + DBUG_RETURN(TRUE); + } + + /* + We must call init_result_field before Item_func::fix_fields() + to make m_sp and result_field members available to fix_length_and_dec(), + which is called from Item_func::fix_fields(). + */ + res= init_result_field(thd, max_length, maybe_null, &null_value, &name); + + if (res) + DBUG_RETURN(TRUE); + + if (m_sp->agg_type() == GROUP_AGGREGATE) + { + Item_sum_sp *item_sp; + Query_arena *arena, backup; + arena= thd->activate_stmt_arena_if_needed(&backup); + + if (arg_count) + { + List<Item> list; + for (uint i= 0; i < arg_count; i++) + list.push_back(args[i]); + item_sp= new (thd->mem_root) Item_sum_sp(thd, context, m_name, sp, list); + } + else + item_sp= new (thd->mem_root) Item_sum_sp(thd, context, m_name, sp); + + if (arena) + thd->restore_active_arena(arena, &backup); + if (!item_sp) + DBUG_RETURN(TRUE); + *ref= item_sp; + item_sp->name= name; + bool err= item_sp->fix_fields(thd, ref); + if (err) + DBUG_RETURN(TRUE); + + DBUG_RETURN(FALSE); + } + + res= Item_func::fix_fields(thd, ref); + + if (res) + DBUG_RETURN(TRUE); + + if (thd->lex->is_view_context_analysis()) + { + /* + Here we check privileges of the stored routine only during view + creation, in order to validate the view. A runtime check is + performed in Item_func_sp::execute(), and this method is not + called during context analysis. Notice, that during view + creation we do not infer into stored routine bodies and do not + check privileges of its statements, which would probably be a + good idea especially if the view has SQL SECURITY DEFINER and + the used stored procedure has SQL SECURITY DEFINER. + */ + res= sp_check_access(thd); +#ifndef NO_EMBEDDED_ACCESS_CHECKS + /* + Try to set and restore the security context to see whether it's valid + */ + Security_context *save_secutiry_ctx; + res= set_routine_security_ctx(thd, m_sp, &save_secutiry_ctx); + if (!res) + m_sp->m_security_ctx.restore_security_context(thd, save_secutiry_ctx); + +#endif /* ! NO_EMBEDDED_ACCESS_CHECKS */ + } + + if (!m_sp->detistic()) + { + used_tables_cache |= RAND_TABLE_BIT; + const_item_cache= FALSE; + } + + DBUG_RETURN(res); +} + + +void Item_func_sp::update_used_tables() +{ + Item_func::update_used_tables(); + + if (!m_sp->detistic()) + { + used_tables_cache |= RAND_TABLE_BIT; + const_item_cache= FALSE; + } +} + +bool Item_func_sp::check_vcol_func_processor(void *arg) +{ + return mark_unsupported_function(func_name(), "()", arg, VCOL_IMPOSSIBLE); +} + +/* + uuid_short handling. + + The short uuid is defined as a longlong that contains the following bytes: + + Bytes Comment + 1 Server_id & 255 + 4 Startup time of server in seconds + 3 Incrementor + + This means that an uuid is guaranteed to be unique + even in a replication environment if the following holds: + + - The last byte of the server id is unique + - If you between two shutdown of the server don't get more than + an average of 2^24 = 16M calls to uuid_short() per second. +*/ + +ulonglong uuid_value; + +void uuid_short_init() +{ + uuid_value= ((((ulonglong) global_system_variables.server_id) << 56) + + (((ulonglong) server_start_time) << 24)); +} + + +longlong Item_func_uuid_short::val_int() +{ + ulonglong val; + mysql_mutex_lock(&LOCK_short_uuid_generator); + val= uuid_value++; + mysql_mutex_unlock(&LOCK_short_uuid_generator); + return (longlong) val; +} + + +/** + Last_value - return last argument. +*/ + +void Item_func_last_value::evaluate_sideeffects() +{ + DBUG_ASSERT(fixed == 1 && arg_count > 0); + for (uint i= 0; i < arg_count-1 ; i++) + args[i]->val_int(); +} + +String *Item_func_last_value::val_str(String *str) +{ + String *tmp; + evaluate_sideeffects(); + tmp= last_value->val_str(str); + null_value= last_value->null_value; + return tmp; +} + + +bool Item_func_last_value::val_native(THD *thd, Native *to) +{ + evaluate_sideeffects(); + return val_native_from_item(thd, last_value, to); +} + + +longlong Item_func_last_value::val_int() +{ + longlong tmp; + evaluate_sideeffects(); + tmp= last_value->val_int(); + null_value= last_value->null_value; + return tmp; +} + +double Item_func_last_value::val_real() +{ + double tmp; + evaluate_sideeffects(); + tmp= last_value->val_real(); + null_value= last_value->null_value; + return tmp; +} + +my_decimal *Item_func_last_value::val_decimal(my_decimal *decimal_value) +{ + my_decimal *tmp; + evaluate_sideeffects(); + tmp= last_value->val_decimal(decimal_value); + null_value= last_value->null_value; + return tmp; +} + + +bool Item_func_last_value::get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) +{ + evaluate_sideeffects(); + bool tmp= last_value->get_date(thd, ltime, fuzzydate); + null_value= last_value->null_value; + return tmp; +} + + +bool Item_func_last_value::fix_length_and_dec() +{ + last_value= args[arg_count -1]; + Type_std_attributes::set(last_value); + maybe_null= last_value->maybe_null; + return FALSE; +} + + +void Cursor_ref::print_func(String *str, const char *func_name) +{ + append_identifier(current_thd, str, &m_cursor_name); + str->append(func_name); +} + + +sp_cursor *Cursor_ref::get_open_cursor_or_error() +{ + THD *thd= current_thd; + sp_cursor *c= thd->spcont->get_cursor(m_cursor_offset); + DBUG_ASSERT(c); + if (!c/*safety*/ || !c->is_open()) + { + my_message(ER_SP_CURSOR_NOT_OPEN, ER_THD(thd, ER_SP_CURSOR_NOT_OPEN), + MYF(0)); + return NULL; + } + return c; +} + + +longlong Item_func_cursor_isopen::val_int() +{ + sp_cursor *c= current_thd->spcont->get_cursor(m_cursor_offset); + DBUG_ASSERT(c != NULL); + return c ? c->is_open() : 0; +} + + +longlong Item_func_cursor_found::val_int() +{ + sp_cursor *c= get_open_cursor_or_error(); + return !(null_value= (!c || c->fetch_count() == 0)) && c->found(); +} + + +longlong Item_func_cursor_notfound::val_int() +{ + sp_cursor *c= get_open_cursor_or_error(); + return !(null_value= (!c || c->fetch_count() == 0)) && !c->found(); +} + + +longlong Item_func_cursor_rowcount::val_int() +{ + sp_cursor *c= get_open_cursor_or_error(); + return !(null_value= !c) ? c->row_count() : 0; +} + +/***************************************************************************** + SEQUENCE functions +*****************************************************************************/ + +longlong Item_func_nextval::val_int() +{ + longlong value; + int error; + const char *key; + uint length= get_table_def_key(table_list, &key); + THD *thd; + SEQUENCE_LAST_VALUE *entry; + char buff[80]; + String key_buff(buff,sizeof(buff), &my_charset_bin); + DBUG_ENTER("Item_func_nextval::val_int"); + update_table(); + DBUG_ASSERT(table && table->s->sequence); + thd= table->in_use; + + if (thd->count_cuted_fields == CHECK_FIELD_EXPRESSION) + { + /* Alter table checking if function works */ + null_value= 0; + DBUG_RETURN(0); + } + + if (table->s->tmp_table != NO_TMP_TABLE) + { + /* + Temporary tables has an extra \0 at end to distinguish it from + normal tables + */ + key_buff.copy(key, length, &my_charset_bin); + key_buff.append((char) 0); + key= key_buff.ptr(); + length++; + } + + if (!(entry= ((SEQUENCE_LAST_VALUE*) + my_hash_search(&thd->sequences, (uchar*) key, length)))) + { + if (!(key= (char*) my_memdup(PSI_INSTRUMENT_ME, key, length, MYF(MY_WME))) || + !(entry= new SEQUENCE_LAST_VALUE((uchar*) key, length))) + { + /* EOM, error given */ + my_free((char*) key); + delete entry; + null_value= 1; + DBUG_RETURN(0); + } + if (my_hash_insert(&thd->sequences, (uchar*) entry)) + { + /* EOM, error given */ + delete entry; + null_value= 1; + DBUG_RETURN(0); + } + } + entry->null_value= null_value= 0; + value= table->s->sequence->next_value(table, 0, &error); + entry->value= value; + entry->set_version(table); + + if (unlikely(error)) // Warning already printed + entry->null_value= null_value= 1; // For not strict mode + DBUG_RETURN(value); +} + + +/* Print for nextval and lastval */ + +void Item_func_nextval::print(String *str, enum_query_type query_type) +{ + char d_name_buff[MAX_ALIAS_NAME], t_name_buff[MAX_ALIAS_NAME]; + LEX_CSTRING d_name= table_list->db; + LEX_CSTRING t_name= table_list->table_name; + bool use_db_name= d_name.str && d_name.str[0]; + THD *thd= current_thd; // Don't trust 'table' + + str->append(func_name()); + str->append('('); + + /* + for next_val we assume that table_list has been updated to contain + the current db. + */ + + if (lower_case_table_names > 0) + { + strmake(t_name_buff, t_name.str, MAX_ALIAS_NAME-1); + t_name.length= my_casedn_str(files_charset_info, t_name_buff); + t_name.str= t_name_buff; + if (use_db_name) + { + strmake(d_name_buff, d_name.str, MAX_ALIAS_NAME-1); + d_name.length= my_casedn_str(files_charset_info, d_name_buff); + d_name.str= d_name_buff; + } + } + + if (use_db_name) + { + append_identifier(thd, str, &d_name); + str->append('.'); + } + append_identifier(thd, str, &t_name); + str->append(')'); +} + + +/* Return last used value for sequence or NULL if sequence hasn't been used */ + +longlong Item_func_lastval::val_int() +{ + const char *key; + SEQUENCE_LAST_VALUE *entry; + uint length= get_table_def_key(table_list, &key); + THD *thd; + char buff[80]; + String key_buff(buff,sizeof(buff), &my_charset_bin); + DBUG_ENTER("Item_func_lastval::val_int"); + update_table(); + thd= table->in_use; + + if (table->s->tmp_table != NO_TMP_TABLE) + { + /* + Temporary tables has an extra \0 at end to distinguish it from + normal tables + */ + key_buff.copy(key, length, &my_charset_bin); + key_buff.append((char) 0); + key= key_buff.ptr(); + length++; + } + + if (!(entry= ((SEQUENCE_LAST_VALUE*) + my_hash_search(&thd->sequences, (uchar*) key, length)))) + { + /* Sequence not used */ + null_value= 1; + DBUG_RETURN(0); + } + if (entry->check_version(table)) + { + /* Table droped and re-created, remove current version */ + my_hash_delete(&thd->sequences, (uchar*) entry); + null_value= 1; + DBUG_RETURN(0); + } + + null_value= entry->null_value; + DBUG_RETURN(entry->value); +} + + +/* + Sets next value to be returned from sequences + + SELECT setval(foo, 42, 0); Next nextval will return 43 + SELECT setval(foo, 42, 0, true); Same as above + SELECT setval(foo, 42, 0, false); Next nextval will return 42 +*/ + +longlong Item_func_setval::val_int() +{ + longlong value; + int error; + THD *thd; + DBUG_ENTER("Item_func_setval::val_int"); + + update_table(); + DBUG_ASSERT(table && table->s->sequence); + thd= table->in_use; + + if (unlikely(thd->count_cuted_fields == CHECK_FIELD_EXPRESSION)) + { + /* Alter table checking if function works */ + null_value= 0; + DBUG_RETURN(0); + } + + value= nextval; + error= table->s->sequence->set_value(table, nextval, round, is_used); + if (unlikely(error)) + { + null_value= 1; + value= 0; + } + DBUG_RETURN(value); +} + + +/* Print for setval */ + +void Item_func_setval::print(String *str, enum_query_type query_type) +{ + char d_name_buff[MAX_ALIAS_NAME], t_name_buff[MAX_ALIAS_NAME]; + LEX_CSTRING d_name= table_list->db; + LEX_CSTRING t_name= table_list->table_name; + bool use_db_name= d_name.str && d_name.str[0]; + THD *thd= current_thd; // Don't trust 'table' + + str->append(func_name()); + str->append('('); + + /* + for next_val we assume that table_list has been updated to contain + the current db. + */ + + if (lower_case_table_names > 0) + { + strmake(t_name_buff, t_name.str, MAX_ALIAS_NAME-1); + t_name.length= my_casedn_str(files_charset_info, t_name_buff); + t_name.str= t_name_buff; + if (use_db_name) + { + strmake(d_name_buff, d_name.str, MAX_ALIAS_NAME-1); + d_name.length= my_casedn_str(files_charset_info, d_name_buff); + d_name.str= d_name_buff; + } + } + + if (use_db_name) + { + append_identifier(thd, str, &d_name); + str->append('.'); + } + append_identifier(thd, str, &t_name); + str->append(','); + str->append_longlong(nextval); + str->append(','); + str->append_longlong(is_used); + str->append(','); + str->append_ulonglong(round); + str->append(')'); +} |