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 = &regexpFunctionClass{}
    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  }