You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@iotdb.apache.org by ja...@apache.org on 2024/10/13 12:09:33 UTC

(iotdb) branch master updated: Add escape grammer and support like's SQL multi column transformer

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

jackietien pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/iotdb.git


The following commit(s) were added to refs/heads/master by this push:
     new 5994c9e4e96 Add escape grammer and support like's SQL multi column transformer
5994c9e4e96 is described below

commit 5994c9e4e968a258d01b29a222814ea1be6689aa
Author: Lin Xintao <lx...@163.com>
AuthorDate: Sun Oct 13 20:09:27 2024 +0800

    Add escape grammer and support like's SQL multi column transformer
---
 .../org/apache/iotdb/db/it/IoTDBNestedQueryIT.java |   6 +-
 .../iotdb/db/it/query/IoTDBFuzzyQueryIT.java       |  24 ++-
 .../iotdb/db/it/query/IoTDBNullOperandIT.java      |   2 +-
 .../it/query/old/query/IoTDBFuzzyQueryTableIT.java | 218 +++------------------
 .../org/apache/iotdb/db/qp/sql/IdentifierParser.g4 |   1 +
 .../org/apache/iotdb/db/qp/sql/IoTDBSqlParser.g4   |   3 +-
 .../antlr4/org/apache/iotdb/db/qp/sql/SqlLexer.g4  |   4 +
 .../relational/ColumnTransformerBuilder.java       |  46 +++--
 .../queryengine/plan/analyze/ExpressionUtils.java  |   2 +-
 .../plan/expression/unary/LikeExpression.java      |  82 +++++---
 .../visitor/ColumnTransformerVisitor.java          |   6 +-
 .../visitor/IntermediateLayerVisitor.java          |   5 +-
 .../visitor/TransformToViewExpressionVisitor.java  |   5 +-
 .../predicate/ConvertPredicateToFilterVisitor.java |   7 +-
 .../visitor/predicate/ReversePredicateVisitor.java |   2 +-
 .../db/queryengine/plan/parser/ASTVisitor.java     |  13 +-
 .../predicate/ConvertPredicateToFilterVisitor.java |   9 +-
 .../ConvertSchemaPredicateToFilterVisitor.java     |   5 +-
 .../dag/column/binary/Like2ColumnTransformer.java  |  92 +++++++++
 .../dag/column/ternary/Like3ColumnTransformer.java | 105 ++++++++++
 .../dag/transformer/unary/LikeTransformer.java     |   1 -
 .../view/visitor/TransformToExpressionVisitor.java |   5 +-
 .../schema/filter/impl/values/LikeFilter.java      |   5 +-
 .../viewExpression/unary/LikeViewExpression.java   |  65 +++---
 .../db/relational/grammar/sql/RelationalSql.g4     |   5 +-
 25 files changed, 417 insertions(+), 301 deletions(-)

