This is an automated email from the ASF dual-hosted git repository.

snuyanzin pushed a commit to branch release-1.20
in repository https://gitbox.apache.org/repos/asf/flink.git


The following commit(s) were added to refs/heads/release-1.20 by this push:
     new 58ffbb0f0f5 [FLINK-39424][table] Setting LIKE does not support default 
escape characters
58ffbb0f0f5 is described below

commit 58ffbb0f0f5be719472d5a4be58b01c7556406f2
Author: Sergey Nuyanzin <[email protected]>
AuthorDate: Thu Apr 16 18:34:27 2026 +0200

    [FLINK-39424][table] Setting LIKE does not support default escape characters
---
 .../apache/flink/table/functions/SqlLikeUtils.java | 12 ++-
 .../table/planner/codegen/calls/LikeCallGen.scala  |  3 +
 .../planner/calcite/FlinkSqlLikeUtilsTest.java     | 30 ++++++++
 .../planner/functions/LikeFunctionITCase.java      |  6 +-
 .../planner/expressions/ScalarFunctionsTest.scala  | 88 ++++++++++++++++++++--
 5 files changed, 129 insertions(+), 10 deletions(-)

diff --git 
a/flink-table/flink-table-api-java/src/main/java/org/apache/flink/table/functions/SqlLikeUtils.java
 
b/flink-table/flink-table-api-java/src/main/java/org/apache/flink/table/functions/SqlLikeUtils.java
index c414f349b6e..380fe972cd7 100644
--- 
a/flink-table/flink-table-api-java/src/main/java/org/apache/flink/table/functions/SqlLikeUtils.java
+++ 
b/flink-table/flink-table-api-java/src/main/java/org/apache/flink/table/functions/SqlLikeUtils.java
@@ -96,6 +96,9 @@ public class SqlLikeUtils {
                 throw invalidEscapeCharacter(escapeStr.toString());
             }
             escapeChar = escapeStr.charAt(0);
+            if (escapeChar == 0) {
+                throw invalidEscapeCharacter(escapeStr.toString());
+            }
         } else {
             escapeChar = 0;
         }
