github.com/insionng/yougam@v0.0.0-20170714101924-2bc18d833463/libraries/pingcap/tidb/evaluator/evaluator_like.go (about) 1 // Copyright 2015 PingCAP, 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 evaluator 15 16 import ( 17 "regexp" 18 19 "github.com/insionng/yougam/libraries/juju/errors" 20 "github.com/insionng/yougam/libraries/pingcap/tidb/ast" 21 "github.com/insionng/yougam/libraries/pingcap/tidb/util/types" 22 ) 23 24 const ( 25 patMatch = iota + 1 26 patOne 27 patAny 28 ) 29 30 // handle escapes and wild cards convert pattern characters and pattern types, 31 func compilePattern(pattern string, escape byte) (patChars, patTypes []byte) { 32 var lastAny bool 33 patChars = make([]byte, len(pattern)) 34 patTypes = make([]byte, len(pattern)) 35 patLen := 0 36 for i := 0; i < len(pattern); i++ { 37 var tp byte 38 var c = pattern[i] 39 switch c { 40 case escape: 41 lastAny = false 42 tp = patMatch 43 if i < len(pattern)-1 { 44 i++ 45 c = pattern[i] 46 if c == escape || c == '_' || c == '%' { 47 // valid escape. 48 } else { 49 // invalid escape, fall back to escape byte 50 // mysql will treat escape character as the origin value even 51 // the escape sequence is invalid in Go or C. 52 // e.g, \m is invalid in Go, but in MySQL we will get "m" for select '\m'. 53 // Following case is correct just for escape \, not for others like +. 54 // TODO: add more checks for other escapes. 55 i-- 56 c = escape 57 } 58 } 59 case '_': 60 lastAny = false 61 tp = patOne 62 case '%': 63 if lastAny { 64 continue 65 } 66 lastAny = true 67 tp = patAny 68 default: 69 lastAny = false 70 tp = patMatch 71 } 72 patChars[patLen] = c 73 patTypes[patLen] = tp 74 patLen++ 75 } 76 for i := 0; i < patLen-1; i++ { 77 if (patTypes[i] == patAny) && (patTypes[i+1] == patOne) { 78 patTypes[i] = patOne 79 patTypes[i+1] = patAny 80 } 81 } 82 patChars = patChars[:patLen] 83 patTypes = patTypes[:patLen] 84 return 85 } 86 87 const caseDiff = 'a' - 'A' 88 89 func matchByteCI(a, b byte) bool { 90 if a == b { 91 return true 92 } 93 if a >= 'a' && a <= 'z' && a-caseDiff == b { 94 return true 95 } 96 return a >= 'A' && a <= 'Z' && a+caseDiff == b 97 } 98 99 func doMatch(str string, patChars, patTypes []byte) bool { 100 var sIdx int 101 for i := 0; i < len(patChars); i++ { 102 switch patTypes[i] { 103 case patMatch: 104 if sIdx >= len(str) || !matchByteCI(str[sIdx], patChars[i]) { 105 return false 106 } 107 sIdx++ 108 case patOne: 109 sIdx++ 110 if sIdx > len(str) { 111 return false 112 } 113 case patAny: 114 i++ 115 if i == len(patChars) { 116 return true 117 } 118 for sIdx < len(str) { 119 if matchByteCI(patChars[i], str[sIdx]) && doMatch(str[sIdx:], patChars[i:], patTypes[i:]) { 120 return true 121 } 122 sIdx++ 123 } 124 return false 125 } 126 } 127 return sIdx == len(str) 128 } 129 130 func (e *Evaluator) patternLike(p *ast.PatternLikeExpr) bool { 131 expr := p.Expr.GetDatum() 132 if expr.Kind() == types.KindNull { 133 p.SetNull() 134 return true 135 } 136 137 sexpr, err := expr.ToString() 138 if err != nil { 139 e.err = errors.Trace(err) 140 return false 141 } 142 143 // We need to compile pattern if it has not been compiled or it is not static. 144 var needCompile = len(p.PatChars) == 0 || !ast.IsConstant(p.Pattern) 145 if needCompile { 146 pattern := p.Pattern.GetDatum() 147 if pattern.Kind() == types.KindNull { 148 p.SetNull() 149 return true 150 } 151 spattern, err := pattern.ToString() 152 if err != nil { 153 e.err = errors.Trace(err) 154 return false 155 } 156 p.PatChars, p.PatTypes = compilePattern(spattern, p.Escape) 157 } 158 match := doMatch(sexpr, p.PatChars, p.PatTypes) 159 if p.Not { 160 match = !match 161 } 162 p.SetInt64(boolToInt64(match)) 163 return true 164 } 165 166 func (e *Evaluator) patternRegexp(p *ast.PatternRegexpExpr) bool { 167 var sexpr string 168 if p.Sexpr != nil { 169 sexpr = *p.Sexpr 170 } else { 171 expr := p.Expr.GetDatum() 172 if expr.Kind() == types.KindNull { 173 p.SetNull() 174 return true 175 } 176 var err error 177 sexpr, err = expr.ToString() 178 if err != nil { 179 e.err = errors.Errorf("non-string Expression in LIKE: %v (Value of type %T)", expr, expr) 180 return false 181 } 182 183 if ast.IsConstant(p.Expr) { 184 p.Sexpr = new(string) 185 *p.Sexpr = sexpr 186 } 187 } 188 189 re := p.Re 190 if re == nil { 191 pattern := p.Pattern.GetDatum() 192 if pattern.Kind() == types.KindNull { 193 p.SetNull() 194 return true 195 } 196 spattern, err := pattern.ToString() 197 if err != nil { 198 e.err = errors.Errorf("non-string pattern in LIKE: %v (Value of type %T)", pattern, pattern) 199 return false 200 } 201 202 if re, err = regexp.Compile(spattern); err != nil { 203 e.err = errors.Trace(err) 204 return false 205 } 206 207 if ast.IsConstant(p.Pattern) { 208 p.Re = re 209 } 210 } 211 match := re.MatchString(sexpr) 212 if p.Not { 213 match = !match 214 } 215 p.SetInt64(boolToInt64(match)) 216 return true 217 }