summaryrefslogtreecommitdiff
path: root/sql/item_func.cc
diff options
context:
space:
mode:
Diffstat (limited to 'sql/item_func.cc')
-rw-r--r--sql/item_func.cc1981
1 files changed, 1981 insertions, 0 deletions
diff --git a/sql/item_func.cc b/sql/item_func.cc
new file mode 100644
index 00000000000..b5e9a0c4035
--- /dev/null
+++ b/sql/item_func.cc
@@ -0,0 +1,1981 @@
+/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
+
+ 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+
+/* This file defines all numerical functions */
+
+#ifdef __GNUC__
+#pragma implementation // gcc: Class implementation
+#endif
+
+#include "mysql_priv.h"
+#include <m_ctype.h>
+#include <hash.h>
+#include <time.h>
+#include <ft_global.h>
+
+/* return TRUE if item is a constant */
+
+bool
+eval_const_cond(COND *cond)
+{
+ return ((Item_func*) cond)->val_int() ? TRUE : FALSE;
+}
+
+
+Item_func::Item_func(List<Item> &list)
+{
+ arg_count=list.elements;
+ if ((args=(Item**) sql_alloc(sizeof(Item*)*arg_count)))
+ {
+ uint i=0;
+ List_iterator<Item> li(list);
+ Item *item;
+
+ while ((item=li++))
+ {
+ args[i++]= item;
+ with_sum_func|=item->with_sum_func;
+ }
+ }
+ list.empty(); // Fields are used
+}
+
+bool
+Item_func::fix_fields(THD *thd,TABLE_LIST *tables)
+{
+ Item **arg,**arg_end;
+ char buff[sizeof(double)]; // Max argument in function
+ binary=0;
+ used_tables_cache=0;
+ const_item_cache=1;
+
+ if (thd && check_stack_overrun(thd,buff))
+ return 0; // Fatal error if flag is set!
+ if (arg_count)
+ { // Print purify happy
+ for (arg=args, arg_end=args+arg_count; arg != arg_end ; arg++)
+ {
+ if ((*arg)->fix_fields(thd,tables))
+ return 1; /* purecov: inspected */
+ if ((*arg)->maybe_null)
+ maybe_null=1;
+ if ((*arg)->binary)
+ binary=1;
+ with_sum_func= with_sum_func || (*arg)->with_sum_func;
+ used_tables_cache|=(*arg)->used_tables();
+ const_item_cache&= (*arg)->const_item();
+ }
+ }
+ fix_length_and_dec();
+ return 0;
+}
+
+
+void Item_func::split_sum_func(List<Item> &fields)
+{
+ Item **arg,**arg_end;
+ for (arg=args, arg_end=args+arg_count; arg != arg_end ; arg++)
+ {
+ if ((*arg)->with_sum_func && (*arg)->type() != SUM_FUNC_ITEM)
+ (*arg)->split_sum_func(fields);
+ else if ((*arg)->used_tables() || (*arg)->type() == SUM_FUNC_ITEM)
+ {
+ fields.push_front(*arg);
+ *arg=new Item_ref((Item**) fields.head_ref(),0,(*arg)->name);
+ }
+ }
+}
+
+
+void Item_func::update_used_tables()
+{
+ used_tables_cache=0;
+ const_item_cache=1;
+ for (uint i=0 ; i < arg_count ; i++)
+ {
+ args[i]->update_used_tables();
+ used_tables_cache|=args[i]->used_tables();
+ const_item_cache&=args[i]->const_item();
+ }
+}
+
+
+table_map Item_func::used_tables() const
+{
+ return used_tables_cache;
+}
+
+void Item_func::print(String *str)
+{
+ str->append(func_name());
+ str->append('(');
+ for (uint i=0 ; i < arg_count ; i++)
+ {
+ if (i)
+ str->append(',');
+ args[i]->print(str);
+ }
+ str->append(')');
+}
+
+
+void Item_func::print_op(String *str)
+{
+ str->append('(');
+ for (uint i=0 ; i < arg_count-1 ; i++)
+ {
+ args[i]->print(str);
+ str->append(' ');
+ str->append(func_name());
+ str->append(' ');
+ }
+ args[arg_count-1]->print(str);
+ str->append(')');
+}
+
+bool Item_func::eq(const Item *item) const
+{
+ /* Assume we don't have rtti */
+ if (this == item)
+ return 1;
+ if (item->type() != FUNC_ITEM)
+ return 0;
+ Item_func *item_func=(Item_func*) item;
+ if (arg_count != item_func->arg_count ||
+ func_name() != item_func->func_name())
+ return 0;
+ for (uint i=0; i < arg_count ; i++)
+ if (!args[i]->eq(item_func->args[i]))
+ return 0;
+ return 1;
+}
+
+
+String *Item_real_func::val_str(String *str)
+{
+ double nr=val();
+ if (null_value)
+ return 0; /* purecov: inspected */
+ else
+ str->set(nr,decimals);
+ return str;
+}
+
+
+String *Item_num_func::val_str(String *str)
+{
+ if (hybrid_type == INT_RESULT)
+ {
+ longlong nr=val_int();
+ if (null_value)
+ return 0; /* purecov: inspected */
+ else
+ str->set(nr);
+ }
+ else
+ {
+ double nr=val();
+ if (null_value)
+ return 0; /* purecov: inspected */
+ else
+ str->set(nr,decimals);
+ }
+ return str;
+}
+
+
+void Item_func::fix_num_length_and_dec()
+{
+ decimals=0;
+ for (uint i=0 ; i < arg_count ; i++)
+ set_if_bigger(decimals,args[i]->decimals);
+ max_length=float_length(decimals);
+}
+
+
+String *Item_int_func::val_str(String *str)
+{
+ longlong nr=val_int();
+ if (null_value)
+ return 0;
+ else
+ str->set(nr);
+ return str;
+}
+
+/* Change from REAL_RESULT (default) to INT_RESULT if both arguments are integers */
+
+void Item_num_op::find_num_type(void)
+{
+ if (args[0]->result_type() == INT_RESULT &&
+ args[1]->result_type() == INT_RESULT)
+ hybrid_type=INT_RESULT;
+}
+
+String *Item_num_op::val_str(String *str)
+{
+ if (hybrid_type == INT_RESULT)
+ {
+ longlong nr=val_int();
+ if (null_value)
+ return 0; /* purecov: inspected */
+ else
+ str->set(nr);
+ }
+ else
+ {
+ double nr=val();
+ if (null_value)
+ return 0; /* purecov: inspected */
+ else
+ str->set(nr,decimals);
+ }
+ return str;
+}
+
+
+double Item_func_plus::val()
+{
+ double value=args[0]->val()+args[1]->val();
+ if ((null_value=args[0]->null_value || args[1]->null_value))
+ return 0.0;
+ return value;
+}
+
+longlong Item_func_plus::val_int()
+{
+ longlong value=args[0]->val_int()+args[1]->val_int();
+ if ((null_value=args[0]->null_value || args[1]->null_value))
+ return 0;
+ return value;
+}
+
+double Item_func_minus::val()
+{
+ double value=args[0]->val() - args[1]->val();
+ if ((null_value=args[0]->null_value || args[1]->null_value))
+ return 0.0;
+ return value;
+}
+
+longlong Item_func_minus::val_int()
+{
+ longlong value=args[0]->val_int() - args[1]->val_int();
+ if ((null_value=args[0]->null_value || args[1]->null_value))
+ return 0;
+ return value;
+}
+
+double Item_func_mul::val()
+{
+ double value=args[0]->val()*args[1]->val();
+ if ((null_value=args[0]->null_value || args[1]->null_value))
+ return 0.0; /* purecov: inspected */
+ return value;
+}
+
+longlong Item_func_mul::val_int()
+{
+ longlong value=args[0]->val_int()*args[1]->val_int();
+ if ((null_value=args[0]->null_value || args[1]->null_value))
+ return 0; /* purecov: inspected */
+ return value;
+}
+
+
+double Item_func_div::val()
+{
+ double value=args[0]->val();
+ double val2=args[1]->val();
+ if ((null_value= val2 == 0.0 || args[0]->null_value || args[1]->null_value))
+ return 0.0;
+ return value/val2;
+}
+
+longlong Item_func_div::val_int()
+{
+ longlong value=args[0]->val_int();
+ longlong val2=args[1]->val_int();
+ if ((null_value= val2 == 0 || args[0]->null_value || args[1]->null_value))
+ return 0;
+ return value/val2;
+}
+
+void Item_func_div::fix_length_and_dec()
+{
+ decimals=max(args[0]->decimals,args[1]->decimals)+2;
+ max_length=args[0]->max_length - args[0]->decimals + decimals;
+ uint tmp=float_length(decimals);
+ set_if_smaller(max_length,tmp);
+ maybe_null=1;
+}
+
+double Item_func_mod::val()
+{
+ double value= floor(args[0]->val()+0.5);
+ double val2=floor(args[1]->val()+0.5);
+ if ((null_value=val2 == 0.0 || args[0]->null_value || args[1]->null_value))
+ return 0.0; /* purecov: inspected */
+ return fmod(value,val2);
+}
+
+longlong Item_func_mod::val_int()
+{
+ longlong value= args[0]->val_int();
+ longlong val2= args[1]->val_int();
+ if ((null_value=val2 == 0 || args[0]->null_value || args[1]->null_value))
+ return 0; /* purecov: inspected */
+ return value % val2;
+}
+
+void Item_func_mod::fix_length_and_dec()
+{
+ max_length=args[1]->max_length;
+ decimals=0;
+ maybe_null=1;
+ find_num_type();
+}
+
+
+double Item_func_neg::val()
+{
+ double value=args[0]->val();
+ null_value=args[0]->null_value;
+ return -value;
+}
+
+longlong Item_func_neg::val_int()
+{
+ longlong value=args[0]->val_int();
+ null_value=args[0]->null_value;
+ return -value;
+}
+
+void Item_func_neg::fix_length_and_dec()
+{
+ decimals=args[0]->decimals;
+ max_length=args[0]->max_length;
+ hybrid_type= args[0]->result_type() == INT_RESULT ? INT_RESULT : REAL_RESULT;
+}
+
+double Item_func_abs::val()
+{
+ double value=args[0]->val();
+ null_value=args[0]->null_value;
+ return fabs(value);
+}
+
+longlong Item_func_abs::val_int()
+{
+ longlong value=args[0]->val_int();
+ null_value=args[0]->null_value;
+ return value >= 0 ? value : -value;
+}
+
+void Item_func_abs::fix_length_and_dec()
+{
+ decimals=args[0]->decimals;
+ max_length=args[0]->max_length;
+ hybrid_type= args[0]->result_type() == INT_RESULT ? INT_RESULT : REAL_RESULT;
+}
+
+double Item_func_log::val()
+{
+ double value=args[0]->val();
+ if ((null_value=(args[0]->null_value || value <= 0.0)))
+ return 0.0; /* purecov: inspected */
+ return log(value);
+}
+
+double Item_func_log10::val()
+{
+ double value=args[0]->val();
+ if ((null_value=(args[0]->null_value || value <= 0.0)))
+ return 0.0; /* purecov: inspected */
+ return log10(value);
+}
+
+double Item_func_exp::val()
+{
+ double value=args[0]->val();
+ if ((null_value=args[0]->null_value))
+ return 0.0; /* purecov: inspected */
+ return exp(value);
+}
+
+double Item_func_sqrt::val()
+{
+ double value=args[0]->val();
+ if ((null_value=(args[0]->null_value || value < 0)))
+ return 0.0; /* purecov: inspected */
+ return sqrt(value);
+}
+
+double Item_func_pow::val()
+{
+ double value=args[0]->val();
+ double val2=args[1]->val();
+ if ((null_value=(args[0]->null_value || args[1]->null_value)))
+ return 0.0; /* purecov: inspected */
+ return pow(value,val2);
+}
+
+// Trigonometric functions
+
+double Item_func_acos::val()
+{
+ double value=args[0]->val();
+ if ((null_value=(args[0]->null_value || (value < -1.0 || value > 1.0))))
+ return 0.0;
+ return fix_result(acos(value));
+}
+
+double Item_func_asin::val()
+{
+ double value=args[0]->val();
+ if ((null_value=(args[0]->null_value || (value < -1.0 || value > 1.0))))
+ return 0.0;
+ return fix_result(asin(value));
+}
+
+double Item_func_atan::val()
+{
+ double value=args[0]->val();
+ if ((null_value=args[0]->null_value))
+ return 0.0;
+ if (arg_count == 2)
+ {
+ double val2= args[1]->val();
+ if ((null_value=args[1]->null_value))
+ return 0.0;
+ return fix_result(atan2(value,val2));
+ }
+ return fix_result(atan(value));
+}
+
+double Item_func_cos::val()
+{
+ double value=args[0]->val();
+ if ((null_value=args[0]->null_value))
+ return 0.0;
+ return fix_result(cos(value));
+}
+
+double Item_func_sin::val()
+{
+ double value=args[0]->val();
+ if ((null_value=args[0]->null_value))
+ return 0.0;
+ return fix_result(sin(value));
+}
+
+double Item_func_tan::val()
+{
+ double value=args[0]->val();
+ if ((null_value=args[0]->null_value))
+ return 0.0;
+ return fix_result(tan(value));
+}
+
+
+// Shift-functions, same as << and >> in C/C++
+
+
+longlong Item_func_shift_left::val_int()
+{
+ uint shift;
+ ulonglong res= ((ulonglong) args[0]->val_int() <<
+ (shift=(uint) args[1]->val_int()));
+ if (args[0]->null_value || args[1]->null_value)
+ {
+ null_value=1;
+ return 0;
+ }
+ null_value=0;
+ return (shift < sizeof(longlong)*8 ? (longlong) res : LL(0));
+}
+
+longlong Item_func_shift_right::val_int()
+{
+ uint shift;
+ ulonglong res= (ulonglong) args[0]->val_int() >>
+ (shift=(uint) args[1]->val_int());
+ if (args[0]->null_value || args[1]->null_value)
+ {
+ null_value=1;
+ return 0;
+ }
+ null_value=0;
+ return (shift < sizeof(longlong)*8 ? (longlong) res : LL(0));
+}
+
+
+longlong Item_func_bit_neg::val_int()
+{
+ ulonglong res= (ulonglong) args[0]->val_int();
+ if ((null_value=args[0]->null_value))
+ return 0;
+ return ~res;
+}
+
+
+// Conversion functions
+
+void Item_func_integer::fix_length_and_dec()
+{
+ max_length=args[0]->max_length - args[0]->decimals+1;
+ uint tmp=float_length(decimals);
+ set_if_smaller(max_length,tmp);
+ decimals=0;
+}
+
+longlong Item_func_ceiling::val_int()
+{
+ double value=args[0]->val();
+ null_value=args[0]->null_value;
+ return (longlong) ceil(value);
+}
+
+longlong Item_func_floor::val_int()
+{
+ double value=args[0]->val();
+ null_value=args[0]->null_value;
+ return (longlong) floor(value);
+}
+
+void Item_func_round::fix_length_and_dec()
+{
+ max_length=args[0]->max_length;
+ decimals=args[0]->decimals;
+ if (args[1]->const_item())
+ {
+ int tmp=(int) args[1]->val_int();
+ if (tmp < 0)
+ decimals=0;
+ else
+ decimals=tmp;
+ }
+}
+
+double Item_func_round::val()
+{
+ double value=args[0]->val();
+ int dec=(int) args[1]->val_int();
+ uint abs_dec=abs(dec);
+
+ if ((null_value=args[0]->null_value || args[1]->null_value))
+ return 0.0;
+ double tmp=(abs_dec < array_elements(log_10) ?
+ log_10[abs_dec] : pow(10.0,(double) abs_dec));
+
+ if (truncate)
+ return dec < 0 ? floor(value/tmp)*tmp : floor(value*tmp)/tmp;
+ return dec < 0 ? rint(value/tmp)*tmp : rint(value*tmp)/tmp;
+}
+
+
+double Item_func_rand::val()
+{
+ if (arg_count)
+ { // Only use argument once in query
+ ulong tmp=((ulong) args[0]->val_int())+55555555L;
+ randominit(&current_thd->rand,tmp,tmp/2);
+#ifdef DELETE_ITEMS
+ delete args[0];
+#endif
+ arg_count=0;
+ }
+ return rnd(&current_thd->rand);
+}
+
+longlong Item_func_sign::val_int()
+{
+ double value=args[0]->val();
+ null_value=args[0]->null_value;
+ return value < 0.0 ? -1 : (value > 0 ? 1 : 0);
+}
+
+
+double Item_func_units::val()
+{
+ double value=args[0]->val();
+ if ((null_value=args[0]->null_value))
+ return 0;
+ return value*mul+add;
+}
+
+
+void Item_func_min_max::fix_length_and_dec()
+{
+ decimals=0;
+ max_length=0;
+ maybe_null=1;
+ binary=0;
+ cmp_type=args[0]->result_type();
+ for (uint i=0 ; i < arg_count ; i++)
+ {
+ if (max_length < args[i]->max_length)
+ max_length=args[i]->max_length;
+ if (decimals < args[i]->decimals)
+ decimals=args[i]->decimals;
+ if (!args[i]->maybe_null)
+ maybe_null=0;
+ cmp_type=item_cmp_type(cmp_type,args[i]->result_type());
+ if (args[i]->binary)
+ binary=1;
+ }
+}
+
+
+String *Item_func_min_max::val_str(String *str)
+{
+ switch (cmp_type) {
+ case INT_RESULT:
+ {
+ longlong nr=val_int();
+ if (null_value)
+ return 0;
+ else
+ str->set(nr);
+ return str;
+ }
+ case REAL_RESULT:
+ {
+ double nr=val();
+ if (null_value)
+ return 0; /* purecov: inspected */
+ else
+ str->set(nr,decimals);
+ return str;
+ }
+ case STRING_RESULT:
+ {
+ String *res;
+ LINT_INIT(res);
+ null_value=1;
+ for (uint i=0; i < arg_count ; i++)
+ {
+ if (null_value)
+ {
+ res=args[i]->val_str(str);
+ null_value=args[i]->null_value;
+ }
+ else
+ {
+ String *res2;
+ res2= args[i]->val_str(res == str ? &tmp_value : str);
+ if (res2)
+ {
+ int cmp=binary ? stringcmp(res,res2) : sortcmp(res,res2);
+ if ((cmp_sign < 0 ? cmp : -cmp) < 0)
+ res=res2;
+ }
+ }
+ }
+ return res;
+ }
+ }
+ return 0; // Keep compiler happy
+}
+
+
+double Item_func_min_max::val()
+{
+ double value=0.0;
+ null_value=1;
+ for (uint i=0; i < arg_count ; i++)
+ {
+ if (null_value)
+ {
+ value=args[i]->val();
+ null_value=args[i]->null_value;
+ }
+ else
+ {
+ double tmp=args[i]->val();
+ if (!args[i]->null_value && (tmp < value ? cmp_sign : -cmp_sign) > 0)
+ value=tmp;
+ }
+ }
+ return value;
+}
+
+
+longlong Item_func_min_max::val_int()
+{
+ longlong value=0;
+ null_value=1;
+ for (uint i=0; i < arg_count ; i++)
+ {
+ if (null_value)
+ {
+ value=args[i]->val_int();
+ null_value=args[i]->null_value;
+ }
+ else
+ {
+ longlong tmp=args[i]->val_int();
+ if (!args[i]->null_value && (tmp < value ? cmp_sign : -cmp_sign) > 0)
+ value=tmp;
+ }
+ }
+ return value;
+}
+
+
+longlong Item_func_length::val_int()
+{
+ 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()
+{
+ String *res=args[0]->val_str(&value);
+ if (!res)
+ {
+ null_value=1;
+ return 0; /* purecov: inspected */
+ }
+ null_value=0;
+ return (longlong) (!args[0]->binary) ? res->numchars() : res->length();
+}
+
+
+longlong Item_func_locate::val_int()
+{
+ String *a=args[0]->val_str(&value1);
+ String *b=args[1]->val_str(&value2);
+#ifdef USE_MB
+ bool binary_str = args[0]->binary || args[1]->binary;
+#endif
+ if (!a || !b)
+ {
+ null_value=1;
+ return 0; /* purecov: inspected */
+ }
+ null_value=0;
+ uint start=0,start0=0;
+ if (arg_count == 3)
+ {
+ start=(uint) args[2]->val_int()-1;
+#ifdef USE_MB
+ if (use_mb(default_charset_info))
+ {
+ start0=start;
+ if (!binary_str)
+ start=a->charpos(start);
+ }
+#endif
+ if (start > a->length() || start+b->length() > a->length())
+ return 0;
+ }
+ if (!b->length()) // Found empty string at start
+ return (longlong) (start+1);
+#ifdef USE_MB
+ if (use_mb(default_charset_info) && !binary_str)
+ {
+ const char *ptr=a->ptr()+start;
+ const char *search=b->ptr();
+ const char *strend = ptr+a->length();
+ const char *end=strend-b->length()+1;
+ const char *search_end=search+b->length();
+ register uint32 l;
+ while (ptr < end)
+ {
+ if (*ptr == *search)
+ {
+ register char *i,*j;
+ i=(char*) ptr+1; j=(char*) search+1;
+ while (j != search_end)
+ if (*i++ != *j++) goto skipp;
+ return (longlong) start0+1;
+ }
+ skipp:
+ if ((l=my_ismbchar(default_charset_info,ptr,strend))) ptr+=l;
+ else ++ptr;
+ ++start0;
+ }
+ return 0;
+ }
+#endif /* USE_MB */
+ return (longlong) (a->strstr(*b,start)+1) ;
+}
+
+
+longlong Item_func_field::val_int()
+{
+ String *field;
+ if (!(field=item->val_str(&value)))
+ return 0; // -1 if null ?
+ for (uint i=0 ; i < arg_count ; i++)
+ {
+ String *tmp_value=args[i]->val_str(&tmp);
+ if (tmp_value && field->length() == tmp_value->length() &&
+ !memcmp(field->ptr(),tmp_value->ptr(),tmp_value->length()))
+ return (longlong) (i+1);
+ }
+ return 0;
+}
+
+
+longlong Item_func_ascii::val_int()
+{
+ 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()
+{
+ 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 (use_mb(default_charset_info) && !args[0]->binary)
+ {
+ register const char *str=res->ptr();
+ register uint32 n=0, l=my_ismbchar(default_charset_info,
+ 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! */
+
+void 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() == FIELD_TYPE_SET)
+ {
+ String *find=args[0]->val_str(&value);
+ if (find)
+ {
+ enum_value=find_enum(((Field_enum*) field)->typelib,find->ptr(),
+ find->length());
+ enum_bit=0;
+ if (enum_value)
+ enum_bit=LL(1) << (enum_value-1);
+ }
+ }
+ }
+}
+
+static const char separator=',';
+
+longlong Item_func_find_in_set::val_int()
+{
+ if (enum_value)
+ {
+ ulonglong tmp=(ulonglong) args[1]->val_int();
+ if (!(null_value=args[1]->null_value || args[0]->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;
+
+ int diff;
+ if ((diff=buffer->length() - find->length()) >= 0)
+ {
+ const char *f_pos=find->ptr();
+ const char *f_end=f_pos+find->length();
+ const char *str=buffer->ptr();
+ const char *end=str+diff+1;
+ const char *real_end=str+buffer->length();
+ uint position=1;
+ do
+ {
+ const char *pos= f_pos;
+ while (pos != f_end)
+ {
+ if (toupper(*str) != toupper(*pos))
+ goto not_found;
+ str++;
+ pos++;
+ }
+ if (str == real_end || str[0] == separator)
+ return (longlong) position;
+ not_found:
+ while (str < end && str[0] != separator)
+ str++;
+ position++;
+ } while (++str <= end);
+ }
+ return 0;
+}
+
+static char nbits[256] = {
+ 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4,
+ 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
+ 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
+ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+ 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
+ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+ 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
+ 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
+ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+ 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
+ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+ 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
+ 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
+ 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8,
+};
+
+uint count_bits(ulonglong v)
+{
+#if SIZEOF_LONG_LONG > 4
+ /* The following code is a bit faster on 16 bit machines than if we would
+ only shift v */
+ ulong v2=(ulong) (v >> 32);
+ return (uint) (uchar) (nbits[(uchar) v] +
+ nbits[(uchar) (v >> 8)] +
+ nbits[(uchar) (v >> 16)] +
+ nbits[(uchar) (v >> 24)] +
+ nbits[(uchar) (v2)] +
+ nbits[(uchar) (v2 >> 8)] +
+ nbits[(uchar) (v2 >> 16)] +
+ nbits[(uchar) (v2 >> 24)]);
+#else
+ return (uint) (uchar) (nbits[(uchar) v] +
+ nbits[(uchar) (v >> 8)] +
+ nbits[(uchar) (v >> 16)] +
+ nbits[(uchar) (v >> 24)]);
+#endif
+}
+
+longlong Item_func_bit_count::val_int()
+{
+ ulonglong value= (ulonglong) args[0]->val_int();
+ if (args[0]->null_value)
+ {
+ null_value=1; /* purecov: inspected */
+ return 0; /* purecov: inspected */
+ }
+ return (longlong) count_bits(value);
+}
+
+
+/****************************************************************************
+** Functions to handle dynamic loadable functions
+** Original source by: Alexis Mikhailov <root@medinf.chuvashia.su>
+** Rewritten by: Monty.
+****************************************************************************/
+
+#ifdef HAVE_DLOPEN
+
+udf_handler::~udf_handler()
+{
+ if (initialized)
+ {
+ if (u_d->func_deinit != NULL)
+ {
+ void (*deinit)(UDF_INIT *) = (void (*)(UDF_INIT*))
+ u_d->func_deinit;
+ (*deinit)(&initid);
+ }
+ free_udf(u_d);
+ }
+ delete [] buffers;
+}
+
+
+bool
+udf_handler::fix_fields(THD *thd,TABLE_LIST *tables,Item_result_field *func,
+ uint arg_count, Item **arguments)
+{
+ char buff[sizeof(double)]; // Max argument in function
+ DBUG_ENTER("Item_udf_func::fix_fields");
+
+ if (thd && check_stack_overrun(thd,buff))
+ return 0; // Fatal error flag is set!
+
+ udf_func *tmp_udf=find_udf(u_d->name,strlen(u_d->name),1);
+
+ if (!tmp_udf)
+ {
+ my_printf_error(ER_CANT_FIND_UDF,ER(ER_CANT_FIND_UDF),MYF(0),u_d->name,
+ errno);
+ DBUG_RETURN(1);
+ }
+ u_d=tmp_udf;
+ args=arguments;
+
+ /* Fix all arguments */
+ func->binary=func->maybe_null=0;
+ used_tables_cache=0;
+ const_item_cache=1;
+
+ if ((f_args.arg_count=arg_count))
+ {
+ if (!(f_args.arg_type= (Item_result*)
+ sql_alloc(f_args.arg_count*sizeof(Item_result))))
+
+ {
+ free_udf(u_d);
+ DBUG_RETURN(1);
+ }
+ uint i;
+ Item **arg,**arg_end;
+ for (i=0, arg=arguments, arg_end=arguments+arg_count;
+ arg != arg_end ;
+ arg++,i++)
+ {
+ if ((*arg)->fix_fields(thd,tables))
+ return 1;
+ if ((*arg)->binary)
+ func->binary=1;
+ if ((*arg)->maybe_null)
+ func->maybe_null=1;
+ func->with_sum_func= func->with_sum_func || (*arg)->with_sum_func;
+ used_tables_cache|=(*arg)->used_tables();
+ const_item_cache&=(*arg)->const_item();
+ f_args.arg_type[i]=(*arg)->result_type();
+ }
+ if (!(buffers=new String[arg_count]) ||
+ !(f_args.args= (char**) sql_alloc(arg_count * sizeof(char *))) ||
+ !(f_args.lengths=(ulong*) sql_alloc(arg_count * sizeof(long))) ||
+ !(f_args.maybe_null=(char*) sql_alloc(arg_count * sizeof(char))) ||
+ !(num_buffer= (char*) sql_alloc(ALIGN_SIZE(sizeof(double))*arg_count)))
+ {
+ free_udf(u_d);
+ DBUG_RETURN(1);
+ }
+ }
+ func->fix_length_and_dec();
+ initid.max_length=func->max_length;
+ initid.maybe_null=func->maybe_null;
+ initid.const_item=const_item_cache;
+ initid.decimals=func->decimals;
+ initid.ptr=0;
+
+ if (u_d->func_init)
+ {
+ char *to=num_buffer;
+ for (uint i=0; i < arg_count; i++)
+ {
+ f_args.args[i]=0;
+ f_args.lengths[i]=arguments[i]->max_length;
+ f_args.maybe_null[i]=(char) arguments[i]->maybe_null;
+
+ switch(arguments[i]->type()) {
+ case Item::STRING_ITEM: // Constant string !
+ {
+ String *res=arguments[i]->val_str((String *) 0);
+ if (arguments[i]->null_value)
+ continue;
+ f_args.args[i]= (char*) res->ptr();
+ break;
+ }
+ case Item::INT_ITEM:
+ *((longlong*) to) = arguments[i]->val_int();
+ if (!arguments[i]->null_value)
+ {
+ f_args.args[i]=to;
+ to+= ALIGN_SIZE(sizeof(longlong));
+ }
+ break;
+ case Item::REAL_ITEM:
+ *((double*) to) = arguments[i]->val();
+ if (!arguments[i]->null_value)
+ {
+ f_args.args[i]=to;
+ to+= ALIGN_SIZE(sizeof(double));
+ }
+ break;
+ default: // Skip these
+ break;
+ }
+ }
+ thd->net.last_error[0]=0;
+ my_bool (*init)(UDF_INIT *, UDF_ARGS *, char *)=
+ (my_bool (*)(UDF_INIT *, UDF_ARGS *, char *))
+ u_d->func_init;
+ if ((error=(uchar) init(&initid, &f_args, thd->net.last_error)))
+ {
+ my_printf_error(ER_CANT_INITIALIZE_UDF,ER(ER_CANT_INITIALIZE_UDF),MYF(0),
+ u_d->name,thd->net.last_error);
+ free_udf(u_d);
+ DBUG_RETURN(1);
+ }
+ func->max_length=min(initid.max_length,MAX_BLOB_WIDTH);
+ func->maybe_null=initid.maybe_null;
+ const_item_cache=initid.const_item;
+ func->decimals=min(initid.decimals,31);
+ }
+ initialized=1;
+ if (error)
+ {
+ my_printf_error(ER_CANT_INITIALIZE_UDF,ER(ER_CANT_INITIALIZE_UDF),MYF(0),
+ u_d->name, ER(ER_UNKNOWN_ERROR));
+ DBUG_RETURN(1);
+ }
+ DBUG_RETURN(0);
+}
+
+
+bool udf_handler::get_arguments()
+{
+ if (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:
+ {
+ 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();
+ 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();
+ if (!args[i]->null_value)
+ {
+ f_args.args[i]=to;
+ to+= ALIGN_SIZE(sizeof(double));
+ }
+ break;
+ }
+ }
+ return 0;
+}
+
+/* This returns (String*) 0 in case of NULL values */
+
+String *udf_handler::val_str(String *str,String *save_str)
+{
+ uchar is_null=0;
+ ulong res_length;
+
+ if (get_arguments())
+ 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;
+ return 0;
+ }
+ }
+ char *res=func(&initid, &f_args, (char*) str->ptr(), &res_length, &is_null,
+ &error);
+ if (is_null || !res || error) // The !res is for safety
+ {
+ return 0;
+ }
+ if (res == str->ptr())
+ {
+ str->length(res_length);
+ return str;
+ }
+ save_str->set(res, res_length);
+ return save_str;
+}
+
+
+
+double Item_func_udf_float::val()
+{
+ DBUG_ENTER("Item_func_udf_float::val");
+ DBUG_PRINT("info",("result_type: %d arg_count: %d",
+ args[0]->result_type(), arg_count));
+ DBUG_RETURN(udf.val(&null_value));
+}
+
+
+String *Item_func_udf_float::val_str(String *str)
+{
+ double nr=val();
+ if (null_value)
+ return 0; /* purecov: inspected */
+ else
+ str->set(nr,decimals);
+ return str;
+}
+
+
+longlong Item_func_udf_int::val_int()
+{
+ DBUG_ENTER("Item_func_udf_int::val_int");
+ DBUG_PRINT("info",("result_type: %d arg_count: %d",
+ args[0]->result_type(), arg_count));
+
+ DBUG_RETURN(udf.val_int(&null_value));
+}
+
+
+String *Item_func_udf_int::val_str(String *str)
+{
+ longlong nr=val_int();
+ if (null_value)
+ return 0;
+ else
+ str->set(nr);
+ return str;
+}
+
+/* Default max_length is max argument length */
+
+void 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_VOID_RETURN;
+}
+
+String *Item_func_udf_str::val_str(String *str)
+{
+ String *res=udf.val_str(str,&str_value);
+ null_value = !res;
+ return res;
+}
+
+#else
+bool udf_handler::get_arguments() { return 0; }
+#endif /* HAVE_DLOPEN */
+
+/*
+** User level locks
+*/
+
+pthread_mutex_t LOCK_user_locks;
+static HASH hash_user_locks;
+
+class ULL
+{
+ char *key;
+ uint key_length;
+
+public:
+ int count;
+ bool locked;
+ pthread_cond_t cond;
+ pthread_t thread;
+
+ ULL(const char *key_arg,uint length) :key_length(length),count(1),locked(1)
+ {
+ key=(char*) my_memdup((byte*) key_arg,length,MYF(0));
+ pthread_cond_init(&cond,NULL);
+ if (key)
+ {
+ if (hash_insert(&hash_user_locks,(byte*) this))
+ {
+ my_free((gptr) key,MYF(0));
+ key=0;
+ }
+ }
+ }
+ ~ULL()
+ {
+ if (key)
+ {
+ hash_delete(&hash_user_locks,(byte*) this);
+ my_free((gptr) key,MYF(0));
+ }
+ pthread_cond_destroy(&cond);
+ }
+ inline bool initialized() { return key != 0; }
+ friend void item_user_lock_release(ULL *ull);
+ friend char *ull_get_key(const ULL *ull,uint *length,my_bool not_used);
+};
+
+char *ull_get_key(const ULL *ull,uint *length,
+ my_bool not_used __attribute__((unused)))
+{
+ *length=(uint) ull->key_length;
+ return (char*) ull->key;
+}
+
+void item_user_lock_init(void)
+{
+ pthread_mutex_init(&LOCK_user_locks,NULL);
+ hash_init(&hash_user_locks,16,0,0,(hash_get_key) ull_get_key,NULL,0);
+}
+
+void item_user_lock_free(void)
+{
+ hash_free(&hash_user_locks);
+}
+
+void item_user_lock_release(ULL *ull)
+{
+ ull->locked=0;
+ if (--ull->count)
+ pthread_cond_signal(&ull->cond);
+ else
+ delete ull;
+}
+
+/*
+ Get a user level lock. If the thread has an old lock this is first released.
+ Returns 1: Got lock
+ Returns 0: Timeout
+ Returns NULL: Error
+*/
+
+longlong Item_func_get_lock::val_int()
+{
+ String *res=args[0]->val_str(&value);
+ longlong timeout=args[1]->val_int();
+ struct timespec abstime;
+ THD *thd=current_thd;
+ ULL *ull;
+ int error;
+
+ pthread_mutex_lock(&LOCK_user_locks);
+
+ if (!res || !res->length())
+ {
+ pthread_mutex_unlock(&LOCK_user_locks);
+ null_value=1;
+ return 0;
+ }
+ null_value=0;
+
+ if (thd->ull)
+ {
+ item_user_lock_release(thd->ull);
+ thd->ull=0;
+ }
+
+ if (!(ull= ((ULL*) hash_search(&hash_user_locks,(byte*) res->ptr(),
+ res->length()))))
+ {
+ ull=new ULL(res->ptr(),res->length());
+ if (!ull || !ull->initialized())
+ {
+ delete ull;
+ pthread_mutex_unlock(&LOCK_user_locks);
+ null_value=1; // Probably out of memory
+ return 0;
+ }
+ ull->thread=thd->real_id;
+ thd->ull=ull;
+ pthread_mutex_unlock(&LOCK_user_locks);
+ return 1; // Got new lock
+ }
+ ull->count++;
+
+ /* structure is now initialized. Try to get the lock */
+ /* Set up control struct to allow others to abort locks */
+ pthread_mutex_lock(&thd->mysys_var->mutex);
+ thd->proc_info="User lock";
+ thd->mysys_var->current_mutex= &LOCK_user_locks;
+ thd->mysys_var->current_cond= &ull->cond;
+ pthread_mutex_unlock(&thd->mysys_var->mutex);
+
+ abstime.tv_sec=time((time_t*) 0)+(time_t) timeout;
+ abstime.tv_nsec=0;
+ while ((error=pthread_cond_timedwait(&ull->cond,&LOCK_user_locks,&abstime))
+ != ETIME && error != ETIMEDOUT && ull->locked)
+ {
+ if (thd->killed || abort_loop)
+ {
+ error=EINTR; // Return NULL
+ break;
+ }
+ }
+ if (ull->locked)
+ {
+ if (!--ull->count)
+ delete ull; // Should never happen
+ if (error != ETIME && error != ETIMEDOUT)
+ {
+ error=1;
+ null_value=1; // Return NULL
+ }
+ }
+ else
+ {
+ ull->locked=1;
+ ull->thread=thd->real_id;
+ thd->ull=ull;
+ error=0;
+ }
+ pthread_mutex_unlock(&LOCK_user_locks);
+
+ pthread_mutex_lock(&thd->mysys_var->mutex);
+ thd->proc_info=0;
+ thd->mysys_var->current_mutex= 0;
+ thd->mysys_var->current_cond= 0;
+ pthread_mutex_unlock(&thd->mysys_var->mutex);
+
+ return !error ? 1 : 0;
+}
+
+
+/*
+** Release a user level lock.
+** Returns 1 if lock released
+** 0 if lock wasn't held
+** NULL if no such lock
+*/
+
+longlong Item_func_release_lock::val_int()
+{
+ String *res=args[0]->val_str(&value);
+ ULL *ull;
+ longlong result;
+ if (!res || !res->length())
+ {
+ null_value=1;
+ return 0;
+ }
+ null_value=0;
+
+ result=0;
+ pthread_mutex_lock(&LOCK_user_locks);
+ if (!(ull= ((ULL*) hash_search(&hash_user_locks,(const byte*) res->ptr(),
+ res->length()))))
+ {
+ null_value=1;
+ }
+ else
+ {
+ if (ull->locked && pthread_equal(pthread_self(),ull->thread))
+ {
+ result=1; // Release is ok
+ item_user_lock_release(ull);
+ current_thd->ull=0;
+ }
+ }
+ pthread_mutex_unlock(&LOCK_user_locks);
+ return result;
+}
+
+
+longlong Item_func_set_last_insert_id::val_int()
+{
+ longlong value=args[0]->val_int();
+ current_thd->insert_id(value);
+ null_value=args[0]->null_value;
+ return value;
+}
+
+/* This function is just used to test speed of different functions */
+
+longlong Item_func_benchmark::val_int()
+{
+ char buff[MAX_FIELD_WIDTH];
+ String tmp(buff,sizeof(buff));
+ THD *thd=current_thd;
+
+ for (ulong loop=0 ; loop < loop_count && !thd->killed; loop++)
+ {
+ switch (args[0]->result_type()) {
+ case REAL_RESULT:
+ (void) args[0]->val();
+ break;
+ case INT_RESULT:
+ (void) args[0]->val_int();
+ break;
+ case STRING_RESULT:
+ (void) args[0]->val_str(&tmp);
+ break;
+ }
+ }
+ return 0;
+}
+
+#define extra_size sizeof(double)
+
+static user_var_entry *get_variable(HASH *hash, LEX_STRING &name,
+ bool create_if_not_exists)
+{
+ user_var_entry *entry;
+
+ if (!(entry = (user_var_entry*) hash_search(hash, (byte*) name.str,
+ name.length)) &&
+ create_if_not_exists)
+ {
+ uint size=ALIGN_SIZE(sizeof(user_var_entry))+name.length+1+extra_size;
+ if (!hash_inited(hash))
+ return 0;
+ if (!(entry = (user_var_entry*) my_malloc(size,MYF(MY_WME))))
+ 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->type=STRING_RESULT;
+ memcpy(entry->name.str, name.str, name.length+1);
+ if (hash_insert(hash,(byte*) entry))
+ {
+ my_free((char*) entry,MYF(0));
+ return 0;
+ }
+ }
+ return entry;
+}
+
+
+bool Item_func_set_user_var::fix_fields(THD *thd,TABLE_LIST *tables)
+{
+ if (Item_func::fix_fields(thd,tables) ||
+ !(entry= get_variable(&thd->user_vars, name, 1)))
+ return 1;
+ return 0;
+}
+
+
+void
+Item_func_set_user_var::fix_length_and_dec()
+{
+ maybe_null=args[0]->maybe_null;
+ max_length=args[0]->max_length;
+ decimals=args[0]->decimals;
+ cached_result_type=args[0]->result_type();
+}
+
+void Item_func_set_user_var::update_hash(void *ptr, uint length,
+ Item_result type)
+{
+ if ((null_value=args[0]->null_value))
+ {
+ char *pos= (char*) entry+ ALIGN_SIZE(sizeof(user_var_entry));
+ if (entry->value && entry->value != pos)
+ my_free(entry->value,MYF(0));
+ entry->value=0;
+ entry->length=0;
+ }
+ else
+ {
+ 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,MYF(0));
+ 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;
+ if (!(entry->value=(char*) my_realloc(entry->value, length,
+ MYF(MY_ALLOW_ZERO_PTR))))
+ goto err;
+ }
+ }
+ memcpy(entry->value,ptr,length);
+ entry->length= length;
+ entry->type=type;
+ }
+ return;
+
+ err:
+ current_thd->fatal_error=1; // Probably end of memory
+ null_value=1;
+ return;
+}
+
+
+bool
+Item_func_set_user_var::update()
+{
+ switch (cached_result_type) {
+ case REAL_RESULT:
+ (void) val();
+ break;
+ case INT_RESULT:
+ (void) val_int();
+ break;
+ case STRING_RESULT:
+ char buffer[MAX_FIELD_WIDTH];
+ String tmp(buffer,sizeof(buffer));
+ (void) val_str(&tmp);
+ break;
+ }
+ return current_thd->fatal_error;
+}
+
+
+double
+Item_func_set_user_var::val()
+{
+ double value=args[0]->val();
+ update_hash((void*) &value,sizeof(value), REAL_RESULT);
+ return value;
+}
+
+longlong
+Item_func_set_user_var::val_int()
+{
+ longlong value=args[0]->val_int();
+ update_hash((void*) &value,sizeof(longlong),INT_RESULT);
+ return value;
+}
+
+String *
+Item_func_set_user_var::val_str(String *str)
+{
+ String *res=args[0]->val_str(str);
+ if (!res) // Null value
+ update_hash((void*) 0,0,STRING_RESULT);
+ else
+ update_hash(res->c_ptr(),res->length()+1,STRING_RESULT);
+ return res;
+}
+
+
+user_var_entry *Item_func_get_user_var::get_entry()
+{
+ if (!entry || ! entry->value)
+ {
+ null_value=1;
+ return 0;
+ }
+ null_value=0;
+ return entry;
+}
+
+
+String *
+Item_func_get_user_var::val_str(String *str)
+{
+ user_var_entry *entry=get_entry();
+ if (!entry)
+ return NULL;
+ switch (entry->type) {
+ case REAL_RESULT:
+ str->set(*(double*) entry->value);
+ break;
+ case INT_RESULT:
+ str->set(*(longlong*) entry->value);
+ break;
+ case STRING_RESULT:
+ if (str->copy(entry->value, entry->length-1))
+ {
+ null_value=1;
+ return NULL;
+ }
+ break;
+ }
+ return str;
+}
+
+
+double Item_func_get_user_var::val()
+{
+ user_var_entry *entry=get_entry();
+ if (!entry)
+ return 0.0;
+ switch (entry->type) {
+ case REAL_RESULT:
+ return *(double*) entry->value;
+ case INT_RESULT:
+ return (double) *(longlong*) entry->value;
+ case STRING_RESULT:
+ return atof(entry->value); // This is null terminated
+ }
+ return 0.0; // Impossible
+}
+
+
+longlong Item_func_get_user_var::val_int()
+{
+ user_var_entry *entry=get_entry();
+ if (!entry)
+ return LL(0);
+ switch (entry->type) {
+ case REAL_RESULT:
+ return (longlong) *(double*) entry->value;
+ case INT_RESULT:
+ return *(longlong*) entry->value;
+ case STRING_RESULT:
+ return strtoull(entry->value,NULL,10); // String is null terminated
+ }
+ return LL(0); // Impossible
+}
+
+
+void Item_func_get_user_var::fix_length_and_dec()
+{
+ maybe_null=1;
+ decimals=NOT_FIXED_DEC;
+ max_length=MAX_BLOB_WIDTH;
+ entry= get_variable(&current_thd->user_vars, name, 0);
+}
+
+
+enum Item_result Item_func_get_user_var::result_type() const
+{
+ user_var_entry *entry;
+ if (!(entry = (user_var_entry*) hash_search(&current_thd->user_vars,
+ (byte*) name.str,
+ name.length)))
+ return STRING_RESULT;
+ return entry->type;
+}
+
+longlong Item_func_inet_aton::val_int()
+{
+ uint byte_result = 0;
+ ulonglong result = 0; // We are ready for 64 bit addresses
+ const char *p,* end;
+ char c = '.'; // we mark c to indicate invalid IP in case length is 0
+ char buff[36];
+
+ String *s,tmp(buff,sizeof(buff));
+ if (!(s = args[0]->val_str(&tmp))) // If null value
+ goto err;
+ null_value=0;
+
+ end= (p = s->ptr()) + s->length();
+ while (p < end)
+ {
+ c = *p++;
+ int digit = (int) (c - '0'); // Assume ascii
+ if (digit >= 0 && digit <= 9)
+ {
+ if ((byte_result = byte_result * 10 + digit) > 255)
+ goto err; // Wrong address
+ }
+ else if (c == '.')
+ {
+ result= (result << 8) + (ulonglong) byte_result;
+ byte_result = 0;
+ }
+ else
+ goto err; // Invalid character
+ }
+ if (c != '.') // IP number can't end on '.'
+ return (result << 8) + (ulonglong) byte_result;
+
+err:
+ null_value=1;
+ return 0;
+}
+
+double Item_func_match::val()
+{
+ int a,b,c;
+ FT_DOC *docs;
+ my_off_t docid;
+
+ docid = table->file->row_position(); // HAVE to do it here...
+
+ if (table->file->ft_handler==NULL && !auto_init_was_done)
+ {
+ /* join won't use this ft-key, but we must to init it anyway */
+ String *ft_tmp=0;
+ char tmp1[FT_QUERY_MAXLEN];
+ String tmp2(tmp1,sizeof(tmp1));
+
+ ft_tmp=key_item()->val_str(&tmp2);
+ table->file->ft_init(key, (byte*) ft_tmp->ptr(), ft_tmp->length(), FALSE);
+ auto_init_was_done=1;
+ }
+
+ // Don't know how to return an error from val(), so NULL will be returned
+ if ((null_value=(table->file->ft_handler==NULL)))
+ return 0.0;
+
+ if (auto_init_was_done)
+ {
+ /* implicit initialization was done, so nobody will set proper
+ ft_relevance for us. We'll look for it in ft_handler array */
+
+ docs = ((FT_DOCLIST *)table->file->ft_handler)->doc;
+// docid = table->file->row_position();
+
+ if ((null_value=(docid==HA_OFFSET_ERROR)))
+ return 0.0;
+
+ // Assuming docs[] is sorted by dpos...
+
+ a=0, b=((FT_DOCLIST *)table->file->ft_handler)->ndocs;
+ for (c=(a+b)/2; b-a>1; c=(a+b)/2)
+ {
+ if (docs[c].dpos > docid)
+ b=c;
+ else
+ a=c;
+ }
+ if (docs[a].dpos == docid)
+ table->file->ft_relevance=docs[a].weight;
+ else
+ table->file->ft_relevance=0;
+ }
+
+ return table->file->ft_relevance;
+}
+
+bool Item_func_match::fix_fields(THD *thd,struct st_table_list *tlist)
+{
+ List_iterator<Item> li(fields);
+ Item *item;
+
+ /* Why testing for const_item ? Monty */
+ /* I'll remove it later, but this should include modifications to
+ find_best and auto_close as complement to auto_init code above. SerG */
+ if (Item_func::fix_fields(thd,tlist) || !const_item())
+ return 1;
+
+ while ((item=li++))
+ {
+ if (item->type() != Item::FIELD_ITEM || item->fix_fields(thd,tlist) ||
+ !item->used_tables())
+ return 1;
+ used_tables_cache|=item->used_tables();
+ }
+ /* check that all columns comes from the same table */
+ if (count_bits(used_tables_cache) != 1)
+ return 1;
+ const_item_cache=0;
+ table=((Item_field *)fields.head())->field->table;
+ auto_init_was_done=0;
+ table->file->ft_close(); // It's a bad solution to do it here, I know :-(
+ return 0;
+}
+
+
+bool Item_func_match::fix_index()
+{
+ List_iterator<Item> li(fields);
+ Item_field *item;
+ uint ft_to_key[MAX_KEY], ft_cnt[MAX_KEY], fts=0, key;
+
+ for (key=0 ; key<table->keys ; key++)
+ {
+ if ((table->key_info[key].flags & HA_FULLTEXT) &&
+ (table->keys_in_use_for_query & (((key_map)1) << key)))
+ {
+ ft_to_key[fts]=key;
+ ft_cnt[fts]=0;
+ fts++;
+ }
+ }
+
+ if (!fts)
+ {
+ my_printf_error(ER_FT_MATCHING_KEY_NOT_FOUND,
+ ER(ER_FT_MATCHING_KEY_NOT_FOUND),MYF(0));
+ return 1;
+ }
+
+ while ((item=(Item_field*)(li++)))
+ {
+ for (key=0 ; key<fts ; key++)
+ {
+ KEY *ft_key=&table->key_info[ft_to_key[key]];
+ uint key_parts=ft_key->key_parts;
+
+ for (uint part=0 ; part < key_parts ; part++)
+ {
+ if (item->field->eq(ft_key->key_part[part].field))
+ ft_cnt[key]++;
+ }
+ }
+ }
+
+ uint max_cnt=0, max_key=0;
+ for (key=0 ; key<fts ; key++)
+ {
+ if (ft_cnt[key] > max_cnt)
+ {
+ max_cnt=ft_cnt[key];
+ max_key=ft_to_key[key];
+ }
+ }
+
+ // for now, partial keys won't work. SerG
+
+ if (max_cnt < fields.elements ||
+ max_cnt < table->key_info[max_key].key_parts)
+ {
+ my_printf_error(ER_FT_MATCHING_KEY_NOT_FOUND,
+ ER(ER_FT_MATCHING_KEY_NOT_FOUND),MYF(0));
+ return 1;
+ }
+
+ this->key=max_key;
+
+ maybe_null=1;
+
+ return 0;
+}
+
+