Uploaded image for project: 'MariaDB Server'
  1. MariaDB Server
  2. MDEV-16874

Implement class Item_handled_func

    XMLWordPrintable

Details

    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.

      Attachments

        Issue Links

          Activity

            People

              bar Alexander Barkov
              bar Alexander Barkov
              Votes:
              0 Vote for this issue
              Watchers:
              2 Start watching this issue

              Dates

                Created:
                Updated:
                Resolved:

                Git Integration

                  Error rendering 'com.xiplink.jira.git.jira_git_plugin:git-issue-webpanel'. Please contact your Jira administrators.