David Mollitor created SPARK-59355:
--------------------------------------

             Summary: Simplify LIKE patterns containing escaped wildcards
                 Key: SPARK-59355
                 URL: https://issues.apache.org/jira/browse/SPARK-59355
             Project: Spark
          Issue Type: Improvement
          Components: SQL
    Affects Versions: 4.1.0
            Reporter: David Mollitor


h3. What

{{LikeSimplification}} currently gives up on a pattern the moment it contains 
the escape character (`if (pattern.contains(escapeChar)) None`), so 
escaped-literal patterns stay a full per-row regex even when they are trivially 
sargable once the escapes are decoded:
{code:java}
col LIKE 'ma\%ca'         ==>  col = 'ma%ca'
col LIKE 'abc\%def%'      ==>  StartsWith(col, 'abc%def')
col LIKE '%mn\%'          ==>  EndsWith(col, 'mn%')
col LIKE 'abbc' ESCAPE 'b'  ==>  col = 'abc'
{code}
This decodes valid escape sequences and applies the rule's existing shape 
simplifications (EqualTo / StartsWith / EndsWith / Contains / 
StartsWith+EndsWith) to the decoded literals.
h3. Why are the changes needed?

Escaped-literal patterns become sargable: the resulting 
{{{}EqualTo{}}}/{{{}StartsWith{}}}/etc. push down to data sources (e.g. Parquet 
prunes on {{{}StringStartsWith{}}}) and short-circuit the per-row regex, 
instead of always running the regex and pushing nothing.
h3. How it works
 * *Fast path (unchanged).* A quick {{pattern.contains(escapeChar)}} scan: when 
the escape character is absent, the existing five regexes run exactly as before 
– the common case pays nothing for the decode logic.
 * *Guard.* Otherwise, if the escape character is itself {{%}} or {{_}} (a 
pathological
{{ESCAPE '%'}} / {{{}ESCAPE '_'{}}}), skip – unchanged behavior. (This guard 
must follow the fast-path scan: an {{ESCAPE '%'}} pattern that contains no 
{{%}} is still simplifiable via the fast path.)
 * *Decode.* Otherwise, walk the pattern turning {{%}} / {{_}} / \{{}} into 
literal characters, treating unescaped {{{}%{}}}/{{{}_{}}} as wildcards, and 
classify the decoded shape.

h3. Correctness
 * *Behavior-preserving.* A valid-escape pattern's decoded literal is exactly 
the string the {{LIKE}} matches, so the resulting predicate accepts the same 
rows.
 * *Invalid and trailing escapes are left alone.* An escape character followed 
by anything other than {{{}%{}}}, {{{}_{}}}, or itself, or a trailing escape 
character, makes {{LIKE}} throw at runtime 
({{{}INVALID_FORMAT.ESC_IN_THE_MIDDLE{}}} / {{{}ESC_AT_THE_END{}}}); the rule 
returns None for these, so the {{Like}} is kept and throws exactly as today.
 * Decoded literals may contain {{{}%{}}}/{{{}_{}}}/the escape character; that 
is fine, since the
predicates compare literal strings. Collation handling is unchanged. Every 
shape fully replaces the {{Like}} (no residual), so no idempotency tag is 
needed.
 * Out of scope: an unescaped {{_}} single-character wildcard, and 
{{{}LikeAll{}}}/{{{}LikeAny{}}} shapes beyond what the shared {{simplifyLike}} 
already covers.

h3. Does this PR introduce any user-facing change?

No. Query results are identical (and error cases still error); this is a 
performance improvement.
h3. How was this patch tested?

{{{}LikeSimplificationSuite{}}}: updated the existing escape tests (they 
previously asserted the pattern was skipped and now assert the simplified form, 
including the {{{}LikeAll{}}}/{{{}LikeAny{}}} multi-pattern tests whose escaped 
entries now fold in), and added tests for valid-escape simplification across 
all shapes and for the still-skipped cases (invalid escape, trailing escape, 
{{{}ESCAPE '%'{}}}/{{{}ESCAPE '{_}'{_}{}}}{_}, and an unescaped {{_}}). 
Scalastyle clean.



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to