summaryrefslogtreecommitdiff
path: root/sql/item.cc
diff options
context:
space:
mode:
Diffstat (limited to 'sql/item.cc')
-rw-r--r--sql/item.cc1307
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(&ltime, 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