Commit a7c52d75 authored by konstantin@mysql.com's avatar konstantin@mysql.com

A fix and test case for Bug#6050 "EXECUTE stmt reports ambiguous field

names with ident. tables fr. diff. schemata": revise all uses of
Item_field and make them prepared-statements friendly when necessary.
parent b686f98f
Branches unavailable
Tags unavailable
...@@ -375,3 +375,38 @@ rand() cast(rand(10)*@precision as unsigned integer) cast(rand(a)*@precision as ...@@ -375,3 +375,38 @@ rand() cast(rand(10)*@precision as unsigned integer) cast(rand(a)*@precision as
- 9647622201 3845601374 6211931236 - 9647622201 3845601374 6211931236
drop table t1; drop table t1;
deallocate prepare stmt; deallocate prepare stmt;
create database mysqltest1;
create table t1 (a int);
create table mysqltest1.t1 (a int);
select * from t1, mysqltest1.t1;
a a
prepare stmt from "select * from t1, mysqltest1.t1";
execute stmt;
a a
execute stmt;
a a
execute stmt;
a a
drop table t1;
drop table mysqltest1.t1;
drop database mysqltest1;
deallocate prepare stmt;
select '1.1' as a, '1.2' as a UNION SELECT '2.1', '2.2';
a a
1.1 1.2
2.1 2.2
prepare stmt from
"select '1.1' as a, '1.2' as a UNION SELECT '2.1', '2.2'";
execute stmt;
a a
1.1 1.2
2.1 2.2
execute stmt;
a a
1.1 1.2
2.1 2.2
execute stmt;
a a
1.1 1.2
2.1 2.2
deallocate prepare stmt;
...@@ -184,7 +184,7 @@ f3 int ...@@ -184,7 +184,7 @@ f3 int
); );
insert into t5( f1, f2, f3) values( 9, 'recreated table', 9); insert into t5( f1, f2, f3) values( 9, 'recreated table', 9);
execute stmt2 ; execute stmt2 ;
ERROR 42S22: Unknown column 't5.a' in 'field list' ERROR 42S22: Unknown column 'test.t5.a' in 'field list'
drop table t5 ; drop table t5 ;
prepare stmt1 from ' select * from t1 where a <= 2 ' ; prepare stmt1 from ' select * from t1 where a <= 2 ' ;
execute stmt1 ; execute stmt1 ;
......
...@@ -390,3 +390,28 @@ set @var=3; ...@@ -390,3 +390,28 @@ set @var=3;
execute stmt using @var; execute stmt using @var;
drop table t1; drop table t1;
deallocate prepare stmt; deallocate prepare stmt;
#
# A test case for Bug#6050 "EXECUTE stmt reports ambiguous fieldnames with
# identical tables from different schemata"
# Check that field name resolving in prepared statements works OK.
#
create database mysqltest1;
create table t1 (a int);
create table mysqltest1.t1 (a int);
select * from t1, mysqltest1.t1;
prepare stmt from "select * from t1, mysqltest1.t1";
execute stmt;
execute stmt;
execute stmt;
drop table t1;
drop table mysqltest1.t1;
drop database mysqltest1;
deallocate prepare stmt;
select '1.1' as a, '1.2' as a UNION SELECT '2.1', '2.2';
prepare stmt from
"select '1.1' as a, '1.2' as a UNION SELECT '2.1', '2.2'";
execute stmt;
execute stmt;
execute stmt;
deallocate prepare stmt;
...@@ -348,17 +348,39 @@ Item_field::Item_field(Field *f) ...@@ -348,17 +348,39 @@ Item_field::Item_field(Field *f)
:Item_ident(NullS, f->table_name, f->field_name) :Item_ident(NullS, f->table_name, f->field_name)
{ {
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))
{ {
/*
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);
}
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)
...@@ -381,6 +403,7 @@ void Item_field::set_field(Field *field_par) ...@@ -381,6 +403,7 @@ 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;
} }
const char *Item_ident::full_name() const const char *Item_ident::full_name() const
...@@ -1374,8 +1397,8 @@ bool Item_field::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref) ...@@ -1374,8 +1397,8 @@ bool Item_field::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref)
field->query_id=thd->query_id; field->query_id=thd->query_id;
table->used_fields++; table->used_fields++;
table->used_keys.intersect(field->part_of_key); table->used_keys.intersect(field->part_of_key);
}
fixed= 1; fixed= 1;
}
return 0; return 0;
} }
...@@ -2120,7 +2143,6 @@ bool Item_default_value::fix_fields(THD *thd, ...@@ -2120,7 +2143,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;
} }
...@@ -2178,7 +2200,6 @@ bool Item_insert_value::fix_fields(THD *thd, ...@@ -2178,7 +2200,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;
} }
......
...@@ -310,6 +310,7 @@ class Item_num: public Item ...@@ -310,6 +310,7 @@ class Item_num: public Item
class st_select_lex; class st_select_lex;
class Item_ident :public Item class Item_ident :public Item
{ {
protected:
/* /*
We have to store initial values of db_name, table_name and field_name We have to store initial values of db_name, table_name and field_name
to be able to restore them during cleanup() because they can be to be able to restore them during cleanup() because they can be
...@@ -347,7 +348,6 @@ class Item_ident :public Item ...@@ -347,7 +348,6 @@ class Item_ident :public Item
class Item_field :public Item_ident class Item_field :public Item_ident
{ {
void set_field(Field *field);
public: public:
Field *field,*result_field; Field *field,*result_field;
...@@ -356,13 +356,21 @@ class Item_field :public Item_ident ...@@ -356,13 +356,21 @@ class Item_field :public Item_ident
:Item_ident(db_par,table_name_par,field_name_par), :Item_ident(db_par,table_name_par,field_name_par),
field(0), result_field(0) field(0), result_field(0)
{ collation.set(DERIVATION_IMPLICIT); } { collation.set(DERIVATION_IMPLICIT); }
// Constructor need to process subselect with temporary tables (see Item) /*
Constructor needed to process subselect with temporary tables (see Item)
*/
Item_field(THD *thd, Item_field *item); Item_field(THD *thd, Item_field *item);
/* /*
Constructor used inside setup_wild(), ensures that field and table Constructor used inside setup_wild(), ensures that field, table,
names will live as long as Item_field (important in prep. stmt.) and database names will live as long as Item_field (this is important
in prepared statements).
*/ */
Item_field(THD *thd, Field *field); Item_field(THD *thd, Field *field);
/*
If this constructor is used, fix_fields() won't work, because
db_name, table_name and column_name are unknown. It's necessary to call
set_field() before fix_fields() for all fields created this way.
*/
Item_field(Field *field); Item_field(Field *field);
enum Type type() const { return FIELD_ITEM; } enum Type type() const { return FIELD_ITEM; }
bool eq(const Item *item, bool binary_cmp) const; bool eq(const Item *item, bool binary_cmp) const;
...@@ -373,6 +381,7 @@ class Item_field :public Item_ident ...@@ -373,6 +381,7 @@ class Item_field :public Item_ident
longlong val_int_result(); longlong val_int_result();
String *str_result(String* tmp); String *str_result(String* tmp);
bool send(Protocol *protocol, String *str_arg); bool send(Protocol *protocol, String *str_arg);
void set_field(Field *field);
bool fix_fields(THD *, struct st_table_list *, Item **); bool fix_fields(THD *, struct st_table_list *, Item **);
void make_field(Send_field *tmp_field); void make_field(Send_field *tmp_field);
int save_in_field(Field *field,bool no_conversions); int save_in_field(Field *field,bool no_conversions);
......
...@@ -2655,8 +2655,8 @@ int setup_conds(THD *thd,TABLE_LIST *tables,COND **conds) ...@@ -2655,8 +2655,8 @@ int setup_conds(THD *thd,TABLE_LIST *tables,COND **conds)
strlen(t1_field_name), 0, 0, strlen(t1_field_name), 0, 0,
&not_used_field_index))) &not_used_field_index)))
{ {
Item_func_eq *tmp=new Item_func_eq(new Item_field(*t1_field), Item_func_eq *tmp=new Item_func_eq(new Item_field(thd, *t1_field),
new Item_field(t2_field)); new Item_field(thd, t2_field));
if (!tmp) if (!tmp)
goto err; goto err;
/* Mark field used for table cache */ /* Mark field used for table cache */
......
...@@ -464,6 +464,8 @@ class Item_arena ...@@ -464,6 +464,8 @@ class Item_arena
inline bool is_stmt_prepare() const { return (int)state < (int)PREPARED; } inline bool is_stmt_prepare() const { return (int)state < (int)PREPARED; }
inline bool is_first_stmt_execute() const { return state == PREPARED; } inline bool is_first_stmt_execute() const { return state == PREPARED; }
inline bool is_stmt_execute() const
{ return state == PREPARED || state == EXECUTED; }
inline bool is_conventional_execution() const inline bool is_conventional_execution() const
{ return state == CONVENTIONAL_EXECUTION; } { return state == CONVENTIONAL_EXECUTION; }
inline gptr alloc(unsigned int size) { return alloc_root(&mem_root,size); } inline gptr alloc(unsigned int size) { return alloc_root(&mem_root,size); }
......
...@@ -266,14 +266,14 @@ int st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result, ...@@ -266,14 +266,14 @@ int st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
if (first_select->next_select()) if (first_select->next_select())
{ {
/* This is not a single select */
// it is not single select
/* /*
Check that it was possible to aggregate Check that it was possible to aggregate
all collations together for UNION. all collations together for UNION.
*/ */
List_iterator_fast<Item> tp(types); List_iterator_fast<Item> tp(types);
Item_arena *arena= thd->current_arena;
Item *type; Item *type;
while ((type= tp++)) while ((type= tp++))
{ {
...@@ -305,7 +305,11 @@ int st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result, ...@@ -305,7 +305,11 @@ int st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
thd_arg->lex->current_select= lex_select_save; thd_arg->lex->current_select= lex_select_save;
if (!item_list.elements) if (!item_list.elements)
{ {
Item_arena *arena= thd->current_arena, backup; /*
We're in statement prepare or in execution
of a conventional statement.
*/
Item_arena backup;
if (arena->is_stmt_prepare()) if (arena->is_stmt_prepare())
thd->set_n_backup_item_arena(arena, &backup); thd->set_n_backup_item_arena(arena, &backup);
Field **field; Field **field;
...@@ -345,6 +349,20 @@ int st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result, ...@@ -345,6 +349,20 @@ int st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
fake_select_lex->table_list.empty(); fake_select_lex->table_list.empty();
} }
} }
else if (arena->is_stmt_execute())
{
/*
We're in execution of a prepared statement: reset field items
to point at fields from the created temporary table.
*/
List_iterator_fast<Item> it(item_list);
for (Field **field= table->field; *field; field++)
{
Item_field *item_field= (Item_field*) it++;
DBUG_ASSERT(item_field);
item_field->set_field(*field);
}
}
} }
else else
first_select->braces= 0; // remove our changes first_select->braces= 0; // remove our changes
......
...@@ -164,6 +164,7 @@ struct st_table { ...@@ -164,6 +164,7 @@ struct st_table {
MEM_ROOT mem_root; MEM_ROOT mem_root;
GRANT_INFO grant; GRANT_INFO grant;
/* A pair "database_name\0table_name\0", widely used as simply a db name */
char *table_cache_key; char *table_cache_key;
char *table_name,*real_name,*path; char *table_name,*real_name,*path;
uint key_length; /* Length of key */ uint key_length; /* Length of key */
......
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