Skip to content

Commit 0c848d6

Browse files
committed
opt: normalize non-null x LIKE '%' to true
This commit adds the NormalizeLikeAny normalization rule, which normalizes `x LIKE '%'` to true when x is non-null in a SelectExpr. Epic: None Fixes: #144523 Release note (performance improvement): LIKE filter expressions of the form `x LIKE '%'` are normalized to true if x is non-Null under a select expression.
1 parent 0777857 commit 0c848d6

File tree

4 files changed

+90
-28
lines changed

4 files changed

+90
-28
lines changed

pkg/sql/opt/norm/general_funcs.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1522,6 +1522,36 @@ func (c *CustomFuncs) IntConst(d *tree.DInt) opt.ScalarExpr {
15221522
return c.f.ConstructConst(d, types.Int)
15231523
}
15241524

1525+
// StrConst constructs a Const holding a DString.
1526+
func (c *CustomFuncs) StrConst(d *tree.DString) opt.ScalarExpr {
1527+
return c.f.ConstructConst(d, types.String)
1528+
}
1529+
1530+
// StringFromConst extracts a string from a Const expression. It returns the
1531+
// string and a boolean indicating whether the extraction was successful.
1532+
func (c *CustomFuncs) StringFromConst(expr opt.ScalarExpr) (string, bool) {
1533+
if constExpr, ok := expr.(*memo.ConstExpr); ok {
1534+
datum := tree.UnwrapDOidWrapper(constExpr.Value)
1535+
switch d := datum.(type) {
1536+
case *tree.DString:
1537+
return string(*d), true
1538+
case *tree.DCollatedString:
1539+
return d.Contents, true
1540+
}
1541+
}
1542+
return "", false
1543+
}
1544+
1545+
// EqualConstString compares two Const expressions to see if they hold equal string values.
1546+
func (c *CustomFuncs) EqualConstStrings(left, right opt.ScalarExpr) bool {
1547+
leftStr, okLeft := c.StringFromConst(left)
1548+
rightStr, okRight := c.StringFromConst(right)
1549+
if !okLeft || !okRight {
1550+
return false
1551+
}
1552+
return leftStr == rightStr
1553+
}
1554+
15251555
// IsGreaterThan returns true if the first datum compares as greater than the
15261556
// second.
15271557
func (c *CustomFuncs) IsGreaterThan(first, second tree.Datum) bool {

pkg/sql/opt/norm/rules/select.opt

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -480,3 +480,23 @@ $input
480480
)
481481
$remainingFilters
482482
)
483+
484+
# NormalizeLikeAny replaces non-Null x LIKE '%' with true.
485+
[NormalizeLikeAny, Normalize]
486+
(Select
487+
$input:*
488+
$filters:[
489+
...
490+
$item:(FiltersItem
491+
(Like | ILike
492+
$left:* &
493+
(ExprIsNeverNull $left (NotNullCols $input))
494+
$pattern:(Const) &
495+
(EqualConstStrings $pattern (StrConst "%"))
496+
)
497+
)
498+
...
499+
]
500+
)
501+
=>
502+
(Select $input (RemoveFiltersItem $filters $item))

pkg/sql/opt/norm/testdata/rules/select

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2562,3 +2562,21 @@ barrier
25622562
└── filters
25632563
├── alice_has_access:3 [outer=(3), constraints=(/3: [/true - /true]; tight), fd=()-->(3)]
25642564
└── y:2 = 20 [outer=(2), constraints=(/2: [/20 - /20]; tight), fd=()-->(2)]
2565+
2566+
# --------------------------------------------------
2567+
# NormalizeLikeAny
2568+
# --------------------------------------------------
2569+
2570+
exec-ddl
2571+
CREATE TABLE strs_notnull (
2572+
s STRING NOT NULL,
2573+
cs STRING COLLATE en_US NOT NULL,
2574+
name NAME NOT NULL
2575+
)
2576+
----
2577+
2578+
norm expect=NormalizeLikeAny
2579+
SELECT * FROM strs_notnull WHERE s LIKE '%' AND cs LIKE '%' COLLATE en_US AND name LIKE '%'::NAME;
2580+
----
2581+
scan strs_notnull
2582+
└── columns: s:1!null cs:2!null name:3!null

pkg/sql/opt/xform/testdata/external/pgjdbc

