diff options
Diffstat (limited to 'sql/item.cc')
-rw-r--r-- | sql/item.cc | 2145 |
1 files changed, 1954 insertions, 191 deletions
diff --git a/sql/item.cc b/sql/item.cc index 739b5385b55..658f5c42a43 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -23,6 +23,12 @@ #include <m_ctype.h> #include "my_dir.h" +static void mark_as_dependent(THD *thd, + SELECT_LEX *last, SELECT_LEX *current, + Item_ident *item); + +const String my_null_string("NULL", 4, default_charset_info); + /***************************************************************************** ** Item functions *****************************************************************************/ @@ -34,48 +40,179 @@ void item_init(void) item_user_lock_init(); } -Item::Item() +Item::Item(): + fixed(0) +{ + marker= 0; + maybe_null=null_value=with_sum_func=unsigned_flag=0; + collation.set(default_charset(), DERIVATION_COERCIBLE); + name= 0; + decimals= 0; max_length= 0; + + /* Put item in free list so that we can free all items at end */ + THD *thd= current_thd; + next= thd->free_list; + thd->free_list= this; + /* + Item constructor can be called during execution other then SQL_COM + command => we should check thd->lex->current_select on zero (thd->lex + can be uninitialised) + */ + if (thd->lex->current_select) + { + SELECT_LEX_NODE::enum_parsing_place place= + thd->lex->current_select->parsing_place; + if (place == SELECT_LEX_NODE::SELECT_LIST || + place == SELECT_LEX_NODE::IN_HAVING) + thd->lex->current_select->select_n_having_items++; + } +} + +/* + Constructor used by Item_field, Item_ref & agregate (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), + max_length(item->max_length), + marker(item->marker), + decimals(item->decimals), + maybe_null(item->maybe_null), + null_value(item->null_value), + unsigned_flag(item->unsigned_flag), + with_sum_func(item->with_sum_func), + fixed(item->fixed), + collation(item->collation) +{ + next= thd->free_list; // Put in free list + thd->free_list= this; +} + + +void Item::print_item_w_name(String *str) { - marker=0; - binary=maybe_null=null_value=with_sum_func=unsigned_flag=0; - name=0; - decimals=0; max_length=0; - next=current_thd->free_list; // Put in free list - current_thd->free_list=this; + print(str); + if (name) + { + str->append(" AS `", 5); + str->append(name); + str->append('`'); + } +} + + +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), changed_during_fix_field(0), + db_name(db_name_par), table_name(table_name_par), + field_name(field_name_par), 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) +Item_ident::Item_ident(THD *thd, Item_ident *item) + :Item(thd, item), + orig_db_name(item->orig_db_name), + orig_table_name(item->orig_table_name), + orig_field_name(item->orig_field_name), + changed_during_fix_field(0), + db_name(item->db_name), + table_name(item->table_name), + field_name(item->field_name), + cached_field_index(item->cached_field_index), + cached_table(item->cached_table), + depended_from(item->depended_from) +{} + +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)); + Item::cleanup(); + if (changed_during_fix_field) + { + *changed_during_fix_field= this; + changed_during_fix_field= 0; + } + db_name= orig_db_name; + table_name= orig_table_name; + field_name= orig_field_name; + DBUG_VOID_RETURN; +} + +bool Item_ident::remove_dependence_processor(byte * arg) +{ + DBUG_ENTER("Item_ident::remove_dependence_processor"); + if (depended_from == (st_select_lex *) arg) + depended_from= 0; + DBUG_RETURN(0); +} + + +bool Item::check_cols(uint c) +{ + if (c != 1) + { + my_error(ER_OPERAND_COLUMNS, MYF(0), c); + return 1; + } + return 0; } -void Item::set_name(char *str,uint length) + +void Item::set_name(const char *str, uint length, CHARSET_INFO *cs) { if (!length) - name=str; // Used by AS - else { - while (length && !isgraph(*str)) - { // Fix problem with yacc - length--; - str++; - } - name=sql_strmake(str,min(length,MAX_FIELD_WIDTH)); + /* Empty string, used by AS or internal function like last_insert_id() */ + name= (char*) str; + return; + } + while (length && !my_isgraph(cs,*str)) + { // Fix problem with yacc + length--; + str++; + } + if (!my_charset_same(cs, system_charset_info)) + { + uint32 res_length; + name= sql_strmake_with_convert(str, length, cs, + MAX_ALIAS_NAME, system_charset_info, + &res_length); } + else + name=sql_strmake(str, min(length,MAX_ALIAS_NAME)); } + /* - This function is only called when comparing items in the WHERE clause + This function is called when: + - Comparing items in the WHERE clause (when doing where optimization) + - When trying to find an ORDER BY/GROUP BY item in the SELECT part */ bool Item::eq(const Item *item, bool binary_cmp) const { return type() == item->type() && name && item->name && - !my_strcasecmp(name,item->name); + !my_strcasecmp(system_charset_info,name,item->name); } + bool Item_string::eq(const Item *item, bool binary_cmp) const { if (type() == item->type()) { if (binary_cmp) return !stringcmp(&str_value, &item->str_value); - return !sortcmp(&str_value, &item->str_value); + return !sortcmp(&str_value, &item->str_value, collation.collation); } return 0; } @@ -86,12 +223,13 @@ bool Item_string::eq(const Item *item, bool binary_cmp) const As a extra convenience the time structure is reset on error! */ -bool Item::get_date(TIME *ltime,bool fuzzydate) +bool Item::get_date(TIME *ltime,uint fuzzydate) { char buff[40]; - String tmp(buff,sizeof(buff)),*res; + String tmp(buff,sizeof(buff), &my_charset_bin),*res; if (!(res=val_str(&tmp)) || - str_to_TIME(res->ptr(),res->length(),ltime,fuzzydate) == TIMESTAMP_NONE) + str_to_datetime_with_warn(res->ptr(), res->length(), + ltime, fuzzydate) <= MYSQL_TIMESTAMP_ERROR) { bzero((char*) ltime,sizeof(*ltime)); return 1; @@ -107,9 +245,9 @@ bool Item::get_date(TIME *ltime,bool fuzzydate) bool Item::get_time(TIME *ltime) { char buff[40]; - String tmp(buff,sizeof(buff)),*res; + String tmp(buff,sizeof(buff),&my_charset_bin),*res; if (!(res=val_str(&tmp)) || - str_to_time(res->ptr(),res->length(),ltime)) + str_to_time_with_warn(res->ptr(), res->length(), ltime)) { bzero((char*) ltime,sizeof(*ltime)); return 1; @@ -117,19 +255,95 @@ bool Item::get_time(TIME *ltime) return 0; } -Item_ident::Item_ident(const char *db_name_par,const char *table_name_par, - const char *field_name_par) - :db_name(db_name_par),table_name(table_name_par),field_name(field_name_par) +CHARSET_INFO *Item::default_charset() { - name = (char*) field_name_par; + return current_thd->variables.collation_connection; } +bool DTCollation::aggregate(DTCollation &dt) +{ + if (!my_charset_same(collation, dt.collation)) + { + /* + We do allow to use binary strings (like BLOBS) + together with character strings. + Binaries have more precedance than a character + string of the same derivation. + */ + if (collation == &my_charset_bin) + { + if (derivation <= dt.derivation) + ; // Do nothing + else + set(dt); + } + else if (dt.collation == &my_charset_bin) + { + if (dt.derivation <= derivation) + set(dt); + else + ; // Do nothing + } + else + { + set(0, DERIVATION_NONE); + return 1; + } + } + else if (derivation < dt.derivation) + { + // Do nothing + } + else if (dt.derivation < derivation) + { + set(dt); + } + else + { + if (collation == dt.collation) + { + // Do nothing + } + else + { + if (derivation == DERIVATION_EXPLICIT) + { + set(0, DERIVATION_NONE); + return 1; + } + CHARSET_INFO *bin= get_charset_by_csname(collation->csname, + MY_CS_BINSORT,MYF(0)); + set(bin, DERIVATION_NONE); + } + } + return 0; +} -Item_field::Item_field(Field *f) :Item_ident(NullS,f->table_name,f->field_name) +Item_field::Item_field(Field *f) + :Item_ident(NullS, f->table_name, f->field_name) { set_field(f); + collation.set(DERIVATION_IMPLICIT); + fixed= 1; } +Item_field::Item_field(THD *thd, Field *f) + :Item_ident(NullS, thd->strdup(f->table_name), + thd->strdup(f->field_name)) +{ + set_field(f); + collation.set(DERIVATION_IMPLICIT); + fixed= 1; +} + +// Constructor need to process subselect with temporary tables (see Item) +Item_field::Item_field(THD *thd, Item_field *item) + :Item_ident(thd, item), + field(item->field), + result_field(item->result_field) +{ + collation.set(DERIVATION_IMPLICIT); +} void Item_field::set_field(Field *field_par) { @@ -139,16 +353,17 @@ void Item_field::set_field(Field *field_par) decimals= field->decimals(); table_name=field_par->table_name; field_name=field_par->field_name; - binary=field_par->binary(); + db_name=field_par->table->table_cache_key; unsigned_flag=test(field_par->flags & UNSIGNED_FLAG); + collation.set(field_par->charset(), DERIVATION_IMPLICIT); } const char *Item_ident::full_name() const { char *tmp; - if (!table_name) + if (!table_name || !field_name) return field_name ? field_name : name ? name : "tmp_field"; - if (db_name) + if (db_name && db_name[0]) { tmp=(char*) sql_alloc((uint) strlen(db_name)+(uint) strlen(table_name)+ (uint) strlen(field_name)+3); @@ -156,9 +371,14 @@ const char *Item_ident::full_name() const } else { - tmp=(char*) sql_alloc((uint) strlen(table_name)+ - (uint) strlen(field_name)+2); - strxmov(tmp,table_name,".",field_name,NullS); + if (table_name[0]) + { + tmp= (char*) sql_alloc((uint) strlen(table_name) + + (uint) strlen(field_name) + 2); + strxmov(tmp, table_name, ".", field_name, NullS); + } + else + tmp= (char*) field_name; } return tmp; } @@ -166,13 +386,16 @@ const char *Item_ident::full_name() const /* ARGSUSED */ String *Item_field::val_str(String *str) { + DBUG_ASSERT(fixed == 1); if ((null_value=field->is_null())) return 0; + str->set_charset(str_value.charset()); return field->val_str(str,&str_value); } double Item_field::val() { + DBUG_ASSERT(fixed == 1); if ((null_value=field->is_null())) return 0.0; return field->val_real(); @@ -180,6 +403,7 @@ double Item_field::val() longlong Item_field::val_int() { + DBUG_ASSERT(fixed == 1); if ((null_value=field->is_null())) return 0; return field->val_int(); @@ -190,10 +414,11 @@ String *Item_field::str_result(String *str) { if ((null_value=result_field->is_null())) return 0; + str->set_charset(str_value.charset()); return result_field->val_str(str,&str_value); } -bool Item_field::get_date(TIME *ltime,bool fuzzydate) +bool Item_field::get_date(TIME *ltime,uint fuzzydate) { if ((null_value=field->is_null()) || field->get_date(ltime,fuzzydate)) { @@ -203,7 +428,7 @@ bool Item_field::get_date(TIME *ltime,bool fuzzydate) return 0; } -bool Item_field::get_date_result(TIME *ltime,bool fuzzydate) +bool Item_field::get_date_result(TIME *ltime,uint fuzzydate) { if ((null_value=result_field->is_null()) || result_field->get_date(ltime,fuzzydate)) @@ -238,72 +463,622 @@ longlong Item_field::val_int_result() return result_field->val_int(); } + bool Item_field::eq(const Item *item, bool binary_cmp) const { - return item->type() == FIELD_ITEM && ((Item_field*) item)->field == field; + if (item->type() != FIELD_ITEM) + return 0; + + Item_field *item_field= (Item_field*) item; + if (item_field->field) + return item_field->field == field; + /* + We may come here when we are trying to find a function in a GROUP BY + clause from the select list. + In this case the '100 % correct' way to do this would be to first + run fix_fields() on the GROUP BY item and then retry this function, but + I think it's better to relax the checking a bit as we will in + most cases do the correct thing by just checking the field name. + (In cases where we would choose wrong we would have to generate a + ER_NON_UNIQ_ERROR). + */ + return (!my_strcasecmp(system_charset_info, item_field->name, + field_name) && + (!item_field->table_name || + (!my_strcasecmp(table_alias_charset, item_field->table_name, + table_name) && + (!item_field->db_name || + (item_field->db_name && !strcmp(item_field->db_name, + db_name)))))); } + table_map Item_field::used_tables() const { if (field->table->const_table) return 0; // const item - return field->table->map; + return (depended_from ? OUTER_REF_TABLE_BIT : field->table->map); +} + + +Item *Item_field::get_tmp_table_item(THD *thd) +{ + Item_field *new_item= new Item_field(thd, this); + if (new_item) + new_item->field= new_item->result_field; + return new_item; +} + + +/* + Create an item from a string we KNOW points to a valid longlong/ulonglong + end \0 terminated number string +*/ + +Item_int::Item_int(const char *str_arg, uint length) +{ + char *end_ptr= (char*) str_arg + length; + int error; + value= my_strtoll10(str_arg, &end_ptr, &error); + max_length= (uint) (end_ptr - str_arg); + name= (char*) str_arg; + fixed= 1; } String *Item_int::val_str(String *str) { - str->set(value); + // following assert is redundant, because fixed=1 assigned in constructor + DBUG_ASSERT(fixed == 1); + str->set(value, &my_charset_bin); return str; } void Item_int::print(String *str) { - if (!name) - { - str_value.set(value); - name=str_value.c_ptr(); - } - str->append(name); + // my_charset_bin is good enough for numbers + str_value.set(value, &my_charset_bin); + str->append(str_value); } + +Item_uint::Item_uint(const char *str_arg, uint length): + Item_int(str_arg, length) +{ + unsigned_flag= 1; +} + + String *Item_uint::val_str(String *str) { - str->set((ulonglong) value); + // following assert is redundant, because fixed=1 assigned in constructor + DBUG_ASSERT(fixed == 1); + str->set((ulonglong) value, &my_charset_bin); return str; } + void Item_uint::print(String *str) { - if (!name) - { - str_value.set((ulonglong) value); - name=str_value.c_ptr(); - } - str->append(name); + // latin1 is good enough for numbers + str_value.set((ulonglong) value, default_charset()); + str->append(str_value); } String *Item_real::val_str(String *str) { - str->set(value,decimals); + // following assert is redundant, because fixed=1 assigned in constructor + DBUG_ASSERT(fixed == 1); + str->set(value,decimals,&my_charset_bin); return str; } + void Item_string::print(String *str) { + str->append('_'); + str->append(collation.collation->csname); str->append('\''); - str->append(full_name()); + str_value.print(str); str->append('\''); } bool Item_null::eq(const Item *item, bool binary_cmp) const { return item->type() == type(); } -double Item_null::val() { null_value=1; return 0.0; } -longlong Item_null::val_int() { null_value=1; return 0; } +double Item_null::val() +{ + // following assert is redundant, because fixed=1 assigned in constructor + DBUG_ASSERT(fixed == 1); + null_value=1; + return 0.0; +} +longlong Item_null::val_int() +{ + // following assert is redundant, because fixed=1 assigned in constructor + DBUG_ASSERT(fixed == 1); + null_value=1; + return 0; +} /* ARGSUSED */ String *Item_null::val_str(String *str) -{ null_value=1; return 0;} +{ + // following assert is redundant, because fixed=1 assigned in constructor + DBUG_ASSERT(fixed == 1); + null_value=1; + return 0; +} + + +/*********************** Item_param related ******************************/ + +/* + Default function of Item_param::set_param_func, so in case + of malformed packet the server won't SIGSEGV +*/ + +static void +default_set_param_func(Item_param *param, + uchar **pos __attribute__((unused)), + ulong len __attribute__((unused))) +{ + 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), + pos_in_query(pos_in_query_arg), + set_param_func(default_set_param_func) +{ + name= (char*) "?"; + /* + Since we can't say whenever this item can be NULL or cannot be NULL + before mysql_stmt_execute(), so we assuming that it can be NULL until + value is set. + */ + 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 + placeholder value: user can submit NULL for any placeholder type, and + string length can be different in each execution. + */ + max_length= 0; + decimals= 0; + state= NULL_VALUE; + DBUG_VOID_RETURN; +} + +void Item_param::set_int(longlong i, uint32 max_length_arg) +{ + DBUG_ENTER("Item_param::set_int"); + value.integer= (longlong) i; + state= INT_VALUE; + max_length= max_length_arg; + decimals= 0; + maybe_null= 0; + DBUG_VOID_RETURN; +} + +void Item_param::set_double(double d) +{ + DBUG_ENTER("Item_param::set_double"); + value.real= d; + state= REAL_VALUE; + max_length= DBL_DIG + 8; + decimals= NOT_FIXED_DEC; + maybe_null= 0; + DBUG_VOID_RETURN; +} + + +void Item_param::set_time(TIME *tm, timestamp_type type, uint32 max_length_arg) +{ + DBUG_ENTER("Item_param::set_time"); + + value.time= *tm; + value.time.time_type= type; + + state= TIME_VALUE; + maybe_null= 0; + max_length= max_length_arg; + decimals= 0; + DBUG_VOID_RETURN; +} + + +bool Item_param::set_str(const char *str, ulong length) +{ + DBUG_ENTER("Item_param::set_str"); + /* + Assign string with no conversion: data is converted only after it's + been written to the binary log. + */ + if (str_value.copy(str, length, &my_charset_bin, &my_charset_bin)) + DBUG_RETURN(TRUE); + state= STRING_VALUE; + 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 */ + DBUG_RETURN(FALSE); +} + + +bool Item_param::set_longdata(const char *str, ulong length) +{ + DBUG_ENTER("Item_param::set_longdata"); + + /* + If client character set is multibyte, end of long data packet + may hit at the middle of a multibyte character. Additionally, + if binary log is open we must write long data value to the + binary log in character set of client. This is why we can't + convert long data to connection character set as it comes + (here), and first have to concatenate all pieces together, + write query to the binary log and only then perform conversion. + */ + if (str_value.append(str, length, &my_charset_bin)) + DBUG_RETURN(TRUE); + state= LONG_DATA_VALUE; + maybe_null= 0; + + DBUG_RETURN(FALSE); +} + + +/* + Set parameter value from user variable value. + + SYNOPSIS + set_from_user_var + thd Current thread + entry User variable structure (NULL means use NULL value) + + RETURN + 0 OK + 1 Out of memort +*/ + +bool Item_param::set_from_user_var(THD *thd, const user_var_entry *entry) +{ + DBUG_ENTER("Item_param::set_from_user_var"); + if (entry && entry->value) + { + item_result_type= entry->type; + switch (entry->type) { + case REAL_RESULT: + set_double(*(double*)entry->value); + item_type= Item::REAL_ITEM; + item_result_type= REAL_RESULT; + break; + case INT_RESULT: + set_int(*(longlong*)entry->value, 21); + item_type= Item::INT_ITEM; + item_result_type= INT_RESULT; + break; + case STRING_RESULT: + { + CHARSET_INFO *fromcs= entry->collation.collation; + CHARSET_INFO *tocs= thd->variables.collation_connection; + uint32 dummy_offset; + + value.cs_info.character_set_client= fromcs; + /* + Setup source and destination character sets so that they + are different only if conversion is necessary: this will + make later checks easier. + */ + value.cs_info.final_character_set_of_str_value= + String::needs_conversion(0, fromcs, tocs, &dummy_offset) ? + tocs : fromcs; + /* + Exact value of max_length is not known unless data is converted to + charset of connection, so we have to set it later. + */ + item_type= Item::STRING_ITEM; + item_result_type= STRING_RESULT; + + if (set_str((const char *)entry->value, entry->length)) + DBUG_RETURN(1); + break; + } + default: + DBUG_ASSERT(0); + set_null(); + } + } + else + set_null(); + + DBUG_RETURN(0); +} + +/* + Resets parameter after execution. + + SYNOPSIS + Item_param::reset() + + NOTES + We clear null_value here instead of setting it in set_* methods, + because we want more easily handle case for long data. +*/ + +void Item_param::reset() +{ + /* Shrink string buffer if it's bigger than max possible CHAR column */ + if (str_value.alloced_length() > MAX_CHAR_WIDTH) + str_value.free(); + else + str_value.length(0); + str_value_ptr.length(0); + /* + We must prevent all charset conversions untill data has been written + to the binary log. + */ + str_value.set_charset(&my_charset_bin); + state= NO_VALUE; + maybe_null= 1; + null_value= 0; + /* + Don't reset item_type to PARAM_ITEM: it's only needed to guard + us from item optimizations at prepare stage, when item doesn't yet + contain a literal of some kind. + In all other cases when this object is accessed its value is + set (this assumption is guarded by 'state' and + DBUG_ASSERTS(state != NO_VALUE) in all Item_param::get_* + methods). + */ +} + + +int Item_param::save_in_field(Field *field, bool no_conversions) +{ + field->set_notnull(); + + switch (state) { + case INT_VALUE: + return field->store(value.integer); + case REAL_VALUE: + return field->store(value.real); + case TIME_VALUE: + field->store_time(&value.time, value.time.time_type); + return 0; + case STRING_VALUE: + case LONG_DATA_VALUE: + return field->store(str_value.ptr(), str_value.length(), + str_value.charset()); + case NULL_VALUE: + return set_field_to_null(field); + case NO_VALUE: + default: + DBUG_ASSERT(0); + } + return 1; +} + + +bool Item_param::get_time(TIME *res) +{ + if (state == TIME_VALUE) + { + *res= value.time; + return 0; + } + /* + If parameter value isn't supplied assertion will fire in val_str() + which is called from Item::get_time(). + */ + return Item::get_time(res); +} + + +bool Item_param::get_date(TIME *res, uint fuzzydate) +{ + if (state == TIME_VALUE) + { + *res= value.time; + return 0; + } + return Item::get_date(res, fuzzydate); +} + + +double Item_param::val() +{ + switch (state) { + case REAL_VALUE: + return value.real; + case INT_VALUE: + return (double) value.integer; + case STRING_VALUE: + case LONG_DATA_VALUE: + { + int dummy_err; + return my_strntod(str_value.charset(), (char*) str_value.ptr(), + str_value.length(), (char**) 0, &dummy_err); + } + case TIME_VALUE: + /* + This works for example when user says SELECT ?+0.0 and supplies + time value for the placeholder. + */ + return (double) TIME_to_ulonglong(&value.time); + case NULL_VALUE: + return 0.0; + default: + DBUG_ASSERT(0); + } + return 0.0; +} + + +longlong Item_param::val_int() +{ + switch (state) { + case REAL_VALUE: + return (longlong) (value.real + (value.real > 0 ? 0.5 : -0.5)); + case INT_VALUE: + return value.integer; + case STRING_VALUE: + case LONG_DATA_VALUE: + { + int dummy_err; + return my_strntoll(str_value.charset(), str_value.ptr(), + str_value.length(), 10, (char**) 0, &dummy_err); + } + case TIME_VALUE: + return (longlong) TIME_to_ulonglong(&value.time); + case NULL_VALUE: + return 0; + default: + DBUG_ASSERT(0); + } + return 0; +} + + +String *Item_param::val_str(String* str) +{ + switch (state) { + case STRING_VALUE: + case LONG_DATA_VALUE: + return &str_value_ptr; + case REAL_VALUE: + str->set(value.real, NOT_FIXED_DEC, &my_charset_bin); + return str; + case INT_VALUE: + str->set(value.integer, &my_charset_bin); + return str; + case TIME_VALUE: + { + if (str->reserve(MAX_DATE_REP_LENGTH)) + break; + TIME_to_string(&value.time, str); + return str; + } + case NULL_VALUE: + return NULL; + default: + DBUG_ASSERT(0); + } + return str; +} + +/* + Return Param item values in string format, for generating the dynamic + query used in update/binary logs + TODO: change interface and implementation to fill log data in place + and avoid one more memcpy/alloc between str and log string. +*/ + +const String *Item_param::query_val_str(String* str) const +{ + switch (state) { + case INT_VALUE: + str->set(value.integer, &my_charset_bin); + break; + case REAL_VALUE: + str->set(value.real, NOT_FIXED_DEC, &my_charset_bin); + break; + case TIME_VALUE: + { + char *buf, *ptr; + String tmp; + str->length(0); + /* + TODO: in case of error we need to notify replication + that binary log contains wrong statement + */ + if (str->reserve(MAX_DATE_REP_LENGTH+3)) + break; + + /* Create date string inplace */ + buf= str->c_ptr_quick(); + ptr= buf; + *ptr++= '\''; + tmp.set(ptr, MAX_DATE_REP_LENGTH, &my_charset_bin); + tmp.length(0); + TIME_to_string(&value.time, &tmp); + + ptr+= tmp.length(); + *ptr++= '\''; + str->length((uint32) (ptr - buf)); + break; + } + case STRING_VALUE: + case LONG_DATA_VALUE: + { + char *buf, *ptr; + str->length(0); + if (str->reserve(str_value.length()*2+3)) + break; + + buf= str->c_ptr_quick(); + ptr= buf; + *ptr++= '\''; + ptr+= escape_string_for_mysql(str_value.charset(), ptr, + str_value.ptr(), str_value.length()); + *ptr++= '\''; + str->length(ptr - buf); + break; + } + case NULL_VALUE: + return &my_null_string; + default: + DBUG_ASSERT(0); + } + return str; +} + + +/* + Convert string from client character set to the character set of + connection. +*/ + +bool Item_param::convert_str_value(THD *thd) +{ + bool rc= FALSE; + if (state == STRING_VALUE || state == LONG_DATA_VALUE) + { + /* + Check is so simple because all charsets were set up properly + in setup_one_conversion_function, where typecode of + placeholder was also taken into account: the variables are different + here only if conversion is really necessary. + */ + if (value.cs_info.final_character_set_of_str_value != + value.cs_info.character_set_client) + { + rc= thd->convert_string(&str_value, + value.cs_info.character_set_client, + value.cs_info.final_character_set_of_str_value); + } + max_length= str_value.length(); + decimals= 0; + /* + str_value_ptr is returned from val_str(). It must be not alloced + to prevent it's modification by val_str() invoker. + */ + str_value_ptr.set(str_value.ptr(), str_value.length(), + str_value.charset()); + } + return rc; +} + +/* End of Item_param related */ void Item_copy_string::copy() @@ -317,57 +1092,285 @@ void Item_copy_string::copy() /* ARGSUSED */ String *Item_copy_string::val_str(String *str) { + // Item_copy_string is used without fix_fields call if (null_value) return (String*) 0; return &str_value; } -bool Item_copy_string::save_in_field(Field *field, bool no_conversions) + +int Item_copy_string::save_in_field(Field *field, bool no_conversions) { if (null_value) return set_field_to_null(field); field->set_notnull(); - field->store(str_value.ptr(), str_value.length()); - return 0; + return field->store(str_value.ptr(),str_value.length(), + collation.collation); } /* -** Functions to convert item to field (for send_fields) + Functions to convert item to field (for send_fields) */ /* ARGSUSED */ bool Item::fix_fields(THD *thd, - struct st_table_list *list) + struct st_table_list *list, + Item ** ref) { + + // We do not check fields which are fixed during construction + DBUG_ASSERT(fixed == 0 || basic_const_item()); + fixed= 1; return 0; } -bool Item_field::fix_fields(THD *thd,TABLE_LIST *tables) +double Item_ref_null_helper::val() +{ + DBUG_ASSERT(fixed == 1); + double tmp= (*ref)->val_result(); + owner->was_null|= null_value= (*ref)->null_value; + return tmp; +} + + +longlong Item_ref_null_helper::val_int() { + DBUG_ASSERT(fixed == 1); + longlong tmp= (*ref)->val_int_result(); + owner->was_null|= null_value= (*ref)->null_value; + return tmp; +} + + +String* Item_ref_null_helper::val_str(String* s) +{ + DBUG_ASSERT(fixed == 1); + String* tmp= (*ref)->str_result(s); + owner->was_null|= null_value= (*ref)->null_value; + return tmp; +} + + +bool Item_ref_null_helper::get_date(TIME *ltime, uint fuzzydate) +{ + return (owner->was_null|= null_value= (*ref)->get_date(ltime, fuzzydate)); +} + + +/* + Mark item and SELECT_LEXs as dependent if it is not outer resolving + + SYNOPSIS + mark_as_dependent() + thd - thread handler + last - select from which current item depend + current - current select + item - item which should be marked +*/ + +static void mark_as_dependent(THD *thd, SELECT_LEX *last, SELECT_LEX *current, + Item_ident *item) +{ + // store pointer on SELECT_LEX from wich item is dependent + 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, + current->select_number, last->select_number); + push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, + ER_WARN_FIELD_RESOLVED, warn_buff); + } +} + + +bool Item_field::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref) +{ + DBUG_ASSERT(fixed == 0); if (!field) // If field is not checked { - Field *tmp; - if (!(tmp=find_field_in_tables(thd,this,tables))) - return 1; + 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)) == + 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; + // 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) + { + 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()) + { + upward_lookup= 1; + table_list= (last= 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; + } + + Item_subselect *prev_subselect_item= prev_unit->item; + if ((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)) != + (Item **) not_found_item) + { + 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(); + } + 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 == + 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) + { + 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); + } + else + { + // Call to report error + find_field_in_tables(thd, this, tables, &where, 1); + } + return -1; + } + else if (refer != (Item **)not_found_item) + { + if (!(*refer)->fixed) + { + my_error(ER_ILLEGAL_REFERENCE, MYF(0), name, + "forward reference in item list"); + return -1; + } + + Item_ref *rf; + *ref= rf= new Item_ref(last->ref_pointer_array + counter, + ref, + (char *)table_name, + (char *)field_name); + register_item_tree_changing(ref); + if (!rf) + return 1; + /* + 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; + + mark_as_dependent(thd, last, cursel, rf); + return 0; + } + else + { + mark_as_dependent(thd, last, cursel, this); + if (last->having_fix_field) + { + Item_ref *rf; + *ref= rf= new Item_ref(ref, *ref, + (where->db[0]?where->db:0), + (char *)where->alias, + (char *)field_name); + if (!rf) + return 1; + /* + 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); + } + } + } + else if (!tmp) + return -1; + set_field(tmp); } - else if (thd && thd->set_query_id && field->query_id != thd->query_id) + else if (thd->set_query_id && field->query_id != thd->query_id) { /* We only come here in unions */ TABLE *table=field->table; field->query_id=thd->query_id; table->used_fields++; - table->used_keys&=field->part_of_key; + table->used_keys.intersect(field->part_of_key); } + fixed= 1; return 0; } +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. + I.e. we can drop 'field'. + */ + field= result_field= 0; + DBUG_VOID_RETURN; +} void Item::init_make_field(Send_field *tmp_field, enum enum_field_types field_type) { - tmp_field->table_name=(char*) ""; - tmp_field->col_name=name; + char *empty_name= (char*) ""; + tmp_field->db_name= empty_name; + tmp_field->org_table_name= empty_name; + tmp_field->org_col_name= empty_name; + tmp_field->table_name= empty_name; + tmp_field->col_name= name; + tmp_field->charsetnr= collation.collation->number; tmp_field->flags=maybe_null ? 0 : NOT_NULL_FLAG; tmp_field->type=field_type; tmp_field->length=max_length; @@ -376,77 +1379,118 @@ void Item::init_make_field(Send_field *tmp_field, tmp_field->flags |= UNSIGNED_FLAG; } -/* ARGSUSED */ -void Item_field::make_field(Send_field *tmp_field) +void Item::make_field(Send_field *tmp_field) { - field->make_field(tmp_field); - if (name) - tmp_field->col_name=name; // Use user supplied name + init_make_field(tmp_field, field_type()); } -void Item_int::make_field(Send_field *tmp_field) -{ - init_make_field(tmp_field,FIELD_TYPE_LONGLONG); -} - -void Item_uint::make_field(Send_field *tmp_field) -{ - init_make_field(tmp_field,FIELD_TYPE_LONGLONG); - tmp_field->flags|= UNSIGNED_FLAG; - unsigned_flag=1; -} - -void Item_real::make_field(Send_field *tmp_field) -{ - init_make_field(tmp_field,FIELD_TYPE_DOUBLE); -} - -void Item_string::make_field(Send_field *tmp_field) -{ - init_make_field(tmp_field,FIELD_TYPE_STRING); -} void Item_empty_string::make_field(Send_field *tmp_field) { init_make_field(tmp_field,FIELD_TYPE_VAR_STRING); } -void Item_datetime::make_field(Send_field *tmp_field) + +enum_field_types Item::field_type() const { - init_make_field(tmp_field,FIELD_TYPE_DATETIME); + return ((result_type() == STRING_RESULT) ? FIELD_TYPE_VAR_STRING : + (result_type() == INT_RESULT) ? FIELD_TYPE_LONGLONG : + FIELD_TYPE_DOUBLE); } -void Item_null::make_field(Send_field *tmp_field) +Field *Item::tmp_table_field_from_field_type(TABLE *table) { - init_make_field(tmp_field,FIELD_TYPE_NULL); - tmp_field->length=4; -} + /* + The field functions defines a field to be not null if null_ptr is not 0 + */ + uchar *null_ptr= maybe_null ? (uchar*) "" : 0; + switch (field_type()) { + 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_TINY: + return new Field_tiny((char*) 0, max_length, null_ptr, 0, Field::NONE, + name, table, 0, unsigned_flag); + case MYSQL_TYPE_SHORT: + return new Field_short((char*) 0, max_length, null_ptr, 0, Field::NONE, + name, table, 0, unsigned_flag); + case MYSQL_TYPE_LONG: + return new Field_long((char*) 0, max_length, null_ptr, 0, Field::NONE, + name, table, 0, unsigned_flag); +#ifdef HAVE_LONG_LONG + case MYSQL_TYPE_LONGLONG: + return new Field_longlong((char*) 0, max_length, null_ptr, 0, Field::NONE, + name, table, 0, unsigned_flag); +#endif + case MYSQL_TYPE_FLOAT: + return new Field_float((char*) 0, max_length, null_ptr, 0, Field::NONE, + name, table, decimals, 0, unsigned_flag); + case MYSQL_TYPE_DOUBLE: + return new Field_double((char*) 0, max_length, null_ptr, 0, Field::NONE, + name, table, decimals, 0, unsigned_flag); + case MYSQL_TYPE_NULL: + return new Field_null((char*) 0, max_length, Field::NONE, + name, table, &my_charset_bin); + case MYSQL_TYPE_NEWDATE: + case MYSQL_TYPE_INT24: + return new Field_medium((char*) 0, max_length, null_ptr, 0, Field::NONE, + name, table, 0, unsigned_flag); + case MYSQL_TYPE_DATE: + return new Field_date(maybe_null, name, table, &my_charset_bin); + case MYSQL_TYPE_TIME: + return new Field_time(maybe_null, name, table, &my_charset_bin); + case MYSQL_TYPE_TIMESTAMP: + case MYSQL_TYPE_DATETIME: + return new Field_datetime(maybe_null, name, table, &my_charset_bin); + case MYSQL_TYPE_YEAR: + return new Field_year((char*) 0, max_length, null_ptr, 0, Field::NONE, + name, table); + default: + /* This case should never be choosen */ + 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_TINY_BLOB: + case MYSQL_TYPE_MEDIUM_BLOB: + case MYSQL_TYPE_LONG_BLOB: + case MYSQL_TYPE_BLOB: + case MYSQL_TYPE_GEOMETRY: + break; // Blob handled outside of case + } -void Item_func::make_field(Send_field *tmp_field) -{ - init_make_field(tmp_field, ((result_type() == STRING_RESULT) ? - FIELD_TYPE_VAR_STRING : - (result_type() == INT_RESULT) ? - FIELD_TYPE_LONGLONG : FIELD_TYPE_DOUBLE)); + /* 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); } -void Item_avg_field::make_field(Send_field *tmp_field) -{ - init_make_field(tmp_field,FIELD_TYPE_DOUBLE); -} -void Item_std_field::make_field(Send_field *tmp_field) +/* ARGSUSED */ +void Item_field::make_field(Send_field *tmp_field) { - init_make_field(tmp_field,FIELD_TYPE_DOUBLE); + field->make_field(tmp_field); + DBUG_ASSERT(tmp_field->table_name); + if (name) + tmp_field->col_name=name; // Use user supplied name } + /* -** Set a field:s value from a item + Set a field:s value from a item */ - void Item_field::save_org_in_field(Field *to) { if (field->is_null()) @@ -462,7 +1506,7 @@ void Item_field::save_org_in_field(Field *to) } } -bool Item_field::save_in_field(Field *to, bool no_conversions) +int Item_field::save_in_field(Field *to, bool no_conversions) { if (result_field->is_null()) { @@ -478,6 +1522,7 @@ bool Item_field::save_in_field(Field *to, bool no_conversions) return 0; } + /* Store null in field @@ -494,7 +1539,7 @@ bool Item_field::save_in_field(Field *to, bool no_conversions) 1 Field doesn't support NULL values and can't handle 'field = NULL' */ -bool Item_null::save_in_field(Field *field, bool no_conversions) +int Item_null::save_in_field(Field *field, bool no_conversions) { return set_field_to_null_with_conversions(field, no_conversions); } @@ -512,30 +1557,32 @@ bool Item_null::save_in_field(Field *field, bool no_conversions) 1 Field doesn't support NULL values */ -bool Item_null::save_safe_in_field(Field *field) +int Item_null::save_safe_in_field(Field *field) { return set_field_to_null(field); } -bool Item::save_in_field(Field *field, bool no_conversions) +int Item::save_in_field(Field *field, bool no_conversions) { + int error; if (result_type() == STRING_RESULT || result_type() == REAL_RESULT && field->result_type() == STRING_RESULT) { String *result; + CHARSET_INFO *cs= collation.collation; char buff[MAX_FIELD_WIDTH]; // Alloc buffer for small columns - str_value.set_quick(buff,sizeof(buff)); + str_value.set_quick(buff, sizeof(buff), cs); result=val_str(&str_value); if (null_value) { - str_value.set_quick(0, 0); + str_value.set_quick(0, 0, cs); return set_field_to_null_with_conversions(field, no_conversions); } field->set_notnull(); - field->store(result->ptr(),result->length()); - str_value.set_quick(0, 0); + error=field->store(result->ptr(),result->length(),cs); + str_value.set_quick(0, 0, cs); } else if (result_type() == REAL_RESULT) { @@ -543,7 +1590,7 @@ bool Item::save_in_field(Field *field, bool no_conversions) if (null_value) return set_field_to_null(field); field->set_notnull(); - field->store(nr); + error=field->store(nr); } else { @@ -551,42 +1598,53 @@ bool Item::save_in_field(Field *field, bool no_conversions) if (null_value) return set_field_to_null_with_conversions(field, no_conversions); field->set_notnull(); - field->store(nr); + error=field->store(nr); } - return 0; + return error; } -bool Item_string::save_in_field(Field *field, bool no_conversions) +int Item_string::save_in_field(Field *field, bool no_conversions) { String *result; result=val_str(&str_value); if (null_value) return set_field_to_null(field); field->set_notnull(); - field->store(result->ptr(),result->length()); - return 0; + return field->store(result->ptr(),result->length(),collation.collation); +} + +int Item_uint::save_in_field(Field *field, bool no_conversions) +{ + /* + TODO: To be fixed when wen have a + field->store(longlong, unsigned_flag) method + */ + return Item_int::save_in_field(field, no_conversions); } -bool Item_int::save_in_field(Field *field, bool no_conversions) +int Item_int::save_in_field(Field *field, bool no_conversions) { longlong nr=val_int(); if (null_value) return set_field_to_null(field); field->set_notnull(); - field->store(nr); - return 0; + return field->store(nr); +} + +Item_num *Item_uint::neg() +{ + return new Item_real(name, - ((double) value), 0, max_length); } -bool Item_real::save_in_field(Field *field, bool no_conversions) +int Item_real::save_in_field(Field *field, bool no_conversions) { double nr=val(); if (null_value) return set_field_to_null(field); field->set_notnull(); - field->store(nr); - return 0; + return field->store(nr); } /**************************************************************************** @@ -602,13 +1660,6 @@ inline uint char_val(char X) X-'a'+10); } -/* In MySQL 4.1 this will always return STRING_RESULT */ - -enum Item_result Item_varbinary::result_type () const -{ - return (current_thd->variables.new_mode) ? STRING_RESULT : INT_RESULT; -} - Item_varbinary::Item_varbinary(const char *str, uint str_length) { @@ -617,7 +1668,7 @@ Item_varbinary::Item_varbinary(const char *str, uint str_length) char *ptr=(char*) sql_alloc(max_length+1); if (!ptr) return; - str_value.set(ptr,max_length); + str_value.set(ptr,max_length,&my_charset_bin); char *end=ptr+max_length; if (max_length*2 != str_length) *ptr++=char_val(*str++); // Not even, assume 0 prefix @@ -627,11 +1678,14 @@ Item_varbinary::Item_varbinary(const char *str, uint str_length) str+=2; } *ptr=0; // Keep purify happy - binary=1; // Binary is default + collation.set(&my_charset_bin, DERIVATION_COERCIBLE); + fixed= 1; } longlong Item_varbinary::val_int() { + // following assert is redundant, because fixed=1 assigned in constructor + DBUG_ASSERT(fixed == 1); char *end=(char*) str_value.ptr()+str_value.length(), *ptr=end-min(str_value.length(),sizeof(longlong)); @@ -642,46 +1696,143 @@ longlong Item_varbinary::val_int() } -bool Item_varbinary::save_in_field(Field *field, bool no_conversions) +int Item_varbinary::save_in_field(Field *field, bool no_conversions) { + int error; field->set_notnull(); if (field->result_type() == STRING_RESULT) { - field->store(str_value.ptr(),str_value.length()); + error=field->store(str_value.ptr(),str_value.length(),collation.collation); } else { longlong nr=val_int(); - field->store(nr); + error=field->store(nr); } - return 0; + return error; } -void Item_varbinary::make_field(Send_field *tmp_field) +/* + Pack data in buffer for sending +*/ + +bool Item_null::send(Protocol *protocol, String *packet) { - init_make_field(tmp_field,FIELD_TYPE_STRING); + return protocol->store_null(); } /* -** pack data in buffer for sending + This is only called from items that is not of type item_field */ -bool Item::send(THD *thd, String *packet) -{ - char buff[MAX_FIELD_WIDTH]; - CONVERT *convert; - String s(buff,sizeof(buff)),*res; - if (!(res=val_str(&s))) - return net_store_null(packet); - if ((convert=thd->variables.convert_set)) - return convert->store(packet,res->ptr(),res->length()); - return net_store_data(packet,res->ptr(),res->length()); +bool Item::send(Protocol *protocol, String *buffer) +{ + bool result; + enum_field_types type; + LINT_INIT(result); + + switch ((type=field_type())) { + default: + case MYSQL_TYPE_NULL: + case MYSQL_TYPE_DECIMAL: + case MYSQL_TYPE_ENUM: + case MYSQL_TYPE_SET: + case MYSQL_TYPE_TINY_BLOB: + case MYSQL_TYPE_MEDIUM_BLOB: + case MYSQL_TYPE_LONG_BLOB: + case MYSQL_TYPE_BLOB: + case MYSQL_TYPE_GEOMETRY: + case MYSQL_TYPE_STRING: + case MYSQL_TYPE_VAR_STRING: + { + String *res; + if ((res=val_str(buffer))) + result= protocol->store(res->ptr(),res->length(),res->charset()); + break; + } + case MYSQL_TYPE_TINY: + { + longlong nr; + nr= val_int(); + if (!null_value) + result= protocol->store_tiny(nr); + break; + } + case MYSQL_TYPE_SHORT: + { + longlong nr; + nr= val_int(); + if (!null_value) + result= protocol->store_short(nr); + break; + } + case MYSQL_TYPE_INT24: + case MYSQL_TYPE_LONG: + { + longlong nr; + nr= val_int(); + if (!null_value) + result= protocol->store_long(nr); + break; + } + case MYSQL_TYPE_LONGLONG: + { + longlong nr; + nr= val_int(); + if (!null_value) + result= protocol->store_longlong(nr, unsigned_flag); + break; + } + case MYSQL_TYPE_FLOAT: + { + float nr; + nr= (float) val(); + if (!null_value) + result= protocol->store(nr, decimals, buffer); + break; + } + case MYSQL_TYPE_DOUBLE: + { + double nr; + nr= val(); + if (!null_value) + result= protocol->store(nr, decimals, buffer); + break; + } + case MYSQL_TYPE_DATETIME: + case MYSQL_TYPE_DATE: + case MYSQL_TYPE_TIMESTAMP: + { + TIME tm; + get_date(&tm, TIME_FUZZY_DATE); + if (!null_value) + { + if (type == MYSQL_TYPE_DATE) + return protocol->store_date(&tm); + else + result= protocol->store(&tm); + } + break; + } + case MYSQL_TYPE_TIME: + { + TIME tm; + get_time(&tm); + if (!null_value) + result= protocol->store_time(&tm); + break; + } + } + if (null_value) + result= protocol->store_null(); + return result; } -bool Item_null::send(THD *thd, String *packet) + +bool Item_field::send(Protocol *protocol, String *buffer) { - return net_store_null(packet); + return protocol->store(result_field); } /* @@ -689,22 +1840,321 @@ bool Item_null::send(THD *thd, String *packet) Find field in select list having the same name */ -bool Item_ref::fix_fields(THD *thd,TABLE_LIST *tables) +bool Item_ref::fix_fields(THD *thd,TABLE_LIST *tables, Item **reference) { + DBUG_ASSERT(fixed == 0); + uint counter; if (!ref) { - if (!(ref=find_item_in_list(this,thd->lex.select->item_list))) + TABLE_LIST *where= 0, *table_list; + bool upward_lookup= 0; + 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))) == + (Item **)not_found_item) + { + upward_lookup= 1; + Field *tmp= (Field*) 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_item_in_list. + */ + SELECT_LEX *last=0; + for ( ; sl ; sl= (prev_unit= sl->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)) != + (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; + } + if ((tmp= find_field_in_tables(thd, this, + table_list, &where, + 0)) != not_found_field) + { + prev_subselect_item->used_tables_cache|= tmp->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; + + if (sl->master_unit()->first_select()->linkage == + DERIVED_TABLE_TYPE) + break; // do not look over derived table + } + + if (!ref) + return 1; + else if (!tmp) + return -1; + else if (ref == (Item **)not_found_item && tmp == 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); + } + else + { + // Call to report error + find_item_in_list(this, + *(thd->lex->current_select->get_item_list()), + &counter, + REPORT_ALL_ERRORS); + } + ref= 0; + return 1; + } + else if (tmp != not_found_field) + { + ref= 0; // To prevent "delete *ref;" on ~Item_erf() of this item + Item_field* fld; + if (!((*reference)= fld= new Item_field(tmp))) + return 1; + register_item_tree_changing(reference); + mark_as_dependent(thd, last, thd->lex->current_select, fld); + return 0; + } + else + { + if (!(*ref)->fixed) + { + my_error(ER_ILLEGAL_REFERENCE, MYF(0), name, + "forward reference in item list"); + return -1; + } + mark_as_dependent(thd, last, thd->lex->current_select, + this); + ref= last->ref_pointer_array + counter; + } + } + else if (!ref) return 1; - max_length= (*ref)->max_length; - maybe_null= (*ref)->maybe_null; - decimals= (*ref)->decimals; - binary= (*ref)->binary; - with_sum_func= (*ref)->with_sum_func; + else + { + if (!(*ref)->fixed) + { + my_error(ER_ILLEGAL_REFERENCE, MYF(0), name, + "forward reference in item list"); + return -1; + } + ref= thd->lex->current_select->ref_pointer_array + counter; + } } + + /* + 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. + */ + if (((*ref)->with_sum_func && name && + (depended_from || + !(thd->lex->current_select->linkage != GLOBAL_OPTIONS_TYPE && + thd->lex->current_select->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; + } + max_length= (*ref)->max_length; + maybe_null= (*ref)->maybe_null; + decimals= (*ref)->decimals; + collation.set((*ref)->collation); + with_sum_func= (*ref)->with_sum_func; + fixed= 1; + + if (ref && (*ref)->check_cols(1)) + return 1; return 0; } +void Item_ref::cleanup() +{ + DBUG_ENTER("Item_ref::cleanup"); + Item_ident::cleanup(); + if (hook_ptr) + *hook_ptr= orig_item; + DBUG_VOID_RETURN; +} + + +void Item_ref::print(String *str) +{ + if (ref && *ref) + (*ref)->print(str); + else + Item_ident::print(str); +} + + +void Item_ref_null_helper::print(String *str) +{ + str->append("<ref_null_helper>(", 18); + if (ref && *ref) + (*ref)->print(str); + else + str->append('?'); + str->append(')'); +} + + +void Item_null_helper::print(String *str) +{ + str->append("<null_helper>(", 14); + store->print(str); + str->append(')'); +} + + +bool Item_default_value::eq(const Item *item, bool binary_cmp) const +{ + return item->type() == DEFAULT_VALUE_ITEM && + ((Item_default_value *)item)->arg->eq(arg, binary_cmp); +} + + +bool Item_default_value::fix_fields(THD *thd, + struct st_table_list *table_list, + Item **items) +{ + DBUG_ASSERT(fixed == 0); + if (!arg) + { + fixed= 1; + return 0; + } + if (arg->fix_fields(thd, table_list, &arg)) + return 1; + + if (arg->type() == REF_ITEM) + { + Item_ref *ref= (Item_ref *)arg; + if (ref->ref[0]->type() != FIELD_ITEM) + { + return 1; + } + 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; + memcpy(def_field, field_arg->field, field_arg->field->size_of()); + def_field->move_field(def_field->table->default_values - + def_field->table->record[0]); + set_field(def_field); + fixed= 1; + return 0; +} + +void Item_default_value::print(String *str) +{ + if (!arg) + { + str->append("default", 7); + return; + } + str->append("default(", 8); + arg->print(str); + str->append(')'); +} + +bool Item_insert_value::eq(const Item *item, bool binary_cmp) const +{ + return item->type() == INSERT_VALUE_ITEM && + ((Item_default_value *)item)->arg->eq(arg, binary_cmp); +} + + +bool Item_insert_value::fix_fields(THD *thd, + struct st_table_list *table_list, + Item **items) +{ + DBUG_ASSERT(fixed == 0); + if (arg->fix_fields(thd, table_list, &arg)) + return 1; + + if (arg->type() == REF_ITEM) + { + Item_ref *ref= (Item_ref *)arg; + if (ref->ref[0]->type() != FIELD_ITEM) + { + return 1; + } + arg= ref->ref[0]; + } + Item_field *field_arg= (Item_field *)arg; + if (field_arg->field->table->insert_values) + { + Field *def_field= (Field*) sql_alloc(field_arg->field->size_of()); + if (!def_field) + return 1; + 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]); + set_field(def_field); + } + else + { + Field *tmp_field= field_arg->field; + /* charset doesn't matter here, it's to avoid sigsegv only */ + set_field(new Field_null(0, 0, Field::NONE, tmp_field->field_name, + tmp_field->table, &my_charset_bin)); + } + fixed= 1; + return 0; +} + +void Item_insert_value::print(String *str) +{ + str->append("values(", 7); + arg->print(str); + str->append(')'); +} + /* If item is a const function, calculate it and return a const item The original item is freed if not returned @@ -716,6 +2166,8 @@ Item_result item_cmp_type(Item_result a,Item_result b) return STRING_RESULT; if (a == INT_RESULT && b == INT_RESULT) return INT_RESULT; + else if (a == ROW_RESULT || b == ROW_RESULT) + return ROW_RESULT; return REAL_RESULT; } @@ -731,30 +2183,19 @@ Item *resolve_const_item(Item *item,Item *comp_item) if (res_type == STRING_RESULT) { char buff[MAX_FIELD_WIDTH]; - String tmp(buff,sizeof(buff)),*result; + String tmp(buff,sizeof(buff),&my_charset_bin),*result; result=item->val_str(&tmp); if (item->null_value) - { -#ifdef DELETE_ITEMS - delete item; -#endif return new Item_null(name); - } uint length=result->length(); char *tmp_str=sql_strmake(result->ptr(),length); -#ifdef DELETE_ITEMS - delete item; -#endif - return new Item_string(name,tmp_str,length); + return new Item_string(name,tmp_str,length,result->charset()); } if (res_type == INT_RESULT) { longlong result=item->val_int(); uint length=item->max_length; bool null_value=item->null_value; -#ifdef DELETE_ITEMS - delete item; -#endif return (null_value ? (Item*) new Item_null(name) : (Item*) new Item_int(name,result,length)); } @@ -763,9 +2204,6 @@ Item *resolve_const_item(Item *item,Item *comp_item) double result=item->val(); uint length=item->max_length,decimals=item->decimals; bool null_value=item->null_value; -#ifdef DELETE_ITEMS - delete item; -#endif return (null_value ? (Item*) new Item_null(name) : (Item*) new Item_real(name,result,decimals,length)); } @@ -786,12 +2224,12 @@ bool field_is_equal_to_item(Field *field,Item *item) { char item_buff[MAX_FIELD_WIDTH]; char field_buff[MAX_FIELD_WIDTH]; - String item_tmp(item_buff,sizeof(item_buff)),*item_result; - String field_tmp(field_buff,sizeof(field_buff)); + String item_tmp(item_buff,sizeof(item_buff),&my_charset_bin),*item_result; + String field_tmp(field_buff,sizeof(field_buff),&my_charset_bin); item_result=item->val_str(&item_tmp); if (item->null_value) return 1; // This must be true - field->val_str(&field_tmp,&field_tmp); + field->val_str(&field_tmp); return !stringcmp(&field_tmp,item_result); } if (res_type == INT_RESULT) @@ -802,6 +2240,331 @@ bool field_is_equal_to_item(Field *field,Item *item) return result == field->val_real(); } +Item_cache* Item_cache::get_cache(Item_result type) +{ + switch (type) + { + case INT_RESULT: + return new Item_cache_int(); + case REAL_RESULT: + return new Item_cache_real(); + case STRING_RESULT: + return new Item_cache_str(); + case ROW_RESULT: + return new Item_cache_row(); + default: + // should never be in real life + DBUG_ASSERT(0); + return 0; + } +} + + +void Item_cache::print(String *str) +{ + str->append("<cache>(", 8); + if (example) + example->print(str); + else + Item::print(str); + str->append(')'); +} + + +void Item_cache_int::store(Item *item) +{ + value= item->val_int_result(); + null_value= item->null_value; +} + + +void Item_cache_real::store(Item *item) +{ + value= item->val_result(); + null_value= item->null_value; +} + + +void Item_cache_str::store(Item *item) +{ + value_buff.set(buffer, sizeof(buffer), item->collation.collation); + value= item->str_result(&value_buff); + if ((null_value= item->null_value)) + value= 0; + else if (value != &value_buff) + { + /* + We copy string value to avoid changing value if 'item' is table field + in queries like following (where t1.c is varchar): + select a, + (select a,b,c from t1 where t1.a=t2.a) = ROW(a,2,'a'), + (select c from t1 where a=t2.a) + from t2; + */ + value_buff.copy(*value); + value= &value_buff; + } +} + + +double Item_cache_str::val() +{ + DBUG_ASSERT(fixed == 1); + int err; + if (value) + return my_strntod(value->charset(), (char*) value->ptr(), + value->length(), (char**) 0, &err); + else + return (double)0; +} + + +longlong Item_cache_str::val_int() +{ + DBUG_ASSERT(fixed == 1); + int err; + if (value) + return my_strntoll(value->charset(), value->ptr(), + value->length(), 10, (char**) 0, &err); + else + return (longlong)0; +} + + +bool Item_cache_row::allocate(uint num) +{ + item_count= num; + THD *thd= current_thd; + return (!(values= + (Item_cache **) thd->calloc(sizeof(Item_cache *)*item_count))); +} + + +bool Item_cache_row::setup(Item * item) +{ + example= item; + if (!values && allocate(item->cols())) + return 1; + for (uint i= 0; i < item_count; i++) + { + Item *el= item->el(i); + Item_cache *tmp; + if (!(tmp= values[i]= Item_cache::get_cache(el->result_type()))) + return 1; + tmp->setup(el); + } + return 0; +} + + +void Item_cache_row::store(Item * item) +{ + null_value= 0; + item->bring_value(); + for (uint i= 0; i < item_count; i++) + { + values[i]->store(item->el(i)); + null_value|= values[i]->null_value; + } +} + + +void Item_cache_row::illegal_method_call(const char *method) +{ + DBUG_ENTER("Item_cache_row::illegal_method_call"); + DBUG_PRINT("error", ("!!! %s method was called for row item", method)); + DBUG_ASSERT(0); + my_error(ER_OPERAND_COLUMNS, MYF(0), 1); + DBUG_VOID_RETURN; +} + + +bool Item_cache_row::check_cols(uint c) +{ + if (c != item_count) + { + my_error(ER_OPERAND_COLUMNS, MYF(0), c); + return 1; + } + return 0; +} + + +bool Item_cache_row::null_inside() +{ + for (uint i= 0; i < item_count; i++) + { + if (values[i]->cols() > 1) + { + if (values[i]->null_inside()) + return 1; + } + else + { + values[i]->val_int(); + if (values[i]->null_value) + return 1; + } + } + return 0; +} + + +void Item_cache_row::bring_value() +{ + for (uint i= 0; i < item_count; i++) + values[i]->bring_value(); + return; +} + + +Item_type_holder::Item_type_holder(THD *thd, Item *item) + :Item(thd, item), item_type(item->result_type()), + orig_type(item_type) +{ + DBUG_ASSERT(item->fixed); + + /* + It is safe assign pointer on field, because it will be used just after + all JOIN::prepare calls and before any SELECT execution + */ + if (item->type() == Item::FIELD_ITEM) + field_example= ((Item_field*) item)->field; + else + field_example= 0; + max_length= real_length(item); + collation.set(item->collation); +} + + +/* + STRING_RESULT, REAL_RESULT, INT_RESULT, ROW_RESULT + + ROW_RESULT should never appear in Item_type_holder::join_types, + but it is included in following table just to make table full + (there DBUG_ASSERT in function to catch ROW_RESULT) +*/ +static Item_result type_convertor[4][4]= +{{STRING_RESULT, STRING_RESULT, STRING_RESULT, ROW_RESULT}, + {STRING_RESULT, REAL_RESULT, REAL_RESULT, ROW_RESULT}, + {STRING_RESULT, REAL_RESULT, INT_RESULT, ROW_RESULT}, + {ROW_RESULT, ROW_RESULT, ROW_RESULT, ROW_RESULT}}; + +bool Item_type_holder::join_types(THD *thd, Item *item) +{ + uint32 new_length= real_length(item); + bool change_field= 0, skip_store_field= 0; + Item_result new_type= type_convertor[item_type][item->result_type()]; + + // we have both fields + if (field_example && item->type() == Item::FIELD_ITEM) + { + Field *field= ((Item_field *)item)->field; + if (field_example->field_cast_type() != field->field_cast_type()) + { + if (!(change_field= + field_example->field_cast_compatible(field->field_cast_type()))) + { + /* + if old field can't store value of 'worse' new field we will make + decision about result field type based only on Item result type + */ + if (!field->field_cast_compatible(field_example->field_cast_type())) + skip_store_field= 1; + } + } + } + + // size/type should be changed + if (change_field || + (new_type != item_type) || + (max_length < new_length) || + ((new_type == INT_RESULT) && + (decimals < item->decimals)) || + (!maybe_null && item->maybe_null) || + (item_type == STRING_RESULT && new_type == STRING_RESULT && + !my_charset_same(collation.collation, item->collation.collation))) + { + // new field has some parameters worse then current + skip_store_field|= (change_field && + (max_length > new_length) || + ((new_type == INT_RESULT) && + (decimals > item->decimals)) || + (maybe_null && !item->maybe_null) || + (item_type == STRING_RESULT && + new_type == STRING_RESULT && + !my_charset_same(collation.collation, + item->collation.collation))); + /* + It is safe assign pointer on field, because it will be used just after + all JOIN::prepare calls and before any SELECT execution + */ + if (skip_store_field || item->type() != Item::FIELD_ITEM) + field_example= 0; + else + field_example= ((Item_field*) item)->field; + + const char *old_cs= collation.collation->name, + *old_derivation= collation.derivation_name(); + if (item_type == STRING_RESULT && collation.aggregate(item->collation)) + { + my_error(ER_CANT_AGGREGATE_2COLLATIONS, MYF(0), + old_cs, old_derivation, + item->collation.collation->name, + item->collation.derivation_name(), + "UNION"); + return 1; + } + + max_length= max(max_length, new_length); + decimals= max(decimals, item->decimals); + maybe_null|= item->maybe_null; + item_type= new_type; + } + DBUG_ASSERT(item_type != ROW_RESULT); + return 0; +} + +uint32 Item_type_holder::real_length(Item *item) +{ + if (item->type() == Item::FIELD_ITEM) + { + return ((Item_field *)item)->max_disp_length(); + } + switch (item->result_type()) + { + case STRING_RESULT: + return item->max_length; + case REAL_RESULT: + return 53; + case INT_RESULT: + return 20; + case ROW_RESULT: + default: + DBUG_ASSERT(0); // we should never go there + return 0; + } +} + +double Item_type_holder::val() +{ + DBUG_ASSERT(0); // should never be called + return 0.0; +} + + +longlong Item_type_holder::val_int() +{ + DBUG_ASSERT(0); // should never be called + return 0; +} + + +String *Item_type_holder::val_str(String*) +{ + DBUG_ASSERT(0); // should never be called + return 0; +} /***************************************************************************** ** Instantiate templates |