github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/sem/tree/like_test.go (about) 1 // Copyright 2017 The Cockroach Authors. 2 // 3 // Use of this software is governed by the Business Source License 4 // included in the file licenses/BSL.txt. 5 // 6 // As of the Change Date specified in that file, in accordance with 7 // the Business Source License, use of this software will be governed 8 // by the Apache License, Version 2.0, included in the file 9 // licenses/APL.txt. 10 11 package tree 12 13 import ( 14 "context" 15 "testing" 16 17 "github.com/cockroachdb/cockroach/pkg/settings/cluster" 18 "github.com/cockroachdb/cockroach/pkg/sql/types" 19 "github.com/cockroachdb/cockroach/pkg/util/leaktest" 20 ) 21 22 func TestLike(t *testing.T) { 23 defer leaktest.AfterTest(t)() 24 testData := []struct { 25 expr string 26 pattern string 27 matches Datum 28 erroneous bool 29 }{ 30 {``, `{`, DBoolFalse, false}, 31 {``, `%%%%%`, DBoolTrue, false}, 32 {`a[b]`, `%[[_]`, DBoolFalse, false}, 33 {`+`, `++`, DBoolFalse, false}, 34 {`a`, `}`, DBoolFalse, false}, 35 {`a{}%`, `%\}\%`, DBoolTrue, false}, 36 {`a{}%a`, `%\}\%\`, DBoolFalse, true}, 37 {`G\n%`, `%__%`, DBoolTrue, false}, 38 {``, `\`, DBoolFalse, false}, 39 {`_%\b\n`, `%__`, DBoolTrue, false}, 40 {`_\nL_`, `%_%`, DBoolTrue, false}, 41 } 42 ctx := NewTestingEvalContext(cluster.MakeTestingClusterSettings()) 43 for _, d := range testData { 44 if matches, err := matchLike(ctx, NewDString(d.expr), NewDString(d.pattern), false); err != nil && !d.erroneous { 45 t.Error(err) 46 } else if err == nil && d.erroneous { 47 t.Errorf("%s matching the pattern %s: expected to return an error", 48 d.expr, 49 d.pattern, 50 ) 51 } else if matches != d.matches { 52 t.Errorf("%s matching the pattern %s: expected %v but found %v", 53 d.expr, 54 d.pattern, 55 d.matches, 56 matches, 57 ) 58 } 59 } 60 } 61 62 func TestLikeEscape(t *testing.T) { 63 defer leaktest.AfterTest(t)() 64 testData := []struct { 65 expr string 66 pattern string 67 escape string 68 matches Datum 69 erroneous bool 70 }{ 71 {``, `{`, string('\x7f'), DBoolFalse, false}, 72 {``, `}`, `}`, DBoolFalse, false}, 73 {``, `%%%%%`, ``, DBoolTrue, false}, 74 {``, `%%%%%`, `\`, DBoolTrue, false}, 75 {`a[b]`, `%[[_]`, `[`, DBoolTrue, false}, 76 {`+`, `++`, `+`, DBoolTrue, false}, 77 {`a`, `}`, `}`, DBoolFalse, true}, 78 {`a{}%`, `%}}}%`, `}`, DBoolTrue, false}, 79 {`BG_`, `%__`, `.`, DBoolTrue, false}, 80 {`_%\b\n`, `%__`, ``, DBoolTrue, false}, 81 {`_\nL_`, `%_%`, `{`, DBoolTrue, false}, 82 {`_\nL_`, `%_%`, `%`, DBoolFalse, true}, 83 {`\n\t`, `_%%_`, string('\x7f'), DBoolTrue, false}, 84 } 85 ctx := NewTestingEvalContext(cluster.MakeTestingClusterSettings()) 86 for _, d := range testData { 87 if matches, err := MatchLikeEscape(ctx, d.expr, d.pattern, d.escape, false); err != nil && !d.erroneous { 88 t.Error(err) 89 } else if err == nil && d.erroneous { 90 t.Errorf("%s matching the pattern %s with escape character %s: expected to return an error", 91 d.expr, 92 d.pattern, 93 d.escape, 94 ) 95 } else if matches != d.matches { 96 t.Errorf("%s matching the pattern %s with escape character %s: expected %v but found %v", 97 d.expr, 98 d.pattern, 99 d.escape, 100 d.matches, 101 matches, 102 ) 103 } 104 } 105 } 106 107 func TestSimilarEscape(t *testing.T) { 108 defer leaktest.AfterTest(t)() 109 testData := []struct { 110 expr string 111 expected string 112 }{ 113 {`test`, `test`}, 114 {`test%`, `test.*`}, 115 {`_test_`, `.test.`}, 116 {`_%*`, `..**`}, 117 {`[_%]*`, `[_%]*`}, 118 {`.^$`, `\.\^\$`}, 119 {`%(b|d)%`, `.*(?:b|d).*`}, 120 {`se\"arch\"[\"]`, `se(arch)[\"]`}, 121 } 122 for _, d := range testData { 123 s := SimilarEscape(d.expr) 124 if s != d.expected { 125 t.Errorf("%s: expected %s, but found %v", d.expr, d.expected, s) 126 } 127 } 128 } 129 130 var benchmarkLikePatterns = []string{ 131 `test%`, 132 `%test%`, 133 `%test`, 134 ``, 135 `%`, 136 `_`, 137 `test`, 138 `bad`, 139 `also\%`, 140 } 141 142 func benchmarkLike(b *testing.B, ctx *EvalContext, caseInsensitive bool) { 143 op := Like 144 if caseInsensitive { 145 op = ILike 146 } 147 likeFn, _ := CmpOps[op].LookupImpl(types.String, types.String) 148 iter := func() { 149 for _, p := range benchmarkLikePatterns { 150 if _, err := likeFn.Fn(ctx, NewDString("test"), NewDString(p)); err != nil { 151 b.Fatalf("LIKE evaluation failed with error: %v", err) 152 } 153 } 154 } 155 // Warm up cache, if applicable 156 iter() 157 b.ResetTimer() 158 for n := 0; n < b.N; n++ { 159 iter() 160 } 161 } 162 163 func BenchmarkLikeWithCache(b *testing.B) { 164 ctx := NewTestingEvalContext(cluster.MakeTestingClusterSettings()) 165 ctx.ReCache = NewRegexpCache(len(benchmarkLikePatterns)) 166 defer ctx.Mon.Stop(context.Background()) 167 168 benchmarkLike(b, ctx, false) 169 } 170 171 func BenchmarkLikeWithoutCache(b *testing.B) { 172 evalCtx := NewTestingEvalContext(cluster.MakeTestingClusterSettings()) 173 defer evalCtx.Stop(context.Background()) 174 175 benchmarkLike(b, evalCtx, false) 176 } 177 178 func BenchmarkILikeWithCache(b *testing.B) { 179 ctx := NewTestingEvalContext(cluster.MakeTestingClusterSettings()) 180 ctx.ReCache = NewRegexpCache(len(benchmarkLikePatterns)) 181 defer ctx.Mon.Stop(context.Background()) 182 183 benchmarkLike(b, ctx, true) 184 } 185 186 func BenchmarkILikeWithoutCache(b *testing.B) { 187 evalCtx := NewTestingEvalContext(cluster.MakeTestingClusterSettings()) 188 defer evalCtx.Stop(context.Background()) 189 190 benchmarkLike(b, evalCtx, true) 191 }