[MDEV-16874] Implement class Item_handled_func Created: 2018-08-01  Updated: 2018-08-01  Resolved: 2018-08-01

Status: Closed
Project: MariaDB Server
Component/s: Data types
Fix Version/s: 10.4.0

Type: Task Priority: Major
Reporter: Alexander Barkov Assignee: Alexander Barkov
Resolution: Fixed Votes: 0
Labels: None

Issue Links:
Blocks
blocks MDEV-4912 Data type plugin API version 1 Closed
blocks MDEV-15750 preserve MYSQL_TYPE_TIMESTAMP in temp... Stalled

 Description   

These methods:

Item_date_add_interval::get_date()
Item_func_add_time::get_date()

have tests for field_type(). This is not friendly to new data types, which might want to have these functions in addition to the currently supported data types DATETIME, DATE, TIME.

Near future candidates:

  • TIMESTAMP: Soon we'll change the temporal arithmetic to preserve the MYSQL_TYPE_TIMESTAMP type (MDEV-15750).
  • TIMESTAMP WITH TIME ZONE

Also, we'll introduce INTERVAL data types natively and it will be possible to use intervals outside of the temporal arithmetic context, e.g.:

CREATE TABLE t1 (a INTERVAL HOUR TO SECOND);
INSERT INTO t1 VALUES (INTERVAL '10:20:30' HOUR TO SECOND);
SELECT * FROM t1 WHERE COALESCE(a, INTERVAL '00:00:00' HOUR TO SECOND) = INTERVAL '10:00:00' HOUR TO SECOND;

So, these syntax tricks:

        | bit_expr '+' INTERVAL_SYM expr interval %prec '+'
          {
            $$= new (thd->mem_root) Item_date_add_interval(thd, $1, $4, $5, 0);
            if (unlikely($$ == NULL))
              MYSQL_YYABORT;
          }

        | DATE_ADD_INTERVAL '(' expr ',' INTERVAL_SYM expr interval ')'
          {
            $$= new (thd->mem_root) Item_date_add_interval(thd, $3, $6, $7, 0);
            if (unlikely($$ == NULL))
              MYSQL_YYABORT;
          }

will disappear soon. The syntax for interval expressions will migrate to the rule simple_expr. Expressions starting with the INTERVAL keyword will become normal expressions with they own Type_handler.

