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 8b0abccd89a [Subscription] Exclude audit database from subscriptions 
(#18451)
8b0abccd89a is described below

commit 8b0abccd89adef633004e5d86e1920352cb4d9c2
Author: Caideyipi <[email protected]>
AuthorDate: Thu Aug 13 15:15:19 2026 +0800

    [Subscription] Exclude audit database from subscriptions (#18451)
    
    * [Subscription] Exclude audit database from subscriptions
    
    * [Subscription] Reject audit-only topic filters
---
 .../iotdb/confignode/i18n/ConfigNodeMessages.java  |   3 +
 .../iotdb/confignode/i18n/ConfigNodeMessages.java  |   3 +
 .../persistence/subscription/SubscriptionInfo.java |  79 ++++++++++++++++
 .../SubscriptionInfoTopicValidationTest.java       | 100 +++++++++++++++++++++
 .../dataregion/DataRegionListeningFilter.java      |   9 ++
 .../ConsensusSubscriptionSetupHandler.java         |   9 +-
 .../dataregion/DataRegionListeningFilterTest.java  |  50 +++++++++++
 .../ConsensusSubscriptionSetupHandlerTest.java     |  25 ++++++
 .../apache/iotdb/commons/schema/table/Audit.java   |  13 +++
 .../iotdb/commons/schema/table/AuditTest.java      |  41 +++++++++
 10 files changed, 329 insertions(+), 3 deletions(-)

diff --git 
a/iotdb-core/confignode/src/main/i18n/en/org/apache/iotdb/confignode/i18n/ConfigNodeMessages.java
 
b/iotdb-core/confignode/src/main/i18n/en/org/apache/iotdb/confignode/i18n/ConfigNodeMessages.java
index 5e79dffbffc..5ac20660610 100644
--- 
a/iotdb-core/confignode/src/main/i18n/en/org/apache/iotdb/confignode/i18n/ConfigNodeMessages.java
+++ 
b/iotdb-core/confignode/src/main/i18n/en/org/apache/iotdb/confignode/i18n/ConfigNodeMessages.java
@@ -672,4 +672,7 @@ public final class ConfigNodeMessages {
   public static final String
       
EXCEPTION_FAILED_TO_CREATE_OR_ALTER_TOPIC_ARG_AND_ARG_ARE_ONLY_SUPPORTED_FOR_INCREMENTAL_TOPICS_D86CEA8E
 =
           "Failed to create or alter topic, %s and %s are only supported for 
incremental topics";
+  public static final String
+      
EXCEPTION_FAILED_TO_CREATE_OR_ALTER_TOPIC_SUBSCRIBING_ONLY_TO_THE_AUDIT_DATABASE_OR_PATHS_UNDER_IT_IS_NOT_ALLOWED_3E96A6BA
 =
+          "Failed to create or alter topic, subscribing only to the __audit 
database or paths under it is not allowed";
 }
diff --git 
a/iotdb-core/confignode/src/main/i18n/zh/org/apache/iotdb/confignode/i18n/ConfigNodeMessages.java
 
b/iotdb-core/confignode/src/main/i18n/zh/org/apache/iotdb/confignode/i18n/ConfigNodeMessages.java
index 5f9a0d8d9ab..9519406be3c 100644
--- 
a/iotdb-core/confignode/src/main/i18n/zh/org/apache/iotdb/confignode/i18n/ConfigNodeMessages.java
+++ 
b/iotdb-core/confignode/src/main/i18n/zh/org/apache/iotdb/confignode/i18n/ConfigNodeMessages.java
@@ -715,4 +715,7 @@ public final class ConfigNodeMessages {
   public static final String
       
EXCEPTION_FAILED_TO_CREATE_OR_ALTER_TOPIC_ARG_AND_ARG_ARE_ONLY_SUPPORTED_FOR_INCREMENTAL_TOPICS_D86CEA8E
 =
           "创建或修改 topic 失败,%s 和 %s 仅支持 incremental 模式的 topic";
+  public static final String
+      
EXCEPTION_FAILED_TO_CREATE_OR_ALTER_TOPIC_SUBSCRIBING_ONLY_TO_THE_AUDIT_DATABASE_OR_PATHS_UNDER_IT_IS_NOT_ALLOWED_3E96A6BA
 =
+          "创建或修改 topic 失败,不允许仅订阅 __audit 数据库或其下的路径";
 }
diff --git 
a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/persistence/subscription/SubscriptionInfo.java
 
b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/persistence/subscription/SubscriptionInfo.java
index 6e26f20c0d8..36b6f35c31d 100644
--- 
a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/persistence/subscription/SubscriptionInfo.java
+++ 
b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/persistence/subscription/SubscriptionInfo.java
@@ -20,7 +20,11 @@
 package org.apache.iotdb.confignode.persistence.subscription;
 
 import org.apache.iotdb.common.rpc.thrift.TSStatus;
+import org.apache.iotdb.commons.path.PartialPath;
+import org.apache.iotdb.commons.pipe.config.constant.PipeSourceConstant;
 import org.apache.iotdb.commons.pipe.config.constant.SystemConstant;
+import org.apache.iotdb.commons.pipe.datastructure.pattern.TablePattern;
+import org.apache.iotdb.commons.pipe.datastructure.pattern.TreePattern;
 import org.apache.iotdb.commons.snapshot.SnapshotProcessor;
 import org.apache.iotdb.commons.subscription.config.SubscriptionConfig;
 import 
org.apache.iotdb.commons.subscription.meta.consumer.CommitProgressKeeper;
@@ -51,6 +55,7 @@ import org.apache.iotdb.confignode.rpc.thrift.TUnsubscribeReq;
 import org.apache.iotdb.consensus.ConsensusFactory;
 import org.apache.iotdb.consensus.common.DataSet;
 import org.apache.iotdb.mpp.rpc.thrift.TTopicOwnerLeaseEntry;
+import org.apache.iotdb.pipe.api.exception.PipeException;
 import org.apache.iotdb.rpc.TSStatusCode;
 import org.apache.iotdb.rpc.subscription.config.TopicConfig;
 import org.apache.iotdb.rpc.subscription.config.TopicConstant;
@@ -67,6 +72,7 @@ import java.io.FileOutputStream;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Locale;
@@ -80,6 +86,9 @@ import java.util.function.Predicate;
 import java.util.stream.Collectors;
 import java.util.stream.StreamSupport;
 
+import static 
org.apache.iotdb.commons.schema.table.Audit.TABLE_MODEL_AUDIT_DATABASE;
+import static org.apache.iotdb.commons.schema.table.Audit.includeByAuditTreeDB;
+
 public class SubscriptionInfo implements SnapshotProcessor {
 
   private static final Logger LOGGER = 
LoggerFactory.getLogger(SubscriptionInfo.class);
@@ -341,6 +350,7 @@ public class SubscriptionInfo implements SnapshotProcessor {
 
   private void validateTopicConfig(final TopicConfig topicConfig) throws 
SubscriptionException {
     validateDuplicateTopicAttributes(topicConfig);
+    validateTopicDoesNotOnlySelectAuditDatabase(topicConfig);
 
     final String mode = topicConfig.getMode();
     if (!TopicConfig.isValidMode(mode)) {
@@ -403,6 +413,75 @@ public class SubscriptionInfo implements SnapshotProcessor 
{
     }
   }
 
+  private void validateTopicDoesNotOnlySelectAuditDatabase(final TopicConfig 
topicConfig)
+      throws SubscriptionException {
+    final boolean onlySelectsAuditDatabase =
+        topicConfig.isTableTopic()
+            ? onlySelectsAuditTableDatabase(topicConfig)
+            : onlySelectsAuditTreePaths(topicConfig);
+
+    if (onlySelectsAuditDatabase) {
+      final String exceptionMessage =
+          ConfigNodeMessages
+              
.EXCEPTION_FAILED_TO_CREATE_OR_ALTER_TOPIC_SUBSCRIBING_ONLY_TO_THE_AUDIT_DATABASE_OR_PATHS_UNDER_IT_IS_NOT_ALLOWED_3E96A6BA;
+      LOGGER.warn(exceptionMessage);
+      throw new SubscriptionException(exceptionMessage);
+    }
+  }
+
+  private static boolean onlySelectsAuditTableDatabase(final TopicConfig 
topicConfig) {
+    final Map<String, String> sourceAttributes =
+        new 
HashMap<>(topicConfig.getAttributesWithSourceDatabaseAndTableName());
+    sourceAttributes.putAll(topicConfig.getAttributesWithSourcePrefix());
+
+    try {
+      return TABLE_MODEL_AUDIT_DATABASE.equalsIgnoreCase(
+          TablePattern.parsePipePatternFromSourceParameters(new 
TopicConfig(sourceAttributes))
+              .getDatabasePattern());
+    } catch (final PipeException ignored) {
+      // Invalid patterns are reported by the existing Pipe source validation 
path.
+      return false;
+    }
+  }
+
+  private static boolean isAuditTreePath(final PartialPath path) {
+    return includeByAuditTreeDB(path);
+  }
+
+  private static boolean onlySelectsAuditTreePaths(final TopicConfig 
topicConfig) {
+    final Map<String, String> sourceAttributes =
+        new HashMap<>(topicConfig.getAttributesWithSourcePathOrPattern());
+    sourceAttributes.putAll(topicConfig.getAttributesWithSourcePrefix());
+
+    try {
+      final TreePattern treePattern =
+          TreePattern.parsePipePatternFromSourceParameters(new 
TopicConfig(sourceAttributes));
+      try {
+        return onlySelectsAuditTreePaths(treePattern);
+      } catch (final UnsupportedOperationException ignored) {
+        // Exclusions can only narrow the selected paths. Inspect the 
inclusion paths when the
+        // effective pattern cannot expose a finite base path list.
+        
sourceAttributes.keySet().removeIf(SubscriptionInfo::isTreeExclusionAttribute);
+        return onlySelectsAuditTreePaths(
+            TreePattern.parsePipePatternFromSourceParameters(new 
TopicConfig(sourceAttributes)));
+      }
+    } catch (final PipeException ignored) {
+      // Invalid patterns are reported by the existing Pipe source validation 
path.
+      return false;
+    }
+  }
+
+  private static boolean onlySelectsAuditTreePaths(final TreePattern 
treePattern) {
+    final List<PartialPath> inclusionPaths = 
treePattern.getBaseInclusionPaths();
+    return !inclusionPaths.isEmpty()
+        && inclusionPaths.stream().allMatch(SubscriptionInfo::isAuditTreePath);
+  }
+
+  private static boolean isTreeExclusionAttribute(final String key) {
+    return 
PipeSourceConstant.SOURCE_PATTERN_EXCLUSION_KEY.equalsIgnoreCase(key)
+        || PipeSourceConstant.SOURCE_PATH_EXCLUSION_KEY.equalsIgnoreCase(key);
+  }
+
   private void validateIncrementalTopicAttributes(final TopicConfig 
topicConfig)
       throws SubscriptionException {
     if (!topicConfig.isIncrementalMode()) {
diff --git 
a/iotdb-core/confignode/src/test/java/org/apache/iotdb/confignode/persistence/subscription/SubscriptionInfoTopicValidationTest.java
 
b/iotdb-core/confignode/src/test/java/org/apache/iotdb/confignode/persistence/subscription/SubscriptionInfoTopicValidationTest.java
index bda9cf81d7f..48523171345 100644
--- 
a/iotdb-core/confignode/src/test/java/org/apache/iotdb/confignode/persistence/subscription/SubscriptionInfoTopicValidationTest.java
+++ 
b/iotdb-core/confignode/src/test/java/org/apache/iotdb/confignode/persistence/subscription/SubscriptionInfoTopicValidationTest.java
@@ -19,6 +19,7 @@
 
 package org.apache.iotdb.confignode.persistence.subscription;
 
+import org.apache.iotdb.commons.pipe.config.constant.PipeSourceConstant;
 import org.apache.iotdb.commons.pipe.config.constant.SystemConstant;
 import org.apache.iotdb.commons.subscription.config.SubscriptionConfig;
 import org.apache.iotdb.commons.subscription.meta.topic.TopicMeta;
@@ -85,6 +86,92 @@ public class SubscriptionInfoTopicValidationTest {
     assertCreateRejected(subscriptionInfo, attributes, "only supported for 
table topics");
   }
 
+  @Test
+  public void testRejectTopicThatOnlySelectsAuditDatabase() {
+    final SubscriptionInfo subscriptionInfo = new SubscriptionInfo();
+
+    final Map<String, String> tableAttributes = 
newInitialTableTopicAttributes();
+    tableAttributes.put(TopicConstant.DATABASE_KEY, "__audit");
+    assertCreateRejected(subscriptionInfo, tableAttributes, "only to the 
__audit database");
+
+    tableAttributes.put(TopicConstant.DATABASE_KEY, "__AUDIT");
+    assertCreateRejected(subscriptionInfo, tableAttributes, "only to the 
__audit database");
+
+    tableAttributes.put(TopicConstant.DATABASE_KEY, "__audit_data");
+    assertCreateAccepted(subscriptionInfo, "table_topic", tableAttributes);
+
+    tableAttributes.put(TopicConstant.DATABASE_KEY, "__audit|user_db");
+    assertCreateAccepted(subscriptionInfo, "table_regex_topic", 
tableAttributes);
+
+    tableAttributes.put(TopicConstant.DATABASE_KEY, "__audit");
+    tableAttributes.put(PipeSourceConstant.SOURCE_DATABASE_NAME_KEY, 
"user_db");
+    assertCreateAccepted(subscriptionInfo, "table_source_override_topic", 
tableAttributes);
+
+    tableAttributes.put(TopicConstant.DATABASE_KEY, "user_db");
+    tableAttributes.put(PipeSourceConstant.SOURCE_DATABASE_NAME_KEY, 
"__audit");
+    assertCreateRejected(subscriptionInfo, tableAttributes, "only to the 
__audit database");
+
+    final Map<String, String> treeAttributes = new HashMap<>();
+    treeAttributes.put(TopicConstant.PATH_KEY, "root.__audit.**");
+    assertCreateRejected(subscriptionInfo, treeAttributes, "only to the 
__audit database");
+
+    treeAttributes.put(TopicConstant.PATH_KEY, "root.__audit.log.device");
+    assertCreateRejected(subscriptionInfo, treeAttributes, "only to the 
__audit database");
+
+    treeAttributes.put(TopicConstant.PATH_KEY, 
"root.__audit.log.**,root.__audit.user.**");
+    assertCreateRejected(subscriptionInfo, treeAttributes, "only to the 
__audit database");
+
+    treeAttributes.put(TopicConstant.PATH_KEY, "root.__audit.**,root.user.**");
+    assertCreateAccepted(subscriptionInfo, "tree_topic", treeAttributes);
+
+    treeAttributes.put(TopicConstant.PATH_KEY, "root.__audit_data.**");
+    assertCreateAccepted(subscriptionInfo, "tree_similar_name_topic", 
treeAttributes);
+
+    treeAttributes.remove(TopicConstant.PATH_KEY);
+    treeAttributes.put(TopicConstant.PATTERN_KEY, "root.__audit.log");
+    assertCreateRejected(subscriptionInfo, treeAttributes, "only to the 
__audit database");
+
+    treeAttributes.put(TopicConstant.PATTERN_KEY, "root.__audit");
+    assertCreateAccepted(subscriptionInfo, "tree_prefix_topic", 
treeAttributes);
+  }
+
+  @Test
+  public void testAuditOnlyValidationHandlesSourceInclusionAndExclusion() {
+    final SubscriptionInfo subscriptionInfo = new SubscriptionInfo();
+    final Map<String, String> attributes = new HashMap<>();
+    attributes.put(TopicConstant.PATH_KEY, "root.__audit.**");
+    attributes.put("source.path.exclusion", "root.__audit.internal.**");
+    assertCreateRejected(subscriptionInfo, attributes, "only to the __audit 
database");
+
+    attributes.put("source.path.inclusion", "root.user.**");
+    assertCreateAccepted(subscriptionInfo, "tree_source_inclusion_topic", 
attributes);
+
+    final Map<String, String> attributesWithFullyExcludedUserPath = new 
HashMap<>();
+    attributesWithFullyExcludedUserPath.put(TopicConstant.PATH_KEY, 
"root.__audit.**,root.user.**");
+    attributesWithFullyExcludedUserPath.put("source.path.exclusion", 
"root.user.**");
+    assertCreateRejected(
+        subscriptionInfo, attributesWithFullyExcludedUserPath, "only to the 
__audit database");
+  }
+
+  @Test
+  public void testRejectAlteringTopicToOnlySelectAuditDatabase() throws 
Exception {
+    final SubscriptionInfo subscriptionInfo = new SubscriptionInfo();
+    final Map<String, String> originalAttributes = new HashMap<>();
+    originalAttributes.put(TopicConstant.PATH_KEY, "root.user.**");
+    subscriptionInfo.createTopic(
+        new CreateTopicPlan(new TopicMeta("tree_topic", 1L, 
originalAttributes)));
+
+    final Map<String, String> updatedAttributes = new HashMap<>();
+    updatedAttributes.put(TopicConstant.PATH_KEY, "root.__audit.log.**");
+    try {
+      subscriptionInfo.validateBeforeAlteringTopic(
+          new TopicMeta("tree_topic", 2L, updatedAttributes));
+      Assert.fail("Expected audit-only topic validation to fail");
+    } catch (final SubscriptionException e) {
+      Assert.assertTrue(e.getMessage().contains("only to the __audit 
database"));
+    }
+  }
+
   @Test
   public void testRejectDuplicateTopicConfigKeys() {
     final SubscriptionInfo subscriptionInfo = new SubscriptionInfo();
@@ -353,4 +440,17 @@ public class SubscriptionInfoTopicValidationTest {
       Assert.assertTrue(e.getMessage().contains(expectedMessagePart));
     }
   }
+
+  private static void assertCreateAccepted(
+      final SubscriptionInfo subscriptionInfo,
+      final String topicName,
+      final Map<String, String> attributes) {
+    try {
+      Assert.assertTrue(
+          subscriptionInfo.validateBeforeCreatingTopic(
+              new TCreateTopicReq(topicName).setTopicAttributes(attributes)));
+    } catch (final SubscriptionException e) {
+      Assert.fail(e.getMessage());
+    }
+  }
 }
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/source/dataregion/DataRegionListeningFilter.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/source/dataregion/DataRegionListeningFilter.java
index e2a15a64480..ad4c1fddff3 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/source/dataregion/DataRegionListeningFilter.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/source/dataregion/DataRegionListeningFilter.java
@@ -38,6 +38,7 @@ import java.util.stream.Collectors;
 import static 
org.apache.iotdb.commons.pipe.datastructure.options.PipeInclusionOptions.getExclusionString;
 import static 
org.apache.iotdb.commons.pipe.datastructure.options.PipeInclusionOptions.getInclusionString;
 import static 
org.apache.iotdb.commons.pipe.datastructure.options.PipeInclusionOptions.parseOptions;
+import static org.apache.iotdb.commons.schema.table.Audit.isAuditDatabase;
 
 /**
  * {@link DataRegionListeningFilter} is to tell the insertion and deletion for 
{@link PipeTask} on
@@ -59,6 +60,10 @@ public class DataRegionListeningFilter {
   public static boolean shouldDatabaseBeListened(
       final PipeParameters parameters, final boolean isTableModel, final 
String databaseRawName)
       throws IllegalPathException {
+    if (isAuditDatabase(databaseRawName)) {
+      return false;
+    }
+
     final Pair<Boolean, Boolean> insertionDeletionListeningOptionPair =
         parseInsertionDeletionListeningOptionPair(parameters);
     final boolean hasSpecificListeningOption =
@@ -101,6 +106,10 @@ public class DataRegionListeningFilter {
     }
 
     final String databaseRawName = dataRegion.getDatabaseName();
+    if (isAuditDatabase(databaseRawName)) {
+      return false;
+    }
+
     final String databaseTreeModel =
         databaseRawName.startsWith("root.") ? databaseRawName : "root." + 
databaseRawName;
     final String databaseTableModel =
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/broker/consensus/ConsensusSubscriptionSetupHandler.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/broker/consensus/ConsensusSubscriptionSetupHandler.java
index bb512922248..3fea7020e72 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/broker/consensus/ConsensusSubscriptionSetupHandler.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/broker/consensus/ConsensusSubscriptionSetupHandler.java
@@ -58,6 +58,8 @@ import java.util.concurrent.ConcurrentHashMap;
 import java.util.function.Consumer;
 import java.util.function.Predicate;
 
+import static org.apache.iotdb.commons.schema.table.Audit.isAuditDatabase;
+
 /**
  * Handles setup and teardown of consensus-based subscription queues on 
DataNode.
  *
@@ -645,10 +647,11 @@ public class ConsensusSubscriptionSetupHandler {
     return new ConsensusLogToTabletConverter(treePattern, tablePattern, null, 
actualDatabaseName);
   }
 
-  private static boolean matchesTopicDatabase(
+  static boolean matchesTopicDatabase(
       final TopicConfig topicConfig, final String actualDatabaseName) {
-    return !topicConfig.isTableTopic()
-        || buildTablePattern(topicConfig).matchesDatabase(actualDatabaseName);
+    return !isAuditDatabase(actualDatabaseName)
+        && (!topicConfig.isTableTopic()
+            || 
buildTablePattern(topicConfig).matchesDatabase(actualDatabaseName));
   }
 
   private static TablePattern buildTablePattern(final TopicConfig topicConfig) 
{
diff --git 
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/pipe/source/dataregion/DataRegionListeningFilterTest.java
 
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/pipe/source/dataregion/DataRegionListeningFilterTest.java
new file mode 100644
index 00000000000..4901afcf912
--- /dev/null
+++ 
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/pipe/source/dataregion/DataRegionListeningFilterTest.java
@@ -0,0 +1,50 @@
+/*
+ * 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.pipe.source.dataregion;
+
+import org.apache.iotdb.commons.pipe.config.constant.SystemConstant;
+import org.apache.iotdb.commons.subscription.meta.topic.TopicMeta;
+import org.apache.iotdb.pipe.api.customizer.parameter.PipeParameters;
+
+import org.junit.Test;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+public class DataRegionListeningFilterTest {
+
+  @Test
+  public void testAuditDatabaseIsNeverListened() throws Exception {
+    final Map<String, String> topicAttributes = new HashMap<>();
+    topicAttributes.put(SystemConstant.SQL_DIALECT_KEY, 
SystemConstant.SQL_DIALECT_TABLE_VALUE);
+    final PipeParameters parameters =
+        new PipeParameters(
+            new TopicMeta("topic", 1, 
topicAttributes).generateExtractorAttributes("root"));
+
+    assertFalse(DataRegionListeningFilter.shouldDatabaseBeListened(parameters, 
true, "__audit"));
+    assertFalse(
+        DataRegionListeningFilter.shouldDatabaseBeListened(parameters, false, 
"root.__audit"));
+    assertFalse(DataRegionListeningFilter.shouldDatabaseBeListened(parameters, 
true, "__AUDIT"));
+    assertTrue(DataRegionListeningFilter.shouldDatabaseBeListened(parameters, 
true, "user_db"));
+  }
+}
diff --git 
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/subscription/broker/consensus/ConsensusSubscriptionSetupHandlerTest.java
 
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/subscription/broker/consensus/ConsensusSubscriptionSetupHandlerTest.java
index bd31d67a790..97ee9df9d3c 100644
--- 
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/subscription/broker/consensus/ConsensusSubscriptionSetupHandlerTest.java
+++ 
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/subscription/broker/consensus/ConsensusSubscriptionSetupHandlerTest.java
@@ -20,7 +20,10 @@
 package org.apache.iotdb.db.subscription.broker.consensus;
 
 import org.apache.iotdb.commons.consensus.DataRegionId;
+import org.apache.iotdb.commons.pipe.config.constant.SystemConstant;
 import org.apache.iotdb.db.conf.IoTDBDescriptor;
+import org.apache.iotdb.rpc.subscription.config.TopicConfig;
+import org.apache.iotdb.rpc.subscription.config.TopicConstant;
 import org.apache.iotdb.rpc.subscription.exception.SubscriptionException;
 
 import org.junit.Rule;
@@ -30,11 +33,14 @@ import org.junit.rules.TemporaryFolder;
 import java.io.File;
 import java.util.Arrays;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.LinkedHashSet;
+import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.atomic.AtomicInteger;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
@@ -131,6 +137,25 @@ public class ConsensusSubscriptionSetupHandlerTest {
     }
   }
 
+  @Test
+  public void testAuditDatabaseNeverMatchesTopic() {
+    final Map<String, String> tableTopicAttributes = new HashMap<>();
+    tableTopicAttributes.put(
+        SystemConstant.SQL_DIALECT_KEY, 
SystemConstant.SQL_DIALECT_TABLE_VALUE);
+    tableTopicAttributes.put(TopicConstant.DATABASE_KEY, ".*");
+    final TopicConfig tableTopicConfig = new TopicConfig(tableTopicAttributes);
+
+    assertFalse(
+        
ConsensusSubscriptionSetupHandler.matchesTopicDatabase(tableTopicConfig, 
"__audit"));
+    assertFalse(
+        
ConsensusSubscriptionSetupHandler.matchesTopicDatabase(tableTopicConfig, 
"root.__audit"));
+    
assertTrue(ConsensusSubscriptionSetupHandler.matchesTopicDatabase(tableTopicConfig,
 "user_db"));
+
+    final TopicConfig treeTopicConfig = new 
TopicConfig(Collections.emptyMap());
+    
assertFalse(ConsensusSubscriptionSetupHandler.matchesTopicDatabase(treeTopicConfig,
 "__audit"));
+    
assertTrue(ConsensusSubscriptionSetupHandler.matchesTopicDatabase(treeTopicConfig,
 "user_db"));
+  }
+
   private static void failOnSecondTopic(
       final String topicName, final Set<String> attemptedTopicNames) {
     attemptedTopicNames.add(topicName);
diff --git 
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/schema/table/Audit.java
 
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/schema/table/Audit.java
index e9d8f7fe1f4..a33cda1b5dc 100644
--- 
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/schema/table/Audit.java
+++ 
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/schema/table/Audit.java
@@ -52,6 +52,19 @@ public class Audit {
         && TABLE_MODEL_AUDIT_DATABASE.equalsIgnoreCase(nodes[1]);
   }
 
+  public static boolean isAuditDatabase(final String databaseName) {
+    return isDatabaseOrDescendant(databaseName, TABLE_MODEL_AUDIT_DATABASE)
+        || isDatabaseOrDescendant(databaseName, TREE_MODEL_AUDIT_DATABASE);
+  }
+
+  private static boolean isDatabaseOrDescendant(
+      final String databaseName, final String auditDatabaseName) {
+    return databaseName != null
+        && databaseName.regionMatches(true, 0, auditDatabaseName, 0, 
auditDatabaseName.length())
+        && (databaseName.length() == auditDatabaseName.length()
+            || databaseName.charAt(auditDatabaseName.length()) == '.');
+  }
+
   public static String getReservedDatabaseNameErrorMsg(String databaseName) {
     return String.format(RESERVED_DATABASE_NAME_ERROR_MSG, databaseName);
   }
diff --git 
a/iotdb-core/node-commons/src/test/java/org/apache/iotdb/commons/schema/table/AuditTest.java
 
b/iotdb-core/node-commons/src/test/java/org/apache/iotdb/commons/schema/table/AuditTest.java
new file mode 100644
index 00000000000..ad66cd0f284
--- /dev/null
+++ 
b/iotdb-core/node-commons/src/test/java/org/apache/iotdb/commons/schema/table/AuditTest.java
@@ -0,0 +1,41 @@
+/*
+ * 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.commons.schema.table;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+public class AuditTest {
+
+  @Test
+  public void testIsAuditDatabase() {
+    assertTrue(Audit.isAuditDatabase("__audit"));
+    assertTrue(Audit.isAuditDatabase("root.__audit"));
+    assertTrue(Audit.isAuditDatabase("__AUDIT"));
+    assertTrue(Audit.isAuditDatabase("ROOT.__AUDIT"));
+    assertTrue(Audit.isAuditDatabase("__audit.data"));
+    assertTrue(Audit.isAuditDatabase("root.__audit.data"));
+    assertFalse(Audit.isAuditDatabase("__audit_data"));
+    assertFalse(Audit.isAuditDatabase("root.__audit_data"));
+    assertFalse(Audit.isAuditDatabase(null));
+  }
+}

Reply via email to