JingsongLi commented on code in PR #8395:
URL: https://github.com/apache/paimon/pull/8395#discussion_r3503107432
##########
paimon-flink/paimon-flink-common/src/main/java/org/apache/paimon/flink/PredicateConverter.java:
##########
@@ -291,6 +330,85 @@ private boolean supportsPredicate(LogicalType type) {
}
}
+ /**
+ * Converts a SQL SIMILAR TO pattern to an equivalent SQL LIKE pattern so
that it can be
+ * evaluated by {@link PredicateBuilder#like}.
+ *
+ * <p>The conversion handles only the subset of SIMILAR TO syntax that
maps directly to SQL
+ * LIKE:
+ * <ul>
+ * <li>{@code %} (any-string wildcard) is preserved as-is.</li>
+ * <li>{@code _} (single-character wildcard) is preserved as-is.</li>
+ * <li>Escape sequences: {@code escape + '_'} and {@code escape + '%'}
become their literal
+ * equivalents, emitted as {@code \ + char} so that the downstream
+ * {@link Like} function (which uses {@code \} as its default
escape) treats them as
+ * literals. {@code escape + escape} becomes a literal escape
character.</li>
+ * </ul>
+ *
+ * <p>SIMILAR TO-only features (character classes {@code [...]},
alternation {@code |},
+ * quantifiers {@code *}, {@code +}, {@code ?}, and grouping {@code ()})
are not supported and
+ * will cause an {@link UnsupportedExpression} to be thrown.
+ *
+ * @param sqlPattern the SIMILAR TO pattern string
+ * @param escape the escape character string (single char), or {@code
null} for no escaping
+ * @return an equivalent SQL LIKE pattern (using {@code \} as the escape
character)
+ * @throws UnsupportedExpression if the pattern uses SIMILAR TO-only
features
+ */
+ private String convertSimilarToLike(String sqlPattern, String escape) {
+ if (sqlPattern == null || sqlPattern.isEmpty()) {
+ return sqlPattern;
+ }
+
+ // Sentinel 0 means no escape character is defined.
+ char escapeChar = (escape != null && !escape.isEmpty()) ?
escape.charAt(0) : 0;
+ // The output LIKE pattern will always use '\' as its escape char,
because that is what
+ // Like.sqlToRegexLike uses by default.
+ final char outputEscape = '\\';
+
+ StringBuilder like = new StringBuilder();
+
+ for (int i = 0; i < sqlPattern.length(); i++) {
+ char c = sqlPattern.charAt(i);
+
+ if (escapeChar != 0 && c == escapeChar) {
+ // Escape sequence
+ if (i + 1 >= sqlPattern.length()) {
+ throw new UnsupportedExpression();
+ }
+ char next = sqlPattern.charAt(i + 1);
+ if (next == '_' || next == '%') {
+ // Escaped wildcard -> literal in the LIKE output.
+ // Emit as outputEscape + wildcard so Like treats it as a
literal.
+ like.append(outputEscape).append(next);
+ } else if (next == escapeChar) {
+ // Escaped escape char -> emit as a literal character.
+ // If the escape char itself is special to LIKE (% or _),
escape it; otherwise
+ // emit it as-is since Like only special-cases % and _.
+ like.append(escapeChar);
+ } else {
+ // Unknown escape sequence - not supported
+ throw new UnsupportedExpression();
+ }
+ i++;
+ } else if (c == '%' || c == '_') {
+ // SIMILAR TO wildcards are the same as SQL LIKE wildcards
+ like.append(c);
+ } else if (c == '[' || c == '|' || c == '(' || c == ')'
+ || c == '*' || c == '+' || c == '?') {
Review Comment:
This unsupported-feature check is incomplete for SIMILAR TO. Calcite/Flink
treat `{` and `}` as SIMILAR special characters as well (quantifiers, e.g.
`a{2}`), but this conversion lets them pass through as literal LIKE characters.
That means a real SIMILAR predicate such as `col SIMILAR TO a{2}` can be pushed
down as a LIKE pattern matching the literal string `a{2}` instead of `aa`,
which is incorrect filtering. Please reject all SIMILAR-only metacharacters
that cannot be represented by Paimon LIKE, including `{` and `}` (and add a
regression test for such a pattern).
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]