To make it possible for new data types to add their own implementations for ADDTIME()/SUBTIME(), DATE_ADD(), DATE_SUB(), as well as for the operators + and -, we'll do the following:

  • Introduce a new class Item_handled_func, with approximately this prototype:

    class Item_handled_func: public Item_func
    {
    public:
      class Handler
      {
      public:
        virtual ~Handler() { }
        virtual String *val_str(Item_handled_func *, String *) const= 0;
        virtual String *val_str_ascii(Item_handled_func *, String *) const= 0;
        virtual double val_real(Item_handled_func *) const= 0;
        virtual longlong val_int(Item_handled_func *) const= 0;
        virtual my_decimal *val_decimal(Item_handled_func *, my_decimal *) const= 0;
        virtual bool get_date(Item_handled_func *, MYSQL_TIME *, ulonglong fuzzydate) const= 0;
        virtual const Type_handler *return_type_handler() const= 0;
        virtual bool fix_length_and_dec(Item_handled_func *) const= 0;
      };
    protected:
      const Handler *m_func_handler;
    public:
      Item_handled_func(THD *thd, Item *a)
       :Item_func(thd, a), m_func_handler(NULL) { }
      Item_handled_func(THD *thd, Item *a, Item *b)
       :Item_func(thd, a, b), m_func_handler(NULL) { }
      void set_func_handler(const Handler *handler)
      {
        m_func_handler= handler;
      }
      const Type_handler *type_handler() const
      {
        return m_func_handler->return_type_handler();
      }
      String *val_str(String *to)
      {
        return m_func_handler->val_str(this, to);
      }
      String *val_str_ascii(String *to)
      {
        return m_func_handler->val_str_ascii(this, to);
      }
      double val_real()
      {
        return m_func_handler->val_real(this);
      }
      longlong val_int()
      {
        return m_func_handler->val_int(this);
      }
      my_decimal *val_decimal(my_decimal *to)
      {
        return m_func_handler->val_decimal(this, to);
      }
      bool get_date(MYSQL_TIME *to, ulonglong fuzzydate)
      {
        return m_func_handler->get_date(this, to, fuzzydate);
      }
    };
    

  • Change Item_func_add_time, Item_date_add_interval, Item_func_str_to_date to derive from the new class Item_handled_func.
  • Remove class Item_temporal_hybrid_func
  • Add handler classes for the former Item_temporal_hybrid_func descendants:

    Item_handled_func::Handler
      Func_handler_temporal_hybrid
        Func_handler_date_add_interval
          Func_handler_date_add_interval_datetime
          Func_handler_date_add_interval_date
          Func_handler_date_add_interval_time
          Func_handler_date_add_interval_string
        Func_handler_add_time
          Func_handler_add_time_datetime
          Func_handler_add_time_time
          Func_handler_add_time_string
        Func_handler_str_to_date_datetime
          Func_handler_str_to_date_datetime_sec
          Func_handler_str_to_date_datetime_usec
        Func_handler_str_to_date_date
        Func_handler_str_to_date_time
          Func_handler_str_to_date_time_sec
          Func_handler_str_to_date_time_usec
    

  • Move the code from Item_func_xxx::get_date() and Item_func_xxx::fix_length_and_dec() to the corresponding methods in Func_handler_xxx

After this change:

  • All tests for field_type() inside Item_func_xxx::get_date() will be gone
  • The block in Item_func_xxx::fix_length_and_dec() which decides the data type and its attributes will be simplified. For example:

    bool Item_date_add_interval::fix_length_and_dec()
    {
      ..
      arg0_field_type= args[0]->field_type();
     
      if (arg0_field_type == MYSQL_TYPE_DATETIME ||
          arg0_field_type == MYSQL_TYPE_TIMESTAMP)
      {
        set_func_handler(&func_handler_date_add_interval_datetime);
      }
      else if (arg0_field_type == MYSQL_TYPE_DATE)
      {
        if (int_type <= INTERVAL_DAY || int_type == INTERVAL_YEAR_MONTH)
          set_func_handler(&func_handler_date_add_interval_date);
        else
          set_func_handler(&func_handler_date_add_interval_datetime);
      }
      else if (arg0_field_type == MYSQL_TYPE_TIME)
      {
        if (int_type >= INTERVAL_DAY && int_type != INTERVAL_YEAR_MONTH)
          set_func_handler(&func_handler_date_add_interval_time);
        else
          set_func_handler(&func_handler_date_add_interval_datetime_arg0_time);
      }
      else
      {
        set_func_handler(&func_handler_date_add_interval_string);
      }
      maybe_null= true;
      return m_func_handler->fix_length_and_dec(this);
    }
    

    Notice, the above code does not set attributes directly anymore. Attributes are set by the call for m_func_handler->fix_length_and_dec(this).

Note, after this change, the fix_length_and_dec() part will sill have conditions testing field_type(). This is OK. This main goal of this task is to remove field_type() tests from get_date()! The remaining tests in fix_length_and_dec() will be removed in a separate change, when we add a registry of overloaded functions (in a way similar to how we added Type_aggregator for comparison functions and for CASE and its abbreviations).

Note, this task does not touch Item_func_plus and Item_func_minus. Eventually they'll also be changed to derive from Item_handled_func, in a separate task.


Generated at Thu Feb 08 08:32:12 UTC 2024 using Jira 8.20.16#820016-sha1:9d11dbea5f4be3d4cc21f03a88dd11d8c8687422.