mirror of
https://github.com/MariaDB/server.git
synced 2025-01-16 12:02:42 +01:00
fixed BUG#1180 (changing WHERE clause of prepared statements by optimisation)
This commit is contained in:
parent
0f456a480e
commit
51374e569d
9 changed files with 186 additions and 7 deletions
|
@ -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);
|
||||
|
|
Loading…
Reference in a new issue