This is an automated email from the ASF dual-hosted git repository.
jiangtian pushed a commit to branch dev/1.3
in repository https://gitbox.apache.org/repos/asf/iotdb.git
The following commit(s) were added to refs/heads/dev/1.3 by this push:
new 63a7ae5dae7 [To dev/1.3] Pipe: support pattern pruning and redundancy
removal in TreePattern parsing (#17059)
63a7ae5dae7 is described below
commit 63a7ae5dae784417d02d7ccba7232fc54a4f8611
Author: VGalaxies <[email protected]>
AuthorDate: Fri Jan 23 09:40:48 2026 +0800
[To dev/1.3] Pipe: support pattern pruning and redundancy removal in
TreePattern parsing (#17059)
---
.../db/pipe/pattern/PipePatternPruningTest.java | 156 +++++++++
.../pipe/datastructure/pattern/PipePattern.java | 377 +++++++++++++++++----
.../pattern/UnionIoTDBPipePattern.java | 6 +
.../pattern/WithExclusionIoTDBPipePattern.java | 2 -
.../pattern/WithExclusionPipePattern.java | 2 -
5 files changed, 472 insertions(+), 71 deletions(-)
diff --git
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/pipe/pattern/PipePatternPruningTest.java
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/pipe/pattern/PipePatternPruningTest.java
new file mode 100644
index 00000000000..4b0c5b1ef2f
--- /dev/null
+++
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/pipe/pattern/PipePatternPruningTest.java
@@ -0,0 +1,156 @@
+/*
+ * 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.pattern;
+
+import org.apache.iotdb.commons.pipe.config.constant.PipeSourceConstant;
+import org.apache.iotdb.commons.pipe.datastructure.pattern.IoTDBPipePattern;
+import org.apache.iotdb.commons.pipe.datastructure.pattern.PipePattern;
+import org.apache.iotdb.commons.pipe.datastructure.pattern.PrefixPipePattern;
+import
org.apache.iotdb.commons.pipe.datastructure.pattern.UnionIoTDBPipePattern;
+import org.apache.iotdb.pipe.api.customizer.parameter.PipeParameters;
+import org.apache.iotdb.pipe.api.exception.PipeException;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.util.HashMap;
+
+public class PipePatternPruningTest {
+
+ @Test
+ public void testUnionInternalPruning_Cover() {
+ final PipeParameters params =
+ new PipeParameters(
+ new HashMap<String, String>() {
+ {
+ put(PipeSourceConstant.SOURCE_PATH_KEY,
"root.db.d1.*,root.db.d1.s1");
+ }
+ });
+
+ final PipePattern result =
PipePattern.parsePipePatternFromSourceParameters(params);
+
+ Assert.assertTrue("Should be IoTDBPipePattern", result instanceof
IoTDBPipePattern);
+ Assert.assertEquals("root.db.d1.*", result.getPattern());
+ }
+
+ @Test
+ public void testUnionInternalPruning_Duplicates() {
+ final PipeParameters params =
+ new PipeParameters(
+ new HashMap<String, String>() {
+ {
+ put(PipeSourceConstant.SOURCE_PATH_KEY,
"root.db.d1,root.db.d1");
+ }
+ });
+
+ final PipePattern result =
PipePattern.parsePipePatternFromSourceParameters(params);
+
+ Assert.assertTrue(result instanceof IoTDBPipePattern);
+ Assert.assertEquals("root.db.d1", result.getPattern());
+ }
+
+ @Test
+ public void testInclusionPrunedByExclusion_Partial() {
+ final PipeParameters params =
+ new PipeParameters(
+ new HashMap<String, String>() {
+ {
+ put(PipeSourceConstant.SOURCE_PATH_KEY,
"root.sg.d1,root.sg.d2");
+ put(PipeSourceConstant.SOURCE_PATH_EXCLUSION_KEY,
"root.sg.d1");
+ }
+ });
+
+ final PipePattern result =
PipePattern.parsePipePatternFromSourceParameters(params);
+
+ Assert.assertTrue(result instanceof IoTDBPipePattern);
+ Assert.assertEquals("root.sg.d2", result.getPattern());
+ }
+
+ @Test
+ public void testInclusionPrunedByExclusion_FullCoverage_Exception() {
+ final PipeParameters params =
+ new PipeParameters(
+ new HashMap<String, String>() {
+ {
+ put(PipeSourceConstant.SOURCE_PATH_KEY, "root.sg.d1");
+ put(PipeSourceConstant.SOURCE_PATH_EXCLUSION_KEY,
"root.sg.**");
+ }
+ });
+
+ try {
+ PipePattern.parsePipePatternFromSourceParameters(params);
+ Assert.fail("Should throw PipeException because Exclusion fully covers
Inclusion");
+ } catch (final PipeException ignored) {
+ // Expected exception
+ }
+ }
+
+ @Test
+ public void testComplexPruning() {
+ final PipeParameters params =
+ new PipeParameters(
+ new HashMap<String, String>() {
+ {
+ put(PipeSourceConstant.SOURCE_PATH_KEY,
"root.sg.A,root.sg.B,root.sg.A.sub");
+ put(PipeSourceConstant.SOURCE_PATH_EXCLUSION_KEY,
"root.sg.A,root.sg.A.**");
+ }
+ });
+
+ final PipePattern result =
PipePattern.parsePipePatternFromSourceParameters(params);
+
+ Assert.assertTrue(result instanceof IoTDBPipePattern);
+ Assert.assertEquals("root.sg.B", result.getPattern());
+ }
+
+ @Test
+ public void testComplexPruning_Prefix() {
+ final PipeParameters params =
+ new PipeParameters(
+ new HashMap<String, String>() {
+ {
+ put(PipeSourceConstant.SOURCE_PATTERN_KEY,
"root.sg.A,root.sg.B,root.sg.A.sub");
+ put(PipeSourceConstant.SOURCE_PATTERN_EXCLUSION_KEY,
"root.sg.A");
+ put(PipeSourceConstant.SOURCE_PATTERN_FORMAT_KEY, "prefix");
+ }
+ });
+
+ final PipePattern result =
PipePattern.parsePipePatternFromSourceParameters(params);
+
+ Assert.assertTrue(result instanceof PrefixPipePattern);
+ Assert.assertEquals("root.sg.B", result.getPattern());
+ }
+
+ @Test
+ public void testUnionPreservedWhenNotCovered() {
+ final PipeParameters params =
+ new PipeParameters(
+ new HashMap<String, String>() {
+ {
+ put(PipeSourceConstant.SOURCE_PATH_KEY,
"root.sg.d1,root.sg.d2");
+ put(PipeSourceConstant.SOURCE_PATH_EXCLUSION_KEY,
"root.other");
+ }
+ });
+
+ final PipePattern result =
PipePattern.parsePipePatternFromSourceParametersInternal(params);
+
+ Assert.assertTrue(result instanceof UnionIoTDBPipePattern);
+ Assert.assertEquals("root.sg.d1,root.sg.d2", result.getPattern());
+ }
+}
diff --git
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/pipe/datastructure/pattern/PipePattern.java
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/pipe/datastructure/pattern/PipePattern.java
index 24973ab7bfa..a76306aab81 100644
---
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/pipe/datastructure/pattern/PipePattern.java
+++
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/pipe/datastructure/pattern/PipePattern.java
@@ -19,7 +19,9 @@
package org.apache.iotdb.commons.pipe.datastructure.pattern;
+import org.apache.iotdb.commons.conf.IoTDBConstant;
import org.apache.iotdb.commons.path.PartialPath;
+import org.apache.iotdb.commons.utils.TestOnly;
import org.apache.iotdb.pipe.api.customizer.parameter.PipeParameters;
import org.apache.iotdb.pipe.api.exception.PipeException;
@@ -29,7 +31,9 @@ import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import java.util.Objects;
import java.util.function.Function;
import java.util.stream.Collectors;
@@ -142,53 +146,71 @@ public abstract class PipePattern {
public static PipePattern parsePipePatternFromSourceParametersInternal(
final PipeParameters sourceParameters) {
- // 1. Define the default inclusion pattern (matches all, "root.**")
- // This is used if no inclusion patterns are specified.
- final PipePattern defaultInclusionPattern =
- buildUnionPattern(Collections.singletonList(new
IoTDBPipePattern(null)));
-
- // 2. Parse INCLUSION patterns using the helper
- final PipePattern inclusionPattern =
- parsePatternUnion(
+ // 1. Parse INCLUSION patterns into a list
+ List<PipePattern> inclusionPatterns =
+ parsePatternList(
sourceParameters,
- // 'path' keys (IoTDB wildcard)
EXTRACTOR_PATH_KEY,
SOURCE_PATH_KEY,
- // 'pattern' keys (Prefix or IoTDB via format)
EXTRACTOR_PATTERN_KEY,
- SOURCE_PATTERN_KEY,
- // Default pattern if no keys are found
- defaultInclusionPattern);
+ SOURCE_PATTERN_KEY);
- // 3. Parse EXCLUSION patterns using the helper
- final PipePattern exclusionPattern =
- parsePatternUnion(
+ // If no inclusion patterns are specified, use default "root.**"
+ if (inclusionPatterns.isEmpty()) {
+ inclusionPatterns = new ArrayList<>(Collections.singletonList(new
IoTDBPipePattern(null)));
+ }
+
+ // 2. Parse EXCLUSION patterns into a list
+ List<PipePattern> exclusionPatterns =
+ parsePatternList(
sourceParameters,
- // 'path.exclusion' keys (IoTDB wildcard)
EXTRACTOR_PATH_EXCLUSION_KEY,
SOURCE_PATH_EXCLUSION_KEY,
- // 'pattern.exclusion' keys (Prefix)
EXTRACTOR_PATTERN_EXCLUSION_KEY,
- SOURCE_PATTERN_EXCLUSION_KEY,
- // Default for exclusion is "match nothing" (null)
- null);
-
- // 4. Combine inclusion and exclusion
- if (exclusionPattern == null) {
- // No exclusion defined, return the inclusion pattern directly
- return inclusionPattern;
- } else {
- // If both inclusion and exclusion patterns support IoTDB operations,
- // use the specialized ExclusionIoTDBPipePattern
- if (inclusionPattern instanceof IoTDBPipePatternOperations
- && exclusionPattern instanceof IoTDBPipePatternOperations) {
- return new WithExclusionIoTDBPipePattern(
- (IoTDBPipePatternOperations) inclusionPattern,
- (IoTDBPipePatternOperations) exclusionPattern);
- }
- // Both are defined, wrap them in an ExclusionPipePattern
- return new WithExclusionPipePattern(inclusionPattern, exclusionPattern);
+ SOURCE_PATTERN_EXCLUSION_KEY);
+
+ // 3. Optimize the lists: remove redundant patterns
+ inclusionPatterns = optimizePatterns(inclusionPatterns);
+ exclusionPatterns = optimizePatterns(exclusionPatterns);
+
+ // 4. Prune inclusion patterns covered by exclusions
+ inclusionPatterns = pruneInclusionPatterns(inclusionPatterns,
exclusionPatterns);
+
+ // 5. Check if the resulting inclusion pattern is empty
+ if (inclusionPatterns.isEmpty()) {
+ final String msg =
+ String.format(
+ "Pipe: The provided exclusion pattern fully covers the inclusion
pattern. "
+ + "This pipe pattern will match nothing. "
+ + "Inclusion: %s, Exclusion: %s",
+ sourceParameters.getStringByKeys(EXTRACTOR_PATTERN_KEY,
SOURCE_PATTERN_KEY),
+ sourceParameters.getStringByKeys(
+ EXTRACTOR_PATTERN_EXCLUSION_KEY,
SOURCE_PATTERN_EXCLUSION_KEY));
+ LOGGER.warn(msg);
+ throw new PipeException(msg);
+ }
+
+ // 6. Prune exclusion patterns that do not overlap with any inclusion
+ exclusionPatterns = pruneIrrelevantExclusions(inclusionPatterns,
exclusionPatterns);
+
+ // 7. Build final patterns
+ final PipePattern finalInclusionPattern =
buildUnionPattern(inclusionPatterns);
+
+ if (exclusionPatterns.isEmpty()) {
+ return finalInclusionPattern;
+ }
+
+ final PipePattern finalExclusionPattern =
buildUnionPattern(exclusionPatterns);
+
+ // 8. Combine inclusion and exclusion
+ if (finalInclusionPattern instanceof IoTDBPipePatternOperations
+ && finalExclusionPattern instanceof IoTDBPipePatternOperations) {
+ return new WithExclusionIoTDBPipePattern(
+ (IoTDBPipePatternOperations) finalInclusionPattern,
+ (IoTDBPipePatternOperations) finalExclusionPattern);
}
+
+ return new WithExclusionPipePattern(finalInclusionPattern,
finalExclusionPattern);
}
/**
@@ -255,53 +277,164 @@ public abstract class PipePattern {
}
/**
- * A private helper method to parse a set of 'path' and 'pattern' keys into
a single union
- * PipePattern. This contains the original logic of
parsePipePatternFromSourceParameters.
- *
- * @param sourceParameters The source parameters.
- * @param extractorPathKey Key for extractor path (e.g., "extractor.path").
- * @param sourcePathKey Key for source path (e.g., "source.path").
- * @param extractorPatternKey Key for extractor pattern (e.g.,
"extractor.pattern").
- * @param sourcePatternKey Key for source pattern (e.g., "source.pattern").
- * @param defaultPattern The pattern to return if both path and pattern are
null. If this
- * parameter is null, this method returns null.
- * @return The parsed PipePattern, or defaultPattern, or null if
defaultPattern is null and no
- * patterns are specified.
+ * Helper method to parse pattern parameters into a list of patterns without
creating the Union
+ * object immediately.
*/
- private static PipePattern parsePatternUnion(
+ private static List<PipePattern> parsePatternList(
final PipeParameters sourceParameters,
final String extractorPathKey,
final String sourcePathKey,
final String extractorPatternKey,
- final String sourcePatternKey,
- final PipePattern defaultPattern) {
+ final String sourcePatternKey) {
final String path = sourceParameters.getStringByKeys(extractorPathKey,
sourcePathKey);
final String pattern =
sourceParameters.getStringByKeys(extractorPatternKey, sourcePatternKey);
- // 1. If both "source.path" and "source.pattern" are specified, their
union will be used.
- if (path != null && pattern != null) {
- final List<PipePattern> result = new ArrayList<>();
- // Parse "source.path" as IoTDB-style path.
+ final List<PipePattern> result = new ArrayList<>();
+
+ if (path != null) {
result.addAll(parseMultiplePatterns(path, IoTDBPipePattern::new));
- // Parse "source.pattern" using the helper method.
+ }
+
+ if (pattern != null) {
result.addAll(parsePatternsFromPatternParameter(pattern,
sourceParameters));
- return buildUnionPattern(result);
}
- // 2. If only "source.path" is specified, it will be interpreted as an
IoTDB-style path.
- if (path != null) {
- return buildUnionPattern(parseMultiplePatterns(path,
IoTDBPipePattern::new));
+ return result;
+ }
+
+ /**
+ * Removes patterns from the list that are covered by other patterns in the
same list. For
+ * example, if "root.**" and "root.db.**" are present, "root.db.**" is
removed.
+ */
+ private static List<PipePattern> optimizePatterns(final List<PipePattern>
patterns) {
+ if (patterns == null || patterns.isEmpty()) {
+ return new ArrayList<>();
+ }
+ if (patterns.size() == 1) {
+ return patterns;
}
- // 3. If only "source.pattern" is specified, parse it using the helper
method.
- if (pattern != null) {
- return buildUnionPattern(parsePatternsFromPatternParameter(pattern,
sourceParameters));
+ // 1. Sort patterns by "Broadness"
+ final List<PipePattern> sortedPatterns = new ArrayList<>(patterns);
+ sortedPatterns.sort(
+ (o1, o2) -> {
+ final PartialPath p1 = o1.getBaseInclusionPaths().get(0);
+ final PartialPath p2 = o2.getBaseInclusionPaths().get(0);
+
+ final int lenCompare = Integer.compare(p1.getNodeLength(),
p2.getNodeLength());
+ if (lenCompare != 0) {
+ return lenCompare;
+ }
+
+ final boolean w1 = p1.hasWildcard();
+ final boolean w2 = p2.hasWildcard();
+ if (w1 && !w2) {
+ return -1;
+ }
+ if (!w1 && w2) {
+ return 1;
+ }
+
+ return p1.compareTo(p2);
+ });
+
+ // 2. Filter using Trie
+ final PatternTrie trie = new PatternTrie();
+ final List<PipePattern> optimized = new ArrayList<>();
+
+ for (final PipePattern pattern : sortedPatterns) {
+ boolean isCovered = true;
+ for (final PartialPath path : pattern.getBaseInclusionPaths()) {
+ if (!trie.isCovered(path)) {
+ isCovered = false;
+ break;
+ }
+ }
+
+ if (!isCovered) {
+ optimized.add(pattern);
+ for (final PartialPath path : pattern.getBaseInclusionPaths()) {
+ trie.add(path);
+ }
+ }
}
- // 4. If neither "source.path" nor "source.pattern" is specified,
- // return the provided default pattern (which may be null).
- return defaultPattern;
+ return optimized;
+ }
+
+ /**
+ * Prunes patterns from the inclusion list that are fully covered by ANY
pattern in the exclusion
+ * list.
+ */
+ private static List<PipePattern> pruneInclusionPatterns(
+ final List<PipePattern> inclusion, final List<PipePattern> exclusion) {
+ if (inclusion == null || inclusion.isEmpty()) {
+ return new ArrayList<>();
+ }
+ if (exclusion == null || exclusion.isEmpty()) {
+ return inclusion;
+ }
+
+ final PatternTrie exclusionTrie = new PatternTrie();
+ for (final PipePattern exc : exclusion) {
+ for (final PartialPath path : exc.getBaseInclusionPaths()) {
+ exclusionTrie.add(path);
+ }
+ }
+
+ final List<PipePattern> prunedInclusion = new ArrayList<>();
+ for (final PipePattern inc : inclusion) {
+ boolean isFullyExcluded = true;
+ for (final PartialPath path : inc.getBaseInclusionPaths()) {
+ if (!exclusionTrie.isCovered(path)) {
+ isFullyExcluded = false;
+ break;
+ }
+ }
+
+ if (!isFullyExcluded) {
+ prunedInclusion.add(inc);
+ }
+ }
+ return prunedInclusion;
+ }
+
+ /**
+ * Prunes patterns from the exclusion list that do NOT overlap with any of
the remaining inclusion
+ * patterns.
+ */
+ private static List<PipePattern> pruneIrrelevantExclusions(
+ final List<PipePattern> inclusion, final List<PipePattern> exclusion) {
+ if (exclusion == null || exclusion.isEmpty()) {
+ return new ArrayList<>();
+ }
+ if (inclusion == null || inclusion.isEmpty()) {
+ return new ArrayList<>();
+ }
+
+ final PatternTrie inclusionTrie = new PatternTrie();
+ for (final PipePattern inc : inclusion) {
+ for (final PartialPath path : inc.getBaseInclusionPaths()) {
+ inclusionTrie.add(path);
+ }
+ }
+
+ final List<PipePattern> relevantExclusion = new ArrayList<>();
+ for (final PipePattern exc : exclusion) {
+ boolean overlapsWithAnyInclusion = false;
+ for (final PartialPath path : exc.getBaseInclusionPaths()) {
+ if (inclusionTrie.overlaps(path)) {
+ overlapsWithAnyInclusion = true;
+ break;
+ }
+ }
+
+ if (overlapsWithAnyInclusion) {
+ relevantExclusion.add(exc);
+ }
+ }
+ return relevantExclusion;
}
/**
@@ -439,6 +572,7 @@ public abstract class PipePattern {
* @return An int array `[coveredCount, totalInclusionPaths]` for testing
non-failing scenarios.
* @throws PipeException If the inclusion pattern is fully covered by the
exclusion pattern.
*/
+ @TestOnly
public static int[] checkAndLogPatternCoverage(
final PipePattern inclusion, final PipePattern exclusion) throws
PipeException {
if (inclusion == null || exclusion == null) {
@@ -502,4 +636,113 @@ public abstract class PipePattern {
return new int[] {coveredCount, inclusionPaths.size()};
}
+
+ /** A specialized Trie to efficiently check path coverage. */
+ private static class PatternTrie {
+ private final TrieNode root = new TrieNode();
+
+ private static class TrieNode {
+ Map<String, TrieNode> children = new HashMap<>();
+ TrieNode wildcardNode = null;
+
+ boolean isLeaf = false;
+ boolean isMultiLevelWildcard = false;
+ }
+
+ public void add(final PartialPath path) {
+ TrieNode node = root;
+ final String[] nodes = path.getNodes();
+
+ for (final String segment : nodes) {
+ if (node.isMultiLevelWildcard) {
+ return;
+ }
+
+ if (segment.equals(IoTDBConstant.MULTI_LEVEL_PATH_WILDCARD)) {
+ node.isMultiLevelWildcard = true;
+ node.children = Collections.emptyMap();
+ node.wildcardNode = null;
+ node.isLeaf = true;
+ return;
+ }
+
+ if (segment.equals(IoTDBConstant.ONE_LEVEL_PATH_WILDCARD)) {
+ if (node.wildcardNode == null) {
+ node.wildcardNode = new TrieNode();
+ }
+ node = node.wildcardNode;
+ } else {
+ node = node.children.computeIfAbsent(segment, k -> new TrieNode());
+ }
+ }
+ node.isLeaf = true;
+ }
+
+ public boolean isCovered(final PartialPath path) {
+ return checkCoverage(root, path.getNodes(), 0);
+ }
+
+ private boolean checkCoverage(final TrieNode node, final String[]
pathNodes, final int index) {
+ if (node.isMultiLevelWildcard) {
+ return true;
+ }
+
+ if (index >= pathNodes.length) {
+ return node.isLeaf;
+ }
+
+ final String currentSegment = pathNodes[index];
+
+ final TrieNode child = node.children.get(currentSegment);
+ if (child != null && checkCoverage(child, pathNodes, index + 1)) {
+ return true;
+ }
+
+ if (node.wildcardNode != null) {
+ return checkCoverage(node.wildcardNode, pathNodes, index + 1);
+ }
+
+ return false;
+ }
+
+ public boolean overlaps(final PartialPath path) {
+ return checkOverlap(root, path.getNodes(), 0);
+ }
+
+ private boolean checkOverlap(final TrieNode node, final String[]
pathNodes, final int index) {
+ if (node.isMultiLevelWildcard) {
+ return true;
+ }
+
+ if (index < pathNodes.length
+ && pathNodes[index].equals(IoTDBConstant.MULTI_LEVEL_PATH_WILDCARD))
{
+ return true;
+ }
+
+ if (index >= pathNodes.length) {
+ return node.isLeaf;
+ }
+
+ final String pNode = pathNodes[index];
+
+ if (pNode.equals(IoTDBConstant.ONE_LEVEL_PATH_WILDCARD)) {
+ for (final TrieNode child : node.children.values()) {
+ if (checkOverlap(child, pathNodes, index + 1)) {
+ return true;
+ }
+ }
+ if (node.wildcardNode != null) {
+ return checkOverlap(node.wildcardNode, pathNodes, index + 1);
+ }
+ return false;
+ }
+
+ final TrieNode exactChild = node.children.get(pNode);
+ if (exactChild != null && checkOverlap(exactChild, pathNodes, index +
1)) {
+ return true;
+ }
+
+ return node.wildcardNode != null && checkOverlap(node.wildcardNode,
pathNodes, index + 1);
+ }
+ }
}
diff --git
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/pipe/datastructure/pattern/UnionIoTDBPipePattern.java
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/pipe/datastructure/pattern/UnionIoTDBPipePattern.java
index 5d331d07b03..5000ff22657 100644
---
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/pipe/datastructure/pattern/UnionIoTDBPipePattern.java
+++
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/pipe/datastructure/pattern/UnionIoTDBPipePattern.java
@@ -21,6 +21,7 @@ package org.apache.iotdb.commons.pipe.datastructure.pattern;
import org.apache.iotdb.commons.path.PartialPath;
import org.apache.iotdb.commons.path.PathPatternTree;
+import org.apache.iotdb.commons.utils.TestOnly;
import java.util.ArrayList;
import java.util.Collections;
@@ -45,6 +46,11 @@ public class UnionIoTDBPipePattern extends
IoTDBPipePatternOperations {
this.patterns = Collections.singletonList(pattern);
}
+ @TestOnly
+ public List<IoTDBPipePattern> getPatterns() {
+ return patterns;
+ }
+
//////////////////////////// Pipe Pattern Operations
////////////////////////////
@Override
diff --git
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/pipe/datastructure/pattern/WithExclusionIoTDBPipePattern.java
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/pipe/datastructure/pattern/WithExclusionIoTDBPipePattern.java
index 51ee704e178..b68f10b54dc 100644
---
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/pipe/datastructure/pattern/WithExclusionIoTDBPipePattern.java
+++
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/pipe/datastructure/pattern/WithExclusionIoTDBPipePattern.java
@@ -42,8 +42,6 @@ public class WithExclusionIoTDBPipePattern extends
IoTDBPipePatternOperations {
final IoTDBPipePatternOperations exclusionPattern) {
this.inclusionPattern = inclusionPattern;
this.exclusionPattern = exclusionPattern;
-
- PipePattern.checkAndLogPatternCoverage(inclusionPattern, exclusionPattern);
}
//////////////////////////// Pipe Pattern Operations
////////////////////////////
diff --git
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/pipe/datastructure/pattern/WithExclusionPipePattern.java
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/pipe/datastructure/pattern/WithExclusionPipePattern.java
index b2e914347e6..5a10613c8f2 100644
---
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/pipe/datastructure/pattern/WithExclusionPipePattern.java
+++
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/pipe/datastructure/pattern/WithExclusionPipePattern.java
@@ -39,8 +39,6 @@ public class WithExclusionPipePattern extends PipePattern {
final PipePattern inclusionPattern, final PipePattern exclusionPattern) {
this.inclusionPattern = inclusionPattern;
this.exclusionPattern = exclusionPattern;
-
- PipePattern.checkAndLogPatternCoverage(inclusionPattern, exclusionPattern);
}
@Override