Loading mysql-test/r/ps.result +25 −0 Original line number Diff line number Diff line Loading @@ -570,3 +570,28 @@ id deallocate prepare stmt| drop procedure p1| drop table t1| drop table if exists t1; Warnings: Note 1051 Unknown table 't1' create table t1 (c1 int(11) not null, c2 int(11) not null, primary key (c1,c2), key c2 (c2), key c1 (c1)); insert into t1 values (200887, 860); insert into t1 values (200887, 200887); select * from t1 where (c1=200887 and c2=200887) or c2=860; c1 c2 200887 860 200887 200887 prepare stmt from "select * from t1 where (c1=200887 and c2=200887) or c2=860"; execute stmt; c1 c2 200887 860 200887 200887 prepare stmt from "select * from t1 where (c1=200887 and c2=?) or c2=?"; set @a=200887, @b=860; execute stmt using @a, @b; c1 c2 200887 860 200887 200887 deallocate prepare stmt; mysql-test/r/type_datetime.result +1 −1 Original line number Diff line number Diff line Loading @@ -88,7 +88,7 @@ date numfacture expedition 0000-00-00 00:00:00 1212 0001-00-00 00:00:00 EXPLAIN SELECT * FROM t1 WHERE expedition='0001-00-00 00:00:00'; id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t1 ref expedition expedition 8 const 1 Using where 1 SIMPLE t1 ref expedition expedition 8 const 1 drop table t1; create table t1 (a datetime not null, b datetime not null); insert into t1 values (now(), now()); Loading mysql-test/t/ps.test +27 −0 Original line number Diff line number Diff line Loading @@ -587,3 +587,30 @@ deallocate prepare stmt| drop procedure p1| drop table t1| delimiter ;| # # Bug#9096 "select doesn't return all matched records if prepared statements # is used" # The bug was is bad co-operation of the optimizer's algorithm which determines # which keys can be used to execute a query, constants propagation # part of the optimizer and parameter markers used by prepared statements. drop table if exists t1; create table t1 (c1 int(11) not null, c2 int(11) not null, primary key (c1,c2), key c2 (c2), key c1 (c1)); insert into t1 values (200887, 860); insert into t1 values (200887, 200887); select * from t1 where (c1=200887 and c2=200887) or c2=860; prepare stmt from "select * from t1 where (c1=200887 and c2=200887) or c2=860"; execute stmt; prepare stmt from "select * from t1 where (c1=200887 and c2=?) or c2=?"; set @a=200887, @b=860; # this query did not return all matching rows execute stmt using @a, @b; deallocate prepare stmt; sql/item.cc +132 −1 Original line number Diff line number Diff line Loading @@ -558,6 +558,11 @@ void Item::set_name(const char *str, uint length, CHARSET_INFO *cs) bool Item::eq(const Item *item, bool binary_cmp) const { /* Note, that this is never TRUE if item is a Item_param: for all basic constants we have special checks, and Item_param's type() can be only among basic constant types. */ return type() == item->type() && name && item->name && !my_strcasecmp(system_charset_info,name,item->name); } Loading Loading @@ -602,7 +607,7 @@ Item *Item_string::safe_charset_converter(CHARSET_INFO *tocs) bool Item_string::eq(const Item *item, bool binary_cmp) const { if (type() == item->type()) if (type() == item->type() && item->basic_const_item()) { if (binary_cmp) return !stringcmp(&str_value, &item->str_value); Loading Loading @@ -1444,6 +1449,24 @@ void Item_decimal::print(String *str) } bool Item_decimal::eq(const Item *item, bool binary_cmp) const { if (type() == item->type() && item->basic_const_item()) { /* We need to cast off const to call val_decimal(). This should be OK for a basic constant. Additionally, we can pass 0 as a true decimal constant will return its internal decimal storage and ignore the argument. */ Item *arg= (Item*) item; my_decimal *value= arg->val_decimal(0); return !my_decimal_cmp(&decimal_value, value); } return 0; } String *Item_float::val_str(String *str) { // following assert is redundant, because fixed=1 assigned in constructor Loading Loading @@ -2195,6 +2218,72 @@ bool Item_param::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref) } bool Item_param::basic_const_item() const { if (state == NO_VALUE || state == TIME_VALUE) return FALSE; return TRUE; } Item * Item_param::new_item() { /* see comments in the header file */ switch (state) { case NULL_VALUE: return new Item_null(name); case INT_VALUE: return new Item_int(name, value.integer, max_length); case REAL_VALUE: return new Item_float(name, value.real, decimals, max_length); case STRING_VALUE: case LONG_DATA_VALUE: return new Item_string(name, str_value.c_ptr_quick(), str_value.length(), str_value.charset()); case TIME_VALUE: break; case NO_VALUE: default: DBUG_ASSERT(0); }; return 0; } bool Item_param::eq(const Item *arg, bool binary_cmp) const { Item *item; if (!basic_const_item() || !arg->basic_const_item() || arg->type() != type()) return FALSE; /* We need to cast off const to call val_int(). This should be OK for a basic constant. */ item= (Item*) arg; switch (state) { case NULL_VALUE: return TRUE; case INT_VALUE: return value.integer == item->val_int() && unsigned_flag == item->unsigned_flag; case REAL_VALUE: return value.real == item->val_real(); case STRING_VALUE: case LONG_DATA_VALUE: if (binary_cmp) return !stringcmp(&str_value, &item->str_value); return !sortcmp(&str_value, &item->str_value, collation.collation); default: break; } return FALSE; } /* End of Item_param related */ void Item_param::print(String *str) { if (state == NO_VALUE) Loading Loading @@ -3412,6 +3501,22 @@ int Item_decimal::save_in_field(Field *field, bool no_conversions) } bool Item_int::eq(const Item *arg, bool binary_cmp) const { /* No need to check for null value as basic constant can't be NULL */ if (arg->basic_const_item() && arg->type() == type()) { /* We need to cast off const to call val_int(). This should be OK for a basic constant. */ Item *item= (Item*) arg; return item->val_int() == value && item->unsigned_flag == unsigned_flag; } return FALSE; } Item_num *Item_uint::neg() { Item_decimal *item= new Item_decimal(value, 0); Loading Loading @@ -3499,6 +3604,21 @@ void Item_float::print(String *str) In number context this is a longlong value. */ bool Item_float::eq(const Item *arg, bool binary_cmp) const { if (arg->basic_const_item() && arg->type() == type()) { /* We need to cast off const to call val_int(). This should be OK for a basic constant. */ Item *item= (Item*) arg; return item->val_real() == value; } return FALSE; } inline uint char_val(char X) { return (uint) (X >= '0' && X <= '9' ? X-'0' : Loading Loading @@ -3569,6 +3689,17 @@ int Item_hex_string::save_in_field(Field *field, bool no_conversions) } bool Item_hex_string::eq(const Item *arg, bool binary_cmp) const { if (arg->basic_const_item() && arg->type() == type()) { if (binary_cmp) return !stringcmp(&str_value, &arg->str_value); return !sortcmp(&str_value, &arg->str_value, collation.collation); } return FALSE; } /* bin item. In string context this is a binary string. Loading sql/item.h +25 −4 Original line number Diff line number Diff line Loading @@ -434,7 +434,7 @@ class Item { virtual table_map not_null_tables() const { return used_tables(); } /* Returns true if this is a simple constant item like an integer, not a constant expression a constant expression. Used in the optimizer to propagate basic constants. */ virtual bool basic_const_item() const { return 0; } /* cloning of constant items (0 if it is not const) */ Loading Loading @@ -914,7 +914,6 @@ class Item_param :public Item bool convert_str_value(THD *thd); Item *new_item() { return new Item_param(pos_in_query); } /* If value for parameter was not set we treat it as non-const so noone will use parameters value in fix_fields still Loading @@ -923,12 +922,29 @@ class Item_param :public Item virtual table_map used_tables() const { return state != NO_VALUE ? (table_map)0 : PARAM_TABLE_BIT; } void print(String *str); /* parameter never equal to other parameter of other item */ bool eq(const Item *item, bool binary_cmp) const { return 0; } bool is_null() { DBUG_ASSERT(state != NO_VALUE); return state == NULL_VALUE; } bool basic_const_item() const; /* This method is used to make a copy of a basic constant item when propagating constants in the optimizer. The reason to create a new item and not use the existing one is not precisely known (2005/04/16). Probably we are trying to preserve tree structure of items, in other words, avoid pointing at one item from two different nodes of the tree. Return a new basic constant item if parameter value is a basic constant, assert otherwise. This method is called only if basic_const_item returned TRUE. */ Item *new_item(); /* Implement by-value equality evaluation if parameter value is set and is a basic constant (integer, real or string). Otherwise return FALSE. */ bool eq(const Item *item, bool binary_cmp) const; }; class Item_int :public Item_num { public: Loading Loading @@ -956,6 +972,7 @@ class Item_int :public Item_num void cleanup() {} void print(String *str); Item_num *neg() { value= -value; return this; } bool eq(const Item *, bool binary_cmp) const; }; Loading Loading @@ -1022,8 +1039,10 @@ class Item_decimal :public Item_num unsigned_flag= !decimal_value.sign(); return this; } bool eq(const Item *, bool binary_cmp) const; }; class Item_float :public Item_num { char *presentation; Loading Loading @@ -1059,6 +1078,7 @@ class Item_float :public Item_num { return new Item_float(name, value, decimals, max_length); } Item_num *neg() { value= -value; return this; } void print(String *str); bool eq(const Item *, bool binary_cmp) const; }; Loading Loading @@ -1198,6 +1218,7 @@ class Item_hex_string: public Item enum_field_types field_type() const { return MYSQL_TYPE_VARCHAR; } // to prevent drop fixed flag (no need parent cleanup call) void cleanup() {} bool eq(const Item *item, bool binary_cmp) const; }; Loading Loading
mysql-test/r/ps.result +25 −0 Original line number Diff line number Diff line Loading @@ -570,3 +570,28 @@ id deallocate prepare stmt| drop procedure p1| drop table t1| drop table if exists t1; Warnings: Note 1051 Unknown table 't1' create table t1 (c1 int(11) not null, c2 int(11) not null, primary key (c1,c2), key c2 (c2), key c1 (c1)); insert into t1 values (200887, 860); insert into t1 values (200887, 200887); select * from t1 where (c1=200887 and c2=200887) or c2=860; c1 c2 200887 860 200887 200887 prepare stmt from "select * from t1 where (c1=200887 and c2=200887) or c2=860"; execute stmt; c1 c2 200887 860 200887 200887 prepare stmt from "select * from t1 where (c1=200887 and c2=?) or c2=?"; set @a=200887, @b=860; execute stmt using @a, @b; c1 c2 200887 860 200887 200887 deallocate prepare stmt;
mysql-test/r/type_datetime.result +1 −1 Original line number Diff line number Diff line Loading @@ -88,7 +88,7 @@ date numfacture expedition 0000-00-00 00:00:00 1212 0001-00-00 00:00:00 EXPLAIN SELECT * FROM t1 WHERE expedition='0001-00-00 00:00:00'; id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t1 ref expedition expedition 8 const 1 Using where 1 SIMPLE t1 ref expedition expedition 8 const 1 drop table t1; create table t1 (a datetime not null, b datetime not null); insert into t1 values (now(), now()); Loading
mysql-test/t/ps.test +27 −0 Original line number Diff line number Diff line Loading @@ -587,3 +587,30 @@ deallocate prepare stmt| drop procedure p1| drop table t1| delimiter ;| # # Bug#9096 "select doesn't return all matched records if prepared statements # is used" # The bug was is bad co-operation of the optimizer's algorithm which determines # which keys can be used to execute a query, constants propagation # part of the optimizer and parameter markers used by prepared statements. drop table if exists t1; create table t1 (c1 int(11) not null, c2 int(11) not null, primary key (c1,c2), key c2 (c2), key c1 (c1)); insert into t1 values (200887, 860); insert into t1 values (200887, 200887); select * from t1 where (c1=200887 and c2=200887) or c2=860; prepare stmt from "select * from t1 where (c1=200887 and c2=200887) or c2=860"; execute stmt; prepare stmt from "select * from t1 where (c1=200887 and c2=?) or c2=?"; set @a=200887, @b=860; # this query did not return all matching rows execute stmt using @a, @b; deallocate prepare stmt;
sql/item.cc +132 −1 Original line number Diff line number Diff line Loading @@ -558,6 +558,11 @@ void Item::set_name(const char *str, uint length, CHARSET_INFO *cs) bool Item::eq(const Item *item, bool binary_cmp) const { /* Note, that this is never TRUE if item is a Item_param: for all basic constants we have special checks, and Item_param's type() can be only among basic constant types. */ return type() == item->type() && name && item->name && !my_strcasecmp(system_charset_info,name,item->name); } Loading Loading @@ -602,7 +607,7 @@ Item *Item_string::safe_charset_converter(CHARSET_INFO *tocs) bool Item_string::eq(const Item *item, bool binary_cmp) const { if (type() == item->type()) if (type() == item->type() && item->basic_const_item()) { if (binary_cmp) return !stringcmp(&str_value, &item->str_value); Loading Loading @@ -1444,6 +1449,24 @@ void Item_decimal::print(String *str) } bool Item_decimal::eq(const Item *item, bool binary_cmp) const { if (type() == item->type() && item->basic_const_item()) { /* We need to cast off const to call val_decimal(). This should be OK for a basic constant. Additionally, we can pass 0 as a true decimal constant will return its internal decimal storage and ignore the argument. */ Item *arg= (Item*) item; my_decimal *value= arg->val_decimal(0); return !my_decimal_cmp(&decimal_value, value); } return 0; } String *Item_float::val_str(String *str) { // following assert is redundant, because fixed=1 assigned in constructor Loading Loading @@ -2195,6 +2218,72 @@ bool Item_param::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref) } bool Item_param::basic_const_item() const { if (state == NO_VALUE || state == TIME_VALUE) return FALSE; return TRUE; } Item * Item_param::new_item() { /* see comments in the header file */ switch (state) { case NULL_VALUE: return new Item_null(name); case INT_VALUE: return new Item_int(name, value.integer, max_length); case REAL_VALUE: return new Item_float(name, value.real, decimals, max_length); case STRING_VALUE: case LONG_DATA_VALUE: return new Item_string(name, str_value.c_ptr_quick(), str_value.length(), str_value.charset()); case TIME_VALUE: break; case NO_VALUE: default: DBUG_ASSERT(0); }; return 0; } bool Item_param::eq(const Item *arg, bool binary_cmp) const { Item *item; if (!basic_const_item() || !arg->basic_const_item() || arg->type() != type()) return FALSE; /* We need to cast off const to call val_int(). This should be OK for a basic constant. */ item= (Item*) arg; switch (state) { case NULL_VALUE: return TRUE; case INT_VALUE: return value.integer == item->val_int() && unsigned_flag == item->unsigned_flag; case REAL_VALUE: return value.real == item->val_real(); case STRING_VALUE: case LONG_DATA_VALUE: if (binary_cmp) return !stringcmp(&str_value, &item->str_value); return !sortcmp(&str_value, &item->str_value, collation.collation); default: break; } return FALSE; } /* End of Item_param related */ void Item_param::print(String *str) { if (state == NO_VALUE) Loading Loading @@ -3412,6 +3501,22 @@ int Item_decimal::save_in_field(Field *field, bool no_conversions) } bool Item_int::eq(const Item *arg, bool binary_cmp) const { /* No need to check for null value as basic constant can't be NULL */ if (arg->basic_const_item() && arg->type() == type()) { /* We need to cast off const to call val_int(). This should be OK for a basic constant. */ Item *item= (Item*) arg; return item->val_int() == value && item->unsigned_flag == unsigned_flag; } return FALSE; } Item_num *Item_uint::neg() { Item_decimal *item= new Item_decimal(value, 0); Loading Loading @@ -3499,6 +3604,21 @@ void Item_float::print(String *str) In number context this is a longlong value. */ bool Item_float::eq(const Item *arg, bool binary_cmp) const { if (arg->basic_const_item() && arg->type() == type()) { /* We need to cast off const to call val_int(). This should be OK for a basic constant. */ Item *item= (Item*) arg; return item->val_real() == value; } return FALSE; } inline uint char_val(char X) { return (uint) (X >= '0' && X <= '9' ? X-'0' : Loading Loading @@ -3569,6 +3689,17 @@ int Item_hex_string::save_in_field(Field *field, bool no_conversions) } bool Item_hex_string::eq(const Item *arg, bool binary_cmp) const { if (arg->basic_const_item() && arg->type() == type()) { if (binary_cmp) return !stringcmp(&str_value, &arg->str_value); return !sortcmp(&str_value, &arg->str_value, collation.collation); } return FALSE; } /* bin item. In string context this is a binary string. Loading
sql/item.h +25 −4 Original line number Diff line number Diff line Loading @@ -434,7 +434,7 @@ class Item { virtual table_map not_null_tables() const { return used_tables(); } /* Returns true if this is a simple constant item like an integer, not a constant expression a constant expression. Used in the optimizer to propagate basic constants. */ virtual bool basic_const_item() const { return 0; } /* cloning of constant items (0 if it is not const) */ Loading Loading @@ -914,7 +914,6 @@ class Item_param :public Item bool convert_str_value(THD *thd); Item *new_item() { return new Item_param(pos_in_query); } /* If value for parameter was not set we treat it as non-const so noone will use parameters value in fix_fields still Loading @@ -923,12 +922,29 @@ class Item_param :public Item virtual table_map used_tables() const { return state != NO_VALUE ? (table_map)0 : PARAM_TABLE_BIT; } void print(String *str); /* parameter never equal to other parameter of other item */ bool eq(const Item *item, bool binary_cmp) const { return 0; } bool is_null() { DBUG_ASSERT(state != NO_VALUE); return state == NULL_VALUE; } bool basic_const_item() const; /* This method is used to make a copy of a basic constant item when propagating constants in the optimizer. The reason to create a new item and not use the existing one is not precisely known (2005/04/16). Probably we are trying to preserve tree structure of items, in other words, avoid pointing at one item from two different nodes of the tree. Return a new basic constant item if parameter value is a basic constant, assert otherwise. This method is called only if basic_const_item returned TRUE. */ Item *new_item(); /* Implement by-value equality evaluation if parameter value is set and is a basic constant (integer, real or string). Otherwise return FALSE. */ bool eq(const Item *item, bool binary_cmp) const; }; class Item_int :public Item_num { public: Loading Loading @@ -956,6 +972,7 @@ class Item_int :public Item_num void cleanup() {} void print(String *str); Item_num *neg() { value= -value; return this; } bool eq(const Item *, bool binary_cmp) const; }; Loading Loading @@ -1022,8 +1039,10 @@ class Item_decimal :public Item_num unsigned_flag= !decimal_value.sign(); return this; } bool eq(const Item *, bool binary_cmp) const; }; class Item_float :public Item_num { char *presentation; Loading Loading @@ -1059,6 +1078,7 @@ class Item_float :public Item_num { return new Item_float(name, value, decimals, max_length); } Item_num *neg() { value= -value; return this; } void print(String *str); bool eq(const Item *, bool binary_cmp) const; }; Loading Loading @@ -1198,6 +1218,7 @@ class Item_hex_string: public Item enum_field_types field_type() const { return MYSQL_TYPE_VARCHAR; } // to prevent drop fixed flag (no need parent cleanup call) void cleanup() {} bool eq(const Item *item, bool binary_cmp) const; }; Loading