diff --git a/integration-test/src/test/java/org/apache/iotdb/db/it/IoTDBNestedQueryIT.java b/integration-test/src/test/java/org/apache/iotdb/db/it/IoTDBNestedQueryIT.java
index 205f8b00910..f60bacb0fd0 100644
--- a/integration-test/src/test/java/org/apache/iotdb/db/it/IoTDBNestedQueryIT.java
+++ b/integration-test/src/test/java/org/apache/iotdb/db/it/IoTDBNestedQueryIT.java
@@ -624,12 +624,8 @@ public class IoTDBNestedQueryIT {
     try (Connection connection = EnvFactory.getEnv().getConnection();
         Statement statement = connection.createStatement()) {
       String query =
-          "SELECT s1 FROM root.vehicle.d1 WHERE s3 LIKE '_' && s3 REGEXP '[0-9]' && s3 IN ('4', '2', '3')";
+          "SELECT s1 FROM root.vehicle.d1 WHERE s3 LIKE '_' && s3 not REGEXP '[0-9]' && s3 IN ('4', '2', '3')";
       try (ResultSet rs = statement.executeQuery(query)) {
-        for (int i = 2; i <= 4; i++) {
-          Assert.assertTrue(rs.next());
-          Assert.assertEquals(i, rs.getLong(1));
-        }
         Assert.assertFalse(rs.next());
       }
 
diff --git a/integration-test/src/test/java/org/apache/iotdb/db/it/query/IoTDBFuzzyQueryIT.java b/integration-test/src/test/java/org/apache/iotdb/db/it/query/IoTDBFuzzyQueryIT.java
index a9e6e37dd56..2694faaf526 100644
--- a/integration-test/src/test/java/org/apache/iotdb/db/it/query/IoTDBFuzzyQueryIT.java
+++ b/integration-test/src/test/java/org/apache/iotdb/db/it/query/IoTDBFuzzyQueryIT.java
@@ -86,7 +86,7 @@ public class IoTDBFuzzyQueryIT {
     sqls.add(
         "insert into root.t1.wf01.wt01 (time,status,temperature) values (1509466080000,'123%',18.3)");
     sqls.add(
-        "insert into root.t1.wf01.wt01 (time,status,temperature) values (1509466090000,'\\\\',10.3)");
+        "insert into root.t1.wf01.wt01 (time,status,temperature) values (1509466090000,'\\\\\\\\',10.3)");
     sqls.add("insert into root.t1.wf01.wt02 (time,status) values (1509465600000,'14')");
   }
 
@@ -106,13 +106,15 @@ public class IoTDBFuzzyQueryIT {
     Connection connection = EnvFactory.getEnv().getConnection();
     Statement st0 = connection.createStatement();
     ResultSet resultSet =
-        st0.executeQuery("select status from root.t1.wf01.wt01 where status like '1'");
+        st0.executeQuery("select status from root.t1.wf01.wt01 where status not like '1'");
 
-    Assert.assertEquals("1", outputResultStr(resultSet));
+    Assert.assertEquals(
+        "14,616,626,6116,6%16,8[sS]*,%123,123%,\\\\\\\\", outputResultStr(resultSet));
 
     resultSet = st0.executeQuery("select status from root.t1.wf01.wt01 where status like '%'");
 
-    Assert.assertEquals("1,14,616,626,6116,6%16,8[sS]*,%123,123%,\\\\", outputResultStr(resultSet));
+    Assert.assertEquals(
+        "1,14,616,626,6116,6%16,8[sS]*,%123,123%,\\\\\\\\", outputResultStr(resultSet));
 
     resultSet = st0.executeQuery("select status from root.t1.wf01.wt01 where status like '1%'");
 
@@ -134,22 +136,28 @@ public class IoTDBFuzzyQueryIT {
 
     Assert.assertEquals("6116,6%16", outputResultStr(resultSet));
 
-    resultSet = st0.executeQuery("select status from root.t1.wf01.wt01 where status like '6\\%%'");
+    resultSet =
+        st0.executeQuery(
+            "select status from root.t1.wf01.wt01 where status like '6\\%%' escape '\\'");
 
     Assert.assertEquals("6%16", outputResultStr(resultSet));
 
-    resultSet = st0.executeQuery("select status from root.t1.wf01.wt01 where status like '\\%%'");
+    resultSet =
+        st0.executeQuery(
+            "select status from root.t1.wf01.wt01 where status like '\\%%' escape '\\'");
 
     Assert.assertEquals("%123", outputResultStr(resultSet));
 
-    resultSet = st0.executeQuery("select status from root.t1.wf01.wt01 where status like '%\\%'");
+    resultSet =
+        st0.executeQuery(
+            "select status from root.t1.wf01.wt01 where status like '%\\%' escape '\\'");
 
     Assert.assertEquals("123%", outputResultStr(resultSet));
 
     resultSet =
         st0.executeQuery("select status from root.t1.wf01.wt01 where status like '%\\\\\\\\%'");
 
-    Assert.assertEquals("\\\\", outputResultStr(resultSet));
+    Assert.assertEquals("\\\\\\\\", outputResultStr(resultSet));
   }
 
   @Test(expected = Exception.class)
diff --git a/integration-test/src/test/java/org/apache/iotdb/db/it/query/IoTDBNullOperandIT.java b/integration-test/src/test/java/org/apache/iotdb/db/it/query/IoTDBNullOperandIT.java
index b1aa94805f3..f292b162155 100644
--- a/integration-test/src/test/java/org/apache/iotdb/db/it/query/IoTDBNullOperandIT.java
+++ b/integration-test/src/test/java/org/apache/iotdb/db/it/query/IoTDBNullOperandIT.java
@@ -104,7 +104,7 @@ public class IoTDBNullOperandIT {
           "root.test.sg1.s1 = root.test.sg1.s2",
           "root.test.sg1.s1 > root.test.sg1.s2",
           "root.test.sg1.s1 < root.test.sg1.s2",
-          "root.test.sg1.s5 LIKE 'LikePattern{pattern='test', escape=\\}'",
+          "root.test.sg1.s5 LIKE pattern = 'test'",
           "root.test.sg1.s2 IN (1,2)",
           "root.test.sg1.s2 BETWEEN 1 AND 3",
         };
diff --git a/integration-test/src/test/java/org/apache/iotdb/relational/it/query/old/query/IoTDBFuzzyQueryTableIT.java b/integration-test/src/test/java/org/apache/iotdb/relational/it/query/old/query/IoTDBFuzzyQueryTableIT.java
index b6d807a2cf8..f3b5ef2ecb2 100644
--- a/integration-test/src/test/java/org/apache/iotdb/relational/it/query/old/query/IoTDBFuzzyQueryTableIT.java
+++ b/integration-test/src/test/java/org/apache/iotdb/relational/it/query/old/query/IoTDBFuzzyQueryTableIT.java
@@ -23,29 +23,22 @@ import org.apache.iotdb.it.env.EnvFactory;
 import org.apache.iotdb.it.framework.IoTDBTestRunner;
 import org.apache.iotdb.itbase.category.TableClusterIT;
 import org.apache.iotdb.itbase.category.TableLocalStandaloneIT;
+import org.apache.iotdb.itbase.env.BaseEnv;
 
 import org.junit.AfterClass;
 import org.junit.Assert;
 import org.junit.BeforeClass;
-import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.experimental.categories.Category;
 import org.junit.runner.RunWith;
 
 import java.sql.Connection;
 import java.sql.ResultSet;
-import java.sql.ResultSetMetaData;
 import java.sql.SQLException;
 import java.sql.Statement;
-import java.sql.Types;
 import java.util.ArrayList;
-import java.util.HashMap;
 import java.util.List;
-import java.util.Map;
 
-import static org.junit.Assert.fail;
-
-@Ignore // TODO in V2
 @RunWith(IoTDBTestRunner.class)
 @Category({TableLocalStandaloneIT.class, TableClusterIT.class})
 public class IoTDBFuzzyQueryTableIT {
@@ -64,37 +57,20 @@ public class IoTDBFuzzyQueryTableIT {
   }
 
   private static void initCreateSQLStatement() {
-    sqls.add("CREATE DATABASE root.t1");
-    sqls.add("CREATE TIMESERIES root.t1.wf01.wt01.status WITH DATATYPE=TEXT, ENCODING=PLAIN");
-    sqls.add("CREATE TIMESERIES root.t1.wf01.wt01.temperature WITH DATATYPE=FLOAT, ENCODING=RLE");
-    sqls.add("CREATE TIMESERIES root.t1.wf01.wt02.status WITH DATATYPE=TEXT, ENCODING=PLAIN");
-
-    sqls.add(
-        "insert into root.t1.wf01.wt01 (time,status,temperature) values (1509465600000,'1',12.1)");
-    sqls.add(
-        "insert into root.t1.wf01.wt01 (time,status,temperature) values (1509465660000,'14',13.1)");
-    sqls.add(
-        "insert into root.t1.wf01.wt01 (time,status,temperature) values (1509465720000,'616',5.5)");
-    sqls.add(
-        "insert into root.t1.wf01.wt01 (time,status,temperature) values (1509465780000,'626',8.1)");
-    sqls.add(
-        "insert into root.t1.wf01.wt01 (time,status,temperature) values (1509465840000,'6116',4.3)");
+    sqls.add("CREATE DATABASE TestFuzzyQuery");
+    sqls.add("USE TestFuzzyQuery");
     sqls.add(
-        "insert into root.t1.wf01.wt01 (time,status,temperature) values (1509465900000,'6%16',10.3)");
+        "CREATE TABLE likeTest(device_id STRING ID, s1 TEXT MEASUREMENT, s2 STRING MEASUREMENT, s3 STRING MEASUREMENT)");
     sqls.add(
-        "insert into root.t1.wf01.wt01 (time,status,temperature) values (1509465960000,'8[sS]*',11.3)");
+        "INSERT INTO likeTest(time, device_id, s1, s2, s3) VALUES(1,'d1', 'abcdef', 'a%', '\\')");
     sqls.add(
-        "insert into root.t1.wf01.wt01 (time,status,temperature) values (1509466020000,'%123',18.3)");
+        "INSERT INTO likeTest(time, device_id, s1, s2, s3) VALUES(2,'d1', '_abcdef', '\\_a%','\\')");
     sqls.add(
-        "insert into root.t1.wf01.wt01 (time,status,temperature) values (1509466080000,'123%',18.3)");
-    sqls.add(
-        "insert into root.t1.wf01.wt01 (time,status,temperature) values (1509466090000,'\\\\',10.3)");
-    sqls.add("insert into root.t1.wf01.wt02 (time,status) values (1509465600000,'14')");
+        "INSERT INTO likeTest(time, device_id, s1, s2, s3) VALUES(3,'d1', 'abcdef%', '%_\\%','\\')");
   }
 
   private static void insertData() throws SQLException {
-
-    Connection connection = EnvFactory.getEnv().getConnection();
+    Connection connection = EnvFactory.getEnv().getConnection(BaseEnv.TABLE_SQL_DIALECT);
     Statement statement = connection.createStatement();
 
     for (String sql : sqls) {
@@ -105,171 +81,27 @@ public class IoTDBFuzzyQueryTableIT {
 
   @Test
   public void testLike() throws SQLException {
-    Connection connection = EnvFactory.getEnv().getConnection();
-    Statement st0 = connection.createStatement();
-    ResultSet resultSet =
-        st0.executeQuery("select status from root.t1.wf01.wt01 where status like '1'");
-
-    Assert.assertEquals("1", outputResultStr(resultSet));
-
-    resultSet = st0.executeQuery("select status from root.t1.wf01.wt01 where status like '%'");
-
-    Assert.assertEquals("1,14,616,626,6116,6%16,8[sS]*,%123,123%,\\\\", outputResultStr(resultSet));
-
-    resultSet = st0.executeQuery("select status from root.t1.wf01.wt01 where status like '1%'");
-
-    Assert.assertEquals("1,14,123%", outputResultStr(resultSet));
-
-    resultSet = st0.executeQuery("select status from root.t1.wf01.wt01 where status like '%1%'");
-
-    Assert.assertEquals("1,14,616,6116,6%16,%123,123%", outputResultStr(resultSet));
-
-    resultSet = st0.executeQuery("select status from root.t1.wf01.wt01 where status like '6%6'");
-
-    Assert.assertEquals("616,626,6116,6%16", outputResultStr(resultSet));
-
-    resultSet = st0.executeQuery("select status from root.t1.wf01.wt01 where status like '1_'");
-
-    Assert.assertEquals("14", outputResultStr(resultSet));
-
-    resultSet = st0.executeQuery("select status from root.t1.wf01.wt01 where status like '6_1%'");
-
-    Assert.assertEquals("6116,6%16", outputResultStr(resultSet));
-
-    resultSet = st0.executeQuery("select status from root.t1.wf01.wt01 where status like '6\\%%'");
-
-    Assert.assertEquals("6%16", outputResultStr(resultSet));
-
-    resultSet = st0.executeQuery("select status from root.t1.wf01.wt01 where status like '\\%%'");
-
-    Assert.assertEquals("%123", outputResultStr(resultSet));
-
-    resultSet = st0.executeQuery("select status from root.t1.wf01.wt01 where status like '%\\%'");
-
-    Assert.assertEquals("123%", outputResultStr(resultSet));
-
-    resultSet =
-        st0.executeQuery("select status from root.t1.wf01.wt01 where status like '%\\\\\\\\%'");
-
-    Assert.assertEquals("\\\\", outputResultStr(resultSet));
-  }
-
-  @Test(expected = Exception.class)
-  public void testLikeNonTextCloumn() throws SQLException {
-    Connection connection = EnvFactory.getEnv().getConnection();
-    Statement st1 = connection.createStatement();
-    st1.executeQuery("select * from root.t1.wf01.wt01 where temperature like '1'");
-  }
-
-  private String outputResultStr(ResultSet resultSet) throws SQLException {
-    StringBuilder resultBuilder = new StringBuilder();
-    while (resultSet.next()) {
-      resultBuilder.append(resultSet.getString(2)).append(",");
-    }
-    String result = resultBuilder.toString();
-    return result.substring(0, result.length() - 1);
-  }
-
-  private List<Integer> checkHeader(
-      ResultSetMetaData resultSetMetaData, String expectedHeaderStrings, int[] expectedTypes)
-      throws SQLException {
-    String[] expectedHeaders = expectedHeaderStrings.split(",");
-    Map<String, Integer> expectedHeaderToTypeIndexMap = new HashMap<>();
-    for (int i = 0; i < expectedHeaders.length; ++i) {
-      expectedHeaderToTypeIndexMap.put(expectedHeaders[i], i);
-    }
-
-    List<Integer> actualIndexToExpectedIndexList = new ArrayList<>();
-    for (int i = 1; i <= resultSetMetaData.getColumnCount(); i++) {
-      Integer typeIndex = expectedHeaderToTypeIndexMap.get(resultSetMetaData.getColumnName(i));
-      Assert.assertNotNull(typeIndex);
-      Assert.assertEquals(expectedTypes[typeIndex], resultSetMetaData.getColumnType(i));
-      actualIndexToExpectedIndexList.add(typeIndex);
-    }
-    return actualIndexToExpectedIndexList;
-  }
-
-  @Test
-  public void selectLikeAlignByDevice() throws ClassNotFoundException {
-    String[] retArray =
-        new String[] {"1509465660000,root.t1.wf01.wt01,14,", "1509465600000,root.t1.wf01.wt02,14"};
-    try (Connection connection = EnvFactory.getEnv().getConnection();
-        Statement statement = connection.createStatement()) {
-
-      try (ResultSet resultSet =
-          statement.executeQuery(
-              "select status from root.t1.wf01.wt0* where status like '14%' align by device")) {
-        ResultSetMetaData resultSetMetaData = resultSet.getMetaData();
-        List<Integer> actualIndexToExpectedIndexList =
-            checkHeader(
-                resultSetMetaData,
-                "Time,Device,status,",
-                new int[] {Types.TIMESTAMP, Types.VARCHAR, Types.VARCHAR});
-
-        int cnt = 0;
-        while (resultSet.next()) {
-          String[] expectedStrings = retArray[cnt].split(",");
-          StringBuilder expectedBuilder = new StringBuilder();
-          StringBuilder actualBuilder = new StringBuilder();
-          for (int i = 1; i <= resultSetMetaData.getColumnCount(); i++) {
-            actualBuilder.append(resultSet.getString(i)).append(",");
-            expectedBuilder
-                .append(expectedStrings[actualIndexToExpectedIndexList.get(i - 1)])
-                .append(",");
-          }
-          Assert.assertEquals(expectedBuilder.toString(), actualBuilder.toString());
-          cnt++;
-        }
-        Assert.assertEquals(2, cnt);
+    Connection connection = EnvFactory.getEnv().getConnection(BaseEnv.TABLE_SQL_DIALECT);
+    Statement statement = connection.createStatement();
+    statement.execute("USE TestFuzzyQuery");
+    String[] ans = new String[] {"abcdef"};
+    String query = "SELECT s1 FROM likeTest where s1 LIKE s2";
+    try (ResultSet rs = statement.executeQuery(query)) {
+      for (int i = 0; i < 1; i++) {
+        Assert.assertTrue(rs.next());
+        Assert.assertEquals(ans[i], rs.getString(1));
       }
-    } catch (Exception e) {
-      e.printStackTrace();
-      fail(e.getMessage());
+      Assert.assertFalse(rs.next());
     }
-  }
-
-  @Test
-  public void selectRegexpAlignByDevice() throws ClassNotFoundException {
-    String[] retArray =
-        new String[] {
-          "1509465600000,root.t1.wf01.wt01,1,",
-          "1509465660000,root.t1.wf01.wt01,14,",
-          "1509466080000,root.t1.wf01.wt01,123%,",
-          "1509465600000,root.t1.wf01.wt02,14,"
-        };
-
-    try (Connection connection = EnvFactory.getEnv().getConnection();
-        Statement statement = connection.createStatement()) {
-
-      try (ResultSet resultSet =
-          statement.executeQuery(
-              "select status from root.t1.wf01.wt0* where status regexp '^1.*' align by device"); ) {
-        ResultSetMetaData resultSetMetaData = resultSet.getMetaData();
-        List<Integer> actualIndexToExpectedIndexList =
-            checkHeader(
-                resultSetMetaData,
-                "Time,Device,status,",
-                new int[] {Types.TIMESTAMP, Types.VARCHAR, Types.VARCHAR});
 
-        int cnt = 0;
-        while (resultSet.next()) {
-          String[] expectedStrings = retArray[cnt].split(",");
-          StringBuilder expectedBuilder = new StringBuilder();
-          StringBuilder actualBuilder = new StringBuilder();
-          for (int i = 1; i <= resultSetMetaData.getColumnCount(); i++) {
-            actualBuilder.append(resultSet.getString(i)).append(",");
-            expectedBuilder
-                .append(expectedStrings[actualIndexToExpectedIndexList.get(i - 1)])
-                .append(",");
-          }
-          Assert.assertEquals(expectedBuilder.toString(), actualBuilder.toString());
-          cnt++;
-        }
-        Assert.assertEquals(4, cnt);
+    ans = new String[] {"abcdef", "_abcdef", "abcdef%"};
+    query = "SELECT s1 FROM likeTest where s1 LIKE s2 ESCAPE s3";
+    try (ResultSet rs = statement.executeQuery(query)) {
+      for (int i = 0; i < 3; i++) {
+        Assert.assertTrue(rs.next());
+        Assert.assertEquals(ans[i], rs.getString(1));
       }
-    } catch (Exception e) {
-      e.printStackTrace();
-      fail(e.getMessage());
+      Assert.assertFalse(rs.next());
     }
   }
 }
diff --git a/iotdb-core/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/IdentifierParser.g4 b/iotdb-core/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/IdentifierParser.g4
index fac1ad4b46e..bb528c001e2 100644
--- a/iotdb-core/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/IdentifierParser.g4
+++ b/iotdb-core/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/IdentifierParser.g4
@@ -96,6 +96,7 @@ keyWords
     | ELSE
     | END
     | ENDTIME
+    | ESCAPE
     | EVERY
     | EXPLAIN
     | EXTRACTOR
diff --git a/iotdb-core/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/IoTDBSqlParser.g4 b/iotdb-core/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/IoTDBSqlParser.g4
index 25bff7e5499..4bbe56b06d5 100644
--- a/iotdb-core/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/IoTDBSqlParser.g4
+++ b/iotdb-core/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/IoTDBSqlParser.g4
@@ -1274,7 +1274,8 @@ expression
     | leftExpression=expression (STAR | DIV | MOD) rightExpression=expression
     | leftExpression=expression (PLUS | MINUS) rightExpression=expression
     | leftExpression=expression (OPERATOR_GT | OPERATOR_GTE | OPERATOR_LT | OPERATOR_LTE | OPERATOR_SEQ | OPERATOR_DEQ | OPERATOR_NEQ) rightExpression=expression
-    | unaryBeforeRegularOrLikeExpression=expression operator_not? (REGEXP | LIKE) STRING_LITERAL
+    | unaryBeforeRegularOrLikeExpression=expression operator_not? REGEXP pattern=STRING_LITERAL
+    | unaryBeforeRegularOrLikeExpression=expression operator_not? LIKE pattern=STRING_LITERAL (ESCAPE escapeSet=STRING_LITERAL)?
     | firstExpression=expression operator_not? operator_between secondExpression=expression operator_and thirdExpression=expression
     | unaryBeforeIsNullExpression=expression operator_is operator_not? null_literal
     | unaryBeforeInExpression=expression operator_not? (operator_in | operator_contains) LR_BRACKET constant (COMMA constant)* RR_BRACKET
diff --git a/iotdb-core/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/SqlLexer.g4 b/iotdb-core/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/SqlLexer.g4
index 58a35d494f2..a2803be52a4 100644
--- a/iotdb-core/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/SqlLexer.g4
+++ b/iotdb-core/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/SqlLexer.g4
@@ -286,6 +286,10 @@ ENDTIME
     : E N D T I M E
     ;
 
+ESCAPE
+    : E S C A P E
+    ;
+
 EVERY
     : E V E R Y
     ;
diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/relational/ColumnTransformerBuilder.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/relational/ColumnTransformerBuilder.java
index fce36442462..6fa27f8ba3c 100644
--- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/relational/ColumnTransformerBuilder.java
+++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/relational/ColumnTransformerBuilder.java
@@ -76,6 +76,7 @@ import org.apache.iotdb.db.queryengine.transformation.dag.column.binary.CompareG
 import org.apache.iotdb.db.queryengine.transformation.dag.column.binary.CompareLessEqualColumnTransformer;
 import org.apache.iotdb.db.queryengine.transformation.dag.column.binary.CompareLessThanColumnTransformer;
 import org.apache.iotdb.db.queryengine.transformation.dag.column.binary.CompareNonEqualColumnTransformer;
+import org.apache.iotdb.db.queryengine.transformation.dag.column.binary.Like2ColumnTransformer;
 import org.apache.iotdb.db.queryengine.transformation.dag.column.leaf.ConstantColumnTransformer;
 import org.apache.iotdb.db.queryengine.transformation.dag.column.leaf.IdentityColumnTransformer;
 import org.apache.iotdb.db.queryengine.transformation.dag.column.leaf.LeafColumnTransformer;
@@ -92,6 +93,7 @@ import org.apache.iotdb.db.queryengine.transformation.dag.column.multi.InMultiCo
 import org.apache.iotdb.db.queryengine.transformation.dag.column.multi.LogicalAndMultiColumnTransformer;
 import org.apache.iotdb.db.queryengine.transformation.dag.column.multi.LogicalOrMultiColumnTransformer;
 import org.apache.iotdb.db.queryengine.transformation.dag.column.ternary.BetweenColumnTransformer;
+import org.apache.iotdb.db.queryengine.transformation.dag.column.ternary.Like3ColumnTransformer;
 import org.apache.iotdb.db.queryengine.transformation.dag.column.unary.IsNullColumnTransformer;
 import org.apache.iotdb.db.queryengine.transformation.dag.column.unary.LikeColumnTransformer;
 import org.apache.iotdb.db.queryengine.transformation.dag.column.unary.LogicNotColumnTransformer;
@@ -174,10 +176,10 @@ import java.util.Optional;
 import java.util.Set;
 import java.util.stream.Collectors;
 
+import static org.apache.iotdb.db.queryengine.plan.expression.unary.LikeExpression.getEscapeCharacter;
 import static org.apache.iotdb.db.queryengine.plan.relational.analyzer.predicate.PredicatePushIntoMetadataChecker.isStringLiteral;
 import static org.apache.iotdb.db.queryengine.plan.relational.type.InternalTypeManager.getTSDataType;
 import static org.apache.iotdb.db.queryengine.plan.relational.type.TypeSignatureTranslator.toTypeSignature;
-import static org.apache.tsfile.common.regexp.LikePattern.getEscapeCharacter;
 import static org.apache.tsfile.read.common.type.BlobType.BLOB;
 import static org.apache.tsfile.read.common.type.BooleanType.BOOLEAN;
 import static org.apache.tsfile.read.common.type.DoubleType.DOUBLE;
@@ -1155,19 +1157,37 @@ public class ColumnTransformerBuilder
         context.inputDataTypes.add(TSDataType.BOOLEAN);
         context.cache.put(node, identity);
       } else {
+        ColumnTransformer likeColumnTransformer = null;
         ColumnTransformer childColumnTransformer = process(node.getValue(), context);
-        Optional<String> escapeValueOpt =
-            node.getEscape().isPresent()
-                ? Optional.ofNullable(((StringLiteral) node.getEscape().get()).getValue())
-                : Optional.empty();
-        context.cache.put(
-            node,
-            new LikeColumnTransformer(
-                BOOLEAN,
-                childColumnTransformer,
-                LikePattern.compile(
-                    ((StringLiteral) node.getPattern()).getValue(),
-                    getEscapeCharacter(escapeValueOpt))));
+        if ((isStringLiteral(node.getPattern()) && !node.getEscape().isPresent())
+            || (isStringLiteral(node.getPattern()) && isStringLiteral(node.getEscape().get()))) {
+          Optional<Character> escapeSet =
+              node.getEscape().isPresent()
+                  ? getEscapeCharacter(((StringLiteral) node.getEscape().get()).getValue())
+                  : Optional.empty();
+          likeColumnTransformer =
+              new LikeColumnTransformer(
+                  BOOLEAN,
+                  childColumnTransformer,
+                  LikePattern.compile(((StringLiteral) node.getPattern()).getValue(), escapeSet));
+        } else {
+          ColumnTransformer patternColumnTransformer = process(node.getPattern(), context);
+          if (node.getEscape().isPresent()) {
+            ColumnTransformer escapeColumnTransformer = process(node.getEscape().get(), context);
+            likeColumnTransformer =
+                new Like3ColumnTransformer(
+                    BOOLEAN,
+                    childColumnTransformer,
+                    patternColumnTransformer,
+                    escapeColumnTransformer);
+          } else {
+            likeColumnTransformer =
+                new Like2ColumnTransformer(
+                    BOOLEAN, childColumnTransformer, patternColumnTransformer);
+          }
+        }
+
+        context.cache.put(node, likeColumnTransformer);
       }
     }
     ColumnTransformer res = context.cache.get(node);
diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/ExpressionUtils.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/ExpressionUtils.java
index 41175c08c9d..6d76384c1c8 100644
--- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/ExpressionUtils.java
+++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/ExpressionUtils.java
@@ -170,8 +170,8 @@ public class ExpressionUtils {
         resultExpression =
             new LikeExpression(
                 childExpression,
-                ((LikeExpression) rawExpression).getPatternString(),
                 ((LikeExpression) rawExpression).getPattern(),
+                ((LikeExpression) rawExpression).getEscape(),
                 ((LikeExpression) rawExpression).isNot());
         break;
       case LOGIC_NOT:
diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/expression/unary/LikeExpression.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/expression/unary/LikeExpression.java
index 69b788cf3a4..f9cdf0427bf 100644
--- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/expression/unary/LikeExpression.java
+++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/expression/unary/LikeExpression.java
@@ -19,12 +19,12 @@
 
 package org.apache.iotdb.db.queryengine.plan.expression.unary;
 
+import org.apache.iotdb.db.exception.sql.SemanticException;
 import org.apache.iotdb.db.queryengine.execution.MemoryEstimationHelper;
 import org.apache.iotdb.db.queryengine.plan.expression.Expression;
 import org.apache.iotdb.db.queryengine.plan.expression.ExpressionType;
 import org.apache.iotdb.db.queryengine.plan.expression.visitor.ExpressionVisitor;
 
-import org.apache.tsfile.common.regexp.LikePattern;
 import org.apache.tsfile.utils.RamUsageEstimator;
 import org.apache.tsfile.utils.ReadWriteIOUtils;
 
@@ -33,46 +33,48 @@ import java.io.IOException;
 import java.nio.ByteBuffer;
 import java.util.Optional;
 
-import static org.apache.tsfile.common.regexp.LikePattern.getEscapeCharacter;
-
 public class LikeExpression extends UnaryExpression {
 
   private static final long INSTANCE_SIZE =
       RamUsageEstimator.shallowSizeOfInstance(LikeExpression.class);
 
-  private final String patternString;
-  private final LikePattern pattern;
+  private final String pattern;
+  private final Optional<Character> escape;
 
   private final boolean isNot;
 
-  public LikeExpression(Expression expression, String patternString, boolean isNot) {
+  public LikeExpression(
+      Expression expression, String pattern, Optional<Character> escape, boolean isNot) {
     super(expression);
-    this.patternString = patternString;
+    this.pattern = pattern;
+    this.escape = escape;
     this.isNot = isNot;
-    pattern = LikePattern.compile(patternString, getEscapeCharacter(Optional.of("\\")));
   }
 
-  public LikeExpression(
-      Expression expression, String patternString, LikePattern pattern, boolean isNot) {
+  public LikeExpression(Expression expression, String pattern, boolean isNot) {
     super(expression);
-    this.patternString = patternString;
     this.pattern = pattern;
+    this.escape = Optional.empty();
     this.isNot = isNot;
   }
 
   public LikeExpression(ByteBuffer byteBuffer) {
     super(Expression.deserialize(byteBuffer));
-    patternString = ReadWriteIOUtils.readString(byteBuffer);
+    pattern = ReadWriteIOUtils.readString(byteBuffer);
+    if (ReadWriteIOUtils.readBool(byteBuffer)) {
+      escape = Optional.of(ReadWriteIOUtils.readString(byteBuffer).charAt(0));
+    } else {
+      escape = Optional.empty();
+    }
     isNot = ReadWriteIOUtils.readBool(byteBuffer);
-    pattern = LikePattern.compile(patternString, getEscapeCharacter(Optional.of("\\")));
   }
 
-  public String getPatternString() {
-    return patternString;
+  public String getPattern() {
+    return pattern;
   }
 
-  public LikePattern getPattern() {
-    return pattern;
+  public Optional<Character> getEscape() {
+    return escape;
   }
 
   public boolean isNot() {
@@ -81,7 +83,17 @@ public class LikeExpression extends UnaryExpression {
 
   @Override
   protected String getExpressionStringInternal() {
-    return expression.getExpressionString() + (isNot ? " NOT" : "") + " LIKE '" + pattern + "'";
+    String res =
+        expression.getExpressionString()
+            + (isNot ? " NOT" : "")
+            + " LIKE "
+            + "pattern = '"
+            + pattern
+            + "'";
+    if (escape.isPresent()) {
+      res = res + " escape = '" + escape.get() + "'";
+    }
+    return res;
   }
 
   @Override
@@ -92,20 +104,38 @@ public class LikeExpression extends UnaryExpression {
   @Override
   protected void serialize(ByteBuffer byteBuffer) {
     super.serialize(byteBuffer);
-    ReadWriteIOUtils.write(patternString, byteBuffer);
+    ReadWriteIOUtils.write(pattern, byteBuffer);
+    ReadWriteIOUtils.write(escape.isPresent(), byteBuffer);
+    if (escape.isPresent()) {
+      ReadWriteIOUtils.write(escape.get().toString(), byteBuffer);
+    }
     ReadWriteIOUtils.write(isNot, byteBuffer);
   }
 
   @Override
   protected void serialize(DataOutputStream stream) throws IOException {
     super.serialize(stream);
-    ReadWriteIOUtils.write(patternString, stream);
+    ReadWriteIOUtils.write(pattern, stream);
+    ReadWriteIOUtils.write(escape.isPresent(), stream);
+    if (escape.isPresent()) {
+      ReadWriteIOUtils.write(escape.get().toString(), stream);
+    }
     ReadWriteIOUtils.write(isNot, stream);
   }
 
   @Override
   public String getOutputSymbolInternal() {
-    return expression.getOutputSymbol() + (isNot ? " NOT" : "") + " LIKE '" + pattern + "'";
+    String res =
+        expression.getOutputSymbol()
+            + (isNot ? " NOT" : "")
+            + " LIKE "
+            + "pattern = '"
+            + pattern
+            + "'";
+    if (escape.isPresent()) {
+      res = res + " escape = '" + escape.get() + "'";
+    }
+    return res;
   }
 
   @Override
@@ -117,6 +147,14 @@ public class LikeExpression extends UnaryExpression {
   public long ramBytesUsed() {
     return INSTANCE_SIZE
         + MemoryEstimationHelper.getEstimatedSizeOfAccountableObject(expression)
-        + RamUsageEstimator.sizeOf(patternString);
+        + RamUsageEstimator.sizeOf(pattern);
+  }
+
+  public static Optional<Character> getEscapeCharacter(String escape) {
+    if (escape.length() == 1) {
+      return Optional.of(escape.charAt(0));
+    } else {
+      throw new SemanticException("Escape string must be a single character");
+    }
   }
 }
diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/expression/visitor/ColumnTransformerVisitor.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/expression/visitor/ColumnTransformerVisitor.java
index bbc12fc7cb3..498cac300fb 100644
--- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/expression/visitor/ColumnTransformerVisitor.java
+++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/expression/visitor/ColumnTransformerVisitor.java
@@ -71,6 +71,7 @@ import org.apache.iotdb.db.queryengine.transformation.dag.udf.UDTFContext;
 import org.apache.iotdb.db.queryengine.transformation.dag.udf.UDTFExecutor;
 import org.apache.iotdb.db.queryengine.transformation.dag.util.TransformUtils;
 
+import org.apache.tsfile.common.regexp.LikePattern;
 import org.apache.tsfile.enums.TSDataType;
 import org.apache.tsfile.read.common.type.LongType;
 import org.apache.tsfile.read.common.type.Type;
@@ -443,8 +444,9 @@ public class ColumnTransformerVisitor
         return new ArithmeticNegationColumnTransformer(returnType, childColumnTransformer);
       case LIKE:
         LikeExpression likeExpression = (LikeExpression) expression;
-        return new LikeColumnTransformer(
-            returnType, childColumnTransformer, likeExpression.getPattern());
+        LikePattern pattern =
+            LikePattern.compile(likeExpression.getPattern(), likeExpression.getEscape());
+        return new LikeColumnTransformer(returnType, childColumnTransformer, pattern);
       case REGEXP:
         RegularExpression regularExpression = (RegularExpression) expression;
         return new RegularColumnTransformer(
diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/expression/visitor/IntermediateLayerVisitor.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/expression/visitor/IntermediateLayerVisitor.java
index 889e90f14e7..b2ab7c6e10f 100644
--- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/expression/visitor/IntermediateLayerVisitor.java
+++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/expression/visitor/IntermediateLayerVisitor.java
@@ -75,6 +75,7 @@ import org.apache.iotdb.db.queryengine.transformation.dag.udf.UDTFContext;
 import org.apache.iotdb.db.queryengine.transformation.dag.udf.UDTFExecutor;
 import org.apache.iotdb.udf.api.customizer.strategy.AccessStrategy;
 
+import org.apache.tsfile.common.regexp.LikePattern;
 import org.apache.tsfile.enums.TSDataType;
 
 import java.io.IOException;
@@ -312,7 +313,9 @@ public class IntermediateLayerVisitor
         return new ArithmeticNegationTransformer(parentReader);
       case LIKE:
         LikeExpression likeExpression = (LikeExpression) expression;
-        return new LikeTransformer(parentReader, likeExpression.getPattern());
+        LikePattern pattern =
+            LikePattern.compile(likeExpression.getPattern(), likeExpression.getEscape());
+        return new LikeTransformer(parentReader, pattern);
       case REGEXP:
         RegularExpression regularExpression = (RegularExpression) expression;
         return new RegularTransformer(parentReader, regularExpression.getPattern());
diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/expression/visitor/TransformToViewExpressionVisitor.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/expression/visitor/TransformToViewExpressionVisitor.java
index 28e1a19c087..27befb28c90 100644
--- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/expression/visitor/TransformToViewExpressionVisitor.java
+++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/expression/visitor/TransformToViewExpressionVisitor.java
@@ -137,10 +137,7 @@ public class TransformToViewExpressionVisitor extends ExpressionVisitor<ViewExpr
   public ViewExpression visitLikeExpression(LikeExpression likeExpression, Void context) {
     ViewExpression child = this.process(likeExpression.getExpression(), context);
     return new LikeViewExpression(
-        child,
-        likeExpression.getPatternString(),
-        likeExpression.getPattern(),
-        likeExpression.isNot());
+        child, likeExpression.getPattern(), likeExpression.getEscape(), likeExpression.isNot());
   }
 
   @Override
diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/expression/visitor/predicate/ConvertPredicateToFilterVisitor.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/expression/visitor/predicate/ConvertPredicateToFilterVisitor.java
index 126313a46f0..a11f2064243 100644
--- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/expression/visitor/predicate/ConvertPredicateToFilterVisitor.java
+++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/expression/visitor/predicate/ConvertPredicateToFilterVisitor.java
@@ -45,6 +45,7 @@ import org.apache.iotdb.db.utils.DateTimeUtils;
 
 import com.google.common.io.BaseEncoding;
 import org.apache.tsfile.common.conf.TSFileConfig;
+import org.apache.tsfile.common.regexp.LikePattern;
 import org.apache.tsfile.enums.TSDataType;
 import org.apache.tsfile.read.filter.basic.Filter;
 import org.apache.tsfile.read.filter.factory.FilterFactory;
@@ -159,10 +160,12 @@ public class ConvertPredicateToFilterVisitor
     PartialPath path = ((TimeSeriesOperand) operand).getPath();
     int measurementIndex = context.getMeasurementIndex(path.getMeasurement());
     TSDataType dataType = context.getType(path);
+    LikePattern pattern =
+        LikePattern.compile(likeExpression.getPattern(), likeExpression.getEscape());
     if (likeExpression.isNot()) {
-      return ValueFilterApi.notLike(measurementIndex, likeExpression.getPattern(), dataType);
+      return ValueFilterApi.notLike(measurementIndex, pattern, dataType);
     } else {
-      return ValueFilterApi.like(measurementIndex, likeExpression.getPattern(), dataType);
+      return ValueFilterApi.like(measurementIndex, pattern, dataType);
     }
   }
 
diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/expression/visitor/predicate/ReversePredicateVisitor.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/expression/visitor/predicate/ReversePredicateVisitor.java
index 0188a247d57..e9b5c715fb4 100644
--- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/expression/visitor/predicate/ReversePredicateVisitor.java
+++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/expression/visitor/predicate/ReversePredicateVisitor.java
@@ -59,8 +59,8 @@ public class ReversePredicateVisitor extends PredicateVisitor<Expression, Void>
   public Expression visitLikeExpression(LikeExpression likeExpression, Void context) {
     return new LikeExpression(
         likeExpression.getExpression(),
-        likeExpression.getPatternString(),
         likeExpression.getPattern(),
+        likeExpression.getEscape(),
         !likeExpression.isNot());
   }
 
diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/parser/ASTVisitor.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/parser/ASTVisitor.java
index 2a59e685793..2139fe40d74 100644
--- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/parser/ASTVisitor.java
+++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/parser/ASTVisitor.java
@@ -248,6 +248,7 @@ import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
+import java.util.Optional;
 import java.util.Set;
 import java.util.function.BiConsumer;
 import java.util.function.Consumer;
@@ -255,6 +256,7 @@ import java.util.regex.Pattern;
 import java.util.stream.Collectors;
 
 import static org.apache.iotdb.commons.schema.SchemaConstant.ALL_RESULT_NODES;
+import static org.apache.iotdb.db.queryengine.plan.expression.unary.LikeExpression.getEscapeCharacter;
 import static org.apache.iotdb.db.queryengine.plan.optimization.LimitOffsetPushDown.canPushDownLimitOffsetToGroupByTime;
 import static org.apache.iotdb.db.queryengine.plan.optimization.LimitOffsetPushDown.pushDownLimitOffsetToTimeParameter;
 import static org.apache.iotdb.db.utils.TimestampPrecisionUtils.TIMESTAMP_PRECISION;
@@ -3135,15 +3137,18 @@ public class ASTVisitor extends IoTDBSqlParserBaseVisitor<Statement> {
   private Expression parseRegularExpression(ExpressionContext context, boolean canUseFullPath) {
     return new RegularExpression(
         parseExpression(context.unaryBeforeRegularOrLikeExpression, canUseFullPath),
-        parseStringLiteral(context.STRING_LITERAL().getText()),
-        false);
+        parseStringLiteral(String.valueOf(context.pattern.getText())),
+        context.operator_not() != null);
   }
 
   private Expression parseLikeExpression(ExpressionContext context, boolean canUseFullPath) {
     return new LikeExpression(
         parseExpression(context.unaryBeforeRegularOrLikeExpression, canUseFullPath),
-        parseStringLiteral(context.STRING_LITERAL().getText()),
-        false);
+        parseStringLiteral(String.valueOf(context.pattern.getText())),
+        context.ESCAPE() == null
+            ? Optional.empty()
+            : getEscapeCharacter(parseStringLiteral(String.valueOf(context.escapeSet.getText()))),
+        context.operator_not() != null);
   }
 
   private Expression parseIsNullExpression(ExpressionContext context, boolean canUseFullPath) {
diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/predicate/ConvertPredicateToFilterVisitor.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/predicate/ConvertPredicateToFilterVisitor.java
index 7d046af30ec..a4ccc8885e6 100644
--- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/predicate/ConvertPredicateToFilterVisitor.java
+++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/predicate/ConvertPredicateToFilterVisitor.java
@@ -64,11 +64,11 @@ import java.util.Set;
 import java.util.stream.Collectors;
 
 import static com.google.common.base.Preconditions.checkArgument;
+import static org.apache.iotdb.db.queryengine.plan.expression.unary.LikeExpression.getEscapeCharacter;
 import static org.apache.iotdb.db.queryengine.plan.relational.analyzer.predicate.ConvertPredicateToTimeFilterVisitor.getLongValue;
 import static org.apache.iotdb.db.queryengine.plan.relational.analyzer.predicate.ConvertPredicateToTimeFilterVisitor.isTimeColumn;
 import static org.apache.iotdb.db.queryengine.plan.relational.analyzer.predicate.PredicatePushIntoScanChecker.isLiteral;
 import static org.apache.iotdb.db.queryengine.plan.relational.analyzer.predicate.PredicatePushIntoScanChecker.isSymbolReference;
-import static org.apache.tsfile.common.regexp.LikePattern.getEscapeCharacter;
 
 public class ConvertPredicateToFilterVisitor
     extends PredicateVisitor<Filter, ConvertPredicateToFilterVisitor.Context> {
@@ -211,16 +211,15 @@ public class ConvertPredicateToFilterVisitor
     SymbolReference operand = (SymbolReference) node.getValue();
     checkArgument(context.isMeasurementColumn(operand));
     int measurementIndex = context.getMeasurementIndex(operand.getName());
-    Optional<String> escapeValueOpt =
+    Optional<Character> escapeSet =
         node.getEscape().isPresent()
-            ? Optional.ofNullable(((StringLiteral) node.getEscape().get()).getValue())
+            ? getEscapeCharacter(((StringLiteral) node.getEscape().get()).getValue())
             : Optional.empty();
     Type type = context.getType(Symbol.from(operand));
     TSDataType dataType = InternalTypeManager.getTSDataType(type);
     return ValueFilterApi.like(
         measurementIndex,
-        LikePattern.compile(
-            ((StringLiteral) node.getPattern()).getValue(), getEscapeCharacter(escapeValueOpt)),
+        LikePattern.compile(((StringLiteral) node.getPattern()).getValue(), escapeSet),
         dataType);
   }
 
diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/predicate/schema/ConvertSchemaPredicateToFilterVisitor.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/predicate/schema/ConvertSchemaPredicateToFilterVisitor.java
index afc0fb769f8..816a546b390 100644
--- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/predicate/schema/ConvertSchemaPredicateToFilterVisitor.java
+++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/predicate/schema/ConvertSchemaPredicateToFilterVisitor.java
@@ -62,8 +62,8 @@ import java.util.Optional;
 import java.util.stream.Collectors;
 
 import static com.google.common.base.Preconditions.checkArgument;
+import static org.apache.iotdb.db.queryengine.plan.expression.unary.LikeExpression.getEscapeCharacter;
 import static org.apache.iotdb.db.queryengine.plan.relational.analyzer.predicate.PredicatePushIntoScanChecker.isSymbolReference;
-import static org.apache.tsfile.common.regexp.LikePattern.getEscapeCharacter;
 
 /**
  * The {@link ConvertSchemaPredicateToFilterVisitor} will convert a predicate to {@link
@@ -120,8 +120,7 @@ public class ConvertSchemaPredicateToFilterVisitor
         new LikeFilter(
             (((StringLiteral) node.getPattern()).getValue()),
             node.getEscape().isPresent()
-                ? getEscapeCharacter(
-                    Optional.ofNullable(((StringLiteral) node.getEscape().get()).getValue()))
+                ? getEscapeCharacter(((StringLiteral) node.getEscape().get()).getValue())
                 : Optional.empty()),
         ((SymbolReference) node.getValue()).getName(),
         context);
diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/transformation/dag/column/binary/Like2ColumnTransformer.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/transformation/dag/column/binary/Like2ColumnTransformer.java
new file mode 100644
index 00000000000..6f8bd9764db
--- /dev/null
+++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/transformation/dag/column/binary/Like2ColumnTransformer.java
@@ -0,0 +1,92 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.iotdb.db.queryengine.transformation.dag.column.binary;
+
+import org.apache.iotdb.db.queryengine.transformation.dag.column.ColumnTransformer;
+
+import org.apache.tsfile.block.column.Column;
+import org.apache.tsfile.block.column.ColumnBuilder;
+import org.apache.tsfile.common.conf.TSFileConfig;
+import org.apache.tsfile.common.regexp.LikePattern;
+import org.apache.tsfile.read.common.type.Type;
+
+import java.util.Optional;
+
+import static org.apache.iotdb.db.queryengine.plan.relational.metadata.TableMetadataImpl.isCharType;
+
+public class Like2ColumnTransformer extends BinaryColumnTransformer {
+
+  public Like2ColumnTransformer(
+      Type returnType, ColumnTransformer leftTransformer, ColumnTransformer rightTransformer) {
+    super(returnType, leftTransformer, rightTransformer);
+  }
+
+  @Override
+  protected void checkType() {
+    if (!isCharType(leftTransformer.getType())) {
+      throw new UnsupportedOperationException(
+          "Unsupported Type: " + leftTransformer.getType().getTypeEnum());
+    }
+    if (!isCharType(rightTransformer.getType())) {
+      throw new UnsupportedOperationException(
+          "Unsupported Type: " + rightTransformer.getType().getTypeEnum());
+    }
+  }
+
+  @Override
+  protected void doTransform(
+      Column leftColumn, Column rightColumn, ColumnBuilder builder, int positionCount) {
+    for (int i = 0; i < positionCount; i++) {
+      transforme(leftColumn, rightColumn, builder, i);
+    }
+  }
+
+  @Override
+  protected void doTransform(
+      Column leftColumn,
+      Column rightColumn,
+      ColumnBuilder builder,
+      int positionCount,
+      boolean[] selection) {
+    for (int i = 0; i < positionCount; i++) {
+      if (selection[i]) {
+        transforme(leftColumn, rightColumn, builder, i);
+      } else {
+        builder.appendNull();
+      }
+    }
+  }
+
+  private void transforme(Column leftColumn, Column rightColumn, ColumnBuilder builder, int i) {
+    if (!leftColumn.isNull(i) && !rightColumn.isNull(i)) {
+      LikePattern pattern =
+          LikePattern.compile(
+              rightColumn.getBinary(i).getStringValue(TSFileConfig.STRING_CHARSET),
+              Optional.empty(),
+              false);
+      builder.writeBoolean(
+          pattern
+              .getMatcher()
+              .match(leftColumn.getBinary(i).getValues(), 0, leftColumn.getBinary(i).getLength()));
+    } else {
+      builder.appendNull();
+    }
+  }
+}
diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/transformation/dag/column/ternary/Like3ColumnTransformer.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/transformation/dag/column/ternary/Like3ColumnTransformer.java
new file mode 100644
index 00000000000..34000e227c6
--- /dev/null
+++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/transformation/dag/column/ternary/Like3ColumnTransformer.java
@@ -0,0 +1,105 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.iotdb.db.queryengine.transformation.dag.column.ternary;
+
+import org.apache.iotdb.db.queryengine.transformation.dag.column.ColumnTransformer;
+
+import org.apache.tsfile.block.column.Column;
+import org.apache.tsfile.block.column.ColumnBuilder;
+import org.apache.tsfile.common.conf.TSFileConfig;
+import org.apache.tsfile.common.regexp.LikePattern;
+import org.apache.tsfile.read.common.type.Type;
+
+import static org.apache.iotdb.db.queryengine.plan.expression.unary.LikeExpression.getEscapeCharacter;
+import static org.apache.iotdb.db.queryengine.plan.relational.metadata.TableMetadataImpl.isCharType;
+
+public class Like3ColumnTransformer extends TernaryColumnTransformer {
+  public Like3ColumnTransformer(
+      Type retuenType,
+      ColumnTransformer firstColumnTransformer,
+      ColumnTransformer secondColumnTransformer,
+      ColumnTransformer thirdColumnTransformer) {
+    super(retuenType, firstColumnTransformer, secondColumnTransformer, thirdColumnTransformer);
+  }
+
+  @Override
+  protected void checkType() {
+    if (!isCharType(firstColumnTransformer.getType())) {
+      throw new UnsupportedOperationException(
+          "Unsupported Type: " + firstColumnTransformer.getType().getTypeEnum());
+    }
+    if (!isCharType(secondColumnTransformer.getType())) {
+      throw new UnsupportedOperationException(
+          "Unsupported Type: " + secondColumnTransformer.getType().getTypeEnum());
+    }
+    if (!isCharType(thirdColumnTransformer.getType())) {
+      throw new UnsupportedOperationException(
+          "Unsupported Type: " + thirdColumnTransformer.getType().getTypeEnum());
+    }
+  }
+
+  @Override
+  protected void doTransform(
+      Column firstColumn,
+      Column secondColumn,
+      Column thirdColumn,
+      ColumnBuilder builder,
+      int positionCount) {
+    for (int i = 0; i < positionCount; i++) {
+      transform(firstColumn, secondColumn, thirdColumn, builder, i);
+    }
+  }
+
+  @Override
+  protected void doTransform(
+      Column firstColumn,
+      Column secondColumn,
+      Column thirdColumn,
+      ColumnBuilder builder,
+      int positionCount,
+      boolean[] selection) {
+    for (int i = 0; i < positionCount; i++) {
+      if (selection[i]) {
+        transform(firstColumn, secondColumn, thirdColumn, builder, i);
+      } else {
+        builder.appendNull();
+      }
+    }
+  }
+
+  private void transform(
+      Column firstColumn, Column secondColumn, Column thirdColumn, ColumnBuilder builder, int i) {
+    if (!firstColumn.isNull(i) && !secondColumn.isNull(i) && !thirdColumn.isNull(i)) {
+      LikePattern pattern =
+          LikePattern.compile(
+              secondColumn.getBinary(i).getStringValue(TSFileConfig.STRING_CHARSET),
+              getEscapeCharacter(
+                  thirdColumn.getBinary(i).getStringValue(TSFileConfig.STRING_CHARSET)),
+              false);
+      builder.writeBoolean(
+          pattern
+              .getMatcher()
+              .match(
+                  firstColumn.getBinary(i).getValues(), 0, firstColumn.getBinary(i).getLength()));
+    } else {
+      builder.appendNull();
+    }
+  }
+}
diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/transformation/dag/transformer/unary/LikeTransformer.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/transformation/dag/transformer/unary/LikeTransformer.java
index 61d896847cf..80690826d74 100644
--- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/transformation/dag/transformer/unary/LikeTransformer.java
+++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/transformation/dag/transformer/unary/LikeTransformer.java
@@ -37,7 +37,6 @@ public class LikeTransformer extends UnaryTransformer {
   public LikeTransformer(LayerReader layerReader, LikePattern pattern) {
     super(layerReader);
     this.pattern = pattern;
-
     if (layerReaderDataType != TSDataType.TEXT) {
       throw new UnSupportedDataTypeException("Unsupported data type: " + layerReaderDataType);
     }
diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/schemaengine/schemaregion/view/visitor/TransformToExpressionVisitor.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/schemaengine/schemaregion/view/visitor/TransformToExpressionVisitor.java
index 7ad1de7a5cb..8bedc236c27 100644
--- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/schemaengine/schemaregion/view/visitor/TransformToExpressionVisitor.java
+++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/schemaengine/schemaregion/view/visitor/TransformToExpressionVisitor.java
@@ -152,10 +152,7 @@ public class TransformToExpressionVisitor extends ViewExpressionVisitor<Expressi
   public Expression visitLikeExpression(LikeViewExpression likeExpression, Void context) {
     Expression child = this.process(likeExpression.getExpression(), context);
     return new org.apache.iotdb.db.queryengine.plan.expression.unary.LikeExpression(
-        child,
-        likeExpression.getPatternString(),
-        likeExpression.getPattern(),
-        likeExpression.isNot());
+        child, likeExpression.getPattern(), likeExpression.getEscape(), likeExpression.isNot());
   }
 
   @Override
diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/schema/filter/impl/values/LikeFilter.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/schema/filter/impl/values/LikeFilter.java
index 168e0b7c88a..b810c91fd80 100644
--- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/schema/filter/impl/values/LikeFilter.java
+++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/schema/filter/impl/values/LikeFilter.java
@@ -32,9 +32,6 @@ import java.nio.ByteBuffer;
 import java.util.Objects;
 import java.util.Optional;
 
-import static org.apache.tsfile.common.regexp.LikePattern.getEscapeCharacter;
-
-// Does not support escape now
 public class LikeFilter extends SchemaFilter {
   private final LikePattern pattern;
 
@@ -47,7 +44,7 @@ public class LikeFilter extends SchemaFilter {
         LikePattern.compile(
             ReadWriteIOUtils.readString(byteBuffer),
             ReadWriteIOUtils.readBool(byteBuffer)
-                ? getEscapeCharacter(Optional.of(ReadWriteIOUtils.readString(byteBuffer)))
+                ? Optional.of(ReadWriteIOUtils.readString(byteBuffer).charAt(0))
                 : Optional.empty());
   }
 
diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/schema/view/viewExpression/unary/LikeViewExpression.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/schema/view/viewExpression/unary/LikeViewExpression.java
index 063116db0b8..e52f436f23e 100644
--- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/schema/view/viewExpression/unary/LikeViewExpression.java
+++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/schema/view/viewExpression/unary/LikeViewExpression.java
@@ -23,7 +23,6 @@ import org.apache.iotdb.commons.schema.view.viewExpression.ViewExpression;
 import org.apache.iotdb.commons.schema.view.viewExpression.ViewExpressionType;
 import org.apache.iotdb.commons.schema.view.viewExpression.visitor.ViewExpressionVisitor;
 
-import org.apache.tsfile.common.regexp.LikePattern;
 import org.apache.tsfile.utils.ReadWriteIOUtils;
 
 import java.io.IOException;
@@ -32,44 +31,49 @@ import java.io.OutputStream;
 import java.nio.ByteBuffer;
 import java.util.Optional;
 
-import static org.apache.tsfile.common.regexp.LikePattern.getEscapeCharacter;
-
 public class LikeViewExpression extends UnaryViewExpression {
 
   // region member variables and init functions
-  private final String patternString;
-  private final LikePattern pattern;
-
+  private final String pattern;
+  private final Optional<Character> escape;
   private final boolean isNot;
 
-  public LikeViewExpression(ViewExpression expression, String patternString, boolean isNot) {
+  public LikeViewExpression(
+      ViewExpression expression, String pattern, Optional<Character> escape, boolean isNot) {
     super(expression);
-    this.patternString = patternString;
+    this.pattern = pattern;
+    this.escape = escape;
     this.isNot = isNot;
-    pattern = LikePattern.compile(patternString, getEscapeCharacter(Optional.of("\\")));
   }
 
-  public LikeViewExpression(
-      ViewExpression expression, String patternString, LikePattern pattern, boolean isNot) {
+  public LikeViewExpression(ViewExpression expression, String pattern, boolean isNot) {
     super(expression);
-    this.patternString = patternString;
     this.pattern = pattern;
+    this.escape = Optional.empty();
     this.isNot = isNot;
   }
 
   public LikeViewExpression(ByteBuffer byteBuffer) {
     super(ViewExpression.deserialize(byteBuffer));
-    patternString = ReadWriteIOUtils.readString(byteBuffer);
+    pattern = ReadWriteIOUtils.readString(byteBuffer);
+    if (ReadWriteIOUtils.readBool(byteBuffer)) {
+      escape = Optional.of(ReadWriteIOUtils.readString(byteBuffer).charAt(0));
+    } else {
+      escape = Optional.empty();
+    }
     isNot = ReadWriteIOUtils.readBool(byteBuffer);
-    pattern = LikePattern.compile(patternString, getEscapeCharacter(Optional.of("\\")));
   }
 
   public LikeViewExpression(InputStream inputStream) {
     super(ViewExpression.deserialize(inputStream));
     try {
-      patternString = ReadWriteIOUtils.readString(inputStream);
+      pattern = ReadWriteIOUtils.readString(inputStream);
+      if (ReadWriteIOUtils.readBool(inputStream)) {
+        escape = Optional.of(ReadWriteIOUtils.readString(inputStream).charAt(0));
+      } else {
+        escape = Optional.empty();
+      }
       isNot = ReadWriteIOUtils.readBool(inputStream);
-      pattern = LikePattern.compile(patternString, getEscapeCharacter(Optional.of("\\")));
     } catch (IOException e) {
       throw new RuntimeException(e);
     }
@@ -91,7 +95,14 @@ public class LikeViewExpression extends UnaryViewExpression {
   @Override
   public String toString(boolean isRoot) {
     String basicString =
-        this.expression.toString(false) + (isNot ? "NOT LIKE " : "LIKE ") + this.patternString;
+        this.expression.toString(false)
+            + (isNot ? "NOT LIKE " : "LIKE ")
+            + "pattern = '"
+            + this.pattern
+            + "'";
+    if (escape.isPresent()) {
+      basicString += " escape = '" + escape.get() + "'";
+    }
     if (isRoot) {
       return basicString;
     }
@@ -101,25 +112,33 @@ public class LikeViewExpression extends UnaryViewExpression {
   @Override
   protected void serialize(ByteBuffer byteBuffer) {
     super.serialize(byteBuffer);
-    ReadWriteIOUtils.write(patternString, byteBuffer);
+    ReadWriteIOUtils.write(pattern, byteBuffer);
+    ReadWriteIOUtils.write(escape.isPresent(), byteBuffer);
+    if (escape.isPresent()) {
+      ReadWriteIOUtils.write(escape.get().toString(), byteBuffer);
+    }
     ReadWriteIOUtils.write(isNot, byteBuffer);
   }
 
   @Override
   protected void serialize(OutputStream stream) throws IOException {
     super.serialize(stream);
-    ReadWriteIOUtils.write(patternString, stream);
+    ReadWriteIOUtils.write(pattern, stream);
+    ReadWriteIOUtils.write(escape.isPresent(), stream);
+    if (escape.isPresent()) {
+      ReadWriteIOUtils.write(escape.get().toString(), stream);
+    }
     ReadWriteIOUtils.write(isNot, stream);
   }
 
   // endregion
 
-  public String getPatternString() {
-    return patternString;
+  public String getPattern() {
+    return pattern;
   }
 
-  public LikePattern getPattern() {
-    return pattern;
+  public Optional<Character> getEscape() {
+    return escape;
   }
 
   public boolean isNot() {
diff --git a/iotdb-core/relational-grammar/src/main/antlr4/org/apache/iotdb/db/relational/grammar/sql/RelationalSql.g4 b/iotdb-core/relational-grammar/src/main/antlr4/org/apache/iotdb/db/relational/grammar/sql/RelationalSql.g4
index 407361db0b4..1a3c4a5817f 100644
--- a/iotdb-core/relational-grammar/src/main/antlr4/org/apache/iotdb/db/relational/grammar/sql/RelationalSql.g4
+++ b/iotdb-core/relational-grammar/src/main/antlr4/org/apache/iotdb/db/relational/grammar/sql/RelationalSql.g4
@@ -167,7 +167,7 @@ dropTableStatement
 
 showTableStatement
     : SHOW TABLES ((FROM | IN) database=identifier)?
-          // ((LIKE pattern=string) | (WHERE expression))?
+          // ((LIKE pattern=string (ESCAPE escape=string)) | (WHERE expression))?
     ;
 
 descTableStatement
@@ -370,7 +370,7 @@ showClusterStatement
 
 showRegionsStatement
     : SHOW (SCHEMA | DATA)? REGIONS ((FROM | IN) identifier)?
-          // ((LIKE pattern=string) | (WHERE expression))?
+          // ((LIKE pattern=string (ESCAPE escape=string)) | (WHERE expression))?
     ;
 
 showDataNodesStatement
@@ -650,7 +650,6 @@ relationPrimary
     | '(' relation ')'                                                #parenthesizedRelation
     ;
 
-
 expression
     : booleanExpression
     ;