// 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 ( "bytes" "encoding/base64" "encoding/hex" "fmt" "math" "strconv" "strings" "unicode/utf8" "github.com/pingcap/errors" "github.com/pingcap/parser/ast" "github.com/pingcap/parser/charset" "github.com/pingcap/parser/mysql" "github.com/pingcap/tidb/sessionctx" "github.com/pingcap/tidb/sessionctx/variable" "github.com/pingcap/tidb/types" "github.com/pingcap/tidb/util/chunk" "github.com/pingcap/tidb/util/collate" "github.com/pingcap/tidb/util/hack" "github.com/pingcap/tidb/util/logutil" "github.com/pingcap/tipb/go-tipb" "go.uber.org/zap" "golang.org/x/text/transform" ) var ( _ functionClass = &lengthFunctionClass{} _ functionClass = &asciiFunctionClass{} _ functionClass = &concatFunctionClass{} _ functionClass = &concatWSFunctionClass{} _ functionClass = &leftFunctionClass{} _ functionClass = &repeatFunctionClass{} _ functionClass = &lowerFunctionClass{} _ functionClass = &reverseFunctionClass{} _ functionClass = &spaceFunctionClass{} _ functionClass = &upperFunctionClass{} _ functionClass = &strcmpFunctionClass{} _ functionClass = &replaceFunctionClass{} _ functionClass = &convertFunctionClass{} _ functionClass = &substringFunctionClass{} _ functionClass = &substringIndexFunctionClass{} _ functionClass = &locateFunctionClass{} _ functionClass = &hexFunctionClass{} _ functionClass = &unhexFunctionClass{} _ functionClass = &trimFunctionClass{} _ functionClass = &lTrimFunctionClass{} _ functionClass = &rTrimFunctionClass{} _ functionClass = &lpadFunctionClass{} _ functionClass = &rpadFunctionClass{} _ functionClass = &bitLengthFunctionClass{} _ functionClass = &charFunctionClass{} _ functionClass = &charLengthFunctionClass{} _ functionClass = &findInSetFunctionClass{} _ functionClass = &fieldFunctionClass{} _ functionClass = &makeSetFunctionClass{} _ functionClass = &octFunctionClass{} _ functionClass = &ordFunctionClass{} _ functionClass = "eFunctionClass{} _ functionClass = &binFunctionClass{} _ functionClass = &eltFunctionClass{} _ functionClass = &exportSetFunctionClass{} _ functionClass = &formatFunctionClass{} _ functionClass = &fromBase64FunctionClass{} _ functionClass = &toBase64FunctionClass{} _ functionClass = &insertFunctionClass{} _ functionClass = &instrFunctionClass{} _ functionClass = &loadFileFunctionClass{} _ functionClass = &weightStringFunctionClass{} ) var ( _ builtinFunc = &builtinLengthSig{} _ builtinFunc = &builtinASCIISig{} _ builtinFunc = &builtinConcatSig{} _ builtinFunc = &builtinConcatWSSig{} _ builtinFunc = &builtinLeftSig{} _ builtinFunc = &builtinLeftUTF8Sig{} _ builtinFunc = &builtinRightSig{} _ builtinFunc = &builtinRightUTF8Sig{} _ builtinFunc = &builtinRepeatSig{} _ builtinFunc = &builtinLowerSig{} _ builtinFunc = &builtinReverseUTF8Sig{} _ builtinFunc = &builtinReverseSig{} _ builtinFunc = &builtinSpaceSig{} _ builtinFunc = &builtinUpperUTF8Sig{} _ builtinFunc = &builtinUpperSig{} _ builtinFunc = &builtinStrcmpSig{} _ builtinFunc = &builtinReplaceSig{} _ builtinFunc = &builtinConvertSig{} _ builtinFunc = &builtinSubstring2ArgsSig{} _ builtinFunc = &builtinSubstring3ArgsSig{} _ builtinFunc = &builtinSubstring2ArgsUTF8Sig{} _ builtinFunc = &builtinSubstring3ArgsUTF8Sig{} _ builtinFunc = &builtinSubstringIndexSig{} _ builtinFunc = &builtinLocate2ArgsUTF8Sig{} _ builtinFunc = &builtinLocate3ArgsUTF8Sig{} _ builtinFunc = &builtinLocate2ArgsSig{} _ builtinFunc = &builtinLocate3ArgsSig{} _ builtinFunc = &builtinHexStrArgSig{} _ builtinFunc = &builtinHexIntArgSig{} _ builtinFunc = &builtinUnHexSig{} _ builtinFunc = &builtinTrim1ArgSig{} _ builtinFunc = &builtinTrim2ArgsSig{} _ builtinFunc = &builtinTrim3ArgsSig{} _ builtinFunc = &builtinLTrimSig{} _ builtinFunc = &builtinRTrimSig{} _ builtinFunc = &builtinLpadUTF8Sig{} _ builtinFunc = &builtinLpadSig{} _ builtinFunc = &builtinRpadUTF8Sig{} _ builtinFunc = &builtinRpadSig{} _ builtinFunc = &builtinBitLengthSig{} _ builtinFunc = &builtinCharSig{} _ builtinFunc = &builtinCharLengthUTF8Sig{} _ builtinFunc = &builtinFindInSetSig{} _ builtinFunc = &builtinMakeSetSig{} _ builtinFunc = &builtinOctIntSig{} _ builtinFunc = &builtinOctStringSig{} _ builtinFunc = &builtinOrdSig{} _ builtinFunc = &builtinQuoteSig{} _ builtinFunc = &builtinBinSig{} _ builtinFunc = &builtinEltSig{} _ builtinFunc = &builtinExportSet3ArgSig{} _ builtinFunc = &builtinExportSet4ArgSig{} _ builtinFunc = &builtinExportSet5ArgSig{} _ builtinFunc = &builtinFormatWithLocaleSig{} _ builtinFunc = &builtinFormatSig{} _ builtinFunc = &builtinFromBase64Sig{} _ builtinFunc = &builtinToBase64Sig{} _ builtinFunc = &builtinInsertSig{} _ builtinFunc = &builtinInsertUTF8Sig{} _ builtinFunc = &builtinInstrUTF8Sig{} _ builtinFunc = &builtinInstrSig{} _ builtinFunc = &builtinFieldRealSig{} _ builtinFunc = &builtinFieldIntSig{} _ builtinFunc = &builtinFieldStringSig{} _ builtinFunc = &builtinWeightStringSig{} ) func reverseBytes(origin []byte) []byte { for i, length := 0, len(origin); i < length/2; i++ { origin[i], origin[length-i-1] = origin[length-i-1], origin[i] } return origin } func reverseRunes(origin []rune) []rune { for i, length := 0, len(origin); i < length/2; i++ { origin[i], origin[length-i-1] = origin[length-i-1], origin[i] } return origin } // SetBinFlagOrBinStr sets resTp to binary string if argTp is a binary string, // if not, sets the binary flag of resTp to true if argTp has binary flag. func SetBinFlagOrBinStr(argTp *types.FieldType, resTp *types.FieldType) { if types.IsBinaryStr(argTp) { types.SetBinChsClnFlag(resTp) } else if mysql.HasBinaryFlag(argTp.Flag) || !types.IsNonBinaryStr(argTp) { resTp.Flag |= mysql.BinaryFlag } } type lengthFunctionClass struct { baseFunctionClass } func (c *lengthFunctionClass) getFunction(ctx sessionctx.Context, args []Expression) (builtinFunc, error) { if err := c.verifyArgs(args); err != nil { return nil, err } bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETInt, types.ETString) if err != nil { return nil, err } bf.tp.Flen = 10 sig := &builtinLengthSig{bf} sig.setPbCode(tipb.ScalarFuncSig_Length) return sig, nil } type builtinLengthSig struct { baseBuiltinFunc } func (b *builtinLengthSig) Clone() builtinFunc { newSig := &builtinLengthSig{} newSig.cloneFrom(&b.baseBuiltinFunc) return newSig } // evalInt evaluates a builtinLengthSig. // See https://dev.mysql.com/doc/refman/5.7/en/string-functions.html func (b *builtinLengthSig) evalInt(row chunk.Row) (int64, bool, error) { val, isNull, err := b.args[0].EvalString(b.ctx, row) if isNull || err != nil { return 0, isNull, err } return int64(len([]byte(val))), false, nil } type asciiFunctionClass struct { baseFunctionClass } func (c *asciiFunctionClass) getFunction(ctx sessionctx.Context, args []Expression) (builtinFunc, error) { if err := c.verifyArgs(args); err != nil { return nil, err } bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETInt, types.ETString) if err != nil { return nil, err } bf.tp.Flen = 3 sig := &builtinASCIISig{bf} sig.setPbCode(tipb.ScalarFuncSig_ASCII) return sig, nil } type builtinASCIISig struct { baseBuiltinFunc } func (b *builtinASCIISig) Clone() builtinFunc { newSig := &builtinASCIISig{} newSig.cloneFrom(&b.baseBuiltinFunc) return newSig } // evalInt evals a builtinASCIISig. // See https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_ascii func (b *builtinASCIISig) evalInt(row chunk.Row) (int64, bool, error) { val, isNull, err := b.args[0].EvalString(b.ctx, row) if isNull || err != nil { return 0, isNull, err } if len(val) == 0 { return 0, false, nil } return int64(val[0]), false, nil } type concatFunctionClass struct { baseFunctionClass } func (c *concatFunctionClass) getFunction(ctx sessionctx.Context, args []Expression) (builtinFunc, error) { if err := c.verifyArgs(args); err != nil { return nil, err } argTps := make([]types.EvalType, 0, len(args)) for i := 0; i < len(args); i++ { argTps = append(argTps, types.ETString) } bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETString, argTps...) if err != nil { return nil, err } bf.tp.Flen = 0 for i := range args { argType := args[i].GetType() SetBinFlagOrBinStr(argType, bf.tp) if argType.Flen < 0 { bf.tp.Flen = mysql.MaxBlobWidth logutil.BgLogger().Warn("unexpected `Flen` value(-1) in CONCAT's args", zap.Int("arg's index", i)) } bf.tp.Flen += argType.Flen } if bf.tp.Flen >= mysql.MaxBlobWidth { bf.tp.Flen = mysql.MaxBlobWidth } valStr, _ := ctx.GetSessionVars().GetSystemVar(variable.MaxAllowedPacket) maxAllowedPacket, err := strconv.ParseUint(valStr, 10, 64) if err != nil { return nil, errors.Trace(err) } sig := &builtinConcatSig{bf, maxAllowedPacket} sig.setPbCode(tipb.ScalarFuncSig_Concat) return sig, nil } type builtinConcatSig struct { baseBuiltinFunc maxAllowedPacket uint64 } func (b *builtinConcatSig) Clone() builtinFunc { newSig := &builtinConcatSig{} newSig.cloneFrom(&b.baseBuiltinFunc) newSig.maxAllowedPacket = b.maxAllowedPacket return newSig } // evalString evals a builtinConcatSig // See https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_concat func (b *builtinConcatSig) evalString(row chunk.Row) (d string, isNull bool, err error) { var s []byte for _, a := range b.getArgs() { d, isNull, err = a.EvalString(b.ctx, row) if isNull || err != nil { return d, isNull, err } if uint64(len(s)+len(d)) > b.maxAllowedPacket { b.ctx.GetSessionVars().StmtCtx.AppendWarning(errWarnAllowedPacketOverflowed.GenWithStackByArgs("concat", b.maxAllowedPacket)) return "", true, nil } s = append(s, []byte(d)...) } return string(s), false, nil } type concatWSFunctionClass struct { baseFunctionClass } func (c *concatWSFunctionClass) getFunction(ctx sessionctx.Context, args []Expression) (builtinFunc, error) { if err := c.verifyArgs(args); err != nil { return nil, err } argTps := make([]types.EvalType, 0, len(args)) for i := 0; i < len(args); i++ { argTps = append(argTps, types.ETString) } bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETString, argTps...) if err != nil { return nil, err } bf.tp.Flen = 0 for i := range args { argType := args[i].GetType() SetBinFlagOrBinStr(argType, bf.tp) // skip separator param if i != 0 { if argType.Flen < 0 { bf.tp.Flen = mysql.MaxBlobWidth logutil.BgLogger().Warn("unexpected `Flen` value(-1) in CONCAT_WS's args", zap.Int("arg's index", i)) } bf.tp.Flen += argType.Flen } } // add separator argsLen := len(args) - 1 bf.tp.Flen += argsLen - 1 if bf.tp.Flen >= mysql.MaxBlobWidth { bf.tp.Flen = mysql.MaxBlobWidth } valStr, _ := ctx.GetSessionVars().GetSystemVar(variable.MaxAllowedPacket) maxAllowedPacket, err := strconv.ParseUint(valStr, 10, 64) if err != nil { return nil, errors.Trace(err) } sig := &builtinConcatWSSig{bf, maxAllowedPacket} sig.setPbCode(tipb.ScalarFuncSig_ConcatWS) return sig, nil } type builtinConcatWSSig struct { baseBuiltinFunc maxAllowedPacket uint64 } func (b *builtinConcatWSSig) Clone() builtinFunc { newSig := &builtinConcatWSSig{} newSig.cloneFrom(&b.baseBuiltinFunc) newSig.maxAllowedPacket = b.maxAllowedPacket return newSig } // evalString evals a CONCAT_WS(separator,str1,str2,...). // See https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_concat-ws func (b *builtinConcatWSSig) evalString(row chunk.Row) (string, bool, error) { args := b.getArgs() strs := make([]string, 0, len(args)) var sep string var targetLength int N := len(args) if N > 0 { val, isNull, err := args[0].EvalString(b.ctx, row) if err != nil || isNull { // If the separator is NULL, the result is NULL. return val, isNull, err } sep = val } for i := 1; i < N; i++ { val, isNull, err := args[i].EvalString(b.ctx, row) if err != nil { return val, isNull, err } if isNull { // CONCAT_WS() does not skip empty strings. However, // it does skip any NULL values after the separator argument. continue } targetLength += len(val) if i > 1 { targetLength += len(sep) } if uint64(targetLength) > b.maxAllowedPacket { b.ctx.GetSessionVars().StmtCtx.AppendWarning(errWarnAllowedPacketOverflowed.GenWithStackByArgs("concat_ws", b.maxAllowedPacket)) return "", true, nil } strs = append(strs, val) } str := strings.Join(strs, sep) // todo check whether the length of result is larger than Flen //if b.tp.Flen != types.UnspecifiedLength && len(str) > b.tp.Flen { // return "", true, nil //} return str, false, nil } type leftFunctionClass struct { baseFunctionClass } func (c *leftFunctionClass) getFunction(ctx sessionctx.Context, args []Expression) (builtinFunc, error) { if err := c.verifyArgs(args); err != nil { return nil, err } bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETString, types.ETString, types.ETInt) if err != nil { return nil, err } argType := args[0].GetType() bf.tp.Flen = argType.Flen SetBinFlagOrBinStr(argType, bf.tp) if types.IsBinaryStr(argType) { sig := &builtinLeftSig{bf} sig.setPbCode(tipb.ScalarFuncSig_Left) return sig, nil } sig := &builtinLeftUTF8Sig{bf} sig.setPbCode(tipb.ScalarFuncSig_LeftUTF8) return sig, nil } type builtinLeftSig struct { baseBuiltinFunc } func (b *builtinLeftSig) Clone() builtinFunc { newSig := &builtinLeftSig{} newSig.cloneFrom(&b.baseBuiltinFunc) return newSig } // evalString evals LEFT(str,len). // See https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_left func (b *builtinLeftSig) evalString(row chunk.Row) (string, bool, error) { str, isNull, err := b.args[0].EvalString(b.ctx, row) if isNull || err != nil { return "", true, err } left, isNull, err := b.args[1].EvalInt(b.ctx, row) if isNull || err != nil { return "", true, err } leftLength := int(left) if strLength := len(str); leftLength > strLength { leftLength = strLength } else if leftLength < 0 { leftLength = 0 } return str[:leftLength], false, nil } type builtinLeftUTF8Sig struct { baseBuiltinFunc } func (b *builtinLeftUTF8Sig) Clone() builtinFunc { newSig := &builtinLeftUTF8Sig{} newSig.cloneFrom(&b.baseBuiltinFunc) return newSig } // evalString evals LEFT(str,len). // See https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_left func (b *builtinLeftUTF8Sig) evalString(row chunk.Row) (string, bool, error) { str, isNull, err := b.args[0].EvalString(b.ctx, row) if isNull || err != nil { return "", true, err } left, isNull, err := b.args[1].EvalInt(b.ctx, row) if isNull || err != nil { return "", true, err } runes, leftLength := []rune(str), int(left) if runeLength := len(runes); leftLength > runeLength { leftLength = runeLength } else if leftLength < 0 { leftLength = 0 } return string(runes[:leftLength]), false, nil } type rightFunctionClass struct { baseFunctionClass } func (c *rightFunctionClass) getFunction(ctx sessionctx.Context, args []Expression) (builtinFunc, error) { if err := c.verifyArgs(args); err != nil { return nil, err } bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETString, types.ETString, types.ETInt) if err != nil { return nil, err } argType := args[0].GetType() bf.tp.Flen = argType.Flen SetBinFlagOrBinStr(argType, bf.tp) if types.IsBinaryStr(argType) { sig := &builtinRightSig{bf} sig.setPbCode(tipb.ScalarFuncSig_Right) return sig, nil } sig := &builtinRightUTF8Sig{bf} sig.setPbCode(tipb.ScalarFuncSig_RightUTF8) return sig, nil } type builtinRightSig struct { baseBuiltinFunc } func (b *builtinRightSig) Clone() builtinFunc { newSig := &builtinRightSig{} newSig.cloneFrom(&b.baseBuiltinFunc) return newSig } // evalString evals RIGHT(str,len). // See https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_right func (b *builtinRightSig) evalString(row chunk.Row) (string, bool, error) { str, isNull, err := b.args[0].EvalString(b.ctx, row) if isNull || err != nil { return "", true, err } right, isNull, err := b.args[1].EvalInt(b.ctx, row) if isNull || err != nil { return "", true, err } strLength, rightLength := len(str), int(right) if rightLength > strLength { rightLength = strLength } else if rightLength < 0 { rightLength = 0 } return str[strLength-rightLength:], false, nil } type builtinRightUTF8Sig struct { baseBuiltinFunc } func (b *builtinRightUTF8Sig) Clone() builtinFunc { newSig := &builtinRightUTF8Sig{} newSig.cloneFrom(&b.baseBuiltinFunc) return newSig } // evalString evals RIGHT(str,len). // See https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_right func (b *builtinRightUTF8Sig) evalString(row chunk.Row) (string, bool, error) { str, isNull, err := b.args[0].EvalString(b.ctx, row) if isNull || err != nil { return "", true, err } right, isNull, err := b.args[1].EvalInt(b.ctx, row) if isNull || err != nil { return "", true, err } runes := []rune(str) strLength, rightLength := len(runes), int(right) if rightLength > strLength { rightLength = strLength } else if rightLength < 0 { rightLength = 0 } return string(runes[strLength-rightLength:]), false, nil } type repeatFunctionClass struct { baseFunctionClass } func (c *repeatFunctionClass) getFunction(ctx sessionctx.Context, args []Expression) (builtinFunc, error) { if err := c.verifyArgs(args); err != nil { return nil, err } bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETString, types.ETString, types.ETInt) if err != nil { return nil, err } bf.tp.Flen = mysql.MaxBlobWidth SetBinFlagOrBinStr(args[0].GetType(), bf.tp) valStr, _ := ctx.GetSessionVars().GetSystemVar(variable.MaxAllowedPacket) maxAllowedPacket, err := strconv.ParseUint(valStr, 10, 64) if err != nil { return nil, errors.Trace(err) } sig := &builtinRepeatSig{bf, maxAllowedPacket} return sig, nil } type builtinRepeatSig struct { baseBuiltinFunc maxAllowedPacket uint64 } func (b *builtinRepeatSig) Clone() builtinFunc { newSig := &builtinRepeatSig{} newSig.cloneFrom(&b.baseBuiltinFunc) newSig.maxAllowedPacket = b.maxAllowedPacket return newSig } // evalString evals a builtinRepeatSig. // See https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_repeat func (b *builtinRepeatSig) evalString(row chunk.Row) (d string, isNull bool, err error) { str, isNull, err := b.args[0].EvalString(b.ctx, row) if isNull || err != nil { return "", isNull, err } byteLength := len(str) num, isNull, err := b.args[1].EvalInt(b.ctx, row) if isNull || err != nil { return "", isNull, err } if num < 1 { return "", false, nil } if num > math.MaxInt32 { num = math.MaxInt32 } if uint64(byteLength)*uint64(num) > b.maxAllowedPacket { b.ctx.GetSessionVars().StmtCtx.AppendWarning(errWarnAllowedPacketOverflowed.GenWithStackByArgs("repeat", b.maxAllowedPacket)) return "", true, nil } if int64(byteLength) > int64(b.tp.Flen)/num { return "", true, nil } return strings.Repeat(str, int(num)), false, nil } type lowerFunctionClass struct { baseFunctionClass } func (c *lowerFunctionClass) getFunction(ctx sessionctx.Context, args []Expression) (builtinFunc, error) { if err := c.verifyArgs(args); err != nil { return nil, err } bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETString, types.ETString) if err != nil { return nil, err } argTp := args[0].GetType() bf.tp.Flen = argTp.Flen SetBinFlagOrBinStr(argTp, bf.tp) sig := &builtinLowerSig{bf} sig.setPbCode(tipb.ScalarFuncSig_Lower) return sig, nil } type builtinLowerSig struct { baseBuiltinFunc } func (b *builtinLowerSig) Clone() builtinFunc { newSig := &builtinLowerSig{} newSig.cloneFrom(&b.baseBuiltinFunc) return newSig } // evalString evals a builtinLowerSig. // See https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_lower func (b *builtinLowerSig) evalString(row chunk.Row) (d string, isNull bool, err error) { d, isNull, err = b.args[0].EvalString(b.ctx, row) if isNull || err != nil { return d, isNull, err } if types.IsBinaryStr(b.args[0].GetType()) { return d, false, nil } return strings.ToLower(d), false, nil } type reverseFunctionClass struct { baseFunctionClass } func (c *reverseFunctionClass) getFunction(ctx sessionctx.Context, args []Expression) (builtinFunc, error) { if err := c.verifyArgs(args); err != nil { return nil, err } bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETString, types.ETString) if err != nil { return nil, err } retTp := *args[0].GetType() retTp.Tp = mysql.TypeVarString retTp.Decimal = types.UnspecifiedLength bf.tp = &retTp var sig builtinFunc if types.IsBinaryStr(bf.tp) { sig = &builtinReverseSig{bf} sig.setPbCode(tipb.ScalarFuncSig_Reverse) } else { sig = &builtinReverseUTF8Sig{bf} sig.setPbCode(tipb.ScalarFuncSig_ReverseUTF8) } return sig, nil } type builtinReverseSig struct { baseBuiltinFunc } func (b *builtinReverseSig) Clone() builtinFunc { newSig := &builtinReverseSig{} newSig.cloneFrom(&b.baseBuiltinFunc) return newSig } // evalString evals a REVERSE(str). // See https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_reverse func (b *builtinReverseSig) evalString(row chunk.Row) (string, bool, error) { str, isNull, err := b.args[0].EvalString(b.ctx, row) if isNull || err != nil { return "", true, err } reversed := reverseBytes([]byte(str)) return string(reversed), false, nil } type builtinReverseUTF8Sig struct { baseBuiltinFunc } func (b *builtinReverseUTF8Sig) Clone() builtinFunc { newSig := &builtinReverseUTF8Sig{} newSig.cloneFrom(&b.baseBuiltinFunc) return newSig } // evalString evals a REVERSE(str). // See https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_reverse func (b *builtinReverseUTF8Sig) evalString(row chunk.Row) (string, bool, error) { str, isNull, err := b.args[0].EvalString(b.ctx, row) if isNull || err != nil { return "", true, err } reversed := reverseRunes([]rune(str)) return string(reversed), false, nil } type spaceFunctionClass struct { baseFunctionClass } func (c *spaceFunctionClass) getFunction(ctx sessionctx.Context, args []Expression) (builtinFunc, error) { if err := c.verifyArgs(args); err != nil { return nil, err } bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETString, types.ETInt) if err != nil { return nil, err } bf.tp.Charset, bf.tp.Collate = ctx.GetSessionVars().GetCharsetInfo() bf.tp.Flen = mysql.MaxBlobWidth valStr, _ := ctx.GetSessionVars().GetSystemVar(variable.MaxAllowedPacket) maxAllowedPacket, err := strconv.ParseUint(valStr, 10, 64) if err != nil { return nil, errors.Trace(err) } sig := &builtinSpaceSig{bf, maxAllowedPacket} sig.setPbCode(tipb.ScalarFuncSig_Space) return sig, nil } type builtinSpaceSig struct { baseBuiltinFunc maxAllowedPacket uint64 } func (b *builtinSpaceSig) Clone() builtinFunc { newSig := &builtinSpaceSig{} newSig.cloneFrom(&b.baseBuiltinFunc) newSig.maxAllowedPacket = b.maxAllowedPacket return newSig } // evalString evals a builtinSpaceSig. // See https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_space func (b *builtinSpaceSig) evalString(row chunk.Row) (d string, isNull bool, err error) { var x int64 x, isNull, err = b.args[0].EvalInt(b.ctx, row) if isNull || err != nil { return d, isNull, err } if x < 0 { x = 0 } if uint64(x) > b.maxAllowedPacket { b.ctx.GetSessionVars().StmtCtx.AppendWarning(errWarnAllowedPacketOverflowed.GenWithStackByArgs("space", b.maxAllowedPacket)) return d, true, nil } if x > mysql.MaxBlobWidth { return d, true, nil } return strings.Repeat(" ", int(x)), false, nil } type upperFunctionClass struct { baseFunctionClass } func (c *upperFunctionClass) getFunction(ctx sessionctx.Context, args []Expression) (builtinFunc, error) { if err := c.verifyArgs(args); err != nil { return nil, err } bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETString, types.ETString) if err != nil { return nil, err } argTp := args[0].GetType() bf.tp.Flen = argTp.Flen SetBinFlagOrBinStr(argTp, bf.tp) var sig builtinFunc if types.IsBinaryStr(argTp) { sig = &builtinUpperSig{bf} sig.setPbCode(tipb.ScalarFuncSig_Upper) } else { sig = &builtinUpperUTF8Sig{bf} sig.setPbCode(tipb.ScalarFuncSig_UpperUTF8) } return sig, nil } type builtinUpperUTF8Sig struct { baseBuiltinFunc } func (b *builtinUpperUTF8Sig) Clone() builtinFunc { newSig := &builtinUpperUTF8Sig{} newSig.cloneFrom(&b.baseBuiltinFunc) return newSig } // evalString evals a builtinUpperUTF8Sig. // See https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_upper func (b *builtinUpperUTF8Sig) evalString(row chunk.Row) (d string, isNull bool, err error) { d, isNull, err = b.args[0].EvalString(b.ctx, row) if isNull || err != nil { return d, isNull, err } return strings.ToUpper(d), false, nil } type builtinUpperSig struct { baseBuiltinFunc } func (b *builtinUpperSig) Clone() builtinFunc { newSig := &builtinUpperSig{} newSig.cloneFrom(&b.baseBuiltinFunc) return newSig } // evalString evals a builtinUpperSig. // See https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_upper func (b *builtinUpperSig) evalString(row chunk.Row) (d string, isNull bool, err error) { d, isNull, err = b.args[0].EvalString(b.ctx, row) if isNull || err != nil { return d, isNull, err } return d, false, nil } type strcmpFunctionClass struct { baseFunctionClass } func (c *strcmpFunctionClass) getFunction(ctx sessionctx.Context, args []Expression) (builtinFunc, error) { if err := c.verifyArgs(args); err != nil { return nil, err } bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETInt, types.ETString, types.ETString) if err != nil { return nil, err } bf.tp.Flen = 2 types.SetBinChsClnFlag(bf.tp) sig := &builtinStrcmpSig{bf} sig.setPbCode(tipb.ScalarFuncSig_Strcmp) return sig, nil } type builtinStrcmpSig struct { baseBuiltinFunc } func (b *builtinStrcmpSig) Clone() builtinFunc { newSig := &builtinStrcmpSig{} newSig.cloneFrom(&b.baseBuiltinFunc) return newSig } // evalInt evals a builtinStrcmpSig. // See https://dev.mysql.com/doc/refman/5.7/en/string-comparison-functions.html func (b *builtinStrcmpSig) evalInt(row chunk.Row) (int64, bool, error) { var ( left, right string isNull bool err error ) left, isNull, err = b.args[0].EvalString(b.ctx, row) if isNull || err != nil { return 0, isNull, err } right, isNull, err = b.args[1].EvalString(b.ctx, row) if isNull || err != nil { return 0, isNull, err } res := types.CompareString(left, right, b.collation) return int64(res), false, nil } type replaceFunctionClass struct { baseFunctionClass } func (c *replaceFunctionClass) getFunction(ctx sessionctx.Context, args []Expression) (builtinFunc, error) { if err := c.verifyArgs(args); err != nil { return nil, err } bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETString, types.ETString, types.ETString, types.ETString) if err != nil { return nil, err } bf.tp.Flen = c.fixLength(args) for _, a := range args { SetBinFlagOrBinStr(a.GetType(), bf.tp) } sig := &builtinReplaceSig{bf} sig.setPbCode(tipb.ScalarFuncSig_Replace) return sig, nil } // fixLength calculate the Flen of the return type. func (c *replaceFunctionClass) fixLength(args []Expression) int { charLen := args[0].GetType().Flen oldStrLen := args[1].GetType().Flen diff := args[2].GetType().Flen - oldStrLen if diff > 0 && oldStrLen > 0 { charLen += (charLen / oldStrLen) * diff } return charLen } type builtinReplaceSig struct { baseBuiltinFunc } func (b *builtinReplaceSig) Clone() builtinFunc { newSig := &builtinReplaceSig{} newSig.cloneFrom(&b.baseBuiltinFunc) return newSig } // evalString evals a builtinReplaceSig. // See https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_replace func (b *builtinReplaceSig) evalString(row chunk.Row) (d string, isNull bool, err error) { var str, oldStr, newStr string str, isNull, err = b.args[0].EvalString(b.ctx, row) if isNull || err != nil { return d, isNull, err } oldStr, isNull, err = b.args[1].EvalString(b.ctx, row) if isNull || err != nil { return d, isNull, err } newStr, isNull, err = b.args[2].EvalString(b.ctx, row) if isNull || err != nil { return d, isNull, err } if oldStr == "" { return str, false, nil } return strings.Replace(str, oldStr, newStr, -1), false, nil } type convertFunctionClass struct { baseFunctionClass } func (c *convertFunctionClass) getFunction(ctx sessionctx.Context, args []Expression) (builtinFunc, error) { if err := c.verifyArgs(args); err != nil { return nil, err } bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETString, types.ETString, types.ETString) if err != nil { return nil, err } charsetArg, ok := args[1].(*Constant) if !ok { // `args[1]` is limited by parser to be a constant string, // should never go into here. return nil, errIncorrectArgs.GenWithStackByArgs("charset") } transcodingName := charsetArg.Value.GetString() bf.tp.Charset = strings.ToLower(transcodingName) // Quoted about the behavior of syntax CONVERT(expr, type) to CHAR(): // In all cases, the string has the default collation for the character set. // See https://dev.mysql.com/doc/refman/5.7/en/cast-functions.html#function_convert // Here in syntax CONVERT(expr USING transcoding_name), behavior is kept the same, // picking the default collation of target charset. bf.tp.Collate, err = charset.GetDefaultCollation(bf.tp.Charset) if err != nil { return nil, errUnknownCharacterSet.GenWithStackByArgs(transcodingName) } // Result will be a binary string if converts charset to BINARY. // See https://dev.mysql.com/doc/refman/5.7/en/charset-binary-set.html if types.IsBinaryStr(bf.tp) { types.SetBinChsClnFlag(bf.tp) } else { bf.tp.Flag &= ^mysql.BinaryFlag } bf.tp.Flen = mysql.MaxBlobWidth sig := &builtinConvertSig{bf} sig.setPbCode(tipb.ScalarFuncSig_Convert) return sig, nil } type builtinConvertSig struct { baseBuiltinFunc } func (b *builtinConvertSig) Clone() builtinFunc { newSig := &builtinConvertSig{} newSig.cloneFrom(&b.baseBuiltinFunc) return newSig } // evalString evals CONVERT(expr USING transcoding_name). // Syntax CONVERT(expr, type) is parsed as cast expr so not handled here. // See https://dev.mysql.com/doc/refman/5.7/en/cast-functions.html#function_convert func (b *builtinConvertSig) evalString(row chunk.Row) (string, bool, error) { expr, isNull, err := b.args[0].EvalString(b.ctx, row) if isNull || err != nil { return "", true, err } // Since charset is already validated and set from getFunction(), there's no // need to get charset from args again. encoding, _ := charset.Lookup(b.tp.Charset) // However, if `b.tp.Charset` is abnormally set to a wrong charset, we still // return with error. if encoding == nil { return "", true, errUnknownCharacterSet.GenWithStackByArgs(b.tp.Charset) } target, _, err := transform.String(encoding.NewDecoder(), expr) return target, err != nil, err } type substringFunctionClass struct { baseFunctionClass } func (c *substringFunctionClass) getFunction(ctx sessionctx.Context, args []Expression) (builtinFunc, error) { if err := c.verifyArgs(args); err != nil { return nil, err } argTps := []types.EvalType{types.ETString, types.ETInt} if len(args) == 3 { argTps = append(argTps, types.ETInt) } bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETString, argTps...) if err != nil { return nil, err } argType := args[0].GetType() bf.tp.Flen = argType.Flen SetBinFlagOrBinStr(argType, bf.tp) var sig builtinFunc switch { case len(args) == 3 && types.IsBinaryStr(argType): sig = &builtinSubstring3ArgsSig{bf} sig.setPbCode(tipb.ScalarFuncSig_Substring3Args) case len(args) == 3: sig = &builtinSubstring3ArgsUTF8Sig{bf} sig.setPbCode(tipb.ScalarFuncSig_Substring3ArgsUTF8) case len(args) == 2 && types.IsBinaryStr(argType): sig = &builtinSubstring2ArgsSig{bf} sig.setPbCode(tipb.ScalarFuncSig_Substring2Args) case len(args) == 2: sig = &builtinSubstring2ArgsUTF8Sig{bf} sig.setPbCode(tipb.ScalarFuncSig_Substring2ArgsUTF8) default: // Should never happens. return nil, errors.Errorf("SUBSTR invalid arg length, expect 2 or 3 but got: %v", len(args)) } return sig, nil } type builtinSubstring2ArgsSig struct { baseBuiltinFunc } func (b *builtinSubstring2ArgsSig) Clone() builtinFunc { newSig := &builtinSubstring2ArgsSig{} newSig.cloneFrom(&b.baseBuiltinFunc) return newSig } // evalString evals SUBSTR(str,pos), SUBSTR(str FROM pos), SUBSTR() is a synonym for SUBSTRING(). // See https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_substr func (b *builtinSubstring2ArgsSig) evalString(row chunk.Row) (string, bool, error) { str, isNull, err := b.args[0].EvalString(b.ctx, row) if isNull || err != nil { return "", true, err } pos, isNull, err := b.args[1].EvalInt(b.ctx, row) if isNull || err != nil { return "", true, err } length := int64(len(str)) if pos < 0 { pos += length } else { pos-- } if pos > length || pos < 0 { pos = length } return str[pos:], false, nil } type builtinSubstring2ArgsUTF8Sig struct { baseBuiltinFunc } func (b *builtinSubstring2ArgsUTF8Sig) Clone() builtinFunc { newSig := &builtinSubstring2ArgsUTF8Sig{} newSig.cloneFrom(&b.baseBuiltinFunc) return newSig } // evalString evals SUBSTR(str,pos), SUBSTR(str FROM pos), SUBSTR() is a synonym for SUBSTRING(). // See https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_substr func (b *builtinSubstring2ArgsUTF8Sig) evalString(row chunk.Row) (string, bool, error) { str, isNull, err := b.args[0].EvalString(b.ctx, row) if isNull || err != nil { return "", true, err } pos, isNull, err := b.args[1].EvalInt(b.ctx, row) if isNull || err != nil { return "", true, err } runes := []rune(str) length := int64(len(runes)) if pos < 0 { pos += length } else { pos-- } if pos > length || pos < 0 { pos = length } return string(runes[pos:]), false, nil } type builtinSubstring3ArgsSig struct { baseBuiltinFunc } func (b *builtinSubstring3ArgsSig) Clone() builtinFunc { newSig := &builtinSubstring3ArgsSig{} newSig.cloneFrom(&b.baseBuiltinFunc) return newSig } // evalString evals SUBSTR(str,pos,len), SUBSTR(str FROM pos FOR len), SUBSTR() is a synonym for SUBSTRING(). // See https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_substr func (b *builtinSubstring3ArgsSig) evalString(row chunk.Row) (string, bool, error) { str, isNull, err := b.args[0].EvalString(b.ctx, row) if isNull || err != nil { return "", true, err } pos, isNull, err := b.args[1].EvalInt(b.ctx, row) if isNull || err != nil { return "", true, err } length, isNull, err := b.args[2].EvalInt(b.ctx, row) if isNull || err != nil { return "", true, err } byteLen := int64(len(str)) if pos < 0 { pos += byteLen } else { pos-- } if pos > byteLen || pos < 0 { pos = byteLen } end := pos + length if end < pos { return "", false, nil } else if end < byteLen { return str[pos:end], false, nil } return str[pos:], false, nil } type builtinSubstring3ArgsUTF8Sig struct { baseBuiltinFunc } func (b *builtinSubstring3ArgsUTF8Sig) Clone() builtinFunc { newSig := &builtinSubstring3ArgsUTF8Sig{} newSig.cloneFrom(&b.baseBuiltinFunc) return newSig } // evalString evals SUBSTR(str,pos,len), SUBSTR(str FROM pos FOR len), SUBSTR() is a synonym for SUBSTRING(). // See https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_substr func (b *builtinSubstring3ArgsUTF8Sig) evalString(row chunk.Row) (string, bool, error) { str, isNull, err := b.args[0].EvalString(b.ctx, row) if isNull || err != nil { return "", true, err } pos, isNull, err := b.args[1].EvalInt(b.ctx, row) if isNull || err != nil { return "", true, err } length, isNull, err := b.args[2].EvalInt(b.ctx, row) if isNull || err != nil { return "", true, err } runes := []rune(str) numRunes := int64(len(runes)) if pos < 0 { pos += numRunes } else { pos-- } if pos > numRunes || pos < 0 { pos = numRunes } end := pos + length if end < pos { return "", false, nil } else if end < numRunes { return string(runes[pos:end]), false, nil } return string(runes[pos:]), false, nil } type substringIndexFunctionClass struct { baseFunctionClass } func (c *substringIndexFunctionClass) getFunction(ctx sessionctx.Context, args []Expression) (builtinFunc, error) { if err := c.verifyArgs(args); err != nil { return nil, err } bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETString, types.ETString, types.ETString, types.ETInt) if err != nil { return nil, err } argType := args[0].GetType() bf.tp.Flen = argType.Flen SetBinFlagOrBinStr(argType, bf.tp) sig := &builtinSubstringIndexSig{bf} sig.setPbCode(tipb.ScalarFuncSig_SubstringIndex) return sig, nil } type builtinSubstringIndexSig struct { baseBuiltinFunc } func (b *builtinSubstringIndexSig) Clone() builtinFunc { newSig := &builtinSubstringIndexSig{} newSig.cloneFrom(&b.baseBuiltinFunc) return newSig } // evalString evals a builtinSubstringIndexSig. // See https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_substring-index func (b *builtinSubstringIndexSig) evalString(row chunk.Row) (d string, isNull bool, err error) { var ( str, delim string count int64 ) str, isNull, err = b.args[0].EvalString(b.ctx, row) if isNull || err != nil { return d, isNull, err } delim, isNull, err = b.args[1].EvalString(b.ctx, row) if isNull || err != nil { return d, isNull, err } count, isNull, err = b.args[2].EvalInt(b.ctx, row) if isNull || err != nil { return d, isNull, err } if len(delim) == 0 { return "", false, nil } strs := strings.Split(str, delim) start, end := int64(0), int64(len(strs)) if count > 0 { // If count is positive, everything to the left of the final delimiter (counting from the left) is returned. if count < end { end = count } } else { // If count is negative, everything to the right of the final delimiter (counting from the right) is returned. count = -count if count < 0 { // -count overflows max int64, returns an empty string. return "", false, nil } if count < end { start = end - count } } substrs := strs[start:end] return strings.Join(substrs, delim), false, nil } type locateFunctionClass struct { baseFunctionClass } func (c *locateFunctionClass) getFunction(ctx sessionctx.Context, args []Expression) (builtinFunc, error) { if err := c.verifyArgs(args); err != nil { return nil, err } hasStartPos, argTps := len(args) == 3, []types.EvalType{types.ETString, types.ETString} if hasStartPos { argTps = append(argTps, types.ETInt) } bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETInt, argTps...) if err != nil { return nil, err } var sig builtinFunc // Locate is multibyte safe. useBinary := bf.collation == charset.CollationBin switch { case hasStartPos && useBinary: sig = &builtinLocate3ArgsSig{bf} sig.setPbCode(tipb.ScalarFuncSig_Locate3Args) case hasStartPos: sig = &builtinLocate3ArgsUTF8Sig{bf} sig.setPbCode(tipb.ScalarFuncSig_Locate3ArgsUTF8) case useBinary: sig = &builtinLocate2ArgsSig{bf} sig.setPbCode(tipb.ScalarFuncSig_Locate2Args) default: sig = &builtinLocate2ArgsUTF8Sig{bf} sig.setPbCode(tipb.ScalarFuncSig_Locate2ArgsUTF8) } return sig, nil } type builtinLocate2ArgsSig struct { baseBuiltinFunc } func (b *builtinLocate2ArgsSig) Clone() builtinFunc { newSig := &builtinLocate2ArgsSig{} newSig.cloneFrom(&b.baseBuiltinFunc) return newSig } // evalInt evals LOCATE(substr,str), case-sensitive. // See https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_locate func (b *builtinLocate2ArgsSig) evalInt(row chunk.Row) (int64, bool, error) { subStr, isNull, err := b.args[0].EvalString(b.ctx, row) if isNull || err != nil { return 0, isNull, err } str, isNull, err := b.args[1].EvalString(b.ctx, row) if isNull || err != nil { return 0, isNull, err } subStrLen := len(subStr) if subStrLen == 0 { return 1, false, nil } ret, idx := 0, strings.Index(str, subStr) if idx != -1 { ret = idx + 1 } return int64(ret), false, nil } type builtinLocate2ArgsUTF8Sig struct { baseBuiltinFunc } func (b *builtinLocate2ArgsUTF8Sig) Clone() builtinFunc { newSig := &builtinLocate2ArgsUTF8Sig{} newSig.cloneFrom(&b.baseBuiltinFunc) return newSig } // evalInt evals LOCATE(substr,str). // See https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_locate func (b *builtinLocate2ArgsUTF8Sig) evalInt(row chunk.Row) (int64, bool, error) { subStr, isNull, err := b.args[0].EvalString(b.ctx, row) if isNull || err != nil { return 0, isNull, err } str, isNull, err := b.args[1].EvalString(b.ctx, row) if isNull || err != nil { return 0, isNull, err } if int64(len([]rune(subStr))) == 0 { return 1, false, nil } if collate.IsCICollation(b.collation) { str = strings.ToLower(str) subStr = strings.ToLower(subStr) } ret, idx := 0, strings.Index(str, subStr) if idx != -1 { ret = utf8.RuneCountInString(str[:idx]) + 1 } return int64(ret), false, nil } type builtinLocate3ArgsSig struct { baseBuiltinFunc } func (b *builtinLocate3ArgsSig) Clone() builtinFunc { newSig := &builtinLocate3ArgsSig{} newSig.cloneFrom(&b.baseBuiltinFunc) return newSig } // evalInt evals LOCATE(substr,str,pos), case-sensitive. // See https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_locate func (b *builtinLocate3ArgsSig) evalInt(row chunk.Row) (int64, bool, error) { subStr, isNull, err := b.args[0].EvalString(b.ctx, row) if isNull || err != nil { return 0, isNull, err } str, isNull, err := b.args[1].EvalString(b.ctx, row) if isNull || err != nil { return 0, isNull, err } pos, isNull, err := b.args[2].EvalInt(b.ctx, row) // Transfer the argument which starts from 1 to real index which starts from 0. pos-- if isNull || err != nil { return 0, isNull, err } subStrLen := len(subStr) if pos < 0 || pos > int64(len(str)-subStrLen) { return 0, false, nil } else if subStrLen == 0 { return pos + 1, false, nil } slice := str[pos:] idx := strings.Index(slice, subStr) if idx != -1 { return pos + int64(idx) + 1, false, nil } return 0, false, nil } type builtinLocate3ArgsUTF8Sig struct { baseBuiltinFunc } func (b *builtinLocate3ArgsUTF8Sig) Clone() builtinFunc { newSig := &builtinLocate3ArgsUTF8Sig{} newSig.cloneFrom(&b.baseBuiltinFunc) return newSig } // evalInt evals LOCATE(substr,str,pos). // See https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_locate func (b *builtinLocate3ArgsUTF8Sig) evalInt(row chunk.Row) (int64, bool, error) { subStr, isNull, err := b.args[0].EvalString(b.ctx, row) if isNull || err != nil { return 0, isNull, err } str, isNull, err := b.args[1].EvalString(b.ctx, row) if isNull || err != nil { return 0, isNull, err } if collate.IsCICollation(b.collation) { subStr = strings.ToLower(subStr) str = strings.ToLower(str) } pos, isNull, err := b.args[2].EvalInt(b.ctx, row) // Transfer the argument which starts from 1 to real index which starts from 0. pos-- if isNull || err != nil { return 0, isNull, err } subStrLen := len([]rune(subStr)) if pos < 0 || pos > int64(len([]rune(str))-subStrLen) { return 0, false, nil } else if subStrLen == 0 { return pos + 1, false, nil } slice := string([]rune(str)[pos:]) idx := strings.Index(slice, subStr) if idx != -1 { return pos + int64(utf8.RuneCountInString(slice[:idx])) + 1, false, nil } return 0, false, nil } type hexFunctionClass struct { baseFunctionClass } func (c *hexFunctionClass) getFunction(ctx sessionctx.Context, args []Expression) (builtinFunc, error) { if err := c.verifyArgs(args); err != nil { return nil, err } argTp := args[0].GetType().EvalType() switch argTp { case types.ETString, types.ETDatetime, types.ETTimestamp, types.ETDuration, types.ETJson: bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETString, types.ETString) if err != nil { return nil, err } bf.tp.Charset, bf.tp.Collate = ctx.GetSessionVars().GetCharsetInfo() // Use UTF-8 as default bf.tp.Flen = args[0].GetType().Flen * 3 * 2 sig := &builtinHexStrArgSig{bf} sig.setPbCode(tipb.ScalarFuncSig_HexStrArg) return sig, nil case types.ETInt, types.ETReal, types.ETDecimal: bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETString, types.ETInt) if err != nil { return nil, err } bf.tp.Flen = args[0].GetType().Flen * 2 bf.tp.Charset, bf.tp.Collate = ctx.GetSessionVars().GetCharsetInfo() sig := &builtinHexIntArgSig{bf} sig.setPbCode(tipb.ScalarFuncSig_HexIntArg) return sig, nil default: return nil, errors.Errorf("Hex invalid args, need int or string but get %T", args[0].GetType()) } } type builtinHexStrArgSig struct { baseBuiltinFunc } func (b *builtinHexStrArgSig) Clone() builtinFunc { newSig := &builtinHexStrArgSig{} newSig.cloneFrom(&b.baseBuiltinFunc) return newSig } // evalString evals a builtinHexStrArgSig, corresponding to hex(str) // See https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_hex func (b *builtinHexStrArgSig) evalString(row chunk.Row) (string, bool, error) { d, isNull, err := b.args[0].EvalString(b.ctx, row) if isNull || err != nil { return d, isNull, err } return strings.ToUpper(hex.EncodeToString(hack.Slice(d))), false, nil } type builtinHexIntArgSig struct { baseBuiltinFunc } func (b *builtinHexIntArgSig) Clone() builtinFunc { newSig := &builtinHexIntArgSig{} newSig.cloneFrom(&b.baseBuiltinFunc) return newSig } // evalString evals a builtinHexIntArgSig, corresponding to hex(N) // See https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_hex func (b *builtinHexIntArgSig) evalString(row chunk.Row) (string, bool, error) { x, isNull, err := b.args[0].EvalInt(b.ctx, row) if isNull || err != nil { return "", isNull, err } return strings.ToUpper(fmt.Sprintf("%x", uint64(x))), false, nil } type unhexFunctionClass struct { baseFunctionClass } func (c *unhexFunctionClass) getFunction(ctx sessionctx.Context, args []Expression) (builtinFunc, error) { if err := c.verifyArgs(args); err != nil { return nil, err } var retFlen int if err := c.verifyArgs(args); err != nil { return nil, err } argType := args[0].GetType() argEvalTp := argType.EvalType() switch argEvalTp { case types.ETString, types.ETDatetime, types.ETTimestamp, types.ETDuration, types.ETJson: // Use UTF-8 as default charset, so there're (Flen * 3 + 1) / 2 byte-pairs retFlen = (argType.Flen*3 + 1) / 2 case types.ETInt, types.ETReal, types.ETDecimal: // For number value, there're (Flen + 1) / 2 byte-pairs retFlen = (argType.Flen + 1) / 2 default: return nil, errors.Errorf("Unhex invalid args, need int or string but get %s", argType) } bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETString, types.ETString) if err != nil { return nil, err } bf.tp.Flen = retFlen types.SetBinChsClnFlag(bf.tp) sig := &builtinUnHexSig{bf} sig.setPbCode(tipb.ScalarFuncSig_UnHex) return sig, nil } type builtinUnHexSig struct { baseBuiltinFunc } func (b *builtinUnHexSig) Clone() builtinFunc { newSig := &builtinUnHexSig{} newSig.cloneFrom(&b.baseBuiltinFunc) return newSig } // evalString evals a builtinUnHexSig. // See https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_unhex func (b *builtinUnHexSig) evalString(row chunk.Row) (string, bool, error) { var bs []byte d, isNull, err := b.args[0].EvalString(b.ctx, row) if isNull || err != nil { return d, isNull, err } // Add a '0' to the front, if the length is not the multiple of 2 if len(d)%2 != 0 { d = "0" + d } bs, err = hex.DecodeString(d) if err != nil { return "", true, nil } return string(bs), false, nil } const spaceChars = " " type trimFunctionClass struct { baseFunctionClass } // getFunction sets trim built-in function signature. // The syntax of trim in mysql is 'TRIM([{BOTH | LEADING | TRAILING} [remstr] FROM] str), TRIM([remstr FROM] str)', // but we wil convert it into trim(str), trim(str, remstr) and trim(str, remstr, direction) in AST. func (c *trimFunctionClass) getFunction(ctx sessionctx.Context, args []Expression) (builtinFunc, error) { if err := c.verifyArgs(args); err != nil { return nil, err } switch len(args) { case 1: bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETString, types.ETString) if err != nil { return nil, err } argType := args[0].GetType() bf.tp.Flen = argType.Flen SetBinFlagOrBinStr(argType, bf.tp) sig := &builtinTrim1ArgSig{bf} sig.setPbCode(tipb.ScalarFuncSig_Trim1Arg) return sig, nil case 2: bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETString, types.ETString, types.ETString) if err != nil { return nil, err } argType := args[0].GetType() SetBinFlagOrBinStr(argType, bf.tp) sig := &builtinTrim2ArgsSig{bf} sig.setPbCode(tipb.ScalarFuncSig_Trim2Args) return sig, nil case 3: bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETString, types.ETString, types.ETString, types.ETInt) if err != nil { return nil, err } argType := args[0].GetType() bf.tp.Flen = argType.Flen SetBinFlagOrBinStr(argType, bf.tp) sig := &builtinTrim3ArgsSig{bf} sig.setPbCode(tipb.ScalarFuncSig_Trim3Args) return sig, nil default: return nil, c.verifyArgs(args) } } type builtinTrim1ArgSig struct { baseBuiltinFunc } func (b *builtinTrim1ArgSig) Clone() builtinFunc { newSig := &builtinTrim1ArgSig{} newSig.cloneFrom(&b.baseBuiltinFunc) return newSig } // evalString evals a builtinTrim1ArgSig, corresponding to trim(str) // See https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_trim func (b *builtinTrim1ArgSig) evalString(row chunk.Row) (d string, isNull bool, err error) { d, isNull, err = b.args[0].EvalString(b.ctx, row) if isNull || err != nil { return d, isNull, err } return strings.Trim(d, spaceChars), false, nil } type builtinTrim2ArgsSig struct { baseBuiltinFunc } func (b *builtinTrim2ArgsSig) Clone() builtinFunc { newSig := &builtinTrim2ArgsSig{} newSig.cloneFrom(&b.baseBuiltinFunc) return newSig } // evalString evals a builtinTrim2ArgsSig, corresponding to trim(str, remstr) // See https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_trim func (b *builtinTrim2ArgsSig) evalString(row chunk.Row) (d string, isNull bool, err error) { var str, remstr string str, isNull, err = b.args[0].EvalString(b.ctx, row) if isNull || err != nil { return d, isNull, err } remstr, isNull, err = b.args[1].EvalString(b.ctx, row) if isNull || err != nil { return d, isNull, err } d = trimLeft(str, remstr) d = trimRight(d, remstr) return d, false, nil } type builtinTrim3ArgsSig struct { baseBuiltinFunc } func (b *builtinTrim3ArgsSig) Clone() builtinFunc { newSig := &builtinTrim3ArgsSig{} newSig.cloneFrom(&b.baseBuiltinFunc) return newSig } // evalString evals a builtinTrim3ArgsSig, corresponding to trim(str, remstr, direction) // See https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_trim func (b *builtinTrim3ArgsSig) evalString(row chunk.Row) (d string, isNull bool, err error) { var ( str, remstr string x int64 direction ast.TrimDirectionType isRemStrNull bool ) str, isNull, err = b.args[0].EvalString(b.ctx, row) if isNull || err != nil { return d, isNull, err } remstr, isRemStrNull, err = b.args[1].EvalString(b.ctx, row) if err != nil { return d, isNull, err } x, isNull, err = b.args[2].EvalInt(b.ctx, row) if isNull || err != nil { return d, isNull, err } direction = ast.TrimDirectionType(x) if direction == ast.TrimLeading { if isRemStrNull { d = strings.TrimLeft(str, spaceChars) } else { d = trimLeft(str, remstr) } } else if direction == ast.TrimTrailing { if isRemStrNull { d = strings.TrimRight(str, spaceChars) } else { d = trimRight(str, remstr) } } else { if isRemStrNull { d = strings.Trim(str, spaceChars) } else { d = trimLeft(str, remstr) d = trimRight(d, remstr) } } return d, false, nil } type lTrimFunctionClass struct { baseFunctionClass } func (c *lTrimFunctionClass) getFunction(ctx sessionctx.Context, args []Expression) (builtinFunc, error) { if err := c.verifyArgs(args); err != nil { return nil, err } bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETString, types.ETString) if err != nil { return nil, err } argType := args[0].GetType() bf.tp.Flen = argType.Flen SetBinFlagOrBinStr(argType, bf.tp) sig := &builtinLTrimSig{bf} sig.setPbCode(tipb.ScalarFuncSig_LTrim) return sig, nil } type builtinLTrimSig struct { baseBuiltinFunc } func (b *builtinLTrimSig) Clone() builtinFunc { newSig := &builtinLTrimSig{} newSig.cloneFrom(&b.baseBuiltinFunc) return newSig } // evalString evals a builtinLTrimSig // See https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_ltrim func (b *builtinLTrimSig) evalString(row chunk.Row) (d string, isNull bool, err error) { d, isNull, err = b.args[0].EvalString(b.ctx, row) if isNull || err != nil { return d, isNull, err } return strings.TrimLeft(d, spaceChars), false, nil } type rTrimFunctionClass struct { baseFunctionClass } func (c *rTrimFunctionClass) getFunction(ctx sessionctx.Context, args []Expression) (builtinFunc, error) { if err := c.verifyArgs(args); err != nil { return nil, err } bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETString, types.ETString) if err != nil { return nil, err } argType := args[0].GetType() bf.tp.Flen = argType.Flen SetBinFlagOrBinStr(argType, bf.tp) sig := &builtinRTrimSig{bf} sig.setPbCode(tipb.ScalarFuncSig_RTrim) return sig, nil } type builtinRTrimSig struct { baseBuiltinFunc } func (b *builtinRTrimSig) Clone() builtinFunc { newSig := &builtinRTrimSig{} newSig.cloneFrom(&b.baseBuiltinFunc) return newSig } // evalString evals a builtinRTrimSig // See https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_rtrim func (b *builtinRTrimSig) evalString(row chunk.Row) (d string, isNull bool, err error) { d, isNull, err = b.args[0].EvalString(b.ctx, row) if isNull || err != nil { return d, isNull, err } return strings.TrimRight(d, spaceChars), false, nil } func trimLeft(str, remstr string) string { for { x := strings.TrimPrefix(str, remstr) if len(x) == len(str) { return x } str = x } } func trimRight(str, remstr string) string { for { x := strings.TrimSuffix(str, remstr) if len(x) == len(str) { return x } str = x } } func getFlen4LpadAndRpad(ctx sessionctx.Context, arg Expression) int { if constant, ok := arg.(*Constant); ok { length, isNull, err := constant.EvalInt(ctx, chunk.Row{}) if err != nil { logutil.BgLogger().Error("eval `Flen` for LPAD/RPAD", zap.Error(err)) } if isNull || err != nil || length > mysql.MaxBlobWidth { return mysql.MaxBlobWidth } return int(length) } return mysql.MaxBlobWidth } type lpadFunctionClass struct { baseFunctionClass } func (c *lpadFunctionClass) getFunction(ctx sessionctx.Context, args []Expression) (builtinFunc, error) { if err := c.verifyArgs(args); err != nil { return nil, err } bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETString, types.ETString, types.ETInt, types.ETString) if err != nil { return nil, err } bf.tp.Flen = getFlen4LpadAndRpad(bf.ctx, args[1]) SetBinFlagOrBinStr(args[0].GetType(), bf.tp) SetBinFlagOrBinStr(args[2].GetType(), bf.tp) valStr, _ := ctx.GetSessionVars().GetSystemVar(variable.MaxAllowedPacket) maxAllowedPacket, err := strconv.ParseUint(valStr, 10, 64) if err != nil { return nil, errors.Trace(err) } if types.IsBinaryStr(args[0].GetType()) || types.IsBinaryStr(args[2].GetType()) { sig := &builtinLpadSig{bf, maxAllowedPacket} sig.setPbCode(tipb.ScalarFuncSig_Lpad) return sig, nil } if bf.tp.Flen *= 4; bf.tp.Flen > mysql.MaxBlobWidth { bf.tp.Flen = mysql.MaxBlobWidth } sig := &builtinLpadUTF8Sig{bf, maxAllowedPacket} sig.setPbCode(tipb.ScalarFuncSig_LpadUTF8) return sig, nil } type builtinLpadSig struct { baseBuiltinFunc maxAllowedPacket uint64 } func (b *builtinLpadSig) Clone() builtinFunc { newSig := &builtinLpadSig{} newSig.cloneFrom(&b.baseBuiltinFunc) newSig.maxAllowedPacket = b.maxAllowedPacket return newSig } // evalString evals LPAD(str,len,padstr). // See https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_lpad func (b *builtinLpadSig) evalString(row chunk.Row) (string, bool, error) { str, isNull, err := b.args[0].EvalString(b.ctx, row) if isNull || err != nil { return "", true, err } byteLength := len(str) length, isNull, err := b.args[1].EvalInt(b.ctx, row) if isNull || err != nil { return "", true, err } targetLength := int(length) if uint64(targetLength) > b.maxAllowedPacket { b.ctx.GetSessionVars().StmtCtx.AppendWarning(errWarnAllowedPacketOverflowed.GenWithStackByArgs("lpad", b.maxAllowedPacket)) return "", true, nil } padStr, isNull, err := b.args[2].EvalString(b.ctx, row) if isNull || err != nil { return "", true, err } padLength := len(padStr) if targetLength < 0 || targetLength > b.tp.Flen || (byteLength < targetLength && padLength == 0) { return "", true, nil } if tailLen := targetLength - byteLength; tailLen > 0 { repeatCount := tailLen/padLength + 1 str = strings.Repeat(padStr, repeatCount)[:tailLen] + str } return str[:targetLength], false, nil } type builtinLpadUTF8Sig struct { baseBuiltinFunc maxAllowedPacket uint64 } func (b *builtinLpadUTF8Sig) Clone() builtinFunc { newSig := &builtinLpadUTF8Sig{} newSig.cloneFrom(&b.baseBuiltinFunc) newSig.maxAllowedPacket = b.maxAllowedPacket return newSig } // evalString evals LPAD(str,len,padstr). // See https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_lpad func (b *builtinLpadUTF8Sig) evalString(row chunk.Row) (string, bool, error) { str, isNull, err := b.args[0].EvalString(b.ctx, row) if isNull || err != nil { return "", true, err } runeLength := len([]rune(str)) length, isNull, err := b.args[1].EvalInt(b.ctx, row) if isNull || err != nil { return "", true, err } targetLength := int(length) if uint64(targetLength)*uint64(mysql.MaxBytesOfCharacter) > b.maxAllowedPacket { b.ctx.GetSessionVars().StmtCtx.AppendWarning(errWarnAllowedPacketOverflowed.GenWithStackByArgs("lpad", b.maxAllowedPacket)) return "", true, nil } padStr, isNull, err := b.args[2].EvalString(b.ctx, row) if isNull || err != nil { return "", true, err } padLength := len([]rune(padStr)) if targetLength < 0 || targetLength*4 > b.tp.Flen || (runeLength < targetLength && padLength == 0) { return "", true, nil } if tailLen := targetLength - runeLength; tailLen > 0 { repeatCount := tailLen/padLength + 1 str = string([]rune(strings.Repeat(padStr, repeatCount))[:tailLen]) + str } return string([]rune(str)[:targetLength]), false, nil } type rpadFunctionClass struct { baseFunctionClass } func (c *rpadFunctionClass) getFunction(ctx sessionctx.Context, args []Expression) (builtinFunc, error) { if err := c.verifyArgs(args); err != nil { return nil, err } bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETString, types.ETString, types.ETInt, types.ETString) if err != nil { return nil, err } bf.tp.Flen = getFlen4LpadAndRpad(bf.ctx, args[1]) SetBinFlagOrBinStr(args[0].GetType(), bf.tp) SetBinFlagOrBinStr(args[2].GetType(), bf.tp) valStr, _ := ctx.GetSessionVars().GetSystemVar(variable.MaxAllowedPacket) maxAllowedPacket, err := strconv.ParseUint(valStr, 10, 64) if err != nil { return nil, errors.Trace(err) } if types.IsBinaryStr(args[0].GetType()) || types.IsBinaryStr(args[2].GetType()) { sig := &builtinRpadSig{bf, maxAllowedPacket} sig.setPbCode(tipb.ScalarFuncSig_Rpad) return sig, nil } if bf.tp.Flen *= 4; bf.tp.Flen > mysql.MaxBlobWidth { bf.tp.Flen = mysql.MaxBlobWidth } sig := &builtinRpadUTF8Sig{bf, maxAllowedPacket} sig.setPbCode(tipb.ScalarFuncSig_RpadUTF8) return sig, nil } type builtinRpadSig struct { baseBuiltinFunc maxAllowedPacket uint64 } func (b *builtinRpadSig) Clone() builtinFunc { newSig := &builtinRpadSig{} newSig.cloneFrom(&b.baseBuiltinFunc) newSig.maxAllowedPacket = b.maxAllowedPacket return newSig } // evalString evals RPAD(str,len,padstr). // See https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_rpad func (b *builtinRpadSig) evalString(row chunk.Row) (string, bool, error) { str, isNull, err := b.args[0].EvalString(b.ctx, row) if isNull || err != nil { return "", true, err } byteLength := len(str) length, isNull, err := b.args[1].EvalInt(b.ctx, row) if isNull || err != nil { return "", true, err } targetLength := int(length) if uint64(targetLength) > b.maxAllowedPacket { b.ctx.GetSessionVars().StmtCtx.AppendWarning(errWarnAllowedPacketOverflowed.GenWithStackByArgs("rpad", b.maxAllowedPacket)) return "", true, nil } padStr, isNull, err := b.args[2].EvalString(b.ctx, row) if isNull || err != nil { return "", true, err } padLength := len(padStr) if targetLength < 0 || targetLength > b.tp.Flen || (byteLength < targetLength && padLength == 0) { return "", true, nil } if tailLen := targetLength - byteLength; tailLen > 0 { repeatCount := tailLen/padLength + 1 str = str + strings.Repeat(padStr, repeatCount) } return str[:targetLength], false, nil } type builtinRpadUTF8Sig struct { baseBuiltinFunc maxAllowedPacket uint64 } func (b *builtinRpadUTF8Sig) Clone() builtinFunc { newSig := &builtinRpadUTF8Sig{} newSig.cloneFrom(&b.baseBuiltinFunc) newSig.maxAllowedPacket = b.maxAllowedPacket return newSig } // evalString evals RPAD(str,len,padstr). // See https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_rpad func (b *builtinRpadUTF8Sig) evalString(row chunk.Row) (string, bool, error) { str, isNull, err := b.args[0].EvalString(b.ctx, row) if isNull || err != nil { return "", true, err } runeLength := len([]rune(str)) length, isNull, err := b.args[1].EvalInt(b.ctx, row) if isNull || err != nil { return "", true, err } targetLength := int(length) if uint64(targetLength)*uint64(mysql.MaxBytesOfCharacter) > b.maxAllowedPacket { b.ctx.GetSessionVars().StmtCtx.AppendWarning(errWarnAllowedPacketOverflowed.GenWithStackByArgs("rpad", b.maxAllowedPacket)) return "", true, nil } padStr, isNull, err := b.args[2].EvalString(b.ctx, row) if isNull || err != nil { return "", true, err } padLength := len([]rune(padStr)) if targetLength < 0 || targetLength*4 > b.tp.Flen || (runeLength < targetLength && padLength == 0) { return "", true, nil } if tailLen := targetLength - runeLength; tailLen > 0 { repeatCount := tailLen/padLength + 1 str = str + strings.Repeat(padStr, repeatCount) } return string([]rune(str)[:targetLength]), false, nil } type bitLengthFunctionClass struct { baseFunctionClass } func (c *bitLengthFunctionClass) getFunction(ctx sessionctx.Context, args []Expression) (builtinFunc, error) { if err := c.verifyArgs(args); err != nil { return nil, err } bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETInt, types.ETString) if err != nil { return nil, err } bf.tp.Flen = 10 sig := &builtinBitLengthSig{bf} sig.setPbCode(tipb.ScalarFuncSig_BitLength) return sig, nil } type builtinBitLengthSig struct { baseBuiltinFunc } func (b *builtinBitLengthSig) Clone() builtinFunc { newSig := &builtinBitLengthSig{} newSig.cloneFrom(&b.baseBuiltinFunc) return newSig } // evalInt evaluates a builtinBitLengthSig. // See https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_bit-length func (b *builtinBitLengthSig) evalInt(row chunk.Row) (int64, bool, error) { val, isNull, err := b.args[0].EvalString(b.ctx, row) if isNull || err != nil { return 0, isNull, err } return int64(len(val) * 8), false, nil } type charFunctionClass struct { baseFunctionClass } func (c *charFunctionClass) getFunction(ctx sessionctx.Context, args []Expression) (builtinFunc, error) { if err := c.verifyArgs(args); err != nil { return nil, err } argTps := make([]types.EvalType, 0, len(args)) for i := 0; i < len(args)-1; i++ { argTps = append(argTps, types.ETInt) } argTps = append(argTps, types.ETString) bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETString, argTps...) if err != nil { return nil, err } // The last argument represents the charset name after "using". if _, ok := args[len(args)-1].(*Constant); !ok { // If we got there, there must be something wrong in other places. logutil.BgLogger().Warn(fmt.Sprintf("The last argument in char function must be constant, but got %T", args[len(args)-1])) return nil, errIncorrectArgs } charsetName, isNull, err := args[len(args)-1].EvalString(ctx, chunk.Row{}) if err != nil { return nil, err } if isNull { // Use the default charset binary if it is nil. bf.tp.Charset, bf.tp.Collate = charset.CharsetBin, charset.CollationBin bf.tp.Flag |= mysql.BinaryFlag } else { bf.tp.Charset = charsetName defaultCollate, err := charset.GetDefaultCollation(charsetName) if err != nil { return nil, err } bf.tp.Collate = defaultCollate } bf.tp.Flen = 4 * (len(args) - 1) sig := &builtinCharSig{bf} sig.setPbCode(tipb.ScalarFuncSig_Char) return sig, nil } type builtinCharSig struct { baseBuiltinFunc } func (b *builtinCharSig) Clone() builtinFunc { newSig := &builtinCharSig{} newSig.cloneFrom(&b.baseBuiltinFunc) return newSig } func (b *builtinCharSig) convertToBytes(ints []int64) []byte { buffer := bytes.NewBuffer([]byte{}) for i := len(ints) - 1; i >= 0; i-- { for count, val := 0, ints[i]; count < 4; count++ { buffer.WriteByte(byte(val & 0xff)) if val >>= 8; val == 0 { break } } } return reverseBytes(buffer.Bytes()) } // evalString evals CHAR(N,... [USING charset_name]). // See https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_char. func (b *builtinCharSig) evalString(row chunk.Row) (string, bool, error) { bigints := make([]int64, 0, len(b.args)-1) for i := 0; i < len(b.args)-1; i++ { val, IsNull, err := b.args[i].EvalInt(b.ctx, row) if err != nil { return "", true, err } if IsNull { continue } bigints = append(bigints, val) } result := string(b.convertToBytes(bigints)) return result, false, nil } type charLengthFunctionClass struct { baseFunctionClass } func (c *charLengthFunctionClass) getFunction(ctx sessionctx.Context, args []Expression) (builtinFunc, error) { if argsErr := c.verifyArgs(args); argsErr != nil { return nil, argsErr } bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETInt, types.ETString) if err != nil { return nil, err } if types.IsBinaryStr(args[0].GetType()) { sig := &builtinCharLengthBinarySig{bf} sig.setPbCode(tipb.ScalarFuncSig_CharLength) return sig, nil } sig := &builtinCharLengthUTF8Sig{bf} sig.setPbCode(tipb.ScalarFuncSig_CharLengthUTF8) return sig, nil } type builtinCharLengthBinarySig struct { baseBuiltinFunc } func (b *builtinCharLengthBinarySig) Clone() builtinFunc { newSig := &builtinCharLengthBinarySig{} newSig.cloneFrom(&b.baseBuiltinFunc) return newSig } // evalInt evals a builtinCharLengthUTF8Sig for binary string type. // See https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_char-length func (b *builtinCharLengthBinarySig) evalInt(row chunk.Row) (int64, bool, error) { val, isNull, err := b.args[0].EvalString(b.ctx, row) if isNull || err != nil { return 0, isNull, err } return int64(len(val)), false, nil } type builtinCharLengthUTF8Sig struct { baseBuiltinFunc } func (b *builtinCharLengthUTF8Sig) Clone() builtinFunc { newSig := &builtinCharLengthUTF8Sig{} newSig.cloneFrom(&b.baseBuiltinFunc) return newSig } // evalInt evals a builtinCharLengthUTF8Sig for non-binary string type. // See https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_char-length func (b *builtinCharLengthUTF8Sig) evalInt(row chunk.Row) (int64, bool, error) { val, isNull, err := b.args[0].EvalString(b.ctx, row) if isNull || err != nil { return 0, isNull, err } return int64(len([]rune(val))), false, nil } type findInSetFunctionClass struct { baseFunctionClass } func (c *findInSetFunctionClass) getFunction(ctx sessionctx.Context, args []Expression) (builtinFunc, error) { if err := c.verifyArgs(args); err != nil { return nil, err } bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETInt, types.ETString, types.ETString) if err != nil { return nil, err } bf.tp.Flen = 3 sig := &builtinFindInSetSig{bf} sig.setPbCode(tipb.ScalarFuncSig_FindInSet) return sig, nil } type builtinFindInSetSig struct { baseBuiltinFunc } func (b *builtinFindInSetSig) Clone() builtinFunc { newSig := &builtinFindInSetSig{} newSig.cloneFrom(&b.baseBuiltinFunc) return newSig } // evalInt evals FIND_IN_SET(str,strlist). // See https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_find-in-set // TODO: This function can be optimized by using bit arithmetic when the first argument is // a constant string and the second is a column of type SET. func (b *builtinFindInSetSig) evalInt(row chunk.Row) (int64, bool, error) { str, isNull, err := b.args[0].EvalString(b.ctx, row) if isNull || err != nil { return 0, isNull, err } strlist, isNull, err := b.args[1].EvalString(b.ctx, row) if isNull || err != nil { return 0, isNull, err } if len(strlist) == 0 { return 0, false, nil } for i, strInSet := range strings.Split(strlist, ",") { if b.ctor.Compare(str, strInSet) == 0 { return int64(i + 1), false, nil } } return 0, false, nil } type fieldFunctionClass struct { baseFunctionClass } func (c *fieldFunctionClass) getFunction(ctx sessionctx.Context, args []Expression) (builtinFunc, error) { if err := c.verifyArgs(args); err != nil { return nil, err } isAllString, isAllNumber := true, true for i, length := 0, len(args); i < length; i++ { argTp := args[i].GetType().EvalType() isAllString = isAllString && (argTp == types.ETString) isAllNumber = isAllNumber && (argTp == types.ETInt) } argTps := make([]types.EvalType, len(args)) argTp := types.ETReal if isAllString { argTp = types.ETString } else if isAllNumber { argTp = types.ETInt } for i, length := 0, len(args); i < length; i++ { argTps[i] = argTp } bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETInt, argTps...) if err != nil { return nil, err } var sig builtinFunc switch argTp { case types.ETReal: sig = &builtinFieldRealSig{bf} sig.setPbCode(tipb.ScalarFuncSig_FieldReal) case types.ETInt: sig = &builtinFieldIntSig{bf} sig.setPbCode(tipb.ScalarFuncSig_FieldInt) case types.ETString: sig = &builtinFieldStringSig{bf} sig.setPbCode(tipb.ScalarFuncSig_FieldString) } return sig, nil } type builtinFieldIntSig struct { baseBuiltinFunc } func (b *builtinFieldIntSig) Clone() builtinFunc { newSig := &builtinFieldIntSig{} newSig.cloneFrom(&b.baseBuiltinFunc) return newSig } // evalInt evals FIELD(str,str1,str2,str3,...). // See https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_field func (b *builtinFieldIntSig) evalInt(row chunk.Row) (int64, bool, error) { str, isNull, err := b.args[0].EvalInt(b.ctx, row) if isNull || err != nil { return 0, err != nil, err } for i, length := 1, len(b.args); i < length; i++ { stri, isNull, err := b.args[i].EvalInt(b.ctx, row) if err != nil { return 0, true, err } if !isNull && str == stri { return int64(i), false, nil } } return 0, false, nil } type builtinFieldRealSig struct { baseBuiltinFunc } func (b *builtinFieldRealSig) Clone() builtinFunc { newSig := &builtinFieldRealSig{} newSig.cloneFrom(&b.baseBuiltinFunc) return newSig } // evalInt evals FIELD(str,str1,str2,str3,...). // See https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_field func (b *builtinFieldRealSig) evalInt(row chunk.Row) (int64, bool, error) { str, isNull, err := b.args[0].EvalReal(b.ctx, row) if isNull || err != nil { return 0, err != nil, err } for i, length := 1, len(b.args); i < length; i++ { stri, isNull, err := b.args[i].EvalReal(b.ctx, row) if err != nil { return 0, true, err } if !isNull && str == stri { return int64(i), false, nil } } return 0, false, nil } type builtinFieldStringSig struct { baseBuiltinFunc } func (b *builtinFieldStringSig) Clone() builtinFunc { newSig := &builtinFieldStringSig{} newSig.cloneFrom(&b.baseBuiltinFunc) return newSig } // evalInt evals FIELD(str,str1,str2,str3,...). // See https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_field func (b *builtinFieldStringSig) evalInt(row chunk.Row) (int64, bool, error) { str, isNull, err := b.args[0].EvalString(b.ctx, row) if isNull || err != nil { return 0, err != nil, err } for i, length := 1, len(b.args); i < length; i++ { stri, isNull, err := b.args[i].EvalString(b.ctx, row) if err != nil { return 0, true, err } if !isNull && b.ctor.Compare(str, stri) == 0 { return int64(i), false, nil } } return 0, false, nil } type makeSetFunctionClass struct { baseFunctionClass } func (c *makeSetFunctionClass) getFlen(ctx sessionctx.Context, args []Expression) int { flen, count := 0, 0 if constant, ok := args[0].(*Constant); ok { bits, isNull, err := constant.EvalInt(ctx, chunk.Row{}) if err == nil && !isNull { for i, length := 1, len(args); i < length; i++ { if (bits & (1 << uint(i-1))) != 0 { flen += args[i].GetType().Flen count++ } } if count > 0 { flen += count - 1 } return flen } } for i, length := 1, len(args); i < length; i++ { flen += args[i].GetType().Flen } return flen + len(args) - 1 - 1 } func (c *makeSetFunctionClass) getFunction(ctx sessionctx.Context, args []Expression) (builtinFunc, error) { if err := c.verifyArgs(args); err != nil { return nil, err } argTps := make([]types.EvalType, len(args)) argTps[0] = types.ETInt for i, length := 1, len(args); i < length; i++ { argTps[i] = types.ETString } bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETString, argTps...) if err != nil { return nil, err } for i, length := 0, len(args); i < length; i++ { SetBinFlagOrBinStr(args[i].GetType(), bf.tp) } bf.tp.Flen = c.getFlen(bf.ctx, args) if bf.tp.Flen > mysql.MaxBlobWidth { bf.tp.Flen = mysql.MaxBlobWidth } sig := &builtinMakeSetSig{bf} sig.setPbCode(tipb.ScalarFuncSig_MakeSet) return sig, nil } type builtinMakeSetSig struct { baseBuiltinFunc } func (b *builtinMakeSetSig) Clone() builtinFunc { newSig := &builtinMakeSetSig{} newSig.cloneFrom(&b.baseBuiltinFunc) return newSig } // evalString evals MAKE_SET(bits,str1,str2,...). // See https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_make-set func (b *builtinMakeSetSig) evalString(row chunk.Row) (string, bool, error) { bits, isNull, err := b.args[0].EvalInt(b.ctx, row) if isNull || err != nil { return "", true, err } sets := make([]string, 0, len(b.args)-1) for i, length := 1, len(b.args); i < length; i++ { if (bits & (1 << uint(i-1))) == 0 { continue } str, isNull, err := b.args[i].EvalString(b.ctx, row) if err != nil { return "", true, err } if !isNull { sets = append(sets, str) } } return strings.Join(sets, ","), false, nil } type octFunctionClass struct { baseFunctionClass } func (c *octFunctionClass) getFunction(ctx sessionctx.Context, args []Expression) (builtinFunc, error) { if err := c.verifyArgs(args); err != nil { return nil, err } var sig builtinFunc if IsBinaryLiteral(args[0]) || args[0].GetType().EvalType() == types.ETInt { bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETString, types.ETInt) if err != nil { return nil, err } bf.tp.Charset, bf.tp.Collate = ctx.GetSessionVars().GetCharsetInfo() bf.tp.Flen, bf.tp.Decimal = 64, types.UnspecifiedLength sig = &builtinOctIntSig{bf} sig.setPbCode(tipb.ScalarFuncSig_OctInt) } else { bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETString, types.ETString) if err != nil { return nil, err } bf.tp.Charset, bf.tp.Collate = ctx.GetSessionVars().GetCharsetInfo() bf.tp.Flen, bf.tp.Decimal = 64, types.UnspecifiedLength sig = &builtinOctStringSig{bf} sig.setPbCode(tipb.ScalarFuncSig_OctString) } return sig, nil } type builtinOctIntSig struct { baseBuiltinFunc } func (b *builtinOctIntSig) Clone() builtinFunc { newSig := &builtinOctIntSig{} newSig.cloneFrom(&b.baseBuiltinFunc) return newSig } // evalString evals OCT(N). // See https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_oct func (b *builtinOctIntSig) evalString(row chunk.Row) (string, bool, error) { val, isNull, err := b.args[0].EvalInt(b.ctx, row) if isNull || err != nil { return "", isNull, err } return strconv.FormatUint(uint64(val), 8), false, nil } type builtinOctStringSig struct { baseBuiltinFunc } func (b *builtinOctStringSig) Clone() builtinFunc { newSig := &builtinOctStringSig{} newSig.cloneFrom(&b.baseBuiltinFunc) return newSig } // evalString evals OCT(N). // See https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_oct func (b *builtinOctStringSig) evalString(row chunk.Row) (string, bool, error) { val, isNull, err := b.args[0].EvalString(b.ctx, row) if isNull || err != nil { return "", isNull, err } negative, overflow := false, false val = getValidPrefix(strings.TrimSpace(val), 10) if len(val) == 0 { return "0", false, nil } if val[0] == '-' { negative, val = true, val[1:] } numVal, err := strconv.ParseUint(val, 10, 64) if err != nil { numError, ok := err.(*strconv.NumError) if !ok || numError.Err != strconv.ErrRange { return "", true, errors.Trace(err) } overflow = true } if negative && !overflow { numVal = -numVal } return strconv.FormatUint(numVal, 8), false, nil } type ordFunctionClass struct { baseFunctionClass } func (c *ordFunctionClass) getFunction(ctx sessionctx.Context, args []Expression) (builtinFunc, error) { if err := c.verifyArgs(args); err != nil { return nil, err } bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETInt, types.ETString) if err != nil { return nil, err } bf.tp.Flen = 10 sig := &builtinOrdSig{bf} sig.setPbCode(tipb.ScalarFuncSig_Ord) return sig, nil } type builtinOrdSig struct { baseBuiltinFunc } func (b *builtinOrdSig) Clone() builtinFunc { newSig := &builtinOrdSig{} newSig.cloneFrom(&b.baseBuiltinFunc) return newSig } // evalInt evals a builtinOrdSig. // See https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_ord func (b *builtinOrdSig) evalInt(row chunk.Row) (int64, bool, error) { str, isNull, err := b.args[0].EvalString(b.ctx, row) if isNull || err != nil { return 0, isNull, err } ord, err := chooseOrdFunc(b.args[0].GetType().Charset) if err != nil { return 0, false, err } return ord(str), false, nil } func chooseOrdFunc(charSet string) (func(string) int64, error) { // use utf8 by default if charSet == "" { charSet = charset.CharsetUTF8 } desc, err := charset.GetCharsetDesc(charSet) if err != nil { return nil, err } if desc.Maxlen == 1 { return ordSingleByte, nil } return ordUtf8, nil } func ordSingleByte(str string) int64 { if len(str) == 0 { return 0 } return int64(str[0]) } func ordUtf8(str string) int64 { if len(str) == 0 { return 0 } _, size := utf8.DecodeRuneInString(str) leftMost := str[:size] var result int64 var factor int64 = 1 for i := len(leftMost) - 1; i >= 0; i-- { result += int64(leftMost[i]) * factor factor *= 256 } return result } type quoteFunctionClass struct { baseFunctionClass } func (c *quoteFunctionClass) getFunction(ctx sessionctx.Context, args []Expression) (builtinFunc, error) { if err := c.verifyArgs(args); err != nil { return nil, err } bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETString, types.ETString) if err != nil { return nil, err } SetBinFlagOrBinStr(args[0].GetType(), bf.tp) bf.tp.Flen = 2*args[0].GetType().Flen + 2 if bf.tp.Flen > mysql.MaxBlobWidth { bf.tp.Flen = mysql.MaxBlobWidth } sig := &builtinQuoteSig{bf} sig.setPbCode(tipb.ScalarFuncSig_Quote) return sig, nil } type builtinQuoteSig struct { baseBuiltinFunc } func (b *builtinQuoteSig) Clone() builtinFunc { newSig := &builtinQuoteSig{} newSig.cloneFrom(&b.baseBuiltinFunc) return newSig } // evalString evals QUOTE(str). // See https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_quote func (b *builtinQuoteSig) evalString(row chunk.Row) (string, bool, error) { str, isNull, err := b.args[0].EvalString(b.ctx, row) if err != nil { return "", true, err } else if isNull { // If the argument is NULL, the return value is the word "NULL" without enclosing single quotation marks. see ref. return "NULL", false, err } return Quote(str), false, nil } // Quote produce a result that can be used as a properly escaped data value in an SQL statement. func Quote(str string) string { runes := []rune(str) buffer := bytes.NewBufferString("") buffer.WriteRune('\'') for i, runeLength := 0, len(runes); i < runeLength; i++ { switch runes[i] { case '\\', '\'': buffer.WriteRune('\\') buffer.WriteRune(runes[i]) case 0: buffer.WriteRune('\\') buffer.WriteRune('0') case '\032': buffer.WriteRune('\\') buffer.WriteRune('Z') default: buffer.WriteRune(runes[i]) } } buffer.WriteRune('\'') return buffer.String() } type binFunctionClass struct { baseFunctionClass } func (c *binFunctionClass) getFunction(ctx sessionctx.Context, args []Expression) (builtinFunc, error) { if err := c.verifyArgs(args); err != nil { return nil, err } bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETString, types.ETInt) if err != nil { return nil, err } bf.tp.Charset, bf.tp.Collate = ctx.GetSessionVars().GetCharsetInfo() bf.tp.Flen = 64 sig := &builtinBinSig{bf} sig.setPbCode(tipb.ScalarFuncSig_Bin) return sig, nil } type builtinBinSig struct { baseBuiltinFunc } func (b *builtinBinSig) Clone() builtinFunc { newSig := &builtinBinSig{} newSig.cloneFrom(&b.baseBuiltinFunc) return newSig } // evalString evals BIN(N). // See https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_bin func (b *builtinBinSig) evalString(row chunk.Row) (string, bool, error) { val, isNull, err := b.args[0].EvalInt(b.ctx, row) if isNull || err != nil { return "", true, err } return fmt.Sprintf("%b", uint64(val)), false, nil } type eltFunctionClass struct { baseFunctionClass } func (c *eltFunctionClass) getFunction(ctx sessionctx.Context, args []Expression) (builtinFunc, error) { if argsErr := c.verifyArgs(args); argsErr != nil { return nil, argsErr } argTps := make([]types.EvalType, 0, len(args)) argTps = append(argTps, types.ETInt) for i := 1; i < len(args); i++ { argTps = append(argTps, types.ETString) } bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETString, argTps...) if err != nil { return nil, err } for _, arg := range args[1:] { argType := arg.GetType() if types.IsBinaryStr(argType) { types.SetBinChsClnFlag(bf.tp) } if argType.Flen > bf.tp.Flen { bf.tp.Flen = argType.Flen } } sig := &builtinEltSig{bf} sig.setPbCode(tipb.ScalarFuncSig_Elt) return sig, nil } type builtinEltSig struct { baseBuiltinFunc } func (b *builtinEltSig) Clone() builtinFunc { newSig := &builtinEltSig{} newSig.cloneFrom(&b.baseBuiltinFunc) return newSig } // evalString evals a ELT(N,str1,str2,str3,...). // See https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_elt func (b *builtinEltSig) evalString(row chunk.Row) (string, bool, error) { idx, isNull, err := b.args[0].EvalInt(b.ctx, row) if isNull || err != nil { return "", true, err } if idx < 1 || idx >= int64(len(b.args)) { return "", true, nil } arg, isNull, err := b.args[idx].EvalString(b.ctx, row) if isNull || err != nil { return "", true, err } return arg, false, nil } type exportSetFunctionClass struct { baseFunctionClass } func (c *exportSetFunctionClass) getFunction(ctx sessionctx.Context, args []Expression) (sig builtinFunc, err error) { if err = c.verifyArgs(args); err != nil { return nil, err } argTps := make([]types.EvalType, 0, 5) argTps = append(argTps, types.ETInt, types.ETString, types.ETString) if len(args) > 3 { argTps = append(argTps, types.ETString) } if len(args) > 4 { argTps = append(argTps, types.ETInt) } bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETString, argTps...) if err != nil { return nil, err } bf.tp.Flen = mysql.MaxBlobWidth switch len(args) { case 3: sig = &builtinExportSet3ArgSig{bf} sig.setPbCode(tipb.ScalarFuncSig_ExportSet3Arg) case 4: sig = &builtinExportSet4ArgSig{bf} sig.setPbCode(tipb.ScalarFuncSig_ExportSet4Arg) case 5: sig = &builtinExportSet5ArgSig{bf} sig.setPbCode(tipb.ScalarFuncSig_ExportSet5Arg) } return sig, nil } // exportSet evals EXPORT_SET(bits,on,off,separator,number_of_bits). // See https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_export-set func exportSet(bits int64, on, off, separator string, numberOfBits int64) string { result := "" for i := uint64(0); i < uint64(numberOfBits); i++ { if (bits & (1 << i)) > 0 { result += on } else { result += off } if i < uint64(numberOfBits)-1 { result += separator } } return result } type builtinExportSet3ArgSig struct { baseBuiltinFunc } func (b *builtinExportSet3ArgSig) Clone() builtinFunc { newSig := &builtinExportSet3ArgSig{} newSig.cloneFrom(&b.baseBuiltinFunc) return newSig } // evalString evals EXPORT_SET(bits,on,off). // See https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_export-set func (b *builtinExportSet3ArgSig) evalString(row chunk.Row) (string, bool, error) { bits, isNull, err := b.args[0].EvalInt(b.ctx, row) if isNull || err != nil { return "", true, err } on, isNull, err := b.args[1].EvalString(b.ctx, row) if isNull || err != nil { return "", true, err } off, isNull, err := b.args[2].EvalString(b.ctx, row) if isNull || err != nil { return "", true, err } return exportSet(bits, on, off, ",", 64), false, nil } type builtinExportSet4ArgSig struct { baseBuiltinFunc } func (b *builtinExportSet4ArgSig) Clone() builtinFunc { newSig := &builtinExportSet4ArgSig{} newSig.cloneFrom(&b.baseBuiltinFunc) return newSig } // evalString evals EXPORT_SET(bits,on,off,separator). // See https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_export-set func (b *builtinExportSet4ArgSig) evalString(row chunk.Row) (string, bool, error) { bits, isNull, err := b.args[0].EvalInt(b.ctx, row) if isNull || err != nil { return "", true, err } on, isNull, err := b.args[1].EvalString(b.ctx, row) if isNull || err != nil { return "", true, err } off, isNull, err := b.args[2].EvalString(b.ctx, row) if isNull || err != nil { return "", true, err } separator, isNull, err := b.args[3].EvalString(b.ctx, row) if isNull || err != nil { return "", true, err } return exportSet(bits, on, off, separator, 64), false, nil } type builtinExportSet5ArgSig struct { baseBuiltinFunc } func (b *builtinExportSet5ArgSig) Clone() builtinFunc { newSig := &builtinExportSet5ArgSig{} newSig.cloneFrom(&b.baseBuiltinFunc) return newSig } // evalString evals EXPORT_SET(bits,on,off,separator,number_of_bits). // See https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_export-set func (b *builtinExportSet5ArgSig) evalString(row chunk.Row) (string, bool, error) { bits, isNull, err := b.args[0].EvalInt(b.ctx, row) if isNull || err != nil { return "", true, err } on, isNull, err := b.args[1].EvalString(b.ctx, row) if isNull || err != nil { return "", true, err } off, isNull, err := b.args[2].EvalString(b.ctx, row) if isNull || err != nil { return "", true, err } separator, isNull, err := b.args[3].EvalString(b.ctx, row) if isNull || err != nil { return "", true, err } numberOfBits, isNull, err := b.args[4].EvalInt(b.ctx, row) if isNull || err != nil { return "", true, err } if numberOfBits < 0 || numberOfBits > 64 { numberOfBits = 64 } return exportSet(bits, on, off, separator, numberOfBits), false, nil } type formatFunctionClass struct { baseFunctionClass } func (c *formatFunctionClass) getFunction(ctx sessionctx.Context, args []Expression) (builtinFunc, error) { if err := c.verifyArgs(args); err != nil { return nil, err } argTps := make([]types.EvalType, 2, 3) argTps[1] = types.ETInt argTp := args[0].GetType().EvalType() if argTp == types.ETDecimal || argTp == types.ETInt { argTps[0] = types.ETDecimal } else { argTps[0] = types.ETReal } if len(args) == 3 { argTps = append(argTps, types.ETString) } bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETString, argTps...) if err != nil { return nil, err } bf.tp.Charset, bf.tp.Collate = ctx.GetSessionVars().GetCharsetInfo() bf.tp.Flen = mysql.MaxBlobWidth var sig builtinFunc if len(args) == 3 { sig = &builtinFormatWithLocaleSig{bf} sig.setPbCode(tipb.ScalarFuncSig_FormatWithLocale) } else { sig = &builtinFormatSig{bf} sig.setPbCode(tipb.ScalarFuncSig_Format) } return sig, nil } // formatMaxDecimals limits the maximum number of decimal digits for result of // function `format`, this value is same as `FORMAT_MAX_DECIMALS` in MySQL source code. const formatMaxDecimals int64 = 30 // evalNumDecArgsForFormat evaluates first 2 arguments, i.e, x and d, for function `format`. func evalNumDecArgsForFormat(f builtinFunc, row chunk.Row) (string, string, bool, error) { var xStr string arg0, arg1 := f.getArgs()[0], f.getArgs()[1] ctx := f.getCtx() if arg0.GetType().EvalType() == types.ETDecimal { x, isNull, err := arg0.EvalDecimal(ctx, row) if isNull || err != nil { return "", "", isNull, err } xStr = x.String() } else { x, isNull, err := arg0.EvalReal(ctx, row) if isNull || err != nil { return "", "", isNull, err } xStr = strconv.FormatFloat(x, 'f', -1, 64) } d, isNull, err := arg1.EvalInt(ctx, row) if isNull || err != nil { return "", "", isNull, err } if d < 0 { d = 0 } else if d > formatMaxDecimals { d = formatMaxDecimals } xStr = roundFormatArgs(xStr, int(d)) dStr := strconv.FormatInt(d, 10) return xStr, dStr, false, nil } func roundFormatArgs(xStr string, maxNumDecimals int) string { if !strings.Contains(xStr, ".") { return xStr } sign := false // xStr cannot have '+' prefix now. // It is built in `evalNumDecArgsFormat` after evaluating `Evalxxx` method. if strings.HasPrefix(xStr, "-") { xStr = strings.Trim(xStr, "-") sign = true } xArr := strings.Split(xStr, ".") integerPart := xArr[0] decimalPart := xArr[1] if len(decimalPart) > maxNumDecimals { t := []byte(decimalPart) carry := false if t[maxNumDecimals] >= '5' { carry = true } for i := maxNumDecimals - 1; i >= 0 && carry; i-- { if t[i] == '9' { t[i] = '0' } else { t[i] = t[i] + 1 carry = false } } decimalPart = string(t) t = []byte(integerPart) for i := len(integerPart) - 1; i >= 0 && carry; i-- { if t[i] == '9' { t[i] = '0' } else { t[i] = t[i] + 1 carry = false } } if carry { integerPart = "1" + string(t) } else { integerPart = string(t) } } xStr = integerPart + "." + decimalPart if sign { xStr = "-" + xStr } return xStr } type builtinFormatWithLocaleSig struct { baseBuiltinFunc } func (b *builtinFormatWithLocaleSig) Clone() builtinFunc { newSig := &builtinFormatWithLocaleSig{} newSig.cloneFrom(&b.baseBuiltinFunc) return newSig } // evalString evals FORMAT(X,D,locale). // See https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_format func (b *builtinFormatWithLocaleSig) evalString(row chunk.Row) (string, bool, error) { x, d, isNull, err := evalNumDecArgsForFormat(b, row) if isNull || err != nil { return "", isNull, err } locale, isNull, err := b.args[2].EvalString(b.ctx, row) if err != nil { return "", false, err } if isNull { b.ctx.GetSessionVars().StmtCtx.AppendWarning(errUnknownLocale.GenWithStackByArgs("NULL")) locale = "en_US" } formatString, err := mysql.GetLocaleFormatFunction(locale)(x, d) return formatString, false, err } type builtinFormatSig struct { baseBuiltinFunc } func (b *builtinFormatSig) Clone() builtinFunc { newSig := &builtinFormatSig{} newSig.cloneFrom(&b.baseBuiltinFunc) return newSig } // evalString evals FORMAT(X,D). // See https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_format func (b *builtinFormatSig) evalString(row chunk.Row) (string, bool, error) { x, d, isNull, err := evalNumDecArgsForFormat(b, row) if isNull || err != nil { return "", isNull, err } formatString, err := mysql.GetLocaleFormatFunction("en_US")(x, d) return formatString, false, err } type fromBase64FunctionClass struct { baseFunctionClass } func (c *fromBase64FunctionClass) getFunction(ctx sessionctx.Context, args []Expression) (builtinFunc, error) { if err := c.verifyArgs(args); err != nil { return nil, err } bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETString, types.ETString) if err != nil { return nil, err } bf.tp.Flen = mysql.MaxBlobWidth valStr, _ := ctx.GetSessionVars().GetSystemVar(variable.MaxAllowedPacket) maxAllowedPacket, err := strconv.ParseUint(valStr, 10, 64) if err != nil { return nil, errors.Trace(err) } types.SetBinChsClnFlag(bf.tp) sig := &builtinFromBase64Sig{bf, maxAllowedPacket} return sig, nil } // base64NeededDecodedLength return the base64 decoded string length. func base64NeededDecodedLength(n int) int { // Returns -1 indicate the result will overflow. if strconv.IntSize == 64 && n > math.MaxInt64/3 { return -1 } if strconv.IntSize == 32 && n > math.MaxInt32/3 { return -1 } return n * 3 / 4 } type builtinFromBase64Sig struct { baseBuiltinFunc maxAllowedPacket uint64 } func (b *builtinFromBase64Sig) Clone() builtinFunc { newSig := &builtinFromBase64Sig{} newSig.cloneFrom(&b.baseBuiltinFunc) newSig.maxAllowedPacket = b.maxAllowedPacket return newSig } // evalString evals FROM_BASE64(str). // See https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_from-base64 func (b *builtinFromBase64Sig) evalString(row chunk.Row) (string, bool, error) { str, isNull, err := b.args[0].EvalString(b.ctx, row) if isNull || err != nil { return "", true, err } needDecodeLen := base64NeededDecodedLength(len(str)) if needDecodeLen == -1 { return "", true, nil } if needDecodeLen > int(b.maxAllowedPacket) { b.ctx.GetSessionVars().StmtCtx.AppendWarning(errWarnAllowedPacketOverflowed.GenWithStackByArgs("from_base64", b.maxAllowedPacket)) return "", true, nil } str = strings.Replace(str, "\t", "", -1) str = strings.Replace(str, " ", "", -1) result, err := base64.StdEncoding.DecodeString(str) if err != nil { // When error happens, take `from_base64("asc")` as an example, we should return NULL. return "", true, nil } return string(result), false, nil } type toBase64FunctionClass struct { baseFunctionClass } func (c *toBase64FunctionClass) getFunction(ctx sessionctx.Context, args []Expression) (builtinFunc, error) { if err := c.verifyArgs(args); err != nil { return nil, err } bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETString, types.ETString) if err != nil { return nil, err } bf.tp.Charset, bf.tp.Collate = ctx.GetSessionVars().GetCharsetInfo() bf.tp.Flen = base64NeededEncodedLength(bf.args[0].GetType().Flen) valStr, _ := ctx.GetSessionVars().GetSystemVar(variable.MaxAllowedPacket) maxAllowedPacket, err := strconv.ParseUint(valStr, 10, 64) if err != nil { return nil, errors.Trace(err) } sig := &builtinToBase64Sig{bf, maxAllowedPacket} return sig, nil } type builtinToBase64Sig struct { baseBuiltinFunc maxAllowedPacket uint64 } func (b *builtinToBase64Sig) Clone() builtinFunc { newSig := &builtinToBase64Sig{} newSig.cloneFrom(&b.baseBuiltinFunc) newSig.maxAllowedPacket = b.maxAllowedPacket return newSig } // base64NeededEncodedLength return the base64 encoded string length. func base64NeededEncodedLength(n int) int { // Returns -1 indicate the result will overflow. if strconv.IntSize == 64 { // len(arg) -> len(to_base64(arg)) // 6827690988321067803 -> 9223372036854775804 // 6827690988321067804 -> -9223372036854775808 if n > 6827690988321067803 { return -1 } } else { // len(arg) -> len(to_base64(arg)) // 1589695686 -> 2147483645 // 1589695687 -> -2147483646 if n > 1589695686 { return -1 } } length := (n + 2) / 3 * 4 return length + (length-1)/76 } // evalString evals a builtinToBase64Sig. // See https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_to-base64 func (b *builtinToBase64Sig) evalString(row chunk.Row) (d string, isNull bool, err error) { str, isNull, err := b.args[0].EvalString(b.ctx, row) if isNull || err != nil { return "", isNull, err } needEncodeLen := base64NeededEncodedLength(len(str)) if needEncodeLen == -1 { return "", true, nil } if needEncodeLen > int(b.maxAllowedPacket) { b.ctx.GetSessionVars().StmtCtx.AppendWarning(errWarnAllowedPacketOverflowed.GenWithStackByArgs("to_base64", b.maxAllowedPacket)) return "", true, nil } if b.tp.Flen == -1 || b.tp.Flen > mysql.MaxBlobWidth { return "", true, nil } //encode strBytes := []byte(str) result := base64.StdEncoding.EncodeToString(strBytes) //A newline is added after each 76 characters of encoded output to divide long output into multiple lines. count := len(result) if count > 76 { resultArr := splitToSubN(result, 76) result = strings.Join(resultArr, "\n") } return result, false, nil } // splitToSubN splits a string every n runes into a string[] func splitToSubN(s string, n int) []string { subs := make([]string, 0, len(s)/n+1) for len(s) > n { subs = append(subs, s[:n]) s = s[n:] } subs = append(subs, s) return subs } type insertFunctionClass struct { baseFunctionClass } func (c *insertFunctionClass) getFunction(ctx sessionctx.Context, args []Expression) (sig builtinFunc, err error) { if err = c.verifyArgs(args); err != nil { return nil, err } bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETString, types.ETString, types.ETInt, types.ETInt, types.ETString) if err != nil { return nil, err } bf.tp.Flen = mysql.MaxBlobWidth SetBinFlagOrBinStr(args[0].GetType(), bf.tp) SetBinFlagOrBinStr(args[3].GetType(), bf.tp) valStr, _ := ctx.GetSessionVars().GetSystemVar(variable.MaxAllowedPacket) maxAllowedPacket, err := strconv.ParseUint(valStr, 10, 64) if err != nil { return nil, errors.Trace(err) } if types.IsBinaryStr(args[0].GetType()) { sig = &builtinInsertSig{bf, maxAllowedPacket} sig.setPbCode(tipb.ScalarFuncSig_Insert) } else { sig = &builtinInsertUTF8Sig{bf, maxAllowedPacket} sig.setPbCode(tipb.ScalarFuncSig_InsertUTF8) } return sig, nil } type builtinInsertSig struct { baseBuiltinFunc maxAllowedPacket uint64 } func (b *builtinInsertSig) Clone() builtinFunc { newSig := &builtinInsertSig{} newSig.cloneFrom(&b.baseBuiltinFunc) newSig.maxAllowedPacket = b.maxAllowedPacket return newSig } // evalString evals INSERT(str,pos,len,newstr). // See https://dev.mysql.com/doc/refman/5.6/en/string-functions.html#function_insert func (b *builtinInsertSig) evalString(row chunk.Row) (string, bool, error) { str, isNull, err := b.args[0].EvalString(b.ctx, row) if isNull || err != nil { return "", true, err } pos, isNull, err := b.args[1].EvalInt(b.ctx, row) if isNull || err != nil { return "", true, err } length, isNull, err := b.args[2].EvalInt(b.ctx, row) if isNull || err != nil { return "", true, err } newstr, isNull, err := b.args[3].EvalString(b.ctx, row) if isNull || err != nil { return "", true, err } strLength := int64(len(str)) if pos < 1 || pos > strLength { return str, false, nil } if length > strLength-pos+1 || length < 0 { length = strLength - pos + 1 } if uint64(strLength-length+int64(len(newstr))) > b.maxAllowedPacket { b.ctx.GetSessionVars().StmtCtx.AppendWarning(errWarnAllowedPacketOverflowed.GenWithStackByArgs("insert", b.maxAllowedPacket)) return "", true, nil } return str[0:pos-1] + newstr + str[pos+length-1:], false, nil } type builtinInsertUTF8Sig struct { baseBuiltinFunc maxAllowedPacket uint64 } func (b *builtinInsertUTF8Sig) Clone() builtinFunc { newSig := &builtinInsertUTF8Sig{} newSig.cloneFrom(&b.baseBuiltinFunc) newSig.maxAllowedPacket = b.maxAllowedPacket return newSig } // evalString evals INSERT(str,pos,len,newstr). // See https://dev.mysql.com/doc/refman/5.6/en/string-functions.html#function_insert func (b *builtinInsertUTF8Sig) evalString(row chunk.Row) (string, bool, error) { str, isNull, err := b.args[0].EvalString(b.ctx, row) if isNull || err != nil { return "", true, err } pos, isNull, err := b.args[1].EvalInt(b.ctx, row) if isNull || err != nil { return "", true, err } length, isNull, err := b.args[2].EvalInt(b.ctx, row) if isNull || err != nil { return "", true, err } newstr, isNull, err := b.args[3].EvalString(b.ctx, row) if isNull || err != nil { return "", true, err } runes := []rune(str) runeLength := int64(len(runes)) if pos < 1 || pos > runeLength { return str, false, nil } if length > runeLength-pos+1 || length < 0 { length = runeLength - pos + 1 } strHead := string(runes[0 : pos-1]) strTail := string(runes[pos+length-1:]) if uint64(len(strHead)+len(newstr)+len(strTail)) > b.maxAllowedPacket { b.ctx.GetSessionVars().StmtCtx.AppendWarning(errWarnAllowedPacketOverflowed.GenWithStackByArgs("insert", b.maxAllowedPacket)) return "", true, nil } return strHead + newstr + strTail, false, nil } type instrFunctionClass struct { baseFunctionClass } func (c *instrFunctionClass) getFunction(ctx sessionctx.Context, args []Expression) (builtinFunc, error) { if err := c.verifyArgs(args); err != nil { return nil, err } bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETInt, types.ETString, types.ETString) if err != nil { return nil, err } bf.tp.Flen = 11 if bf.collation == charset.CollationBin { sig := &builtinInstrSig{bf} sig.setPbCode(tipb.ScalarFuncSig_Instr) return sig, nil } sig := &builtinInstrUTF8Sig{bf} sig.setPbCode(tipb.ScalarFuncSig_InstrUTF8) return sig, nil } type builtinInstrUTF8Sig struct{ baseBuiltinFunc } func (b *builtinInstrUTF8Sig) Clone() builtinFunc { newSig := &builtinInstrUTF8Sig{} newSig.cloneFrom(&b.baseBuiltinFunc) return newSig } type builtinInstrSig struct{ baseBuiltinFunc } func (b *builtinInstrSig) Clone() builtinFunc { newSig := &builtinInstrSig{} newSig.cloneFrom(&b.baseBuiltinFunc) return newSig } // evalInt evals INSTR(str,substr). // See https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_instr func (b *builtinInstrUTF8Sig) evalInt(row chunk.Row) (int64, bool, error) { str, IsNull, err := b.args[0].EvalString(b.ctx, row) if IsNull || err != nil { return 0, true, err } substr, IsNull, err := b.args[1].EvalString(b.ctx, row) if IsNull || err != nil { return 0, true, err } if collate.IsCICollation(b.collation) { str = strings.ToLower(str) substr = strings.ToLower(substr) } idx := strings.Index(str, substr) if idx == -1 { return 0, false, nil } return int64(utf8.RuneCountInString(str[:idx]) + 1), false, nil } // evalInt evals INSTR(str,substr), case sensitive. // See https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_instr func (b *builtinInstrSig) evalInt(row chunk.Row) (int64, bool, error) { str, IsNull, err := b.args[0].EvalString(b.ctx, row) if IsNull || err != nil { return 0, true, err } substr, IsNull, err := b.args[1].EvalString(b.ctx, row) if IsNull || err != nil { return 0, true, err } idx := strings.Index(str, substr) if idx == -1 { return 0, false, nil } return int64(idx + 1), false, nil } type loadFileFunctionClass struct { baseFunctionClass } func (c *loadFileFunctionClass) getFunction(ctx sessionctx.Context, args []Expression) (builtinFunc, error) { return nil, errFunctionNotExists.GenWithStackByArgs("FUNCTION", "load_file") } type weightStringPadding byte const ( // weightStringPaddingNone is used for WEIGHT_STRING(expr) if the expr is non-numeric. weightStringPaddingNone weightStringPadding = iota // weightStringPaddingAsChar is used for WEIGHT_STRING(expr AS CHAR(x)) and the expr is non-numeric. weightStringPaddingAsChar // weightStringPaddingAsBinary is used for WEIGHT_STRING(expr as BINARY(x)) and the expr is not null. weightStringPaddingAsBinary // weightStringPaddingNull is used for WEIGHT_STRING(expr [AS (CHAR|BINARY)]) for all other cases, it returns null always. weightStringPaddingNull ) type weightStringFunctionClass struct { baseFunctionClass } func (c *weightStringFunctionClass) verifyArgs(args []Expression) (weightStringPadding, int, error) { padding := weightStringPaddingNone l := len(args) if l != 1 && l != 3 { return weightStringPaddingNone, 0, ErrIncorrectParameterCount.GenWithStackByArgs(c.funcName) } if types.IsTypeNumeric(args[0].GetType().Tp) { padding = weightStringPaddingNull } length := 0 if l == 3 { if args[1].GetType().EvalType() != types.ETString { return weightStringPaddingNone, 0, ErrIncorrectType.GenWithStackByArgs(args[1].String(), c.funcName) } c1, ok := args[1].(*Constant) if !ok { return weightStringPaddingNone, 0, ErrIncorrectType.GenWithStackByArgs(args[1].String(), c.funcName) } switch x := c1.Value.GetString(); x { case "CHAR": if padding == weightStringPaddingNone { padding = weightStringPaddingAsChar } case "BINARY": padding = weightStringPaddingAsBinary default: return weightStringPaddingNone, 0, ErrIncorrectType.GenWithStackByArgs(x, c.funcName) } if args[2].GetType().EvalType() != types.ETInt { return weightStringPaddingNone, 0, ErrIncorrectType.GenWithStackByArgs(args[2].String(), c.funcName) } c2, ok := args[2].(*Constant) if !ok { return weightStringPaddingNone, 0, ErrIncorrectType.GenWithStackByArgs(args[1].String(), c.funcName) } length = int(c2.Value.GetInt64()) if length == 0 { return weightStringPaddingNone, 0, ErrIncorrectType.GenWithStackByArgs(args[2].String(), c.funcName) } } return padding, length, nil } func (c *weightStringFunctionClass) getFunction(ctx sessionctx.Context, args []Expression) (builtinFunc, error) { padding, length, err := c.verifyArgs(args) if err != nil { return nil, err } argTps := make([]types.EvalType, len(args)) argTps[0] = types.ETString if len(args) == 3 { argTps[1] = types.ETString argTps[2] = types.ETInt } bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETString, argTps...) if err != nil { return nil, err } var sig builtinFunc if padding == weightStringPaddingNull { sig = &builtinWeightStringNullSig{bf} } else { sig = &builtinWeightStringSig{bf, padding, length} } return sig, nil } type builtinWeightStringNullSig struct { baseBuiltinFunc } func (b *builtinWeightStringNullSig) Clone() builtinFunc { newSig := &builtinWeightStringNullSig{} newSig.cloneFrom(&b.baseBuiltinFunc) return newSig } // evalString evals a WEIGHT_STRING(expr [AS CHAR|BINARY]) when the expr is numeric types, it always returns null. // See https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_weight-string func (b *builtinWeightStringNullSig) evalString(row chunk.Row) (string, bool, error) { return "", true, nil } type builtinWeightStringSig struct { baseBuiltinFunc padding weightStringPadding length int } func (b *builtinWeightStringSig) Clone() builtinFunc { newSig := &builtinWeightStringSig{} newSig.cloneFrom(&b.baseBuiltinFunc) newSig.padding = b.padding newSig.length = b.length return newSig } // evalString evals a WEIGHT_STRING(expr [AS (CHAR|BINARY)]) when the expr is non-numeric types. // See https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_weight-string func (b *builtinWeightStringSig) evalString(row chunk.Row) (string, bool, error) { str, isNull, err := b.args[0].EvalString(b.ctx, row) if err != nil { return "", false, err } if isNull { return "", true, nil } var ctor collate.Collator // TODO: refactor padding codes after padding is implemented by all collators. switch b.padding { case weightStringPaddingAsChar: runes := []rune(str) lenRunes := len(runes) if b.length < lenRunes { str = string(runes[:b.length]) } else if b.length > lenRunes { str += strings.Repeat(" ", b.length-lenRunes) } ctor = collate.GetCollator(b.args[0].GetType().Collate) case weightStringPaddingAsBinary: lenStr := len(str) if b.length < lenStr { tpInfo := fmt.Sprintf("BINARY(%d)", b.length) b.ctx.GetSessionVars().StmtCtx.AppendWarning(errTruncatedWrongValue.GenWithStackByArgs(tpInfo, str)) str = str[:b.length] } else if b.length > lenStr { str += strings.Repeat("\x00", b.length-lenStr) } ctor = collate.GetCollator(charset.CollationBin) case weightStringPaddingNone: ctor = collate.GetCollator(b.args[0].GetType().Collate) default: return "", false, ErrIncorrectType.GenWithStackByArgs(ast.WeightString, string(b.padding)) } return string(ctor.Key(str)), false, nil }