← Back to team overview

maria-developers team mailing list archive

Re: [Commits] a288cb6: MDEV-8320 Allow index usage for DATE(datetime_column) = const.

 

Hi Alexey,

The patch doesn't have any testcase. Did you forget to add them?

On Tue, Sep 20, 2016 at 01:22:19PM +0400, Alexey Botchkov wrote:
> revision-id: a288cb698195b1e57abbb426f1cc9a804d65ff45 (mariadb-10.1.8-262-ga288cb6)
> parent(s): cb575abf76be82553b9c1c12c9112cbc6f53a547
> committer: Alexey Botchkov
> timestamp: 2016-09-20 13:19:08 +0400
> message:
> 
> MDEV-8320 Allow index usage for DATE(datetime_column) = const.
> 
>         create_reverse_func() method added so functions can specify how
>         to unpack field argument out of it.
>         opt_arguments added to Item_bool_func2 so it can have different
>         arguments for the optimizer and the calcualtion itself.
> 
> ---
>  sql/item.h           |  8 +++++
>  sql/item_cmpfunc.h   | 52 ++++++++++++-------------------
>  sql/item_func.h      |  5 +++
>  sql/item_timefunc.cc | 87 ++++++++++++++++++++++++++++++++++++++++++++++++++++
>  sql/item_timefunc.h  | 49 ++++++++++++++++++++++++++++-
>  sql/opt_range.cc     | 48 +++++++++++++++++++++++++++++
>  sql/sql_select.cc    | 70 ++++++++++++++++++++++++++++++++++++++----
>  7 files changed, 279 insertions(+), 40 deletions(-)
> 
> diff --git a/sql/item.h b/sql/item.h
> index 5b82548..200e2e0 100644
> --- a/sql/item.h
> +++ b/sql/item.h
> @@ -1212,6 +1212,14 @@ class Item: public Value_source,
>    {
>      return;
>    }
> +  virtual bool add_extra_key_fields(THD *thd,
> +                                    JOIN *join, KEY_FIELD **key_fields,
> +                                    uint *and_level,
> +                                    table_map usable_tables,
> +                                    SARGABLE_PARAM **sargables)
> +  {
> +    return false;
> +  }
>     /*
>       Make a select tree for all keys in a condition or a condition part
>       @param param         Context
> diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h
> index 6d432bd..516bb07 100644
> --- a/sql/item_cmpfunc.h
> +++ b/sql/item_cmpfunc.h
> @@ -136,6 +136,14 @@ class Item_bool_func :public Item_int_func
>  {
>  protected:
>    /*
> +    Some functions modify it's arguments for the optimizer.
> +    So for example the condition 'Func(fieldX) = constY' turned into
> +    'fieldX = cnuR(constY)' so that optimizer can use an index on fieldX.
> +  */
> +  Item *opt_args[3];
> +  uint opt_arg_count;
> +
> +  /*
>      Build a SEL_TREE for a simple predicate
>      @param  param       PARAM from SQL_SELECT::test_quick_select
>      @param  field       field in the predicate
> @@ -189,12 +197,12 @@ class Item_bool_func :public Item_int_func
>                                 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) {}
> +  Item_bool_func(THD *thd): Item_int_func(thd), opt_arg_count(0) {}
> +  Item_bool_func(THD *thd, Item *a): Item_int_func(thd, a), opt_arg_count(0) {}
> +  Item_bool_func(THD *thd, Item *a, Item *b): Item_int_func(thd, a, b), opt_arg_count(0) {}
> +  Item_bool_func(THD *thd, Item *a, Item *b, Item *c): Item_int_func(thd, a, b, c), opt_arg_count(0) {}
> +  Item_bool_func(THD *thd, List<Item> &list): Item_int_func(thd, list), opt_arg_count(0) { }
> +  Item_bool_func(THD *thd, Item_bool_func *item) :Item_int_func(thd, item), opt_arg_count(0) {}
>    bool is_bool_type() { return true; }
>    virtual CHARSET_INFO *compare_collation() const { return NULL; }
>    void fix_length_and_dec() { decimals=0; max_length=1; }
> @@ -436,33 +444,7 @@ class Item_bool_func2_with_rev :public Item_bool_func2
>    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);
> -  }
> +  SEL_TREE *get_mm_tree(RANGE_OPT_PARAM *param, Item **cond_ptr);
>  };
>  
>  
> @@ -504,6 +486,10 @@ class Item_bool_rowready_func2 :public Item_bool_func2_with_rev
>      Item_bool_func2::cleanup();
>      cmp.cleanup();
>    }
> +  bool add_extra_key_fields(THD *thd,
> +                            JOIN *join, KEY_FIELD **key_fields,
> +                            uint *and_level, table_map usable_tables,
> +                            SARGABLE_PARAM **sargables);
>    void add_key_fields(JOIN *join, KEY_FIELD **key_fields,
>                        uint *and_level, table_map usable_tables,
>                        SARGABLE_PARAM **sargables)
> diff --git a/sql/item_func.h b/sql/item_func.h
> index ca7c481..1f802db 100644
> --- a/sql/item_func.h
> +++ b/sql/item_func.h
> @@ -358,6 +358,11 @@ class Item_func :public Item_func_or_sum
>      - or replaced to an Item_int_with_ref
>    */
>    bool setup_args_and_comparator(THD *thd, Arg_comparator *cmp);
> +  virtual bool create_reverse_func(enum Functype cmp_type,
> +                                   THD *thd, Item *r_arg, uint *a_cnt, Item** a)
> +  {
> +    return false;
> +  }
>  };
>  
>  
> diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc
> index 41dc967..3124444 100644
> --- a/sql/item_timefunc.cc
> +++ b/sql/item_timefunc.cc
> @@ -2569,6 +2569,39 @@ bool Item_date_typecast::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date)
>  }
>  
>  
> +bool Item_date_typecast::create_reverse_func(enum Functype cmp_type,
> +                                 THD *thd, Item *r_arg, uint *a_cnt, Item** a)
> +{
> +  switch (cmp_type)
> +  {
> +  case GT_FUNC:
> +  case LE_FUNC:
> +    (*a_cnt)++;
> +    if (!(a[0]= new (thd->mem_root) Item_func_day_end(thd, r_arg)) ||
> +        a[0]->fix_fields(thd, a+1))
> +      return true;
> +    break;
> +  case LT_FUNC:
> +  case GE_FUNC:
> +    (*a_cnt)++;
> +    if (!(a[0]= new (thd->mem_root) Item_func_day_begin(thd, r_arg)) ||
> +        a[0]->fix_fields(thd, a+1))
> +      return true;
> +    break;
> +  case EQ_FUNC:
> +    (*a_cnt)+= 2;
> +    if (!(a[0]= new (thd->mem_root) Item_func_day_begin(thd, r_arg)) ||
> +        a[0]->fix_fields(thd, a+1))
> +      return true;
> +    if (!(a[1]= new (thd->mem_root) Item_func_day_end(thd, r_arg)) ||
> +        a[1]->fix_fields(thd, a+2))
> +      return true;
> +  default:;
> +  }
> +  return false;
> +}
> +
> +
>  bool Item_datetime_typecast::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date)
>  {
>    fuzzy_date |= sql_mode_for_dates(current_thd);
> @@ -3240,3 +3273,57 @@ bool Item_func_last_day::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date)
>    ltime->time_type= MYSQL_TIMESTAMP_DATE;
>    return (null_value= 0);
>  }
> +
> +
> +bool Item_func_day_begin::get_date(MYSQL_TIME *res, ulonglong fuzzy_date)
> +{
> +  if (get_arg0_date(res, fuzzy_date))
> +    return (null_value=1);
> +
> +  res->second_part= res->second= res->minute= res->hour= 0;
> +  res->time_type= MYSQL_TIMESTAMP_DATETIME;
> +
> +  return null_value= 0;
> +}
> +
> +
> +bool Item_func_day_end::get_date(MYSQL_TIME *res, ulonglong fuzzy_date)
> +{
> +  if (get_arg0_date(res, fuzzy_date))
> +    return (null_value=1);
> +
> +  res->hour= 23;
> +  res->second= res->minute= 59;
> +  res->second_part= 999999;
> +  res->time_type= MYSQL_TIMESTAMP_DATETIME;
> +  return null_value= 0;
> +}
> +
> +
> +bool Item_func_year_begin::get_date(MYSQL_TIME *res, ulonglong fuzzy_date)
> +{
> +  res->year= args[0]->val_int();
> +  if ((null_value= args[0]->null_value || res->year >= 9999))
> +    return 0;
> +
> +  res->day= res->month= 1;
> +  res->second_part= res->second= res->minute= res->hour= 0;
> +  res->time_type= MYSQL_TIMESTAMP_DATETIME;
> +  return null_value= 0;
> +}
> +
> +
> +bool Item_func_year_end::get_date(MYSQL_TIME *res, ulonglong fuzzy_date)
> +{
> +  res->year= args[0]->val_int();
> +  if ((null_value= args[0]->null_value || res->year >= 9999))
> +    return 0;
> +
> +  res->month= 12;
> +  res->day= 31;
> +  res->hour= 23;
> +  res->second= res->minute= 59;
> +  res->second_part= 999999;
> +  res->time_type= MYSQL_TIMESTAMP_DATETIME;
> +  return null_value= 0;
> +}
> diff --git a/sql/item_timefunc.h b/sql/item_timefunc.h
> index a853c63..b4f64ef 100644
> --- a/sql/item_timefunc.h
> +++ b/sql/item_timefunc.h
> @@ -745,7 +745,7 @@ class Item_func_now_local :public Item_func_now
>  {
>  public:
>    Item_func_now_local(THD *thd, uint dec): Item_func_now(thd, dec) {}
> -  const char *func_name() const { return "now"; }
> +  const char *func_name() const { return "day_start"; }
>    virtual void store_now_in_TIME(THD *thd, MYSQL_TIME *now_time);
>    virtual enum Functype functype() const { return NOW_FUNC; }
>    Item *get_copy(THD *thd, MEM_ROOT *mem_root)
> @@ -1074,6 +1074,8 @@ class Item_date_typecast :public Item_temporal_typecast
>    bool get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date);
>    const char *cast_type() const { return "date"; }
>    enum_field_types field_type() const { return MYSQL_TYPE_DATE; }
> +  bool create_reverse_func(enum Functype cmp_type,
> +                           THD *thd, Item *r_arg, uint *a_cnt, Item** a);
>    Item *get_copy(THD *thd, MEM_ROOT *mem_root)
>    { return get_item_copy<Item_date_typecast>(thd, mem_root, this); }
>  };
> @@ -1268,4 +1270,49 @@ class Item_func_last_day :public Item_datefunc
>    { return get_item_copy<Item_func_last_day>(thd, mem_root, this); }
>  };
>  
> +
> +class Item_func_day_begin :public Item_datetimefunc
> +{
> +public:
> +  Item_func_day_begin(THD *thd, Item *a): Item_datetimefunc(thd, a) {}
> +  const char *func_name() const { return "day_begin"; }
> +  bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date);
> +  Item *get_copy(THD *thd, MEM_ROOT *mem_root)
> +  { return get_item_copy<Item_func_day_begin>(thd, mem_root, this); }
> +};
> +
> +
> +class Item_func_day_end :public Item_datetimefunc
> +{
> +public:
> +  Item_func_day_end(THD *thd, Item *a): Item_datetimefunc(thd, a) {}
> +  const char *func_name() const { return "day_end"; }
> +  bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date);
> +  Item *get_copy(THD *thd, MEM_ROOT *mem_root)
> +  { return get_item_copy<Item_func_day_end>(thd, mem_root, this); }
> +};
> +
> +
> +class Item_func_year_begin :public Item_datetimefunc
> +{
> +public:
> +  Item_func_year_begin(THD *thd, Item *a): Item_datetimefunc(thd, a) {}
> +  const char *func_name() const { return "year_begin"; }
> +  bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date);
> +  Item *get_copy(THD *thd, MEM_ROOT *mem_root)
> +  { return get_item_copy<Item_func_year_begin>(thd, mem_root, this); }
> +};
> +
> +
> +class Item_func_year_end :public Item_datetimefunc
> +{
> +public:
> +  Item_func_year_end(THD *thd, Item *a): Item_datetimefunc(thd, a) {}
> +  const char *func_name() const { return "year_end"; }
> +  bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date);
> +  Item *get_copy(THD *thd, MEM_ROOT *mem_root)
> +  { return get_item_copy<Item_func_year_end>(thd, mem_root, this); }
> +};
> +
> +
>  #endif /* ITEM_TIMEFUNC_INCLUDED */
> diff --git a/sql/opt_range.cc b/sql/opt_range.cc
> index 3ea9f4e..e533608 100644
> --- a/sql/opt_range.cc
> +++ b/sql/opt_range.cc
> @@ -6998,6 +6998,54 @@ SEL_TREE *Item_bool_func::get_ne_mm_tree(RANGE_OPT_PARAM *param,
>  }
>  
>  
> +SEL_TREE *Item_bool_func2_with_rev::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 (opt_arg_count)
> +  {
> +    if (opt_arg_count == 2)
> +    {
> +      ftree= get_full_func_mm_tree_for_args(param, opt_args[0], opt_args[1]);
> +    }
> +    else if (opt_arg_count == 3)
> +    {
> +      Field *f= ((Item_field *) opt_args[0])->field;
> +      ftree= get_mm_parts(param, f, Item_func::GE_FUNC, opt_args[1]);
> +      if (ftree)
> +      {
> +        ftree= tree_and(param, ftree,
> +                        get_mm_parts(param, f,
> +                                     Item_func::LE_FUNC, opt_args[2]));
> +      }
> +    }
> +  }
> +  if (!ftree &&
> +      !(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);
> +};
> +
> +
>  SEL_TREE *Item_func_between::get_func_mm_tree(RANGE_OPT_PARAM *param,
>                                                Field *field, Item *value)
>  {
> diff --git a/sql/sql_select.cc b/sql/sql_select.cc
> index aa08420..51f6204 100644
> --- a/sql/sql_select.cc
> +++ b/sql/sql_select.cc
> @@ -4833,6 +4833,30 @@ is_local_field (Item *field)
>  }
>  
>  
> +static Item_field *get_local_field (Item *field)
> +{
> +  Item *ri= field->real_item();
> +  return (ri->type() == Item::FIELD_ITEM
> +     && !(field->used_tables() & OUTER_REF_TABLE_BIT)
> +    && !((Item_field *)ri)->get_depended_from()) ? (Item_field *) ri : 0;
> +}
> +
> +
> +static Item_field *field_in_sargable_func(Item *fn)
> +{
> +  fn= fn->real_item();
> +
> +  if (fn->type() == Item::FUNC_ITEM &&
> +      strcmp(((Item_func *)fn)->func_name(), "cast_as_date") == 0)
> +
> +  {
> +    Item_date_typecast *dt= (Item_date_typecast *) fn;
> +    return get_local_field(dt->arguments()[0]);
> +  }
> +  return 0;
> +}
> +
> +
>  /*
>    In this and other functions, and_level is a number that is ever-growing
>    and is different for the contents of every AND or OR clause. For example,
> @@ -5036,6 +5060,25 @@ Item_func_like::add_key_fields(JOIN *join, KEY_FIELD **key_fields,
>  }
>  
>  
> +bool Item_bool_rowready_func2::add_extra_key_fields(THD *thd,
> +                                           JOIN *join, KEY_FIELD **key_fields,
> +                                           uint *and_level,
> +                                           table_map usable_tables,
> +                                           SARGABLE_PARAM **sargables)
> +{
> +  Item_field *f;
> +  if ((f= field_in_sargable_func(args[0])) && args[1]->const_item())
> +  {
> +    opt_arg_count= 1;
> +    opt_args[0]= f;
> +    if (((Item_func *) args[0])->create_reverse_func(
> +            functype(), thd, args[1], &opt_arg_count, opt_args+1))
> +      return true;
> +  }
> +  return false;
> +}
> +
> +
>  void
>  Item_bool_func2::add_key_fields_optimize_op(JOIN *join, KEY_FIELD **key_fields,
>                                              uint *and_level,
> @@ -5043,19 +5086,28 @@ Item_bool_func2::add_key_fields_optimize_op(JOIN *join, KEY_FIELD **key_fields,
>                                              SARGABLE_PARAM **sargables,
>                                              bool equal_func)
>  {
> +  Item_field *f;
>    /* If item is of type 'field op field/constant' add it to key_fields */
> -  if (is_local_field(args[0]))
> +  if ((f= get_local_field(args[0])))
>    {
> -    add_key_equal_fields(join, key_fields, *and_level, this,
> -                         (Item_field*) args[0]->real_item(), equal_func,
> +    add_key_equal_fields(join, key_fields, *and_level, this, f, equal_func,
>                           args + 1, 1, usable_tables, sargables);
>    }
> -  if (is_local_field(args[1]))
> +  else if ((f= get_local_field(args[1])))
>    {
> -    add_key_equal_fields(join, key_fields, *and_level, this, 
> -                         (Item_field*) args[1]->real_item(), equal_func,
> +    add_key_equal_fields(join, key_fields, *and_level, this, f, equal_func,
>                           args, 1, usable_tables, sargables);
>    }
> +  if (opt_arg_count == 2)
> +  {
> +    add_key_equal_fields(join, key_fields, *and_level, this, opt_args[0],
> +        equal_func, opt_args+1, 1, usable_tables, sargables); 
> +  }
> +  else if (opt_arg_count == 3)
> +  {
> +    add_key_equal_fields(join, key_fields, *and_level, this, opt_args[0],
> +        false, opt_args+1, 2, usable_tables, sargables); 
> +  }
>  }
>  
>  
> @@ -5521,8 +5573,14 @@ update_ref_and_keys(THD *thd, DYNAMIC_ARRAY *keyuse,JOIN_TAB *join_tab,
>    if (cond)
>    {
>      KEY_FIELD *saved_field= field;
> +
> +    if (cond->add_extra_key_fields(thd, join_tab->join, &end, &and_level,
> +                                   normal_tables, sargables))
> +      DBUG_RETURN(TRUE);
> +
>      cond->add_key_fields(join_tab->join, &end, &and_level, normal_tables,
>                           sargables);
> +
>      for (; field != end ; field++)
>      {
>  
> _______________________________________________
> commits mailing list
> commits@xxxxxxxxxxx
> https://lists.askmonty.org/cgi-bin/mailman/listinfo/commits

-- 
BR
 Sergei
-- 
Sergei Petrunia, Software Developer
MariaDB Corporation | Skype: sergefp | Blog: http://s.petrunia.net/blog