Lines changed: 22 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -302,40 +302,34 @@ sort
302302
│ │ │ │ │ │ │ ├── inner-join (hash)
303303
│ │ │ │ │ │ │ │ ├── columns: n.oid:2!null n.nspname:3!null c.oid:7!null c.relname:8!null c.relnamespace:9!null c.relkind:24!null attrelid:44!null attname:45 atttypid:46 attlen:48 attnum:49!null atttypmod:52 a.attnotnull:56 attisdropped:60!null
304304
│ │ │ │ │ │ │ │ ├── fd: ()-->(3,60), (2)==(9), (9)==(2), (7)==(44), (44)==(7)
305-
│ │ │ │ │ │ │ │ ├── inner-join (merge)
306-
│ │ │ │ │ │ │ │ │ ├── columns: c.oid:7!null c.relname:8!null c.relnamespace:9 c.relkind:24!null attrelid:44!null attname:45 atttypid:46 attlen:48 attnum:49!null atttypmod:52 a.attnotnull:56 attisdropped:60!null
307-
│ │ │ │ │ │ │ │ │ ├── left ordering: +44
308-
│ │ │ │ │ │ │ │ │ ├── right ordering: +7
309-
│ │ │ │ │ │ │ │ │ ├── fd: ()-->(60), (7)==(44), (44)==(7)
305+
│ │ │ │ │ │ │ │ ├── select
306+
│ │ │ │ │ │ │ │ │ ├── columns: attrelid:44!null attname:45 atttypid:46 attlen:48 attnum:49!null atttypmod:52 a.attnotnull:56 attisdropped:60!null
307+
│ │ │ │ │ │ │ │ │ ├── fd: ()-->(60)
308+
│ │ │ │ │ │ │ │ │ ├── scan pg_attribute [as=a]
309+
│ │ │ │ │ │ │ │ │ │ └── columns: attrelid:44!null attname:45 atttypid:46 attlen:48 attnum:49 atttypmod:52 a.attnotnull:56 attisdropped:60
310+
│ │ │ │ │ │ │ │ │ └── filters
311+
│ │ │ │ │ │ │ │ │ ├── attnum:49 > 0 [outer=(49), constraints=(/49: [/1 - ]; tight)]
312+
│ │ │ │ │ │ │ │ │ └── NOT attisdropped:60 [outer=(60), constraints=(/60: [/false - /false]; tight), fd=()-->(60)]
313+
│ │ │ │ │ │ │ │ ├── inner-join (hash)
314+
│ │ │ │ │ │ │ │ │ ├── columns: n.oid:2!null n.nspname:3!null c.oid:7!null c.relname:8!null c.relnamespace:9!null c.relkind:24!null
315+
│ │ │ │ │ │ │ │ │ ├── fd: ()-->(3), (2)==(9), (9)==(2)
310316
│ │ │ │ │ │ │ │ │ ├── select
311-
│ │ │ │ │ │ │ │ │ │ ├── columns: attrelid:44!null attname:45 atttypid:46 attlen:48 attnum:49!null atttypmod:52 a.attnotnull:56 attisdropped:60!null
312-
│ │ │ │ │ │ │ │ │ │ ├── fd: ()-->(60)
313-
│ │ │ │ │ │ │ │ │ │ ├── ordering: +44 opt(60) [actual: +44]
314-
│ │ │ │ │ │ │ │ │ │ ├── scan pg_attribute@pg_attribute_attrelid_idx [as=a]
315-
│ │ │ │ │ │ │ │ │ │ │ ├── columns: attrelid:44!null attname:45 atttypid:46 attlen:48 attnum:49 atttypmod:52 a.attnotnull:56 attisdropped:60
316-
│ │ │ │ │ │ │ │ │ │ │ └── ordering: +44
317+
│ │ │ │ │ │ │ │ │ │ ├── columns: c.oid:7!null c.relname:8!null c.relnamespace:9 c.relkind:24!null
318+
│ │ │ │ │ │ │ │ │ │ ├── scan pg_class [as=c]
319+
│ │ │ │ │ │ │ │ │ │ │ └── columns: c.oid:7!null c.relname:8!null c.relnamespace:9 c.relkind:24
317320
│ │ │ │ │ │ │ │ │ │ └── filters
318-
│ │ │ │ │ │ │ │ │ │ ├── attnum:49 > 0 [outer=(49), constraints=(/49: [/1 - ]; tight)]
319-
│ │ │ │ │ │ │ │ │ │ └── NOT attisdropped:60 [outer=(60), constraints=(/60: [/false - /false]; tight), fd=()-->(60)]
321+
│ │ │ │ │ │ │ │ │ │ └── c.relkind:24 IN ('f', 'm', 'p', 'r', 'v') [outer=(24), constraints=(/24: [/'f' - /'f'] [/'m' - /'m'] [/'p' - /'p'] [/'r' - /'r'] [/'v' - /'v']; tight)]
320322
│ │ │ │ │ │ │ │ │ ├── select
321-
│ │ │ │ │ │ │ │ │ │ ├── columns: c.oid:7!null c.relname:8!null c.relnamespace:9 c.relkind:24!null
322-
│ │ │ │ │ │ │ │ │ │ ├── ordering: +7
323-
│ │ │ │ │ │ │ │ │ │ ├── scan pg_class@pg_class_oid_idx [as=c]
324-
│ │ │ │ │ │ │ │ │ │ │ ├── columns: c.oid:7!null c.relname:8!null c.relnamespace:9 c.relkind:24
325-
│ │ │ │ │ │ │ │ │ │ │ └── ordering: +7
323+
│ │ │ │ │ │ │ │ │ │ ├── columns: n.oid:2 n.nspname:3!null
324+
│ │ │ │ │ │ │ │ │ │ ├── fd: ()-->(3)
325+
│ │ │ │ │ │ │ │ │ │ ├── scan pg_namespace [as=n]
326+
│ │ │ │ │ │ │ │ │ │ │ └── columns: n.oid:2 n.nspname:3!null
326327
│ │ │ │ │ │ │ │ │ │ └── filters
327-
│ │ │ │ │ │ │ │ │ │ ├── c.relkind:24 IN ('f', 'm', 'p', 'r', 'v') [outer=(24), constraints=(/24: [/'f' - /'f'] [/'m' - /'m'] [/'p' - /'p'] [/'r' - /'r'] [/'v' - /'v']; tight)]
328-
│ │ │ │ │ │ │ │ │ │ └── c.relname:8 LIKE '%' [outer=(8), constraints=(/8: (/NULL - ])]
329-
│ │ │ │ │ │ │ │ │ └── filters (true)
330-
│ │ │ │ │ │ │ │ ├── select
331-
│ │ │ │ │ │ │ │ │ ├── columns: n.oid:2 n.nspname:3!null
332-
│ │ │ │ │ │ │ │ │ ├── fd: ()-->(3)
333-
│ │ │ │ │ │ │ │ │ ├── scan pg_namespace [as=n]
334-
│ │ │ │ │ │ │ │ │ │ └── columns: n.oid:2 n.nspname:3!null
328+
│ │ │ │ │ │ │ │ │ │ └── n.nspname:3 LIKE 'public' [outer=(3), constraints=(/3: [/'public' - /'public']; tight), fd=()-->(3)]
335329
│ │ │ │ │ │ │ │ │ └── filters
336-
│ │ │ │ │ │ │ │ │ └── n.nspname:3 LIKE 'public' [outer=(3), constraints=(/3: [/'public' - /'public']; tight), fd=()-->(3)]
330+
│ │ │ │ │ │ │ │ │ └── c.relnamespace:9 = n.oid:2 [outer=(2,9), constraints=(/2: (/NULL - ]; /9: (/NULL - ]), fd=(2)==(9), (9)==(2)]
337331
│ │ │ │ │ │ │ │ └── filters
338-
│ │ │ │ │ │ │ │ └── c.relnamespace:9 = n.oid:2 [outer=(2,9), constraints=(/2: (/NULL - ]; /9: (/NULL - ]), fd=(2)==(9), (9)==(2)]
332+
│ │ │ │ │ │ │ │ └── attrelid:44 = c.oid:7 [outer=(7,44), constraints=(/7: (/NULL - ]; /44: (/NULL - ]), fd=(7)==(44), (44)==(7)]
339333
│ │ │ │ │ │ │ └── filters
340334
│ │ │ │ │ │ │ ├── c.oid:7 = crdb_internal.kv_catalog_comments.objoid:110 [outer=(7,110), constraints=(/7: (/NULL - ]; /110: (/NULL - ]), fd=(7)==(110), (110)==(7)]
341335
│ │ │ │ │ │ │ └── attnum:49 = objsubid:118 [outer=(49,118), constraints=(/49: (/NULL - ]; /118: (/NULL - ]), fd=(49)==(118), (118)==(49)]

0 commit comments

Comments
 (0)