1 Star 0 Fork 0

zhoujin826/tidb

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
克隆/下载
builtin_string.go 81.52 KB
一键复制 编辑 原始数据 按行查看 历史
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890
// 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"
log "github.com/Sirupsen/logrus"
"github.com/juju/errors"
"github.com/pingcap/tidb/ast"
"github.com/pingcap/tidb/context"
"github.com/pingcap/tidb/mysql"
"github.com/pingcap/tidb/sessionctx/variable"
"github.com/pingcap/tidb/util/charset"
"github.com/pingcap/tidb/util/hack"
"github.com/pingcap/tidb/util/types"
"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 = &quoteFunctionClass{}
_ functionClass = &binFunctionClass{}
_ functionClass = &eltFunctionClass{}
_ functionClass = &exportSetFunctionClass{}
_ functionClass = &formatFunctionClass{}
_ functionClass = &fromBase64FunctionClass{}
_ functionClass = &toBase64FunctionClass{}
_ functionClass = &insertFunctionClass{}
_ functionClass = &instrFunctionClass{}
_ functionClass = &loadFileFunctionClass{}
)
var (
_ builtinFunc = &builtinLengthSig{}
_ builtinFunc = &builtinASCIISig{}
_ builtinFunc = &builtinConcatSig{}
_ builtinFunc = &builtinConcatWSSig{}
_ builtinFunc = &builtinLeftBinarySig{}
_ builtinFunc = &builtinLeftSig{}
_ builtinFunc = &builtinRightBinarySig{}
_ builtinFunc = &builtinRightSig{}
_ builtinFunc = &builtinRepeatSig{}
_ builtinFunc = &builtinLowerSig{}
_ builtinFunc = &builtinReverseSig{}
_ builtinFunc = &builtinReverseBinarySig{}
_ builtinFunc = &builtinSpaceSig{}
_ builtinFunc = &builtinUpperSig{}
_ builtinFunc = &builtinStrcmpSig{}
_ builtinFunc = &builtinReplaceSig{}
_ builtinFunc = &builtinConvertSig{}
_ builtinFunc = &builtinSubstringBinary2ArgsSig{}
_ builtinFunc = &builtinSubstringBinary3ArgsSig{}
_ builtinFunc = &builtinSubstring2ArgsSig{}
_ builtinFunc = &builtinSubstring3ArgsSig{}
_ builtinFunc = &builtinSubstringIndexSig{}
_ builtinFunc = &builtinLocate2ArgsSig{}
_ builtinFunc = &builtinLocate3ArgsSig{}
_ builtinFunc = &builtinLocateBinary2ArgsSig{}
_ builtinFunc = &builtinLocateBinary3ArgsSig{}
_ builtinFunc = &builtinHexStrArgSig{}
_ builtinFunc = &builtinHexIntArgSig{}
_ builtinFunc = &builtinUnHexSig{}
_ builtinFunc = &builtinTrim1ArgSig{}
_ builtinFunc = &builtinTrim2ArgsSig{}
_ builtinFunc = &builtinTrim3ArgsSig{}
_ builtinFunc = &builtinLTrimSig{}
_ builtinFunc = &builtinRTrimSig{}
_ builtinFunc = &builtinLpadSig{}
_ builtinFunc = &builtinLpadBinarySig{}
_ builtinFunc = &builtinRpadSig{}
_ builtinFunc = &builtinRpadBinarySig{}
_ builtinFunc = &builtinBitLengthSig{}
_ builtinFunc = &builtinCharSig{}
_ builtinFunc = &builtinCharLengthSig{}
_ 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 = &builtinInsertBinarySig{}
_ builtinFunc = &builtinInsertSig{}
_ builtinFunc = &builtinInstrSig{}
_ builtinFunc = &builtinInstrBinarySig{}
_ builtinFunc = &builtinFieldRealSig{}
_ builtinFunc = &builtinFieldIntSig{}
_ builtinFunc = &builtinFieldStringSig{}
)
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) {
resTp.Flag |= mysql.BinaryFlag
}
}
type lengthFunctionClass struct {
baseFunctionClass
}
func (c *lengthFunctionClass) getFunction(ctx context.Context, args []Expression) (builtinFunc, error) {
if err := c.verifyArgs(args); err != nil {
return nil, errors.Trace(err)
}
bf := newBaseBuiltinFuncWithTp(ctx, args, types.ETInt, types.ETString)
bf.tp.Flen = 10
sig := &builtinLengthSig{bf}
return sig, nil
}
type builtinLengthSig struct {
baseBuiltinFunc
}
// evalInt evaluates a builtinLengthSig.
// See https://dev.mysql.com/doc/refman/5.7/en/string-functions.html
func (b *builtinLengthSig) evalInt(row []types.Datum) (int64, bool, error) {
val, isNull, err := b.args[0].EvalString(row, b.ctx.GetSessionVars().StmtCtx)
if isNull || err != nil {
return 0, isNull, errors.Trace(err)
}
return int64(len([]byte(val))), false, nil
}
type asciiFunctionClass struct {
baseFunctionClass
}
func (c *asciiFunctionClass) getFunction(ctx context.Context, args []Expression) (builtinFunc, error) {
if err := c.verifyArgs(args); err != nil {
return nil, errors.Trace(err)
}
bf := newBaseBuiltinFuncWithTp(ctx, args, types.ETInt, types.ETString)
bf.tp.Flen = 3
sig := &builtinASCIISig{bf}
return sig, nil
}
type builtinASCIISig struct {
baseBuiltinFunc
}
// eval evals a builtinASCIISig.
// See https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_ascii
func (b *builtinASCIISig) evalInt(row []types.Datum) (int64, bool, error) {
val, isNull, err := b.args[0].EvalString(row, b.ctx.GetSessionVars().StmtCtx)
if isNull || err != nil {
return 0, isNull, errors.Trace(err)
}
if len(val) == 0 {
return 0, false, nil
}
return int64(val[0]), false, nil
}
type concatFunctionClass struct {
baseFunctionClass
}
func (c *concatFunctionClass) getFunction(ctx context.Context, args []Expression) (builtinFunc, error) {
if err := c.verifyArgs(args); err != nil {
return nil, errors.Trace(err)
}
argTps := make([]types.EvalType, 0, len(args))
for i := 0; i < len(args); i++ {
argTps = append(argTps, types.ETString)
}
bf := newBaseBuiltinFuncWithTp(ctx, args, types.ETString, argTps...)
for i := range args {
argType := args[i].GetType()
SetBinFlagOrBinStr(argType, bf.tp)
if argType.Flen < 0 {
bf.tp.Flen = mysql.MaxBlobWidth
log.Warningf("Not Expected: `Flen` of arg[%v] in CONCAT is -1.", i)
}
bf.tp.Flen += argType.Flen
}
if bf.tp.Flen >= mysql.MaxBlobWidth {
bf.tp.Flen = mysql.MaxBlobWidth
}
sig := &builtinConcatSig{bf}
return sig, nil
}
type builtinConcatSig struct {
baseBuiltinFunc
}
// See https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_concat
func (b *builtinConcatSig) evalString(row []types.Datum) (d string, isNull bool, err error) {
var s []byte
for _, a := range b.getArgs() {
d, isNull, err = a.EvalString(row, b.ctx.GetSessionVars().StmtCtx)
if isNull || err != nil {
return d, isNull, errors.Trace(err)
}
s = append(s, []byte(d)...)
}
return string(s), false, nil
}
type concatWSFunctionClass struct {
baseFunctionClass
}
func (c *concatWSFunctionClass) getFunction(ctx context.Context, args []Expression) (builtinFunc, error) {
if err := c.verifyArgs(args); err != nil {
return nil, errors.Trace(err)
}
argTps := make([]types.EvalType, 0, len(args))
for i := 0; i < len(args); i++ {
argTps = append(argTps, types.ETString)
}
bf := newBaseBuiltinFuncWithTp(ctx, args, types.ETString, argTps...)
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
log.Warningf("Not Expected: `Flen` of arg[%v] in CONCAT_WS is -1.", 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
}
sig := &builtinConcatWSSig{bf}
return sig, nil
}
type builtinConcatWSSig struct {
baseBuiltinFunc
}
// evalString evals a builtinConcatWSSig.
// See https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_concat-ws
func (b *builtinConcatWSSig) evalString(row []types.Datum) (string, bool, error) {
args := b.getArgs()
strs := make([]string, 0, len(args))
var sep string
for i, arg := range args {
val, isNull, err := arg.EvalString(row, b.ctx.GetSessionVars().StmtCtx)
if err != nil {
return val, isNull, errors.Trace(err)
}
if isNull {
// If the separator is NULL, the result is NULL.
if i == 0 {
return val, isNull, nil
}
// CONCAT_WS() does not skip empty strings. However,
// it does skip any NULL values after the separator argument.
continue
}
if i == 0 {
sep = val
continue
}
strs = append(strs, val)
}
// TODO: check whether the length of result is larger than Flen
return strings.Join(strs, sep), false, nil
}
type leftFunctionClass struct {
baseFunctionClass
}
func (c *leftFunctionClass) getFunction(ctx context.Context, args []Expression) (builtinFunc, error) {
if err := c.verifyArgs(args); err != nil {
return nil, errors.Trace(err)
}
bf := newBaseBuiltinFuncWithTp(ctx, args, types.ETString, types.ETString, types.ETInt)
argType := args[0].GetType()
bf.tp.Flen = argType.Flen
SetBinFlagOrBinStr(argType, bf.tp)
if types.IsBinaryStr(argType) {
sig := &builtinLeftBinarySig{bf}
return sig, nil
}
sig := &builtinLeftSig{bf}
return sig, nil
}
type builtinLeftBinarySig struct {
baseBuiltinFunc
}
// evalString evals LEFT(str,len).
// See https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_left
func (b *builtinLeftBinarySig) evalString(row []types.Datum) (string, bool, error) {
sc := b.ctx.GetSessionVars().StmtCtx
str, isNull, err := b.args[0].EvalString(row, sc)
if isNull || err != nil {
return "", true, errors.Trace(err)
}
left, isNull, err := b.args[1].EvalInt(row, sc)
if isNull || err != nil {
return "", true, errors.Trace(err)
}
leftLength := int(left)
if strLength := len(str); leftLength > strLength {
leftLength = strLength
} else if leftLength < 0 {
leftLength = 0
}
return str[:leftLength], false, nil
}
type builtinLeftSig struct {
baseBuiltinFunc
}
// 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 []types.Datum) (string, bool, error) {
sc := b.ctx.GetSessionVars().StmtCtx
str, isNull, err := b.args[0].EvalString(row, sc)
if isNull || err != nil {
return "", true, errors.Trace(err)
}
left, isNull, err := b.args[1].EvalInt(row, sc)
if isNull || err != nil {
return "", true, errors.Trace(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 context.Context, args []Expression) (builtinFunc, error) {
if err := c.verifyArgs(args); err != nil {
return nil, errors.Trace(err)
}
bf := newBaseBuiltinFuncWithTp(ctx, args, types.ETString, types.ETString, types.ETInt)
argType := args[0].GetType()
bf.tp.Flen = argType.Flen
SetBinFlagOrBinStr(argType, bf.tp)
if types.IsBinaryStr(argType) {
sig := &builtinRightBinarySig{bf}
return sig, nil
}
sig := &builtinRightSig{bf}
return sig, nil
}
type builtinRightBinarySig struct {
baseBuiltinFunc
}
// evalString evals RIGHT(str,len).
// See https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_right
func (b *builtinRightBinarySig) evalString(row []types.Datum) (string, bool, error) {
sc := b.ctx.GetSessionVars().StmtCtx
str, isNull, err := b.args[0].EvalString(row, sc)
if isNull || err != nil {
return "", true, errors.Trace(err)
}
right, isNull, err := b.args[1].EvalInt(row, sc)
if isNull || err != nil {
return "", true, errors.Trace(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 builtinRightSig struct {
baseBuiltinFunc
}
// 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 []types.Datum) (string, bool, error) {
sc := b.ctx.GetSessionVars().StmtCtx
str, isNull, err := b.args[0].EvalString(row, sc)
if isNull || err != nil {
return "", true, errors.Trace(err)
}
right, isNull, err := b.args[1].EvalInt(row, sc)
if isNull || err != nil {
return "", true, errors.Trace(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 context.Context, args []Expression) (builtinFunc, error) {
if err := c.verifyArgs(args); err != nil {
return nil, errors.Trace(err)
}
bf := newBaseBuiltinFuncWithTp(ctx, args, types.ETString, types.ETString, types.ETInt)
bf.tp.Flen = mysql.MaxBlobWidth
SetBinFlagOrBinStr(args[0].GetType(), bf.tp)
sig := &builtinRepeatSig{bf}
return sig, nil
}
type builtinRepeatSig struct {
baseBuiltinFunc
}
// eval evals a builtinRepeatSig.
// See https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_repeat
func (b *builtinRepeatSig) evalString(row []types.Datum) (d string, isNull bool, err error) {
sc := b.ctx.GetSessionVars().StmtCtx
str, isNull, err := b.args[0].EvalString(row, sc)
if isNull || err != nil {
return "", isNull, errors.Trace(err)
}
num, isNull, err := b.args[1].EvalInt(row, sc)
if isNull || err != nil {
return "", isNull, errors.Trace(err)
}
if num < 1 {
return "", false, nil
}
if num > math.MaxInt32 {
num = math.MaxInt32
}
if int64(len(str)) > 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 context.Context, args []Expression) (builtinFunc, error) {
if err := c.verifyArgs(args); err != nil {
return nil, errors.Trace(err)
}
bf := newBaseBuiltinFuncWithTp(ctx, args, types.ETString, types.ETString)
argTp := args[0].GetType()
bf.tp.Flen = argTp.Flen
SetBinFlagOrBinStr(argTp, bf.tp)
sig := &builtinLowerSig{bf}
return sig, nil
}
type builtinLowerSig struct {
baseBuiltinFunc
}
// evalString evals a builtinLowerSig.
// See https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_lower
func (b *builtinLowerSig) evalString(row []types.Datum) (d string, isNull bool, err error) {
d, isNull, err = b.args[0].EvalString(row, b.ctx.GetSessionVars().StmtCtx)
if isNull || err != nil {
return d, isNull, errors.Trace(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 context.Context, args []Expression) (builtinFunc, error) {
if err := c.verifyArgs(args); err != nil {
return nil, errors.Trace(err)
}
bf := newBaseBuiltinFuncWithTp(ctx, args, types.ETString, types.ETString)
retTp := *args[0].GetType()
retTp.Tp = mysql.TypeVarString
retTp.Decimal = types.UnspecifiedLength
bf.tp = &retTp
var sig builtinFunc
if types.IsBinaryStr(bf.tp) {
sig = &builtinReverseBinarySig{bf}
} else {
sig = &builtinReverseSig{bf}
}
return sig, nil
}
type builtinReverseBinarySig struct {
baseBuiltinFunc
}
// evalString evals a REVERSE(str).
// See https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_reverse
func (b *builtinReverseBinarySig) evalString(row []types.Datum) (string, bool, error) {
str, isNull, err := b.args[0].EvalString(row, b.ctx.GetSessionVars().StmtCtx)
if isNull || err != nil {
return "", true, errors.Trace(err)
}
reversed := reverseBytes([]byte(str))
return string(reversed), false, nil
}
type builtinReverseSig struct {
baseBuiltinFunc
}
// 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 []types.Datum) (string, bool, error) {
str, isNull, err := b.args[0].EvalString(row, b.ctx.GetSessionVars().StmtCtx)
if isNull || err != nil {
return "", true, errors.Trace(err)
}
reversed := reverseRunes([]rune(str))
return string(reversed), false, nil
}
type spaceFunctionClass struct {
baseFunctionClass
}
func (c *spaceFunctionClass) getFunction(ctx context.Context, args []Expression) (builtinFunc, error) {
if err := c.verifyArgs(args); err != nil {
return nil, errors.Trace(err)
}
bf := newBaseBuiltinFuncWithTp(ctx, args, types.ETString, types.ETInt)
bf.tp.Flen = mysql.MaxBlobWidth
sig := &builtinSpaceSig{bf}
return sig, nil
}
type builtinSpaceSig struct {
baseBuiltinFunc
}
// evalString evals a builtinSpaceSig.
// See https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_space
func (b *builtinSpaceSig) evalString(row []types.Datum) (d string, isNull bool, err error) {
var x int64
x, isNull, err = b.args[0].EvalInt(row, b.ctx.GetSessionVars().StmtCtx)
if isNull || err != nil {
return d, isNull, errors.Trace(err)
}
if x > mysql.MaxBlobWidth {
return d, true, nil
}
if x < 0 {
x = 0
}
return strings.Repeat(" ", int(x)), false, nil
}
type upperFunctionClass struct {
baseFunctionClass
}
func (c *upperFunctionClass) getFunction(ctx context.Context, args []Expression) (builtinFunc, error) {
if err := c.verifyArgs(args); err != nil {
return nil, errors.Trace(err)
}
bf := newBaseBuiltinFuncWithTp(ctx, args, types.ETString, types.ETString)
argTp := args[0].GetType()
bf.tp.Flen = argTp.Flen
SetBinFlagOrBinStr(argTp, bf.tp)
sig := &builtinUpperSig{bf}
return sig, nil
}
type builtinUpperSig struct {
baseBuiltinFunc
}
// evalString evals a builtinUpperSig.
// See https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_upper
func (b *builtinUpperSig) evalString(row []types.Datum) (d string, isNull bool, err error) {
d, isNull, err = b.args[0].EvalString(row, b.ctx.GetSessionVars().StmtCtx)
if isNull || err != nil {
return d, isNull, errors.Trace(err)
}
if types.IsBinaryStr(b.args[0].GetType()) {
return d, false, nil
}
return strings.ToUpper(d), false, nil
}
type strcmpFunctionClass struct {
baseFunctionClass
}
func (c *strcmpFunctionClass) getFunction(ctx context.Context, args []Expression) (builtinFunc, error) {
if err := c.verifyArgs(args); err != nil {
return nil, errors.Trace(err)
}
bf := newBaseBuiltinFuncWithTp(ctx, args, types.ETInt, types.ETString, types.ETString)
bf.tp.Flen = 2
types.SetBinChsClnFlag(bf.tp)
sig := &builtinStrcmpSig{bf}
return sig, nil
}
type builtinStrcmpSig struct {
baseBuiltinFunc
}
// evalInt evals a builtinStrcmpSig.
// See https://dev.mysql.com/doc/refman/5.7/en/string-comparison-functions.html
func (b *builtinStrcmpSig) evalInt(row []types.Datum) (int64, bool, error) {
var (
left, right string
isNull bool
err error
)
sc := b.ctx.GetSessionVars().StmtCtx
left, isNull, err = b.args[0].EvalString(row, sc)
if isNull || err != nil {
return 0, isNull, errors.Trace(err)
}
right, isNull, err = b.args[1].EvalString(row, sc)
if isNull || err != nil {
return 0, isNull, errors.Trace(err)
}
res := types.CompareString(left, right)
return int64(res), false, nil
}
type replaceFunctionClass struct {
baseFunctionClass
}
func (c *replaceFunctionClass) getFunction(ctx context.Context, args []Expression) (builtinFunc, error) {
if err := c.verifyArgs(args); err != nil {
return nil, errors.Trace(err)
}
bf := newBaseBuiltinFuncWithTp(ctx, args, types.ETString, types.ETString, types.ETString, types.ETString)
bf.tp.Flen = c.fixLength(args)
for _, a := range args {
SetBinFlagOrBinStr(a.GetType(), bf.tp)
}
sig := &builtinReplaceSig{bf}
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
}
// evalString evals a builtinReplaceSig.
// See https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_replace
func (b *builtinReplaceSig) evalString(row []types.Datum) (d string, isNull bool, err error) {
var str, oldStr, newStr string
sc := b.ctx.GetSessionVars().StmtCtx
str, isNull, err = b.args[0].EvalString(row, sc)
if isNull || err != nil {
return d, isNull, errors.Trace(err)
}
oldStr, isNull, err = b.args[1].EvalString(row, sc)
if isNull || err != nil {
return d, isNull, errors.Trace(err)
}
newStr, isNull, err = b.args[2].EvalString(row, sc)
if isNull || err != nil {
return d, isNull, errors.Trace(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 context.Context, args []Expression) (builtinFunc, error) {
if err := c.verifyArgs(args); err != nil {
return nil, errors.Trace(err)
}
bf := newBaseBuiltinFuncWithTp(ctx, args, types.ETString, types.ETString, types.ETString)
// TODO: issue #4436: The second parameter should be a constant.
// TODO: issue #4474: Charset supported by TiDB and MySQL is not the same.
// TODO: Fix #4436 && #4474, set the correct charset and flag of `bf.tp`.
bf.tp.Flen = mysql.MaxBlobWidth
sig := &builtinConvertSig{bf}
return sig, nil
}
type builtinConvertSig struct {
baseBuiltinFunc
}
// evalString evals CONVERT(expr USING transcoding_name).
// See https://dev.mysql.com/doc/refman/5.7/en/cast-functions.html#function_convert
func (b *builtinConvertSig) evalString(row []types.Datum) (string, bool, error) {
sc := b.ctx.GetSessionVars().StmtCtx
expr, isNull, err := b.args[0].EvalString(row, sc)
if isNull || err != nil {
return "", true, errors.Trace(err)
}
charsetName, isNull, err := b.args[1].EvalString(row, sc)
if isNull || err != nil {
return "", true, errors.Trace(err)
}
encoding, _ := charset.Lookup(charsetName)
if encoding == nil {
return "", true, errUnknownCharacterSet.GenByArgs(charsetName)
}
target, _, err := transform.String(encoding.NewDecoder(), expr)
return target, err != nil, errors.Trace(err)
}
type substringFunctionClass struct {
baseFunctionClass
}
func (c *substringFunctionClass) getFunction(ctx context.Context, args []Expression) (builtinFunc, error) {
if err := c.verifyArgs(args); err != nil {
return nil, errors.Trace(err)
}
argTps := []types.EvalType{types.ETString, types.ETInt}
if len(args) == 3 {
argTps = append(argTps, types.ETInt)
}
bf := newBaseBuiltinFuncWithTp(ctx, args, types.ETString, argTps...)
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 = &builtinSubstringBinary3ArgsSig{bf}
case len(args) == 3:
sig = &builtinSubstring3ArgsSig{bf}
case len(args) == 2 && types.IsBinaryStr(argType):
sig = &builtinSubstringBinary2ArgsSig{bf}
case len(args) == 2:
sig = &builtinSubstring2ArgsSig{bf}
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 builtinSubstringBinary2ArgsSig struct {
baseBuiltinFunc
}
// 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 *builtinSubstringBinary2ArgsSig) evalString(row []types.Datum) (string, bool, error) {
sc := b.ctx.GetSessionVars().StmtCtx
str, isNull, err := b.args[0].EvalString(row, sc)
if isNull || err != nil {
return "", true, errors.Trace(err)
}
pos, isNull, err := b.args[1].EvalInt(row, sc)
if isNull || err != nil {
return "", true, errors.Trace(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 builtinSubstring2ArgsSig struct {
baseBuiltinFunc
}
// 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 []types.Datum) (string, bool, error) {
sc := b.ctx.GetSessionVars().StmtCtx
str, isNull, err := b.args[0].EvalString(row, sc)
if isNull || err != nil {
return "", true, errors.Trace(err)
}
pos, isNull, err := b.args[1].EvalInt(row, sc)
if isNull || err != nil {
return "", true, errors.Trace(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 builtinSubstringBinary3ArgsSig struct {
baseBuiltinFunc
}
// 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 *builtinSubstringBinary3ArgsSig) evalString(row []types.Datum) (string, bool, error) {
sc := b.ctx.GetSessionVars().StmtCtx
str, isNull, err := b.args[0].EvalString(row, sc)
if isNull || err != nil {
return "", true, errors.Trace(err)
}
pos, isNull, err := b.args[1].EvalInt(row, sc)
if isNull || err != nil {
return "", true, errors.Trace(err)
}
length, isNull, err := b.args[2].EvalInt(row, sc)
if isNull || err != nil {
return "", true, errors.Trace(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 builtinSubstring3ArgsSig struct {
baseBuiltinFunc
}
// 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 []types.Datum) (string, bool, error) {
sc := b.ctx.GetSessionVars().StmtCtx
str, isNull, err := b.args[0].EvalString(row, sc)
if isNull || err != nil {
return "", true, errors.Trace(err)
}
pos, isNull, err := b.args[1].EvalInt(row, sc)
if isNull || err != nil {
return "", true, errors.Trace(err)
}
length, isNull, err := b.args[2].EvalInt(row, sc)
if isNull || err != nil {
return "", true, errors.Trace(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 context.Context, args []Expression) (builtinFunc, error) {
if err := c.verifyArgs(args); err != nil {
return nil, errors.Trace(err)
}
bf := newBaseBuiltinFuncWithTp(ctx, args, types.ETString, types.ETString, types.ETString, types.ETInt)
argType := args[0].GetType()
bf.tp.Flen = argType.Flen
SetBinFlagOrBinStr(argType, bf.tp)
sig := &builtinSubstringIndexSig{bf}
return sig, nil
}
type builtinSubstringIndexSig struct {
baseBuiltinFunc
}
// 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 []types.Datum) (d string, isNull bool, err error) {
var (
str, delim string
count int64
)
sc := b.ctx.GetSessionVars().StmtCtx
str, isNull, err = b.args[0].EvalString(row, sc)
if isNull || err != nil {
return d, isNull, errors.Trace(err)
}
delim, isNull, err = b.args[1].EvalString(row, sc)
if isNull || err != nil {
return d, isNull, errors.Trace(err)
}
count, isNull, err = b.args[2].EvalInt(row, sc)
if isNull || err != nil {
return d, isNull, errors.Trace(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 < end {
start = end - count
}
}
substrs := strs[start:end]
return strings.Join(substrs, delim), false, nil
}
type locateFunctionClass struct {
baseFunctionClass
}
func (c *locateFunctionClass) getFunction(ctx context.Context, args []Expression) (builtinFunc, error) {
if err := c.verifyArgs(args); err != nil {
return nil, errors.Trace(err)
}
hasStartPos, argTps := len(args) == 3, []types.EvalType{types.ETString, types.ETString}
if hasStartPos {
argTps = append(argTps, types.ETInt)
}
bf := newBaseBuiltinFuncWithTp(ctx, args, types.ETInt, argTps...)
var sig builtinFunc
// Loacte is multibyte safe, and is case-sensitive only if at least one argument is a binary string.
hasBianryInput := types.IsBinaryStr(args[0].GetType()) || types.IsBinaryStr(args[1].GetType())
switch {
case hasStartPos && hasBianryInput:
sig = &builtinLocateBinary3ArgsSig{bf}
case hasStartPos:
sig = &builtinLocate3ArgsSig{bf}
case hasBianryInput:
sig = &builtinLocateBinary2ArgsSig{bf}
default:
sig = &builtinLocate2ArgsSig{bf}
}
return sig, nil
}
type builtinLocateBinary2ArgsSig struct {
baseBuiltinFunc
}
// evalInt evals LOCATE(substr,str), case-sensitive.
// See https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_locate
func (b *builtinLocateBinary2ArgsSig) evalInt(row []types.Datum) (int64, bool, error) {
sc := b.ctx.GetSessionVars().StmtCtx
subStr, isNull, err := b.args[0].EvalString(row, sc)
if isNull || err != nil {
return 0, isNull, errors.Trace(err)
}
str, isNull, err := b.args[1].EvalString(row, sc)
if isNull || err != nil {
return 0, isNull, errors.Trace(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 builtinLocate2ArgsSig struct {
baseBuiltinFunc
}
// evalInt evals LOCATE(substr,str), non case-sensitive.
// See https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_locate
func (b *builtinLocate2ArgsSig) evalInt(row []types.Datum) (int64, bool, error) {
sc := b.ctx.GetSessionVars().StmtCtx
subStr, isNull, err := b.args[0].EvalString(row, sc)
if isNull || err != nil {
return 0, isNull, errors.Trace(err)
}
str, isNull, err := b.args[1].EvalString(row, sc)
if isNull || err != nil {
return 0, isNull, errors.Trace(err)
}
if int64(len([]rune(subStr))) == 0 {
return 1, false, nil
}
slice := string([]rune(strings.ToLower(str)))
ret, idx := 0, strings.Index(slice, strings.ToLower(subStr))
if idx != -1 {
ret = utf8.RuneCountInString(slice[:idx]) + 1
}
return int64(ret), false, nil
}
type builtinLocateBinary3ArgsSig struct {
baseBuiltinFunc
}
// 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 *builtinLocateBinary3ArgsSig) evalInt(row []types.Datum) (int64, bool, error) {
sc := b.ctx.GetSessionVars().StmtCtx
subStr, isNull, err := b.args[0].EvalString(row, sc)
if isNull || err != nil {
return 0, isNull, errors.Trace(err)
}
str, isNull, err := b.args[1].EvalString(row, sc)
if isNull || err != nil {
return 0, isNull, errors.Trace(err)
}
pos, isNull, err := b.args[2].EvalInt(row, sc)
// Transfer the argument which starts from 1 to real index which starts from 0.
pos--
if isNull || err != nil {
return 0, isNull, errors.Trace(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 builtinLocate3ArgsSig struct {
baseBuiltinFunc
}
// evalInt evals LOCATE(substr,str,pos), non case-sensitive.
// See https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_locate
func (b *builtinLocate3ArgsSig) evalInt(row []types.Datum) (int64, bool, error) {
sc := b.ctx.GetSessionVars().StmtCtx
subStr, isNull, err := b.args[0].EvalString(row, sc)
if isNull || err != nil {
return 0, isNull, errors.Trace(err)
}
str, isNull, err := b.args[1].EvalString(row, sc)
if isNull || err != nil {
return 0, isNull, errors.Trace(err)
}
pos, isNull, err := b.args[2].EvalInt(row, sc)
// Transfer the argument which starts from 1 to real index which starts from 0.
pos--
if isNull || err != nil {
return 0, isNull, errors.Trace(err)
}
subStrLen := len([]rune(subStr))
if pos < 0 || pos > int64(len([]rune(strings.ToLower(str)))-subStrLen) {
return 0, false, nil
} else if subStrLen == 0 {
return pos + 1, false, nil
}
slice := string([]rune(strings.ToLower(str))[pos:])
idx := strings.Index(slice, strings.ToLower(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 context.Context, args []Expression) (builtinFunc, error) {
if err := c.verifyArgs(args); err != nil {
return nil, errors.Trace(err)
}
argTp := args[0].GetType().EvalType()
switch argTp {
case types.ETString, types.ETDatetime, types.ETTimestamp, types.ETDuration, types.ETJson:
bf := newBaseBuiltinFuncWithTp(ctx, args, types.ETString, types.ETString)
// Use UTF-8 as default
bf.tp.Flen = args[0].GetType().Flen * 3 * 2
sig := &builtinHexStrArgSig{bf}
return sig, nil
case types.ETInt, types.ETReal, types.ETDecimal:
bf := newBaseBuiltinFuncWithTp(ctx, args, types.ETString, types.ETInt)
bf.tp.Flen = args[0].GetType().Flen * 2
sig := &builtinHexIntArgSig{bf}
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
}
// 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 []types.Datum) (string, bool, error) {
d, isNull, err := b.args[0].EvalString(row, b.ctx.GetSessionVars().StmtCtx)
if isNull || err != nil {
return d, isNull, errors.Trace(err)
}
return strings.ToUpper(hex.EncodeToString(hack.Slice(d))), false, nil
}
type builtinHexIntArgSig struct {
baseBuiltinFunc
}
// 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 []types.Datum) (string, bool, error) {
x, isNull, err := b.args[0].EvalInt(row, b.ctx.GetSessionVars().StmtCtx)
if isNull || err != nil {
return "", isNull, errors.Trace(err)
}
return strings.ToUpper(fmt.Sprintf("%x", uint64(x))), false, nil
}
type unhexFunctionClass struct {
baseFunctionClass
}
func (c *unhexFunctionClass) getFunction(ctx context.Context, args []Expression) (builtinFunc, error) {
if err := c.verifyArgs(args); err != nil {
return nil, errors.Trace(err)
}
var retFlen int
if err := c.verifyArgs(args); err != nil {
return nil, errors.Trace(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 := newBaseBuiltinFuncWithTp(ctx, args, types.ETString, types.ETString)
bf.tp.Flen = retFlen
types.SetBinChsClnFlag(bf.tp)
sig := &builtinUnHexSig{bf}
return sig, nil
}
type builtinUnHexSig struct {
baseBuiltinFunc
}
// evalString evals a builtinUnHexSig.
// See https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_unhex
func (b *builtinUnHexSig) evalString(row []types.Datum) (string, bool, error) {
var bs []byte
d, isNull, err := b.args[0].EvalString(row, b.ctx.GetSessionVars().StmtCtx)
if isNull || err != nil {
return d, isNull, errors.Trace(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 = "\n\t\r "
type trimFunctionClass struct {
baseFunctionClass
}
// 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 context.Context, args []Expression) (builtinFunc, error) {
if err := c.verifyArgs(args); err != nil {
return nil, errors.Trace(err)
}
switch len(args) {
case 1:
bf := newBaseBuiltinFuncWithTp(ctx, args, types.ETString, types.ETString)
argType := args[0].GetType()
bf.tp.Flen = argType.Flen
SetBinFlagOrBinStr(argType, bf.tp)
sig := &builtinTrim1ArgSig{bf}
return sig, nil
case 2:
bf := newBaseBuiltinFuncWithTp(ctx, args, types.ETString, types.ETString, types.ETString)
argType := args[0].GetType()
SetBinFlagOrBinStr(argType, bf.tp)
sig := &builtinTrim2ArgsSig{bf}
return sig, nil
case 3:
bf := newBaseBuiltinFuncWithTp(ctx, args, types.ETString, types.ETString, types.ETString, types.ETInt)
argType := args[0].GetType()
bf.tp.Flen = argType.Flen
SetBinFlagOrBinStr(argType, bf.tp)
sig := &builtinTrim3ArgsSig{bf}
return sig, nil
default:
return nil, errors.Trace(c.verifyArgs(args))
}
}
type builtinTrim1ArgSig struct {
baseBuiltinFunc
}
// 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 []types.Datum) (d string, isNull bool, err error) {
d, isNull, err = b.args[0].EvalString(row, b.ctx.GetSessionVars().StmtCtx)
if isNull || err != nil {
return d, isNull, errors.Trace(err)
}
return strings.Trim(d, spaceChars), false, nil
}
type builtinTrim2ArgsSig struct {
baseBuiltinFunc
}
// 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 []types.Datum) (d string, isNull bool, err error) {
var str, remstr string
sc := b.ctx.GetSessionVars().StmtCtx
str, isNull, err = b.args[0].EvalString(row, sc)
if isNull || err != nil {
return d, isNull, errors.Trace(err)
}
remstr, isNull, err = b.args[1].EvalString(row, sc)
if isNull || err != nil {
return d, isNull, errors.Trace(err)
}
d = trimLeft(str, remstr)
d = trimRight(d, remstr)
return d, false, nil
}
type builtinTrim3ArgsSig struct {
baseBuiltinFunc
}
// 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 []types.Datum) (d string, isNull bool, err error) {
var (
str, remstr string
x int64
direction ast.TrimDirectionType
isRemStrNull bool
)
sc := b.ctx.GetSessionVars().StmtCtx
str, isNull, err = b.args[0].EvalString(row, sc)
if isNull || err != nil {
return d, isNull, errors.Trace(err)
}
remstr, isRemStrNull, err = b.args[1].EvalString(row, sc)
if err != nil {
return d, isNull, errors.Trace(err)
}
x, isNull, err = b.args[2].EvalInt(row, sc)
if isNull || err != nil {
return d, isNull, errors.Trace(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 context.Context, args []Expression) (builtinFunc, error) {
if err := c.verifyArgs(args); err != nil {
return nil, errors.Trace(err)
}
bf := newBaseBuiltinFuncWithTp(ctx, args, types.ETString, types.ETString)
argType := args[0].GetType()
bf.tp.Flen = argType.Flen
SetBinFlagOrBinStr(argType, bf.tp)
sig := &builtinLTrimSig{bf}
return sig, nil
}
type builtinLTrimSig struct {
baseBuiltinFunc
}
// evalString evals a builtinLTrimSig
// See https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_ltrim
func (b *builtinLTrimSig) evalString(row []types.Datum) (d string, isNull bool, err error) {
d, isNull, err = b.args[0].EvalString(row, b.ctx.GetSessionVars().StmtCtx)
if isNull || err != nil {
return d, isNull, errors.Trace(err)
}
return strings.TrimLeft(d, spaceChars), false, nil
}
type rTrimFunctionClass struct {
baseFunctionClass
}
func (c *rTrimFunctionClass) getFunction(ctx context.Context, args []Expression) (builtinFunc, error) {
if err := c.verifyArgs(args); err != nil {
return nil, errors.Trace(err)
}
bf := newBaseBuiltinFuncWithTp(ctx, args, types.ETString, types.ETString)
argType := args[0].GetType()
bf.tp.Flen = argType.Flen
SetBinFlagOrBinStr(argType, bf.tp)
sig := &builtinRTrimSig{bf}
return sig, nil
}
type builtinRTrimSig struct {
baseBuiltinFunc
}
// evalString evals a builtinRTrimSig
// See https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_rtrim
func (b *builtinRTrimSig) evalString(row []types.Datum) (d string, isNull bool, err error) {
d, isNull, err = b.args[0].EvalString(row, b.ctx.GetSessionVars().StmtCtx)
if isNull || err != nil {
return d, isNull, errors.Trace(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(sc *variable.StatementContext, arg Expression) int {
if constant, ok := arg.(*Constant); ok {
length, isNull, err := constant.EvalInt(nil, sc)
if err != nil {
log.Errorf("getFlen4LpadAndRpad with error: %v", err.Error())
}
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 context.Context, args []Expression) (builtinFunc, error) {
if err := c.verifyArgs(args); err != nil {
return nil, errors.Trace(err)
}
bf := newBaseBuiltinFuncWithTp(ctx, args, types.ETString, types.ETString, types.ETInt, types.ETString)
bf.tp.Flen = getFlen4LpadAndRpad(bf.ctx.GetSessionVars().StmtCtx, args[1])
SetBinFlagOrBinStr(args[0].GetType(), bf.tp)
SetBinFlagOrBinStr(args[2].GetType(), bf.tp)
if types.IsBinaryStr(args[0].GetType()) || types.IsBinaryStr(args[2].GetType()) {
sig := &builtinLpadBinarySig{bf}
return sig, nil
}
if bf.tp.Flen *= 4; bf.tp.Flen > mysql.MaxBlobWidth {
bf.tp.Flen = mysql.MaxBlobWidth
}
sig := &builtinLpadSig{bf}
return sig, nil
}
type builtinLpadBinarySig struct {
baseBuiltinFunc
}
// evalString evals LPAD(str,len,padstr).
// See https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_lpad
func (b *builtinLpadBinarySig) evalString(row []types.Datum) (string, bool, error) {
sc := b.ctx.GetSessionVars().StmtCtx
str, isNull, err := b.args[0].EvalString(row, sc)
if isNull || err != nil {
return "", true, errors.Trace(err)
}
byteLength := len(str)
length, isNull, err := b.args[1].EvalInt(row, sc)
if isNull || err != nil {
return "", true, errors.Trace(err)
}
targetLength := int(length)
padStr, isNull, err := b.args[2].EvalString(row, sc)
if isNull || err != nil {
return "", true, errors.Trace(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 builtinLpadSig struct {
baseBuiltinFunc
}
// 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 []types.Datum) (string, bool, error) {
sc := b.ctx.GetSessionVars().StmtCtx
str, isNull, err := b.args[0].EvalString(row, sc)
if isNull || err != nil {
return "", true, errors.Trace(err)
}
runeLength := len([]rune(str))
length, isNull, err := b.args[1].EvalInt(row, sc)
if isNull || err != nil {
return "", true, errors.Trace(err)
}
targetLength := int(length)
padStr, isNull, err := b.args[2].EvalString(row, sc)
if isNull || err != nil {
return "", true, errors.Trace(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 context.Context, args []Expression) (builtinFunc, error) {
if err := c.verifyArgs(args); err != nil {
return nil, errors.Trace(err)
}
bf := newBaseBuiltinFuncWithTp(ctx, args, types.ETString, types.ETString, types.ETInt, types.ETString)
bf.tp.Flen = getFlen4LpadAndRpad(bf.ctx.GetSessionVars().StmtCtx, args[1])
SetBinFlagOrBinStr(args[0].GetType(), bf.tp)
SetBinFlagOrBinStr(args[2].GetType(), bf.tp)
if types.IsBinaryStr(args[0].GetType()) || types.IsBinaryStr(args[2].GetType()) {
sig := &builtinRpadBinarySig{bf}
return sig, nil
}
if bf.tp.Flen *= 4; bf.tp.Flen > mysql.MaxBlobWidth {
bf.tp.Flen = mysql.MaxBlobWidth
}
sig := &builtinRpadSig{bf}
return sig, nil
}
type builtinRpadBinarySig struct {
baseBuiltinFunc
}
// evalString evals RPAD(str,len,padstr).
// See https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_rpad
func (b *builtinRpadBinarySig) evalString(row []types.Datum) (string, bool, error) {
sc := b.ctx.GetSessionVars().StmtCtx
str, isNull, err := b.args[0].EvalString(row, sc)
if isNull || err != nil {
return "", true, errors.Trace(err)
}
byteLength := len(str)
length, isNull, err := b.args[1].EvalInt(row, sc)
if isNull || err != nil {
return "", true, errors.Trace(err)
}
targetLength := int(length)
padStr, isNull, err := b.args[2].EvalString(row, sc)
if isNull || err != nil {
return "", true, errors.Trace(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 builtinRpadSig struct {
baseBuiltinFunc
}
// 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 []types.Datum) (string, bool, error) {
sc := b.ctx.GetSessionVars().StmtCtx
str, isNull, err := b.args[0].EvalString(row, sc)
if isNull || err != nil {
return "", true, errors.Trace(err)
}
runeLength := len([]rune(str))
length, isNull, err := b.args[1].EvalInt(row, sc)
if isNull || err != nil {
return "", true, errors.Trace(err)
}
targetLength := int(length)
padStr, isNull, err := b.args[2].EvalString(row, sc)
if isNull || err != nil {
return "", true, errors.Trace(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 context.Context, args []Expression) (builtinFunc, error) {
if err := c.verifyArgs(args); err != nil {
return nil, errors.Trace(err)
}
bf := newBaseBuiltinFuncWithTp(ctx, args, types.ETInt, types.ETString)
bf.tp.Flen = 10
sig := &builtinBitLengthSig{bf}
return sig, nil
}
type builtinBitLengthSig struct {
baseBuiltinFunc
}
// 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 []types.Datum) (int64, bool, error) {
val, isNull, err := b.args[0].EvalString(row, b.ctx.GetSessionVars().StmtCtx)
if isNull || err != nil {
return 0, isNull, errors.Trace(err)
}
return int64(len(val) * 8), false, nil
}
type charFunctionClass struct {
baseFunctionClass
}
func (c *charFunctionClass) getFunction(ctx context.Context, args []Expression) (builtinFunc, error) {
if err := c.verifyArgs(args); err != nil {
return nil, errors.Trace(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 := newBaseBuiltinFuncWithTp(ctx, args, types.ETString, argTps...)
bf.tp.Flen = 4 * (len(args) - 1)
types.SetBinChsClnFlag(bf.tp)
sig := &builtinCharSig{bf}
return sig, nil
}
type builtinCharSig struct {
baseBuiltinFunc
}
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 []types.Datum) (string, bool, error) {
bigints := make([]int64, 0, len(b.args)-1)
sc := b.ctx.GetSessionVars().StmtCtx
for i := 0; i < len(b.args)-1; i++ {
val, IsNull, err := b.args[i].EvalInt(row, sc)
if err != nil {
return "", true, errors.Trace(err)
}
if IsNull {
continue
}
bigints = append(bigints, val)
}
// The last argument represents the charset name after "using".
// Use default charset utf8 if it is nil.
argCharset, IsNull, err := b.args[len(b.args)-1].EvalString(row, sc)
if err != nil {
return "", true, errors.Trace(err)
}
result := string(b.convertToBytes(bigints))
charsetLabel := strings.ToLower(argCharset)
if IsNull || charsetLabel == "ascii" || strings.HasPrefix(charsetLabel, "utf8") {
return result, false, nil
}
encoding, charsetName := charset.Lookup(charsetLabel)
if encoding == nil {
return "", true, errors.Errorf("unknown encoding: %s", argCharset)
}
oldStr := result
result, _, err = transform.String(encoding.NewDecoder(), result)
if err != nil {
log.Errorf("Convert %s to %s with error: %v", oldStr, charsetName, err.Error())
return "", true, errors.Trace(err)
}
return result, false, nil
}
type charLengthFunctionClass struct {
baseFunctionClass
}
func (c *charLengthFunctionClass) getFunction(ctx context.Context, args []Expression) (builtinFunc, error) {
if argsErr := c.verifyArgs(args); argsErr != nil {
return nil, errors.Trace(argsErr)
}
bf := newBaseBuiltinFuncWithTp(ctx, args, types.ETInt, types.ETString)
sig := &builtinCharLengthSig{bf}
return sig, nil
}
type builtinCharLengthSig struct {
baseBuiltinFunc
}
// evalInt evals a builtinCharLengthSig.
// See https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_char-length
func (b *builtinCharLengthSig) evalInt(row []types.Datum) (int64, bool, error) {
val, isNull, err := b.args[0].EvalString(row, b.ctx.GetSessionVars().StmtCtx)
if isNull || err != nil {
return 0, isNull, errors.Trace(err)
}
return int64(len([]rune(val))), false, nil
}
type findInSetFunctionClass struct {
baseFunctionClass
}
func (c *findInSetFunctionClass) getFunction(ctx context.Context, args []Expression) (builtinFunc, error) {
if err := c.verifyArgs(args); err != nil {
return nil, errors.Trace(err)
}
bf := newBaseBuiltinFuncWithTp(ctx, args, types.ETInt, types.ETString, types.ETString)
bf.tp.Flen = 3
sig := &builtinFindInSetSig{bf}
return sig, nil
}
type builtinFindInSetSig struct {
baseBuiltinFunc
}
// 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 []types.Datum) (int64, bool, error) {
sc := b.ctx.GetSessionVars().StmtCtx
str, isNull, err := b.args[0].EvalString(row, sc)
if isNull || err != nil {
return 0, isNull, errors.Trace(err)
}
strlist, isNull, err := b.args[1].EvalString(row, sc)
if isNull || err != nil {
return 0, isNull, errors.Trace(err)
}
if len(strlist) == 0 {
return 0, false, nil
}
for i, strInSet := range strings.Split(strlist, ",") {
if str == strInSet {
return int64(i + 1), false, nil
}
}
return 0, false, nil
}
type fieldFunctionClass struct {
baseFunctionClass
}
func (c *fieldFunctionClass) getFunction(ctx context.Context, args []Expression) (builtinFunc, error) {
if err := c.verifyArgs(args); err != nil {
return nil, errors.Trace(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 := newBaseBuiltinFuncWithTp(ctx, args, types.ETInt, argTps...)
var sig builtinFunc
switch argTp {
case types.ETReal:
sig = &builtinFieldRealSig{bf}
case types.ETInt:
sig = &builtinFieldIntSig{bf}
case types.ETString:
sig = &builtinFieldStringSig{bf}
}
return sig, nil
}
type builtinFieldIntSig struct {
baseBuiltinFunc
}
// 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 []types.Datum) (int64, bool, error) {
sc := b.ctx.GetSessionVars().StmtCtx
str, isNull, err := b.args[0].EvalInt(row, sc)
if isNull || err != nil {
return 0, err != nil, errors.Trace(err)
}
for i, length := 1, len(b.args); i < length; i++ {
stri, isNull, err := b.args[i].EvalInt(row, sc)
if err != nil {
return 0, true, errors.Trace(err)
}
if !isNull && str == stri {
return int64(i), false, nil
}
}
return 0, false, nil
}
type builtinFieldRealSig struct {
baseBuiltinFunc
}
// 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 []types.Datum) (int64, bool, error) {
sc := b.ctx.GetSessionVars().StmtCtx
str, isNull, err := b.args[0].EvalReal(row, sc)
if isNull || err != nil {
return 0, err != nil, errors.Trace(err)
}
for i, length := 1, len(b.args); i < length; i++ {
stri, isNull, err := b.args[i].EvalReal(row, sc)
if err != nil {
return 0, true, errors.Trace(err)
}
if !isNull && str == stri {
return int64(i), false, nil
}
}
return 0, false, nil
}
type builtinFieldStringSig struct {
baseBuiltinFunc
}
// 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 []types.Datum) (int64, bool, error) {
sc := b.ctx.GetSessionVars().StmtCtx
str, isNull, err := b.args[0].EvalString(row, sc)
if isNull || err != nil {
return 0, err != nil, errors.Trace(err)
}
for i, length := 1, len(b.args); i < length; i++ {
stri, isNull, err := b.args[i].EvalString(row, sc)
if err != nil {
return 0, true, errors.Trace(err)
}
if !isNull && str == stri {
return int64(i), false, nil
}
}
return 0, false, nil
}
type makeSetFunctionClass struct {
baseFunctionClass
}
func (c *makeSetFunctionClass) getFlen(sc *variable.StatementContext, args []Expression) int {
flen, count := 0, 0
if constant, ok := args[0].(*Constant); ok {
bits, isNull, err := constant.EvalInt(nil, sc)
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 context.Context, args []Expression) (builtinFunc, error) {
if err := c.verifyArgs(args); err != nil {
return nil, errors.Trace(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 := newBaseBuiltinFuncWithTp(ctx, args, types.ETString, argTps...)
for i, length := 0, len(args); i < length; i++ {
SetBinFlagOrBinStr(args[i].GetType(), bf.tp)
}
bf.tp.Flen = c.getFlen(bf.ctx.GetSessionVars().StmtCtx, args)
if bf.tp.Flen > mysql.MaxBlobWidth {
bf.tp.Flen = mysql.MaxBlobWidth
}
sig := &builtinMakeSetSig{bf}
return sig, nil
}
type builtinMakeSetSig struct {
baseBuiltinFunc
}
// 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 []types.Datum) (string, bool, error) {
sc := b.ctx.GetSessionVars().StmtCtx
bits, isNull, err := b.args[0].EvalInt(row, sc)
if isNull || err != nil {
return "", true, errors.Trace(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(row, sc)
if err != nil {
return "", true, errors.Trace(err)
}
if !isNull {
sets = append(sets, str)
}
}
return strings.Join(sets, ","), false, nil
}
type octFunctionClass struct {
baseFunctionClass
}
func (c *octFunctionClass) getFunction(ctx context.Context, args []Expression) (builtinFunc, error) {
if err := c.verifyArgs(args); err != nil {
return nil, errors.Trace(err)
}
var sig builtinFunc
if IsHybridType(args[0]) || args[0].GetType().EvalType() == types.ETInt {
bf := newBaseBuiltinFuncWithTp(ctx, args, types.ETString, types.ETInt)
bf.tp.Flen, bf.tp.Decimal = 64, types.UnspecifiedLength
sig = &builtinOctIntSig{bf}
} else {
bf := newBaseBuiltinFuncWithTp(ctx, args, types.ETString, types.ETString)
bf.tp.Flen, bf.tp.Decimal = 64, types.UnspecifiedLength
sig = &builtinOctStringSig{bf}
}
return sig, nil
}
type builtinOctIntSig struct {
baseBuiltinFunc
}
// evalString evals OCT(N).
// See https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_oct
func (b *builtinOctIntSig) evalString(row []types.Datum) (string, bool, error) {
val, isNull, err := b.args[0].EvalInt(row, b.ctx.GetSessionVars().StmtCtx)
if isNull || err != nil {
return "", isNull, errors.Trace(err)
}
return strconv.FormatUint(uint64(val), 8), false, nil
}
type builtinOctStringSig struct {
baseBuiltinFunc
}
// // evalString evals OCT(N).
// // See https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_oct
func (b *builtinOctStringSig) evalString(row []types.Datum) (string, bool, error) {
sc := b.ctx.GetSessionVars().StmtCtx
val, isNull, err := b.args[0].EvalString(row, sc)
if isNull || err != nil {
return "", isNull, errors.Trace(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 context.Context, args []Expression) (builtinFunc, error) {
if err := c.verifyArgs(args); err != nil {
return nil, errors.Trace(err)
}
bf := newBaseBuiltinFuncWithTp(ctx, args, types.ETInt, types.ETString)
bf.tp.Flen = 10
sig := &builtinOrdSig{bf}
return sig, nil
}
type builtinOrdSig struct {
baseBuiltinFunc
}
// evalInt evals a builtinOrdSig.
// See https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_ord
func (b *builtinOrdSig) evalInt(row []types.Datum) (int64, bool, error) {
str, isNull, err := b.args[0].EvalString(row, b.ctx.GetSessionVars().StmtCtx)
if isNull || err != nil {
return 0, isNull, errors.Trace(err)
}
if len(str) == 0 {
return 0, false, nil
}
_, 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, false, nil
}
type quoteFunctionClass struct {
baseFunctionClass
}
func (c *quoteFunctionClass) getFunction(ctx context.Context, args []Expression) (builtinFunc, error) {
if err := c.verifyArgs(args); err != nil {
return nil, errors.Trace(err)
}
bf := newBaseBuiltinFuncWithTp(ctx, args, types.ETString, types.ETString)
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}
return sig, nil
}
type builtinQuoteSig struct {
baseBuiltinFunc
}
// evalString evals QUOTE(str).
// See https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_quote
func (b *builtinQuoteSig) evalString(row []types.Datum) (string, bool, error) {
sc := b.ctx.GetSessionVars().StmtCtx
str, isNull, err := b.args[0].EvalString(row, sc)
if isNull || err != nil {
return "", true, errors.Trace(err)
}
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(), false, nil
}
type binFunctionClass struct {
baseFunctionClass
}
func (c *binFunctionClass) getFunction(ctx context.Context, args []Expression) (builtinFunc, error) {
if err := c.verifyArgs(args); err != nil {
return nil, errors.Trace(err)
}
bf := newBaseBuiltinFuncWithTp(ctx, args, types.ETString, types.ETInt)
bf.tp.Flen = 64
sig := &builtinBinSig{bf}
return sig, nil
}
type builtinBinSig struct {
baseBuiltinFunc
}
// evalString evals BIN(N).
// See https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_bin
func (b *builtinBinSig) evalString(row []types.Datum) (string, bool, error) {
val, isNull, err := b.args[0].EvalInt(row, b.ctx.GetSessionVars().StmtCtx)
if isNull || err != nil {
return "", true, errors.Trace(err)
}
return fmt.Sprintf("%b", uint64(val)), false, nil
}
type eltFunctionClass struct {
baseFunctionClass
}
func (c *eltFunctionClass) getFunction(ctx context.Context, args []Expression) (builtinFunc, error) {
if argsErr := c.verifyArgs(args); argsErr != nil {
return nil, errors.Trace(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 := newBaseBuiltinFuncWithTp(ctx, args, types.ETString, argTps...)
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}
return sig, nil
}
type builtinEltSig struct {
baseBuiltinFunc
}
// evalString evals a builtinEltSig.
// See https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_elt
func (b *builtinEltSig) evalString(row []types.Datum) (string, bool, error) {
idx, isNull, err := b.args[0].EvalInt(row, b.ctx.GetSessionVars().StmtCtx)
if isNull || err != nil {
return "", true, errors.Trace(err)
}
if idx < 1 || idx >= int64(len(b.args)) {
return "", true, nil
}
arg, isNull, err := b.args[idx].EvalString(row, b.ctx.GetSessionVars().StmtCtx)
if isNull || err != nil {
return "", true, errors.Trace(err)
}
return arg, false, nil
}
type exportSetFunctionClass struct {
baseFunctionClass
}
func (c *exportSetFunctionClass) getFunction(ctx context.Context, args []Expression) (sig builtinFunc, err error) {
if err = c.verifyArgs(args); err != nil {
return nil, errors.Trace(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 := newBaseBuiltinFuncWithTp(ctx, args, types.ETString, argTps...)
bf.tp.Flen = mysql.MaxBlobWidth
switch len(args) {
case 3:
sig = &builtinExportSet3ArgSig{bf}
case 4:
sig = &builtinExportSet4ArgSig{bf}
case 5:
sig = &builtinExportSet5ArgSig{bf}
}
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
}
// 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 []types.Datum) (string, bool, error) {
sc := b.ctx.GetSessionVars().StmtCtx
bits, isNull, err := b.args[0].EvalInt(row, sc)
if isNull || err != nil {
return "", true, errors.Trace(err)
}
on, isNull, err := b.args[1].EvalString(row, sc)
if isNull || err != nil {
return "", true, errors.Trace(err)
}
off, isNull, err := b.args[2].EvalString(row, sc)
if isNull || err != nil {
return "", true, errors.Trace(err)
}
return exportSet(bits, on, off, ",", 64), false, nil
}
type builtinExportSet4ArgSig struct {
baseBuiltinFunc
}
// 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 []types.Datum) (string, bool, error) {
sc := b.ctx.GetSessionVars().StmtCtx
bits, isNull, err := b.args[0].EvalInt(row, sc)
if isNull || err != nil {
return "", true, errors.Trace(err)
}
on, isNull, err := b.args[1].EvalString(row, sc)
if isNull || err != nil {
return "", true, errors.Trace(err)
}
off, isNull, err := b.args[2].EvalString(row, sc)
if isNull || err != nil {
return "", true, errors.Trace(err)
}
separator, isNull, err := b.args[3].EvalString(row, sc)
if isNull || err != nil {
return "", true, errors.Trace(err)
}
return exportSet(bits, on, off, separator, 64), false, nil
}
type builtinExportSet5ArgSig struct {
baseBuiltinFunc
}
// 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 []types.Datum) (string, bool, error) {
sc := b.ctx.GetSessionVars().StmtCtx
bits, isNull, err := b.args[0].EvalInt(row, sc)
if isNull || err != nil {
return "", true, errors.Trace(err)
}
on, isNull, err := b.args[1].EvalString(row, sc)
if isNull || err != nil {
return "", true, errors.Trace(err)
}
off, isNull, err := b.args[2].EvalString(row, sc)
if isNull || err != nil {
return "", true, errors.Trace(err)
}
separator, isNull, err := b.args[3].EvalString(row, sc)
if isNull || err != nil {
return "", true, errors.Trace(err)
}
numberOfBits, isNull, err := b.args[4].EvalInt(row, sc)
if isNull || err != nil {
return "", true, errors.Trace(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 context.Context, args []Expression) (builtinFunc, error) {
if err := c.verifyArgs(args); err != nil {
return nil, errors.Trace(err)
}
argTps := make([]types.EvalType, 2, 3)
argTps[0], argTps[1] = types.ETString, types.ETString
if len(args) == 3 {
argTps = append(argTps, types.ETString)
}
bf := newBaseBuiltinFuncWithTp(ctx, args, types.ETString, argTps...)
bf.tp.Flen = mysql.MaxBlobWidth
var sig builtinFunc
if len(args) == 3 {
sig = &builtinFormatWithLocaleSig{bf}
} else {
sig = &builtinFormatSig{bf}
}
return sig, nil
}
type builtinFormatWithLocaleSig struct {
baseBuiltinFunc
}
// 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 []types.Datum) (string, bool, error) {
sc := b.ctx.GetSessionVars().StmtCtx
x, isNull, err := b.args[0].EvalString(row, sc)
if isNull || err != nil {
return "", true, errors.Trace(err)
}
d, isNull, err := b.args[1].EvalString(row, sc)
if isNull || err != nil {
return "", true, errors.Trace(err)
}
locale, isNull, err := b.args[2].EvalString(row, sc)
if isNull || err != nil {
return "", true, errors.Trace(err)
}
formatString, err := mysql.GetLocaleFormatFunction(locale)(x, d)
return formatString, err != nil, errors.Trace(err)
}
type builtinFormatSig struct {
baseBuiltinFunc
}
// 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 []types.Datum) (string, bool, error) {
sc := b.ctx.GetSessionVars().StmtCtx
x, isNull, err := b.args[0].EvalString(row, sc)
if isNull || err != nil {
return "", true, errors.Trace(err)
}
d, isNull, err := b.args[1].EvalString(row, sc)
if isNull || err != nil {
return "", true, errors.Trace(err)
}
formatString, err := mysql.GetLocaleFormatFunction("en_US")(x, d)
return formatString, err != nil, errors.Trace(err)
}
type fromBase64FunctionClass struct {
baseFunctionClass
}
func (c *fromBase64FunctionClass) getFunction(ctx context.Context, args []Expression) (builtinFunc, error) {
if err := c.verifyArgs(args); err != nil {
return nil, errors.Trace(err)
}
bf := newBaseBuiltinFuncWithTp(ctx, args, types.ETString, types.ETString)
bf.tp.Flen = mysql.MaxBlobWidth
types.SetBinChsClnFlag(bf.tp)
sig := &builtinFromBase64Sig{bf}
return sig, nil
}
type builtinFromBase64Sig struct {
baseBuiltinFunc
}
// 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 []types.Datum) (string, bool, error) {
str, isNull, err := b.args[0].EvalString(row, b.ctx.GetSessionVars().StmtCtx)
if isNull || err != nil {
return "", true, errors.Trace(err)
}
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 context.Context, args []Expression) (builtinFunc, error) {
if err := c.verifyArgs(args); err != nil {
return nil, errors.Trace(err)
}
bf := newBaseBuiltinFuncWithTp(ctx, args, types.ETString, types.ETString)
bf.tp.Flen = base64NeededEncodedLength(bf.args[0].GetType().Flen)
sig := &builtinToBase64Sig{bf}
return sig, nil
}
type builtinToBase64Sig struct {
baseBuiltinFunc
}
// 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 []types.Datum) (d string, isNull bool, err error) {
sc := b.ctx.GetSessionVars().StmtCtx
str, isNull, err := b.args[0].EvalString(row, sc)
if isNull || err != nil {
return "", isNull, errors.Trace(err)
}
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 context.Context, args []Expression) (sig builtinFunc, err error) {
if err = c.verifyArgs(args); err != nil {
return nil, errors.Trace(err)
}
bf := newBaseBuiltinFuncWithTp(ctx, args, types.ETString, types.ETString, types.ETInt, types.ETInt, types.ETString)
bf.tp.Flen = mysql.MaxBlobWidth
SetBinFlagOrBinStr(args[0].GetType(), bf.tp)
SetBinFlagOrBinStr(args[3].GetType(), bf.tp)
if types.IsBinaryStr(args[0].GetType()) {
sig = &builtinInsertBinarySig{bf}
} else {
sig = &builtinInsertSig{bf}
}
return sig, nil
}
type builtinInsertBinarySig struct {
baseBuiltinFunc
}
// evalString evals INSERT(str,pos,len,newstr).
// See https://dev.mysql.com/doc/refman/5.6/en/string-functions.html#function_insert
func (b *builtinInsertBinarySig) evalString(row []types.Datum) (string, bool, error) {
sc := b.ctx.GetSessionVars().StmtCtx
str, isNull, err := b.args[0].EvalString(row, sc)
if isNull || err != nil {
return "", true, errors.Trace(err)
}
strLength := int64(len(str))
pos, isNull, err := b.args[1].EvalInt(row, sc)
if isNull || err != nil {
return "", true, errors.Trace(err)
}
if pos < 1 || pos > strLength {
return str, false, nil
}
length, isNull, err := b.args[2].EvalInt(row, sc)
if isNull || err != nil {
return "", true, errors.Trace(err)
}
newstr, isNull, err := b.args[3].EvalString(row, sc)
if isNull || err != nil {
return "", true, errors.Trace(err)
}
if length > strLength-pos+1 || length < 0 {
return str[0:pos-1] + newstr, false, nil
}
return str[0:pos-1] + newstr + str[pos+length-1:], false, nil
}
type builtinInsertSig struct {
baseBuiltinFunc
}
// 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 []types.Datum) (string, bool, error) {
sc := b.ctx.GetSessionVars().StmtCtx
str, isNull, err := b.args[0].EvalString(row, sc)
if isNull || err != nil {
return "", true, errors.Trace(err)
}
runes := []rune(str)
runeLength := int64(len(runes))
pos, isNull, err := b.args[1].EvalInt(row, sc)
if isNull || err != nil {
return "", true, errors.Trace(err)
}
if pos < 1 || pos > runeLength {
return str, false, nil
}
length, isNull, err := b.args[2].EvalInt(row, sc)
if isNull || err != nil {
return "", true, errors.Trace(err)
}
newstr, isNull, err := b.args[3].EvalString(row, sc)
if isNull || err != nil {
return "", true, errors.Trace(err)
}
if length > runeLength-pos+1 || length < 0 {
return string(runes[0:pos-1]) + newstr, false, nil
}
return string(runes[0:pos-1]) + newstr + string(runes[pos+length-1:]), false, nil
}
type instrFunctionClass struct {
baseFunctionClass
}
func (c *instrFunctionClass) getFunction(ctx context.Context, args []Expression) (builtinFunc, error) {
if err := c.verifyArgs(args); err != nil {
return nil, errors.Trace(err)
}
bf := newBaseBuiltinFuncWithTp(ctx, args, types.ETInt, types.ETString, types.ETString)
bf.tp.Flen = 11
if types.IsBinaryStr(bf.args[0].GetType()) || types.IsBinaryStr(bf.args[1].GetType()) {
sig := &builtinInstrBinarySig{bf}
return sig, nil
}
sig := &builtinInstrSig{bf}
return sig, nil
}
type builtinInstrSig struct{ baseBuiltinFunc }
type builtinInstrBinarySig struct{ baseBuiltinFunc }
// evalInt evals INSTR(str,substr), case insensitive
// See https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_instr
func (b *builtinInstrSig) evalInt(row []types.Datum) (int64, bool, error) {
sc := b.ctx.GetSessionVars().StmtCtx
str, IsNull, err := b.args[0].EvalString(row, sc)
if IsNull || err != nil {
return 0, true, errors.Trace(err)
}
str = strings.ToLower(str)
substr, IsNull, err := b.args[1].EvalString(row, sc)
if IsNull || err != nil {
return 0, true, errors.Trace(err)
}
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 *builtinInstrBinarySig) evalInt(row []types.Datum) (int64, bool, error) {
sc := b.ctx.GetSessionVars().StmtCtx
str, IsNull, err := b.args[0].EvalString(row, sc)
if IsNull || err != nil {
return 0, true, errors.Trace(err)
}
substr, IsNull, err := b.args[1].EvalString(row, sc)
if IsNull || err != nil {
return 0, true, errors.Trace(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 context.Context, args []Expression) (builtinFunc, error) {
return nil, errFunctionNotExists.GenByArgs("FUNCTION", "load_file")
}
Loading...
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
1
https://gitee.com/zhoujin826/tidb.git
git@gitee.com:zhoujin826/tidb.git
zhoujin826
tidb
tidb
v1.0.5

搜索帮助