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

            bar Alexander Barkov created issue -
            bar Alexander Barkov made changes -
            Field Original Value New Value
            Description These methods:
            {code:cpp}
            Item_date_add_interval::get_date()
            Item_func_add_time::get_date()
            {code}
            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.:

            {code:sql}
            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;
            {code}

            So, these syntax tricks:

            {code:cpp}
                    | 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;
                      }
            {code}
            {code:cpp}
                    | 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;
                      }
            {code}

            will disappear. 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:
            {code:cpp}
            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);
              }
            };
            {code}
            - 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:
            {noformat}
            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
            {noformat}
            - 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:
            {code:cpp}
            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);
            }
            {code}
            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).
            These methods:
            {code:cpp}
            Item_date_add_interval::get_date()
            Item_func_add_time::get_date()
            {code}
            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.:

            {code:sql}
            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;
            {code}

            So, these syntax tricks:

            {code:cpp}
                    | 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;
                      }
            {code}
            {code:cpp}
                    | 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;
                      }
            {code}

            will disappear. 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:
            {code:cpp}
            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);
              }
            };
            {code}
            - 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:
            {noformat}
            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
            {noformat}
            - 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:
            {code:cpp}
            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);
            }
            {code}
            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.
            bar Alexander Barkov made changes -
            bar Alexander Barkov made changes -
            bar Alexander Barkov made changes -
            Description These methods:
            {code:cpp}
            Item_date_add_interval::get_date()
            Item_func_add_time::get_date()
            {code}
            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.:

            {code:sql}
            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;
            {code}

            So, these syntax tricks:

            {code:cpp}
                    | 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;
                      }
            {code}
            {code:cpp}
                    | 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;
                      }
            {code}

            will disappear. 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:
            {code:cpp}
            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);
              }
            };
            {code}
            - 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:
            {noformat}
            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
            {noformat}
            - 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:
            {code:cpp}
            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);
            }
            {code}
            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.
            These methods:
            {code:cpp}
            Item_date_add_interval::get_date()
            Item_func_add_time::get_date()
            {code}
            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.:

            {code:sql}
            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;
            {code}

            So, these syntax tricks:

            {code:cpp}
                    | 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;
                      }
            {code}
            {code:cpp}
                    | 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;
                      }
            {code}

            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:
            {code:cpp}
            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);
              }
            };
            {code}
            - 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:
            {noformat}
            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
            {noformat}
            - 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:
            {code:cpp}
            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);
            }
            {code}
            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.
            bar Alexander Barkov made changes -
            issue.field.resolutiondate 2018-08-01 12:31:26.0 2018-08-01 12:31:26.247
            bar Alexander Barkov made changes -
            Fix Version/s 10.4.0 [ 23115 ]
            Fix Version/s 10.4 [ 22408 ]
            Resolution Fixed [ 1 ]
            Status Open [ 1 ] Closed [ 6 ]
            serg Sergei Golubchik made changes -
            Workflow MariaDB v3 [ 88694 ] MariaDB v4 [ 133643 ]

            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.