You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
7013 lines
209 KiB
7013 lines
209 KiB
// Copyright 2013 The ql Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSES/QL-LICENSE file.
|
|
|
|
// Copyright 2015 PingCAP, Inc.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package expression
|
|
|
|
import (
|
|
"fmt"
|
|
"math"
|
|
"regexp"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/cznic/mathutil"
|
|
"github.com/pingcap/errors"
|
|
"github.com/pingcap/parser/mysql"
|
|
"github.com/pingcap/parser/terror"
|
|
"github.com/pingcap/tidb/sessionctx"
|
|
"github.com/pingcap/tidb/sessionctx/stmtctx"
|
|
"github.com/pingcap/tidb/sessionctx/variable"
|
|
"github.com/pingcap/tidb/store/tikv/oracle"
|
|
"github.com/pingcap/tidb/types"
|
|
"github.com/pingcap/tidb/util/chunk"
|
|
"github.com/pingcap/tidb/util/logutil"
|
|
"github.com/pingcap/tipb/go-tipb"
|
|
"go.uber.org/zap"
|
|
)
|
|
|
|
const ( // GET_FORMAT first argument.
|
|
dateFormat = "DATE"
|
|
datetimeFormat = "DATETIME"
|
|
timestampFormat = "TIMESTAMP"
|
|
timeFormat = "TIME"
|
|
)
|
|
|
|
const ( // GET_FORMAT location.
|
|
usaLocation = "USA"
|
|
jisLocation = "JIS"
|
|
isoLocation = "ISO"
|
|
eurLocation = "EUR"
|
|
internalLocation = "INTERNAL"
|
|
)
|
|
|
|
var (
|
|
// durationPattern checks whether a string matchs the format of duration.
|
|
durationPattern = regexp.MustCompile(`^\s*[-]?(((\d{1,2}\s+)?0*\d{0,3}(:0*\d{1,2}){0,2})|(\d{1,7}))?(\.\d*)?\s*$`)
|
|
|
|
// timestampPattern checks whether a string matchs the format of timestamp.
|
|
timestampPattern = regexp.MustCompile(`^\s*0*\d{1,4}([^\d]0*\d{1,2}){2}\s+(0*\d{0,2}([^\d]0*\d{1,2}){2})?(\.\d*)?\s*$`)
|
|
|
|
// datePattern determine whether to match the format of date.
|
|
datePattern = regexp.MustCompile(`^\s*((0*\d{1,4}([^\d]0*\d{1,2}){2})|(\d{2,4}(\d{2}){2}))\s*$`)
|
|
)
|
|
|
|
var (
|
|
_ functionClass = &dateFunctionClass{}
|
|
_ functionClass = &dateLiteralFunctionClass{}
|
|
_ functionClass = &dateDiffFunctionClass{}
|
|
_ functionClass = &timeDiffFunctionClass{}
|
|
_ functionClass = &dateFormatFunctionClass{}
|
|
_ functionClass = &hourFunctionClass{}
|
|
_ functionClass = &minuteFunctionClass{}
|
|
_ functionClass = &secondFunctionClass{}
|
|
_ functionClass = µSecondFunctionClass{}
|
|
_ functionClass = &monthFunctionClass{}
|
|
_ functionClass = &monthNameFunctionClass{}
|
|
_ functionClass = &nowFunctionClass{}
|
|
_ functionClass = &dayNameFunctionClass{}
|
|
_ functionClass = &dayOfMonthFunctionClass{}
|
|
_ functionClass = &dayOfWeekFunctionClass{}
|
|
_ functionClass = &dayOfYearFunctionClass{}
|
|
_ functionClass = &weekFunctionClass{}
|
|
_ functionClass = &weekDayFunctionClass{}
|
|
_ functionClass = &weekOfYearFunctionClass{}
|
|
_ functionClass = &yearFunctionClass{}
|
|
_ functionClass = &yearWeekFunctionClass{}
|
|
_ functionClass = &fromUnixTimeFunctionClass{}
|
|
_ functionClass = &getFormatFunctionClass{}
|
|
_ functionClass = &strToDateFunctionClass{}
|
|
_ functionClass = &sysDateFunctionClass{}
|
|
_ functionClass = ¤tDateFunctionClass{}
|
|
_ functionClass = ¤tTimeFunctionClass{}
|
|
_ functionClass = &timeFunctionClass{}
|
|
_ functionClass = &timeLiteralFunctionClass{}
|
|
_ functionClass = &utcDateFunctionClass{}
|
|
_ functionClass = &utcTimestampFunctionClass{}
|
|
_ functionClass = &extractFunctionClass{}
|
|
_ functionClass = &unixTimestampFunctionClass{}
|
|
_ functionClass = &addTimeFunctionClass{}
|
|
_ functionClass = &convertTzFunctionClass{}
|
|
_ functionClass = &makeDateFunctionClass{}
|
|
_ functionClass = &makeTimeFunctionClass{}
|
|
_ functionClass = &periodAddFunctionClass{}
|
|
_ functionClass = &periodDiffFunctionClass{}
|
|
_ functionClass = &quarterFunctionClass{}
|
|
_ functionClass = &secToTimeFunctionClass{}
|
|
_ functionClass = &subTimeFunctionClass{}
|
|
_ functionClass = &timeFormatFunctionClass{}
|
|
_ functionClass = &timeToSecFunctionClass{}
|
|
_ functionClass = ×tampAddFunctionClass{}
|
|
_ functionClass = &toDaysFunctionClass{}
|
|
_ functionClass = &toSecondsFunctionClass{}
|
|
_ functionClass = &utcTimeFunctionClass{}
|
|
_ functionClass = ×tampFunctionClass{}
|
|
_ functionClass = ×tampLiteralFunctionClass{}
|
|
_ functionClass = &lastDayFunctionClass{}
|
|
_ functionClass = &addDateFunctionClass{}
|
|
_ functionClass = &subDateFunctionClass{}
|
|
)
|
|
|
|
var (
|
|
_ builtinFuncNew = &builtinUnixTimestampIntSig{}
|
|
)
|
|
|
|
var (
|
|
_ builtinFunc = &builtinDateSig{}
|
|
_ builtinFunc = &builtinDateLiteralSig{}
|
|
_ builtinFunc = &builtinDateDiffSig{}
|
|
_ builtinFunc = &builtinNullTimeDiffSig{}
|
|
_ builtinFunc = &builtinTimeStringTimeDiffSig{}
|
|
_ builtinFunc = &builtinDurationStringTimeDiffSig{}
|
|
_ builtinFunc = &builtinDurationDurationTimeDiffSig{}
|
|
_ builtinFunc = &builtinStringTimeTimeDiffSig{}
|
|
_ builtinFunc = &builtinStringDurationTimeDiffSig{}
|
|
_ builtinFunc = &builtinStringStringTimeDiffSig{}
|
|
_ builtinFunc = &builtinTimeTimeTimeDiffSig{}
|
|
_ builtinFunc = &builtinDateFormatSig{}
|
|
_ builtinFunc = &builtinHourSig{}
|
|
_ builtinFunc = &builtinMinuteSig{}
|
|
_ builtinFunc = &builtinSecondSig{}
|
|
_ builtinFunc = &builtinMicroSecondSig{}
|
|
_ builtinFunc = &builtinMonthSig{}
|
|
_ builtinFunc = &builtinMonthNameSig{}
|
|
_ builtinFunc = &builtinNowWithArgSig{}
|
|
_ builtinFunc = &builtinNowWithoutArgSig{}
|
|
_ builtinFunc = &builtinDayNameSig{}
|
|
_ builtinFunc = &builtinDayOfMonthSig{}
|
|
_ builtinFunc = &builtinDayOfWeekSig{}
|
|
_ builtinFunc = &builtinDayOfYearSig{}
|
|
_ builtinFunc = &builtinWeekWithModeSig{}
|
|
_ builtinFunc = &builtinWeekWithoutModeSig{}
|
|
_ builtinFunc = &builtinWeekDaySig{}
|
|
_ builtinFunc = &builtinWeekOfYearSig{}
|
|
_ builtinFunc = &builtinYearSig{}
|
|
_ builtinFunc = &builtinYearWeekWithModeSig{}
|
|
_ builtinFunc = &builtinYearWeekWithoutModeSig{}
|
|
_ builtinFunc = &builtinGetFormatSig{}
|
|
_ builtinFunc = &builtinSysDateWithFspSig{}
|
|
_ builtinFunc = &builtinSysDateWithoutFspSig{}
|
|
_ builtinFunc = &builtinCurrentDateSig{}
|
|
_ builtinFunc = &builtinCurrentTime0ArgSig{}
|
|
_ builtinFunc = &builtinCurrentTime1ArgSig{}
|
|
_ builtinFunc = &builtinTimeSig{}
|
|
_ builtinFunc = &builtinTimeLiteralSig{}
|
|
_ builtinFunc = &builtinUTCDateSig{}
|
|
_ builtinFunc = &builtinUTCTimestampWithArgSig{}
|
|
_ builtinFunc = &builtinUTCTimestampWithoutArgSig{}
|
|
_ builtinFunc = &builtinAddDatetimeAndDurationSig{}
|
|
_ builtinFunc = &builtinAddDatetimeAndStringSig{}
|
|
_ builtinFunc = &builtinAddTimeDateTimeNullSig{}
|
|
_ builtinFunc = &builtinAddStringAndDurationSig{}
|
|
_ builtinFunc = &builtinAddStringAndStringSig{}
|
|
_ builtinFunc = &builtinAddTimeStringNullSig{}
|
|
_ builtinFunc = &builtinAddDurationAndDurationSig{}
|
|
_ builtinFunc = &builtinAddDurationAndStringSig{}
|
|
_ builtinFunc = &builtinAddTimeDurationNullSig{}
|
|
_ builtinFunc = &builtinAddDateAndDurationSig{}
|
|
_ builtinFunc = &builtinAddDateAndStringSig{}
|
|
_ builtinFunc = &builtinSubDatetimeAndDurationSig{}
|
|
_ builtinFunc = &builtinSubDatetimeAndStringSig{}
|
|
_ builtinFunc = &builtinSubTimeDateTimeNullSig{}
|
|
_ builtinFunc = &builtinSubStringAndDurationSig{}
|
|
_ builtinFunc = &builtinSubStringAndStringSig{}
|
|
_ builtinFunc = &builtinSubTimeStringNullSig{}
|
|
_ builtinFunc = &builtinSubDurationAndDurationSig{}
|
|
_ builtinFunc = &builtinSubDurationAndStringSig{}
|
|
_ builtinFunc = &builtinSubTimeDurationNullSig{}
|
|
_ builtinFunc = &builtinSubDateAndDurationSig{}
|
|
_ builtinFunc = &builtinSubDateAndStringSig{}
|
|
_ builtinFunc = &builtinUnixTimestampCurrentSig{}
|
|
_ builtinFunc = &builtinUnixTimestampIntSig{}
|
|
_ builtinFunc = &builtinUnixTimestampDecSig{}
|
|
_ builtinFunc = &builtinConvertTzSig{}
|
|
_ builtinFunc = &builtinMakeDateSig{}
|
|
_ builtinFunc = &builtinMakeTimeSig{}
|
|
_ builtinFunc = &builtinPeriodAddSig{}
|
|
_ builtinFunc = &builtinPeriodDiffSig{}
|
|
_ builtinFunc = &builtinQuarterSig{}
|
|
_ builtinFunc = &builtinSecToTimeSig{}
|
|
_ builtinFunc = &builtinTimeToSecSig{}
|
|
_ builtinFunc = &builtinTimestampAddSig{}
|
|
_ builtinFunc = &builtinToDaysSig{}
|
|
_ builtinFunc = &builtinToSecondsSig{}
|
|
_ builtinFunc = &builtinUTCTimeWithArgSig{}
|
|
_ builtinFunc = &builtinUTCTimeWithoutArgSig{}
|
|
_ builtinFunc = &builtinTimestamp1ArgSig{}
|
|
_ builtinFunc = &builtinTimestamp2ArgsSig{}
|
|
_ builtinFunc = &builtinTimestampLiteralSig{}
|
|
_ builtinFunc = &builtinLastDaySig{}
|
|
_ builtinFunc = &builtinStrToDateDateSig{}
|
|
_ builtinFunc = &builtinStrToDateDatetimeSig{}
|
|
_ builtinFunc = &builtinStrToDateDurationSig{}
|
|
_ builtinFunc = &builtinFromUnixTime1ArgSig{}
|
|
_ builtinFunc = &builtinFromUnixTime2ArgSig{}
|
|
_ builtinFunc = &builtinExtractDatetimeSig{}
|
|
_ builtinFunc = &builtinExtractDurationSig{}
|
|
_ builtinFunc = &builtinAddDateStringStringSig{}
|
|
_ builtinFunc = &builtinAddDateStringIntSig{}
|
|
_ builtinFunc = &builtinAddDateStringRealSig{}
|
|
_ builtinFunc = &builtinAddDateStringDecimalSig{}
|
|
_ builtinFunc = &builtinAddDateIntStringSig{}
|
|
_ builtinFunc = &builtinAddDateIntIntSig{}
|
|
_ builtinFunc = &builtinAddDateIntRealSig{}
|
|
_ builtinFunc = &builtinAddDateIntDecimalSig{}
|
|
_ builtinFunc = &builtinAddDateDatetimeStringSig{}
|
|
_ builtinFunc = &builtinAddDateDatetimeIntSig{}
|
|
_ builtinFunc = &builtinAddDateDatetimeRealSig{}
|
|
_ builtinFunc = &builtinAddDateDatetimeDecimalSig{}
|
|
_ builtinFunc = &builtinSubDateStringStringSig{}
|
|
_ builtinFunc = &builtinSubDateStringIntSig{}
|
|
_ builtinFunc = &builtinSubDateStringRealSig{}
|
|
_ builtinFunc = &builtinSubDateStringDecimalSig{}
|
|
_ builtinFunc = &builtinSubDateIntStringSig{}
|
|
_ builtinFunc = &builtinSubDateIntIntSig{}
|
|
_ builtinFunc = &builtinSubDateIntRealSig{}
|
|
_ builtinFunc = &builtinSubDateIntDecimalSig{}
|
|
_ builtinFunc = &builtinSubDateDatetimeStringSig{}
|
|
_ builtinFunc = &builtinSubDateDatetimeIntSig{}
|
|
_ builtinFunc = &builtinSubDateDatetimeRealSig{}
|
|
_ builtinFunc = &builtinSubDateDatetimeDecimalSig{}
|
|
)
|
|
|
|
func convertTimeToMysqlTime(t time.Time, fsp int8, roundMode types.RoundMode) (types.Time, error) {
|
|
var tr time.Time
|
|
var err error
|
|
if roundMode == types.ModeTruncate {
|
|
tr, err = types.TruncateFrac(t, fsp)
|
|
} else {
|
|
tr, err = types.RoundFrac(t, fsp)
|
|
}
|
|
if err != nil {
|
|
return types.ZeroTime, err
|
|
}
|
|
|
|
return types.NewTime(types.FromGoTime(tr), mysql.TypeDatetime, fsp), nil
|
|
}
|
|
|
|
type dateFunctionClass struct {
|
|
baseFunctionClass
|
|
}
|
|
|
|
func (c *dateFunctionClass) getFunction(ctx sessionctx.Context, args []Expression) (builtinFunc, error) {
|
|
if err := c.verifyArgs(args); err != nil {
|
|
return nil, err
|
|
}
|
|
bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETDatetime, types.ETDatetime)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
bf.tp.Tp, bf.tp.Flen, bf.tp.Decimal = mysql.TypeDate, 10, 0
|
|
sig := &builtinDateSig{bf}
|
|
sig.setPbCode(tipb.ScalarFuncSig_Date)
|
|
return sig, nil
|
|
}
|
|
|
|
type builtinDateSig struct {
|
|
baseBuiltinFunc
|
|
}
|
|
|
|
func (b *builtinDateSig) Clone() builtinFunc {
|
|
newSig := &builtinDateSig{}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
// evalTime evals DATE(expr).
|
|
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_date
|
|
func (b *builtinDateSig) evalTime(row chunk.Row) (types.Time, bool, error) {
|
|
expr, isNull, err := b.args[0].EvalTime(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return types.ZeroTime, true, handleInvalidTimeError(b.ctx, err)
|
|
}
|
|
|
|
if expr.IsZero() {
|
|
return types.ZeroTime, true, handleInvalidTimeError(b.ctx, types.ErrWrongValue.GenWithStackByArgs(types.DateTimeStr, expr.String()))
|
|
}
|
|
|
|
expr.SetCoreTime(types.FromDate(expr.Year(), expr.Month(), expr.Day(), 0, 0, 0, 0))
|
|
expr.SetType(mysql.TypeDate)
|
|
return expr, false, nil
|
|
}
|
|
|
|
type dateLiteralFunctionClass struct {
|
|
baseFunctionClass
|
|
}
|
|
|
|
func (c *dateLiteralFunctionClass) getFunction(ctx sessionctx.Context, args []Expression) (builtinFunc, error) {
|
|
if err := c.verifyArgs(args); err != nil {
|
|
return nil, err
|
|
}
|
|
con, ok := args[0].(*Constant)
|
|
if !ok {
|
|
panic("Unexpected parameter for date literal")
|
|
}
|
|
dt, err := con.Eval(chunk.Row{})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
str := dt.GetString()
|
|
if !datePattern.MatchString(str) {
|
|
return nil, types.ErrWrongValue.GenWithStackByArgs(types.DateTimeStr, str)
|
|
}
|
|
tm, err := types.ParseDate(ctx.GetSessionVars().StmtCtx, str)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, []Expression{}, types.ETDatetime)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
bf.tp.Tp, bf.tp.Flen, bf.tp.Decimal = mysql.TypeDate, 10, 0
|
|
sig := &builtinDateLiteralSig{bf, tm}
|
|
return sig, nil
|
|
}
|
|
|
|
type builtinDateLiteralSig struct {
|
|
baseBuiltinFunc
|
|
literal types.Time
|
|
}
|
|
|
|
func (b *builtinDateLiteralSig) Clone() builtinFunc {
|
|
newSig := &builtinDateLiteralSig{literal: b.literal}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
// evalTime evals DATE 'stringLit'.
|
|
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-literals.html
|
|
func (b *builtinDateLiteralSig) evalTime(row chunk.Row) (types.Time, bool, error) {
|
|
mode := b.ctx.GetSessionVars().SQLMode
|
|
if mode.HasNoZeroDateMode() && b.literal.IsZero() {
|
|
return b.literal, true, types.ErrWrongValue.GenWithStackByArgs(types.DateTimeStr, b.literal.String())
|
|
}
|
|
if mode.HasNoZeroInDateMode() && (b.literal.InvalidZero() && !b.literal.IsZero()) {
|
|
return b.literal, true, types.ErrWrongValue.GenWithStackByArgs(types.DateTimeStr, b.literal.String())
|
|
}
|
|
return b.literal, false, nil
|
|
}
|
|
|
|
type dateDiffFunctionClass struct {
|
|
baseFunctionClass
|
|
}
|
|
|
|
func (c *dateDiffFunctionClass) getFunction(ctx sessionctx.Context, args []Expression) (builtinFunc, error) {
|
|
if err := c.verifyArgs(args); err != nil {
|
|
return nil, err
|
|
}
|
|
bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETInt, types.ETDatetime, types.ETDatetime)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
sig := &builtinDateDiffSig{bf}
|
|
sig.setPbCode(tipb.ScalarFuncSig_DateDiff)
|
|
return sig, nil
|
|
}
|
|
|
|
type builtinDateDiffSig struct {
|
|
baseBuiltinFunc
|
|
}
|
|
|
|
func (b *builtinDateDiffSig) Clone() builtinFunc {
|
|
newSig := &builtinDateDiffSig{}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
// evalInt evals a builtinDateDiffSig.
|
|
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_datediff
|
|
func (b *builtinDateDiffSig) evalInt(row chunk.Row) (int64, bool, error) {
|
|
lhs, isNull, err := b.args[0].EvalTime(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return 0, true, handleInvalidTimeError(b.ctx, err)
|
|
}
|
|
rhs, isNull, err := b.args[1].EvalTime(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return 0, true, handleInvalidTimeError(b.ctx, err)
|
|
}
|
|
if invalidLHS, invalidRHS := lhs.InvalidZero(), rhs.InvalidZero(); invalidLHS || invalidRHS {
|
|
if invalidLHS {
|
|
err = handleInvalidTimeError(b.ctx, types.ErrWrongValue.GenWithStackByArgs(types.DateTimeStr, lhs.String()))
|
|
}
|
|
if invalidRHS {
|
|
err = handleInvalidTimeError(b.ctx, types.ErrWrongValue.GenWithStackByArgs(types.DateTimeStr, rhs.String()))
|
|
}
|
|
return 0, true, err
|
|
}
|
|
return int64(types.DateDiff(lhs.CoreTime(), rhs.CoreTime())), false, nil
|
|
}
|
|
|
|
type timeDiffFunctionClass struct {
|
|
baseFunctionClass
|
|
}
|
|
|
|
func (c *timeDiffFunctionClass) getArgEvalTp(fieldTp *types.FieldType) types.EvalType {
|
|
argTp := types.ETString
|
|
switch tp := fieldTp.EvalType(); tp {
|
|
case types.ETDuration, types.ETDatetime, types.ETTimestamp:
|
|
argTp = tp
|
|
}
|
|
return argTp
|
|
}
|
|
|
|
func (c *timeDiffFunctionClass) getFunction(ctx sessionctx.Context, args []Expression) (builtinFunc, error) {
|
|
if err := c.verifyArgs(args); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
arg0FieldTp, arg1FieldTp := args[0].GetType(), args[1].GetType()
|
|
arg0Tp, arg1Tp := c.getArgEvalTp(arg0FieldTp), c.getArgEvalTp(arg1FieldTp)
|
|
bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETDuration, arg0Tp, arg1Tp)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
arg0Dec, err := getExpressionFsp(ctx, args[0])
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
arg1Dec, err := getExpressionFsp(ctx, args[1])
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
bf.tp.Decimal = mathutil.Max(arg0Dec, arg1Dec)
|
|
|
|
var sig builtinFunc
|
|
// arg0 and arg1 must be the same time type(compatible), or timediff will return NULL.
|
|
// TODO: we don't really need Duration type, actually in MySQL, it use Time class to represent
|
|
// all the time type, and use filed type to distinguish datetime, date, timestamp or time(duration).
|
|
// With the duration type, we are hard to port all the MySQL behavior.
|
|
switch arg0Tp {
|
|
case types.ETDuration:
|
|
switch arg1Tp {
|
|
case types.ETDuration:
|
|
sig = &builtinDurationDurationTimeDiffSig{bf}
|
|
sig.setPbCode(tipb.ScalarFuncSig_DurationDurationTimeDiff)
|
|
case types.ETDatetime, types.ETTimestamp:
|
|
sig = &builtinNullTimeDiffSig{bf}
|
|
sig.setPbCode(tipb.ScalarFuncSig_NullTimeDiff)
|
|
default:
|
|
sig = &builtinDurationStringTimeDiffSig{bf}
|
|
sig.setPbCode(tipb.ScalarFuncSig_DurationStringTimeDiff)
|
|
}
|
|
case types.ETDatetime, types.ETTimestamp:
|
|
switch arg1Tp {
|
|
case types.ETDuration:
|
|
sig = &builtinNullTimeDiffSig{bf}
|
|
sig.setPbCode(tipb.ScalarFuncSig_NullTimeDiff)
|
|
case types.ETDatetime, types.ETTimestamp:
|
|
sig = &builtinTimeTimeTimeDiffSig{bf}
|
|
sig.setPbCode(tipb.ScalarFuncSig_TimeTimeTimeDiff)
|
|
default:
|
|
sig = &builtinTimeStringTimeDiffSig{bf}
|
|
sig.setPbCode(tipb.ScalarFuncSig_TimeStringTimeDiff)
|
|
}
|
|
default:
|
|
switch arg1Tp {
|
|
case types.ETDuration:
|
|
sig = &builtinStringDurationTimeDiffSig{bf}
|
|
sig.setPbCode(tipb.ScalarFuncSig_StringDurationTimeDiff)
|
|
case types.ETDatetime, types.ETTimestamp:
|
|
sig = &builtinStringTimeTimeDiffSig{bf}
|
|
sig.setPbCode(tipb.ScalarFuncSig_StringTimeTimeDiff)
|
|
default:
|
|
sig = &builtinStringStringTimeDiffSig{bf}
|
|
sig.setPbCode(tipb.ScalarFuncSig_StringStringTimeDiff)
|
|
}
|
|
}
|
|
return sig, nil
|
|
}
|
|
|
|
type builtinDurationDurationTimeDiffSig struct {
|
|
baseBuiltinFunc
|
|
}
|
|
|
|
func (b *builtinDurationDurationTimeDiffSig) Clone() builtinFunc {
|
|
newSig := &builtinDurationDurationTimeDiffSig{}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
// evalDuration evals a builtinDurationDurationTimeDiffSig.
|
|
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_timediff
|
|
func (b *builtinDurationDurationTimeDiffSig) evalDuration(row chunk.Row) (d types.Duration, isNull bool, err error) {
|
|
lhs, isNull, err := b.args[0].EvalDuration(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return d, isNull, err
|
|
}
|
|
|
|
rhs, isNull, err := b.args[1].EvalDuration(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return d, isNull, err
|
|
}
|
|
|
|
d, isNull, err = calculateDurationTimeDiff(b.ctx, lhs, rhs)
|
|
return d, isNull, err
|
|
}
|
|
|
|
type builtinTimeTimeTimeDiffSig struct {
|
|
baseBuiltinFunc
|
|
}
|
|
|
|
func (b *builtinTimeTimeTimeDiffSig) Clone() builtinFunc {
|
|
newSig := &builtinTimeTimeTimeDiffSig{}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
// evalDuration evals a builtinTimeTimeTimeDiffSig.
|
|
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_timediff
|
|
func (b *builtinTimeTimeTimeDiffSig) evalDuration(row chunk.Row) (d types.Duration, isNull bool, err error) {
|
|
lhs, isNull, err := b.args[0].EvalTime(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return d, isNull, err
|
|
}
|
|
|
|
rhs, isNull, err := b.args[1].EvalTime(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return d, isNull, err
|
|
}
|
|
|
|
sc := b.ctx.GetSessionVars().StmtCtx
|
|
d, isNull, err = calculateTimeDiff(sc, lhs, rhs)
|
|
return d, isNull, err
|
|
}
|
|
|
|
type builtinDurationStringTimeDiffSig struct {
|
|
baseBuiltinFunc
|
|
}
|
|
|
|
func (b *builtinDurationStringTimeDiffSig) Clone() builtinFunc {
|
|
newSig := &builtinDurationStringTimeDiffSig{}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
// evalDuration evals a builtinDurationStringTimeDiffSig.
|
|
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_timediff
|
|
func (b *builtinDurationStringTimeDiffSig) evalDuration(row chunk.Row) (d types.Duration, isNull bool, err error) {
|
|
lhs, isNull, err := b.args[0].EvalDuration(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return d, isNull, err
|
|
}
|
|
|
|
rhsStr, isNull, err := b.args[1].EvalString(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return d, isNull, err
|
|
}
|
|
|
|
sc := b.ctx.GetSessionVars().StmtCtx
|
|
rhs, _, isDuration, err := convertStringToDuration(sc, rhsStr, int8(b.tp.Decimal))
|
|
if err != nil || !isDuration {
|
|
return d, true, err
|
|
}
|
|
|
|
d, isNull, err = calculateDurationTimeDiff(b.ctx, lhs, rhs)
|
|
return d, isNull, err
|
|
}
|
|
|
|
type builtinStringDurationTimeDiffSig struct {
|
|
baseBuiltinFunc
|
|
}
|
|
|
|
func (b *builtinStringDurationTimeDiffSig) Clone() builtinFunc {
|
|
newSig := &builtinStringDurationTimeDiffSig{}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
// evalDuration evals a builtinStringDurationTimeDiffSig.
|
|
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_timediff
|
|
func (b *builtinStringDurationTimeDiffSig) evalDuration(row chunk.Row) (d types.Duration, isNull bool, err error) {
|
|
lhsStr, isNull, err := b.args[0].EvalString(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return d, isNull, err
|
|
}
|
|
|
|
rhs, isNull, err := b.args[1].EvalDuration(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return d, isNull, err
|
|
}
|
|
|
|
sc := b.ctx.GetSessionVars().StmtCtx
|
|
lhs, _, isDuration, err := convertStringToDuration(sc, lhsStr, int8(b.tp.Decimal))
|
|
if err != nil || !isDuration {
|
|
return d, true, err
|
|
}
|
|
|
|
d, isNull, err = calculateDurationTimeDiff(b.ctx, lhs, rhs)
|
|
return d, isNull, err
|
|
}
|
|
|
|
// calculateTimeDiff calculates interval difference of two types.Time.
|
|
func calculateTimeDiff(sc *stmtctx.StatementContext, lhs, rhs types.Time) (d types.Duration, isNull bool, err error) {
|
|
d = lhs.Sub(sc, &rhs)
|
|
d.Duration, err = types.TruncateOverflowMySQLTime(d.Duration)
|
|
if types.ErrTruncatedWrongVal.Equal(err) {
|
|
err = sc.HandleTruncate(err)
|
|
}
|
|
return d, err != nil, err
|
|
}
|
|
|
|
// calculateDurationTimeDiff calculates interval difference of two types.Duration.
|
|
func calculateDurationTimeDiff(ctx sessionctx.Context, lhs, rhs types.Duration) (d types.Duration, isNull bool, err error) {
|
|
d, err = lhs.Sub(rhs)
|
|
if err != nil {
|
|
return d, true, err
|
|
}
|
|
|
|
d.Duration, err = types.TruncateOverflowMySQLTime(d.Duration)
|
|
if types.ErrTruncatedWrongVal.Equal(err) {
|
|
sc := ctx.GetSessionVars().StmtCtx
|
|
err = sc.HandleTruncate(err)
|
|
}
|
|
return d, err != nil, err
|
|
}
|
|
|
|
type builtinTimeStringTimeDiffSig struct {
|
|
baseBuiltinFunc
|
|
}
|
|
|
|
func (b *builtinTimeStringTimeDiffSig) Clone() builtinFunc {
|
|
newSig := &builtinTimeStringTimeDiffSig{}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
// evalDuration evals a builtinTimeStringTimeDiffSig.
|
|
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_timediff
|
|
func (b *builtinTimeStringTimeDiffSig) evalDuration(row chunk.Row) (d types.Duration, isNull bool, err error) {
|
|
lhs, isNull, err := b.args[0].EvalTime(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return d, isNull, err
|
|
}
|
|
|
|
rhsStr, isNull, err := b.args[1].EvalString(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return d, isNull, err
|
|
}
|
|
|
|
sc := b.ctx.GetSessionVars().StmtCtx
|
|
_, rhs, isDuration, err := convertStringToDuration(sc, rhsStr, int8(b.tp.Decimal))
|
|
if err != nil || isDuration {
|
|
return d, true, err
|
|
}
|
|
|
|
d, isNull, err = calculateTimeDiff(sc, lhs, rhs)
|
|
return d, isNull, err
|
|
}
|
|
|
|
type builtinStringTimeTimeDiffSig struct {
|
|
baseBuiltinFunc
|
|
}
|
|
|
|
func (b *builtinStringTimeTimeDiffSig) Clone() builtinFunc {
|
|
newSig := &builtinStringTimeTimeDiffSig{}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
// evalDuration evals a builtinStringTimeTimeDiffSig.
|
|
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_timediff
|
|
func (b *builtinStringTimeTimeDiffSig) evalDuration(row chunk.Row) (d types.Duration, isNull bool, err error) {
|
|
lhsStr, isNull, err := b.args[0].EvalString(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return d, isNull, err
|
|
}
|
|
|
|
rhs, isNull, err := b.args[1].EvalTime(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return d, isNull, err
|
|
}
|
|
|
|
sc := b.ctx.GetSessionVars().StmtCtx
|
|
_, lhs, isDuration, err := convertStringToDuration(sc, lhsStr, int8(b.tp.Decimal))
|
|
if err != nil || isDuration {
|
|
return d, true, err
|
|
}
|
|
|
|
d, isNull, err = calculateTimeDiff(sc, lhs, rhs)
|
|
return d, isNull, err
|
|
}
|
|
|
|
type builtinStringStringTimeDiffSig struct {
|
|
baseBuiltinFunc
|
|
}
|
|
|
|
func (b *builtinStringStringTimeDiffSig) Clone() builtinFunc {
|
|
newSig := &builtinStringStringTimeDiffSig{}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
// evalDuration evals a builtinStringStringTimeDiffSig.
|
|
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_timediff
|
|
func (b *builtinStringStringTimeDiffSig) evalDuration(row chunk.Row) (d types.Duration, isNull bool, err error) {
|
|
lhs, isNull, err := b.args[0].EvalString(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return d, isNull, err
|
|
}
|
|
|
|
rhs, isNull, err := b.args[1].EvalString(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return d, isNull, err
|
|
}
|
|
|
|
sc := b.ctx.GetSessionVars().StmtCtx
|
|
fsp := int8(b.tp.Decimal)
|
|
lhsDur, lhsTime, lhsIsDuration, err := convertStringToDuration(sc, lhs, fsp)
|
|
if err != nil {
|
|
return d, true, err
|
|
}
|
|
|
|
rhsDur, rhsTime, rhsIsDuration, err := convertStringToDuration(sc, rhs, fsp)
|
|
if err != nil {
|
|
return d, true, err
|
|
}
|
|
|
|
if lhsIsDuration != rhsIsDuration {
|
|
return d, true, nil
|
|
}
|
|
|
|
if lhsIsDuration {
|
|
d, isNull, err = calculateDurationTimeDiff(b.ctx, lhsDur, rhsDur)
|
|
} else {
|
|
d, isNull, err = calculateTimeDiff(sc, lhsTime, rhsTime)
|
|
}
|
|
|
|
return d, isNull, err
|
|
}
|
|
|
|
type builtinNullTimeDiffSig struct {
|
|
baseBuiltinFunc
|
|
}
|
|
|
|
func (b *builtinNullTimeDiffSig) Clone() builtinFunc {
|
|
newSig := &builtinNullTimeDiffSig{}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
// evalDuration evals a builtinNullTimeDiffSig.
|
|
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_timediff
|
|
func (b *builtinNullTimeDiffSig) evalDuration(row chunk.Row) (d types.Duration, isNull bool, err error) {
|
|
return d, true, nil
|
|
}
|
|
|
|
// convertStringToDuration converts string to duration, it return types.Time because in some case
|
|
// it will converts string to datetime.
|
|
func convertStringToDuration(sc *stmtctx.StatementContext, str string, fsp int8) (d types.Duration, t types.Time,
|
|
isDuration bool, err error) {
|
|
if n := strings.IndexByte(str, '.'); n >= 0 {
|
|
lenStrFsp := len(str[n+1:])
|
|
if lenStrFsp <= int(types.MaxFsp) {
|
|
fsp = mathutil.MaxInt8(int8(lenStrFsp), fsp)
|
|
}
|
|
}
|
|
return types.StrToDuration(sc, str, fsp)
|
|
}
|
|
|
|
type dateFormatFunctionClass struct {
|
|
baseFunctionClass
|
|
}
|
|
|
|
func (c *dateFormatFunctionClass) getFunction(ctx sessionctx.Context, args []Expression) (builtinFunc, error) {
|
|
if err := c.verifyArgs(args); err != nil {
|
|
return nil, err
|
|
}
|
|
bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETString, types.ETDatetime, types.ETString)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
// worst case: formatMask=%r%r%r...%r, each %r takes 11 characters
|
|
bf.tp.Flen = (args[1].GetType().Flen + 1) / 2 * 11
|
|
sig := &builtinDateFormatSig{bf}
|
|
sig.setPbCode(tipb.ScalarFuncSig_DateFormatSig)
|
|
return sig, nil
|
|
}
|
|
|
|
type builtinDateFormatSig struct {
|
|
baseBuiltinFunc
|
|
}
|
|
|
|
func (b *builtinDateFormatSig) Clone() builtinFunc {
|
|
newSig := &builtinDateFormatSig{}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
// evalString evals a builtinDateFormatSig.
|
|
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_date-format
|
|
func (b *builtinDateFormatSig) evalString(row chunk.Row) (string, bool, error) {
|
|
t, isNull, err := b.args[0].EvalTime(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return "", isNull, handleInvalidTimeError(b.ctx, err)
|
|
}
|
|
formatMask, isNull, err := b.args[1].EvalString(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return "", isNull, err
|
|
}
|
|
// MySQL compatibility, #11203
|
|
// If format mask is 0 then return 0 without warnings
|
|
if formatMask == "0" {
|
|
return "0", false, nil
|
|
}
|
|
|
|
if t.InvalidZero() {
|
|
// MySQL compatibility, #11203
|
|
// 0 | 0.0 should be converted to null without warnings
|
|
n, err := t.ToNumber().ToInt()
|
|
isOriginalIntOrDecimalZero := err == nil && n == 0
|
|
// Args like "0000-00-00", "0000-00-00 00:00:00" set Fsp to 6
|
|
isOriginalStringZero := t.Fsp() > 0
|
|
if isOriginalIntOrDecimalZero && !isOriginalStringZero {
|
|
return "", true, nil
|
|
}
|
|
return "", true, handleInvalidTimeError(b.ctx, types.ErrWrongValue.GenWithStackByArgs(types.DateTimeStr, t.String()))
|
|
}
|
|
|
|
res, err := t.DateFormat(formatMask)
|
|
return res, isNull, err
|
|
}
|
|
|
|
type fromDaysFunctionClass struct {
|
|
baseFunctionClass
|
|
}
|
|
|
|
func (c *fromDaysFunctionClass) getFunction(ctx sessionctx.Context, args []Expression) (builtinFunc, error) {
|
|
if err := c.verifyArgs(args); err != nil {
|
|
return nil, err
|
|
}
|
|
bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETDatetime, types.ETInt)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
bf.tp.Flen, bf.tp.Decimal = 10, 0
|
|
sig := &builtinFromDaysSig{bf}
|
|
sig.setPbCode(tipb.ScalarFuncSig_FromDays)
|
|
return sig, nil
|
|
}
|
|
|
|
type builtinFromDaysSig struct {
|
|
baseBuiltinFunc
|
|
}
|
|
|
|
func (b *builtinFromDaysSig) Clone() builtinFunc {
|
|
newSig := &builtinFromDaysSig{}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
// evalTime evals FROM_DAYS(N).
|
|
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_from-days
|
|
func (b *builtinFromDaysSig) evalTime(row chunk.Row) (types.Time, bool, error) {
|
|
n, isNull, err := b.args[0].EvalInt(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return types.ZeroTime, true, err
|
|
}
|
|
|
|
return types.TimeFromDays(n), false, nil
|
|
}
|
|
|
|
type hourFunctionClass struct {
|
|
baseFunctionClass
|
|
}
|
|
|
|
func (c *hourFunctionClass) getFunction(ctx sessionctx.Context, args []Expression) (builtinFunc, error) {
|
|
if err := c.verifyArgs(args); err != nil {
|
|
return nil, err
|
|
}
|
|
bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETInt, types.ETDuration)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
bf.tp.Flen, bf.tp.Decimal = 3, 0
|
|
sig := &builtinHourSig{bf}
|
|
sig.setPbCode(tipb.ScalarFuncSig_Hour)
|
|
return sig, nil
|
|
}
|
|
|
|
type builtinHourSig struct {
|
|
baseBuiltinFunc
|
|
}
|
|
|
|
func (b *builtinHourSig) Clone() builtinFunc {
|
|
newSig := &builtinHourSig{}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
// evalInt evals HOUR(time).
|
|
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_hour
|
|
func (b *builtinHourSig) evalInt(row chunk.Row) (int64, bool, error) {
|
|
dur, isNull, err := b.args[0].EvalDuration(b.ctx, row)
|
|
// ignore error and return NULL
|
|
if isNull || err != nil {
|
|
return 0, true, nil
|
|
}
|
|
return int64(dur.Hour()), false, nil
|
|
}
|
|
|
|
type minuteFunctionClass struct {
|
|
baseFunctionClass
|
|
}
|
|
|
|
func (c *minuteFunctionClass) getFunction(ctx sessionctx.Context, args []Expression) (builtinFunc, error) {
|
|
if err := c.verifyArgs(args); err != nil {
|
|
return nil, err
|
|
}
|
|
bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETInt, types.ETDuration)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
bf.tp.Flen, bf.tp.Decimal = 2, 0
|
|
sig := &builtinMinuteSig{bf}
|
|
sig.setPbCode(tipb.ScalarFuncSig_Minute)
|
|
return sig, nil
|
|
}
|
|
|
|
type builtinMinuteSig struct {
|
|
baseBuiltinFunc
|
|
}
|
|
|
|
func (b *builtinMinuteSig) Clone() builtinFunc {
|
|
newSig := &builtinMinuteSig{}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
// evalInt evals MINUTE(time).
|
|
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_minute
|
|
func (b *builtinMinuteSig) evalInt(row chunk.Row) (int64, bool, error) {
|
|
dur, isNull, err := b.args[0].EvalDuration(b.ctx, row)
|
|
// ignore error and return NULL
|
|
if isNull || err != nil {
|
|
return 0, true, nil
|
|
}
|
|
return int64(dur.Minute()), false, nil
|
|
}
|
|
|
|
type secondFunctionClass struct {
|
|
baseFunctionClass
|
|
}
|
|
|
|
func (c *secondFunctionClass) getFunction(ctx sessionctx.Context, args []Expression) (builtinFunc, error) {
|
|
if err := c.verifyArgs(args); err != nil {
|
|
return nil, err
|
|
}
|
|
bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETInt, types.ETDuration)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
bf.tp.Flen, bf.tp.Decimal = 2, 0
|
|
sig := &builtinSecondSig{bf}
|
|
sig.setPbCode(tipb.ScalarFuncSig_Second)
|
|
return sig, nil
|
|
}
|
|
|
|
type builtinSecondSig struct {
|
|
baseBuiltinFunc
|
|
}
|
|
|
|
func (b *builtinSecondSig) Clone() builtinFunc {
|
|
newSig := &builtinSecondSig{}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
// evalInt evals SECOND(time).
|
|
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_second
|
|
func (b *builtinSecondSig) evalInt(row chunk.Row) (int64, bool, error) {
|
|
dur, isNull, err := b.args[0].EvalDuration(b.ctx, row)
|
|
// ignore error and return NULL
|
|
if isNull || err != nil {
|
|
return 0, true, nil
|
|
}
|
|
return int64(dur.Second()), false, nil
|
|
}
|
|
|
|
type microSecondFunctionClass struct {
|
|
baseFunctionClass
|
|
}
|
|
|
|
func (c *microSecondFunctionClass) getFunction(ctx sessionctx.Context, args []Expression) (builtinFunc, error) {
|
|
if err := c.verifyArgs(args); err != nil {
|
|
return nil, err
|
|
}
|
|
bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETInt, types.ETDuration)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
bf.tp.Flen, bf.tp.Decimal = 6, 0
|
|
sig := &builtinMicroSecondSig{bf}
|
|
sig.setPbCode(tipb.ScalarFuncSig_MicroSecond)
|
|
return sig, nil
|
|
}
|
|
|
|
type builtinMicroSecondSig struct {
|
|
baseBuiltinFunc
|
|
}
|
|
|
|
func (b *builtinMicroSecondSig) Clone() builtinFunc {
|
|
newSig := &builtinMicroSecondSig{}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
// evalInt evals MICROSECOND(expr).
|
|
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_microsecond
|
|
func (b *builtinMicroSecondSig) evalInt(row chunk.Row) (int64, bool, error) {
|
|
dur, isNull, err := b.args[0].EvalDuration(b.ctx, row)
|
|
// ignore error and return NULL
|
|
if isNull || err != nil {
|
|
return 0, true, nil
|
|
}
|
|
return int64(dur.MicroSecond()), false, nil
|
|
}
|
|
|
|
type monthFunctionClass struct {
|
|
baseFunctionClass
|
|
}
|
|
|
|
func (c *monthFunctionClass) getFunction(ctx sessionctx.Context, args []Expression) (builtinFunc, error) {
|
|
if err := c.verifyArgs(args); err != nil {
|
|
return nil, err
|
|
}
|
|
bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETInt, types.ETDatetime)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
bf.tp.Flen, bf.tp.Decimal = 2, 0
|
|
sig := &builtinMonthSig{bf}
|
|
sig.setPbCode(tipb.ScalarFuncSig_Month)
|
|
return sig, nil
|
|
}
|
|
|
|
type builtinMonthSig struct {
|
|
baseBuiltinFunc
|
|
}
|
|
|
|
func (b *builtinMonthSig) Clone() builtinFunc {
|
|
newSig := &builtinMonthSig{}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
// evalInt evals MONTH(date).
|
|
// see: https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_month
|
|
func (b *builtinMonthSig) evalInt(row chunk.Row) (int64, bool, error) {
|
|
date, isNull, err := b.args[0].EvalTime(b.ctx, row)
|
|
|
|
if isNull || err != nil {
|
|
return 0, true, handleInvalidTimeError(b.ctx, err)
|
|
}
|
|
|
|
if date.IsZero() {
|
|
if b.ctx.GetSessionVars().SQLMode.HasNoZeroDateMode() {
|
|
return 0, true, handleInvalidTimeError(b.ctx, types.ErrWrongValue.GenWithStackByArgs(types.DateTimeStr, date.String()))
|
|
}
|
|
return 0, false, nil
|
|
}
|
|
|
|
return int64(date.Month()), false, nil
|
|
}
|
|
|
|
// monthNameFunctionClass see https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_monthname
|
|
type monthNameFunctionClass struct {
|
|
baseFunctionClass
|
|
}
|
|
|
|
func (c *monthNameFunctionClass) getFunction(ctx sessionctx.Context, args []Expression) (builtinFunc, error) {
|
|
if err := c.verifyArgs(args); err != nil {
|
|
return nil, err
|
|
}
|
|
bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETString, types.ETDatetime)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
bf.tp.Charset, bf.tp.Collate = ctx.GetSessionVars().GetCharsetInfo()
|
|
bf.tp.Flen = 10
|
|
sig := &builtinMonthNameSig{bf}
|
|
sig.setPbCode(tipb.ScalarFuncSig_MonthName)
|
|
return sig, nil
|
|
}
|
|
|
|
type builtinMonthNameSig struct {
|
|
baseBuiltinFunc
|
|
}
|
|
|
|
func (b *builtinMonthNameSig) Clone() builtinFunc {
|
|
newSig := &builtinMonthNameSig{}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
func (b *builtinMonthNameSig) evalString(row chunk.Row) (string, bool, error) {
|
|
arg, isNull, err := b.args[0].EvalTime(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return "", true, handleInvalidTimeError(b.ctx, err)
|
|
}
|
|
mon := arg.Month()
|
|
if (arg.IsZero() && b.ctx.GetSessionVars().SQLMode.HasNoZeroDateMode()) || mon < 0 || mon > len(types.MonthNames) {
|
|
return "", true, handleInvalidTimeError(b.ctx, types.ErrWrongValue.GenWithStackByArgs(types.DateTimeStr, arg.String()))
|
|
} else if mon == 0 || arg.IsZero() {
|
|
return "", true, nil
|
|
}
|
|
return types.MonthNames[mon-1], false, nil
|
|
}
|
|
|
|
type dayNameFunctionClass struct {
|
|
baseFunctionClass
|
|
}
|
|
|
|
func (c *dayNameFunctionClass) getFunction(ctx sessionctx.Context, args []Expression) (builtinFunc, error) {
|
|
if err := c.verifyArgs(args); err != nil {
|
|
return nil, err
|
|
}
|
|
bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETString, types.ETDatetime)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
bf.tp.Charset, bf.tp.Collate = ctx.GetSessionVars().GetCharsetInfo()
|
|
bf.tp.Flen = 10
|
|
sig := &builtinDayNameSig{bf}
|
|
sig.setPbCode(tipb.ScalarFuncSig_DayName)
|
|
return sig, nil
|
|
}
|
|
|
|
type builtinDayNameSig struct {
|
|
baseBuiltinFunc
|
|
}
|
|
|
|
func (b *builtinDayNameSig) Clone() builtinFunc {
|
|
newSig := &builtinDayNameSig{}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
func (b *builtinDayNameSig) evalIndex(row chunk.Row) (int64, bool, error) {
|
|
arg, isNull, err := b.args[0].EvalTime(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return 0, isNull, err
|
|
}
|
|
if arg.InvalidZero() {
|
|
return 0, true, handleInvalidTimeError(b.ctx, types.ErrWrongValue.GenWithStackByArgs(types.DateTimeStr, arg.String()))
|
|
}
|
|
// Monday is 0, ... Sunday = 6 in MySQL
|
|
// but in go, Sunday is 0, ... Saturday is 6
|
|
// w will do a conversion.
|
|
res := (int64(arg.Weekday()) + 6) % 7
|
|
return res, false, nil
|
|
}
|
|
|
|
// evalString evals a builtinDayNameSig.
|
|
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_dayname
|
|
func (b *builtinDayNameSig) evalString(row chunk.Row) (string, bool, error) {
|
|
idx, isNull, err := b.evalIndex(row)
|
|
if isNull || err != nil {
|
|
return "", isNull, err
|
|
}
|
|
return types.WeekdayNames[idx], false, nil
|
|
}
|
|
|
|
func (b *builtinDayNameSig) evalReal(row chunk.Row) (float64, bool, error) {
|
|
idx, isNull, err := b.evalIndex(row)
|
|
if isNull || err != nil {
|
|
return 0, isNull, err
|
|
}
|
|
return float64(idx), false, nil
|
|
}
|
|
|
|
func (b *builtinDayNameSig) evalInt(row chunk.Row) (int64, bool, error) {
|
|
idx, isNull, err := b.evalIndex(row)
|
|
if isNull || err != nil {
|
|
return 0, isNull, err
|
|
}
|
|
return idx, false, nil
|
|
}
|
|
|
|
type dayOfMonthFunctionClass struct {
|
|
baseFunctionClass
|
|
}
|
|
|
|
func (c *dayOfMonthFunctionClass) getFunction(ctx sessionctx.Context, args []Expression) (builtinFunc, error) {
|
|
if err := c.verifyArgs(args); err != nil {
|
|
return nil, err
|
|
}
|
|
bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETInt, types.ETDatetime)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
bf.tp.Flen = 2
|
|
sig := &builtinDayOfMonthSig{bf}
|
|
sig.setPbCode(tipb.ScalarFuncSig_DayOfMonth)
|
|
return sig, nil
|
|
}
|
|
|
|
type builtinDayOfMonthSig struct {
|
|
baseBuiltinFunc
|
|
}
|
|
|
|
func (b *builtinDayOfMonthSig) Clone() builtinFunc {
|
|
newSig := &builtinDayOfMonthSig{}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
// evalInt evals a builtinDayOfMonthSig.
|
|
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_dayofmonth
|
|
func (b *builtinDayOfMonthSig) evalInt(row chunk.Row) (int64, bool, error) {
|
|
arg, isNull, err := b.args[0].EvalTime(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return 0, true, handleInvalidTimeError(b.ctx, err)
|
|
}
|
|
if arg.IsZero() {
|
|
if b.ctx.GetSessionVars().SQLMode.HasNoZeroDateMode() {
|
|
return 0, true, handleInvalidTimeError(b.ctx, types.ErrWrongValue.GenWithStackByArgs(types.DateTimeStr, arg.String()))
|
|
}
|
|
return 0, false, nil
|
|
}
|
|
return int64(arg.Day()), false, nil
|
|
}
|
|
|
|
type dayOfWeekFunctionClass struct {
|
|
baseFunctionClass
|
|
}
|
|
|
|
func (c *dayOfWeekFunctionClass) getFunction(ctx sessionctx.Context, args []Expression) (builtinFunc, error) {
|
|
if err := c.verifyArgs(args); err != nil {
|
|
return nil, err
|
|
}
|
|
bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETInt, types.ETDatetime)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
bf.tp.Flen = 1
|
|
sig := &builtinDayOfWeekSig{bf}
|
|
sig.setPbCode(tipb.ScalarFuncSig_DayOfWeek)
|
|
return sig, nil
|
|
}
|
|
|
|
type builtinDayOfWeekSig struct {
|
|
baseBuiltinFunc
|
|
}
|
|
|
|
func (b *builtinDayOfWeekSig) Clone() builtinFunc {
|
|
newSig := &builtinDayOfWeekSig{}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
// evalInt evals a builtinDayOfWeekSig.
|
|
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_dayofweek
|
|
func (b *builtinDayOfWeekSig) evalInt(row chunk.Row) (int64, bool, error) {
|
|
arg, isNull, err := b.args[0].EvalTime(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return 0, true, handleInvalidTimeError(b.ctx, err)
|
|
}
|
|
if arg.InvalidZero() {
|
|
return 0, true, handleInvalidTimeError(b.ctx, types.ErrWrongValue.GenWithStackByArgs(types.DateTimeStr, arg.String()))
|
|
}
|
|
// 1 is Sunday, 2 is Monday, .... 7 is Saturday
|
|
return int64(arg.Weekday() + 1), false, nil
|
|
}
|
|
|
|
type dayOfYearFunctionClass struct {
|
|
baseFunctionClass
|
|
}
|
|
|
|
func (c *dayOfYearFunctionClass) getFunction(ctx sessionctx.Context, args []Expression) (builtinFunc, error) {
|
|
if err := c.verifyArgs(args); err != nil {
|
|
return nil, err
|
|
}
|
|
bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETInt, types.ETDatetime)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
bf.tp.Flen = 3
|
|
sig := &builtinDayOfYearSig{bf}
|
|
sig.setPbCode(tipb.ScalarFuncSig_DayOfYear)
|
|
return sig, nil
|
|
}
|
|
|
|
type builtinDayOfYearSig struct {
|
|
baseBuiltinFunc
|
|
}
|
|
|
|
func (b *builtinDayOfYearSig) Clone() builtinFunc {
|
|
newSig := &builtinDayOfYearSig{}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
// evalInt evals a builtinDayOfYearSig.
|
|
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_dayofyear
|
|
func (b *builtinDayOfYearSig) evalInt(row chunk.Row) (int64, bool, error) {
|
|
arg, isNull, err := b.args[0].EvalTime(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return 0, isNull, handleInvalidTimeError(b.ctx, err)
|
|
}
|
|
if arg.InvalidZero() {
|
|
return 0, true, handleInvalidTimeError(b.ctx, types.ErrWrongValue.GenWithStackByArgs(types.DateTimeStr, arg.String()))
|
|
}
|
|
|
|
return int64(arg.YearDay()), false, nil
|
|
}
|
|
|
|
type weekFunctionClass struct {
|
|
baseFunctionClass
|
|
}
|
|
|
|
func (c *weekFunctionClass) getFunction(ctx sessionctx.Context, args []Expression) (builtinFunc, error) {
|
|
if err := c.verifyArgs(args); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
argTps := []types.EvalType{types.ETDatetime}
|
|
if len(args) == 2 {
|
|
argTps = append(argTps, types.ETInt)
|
|
}
|
|
|
|
bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETInt, argTps...)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
bf.tp.Flen, bf.tp.Decimal = 2, 0
|
|
|
|
var sig builtinFunc
|
|
if len(args) == 2 {
|
|
sig = &builtinWeekWithModeSig{bf}
|
|
sig.setPbCode(tipb.ScalarFuncSig_WeekWithMode)
|
|
} else {
|
|
sig = &builtinWeekWithoutModeSig{bf}
|
|
sig.setPbCode(tipb.ScalarFuncSig_WeekWithoutMode)
|
|
}
|
|
return sig, nil
|
|
}
|
|
|
|
type builtinWeekWithModeSig struct {
|
|
baseBuiltinFunc
|
|
}
|
|
|
|
func (b *builtinWeekWithModeSig) Clone() builtinFunc {
|
|
newSig := &builtinWeekWithModeSig{}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
// evalInt evals WEEK(date, mode).
|
|
// see: https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_week
|
|
func (b *builtinWeekWithModeSig) evalInt(row chunk.Row) (int64, bool, error) {
|
|
date, isNull, err := b.args[0].EvalTime(b.ctx, row)
|
|
|
|
if isNull || err != nil {
|
|
return 0, true, handleInvalidTimeError(b.ctx, err)
|
|
}
|
|
|
|
if date.IsZero() {
|
|
return 0, true, handleInvalidTimeError(b.ctx, types.ErrWrongValue.GenWithStackByArgs(types.DateTimeStr, date.String()))
|
|
}
|
|
|
|
mode, isNull, err := b.args[1].EvalInt(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return 0, isNull, err
|
|
}
|
|
|
|
week := date.Week(int(mode))
|
|
return int64(week), false, nil
|
|
}
|
|
|
|
type builtinWeekWithoutModeSig struct {
|
|
baseBuiltinFunc
|
|
}
|
|
|
|
func (b *builtinWeekWithoutModeSig) Clone() builtinFunc {
|
|
newSig := &builtinWeekWithoutModeSig{}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
// evalInt evals WEEK(date).
|
|
// see: https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_week
|
|
func (b *builtinWeekWithoutModeSig) evalInt(row chunk.Row) (int64, bool, error) {
|
|
date, isNull, err := b.args[0].EvalTime(b.ctx, row)
|
|
|
|
if isNull || err != nil {
|
|
return 0, true, handleInvalidTimeError(b.ctx, err)
|
|
}
|
|
|
|
if date.IsZero() {
|
|
return 0, true, handleInvalidTimeError(b.ctx, types.ErrWrongValue.GenWithStackByArgs(types.DateTimeStr, date.String()))
|
|
}
|
|
|
|
mode := 0
|
|
modeStr, ok := b.ctx.GetSessionVars().GetSystemVar(variable.DefaultWeekFormat)
|
|
if ok && modeStr != "" {
|
|
mode, err = strconv.Atoi(modeStr)
|
|
if err != nil {
|
|
return 0, true, handleInvalidTimeError(b.ctx, types.ErrInvalidWeekModeFormat.GenWithStackByArgs(modeStr))
|
|
}
|
|
}
|
|
|
|
week := date.Week(mode)
|
|
return int64(week), false, nil
|
|
}
|
|
|
|
type weekDayFunctionClass struct {
|
|
baseFunctionClass
|
|
}
|
|
|
|
func (c *weekDayFunctionClass) getFunction(ctx sessionctx.Context, args []Expression) (builtinFunc, error) {
|
|
if err := c.verifyArgs(args); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETInt, types.ETDatetime)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
bf.tp.Flen = 1
|
|
|
|
sig := &builtinWeekDaySig{bf}
|
|
sig.setPbCode(tipb.ScalarFuncSig_WeekDay)
|
|
return sig, nil
|
|
}
|
|
|
|
type builtinWeekDaySig struct {
|
|
baseBuiltinFunc
|
|
}
|
|
|
|
func (b *builtinWeekDaySig) Clone() builtinFunc {
|
|
newSig := &builtinWeekDaySig{}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
// evalInt evals WEEKDAY(date).
|
|
func (b *builtinWeekDaySig) evalInt(row chunk.Row) (int64, bool, error) {
|
|
date, isNull, err := b.args[0].EvalTime(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return 0, true, handleInvalidTimeError(b.ctx, err)
|
|
}
|
|
|
|
if date.IsZero() {
|
|
return 0, true, handleInvalidTimeError(b.ctx, types.ErrWrongValue.GenWithStackByArgs(types.DateTimeStr, date.String()))
|
|
}
|
|
|
|
return int64(date.Weekday()+6) % 7, false, nil
|
|
}
|
|
|
|
type weekOfYearFunctionClass struct {
|
|
baseFunctionClass
|
|
}
|
|
|
|
func (c *weekOfYearFunctionClass) getFunction(ctx sessionctx.Context, args []Expression) (builtinFunc, error) {
|
|
if err := c.verifyArgs(args); err != nil {
|
|
return nil, err
|
|
}
|
|
bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETInt, types.ETDatetime)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
bf.tp.Flen, bf.tp.Decimal = 2, 0
|
|
sig := &builtinWeekOfYearSig{bf}
|
|
sig.setPbCode(tipb.ScalarFuncSig_WeekOfYear)
|
|
return sig, nil
|
|
}
|
|
|
|
type builtinWeekOfYearSig struct {
|
|
baseBuiltinFunc
|
|
}
|
|
|
|
func (b *builtinWeekOfYearSig) Clone() builtinFunc {
|
|
newSig := &builtinWeekOfYearSig{}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
// evalInt evals WEEKOFYEAR(date).
|
|
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_weekofyear
|
|
func (b *builtinWeekOfYearSig) evalInt(row chunk.Row) (int64, bool, error) {
|
|
date, isNull, err := b.args[0].EvalTime(b.ctx, row)
|
|
|
|
if isNull || err != nil {
|
|
return 0, true, handleInvalidTimeError(b.ctx, err)
|
|
}
|
|
|
|
if date.IsZero() {
|
|
return 0, true, handleInvalidTimeError(b.ctx, types.ErrWrongValue.GenWithStackByArgs(types.DateTimeStr, date.String()))
|
|
}
|
|
|
|
week := date.Week(3)
|
|
return int64(week), false, nil
|
|
}
|
|
|
|
type yearFunctionClass struct {
|
|
baseFunctionClass
|
|
}
|
|
|
|
func (c *yearFunctionClass) getFunction(ctx sessionctx.Context, args []Expression) (builtinFunc, error) {
|
|
if err := c.verifyArgs(args); err != nil {
|
|
return nil, err
|
|
}
|
|
bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETInt, types.ETDatetime)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
bf.tp.Flen, bf.tp.Decimal = 4, 0
|
|
sig := &builtinYearSig{bf}
|
|
sig.setPbCode(tipb.ScalarFuncSig_Year)
|
|
return sig, nil
|
|
}
|
|
|
|
type builtinYearSig struct {
|
|
baseBuiltinFunc
|
|
}
|
|
|
|
func (b *builtinYearSig) Clone() builtinFunc {
|
|
newSig := &builtinYearSig{}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
// evalInt evals YEAR(date).
|
|
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_year
|
|
func (b *builtinYearSig) evalInt(row chunk.Row) (int64, bool, error) {
|
|
date, isNull, err := b.args[0].EvalTime(b.ctx, row)
|
|
|
|
if isNull || err != nil {
|
|
return 0, true, handleInvalidTimeError(b.ctx, err)
|
|
}
|
|
|
|
if date.IsZero() {
|
|
if b.ctx.GetSessionVars().SQLMode.HasNoZeroDateMode() {
|
|
return 0, true, handleInvalidTimeError(b.ctx, types.ErrWrongValue.GenWithStackByArgs(types.DateTimeStr, date.String()))
|
|
}
|
|
return 0, false, nil
|
|
}
|
|
return int64(date.Year()), false, nil
|
|
}
|
|
|
|
type yearWeekFunctionClass struct {
|
|
baseFunctionClass
|
|
}
|
|
|
|
func (c *yearWeekFunctionClass) getFunction(ctx sessionctx.Context, args []Expression) (builtinFunc, error) {
|
|
if err := c.verifyArgs(args); err != nil {
|
|
return nil, err
|
|
}
|
|
argTps := []types.EvalType{types.ETDatetime}
|
|
if len(args) == 2 {
|
|
argTps = append(argTps, types.ETInt)
|
|
}
|
|
|
|
bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETInt, argTps...)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
bf.tp.Flen, bf.tp.Decimal = 6, 0
|
|
|
|
var sig builtinFunc
|
|
if len(args) == 2 {
|
|
sig = &builtinYearWeekWithModeSig{bf}
|
|
sig.setPbCode(tipb.ScalarFuncSig_YearWeekWithMode)
|
|
} else {
|
|
sig = &builtinYearWeekWithoutModeSig{bf}
|
|
sig.setPbCode(tipb.ScalarFuncSig_YearWeekWithoutMode)
|
|
}
|
|
return sig, nil
|
|
}
|
|
|
|
type builtinYearWeekWithModeSig struct {
|
|
baseBuiltinFunc
|
|
}
|
|
|
|
func (b *builtinYearWeekWithModeSig) Clone() builtinFunc {
|
|
newSig := &builtinYearWeekWithModeSig{}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
// evalInt evals YEARWEEK(date,mode).
|
|
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_yearweek
|
|
func (b *builtinYearWeekWithModeSig) evalInt(row chunk.Row) (int64, bool, error) {
|
|
date, isNull, err := b.args[0].EvalTime(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return 0, isNull, handleInvalidTimeError(b.ctx, err)
|
|
}
|
|
if date.IsZero() {
|
|
return 0, true, handleInvalidTimeError(b.ctx, types.ErrWrongValue.GenWithStackByArgs(types.DateTimeStr, date.String()))
|
|
}
|
|
|
|
mode, isNull, err := b.args[1].EvalInt(b.ctx, row)
|
|
if err != nil {
|
|
return 0, true, err
|
|
}
|
|
if isNull {
|
|
mode = 0
|
|
}
|
|
|
|
year, week := date.YearWeek(int(mode))
|
|
result := int64(week + year*100)
|
|
if result < 0 {
|
|
return int64(math.MaxUint32), false, nil
|
|
}
|
|
return result, false, nil
|
|
}
|
|
|
|
type builtinYearWeekWithoutModeSig struct {
|
|
baseBuiltinFunc
|
|
}
|
|
|
|
func (b *builtinYearWeekWithoutModeSig) Clone() builtinFunc {
|
|
newSig := &builtinYearWeekWithoutModeSig{}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
// evalInt evals YEARWEEK(date).
|
|
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_yearweek
|
|
func (b *builtinYearWeekWithoutModeSig) evalInt(row chunk.Row) (int64, bool, error) {
|
|
date, isNull, err := b.args[0].EvalTime(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return 0, true, handleInvalidTimeError(b.ctx, err)
|
|
}
|
|
|
|
if date.InvalidZero() {
|
|
return 0, true, handleInvalidTimeError(b.ctx, types.ErrWrongValue.GenWithStackByArgs(types.DateTimeStr, date.String()))
|
|
}
|
|
|
|
year, week := date.YearWeek(0)
|
|
result := int64(week + year*100)
|
|
if result < 0 {
|
|
return int64(math.MaxUint32), false, nil
|
|
}
|
|
return result, false, nil
|
|
}
|
|
|
|
type fromUnixTimeFunctionClass struct {
|
|
baseFunctionClass
|
|
}
|
|
|
|
func (c *fromUnixTimeFunctionClass) getFunction(ctx sessionctx.Context, args []Expression) (sig builtinFunc, err error) {
|
|
if err = c.verifyArgs(args); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
retTp, argTps := types.ETDatetime, make([]types.EvalType, 0, len(args))
|
|
argTps = append(argTps, types.ETDecimal)
|
|
if len(args) == 2 {
|
|
retTp = types.ETString
|
|
argTps = append(argTps, types.ETString)
|
|
}
|
|
|
|
isArg0Str := args[0].GetType().EvalType() == types.ETString
|
|
bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, retTp, argTps...)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if len(args) > 1 {
|
|
sig = &builtinFromUnixTime2ArgSig{bf}
|
|
sig.setPbCode(tipb.ScalarFuncSig_FromUnixTime2Arg)
|
|
return sig, nil
|
|
}
|
|
|
|
// Calculate the time fsp.
|
|
bf.tp.Decimal = int(types.MaxFsp)
|
|
if !isArg0Str {
|
|
if args[0].GetType().Decimal != types.UnspecifiedLength {
|
|
bf.tp.Decimal = mathutil.Min(bf.tp.Decimal, args[0].GetType().Decimal)
|
|
}
|
|
}
|
|
|
|
sig = &builtinFromUnixTime1ArgSig{bf}
|
|
sig.setPbCode(tipb.ScalarFuncSig_FromUnixTime1Arg)
|
|
return sig, nil
|
|
}
|
|
|
|
func evalFromUnixTime(ctx sessionctx.Context, fsp int8, unixTimeStamp *types.MyDecimal) (res types.Time, isNull bool, err error) {
|
|
// 0 <= unixTimeStamp <= INT32_MAX
|
|
if unixTimeStamp.IsNegative() {
|
|
return res, true, nil
|
|
}
|
|
integralPart, err := unixTimeStamp.ToInt()
|
|
if err != nil && !terror.ErrorEqual(err, types.ErrTruncated) {
|
|
return res, true, err
|
|
}
|
|
if integralPart > int64(math.MaxInt32) {
|
|
return res, true, nil
|
|
}
|
|
// Split the integral part and fractional part of a decimal timestamp.
|
|
// e.g. for timestamp 12345.678,
|
|
// first get the integral part 12345,
|
|
// then (12345.678 - 12345) * (10^9) to get the decimal part and convert it to nanosecond precision.
|
|
integerDecimalTp := new(types.MyDecimal).FromInt(integralPart)
|
|
fracDecimalTp := new(types.MyDecimal)
|
|
err = types.DecimalSub(unixTimeStamp, integerDecimalTp, fracDecimalTp)
|
|
if err != nil {
|
|
return res, true, err
|
|
}
|
|
nano := new(types.MyDecimal).FromInt(int64(time.Second))
|
|
x := new(types.MyDecimal)
|
|
err = types.DecimalMul(fracDecimalTp, nano, x)
|
|
if err != nil {
|
|
return res, true, err
|
|
}
|
|
fractionalPart, err := x.ToInt() // here fractionalPart is result multiplying the original fractional part by 10^9.
|
|
if err != nil && !terror.ErrorEqual(err, types.ErrTruncated) {
|
|
return res, true, err
|
|
}
|
|
if fsp < 0 {
|
|
fsp = types.MaxFsp
|
|
}
|
|
|
|
sc := ctx.GetSessionVars().StmtCtx
|
|
tmp := time.Unix(integralPart, fractionalPart).In(sc.TimeZone)
|
|
t, err := convertTimeToMysqlTime(tmp, fsp, types.ModeHalfEven)
|
|
if err != nil {
|
|
return res, true, err
|
|
}
|
|
return t, false, nil
|
|
}
|
|
|
|
type builtinFromUnixTime1ArgSig struct {
|
|
baseBuiltinFunc
|
|
}
|
|
|
|
func (b *builtinFromUnixTime1ArgSig) Clone() builtinFunc {
|
|
newSig := &builtinFromUnixTime1ArgSig{}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
// evalTime evals a builtinFromUnixTime1ArgSig.
|
|
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_from-unixtime
|
|
func (b *builtinFromUnixTime1ArgSig) evalTime(row chunk.Row) (res types.Time, isNull bool, err error) {
|
|
unixTimeStamp, isNull, err := b.args[0].EvalDecimal(b.ctx, row)
|
|
if err != nil || isNull {
|
|
return res, isNull, err
|
|
}
|
|
return evalFromUnixTime(b.ctx, int8(b.tp.Decimal), unixTimeStamp)
|
|
}
|
|
|
|
type builtinFromUnixTime2ArgSig struct {
|
|
baseBuiltinFunc
|
|
}
|
|
|
|
func (b *builtinFromUnixTime2ArgSig) Clone() builtinFunc {
|
|
newSig := &builtinFromUnixTime2ArgSig{}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
// evalString evals a builtinFromUnixTime2ArgSig.
|
|
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_from-unixtime
|
|
func (b *builtinFromUnixTime2ArgSig) evalString(row chunk.Row) (res string, isNull bool, err error) {
|
|
format, isNull, err := b.args[1].EvalString(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return "", true, err
|
|
}
|
|
unixTimeStamp, isNull, err := b.args[0].EvalDecimal(b.ctx, row)
|
|
if err != nil || isNull {
|
|
return "", isNull, err
|
|
}
|
|
t, isNull, err := evalFromUnixTime(b.ctx, int8(b.tp.Decimal), unixTimeStamp)
|
|
if isNull || err != nil {
|
|
return "", isNull, err
|
|
}
|
|
res, err = t.DateFormat(format)
|
|
return res, err != nil, err
|
|
}
|
|
|
|
type getFormatFunctionClass struct {
|
|
baseFunctionClass
|
|
}
|
|
|
|
func (c *getFormatFunctionClass) getFunction(ctx sessionctx.Context, args []Expression) (builtinFunc, error) {
|
|
if err := c.verifyArgs(args); err != nil {
|
|
return nil, err
|
|
}
|
|
bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETString, types.ETString, types.ETString)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
bf.tp.Flen = 17
|
|
sig := &builtinGetFormatSig{bf}
|
|
sig.setPbCode(tipb.ScalarFuncSig_GetFormat)
|
|
return sig, nil
|
|
}
|
|
|
|
type builtinGetFormatSig struct {
|
|
baseBuiltinFunc
|
|
}
|
|
|
|
func (b *builtinGetFormatSig) Clone() builtinFunc {
|
|
newSig := &builtinGetFormatSig{}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
// evalString evals a builtinGetFormatSig.
|
|
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_get-format
|
|
func (b *builtinGetFormatSig) evalString(row chunk.Row) (string, bool, error) {
|
|
t, isNull, err := b.args[0].EvalString(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return "", isNull, err
|
|
}
|
|
l, isNull, err := b.args[1].EvalString(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return "", isNull, err
|
|
}
|
|
|
|
res := b.getFormat(t, l)
|
|
return res, false, nil
|
|
}
|
|
|
|
type strToDateFunctionClass struct {
|
|
baseFunctionClass
|
|
}
|
|
|
|
func (c *strToDateFunctionClass) getRetTp(ctx sessionctx.Context, arg Expression) (tp byte, fsp int8) {
|
|
tp = mysql.TypeDatetime
|
|
if _, ok := arg.(*Constant); !ok {
|
|
return tp, types.MaxFsp
|
|
}
|
|
strArg := WrapWithCastAsString(ctx, arg)
|
|
format, isNull, err := strArg.EvalString(ctx, chunk.Row{})
|
|
if err != nil || isNull {
|
|
return
|
|
}
|
|
|
|
isDuration, isDate := types.GetFormatType(format)
|
|
if isDuration && !isDate {
|
|
tp = mysql.TypeDuration
|
|
} else if !isDuration && isDate {
|
|
tp = mysql.TypeDate
|
|
}
|
|
if strings.Contains(format, "%f") {
|
|
fsp = types.MaxFsp
|
|
}
|
|
return
|
|
}
|
|
|
|
// getFunction see https://dev.mysql.com/doc/refman/5.5/en/date-and-time-functions.html#function_str-to-date
|
|
func (c *strToDateFunctionClass) getFunction(ctx sessionctx.Context, args []Expression) (sig builtinFunc, err error) {
|
|
if err := c.verifyArgs(args); err != nil {
|
|
return nil, err
|
|
}
|
|
retTp, fsp := c.getRetTp(ctx, args[1])
|
|
switch retTp {
|
|
case mysql.TypeDate:
|
|
bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETDatetime, types.ETString, types.ETString)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
bf.tp.Tp, bf.tp.Flen, bf.tp.Decimal = mysql.TypeDate, mysql.MaxDateWidth, int(types.MinFsp)
|
|
sig = &builtinStrToDateDateSig{bf}
|
|
sig.setPbCode(tipb.ScalarFuncSig_StrToDateDate)
|
|
case mysql.TypeDatetime:
|
|
bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETDatetime, types.ETString, types.ETString)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if fsp == types.MinFsp {
|
|
bf.tp.Flen, bf.tp.Decimal = mysql.MaxDatetimeWidthNoFsp, int(types.MinFsp)
|
|
} else {
|
|
bf.tp.Flen, bf.tp.Decimal = mysql.MaxDatetimeWidthWithFsp, int(types.MaxFsp)
|
|
}
|
|
sig = &builtinStrToDateDatetimeSig{bf}
|
|
sig.setPbCode(tipb.ScalarFuncSig_StrToDateDatetime)
|
|
case mysql.TypeDuration:
|
|
bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETDuration, types.ETString, types.ETString)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if fsp == types.MinFsp {
|
|
bf.tp.Flen, bf.tp.Decimal = mysql.MaxDurationWidthNoFsp, int(types.MinFsp)
|
|
} else {
|
|
bf.tp.Flen, bf.tp.Decimal = mysql.MaxDurationWidthWithFsp, int(types.MaxFsp)
|
|
}
|
|
sig = &builtinStrToDateDurationSig{bf}
|
|
sig.setPbCode(tipb.ScalarFuncSig_StrToDateDuration)
|
|
}
|
|
return sig, nil
|
|
}
|
|
|
|
type builtinStrToDateDateSig struct {
|
|
baseBuiltinFunc
|
|
}
|
|
|
|
func (b *builtinStrToDateDateSig) Clone() builtinFunc {
|
|
newSig := &builtinStrToDateDateSig{}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
func (b *builtinStrToDateDateSig) evalTime(row chunk.Row) (types.Time, bool, error) {
|
|
date, isNull, err := b.args[0].EvalString(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return types.ZeroTime, isNull, err
|
|
}
|
|
format, isNull, err := b.args[1].EvalString(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return types.ZeroTime, isNull, err
|
|
}
|
|
var t types.Time
|
|
sc := b.ctx.GetSessionVars().StmtCtx
|
|
succ := t.StrToDate(sc, date, format)
|
|
if !succ {
|
|
return types.ZeroTime, true, handleInvalidTimeError(b.ctx, types.ErrWrongValue.GenWithStackByArgs(types.DateTimeStr, t.String()))
|
|
}
|
|
if b.ctx.GetSessionVars().SQLMode.HasNoZeroDateMode() && (t.Year() == 0 || t.Month() == 0 || t.Day() == 0) {
|
|
return types.ZeroTime, true, handleInvalidTimeError(b.ctx, types.ErrWrongValue.GenWithStackByArgs(types.DateTimeStr, t.String()))
|
|
}
|
|
t.SetType(mysql.TypeDate)
|
|
t.SetFsp(types.MinFsp)
|
|
return t, false, nil
|
|
}
|
|
|
|
type builtinStrToDateDatetimeSig struct {
|
|
baseBuiltinFunc
|
|
}
|
|
|
|
func (b *builtinStrToDateDatetimeSig) Clone() builtinFunc {
|
|
newSig := &builtinStrToDateDatetimeSig{}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
func (b *builtinStrToDateDatetimeSig) evalTime(row chunk.Row) (types.Time, bool, error) {
|
|
date, isNull, err := b.args[0].EvalString(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return types.ZeroTime, isNull, err
|
|
}
|
|
format, isNull, err := b.args[1].EvalString(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return types.ZeroTime, isNull, err
|
|
}
|
|
var t types.Time
|
|
sc := b.ctx.GetSessionVars().StmtCtx
|
|
succ := t.StrToDate(sc, date, format)
|
|
if !succ {
|
|
return types.ZeroTime, true, handleInvalidTimeError(b.ctx, types.ErrWrongValue.GenWithStackByArgs(types.DateTimeStr, t.String()))
|
|
}
|
|
if b.ctx.GetSessionVars().SQLMode.HasNoZeroDateMode() && (t.Year() == 0 || t.Month() == 0 || t.Day() == 0) {
|
|
return types.ZeroTime, true, handleInvalidTimeError(b.ctx, types.ErrWrongValue.GenWithStackByArgs(types.DateTimeStr, t.String()))
|
|
}
|
|
t.SetType(mysql.TypeDatetime)
|
|
t.SetFsp(int8(b.tp.Decimal))
|
|
return t, false, nil
|
|
}
|
|
|
|
type builtinStrToDateDurationSig struct {
|
|
baseBuiltinFunc
|
|
}
|
|
|
|
func (b *builtinStrToDateDurationSig) Clone() builtinFunc {
|
|
newSig := &builtinStrToDateDurationSig{}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
// evalDuration
|
|
// TODO: If the NO_ZERO_DATE or NO_ZERO_IN_DATE SQL mode is enabled, zero dates or part of dates are disallowed.
|
|
// In that case, STR_TO_DATE() returns NULL and generates a warning.
|
|
func (b *builtinStrToDateDurationSig) evalDuration(row chunk.Row) (types.Duration, bool, error) {
|
|
date, isNull, err := b.args[0].EvalString(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return types.Duration{}, isNull, err
|
|
}
|
|
format, isNull, err := b.args[1].EvalString(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return types.Duration{}, isNull, err
|
|
}
|
|
var t types.Time
|
|
sc := b.ctx.GetSessionVars().StmtCtx
|
|
succ := t.StrToDate(sc, date, format)
|
|
if !succ {
|
|
return types.Duration{}, true, handleInvalidTimeError(b.ctx, types.ErrWrongValue.GenWithStackByArgs(types.DateTimeStr, t.String()))
|
|
}
|
|
if b.ctx.GetSessionVars().SQLMode.HasNoZeroDateMode() && (t.Year() == 0 || t.Month() == 0 || t.Day() == 0) {
|
|
return types.Duration{}, true, handleInvalidTimeError(b.ctx, types.ErrWrongValue.GenWithStackByArgs(types.DateTimeStr, t.String()))
|
|
}
|
|
t.SetFsp(int8(b.tp.Decimal))
|
|
dur, err := t.ConvertToDuration()
|
|
return dur, err != nil, err
|
|
}
|
|
|
|
type sysDateFunctionClass struct {
|
|
baseFunctionClass
|
|
}
|
|
|
|
func (c *sysDateFunctionClass) getFunction(ctx sessionctx.Context, args []Expression) (builtinFunc, error) {
|
|
if err := c.verifyArgs(args); err != nil {
|
|
return nil, err
|
|
}
|
|
var argTps = make([]types.EvalType, 0)
|
|
if len(args) == 1 {
|
|
argTps = append(argTps, types.ETInt)
|
|
}
|
|
bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETDatetime, argTps...)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
bf.tp.Flen, bf.tp.Decimal = 19, 0
|
|
|
|
var sig builtinFunc
|
|
if len(args) == 1 {
|
|
sig = &builtinSysDateWithFspSig{bf}
|
|
sig.setPbCode(tipb.ScalarFuncSig_SysDateWithFsp)
|
|
} else {
|
|
sig = &builtinSysDateWithoutFspSig{bf}
|
|
sig.setPbCode(tipb.ScalarFuncSig_SysDateWithoutFsp)
|
|
}
|
|
return sig, nil
|
|
}
|
|
|
|
type builtinSysDateWithFspSig struct {
|
|
baseBuiltinFunc
|
|
}
|
|
|
|
func (b *builtinSysDateWithFspSig) Clone() builtinFunc {
|
|
newSig := &builtinSysDateWithFspSig{}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
// evalTime evals SYSDATE(fsp).
|
|
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_sysdate
|
|
func (b *builtinSysDateWithFspSig) evalTime(row chunk.Row) (d types.Time, isNull bool, err error) {
|
|
fsp, isNull, err := b.args[0].EvalInt(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return types.ZeroTime, isNull, err
|
|
}
|
|
|
|
loc := b.ctx.GetSessionVars().Location()
|
|
now := time.Now().In(loc)
|
|
result, err := convertTimeToMysqlTime(now, int8(fsp), types.ModeHalfEven)
|
|
if err != nil {
|
|
return types.ZeroTime, true, err
|
|
}
|
|
return result, false, nil
|
|
}
|
|
|
|
type builtinSysDateWithoutFspSig struct {
|
|
baseBuiltinFunc
|
|
}
|
|
|
|
func (b *builtinSysDateWithoutFspSig) Clone() builtinFunc {
|
|
newSig := &builtinSysDateWithoutFspSig{}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
// evalTime evals SYSDATE().
|
|
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_sysdate
|
|
func (b *builtinSysDateWithoutFspSig) evalTime(row chunk.Row) (d types.Time, isNull bool, err error) {
|
|
tz := b.ctx.GetSessionVars().Location()
|
|
now := time.Now().In(tz)
|
|
result, err := convertTimeToMysqlTime(now, 0, types.ModeHalfEven)
|
|
if err != nil {
|
|
return types.ZeroTime, true, err
|
|
}
|
|
return result, false, nil
|
|
}
|
|
|
|
type currentDateFunctionClass struct {
|
|
baseFunctionClass
|
|
}
|
|
|
|
func (c *currentDateFunctionClass) getFunction(ctx sessionctx.Context, args []Expression) (builtinFunc, error) {
|
|
if err := c.verifyArgs(args); err != nil {
|
|
return nil, err
|
|
}
|
|
bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETDatetime)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
bf.tp.Flen, bf.tp.Decimal = 10, 0
|
|
sig := &builtinCurrentDateSig{bf}
|
|
return sig, nil
|
|
}
|
|
|
|
type builtinCurrentDateSig struct {
|
|
baseBuiltinFunc
|
|
}
|
|
|
|
func (b *builtinCurrentDateSig) Clone() builtinFunc {
|
|
newSig := &builtinCurrentDateSig{}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
// evalTime evals CURDATE().
|
|
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_curdate
|
|
func (b *builtinCurrentDateSig) evalTime(row chunk.Row) (d types.Time, isNull bool, err error) {
|
|
tz := b.ctx.GetSessionVars().Location()
|
|
nowTs, err := getStmtTimestamp(b.ctx)
|
|
if err != nil {
|
|
return types.ZeroTime, true, err
|
|
}
|
|
year, month, day := nowTs.In(tz).Date()
|
|
result := types.NewTime(types.FromDate(year, int(month), day, 0, 0, 0, 0), mysql.TypeDate, 0)
|
|
return result, false, nil
|
|
}
|
|
|
|
type currentTimeFunctionClass struct {
|
|
baseFunctionClass
|
|
}
|
|
|
|
func (c *currentTimeFunctionClass) getFunction(ctx sessionctx.Context, args []Expression) (sig builtinFunc, err error) {
|
|
if err = c.verifyArgs(args); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if len(args) == 0 {
|
|
bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETDuration)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
bf.tp.Flen, bf.tp.Decimal = mysql.MaxDurationWidthNoFsp, int(types.MinFsp)
|
|
sig = &builtinCurrentTime0ArgSig{bf}
|
|
sig.setPbCode(tipb.ScalarFuncSig_CurrentTime0Arg)
|
|
return sig, nil
|
|
}
|
|
// args[0] must be a constant which should not be null.
|
|
_, ok := args[0].(*Constant)
|
|
fsp := int64(types.MaxFsp)
|
|
if ok {
|
|
fsp, _, err = args[0].EvalInt(ctx, chunk.Row{})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if fsp > int64(types.MaxFsp) {
|
|
return nil, errors.Errorf("Too-big precision %v specified for 'curtime'. Maximum is %v.", fsp, types.MaxFsp)
|
|
} else if fsp < int64(types.MinFsp) {
|
|
return nil, errors.Errorf("Invalid negative %d specified, must in [0, 6].", fsp)
|
|
}
|
|
}
|
|
bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETDuration, types.ETInt)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
bf.tp.Flen, bf.tp.Decimal = mysql.MaxDurationWidthWithFsp, int(fsp)
|
|
sig = &builtinCurrentTime1ArgSig{bf}
|
|
sig.setPbCode(tipb.ScalarFuncSig_CurrentTime1Arg)
|
|
return sig, nil
|
|
}
|
|
|
|
type builtinCurrentTime0ArgSig struct {
|
|
baseBuiltinFunc
|
|
}
|
|
|
|
func (b *builtinCurrentTime0ArgSig) Clone() builtinFunc {
|
|
newSig := &builtinCurrentTime0ArgSig{}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
func (b *builtinCurrentTime0ArgSig) evalDuration(row chunk.Row) (types.Duration, bool, error) {
|
|
tz := b.ctx.GetSessionVars().Location()
|
|
nowTs, err := getStmtTimestamp(b.ctx)
|
|
if err != nil {
|
|
return types.Duration{}, true, err
|
|
}
|
|
dur := nowTs.In(tz).Format(types.TimeFormat)
|
|
res, err := types.ParseDuration(b.ctx.GetSessionVars().StmtCtx, dur, types.MinFsp)
|
|
if err != nil {
|
|
return types.Duration{}, true, err
|
|
}
|
|
return res, false, nil
|
|
}
|
|
|
|
type builtinCurrentTime1ArgSig struct {
|
|
baseBuiltinFunc
|
|
}
|
|
|
|
func (b *builtinCurrentTime1ArgSig) Clone() builtinFunc {
|
|
newSig := &builtinCurrentTime1ArgSig{}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
func (b *builtinCurrentTime1ArgSig) evalDuration(row chunk.Row) (types.Duration, bool, error) {
|
|
fsp, _, err := b.args[0].EvalInt(b.ctx, row)
|
|
if err != nil {
|
|
return types.Duration{}, true, err
|
|
}
|
|
tz := b.ctx.GetSessionVars().Location()
|
|
nowTs, err := getStmtTimestamp(b.ctx)
|
|
if err != nil {
|
|
return types.Duration{}, true, err
|
|
}
|
|
dur := nowTs.In(tz).Format(types.TimeFSPFormat)
|
|
res, err := types.ParseDuration(b.ctx.GetSessionVars().StmtCtx, dur, int8(fsp))
|
|
if err != nil {
|
|
return types.Duration{}, true, err
|
|
}
|
|
return res, false, nil
|
|
}
|
|
|
|
type timeFunctionClass struct {
|
|
baseFunctionClass
|
|
}
|
|
|
|
func (c *timeFunctionClass) getFunction(ctx sessionctx.Context, args []Expression) (builtinFunc, error) {
|
|
err := c.verifyArgs(args)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETDuration, types.ETString)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
bf.tp.Decimal, err = getExpressionFsp(ctx, args[0])
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
sig := &builtinTimeSig{bf}
|
|
sig.setPbCode(tipb.ScalarFuncSig_Time)
|
|
return sig, nil
|
|
}
|
|
|
|
type builtinTimeSig struct {
|
|
baseBuiltinFunc
|
|
}
|
|
|
|
func (b *builtinTimeSig) Clone() builtinFunc {
|
|
newSig := &builtinTimeSig{}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
// evalDuration evals a builtinTimeSig.
|
|
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_time.
|
|
func (b *builtinTimeSig) evalDuration(row chunk.Row) (res types.Duration, isNull bool, err error) {
|
|
expr, isNull, err := b.args[0].EvalString(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return res, isNull, err
|
|
}
|
|
|
|
fsp := 0
|
|
if idx := strings.Index(expr, "."); idx != -1 {
|
|
fsp = len(expr) - idx - 1
|
|
}
|
|
|
|
var tmpFsp int8
|
|
if tmpFsp, err = types.CheckFsp(fsp); err != nil {
|
|
return res, isNull, err
|
|
}
|
|
fsp = int(tmpFsp)
|
|
|
|
sc := b.ctx.GetSessionVars().StmtCtx
|
|
res, err = types.ParseDuration(sc, expr, int8(fsp))
|
|
if types.ErrTruncatedWrongVal.Equal(err) {
|
|
err = sc.HandleTruncate(err)
|
|
}
|
|
return res, isNull, err
|
|
}
|
|
|
|
type timeLiteralFunctionClass struct {
|
|
baseFunctionClass
|
|
}
|
|
|
|
func (c *timeLiteralFunctionClass) getFunction(ctx sessionctx.Context, args []Expression) (builtinFunc, error) {
|
|
if err := c.verifyArgs(args); err != nil {
|
|
return nil, err
|
|
}
|
|
con, ok := args[0].(*Constant)
|
|
if !ok {
|
|
panic("Unexpected parameter for time literal")
|
|
}
|
|
dt, err := con.Eval(chunk.Row{})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
str := dt.GetString()
|
|
if !isDuration(str) {
|
|
return nil, types.ErrWrongValue.GenWithStackByArgs(types.DateTimeStr, str)
|
|
}
|
|
duration, err := types.ParseDuration(ctx.GetSessionVars().StmtCtx, str, types.GetFsp(str))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, []Expression{}, types.ETDuration)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
bf.tp.Flen, bf.tp.Decimal = 10, int(duration.Fsp)
|
|
if int(duration.Fsp) > 0 {
|
|
bf.tp.Flen += 1 + int(duration.Fsp)
|
|
}
|
|
sig := &builtinTimeLiteralSig{bf, duration}
|
|
return sig, nil
|
|
}
|
|
|
|
type builtinTimeLiteralSig struct {
|
|
baseBuiltinFunc
|
|
duration types.Duration
|
|
}
|
|
|
|
func (b *builtinTimeLiteralSig) Clone() builtinFunc {
|
|
newSig := &builtinTimeLiteralSig{duration: b.duration}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
// evalDuration evals TIME 'stringLit'.
|
|
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-literals.html
|
|
func (b *builtinTimeLiteralSig) evalDuration(row chunk.Row) (types.Duration, bool, error) {
|
|
return b.duration, false, nil
|
|
}
|
|
|
|
type utcDateFunctionClass struct {
|
|
baseFunctionClass
|
|
}
|
|
|
|
func (c *utcDateFunctionClass) getFunction(ctx sessionctx.Context, args []Expression) (builtinFunc, error) {
|
|
if err := c.verifyArgs(args); err != nil {
|
|
return nil, err
|
|
}
|
|
bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETDatetime)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
bf.tp.Flen, bf.tp.Decimal = 10, 0
|
|
sig := &builtinUTCDateSig{bf}
|
|
return sig, nil
|
|
}
|
|
|
|
type builtinUTCDateSig struct {
|
|
baseBuiltinFunc
|
|
}
|
|
|
|
func (b *builtinUTCDateSig) Clone() builtinFunc {
|
|
newSig := &builtinUTCDateSig{}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
// evalTime evals UTC_DATE, UTC_DATE().
|
|
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_utc-date
|
|
func (b *builtinUTCDateSig) evalTime(row chunk.Row) (types.Time, bool, error) {
|
|
nowTs, err := getStmtTimestamp(b.ctx)
|
|
if err != nil {
|
|
return types.ZeroTime, true, err
|
|
}
|
|
year, month, day := nowTs.UTC().Date()
|
|
result := types.NewTime(types.FromGoTime(time.Date(year, month, day, 0, 0, 0, 0, time.UTC)), mysql.TypeDate, types.UnspecifiedFsp)
|
|
return result, false, nil
|
|
}
|
|
|
|
type utcTimestampFunctionClass struct {
|
|
baseFunctionClass
|
|
}
|
|
|
|
func getFlenAndDecimal4UTCTimestampAndNow(ctx sessionctx.Context, arg Expression) (flen, decimal int) {
|
|
if constant, ok := arg.(*Constant); ok {
|
|
fsp, isNull, err := constant.EvalInt(ctx, chunk.Row{})
|
|
if isNull || err != nil || fsp > int64(types.MaxFsp) {
|
|
decimal = int(types.MaxFsp)
|
|
} else if fsp < int64(types.MinFsp) {
|
|
decimal = int(types.MinFsp)
|
|
} else {
|
|
decimal = int(fsp)
|
|
}
|
|
}
|
|
if decimal > 0 {
|
|
flen = 19 + 1 + decimal
|
|
} else {
|
|
flen = 19
|
|
}
|
|
return flen, decimal
|
|
}
|
|
|
|
func (c *utcTimestampFunctionClass) getFunction(ctx sessionctx.Context, args []Expression) (builtinFunc, error) {
|
|
if err := c.verifyArgs(args); err != nil {
|
|
return nil, err
|
|
}
|
|
argTps := make([]types.EvalType, 0, 1)
|
|
if len(args) == 1 {
|
|
argTps = append(argTps, types.ETInt)
|
|
}
|
|
bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETDatetime, argTps...)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if len(args) == 1 {
|
|
bf.tp.Flen, bf.tp.Decimal = getFlenAndDecimal4UTCTimestampAndNow(bf.ctx, args[0])
|
|
} else {
|
|
bf.tp.Flen, bf.tp.Decimal = 19, 0
|
|
}
|
|
|
|
var sig builtinFunc
|
|
if len(args) == 1 {
|
|
sig = &builtinUTCTimestampWithArgSig{bf}
|
|
sig.setPbCode(tipb.ScalarFuncSig_UTCTimestampWithArg)
|
|
} else {
|
|
sig = &builtinUTCTimestampWithoutArgSig{bf}
|
|
sig.setPbCode(tipb.ScalarFuncSig_UTCTimestampWithoutArg)
|
|
}
|
|
return sig, nil
|
|
}
|
|
|
|
func evalUTCTimestampWithFsp(ctx sessionctx.Context, fsp int8) (types.Time, bool, error) {
|
|
nowTs, err := getStmtTimestamp(ctx)
|
|
if err != nil {
|
|
return types.ZeroTime, true, err
|
|
}
|
|
result, err := convertTimeToMysqlTime(nowTs.UTC(), fsp, types.ModeHalfEven)
|
|
if err != nil {
|
|
return types.ZeroTime, true, err
|
|
}
|
|
return result, false, nil
|
|
}
|
|
|
|
type builtinUTCTimestampWithArgSig struct {
|
|
baseBuiltinFunc
|
|
}
|
|
|
|
func (b *builtinUTCTimestampWithArgSig) Clone() builtinFunc {
|
|
newSig := &builtinUTCTimestampWithArgSig{}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
// evalTime evals UTC_TIMESTAMP(fsp).
|
|
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_utc-timestamp
|
|
func (b *builtinUTCTimestampWithArgSig) evalTime(row chunk.Row) (types.Time, bool, error) {
|
|
num, isNull, err := b.args[0].EvalInt(b.ctx, row)
|
|
if err != nil {
|
|
return types.ZeroTime, true, err
|
|
}
|
|
|
|
if !isNull && num > int64(types.MaxFsp) {
|
|
return types.ZeroTime, true, errors.Errorf("Too-big precision %v specified for 'utc_timestamp'. Maximum is %v.", num, types.MaxFsp)
|
|
}
|
|
if !isNull && num < int64(types.MinFsp) {
|
|
return types.ZeroTime, true, errors.Errorf("Invalid negative %d specified, must in [0, 6].", num)
|
|
}
|
|
|
|
result, isNull, err := evalUTCTimestampWithFsp(b.ctx, int8(num))
|
|
return result, isNull, err
|
|
}
|
|
|
|
type builtinUTCTimestampWithoutArgSig struct {
|
|
baseBuiltinFunc
|
|
}
|
|
|
|
func (b *builtinUTCTimestampWithoutArgSig) Clone() builtinFunc {
|
|
newSig := &builtinUTCTimestampWithoutArgSig{}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
// evalTime evals UTC_TIMESTAMP().
|
|
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_utc-timestamp
|
|
func (b *builtinUTCTimestampWithoutArgSig) evalTime(row chunk.Row) (types.Time, bool, error) {
|
|
result, isNull, err := evalUTCTimestampWithFsp(b.ctx, int8(0))
|
|
return result, isNull, err
|
|
}
|
|
|
|
type nowFunctionClass struct {
|
|
baseFunctionClass
|
|
}
|
|
|
|
func (c *nowFunctionClass) getFunction(ctx sessionctx.Context, args []Expression) (builtinFunc, error) {
|
|
if err := c.verifyArgs(args); err != nil {
|
|
return nil, err
|
|
}
|
|
argTps := make([]types.EvalType, 0, 1)
|
|
if len(args) == 1 {
|
|
argTps = append(argTps, types.ETInt)
|
|
}
|
|
bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETDatetime, argTps...)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if len(args) == 1 {
|
|
bf.tp.Flen, bf.tp.Decimal = getFlenAndDecimal4UTCTimestampAndNow(bf.ctx, args[0])
|
|
} else {
|
|
bf.tp.Flen, bf.tp.Decimal = 19, 0
|
|
}
|
|
|
|
var sig builtinFunc
|
|
if len(args) == 1 {
|
|
sig = &builtinNowWithArgSig{bf}
|
|
sig.setPbCode(tipb.ScalarFuncSig_NowWithArg)
|
|
} else {
|
|
sig = &builtinNowWithoutArgSig{bf}
|
|
sig.setPbCode(tipb.ScalarFuncSig_NowWithoutArg)
|
|
}
|
|
return sig, nil
|
|
}
|
|
|
|
func evalNowWithFsp(ctx sessionctx.Context, fsp int8) (types.Time, bool, error) {
|
|
nowTs, err := getStmtTimestamp(ctx)
|
|
if err != nil {
|
|
return types.ZeroTime, true, err
|
|
}
|
|
|
|
// In MySQL's implementation, now() will truncate the result instead of rounding it.
|
|
// Results below are from MySQL 5.7, which can prove it.
|
|
// mysql> select now(6), now(3), now();
|
|
// +----------------------------+-------------------------+---------------------+
|
|
// | now(6) | now(3) | now() |
|
|
// +----------------------------+-------------------------+---------------------+
|
|
// | 2019-03-25 15:57:56.612966 | 2019-03-25 15:57:56.612 | 2019-03-25 15:57:56 |
|
|
// +----------------------------+-------------------------+---------------------+
|
|
result, err := convertTimeToMysqlTime(nowTs, fsp, types.ModeTruncate)
|
|
if err != nil {
|
|
return types.ZeroTime, true, err
|
|
}
|
|
|
|
err = result.ConvertTimeZone(time.Local, ctx.GetSessionVars().Location())
|
|
if err != nil {
|
|
return types.ZeroTime, true, err
|
|
}
|
|
|
|
return result, false, nil
|
|
}
|
|
|
|
type builtinNowWithArgSig struct {
|
|
baseBuiltinFunc
|
|
}
|
|
|
|
func (b *builtinNowWithArgSig) Clone() builtinFunc {
|
|
newSig := &builtinNowWithArgSig{}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
// evalTime evals NOW(fsp)
|
|
// see: https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_now
|
|
func (b *builtinNowWithArgSig) evalTime(row chunk.Row) (types.Time, bool, error) {
|
|
fsp, isNull, err := b.args[0].EvalInt(b.ctx, row)
|
|
|
|
if err != nil {
|
|
return types.ZeroTime, true, err
|
|
}
|
|
|
|
if isNull {
|
|
fsp = 0
|
|
} else if fsp > int64(types.MaxFsp) {
|
|
return types.ZeroTime, true, errors.Errorf("Too-big precision %v specified for 'now'. Maximum is %v.", fsp, types.MaxFsp)
|
|
} else if fsp < int64(types.MinFsp) {
|
|
return types.ZeroTime, true, errors.Errorf("Invalid negative %d specified, must in [0, 6].", fsp)
|
|
}
|
|
|
|
result, isNull, err := evalNowWithFsp(b.ctx, int8(fsp))
|
|
return result, isNull, err
|
|
}
|
|
|
|
type builtinNowWithoutArgSig struct {
|
|
baseBuiltinFunc
|
|
}
|
|
|
|
func (b *builtinNowWithoutArgSig) Clone() builtinFunc {
|
|
newSig := &builtinNowWithoutArgSig{}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
// evalTime evals NOW()
|
|
// see: https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_now
|
|
func (b *builtinNowWithoutArgSig) evalTime(row chunk.Row) (types.Time, bool, error) {
|
|
result, isNull, err := evalNowWithFsp(b.ctx, int8(0))
|
|
return result, isNull, err
|
|
}
|
|
|
|
type extractFunctionClass struct {
|
|
baseFunctionClass
|
|
}
|
|
|
|
func (c *extractFunctionClass) getFunction(ctx sessionctx.Context, args []Expression) (sig builtinFunc, err error) {
|
|
if err = c.verifyArgs(args); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
datetimeUnits := map[string]struct{}{
|
|
"DAY": {},
|
|
"WEEK": {},
|
|
"MONTH": {},
|
|
"QUARTER": {},
|
|
"YEAR": {},
|
|
"DAY_MICROSECOND": {},
|
|
"DAY_SECOND": {},
|
|
"DAY_MINUTE": {},
|
|
"DAY_HOUR": {},
|
|
"YEAR_MONTH": {},
|
|
}
|
|
isDatetimeUnit := true
|
|
args[0] = WrapWithCastAsString(ctx, args[0])
|
|
if _, isCon := args[0].(*Constant); isCon {
|
|
unit, _, err1 := args[0].EvalString(ctx, chunk.Row{})
|
|
if err1 != nil {
|
|
return nil, err1
|
|
}
|
|
_, isDatetimeUnit = datetimeUnits[unit]
|
|
}
|
|
var bf baseBuiltinFunc
|
|
if isDatetimeUnit {
|
|
bf, err = newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETInt, types.ETString, types.ETDatetime)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
sig = &builtinExtractDatetimeSig{bf}
|
|
sig.setPbCode(tipb.ScalarFuncSig_ExtractDatetime)
|
|
} else {
|
|
bf, err = newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETInt, types.ETString, types.ETDuration)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
sig = &builtinExtractDurationSig{bf}
|
|
sig.setPbCode(tipb.ScalarFuncSig_ExtractDuration)
|
|
}
|
|
return sig, nil
|
|
}
|
|
|
|
type builtinExtractDatetimeSig struct {
|
|
baseBuiltinFunc
|
|
}
|
|
|
|
func (b *builtinExtractDatetimeSig) Clone() builtinFunc {
|
|
newSig := &builtinExtractDatetimeSig{}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
// evalInt evals a builtinExtractDatetimeSig.
|
|
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_extract
|
|
func (b *builtinExtractDatetimeSig) evalInt(row chunk.Row) (int64, bool, error) {
|
|
unit, isNull, err := b.args[0].EvalString(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return 0, isNull, err
|
|
}
|
|
dt, isNull, err := b.args[1].EvalTime(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return 0, isNull, err
|
|
}
|
|
res, err := types.ExtractDatetimeNum(&dt, unit)
|
|
return res, err != nil, err
|
|
}
|
|
|
|
type builtinExtractDurationSig struct {
|
|
baseBuiltinFunc
|
|
}
|
|
|
|
func (b *builtinExtractDurationSig) Clone() builtinFunc {
|
|
newSig := &builtinExtractDurationSig{}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
// evalInt evals a builtinExtractDurationSig.
|
|
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_extract
|
|
func (b *builtinExtractDurationSig) evalInt(row chunk.Row) (int64, bool, error) {
|
|
unit, isNull, err := b.args[0].EvalString(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return 0, isNull, err
|
|
}
|
|
dur, isNull, err := b.args[1].EvalDuration(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return 0, isNull, err
|
|
}
|
|
res, err := types.ExtractDurationNum(&dur, unit)
|
|
return res, err != nil, err
|
|
}
|
|
|
|
// baseDateArithmitical is the base class for all "builtinAddDateXXXSig" and "builtinSubDateXXXSig",
|
|
// which provides parameter getter and date arithmetical calculate functions.
|
|
type baseDateArithmitical struct {
|
|
// intervalRegexp is "*Regexp" used to extract string interval for "DAY" unit.
|
|
intervalRegexp *regexp.Regexp
|
|
}
|
|
|
|
func newDateArighmeticalUtil() baseDateArithmitical {
|
|
return baseDateArithmitical{
|
|
intervalRegexp: regexp.MustCompile(`-?[\d]+`),
|
|
}
|
|
}
|
|
|
|
func (du *baseDateArithmitical) getDateFromString(ctx sessionctx.Context, args []Expression, row chunk.Row, unit string) (types.Time, bool, error) {
|
|
dateStr, isNull, err := args[0].EvalString(ctx, row)
|
|
if isNull || err != nil {
|
|
return types.ZeroTime, true, err
|
|
}
|
|
|
|
dateTp := mysql.TypeDate
|
|
if !types.IsDateFormat(dateStr) || types.IsClockUnit(unit) {
|
|
dateTp = mysql.TypeDatetime
|
|
}
|
|
|
|
sc := ctx.GetSessionVars().StmtCtx
|
|
date, err := types.ParseTime(sc, dateStr, dateTp, types.MaxFsp)
|
|
return date, err != nil, handleInvalidTimeError(ctx, err)
|
|
}
|
|
|
|
func (du *baseDateArithmitical) getDateFromInt(ctx sessionctx.Context, args []Expression, row chunk.Row, unit string) (types.Time, bool, error) {
|
|
dateInt, isNull, err := args[0].EvalInt(ctx, row)
|
|
if isNull || err != nil {
|
|
return types.ZeroTime, true, err
|
|
}
|
|
|
|
sc := ctx.GetSessionVars().StmtCtx
|
|
date, err := types.ParseTimeFromInt64(sc, dateInt)
|
|
if err != nil {
|
|
return types.ZeroTime, true, handleInvalidTimeError(ctx, err)
|
|
}
|
|
|
|
dateTp := mysql.TypeDate
|
|
if date.Type() == mysql.TypeDatetime || date.Type() == mysql.TypeTimestamp || types.IsClockUnit(unit) {
|
|
dateTp = mysql.TypeDatetime
|
|
}
|
|
date.SetType(dateTp)
|
|
return date, false, nil
|
|
}
|
|
|
|
func (du *baseDateArithmitical) getDateFromDatetime(ctx sessionctx.Context, args []Expression, row chunk.Row, unit string) (types.Time, bool, error) {
|
|
date, isNull, err := args[0].EvalTime(ctx, row)
|
|
if isNull || err != nil {
|
|
return types.ZeroTime, true, err
|
|
}
|
|
|
|
if types.IsClockUnit(unit) {
|
|
date.SetType(mysql.TypeDatetime)
|
|
}
|
|
return date, false, nil
|
|
}
|
|
|
|
func (du *baseDateArithmitical) getIntervalFromString(ctx sessionctx.Context, args []Expression, row chunk.Row, unit string) (string, bool, error) {
|
|
interval, isNull, err := args[1].EvalString(ctx, row)
|
|
if isNull || err != nil {
|
|
return "", true, err
|
|
}
|
|
// unit "DAY" and "HOUR" has to be specially handled.
|
|
if toLower := strings.ToLower(unit); toLower == "day" || toLower == "hour" {
|
|
if strings.ToLower(interval) == "true" {
|
|
interval = "1"
|
|
} else if strings.ToLower(interval) == "false" {
|
|
interval = "0"
|
|
} else {
|
|
interval = du.intervalRegexp.FindString(interval)
|
|
}
|
|
}
|
|
return interval, false, nil
|
|
}
|
|
|
|
func (du *baseDateArithmitical) getIntervalFromDecimal(ctx sessionctx.Context, args []Expression, row chunk.Row, unit string) (string, bool, error) {
|
|
decimal, isNull, err := args[1].EvalDecimal(ctx, row)
|
|
if isNull || err != nil {
|
|
return "", true, err
|
|
}
|
|
interval := decimal.String()
|
|
|
|
switch strings.ToUpper(unit) {
|
|
case "HOUR_MINUTE", "MINUTE_SECOND", "YEAR_MONTH", "DAY_HOUR", "DAY_MINUTE",
|
|
"DAY_SECOND", "DAY_MICROSECOND", "HOUR_MICROSECOND", "HOUR_SECOND", "MINUTE_MICROSECOND", "SECOND_MICROSECOND":
|
|
neg := false
|
|
if interval != "" && interval[0] == '-' {
|
|
neg = true
|
|
interval = interval[1:]
|
|
}
|
|
switch strings.ToUpper(unit) {
|
|
case "HOUR_MINUTE", "MINUTE_SECOND":
|
|
interval = strings.Replace(interval, ".", ":", -1)
|
|
case "YEAR_MONTH":
|
|
interval = strings.Replace(interval, ".", "-", -1)
|
|
case "DAY_HOUR":
|
|
interval = strings.Replace(interval, ".", " ", -1)
|
|
case "DAY_MINUTE":
|
|
interval = "0 " + strings.Replace(interval, ".", ":", -1)
|
|
case "DAY_SECOND":
|
|
interval = "0 00:" + strings.Replace(interval, ".", ":", -1)
|
|
case "DAY_MICROSECOND":
|
|
interval = "0 00:00:" + interval
|
|
case "HOUR_MICROSECOND":
|
|
interval = "00:00:" + interval
|
|
case "HOUR_SECOND":
|
|
interval = "00:" + strings.Replace(interval, ".", ":", -1)
|
|
case "MINUTE_MICROSECOND":
|
|
interval = "00:" + interval
|
|
case "SECOND_MICROSECOND":
|
|
/* keep interval as original decimal */
|
|
}
|
|
if neg {
|
|
interval = "-" + interval
|
|
}
|
|
case "SECOND":
|
|
// interval is already like the %f format.
|
|
default:
|
|
// YEAR, QUARTER, MONTH, WEEK, DAY, HOUR, MINUTE, MICROSECOND
|
|
castExpr := WrapWithCastAsString(ctx, WrapWithCastAsInt(ctx, args[1]))
|
|
interval, isNull, err = castExpr.EvalString(ctx, row)
|
|
if isNull || err != nil {
|
|
return "", true, err
|
|
}
|
|
}
|
|
|
|
return interval, false, nil
|
|
}
|
|
|
|
func (du *baseDateArithmitical) getIntervalFromInt(ctx sessionctx.Context, args []Expression, row chunk.Row, unit string) (string, bool, error) {
|
|
interval, isNull, err := args[1].EvalInt(ctx, row)
|
|
if isNull || err != nil {
|
|
return "", true, err
|
|
}
|
|
return strconv.FormatInt(interval, 10), false, nil
|
|
}
|
|
|
|
func (du *baseDateArithmitical) getIntervalFromReal(ctx sessionctx.Context, args []Expression, row chunk.Row, unit string) (string, bool, error) {
|
|
interval, isNull, err := args[1].EvalReal(ctx, row)
|
|
if isNull || err != nil {
|
|
return "", true, err
|
|
}
|
|
return strconv.FormatFloat(interval, 'f', args[1].GetType().Decimal, 64), false, nil
|
|
}
|
|
|
|
func (du *baseDateArithmitical) add(ctx sessionctx.Context, date types.Time, interval string, unit string) (types.Time, bool, error) {
|
|
year, month, day, nano, err := types.ParseDurationValue(unit, interval)
|
|
if err := handleInvalidTimeError(ctx, err); err != nil {
|
|
return types.ZeroTime, true, err
|
|
}
|
|
return du.addDate(ctx, date, year, month, day, nano)
|
|
}
|
|
|
|
func (du *baseDateArithmitical) addDate(ctx sessionctx.Context, date types.Time, year, month, day, nano int64) (types.Time, bool, error) {
|
|
goTime, err := date.GoTime(time.UTC)
|
|
if err := handleInvalidTimeError(ctx, err); err != nil {
|
|
return types.ZeroTime, true, err
|
|
}
|
|
|
|
goTime = goTime.Add(time.Duration(nano))
|
|
goTime = types.AddDate(year, month, day, goTime)
|
|
|
|
if goTime.Nanosecond() == 0 {
|
|
date.SetFsp(0)
|
|
} else {
|
|
date.SetFsp(6)
|
|
}
|
|
|
|
// fix https://github.com/pingcap/tidb/issues/11329
|
|
if goTime.Year() == 0 {
|
|
hour, minute, second := goTime.Clock()
|
|
date.SetCoreTime(types.FromDate(0, 0, 0, hour, minute, second, goTime.Nanosecond()/1000))
|
|
return date, false, nil
|
|
}
|
|
|
|
if goTime.Year() < 0 || goTime.Year() > 9999 {
|
|
return types.ZeroTime, true, handleInvalidTimeError(ctx, types.ErrDatetimeFunctionOverflow.GenWithStackByArgs("datetime"))
|
|
}
|
|
|
|
date.SetCoreTime(types.FromGoTime(goTime))
|
|
overflow, err := types.DateTimeIsOverflow(ctx.GetSessionVars().StmtCtx, date)
|
|
if err := handleInvalidTimeError(ctx, err); err != nil {
|
|
return types.ZeroTime, true, err
|
|
}
|
|
if overflow {
|
|
return types.ZeroTime, true, handleInvalidTimeError(ctx, types.ErrDatetimeFunctionOverflow.GenWithStackByArgs("datetime"))
|
|
}
|
|
return date, false, nil
|
|
}
|
|
|
|
func (du *baseDateArithmitical) addDuration(ctx sessionctx.Context, d types.Duration, interval string, unit string) (types.Duration, bool, error) {
|
|
dur, err := types.ExtractDurationValue(unit, interval)
|
|
if err != nil {
|
|
return types.ZeroDuration, true, handleInvalidTimeError(ctx, err)
|
|
}
|
|
retDur, err := d.Add(dur)
|
|
if err != nil {
|
|
return types.ZeroDuration, true, err
|
|
}
|
|
return retDur, false, nil
|
|
}
|
|
|
|
func (du *baseDateArithmitical) subDuration(ctx sessionctx.Context, d types.Duration, interval string, unit string) (types.Duration, bool, error) {
|
|
dur, err := types.ExtractDurationValue(unit, interval)
|
|
if err != nil {
|
|
return types.ZeroDuration, true, handleInvalidTimeError(ctx, err)
|
|
}
|
|
retDur, err := d.Sub(dur)
|
|
if err != nil {
|
|
return types.ZeroDuration, true, err
|
|
}
|
|
return retDur, false, nil
|
|
}
|
|
|
|
func (du *baseDateArithmitical) sub(ctx sessionctx.Context, date types.Time, interval string, unit string) (types.Time, bool, error) {
|
|
year, month, day, nano, err := types.ParseDurationValue(unit, interval)
|
|
if err := handleInvalidTimeError(ctx, err); err != nil {
|
|
return types.ZeroTime, true, err
|
|
}
|
|
return du.addDate(ctx, date, -year, -month, -day, -nano)
|
|
}
|
|
|
|
func (du *baseDateArithmitical) vecGetDateFromInt(b *baseBuiltinFunc, input *chunk.Chunk, unit string, result *chunk.Column) error {
|
|
n := input.NumRows()
|
|
buf, err := b.bufAllocator.get(types.ETInt, n)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer b.bufAllocator.put(buf)
|
|
if err := b.args[0].VecEvalInt(b.ctx, input, buf); err != nil {
|
|
return err
|
|
}
|
|
|
|
result.ResizeTime(n, false)
|
|
result.MergeNulls(buf)
|
|
dates := result.Times()
|
|
i64s := buf.Int64s()
|
|
sc := b.ctx.GetSessionVars().StmtCtx
|
|
isClockUnit := types.IsClockUnit(unit)
|
|
for i := 0; i < n; i++ {
|
|
if result.IsNull(i) {
|
|
continue
|
|
}
|
|
|
|
date, err := types.ParseTimeFromInt64(sc, i64s[i])
|
|
if err != nil {
|
|
err = handleInvalidTimeError(b.ctx, err)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
result.SetNull(i, true)
|
|
continue
|
|
}
|
|
|
|
dateTp := mysql.TypeDate
|
|
if date.Type() == mysql.TypeDatetime || date.Type() == mysql.TypeTimestamp || isClockUnit {
|
|
dateTp = mysql.TypeDatetime
|
|
}
|
|
date.SetType(dateTp)
|
|
dates[i] = date
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (du *baseDateArithmitical) vecGetDateFromString(b *baseBuiltinFunc, input *chunk.Chunk, unit string, result *chunk.Column) error {
|
|
n := input.NumRows()
|
|
buf, err := b.bufAllocator.get(types.ETString, n)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer b.bufAllocator.put(buf)
|
|
if err := b.args[0].VecEvalString(b.ctx, input, buf); err != nil {
|
|
return err
|
|
}
|
|
|
|
result.ResizeTime(n, false)
|
|
result.MergeNulls(buf)
|
|
dates := result.Times()
|
|
sc := b.ctx.GetSessionVars().StmtCtx
|
|
isClockUnit := types.IsClockUnit(unit)
|
|
for i := 0; i < n; i++ {
|
|
if result.IsNull(i) {
|
|
continue
|
|
}
|
|
|
|
dateStr := buf.GetString(i)
|
|
dateTp := mysql.TypeDate
|
|
if !types.IsDateFormat(dateStr) || isClockUnit {
|
|
dateTp = mysql.TypeDatetime
|
|
}
|
|
|
|
date, err := types.ParseTime(sc, dateStr, dateTp, types.MaxFsp)
|
|
if err != nil {
|
|
err = handleInvalidTimeError(b.ctx, err)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
result.SetNull(i, true)
|
|
} else {
|
|
dates[i] = date
|
|
}
|
|
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (du *baseDateArithmitical) vecGetDateFromDatetime(b *baseBuiltinFunc, input *chunk.Chunk, unit string, result *chunk.Column) error {
|
|
n := input.NumRows()
|
|
result.ResizeTime(n, false)
|
|
if err := b.args[0].VecEvalTime(b.ctx, input, result); err != nil {
|
|
return err
|
|
}
|
|
|
|
dates := result.Times()
|
|
isClockUnit := types.IsClockUnit(unit)
|
|
for i := 0; i < n; i++ {
|
|
if result.IsNull(i) {
|
|
continue
|
|
}
|
|
|
|
if isClockUnit {
|
|
dates[i].SetType(mysql.TypeDatetime)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (du *baseDateArithmitical) vecGetIntervalFromString(b *baseBuiltinFunc, input *chunk.Chunk, unit string, result *chunk.Column) error {
|
|
n := input.NumRows()
|
|
buf, err := b.bufAllocator.get(types.ETString, n)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer b.bufAllocator.put(buf)
|
|
if err := b.args[1].VecEvalString(b.ctx, input, buf); err != nil {
|
|
return err
|
|
}
|
|
|
|
amendInterval := func(val string) string {
|
|
return val
|
|
}
|
|
if unitLower := strings.ToLower(unit); unitLower == "day" || unitLower == "hour" {
|
|
amendInterval = func(val string) string {
|
|
if intervalLower := strings.ToLower(val); intervalLower == "true" {
|
|
return "1"
|
|
} else if intervalLower == "false" {
|
|
return "0"
|
|
}
|
|
return du.intervalRegexp.FindString(val)
|
|
}
|
|
}
|
|
|
|
result.ReserveString(n)
|
|
for i := 0; i < n; i++ {
|
|
if buf.IsNull(i) {
|
|
result.AppendNull()
|
|
continue
|
|
}
|
|
|
|
result.AppendString(amendInterval(buf.GetString(i)))
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (du *baseDateArithmitical) vecGetIntervalFromDecimal(b *baseBuiltinFunc, input *chunk.Chunk, unit string, result *chunk.Column) error {
|
|
n := input.NumRows()
|
|
buf, err := b.bufAllocator.get(types.ETDecimal, n)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer b.bufAllocator.put(buf)
|
|
if err := b.args[1].VecEvalDecimal(b.ctx, input, buf); err != nil {
|
|
return err
|
|
}
|
|
|
|
isCompoundUnit := false
|
|
amendInterval := func(val string, row *chunk.Row) (string, bool, error) {
|
|
return val, false, nil
|
|
}
|
|
switch unitUpper := strings.ToUpper(unit); unitUpper {
|
|
case "HOUR_MINUTE", "MINUTE_SECOND", "YEAR_MONTH", "DAY_HOUR", "DAY_MINUTE",
|
|
"DAY_SECOND", "DAY_MICROSECOND", "HOUR_MICROSECOND", "HOUR_SECOND", "MINUTE_MICROSECOND", "SECOND_MICROSECOND":
|
|
isCompoundUnit = true
|
|
switch strings.ToUpper(unit) {
|
|
case "HOUR_MINUTE", "MINUTE_SECOND":
|
|
amendInterval = func(val string, _ *chunk.Row) (string, bool, error) {
|
|
return strings.Replace(val, ".", ":", -1), false, nil
|
|
}
|
|
case "YEAR_MONTH":
|
|
amendInterval = func(val string, _ *chunk.Row) (string, bool, error) {
|
|
return strings.Replace(val, ".", "-", -1), false, nil
|
|
}
|
|
case "DAY_HOUR":
|
|
amendInterval = func(val string, _ *chunk.Row) (string, bool, error) {
|
|
return strings.Replace(val, ".", " ", -1), false, nil
|
|
}
|
|
case "DAY_MINUTE":
|
|
amendInterval = func(val string, _ *chunk.Row) (string, bool, error) {
|
|
return "0 " + strings.Replace(val, ".", ":", -1), false, nil
|
|
}
|
|
case "DAY_SECOND":
|
|
amendInterval = func(val string, _ *chunk.Row) (string, bool, error) {
|
|
return "0 00:" + strings.Replace(val, ".", ":", -1), false, nil
|
|
}
|
|
case "DAY_MICROSECOND":
|
|
amendInterval = func(val string, _ *chunk.Row) (string, bool, error) {
|
|
return "0 00:00:" + val, false, nil
|
|
}
|
|
case "HOUR_MICROSECOND":
|
|
amendInterval = func(val string, _ *chunk.Row) (string, bool, error) {
|
|
return "00:00:" + val, false, nil
|
|
}
|
|
case "HOUR_SECOND":
|
|
amendInterval = func(val string, _ *chunk.Row) (string, bool, error) {
|
|
return "00:" + strings.Replace(val, ".", ":", -1), false, nil
|
|
}
|
|
case "MINUTE_MICROSECOND":
|
|
amendInterval = func(val string, _ *chunk.Row) (string, bool, error) {
|
|
return "00:" + val, false, nil
|
|
}
|
|
case "SECOND_MICROSECOND":
|
|
/* keep interval as original decimal */
|
|
}
|
|
case "SECOND":
|
|
/* keep interval as original decimal */
|
|
default:
|
|
// YEAR, QUARTER, MONTH, WEEK, DAY, HOUR, MINUTE, MICROSECOND
|
|
castExpr := WrapWithCastAsString(b.ctx, WrapWithCastAsInt(b.ctx, b.args[1]))
|
|
amendInterval = func(_ string, row *chunk.Row) (string, bool, error) {
|
|
interval, isNull, err := castExpr.EvalString(b.ctx, *row)
|
|
return interval, isNull || err != nil, err
|
|
}
|
|
}
|
|
|
|
result.ReserveString(n)
|
|
decs := buf.Decimals()
|
|
for i := 0; i < n; i++ {
|
|
if buf.IsNull(i) {
|
|
result.AppendNull()
|
|
continue
|
|
}
|
|
|
|
interval := decs[i].String()
|
|
row := input.GetRow(i)
|
|
isNeg := false
|
|
if isCompoundUnit && interval != "" && interval[0] == '-' {
|
|
isNeg = true
|
|
interval = interval[1:]
|
|
}
|
|
interval, isNull, err := amendInterval(interval, &row)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if isNull {
|
|
result.AppendNull()
|
|
continue
|
|
}
|
|
if isCompoundUnit && isNeg {
|
|
interval = "-" + interval
|
|
}
|
|
result.AppendString(interval)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (du *baseDateArithmitical) vecGetIntervalFromInt(b *baseBuiltinFunc, input *chunk.Chunk, unit string, result *chunk.Column) error {
|
|
n := input.NumRows()
|
|
buf, err := b.bufAllocator.get(types.ETInt, n)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer b.bufAllocator.put(buf)
|
|
if err := b.args[1].VecEvalInt(b.ctx, input, buf); err != nil {
|
|
return err
|
|
}
|
|
|
|
result.ReserveString(n)
|
|
i64s := buf.Int64s()
|
|
for i := 0; i < n; i++ {
|
|
if buf.IsNull(i) {
|
|
result.AppendNull()
|
|
} else {
|
|
result.AppendString(strconv.FormatInt(i64s[i], 10))
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (du *baseDateArithmitical) vecGetIntervalFromReal(b *baseBuiltinFunc, input *chunk.Chunk, unit string, result *chunk.Column) error {
|
|
n := input.NumRows()
|
|
buf, err := b.bufAllocator.get(types.ETReal, n)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer b.bufAllocator.put(buf)
|
|
if err := b.args[1].VecEvalReal(b.ctx, input, buf); err != nil {
|
|
return err
|
|
}
|
|
|
|
result.ReserveString(n)
|
|
f64s := buf.Float64s()
|
|
prec := b.args[1].GetType().Decimal
|
|
for i := 0; i < n; i++ {
|
|
if buf.IsNull(i) {
|
|
result.AppendNull()
|
|
} else {
|
|
result.AppendString(strconv.FormatFloat(f64s[i], 'f', prec, 64))
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
type addDateFunctionClass struct {
|
|
baseFunctionClass
|
|
}
|
|
|
|
func (c *addDateFunctionClass) getFunction(ctx sessionctx.Context, args []Expression) (sig builtinFunc, err error) {
|
|
if err = c.verifyArgs(args); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
dateEvalTp := args[0].GetType().EvalType()
|
|
if dateEvalTp != types.ETString && dateEvalTp != types.ETInt && dateEvalTp != types.ETDuration {
|
|
dateEvalTp = types.ETDatetime
|
|
}
|
|
|
|
intervalEvalTp := args[1].GetType().EvalType()
|
|
if intervalEvalTp != types.ETString && intervalEvalTp != types.ETDecimal && intervalEvalTp != types.ETReal {
|
|
intervalEvalTp = types.ETInt
|
|
}
|
|
|
|
argTps := []types.EvalType{dateEvalTp, intervalEvalTp, types.ETString}
|
|
var bf baseBuiltinFunc
|
|
if dateEvalTp == types.ETDuration {
|
|
unit, _, err := args[2].EvalString(ctx, chunk.Row{})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
internalFsp := 0
|
|
switch unit {
|
|
// If the unit has micro second, then the fsp must be the MaxFsp.
|
|
case "MICROSECOND", "SECOND_MICROSECOND", "MINUTE_MICROSECOND", "HOUR_MICROSECOND", "DAY_MICROSECOND":
|
|
internalFsp = int(types.MaxFsp)
|
|
// If the unit is second, the fsp is related with the arg[1]'s.
|
|
case "SECOND":
|
|
internalFsp = int(types.MaxFsp)
|
|
if intervalEvalTp != types.ETString {
|
|
internalFsp = mathutil.Min(args[1].GetType().Decimal, int(types.MaxFsp))
|
|
}
|
|
// Otherwise, the fsp should be 0.
|
|
}
|
|
bf, err = newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETDuration, argTps...)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
arg0Dec, err := getExpressionFsp(ctx, args[0])
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
bf.tp.Flen, bf.tp.Decimal = mysql.MaxDurationWidthWithFsp, mathutil.Max(arg0Dec, internalFsp)
|
|
} else {
|
|
bf, err = newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETDatetime, argTps...)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
bf.tp.Flen, bf.tp.Decimal = mysql.MaxDatetimeFullWidth, types.UnspecifiedLength
|
|
}
|
|
|
|
switch {
|
|
case dateEvalTp == types.ETString && intervalEvalTp == types.ETString:
|
|
sig = &builtinAddDateStringStringSig{
|
|
baseBuiltinFunc: bf,
|
|
baseDateArithmitical: newDateArighmeticalUtil(),
|
|
}
|
|
sig.setPbCode(tipb.ScalarFuncSig_AddDateStringString)
|
|
case dateEvalTp == types.ETString && intervalEvalTp == types.ETInt:
|
|
sig = &builtinAddDateStringIntSig{
|
|
baseBuiltinFunc: bf,
|
|
baseDateArithmitical: newDateArighmeticalUtil(),
|
|
}
|
|
sig.setPbCode(tipb.ScalarFuncSig_AddDateStringInt)
|
|
case dateEvalTp == types.ETString && intervalEvalTp == types.ETReal:
|
|
sig = &builtinAddDateStringRealSig{
|
|
baseBuiltinFunc: bf,
|
|
baseDateArithmitical: newDateArighmeticalUtil(),
|
|
}
|
|
sig.setPbCode(tipb.ScalarFuncSig_AddDateStringReal)
|
|
case dateEvalTp == types.ETString && intervalEvalTp == types.ETDecimal:
|
|
sig = &builtinAddDateStringDecimalSig{
|
|
baseBuiltinFunc: bf,
|
|
baseDateArithmitical: newDateArighmeticalUtil(),
|
|
}
|
|
sig.setPbCode(tipb.ScalarFuncSig_AddDateStringDecimal)
|
|
case dateEvalTp == types.ETInt && intervalEvalTp == types.ETString:
|
|
sig = &builtinAddDateIntStringSig{
|
|
baseBuiltinFunc: bf,
|
|
baseDateArithmitical: newDateArighmeticalUtil(),
|
|
}
|
|
sig.setPbCode(tipb.ScalarFuncSig_AddDateIntString)
|
|
case dateEvalTp == types.ETInt && intervalEvalTp == types.ETInt:
|
|
sig = &builtinAddDateIntIntSig{
|
|
baseBuiltinFunc: bf,
|
|
baseDateArithmitical: newDateArighmeticalUtil(),
|
|
}
|
|
sig.setPbCode(tipb.ScalarFuncSig_AddDateIntInt)
|
|
case dateEvalTp == types.ETInt && intervalEvalTp == types.ETReal:
|
|
sig = &builtinAddDateIntRealSig{
|
|
baseBuiltinFunc: bf,
|
|
baseDateArithmitical: newDateArighmeticalUtil(),
|
|
}
|
|
sig.setPbCode(tipb.ScalarFuncSig_AddDateIntReal)
|
|
case dateEvalTp == types.ETInt && intervalEvalTp == types.ETDecimal:
|
|
sig = &builtinAddDateIntDecimalSig{
|
|
baseBuiltinFunc: bf,
|
|
baseDateArithmitical: newDateArighmeticalUtil(),
|
|
}
|
|
sig.setPbCode(tipb.ScalarFuncSig_AddDateIntDecimal)
|
|
case dateEvalTp == types.ETDatetime && intervalEvalTp == types.ETString:
|
|
sig = &builtinAddDateDatetimeStringSig{
|
|
baseBuiltinFunc: bf,
|
|
baseDateArithmitical: newDateArighmeticalUtil(),
|
|
}
|
|
sig.setPbCode(tipb.ScalarFuncSig_AddDateDatetimeString)
|
|
case dateEvalTp == types.ETDatetime && intervalEvalTp == types.ETInt:
|
|
sig = &builtinAddDateDatetimeIntSig{
|
|
baseBuiltinFunc: bf,
|
|
baseDateArithmitical: newDateArighmeticalUtil(),
|
|
}
|
|
sig.setPbCode(tipb.ScalarFuncSig_AddDateDatetimeInt)
|
|
case dateEvalTp == types.ETDatetime && intervalEvalTp == types.ETReal:
|
|
sig = &builtinAddDateDatetimeRealSig{
|
|
baseBuiltinFunc: bf,
|
|
baseDateArithmitical: newDateArighmeticalUtil(),
|
|
}
|
|
sig.setPbCode(tipb.ScalarFuncSig_AddDateDatetimeReal)
|
|
case dateEvalTp == types.ETDatetime && intervalEvalTp == types.ETDecimal:
|
|
sig = &builtinAddDateDatetimeDecimalSig{
|
|
baseBuiltinFunc: bf,
|
|
baseDateArithmitical: newDateArighmeticalUtil(),
|
|
}
|
|
sig.setPbCode(tipb.ScalarFuncSig_AddDateDatetimeDecimal)
|
|
case dateEvalTp == types.ETDuration && intervalEvalTp == types.ETString:
|
|
sig = &builtinAddDateDurationStringSig{
|
|
baseBuiltinFunc: bf,
|
|
baseDateArithmitical: newDateArighmeticalUtil(),
|
|
}
|
|
sig.setPbCode(tipb.ScalarFuncSig_AddDateDurationString)
|
|
case dateEvalTp == types.ETDuration && intervalEvalTp == types.ETInt:
|
|
sig = &builtinAddDateDurationIntSig{
|
|
baseBuiltinFunc: bf,
|
|
baseDateArithmitical: newDateArighmeticalUtil(),
|
|
}
|
|
sig.setPbCode(tipb.ScalarFuncSig_AddDateDurationInt)
|
|
case dateEvalTp == types.ETDuration && intervalEvalTp == types.ETReal:
|
|
sig = &builtinAddDateDurationRealSig{
|
|
baseBuiltinFunc: bf,
|
|
baseDateArithmitical: newDateArighmeticalUtil(),
|
|
}
|
|
sig.setPbCode(tipb.ScalarFuncSig_AddDateDurationReal)
|
|
case dateEvalTp == types.ETDuration && intervalEvalTp == types.ETDecimal:
|
|
sig = &builtinAddDateDurationDecimalSig{
|
|
baseBuiltinFunc: bf,
|
|
baseDateArithmitical: newDateArighmeticalUtil(),
|
|
}
|
|
sig.setPbCode(tipb.ScalarFuncSig_AddDateDurationDecimal)
|
|
}
|
|
return sig, nil
|
|
}
|
|
|
|
type builtinAddDateStringStringSig struct {
|
|
baseBuiltinFunc
|
|
baseDateArithmitical
|
|
}
|
|
|
|
func (b *builtinAddDateStringStringSig) Clone() builtinFunc {
|
|
newSig := &builtinAddDateStringStringSig{baseDateArithmitical: b.baseDateArithmitical}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
// evalTime evals ADDDATE(date,INTERVAL expr unit).
|
|
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_adddate
|
|
func (b *builtinAddDateStringStringSig) evalTime(row chunk.Row) (types.Time, bool, error) {
|
|
unit, isNull, err := b.args[2].EvalString(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return types.ZeroTime, true, err
|
|
}
|
|
|
|
date, isNull, err := b.getDateFromString(b.ctx, b.args, row, unit)
|
|
if isNull || err != nil {
|
|
return types.ZeroTime, true, err
|
|
}
|
|
|
|
interval, isNull, err := b.getIntervalFromString(b.ctx, b.args, row, unit)
|
|
if isNull || err != nil {
|
|
return types.ZeroTime, true, err
|
|
}
|
|
|
|
result, isNull, err := b.add(b.ctx, date, interval, unit)
|
|
return result, isNull || err != nil, err
|
|
}
|
|
|
|
type builtinAddDateStringIntSig struct {
|
|
baseBuiltinFunc
|
|
baseDateArithmitical
|
|
}
|
|
|
|
func (b *builtinAddDateStringIntSig) Clone() builtinFunc {
|
|
newSig := &builtinAddDateStringIntSig{baseDateArithmitical: b.baseDateArithmitical}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
// evalTime evals ADDDATE(date,INTERVAL expr unit).
|
|
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_adddate
|
|
func (b *builtinAddDateStringIntSig) evalTime(row chunk.Row) (types.Time, bool, error) {
|
|
unit, isNull, err := b.args[2].EvalString(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return types.ZeroTime, true, err
|
|
}
|
|
|
|
date, isNull, err := b.getDateFromString(b.ctx, b.args, row, unit)
|
|
if isNull || err != nil {
|
|
return types.ZeroTime, true, err
|
|
}
|
|
|
|
interval, isNull, err := b.getIntervalFromInt(b.ctx, b.args, row, unit)
|
|
if isNull || err != nil {
|
|
return types.ZeroTime, true, err
|
|
}
|
|
|
|
result, isNull, err := b.add(b.ctx, date, interval, unit)
|
|
return result, isNull || err != nil, err
|
|
}
|
|
|
|
type builtinAddDateStringRealSig struct {
|
|
baseBuiltinFunc
|
|
baseDateArithmitical
|
|
}
|
|
|
|
func (b *builtinAddDateStringRealSig) Clone() builtinFunc {
|
|
newSig := &builtinAddDateStringRealSig{baseDateArithmitical: b.baseDateArithmitical}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
// evalTime evals ADDDATE(date,INTERVAL expr unit).
|
|
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_adddate
|
|
func (b *builtinAddDateStringRealSig) evalTime(row chunk.Row) (types.Time, bool, error) {
|
|
unit, isNull, err := b.args[2].EvalString(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return types.ZeroTime, true, err
|
|
}
|
|
|
|
date, isNull, err := b.getDateFromString(b.ctx, b.args, row, unit)
|
|
if isNull || err != nil {
|
|
return types.ZeroTime, true, err
|
|
}
|
|
|
|
interval, isNull, err := b.getIntervalFromReal(b.ctx, b.args, row, unit)
|
|
if isNull || err != nil {
|
|
return types.ZeroTime, true, err
|
|
}
|
|
|
|
result, isNull, err := b.add(b.ctx, date, interval, unit)
|
|
return result, isNull || err != nil, err
|
|
}
|
|
|
|
type builtinAddDateStringDecimalSig struct {
|
|
baseBuiltinFunc
|
|
baseDateArithmitical
|
|
}
|
|
|
|
func (b *builtinAddDateStringDecimalSig) Clone() builtinFunc {
|
|
newSig := &builtinAddDateStringDecimalSig{baseDateArithmitical: b.baseDateArithmitical}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
// evalTime evals ADDDATE(date,INTERVAL expr unit).
|
|
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_adddate
|
|
func (b *builtinAddDateStringDecimalSig) evalTime(row chunk.Row) (types.Time, bool, error) {
|
|
unit, isNull, err := b.args[2].EvalString(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return types.ZeroTime, true, err
|
|
}
|
|
|
|
date, isNull, err := b.getDateFromString(b.ctx, b.args, row, unit)
|
|
if isNull || err != nil {
|
|
return types.ZeroTime, true, err
|
|
}
|
|
|
|
interval, isNull, err := b.getIntervalFromDecimal(b.ctx, b.args, row, unit)
|
|
if isNull || err != nil {
|
|
return types.ZeroTime, true, err
|
|
}
|
|
|
|
result, isNull, err := b.add(b.ctx, date, interval, unit)
|
|
return result, isNull || err != nil, err
|
|
}
|
|
|
|
type builtinAddDateIntStringSig struct {
|
|
baseBuiltinFunc
|
|
baseDateArithmitical
|
|
}
|
|
|
|
func (b *builtinAddDateIntStringSig) Clone() builtinFunc {
|
|
newSig := &builtinAddDateIntStringSig{baseDateArithmitical: b.baseDateArithmitical}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
// evalTime evals ADDDATE(date,INTERVAL expr unit).
|
|
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_adddate
|
|
func (b *builtinAddDateIntStringSig) evalTime(row chunk.Row) (types.Time, bool, error) {
|
|
unit, isNull, err := b.args[2].EvalString(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return types.ZeroTime, true, err
|
|
}
|
|
|
|
date, isNull, err := b.getDateFromInt(b.ctx, b.args, row, unit)
|
|
if isNull || err != nil {
|
|
return types.ZeroTime, true, err
|
|
}
|
|
|
|
interval, isNull, err := b.getIntervalFromString(b.ctx, b.args, row, unit)
|
|
if isNull || err != nil {
|
|
return types.ZeroTime, true, err
|
|
}
|
|
|
|
result, isNull, err := b.add(b.ctx, date, interval, unit)
|
|
return result, isNull || err != nil, err
|
|
}
|
|
|
|
type builtinAddDateIntIntSig struct {
|
|
baseBuiltinFunc
|
|
baseDateArithmitical
|
|
}
|
|
|
|
func (b *builtinAddDateIntIntSig) Clone() builtinFunc {
|
|
newSig := &builtinAddDateIntIntSig{baseDateArithmitical: b.baseDateArithmitical}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
// evalTime evals ADDDATE(date,INTERVAL expr unit).
|
|
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_adddate
|
|
func (b *builtinAddDateIntIntSig) evalTime(row chunk.Row) (types.Time, bool, error) {
|
|
unit, isNull, err := b.args[2].EvalString(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return types.ZeroTime, true, err
|
|
}
|
|
|
|
date, isNull, err := b.getDateFromInt(b.ctx, b.args, row, unit)
|
|
if isNull || err != nil {
|
|
return types.ZeroTime, true, err
|
|
}
|
|
|
|
interval, isNull, err := b.getIntervalFromInt(b.ctx, b.args, row, unit)
|
|
if isNull || err != nil {
|
|
return types.ZeroTime, true, err
|
|
}
|
|
|
|
result, isNull, err := b.add(b.ctx, date, interval, unit)
|
|
return result, isNull || err != nil, err
|
|
}
|
|
|
|
type builtinAddDateIntRealSig struct {
|
|
baseBuiltinFunc
|
|
baseDateArithmitical
|
|
}
|
|
|
|
func (b *builtinAddDateIntRealSig) Clone() builtinFunc {
|
|
newSig := &builtinAddDateIntRealSig{baseDateArithmitical: b.baseDateArithmitical}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
// evalTime evals ADDDATE(date,INTERVAL expr unit).
|
|
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_adddate
|
|
func (b *builtinAddDateIntRealSig) evalTime(row chunk.Row) (types.Time, bool, error) {
|
|
unit, isNull, err := b.args[2].EvalString(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return types.ZeroTime, true, err
|
|
}
|
|
|
|
date, isNull, err := b.getDateFromInt(b.ctx, b.args, row, unit)
|
|
if isNull || err != nil {
|
|
return types.ZeroTime, true, err
|
|
}
|
|
|
|
interval, isNull, err := b.getIntervalFromReal(b.ctx, b.args, row, unit)
|
|
if isNull || err != nil {
|
|
return types.ZeroTime, true, err
|
|
}
|
|
|
|
result, isNull, err := b.add(b.ctx, date, interval, unit)
|
|
return result, isNull || err != nil, err
|
|
}
|
|
|
|
type builtinAddDateIntDecimalSig struct {
|
|
baseBuiltinFunc
|
|
baseDateArithmitical
|
|
}
|
|
|
|
func (b *builtinAddDateIntDecimalSig) Clone() builtinFunc {
|
|
newSig := &builtinAddDateIntDecimalSig{baseDateArithmitical: b.baseDateArithmitical}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
// evalTime evals ADDDATE(date,INTERVAL expr unit).
|
|
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_adddate
|
|
func (b *builtinAddDateIntDecimalSig) evalTime(row chunk.Row) (types.Time, bool, error) {
|
|
unit, isNull, err := b.args[2].EvalString(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return types.ZeroTime, true, err
|
|
}
|
|
|
|
date, isNull, err := b.getDateFromInt(b.ctx, b.args, row, unit)
|
|
if isNull || err != nil {
|
|
return types.ZeroTime, true, err
|
|
}
|
|
|
|
interval, isNull, err := b.getIntervalFromDecimal(b.ctx, b.args, row, unit)
|
|
if isNull || err != nil {
|
|
return types.ZeroTime, true, err
|
|
}
|
|
|
|
result, isNull, err := b.add(b.ctx, date, interval, unit)
|
|
return result, isNull || err != nil, err
|
|
}
|
|
|
|
type builtinAddDateDatetimeStringSig struct {
|
|
baseBuiltinFunc
|
|
baseDateArithmitical
|
|
}
|
|
|
|
func (b *builtinAddDateDatetimeStringSig) Clone() builtinFunc {
|
|
newSig := &builtinAddDateDatetimeStringSig{baseDateArithmitical: b.baseDateArithmitical}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
// evalTime evals ADDDATE(date,INTERVAL expr unit).
|
|
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_adddate
|
|
func (b *builtinAddDateDatetimeStringSig) evalTime(row chunk.Row) (types.Time, bool, error) {
|
|
unit, isNull, err := b.args[2].EvalString(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return types.ZeroTime, true, err
|
|
}
|
|
|
|
date, isNull, err := b.getDateFromDatetime(b.ctx, b.args, row, unit)
|
|
if isNull || err != nil {
|
|
return types.ZeroTime, true, err
|
|
}
|
|
|
|
interval, isNull, err := b.getIntervalFromString(b.ctx, b.args, row, unit)
|
|
if isNull || err != nil {
|
|
return types.ZeroTime, true, err
|
|
}
|
|
|
|
result, isNull, err := b.add(b.ctx, date, interval, unit)
|
|
return result, isNull || err != nil, err
|
|
}
|
|
|
|
type builtinAddDateDatetimeIntSig struct {
|
|
baseBuiltinFunc
|
|
baseDateArithmitical
|
|
}
|
|
|
|
func (b *builtinAddDateDatetimeIntSig) Clone() builtinFunc {
|
|
newSig := &builtinAddDateDatetimeIntSig{baseDateArithmitical: b.baseDateArithmitical}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
// evalTime evals ADDDATE(date,INTERVAL expr unit).
|
|
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_adddate
|
|
func (b *builtinAddDateDatetimeIntSig) evalTime(row chunk.Row) (types.Time, bool, error) {
|
|
unit, isNull, err := b.args[2].EvalString(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return types.ZeroTime, true, err
|
|
}
|
|
|
|
date, isNull, err := b.getDateFromDatetime(b.ctx, b.args, row, unit)
|
|
if isNull || err != nil {
|
|
return types.ZeroTime, true, err
|
|
}
|
|
|
|
interval, isNull, err := b.getIntervalFromInt(b.ctx, b.args, row, unit)
|
|
if isNull || err != nil {
|
|
return types.ZeroTime, true, err
|
|
}
|
|
|
|
result, isNull, err := b.add(b.ctx, date, interval, unit)
|
|
return result, isNull || err != nil, err
|
|
}
|
|
|
|
type builtinAddDateDatetimeRealSig struct {
|
|
baseBuiltinFunc
|
|
baseDateArithmitical
|
|
}
|
|
|
|
func (b *builtinAddDateDatetimeRealSig) Clone() builtinFunc {
|
|
newSig := &builtinAddDateDatetimeRealSig{baseDateArithmitical: b.baseDateArithmitical}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
// evalTime evals ADDDATE(date,INTERVAL expr unit).
|
|
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_adddate
|
|
func (b *builtinAddDateDatetimeRealSig) evalTime(row chunk.Row) (types.Time, bool, error) {
|
|
unit, isNull, err := b.args[2].EvalString(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return types.ZeroTime, true, err
|
|
}
|
|
|
|
date, isNull, err := b.getDateFromDatetime(b.ctx, b.args, row, unit)
|
|
if isNull || err != nil {
|
|
return types.ZeroTime, true, err
|
|
}
|
|
|
|
interval, isNull, err := b.getIntervalFromReal(b.ctx, b.args, row, unit)
|
|
if isNull || err != nil {
|
|
return types.ZeroTime, true, err
|
|
}
|
|
|
|
result, isNull, err := b.add(b.ctx, date, interval, unit)
|
|
return result, isNull || err != nil, err
|
|
}
|
|
|
|
type builtinAddDateDatetimeDecimalSig struct {
|
|
baseBuiltinFunc
|
|
baseDateArithmitical
|
|
}
|
|
|
|
func (b *builtinAddDateDatetimeDecimalSig) Clone() builtinFunc {
|
|
newSig := &builtinAddDateDatetimeDecimalSig{baseDateArithmitical: b.baseDateArithmitical}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
// evalTime evals ADDDATE(date,INTERVAL expr unit).
|
|
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_adddate
|
|
func (b *builtinAddDateDatetimeDecimalSig) evalTime(row chunk.Row) (types.Time, bool, error) {
|
|
unit, isNull, err := b.args[2].EvalString(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return types.ZeroTime, true, err
|
|
}
|
|
|
|
date, isNull, err := b.getDateFromDatetime(b.ctx, b.args, row, unit)
|
|
if isNull || err != nil {
|
|
return types.ZeroTime, true, err
|
|
}
|
|
|
|
interval, isNull, err := b.getIntervalFromDecimal(b.ctx, b.args, row, unit)
|
|
if isNull || err != nil {
|
|
return types.ZeroTime, true, err
|
|
}
|
|
|
|
result, isNull, err := b.add(b.ctx, date, interval, unit)
|
|
return result, isNull || err != nil, err
|
|
}
|
|
|
|
type builtinAddDateDurationStringSig struct {
|
|
baseBuiltinFunc
|
|
baseDateArithmitical
|
|
}
|
|
|
|
func (b *builtinAddDateDurationStringSig) Clone() builtinFunc {
|
|
newSig := &builtinAddDateDurationStringSig{baseDateArithmitical: b.baseDateArithmitical}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
func (b *builtinAddDateDurationStringSig) evalDuration(row chunk.Row) (types.Duration, bool, error) {
|
|
unit, isNull, err := b.args[2].EvalString(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return types.ZeroDuration, true, err
|
|
}
|
|
|
|
dur, isNull, err := b.args[0].EvalDuration(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return types.ZeroDuration, true, err
|
|
}
|
|
|
|
interval, isNull, err := b.getIntervalFromString(b.ctx, b.args, row, unit)
|
|
if isNull || err != nil {
|
|
return types.ZeroDuration, true, err
|
|
}
|
|
|
|
result, isNull, err := b.addDuration(b.ctx, dur, interval, unit)
|
|
return result, isNull || err != nil, err
|
|
}
|
|
|
|
type builtinAddDateDurationIntSig struct {
|
|
baseBuiltinFunc
|
|
baseDateArithmitical
|
|
}
|
|
|
|
func (b *builtinAddDateDurationIntSig) Clone() builtinFunc {
|
|
newSig := &builtinAddDateDurationIntSig{baseDateArithmitical: b.baseDateArithmitical}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
func (b *builtinAddDateDurationIntSig) evalDuration(row chunk.Row) (types.Duration, bool, error) {
|
|
unit, isNull, err := b.args[2].EvalString(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return types.ZeroDuration, true, err
|
|
}
|
|
|
|
dur, isNull, err := b.args[0].EvalDuration(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return types.ZeroDuration, true, err
|
|
}
|
|
interval, isNull, err := b.getIntervalFromInt(b.ctx, b.args, row, unit)
|
|
if isNull || err != nil {
|
|
return types.ZeroDuration, true, err
|
|
}
|
|
|
|
result, isNull, err := b.addDuration(b.ctx, dur, interval, unit)
|
|
return result, isNull || err != nil, err
|
|
}
|
|
|
|
type builtinAddDateDurationDecimalSig struct {
|
|
baseBuiltinFunc
|
|
baseDateArithmitical
|
|
}
|
|
|
|
func (b *builtinAddDateDurationDecimalSig) Clone() builtinFunc {
|
|
newSig := &builtinAddDateDurationDecimalSig{baseDateArithmitical: b.baseDateArithmitical}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
func (b *builtinAddDateDurationDecimalSig) evalDuration(row chunk.Row) (types.Duration, bool, error) {
|
|
unit, isNull, err := b.args[2].EvalString(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return types.ZeroDuration, true, err
|
|
}
|
|
|
|
dur, isNull, err := b.args[0].EvalDuration(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return types.ZeroDuration, true, err
|
|
}
|
|
interval, isNull, err := b.getIntervalFromDecimal(b.ctx, b.args, row, unit)
|
|
if isNull || err != nil {
|
|
return types.ZeroDuration, true, err
|
|
}
|
|
|
|
result, isNull, err := b.addDuration(b.ctx, dur, interval, unit)
|
|
return result, isNull || err != nil, err
|
|
}
|
|
|
|
type builtinAddDateDurationRealSig struct {
|
|
baseBuiltinFunc
|
|
baseDateArithmitical
|
|
}
|
|
|
|
func (b *builtinAddDateDurationRealSig) Clone() builtinFunc {
|
|
newSig := &builtinAddDateDurationRealSig{baseDateArithmitical: b.baseDateArithmitical}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
func (b *builtinAddDateDurationRealSig) evalDuration(row chunk.Row) (types.Duration, bool, error) {
|
|
unit, isNull, err := b.args[2].EvalString(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return types.ZeroDuration, true, err
|
|
}
|
|
|
|
dur, isNull, err := b.args[0].EvalDuration(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return types.ZeroDuration, true, err
|
|
}
|
|
interval, isNull, err := b.getIntervalFromReal(b.ctx, b.args, row, unit)
|
|
if isNull || err != nil {
|
|
return types.ZeroDuration, true, err
|
|
}
|
|
|
|
result, isNull, err := b.addDuration(b.ctx, dur, interval, unit)
|
|
return result, isNull || err != nil, err
|
|
}
|
|
|
|
type subDateFunctionClass struct {
|
|
baseFunctionClass
|
|
}
|
|
|
|
func (c *subDateFunctionClass) getFunction(ctx sessionctx.Context, args []Expression) (sig builtinFunc, err error) {
|
|
if err = c.verifyArgs(args); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
dateEvalTp := args[0].GetType().EvalType()
|
|
if dateEvalTp != types.ETString && dateEvalTp != types.ETInt && dateEvalTp != types.ETDuration {
|
|
dateEvalTp = types.ETDatetime
|
|
}
|
|
|
|
intervalEvalTp := args[1].GetType().EvalType()
|
|
if intervalEvalTp != types.ETString && intervalEvalTp != types.ETDecimal && intervalEvalTp != types.ETReal {
|
|
intervalEvalTp = types.ETInt
|
|
}
|
|
|
|
argTps := []types.EvalType{dateEvalTp, intervalEvalTp, types.ETString}
|
|
var bf baseBuiltinFunc
|
|
if dateEvalTp == types.ETDuration {
|
|
unit, _, err := args[2].EvalString(ctx, chunk.Row{})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
internalFsp := 0
|
|
switch unit {
|
|
// If the unit has micro second, then the fsp must be the MaxFsp.
|
|
case "MICROSECOND", "SECOND_MICROSECOND", "MINUTE_MICROSECOND", "HOUR_MICROSECOND", "DAY_MICROSECOND":
|
|
internalFsp = int(types.MaxFsp)
|
|
// If the unit is second, the fsp is related with the arg[1]'s.
|
|
case "SECOND":
|
|
internalFsp = int(types.MaxFsp)
|
|
if intervalEvalTp != types.ETString {
|
|
internalFsp = mathutil.Min(args[1].GetType().Decimal, int(types.MaxFsp))
|
|
}
|
|
// Otherwise, the fsp should be 0.
|
|
}
|
|
bf, err = newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETDuration, argTps...)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
arg0Dec, err := getExpressionFsp(ctx, args[0])
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
bf.tp.Flen, bf.tp.Decimal = mysql.MaxDurationWidthWithFsp, mathutil.Max(arg0Dec, internalFsp)
|
|
} else {
|
|
bf, err = newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETDatetime, argTps...)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
bf.tp.Flen, bf.tp.Decimal = mysql.MaxDatetimeFullWidth, types.UnspecifiedLength
|
|
}
|
|
|
|
switch {
|
|
case dateEvalTp == types.ETString && intervalEvalTp == types.ETString:
|
|
sig = &builtinSubDateStringStringSig{
|
|
baseBuiltinFunc: bf,
|
|
baseDateArithmitical: newDateArighmeticalUtil(),
|
|
}
|
|
sig.setPbCode(tipb.ScalarFuncSig_SubDateStringString)
|
|
case dateEvalTp == types.ETString && intervalEvalTp == types.ETInt:
|
|
sig = &builtinSubDateStringIntSig{
|
|
baseBuiltinFunc: bf,
|
|
baseDateArithmitical: newDateArighmeticalUtil(),
|
|
}
|
|
sig.setPbCode(tipb.ScalarFuncSig_SubDateStringInt)
|
|
case dateEvalTp == types.ETString && intervalEvalTp == types.ETReal:
|
|
sig = &builtinSubDateStringRealSig{
|
|
baseBuiltinFunc: bf,
|
|
baseDateArithmitical: newDateArighmeticalUtil(),
|
|
}
|
|
sig.setPbCode(tipb.ScalarFuncSig_SubDateStringReal)
|
|
case dateEvalTp == types.ETString && intervalEvalTp == types.ETDecimal:
|
|
sig = &builtinSubDateStringDecimalSig{
|
|
baseBuiltinFunc: bf,
|
|
baseDateArithmitical: newDateArighmeticalUtil(),
|
|
}
|
|
sig.setPbCode(tipb.ScalarFuncSig_SubDateStringDecimal)
|
|
case dateEvalTp == types.ETInt && intervalEvalTp == types.ETString:
|
|
sig = &builtinSubDateIntStringSig{
|
|
baseBuiltinFunc: bf,
|
|
baseDateArithmitical: newDateArighmeticalUtil(),
|
|
}
|
|
sig.setPbCode(tipb.ScalarFuncSig_SubDateIntString)
|
|
case dateEvalTp == types.ETInt && intervalEvalTp == types.ETInt:
|
|
sig = &builtinSubDateIntIntSig{
|
|
baseBuiltinFunc: bf,
|
|
baseDateArithmitical: newDateArighmeticalUtil(),
|
|
}
|
|
sig.setPbCode(tipb.ScalarFuncSig_SubDateIntInt)
|
|
case dateEvalTp == types.ETInt && intervalEvalTp == types.ETReal:
|
|
sig = &builtinSubDateIntRealSig{
|
|
baseBuiltinFunc: bf,
|
|
baseDateArithmitical: newDateArighmeticalUtil(),
|
|
}
|
|
sig.setPbCode(tipb.ScalarFuncSig_SubDateIntReal)
|
|
case dateEvalTp == types.ETInt && intervalEvalTp == types.ETDecimal:
|
|
sig = &builtinSubDateIntDecimalSig{
|
|
baseBuiltinFunc: bf,
|
|
baseDateArithmitical: newDateArighmeticalUtil(),
|
|
}
|
|
sig.setPbCode(tipb.ScalarFuncSig_SubDateIntDecimal)
|
|
case dateEvalTp == types.ETDatetime && intervalEvalTp == types.ETString:
|
|
sig = &builtinSubDateDatetimeStringSig{
|
|
baseBuiltinFunc: bf,
|
|
baseDateArithmitical: newDateArighmeticalUtil(),
|
|
}
|
|
sig.setPbCode(tipb.ScalarFuncSig_SubDateDatetimeString)
|
|
case dateEvalTp == types.ETDatetime && intervalEvalTp == types.ETInt:
|
|
sig = &builtinSubDateDatetimeIntSig{
|
|
baseBuiltinFunc: bf,
|
|
baseDateArithmitical: newDateArighmeticalUtil(),
|
|
}
|
|
sig.setPbCode(tipb.ScalarFuncSig_SubDateDatetimeInt)
|
|
case dateEvalTp == types.ETDatetime && intervalEvalTp == types.ETReal:
|
|
sig = &builtinSubDateDatetimeRealSig{
|
|
baseBuiltinFunc: bf,
|
|
baseDateArithmitical: newDateArighmeticalUtil(),
|
|
}
|
|
sig.setPbCode(tipb.ScalarFuncSig_SubDateDatetimeReal)
|
|
case dateEvalTp == types.ETDatetime && intervalEvalTp == types.ETDecimal:
|
|
sig = &builtinSubDateDatetimeDecimalSig{
|
|
baseBuiltinFunc: bf,
|
|
baseDateArithmitical: newDateArighmeticalUtil(),
|
|
}
|
|
sig.setPbCode(tipb.ScalarFuncSig_SubDateDatetimeDecimal)
|
|
case dateEvalTp == types.ETDuration && intervalEvalTp == types.ETString:
|
|
sig = &builtinSubDateDurationStringSig{
|
|
baseBuiltinFunc: bf,
|
|
baseDateArithmitical: newDateArighmeticalUtil(),
|
|
}
|
|
sig.setPbCode(tipb.ScalarFuncSig_SubDateDurationString)
|
|
case dateEvalTp == types.ETDuration && intervalEvalTp == types.ETInt:
|
|
sig = &builtinSubDateDurationIntSig{
|
|
baseBuiltinFunc: bf,
|
|
baseDateArithmitical: newDateArighmeticalUtil(),
|
|
}
|
|
sig.setPbCode(tipb.ScalarFuncSig_SubDateDurationInt)
|
|
case dateEvalTp == types.ETDuration && intervalEvalTp == types.ETReal:
|
|
sig = &builtinSubDateDurationRealSig{
|
|
baseBuiltinFunc: bf,
|
|
baseDateArithmitical: newDateArighmeticalUtil(),
|
|
}
|
|
sig.setPbCode(tipb.ScalarFuncSig_SubDateDurationReal)
|
|
case dateEvalTp == types.ETDuration && intervalEvalTp == types.ETDecimal:
|
|
sig = &builtinSubDateDurationDecimalSig{
|
|
baseBuiltinFunc: bf,
|
|
baseDateArithmitical: newDateArighmeticalUtil(),
|
|
}
|
|
sig.setPbCode(tipb.ScalarFuncSig_SubDateDurationDecimal)
|
|
}
|
|
return sig, nil
|
|
}
|
|
|
|
type builtinSubDateStringStringSig struct {
|
|
baseBuiltinFunc
|
|
baseDateArithmitical
|
|
}
|
|
|
|
func (b *builtinSubDateStringStringSig) Clone() builtinFunc {
|
|
newSig := &builtinSubDateStringStringSig{baseDateArithmitical: b.baseDateArithmitical}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
// evalTime evals SUBDATE(date,INTERVAL expr unit).
|
|
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_subdate
|
|
func (b *builtinSubDateStringStringSig) evalTime(row chunk.Row) (types.Time, bool, error) {
|
|
unit, isNull, err := b.args[2].EvalString(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return types.ZeroTime, true, err
|
|
}
|
|
|
|
date, isNull, err := b.getDateFromString(b.ctx, b.args, row, unit)
|
|
if isNull || err != nil {
|
|
return types.ZeroTime, true, err
|
|
}
|
|
|
|
interval, isNull, err := b.getIntervalFromString(b.ctx, b.args, row, unit)
|
|
if isNull || err != nil {
|
|
return types.ZeroTime, true, err
|
|
}
|
|
|
|
result, isNull, err := b.sub(b.ctx, date, interval, unit)
|
|
return result, isNull || err != nil, err
|
|
}
|
|
|
|
type builtinSubDateStringIntSig struct {
|
|
baseBuiltinFunc
|
|
baseDateArithmitical
|
|
}
|
|
|
|
func (b *builtinSubDateStringIntSig) Clone() builtinFunc {
|
|
newSig := &builtinSubDateStringIntSig{baseDateArithmitical: b.baseDateArithmitical}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
// evalTime evals SUBDATE(date,INTERVAL expr unit).
|
|
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_subdate
|
|
func (b *builtinSubDateStringIntSig) evalTime(row chunk.Row) (types.Time, bool, error) {
|
|
unit, isNull, err := b.args[2].EvalString(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return types.ZeroTime, true, err
|
|
}
|
|
|
|
date, isNull, err := b.getDateFromString(b.ctx, b.args, row, unit)
|
|
if isNull || err != nil {
|
|
return types.ZeroTime, true, err
|
|
}
|
|
|
|
interval, isNull, err := b.getIntervalFromInt(b.ctx, b.args, row, unit)
|
|
if isNull || err != nil {
|
|
return types.ZeroTime, true, err
|
|
}
|
|
|
|
result, isNull, err := b.sub(b.ctx, date, interval, unit)
|
|
return result, isNull || err != nil, err
|
|
}
|
|
|
|
type builtinSubDateStringRealSig struct {
|
|
baseBuiltinFunc
|
|
baseDateArithmitical
|
|
}
|
|
|
|
func (b *builtinSubDateStringRealSig) Clone() builtinFunc {
|
|
newSig := &builtinSubDateStringRealSig{baseDateArithmitical: b.baseDateArithmitical}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
// evalTime evals SUBDATE(date,INTERVAL expr unit).
|
|
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_subdate
|
|
func (b *builtinSubDateStringRealSig) evalTime(row chunk.Row) (types.Time, bool, error) {
|
|
unit, isNull, err := b.args[2].EvalString(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return types.ZeroTime, true, err
|
|
}
|
|
|
|
date, isNull, err := b.getDateFromString(b.ctx, b.args, row, unit)
|
|
if isNull || err != nil {
|
|
return types.ZeroTime, true, err
|
|
}
|
|
|
|
interval, isNull, err := b.getIntervalFromReal(b.ctx, b.args, row, unit)
|
|
if isNull || err != nil {
|
|
return types.ZeroTime, true, err
|
|
}
|
|
|
|
result, isNull, err := b.sub(b.ctx, date, interval, unit)
|
|
return result, isNull || err != nil, err
|
|
}
|
|
|
|
type builtinSubDateStringDecimalSig struct {
|
|
baseBuiltinFunc
|
|
baseDateArithmitical
|
|
}
|
|
|
|
func (b *builtinSubDateStringDecimalSig) Clone() builtinFunc {
|
|
newSig := &builtinSubDateStringDecimalSig{baseDateArithmitical: b.baseDateArithmitical}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
func (b *builtinSubDateStringDecimalSig) evalTime(row chunk.Row) (types.Time, bool, error) {
|
|
unit, isNull, err := b.args[2].EvalString(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return types.ZeroTime, true, err
|
|
}
|
|
|
|
date, isNull, err := b.getDateFromString(b.ctx, b.args, row, unit)
|
|
if isNull || err != nil {
|
|
return types.ZeroTime, true, err
|
|
}
|
|
|
|
interval, isNull, err := b.getIntervalFromDecimal(b.ctx, b.args, row, unit)
|
|
if isNull || err != nil {
|
|
return types.ZeroTime, true, err
|
|
}
|
|
|
|
result, isNull, err := b.sub(b.ctx, date, interval, unit)
|
|
return result, isNull || err != nil, err
|
|
}
|
|
|
|
type builtinSubDateIntStringSig struct {
|
|
baseBuiltinFunc
|
|
baseDateArithmitical
|
|
}
|
|
|
|
func (b *builtinSubDateIntStringSig) Clone() builtinFunc {
|
|
newSig := &builtinSubDateIntStringSig{baseDateArithmitical: b.baseDateArithmitical}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
// evalTime evals SUBDATE(date,INTERVAL expr unit).
|
|
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_subdate
|
|
func (b *builtinSubDateIntStringSig) evalTime(row chunk.Row) (types.Time, bool, error) {
|
|
unit, isNull, err := b.args[2].EvalString(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return types.ZeroTime, true, err
|
|
}
|
|
|
|
date, isNull, err := b.getDateFromInt(b.ctx, b.args, row, unit)
|
|
if isNull || err != nil {
|
|
return types.ZeroTime, true, err
|
|
}
|
|
|
|
interval, isNull, err := b.getIntervalFromString(b.ctx, b.args, row, unit)
|
|
if isNull || err != nil {
|
|
return types.ZeroTime, true, err
|
|
}
|
|
|
|
result, isNull, err := b.sub(b.ctx, date, interval, unit)
|
|
return result, isNull || err != nil, err
|
|
}
|
|
|
|
type builtinSubDateIntIntSig struct {
|
|
baseBuiltinFunc
|
|
baseDateArithmitical
|
|
}
|
|
|
|
func (b *builtinSubDateIntIntSig) Clone() builtinFunc {
|
|
newSig := &builtinSubDateIntIntSig{baseDateArithmitical: b.baseDateArithmitical}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
// evalTime evals SUBDATE(date,INTERVAL expr unit).
|
|
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_subdate
|
|
func (b *builtinSubDateIntIntSig) evalTime(row chunk.Row) (types.Time, bool, error) {
|
|
unit, isNull, err := b.args[2].EvalString(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return types.ZeroTime, true, err
|
|
}
|
|
|
|
date, isNull, err := b.getDateFromInt(b.ctx, b.args, row, unit)
|
|
if isNull || err != nil {
|
|
return types.ZeroTime, true, err
|
|
}
|
|
|
|
interval, isNull, err := b.getIntervalFromInt(b.ctx, b.args, row, unit)
|
|
if isNull || err != nil {
|
|
return types.ZeroTime, true, err
|
|
}
|
|
|
|
result, isNull, err := b.sub(b.ctx, date, interval, unit)
|
|
return result, isNull || err != nil, err
|
|
}
|
|
|
|
type builtinSubDateIntRealSig struct {
|
|
baseBuiltinFunc
|
|
baseDateArithmitical
|
|
}
|
|
|
|
func (b *builtinSubDateIntRealSig) Clone() builtinFunc {
|
|
newSig := &builtinSubDateIntRealSig{baseDateArithmitical: b.baseDateArithmitical}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
// evalTime evals SUBDATE(date,INTERVAL expr unit).
|
|
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_subdate
|
|
func (b *builtinSubDateIntRealSig) evalTime(row chunk.Row) (types.Time, bool, error) {
|
|
unit, isNull, err := b.args[2].EvalString(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return types.ZeroTime, true, err
|
|
}
|
|
|
|
date, isNull, err := b.getDateFromInt(b.ctx, b.args, row, unit)
|
|
if isNull || err != nil {
|
|
return types.ZeroTime, true, err
|
|
}
|
|
|
|
interval, isNull, err := b.getIntervalFromReal(b.ctx, b.args, row, unit)
|
|
if isNull || err != nil {
|
|
return types.ZeroTime, true, err
|
|
}
|
|
|
|
result, isNull, err := b.sub(b.ctx, date, interval, unit)
|
|
return result, isNull || err != nil, err
|
|
}
|
|
|
|
type builtinSubDateDatetimeStringSig struct {
|
|
baseBuiltinFunc
|
|
baseDateArithmitical
|
|
}
|
|
|
|
type builtinSubDateIntDecimalSig struct {
|
|
baseBuiltinFunc
|
|
baseDateArithmitical
|
|
}
|
|
|
|
func (b *builtinSubDateIntDecimalSig) Clone() builtinFunc {
|
|
newSig := &builtinSubDateIntDecimalSig{baseDateArithmitical: b.baseDateArithmitical}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
// evalTime evals SUBDATE(date,INTERVAL expr unit).
|
|
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_subdate
|
|
func (b *builtinSubDateIntDecimalSig) evalTime(row chunk.Row) (types.Time, bool, error) {
|
|
unit, isNull, err := b.args[2].EvalString(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return types.ZeroTime, true, err
|
|
}
|
|
|
|
date, isNull, err := b.getDateFromInt(b.ctx, b.args, row, unit)
|
|
if isNull || err != nil {
|
|
return types.ZeroTime, true, err
|
|
}
|
|
|
|
interval, isNull, err := b.getIntervalFromDecimal(b.ctx, b.args, row, unit)
|
|
if isNull || err != nil {
|
|
return types.ZeroTime, true, err
|
|
}
|
|
|
|
result, isNull, err := b.sub(b.ctx, date, interval, unit)
|
|
return result, isNull || err != nil, err
|
|
}
|
|
|
|
func (b *builtinSubDateDatetimeStringSig) Clone() builtinFunc {
|
|
newSig := &builtinSubDateDatetimeStringSig{baseDateArithmitical: b.baseDateArithmitical}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
// evalTime evals SUBDATE(date,INTERVAL expr unit).
|
|
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_subdate
|
|
func (b *builtinSubDateDatetimeStringSig) evalTime(row chunk.Row) (types.Time, bool, error) {
|
|
unit, isNull, err := b.args[2].EvalString(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return types.ZeroTime, true, err
|
|
}
|
|
|
|
date, isNull, err := b.getDateFromDatetime(b.ctx, b.args, row, unit)
|
|
if isNull || err != nil {
|
|
return types.ZeroTime, true, err
|
|
}
|
|
|
|
interval, isNull, err := b.getIntervalFromString(b.ctx, b.args, row, unit)
|
|
if isNull || err != nil {
|
|
return types.ZeroTime, true, err
|
|
}
|
|
|
|
result, isNull, err := b.sub(b.ctx, date, interval, unit)
|
|
return result, isNull || err != nil, err
|
|
}
|
|
|
|
type builtinSubDateDatetimeIntSig struct {
|
|
baseBuiltinFunc
|
|
baseDateArithmitical
|
|
}
|
|
|
|
func (b *builtinSubDateDatetimeIntSig) Clone() builtinFunc {
|
|
newSig := &builtinSubDateDatetimeIntSig{baseDateArithmitical: b.baseDateArithmitical}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
// evalTime evals SUBDATE(date,INTERVAL expr unit).
|
|
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_subdate
|
|
func (b *builtinSubDateDatetimeIntSig) evalTime(row chunk.Row) (types.Time, bool, error) {
|
|
unit, isNull, err := b.args[2].EvalString(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return types.ZeroTime, true, err
|
|
}
|
|
|
|
date, isNull, err := b.getDateFromDatetime(b.ctx, b.args, row, unit)
|
|
if isNull || err != nil {
|
|
return types.ZeroTime, true, err
|
|
}
|
|
|
|
interval, isNull, err := b.getIntervalFromInt(b.ctx, b.args, row, unit)
|
|
if isNull || err != nil {
|
|
return types.ZeroTime, true, err
|
|
}
|
|
|
|
result, isNull, err := b.sub(b.ctx, date, interval, unit)
|
|
return result, isNull || err != nil, err
|
|
}
|
|
|
|
type builtinSubDateDatetimeRealSig struct {
|
|
baseBuiltinFunc
|
|
baseDateArithmitical
|
|
}
|
|
|
|
func (b *builtinSubDateDatetimeRealSig) Clone() builtinFunc {
|
|
newSig := &builtinSubDateDatetimeRealSig{baseDateArithmitical: b.baseDateArithmitical}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
// evalTime evals SUBDATE(date,INTERVAL expr unit).
|
|
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_subdate
|
|
func (b *builtinSubDateDatetimeRealSig) evalTime(row chunk.Row) (types.Time, bool, error) {
|
|
unit, isNull, err := b.args[2].EvalString(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return types.ZeroTime, true, err
|
|
}
|
|
|
|
date, isNull, err := b.getDateFromDatetime(b.ctx, b.args, row, unit)
|
|
if isNull || err != nil {
|
|
return types.ZeroTime, true, err
|
|
}
|
|
|
|
interval, isNull, err := b.getIntervalFromReal(b.ctx, b.args, row, unit)
|
|
if isNull || err != nil {
|
|
return types.ZeroTime, true, err
|
|
}
|
|
|
|
result, isNull, err := b.sub(b.ctx, date, interval, unit)
|
|
return result, isNull || err != nil, err
|
|
}
|
|
|
|
type builtinSubDateDatetimeDecimalSig struct {
|
|
baseBuiltinFunc
|
|
baseDateArithmitical
|
|
}
|
|
|
|
func (b *builtinSubDateDatetimeDecimalSig) Clone() builtinFunc {
|
|
newSig := &builtinSubDateDatetimeDecimalSig{baseDateArithmitical: b.baseDateArithmitical}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
// evalTime evals SUBDATE(date,INTERVAL expr unit).
|
|
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_subdate
|
|
func (b *builtinSubDateDatetimeDecimalSig) evalTime(row chunk.Row) (types.Time, bool, error) {
|
|
unit, isNull, err := b.args[2].EvalString(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return types.ZeroTime, true, err
|
|
}
|
|
|
|
date, isNull, err := b.getDateFromDatetime(b.ctx, b.args, row, unit)
|
|
if isNull || err != nil {
|
|
return types.ZeroTime, true, err
|
|
}
|
|
|
|
interval, isNull, err := b.getIntervalFromDecimal(b.ctx, b.args, row, unit)
|
|
if isNull || err != nil {
|
|
return types.ZeroTime, true, err
|
|
}
|
|
|
|
result, isNull, err := b.sub(b.ctx, date, interval, unit)
|
|
return result, isNull || err != nil, err
|
|
}
|
|
|
|
type builtinSubDateDurationStringSig struct {
|
|
baseBuiltinFunc
|
|
baseDateArithmitical
|
|
}
|
|
|
|
func (b *builtinSubDateDurationStringSig) Clone() builtinFunc {
|
|
newSig := &builtinSubDateDurationStringSig{baseDateArithmitical: b.baseDateArithmitical}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
func (b *builtinSubDateDurationStringSig) evalDuration(row chunk.Row) (types.Duration, bool, error) {
|
|
unit, isNull, err := b.args[2].EvalString(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return types.ZeroDuration, true, err
|
|
}
|
|
|
|
dur, isNull, err := b.args[0].EvalDuration(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return types.ZeroDuration, true, err
|
|
}
|
|
|
|
interval, isNull, err := b.getIntervalFromString(b.ctx, b.args, row, unit)
|
|
if isNull || err != nil {
|
|
return types.ZeroDuration, true, err
|
|
}
|
|
|
|
result, isNull, err := b.subDuration(b.ctx, dur, interval, unit)
|
|
return result, isNull || err != nil, err
|
|
}
|
|
|
|
type builtinSubDateDurationIntSig struct {
|
|
baseBuiltinFunc
|
|
baseDateArithmitical
|
|
}
|
|
|
|
func (b *builtinSubDateDurationIntSig) Clone() builtinFunc {
|
|
newSig := &builtinSubDateDurationIntSig{baseDateArithmitical: b.baseDateArithmitical}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
func (b *builtinSubDateDurationIntSig) evalDuration(row chunk.Row) (types.Duration, bool, error) {
|
|
unit, isNull, err := b.args[2].EvalString(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return types.ZeroDuration, true, err
|
|
}
|
|
|
|
dur, isNull, err := b.args[0].EvalDuration(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return types.ZeroDuration, true, err
|
|
}
|
|
|
|
interval, isNull, err := b.getIntervalFromInt(b.ctx, b.args, row, unit)
|
|
if isNull || err != nil {
|
|
return types.ZeroDuration, true, err
|
|
}
|
|
|
|
result, isNull, err := b.subDuration(b.ctx, dur, interval, unit)
|
|
return result, isNull || err != nil, err
|
|
}
|
|
|
|
type builtinSubDateDurationDecimalSig struct {
|
|
baseBuiltinFunc
|
|
baseDateArithmitical
|
|
}
|
|
|
|
func (b *builtinSubDateDurationDecimalSig) Clone() builtinFunc {
|
|
newSig := &builtinSubDateDurationDecimalSig{baseDateArithmitical: b.baseDateArithmitical}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
func (b *builtinSubDateDurationDecimalSig) evalDuration(row chunk.Row) (types.Duration, bool, error) {
|
|
unit, isNull, err := b.args[2].EvalString(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return types.ZeroDuration, true, err
|
|
}
|
|
|
|
dur, isNull, err := b.args[0].EvalDuration(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return types.ZeroDuration, true, err
|
|
}
|
|
|
|
interval, isNull, err := b.getIntervalFromDecimal(b.ctx, b.args, row, unit)
|
|
if isNull || err != nil {
|
|
return types.ZeroDuration, true, err
|
|
}
|
|
|
|
result, isNull, err := b.subDuration(b.ctx, dur, interval, unit)
|
|
return result, isNull || err != nil, err
|
|
}
|
|
|
|
type builtinSubDateDurationRealSig struct {
|
|
baseBuiltinFunc
|
|
baseDateArithmitical
|
|
}
|
|
|
|
func (b *builtinSubDateDurationRealSig) Clone() builtinFunc {
|
|
newSig := &builtinSubDateDurationRealSig{baseDateArithmitical: b.baseDateArithmitical}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
func (b *builtinSubDateDurationRealSig) evalDuration(row chunk.Row) (types.Duration, bool, error) {
|
|
unit, isNull, err := b.args[2].EvalString(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return types.ZeroDuration, true, err
|
|
}
|
|
|
|
dur, isNull, err := b.args[0].EvalDuration(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return types.ZeroDuration, true, err
|
|
}
|
|
interval, isNull, err := b.getIntervalFromReal(b.ctx, b.args, row, unit)
|
|
if isNull || err != nil {
|
|
return types.ZeroDuration, true, err
|
|
}
|
|
|
|
result, isNull, err := b.subDuration(b.ctx, dur, interval, unit)
|
|
return result, isNull || err != nil, err
|
|
}
|
|
|
|
type timestampDiffFunctionClass struct {
|
|
baseFunctionClass
|
|
}
|
|
|
|
func (c *timestampDiffFunctionClass) getFunction(ctx sessionctx.Context, args []Expression) (builtinFunc, error) {
|
|
if err := c.verifyArgs(args); err != nil {
|
|
return nil, err
|
|
}
|
|
bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETInt, types.ETString, types.ETDatetime, types.ETDatetime)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
sig := &builtinTimestampDiffSig{bf}
|
|
sig.setPbCode(tipb.ScalarFuncSig_TimestampDiff)
|
|
return sig, nil
|
|
}
|
|
|
|
type builtinTimestampDiffSig struct {
|
|
baseBuiltinFunc
|
|
}
|
|
|
|
func (b *builtinTimestampDiffSig) Clone() builtinFunc {
|
|
newSig := &builtinTimestampDiffSig{}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
// evalInt evals a builtinTimestampDiffSig.
|
|
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_timestampdiff
|
|
func (b *builtinTimestampDiffSig) evalInt(row chunk.Row) (int64, bool, error) {
|
|
unit, isNull, err := b.args[0].EvalString(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return 0, isNull, err
|
|
}
|
|
lhs, isNull, err := b.args[1].EvalTime(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return 0, isNull, handleInvalidTimeError(b.ctx, err)
|
|
}
|
|
rhs, isNull, err := b.args[2].EvalTime(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return 0, isNull, handleInvalidTimeError(b.ctx, err)
|
|
}
|
|
if invalidLHS, invalidRHS := lhs.InvalidZero(), rhs.InvalidZero(); invalidLHS || invalidRHS {
|
|
if invalidLHS {
|
|
err = handleInvalidTimeError(b.ctx, types.ErrWrongValue.GenWithStackByArgs(types.DateTimeStr, lhs.String()))
|
|
}
|
|
if invalidRHS {
|
|
err = handleInvalidTimeError(b.ctx, types.ErrWrongValue.GenWithStackByArgs(types.DateTimeStr, rhs.String()))
|
|
}
|
|
return 0, true, err
|
|
}
|
|
return types.TimestampDiff(unit, lhs, rhs), false, nil
|
|
}
|
|
|
|
type unixTimestampFunctionClass struct {
|
|
baseFunctionClass
|
|
}
|
|
|
|
func (c *unixTimestampFunctionClass) getFunction(ctx sessionctx.Context, args []Expression) (builtinFunc, error) {
|
|
if err := c.verifyArgs(args); err != nil {
|
|
return nil, err
|
|
}
|
|
var (
|
|
argTps []types.EvalType
|
|
retTp types.EvalType
|
|
retFLen, retDecimal int
|
|
)
|
|
|
|
if len(args) == 0 {
|
|
retTp, retDecimal = types.ETInt, 0
|
|
} else {
|
|
argTps = []types.EvalType{types.ETDatetime}
|
|
argType := args[0].GetType()
|
|
argEvaltp := argType.EvalType()
|
|
if argEvaltp == types.ETString {
|
|
// Treat types.ETString as unspecified decimal.
|
|
retDecimal = types.UnspecifiedLength
|
|
if cnst, ok := args[0].(*Constant); ok {
|
|
tmpStr, _, err := cnst.EvalString(ctx, chunk.Row{})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
retDecimal = 0
|
|
if dotIdx := strings.LastIndex(tmpStr, "."); dotIdx >= 0 {
|
|
retDecimal = len(tmpStr) - dotIdx - 1
|
|
}
|
|
}
|
|
} else {
|
|
retDecimal = argType.Decimal
|
|
}
|
|
if retDecimal > 6 || retDecimal == types.UnspecifiedLength {
|
|
retDecimal = 6
|
|
}
|
|
if retDecimal == 0 {
|
|
retTp = types.ETInt
|
|
} else {
|
|
retTp = types.ETDecimal
|
|
}
|
|
}
|
|
if retTp == types.ETInt {
|
|
retFLen = 11
|
|
} else if retTp == types.ETDecimal {
|
|
retFLen = 12 + retDecimal
|
|
} else {
|
|
panic("Unexpected retTp")
|
|
}
|
|
|
|
bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, retTp, argTps...)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
bf.tp.Flen = retFLen
|
|
bf.tp.Decimal = retDecimal
|
|
|
|
var sig builtinFunc
|
|
if len(args) == 0 {
|
|
sig = &builtinUnixTimestampCurrentSig{bf}
|
|
sig.setPbCode(tipb.ScalarFuncSig_UnixTimestampCurrent)
|
|
} else if retTp == types.ETInt {
|
|
sig = &builtinUnixTimestampIntSig{bf}
|
|
sig.setPbCode(tipb.ScalarFuncSig_UnixTimestampInt)
|
|
} else if retTp == types.ETDecimal {
|
|
sig = &builtinUnixTimestampDecSig{bf}
|
|
sig.setPbCode(tipb.ScalarFuncSig_UnixTimestampDec)
|
|
}
|
|
return sig, nil
|
|
}
|
|
|
|
// goTimeToMysqlUnixTimestamp converts go time into MySQL's Unix timestamp.
|
|
// MySQL's Unix timestamp ranges in int32. Values out of range should be rewritten to 0.
|
|
func goTimeToMysqlUnixTimestamp(t time.Time, decimal int) (*types.MyDecimal, error) {
|
|
nanoSeconds := t.UnixNano()
|
|
if nanoSeconds < 0 || (nanoSeconds/1e3) >= (math.MaxInt32+1)*1e6 {
|
|
return new(types.MyDecimal), nil
|
|
}
|
|
dec := new(types.MyDecimal)
|
|
// Here we don't use float to prevent precision lose.
|
|
dec.FromInt(nanoSeconds)
|
|
err := dec.Shift(-9)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// In MySQL's implementation, unix_timestamp() will truncate the result instead of rounding it.
|
|
// Results below are from MySQL 5.7, which can prove it.
|
|
// mysql> select unix_timestamp(), unix_timestamp(now(0)), now(0), unix_timestamp(now(3)), now(3), now(6);
|
|
// +------------------+------------------------+---------------------+------------------------+-------------------------+----------------------------+
|
|
// | unix_timestamp() | unix_timestamp(now(0)) | now(0) | unix_timestamp(now(3)) | now(3) | now(6) |
|
|
// +------------------+------------------------+---------------------+------------------------+-------------------------+----------------------------+
|
|
// | 1553503194 | 1553503194 | 2019-03-25 16:39:54 | 1553503194.992 | 2019-03-25 16:39:54.992 | 2019-03-25 16:39:54.992969 |
|
|
// +------------------+------------------------+---------------------+------------------------+-------------------------+----------------------------+
|
|
err = dec.Round(dec, decimal, types.ModeTruncate)
|
|
return dec, err
|
|
}
|
|
|
|
type builtinUnixTimestampCurrentSig struct {
|
|
baseBuiltinFunc
|
|
}
|
|
|
|
func (b *builtinUnixTimestampCurrentSig) Clone() builtinFunc {
|
|
newSig := &builtinUnixTimestampCurrentSig{}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
// evalInt evals a UNIX_TIMESTAMP().
|
|
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_unix-timestamp
|
|
func (b *builtinUnixTimestampCurrentSig) evalInt(row chunk.Row) (int64, bool, error) {
|
|
nowTs, err := getStmtTimestamp(b.ctx)
|
|
if err != nil {
|
|
return 0, true, err
|
|
}
|
|
dec, err := goTimeToMysqlUnixTimestamp(nowTs, 1)
|
|
if err != nil {
|
|
return 0, true, err
|
|
}
|
|
intVal, err := dec.ToInt()
|
|
if !terror.ErrorEqual(err, types.ErrTruncated) {
|
|
terror.Log(err)
|
|
}
|
|
return intVal, false, nil
|
|
}
|
|
|
|
type builtinUnixTimestampIntSig struct {
|
|
baseBuiltinFunc
|
|
}
|
|
|
|
func (b *builtinUnixTimestampIntSig) Clone() builtinFunc {
|
|
newSig := &builtinUnixTimestampIntSig{}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
// evalInt evals a UNIX_TIMESTAMP(time).
|
|
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_unix-timestamp
|
|
func (b *builtinUnixTimestampIntSig) evalInt(row chunk.Row) (int64, bool, error) {
|
|
return b.evalIntWithCtx(b.ctx, row)
|
|
}
|
|
|
|
func (b *builtinUnixTimestampIntSig) evalIntWithCtx(ctx sessionctx.Context, row chunk.Row) (int64, bool, error) {
|
|
val, isNull, err := b.args[0].EvalTime(ctx, row)
|
|
if err != nil && terror.ErrorEqual(types.ErrWrongValue.GenWithStackByArgs(types.TimeStr, val), err) {
|
|
// Return 0 for invalid date time.
|
|
return 0, false, nil
|
|
}
|
|
if isNull {
|
|
return 0, true, nil
|
|
}
|
|
|
|
tz := ctx.GetSessionVars().Location()
|
|
t, err := val.GoTime(tz)
|
|
if err != nil {
|
|
return 0, false, nil
|
|
}
|
|
dec, err := goTimeToMysqlUnixTimestamp(t, 1)
|
|
if err != nil {
|
|
return 0, true, err
|
|
}
|
|
intVal, err := dec.ToInt()
|
|
if !terror.ErrorEqual(err, types.ErrTruncated) {
|
|
terror.Log(err)
|
|
}
|
|
return intVal, false, nil
|
|
}
|
|
|
|
type builtinUnixTimestampDecSig struct {
|
|
baseBuiltinFunc
|
|
}
|
|
|
|
func (b *builtinUnixTimestampDecSig) Clone() builtinFunc {
|
|
newSig := &builtinUnixTimestampDecSig{}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
// evalDecimal evals a UNIX_TIMESTAMP(time).
|
|
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_unix-timestamp
|
|
func (b *builtinUnixTimestampDecSig) evalDecimal(row chunk.Row) (*types.MyDecimal, bool, error) {
|
|
val, isNull, err := b.args[0].EvalTime(b.ctx, row)
|
|
if isNull || err != nil {
|
|
// Return 0 for invalid date time.
|
|
return new(types.MyDecimal), isNull, nil
|
|
}
|
|
t, err := val.GoTime(getTimeZone(b.ctx))
|
|
if err != nil {
|
|
return new(types.MyDecimal), false, nil
|
|
}
|
|
result, err := goTimeToMysqlUnixTimestamp(t, b.tp.Decimal)
|
|
return result, err != nil, err
|
|
}
|
|
|
|
type timestampFunctionClass struct {
|
|
baseFunctionClass
|
|
}
|
|
|
|
func (c *timestampFunctionClass) getDefaultFsp(tp *types.FieldType) int8 {
|
|
if tp.Tp == mysql.TypeDatetime || tp.Tp == mysql.TypeDate || tp.Tp == mysql.TypeDuration ||
|
|
tp.Tp == mysql.TypeTimestamp {
|
|
return int8(tp.Decimal)
|
|
}
|
|
switch cls := tp.EvalType(); cls {
|
|
case types.ETInt:
|
|
return types.MinFsp
|
|
case types.ETReal, types.ETDatetime, types.ETTimestamp, types.ETDuration, types.ETJson, types.ETString:
|
|
return types.MaxFsp
|
|
case types.ETDecimal:
|
|
if tp.Decimal < int(types.MaxFsp) {
|
|
return int8(tp.Decimal)
|
|
}
|
|
return types.MaxFsp
|
|
}
|
|
return types.MaxFsp
|
|
}
|
|
|
|
func (c *timestampFunctionClass) getFunction(ctx sessionctx.Context, args []Expression) (builtinFunc, error) {
|
|
if err := c.verifyArgs(args); err != nil {
|
|
return nil, err
|
|
}
|
|
evalTps, argLen := []types.EvalType{types.ETString}, len(args)
|
|
if argLen == 2 {
|
|
evalTps = append(evalTps, types.ETString)
|
|
}
|
|
fsp := c.getDefaultFsp(args[0].GetType())
|
|
if argLen == 2 {
|
|
fsp = mathutil.MaxInt8(fsp, c.getDefaultFsp(args[1].GetType()))
|
|
}
|
|
isFloat := false
|
|
switch args[0].GetType().Tp {
|
|
case mysql.TypeFloat, mysql.TypeDouble, mysql.TypeNewDecimal, mysql.TypeLonglong:
|
|
isFloat = true
|
|
}
|
|
bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETDatetime, evalTps...)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
bf.tp.Decimal, bf.tp.Flen = -1, 19
|
|
if fsp != 0 {
|
|
bf.tp.Flen += 1 + int(fsp)
|
|
}
|
|
var sig builtinFunc
|
|
if argLen == 2 {
|
|
sig = &builtinTimestamp2ArgsSig{bf, isFloat}
|
|
sig.setPbCode(tipb.ScalarFuncSig_Timestamp2Args)
|
|
} else {
|
|
sig = &builtinTimestamp1ArgSig{bf, isFloat}
|
|
sig.setPbCode(tipb.ScalarFuncSig_Timestamp1Arg)
|
|
}
|
|
return sig, nil
|
|
}
|
|
|
|
type builtinTimestamp1ArgSig struct {
|
|
baseBuiltinFunc
|
|
|
|
isFloat bool
|
|
}
|
|
|
|
func (b *builtinTimestamp1ArgSig) Clone() builtinFunc {
|
|
newSig := &builtinTimestamp1ArgSig{isFloat: b.isFloat}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
// evalTime evals a builtinTimestamp1ArgSig.
|
|
// See https://dev.mysql.com/doc/refman/5.5/en/date-and-time-functions.html#function_timestamp
|
|
func (b *builtinTimestamp1ArgSig) evalTime(row chunk.Row) (types.Time, bool, error) {
|
|
s, isNull, err := b.args[0].EvalString(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return types.ZeroTime, isNull, err
|
|
}
|
|
var tm types.Time
|
|
sc := b.ctx.GetSessionVars().StmtCtx
|
|
if b.isFloat {
|
|
tm, err = types.ParseTimeFromFloatString(sc, s, mysql.TypeDatetime, types.GetFsp(s))
|
|
} else {
|
|
tm, err = types.ParseTime(sc, s, mysql.TypeDatetime, types.GetFsp(s))
|
|
}
|
|
if err != nil {
|
|
return types.ZeroTime, true, handleInvalidTimeError(b.ctx, err)
|
|
}
|
|
return tm, false, nil
|
|
}
|
|
|
|
type builtinTimestamp2ArgsSig struct {
|
|
baseBuiltinFunc
|
|
|
|
isFloat bool
|
|
}
|
|
|
|
func (b *builtinTimestamp2ArgsSig) Clone() builtinFunc {
|
|
newSig := &builtinTimestamp2ArgsSig{isFloat: b.isFloat}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
// evalTime evals a builtinTimestamp2ArgsSig.
|
|
// See https://dev.mysql.com/doc/refman/5.5/en/date-and-time-functions.html#function_timestamp
|
|
func (b *builtinTimestamp2ArgsSig) evalTime(row chunk.Row) (types.Time, bool, error) {
|
|
arg0, isNull, err := b.args[0].EvalString(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return types.ZeroTime, isNull, err
|
|
}
|
|
var tm types.Time
|
|
sc := b.ctx.GetSessionVars().StmtCtx
|
|
if b.isFloat {
|
|
tm, err = types.ParseTimeFromFloatString(sc, arg0, mysql.TypeDatetime, types.GetFsp(arg0))
|
|
} else {
|
|
tm, err = types.ParseTime(sc, arg0, mysql.TypeDatetime, types.GetFsp(arg0))
|
|
}
|
|
if err != nil {
|
|
return types.ZeroTime, true, handleInvalidTimeError(b.ctx, err)
|
|
}
|
|
arg1, isNull, err := b.args[1].EvalString(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return types.ZeroTime, isNull, err
|
|
}
|
|
if !isDuration(arg1) {
|
|
return types.ZeroTime, true, nil
|
|
}
|
|
duration, err := types.ParseDuration(sc, arg1, types.GetFsp(arg1))
|
|
if err != nil {
|
|
return types.ZeroTime, true, handleInvalidTimeError(b.ctx, err)
|
|
}
|
|
tmp, err := tm.Add(sc, duration)
|
|
if err != nil {
|
|
return types.ZeroTime, true, err
|
|
}
|
|
return tmp, false, nil
|
|
}
|
|
|
|
type timestampLiteralFunctionClass struct {
|
|
baseFunctionClass
|
|
}
|
|
|
|
func (c *timestampLiteralFunctionClass) getFunction(ctx sessionctx.Context, args []Expression) (builtinFunc, error) {
|
|
if err := c.verifyArgs(args); err != nil {
|
|
return nil, err
|
|
}
|
|
con, ok := args[0].(*Constant)
|
|
if !ok {
|
|
panic("Unexpected parameter for timestamp literal")
|
|
}
|
|
dt, err := con.Eval(chunk.Row{})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
str, err := dt.ToString()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if !timestampPattern.MatchString(str) {
|
|
return nil, types.ErrWrongValue.GenWithStackByArgs(types.DateTimeStr, str)
|
|
}
|
|
tm, err := types.ParseTime(ctx.GetSessionVars().StmtCtx, str, mysql.TypeTimestamp, types.GetFsp(str))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, []Expression{}, types.ETDatetime)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
bf.tp.Flen, bf.tp.Decimal = mysql.MaxDatetimeWidthNoFsp, int(tm.Fsp())
|
|
if tm.Fsp() > 0 {
|
|
bf.tp.Flen += int(tm.Fsp()) + 1
|
|
}
|
|
sig := &builtinTimestampLiteralSig{bf, tm}
|
|
return sig, nil
|
|
}
|
|
|
|
type builtinTimestampLiteralSig struct {
|
|
baseBuiltinFunc
|
|
tm types.Time
|
|
}
|
|
|
|
func (b *builtinTimestampLiteralSig) Clone() builtinFunc {
|
|
newSig := &builtinTimestampLiteralSig{tm: b.tm}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
// evalTime evals TIMESTAMP 'stringLit'.
|
|
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-literals.html
|
|
func (b *builtinTimestampLiteralSig) evalTime(row chunk.Row) (types.Time, bool, error) {
|
|
return b.tm, false, nil
|
|
}
|
|
|
|
// getFsp4TimeAddSub is used to in function 'ADDTIME' and 'SUBTIME' to evaluate `fsp` for the
|
|
// second parameter. It's used only if the second parameter is of string type. It's different
|
|
// from getFsp in that the result of getFsp4TimeAddSub is either 6 or 0.
|
|
func getFsp4TimeAddSub(s string) int8 {
|
|
if len(s)-strings.Index(s, ".")-1 == len(s) {
|
|
return types.MinFsp
|
|
}
|
|
for _, c := range s[strings.Index(s, ".")+1:] {
|
|
if c != '0' {
|
|
return types.MaxFsp
|
|
}
|
|
}
|
|
return types.MinFsp
|
|
}
|
|
|
|
// getBf4TimeAddSub parses input types, generates baseBuiltinFunc and set related attributes for
|
|
// builtin function 'ADDTIME' and 'SUBTIME'
|
|
func getBf4TimeAddSub(ctx sessionctx.Context, funcName string, args []Expression) (tp1, tp2 *types.FieldType, bf baseBuiltinFunc, err error) {
|
|
tp1, tp2 = args[0].GetType(), args[1].GetType()
|
|
var argTp1, argTp2, retTp types.EvalType
|
|
switch tp1.Tp {
|
|
case mysql.TypeDatetime, mysql.TypeTimestamp:
|
|
argTp1, retTp = types.ETDatetime, types.ETDatetime
|
|
case mysql.TypeDuration:
|
|
argTp1, retTp = types.ETDuration, types.ETDuration
|
|
case mysql.TypeDate:
|
|
argTp1, retTp = types.ETDuration, types.ETString
|
|
default:
|
|
argTp1, retTp = types.ETString, types.ETString
|
|
}
|
|
switch tp2.Tp {
|
|
case mysql.TypeDatetime, mysql.TypeDuration:
|
|
argTp2 = types.ETDuration
|
|
default:
|
|
argTp2 = types.ETString
|
|
}
|
|
arg0Dec, err := getExpressionFsp(ctx, args[0])
|
|
if err != nil {
|
|
return
|
|
}
|
|
arg1Dec, err := getExpressionFsp(ctx, args[1])
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
bf, err = newBaseBuiltinFuncWithTp(ctx, funcName, args, retTp, argTp1, argTp2)
|
|
if err != nil {
|
|
return
|
|
}
|
|
bf.tp.Decimal = mathutil.Min(mathutil.Max(arg0Dec, arg1Dec), int(types.MaxFsp))
|
|
if retTp == types.ETString {
|
|
bf.tp.Tp, bf.tp.Flen, bf.tp.Decimal = mysql.TypeString, mysql.MaxDatetimeWidthWithFsp, types.UnspecifiedLength
|
|
}
|
|
return
|
|
}
|
|
|
|
func getTimeZone(ctx sessionctx.Context) *time.Location {
|
|
ret := ctx.GetSessionVars().TimeZone
|
|
if ret == nil {
|
|
ret = time.Local
|
|
}
|
|
return ret
|
|
}
|
|
|
|
// isDuration returns a boolean indicating whether the str matches the format of duration.
|
|
// See https://dev.mysql.com/doc/refman/5.7/en/time.html
|
|
func isDuration(str string) bool {
|
|
return durationPattern.MatchString(str)
|
|
}
|
|
|
|
// strDatetimeAddDuration adds duration to datetime string, returns a string value.
|
|
func strDatetimeAddDuration(sc *stmtctx.StatementContext, d string, arg1 types.Duration) (string, error) {
|
|
arg0, err := types.ParseTime(sc, d, mysql.TypeDatetime, types.MaxFsp)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
ret, err := arg0.Add(sc, arg1)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
fsp := types.MaxFsp
|
|
if ret.Microsecond() == 0 {
|
|
fsp = types.MinFsp
|
|
}
|
|
ret.SetFsp(fsp)
|
|
return ret.String(), nil
|
|
}
|
|
|
|
// strDurationAddDuration adds duration to duration string, returns a string value.
|
|
func strDurationAddDuration(sc *stmtctx.StatementContext, d string, arg1 types.Duration) (string, error) {
|
|
arg0, err := types.ParseDuration(sc, d, types.MaxFsp)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
tmpDuration, err := arg0.Add(arg1)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
tmpDuration.Fsp = types.MaxFsp
|
|
if tmpDuration.MicroSecond() == 0 {
|
|
tmpDuration.Fsp = types.MinFsp
|
|
}
|
|
return tmpDuration.String(), nil
|
|
}
|
|
|
|
// strDatetimeSubDuration subtracts duration from datetime string, returns a string value.
|
|
func strDatetimeSubDuration(sc *stmtctx.StatementContext, d string, arg1 types.Duration) (string, error) {
|
|
arg0, err := types.ParseTime(sc, d, mysql.TypeDatetime, types.MaxFsp)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
arg1time, err := arg1.ConvertToTime(sc, uint8(types.GetFsp(arg1.String())))
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
tmpDuration := arg0.Sub(sc, &arg1time)
|
|
fsp := types.MaxFsp
|
|
if tmpDuration.MicroSecond() == 0 {
|
|
fsp = types.MinFsp
|
|
}
|
|
resultDuration, err := tmpDuration.ConvertToTime(sc, mysql.TypeDatetime)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
resultDuration.SetFsp(fsp)
|
|
return resultDuration.String(), nil
|
|
}
|
|
|
|
// strDurationSubDuration subtracts duration from duration string, returns a string value.
|
|
func strDurationSubDuration(sc *stmtctx.StatementContext, d string, arg1 types.Duration) (string, error) {
|
|
arg0, err := types.ParseDuration(sc, d, types.MaxFsp)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
tmpDuration, err := arg0.Sub(arg1)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
tmpDuration.Fsp = types.MaxFsp
|
|
if tmpDuration.MicroSecond() == 0 {
|
|
tmpDuration.Fsp = types.MinFsp
|
|
}
|
|
return tmpDuration.String(), nil
|
|
}
|
|
|
|
type addTimeFunctionClass struct {
|
|
baseFunctionClass
|
|
}
|
|
|
|
func (c *addTimeFunctionClass) getFunction(ctx sessionctx.Context, args []Expression) (sig builtinFunc, err error) {
|
|
if err = c.verifyArgs(args); err != nil {
|
|
return nil, err
|
|
}
|
|
tp1, tp2, bf, err := getBf4TimeAddSub(ctx, c.funcName, args)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
switch tp1.Tp {
|
|
case mysql.TypeDatetime, mysql.TypeTimestamp:
|
|
switch tp2.Tp {
|
|
case mysql.TypeDuration:
|
|
sig = &builtinAddDatetimeAndDurationSig{bf}
|
|
sig.setPbCode(tipb.ScalarFuncSig_AddDatetimeAndDuration)
|
|
case mysql.TypeDatetime, mysql.TypeTimestamp:
|
|
sig = &builtinAddTimeDateTimeNullSig{bf}
|
|
sig.setPbCode(tipb.ScalarFuncSig_AddTimeDateTimeNull)
|
|
default:
|
|
sig = &builtinAddDatetimeAndStringSig{bf}
|
|
sig.setPbCode(tipb.ScalarFuncSig_AddDatetimeAndString)
|
|
}
|
|
case mysql.TypeDate:
|
|
bf.tp.Charset, bf.tp.Collate = ctx.GetSessionVars().GetCharsetInfo()
|
|
switch tp2.Tp {
|
|
case mysql.TypeDuration:
|
|
sig = &builtinAddDateAndDurationSig{bf}
|
|
sig.setPbCode(tipb.ScalarFuncSig_AddDateAndDuration)
|
|
case mysql.TypeDatetime, mysql.TypeTimestamp:
|
|
sig = &builtinAddTimeStringNullSig{bf}
|
|
sig.setPbCode(tipb.ScalarFuncSig_AddTimeStringNull)
|
|
default:
|
|
sig = &builtinAddDateAndStringSig{bf}
|
|
sig.setPbCode(tipb.ScalarFuncSig_AddDateAndString)
|
|
}
|
|
case mysql.TypeDuration:
|
|
switch tp2.Tp {
|
|
case mysql.TypeDuration:
|
|
sig = &builtinAddDurationAndDurationSig{bf}
|
|
sig.setPbCode(tipb.ScalarFuncSig_AddDurationAndDuration)
|
|
case mysql.TypeDatetime, mysql.TypeTimestamp:
|
|
sig = &builtinAddTimeDurationNullSig{bf}
|
|
sig.setPbCode(tipb.ScalarFuncSig_AddTimeDurationNull)
|
|
default:
|
|
sig = &builtinAddDurationAndStringSig{bf}
|
|
sig.setPbCode(tipb.ScalarFuncSig_AddDurationAndString)
|
|
}
|
|
default:
|
|
switch tp2.Tp {
|
|
case mysql.TypeDuration:
|
|
sig = &builtinAddStringAndDurationSig{bf}
|
|
sig.setPbCode(tipb.ScalarFuncSig_AddStringAndDuration)
|
|
case mysql.TypeDatetime, mysql.TypeTimestamp:
|
|
sig = &builtinAddTimeStringNullSig{bf}
|
|
sig.setPbCode(tipb.ScalarFuncSig_AddTimeStringNull)
|
|
default:
|
|
sig = &builtinAddStringAndStringSig{bf}
|
|
sig.setPbCode(tipb.ScalarFuncSig_AddStringAndString)
|
|
}
|
|
}
|
|
return sig, nil
|
|
}
|
|
|
|
type builtinAddTimeDateTimeNullSig struct {
|
|
baseBuiltinFunc
|
|
}
|
|
|
|
func (b *builtinAddTimeDateTimeNullSig) Clone() builtinFunc {
|
|
newSig := &builtinAddTimeDateTimeNullSig{}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
// evalTime evals a builtinAddTimeDateTimeNullSig.
|
|
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_addtime
|
|
func (b *builtinAddTimeDateTimeNullSig) evalTime(row chunk.Row) (types.Time, bool, error) {
|
|
return types.ZeroDatetime, true, nil
|
|
}
|
|
|
|
type builtinAddDatetimeAndDurationSig struct {
|
|
baseBuiltinFunc
|
|
}
|
|
|
|
func (b *builtinAddDatetimeAndDurationSig) Clone() builtinFunc {
|
|
newSig := &builtinAddDatetimeAndDurationSig{}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
// evalTime evals a builtinAddDatetimeAndDurationSig.
|
|
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_addtime
|
|
func (b *builtinAddDatetimeAndDurationSig) evalTime(row chunk.Row) (types.Time, bool, error) {
|
|
arg0, isNull, err := b.args[0].EvalTime(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return types.ZeroDatetime, isNull, err
|
|
}
|
|
arg1, isNull, err := b.args[1].EvalDuration(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return types.ZeroDatetime, isNull, err
|
|
}
|
|
result, err := arg0.Add(b.ctx.GetSessionVars().StmtCtx, arg1)
|
|
return result, err != nil, err
|
|
}
|
|
|
|
type builtinAddDatetimeAndStringSig struct {
|
|
baseBuiltinFunc
|
|
}
|
|
|
|
func (b *builtinAddDatetimeAndStringSig) Clone() builtinFunc {
|
|
newSig := &builtinAddDatetimeAndStringSig{}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
// evalTime evals a builtinAddDatetimeAndStringSig.
|
|
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_addtime
|
|
func (b *builtinAddDatetimeAndStringSig) evalTime(row chunk.Row) (types.Time, bool, error) {
|
|
arg0, isNull, err := b.args[0].EvalTime(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return types.ZeroDatetime, isNull, err
|
|
}
|
|
s, isNull, err := b.args[1].EvalString(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return types.ZeroDatetime, isNull, err
|
|
}
|
|
if !isDuration(s) {
|
|
return types.ZeroDatetime, true, nil
|
|
}
|
|
sc := b.ctx.GetSessionVars().StmtCtx
|
|
arg1, err := types.ParseDuration(sc, s, types.GetFsp(s))
|
|
if err != nil {
|
|
if terror.ErrorEqual(err, types.ErrTruncatedWrongVal) {
|
|
sc.AppendWarning(err)
|
|
return types.ZeroDatetime, true, nil
|
|
}
|
|
return types.ZeroDatetime, true, err
|
|
}
|
|
result, err := arg0.Add(sc, arg1)
|
|
return result, err != nil, err
|
|
}
|
|
|
|
type builtinAddTimeDurationNullSig struct {
|
|
baseBuiltinFunc
|
|
}
|
|
|
|
func (b *builtinAddTimeDurationNullSig) Clone() builtinFunc {
|
|
newSig := &builtinAddTimeDurationNullSig{}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
// evalDuration evals a builtinAddTimeDurationNullSig.
|
|
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_addtime
|
|
func (b *builtinAddTimeDurationNullSig) evalDuration(row chunk.Row) (types.Duration, bool, error) {
|
|
return types.ZeroDuration, true, nil
|
|
}
|
|
|
|
type builtinAddDurationAndDurationSig struct {
|
|
baseBuiltinFunc
|
|
}
|
|
|
|
func (b *builtinAddDurationAndDurationSig) Clone() builtinFunc {
|
|
newSig := &builtinAddDurationAndDurationSig{}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
// evalDuration evals a builtinAddDurationAndDurationSig.
|
|
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_addtime
|
|
func (b *builtinAddDurationAndDurationSig) evalDuration(row chunk.Row) (types.Duration, bool, error) {
|
|
arg0, isNull, err := b.args[0].EvalDuration(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return types.ZeroDuration, isNull, err
|
|
}
|
|
arg1, isNull, err := b.args[1].EvalDuration(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return types.ZeroDuration, isNull, err
|
|
}
|
|
result, err := arg0.Add(arg1)
|
|
if err != nil {
|
|
return types.ZeroDuration, true, err
|
|
}
|
|
return result, false, nil
|
|
}
|
|
|
|
type builtinAddDurationAndStringSig struct {
|
|
baseBuiltinFunc
|
|
}
|
|
|
|
func (b *builtinAddDurationAndStringSig) Clone() builtinFunc {
|
|
newSig := &builtinAddDurationAndStringSig{}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
// evalDuration evals a builtinAddDurationAndStringSig.
|
|
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_addtime
|
|
func (b *builtinAddDurationAndStringSig) evalDuration(row chunk.Row) (types.Duration, bool, error) {
|
|
arg0, isNull, err := b.args[0].EvalDuration(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return types.ZeroDuration, isNull, err
|
|
}
|
|
s, isNull, err := b.args[1].EvalString(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return types.ZeroDuration, isNull, err
|
|
}
|
|
if !isDuration(s) {
|
|
return types.ZeroDuration, true, nil
|
|
}
|
|
sc := b.ctx.GetSessionVars().StmtCtx
|
|
arg1, err := types.ParseDuration(sc, s, types.GetFsp(s))
|
|
if err != nil {
|
|
if terror.ErrorEqual(err, types.ErrTruncatedWrongVal) {
|
|
sc.AppendWarning(err)
|
|
return types.ZeroDuration, true, nil
|
|
}
|
|
return types.ZeroDuration, true, err
|
|
}
|
|
result, err := arg0.Add(arg1)
|
|
if err != nil {
|
|
return types.ZeroDuration, true, err
|
|
}
|
|
return result, false, nil
|
|
}
|
|
|
|
type builtinAddTimeStringNullSig struct {
|
|
baseBuiltinFunc
|
|
}
|
|
|
|
func (b *builtinAddTimeStringNullSig) Clone() builtinFunc {
|
|
newSig := &builtinAddTimeStringNullSig{}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
// evalString evals a builtinAddDurationAndDurationSig.
|
|
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_addtime
|
|
func (b *builtinAddTimeStringNullSig) evalString(row chunk.Row) (string, bool, error) {
|
|
return "", true, nil
|
|
}
|
|
|
|
type builtinAddStringAndDurationSig struct {
|
|
baseBuiltinFunc
|
|
}
|
|
|
|
func (b *builtinAddStringAndDurationSig) Clone() builtinFunc {
|
|
newSig := &builtinAddStringAndDurationSig{}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
// evalString evals a builtinAddStringAndDurationSig.
|
|
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_addtime
|
|
func (b *builtinAddStringAndDurationSig) evalString(row chunk.Row) (result string, isNull bool, err error) {
|
|
var (
|
|
arg0 string
|
|
arg1 types.Duration
|
|
)
|
|
arg0, isNull, err = b.args[0].EvalString(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return "", isNull, err
|
|
}
|
|
arg1, isNull, err = b.args[1].EvalDuration(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return "", isNull, err
|
|
}
|
|
sc := b.ctx.GetSessionVars().StmtCtx
|
|
if isDuration(arg0) {
|
|
result, err = strDurationAddDuration(sc, arg0, arg1)
|
|
if err != nil {
|
|
if terror.ErrorEqual(err, types.ErrTruncatedWrongVal) {
|
|
sc.AppendWarning(err)
|
|
return "", true, nil
|
|
}
|
|
return "", true, err
|
|
}
|
|
return result, false, nil
|
|
}
|
|
result, err = strDatetimeAddDuration(sc, arg0, arg1)
|
|
return result, err != nil, err
|
|
}
|
|
|
|
type builtinAddStringAndStringSig struct {
|
|
baseBuiltinFunc
|
|
}
|
|
|
|
func (b *builtinAddStringAndStringSig) Clone() builtinFunc {
|
|
newSig := &builtinAddStringAndStringSig{}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
// evalString evals a builtinAddStringAndStringSig.
|
|
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_addtime
|
|
func (b *builtinAddStringAndStringSig) evalString(row chunk.Row) (result string, isNull bool, err error) {
|
|
var (
|
|
arg0, arg1Str string
|
|
arg1 types.Duration
|
|
)
|
|
arg0, isNull, err = b.args[0].EvalString(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return "", isNull, err
|
|
}
|
|
arg1Type := b.args[1].GetType()
|
|
if mysql.HasBinaryFlag(arg1Type.Flag) {
|
|
return "", true, nil
|
|
}
|
|
arg1Str, isNull, err = b.args[1].EvalString(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return "", isNull, err
|
|
}
|
|
sc := b.ctx.GetSessionVars().StmtCtx
|
|
arg1, err = types.ParseDuration(sc, arg1Str, getFsp4TimeAddSub(arg1Str))
|
|
if err != nil {
|
|
if terror.ErrorEqual(err, types.ErrTruncatedWrongVal) {
|
|
sc.AppendWarning(err)
|
|
return "", true, nil
|
|
}
|
|
return "", true, err
|
|
}
|
|
if isDuration(arg0) {
|
|
result, err = strDurationAddDuration(sc, arg0, arg1)
|
|
if err != nil {
|
|
if terror.ErrorEqual(err, types.ErrTruncatedWrongVal) {
|
|
sc.AppendWarning(err)
|
|
return "", true, nil
|
|
}
|
|
return "", true, err
|
|
}
|
|
return result, false, nil
|
|
}
|
|
result, err = strDatetimeAddDuration(sc, arg0, arg1)
|
|
return result, err != nil, err
|
|
}
|
|
|
|
type builtinAddDateAndDurationSig struct {
|
|
baseBuiltinFunc
|
|
}
|
|
|
|
func (b *builtinAddDateAndDurationSig) Clone() builtinFunc {
|
|
newSig := &builtinAddDateAndDurationSig{}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
// evalString evals a builtinAddDurationAndDurationSig.
|
|
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_addtime
|
|
func (b *builtinAddDateAndDurationSig) evalString(row chunk.Row) (string, bool, error) {
|
|
arg0, isNull, err := b.args[0].EvalDuration(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return "", isNull, err
|
|
}
|
|
arg1, isNull, err := b.args[1].EvalDuration(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return "", isNull, err
|
|
}
|
|
result, err := arg0.Add(arg1)
|
|
return result.String(), err != nil, err
|
|
}
|
|
|
|
type builtinAddDateAndStringSig struct {
|
|
baseBuiltinFunc
|
|
}
|
|
|
|
func (b *builtinAddDateAndStringSig) Clone() builtinFunc {
|
|
newSig := &builtinAddDateAndStringSig{}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
// evalString evals a builtinAddDateAndStringSig.
|
|
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_addtime
|
|
func (b *builtinAddDateAndStringSig) evalString(row chunk.Row) (string, bool, error) {
|
|
arg0, isNull, err := b.args[0].EvalDuration(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return "", isNull, err
|
|
}
|
|
s, isNull, err := b.args[1].EvalString(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return "", isNull, err
|
|
}
|
|
if !isDuration(s) {
|
|
return "", true, nil
|
|
}
|
|
sc := b.ctx.GetSessionVars().StmtCtx
|
|
arg1, err := types.ParseDuration(sc, s, getFsp4TimeAddSub(s))
|
|
if err != nil {
|
|
if terror.ErrorEqual(err, types.ErrTruncatedWrongVal) {
|
|
sc.AppendWarning(err)
|
|
return "", true, nil
|
|
}
|
|
return "", true, err
|
|
}
|
|
result, err := arg0.Add(arg1)
|
|
return result.String(), err != nil, err
|
|
}
|
|
|
|
type convertTzFunctionClass struct {
|
|
baseFunctionClass
|
|
}
|
|
|
|
func (c *convertTzFunctionClass) getDecimal(ctx sessionctx.Context, arg Expression) int {
|
|
decimal := int(types.MaxFsp)
|
|
if dt, isConstant := arg.(*Constant); isConstant {
|
|
switch arg.GetType().EvalType() {
|
|
case types.ETInt:
|
|
decimal = 0
|
|
case types.ETReal, types.ETDecimal:
|
|
decimal = arg.GetType().Decimal
|
|
case types.ETString:
|
|
str, isNull, err := dt.EvalString(ctx, chunk.Row{})
|
|
if err == nil && !isNull {
|
|
decimal = types.DateFSP(str)
|
|
}
|
|
}
|
|
}
|
|
if decimal > int(types.MaxFsp) {
|
|
return int(types.MaxFsp)
|
|
}
|
|
if decimal < int(types.MinFsp) {
|
|
return int(types.MinFsp)
|
|
}
|
|
return decimal
|
|
}
|
|
|
|
func (c *convertTzFunctionClass) getFunction(ctx sessionctx.Context, args []Expression) (builtinFunc, error) {
|
|
if err := c.verifyArgs(args); err != nil {
|
|
return nil, err
|
|
}
|
|
// tzRegex holds the regex to check whether a string is a time zone.
|
|
tzRegex, err := regexp.Compile(`(^(\+|-)(0?[0-9]|1[0-2]):[0-5]?\d$)|(^\+13:00$)`)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
decimal := c.getDecimal(ctx, args[0])
|
|
bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETDatetime, types.ETDatetime, types.ETString, types.ETString)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
bf.tp.Decimal = decimal
|
|
sig := &builtinConvertTzSig{
|
|
baseBuiltinFunc: bf,
|
|
timezoneRegex: tzRegex,
|
|
}
|
|
sig.setPbCode(tipb.ScalarFuncSig_ConvertTz)
|
|
return sig, nil
|
|
}
|
|
|
|
type builtinConvertTzSig struct {
|
|
baseBuiltinFunc
|
|
timezoneRegex *regexp.Regexp
|
|
}
|
|
|
|
func (b *builtinConvertTzSig) Clone() builtinFunc {
|
|
newSig := &builtinConvertTzSig{timezoneRegex: b.timezoneRegex}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
// evalTime evals CONVERT_TZ(dt,from_tz,to_tz).
|
|
// `CONVERT_TZ` function returns NULL if the arguments are invalid.
|
|
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_convert-tz
|
|
func (b *builtinConvertTzSig) evalTime(row chunk.Row) (types.Time, bool, error) {
|
|
dt, isNull, err := b.args[0].EvalTime(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return types.ZeroTime, true, nil
|
|
}
|
|
|
|
fromTzStr, isNull, err := b.args[1].EvalString(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return types.ZeroTime, true, nil
|
|
}
|
|
|
|
toTzStr, isNull, err := b.args[2].EvalString(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return types.ZeroTime, true, nil
|
|
}
|
|
|
|
return b.convertTz(dt, fromTzStr, toTzStr)
|
|
}
|
|
|
|
func (b *builtinConvertTzSig) convertTz(dt types.Time, fromTzStr, toTzStr string) (types.Time, bool, error) {
|
|
if fromTzStr == "" || toTzStr == "" {
|
|
return types.ZeroTime, true, nil
|
|
}
|
|
fromTzMatched := b.timezoneRegex.MatchString(fromTzStr)
|
|
toTzMatched := b.timezoneRegex.MatchString(toTzStr)
|
|
|
|
if !fromTzMatched && !toTzMatched {
|
|
fromTz, err := time.LoadLocation(fromTzStr)
|
|
if err != nil {
|
|
return types.ZeroTime, true, nil
|
|
}
|
|
|
|
toTz, err := time.LoadLocation(toTzStr)
|
|
if err != nil {
|
|
return types.ZeroTime, true, nil
|
|
}
|
|
|
|
t, err := dt.GoTime(fromTz)
|
|
if err != nil {
|
|
return types.ZeroTime, true, nil
|
|
}
|
|
|
|
return types.NewTime(types.FromGoTime(t.In(toTz)), mysql.TypeDatetime, int8(b.tp.Decimal)), false, nil
|
|
}
|
|
if fromTzMatched && toTzMatched {
|
|
t, err := dt.GoTime(time.Local)
|
|
if err != nil {
|
|
return types.ZeroTime, true, nil
|
|
}
|
|
|
|
return types.NewTime(types.FromGoTime(t.Add(timeZone2Duration(toTzStr)-timeZone2Duration(fromTzStr))), mysql.TypeDatetime, int8(b.tp.Decimal)), false, nil
|
|
}
|
|
return types.ZeroTime, true, nil
|
|
}
|
|
|
|
type makeDateFunctionClass struct {
|
|
baseFunctionClass
|
|
}
|
|
|
|
func (c *makeDateFunctionClass) getFunction(ctx sessionctx.Context, args []Expression) (builtinFunc, error) {
|
|
if err := c.verifyArgs(args); err != nil {
|
|
return nil, err
|
|
}
|
|
bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETDatetime, types.ETInt, types.ETInt)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
tp := bf.tp
|
|
tp.Tp, tp.Flen, tp.Decimal = mysql.TypeDate, mysql.MaxDateWidth, 0
|
|
sig := &builtinMakeDateSig{bf}
|
|
sig.setPbCode(tipb.ScalarFuncSig_MakeDate)
|
|
return sig, nil
|
|
}
|
|
|
|
type builtinMakeDateSig struct {
|
|
baseBuiltinFunc
|
|
}
|
|
|
|
func (b *builtinMakeDateSig) Clone() builtinFunc {
|
|
newSig := &builtinMakeDateSig{}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
// evalTime evaluates a builtinMakeDateSig.
|
|
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_makedate
|
|
func (b *builtinMakeDateSig) evalTime(row chunk.Row) (d types.Time, isNull bool, err error) {
|
|
args := b.getArgs()
|
|
var year, dayOfYear int64
|
|
year, isNull, err = args[0].EvalInt(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return d, true, err
|
|
}
|
|
dayOfYear, isNull, err = args[1].EvalInt(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return d, true, err
|
|
}
|
|
if dayOfYear <= 0 || year < 0 || year > 9999 {
|
|
return d, true, nil
|
|
}
|
|
if year < 70 {
|
|
year += 2000
|
|
} else if year < 100 {
|
|
year += 1900
|
|
}
|
|
startTime := types.NewTime(types.FromDate(int(year), 1, 1, 0, 0, 0, 0), mysql.TypeDate, 0)
|
|
retTimestamp := types.TimestampDiff("DAY", types.ZeroDate, startTime)
|
|
if retTimestamp == 0 {
|
|
return d, true, handleInvalidTimeError(b.ctx, types.ErrWrongValue.GenWithStackByArgs(types.DateTimeStr, startTime.String()))
|
|
}
|
|
ret := types.TimeFromDays(retTimestamp + dayOfYear - 1)
|
|
if ret.IsZero() || ret.Year() > 9999 {
|
|
return d, true, nil
|
|
}
|
|
return ret, false, nil
|
|
}
|
|
|
|
type makeTimeFunctionClass struct {
|
|
baseFunctionClass
|
|
}
|
|
|
|
func (c *makeTimeFunctionClass) getFunction(ctx sessionctx.Context, args []Expression) (builtinFunc, error) {
|
|
if err := c.verifyArgs(args); err != nil {
|
|
return nil, err
|
|
}
|
|
tp, flen, decimal := args[2].GetType().EvalType(), 10, 0
|
|
switch tp {
|
|
case types.ETInt:
|
|
case types.ETReal, types.ETDecimal:
|
|
decimal = args[2].GetType().Decimal
|
|
if decimal > 6 || decimal == types.UnspecifiedLength {
|
|
decimal = 6
|
|
}
|
|
if decimal > 0 {
|
|
flen += 1 + decimal
|
|
}
|
|
default:
|
|
flen, decimal = 17, 6
|
|
}
|
|
// MySQL will cast the first and second arguments to INT, and the third argument to DECIMAL.
|
|
bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETDuration, types.ETInt, types.ETInt, types.ETReal)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
bf.tp.Flen, bf.tp.Decimal = flen, decimal
|
|
sig := &builtinMakeTimeSig{bf}
|
|
sig.setPbCode(tipb.ScalarFuncSig_MakeTime)
|
|
return sig, nil
|
|
}
|
|
|
|
type builtinMakeTimeSig struct {
|
|
baseBuiltinFunc
|
|
}
|
|
|
|
func (b *builtinMakeTimeSig) Clone() builtinFunc {
|
|
newSig := &builtinMakeTimeSig{}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
func (b *builtinMakeTimeSig) makeTime(hour int64, minute int64, second float64, hourUnsignedFlag bool) (types.Duration, error) {
|
|
var overflow bool
|
|
// MySQL TIME datatype: https://dev.mysql.com/doc/refman/5.7/en/time.html
|
|
// ranges from '-838:59:59.000000' to '838:59:59.000000'
|
|
if hour < 0 && hourUnsignedFlag {
|
|
hour = 838
|
|
overflow = true
|
|
}
|
|
if hour < -838 {
|
|
hour = -838
|
|
overflow = true
|
|
} else if hour > 838 {
|
|
hour = 838
|
|
overflow = true
|
|
}
|
|
if hour == -838 || hour == 838 {
|
|
if second > 59 {
|
|
second = 59
|
|
}
|
|
}
|
|
if overflow {
|
|
minute = 59
|
|
second = 59
|
|
}
|
|
fsp := b.tp.Decimal
|
|
return types.ParseDuration(b.ctx.GetSessionVars().StmtCtx, fmt.Sprintf("%02d:%02d:%v", hour, minute, second), int8(fsp))
|
|
}
|
|
|
|
// evalDuration evals a builtinMakeTimeIntSig.
|
|
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_maketime
|
|
func (b *builtinMakeTimeSig) evalDuration(row chunk.Row) (types.Duration, bool, error) {
|
|
dur := types.ZeroDuration
|
|
dur.Fsp = types.MaxFsp
|
|
hour, isNull, err := b.args[0].EvalInt(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return dur, isNull, err
|
|
}
|
|
minute, isNull, err := b.args[1].EvalInt(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return dur, isNull, err
|
|
}
|
|
if minute < 0 || minute >= 60 {
|
|
return dur, true, nil
|
|
}
|
|
second, isNull, err := b.args[2].EvalReal(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return dur, isNull, err
|
|
}
|
|
if second < 0 || second >= 60 {
|
|
return dur, true, nil
|
|
}
|
|
dur, err = b.makeTime(hour, minute, second, mysql.HasUnsignedFlag(b.args[0].GetType().Flag))
|
|
if err != nil {
|
|
return dur, true, err
|
|
}
|
|
return dur, false, nil
|
|
}
|
|
|
|
type periodAddFunctionClass struct {
|
|
baseFunctionClass
|
|
}
|
|
|
|
func (c *periodAddFunctionClass) getFunction(ctx sessionctx.Context, args []Expression) (builtinFunc, error) {
|
|
if err := c.verifyArgs(args); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETInt, types.ETInt, types.ETInt)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
bf.tp.Flen = 6
|
|
sig := &builtinPeriodAddSig{bf}
|
|
return sig, nil
|
|
}
|
|
|
|
// validPeriod checks if this period is valid, it comes from MySQL 8.0+.
|
|
func validPeriod(p int64) bool {
|
|
return !(p < 0 || p%100 == 0 || p%100 > 12)
|
|
}
|
|
|
|
// period2Month converts a period to months, in which period is represented in the format of YYMM or YYYYMM.
|
|
// Note that the period argument is not a date value.
|
|
func period2Month(period uint64) uint64 {
|
|
if period == 0 {
|
|
return 0
|
|
}
|
|
|
|
year, month := period/100, period%100
|
|
if year < 70 {
|
|
year += 2000
|
|
} else if year < 100 {
|
|
year += 1900
|
|
}
|
|
|
|
return year*12 + month - 1
|
|
}
|
|
|
|
// month2Period converts a month to a period.
|
|
func month2Period(month uint64) uint64 {
|
|
if month == 0 {
|
|
return 0
|
|
}
|
|
|
|
year := month / 12
|
|
if year < 70 {
|
|
year += 2000
|
|
} else if year < 100 {
|
|
year += 1900
|
|
}
|
|
|
|
return year*100 + month%12 + 1
|
|
}
|
|
|
|
type builtinPeriodAddSig struct {
|
|
baseBuiltinFunc
|
|
}
|
|
|
|
func (b *builtinPeriodAddSig) Clone() builtinFunc {
|
|
newSig := &builtinPeriodAddSig{}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
// evalInt evals PERIOD_ADD(P,N).
|
|
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_period-add
|
|
func (b *builtinPeriodAddSig) evalInt(row chunk.Row) (int64, bool, error) {
|
|
p, isNull, err := b.args[0].EvalInt(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return 0, true, err
|
|
}
|
|
|
|
n, isNull, err := b.args[1].EvalInt(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return 0, true, err
|
|
}
|
|
|
|
// in MySQL, if p is invalid but n is NULL, the result is NULL, so we have to check if n is NULL first.
|
|
if !validPeriod(p) {
|
|
return 0, false, errIncorrectArgs.GenWithStackByArgs("period_add")
|
|
}
|
|
|
|
sumMonth := int64(period2Month(uint64(p))) + n
|
|
return int64(month2Period(uint64(sumMonth))), false, nil
|
|
}
|
|
|
|
type periodDiffFunctionClass struct {
|
|
baseFunctionClass
|
|
}
|
|
|
|
func (c *periodDiffFunctionClass) getFunction(ctx sessionctx.Context, args []Expression) (builtinFunc, error) {
|
|
if err := c.verifyArgs(args); err != nil {
|
|
return nil, err
|
|
}
|
|
bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETInt, types.ETInt, types.ETInt)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
bf.tp.Flen = 6
|
|
sig := &builtinPeriodDiffSig{bf}
|
|
return sig, nil
|
|
}
|
|
|
|
type builtinPeriodDiffSig struct {
|
|
baseBuiltinFunc
|
|
}
|
|
|
|
func (b *builtinPeriodDiffSig) Clone() builtinFunc {
|
|
newSig := &builtinPeriodDiffSig{}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
// evalInt evals PERIOD_DIFF(P1,P2).
|
|
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_period-diff
|
|
func (b *builtinPeriodDiffSig) evalInt(row chunk.Row) (int64, bool, error) {
|
|
p1, isNull, err := b.args[0].EvalInt(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return 0, isNull, err
|
|
}
|
|
|
|
p2, isNull, err := b.args[1].EvalInt(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return 0, isNull, err
|
|
}
|
|
|
|
if !validPeriod(p1) {
|
|
return 0, false, errIncorrectArgs.GenWithStackByArgs("period_diff")
|
|
}
|
|
|
|
if !validPeriod(p2) {
|
|
return 0, false, errIncorrectArgs.GenWithStackByArgs("period_diff")
|
|
}
|
|
|
|
return int64(period2Month(uint64(p1)) - period2Month(uint64(p2))), false, nil
|
|
}
|
|
|
|
type quarterFunctionClass struct {
|
|
baseFunctionClass
|
|
}
|
|
|
|
func (c *quarterFunctionClass) getFunction(ctx sessionctx.Context, args []Expression) (builtinFunc, error) {
|
|
if err := c.verifyArgs(args); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETInt, types.ETDatetime)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
bf.tp.Flen = 1
|
|
|
|
sig := &builtinQuarterSig{bf}
|
|
sig.setPbCode(tipb.ScalarFuncSig_Quarter)
|
|
return sig, nil
|
|
}
|
|
|
|
type builtinQuarterSig struct {
|
|
baseBuiltinFunc
|
|
}
|
|
|
|
func (b *builtinQuarterSig) Clone() builtinFunc {
|
|
newSig := &builtinQuarterSig{}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
// evalInt evals QUARTER(date).
|
|
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_quarter
|
|
func (b *builtinQuarterSig) evalInt(row chunk.Row) (int64, bool, error) {
|
|
date, isNull, err := b.args[0].EvalTime(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return 0, true, handleInvalidTimeError(b.ctx, err)
|
|
}
|
|
|
|
if date.IsZero() {
|
|
// MySQL compatibility, #11203
|
|
// 0 | 0.0 should be converted to 0 value (not null)
|
|
n, err := date.ToNumber().ToInt()
|
|
isOriginalIntOrDecimalZero := err == nil && n == 0
|
|
// Args like "0000-00-00", "0000-00-00 00:00:00" set Fsp to 6
|
|
isOriginalStringZero := date.Fsp() > 0
|
|
if isOriginalIntOrDecimalZero && !isOriginalStringZero {
|
|
return 0, false, nil
|
|
}
|
|
return 0, true, handleInvalidTimeError(b.ctx, types.ErrWrongValue.GenWithStackByArgs(types.DateTimeStr, date.String()))
|
|
}
|
|
|
|
return int64((date.Month() + 2) / 3), false, nil
|
|
}
|
|
|
|
type secToTimeFunctionClass struct {
|
|
baseFunctionClass
|
|
}
|
|
|
|
func (c *secToTimeFunctionClass) getFunction(ctx sessionctx.Context, args []Expression) (builtinFunc, error) {
|
|
if err := c.verifyArgs(args); err != nil {
|
|
return nil, err
|
|
}
|
|
var retFlen, retFsp int
|
|
argType := args[0].GetType()
|
|
argEvalTp := argType.EvalType()
|
|
if argEvalTp == types.ETString {
|
|
retFsp = types.UnspecifiedLength
|
|
} else {
|
|
retFsp = argType.Decimal
|
|
}
|
|
if retFsp > int(types.MaxFsp) || retFsp == int(types.UnspecifiedFsp) {
|
|
retFsp = int(types.MaxFsp)
|
|
} else if retFsp < int(types.MinFsp) {
|
|
retFsp = int(types.MinFsp)
|
|
}
|
|
retFlen = 10
|
|
if retFsp > 0 {
|
|
retFlen += 1 + retFsp
|
|
}
|
|
bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETDuration, types.ETReal)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
bf.tp.Flen, bf.tp.Decimal = retFlen, retFsp
|
|
sig := &builtinSecToTimeSig{bf}
|
|
sig.setPbCode(tipb.ScalarFuncSig_SecToTime)
|
|
return sig, nil
|
|
}
|
|
|
|
type builtinSecToTimeSig struct {
|
|
baseBuiltinFunc
|
|
}
|
|
|
|
func (b *builtinSecToTimeSig) Clone() builtinFunc {
|
|
newSig := &builtinSecToTimeSig{}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
// evalDuration evals SEC_TO_TIME(seconds).
|
|
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_sec-to-time
|
|
func (b *builtinSecToTimeSig) evalDuration(row chunk.Row) (types.Duration, bool, error) {
|
|
secondsFloat, isNull, err := b.args[0].EvalReal(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return types.Duration{}, isNull, err
|
|
}
|
|
var (
|
|
hour int64
|
|
minute int64
|
|
second int64
|
|
demical float64
|
|
secondDemical float64
|
|
negative string
|
|
)
|
|
|
|
if secondsFloat < 0 {
|
|
negative = "-"
|
|
secondsFloat = math.Abs(secondsFloat)
|
|
}
|
|
seconds := int64(secondsFloat)
|
|
demical = secondsFloat - float64(seconds)
|
|
|
|
hour = seconds / 3600
|
|
if hour > 838 {
|
|
hour = 838
|
|
minute = 59
|
|
second = 59
|
|
} else {
|
|
minute = seconds % 3600 / 60
|
|
second = seconds % 60
|
|
}
|
|
secondDemical = float64(second) + demical
|
|
|
|
var dur types.Duration
|
|
dur, err = types.ParseDuration(b.ctx.GetSessionVars().StmtCtx, fmt.Sprintf("%s%02d:%02d:%s", negative, hour, minute, strconv.FormatFloat(secondDemical, 'f', -1, 64)), int8(b.tp.Decimal))
|
|
if err != nil {
|
|
return types.Duration{}, err != nil, err
|
|
}
|
|
return dur, false, nil
|
|
}
|
|
|
|
type subTimeFunctionClass struct {
|
|
baseFunctionClass
|
|
}
|
|
|
|
func (c *subTimeFunctionClass) getFunction(ctx sessionctx.Context, args []Expression) (sig builtinFunc, err error) {
|
|
if err = c.verifyArgs(args); err != nil {
|
|
return nil, err
|
|
}
|
|
tp1, tp2, bf, err := getBf4TimeAddSub(ctx, c.funcName, args)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
switch tp1.Tp {
|
|
case mysql.TypeDatetime, mysql.TypeTimestamp:
|
|
switch tp2.Tp {
|
|
case mysql.TypeDuration:
|
|
sig = &builtinSubDatetimeAndDurationSig{bf}
|
|
sig.setPbCode(tipb.ScalarFuncSig_SubDatetimeAndDuration)
|
|
case mysql.TypeDatetime, mysql.TypeTimestamp:
|
|
sig = &builtinSubTimeDateTimeNullSig{bf}
|
|
sig.setPbCode(tipb.ScalarFuncSig_SubTimeDateTimeNull)
|
|
default:
|
|
sig = &builtinSubDatetimeAndStringSig{bf}
|
|
sig.setPbCode(tipb.ScalarFuncSig_SubDatetimeAndString)
|
|
}
|
|
case mysql.TypeDate:
|
|
bf.tp.Charset, bf.tp.Collate = ctx.GetSessionVars().GetCharsetInfo()
|
|
switch tp2.Tp {
|
|
case mysql.TypeDuration:
|
|
sig = &builtinSubDateAndDurationSig{bf}
|
|
sig.setPbCode(tipb.ScalarFuncSig_SubDateAndDuration)
|
|
case mysql.TypeDatetime, mysql.TypeTimestamp:
|
|
sig = &builtinSubTimeStringNullSig{bf}
|
|
sig.setPbCode(tipb.ScalarFuncSig_SubTimeStringNull)
|
|
default:
|
|
sig = &builtinSubDateAndStringSig{bf}
|
|
sig.setPbCode(tipb.ScalarFuncSig_SubDateAndString)
|
|
}
|
|
case mysql.TypeDuration:
|
|
switch tp2.Tp {
|
|
case mysql.TypeDuration:
|
|
sig = &builtinSubDurationAndDurationSig{bf}
|
|
sig.setPbCode(tipb.ScalarFuncSig_SubDurationAndDuration)
|
|
case mysql.TypeDatetime, mysql.TypeTimestamp:
|
|
sig = &builtinSubTimeDurationNullSig{bf}
|
|
sig.setPbCode(tipb.ScalarFuncSig_SubTimeDurationNull)
|
|
default:
|
|
sig = &builtinSubDurationAndStringSig{bf}
|
|
sig.setPbCode(tipb.ScalarFuncSig_SubDurationAndString)
|
|
}
|
|
default:
|
|
switch tp2.Tp {
|
|
case mysql.TypeDuration:
|
|
sig = &builtinSubStringAndDurationSig{bf}
|
|
sig.setPbCode(tipb.ScalarFuncSig_SubStringAndDuration)
|
|
case mysql.TypeDatetime, mysql.TypeTimestamp:
|
|
sig = &builtinSubTimeStringNullSig{bf}
|
|
sig.setPbCode(tipb.ScalarFuncSig_SubTimeStringNull)
|
|
default:
|
|
sig = &builtinSubStringAndStringSig{bf}
|
|
sig.setPbCode(tipb.ScalarFuncSig_SubStringAndString)
|
|
}
|
|
}
|
|
return sig, nil
|
|
}
|
|
|
|
type builtinSubDatetimeAndDurationSig struct {
|
|
baseBuiltinFunc
|
|
}
|
|
|
|
func (b *builtinSubDatetimeAndDurationSig) Clone() builtinFunc {
|
|
newSig := &builtinSubDatetimeAndDurationSig{}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
// evalTime evals a builtinSubDatetimeAndDurationSig.
|
|
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_subtime
|
|
func (b *builtinSubDatetimeAndDurationSig) evalTime(row chunk.Row) (types.Time, bool, error) {
|
|
arg0, isNull, err := b.args[0].EvalTime(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return types.ZeroDatetime, isNull, err
|
|
}
|
|
arg1, isNull, err := b.args[1].EvalDuration(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return types.ZeroDatetime, isNull, err
|
|
}
|
|
sc := b.ctx.GetSessionVars().StmtCtx
|
|
arg1time, err := arg1.ConvertToTime(sc, mysql.TypeDatetime)
|
|
if err != nil {
|
|
return arg1time, true, err
|
|
}
|
|
tmpDuration := arg0.Sub(sc, &arg1time)
|
|
result, err := tmpDuration.ConvertToTime(sc, arg0.Type())
|
|
return result, err != nil, err
|
|
}
|
|
|
|
type builtinSubDatetimeAndStringSig struct {
|
|
baseBuiltinFunc
|
|
}
|
|
|
|
func (b *builtinSubDatetimeAndStringSig) Clone() builtinFunc {
|
|
newSig := &builtinSubDatetimeAndStringSig{}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
// evalTime evals a builtinSubDatetimeAndStringSig.
|
|
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_subtime
|
|
func (b *builtinSubDatetimeAndStringSig) evalTime(row chunk.Row) (types.Time, bool, error) {
|
|
arg0, isNull, err := b.args[0].EvalTime(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return types.ZeroDatetime, isNull, err
|
|
}
|
|
s, isNull, err := b.args[1].EvalString(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return types.ZeroDatetime, isNull, err
|
|
}
|
|
if err != nil {
|
|
return types.ZeroDatetime, true, err
|
|
}
|
|
if !isDuration(s) {
|
|
return types.ZeroDatetime, true, nil
|
|
}
|
|
sc := b.ctx.GetSessionVars().StmtCtx
|
|
arg1, err := types.ParseDuration(sc, s, types.GetFsp(s))
|
|
if err != nil {
|
|
if terror.ErrorEqual(err, types.ErrTruncatedWrongVal) {
|
|
sc.AppendWarning(err)
|
|
return types.ZeroDatetime, true, nil
|
|
}
|
|
return types.ZeroDatetime, true, err
|
|
}
|
|
arg1time, err := arg1.ConvertToTime(sc, mysql.TypeDatetime)
|
|
if err != nil {
|
|
return types.ZeroDatetime, true, err
|
|
}
|
|
tmpDuration := arg0.Sub(sc, &arg1time)
|
|
result, err := tmpDuration.ConvertToTime(sc, mysql.TypeDatetime)
|
|
return result, err != nil, err
|
|
}
|
|
|
|
type builtinSubTimeDateTimeNullSig struct {
|
|
baseBuiltinFunc
|
|
}
|
|
|
|
func (b *builtinSubTimeDateTimeNullSig) Clone() builtinFunc {
|
|
newSig := &builtinSubTimeDateTimeNullSig{}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
// evalTime evals a builtinSubTimeDateTimeNullSig.
|
|
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_subtime
|
|
func (b *builtinSubTimeDateTimeNullSig) evalTime(row chunk.Row) (types.Time, bool, error) {
|
|
return types.ZeroDatetime, true, nil
|
|
}
|
|
|
|
type builtinSubStringAndDurationSig struct {
|
|
baseBuiltinFunc
|
|
}
|
|
|
|
func (b *builtinSubStringAndDurationSig) Clone() builtinFunc {
|
|
newSig := &builtinSubStringAndDurationSig{}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
// evalString evals a builtinSubStringAndDurationSig.
|
|
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_subtime
|
|
func (b *builtinSubStringAndDurationSig) evalString(row chunk.Row) (result string, isNull bool, err error) {
|
|
var (
|
|
arg0 string
|
|
arg1 types.Duration
|
|
)
|
|
arg0, isNull, err = b.args[0].EvalString(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return "", isNull, err
|
|
}
|
|
arg1, isNull, err = b.args[1].EvalDuration(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return "", isNull, err
|
|
}
|
|
sc := b.ctx.GetSessionVars().StmtCtx
|
|
if isDuration(arg0) {
|
|
result, err = strDurationSubDuration(sc, arg0, arg1)
|
|
if err != nil {
|
|
if terror.ErrorEqual(err, types.ErrTruncatedWrongVal) {
|
|
sc.AppendWarning(err)
|
|
return "", true, nil
|
|
}
|
|
return "", true, err
|
|
}
|
|
return result, false, nil
|
|
}
|
|
result, err = strDatetimeSubDuration(sc, arg0, arg1)
|
|
return result, err != nil, err
|
|
}
|
|
|
|
type builtinSubStringAndStringSig struct {
|
|
baseBuiltinFunc
|
|
}
|
|
|
|
func (b *builtinSubStringAndStringSig) Clone() builtinFunc {
|
|
newSig := &builtinSubStringAndStringSig{}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
// evalString evals a builtinSubStringAndStringSig.
|
|
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_subtime
|
|
func (b *builtinSubStringAndStringSig) evalString(row chunk.Row) (result string, isNull bool, err error) {
|
|
var (
|
|
s, arg0 string
|
|
arg1 types.Duration
|
|
)
|
|
arg0, isNull, err = b.args[0].EvalString(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return "", isNull, err
|
|
}
|
|
arg1Type := b.args[1].GetType()
|
|
if mysql.HasBinaryFlag(arg1Type.Flag) {
|
|
return "", true, nil
|
|
}
|
|
s, isNull, err = b.args[1].EvalString(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return "", isNull, err
|
|
}
|
|
sc := b.ctx.GetSessionVars().StmtCtx
|
|
arg1, err = types.ParseDuration(sc, s, getFsp4TimeAddSub(s))
|
|
if err != nil {
|
|
if terror.ErrorEqual(err, types.ErrTruncatedWrongVal) {
|
|
sc.AppendWarning(err)
|
|
return "", true, nil
|
|
}
|
|
return "", true, err
|
|
}
|
|
if isDuration(arg0) {
|
|
result, err = strDurationSubDuration(sc, arg0, arg1)
|
|
if err != nil {
|
|
if terror.ErrorEqual(err, types.ErrTruncatedWrongVal) {
|
|
sc.AppendWarning(err)
|
|
return "", true, nil
|
|
}
|
|
return "", true, err
|
|
}
|
|
return result, false, nil
|
|
}
|
|
result, err = strDatetimeSubDuration(sc, arg0, arg1)
|
|
return result, err != nil, err
|
|
}
|
|
|
|
type builtinSubTimeStringNullSig struct {
|
|
baseBuiltinFunc
|
|
}
|
|
|
|
func (b *builtinSubTimeStringNullSig) Clone() builtinFunc {
|
|
newSig := &builtinSubTimeStringNullSig{}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
// evalString evals a builtinSubTimeStringNullSig.
|
|
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_subtime
|
|
func (b *builtinSubTimeStringNullSig) evalString(row chunk.Row) (string, bool, error) {
|
|
return "", true, nil
|
|
}
|
|
|
|
type builtinSubDurationAndDurationSig struct {
|
|
baseBuiltinFunc
|
|
}
|
|
|
|
func (b *builtinSubDurationAndDurationSig) Clone() builtinFunc {
|
|
newSig := &builtinSubDurationAndDurationSig{}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
// evalDuration evals a builtinSubDurationAndDurationSig.
|
|
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_subtime
|
|
func (b *builtinSubDurationAndDurationSig) evalDuration(row chunk.Row) (types.Duration, bool, error) {
|
|
arg0, isNull, err := b.args[0].EvalDuration(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return types.ZeroDuration, isNull, err
|
|
}
|
|
arg1, isNull, err := b.args[1].EvalDuration(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return types.ZeroDuration, isNull, err
|
|
}
|
|
result, err := arg0.Sub(arg1)
|
|
if err != nil {
|
|
return types.ZeroDuration, true, err
|
|
}
|
|
return result, false, nil
|
|
}
|
|
|
|
type builtinSubDurationAndStringSig struct {
|
|
baseBuiltinFunc
|
|
}
|
|
|
|
func (b *builtinSubDurationAndStringSig) Clone() builtinFunc {
|
|
newSig := &builtinSubDurationAndStringSig{}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
// evalDuration evals a builtinSubDurationAndStringSig.
|
|
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_subtime
|
|
func (b *builtinSubDurationAndStringSig) evalDuration(row chunk.Row) (types.Duration, bool, error) {
|
|
arg0, isNull, err := b.args[0].EvalDuration(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return types.ZeroDuration, isNull, err
|
|
}
|
|
s, isNull, err := b.args[1].EvalString(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return types.ZeroDuration, isNull, err
|
|
}
|
|
if !isDuration(s) {
|
|
return types.ZeroDuration, true, nil
|
|
}
|
|
sc := b.ctx.GetSessionVars().StmtCtx
|
|
arg1, err := types.ParseDuration(sc, s, types.GetFsp(s))
|
|
if err != nil {
|
|
if terror.ErrorEqual(err, types.ErrTruncatedWrongVal) {
|
|
sc.AppendWarning(err)
|
|
return types.ZeroDuration, true, nil
|
|
}
|
|
return types.ZeroDuration, true, err
|
|
}
|
|
result, err := arg0.Sub(arg1)
|
|
return result, err != nil, err
|
|
}
|
|
|
|
type builtinSubTimeDurationNullSig struct {
|
|
baseBuiltinFunc
|
|
}
|
|
|
|
func (b *builtinSubTimeDurationNullSig) Clone() builtinFunc {
|
|
newSig := &builtinSubTimeDurationNullSig{}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
// evalDuration evals a builtinSubTimeDurationNullSig.
|
|
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_subtime
|
|
func (b *builtinSubTimeDurationNullSig) evalDuration(row chunk.Row) (types.Duration, bool, error) {
|
|
return types.ZeroDuration, true, nil
|
|
}
|
|
|
|
type builtinSubDateAndDurationSig struct {
|
|
baseBuiltinFunc
|
|
}
|
|
|
|
func (b *builtinSubDateAndDurationSig) Clone() builtinFunc {
|
|
newSig := &builtinSubDateAndDurationSig{}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
// evalString evals a builtinSubDateAndDurationSig.
|
|
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_subtime
|
|
func (b *builtinSubDateAndDurationSig) evalString(row chunk.Row) (string, bool, error) {
|
|
arg0, isNull, err := b.args[0].EvalDuration(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return "", isNull, err
|
|
}
|
|
arg1, isNull, err := b.args[1].EvalDuration(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return "", isNull, err
|
|
}
|
|
result, err := arg0.Sub(arg1)
|
|
return result.String(), err != nil, err
|
|
}
|
|
|
|
type builtinSubDateAndStringSig struct {
|
|
baseBuiltinFunc
|
|
}
|
|
|
|
func (b *builtinSubDateAndStringSig) Clone() builtinFunc {
|
|
newSig := &builtinSubDateAndStringSig{}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
// evalString evals a builtinSubDateAndStringSig.
|
|
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_subtime
|
|
func (b *builtinSubDateAndStringSig) evalString(row chunk.Row) (string, bool, error) {
|
|
arg0, isNull, err := b.args[0].EvalDuration(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return "", isNull, err
|
|
}
|
|
s, isNull, err := b.args[1].EvalString(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return "", isNull, err
|
|
}
|
|
if !isDuration(s) {
|
|
return "", true, nil
|
|
}
|
|
sc := b.ctx.GetSessionVars().StmtCtx
|
|
arg1, err := types.ParseDuration(sc, s, getFsp4TimeAddSub(s))
|
|
if err != nil {
|
|
if terror.ErrorEqual(err, types.ErrTruncatedWrongVal) {
|
|
sc.AppendWarning(err)
|
|
return "", true, nil
|
|
}
|
|
return "", true, err
|
|
}
|
|
result, err := arg0.Sub(arg1)
|
|
if err != nil {
|
|
return "", true, err
|
|
}
|
|
return result.String(), false, nil
|
|
}
|
|
|
|
type timeFormatFunctionClass struct {
|
|
baseFunctionClass
|
|
}
|
|
|
|
func (c *timeFormatFunctionClass) getFunction(ctx sessionctx.Context, args []Expression) (builtinFunc, error) {
|
|
if err := c.verifyArgs(args); err != nil {
|
|
return nil, err
|
|
}
|
|
bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETString, types.ETDuration, types.ETString)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
// worst case: formatMask=%r%r%r...%r, each %r takes 11 characters
|
|
bf.tp.Flen = (args[1].GetType().Flen + 1) / 2 * 11
|
|
sig := &builtinTimeFormatSig{bf}
|
|
sig.setPbCode(tipb.ScalarFuncSig_TimeFormat)
|
|
return sig, nil
|
|
}
|
|
|
|
type builtinTimeFormatSig struct {
|
|
baseBuiltinFunc
|
|
}
|
|
|
|
func (b *builtinTimeFormatSig) Clone() builtinFunc {
|
|
newSig := &builtinTimeFormatSig{}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
// evalString evals a builtinTimeFormatSig.
|
|
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_time-format
|
|
func (b *builtinTimeFormatSig) evalString(row chunk.Row) (string, bool, error) {
|
|
dur, isNull, err := b.args[0].EvalDuration(b.ctx, row)
|
|
// if err != nil, then dur is ZeroDuration, outputs 00:00:00 in this case which follows the behavior of mysql.
|
|
if err != nil {
|
|
logutil.BgLogger().Warn("time_format.args[0].EvalDuration failed", zap.Error(err))
|
|
}
|
|
if isNull {
|
|
return "", isNull, err
|
|
}
|
|
formatMask, isNull, err := b.args[1].EvalString(b.ctx, row)
|
|
if err != nil || isNull {
|
|
return "", isNull, err
|
|
}
|
|
res, err := b.formatTime(b.ctx, dur, formatMask)
|
|
return res, isNull, err
|
|
}
|
|
|
|
// formatTime see https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_time-format
|
|
func (b *builtinTimeFormatSig) formatTime(ctx sessionctx.Context, t types.Duration, formatMask string) (res string, err error) {
|
|
return t.DurationFormat(formatMask)
|
|
}
|
|
|
|
type timeToSecFunctionClass struct {
|
|
baseFunctionClass
|
|
}
|
|
|
|
func (c *timeToSecFunctionClass) getFunction(ctx sessionctx.Context, args []Expression) (builtinFunc, error) {
|
|
if err := c.verifyArgs(args); err != nil {
|
|
return nil, err
|
|
}
|
|
bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETInt, types.ETDuration)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
bf.tp.Flen = 10
|
|
sig := &builtinTimeToSecSig{bf}
|
|
sig.setPbCode(tipb.ScalarFuncSig_TimeToSec)
|
|
return sig, nil
|
|
}
|
|
|
|
type builtinTimeToSecSig struct {
|
|
baseBuiltinFunc
|
|
}
|
|
|
|
func (b *builtinTimeToSecSig) Clone() builtinFunc {
|
|
newSig := &builtinTimeToSecSig{}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
// evalInt evals TIME_TO_SEC(time).
|
|
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_time-to-sec
|
|
func (b *builtinTimeToSecSig) evalInt(row chunk.Row) (int64, bool, error) {
|
|
duration, isNull, err := b.args[0].EvalDuration(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return 0, isNull, err
|
|
}
|
|
var sign int
|
|
if duration.Duration >= 0 {
|
|
sign = 1
|
|
} else {
|
|
sign = -1
|
|
}
|
|
return int64(sign * (duration.Hour()*3600 + duration.Minute()*60 + duration.Second())), false, nil
|
|
}
|
|
|
|
type timestampAddFunctionClass struct {
|
|
baseFunctionClass
|
|
}
|
|
|
|
func (c *timestampAddFunctionClass) getFunction(ctx sessionctx.Context, args []Expression) (builtinFunc, error) {
|
|
if err := c.verifyArgs(args); err != nil {
|
|
return nil, err
|
|
}
|
|
bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETString, types.ETString, types.ETInt, types.ETDatetime)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
bf.tp = &types.FieldType{Tp: mysql.TypeString, Flen: mysql.MaxDatetimeWidthNoFsp, Decimal: types.UnspecifiedLength}
|
|
sig := &builtinTimestampAddSig{bf}
|
|
sig.setPbCode(tipb.ScalarFuncSig_TimestampAdd)
|
|
return sig, nil
|
|
|
|
}
|
|
|
|
type builtinTimestampAddSig struct {
|
|
baseBuiltinFunc
|
|
}
|
|
|
|
func (b *builtinTimestampAddSig) Clone() builtinFunc {
|
|
newSig := &builtinTimestampAddSig{}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
// evalString evals a builtinTimestampAddSig.
|
|
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_timestampadd
|
|
func (b *builtinTimestampAddSig) evalString(row chunk.Row) (string, bool, error) {
|
|
unit, isNull, err := b.args[0].EvalString(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return "", isNull, err
|
|
}
|
|
v, isNull, err := b.args[1].EvalInt(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return "", isNull, err
|
|
}
|
|
arg, isNull, err := b.args[2].EvalTime(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return "", isNull, err
|
|
}
|
|
tm1, err := arg.GoTime(time.Local)
|
|
if err != nil {
|
|
return "", isNull, err
|
|
}
|
|
var tb time.Time
|
|
fsp := types.DefaultFsp
|
|
switch unit {
|
|
case "MICROSECOND":
|
|
tb = tm1.Add(time.Duration(v) * time.Microsecond)
|
|
fsp = types.MaxFsp
|
|
case "SECOND":
|
|
tb = tm1.Add(time.Duration(v) * time.Second)
|
|
case "MINUTE":
|
|
tb = tm1.Add(time.Duration(v) * time.Minute)
|
|
case "HOUR":
|
|
tb = tm1.Add(time.Duration(v) * time.Hour)
|
|
case "DAY":
|
|
tb = tm1.AddDate(0, 0, int(v))
|
|
case "WEEK":
|
|
tb = tm1.AddDate(0, 0, 7*int(v))
|
|
case "MONTH":
|
|
tb = tm1.AddDate(0, int(v), 0)
|
|
case "QUARTER":
|
|
tb = tm1.AddDate(0, 3*int(v), 0)
|
|
case "YEAR":
|
|
tb = tm1.AddDate(int(v), 0, 0)
|
|
default:
|
|
return "", true, types.ErrWrongValue.GenWithStackByArgs(types.TimeStr, unit)
|
|
}
|
|
r := types.NewTime(types.FromGoTime(tb), b.resolveType(arg.Type(), unit), fsp)
|
|
if err = r.Check(b.ctx.GetSessionVars().StmtCtx); err != nil {
|
|
return "", true, handleInvalidTimeError(b.ctx, err)
|
|
}
|
|
return r.String(), false, nil
|
|
}
|
|
|
|
func (b *builtinTimestampAddSig) resolveType(typ uint8, unit string) uint8 {
|
|
// The approach below is from MySQL.
|
|
// The field type for the result of an Item_date function is defined as
|
|
// follows:
|
|
//
|
|
//- If first arg is a MYSQL_TYPE_DATETIME result is MYSQL_TYPE_DATETIME
|
|
//- If first arg is a MYSQL_TYPE_DATE and the interval type uses hours,
|
|
// minutes, seconds or microsecond then type is MYSQL_TYPE_DATETIME.
|
|
//- Otherwise the result is MYSQL_TYPE_STRING
|
|
// (This is because you can't know if the string contains a DATE, MYSQL_TIME
|
|
// or DATETIME argument)
|
|
if typ == mysql.TypeDate && (unit == "HOUR" || unit == "MINUTE" || unit == "SECOND" || unit == "MICROSECOND") {
|
|
return mysql.TypeDatetime
|
|
}
|
|
return typ
|
|
}
|
|
|
|
type toDaysFunctionClass struct {
|
|
baseFunctionClass
|
|
}
|
|
|
|
func (c *toDaysFunctionClass) getFunction(ctx sessionctx.Context, args []Expression) (builtinFunc, error) {
|
|
if err := c.verifyArgs(args); err != nil {
|
|
return nil, err
|
|
}
|
|
bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETInt, types.ETDatetime)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
sig := &builtinToDaysSig{bf}
|
|
sig.setPbCode(tipb.ScalarFuncSig_ToDays)
|
|
return sig, nil
|
|
}
|
|
|
|
type builtinToDaysSig struct {
|
|
baseBuiltinFunc
|
|
}
|
|
|
|
func (b *builtinToDaysSig) Clone() builtinFunc {
|
|
newSig := &builtinToDaysSig{}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
// evalInt evals a builtinToDaysSig.
|
|
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_to-days
|
|
func (b *builtinToDaysSig) evalInt(row chunk.Row) (int64, bool, error) {
|
|
arg, isNull, err := b.args[0].EvalTime(b.ctx, row)
|
|
|
|
if isNull || err != nil {
|
|
return 0, true, handleInvalidTimeError(b.ctx, err)
|
|
}
|
|
ret := types.TimestampDiff("DAY", types.ZeroDate, arg)
|
|
if ret == 0 {
|
|
return 0, true, handleInvalidTimeError(b.ctx, types.ErrWrongValue.GenWithStackByArgs(types.DateTimeStr, arg.String()))
|
|
}
|
|
return ret, false, nil
|
|
}
|
|
|
|
type toSecondsFunctionClass struct {
|
|
baseFunctionClass
|
|
}
|
|
|
|
func (c *toSecondsFunctionClass) getFunction(ctx sessionctx.Context, args []Expression) (builtinFunc, error) {
|
|
if err := c.verifyArgs(args); err != nil {
|
|
return nil, err
|
|
}
|
|
bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETInt, types.ETDatetime)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
sig := &builtinToSecondsSig{bf}
|
|
sig.setPbCode(tipb.ScalarFuncSig_ToSeconds)
|
|
return sig, nil
|
|
}
|
|
|
|
type builtinToSecondsSig struct {
|
|
baseBuiltinFunc
|
|
}
|
|
|
|
func (b *builtinToSecondsSig) Clone() builtinFunc {
|
|
newSig := &builtinToSecondsSig{}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
// evalInt evals a builtinToSecondsSig.
|
|
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_to-seconds
|
|
func (b *builtinToSecondsSig) evalInt(row chunk.Row) (int64, bool, error) {
|
|
arg, isNull, err := b.args[0].EvalTime(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return 0, true, handleInvalidTimeError(b.ctx, err)
|
|
}
|
|
ret := types.TimestampDiff("SECOND", types.ZeroDate, arg)
|
|
if ret == 0 {
|
|
return 0, true, handleInvalidTimeError(b.ctx, types.ErrWrongValue.GenWithStackByArgs(types.DateTimeStr, arg.String()))
|
|
}
|
|
return ret, false, nil
|
|
}
|
|
|
|
type utcTimeFunctionClass struct {
|
|
baseFunctionClass
|
|
}
|
|
|
|
func (c *utcTimeFunctionClass) getFlenAndDecimal4UTCTime(ctx sessionctx.Context, args []Expression) (flen, decimal int) {
|
|
if len(args) == 0 {
|
|
flen, decimal = 8, 0
|
|
return
|
|
}
|
|
if constant, ok := args[0].(*Constant); ok {
|
|
fsp, isNull, err := constant.EvalInt(ctx, chunk.Row{})
|
|
if isNull || err != nil || fsp > int64(types.MaxFsp) {
|
|
decimal = int(types.MaxFsp)
|
|
} else if fsp < int64(types.MinFsp) {
|
|
decimal = int(types.MinFsp)
|
|
} else {
|
|
decimal = int(fsp)
|
|
}
|
|
}
|
|
if decimal > 0 {
|
|
flen = 8 + 1 + decimal
|
|
} else {
|
|
flen = 8
|
|
}
|
|
return flen, decimal
|
|
}
|
|
|
|
func (c *utcTimeFunctionClass) getFunction(ctx sessionctx.Context, args []Expression) (builtinFunc, error) {
|
|
if err := c.verifyArgs(args); err != nil {
|
|
return nil, err
|
|
}
|
|
argTps := make([]types.EvalType, 0, 1)
|
|
if len(args) == 1 {
|
|
argTps = append(argTps, types.ETInt)
|
|
}
|
|
bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETDuration, argTps...)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
bf.tp.Flen, bf.tp.Decimal = c.getFlenAndDecimal4UTCTime(bf.ctx, args)
|
|
|
|
var sig builtinFunc
|
|
if len(args) == 1 {
|
|
sig = &builtinUTCTimeWithArgSig{bf}
|
|
sig.setPbCode(tipb.ScalarFuncSig_UTCTimeWithArg)
|
|
} else {
|
|
sig = &builtinUTCTimeWithoutArgSig{bf}
|
|
sig.setPbCode(tipb.ScalarFuncSig_UTCTimeWithoutArg)
|
|
}
|
|
return sig, nil
|
|
}
|
|
|
|
type builtinUTCTimeWithoutArgSig struct {
|
|
baseBuiltinFunc
|
|
}
|
|
|
|
func (b *builtinUTCTimeWithoutArgSig) Clone() builtinFunc {
|
|
newSig := &builtinUTCTimeWithoutArgSig{}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
// evalDuration evals a builtinUTCTimeWithoutArgSig.
|
|
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_utc-time
|
|
func (b *builtinUTCTimeWithoutArgSig) evalDuration(row chunk.Row) (types.Duration, bool, error) {
|
|
nowTs, err := getStmtTimestamp(b.ctx)
|
|
if err != nil {
|
|
return types.Duration{}, true, err
|
|
}
|
|
v, err := types.ParseDuration(b.ctx.GetSessionVars().StmtCtx, nowTs.UTC().Format(types.TimeFormat), 0)
|
|
return v, false, err
|
|
}
|
|
|
|
type builtinUTCTimeWithArgSig struct {
|
|
baseBuiltinFunc
|
|
}
|
|
|
|
func (b *builtinUTCTimeWithArgSig) Clone() builtinFunc {
|
|
newSig := &builtinUTCTimeWithArgSig{}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
// evalDuration evals a builtinUTCTimeWithArgSig.
|
|
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_utc-time
|
|
func (b *builtinUTCTimeWithArgSig) evalDuration(row chunk.Row) (types.Duration, bool, error) {
|
|
fsp, isNull, err := b.args[0].EvalInt(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return types.Duration{}, isNull, err
|
|
}
|
|
if fsp > int64(types.MaxFsp) {
|
|
return types.Duration{}, true, errors.Errorf("Too-big precision %v specified for 'utc_time'. Maximum is %v.", fsp, types.MaxFsp)
|
|
}
|
|
if fsp < int64(types.MinFsp) {
|
|
return types.Duration{}, true, errors.Errorf("Invalid negative %d specified, must in [0, 6].", fsp)
|
|
}
|
|
nowTs, err := getStmtTimestamp(b.ctx)
|
|
if err != nil {
|
|
return types.Duration{}, true, err
|
|
}
|
|
v, err := types.ParseDuration(b.ctx.GetSessionVars().StmtCtx, nowTs.UTC().Format(types.TimeFSPFormat), int8(fsp))
|
|
return v, false, err
|
|
}
|
|
|
|
type lastDayFunctionClass struct {
|
|
baseFunctionClass
|
|
}
|
|
|
|
func (c *lastDayFunctionClass) getFunction(ctx sessionctx.Context, args []Expression) (builtinFunc, error) {
|
|
if err := c.verifyArgs(args); err != nil {
|
|
return nil, err
|
|
}
|
|
bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETDatetime, types.ETDatetime)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
bf.tp.Tp, bf.tp.Flen, bf.tp.Decimal = mysql.TypeDate, mysql.MaxDateWidth, int(types.DefaultFsp)
|
|
sig := &builtinLastDaySig{bf}
|
|
sig.setPbCode(tipb.ScalarFuncSig_LastDay)
|
|
return sig, nil
|
|
}
|
|
|
|
type builtinLastDaySig struct {
|
|
baseBuiltinFunc
|
|
}
|
|
|
|
func (b *builtinLastDaySig) Clone() builtinFunc {
|
|
newSig := &builtinLastDaySig{}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
// evalTime evals a builtinLastDaySig.
|
|
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_last-day
|
|
func (b *builtinLastDaySig) evalTime(row chunk.Row) (types.Time, bool, error) {
|
|
arg, isNull, err := b.args[0].EvalTime(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return types.ZeroTime, true, handleInvalidTimeError(b.ctx, err)
|
|
}
|
|
tm := arg
|
|
year, month := tm.Year(), tm.Month()
|
|
if arg.InvalidZero() {
|
|
return types.ZeroTime, true, handleInvalidTimeError(b.ctx, types.ErrWrongValue.GenWithStackByArgs(types.DateTimeStr, arg.String()))
|
|
}
|
|
lastDay := types.GetLastDay(year, month)
|
|
ret := types.NewTime(types.FromDate(year, month, lastDay, 0, 0, 0, 0), mysql.TypeDate, types.DefaultFsp)
|
|
return ret, false, nil
|
|
}
|
|
|
|
// getExpressionFsp calculates the fsp from given expression.
|
|
func getExpressionFsp(ctx sessionctx.Context, expression Expression) (int, error) {
|
|
constExp, isConstant := expression.(*Constant)
|
|
if isConstant && types.IsString(expression.GetType().Tp) && !isTemporalColumn(expression) {
|
|
str, isNil, err := constExp.EvalString(ctx, chunk.Row{})
|
|
if isNil || err != nil {
|
|
return 0, err
|
|
}
|
|
return int(types.GetFsp(str)), nil
|
|
}
|
|
return mathutil.Min(expression.GetType().Decimal, int(types.MaxFsp)), nil
|
|
}
|
|
|
|
// tidbParseTsoFunctionClass extracts physical time from a tso
|
|
type tidbParseTsoFunctionClass struct {
|
|
baseFunctionClass
|
|
}
|
|
|
|
func (c *tidbParseTsoFunctionClass) getFunction(ctx sessionctx.Context, args []Expression) (builtinFunc, error) {
|
|
if err := c.verifyArgs(args); err != nil {
|
|
return nil, err
|
|
}
|
|
argTp := args[0].GetType().EvalType()
|
|
bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, argTp, types.ETInt)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
bf.tp.Tp, bf.tp.Flen, bf.tp.Decimal = mysql.TypeDate, mysql.MaxDateWidth, int(types.DefaultFsp)
|
|
sig := &builtinTidbParseTsoSig{bf}
|
|
return sig, nil
|
|
}
|
|
|
|
type builtinTidbParseTsoSig struct {
|
|
baseBuiltinFunc
|
|
}
|
|
|
|
func (b *builtinTidbParseTsoSig) Clone() builtinFunc {
|
|
newSig := &builtinTidbParseTsoSig{}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
// evalTime evals a builtinTidbParseTsoSig.
|
|
func (b *builtinTidbParseTsoSig) evalTime(row chunk.Row) (types.Time, bool, error) {
|
|
arg, isNull, err := b.args[0].EvalInt(b.ctx, row)
|
|
if isNull || err != nil || arg <= 0 {
|
|
return types.ZeroTime, true, handleInvalidTimeError(b.ctx, err)
|
|
}
|
|
|
|
t := oracle.GetTimeFromTS(uint64(arg))
|
|
result := types.NewTime(types.FromGoTime(t), mysql.TypeDatetime, types.MaxFsp)
|
|
err = result.ConvertTimeZone(time.Local, b.ctx.GetSessionVars().Location())
|
|
if err != nil {
|
|
return types.ZeroTime, true, err
|
|
}
|
|
return result, false, nil
|
|
}
|
|
|