diff options
Diffstat (limited to 'sql/item.cc')
-rw-r--r-- | sql/item.cc | 2890 |
1 files changed, 2497 insertions, 393 deletions
diff --git a/sql/item.cc b/sql/item.cc index 7e74bb1fd85..495ad772ec6 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -22,6 +22,10 @@ #include "mysql_priv.h" #include <m_ctype.h> #include "my_dir.h" +#include "sp_rcontext.h" +#include "sp_head.h" +#include "sql_trigger.h" +#include "sql_select.h" static void mark_as_dependent(THD *thd, SELECT_LEX *last, SELECT_LEX *current, @@ -29,6 +33,131 @@ static void mark_as_dependent(THD *thd, const String my_null_string("NULL", 4, default_charset_info); +/****************************************************************************/ + +/* Hybrid_type_traits {_real} */ + +void Hybrid_type_traits::fix_length_and_dec(Item *item, Item *arg) const +{ + item->decimals= NOT_FIXED_DEC; + item->max_length= item->float_length(arg->decimals); +} + +static const Hybrid_type_traits real_traits_instance; + +const Hybrid_type_traits *Hybrid_type_traits::instance() +{ + return &real_traits_instance; +} + + +my_decimal * +Hybrid_type_traits::val_decimal(Hybrid_type *val, my_decimal *to) const +{ + double2my_decimal(E_DEC_FATAL_ERROR, val->real, val->dec_buf); + return val->dec_buf; +} + + +String * +Hybrid_type_traits::val_str(Hybrid_type *val, String *to, uint8 decimals) const +{ + to->set(val->real, decimals, &my_charset_bin); + return to; +} + +/* Hybrid_type_traits_decimal */ +static const Hybrid_type_traits_decimal decimal_traits_instance; + +const Hybrid_type_traits_decimal *Hybrid_type_traits_decimal::instance() +{ + return &decimal_traits_instance; +} + + +void +Hybrid_type_traits_decimal::fix_length_and_dec(Item *item, Item *arg) const +{ + item->decimals= arg->decimals; + item->max_length= min(arg->max_length + DECIMAL_LONGLONG_DIGITS, + DECIMAL_MAX_LENGTH); +} + + +void Hybrid_type_traits_decimal::set_zero(Hybrid_type *val) const +{ + my_decimal_set_zero(&val->dec_buf[0]); + val->used_dec_buf_no= 0; +} + + +void Hybrid_type_traits_decimal::add(Hybrid_type *val, Field *f) const +{ + my_decimal_add(E_DEC_FATAL_ERROR, + &val->dec_buf[val->used_dec_buf_no ^ 1], + &val->dec_buf[val->used_dec_buf_no], + f->val_decimal(&val->dec_buf[2])); + val->used_dec_buf_no^= 1; +} + + +void Hybrid_type_traits_decimal::div(Hybrid_type *val, ulonglong u) const +{ + int2my_decimal(E_DEC_FATAL_ERROR, u, TRUE, &val->dec_buf[2]); + /* XXX: what is '4' for scale? */ + my_decimal_div(E_DEC_FATAL_ERROR, + &val->dec_buf[val->used_dec_buf_no ^ 1], + &val->dec_buf[val->used_dec_buf_no], + &val->dec_buf[2], 4); + val->used_dec_buf_no^= 1; +} + + +longlong +Hybrid_type_traits_decimal::val_int(Hybrid_type *val, bool unsigned_flag) const +{ + longlong result; + my_decimal2int(E_DEC_FATAL_ERROR, &val->dec_buf[val->used_dec_buf_no], + unsigned_flag, &result); + return result; +} + + +double +Hybrid_type_traits_decimal::val_real(Hybrid_type *val) const +{ + my_decimal2double(E_DEC_FATAL_ERROR, &val->dec_buf[val->used_dec_buf_no], + &val->real); + return val->real; +} + + +String * +Hybrid_type_traits_decimal::val_str(Hybrid_type *val, String *to, + uint8 decimals) const +{ + my_decimal_round(E_DEC_FATAL_ERROR, &val->dec_buf[val->used_dec_buf_no], + decimals, FALSE, &val->dec_buf[2]); + my_decimal2string(E_DEC_FATAL_ERROR, &val->dec_buf[2], 0, 0, 0, to); + return to; +} + +/* Hybrid_type_traits_integer */ +static const Hybrid_type_traits_integer integer_traits_instance; + +const Hybrid_type_traits_integer *Hybrid_type_traits_integer::instance() +{ + return &integer_traits_instance; +} + +void +Hybrid_type_traits_integer::fix_length_and_dec(Item *item, Item *arg) const +{ + item->decimals= 0; + item->max_length= 21; + item->unsigned_flag= 0; +} + /***************************************************************************** ** Item functions *****************************************************************************/ @@ -40,13 +169,140 @@ void item_init(void) item_user_lock_init(); } + +/* +TODO: make this functions class dependent +*/ + +bool Item::val_bool() +{ + switch(result_type()) { + case INT_RESULT: + return val_int() != 0; + case DECIMAL_RESULT: + { + my_decimal decimal_value; + my_decimal *val= val_decimal(&decimal_value); + if (val) + return !my_decimal_is_zero(val); + return 0; + } + case REAL_RESULT: + case STRING_RESULT: + return val_real() != 0.0; + case ROW_RESULT: + default: + DBUG_ASSERT(0); + return 0; // Wrong (but safe) + } +} + + +String *Item::val_string_from_real(String *str) +{ + double nr= val_real(); + if (null_value) + return 0; /* purecov: inspected */ + str->set(nr,decimals, &my_charset_bin); + return str; +} + + +String *Item::val_string_from_int(String *str) +{ + longlong nr= val_int(); + if (null_value) + return 0; + if (unsigned_flag) + str->set((ulonglong) nr, &my_charset_bin); + else + str->set(nr, &my_charset_bin); + return str; +} + + +String *Item::val_string_from_decimal(String *str) +{ + my_decimal dec_buf, *dec= val_decimal(&dec_buf); + if (null_value) + return 0; + my_decimal_round(E_DEC_FATAL_ERROR, dec, decimals, FALSE, &dec_buf); + my_decimal2string(E_DEC_FATAL_ERROR, &dec_buf, 0, 0, 0, str); + return str; +} + + +my_decimal *Item::val_decimal_from_real(my_decimal *decimal_value) +{ + double nr= val_real(); + if (null_value) + return 0; + double2my_decimal(E_DEC_FATAL_ERROR, nr, decimal_value); + return (decimal_value); +} + + +my_decimal *Item::val_decimal_from_int(my_decimal *decimal_value) +{ + longlong nr= val_int(); + if (null_value) + return 0; + int2my_decimal(E_DEC_FATAL_ERROR, nr, unsigned_flag, decimal_value); + return decimal_value; +} + + +my_decimal *Item::val_decimal_from_string(my_decimal *decimal_value) +{ + String *res; + char *end_ptr; + if (!(res= val_str(&str_value))) + return 0; // NULL or EOM + + end_ptr= (char*) res->ptr()+ res->length(); + if (str2my_decimal(E_DEC_FATAL_ERROR & ~E_DEC_BAD_NUM, + res->ptr(), res->length(), res->charset(), + decimal_value) & E_DEC_BAD_NUM) + { + push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + ER_TRUNCATED_WRONG_VALUE, + ER(ER_TRUNCATED_WRONG_VALUE), "DECIMAL", + str_value.c_ptr()); + } + return decimal_value; +} + + +double Item::val_real_from_decimal() +{ + /* Note that fix_fields may not be called for Item_avg_field items */ + double result; + my_decimal value_buff, *dec_val= val_decimal(&value_buff); + if (null_value) + return 0.0; + my_decimal2double(E_DEC_FATAL_ERROR, dec_val, &result); + return result; +} + + +longlong Item::val_int_from_decimal() +{ + /* Note that fix_fields may not be called for Item_avg_field items */ + longlong result; + my_decimal value, *dec_val= val_decimal(&value); + if (null_value) + return 0; + my_decimal2int(E_DEC_FATAL_ERROR, dec_val, unsigned_flag, &result); + return result; +} + + Item::Item(): - fixed(0) + name(0), orig_name(0), name_length(0), fixed(0), + collation(&my_charset_bin, DERIVATION_COERCIBLE) { marker= 0; maybe_null=null_value=with_sum_func=unsigned_flag=0; - collation.set(&my_charset_bin, DERIVATION_COERCIBLE); - name= 0; decimals= 0; max_length= 0; /* Put item in free list so that we can free all items at end */ @@ -69,13 +325,14 @@ Item::Item(): } /* - Constructor used by Item_field, Item_*_ref & agregate (sum) functions. + Constructor used by Item_field, Item_*_ref & aggregate (sum) functions. Used for duplicating lists in processing queries with temporary tables */ Item::Item(THD *thd, Item *item): str_value(item->str_value), name(item->name), + orig_name(item->orig_name), max_length(item->max_length), marker(item->marker), decimals(item->decimals), @@ -96,25 +353,78 @@ void Item::print_item_w_name(String *str) print(str); if (name) { - str->append(" AS `", 5); - str->append(name); - str->append('`'); + THD *thd= current_thd; + str->append(" AS ", 4); + append_identifier(thd, str, name, strlen(name)); } } +void Item::cleanup() +{ + DBUG_ENTER("Item::cleanup"); + DBUG_PRINT("info", ("Item: 0x%lx, Type: %d, name %s, original name %s", + this, (int)type(), name ? name : "(null)", + orig_name ? orig_name : "null")); + fixed=0; + marker= 0; + if (orig_name) + name= orig_name; + DBUG_VOID_RETURN; +} + + +/* + cleanup() item if it is 'fixed' + + SYNOPSIS + cleanup_processor() + arg - a dummy parameter, is not used here +*/ + +bool Item::cleanup_processor(byte *arg) +{ + if (fixed) + cleanup(); + return FALSE; +} + + +/* + rename item (used for views, cleanup() return original name) + + SYNOPSIS + Item::rename() + new_name new name of item; +*/ + +void Item::rename(char *new_name) +{ + /* + we can compare pointers to names here, because if name was not changed, + pointer will be same + */ + if (!orig_name && new_name != name) + orig_name= name; + name= new_name; +} + + Item_ident::Item_ident(const char *db_name_par,const char *table_name_par, const char *field_name_par) :orig_db_name(db_name_par), orig_table_name(table_name_par), orig_field_name(field_name_par), - db_name(db_name_par), table_name(table_name_par), - field_name(field_name_par), cached_field_index(NO_CACHED_FIELD_INDEX), + db_name(db_name_par), table_name(table_name_par), + field_name(field_name_par), + alias_name_used(FALSE), cached_field_index(NO_CACHED_FIELD_INDEX), cached_table(0), depended_from(0) { name = (char*) field_name_par; } -// Constructor used by Item_field & Item_*_ref (see Item comment) + +/* Constructor used by Item_field & Item_*_ref (see Item comment) */ + Item_ident::Item_ident(THD *thd, Item_ident *item) :Item(thd, item), orig_db_name(item->orig_db_name), @@ -123,6 +433,7 @@ Item_ident::Item_ident(THD *thd, Item_ident *item) db_name(item->db_name), table_name(item->table_name), field_name(item->field_name), + alias_name_used(item->alias_name_used), cached_field_index(item->cached_field_index), cached_table(item->cached_table), depended_from(item->depended_from) @@ -131,10 +442,14 @@ Item_ident::Item_ident(THD *thd, Item_ident *item) void Item_ident::cleanup() { DBUG_ENTER("Item_ident::cleanup"); - DBUG_PRINT("enter", ("b:%s(%s), t:%s(%s), f:%s(%s)", - db_name, orig_db_name, - table_name, orig_table_name, - field_name, orig_field_name)); +#ifdef CANT_BE_USED_AS_MEMORY_IS_FREED + db_name ? db_name : "(null)", + orig_db_name ? orig_db_name : "(null)", + table_name ? table_name : "(null)", + orig_table_name ? orig_table_name : "(null)", + field_name ? field_name : "(null)", + orig_field_name ? orig_field_name : "(null)")); +#endif Item::cleanup(); db_name= orig_db_name; table_name= orig_table_name; @@ -151,6 +466,45 @@ bool Item_ident::remove_dependence_processor(byte * arg) } +/* + Store the pointer to this item field into a list if not already there. + + SYNOPSIS + Item_field::collect_item_field_processor() + arg pointer to a List<Item_field> + + DESCRIPTION + The method is used by Item::walk to collect all unique Item_field objects + from a tree of Items into a set of items represented as a list. + + IMPLEMENTATION + Item_cond::walk() and Item_func::walk() stop the evaluation of the + processor function for its arguments once the processor returns + true.Therefore in order to force this method being called for all item + arguments in a condition the method must return false. + + RETURN + false to force the evaluation of collect_item_field_processor + for the subsequent items. +*/ + +bool Item_field::collect_item_field_processor(byte *arg) +{ + DBUG_ENTER("Item_field::collect_item_field_processor"); + DBUG_PRINT("info", ("%s", field->field_name ? field->field_name : "noname")); + List<Item_field> *item_list= (List<Item_field>*) arg; + List_iterator<Item_field> item_list_it(*item_list); + Item_field *curr_item; + while ((curr_item= item_list_it++)) + { + if (curr_item->eq(this, 1)) + DBUG_RETURN(false); /* Already in the set. */ + } + item_list->push_back(this); + DBUG_RETURN(false); +} + + bool Item::check_cols(uint c) { if (c != 1) @@ -168,12 +522,15 @@ void Item::set_name(const char *str, uint length, CHARSET_INFO *cs) { /* Empty string, used by AS or internal function like last_insert_id() */ name= (char*) str; + name_length= 0; return; } if (cs->ctype) { - // This will probably need a better implementation in the future: - // a function in CHARSET_INFO structure. + /* + This will probably need a better implementation in the future: + a function in CHARSET_INFO structure. + */ while (length && !my_isgraph(cs,*str)) { // Fix problem with yacc length--; @@ -183,12 +540,12 @@ void Item::set_name(const char *str, uint length, CHARSET_INFO *cs) if (!my_charset_same(cs, system_charset_info)) { uint32 res_length; - name= sql_strmake_with_convert(str, length, cs, + name= sql_strmake_with_convert(str, name_length= length, cs, MAX_ALIAS_NAME, system_charset_info, &res_length); } else - name=sql_strmake(str, min(length,MAX_ALIAS_NAME)); + name= sql_strmake(str, (name_length= min(length,MAX_ALIAS_NAME))); } @@ -241,18 +598,8 @@ Item *Item_string::safe_charset_converter(CHARSET_INFO *tocs) return NULL; } conv->str_value.copy(); - /* - The above line executes str_value.realloc() internally, - which alligns Alloced_length using ALLIGN_SIZE. - In the case of Item_string::str_value we don't want - Alloced_length to be longer than str_length. - Otherwise, some functions like Item_func_concat::val_str() - try to reuse str_value as a buffer for concatenation result - for optimization purposes, so our string constant become - corrupted. See bug#8785 for more details. - Let's shrink Alloced_length to str_length to avoid this problem. - */ - conv->str_value.shrink_to_length(); + /* Ensure that no one is going to change the result string */ + conv->str_value.mark_as_const(); return conv; } @@ -312,6 +659,121 @@ CHARSET_INFO *Item::default_charset() } +int Item::save_in_field_no_warnings(Field *field, bool no_conversions) +{ + int res; + THD *thd= field->table->in_use; + enum_check_fields tmp= thd->count_cuted_fields; + thd->count_cuted_fields= CHECK_FIELD_IGNORE; + res= save_in_field(field, no_conversions); + thd->count_cuted_fields= tmp; + return res; +} + + +double Item_splocal::val_real() +{ + DBUG_ASSERT(fixed); + Item *it= this_item(); + double ret= it->val_real(); + Item::null_value= it->null_value; + return ret; +} + + +longlong Item_splocal::val_int() +{ + DBUG_ASSERT(fixed); + Item *it= this_item(); + longlong ret= it->val_int(); + Item::null_value= it->null_value; + return ret; +} + + +String *Item_splocal::val_str(String *sp) +{ + DBUG_ASSERT(fixed); + Item *it= this_item(); + String *ret= it->val_str(sp); + Item::null_value= it->null_value; + return ret; +} + + +my_decimal *Item_splocal::val_decimal(my_decimal *decimal_value) +{ + DBUG_ASSERT(fixed); + Item *it= this_item(); + my_decimal *val= it->val_decimal(decimal_value); + Item::null_value= it->null_value; + return val; +} + + +bool Item_splocal::is_null() +{ + Item *it= this_item(); + bool ret= it->is_null(); + Item::null_value= it->null_value; + return ret; +} + + +Item * +Item_splocal::this_item() +{ + THD *thd= current_thd; + + return thd->spcont->get_item(m_offset); +} + +Item * +Item_splocal::this_const_item() const +{ + THD *thd= current_thd; + + return thd->spcont->get_item(m_offset); +} + +Item::Type +Item_splocal::type() const +{ + THD *thd= current_thd; + + if (thd->spcont) + return thd->spcont->get_item(m_offset)->type(); + return NULL_ITEM; // Anything but SUBSELECT_ITEM +} + + +bool Item_splocal::fix_fields(THD *, struct st_table_list *, Item **) +{ + Item *it= this_item(); + DBUG_ASSERT(it->fixed); + max_length= it->max_length; + decimals= it->decimals; + fixed= 1; + return FALSE; +} + + +void Item_splocal::cleanup() +{ + fixed= 0; +} + + +void Item_splocal::print(String *str) +{ + str->reserve(m_name.length+8); + str->append(m_name.str, m_name.length); + str->append('@'); + str->qs_append(m_offset); +} + + + /* Move SUM items out from item tree and replace with reference @@ -368,7 +830,7 @@ void Item::split_sum_func2(THD *thd, Item **ref_pointer_array, Aggregate two collations together taking into account their coercibility (aka derivation): - 0 == DERIVATION_EXPLICIT - an explicitely written COLLATE clause + 0 == DERIVATION_EXPLICIT - an explicitly written COLLATE clause 1 == DERIVATION_NONE - a mix of two different collations 2 == DERIVATION_IMPLICIT - a column 3 == DERIVATION_COERCIBLE - a string constant @@ -406,7 +868,7 @@ bool DTCollation::aggregate(DTCollation &dt, uint flags) /* We do allow to use binary strings (like BLOBS) together with character strings. - Binaries have more precedance than a character + Binaries have more precedence than a character string of the same derivation. */ if (collation == &my_charset_bin) @@ -497,7 +959,9 @@ bool DTCollation::aggregate(DTCollation &dt, uint flags) } Item_field::Item_field(Field *f) - :Item_ident(NullS, f->table_name, f->field_name) + :Item_ident(NullS, *f->table_name, f->field_name), + item_equal(0), no_const_subst(0), + have_privileges(0), any_privileges(0) { set_field(f); /* @@ -508,7 +972,9 @@ Item_field::Item_field(Field *f) } Item_field::Item_field(THD *thd, Field *f) - :Item_ident(f->table->table_cache_key, f->table_name, f->field_name) + :Item_ident(f->table->s->db, *f->table_name, f->field_name), + item_equal(0), no_const_subst(0), + have_privileges(0), any_privileges(0) { /* We always need to provide Item_field with a fully qualified field @@ -545,7 +1011,11 @@ Item_field::Item_field(THD *thd, Field *f) Item_field::Item_field(THD *thd, Item_field *item) :Item_ident(thd, item), field(item->field), - result_field(item->result_field) + result_field(item->result_field), + item_equal(item->item_equal), + no_const_subst(item->no_const_subst), + have_privileges(item->have_privileges), + any_privileges(item->any_privileges) { collation.set(DERIVATION_IMPLICIT); } @@ -554,11 +1024,12 @@ void Item_field::set_field(Field *field_par) { field=result_field=field_par; // for easy coding with fields maybe_null=field->maybe_null(); - max_length=field_par->field_length; decimals= field->decimals(); - table_name=field_par->table_name; - field_name=field_par->field_name; - db_name=field_par->table->table_cache_key; + max_length= field_par->representation_length(); + table_name= *field_par->table_name; + field_name= field_par->field_name; + db_name= field_par->table->s->db; + alias_name_used= field_par->table->alias_name_used; unsigned_flag=test(field_par->flags & UNSIGNED_FLAG); collation.set(field_par->charset(), DERIVATION_IMPLICIT); fixed= 1; @@ -603,6 +1074,55 @@ const char *Item_ident::full_name() const return tmp; } +void Item_ident::print(String *str) +{ + THD *thd= current_thd; + char d_name_buff[MAX_ALIAS_NAME], t_name_buff[MAX_ALIAS_NAME]; + const char *d_name= db_name, *t_name= table_name; + if (lower_case_table_names== 1 || + (lower_case_table_names == 2 && !alias_name_used)) + { + if (table_name && table_name[0]) + { + strmov(t_name_buff, table_name); + my_casedn_str(files_charset_info, t_name_buff); + t_name= t_name_buff; + } + if (db_name && db_name[0]) + { + strmov(d_name_buff, db_name); + my_casedn_str(files_charset_info, d_name_buff); + d_name= d_name_buff; + } + } + + if (!table_name || !field_name) + { + const char *nm= field_name ? field_name : name ? name : "tmp_field"; + append_identifier(thd, str, nm, strlen(nm)); + return; + } + if (db_name && db_name[0] && !alias_name_used) + { + append_identifier(thd, str, d_name, strlen(d_name)); + str->append('.'); + append_identifier(thd, str, t_name, strlen(t_name)); + str->append('.'); + append_identifier(thd, str, field_name, strlen(field_name)); + } + else + { + if (table_name[0]) + { + append_identifier(thd, str, t_name, strlen(t_name)); + str->append('.'); + append_identifier(thd, str, field_name, strlen(field_name)); + } + else + append_identifier(thd, str, field_name, strlen(field_name)); + } +} + /* ARGSUSED */ String *Item_field::val_str(String *str) { @@ -613,7 +1133,8 @@ String *Item_field::val_str(String *str) return field->val_str(str,&str_value); } -double Item_field::val() + +double Item_field::val_real() { DBUG_ASSERT(fixed == 1); if ((null_value=field->is_null())) @@ -621,6 +1142,7 @@ double Item_field::val() return field->val_real(); } + longlong Item_field::val_int() { DBUG_ASSERT(fixed == 1); @@ -630,6 +1152,14 @@ longlong Item_field::val_int() } +my_decimal *Item_field::val_decimal(my_decimal *decimal_value) +{ + if ((null_value= field->is_null())) + return 0; + return field->val_decimal(decimal_value); +} + + String *Item_field::str_result(String *str) { if ((null_value=result_field->is_null())) @@ -684,6 +1214,40 @@ longlong Item_field::val_int_result() } +my_decimal *Item_field::val_decimal_result(my_decimal *decimal_value) +{ + if ((null_value= result_field->is_null())) + return 0; + return result_field->val_decimal(decimal_value); +} + + +bool Item_field::val_bool_result() +{ + if ((null_value= result_field->is_null())) + return FALSE; + switch (result_field->result_type()) { + case INT_RESULT: + return result_field->val_int() != 0; + case DECIMAL_RESULT: + { + my_decimal decimal_value; + my_decimal *val= result_field->val_decimal(&decimal_value); + if (val) + return !my_decimal_is_zero(val); + return 0; + } + case REAL_RESULT: + case STRING_RESULT: + return result_field->val_real() != 0.0; + case ROW_RESULT: + default: + DBUG_ASSERT(0); + return 0; // Shut up compiler + } +} + + bool Item_field::eq(const Item *item, bool binary_cmp) const { if (item->type() != FIELD_ITEM) @@ -731,8 +1295,9 @@ Item *Item_field::get_tmp_table_item(THD *thd) /* - Create an item from a string we KNOW points to a valid longlong/ulonglong - end \0 terminated number string + Create an item from a string we KNOW points to a valid longlong + end \0 terminated number string. + This is always 'signed'. Unsigned values are created with Item_uint() */ Item_int::Item_int(const char *str_arg, uint length) @@ -746,6 +1311,12 @@ Item_int::Item_int(const char *str_arg, uint length) } +my_decimal *Item_int::val_decimal(my_decimal *decimal_value) +{ + int2my_decimal(E_DEC_FATAL_ERROR, value, unsigned_flag, decimal_value); + return decimal_value; +} + String *Item_int::val_str(String *str) { // following assert is redundant, because fixed=1 assigned in constructor @@ -786,7 +1357,98 @@ void Item_uint::print(String *str) } -String *Item_real::val_str(String *str) +Item_decimal::Item_decimal(const char *str_arg, uint length, + CHARSET_INFO *charset) +{ + str2my_decimal(E_DEC_FATAL_ERROR, str_arg, length, charset, &decimal_value); + name= (char*) str_arg; + decimals= (uint8) decimal_value.frac; + max_length= my_decimal_max_length(&decimal_value); + fixed= 1; + unsigned_flag= !decimal_value.sign(); +} + +Item_decimal::Item_decimal(longlong val, bool unsig) +{ + int2my_decimal(E_DEC_FATAL_ERROR, val, unsig, &decimal_value); + decimals= (uint8) decimal_value.frac; + max_length= my_decimal_max_length(&decimal_value); + fixed= 1; + unsigned_flag= !decimal_value.sign(); +} + + +Item_decimal::Item_decimal(double val, int precision, int scale) +{ + double2my_decimal(E_DEC_FATAL_ERROR, val, &decimal_value); + decimals= (uint8) decimal_value.frac; + max_length= my_decimal_max_length(&decimal_value); + fixed= 1; + unsigned_flag= !decimal_value.sign(); +} + + +Item_decimal::Item_decimal(const char *str, const my_decimal *val_arg, + uint decimal_par, uint length) +{ + my_decimal2decimal(val_arg, &decimal_value); + name= (char*) str; + decimals= (uint8) decimal_par; + max_length= length; + fixed= 1; + unsigned_flag= !decimal_value.sign(); +} + + +Item_decimal::Item_decimal(my_decimal *value_par) +{ + my_decimal2decimal(value_par, &decimal_value); + decimals= (uint8) decimal_value.frac; + max_length= my_decimal_max_length(value_par); + fixed= 1; + unsigned_flag= !decimal_value.sign(); +} + + +Item_decimal::Item_decimal(const char *bin, int precision, int scale) +{ + binary2my_decimal(E_DEC_FATAL_ERROR, bin, &decimal_value, precision, scale); + decimals= (uint8) decimal_value.frac; + max_length= my_decimal_max_length(&decimal_value); + fixed= 1; + unsigned_flag= !decimal_value.sign(); +} + + +longlong Item_decimal::val_int() +{ + longlong result; + my_decimal2int(E_DEC_FATAL_ERROR, &decimal_value, unsigned_flag, &result); + return result; +} + +double Item_decimal::val_real() +{ + double result; + my_decimal2double(E_DEC_FATAL_ERROR, &decimal_value, &result); + return result; +} + +String *Item_decimal::val_str(String *result) +{ + result->set_charset(&my_charset_bin); + my_decimal2string(E_DEC_FATAL_ERROR, &decimal_value, 0, 0, 0, result); + return result; +} + +void Item_decimal::print(String *str) +{ + my_decimal2string(E_DEC_FATAL_ERROR, &decimal_value, 0, 0, 0, &str_value); + str->append(str_value); +} + + +String *Item_float::val_str(String *str) { // following assert is redundant, because fixed=1 assigned in constructor DBUG_ASSERT(fixed == 1); @@ -795,6 +1457,15 @@ String *Item_real::val_str(String *str) } +my_decimal *Item_float::val_decimal(my_decimal *decimal_value) +{ + // following assert is redundant, because fixed=1 assigned in constructor + DBUG_ASSERT(fixed == 1); + double2my_decimal(E_DEC_FATAL_ERROR, value, decimal_value); + return (decimal_value); +} + + void Item_string::print(String *str) { str->append('_'); @@ -804,9 +1475,76 @@ void Item_string::print(String *str) str->append('\''); } + +inline bool check_if_only_end_space(CHARSET_INFO *cs, char *str, char *end) +{ + return str+ cs->cset->scan(cs, str, end, MY_SEQ_SPACES) == end; +} + + +double Item_string::val_real() +{ + DBUG_ASSERT(fixed == 1); + int error; + char *end, *org_end; + double tmp; + CHARSET_INFO *cs= str_value.charset(); + + org_end= (char*) str_value.ptr() + str_value.length(); + tmp= my_strntod(cs, (char*) str_value.ptr(), str_value.length(), &end, + &error); + if (error || (end != org_end && !check_if_only_end_space(cs, end, org_end))) + { + /* + We can use str_value.ptr() here as Item_string is gurantee to put an + end \0 here. + */ + push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + ER_TRUNCATED_WRONG_VALUE, + ER(ER_TRUNCATED_WRONG_VALUE), "DOUBLE", + str_value.ptr()); + } + return tmp; +} + + +longlong Item_string::val_int() +{ + DBUG_ASSERT(fixed == 1); + int err; + longlong tmp; + char *end= (char*) str_value.ptr()+ str_value.length(); + char *org_end= end; + CHARSET_INFO *cs= str_value.charset(); + + tmp= (*(cs->cset->strtoll10))(cs, str_value.ptr(), &end, &err); + /* + TODO: Give error if we wanted a signed integer and we got an unsigned + one + */ + if (err > 0 || + (end != org_end && !check_if_only_end_space(cs, end, org_end))) + { + push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + ER_TRUNCATED_WRONG_VALUE, + ER(ER_TRUNCATED_WRONG_VALUE), "INTEGER", + str_value.ptr()); + } + return tmp; +} + + +my_decimal *Item_string::val_decimal(my_decimal *decimal_value) +{ + return val_decimal_from_string(decimal_value); +} + + bool Item_null::eq(const Item *item, bool binary_cmp) const { return item->type() == type(); } -double Item_null::val() + + +double Item_null::val_real() { // following assert is redundant, because fixed=1 assigned in constructor DBUG_ASSERT(fixed == 1); @@ -829,6 +1567,11 @@ String *Item_null::val_str(String *str) return 0; } +my_decimal *Item_null::val_decimal(my_decimal *decimal_value) +{ + return 0; +} + Item *Item_null::safe_charset_converter(CHARSET_INFO *tocs) { @@ -851,12 +1594,13 @@ default_set_param_func(Item_param *param, param->set_null(); } + Item_param::Item_param(unsigned pos_in_query_arg) : state(NO_VALUE), item_result_type(STRING_RESULT), /* Don't pretend to be a literal unless value for this item is set. */ item_type(PARAM_ITEM), - param_type(MYSQL_TYPE_STRING), + param_type(MYSQL_TYPE_VARCHAR), pos_in_query(pos_in_query_arg), set_param_func(default_set_param_func) { @@ -869,11 +1613,11 @@ Item_param::Item_param(unsigned pos_in_query_arg) : maybe_null= 1; } + void Item_param::set_null() { DBUG_ENTER("Item_param::set_null"); /* These are cleared after each execution by reset() method */ - max_length= 0; null_value= 1; /* Because of NULL and string values we need to set max_length for each new @@ -910,6 +1654,35 @@ void Item_param::set_double(double d) /* + Set decimal parameter value from string. + + SYNOPSIS + set_decimal() + str - character string + length - string length + + NOTE + as we use character strings to send decimal values in + binary protocol, we use str2my_decimal to convert it to + internal decimal value. +*/ + +void Item_param::set_decimal(const char *str, ulong length) +{ + char *end; + DBUG_ENTER("Item_param::set_decimal"); + + end= (char*) str+length; + str2my_decimal(E_DEC_FATAL_ERROR, str, &decimal_value, &end); + state= DECIMAL_VALUE; + decimals= decimal_value.frac; + max_length= decimal_value.intg + decimals + 2; + maybe_null= 0; + DBUG_VOID_RETURN; +} + + +/* Set parameter value from TIME value. SYNOPSIS @@ -938,7 +1711,7 @@ void Item_param::set_time(TIME *tm, timestamp_type type, uint32 max_length_arg) { char buff[MAX_DATE_STRING_REP_LENGTH]; uint length= my_TIME_to_str(&value.time, buff); - make_truncated_value_warning(current_thd, buff, length, type); + make_truncated_value_warning(current_thd, buff, length, type, 0); set_zero_time(&value.time, MYSQL_TIMESTAMP_ERROR); } @@ -962,6 +1735,7 @@ bool Item_param::set_str(const char *str, ulong length) &dummy_errors)) DBUG_RETURN(TRUE); state= STRING_VALUE; + max_length= length; maybe_null= 0; /* max_length and decimals are set after charset conversion */ /* sic: str may be not null-terminated, don't add DBUG_PRINT here */ @@ -1001,7 +1775,7 @@ bool Item_param::set_longdata(const char *str, ulong length) RETURN 0 OK - 1 Out of memort + 1 Out of memory */ bool Item_param::set_from_user_var(THD *thd, const user_var_entry *entry) @@ -1047,6 +1821,15 @@ bool Item_param::set_from_user_var(THD *thd, const user_var_entry *entry) DBUG_RETURN(1); break; } + case DECIMAL_RESULT: + { + const my_decimal *ent_value= (const my_decimal *)entry->value; + my_decimal2decimal(ent_value, &decimal_value); + state= DECIMAL_VALUE; + decimals= ent_value->frac; + max_length= ent_value->intg + decimals + 2; + break; + } default: DBUG_ASSERT(0); set_null(); @@ -1078,7 +1861,7 @@ void Item_param::reset() str_value.length(0); str_value_ptr.length(0); /* - We must prevent all charset conversions untill data has been written + We must prevent all charset conversions until data has been written to the binary log. */ str_value.set_charset(&my_charset_bin); @@ -1106,6 +1889,8 @@ int Item_param::save_in_field(Field *field, bool no_conversions) return field->store(value.integer); case REAL_VALUE: return field->store(value.real); + case DECIMAL_VALUE: + return field->store_decimal(&decimal_value); case TIME_VALUE: field->store_time(&value.time, value.time.time_type); return 0; @@ -1149,21 +1934,27 @@ bool Item_param::get_date(TIME *res, uint fuzzydate) } -double Item_param::val() +double Item_param::val_real() { switch (state) { case REAL_VALUE: return value.real; case INT_VALUE: return (double) value.integer; + case DECIMAL_VALUE: + { + double result; + my_decimal2double(E_DEC_FATAL_ERROR, &decimal_value, &result); + return result; + } case STRING_VALUE: case LONG_DATA_VALUE: - { - int dummy_err; - char *end_not_used; - return my_strntod(str_value.charset(), (char*) str_value.ptr(), - str_value.length(), &end_not_used, &dummy_err); - } + { + int dummy_err; + char *end_not_used; + return my_strntod(str_value.charset(), (char*) str_value.ptr(), + str_value.length(), &end_not_used, &dummy_err); + } case TIME_VALUE: /* This works for example when user says SELECT ?+0.0 and supplies @@ -1186,6 +1977,12 @@ longlong Item_param::val_int() return (longlong) (value.real + (value.real > 0 ? 0.5 : -0.5)); case INT_VALUE: return value.integer; + case DECIMAL_VALUE: + { + longlong i; + my_decimal2int(E_DEC_FATAL_ERROR, &decimal_value, unsigned_flag, &i); + return i; + } case STRING_VALUE: case LONG_DATA_VALUE: { @@ -1204,6 +2001,36 @@ longlong Item_param::val_int() } +my_decimal *Item_param::val_decimal(my_decimal *dec) +{ + switch (state) { + case DECIMAL_VALUE: + return &decimal_value; + case REAL_VALUE: + double2my_decimal(E_DEC_FATAL_ERROR, value.real, dec); + return dec; + case INT_VALUE: + int2my_decimal(E_DEC_FATAL_ERROR, value.integer, unsigned_flag, dec); + return dec; + case STRING_VALUE: + case LONG_DATA_VALUE: + string2my_decimal(E_DEC_FATAL_ERROR, &str_value, dec); + return dec; + case TIME_VALUE: + { + longlong i= (longlong) TIME_to_ulonglong(&value.time); + int2my_decimal(E_DEC_FATAL_ERROR, i, 0, dec); + return dec; + } + case NULL_VALUE: + return 0; + default: + DBUG_ASSERT(0); + } + return 0; +} + + String *Item_param::val_str(String* str) { switch (state) { @@ -1216,6 +2043,11 @@ String *Item_param::val_str(String* str) case INT_VALUE: str->set(value.integer, &my_charset_bin); return str; + case DECIMAL_VALUE: + if (my_decimal2string(E_DEC_FATAL_ERROR, &decimal_value, + 0, 0, 0, str) <= 1) + return str; + return NULL; case TIME_VALUE: { if (str->reserve(MAX_DATE_STRING_REP_LENGTH)) @@ -1248,6 +2080,11 @@ const String *Item_param::query_val_str(String* str) const case REAL_VALUE: str->set(value.real, NOT_FIXED_DEC, &my_charset_bin); break; + case DECIMAL_VALUE: + if (my_decimal2string(E_DEC_FATAL_ERROR, &decimal_value, + 0, 0, 0, str) > 1) + return &my_null_string; + break; case TIME_VALUE: { char *buf, *ptr; @@ -1279,7 +2116,7 @@ const String *Item_param::query_val_str(String* str) const buf= str->c_ptr_quick(); ptr= buf; *ptr++= '\''; - ptr+= escape_string_for_mysql(str_value.charset(), ptr, + ptr+= escape_string_for_mysql(str_value.charset(), ptr, 0, str_value.ptr(), str_value.length()); *ptr++= '\''; str->length(ptr - buf); @@ -1369,6 +2206,7 @@ bool Item_param::basic_const_item() const return TRUE; } + Item * Item_param::new_item() { @@ -1427,6 +2265,26 @@ Item_param::eq(const Item *arg, bool binary_cmp) const /* End of Item_param related */ +void Item_param::print(String *str) +{ + if (state == NO_VALUE) + { + str->append('?'); + } + else + { + char buffer[STRING_BUFFER_USUAL_SIZE]; + String tmp(buffer, sizeof(buffer), &my_charset_bin); + const String *res; + res= query_val_str(&tmp); + str->append(*res); + } +} + + +/**************************************************************************** + Item_copy_string +****************************************************************************/ void Item_copy_string::copy() { @@ -1446,6 +2304,17 @@ String *Item_copy_string::val_str(String *str) } +my_decimal *Item_copy_string::val_decimal(my_decimal *decimal_value) +{ + // Item_copy_string is used without fix_fields call + if (null_value) + return 0; + string2my_decimal(E_DEC_FATAL_ERROR, &str_value, decimal_value); + return (decimal_value); +} + + + int Item_copy_string::save_in_field(Field *field, bool no_conversions) { if (null_value) @@ -1468,10 +2337,10 @@ bool Item::fix_fields(THD *thd, // We do not check fields which are fixed during construction DBUG_ASSERT(fixed == 0 || basic_const_item()); fixed= 1; - return 0; + return FALSE; } -double Item_ref_null_helper::val() +double Item_ref_null_helper::val_real() { DBUG_ASSERT(fixed == 1); double tmp= (*ref)->val_result(); @@ -1489,6 +2358,24 @@ longlong Item_ref_null_helper::val_int() } +my_decimal *Item_ref_null_helper::val_decimal(my_decimal *decimal_value) +{ + DBUG_ASSERT(fixed == 1); + my_decimal *val= (*ref)->val_decimal_result(decimal_value); + owner->was_null|= null_value= (*ref)->null_value; + return val; +} + + +bool Item_ref_null_helper::val_bool() +{ + DBUG_ASSERT(fixed == 1); + bool val= (*ref)->val_bool_result(); + owner->was_null|= null_value= (*ref)->null_value; + return val; +} + + String* Item_ref_null_helper::val_str(String* s) { DBUG_ASSERT(fixed == 1); @@ -1505,29 +2392,37 @@ bool Item_ref_null_helper::get_date(TIME *ltime, uint fuzzydate) /* - Mark item and SELECT_LEXs as dependent if it is not outer resolving + Mark item and SELECT_LEXs as dependent if item was resolved in outer SELECT SYNOPSIS mark_as_dependent() thd - thread handler last - select from which current item depend current - current select - item - item which should be marked + resolved_item - item which was resolved in outer SELECT(for warning) + mark_item - item which should be marked (can be differ in case of + substitution) */ static void mark_as_dependent(THD *thd, SELECT_LEX *last, SELECT_LEX *current, - Item_ident *item) -{ - // store pointer on SELECT_LEX from which item is dependent - item->depended_from= last; + Item_ident *resolved_item, + Item_ident *mark_item) +{ + const char *db_name= (resolved_item->db_name ? + resolved_item->db_name : ""); + const char *table_name= (resolved_item->table_name ? + resolved_item->table_name : ""); + /* store pointer on SELECT_LEX from which item is dependent */ + if (mark_item) + mark_item->depended_from= last; current->mark_as_dependent(last); if (thd->lex->describe & DESCRIBE_EXTENDED) { char warn_buff[MYSQL_ERRMSG_SIZE]; sprintf(warn_buff, ER(ER_WARN_FIELD_RESOLVED), - (item->db_name?item->db_name:""), (item->db_name?".":""), - (item->table_name?item->table_name:""), (item->table_name?".":""), - item->field_name, + db_name, (db_name[0] ? "." : ""), + table_name, (table_name [0] ? "." : ""), + resolved_item->field_name, current->select_number, last->select_number); push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, ER_WARN_FIELD_RESOLVED, warn_buff); @@ -1535,177 +2430,471 @@ static void mark_as_dependent(THD *thd, SELECT_LEX *last, SELECT_LEX *current, } -bool Item_field::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref) + + +/* + Search a GROUP BY clause for a field with a certain name. + + SYNOPSIS + find_field_in_group_list() + find_item the item being searched for + group_list GROUP BY clause + + DESCRIPTION + Search the GROUP BY list for a column named as find_item. When searching + preference is given to columns that are qualified with the same table (and + database) name as the one being searched for. + + RETURN + - the found item on success + - NULL if find_item is not in group_list +*/ + +static Item** find_field_in_group_list(Item *find_item, ORDER *group_list) +{ + const char *db_name; + const char *table_name; + const char *field_name; + ORDER *found_group= NULL; + int found_match_degree= 0; + Item_field *cur_field; + int cur_match_degree= 0; + + if (find_item->type() == Item::FIELD_ITEM || + find_item->type() == Item::REF_ITEM) + { + db_name= ((Item_ident*) find_item)->db_name; + table_name= ((Item_ident*) find_item)->table_name; + field_name= ((Item_ident*) find_item)->field_name; + } + else + return NULL; + + DBUG_ASSERT(field_name != 0); + + for (ORDER *cur_group= group_list ; cur_group ; cur_group= cur_group->next) + { + if ((*(cur_group->item))->type() == Item::FIELD_ITEM) + { + cur_field= (Item_field*) *cur_group->item; + cur_match_degree= 0; + + DBUG_ASSERT(cur_field->field_name != 0); + + if (!my_strcasecmp(system_charset_info, + cur_field->field_name, field_name)) + ++cur_match_degree; + else + continue; + + if (cur_field->table_name && table_name) + { + /* If field_name is qualified by a table name. */ + if (strcmp(cur_field->table_name, table_name)) + /* Same field names, different tables. */ + return NULL; + + ++cur_match_degree; + if (cur_field->db_name && db_name) + { + /* If field_name is also qualified by a database name. */ + if (strcmp(cur_field->db_name, db_name)) + /* Same field names, different databases. */ + return NULL; + ++cur_match_degree; + } + } + + if (cur_match_degree > found_match_degree) + { + found_match_degree= cur_match_degree; + found_group= cur_group; + } + else if (found_group && (cur_match_degree == found_match_degree) && + ! (*(found_group->item))->eq(cur_field, 0)) + { + /* + If the current resolve candidate matches equally well as the current + best match, they must reference the same column, otherwise the field + is ambiguous. + */ + my_error(ER_NON_UNIQ_ERROR, MYF(0), + find_item->full_name(), current_thd->where); + return NULL; + } + } + } + + if (found_group) + return found_group->item; + else + return NULL; +} + + +/* + Resolve a column reference in a sub-select. + + SYNOPSIS + resolve_ref_in_select_and_group() + thd current thread + ref column reference being resolved + select the sub-select that ref is resolved against + + DESCRIPTION + Resolve a column reference (usually inside a HAVING clause) against the + SELECT and GROUP BY clauses of the query described by 'select'. The name + resolution algorithm searches both the SELECT and GROUP BY clauses, and in + case of a name conflict prefers GROUP BY column names over SELECT names. If + both clauses contain different fields with the same names, a warning is + issued that name of 'ref' is ambiguous. We extend ANSI SQL in that when no + GROUP BY column is found, then a HAVING name is resolved as a possibly + derived SELECT column. + + NOTES + The resolution procedure is: + - Search for a column or derived column named col_ref_i [in table T_j] + in the SELECT clause of Q. + - Search for a column named col_ref_i [in table T_j] + in the GROUP BY clause of Q. + - If found different columns with the same name in GROUP BY and SELECT + - issue a warning and return the GROUP BY column, + - otherwise return the found SELECT column. + + + RETURN + NULL - there was an error, and the error was already reported + not_found_item - the item was not resolved, no error was reported + resolved item - if the item was resolved +*/ + +static Item** +resolve_ref_in_select_and_group(THD *thd, Item_ident *ref, SELECT_LEX *select) +{ + Item **group_by_ref= NULL; + Item **select_ref= NULL; + ORDER *group_list= (ORDER*) select->group_list.first; + bool ambiguous_fields= FALSE; + uint counter; + bool not_used; + + /* + Search for a column or derived column named as 'ref' in the SELECT + clause of the current select. + */ + if (!(select_ref= find_item_in_list(ref, *(select->get_item_list()), + &counter, REPORT_EXCEPT_NOT_FOUND, + ¬_used))) + return NULL; /* Some error occurred. */ + + /* If this is a non-aggregated field inside HAVING, search in GROUP BY. */ + if (select->having_fix_field && !ref->with_sum_func && group_list) + { + group_by_ref= find_field_in_group_list(ref, group_list); + + /* Check if the fields found in SELECT and GROUP BY are the same field. */ + if (group_by_ref && (select_ref != not_found_item) && + !((*group_by_ref)->eq(*select_ref, 0))) + { + ambiguous_fields= TRUE; + push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_NON_UNIQ_ERROR, + ER(ER_NON_UNIQ_ERROR), ref->full_name(), + current_thd->where); + + } + } + + if (select_ref != not_found_item || group_by_ref) + { + if (select_ref != not_found_item && !ambiguous_fields) + { + DBUG_ASSERT(*select_ref != 0); + if (!select->ref_pointer_array[counter]) + { + my_error(ER_ILLEGAL_REFERENCE, MYF(0), + ref->name, "forward reference in item list"); + return NULL; + } + DBUG_ASSERT((*select_ref)->fixed); + return (select->ref_pointer_array + counter); + } + if (group_by_ref) + return group_by_ref; + DBUG_ASSERT(FALSE); + return NULL; /* So there is no compiler warning. */ + } + + return (Item**) not_found_item; +} + + +/* + Resolve the name of a column reference. + + SYNOPSIS + Item_field::fix_fields() + thd [in] current thread + tables [in] the tables in a FROM clause + reference [in/out] view column if this item was resolved to a view column + + DESCRIPTION + The method resolves the column reference represented by 'this' as a column + present in one of: FROM clause, SELECT clause, GROUP BY clause of a query + Q, or in outer queries that contain Q. + + NOTES + The name resolution algorithm used is (where [T_j] is an optional table + name that qualifies the column name): + + resolve_column_reference([T_j].col_ref_i) + { + search for a column or derived column named col_ref_i + [in table T_j] in the FROM clause of Q; + + if such a column is NOT found AND // Lookup in outer queries. + there are outer queries + { + for each outer query Q_k beginning from the inner-most one + { + if - Q_k is not a group query AND + - Q_k is not inside an aggregate function + OR + - Q_(k-1) is not in a HAVING or SELECT clause of Q_k + { + search for a column or derived column named col_ref_i + [in table T_j] in the FROM clause of Q_k; + } + + if such a column is not found + Search for a column or derived column named col_ref_i + [in table T_j] in the SELECT and GROUP clauses of Q_k. + } + } + } + + Notice that compared to Item_ref::fix_fields, here we first search the FROM + clause, and then we search the SELECT and GROUP BY clauses. + + RETURN + TRUE if error + FALSE on success +*/ + +bool Item_field::fix_fields(THD *thd, TABLE_LIST *tables, Item **reference) { enum_parsing_place place= NO_MATTER; DBUG_ASSERT(fixed == 0); if (!field) // If field is not checked { - TABLE_LIST *where= 0; - bool upward_lookup= 0; - Field *tmp= (Field *)not_found_field; - if ((tmp= find_field_in_tables(thd, this, tables, &where, 0)) == + bool upward_lookup= FALSE; + Field *from_field= (Field *)not_found_field; + /* + In case of view, find_field_in_tables() write pointer to view field + expression to 'reference', i.e. it substitute that expression instead + of this Item_field + */ + if ((from_field= find_field_in_tables(thd, this, tables, reference, + IGNORE_EXCEPT_NON_UNIQUE, + !any_privileges)) == not_found_field) { - /* - We can't find table field in table list of current select, - consequently we have to find it in outer subselect(s). - We can't join lists of outer & current select, because of scope - of view rules. For example if both tables (outer & current) have - field 'field' it is not mistake to refer to this field without - mention of table name, but if we join tables in one list it will - cause error ER_NON_UNIQ_ERROR in find_field_in_tables. - */ SELECT_LEX *last= 0; -#ifdef EMBEDDED_LIBRARY - thd->net.last_errno= 0; -#endif TABLE_LIST *table_list; - Item **refer= (Item **)not_found_item; - uint counter; - bool not_used; - // Prevent using outer fields in subselects, that is not supported now - SELECT_LEX *cursel= (SELECT_LEX *) thd->lex->current_select; - if (cursel->master_unit()->first_select()->linkage != DERIVED_TABLE_TYPE) + Item **ref= (Item **) not_found_item; + SELECT_LEX *current_sel= (SELECT_LEX *) thd->lex->current_select; + /* + If there is an outer select, and it is not a derived table (which do + not support the use of outer fields for now), try to resolve this + reference in the outer select(s). + + We treat each subselect as a separate namespace, so that different + subselects may contain columns with the same names. The subselects are + searched starting from the innermost. + */ + if (current_sel->master_unit()->first_select()->linkage != + DERIVED_TABLE_TYPE) { - SELECT_LEX_UNIT *prev_unit= cursel->master_unit(); - for (SELECT_LEX *sl= prev_unit->outer_select(); - sl; - sl= (prev_unit= sl->master_unit())->outer_select()) + SELECT_LEX_UNIT *prev_unit= current_sel->master_unit(); + SELECT_LEX *outer_sel= prev_unit->outer_select(); + for ( ; outer_sel ; + outer_sel= (prev_unit= outer_sel->master_unit())->outer_select()) { - upward_lookup= 1; - table_list= (last= sl)->get_table_list(); - if (sl->resolve_mode == SELECT_LEX::INSERT_MODE && table_list) + last= outer_sel; + Item_subselect *prev_subselect_item= prev_unit->item; + upward_lookup= TRUE; + + /* Search in the tables of the FROM clause of the outer select. */ + table_list= outer_sel->get_table_list(); + if (outer_sel->resolve_mode == SELECT_LEX::INSERT_MODE && table_list) { /* - it is primary INSERT st_select_lex => skip first table - resolving + It is a primary INSERT st_select_lex => do not resolve against the + first table. */ - table_list= table_list->next; - } - - Item_subselect *prev_subselect_item= prev_unit->item; + table_list= table_list->next_local; + } place= prev_subselect_item->parsing_place; /* - check table fields only if subquery used somewhere out of HAVING - or outer SELECT do not use groupping (i.e. tables are - accessable) + Check table fields only if the subquery is used somewhere out of + HAVING, or the outer SELECT does not use grouping (i.e. tables are + accessible). + + In case of view, find_field_in_tables() write pointer to view + field expression to 'reference', i.e. it substitute that + expression instead of this Item_field */ if ((place != IN_HAVING || - (sl->with_sum_func == 0 && sl->group_list.elements == 0)) && - (tmp= find_field_in_tables(thd, this, - table_list, &where, - 0)) != not_found_field) - { - if (!tmp) - return -1; - prev_subselect_item->used_tables_cache|= tmp->table->map; - prev_subselect_item->const_item_cache= 0; - break; - } - if (sl->resolve_mode == SELECT_LEX::SELECT_MODE && - (refer= find_item_in_list(this, sl->item_list, &counter, - REPORT_EXCEPT_NOT_FOUND, - ¬_used)) != - (Item **) not_found_item) + (outer_sel->with_sum_func == 0 && + outer_sel->group_list.elements == 0)) && + (from_field= find_field_in_tables(thd, this, table_list, + reference, + IGNORE_EXCEPT_NON_UNIQUE, + TRUE)) != + not_found_field) { - if (refer && (*refer)->fixed) // Avoid crash in case of error - { - prev_subselect_item->used_tables_cache|= (*refer)->used_tables(); - prev_subselect_item->const_item_cache&= (*refer)->const_item(); - } + if (from_field) + { + if (from_field != view_ref_found) + { + prev_subselect_item->used_tables_cache|= from_field->table->map; + prev_subselect_item->const_item_cache= 0; + } + else + { + Item::Type type= (*reference)->type(); + prev_subselect_item->used_tables_cache|= + (*reference)->used_tables(); + prev_subselect_item->const_item_cache&= + (*reference)->const_item(); + mark_as_dependent(thd, last, current_sel, this, + ((type == REF_ITEM || type == FIELD_ITEM) ? + (Item_ident*) (*reference) : + 0)); + /* + view reference found, we substituted it instead of this + Item (find_field_in_tables do it by assigning new value to + *reference), so can quit + */ + return FALSE; + } + } break; } + /* Search in the SELECT and GROUP lists of the outer select. */ + if (outer_sel->resolve_mode == SELECT_LEX::SELECT_MODE) + { + if (!(ref= resolve_ref_in_select_and_group(thd, this, outer_sel))) + return TRUE; /* Some error occurred (e.g. ambiguous names). */ + if (ref != not_found_item) + { + DBUG_ASSERT(*ref && (*ref)->fixed); + prev_subselect_item->used_tables_cache|= (*ref)->used_tables(); + prev_subselect_item->const_item_cache&= (*ref)->const_item(); + break; + } + } + // Reference is not found => depend from outer (or just error) prev_subselect_item->used_tables_cache|= OUTER_REF_TABLE_BIT; prev_subselect_item->const_item_cache= 0; - if (sl->master_unit()->first_select()->linkage == + if (outer_sel->master_unit()->first_select()->linkage == DERIVED_TABLE_TYPE) break; // do not look over derived table } } - if (!tmp) - return -1; - else if (!refer) - return 1; - else if (tmp == not_found_field && refer == (Item **)not_found_item) + + DBUG_ASSERT(ref != 0); + if (!from_field) + return TRUE; + if (ref == not_found_item && from_field == not_found_field) { if (upward_lookup) { - // We can't say exactly what absend table or field - my_printf_error(ER_BAD_FIELD_ERROR, ER(ER_BAD_FIELD_ERROR), MYF(0), - full_name(), thd->where); + // We can't say exactly what absent table or field + my_error(ER_BAD_FIELD_ERROR, MYF(0), full_name(), thd->where); } else { // Call to report error - find_field_in_tables(thd, this, tables, &where, 1); + find_field_in_tables(thd, this, tables, reference, REPORT_ALL_ERRORS, + TRUE); } - return -1; + return TRUE; } - else if (refer != (Item **)not_found_item) + else if (ref != not_found_item) { - if (!last->ref_pointer_array[counter]) - { - my_error(ER_ILLEGAL_REFERENCE, MYF(0), name, - "forward reference in item list"); - return -1; - } - DBUG_ASSERT((*refer)->fixed); + Item *save; + Item_ref *rf; + + /* Should have been checked in resolve_ref_in_select_and_group(). */ + DBUG_ASSERT(*ref && (*ref)->fixed); /* Here, a subset of actions performed by Item_ref::set_properties is not enough. So we pass ptr to NULL into Item_[direct]_ref constructor, so no initialization is performed, and call fix_fields() below. */ - Item *save= last->ref_pointer_array[counter]; - last->ref_pointer_array[counter]= NULL; - Item_ref *rf= (place == IN_HAVING ? - new Item_ref(last->ref_pointer_array + counter, - (char *)table_name, - (char *)field_name) : - new Item_direct_ref(last->ref_pointer_array + counter, - (char *)table_name, - (char *)field_name)); + save= *ref; + *ref= NULL; // Don't call set_properties() + rf= (place == IN_HAVING ? + new Item_ref(ref, (char*) table_name, (char*) field_name) : + new Item_direct_ref(ref, (char*) table_name, (char*) field_name)); + *ref= save; if (!rf) - return 1; - thd->change_item_tree(ref, rf); - last->ref_pointer_array[counter]= save; + return TRUE; + thd->change_item_tree(reference, rf); /* rf is Item_ref => never substitute other items (in this case) during fix_fields() => we can use rf after fix_fields() */ - if (rf->fix_fields(thd, tables, ref) || rf->check_cols(1)) - return 1; + DBUG_ASSERT(!rf->fixed); // Assured by Item_ref() + if (rf->fix_fields(thd, tables, reference) || rf->check_cols(1)) + return TRUE; - mark_as_dependent(thd, last, cursel, rf); - return 0; + mark_as_dependent(thd, last, current_sel, this, rf); + return FALSE; } else { - mark_as_dependent(thd, last, cursel, this); + mark_as_dependent(thd, last, current_sel, this, this); if (last->having_fix_field) { Item_ref *rf; - rf= new Item_ref((where->db[0] ? where->db : 0), - (char*) where->alias, (char*) field_name); + rf= new Item_ref((cached_table->db[0] ? cached_table->db : 0), + (char*) cached_table->alias, (char*) field_name); if (!rf) - return 1; - thd->change_item_tree(ref, rf); + return TRUE; + thd->change_item_tree(reference, rf); /* rf is Item_ref => never substitute other items (in this case) during fix_fields() => we can use rf after fix_fields() */ - return rf->fix_fields(thd, tables, ref) || rf->check_cols(1); + DBUG_ASSERT(!rf->fixed); // Assured by Item_ref() + return (rf->fix_fields(thd, tables, reference) || rf->check_cols(1)); } } } - else if (!tmp) - return -1; + else if (!from_field) + return TRUE; - set_field(tmp); + /* + if it is not expression from merged VIEW we will set this field. + + We can leave expression substituted from view for next PS/SP rexecution + (i.e. do not register this substitution for reverting on cleupup() + (register_item_tree_changing())), because this subtree will be + fix_field'ed during setup_tables()->setup_ancestor() (i.e. before + all other expressions of query, and references on tables which do + not present in query will not make problems. + + Also we suppose that view can't be changed during PS/SP life. + */ + if (from_field == view_ref_found) + return FALSE; + + set_field(from_field); } else if (thd->set_query_id && field->query_id != thd->query_id) { @@ -1714,24 +2903,187 @@ bool Item_field::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref) field->query_id=thd->query_id; table->used_fields++; table->used_keys.intersect(field->part_of_key); - fixed= 1; } - return 0; +#ifndef NO_EMBEDDED_ACCESS_CHECKS + if (any_privileges) + { + char *db, *tab; + if (cached_table->view) + { + db= cached_table->view_db.str; + tab= cached_table->view_name.str; + } + else + { + db= cached_table->db; + tab= cached_table->table_name; + } + if (!(have_privileges= (get_column_grant(thd, &field->table->grant, + db, tab, field_name) & + VIEW_ANY_ACL))) + { + my_error(ER_COLUMNACCESS_DENIED_ERROR, MYF(0), + "ANY", thd->priv_user, thd->host_or_ip, + field_name, tab); + return TRUE; + } + } +#endif + fixed= 1; + return FALSE; +} + + +Item *Item_field::safe_charset_converter(CHARSET_INFO *tocs) +{ + no_const_subst= 1; + return Item::safe_charset_converter(tocs); } + void Item_field::cleanup() { DBUG_ENTER("Item_field::cleanup"); Item_ident::cleanup(); /* Even if this object was created by direct link to field in setup_wild() - it will be linked correctly next tyme by name of field and table alias. + it will be linked correctly next time by name of field and table alias. I.e. we can drop 'field'. */ field= result_field= 0; DBUG_VOID_RETURN; } +/* + Find a field among specified multiple equalities + + SYNOPSIS + find_item_equal() + cond_equal reference to list of multiple equalities where + the field (this object) is to be looked for + + DESCRIPTION + The function first searches the field among multiple equalities + of the current level (in the cond_equal->current_level list). + If it fails, it continues searching in upper levels accessed + through a pointer cond_equal->upper_levels. + The search terminates as soon as a multiple equality containing + the field is found. + + RETURN VALUES + First Item_equal containing the field, if success + 0, otherwise +*/ +Item_equal *Item_field::find_item_equal(COND_EQUAL *cond_equal) +{ + Item_equal *item= 0; + while (cond_equal) + { + List_iterator_fast<Item_equal> li(cond_equal->current_level); + while ((item= li++)) + { + if (item->contains(field)) + return item; + } + /* + The field is not found in any of the multiple equalities + of the current level. Look for it in upper levels + */ + cond_equal= cond_equal->upper_levels; + } + return 0; +} + + +/* + Set a pointer to the multiple equality the field reference belongs to + (if any) + + SYNOPSIS + equal_fields_propagator() + arg - reference to list of multiple equalities where + the field (this object) is to be looked for + + DESCRIPTION + The function looks for a multiple equality containing the field item + 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 field + reference is substituted for this constant, otherwise it sets a pointer + to the multiple equality in the field item. + + NOTES + This function is supposed to be called as a callback parameter in calls + of the transform method. + + RETURN VALUES + pointer to the replacing constant item, if the field item was substituted + pointer to the field item, otherwise. +*/ + +Item *Item_field::equal_fields_propagator(byte *arg) +{ + if (no_const_subst) + return this; + item_equal= find_item_equal((COND_EQUAL *) arg); + Item *item= 0; + if (item_equal) + item= item_equal->get_const(); + if (!item) + item= this; + return item; +} + + +/* + Mark the item to not be part of substitution if it's not a binary item + See comments in Arg_comparator::set_compare_func() for details +*/ + +Item *Item_field::set_no_const_sub(byte *arg) +{ + if (field->charset() != &my_charset_bin) + no_const_subst=1; + return this; +} + + +/* + Replace an Item_field for an equal Item_field that evaluated earlier + (if any) + + SYNOPSIS + replace_equal_field_() + arg - a dummy parameter, is not used here + + DESCRIPTION + 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). + If the Item_field object does not refer any Item_equal object + 'this' is returned + + NOTES + This function is supposed to be called as a callback parameter in calls + of the thransformer method. + + RETURN VALUES + pointer to a replacement Item_field if there is a better equal item; + this - otherwise. +*/ + +Item *Item_field::replace_equal_field(byte *arg) +{ + if (item_equal) + { + Item_field *subst= item_equal->get_first(); + if (!field->eq(subst->field)) + return subst; + } + return this; +} + + void Item::init_make_field(Send_field *tmp_field, enum enum_field_types field_type) { @@ -1760,18 +3112,63 @@ void Item::make_field(Send_field *tmp_field) void Item_empty_string::make_field(Send_field *tmp_field) { - init_make_field(tmp_field,FIELD_TYPE_VAR_STRING); + init_make_field(tmp_field, MYSQL_TYPE_VARCHAR); } enum_field_types Item::field_type() const { - return ((result_type() == STRING_RESULT) ? FIELD_TYPE_VAR_STRING : - (result_type() == INT_RESULT) ? FIELD_TYPE_LONGLONG : - FIELD_TYPE_DOUBLE); + switch (result_type()) { + case STRING_RESULT: return MYSQL_TYPE_VARCHAR; + case INT_RESULT: return FIELD_TYPE_LONGLONG; + case DECIMAL_RESULT: return FIELD_TYPE_NEWDECIMAL; + case REAL_RESULT: return FIELD_TYPE_DOUBLE; + case ROW_RESULT: + default: + DBUG_ASSERT(0); + return MYSQL_TYPE_VARCHAR; + } +} + + +/* + Create a field to hold a string value from an item + + SYNOPSIS + make_string_field() + table Table for which the field is created + + IMPLEMENTATION + If max_length > CONVERT_IF_BIGGER_TO_BLOB create a blob + If max_length > 0 create a varchar + If max_length == 0 create a CHAR(0) +*/ + + +Field *Item::make_string_field(TABLE *table) +{ + if (max_length > CONVERT_IF_BIGGER_TO_BLOB) + return new Field_blob(max_length, maybe_null, name, table, + collation.collation); + if (max_length > 0) + return new Field_varstring(max_length, maybe_null, name, table, + collation.collation); + return new Field_string(max_length, maybe_null, name, table, + collation.collation); } +/* + Create a field based on field_type of argument + + For now, this is only used to create a field for + IFNULL(x,something) + + RETURN + 0 error + # Created field +*/ + Field *Item::tmp_table_field_from_field_type(TABLE *table) { /* @@ -1783,6 +3180,11 @@ Field *Item::tmp_table_field_from_field_type(TABLE *table) case MYSQL_TYPE_DECIMAL: return new Field_decimal((char*) 0, max_length, null_ptr, 0, Field::NONE, name, table, decimals, 0, unsigned_flag); + case MYSQL_TYPE_NEWDECIMAL: + return new Field_new_decimal((char*) 0, max_length - (decimals?1:0), + null_ptr, 0, + Field::NONE, name, table, decimals, 0, + unsigned_flag); case MYSQL_TYPE_TINY: return new Field_tiny((char*) 0, max_length, null_ptr, 0, Field::NONE, name, table, 0, unsigned_flag); @@ -1821,32 +3223,24 @@ Field *Item::tmp_table_field_from_field_type(TABLE *table) return new Field_year((char*) 0, max_length, null_ptr, 0, Field::NONE, name, table); default: - /* This case should never be choosen */ + /* This case should never be chosen */ DBUG_ASSERT(0); /* If something goes awfully wrong, it's better to get a string than die */ case MYSQL_TYPE_ENUM: case MYSQL_TYPE_SET: - case MYSQL_TYPE_VAR_STRING: - if (max_length > 255) - break; // If blob - return new Field_varstring(max_length, maybe_null, name, table, - collation.collation); case MYSQL_TYPE_STRING: - if (max_length > 255) // If blob - break; - return new Field_string(max_length, maybe_null, name, table, - collation.collation); + case MYSQL_TYPE_VAR_STRING: + case MYSQL_TYPE_VARCHAR: + return make_string_field(table); case MYSQL_TYPE_TINY_BLOB: case MYSQL_TYPE_MEDIUM_BLOB: case MYSQL_TYPE_LONG_BLOB: case MYSQL_TYPE_BLOB: case MYSQL_TYPE_GEOMETRY: + return new Field_blob(max_length, maybe_null, name, table, + collation.collation); break; // Blob handled outside of case } - - /* blob is special as it's generated for both blobs and long strings */ - return new Field_blob(max_length, maybe_null, name, table, - collation.collation); } @@ -1854,7 +3248,7 @@ Field *Item::tmp_table_field_from_field_type(TABLE *table) void Item_field::make_field(Send_field *tmp_field) { field->make_field(tmp_field); - DBUG_ASSERT(tmp_field->table_name); + DBUG_ASSERT(tmp_field->table_name != 0); if (name) tmp_field->col_name=name; // Use user supplied name } @@ -1926,7 +3320,7 @@ int Item_null::save_in_field(Field *field, bool no_conversions) field Field where we want to store NULL RETURN VALUES - 0 ok + 0 OK 1 Field doesn't support NULL values */ @@ -1959,12 +3353,21 @@ int Item::save_in_field(Field *field, bool no_conversions) } else if (result_type() == REAL_RESULT) { - double nr=val(); + double nr= val_real(); if (null_value) return set_field_to_null(field); field->set_notnull(); error=field->store(nr); } + else if (result_type() == DECIMAL_RESULT) + { + my_decimal decimal_value; + my_decimal *value= val_decimal(&decimal_value); + if (null_value) + return set_field_to_null(field); + field->set_notnull(); + error=field->store_decimal(value); + } else { longlong nr=val_int(); @@ -2007,6 +3410,13 @@ int Item_int::save_in_field(Field *field, bool no_conversions) } +int Item_decimal::save_in_field(Field *field, bool no_conversions) +{ + field->set_notnull(); + return field->store_decimal(&decimal_value); +} + + bool Item_int::eq(const Item *arg, bool binary_cmp) const { /* No need to check for null value as basic constant can't be NULL */ @@ -2025,12 +3435,64 @@ bool Item_int::eq(const Item *arg, bool binary_cmp) const Item_num *Item_uint::neg() { - return new Item_real(name, - ((double) value), 0, max_length); + Item_decimal *item= new Item_decimal(value, 0); + return item->neg(); } -int Item_real::save_in_field(Field *field, bool no_conversions) + +static uint nr_of_decimals(const char *str, const char *end) { - double nr=val(); + const char *decimal_point; + + /* Find position for '.' */ + for (;;) + { + if (str == end) + return 0; + if (*str == 'e' || *str == 'E') + return NOT_FIXED_DEC; + if (*str++ == '.') + break; + } + decimal_point= str; + for (; my_isdigit(system_charset_info, *str) ; str++) + ; + if (*str == 'e' || *str == 'E') + return NOT_FIXED_DEC; + return (uint) (str - decimal_point); +} + + +/* + This function is only called during parsing. We will signal an error if + value is not a true double value (overflow) +*/ + +Item_float::Item_float(const char *str_arg, uint length) +{ + int error; + char *end_not_used; + value= my_strntod(&my_charset_bin, (char*) str_arg, length, &end_not_used, + &error); + if (error) + { + /* + Note that we depend on that str_arg is null terminated, which is true + when we are in the parser + */ + DBUG_ASSERT(str_arg[length] == 0); + my_error(ER_ILLEGAL_VALUE_FOR_TYPE, MYF(0), "double", (char*) str_arg); + } + presentation= name=(char*) str_arg; + decimals=(uint8) nr_of_decimals(str_arg, str_arg+length); + max_length=length; + fixed= 1; +} + + +int Item_float::save_in_field(Field *field, bool no_conversions) +{ + double nr= val_real(); if (null_value) return set_field_to_null(field); field->set_notnull(); @@ -2038,6 +3500,26 @@ int Item_real::save_in_field(Field *field, bool no_conversions) } +void Item_float::print(String *str) +{ + if (presentation) + { + str->append(presentation); + return; + } + char buffer[20]; + String num(buffer, sizeof(buffer), &my_charset_bin); + num.set(value, decimals, &my_charset_bin); + str->append(num); +} + + +/* + hex item + In string context this is a binary string. + In number context this is a longlong value. +*/ + bool Item_real::eq(const Item *arg, bool binary_cmp) const { if (arg->basic_const_item() && arg->type() == type()) @@ -2052,11 +3534,6 @@ bool Item_real::eq(const Item *arg, bool binary_cmp) const return FALSE; } -/**************************************************************************** -** varbinary item -** In string context this is a binary string -** In number context this is a longlong value. -****************************************************************************/ inline uint char_val(char X) { @@ -2066,7 +3543,7 @@ inline uint char_val(char X) } -Item_varbinary::Item_varbinary(const char *str, uint str_length) +Item_hex_string::Item_hex_string(const char *str, uint str_length) { name=(char*) str-2; // Lex makes this start with 0x max_length=(str_length+1)/2; @@ -2087,7 +3564,7 @@ Item_varbinary::Item_varbinary(const char *str, uint str_length) fixed= 1; } -longlong Item_varbinary::val_int() +longlong Item_hex_string::val_int() { // following assert is redundant, because fixed=1 assigned in constructor DBUG_ASSERT(fixed == 1); @@ -2101,7 +3578,17 @@ longlong Item_varbinary::val_int() } -int Item_varbinary::save_in_field(Field *field, bool no_conversions) +my_decimal *Item_hex_string::val_decimal(my_decimal *decimal_value) +{ + // following assert is redundant, because fixed=1 assigned in constructor + DBUG_ASSERT(fixed == 1); + ulonglong value= (ulonglong)val_int(); + int2my_decimal(E_DEC_FATAL_ERROR, value, TRUE, decimal_value); + return (decimal_value); +} + + +int Item_hex_string::save_in_field(Field *field, bool no_conversions) { int error; field->set_notnull(); @@ -2130,6 +3617,44 @@ bool Item_varbinary::eq(const Item *arg, bool binary_cmp) const } /* + bin item. + In string context this is a binary string. + In number context this is a longlong value. +*/ + +Item_bin_string::Item_bin_string(const char *str, uint str_length) +{ + const char *end= str + str_length - 1; + uchar bits= 0; + uint power= 1; + + name= (char*) str - 2; + max_length= (str_length + 7) >> 3; + char *ptr= (char*) sql_alloc(max_length + 1); + if (!ptr) + return; + str_value.set(ptr, max_length, &my_charset_bin); + ptr+= max_length - 1; + ptr[1]= 0; // Set end null for string + for (; end >= str; end--) + { + if (power == 256) + { + power= 1; + *ptr--= bits; + bits= 0; + } + if (*end == '1') + bits|= power; + power<<= 1; + } + *ptr= (char) bits; + collation.set(&my_charset_bin, DERIVATION_COERCIBLE); + fixed= 1; +} + + +/* Pack data in buffer for sending */ @@ -2146,7 +3671,7 @@ bool Item::send(Protocol *protocol, String *buffer) { bool result; enum_field_types type; - LINT_INIT(result); + LINT_INIT(result); // Will be set if null_value == 0 switch ((type=field_type())) { default: @@ -2161,6 +3686,9 @@ bool Item::send(Protocol *protocol, String *buffer) case MYSQL_TYPE_GEOMETRY: case MYSQL_TYPE_STRING: case MYSQL_TYPE_VAR_STRING: + case MYSQL_TYPE_VARCHAR: + case MYSQL_TYPE_BIT: + case MYSQL_TYPE_NEWDECIMAL: { String *res; if ((res=val_str(buffer))) @@ -2176,6 +3704,7 @@ bool Item::send(Protocol *protocol, String *buffer) break; } case MYSQL_TYPE_SHORT: + case MYSQL_TYPE_YEAR: { longlong nr; nr= val_int(); @@ -2203,15 +3732,14 @@ bool Item::send(Protocol *protocol, String *buffer) case MYSQL_TYPE_FLOAT: { float nr; - nr= (float) val(); + nr= (float) val_real(); if (!null_value) result= protocol->store(nr, decimals, buffer); break; } case MYSQL_TYPE_DOUBLE: { - double nr; - nr= val(); + double nr= val_real(); if (!null_value) result= protocol->store(nr, decimals, buffer); break; @@ -2252,200 +3780,298 @@ bool Item_field::send(Protocol *protocol, String *buffer) } +Item_ref::Item_ref(Item **item, const char *table_name_par, + const char *field_name_par) + :Item_ident(NullS, table_name_par, field_name_par), result_field(0), + ref(item) +{ + /* + This constructor used to create some internals references over fixed items + */ + DBUG_ASSERT(ref != 0); + if (*ref) + set_properties(); +} + + /* - This is used for HAVING clause - Find field in select list having the same name + Resolve the name of a reference to a column reference. + + SYNOPSIS + Item_ref::fix_fields() + thd [in] current thread + tables [in] the tables in a FROM clause + reference [in/out] view column if this item was resolved to a view column + + DESCRIPTION + The method resolves the column reference represented by 'this' as a column + present in one of: GROUP BY clause, SELECT clause, outer queries. It is + used typically for columns in the HAVING clause which are not under + aggregate functions. + + NOTES + The name resolution algorithm used is (where [T_j] is an optional table + name that qualifies the column name): + + resolve_extended([T_j].col_ref_i) + { + Search for a column or derived column named col_ref_i [in table T_j] + in the SELECT and GROUP clauses of Q. + + if such a column is NOT found AND // Lookup in outer queries. + there are outer queries + { + for each outer query Q_k beginning from the inner-most one + { + Search for a column or derived column named col_ref_i + [in table T_j] in the SELECT and GROUP clauses of Q_k. + + if such a column is not found AND + - Q_k is not a group query AND + - Q_k is not inside an aggregate function + OR + - Q_(k-1) is not in a HAVING or SELECT clause of Q_k + { + search for a column or derived column named col_ref_i + [in table T_j] in the FROM clause of Q_k; + } + } + } + } + + This procedure treats GROUP BY and SELECT clauses as one namespace for + column references in HAVING. Notice that compared to + Item_field::fix_fields, here we first search the SELECT and GROUP BY + clauses, and then we search the FROM clause. + + POSTCONDITION + Item_ref::ref is 0 or points to a valid item + + RETURN + TRUE if error + FALSE on success */ -bool Item_ref::fix_fields(THD *thd,TABLE_LIST *tables, Item **reference) +bool Item_ref::fix_fields(THD *thd, TABLE_LIST *tables, Item **reference) { DBUG_ASSERT(fixed == 0); - uint counter; enum_parsing_place place= NO_MATTER; - bool not_used; - if (!ref) + SELECT_LEX *current_sel= thd->lex->current_select; + + if (!ref || ref == not_found_item) { - TABLE_LIST *where= 0, *table_list; - SELECT_LEX_UNIT *prev_unit= thd->lex->current_select->master_unit(); - SELECT_LEX *sl= prev_unit->outer_select(); - /* - Finding only in current select will be performed for selects that have - not outer one and for derived tables (which not support using outer - fields for now) - */ - if ((ref= find_item_in_list(this, - *(thd->lex->current_select->get_item_list()), - &counter, - ((sl && - thd->lex->current_select->master_unit()-> - first_select()->linkage != - DERIVED_TABLE_TYPE) ? - REPORT_EXCEPT_NOT_FOUND : - REPORT_ALL_ERRORS ), ¬_used)) == - (Item **)not_found_item) + SELECT_LEX_UNIT *prev_unit= current_sel->master_unit(); + SELECT_LEX *outer_sel= prev_unit->outer_select(); + + if (!(ref= resolve_ref_in_select_and_group(thd, this, current_sel))) + return TRUE; /* Some error occurred (e.g. ambiguous names). */ + + if (ref == not_found_item) /* This reference was not resolved. */ { - Field *tmp= (Field*) not_found_field; - SELECT_LEX *last= 0; + TABLE_LIST *table_list; + Field *from_field; + SELECT_LEX *last; + ref= 0; + + if (!outer_sel || (current_sel->master_unit()->first_select()->linkage == + DERIVED_TABLE_TYPE)) + { + /* The current reference cannot be resolved in this query. */ + my_error(ER_BAD_FIELD_ERROR,MYF(0), + this->full_name(), current_thd->where); + return TRUE; + } /* - We can't find table field in select list of current select, - consequently we have to find it in outer subselect(s). - We can't join lists of outer & current select, because of scope - of view rules. For example if both tables (outer & current) have - field 'field' it is not mistake to refer to this field without - mention of table name, but if we join tables in one list it will - cause error ER_NON_UNIQ_ERROR in find_item_in_list. + If there is an outer select, and it is not a derived table (which do + not support the use of outer fields for now), try to resolve this + reference in the outer select(s). + + We treat each subselect as a separate namespace, so that different + subselects may contain columns with the same names. The subselects are + searched starting from the innermost. */ - for ( ; sl ; sl= (prev_unit= sl->master_unit())->outer_select()) + from_field= (Field*) not_found_field; + last= 0; + + /* The following loop will always be excuted at least once */ + for ( ; outer_sel ; + outer_sel= (prev_unit= outer_sel->master_unit())->outer_select()) { - last= sl; - Item_subselect *prev_subselect_item= prev_unit->item; - if (sl->resolve_mode == SELECT_LEX::SELECT_MODE && - (ref= find_item_in_list(this, sl->item_list, - &counter, REPORT_EXCEPT_NOT_FOUND, - ¬_used)) != - (Item **)not_found_item) - { - if (ref && (*ref)->fixed) // Avoid crash in case of error - { - prev_subselect_item->used_tables_cache|= (*ref)->used_tables(); - prev_subselect_item->const_item_cache&= (*ref)->const_item(); - } - break; - } - table_list= sl->get_table_list(); - if (sl->resolve_mode == SELECT_LEX::INSERT_MODE && table_list) - { - // it is primary INSERT st_select_lex => skip first table resolving - table_list= table_list->next; - } + last= outer_sel; + Item_subselect *prev_subselect_item= prev_unit->item; + + /* Search in the SELECT and GROUP lists of the outer select. */ + if (outer_sel->resolve_mode == SELECT_LEX::SELECT_MODE) + { + if (!(ref= resolve_ref_in_select_and_group(thd, this, outer_sel))) + return TRUE; /* Some error occurred (e.g. ambiguous names). */ + if (ref != not_found_item) + { + DBUG_ASSERT(*ref && (*ref)->fixed); + prev_subselect_item->used_tables_cache|= (*ref)->used_tables(); + prev_subselect_item->const_item_cache&= (*ref)->const_item(); + break; + } + /* + Set ref to 0 to ensure that we get an error in case we replaced + this item with another item and still use this item in some + other place of the parse tree. + */ + ref= 0; + } + + /* Search in the tables of the FROM clause of the outer select. */ + table_list= outer_sel->get_table_list(); + if (outer_sel->resolve_mode == SELECT_LEX::INSERT_MODE && table_list) + { + /* + It is a primary INSERT st_select_lex => do not resolve against + the first table. + */ + table_list= table_list->next_local; + } + place= prev_subselect_item->parsing_place; /* - check table fields only if subquery used somewhere out of HAVING - or SELECT list or outer SELECT do not use groupping (i.e. tables - are accessable) + Check table fields only if the subquery is used somewhere out of + HAVING or the outer SELECT does not use grouping (i.e. tables are + accessible). + TODO: + Here we could first find the field anyway, and then test this + condition, so that we can give a better error message - + ER_WRONG_FIELD_WITH_GROUP, instead of the less informative + ER_BAD_FIELD_ERROR which we produce now. */ if ((place != IN_HAVING || - (sl->with_sum_func == 0 && sl->group_list.elements == 0)) && - (tmp= find_field_in_tables(thd, this, - table_list, &where, - 0)) != not_found_field) + (!outer_sel->with_sum_func && + outer_sel->group_list.elements == 0))) { - prev_subselect_item->used_tables_cache|= tmp->table->map; - prev_subselect_item->const_item_cache= 0; - break; + /* + In case of view, find_field_in_tables() write pointer to view + field expression to 'reference', i.e. it substitute that + expression instead of this Item_ref + */ + from_field= find_field_in_tables(thd, this, table_list, + reference, + IGNORE_EXCEPT_NON_UNIQUE, + TRUE); + if (! from_field) + return TRUE; + if (from_field == view_ref_found) + { + Item::Type type= (*reference)->type(); + prev_subselect_item->used_tables_cache|= + (*reference)->used_tables(); + prev_subselect_item->const_item_cache&= + (*reference)->const_item(); + DBUG_ASSERT((*reference)->type() == REF_ITEM); + mark_as_dependent(thd, last, current_sel, this, + ((type == REF_ITEM || type == FIELD_ITEM) ? + (Item_ident*) (*reference) : + 0)); + /* + view reference found, we substituted it instead of this + Item, so can quit + */ + return FALSE; + } + if (from_field != not_found_field) + { + prev_subselect_item->used_tables_cache|= from_field->table->map; + prev_subselect_item->const_item_cache= 0; + break; + } } - // Reference is not found => depend from outer (or just error) - prev_subselect_item->used_tables_cache|= OUTER_REF_TABLE_BIT; - prev_subselect_item->const_item_cache= 0; + DBUG_ASSERT(from_field == not_found_field); - if (sl->master_unit()->first_select()->linkage == - DERIVED_TABLE_TYPE) - break; // do not look over derived table - } + /* Reference is not found => depend on outer (or just error). */ + prev_subselect_item->used_tables_cache|= OUTER_REF_TABLE_BIT; + prev_subselect_item->const_item_cache= 0; - if (!ref) - return 1; - if (!tmp) - return -1; - if (ref == (Item **)not_found_item && tmp == not_found_field) - { - // We can't say exactly what absend (table or field) - my_printf_error(ER_BAD_FIELD_ERROR, ER(ER_BAD_FIELD_ERROR), MYF(0), - full_name(), thd->where); - ref= 0; // Safety - return 1; + if (outer_sel->master_unit()->first_select()->linkage == + DERIVED_TABLE_TYPE) + break; /* Do not consider derived tables. */ } - if (tmp != not_found_field) + + DBUG_ASSERT(from_field != 0 && from_field != view_ref_found); + if (from_field != not_found_field) { Item_field* fld; - /* - Set ref to 0 as we are replacing this item with the found item - and this will ensure we get an error if this item would be - used elsewhere - */ - ref= 0; // Safety - if (!(fld= new Item_field(tmp))) - return 1; - thd->change_item_tree(reference, fld); - mark_as_dependent(thd, last, thd->lex->current_select, fld); - return 0; - } - if (!last->ref_pointer_array[counter]) - { - my_error(ER_ILLEGAL_REFERENCE, MYF(0), name, - "forward reference in item list"); - return -1; + if (!(fld= new Item_field(from_field))) + return TRUE; + thd->change_item_tree(reference, fld); + mark_as_dependent(thd, last, thd->lex->current_select, this, fld); + return FALSE; } - DBUG_ASSERT((*ref)->fixed); - mark_as_dependent(thd, last, thd->lex->current_select, - this); - if (place == IN_HAVING) - { - Item_ref *rf; - if (!(rf= new Item_direct_ref(last->ref_pointer_array + counter, - (char *)table_name, - (char *)field_name))) - return 1; - ref= 0; // Safety - if (rf->fix_fields(thd, tables, ref) || rf->check_cols(1)) - return 1; - thd->change_item_tree(reference, rf); - return 0; - } - ref= last->ref_pointer_array + counter; - } - else if (!ref) - return 1; - else - { - if (!(*ref)->fixed) + if (ref == 0) { - my_error(ER_ILLEGAL_REFERENCE, MYF(0), name, - "forward reference in item list"); - return -1; + /* The item was not a table field and not a reference */ + my_error(ER_BAD_FIELD_ERROR, MYF(0), + this->full_name(), current_thd->where); + return TRUE; } - ref= thd->lex->current_select->ref_pointer_array + counter; + /* Should be checked in resolve_ref_in_select_and_group(). */ + DBUG_ASSERT(*ref && (*ref)->fixed); + mark_as_dependent(thd, last, current_sel, this, this); } } + DBUG_ASSERT(*ref); /* - The following conditional is changed as to correctly identify - incorrect references in group functions or forward references - with sub-select's / derived tables, while it prevents this - check when Item_ref is created in an expression involving - summing function, which is to be placed in the user variable. + Check if this is an incorrect reference in a group function or forward + reference. Do not issue an error if this is an unnamed reference inside an + aggregate function. */ if (((*ref)->with_sum_func && name && (depended_from || - !(thd->lex->current_select->linkage != GLOBAL_OPTIONS_TYPE && - thd->lex->current_select->having_fix_field))) || + !(current_sel->linkage != GLOBAL_OPTIONS_TYPE && + current_sel->having_fix_field))) || !(*ref)->fixed) { - my_error(ER_ILLEGAL_REFERENCE, MYF(0), name, - ((*ref)->with_sum_func? - "reference on group function": - "forward reference in item list")); - return 1; + my_error(ER_ILLEGAL_REFERENCE, MYF(0), + name, ((*ref)->with_sum_func? + "reference to group function": + "forward reference in item list")); + return TRUE; } set_properties(); - if (ref && (*ref)->check_cols(1)) - return 1; - return 0; + if ((*ref)->check_cols(1)) + return TRUE; + return FALSE; } + void Item_ref::set_properties() { max_length= (*ref)->max_length; maybe_null= (*ref)->maybe_null; decimals= (*ref)->decimals; collation.set((*ref)->collation); + /* + We have to remember if we refer to a sum function, to ensure that + split_sum_func() doesn't try to change the reference. + */ with_sum_func= (*ref)->with_sum_func; unsigned_flag= (*ref)->unsigned_flag; + if ((*ref)->type() == FIELD_ITEM) + alias_name_used= ((Item_ident *) (*ref))->alias_name_used; + else + alias_name_used= TRUE; // it is not field, so it is was resolved by alias fixed= 1; } + +void Item_ref::cleanup() +{ + DBUG_ENTER("Item_ref::cleanup"); + Item_ident::cleanup(); + result_field= 0; + DBUG_VOID_RETURN; +} + + void Item_ref::print(String *str) { if (ref && *ref) @@ -2455,6 +4081,150 @@ void Item_ref::print(String *str) } +bool Item_ref::send(Protocol *prot, String *tmp) +{ + if (result_field) + return prot->store(result_field); + return (*ref)->send(prot, tmp); +} + + +double Item_ref::val_result() +{ + if (result_field) + { + if ((null_value= result_field->is_null())) + return 0.0; + return result_field->val_real(); + } + return val_real(); +} + + +longlong Item_ref::val_int_result() +{ + if (result_field) + { + if ((null_value= result_field->is_null())) + return 0; + return result_field->val_int(); + } + return val_int(); +} + + +String *Item_ref::str_result(String* str) +{ + if (result_field) + { + if ((null_value= result_field->is_null())) + return 0; + str->set_charset(str_value.charset()); + return result_field->val_str(str, &str_value); + } + return val_str(str); +} + + +my_decimal *Item_ref::val_decimal_result(my_decimal *decimal_value) +{ + if (result_field) + { + if ((null_value= result_field->is_null())) + return 0; + return result_field->val_decimal(decimal_value); + } + return val_decimal(decimal_value); +} + + +bool Item_ref::val_bool_result() +{ + if (result_field) + { + if ((null_value= result_field->is_null())) + return 0; + switch (result_field->result_type()) { + case INT_RESULT: + return result_field->val_int() != 0; + case DECIMAL_RESULT: + { + my_decimal decimal_value; + my_decimal *val= result_field->val_decimal(&decimal_value); + if (val) + return !my_decimal_is_zero(val); + return 0; + } + case REAL_RESULT: + case STRING_RESULT: + return result_field->val_real() != 0.0; + case ROW_RESULT: + default: + DBUG_ASSERT(0); + } + } + return val_bool(); +} + + +double Item_ref::val_real() +{ + DBUG_ASSERT(fixed); + double tmp=(*ref)->val_result(); + null_value=(*ref)->null_value; + return tmp; +} + + +longlong Item_ref::val_int() +{ + DBUG_ASSERT(fixed); + longlong tmp=(*ref)->val_int_result(); + null_value=(*ref)->null_value; + return tmp; +} + + +bool Item_ref::val_bool() +{ + DBUG_ASSERT(fixed); + bool tmp= (*ref)->val_bool_result(); + null_value= (*ref)->null_value; + return tmp; +} + + +String *Item_ref::val_str(String* tmp) +{ + DBUG_ASSERT(fixed); + tmp=(*ref)->str_result(tmp); + null_value=(*ref)->null_value; + return tmp; +} + + +bool Item_ref::is_null() +{ + DBUG_ASSERT(fixed); + (void) (*ref)->val_int_result(); + return (*ref)->null_value; +} + + +bool Item_ref::get_date(TIME *ltime,uint fuzzydate) +{ + return (null_value=(*ref)->get_date_result(ltime,fuzzydate)); +} + + +my_decimal *Item_ref::val_decimal(my_decimal *decimal_value) +{ + my_decimal *val= (*ref)->val_decimal(decimal_value); + null_value= (*ref)->null_value; + return val; +} + + void Item_ref_null_helper::print(String *str) { str->append("<ref_null_helper>(", 18); @@ -2466,6 +4236,59 @@ void Item_ref_null_helper::print(String *str) } +double Item_direct_ref::val_real() +{ + double tmp=(*ref)->val_real(); + null_value=(*ref)->null_value; + return tmp; +} + + +longlong Item_direct_ref::val_int() +{ + longlong tmp=(*ref)->val_int(); + null_value=(*ref)->null_value; + return tmp; +} + + +String *Item_direct_ref::val_str(String* tmp) +{ + tmp=(*ref)->val_str(tmp); + null_value=(*ref)->null_value; + return tmp; +} + + +my_decimal *Item_direct_ref::val_decimal(my_decimal *decimal_value) +{ + my_decimal *tmp= (*ref)->val_decimal(decimal_value); + null_value=(*ref)->null_value; + return tmp; +} + + +bool Item_direct_ref::val_bool() +{ + bool tmp= (*ref)->val_bool(); + null_value=(*ref)->null_value; + return tmp; +} + + +bool Item_direct_ref::is_null() +{ + (void) (*ref)->val_int(); + return (*ref)->null_value; +} + + +bool Item_direct_ref::get_date(TIME *ltime,uint fuzzydate) +{ + return (null_value=(*ref)->get_date(ltime,fuzzydate)); +} + + void Item_null_helper::print(String *str) { str->append("<null_helper>(", 14); @@ -2485,35 +4308,43 @@ bool Item_default_value::fix_fields(THD *thd, struct st_table_list *table_list, Item **items) { + Item_field *field_arg; + Field *def_field; DBUG_ASSERT(fixed == 0); + if (!arg) { fixed= 1; - return 0; + return FALSE; } if (!arg->fixed && arg->fix_fields(thd, table_list, &arg)) - return 1; + return TRUE; if (arg->type() == REF_ITEM) { Item_ref *ref= (Item_ref *)arg; if (ref->ref[0]->type() != FIELD_ITEM) { - return 1; + return TRUE; } arg= ref->ref[0]; } - Item_field *field_arg= (Item_field *)arg; - Field *def_field= (Field*) sql_alloc(field_arg->field->size_of()); - if (!def_field) - return 1; + field_arg= (Item_field *)arg; + if (field_arg->field->flags & NO_DEFAULT_VALUE_FLAG) + { + my_error(ER_NO_DEFAULT_FOR_FIELD, MYF(0), field_arg->field->field_name); + return TRUE; + } + if (!(def_field= (Field*) sql_alloc(field_arg->field->size_of()))) + return TRUE; memcpy(def_field, field_arg->field, field_arg->field->size_of()); - def_field->move_field(def_field->table->default_values - + def_field->move_field(def_field->table->s->default_values - def_field->table->record[0]); set_field(def_field); - return 0; + return FALSE; } + void Item_default_value::print(String *str) { if (!arg) @@ -2526,6 +4357,27 @@ void Item_default_value::print(String *str) str->append(')'); } + +int Item_default_value::save_in_field(Field *field_arg, bool no_conversions) +{ + if (!arg) + { + if (field_arg->flags & NO_DEFAULT_VALUE_FLAG) + { + push_warning_printf(field_arg->table->in_use, + MYSQL_ERROR::WARN_LEVEL_WARN, + ER_NO_DEFAULT_FOR_FIELD, + ER(ER_NO_DEFAULT_FOR_FIELD), + field_arg->field_name); + return 1; + } + field_arg->set_default(); + return 0; + } + return Item_field::save_in_field(field_arg, no_conversions); +} + + bool Item_insert_value::eq(const Item *item, bool binary_cmp) const { return item->type() == INSERT_VALUE_ITEM && @@ -2539,14 +4391,14 @@ bool Item_insert_value::fix_fields(THD *thd, { DBUG_ASSERT(fixed == 0); if (!arg->fixed && arg->fix_fields(thd, table_list, &arg)) - return 1; + return TRUE; if (arg->type() == REF_ITEM) { Item_ref *ref= (Item_ref *)arg; if (ref->ref[0]->type() != FIELD_ITEM) { - return 1; + return TRUE; } arg= ref->ref[0]; } @@ -2555,7 +4407,7 @@ bool Item_insert_value::fix_fields(THD *thd, { Field *def_field= (Field*) sql_alloc(field_arg->field->size_of()); if (!def_field) - return 1; + return TRUE; memcpy(def_field, field_arg->field, field_arg->field->size_of()); def_field->move_field(def_field->table->insert_values - def_field->table->record[0]); @@ -2568,7 +4420,7 @@ bool Item_insert_value::fix_fields(THD *thd, set_field(new Field_null(0, 0, Field::NONE, tmp_field->field_name, tmp_field->table, &my_charset_bin)); } - return 0; + return FALSE; } void Item_insert_value::print(String *str) @@ -2578,6 +4430,98 @@ void Item_insert_value::print(String *str) str->append(')'); } + +/* + Bind item representing field of row being changed in trigger + to appropriate Field object. + + SYNOPSIS + setup_field() + thd - current thread context + table - table of trigger (and where we looking for fields) + event - type of trigger event + + NOTE + This function does almost the same as fix_fields() for Item_field + but is invoked during trigger definition parsing and takes TABLE + object as its argument. If proper field was not found in table + error will be reported at fix_fields() time. +*/ +void Item_trigger_field::setup_field(THD *thd, TABLE *table, + enum trg_event_type event) +{ + uint field_idx= (uint)-1; + bool save_set_query_id= thd->set_query_id; + + /* TODO: Think more about consequences of this step. */ + thd->set_query_id= 0; + + if (find_field_in_real_table(thd, table, field_name, + strlen(field_name), 0, 0, + &field_idx)) + { + field= (row_version == OLD_ROW && event == TRG_EVENT_UPDATE) ? + table->triggers->old_field[field_idx] : + table->field[field_idx]; + } + + thd->set_query_id= save_set_query_id; +} + + +bool Item_trigger_field::eq(const Item *item, bool binary_cmp) const +{ + return item->type() == TRIGGER_FIELD_ITEM && + row_version == ((Item_trigger_field *)item)->row_version && + !my_strcasecmp(system_charset_info, field_name, + ((Item_trigger_field *)item)->field_name); +} + + +bool Item_trigger_field::fix_fields(THD *thd, + TABLE_LIST *table_list, + Item **items) +{ + /* + Since trigger is object tightly associated with TABLE object most + of its set up can be performed during trigger loading i.e. trigger + parsing! So we have little to do in fix_fields. :) + FIXME may be we still should bother about permissions here. + */ + DBUG_ASSERT(fixed == 0); + + if (field) + { + // QQ: May be this should be moved to setup_field? + set_field(field); + fixed= 1; + return 0; + } + + my_error(ER_BAD_FIELD_ERROR, MYF(0), field_name, + (row_version == NEW_ROW) ? "NEW" : "OLD"); + return 1; +} + + +void Item_trigger_field::print(String *str) +{ + str->append((row_version == NEW_ROW) ? "NEW" : "OLD", 3); + str->append('.'); + str->append(field_name); +} + + +void Item_trigger_field::cleanup() +{ + /* + Since special nature of Item_trigger_field we should not do most of + things from Item_field::cleanup() or Item_ident::cleanup() here. + */ + Item::cleanup(); +} + + /* If item is a const function, calculate it and return a const item The original item is freed if not returned @@ -2591,6 +4535,9 @@ 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; + if ((a == INT_RESULT || a == DECIMAL_RESULT) && + (b == INT_RESULT || b == DECIMAL_RESULT)) + return DECIMAL_RESULT; return REAL_RESULT; } @@ -2605,7 +4552,8 @@ void resolve_const_item(THD *thd, Item **ref, Item *comp_item) item->result_type()); char *name=item->name; // Alloced by sql_alloc - if (res_type == STRING_RESULT) + switch (res_type) { + case STRING_RESULT: { char buff[MAX_FIELD_WIDTH]; String tmp(buff,sizeof(buff),&my_charset_bin),*result; @@ -2618,22 +4566,40 @@ void resolve_const_item(THD *thd, Item **ref, Item *comp_item) char *tmp_str= sql_strmake(result->ptr(), length); new_item= new Item_string(name, tmp_str, length, result->charset()); } + break; } - else if (res_type == INT_RESULT) + case INT_RESULT: { longlong result=item->val_int(); uint length=item->max_length; bool null_value=item->null_value; new_item= (null_value ? (Item*) new Item_null(name) : (Item*) new Item_int(name, result, length)); + break; } - else + case REAL_RESULT: { // It must REAL_RESULT - double result=item->val(); + double result= item->val_real(); uint length=item->max_length,decimals=item->decimals; bool null_value=item->null_value; new_item= (null_value ? (Item*) new Item_null(name) : (Item*) - new Item_real(name, result, decimals, length)); + new Item_float(name, result, decimals, length)); + break; + } + case DECIMAL_RESULT: + { + my_decimal decimal_value; + my_decimal *result= item->val_decimal(&decimal_value); + uint length= item->max_length, decimals= item->decimals; + bool null_value= item->null_value; + new_item= (null_value ? + (Item*) new Item_null(name) : + (Item*) new Item_decimal(name, result, length, decimals)); + break; + } + case ROW_RESULT: + default: + DBUG_ASSERT(0); } if (new_item) thd->change_item_tree(ref, new_item); @@ -2664,7 +4630,17 @@ bool field_is_equal_to_item(Field *field,Item *item) } if (res_type == INT_RESULT) return 1; // Both where of type int - double result=item->val(); + if (res_type == DECIMAL_RESULT) + { + my_decimal item_buf, *item_val, + field_buf, *field_val; + item_val= item->val_decimal(&item_buf); + if (item->null_value) + return 1; // This must be true + field_val= field->val_decimal(&field_buf); + return !my_decimal_cmp(item_val, field_val); + } + double result= item->val_real(); if (item->null_value) return 1; return result == field->val_real(); @@ -2672,12 +4648,13 @@ bool field_is_equal_to_item(Field *field,Item *item) Item_cache* Item_cache::get_cache(Item_result type) { - switch (type) - { + switch (type) { case INT_RESULT: return new Item_cache_int(); case REAL_RESULT: return new Item_cache_real(); + case DECIMAL_RESULT: + return new Item_cache_decimal(); case STRING_RESULT: return new Item_cache_str(); case ROW_RESULT: @@ -2705,6 +4682,23 @@ void Item_cache_int::store(Item *item) { value= item->val_int_result(); null_value= item->null_value; + unsigned_flag= item->unsigned_flag; +} + + +String *Item_cache_int::val_str(String *str) +{ + DBUG_ASSERT(fixed == 1); + str->set(value, default_charset()); + return str; +} + + +my_decimal *Item_cache_int::val_decimal(my_decimal *decimal_val) +{ + DBUG_ASSERT(fixed == 1); + int2my_decimal(E_DEC_FATAL_ERROR, value, unsigned_flag, decimal_val); + return decimal_val; } @@ -2715,6 +4709,68 @@ void Item_cache_real::store(Item *item) } +longlong Item_cache_real::val_int() +{ + DBUG_ASSERT(fixed == 1); + return (longlong) (value+(value > 0 ? 0.5 : -0.5)); +} + + +String* Item_cache_real::val_str(String *str) +{ + DBUG_ASSERT(fixed == 1); + str->set(value, decimals, default_charset()); + return str; +} + + +my_decimal *Item_cache_real::val_decimal(my_decimal *decimal_val) +{ + DBUG_ASSERT(fixed == 1); + double2my_decimal(E_DEC_FATAL_ERROR, value, decimal_val); + return decimal_val; +} + + +void Item_cache_decimal::store(Item *item) +{ + my_decimal *val= item->val_decimal_result(&decimal_value); + if (!(null_value= item->null_value) && val != &decimal_value) + my_decimal2decimal(val, &decimal_value); +} + +double Item_cache_decimal::val_real() +{ + DBUG_ASSERT(fixed); + double res; + my_decimal2double(E_DEC_FATAL_ERROR, &decimal_value, &res); + return res; +} + +longlong Item_cache_decimal::val_int() +{ + DBUG_ASSERT(fixed); + longlong res; + my_decimal2int(E_DEC_FATAL_ERROR, &decimal_value, unsigned_flag, &res); + return res; +} + +String* Item_cache_decimal::val_str(String *str) +{ + DBUG_ASSERT(fixed); + my_decimal_round(E_DEC_FATAL_ERROR, &decimal_value, decimals, FALSE, + &decimal_value); + my_decimal2string(E_DEC_FATAL_ERROR, &decimal_value, 0, 0, 0, str); + return str; +} + +my_decimal *Item_cache_decimal::val_decimal(my_decimal *val) +{ + DBUG_ASSERT(fixed); + return &decimal_value; +} + + void Item_cache_str::store(Item *item) { value_buff.set(buffer, sizeof(buffer), item->collation.collation); @@ -2736,18 +4792,15 @@ void Item_cache_str::store(Item *item) } } - -double Item_cache_str::val() +double Item_cache_str::val_real() { DBUG_ASSERT(fixed == 1); - int err; + int err_not_used; + char *end_not_used; if (value) - { - char *end_not_used; return my_strntod(value->charset(), (char*) value->ptr(), - value->length(), &end_not_used, &err); - } - return (double)0; + value->length(), &end_not_used, &err_not_used); + return (double) 0; } @@ -2762,6 +4815,16 @@ longlong Item_cache_str::val_int() return (longlong)0; } +my_decimal *Item_cache_str::val_decimal(my_decimal *decimal_val) +{ + DBUG_ASSERT(fixed == 1); + if (value) + string2my_decimal(E_DEC_FATAL_ERROR, value, decimal_val); + else + decimal_val= 0; + return decimal_val; +} + bool Item_cache_row::allocate(uint num) { @@ -2859,6 +4922,9 @@ Item_type_holder::Item_type_holder(THD *thd, Item *item) maybe_null= item->maybe_null; collation.set(item->collation); get_full_info(item); + /* fix variable decimals which always is NOT_FIXED_DEC */ + if (Field::result_merge_type(fld_type) == INT_RESULT) + decimals= 0; } @@ -2917,7 +4983,7 @@ enum_field_types Item_type_holder::get_real_type(Item *item) break; } case FUNC_ITEM: - if (((Item_func *) item)->functype() == Item_func::VAR_VALUE_FUNC) + if (((Item_func *) item)->functype() == Item_func::GUSERVAR_FUNC) { /* There are work around of problem with changing variable type on the @@ -2933,6 +4999,8 @@ enum_field_types Item_type_holder::get_real_type(Item *item) return MYSQL_TYPE_LONGLONG; case REAL_RESULT: return MYSQL_TYPE_DOUBLE; + case DECIMAL_RESULT: + return MYSQL_TYPE_NEWDECIMAL; case ROW_RESULT: default: DBUG_ASSERT(0); @@ -2962,8 +5030,38 @@ enum_field_types Item_type_holder::get_real_type(Item *item) bool Item_type_holder::join_types(THD *thd, Item *item) { - max_length= max(max_length, display_length(item)); + DBUG_ENTER("Item_type_holder::join_types"); + DBUG_PRINT("info:", ("was type %d len %d, dec %d name %s", + fld_type, max_length, decimals, + (name ? name : "<NULL>"))); + DBUG_PRINT("info:", ("in type %d len %d, dec %d", + get_real_type(item), + item->max_length, item->decimals)); fld_type= Field::field_type_merge(fld_type, get_real_type(item)); + { + int item_decimals= item->decimals; + /* fix variable decimals which always is NOT_FIXED_DEC */ + if (Field::result_merge_type(fld_type) == INT_RESULT) + item_decimals= 0; + decimals= max(decimals, item_decimals); + } + if (Field::result_merge_type(fld_type) == DECIMAL_RESULT) + { + int item_length= display_length(item); + int intp1= item_length - min(item->decimals, NOT_FIXED_DEC - 1); + int intp2= max_length - min(decimals, NOT_FIXED_DEC - 1); + /* can't be overflow because it work only for decimals (no strings) */ + int dec_length= max(intp1, intp2) + decimals; + max_length= max(max_length, (uint) max(item_length, dec_length)); + /* + we can't allow decimals to be NOT_FIXED_DEC, to prevent creation + decimal with max precision (see Field_new_decimal constcuctor) + */ + if (decimals >= NOT_FIXED_DEC) + decimals= NOT_FIXED_DEC - 1; + } + else + max_length= max(max_length, display_length(item)); if (Field::result_merge_type(fld_type) == STRING_RESULT) { const char *old_cs, *old_derivation; @@ -2976,13 +5074,14 @@ bool Item_type_holder::join_types(THD *thd, Item *item) item->collation.collation->name, item->collation.derivation_name(), "UNION"); - return TRUE; + DBUG_RETURN(TRUE); } } - decimals= max(decimals, item->decimals); maybe_null|= item->maybe_null; get_full_info(item); - return FALSE; + DBUG_PRINT("info", ("become type: %d len: %u dec: %u", + (int) fld_type, max_length, (uint) decimals)); + DBUG_RETURN(FALSE); } /* @@ -3010,6 +5109,9 @@ uint32 Item_type_holder::display_length(Item *item) case MYSQL_TYPE_DATETIME: case MYSQL_TYPE_YEAR: case MYSQL_TYPE_NEWDATE: + case MYSQL_TYPE_VARCHAR: + case MYSQL_TYPE_BIT: + case MYSQL_TYPE_NEWDECIMAL: case MYSQL_TYPE_ENUM: case MYSQL_TYPE_SET: case MYSQL_TYPE_TINY_BLOB: @@ -3075,10 +5177,6 @@ Field *Item_type_holder::make_field_by_type(TABLE *table) Field::NONE, name, table, get_set_pack_length(enum_set_typelib->count), enum_set_typelib, collation.collation); - case MYSQL_TYPE_VAR_STRING: - table->db_create_options|= HA_OPTION_PACK_RECORD; - return new Field_string(max_length, maybe_null, name, table, - collation.collation); default: break; } @@ -3118,7 +5216,7 @@ void Item_type_holder::get_full_info(Item *item) } -double Item_type_holder::val() +double Item_type_holder::val_real() { DBUG_ASSERT(0); // should never be called return 0.0; @@ -3131,6 +5229,11 @@ longlong Item_type_holder::val_int() return 0; } +my_decimal *Item_type_holder::val_decimal(my_decimal *) +{ + DBUG_ASSERT(0); // should never be called + return 0; +} String *Item_type_holder::val_str(String*) { @@ -3154,5 +5257,6 @@ void Item_result_field::cleanup() template class List<Item>; template class List_iterator<Item>; template class List_iterator_fast<Item>; +template class List_iterator_fast<Item_field>; template class List<List_item>; #endif |