Commit d2906f0e authored by unknown's avatar unknown

Fix for Bug #3307 "FLUSH TABLES sometimes breaks prepared statement

table resolution".
Added members to Item_ident for storing original db, table and field
names since those that set later from Field have shorter life-time 
than required by prep. stmt. So we need to restore original names in 
Item_ident::cleanup(). Also now using special construnctor for creation
of Item_field from Field object that ensures that table and field name 
have big enough life-time.

"Fix" for bug #2050 "10 to 1 performance drop with server 4.1.1"
Clean ups in implementation of caching of field number in table.
Added caching of table in which field is found in find_field_in_tables(). 


sql/item.cc:
  Added members to Item_ident for storing original db, table and field
  names since those that set later from Field have shorter life-time 
  than required by prep. stmt. So we need to restore original names in 
  Item_ident::cleanup().
  Added Item_ident::cached_table for caching table there we found 
  our field.
  Added special constructor for creation of Item_field from Field object
  that ensures that table and field name have big enough life-time.
sql/item.h:
  Added members to Item_ident for storing original db, table and field
  names since those that set later from Field have shorter life-time 
  than required by prep. stmt. So we need to restore original names in 
  Item_ident::cleanup().
  Changed type of Item_ident::cached_field_index from int to uint
  (and so NO_CACHED_FIELD_INDEX is UINT_MAX now) because we want to
  save one comparison in find_field_in_table().
  Added Item_ident::cached_table for caching table there we found 
  our field.
  Added special constructor for creation of Item_field from Field object
  that ensures that table and field name have big enough life-time.
sql/mysql_priv.h:
  Changed type of cached_field_index_ptr from int* to uint*
  (and so NO_CACHED_FIELD_INDEX is UINT_MAX now) because we want to
  save one comparison in find_field_in_table().
sql/sql_acl.cc:
  Changed type of find_field_in_table() paremeter to make it 
  faster.
sql/sql_base.cc:
  find_field_in_table(): small optimization and soime clean ups in 
   caching of field index.
  find_field_in_tables(): added aggresive caching of table in which
   field is found in Item_ident::cached_table.
  insert_fields():
   using special construnctor for creation of Item_field from Field 
   object that ensures that table and field name have big enough life-time.
