tidb builtin_time 源码
tidb builtin_time 代码
文件路径:/expression/builtin_time.go
// 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,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// 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.
package expression
import (
"fmt"
"math"
"regexp"
"strconv"
"strings"
"time"
"github.com/pingcap/errors"
"github.com/pingcap/failpoint"
"github.com/pingcap/tidb/config"
"github.com/pingcap/tidb/parser/ast"
"github.com/pingcap/tidb/parser/mysql"
"github.com/pingcap/tidb/parser/terror"
"github.com/pingcap/tidb/sessionctx"
"github.com/pingcap/tidb/sessionctx/stmtctx"
"github.com/pingcap/tidb/sessionctx/variable"
"github.com/pingcap/tidb/types"
"github.com/pingcap/tidb/util/chunk"
"github.com/pingcap/tidb/util/logutil"
"github.com/pingcap/tidb/util/mathutil"
"github.com/pingcap/tidb/util/parser"
"github.com/pingcap/tipb/go-tipb"
"github.com/tikv/client-go/v2/oracle"
"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 = &addSubDateFunctionClass{}
)
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 = &builtinExtractDatetimeFromStringSig{}
_ builtinFunc = &builtinExtractDatetimeSig{}
_ builtinFunc = &builtinExtractDurationSig{}
_ builtinFunc = &builtinAddSubDateAsStringSig{}
_ builtinFunc = &builtinAddSubDateDatetimeAnySig{}
_ builtinFunc = &builtinAddSubDateDurationAnySig{}
)
func convertTimeToMysqlTime(t time.Time, fsp int, 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.setDecimalAndFlenForDate()
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() && b.ctx.GetSessionVars().SQLMode.HasNoZeroDateMode() {
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.DateStr, 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.setDecimalAndFlenForDate()
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.DateStr, b.literal.String())
}
if mode.HasNoZeroInDateMode() && (b.literal.InvalidZero() && !b.literal.IsZero()) {
return b.literal, true, types.ErrWrongValue.GenWithStackByArgs(types.DateStr, 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)
arg0Dec, err := getExpressionFsp(ctx, args[0])
if err != nil {
return nil, err
}
arg1Dec, err := getExpressionFsp(ctx, args[1])
if err != nil {
return nil, err
}
fsp := mathutil.Max(arg0Dec, arg1Dec)
bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETDuration, arg0Tp, arg1Tp)
if err != nil {
return nil, err
}
bf.setDecimalAndFlenForTime(fsp)
var sig builtinFunc
// arg0 and arg1 must be the same time type(compatible), or timediff will return NULL.
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, b.tp.GetDecimal())
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, b.tp.GetDecimal())
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, b.tp.GetDecimal())
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, b.tp.GetDecimal())
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 := b.tp.GetDecimal()
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 int) (d types.Duration, t types.Time,
isDuration bool, err error) {
if n := strings.IndexByte(str, '.'); n >= 0 {
lenStrFsp := len(str[n+1:])
if lenStrFsp <= types.MaxFsp {
fsp = mathutil.Max(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.SetFlen((args[1].GetType().GetFlen() + 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.setDecimalAndFlenForDate()
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/8.0/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
}
ret := types.TimeFromDays(n)
// the maximum date value is 9999-12-31 in mysql 5.8.
if ret.Year() > 9999 {
return types.ZeroTime, true, nil
}
return ret, 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.SetFlen(3)
bf.tp.SetDecimal(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.SetFlen(2)
bf.tp.SetDecimal(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.SetFlen(2)
bf.tp.SetDecimal(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.SetFlen(6)
bf.tp.SetDecimal(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.SetFlen(2)
bf.tp.SetDecimal(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)
}
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
}
charset, collate := ctx.GetSessionVars().GetCharsetInfo()
bf.tp.SetCharset(charset)
bf.tp.SetCollate(collate)
bf.tp.SetFlen(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
}
charset, collate := ctx.GetSessionVars().GetCharsetInfo()
bf.tp.SetCharset(charset)
bf.tp.SetCollate(collate)
bf.tp.SetFlen(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.SetFlen(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)
}
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.SetFlen(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.SetFlen(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.SetFlen(2)
bf.tp.SetDecimal(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() || date.InvalidZero() {
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() || date.InvalidZero() {
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.SetFlen(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() || date.InvalidZero() {
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.SetFlen(2)
bf.tp.SetDecimal(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() || date.InvalidZero() {
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.SetFlen(4)
bf.tp.SetDecimal(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)
}
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.SetFlen(6)
bf.tp.SetDecimal(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() || date.InvalidZero() {
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)
}
arg0Tp := args[0].GetType()
isArg0Str := arg0Tp.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.
fsp := types.MaxFsp
if !isArg0Str {
if arg0Tp.GetDecimal() != types.UnspecifiedLength {
fsp = mathutil.Min(bf.tp.GetDecimal(), arg0Tp.GetDecimal())
}
}
bf.setDecimalAndFlenForDatetime(fsp)
sig = &builtinFromUnixTime1ArgSig{bf}
sig.setPbCode(tipb.ScalarFuncSig_FromUnixTime1Arg)
return sig, nil
}
func evalFromUnixTime(ctx sessionctx.Context, fsp int, unixTimeStamp *types.MyDecimal) (res types.Time, isNull bool, err error) {
// 0 <= unixTimeStamp <= 32536771199.999999
if unixTimeStamp.IsNegative() {
return res, true, nil
}
integralPart, err := unixTimeStamp.ToInt()
if err != nil && !terror.ErrorEqual(err, types.ErrTruncated) {
return res, true, err
}
// The max integralPart should not be larger than 32536771199.
// Refer to https://dev.mysql.com/doc/relnotes/mysql/8.0/en/news-8-0-28.html
if integralPart > 32536771199 {
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.ModeHalfUp)
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, b.tp.GetDecimal(), 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, b.tp.GetDecimal(), 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.SetFlen(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 int) {
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.setDecimalAndFlenForDate()
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
}
bf.setDecimalAndFlenForDatetime(fsp)
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
}
bf.setDecimalAndFlenForTime(fsp)
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.ErrWrongValueForType.GenWithStackByArgs(types.DateTimeStr, date, ast.StrToDate))
}
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(b.tp.GetDecimal())
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()))
}
t.SetFsp(b.tp.GetDecimal())
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
}
fsp, err := getFspByIntArg(ctx, args)
if 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.setDecimalAndFlenForDatetime(fsp)
// Illegal parameters have been filtered out in the parser, so the result is always not null.
bf.tp.SetFlag(bf.tp.GetFlag() | mysql.NotNullFlag)
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, int(fsp), types.ModeHalfUp)
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.ModeHalfUp)
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.setDecimalAndFlenForDate()
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
}
fsp, err := getFspByIntArg(ctx, args)
if 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.ETDuration, argTps...)
if err != nil {
return nil, err
}
bf.setDecimalAndFlenForTime(fsp)
// 1. no sign.
// 2. hour is in the 2-digit range.
bf.tp.SetFlen(bf.tp.GetFlen() - 2)
if len(args) == 0 {
sig = &builtinCurrentTime0ArgSig{bf}
sig.setPbCode(tipb.ScalarFuncSig_CurrentTime0Arg)
return sig, nil
}
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, int(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
}
fsp, err := getExpressionFsp(ctx, args[0])
if err != nil {
return nil, err
}
bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETDuration, types.ETString)
if err != nil {
return nil, err
}
bf.setDecimalAndFlenForTime(fsp)
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 int
if tmpFsp, err = types.CheckFsp(fsp); err != nil {
return res, isNull, err
}
fsp = tmpFsp
sc := b.ctx.GetSessionVars().StmtCtx
res, _, err = types.ParseDuration(sc, expr, 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.TimeStr, 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.setDecimalAndFlenForTime(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.setDecimalAndFlenForDate()
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 (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
}
fsp, err := getFspByIntArg(bf.ctx, args)
if err != nil {
return nil, err
}
bf.setDecimalAndFlenForDatetime(fsp)
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 int) (types.Time, bool, error) {
nowTs, err := getStmtTimestamp(ctx)
if err != nil {
return types.ZeroTime, true, err
}
result, err := convertTimeToMysqlTime(nowTs.UTC(), fsp, types.ModeHalfUp)
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, int(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, 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
}
fsp, err := getFspByIntArg(bf.ctx, args)
if err != nil {
return nil, err
}
bf.setDecimalAndFlenForDatetime(fsp)
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
}
// GetStmtTimestamp directly calls getTimeZone with timezone
func GetStmtTimestamp(ctx sessionctx.Context) (time.Time, error) {
tz := getTimeZone(ctx)
tVal, err := getStmtTimestamp(ctx)
if err != nil {
return tVal, err
}
return tVal.In(tz), nil
}
func evalNowWithFsp(ctx sessionctx.Context, fsp int) (types.Time, bool, error) {
nowTs, err := getStmtTimestamp(ctx)
if err != nil {
return types.ZeroTime, true, err
}
failpoint.Inject("injectNow", func(val failpoint.Value) {
nowTs = time.Unix(int64(val.(int)), 0)
})
// 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, int(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, 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
}
args[0] = WrapWithCastAsString(ctx, args[0])
unit, _, err := args[0].EvalString(ctx, chunk.Row{})
if err != nil {
return nil, err
}
isClockUnit := types.IsClockUnit(unit)
isDateUnit := types.IsDateUnit(unit)
var bf baseBuiltinFunc
if isClockUnit && isDateUnit {
// For unit DAY_MICROSECOND/DAY_SECOND/DAY_MINUTE/DAY_HOUR, the interpretation of the second argument depends on its evaluation type:
// 1. Datetime/timestamp are interpreted as datetime. For example:
// extract(day_second from datetime('2001-01-01 02:03:04')) = 120304
// Note that MySQL 5.5+ has a bug of no day portion in the result (20304) for this case, see https://bugs.mysql.com/bug.php?id=73240.
// 2. Time is interpreted as is. For example:
// extract(day_second from time('02:03:04')) = 20304
// Note that time shouldn't be implicitly cast to datetime, or else the date portion will be padded with the current date and this will adjust time portion accordingly.
// 3. Otherwise, string/int/float are interpreted as arbitrarily either datetime or time, depending on which fits. For example:
// extract(day_second from '2001-01-01 02:03:04') = 1020304 // datetime
// extract(day_second from 20010101020304) = 1020304 // datetime
// extract(day_second from '01 02:03:04') = 260304 // time
if args[1].GetType().EvalType() == types.ETDatetime || args[1].GetType().EvalType() == types.ETTimestamp {
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 if args[1].GetType().EvalType() == types.ETDuration {
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)
} else {
bf, err = newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETInt, types.ETString, types.ETString)
if err != nil {
return nil, err
}
bf.args[1].GetType().SetDecimal(int(types.MaxFsp))
sig = &builtinExtractDatetimeFromStringSig{bf}
sig.setPbCode(tipb.ScalarFuncSig_ExtractDatetimeFromString)
}
} else if isClockUnit {
// Clock units interpret the second argument as time.
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)
} else {
// Date units interpret the second argument as datetime.
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)
}
return sig, nil
}
type builtinExtractDatetimeFromStringSig struct {
baseBuiltinFunc
}
func (b *builtinExtractDatetimeFromStringSig) Clone() builtinFunc {
newSig := &builtinExtractDatetimeFromStringSig{}
newSig.cloneFrom(&b.baseBuiltinFunc)
return newSig
}
// evalInt evals a builtinExtractDatetimeFromStringSig.
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_extract
func (b *builtinExtractDatetimeFromStringSig) 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
}
dtStr, isNull, err := b.args[1].EvalString(b.ctx, row)
if isNull || err != nil {
return 0, isNull, err
}
sc := b.ctx.GetSessionVars().StmtCtx
if types.IsClockUnit(unit) && types.IsDateUnit(unit) {
dur, _, err := types.ParseDuration(sc, dtStr, types.GetFsp(dtStr))
if err != nil {
return 0, true, err
}
res, err := types.ExtractDurationNum(&dur, unit)
if err != nil {
return 0, true, err
}
dt, err := types.ParseDatetime(sc, dtStr)
if err != nil {
return res, false, nil
}
if dt.Hour() == dur.Hour() && dt.Minute() == dur.Minute() && dt.Second() == dur.Second() && dt.Year() > 0 {
res, err = types.ExtractDatetimeNum(&dt, unit)
}
return res, err != nil, err
}
panic("Unexpected unit for extract")
}
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
}
// baseDateArithmetical is the base class for all "builtinAddDateXXXSig" and "builtinSubDateXXXSig",
// which provides parameter getter and date arithmetical calculate functions.
type baseDateArithmetical struct {
// intervalRegexp is "*Regexp" used to extract string interval for "DAY" unit.
intervalRegexp *regexp.Regexp
}
func newDateArithmeticalUtil() baseDateArithmetical {
return baseDateArithmetical{
intervalRegexp: regexp.MustCompile(`-?[\d]+`),
}
}
func (du *baseDateArithmetical) 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)
if err != nil {
err = handleInvalidTimeError(ctx, err)
if err != nil {
return types.ZeroTime, true, err
}
return date, true, handleInvalidTimeError(ctx, err)
} else if ctx.GetSessionVars().SQLMode.HasNoZeroDateMode() && (date.Year() == 0 || date.Month() == 0 || date.Day() == 0) {
return types.ZeroTime, true, handleInvalidTimeError(ctx, types.ErrWrongValue.GenWithStackByArgs(types.DateTimeStr, dateStr))
}
return date, false, handleInvalidTimeError(ctx, err)
}
func (du *baseDateArithmetical) 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)
}
// The actual date.Type() might be date or datetime.
// When the unit contains clock, the date part is treated as datetime even though it might be actually a date.
if types.IsClockUnit(unit) {
date.SetType(mysql.TypeDatetime)
}
return date, false, nil
}
func (du *baseDateArithmetical) getDateFromReal(ctx sessionctx.Context, args []Expression, row chunk.Row, unit string) (types.Time, bool, error) {
dateReal, isNull, err := args[0].EvalReal(ctx, row)
if isNull || err != nil {
return types.ZeroTime, true, err
}
sc := ctx.GetSessionVars().StmtCtx
date, err := types.ParseTimeFromFloat64(sc, dateReal)
if err != nil {
return types.ZeroTime, true, handleInvalidTimeError(ctx, err)
}
// The actual date.Type() might be date or datetime.
// When the unit contains clock, the date part is treated as datetime even though it might be actually a date.
if types.IsClockUnit(unit) {
date.SetType(mysql.TypeDatetime)
}
return date, false, nil
}
func (du *baseDateArithmetical) getDateFromDecimal(ctx sessionctx.Context, args []Expression, row chunk.Row, unit string) (types.Time, bool, error) {
dateDec, isNull, err := args[0].EvalDecimal(ctx, row)
if isNull || err != nil {
return types.ZeroTime, true, err
}
sc := ctx.GetSessionVars().StmtCtx
date, err := types.ParseTimeFromDecimal(sc, dateDec)
if err != nil {
return types.ZeroTime, true, handleInvalidTimeError(ctx, err)
}
// The actual date.Type() might be date or datetime.
// When the unit contains clock, the date part is treated as datetime even though it might be actually a date.
if types.IsClockUnit(unit) {
date.SetType(mysql.TypeDatetime)
}
return date, false, nil
}
func (du *baseDateArithmetical) 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
}
// The actual date.Type() might be date, datetime or timestamp.
// Datetime is treated as is.
// Timestamp is treated as datetime, as MySQL manual says: https://dev.mysql.com/doc/refman/8.0/en/date-and-time-functions.html#function_date-add
// When the unit contains clock, the date part is treated as datetime even though it might be actually a date.
if types.IsClockUnit(unit) || date.Type() == mysql.TypeTimestamp {
date.SetType(mysql.TypeDatetime)
}
return date, false, nil
}
func (du *baseDateArithmetical) 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 *baseDateArithmetical) 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 *baseDateArithmetical) 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 *baseDateArithmetical) 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().GetDecimal(), 64), false, nil
}
func (du *baseDateArithmetical) add(ctx sessionctx.Context, date types.Time, interval, unit string, resultFsp int) (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, resultFsp)
}
func (du *baseDateArithmetical) addDate(ctx sessionctx.Context, date types.Time, year, month, day, nano int64, resultFsp int) (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)
// Adjust fsp as required by outer - always respect type inference.
date.SetFsp(resultFsp)
// 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
}
type funcDurationOp func(d, interval types.Duration) (types.Duration, error)
func (du *baseDateArithmetical) opDuration(ctx sessionctx.Context, op funcDurationOp, d types.Duration, interval string, unit string, resultFsp int) (types.Duration, bool, error) {
dur, err := types.ExtractDurationValue(unit, interval)
if err != nil {
return types.ZeroDuration, true, handleInvalidTimeError(ctx, err)
}
retDur, err := op(d, dur)
if err != nil {
return types.ZeroDuration, true, err
}
// Adjust fsp as required by outer - always respect type inference.
retDur.Fsp = resultFsp
return retDur, false, nil
}
func (du *baseDateArithmetical) addDuration(ctx sessionctx.Context, d types.Duration, interval string, unit string, resultFsp int) (types.Duration, bool, error) {
add := func(d, interval types.Duration) (types.Duration, error) {
return d.Add(interval)
}
return du.opDuration(ctx, add, d, interval, unit, resultFsp)
}
func (du *baseDateArithmetical) subDuration(ctx sessionctx.Context, d types.Duration, interval string, unit string, resultFsp int) (types.Duration, bool, error) {
sub := func(d, interval types.Duration) (types.Duration, error) {
return d.Sub(interval)
}
return du.opDuration(ctx, sub, d, interval, unit, resultFsp)
}
func (du *baseDateArithmetical) sub(ctx sessionctx.Context, date types.Time, interval string, unit string, resultFsp int) (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, resultFsp)
}
func (du *baseDateArithmetical) vecGetDateFromInt(b *baseBuiltinFunc, input *chunk.Chunk, unit string, result *chunk.Column) error {
n := input.NumRows()
buf, err := b.bufAllocator.get()
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
}
// The actual date.Type() might be date or datetime.
// When the unit contains clock, the date part is treated as datetime even though it might be actually a date.
if isClockUnit {
date.SetType(mysql.TypeDatetime)
}
dates[i] = date
}
return nil
}
func (du *baseDateArithmetical) vecGetDateFromReal(b *baseBuiltinFunc, input *chunk.Chunk, unit string, result *chunk.Column) error {
n := input.NumRows()
buf, err := b.bufAllocator.get()
if err != nil {
return err
}
defer b.bufAllocator.put(buf)
if err := b.args[0].VecEvalReal(b.ctx, input, buf); err != nil {
return err
}
result.ResizeTime(n, false)
result.MergeNulls(buf)
dates := result.Times()
f64s := buf.Float64s()
sc := b.ctx.GetSessionVars().StmtCtx
isClockUnit := types.IsClockUnit(unit)
for i := 0; i < n; i++ {
if result.IsNull(i) {
continue
}
date, err := types.ParseTimeFromFloat64(sc, f64s[i])
if err != nil {
err = handleInvalidTimeError(b.ctx, err)
if err != nil {
return err
}
result.SetNull(i, true)
continue
}
// The actual date.Type() might be date or datetime.
// When the unit contains clock, the date part is treated as datetime even though it might be actually a date.
if isClockUnit {
date.SetType(mysql.TypeDatetime)
}
dates[i] = date
}
return nil
}
func (du *baseDateArithmetical) vecGetDateFromDecimal(b *baseBuiltinFunc, input *chunk.Chunk, unit string, result *chunk.Column) error {
n := input.NumRows()
buf, err := b.bufAllocator.get()
if err != nil {
return err
}
defer b.bufAllocator.put(buf)
if err := b.args[0].VecEvalDecimal(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
}
dec := buf.GetDecimal(i)
date, err := types.ParseTimeFromDecimal(sc, dec)
if err != nil {
err = handleInvalidTimeError(b.ctx, err)
if err != nil {
return err
}
result.SetNull(i, true)
continue
}
// The actual date.Type() might be date or datetime.
// When the unit contains clock, the date part is treated as datetime even though it might be actually a date.
if isClockUnit {
date.SetType(mysql.TypeDatetime)
}
dates[i] = date
}
return nil
}
func (du *baseDateArithmetical) vecGetDateFromString(b *baseBuiltinFunc, input *chunk.Chunk, unit string, result *chunk.Column) error {
n := input.NumRows()
buf, err := b.bufAllocator.get()
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 if b.ctx.GetSessionVars().SQLMode.HasNoZeroDateMode() && (date.Year() == 0 || date.Month() == 0 || date.Day() == 0) {
return handleInvalidTimeError(b.ctx, types.ErrWrongValue.GenWithStackByArgs(types.DateTimeStr, dateStr))
} else {
dates[i] = date
}
}
return nil
}
func (du *baseDateArithmetical) 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
}
// The actual date[i].Type() might be date, datetime or timestamp.
// Datetime is treated as is.
// Timestamp is treated as datetime, as MySQL manual says: https://dev.mysql.com/doc/refman/8.0/en/date-and-time-functions.html#function_date-add
// When the unit contains clock, the date part is treated as datetime even though it might be actually a date.
if isClockUnit || dates[i].Type() == mysql.TypeTimestamp {
dates[i].SetType(mysql.TypeDatetime)
}
}
return nil
}
func (du *baseDateArithmetical) vecGetIntervalFromString(b *baseBuiltinFunc, input *chunk.Chunk, unit string, result *chunk.Column) error {
n := input.NumRows()
buf, err := b.bufAllocator.get()
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 *baseDateArithmetical) vecGetIntervalFromDecimal(b *baseBuiltinFunc, input *chunk.Chunk, unit string, result *chunk.Column) error {
n := input.NumRows()
buf, err := b.bufAllocator.get()
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 *baseDateArithmetical) vecGetIntervalFromInt(b *baseBuiltinFunc, input *chunk.Chunk, unit string, result *chunk.Column) error {
n := input.NumRows()
buf, err := b.bufAllocator.get()
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 *baseDateArithmetical) vecGetIntervalFromReal(b *baseBuiltinFunc, input *chunk.Chunk, unit string, result *chunk.Column) error {
n := input.NumRows()
buf, err := b.bufAllocator.get()
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().GetDecimal()
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 funcTimeOpForDateAddSub func(da *baseDateArithmetical, ctx sessionctx.Context, date types.Time, interval, unit string, resultFsp int) (types.Time, bool, error)
func addTime(da *baseDateArithmetical, ctx sessionctx.Context, date types.Time, interval, unit string, resultFsp int) (types.Time, bool, error) {
return da.add(ctx, date, interval, unit, resultFsp)
}
func subTime(da *baseDateArithmetical, ctx sessionctx.Context, date types.Time, interval, unit string, resultFsp int) (types.Time, bool, error) {
return da.sub(ctx, date, interval, unit, resultFsp)
}
type funcDurationOpForDateAddSub func(da *baseDateArithmetical, ctx sessionctx.Context, d types.Duration, interval, unit string, resultFsp int) (types.Duration, bool, error)
func addDuration(da *baseDateArithmetical, ctx sessionctx.Context, d types.Duration, interval, unit string, resultFsp int) (types.Duration, bool, error) {
return da.addDuration(ctx, d, interval, unit, resultFsp)
}
func subDuration(da *baseDateArithmetical, ctx sessionctx.Context, d types.Duration, interval, unit string, resultFsp int) (types.Duration, bool, error) {
return da.subDuration(ctx, d, interval, unit, resultFsp)
}
type funcSetPbCodeOp func(b builtinFunc, add, sub tipb.ScalarFuncSig)
func setAdd(b builtinFunc, add, sub tipb.ScalarFuncSig) {
b.setPbCode(add)
}
func setSub(b builtinFunc, add, sub tipb.ScalarFuncSig) {
b.setPbCode(sub)
}
type funcGetDateForDateAddSub func(da *baseDateArithmetical, ctx sessionctx.Context, args []Expression, row chunk.Row, unit string) (types.Time, bool, error)
func getDateFromString(da *baseDateArithmetical, ctx sessionctx.Context, args []Expression, row chunk.Row, unit string) (types.Time, bool, error) {
return da.getDateFromString(ctx, args, row, unit)
}
func getDateFromInt(da *baseDateArithmetical, ctx sessionctx.Context, args []Expression, row chunk.Row, unit string) (types.Time, bool, error) {
return da.getDateFromInt(ctx, args, row, unit)
}
func getDateFromReal(da *baseDateArithmetical, ctx sessionctx.Context, args []Expression, row chunk.Row, unit string) (types.Time, bool, error) {
return da.getDateFromReal(ctx, args, row, unit)
}
func getDateFromDecimal(da *baseDateArithmetical, ctx sessionctx.Context, args []Expression, row chunk.Row, unit string) (types.Time, bool, error) {
return da.getDateFromDecimal(ctx, args, row, unit)
}
type funcVecGetDateForDateAddSub func(da *baseDateArithmetical, b *baseBuiltinFunc, input *chunk.Chunk, unit string, result *chunk.Column) error
func vecGetDateFromString(da *baseDateArithmetical, b *baseBuiltinFunc, input *chunk.Chunk, unit string, result *chunk.Column) error {
return da.vecGetDateFromString(b, input, unit, result)
}
func vecGetDateFromInt(da *baseDateArithmetical, b *baseBuiltinFunc, input *chunk.Chunk, unit string, result *chunk.Column) error {
return da.vecGetDateFromInt(b, input, unit, result)
}
func vecGetDateFromReal(da *baseDateArithmetical, b *baseBuiltinFunc, input *chunk.Chunk, unit string, result *chunk.Column) error {
return da.vecGetDateFromReal(b, input, unit, result)
}
func vecGetDateFromDecimal(da *baseDateArithmetical, b *baseBuiltinFunc, input *chunk.Chunk, unit string, result *chunk.Column) error {
return da.vecGetDateFromDecimal(b, input, unit, result)
}
type funcGetIntervalForDateAddSub func(da *baseDateArithmetical, ctx sessionctx.Context, args []Expression, row chunk.Row, unit string) (string, bool, error)
func getIntervalFromString(da *baseDateArithmetical, ctx sessionctx.Context, args []Expression, row chunk.Row, unit string) (string, bool, error) {
return da.getIntervalFromString(ctx, args, row, unit)
}
func getIntervalFromInt(da *baseDateArithmetical, ctx sessionctx.Context, args []Expression, row chunk.Row, unit string) (string, bool, error) {
return da.getIntervalFromInt(ctx, args, row, unit)
}
func getIntervalFromReal(da *baseDateArithmetical, ctx sessionctx.Context, args []Expression, row chunk.Row, unit string) (string, bool, error) {
return da.getIntervalFromReal(ctx, args, row, unit)
}
func getIntervalFromDecimal(da *baseDateArithmetical, ctx sessionctx.Context, args []Expression, row chunk.Row, unit string) (string, bool, error) {
return da.getIntervalFromDecimal(ctx, args, row, unit)
}
type funcVecGetIntervalForDateAddSub func(da *baseDateArithmetical, b *baseBuiltinFunc, input *chunk.Chunk, unit string, result *chunk.Column) error
func vecGetIntervalFromString(da *baseDateArithmetical, b *baseBuiltinFunc, input *chunk.Chunk, unit string, result *chunk.Column) error {
return da.vecGetIntervalFromString(b, input, unit, result)
}
func vecGetIntervalFromInt(da *baseDateArithmetical, b *baseBuiltinFunc, input *chunk.Chunk, unit string, result *chunk.Column) error {
return da.vecGetIntervalFromInt(b, input, unit, result)
}
func vecGetIntervalFromReal(da *baseDateArithmetical, b *baseBuiltinFunc, input *chunk.Chunk, unit string, result *chunk.Column) error {
return da.vecGetIntervalFromReal(b, input, unit, result)
}
func vecGetIntervalFromDecimal(da *baseDateArithmetical, b *baseBuiltinFunc, input *chunk.Chunk, unit string, result *chunk.Column) error {
return da.vecGetIntervalFromDecimal(b, input, unit, result)
}
type addSubDateFunctionClass struct {
baseFunctionClass
timeOp funcTimeOpForDateAddSub
durationOp funcDurationOpForDateAddSub
setPbCodeOp funcSetPbCodeOp
}
func (c *addSubDateFunctionClass) 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()
// Some special evaluation type treatment.
// Note that it could be more elegant if we always evaluate datetime for int, real, decimal and string, by leveraging existing implicit casts.
// However, MySQL has a weird behavior for date_add(string, ...), whose result depends on the content of the first argument.
// E.g., date_add('2000-01-02 00:00:00', interval 1 day) evaluates to '2021-01-03 00:00:00' (which is normal),
// whereas date_add('2000-01-02', interval 1 day) evaluates to '2000-01-03' instead of '2021-01-03 00:00:00'.
// This requires a customized parsing of the content of the first argument, by recognizing if it is a pure date format or contains HMS part.
// So implicit casts are not viable here.
if dateEvalTp == types.ETTimestamp {
dateEvalTp = types.ETDatetime
} else if dateEvalTp == types.ETJson {
dateEvalTp = types.ETString
}
intervalEvalTp := args[1].GetType().EvalType()
if intervalEvalTp == types.ETJson {
intervalEvalTp = types.ETString
} else if intervalEvalTp != types.ETString && intervalEvalTp != types.ETDecimal && intervalEvalTp != types.ETReal {
intervalEvalTp = types.ETInt
}
unit, _, err := args[2].EvalString(ctx, chunk.Row{})
if err != nil {
return nil, err
}
resultTp := mysql.TypeVarString
resultEvalTp := types.ETString
if args[0].GetType().GetType() == mysql.TypeDate {
if !types.IsClockUnit(unit) {
// First arg is date and unit contains no HMS, return date.
resultTp = mysql.TypeDate
resultEvalTp = types.ETDatetime
} else {
// First arg is date and unit contains HMS, return datetime.
resultTp = mysql.TypeDatetime
resultEvalTp = types.ETDatetime
}
} else if dateEvalTp == types.ETDuration {
if types.IsDateUnit(unit) && unit != "DAY_MICROSECOND" {
// First arg is time and unit contains YMD (except DAY_MICROSECOND), return datetime.
resultTp = mysql.TypeDatetime
resultEvalTp = types.ETDatetime
} else {
// First arg is time and unit contains no YMD or is DAY_MICROSECOND, return time.
resultTp = mysql.TypeDuration
resultEvalTp = types.ETDuration
}
} else if dateEvalTp == types.ETDatetime {
// First arg is datetime or timestamp, return datetime.
resultTp = mysql.TypeDatetime
resultEvalTp = types.ETDatetime
}
argTps := []types.EvalType{dateEvalTp, intervalEvalTp, types.ETString}
var bf baseBuiltinFunc
bf, err = newBaseBuiltinFuncWithTp(ctx, c.funcName, args, resultEvalTp, argTps...)
bf.tp.SetType(resultTp)
var resultFsp int
if types.IsMicrosecondUnit(unit) {
resultFsp = types.MaxFsp
} else {
intervalFsp := types.MinFsp
if unit == "SECOND" {
if intervalEvalTp == types.ETString || intervalEvalTp == types.ETReal {
intervalFsp = types.MaxFsp
} else {
intervalFsp = mathutil.Min(types.MaxFsp, args[1].GetType().GetDecimal())
}
}
resultFsp = mathutil.Min(types.MaxFsp, mathutil.Max(args[0].GetType().GetDecimal(), intervalFsp))
}
switch resultTp {
case mysql.TypeDate:
bf.setDecimalAndFlenForDate()
case mysql.TypeDuration:
bf.setDecimalAndFlenForTime(resultFsp)
case mysql.TypeDatetime:
bf.setDecimalAndFlenForDatetime(resultFsp)
case mysql.TypeVarString:
bf.tp.SetFlen(mysql.MaxDatetimeFullWidth)
bf.tp.SetDecimal(types.MinFsp)
}
switch {
case dateEvalTp == types.ETString && intervalEvalTp == types.ETString:
sig = &builtinAddSubDateAsStringSig{
baseBuiltinFunc: bf,
baseDateArithmetical: newDateArithmeticalUtil(),
getDate: getDateFromString,
vecGetDate: vecGetDateFromString,
getInterval: getIntervalFromString,
vecGetInterval: vecGetIntervalFromString,
timeOp: c.timeOp,
}
c.setPbCodeOp(sig, tipb.ScalarFuncSig_AddDateStringString, tipb.ScalarFuncSig_SubDateStringString)
case dateEvalTp == types.ETString && intervalEvalTp == types.ETInt:
sig = &builtinAddSubDateAsStringSig{
baseBuiltinFunc: bf,
baseDateArithmetical: newDateArithmeticalUtil(),
getDate: getDateFromString,
vecGetDate: vecGetDateFromString,
getInterval: getIntervalFromInt,
vecGetInterval: vecGetIntervalFromInt,
timeOp: c.timeOp,
}
c.setPbCodeOp(sig, tipb.ScalarFuncSig_AddDateStringInt, tipb.ScalarFuncSig_SubDateStringInt)
case dateEvalTp == types.ETString && intervalEvalTp == types.ETReal:
sig = &builtinAddSubDateAsStringSig{
baseBuiltinFunc: bf,
baseDateArithmetical: newDateArithmeticalUtil(),
getDate: getDateFromString,
vecGetDate: vecGetDateFromString,
getInterval: getIntervalFromReal,
vecGetInterval: vecGetIntervalFromReal,
timeOp: c.timeOp,
}
c.setPbCodeOp(sig, tipb.ScalarFuncSig_AddDateStringReal, tipb.ScalarFuncSig_SubDateStringReal)
case dateEvalTp == types.ETString && intervalEvalTp == types.ETDecimal:
sig = &builtinAddSubDateAsStringSig{
baseBuiltinFunc: bf,
baseDateArithmetical: newDateArithmeticalUtil(),
getDate: getDateFromString,
vecGetDate: vecGetDateFromString,
getInterval: getIntervalFromDecimal,
vecGetInterval: vecGetIntervalFromDecimal,
timeOp: c.timeOp,
}
c.setPbCodeOp(sig, tipb.ScalarFuncSig_AddDateStringDecimal, tipb.ScalarFuncSig_SubDateStringDecimal)
case dateEvalTp == types.ETInt && intervalEvalTp == types.ETString:
sig = &builtinAddSubDateAsStringSig{
baseBuiltinFunc: bf,
baseDateArithmetical: newDateArithmeticalUtil(),
getDate: getDateFromInt,
vecGetDate: vecGetDateFromInt,
getInterval: getIntervalFromString,
vecGetInterval: vecGetIntervalFromString,
timeOp: c.timeOp,
}
c.setPbCodeOp(sig, tipb.ScalarFuncSig_AddDateIntString, tipb.ScalarFuncSig_SubDateIntString)
case dateEvalTp == types.ETInt && intervalEvalTp == types.ETInt:
sig = &builtinAddSubDateAsStringSig{
baseBuiltinFunc: bf,
baseDateArithmetical: newDateArithmeticalUtil(),
getDate: getDateFromInt,
vecGetDate: vecGetDateFromInt,
getInterval: getIntervalFromInt,
vecGetInterval: vecGetIntervalFromInt,
timeOp: c.timeOp,
}
c.setPbCodeOp(sig, tipb.ScalarFuncSig_AddDateIntInt, tipb.ScalarFuncSig_SubDateIntInt)
case dateEvalTp == types.ETInt && intervalEvalTp == types.ETReal:
sig = &builtinAddSubDateAsStringSig{
baseBuiltinFunc: bf,
baseDateArithmetical: newDateArithmeticalUtil(),
getDate: getDateFromInt,
vecGetDate: vecGetDateFromInt,
getInterval: getIntervalFromReal,
vecGetInterval: vecGetIntervalFromReal,
timeOp: c.timeOp,
}
c.setPbCodeOp(sig, tipb.ScalarFuncSig_AddDateIntReal, tipb.ScalarFuncSig_SubDateIntReal)
case dateEvalTp == types.ETInt && intervalEvalTp == types.ETDecimal:
sig = &builtinAddSubDateAsStringSig{
baseBuiltinFunc: bf,
baseDateArithmetical: newDateArithmeticalUtil(),
getDate: getDateFromInt,
vecGetDate: vecGetDateFromInt,
getInterval: getIntervalFromDecimal,
vecGetInterval: vecGetIntervalFromDecimal,
timeOp: c.timeOp,
}
c.setPbCodeOp(sig, tipb.ScalarFuncSig_AddDateIntDecimal, tipb.ScalarFuncSig_SubDateIntDecimal)
case dateEvalTp == types.ETReal && intervalEvalTp == types.ETString:
sig = &builtinAddSubDateAsStringSig{
baseBuiltinFunc: bf,
baseDateArithmetical: newDateArithmeticalUtil(),
getDate: getDateFromReal,
vecGetDate: vecGetDateFromReal,
getInterval: getIntervalFromString,
vecGetInterval: vecGetIntervalFromString,
timeOp: c.timeOp,
}
c.setPbCodeOp(sig, tipb.ScalarFuncSig_AddDateRealString, tipb.ScalarFuncSig_SubDateRealString)
case dateEvalTp == types.ETReal && intervalEvalTp == types.ETInt:
sig = &builtinAddSubDateAsStringSig{
baseBuiltinFunc: bf,
baseDateArithmetical: newDateArithmeticalUtil(),
getDate: getDateFromReal,
vecGetDate: vecGetDateFromReal,
getInterval: getIntervalFromInt,
vecGetInterval: vecGetIntervalFromInt,
timeOp: c.timeOp,
}
c.setPbCodeOp(sig, tipb.ScalarFuncSig_AddDateRealInt, tipb.ScalarFuncSig_SubDateRealInt)
case dateEvalTp == types.ETReal && intervalEvalTp == types.ETReal:
sig = &builtinAddSubDateAsStringSig{
baseBuiltinFunc: bf,
baseDateArithmetical: newDateArithmeticalUtil(),
getDate: getDateFromReal,
vecGetDate: vecGetDateFromReal,
getInterval: getIntervalFromReal,
vecGetInterval: vecGetIntervalFromReal,
timeOp: c.timeOp,
}
c.setPbCodeOp(sig, tipb.ScalarFuncSig_AddDateRealReal, tipb.ScalarFuncSig_SubDateRealReal)
case dateEvalTp == types.ETReal && intervalEvalTp == types.ETDecimal:
sig = &builtinAddSubDateAsStringSig{
baseBuiltinFunc: bf,
baseDateArithmetical: newDateArithmeticalUtil(),
getDate: getDateFromReal,
vecGetDate: vecGetDateFromReal,
getInterval: getIntervalFromDecimal,
vecGetInterval: vecGetIntervalFromDecimal,
timeOp: c.timeOp,
}
c.setPbCodeOp(sig, tipb.ScalarFuncSig_AddDateRealDecimal, tipb.ScalarFuncSig_SubDateRealDecimal)
case dateEvalTp == types.ETDecimal && intervalEvalTp == types.ETString:
sig = &builtinAddSubDateAsStringSig{
baseBuiltinFunc: bf,
baseDateArithmetical: newDateArithmeticalUtil(),
getDate: getDateFromDecimal,
vecGetDate: vecGetDateFromDecimal,
getInterval: getIntervalFromString,
vecGetInterval: vecGetIntervalFromString,
timeOp: c.timeOp,
}
c.setPbCodeOp(sig, tipb.ScalarFuncSig_AddDateDecimalString, tipb.ScalarFuncSig_SubDateDecimalString)
case dateEvalTp == types.ETDecimal && intervalEvalTp == types.ETInt:
sig = &builtinAddSubDateAsStringSig{
baseBuiltinFunc: bf,
baseDateArithmetical: newDateArithmeticalUtil(),
getDate: getDateFromDecimal,
vecGetDate: vecGetDateFromDecimal,
getInterval: getIntervalFromInt,
vecGetInterval: vecGetIntervalFromInt,
timeOp: c.timeOp,
}
c.setPbCodeOp(sig, tipb.ScalarFuncSig_AddDateDecimalInt, tipb.ScalarFuncSig_SubDateDecimalInt)
case dateEvalTp == types.ETDecimal && intervalEvalTp == types.ETReal:
sig = &builtinAddSubDateAsStringSig{
baseBuiltinFunc: bf,
baseDateArithmetical: newDateArithmeticalUtil(),
getDate: getDateFromDecimal,
vecGetDate: vecGetDateFromDecimal,
getInterval: getIntervalFromReal,
vecGetInterval: vecGetIntervalFromReal,
timeOp: c.timeOp,
}
c.setPbCodeOp(sig, tipb.ScalarFuncSig_AddDateDecimalReal, tipb.ScalarFuncSig_SubDateDecimalReal)
case dateEvalTp == types.ETDecimal && intervalEvalTp == types.ETDecimal:
sig = &builtinAddSubDateAsStringSig{
baseBuiltinFunc: bf,
baseDateArithmetical: newDateArithmeticalUtil(),
getDate: getDateFromDecimal,
vecGetDate: vecGetDateFromDecimal,
getInterval: getIntervalFromDecimal,
vecGetInterval: vecGetIntervalFromDecimal,
timeOp: c.timeOp,
}
c.setPbCodeOp(sig, tipb.ScalarFuncSig_AddDateDecimalDecimal, tipb.ScalarFuncSig_SubDateDecimalDecimal)
case dateEvalTp == types.ETDatetime && intervalEvalTp == types.ETString:
sig = &builtinAddSubDateDatetimeAnySig{
baseBuiltinFunc: bf,
baseDateArithmetical: newDateArithmeticalUtil(),
getInterval: getIntervalFromString,
vecGetInterval: vecGetIntervalFromString,
timeOp: c.timeOp,
}
c.setPbCodeOp(sig, tipb.ScalarFuncSig_AddDateDatetimeString, tipb.ScalarFuncSig_SubDateDatetimeString)
case dateEvalTp == types.ETDatetime && intervalEvalTp == types.ETInt:
sig = &builtinAddSubDateDatetimeAnySig{
baseBuiltinFunc: bf,
baseDateArithmetical: newDateArithmeticalUtil(),
getInterval: getIntervalFromInt,
vecGetInterval: vecGetIntervalFromInt,
timeOp: c.timeOp,
}
c.setPbCodeOp(sig, tipb.ScalarFuncSig_AddDateDatetimeInt, tipb.ScalarFuncSig_SubDateDatetimeInt)
case dateEvalTp == types.ETDatetime && intervalEvalTp == types.ETReal:
sig = &builtinAddSubDateDatetimeAnySig{
baseBuiltinFunc: bf,
baseDateArithmetical: newDateArithmeticalUtil(),
getInterval: getIntervalFromReal,
vecGetInterval: vecGetIntervalFromReal,
timeOp: c.timeOp,
}
c.setPbCodeOp(sig, tipb.ScalarFuncSig_AddDateDatetimeReal, tipb.ScalarFuncSig_SubDateDatetimeReal)
case dateEvalTp == types.ETDatetime && intervalEvalTp == types.ETDecimal:
sig = &builtinAddSubDateDatetimeAnySig{
baseBuiltinFunc: bf,
baseDateArithmetical: newDateArithmeticalUtil(),
getInterval: getIntervalFromDecimal,
vecGetInterval: vecGetIntervalFromDecimal,
timeOp: c.timeOp,
}
c.setPbCodeOp(sig, tipb.ScalarFuncSig_AddDateDatetimeDecimal, tipb.ScalarFuncSig_SubDateDatetimeDecimal)
case dateEvalTp == types.ETDuration && intervalEvalTp == types.ETString:
sig = &builtinAddSubDateDurationAnySig{
baseBuiltinFunc: bf,
baseDateArithmetical: newDateArithmeticalUtil(),
getInterval: getIntervalFromString,
vecGetInterval: vecGetIntervalFromString,
timeOp: c.timeOp,
durationOp: c.durationOp,
}
c.setPbCodeOp(sig, tipb.ScalarFuncSig_AddDateDurationString, tipb.ScalarFuncSig_SubDateDurationString)
case dateEvalTp == types.ETDuration && intervalEvalTp == types.ETInt:
sig = &builtinAddSubDateDurationAnySig{
baseBuiltinFunc: bf,
baseDateArithmetical: newDateArithmeticalUtil(),
getInterval: getIntervalFromInt,
vecGetInterval: vecGetIntervalFromInt,
timeOp: c.timeOp,
durationOp: c.durationOp,
}
c.setPbCodeOp(sig, tipb.ScalarFuncSig_AddDateDurationInt, tipb.ScalarFuncSig_SubDateDurationInt)
case dateEvalTp == types.ETDuration && intervalEvalTp == types.ETReal:
sig = &builtinAddSubDateDurationAnySig{
baseBuiltinFunc: bf,
baseDateArithmetical: newDateArithmeticalUtil(),
getInterval: getIntervalFromReal,
vecGetInterval: vecGetIntervalFromReal,
timeOp: c.timeOp,
durationOp: c.durationOp,
}
c.setPbCodeOp(sig, tipb.ScalarFuncSig_AddDateDurationReal, tipb.ScalarFuncSig_SubDateDurationReal)
case dateEvalTp == types.ETDuration && intervalEvalTp == types.ETDecimal:
sig = &builtinAddSubDateDurationAnySig{
baseBuiltinFunc: bf,
baseDateArithmetical: newDateArithmeticalUtil(),
getInterval: getIntervalFromDecimal,
vecGetInterval: vecGetIntervalFromDecimal,
timeOp: c.timeOp,
durationOp: c.durationOp,
}
c.setPbCodeOp(sig, tipb.ScalarFuncSig_AddDateDurationDecimal, tipb.ScalarFuncSig_SubDateDurationDecimal)
}
return sig, nil
}
type builtinAddSubDateAsStringSig struct {
baseBuiltinFunc
baseDateArithmetical
getDate funcGetDateForDateAddSub
vecGetDate funcVecGetDateForDateAddSub
getInterval funcGetIntervalForDateAddSub
vecGetInterval funcVecGetIntervalForDateAddSub
timeOp funcTimeOpForDateAddSub
}
func (b *builtinAddSubDateAsStringSig) Clone() builtinFunc {
newSig := &builtinAddSubDateAsStringSig{
baseDateArithmetical: b.baseDateArithmetical,
getDate: b.getDate,
vecGetDate: b.vecGetDate,
getInterval: b.getInterval,
vecGetInterval: b.vecGetInterval,
timeOp: b.timeOp,
}
newSig.cloneFrom(&b.baseBuiltinFunc)
return newSig
}
func (b *builtinAddSubDateAsStringSig) evalString(row chunk.Row) (string, bool, error) {
unit, isNull, err := b.args[2].EvalString(b.ctx, row)
if isNull || err != nil {
return types.ZeroTime.String(), true, err
}
date, isNull, err := b.getDate(&b.baseDateArithmetical, b.ctx, b.args, row, unit)
if isNull || err != nil {
return types.ZeroTime.String(), true, err
}
if date.InvalidZero() {
return types.ZeroTime.String(), true, handleInvalidTimeError(b.ctx, types.ErrWrongValue.GenWithStackByArgs(types.DateTimeStr, date.String()))
}
interval, isNull, err := b.getInterval(&b.baseDateArithmetical, b.ctx, b.args, row, unit)
if isNull || err != nil {
return types.ZeroTime.String(), true, err
}
result, isNull, err := b.timeOp(&b.baseDateArithmetical, b.ctx, date, interval, unit, b.tp.GetDecimal())
if result.Microsecond() == 0 {
result.SetFsp(types.MinFsp)
} else {
result.SetFsp(types.MaxFsp)
}
return result.String(), isNull, err
}
type builtinAddSubDateDatetimeAnySig struct {
baseBuiltinFunc
baseDateArithmetical
getInterval funcGetIntervalForDateAddSub
vecGetInterval funcVecGetIntervalForDateAddSub
timeOp funcTimeOpForDateAddSub
}
func (b *builtinAddSubDateDatetimeAnySig) Clone() builtinFunc {
newSig := &builtinAddSubDateDatetimeAnySig{
baseDateArithmetical: b.baseDateArithmetical,
getInterval: b.getInterval,
vecGetInterval: b.vecGetInterval,
timeOp: b.timeOp,
}
newSig.cloneFrom(&b.baseBuiltinFunc)
return newSig
}
func (b *builtinAddSubDateDatetimeAnySig) 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.getInterval(&b.baseDateArithmetical, b.ctx, b.args, row, unit)
if isNull || err != nil {
return types.ZeroTime, true, err
}
result, isNull, err := b.timeOp(&b.baseDateArithmetical, b.ctx, date, interval, unit, b.tp.GetDecimal())
return result, isNull || err != nil, err
}
type builtinAddSubDateDurationAnySig struct {
baseBuiltinFunc
baseDateArithmetical
getInterval funcGetIntervalForDateAddSub
vecGetInterval funcVecGetIntervalForDateAddSub
timeOp funcTimeOpForDateAddSub
durationOp funcDurationOpForDateAddSub
}
func (b *builtinAddSubDateDurationAnySig) Clone() builtinFunc {
newSig := &builtinAddSubDateDurationAnySig{
baseDateArithmetical: b.baseDateArithmetical,
getInterval: b.getInterval,
vecGetInterval: b.vecGetInterval,
timeOp: b.timeOp,
durationOp: b.durationOp,
}
newSig.cloneFrom(&b.baseBuiltinFunc)
return newSig
}
func (b *builtinAddSubDateDurationAnySig) 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
}
d, isNull, err := b.args[0].EvalDuration(b.ctx, row)
if isNull || err != nil {
return types.ZeroTime, true, err
}
interval, isNull, err := b.getInterval(&b.baseDateArithmetical, b.ctx, b.args, row, unit)
if isNull || err != nil {
return types.ZeroTime, true, err
}
sc := b.ctx.GetSessionVars().StmtCtx
t, err := d.ConvertToTime(sc, mysql.TypeDatetime)
if err != nil {
return types.ZeroTime, true, err
}
result, isNull, err := b.timeOp(&b.baseDateArithmetical, b.ctx, t, interval, unit, b.tp.GetDecimal())
return result, isNull || err != nil, err
}
func (b *builtinAddSubDateDurationAnySig) 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.getInterval(&b.baseDateArithmetical, b.ctx, b.args, row, unit)
if isNull || err != nil {
return types.ZeroDuration, true, err
}
result, isNull, err := b.durationOp(&b.baseDateArithmetical, b.ctx, dur, interval, unit, b.tp.GetDecimal())
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.GetDecimal()
}
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.SetFlenUnderLimit(retFLen)
bf.tp.SetDecimalUnderLimit(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.
// https://dev.mysql.com/doc/refman/8.0/en/date-and-time-functions.html#function_unix-timestamp
func goTimeToMysqlUnixTimestamp(t time.Time, decimal int) (*types.MyDecimal, error) {
nanoSeconds := t.UnixNano()
// Prior to MySQL 8.0.28, the valid range of argument values is the same as for the TIMESTAMP data type:
// '1970-01-01 00:00:01.000000' UTC to '2038-01-19 03:14:07.999999' UTC.
// This is also the case in MySQL 8.0.28 and later for 32-bit platforms.
if nanoSeconds < 1e9 || (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.AdjustedGoTime(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.GetDecimal())
return result, err != nil, err
}
type timestampFunctionClass struct {
baseFunctionClass
}
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, err := getExpressionFsp(ctx, args[0])
if err != nil {
return nil, err
}
if argLen == 2 {
fsp2, err := getExpressionFsp(ctx, args[1])
if err != nil {
return nil, err
}
if fsp2 > fsp {
fsp = fsp2
}
}
isFloat := false
switch args[0].GetType().GetType() {
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.setDecimalAndFlenForDatetime(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)
}
if tm.Year() == 0 {
// MySQL won't evaluate add for date with zero year.
// See https://github.com/mysql/mysql-server/blob/5.7/sql/item_timefunc.cc#L2805
return types.ZeroTime, true, nil
}
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.TypeDatetime, 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.setDecimalAndFlenForDatetime(tm.Fsp())
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) int {
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.GetType() {
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.GetType() {
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
}
switch retTp {
case types.ETDatetime:
bf.setDecimalAndFlenForDatetime(mathutil.Min(mathutil.Max(arg0Dec, arg1Dec), types.MaxFsp))
case types.ETDuration:
bf.setDecimalAndFlenForTime(mathutil.Min(mathutil.Max(arg0Dec, arg1Dec), types.MaxFsp))
case types.ETString:
bf.tp.SetType(mysql.TypeString)
bf.tp.SetFlen(mysql.MaxDatetimeWidthWithFsp)
bf.tp.SetDecimal(types.UnspecifiedLength)
}
return
}
func getTimeZone(ctx sessionctx.Context) *time.Location {
ret := ctx.GetSessionVars().Location()
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) (result string, isNull bool, err error) {
arg0, err := types.ParseTime(sc, d, mysql.TypeDatetime, types.MaxFsp)
if err != nil {
// Return a warning regardless of the sql_mode, this is compatible with MySQL.
sc.AppendWarning(err)
return "", true, nil
}
ret, err := arg0.Add(sc, arg1)
if err != nil {
return "", false, err
}
fsp := types.MaxFsp
if ret.Microsecond() == 0 {
fsp = types.MinFsp
}
ret.SetFsp(fsp)
return ret.String(), false, 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) (result string, isNull bool, err error) {
arg0, err := types.ParseTime(sc, d, mysql.TypeDatetime, types.MaxFsp)
if err != nil {
// Return a warning regardless of the sql_mode, this is compatible with MySQL.
sc.AppendWarning(err)
return "", true, nil
}
resultTime, err := arg0.Add(sc, arg1.Neg())
if err != nil {
return "", false, err
}
fsp := types.MaxFsp
if resultTime.Microsecond() == 0 {
fsp = types.MinFsp
}
resultTime.SetFsp(fsp)
return resultTime.String(), false, 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.GetType() {
case mysql.TypeDatetime, mysql.TypeTimestamp:
switch tp2.GetType() {
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:
charset, collate := ctx.GetSessionVars().GetCharsetInfo()
bf.tp.SetCharset(charset)
bf.tp.SetCollate(collate)
switch tp2.GetType() {
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.GetType() {
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.GetType() {
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, isNull, err = strDatetimeAddDuration(sc, arg0, arg1)
return result, isNull, 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.GetFlag()) {
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
}
check := arg1Str
_, check, err = parser.Number(parser.Space0(check))
if err == nil {
check, err = parser.Char(check, '-')
if strings.Compare(check, "") != 0 && err == nil {
return "", true, nil
}
}
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, isNull, err = strDatetimeAddDuration(sc, arg0, arg1)
return result, isNull, 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 := 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().GetDecimal()
case types.ETString:
str, isNull, err := dt.EvalString(ctx, chunk.Row{})
if err == nil && !isNull {
decimal = types.DateFSP(str)
}
}
}
if decimal > types.MaxFsp {
return types.MaxFsp
}
if decimal < types.MinFsp {
return 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-3]):[0-5]?\d$)|(^\+14: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.setDecimalAndFlenForDatetime(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
}
if dt.InvalidZero() {
return types.ZeroTime, true, handleInvalidTimeError(b.ctx, types.ErrWrongValue.GenWithStackByArgs(types.DateTimeStr, dt.String()))
}
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)
var fromTz, toTz *time.Location
var err error
if fromTzMatched {
fromTz = time.FixedZone(fromTzStr, timeZone2int(fromTzStr))
} else {
if strings.EqualFold(fromTzStr, "SYSTEM") {
fromTzStr = "Local"
}
fromTz, err = time.LoadLocation(fromTzStr)
if err != nil {
return types.ZeroTime, true, nil
}
}
t, err := dt.GoTime(fromTz)
if err != nil {
return types.ZeroTime, true, nil
}
t = t.In(time.UTC)
if toTzMatched {
toTz = time.FixedZone(toTzStr, timeZone2int(toTzStr))
} else {
if strings.EqualFold(toTzStr, "SYSTEM") {
toTzStr = "Local"
}
toTz, err = time.LoadLocation(toTzStr)
if err != nil {
return types.ZeroTime, true, nil
}
}
return types.NewTime(types.FromGoTime(t.In(toTz)), mysql.TypeDatetime, b.tp.GetDecimal()), false, 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
}
bf.setDecimalAndFlenForDate()
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, decimal := args[2].GetType().EvalType(), 0
switch tp {
case types.ETInt:
case types.ETReal, types.ETDecimal:
decimal = args[2].GetType().GetDecimal()
if decimal > 6 || decimal == types.UnspecifiedLength {
decimal = 6
}
default:
decimal = 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.setDecimalAndFlenForTime(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) && minute == 59 && second > 59 {
overflow = true
}
if overflow {
minute = 59
second = 59
}
fsp := b.tp.GetDecimal()
d, _, err := types.ParseDuration(b.ctx.GetSessionVars().StmtCtx, fmt.Sprintf("%02d:%02d:%v", hour, minute, second), fsp)
return d, err
}
// 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().GetFlag()))
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.SetFlen(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.SetFlen(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.SetFlen(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)
}
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 retFsp int
argType := args[0].GetType()
argEvalTp := argType.EvalType()
if argEvalTp == types.ETString {
retFsp = types.UnspecifiedLength
} else {
retFsp = argType.GetDecimal()
}
if retFsp > types.MaxFsp || retFsp == types.UnspecifiedFsp {
retFsp = types.MaxFsp
} else if retFsp < types.MinFsp {
retFsp = types.MinFsp
}
bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETDuration, types.ETReal)
if err != nil {
return nil, err
}
bf.setDecimalAndFlenForTime(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 uint64
minute uint64
second uint64
demical float64
secondDemical float64
negative string
)
if secondsFloat < 0 {
negative = "-"
secondsFloat = math.Abs(secondsFloat)
}
seconds := uint64(secondsFloat)
demical = secondsFloat - float64(seconds)
hour = seconds / 3600
if hour > 838 {
hour = 838
minute = 59
second = 59
demical = 0
err = b.ctx.GetSessionVars().StmtCtx.HandleTruncate(errTruncatedWrongValue.GenWithStackByArgs("time", strconv.FormatFloat(secondsFloat, 'f', -1, 64)))
if err != nil {
return types.Duration{}, err != nil, err
}
} 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)), b.tp.GetDecimal())
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.GetType() {
case mysql.TypeDatetime, mysql.TypeTimestamp:
switch tp2.GetType() {
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:
charset, collate := ctx.GetSessionVars().GetCharsetInfo()
bf.tp.SetCharset(charset)
bf.tp.SetCollate(collate)
switch tp2.GetType() {
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.GetType() {
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.GetType() {
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
result, err := arg0.Add(sc, arg1.Neg())
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 !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.Neg())
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, isNull, err = strDatetimeSubDuration(sc, arg0, arg1)
return result, isNull, 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.GetFlag()) {
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, isNull, err = strDatetimeSubDuration(sc, arg0, arg1)
return result, isNull, 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.SetFlen((args[1].GetType().GetFlen() + 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.SetFlen(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
}
flen := mysql.MaxDatetimeWidthNoFsp
con, ok := args[0].(*Constant)
if !ok {
return nil, errors.New("should not happened")
}
unit, null, err := con.EvalString(ctx, chunk.Row{})
if null || err != nil {
return nil, errors.New("should not happened")
}
if unit == ast.TimeUnitMicrosecond.String() {
flen = mysql.MaxDatetimeWidthWithFsp
}
bf.tp.SetFlen(flen)
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 {
b.ctx.GetSessionVars().StmtCtx.AppendWarning(err)
return "", true, nil
}
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)
}
if arg.InvalidZero() {
return 0, true, handleInvalidTimeError(b.ctx, types.ErrWrongValue.GenWithStackByArgs(types.DateTimeStr, arg.String()))
}
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)
}
if arg.InvalidZero() {
return 0, true, handleInvalidTimeError(b.ctx, types.ErrWrongValue.GenWithStackByArgs(types.DateTimeStr, arg.String()))
}
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) 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
}
fsp, err := getFspByIntArg(bf.ctx, args)
if err != nil {
return nil, err
}
bf.setDecimalAndFlenForTime(fsp)
// 1. no sign.
// 2. hour is in the 2-digit range.
bf.tp.SetFlen(bf.tp.GetFlen() - 2)
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), int(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.setDecimalAndFlenForDate()
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 tm.Month() == 0 || (tm.Day() == 0 && b.ctx.GetSessionVars().SQLMode.HasNoZeroDateMode()) {
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.
// This function must by called before calling newBaseBuiltinFuncWithTp.
func getExpressionFsp(ctx sessionctx.Context, expression Expression) (int, error) {
constExp, isConstant := expression.(*Constant)
if isConstant {
str, isNil, err := constExp.EvalString(ctx, chunk.Row{})
if isNil || err != nil {
return 0, err
}
return types.GetFsp(str), nil
}
warpExpr := WrapWithCastAsTime(ctx, expression, types.NewFieldType(mysql.TypeDatetime))
return mathutil.Min(warpExpr.GetType().GetDecimal(), 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.SetType(mysql.TypeDate)
bf.tp.SetFlen(mysql.MaxDateWidth)
bf.tp.SetDecimal(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
}
// tidbBoundedStalenessFunctionClass reads a time window [a, b] and compares it with the latest SafeTS
// to determine which TS to use in a read only transaction.
type tidbBoundedStalenessFunctionClass struct {
baseFunctionClass
}
func (c *tidbBoundedStalenessFunctionClass) 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, types.ETDatetime)
if err != nil {
return nil, err
}
bf.setDecimalAndFlenForDatetime(3)
sig := &builtinTiDBBoundedStalenessSig{bf}
return sig, nil
}
type builtinTiDBBoundedStalenessSig struct {
baseBuiltinFunc
}
func (b *builtinTiDBBoundedStalenessSig) Clone() builtinFunc {
newSig := &builtinTidbParseTsoSig{}
newSig.cloneFrom(&b.baseBuiltinFunc)
return newSig
}
func (b *builtinTiDBBoundedStalenessSig) evalTime(row chunk.Row) (types.Time, bool, error) {
leftTime, isNull, err := b.args[0].EvalTime(b.ctx, row)
if isNull || err != nil {
return types.ZeroTime, true, handleInvalidTimeError(b.ctx, err)
}
rightTime, isNull, err := b.args[1].EvalTime(b.ctx, row)
if isNull || err != nil {
return types.ZeroTime, true, handleInvalidTimeError(b.ctx, err)
}
if invalidLeftTime, invalidRightTime := leftTime.InvalidZero(), rightTime.InvalidZero(); invalidLeftTime || invalidRightTime {
if invalidLeftTime {
err = handleInvalidTimeError(b.ctx, types.ErrWrongValue.GenWithStackByArgs(types.DateTimeStr, leftTime.String()))
}
if invalidRightTime {
err = handleInvalidTimeError(b.ctx, types.ErrWrongValue.GenWithStackByArgs(types.DateTimeStr, rightTime.String()))
}
return types.ZeroTime, true, err
}
timeZone := getTimeZone(b.ctx)
minTime, err := leftTime.GoTime(timeZone)
if err != nil {
return types.ZeroTime, true, err
}
maxTime, err := rightTime.GoTime(timeZone)
if err != nil {
return types.ZeroTime, true, err
}
if minTime.After(maxTime) {
return types.ZeroTime, true, nil
}
// Because the minimum unit of a TSO is millisecond, so we only need fsp to be 3.
return types.NewTime(types.FromGoTime(calAppropriateTime(minTime, maxTime, getMinSafeTime(b.ctx, timeZone))), mysql.TypeDatetime, 3), false, nil
}
// GetMinSafeTime get minSafeTime
func GetMinSafeTime(sessionCtx sessionctx.Context) time.Time {
return getMinSafeTime(sessionCtx, getTimeZone(sessionCtx))
}
func getMinSafeTime(sessionCtx sessionctx.Context, timeZone *time.Location) time.Time {
var minSafeTS uint64
txnScope := config.GetTxnScopeFromConfig()
if store := sessionCtx.GetStore(); store != nil {
minSafeTS = store.GetMinSafeTS(txnScope)
}
// Inject mocked SafeTS for test.
failpoint.Inject("injectSafeTS", func(val failpoint.Value) {
injectTS := val.(int)
minSafeTS = uint64(injectTS)
})
// Try to get from the stmt cache to make sure this function is deterministic.
stmtCtx := sessionCtx.GetSessionVars().StmtCtx
minSafeTS = stmtCtx.GetOrStoreStmtCache(stmtctx.StmtSafeTSCacheKey, minSafeTS).(uint64)
return oracle.GetTimeFromTS(minSafeTS).In(timeZone)
}
// CalAppropriateTime directly calls calAppropriateTime
func CalAppropriateTime(minTime, maxTime, minSafeTime time.Time) time.Time {
return calAppropriateTime(minTime, maxTime, minSafeTime)
}
// For a SafeTS t and a time range [t1, t2]:
// 1. If t < t1, we will use t1 as the result,
// and with it, a read request may fail because it's an unreached SafeTS.
// 2. If t1 <= t <= t2, we will use t as the result, and with it,
// a read request won't fail.
// 2. If t2 < t, we will use t2 as the result,
// and with it, a read request won't fail because it's bigger than the latest SafeTS.
func calAppropriateTime(minTime, maxTime, minSafeTime time.Time) time.Time {
if minSafeTime.Before(minTime) || minSafeTime.After(maxTime) {
logutil.BgLogger().Warn("calAppropriateTime",
zap.Time("minTime", minTime),
zap.Time("maxTime", maxTime),
zap.Time("minSafeTime", minSafeTime))
if minSafeTime.Before(minTime) {
return minTime
} else if minSafeTime.After(maxTime) {
return maxTime
}
}
logutil.BgLogger().Debug("calAppropriateTime",
zap.Time("minTime", minTime),
zap.Time("maxTime", maxTime),
zap.Time("minSafeTime", minSafeTime))
return minSafeTime
}
// getFspByIntArg is used by some time functions to get the result fsp. If len(expr) == 0, then the fsp is not explicit set, use 0 as default.
func getFspByIntArg(ctx sessionctx.Context, exps []Expression) (int, error) {
if len(exps) == 0 {
return 0, nil
}
if len(exps) != 1 {
return 0, errors.Errorf("Should not happen, the num of argument should be 1, but got %d", len(exps))
}
_, ok := exps[0].(*Constant)
if ok {
fsp, isNuLL, err := exps[0].EvalInt(ctx, chunk.Row{})
if err != nil || isNuLL {
// If isNULL, it may be a bug of parser. Return 0 to be compatible with old version.
return 0, err
}
if fsp > int64(types.MaxFsp) {
return 0, errors.Errorf("Too-big precision %v specified for 'curtime'. Maximum is %v", fsp, types.MaxFsp)
} else if fsp < int64(types.MinFsp) {
return 0, errors.Errorf("Invalid negative %d specified, must in [0, 6]", fsp)
}
return int(fsp), nil
}
// Should no happen. But our tests may generate non-constant input.
return 0, nil
}
相关信息
相关文章
tidb builtin_arithmetic_vec 源码
0
赞
热门推荐
-
2、 - 优质文章
-
3、 gate.io
-
8、 golang
-
9、 openharmony
-
10、 Vue中input框自动聚焦