@@ -112,7 +115,7 @@ public class SqlLikeUtils {
             if (JAVA_REGEX_SPECIALS.indexOf(c) >= 0) {
                 javaPattern.append('\\');
             }
-            if (c == escapeChar) {
+            if (c == escapeChar && escapeChar != 0) {
                 if (i == (sqlPattern.length() - 1)) {
                     throw invalidEscapeSequence(sqlPattern, i);
                 }
@@ -186,7 +189,7 @@ public class SqlLikeUtils {
             char c = sqlPattern.charAt(i);
             if (c == ']') {
                 return i - 1;
-            } else if (c == escapeChar) {
+            } else if (c == escapeChar && escapeChar != 0) {
                 i++;
                 char nextChar = sqlPattern.charAt(i);
                 if (SQL_SIMILAR_SPECIALS.indexOf(nextChar) >= 0) {
@@ -235,6 +238,9 @@ public class SqlLikeUtils {
                 throw invalidEscapeCharacter(escapeStr.toString());
             }
             escapeChar = escapeStr.charAt(0);
+            if (escapeChar == 0) {
+                throw invalidEscapeCharacter(escapeStr.toString());
+            }
         } else {
             escapeChar = 0;
         }
@@ -250,7 +256,7 @@ public class SqlLikeUtils {
         final int len = sqlPattern.length();
         for (int i = 0; i < len; i++) {
             char c = sqlPattern.charAt(i);
-            if (c == escapeChar) {
+            if (c == escapeChar && escapeChar != 0) {
                 if (i == (len - 1)) {
                     // It should never reach here after the escape rule
                     // checking.
diff --git 
a/flink-table/flink-table-planner/src/main/scala/org/apache/flink/table/planner/codegen/calls/LikeCallGen.scala
 
b/flink-table/flink-table-planner/src/main/scala/org/apache/flink/table/planner/codegen/calls/LikeCallGen.scala
index 7d05ad13d02..f734389b629 100644
--- 
a/flink-table/flink-table-planner/src/main/scala/org/apache/flink/table/planner/codegen/calls/LikeCallGen.scala
+++ 
b/flink-table/flink-table-planner/src/main/scala/org/apache/flink/table/planner/codegen/calls/LikeCallGen.scala
@@ -65,6 +65,9 @@ class LikeCallGen extends CallGenerator {
                 throw SqlLikeUtils.invalidEscapeCharacter(escape)
               }
               val escapeChar = escape.charAt(escape.length - 1)
+              if (escapeChar == 0) {
+                throw SqlLikeUtils.invalidEscapeCharacter(escape)
+              }
               var matched = true
               var i = 0
               val newBuilder = new StringBuilder
diff --git 
a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/calcite/FlinkSqlLikeUtilsTest.java
 
b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/calcite/FlinkSqlLikeUtilsTest.java
index 9a55dec5b13..6bc784b0638 100644
--- 
a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/calcite/FlinkSqlLikeUtilsTest.java
+++ 
b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/calcite/FlinkSqlLikeUtilsTest.java
@@ -23,6 +23,7 @@ import org.apache.flink.table.functions.SqlLikeUtils;
 import org.junit.jupiter.api.Test;
 
 import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
 
 /** Test for the SqlLikeUtils. */
 class FlinkSqlLikeUtilsTest {
@@ -33,8 +34,37 @@ class FlinkSqlLikeUtilsTest {
         assertThat(SqlLikeUtils.like("abcd", "a.*d", "\\")).isEqualTo(false);
         assertThat(SqlLikeUtils.like("abcde", "%c.e", "\\")).isEqualTo(false);
 
+        // no default escape character - backslash is treated as a literal 
character
+        assertThat(SqlLikeUtils.like("a-c", "a\\_c")).isEqualTo(false);
+        assertThat(SqlLikeUtils.like("a_c", "a\\_c")).isEqualTo(false);
+        assertThat(SqlLikeUtils.like("a\\_c", "a\\_c")).isEqualTo(true);
+
+        // default escape also excludes \u0000
+        assertThat(SqlLikeUtils.like("_", "\u0000_", null)).isEqualTo(false);
+        assertThat(SqlLikeUtils.like("\u0000x", "\u0000_", 
null)).isEqualTo(true);
+
+        // -------------------------------- sqlToRegexLike 
----------------------------------------
+
+        assertThat(SqlLikeUtils.sqlToRegexLike(".", "\\")).isEqualTo("\\.");
+        assertThat(SqlLikeUtils.sqlToRegexLike("c", "\\")).isEqualTo("c");
+        assertThat(SqlLikeUtils.sqlToRegexLike("_", "\\")).isEqualTo(".");
+        assertThat(SqlLikeUtils.sqlToRegexLike("%", 
"\\")).isEqualTo("(?s:.*)");
+
+        // exception
+        assertThatThrownBy(() -> SqlLikeUtils.sqlToRegexLike("\\a", "\\"))
+                .isInstanceOf(RuntimeException.class)
+                .hasMessageContaining("Invalid escape");
+        assertThatThrownBy(() -> SqlLikeUtils.sqlToRegexLike("\\", "\\"))
+                .isInstanceOf(RuntimeException.class)
+                .hasMessageContaining("Invalid escape");
+
+        // --------------------------------- similar 
----------------------------------------------
+
         assertThat(SqlLikeUtils.similar("abc", "a.c", "\\")).isEqualTo(true);
         assertThat(SqlLikeUtils.similar("a.c", "a.c", "\\")).isEqualTo(true);
         assertThat(SqlLikeUtils.similar("abcd", "a.*d", "\\")).isEqualTo(true);
+        // default escape also excludes \u0000
+        assertThat(SqlLikeUtils.similar("_", "\u0000_", 
null)).isEqualTo(false);
+        assertThat(SqlLikeUtils.similar("\u0000x", "\u0000_", 
null)).isEqualTo(true);
     }
 }
diff --git 
a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/functions/LikeFunctionITCase.java
 
b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/functions/LikeFunctionITCase.java
index f2c29eae578..17981f0fd8c 100644
--- 
a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/functions/LikeFunctionITCase.java
+++ 
b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/functions/LikeFunctionITCase.java
@@ -204,6 +204,10 @@ class LikeFunctionITCase extends BuiltInFunctionTestBase {
                         .testSqlResult(
                                 "'\btest\ne\\nd\f' LIKE '\btest\ne\\nd\f' 
ESCAPE '!'",
                                 true,
-                                DataTypes.BOOLEAN().notNull()));
+                                DataTypes.BOOLEAN().notNull())
+                        // Invalid escape character
+                        .testSqlValidationError(
+                                "f0 LIKE 'test' ESCAPE '\u0000'",
+                                "Invalid escape character '\u0000'"));
     }
 }
diff --git 
a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/expressions/ScalarFunctionsTest.scala
 
b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/expressions/ScalarFunctionsTest.scala
index 700f0138b97..975cdb32269 100644
--- 
a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/expressions/ScalarFunctionsTest.scala
+++ 
b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/expressions/ScalarFunctionsTest.scala
@@ -423,23 +423,99 @@ class ScalarFunctionsTest extends ScalarTypesTestBase {
   @Test
   def testLikeWithEscape(): Unit = {
     testSqlApi("f23 LIKE '&%Th_s%' ESCAPE '&'", "TRUE")
-
     testSqlApi("f23 LIKE '&%%is a%' ESCAPE '&'", "TRUE")
-
     testSqlApi("f0 LIKE 'Th_s%' ESCAPE '&'", "TRUE")
-
     testSqlApi("f0 LIKE '%is a%' ESCAPE '&'", "TRUE")
+
+    // normal escape character
+    testSqlApi("'TE-ST' LIKE '%E#_S%' ESCAPE '#'", "FALSE")
+    testSqlApi("'TE_ST' LIKE '%E#_S%' ESCAPE '#'", "TRUE")
+
+    // special character in SQL
+    testSqlApi("'TE-ST' LIKE '%E__S%' ESCAPE '_'", "FALSE")
+    testSqlApi("'TE_ST' LIKE '%E__S%' ESCAPE '_'", "TRUE")
+    testSqlApi("'TE-ST' LIKE 'TE%_ST' ESCAPE '%'", "FALSE")
+    testSqlApi("'TE_ST' LIKE 'TE%_ST' ESCAPE '%'", "TRUE")
+    testSqlApi("'TE-ST' LIKE '%E*_S%' ESCAPE '*'", "FALSE")
+    testSqlApi("'TE_ST' LIKE '%E*_S%' ESCAPE '*'", "TRUE")
+
+    // special character in Java Regex
+    testSqlApi("'TE-ST' LIKE '%E\\_S%' ESCAPE '\\'", "FALSE")
+    testSqlApi("'TE_ST' LIKE '%E\\_S%' ESCAPE '\\'", "TRUE")
+    testSqlApi("'TE-ST' LIKE '%E._S%' ESCAPE '.'", "FALSE")
+    testSqlApi("'TE_ST' LIKE '%E._S%' ESCAPE '.'", "TRUE")
+
+    // invalid escape character
+    testExpectedSqlException(
+      "'TE-ST' LIKE '%E_S%' ESCAPE 'ab'",
+      "Invalid escape",
+      classOf[RuntimeException])
+    testExpectedSqlException(
+      "'TE-ST' LIKE '%E_S%' ESCAPE '\\c'",
+      "Invalid escape",
+      classOf[RuntimeException])
+
+    // escape character at the end
+    testExpectedSqlException("'TE-ST' LIKE '%E_S%&' ESCAPE '&'", "", 
classOf[RuntimeException])
+
+    // invalid character after escape character
+    testExpectedSqlException(
+      "'TE-ST' LIKE '%E&-S%' ESCAPE '&'",
+      "Invalid escape",
+      classOf[RuntimeException])
+    testExpectedSqlException(
+      "'TE-ST' LIKE '%E_S%' ESCAPE '_'",
+      "Invalid escape",
+      classOf[RuntimeException])
   }
 
   @Test
   def testNotLikeWithEscape(): Unit = {
     testSqlApi("f23 NOT LIKE '&%Th_s%' ESCAPE '&'", "FALSE")
-
     testSqlApi("f23 NOT LIKE '&%%is a%' ESCAPE '&'", "FALSE")
-
     testSqlApi("f0 NOT LIKE 'Th_s%' ESCAPE '&'", "FALSE")
-
     testSqlApi("f0 NOT LIKE '%is a%' ESCAPE '&'", "FALSE")
+
+    // normal escape character
+    testSqlApi("'TE-ST' NOT LIKE '%E#_S%' ESCAPE '#'", "TRUE")
+    testSqlApi("'TE_ST' NOT LIKE '%E#_S%' ESCAPE '#'", "FALSE")
+
+    // special character in SQL
+    testSqlApi("'TE-ST' NOT LIKE '%E__S%' ESCAPE '_'", "TRUE")
+    testSqlApi("'TE_ST' NOT LIKE '%E__S%' ESCAPE '_'", "FALSE")
+    testSqlApi("'TE-ST' NOT LIKE 'TE%_ST' ESCAPE '%'", "TRUE")
+    testSqlApi("'TE_ST' NOT LIKE 'TE%_ST' ESCAPE '%'", "FALSE")
+    testSqlApi("'TE-ST' NOT LIKE '%E*_S%' ESCAPE '*'", "TRUE")
+    testSqlApi("'TE_ST' NOT LIKE '%E*_S%' ESCAPE '*'", "FALSE")
+
+    // special character in Java Regex
+    testSqlApi("'TE-ST' NOT LIKE '%E\\_S%' ESCAPE '\\'", "TRUE")
+    testSqlApi("'TE_ST' NOT LIKE '%E\\_S%' ESCAPE '\\'", "FALSE")
+    testSqlApi("'TE-ST' NOT LIKE '%E._S%' ESCAPE '.'", "TRUE")
+    testSqlApi("'TE_ST' NOT LIKE '%E._S%' ESCAPE '.'", "FALSE")
+
+    // invalid character
+    testExpectedSqlException(
+      "'TE-ST' NOT LIKE '%E_S%' ESCAPE 'ab'",
+      "Invalid escape",
+      classOf[RuntimeException])
+    testExpectedSqlException(
+      "'TE-ST' NOT LIKE '%E_S%' ESCAPE '\\c'",
+      "Invalid escape",
+      classOf[RuntimeException])
+
+    // escape character at the end
+    testExpectedSqlException("'TE-ST' NOT LIKE '%E_S%&' ESCAPE '&'", "", 
classOf[RuntimeException])
+
+    // invalid character after escape character
+    testExpectedSqlException(
+      "'TE-ST' NOT LIKE '%E&-S%' ESCAPE '&'",
+      "Invalid escape",
+      classOf[RuntimeException])
+    testExpectedSqlException(
+      "'TE-ST' NOT LIKE '%E_S%' ESCAPE '_'",
+      "Invalid escape",
+      classOf[RuntimeException])
   }
 
   @Test

Reply via email to