With the default filter_use_like_shift_and = true, LIKE/ILIKE patterns that don't start and end with % return incorrect rows. Found while testing #2 / #3, but it reproduces with plain LIKE on current main.
Repro
Table with a single VARCHAR column containing:
Widget, WIDGET, widget, wIdGeT-Pro, Gadget, gizmo, WidgetX, midget
SELECT name FROM products WHERE name LIKE 'w_dget';
- Expected:
widget
- Actual (shift-and path, default):
widget, gizmo
- Actual with
GQE_FILTER_USE_LIKE_SHIFT_AND=false: widget (correct)
Root cause (three related defects)
-
Strings shorter than the prefix/suffix silently match. In like_fn_shift_and_kernel (src/executor/like.cu), result is initialized to true and the prefix check is skipped when string_len < prefix_len — so the flag is never set to false for strings too short to match (this is how 5-char gizmo matches a 6-char pattern). The suffix check a few lines below has the same hole.
-
Patterns with no unescaped % lose exactness. preprocess_like decomposes 'w_dget' into prefix=w_dget, no suffix, no middles — which the kernel evaluates as 'w_dget%' (prefix-only, no length constraint), so e.g. WidgetX matches ILIKE 'w_dget'.
-
Overlapping prefix/suffix windows. For a pattern like 'ab%ba' on the 3-char string "aba", the suffix window overlaps the prefix, and the length checks are evaluated independently — the correct requirement is string_len >= prefix_len + suffix_len (the % must match zero or more characters between non-overlapping prefix and suffix). This also means string_len - prefix_len - suffix_len, passed to the middle-pattern matcher, can underflow as an unsigned subtraction.
Existing unit tests don't catch these because every LIKE test uses %-anchored patterns.
Proposed fix
Happy to submit a PR:
- Hoist a single length guard before the prefix/suffix checks:
if (result && string_len < prefix_len + suffix_len) result = false; — fixes defects 1 and 3 and makes the middle-matcher length non-negative by construction.
- For defect 2, either pass a
requires_exact_length flag to the kernel (pattern has no unescaped % ⇒ require string_len == prefix_len; preprocess_like already strips escape chars so prefix_len is the match length), or fall back to cudf::strings::like for such patterns, mirroring what like_utf8 already does for prefix/suffix patterns.
- Parameterized tests covering: no-
% patterns with _, no-wildcard ILIKE, the 'ab%ba' overlap cases, strings shorter than prefix, and escaped variants ('w^_dget' escape '^').
With the default
filter_use_like_shift_and = true,LIKE/ILIKEpatterns that don't start and end with%return incorrect rows. Found while testing #2 / #3, but it reproduces with plainLIKEon currentmain.Repro
Table with a single VARCHAR column containing:
Widget, WIDGET, widget, wIdGeT-Pro, Gadget, gizmo, WidgetX, midgetwidgetwidget, gizmoGQE_FILTER_USE_LIKE_SHIFT_AND=false:widget(correct)Root cause (three related defects)
Strings shorter than the prefix/suffix silently match. In
like_fn_shift_and_kernel(src/executor/like.cu),resultis initialized totrueand the prefix check is skipped whenstring_len < prefix_len— so the flag is never set tofalsefor strings too short to match (this is how 5-chargizmomatches a 6-char pattern). The suffix check a few lines below has the same hole.Patterns with no unescaped
%lose exactness.preprocess_likedecomposes'w_dget'into prefix=w_dget, no suffix, no middles — which the kernel evaluates as'w_dget%'(prefix-only, no length constraint), so e.g.WidgetXmatchesILIKE 'w_dget'.Overlapping prefix/suffix windows. For a pattern like
'ab%ba'on the 3-char string"aba", the suffix window overlaps the prefix, and the length checks are evaluated independently — the correct requirement isstring_len >= prefix_len + suffix_len(the%must match zero or more characters between non-overlapping prefix and suffix). This also meansstring_len - prefix_len - suffix_len, passed to the middle-pattern matcher, can underflow as an unsigned subtraction.Existing unit tests don't catch these because every LIKE test uses %-anchored patterns.
Proposed fix
Happy to submit a PR:
if (result && string_len < prefix_len + suffix_len) result = false;— fixes defects 1 and 3 and makes the middle-matcher length non-negative by construction.requires_exact_lengthflag to the kernel (pattern has no unescaped%⇒ requirestring_len == prefix_len;preprocess_likealready strips escape chars soprefix_lenis the match length), or fall back tocudf::strings::likefor such patterns, mirroring whatlike_utf8already does for prefix/suffix patterns.%patterns with_, no-wildcard ILIKE, the'ab%ba'overlap cases, strings shorter than prefix, and escaped variants ('w^_dget'escape'^').