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:
Constructor used by Item_field, Item_ref & agregate (sum) functions.
Used for duplicating lists in processing queries with temporary
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);
virtual ~Item() { name=0; } /*lint -e1509 */
......@@ -184,6 +187,7 @@ public:
virtual void save_in_result_field(bool no_conversions) {}
virtual void no_rows_in_result() {}
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 *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 */
public:
Field *result_field; /* Save result here */
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(thd, item), result_field(item.result_field)
{}
......
......@@ -1549,6 +1549,31 @@ longlong Item_func_bit_and::val_int()
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
Item_cond::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref)
......
......@@ -82,6 +82,7 @@ public:
Item_bool_func() :Item_int_func() {}
Item_bool_func(Item *a) :Item_int_func(a) {}
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; }
};
......@@ -115,8 +116,8 @@ protected:
String tmp_value1,tmp_value2;
public:
Item_bool_func2(Item *a,Item *b):
Item_int_func(a,b), cmp(tmp_arg, tmp_arg+1) {}
Item_bool_func2(Item *a,Item *b)
:Item_int_func(a,b), cmp(tmp_arg, tmp_arg+1) {}
void fix_length_and_dec();
void set_cmp_func()
{
......@@ -158,7 +159,7 @@ public:
class Item_func_eq :public Item_bool_rowready_func2
{
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();
enum Functype functype() const { return EQ_FUNC; }
enum Functype rev_functype() const { return EQ_FUNC; }
......@@ -791,8 +792,13 @@ protected:
public:
/* 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 *i1,Item *i2) :Item_bool_func(), abort_on_null(0)
{ list.push_back(i1); list.push_back(i2); }
Item_cond(Item *i1,Item *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(); }
bool add(Item *item) { return list.push_back(item); }
bool fix_fields(THD *, struct st_table_list *, Item **ref);
......@@ -805,6 +811,7 @@ public:
void split_sum_func(Item **ref_pointer_array, List<Item> &fields);
friend int setup_conds(THD *thd,TABLE_LIST *tables,COND **conds);
void top_level_item() { abort_on_null=1; }
void copy_andor_arguments(THD *thd, Item_cond *item);
bool walk(Item_processor processor, byte *arg);
};
......@@ -815,9 +822,17 @@ class Item_cond_and :public Item_cond
public:
Item_cond_and() :Item_cond() {}
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; }
longlong val_int();
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
......@@ -825,10 +840,18 @@ class Item_cond_or :public Item_cond
public:
Item_cond_or() :Item_cond() {}
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; }
longlong val_int();
const char *func_name() const { return "or"; }
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)
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
......
......@@ -104,6 +104,8 @@ public:
}
}
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 */
bool fix_fields(THD *,struct st_table_list *, Item **ref);
table_map used_tables() const;
......@@ -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 *c) :Item_func(a,b,c) { 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(); }
String *val_str(String*str);
enum Item_result result_type () const { return INT_RESULT; }
......
......@@ -982,6 +982,7 @@ void st_select_lex::init_query()
cond_count= with_wild= 0;
ref_pointer_array= 0;
select_n_having_items= 0;
prep_where= 0;
}
void st_select_lex::init_select()
......
......@@ -339,6 +339,7 @@ class st_select_lex: public st_select_lex_node
public:
char *db, *db1, *table1, *db2, *table2; /* For outer join using .. */
Item *where, *having; /* WHERE & HAVING clauses */
Item *prep_where; /* saved WHERE clause for prepared statement processing */
enum olap_type olap;
SQL_LIST table_list, group_list; /* FROM & GROUP BY clauses */
List<Item> item_list; /* list of fields & expressions */
......
......@@ -873,11 +873,21 @@ bool mysql_stmt_prepare(THD *thd, char *packet, uint packet_length)
if (!(specialflag & SPECIAL_NO_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))
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);
thd->mem_root= thd_root; // restore main mem_root
DBUG_RETURN(0);
......@@ -919,6 +929,17 @@ void mysql_stmt_execute(THD *thd, char *packet)
LEX thd_lex= thd->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);
if (stmt->param_count && setup_params_data(stmt))
......
......@@ -1839,6 +1839,85 @@ session_id char(9) NOT NULL, \
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 *
*********************************************************/
......@@ -7898,6 +7977,7 @@ int main(int argc, char **argv)
test_sqlmode(); /* test for SQL_MODE */
test_ts(); /* test for timestamp BR#819 */
test_bug1115(); /* BUG#1115 */
test_bug1180(); /* BUG#1180 */
end_time= time((time_t *)0);
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