parent f6a8ec5f
......@@ -103,9 +103,12 @@ void Item::print_item_w_name(String *str)
Item_ident::Item_ident(const char *db_name_par,const char *table_name_par,
const char *field_name_par)
:changed_during_fix_field(0), db_name(db_name_par),
table_name(table_name_par), field_name(field_name_par),
cached_field_index(-1), depended_from(0)
:
orig_db_name(db_name_par), orig_table_name(table_name_par),
orig_field_name(field_name_par), changed_during_fix_field(0),
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)
{
name = (char*) field_name_par;
}
......@@ -113,11 +116,15 @@ Item_ident::Item_ident(const char *db_name_par,const char *table_name_par,
// Constructor used by Item_field & Item_ref (see Item comment)
Item_ident::Item_ident(THD *thd, Item_ident *item)
:Item(thd, 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),
cached_field_index(item->cached_field_index),
cached_table(item->cached_table),
depended_from(item->depended_from)
{}
......@@ -129,6 +136,9 @@ void Item_ident::cleanup()
*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;
}
bool Item_ident::remove_dependence_processor(byte * arg)
......@@ -310,6 +320,15 @@ Item_field::Item_field(Field *f)
fixed= 1;
}
Item_field::Item_field(THD *thd, Field *f)
:Item_ident(NullS, thd->strdup(f->table_name),
thd->strdup(f->field_name))
{
set_field(f);
collation.set(DERIVATION_IMPLICIT);
fixed= 1;
}
// Constructor need to process subselect with temporary tables (see Item)
Item_field::Item_field(THD *thd, Item_field *item)
:Item_ident(thd, item),
......
......@@ -253,10 +253,20 @@ class Item_num: public Item
virtual Item_num* neg()= 0;
};
#define NO_CACHED_FIELD_INDEX ((uint)(-1))
class st_select_lex;
class Item_ident :public Item
{
/*
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
updated during fix_fields() to values from Field object and life-time
of those is shorter than life-time of Item_field.
*/
const char *orig_db_name;
const char *orig_table_name;
const char *orig_field_name;
Item **changed_during_fix_field;
public:
const char *db_name;
......@@ -264,9 +274,16 @@ class Item_ident :public Item
const char *field_name;
/*
Cached value of index for this field in table->field array, used by prep.
stmts for speeding up their re-execution, -1 if index value is not known.
stmts for speeding up their re-execution. Holds NO_CACHED_FIELD_INDEX
if index value is not known.
*/
uint cached_field_index;
/*
Cached pointer to table which contains this field, used for the same reason
by prep. stmt. too in case then we have not-fully qualified field.
0 - means no cached value.
*/
int cached_field_index;
TABLE_LIST *cached_table;
st_select_lex *depended_from;
Item_ident(const char *db_name_par,const char *table_name_par,
const char *field_name_par);
......@@ -298,6 +315,11 @@ class Item_field :public Item_ident
{ collation.set(DERIVATION_IMPLICIT); }
// Constructor need 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.)
*/
Item_field(THD *thd, Field *field);
Item_field(Field *field);
enum Type type() const { return FIELD_ITEM; }
bool eq(const Item *item, bool binary_cmp) const;
......
......@@ -559,7 +559,7 @@ Field *find_field_in_tables(THD *thd, Item_ident *item, TABLE_LIST *tables,
TABLE_LIST **where, bool report_error);
Field *find_field_in_table(THD *thd,TABLE *table,const char *name,uint length,
bool check_grant,bool allow_rowid,
int *cached_field_index_ptr);
uint *cached_field_index_ptr);
#ifdef HAVE_OPENSSL
#include <openssl/des.h>
struct st_des_keyblock
......
......@@ -2194,7 +2194,7 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list,
DBUG_RETURN(-1);
while ((column = column_iter++))
{
int unused_field_idx= -1;
uint unused_field_idx= NO_CACHED_FIELD_INDEX;
if (!find_field_in_table(thd,table,column->column.ptr(),
column->column.length(),0,0,
&unused_field_idx))
......
......@@ -1791,12 +1791,13 @@ bool rm_temporary_table(enum db_type base, char *path)
Field *find_field_in_table(THD *thd,TABLE *table,const char *name,uint length,
bool check_grants, bool allow_rowid,
int *cached_field_index_ptr)
uint *cached_field_index_ptr)
{
Field **field_ptr= 0, *field;
int cached_field_index= *cached_field_index_ptr;
Field **field_ptr, *field;
uint cached_field_index= *cached_field_index_ptr;
if (cached_field_index >= 0 && cached_field_index < table->fields &&
/* We assume here that table->field < NO_CACHED_FIELD_INDEX = UINT_MAX */
if (cached_field_index < table->fields &&
!my_strcasecmp(system_charset_info,
table->field[cached_field_index]->field_name, name))
field_ptr= table->field + cached_field_index;
......@@ -1805,14 +1806,11 @@ Field *find_field_in_table(THD *thd,TABLE *table,const char *name,uint length,
length);
else
{
if (!(field_ptr=table->field))
if (!(field_ptr= table->field))
return (Field *)0;
while (*field_ptr)
{
for (; *field_ptr; ++field_ptr)
if (!my_strcasecmp(system_charset_info, (*field_ptr)->field_name, name))
break;
++field_ptr;
}
}
if (field_ptr && *field_ptr)
......@@ -1882,6 +1880,31 @@ find_field_in_tables(THD *thd, Item_ident *item, TABLE_LIST *tables,
uint length=(uint) strlen(name);
char name_buff[NAME_LEN+1];
if (item->cached_table)
{
/*
This shortcut is used by prepared statements. We assuming that
TABLE_LIST *tables is not changed during query execution (which
is true for all queries except RENAME but luckily RENAME doesn't
use fields...) so we can rely on reusing pointer to its member.
With this optimisation we also miss case when addition of one more
field makes some prepared query ambiguous and so erronous, but we
accept this trade off.
*/
found= find_field_in_table(thd,tables->table,name,length,
test(tables->table->grant.want_privilege),
1, &(item->cached_field_index));
if (found)
{
(*where)= tables;
if (found == WRONG_GRANT)
return (Field*) 0;
return found;
}
}
if (db && lower_case_table_names)
{
/*
......@@ -1909,7 +1932,7 @@ find_field_in_tables(THD *thd, Item_ident *item, TABLE_LIST *tables,
1, &(item->cached_field_index));
if (find)
{
(*where)= tables;
(*where)= item->cached_table= tables;
if (find == WRONG_GRANT)
return (Field*) 0;
if (db || !thd->where)
......@@ -1968,7 +1991,7 @@ find_field_in_tables(THD *thd, Item_ident *item, TABLE_LIST *tables,
{
if (field == WRONG_GRANT)
return (Field*) 0;
(*where)= tables;
(*where)= item->cached_table= tables;
if (found)
{
if (!thd->where) // Returns first found
......@@ -2304,7 +2327,7 @@ insert_fields(THD *thd,TABLE_LIST *tables, const char *db_name,
thd->used_tables|=table->map;
while ((field = *ptr++))
{
Item_field *item= new Item_field(field);
Item_field *item= new Item_field(thd, field);
if (!found++)
(void) it->replace(item); // Replace '*'
else
......
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