diff options
Diffstat (limited to 'sql/item_cmpfunc.h')
-rw-r--r-- | sql/item_cmpfunc.h | 1551 |
1 files changed, 1090 insertions, 461 deletions
diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h index 12a12e05845..eb1da504e7c 100644 --- a/sql/item_cmpfunc.h +++ b/sql/item_cmpfunc.h @@ -25,9 +25,18 @@ #include "thr_malloc.h" /* sql_calloc */ #include "item_func.h" /* Item_int_func, Item_bool_func */ -#include "my_regex.h" +#define PCRE_STATIC 1 /* Important on Windows */ +#include "pcre.h" /* pcre header file */ extern Item_result item_cmp_type(Item_result a,Item_result b); +inline Item_result item_cmp_type(const Item *a, const Item *b) +{ + return item_cmp_type(a->cmp_type(), b->cmp_type()); +} +inline Item_result item_cmp_type(Item_result a, const Item *b) +{ + return item_cmp_type(a, b->cmp_type()); +} class Item_bool_func2; class Arg_comparator; @@ -38,46 +47,46 @@ typedef int (*Item_field_cmpfunc)(Item *f1, Item *f2, void *arg); class Arg_comparator: public Sql_alloc { Item **a, **b; + Item_result m_compare_type; + CHARSET_INFO *m_compare_collation; arg_cmp_func func; - Item_result_field *owner; + Item_func_or_sum *owner; bool set_null; // TRUE <=> set owner->null_value Arg_comparator *comparators; // used only for compare_row() double precision; /* Fields used in DATE/DATETIME comparison. */ Item *a_cache, *b_cache; // Cached values of a and b items // when one of arguments is NULL. + int set_compare_func(Item_func_or_sum *owner, Item_result type); + int set_cmp_func(Item_func_or_sum *owner_arg, Item **a1, Item **a2); + + int compare_temporal(enum_field_types type); + int compare_e_temporal(enum_field_types type); + public: - DTCollation cmp_collation; /* Allow owner function to use string buffers. */ String value1, value2; - Arg_comparator(): set_null(TRUE), comparators(0), + Arg_comparator(): m_compare_type(STRING_RESULT), + m_compare_collation(&my_charset_bin), + set_null(TRUE), comparators(0), + a_cache(0), b_cache(0) {}; + Arg_comparator(Item **a1, Item **a2): a(a1), b(a2), + m_compare_type(STRING_RESULT), + m_compare_collation(&my_charset_bin), + set_null(TRUE), comparators(0), a_cache(0), b_cache(0) {}; - Arg_comparator(Item **a1, Item **a2): a(a1), b(a2), set_null(TRUE), - comparators(0), a_cache(0), b_cache(0) {}; - - int set_compare_func(Item_result_field *owner, Item_result type); - inline int set_compare_func(Item_result_field *owner_arg) - { - return set_compare_func(owner_arg, item_cmp_type((*a)->result_type(), - (*b)->result_type())); - } - int set_cmp_func(Item_result_field *owner_arg, - Item **a1, Item **a2, - Item_result type); - inline int set_cmp_func(Item_result_field *owner_arg, +public: + inline int set_cmp_func(Item_func_or_sum *owner_arg, Item **a1, Item **a2, bool set_null_arg) { set_null= set_null_arg; - return set_cmp_func(owner_arg, a1, a2, - item_cmp_type((*a1)->cmp_type(), - (*a2)->cmp_type())); + return set_cmp_func(owner_arg, a1, a2); } inline int compare() { return (this->*func)(); } int compare_string(); // compare args[0] & args[1] - int compare_binary_string(); // compare args[0] & args[1] int compare_real(); // compare args[0] & args[1] int compare_decimal(); // compare args[0] & args[1] int compare_int_signed(); // compare args[0] & args[1] @@ -86,7 +95,6 @@ public: int compare_int_unsigned(); int compare_row(); // compare args[0] & args[1] int compare_e_string(); // compare args[0] & args[1] - int compare_e_binary_string(); // compare args[0] & args[1] int compare_e_real(); // compare args[0] & args[1] int compare_e_decimal(); // compare args[0] & args[1] int compare_e_int(); // compare args[0] & args[1] @@ -94,18 +102,22 @@ public: int compare_e_row(); // compare args[0] & args[1] int compare_real_fixed(); int compare_e_real_fixed(); - int compare_datetime(); // compare args[0] & args[1] as DATETIMEs - int compare_e_datetime(); + int compare_datetime() { return compare_temporal(MYSQL_TYPE_DATETIME); } + int compare_e_datetime() { return compare_e_temporal(MYSQL_TYPE_DATETIME); } + int compare_time() { return compare_temporal(MYSQL_TYPE_TIME); } + int compare_e_time() { return compare_e_temporal(MYSQL_TYPE_TIME); } Item** cache_converted_constant(THD *thd, Item **value, Item **cache, Item_result type); - void set_datetime_cmp_func(Item_result_field *owner_arg, Item **a1, Item **b1); static arg_cmp_func comparator_matrix [6][2]; inline bool is_owner_equal_func() { return (owner->type() == Item::FUNC_ITEM && ((Item_func*)owner)->functype() == Item_func::EQUAL_FUNC); } + Item_result compare_type() const { return m_compare_type; } + CHARSET_INFO *compare_collation() const { return m_compare_collation; } + Arg_comparator *subcomparators() const { return comparators; } void cleanup() { delete [] comparators; @@ -114,14 +126,75 @@ public: friend class Item_func; }; + +class SEL_ARG; +struct KEY_PART; + class Item_bool_func :public Item_int_func { -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) {} +protected: + /* + Build a SEL_TREE for a simple predicate + @param param PARAM from SQL_SELECT::test_quick_select + @param field field in the predicate + @param value constant in the predicate + @return Pointer to the tree built tree + */ + virtual SEL_TREE *get_func_mm_tree(RANGE_OPT_PARAM *param, + Field *field, Item *value) + { + DBUG_ENTER("Item_bool_func::get_func_mm_tree"); + DBUG_ASSERT(0); + DBUG_RETURN(0); + } + /* + Return the full select tree for "field_item" and "value": + - a single SEL_TREE if the field is not in a multiple equality, or + - a conjuction of all SEL_TREEs for all fields from + the same multiple equality with "field_item". + */ + SEL_TREE *get_full_func_mm_tree(RANGE_OPT_PARAM *param, + Item_field *field_item, Item *value); + /** + Test if "item" and "value" are suitable for the range optimization + and get their full select tree. + + "Suitable" means: + - "item" is a field or a field reference + - "value" is NULL (e.g. WHERE field IS NULL), or + "value" is an unexpensive item (e.g. WHERE field OP value) + + @param item - the argument that is checked to be a field + @param value - the other argument + @returns - NULL if the arguments are not suitable for the range optimizer. + @returns - the full select tree if the arguments are suitable. + */ + SEL_TREE *get_full_func_mm_tree_for_args(RANGE_OPT_PARAM *param, + Item *item, Item *value) + { + DBUG_ENTER("Item_bool_func::get_full_func_mm_tree_for_args"); + Item *field= item->real_item(); + if (field->type() == Item::FIELD_ITEM && !field->const_item() && + (!value || !value->is_expensive())) + DBUG_RETURN(get_full_func_mm_tree(param, (Item_field *) field, value)); + DBUG_RETURN(NULL); + } + SEL_TREE *get_mm_parts(RANGE_OPT_PARAM *param, Field *field, + Item_func::Functype type, Item *value); + SEL_TREE *get_ne_mm_tree(RANGE_OPT_PARAM *param, + Field *field, Item *lt_value, Item *gt_value); + virtual SEL_ARG *get_mm_leaf(RANGE_OPT_PARAM *param, Field *field, + KEY_PART *key_part, + Item_func::Functype type, Item *value); +public: + Item_bool_func(THD *thd): Item_int_func(thd) {} + Item_bool_func(THD *thd, Item *a): Item_int_func(thd, a) {} + Item_bool_func(THD *thd, Item *a, Item *b): Item_int_func(thd, a, b) {} + Item_bool_func(THD *thd, Item *a, Item *b, Item *c): Item_int_func(thd, a, b, c) {} + Item_bool_func(THD *thd, List<Item> &list): Item_int_func(thd, list) { } Item_bool_func(THD *thd, Item_bool_func *item) :Item_int_func(thd, item) {} - bool is_bool_func() { return 1; } + bool is_bool_type() { return true; } + virtual CHARSET_INFO *compare_collation() const { return NULL; } void fix_length_and_dec() { decimals=0; max_length=1; } uint decimal_precision() const { return 1; } }; @@ -141,8 +214,8 @@ public: virtual void print(String *str, enum_query_type query_type); protected: - Item_func_truth(Item *a, bool a_value, bool a_affirmative) - : Item_bool_func(a), value(a_value), affirmative(a_affirmative) + Item_func_truth(THD *thd, Item *a, bool a_value, bool a_affirmative): + Item_bool_func(thd, a), value(a_value), affirmative(a_affirmative) {} ~Item_func_truth() @@ -167,7 +240,7 @@ private: class Item_func_istrue : public Item_func_truth { public: - Item_func_istrue(Item *a) : Item_func_truth(a, true, true) {} + Item_func_istrue(THD *thd, Item *a): Item_func_truth(thd, a, true, true) {} ~Item_func_istrue() {} virtual const char* func_name() const { return "istrue"; } }; @@ -180,7 +253,8 @@ public: class Item_func_isnottrue : public Item_func_truth { public: - Item_func_isnottrue(Item *a) : Item_func_truth(a, true, false) {} + Item_func_isnottrue(THD *thd, Item *a): + Item_func_truth(thd, a, true, false) {} ~Item_func_isnottrue() {} virtual const char* func_name() const { return "isnottrue"; } }; @@ -193,7 +267,7 @@ public: class Item_func_isfalse : public Item_func_truth { public: - Item_func_isfalse(Item *a) : Item_func_truth(a, false, true) {} + Item_func_isfalse(THD *thd, Item *a): Item_func_truth(thd, a, false, true) {} ~Item_func_isfalse() {} virtual const char* func_name() const { return "isfalse"; } }; @@ -206,7 +280,8 @@ public: class Item_func_isnotfalse : public Item_func_truth { public: - Item_func_isnotfalse(Item *a) : Item_func_truth(a, false, false) {} + Item_func_isnotfalse(THD *thd, Item *a): + Item_func_truth(thd, a, false, false) {} ~Item_func_isnotfalse() {} virtual const char* func_name() const { return "isnotfalse"; } }; @@ -245,12 +320,12 @@ protected: */ int result_for_null_param; public: - Item_in_optimizer(Item *a, Item_in_subselect *b): - Item_bool_func(a, reinterpret_cast<Item *>(b)), cache(0), expr_cache(0), + Item_in_optimizer(THD *thd, Item *a, Item *b): + Item_bool_func(thd, a, b), cache(0), expr_cache(0), save_cache(0), result_for_null_param(UNKNOWN) { with_subselect= true; } bool fix_fields(THD *, Item **); - bool fix_left(THD *thd, Item **ref); + bool fix_left(THD *thd); table_map not_null_tables() const { return 0; } bool is_null(); longlong val_int(); @@ -259,8 +334,8 @@ public: const char *func_name() const { return "<in_optimizer>"; } Item_cache **get_cache() { return &cache; } void keep_top_level_cache(); - Item *transform(Item_transformer transformer, uchar *arg); - virtual Item *expr_cache_insert_transformer(uchar *thd_arg); + Item *transform(THD *thd, Item_transformer transformer, uchar *arg); + virtual Item *expr_cache_insert_transformer(THD *thd, uchar *unused); bool is_expensive_processor(uchar *arg); bool is_expensive(); void set_join_tab_idx(uint join_tab_idx_arg) @@ -268,7 +343,9 @@ public: virtual void get_cache_parameters(List<Item> ¶meters); bool is_top_level_item(); bool eval_not_null_tables(uchar *opt_arg); - void fix_after_pullout(st_select_lex *new_parent, Item **ref); + void fix_after_pullout(st_select_lex *new_parent, Item **ref, bool merge); + bool invisible_mode(); + void reset_cache() { cache= NULL; } virtual void print(String *str, enum_query_type query_type); void restore_first_argument(); Item* get_wrapped_in_subselect_item() @@ -277,174 +354,189 @@ public: } }; -class Comp_creator -{ -public: - Comp_creator() {} /* Remove gcc warning */ - virtual ~Comp_creator() {} /* Remove gcc warning */ - /** - Create operation with given arguments. - */ - virtual Item_bool_func2* create(Item *a, Item *b) const = 0; - /** - Create operation with given arguments in swap order. - */ - virtual Item_bool_func2* create_swap(Item *a, Item *b) const = 0; - virtual const char* symbol(bool invert) const = 0; - virtual bool eqne_op() const = 0; - virtual bool l_op() const = 0; -}; -class Eq_creator :public Comp_creator -{ +/* + Functions and operators with two arguments that can use range optimizer. +*/ +class Item_bool_func2 :public Item_bool_func +{ /* Bool with 2 string args */ +protected: + void add_key_fields_optimize_op(JOIN *join, KEY_FIELD **key_fields, + uint *and_level, table_map usable_tables, + SARGABLE_PARAM **sargables, bool equal_func); public: - Eq_creator() {} /* Remove gcc warning */ - virtual ~Eq_creator() {} /* Remove gcc warning */ - virtual Item_bool_func2* create(Item *a, Item *b) const; - virtual Item_bool_func2* create_swap(Item *a, Item *b) const; - virtual const char* symbol(bool invert) const { return invert? "<>" : "="; } - virtual bool eqne_op() const { return 1; } - virtual bool l_op() const { return 0; } -}; + Item_bool_func2(THD *thd, Item *a, Item *b): + Item_bool_func(thd, a, b) { } -class Ne_creator :public Comp_creator -{ -public: - Ne_creator() {} /* Remove gcc warning */ - virtual ~Ne_creator() {} /* Remove gcc warning */ - virtual Item_bool_func2* create(Item *a, Item *b) const; - virtual Item_bool_func2* create_swap(Item *a, Item *b) const; - virtual const char* symbol(bool invert) const { return invert? "=" : "<>"; } - virtual bool eqne_op() const { return 1; } - virtual bool l_op() const { return 0; } + bool is_null() { return MY_TEST(args[0]->is_null() || args[1]->is_null()); } + COND *remove_eq_conds(THD *thd, Item::cond_result *cond_value, + bool top_level); + bool count_sargable_conds(uchar *arg); + /* + Specifies which result type the function uses to compare its arguments. + This method is used in equal field propagation. + */ + virtual Item_result compare_type() const + { + /* + Have STRING_RESULT by default, which means the function compares + val_str() results of the arguments. This is suitable for Item_func_like + and for Item_func_spatial_rel. + Note, Item_bool_rowready_func2 overrides this default behaviour. + */ + return STRING_RESULT; + } + SEL_TREE *get_mm_tree(RANGE_OPT_PARAM *param, Item **cond_ptr) + { + DBUG_ENTER("Item_bool_func2::get_mm_tree"); + DBUG_ASSERT(arg_count == 2); + SEL_TREE *ftree= get_full_func_mm_tree_for_args(param, args[0], args[1]); + if (!ftree) + ftree= Item_func::get_mm_tree(param, cond_ptr); + DBUG_RETURN(ftree); + } }; -class Gt_creator :public Comp_creator -{ -public: - Gt_creator() {} /* Remove gcc warning */ - virtual ~Gt_creator() {} /* Remove gcc warning */ - virtual Item_bool_func2* create(Item *a, Item *b) const; - virtual Item_bool_func2* create_swap(Item *a, Item *b) const; - virtual const char* symbol(bool invert) const { return invert? "<=" : ">"; } - virtual bool eqne_op() const { return 0; } - virtual bool l_op() const { return 0; } -}; -class Lt_creator :public Comp_creator +/** + A class for functions and operators that can use the range optimizer and + have a reverse function/operator that can also use the range optimizer, + so this condition: + WHERE value OP field + can be optimized as equivalent to: + WHERE field REV_OP value + + This class covers: + - scalar comparison predicates: <, <=, =, <=>, >=, > + - MBR and precise spatial relation predicates (e.g. SP_TOUCHES(x,y)) + + For example: + WHERE 10 > field + can be optimized as: + WHERE field < 10 +*/ +class Item_bool_func2_with_rev :public Item_bool_func2 { +protected: + SEL_TREE *get_func_mm_tree(RANGE_OPT_PARAM *param, + Field *field, Item *value) + { + DBUG_ENTER("Item_bool_func2_with_rev::get_func_mm_tree"); + Item_func::Functype func_type= + (value != arguments()[0]) ? functype() : rev_functype(); + DBUG_RETURN(get_mm_parts(param, field, func_type, value)); + } public: - Lt_creator() {} /* Remove gcc warning */ - virtual ~Lt_creator() {} /* Remove gcc warning */ - virtual Item_bool_func2* create(Item *a, Item *b) const; - virtual Item_bool_func2* create_swap(Item *a, Item *b) const; - virtual const char* symbol(bool invert) const { return invert? ">=" : "<"; } - virtual bool eqne_op() const { return 0; } - virtual bool l_op() const { return 1; } + Item_bool_func2_with_rev(THD *thd, Item *a, Item *b): + Item_bool_func2(thd, a, b) { } + virtual enum Functype rev_functype() const= 0; + SEL_TREE *get_mm_tree(RANGE_OPT_PARAM *param, Item **cond_ptr) + { + DBUG_ENTER("Item_bool_func2_with_rev::get_mm_tree"); + DBUG_ASSERT(arg_count == 2); + SEL_TREE *ftree; + /* + Even if get_full_func_mm_tree_for_args(param, args[0], args[1]) will not + return a range predicate it may still be possible to create one + by reversing the order of the operands. Note that this only + applies to predicates where both operands are fields. Example: A + query of the form + + WHERE t1.a OP t2.b + + In this case, args[0] == t1.a and args[1] == t2.b. + When creating range predicates for t2, + get_full_func_mm_tree_for_args(param, args[0], args[1]) + will return NULL because 'field' belongs to t1 and only + predicates that applies to t2 are of interest. In this case a + call to get_full_func_mm_tree_for_args() with reversed operands + may succeed. + */ + if (!(ftree= get_full_func_mm_tree_for_args(param, args[0], args[1])) && + !(ftree= get_full_func_mm_tree_for_args(param, args[1], args[0]))) + ftree= Item_func::get_mm_tree(param, cond_ptr); + DBUG_RETURN(ftree); + } }; -class Ge_creator :public Comp_creator -{ -public: - Ge_creator() {} /* Remove gcc warning */ - virtual ~Ge_creator() {} /* Remove gcc warning */ - virtual Item_bool_func2* create(Item *a, Item *b) const; - virtual Item_bool_func2* create_swap(Item *a, Item *b) const; - virtual const char* symbol(bool invert) const { return invert? "<" : ">="; } - virtual bool eqne_op() const { return 0; } - virtual bool l_op() const { return 0; } -}; -class Le_creator :public Comp_creator +class Item_bool_rowready_func2 :public Item_bool_func2_with_rev { -public: - Le_creator() {} /* Remove gcc warning */ - virtual ~Le_creator() {} /* Remove gcc warning */ - virtual Item_bool_func2* create(Item *a, Item *b) const; - virtual Item_bool_func2* create_swap(Item *a, Item *b) const; - virtual const char* symbol(bool invert) const { return invert? ">" : "<="; } - virtual bool eqne_op() const { return 0; } - virtual bool l_op() const { return 1; } -}; - -class Item_bool_func2 :public Item_int_func -{ /* Bool with 2 string args */ protected: Arg_comparator cmp; - bool abort_on_null; - public: - Item_bool_func2(Item *a,Item *b) - :Item_int_func(a,b), cmp(tmp_arg, tmp_arg+1), - abort_on_null(FALSE) { sargable= TRUE; } - void fix_length_and_dec(); - int set_cmp_func() + Item_bool_rowready_func2(THD *thd, Item *a, Item *b): + Item_bool_func2_with_rev(thd, a, b), cmp(tmp_arg, tmp_arg + 1) { - return cmp.set_cmp_func(this, tmp_arg, tmp_arg+1, TRUE); + allowed_arg_cols= 0; // Fetch this value from first argument } - optimize_type select_optimize() const { return OPTIMIZE_OP; } - virtual enum Functype rev_functype() const { return UNKNOWN_FUNC; } - bool have_rev_func() const { return rev_functype() != UNKNOWN_FUNC; } - - virtual inline void print(String *str, enum_query_type query_type) + void print(String *str, enum_query_type query_type) { Item_func::print_op(str, query_type); } - - bool is_null() { return test(args[0]->is_null() || args[1]->is_null()); } - bool is_bool_func() { return 1; } - CHARSET_INFO *compare_collation() { return cmp.cmp_collation.collation; } - uint decimal_precision() const { return 1; } - void top_level_item() { abort_on_null= TRUE; } + Item *neg_transformer(THD *thd); + virtual Item *negated_item(THD *thd); + Item* propagate_equal_fields(THD *thd, const Context &ctx, COND_EQUAL *cond) + { + Item_args::propagate_equal_fields(thd, + Context(ANY_SUBST, + cmp.compare_type(), + compare_collation()), + cond); + return this; + } + void fix_length_and_dec(); + int set_cmp_func() + { + return cmp.set_cmp_func(this, tmp_arg, tmp_arg + 1, true); + } + CHARSET_INFO *compare_collation() const { return cmp.compare_collation(); } + Item_result compare_type() const { return cmp.compare_type(); } Arg_comparator *get_comparator() { return &cmp; } void cleanup() { - Item_int_func::cleanup(); + Item_bool_func2::cleanup(); cmp.cleanup(); } - - friend class Arg_comparator; -}; - -class Item_bool_rowready_func2 :public Item_bool_func2 -{ -public: - Item_bool_rowready_func2(Item *a, Item *b) :Item_bool_func2(a, b) - { - allowed_arg_cols= 0; // Fetch this value from first argument - } - Item *neg_transformer(THD *thd); - virtual Item *negated_item(); - bool subst_argument_checker(uchar **arg) + void add_key_fields(JOIN *join, KEY_FIELD **key_fields, + uint *and_level, table_map usable_tables, + SARGABLE_PARAM **sargables) { - return (*arg != NULL); + return add_key_fields_optimize_op(join, key_fields, and_level, + usable_tables, sargables, false); } }; /** - XOR inherits from Item_bool_func2 because it is not optimized yet. + XOR inherits from Item_bool_func because it is not optimized yet. Later, when XOR is optimized, it needs to inherit from Item_cond instead. See WL#5800. */ -class Item_func_xor :public Item_bool_func2 +class Item_func_xor :public Item_bool_func { public: - Item_func_xor(Item *i1, Item *i2) :Item_bool_func2(i1, i2) {} + Item_func_xor(THD *thd, Item *i1, Item *i2): Item_bool_func(thd, i1, i2) {} enum Functype functype() const { return XOR_FUNC; } const char *func_name() const { return "xor"; } + void print(String *str, enum_query_type query_type) + { Item_func::print_op(str, query_type); } longlong val_int(); - void top_level_item() {} Item *neg_transformer(THD *thd); - bool subst_argument_checker(uchar **arg) + Item* propagate_equal_fields(THD *thd, const Context &ctx, COND_EQUAL *cond) { - return (*arg != NULL); + Item_args::propagate_equal_fields(thd, Context_boolean(), cond); + return this; } }; class Item_func_not :public Item_bool_func { + bool abort_on_null; public: - Item_func_not(Item *a) :Item_bool_func(a) {} + Item_func_not(THD *thd, Item *a): + Item_bool_func(thd, a), abort_on_null(FALSE) {} + virtual void top_level_item() { abort_on_null= 1; } + bool is_top_level_item() { return abort_on_null; } longlong val_int(); enum Functype functype() const { return NOT_FUNC; } const char *func_name() const { return "not"; } @@ -488,12 +580,16 @@ class Item_func_trig_cond: public Item_bool_func { bool *trig_var; public: - Item_func_trig_cond(Item *a, bool *f) : Item_bool_func(a) { trig_var= f; } + Item_func_trig_cond(THD *thd, Item *a, bool *f): Item_bool_func(thd, a) + { trig_var= f; } longlong val_int() { return *trig_var ? args[0]->val_int() : 1; } enum Functype functype() const { return TRIG_COND_FUNC; }; const char *func_name() const { return "trigcond"; }; bool const_item() const { return FALSE; } bool *get_trig_var() { return trig_var; } + void add_key_fields(JOIN *join, KEY_FIELD **key_fields, + uint *and_level, table_map usable_tables, + SARGABLE_PARAM **sargables); }; class Item_func_not_all :public Item_func_not @@ -502,16 +598,12 @@ class Item_func_not_all :public Item_func_not Item_sum_hybrid *test_sum_item; Item_maxmin_subselect *test_sub_item; - bool abort_on_null; public: bool show; - Item_func_not_all(Item *a) - :Item_func_not(a), test_sum_item(0), test_sub_item(0), abort_on_null(0), - show(0) + Item_func_not_all(THD *thd, Item *a): + Item_func_not(thd, a), test_sum_item(0), test_sub_item(0), show(0) {} - virtual void top_level_item() { abort_on_null= 1; } - bool is_top_level_item() { return abort_on_null; } table_map not_null_tables() const { return 0; } longlong val_int(); enum Functype functype() const { return NOT_ALL_FUNC; } @@ -530,7 +622,7 @@ class Item_func_nop_all :public Item_func_not_all { public: - Item_func_nop_all(Item *a) :Item_func_not_all(a) {} + Item_func_nop_all(THD *thd, Item *a): Item_func_not_all(thd, a) {} longlong val_int(); const char *func_name() const { return "<nop>"; } Item *neg_transformer(THD *thd); @@ -539,16 +631,30 @@ public: class Item_func_eq :public Item_bool_rowready_func2 { + bool abort_on_null; public: - Item_func_eq(Item *a,Item *b) : - Item_bool_rowready_func2(a,b), in_equality_no(UINT_MAX) + Item_func_eq(THD *thd, Item *a, Item *b): + Item_bool_rowready_func2(thd, a, b), + abort_on_null(false), in_equality_no(UINT_MAX) {} longlong val_int(); enum Functype functype() const { return EQ_FUNC; } enum Functype rev_functype() const { return EQ_FUNC; } cond_result eq_cmp_result() const { return COND_TRUE; } const char *func_name() const { return "="; } - Item *negated_item(); + void top_level_item() { abort_on_null= true; } + Item *negated_item(THD *thd); + COND *build_equal_items(THD *thd, COND_EQUAL *inherited, + bool link_item_fields, + COND_EQUAL **cond_equal_ref); + void add_key_fields(JOIN *join, KEY_FIELD **key_fields, + uint *and_level, table_map usable_tables, + SARGABLE_PARAM **sargables) + { + return add_key_fields_optimize_op(join, key_fields, and_level, + usable_tables, sargables, true); + } + bool check_equality(THD *thd, COND_EQUAL *cond, List<Item> *eq_list); /* - If this equality is created from the subquery's IN-equality: number of the item it was created from, e.g. for @@ -557,12 +663,15 @@ public: - Otherwise, UINT_MAX */ uint in_equality_no; + virtual uint exists2in_reserved_items() { return 1; }; + friend class Arg_comparator; }; class Item_func_equal :public Item_bool_rowready_func2 { public: - Item_func_equal(Item *a,Item *b) :Item_bool_rowready_func2(a,b) {}; + Item_func_equal(THD *thd, Item *a, Item *b): + Item_bool_rowready_func2(thd, a, b) {} longlong val_int(); void fix_length_and_dec(); table_map not_null_tables() const { return 0; } @@ -571,71 +680,92 @@ public: cond_result eq_cmp_result() const { return COND_TRUE; } const char *func_name() const { return "<=>"; } Item *neg_transformer(THD *thd) { return 0; } + void add_key_fields(JOIN *join, KEY_FIELD **key_fields, + uint *and_level, table_map usable_tables, + SARGABLE_PARAM **sargables) + { + return add_key_fields_optimize_op(join, key_fields, and_level, + usable_tables, sargables, true); + } }; class Item_func_ge :public Item_bool_rowready_func2 { public: - Item_func_ge(Item *a,Item *b) :Item_bool_rowready_func2(a,b) {}; + Item_func_ge(THD *thd, Item *a, Item *b): + Item_bool_rowready_func2(thd, a, b) {}; longlong val_int(); enum Functype functype() const { return GE_FUNC; } enum Functype rev_functype() const { return LE_FUNC; } cond_result eq_cmp_result() const { return COND_TRUE; } const char *func_name() const { return ">="; } - Item *negated_item(); + Item *negated_item(THD *thd); }; class Item_func_gt :public Item_bool_rowready_func2 { public: - Item_func_gt(Item *a,Item *b) :Item_bool_rowready_func2(a,b) {}; + Item_func_gt(THD *thd, Item *a, Item *b): + Item_bool_rowready_func2(thd, a, b) {}; longlong val_int(); enum Functype functype() const { return GT_FUNC; } enum Functype rev_functype() const { return LT_FUNC; } cond_result eq_cmp_result() const { return COND_FALSE; } const char *func_name() const { return ">"; } - Item *negated_item(); + Item *negated_item(THD *thd); }; class Item_func_le :public Item_bool_rowready_func2 { public: - Item_func_le(Item *a,Item *b) :Item_bool_rowready_func2(a,b) {}; + Item_func_le(THD *thd, Item *a, Item *b): + Item_bool_rowready_func2(thd, a, b) {}; longlong val_int(); enum Functype functype() const { return LE_FUNC; } enum Functype rev_functype() const { return GE_FUNC; } cond_result eq_cmp_result() const { return COND_TRUE; } const char *func_name() const { return "<="; } - Item *negated_item(); + Item *negated_item(THD *thd); }; class Item_func_lt :public Item_bool_rowready_func2 { public: - Item_func_lt(Item *a,Item *b) :Item_bool_rowready_func2(a,b) {} + Item_func_lt(THD *thd, Item *a, Item *b): + Item_bool_rowready_func2(thd, a, b) {} longlong val_int(); enum Functype functype() const { return LT_FUNC; } enum Functype rev_functype() const { return GT_FUNC; } cond_result eq_cmp_result() const { return COND_FALSE; } const char *func_name() const { return "<"; } - Item *negated_item(); + Item *negated_item(THD *thd); }; class Item_func_ne :public Item_bool_rowready_func2 { +protected: + SEL_TREE *get_func_mm_tree(RANGE_OPT_PARAM *param, + Field *field, Item *value) + { + DBUG_ENTER("Item_func_ne::get_func_mm_tree"); + DBUG_RETURN(get_ne_mm_tree(param, field, value, value)); + } public: - Item_func_ne(Item *a,Item *b) :Item_bool_rowready_func2(a,b) {} + Item_func_ne(THD *thd, Item *a, Item *b): + Item_bool_rowready_func2(thd, a, b) {} longlong val_int(); enum Functype functype() const { return NE_FUNC; } + enum Functype rev_functype() const { return NE_FUNC; } cond_result eq_cmp_result() const { return COND_FALSE; } - optimize_type select_optimize() const { return OPTIMIZE_KEY; } const char *func_name() const { return "<>"; } - Item *negated_item(); + Item *negated_item(THD *thd); + void add_key_fields(JOIN *join, KEY_FIELD **key_fields, uint *and_level, + table_map usable_tables, SARGABLE_PARAM **sargables); }; @@ -648,52 +778,74 @@ public: */ -class Item_func_opt_neg :public Item_int_func +class Item_func_opt_neg :public Item_bool_func { +protected: + /* + The result type that will be used for comparison. + cmp_type() of all arguments are collected to here. + */ + Item_result m_compare_type; + /* + The collation that will be used for comparison in case + when m_compare_type is STRING_RESULT. + */ + DTCollation cmp_collation; public: bool negated; /* <=> the item represents NOT <func> */ bool pred_level; /* <=> [NOT] <func> is used on a predicate level */ public: - Item_func_opt_neg(Item *a, Item *b, Item *c) - :Item_int_func(a, b, c), negated(0), pred_level(0) {} - Item_func_opt_neg(List<Item> &list) - :Item_int_func(list), negated(0), pred_level(0) {} + Item_func_opt_neg(THD *thd, Item *a, Item *b, Item *c): + Item_bool_func(thd, a, b, c), negated(0), pred_level(0) {} + Item_func_opt_neg(THD *thd, List<Item> &list): + Item_bool_func(thd, list), negated(0), pred_level(0) {} public: inline void negate() { negated= !negated; } inline void top_level_item() { pred_level= 1; } + bool is_top_level_item() const { return pred_level; } Item *neg_transformer(THD *thd) { negated= !negated; return this; } bool eq(const Item *item, bool binary_cmp) const; - bool subst_argument_checker(uchar **arg) { return TRUE; } + CHARSET_INFO *compare_collation() const { return cmp_collation.collation; } + Item* propagate_equal_fields(THD *, const Context &, COND_EQUAL *) = 0; }; class Item_func_between :public Item_func_opt_neg { - DTCollation cmp_collation; +protected: + SEL_TREE *get_func_mm_tree(RANGE_OPT_PARAM *param, + Field *field, Item *value); public: - Item_result cmp_type; String value0,value1,value2; /* TRUE <=> arguments will be compared as dates. */ Item *compare_as_dates; - Item_func_between(Item *a, Item *b, Item *c) - :Item_func_opt_neg(a, b, c), compare_as_dates(FALSE) { sargable= TRUE; } + Item_func_between(THD *thd, Item *a, Item *b, Item *c): + Item_func_opt_neg(thd, a, b, c), compare_as_dates(FALSE) { } longlong val_int(); - optimize_type select_optimize() const { return OPTIMIZE_KEY; } enum Functype functype() const { return BETWEEN; } const char *func_name() const { return "between"; } - bool fix_fields(THD *, Item **); void fix_length_and_dec(); virtual void print(String *str, enum_query_type query_type); - bool is_bool_func() { return 1; } - CHARSET_INFO *compare_collation() { return cmp_collation.collation; } - uint decimal_precision() const { return 1; } bool eval_not_null_tables(uchar *opt_arg); - void fix_after_pullout(st_select_lex *new_parent, Item **ref); + void fix_after_pullout(st_select_lex *new_parent, Item **ref, bool merge); bool count_sargable_conds(uchar *arg); + void add_key_fields(JOIN *join, KEY_FIELD **key_fields, + uint *and_level, table_map usable_tables, + SARGABLE_PARAM **sargables); + SEL_TREE *get_mm_tree(RANGE_OPT_PARAM *param, Item **cond_ptr); + Item* propagate_equal_fields(THD *thd, const Context &ctx, COND_EQUAL *cond) + { + Item_args::propagate_equal_fields(thd, + Context(ANY_SUBST, + m_compare_type, + compare_collation()), + cond); + return this; + } longlong val_int_cmp_string(); longlong val_int_cmp_int(); @@ -702,21 +854,19 @@ public: }; -class Item_func_strcmp :public Item_bool_func2 +class Item_func_strcmp :public Item_int_func { + String value1, value2; + DTCollation cmp_collation; public: - Item_func_strcmp(Item *a,Item *b) :Item_bool_func2(a,b) {} + Item_func_strcmp(THD *thd, Item *a, Item *b): + Item_int_func(thd, a, b) {} longlong val_int(); - optimize_type select_optimize() const { return OPTIMIZE_NONE; } + uint decimal_precision() const { return 1; } const char *func_name() const { return "strcmp"; } - - virtual inline void print(String *str, enum_query_type query_type) - { - Item_func::print(str, query_type); - } void fix_length_and_dec() { - Item_bool_func2::fix_length_and_dec(); + agg_arg_charsets_for_comparison(cmp_collation, args, 2); fix_char_length(2); // returns "1" or "0" or "-1" } }; @@ -735,8 +885,8 @@ class Item_func_interval :public Item_int_func bool use_decimal_comparison; interval_range *intervals; public: - Item_func_interval(Item_row *a) - :Item_int_func(a),row(a),intervals(0) + Item_func_interval(THD *thd, Item_row *a): + Item_int_func(thd, a), row(a), intervals(0) { allowed_arg_cols= 0; // Fetch this value from first argument } @@ -745,48 +895,90 @@ public: void fix_length_and_dec(); const char *func_name() const { return "interval"; } uint decimal_precision() const { return 2; } + void print(String *str, enum_query_type query_type) + { + str->append(func_name()); + print_args(str, 0, query_type); + } }; class Item_func_coalesce :public Item_func_hybrid_field_type { public: - Item_func_coalesce(Item *a, Item *b) :Item_func_hybrid_field_type(a, b) {} - Item_func_coalesce(List<Item> &list) :Item_func_hybrid_field_type(list) {} + Item_func_coalesce(THD *thd, Item *a, Item *b): + Item_func_hybrid_field_type(thd, a, b) {} + Item_func_coalesce(THD *thd, List<Item> &list): + Item_func_hybrid_field_type(thd, list) {} double real_op(); longlong int_op(); String *str_op(String *); my_decimal *decimal_op(my_decimal *); bool date_op(MYSQL_TIME *ltime,uint fuzzydate); - void fix_length_and_dec(); + void fix_length_and_dec() + { + set_handler_by_field_type(agg_field_type(args, arg_count, true)); + fix_attributes(args, arg_count); + } const char *func_name() const { return "coalesce"; } table_map not_null_tables() const { return 0; } }; -class Item_func_ifnull :public Item_func_coalesce +/* + Case abbreviations that aggregate its result field type by two arguments: + IFNULL(arg1, arg2) + IF(switch, arg1, arg2) +*/ +class Item_func_case_abbreviation2 :public Item_func_hybrid_field_type { protected: - bool field_type_defined; + void fix_length_and_dec2(Item **items) + { + set_handler_by_field_type(agg_field_type(items, 2, true)); + fix_attributes(items, 2); + } + uint decimal_precision2(Item **args) const; +public: + Item_func_case_abbreviation2(THD *thd, Item *a, Item *b): + Item_func_hybrid_field_type(thd, a, b) { } + Item_func_case_abbreviation2(THD *thd, Item *a, Item *b, Item *c): + Item_func_hybrid_field_type(thd, a, b, c) { } +}; + + +class Item_func_ifnull :public Item_func_case_abbreviation2 +{ public: - Item_func_ifnull(Item *a, Item *b) :Item_func_coalesce(a,b) {} + Item_func_ifnull(THD *thd, Item *a, Item *b): + Item_func_case_abbreviation2(thd, a, b) {} double real_op(); longlong int_op(); String *str_op(String *str); my_decimal *decimal_op(my_decimal *); bool date_op(MYSQL_TIME *ltime,uint fuzzydate); - void fix_length_and_dec(); + void fix_length_and_dec() + { + Item_func_case_abbreviation2::fix_length_and_dec2(args); + maybe_null= args[1]->maybe_null; + } const char *func_name() const { return "ifnull"; } - Field *tmp_table_field(TABLE *table); - uint decimal_precision() const; + Field *create_field_for_create_select(TABLE *table) + { return tmp_table_field_from_field_type(table, false, false); } + + table_map not_null_tables() const { return 0; } + uint decimal_precision() const + { + return Item_func_case_abbreviation2::decimal_precision2(args); + } }; -class Item_func_if :public Item_func_hybrid_field_type +class Item_func_if :public Item_func_case_abbreviation2 { public: - Item_func_if(Item *a,Item *b,Item *c) - :Item_func_hybrid_field_type(a,b,c) + Item_func_if(THD *thd, Item *a, Item *b, Item *c): + Item_func_case_abbreviation2(thd, a, b, c) {} bool date_op(MYSQL_TIME *ltime, uint fuzzydate); longlong int_op(); @@ -795,38 +987,95 @@ public: String *str_op(String *); bool fix_fields(THD *, Item **); void fix_length_and_dec(); - uint decimal_precision() const; + uint decimal_precision() const + { + return Item_func_case_abbreviation2::decimal_precision2(args + 1); + } const char *func_name() const { return "if"; } bool eval_not_null_tables(uchar *opt_arg); - void fix_after_pullout(st_select_lex *new_parent, Item **ref); + void fix_after_pullout(st_select_lex *new_parent, Item **ref, bool merge); private: void cache_type_info(Item *source); }; -class Item_func_nullif :public Item_bool_func2 +class Item_func_nullif :public Item_func_hybrid_field_type { - enum Item_result cached_result_type; -public: - Item_func_nullif(Item *a,Item *b) - :Item_bool_func2(a,b), cached_result_type(INT_RESULT) - {} - double val_real(); - longlong val_int(); - String *val_str(String *str); - my_decimal *val_decimal(my_decimal *); - enum Item_result result_type () const { return cached_result_type; } - void fix_length_and_dec(); - uint decimal_precision() const { return args[0]->decimal_precision(); } - const char *func_name() const { return "nullif"; } + Arg_comparator cmp; + /* + NULLIF(a,b) is a short for: + CASE WHEN a=b THEN NULL ELSE a END - virtual inline void print(String *str, enum_query_type query_type) + The left "a" is for comparison purposes. + The right "a" is for return value purposes. + These are two different "a" and they can be replaced to different items. + + The left "a" is in a comparison and can be replaced by: + - Item_func::convert_const_compared_to_int_field() + - agg_item_set_converter() in set_cmp_func() + - Arg_comparator::cache_converted_constant() in set_cmp_func() + + Both "a"s are subject to equal fields propagation and can be replaced by: + - Item_field::propagate_equal_fields(ANY_SUBST) for the left "a" + - Item_field::propagate_equal_fields(IDENTITY_SUBST) for the right "a" + */ + Item_cache *m_cache; + int compare(); + Item *m_arg0; +public: + /* + Here we pass three arguments to the parent constructor, as NULLIF + is a three-argument function, it needs two copies of the first argument + (see above). But fix_fields() will be confused if we try to prepare the + same Item twice (if args[0]==args[2]), so we hide the third argument + (decrementing arg_count) and copy args[2]=args[0] again after fix_fields(). + See also Item_func_nullif::fix_length_and_dec(). + */ + Item_func_nullif(THD *thd, Item *a, Item *b): + Item_func_hybrid_field_type(thd, a, b, a), + m_cache(NULL), + m_arg0(NULL) + { arg_count--; } + void cleanup() { - Item_func::print(str, query_type); + Item_func_hybrid_field_type::cleanup(); + arg_count= 2; // See the comment to the constructor } - + bool date_op(MYSQL_TIME *ltime, uint fuzzydate); + double real_op(); + longlong int_op(); + String *str_op(String *str); + my_decimal *decimal_op(my_decimal *); + void fix_length_and_dec(); + bool walk(Item_processor processor, bool walk_subquery, uchar *arg); + uint decimal_precision() const { return args[2]->decimal_precision(); } + const char *func_name() const { return "nullif"; } + void print(String *str, enum_query_type query_type); + void split_sum_func(THD *thd, Item **ref_pointer_array, List<Item> &fields, + uint flags); + void update_used_tables(); table_map not_null_tables() const { return 0; } bool is_null(); + Item* propagate_equal_fields(THD *thd, const Context &ctx, COND_EQUAL *cond) + { + Context cmpctx(ANY_SUBST, cmp.compare_type(), cmp.compare_collation()); + const Item *old0= args[0]; + args[0]->propagate_equal_fields_and_change_item_tree(thd, cmpctx, + cond, &args[0]); + args[1]->propagate_equal_fields_and_change_item_tree(thd, cmpctx, + cond, &args[1]); + /* + MDEV-9712 Performance degradation of nested NULLIF + ANY_SUBST is more relaxed than IDENTITY_SUBST. + If ANY_SUBST did not change args[0], + then we can skip propagation for args[2]. + */ + if (old0 != args[0]) + args[2]->propagate_equal_fields_and_change_item_tree(thd, + Context_identity(), + cond, &args[2]); + return this; + } }; @@ -857,7 +1106,7 @@ public: { my_qsort2(base,used_count,size,compare,(void*)collation); } - int find(Item *item); + bool find(Item *item); /* Create an instance of Item_{type} (e.g. Item_decimal) constant object @@ -867,7 +1116,7 @@ public: vector in form of Item_xxx constants without creating Item_xxx object for every array element you get (i.e. this implements "FlyWeight" pattern) */ - virtual Item* create_item() { return NULL; } + virtual Item* create_item(THD *thd) { return NULL; } /* Store the value at position #pos into provided item object @@ -882,7 +1131,7 @@ public: /* Compare values number pos1 and pos2 for equality */ bool compare_elems(uint pos1, uint pos2) { - return test(compare(collation, base + pos1*size, base + pos2*size)); + return MY_TEST(compare(collation, base + pos1 * size, base + pos2 * size)); } virtual Item_result result_type()= 0; }; @@ -891,20 +1140,29 @@ class in_string :public in_vector { char buff[STRING_BUFFER_USUAL_SIZE]; String tmp; + class Item_string_for_in_vector: public Item_string + { + public: + Item_string_for_in_vector(THD *thd, CHARSET_INFO *cs): + Item_string(thd, cs) + { } + void set_value(const String *str) + { + str_value= *str; + collation.set(str->charset()); + } + }; public: in_string(uint elements,qsort2_cmp cmp_func, CHARSET_INFO *cs); ~in_string(); void set(uint pos,Item *item); uchar *get_value(Item *item); - Item* create_item() - { - return new Item_string(collation); - } + Item* create_item(THD *thd); void value_to_item(uint pos, Item *item) { String *str=((String*) base)+pos; - Item_string *to= (Item_string*)item; - to->str_value= *str; + Item_string_for_in_vector *to= (Item_string_for_in_vector*) item; + to->set_value(str); } Item_result result_type() { return STRING_RESULT; } }; @@ -926,15 +1184,7 @@ public: in_longlong(uint elements); void set(uint pos,Item *item); uchar *get_value(Item *item); - - Item* create_item() - { - /* - We're created a signed INT, this may not be correct in - general case (see BUG#19342). - */ - return new Item_int((longlong)0); - } + Item* create_item(THD *thd); void value_to_item(uint pos, Item *item) { ((Item_int*) item)->value= ((packed_longlong*) base)[pos].val; @@ -965,10 +1215,7 @@ public: :in_longlong(elements), warn_item(warn_item_arg), lval_cache(0) {}; void set(uint pos,Item *item); uchar *get_value(Item *item); - Item* create_item() - { - return new Item_datetime(); - } + Item *create_item(THD *thd); void value_to_item(uint pos, Item *item) { packed_longlong *val= reinterpret_cast<packed_longlong*>(base)+pos; @@ -986,10 +1233,7 @@ public: in_double(uint elements); void set(uint pos,Item *item); uchar *get_value(Item *item); - Item *create_item() - { - return new Item_float(0.0, 0); - } + Item *create_item(THD *thd); void value_to_item(uint pos, Item *item) { ((Item_float*)item)->value= ((double*) base)[pos]; @@ -1005,10 +1249,7 @@ public: in_decimal(uint elements); void set(uint pos, Item *item); uchar *get_value(Item *item); - Item *create_item() - { - return new Item_decimal(0, FALSE); - } + Item *create_item(THD *thd); void value_to_item(uint pos, Item *item) { my_decimal *dec= ((my_decimal *)base) + pos; @@ -1031,19 +1272,30 @@ public: cmp_item() { cmp_charset= &my_charset_bin; } virtual ~cmp_item() {} virtual void store_value(Item *item)= 0; + /** + @returns result (TRUE, FALSE or UNKNOWN) of + "stored argument's value <> item's value" + */ virtual int cmp(Item *item)= 0; // for optimized IN with row virtual int compare(cmp_item *item)= 0; static cmp_item* get_comparator(Item_result type, Item * warn_item, CHARSET_INFO *cs); virtual cmp_item *make_same()= 0; - virtual void store_value_by_template(cmp_item *tmpl, Item *item) + virtual void store_value_by_template(THD *thd, cmp_item *tmpl, Item *item) { store_value(item); } }; -class cmp_item_string :public cmp_item +/// cmp_item which stores a scalar (i.e. non-ROW). +class cmp_item_scalar : public cmp_item +{ +protected: + bool m_null_value; ///< If stored value is NULL +}; + +class cmp_item_string : public cmp_item_scalar { protected: String *value_res; @@ -1069,14 +1321,27 @@ public: void store_value(Item *item) { value_res= item->val_str(&value); + m_null_value= item->null_value; + // Make sure to cache the result String inside "value" + if (value_res && value_res != &value) + { + if (value.copy(*value_res)) + value.set("", 0, item->collation.collation); + value_res= &value; + } } int cmp(Item *arg) { char buff[STRING_BUFFER_USUAL_SIZE]; - String tmp(buff, sizeof(buff), cmp_charset), *res; - res= arg->val_str(&tmp); - return (value_res ? (res ? sortcmp(value_res, res, cmp_charset) : 1) : - (res ? -1 : 0)); + String tmp(buff, sizeof(buff), cmp_charset), *res= arg->val_str(&tmp); + if (m_null_value || arg->null_value) + return UNKNOWN; + if (value_res && res) + return sortcmp(value_res, res, cmp_charset) != 0; + else if (!value_res && !res) + return FALSE; + else + return TRUE; } int compare(cmp_item *ci) { @@ -1091,7 +1356,7 @@ public: } }; -class cmp_item_int :public cmp_item +class cmp_item_int : public cmp_item_scalar { longlong value; public: @@ -1099,10 +1364,12 @@ public: void store_value(Item *item) { value= item->val_int(); + m_null_value= item->null_value; } int cmp(Item *arg) { - return value != arg->val_int(); + const bool rc= value != arg->val_int(); + return (m_null_value || arg->null_value) ? UNKNOWN : rc; } int compare(cmp_item *ci) { @@ -1118,7 +1385,7 @@ public: If the left item is a constant one then its value is cached in the lval_cache variable. */ -class cmp_item_datetime :public cmp_item +class cmp_item_datetime : public cmp_item_scalar { longlong value; public: @@ -1135,7 +1402,7 @@ public: cmp_item *make_same(); }; -class cmp_item_real :public cmp_item +class cmp_item_real : public cmp_item_scalar { double value; public: @@ -1143,10 +1410,12 @@ public: void store_value(Item *item) { value= item->val_real(); + m_null_value= item->null_value; } int cmp(Item *arg) { - return value != arg->val_real(); + const bool rc= value != arg->val_real(); + return (m_null_value || arg->null_value) ? UNKNOWN : rc; } int compare(cmp_item *ci) { @@ -1157,7 +1426,7 @@ public: }; -class cmp_item_decimal :public cmp_item +class cmp_item_decimal : public cmp_item_scalar { my_decimal value; public: @@ -1184,12 +1453,13 @@ public: void store_value(Item *item) { value_res= item->val_str(&value); + m_null_value= item->null_value; } int cmp(Item *item) { // Should never be called - DBUG_ASSERT(0); - return 1; + DBUG_ASSERT(false); + return TRUE; } int compare(cmp_item *ci) { @@ -1224,32 +1494,18 @@ public: class Item_func_case :public Item_func_hybrid_field_type { int first_expr_num, else_expr_num; - enum Item_result left_result_type; + enum Item_result left_cmp_type; String tmp_value; uint ncases; Item_result cmp_type; DTCollation cmp_collation; cmp_item *cmp_items[6]; /* For all result types */ cmp_item *case_item; + Item **arg_buffer; + uint m_found_types; public: - Item_func_case(List<Item> &list, Item *first_expr_arg, Item *else_expr_arg) - :Item_func_hybrid_field_type(), first_expr_num(-1), else_expr_num(-1), - left_result_type(INT_RESULT), case_item(0) - { - ncases= list.elements; - if (first_expr_arg) - { - first_expr_num= list.elements; - list.push_back(first_expr_arg); - } - if (else_expr_arg) - { - else_expr_num= list.elements; - list.push_back(else_expr_arg); - } - set_arguments(list); - bzero(&cmp_items, sizeof(cmp_items)); - } + Item_func_case(THD *thd, List<Item> &list, Item *first_expr_arg, + Item *else_expr_arg); double real_op(); longlong int_op(); String *str_op(String *); @@ -1262,54 +1518,65 @@ public: const char *func_name() const { return "case"; } virtual void print(String *str, enum_query_type query_type); Item *find_item(String *str); - CHARSET_INFO *compare_collation() { return cmp_collation.collation; } + CHARSET_INFO *compare_collation() const { return cmp_collation.collation; } void cleanup(); + Item* propagate_equal_fields(THD *thd, const Context &ctx, COND_EQUAL *cond); }; /* - The Item_func_in class implements the in_expr IN(values_list) function. + The Item_func_in class implements + in_expr IN (<in value list>) + and + in_expr NOT IN (<in value list>) The current implementation distinguishes 2 cases: - 1) all items in the value_list are constants and have the same + 1) all items in <in value list> are constants and have the same result type. This case is handled by in_vector class. - 2) items in the value_list have different result types or there is some - non-constant items. - In this case Item_func_in employs several cmp_item objects to performs - comparisons of in_expr and an item from the values_list. One cmp_item + 2) otherwise Item_func_in employs several cmp_item objects to perform + comparisons of in_expr and an item from <in value list>. One cmp_item object for each result type. Different result types are collected in the fix_length_and_dec() member function by means of collect_cmp_types() function. */ class Item_func_in :public Item_func_opt_neg { -public: - /* - an array of values when the right hand arguments of IN - are all SQL constant and there are no nulls + /** + Usable if <in value list> is made only of constants. Returns true if one + of these constants contains a NULL. Example: + IN ( (-5, (12,NULL)), ... ). */ + bool list_contains_null(); +protected: + SEL_TREE *get_func_mm_tree(RANGE_OPT_PARAM *param, + Field *field, Item *value); +public: + /// An array of values, created when the bisection lookup method is used in_vector *array; + /** + If there is some NULL among <in value list>, during a val_int() call; for + example + IN ( (1,(3,'col')), ... ), where 'col' is a column which evaluates to + NULL. + */ bool have_null; - /* - true when all arguments of the IN clause are of compatible types + /** + true when all arguments of the IN list are of compatible types and can be used safely as comparisons for key conditions */ bool arg_types_compatible; - Item_result left_result_type; + Item_result left_cmp_type; cmp_item *cmp_items[6]; /* One cmp_item for each result type */ - DTCollation cmp_collation; - Item_func_in(List<Item> &list) - :Item_func_opt_neg(list), array(0), have_null(0), - arg_types_compatible(FALSE) + Item_func_in(THD *thd, List<Item> &list): + Item_func_opt_neg(thd, list), array(0), have_null(0), + arg_types_compatible(FALSE) { bzero(&cmp_items, sizeof(cmp_items)); allowed_arg_cols= 0; // Fetch this value from first argument - sargable= TRUE; } longlong val_int(); bool fix_fields(THD *, Item **); void fix_length_and_dec(); - uint decimal_precision() const { return 1; } void cleanup() { uint i; @@ -1324,16 +1591,32 @@ public: } DBUG_VOID_RETURN; } - optimize_type select_optimize() const - { return OPTIMIZE_KEY; } + void add_key_fields(JOIN *join, KEY_FIELD **key_fields, uint *and_level, + table_map usable_tables, SARGABLE_PARAM **sargables); + SEL_TREE *get_mm_tree(RANGE_OPT_PARAM *param, Item **cond_ptr); + Item* propagate_equal_fields(THD *thd, const Context &ctx, COND_EQUAL *cond) + { + /* + Note, we pass ANY_SUBST, this makes sure that non of the args + will be replaced to a zero-filled Item_string. + Such a change would require rebuilding of cmp_items. + */ + Context cmpctx(ANY_SUBST, m_compare_type, + Item_func_in::compare_collation()); + for (uint i= 0; i < arg_count; i++) + { + if (arg_types_compatible || i > 0) + args[i]->propagate_equal_fields_and_change_item_tree(thd, cmpctx, + cond, &args[i]); + } + return this; + } virtual void print(String *str, enum_query_type query_type); enum Functype functype() const { return IN_FUNC; } const char *func_name() const { return " IN "; } - bool nulls_in_row(); - bool is_bool_func() { return 1; } - CHARSET_INFO *compare_collation() { return cmp_collation.collation; } bool eval_not_null_tables(uchar *opt_arg); - void fix_after_pullout(st_select_lex *new_parent, Item **ref); + void fix_after_pullout(st_select_lex *new_parent, Item **ref, bool merge); + bool count_sargable_conds(uchar *arg); }; class cmp_item_row :public cmp_item @@ -1348,7 +1631,7 @@ public: int cmp(Item *arg); int compare(cmp_item *arg); cmp_item *make_same(); - void store_value_by_template(cmp_item *tmpl, Item *); + void store_value_by_template(THD *thd, cmp_item *tmpl, Item *); friend void Item_func_in::fix_length_and_dec(); }; @@ -1357,7 +1640,7 @@ class in_row :public in_vector { cmp_item_row tmp; public: - in_row(uint elements, Item *); + in_row(THD *thd, uint elements, Item *); ~in_row(); void set(uint pos,Item *item); uchar *get_value(Item *item); @@ -1366,23 +1649,69 @@ public: }; /* Functions used by where clause */ +class Item_func_null_predicate :public Item_bool_func +{ +protected: + SEL_TREE *get_func_mm_tree(RANGE_OPT_PARAM *param, + Field *field, Item *value) + { + DBUG_ENTER("Item_func_null_predicate::get_func_mm_tree"); + DBUG_RETURN(get_mm_parts(param, field, functype(), value)); + } + SEL_ARG *get_mm_leaf(RANGE_OPT_PARAM *param, Field *field, + KEY_PART *key_part, + Item_func::Functype type, Item *value); +public: + Item_func_null_predicate(THD *thd, Item *a): Item_bool_func(thd, a) { } + void add_key_fields(JOIN *join, KEY_FIELD **key_fields, uint *and_level, + table_map usable_tables, SARGABLE_PARAM **sargables); + SEL_TREE *get_mm_tree(RANGE_OPT_PARAM *param, Item **cond_ptr) + { + DBUG_ENTER("Item_func_null_predicate::get_mm_tree"); + SEL_TREE *ftree= get_full_func_mm_tree_for_args(param, args[0], NULL); + if (!ftree) + ftree= Item_func::get_mm_tree(param, cond_ptr); + DBUG_RETURN(ftree); + } + CHARSET_INFO *compare_collation() const + { return args[0]->collation.collation; } + void fix_length_and_dec() { decimals=0; max_length=1; maybe_null=0; } + bool count_sargable_conds(uchar *arg); +}; + -class Item_func_isnull :public Item_bool_func +class Item_func_isnull :public Item_func_null_predicate { public: - Item_func_isnull(Item *a) :Item_bool_func(a) { sargable= TRUE; } + Item_func_isnull(THD *thd, Item *a): Item_func_null_predicate(thd, a) {} longlong val_int(); enum Functype functype() const { return ISNULL_FUNC; } void fix_length_and_dec() { - decimals=0; max_length=1; maybe_null=0; + Item_func_null_predicate::fix_length_and_dec(); update_used_tables(); } const char *func_name() const { return "isnull"; } + + bool arg_is_datetime_notnull_field() + { + Item **args= arguments(); + if (args[0]->real_item()->type() == Item::FIELD_ITEM) + { + Field *field=((Item_field*) args[0]->real_item())->field; + + if (((field->type() == MYSQL_TYPE_DATE) || + (field->type() == MYSQL_TYPE_DATETIME)) && + (field->flags & NOT_NULL_FLAG)) + return true; + } + return false; + } + /* Optimize case of not_null_column IS NULL */ virtual void update_used_tables() { - if (!args[0]->maybe_null) + if (!args[0]->maybe_null && !arg_is_datetime_notnull_field()) { used_tables_cache= 0; /* is always false */ const_item_cache= 1; @@ -1394,11 +1723,11 @@ public: const_item_cache= args[0]->const_item(); } } + COND *remove_eq_conds(THD *thd, Item::cond_result *cond_value, + bool top_level); virtual void print(String *str, enum_query_type query_type); table_map not_null_tables() const { return 0; } - optimize_type select_optimize() const { return OPTIMIZE_NULL; } Item *neg_transformer(THD *thd); - CHARSET_INFO *compare_collation() { return args[0]->collation.collation; } }; /* Functions used by HAVING for rewriting IN subquery */ @@ -1413,8 +1742,8 @@ class Item_is_not_null_test :public Item_func_isnull { Item_in_subselect* owner; public: - Item_is_not_null_test(Item_in_subselect* ow, Item *a) - :Item_func_isnull(a), owner(ow) + Item_is_not_null_test(THD *thd, Item_in_subselect* ow, Item *a): + Item_func_isnull(thd, a), owner(ow) {} enum Functype functype() const { return ISNOTNULLTEST_FUNC; } longlong val_int(); @@ -1429,25 +1758,20 @@ public: }; -class Item_func_isnotnull :public Item_bool_func +class Item_func_isnotnull :public Item_func_null_predicate { bool abort_on_null; public: - Item_func_isnotnull(Item *a) :Item_bool_func(a), abort_on_null(0) - { sargable= TRUE; } + Item_func_isnotnull(THD *thd, Item *a): + Item_func_null_predicate(thd, a), abort_on_null(0) + { } longlong val_int(); enum Functype functype() const { return ISNOTNULL_FUNC; } - void fix_length_and_dec() - { - decimals=0; max_length=1; maybe_null=0; - } const char *func_name() const { return "isnotnull"; } - optimize_type select_optimize() const { return OPTIMIZE_NULL; } table_map not_null_tables() const { return abort_on_null ? not_null_tables_cache : 0; } Item *neg_transformer(THD *thd); virtual void print(String *str, enum_query_type query_type); - CHARSET_INFO *compare_collation() { return args[0]->collation.collation; } void top_level_item() { abort_on_null=1; } }; @@ -1470,43 +1794,226 @@ class Item_func_like :public Item_bool_func2 enum { alphabet_size = 256 }; Item *escape_item; - + bool escape_used_in_parsing; + bool use_sampling; + DTCollation cmp_collation; + String cmp_value1, cmp_value2; + bool with_sargable_pattern() const; +protected: + SEL_TREE *get_func_mm_tree(RANGE_OPT_PARAM *param, + Field *field, Item *value) + { + DBUG_ENTER("Item_func_like::get_func_mm_tree"); + DBUG_RETURN(get_mm_parts(param, field, LIKE_FUNC, value)); + } + SEL_ARG *get_mm_leaf(RANGE_OPT_PARAM *param, Field *field, + KEY_PART *key_part, + Item_func::Functype type, Item *value); public: int escape; - Item_func_like(Item *a,Item *b, Item *escape_arg, bool escape_used) - :Item_bool_func2(a,b), canDoTurboBM(FALSE), pattern(0), pattern_len(0), - bmGs(0), bmBc(0), escape_item(escape_arg), - escape_used_in_parsing(escape_used) {} + Item_func_like(THD *thd, Item *a, Item *b, Item *escape_arg, bool escape_used): + Item_bool_func2(thd, a, b), canDoTurboBM(FALSE), pattern(0), pattern_len(0), + bmGs(0), bmBc(0), escape_item(escape_arg), + escape_used_in_parsing(escape_used), use_sampling(0) {} longlong val_int(); enum Functype functype() const { return LIKE_FUNC; } - optimize_type select_optimize() const; - cond_result eq_cmp_result() const { return COND_TRUE; } + void print(String *str, enum_query_type query_type) + { + Item_func::print_op(str, query_type); + } + CHARSET_INFO *compare_collation() const + { return cmp_collation.collation; } + cond_result eq_cmp_result() const + { + /** + We cannot always rewrite conditions as follows: + from: WHERE expr1=const AND expr1 LIKE expr2 + to: WHERE expr1=const AND const LIKE expr2 + or + from: WHERE expr1=const AND expr2 LIKE expr1 + to: WHERE expr1=const AND expr2 LIKE const + + because LIKE works differently comparing to the regular "=" operator: + + 1. LIKE performs a stricter one-character-to-one-character comparison + and does not recognize contractions and expansions. + Replacing "expr1" to "const in LIKE would make the condition + stricter in case of a complex collation. + + 2. LIKE does not ignore trailing spaces and thus works differently + from the "=" operator in case of "PAD SPACE" collations + (which are the majority in MariaDB). So, for "PAD SPACE" collations: + + - expr1=const - ignores trailing spaces + - const LIKE expr2 - does not ignore trailing spaces + - expr2 LIKE const - does not ignore trailing spaces + + Allow only "binary" for now. + It neither ignores trailing spaces nor has contractions/expansions. + + TODO: + We could still replace "expr1" to "const" in "expr1 LIKE expr2" + in case of a "PAD SPACE" collation, but only if "expr2" has '%' + at the end. + */ + return compare_collation() == &my_charset_bin ? COND_TRUE : COND_OK; + } + void add_key_fields(JOIN *join, KEY_FIELD **key_fields, uint *and_level, + table_map usable_tables, SARGABLE_PARAM **sargables); + SEL_TREE *get_mm_tree(RANGE_OPT_PARAM *param, Item **cond_ptr); + Item* propagate_equal_fields(THD *thd, const Context &ctx, COND_EQUAL *cond) + { + /* + LIKE differs from the regular comparison operator ('=') in the following: + - LIKE never ignores trailing spaces (even for PAD SPACE collations) + Propagation of equal fields with a PAD SPACE collation into LIKE + is not safe. + Example: + WHERE a='a ' AND a LIKE 'a' - returns true for 'a' + cannot be rewritten to: + WHERE a='a ' AND 'a ' LIKE 'a' - returns false for 'a' + Note, binary collations in MySQL/MariaDB, e.g. latin1_bin, + still have the PAD SPACE attribute and ignore trailing spaces! + - LIKE does not take into account contractions, expansions, + and ignorable characters. + Propagation of equal fields with contractions/expansions/ignorables + is also not safe. + + It's safe to propagate my_charset_bin (BINARY/VARBINARY/BLOB) values, + because they do not ignore trailing spaces and have one-to-one mapping + between a string and its weights. + The below condition should be true only for my_charset_bin + (as of version 10.1.7). + */ + uint flags= Item_func_like::compare_collation()->state; + if ((flags & MY_CS_NOPAD) && !(flags & MY_CS_NON1TO1)) + Item_args::propagate_equal_fields(thd, + Context(ANY_SUBST, + STRING_RESULT, + compare_collation()), + cond); + return this; + } const char *func_name() const { return "like"; } bool fix_fields(THD *thd, Item **ref); + void fix_length_and_dec() + { + max_length= 1; + agg_arg_charsets_for_comparison(cmp_collation, args, 2); + } void cleanup(); + + bool find_selective_predicates_list_processor(uchar *arg); +}; + + +class Regexp_processor_pcre +{ + pcre *m_pcre; + pcre_extra m_pcre_extra; + bool m_conversion_is_needed; + bool m_is_const; + int m_library_flags; + CHARSET_INFO *m_data_charset; + CHARSET_INFO *m_library_charset; + String m_prev_pattern; + int m_pcre_exec_rc; + int m_SubStrVec[30]; + void pcre_exec_warn(int rc) const; + int pcre_exec_with_warn(const pcre *code, const pcre_extra *extra, + const char *subject, int length, int startoffset, + int options, int *ovector, int ovecsize); +public: + String *convert_if_needed(String *src, String *converter); + String subject_converter; + String pattern_converter; + String replace_converter; + Regexp_processor_pcre() : + m_pcre(NULL), m_conversion_is_needed(true), m_is_const(0), + m_library_flags(0), + m_data_charset(&my_charset_utf8_general_ci), + m_library_charset(&my_charset_utf8_general_ci) + { + m_pcre_extra.flags= PCRE_EXTRA_MATCH_LIMIT_RECURSION; + m_pcre_extra.match_limit_recursion= 100L; + } + int default_regex_flags(); + void set_recursion_limit(THD *); + void init(CHARSET_INFO *data_charset, int extra_flags) + { + m_library_flags= default_regex_flags() | extra_flags | + (data_charset != &my_charset_bin ? + (PCRE_UTF8 | PCRE_UCP) : 0) | + ((data_charset->state & + (MY_CS_BINSORT | MY_CS_CSSORT)) ? 0 : PCRE_CASELESS); + + // Convert text data to utf-8. + m_library_charset= data_charset == &my_charset_bin ? + &my_charset_bin : &my_charset_utf8_general_ci; + + m_conversion_is_needed= (data_charset != &my_charset_bin) && + !my_charset_same(data_charset, m_library_charset); + } + void fix_owner(Item_func *owner, Item *subject_arg, Item *pattern_arg); + bool compile(String *pattern, bool send_error); + bool compile(Item *item, bool send_error); + bool recompile(Item *item) + { + return !m_is_const && compile(item, false); + } + bool exec(const char *str, int length, int offset); + bool exec(String *str, int offset, uint n_result_offsets_to_convert); + bool exec(Item *item, int offset, uint n_result_offsets_to_convert); + bool match() const { return m_pcre_exec_rc < 0 ? 0 : 1; } + int nsubpatterns() const { return m_pcre_exec_rc <= 0 ? 0 : m_pcre_exec_rc; } + int subpattern_start(int n) const + { + return m_pcre_exec_rc <= 0 ? 0 : m_SubStrVec[n * 2]; + } + int subpattern_end(int n) const + { + return m_pcre_exec_rc <= 0 ? 0 : m_SubStrVec[n * 2 + 1]; + } + int subpattern_length(int n) const + { + return subpattern_end(n) - subpattern_start(n); + } + void cleanup() + { + if (m_pcre) + { + pcre_free(m_pcre); + m_pcre= NULL; + } + m_prev_pattern.length(0); + } + bool is_compiled() const { return m_pcre != NULL; } + bool is_const() const { return m_is_const; } + void set_const(bool arg) { m_is_const= arg; } + CHARSET_INFO * library_charset() const { return m_library_charset; } }; class Item_func_regex :public Item_bool_func { - my_regex_t preg; - bool regex_compiled; - bool regex_is_const; - String prev_regexp; + Regexp_processor_pcre re; DTCollation cmp_collation; - CHARSET_INFO *regex_lib_charset; - int regex_lib_flags; - String conv; - int regcomp(bool send_error); public: - Item_func_regex(Item *a,Item *b) :Item_bool_func(a,b), - regex_compiled(0),regex_is_const(0) {} - void cleanup(); + Item_func_regex(THD *thd, Item *a, Item *b): Item_bool_func(thd, a, b) + {} + void cleanup() + { + DBUG_ENTER("Item_func_regex::cleanup"); + Item_bool_func::cleanup(); + re.cleanup(); + DBUG_VOID_RETURN; + } longlong val_int(); bool fix_fields(THD *thd, Item **ref); + void fix_length_and_dec(); const char *func_name() const { return "regexp"; } virtual inline void print(String *str, enum_query_type query_type) @@ -1514,7 +2021,28 @@ public: print_op(str, query_type); } - CHARSET_INFO *compare_collation() { return cmp_collation.collation; } + CHARSET_INFO *compare_collation() const { return cmp_collation.collation; } +}; + + +class Item_func_regexp_instr :public Item_int_func +{ + Regexp_processor_pcre re; + DTCollation cmp_collation; +public: + Item_func_regexp_instr(THD *thd, Item *a, Item *b): Item_int_func(thd, a, b) + {} + void cleanup() + { + DBUG_ENTER("Item_func_regexp_instr::cleanup"); + Item_int_func::cleanup(); + re.cleanup(); + DBUG_VOID_RETURN; + } + longlong val_int(); + bool fix_fields(THD *thd, Item **ref); + void fix_length_and_dec(); + const char *func_name() const { return "regexp_instr"; } }; @@ -1529,58 +2057,67 @@ protected: public: /* Item_cond() is only used to create top level items */ - Item_cond(): Item_bool_func(), abort_on_null(1) + Item_cond(THD *thd): Item_bool_func(thd), 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(THD *thd, Item *i1, Item *i2); Item_cond(THD *thd, Item_cond *item); - Item_cond(List<Item> &nlist) - :Item_bool_func(), list(nlist), abort_on_null(0) {} - bool add(Item *item) + Item_cond(THD *thd, List<Item> &nlist): + Item_bool_func(thd), list(nlist), abort_on_null(0) {} + bool add(Item *item, MEM_ROOT *root) { DBUG_ASSERT(item); - return list.push_back(item); + return list.push_back(item, root); } - bool add_at_head(Item *item) + bool add_at_head(Item *item, MEM_ROOT *root) { DBUG_ASSERT(item); - return list.push_front(item); + return list.push_front(item, root); } void add_at_head(List<Item> *nlist) { DBUG_ASSERT(nlist->elements); - list.prepand(nlist); + list.prepend(nlist); } void add_at_end(List<Item> *nlist) { DBUG_ASSERT(nlist->elements); - list.concat(nlist); + list.append(nlist); } bool fix_fields(THD *, Item **ref); - void fix_after_pullout(st_select_lex *new_parent, Item **ref); + void fix_after_pullout(st_select_lex *new_parent, Item **ref, bool merge); enum Type type() const { return COND_ITEM; } List<Item>* argument_list() { return &list; } table_map used_tables() const; - void update_used_tables(); + void update_used_tables() + { + used_tables_and_const_cache_init(); + used_tables_and_const_cache_update_and_join(list); + } + COND *build_equal_items(THD *thd, COND_EQUAL *inherited, + bool link_item_fields, + COND_EQUAL **cond_equal_ref); + COND *remove_eq_conds(THD *thd, Item::cond_result *cond_value, + bool top_level); + void add_key_fields(JOIN *join, KEY_FIELD **key_fields, + uint *and_level, table_map usable_tables, + SARGABLE_PARAM **sargables); + SEL_TREE *get_mm_tree(RANGE_OPT_PARAM *param, Item **cond_ptr); virtual void print(String *str, enum_query_type query_type); - void split_sum_func(THD *thd, Item **ref_pointer_array, List<Item> &fields); + void split_sum_func(THD *thd, Item **ref_pointer_array, List<Item> &fields, + uint flags); friend int setup_conds(THD *thd, TABLE_LIST *tables, TABLE_LIST *leaves, COND **conds); void top_level_item() { abort_on_null=1; } bool top_level() { return abort_on_null; } void copy_andor_arguments(THD *thd, Item_cond *item); bool walk(Item_processor processor, bool walk_subquery, uchar *arg); - Item *transform(Item_transformer transformer, uchar *arg); + Item *transform(THD *thd, Item_transformer transformer, uchar *arg); void traverse_cond(Cond_traverser, void *arg, traverse_order order); void neg_arguments(THD *thd); enum_field_types field_type() const { return MYSQL_TYPE_LONGLONG; } - bool subst_argument_checker(uchar **arg) { return TRUE; } - Item *compile(Item_analyzer analyzer, uchar **arg_p, + Item* propagate_equal_fields(THD *, const Context &, COND_EQUAL *); + Item *compile(THD *thd, Item_analyzer analyzer, uchar **arg_p, Item_transformer transformer, uchar *arg_t); bool eval_not_null_tables(uchar *opt_arg); }; @@ -1702,67 +2239,71 @@ class Item_equal: public Item_bool_func the equal_items should be ignored. */ bool cond_true; - /* - compare_as_dates=TRUE <-> constants equal to fields from equal_items - must be compared as datetimes and not as strings. - compare_as_dates can be TRUE only if with_const=TRUE - */ - bool compare_as_dates; - /* - The comparator used to compare constants equal to fields from equal_items - as datetimes. The comparator is used only if compare_as_dates=TRUE - */ - Arg_comparator cmp; - /* For Item_equal objects inside an OR clause: one of the fields that were used in the original equality. */ Item_field *context_field; + bool link_equal_fields; + + Item_result m_compare_type; + CHARSET_INFO *m_compare_collation; + String cmp_value1, cmp_value2; public: COND_EQUAL *upper_levels; /* multiple equalities of upper and levels */ - inline Item_equal() - : Item_bool_func(), with_const(FALSE), eval_item(0), cond_false(0), - context_field(NULL) - { const_item_cache=0; sargable= TRUE; } - Item_equal(Item *f1, Item *f2, bool with_const_item); - Item_equal(Item_equal *item_equal); + Item_equal(THD *thd, Item *f1, Item *f2, bool with_const_item); + Item_equal(THD *thd, Item_equal *item_equal); /* Currently the const item is always the first in the list of equal items */ inline Item* get_const() { return with_const ? equal_items.head() : NULL; } - void add_const(Item *c, Item *f = NULL); + void add_const(THD *thd, Item *c); /** Add a non-constant item to the multiple equality */ - void add(Item *f) { equal_items.push_back(f); } + void add(Item *f, MEM_ROOT *root) { equal_items.push_back(f, root); } bool contains(Field *field); Item* get_first(struct st_join_table *context, Item *field); /** Get number of field items / references to field items in this object */ - uint n_field_items() { return equal_items.elements-test(with_const); } - void merge(Item_equal *item); - bool merge_with_check(Item_equal *equal_item, bool save_merged); - void merge_into_list(List<Item_equal> *list, bool save_merged, + uint n_field_items() { return equal_items.elements - MY_TEST(with_const); } + void merge(THD *thd, Item_equal *item); + bool merge_with_check(THD *thd, Item_equal *equal_item, bool save_merged); + void merge_into_list(THD *thd, List<Item_equal> *list, bool save_merged, bool only_intersected); - void update_const(); + void update_const(THD *thd); enum Functype functype() const { return MULT_EQUAL_FUNC; } longlong val_int(); const char *func_name() const { return "multiple equal"; } - optimize_type select_optimize() const { return OPTIMIZE_EQUAL; } void sort(Item_field_cmpfunc compare, void *arg); void fix_length_and_dec(); bool fix_fields(THD *thd, Item **ref); + void cleanup() + { + delete eval_item; + eval_item= NULL; + } void update_used_tables(); + COND *build_equal_items(THD *thd, COND_EQUAL *inherited, + bool link_item_fields, + COND_EQUAL **cond_equal_ref); + void add_key_fields(JOIN *join, KEY_FIELD **key_fields, + uint *and_level, table_map usable_tables, + SARGABLE_PARAM **sargables); + SEL_TREE *get_mm_tree(RANGE_OPT_PARAM *param, Item **cond_ptr); bool walk(Item_processor processor, bool walk_subquery, uchar *arg); - Item *transform(Item_transformer transformer, uchar *arg); + Item *transform(THD *thd, Item_transformer transformer, uchar *arg); virtual void print(String *str, enum_query_type query_type); - CHARSET_INFO *compare_collation(); + Item_result compare_type() const { return m_compare_type; } + CHARSET_INFO *compare_collation() const { return m_compare_collation; } void set_context_field(Item_field *ctx_field) { context_field= ctx_field; } + void set_link_equal_fields(bool flag) { link_equal_fields= flag; } + friend class Item_equal_fields_iterator; bool count_sargable_conds(uchar *arg); friend class Item_equal_iterator<List_iterator_fast,Item>; friend class Item_equal_iterator<List_iterator,Item>; - friend Item *eliminate_item_equal(COND *cond, COND_EQUAL *upper_levels, - Item_equal *item_equal); + friend Item *eliminate_item_equal(THD *thd, COND *cond, + COND_EQUAL *upper_levels, + Item_equal *item_equal); friend bool setup_sj_materialization_part1(struct st_join_table *tab); friend bool setup_sj_materialization_part2(struct st_join_table *tab); }; @@ -1779,6 +2320,11 @@ public: { upper_levels= 0; } + COND_EQUAL(Item_equal *item, MEM_ROOT *mem_root) + :upper_levels(0) + { + current_level.push_back(item, mem_root); + } void copy(COND_EQUAL &cond_equal) { max_members= cond_equal.max_members; @@ -1873,27 +2419,29 @@ public: class Item_cond_and :public Item_cond { public: - COND_EQUAL cond_equal; /* contains list of Item_equal objects for - the current and level and reference - to multiple equalities of upper and levels */ - 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) {} - Item_cond_and(List<Item> &list_arg): Item_cond(list_arg) {} + COND_EQUAL m_cond_equal; /* contains list of Item_equal objects for + the current and level and reference + to multiple equalities of upper and levels */ + Item_cond_and(THD *thd): Item_cond(thd) {} + Item_cond_and(THD *thd, Item *i1,Item *i2): Item_cond(thd, i1, i2) {} + Item_cond_and(THD *thd, Item_cond_and *item): Item_cond(thd, item) {} + Item_cond_and(THD *thd, List<Item> &list_arg): Item_cond(thd, list_arg) {} enum Functype functype() const { return COND_AND_FUNC; } longlong val_int(); const char *func_name() const { return "and"; } table_map not_null_tables() const { return abort_on_null ? not_null_tables_cache: and_tables_cache; } - 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; - } + Item *copy_andor_structure(THD *thd); Item *neg_transformer(THD *thd); void mark_as_condition_AND_part(TABLE_LIST *embedding); + virtual uint exists2in_reserved_items() { return list.elements; }; + bool walk_top_and(Item_processor processor, uchar *arg); + COND *build_equal_items(THD *thd, COND_EQUAL *inherited, + bool link_item_fields, + COND_EQUAL **cond_equal_ref); + void add_key_fields(JOIN *join, KEY_FIELD **key_fields, uint *and_level, + table_map usable_tables, SARGABLE_PARAM **sargables); + SEL_TREE *get_mm_tree(RANGE_OPT_PARAM *param, Item **cond_ptr); }; inline bool is_cond_and(Item *item) @@ -1908,28 +2456,31 @@ inline bool is_cond_and(Item *item) 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) {} - Item_cond_or(List<Item> &list_arg): Item_cond(list_arg) {} + Item_cond_or(THD *thd): Item_cond(thd) {} + Item_cond_or(THD *thd, Item *i1,Item *i2): Item_cond(thd, i1, i2) {} + Item_cond_or(THD *thd, Item_cond_or *item): Item_cond(thd, item) {} + Item_cond_or(THD *thd, List<Item> &list_arg): Item_cond(thd, list_arg) {} 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; - } + Item *copy_andor_structure(THD *thd); Item *neg_transformer(THD *thd); }; +class Item_func_dyncol_check :public Item_bool_func +{ +public: + Item_func_dyncol_check(THD *thd, Item *str): Item_bool_func(thd, str) {} + longlong val_int(); + const char *func_name() const { return "column_check"; } +}; + class Item_func_dyncol_exists :public Item_bool_func { public: - Item_func_dyncol_exists(Item *str, Item *num) :Item_bool_func(str, num) {} + Item_func_dyncol_exists(THD *thd, Item *str, Item *num): + Item_bool_func(thd, str, num) {} longlong val_int(); const char *func_name() const { return "column_exists"; } }; @@ -1943,24 +2494,103 @@ inline bool is_cond_or(Item *item) return (cond_item->functype() == Item_func::COND_OR_FUNC); } -/* Some useful inline functions */ +Item *and_expressions(Item *a, Item *b, Item **org_item); + +longlong get_datetime_value(THD *thd, Item ***item_arg, Item **cache_arg, + enum_field_types f_type, bool *is_null); -inline Item *and_conds(Item *a, Item *b) + +class Comp_creator { - if (!b) return a; - if (!a) return b; - return new Item_cond_and(a, b); -} +public: + Comp_creator() {} /* Remove gcc warning */ + virtual ~Comp_creator() {} /* Remove gcc warning */ + /** + Create operation with given arguments. + */ + virtual Item_bool_rowready_func2* create(THD *thd, Item *a, Item *b) + const = 0; + /** + Create operation with given arguments in swap order. + */ + virtual Item_bool_rowready_func2* create_swap(THD *thd, Item *a, Item *b) + const = 0; + virtual const char* symbol(bool invert) const = 0; + virtual bool eqne_op() const = 0; + virtual bool l_op() const = 0; +}; +class Eq_creator :public Comp_creator +{ +public: + Eq_creator() {} /* Remove gcc warning */ + virtual ~Eq_creator() {} /* Remove gcc warning */ + Item_bool_rowready_func2* create(THD *thd, Item *a, Item *b) const; + Item_bool_rowready_func2* create_swap(THD *thd, Item *a, Item *b) const; + const char* symbol(bool invert) const { return invert? "<>" : "="; } + bool eqne_op() const { return 1; } + bool l_op() const { return 0; } +}; -Item *and_expressions(Item *a, Item *b, Item **org_item); +class Ne_creator :public Comp_creator +{ +public: + Ne_creator() {} /* Remove gcc warning */ + virtual ~Ne_creator() {} /* Remove gcc warning */ + Item_bool_rowready_func2* create(THD *thd, Item *a, Item *b) const; + Item_bool_rowready_func2* create_swap(THD *thd, Item *a, Item *b) const; + const char* symbol(bool invert) const { return invert? "=" : "<>"; } + bool eqne_op() const { return 1; } + bool l_op() const { return 0; } +}; -longlong get_datetime_value(THD *thd, Item ***item_arg, Item **cache_arg, - Item *warn_item, bool *is_null); +class Gt_creator :public Comp_creator +{ +public: + Gt_creator() {} /* Remove gcc warning */ + virtual ~Gt_creator() {} /* Remove gcc warning */ + Item_bool_rowready_func2* create(THD *thd, Item *a, Item *b) const; + Item_bool_rowready_func2* create_swap(THD *thd, Item *a, Item *b) const; + const char* symbol(bool invert) const { return invert? "<=" : ">"; } + bool eqne_op() const { return 0; } + bool l_op() const { return 0; } +}; + +class Lt_creator :public Comp_creator +{ +public: + Lt_creator() {} /* Remove gcc warning */ + virtual ~Lt_creator() {} /* Remove gcc warning */ + Item_bool_rowready_func2* create(THD *thd, Item *a, Item *b) const; + Item_bool_rowready_func2* create_swap(THD *thd, Item *a, Item *b) const; + const char* symbol(bool invert) const { return invert? ">=" : "<"; } + bool eqne_op() const { return 0; } + bool l_op() const { return 1; } +}; +class Ge_creator :public Comp_creator +{ +public: + Ge_creator() {} /* Remove gcc warning */ + virtual ~Ge_creator() {} /* Remove gcc warning */ + Item_bool_rowready_func2* create(THD *thd, Item *a, Item *b) const; + Item_bool_rowready_func2* create_swap(THD *thd, Item *a, Item *b) const; + const char* symbol(bool invert) const { return invert? "<" : ">="; } + bool eqne_op() const { return 0; } + bool l_op() const { return 0; } +}; -bool get_mysql_time_from_str(THD *thd, String *str, timestamp_type warn_type, - const char *warn_name, MYSQL_TIME *l_time); +class Le_creator :public Comp_creator +{ +public: + Le_creator() {} /* Remove gcc warning */ + virtual ~Le_creator() {} /* Remove gcc warning */ + Item_bool_rowready_func2* create(THD *thd, Item *a, Item *b) const; + Item_bool_rowready_func2* create_swap(THD *thd, Item *a, Item *b) const; + const char* symbol(bool invert) const { return invert? ">" : "<="; } + bool eqne_op() const { return 0; } + bool l_op() const { return 1; } +}; /* These need definitions from this file but the variables are defined @@ -1975,4 +2605,3 @@ extern Ge_creator ge_creator; extern Le_creator le_creator; #endif /* ITEM_CMPFUNC_INCLUDED */ - |