/* 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> #include "slave.h" // for wait_for_master_pos /* 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() { // the integer result of division of two arguments needs to be computed // as a type-cast division of val(), not as diviion of val_int() of each // argument. For example, val_int(41.5/3.4) = val_int(12.206) = 12, but // if you do val_int(41.5)/val_int(3.4), as in the old code, we get 42/3= // 14, which is wrong. This would break sec_to_time(a/b), // from_unixtime(a/b), and // all functions that do val_int() on their arguments return (longlong)val(); } 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(¤t_thd->rand,tmp,tmp/2); #ifdef DELETE_ITEMS delete args[0]; #endif arg_count=0; } return rnd(¤t_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; #ifdef USE_MB uint start0=0; #endif 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) { if (check_stack_overrun(thd,buff)) return 0; // Fatal error flag is set! } else thd=current_thd; // In WHERE / const clause udf_func *tmp_udf=find_udf(u_d->name,(uint) 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,MY_MUTEX_INIT_SLOW); 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; } /* Wait until we are at or past the given position in the master binlog on the slave */ longlong Item_master_pos_wait::val_int() { THD* thd = current_thd; String *log_name = args[0]->val_str(&value); int event_count; null_value=0; if (thd->slave_thread || !log_name || !log_name->length()) { null_value = 1; return 0; } ulong pos = (ulong)args[1]->val_int(); if ((event_count = glob_mi.wait_for_pos(thd, log_name, pos)) == -1) { null_value = 1; event_count=0; } return event_count; } /* 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->update_query_id=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 (!thd) thd=current_thd; if (Item_func::fix_fields(thd,tables) || !(entry= get_variable(&thd->user_vars, name, 1))) return 1; entry->update_query_id=thd->query_id; 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,decimals); 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() { THD *thd=current_thd; maybe_null=1; decimals=NOT_FIXED_DEC; max_length=MAX_BLOB_WIDTH; if ((entry= get_variable(&thd->user_vars, name, 0))) const_var_flag= thd->query_id != entry->update_query_id; } enum Item_result Item_func_get_user_var::result_type() const { user_var_entry *entry; if (!(entry = (user_var_entry*) hash_search(¤t_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() { if (ft_handler==NULL) init_search(1); if ((null_value= (ft_handler==NULL))) return 0.0; if (join_key) { if (table->file->ft_handler) return ft_get_relevance(ft_handler); join_key=0; // Magic here ! See ha_myisam::ft_read() } /* we'll have to find ft_relevance manually in ft_handler array */ int a,b,c; FT_DOC *docs=ft_handler->doc; my_off_t docid=table->file->row_position(); if ((null_value=(docid==HA_OFFSET_ERROR))) return 0.0; // Assuming docs[] is sorted by dpos... for (a=0, b=ft_handler->ndocs, 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) return docs[a].weight; else return 0.0; } void Item_func_match::init_search(bool no_order) { if (ft_handler) return; if (master) { join_key=master->join_key=join_key|master->join_key; master->init_search(no_order); ft_handler=master->ft_handler; join_key=master->join_key; return; } String *ft_tmp=0; char tmp1[FT_QUERY_MAXLEN]; String tmp2(tmp1,sizeof(tmp1)); // MATCH ... AGAINST (NULL) is meaningless, but possible if (!(ft_tmp=key_item()->val_str(&tmp2))) { ft_tmp=&tmp2; tmp2.set("",0); } ft_handler=(FT_DOCLIST *) table->file->ft_init_ext(key, (byte*) ft_tmp->ptr(), ft_tmp->length(), join_key && !no_order); if (join_key) { table->file->ft_handler=ft_handler; return; } } bool Item_func_match::fix_fields(THD *thd,struct st_table_list *tlist) { List_iterator<Item> li(fields); Item *item; maybe_null=1; join_key=0; /* Serg: I'd rather say now that 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,tlist) || !const_item()) { my_error(ER_WRONG_ARGUMENTS,MYF(0),"AGAINST"); return 1; } while ((item=li++)) { if (item->fix_fields(thd,tlist)) return 1; if (item->type() == Item::REF_ITEM) li.replace(item= *((Item_ref *)item)->ref); if (item->type() != Item::FIELD_ITEM || !item->used_tables()) { my_error(ER_WRONG_ARGUMENTS,MYF(0),"MATCH"); return 1; } used_tables_cache|=item->used_tables(); } /* check that all columns come from the same table */ if (count_bits(used_tables_cache) != 1) { my_error(ER_WRONG_ARGUMENTS,MYF(0),"MATCH"); return 1; } const_item_cache=0; table=((Item_field *)fields.head())->field->table; 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, mkeys=0; for (key=0 ; key<fts ; key++) { if (ft_cnt[key] > max_cnt) { mkeys=0; max_cnt=ft_cnt[mkeys]=ft_cnt[key]; ft_to_key[mkeys]=ft_to_key[key]; continue; } if (max_cnt && ft_cnt[key] == max_cnt) { mkeys++; ft_cnt[mkeys]=ft_cnt[key]; ft_to_key[mkeys]=ft_to_key[key]; continue; } } for (key=0 ; key<=mkeys ; key++) { // for now, partial keys won't work. SerG if (max_cnt < fields.elements || max_cnt < table->key_info[ft_to_key[key]].key_parts) continue; this->key=ft_to_key[key]; return 0; } my_printf_error(ER_FT_MATCHING_KEY_NOT_FOUND, ER(ER_FT_MATCHING_KEY_NOT_FOUND),MYF(0)); return 1; } bool Item_func_match::eq(const Item *item) const { if (item->type() != FUNC_ITEM) return 0; if (func_name() != ((Item_func*)item)->func_name()) return 0; Item_func_match *ifm=(Item_func_match*) item; if (key == ifm->key && table == ifm->table && key_item()->eq(ifm->key_item())) return 1; return 0; } /*************************************************************************** System variables This has to be recoded after we get more than 3 system variables ****************************************************************************/ Item *get_system_var(LEX_STRING name) { if (!strcmp(name.str,"IDENTITY")) return new Item_int((char*) "@@IDENTITY", current_thd->insert_id(),21); my_error(ER_UNKNOWN_SYSTEM_VARIABLE,MYF(0),name); return 0; }