summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorkonstantin@mysql.com <>2005-05-03 12:49:22 +0400
committerkonstantin@mysql.com <>2005-05-03 12:49:22 +0400
commit018b8025c0ff6d1e4f5662728aca2a7d94c3704d (patch)
treefe569864863fd1ff447cbf75426bfd6e56b5d464
parent8e87db93183b1a4787d7cd0fdd89f1a8179bed68 (diff)
parent3589e78fdaad94daaefa69054588b2769c0096b1 (diff)
downloadmariadb-git-018b8025c0ff6d1e4f5662728aca2a7d94c3704d.tar.gz
Merge mysql.com:/opt/local/work/mysql-4.1-root
into mysql.com:/opt/local/work/mysql-4.1-9096-fresh
-rw-r--r--mysql-test/r/ps.result25
-rw-r--r--mysql-test/t/ps.test26
-rw-r--r--sql/item.cc114
-rw-r--r--sql/item.h27
-rw-r--r--sql/item_func.cc9
5 files changed, 192 insertions, 9 deletions
diff --git a/mysql-test/r/ps.result b/mysql-test/r/ps.result
index 89c369a51e8..df3e24afd1a 100644
--- a/mysql-test/r/ps.result
+++ b/mysql-test/r/ps.result
@@ -494,3 +494,28 @@ SELECT FOUND_ROWS();
FOUND_ROWS()
2
deallocate prepare stmt;
+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;
diff --git a/mysql-test/t/ps.test b/mysql-test/t/ps.test
index b204e59267e..ef36b8b1b0b 100644
--- a/mysql-test/t/ps.test
+++ b/mysql-test/t/ps.test
@@ -496,3 +496,29 @@ SELECT FOUND_ROWS();
execute stmt;
SELECT FOUND_ROWS();
deallocate prepare stmt;
+
+#
+# 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;
diff --git a/sql/item.cc b/sql/item.cc
index ffde92c4214..7e74bb1fd85 100644
--- a/sql/item.cc
+++ b/sql/item.cc
@@ -200,6 +200,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);
}
@@ -254,7 +259,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);
@@ -1356,6 +1361,70 @@ bool Item_param::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref)
return 0;
}
+
+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_real(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();
+ 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 */
@@ -1937,6 +2006,23 @@ int Item_int::save_in_field(Field *field, bool no_conversions)
return field->store(nr);
}
+
+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()
{
return new Item_real(name, - ((double) value), 0, max_length);
@@ -1951,6 +2037,21 @@ int Item_real::save_in_field(Field *field, bool no_conversions)
return field->store(nr);
}
+
+bool Item_real::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() == value;
+ }
+ return FALSE;
+}
+
/****************************************************************************
** varbinary item
** In string context this is a binary string
@@ -2017,6 +2118,17 @@ int Item_varbinary::save_in_field(Field *field, bool no_conversions)
}
+bool Item_varbinary::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;
+}
+
/*
Pack data in buffer for sending
*/
diff --git a/sql/item.h b/sql/item.h
index d576fbbc60a..3f3ff491bb5 100644
--- a/sql/item.h
+++ b/sql/item.h
@@ -235,7 +235,7 @@ public:
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) */
@@ -586,7 +586,6 @@ public:
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
@@ -595,12 +594,29 @@ public:
virtual table_map used_tables() const
{ return state != NO_VALUE ? (table_map)0 : PARAM_TABLE_BIT; }
void print(String *str) { str->append('?'); }
- /* 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:
@@ -627,6 +643,7 @@ public:
void cleanup() {}
void print(String *str);
Item_num *neg() { value= -value; return this; }
+ bool eq(const Item *, bool binary_cmp) const;
};
@@ -682,6 +699,7 @@ public:
void cleanup() {}
Item *new_item() { return new Item_real(name,value,decimals,max_length); }
Item_num *neg() { value= -value; return this; }
+ bool eq(const Item *, bool binary_cmp) const;
};
@@ -815,6 +833,7 @@ public:
enum_field_types field_type() const { return MYSQL_TYPE_STRING; }
// to prevent drop fixed flag (no need parent cleanup call)
void cleanup() {}
+ bool eq(const Item *item, bool binary_cmp) const;
};
diff --git a/sql/item_func.cc b/sql/item_func.cc
index 1b80ef06251..477849a66c5 100644
--- a/sql/item_func.cc
+++ b/sql/item_func.cc
@@ -863,10 +863,12 @@ void Item_func_neg::fix_length_and_dec()
maximum number of bytes real or integer may require. Note that all
constants are non negative so we don't need to account for removed '-'.
B) argument returns a string.
+ Use val() to get value as arg_type doesn't mean that item is
+ Item_int or Item_real due to existence of Item_param.
*/
if (arg_result == STRING_RESULT ||
- (arg_type == REAL_ITEM && ((Item_real*)args[0])->value >= 0) ||
- (arg_type == INT_ITEM && ((Item_int*)args[0])->value > 0))
+ (arg_type == REAL_ITEM && args[0]->val() >= 0) ||
+ (arg_type == INT_ITEM && args[0]->val_int() > 0))
max_length++;
if (args[0]->result_type() == INT_RESULT)
@@ -882,8 +884,7 @@ void Item_func_neg::fix_length_and_dec()
signed integers)
*/
if (args[0]->type() != INT_ITEM ||
- ((ulonglong) ((Item_uint*) args[0])->value <=
- (ulonglong) LONGLONG_MIN))
+ (((ulonglong) args[0]->val_int()) <= (ulonglong) LONGLONG_MIN))
hybrid_type= INT_RESULT;
}
}