This is an automated email from the ASF dual-hosted git repository.
JackieTien97 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 ce74949268e Fix tag index misalignment in read_tsfile when projection
pruning removes tag columns (#18191)
ce74949268e is described below
commit ce74949268ec18f8e4b894b5bbc4373defe993a0
Author: shuwenwei <[email protected]>
AuthorDate: Mon Jul 13 15:15:44 2026 +0800
Fix tag index misalignment in read_tsfile when projection pruning removes
tag columns (#18191)
---
.../recent/IoTDBReadTsFileTableFunctionIT.java | 50 ++++++++++++++++++++++
.../optimizations/PushPredicateIntoTableScan.java | 3 +-
2 files changed, 52 insertions(+), 1 deletion(-)
diff --git
a/integration-test/src/test/java/org/apache/iotdb/relational/it/query/recent/IoTDBReadTsFileTableFunctionIT.java
b/integration-test/src/test/java/org/apache/iotdb/relational/it/query/recent/IoTDBReadTsFileTableFunctionIT.java
index 0b5abc38a62..33c562b3c6d 100644
---
a/integration-test/src/test/java/org/apache/iotdb/relational/it/query/recent/IoTDBReadTsFileTableFunctionIT.java
+++
b/integration-test/src/test/java/org/apache/iotdb/relational/it/query/recent/IoTDBReadTsFileTableFunctionIT.java
@@ -129,6 +129,56 @@ public class IoTDBReadTsFileTableFunctionIT {
DATABASE_NAME);
}
+ @Test
+ public void testReadTsFileWithTagFilterAndProjectionPruning() throws
Exception {
+ // Reproduces a bug where projection pruning removes tag columns from the
scan node's
+ // assignments, and the subsequent device filter construction rebuilds tag
indices from the
+ // pruned list, causing tag index misalignment in
ExternalTsFileDeviceFilterVisitor.
+ // The TsFile has tag columns [tag1, tag2]. When SELECT projects only time
and s1 (omitting
+ // both tag columns), the old code would assign tag2 an index of 0 instead
of 1, and
+ // deviceID.segment(1) would read tag1 instead of tag2.
+ File tsFile = new File(tmpDir, "tag-projection-pruning.tsfile");
+ try (TsFileWriter writer = new TsFileWriter(tsFile)) {
+ generateTable(
+ writer, "table1", Arrays.asList("tag1", "tag2"), Arrays.asList("s1",
"s2"), 1, 2);
+ }
+
+ // tag2 = 'tag2_1' should match only the device whose tag2 is tag2_1
+ // Before fix: read deviceID.segment(1)=tag1 instead of tag2, so this
actually evaluated
+ // tag1 = 'tag2_1' and returned empty
+ tableResultSetEqualTest(
+ "SELECT time, s1 FROM read_tsfile(PATHS => '"
+ + toSqlPath(tsFile)
+ + "', TABLE_NAME => 'table1') WHERE tag2 = 'tag2_1' ORDER BY time",
+ new String[] {"time", "s1"},
+ new String[] {
+ "1970-01-01T00:00:00.001Z,1,", "1970-01-01T00:00:00.002Z,2,",
+ },
+ DATABASE_NAME);
+
+ // tag2 = 'tag1_1' should match nothing: no device has tag2 = tag1_1
+ // Before fix: read deviceID.segment(1)=tag1, so this actually evaluated
tag1 = 'tag1_1'
+ // and returned 2 rows (the device with tag1_1)
+ tableResultSetEqualTest(
+ "SELECT time, s1 FROM read_tsfile(PATHS => '"
+ + toSqlPath(tsFile)
+ + "', TABLE_NAME => 'table1') WHERE tag2 = 'tag1_1'",
+ new String[] {"time", "s1"},
+ new String[] {},
+ DATABASE_NAME);
+
+ // Verify: select tag2 as well to confirm correct matching
+ tableResultSetEqualTest(
+ "SELECT time, tag2, s1 FROM read_tsfile(PATHS => '"
+ + toSqlPath(tsFile)
+ + "', TABLE_NAME => 'table1') WHERE tag2 = 'tag2_2' ORDER BY time",
+ new String[] {"time", "tag2", "s1"},
+ new String[] {
+ "1970-01-01T00:00:00.001Z,tag2_2,1,",
"1970-01-01T00:00:00.002Z,tag2_2,2,",
+ },
+ DATABASE_NAME);
+ }
+
@Test
public void testReadTsFileAggregationWithNoMatchedDevice() throws Exception {
File tsFile = new File(tmpDir, "empty-device-aggregation.tsfile");
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/PushPredicateIntoTableScan.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/PushPredicateIntoTableScan.java
index 44730de6d3f..1ceb4c86f6c 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/PushPredicateIntoTableScan.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/PushPredicateIntoTableScan.java
@@ -558,7 +558,8 @@ public class PushPredicateIntoTableScan implements
PlanOptimizer {
return null;
}
TsTable table = new
TsTable(tableScanNode.getQualifiedObjectName().getObjectName());
- for (Map.Entry<Symbol, ColumnSchema> entry :
tableScanNode.getAssignments().entrySet()) {
+ for (Map.Entry<Symbol, ColumnSchema> entry :
+
tableScanNode.getExternalTsFileQueryResource().getTableColumnSchema().entrySet())
{
ColumnSchema columnSchema = entry.getValue();
if (TAG.equals(columnSchema.getColumnCategory())) {
table.addColumnSchema(