This is an automated email from the ASF dual-hosted git repository. shuwenwei pushed a commit to branch fixBug-0713 in repository https://gitbox.apache.org/repos/asf/iotdb.git
commit 9b315588b44be010d999a3eee38d4e1a7b1773b0 Author: shuwenwei <[email protected]> AuthorDate: Mon Jul 13 14:33:26 2026 +0800 Fix tag index misalignment in read_tsfile when projection pruning removes tag columns When projection pruning removes tag columns from the scan node's assignments, constructExternalTsFileDeviceFilter builds a TsTable from the pruned assignments and recomputes tag indices via getTagColumnIndex. This causes tag2 to be assigned index 0 instead of 1, and ExternalTsFileDeviceFilterVisitor reads the wrong deviceID segment (tag1 instead of tag2), effectively evaluating the wrong tag. Fix: Use ExternalTsFileQueryResource.getTableColumnSchema() which always holds the complete, unpruned column schema with correct tag ordering, instead of the pruned scan node assignments. --- .../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(
