summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbell@sanja.is.com.ua <>2003-09-02 19:56:55 +0300
committerbell@sanja.is.com.ua <>2003-09-02 19:56:55 +0300
commit51374e569ddf25822dec7a084cff0265714313f5 (patch)
tree354683527bb4727d44f35e20a3d4f06e5e53d9b3
parent0f456a480e25521f7d712c7043a7ec27d9c171d8 (diff)
downloadmariadb-git-51374e569ddf25822dec7a084cff0265714313f5.tar.gz
fixed BUG#1180 (changing WHERE clause of prepared statements by optimisation)
-rw-r--r--sql/item.h6
-rw-r--r--sql/item_cmpfunc.cc25
-rw-r--r--sql/item_cmpfunc.h33
-rw-r--r--sql/item_func.cc21
-rw-r--r--sql/item_func.h3
-rw-r--r--sql/sql_lex.cc1
-rw-r--r--sql/sql_lex.h1
-rw-r--r--sql/sql_prepare.cc23
-rw-r--r--tests/client_test.c80
9 files changed, 186 insertions, 7 deletions
diff --git a/sql/item.h b/sql/item.h
index c7eb7fc2c1b..147c350878e 100644
--- a/sql/item.h
+++ b/sql/item.h
@@ -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)
{}
diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc
index 3471ddd30e9..7460c103550 100644
--- a/sql/item_cmpfunc.cc
+++ b/sql/item_cmpfunc.cc
@@ -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)
diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h
index 9379df84199..f8104491978 100644
--- a/sql/item_cmpfunc.h
+++ b/sql/item_cmpfunc.h
@@ -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;
+ }
};
diff --git a/sql/item_func.cc b/sql/item_func.cc
index 4bda8ae78cd..9220fb335e6 100644
--- a/sql/item_func.cc
+++ b/sql/item_func.cc
@@ -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
diff --git a/sql/item_func.h b/sql/item_func.h
index 7fedbcf48ee..a01c3a37c2f 100644
--- a/sql/item_func.h
+++ b/sql/item_func.h
@@ -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; }
diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc
index d6cfd555c40..80d698dfc26 100644
--- a/sql/sql_lex.cc
+++ b/sql/sql_lex.cc
@@ -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()
diff --git a/sql/sql_lex.h b/sql/sql_lex.h
index 7c39d2fe776..bbf1cd9a130 100644
--- a/sql/sql_lex.h
+++ b/sql/sql_lex.h
@@ -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 */
diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc
index 550e4bbe086..3bebdef7738 100644
--- a/sql/sql_prepare.cc
+++ b/sql/sql_prepare.cc
@@ -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))
diff --git a/tests/client_test.c b/tests/client_test.c
index 46a272f2acb..c2bc966fee8 100644
--- a/tests/client_test.c
+++ b/tests/client_test.c
@@ -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);