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
......@@ -375,3 +375,38 @@ rand() cast(rand(10)*@precision as unsigned integer) cast(rand(a)*@precision as
- 9647622201 3845601374 6211931236
drop table t1;
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
);
insert into t5( f1, f2, f3) values( 9, 'recreated table', 9);
execute stmt2 ;
ERROR 42S22: Unknown column 't5.a' in 'field list'
ERROR 42S22: Unknown column 'test.t5.a' in 'field list'
drop table t5 ;
prepare stmt1 from ' select * from t1 where a <= 2 ' ;
execute stmt1 ;
......
......@@ -390,3 +390,28 @@ set @var=3;
execute stmt using @var;
drop table t1;
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)
:Item_ident(NullS, f->table_name, f->field_name)
{
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)
{
/*
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);
collation.set(DERIVATION_IMPLICIT);
fixed= 1;
}
// Constructor need to process subselect with temporary tables (see Item)
......@@ -381,6 +403,7 @@ 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;
}
const char *Item_ident::full_name() const
......@@ -1374,8 +1397,8 @@ bool Item_field::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref)
field->query_id=thd->query_id;
table->used_fields++;
table->used_keys.intersect(field->part_of_key);
fixed= 1;
}
fixed= 1;
return 0;
}
......@@ -2120,7 +2143,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;
}
......@@ -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,
tmp_field->table, &my_charset_bin));
}
fixed= 1;
return 0;
}
......
......@@ -310,6 +310,7 @@ class Item_num: public Item
class st_select_lex;
class Item_ident :public Item
{
protected:
/*
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
......@@ -347,7 +348,6 @@ class Item_ident :public Item
class Item_field :public Item_ident
{
void set_field(Field *field);
public:
Field *field,*result_field;
......@@ -356,13 +356,21 @@ class Item_field :public Item_ident
:Item_ident(db_par,table_name_par,field_name_par),
field(0), result_field(0)
{ 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);
/*
Constructor used inside setup_wild(), ensures that field and table
names will live as long as Item_field (important in prep. stmt.)
Constructor used inside setup_wild(), ensures that field, table,
and database names will live as long as Item_field (this is important
in prepared statements).
*/
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);
enum Type type() const { return FIELD_ITEM; }
bool eq(const Item *item, bool binary_cmp) const;
......@@ -373,6 +381,7 @@ class Item_field :public Item_ident
longlong val_int_result();
String *str_result(String* tmp);
bool send(Protocol *protocol, String *str_arg);
void set_field(Field *field);
bool fix_fields(THD *, struct st_table_list *, Item **);
void make_field(Send_field *tmp_field);
int save_in_field(Field *field,bool no_conversions);
......
......@@ -2655,8 +2655,8 @@ int setup_conds(THD *thd,TABLE_LIST *tables,COND **conds)
strlen(t1_field_name), 0, 0,
&not_used_field_index)))
{
Item_func_eq *tmp=new Item_func_eq(new Item_field(*t1_field),
new Item_field(t2_field));
Item_func_eq *tmp=new Item_func_eq(new Item_field(thd, *t1_field),
new Item_field(thd, t2_field));
if (!tmp)
goto err;
/* Mark field used for table cache */
......
......@@ -464,6 +464,8 @@ class Item_arena
inline bool is_stmt_prepare() const { return (int)state < (int)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
{ return state == CONVENTIONAL_EXECUTION; }
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,
if (first_select->next_select())
{
// it is not single select
/* This is not a single select */
/*
Check that it was possible to aggregate
all collations together for UNION.
*/
List_iterator_fast<Item> tp(types);
Item_arena *arena= thd->current_arena;
Item *type;
while ((type= tp++))
{
......@@ -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;
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())
thd->set_n_backup_item_arena(arena, &backup);
Field **field;
......@@ -345,6 +349,20 @@ int st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
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
first_select->braces= 0; // remove our changes
......
......@@ -164,6 +164,7 @@ struct st_table {
MEM_ROOT mem_root;
GRANT_INFO grant;
/* A pair "database_name\0table_name\0", widely used as simply a db name */
char *table_cache_key;
char *table_name,*real_name,*path;
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