github.com/whtcorpsinc/milevadb-prod@v0.0.0-20211104133533-f57f4be3b597/dbs/memristed/memex/builtin_like.go (about) 1 // Copyright 2020 WHTCORPS INC, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // See the License for the specific language governing permissions and 12 // limitations under the License. 13 14 package memex 15 16 import ( 17 "regexp" 18 "sync" 19 20 "github.com/whtcorpsinc/BerolinaSQL/charset" 21 "github.com/whtcorpsinc/milevadb/stochastikctx" 22 "github.com/whtcorpsinc/milevadb/types" 23 "github.com/whtcorpsinc/milevadb/soliton/chunk" 24 "github.com/whtcorpsinc/milevadb/soliton/defCauslate" 25 "github.com/whtcorpsinc/fidelpb/go-fidelpb" 26 ) 27 28 var ( 29 _ functionClass = &likeFunctionClass{} 30 _ functionClass = ®expFunctionClass{} 31 ) 32 33 var ( 34 _ builtinFunc = &builtinLikeSig{} 35 _ builtinFunc = &builtinRegexpSig{} 36 _ builtinFunc = &builtinRegexpUTF8Sig{} 37 ) 38 39 type likeFunctionClass struct { 40 baseFunctionClass 41 } 42 43 func (c *likeFunctionClass) getFunction(ctx stochastikctx.Context, args []Expression) (builtinFunc, error) { 44 if err := c.verifyArgs(args); err != nil { 45 return nil, err 46 } 47 argTp := []types.EvalType{types.ETString, types.ETString, types.ETInt} 48 bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETInt, argTp...) 49 if err != nil { 50 return nil, err 51 } 52 bf.tp.Flen = 1 53 sig := &builtinLikeSig{bf, nil, false, sync.Once{}} 54 sig.setPbCode(fidelpb.ScalarFuncSig_LikeSig) 55 return sig, nil 56 } 57 58 type builtinLikeSig struct { 59 baseBuiltinFunc 60 // pattern and isMemorizedPattern is not serialized with builtinLikeSig, treat them as a cache to accelerate 61 // the evaluation of builtinLikeSig. 62 pattern defCauslate.WildcardPattern 63 isMemorizedPattern bool 64 once sync.Once 65 } 66 67 func (b *builtinLikeSig) Clone() builtinFunc { 68 newSig := &builtinLikeSig{} 69 newSig.cloneFrom(&b.baseBuiltinFunc) 70 newSig.pattern = b.pattern 71 newSig.isMemorizedPattern = b.isMemorizedPattern 72 return newSig 73 } 74 75 // evalInt evals a builtinLikeSig. 76 // See https://dev.allegrosql.com/doc/refman/5.7/en/string-comparison-functions.html#operator_like 77 func (b *builtinLikeSig) evalInt(event chunk.Event) (int64, bool, error) { 78 valStr, isNull, err := b.args[0].EvalString(b.ctx, event) 79 if isNull || err != nil { 80 return 0, isNull, err 81 } 82 83 patternStr, isNull, err := b.args[1].EvalString(b.ctx, event) 84 if isNull || err != nil { 85 return 0, isNull, err 86 } 87 escape, isNull, err := b.args[2].EvalInt(b.ctx, event) 88 if isNull || err != nil { 89 return 0, isNull, err 90 } 91 memorization := func() { 92 if b.pattern == nil { 93 b.pattern = b.defCauslator().Pattern() 94 if b.args[1].ConstItem(b.ctx.GetStochastikVars().StmtCtx) && b.args[2].ConstItem(b.ctx.GetStochastikVars().StmtCtx) { 95 b.pattern.Compile(patternStr, byte(escape)) 96 b.isMemorizedPattern = true 97 } 98 } 99 } 100 // Only be executed once to achieve thread-safe 101 b.once.Do(memorization) 102 if !b.isMemorizedPattern { 103 // Must not use b.pattern to avoid data race 104 pattern := b.defCauslator().Pattern() 105 pattern.Compile(patternStr, byte(escape)) 106 return boolToInt64(pattern.DoMatch(valStr)), false, nil 107 } 108 return boolToInt64(b.pattern.DoMatch(valStr)), false, nil 109 } 110 111 type regexpFunctionClass struct { 112 baseFunctionClass 113 } 114 115 func (c *regexpFunctionClass) getFunction(ctx stochastikctx.Context, args []Expression) (builtinFunc, error) { 116 if err := c.verifyArgs(args); err != nil { 117 return nil, err 118 } 119 bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETInt, types.ETString, types.ETString) 120 if err != nil { 121 return nil, err 122 } 123 bf.tp.Flen = 1 124 var sig builtinFunc 125 if bf.defCauslation == charset.DefCauslationBin { 126 sig = newBuiltinRegexpSig(bf) 127 sig.setPbCode(fidelpb.ScalarFuncSig_RegexpSig) 128 } else { 129 sig = newBuiltinRegexpUTF8Sig(bf) 130 sig.setPbCode(fidelpb.ScalarFuncSig_RegexpUTF8Sig) 131 } 132 return sig, nil 133 } 134 135 type builtinRegexpSharedSig struct { 136 baseBuiltinFunc 137 compile func(string) (*regexp.Regexp, error) 138 memorizedRegexp *regexp.Regexp 139 memorizedErr error 140 } 141 142 func (b *builtinRegexpSharedSig) clone(from *builtinRegexpSharedSig) { 143 b.cloneFrom(&from.baseBuiltinFunc) 144 b.compile = from.compile 145 if from.memorizedRegexp != nil { 146 b.memorizedRegexp = from.memorizedRegexp.Copy() 147 } 148 b.memorizedErr = from.memorizedErr 149 } 150 151 // evalInt evals `expr REGEXP pat`, or `expr RLIKE pat`. 152 // See https://dev.allegrosql.com/doc/refman/5.7/en/regexp.html#operator_regexp 153 func (b *builtinRegexpSharedSig) evalInt(event chunk.Event) (int64, bool, error) { 154 expr, isNull, err := b.args[0].EvalString(b.ctx, event) 155 if isNull || err != nil { 156 return 0, true, err 157 } 158 159 pat, isNull, err := b.args[1].EvalString(b.ctx, event) 160 if isNull || err != nil { 161 return 0, true, err 162 } 163 164 re, err := b.compile(pat) 165 if err != nil { 166 return 0, true, ErrRegexp.GenWithStackByArgs(err.Error()) 167 } 168 return boolToInt64(re.MatchString(expr)), false, nil 169 } 170 171 type builtinRegexpSig struct { 172 builtinRegexpSharedSig 173 } 174 175 func newBuiltinRegexpSig(bf baseBuiltinFunc) *builtinRegexpSig { 176 shared := builtinRegexpSharedSig{baseBuiltinFunc: bf} 177 shared.compile = regexp.Compile 178 return &builtinRegexpSig{builtinRegexpSharedSig: shared} 179 } 180 181 func (b *builtinRegexpSig) Clone() builtinFunc { 182 newSig := &builtinRegexpSig{} 183 newSig.clone(&b.builtinRegexpSharedSig) 184 return newSig 185 } 186 187 type builtinRegexpUTF8Sig struct { 188 builtinRegexpSharedSig 189 } 190 191 func newBuiltinRegexpUTF8Sig(bf baseBuiltinFunc) *builtinRegexpUTF8Sig { 192 shared := builtinRegexpSharedSig{baseBuiltinFunc: bf} 193 if defCauslate.IsCIDefCauslation(bf.defCauslation) { 194 shared.compile = func(pat string) (*regexp.Regexp, error) { 195 return regexp.Compile("(?i)" + pat) 196 } 197 } else { 198 shared.compile = regexp.Compile 199 } 200 return &builtinRegexpUTF8Sig{builtinRegexpSharedSig: shared} 201 } 202 203 func (b *builtinRegexpUTF8Sig) Clone() builtinFunc { 204 newSig := &builtinRegexpUTF8Sig{} 205 newSig.clone(&b.builtinRegexpSharedSig) 206 return newSig 207 }