# ChangeSet
# 2006/09/07 08:07:18-07:00 igor@stripped
# Fixed bug #21698: erroneously a field could be replaced by an
# equal constant under any circumstances.
# In fact this substitution can be allowed if the field is
# not of a type string or if the field reference serves as
# an argument of a comparison predicate.
#
# mysql-test/r/func_str.result
# 2006/09/07 08:07:14-07:00 igor@stripped +35 -0
# Added test cases for bug #21698.
#
# mysql-test/r/heap_hash.result
# 2006/09/07 08:07:14-07:00 igor@stripped +1 -1
# Adjusted results after the fix for bug #21198.
#
# mysql-test/t/func_str.test
# 2006/09/07 08:07:14-07:00 igor@stripped +27 -0
# Added test cases for bug #21698.
#
# sql/item.cc
# 2006/09/07 08:07:14-07:00 igor@stripped +38 -3
# Fixed bug #21198.
# Added a method to check whether a field reference can be
# substituted for a constant equal to the field.
# This substitution is allowed if the field is not of a type string
# or if the field reference serves as an argument of a comparison
# predicate.
#
# sql/item.h
# 2006/09/07 08:07:14-07:00 igor@stripped +19 -1
# Fixed bug #21698.
# Added a new virtual transformation method for a item 'compile'
# with two callback function parameters.
# Added a new virtual method 'subst_argument_checker' to be used
# as an amnalyzer method.
# This method is supposed to set its in/out argument to NULL for
# the nodes where substitution of a string field for a constant
# is not valid.
#
# sql/item_cmpfunc.cc
# 2006/09/07 08:07:14-07:00 igor@stripped +55 -6
# Fixed bug #21698.
# Added an implementation of the compile method for class Item_cond.
# First it processes the Item_cond node with a callback function and if
# the latter returns TRUE it proceeds with a transformation performed by
# another callback function.
#
# sql/item_cmpfunc.h
# 2006/09/07 08:07:14-07:00 igor@stripped +4 -0
# Fixed bug #21698.
# Added the implementations of 'subst_argument_checker'
# for the Item_func and Item_cond classes.
# This method is supposed to set its in/out argument to NULL for
# the nodes where substitution of a string field for a constant
# is not valid.
# Added the declaration of an implementation of the compile method for
# class Item_cond.
# First it processes the Item_cond node with a callback function and if
# the latter returns TRUE it proceeds with a transformation performed by
# another callback function.
#
# sql/item_func.cc
# 2006/09/07 08:07:14-07:00 igor@stripped +56 -8
# Fixed bug #21698.
# Added an implementation of the compile method for class Item_func.
# First it processes the Item_func node with a callback function and if
# the latter returns TRUE it proceeds with a transformation performed by
# another callback function.
#
# sql/item_func.h
# 2006/09/07 08:07:14-07:00 igor@stripped +2 -0
# Fixed bug #21698.
# Added the declaration of the implementation of the compile method for
# class Item_func.
# First it processes the Item_func node with a callback function and if
# the latter returns TRUE it proceeds with a transformation performed by
# another callback function.
#
#
# sql/sql_select.cc
# 2006/09/07 08:07:14-07:00 igor@stripped +12 -5
# Fixed bug #21698.
# Limited the conditions at which a field can be substituted
# a for an equal constant in a formula.
# This substitution is allowed if the field is not of a type string
# or if the field reference serves as an argument of a comparison
# predicate.
#
diff -Nru a/mysql-test/r/func_str.result b/mysql-test/r/func_str.result
--- a/mysql-test/r/func_str.result 2006-09-07 09:07:50 -07:00
+++ b/mysql-test/r/func_str.result 2006-09-07 09:07:50 -07:00
@@ -1113,4 +1113,39 @@
select "18383815659218730760" + 0;
"18383815659218730760" + 0
1.8383815659219e+19
+CREATE TABLE t1 (code varchar(10));
+INSERT INTO t1 VALUES ('a12'), ('A12'), ('a13');
+SELECT ASCII(code), code FROM t1 WHERE code='A12';
+ASCII(code) code
+97 a12
+65 A12
+SELECT ASCII(code), code FROM t1 WHERE code='A12' AND ASCII(code)=65;
+ASCII(code) code
+65 A12
+INSERT INTO t1 VALUES ('a12 '), ('A12 ');
+SELECT LENGTH(code), code FROM t1 WHERE code='A12';
+LENGTH(code) code
+3 a12
+3 A12
+4 a12
+5 A12
+SELECT LENGTH(code), code FROM t1 WHERE code='A12' AND LENGTH(code)=5;
+LENGTH(code) code
+5 A12
+ALTER TABLE t1 ADD INDEX (code);
+CREATE TABLE t2 (id varchar(10) PRIMARY KEY);
+INSERT INTO t2 VALUES ('a11'), ('a12'), ('a13'), ('a14');
+SELECT * FROM t1 INNER JOIN t2 ON t1.code=t2.id
+WHERE t2.id='a12' AND (code < 'a00' OR LENGTH(code)=5);
+code id
+A12 a12
+EXPLAIN EXTENDED
+SELECT * FROM t1 INNER JOIN t2 ON code=id
+WHERE id='a12' AND (code < 'a00' OR LENGTH(code)=5);
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 ref code code 13 const 3 Using where; Using index
+1 SIMPLE t2 ref PRIMARY PRIMARY 12 const 1 Using where; Using index
+Warnings:
+Note 1003 select `test`.`t1`.`code` AS `code`,`test`.`t2`.`id` AS `id` from `test`.`t1`
join `test`.`t2` where ((`test`.`t1`.`code` = _latin1'a12') and (`test`.`t2`.`id` =
_latin1'a12') and (length(`test`.`t1`.`code`) = 5))
+DROP TABLE t1,t2;
End of 5.0 tests
diff -Nru a/mysql-test/r/heap_hash.result b/mysql-test/r/heap_hash.result
--- a/mysql-test/r/heap_hash.result 2006-09-07 09:07:50 -07:00
+++ b/mysql-test/r/heap_hash.result 2006-09-07 09:07:50 -07:00
@@ -354,7 +354,7 @@
explain select * from t1 ignore key(btree_idx), t3 where t1.name='matt' and t3.a =
concat('',t1.name) and t3.b=t1.name;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 ref heap_idx heap_idx 22 const 7 Using where
-1 SIMPLE t3 ref a a 44 const,const 7 Using where
+1 SIMPLE t3 ref a a 44 func,const 7 Using where
drop table t1, t2, t3;
create temporary table t1 ( a int, index (a) ) engine=memory;
insert into t1 values (1),(2),(3),(4),(5);
diff -Nru a/mysql-test/t/func_str.test b/mysql-test/t/func_str.test
--- a/mysql-test/t/func_str.test 2006-09-07 09:07:50 -07:00
+++ b/mysql-test/t/func_str.test 2006-09-07 09:07:50 -07:00
@@ -753,4 +753,31 @@
select conv("18383815659218730760",10,10) + 0;
select "18383815659218730760" + 0;
+#
+# Bug #21698: substitution of a string field for a constant under a function
+#
+
+CREATE TABLE t1 (code varchar(10));
+INSERT INTO t1 VALUES ('a12'), ('A12'), ('a13');
+
+SELECT ASCII(code), code FROM t1 WHERE code='A12';
+SELECT ASCII(code), code FROM t1 WHERE code='A12' AND ASCII(code)=65;
+
+INSERT INTO t1 VALUES ('a12 '), ('A12 ');
+
+SELECT LENGTH(code), code FROM t1 WHERE code='A12';
+SELECT LENGTH(code), code FROM t1 WHERE code='A12' AND LENGTH(code)=5;
+
+ALTER TABLE t1 ADD INDEX (code);
+CREATE TABLE t2 (id varchar(10) PRIMARY KEY);
+INSERT INTO t2 VALUES ('a11'), ('a12'), ('a13'), ('a14');
+
+SELECT * FROM t1 INNER JOIN t2 ON t1.code=t2.id
+ WHERE t2.id='a12' AND (code < 'a00' OR LENGTH(code)=5);
+EXPLAIN EXTENDED
+SELECT * FROM t1 INNER JOIN t2 ON code=id
+ WHERE id='a12' AND (code < 'a00' OR LENGTH(code)=5);
+
+DROP TABLE t1,t2;
+
--echo End of 5.0 tests
diff -Nru a/sql/item.cc b/sql/item.cc
--- a/sql/item.cc 2006-09-07 09:07:50 -07:00
+++ b/sql/item.cc 2006-09-07 09:07:50 -07:00
@@ -3746,13 +3746,48 @@
/*
+ Check whether a field can be substituted by an equal item
+
+ SYNOPSIS
+ equal_fields_propagator()
+ arg - *arg == TRUE is the field is in the context where
+ substitution for an equal item is valid
+
+ DESCRIPTION
+ The function checks whether a substitution of the field
+ occurrence for an equal item is valid.
+
+ NOTES
+ The following statement is not always true:
+ x=y => F(x)=F(x/y).
+ This means substitution of an item for an equal item not always
+ yields an equavalent condition.
+ Here's an example:
+ 'a'='a '
+ (LENGTH('a')=1) != (LENGTH('a ')=2)
+ Such a substitution is surely valid if either the substituted
+ field is not of a STRING type or if it is an argument of
+ a comparison predicate.
+
+ RETURN
+ TRUE substitution is valid
+ FALSE otherwise
+*/
+
+bool Item_field::subst_argument_checker(byte **arg)
+{
+ return (result_type() != STRING_RESULT) || (*arg);
+}
+
+
+/*
Set a pointer to the multiple equality the field reference belongs to
(if any)
SYNOPSIS
equal_fields_propagator()
- arg - reference to list of multiple equalities where
- the field (this object) is to be looked for
+ arg - reference to list of multiple equalities where
+ the field (this object) is to be looked for
DESCRIPTION
The function looks for a multiple equality containing the field item
@@ -3764,7 +3799,7 @@
NOTES
This function is supposed to be called as a callback parameter in calls
- of the transform method.
+ of the compile method.
RETURN VALUES
pointer to the replacing constant item, if the field item was substituted
diff -Nru a/sql/item.h b/sql/item.h
--- a/sql/item.h 2006-09-07 09:07:50 -07:00
+++ b/sql/item.h 2006-09-07 09:07:50 -07:00
@@ -410,10 +410,13 @@
};
-typedef bool (Item::*Item_processor)(byte *arg);
+typedef bool (Item::*Item_processor) (byte *arg);
+typedef bool (Item::*Item_analyzer) (byte **argp);
typedef Item* (Item::*Item_transformer) (byte *arg);
typedef void (*Cond_traverser) (const Item *item, void *arg);
+extern bool const_false_value;
+
class Item {
Item(const Item &); /* Prevent use of these */
@@ -739,6 +742,14 @@
return (this->*transformer)(arg);
}
+ virtual Item* compile(Item_analyzer analyzer, byte **arg_p,
+ Item_transformer transformer, byte *arg_t)
+ {
+ if ((this->*analyzer) (arg_p))
+ return ((this->*transformer) (arg_t));
+ return 0;
+ }
+
virtual void traverse_cond(Cond_traverser traverser,
void *arg, traverse_order order)
{
@@ -753,6 +764,12 @@
virtual bool change_context_processor(byte *context) { return 0; }
virtual bool reset_query_id_processor(byte *query_id) { return 0; }
virtual bool is_expensive_processor(byte *arg) { return 0; }
+ virtual bool subst_argument_checker(byte **arg)
+ {
+ if (*arg)
+ *arg = NULL;
+ return TRUE;
+ }
virtual Item *equal_fields_propagator(byte * arg) { return this; }
virtual Item *set_no_const_sub(byte *arg) { return this; }
@@ -1254,6 +1271,7 @@
return field->can_be_compared_as_longlong();
}
Item_equal *find_item_equal(COND_EQUAL *cond_equal);
+ bool subst_argument_checker(byte **arg);
Item *equal_fields_propagator(byte *arg);
Item *set_no_const_sub(byte *arg);
Item *replace_equal_field(byte *arg);
diff -Nru a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc
--- a/sql/item_cmpfunc.cc 2006-09-07 09:07:50 -07:00
+++ b/sql/item_cmpfunc.cc 2006-09-07 09:07:50 -07:00
@@ -2747,16 +2747,16 @@
SYNOPSIS
transform()
- transformer the transformer callback function to be applied to the nodes
- of the tree of the object
- arg parameter to be passed to the transformer
+ transformer the transformer callback function to be applied to the nodes
+ of the tree of the object
+ arg parameter to be passed to the transformer
DESCRIPTION
- The function recursively applies the transform method with the
- same transformer to each member item of the condition list.
+ The function recursively applies the transform method to each
+ member item of the condition list.
If the call of the method for a member item returns a new item
the old item is substituted for a new one.
- After this the transform method is applied to the root node
+ After this the transformer is applied to the root node
of the Item_cond object.
RETURN VALUES
@@ -2776,6 +2776,55 @@
li.replace(new_item);
}
return Item_func::transform(transformer, arg);
+}
+
+
+/*
+ Compile Item_cond object with a processor and a transformer callback functions
+
+ SYNOPSIS
+ compile()
+ analyzer the analyzer callback function to be applied to the nodes
+ of the tree of the object
+ arg_p in/out parameter to be passed to the analyzer
+ transformer the transformer callback function to be applied to the nodes
+ of the tree of the object
+ arg_t parameter to be passed to the transformer
+
+ DESCRIPTION
+ First the function applies the analyzer to the root node of
+ the Item_func object. Then if the analyzer succeeeds (returns TRUE)
+ the function recursively applies the compile method to member
+ item of the condition list.
+ If the call of the method for a member item returns a new item
+ the old item is substituted for a new one.
+ After this the transformer is applied to the root node
+ of the Item_cond object.
+
+ RETURN VALUES
+ Item returned as the result of transformation of the root node
+*/
+
+Item *Item_cond::compile(Item_analyzer analyzer, byte **arg_p,
+ Item_transformer transformer, byte *arg_t)
+{
+ if (!(this->*analyzer)(arg_p))
+ return 0;
+
+ byte *arg_v= *arg_p;
+ List_iterator<Item> li(list);
+ Item *item;
+ while ((item= li++))
+ {
+ /*
+ The same parameter value of arg_p must be passed
+ to analyze any argument of the condition formula.
+ */
+ Item *new_item= item->compile(analyzer, &arg_v, transformer, arg_t);
+ if (new_item && new_item != item)
+ li.replace(new_item);
+ }
+ return Item_func::transform(transformer, arg_t);
}
void Item_cond::traverse_cond(Cond_traverser traverser,
diff -Nru a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h
--- a/sql/item_cmpfunc.h 2006-09-07 09:07:50 -07:00
+++ b/sql/item_cmpfunc.h 2006-09-07 09:07:50 -07:00
@@ -240,6 +240,7 @@
}
Item *neg_transformer(THD *thd);
virtual Item *negated_item();
+ bool subst_argument_checker(byte **arg) { return TRUE; }
};
class Item_func_not :public Item_bool_func
@@ -1171,6 +1172,9 @@
Item *transform(Item_transformer transformer, byte *arg);
void traverse_cond(Cond_traverser, void *arg, traverse_order order);
void neg_arguments(THD *thd);
+ bool subst_argument_checker(byte **arg) { return TRUE; }
+ Item *compile(Item_analyzer analyzer, byte **arg_p,
+ Item_transformer transformer, byte *arg_t);
};
diff -Nru a/sql/item_func.cc b/sql/item_func.cc
--- a/sql/item_func.cc 2006-09-07 09:07:50 -07:00
+++ b/sql/item_func.cc 2006-09-07 09:07:50 -07:00
@@ -234,22 +234,21 @@
}
-
/*
Transform an Item_func object with a transformer callback function
SYNOPSIS
transform()
- transformer the transformer callback function to be applied to the nodes
- of the tree of the object
- argument parameter to be passed to the transformer
+ transformer the transformer callback function to be applied to the nodes
+ of the tree of the object
+ argument parameter to be passed to the transformer
DESCRIPTION
- The function recursively applies the transform method with the
- same transformer to each argument the function.
- If the call of the method for a member item returns a new item
+ The function recursively applies the transform method to each
+ argument of the Item_func node.
+ If the call of the method for an argument item returns a new item
the old item is substituted for a new one.
- After this the transform method is applied to the root node
+ After this the transformer is applied to the root node
of the Item_func object.
RETURN VALUES
@@ -273,6 +272,55 @@
return (this->*transformer)(argument);
}
+
+/*
+ Compile Item_func object with a processor and a transformer callback functions
+
+ SYNOPSIS
+ compile()
+ analyzer the analyzer callback function to be applied to the nodes
+ of the tree of the object
+ arg_p in/out parameter to be passed to the processor
+ transformer the transformer callback function to be applied to the nodes
+ of the tree of the object
+ arg_t parameter to be passed to the transformer
+
+ DESCRIPTION
+ First the function applies the analyzer to the root node of
+ the Item_func object. Then if the analizer succeeeds (returns TRUE)
+ the function recursively applies the compile method to each argument
+ of the Item_func node.
+ If the call of the method for an argument item returns a new item
+ the old item is substituted for a new one.
+ After this the transformer is applied to the root node
+ of the Item_func object.
+
+ RETURN VALUES
+ Item returned as the result of transformation of the root node
+*/
+
+Item *Item_func::compile(Item_analyzer analyzer, byte **arg_p,
+ Item_transformer transformer, byte *arg_t)
+{
+ if (!(this->*analyzer)(arg_p))
+ return 0;
+ byte *arg_v= *arg_p;
+ if (arg_count)
+ {
+ Item **arg,**arg_end;
+ for (arg= args, arg_end= args+arg_count; arg != arg_end; arg++)
+ {
+ /*
+ The same parameter value of arg_p must be passed
+ to analyze any argument of the condition formula.
+ */
+ Item *new_item= (*arg)->compile(analyzer, &arg_v, transformer, arg_t);
+ if (new_item && *arg != new_item)
+ current_thd->change_item_tree(arg, new_item);
+ }
+ }
+ return (this->*transformer)(arg_t);
+}
/* See comments in Item_cmp_func::split_sum_func() */
diff -Nru a/sql/item_func.h b/sql/item_func.h
--- a/sql/item_func.h 2006-09-07 09:07:50 -07:00
+++ b/sql/item_func.h 2006-09-07 09:07:50 -07:00
@@ -187,6 +187,8 @@
}
bool walk(Item_processor processor, byte *arg);
Item *transform(Item_transformer transformer, byte *arg);
+ Item* compile(Item_analyzer analyzer, byte **arg_p,
+ Item_transformer transformer, byte *arg_t);
void traverse_cond(Cond_traverser traverser,
void * arg, traverse_order order);
bool is_expensive_processor(byte *arg);
diff -Nru a/sql/sql_select.cc b/sql/sql_select.cc
--- a/sql/sql_select.cc 2006-09-07 09:07:50 -07:00
+++ b/sql/sql_select.cc 2006-09-07 09:07:50 -07:00
@@ -6593,8 +6593,8 @@
SYNOPSIS
build_equal_items_for_cond()
- cond condition(expression) where to make replacement
- inherited path to all inherited multiple equality items
+ cond condition(expression) where to make replacement
+ inherited path to all inherited multiple equality items
DESCRIPTION
At each 'and' level the function detects items for equality predicates
@@ -6608,7 +6608,9 @@
The function also traverses the cond tree and and for each field reference
sets a pointer to the multiple equality item containing the field, if there
is any. If this multiple equality equates fields to a constant the
- function replace the field reference by the constant.
+ function replaces the field reference by the constant in the cases
+ when the field is not of a string type or when the field reference is
+ just an argument of a comparison predicate.
The function also determines the maximum number of members in
equality lists of each Item_cond_and object assigning it to
cond_equal->max_members of this object and updating accordingly
@@ -6756,9 +6758,14 @@
/*
For each field reference in cond, not from equal item predicates,
set a pointer to the multiple equality it belongs to (if there is any)
+ as soon the field is not of a string type or the field reference is
+ an argument of a comparison predicate.
*/
- cond= cond->transform(&Item::equal_fields_propagator,
- (byte *) inherited);
+ byte *dummy;
+ cond= cond->compile(&Item::subst_argument_checker,
+ &dummy,
+ &Item::equal_fields_propagator,
+ (byte *) inherited);
cond->update_used_tables();
}
return cond;
| Thread |
|---|
| • bk commit - 5.0 tree (igor:1.2254) BUG#21698 | Sergey Petrunia | 7 Sep |