Commit 2d7b4898 authored by unknown's avatar unknown

fixed BUG#1180 (changing WHERE clause of prepared statements by optimisation)


sql/item.h:
  constructor and method for aloning AND/OR structure of WHERE clause
sql/item_cmpfunc.cc:
  constructor and method for aloning AND/OR structure of WHERE clause
sql/item_cmpfunc.h:
  constructor and method for aloning AND/OR structure of WHERE clause
sql/item_func.cc:
  constructor for aloning AND/OR structure of WHERE clause
sql/item_func.h:
  constructor for aloning AND/OR structure of WHERE clause
sql/sql_lex.cc:
  field for saving WHERE root
sql/sql_lex.h:
  field for saving WHERE root
sql/sql_prepare.cc:
  saving WHERE root
  creating new AND/OR structure before executing prepared statement
tests/client_test.c:
  test suite for bug #1180
parent 0e2f6264
...@@ -120,6 +120,9 @@ public: ...@@ -120,6 +120,9 @@ public:
Constructor used by Item_field, Item_ref & agregate (sum) functions. Constructor used by Item_field, Item_ref & agregate (sum) functions.
Used for duplicating lists in processing queries with temporary Used for duplicating lists in processing queries with temporary
tables tables
Also it used for Item_cond_and/Item_cond_or for creating
top AND/OR ctructure of WHERE clause to protect it of
optimisation changes in prepared statements
*/ */
Item(THD *thd, Item &item); Item(THD *thd, Item &item);
virtual ~Item() { name=0; } /*lint -e1509 */ virtual ~Item() { name=0; } /*lint -e1509 */
...@@ -184,6 +187,7 @@ public: ...@@ -184,6 +187,7 @@ public:
virtual void save_in_result_field(bool no_conversions) {} virtual void save_in_result_field(bool no_conversions) {}
virtual void no_rows_in_result() {} virtual void no_rows_in_result() {}
virtual Item *copy_or_same(THD *thd) { return this; } virtual Item *copy_or_same(THD *thd) { return this; }
virtual Item *copy_andor_structure(THD *thd) { return this; }
virtual Item *real_item() { return this; } virtual Item *real_item() { return this; }
virtual Item *get_tmp_table_item(THD *thd) { return copy_or_same(thd); } virtual Item *get_tmp_table_item(THD *thd) { return copy_or_same(thd); }
...@@ -541,7 +545,7 @@ class Item_result_field :public Item /* Item with result field */ ...@@ -541,7 +545,7 @@ class Item_result_field :public Item /* Item with result field */
public: public:
Field *result_field; /* Save result here */ Field *result_field; /* Save result here */
Item_result_field() :result_field(0) {} Item_result_field() :result_field(0) {}
// Constructor used for Item_sum (see Item comment) // Constructor used for Item_sum/Item_cond_and/or (see Item comment)
Item_result_field(THD *thd, Item_result_field &item): Item_result_field(THD *thd, Item_result_field &item):
Item(thd, item), result_field(item.result_field) Item(thd, item), result_field(item.result_field)
{} {}
......
...@@ -1549,6 +1549,31 @@ longlong Item_func_bit_and::val_int() ...@@ -1549,6 +1549,31 @@ longlong Item_func_bit_and::val_int()
return (longlong) (arg1 & arg2); return (longlong) (arg1 & arg2);
} }
Item_cond::Item_cond(THD *thd, Item_cond &item)
:Item_bool_func(thd, item),
abort_on_null(item.abort_on_null),
and_tables_cache(item.and_tables_cache)
{
/*
here should be following text:
List_iterator_fast<Item*> li(item.list);
while(Item *it= li++)
list.push_back(it);
but it do not need,
because this constructor used only for AND/OR and
argument list will be copied by copy_andor_arguments call
*/
}
void Item_cond::copy_andor_arguments(THD *thd, Item_cond *item)
{
List_iterator_fast<Item> li(item->list);
while(Item *it= li++)
list.push_back(it->copy_andor_structure(thd));
}
bool bool
Item_cond::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref) Item_cond::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref)
......
...@@ -82,6 +82,7 @@ public: ...@@ -82,6 +82,7 @@ public:
Item_bool_func() :Item_int_func() {} Item_bool_func() :Item_int_func() {}
Item_bool_func(Item *a) :Item_int_func(a) {} Item_bool_func(Item *a) :Item_int_func(a) {}
Item_bool_func(Item *a,Item *b) :Item_int_func(a,b) {} Item_bool_func(Item *a,Item *b) :Item_int_func(a,b) {}
Item_bool_func(THD *thd, Item_bool_func &item) :Item_int_func(thd, item) {}
void fix_length_and_dec() { decimals=0; max_length=1; } void fix_length_and_dec() { decimals=0; max_length=1; }
}; };
...@@ -115,8 +116,8 @@ protected: ...@@ -115,8 +116,8 @@ protected:
String tmp_value1,tmp_value2; String tmp_value1,tmp_value2;
public: public:
Item_bool_func2(Item *a,Item *b): Item_bool_func2(Item *a,Item *b)
Item_int_func(a,b), cmp(tmp_arg, tmp_arg+1) {} :Item_int_func(a,b), cmp(tmp_arg, tmp_arg+1) {}
void fix_length_and_dec(); void fix_length_and_dec();
void set_cmp_func() void set_cmp_func()
{ {
...@@ -158,7 +159,7 @@ public: ...@@ -158,7 +159,7 @@ public:
class Item_func_eq :public Item_bool_rowready_func2 class Item_func_eq :public Item_bool_rowready_func2
{ {
public: public:
Item_func_eq(Item *a,Item *b) :Item_bool_rowready_func2(a,b) {}; Item_func_eq(Item *a,Item *b) :Item_bool_rowready_func2(a,b) {}
longlong val_int(); longlong val_int();
enum Functype functype() const { return EQ_FUNC; } enum Functype functype() const { return EQ_FUNC; }
enum Functype rev_functype() const { return EQ_FUNC; } enum Functype rev_functype() const { return EQ_FUNC; }
...@@ -791,8 +792,13 @@ protected: ...@@ -791,8 +792,13 @@ protected:
public: public:
/* Item_cond() is only used to create top level items */ /* Item_cond() is only used to create top level items */
Item_cond() : Item_bool_func(), abort_on_null(1) { const_item_cache=0; } Item_cond() : Item_bool_func(), abort_on_null(1) { const_item_cache=0; }
Item_cond(Item *i1,Item *i2) :Item_bool_func(), abort_on_null(0) Item_cond(Item *i1,Item *i2)
{ list.push_back(i1); list.push_back(i2); } :Item_bool_func(), abort_on_null(0)
{
list.push_back(i1);
list.push_back(i2);
}
Item_cond(THD *thd, Item_cond &item);
~Item_cond() { list.delete_elements(); } ~Item_cond() { list.delete_elements(); }
bool add(Item *item) { return list.push_back(item); } bool add(Item *item) { return list.push_back(item); }
bool fix_fields(THD *, struct st_table_list *, Item **ref); bool fix_fields(THD *, struct st_table_list *, Item **ref);
...@@ -805,6 +811,7 @@ public: ...@@ -805,6 +811,7 @@ public:
void split_sum_func(Item **ref_pointer_array, List<Item> &fields); void split_sum_func(Item **ref_pointer_array, List<Item> &fields);
friend int setup_conds(THD *thd,TABLE_LIST *tables,COND **conds); friend int setup_conds(THD *thd,TABLE_LIST *tables,COND **conds);
void top_level_item() { abort_on_null=1; } void top_level_item() { abort_on_null=1; }
void copy_andor_arguments(THD *thd, Item_cond *item);
bool walk(Item_processor processor, byte *arg); bool walk(Item_processor processor, byte *arg);
}; };
...@@ -815,9 +822,17 @@ class Item_cond_and :public Item_cond ...@@ -815,9 +822,17 @@ class Item_cond_and :public Item_cond
public: public:
Item_cond_and() :Item_cond() {} Item_cond_and() :Item_cond() {}
Item_cond_and(Item *i1,Item *i2) :Item_cond(i1,i2) {} Item_cond_and(Item *i1,Item *i2) :Item_cond(i1,i2) {}
Item_cond_and(THD *thd, Item_cond_and &item) :Item_cond(thd, item) {}
enum Functype functype() const { return COND_AND_FUNC; } enum Functype functype() const { return COND_AND_FUNC; }
longlong val_int(); longlong val_int();
const char *func_name() const { return "and"; } const char *func_name() const { return "and"; }
Item* copy_andor_structure(THD *thd)
{
Item_cond_and *item;
if((item= new Item_cond_and(thd, *this)))
item->copy_andor_arguments(thd, this);
return item;
}
}; };
class Item_cond_or :public Item_cond class Item_cond_or :public Item_cond
...@@ -825,10 +840,18 @@ class Item_cond_or :public Item_cond ...@@ -825,10 +840,18 @@ class Item_cond_or :public Item_cond
public: public:
Item_cond_or() :Item_cond() {} Item_cond_or() :Item_cond() {}
Item_cond_or(Item *i1,Item *i2) :Item_cond(i1,i2) {} Item_cond_or(Item *i1,Item *i2) :Item_cond(i1,i2) {}
Item_cond_or(THD *thd, Item_cond_or &item) :Item_cond(thd, item) {}
enum Functype functype() const { return COND_OR_FUNC; } enum Functype functype() const { return COND_OR_FUNC; }
longlong val_int(); longlong val_int();
const char *func_name() const { return "or"; } const char *func_name() const { return "or"; }
table_map not_null_tables() const { return and_tables_cache; } table_map not_null_tables() const { return and_tables_cache; }
Item* copy_andor_structure(THD *thd)
{
Item_cond_or *item;
if((item= new Item_cond_or(thd, *this)))
item->copy_andor_arguments(thd, this);
return item;
}
}; };
......
...@@ -134,6 +134,27 @@ Item_func::Item_func(List<Item> &list) ...@@ -134,6 +134,27 @@ Item_func::Item_func(List<Item> &list)
set_arguments(list); set_arguments(list);
} }
Item_func::Item_func(THD *thd, Item_func &item)
:Item_result_field(thd, item),
allowed_arg_cols(item.allowed_arg_cols),
arg_count(item.arg_count),
used_tables_cache(item.used_tables_cache),
not_null_tables_cache(item.not_null_tables_cache),
const_item_cache(item.const_item_cache)
{
if (arg_count)
{
if (arg_count <=2)
args= tmp_arg;
else
{
if (!(args=(Item**) thd->alloc(sizeof(Item*)*arg_count)))
return;
}
memcpy((char*) args, (char*) item.args, sizeof(Item*)*arg_count);
}
}
/* /*
Resolve references to table column for a function and it's argument Resolve references to table column for a function and it's argument
......
...@@ -104,6 +104,8 @@ public: ...@@ -104,6 +104,8 @@ public:
} }
} }
Item_func(List<Item> &list); Item_func(List<Item> &list);
// Constructor used for Item_cond_and/or (see Item comment)
Item_func(THD *thd, Item_func &item);
~Item_func() {} /* Nothing to do; Items are freed automaticly */ ~Item_func() {} /* Nothing to do; Items are freed automaticly */
bool fix_fields(THD *,struct st_table_list *, Item **ref); bool fix_fields(THD *,struct st_table_list *, Item **ref);
table_map used_tables() const; table_map used_tables() const;
...@@ -196,6 +198,7 @@ public: ...@@ -196,6 +198,7 @@ public:
Item_int_func(Item *a,Item *b) :Item_func(a,b) { max_length=21; } Item_int_func(Item *a,Item *b) :Item_func(a,b) { max_length=21; }
Item_int_func(Item *a,Item *b,Item *c) :Item_func(a,b,c) { max_length=21; } Item_int_func(Item *a,Item *b,Item *c) :Item_func(a,b,c) { max_length=21; }
Item_int_func(List<Item> &list) :Item_func(list) { max_length=21; } Item_int_func(List<Item> &list) :Item_func(list) { max_length=21; }
Item_int_func(THD *thd, Item_int_func &item) :Item_func(thd, item) {}
double val() { return (double) val_int(); } double val() { return (double) val_int(); }
String *val_str(String*str); String *val_str(String*str);
enum Item_result result_type () const { return INT_RESULT; } enum Item_result result_type () const { return INT_RESULT; }
......
...@@ -982,6 +982,7 @@ void st_select_lex::init_query() ...@@ -982,6 +982,7 @@ void st_select_lex::init_query()
cond_count= with_wild= 0; cond_count= with_wild= 0;
ref_pointer_array= 0; ref_pointer_array= 0;
select_n_having_items= 0; select_n_having_items= 0;
prep_where= 0;
} }
void st_select_lex::init_select() void st_select_lex::init_select()
......
...@@ -339,6 +339,7 @@ class st_select_lex: public st_select_lex_node ...@@ -339,6 +339,7 @@ class st_select_lex: public st_select_lex_node
public: public:
char *db, *db1, *table1, *db2, *table2; /* For outer join using .. */ char *db, *db1, *table1, *db2, *table2; /* For outer join using .. */
Item *where, *having; /* WHERE & HAVING clauses */ Item *where, *having; /* WHERE & HAVING clauses */
Item *prep_where; /* saved WHERE clause for prepared statement processing */
enum olap_type olap; enum olap_type olap;
SQL_LIST table_list, group_list; /* FROM & GROUP BY clauses */ SQL_LIST table_list, group_list; /* FROM & GROUP BY clauses */
List<Item> item_list; /* list of fields & expressions */ List<Item> item_list; /* list of fields & expressions */
......
...@@ -874,9 +874,19 @@ bool mysql_stmt_prepare(THD *thd, char *packet, uint packet_length) ...@@ -874,9 +874,19 @@ bool mysql_stmt_prepare(THD *thd, char *packet, uint packet_length)
if (!(specialflag & SPECIAL_NO_PRIOR)) if (!(specialflag & SPECIAL_NO_PRIOR))
my_pthread_setprio(pthread_self(),WAIT_PRIOR); my_pthread_setprio(pthread_self(),WAIT_PRIOR);
// save WHERE clause pointers to avoid damaging they by optimisation
for (SELECT_LEX *sl= thd->lex.all_selects_list;
sl;
sl= sl->next_select_in_list())
{
sl->prep_where= sl->where;
}
if (init_param_items(&stmt)) if (init_param_items(&stmt))
goto err; goto err;
stmt.mem_root= stmt.thd->mem_root; stmt.mem_root= stmt.thd->mem_root;
tree_insert(&thd->prepared_statements, (void *)&stmt, 0, (void *)0); tree_insert(&thd->prepared_statements, (void *)&stmt, 0, (void *)0);
thd->mem_root= thd_root; // restore main mem_root thd->mem_root= thd_root; // restore main mem_root
...@@ -919,6 +929,17 @@ void mysql_stmt_execute(THD *thd, char *packet) ...@@ -919,6 +929,17 @@ void mysql_stmt_execute(THD *thd, char *packet)
LEX thd_lex= thd->lex; LEX thd_lex= thd->lex;
thd->lex= stmt->lex; thd->lex= stmt->lex;
for (SELECT_LEX *sl= stmt->lex.all_selects_list;
sl;
sl= sl->next_select_in_list())
{
// copy WHERE clause pointers to avoid damaging they by optimisation
if (sl->prep_where)
sl->where= sl->prep_where->copy_andor_structure(thd);
// force allocation new JOIN for this mem_root (for safety)
sl->join= 0;
}
init_stmt_execute(stmt); init_stmt_execute(stmt);
if (stmt->param_count && setup_params_data(stmt)) if (stmt->param_count && setup_params_data(stmt))
......
...@@ -1839,6 +1839,85 @@ session_id char(9) NOT NULL, \ ...@@ -1839,6 +1839,85 @@ session_id char(9) NOT NULL, \
mysql_stmt_close(stmt); mysql_stmt_close(stmt);
} }
/*
test BUG#1180 (optimized away part of WHERE clause)
*/
static void test_bug1180()
{
MYSQL_STMT *stmt;
int rc;
MYSQL_BIND bind[1];
ulong length[1];
char szData[11];
int nData=1;
myheader("test_select_bug");
rc = mysql_query(mysql,"DROP TABLE IF EXISTS test_select");
myquery(rc);
rc = mysql_query(mysql,"CREATE TABLE test_select(session_id char(9) NOT NULL)");
myquery(rc);
rc = mysql_query(mysql,"INSERT INTO test_select VALUES (\"abc\")");
myquery(rc);
strmov(query,"SELECT * FROM test_select WHERE ?=\"1111\" and session_id = \"abc\"");
stmt = mysql_prepare(mysql, query, strlen(query));
mystmt_init(stmt);
verify_param_count(stmt,1);
strmov(szData,(char *)"abc");
bind[0].buffer_type=FIELD_TYPE_STRING;
bind[0].buffer=(char *)szData;
bind[0].buffer_length= 10;
bind[0].length= &length[0];
length[0]= 3;
bind[0].is_null=0;
rc = mysql_bind_param(stmt,bind);
mystmt(stmt, rc);
rc = mysql_execute(stmt);
mystmt(stmt, rc);
myassert(my_process_stmt_result(stmt) == 0);
strmov(szData,(char *)"1111");
bind[0].buffer_type=FIELD_TYPE_STRING;
bind[0].buffer=(char *)szData;
bind[0].buffer_length= 10;
bind[0].length= &length[0];
length[0]= 4;
bind[0].is_null=0;
rc = mysql_bind_param(stmt,bind);
mystmt(stmt, rc);
rc = mysql_execute(stmt);
mystmt(stmt, rc);
myassert(my_process_stmt_result(stmt) == 1);
strmov(szData,(char *)"abc");
bind[0].buffer_type=FIELD_TYPE_STRING;
bind[0].buffer=(char *)szData;
bind[0].buffer_length= 10;
bind[0].length= &length[0];
length[0]= 3;
bind[0].is_null=0;
rc = mysql_bind_param(stmt,bind);
mystmt(stmt, rc);
rc = mysql_execute(stmt);
mystmt(stmt, rc);
myassert(my_process_stmt_result(stmt) == 0);
mysql_stmt_close(stmt);
}
/******************************************************** /********************************************************
* to test simple select show * * to test simple select show *
*********************************************************/ *********************************************************/
...@@ -7898,6 +7977,7 @@ int main(int argc, char **argv) ...@@ -7898,6 +7977,7 @@ int main(int argc, char **argv)
test_sqlmode(); /* test for SQL_MODE */ test_sqlmode(); /* test for SQL_MODE */
test_ts(); /* test for timestamp BR#819 */ test_ts(); /* test for timestamp BR#819 */
test_bug1115(); /* BUG#1115 */ test_bug1115(); /* BUG#1115 */
test_bug1180(); /* BUG#1180 */
end_time= time((time_t *)0); end_time= time((time_t *)0);
total_time+= difftime(end_time, start_time); total_time+= difftime(end_time, start_time);
......
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