mirror of
https://github.com/MariaDB/server.git
synced 2025-01-16 12:02:42 +01:00
2122fc2603
include/ft_global.h: fixed bug with ORDER BY sql/ha_myisam.cc: fixed bug with ORDER BY sql/item_func.cc: fixed bug with ORDER BY sql/item_func.h: fixed bug with ORDER BY sql/sql_select.cc: fixed bug with ORDER BY
2035 lines
44 KiB
C++
2035 lines
44 KiB
C++
/* 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;
|
|
#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,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 (!thd)
|
|
thd=current_thd;
|
|
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()
|
|
{
|
|
// Don't know how to return an error from val(), so NULL will be returned
|
|
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));
|
|
|
|
ft_tmp=key_item()->val_str(&tmp2);
|
|
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;
|
|
|
|
/* 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 */
|
|
/* I'd rather say now that const_item is assumed in quite a bit of
|
|
places, so it would be difficult to remove. 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;
|
|
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;
|
|
join_key=0;
|
|
|
|
return 0;
|
|
}
|
|
|
|
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;
|
|
}
|