diff options
author | bell@sanja.is.com.ua <> | 2003-09-02 19:56:55 +0300 |
---|---|---|
committer | bell@sanja.is.com.ua <> | 2003-09-02 19:56:55 +0300 |
commit | 51374e569ddf25822dec7a084cff0265714313f5 (patch) | |
tree | 354683527bb4727d44f35e20a3d4f06e5e53d9b3 | |
parent | 0f456a480e25521f7d712c7043a7ec27d9c171d8 (diff) | |
download | mariadb-git-51374e569ddf25822dec7a084cff0265714313f5.tar.gz |
fixed BUG#1180 (changing WHERE clause of prepared statements by optimisation)
-rw-r--r-- | sql/item.h | 6 | ||||
-rw-r--r-- | sql/item_cmpfunc.cc | 25 | ||||
-rw-r--r-- | sql/item_cmpfunc.h | 33 | ||||
-rw-r--r-- | sql/item_func.cc | 21 | ||||
-rw-r--r-- | sql/item_func.h | 3 | ||||
-rw-r--r-- | sql/sql_lex.cc | 1 | ||||
-rw-r--r-- | sql/sql_lex.h | 1 | ||||
-rw-r--r-- | sql/sql_prepare.cc | 23 | ||||
-rw-r--r-- | tests/client_test.c | 80 |
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); |