diff options
Diffstat (limited to 'sql/item_func.cc')
-rw-r--r-- | sql/item_func.cc | 1981 |
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(¤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,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(¤t_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(¤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() +{ + 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; +} + + |