summaryrefslogtreecommitdiff
path: root/sql/item.cc
diff options
context:
space:
mode:
authormonty@mysql.com <>2004-10-29 19:26:52 +0300
committermonty@mysql.com <>2004-10-29 19:26:52 +0300
commitafbe601302fc59c498437321b296ed6c8d360564 (patch)
tree23bcc9a71fe7237887a111b158e30f5a6bb665d3 /sql/item.cc
parent67456bb970cc949ceb5779b230592e455843c35c (diff)
parent541883f9d89a8d38affba60bf9506289a6232da1 (diff)
downloadmariadb-git-afbe601302fc59c498437321b296ed6c8d360564.tar.gz
merge with 4.1
Diffstat (limited to 'sql/item.cc')
-rw-r--r--sql/item.cc328
1 files changed, 202 insertions, 126 deletions
diff --git a/sql/item.cc b/sql/item.cc
index 134b04d5540..7476f5af312 100644
--- a/sql/item.cc
+++ b/sql/item.cc
@@ -113,13 +113,14 @@ void Item::cleanup()
DBUG_PRINT("info", ("Item: 0x%lx", this));
DBUG_PRINT("info", ("Type: %d", (int)type()));
fixed=0;
+ marker= 0;
DBUG_VOID_RETURN;
}
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),
+ orig_field_name(field_name_par),
db_name(db_name_par), table_name(table_name_par),
field_name(field_name_par), cached_field_index(NO_CACHED_FIELD_INDEX),
cached_table(0), depended_from(0)
@@ -133,7 +134,6 @@ Item_ident::Item_ident(THD *thd, Item_ident *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),
@@ -150,11 +150,6 @@ void Item_ident::cleanup()
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;
@@ -451,19 +446,47 @@ Item_field::Item_field(Field *f)
have_privileges(0), any_privileges(0)
{
set_field(f);
- collation.set(DERIVATION_IMPLICIT);
- fixed= 1;
+ /*
+ field_name and talbe_name should not point to garbage
+ if this item is to be reused
+ */
+ orig_table_name= orig_field_name= "";
}
Item_field::Item_field(THD *thd, Field *f)
- :Item_ident(NullS, thd->strdup(f->table_name),
- thd->strdup(f->field_name)),
+ :Item_ident(f->table->table_cache_key, f->table_name, f->field_name),
item_equal(0), no_const_subst(0),
have_privileges(0), any_privileges(0)
{
+ /*
+ We always need to provide Item_field with a fully qualified field
+ name to avoid ambiguity when executing prepared statements like
+ SELECT * from d1.t1, d2.t1; (assuming d1.t1 and d2.t1 have columns
+ with same names).
+ This is because prepared statements never deal with wildcards in
+ select list ('*') and always fix fields using fully specified path
+ (i.e. db.table.column).
+ No check for OOM: if db_name is NULL, we'll just get
+ "Field not found" error.
+ We need to copy db_name, table_name and field_name because they must
+ be allocated in the statement memory, not in table memory (the table
+ structure can go away and pop up again between subsequent executions
+ of a prepared statement).
+ */
+ if (thd->current_arena->is_stmt_prepare())
+ {
+ if (db_name)
+ orig_db_name= thd->strdup(db_name);
+ orig_table_name= thd->strdup(table_name);
+ orig_field_name= thd->strdup(field_name);
+ /*
+ We don't restore 'name' in cleanup because it's not changed
+ during execution. Still we need it to point to persistent
+ memory if this item is to be reused.
+ */
+ name= (char*) orig_field_name;
+ }
set_field(f);
- collation.set(DERIVATION_IMPLICIT);
- fixed= 1;
}
// Constructor need to process subselect with temporary tables (see Item)
@@ -490,6 +513,21 @@ void Item_field::set_field(Field *field_par)
db_name=field_par->table->table_cache_key;
unsigned_flag=test(field_par->flags & UNSIGNED_FLAG);
collation.set(field_par->charset(), DERIVATION_IMPLICIT);
+ fixed= 1;
+}
+
+
+/*
+ Reset this item to point to a field from the new temporary table.
+ This is used when we create a new temporary table for each execution
+ of prepared statement.
+*/
+
+void Item_field::reset_field(Field *f)
+{
+ set_field(f);
+ /* 'name' is pointing at field->field_name of old field */
+ name= (char*) f->field_name;
}
const char *Item_ident::full_name() const
@@ -1042,7 +1080,7 @@ int Item_param::save_in_field(Field *field, bool no_conversions)
return field->store(str_value.ptr(), str_value.length(),
str_value.charset());
case NULL_VALUE:
- return set_field_to_null(field);
+ return set_field_to_null_with_conversions(field, no_conversions);
case NO_VALUE:
default:
DBUG_ASSERT(0);
@@ -1145,9 +1183,10 @@ String *Item_param::val_str(String* str)
return str;
case TIME_VALUE:
{
- if (str->reserve(MAX_DATE_REP_LENGTH))
+ if (str->reserve(MAX_DATE_STRING_REP_LENGTH))
break;
- TIME_to_string(&value.time, str);
+ str->length((uint) my_TIME_to_str(&value.time, (char*) str->ptr()));
+ str->set_charset(&my_charset_bin);
return str;
}
case NULL_VALUE:
@@ -1177,24 +1216,19 @@ const String *Item_param::query_val_str(String* str) const
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))
+ if (str->reserve(MAX_DATE_STRING_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+= (uint) my_TIME_to_str(&value.time, ptr);
*ptr++= '\'';
str->length((uint32) (ptr - buf));
break;
@@ -1372,7 +1406,7 @@ bool Item_ref_null_helper::get_date(TIME *ltime, uint fuzzydate)
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
+ // store pointer on SELECT_LEX from which item is dependent
item->depended_from= last;
current->mark_as_dependent(last);
if (thd->lex->describe & DESCRIBE_EXTENDED)
@@ -1416,6 +1450,7 @@ bool Item_field::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref)
TABLE_LIST *table_list;
Item **refer= (Item **)not_found_item;
uint counter;
+ bool not_used;
// Prevent using outer fields in subselects, that is not supported now
SELECT_LEX *cursel= (SELECT_LEX *) thd->lex->current_select;
if (cursel->master_unit()->first_select()->linkage != DERIVED_TABLE_TYPE)
@@ -1440,11 +1475,9 @@ bool Item_field::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref)
enum_parsing_place place= prev_subselect_item->parsing_place;
/*
check table fields only if subquery used somewhere out of HAVING
- or SELECT list or outer SELECT do not use groupping (i.e. tables
- are accessable)
+ or outer SELECT do not use groupping (i.e. tables are accessable)
*/
- if (((place != IN_HAVING &&
- place != SELECT_LIST) ||
+ if ((place != IN_HAVING ||
(sl->with_sum_func == 0 && sl->group_list.elements == 0)) &&
(tmp= find_field_in_tables(thd, this,
table_list, ref,
@@ -1469,7 +1502,8 @@ bool Item_field::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref)
}
if (sl->resolve_mode == SELECT_LEX::SELECT_MODE &&
(refer= find_item_in_list(this, sl->item_list, &counter,
- REPORT_EXCEPT_NOT_FOUND)) !=
+ REPORT_EXCEPT_NOT_FOUND,
+ &not_used)) !=
(Item **) not_found_item)
{
if (*refer && (*refer)->fixed) // Avoid crash in case of error
@@ -1517,14 +1551,11 @@ bool Item_field::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref)
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);
+ Item_ref *rf= new Item_ref(last->ref_pointer_array + counter,
+ (char *)table_name, (char *)field_name);
if (!rf)
return 1;
+ thd->change_item_tree(ref, rf);
/*
rf is Item_ref => never substitute other items (in this case)
during fix_fields() => we can use rf after fix_fields()
@@ -1541,10 +1572,8 @@ bool Item_field::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref)
if (last->having_fix_field)
{
Item_ref *rf;
- *ref= rf= new Item_ref(ref, *ref,
- (cached_table->db[0]?cached_table->db:0),
- (char *)cached_table->alias,
- (char *)field_name);
+ rf= new Item_ref((cached_table->db[0] ? cached_table->db : 0),
+ (char*) cached_table->alias, (char*) field_name);
if (!rf)
return 1;
/*
@@ -1754,7 +1783,9 @@ void Item::init_make_field(Send_field *tmp_field,
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->flags= (maybe_null ? 0 : NOT_NULL_FLAG) |
+ (my_binary_compare(collation.collation) ?
+ BINARY_FLAG : 0);
tmp_field->type=field_type;
tmp_field->length=max_length;
tmp_field->decimals=decimals;
@@ -2261,15 +2292,17 @@ bool Item_field::send(Protocol *protocol, String *buffer)
return protocol->store(result_field);
}
+
/*
This is used for HAVING clause
Find field in select list having the same name
- */
+*/
bool Item_ref::fix_fields(THD *thd, TABLE_LIST *tables, Item **reference)
{
DBUG_ASSERT(fixed == 0);
uint counter;
+ bool not_used;
if (!ref)
{
TABLE_LIST *table_list;
@@ -2289,13 +2322,14 @@ bool Item_ref::fix_fields(THD *thd, TABLE_LIST *tables, Item **reference)
first_select()->linkage !=
DERIVED_TABLE_TYPE) ?
REPORT_EXCEPT_NOT_FOUND :
- REPORT_ALL_ERRORS))) ==
+ REPORT_ALL_ERRORS), &not_used)) ==
(Item **)not_found_item)
{
- upward_lookup= 1;
Field *tmp= (Field*) not_found_field;
+ SELECT_LEX *last= 0;
+ upward_lookup= 1;
/*
- We can't find table field in table list of current select,
+ We can't find table field in select list of current select,
consequently we have to find it in outer subselect(s).
We can't join lists of outer & current select, because of scope
of view rules. For example if both tables (outer & current) have
@@ -2303,15 +2337,14 @@ bool Item_ref::fix_fields(THD *thd, TABLE_LIST *tables, Item **reference)
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)) !=
+ &counter, REPORT_EXCEPT_NOT_FOUND,
+ &not_used)) !=
(Item **)not_found_item)
{
if (*ref && (*ref)->fixed) // Avoid crash in case of error
@@ -2333,8 +2366,7 @@ bool Item_ref::fix_fields(THD *thd, TABLE_LIST *tables, Item **reference)
or SELECT list or outer SELECT do not use groupping (i.e. tables
are accessable)
*/
- if (((place != IN_HAVING &&
- place != SELECT_LIST) ||
+ if ((place != IN_HAVING ||
(sl->with_sum_func == 0 && sl->group_list.elements == 0)) &&
(tmp= find_field_in_tables(thd, this,
table_list, reference,
@@ -2369,9 +2401,9 @@ bool Item_ref::fix_fields(THD *thd, TABLE_LIST *tables, Item **reference)
if (!ref)
return 1;
- else if (!tmp)
+ if (!tmp)
return -1;
- else if (ref == (Item **)not_found_item && tmp == not_found_field)
+ if (ref == (Item **)not_found_item && tmp == not_found_field)
{
if (upward_lookup)
{
@@ -2384,22 +2416,26 @@ bool Item_ref::fix_fields(THD *thd, TABLE_LIST *tables, Item **reference)
// Call to report error
find_item_in_list(this,
*(thd->lex->current_select->get_item_list()),
- &counter,
- REPORT_ALL_ERRORS);
+ &counter, REPORT_ALL_ERRORS, &not_used);
}
- ref= 0;
+ ref= 0; // Safety
return 1;
}
- else if (tmp != not_found_field)
+ if (tmp != not_found_field)
{
- ref= 0; // To prevent "delete *ref;" on ~Item_erf() of this item
+ /*
+ Set ref to 0 as we are replacing this item with the found item
+ and this will ensure we get an error if this item would be
+ used elsewhere
+ */
+ ref= 0; // Safety
if (tmp != view_ref_found)
{
Item_field* fld;
- if (!((*reference)= fld= new Item_field(tmp)))
+ if (!(fld= new Item_field(tmp)))
return 1;
+ thd->change_item_tree(reference, fld);
mark_as_dependent(thd, last, thd->lex->current_select, fld);
- register_item_tree_changing(reference);
return 0;
}
/*
@@ -2416,15 +2452,15 @@ bool Item_ref::fix_fields(THD *thd, TABLE_LIST *tables, Item **reference)
}
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;
+ 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)
@@ -2478,8 +2514,6 @@ void Item_ref::cleanup()
DBUG_ENTER("Item_ref::cleanup");
Item_ident::cleanup();
result_field= 0;
- if (hook_ptr)
- *hook_ptr= orig_item;
DBUG_VOID_RETURN;
}
@@ -2602,7 +2636,6 @@ bool Item_default_value::fix_fields(THD *thd,
def_field->move_field(def_field->table->default_values -
def_field->table->record[0]);
set_field(def_field);
- fixed= 1;
return 0;
}
@@ -2660,7 +2693,6 @@ bool Item_insert_value::fix_fields(THD *thd,
set_field(new Field_null(0, 0, Field::NONE, tmp_field->field_name,
tmp_field->table, &my_charset_bin));
}
- fixed= 1;
return 0;
}
@@ -2779,10 +2811,12 @@ Item_result item_cmp_type(Item_result a,Item_result b)
}
-Item *resolve_const_item(Item *item,Item *comp_item)
+void resolve_const_item(THD *thd, Item **ref, Item *comp_item)
{
+ Item *item= *ref;
+ Item *new_item;
if (item->basic_const_item())
- return item; // Can't be better
+ return; // Can't be better
Item_result res_type=item_cmp_type(comp_item->result_type(),
item->result_type());
char *name=item->name; // Alloced by sql_alloc
@@ -2793,27 +2827,32 @@ Item *resolve_const_item(Item *item,Item *comp_item)
String tmp(buff,sizeof(buff),&my_charset_bin),*result;
result=item->val_str(&tmp);
if (item->null_value)
- return new Item_null(name);
- uint length=result->length();
- char *tmp_str=sql_strmake(result->ptr(),length);
- return new Item_string(name,tmp_str,length,result->charset());
+ new_item= new Item_null(name);
+ else
+ {
+ uint length= result->length();
+ char *tmp_str= sql_strmake(result->ptr(), length);
+ new_item= new Item_string(name, tmp_str, length, result->charset());
+ }
}
- if (res_type == INT_RESULT)
+ else if (res_type == INT_RESULT)
{
longlong result=item->val_int();
uint length=item->max_length;
bool null_value=item->null_value;
- return (null_value ? (Item*) new Item_null(name) :
- (Item*) new Item_int(name,result,length));
+ new_item= (null_value ? (Item*) new Item_null(name) :
+ (Item*) new Item_int(name, result, length));
}
else
{ // It must REAL_RESULT
double result=item->val();
uint length=item->max_length,decimals=item->decimals;
bool null_value=item->null_value;
- return (null_value ? (Item*) new Item_null(name) :
- (Item*) new Item_real(name,result,decimals,length));
+ new_item= (null_value ? (Item*) new Item_null(name) : (Item*)
+ new Item_real(name, result, decimals, length));
}
+ if (new_item)
+ thd->change_item_tree(ref, new_item);
}
/*
@@ -3040,6 +3079,7 @@ Item_type_holder::Item_type_holder(THD *thd, Item *item)
else
field_example= 0;
max_length= real_length(item);
+ maybe_null= item->maybe_null;
collation.set(item->collation);
}
@@ -3057,62 +3097,90 @@ static Item_result type_convertor[4][4]=
{STRING_RESULT, REAL_RESULT, INT_RESULT, ROW_RESULT},
{ROW_RESULT, ROW_RESULT, ROW_RESULT, ROW_RESULT}};
+
+/*
+ Values of 'from' field can be stored in 'to' field.
+
+ SYNOPSIS
+ is_attr_compatible()
+ from Item which values should be saved
+ to Item where values should be saved
+
+ RETURN
+ 1 can be saved
+ 0 can not be saved
+*/
+
+inline bool is_attr_compatible(Item *from, Item *to)
+{
+ return ((to->max_length >= from->max_length) &&
+ (to->maybe_null || !from->maybe_null) &&
+ (to->result_type() != STRING_RESULT ||
+ from->result_type() != STRING_RESULT ||
+ my_charset_same(from->collation.collation,
+ to->collation.collation)));
+}
+
+
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()];
+ bool use_new_field= 0, use_expression_type= 0;
+ Item_result new_result_type= type_convertor[item_type][item->result_type()];
+ bool item_is_a_field= item->type() == Item::FIELD_ITEM;
- // we have both fields
- if (field_example && item->type() == Item::FIELD_ITEM)
+ /*
+ Check if both items point to fields: in this case we
+ can adjust column types of result table in the union smartly.
+ */
+ if (field_example && item_is_a_field)
{
Field *field= ((Item_field *)item)->field;
- if (field_example->field_cast_type() != field->field_cast_type())
+ /* Can 'field_example' field store data of the column? */
+ if ((use_new_field=
+ (!field->field_cast_compatible(field_example->field_cast_type()) ||
+ !is_attr_compatible(item, this))))
{
- 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;
- }
+ /*
+ The old field can't store value of the new field.
+ Check if the new field can store value of the old one.
+ */
+ use_expression_type|=
+ (!field_example->field_cast_compatible(field->field_cast_type()) ||
+ !is_attr_compatible(this, item));
}
}
-
- // 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)));
+ else if (field_example || item_is_a_field)
+ {
/*
- It is safe assign pointer on field, because it will be used just after
- all JOIN::prepare calls and before any SELECT execution
+ Expression types can't be mixed with field types, we have to use
+ expression types.
*/
- if (skip_store_field || item->type() != Item::FIELD_ITEM)
+ use_new_field= 1; // make next if test easier
+ use_expression_type= 1;
+ }
+
+ /* Check whether size/type of the result item should be changed */
+ if (use_new_field ||
+ (new_result_type != item_type) || (new_length > max_length) ||
+ (!maybe_null && item->maybe_null) ||
+ (item_type == STRING_RESULT &&
+ collation.collation != item->collation.collation))
+ {
+ const char *old_cs,*old_derivation;
+ if (use_expression_type || !item_is_a_field)
field_example= 0;
else
+ {
+ /*
+ It is safe to assign a pointer to field here, because it will be used
+ before any table is closed.
+ */
field_example= ((Item_field*) item)->field;
+ }
- const char *old_cs= collation.collation->name,
- *old_derivation= collation.derivation_name();
+ 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),
@@ -3126,18 +3194,18 @@ bool Item_type_holder::join_types(THD *thd, Item *item)
max_length= max(max_length, new_length);
decimals= max(decimals, item->decimals);
maybe_null|= item->maybe_null;
- item_type= new_type;
+ item_type= new_result_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:
@@ -3173,6 +3241,14 @@ String *Item_type_holder::val_str(String*)
return 0;
}
+void Item_result_field::cleanup()
+{
+ DBUG_ENTER("Item_result_field::cleanup()");
+ Item::cleanup();
+ result_field= 0;
+ DBUG_VOID_RETURN;
+}
+
/*****************************************************************************
** Instantiate templates
*****************************************************************************/