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

Caideyipi pushed a commit to branch fix-delete-dnf-limit
in repository https://gitbox.apache.org/repos/asf/iotdb.git

commit 0dd9b6d60d324e12c1b402b32af7011c62d4101c
Author: Caideyipi <[email protected]>
AuthorDate: Wed Jul 8 12:10:01 2026 +0800

    Limit delete predicate DNF expansion
---
 .../iotdb/db/i18n/DataNodeQueryMessages.java       |  4 +++
 .../iotdb/db/i18n/DataNodeQueryMessages.java       |  4 +++
 .../db/queryengine/plan/analyze/AnalyzeUtils.java  | 40 +++++++++++++++++-----
 .../queryengine/plan/analyze/AnalyzeUtilsTest.java | 36 +++++++++++++++++++
 4 files changed, 76 insertions(+), 8 deletions(-)

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 7978fce5113..4ea569ad799 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
@@ -335,6 +335,10 @@ public final class DataNodeQueryMessages {
   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
+      
EXCEPTION_DELETION_PREDICATE_EXPANDS_TO_TOO_MANY_DISJUNCTIVE_TERMS_ARG_LIMIT_IS_ARG_PLEASE_SIMPLIFY_THE_OR_CONDITIONS_BA138588
 =
+          "Deletion predicate expands to too many disjunctive terms (%d), 
limit is %d. "
+              + "Please simplify the OR conditions.";
   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 =
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 34a7fa084bf..adcfc5b252a 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
@@ -319,6 +319,10 @@ public final class DataNodeQueryMessages {
       "列 '%s' 不存在或不是标签列";
   public static final String 
TOO_MANY_DEVICES_MATCHED_BY_ATTRIBUTE_FILTERS_IN_DELETION =
       "删除语句中的属性过滤条件匹配了过多设备(%d),限制为 %d。请移除所有属性过滤条件(%s),或进一步增加属性过滤条件。";
+  public static final String
+      
EXCEPTION_DELETION_PREDICATE_EXPANDS_TO_TOO_MANY_DISJUNCTIVE_TERMS_ARG_LIMIT_IS_ARG_PLEASE_SIMPLIFY_THE_OR_CONDITIONS_BA138588
 =
+          "删除谓词会展开为过多析取项(%d),限制为 %d。"
+              + "请简化 OR 条件。";
   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 =
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 81849ff9dfb..65fbc06b214 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
@@ -96,6 +96,7 @@ 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 int DELETE_DNF_EXPRESSION_LIMIT = 1000;
   private static final CommonQueryAstVisitor<PredicateParseResult, 
PredicateParseContext>
       DELETION_PREDICATE_PARSE_VISITOR =
           new CommonQueryAstVisitor<>() {
@@ -455,14 +456,18 @@ public class AnalyzeUtils {
   }
 
   /**
-   * Convert to a disjunctive normal forms.
+   * Convert to disjunctive normal forms.
    *
    * <p>For example: ( A | B ) & ( C | D ) => ( A & C ) | ( A & D ) | ( B & C 
) | ( B & D)
    *
-   * <p>Returns the original expression if the expression is null or if the 
distribution will expand
-   * the expression by too much.
+   * <p>Throws SemanticException if the distribution will expand the 
expression by too much.
    */
   public static List<Expression> toDisjunctiveNormalForms(Expression 
expression) {
+    return toDisjunctiveNormalForms(expression, DELETE_DNF_EXPRESSION_LIMIT);
+  }
+
+  private static List<Expression> toDisjunctiveNormalForms(
+      Expression expression, int expressionLimit) {
     if (!(expression instanceof LogicalExpression)) {
       return Collections.singletonList(expression);
     }
@@ -472,20 +477,24 @@ public class AnalyzeUtils {
       // ( A | B ) & ( C | D ) => ( A & C ) | ( A & D ) | ( B & C ) | ( B & D)
       List<Expression> results = null;
       for (Expression term : logicalExpression.getTerms()) {
+        List<Expression> termResults = toDisjunctiveNormalForms(term, 
expressionLimit);
         if (results == null) {
-          results = toDisjunctiveNormalForms(term);
+          results = termResults;
         } else {
           results =
               crossProductOfDisjunctiveNormalForms(
-                  results, toDisjunctiveNormalForms(term), Operator.AND);
+                  results, termResults, Operator.AND, expressionLimit);
         }
+        ensureDnfExpressionLimit(results.size(), expressionLimit);
       }
       return results;
     } else if (logicalExpression.getOperator() == Operator.OR) {
       // ( A | B ) | ( C | D ) => A | B | C | D
       List<Expression> results = new ArrayList<>();
       for (Expression term : logicalExpression.getTerms()) {
-        results.addAll(toDisjunctiveNormalForms(term));
+        List<Expression> termResults = toDisjunctiveNormalForms(term, 
expressionLimit);
+        ensureDnfExpressionLimit((long) results.size() + termResults.size(), 
expressionLimit);
+        results.addAll(termResults);
       }
       return results;
     } else {
@@ -495,8 +504,12 @@ public class AnalyzeUtils {
   }
 
   private static List<Expression> crossProductOfDisjunctiveNormalForms(
-      List<Expression> leftList, List<Expression> rightList, Operator 
operator) {
-    List<Expression> results = new ArrayList<>();
+      List<Expression> leftList,
+      List<Expression> rightList,
+      Operator operator,
+      int expressionLimit) {
+    ensureDnfExpressionLimit((long) leftList.size() * rightList.size(), 
expressionLimit);
+    List<Expression> results = new ArrayList<>(leftList.size() * 
rightList.size());
     for (Expression leftExp : leftList) {
       for (Expression rightExp : rightList) {
         List<Expression> terms = new ArrayList<>();
@@ -518,6 +531,17 @@ public class AnalyzeUtils {
     return results;
   }
 
+  private static void ensureDnfExpressionLimit(final long expressionSize, 
final int limit) {
+    if (expressionSize > limit) {
+      throw new SemanticException(
+          String.format(
+              DataNodeQueryMessages
+                  
.EXCEPTION_DELETION_PREDICATE_EXPANDS_TO_TOO_MANY_DISJUNCTIVE_TERMS_ARG_LIMIT_IS_ARG_PLEASE_SIMPLIFY_THE_OR_CONDITIONS_BA138588,
+              expressionSize,
+              limit));
+    }
+  }
+
   private static TableDeletionEntry parsePredicate(
       Expression expression,
       TsTable table,
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 915006a8286..8ecf2b49787 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
@@ -23,11 +23,14 @@ import org.apache.iotdb.common.rpc.thrift.TConsensusGroupId;
 import org.apache.iotdb.common.rpc.thrift.TConsensusGroupType;
 import org.apache.iotdb.common.rpc.thrift.TRegionReplicaSet;
 import org.apache.iotdb.common.rpc.thrift.TSStatus;
+import org.apache.iotdb.commons.exception.SemanticException;
 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.LogicalExpression;
 import 
org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.LongLiteral;
 import 
org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.QualifiedName;
+import 
org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.StringLiteral;
 import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Table;
 import org.apache.iotdb.commons.schema.table.TsTable;
 import org.apache.iotdb.commons.schema.table.column.TimeColumnSchema;
@@ -47,6 +50,7 @@ import org.junit.Test;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mockito;
 
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashSet;
@@ -54,6 +58,7 @@ import java.util.List;
 import java.util.Set;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThrows;
 import static org.junit.Assert.assertTrue;
 
 public class AnalyzeUtilsTest {
@@ -77,6 +82,24 @@ public class AnalyzeUtilsTest {
     assertEquals(100, entries.get(0).getEndTime());
   }
 
+  @Test
+  public void testDeletePredicateDnfExpansionWithinLimit() {
+    List<Expression> result =
+        AnalyzeUtils.toDisjunctiveNormalForms(buildConjunctiveOrExpression(9));
+
+    assertEquals(512, result.size());
+  }
+
+  @Test
+  public void testDeletePredicateDnfExpansionRejectsTooManyTerms() {
+    SemanticException exception =
+        assertThrows(
+            SemanticException.class,
+            () -> 
AnalyzeUtils.toDisjunctiveNormalForms(buildConjunctiveOrExpression(10)));
+
+    assertTrue(exception.getMessage().contains("too many disjunctive terms 
(1024)"));
+  }
+
   @Test
   public void testFetchDeleteReplicaSetsOnlyQueriesTargetDatabaseRegions() 
throws Exception {
     final Delete delete = new Delete(new Table(QualifiedName.of("table1")));
@@ -118,6 +141,19 @@ public class AnalyzeUtilsTest {
     assertEquals(40, requests.get(1).getEndTime());
   }
 
+  private static Expression buildConjunctiveOrExpression(final int termCount) {
+    final List<Expression> terms = new ArrayList<>();
+    for (int i = 0; i < termCount; i++) {
+      terms.add(LogicalExpression.or(equalTag("tag" + i, "left"), 
equalTag("tag" + i, "right")));
+    }
+    return new LogicalExpression(LogicalExpression.Operator.AND, terms);
+  }
+
+  private static Expression equalTag(final String column, final String value) {
+    return new ComparisonExpression(
+        ComparisonExpression.Operator.EQUAL, new Identifier(column), new 
StringLiteral(value));
+  }
+
   private static TGetRegionGroupsByTimeResp successRegionGroupsResp(
       final Set<TRegionReplicaSet> replicaSets) {
     final TGetRegionGroupsByTimeResp resp =

Reply via email to