3406 lines
99 KiB
Go
3406 lines
99 KiB
Go
// Copyright 2013 The ql Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSES/QL-LICENSE file.
|
|
|
|
// Copyright 2015 PingCAP, Inc.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package expression
|
|
|
|
import (
|
|
"fmt"
|
|
"math"
|
|
"regexp"
|
|
"strings"
|
|
"time"
|
|
|
|
log "github.com/Sirupsen/logrus"
|
|
"github.com/cznic/mathutil"
|
|
"github.com/juju/errors"
|
|
"github.com/pingcap/tidb/ast"
|
|
"github.com/pingcap/tidb/context"
|
|
"github.com/pingcap/tidb/mysql"
|
|
"github.com/pingcap/tidb/sessionctx/variable"
|
|
"github.com/pingcap/tidb/terror"
|
|
"github.com/pingcap/tidb/util/types"
|
|
)
|
|
|
|
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"
|
|
)
|
|
|
|
// DurationPattern determine whether to match the format of duration.
|
|
var DurationPattern = regexp.MustCompile(`^(|[-]?)(|\d{1,2}\s)(\d{2,3}:\d{2}:\d{2}|\d{1,2}:\d{2}|\d{1,6})(|\.\d*)$`)
|
|
|
|
var (
|
|
_ functionClass = &dateFunctionClass{}
|
|
_ 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 = &utcDateFunctionClass{}
|
|
_ functionClass = &utcTimestampFunctionClass{}
|
|
_ functionClass = &extractFunctionClass{}
|
|
_ functionClass = &arithmeticFunctionClass{}
|
|
_ 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 = &lastDayFunctionClass{}
|
|
)
|
|
|
|
var (
|
|
_ builtinFunc = &builtinDateSig{}
|
|
_ builtinFunc = &builtinDateDiffSig{}
|
|
_ builtinFunc = &builtinTimeDiffSig{}
|
|
_ 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 = &builtinUTCDateSig{}
|
|
_ builtinFunc = &builtinUTCTimestampWithArgSig{}
|
|
_ builtinFunc = &builtinUTCTimestampWithoutArgSig{}
|
|
_ builtinFunc = &builtinArithmeticSig{}
|
|
_ builtinFunc = &builtinUnixTimestampCurrentSig{}
|
|
_ builtinFunc = &builtinUnixTimestampIntSig{}
|
|
_ builtinFunc = &builtinUnixTimestampDecSig{}
|
|
_ builtinFunc = &builtinAddTimeSig{}
|
|
_ builtinFunc = &builtinConvertTzSig{}
|
|
_ builtinFunc = &builtinMakeDateSig{}
|
|
_ builtinFunc = &builtinMakeTimeSig{}
|
|
_ builtinFunc = &builtinPeriodAddSig{}
|
|
_ builtinFunc = &builtinPeriodDiffSig{}
|
|
_ builtinFunc = &builtinQuarterSig{}
|
|
_ builtinFunc = &builtinSecToTimeSig{}
|
|
_ builtinFunc = &builtinSubTimeSig{}
|
|
_ builtinFunc = &builtinTimeToSecSig{}
|
|
_ builtinFunc = &builtinTimestampAddSig{}
|
|
_ builtinFunc = &builtinToDaysSig{}
|
|
_ builtinFunc = &builtinToSecondsSig{}
|
|
_ builtinFunc = &builtinUTCTimeWithArgSig{}
|
|
_ builtinFunc = &builtinUTCTimeWithoutArgSig{}
|
|
_ builtinFunc = &builtinTimestamp1ArgSig{}
|
|
_ builtinFunc = &builtinTimestamp2ArgsSig{}
|
|
_ builtinFunc = &builtinLastDaySig{}
|
|
_ builtinFunc = &builtinStrToDateDateSig{}
|
|
_ builtinFunc = &builtinStrToDateDatetimeSig{}
|
|
_ builtinFunc = &builtinStrToDateDurationSig{}
|
|
_ builtinFunc = &builtinFromUnixTime1ArgSig{}
|
|
_ builtinFunc = &builtinFromUnixTime2ArgSig{}
|
|
_ builtinFunc = &builtinExtractDatetimeSig{}
|
|
_ builtinFunc = &builtinExtractDurationSig{}
|
|
)
|
|
|
|
// handleInvalidTimeError reports error or warning depend on the context.
|
|
func handleInvalidTimeError(ctx context.Context, err error) error {
|
|
if err == nil || !terror.ErrorEqual(err, types.ErrInvalidTimeFormat) {
|
|
return err
|
|
}
|
|
sc := ctx.GetSessionVars().StmtCtx
|
|
if ctx.GetSessionVars().StrictSQLMode && (sc.InInsertStmt || sc.InUpdateOrDeleteStmt) {
|
|
return err
|
|
}
|
|
sc.AppendWarning(err)
|
|
return nil
|
|
}
|
|
|
|
func convertTimeToMysqlTime(t time.Time, fsp int) (types.Time, error) {
|
|
tr, err := types.RoundFrac(t, int(fsp))
|
|
if err != nil {
|
|
return types.Time{}, errors.Trace(err)
|
|
}
|
|
|
|
return types.Time{
|
|
Time: types.FromGoTime(tr),
|
|
Type: mysql.TypeDatetime,
|
|
Fsp: fsp,
|
|
}, nil
|
|
}
|
|
|
|
func convertToTimeWithFsp(sc *variable.StatementContext, arg types.Datum, tp byte, fsp int) (d types.Datum, err error) {
|
|
if fsp > types.MaxFsp {
|
|
fsp = types.MaxFsp
|
|
}
|
|
|
|
f := types.NewFieldType(tp)
|
|
f.Decimal = fsp
|
|
|
|
d, err = arg.ConvertTo(sc, f)
|
|
if err != nil {
|
|
d.SetNull()
|
|
return d, errors.Trace(err)
|
|
}
|
|
|
|
if d.IsNull() {
|
|
return
|
|
}
|
|
|
|
if d.Kind() != types.KindMysqlTime {
|
|
d.SetNull()
|
|
return d, errors.Errorf("need time type, but got %T", d.GetValue())
|
|
}
|
|
return
|
|
}
|
|
|
|
func convertToTime(sc *variable.StatementContext, arg types.Datum, tp byte) (d types.Datum, err error) {
|
|
return convertToTimeWithFsp(sc, arg, tp, types.MaxFsp)
|
|
}
|
|
|
|
func convertToDuration(sc *variable.StatementContext, arg types.Datum, fsp int) (d types.Datum, err error) {
|
|
f := types.NewFieldType(mysql.TypeDuration)
|
|
f.Decimal = fsp
|
|
|
|
d, err = arg.ConvertTo(sc, f)
|
|
if err != nil {
|
|
d.SetNull()
|
|
return d, errors.Trace(err)
|
|
}
|
|
|
|
if d.IsNull() {
|
|
return
|
|
}
|
|
|
|
if d.Kind() != types.KindMysqlDuration {
|
|
d.SetNull()
|
|
return d, errors.Errorf("need duration type, but got %T", d.GetValue())
|
|
}
|
|
return
|
|
}
|
|
|
|
type dateFunctionClass struct {
|
|
baseFunctionClass
|
|
}
|
|
|
|
func (c *dateFunctionClass) getFunction(ctx context.Context, args []Expression) (builtinFunc, error) {
|
|
if err := c.verifyArgs(args); err != nil {
|
|
return nil, errors.Trace(err)
|
|
}
|
|
bf := newBaseBuiltinFuncWithTp(args, ctx, tpDatetime, tpDatetime)
|
|
bf.tp.Tp, bf.tp.Flen, bf.tp.Decimal = mysql.TypeDate, 10, 0
|
|
sig := &builtinDateSig{baseTimeBuiltinFunc{bf}}
|
|
return sig.setSelf(sig), nil
|
|
}
|
|
|
|
type builtinDateSig struct {
|
|
baseTimeBuiltinFunc
|
|
}
|
|
|
|
// 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 []types.Datum) (types.Time, bool, error) {
|
|
sc := b.ctx.GetSessionVars().StmtCtx
|
|
|
|
expr, isNull, err := b.args[0].EvalTime(row, sc)
|
|
if isNull || err != nil {
|
|
return types.Time{}, true, errors.Trace(handleInvalidTimeError(b.ctx, err))
|
|
}
|
|
|
|
if expr.IsZero() {
|
|
return types.Time{}, true, errors.Trace(handleInvalidTimeError(b.ctx, types.ErrInvalidTimeFormat))
|
|
}
|
|
|
|
expr.Time = types.FromDate(expr.Time.Year(), expr.Time.Month(), expr.Time.Day(), 0, 0, 0, 0)
|
|
expr.Type = mysql.TypeDate
|
|
return expr, false, nil
|
|
}
|
|
|
|
func convertDatumToTime(sc *variable.StatementContext, d types.Datum) (t types.Time, err error) {
|
|
if d.Kind() != types.KindMysqlTime {
|
|
d, err = convertToTime(sc, d, mysql.TypeDatetime)
|
|
if err != nil {
|
|
return t, errors.Trace(err)
|
|
}
|
|
}
|
|
return d.GetMysqlTime(), nil
|
|
}
|
|
|
|
type dateDiffFunctionClass struct {
|
|
baseFunctionClass
|
|
}
|
|
|
|
func (c *dateDiffFunctionClass) getFunction(ctx context.Context, args []Expression) (builtinFunc, error) {
|
|
if err := c.verifyArgs(args); err != nil {
|
|
return nil, errors.Trace(err)
|
|
}
|
|
bf := newBaseBuiltinFuncWithTp(args, ctx, tpInt, tpDatetime, tpDatetime)
|
|
sig := &builtinDateDiffSig{baseIntBuiltinFunc{bf}}
|
|
return sig.setSelf(sig), nil
|
|
}
|
|
|
|
type builtinDateDiffSig struct {
|
|
baseIntBuiltinFunc
|
|
}
|
|
|
|
// 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 []types.Datum) (int64, bool, error) {
|
|
ctx := b.ctx.GetSessionVars().StmtCtx
|
|
t1, isNull, err := b.args[0].EvalTime(row, ctx)
|
|
if isNull || err != nil {
|
|
return 0, true, errors.Trace(handleInvalidTimeError(b.ctx, err))
|
|
}
|
|
t2, isNull, err := b.args[1].EvalTime(row, ctx)
|
|
if isNull || err != nil {
|
|
return 0, true, errors.Trace(handleInvalidTimeError(b.ctx, err))
|
|
}
|
|
if t1.Time.Month() == 0 || t1.Time.Day() == 0 || t2.Time.Month() == 0 || t2.Time.Day() == 0 {
|
|
return 0, true, errors.Trace(handleInvalidTimeError(b.ctx, types.ErrInvalidTimeFormat))
|
|
}
|
|
return int64(types.DateDiff(t1.Time, t2.Time)), false, nil
|
|
}
|
|
|
|
type timeDiffFunctionClass struct {
|
|
baseFunctionClass
|
|
}
|
|
|
|
func (c *timeDiffFunctionClass) getFunction(ctx context.Context, args []Expression) (builtinFunc, error) {
|
|
if err := c.verifyArgs(args); err != nil {
|
|
return nil, errors.Trace(err)
|
|
}
|
|
sig := &builtinTimeDiffSig{newBaseBuiltinFunc(args, ctx)}
|
|
return sig.setSelf(sig), nil
|
|
}
|
|
|
|
type builtinTimeDiffSig struct {
|
|
baseBuiltinFunc
|
|
}
|
|
|
|
func (b *builtinTimeDiffSig) getStrFsp(strArg string, fsp int) int {
|
|
if n := strings.IndexByte(strArg, '.'); n >= 0 {
|
|
lenStrFsp := len(strArg[n+1:])
|
|
if lenStrFsp <= types.MaxFsp {
|
|
fsp = int(math.Max(float64(lenStrFsp), float64(fsp)))
|
|
}
|
|
}
|
|
return fsp
|
|
}
|
|
|
|
func (b *builtinTimeDiffSig) convertArgToTime(sc *variable.StatementContext, arg types.Datum, fsp int) (t types.Time, err error) {
|
|
// Fix issue #3923, see https://github.com/pingcap/tidb/issues/3923,
|
|
// TIMEDIFF() returns expr1 − expr2 expressed as a Duration value. expr1 and expr2 are Duration or date-and-time expressions,
|
|
// but both must be of the same type. if expr is a string, we first try to convert it to Duration, if it failed,
|
|
// we then try to convert it to Datetime
|
|
switch arg.Kind() {
|
|
case types.KindString, types.KindBytes:
|
|
strArg := arg.GetString()
|
|
fsp = b.getStrFsp(strArg, fsp)
|
|
t, err = types.StrToDuration(sc, strArg, fsp)
|
|
case types.KindMysqlDuration:
|
|
t, err = arg.GetMysqlDuration().ConvertToTime(mysql.TypeDuration)
|
|
default:
|
|
t, err = convertDatumToTime(sc, arg)
|
|
}
|
|
return t, errors.Trace(err)
|
|
}
|
|
|
|
// eval evals a builtinTimeDiffSig.
|
|
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_timediff
|
|
func (b *builtinTimeDiffSig) eval(row []types.Datum) (d types.Datum, err error) {
|
|
args, err := b.evalArgs(row)
|
|
if err != nil {
|
|
return d, errors.Trace(err)
|
|
}
|
|
if args[0].IsNull() || args[1].IsNull() {
|
|
return
|
|
}
|
|
|
|
sc := b.ctx.GetSessionVars().StmtCtx
|
|
fsp := int(math.Max(float64(args[0].Frac()), float64(args[1].Frac())))
|
|
t0, err := b.convertArgToTime(sc, args[0], fsp)
|
|
if err != nil {
|
|
return d, errors.Trace(err)
|
|
}
|
|
t1, err := b.convertArgToTime(sc, args[1], fsp)
|
|
if err != nil {
|
|
return d, errors.Trace(err)
|
|
}
|
|
if (types.IsTemporalWithDate(t0.Type) &&
|
|
t1.Type == mysql.TypeDuration) ||
|
|
(types.IsTemporalWithDate(t1.Type) &&
|
|
t0.Type == mysql.TypeDuration) {
|
|
return d, nil // Incompatible types, return NULL
|
|
}
|
|
|
|
t := t0.Sub(&t1)
|
|
ret, truncated := types.TruncateOverflowMySQLTime(t.Duration)
|
|
if truncated {
|
|
err = types.ErrTruncatedWrongVal.GenByArgs("time", t.String())
|
|
err = sc.HandleTruncate(err)
|
|
}
|
|
t.Duration = ret
|
|
d.SetMysqlDuration(t)
|
|
return
|
|
}
|
|
|
|
type dateFormatFunctionClass struct {
|
|
baseFunctionClass
|
|
}
|
|
|
|
func (c *dateFormatFunctionClass) getFunction(ctx context.Context, args []Expression) (builtinFunc, error) {
|
|
if err := c.verifyArgs(args); err != nil {
|
|
return nil, errors.Trace(err)
|
|
}
|
|
bf := newBaseBuiltinFuncWithTp(args, ctx, tpString, tpDatetime, tpString)
|
|
// worst case: formatMask=%r%r%r...%r, each %r takes 11 characters
|
|
bf.tp.Flen = (args[1].GetType().Flen + 1) / 2 * 11
|
|
sig := &builtinDateFormatSig{baseStringBuiltinFunc{bf}}
|
|
return sig.setSelf(sig), nil
|
|
|
|
}
|
|
|
|
type builtinDateFormatSig struct {
|
|
baseStringBuiltinFunc
|
|
}
|
|
|
|
// eval 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 []types.Datum) (string, bool, error) {
|
|
sc := b.ctx.GetSessionVars().StmtCtx
|
|
t, isNull, err := b.args[0].EvalTime(row, sc)
|
|
if isNull || err != nil {
|
|
return "", isNull, errors.Trace(handleInvalidTimeError(b.ctx, err))
|
|
}
|
|
if t.InvalidZero() {
|
|
return "", true, errors.Trace(handleInvalidTimeError(b.ctx, types.ErrInvalidTimeFormat))
|
|
}
|
|
formatMask, isNull, err := b.args[1].EvalString(row, sc)
|
|
if isNull || err != nil {
|
|
return "", isNull, errors.Trace(err)
|
|
}
|
|
|
|
res, err := t.DateFormat(formatMask)
|
|
return res, isNull, errors.Trace(err)
|
|
}
|
|
|
|
// builtinDateFormat ...
|
|
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_date-format
|
|
func builtinDateFormat(ctx context.Context, args []types.Datum) (d types.Datum, err error) {
|
|
date, err := convertToTime(ctx.GetSessionVars().StmtCtx, args[0], mysql.TypeDatetime)
|
|
if err != nil {
|
|
return d, errors.Trace(err)
|
|
}
|
|
|
|
if date.IsNull() {
|
|
return
|
|
}
|
|
t := date.GetMysqlTime()
|
|
str, err := t.DateFormat(args[1].GetString())
|
|
if err != nil {
|
|
return d, errors.Trace(err)
|
|
}
|
|
d.SetString(str)
|
|
return
|
|
}
|
|
|
|
type fromDaysFunctionClass struct {
|
|
baseFunctionClass
|
|
}
|
|
|
|
func (c *fromDaysFunctionClass) getFunction(ctx context.Context, args []Expression) (builtinFunc, error) {
|
|
if err := c.verifyArgs(args); err != nil {
|
|
return nil, errors.Trace(err)
|
|
}
|
|
bf := newBaseBuiltinFuncWithTp(args, ctx, tpDatetime, tpInt)
|
|
bf.tp.Flen, bf.tp.Decimal = 10, 0
|
|
sig := &builtinFromDaysSig{baseTimeBuiltinFunc{bf}}
|
|
return sig.setSelf(sig), nil
|
|
}
|
|
|
|
type builtinFromDaysSig struct {
|
|
baseTimeBuiltinFunc
|
|
}
|
|
|
|
// evalTime evals FROM_DAYS(N).
|
|
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_from-days
|
|
func (b *builtinFromDaysSig) evalTime(row []types.Datum) (types.Time, bool, error) {
|
|
sc := b.getCtx().GetSessionVars().StmtCtx
|
|
|
|
n, isNull, err := b.args[0].EvalInt(row, sc)
|
|
if isNull || err != nil {
|
|
return types.Time{}, true, errors.Trace(err)
|
|
}
|
|
|
|
return types.TimeFromDays(n), false, nil
|
|
}
|
|
|
|
type hourFunctionClass struct {
|
|
baseFunctionClass
|
|
}
|
|
|
|
func (c *hourFunctionClass) getFunction(ctx context.Context, args []Expression) (builtinFunc, error) {
|
|
if err := c.verifyArgs(args); err != nil {
|
|
return nil, errors.Trace(err)
|
|
}
|
|
bf := newBaseBuiltinFuncWithTp(args, ctx, tpInt, tpDuration)
|
|
bf.tp.Flen, bf.tp.Decimal = 3, 0
|
|
sig := &builtinHourSig{baseIntBuiltinFunc{bf}}
|
|
return sig.setSelf(sig), nil
|
|
}
|
|
|
|
type builtinHourSig struct {
|
|
baseIntBuiltinFunc
|
|
}
|
|
|
|
// 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 []types.Datum) (int64, bool, error) {
|
|
dur, isNull, err := b.args[0].EvalDuration(row, b.ctx.GetSessionVars().StmtCtx)
|
|
// 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 context.Context, args []Expression) (builtinFunc, error) {
|
|
if err := c.verifyArgs(args); err != nil {
|
|
return nil, errors.Trace(err)
|
|
}
|
|
bf := newBaseBuiltinFuncWithTp(args, ctx, tpInt, tpDuration)
|
|
bf.tp.Flen, bf.tp.Decimal = 2, 0
|
|
sig := &builtinMinuteSig{baseIntBuiltinFunc{bf}}
|
|
return sig.setSelf(sig), nil
|
|
}
|
|
|
|
type builtinMinuteSig struct {
|
|
baseIntBuiltinFunc
|
|
}
|
|
|
|
// 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 []types.Datum) (int64, bool, error) {
|
|
dur, isNull, err := b.args[0].EvalDuration(row, b.ctx.GetSessionVars().StmtCtx)
|
|
// 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 context.Context, args []Expression) (builtinFunc, error) {
|
|
if err := c.verifyArgs(args); err != nil {
|
|
return nil, errors.Trace(err)
|
|
}
|
|
bf := newBaseBuiltinFuncWithTp(args, ctx, tpInt, tpDuration)
|
|
bf.tp.Flen, bf.tp.Decimal = 2, 0
|
|
sig := &builtinSecondSig{baseIntBuiltinFunc{bf}}
|
|
return sig.setSelf(sig), nil
|
|
}
|
|
|
|
type builtinSecondSig struct {
|
|
baseIntBuiltinFunc
|
|
}
|
|
|
|
// 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 []types.Datum) (int64, bool, error) {
|
|
dur, isNull, err := b.args[0].EvalDuration(row, b.ctx.GetSessionVars().StmtCtx)
|
|
// 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 context.Context, args []Expression) (builtinFunc, error) {
|
|
if err := c.verifyArgs(args); err != nil {
|
|
return nil, errors.Trace(err)
|
|
}
|
|
bf := newBaseBuiltinFuncWithTp(args, ctx, tpInt, tpDuration)
|
|
bf.tp.Flen, bf.tp.Decimal = 6, 0
|
|
sig := &builtinMicroSecondSig{baseIntBuiltinFunc{bf}}
|
|
return sig.setSelf(sig), nil
|
|
}
|
|
|
|
type builtinMicroSecondSig struct {
|
|
baseIntBuiltinFunc
|
|
}
|
|
|
|
// 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 []types.Datum) (int64, bool, error) {
|
|
dur, isNull, err := b.args[0].EvalDuration(row, b.ctx.GetSessionVars().StmtCtx)
|
|
// 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 context.Context, args []Expression) (builtinFunc, error) {
|
|
if err := c.verifyArgs(args); err != nil {
|
|
return nil, errors.Trace(err)
|
|
}
|
|
bf := newBaseBuiltinFuncWithTp(args, ctx, tpInt, tpDatetime)
|
|
bf.tp.Flen, bf.tp.Decimal = 2, 0
|
|
sig := &builtinMonthSig{baseIntBuiltinFunc{bf}}
|
|
return sig.setSelf(sig), nil
|
|
}
|
|
|
|
type builtinMonthSig struct {
|
|
baseIntBuiltinFunc
|
|
}
|
|
|
|
// 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 []types.Datum) (int64, bool, error) {
|
|
sc := b.getCtx().GetSessionVars().StmtCtx
|
|
date, isNull, err := b.args[0].EvalTime(row, sc)
|
|
|
|
if isNull || err != nil {
|
|
return 0, true, errors.Trace(handleInvalidTimeError(b.ctx, err))
|
|
}
|
|
|
|
if date.IsZero() {
|
|
return 0, true, errors.Trace(handleInvalidTimeError(b.ctx, types.ErrInvalidTimeFormat))
|
|
}
|
|
|
|
return int64(date.Time.Month()), false, nil
|
|
}
|
|
|
|
// builtinMonth ...
|
|
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_month
|
|
func builtinMonth(args []types.Datum, ctx context.Context) (d types.Datum, err error) {
|
|
d, err = convertToTime(ctx.GetSessionVars().StmtCtx, args[0], mysql.TypeDate)
|
|
if err != nil || d.IsNull() {
|
|
return d, errors.Trace(err)
|
|
}
|
|
|
|
// No need to check type here.
|
|
t := d.GetMysqlTime()
|
|
i := int64(0)
|
|
if t.IsZero() {
|
|
d.SetInt64(i)
|
|
return
|
|
}
|
|
i = int64(t.Time.Month())
|
|
d.SetInt64(i)
|
|
return
|
|
}
|
|
|
|
// 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 context.Context, args []Expression) (builtinFunc, error) {
|
|
if err := c.verifyArgs(args); err != nil {
|
|
return nil, errors.Trace(err)
|
|
}
|
|
bf := newBaseBuiltinFuncWithTp(args, ctx, tpString, tpDatetime)
|
|
bf.tp.Flen = 10
|
|
sig := &builtinMonthNameSig{baseStringBuiltinFunc{bf}}
|
|
return sig.setSelf(sig), nil
|
|
}
|
|
|
|
type builtinMonthNameSig struct {
|
|
baseStringBuiltinFunc
|
|
}
|
|
|
|
func (b *builtinMonthNameSig) evalString(row []types.Datum) (string, bool, error) {
|
|
sc := b.ctx.GetSessionVars().StmtCtx
|
|
arg, isNull, err := b.args[0].EvalTime(row, sc)
|
|
if isNull || err != nil {
|
|
return "", true, errors.Trace(handleInvalidTimeError(b.ctx, err))
|
|
}
|
|
mon := arg.Time.Month()
|
|
if arg.IsZero() || mon < 0 || mon > len(types.MonthNames) {
|
|
return "", true, errors.Trace(handleInvalidTimeError(b.ctx, types.ErrInvalidTimeFormat))
|
|
} else if mon == 0 {
|
|
return "", true, nil
|
|
}
|
|
return types.MonthNames[mon-1], false, nil
|
|
}
|
|
|
|
type dayNameFunctionClass struct {
|
|
baseFunctionClass
|
|
}
|
|
|
|
func (c *dayNameFunctionClass) getFunction(ctx context.Context, args []Expression) (builtinFunc, error) {
|
|
if err := c.verifyArgs(args); err != nil {
|
|
return nil, errors.Trace(err)
|
|
}
|
|
bf := newBaseBuiltinFuncWithTp(args, ctx, tpString, tpDatetime)
|
|
bf.tp.Flen = 10
|
|
sig := &builtinDayNameSig{baseStringBuiltinFunc{bf}}
|
|
return sig.setSelf(sig), nil
|
|
}
|
|
|
|
type builtinDayNameSig struct {
|
|
baseStringBuiltinFunc
|
|
}
|
|
|
|
// 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 []types.Datum) (string, bool, error) {
|
|
arg, isNull, err := b.args[0].EvalTime(row, b.ctx.GetSessionVars().StmtCtx)
|
|
if isNull || err != nil {
|
|
return "", isNull, errors.Trace(err)
|
|
}
|
|
if arg.InvalidZero() {
|
|
return "", true, errors.Trace(handleInvalidTimeError(b.ctx, types.ErrInvalidTimeFormat))
|
|
}
|
|
// Monday is 0, ... Sunday = 6 in MySQL
|
|
// but in go, Sunday is 0, ... Saturday is 6
|
|
// w will do a conversion.
|
|
res := (int64(arg.Time.Weekday()) + 6) % 7
|
|
return types.WeekdayNames[res], false, nil
|
|
}
|
|
|
|
type dayOfMonthFunctionClass struct {
|
|
baseFunctionClass
|
|
}
|
|
|
|
func (c *dayOfMonthFunctionClass) getFunction(ctx context.Context, args []Expression) (builtinFunc, error) {
|
|
if err := c.verifyArgs(args); err != nil {
|
|
return nil, errors.Trace(err)
|
|
}
|
|
bf := newBaseBuiltinFuncWithTp(args, ctx, tpInt, tpDatetime)
|
|
bf.tp.Flen = 2
|
|
sig := &builtinDayOfMonthSig{baseIntBuiltinFunc{bf}}
|
|
return sig.setSelf(sig), nil
|
|
}
|
|
|
|
type builtinDayOfMonthSig struct {
|
|
baseIntBuiltinFunc
|
|
}
|
|
|
|
// 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 []types.Datum) (int64, bool, error) {
|
|
arg, isNull, err := b.args[0].EvalTime(row, b.ctx.GetSessionVars().StmtCtx)
|
|
if isNull || err != nil {
|
|
return 0, true, errors.Trace(handleInvalidTimeError(b.ctx, err))
|
|
}
|
|
if arg.IsZero() {
|
|
return 0, true, errors.Trace(handleInvalidTimeError(b.ctx, types.ErrInvalidTimeFormat))
|
|
}
|
|
return int64(arg.Time.Day()), false, nil
|
|
}
|
|
|
|
type dayOfWeekFunctionClass struct {
|
|
baseFunctionClass
|
|
}
|
|
|
|
func (c *dayOfWeekFunctionClass) getFunction(ctx context.Context, args []Expression) (builtinFunc, error) {
|
|
if err := c.verifyArgs(args); err != nil {
|
|
return nil, errors.Trace(err)
|
|
}
|
|
bf := newBaseBuiltinFuncWithTp(args, ctx, tpInt, tpDatetime)
|
|
bf.tp.Flen = 1
|
|
sig := &builtinDayOfWeekSig{baseIntBuiltinFunc{bf}}
|
|
return sig.setSelf(sig), nil
|
|
}
|
|
|
|
type builtinDayOfWeekSig struct {
|
|
baseIntBuiltinFunc
|
|
}
|
|
|
|
// 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 []types.Datum) (int64, bool, error) {
|
|
sc := b.ctx.GetSessionVars().StmtCtx
|
|
arg, isNull, err := b.args[0].EvalTime(row, sc)
|
|
if isNull || err != nil {
|
|
return 0, true, errors.Trace(handleInvalidTimeError(b.ctx, err))
|
|
}
|
|
if arg.InvalidZero() {
|
|
return 0, true, errors.Trace(handleInvalidTimeError(b.ctx, types.ErrInvalidTimeFormat))
|
|
}
|
|
// 1 is Sunday, 2 is Monday, .... 7 is Saturday
|
|
return int64(arg.Time.Weekday() + 1), false, nil
|
|
}
|
|
|
|
type dayOfYearFunctionClass struct {
|
|
baseFunctionClass
|
|
}
|
|
|
|
func (c *dayOfYearFunctionClass) getFunction(ctx context.Context, args []Expression) (builtinFunc, error) {
|
|
if err := c.verifyArgs(args); err != nil {
|
|
return nil, errors.Trace(err)
|
|
}
|
|
bf := newBaseBuiltinFuncWithTp(args, ctx, tpInt, tpDatetime)
|
|
bf.tp.Flen = 3
|
|
sig := &builtinDayOfYearSig{baseIntBuiltinFunc{bf}}
|
|
return sig.setSelf(sig), nil
|
|
}
|
|
|
|
type builtinDayOfYearSig struct {
|
|
baseIntBuiltinFunc
|
|
}
|
|
|
|
// 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 []types.Datum) (int64, bool, error) {
|
|
arg, isNull, err := b.args[0].EvalTime(row, b.ctx.GetSessionVars().StmtCtx)
|
|
if isNull || err != nil {
|
|
return 0, isNull, errors.Trace(handleInvalidTimeError(b.ctx, err))
|
|
}
|
|
if arg.InvalidZero() {
|
|
return 0, true, errors.Trace(handleInvalidTimeError(b.ctx, types.ErrInvalidTimeFormat))
|
|
}
|
|
|
|
return int64(arg.Time.YearDay()), false, nil
|
|
}
|
|
|
|
type weekFunctionClass struct {
|
|
baseFunctionClass
|
|
}
|
|
|
|
func (c *weekFunctionClass) getFunction(ctx context.Context, args []Expression) (builtinFunc, error) {
|
|
if err := c.verifyArgs(args); err != nil {
|
|
return nil, errors.Trace(err)
|
|
}
|
|
|
|
argTps := []evalTp{tpDatetime}
|
|
if len(args) == 2 {
|
|
argTps = append(argTps, tpInt)
|
|
}
|
|
|
|
bf := newBaseBuiltinFuncWithTp(args, ctx, tpInt, argTps...)
|
|
|
|
bf.tp.Flen, bf.tp.Decimal = 2, 0
|
|
|
|
var sig builtinFunc
|
|
if len(args) == 2 {
|
|
sig = &builtinWeekWithModeSig{baseIntBuiltinFunc{bf}}
|
|
} else {
|
|
sig = &builtinWeekWithoutModeSig{baseIntBuiltinFunc{bf}}
|
|
}
|
|
return sig.setSelf(sig), nil
|
|
}
|
|
|
|
type builtinWeekWithModeSig struct {
|
|
baseIntBuiltinFunc
|
|
}
|
|
|
|
// 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 []types.Datum) (int64, bool, error) {
|
|
sc := b.ctx.GetSessionVars().StmtCtx
|
|
date, isNull, err := b.args[0].EvalTime(row, sc)
|
|
|
|
if isNull || err != nil {
|
|
return 0, true, errors.Trace(handleInvalidTimeError(b.ctx, err))
|
|
}
|
|
|
|
if date.IsZero() {
|
|
return 0, true, errors.Trace(handleInvalidTimeError(b.ctx, types.ErrInvalidTimeFormat))
|
|
}
|
|
|
|
mode, isNull, err := b.args[1].EvalInt(row, sc)
|
|
if isNull || err != nil {
|
|
return 0, isNull, errors.Trace(err)
|
|
}
|
|
|
|
week := date.Time.Week(int(mode))
|
|
return int64(week), false, nil
|
|
}
|
|
|
|
type builtinWeekWithoutModeSig struct {
|
|
baseIntBuiltinFunc
|
|
}
|
|
|
|
// 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 []types.Datum) (int64, bool, error) {
|
|
sc := b.ctx.GetSessionVars().StmtCtx
|
|
date, isNull, err := b.args[0].EvalTime(row, sc)
|
|
|
|
if isNull || err != nil {
|
|
return 0, true, errors.Trace(handleInvalidTimeError(b.ctx, err))
|
|
}
|
|
|
|
if date.IsZero() {
|
|
return 0, true, errors.Trace(handleInvalidTimeError(b.ctx, types.ErrInvalidTimeFormat))
|
|
}
|
|
|
|
week := date.Time.Week(0)
|
|
return int64(week), false, nil
|
|
}
|
|
|
|
type weekDayFunctionClass struct {
|
|
baseFunctionClass
|
|
}
|
|
|
|
func (c *weekDayFunctionClass) getFunction(ctx context.Context, args []Expression) (builtinFunc, error) {
|
|
if err := c.verifyArgs(args); err != nil {
|
|
return nil, errors.Trace(err)
|
|
}
|
|
|
|
bf := newBaseBuiltinFuncWithTp(args, ctx, tpInt, tpDatetime)
|
|
bf.tp.Flen = 1
|
|
|
|
sig := &builtinWeekDaySig{baseIntBuiltinFunc{bf}}
|
|
return sig.setSelf(sig), nil
|
|
}
|
|
|
|
type builtinWeekDaySig struct {
|
|
baseIntBuiltinFunc
|
|
}
|
|
|
|
// evalInt evals WEEKDAY(date).
|
|
func (b *builtinWeekDaySig) evalInt(row []types.Datum) (int64, bool, error) {
|
|
sc := b.ctx.GetSessionVars().StmtCtx
|
|
|
|
date, isNull, err := b.args[0].EvalTime(row, sc)
|
|
if isNull || err != nil {
|
|
return 0, true, errors.Trace(handleInvalidTimeError(b.ctx, err))
|
|
}
|
|
|
|
if date.IsZero() {
|
|
return 0, true, errors.Trace(handleInvalidTimeError(b.ctx, types.ErrInvalidTimeFormat))
|
|
}
|
|
|
|
return int64(date.Time.Weekday()+6) % 7, false, nil
|
|
}
|
|
|
|
type weekOfYearFunctionClass struct {
|
|
baseFunctionClass
|
|
}
|
|
|
|
func (c *weekOfYearFunctionClass) getFunction(ctx context.Context, args []Expression) (builtinFunc, error) {
|
|
if err := c.verifyArgs(args); err != nil {
|
|
return nil, errors.Trace(err)
|
|
}
|
|
bf := newBaseBuiltinFuncWithTp(args, ctx, tpInt, tpDatetime)
|
|
bf.tp.Flen, bf.tp.Decimal = 2, 0
|
|
sig := &builtinWeekOfYearSig{baseIntBuiltinFunc{bf}}
|
|
return sig.setSelf(sig), nil
|
|
}
|
|
|
|
type builtinWeekOfYearSig struct {
|
|
baseIntBuiltinFunc
|
|
}
|
|
|
|
// 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 []types.Datum) (int64, bool, error) {
|
|
sc := b.ctx.GetSessionVars().StmtCtx
|
|
date, isNull, err := b.args[0].EvalTime(row, sc)
|
|
|
|
if isNull || err != nil {
|
|
return 0, true, errors.Trace(handleInvalidTimeError(b.ctx, err))
|
|
}
|
|
|
|
if date.IsZero() {
|
|
return 0, true, errors.Trace(handleInvalidTimeError(b.ctx, types.ErrInvalidTimeFormat))
|
|
}
|
|
|
|
week := date.Time.Week(3)
|
|
return int64(week), false, nil
|
|
}
|
|
|
|
type yearFunctionClass struct {
|
|
baseFunctionClass
|
|
}
|
|
|
|
func (c *yearFunctionClass) getFunction(ctx context.Context, args []Expression) (builtinFunc, error) {
|
|
if err := c.verifyArgs(args); err != nil {
|
|
return nil, errors.Trace(err)
|
|
}
|
|
bf := newBaseBuiltinFuncWithTp(args, ctx, tpInt, tpDatetime)
|
|
bf.tp.Flen, bf.tp.Decimal = 4, 0
|
|
sig := &builtinYearSig{baseIntBuiltinFunc{bf}}
|
|
return sig.setSelf(sig), nil
|
|
}
|
|
|
|
type builtinYearSig struct {
|
|
baseIntBuiltinFunc
|
|
}
|
|
|
|
// 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 []types.Datum) (int64, bool, error) {
|
|
sc := b.getCtx().GetSessionVars().StmtCtx
|
|
date, isNull, err := b.args[0].EvalTime(row, sc)
|
|
|
|
if isNull || err != nil {
|
|
return 0, true, errors.Trace(handleInvalidTimeError(b.ctx, err))
|
|
}
|
|
|
|
if date.IsZero() {
|
|
return 0, true, errors.Trace(handleInvalidTimeError(b.ctx, types.ErrInvalidTimeFormat))
|
|
}
|
|
|
|
return int64(date.Time.Year()), false, nil
|
|
}
|
|
|
|
type yearWeekFunctionClass struct {
|
|
baseFunctionClass
|
|
}
|
|
|
|
func (c *yearWeekFunctionClass) getFunction(ctx context.Context, args []Expression) (builtinFunc, error) {
|
|
if err := c.verifyArgs(args); err != nil {
|
|
return nil, errors.Trace(err)
|
|
}
|
|
argTps := []evalTp{tpDatetime}
|
|
if len(args) == 2 {
|
|
argTps = append(argTps, tpInt)
|
|
}
|
|
|
|
bf := newBaseBuiltinFuncWithTp(args, ctx, tpInt, argTps...)
|
|
|
|
bf.tp.Flen, bf.tp.Decimal = 6, 0
|
|
|
|
var sig builtinFunc
|
|
if len(args) == 2 {
|
|
sig = &builtinYearWeekWithModeSig{baseIntBuiltinFunc{bf}}
|
|
} else {
|
|
sig = &builtinYearWeekWithoutModeSig{baseIntBuiltinFunc{bf}}
|
|
}
|
|
return sig.setSelf(sig), nil
|
|
}
|
|
|
|
type builtinYearWeekWithModeSig struct {
|
|
baseIntBuiltinFunc
|
|
}
|
|
|
|
// 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 []types.Datum) (int64, bool, error) {
|
|
sc := b.ctx.GetSessionVars().StmtCtx
|
|
|
|
date, isNull, err := b.args[0].EvalTime(row, sc)
|
|
if isNull || err != nil {
|
|
return 0, isNull, errors.Trace(handleInvalidTimeError(b.ctx, err))
|
|
}
|
|
if date.IsZero() {
|
|
return 0, true, errors.Trace(handleInvalidTimeError(b.ctx, types.ErrInvalidTimeFormat))
|
|
}
|
|
|
|
mode, isNull, err := b.args[1].EvalInt(row, sc)
|
|
if err != nil {
|
|
return 0, true, errors.Trace(err)
|
|
}
|
|
if isNull {
|
|
mode = 0
|
|
}
|
|
|
|
year, week := date.Time.YearWeek(int(mode))
|
|
result := int64(week + year*100)
|
|
if result < 0 {
|
|
return int64(math.MaxUint32), false, nil
|
|
}
|
|
return result, false, nil
|
|
}
|
|
|
|
type builtinYearWeekWithoutModeSig struct {
|
|
baseIntBuiltinFunc
|
|
}
|
|
|
|
// 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 []types.Datum) (int64, bool, error) {
|
|
sc := b.ctx.GetSessionVars().StmtCtx
|
|
|
|
date, isNull, err := b.args[0].EvalTime(row, sc)
|
|
if isNull || err != nil {
|
|
return 0, true, errors.Trace(handleInvalidTimeError(b.ctx, err))
|
|
}
|
|
|
|
if date.InvalidZero() {
|
|
return 0, true, errors.Trace(handleInvalidTimeError(b.ctx, types.ErrInvalidTimeFormat))
|
|
}
|
|
|
|
year, week := date.Time.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 context.Context, args []Expression) (sig builtinFunc, err error) {
|
|
if err = c.verifyArgs(args); err != nil {
|
|
return nil, errors.Trace(err)
|
|
}
|
|
|
|
retTp, argTps := tpDatetime, make([]evalTp, 0, len(args))
|
|
argTps = append(argTps, tpDecimal)
|
|
if len(args) == 2 {
|
|
retTp = tpString
|
|
argTps = append(argTps, tpString)
|
|
}
|
|
|
|
_, isArg0Con := args[0].(*Constant)
|
|
isArg0Str := fieldTp2EvalTp(args[0].GetType()) == tpString
|
|
bf := newBaseBuiltinFuncWithTp(args, ctx, retTp, argTps...)
|
|
if len(args) == 1 {
|
|
if isArg0Str {
|
|
bf.tp.Decimal = types.MaxFsp
|
|
} else if isArg0Con {
|
|
arg0, _, err1 := args[0].EvalDecimal(nil, ctx.GetSessionVars().StmtCtx)
|
|
if err1 != nil {
|
|
return sig, errors.Trace(err1)
|
|
}
|
|
fsp := int(arg0.GetDigitsFrac())
|
|
if fsp > types.MaxFsp {
|
|
fsp = types.MaxFsp
|
|
}
|
|
bf.tp.Decimal = fsp
|
|
}
|
|
sig = &builtinFromUnixTime1ArgSig{baseTimeBuiltinFunc{bf}}
|
|
} else {
|
|
bf.tp.Flen = args[1].GetType().Flen
|
|
sig = &builtinFromUnixTime2ArgSig{baseStringBuiltinFunc{bf}}
|
|
}
|
|
return sig.setSelf(sig), nil
|
|
}
|
|
|
|
func evalFromUnixTime(ctx context.Context, fsp int, row []types.Datum, arg Expression) (res types.Time, isNull bool, err error) {
|
|
sc := ctx.GetSessionVars().StmtCtx
|
|
unixTimeStamp, isNull, err := arg.EvalDecimal(row, sc)
|
|
if err != nil || isNull {
|
|
return res, isNull, errors.Trace(err)
|
|
}
|
|
// 0 <= unixTimeStamp <= INT32_MAX
|
|
if unixTimeStamp.IsNegative() {
|
|
return res, true, nil
|
|
}
|
|
integralPart, err := unixTimeStamp.ToInt()
|
|
if err != nil && !terror.ErrorEqual(err, types.ErrTruncated) {
|
|
return res, true, errors.Trace(err)
|
|
}
|
|
if integralPart > int64(math.MaxInt32) {
|
|
return
|
|
}
|
|
// 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, errors.Trace(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, errors.Trace(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, errors.Trace(err)
|
|
}
|
|
fracDigitsNumber := int(unixTimeStamp.GetDigitsFrac())
|
|
if fsp < 0 {
|
|
fsp = types.MaxFsp
|
|
}
|
|
fsp = mathutil.Max(fracDigitsNumber, fsp)
|
|
if fsp > types.MaxFsp {
|
|
fsp = types.MaxFsp
|
|
}
|
|
|
|
tmp := time.Unix(integralPart, fractionalPart).In(sc.TimeZone)
|
|
t, err := convertTimeToMysqlTime(tmp, fsp)
|
|
if err != nil {
|
|
return res, true, errors.Trace(err)
|
|
}
|
|
return t, false, nil
|
|
}
|
|
|
|
type builtinFromUnixTime1ArgSig struct {
|
|
baseTimeBuiltinFunc
|
|
}
|
|
|
|
// 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 []types.Datum) (res types.Time, isNull bool, err error) {
|
|
return evalFromUnixTime(b.ctx, b.tp.Decimal, row, b.args[0])
|
|
}
|
|
|
|
type builtinFromUnixTime2ArgSig struct {
|
|
baseStringBuiltinFunc
|
|
}
|
|
|
|
// 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 []types.Datum) (res string, isNull bool, err error) {
|
|
sc := b.ctx.GetSessionVars().StmtCtx
|
|
format, isNull, err := b.args[1].EvalString(row, sc)
|
|
if isNull || err != nil {
|
|
return "", true, errors.Trace(err)
|
|
}
|
|
t, isNull, err := evalFromUnixTime(b.ctx, b.tp.Decimal, row, b.args[0])
|
|
if isNull || err != nil {
|
|
return "", isNull, errors.Trace(err)
|
|
}
|
|
res, err = t.DateFormat(format)
|
|
return res, err != nil, errors.Trace(err)
|
|
}
|
|
|
|
type getFormatFunctionClass struct {
|
|
baseFunctionClass
|
|
}
|
|
|
|
func (c *getFormatFunctionClass) getFunction(ctx context.Context, args []Expression) (builtinFunc, error) {
|
|
if err := c.verifyArgs(args); err != nil {
|
|
return nil, errors.Trace(err)
|
|
}
|
|
bf := newBaseBuiltinFuncWithTp(args, ctx, tpString, tpString, tpString)
|
|
bf.tp.Flen = 17
|
|
sig := &builtinGetFormatSig{baseStringBuiltinFunc{bf}}
|
|
return sig.setSelf(sig), nil
|
|
}
|
|
|
|
type builtinGetFormatSig struct {
|
|
baseStringBuiltinFunc
|
|
}
|
|
|
|
// 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 []types.Datum) (string, bool, error) {
|
|
sc := b.ctx.GetSessionVars().StmtCtx
|
|
t, isNull, err := b.args[0].EvalString(row, sc)
|
|
if isNull || err != nil {
|
|
return "", isNull, errors.Trace(err)
|
|
}
|
|
l, isNull, err := b.args[1].EvalString(row, sc)
|
|
if isNull || err != nil {
|
|
return "", isNull, errors.Trace(err)
|
|
}
|
|
|
|
var res string
|
|
switch t {
|
|
case dateFormat:
|
|
switch l {
|
|
case usaLocation:
|
|
res = "%m.%d.%Y"
|
|
case jisLocation:
|
|
res = "%Y-%m-%d"
|
|
case isoLocation:
|
|
res = "%Y-%m-%d"
|
|
case eurLocation:
|
|
res = "%d.%m.%Y"
|
|
case internalLocation:
|
|
res = "%Y%m%d"
|
|
}
|
|
case datetimeFormat, timestampFormat:
|
|
switch l {
|
|
case usaLocation:
|
|
res = "%Y-%m-%d %H.%i.%s"
|
|
case jisLocation:
|
|
res = "%Y-%m-%d %H:%i:%s"
|
|
case isoLocation:
|
|
res = "%Y-%m-%d %H:%i:%s"
|
|
case eurLocation:
|
|
res = "%Y-%m-%d %H.%i.%s"
|
|
case internalLocation:
|
|
res = "%Y%m%d%H%i%s"
|
|
}
|
|
case timeFormat:
|
|
switch l {
|
|
case usaLocation:
|
|
res = "%h:%i:%s %p"
|
|
case jisLocation:
|
|
res = "%H:%i:%s"
|
|
case isoLocation:
|
|
res = "%H:%i:%s"
|
|
case eurLocation:
|
|
res = "%H.%i.%s"
|
|
case internalLocation:
|
|
res = "%H%i%s"
|
|
}
|
|
}
|
|
|
|
return res, false, nil
|
|
}
|
|
|
|
type strToDateFunctionClass struct {
|
|
baseFunctionClass
|
|
}
|
|
|
|
func (c *strToDateFunctionClass) getRetTp(arg Expression, ctx context.Context) (tp byte, fsp int) {
|
|
tp = mysql.TypeDatetime
|
|
if _, ok := arg.(*Constant); !ok {
|
|
return tp, types.MaxFsp
|
|
}
|
|
strArg := WrapWithCastAsString(arg, ctx)
|
|
format, isNull, err := strArg.EvalString(nil, ctx.GetSessionVars().StmtCtx)
|
|
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
|
|
}
|
|
|
|
// See https://dev.mysql.com/doc/refman/5.5/en/date-and-time-functions.html#function_str-to-date
|
|
func (c *strToDateFunctionClass) getFunction(ctx context.Context, args []Expression) (sig builtinFunc, err error) {
|
|
if err := c.verifyArgs(args); err != nil {
|
|
return nil, errors.Trace(err)
|
|
}
|
|
retTp, fsp := c.getRetTp(args[1], ctx)
|
|
switch retTp {
|
|
case mysql.TypeDate:
|
|
bf := newBaseBuiltinFuncWithTp(args, ctx, tpDatetime, tpString, tpString)
|
|
bf.tp.Tp, bf.tp.Flen, bf.tp.Decimal = mysql.TypeDate, mysql.MaxDateWidth, types.MinFsp
|
|
sig = &builtinStrToDateDateSig{baseTimeBuiltinFunc{bf}}
|
|
case mysql.TypeDatetime:
|
|
bf := newBaseBuiltinFuncWithTp(args, ctx, tpDatetime, tpString, tpString)
|
|
if fsp == types.MinFsp {
|
|
bf.tp.Flen, bf.tp.Decimal = mysql.MaxDatetimeWidthNoFsp, types.MinFsp
|
|
} else {
|
|
bf.tp.Flen, bf.tp.Decimal = mysql.MaxDatetimeWidthWithFsp, types.MaxFsp
|
|
}
|
|
sig = &builtinStrToDateDatetimeSig{baseTimeBuiltinFunc{bf}}
|
|
case mysql.TypeDuration:
|
|
bf := newBaseBuiltinFuncWithTp(args, ctx, tpDuration, tpString, tpString)
|
|
if fsp == types.MinFsp {
|
|
bf.tp.Flen, bf.tp.Decimal = mysql.MaxDurationWidthNoFsp, types.MinFsp
|
|
} else {
|
|
bf.tp.Flen, bf.tp.Decimal = mysql.MaxDurationWidthWithFsp, types.MaxFsp
|
|
}
|
|
sig = &builtinStrToDateDurationSig{baseDurationBuiltinFunc{bf}}
|
|
}
|
|
return sig.setSelf(sig), nil
|
|
}
|
|
|
|
type builtinStrToDateDateSig struct {
|
|
baseTimeBuiltinFunc
|
|
}
|
|
|
|
func (b *builtinStrToDateDateSig) evalTime(row []types.Datum) (types.Time, bool, error) {
|
|
sc := b.ctx.GetSessionVars().StmtCtx
|
|
date, isNull, err := b.args[0].EvalString(row, sc)
|
|
if isNull || err != nil {
|
|
return types.Time{}, isNull, errors.Trace(err)
|
|
}
|
|
format, isNull, err := b.args[1].EvalString(row, sc)
|
|
if isNull || err != nil {
|
|
return types.Time{}, isNull, errors.Trace(err)
|
|
}
|
|
var t types.Time
|
|
succ := t.StrToDate(date, format)
|
|
if !succ {
|
|
return types.Time{}, true, handleInvalidTimeError(b.ctx, types.ErrInvalidTimeFormat)
|
|
}
|
|
t.Type, t.Fsp = mysql.TypeDate, types.MinFsp
|
|
return t, false, nil
|
|
}
|
|
|
|
type builtinStrToDateDatetimeSig struct {
|
|
baseTimeBuiltinFunc
|
|
}
|
|
|
|
func (b *builtinStrToDateDatetimeSig) evalTime(row []types.Datum) (types.Time, bool, error) {
|
|
sc := b.ctx.GetSessionVars().StmtCtx
|
|
date, isNull, err := b.args[0].EvalString(row, sc)
|
|
if isNull || err != nil {
|
|
return types.Time{}, isNull, errors.Trace(err)
|
|
}
|
|
format, isNull, err := b.args[1].EvalString(row, sc)
|
|
if isNull || err != nil {
|
|
return types.Time{}, isNull, errors.Trace(err)
|
|
}
|
|
var t types.Time
|
|
succ := t.StrToDate(date, format)
|
|
if !succ {
|
|
return types.Time{}, true, handleInvalidTimeError(b.ctx, types.ErrInvalidTimeFormat)
|
|
}
|
|
t.Type, t.Fsp = mysql.TypeDatetime, b.tp.Decimal
|
|
return t, false, nil
|
|
}
|
|
|
|
type builtinStrToDateDurationSig struct {
|
|
baseDurationBuiltinFunc
|
|
}
|
|
|
|
// 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 []types.Datum) (types.Duration, bool, error) {
|
|
sc := b.ctx.GetSessionVars().StmtCtx
|
|
date, isNull, err := b.args[0].EvalString(row, sc)
|
|
if isNull || err != nil {
|
|
return types.Duration{}, isNull, errors.Trace(err)
|
|
}
|
|
format, isNull, err := b.args[1].EvalString(row, sc)
|
|
if isNull || err != nil {
|
|
return types.Duration{}, isNull, errors.Trace(err)
|
|
}
|
|
var t types.Time
|
|
succ := t.StrToDate(date, format)
|
|
if !succ {
|
|
return types.Duration{}, true, handleInvalidTimeError(b.ctx, types.ErrInvalidTimeFormat)
|
|
}
|
|
t.Fsp = b.tp.Decimal
|
|
dur, err := t.ConvertToDuration()
|
|
return dur, false, errors.Trace(err)
|
|
}
|
|
|
|
type sysDateFunctionClass struct {
|
|
baseFunctionClass
|
|
}
|
|
|
|
func (c *sysDateFunctionClass) getFunction(ctx context.Context, args []Expression) (builtinFunc, error) {
|
|
if err := c.verifyArgs(args); err != nil {
|
|
return nil, errors.Trace(err)
|
|
}
|
|
argTps := []evalTp{}
|
|
if len(args) == 1 {
|
|
argTps = append(argTps, tpInt)
|
|
}
|
|
bf := newBaseBuiltinFuncWithTp(args, ctx, tpDatetime, argTps...)
|
|
bf.tp.Flen, bf.tp.Decimal = 19, 0
|
|
bf.foldable = false
|
|
|
|
var sig builtinFunc
|
|
if len(args) == 1 {
|
|
sig = &builtinSysDateWithFspSig{baseTimeBuiltinFunc{bf}}
|
|
} else {
|
|
sig = &builtinSysDateWithoutFspSig{baseTimeBuiltinFunc{bf}}
|
|
}
|
|
return sig.setSelf(sig), nil
|
|
}
|
|
|
|
type builtinSysDateWithFspSig struct {
|
|
baseTimeBuiltinFunc
|
|
}
|
|
|
|
// 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 []types.Datum) (d types.Time, isNull bool, err error) {
|
|
sc := b.ctx.GetSessionVars().StmtCtx
|
|
|
|
fsp, isNull, err := b.args[0].EvalInt(row, sc)
|
|
if isNull || err != nil {
|
|
return types.Time{}, isNull, errors.Trace(err)
|
|
}
|
|
|
|
result, err := convertTimeToMysqlTime(time.Now(), int(fsp))
|
|
if err != nil {
|
|
return types.Time{}, true, errors.Trace(err)
|
|
}
|
|
return result, false, nil
|
|
}
|
|
|
|
type builtinSysDateWithoutFspSig struct {
|
|
baseTimeBuiltinFunc
|
|
}
|
|
|
|
// 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 []types.Datum) (d types.Time, isNull bool, err error) {
|
|
result, err := convertTimeToMysqlTime(time.Now(), 0)
|
|
if err != nil {
|
|
return types.Time{}, true, errors.Trace(err)
|
|
}
|
|
return result, false, nil
|
|
}
|
|
|
|
type currentDateFunctionClass struct {
|
|
baseFunctionClass
|
|
}
|
|
|
|
func (c *currentDateFunctionClass) getFunction(ctx context.Context, args []Expression) (builtinFunc, error) {
|
|
if err := c.verifyArgs(args); err != nil {
|
|
return nil, errors.Trace(err)
|
|
}
|
|
bf := newBaseBuiltinFuncWithTp(args, ctx, tpDatetime)
|
|
bf.tp.Flen, bf.tp.Decimal = 10, 0
|
|
bf.foldable = false
|
|
sig := &builtinCurrentDateSig{baseTimeBuiltinFunc{bf}}
|
|
return sig.setSelf(sig), nil
|
|
}
|
|
|
|
type builtinCurrentDateSig struct {
|
|
baseTimeBuiltinFunc
|
|
}
|
|
|
|
// eval evals CURDATE().
|
|
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_curdate
|
|
func (b *builtinCurrentDateSig) evalTime(row []types.Datum) (d types.Time, isNull bool, err error) {
|
|
year, month, day := time.Now().Date()
|
|
result := types.Time{
|
|
Time: types.FromDate(year, int(month), day, 0, 0, 0, 0),
|
|
Type: mysql.TypeDate,
|
|
Fsp: 0}
|
|
return result, false, nil
|
|
}
|
|
|
|
type currentTimeFunctionClass struct {
|
|
baseFunctionClass
|
|
}
|
|
|
|
func (c *currentTimeFunctionClass) getFunction(ctx context.Context, args []Expression) (sig builtinFunc, err error) {
|
|
if err = c.verifyArgs(args); err != nil {
|
|
return nil, errors.Trace(err)
|
|
}
|
|
|
|
if len(args) == 0 {
|
|
bf := newBaseBuiltinFuncWithTp(args, ctx, tpDuration)
|
|
bf.tp.Flen, bf.tp.Decimal = mysql.MaxDurationWidthNoFsp, types.MinFsp
|
|
sig = &builtinCurrentTime0ArgSig{baseDurationBuiltinFunc{bf}}
|
|
return sig.setSelf(sig), nil
|
|
}
|
|
// args[0] must be a constant which should not be null.
|
|
_, ok := args[0].(*Constant)
|
|
fsp := int64(types.MaxFsp)
|
|
if ok {
|
|
fsp, _, err = args[0].EvalInt(nil, ctx.GetSessionVars().StmtCtx)
|
|
if err != nil {
|
|
return nil, errors.Trace(err)
|
|
}
|
|
if fsp > int64(types.MaxFsp) {
|
|
return nil, errors.Errorf("Too-big precision %v specified for 'curtime'. Maximum is %v.", fsp, types.MaxFsp)
|
|
} else if fsp < int64(types.MinFsp) {
|
|
return nil, errors.Errorf("Invalid negative %d specified, must in [0, 6].", fsp)
|
|
}
|
|
}
|
|
bf := newBaseBuiltinFuncWithTp(args, ctx, tpDuration, tpInt)
|
|
bf.tp.Flen, bf.tp.Decimal = mysql.MaxDurationWidthWithFsp, int(fsp)
|
|
sig = &builtinCurrentTime1ArgSig{baseDurationBuiltinFunc{bf}}
|
|
return sig.setSelf(sig), nil
|
|
}
|
|
|
|
type builtinCurrentTime0ArgSig struct {
|
|
baseDurationBuiltinFunc
|
|
}
|
|
|
|
func (b *builtinCurrentTime0ArgSig) evalDuration(row []types.Datum) (types.Duration, bool, error) {
|
|
res, err := types.ParseDuration(time.Now().Format(types.TimeFormat), types.MinFsp)
|
|
if err != nil {
|
|
return types.Duration{}, true, errors.Trace(err)
|
|
}
|
|
return res, false, nil
|
|
}
|
|
|
|
type builtinCurrentTime1ArgSig struct {
|
|
baseDurationBuiltinFunc
|
|
}
|
|
|
|
func (b *builtinCurrentTime1ArgSig) evalDuration(row []types.Datum) (types.Duration, bool, error) {
|
|
fsp, _, err := b.args[0].EvalInt(row, b.ctx.GetSessionVars().StmtCtx)
|
|
if err != nil {
|
|
return types.Duration{}, true, errors.Trace(err)
|
|
}
|
|
res, err := types.ParseDuration(time.Now().Format(types.TimeFSPFormat), int(fsp))
|
|
if err != nil {
|
|
return types.Duration{}, true, errors.Trace(err)
|
|
}
|
|
return res, false, nil
|
|
}
|
|
|
|
type timeFunctionClass struct {
|
|
baseFunctionClass
|
|
}
|
|
|
|
func (c *timeFunctionClass) getFunction(ctx context.Context, args []Expression) (builtinFunc, error) {
|
|
if err := c.verifyArgs(args); err != nil {
|
|
return nil, errors.Trace(err)
|
|
}
|
|
bf := newBaseBuiltinFuncWithTp(args, ctx, tpDuration, tpString)
|
|
sig := &builtinTimeSig{baseDurationBuiltinFunc{bf}}
|
|
return sig.setSelf(sig), nil
|
|
}
|
|
|
|
type builtinTimeSig struct {
|
|
baseDurationBuiltinFunc
|
|
}
|
|
|
|
// 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 []types.Datum) (res types.Duration, isNull bool, err error) {
|
|
sc := b.getCtx().GetSessionVars().StmtCtx
|
|
expr, isNull, err := b.args[0].EvalString(row, sc)
|
|
if isNull || err != nil {
|
|
return res, isNull, errors.Trace(err)
|
|
}
|
|
|
|
fsp := 0
|
|
if idx := strings.Index(expr, "."); idx != -1 {
|
|
fsp = len(expr) - idx - 1
|
|
}
|
|
|
|
if fsp, err = types.CheckFsp(fsp); err != nil {
|
|
return res, isNull, errors.Trace(err)
|
|
}
|
|
|
|
res, err = types.ParseDuration(expr, fsp)
|
|
if types.ErrTruncatedWrongVal.Equal(err) {
|
|
err = sc.HandleTruncate(err)
|
|
}
|
|
return res, isNull, errors.Trace(err)
|
|
}
|
|
|
|
type utcDateFunctionClass struct {
|
|
baseFunctionClass
|
|
}
|
|
|
|
func (c *utcDateFunctionClass) getFunction(ctx context.Context, args []Expression) (builtinFunc, error) {
|
|
if err := c.verifyArgs(args); err != nil {
|
|
return nil, errors.Trace(err)
|
|
}
|
|
bf := newBaseBuiltinFuncWithTp(args, ctx, tpDatetime)
|
|
bf.tp.Flen, bf.tp.Decimal = 10, 0
|
|
bf.foldable = false
|
|
sig := &builtinUTCDateSig{baseTimeBuiltinFunc{bf}}
|
|
return sig.setSelf(sig), nil
|
|
}
|
|
|
|
type builtinUTCDateSig struct {
|
|
baseTimeBuiltinFunc
|
|
}
|
|
|
|
// 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 []types.Datum) (types.Time, bool, error) {
|
|
year, month, day := time.Now().UTC().Date()
|
|
result := types.Time{
|
|
Time: types.FromGoTime(time.Date(year, month, day, 0, 0, 0, 0, time.UTC)),
|
|
Type: mysql.TypeDate,
|
|
Fsp: types.UnspecifiedFsp}
|
|
return result, false, nil
|
|
}
|
|
|
|
type utcTimestampFunctionClass struct {
|
|
baseFunctionClass
|
|
}
|
|
|
|
func getFlenAndDecimal4UTCTimestampAndNow(sc *variable.StatementContext, arg Expression) (flen, decimal int) {
|
|
if constant, ok := arg.(*Constant); ok {
|
|
fsp, isNull, err := constant.EvalInt(nil, sc)
|
|
if isNull || err != nil || fsp > int64(types.MaxFsp) {
|
|
decimal = types.MaxFsp
|
|
} else if fsp < int64(types.MinFsp) {
|
|
decimal = types.MinFsp
|
|
} else {
|
|
decimal = int(fsp)
|
|
}
|
|
}
|
|
if decimal > 0 {
|
|
flen = 19 + 1 + decimal
|
|
} else {
|
|
flen = 19
|
|
}
|
|
return flen, decimal
|
|
}
|
|
|
|
func (c *utcTimestampFunctionClass) getFunction(ctx context.Context, args []Expression) (builtinFunc, error) {
|
|
if err := c.verifyArgs(args); err != nil {
|
|
return nil, errors.Trace(err)
|
|
}
|
|
argTps := make([]evalTp, 0, 1)
|
|
if len(args) == 1 {
|
|
argTps = append(argTps, tpInt)
|
|
}
|
|
bf := newBaseBuiltinFuncWithTp(args, ctx, tpDatetime, argTps...)
|
|
|
|
if len(args) == 1 {
|
|
bf.tp.Flen, bf.tp.Decimal = getFlenAndDecimal4UTCTimestampAndNow(bf.ctx.GetSessionVars().StmtCtx, args[0])
|
|
} else {
|
|
bf.tp.Flen, bf.tp.Decimal = 19, 0
|
|
}
|
|
bf.foldable = false
|
|
|
|
var sig builtinFunc
|
|
if len(args) == 1 {
|
|
sig = &builtinUTCTimestampWithArgSig{baseTimeBuiltinFunc{bf}}
|
|
} else {
|
|
sig = &builtinUTCTimestampWithoutArgSig{baseTimeBuiltinFunc{bf}}
|
|
}
|
|
return sig.setSelf(sig), nil
|
|
}
|
|
|
|
func evalUTCTimestampWithFsp(fsp int) (types.Time, bool, error) {
|
|
result, err := convertTimeToMysqlTime(time.Now().UTC(), fsp)
|
|
if err != nil {
|
|
return types.Time{}, true, errors.Trace(err)
|
|
}
|
|
return result, false, nil
|
|
}
|
|
|
|
type builtinUTCTimestampWithArgSig struct {
|
|
baseTimeBuiltinFunc
|
|
}
|
|
|
|
// 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 []types.Datum) (types.Time, bool, error) {
|
|
num, isNull, err := b.args[0].EvalInt(row, b.ctx.GetSessionVars().StmtCtx)
|
|
if err != nil {
|
|
return types.Time{}, true, errors.Trace(err)
|
|
}
|
|
|
|
if !isNull && num > int64(types.MaxFsp) {
|
|
return types.Time{}, 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.Time{}, true, errors.Errorf("Invalid negative %d specified, must in [0, 6].", num)
|
|
}
|
|
|
|
result, isNull, err := evalUTCTimestampWithFsp(int(num))
|
|
return result, isNull, errors.Trace(err)
|
|
}
|
|
|
|
type builtinUTCTimestampWithoutArgSig struct {
|
|
baseTimeBuiltinFunc
|
|
}
|
|
|
|
// 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 []types.Datum) (types.Time, bool, error) {
|
|
result, isNull, err := evalUTCTimestampWithFsp(0)
|
|
return result, isNull, errors.Trace(err)
|
|
}
|
|
|
|
type nowFunctionClass struct {
|
|
baseFunctionClass
|
|
}
|
|
|
|
func (c *nowFunctionClass) getFunction(ctx context.Context, args []Expression) (builtinFunc, error) {
|
|
if err := c.verifyArgs(args); err != nil {
|
|
return nil, errors.Trace(err)
|
|
}
|
|
argTps := make([]evalTp, 0, 1)
|
|
if len(args) == 1 {
|
|
argTps = append(argTps, tpInt)
|
|
}
|
|
bf := newBaseBuiltinFuncWithTp(args, ctx, tpDatetime, argTps...)
|
|
|
|
if len(args) == 1 {
|
|
bf.tp.Flen, bf.tp.Decimal = getFlenAndDecimal4UTCTimestampAndNow(bf.ctx.GetSessionVars().StmtCtx, args[0])
|
|
} else {
|
|
bf.tp.Flen, bf.tp.Decimal = 19, 0
|
|
}
|
|
|
|
var sig builtinFunc
|
|
if len(args) == 1 {
|
|
sig = &builtinNowWithArgSig{baseTimeBuiltinFunc{bf}}
|
|
} else {
|
|
sig = &builtinNowWithoutArgSig{baseTimeBuiltinFunc{bf}}
|
|
}
|
|
return sig.setSelf(sig), nil
|
|
}
|
|
|
|
func evalNowWithFsp(ctx context.Context, fsp int) (types.Time, bool, error) {
|
|
sysTs, err := getSystemTimestamp(ctx)
|
|
if err != nil {
|
|
return types.Time{}, true, errors.Trace(err)
|
|
}
|
|
|
|
result, err := convertTimeToMysqlTime(sysTs, fsp)
|
|
if err != nil {
|
|
return types.Time{}, true, errors.Trace(err)
|
|
}
|
|
|
|
err = result.ConvertTimeZone(time.Local, ctx.GetSessionVars().GetTimeZone())
|
|
if err != nil {
|
|
return types.Time{}, true, errors.Trace(err)
|
|
}
|
|
|
|
return result, false, nil
|
|
}
|
|
|
|
type builtinNowWithArgSig struct {
|
|
baseTimeBuiltinFunc
|
|
}
|
|
|
|
// 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 []types.Datum) (types.Time, bool, error) {
|
|
fsp, isNull, err := b.args[0].EvalInt(row, b.ctx.GetSessionVars().StmtCtx)
|
|
|
|
if err != nil {
|
|
return types.Time{}, true, errors.Trace(err)
|
|
}
|
|
|
|
if isNull {
|
|
fsp = 0
|
|
} else if fsp > int64(types.MaxFsp) {
|
|
return types.Time{}, true, errors.Errorf("Too-big precision %v specified for 'now'. Maximum is %v.", fsp, types.MaxFsp)
|
|
} else if fsp < int64(types.MinFsp) {
|
|
return types.Time{}, true, errors.Errorf("Invalid negative %d specified, must in [0, 6].", fsp)
|
|
}
|
|
|
|
result, isNull, err := evalNowWithFsp(b.ctx, int(fsp))
|
|
return result, isNull, errors.Trace(err)
|
|
}
|
|
|
|
type builtinNowWithoutArgSig struct {
|
|
baseTimeBuiltinFunc
|
|
}
|
|
|
|
// 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 []types.Datum) (types.Time, bool, error) {
|
|
result, isNull, err := evalNowWithFsp(b.ctx, 0)
|
|
return result, isNull, errors.Trace(err)
|
|
}
|
|
|
|
type extractFunctionClass struct {
|
|
baseFunctionClass
|
|
}
|
|
|
|
func (c *extractFunctionClass) getFunction(ctx context.Context, args []Expression) (sig builtinFunc, err error) {
|
|
if err = c.verifyArgs(args); err != nil {
|
|
return nil, errors.Trace(err)
|
|
}
|
|
|
|
datetimeUnits := map[string]struct{}{
|
|
"DAY": {},
|
|
"WEEK": {},
|
|
"MONTH": {},
|
|
"QUARTER": {},
|
|
"YEAR": {},
|
|
"DAY_MICROSECOND": {},
|
|
"DAY_SECOND": {},
|
|
"DAY_MINUTE": {},
|
|
"DAY_HOUR": {},
|
|
"YEAR_MONTH": {},
|
|
}
|
|
isDatetimeUnit := true
|
|
args[0] = WrapWithCastAsString(args[0], ctx)
|
|
if _, isCon := args[0].(*Constant); isCon {
|
|
unit, _, err1 := args[0].EvalString(nil, ctx.GetSessionVars().StmtCtx)
|
|
if err1 != nil {
|
|
return nil, errors.Trace(err1)
|
|
}
|
|
_, isDatetimeUnit = datetimeUnits[unit]
|
|
}
|
|
var bf baseBuiltinFunc
|
|
if isDatetimeUnit {
|
|
bf = newBaseBuiltinFuncWithTp(args, ctx, tpInt, tpString, tpDatetime)
|
|
sig = &builtinExtractDatetimeSig{baseIntBuiltinFunc{bf}}
|
|
} else {
|
|
bf = newBaseBuiltinFuncWithTp(args, ctx, tpInt, tpString, tpDuration)
|
|
sig = &builtinExtractDurationSig{baseIntBuiltinFunc{bf}}
|
|
}
|
|
return sig.setSelf(sig), nil
|
|
}
|
|
|
|
type builtinExtractDatetimeSig struct {
|
|
baseIntBuiltinFunc
|
|
}
|
|
|
|
// 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 []types.Datum) (int64, bool, error) {
|
|
sc := b.ctx.GetSessionVars().StmtCtx
|
|
unit, isNull, err := b.args[0].EvalString(row, sc)
|
|
if isNull || err != nil {
|
|
return 0, isNull, errors.Trace(err)
|
|
}
|
|
dt, isNull, err := b.args[1].EvalTime(row, sc)
|
|
if isNull || err != nil {
|
|
return 0, isNull, errors.Trace(err)
|
|
}
|
|
res, err := types.ExtractDatetimeNum(&dt, unit)
|
|
return res, false, errors.Trace(err)
|
|
}
|
|
|
|
type builtinExtractDurationSig struct {
|
|
baseIntBuiltinFunc
|
|
}
|
|
|
|
// 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 []types.Datum) (int64, bool, error) {
|
|
sc := b.ctx.GetSessionVars().StmtCtx
|
|
unit, isNull, err := b.args[0].EvalString(row, sc)
|
|
if isNull || err != nil {
|
|
return 0, isNull, errors.Trace(err)
|
|
}
|
|
dur, isNull, err := b.args[1].EvalDuration(row, sc)
|
|
if isNull || err != nil {
|
|
return 0, isNull, errors.Trace(err)
|
|
}
|
|
res, err := types.ExtractDurationNum(&dur, unit)
|
|
return res, false, errors.Trace(err)
|
|
}
|
|
|
|
type dateArithFunctionClass struct {
|
|
baseFunctionClass
|
|
|
|
op ast.DateArithType
|
|
}
|
|
|
|
func (c *dateArithFunctionClass) getFunction(ctx context.Context, args []Expression) (builtinFunc, error) {
|
|
if err := c.verifyArgs(args); err != nil {
|
|
return nil, errors.Trace(err)
|
|
}
|
|
sig := &builtinDateArithSig{newBaseBuiltinFunc(args, ctx), c.op}
|
|
return sig.setSelf(sig), nil
|
|
}
|
|
|
|
type builtinDateArithSig struct {
|
|
baseBuiltinFunc
|
|
|
|
op ast.DateArithType
|
|
}
|
|
|
|
func (b *builtinDateArithSig) eval(row []types.Datum) (d types.Datum, err error) {
|
|
args, err := b.evalArgs(row)
|
|
if err != nil {
|
|
return d, errors.Trace(err)
|
|
}
|
|
// args[0] -> Date
|
|
// args[1] -> Interval Value
|
|
// args[2] -> Interval Unit
|
|
// health check for date and interval
|
|
if args[0].IsNull() || args[1].IsNull() {
|
|
return
|
|
}
|
|
nodeDate := args[0]
|
|
nodeIntervalValue := args[1]
|
|
nodeIntervalUnit := args[2].GetString()
|
|
if nodeIntervalValue.IsNull() {
|
|
return
|
|
}
|
|
// parse date
|
|
fieldType := mysql.TypeDate
|
|
var resultField *types.FieldType
|
|
switch nodeDate.Kind() {
|
|
case types.KindMysqlTime:
|
|
x := nodeDate.GetMysqlTime()
|
|
if (x.Type == mysql.TypeDatetime) || (x.Type == mysql.TypeTimestamp) {
|
|
fieldType = mysql.TypeDatetime
|
|
}
|
|
case types.KindString:
|
|
x := nodeDate.GetString()
|
|
if !types.IsDateFormat(x) {
|
|
fieldType = mysql.TypeDatetime
|
|
}
|
|
case types.KindInt64:
|
|
x := nodeDate.GetInt64()
|
|
if t, err1 := types.ParseTimeFromInt64(x); err1 == nil {
|
|
if (t.Type == mysql.TypeDatetime) || (t.Type == mysql.TypeTimestamp) {
|
|
fieldType = mysql.TypeDatetime
|
|
}
|
|
}
|
|
}
|
|
sc := b.ctx.GetSessionVars().StmtCtx
|
|
if types.IsClockUnit(nodeIntervalUnit) {
|
|
fieldType = mysql.TypeDatetime
|
|
}
|
|
resultField = types.NewFieldType(fieldType)
|
|
resultField.Decimal = types.MaxFsp
|
|
value, err := nodeDate.ConvertTo(b.ctx.GetSessionVars().StmtCtx, resultField)
|
|
if err != nil {
|
|
return d, errInvalidOperation.Gen("DateArith invalid args, need date but get %T", nodeDate)
|
|
}
|
|
if value.IsNull() {
|
|
return d, errInvalidOperation.Gen("DateArith invalid args, need date but get %v", value.GetValue())
|
|
}
|
|
if value.Kind() != types.KindMysqlTime {
|
|
return d, errInvalidOperation.Gen("DateArith need time type, but got %T", value.GetValue())
|
|
}
|
|
result := value.GetMysqlTime()
|
|
// parse interval
|
|
var interval string
|
|
if strings.ToLower(nodeIntervalUnit) == "day" {
|
|
day, err1 := parseDayInterval(sc, nodeIntervalValue)
|
|
if err1 != nil {
|
|
return d, errInvalidOperation.Gen("DateArith invalid day interval, need int but got %T", nodeIntervalValue.GetString())
|
|
}
|
|
interval = fmt.Sprintf("%d", day)
|
|
} else {
|
|
if nodeIntervalValue.Kind() == types.KindString {
|
|
interval = fmt.Sprintf("%v", nodeIntervalValue.GetString())
|
|
} else {
|
|
ii, err1 := nodeIntervalValue.ToInt64(sc)
|
|
if err1 != nil {
|
|
return d, errors.Trace(err1)
|
|
}
|
|
interval = fmt.Sprintf("%v", ii)
|
|
}
|
|
}
|
|
year, month, day, dur, err := types.ExtractTimeValue(nodeIntervalUnit, interval)
|
|
if err != nil {
|
|
return d, errors.Trace(err)
|
|
}
|
|
if b.op == ast.DateArithSub {
|
|
year, month, day, dur = -year, -month, -day, -dur
|
|
}
|
|
// TODO: Consider time_zone variable.
|
|
t, err := result.Time.GoTime(time.Local)
|
|
if err != nil {
|
|
return d, errors.Trace(err)
|
|
}
|
|
t = t.Add(dur)
|
|
t = t.AddDate(int(year), int(month), int(day))
|
|
if t.Nanosecond() == 0 {
|
|
result.Fsp = 0
|
|
}
|
|
result.Time = types.FromGoTime(t)
|
|
d.SetMysqlTime(result)
|
|
return
|
|
}
|
|
|
|
var reg = regexp.MustCompile(`[\d]+`)
|
|
|
|
func parseDayInterval(sc *variable.StatementContext, value types.Datum) (int64, error) {
|
|
switch value.Kind() {
|
|
case types.KindString:
|
|
vs := value.GetString()
|
|
s := strings.ToLower(vs)
|
|
if s == "false" {
|
|
return 0, nil
|
|
} else if s == "true" {
|
|
return 1, nil
|
|
}
|
|
value.SetString(reg.FindString(vs))
|
|
}
|
|
return value.ToInt64(sc)
|
|
}
|
|
|
|
type timestampDiffFunctionClass struct {
|
|
baseFunctionClass
|
|
}
|
|
|
|
func (c *timestampDiffFunctionClass) getFunction(ctx context.Context, args []Expression) (builtinFunc, error) {
|
|
if err := c.verifyArgs(args); err != nil {
|
|
return nil, errors.Trace(err)
|
|
}
|
|
bf := newBaseBuiltinFuncWithTp(args, ctx, tpInt, tpString, tpDatetime, tpDatetime)
|
|
sig := &builtinTimestampDiffSig{baseIntBuiltinFunc{bf}}
|
|
return sig.setSelf(sig), nil
|
|
}
|
|
|
|
type builtinTimestampDiffSig struct {
|
|
baseIntBuiltinFunc
|
|
}
|
|
|
|
// 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 []types.Datum) (int64, bool, error) {
|
|
ctx := b.getCtx().GetSessionVars().StmtCtx
|
|
unit, isNull, err := b.args[0].EvalString(row, ctx)
|
|
if isNull || err != nil {
|
|
return 0, isNull, errors.Trace(err)
|
|
}
|
|
t1, isNull, err := b.args[1].EvalTime(row, ctx)
|
|
if isNull || err != nil {
|
|
return 0, isNull, errors.Trace(handleInvalidTimeError(b.getCtx(), err))
|
|
}
|
|
t2, isNull, err := b.args[2].EvalTime(row, ctx)
|
|
if isNull || err != nil {
|
|
return 0, isNull, errors.Trace(handleInvalidTimeError(b.getCtx(), err))
|
|
}
|
|
if t1.InvalidZero() || t2.InvalidZero() {
|
|
return 0, true, errors.Trace(handleInvalidTimeError(b.ctx, types.ErrInvalidTimeFormat))
|
|
}
|
|
return types.TimestampDiff(unit, t1, t2), false, nil
|
|
}
|
|
|
|
type unixTimestampFunctionClass struct {
|
|
baseFunctionClass
|
|
}
|
|
|
|
func (c *unixTimestampFunctionClass) getFunction(ctx context.Context, args []Expression) (builtinFunc, error) {
|
|
if err := c.verifyArgs(args); err != nil {
|
|
return nil, errors.Trace(err)
|
|
}
|
|
var argTps []evalTp
|
|
var retTp evalTp
|
|
var retFLen, retDecimal int
|
|
|
|
if len(args) == 0 {
|
|
retTp, retDecimal = tpInt, 0
|
|
} else {
|
|
argTps = []evalTp{tpDatetime}
|
|
argType := args[0].GetType()
|
|
argEvaltp := fieldTp2EvalTp(argType)
|
|
if argEvaltp == tpString {
|
|
// Treat tpString as unspecified decimal.
|
|
retDecimal = types.UnspecifiedLength
|
|
} else {
|
|
retDecimal = argType.Decimal
|
|
}
|
|
if retDecimal > 6 || retDecimal == types.UnspecifiedLength {
|
|
retDecimal = 6
|
|
}
|
|
if retDecimal == 0 {
|
|
retTp = tpInt
|
|
} else {
|
|
retTp = tpDecimal
|
|
}
|
|
}
|
|
if retTp == tpInt {
|
|
retFLen = 11
|
|
} else if retTp == tpDecimal {
|
|
retFLen = 12 + retDecimal
|
|
} else {
|
|
panic("Unexpected retTp")
|
|
}
|
|
|
|
bf := newBaseBuiltinFuncWithTp(args, ctx, retTp, argTps...)
|
|
bf.foldable = false
|
|
bf.tp.Flen = retFLen
|
|
bf.tp.Decimal = retDecimal
|
|
|
|
var sig builtinFunc
|
|
if len(args) == 0 {
|
|
sig = &builtinUnixTimestampCurrentSig{baseIntBuiltinFunc{bf}}
|
|
} else if retTp == tpInt {
|
|
sig = &builtinUnixTimestampIntSig{baseIntBuiltinFunc{bf}}
|
|
} else if retTp == tpDecimal {
|
|
sig = &builtinUnixTimestampDecSig{baseDecimalBuiltinFunc{bf}}
|
|
} else {
|
|
panic("Unexpected retTp")
|
|
}
|
|
|
|
return sig.setSelf(sig), nil
|
|
}
|
|
|
|
// goTimeToMysqlUnixTimestamp converts go time into MySQL's Unix timestamp.
|
|
// MySQL's Unix timestamp ranges in int32. Values out of range should be rewritten to 0.
|
|
func goTimeToMysqlUnixTimestamp(t time.Time, decimal int) *types.MyDecimal {
|
|
nanoSeconds := t.UnixNano()
|
|
if nanoSeconds < 0 || (nanoSeconds/1e3) >= (math.MaxInt32+1)*1e6 {
|
|
return new(types.MyDecimal)
|
|
}
|
|
dec := new(types.MyDecimal)
|
|
// Here we don't use float to prevent precision lose.
|
|
dec.FromInt(nanoSeconds)
|
|
dec.Shift(-9)
|
|
dec.Round(dec, decimal, types.ModeHalfEven)
|
|
return dec
|
|
}
|
|
|
|
type builtinUnixTimestampCurrentSig struct {
|
|
baseIntBuiltinFunc
|
|
}
|
|
|
|
// 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 []types.Datum) (int64, bool, error) {
|
|
dec := goTimeToMysqlUnixTimestamp(time.Now(), 1)
|
|
intVal, _ := dec.ToInt() // Ignore truncate errors.
|
|
return intVal, false, nil
|
|
}
|
|
|
|
type builtinUnixTimestampIntSig struct {
|
|
baseIntBuiltinFunc
|
|
}
|
|
|
|
// 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 []types.Datum) (int64, bool, error) {
|
|
val, isNull, err := b.args[0].EvalTime(row, b.getCtx().GetSessionVars().StmtCtx)
|
|
if isNull || err != nil {
|
|
// Return 0 for invalid date time.
|
|
return 0, isNull, nil
|
|
}
|
|
t, err := val.Time.GoTime(getTimeZone(b.getCtx()))
|
|
if err != nil {
|
|
return 0, false, nil
|
|
}
|
|
dec := goTimeToMysqlUnixTimestamp(t, 1)
|
|
intVal, _ := dec.ToInt() // Ignore truncate errors.
|
|
return intVal, false, nil
|
|
}
|
|
|
|
type builtinUnixTimestampDecSig struct {
|
|
baseDecimalBuiltinFunc
|
|
}
|
|
|
|
// 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 []types.Datum) (*types.MyDecimal, bool, error) {
|
|
val, isNull, err := b.args[0].EvalTime(row, b.getCtx().GetSessionVars().StmtCtx)
|
|
if isNull || err != nil {
|
|
// Return 0 for invalid date time.
|
|
return new(types.MyDecimal), isNull, nil
|
|
}
|
|
t, err := val.Time.GoTime(getTimeZone(b.getCtx()))
|
|
if err != nil {
|
|
return new(types.MyDecimal), false, nil
|
|
}
|
|
return goTimeToMysqlUnixTimestamp(t, b.tp.Decimal), false, nil
|
|
}
|
|
|
|
type timestampFunctionClass struct {
|
|
baseFunctionClass
|
|
}
|
|
|
|
func (c *timestampFunctionClass) getDefaultFsp(tp *types.FieldType) int {
|
|
if tp.Tp == mysql.TypeDatetime || tp.Tp == mysql.TypeDate || tp.Tp == mysql.TypeDuration ||
|
|
tp.Tp == mysql.TypeTimestamp || tp.Tp == mysql.TypeNewDate {
|
|
return tp.Decimal
|
|
}
|
|
switch cls := tp.ToClass(); cls {
|
|
case types.ClassInt:
|
|
return types.MinFsp
|
|
case types.ClassReal, types.ClassString:
|
|
return types.MaxFsp
|
|
case types.ClassDecimal:
|
|
if tp.Decimal < types.MaxFsp {
|
|
return tp.Decimal
|
|
}
|
|
return types.MaxFsp
|
|
}
|
|
return types.MaxFsp
|
|
}
|
|
|
|
func (c *timestampFunctionClass) getFunction(ctx context.Context, args []Expression) (builtinFunc, error) {
|
|
if err := c.verifyArgs(args); err != nil {
|
|
return nil, errors.Trace(err)
|
|
}
|
|
evalTps, argLen := []evalTp{tpString}, len(args)
|
|
if argLen == 2 {
|
|
evalTps = append(evalTps, tpString)
|
|
}
|
|
fsp := c.getDefaultFsp(args[0].GetType())
|
|
if argLen == 2 {
|
|
fsp = mathutil.Max(fsp, c.getDefaultFsp(args[1].GetType()))
|
|
}
|
|
bf := newBaseBuiltinFuncWithTp(args, ctx, tpDatetime, evalTps...)
|
|
bf.tp.Decimal, bf.tp.Flen = fsp, 19
|
|
if fsp != 0 {
|
|
bf.tp.Flen += 1 + fsp
|
|
}
|
|
var sig builtinFunc
|
|
if argLen == 2 {
|
|
sig = &builtinTimestamp2ArgsSig{baseTimeBuiltinFunc{bf}}
|
|
} else {
|
|
sig = &builtinTimestamp1ArgSig{baseTimeBuiltinFunc{bf}}
|
|
}
|
|
return sig.setSelf(sig), nil
|
|
}
|
|
|
|
type builtinTimestamp1ArgSig struct {
|
|
baseTimeBuiltinFunc
|
|
}
|
|
|
|
// 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 []types.Datum) (types.Time, bool, error) {
|
|
s, isNull, err := b.args[0].EvalString(row, b.ctx.GetSessionVars().StmtCtx)
|
|
if isNull || err != nil {
|
|
return types.Time{}, isNull, errors.Trace(err)
|
|
}
|
|
tm, err := types.ParseTime(s, mysql.TypeDatetime, getFsp(s))
|
|
if err != nil {
|
|
return types.Time{}, true, errors.Trace(handleInvalidTimeError(b.ctx, err))
|
|
}
|
|
return tm, false, nil
|
|
}
|
|
|
|
type builtinTimestamp2ArgsSig struct {
|
|
baseTimeBuiltinFunc
|
|
}
|
|
|
|
// 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 []types.Datum) (types.Time, bool, error) {
|
|
sc := b.ctx.GetSessionVars().StmtCtx
|
|
arg0, isNull, err := b.args[0].EvalString(row, sc)
|
|
if isNull || err != nil {
|
|
return types.Time{}, isNull, errors.Trace(err)
|
|
}
|
|
tm, err := types.ParseTime(arg0, mysql.TypeDatetime, getFsp(arg0))
|
|
if err != nil {
|
|
return types.Time{}, true, errors.Trace(handleInvalidTimeError(b.ctx, err))
|
|
}
|
|
arg1, isNull, err := b.args[1].EvalString(row, sc)
|
|
if isNull || err != nil {
|
|
return types.Time{}, isNull, errors.Trace(err)
|
|
}
|
|
duration, err := types.ParseDuration(arg1, getFsp(arg1))
|
|
if err != nil {
|
|
return types.Time{}, true, errors.Trace(handleInvalidTimeError(b.ctx, err))
|
|
}
|
|
if !isDuration(arg1) {
|
|
return types.Time{}, true, nil
|
|
}
|
|
tmp, err := tm.Add(duration)
|
|
if err != nil {
|
|
return types.Time{}, true, errors.Trace(err)
|
|
}
|
|
return tmp, false, nil
|
|
}
|
|
|
|
func getFsp(s string) (fsp int) {
|
|
fsp = len(s) - strings.Index(s, ".") - 1
|
|
if fsp == len(s) {
|
|
fsp = 0
|
|
} else if fsp > 6 {
|
|
fsp = 6
|
|
}
|
|
return
|
|
}
|
|
|
|
func getTimeZone(ctx context.Context) *time.Location {
|
|
ret := ctx.GetSessionVars().TimeZone
|
|
if ret == nil {
|
|
ret = time.Local
|
|
}
|
|
return ret
|
|
}
|
|
|
|
// isDuration returns a boolean indicating whether the str matches the format of duration.
|
|
// See https://dev.mysql.com/doc/refman/5.7/en/time.html
|
|
func isDuration(str string) bool {
|
|
return DurationPattern.MatchString(str)
|
|
}
|
|
|
|
// strDatetimeAddDuration adds duration to datetime string, returns a datum value.
|
|
func strDatetimeAddDuration(d string, arg1 types.Duration) (result types.Datum, err error) {
|
|
arg0, err := types.ParseTime(d, mysql.TypeDatetime, getFsp(d))
|
|
if err != nil {
|
|
return result, errors.Trace(err)
|
|
}
|
|
tmp, err1 := arg0.Add(arg1)
|
|
if err1 != nil {
|
|
err = errors.Trace(err)
|
|
return
|
|
}
|
|
if getFsp(d) != 0 {
|
|
tmp.Fsp = types.MaxFsp
|
|
} else {
|
|
tmp.Fsp = types.MinFsp
|
|
}
|
|
result.SetString(tmp.String())
|
|
return
|
|
}
|
|
|
|
// strDurationAddDuration adds duration to duration string, returns a datum value.
|
|
func strDurationAddDuration(d string, arg1 types.Duration) (result types.Datum, err error) {
|
|
arg0, err := types.ParseDuration(d, getFsp(d))
|
|
if err != nil {
|
|
return result, errors.Trace(err)
|
|
}
|
|
tmpDuration, err := arg0.Add(arg1)
|
|
if err != nil {
|
|
return result, errors.Trace(err)
|
|
}
|
|
if getFsp(d) != 0 {
|
|
tmpDuration.Fsp = types.MaxFsp
|
|
} else {
|
|
tmpDuration.Fsp = types.MinFsp
|
|
}
|
|
result.SetString(tmpDuration.String())
|
|
return
|
|
}
|
|
|
|
// strDatetimeSubDuration subtracts duration from datetime string, returns a datum value.
|
|
func strDatetimeSubDuration(d string, arg1 types.Duration) (result types.Datum, err error) {
|
|
arg0, err := types.ParseTime(d, mysql.TypeDatetime, getFsp(d))
|
|
if err != nil {
|
|
return result, errors.Trace(err)
|
|
}
|
|
arg1time, err := arg1.ConvertToTime(uint8(getFsp(arg1.String())))
|
|
if err != nil {
|
|
return result, errors.Trace(err)
|
|
}
|
|
tmpDuration := arg0.Sub(&arg1time)
|
|
resultDuration, err := tmpDuration.ConvertToTime(mysql.TypeDatetime)
|
|
if err != nil {
|
|
return result, errors.Trace(err)
|
|
}
|
|
if getFsp(d) != 0 {
|
|
tmpDuration.Fsp = types.MaxFsp
|
|
} else {
|
|
tmpDuration.Fsp = types.MinFsp
|
|
}
|
|
result.SetString(resultDuration.String())
|
|
return
|
|
}
|
|
|
|
// strDurationSubDuration subtracts duration from duration string, returns a datum value.
|
|
func strDurationSubDuration(d string, arg1 types.Duration) (result types.Datum, err error) {
|
|
arg0, err := types.ParseDuration(d, getFsp(d))
|
|
if err != nil {
|
|
return result, errors.Trace(err)
|
|
}
|
|
tmpDuration, err := arg0.Sub(arg1)
|
|
if err != nil {
|
|
return result, errors.Trace(err)
|
|
}
|
|
if getFsp(d) != 0 {
|
|
tmpDuration.Fsp = types.MaxFsp
|
|
} else {
|
|
tmpDuration.Fsp = types.MinFsp
|
|
}
|
|
result.SetString(tmpDuration.String())
|
|
return
|
|
}
|
|
|
|
type addTimeFunctionClass struct {
|
|
baseFunctionClass
|
|
}
|
|
|
|
func (c *addTimeFunctionClass) getFunction(ctx context.Context, args []Expression) (builtinFunc, error) {
|
|
if err := c.verifyArgs(args); err != nil {
|
|
return nil, errors.Trace(err)
|
|
}
|
|
sig := &builtinAddTimeSig{newBaseBuiltinFunc(args, ctx)}
|
|
return sig.setSelf(sig), nil
|
|
}
|
|
|
|
type builtinAddTimeSig struct {
|
|
baseBuiltinFunc
|
|
}
|
|
|
|
// eval evals a builtinAddTimeSig.
|
|
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_addtime
|
|
func (b *builtinAddTimeSig) eval(row []types.Datum) (d types.Datum, err error) {
|
|
args, err := b.evalArgs(row)
|
|
if err != nil {
|
|
return d, errors.Trace(err)
|
|
}
|
|
if args[0].IsNull() || args[1].IsNull() {
|
|
return
|
|
}
|
|
var arg1 types.Duration
|
|
switch tp := args[1].Kind(); tp {
|
|
case types.KindMysqlDuration:
|
|
arg1 = args[1].GetMysqlDuration()
|
|
default:
|
|
s, err := args[1].ToString()
|
|
if err != nil {
|
|
return d, errors.Trace(err)
|
|
}
|
|
if getFsp(s) == 0 {
|
|
arg1, err = types.ParseDuration(s, 0)
|
|
} else {
|
|
arg1, err = types.ParseDuration(s, types.MaxFsp)
|
|
}
|
|
if err != nil {
|
|
return d, errors.Trace(err)
|
|
}
|
|
|
|
}
|
|
switch tp := args[0].Kind(); tp {
|
|
case types.KindMysqlTime:
|
|
arg0 := args[0].GetMysqlTime()
|
|
result, err := arg0.Add(arg1)
|
|
if err != nil {
|
|
return d, errors.Trace(err)
|
|
}
|
|
d.SetMysqlTime(result)
|
|
case types.KindMysqlDuration:
|
|
arg0 := args[0].GetMysqlDuration()
|
|
result, err := arg0.Add(arg1)
|
|
if err != nil {
|
|
return d, errors.Trace(err)
|
|
}
|
|
d.SetMysqlDuration(result)
|
|
default:
|
|
ss, err := args[0].ToString()
|
|
if err != nil {
|
|
return d, errors.Trace(err)
|
|
}
|
|
if isDuration(ss) {
|
|
return strDurationAddDuration(ss, arg1)
|
|
}
|
|
return strDatetimeAddDuration(ss, arg1)
|
|
}
|
|
return
|
|
}
|
|
|
|
type convertTzFunctionClass struct {
|
|
baseFunctionClass
|
|
}
|
|
|
|
func (c *convertTzFunctionClass) getDecimal(ctx context.Context, arg Expression) int {
|
|
decimal := types.MaxFsp
|
|
if dt, isConstant := arg.(*Constant); isConstant {
|
|
switch fieldTp2EvalTp(arg.GetType()) {
|
|
case tpInt:
|
|
decimal = 0
|
|
case tpReal, tpDecimal:
|
|
decimal = arg.GetType().Decimal
|
|
case tpString:
|
|
str, isNull, err := dt.EvalString(nil, ctx.GetSessionVars().StmtCtx)
|
|
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 context.Context, args []Expression) (builtinFunc, error) {
|
|
if err := c.verifyArgs(args); err != nil {
|
|
return nil, errors.Trace(err)
|
|
}
|
|
// tzRegex holds the regex to check whether a string is a time zone.
|
|
tzRegex, err := regexp.Compile(`(^(\+|-)(0?[0-9]|1[0-2]):[0-5]?\d$)|(^\+13:00$)`)
|
|
if err != nil {
|
|
return nil, errors.Trace(err)
|
|
}
|
|
|
|
decimal := c.getDecimal(ctx, args[0])
|
|
bf := newBaseBuiltinFuncWithTp(args, ctx, tpDatetime, tpDatetime, tpString, tpString)
|
|
bf.tp.Decimal = decimal
|
|
sig := &builtinConvertTzSig{
|
|
baseTimeBuiltinFunc: baseTimeBuiltinFunc{bf},
|
|
timezoneRegex: tzRegex,
|
|
}
|
|
return sig.setSelf(sig), nil
|
|
}
|
|
|
|
type builtinConvertTzSig struct {
|
|
baseTimeBuiltinFunc
|
|
timezoneRegex *regexp.Regexp
|
|
}
|
|
|
|
// evalTime evals CONVERT_TZ(dt,from_tz,to_tz).
|
|
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_convert-tz
|
|
func (b *builtinConvertTzSig) evalTime(row []types.Datum) (types.Time, bool, error) {
|
|
sc := b.ctx.GetSessionVars().StmtCtx
|
|
|
|
dt, isNull, err := b.args[0].EvalTime(row, sc)
|
|
if isNull || err != nil {
|
|
return types.Time{}, true, errors.Trace(err)
|
|
}
|
|
|
|
fromTzStr, isNull, err := b.args[1].EvalString(row, sc)
|
|
if isNull || err != nil {
|
|
return types.Time{}, true, errors.Trace(err)
|
|
}
|
|
|
|
toTzStr, isNull, err := b.args[2].EvalString(row, sc)
|
|
if isNull || err != nil {
|
|
return types.Time{}, true, errors.Trace(err)
|
|
}
|
|
|
|
fromTzMatched := b.timezoneRegex.MatchString(fromTzStr)
|
|
toTzMatched := b.timezoneRegex.MatchString(toTzStr)
|
|
|
|
if !fromTzMatched && !toTzMatched {
|
|
fromTz, err := time.LoadLocation(fromTzStr)
|
|
if err != nil {
|
|
return types.Time{}, true, errors.Trace(err)
|
|
}
|
|
|
|
toTz, err := time.LoadLocation(toTzStr)
|
|
if err != nil {
|
|
return types.Time{}, true, errors.Trace(err)
|
|
}
|
|
|
|
t, err := dt.Time.GoTime(fromTz)
|
|
if err != nil {
|
|
return types.Time{}, true, errors.Trace(err)
|
|
}
|
|
|
|
return types.Time{
|
|
Time: types.FromGoTime(t.In(toTz)),
|
|
Type: mysql.TypeDatetime,
|
|
Fsp: b.tp.Decimal,
|
|
}, false, nil
|
|
}
|
|
if fromTzMatched && toTzMatched {
|
|
t, err := dt.Time.GoTime(time.Local)
|
|
if err != nil {
|
|
return types.Time{}, true, errors.Trace(err)
|
|
}
|
|
|
|
return types.Time{
|
|
Time: types.FromGoTime(t.Add(timeZone2Duration(toTzStr) - timeZone2Duration(fromTzStr))),
|
|
Type: mysql.TypeDatetime,
|
|
Fsp: b.tp.Decimal,
|
|
}, false, nil
|
|
}
|
|
return types.Time{}, true, nil
|
|
}
|
|
|
|
type makeDateFunctionClass struct {
|
|
baseFunctionClass
|
|
}
|
|
|
|
func (c *makeDateFunctionClass) getFunction(ctx context.Context, args []Expression) (builtinFunc, error) {
|
|
if err := c.verifyArgs(args); err != nil {
|
|
return nil, errors.Trace(err)
|
|
}
|
|
bf := newBaseBuiltinFuncWithTp(args, ctx, tpDatetime, tpInt, tpInt)
|
|
tp := bf.tp
|
|
tp.Tp, tp.Flen, tp.Decimal = mysql.TypeDate, mysql.MaxDateWidth, 0
|
|
sig := &builtinMakeDateSig{baseTimeBuiltinFunc{bf}}
|
|
return sig.setSelf(sig), nil
|
|
}
|
|
|
|
type builtinMakeDateSig struct {
|
|
baseTimeBuiltinFunc
|
|
}
|
|
|
|
// 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 []types.Datum) (d types.Time, isNull bool, err error) {
|
|
args := b.getArgs()
|
|
sc := b.ctx.GetSessionVars().StmtCtx
|
|
var year, dayOfYear int64
|
|
year, isNull, err = args[0].EvalInt(row, sc)
|
|
if isNull || err != nil {
|
|
return d, true, errors.Trace(err)
|
|
}
|
|
dayOfYear, isNull, err = args[1].EvalInt(row, sc)
|
|
if isNull || err != nil {
|
|
return d, true, errors.Trace(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.Time{
|
|
Time: types.FromDate(int(year), 1, 1, 0, 0, 0, 0),
|
|
Type: mysql.TypeDate,
|
|
Fsp: 0,
|
|
}
|
|
retTimestamp := types.TimestampDiff("DAY", types.ZeroDate, startTime)
|
|
if retTimestamp == 0 {
|
|
return d, true, errors.Trace(handleInvalidTimeError(b.ctx, types.ErrInvalidTimeFormat))
|
|
}
|
|
ret := types.TimeFromDays(retTimestamp + dayOfYear - 1)
|
|
if ret.IsZero() || ret.Time.Year() > 9999 {
|
|
return d, true, nil
|
|
}
|
|
return ret, false, nil
|
|
}
|
|
|
|
type makeTimeFunctionClass struct {
|
|
baseFunctionClass
|
|
}
|
|
|
|
func (c *makeTimeFunctionClass) getFunction(ctx context.Context, args []Expression) (builtinFunc, error) {
|
|
if err := c.verifyArgs(args); err != nil {
|
|
return nil, errors.Trace(err)
|
|
}
|
|
sig := &builtinMakeTimeSig{newBaseBuiltinFunc(args, ctx)}
|
|
return sig.setSelf(sig), nil
|
|
}
|
|
|
|
type builtinMakeTimeSig struct {
|
|
baseBuiltinFunc
|
|
}
|
|
|
|
// eval evals a builtinMakeTimeSig.
|
|
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_maketime
|
|
func (b *builtinMakeTimeSig) eval(row []types.Datum) (d types.Datum, err error) {
|
|
args, err := b.evalArgs(row)
|
|
if err != nil {
|
|
return d, errors.Trace(err)
|
|
}
|
|
// MAKETIME(hour, minute, second)
|
|
if args[0].IsNull() || args[1].IsNull() || args[2].IsNull() {
|
|
return
|
|
}
|
|
|
|
var (
|
|
hour int64
|
|
minute int64
|
|
second float64
|
|
overflow bool
|
|
)
|
|
sc := b.ctx.GetSessionVars().StmtCtx
|
|
hour, _ = args[0].ToInt64(sc)
|
|
// 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 < -838 {
|
|
hour = -838
|
|
overflow = true
|
|
} else if hour > 838 {
|
|
hour = 838
|
|
overflow = true
|
|
}
|
|
|
|
minute, _ = args[1].ToInt64(sc)
|
|
if minute < 0 || minute >= 60 {
|
|
return
|
|
}
|
|
|
|
second, _ = args[2].ToFloat64(sc)
|
|
if second < 0 || second >= 60 {
|
|
return
|
|
}
|
|
if hour == -838 || hour == 838 {
|
|
if second > 59 {
|
|
second = 59
|
|
}
|
|
}
|
|
if overflow {
|
|
minute = 59
|
|
second = 59
|
|
}
|
|
|
|
var dur types.Duration
|
|
fsp := types.MaxFsp
|
|
if args[2].Kind() != types.KindString {
|
|
sec, _ := args[2].ToString()
|
|
secs := strings.Split(sec, ".")
|
|
if len(secs) <= 1 {
|
|
fsp = 0
|
|
} else if len(secs[1]) < fsp {
|
|
fsp = len(secs[1])
|
|
}
|
|
}
|
|
dur, err = types.ParseDuration(fmt.Sprintf("%02d:%02d:%v", hour, minute, second), fsp)
|
|
if err != nil {
|
|
return d, errors.Trace(err)
|
|
}
|
|
d.SetMysqlDuration(dur)
|
|
return
|
|
}
|
|
|
|
type periodAddFunctionClass struct {
|
|
baseFunctionClass
|
|
}
|
|
|
|
func (c *periodAddFunctionClass) getFunction(ctx context.Context, args []Expression) (builtinFunc, error) {
|
|
if err := c.verifyArgs(args); err != nil {
|
|
return nil, errors.Trace(err)
|
|
}
|
|
|
|
bf := newBaseBuiltinFuncWithTp(args, ctx, tpInt, tpInt, tpInt)
|
|
bf.tp.Flen = 6
|
|
sig := &builtinPeriodAddSig{baseIntBuiltinFunc{bf}}
|
|
return sig.setSelf(sig), nil
|
|
}
|
|
|
|
// 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 {
|
|
baseIntBuiltinFunc
|
|
}
|
|
|
|
// 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 []types.Datum) (int64, bool, error) {
|
|
sc := b.ctx.GetSessionVars().StmtCtx
|
|
|
|
p, isNull, err := b.args[0].EvalInt(row, sc)
|
|
if isNull || err != nil {
|
|
return 0, true, errors.Trace(err)
|
|
}
|
|
|
|
if p == 0 {
|
|
return 0, false, nil
|
|
}
|
|
|
|
n, isNull, err := b.args[1].EvalInt(row, sc)
|
|
if isNull || err != nil {
|
|
return 0, true, errors.Trace(err)
|
|
}
|
|
|
|
sumMonth := int64(period2Month(uint64(p))) + n
|
|
return int64(month2Period(uint64(sumMonth))), false, nil
|
|
}
|
|
|
|
type periodDiffFunctionClass struct {
|
|
baseFunctionClass
|
|
}
|
|
|
|
func (c *periodDiffFunctionClass) getFunction(ctx context.Context, args []Expression) (builtinFunc, error) {
|
|
if err := c.verifyArgs(args); err != nil {
|
|
return nil, errors.Trace(err)
|
|
}
|
|
bf := newBaseBuiltinFuncWithTp(args, ctx, tpInt, tpInt, tpInt)
|
|
bf.tp.Flen = 6
|
|
sig := &builtinPeriodDiffSig{baseIntBuiltinFunc{bf}}
|
|
return sig.setSelf(sig), nil
|
|
}
|
|
|
|
type builtinPeriodDiffSig struct {
|
|
baseIntBuiltinFunc
|
|
}
|
|
|
|
// 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 []types.Datum) (int64, bool, error) {
|
|
sc := b.ctx.GetSessionVars().StmtCtx
|
|
|
|
p1, isNull, err := b.args[0].EvalInt(row, sc)
|
|
if isNull || err != nil {
|
|
return 0, isNull, errors.Trace(err)
|
|
}
|
|
|
|
p2, isNull, err := b.args[1].EvalInt(row, sc)
|
|
if isNull || err != nil {
|
|
return 0, isNull, errors.Trace(err)
|
|
}
|
|
|
|
return int64(period2Month(uint64(p1)) - period2Month(uint64(p2))), false, nil
|
|
}
|
|
|
|
type quarterFunctionClass struct {
|
|
baseFunctionClass
|
|
}
|
|
|
|
func (c *quarterFunctionClass) getFunction(ctx context.Context, args []Expression) (builtinFunc, error) {
|
|
if err := c.verifyArgs(args); err != nil {
|
|
return nil, errors.Trace(err)
|
|
}
|
|
|
|
bf := newBaseBuiltinFuncWithTp(args, ctx, tpInt, tpDatetime)
|
|
bf.tp.Flen = 1
|
|
|
|
sig := &builtinQuarterSig{baseIntBuiltinFunc{bf}}
|
|
return sig.setSelf(sig), nil
|
|
}
|
|
|
|
type builtinQuarterSig struct {
|
|
baseIntBuiltinFunc
|
|
}
|
|
|
|
// 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 []types.Datum) (int64, bool, error) {
|
|
sc := b.ctx.GetSessionVars().StmtCtx
|
|
|
|
date, isNull, err := b.args[0].EvalTime(row, sc)
|
|
if isNull || err != nil {
|
|
return 0, true, errors.Trace(handleInvalidTimeError(b.ctx, err))
|
|
}
|
|
|
|
if date.IsZero() {
|
|
return 0, true, errors.Trace(handleInvalidTimeError(b.ctx, types.ErrInvalidTimeFormat))
|
|
}
|
|
|
|
return int64((date.Time.Month() + 2) / 3), false, nil
|
|
}
|
|
|
|
type secToTimeFunctionClass struct {
|
|
baseFunctionClass
|
|
}
|
|
|
|
func (c *secToTimeFunctionClass) getFunction(ctx context.Context, args []Expression) (builtinFunc, error) {
|
|
if err := c.verifyArgs(args); err != nil {
|
|
return nil, errors.Trace(err)
|
|
}
|
|
var retFlen, retFsp int
|
|
argType := args[0].GetType()
|
|
argEvalTp := fieldTp2EvalTp(argType)
|
|
if argEvalTp == tpString {
|
|
retFsp = types.UnspecifiedLength
|
|
} else {
|
|
retFsp = argType.Decimal
|
|
}
|
|
if retFsp > types.MaxFsp || retFsp == types.UnspecifiedLength {
|
|
retFsp = types.MaxFsp
|
|
} else if retFsp < types.MinFsp {
|
|
retFsp = types.MinFsp
|
|
}
|
|
retFlen = 10
|
|
if retFsp > 0 {
|
|
retFlen += 1 + retFsp
|
|
}
|
|
bf := newBaseBuiltinFuncWithTp(args, ctx, tpDuration, tpReal)
|
|
bf.tp.Flen, bf.tp.Decimal = retFlen, retFsp
|
|
sig := &builtinSecToTimeSig{baseDurationBuiltinFunc{bf}}
|
|
return sig.setSelf(sig), nil
|
|
}
|
|
|
|
type builtinSecToTimeSig struct {
|
|
baseDurationBuiltinFunc
|
|
}
|
|
|
|
// 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 []types.Datum) (types.Duration, bool, error) {
|
|
ctx := b.getCtx().GetSessionVars().StmtCtx
|
|
secondsFloat, isNull, err := b.args[0].EvalReal(row, ctx)
|
|
if isNull || err != nil {
|
|
return types.Duration{}, isNull, errors.Trace(err)
|
|
}
|
|
var (
|
|
hour int64
|
|
minute int64
|
|
second int64
|
|
demical float64
|
|
secondDemical float64
|
|
negative string
|
|
)
|
|
|
|
if secondsFloat < 0 {
|
|
negative = "-"
|
|
secondsFloat = math.Abs(secondsFloat)
|
|
}
|
|
seconds := int64(secondsFloat)
|
|
demical = secondsFloat - float64(seconds)
|
|
|
|
hour = seconds / 3600
|
|
if hour > 838 {
|
|
hour = 838
|
|
minute = 59
|
|
second = 59
|
|
} else {
|
|
minute = seconds % 3600 / 60
|
|
second = seconds % 60
|
|
}
|
|
secondDemical = float64(second) + demical
|
|
|
|
var dur types.Duration
|
|
dur, err = types.ParseDuration(fmt.Sprintf("%s%02d:%02d:%v", negative, hour, minute, secondDemical), b.tp.Decimal)
|
|
if err != nil {
|
|
return types.Duration{}, false, errors.Trace(err)
|
|
}
|
|
return dur, false, nil
|
|
}
|
|
|
|
type subTimeFunctionClass struct {
|
|
baseFunctionClass
|
|
}
|
|
|
|
func (c *subTimeFunctionClass) getFunction(ctx context.Context, args []Expression) (builtinFunc, error) {
|
|
if err := c.verifyArgs(args); err != nil {
|
|
return nil, errors.Trace(err)
|
|
}
|
|
sig := &builtinSubTimeSig{newBaseBuiltinFunc(args, ctx)}
|
|
return sig.setSelf(sig), nil
|
|
}
|
|
|
|
type builtinSubTimeSig struct {
|
|
baseBuiltinFunc
|
|
}
|
|
|
|
// eval evals a builtinSubTimeSig.
|
|
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_subtime
|
|
func (b *builtinSubTimeSig) eval(row []types.Datum) (d types.Datum, err error) {
|
|
args, err := b.evalArgs(row)
|
|
if err != nil {
|
|
return d, errors.Trace(err)
|
|
}
|
|
if args[0].IsNull() || args[1].IsNull() {
|
|
return
|
|
}
|
|
var arg1 types.Duration
|
|
switch args[1].Kind() {
|
|
case types.KindMysqlDuration:
|
|
arg1 = args[1].GetMysqlDuration()
|
|
default:
|
|
s, err := args[1].ToString()
|
|
if err != nil {
|
|
return d, errors.Trace(err)
|
|
}
|
|
if getFsp(s) == 0 {
|
|
arg1, err = types.ParseDuration(s, 0)
|
|
} else {
|
|
arg1, err = types.ParseDuration(s, types.MaxFsp)
|
|
}
|
|
if err != nil {
|
|
return d, errors.Trace(err)
|
|
}
|
|
|
|
}
|
|
switch args[0].Kind() {
|
|
case types.KindMysqlTime:
|
|
arg0 := args[0].GetMysqlTime()
|
|
arg1time, err := arg1.ConvertToTime(uint8(getFsp(arg1.String())))
|
|
if err != nil {
|
|
return d, errors.Trace(err)
|
|
}
|
|
tmpDuration := arg0.Sub(&arg1time)
|
|
result, err := tmpDuration.ConvertToTime(arg0.Type)
|
|
if err != nil {
|
|
return d, errors.Trace(err)
|
|
}
|
|
d.SetMysqlTime(result)
|
|
case types.KindMysqlDuration:
|
|
arg0 := args[0].GetMysqlDuration()
|
|
result, err := arg0.Sub(arg1)
|
|
if err != nil {
|
|
return d, errors.Trace(err)
|
|
}
|
|
d.SetMysqlDuration(result)
|
|
default:
|
|
ss, err := args[0].ToString()
|
|
if err != nil {
|
|
return d, errors.Trace(err)
|
|
}
|
|
if isDuration(ss) {
|
|
return strDurationSubDuration(ss, arg1)
|
|
}
|
|
return strDatetimeSubDuration(ss, arg1)
|
|
}
|
|
return
|
|
}
|
|
|
|
type timeFormatFunctionClass struct {
|
|
baseFunctionClass
|
|
}
|
|
|
|
func (c *timeFormatFunctionClass) getFunction(ctx context.Context, args []Expression) (builtinFunc, error) {
|
|
if err := c.verifyArgs(args); err != nil {
|
|
return nil, errors.Trace(err)
|
|
}
|
|
bf := newBaseBuiltinFuncWithTp(args, ctx, tpString, tpDuration, tpString)
|
|
// worst case: formatMask=%r%r%r...%r, each %r takes 11 characters
|
|
bf.tp.Flen = (args[1].GetType().Flen + 1) / 2 * 11
|
|
sig := &builtinTimeFormatSig{baseStringBuiltinFunc{bf}}
|
|
return sig.setSelf(sig), nil
|
|
}
|
|
|
|
type builtinTimeFormatSig struct {
|
|
baseStringBuiltinFunc
|
|
}
|
|
|
|
// 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 []types.Datum) (string, bool, error) {
|
|
dur, isNull, err := b.args[0].EvalDuration(row, b.ctx.GetSessionVars().StmtCtx)
|
|
// if err != nil, then dur is ZeroDuration, outputs 00:00:00 in this case which follows the behavior of mysql.
|
|
if err != nil {
|
|
log.Warnf("Expression.EvalDuration() in time_format() failed, due to %s", err.Error())
|
|
}
|
|
if isNull {
|
|
return "", isNull, errors.Trace(err)
|
|
}
|
|
formatMask, isNull, err := b.args[1].EvalString(row, b.ctx.GetSessionVars().StmtCtx)
|
|
if err != nil || isNull {
|
|
return "", isNull, errors.Trace(err)
|
|
}
|
|
res, err := b.formatTime(dur, formatMask, b.ctx)
|
|
return res, isNull, errors.Trace(err)
|
|
}
|
|
|
|
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_time-format
|
|
func (b *builtinTimeFormatSig) formatTime(t types.Duration, formatMask string, ctx context.Context) (res string, err error) {
|
|
t2 := types.Time{
|
|
Time: types.FromDate(0, 0, 0, t.Hour(), t.Minute(), t.Second(), t.MicroSecond()),
|
|
Type: mysql.TypeDate, Fsp: 0}
|
|
|
|
str, err := t2.DateFormat(formatMask)
|
|
return str, errors.Trace(err)
|
|
}
|
|
|
|
type timeToSecFunctionClass struct {
|
|
baseFunctionClass
|
|
}
|
|
|
|
func (c *timeToSecFunctionClass) getFunction(ctx context.Context, args []Expression) (builtinFunc, error) {
|
|
if err := c.verifyArgs(args); err != nil {
|
|
return nil, errors.Trace(err)
|
|
}
|
|
bf := newBaseBuiltinFuncWithTp(args, ctx, tpInt, tpDuration)
|
|
bf.tp.Flen = 10
|
|
sig := &builtinTimeToSecSig{baseIntBuiltinFunc{bf}}
|
|
return sig.setSelf(sig), nil
|
|
}
|
|
|
|
type builtinTimeToSecSig struct {
|
|
baseIntBuiltinFunc
|
|
}
|
|
|
|
// 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 []types.Datum) (int64, bool, error) {
|
|
ctx := b.getCtx().GetSessionVars().StmtCtx
|
|
duration, isNull, err := b.args[0].EvalDuration(row, ctx)
|
|
if isNull || err != nil {
|
|
return 0, isNull, errors.Trace(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 context.Context, args []Expression) (builtinFunc, error) {
|
|
if err := c.verifyArgs(args); err != nil {
|
|
return nil, errors.Trace(err)
|
|
}
|
|
bf := newBaseBuiltinFuncWithTp(args, ctx, tpString, tpString, tpInt, tpDatetime)
|
|
bf.tp = &types.FieldType{Tp: mysql.TypeString, Flen: mysql.MaxDatetimeWidthNoFsp, Decimal: types.UnspecifiedLength}
|
|
sig := &builtinTimestampAddSig{baseStringBuiltinFunc{bf}}
|
|
return sig.setSelf(sig), nil
|
|
|
|
}
|
|
|
|
type builtinTimestampAddSig struct {
|
|
baseStringBuiltinFunc
|
|
}
|
|
|
|
// 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 []types.Datum) (string, bool, error) {
|
|
ctx := b.getCtx().GetSessionVars().StmtCtx
|
|
unit, isNull, err := b.args[0].EvalString(row, ctx)
|
|
if isNull || err != nil {
|
|
return "", isNull, errors.Trace(err)
|
|
}
|
|
v, isNull, err := b.args[1].EvalInt(row, ctx)
|
|
if isNull || err != nil {
|
|
return "", isNull, errors.Trace(err)
|
|
}
|
|
arg, isNull, err := b.args[2].EvalTime(row, ctx)
|
|
if isNull || err != nil {
|
|
return "", isNull, errors.Trace(err)
|
|
}
|
|
tm1, err := arg.Time.GoTime(time.Local)
|
|
if err != nil {
|
|
return "", isNull, errors.Trace(err)
|
|
}
|
|
var tb time.Time
|
|
fsp := types.DefaultFsp
|
|
switch unit {
|
|
case "MICROSECOND":
|
|
tb = tm1.Add(time.Duration(v) * time.Microsecond)
|
|
fsp = types.MaxFsp
|
|
case "SECOND":
|
|
tb = tm1.Add(time.Duration(v) * time.Second)
|
|
case "MINUTE":
|
|
tb = tm1.Add(time.Duration(v) * time.Minute)
|
|
case "HOUR":
|
|
tb = tm1.Add(time.Duration(v) * time.Hour)
|
|
case "DAY":
|
|
tb = tm1.AddDate(0, 0, int(v))
|
|
case "WEEK":
|
|
tb = tm1.AddDate(0, 0, 7*int(v))
|
|
case "MONTH":
|
|
tb = tm1.AddDate(0, int(v), 0)
|
|
case "QUARTER":
|
|
tb = tm1.AddDate(0, 3*int(v), 0)
|
|
case "YEAR":
|
|
tb = tm1.AddDate(int(v), 0, 0)
|
|
default:
|
|
return "", true, errors.Trace(types.ErrInvalidTimeFormat)
|
|
}
|
|
r := types.Time{Time: types.FromGoTime(tb), Type: mysql.TypeDatetime, Fsp: fsp}
|
|
if err = r.Check(); err != nil {
|
|
return "", true, errors.Trace(handleInvalidTimeError(b.ctx, err))
|
|
}
|
|
return r.String(), false, nil
|
|
}
|
|
|
|
type toDaysFunctionClass struct {
|
|
baseFunctionClass
|
|
}
|
|
|
|
func (c *toDaysFunctionClass) getFunction(ctx context.Context, args []Expression) (builtinFunc, error) {
|
|
if err := c.verifyArgs(args); err != nil {
|
|
return nil, errors.Trace(err)
|
|
}
|
|
bf := newBaseBuiltinFuncWithTp(args, ctx, tpInt, tpDatetime)
|
|
sig := &builtinToDaysSig{baseIntBuiltinFunc{bf}}
|
|
return sig.setSelf(sig), nil
|
|
}
|
|
|
|
type builtinToDaysSig struct {
|
|
baseIntBuiltinFunc
|
|
}
|
|
|
|
// 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 []types.Datum) (int64, bool, error) {
|
|
sc := b.ctx.GetSessionVars().StmtCtx
|
|
arg, isNull, err := b.args[0].EvalTime(row, sc)
|
|
|
|
if isNull || err != nil {
|
|
return 0, true, errors.Trace(handleInvalidTimeError(b.ctx, err))
|
|
}
|
|
ret := types.TimestampDiff("DAY", types.ZeroDate, arg)
|
|
if ret == 0 {
|
|
return 0, true, errors.Trace(handleInvalidTimeError(b.ctx, types.ErrInvalidTimeFormat))
|
|
}
|
|
return ret, false, nil
|
|
}
|
|
|
|
type toSecondsFunctionClass struct {
|
|
baseFunctionClass
|
|
}
|
|
|
|
func (c *toSecondsFunctionClass) getFunction(ctx context.Context, args []Expression) (builtinFunc, error) {
|
|
if err := c.verifyArgs(args); err != nil {
|
|
return nil, errors.Trace(err)
|
|
}
|
|
bf := newBaseBuiltinFuncWithTp(args, ctx, tpInt, tpDatetime)
|
|
sig := &builtinToSecondsSig{baseIntBuiltinFunc{bf}}
|
|
return sig.setSelf(sig), nil
|
|
}
|
|
|
|
type builtinToSecondsSig struct {
|
|
baseIntBuiltinFunc
|
|
}
|
|
|
|
// 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 []types.Datum) (int64, bool, error) {
|
|
arg, isNull, err := b.args[0].EvalTime(row, b.getCtx().GetSessionVars().StmtCtx)
|
|
if isNull || err != nil {
|
|
return 0, true, errors.Trace(handleInvalidTimeError(b.ctx, err))
|
|
}
|
|
ret := types.TimestampDiff("SECOND", types.ZeroDate, arg)
|
|
if ret == 0 {
|
|
return 0, true, errors.Trace(handleInvalidTimeError(b.ctx, types.ErrInvalidTimeFormat))
|
|
}
|
|
return ret, false, nil
|
|
}
|
|
|
|
type utcTimeFunctionClass struct {
|
|
baseFunctionClass
|
|
}
|
|
|
|
func (c *utcTimeFunctionClass) getFlenAndDecimal4UTCTime(sc *variable.StatementContext, args []Expression) (flen, decimal int) {
|
|
if len(args) == 0 {
|
|
flen, decimal = 8, 0
|
|
return
|
|
}
|
|
if constant, ok := args[0].(*Constant); ok {
|
|
fsp, isNull, err := constant.EvalInt(nil, sc)
|
|
if isNull || err != nil || fsp > int64(types.MaxFsp) {
|
|
decimal = types.MaxFsp
|
|
} else if fsp < int64(types.MinFsp) {
|
|
decimal = types.MinFsp
|
|
} else {
|
|
decimal = int(fsp)
|
|
}
|
|
}
|
|
if decimal > 0 {
|
|
flen = 8 + 1 + decimal
|
|
} else {
|
|
flen = 8
|
|
}
|
|
return flen, decimal
|
|
}
|
|
|
|
func (c *utcTimeFunctionClass) getFunction(ctx context.Context, args []Expression) (builtinFunc, error) {
|
|
if err := c.verifyArgs(args); err != nil {
|
|
return nil, errors.Trace(err)
|
|
}
|
|
argTps := make([]evalTp, 0, 1)
|
|
if len(args) == 1 {
|
|
argTps = append(argTps, tpInt)
|
|
}
|
|
bf := newBaseBuiltinFuncWithTp(args, ctx, tpDuration, argTps...)
|
|
bf.tp.Flen, bf.tp.Decimal = c.getFlenAndDecimal4UTCTime(bf.ctx.GetSessionVars().StmtCtx, args)
|
|
|
|
var sig builtinFunc
|
|
if len(args) == 1 {
|
|
sig = &builtinUTCTimeWithArgSig{baseDurationBuiltinFunc{bf}}
|
|
} else {
|
|
sig = &builtinUTCTimeWithoutArgSig{baseDurationBuiltinFunc{bf}}
|
|
}
|
|
return sig.setSelf(sig), nil
|
|
}
|
|
|
|
type builtinUTCTimeWithoutArgSig struct {
|
|
baseDurationBuiltinFunc
|
|
}
|
|
|
|
// 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 []types.Datum) (types.Duration, bool, error) {
|
|
// the types.ParseDuration here would never fail, so the err returned can be ignored.
|
|
v, _ := types.ParseDuration(time.Now().UTC().Format(types.TimeFormat), 0)
|
|
return v, false, nil
|
|
}
|
|
|
|
type builtinUTCTimeWithArgSig struct {
|
|
baseDurationBuiltinFunc
|
|
}
|
|
|
|
// 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 []types.Datum) (types.Duration, bool, error) {
|
|
fsp, isNull, err := b.args[0].EvalInt(row, b.ctx.GetSessionVars().StmtCtx)
|
|
if isNull || err != nil {
|
|
return types.Duration{}, isNull, errors.Trace(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)
|
|
}
|
|
// the types.ParseDuration here would never fail, so the err returned can be ignored.
|
|
v, _ := types.ParseDuration(time.Now().UTC().Format(types.TimeFSPFormat), int(fsp))
|
|
return v, false, nil
|
|
}
|
|
|
|
type lastDayFunctionClass struct {
|
|
baseFunctionClass
|
|
}
|
|
|
|
func (c *lastDayFunctionClass) getFunction(ctx context.Context, args []Expression) (builtinFunc, error) {
|
|
if err := c.verifyArgs(args); err != nil {
|
|
return nil, errors.Trace(err)
|
|
}
|
|
bf := newBaseBuiltinFuncWithTp(args, ctx, tpDatetime, tpDatetime)
|
|
bf.tp.Tp, bf.tp.Flen, bf.tp.Decimal = mysql.TypeDate, mysql.MaxDateWidth, types.DefaultFsp
|
|
sig := &builtinLastDaySig{baseTimeBuiltinFunc{bf}}
|
|
return sig.setSelf(sig), nil
|
|
}
|
|
|
|
type builtinLastDaySig struct {
|
|
baseTimeBuiltinFunc
|
|
}
|
|
|
|
// 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 []types.Datum) (types.Time, bool, error) {
|
|
sc := b.ctx.GetSessionVars().StmtCtx
|
|
arg, isNull, err := b.args[0].EvalTime(row, sc)
|
|
if isNull || err != nil {
|
|
return types.Time{}, true, errors.Trace(handleInvalidTimeError(b.ctx, err))
|
|
}
|
|
tm := arg.Time
|
|
year, month, day := tm.Year(), tm.Month(), 30
|
|
if year == 0 && month == 0 && tm.Day() == 0 {
|
|
return types.Time{}, true, errors.Trace(handleInvalidTimeError(b.ctx, types.ErrInvalidTimeFormat))
|
|
}
|
|
if month == 1 || month == 3 || month == 5 ||
|
|
month == 7 || month == 8 || month == 10 || month == 12 {
|
|
day = 31
|
|
} else if month == 2 {
|
|
day = 28
|
|
if tm.IsLeapYear() {
|
|
day = 29
|
|
}
|
|
}
|
|
ret := types.Time{
|
|
Time: types.FromDate(year, month, day, 0, 0, 0, 0),
|
|
Type: mysql.TypeDate,
|
|
Fsp: types.DefaultFsp,
|
|
}
|
|
return ret, false, nil
|
|
}
|