Commit 9a4ffb5a authored by timour@mysql.com's avatar timour@mysql.com

WL#1972 - manual merge with latest bk source tree

parent 71c4cc4a
...@@ -46,11 +46,11 @@ void item_init(void) ...@@ -46,11 +46,11 @@ void item_init(void)
} }
Item::Item(): Item::Item():
name_length(0), fixed(0) name_length(0), fixed(0),
collation(default_charset(), DERIVATION_COERCIBLE)
{ {
marker= 0; marker= 0;
maybe_null=null_value=with_sum_func=unsigned_flag=0; maybe_null=null_value=with_sum_func=unsigned_flag=0;
collation.set(default_charset(), DERIVATION_COERCIBLE);
name= 0; name= 0;
decimals= 0; max_length= 0; decimals= 0; max_length= 0;
...@@ -114,13 +114,31 @@ void Item::cleanup() ...@@ -114,13 +114,31 @@ void Item::cleanup()
DBUG_PRINT("info", ("Item: 0x%lx", this)); DBUG_PRINT("info", ("Item: 0x%lx", this));
DBUG_PRINT("info", ("Type: %d", (int)type())); DBUG_PRINT("info", ("Type: %d", (int)type()));
fixed=0; fixed=0;
marker= 0;
DBUG_VOID_RETURN; DBUG_VOID_RETURN;
} }
/*
cleanup() item if it is 'fixed'
SYNOPSIS
cleanup_processor()
arg - a dummy parameter, is not used here
*/
bool Item::cleanup_processor(byte *arg)
{
if (fixed)
cleanup();
return FALSE;
}
Item_ident::Item_ident(const char *db_name_par,const char *table_name_par, Item_ident::Item_ident(const char *db_name_par,const char *table_name_par,
const char *field_name_par) const char *field_name_par)
:orig_db_name(db_name_par), orig_table_name(table_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), db_name(db_name_par), table_name(table_name_par),
field_name(field_name_par), cached_field_index(NO_CACHED_FIELD_INDEX), field_name(field_name_par), cached_field_index(NO_CACHED_FIELD_INDEX),
cached_table(0), depended_from(0) cached_table(0), depended_from(0)
...@@ -134,7 +152,6 @@ Item_ident::Item_ident(THD *thd, Item_ident *item) ...@@ -134,7 +152,6 @@ Item_ident::Item_ident(THD *thd, Item_ident *item)
orig_db_name(item->orig_db_name), orig_db_name(item->orig_db_name),
orig_table_name(item->orig_table_name), orig_table_name(item->orig_table_name),
orig_field_name(item->orig_field_name), orig_field_name(item->orig_field_name),
changed_during_fix_field(0),
db_name(item->db_name), db_name(item->db_name),
table_name(item->table_name), table_name(item->table_name),
field_name(item->field_name), field_name(item->field_name),
...@@ -151,11 +168,6 @@ void Item_ident::cleanup() ...@@ -151,11 +168,6 @@ void Item_ident::cleanup()
table_name, orig_table_name, table_name, orig_table_name,
field_name, orig_field_name)); field_name, orig_field_name));
Item::cleanup(); Item::cleanup();
if (changed_during_fix_field)
{
*changed_during_fix_field= this;
changed_during_fix_field= 0;
}
db_name= orig_db_name; db_name= orig_db_name;
table_name= orig_table_name; table_name= orig_table_name;
field_name= orig_field_name; field_name= orig_field_name;
...@@ -267,6 +279,41 @@ bool Item::eq(const Item *item, bool binary_cmp) const ...@@ -267,6 +279,41 @@ bool Item::eq(const Item *item, bool binary_cmp) const
} }
Item *Item::safe_charset_converter(CHARSET_INFO *tocs)
{
/*
Don't allow automatic conversion to non-Unicode charsets,
as it potentially loses data.
*/
if (!(tocs->state & MY_CS_UNICODE))
return NULL; // safe conversion is not possible
return new Item_func_conv_charset(this, tocs);
}
Item *Item_string::safe_charset_converter(CHARSET_INFO *tocs)
{
Item_string *conv;
uint conv_errors;
String tmp, cstr, *ostr= val_str(&tmp);
cstr.copy(ostr->ptr(), ostr->length(), ostr->charset(), tocs, &conv_errors);
if (conv_errors || !(conv= new Item_string(cstr.ptr(), cstr.length(),
cstr.charset(),
collation.derivation)))
{
/*
Safe conversion is not possible (or EOM).
We could not convert a string into the requested character set
without data loss. The target charset does not cover all the
characters from the string. Operation cannot be done correctly.
*/
return NULL;
}
conv->str_value.copy();
return conv;
}
bool Item_string::eq(const Item *item, bool binary_cmp) const bool Item_string::eq(const Item *item, bool binary_cmp) const
{ {
if (type() == item->type()) if (type() == item->type())
...@@ -361,7 +408,43 @@ Item_splocal::type() const ...@@ -361,7 +408,43 @@ Item_splocal::type() const
} }
bool DTCollation::aggregate(DTCollation &dt, bool superset_conversion)
/*
Aggregate two collations together taking
into account their coercibility (aka derivation):
0 == DERIVATION_EXPLICIT - an explicitely written COLLATE clause
1 == DERIVATION_NONE - a mix of two different collations
2 == DERIVATION_IMPLICIT - a column
3 == DERIVATION_COERCIBLE - a string constant
The most important rules are:
1. If collations are the same:
chose this collation, and the strongest derivation.
2. If collations are different:
- Character sets may differ, but only if conversion without
data loss is possible. The caller provides flags whether
character set conversion attempts should be done. If no
flags are substituted, then the character sets must be the same.
Currently processed flags are:
MY_COLL_ALLOW_SUPERSET_CONV - allow conversion to a superset
MY_COLL_ALLOW_COERCIBLE_CONV - allow conversion of a coercible value
- two EXPLICIT collations produce an error, e.g. this is wrong:
CONCAT(expr1 collate latin1_swedish_ci, expr2 collate latin1_german_ci)
- the side with smaller derivation value wins,
i.e. a column is stronger than a string constant,
an explicit COLLATE clause is stronger than a column.
- if derivations are the same, we have DERIVATION_NONE,
we'll wait for an explicit COLLATE clause which possibly can
come from another argument later: for example, this is valid,
but we don't know yet when collecting the first two arguments:
CONCAT(latin1_swedish_ci_column,
latin1_german1_ci_column,
expr COLLATE latin1_german2_ci)
*/
bool DTCollation::aggregate(DTCollation &dt, uint flags)
{ {
nagg++; nagg++;
if (!my_charset_same(collation, dt.collation)) if (!my_charset_same(collation, dt.collation))
...@@ -392,26 +475,35 @@ bool DTCollation::aggregate(DTCollation &dt, bool superset_conversion) ...@@ -392,26 +475,35 @@ bool DTCollation::aggregate(DTCollation &dt, bool superset_conversion)
else else
; // Do nothing ; // Do nothing
} }
else if (superset_conversion) else if ((flags & MY_COLL_ALLOW_SUPERSET_CONV) &&
{ derivation < dt.derivation &&
if (derivation < dt.derivation &&
collation->state & MY_CS_UNICODE) collation->state & MY_CS_UNICODE)
; // Do nothing {
else if (dt.derivation < derivation && // Do nothing
}
else if ((flags & MY_COLL_ALLOW_SUPERSET_CONV) &&
dt.derivation < derivation &&
dt.collation->state & MY_CS_UNICODE) dt.collation->state & MY_CS_UNICODE)
{ {
set(dt); set(dt);
strong= nagg; strong= nagg;
} }
else else if ((flags & MY_COLL_ALLOW_COERCIBLE_CONV) &&
derivation < dt.derivation &&
dt.derivation == DERIVATION_COERCIBLE)
{ {
// Cannot convert to superset // Do nothing;
set(0, DERIVATION_NONE);
return 1;
} }
else if ((flags & MY_COLL_ALLOW_COERCIBLE_CONV) &&
dt.derivation < derivation &&
derivation == DERIVATION_COERCIBLE)
{
set(dt);
strong= nagg;
} }
else else
{ {
// Cannot apply conversion
set(0, DERIVATION_NONE); set(0, DERIVATION_NONE);
return 1; return 1;
} }
...@@ -452,19 +544,47 @@ Item_field::Item_field(Field *f) ...@@ -452,19 +544,47 @@ Item_field::Item_field(Field *f)
have_privileges(0), any_privileges(0) have_privileges(0), any_privileges(0)
{ {
set_field(f); 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_field::Item_field(THD *thd, Field *f)
:Item_ident(NullS, thd->strdup(f->table_name), :Item_ident(f->table->table_cache_key, f->table_name, f->field_name),
thd->strdup(f->field_name)),
item_equal(0), no_const_subst(0), item_equal(0), no_const_subst(0),
have_privileges(0), any_privileges(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); set_field(f);
collation.set(DERIVATION_IMPLICIT);
fixed= 1;
} }
// Constructor need to process subselect with temporary tables (see Item) // Constructor need to process subselect with temporary tables (see Item)
...@@ -491,6 +611,21 @@ void Item_field::set_field(Field *field_par) ...@@ -491,6 +611,21 @@ void Item_field::set_field(Field *field_par)
db_name=field_par->table->table_cache_key; db_name=field_par->table->table_cache_key;
unsigned_flag=test(field_par->flags & UNSIGNED_FLAG); unsigned_flag=test(field_par->flags & UNSIGNED_FLAG);
collation.set(field_par->charset(), DERIVATION_IMPLICIT); 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 const char *Item_ident::full_name() const
...@@ -793,6 +928,12 @@ String *Item_null::val_str(String *str) ...@@ -793,6 +928,12 @@ String *Item_null::val_str(String *str)
} }
Item *Item_null::safe_charset_converter(CHARSET_INFO *tocs)
{
collation.set(tocs);
return this;
}
/*********************** Item_param related ******************************/ /*********************** Item_param related ******************************/
/* /*
...@@ -888,7 +1029,9 @@ bool Item_param::set_str(const char *str, ulong length) ...@@ -888,7 +1029,9 @@ bool Item_param::set_str(const char *str, ulong length)
Assign string with no conversion: data is converted only after it's Assign string with no conversion: data is converted only after it's
been written to the binary log. been written to the binary log.
*/ */
if (str_value.copy(str, length, &my_charset_bin, &my_charset_bin)) uint dummy_errors;
if (str_value.copy(str, length, &my_charset_bin, &my_charset_bin,
&dummy_errors))
DBUG_RETURN(TRUE); DBUG_RETURN(TRUE);
state= STRING_VALUE; state= STRING_VALUE;
maybe_null= 0; maybe_null= 0;
...@@ -1043,7 +1186,7 @@ int Item_param::save_in_field(Field *field, bool no_conversions) ...@@ -1043,7 +1186,7 @@ int Item_param::save_in_field(Field *field, bool no_conversions)
return field->store(str_value.ptr(), str_value.length(), return field->store(str_value.ptr(), str_value.length(),
str_value.charset()); str_value.charset());
case NULL_VALUE: case NULL_VALUE:
return set_field_to_null(field); return set_field_to_null_with_conversions(field, no_conversions);
case NO_VALUE: case NO_VALUE:
default: default:
DBUG_ASSERT(0); DBUG_ASSERT(0);
...@@ -1146,9 +1289,10 @@ String *Item_param::val_str(String* str) ...@@ -1146,9 +1289,10 @@ String *Item_param::val_str(String* str)
return str; return str;
case TIME_VALUE: case TIME_VALUE:
{ {
if (str->reserve(MAX_DATE_REP_LENGTH)) if (str->reserve(MAX_DATE_STRING_REP_LENGTH))
break; 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; return str;
} }
case NULL_VALUE: case NULL_VALUE:
...@@ -1178,24 +1322,19 @@ const String *Item_param::query_val_str(String* str) const ...@@ -1178,24 +1322,19 @@ const String *Item_param::query_val_str(String* str) const
case TIME_VALUE: case TIME_VALUE:
{ {
char *buf, *ptr; char *buf, *ptr;
String tmp;
str->length(0); str->length(0);
/* /*
TODO: in case of error we need to notify replication TODO: in case of error we need to notify replication
that binary log contains wrong statement that binary log contains wrong statement
*/ */
if (str->reserve(MAX_DATE_REP_LENGTH+3)) if (str->reserve(MAX_DATE_STRING_REP_LENGTH+3))
break; break;
/* Create date string inplace */ /* Create date string inplace */
buf= str->c_ptr_quick(); buf= str->c_ptr_quick();
ptr= buf; ptr= buf;
*ptr++= '\''; *ptr++= '\'';
tmp.set(ptr, MAX_DATE_REP_LENGTH, &my_charset_bin); ptr+= (uint) my_TIME_to_str(&value.time, ptr);
tmp.length(0);
TIME_to_string(&value.time, &tmp);
ptr+= tmp.length();
*ptr++= '\''; *ptr++= '\'';
str->length((uint32) (ptr - buf)); str->length((uint32) (ptr - buf));
break; break;
...@@ -1249,6 +1388,10 @@ bool Item_param::convert_str_value(THD *thd) ...@@ -1249,6 +1388,10 @@ bool Item_param::convert_str_value(THD *thd)
value.cs_info.character_set_client, value.cs_info.character_set_client,
value.cs_info.final_character_set_of_str_value); value.cs_info.final_character_set_of_str_value);
} }
else
str_value.set_charset(value.cs_info.final_character_set_of_str_value);
/* Here str_value is guaranteed to be in final_character_set_of_str_value */
max_length= str_value.length(); max_length= str_value.length();
decimals= 0; decimals= 0;
/* /*
...@@ -1373,7 +1516,7 @@ bool Item_ref_null_helper::get_date(TIME *ltime, uint fuzzydate) ...@@ -1373,7 +1516,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, static void mark_as_dependent(THD *thd, SELECT_LEX *last, SELECT_LEX *current,
Item_ident *item) 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; item->depended_from= last;
current->mark_as_dependent(last); current->mark_as_dependent(last);
if (thd->lex->describe & DESCRIBE_EXTENDED) if (thd->lex->describe & DESCRIBE_EXTENDED)
...@@ -1390,6 +1533,8 @@ static void mark_as_dependent(THD *thd, SELECT_LEX *last, SELECT_LEX *current, ...@@ -1390,6 +1533,8 @@ static void mark_as_dependent(THD *thd, SELECT_LEX *last, SELECT_LEX *current,
} }
/* /*
Search a GROUP BY clause for a field with a certain name. Search a GROUP BY clause for a field with a certain name.
...@@ -1534,13 +1679,14 @@ resolve_ref_in_select_and_group(THD *thd, Item_ident *ref, SELECT_LEX *select) ...@@ -1534,13 +1679,14 @@ resolve_ref_in_select_and_group(THD *thd, Item_ident *ref, SELECT_LEX *select)
ORDER *group_list= (ORDER*) select->group_list.first; ORDER *group_list= (ORDER*) select->group_list.first;
bool ambiguous_fields= FALSE; bool ambiguous_fields= FALSE;
uint counter; uint counter;
bool not_used;
/* /*
Search for a column or derived column named as 'ref' in the SELECT Search for a column or derived column named as 'ref' in the SELECT
clause of the current select. clause of the current select.
*/ */
if (!(select_ref= find_item_in_list(ref, *(select->get_item_list()), &counter, if (!(select_ref= find_item_in_list(ref, *(select->get_item_list()), &counter,
REPORT_EXCEPT_NOT_FOUND))) REPORT_EXCEPT_NOT_FOUND, &not_used)))
return NULL; /* Some error occurred. */ return NULL; /* Some error occurred. */
/* If this is a non-aggregated field inside HAVING, search in GROUP BY. */ /* If this is a non-aggregated field inside HAVING, search in GROUP BY. */
...@@ -1687,10 +1833,10 @@ bool Item_field::fix_fields(THD *thd, TABLE_LIST *tables, Item **reference) ...@@ -1687,10 +1833,10 @@ bool Item_field::fix_fields(THD *thd, TABLE_LIST *tables, Item **reference)
enum_parsing_place place= prev_subselect_item->parsing_place; enum_parsing_place place= prev_subselect_item->parsing_place;
/* /*
Check table fields only if the subquery is used somewhere out of Check table fields only if the subquery is used somewhere out of
HAVING or SELECT list, or the outer SELECT does not use grouping HAVING, or the outer SELECT does not use grouping (i.e. tables are
(i.e. tables are accessible). accessible).
*/ */
if (((place != IN_HAVING && place != SELECT_LIST) || if ((place != IN_HAVING ||
(outer_sel->with_sum_func == 0 && (outer_sel->with_sum_func == 0 &&
outer_sel->group_list.elements == 0)) && outer_sel->group_list.elements == 0)) &&
(from_field= find_field_in_tables(thd, this, table_list, (from_field= find_field_in_tables(thd, this, table_list,
...@@ -1762,15 +1908,14 @@ bool Item_field::fix_fields(THD *thd, TABLE_LIST *tables, Item **reference) ...@@ -1762,15 +1908,14 @@ bool Item_field::fix_fields(THD *thd, TABLE_LIST *tables, Item **reference)
} }
else if (ref != not_found_item) else if (ref != not_found_item)
{ {
/* Should be checked in resolve_ref_in_select_and_group(). */ /* Should have been checked in resolve_ref_in_select_and_group(). */
DBUG_ASSERT(*ref && (*ref)->fixed); DBUG_ASSERT(*ref && (*ref)->fixed);
Item_ref *rf; Item_ref *rf= new Item_ref(last->ref_pointer_array + counter,
*reference= rf= new Item_ref(ref, reference, (char *) table_name, (char *)table_name, (char *)field_name);
(char *) field_name);
register_item_tree_changing(reference);
if (!rf) if (!rf)
return TRUE; return TRUE;
thd->change_item_tree(reference, rf);
/* /*
rf is Item_ref => never substitute other items (in this case) rf is Item_ref => never substitute other items (in this case)
during fix_fields() => we can use rf after fix_fields() during fix_fields() => we can use rf after fix_fields()
...@@ -1787,12 +1932,11 @@ bool Item_field::fix_fields(THD *thd, TABLE_LIST *tables, Item **reference) ...@@ -1787,12 +1932,11 @@ bool Item_field::fix_fields(THD *thd, TABLE_LIST *tables, Item **reference)
if (last->having_fix_field) if (last->having_fix_field)
{ {
Item_ref *rf; Item_ref *rf;
*reference= rf= new Item_ref(reference, *reference, rf= new Item_ref((cached_table->db[0] ? cached_table->db : 0),
(cached_table->db[0]?cached_table->db:0), (char*) cached_table->alias, (char*) field_name);
(char *)cached_table->alias,
(char *)field_name);
if (!rf) if (!rf)
return TRUE; return TRUE;
thd->change_item_tree(reference, rf);
/* /*
rf is Item_ref => never substitute other items (in this case) rf is Item_ref => never substitute other items (in this case)
during fix_fields() => we can use rf after fix_fields() during fix_fields() => we can use rf after fix_fields()
...@@ -1861,6 +2005,14 @@ bool Item_field::fix_fields(THD *thd, TABLE_LIST *tables, Item **reference) ...@@ -1861,6 +2005,14 @@ bool Item_field::fix_fields(THD *thd, TABLE_LIST *tables, Item **reference)
return FALSE; return FALSE;
} }
Item *Item_field::safe_charset_converter(CHARSET_INFO *tocs)
{
no_const_subst= 1;
return Item::safe_charset_converter(tocs);
}
void Item_field::cleanup() void Item_field::cleanup()
{ {
DBUG_ENTER("Item_field::cleanup"); DBUG_ENTER("Item_field::cleanup");
...@@ -1871,6 +2023,7 @@ void Item_field::cleanup() ...@@ -1871,6 +2023,7 @@ void Item_field::cleanup()
I.e. we can drop 'field'. I.e. we can drop 'field'.
*/ */
field= result_field= 0; field= result_field= 0;
DBUG_VOID_RETURN;
} }
/* /*
...@@ -1915,7 +2068,8 @@ Item_equal *Item_field::find_item_equal(COND_EQUAL *cond_equal) ...@@ -1915,7 +2068,8 @@ Item_equal *Item_field::find_item_equal(COND_EQUAL *cond_equal)
/* /*
Set a pointer to the multiple equality the field reference belongs to (if any) Set a pointer to the multiple equality the field reference belongs to
(if any)
SYNOPSIS SYNOPSIS
equal_fields_propagator() equal_fields_propagator()
...@@ -1954,7 +2108,21 @@ Item *Item_field::equal_fields_propagator(byte *arg) ...@@ -1954,7 +2108,21 @@ Item *Item_field::equal_fields_propagator(byte *arg)
/* /*
Set a pointer to the multiple equality the field reference belongs to (if any) Mark the item to not be part of substitution if it's not a binary item
See comments in Arg_comparator::set_compare_func() for details
*/
Item *Item_field::set_no_const_sub(byte *arg)
{
if (field->charset() != &my_charset_bin)
no_const_subst=1;
return this;
}
/*
Set a pointer to the multiple equality the field reference belongs to
(if any)
SYNOPSIS SYNOPSIS
replace_equal_field_processor() replace_equal_field_processor()
...@@ -1990,6 +2158,7 @@ bool Item_field::replace_equal_field_processor(byte *arg) ...@@ -1990,6 +2158,7 @@ bool Item_field::replace_equal_field_processor(byte *arg)
return 0; return 0;
} }
void Item::init_make_field(Send_field *tmp_field, void Item::init_make_field(Send_field *tmp_field,
enum enum_field_types field_type) enum enum_field_types field_type)
{ {
...@@ -2000,7 +2169,9 @@ void Item::init_make_field(Send_field *tmp_field, ...@@ -2000,7 +2169,9 @@ void Item::init_make_field(Send_field *tmp_field,
tmp_field->table_name= empty_name; tmp_field->table_name= empty_name;
tmp_field->col_name= name; tmp_field->col_name= name;
tmp_field->charsetnr= collation.collation->number; 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->type=field_type;
tmp_field->length=max_length; tmp_field->length=max_length;
tmp_field->decimals=decimals; tmp_field->decimals=decimals;
...@@ -2629,15 +2800,15 @@ bool Item_ref::fix_fields(THD *thd, TABLE_LIST *tables, Item **reference) ...@@ -2629,15 +2800,15 @@ bool Item_ref::fix_fields(THD *thd, TABLE_LIST *tables, Item **reference)
enum_parsing_place place= prev_subselect_item->parsing_place; enum_parsing_place place= prev_subselect_item->parsing_place;
/* /*
Check table fields only if the subquery is used somewhere out of Check table fields only if the subquery is used somewhere out of
HAVING or SELECT list, or the outer SELECT does not use grouping HAVING or the outer SELECT does not use grouping (i.e. tables are
(i.e. tables are accessible). accessible).
TODO: TODO:
Here we could first find the field anyway, and then test this Here we could first find the field anyway, and then test this
condition, so that we can give a better error message - condition, so that we can give a better error message -
ER_WRONG_FIELD_WITH_GROUP, instead of the less informative ER_WRONG_FIELD_WITH_GROUP, instead of the less informative
ER_BAD_FIELD_ERROR which we produce now. ER_BAD_FIELD_ERROR which we produce now.
*/ */
if (((place != IN_HAVING && place != SELECT_LIST) || if ((place != IN_HAVING ||
(!outer_sel->with_sum_func && (!outer_sel->with_sum_func &&
outer_sel->group_list.elements == 0))) outer_sel->group_list.elements == 0)))
{ {
...@@ -2679,19 +2850,24 @@ bool Item_ref::fix_fields(THD *thd, TABLE_LIST *tables, Item **reference) ...@@ -2679,19 +2850,24 @@ bool Item_ref::fix_fields(THD *thd, TABLE_LIST *tables, Item **reference)
{ {
my_printf_error(ER_BAD_FIELD_ERROR, ER(ER_BAD_FIELD_ERROR), MYF(0), my_printf_error(ER_BAD_FIELD_ERROR, ER(ER_BAD_FIELD_ERROR), MYF(0),
this->full_name(), current_thd->where); this->full_name(), current_thd->where);
ref= 0; ref= 0; // Safety
return TRUE; return TRUE;
} }
else if (from_field != not_found_field) if (from_field != not_found_field)
{ {
ref= 0; // To prevent "delete *ref;" on ~Item_ref() 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 (from_field != view_ref_found) if (from_field != view_ref_found)
{ {
Item_field* fld= new Item_field(from_field); Item_field* fld;
if (!((*reference)= fld)) if (!(fld= new Item_field(tmp)))
return TRUE; return TRUE;
mark_as_dependent(thd, last, current_sel, fld); thd->change_item_tree(reference, fld);
register_item_tree_changing(reference); mark_as_dependent(thd, last, thd->lex->current_select, fld);
return FALSE; return FALSE;
} }
/* /*
...@@ -2757,8 +2933,6 @@ void Item_ref::cleanup() ...@@ -2757,8 +2933,6 @@ void Item_ref::cleanup()
DBUG_ENTER("Item_ref::cleanup"); DBUG_ENTER("Item_ref::cleanup");
Item_ident::cleanup(); Item_ident::cleanup();
result_field= 0; result_field= 0;
if (hook_ptr)
*hook_ptr= orig_item;
DBUG_VOID_RETURN; DBUG_VOID_RETURN;
} }
...@@ -2881,7 +3055,6 @@ bool Item_default_value::fix_fields(THD *thd, ...@@ -2881,7 +3055,6 @@ bool Item_default_value::fix_fields(THD *thd,
def_field->move_field(def_field->table->default_values - def_field->move_field(def_field->table->default_values -
def_field->table->record[0]); def_field->table->record[0]);
set_field(def_field); set_field(def_field);
fixed= 1;
return 0; return 0;
} }
...@@ -2939,7 +3112,6 @@ bool Item_insert_value::fix_fields(THD *thd, ...@@ -2939,7 +3112,6 @@ bool Item_insert_value::fix_fields(THD *thd,
set_field(new Field_null(0, 0, Field::NONE, tmp_field->field_name, set_field(new Field_null(0, 0, Field::NONE, tmp_field->field_name,
tmp_field->table, &my_charset_bin)); tmp_field->table, &my_charset_bin));
} }
fixed= 1;
return 0; return 0;
} }
...@@ -3058,10 +3230,12 @@ Item_result item_cmp_type(Item_result a,Item_result b) ...@@ -3058,10 +3230,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()) 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 res_type=item_cmp_type(comp_item->result_type(),
item->result_type()); item->result_type());
char *name=item->name; // Alloced by sql_alloc char *name=item->name; // Alloced by sql_alloc
...@@ -3072,27 +3246,32 @@ Item *resolve_const_item(Item *item,Item *comp_item) ...@@ -3072,27 +3246,32 @@ Item *resolve_const_item(Item *item,Item *comp_item)
String tmp(buff,sizeof(buff),&my_charset_bin),*result; String tmp(buff,sizeof(buff),&my_charset_bin),*result;
result=item->val_str(&tmp); result=item->val_str(&tmp);
if (item->null_value) if (item->null_value)
return new Item_null(name); new_item= new Item_null(name);
uint length=result->length(); else
char *tmp_str=sql_strmake(result->ptr(),length); {
return new Item_string(name,tmp_str,length,result->charset()); 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(); longlong result=item->val_int();
uint length=item->max_length; uint length=item->max_length;
bool null_value=item->null_value; bool null_value=item->null_value;
return (null_value ? (Item*) new Item_null(name) : new_item= (null_value ? (Item*) new Item_null(name) :
(Item*) new Item_int(name,result,length)); (Item*) new Item_int(name, result, length));
} }
else else
{ // It must REAL_RESULT { // It must REAL_RESULT
double result=item->val(); double result=item->val();
uint length=item->max_length,decimals=item->decimals; uint length=item->max_length,decimals=item->decimals;
bool null_value=item->null_value; bool null_value=item->null_value;
return (null_value ? (Item*) new Item_null(name) : new_item= (null_value ? (Item*) new Item_null(name) : (Item*)
(Item*) new Item_real(name,result,decimals,length)); new Item_real(name, result, decimals, length));
} }
if (new_item)
thd->change_item_tree(ref, new_item);
} }
/* /*
...@@ -3319,6 +3498,7 @@ Item_type_holder::Item_type_holder(THD *thd, Item *item) ...@@ -3319,6 +3498,7 @@ Item_type_holder::Item_type_holder(THD *thd, Item *item)
else else
field_example= 0; field_example= 0;
max_length= real_length(item); max_length= real_length(item);
maybe_null= item->maybe_null;
collation.set(item->collation); collation.set(item->collation);
} }
...@@ -3336,62 +3516,90 @@ static Item_result type_convertor[4][4]= ...@@ -3336,62 +3516,90 @@ static Item_result type_convertor[4][4]=
{STRING_RESULT, REAL_RESULT, INT_RESULT, ROW_RESULT}, {STRING_RESULT, REAL_RESULT, INT_RESULT, ROW_RESULT},
{ROW_RESULT, ROW_RESULT, ROW_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) bool Item_type_holder::join_types(THD *thd, Item *item)
{ {
uint32 new_length= real_length(item); uint32 new_length= real_length(item);
bool change_field= 0, skip_store_field= 0; bool use_new_field= 0, use_expression_type= 0;
Item_result new_type= type_convertor[item_type][item->result_type()]; 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; 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=
if (!(change_field= (!field->field_cast_compatible(field_example->field_cast_type()) ||
field_example->field_cast_compatible(field->field_cast_type()))) !is_attr_compatible(item, this))))
{ {
/* /*
if old field can't store value of 'worse' new field we will make The old field can't store value of the new field.
decision about result field type based only on Item result type Check if the new field can store value of the old one.
*/ */
if (!field->field_cast_compatible(field_example->field_cast_type())) use_expression_type|=
skip_store_field= 1; (!field_example->field_cast_compatible(field->field_cast_type()) ||
!is_attr_compatible(this, item));
} }
} }
else if (field_example || item_is_a_field)
{
/*
Expression types can't be mixed with field types, we have to use
expression types.
*/
use_new_field= 1; // make next if test easier
use_expression_type= 1;
} }
// size/type should be changed /* Check whether size/type of the result item should be changed */
if (change_field || if (use_new_field ||
(new_type != item_type) || (new_result_type != item_type) || (new_length > max_length) ||
(max_length < new_length) ||
((new_type == INT_RESULT) &&
(decimals < item->decimals)) ||
(!maybe_null && item->maybe_null) || (!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 && (item_type == STRING_RESULT &&
new_type == STRING_RESULT && collation.collation != item->collation.collation))
!my_charset_same(collation.collation, {
item->collation.collation))); const char *old_cs,*old_derivation;
/* if (use_expression_type || !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
*/
if (skip_store_field || item->type() != Item::FIELD_ITEM)
field_example= 0; field_example= 0;
else 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; field_example= ((Item_field*) item)->field;
}
const char *old_cs= collation.collation->name, old_cs= collation.collation->name;
*old_derivation= collation.derivation_name(); old_derivation= collation.derivation_name();
if (item_type == STRING_RESULT && collation.aggregate(item->collation)) if (item_type == STRING_RESULT && collation.aggregate(item->collation))
{ {
my_error(ER_CANT_AGGREGATE_2COLLATIONS, MYF(0), my_error(ER_CANT_AGGREGATE_2COLLATIONS, MYF(0),
...@@ -3405,18 +3613,18 @@ bool Item_type_holder::join_types(THD *thd, Item *item) ...@@ -3405,18 +3613,18 @@ bool Item_type_holder::join_types(THD *thd, Item *item)
max_length= max(max_length, new_length); max_length= max(max_length, new_length);
decimals= max(decimals, item->decimals); decimals= max(decimals, item->decimals);
maybe_null|= item->maybe_null; maybe_null|= item->maybe_null;
item_type= new_type; item_type= new_result_type;
} }
DBUG_ASSERT(item_type != ROW_RESULT); DBUG_ASSERT(item_type != ROW_RESULT);
return 0; return 0;
} }
uint32 Item_type_holder::real_length(Item *item) uint32 Item_type_holder::real_length(Item *item)
{ {
if (item->type() == Item::FIELD_ITEM) if (item->type() == Item::FIELD_ITEM)
{
return ((Item_field *)item)->max_disp_length(); return ((Item_field *)item)->max_disp_length();
}
switch (item->result_type()) switch (item->result_type())
{ {
case STRING_RESULT: case STRING_RESULT:
...@@ -3452,6 +3660,14 @@ String *Item_type_holder::val_str(String*) ...@@ -3452,6 +3660,14 @@ String *Item_type_holder::val_str(String*)
return 0; return 0;
} }
void Item_result_field::cleanup()
{
DBUG_ENTER("Item_result_field::cleanup()");
Item::cleanup();
result_field= 0;
DBUG_VOID_RETURN;
}
/***************************************************************************** /*****************************************************************************
** Instantiate templates ** Instantiate templates
*****************************************************************************/ *****************************************************************************/
...@@ -3460,5 +3676,6 @@ String *Item_type_holder::val_str(String*) ...@@ -3460,5 +3676,6 @@ String *Item_type_holder::val_str(String*)
template class List<Item>; template class List<Item>;
template class List_iterator<Item>; template class List_iterator<Item>;
template class List_iterator_fast<Item>; template class List_iterator_fast<Item>;
template class List_iterator_fast<Item_field>;
template class List<List_item>; template class List<List_item>;
#endif #endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment