github.com/dolthub/go-mysql-server@v0.18.0/sql/expression/function/date.go (about)

     1  // Copyright 2020-2021 Dolthub, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package function
    16  
    17  import (
    18  	"fmt"
    19  	"strings"
    20  	"time"
    21  
    22  	gmstime "github.com/dolthub/go-mysql-server/internal/time"
    23  	"github.com/dolthub/go-mysql-server/sql"
    24  	"github.com/dolthub/go-mysql-server/sql/expression"
    25  	"github.com/dolthub/go-mysql-server/sql/types"
    26  )
    27  
    28  // DateAdd adds an interval to a date.
    29  type DateAdd struct {
    30  	Date     sql.Expression
    31  	Interval *expression.Interval
    32  }
    33  
    34  var _ sql.FunctionExpression = (*DateAdd)(nil)
    35  var _ sql.CollationCoercible = (*DateAdd)(nil)
    36  
    37  // NewDateAdd creates a new date add function.
    38  func NewDateAdd(args ...sql.Expression) (sql.Expression, error) {
    39  	if len(args) != 2 {
    40  		return nil, sql.ErrInvalidArgumentNumber.New("DATE_ADD", 2, len(args))
    41  	}
    42  
    43  	i, ok := args[1].(*expression.Interval)
    44  	if !ok {
    45  		return nil, fmt.Errorf("DATE_ADD expects an interval as second parameter")
    46  	}
    47  
    48  	return &DateAdd{args[0], i}, nil
    49  }
    50  
    51  // FunctionName implements sql.FunctionExpression
    52  func (d *DateAdd) FunctionName() string {
    53  	return "date_add"
    54  }
    55  
    56  // Description implements sql.FunctionExpression
    57  func (d *DateAdd) Description() string {
    58  	return "adds the interval to the given date."
    59  }
    60  
    61  // Children implements the sql.Expression interface.
    62  func (d *DateAdd) Children() []sql.Expression {
    63  	return []sql.Expression{d.Date, d.Interval}
    64  }
    65  
    66  // Resolved implements the sql.Expression interface.
    67  func (d *DateAdd) Resolved() bool {
    68  	return d.Date.Resolved() && d.Interval.Resolved()
    69  }
    70  
    71  // IsNullable implements the sql.Expression interface.
    72  func (d *DateAdd) IsNullable() bool {
    73  	return true
    74  }
    75  
    76  // Type implements the sql.Expression interface.
    77  func (d *DateAdd) Type() sql.Type {
    78  	sqlType := dateOffsetType(d.Date, d.Interval)
    79  	return sqlType
    80  }
    81  
    82  // CollationCoercibility implements the interface sql.CollationCoercible.
    83  func (*DateAdd) CollationCoercibility(ctx *sql.Context) (collation sql.CollationID, coercibility byte) {
    84  	return ctx.GetCollation(), 4
    85  }
    86  
    87  // WithChildren implements the Expression interface.
    88  func (d *DateAdd) WithChildren(children ...sql.Expression) (sql.Expression, error) {
    89  	return NewDateAdd(children...)
    90  }
    91  
    92  // Eval implements the sql.Expression interface.
    93  func (d *DateAdd) Eval(ctx *sql.Context, row sql.Row) (interface{}, error) {
    94  	val, err := d.Date.Eval(ctx, row)
    95  	if err != nil {
    96  		return nil, err
    97  	}
    98  
    99  	if val == nil {
   100  		return nil, nil
   101  	}
   102  
   103  	delta, err := d.Interval.EvalDelta(ctx, row)
   104  	if err != nil {
   105  		return nil, err
   106  	}
   107  
   108  	if delta == nil {
   109  		return nil, nil
   110  	}
   111  
   112  	date, _, err := types.DatetimeMaxPrecision.Convert(val)
   113  	if err != nil {
   114  		ctx.Warn(1292, err.Error())
   115  		return nil, nil
   116  	}
   117  
   118  	// return appropriate type
   119  	res := types.ValidateTime(delta.Add(date.(time.Time)))
   120  	resType := d.Type()
   121  	if types.IsText(resType) {
   122  		return res, nil
   123  	}
   124  	ret, _, err := resType.Convert(res)
   125  	return ret, err
   126  }
   127  
   128  func (d *DateAdd) String() string {
   129  	return fmt.Sprintf("%s(%s,%s)", d.FunctionName(), d.Date, d.Interval)
   130  }
   131  
   132  // NewSubDate returns a new function expression, or an error if one couldn't be created. The SUBDATE
   133  // function is a synonym for DATE_SUB, with the one exception that if the second argument is NOT an
   134  // explicitly declared interval, then the value is used and the interval period is assumed to be DAY.
   135  // In either case, this function will actually return a *DateSub struct.
   136  func NewSubDate(args ...sql.Expression) (sql.Expression, error) {
   137  	if len(args) != 2 {
   138  		return nil, sql.ErrInvalidArgumentNumber.New("SUBDATE", 2, len(args))
   139  	}
   140  
   141  	// If the interval is explicitly specified, then we simply pass it all to DateSub
   142  	i, ok := args[1].(*expression.Interval)
   143  	if ok {
   144  		return &DateSub{args[0], i}, nil
   145  	}
   146  
   147  	// Otherwise, the interval period is assumed to be DAY
   148  	i = expression.NewInterval(args[1], "DAY")
   149  	return &DateSub{args[0], i}, nil
   150  }
   151  
   152  // DateSub subtracts an interval from a date.
   153  type DateSub struct {
   154  	Date     sql.Expression
   155  	Interval *expression.Interval
   156  }
   157  
   158  var _ sql.FunctionExpression = (*DateSub)(nil)
   159  var _ sql.CollationCoercible = (*DateSub)(nil)
   160  
   161  // NewDateSub creates a new date add function.
   162  func NewDateSub(args ...sql.Expression) (sql.Expression, error) {
   163  	if len(args) != 2 {
   164  		return nil, sql.ErrInvalidArgumentNumber.New("DATE_SUB", 2, len(args))
   165  	}
   166  
   167  	i, ok := args[1].(*expression.Interval)
   168  	if !ok {
   169  		return nil, fmt.Errorf("DATE_SUB expects an interval as second parameter")
   170  	}
   171  
   172  	return &DateSub{args[0], i}, nil
   173  }
   174  
   175  // FunctionName implements sql.FunctionExpression
   176  func (d *DateSub) FunctionName() string {
   177  	return "date_sub"
   178  }
   179  
   180  // Description implements sql.FunctionExpression
   181  func (d *DateSub) Description() string {
   182  	return "subtracts the interval from the given date."
   183  }
   184  
   185  // Children implements the sql.Expression interface.
   186  func (d *DateSub) Children() []sql.Expression {
   187  	return []sql.Expression{d.Date, d.Interval}
   188  }
   189  
   190  // Resolved implements the sql.Expression interface.
   191  func (d *DateSub) Resolved() bool {
   192  	return d.Date.Resolved() && d.Interval.Resolved()
   193  }
   194  
   195  // IsNullable implements the sql.Expression interface.
   196  func (d *DateSub) IsNullable() bool {
   197  	return true
   198  }
   199  
   200  // Type implements the sql.Expression interface.
   201  func (d *DateSub) Type() sql.Type {
   202  	sqlType := dateOffsetType(d.Date, d.Interval)
   203  	return sqlType
   204  }
   205  
   206  // CollationCoercibility implements the interface sql.CollationCoercible.
   207  func (*DateSub) CollationCoercibility(ctx *sql.Context) (collation sql.CollationID, coercibility byte) {
   208  	return ctx.GetCollation(), 4
   209  }
   210  
   211  // WithChildren implements the Expression interface.
   212  func (d *DateSub) WithChildren(children ...sql.Expression) (sql.Expression, error) {
   213  	return NewDateSub(children...)
   214  }
   215  
   216  // Eval implements the sql.Expression interface.
   217  func (d *DateSub) Eval(ctx *sql.Context, row sql.Row) (interface{}, error) {
   218  	date, err := d.Date.Eval(ctx, row)
   219  	if err != nil {
   220  		return nil, err
   221  	}
   222  
   223  	if date == nil {
   224  		return nil, nil
   225  	}
   226  
   227  	date, _, err = types.DatetimeMaxPrecision.Convert(date)
   228  	if err != nil {
   229  		ctx.Warn(1292, err.Error())
   230  		return nil, nil
   231  	}
   232  
   233  	delta, err := d.Interval.EvalDelta(ctx, row)
   234  	if err != nil {
   235  		return nil, err
   236  	}
   237  
   238  	if delta == nil {
   239  		return nil, nil
   240  	}
   241  
   242  	// return appropriate type
   243  	res := types.ValidateTime(delta.Sub(date.(time.Time)))
   244  	resType := d.Type()
   245  	if types.IsText(resType) {
   246  		return res, nil
   247  	}
   248  	ret, _, err := resType.Convert(res)
   249  	return ret, err
   250  }
   251  
   252  func (d *DateSub) String() string {
   253  	return fmt.Sprintf("%s(%s,%s)", d.FunctionName(), d.Date, d.Interval)
   254  }
   255  
   256  // TimestampConversion is a shorthand function for CONVERT(expr, TIMESTAMP)
   257  type TimestampConversion struct {
   258  	Date sql.Expression
   259  }
   260  
   261  var _ sql.FunctionExpression = (*TimestampConversion)(nil)
   262  var _ sql.CollationCoercible = (*TimestampConversion)(nil)
   263  
   264  // FunctionName implements sql.FunctionExpression
   265  func (t *TimestampConversion) FunctionName() string {
   266  	return "timestamp"
   267  }
   268  
   269  // Description implements sql.FunctionExpression
   270  func (t *TimestampConversion) Description() string {
   271  	return "returns a timestamp value for the expression given (e.g. the string '2020-01-02')."
   272  }
   273  
   274  func (t *TimestampConversion) Resolved() bool {
   275  	return t.Date == nil || t.Date.Resolved()
   276  }
   277  
   278  func (t *TimestampConversion) String() string {
   279  	return fmt.Sprintf("%s(%s)", t.FunctionName(), t.Date)
   280  }
   281  
   282  func (t *TimestampConversion) Type() sql.Type {
   283  	return t.Date.Type()
   284  }
   285  
   286  // CollationCoercibility implements the interface sql.CollationCoercible.
   287  func (*TimestampConversion) CollationCoercibility(ctx *sql.Context) (collation sql.CollationID, coercibility byte) {
   288  	return sql.Collation_binary, 5
   289  }
   290  
   291  func (t *TimestampConversion) IsNullable() bool {
   292  	return false
   293  }
   294  
   295  func (t *TimestampConversion) Eval(ctx *sql.Context, r sql.Row) (interface{}, error) {
   296  	e, err := t.Date.Eval(ctx, r)
   297  	if err != nil {
   298  		return nil, err
   299  	}
   300  	ret, _, err := types.TimestampMaxPrecision.Convert(e)
   301  	return ret, err
   302  }
   303  
   304  func (t *TimestampConversion) Children() []sql.Expression {
   305  	if t.Date == nil {
   306  		return nil
   307  	}
   308  	return []sql.Expression{t.Date}
   309  }
   310  
   311  func (t *TimestampConversion) WithChildren(children ...sql.Expression) (sql.Expression, error) {
   312  	return NewTimestamp(children...)
   313  }
   314  
   315  func NewTimestamp(args ...sql.Expression) (sql.Expression, error) {
   316  	if len(args) != 1 {
   317  		return nil, sql.ErrInvalidArgumentNumber.New("TIMESTAMP", 1, len(args))
   318  	}
   319  	return &TimestampConversion{args[0]}, nil
   320  }
   321  
   322  // DatetimeConversion is a shorthand function for CONVERT(expr, DATETIME)
   323  type DatetimeConversion struct {
   324  	Date sql.Expression
   325  }
   326  
   327  var _ sql.FunctionExpression = (*DatetimeConversion)(nil)
   328  var _ sql.CollationCoercible = (*DatetimeConversion)(nil)
   329  
   330  // FunctionName implements sql.FunctionExpression
   331  func (t *DatetimeConversion) FunctionName() string {
   332  	return "datetime"
   333  }
   334  
   335  // Description implements sql.FunctionExpression
   336  func (t *DatetimeConversion) Description() string {
   337  	return "returns a DATETIME value for the expression given (e.g. the string '2020-01-02')."
   338  }
   339  
   340  func (t *DatetimeConversion) Resolved() bool {
   341  	return t.Date == nil || t.Date.Resolved()
   342  }
   343  
   344  func (t *DatetimeConversion) String() string {
   345  	return fmt.Sprintf("%s(%s)", t.FunctionName(), t.Date)
   346  }
   347  
   348  func (t *DatetimeConversion) Type() sql.Type {
   349  	return t.Date.Type()
   350  }
   351  
   352  // CollationCoercibility implements the interface sql.CollationCoercible.
   353  func (*DatetimeConversion) CollationCoercibility(ctx *sql.Context) (collation sql.CollationID, coercibility byte) {
   354  	return sql.Collation_binary, 5
   355  }
   356  
   357  func (t *DatetimeConversion) IsNullable() bool {
   358  	return false
   359  }
   360  
   361  func (t *DatetimeConversion) Eval(ctx *sql.Context, r sql.Row) (interface{}, error) {
   362  	e, err := t.Date.Eval(ctx, r)
   363  	if err != nil {
   364  		return nil, err
   365  	}
   366  	ret, _, err := types.DatetimeMaxPrecision.Convert(e)
   367  	return ret, err
   368  }
   369  
   370  func (t *DatetimeConversion) Children() []sql.Expression {
   371  	if t.Date == nil {
   372  		return nil
   373  	}
   374  	return []sql.Expression{t.Date}
   375  }
   376  
   377  func (t *DatetimeConversion) WithChildren(children ...sql.Expression) (sql.Expression, error) {
   378  	return NewDatetime(children...)
   379  }
   380  
   381  // NewDatetime returns a DatetimeConversion instance to handle the sql function "datetime". This is
   382  // not a standard mysql function, but provides a shorthand for datetime conversions.
   383  func NewDatetime(args ...sql.Expression) (sql.Expression, error) {
   384  	if len(args) != 1 {
   385  		return nil, sql.ErrInvalidArgumentNumber.New("DATETIME", 1, len(args))
   386  	}
   387  
   388  	return &DatetimeConversion{args[0]}, nil
   389  }
   390  
   391  // UnixTimestamp converts the argument to the number of seconds since 1970-01-01 00:00:00 UTC.
   392  // With no argument, returns number of seconds since unix epoch for the current time.
   393  type UnixTimestamp struct {
   394  	Date sql.Expression
   395  }
   396  
   397  var _ sql.FunctionExpression = (*UnixTimestamp)(nil)
   398  var _ sql.CollationCoercible = (*UnixTimestamp)(nil)
   399  
   400  func NewUnixTimestamp(args ...sql.Expression) (sql.Expression, error) {
   401  	if len(args) > 1 {
   402  		return nil, sql.ErrInvalidArgumentNumber.New("UNIX_TIMESTAMP", 1, len(args))
   403  	}
   404  	if len(args) == 0 {
   405  		return &UnixTimestamp{nil}, nil
   406  	}
   407  	return &UnixTimestamp{args[0]}, nil
   408  }
   409  
   410  // FunctionName implements sql.FunctionExpression
   411  func (ut *UnixTimestamp) FunctionName() string {
   412  	return "unix_timestamp"
   413  }
   414  
   415  // Description implements sql.FunctionExpression
   416  func (ut *UnixTimestamp) Description() string {
   417  	return "returns the datetime argument to the number of seconds since the Unix epoch. With no argument, returns the number of seconds since the Unix epoch for the current time."
   418  }
   419  
   420  func (ut *UnixTimestamp) Children() []sql.Expression {
   421  	if ut.Date != nil {
   422  		return []sql.Expression{ut.Date}
   423  	}
   424  	return nil
   425  }
   426  
   427  func (ut *UnixTimestamp) Resolved() bool {
   428  	if ut.Date != nil {
   429  		return ut.Date.Resolved()
   430  	}
   431  	return true
   432  }
   433  
   434  func (ut *UnixTimestamp) IsNullable() bool {
   435  	return true
   436  }
   437  
   438  func (ut *UnixTimestamp) Type() sql.Type {
   439  	return types.Float64
   440  }
   441  
   442  // CollationCoercibility implements the interface sql.CollationCoercible.
   443  func (*UnixTimestamp) CollationCoercibility(ctx *sql.Context) (collation sql.CollationID, coercibility byte) {
   444  	return sql.Collation_binary, 5
   445  }
   446  
   447  func (ut *UnixTimestamp) WithChildren(children ...sql.Expression) (sql.Expression, error) {
   448  	return NewUnixTimestamp(children...)
   449  }
   450  
   451  func (ut *UnixTimestamp) Eval(ctx *sql.Context, row sql.Row) (interface{}, error) {
   452  	if ut.Date == nil {
   453  		return toUnixTimestamp(ctx.QueryTime())
   454  	}
   455  
   456  	date, err := ut.Date.Eval(ctx, row)
   457  	if err != nil {
   458  		return nil, err
   459  	}
   460  	if date == nil {
   461  		return nil, nil
   462  	}
   463  
   464  	date, _, err = types.DatetimeMaxPrecision.Convert(date)
   465  	if err != nil {
   466  		// If we aren't able to convert the value to a date, return 0 and set
   467  		// a warning to match MySQL's behavior
   468  		ctx.Warn(1292, "Incorrect datetime value: %s", ut.Date.String())
   469  		return 0, nil
   470  	}
   471  
   472  	// The function above returns the time value in UTC time zone.
   473  	// Instead, it should use the current session time zone.
   474  
   475  	// For example, if the current session TZ is set to +07:00 and given value is '2023-09-25 07:02:57',
   476  	// then the correct time value is '2023-09-25 07:02:57 +07:00'.
   477  	// Currently, we get '2023-09-25 07:02:57 +00:00' from the above function.
   478  	// ConvertTimeZone function is used to get the value in +07:00 TZ
   479  	// It will return the correct value of '2023-09-25 00:02:57 +00:00',
   480  	// which is equivalent of '2023-09-25 07:02:57 +07:00'.
   481  	stz, err := SessionTimeZone(ctx)
   482  	if err != nil {
   483  		return nil, err
   484  	}
   485  
   486  	ctz, ok := gmstime.ConvertTimeZone(date.(time.Time), stz, "UTC")
   487  	if ok {
   488  		date = ctz
   489  	}
   490  
   491  	return toUnixTimestamp(date.(time.Time))
   492  }
   493  
   494  func toUnixTimestamp(t time.Time) (interface{}, error) {
   495  	ret, _, err := types.Float64.Convert(float64(t.Unix()) + float64(t.Nanosecond())/float64(1000000000))
   496  	return ret, err
   497  }
   498  
   499  func (ut *UnixTimestamp) String() string {
   500  	if ut.Date != nil {
   501  		return fmt.Sprintf("%s(%s)", ut.FunctionName(), ut.Date)
   502  	} else {
   503  		return fmt.Sprintf("%s()", ut.FunctionName())
   504  	}
   505  }
   506  
   507  // FromUnixtime converts the argument to a datetime.
   508  type FromUnixtime struct {
   509  	*UnaryFunc
   510  }
   511  
   512  var _ sql.FunctionExpression = (*FromUnixtime)(nil)
   513  var _ sql.CollationCoercible = (*FromUnixtime)(nil)
   514  
   515  func NewFromUnixtime(arg sql.Expression) sql.Expression {
   516  	return &FromUnixtime{NewUnaryFunc(arg, "FROM_UNIXTIME", types.DatetimeMaxPrecision)}
   517  }
   518  
   519  // Description implements sql.FunctionExpression
   520  func (r *FromUnixtime) Description() string {
   521  	return "formats Unix timestamp as a date."
   522  }
   523  
   524  // CollationCoercibility implements the interface sql.CollationCoercible.
   525  func (*FromUnixtime) CollationCoercibility(ctx *sql.Context) (collation sql.CollationID, coercibility byte) {
   526  	return sql.Collation_binary, 5
   527  }
   528  
   529  func (r *FromUnixtime) Eval(ctx *sql.Context, row sql.Row) (interface{}, error) {
   530  	val, err := r.EvalChild(ctx, row)
   531  	if err != nil {
   532  		return nil, err
   533  	}
   534  
   535  	if val == nil {
   536  		return nil, nil
   537  	}
   538  
   539  	n, _, err := types.Int64.Convert(val)
   540  	if err != nil {
   541  		return nil, err
   542  	}
   543  
   544  	return time.Unix(n.(int64), 0), nil
   545  }
   546  
   547  func (r *FromUnixtime) WithChildren(children ...sql.Expression) (sql.Expression, error) {
   548  	if len(children) != 1 {
   549  		return nil, sql.ErrInvalidChildrenNumber.New(r, len(children), 1)
   550  	}
   551  	return NewFromUnixtime(children[0]), nil
   552  }
   553  
   554  type CurrDate struct {
   555  	NoArgFunc
   556  }
   557  
   558  func (c CurrDate) IsNonDeterministic() bool {
   559  	return true
   560  }
   561  
   562  var _ sql.FunctionExpression = CurrDate{}
   563  var _ sql.CollationCoercible = CurrDate{}
   564  
   565  // Description implements sql.FunctionExpression
   566  func (c CurrDate) Description() string {
   567  	return "returns the current date."
   568  }
   569  
   570  func NewCurrDate() sql.Expression {
   571  	return CurrDate{
   572  		NoArgFunc: NoArgFunc{"curdate", types.LongText},
   573  	}
   574  }
   575  
   576  func NewCurrentDate() sql.Expression {
   577  	return CurrDate{
   578  		NoArgFunc: NoArgFunc{"current_date", types.LongText},
   579  	}
   580  }
   581  
   582  func currDateLogic(ctx *sql.Context, _ sql.Row) (interface{}, error) {
   583  	t := ctx.QueryTime()
   584  	return fmt.Sprintf("%d-%02d-%02d", t.Year(), t.Month(), t.Day()), nil
   585  }
   586  
   587  // Eval implements sql.Expression
   588  func (c CurrDate) Eval(ctx *sql.Context, row sql.Row) (interface{}, error) {
   589  	return currDateLogic(ctx, row)
   590  }
   591  
   592  // CollationCoercibility implements the interface sql.CollationCoercible.
   593  func (CurrDate) CollationCoercibility(ctx *sql.Context) (collation sql.CollationID, coercibility byte) {
   594  	return sql.Collation_binary, 5
   595  }
   596  
   597  // WithChildren implements sql.Expression
   598  func (c CurrDate) WithChildren(children ...sql.Expression) (sql.Expression, error) {
   599  	return NoArgFuncWithChildren(c, children)
   600  }
   601  
   602  // Determines the return type of a DateAdd/DateSub expression
   603  // Logic is based on https://dev.mysql.com/doc/refman/8.0/en/date-and-time-functions.html#function_date-add
   604  func dateOffsetType(input sql.Expression, interval *expression.Interval) sql.Type {
   605  	if input == nil {
   606  		return types.Null
   607  	}
   608  	inputType := input.Type()
   609  
   610  	// result is null if expression is null
   611  	if inputType == types.Null {
   612  		return types.Null
   613  	}
   614  
   615  	// set type flags
   616  	isInputDate := inputType == types.Date
   617  	isInputTime := inputType == types.Time
   618  	isInputDatetime := types.IsDatetimeType(inputType) || types.IsTimestampType(inputType)
   619  
   620  	// result is Datetime if expression is Datetime or Timestamp
   621  	if isInputDatetime {
   622  		return types.DatetimeMaxPrecision
   623  	}
   624  
   625  	// determine what kind of interval we're dealing with
   626  	isYmdInterval := strings.Contains(interval.Unit, "YEAR") ||
   627  		strings.Contains(interval.Unit, "QUARTER") ||
   628  		strings.Contains(interval.Unit, "MONTH") ||
   629  		strings.Contains(interval.Unit, "WEEK") ||
   630  		strings.Contains(interval.Unit, "DAY")
   631  
   632  	isHmsInterval := strings.Contains(interval.Unit, "HOUR") ||
   633  		strings.Contains(interval.Unit, "MINUTE") ||
   634  		strings.Contains(interval.Unit, "SECOND")
   635  	isMixedInterval := isYmdInterval && isHmsInterval
   636  
   637  	// handle input of Date type
   638  	if isInputDate {
   639  		if isHmsInterval || isMixedInterval {
   640  			// if interval contains time components, result is Datetime
   641  			return types.DatetimeMaxPrecision
   642  		} else {
   643  			// otherwise result is Date
   644  			return types.Date
   645  		}
   646  	}
   647  
   648  	// handle input of Time type
   649  	if isInputTime {
   650  		if isYmdInterval || isMixedInterval {
   651  			// if interval contains date components, result is Datetime
   652  			return types.DatetimeMaxPrecision
   653  		} else {
   654  			// otherwise result is Time
   655  			return types.Time
   656  		}
   657  	}
   658  
   659  	// handle dynamic input type
   660  	if types.IsDeferredType(inputType) {
   661  		if isYmdInterval && !isHmsInterval {
   662  			// if interval contains only date components, result is Date
   663  			return types.Date
   664  		} else {
   665  			// otherwise result is Datetime
   666  			return types.DatetimeMaxPrecision
   667  		}
   668  	}
   669  
   670  	// default type is VARCHAR
   671  	return types.Text
   672  }