Skip to content

Commit 7d22a78

Browse files
authored
Cache REGEX (#3036)
1 parent 4810ae9 commit 7d22a78

File tree

8 files changed

+351
-117
lines changed

8 files changed

+351
-117
lines changed

sql/expression/function/regexp_instr.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -167,8 +167,8 @@ func (r *RegexpInstr) String() string {
167167
// compile handles compilation of the regex.
168168
func (r *RegexpInstr) compile(ctx *sql.Context, row sql.Row) {
169169
r.compileOnce.Do(func() {
170-
r.cacheRegex = canBeCached(r.Text, r.Pattern, r.Flags)
171-
r.cacheVal = canBeCached(r.Text, r.Pattern, r.Position, r.Occurrence, r.ReturnOption, r.Flags)
170+
r.cacheRegex = canBeCached(r.Pattern, r.Flags)
171+
r.cacheVal = r.cacheRegex && canBeCached(r.Text, r.Position, r.Occurrence, r.ReturnOption)
172172
if r.cacheRegex {
173173
r.re, r.compileErr = compileRegex(ctx, r.Pattern, r.Text, r.Flags, r.FunctionName(), row)
174174
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
// Copyright 2025 Dolthub, 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+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package function
16+
17+
import (
18+
"fmt"
19+
"testing"
20+
21+
"github.com/stretchr/testify/require"
22+
23+
"github.com/dolthub/go-mysql-server/sql"
24+
"github.com/dolthub/go-mysql-server/sql/expression"
25+
"github.com/dolthub/go-mysql-server/sql/types"
26+
)
27+
28+
// Last Run: 06/17/2025
29+
// BenchmarkRegexpInStr
30+
// BenchmarkRegexpInStr-14 100 97313270 ns/op
31+
// BenchmarkRegexpInStr-14 10000 1001064 ns/op
32+
func BenchmarkRegexpInStr(b *testing.B) {
33+
ctx := sql.NewEmptyContext()
34+
data := make([]sql.Row, 100)
35+
for i := range data {
36+
data[i] = sql.Row{fmt.Sprintf("test%d", i)}
37+
}
38+
39+
for i := 0; i < b.N; i++ {
40+
f, err := NewRegexpInstr(
41+
expression.NewGetField(0, types.LongText, "text", false),
42+
expression.NewLiteral("^test[0-9]$", types.LongText),
43+
)
44+
require.NoError(b, err)
45+
var total int
46+
for _, row := range data {
47+
res, err := f.Eval(ctx, row)
48+
require.NoError(b, err)
49+
total += int(res.(int32))
50+
}
51+
require.Equal(b, 10, total)
52+
f.(*RegexpInstr).Dispose()
53+
}
54+
}

sql/expression/function/regexp_like.go

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,9 @@ type RegexpLike struct {
3434
Pattern sql.Expression
3535
Flags sql.Expression
3636

37+
cacheVal bool
3738
cachedVal any
38-
cacheable bool
39+
cacheRegex bool
3940
re regex.Regex
4041
compileOnce sync.Once
4142
compileErr error
@@ -136,12 +137,13 @@ func (r *RegexpLike) String() string {
136137
// compile handles compilation of the regex.
137138
func (r *RegexpLike) compile(ctx *sql.Context, row sql.Row) {
138139
r.compileOnce.Do(func() {
139-
r.cacheable = canBeCached(r.Text, r.Pattern, r.Flags)
140-
if r.cacheable {
140+
r.cacheRegex = canBeCached(r.Pattern, r.Flags)
141+
r.cacheVal = r.cacheRegex && canBeCached(r.Text)
142+
if r.cacheRegex {
141143
r.re, r.compileErr = compileRegex(ctx, r.Pattern, r.Text, r.Flags, r.FunctionName(), row)
142144
}
143145
})
144-
if !r.cacheable {
146+
if !r.cacheRegex {
145147
if r.re != nil {
146148
if r.compileErr = r.re.Close(); r.compileErr != nil {
147149
return
@@ -199,7 +201,7 @@ func (r *RegexpLike) Eval(ctx *sql.Context, row sql.Row) (interface{}, error) {
199201
outVal = int8(0)
200202
}
201203

202-
if r.cacheable {
204+
if r.cacheVal {
203205
r.cachedVal = outVal
204206
}
205207
return outVal, nil

sql/expression/function/regexp_like_test.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -363,3 +363,31 @@ func TestRegexpLikeNilAndErrors(t *testing.T) {
363363
require.Equal(t, nil, res)
364364
f.(*RegexpLike).Dispose()
365365
}
366+
367+
// Last Run: 06/17/2025
368+
// BenchmarkRegexpLike
369+
// BenchmarkRegexpLike-14 100 98269522 ns/op
370+
// BenchmarkRegexpLike-14 10000 958159 ns/op
371+
func BenchmarkRegexpLike(b *testing.B) {
372+
ctx := sql.NewEmptyContext()
373+
data := make([]sql.Row, 100)
374+
for i := range data {
375+
data[i] = sql.Row{fmt.Sprintf("test%d", i)}
376+
}
377+
378+
for i := 0; i < b.N; i++ {
379+
f, err := NewRegexpLike(
380+
expression.NewGetField(0, types.LongText, "text", false),
381+
expression.NewLiteral("^test[0-9]$", types.LongText),
382+
)
383+
require.NoError(b, err)
384+
var total int8
385+
for _, row := range data {
386+
res, err := f.Eval(ctx, row)
387+
require.NoError(b, err)
388+
total += res.(int8)
389+
}
390+
require.Equal(b, int8(10), total)
391+
f.(*RegexpLike).Dispose()
392+
}
393+
}

0 commit comments

Comments
 (0)