summaryrefslogtreecommitdiff
path: root/sql/item.cc
diff options
context:
space:
mode:
Diffstat (limited to 'sql/item.cc')
-rw-r--r--sql/item.cc1168
1 files changed, 793 insertions, 375 deletions
diff --git a/sql/item.cc b/sql/item.cc
index 48449f9033d..0d4b25f4440 100644
--- a/sql/item.cc
+++ b/sql/item.cc
@@ -47,6 +47,16 @@ const String my_null_string("NULL", 4, default_charset_info);
static int save_field_in_field(Field *from, bool *null_value,
Field *to, bool no_conversions);
+
+/**
+ Compare two Items for List<Item>::add_unique()
+*/
+
+bool cmp_items(Item *a, Item *b)
+{
+ return a->eq(b, FALSE);
+}
+
/****************************************************************************/
/* Hybrid_type_traits {_real} */
@@ -213,10 +223,12 @@ bool Item::val_bool()
case STRING_RESULT:
return val_real() != 0.0;
case ROW_RESULT:
- default:
+ case TIME_RESULT:
+ case IMPOSSIBLE_RESULT:
DBUG_ASSERT(0);
return 0; // Wrong (but safe)
}
+ return 0; // Wrong (but safe)
}
@@ -254,7 +266,7 @@ String *Item::val_string_from_real(String *str)
double nr= val_real();
if (null_value)
return 0; /* purecov: inspected */
- str->set_real(nr,decimals, &my_charset_bin);
+ str->set_real(nr,decimals, &my_charset_numeric);
return str;
}
@@ -264,7 +276,7 @@ String *Item::val_string_from_int(String *str)
longlong nr= val_int();
if (null_value)
return 0;
- str->set_int(nr, unsigned_flag, &my_charset_bin);
+ str->set_int(nr, unsigned_flag, &my_charset_numeric);
return str;
}
@@ -280,6 +292,21 @@ String *Item::val_string_from_decimal(String *str)
}
+String *Item::val_string_from_date(String *str)
+{
+ MYSQL_TIME ltime;
+ if (get_date(&ltime, TIME_FUZZY_DATE) ||
+ str->alloc(MAX_DATE_STRING_REP_LENGTH))
+ {
+ null_value= 1;
+ return (String *) 0;
+ }
+ str->length(my_TIME_to_str(&ltime, const_cast<char*>(str->ptr()), decimals));
+ str->set_charset(&my_charset_numeric);
+ return str;
+}
+
+
my_decimal *Item::val_decimal_from_real(my_decimal *decimal_value)
{
double nr= val_real();
@@ -377,17 +404,20 @@ int Item::save_time_in_field(Field *field)
if (get_time(&ltime))
return set_field_to_null_with_conversions(field, 0);
field->set_notnull();
- return field->store_time(&ltime, MYSQL_TIMESTAMP_TIME);
+ return field->store_time_dec(&ltime, decimals);
}
int Item::save_date_in_field(Field *field)
{
MYSQL_TIME ltime;
- if (get_date(&ltime, TIME_FUZZY_DATE))
+ if (get_date(&ltime, TIME_FUZZY_DATE |
+ (current_thd->variables.sql_mode &
+ (MODE_NO_ZERO_IN_DATE | MODE_NO_ZERO_DATE |
+ MODE_INVALID_DATES))))
return set_field_to_null_with_conversions(field, 0);
field->set_notnull();
- return field->store_time(&ltime, MYSQL_TIMESTAMP_DATETIME);
+ return field->store_time_dec(&ltime, decimals);
}
@@ -428,10 +458,13 @@ Item::Item():
collation(&my_charset_bin, DERIVATION_COERCIBLE)
{
marker= 0;
- maybe_null=null_value=with_sum_func=unsigned_flag=0;
+ maybe_null=null_value=with_sum_func=with_field=unsigned_flag=0;
+ in_rollup= 0;
decimals= 0; max_length= 0;
with_subselect= 0;
- cmp_context= (Item_result)-1;
+ cmp_context= IMPOSSIBLE_RESULT;
+ /* Initially this item is not attached to any JOIN_TAB. */
+ join_tab_idx= MAX_TABLES;
/* Put item in free list so that we can free all items at end */
THD *thd= current_thd;
@@ -460,6 +493,7 @@ Item::Item():
tables.
*/
Item::Item(THD *thd, Item *item):
+ join_tab_idx(item->join_tab_idx),
is_expensive_cache(-1),
rsize(0),
str_value(item->str_value),
@@ -470,9 +504,11 @@ Item::Item(THD *thd, Item *item):
marker(item->marker),
decimals(item->decimals),
maybe_null(item->maybe_null),
+ in_rollup(item->in_rollup),
null_value(item->null_value),
unsigned_flag(item->unsigned_flag),
with_sum_func(item->with_sum_func),
+ with_field(item->with_field),
fixed(item->fixed),
is_autogenerated_name(item->is_autogenerated_name),
with_subselect(item->with_subselect),
@@ -512,11 +548,40 @@ void Item::print_item_w_name(String *str, enum_query_type query_type)
}
+void Item::print_value(String *str)
+{
+ char buff[MAX_FIELD_WIDTH];
+ String *ptr, tmp(buff,sizeof(buff),str->charset());
+ ptr= val_str(&tmp);
+ if (!ptr)
+ str->append("NULL");
+ else
+ {
+ switch (result_type()) {
+ case STRING_RESULT:
+ append_unescaped(str, ptr->ptr(), ptr->length());
+ break;
+ case DECIMAL_RESULT:
+ case REAL_RESULT:
+ case INT_RESULT:
+ str->append(*ptr);
+ break;
+ case ROW_RESULT:
+ case TIME_RESULT:
+ case IMPOSSIBLE_RESULT:
+ DBUG_ASSERT(0);
+ }
+ }
+}
+
+
void Item::cleanup()
{
DBUG_ENTER("Item::cleanup");
+ DBUG_PRINT("enter", ("this: %p", this));
fixed=0;
marker= 0;
+ join_tab_idx= MAX_TABLES;
if (orig_name)
name= orig_name;
DBUG_VOID_RETURN;
@@ -554,6 +619,45 @@ void Item::rename(char *new_name)
name= new_name;
}
+Item_result Item::cmp_type() const
+{
+ switch (field_type()) {
+ case MYSQL_TYPE_DECIMAL:
+ case MYSQL_TYPE_NEWDECIMAL:
+ return DECIMAL_RESULT;
+ case MYSQL_TYPE_TINY:
+ case MYSQL_TYPE_SHORT:
+ case MYSQL_TYPE_LONG:
+ case MYSQL_TYPE_LONGLONG:
+ case MYSQL_TYPE_INT24:
+ case MYSQL_TYPE_YEAR:
+ case MYSQL_TYPE_BIT:
+ return INT_RESULT;
+ case MYSQL_TYPE_FLOAT:
+ case MYSQL_TYPE_DOUBLE:
+ return REAL_RESULT;
+ case MYSQL_TYPE_NULL:
+ case MYSQL_TYPE_VARCHAR:
+ case MYSQL_TYPE_TINY_BLOB:
+ case MYSQL_TYPE_MEDIUM_BLOB:
+ case MYSQL_TYPE_LONG_BLOB:
+ case MYSQL_TYPE_BLOB:
+ case MYSQL_TYPE_VAR_STRING:
+ case MYSQL_TYPE_STRING:
+ case MYSQL_TYPE_ENUM:
+ case MYSQL_TYPE_SET:
+ case MYSQL_TYPE_GEOMETRY:
+ return STRING_RESULT;
+ case MYSQL_TYPE_TIMESTAMP:
+ case MYSQL_TYPE_DATE:
+ case MYSQL_TYPE_TIME:
+ case MYSQL_TYPE_DATETIME:
+ case MYSQL_TYPE_NEWDATE:
+ return TIME_RESULT;
+ };
+ DBUG_ASSERT(0);
+ return IMPOSSIBLE_RESULT;
+}
/**
Traverse item tree possibly transforming it (replacing items).
@@ -607,14 +711,14 @@ Item* Item::transform(Item_transformer transformer, uchar *arg)
A pointer to created wrapper item if successful, NULL - otherwise
*/
-Item* Item::set_expr_cache(THD *thd, List<Item *> &depends_on)
+Item* Item::set_expr_cache(THD *thd)
{
DBUG_ENTER("Item::set_expr_cache");
Item_cache_wrapper *wrapper;
if ((wrapper= new Item_cache_wrapper(this)) &&
!wrapper->fix_fields(thd, (Item**)&wrapper))
{
- if (wrapper->set_cache(thd, depends_on))
+ if (wrapper->set_cache(thd))
DBUG_RETURN(NULL);
DBUG_RETURN(wrapper);
}
@@ -691,13 +795,22 @@ void Item_ident::cleanup()
bool Item_ident::remove_dependence_processor(uchar * arg)
{
DBUG_ENTER("Item_ident::remove_dependence_processor");
- if (depended_from == (st_select_lex *) arg)
+ if (get_depended_from() == (st_select_lex *) arg)
depended_from= 0;
context= &((st_select_lex *) arg)->context;
DBUG_RETURN(0);
}
+bool Item_ident::collect_outer_ref_processor(uchar *param)
+{
+ Collect_deps_prm *prm= (Collect_deps_prm *)param;
+ if (depended_from && depended_from->nest_level < prm->nest_level)
+ prm->parameters->add_unique(this, &cmp_items);
+ return FALSE;
+}
+
+
/**
Store the pointer to this item field into a list if not already there.
@@ -806,6 +919,23 @@ bool Item_field::register_field_in_bitmap(uchar *arg)
return 0;
}
+
+/*
+ Mark field in write_map
+
+ NOTES
+ This is used by UPDATE to register underlying fields of used view fields.
+*/
+
+bool Item_field::register_field_in_write_map(uchar *arg)
+{
+ TABLE *table= (TABLE *) arg;
+ if (field->table == table || !table)
+ bitmap_set_bit(field->table->write_set, field->field_index);
+ return 0;
+}
+
+
bool Item::check_cols(uint c)
{
if (c != 1)
@@ -828,7 +958,8 @@ void Item::set_name(const char *str, uint length, CHARSET_INFO *cs)
}
if (cs->ctype)
{
- uint orig_len= length;
+ const char *str_start= str;
+
/*
This will probably need a better implementation in the future:
a function in CHARSET_INFO structure.
@@ -838,16 +969,20 @@ void Item::set_name(const char *str, uint length, CHARSET_INFO *cs)
length--;
str++;
}
- if (orig_len != length && !is_autogenerated_name)
+ if (str != str_start && !is_autogenerated_name)
{
+ char buff[SAFE_NAME_LEN];
+ strmake(buff, str_start,
+ min(sizeof(buff)-1, length + (int) (str-str_start)));
+
if (length == 0)
push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
ER_NAME_BECOMES_EMPTY, ER(ER_NAME_BECOMES_EMPTY),
- str + length - orig_len);
+ buff);
else
push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
ER_REMOVED_SPACES, ER(ER_REMOVED_SPACES),
- str + length - orig_len);
+ buff);
}
}
if (!my_charset_same(cs, system_charset_info))
@@ -1043,12 +1178,52 @@ bool Item_string::eq(const Item *item, bool binary_cmp) const
/**
Get the value of the function as a MYSQL_TIME structure.
- As a extra convenience the time structure is reset on error!
+ As a extra convenience the time structure is reset on error or NULL values!
*/
bool Item::get_date(MYSQL_TIME *ltime,uint fuzzydate)
{
- if (result_type() == STRING_RESULT)
+ if (field_type() == MYSQL_TYPE_TIME)
+ fuzzydate|= TIME_TIME_ONLY;
+
+ switch (result_type()) {
+ case INT_RESULT:
+ {
+ longlong value= val_int();
+ if (field_type() == MYSQL_TYPE_YEAR)
+ {
+ if (max_length == 2)
+ {
+ if (value < 70)
+ value+= 2000;
+ else if (value <= 1900)
+ value+= 1900;
+ }
+ value*= 10000; /* make it YYYYMMHH */
+ }
+ if (null_value || int_to_datetime_with_warn(value, ltime, fuzzydate,
+ field_name_or_null()))
+ goto err;
+ break;
+ }
+ case REAL_RESULT:
+ {
+ double value= val_real();
+ if (null_value || double_to_datetime_with_warn(value, ltime, fuzzydate,
+ field_name_or_null()))
+ goto err;
+ break;
+ }
+ case DECIMAL_RESULT:
+ {
+ my_decimal value, *res;
+ if (!(res= val_decimal(&value)) ||
+ decimal_to_datetime_with_warn(res, ltime, fuzzydate,
+ field_name_or_null()))
+ goto err;
+ break;
+ }
+ case STRING_RESULT:
{
char buff[40];
String tmp(buff,sizeof(buff), &my_charset_bin),*res;
@@ -1056,25 +1231,12 @@ bool Item::get_date(MYSQL_TIME *ltime,uint fuzzydate)
str_to_datetime_with_warn(res->charset(), res->ptr(), res->length(),
ltime, fuzzydate) <= MYSQL_TIMESTAMP_ERROR)
goto err;
+ break;
}
- else
- {
- int was_cut;
- longlong value= val_int();
-
- if (null_value)
- goto err;
-
- if (number_to_datetime(value, ltime, fuzzydate, &was_cut) == LL(-1))
- {
- char buff[22], *end;
- end= longlong10_to_str(value, buff, -10);
- make_truncated_value_warning(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
- buff, (int) (end-buff), MYSQL_TIMESTAMP_NONE,
- NullS);
- goto err;
- }
+ default:
+ DBUG_ASSERT(0);
}
+
return 0;
err:
@@ -1082,23 +1244,20 @@ err:
return 1;
}
-/**
- Get time of first argument.\
-
- As a extra convenience the time structure is reset on error!
-*/
-
-bool Item::get_time(MYSQL_TIME *ltime)
+bool Item::get_seconds(ulonglong *sec, ulong *sec_part)
{
- char buff[40];
- String tmp(buff,sizeof(buff),&my_charset_bin),*res;
- if (!(res=val_str_ascii(&tmp)) ||
- str_to_time_with_warn(res->charset(), res->ptr(), res->length(), ltime))
- {
- bzero((char*) ltime,sizeof(*ltime));
- return 1;
+ if (result_type() == INT_RESULT)
+ { // optimize for an important special case
+ longlong val= val_int();
+ bool neg= val < 0 && !unsigned_flag;
+ *sec= neg ? -val : val;
+ *sec_part= 0;
+ return neg;
}
- return 0;
+ my_decimal tmp, *dec= val_decimal(&tmp);
+ if (!dec)
+ return 0;
+ return my_decimal2seconds(dec, sec, sec_part);
}
CHARSET_INFO *Item::default_charset()
@@ -1124,6 +1283,7 @@ int Item::save_in_field_no_warnings(Field *field, bool no_conversions)
my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->write_set);
ulonglong sql_mode= thd->variables.sql_mode;
thd->variables.sql_mode&= ~(MODE_NO_ZERO_IN_DATE | MODE_NO_ZERO_DATE);
+ thd->variables.sql_mode|= MODE_INVALID_DATES;
thd->count_cuted_fields= CHECK_FIELD_IGNORE;
res= save_in_field(field, no_conversions);
@@ -1566,6 +1726,11 @@ void Item::split_sum_func2(THD *thd, Item **ref_pointer_array,
*/
Item_aggregate_ref *item_ref;
uint el= fields.elements;
+ /*
+ If this is an item_ref, get the original item
+ This is a safety measure if this is called for things that is
+ already a reference.
+ */
Item *real_itm= real_item();
ref_pointer_array[el]= real_itm;
@@ -1995,6 +2160,7 @@ Item_field::Item_field(Field *f)
if this item is to be reused
*/
orig_table_name= orig_field_name= "";
+ with_field= 1;
}
@@ -2043,6 +2209,7 @@ Item_field::Item_field(THD *thd, Name_resolution_context *context_arg,
name= (char*) orig_field_name;
}
set_field(f);
+ with_field= 1;
}
@@ -2057,6 +2224,7 @@ Item_field::Item_field(Name_resolution_context *context_arg,
collation.set(DERIVATION_IMPLICIT);
if (select && select->parsing_place != IN_HAVING)
select->select_n_where_fields++;
+ with_field= 1;
}
/**
@@ -2073,6 +2241,7 @@ Item_field::Item_field(THD *thd, Item_field *item)
any_privileges(item->any_privileges)
{
collation.set(DERIVATION_IMPLICIT);
+ with_field= 1;
}
@@ -2310,24 +2479,14 @@ bool Item_field::get_date(MYSQL_TIME *ltime,uint fuzzydate)
bool Item_field::get_date_result(MYSQL_TIME *ltime,uint fuzzydate)
{
- if ((null_value=result_field->is_null()) ||
- result_field->get_date(ltime,fuzzydate))
+ if (result_field->is_null() || result_field->get_date(ltime,fuzzydate))
{
bzero((char*) ltime,sizeof(*ltime));
- return 1;
+ return (null_value= 1);
}
- return 0;
+ return (null_value= 0);
}
-bool Item_field::get_time(MYSQL_TIME *ltime)
-{
- if ((null_value=field->is_null()) || field->get_time(ltime))
- {
- bzero((char*) ltime,sizeof(*ltime));
- return 1;
- }
- return 0;
-}
void Item_field::save_result(Field *to)
{
@@ -2377,10 +2536,12 @@ bool Item_field::val_bool_result()
case STRING_RESULT:
return result_field->val_real() != 0.0;
case ROW_RESULT:
- default:
+ case TIME_RESULT:
+ case IMPOSSIBLE_RESULT:
DBUG_ASSERT(0);
return 0; // Shut up compiler
}
+ return 0;
}
@@ -2424,13 +2585,17 @@ table_map Item_field::used_tables() const
{
if (field->table->const_table)
return 0; // const item
- return (depended_from ? OUTER_REF_TABLE_BIT : field->table->map);
+ return (get_depended_from() ? OUTER_REF_TABLE_BIT : field->table->map);
}
+table_map Item_field::all_used_tables() const
+{
+ return (get_depended_from() ? OUTER_REF_TABLE_BIT : field->table->map);
+}
void Item_field::fix_after_pullout(st_select_lex *new_parent, Item **ref)
{
- if (new_parent == depended_from)
+ if (new_parent == get_depended_from())
depended_from= NULL;
Name_resolution_context *ctx= new Name_resolution_context();
ctx->outer_context= NULL; // We don't build a complete name resolver
@@ -2742,19 +2907,20 @@ void Item_string::print(String *str, enum_query_type query_type)
double
-double_from_string_with_check (CHARSET_INFO *cs, const char *cptr, char *end)
+double_from_string_with_check(CHARSET_INFO *cs, const char *cptr,
+ const char *end)
{
int error;
- char *org_end;
+ char *end_of_num= (char*) end;
double tmp;
- org_end= end;
- tmp= my_strntod(cs, (char*) cptr, end - cptr, &end, &error);
- if (error || (end != org_end && !check_if_only_end_space(cs, end, org_end)))
+ tmp= my_strntod(cs, (char*) cptr, end - cptr, &end_of_num, &error);
+ if (error || (end != end_of_num &&
+ !check_if_only_end_space(cs, end_of_num, end)))
{
- ErrConvString err(cptr, cs);
+ ErrConvString err(cptr, end - cptr, cs);
/*
- We can use str_value.ptr() here as Item_string is gurantee to put an
+ We can use err.ptr() here as ErrConvString is guranteed to put an
end \0 here.
*/
push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
@@ -2769,28 +2935,31 @@ double_from_string_with_check (CHARSET_INFO *cs, const char *cptr, char *end)
double Item_string::val_real()
{
DBUG_ASSERT(fixed == 1);
- return double_from_string_with_check (str_value.charset(), str_value.ptr(),
- (char *) str_value.ptr() + str_value.length());
+ return double_from_string_with_check(str_value.charset(),
+ str_value.ptr(),
+ str_value.ptr() +
+ str_value.length());
}
longlong
-longlong_from_string_with_check (CHARSET_INFO *cs, const char *cptr, char *end)
+longlong_from_string_with_check(CHARSET_INFO *cs, const char *cptr,
+ const char *end)
{
int err;
longlong tmp;
- char *org_end= end;
+ char *end_of_num= (char*) end;
- tmp= (*(cs->cset->strtoll10))(cs, cptr, &end, &err);
+ tmp= (*(cs->cset->strtoll10))(cs, cptr, &end_of_num, &err);
/*
TODO: Give error if we wanted a signed integer and we got an unsigned
one
*/
if (!current_thd->no_errors &&
(err > 0 ||
- (end != org_end && !check_if_only_end_space(cs, end, org_end))))
+ (end != end_of_num && !check_if_only_end_space(cs, end_of_num, end))))
{
- ErrConvString err(cptr, cs);
+ ErrConvString err(cptr, end - cptr, cs);
push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
ER_TRUNCATED_WRONG_VALUE,
ER(ER_TRUNCATED_WRONG_VALUE), "INTEGER",
@@ -2808,7 +2977,7 @@ longlong Item_string::val_int()
{
DBUG_ASSERT(fixed == 1);
return longlong_from_string_with_check(str_value.charset(), str_value.ptr(),
- (char *) str_value.ptr()+ str_value.length());
+ str_value.ptr()+ str_value.length());
}
@@ -3000,19 +3169,19 @@ void Item_param::set_time(MYSQL_TIME *tm, timestamp_type time_type,
if (value.time.year > 9999 || value.time.month > 12 ||
value.time.day > 31 ||
(time_type != MYSQL_TIMESTAMP_TIME && value.time.hour > 23) ||
- value.time.minute > 59 || value.time.second > 59)
+ value.time.minute > 59 || value.time.second > 59 ||
+ value.time.second_part > TIME_MAX_SECOND_PART)
{
- char buff[MAX_DATE_STRING_REP_LENGTH];
- uint length= my_TIME_to_str(&value.time, buff);
+ ErrConvTime str(&value.time);
make_truncated_value_warning(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
- buff, length, time_type, 0);
+ &str, time_type, 0);
set_zero_time(&value.time, MYSQL_TIMESTAMP_ERROR);
}
state= TIME_VALUE;
maybe_null= 0;
max_length= max_length_arg;
- decimals= 0;
+ decimals= tm->second_part > 0 ? TIME_SECOND_PART_DIGITS : 0;
DBUG_VOID_RETURN;
}
@@ -3099,10 +3268,12 @@ bool Item_param::set_from_user_var(THD *thd, const user_var_entry *entry)
case REAL_RESULT:
set_double(*(double*)entry->value);
item_type= Item::REAL_ITEM;
+ param_type= MYSQL_TYPE_DOUBLE;
break;
case INT_RESULT:
set_int(*(longlong*)entry->value, MY_INT64_NUM_DECIMAL_DIGITS);
item_type= Item::INT_ITEM;
+ param_type= MYSQL_TYPE_LONGLONG;
break;
case STRING_RESULT:
{
@@ -3125,6 +3296,7 @@ bool Item_param::set_from_user_var(THD *thd, const user_var_entry *entry)
charset of connection, so we have to set it later.
*/
item_type= Item::STRING_ITEM;
+ param_type= MYSQL_TYPE_VARCHAR;
if (set_str((const char *)entry->value, entry->length))
DBUG_RETURN(1);
@@ -3140,9 +3312,12 @@ bool Item_param::set_from_user_var(THD *thd, const user_var_entry *entry)
my_decimal_precision_to_length_no_truncation(ent_value->precision(),
decimals, unsigned_flag);
item_type= Item::DECIMAL_ITEM;
+ param_type= MYSQL_TYPE_NEWDECIMAL;
break;
}
- default:
+ case ROW_RESULT:
+ case TIME_RESULT:
+ case IMPOSSIBLE_RESULT:
DBUG_ASSERT(0);
set_null();
}
@@ -3204,7 +3379,7 @@ int Item_param::save_in_field(Field *field, bool no_conversions)
case DECIMAL_VALUE:
return field->store_decimal(&decimal_value);
case TIME_VALUE:
- field->store_time(&value.time, value.time.time_type);
+ field->store_time_dec(&value.time, decimals);
return 0;
case STRING_VALUE:
case LONG_DATA_VALUE:
@@ -3220,21 +3395,6 @@ int Item_param::save_in_field(Field *field, bool no_conversions)
}
-bool Item_param::get_time(MYSQL_TIME *res)
-{
- if (state == TIME_VALUE)
- {
- *res= value.time;
- return 0;
- }
- /*
- If parameter value isn't supplied assertion will fire in val_str()
- which is called from Item::get_time().
- */
- return Item::get_time(res);
-}
-
-
bool Item_param::get_date(MYSQL_TIME *res, uint fuzzydate)
{
if (state == TIME_VALUE)
@@ -3364,7 +3524,8 @@ String *Item_param::val_str(String* str)
{
if (str->reserve(MAX_DATE_STRING_REP_LENGTH))
break;
- str->length((uint) my_TIME_to_str(&value.time, (char*) str->ptr()));
+ str->length((uint) my_TIME_to_str(&value.time, (char*) str->ptr(),
+ decimals));
str->set_charset(&my_charset_bin);
return str;
}
@@ -3416,7 +3577,7 @@ const String *Item_param::query_val_str(String* str) const
buf= str->c_ptr_quick();
ptr= buf;
*ptr++= '\'';
- ptr+= (uint) my_TIME_to_str(&value.time, ptr);
+ ptr+= (uint) my_TIME_to_str(&value.time, ptr, decimals);
*ptr++= '\'';
str->length((uint32) (ptr - buf));
break;
@@ -3759,6 +3920,7 @@ void Item_param::make_field(Send_field *field)
/****************************************************************************
Item_copy
****************************************************************************/
+
Item_copy *Item_copy::create (Item *item)
{
switch (item->result_type())
@@ -3772,7 +3934,9 @@ Item_copy *Item_copy::create (Item *item)
new Item_copy_uint (item) : new Item_copy_int (item);
case DECIMAL_RESULT:
return new Item_copy_decimal (item);
- default:
+ case TIME_RESULT:
+ case ROW_RESULT:
+ case IMPOSSIBLE_RESULT:
DBUG_ASSERT (0);
}
/* should not happen */
@@ -3845,8 +4009,7 @@ void Item_copy_int::copy()
null_value=item->null_value;
}
-static int save_int_value_in_field (Field *field, longlong nr,
- bool null_value, bool unsigned_flag);
+static int save_int_value_in_field (Field *, longlong, bool, bool);
int Item_copy_int::save_in_field(Field *field, bool no_conversions)
{
@@ -4379,6 +4542,34 @@ resolve_ref_in_select_and_group(THD *thd, Item_ident *ref, SELECT_LEX *select)
}
+/*
+ @brief
+ Whether a table belongs to an outer select.
+
+ @param table table to check
+ @param select current select
+
+ @details
+ Try to find select the table belongs to by ascending the derived tables chain.
+*/
+
+static
+bool is_outer_table(TABLE_LIST *table, SELECT_LEX *select)
+{
+ DBUG_ASSERT(table->select_lex != select);
+ TABLE_LIST *tl;
+
+ for (tl= select->master_unit()->derived;
+ tl && tl->is_merged_derived();
+ select= tl->select_lex, tl= select->master_unit()->derived)
+ {
+ if (tl->select_lex == table->select_lex)
+ return FALSE;
+ }
+ return TRUE;
+}
+
+
/**
Resolve the name of an outer select column reference.
@@ -4547,9 +4738,6 @@ Item_field::fix_outer_field(THD *thd, Field **from_field, Item **reference)
((ref_type == REF_ITEM || ref_type == FIELD_ITEM) ?
(Item_ident*) (*reference) :
0));
- context->select_lex->
- register_dependency_item(last_checked_context->select_lex,
- reference);
/*
A reference to a view field had been found and we
substituted it instead of this Item (find_field_in_tables
@@ -4650,9 +4838,6 @@ Item_field::fix_outer_field(THD *thd, Field **from_field, Item **reference)
mark_as_dependent(thd, last_checked_context->select_lex,
context->select_lex, rf,
rf);
- context->select_lex->
- register_dependency_item(last_checked_context->select_lex,
- reference);
return 0;
}
@@ -4661,9 +4846,6 @@ Item_field::fix_outer_field(THD *thd, Field **from_field, Item **reference)
mark_as_dependent(thd, last_checked_context->select_lex,
context->select_lex,
this, (Item_ident*)*reference);
- context->select_lex->
- register_dependency_item(last_checked_context->select_lex,
- reference);
if (last_checked_context->select_lex->having_fix_field)
{
Item_ref *rf;
@@ -4827,7 +5009,8 @@ bool Item_field::fix_fields(THD *thd, Item **reference)
if (!outer_fixed && cached_table && cached_table->select_lex &&
context->select_lex &&
- cached_table->select_lex != context->select_lex)
+ cached_table->select_lex != context->select_lex &&
+ is_outer_table(cached_table, context->select_lex))
{
int ret;
if ((ret= fix_outer_field(thd, &from_field, reference)) < 0)
@@ -5022,13 +5205,14 @@ Item_equal *Item_field::find_item_equal(COND_EQUAL *cond_equal)
/**
- Check whether a field can be substituted by an equal item.
+ Check whether a field item can be substituted for an equal item
- The function checks whether a substitution of the field
- occurrence for an equal item is valid.
+ @details
+ The function checks whether a substitution of a field item for
+ an equal item is valid.
- @param arg *arg != NULL <-> the field is in the context where
- substitution for an equal item is valid
+ @param arg *arg != NULL <-> the field is in the context
+ where substitution for an equal item is valid
@note
The following statement is not always true:
@@ -5053,7 +5237,10 @@ Item_equal *Item_field::find_item_equal(COND_EQUAL *cond_equal)
bool Item_field::subst_argument_checker(uchar **arg)
{
- return (result_type() != STRING_RESULT) || (*arg);
+ return *arg &&
+ (*arg == (uchar *) Item::ANY_SUBST ||
+ result_type() != STRING_RESULT ||
+ (field->flags & BINARY_FLAG));
}
@@ -5131,12 +5318,7 @@ Item *Item_field::equal_fields_propagator(uchar *arg)
item= this;
else if (field && (field->flags & ZEROFILL_FLAG) && IS_NUM(field->type()))
{
- /*
- We don't need to zero-fill timestamp columns here because they will be
- first converted to a string (in date/time format) and compared as such if
- compared with another string.
- */
- if (item && field->type() != FIELD_TYPE_TIMESTAMP && cmp_context != INT_RESULT)
+ if (item && (cmp_context == STRING_RESULT || cmp_context == IMPOSSIBLE_RESULT))
convert_zerofill_number_to_string(&item, (Field_num *)field);
else
item= this;
@@ -5163,7 +5345,8 @@ bool Item_field::set_no_const_sub(uchar *arg)
Replace an Item_field for an equal Item_field that evaluated earlier
(if any).
- The function returns a pointer to an item that is taken from
+ If this->item_equal points to some item and coincides with arg then
+ the function returns a pointer to an item that is taken from
the very beginning of the item_equal list which the Item_field
object refers to (belongs to) unless item_equal contains a constant
item. In this case the function returns this constant item,
@@ -5171,12 +5354,12 @@ bool Item_field::set_no_const_sub(uchar *arg)
If the Item_field object does not refer any Item_equal object
'this' is returned .
- @param arg a dummy parameter, is not used here
+ @param arg NULL or points to so some item of the Item_equal type
@note
This function is supposed to be called as a callback parameter in calls
- of the thransformer method.
+ of the transformer method.
@return
- pointer to a replacement Item_field if there is a better equal item or
@@ -5186,7 +5369,7 @@ bool Item_field::set_no_const_sub(uchar *arg)
Item *Item_field::replace_equal_field(uchar *arg)
{
- if (item_equal)
+ if (item_equal && item_equal == (Item_equal *) arg)
{
Item *const_item= item_equal->get_const();
if (const_item)
@@ -5195,8 +5378,10 @@ Item *Item_field::replace_equal_field(uchar *arg)
return this;
return const_item;
}
- Item_field *subst= item_equal->get_first(this);
- if (subst && field->table != subst->field->table && !field->eq(subst->field))
+ Item_field *subst= (Item_field *)(item_equal->get_first(this));
+ if (subst)
+ subst= (Item_field *) (subst->real_item());
+ if (subst && !field->eq(subst->field))
return subst;
}
return this;
@@ -5254,10 +5439,12 @@ enum_field_types Item::field_type() const
case DECIMAL_RESULT: return MYSQL_TYPE_NEWDECIMAL;
case REAL_RESULT: return MYSQL_TYPE_DOUBLE;
case ROW_RESULT:
- default:
+ case TIME_RESULT:
+ case IMPOSSIBLE_RESULT:
DBUG_ASSERT(0);
return MYSQL_TYPE_VARCHAR;
}
+ return MYSQL_TYPE_VARCHAR;
}
@@ -5434,16 +5621,19 @@ Field *Item::tmp_table_field_from_field_type(TABLE *table, bool fixed_length)
break;
case MYSQL_TYPE_NEWDATE:
case MYSQL_TYPE_DATE:
- field= new Field_newdate(maybe_null, name, &my_charset_bin);
+ field= new Field_newdate(0, null_ptr, 0, Field::NONE, name, &my_charset_bin);
break;
case MYSQL_TYPE_TIME:
- field= new Field_time(maybe_null, name, &my_charset_bin);
+ field= new_Field_time(0, null_ptr, 0, Field::NONE, name,
+ decimals, &my_charset_bin);
break;
case MYSQL_TYPE_TIMESTAMP:
- field= new Field_timestamp(maybe_null, name, &my_charset_bin);
+ field= new_Field_timestamp(0, null_ptr, 0,
+ Field::NONE, name, 0, decimals, &my_charset_bin);
break;
case MYSQL_TYPE_DATETIME:
- field= new Field_datetime(maybe_null, name, &my_charset_bin);
+ field= new_Field_datetime(0, null_ptr, 0, Field::NONE, name,
+ decimals, &my_charset_bin);
break;
case MYSQL_TYPE_YEAR:
field= new Field_year((uchar*) 0, max_length, null_ptr, 0, Field::NONE,
@@ -5615,7 +5805,7 @@ int Item_null::save_safe_in_field(Field *field)
Item uses str_value to store something, it should
reimplement it's ::save_in_field() as Item_string, for example, does.
- Note: all Item_XXX::val_str(str) methods must NOT rely on the fact that
+ Note: all Item_XXX::val_str(str) methods must NOT assume that
str != str_value. For example, see fix for bug #44743.
*/
@@ -5641,15 +5831,6 @@ int Item::save_in_field(Field *field, bool no_conversions)
error=field->store(result->ptr(),result->length(),cs);
str_value.set_quick(0, 0, cs);
}
- else if (result_type() == REAL_RESULT &&
- field->result_type() == STRING_RESULT)
- {
- double nr= val_real();
- if (null_value)
- return set_field_to_null_with_conversions(field, no_conversions);
- field->set_notnull();
- error= field->store(nr);
- }
else if (result_type() == REAL_RESULT)
{
double nr= val_real();
@@ -5687,12 +5868,6 @@ int Item_string::save_in_field(Field *field, bool no_conversions)
}
-int Item_uint::save_in_field(Field *field, bool no_conversions)
-{
- /* Item_int::save_in_field handles both signed and unsigned. */
- return Item_int::save_in_field(field, no_conversions);
-}
-
static int save_int_value_in_field (Field *field, longlong nr,
bool null_value, bool unsigned_flag)
{
@@ -5709,6 +5884,22 @@ int Item_int::save_in_field(Field *field, bool no_conversions)
}
+void Item_datetime::set(longlong packed)
+{
+ unpack_time(packed, &ltime);
+}
+
+int Item_datetime::save_in_field(Field *field, bool no_conversions)
+{
+ field->set_notnull();
+ return field->store_time_dec(&ltime, decimals);
+}
+
+longlong Item_datetime::val_int()
+{
+ return TIME_to_ulonglong(&ltime);
+}
+
int Item_decimal::save_in_field(Field *field, bool no_conversions)
{
field->set_notnull();
@@ -5726,7 +5917,9 @@ bool Item_int::eq(const Item *arg, bool binary_cmp) const
a basic constant.
*/
Item *item= (Item*) arg;
- return item->val_int() == value && item->unsigned_flag == unsigned_flag;
+ return (item->val_int() == value &&
+ ((longlong) value >= 0 ||
+ (item->unsigned_flag == unsigned_flag)));
}
return FALSE;
}
@@ -6087,7 +6280,10 @@ bool Item::send(Protocol *protocol, String *buffer)
{
String *res;
if ((res=val_str(buffer)))
+ {
+ DBUG_ASSERT(!null_value);
result= protocol->store(res->ptr(),res->length(),res->charset());
+ }
else
{
DBUG_ASSERT(null_value);
@@ -6154,7 +6350,7 @@ bool Item::send(Protocol *protocol, String *buffer)
if (f_type == MYSQL_TYPE_DATE)
return protocol->store_date(&tm);
else
- result= protocol->store(&tm);
+ result= protocol->store(&tm, decimals);
}
break;
}
@@ -6163,7 +6359,7 @@ bool Item::send(Protocol *protocol, String *buffer)
MYSQL_TIME tm;
get_time(&tm);
if (!null_value)
- result= protocol->store_time(&tm);
+ result= protocol->store_time(&tm, decimals);
break;
}
}
@@ -6306,17 +6502,7 @@ void Item_field::print(String *str, enum_query_type query_type)
{
if (field && field->table->const_table)
{
- char buff[MAX_FIELD_WIDTH];
- String tmp(buff,sizeof(buff),str->charset());
- field->val_str(&tmp);
- if (field->is_null())
- str->append("NULL");
- else
- {
- str->append('\'');
- str->append(tmp);
- str->append('\'');
- }
+ print_value(str);
return;
}
Item_ident::print(str, query_type);
@@ -6328,7 +6514,7 @@ Item_ref::Item_ref(Name_resolution_context *context_arg,
const char *field_name_arg,
bool alias_name_used_arg)
:Item_ident(context_arg, NullS, table_name_arg, field_name_arg),
- result_field(0), ref(item)
+ result_field(0), ref(item), reference_trough_name(0)
{
alias_name_used= alias_name_used_arg;
/*
@@ -6354,8 +6540,9 @@ public:
st_select_lex *sel;
for (sel= current_select; sel; sel= sel->outer_select())
{
+ List_iterator<TABLE_LIST> li(sel->leaf_tables);
TABLE_LIST *tbl;
- for (tbl= sel->leaf_tables; tbl; tbl= tbl->next_leaf)
+ while ((tbl= li++))
{
if (tbl->table == item->field->table)
{
@@ -6371,7 +6558,7 @@ public:
Item_ref::Item_ref(TABLE_LIST *view_arg, Item **item,
const char *field_name_arg, bool alias_name_used_arg)
:Item_ident(view_arg, field_name_arg),
- result_field(NULL), ref(item)
+ result_field(NULL), ref(item), reference_trough_name(0)
{
alias_name_used= alias_name_used_arg;
/*
@@ -6454,6 +6641,7 @@ bool Item_ref::fix_fields(THD *thd, Item **reference)
if (!ref || ref == not_found_item)
{
+ DBUG_ASSERT(reference_trough_name != 0);
if (!(ref= resolve_ref_in_select_and_group(thd, this,
context->select_lex)))
goto error; /* Some error occurred (e.g. ambiguous names). */
@@ -6555,9 +6743,6 @@ bool Item_ref::fix_fields(THD *thd, Item **reference)
refer_type == FIELD_ITEM) ?
(Item_ident*) (*reference) :
0));
- context->select_lex->
- register_dependency_item(last_checked_context->select_lex,
- reference);
/*
view reference found, we substituted it instead of this
Item, so can quit
@@ -6608,9 +6793,6 @@ bool Item_ref::fix_fields(THD *thd, Item **reference)
thd->change_item_tree(reference, fld);
mark_as_dependent(thd, last_checked_context->select_lex,
thd->lex->current_select, fld, fld);
- context->select_lex->
- register_dependency_item(last_checked_context->select_lex,
- reference);
/*
A reference is resolved to a nest level that's outer or the same as
the nest level of the enclosing set function : adjust the value of
@@ -6634,9 +6816,6 @@ bool Item_ref::fix_fields(THD *thd, Item **reference)
DBUG_ASSERT(*ref && (*ref)->fixed);
mark_as_dependent(thd, last_checked_context->select_lex,
context->select_lex, this, this);
- context->select_lex->
- register_dependency_item(last_checked_context->select_lex,
- reference);
/*
A reference is resolved to a nest level that's outer or the same as
the nest level of the enclosing set function : adjust the value of
@@ -6649,13 +6828,8 @@ bool Item_ref::fix_fields(THD *thd, Item **reference)
last_checked_context->select_lex->nest_level);
}
}
- else
+ else if (ref_type() != VIEW_REF)
{
- if (depended_from && reference)
- {
- DBUG_ASSERT(context->select_lex != depended_from);
- context->select_lex->register_dependency_item(depended_from, reference);
- }
/*
It could be that we're referring to something that's in ancestor selects.
We must make an appropriate mark_as_dependent() call for each such
@@ -6712,6 +6886,7 @@ void Item_ref::set_properties()
split_sum_func() doesn't try to change the reference.
*/
with_sum_func= (*ref)->with_sum_func;
+ with_field= (*ref)->with_field;
unsigned_flag= (*ref)->unsigned_flag;
fixed= 1;
if (alias_name_used)
@@ -6728,10 +6903,100 @@ void Item_ref::cleanup()
DBUG_ENTER("Item_ref::cleanup");
Item_ident::cleanup();
result_field= 0;
+ if (reference_trough_name)
+ {
+ /* We have to reset the reference as it may been freed */
+ ref= 0;
+ }
DBUG_VOID_RETURN;
}
+/**
+ Transform an Item_ref object with a transformer callback function.
+
+ The function first applies the transform method to the item
+ referenced by this Item_reg object. If this returns a new item the
+ old item is substituted for a new one. After this the transformer
+ is applied to the Item_ref object.
+
+ @param transformer the transformer callback function to be applied to
+ the nodes of the tree of the object
+ @param argument parameter to be passed to the transformer
+
+ @return Item returned as the result of transformation of the Item_ref object
+ @retval !NULL The transformation was successful
+ @retval NULL Out of memory error
+*/
+
+Item* Item_ref::transform(Item_transformer transformer, uchar *arg)
+{
+ DBUG_ASSERT(!current_thd->stmt_arena->is_stmt_prepare());
+ DBUG_ASSERT((*ref) != NULL);
+
+ /* Transform the object we are referencing. */
+ Item *new_item= (*ref)->transform(transformer, arg);
+ if (!new_item)
+ return NULL;
+
+ /*
+ THD::change_item_tree() should be called only if the tree was
+ really transformed, i.e. when a new item has been created.
+ Otherwise we'll be allocating a lot of unnecessary memory for
+ change records at each execution.
+ */
+ if (*ref != new_item)
+ current_thd->change_item_tree(ref, new_item);
+
+ /* Transform the item ref object. */
+ return (this->*transformer)(arg);
+}
+
+
+/**
+ Compile an Item_ref object with a processor and a transformer
+ callback functions.
+
+ First the function applies the analyzer to the Item_ref object. Then
+ if the analizer succeeeds we first applies the compile method to the
+ object the Item_ref object is referencing. If this returns a new
+ item the old item is substituted for a new one. After this the
+ transformer is applied to the Item_ref object itself.
+ The compile function is not called if the analyzer returns NULL
+ in the parameter arg_p.
+
+ @param analyzer the analyzer callback function to be applied to the
+ nodes of the tree of the object
+ @param[in,out] arg_p parameter to be passed to the processor
+ @param transformer the transformer callback function to be applied to the
+ nodes of the tree of the object
+ @param arg_t parameter to be passed to the transformer
+
+ @return Item returned as the result of transformation of the Item_ref object
+*/
+
+Item* Item_ref::compile(Item_analyzer analyzer, uchar **arg_p,
+ Item_transformer transformer, uchar *arg_t)
+{
+ /* Analyze this Item object. */
+ if (!(this->*analyzer)(arg_p))
+ return NULL;
+
+ /* Compile the Item we are referencing. */
+ DBUG_ASSERT((*ref) != NULL);
+ if (*arg_p)
+ {
+ uchar *arg_v= *arg_p;
+ Item *new_item= (*ref)->compile(analyzer, &arg_v, transformer, arg_t);
+ if (new_item && *ref != new_item)
+ current_thd->change_item_tree(ref, new_item);
+ }
+
+ /* Transform this Item object. */
+ return (this->*transformer)(arg_t);
+}
+
+
void Item_ref::print(String *str, enum_query_type query_type)
{
if (ref)
@@ -6838,7 +7103,8 @@ bool Item_ref::val_bool_result()
case STRING_RESULT:
return result_field->val_real() != 0.0;
case ROW_RESULT:
- default:
+ case TIME_RESULT:
+ case IMPOSSIBLE_RESULT:
DBUG_ASSERT(0);
}
}
@@ -6926,7 +7192,19 @@ my_decimal *Item_ref::val_decimal(my_decimal *decimal_value)
int Item_ref::save_in_field(Field *to, bool no_conversions)
{
int res;
- DBUG_ASSERT(!result_field);
+ if (result_field)
+ {
+ if (result_field->is_null())
+ {
+ null_value= 1;
+ res= set_field_to_null_with_conversions(to, no_conversions);
+ return res;
+ }
+ to->set_notnull();
+ res= field_conv(to, result_field);
+ null_value= 0;
+ return res;
+ }
res= (*ref)->save_in_field(to, no_conversions);
null_value= (*ref)->null_value;
return res;
@@ -7043,8 +7321,7 @@ bool Item_direct_ref::get_date(MYSQL_TIME *ltime,uint fuzzydate)
Item_cache_wrapper::~Item_cache_wrapper()
{
- delete expr_cache;
- /* expr_value is Item so it will be destroyed from list of Items */
+ DBUG_ASSERT(expr_cache == 0);
}
Item_cache_wrapper::Item_cache_wrapper(Item *item_arg)
@@ -7056,9 +7333,11 @@ Item_cache_wrapper::Item_cache_wrapper(Item *item_arg)
decimals= orig_item->decimals;
collation.set(orig_item->collation);
with_sum_func= orig_item->with_sum_func;
+ with_field= orig_item->with_field;
unsigned_flag= orig_item->unsigned_flag;
name= item_arg->name;
name_length= item_arg->name_length;
+ with_subselect= orig_item->with_subselect;
if ((expr_value= Item_cache::get_cache(orig_item)))
expr_value->setup(orig_item);
@@ -7067,11 +7346,28 @@ Item_cache_wrapper::Item_cache_wrapper(Item *item_arg)
}
+/**
+ Initialize the cache if it is needed
+*/
+
+void Item_cache_wrapper::init_on_demand()
+{
+ if (!expr_cache->is_inited())
+ {
+ orig_item->get_cache_parameters(parameters);
+ expr_cache->init();
+ }
+}
+
+
void Item_cache_wrapper::print(String *str, enum_query_type query_type)
{
str->append(func_name());
if (expr_cache)
+ {
+ init_on_demand();
expr_cache->print(str, query_type);
+ }
else
str->append(STRING_WITH_LEN("<<DISABLED>>"));
str->append('(');
@@ -7107,10 +7403,14 @@ bool Item_cache_wrapper::send(Protocol *protocol, String *buffer)
void Item_cache_wrapper::cleanup()
{
+ DBUG_ENTER("Item_cache_wrapper::cleanup");
+ Item_result_field::cleanup();
delete expr_cache;
expr_cache= 0;
- // expr_value is Item so it will be destroyed from list of Items
+ /* expr_value is Item so it will be destroyed from list of Items */
expr_value= 0;
+ parameters.empty();
+ DBUG_VOID_RETURN;
}
@@ -7130,10 +7430,11 @@ void Item_cache_wrapper::cleanup()
@retval TRUE Error
*/
-bool Item_cache_wrapper::set_cache(THD *thd, List<Item*> &depends_on)
+bool Item_cache_wrapper::set_cache(THD *thd)
{
DBUG_ENTER("Item_cache_wrapper::set_cache");
- expr_cache= new Expression_cache_tmptable(thd, depends_on, expr_value);
+ DBUG_ASSERT(expr_cache == 0);
+ expr_cache= new Expression_cache_tmptable(thd, parameters, expr_value);
DBUG_RETURN(expr_cache == NULL);
}
@@ -7158,6 +7459,7 @@ Item *Item_cache_wrapper::check_cache()
{
Expression_cache_tmptable::result res;
Item *cached_value;
+ init_on_demand();
res= expr_cache->check_value(&cached_value);
if (res == Expression_cache_tmptable::HIT)
DBUG_RETURN(cached_value);
@@ -7388,25 +7690,6 @@ bool Item_cache_wrapper::get_date(MYSQL_TIME *ltime, uint fuzzydate)
}
-/**
- Get the time value of the possibly cached item
-*/
-
-bool Item_cache_wrapper::get_time(MYSQL_TIME *ltime)
-{
- Item *cached_value;
- DBUG_ENTER("Item_cache_wrapper::get_time");
- if (!expr_cache)
- DBUG_RETURN((null_value= orig_item->get_time(ltime)));
-
- if ((cached_value= check_cache()))
- DBUG_RETURN((null_value= cached_value->get_time(ltime)));
-
- cache();
- DBUG_RETURN((null_value= expr_value->get_time(ltime)));
-}
-
-
int Item_cache_wrapper::save_in_field(Field *to, bool no_conversions)
{
int res;
@@ -7495,7 +7778,7 @@ bool Item_outer_ref::fix_fields(THD *thd, Item **reference)
void Item_outer_ref::fix_after_pullout(st_select_lex *new_parent, Item **ref)
{
- if (depended_from == new_parent)
+ if (get_depended_from() == new_parent)
{
*ref= outer_ref;
(*ref)->fix_after_pullout(new_parent, ref);
@@ -7505,7 +7788,7 @@ void Item_outer_ref::fix_after_pullout(st_select_lex *new_parent, Item **ref)
void Item_ref::fix_after_pullout(st_select_lex *new_parent, Item **refptr)
{
(*ref)->fix_after_pullout(new_parent, ref);
- if (depended_from == new_parent)
+ if (get_depended_from() == new_parent)
depended_from= NULL;
}
@@ -7577,6 +7860,138 @@ bool Item_direct_view_ref::eq(const Item *item, bool binary_cmp) const
return FALSE;
}
+
+Item_equal *Item_direct_view_ref::find_item_equal(COND_EQUAL *cond_equal)
+{
+ Item* field_item= real_item();
+ if (field_item->type() != FIELD_ITEM)
+ return NULL;
+ return ((Item_field *) field_item)->find_item_equal(cond_equal);
+}
+
+
+/**
+ Check whether a reference to field item can be substituted for an equal item
+
+ @details
+ The function checks whether a substitution of a reference to field item for
+ an equal item is valid.
+
+ @param arg *arg != NULL <-> the reference is in the context
+ where substitution for an equal item is valid
+
+ @note
+ See also the note for Item_field::subst_argument_checker
+
+ @retval
+ TRUE substitution is valid
+ @retval
+ FALSE otherwise
+*/
+bool Item_direct_view_ref::subst_argument_checker(uchar **arg)
+{
+ bool res= FALSE;
+ if (*arg)
+ {
+ Item *item= real_item();
+ if (item->type() == FIELD_ITEM &&
+ (*arg == (uchar *) Item::ANY_SUBST ||
+ result_type() != STRING_RESULT ||
+ (((Item_field *) item)->field->flags & BINARY_FLAG)))
+ res= TRUE;
+ }
+ /* Block any substitution into the wrapped object */
+ if (*arg)
+ *arg= NULL;
+ return res;
+}
+
+
+/**
+ Set a pointer to the multiple equality the view field reference belongs to
+ (if any).
+
+ @details
+ The function looks for a multiple equality containing this item of the type
+ Item_direct_view_ref among those referenced by arg.
+ In the case such equality exists the function does the following.
+ If the found multiple equality contains a constant, then the item
+ is substituted for this constant, otherwise the function sets a pointer
+ to the multiple equality in the item.
+
+ @param arg reference to list of multiple equalities where
+ the item (this object) is to be looked for
+
+ @note
+ This function is supposed to be called as a callback parameter in calls
+ of the compile method.
+
+ @note
+ The function calls Item_field::equal_fields_propagator for the field item
+ this->real_item() to do the job. Then it takes the pointer to equal_item
+ from this field item and assigns it to this->item_equal.
+
+ @return
+ - pointer to the replacing constant item, if the field item was substituted
+ - pointer to the field item, otherwise.
+*/
+
+Item *Item_direct_view_ref::equal_fields_propagator(uchar *arg)
+{
+ Item *field_item= real_item();
+ if (field_item->type() != FIELD_ITEM)
+ return this;
+ Item *item= field_item->equal_fields_propagator(arg);
+ set_item_equal(field_item->get_item_equal());
+ field_item->set_item_equal(NULL);
+ if (item != field_item)
+ return item;
+ return this;
+}
+
+
+/**
+ Replace an Item_direct_view_ref for an equal Item_field evaluated earlier
+ (if any).
+
+ @details
+ If this->item_equal points to some item and coincides with arg then
+ the function returns a pointer to a field item that is referred to by the
+ first element of the item_equal list which the Item_direct_view_ref
+ object belongs to unless item_equal contains a constant item. In this
+ case the function returns this constant item (if the substitution does
+ not require conversion).
+ If the Item_direct_view_item object does not refer any Item_equal object
+ 'this' is returned .
+
+ @param arg NULL or points to so some item of the Item_equal type
+
+ @note
+ This function is supposed to be called as a callback parameter in calls
+ of the transformer method.
+
+ @note
+ The function calls Item_field::replace_equal_field for the field item
+ this->real_item() to do the job.
+
+ @return
+ - pointer to a replacement Item_field if there is a better equal item or
+ a pointer to a constant equal item;
+ - this - otherwise.
+*/
+
+Item *Item_direct_view_ref::replace_equal_field(uchar *arg)
+{
+ Item *field_item= real_item();
+ if (field_item->type() != FIELD_ITEM)
+ return this;
+ field_item->set_item_equal(item_equal);
+ Item *item= field_item->replace_equal_field(arg);
+ field_item->set_item_equal(0);
+ return item != field_item ? item : this;
+}
+
+
bool Item_default_value::eq(const Item *item, bool binary_cmp) const
{
return item->type() == DEFAULT_VALUE_ITEM &&
@@ -7949,6 +8364,8 @@ Item_result item_cmp_type(Item_result a,Item_result b)
return INT_RESULT;
else if (a == ROW_RESULT || b == ROW_RESULT)
return ROW_RESULT;
+ else if (a == TIME_RESULT || b == TIME_RESULT)
+ return TIME_RESULT;
if ((a == INT_RESULT || a == DECIMAL_RESULT) &&
(b == INT_RESULT || b == DECIMAL_RESULT))
return DECIMAL_RESULT;
@@ -7962,11 +8379,20 @@ void resolve_const_item(THD *thd, Item **ref, Item *comp_item)
Item *new_item= NULL;
if (item->basic_const_item())
return; // Can't be better
- Item_result res_type=item_cmp_type(comp_item->result_type(),
- item->result_type());
+ Item_result res_type=item_cmp_type(comp_item->cmp_type(), item->cmp_type());
char *name=item->name; // Alloced by sql_alloc
switch (res_type) {
+ case TIME_RESULT:
+ {
+ bool is_null;
+ Item **ref_copy= ref;
+ /* the following call creates a constant and puts it in new_item */
+ get_datetime_value(thd, &ref_copy, &new_item, comp_item, &is_null);
+ if (is_null)
+ new_item= new Item_null(name);
+ break;
+ }
case STRING_RESULT:
{
char buff[MAX_FIELD_WIDTH];
@@ -8041,8 +8467,9 @@ void resolve_const_item(THD *thd, Item **ref, Item *comp_item)
(Item*) new Item_decimal(name, result, length, decimals));
break;
}
- default:
+ case IMPOSSIBLE_RESULT:
DBUG_ASSERT(0);
+ break;
}
if (new_item)
thd->change_item_tree(ref, new_item);
@@ -8062,6 +8489,9 @@ void resolve_const_item(THD *thd, Item **ref, Item *comp_item)
@note We only use this on the range optimizer/partition pruning,
because in some cases we can't store the value in the field
without some precision/character loss.
+
+ @todo rewrite it to use Arg_comparator (currently it's a simplified and
+ incomplete version of it)
*/
int stored_field_cmp_to_item(THD *thd, Field *field, Item *item)
@@ -8093,9 +8523,7 @@ int stored_field_cmp_to_item(THD *thd, Field *field, Item *item)
if (field_type == MYSQL_TYPE_DATE)
type= MYSQL_TIMESTAMP_DATE;
-
- if (field_type == MYSQL_TYPE_DATETIME ||
- field_type == MYSQL_TYPE_TIMESTAMP)
+ else
type= MYSQL_TIMESTAMP_DATETIME;
const char *field_name= field->field_name;
@@ -8119,6 +8547,25 @@ int stored_field_cmp_to_item(THD *thd, Field *field, Item *item)
field_val= field->val_decimal(&field_buf);
return my_decimal_cmp(item_val, field_val);
}
+ /*
+ We have to check field->cmp_type() instead of res_type,
+ as result_type() - and thus res_type - can never be TIME_RESULT (yet).
+ */
+ if (field->cmp_type() == TIME_RESULT)
+ {
+ MYSQL_TIME field_time, item_time;
+ if (field->type() == MYSQL_TYPE_TIME)
+ {
+ field->get_time(&field_time);
+ item->get_time(&item_time);
+ }
+ else
+ {
+ field->get_date(&field_time, TIME_FUZZY_DATE | TIME_INVALID_DATES);
+ item->get_date(&item_time, TIME_FUZZY_DATE | TIME_INVALID_DATES);
+ }
+ return my_time_compare(&field_time, &item_time);
+ }
double result= item->val_real();
if (item->null_value)
return 0;
@@ -8155,19 +8602,16 @@ Item_cache* Item_cache::get_cache(const Item *item, const Item_result type)
case DECIMAL_RESULT:
return new Item_cache_decimal();
case STRING_RESULT:
- /* Not all functions that return DATE/TIME are actually DATE/TIME funcs. */
- if ((item->is_datetime() ||
- item->field_type() == MYSQL_TYPE_TIME) &&
- (const_cast<Item*>(item))->result_as_longlong())
- return new Item_cache_datetime(item->field_type());
return new Item_cache_str(item);
case ROW_RESULT:
return new Item_cache_row();
- default:
- // should never be in real life
+ case TIME_RESULT:
+ return new Item_cache_temporal(item->field_type());
+ case IMPOSSIBLE_RESULT:
DBUG_ASSERT(0);
- return 0;
+ break;
}
+ return 0; // Impossible
}
void Item_cache::store(Item *item)
@@ -8180,6 +8624,11 @@ void Item_cache::store(Item *item)
void Item_cache::print(String *str, enum_query_type query_type)
{
+ if (value_cached)
+ {
+ print_value(str);
+ return;
+ }
str->append(STRING_WITH_LEN("<cache>("));
if (example)
example->print(str, query_type);
@@ -8200,16 +8649,6 @@ bool Item_cache_int::cache_value()
}
-void Item_cache_int::store_longlong(Item *item, longlong val_arg)
-{
- /* An explicit values is given, save it. */
- value_cached= TRUE;
- value= val_arg;
- null_value= item->null_value;
- unsigned_flag= item->unsigned_flag;
-}
-
-
String *Item_cache_int::val_str(String *str)
{
DBUG_ASSERT(fixed == 1);
@@ -8245,171 +8684,104 @@ longlong Item_cache_int::val_int()
return value;
}
-bool Item_cache_datetime::cache_value_int()
+int Item_cache_int::save_in_field(Field *field, bool no_conversions)
{
- if (!example)
- return false;
-
- value_cached= true;
- // Mark cached string value obsolete
- str_value_cached= false;
-
- MYSQL_TIME ltime;
- const bool eval_error=
- (field_type() == MYSQL_TYPE_TIME) ?
- example->get_time(&ltime) :
- example->get_date(&ltime, TIME_FUZZY_DATE);
-
- if (eval_error)
- int_value= 0;
- else
- {
- switch(field_type())
- {
- case MYSQL_TYPE_DATETIME:
- case MYSQL_TYPE_TIMESTAMP:
- int_value= TIME_to_ulonglong_datetime(&ltime);
- break;
- case MYSQL_TYPE_TIME:
- int_value= TIME_to_ulonglong_time(&ltime);
- break;
- default:
- int_value= TIME_to_ulonglong_date(&ltime);
- break;
- }
- if (ltime.neg)
- int_value= -int_value;
- }
+ int error;
+ if (!has_value())
+ return set_field_to_null_with_conversions(field, no_conversions);
- null_value= example->null_value;
- unsigned_flag= example->unsigned_flag;
+ field->set_notnull();
+ error= field->store(value, unsigned_flag);
- return true;
+ return error ? error : field->table->in_use->is_error() ? 1 : 0;
}
-bool Item_cache_datetime::cache_value()
+Item_cache_temporal::Item_cache_temporal(enum_field_types field_type_arg):
+ Item_cache_int(field_type_arg)
{
- if (!example)
- return FALSE;
-
- if (cmp_context == INT_RESULT)
- return cache_value_int();
-
- str_value_cached= TRUE;
- // Mark cached int value obsolete
- value_cached= FALSE;
- /* Assume here that the underlying item will do correct conversion.*/
- String *res= example->str_result(&str_value);
- if (res && res != &str_value)
- str_value.copy(*res);
- null_value= example->null_value;
- unsigned_flag= example->unsigned_flag;
- return TRUE;
+ if (mysql_type_to_time_type(cached_field_type) == MYSQL_TIMESTAMP_ERROR)
+ cached_field_type= MYSQL_TYPE_DATETIME;
}
-void Item_cache_datetime::store(Item *item, longlong val_arg)
+String *Item_cache_temporal::val_str(String *str)
{
- /* An explicit values is given, save it. */
- value_cached= TRUE;
- int_value= val_arg;
- null_value= item->null_value;
- unsigned_flag= item->unsigned_flag;
+ DBUG_ASSERT(fixed == 1);
+ if (!has_value())
+ {
+ null_value= true;
+ return NULL;
+ }
+ return val_string_from_date(str);
}
-void Item_cache_datetime::store(Item *item)
+bool Item_cache_temporal::cache_value()
{
- Item_cache::store(item);
- str_value_cached= FALSE;
+ if (!example)
+ return false;
+
+ value_cached= true;
+
+ MYSQL_TIME ltime;
+ if (example->get_date(&ltime, TIME_FUZZY_DATE))
+ value=0;
+ else
+ value= pack_time(&ltime);
+ null_value= example->null_value;
+ return true;
}
-String *Item_cache_datetime::val_str(String *str)
+
+bool Item_cache_temporal::get_date(MYSQL_TIME *ltime, uint fuzzydate)
{
- DBUG_ASSERT(fixed == 1);
+ ErrConvInteger str(value);
- if ((value_cached || str_value_cached) && null_value)
- return NULL;
+ if (!has_value())
+ {
+ bzero((char*) ltime,sizeof(*ltime));
+ return 1;
+ }
- if (!str_value_cached)
+ unpack_time(value, ltime);
+ ltime->time_type= mysql_type_to_time_type(field_type());
+ if (ltime->time_type == MYSQL_TIMESTAMP_TIME)
{
- /*
- When it's possible the Item_cache_datetime uses INT datetime
- representation due to speed reasons. But still, it always has the STRING
- result type and thus it can be asked to return a string value.
- It is possible that at this time cached item doesn't contain correct
- string value, thus we have to convert cached int value to string and
- return it.
- */
- if (value_cached)
- {
- MYSQL_TIME ltime;
- /* Return NULL in case of OOM/conversion error. */
- null_value= TRUE;
- if (str_value.alloc(MAX_DATE_STRING_REP_LENGTH))
- return NULL;
- if (cached_field_type == MYSQL_TYPE_TIME)
- {
- longlong time= int_value;
- set_zero_time(&ltime, MYSQL_TIMESTAMP_TIME);
- if (time < 0)
- {
- time= -time;
- ltime.neg= TRUE;
- }
- DBUG_ASSERT(time <= TIME_MAX_VALUE);
- ltime.second= time % 100;
- time/= 100;
- ltime.minute= time % 100;
- time/= 100;
- ltime.hour= time;
- }
- else
- {
- int was_cut;
- longlong res;
- res= number_to_datetime(int_value, &ltime, TIME_FUZZY_DATE, &was_cut);
- if (res == -1)
- return NULL;
- }
- str_value.length(my_TIME_to_str(&ltime,
- const_cast<char*>(str_value.ptr())));
- str_value_cached= TRUE;
- null_value= FALSE;
- }
- else if (!cache_value())
- return NULL;
+ ltime->hour+= (ltime->month*32+ltime->day)*24;
+ ltime->month= ltime->day= 0;
}
- return null_value ? NULL : &str_value;
+ return 0;
+
}
-my_decimal *Item_cache_datetime::val_decimal(my_decimal *decimal_val)
+int Item_cache_temporal::save_in_field(Field *field, bool no_conversions)
{
- DBUG_ASSERT(fixed == 1);
+ int error;
if (!has_value())
- return NULL;
- int2my_decimal(E_DEC_FATAL_ERROR, int_value, unsigned_flag, decimal_val);
- return decimal_val;
-}
+ return set_field_to_null_with_conversions(field, no_conversions);
-double Item_cache_datetime::val_real()
-{
- DBUG_ASSERT(fixed == 1);
- if ((!value_cached && !cache_value_int()) || null_value)
- return 0.0;
- return (double) int_value;
+ field->set_notnull();
+
+ MYSQL_TIME ltime;
+ unpack_time(value, &ltime);
+ ltime.time_type= mysql_type_to_time_type(field_type());
+ error= field->store_time_dec(&ltime, decimals);
+
+ return error ? error : field->table->in_use->is_error() ? 1 : 0;
}
-longlong Item_cache_datetime::val_int()
+
+void Item_cache_temporal::store_packed(longlong val_arg)
{
- DBUG_ASSERT(fixed == 1);
- if ((!value_cached && !cache_value_int()) || null_value)
- return 0;
- return int_value;
+ /* An explicit values is given, save it. */
+ value_cached= true;
+ value= val_arg;
+ null_value= false;
}
+
bool Item_cache_real::cache_value()
{
if (!example)
@@ -8587,7 +8959,7 @@ my_decimal *Item_cache_str::val_decimal(my_decimal *decimal_val)
int Item_cache_str::save_in_field(Field *field, bool no_conversions)
{
if (!has_value())
- return 0;
+ return set_field_to_null_with_conversions(field, no_conversions);
int res= Item_cache::save_in_field(field, no_conversions);
return (is_varbinary && field->type() == MYSQL_TYPE_STRING &&
value->length() < field->field_length) ? 1 : res;
@@ -8741,12 +9113,14 @@ Item_result Item_type_holder::result_type() const
enum_field_types Item_type_holder::get_real_type(Item *item)
{
+ if (item->type() == REF_ITEM)
+ item= item->real_item();
switch(item->type())
{
case FIELD_ITEM:
{
/*
- Item_fields::field_type ask Field_type() but sometimes field return
+ Item_field::field_type ask Field_type() but sometimes field return
a different type, like for enum/set, so we need to ask real type.
*/
Field *field= ((Item_field *) item)->field;
@@ -8788,7 +9162,8 @@ enum_field_types Item_type_holder::get_real_type(Item *item)
case DECIMAL_RESULT:
return MYSQL_TYPE_NEWDECIMAL;
case ROW_RESULT:
- default:
+ case TIME_RESULT:
+ case IMPOSSIBLE_RESULT:
DBUG_ASSERT(0);
return MYSQL_TYPE_VAR_STRING;
}
@@ -9115,6 +9490,49 @@ void view_error_processor(THD *thd, void *data)
((TABLE_LIST *)data)->hide_view_error(thd);
}
+
+st_select_lex *Item_ident::get_depended_from() const
+{
+ st_select_lex *dep;
+ if ((dep= depended_from))
+ for ( ; dep->merged_into; dep= dep->merged_into) ;
+ return dep;
+}
+
+
+table_map Item_ref::used_tables() const
+{
+ return get_depended_from() ? OUTER_REF_TABLE_BIT : (*ref)->used_tables();
+}
+
+
+void Item_ref::update_used_tables()
+{
+ if (!get_depended_from())
+ (*ref)->update_used_tables();
+}
+
+
+table_map Item_direct_view_ref::used_tables() const
+{
+ return get_depended_from() ?
+ OUTER_REF_TABLE_BIT :
+ (view->merged ? (*ref)->used_tables() : view->table->map);
+}
+
+
+/*
+ we add RAND_TABLE_BIT to prevent moving this item from HAVING to WHERE
+*/
+table_map Item_ref_null_helper::used_tables() const
+{
+ return (get_depended_from() ?
+ OUTER_REF_TABLE_BIT :
+ (*ref)->used_tables() | RAND_TABLE_BIT);
+}
+
+
+
/*****************************************************************************
** Instantiate templates
*****************************************************************************/