diff options
Diffstat (limited to 'sql/item.cc')
-rw-r--r-- | sql/item.cc | 1307 |
1 files changed, 1162 insertions, 145 deletions
diff --git a/sql/item.cc b/sql/item.cc index 0e9085180cd..371ce0a947d 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -23,6 +23,9 @@ #include <m_ctype.h> #include "my_dir.h" +static void mark_as_dependent(SELECT_LEX *last, SELECT_LEX *current, + Item_ident *item); + /***************************************************************************** ** Item functions *****************************************************************************/ @@ -34,39 +37,118 @@ 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; + THD *thd= current_thd; + next= thd->free_list; // Put in 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; +} + +// Constructor used by Item_field & Item_ref (see Item comment) +Item_ident::Item_ident(THD *thd, Item_ident &item): + Item(thd, item), + db_name(item.db_name), + table_name(item.table_name), + field_name(item.field_name), + depended_from(item.depended_from) +{} + +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(1); +} + + +bool Item::check_cols(uint c) { - 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; + if (c != 1) + { + my_error(ER_CARDINALITY_COL, 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 @@ -74,8 +156,8 @@ 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, &my_charset_bin); + return !sortcmp(&str_value, &item->str_value, collation.collation); } return 0; } @@ -89,7 +171,7 @@ bool Item_string::eq(const Item *item, bool binary_cmp) const bool Item::get_date(TIME *ltime,bool 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) { @@ -107,7 +189,7 @@ 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)) { @@ -117,11 +199,83 @@ bool Item::get_time(TIME *ltime) return 0; } +CHARSET_INFO * Item::default_charset() const +{ + 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) { set_field(f); + collation.set(DERIVATION_IMPLICIT); + fixed= 1; // This item is not needed in fix_fields } +// 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) { @@ -131,8 +285,9 @@ 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 @@ -140,7 +295,7 @@ const char *Item_ident::full_name() const char *tmp; if (!table_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); @@ -160,6 +315,7 @@ String *Item_field::val_str(String *str) { if ((null_value=field->is_null())) return 0; + str->set_charset(str_value.charset()); return field->val_str(str,&str_value); } @@ -182,6 +338,7 @@ 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); } @@ -230,22 +387,54 @@ 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 && !my_strcasecmp(table_alias_charset, + 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; +} String *Item_int::val_str(String *str) { - str->set(value); + str->set(value, default_charset()); return str; } @@ -253,7 +442,7 @@ void Item_int::print(String *str) { if (!name) { - str_value.set(value); + str_value.set(value, default_charset()); name=str_value.c_ptr(); } str->append(name); @@ -261,7 +450,7 @@ void Item_int::print(String *str) String *Item_uint::val_str(String *str) { - str->set((ulonglong) value); + str->set((ulonglong) value, default_charset()); return str; } @@ -269,7 +458,7 @@ void Item_uint::print(String *str) { if (!name) { - str_value.set((ulonglong) value); + str_value.set((ulonglong) value, default_charset()); name=str_value.c_ptr(); } str->append(name); @@ -278,7 +467,7 @@ void Item_uint::print(String *str) String *Item_real::val_str(String *str) { - str->set(value,decimals); + str->set(value,decimals,default_charset()); return str; } @@ -298,6 +487,212 @@ String *Item_null::val_str(String *str) { null_value=1; return 0;} +/* Item_param related */ +void Item_param::set_null() +{ + maybe_null=null_value=1; +} + +void Item_param::set_int(longlong i) +{ + int_value=(longlong)i; + item_type = INT_ITEM; +} + +void Item_param::set_double(double value) +{ + real_value=value; + item_type = REAL_ITEM; +} + + +void Item_param::set_value(const char *str, uint length) +{ + str_value.set(str,length,default_charset()); + item_type = STRING_ITEM; +} + + +void Item_param::set_time(TIME *tm, timestamp_type type) +{ + ltime.year= tm->year; + ltime.month= tm->month; + ltime.day= tm->day; + + ltime.hour= tm->hour; + ltime.minute= tm->minute; + ltime.second= tm->second; + + ltime.second_part= tm->second_part; + + ltime.time_type= type; + + item_is_time= true; + item_type= STRING_ITEM; +} + + +void Item_param::set_longdata(const char *str, ulong length) +{ + str_value.append(str,length); + long_data_supplied= 1; +} + + +int Item_param::save_in_field(Field *field, bool no_conversions) +{ + THD *thd= current_thd; + + if (thd->command == COM_PREPARE) + return -1; + + if (null_value) + return (int) set_field_to_null(field); + + field->set_notnull(); + if (item_result_type == INT_RESULT) + { + longlong nr=val_int(); + return (field->store(nr)) ? -1 : 0; + } + if (item_result_type == REAL_RESULT) + { + double nr=val(); + return (field->store(nr)) ? -1 : 0; + } + if (item_is_time) + { + field->store_time(<ime, ltime.time_type); + return 0; + } + String *result=val_str(&str_value); + return (field->store(result->ptr(),result->length(),field->charset())) ? -1 : 0; +} + +bool Item_param::get_time(TIME *res) +{ + *res=ltime; + return 0; +} + +double Item_param::val() +{ + int err; + switch (item_result_type) { + case STRING_RESULT: + return (double) my_strntod(str_value.charset(), (char*) str_value.ptr(), + str_value.length(), (char**) 0, &err); + case INT_RESULT: + return (double)int_value; + default: + return real_value; + } +} + + +longlong Item_param::val_int() +{ + int err; + switch (item_result_type) { + case STRING_RESULT: + return my_strntoll(str_value.charset(), + str_value.ptr(),str_value.length(),10, + (char**) 0,&err); + case REAL_RESULT: + return (longlong) (real_value+(real_value > 0 ? 0.5 : -0.5)); + default: + return int_value; + } +} + + +String *Item_param::val_str(String* str) +{ + switch (item_result_type) { + case INT_RESULT: + str->set(int_value, default_charset()); + return str; + case REAL_RESULT: + str->set(real_value, 2, default_charset()); + return str; + default: + return (String*) &str_value; + } +} + +/* + Return Param item values in string format, for generating the dynamic + query used in update/binary logs +*/ + +String *Item_param::query_val_str(String* str) +{ + switch (item_result_type) { + case INT_RESULT: + case REAL_RESULT: + return val_str(str); + break; + default: + str->set("'", 1, default_charset()); + + if (!item_is_time) + { + str->append(str_value); + const char *from= str->ptr(); + uint32 length= 1; + + // Escape misc cases + char *to= (char *)from, *end= (char *)to+str->length(); + for (to++; to != end ; length++, to++) + { + switch(*to) { + case '\'': + case '"': + case '\r': + case '\n': + case '\\': // TODO: Add remaining .. + str->replace(length,0,"\\",1); + to++; end++; length++; + break; + default: + break; + } + } + } + else + { + char buff[25]; + + switch (ltime.time_type) { + case TIMESTAMP_NONE: + break; + case TIMESTAMP_DATE: + sprintf(buff, "%04d-%02d-%02d", + ltime.year,ltime.month,ltime.day); + str->append(buff, 10); + break; + case TIMESTAMP_FULL: + sprintf(buff, "%04d-%02d-%02d %02d:%02d:%02d", + ltime.year,ltime.month,ltime.day, + ltime.hour,ltime.minute,ltime.second); + str->append(buff, 19); + break; + case TIMESTAMP_TIME: + { + sprintf(buff, "%02d:%02d:%02d", + ltime.hour,ltime.minute,ltime.second); + str->append(buff, 8); + break; + } + } + } + str->append("'"); + } + return str; +} +/* End of Item_param related */ + + void Item_copy_string::copy() { String *res=item->val_str(&str_value); @@ -315,23 +710,161 @@ String *Item_copy_string::val_str(String *str) } /* -** 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) { + fixed= 1; return 0; } -bool Item_field::fix_fields(THD *thd,TABLE_LIST *tables) +double Item_ref_null_helper::val() +{ + double tmp= (*ref)->val_result(); + owner->was_null|= null_value= (*ref)->null_value; + return tmp; +} +longlong Item_ref_null_helper::val_int() +{ + 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) +{ + 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, bool 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() + last - select from which current item depend + current - current select + item - item which should be marked +*/ + +static void mark_as_dependent(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); +} + + +bool Item_field::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref) { if (!field) // If field is not checked { - Field *tmp; - if (!(tmp=find_field_in_tables(thd,this,tables))) - return 1; + TABLE_LIST *where= 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) + for (SELECT_LEX *sl= cursel->outer_select(); + sl; + sl= sl->outer_select()) + { + 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; + } + if ((tmp= find_field_in_tables(thd, this, + table_list, &where, + 0)) != not_found_field) + 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) + break; + 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) + { + // call to return error code + 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, + (char *)table_name, + (char *)field_name); + if (!rf) + return 1; + if (rf->fix_fields(thd, tables, ref) || rf->check_cols(1)) + return 1; + + mark_as_dependent(last, cursel, rf); + return 0; + } + else + { + mark_as_dependent(last, cursel, this); + if (last->having_fix_field) + { + Item_ref *rf; + *ref= rf= new Item_ref((where->db[0]?where->db:0), + (char *)where->alias, + (char *)field_name); + if (!rf) + return 1; + 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) @@ -342,15 +875,21 @@ bool Item_field::fix_fields(THD *thd,TABLE_LIST *tables) table->used_fields++; table->used_keys&=field->part_of_key; } + fixed= 1; return 0; } 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; @@ -359,70 +898,98 @@ 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) -{ - init_make_field(tmp_field,FIELD_TYPE_DATETIME); -} - - -void Item_null::make_field(Send_field *tmp_field) -{ - init_make_field(tmp_field,FIELD_TYPE_NULL); - tmp_field->length=4; -} - -void Item_func::make_field(Send_field *tmp_field) +enum_field_types Item::field_type() const { - init_make_field(tmp_field, ((result_type() == STRING_RESULT) ? - FIELD_TYPE_VAR_STRING : - (result_type() == INT_RESULT) ? - FIELD_TYPE_LONGLONG : FIELD_TYPE_DOUBLE)); + return ((result_type() == STRING_RESULT) ? FIELD_TYPE_VAR_STRING : + (result_type() == INT_RESULT) ? FIELD_TYPE_LONGLONG : + FIELD_TYPE_DOUBLE); } -void Item_avg_field::make_field(Send_field *tmp_field) +Field *Item::tmp_table_field_from_field_type(TABLE *table) { - init_make_field(tmp_field,FIELD_TYPE_DOUBLE); + switch (field_type()) + { + case MYSQL_TYPE_DECIMAL: + return new Field_decimal(max_length, maybe_null, name, table, + unsigned_flag); + case MYSQL_TYPE_TINY: + return new Field_tiny(max_length, maybe_null, name, table, + unsigned_flag); + case MYSQL_TYPE_SHORT: + return new Field_short(max_length, maybe_null, name, table, + unsigned_flag); + case MYSQL_TYPE_LONG: + return new Field_long(max_length, maybe_null, name, table, + unsigned_flag); + case MYSQL_TYPE_FLOAT: + return new Field_float(max_length, maybe_null, name, table, decimals); + case MYSQL_TYPE_DOUBLE: + return new Field_double(max_length, maybe_null, name, table, decimals); + case MYSQL_TYPE_NULL: + return new Field_null(max_length, name, table, &my_charset_bin); +#ifdef HAVE_LONG_LONG + case MYSQL_TYPE_LONGLONG: + return new Field_longlong(max_length, maybe_null, name, table, + unsigned_flag); +#endif + case MYSQL_TYPE_NEWDATE: + case MYSQL_TYPE_INT24: + return new Field_long(max_length, maybe_null, name, table, + 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(max_length, maybe_null, name, table); + case MYSQL_TYPE_ENUM: + case MYSQL_TYPE_SET: + return new Field_long(max_length, maybe_null, name, table, + unsigned_flag); + 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); + case MYSQL_TYPE_VAR_STRING: + if (max_length > 255) + return new Field_blob(max_length, maybe_null, name, table, collation.collation); + else + return new Field_varstring(max_length, maybe_null, name, table, collation.collation); + case MYSQL_TYPE_STRING: + if (max_length > 255) + return new Field_blob(max_length, maybe_null, name, table, collation.collation); + else + return new Field_string(max_length, maybe_null, name, table, collation.collation); + default: + // This case should never be choosen + DBUG_ASSERT(0); + return 0; + } } -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); + if (name) + tmp_field->col_name=name; // Use user supplied name } /* @@ -445,7 +1012,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()) { @@ -461,6 +1028,7 @@ bool Item_field::save_in_field(Field *to, bool no_conversions) return 0; } + /* Store null in field @@ -477,7 +1045,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); } @@ -495,27 +1063,29 @@ 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) 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) { @@ -523,7 +1093,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 { @@ -531,41 +1101,41 @@ 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) ? -1 : 0; } -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)) ? + -1 : 0; } -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)) ? -1 : 0; } -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)) ? -1 : 0; } /**************************************************************************** @@ -581,13 +1151,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) { @@ -596,7 +1159,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 @@ -606,7 +1169,7 @@ 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); } longlong Item_varbinary::val_int() @@ -621,46 +1184,135 @@ 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) ? -1 : 0; } -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) +bool Item::send(Protocol *protocol, String *buffer) { - 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 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_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, 1); + 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); } /* @@ -668,22 +1320,248 @@ 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) { + uint counter; if (!ref) { - if (!(ref=find_item_in_list(this,thd->lex.select->item_list))) + TABLE_LIST *where= 0, *table_list; + SELECT_LEX *sl= thd->lex.current_select->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) + { + 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= sl->outer_select()) + { + last= sl; + 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) + 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) + break; + 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) + { + // 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; + mark_as_dependent(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(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; } +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) +{ + if (!arg) + return false; + bool res= arg->fix_fields(thd, table_list, items); + if (res) + return res; + /* arg->type() can be only REF_ITEM or FIELD_ITEM for it defined as + simple_ident in sql_yacc.yy + */ + 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); + return 0; +} + +void Item_default_value::print(String *str) +{ + if (!arg) + { + str->append("DEFAULT"); + return; + } + str->append("DEFAULT("); + 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) +{ + bool res= arg->fix_fields(thd, table_list, items); + if (res) + return res; + /* + arg->type() can be only REF_ITEM or FIELD_ITEM as arg is + a simple_ident in sql_yacc.yy + */ + 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 *field=field_arg->field; + /* charset doesn't matter here, it's to avoid sigsegv only */ + set_field(new Field_null(0,0,Field::NONE,field->field_name,field->table, + &my_charset_bin)); + } + return 0; +} + +void Item_insert_value::print(String *str) +{ + str->append("VALUE("); + 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 @@ -695,6 +1573,8 @@ Item_result item_cmp_type(Item_result a,Item_result b) return STRING_RESULT; else if (a == INT_RESULT && b == INT_RESULT) return INT_RESULT; + else if (a == ROW_RESULT || b == ROW_RESULT) + return ROW_RESULT; else return REAL_RESULT; } @@ -711,7 +1591,7 @@ 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) { @@ -725,7 +1605,7 @@ Item *resolve_const_item(Item *item,Item *comp_item) #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) { @@ -766,13 +1646,13 @@ 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); - return !stringcmp(&field_tmp,item_result); + return !sortcmp(&field_tmp,item_result,&my_charset_bin); } if (res_type == INT_RESULT) return 1; // Both where of type int @@ -782,6 +1662,143 @@ 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_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; + } + collation.set(item->collation); +} +double Item_cache_str::val() +{ + 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() +{ + 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) +{ + 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_CARDINALITY_COL, MYF(0), 1); + DBUG_VOID_RETURN; +} + +bool Item_cache_row::check_cols(uint c) +{ + if (c != item_count) + { + my_error(ER_CARDINALITY_COL, 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; +} /***************************************************************************** ** Instantiate templates |