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

jt2594838 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 7bfd4400c69 Support attribute filters in table deletion (#18079)
7bfd4400c69 is described below

commit 7bfd4400c698c307dd9e4e0a819d74f0efc0caaa
Author: Jiang Tian <[email protected]>
AuthorDate: Mon Jul 6 18:21:07 2026 +0800

    Support attribute filters in table deletion (#18079)
    
    * temp1
    
    * rename class
    
    * add DEVICE_IN filter
    
    * convert attribute filter to DEVICE_IN
    
    * add test
    
    * add test
    
    * add test
    
    * add test
    
    * Fix table deletion predicate review issues
    
    * support LIKE
    
    * support IN
    
    * refactor predicate parsing
    
    * i18n
    
    * add tests
    
    * fix compilation
    
    * fix compilation
---
 .../relational/it/db/it/IoTDBDeletionTableIT.java  | 565 ++++++++++++++++++++-
 .../iotdb/db/i18n/DataNodeQueryMessages.java       |  29 +-
 .../iotdb/db/i18n/StorageEngineMessages.java       |   2 +
 .../iotdb/db/i18n/DataNodeQueryMessages.java       |  28 +-
 .../iotdb/db/i18n/StorageEngineMessages.java       |   2 +
 .../DirectBufferMemoryAllocationException.java     |  35 ++
 .../impl/DataNodeInternalRPCServiceImpl.java       |   4 +-
 .../db/queryengine/plan/analyze/AnalyzeUtils.java  | 436 ++++++++++++++--
 .../relational/analyzer/StatementAnalyzer.java     |   2 +-
 .../plan/relational/sql/ast/DeleteDevice.java      |   5 +-
 .../iotdb/db/storageengine/StorageEngine.java      |  18 +-
 .../db/storageengine/dataregion/DataRegion.java    |  26 +-
 .../compaction/execute/utils/CompactionUtils.java  |   2 +-
 .../dataregion/modification/DeletionPredicate.java |  46 +-
 .../modification/TableDeletionEntry.java           |   6 +-
 .../{IDPredicate.java => TagPredicate.java}        | 261 ++++++++--
 .../file/UnsealedTsFileRecoverPerformer.java       |   2 +-
 .../iotdb/db/metadata/path/PatternTreeMapTest.java |   2 +-
 .../db/pipe/consensus/DeletionRecoverTest.java     |   2 +-
 .../db/pipe/consensus/DeletionResourceTest.java    |   2 +-
 .../PipePlanTablePatternParseVisitorTest.java      |   8 +-
 .../queryengine/plan/analyze/AnalyzeUtilsTest.java |   6 +-
 .../node/write/RelationalDeleteDataNodeTest.java   |   8 +-
 .../iotdb/db/storageengine/StorageEngineTest.java  |  45 ++
 .../storageengine/dataregion/DataRegionTest.java   |   2 +-
 .../tablemodel/CompactionWithAllNullRowsTest.java  |  12 +-
 .../modification/ModificationFileTest.java         |  27 +-
 .../modification/TableDeletionEntryTest.java       |  48 +-
 28 files changed, 1427 insertions(+), 204 deletions(-)

diff --git 
a/integration-test/src/test/java/org/apache/iotdb/relational/it/db/it/IoTDBDeletionTableIT.java
 
b/integration-test/src/test/java/org/apache/iotdb/relational/it/db/it/IoTDBDeletionTableIT.java
index 66d0e3a6a0d..3ac335df1ad 100644
--- 
a/integration-test/src/test/java/org/apache/iotdb/relational/it/db/it/IoTDBDeletionTableIT.java
+++ 
b/integration-test/src/test/java/org/apache/iotdb/relational/it/db/it/IoTDBDeletionTableIT.java
@@ -248,14 +248,6 @@ public class IoTDBDeletionTableIT {
         assertEquals("701: The column 's1' does not exist or is not a tag 
column", e.getMessage());
       }
 
-      try {
-        statement.execute("DELETE FROM vehicle1  WHERE attr1 = 'text'");
-        fail("should not reach here!");
-      } catch (SQLException e) {
-        assertEquals(
-            "701: The column 'attr1' does not exist or is not a tag column", 
e.getMessage());
-      }
-
       try {
         statement.execute("DELETE FROM vehicle1  WHERE s3 = 'text'");
         fail("should not reach here!");
@@ -277,14 +269,7 @@ public class IoTDBDeletionTableIT {
         assertEquals("701: The operator of tag predicate must be '=' for 
'd0'", e.getMessage());
       }
 
-      try {
-        statement.execute("DELETE FROM vehicle1  WHERE time < 10 and deviceId 
is not null");
-        fail("should not reach here!");
-      } catch (SQLException e) {
-        assertEquals(
-            "701: Unsupported expression: (deviceId IS NOT NULL) in ((time < 
10) AND (deviceId IS NOT NULL))",
-            e.getMessage());
-      }
+      statement.execute("DELETE FROM vehicle1  WHERE time < 10 and deviceId is 
not null");
 
       try {
         statement.execute("DELETE FROM vehicle1  WHERE time < 10 and deviceId 
= null");
@@ -335,6 +320,554 @@ public class IoTDBDeletionTableIT {
     }
   }
 
+  @Test
+  public void testDeleteDataByAttributeFilter() throws SQLException {
+    try (Connection connection = 
EnvFactory.getEnv().getConnection(BaseEnv.TABLE_SQL_DIALECT);
+        Statement statement = connection.createStatement()) {
+      statement.execute("use test");
+      statement.execute(
+          "CREATE TABLE delete_by_attr(deviceId STRING TAG, attr1 ATTRIBUTE, 
attr2 ATTRIBUTE, s1 INT32 FIELD)");
+      statement.execute(
+          "INSERT INTO delete_by_attr(time, deviceId, attr1, attr2, s1) VALUES 
"
+              + "(1, 'd1', 'red', 'small', 1),"
+              + "(2, 'd1', 'red', 'small', 2),"
+              + "(1, 'd2', 'red', 'large', 3),"
+              + "(2, 'd2', 'red', 'large', 4),"
+              + "(1, 'd3', 'blue', 'small', 5),"
+              + "(2, 'd3', 'blue', 'small', 6)");
+
+      statement.execute("DELETE FROM delete_by_attr WHERE attr1 = 'red' AND 
attr2 = 'small'");
+
+      try (ResultSet resultSet =
+          statement.executeQuery("SELECT deviceId, s1 FROM delete_by_attr 
ORDER BY deviceId, s1")) {
+        assertTrue(resultSet.next());
+        assertEquals("d2", resultSet.getString(1));
+        assertEquals(3, resultSet.getInt(2));
+        assertTrue(resultSet.next());
+        assertEquals("d2", resultSet.getString(1));
+        assertEquals(4, resultSet.getInt(2));
+        assertTrue(resultSet.next());
+        assertEquals("d3", resultSet.getString(1));
+        assertEquals(5, resultSet.getInt(2));
+        assertTrue(resultSet.next());
+        assertEquals("d3", resultSet.getString(1));
+        assertEquals(6, resultSet.getInt(2));
+        assertFalse(resultSet.next());
+      }
+    }
+  }
+
+  @Test
+  public void testDeleteDataByAttributeFilterWithTagAndTimeRange() throws 
SQLException {
+    try (Connection connection = 
EnvFactory.getEnv().getConnection(BaseEnv.TABLE_SQL_DIALECT);
+        Statement statement = connection.createStatement()) {
+      statement.execute("use test");
+      statement.execute(
+          "CREATE TABLE delete_by_attr_tag_time("
+              + "deviceId STRING TAG, site STRING TAG, attr1 ATTRIBUTE, attr2 
ATTRIBUTE, "
+              + "s1 INT32 FIELD)");
+      statement.execute(
+          "INSERT INTO delete_by_attr_tag_time(time, deviceId, site, attr1, 
attr2, s1) VALUES "
+              + "(1, 'd1', 'north', 'red', 'small', 11),"
+              + "(2, 'd1', 'north', 'red', 'small', 12),"
+              + "(3, 'd1', 'north', 'red', 'small', 13),"
+              + "(4, 'd1', 'north', 'red', 'small', 14),"
+              + "(1, 'd2', 'south', 'red', 'small', 21),"
+              + "(2, 'd2', 'south', 'red', 'small', 22),"
+              + "(2, 'd3', 'north', 'blue', 'small', 32),"
+              + "(3, 'd4', 'north', 'red', 'large', 43)");
+
+      statement.execute(
+          "DELETE FROM delete_by_attr_tag_time "
+              + "WHERE time >= 2 AND time <= 3 "
+              + "AND site = 'north' AND attr1 = 'red' AND attr2 = 'small'");
+
+      final List<String> actual = new ArrayList<>();
+      try (ResultSet resultSet =
+          statement.executeQuery(
+              "SELECT deviceId, time, s1 FROM delete_by_attr_tag_time ORDER BY 
deviceId, time")) {
+        while (resultSet.next()) {
+          actual.add(
+              resultSet.getString(1) + "," + resultSet.getLong(2) + "," + 
resultSet.getInt(3));
+        }
+      }
+      assertEquals(
+          List.of("d1,1,11", "d1,4,14", "d2,1,21", "d2,2,22", "d3,2,32", 
"d4,3,43"), actual);
+    }
+  }
+
+  @Test
+  public void testDeleteDataByAttributeFilterWithOrAndNull() throws 
SQLException {
+    try (Connection connection = 
EnvFactory.getEnv().getConnection(BaseEnv.TABLE_SQL_DIALECT);
+        Statement statement = connection.createStatement()) {
+      statement.execute("use test");
+      statement.execute(
+          "CREATE TABLE delete_by_attr_or_null("
+              + "deviceId STRING TAG, site STRING TAG, attr1 ATTRIBUTE, attr2 
ATTRIBUTE, "
+              + "s1 INT32 FIELD)");
+      statement.execute(
+          "INSERT INTO delete_by_attr_or_null(time, deviceId, site, attr1, 
attr2, s1) VALUES "
+              + "(1, 'd1', 'north', 'red', 'small', 11),"
+              + "(2, 'd1', 'north', 'red', 'small', 12),"
+              + "(1, 'd2', 'north', 'blue', 'small', 21),"
+              + "(2, 'd2', 'north', 'blue', 'small', 22),"
+              + "(1, 'd3', 'south', 'red', 'large', 31),"
+              + "(2, 'd3', 'south', 'red', 'large', 32)");
+      statement.execute(
+          "INSERT INTO delete_by_attr_or_null(time, deviceId, site, attr1, s1) 
VALUES "
+              + "(1, 'd4', 'north', 'red', 41),"
+              + "(2, 'd4', 'north', 'red', 42)");
+
+      statement.execute(
+          "DELETE FROM delete_by_attr_or_null "
+              + "WHERE (attr1 = 'red' AND site = 'north' AND time = 1) "
+              + "OR (attr2 IS NULL AND time = 2)");
+
+      final List<String> actual = new ArrayList<>();
+      try (ResultSet resultSet =
+          statement.executeQuery(
+              "SELECT deviceId, time, s1 FROM delete_by_attr_or_null ORDER BY 
deviceId, time")) {
+        while (resultSet.next()) {
+          actual.add(
+              resultSet.getString(1) + "," + resultSet.getLong(2) + "," + 
resultSet.getInt(3));
+        }
+      }
+      assertEquals(List.of("d1,2,12", "d2,1,21", "d2,2,22", "d3,1,31", 
"d3,2,32"), actual);
+    }
+  }
+
+  @Test
+  public void testDeleteDataByAttributeFilterWithOtherOperators() throws 
SQLException {
+    try (Connection connection = 
EnvFactory.getEnv().getConnection(BaseEnv.TABLE_SQL_DIALECT);
+        Statement statement = connection.createStatement()) {
+      statement.execute("use test");
+      statement.execute(
+          "CREATE TABLE delete_by_attr_other_ops("
+              + "deviceId STRING TAG, attr1 ATTRIBUTE, attr2 ATTRIBUTE, s1 
INT32 FIELD)");
+      statement.execute(
+          "INSERT INTO delete_by_attr_other_ops(time, deviceId, attr1, attr2, 
s1) VALUES "
+              + "(1, 'd1', 'a', 'x', 11),"
+              + "(2, 'd1', 'a', 'x', 12),"
+              + "(1, 'd2', 'b', 'y', 21),"
+              + "(2, 'd2', 'b', 'y', 22),"
+              + "(1, 'd3', 'c', 'z', 31),"
+              + "(2, 'd3', 'c', 'z', 32)");
+      statement.execute(
+          "INSERT INTO delete_by_attr_other_ops(time, deviceId, attr2, s1) 
VALUES "
+              + "(1, 'd4', 'null_attr1', 41),"
+              + "(2, 'd4', 'null_attr1', 42)");
+
+      statement.execute("DELETE FROM delete_by_attr_other_ops WHERE attr1 > 
'b' AND time = 1");
+
+      List<String> actual = new ArrayList<>();
+      try (ResultSet resultSet =
+          statement.executeQuery(
+              "SELECT deviceId, time, s1 FROM delete_by_attr_other_ops ORDER 
BY deviceId, time")) {
+        while (resultSet.next()) {
+          actual.add(
+              resultSet.getString(1) + "," + resultSet.getLong(2) + "," + 
resultSet.getInt(3));
+        }
+      }
+      assertEquals(
+          List.of("d1,1,11", "d1,2,12", "d2,1,21", "d2,2,22", "d3,2,32", 
"d4,1,41", "d4,2,42"),
+          actual);
+
+      statement.execute("DELETE FROM delete_by_attr_other_ops WHERE attr1 != 
'a' AND time = 2");
+
+      actual = new ArrayList<>();
+      try (ResultSet resultSet =
+          statement.executeQuery(
+              "SELECT deviceId, time, s1 FROM delete_by_attr_other_ops ORDER 
BY deviceId, time")) {
+        while (resultSet.next()) {
+          actual.add(
+              resultSet.getString(1) + "," + resultSet.getLong(2) + "," + 
resultSet.getInt(3));
+        }
+      }
+      assertEquals(List.of("d1,1,11", "d1,2,12", "d2,1,21", "d4,1,41", 
"d4,2,42"), actual);
+
+      statement.execute("DELETE FROM delete_by_attr_other_ops WHERE attr1 <= 
'a' AND time = 1");
+
+      actual = new ArrayList<>();
+      try (ResultSet resultSet =
+          statement.executeQuery(
+              "SELECT deviceId, time, s1 FROM delete_by_attr_other_ops ORDER 
BY deviceId, time")) {
+        while (resultSet.next()) {
+          actual.add(
+              resultSet.getString(1) + "," + resultSet.getLong(2) + "," + 
resultSet.getInt(3));
+        }
+      }
+      assertEquals(List.of("d1,2,12", "d2,1,21", "d4,1,41", "d4,2,42"), 
actual);
+
+      statement.execute("DELETE FROM delete_by_attr_other_ops WHERE attr1 IS 
NOT NULL");
+
+      actual = new ArrayList<>();
+      try (ResultSet resultSet =
+          statement.executeQuery(
+              "SELECT deviceId, time, s1 FROM delete_by_attr_other_ops ORDER 
BY deviceId, time")) {
+        while (resultSet.next()) {
+          actual.add(
+              resultSet.getString(1) + "," + resultSet.getLong(2) + "," + 
resultSet.getInt(3));
+        }
+      }
+      assertEquals(List.of("d4,1,41", "d4,2,42"), actual);
+    }
+  }
+
+  @Test
+  public void testDeleteDataByAttributeFilterWithLike() throws SQLException {
+    try (Connection connection = 
EnvFactory.getEnv().getConnection(BaseEnv.TABLE_SQL_DIALECT);
+        Statement statement = connection.createStatement()) {
+      statement.execute("use test");
+      statement.execute(
+          "CREATE TABLE delete_by_attr_like("
+              + "deviceId STRING TAG, attr1 ATTRIBUTE, attr2 ATTRIBUTE, s1 
INT32 FIELD)");
+      statement.execute(
+          "INSERT INTO delete_by_attr_like(time, deviceId, attr1, attr2, s1) 
VALUES "
+              + "(1, 'd1', 'red-small', 'a', 11),"
+              + "(2, 'd1', 'red-small', 'a', 12),"
+              + "(1, 'd2', 'red-large', 'b', 21),"
+              + "(2, 'd2', 'red-large', 'b', 22),"
+              + "(1, 'd3', 'blue-small', 'c', 31),"
+              + "(2, 'd3', 'blue-small', 'c', 32)");
+
+      statement.execute("DELETE FROM delete_by_attr_like WHERE attr1 LIKE 
'red%' AND time = 1");
+
+      final List<String> actual = new ArrayList<>();
+      try (ResultSet resultSet =
+          statement.executeQuery(
+              "SELECT deviceId, time, s1 FROM delete_by_attr_like ORDER BY 
deviceId, time")) {
+        while (resultSet.next()) {
+          actual.add(
+              resultSet.getString(1) + "," + resultSet.getLong(2) + "," + 
resultSet.getInt(3));
+        }
+      }
+      assertEquals(List.of("d1,2,12", "d2,2,22", "d3,1,31", "d3,2,32"), 
actual);
+    }
+  }
+
+  @Test
+  public void testDeleteDataByAttributeFilterWithIn() throws SQLException {
+    try (Connection connection = 
EnvFactory.getEnv().getConnection(BaseEnv.TABLE_SQL_DIALECT);
+        Statement statement = connection.createStatement()) {
+      statement.execute("use test");
+      statement.execute(
+          "CREATE TABLE delete_by_attr_in("
+              + "deviceId STRING TAG, attr1 ATTRIBUTE, attr2 ATTRIBUTE, s1 
INT32 FIELD)");
+      statement.execute(
+          "INSERT INTO delete_by_attr_in(time, deviceId, attr1, attr2, s1) 
VALUES "
+              + "(1, 'd1', 'red', 'small', 11),"
+              + "(2, 'd1', 'red', 'small', 12),"
+              + "(1, 'd2', 'blue', 'large', 21),"
+              + "(2, 'd2', 'blue', 'large', 22),"
+              + "(1, 'd3', 'green', 'small', 31),"
+              + "(2, 'd3', 'green', 'small', 32)");
+
+      statement.execute(
+          "DELETE FROM delete_by_attr_in WHERE attr1 IN ('red', 'blue') AND 
time = 1");
+
+      final List<String> actual = new ArrayList<>();
+      try (ResultSet resultSet =
+          statement.executeQuery(
+              "SELECT deviceId, time, s1 FROM delete_by_attr_in ORDER BY 
deviceId, time")) {
+        while (resultSet.next()) {
+          actual.add(
+              resultSet.getString(1) + "," + resultSet.getLong(2) + "," + 
resultSet.getInt(3));
+        }
+      }
+      assertEquals(List.of("d1,2,12", "d2,2,22", "d3,1,31", "d3,2,32"), 
actual);
+    }
+  }
+
+  @Test
+  public void testDeleteDataByAttributeFilterWithMixedOperators() throws 
SQLException {
+    try (Connection connection = 
EnvFactory.getEnv().getConnection(BaseEnv.TABLE_SQL_DIALECT);
+        Statement statement = connection.createStatement()) {
+      statement.execute("use test");
+      statement.execute(
+          "CREATE TABLE delete_by_attr_mixed_ops("
+              + "deviceId STRING TAG, site STRING TAG, "
+              + "attr1 ATTRIBUTE, attr2 ATTRIBUTE, attr3 ATTRIBUTE, s1 INT32 
FIELD)");
+      statement.execute(
+          "INSERT INTO delete_by_attr_mixed_ops(time, deviceId, site, attr1, 
attr2, attr3, s1) "
+              + "VALUES "
+              + "(1, 'd1', 'north', 'red-a', 'small', 'enabled', 11),"
+              + "(2, 'd1', 'north', 'red-a', 'small', 'enabled', 12),"
+              + "(3, 'd1', 'north', 'red-a', 'small', 'enabled', 13),"
+              + "(4, 'd1', 'north', 'red-a', 'small', 'enabled', 14),"
+              + "(5, 'd1', 'north', 'red-a', 'small', 'enabled', 15),"
+              + "(2, 'd2', 'north', 'blue-a', 'small', 'enabled', 22),"
+              + "(3, 'd3', 'south', 'red-b', 'medium', 'enabled', 33),"
+              + "(3, 'd4', 'north', 'red-c', 'large', 'enabled', 43)");
+      statement.execute(
+          "INSERT INTO delete_by_attr_mixed_ops(time, deviceId, site, attr1, 
attr2, s1) "
+              + "VALUES (3, 'd5', 'north', 'red-d', 'medium', 53)");
+
+      statement.execute(
+          "DELETE FROM delete_by_attr_mixed_ops "
+              + "WHERE time >= 2 AND time <= 4 "
+              + "AND site = 'north' "
+              + "AND attr1 != 'blue-a' "
+              + "AND attr1 LIKE 'red%' "
+              + "AND attr2 IN ('small', 'medium') "
+              + "AND attr3 IS NOT NULL");
+
+      final List<String> actual = new ArrayList<>();
+      try (ResultSet resultSet =
+          statement.executeQuery(
+              "SELECT deviceId, site, time, s1 FROM delete_by_attr_mixed_ops "
+                  + "ORDER BY deviceId, time")) {
+        while (resultSet.next()) {
+          actual.add(
+              resultSet.getString(1)
+                  + ","
+                  + resultSet.getString(2)
+                  + ","
+                  + resultSet.getLong(3)
+                  + ","
+                  + resultSet.getInt(4));
+        }
+      }
+      assertEquals(
+          List.of(
+              "d1,north,1,11",
+              "d1,north,5,15",
+              "d2,north,2,22",
+              "d3,south,3,33",
+              "d4,north,3,43",
+              "d5,north,3,53"),
+          actual);
+    }
+  }
+
+  @Test
+  public void testDeleteDataByAttributeFilterWithoutTagColumns() throws 
SQLException {
+    try (Connection connection = 
EnvFactory.getEnv().getConnection(BaseEnv.TABLE_SQL_DIALECT);
+        Statement statement = connection.createStatement()) {
+      statement.execute("use test");
+      statement.execute(
+          "CREATE TABLE delete_by_attr_no_tag(attr1 ATTRIBUTE, attr2 
ATTRIBUTE, s1 INT32 FIELD)");
+      statement.execute(
+          "INSERT INTO delete_by_attr_no_tag(time, attr1, attr2, s1) VALUES "
+              + "(1, 'red', 'small', 11),"
+              + "(2, 'blue', 'large', 12),"
+              + "(3, 'blue', 'large', 13),"
+              + "(4, 'red', 'small', 14)");
+
+      statement.execute(
+          "DELETE FROM delete_by_attr_no_tag "
+              + "WHERE time >= 2 AND time <= 4 AND attr1 = 'red' AND attr2 = 
'small'");
+
+      final List<String> actual = new ArrayList<>();
+      try (ResultSet resultSet =
+          statement.executeQuery("SELECT time, s1 FROM delete_by_attr_no_tag 
ORDER BY time")) {
+        while (resultSet.next()) {
+          actual.add(resultSet.getLong(1) + "," + resultSet.getInt(2));
+        }
+      }
+      assertEquals(List.of("1,11"), actual);
+    }
+  }
+
+  @Test
+  public void testDeleteDataByAttributeFilterWithNullTagDevice() throws 
SQLException {
+    try (Connection connection = 
EnvFactory.getEnv().getConnection(BaseEnv.TABLE_SQL_DIALECT);
+        Statement statement = connection.createStatement()) {
+      statement.execute("use test");
+      statement.execute(
+          "CREATE TABLE delete_by_attr_null_tag("
+              + "deviceId STRING TAG, site STRING TAG, attr1 ATTRIBUTE, s1 
INT32 FIELD)");
+      statement.execute(
+          "INSERT INTO delete_by_attr_null_tag(time, deviceId, site, attr1, 
s1) VALUES "
+              + "(1, 'd1', 'north', 'red', 11),"
+              + "(2, 'd1', 'north', 'red', 12),"
+              + "(1, 'd2', 'south', 'blue', 21),"
+              + "(2, 'd2', 'south', 'blue', 22)");
+      statement.execute(
+          "INSERT INTO delete_by_attr_null_tag(time, deviceId, attr1, s1) 
VALUES "
+              + "(1, 'd3', 'red', 31),"
+              + "(2, 'd3', 'red', 32),"
+              + "(1, 'd4', 'blue', 41),"
+              + "(2, 'd4', 'blue', 42)");
+
+      statement.execute(
+          "DELETE FROM delete_by_attr_null_tag "
+              + "WHERE attr1 = 'red' AND site IS NULL AND time = 2");
+
+      final List<String> actual = new ArrayList<>();
+      try (ResultSet resultSet =
+          statement.executeQuery(
+              "SELECT deviceId, site, time, s1 FROM delete_by_attr_null_tag "
+                  + "ORDER BY deviceId, time")) {
+        while (resultSet.next()) {
+          actual.add(
+              resultSet.getString(1)
+                  + ","
+                  + resultSet.getString(2)
+                  + ","
+                  + resultSet.getLong(3)
+                  + ","
+                  + resultSet.getInt(4));
+        }
+      }
+      assertEquals(
+          List.of(
+              "d1,north,1,11",
+              "d1,north,2,12",
+              "d2,south,1,21",
+              "d2,south,2,22",
+              "d3,null,1,31",
+              "d4,null,1,41",
+              "d4,null,2,42"),
+          actual);
+    }
+  }
+
+  @Test
+  public void testDeleteDataByAttributeFilterWithInvalidComparison() throws 
SQLException {
+    try (Connection connection = 
EnvFactory.getEnv().getConnection(BaseEnv.TABLE_SQL_DIALECT);
+        Statement statement = connection.createStatement()) {
+      statement.execute("use test");
+      statement.execute(
+          "CREATE TABLE delete_by_attr_invalid_ops("
+              + "deviceId STRING TAG, attr1 ATTRIBUTE, s1 INT32 FIELD)");
+      statement.execute(
+          "INSERT INTO delete_by_attr_invalid_ops(time, deviceId, attr1, s1) 
VALUES "
+              + "(1, 'd1', 'a', 11),"
+              + "(1, 'd2', 'b', 21)");
+
+      try {
+        statement.execute("DELETE FROM delete_by_attr_invalid_ops WHERE attr1 
= 1");
+        fail("should not reach here!");
+      } catch (SQLException e) {
+        assertEquals(
+            "701: The right hand value of attribute predicate must be a 
string: 1", e.getMessage());
+      }
+
+      try {
+        statement.execute("DELETE FROM delete_by_attr_invalid_ops WHERE attr1 
= null");
+        fail("should not reach here!");
+      } catch (SQLException e) {
+        assertEquals(
+            "701: The right hand value of attribute predicate cannot be null 
with comparison operator, "
+                + "please use IS NULL or IS NOT NULL instead",
+            e.getMessage());
+      }
+
+      try {
+        statement.execute(
+            "DELETE FROM delete_by_attr_invalid_ops WHERE attr1 IS DISTINCT 
FROM 'a'");
+        fail("should not reach here!");
+      } catch (SQLException e) {
+        assertEquals(
+            "701: The operator of attribute predicate must be =, !=, <, <=, >, 
>=, LIKE, or IN for 'a'",
+            e.getMessage());
+      }
+    }
+  }
+
+  @Test
+  public void testDeleteDataByTagIsNotNullFilter() throws SQLException {
+    try (Connection connection = 
EnvFactory.getEnv().getConnection(BaseEnv.TABLE_SQL_DIALECT);
+        Statement statement = connection.createStatement()) {
+      statement.execute("use test");
+      statement.execute(
+          "CREATE TABLE delete_by_tag_not_null("
+              + "deviceId STRING TAG, site STRING TAG, attr1 ATTRIBUTE, s1 
INT32 FIELD)");
+      statement.execute(
+          "INSERT INTO delete_by_tag_not_null(time, deviceId, site, attr1, s1) 
VALUES "
+              + "(1, 'd1', 'north', 'red', 11),"
+              + "(2, 'd1', 'north', 'red', 12),"
+              + "(1, 'd2', 'south', 'blue', 21),"
+              + "(2, 'd2', 'south', 'blue', 22)");
+      statement.execute(
+          "INSERT INTO delete_by_tag_not_null(time, deviceId, attr1, s1) 
VALUES "
+              + "(1, 'd3', 'green', 31),"
+              + "(2, 'd3', 'green', 32),"
+              + "(1, 'd4', 'red', 41),"
+              + "(2, 'd4', 'red', 42)");
+
+      statement.execute("DELETE FROM delete_by_tag_not_null WHERE site IS NOT 
NULL AND time = 1");
+
+      final List<String> actual = new ArrayList<>();
+      try (ResultSet resultSet =
+          statement.executeQuery(
+              "SELECT deviceId, site, time, s1 FROM delete_by_tag_not_null "
+                  + "ORDER BY deviceId, time")) {
+        while (resultSet.next()) {
+          actual.add(
+              resultSet.getString(1)
+                  + ","
+                  + resultSet.getString(2)
+                  + ","
+                  + resultSet.getLong(3)
+                  + ","
+                  + resultSet.getInt(4));
+        }
+      }
+      assertEquals(
+          List.of(
+              "d1,north,2,12",
+              "d2,south,2,22",
+              "d3,null,1,31",
+              "d3,null,2,32",
+              "d4,null,1,41",
+              "d4,null,2,42"),
+          actual);
+
+      statement.execute(
+          "DELETE FROM delete_by_tag_not_null WHERE site IS NOT NULL AND 
attr1='red'");
+
+      actual.clear();
+      try (ResultSet resultSet =
+          statement.executeQuery(
+              "SELECT deviceId, site, time, s1 FROM delete_by_tag_not_null "
+                  + "ORDER BY deviceId, time")) {
+        while (resultSet.next()) {
+          actual.add(
+              resultSet.getString(1)
+                  + ","
+                  + resultSet.getString(2)
+                  + ","
+                  + resultSet.getLong(3)
+                  + ","
+                  + resultSet.getInt(4));
+        }
+      }
+      assertEquals(
+          List.of("d2,south,2,22", "d3,null,1,31", "d3,null,2,32", 
"d4,null,1,41", "d4,null,2,42"),
+          actual);
+    }
+  }
+
+  @Test
+  public void testDeleteDataByAttributeFilterRejectsTooManyDevices() throws 
SQLException {
+    try (Connection connection = 
EnvFactory.getEnv().getConnection(BaseEnv.TABLE_SQL_DIALECT);
+        Statement statement = connection.createStatement()) {
+      statement.execute("use test");
+      statement.execute(
+          "CREATE TABLE delete_by_attr_limit(deviceId STRING TAG, attr1 
ATTRIBUTE, s1 INT32 FIELD)");
+      final StringBuilder builder =
+          new StringBuilder("INSERT INTO delete_by_attr_limit(time, deviceId, 
attr1, s1) VALUES ");
+      for (int i = 0; i <= 1000; i++) {
+        if (i > 0) {
+          builder.append(',');
+        }
+        builder.append("(1, 'd").append(i).append("', 'same', 
").append(i).append(')');
+      }
+      statement.execute(builder.toString());
+
+      try {
+        statement.execute("DELETE FROM delete_by_attr_limit WHERE attr1 = 
'same'");
+        fail("should not reach here!");
+      } catch (SQLException e) {
+        assertTrue(e.getMessage().contains("Too many devices (1001)"));
+        assertTrue(e.getMessage().contains("limit is 1000"));
+        assertTrue(e.getMessage().contains("attr1"));
+        assertTrue(e.getMessage().contains("Please remove all attribute 
filters"));
+      }
+    }
+  }
+
   @Test
   public void test() throws SQLException {
     int testId = 2;
diff --git 
a/iotdb-core/datanode/src/main/i18n/en/org/apache/iotdb/db/i18n/DataNodeQueryMessages.java
 
b/iotdb-core/datanode/src/main/i18n/en/org/apache/iotdb/db/i18n/DataNodeQueryMessages.java
index d9f5b182122..7978fce5113 100644
--- 
a/iotdb-core/datanode/src/main/i18n/en/org/apache/iotdb/db/i18n/DataNodeQueryMessages.java
+++ 
b/iotdb-core/datanode/src/main/i18n/en/org/apache/iotdb/db/i18n/DataNodeQueryMessages.java
@@ -308,8 +308,33 @@ public final class DataNodeQueryMessages {
       "The left hand value must be an identifier: ";
   public static final String THE_TABLE_S_DOES_NOT_CONTAIN_A_TIME_COLUMN =
       "The table '%s' does not contain a time column";
+  public static final String START_TIME_IS_GREATER_THAN_END_TIME =
+      "Start time %d is greater than end time %d";
+  public static final String 
THE_RIGHT_HAND_VALUE_OF_TIME_PREDICATE_MUST_BE_A_LONG =
+      "The right hand value of time predicate must be a long: ";
+  public static final String THE_OPERATOR_OF_TIME_PREDICATE_MUST_BE_FOR =
+      "The operator of time predicate must be <, <=, >, or >=: ";
   public static final String THE_OPERATOR_OF_TAG_PREDICATE_MUST_BE_FOR =
       "The operator of tag predicate must be '=' for ";
+  public static final String
+      
THE_RIGHT_HAND_VALUE_OF_TAG_PREDICATE_CANNOT_BE_NULL_WITH_COMPARISON_OPERATOR =
+          "The right hand value of tag predicate cannot be null with '=' 
operator, "
+              + "please use 'IS NULL' instead";
+  public static final String 
THE_RIGHT_HAND_VALUE_OF_TAG_PREDICATE_MUST_BE_A_STRING =
+      "The right hand value of tag predicate must be a string: ";
+  public static final String THE_OPERATOR_OF_ATTRIBUTE_PREDICATE_MUST_BE_FOR =
+      "The operator of attribute predicate must be =, !=, <, <=, >, >=, LIKE, 
or IN for ";
+  public static final String
+      
THE_RIGHT_HAND_VALUE_OF_ATTRIBUTE_PREDICATE_CANNOT_BE_NULL_WITH_COMPARISON_OPERATOR
 =
+          "The right hand value of attribute predicate cannot be null with 
comparison operator, "
+              + "please use IS NULL or IS NOT NULL instead";
+  public static final String 
THE_RIGHT_HAND_VALUE_OF_ATTRIBUTE_PREDICATE_MUST_BE_A_STRING =
+      "The right hand value of attribute predicate must be a string: ";
+  public static final String 
THE_COLUMN_S_DOES_NOT_EXIST_OR_IS_NOT_A_TAG_COLUMN =
+      "The column '%s' does not exist or is not a tag column";
+  public static final String 
TOO_MANY_DEVICES_MATCHED_BY_ATTRIBUTE_FILTERS_IN_DELETION =
+      "Too many devices (%d) matched by attribute filters in deletion, limit 
is %d. "
+          + "Please remove all attribute filters (%s) or add more attribute 
filters.";
   public static final String ONLY_TIME_FILTERS_ARE_SUPPORTED_IN_LAST_QUERY =
       "Only time filters are supported in LAST query";
   public static final String VIEWS_CANNOT_BE_USED_IN_GROUP_BY_TAGS =
@@ -1691,14 +1716,10 @@ public final class DataNodeQueryMessages {
       "The column '";
   public static final String DOES_NOT_EXIST_OR_IS_NOT_A_TAG_COLUMN =
       "' does not exist or is not a tag column";
-  public static final String 
THE_RIGHT_HAND_VALUE_OF_TIME_PREDICATE_MUST_BE_A_LONG =
-      "The right hand value of time predicate must be a long: ";
   public static final String 
THE_OPERATOR_OF_TIME_PREDICATE_MUST_BE_LT_LT_EQ_GT_OR_GT_EQ =
       "The operator of time predicate must be <, <=, >, or >=: ";
   public static final String 
THE_RIGHT_HAND_VALUE_OF_TAG_PREDICATE_CANNOT_BE_NULL_WITH_EQ_OPERATOR_PLEASE_USE_IS_NULL
 =
       "The right hand value of tag predicate cannot be null with '=' operator, 
please use 'IS NULL' instead";
-  public static final String 
THE_RIGHT_HAND_VALUE_OF_TAG_PREDICATE_MUST_BE_A_STRING =
-      "The right hand value of tag predicate must be a string: ";
   public static final String 
SELECT_INTO_PLACEHOLDER_CAN_ONLY_BE_USED_AT_THE_END_OF_THE_PATH =
       "select into: placeholder `::` can only be used at the end of the path.";
   public static final String 
SELECT_INTO_THE_I_OF_DOLLAR_I_SHOULD_BE_GREATER_THAN_0_AND_EQUAL_TO_OR_LESS_THAN_THE
 =
diff --git 
a/iotdb-core/datanode/src/main/i18n/en/org/apache/iotdb/db/i18n/StorageEngineMessages.java
 
b/iotdb-core/datanode/src/main/i18n/en/org/apache/iotdb/db/i18n/StorageEngineMessages.java
index ad391f24bcd..f8b6e50ed53 100644
--- 
a/iotdb-core/datanode/src/main/i18n/en/org/apache/iotdb/db/i18n/StorageEngineMessages.java
+++ 
b/iotdb-core/datanode/src/main/i18n/en/org/apache/iotdb/db/i18n/StorageEngineMessages.java
@@ -80,6 +80,8 @@ public final class StorageEngineMessages {
   public static final String INTERRUPTED_WAITING_THREAD_POOL_EXIT = 
"Interrupted while waiting {} thread pool to exit. ";
   public static final String BUFFERED_ARRAY_SIZE_THRESHOLD = 
"BufferedArraySizeThreshold is {}";
   public static final String CURRENT_SG_COST = "Current Sg cost is {}";
+  public static final String DIRECT_BUFFER_MEMORY_ALLOCATION_FAILED =
+      "Total allocated memory for direct buffer will be %d, which is greater 
than limit mem cost: %d";
   public static final String FORCE_DEGRADE_TSFILE_RESOURCE = "Force degrade 
tsfile resource {}";
   public static final String CANNOT_DEGRADE_TIME_INDEX_ALL_FILE_LEVEL = "Can't 
degrade time index any more because all time index are file level.";
   public static final String DEGRADE_TSFILE_RESOURCE = "Degrade tsfile 
resource {}";
diff --git 
a/iotdb-core/datanode/src/main/i18n/zh/org/apache/iotdb/db/i18n/DataNodeQueryMessages.java
 
b/iotdb-core/datanode/src/main/i18n/zh/org/apache/iotdb/db/i18n/DataNodeQueryMessages.java
index 4ca8eda2fb0..34a7fa084bf 100644
--- 
a/iotdb-core/datanode/src/main/i18n/zh/org/apache/iotdb/db/i18n/DataNodeQueryMessages.java
+++ 
b/iotdb-core/datanode/src/main/i18n/zh/org/apache/iotdb/db/i18n/DataNodeQueryMessages.java
@@ -295,8 +295,30 @@ public final class DataNodeQueryMessages {
       "左侧值必须是标识符:";
   public static final String THE_TABLE_S_DOES_NOT_CONTAIN_A_TIME_COLUMN =
       "表 '%s' 不包含时间列";
+  public static final String START_TIME_IS_GREATER_THAN_END_TIME =
+      "开始时间 %d 大于结束时间 %d";
+  public static final String 
THE_RIGHT_HAND_VALUE_OF_TIME_PREDICATE_MUST_BE_A_LONG =
+      "时间谓词的右值必须是 long 类型:";
+  public static final String THE_OPERATOR_OF_TIME_PREDICATE_MUST_BE_FOR =
+      "时间谓词的操作符必须是 <、<=、> 或 >=:";
   public static final String THE_OPERATOR_OF_TAG_PREDICATE_MUST_BE_FOR =
       "标签谓词的运算符必须为 '=',目标:";
+  public static final String
+      
THE_RIGHT_HAND_VALUE_OF_TAG_PREDICATE_CANNOT_BE_NULL_WITH_COMPARISON_OPERATOR =
+          "标签谓词在使用 '=' 操作符时右值不能为 null,请改用 'IS NULL'";
+  public static final String 
THE_RIGHT_HAND_VALUE_OF_TAG_PREDICATE_MUST_BE_A_STRING =
+      "标签谓词的右值必须是字符串:";
+  public static final String THE_OPERATOR_OF_ATTRIBUTE_PREDICATE_MUST_BE_FOR =
+      "属性谓词的运算符必须为 =, !=, <, <=, >, >=, LIKE 或 IN,目标:";
+  public static final String
+      
THE_RIGHT_HAND_VALUE_OF_ATTRIBUTE_PREDICATE_CANNOT_BE_NULL_WITH_COMPARISON_OPERATOR
 =
+          "属性谓词的右侧值不能在比较运算符中为 null,请改用 IS NULL 或 IS NOT NULL";
+  public static final String 
THE_RIGHT_HAND_VALUE_OF_ATTRIBUTE_PREDICATE_MUST_BE_A_STRING =
+      "属性谓词的右侧值必须为字符串:";
+  public static final String 
THE_COLUMN_S_DOES_NOT_EXIST_OR_IS_NOT_A_TAG_COLUMN =
+      "列 '%s' 不存在或不是标签列";
+  public static final String 
TOO_MANY_DEVICES_MATCHED_BY_ATTRIBUTE_FILTERS_IN_DELETION =
+      "删除语句中的属性过滤条件匹配了过多设备(%d),限制为 %d。请移除所有属性过滤条件(%s),或进一步增加属性过滤条件。";
   public static final String ONLY_TIME_FILTERS_ARE_SUPPORTED_IN_LAST_QUERY =
       "LAST 查询中仅支持时间过滤器";
   public static final String VIEWS_CANNOT_BE_USED_IN_GROUP_BY_TAGS =
@@ -1742,9 +1764,6 @@ public final class DataNodeQueryMessages {
   public static final String DOES_NOT_EXIST_OR_IS_NOT_A_TAG_COLUMN =
 
       "' 不存在或不是标签列";
-  public static final String 
THE_RIGHT_HAND_VALUE_OF_TIME_PREDICATE_MUST_BE_A_LONG =
-
-      "时间谓词的右值必须为 long:";
   public static final String 
THE_OPERATOR_OF_TIME_PREDICATE_MUST_BE_LT_LT_EQ_GT_OR_GT_EQ =
 
       "时间谓词的操作符必须为 <、<=、> 或 >=:";
@@ -1752,9 +1771,6 @@ public final class DataNodeQueryMessages {
 
 
       "使用 '=' 操作符时,标签谓词的右值不能为 null,请改用 'IS NULL'";
-  public static final String 
THE_RIGHT_HAND_VALUE_OF_TAG_PREDICATE_MUST_BE_A_STRING =
-
-      "标签谓词的右值必须为字符串:";
   public static final String 
SELECT_INTO_PLACEHOLDER_CAN_ONLY_BE_USED_AT_THE_END_OF_THE_PATH =
 
 
diff --git 
a/iotdb-core/datanode/src/main/i18n/zh/org/apache/iotdb/db/i18n/StorageEngineMessages.java
 
b/iotdb-core/datanode/src/main/i18n/zh/org/apache/iotdb/db/i18n/StorageEngineMessages.java
index 3f237d61f13..bffda3d5287 100644
--- 
a/iotdb-core/datanode/src/main/i18n/zh/org/apache/iotdb/db/i18n/StorageEngineMessages.java
+++ 
b/iotdb-core/datanode/src/main/i18n/zh/org/apache/iotdb/db/i18n/StorageEngineMessages.java
@@ -80,6 +80,8 @@ public final class StorageEngineMessages {
   public static final String INTERRUPTED_WAITING_THREAD_POOL_EXIT = "等待 {} 
线程池退出时被中断。 ";
   public static final String BUFFERED_ARRAY_SIZE_THRESHOLD = 
"BufferedArraySizeThreshold 为 {}";
   public static final String CURRENT_SG_COST = "当前存储组内存开销为 {}";
+  public static final String DIRECT_BUFFER_MEMORY_ALLOCATION_FAILED =
+      "DirectBuffer 总分配内存将达到 %d,超过内存限制:%d";
   public static final String FORCE_DEGRADE_TSFILE_RESOURCE = "强制降级 TsFile 资源 
{}";
   public static final String CANNOT_DEGRADE_TIME_INDEX_ALL_FILE_LEVEL = 
"无法继续降级时间索引,所有时间索引已为文件级别。";
   public static final String DEGRADE_TSFILE_RESOURCE = "降级 TsFile 资源 {}";
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/exception/DirectBufferMemoryAllocationException.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/exception/DirectBufferMemoryAllocationException.java
new file mode 100644
index 00000000000..0e29e76b880
--- /dev/null
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/exception/DirectBufferMemoryAllocationException.java
@@ -0,0 +1,35 @@
+/*
+ * 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.exception;
+
+import org.apache.iotdb.db.i18n.StorageEngineMessages;
+
+public class DirectBufferMemoryAllocationException extends DataRegionException 
{
+
+  private static final long serialVersionUID = -8268138833145785933L;
+
+  public DirectBufferMemoryAllocationException(
+      long allocatedDirectBufferMemory, long directBufferMemoryLimit) {
+    super(
+        String.format(
+            StorageEngineMessages.DIRECT_BUFFER_MEMORY_ALLOCATION_FAILED,
+            allocatedDirectBufferMemory,
+            directBufferMemoryLimit));
+  }
+}
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/thrift/impl/DataNodeInternalRPCServiceImpl.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/thrift/impl/DataNodeInternalRPCServiceImpl.java
index a96839d896a..93cff6b8ff9 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/thrift/impl/DataNodeInternalRPCServiceImpl.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/thrift/impl/DataNodeInternalRPCServiceImpl.java
@@ -212,8 +212,8 @@ import 
org.apache.iotdb.db.storageengine.dataregion.compaction.schedule.Compacti
 import 
org.apache.iotdb.db.storageengine.dataregion.compaction.settle.SettleRequestHandler;
 import org.apache.iotdb.db.storageengine.dataregion.flush.CompressionRatio;
 import 
org.apache.iotdb.db.storageengine.dataregion.modification.DeletionPredicate;
-import org.apache.iotdb.db.storageengine.dataregion.modification.IDPredicate;
 import 
org.apache.iotdb.db.storageengine.dataregion.modification.TableDeletionEntry;
+import org.apache.iotdb.db.storageengine.dataregion.modification.TagPredicate;
 import org.apache.iotdb.db.storageengine.dataregion.tsfile.TsFileManager;
 import org.apache.iotdb.db.storageengine.dataregion.tsfile.TsFileResource;
 import 
org.apache.iotdb.db.storageengine.rescon.quotas.DataNodeSpaceQuotaManager;
@@ -2127,7 +2127,7 @@ public class DataNodeInternalRPCServiceImpl implements 
IDataNodeRPCService.Iface
                             new TableDeletionEntry(
                                 new DeletionPredicate(
                                     req.getTableName(),
-                                    new IDPredicate.NOP(),
+                                    new TagPredicate.NOP(),
                                     
Collections.singletonList(req.getColumnName())),
                                 new TimeRange(Long.MIN_VALUE, Long.MAX_VALUE)),
                             // the request is only sent to associated region
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/AnalyzeUtils.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/AnalyzeUtils.java
index 50a97291548..81849ff9dfb 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/AnalyzeUtils.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/AnalyzeUtils.java
@@ -26,16 +26,23 @@ import 
org.apache.iotdb.commons.exception.IoTDBRuntimeException;
 import org.apache.iotdb.commons.exception.SemanticException;
 import org.apache.iotdb.commons.partition.DataPartition;
 import org.apache.iotdb.commons.partition.DataPartitionQueryParam;
+import 
org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.CommonQueryAstVisitor;
 import 
org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.ComparisonExpression;
 import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Expression;
 import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Identifier;
+import 
org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.InListExpression;
+import 
org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.InPredicate;
+import 
org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.IsNotNullPredicate;
 import 
org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.IsNullPredicate;
+import 
org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.LikePredicate;
 import 
org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.LogicalExpression;
 import 
org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.LogicalExpression.Operator;
 import 
org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.LongLiteral;
 import 
org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.NullLiteral;
 import 
org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.StringLiteral;
+import 
org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.SymbolReference;
 import org.apache.iotdb.commons.schema.table.TsTable;
+import org.apache.iotdb.commons.schema.table.column.TsTableColumnCategory;
 import org.apache.iotdb.commons.schema.table.column.TsTableColumnSchema;
 import org.apache.iotdb.commons.service.metric.PerformanceOverviewMetrics;
 import org.apache.iotdb.confignode.rpc.thrift.TGetRegionGroupsByTimeReq;
@@ -45,6 +52,10 @@ import org.apache.iotdb.db.protocol.client.ConfigNodeClient;
 import org.apache.iotdb.db.protocol.client.ConfigNodeClientManager;
 import org.apache.iotdb.db.protocol.client.ConfigNodeInfo;
 import org.apache.iotdb.db.queryengine.common.MPPQueryContext;
+import org.apache.iotdb.db.queryengine.plan.relational.metadata.DeviceEntry;
+import 
org.apache.iotdb.db.queryengine.plan.relational.metadata.fetcher.TableDeviceSchemaFetcher;
+import 
org.apache.iotdb.db.queryengine.plan.relational.planner.ir.ExpressionRewriter;
+import 
org.apache.iotdb.db.queryengine.plan.relational.planner.ir.ExpressionTreeRewriter;
 import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.Delete;
 import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.TimeRange;
 import org.apache.iotdb.db.queryengine.plan.statement.crud.InsertBaseStatement;
@@ -55,10 +66,12 @@ import 
org.apache.iotdb.db.queryengine.plan.statement.crud.InsertTabletStatement
 import org.apache.iotdb.db.schemaengine.table.DataNodeTableCache;
 import org.apache.iotdb.db.schemaengine.table.DataNodeTreeViewSchemaUtils;
 import 
org.apache.iotdb.db.storageengine.dataregion.modification.DeletionPredicate;
-import org.apache.iotdb.db.storageengine.dataregion.modification.IDPredicate;
-import 
org.apache.iotdb.db.storageengine.dataregion.modification.IDPredicate.And;
-import 
org.apache.iotdb.db.storageengine.dataregion.modification.IDPredicate.SegmentExactMatch;
 import 
org.apache.iotdb.db.storageengine.dataregion.modification.TableDeletionEntry;
+import org.apache.iotdb.db.storageengine.dataregion.modification.TagPredicate;
+import 
org.apache.iotdb.db.storageengine.dataregion.modification.TagPredicate.And;
+import 
org.apache.iotdb.db.storageengine.dataregion.modification.TagPredicate.DeviceIn;
+import 
org.apache.iotdb.db.storageengine.dataregion.modification.TagPredicate.SegmentExactMatch;
+import 
org.apache.iotdb.db.storageengine.dataregion.modification.TagPredicate.SegmentNotNull;
 import org.apache.iotdb.rpc.RpcUtils;
 import org.apache.iotdb.rpc.TSStatusCode;
 
@@ -82,6 +95,58 @@ import static 
org.apache.iotdb.db.queryengine.plan.execution.config.TableConfigT
 
 public class AnalyzeUtils {
 
+  private static final int ATTRIBUTE_FILTER_DELETE_DEVICE_IN_LIMIT = 1000;
+  private static final CommonQueryAstVisitor<PredicateParseResult, 
PredicateParseContext>
+      DELETION_PREDICATE_PARSE_VISITOR =
+          new CommonQueryAstVisitor<>() {
+            @Override
+            public PredicateParseResult visitLogicalExpression(
+                final LogicalExpression node, final PredicateParseContext 
context) {
+              parseAndPredicate(node, context.expressionQueue);
+              return PredicateParseResult.empty(context.tagPredicate);
+            }
+
+            @Override
+            public PredicateParseResult visitComparisonExpression(
+                final ComparisonExpression node, final PredicateParseContext 
context) {
+              return parseComparison(node, context.timeRange, 
context.tagPredicate, context.table);
+            }
+
+            @Override
+            public PredicateParseResult visitIsNullPredicate(
+                final IsNullPredicate node, final PredicateParseContext 
context) {
+              return parseIsNull(node, context.tagPredicate, context.table);
+            }
+
+            @Override
+            public PredicateParseResult visitIsNotNullPredicate(
+                final IsNotNullPredicate node, final PredicateParseContext 
context) {
+              return parseIsNotNull(node, context.tagPredicate, context.table);
+            }
+
+            @Override
+            public PredicateParseResult visitLikePredicate(
+                final LikePredicate node, final PredicateParseContext context) 
{
+              return parseLike(node, context.tagPredicate, context.table);
+            }
+
+            @Override
+            public PredicateParseResult visitInPredicate(
+                final InPredicate node, final PredicateParseContext context) {
+              return parseIn(node, context.tagPredicate, context.table);
+            }
+
+            @Override
+            public PredicateParseResult visitExpression(
+                final Expression node, final PredicateParseContext context) {
+              throw new SemanticException(
+                  DataNodeQueryMessages.UNSUPPORTED_EXPRESSION
+                      + node
+                      + " in "
+                      + context.rootExpression);
+            }
+          };
+
   private static final PerformanceOverviewMetrics PERFORMANCE_OVERVIEW_METRICS 
=
       PerformanceOverviewMetrics.getInstance();
   private static final Logger LOGGER = 
LoggerFactory.getLogger(AnalyzeUtils.class);
@@ -372,14 +437,20 @@ public class AnalyzeUtils {
     // Maybe set by pipe transfer
     if (Objects.isNull(node.getTableDeletionEntries())) {
       node.setTableDeletionEntries(
-          parseExpressions2ModEntries(node.getWhere().orElse(null), table));
+          parseExpressions2ModEntries(
+              node.getWhere().orElse(null), table, databaseName, 
queryContext));
     }
   }
 
   public static List<TableDeletionEntry> parseExpressions2ModEntries(
-      final Expression expression, final TsTable table) {
+      final Expression expression,
+      final TsTable table,
+      final String databaseName,
+      final MPPQueryContext queryContext) {
     return toDisjunctiveNormalForms(expression).stream()
-        .map(disjunctiveNormalForm -> parsePredicate(disjunctiveNormalForm, 
table))
+        .map(
+            disjunctiveNormalForm ->
+                parsePredicate(disjunctiveNormalForm, table, databaseName, 
queryContext))
         .collect(Collectors.toList());
   }
 
@@ -447,47 +518,90 @@ public class AnalyzeUtils {
     return results;
   }
 
-  private static TableDeletionEntry parsePredicate(Expression expression, 
TsTable table) {
+  private static TableDeletionEntry parsePredicate(
+      Expression expression,
+      TsTable table,
+      final String databaseName,
+      final MPPQueryContext queryContext) {
     if (expression == null) {
       return new TableDeletionEntry(
           new DeletionPredicate(table.getTableName()),
           new TimeRange(Long.MIN_VALUE, Long.MAX_VALUE, 
true).toTsFileTimeRange());
     }
 
-    Queue<Expression> expressionQueue = new LinkedList<>();
-    expressionQueue.add(expression);
+    final PredicateParseContext predicateParseContext =
+        new PredicateParseContext(table, new TimeRange(Long.MIN_VALUE, 
Long.MAX_VALUE, true));
+    predicateParseContext.expressionQueue.add(expression);
     DeletionPredicate predicate = new DeletionPredicate(table.getTableName());
-    IDPredicate tagPredicate = null;
-    TimeRange timeRange = new TimeRange(Long.MIN_VALUE, Long.MAX_VALUE, true);
-    while (!expressionQueue.isEmpty()) {
-      Expression currExp = expressionQueue.remove();
-      if (currExp instanceof LogicalExpression) {
-        parseAndPredicate(((LogicalExpression) currExp), expressionQueue);
-      } else if (currExp instanceof ComparisonExpression) {
-        tagPredicate =
-            parseComparison(((ComparisonExpression) currExp), timeRange, 
tagPredicate, table);
-      } else if (currExp instanceof IsNullPredicate) {
-        tagPredicate = parseIsNull((IsNullPredicate) currExp, tagPredicate, 
table);
-      } else {
+    predicateParseContext.rootExpression = expression;
+    while (!predicateParseContext.expressionQueue.isEmpty()) {
+      final Expression currExp = 
predicateParseContext.expressionQueue.remove();
+      applyPredicateParseResult(
+          currExp,
+          DELETION_PREDICATE_PARSE_VISITOR.process(currExp, 
predicateParseContext),
+          predicateParseContext);
+    }
+    if (Objects.nonNull(predicateParseContext.attributeColumns)) {
+      final Set<IDeviceID> deviceIDs =
+          TableDeviceSchemaFetcher.getInstance()
+              .fetchDeviceSchemaForDataQuery(
+                  databaseName,
+                  table.getTableName(),
+                  predicateParseContext.deviceFilterExpressions,
+                  predicateParseContext.attributeColumns,
+                  queryContext)
+              .values()
+              .stream()
+              .flatMap(List::stream)
+              .map(DeviceEntry::getDeviceID)
+              .collect(Collectors.toSet());
+      if (deviceIDs.size() > ATTRIBUTE_FILTER_DELETE_DEVICE_IN_LIMIT) {
         throw new SemanticException(
-            DataNodeQueryMessages.UNSUPPORTED_EXPRESSION
-                + currExp
-                + DataNodeQueryMessages.IN
-                + expression);
+            String.format(
+                
DataNodeQueryMessages.TOO_MANY_DEVICES_MATCHED_BY_ATTRIBUTE_FILTERS_IN_DELETION,
+                deviceIDs.size(),
+                ATTRIBUTE_FILTER_DELETE_DEVICE_IN_LIMIT,
+                predicateParseContext.attributeColumns));
       }
+      predicateParseContext.tagPredicate = new DeviceIn(deviceIDs);
     }
-    if (tagPredicate != null) {
-      predicate.setIdPredicate(tagPredicate);
+    if (predicateParseContext.tagPredicate != null) {
+      predicate.setTagPredicate(predicateParseContext.tagPredicate);
     }
-    if (timeRange.getStartTime() > timeRange.getEndTime()) {
+    if (predicateParseContext.timeRange.getStartTime()
+        > predicateParseContext.timeRange.getEndTime()) {
       throw new SemanticException(
           String.format(
-              DataNodeQueryMessages.START_TIME_D_IS_GREATER_THAN_END_TIME_D,
-              timeRange.getStartTime(),
-              timeRange.getEndTime()));
+              DataNodeQueryMessages.START_TIME_IS_GREATER_THAN_END_TIME,
+              predicateParseContext.timeRange.getStartTime(),
+              predicateParseContext.timeRange.getEndTime()));
     }
 
-    return new TableDeletionEntry(predicate, timeRange.toTsFileTimeRange());
+    return new TableDeletionEntry(predicate, 
predicateParseContext.timeRange.toTsFileTimeRange());
+  }
+
+  private static void applyPredicateParseResult(
+      final Expression expression,
+      final PredicateParseResult parseResult,
+      final PredicateParseContext context) {
+    context.tagPredicate = parseResult.tagPredicate;
+    if (parseResult.shouldQueryDevice()) {
+      if (Objects.isNull(context.attributeColumns)) {
+        context.attributeColumns = new ArrayList<>();
+      }
+      addDeviceFilterExpression(expression, context);
+      collectAttributeColumn(context.attributeColumns, 
parseResult.attributeColumn);
+    } else if (parseResult.shouldFilterDevice()) {
+      addDeviceFilterExpression(expression, context);
+    }
+  }
+
+  private static void addDeviceFilterExpression(
+      final Expression expression, final PredicateParseContext context) {
+    if (Objects.isNull(context.deviceFilterExpressions)) {
+      context.deviceFilterExpressions = new ArrayList<>();
+    }
+    
context.deviceFilterExpressions.add(toSymbolReferenceExpression(expression));
   }
 
   private static void parseAndPredicate(
@@ -498,42 +612,133 @@ public class AnalyzeUtils {
     expressionQueue.addAll(expression.getTerms());
   }
 
-  private static IDPredicate parseIsNull(
-      IsNullPredicate isNullPredicate, IDPredicate oldPredicate, TsTable 
table) {
+  private static void collectAttributeColumn(
+      final List<String> attributeColumns, final String attributeColumn) {
+    if (Objects.nonNull(attributeColumn) && 
!attributeColumns.contains(attributeColumn)) {
+      attributeColumns.add(attributeColumn);
+    }
+  }
+
+  private static PredicateParseResult parseIsNull(
+      IsNullPredicate isNullPredicate, TagPredicate oldPredicate, TsTable 
table) {
     Expression leftHandExp = isNullPredicate.getValue();
     if (!(leftHandExp instanceof Identifier)) {
       throw new SemanticException(
           DataNodeQueryMessages.LEFT_HAND_EXPRESSION_IS_NOT_AN_IDENTIFIER + 
leftHandExp);
     }
     String columnName = ((Identifier) leftHandExp).getValue();
+    final TsTableColumnSchema columnSchema = table.getColumnSchema(columnName);
+    if (Objects.nonNull(columnSchema)
+        && 
columnSchema.getColumnCategory().equals(TsTableColumnCategory.ATTRIBUTE)) {
+      return PredicateParseResult.attribute(columnName, oldPredicate);
+    }
+    int tagColumnOrdinal = table.getTagColumnOrdinal(columnName);
+    if (tagColumnOrdinal == -1) {
+      throw new SemanticException(
+          String.format(
+              
DataNodeQueryMessages.THE_COLUMN_S_DOES_NOT_EXIST_OR_IS_NOT_A_TAG_COLUMN,
+              columnName));
+    }
+
+    // the first segment is the table name, so + 1
+    TagPredicate newPredicate = new SegmentExactMatch(null, tagColumnOrdinal + 
1);
+    return PredicateParseResult.tag(combinePredicates(oldPredicate, 
newPredicate));
+  }
+
+  private static PredicateParseResult parseIsNotNull(
+      IsNotNullPredicate isNotNullPredicate, TagPredicate oldPredicate, 
TsTable table) {
+    Expression leftHandExp = isNotNullPredicate.getValue();
+    if (!(leftHandExp instanceof Identifier)) {
+      throw new SemanticException(
+          DataNodeQueryMessages.LEFT_HAND_EXPRESSION_IS_NOT_AN_IDENTIFIER + 
leftHandExp);
+    }
+    String columnName = ((Identifier) leftHandExp).getValue();
+    final TsTableColumnSchema columnSchema = table.getColumnSchema(columnName);
+    if (Objects.nonNull(columnSchema)
+        && 
columnSchema.getColumnCategory().equals(TsTableColumnCategory.ATTRIBUTE)) {
+      return PredicateParseResult.attribute(columnName, oldPredicate);
+    }
     int tagColumnOrdinal = table.getTagColumnOrdinal(columnName);
     if (tagColumnOrdinal == -1) {
       throw new SemanticException(
-          DataNodeQueryMessages.THE_COLUMN
-              + columnName
-              + DataNodeQueryMessages.DOES_NOT_EXIST_OR_IS_NOT_A_TAG_COLUMN);
+          String.format(
+              
DataNodeQueryMessages.THE_COLUMN_S_DOES_NOT_EXIST_OR_IS_NOT_A_TAG_COLUMN,
+              columnName));
     }
 
     // the first segment is the table name, so + 1
-    IDPredicate newPredicate = new SegmentExactMatch(null, tagColumnOrdinal + 
1);
-    return combinePredicates(oldPredicate, newPredicate);
+    TagPredicate newPredicate = new SegmentNotNull(tagColumnOrdinal + 1);
+    return PredicateParseResult.tag(combinePredicates(oldPredicate, 
newPredicate));
+  }
+
+  private static PredicateParseResult parseLike(
+      LikePredicate likePredicate, TagPredicate oldPredicate, TsTable table) {
+    Expression leftHandExp = likePredicate.getValue();
+    if (!(leftHandExp instanceof Identifier)) {
+      throw new SemanticException(
+          DataNodeQueryMessages.LEFT_HAND_EXPRESSION_IS_NOT_AN_IDENTIFIER + 
leftHandExp);
+    }
+    String columnName = ((Identifier) leftHandExp).getValue();
+    final TsTableColumnSchema columnSchema = table.getColumnSchema(columnName);
+    if (Objects.nonNull(columnSchema)
+        && 
columnSchema.getColumnCategory().equals(TsTableColumnCategory.ATTRIBUTE)) {
+      validateAttributeComparison(likePredicate);
+      return PredicateParseResult.attribute(columnName, oldPredicate);
+    }
+    int tagColumnOrdinal = table.getTagColumnOrdinal(columnName);
+    if (tagColumnOrdinal == -1) {
+      throw new SemanticException(
+          String.format(
+              
DataNodeQueryMessages.THE_COLUMN_S_DOES_NOT_EXIST_OR_IS_NOT_A_TAG_COLUMN,
+              columnName));
+    }
+    throw new SemanticException(
+        DataNodeQueryMessages.THE_OPERATOR_OF_TAG_PREDICATE_MUST_BE_FOR
+            + likePredicate.getPattern());
+  }
+
+  private static PredicateParseResult parseIn(
+      InPredicate inPredicate, TagPredicate oldPredicate, TsTable table) {
+    Expression leftHandExp = inPredicate.getValue();
+    if (!(leftHandExp instanceof Identifier)) {
+      throw new SemanticException(
+          DataNodeQueryMessages.LEFT_HAND_EXPRESSION_IS_NOT_AN_IDENTIFIER + 
leftHandExp);
+    }
+    String columnName = ((Identifier) leftHandExp).getValue();
+    final TsTableColumnSchema columnSchema = table.getColumnSchema(columnName);
+    if (Objects.nonNull(columnSchema)
+        && 
columnSchema.getColumnCategory().equals(TsTableColumnCategory.ATTRIBUTE)) {
+      validateAttributeComparison(inPredicate);
+      return PredicateParseResult.attribute(columnName, oldPredicate);
+    }
+    int tagColumnOrdinal = table.getTagColumnOrdinal(columnName);
+    if (tagColumnOrdinal == -1) {
+      throw new SemanticException(
+          String.format(
+              
DataNodeQueryMessages.THE_COLUMN_S_DOES_NOT_EXIST_OR_IS_NOT_A_TAG_COLUMN,
+              columnName));
+    }
+    throw new SemanticException(
+        DataNodeQueryMessages.THE_OPERATOR_OF_TAG_PREDICATE_MUST_BE_FOR
+            + inPredicate.getValueList());
   }
 
-  private static IDPredicate combinePredicates(IDPredicate oldPredicate, 
IDPredicate newPredicate) {
+  private static TagPredicate combinePredicates(
+      TagPredicate oldPredicate, TagPredicate newPredicate) {
     if (oldPredicate == null) {
       return newPredicate;
     }
-    if (oldPredicate instanceof IDPredicate.And) {
+    if (oldPredicate instanceof TagPredicate.And) {
       ((And) oldPredicate).add(newPredicate);
       return oldPredicate;
     }
-    return new IDPredicate.And(oldPredicate, newPredicate);
+    return new TagPredicate.And(oldPredicate, newPredicate);
   }
 
-  private static IDPredicate parseComparison(
+  private static PredicateParseResult parseComparison(
       ComparisonExpression comparisonExpression,
       TimeRange timeRange,
-      IDPredicate oldPredicate,
+      TagPredicate oldPredicate,
       TsTable table) {
     Expression left = comparisonExpression.getLeft();
     Expression right = comparisonExpression.getRight();
@@ -573,24 +778,80 @@ public class AnalyzeUtils {
         case IS_DISTINCT_FROM:
         default:
           throw new SemanticException(
-              
DataNodeQueryMessages.THE_OPERATOR_OF_TIME_PREDICATE_MUST_BE_LT_LT_EQ_GT_OR_GT_EQ
-                  + right);
+              DataNodeQueryMessages.THE_OPERATOR_OF_TIME_PREDICATE_MUST_BE_FOR 
+ right);
       }
 
-      return oldPredicate;
+      return PredicateParseResult.time(oldPredicate);
     }
     // tag predicate
     String columnName = identifier.getValue();
+    final TsTableColumnSchema columnSchema = table.getColumnSchema(columnName);
+    if (Objects.nonNull(columnSchema)
+        && 
columnSchema.getColumnCategory().equals(TsTableColumnCategory.ATTRIBUTE)) {
+      validateAttributeComparison(comparisonExpression);
+      return PredicateParseResult.attribute(columnName, oldPredicate);
+    }
     int tagColumnOrdinal = table.getTagColumnOrdinal(columnName);
     if (tagColumnOrdinal == -1) {
       throw new SemanticException(
-          DataNodeQueryMessages.THE_COLUMN
-              + columnName
-              + DataNodeQueryMessages.DOES_NOT_EXIST_OR_IS_NOT_A_TAG_COLUMN);
+          String.format(
+              
DataNodeQueryMessages.THE_COLUMN_S_DOES_NOT_EXIST_OR_IS_NOT_A_TAG_COLUMN,
+              columnName));
+    }
+
+    TagPredicate newPredicate = getTagPredicate(comparisonExpression, right, 
tagColumnOrdinal);
+    return PredicateParseResult.tag(combinePredicates(oldPredicate, 
newPredicate));
+  }
+
+  private static void validateAttributeComparison(final LikePredicate 
likePredicate) {
+    validateAttributePredicateStringValue(likePredicate.getPattern());
+    if (likePredicate.getEscape().isPresent()) {
+      validateAttributePredicateStringValue(likePredicate.getEscape().get());
     }
+  }
 
-    IDPredicate newPredicate = getTagPredicate(comparisonExpression, right, 
tagColumnOrdinal);
-    return combinePredicates(oldPredicate, newPredicate);
+  private static void validateAttributeComparison(final InPredicate 
inPredicate) {
+    if (!(inPredicate.getValueList() instanceof InListExpression)) {
+      throw new SemanticException(
+          
DataNodeQueryMessages.THE_RIGHT_HAND_VALUE_OF_ATTRIBUTE_PREDICATE_MUST_BE_A_STRING
+              + inPredicate.getValueList());
+    }
+    for (final Expression expression :
+        ((InListExpression) inPredicate.getValueList()).getValues()) {
+      validateAttributePredicateStringValue(expression);
+    }
+  }
+
+  private static void validateAttributeComparison(final ComparisonExpression 
comparisonExpression) {
+    switch (comparisonExpression.getOperator()) {
+      case EQUAL:
+      case NOT_EQUAL:
+      case LESS_THAN:
+      case LESS_THAN_OR_EQUAL:
+      case GREATER_THAN:
+      case GREATER_THAN_OR_EQUAL:
+        break;
+      case IS_DISTINCT_FROM:
+      default:
+        throw new SemanticException(
+            
DataNodeQueryMessages.THE_OPERATOR_OF_ATTRIBUTE_PREDICATE_MUST_BE_FOR
+                + comparisonExpression.getRight());
+    }
+
+    validateAttributePredicateStringValue(comparisonExpression.getRight());
+  }
+
+  private static void validateAttributePredicateStringValue(final Expression 
expression) {
+    if (expression instanceof NullLiteral) {
+      throw new SemanticException(
+          DataNodeQueryMessages
+              
.THE_RIGHT_HAND_VALUE_OF_ATTRIBUTE_PREDICATE_CANNOT_BE_NULL_WITH_COMPARISON_OPERATOR);
+    }
+    if (!(expression instanceof StringLiteral)) {
+      throw new SemanticException(
+          
DataNodeQueryMessages.THE_RIGHT_HAND_VALUE_OF_ATTRIBUTE_PREDICATE_MUST_BE_A_STRING
+              + expression);
+    }
   }
 
   private static String getTimeColumnName(final TsTable table) {
@@ -604,7 +865,7 @@ public class AnalyzeUtils {
     return timeColumnSchema.getColumnName();
   }
 
-  private static IDPredicate getTagPredicate(
+  private static TagPredicate getTagPredicate(
       ComparisonExpression comparisonExpression, Expression right, int 
tagColumnOrdinal) {
     if (comparisonExpression.getOperator() != 
ComparisonExpression.Operator.EQUAL) {
       throw new SemanticException(
@@ -617,7 +878,7 @@ public class AnalyzeUtils {
     } else if (right instanceof NullLiteral) {
       throw new SemanticException(
           DataNodeQueryMessages
-              
.THE_RIGHT_HAND_VALUE_OF_TAG_PREDICATE_CANNOT_BE_NULL_WITH_EQ_OPERATOR_PLEASE_USE_IS_NULL);
+              
.THE_RIGHT_HAND_VALUE_OF_TAG_PREDICATE_CANNOT_BE_NULL_WITH_COMPARISON_OPERATOR);
     } else {
       throw new SemanticException(
           
DataNodeQueryMessages.THE_RIGHT_HAND_VALUE_OF_TAG_PREDICATE_MUST_BE_A_STRING + 
right);
@@ -626,6 +887,73 @@ public class AnalyzeUtils {
     return new SegmentExactMatch(rightHandValue, tagColumnOrdinal + 1);
   }
 
+  private static Expression toSymbolReferenceExpression(final Expression 
expression) {
+    return ExpressionTreeRewriter.rewriteWith(
+        new ExpressionRewriter<>() {
+          @Override
+          public Expression rewriteIdentifier(
+              final Identifier node,
+              final Void context,
+              final ExpressionTreeRewriter<Void> treeRewriter) {
+            return new SymbolReference(node.getValue());
+          }
+        },
+        expression);
+  }
+
+  private static class PredicateParseContext {
+    private final TsTable table;
+    private final Queue<Expression> expressionQueue = new LinkedList<>();
+    private final TimeRange timeRange;
+    private Expression rootExpression;
+    private TagPredicate tagPredicate;
+    private List<Expression> deviceFilterExpressions;
+    private List<String> attributeColumns;
+
+    private PredicateParseContext(final TsTable table, final TimeRange 
timeRange) {
+      this.table = table;
+      this.timeRange = timeRange;
+    }
+  }
+
+  private static class PredicateParseResult {
+    private final TagPredicate tagPredicate;
+    private final String attributeColumn;
+    private final boolean filterDevice;
+
+    private PredicateParseResult(
+        final TagPredicate tagPredicate, final String attributeColumn, final 
boolean filterDevice) {
+      this.tagPredicate = tagPredicate;
+      this.attributeColumn = attributeColumn;
+      this.filterDevice = filterDevice;
+    }
+
+    private static PredicateParseResult time(final TagPredicate tagPredicate) {
+      return new PredicateParseResult(tagPredicate, null, false);
+    }
+
+    private static PredicateParseResult empty(final TagPredicate tagPredicate) 
{
+      return new PredicateParseResult(tagPredicate, null, false);
+    }
+
+    private static PredicateParseResult tag(final TagPredicate tagPredicate) {
+      return new PredicateParseResult(tagPredicate, null, true);
+    }
+
+    private static PredicateParseResult attribute(
+        final String attributeColumn, final TagPredicate tagPredicate) {
+      return new PredicateParseResult(tagPredicate, attributeColumn, true);
+    }
+
+    private boolean shouldQueryDevice() {
+      return Objects.nonNull(attributeColumn);
+    }
+
+    private boolean shouldFilterDevice() {
+      return filterDevice;
+    }
+  }
+
   public interface DataPartitionQueryFunc {
 
     DataPartition queryDataPartition(
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/StatementAnalyzer.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/StatementAnalyzer.java
index d20e7a42b0a..80e8bae032a 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/StatementAnalyzer.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/StatementAnalyzer.java
@@ -1050,7 +1050,7 @@ public class StatementAnalyzer {
       final TsTable table =
           DataNodeTableCache.getInstance().getTable(node.getDatabase(), 
node.getTableName());
       DataNodeTreeViewSchemaUtils.checkTableInWrite(node.getDatabase(), table);
-      node.parseModEntries(table);
+      node.parseModEntries(table, node.getDatabase(), queryContext);
       analyzeTraverseDevice(node, context, node.getWhere().isPresent());
       node.parseWhere(
           null,
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/DeleteDevice.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/DeleteDevice.java
index d2fa9bbabde..39bc2ecfed5 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/DeleteDevice.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/DeleteDevice.java
@@ -94,8 +94,9 @@ public class DeleteDevice extends AbstractTraverseDevice {
     return mayDeleteDevice;
   }
 
-  public void parseModEntries(final TsTable table) {
-    modEntries = AnalyzeUtils.parseExpressions2ModEntries(where, table);
+  public void parseModEntries(
+      final TsTable table, final String databaseName, final MPPQueryContext 
context) {
+    modEntries = AnalyzeUtils.parseExpressions2ModEntries(where, table, 
databaseName, context);
   }
 
   public void serializeModEntries(final DataOutputStream stream) throws 
IOException {
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/StorageEngine.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/StorageEngine.java
index 972ed73bd41..e0a7dbdcd97 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/StorageEngine.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/StorageEngine.java
@@ -49,6 +49,7 @@ import org.apache.iotdb.consensus.ConsensusFactory;
 import org.apache.iotdb.db.conf.IoTDBConfig;
 import org.apache.iotdb.db.conf.IoTDBDescriptor;
 import org.apache.iotdb.db.exception.DataRegionException;
+import org.apache.iotdb.db.exception.DirectBufferMemoryAllocationException;
 import org.apache.iotdb.db.exception.StorageEngineException;
 import org.apache.iotdb.db.exception.TsFileProcessorException;
 import org.apache.iotdb.db.exception.WriteProcessRejectException;
@@ -260,11 +261,7 @@ public class StorageEngine implements IService {
               try {
                 dataRegion = buildNewDataRegion(sgName, dataRegionId);
               } catch (DataRegionException e) {
-                LOGGER.error(
-                    
StorageEngineMessages.STORAGE_LOG_FAILED_TO_RECOVER_DATA_REGION_804B162D,
-                    sgName,
-                    dataRegionId.getId(),
-                    e);
+                handleDataRegionRecoverFailure(sgName, dataRegionId, e);
                 return null;
               }
               dataRegionMap.put(dataRegionId, dataRegion);
@@ -279,6 +276,17 @@ public class StorageEngine implements IService {
     }
   }
 
+  void handleDataRegionRecoverFailure(
+      String sgName, DataRegionId dataRegionId, DataRegionException e) throws 
DataRegionException {
+    LOGGER.error("Failed to recover data region {}[{}]", sgName, 
dataRegionId.getId(), e);
+    WALRecoverManager.getInstance()
+        .getAllDataRegionScannedLatch()
+        .countDownWithException(e.getMessage());
+    if (e instanceof DirectBufferMemoryAllocationException) {
+      throw e;
+    }
+  }
+
   /** get StorageGroup -> DataRegionIdList map from data/system directory. */
   public Map<String, List<DataRegionId>> getLocalDataRegionInfo() {
     File system = SystemFileFactory.INSTANCE.getFile(systemDir);
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/DataRegion.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/DataRegion.java
index 9f4de75f794..9f8f11cdc50 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/DataRegion.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/DataRegion.java
@@ -61,6 +61,7 @@ import org.apache.iotdb.db.consensus.DataRegionConsensusImpl;
 import org.apache.iotdb.db.exception.BatchProcessException;
 import org.apache.iotdb.db.exception.DataRegionException;
 import org.apache.iotdb.db.exception.DataTypeInconsistentException;
+import org.apache.iotdb.db.exception.DirectBufferMemoryAllocationException;
 import org.apache.iotdb.db.exception.TsFileProcessorException;
 import org.apache.iotdb.db.exception.WriteProcessException;
 import org.apache.iotdb.db.exception.WriteProcessRejectException;
@@ -119,10 +120,10 @@ import 
org.apache.iotdb.db.storageengine.dataregion.flush.TsFileFlushPolicy;
 import org.apache.iotdb.db.storageengine.dataregion.memtable.IMemTable;
 import org.apache.iotdb.db.storageengine.dataregion.memtable.TsFileProcessor;
 import 
org.apache.iotdb.db.storageengine.dataregion.memtable.TsFileProcessorInfo;
-import org.apache.iotdb.db.storageengine.dataregion.modification.IDPredicate;
 import org.apache.iotdb.db.storageengine.dataregion.modification.ModEntry;
 import 
org.apache.iotdb.db.storageengine.dataregion.modification.ModificationFile;
 import 
org.apache.iotdb.db.storageengine.dataregion.modification.TableDeletionEntry;
+import org.apache.iotdb.db.storageengine.dataregion.modification.TagPredicate;
 import 
org.apache.iotdb.db.storageengine.dataregion.modification.TreeDeletionEntry;
 import 
org.apache.iotdb.db.storageengine.dataregion.modification.v1.ModificationFileV1;
 import org.apache.iotdb.db.storageengine.dataregion.read.IQueryDataSource;
@@ -605,10 +606,6 @@ public class DataRegion implements IDataRegionForQuery {
     try {
       recoverCompaction();
     } catch (Exception e) {
-      // signal wal recover manager to recover this region's files
-      WALRecoverManager.getInstance()
-          .getAllDataRegionScannedLatch()
-          .countDownWithException(e.getMessage());
       throw new DataRegionException(e);
     }
 
@@ -779,10 +776,6 @@ public class DataRegion implements IDataRegionForQuery {
         updatePartitionFileVersion(partitionNum, resource.getVersion());
       }
     } catch (IOException e) {
-      // signal wal recover manager to recover this region's files
-      WALRecoverManager.getInstance()
-          .getAllDataRegionScannedLatch()
-          .countDownWithException(e.getMessage());
       throw new DataRegionException(e);
     }
 
@@ -3535,12 +3528,12 @@ public class DataRegion implements IDataRegionForQuery {
     List<TsFileResource> deletedByMods = new ArrayList<>();
     List<TsFileResource> deletedByFiles = new ArrayList<>();
     boolean isDropMeasurementExist = false;
-    IDPredicate.IDPredicateType idPredicateType = null;
+    TagPredicate.TagPredicateType tagPredicateType = null;
 
     if (deletion instanceof TableDeletionEntry) {
       TableDeletionEntry tableDeletionEntry = (TableDeletionEntry) deletion;
       isDropMeasurementExist = 
!tableDeletionEntry.getPredicate().getMeasurementNames().isEmpty();
-      idPredicateType = tableDeletionEntry.getPredicate().getIdPredicateType();
+      tagPredicateType = 
tableDeletionEntry.getPredicate().getTagPredicateType();
     }
 
     for (TsFileResource sealedTsFile : sealedTsFiles) {
@@ -3616,7 +3609,7 @@ public class DataRegion implements IDataRegionForQuery {
                   fileEndTime);
             }
             if (isFileFullyMatchedByTime(deletion, fileStartTime, fileEndTime)
-                && idPredicateType.equals(IDPredicate.IDPredicateType.NOP)
+                && tagPredicateType.equals(TagPredicate.TagPredicateType.NOP)
                 && !isDropMeasurementExist) {
               ++matchSize;
             } else {
@@ -5190,12 +5183,9 @@ public class DataRegion implements IDataRegionForQuery {
   private void acquireDirectBufferMemory() throws DataRegionException {
     long acquireDirectBufferMemCost = getAcquireDirectBufferMemCost();
     if 
(!SystemInfo.getInstance().addDirectBufferMemoryCost(acquireDirectBufferMemCost))
 {
-      throw new DataRegionException(
-          String.format(
-              StorageEngineMessages
-                  
.STORAGE_EXCEPTION_TOTAL_ALLOCATED_MEMORY_FOR_DIRECT_BUFFER_WILL_BE_S_WHICH_FD7DC149,
-              (SystemInfo.getInstance().getDirectBufferMemoryCost() + 
acquireDirectBufferMemCost),
-              SystemInfo.getInstance().getTotalDirectBufferMemorySizeLimit()));
+      throw new DirectBufferMemoryAllocationException(
+          SystemInfo.getInstance().getDirectBufferMemoryCost() + 
acquireDirectBufferMemCost,
+          SystemInfo.getInstance().getTotalDirectBufferMemorySizeLimit());
     }
     this.directBufferMemoryCost = acquireDirectBufferMemCost;
   }
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/compaction/execute/utils/CompactionUtils.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/compaction/execute/utils/CompactionUtils.java
index beb8647fdab..5798f8fd382 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/compaction/execute/utils/CompactionUtils.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/compaction/execute/utils/CompactionUtils.java
@@ -39,11 +39,11 @@ import org.apache.iotdb.db.service.metrics.FileMetrics;
 import 
org.apache.iotdb.db.storageengine.dataregion.compaction.constant.CompactionTaskType;
 import 
org.apache.iotdb.db.storageengine.dataregion.compaction.schedule.CompactionTaskManager;
 import 
org.apache.iotdb.db.storageengine.dataregion.modification.DeletionPredicate;
-import 
org.apache.iotdb.db.storageengine.dataregion.modification.IDPredicate.FullExactMatch;
 import org.apache.iotdb.db.storageengine.dataregion.modification.ModEntry;
 import 
org.apache.iotdb.db.storageengine.dataregion.modification.ModFileManagement;
 import 
org.apache.iotdb.db.storageengine.dataregion.modification.ModificationFile;
 import 
org.apache.iotdb.db.storageengine.dataregion.modification.TableDeletionEntry;
+import 
org.apache.iotdb.db.storageengine.dataregion.modification.TagPredicate.FullExactMatch;
 import 
org.apache.iotdb.db.storageengine.dataregion.modification.TreeDeletionEntry;
 import org.apache.iotdb.db.storageengine.dataregion.tsfile.TsFileResource;
 import 
org.apache.iotdb.db.storageengine.dataregion.tsfile.timeindex.ArrayDeviceTimeIndex;
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/modification/DeletionPredicate.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/modification/DeletionPredicate.java
index fcccaecc60f..b8722f6b414 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/modification/DeletionPredicate.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/modification/DeletionPredicate.java
@@ -18,7 +18,7 @@
  */
 package org.apache.iotdb.db.storageengine.dataregion.modification;
 
-import 
org.apache.iotdb.db.storageengine.dataregion.modification.IDPredicate.NOP;
+import 
org.apache.iotdb.db.storageengine.dataregion.modification.TagPredicate.NOP;
 import org.apache.iotdb.db.utils.io.BufferSerializable;
 import org.apache.iotdb.db.utils.io.StreamSerializable;
 
@@ -42,7 +42,7 @@ public class DeletionPredicate implements StreamSerializable, 
BufferSerializable
   public static final long SHALLOW_SIZE =
       RamUsageEstimator.shallowSizeOfInstance(DeletionPredicate.class);
   private String tableName;
-  private IDPredicate idPredicate = new NOP();
+  private TagPredicate tagPredicate = new NOP();
   // an empty list means affecting all columns
   private List<String> measurementNames = Collections.emptyList();
 
@@ -52,32 +52,32 @@ public class DeletionPredicate implements 
StreamSerializable, BufferSerializable
     this.tableName = tableName;
   }
 
-  public DeletionPredicate(String tableName, IDPredicate idPredicate) {
+  public DeletionPredicate(String tableName, TagPredicate tagPredicate) {
     this.tableName = tableName;
-    this.idPredicate = idPredicate;
+    this.tagPredicate = tagPredicate;
   }
 
   public DeletionPredicate(
-      String tableName, IDPredicate idPredicate, List<String> 
measurementNames) {
+      String tableName, TagPredicate tagPredicate, List<String> 
measurementNames) {
     this.tableName = tableName;
-    this.idPredicate = idPredicate;
+    this.tagPredicate = tagPredicate;
     this.measurementNames = measurementNames;
   }
 
   public boolean matches(IDeviceID deviceID) {
-    return tableName.equals(deviceID.getTableName()) && 
idPredicate.matches(deviceID);
+    return tableName.equals(deviceID.getTableName()) && 
tagPredicate.matches(deviceID);
   }
 
-  public void setIdPredicate(IDPredicate idPredicate) {
-    this.idPredicate = idPredicate;
+  public void setTagPredicate(TagPredicate tagPredicate) {
+    this.tagPredicate = tagPredicate;
   }
 
-  public IDPredicate getIdPredicate() {
-    return idPredicate;
+  public TagPredicate getTagPredicate() {
+    return tagPredicate;
   }
 
-  public IDPredicate.IDPredicateType getIdPredicateType() {
-    return this.idPredicate.type;
+  public TagPredicate.TagPredicateType getTagPredicateType() {
+    return this.tagPredicate.type;
   }
 
   public String getTableName() {
@@ -95,7 +95,7 @@ public class DeletionPredicate implements StreamSerializable, 
BufferSerializable
   @Override
   public long serialize(OutputStream stream) throws IOException {
     long size = ReadWriteIOUtils.writeVar(tableName, stream);
-    size += idPredicate.serialize(stream);
+    size += tagPredicate.serialize(stream);
     size += ReadWriteForEncodingUtils.writeVarInt(measurementNames.size(), 
stream);
     for (String measurementName : measurementNames) {
       size += ReadWriteIOUtils.writeVar(measurementName, stream);
@@ -106,7 +106,7 @@ public class DeletionPredicate implements 
StreamSerializable, BufferSerializable
   @Override
   public long serialize(ByteBuffer buffer) {
     long size = ReadWriteIOUtils.writeVar(tableName, buffer);
-    size += idPredicate.serialize(buffer);
+    size += tagPredicate.serialize(buffer);
     size += ReadWriteForEncodingUtils.writeVarInt(measurementNames.size(), 
buffer);
     for (String measurementName : measurementNames) {
       size += ReadWriteIOUtils.writeVar(measurementName, buffer);
@@ -117,7 +117,7 @@ public class DeletionPredicate implements 
StreamSerializable, BufferSerializable
   @Override
   public void deserialize(InputStream stream) throws IOException {
     tableName = ReadWriteIOUtils.readVarIntString(stream);
-    idPredicate = IDPredicate.createFrom(stream);
+    tagPredicate = TagPredicate.createFrom(stream);
 
     int measurementLength = ReadWriteForEncodingUtils.readVarInt(stream);
     if (measurementLength > 0) {
@@ -133,7 +133,7 @@ public class DeletionPredicate implements 
StreamSerializable, BufferSerializable
   @Override
   public void deserialize(ByteBuffer buffer) {
     tableName = ReadWriteIOUtils.readVarIntString(buffer);
-    idPredicate = IDPredicate.createFrom(buffer);
+    tagPredicate = TagPredicate.createFrom(buffer);
 
     int measurementLength = ReadWriteForEncodingUtils.readVarInt(buffer);
     if (measurementLength > 0) {
@@ -150,7 +150,7 @@ public class DeletionPredicate implements 
StreamSerializable, BufferSerializable
     // table name + id predicate + measurement names
     int size =
         ModEntry.sizeToWriteVarString(tableName)
-            + idPredicate.serializedSize()
+            + tagPredicate.serializedSize()
             + ReadWriteForEncodingUtils.varIntSize(measurementNames.size());
     for (String measurementName : measurementNames) {
       size += ModEntry.sizeToWriteVarString(measurementName);
@@ -168,13 +168,13 @@ public class DeletionPredicate implements 
StreamSerializable, BufferSerializable
     }
     DeletionPredicate that = (DeletionPredicate) o;
     return Objects.equals(tableName, that.tableName)
-        && Objects.equals(idPredicate, that.idPredicate)
+        && Objects.equals(tagPredicate, that.tagPredicate)
         && Objects.equals(measurementNames, that.measurementNames);
   }
 
   @Override
   public int hashCode() {
-    return Objects.hash(tableName, idPredicate, measurementNames);
+    return Objects.hash(tableName, tagPredicate, measurementNames);
   }
 
   @Override
@@ -183,8 +183,8 @@ public class DeletionPredicate implements 
StreamSerializable, BufferSerializable
         + "tableName='"
         + tableName
         + '\''
-        + ", idPredicate="
-        + idPredicate
+        + ", tagPredicate="
+        + tagPredicate
         + ", measurementNames="
         + measurementNames
         + '}';
@@ -194,7 +194,7 @@ public class DeletionPredicate implements 
StreamSerializable, BufferSerializable
   public long ramBytesUsed() {
     return SHALLOW_SIZE
         + RamUsageEstimator.sizeOf(tableName)
-        + RamUsageEstimator.sizeOfObject(idPredicate)
+        + RamUsageEstimator.sizeOfObject(tagPredicate)
         + RamUsageEstimator.sizeOfArrayList(measurementNames);
   }
 }
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/modification/TableDeletionEntry.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/modification/TableDeletionEntry.java
index 952bcc2eaf6..b2c7ccee4e5 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/modification/TableDeletionEntry.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/modification/TableDeletionEntry.java
@@ -21,7 +21,7 @@ package 
org.apache.iotdb.db.storageengine.dataregion.modification;
 import org.apache.iotdb.commons.conf.IoTDBConstant;
 import org.apache.iotdb.commons.path.PartialPath;
 import org.apache.iotdb.commons.queryengine.execution.MemoryEstimationHelper;
-import 
org.apache.iotdb.db.storageengine.dataregion.modification.IDPredicate.IDPredicateType;
+import 
org.apache.iotdb.db.storageengine.dataregion.modification.TagPredicate.TagPredicateType;
 import org.apache.iotdb.db.utils.ModificationUtils;
 
 import org.apache.tsfile.file.metadata.IDeviceID;
@@ -138,8 +138,8 @@ public class TableDeletionEntry extends ModEntry {
   }
 
   public boolean isDroppingTable() {
-    IDPredicate idPredicate = predicate.getIdPredicate();
-    return idPredicate.type == IDPredicateType.NOP
+    TagPredicate tagPredicate = predicate.getTagPredicate();
+    return tagPredicate.type == TagPredicateType.NOP
         && predicate.getMeasurementNames().isEmpty()
         && timeRange.getMin() == Long.MIN_VALUE
         && timeRange.getMax() == Long.MAX_VALUE;
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/modification/IDPredicate.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/modification/TagPredicate.java
similarity index 58%
rename from 
iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/modification/IDPredicate.java
rename to 
iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/modification/TagPredicate.java
index 487689d83b9..8d41d23ca06 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/modification/IDPredicate.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/modification/TagPredicate.java
@@ -35,11 +35,14 @@ import java.io.InputStream;
 import java.io.OutputStream;
 import java.nio.ByteBuffer;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Collections;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Objects;
+import java.util.Set;
 
-public abstract class IDPredicate implements StreamSerializable, 
BufferSerializable, Accountable {
+public abstract class TagPredicate implements StreamSerializable, 
BufferSerializable, Accountable {
 
   public int serializedSize() {
     // type
@@ -47,11 +50,13 @@ public abstract class IDPredicate implements 
StreamSerializable, BufferSerializa
   }
 
   @SuppressWarnings("java:S6548")
-  public enum IDPredicateType {
+  public enum TagPredicateType {
     NOP,
     FULL_EXACT_MATCH,
     SEGMENT_EXACT_MATCH,
-    AND;
+    AND,
+    DEVICE_IN,
+    SEGMENT_NOT_NULL;
 
     public long serialize(OutputStream stream) throws IOException {
       stream.write((byte) ordinal());
@@ -63,7 +68,7 @@ public abstract class IDPredicate implements 
StreamSerializable, BufferSerializa
       return 1;
     }
 
-    public static IDPredicateType deserialize(InputStream stream) throws 
IOException {
+    public static TagPredicateType deserialize(InputStream stream) throws 
IOException {
       int typeNum = stream.read();
       if (typeNum == -1) {
         throw new EOFException();
@@ -71,14 +76,14 @@ public abstract class IDPredicate implements 
StreamSerializable, BufferSerializa
       return values()[typeNum];
     }
 
-    public static IDPredicateType deserialize(ByteBuffer buffer) {
+    public static TagPredicateType deserialize(ByteBuffer buffer) {
       return values()[buffer.get()];
     }
   }
 
-  protected final IDPredicateType type;
+  protected final TagPredicateType type;
 
-  protected IDPredicate(IDPredicateType type) {
+  protected TagPredicate(TagPredicateType type) {
     this.type = type;
   }
 
@@ -94,17 +99,21 @@ public abstract class IDPredicate implements 
StreamSerializable, BufferSerializa
     return type.serialize(buffer);
   }
 
-  public static IDPredicate createFrom(ByteBuffer buffer) {
-    IDPredicateType type = IDPredicateType.deserialize(buffer);
-    IDPredicate predicate;
-    if (Objects.requireNonNull(type) == IDPredicateType.NOP) {
+  public static TagPredicate createFrom(ByteBuffer buffer) {
+    TagPredicateType type = TagPredicateType.deserialize(buffer);
+    TagPredicate predicate;
+    if (Objects.requireNonNull(type) == TagPredicateType.NOP) {
       predicate = new NOP();
-    } else if (Objects.requireNonNull(type) == 
IDPredicateType.FULL_EXACT_MATCH) {
+    } else if (Objects.requireNonNull(type) == 
TagPredicateType.FULL_EXACT_MATCH) {
       predicate = new FullExactMatch();
-    } else if (Objects.requireNonNull(type) == 
IDPredicateType.SEGMENT_EXACT_MATCH) {
+    } else if (Objects.requireNonNull(type) == 
TagPredicateType.SEGMENT_EXACT_MATCH) {
       predicate = new SegmentExactMatch();
-    } else if (Objects.requireNonNull(type) == IDPredicateType.AND) {
+    } else if (Objects.requireNonNull(type) == TagPredicateType.AND) {
       predicate = new And();
+    } else if (Objects.requireNonNull(type) == TagPredicateType.DEVICE_IN) {
+      predicate = new DeviceIn();
+    } else if (Objects.requireNonNull(type) == 
TagPredicateType.SEGMENT_NOT_NULL) {
+      predicate = new SegmentNotNull();
     } else {
       throw new 
IllegalArgumentException(StorageEngineMessages.UNRECOGNIZED_PREDICATE_TYPE + 
type);
     }
@@ -112,17 +121,21 @@ public abstract class IDPredicate implements 
StreamSerializable, BufferSerializa
     return predicate;
   }
 
-  public static IDPredicate createFrom(InputStream stream) throws IOException {
-    IDPredicateType type = IDPredicateType.deserialize(stream);
-    IDPredicate predicate;
-    if (Objects.requireNonNull(type) == IDPredicateType.NOP) {
+  public static TagPredicate createFrom(InputStream stream) throws IOException 
{
+    TagPredicateType type = TagPredicateType.deserialize(stream);
+    TagPredicate predicate;
+    if (Objects.requireNonNull(type) == TagPredicateType.NOP) {
       predicate = new NOP();
-    } else if (Objects.requireNonNull(type) == 
IDPredicateType.FULL_EXACT_MATCH) {
+    } else if (Objects.requireNonNull(type) == 
TagPredicateType.FULL_EXACT_MATCH) {
       predicate = new FullExactMatch();
-    } else if (Objects.requireNonNull(type) == 
IDPredicateType.SEGMENT_EXACT_MATCH) {
+    } else if (Objects.requireNonNull(type) == 
TagPredicateType.SEGMENT_EXACT_MATCH) {
       predicate = new SegmentExactMatch();
-    } else if (Objects.requireNonNull(type) == IDPredicateType.AND) {
+    } else if (Objects.requireNonNull(type) == TagPredicateType.AND) {
       predicate = new And();
+    } else if (Objects.requireNonNull(type) == TagPredicateType.DEVICE_IN) {
+      predicate = new DeviceIn();
+    } else if (Objects.requireNonNull(type) == 
TagPredicateType.SEGMENT_NOT_NULL) {
+      predicate = new SegmentNotNull();
     } else {
       throw new 
IllegalArgumentException(StorageEngineMessages.UNRECOGNIZED_PREDICATE_TYPE + 
type);
     }
@@ -130,11 +143,11 @@ public abstract class IDPredicate implements 
StreamSerializable, BufferSerializa
     return predicate;
   }
 
-  public static class NOP extends IDPredicate {
+  public static class NOP extends TagPredicate {
     public static final long SHALLOW_SIZE = 
RamUsageEstimator.shallowSizeOfInstance(NOP.class);
 
     public NOP() {
-      super(IDPredicateType.NOP);
+      super(TagPredicateType.NOP);
     }
 
     @Override
@@ -173,19 +186,19 @@ public abstract class IDPredicate implements 
StreamSerializable, BufferSerializa
     }
   }
 
-  public static class FullExactMatch extends IDPredicate {
+  public static class FullExactMatch extends TagPredicate {
 
     public static final long SHALLOW_SIZE =
         RamUsageEstimator.shallowSizeOfInstance(FullExactMatch.class);
     private IDeviceID deviceID;
 
     public FullExactMatch(IDeviceID deviceID) {
-      super(IDPredicateType.FULL_EXACT_MATCH);
+      super(TagPredicateType.FULL_EXACT_MATCH);
       this.deviceID = deviceID;
     }
 
     public FullExactMatch() {
-      super(IDPredicateType.FULL_EXACT_MATCH);
+      super(TagPredicateType.FULL_EXACT_MATCH);
     }
 
     @Override
@@ -250,7 +263,7 @@ public abstract class IDPredicate implements 
StreamSerializable, BufferSerializa
     }
   }
 
-  public static class SegmentExactMatch extends IDPredicate {
+  public static class SegmentExactMatch extends TagPredicate {
 
     public static final long SHALLOW_SIZE =
         RamUsageEstimator.shallowSizeOfInstance(SegmentExactMatch.class);
@@ -258,13 +271,13 @@ public abstract class IDPredicate implements 
StreamSerializable, BufferSerializa
     private int segmentIndex;
 
     public SegmentExactMatch(String pattern, int segmentIndex) {
-      super(IDPredicateType.SEGMENT_EXACT_MATCH);
+      super(TagPredicateType.SEGMENT_EXACT_MATCH);
       this.pattern = pattern;
       this.segmentIndex = segmentIndex;
     }
 
     public SegmentExactMatch() {
-      super(IDPredicateType.SEGMENT_EXACT_MATCH);
+      super(TagPredicateType.SEGMENT_EXACT_MATCH);
     }
 
     @Override
@@ -341,17 +354,187 @@ public abstract class IDPredicate implements 
StreamSerializable, BufferSerializa
     }
   }
 
-  public static class And extends IDPredicate {
+  public static class DeviceIn extends TagPredicate {
+
+    public static final long SHALLOW_SIZE = 
RamUsageEstimator.shallowSizeOfInstance(DeviceIn.class);
+    private final Set<IDeviceID> deviceIDs = new HashSet<>();
+
+    public DeviceIn(Collection<IDeviceID> deviceIDs) {
+      super(TagPredicateType.DEVICE_IN);
+      this.deviceIDs.addAll(deviceIDs);
+    }
+
+    public DeviceIn() {
+      super(TagPredicateType.DEVICE_IN);
+    }
+
+    @Override
+    public int serializedSize() {
+      int serializedSize = super.serializedSize();
+      serializedSize += ReadWriteForEncodingUtils.varIntSize(deviceIDs.size());
+      for (IDeviceID deviceID : deviceIDs) {
+        serializedSize += deviceID.serializedSize();
+      }
+      return serializedSize;
+    }
+
+    @Override
+    public long serialize(OutputStream stream) throws IOException {
+      long size = super.serialize(stream);
+      size += ReadWriteForEncodingUtils.writeVarInt(deviceIDs.size(), stream);
+      for (IDeviceID deviceID : deviceIDs) {
+        size += deviceID.serialize(stream);
+      }
+      return size;
+    }
+
+    @Override
+    public long serialize(ByteBuffer buffer) {
+      long size = super.serialize(buffer);
+      size += ReadWriteForEncodingUtils.writeVarInt(deviceIDs.size(), buffer);
+      for (IDeviceID deviceID : deviceIDs) {
+        size += deviceID.serialize(buffer);
+      }
+      return size;
+    }
+
+    @Override
+    public void deserialize(InputStream stream) throws IOException {
+      int size = ReadWriteForEncodingUtils.readVarInt(stream);
+      for (int i = 0; i < size; i++) {
+        
deviceIDs.add(Deserializer.DEFAULT_DESERIALIZER.deserializeFrom(stream));
+      }
+    }
+
+    @Override
+    public void deserialize(ByteBuffer buffer) {
+      int size = ReadWriteForEncodingUtils.readVarInt(buffer);
+      for (int i = 0; i < size; i++) {
+        
deviceIDs.add(Deserializer.DEFAULT_DESERIALIZER.deserializeFrom(buffer));
+      }
+    }
+
+    @Override
+    public boolean matches(IDeviceID deviceID) {
+      return deviceIDs.contains(deviceID);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+      if (this == o) {
+        return true;
+      }
+      if (o == null || getClass() != o.getClass()) {
+        return false;
+      }
+      DeviceIn deviceIn = (DeviceIn) o;
+      return Objects.equals(deviceIDs, deviceIn.deviceIDs);
+    }
+
+    @Override
+    public int hashCode() {
+      return Objects.hashCode(deviceIDs);
+    }
+
+    @Override
+    public String toString() {
+      return "DeviceIn{" + "deviceIDs=" + deviceIDs + '}';
+    }
+
+    @Override
+    public long ramBytesUsed() {
+      return SHALLOW_SIZE + RamUsageEstimator.sizeOfHashSet(deviceIDs);
+    }
+  }
+
+  public static class SegmentNotNull extends TagPredicate {
+
+    public static final long SHALLOW_SIZE =
+        RamUsageEstimator.shallowSizeOfInstance(SegmentNotNull.class);
+    private int segmentIndex;
+
+    public SegmentNotNull(int segmentIndex) {
+      super(TagPredicateType.SEGMENT_NOT_NULL);
+      this.segmentIndex = segmentIndex;
+    }
+
+    public SegmentNotNull() {
+      super(TagPredicateType.SEGMENT_NOT_NULL);
+    }
+
+    @Override
+    public int serializedSize() {
+      return super.serializedSize() + 
ReadWriteForEncodingUtils.varIntSize(segmentIndex);
+    }
+
+    @Override
+    public long serialize(OutputStream stream) throws IOException {
+      long size = super.serialize(stream);
+      size += ReadWriteForEncodingUtils.writeVarInt(segmentIndex, stream);
+      return size;
+    }
+
+    @Override
+    public long serialize(ByteBuffer buffer) {
+      long size = super.serialize(buffer);
+      size += ReadWriteForEncodingUtils.writeVarInt(segmentIndex, buffer);
+      return size;
+    }
+
+    @Override
+    public void deserialize(InputStream stream) throws IOException {
+      segmentIndex = ReadWriteForEncodingUtils.readVarInt(stream);
+    }
+
+    @Override
+    public void deserialize(ByteBuffer buffer) {
+      segmentIndex = ReadWriteForEncodingUtils.readVarInt(buffer);
+    }
+
+    @Override
+    public boolean matches(IDeviceID deviceID) {
+      return deviceID.segment(segmentIndex) != null;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+      if (this == o) {
+        return true;
+      }
+      if (o == null || getClass() != o.getClass()) {
+        return false;
+      }
+      SegmentNotNull that = (SegmentNotNull) o;
+      return segmentIndex == that.segmentIndex;
+    }
+
+    @Override
+    public int hashCode() {
+      return Objects.hashCode(segmentIndex);
+    }
+
+    @Override
+    public String toString() {
+      return "SegmentNotNull{" + "segmentIndex=" + segmentIndex + '}';
+    }
+
+    @Override
+    public long ramBytesUsed() {
+      return SHALLOW_SIZE;
+    }
+  }
+
+  public static class And extends TagPredicate {
 
     public static final long SHALLOW_SIZE = 
RamUsageEstimator.shallowSizeOfInstance(And.class);
-    private final List<IDPredicate> predicates = new ArrayList<>();
+    private final List<TagPredicate> predicates = new ArrayList<>();
 
-    public And(IDPredicate... predicates) {
-      super(IDPredicateType.AND);
+    public And(TagPredicate... predicates) {
+      super(TagPredicateType.AND);
       Collections.addAll(this.predicates, predicates);
     }
 
-    public void add(IDPredicate predicate) {
+    public void add(TagPredicate predicate) {
       predicates.add(predicate);
     }
 
@@ -359,7 +542,7 @@ public abstract class IDPredicate implements 
StreamSerializable, BufferSerializa
     public int serializedSize() {
       int serializedSize = super.serializedSize();
       serializedSize += 
ReadWriteForEncodingUtils.varIntSize(predicates.size());
-      for (IDPredicate predicate : predicates) {
+      for (TagPredicate predicate : predicates) {
         serializedSize += predicate.serializedSize();
       }
       return serializedSize;
@@ -369,7 +552,7 @@ public abstract class IDPredicate implements 
StreamSerializable, BufferSerializa
     public long serialize(OutputStream stream) throws IOException {
       long size = super.serialize(stream);
       size += ReadWriteForEncodingUtils.writeVarInt(predicates.size(), stream);
-      for (IDPredicate predicate : predicates) {
+      for (TagPredicate predicate : predicates) {
         size += predicate.serialize(stream);
       }
       return size;
@@ -379,7 +562,7 @@ public abstract class IDPredicate implements 
StreamSerializable, BufferSerializa
     public long serialize(ByteBuffer buffer) {
       long size = super.serialize(buffer);
       size += ReadWriteForEncodingUtils.writeVarInt(predicates.size(), buffer);
-      for (IDPredicate predicate : predicates) {
+      for (TagPredicate predicate : predicates) {
         size += predicate.serialize(buffer);
       }
       return size;
@@ -389,7 +572,7 @@ public abstract class IDPredicate implements 
StreamSerializable, BufferSerializa
     public void deserialize(InputStream stream) throws IOException {
       int size = ReadWriteForEncodingUtils.readVarInt(stream);
       for (int i = 0; i < size; i++) {
-        predicates.add(IDPredicate.createFrom(stream));
+        predicates.add(TagPredicate.createFrom(stream));
       }
     }
 
@@ -397,7 +580,7 @@ public abstract class IDPredicate implements 
StreamSerializable, BufferSerializa
     public void deserialize(ByteBuffer buffer) {
       int size = ReadWriteForEncodingUtils.readVarInt(buffer);
       for (int i = 0; i < size; i++) {
-        predicates.add(IDPredicate.createFrom(buffer));
+        predicates.add(TagPredicate.createFrom(buffer));
       }
     }
 
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/wal/recover/file/UnsealedTsFileRecoverPerformer.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/wal/recover/file/UnsealedTsFileRecoverPerformer.java
index d9a54490d69..f9696cc2617 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/wal/recover/file/UnsealedTsFileRecoverPerformer.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/wal/recover/file/UnsealedTsFileRecoverPerformer.java
@@ -35,9 +35,9 @@ import 
org.apache.iotdb.db.storageengine.dataregion.memtable.IMemTable;
 import org.apache.iotdb.db.storageengine.dataregion.memtable.IWritableMemChunk;
 import 
org.apache.iotdb.db.storageengine.dataregion.memtable.IWritableMemChunkGroup;
 import 
org.apache.iotdb.db.storageengine.dataregion.modification.DeletionPredicate;
-import 
org.apache.iotdb.db.storageengine.dataregion.modification.IDPredicate.FullExactMatch;
 import org.apache.iotdb.db.storageengine.dataregion.modification.ModEntry;
 import 
org.apache.iotdb.db.storageengine.dataregion.modification.TableDeletionEntry;
+import 
org.apache.iotdb.db.storageengine.dataregion.modification.TagPredicate.FullExactMatch;
 import 
org.apache.iotdb.db.storageengine.dataregion.modification.TreeDeletionEntry;
 import org.apache.iotdb.db.storageengine.dataregion.tsfile.TsFileResource;
 import 
org.apache.iotdb.db.storageengine.dataregion.tsfile.timeindex.FileTimeIndexCacheRecorder;
diff --git 
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/metadata/path/PatternTreeMapTest.java
 
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/metadata/path/PatternTreeMapTest.java
index 7a7d71bef51..6f14ac86384 100644
--- 
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/metadata/path/PatternTreeMapTest.java
+++ 
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/metadata/path/PatternTreeMapTest.java
@@ -24,9 +24,9 @@ import org.apache.iotdb.commons.path.MeasurementPath;
 import org.apache.iotdb.commons.path.PartialPath;
 import org.apache.iotdb.commons.path.PatternTreeMap;
 import 
org.apache.iotdb.db.storageengine.dataregion.modification.DeletionPredicate;
-import 
org.apache.iotdb.db.storageengine.dataregion.modification.IDPredicate.NOP;
 import org.apache.iotdb.db.storageengine.dataregion.modification.ModEntry;
 import 
org.apache.iotdb.db.storageengine.dataregion.modification.TableDeletionEntry;
+import 
org.apache.iotdb.db.storageengine.dataregion.modification.TagPredicate.NOP;
 import 
org.apache.iotdb.db.storageengine.dataregion.modification.TreeDeletionEntry;
 import org.apache.iotdb.db.utils.datastructure.PatternTreeMapFactory;
 import 
org.apache.iotdb.db.utils.datastructure.PatternTreeMapFactory.ModsSerializer;
diff --git 
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/pipe/consensus/DeletionRecoverTest.java
 
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/pipe/consensus/DeletionRecoverTest.java
index bcdc84a1fd0..be35ae8c881 100644
--- 
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/pipe/consensus/DeletionRecoverTest.java
+++ 
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/pipe/consensus/DeletionRecoverTest.java
@@ -30,8 +30,8 @@ import 
org.apache.iotdb.db.queryengine.plan.planner.plan.node.write.AbstractDele
 import 
org.apache.iotdb.db.queryengine.plan.planner.plan.node.write.DeleteDataNode;
 import 
org.apache.iotdb.db.queryengine.plan.planner.plan.node.write.RelationalDeleteDataNode;
 import 
org.apache.iotdb.db.storageengine.dataregion.modification.DeletionPredicate;
-import 
org.apache.iotdb.db.storageengine.dataregion.modification.IDPredicate.NOP;
 import 
org.apache.iotdb.db.storageengine.dataregion.modification.TableDeletionEntry;
+import 
org.apache.iotdb.db.storageengine.dataregion.modification.TagPredicate.NOP;
 
 import org.apache.tsfile.read.common.TimeRange;
 import org.junit.After;
diff --git 
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/pipe/consensus/DeletionResourceTest.java
 
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/pipe/consensus/DeletionResourceTest.java
index c64174720e1..4ef4d084718 100644
--- 
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/pipe/consensus/DeletionResourceTest.java
+++ 
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/pipe/consensus/DeletionResourceTest.java
@@ -43,8 +43,8 @@ import 
org.apache.iotdb.db.queryengine.plan.planner.plan.node.write.AbstractDele
 import 
org.apache.iotdb.db.queryengine.plan.planner.plan.node.write.DeleteDataNode;
 import 
org.apache.iotdb.db.queryengine.plan.planner.plan.node.write.RelationalDeleteDataNode;
 import 
org.apache.iotdb.db.storageengine.dataregion.modification.DeletionPredicate;
-import 
org.apache.iotdb.db.storageengine.dataregion.modification.IDPredicate.NOP;
 import 
org.apache.iotdb.db.storageengine.dataregion.modification.TableDeletionEntry;
+import 
org.apache.iotdb.db.storageengine.dataregion.modification.TagPredicate.NOP;
 import org.apache.iotdb.pipe.api.customizer.parameter.PipeParameters;
 
 import org.apache.tsfile.read.common.TimeRange;
diff --git 
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/pipe/source/PipePlanTablePatternParseVisitorTest.java
 
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/pipe/source/PipePlanTablePatternParseVisitorTest.java
index 0a7fbfb699d..a4da1fc2fdf 100644
--- 
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/pipe/source/PipePlanTablePatternParseVisitorTest.java
+++ 
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/pipe/source/PipePlanTablePatternParseVisitorTest.java
@@ -29,8 +29,8 @@ import 
org.apache.iotdb.db.queryengine.plan.relational.planner.node.schema.Creat
 import 
org.apache.iotdb.db.queryengine.plan.relational.planner.node.schema.TableDeviceAttributeUpdateNode;
 import org.apache.iotdb.db.storageengine.dataregion.memtable.DeviceIDFactory;
 import 
org.apache.iotdb.db.storageengine.dataregion.modification.DeletionPredicate;
-import org.apache.iotdb.db.storageengine.dataregion.modification.IDPredicate;
 import 
org.apache.iotdb.db.storageengine.dataregion.modification.TableDeletionEntry;
+import org.apache.iotdb.db.storageengine.dataregion.modification.TagPredicate;
 
 import org.apache.tsfile.read.common.TimeRange;
 import org.junit.Assert;
@@ -115,12 +115,12 @@ public class PipePlanTablePatternParseVisitorTest {
                         new TableDeletionEntry(
                             new DeletionPredicate(
                                 "ac",
-                                new IDPredicate.And(
-                                    new IDPredicate.FullExactMatch(
+                                new TagPredicate.And(
+                                    new TagPredicate.FullExactMatch(
                                         DeviceIDFactory.getInstance()
                                             .getDeviceID(
                                                 new PartialPath(new String[] 
{"ac", "device1"}))),
-                                    new 
IDPredicate.SegmentExactMatch("device2", 1))),
+                                    new 
TagPredicate.SegmentExactMatch("device2", 1))),
                             new TimeRange(0, 1))),
                     "db1"),
                 tablePattern)
diff --git 
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/analyze/AnalyzeUtilsTest.java
 
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/analyze/AnalyzeUtilsTest.java
index bbeaa629d4f..915006a8286 100644
--- 
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/analyze/AnalyzeUtilsTest.java
+++ 
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/analyze/AnalyzeUtilsTest.java
@@ -34,6 +34,8 @@ import 
org.apache.iotdb.commons.schema.table.column.TimeColumnSchema;
 import org.apache.iotdb.confignode.rpc.thrift.TGetRegionGroupsByTimeReq;
 import org.apache.iotdb.confignode.rpc.thrift.TGetRegionGroupsByTimeResp;
 import org.apache.iotdb.db.protocol.client.ConfigNodeClient;
+import org.apache.iotdb.db.queryengine.common.MPPQueryContext;
+import org.apache.iotdb.db.queryengine.common.QueryId;
 import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.Delete;
 import 
org.apache.iotdb.db.storageengine.dataregion.modification.DeletionPredicate;
 import 
org.apache.iotdb.db.storageengine.dataregion.modification.TableDeletionEntry;
@@ -66,7 +68,9 @@ public class AnalyzeUtilsTest {
             new Identifier("ts"),
             new LongLiteral("100"));
 
-    List<TableDeletionEntry> entries = 
AnalyzeUtils.parseExpressions2ModEntries(expression, table);
+    List<TableDeletionEntry> entries =
+        AnalyzeUtils.parseExpressions2ModEntries(
+            expression, table, "db", new MPPQueryContext(new QueryId("1")));
 
     assertEquals(1, entries.size());
     assertEquals(Long.MIN_VALUE, entries.get(0).getStartTime());
diff --git 
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/write/RelationalDeleteDataNodeTest.java
 
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/write/RelationalDeleteDataNodeTest.java
index e97c11c1b47..910c38f44eb 100644
--- 
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/write/RelationalDeleteDataNodeTest.java
+++ 
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/write/RelationalDeleteDataNodeTest.java
@@ -24,11 +24,11 @@ import 
org.apache.iotdb.commons.queryengine.plan.planner.plan.node.PlanNode;
 import org.apache.iotdb.commons.queryengine.plan.planner.plan.node.PlanNodeId;
 import 
org.apache.iotdb.commons.queryengine.plan.planner.plan.node.PlanNodeType;
 import 
org.apache.iotdb.db.storageengine.dataregion.modification.DeletionPredicate;
-import 
org.apache.iotdb.db.storageengine.dataregion.modification.IDPredicate.And;
-import 
org.apache.iotdb.db.storageengine.dataregion.modification.IDPredicate.FullExactMatch;
-import 
org.apache.iotdb.db.storageengine.dataregion.modification.IDPredicate.NOP;
-import 
org.apache.iotdb.db.storageengine.dataregion.modification.IDPredicate.SegmentExactMatch;
 import 
org.apache.iotdb.db.storageengine.dataregion.modification.TableDeletionEntry;
+import 
org.apache.iotdb.db.storageengine.dataregion.modification.TagPredicate.And;
+import 
org.apache.iotdb.db.storageengine.dataregion.modification.TagPredicate.FullExactMatch;
+import 
org.apache.iotdb.db.storageengine.dataregion.modification.TagPredicate.NOP;
+import 
org.apache.iotdb.db.storageengine.dataregion.modification.TagPredicate.SegmentExactMatch;
 import 
org.apache.iotdb.db.storageengine.dataregion.wal.utils.WALByteBufferForTest;
 
 import org.apache.tsfile.file.metadata.IDeviceID.Factory;
diff --git 
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/storageengine/StorageEngineTest.java
 
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/storageengine/StorageEngineTest.java
index 4c4ac20e9ae..b0b9ce2880a 100644
--- 
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/storageengine/StorageEngineTest.java
+++ 
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/storageengine/StorageEngineTest.java
@@ -18,9 +18,13 @@
  */
 package org.apache.iotdb.db.storageengine;
 
+import org.apache.iotdb.commons.concurrent.ExceptionalCountDownLatch;
 import org.apache.iotdb.commons.consensus.DataRegionId;
 import org.apache.iotdb.commons.utils.TimePartitionUtils;
+import org.apache.iotdb.db.exception.DataRegionException;
+import org.apache.iotdb.db.exception.DirectBufferMemoryAllocationException;
 import org.apache.iotdb.db.storageengine.dataregion.DataRegion;
+import 
org.apache.iotdb.db.storageengine.dataregion.wal.recover.WALRecoverManager;
 
 import com.google.common.collect.Lists;
 import org.junit.After;
@@ -84,4 +88,45 @@ public class StorageEngineTest {
     Assert.assertEquals(1, 
TimePartitionUtils.getTimePartitionId(timePartitionInterval * 2 - 1));
     Assert.assertEquals(2, 
TimePartitionUtils.getTimePartitionId(timePartitionInterval * 2 + 1));
   }
+
+  @Test
+  public void testNotifyWALRecoverManagerWhenDirectBufferAllocationFailed() 
throws Exception {
+    DirectBufferMemoryAllocationException 
directBufferMemoryAllocationException =
+        new DirectBufferMemoryAllocationException(2, 1);
+    WALRecoverManager.getInstance().setAllDataRegionScannedLatch(new 
ExceptionalCountDownLatch(1));
+    try {
+      try {
+        storageEngine.handleDataRegionRecoverFailure(
+            "root.sg", new DataRegionId(0), 
directBufferMemoryAllocationException);
+        Assert.fail("Expected data region recovery to fail.");
+      } catch (DataRegionException e) {
+        Assert.assertSame(directBufferMemoryAllocationException, e);
+      }
+
+      ExceptionalCountDownLatch latch =
+          WALRecoverManager.getInstance().getAllDataRegionScannedLatch();
+      Assert.assertTrue(latch.hasException());
+      Assert.assertEquals(
+          directBufferMemoryAllocationException.getMessage(), 
latch.getExceptionMessage());
+    } finally {
+      WALRecoverManager.getInstance().clear();
+    }
+  }
+
+  @Test
+  public void 
testNotifyWALRecoverManagerButContinueForOtherDataRegionException() throws 
Exception {
+    DataRegionException dataRegionException = new DataRegionException("other 
recovery failure");
+    WALRecoverManager.getInstance().setAllDataRegionScannedLatch(new 
ExceptionalCountDownLatch(1));
+    try {
+      storageEngine.handleDataRegionRecoverFailure(
+          "root.sg", new DataRegionId(0), dataRegionException);
+
+      ExceptionalCountDownLatch latch =
+          WALRecoverManager.getInstance().getAllDataRegionScannedLatch();
+      Assert.assertTrue(latch.hasException());
+      Assert.assertEquals(dataRegionException.getMessage(), 
latch.getExceptionMessage());
+    } finally {
+      WALRecoverManager.getInstance().clear();
+    }
+  }
 }
diff --git 
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/storageengine/dataregion/DataRegionTest.java
 
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/storageengine/dataregion/DataRegionTest.java
index 152189332b5..f2487eed6c9 100644
--- 
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/storageengine/dataregion/DataRegionTest.java
+++ 
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/storageengine/dataregion/DataRegionTest.java
@@ -68,9 +68,9 @@ import 
org.apache.iotdb.db.storageengine.dataregion.flush.TsFileFlushPolicy;
 import org.apache.iotdb.db.storageengine.dataregion.memtable.ReadOnlyMemChunk;
 import org.apache.iotdb.db.storageengine.dataregion.memtable.TsFileProcessor;
 import 
org.apache.iotdb.db.storageengine.dataregion.modification.DeletionPredicate;
-import 
org.apache.iotdb.db.storageengine.dataregion.modification.IDPredicate.NOP;
 import 
org.apache.iotdb.db.storageengine.dataregion.modification.ModificationFile;
 import 
org.apache.iotdb.db.storageengine.dataregion.modification.TableDeletionEntry;
+import 
org.apache.iotdb.db.storageengine.dataregion.modification.TagPredicate.NOP;
 import org.apache.iotdb.db.storageengine.dataregion.read.QueryDataSource;
 import org.apache.iotdb.db.storageengine.dataregion.tsfile.TsFileResource;
 import 
org.apache.iotdb.db.storageengine.dataregion.tsfile.TsFileResourceStatus;
diff --git 
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/storageengine/dataregion/compaction/tablemodel/CompactionWithAllNullRowsTest.java
 
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/storageengine/dataregion/compaction/tablemodel/CompactionWithAllNullRowsTest.java
index 9ff8a401150..69e83e769a3 100644
--- 
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/storageengine/dataregion/compaction/tablemodel/CompactionWithAllNullRowsTest.java
+++ 
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/storageengine/dataregion/compaction/tablemodel/CompactionWithAllNullRowsTest.java
@@ -35,9 +35,9 @@ import 
org.apache.iotdb.db.storageengine.dataregion.compaction.execute.performer
 import 
org.apache.iotdb.db.storageengine.dataregion.compaction.execute.performer.impl.ReadPointCompactionPerformer;
 import 
org.apache.iotdb.db.storageengine.dataregion.compaction.execute.task.InnerSpaceCompactionTask;
 import 
org.apache.iotdb.db.storageengine.dataregion.modification.DeletionPredicate;
-import org.apache.iotdb.db.storageengine.dataregion.modification.IDPredicate;
-import 
org.apache.iotdb.db.storageengine.dataregion.modification.IDPredicate.FullExactMatch;
 import 
org.apache.iotdb.db.storageengine.dataregion.modification.TableDeletionEntry;
+import org.apache.iotdb.db.storageengine.dataregion.modification.TagPredicate;
+import 
org.apache.iotdb.db.storageengine.dataregion.modification.TagPredicate.FullExactMatch;
 import org.apache.iotdb.db.storageengine.dataregion.tsfile.TsFileResource;
 
 import org.apache.tsfile.exception.write.WriteProcessException;
@@ -333,7 +333,7 @@ public class CompactionWithAllNullRowsTest extends 
AbstractCompactionTest {
             new TableDeletionEntry(
                 new DeletionPredicate(
                     "t1",
-                    new IDPredicate.FullExactMatch(deviceID),
+                    new TagPredicate.FullExactMatch(deviceID),
                     Collections.singletonList("s0")),
                 new TimeRange(Long.MIN_VALUE, 11)));
     resource1
@@ -342,7 +342,7 @@ public class CompactionWithAllNullRowsTest extends 
AbstractCompactionTest {
             new TableDeletionEntry(
                 new DeletionPredicate(
                     "t1",
-                    new IDPredicate.FullExactMatch(deviceID),
+                    new TagPredicate.FullExactMatch(deviceID),
                     Collections.singletonList("s1")),
                 new TimeRange(Long.MIN_VALUE, 11)));
     resource1
@@ -351,7 +351,7 @@ public class CompactionWithAllNullRowsTest extends 
AbstractCompactionTest {
             new TableDeletionEntry(
                 new DeletionPredicate(
                     "t1",
-                    new IDPredicate.FullExactMatch(deviceID),
+                    new TagPredicate.FullExactMatch(deviceID),
                     Collections.singletonList("s2")),
                 new TimeRange(Long.MIN_VALUE, 11)));
     resource1
@@ -360,7 +360,7 @@ public class CompactionWithAllNullRowsTest extends 
AbstractCompactionTest {
             new TableDeletionEntry(
                 new DeletionPredicate(
                     "t1",
-                    new IDPredicate.FullExactMatch(deviceID),
+                    new TagPredicate.FullExactMatch(deviceID),
                     Collections.singletonList("s3")),
                 new TimeRange(Long.MIN_VALUE, 11)));
     resource1.getModFileForWrite().close();
diff --git 
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/storageengine/dataregion/modification/ModificationFileTest.java
 
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/storageengine/dataregion/modification/ModificationFileTest.java
index fb4ce06fc33..58fe5e90245 100644
--- 
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/storageengine/dataregion/modification/ModificationFileTest.java
+++ 
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/storageengine/dataregion/modification/ModificationFileTest.java
@@ -23,9 +23,10 @@ import 
org.apache.iotdb.commons.exception.IllegalPathException;
 import org.apache.iotdb.commons.path.MeasurementPath;
 import org.apache.iotdb.db.service.metrics.FileMetrics;
 import 
org.apache.iotdb.db.storageengine.dataregion.compaction.execute.recover.CompactionRecoverManager;
-import 
org.apache.iotdb.db.storageengine.dataregion.modification.IDPredicate.FullExactMatch;
-import 
org.apache.iotdb.db.storageengine.dataregion.modification.IDPredicate.NOP;
-import 
org.apache.iotdb.db.storageengine.dataregion.modification.IDPredicate.SegmentExactMatch;
+import 
org.apache.iotdb.db.storageengine.dataregion.modification.TagPredicate.DeviceIn;
+import 
org.apache.iotdb.db.storageengine.dataregion.modification.TagPredicate.FullExactMatch;
+import 
org.apache.iotdb.db.storageengine.dataregion.modification.TagPredicate.NOP;
+import 
org.apache.iotdb.db.storageengine.dataregion.modification.TagPredicate.SegmentExactMatch;
 import org.apache.iotdb.db.utils.constant.TestConstant;
 
 import org.apache.tsfile.file.metadata.IDeviceID.Factory;
@@ -131,6 +132,14 @@ public class ModificationFileTest {
                   new FullExactMatch(Factory.DEFAULT_FACTORY.create(new 
String[] {"id1", "id2"}))),
               new TimeRange(5, 6)),
           new TableDeletionEntry(new DeletionPredicate("table4"), new 
TimeRange(7, 8)),
+          new TableDeletionEntry(
+              new DeletionPredicate(
+                  "table5",
+                  new DeviceIn(
+                      Arrays.asList(
+                          Factory.DEFAULT_FACTORY.create(new String[] 
{"table5", "id1", "id2"}),
+                          Factory.DEFAULT_FACTORY.create(new String[] 
{"table5", "id3", "id4"})))),
+              new TimeRange(9, 10)),
         };
     try (ModificationFile mFile = new ModificationFile(tempFileName, false)) {
       for (int i = 0; i < 4; i++) {
@@ -141,11 +150,11 @@ public class ModificationFileTest {
         assertEquals(modifications[i], modificationList.get(i));
       }
 
-      for (int i = 4; i < 8; i++) {
+      for (int i = 4; i < modifications.length; i++) {
         mFile.write(modifications[i]);
       }
       modificationList = mFile.getAllMods();
-      for (int i = 0; i < 8; i++) {
+      for (int i = 0; i < modifications.length; i++) {
         assertEquals(modifications[i], modificationList.get(i));
       }
     } catch (IOException e) {
@@ -172,6 +181,14 @@ public class ModificationFileTest {
                   new FullExactMatch(Factory.DEFAULT_FACTORY.create(new 
String[] {"id1", "id2"}))),
               new TimeRange(5, 6)),
           new TableDeletionEntry(new DeletionPredicate("table4"), new 
TimeRange(7, 8)),
+          new TableDeletionEntry(
+              new DeletionPredicate(
+                  "table5",
+                  new DeviceIn(
+                      Arrays.asList(
+                          Factory.DEFAULT_FACTORY.create(new String[] 
{"table5", "id1", "id2"}),
+                          Factory.DEFAULT_FACTORY.create(new String[] 
{"table5", "id3", "id4"})))),
+              new TimeRange(9, 10)),
         };
     try (ModificationFile mFile = new ModificationFile(tempFileName, false)) {
       mFile.write(Arrays.asList(modifications));
diff --git 
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/storageengine/dataregion/modification/TableDeletionEntryTest.java
 
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/storageengine/dataregion/modification/TableDeletionEntryTest.java
index f4e90957945..99a889e2e5f 100644
--- 
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/storageengine/dataregion/modification/TableDeletionEntryTest.java
+++ 
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/storageengine/dataregion/modification/TableDeletionEntryTest.java
@@ -18,10 +18,11 @@
  */
 package org.apache.iotdb.db.storageengine.dataregion.modification;
 
-import 
org.apache.iotdb.db.storageengine.dataregion.modification.IDPredicate.And;
-import 
org.apache.iotdb.db.storageengine.dataregion.modification.IDPredicate.FullExactMatch;
-import 
org.apache.iotdb.db.storageengine.dataregion.modification.IDPredicate.NOP;
-import 
org.apache.iotdb.db.storageengine.dataregion.modification.IDPredicate.SegmentExactMatch;
+import 
org.apache.iotdb.db.storageengine.dataregion.modification.TagPredicate.And;
+import 
org.apache.iotdb.db.storageengine.dataregion.modification.TagPredicate.DeviceIn;
+import 
org.apache.iotdb.db.storageengine.dataregion.modification.TagPredicate.FullExactMatch;
+import 
org.apache.iotdb.db.storageengine.dataregion.modification.TagPredicate.NOP;
+import 
org.apache.iotdb.db.storageengine.dataregion.modification.TagPredicate.SegmentExactMatch;
 
 import org.apache.tsfile.file.metadata.IDeviceID;
 import org.apache.tsfile.file.metadata.IDeviceID.Factory;
@@ -69,7 +70,28 @@ public class TableDeletionEntryTest {
   public void testDeserializePredicateTypeFromEmptyStream() {
     assertThrows(
         EOFException.class,
-        () -> IDPredicate.IDPredicateType.deserialize(new 
ByteArrayInputStream(new byte[0])));
+        () -> TagPredicate.TagPredicateType.deserialize(new 
ByteArrayInputStream(new byte[0])));
+  }
+
+  @Test
+  public void testDeviceInSerialization() throws IOException {
+    DeviceIn predicate =
+        new DeviceIn(
+            Arrays.asList(
+                Factory.DEFAULT_FACTORY.create(new String[] {"table1", "id1", 
"id2"}),
+                Factory.DEFAULT_FACTORY.create(new String[] {"table1", "id3", 
"id4"})));
+
+    ByteBuffer buffer = ByteBuffer.allocate(predicate.serializedSize());
+    predicate.serialize(buffer);
+    assertEquals(predicate.serializedSize(), buffer.position());
+    buffer.flip();
+    assertEquals(predicate, TagPredicate.createFrom(buffer));
+
+    ByteArrayOutputStream bos = new ByteArrayOutputStream();
+    predicate.serialize(bos);
+    byte[] byteArray = bos.toByteArray();
+    assertEquals(predicate.serializedSize(), byteArray.length);
+    assertEquals(predicate, TagPredicate.createFrom(new 
ByteArrayInputStream(byteArray)));
   }
 
   @Test
@@ -92,6 +114,15 @@ public class TableDeletionEntryTest {
                 "table1",
                 new And(new SegmentExactMatch("id1", 1), new 
SegmentExactMatch("id2", 2))),
             new TimeRange(1, 5));
+    TableDeletionEntry entry5 =
+        new TableDeletionEntry(
+            new DeletionPredicate(
+                "table1",
+                new DeviceIn(
+                    Arrays.asList(
+                        Factory.DEFAULT_FACTORY.create(new String[] {"table1", 
"id1", "id2"}),
+                        Factory.DEFAULT_FACTORY.create(new String[] {"table1", 
"id1"})))),
+            new TimeRange(1, 5));
 
     IDeviceID deviceID1 = Factory.DEFAULT_FACTORY.create(new String[] 
{"table1", "id1", "id2"});
     IDeviceID deviceID2 =
@@ -128,6 +159,13 @@ public class TableDeletionEntryTest {
     assertFalse(entry4.affects(deviceID4));
     assertFalse(entry4.affects(deviceID5));
     assertFalse(entry4.affects(deviceID6));
+
+    assertTrue(entry5.affects(deviceID1));
+    assertFalse(entry5.affects(deviceID2));
+    assertFalse(entry5.affects(deviceID3));
+    assertTrue(entry5.affects(deviceID4));
+    assertFalse(entry5.affects(deviceID5));
+    assertFalse(entry5.affects(deviceID6));
   }
 
   @Test

Reply via email to