You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@iotdb.apache.org by ja...@apache.org on 2026/04/02 10:45:34 UTC
(iotdb) 06/06: add comments for expected json format for it
This is an automated email from the ASF dual-hosted git repository.
jackietien pushed a commit to branch ty/explain_format
in repository https://gitbox.apache.org/repos/asf/iotdb.git
commit a586954dde7d49b73c7a499ffb1572e98718cc9a
Author: JackieTien97 <ja...@gmail.com>
AuthorDate: Thu Apr 2 18:45:06 2026 +0800
add comments for expected json format for it
---
.../it/query/recent/IoTExplainJsonFormatIT.java | 293 +++++++++++++++++++++
.../optimizations/UnaliasSymbolReferences.java | 3 +-
2 files changed, 295 insertions(+), 1 deletion(-)
diff --git a/integration-test/src/test/java/org/apache/iotdb/relational/it/query/recent/IoTExplainJsonFormatIT.java b/integration-test/src/test/java/org/apache/iotdb/relational/it/query/recent/IoTExplainJsonFormatIT.java
index 0f3d0313124..4cbfd02bf5d 100644
--- a/integration-test/src/test/java/org/apache/iotdb/relational/it/query/recent/IoTExplainJsonFormatIT.java
+++ b/integration-test/src/test/java/org/apache/iotdb/relational/it/query/recent/IoTExplainJsonFormatIT.java
@@ -91,6 +91,54 @@ public class IoTExplainJsonFormatIT {
@Test
public void testExplainJsonFormat() {
+ // Expected output (single row, single JSON object representing the distributed plan tree):
+ // {
+ // "name": "OutputNode-<id>",
+ // "id": "<id>",
+ // "properties": {
+ // "OutputColumns": ["time", "deviceid", "voltage"],
+ // "OutputSymbols": ["time", "deviceid", "voltage"]
+ // },
+ // "children": [
+ // {
+ // "name": "CollectNode-<id>",
+ // "id": "<id>",
+ // "children": [
+ // {
+ // "name": "ExchangeNode-<id>",
+ // "id": "<id>",
+ // "children": [
+ // {
+ // "name": "DeviceTableScanNode-<id>",
+ // "id": "<id>",
+ // "properties": {
+ // "QualifiedTableName": "testdb_json.testtb",
+ // "OutputSymbols": ["time", "deviceid", "voltage"],
+ // "DeviceNumber": "1",
+ // "ScanOrder": "ASC",
+ // "PushDownOffset": "0",
+ // "PushDownLimit": "0",
+ // "PushDownLimitToEachDevice": "false",
+ // "RegionId": "<regionId>"
+ // }
+ // }
+ // ]
+ // },
+ // {
+ // "name": "ExchangeNode-<id>",
+ // "id": "<id>",
+ // "children": [
+ // {
+ // "name": "DeviceTableScanNode-<id>",
+ // "id": "<id>",
+ // "properties": { ... }
+ // }
+ // ]
+ // }
+ // ]
+ // }
+ // ]
+ // }
String sql = "EXPLAIN (FORMAT JSON) SELECT * FROM testtb";
try (Connection conn = EnvFactory.getEnv().getConnection(BaseEnv.TABLE_SQL_DIALECT);
Statement statement = conn.createStatement()) {
@@ -134,6 +182,76 @@ public class IoTExplainJsonFormatIT {
@Test
public void testExplainAnalyzeJsonFormat() {
+ // Expected output (single row, single JSON object with plan statistics + fragment instances):
+ // {
+ // "planStatistics": {
+ // "analyzeCostMs": <ms>,
+ // "fetchPartitionCostMs": <ms>,
+ // "fetchSchemaCostMs": <ms>,
+ // "logicalPlanCostMs": <ms>,
+ // "logicalOptimizationCostMs": <ms>,
+ // "distributionPlanCostMs": <ms>,
+ // "dispatchCostMs": <ms>
+ // },
+ // "fragmentInstancesCount": 3,
+ // "fragmentInstances": [
+ // {
+ // "id": "<queryId>.<fragmentId>.<instanceId>",
+ // "ip": "127.0.0.1:<port>",
+ // "dataRegion": "virtual_data_region",
+ // "state": "FINISHED",
+ // "totalWallTimeMs": <ms>,
+ // "initDataQuerySourceCostMs": <ms>,
+ // "seqFileUnclosed": 0, "seqFileClosed": 0,
+ // "unseqFileUnclosed": 0, "unseqFileClosed": 0,
+ // "readyQueuedTimeMs": <ms>,
+ // "blockQueuedTimeMs": <ms>,
+ // "queryStatistics": {
+ // "timeSeriesIndexFilteredRows": 0,
+ // "chunkIndexFilteredRows": 0,
+ // "pageIndexFilteredRows": 0
+ // },
+ // "operators": {
+ // "planNodeId": "<id>",
+ // "nodeType": "IdentitySinkNode",
+ // "operatorType": "IdentitySinkOperator",
+ // "cpuTimeMs": <ms>,
+ // "outputRows": 5,
+ // "hasNextCalledCount": 5,
+ // "nextCalledCount": 4,
+ // "estimatedMemorySize": 1024,
+ // "specifiedInfo": { "DownStreamPlanNodeId": "<id>" },
+ // "children": [
+ // {
+ // "planNodeId": "<id>",
+ // "nodeType": "CollectNode",
+ // "operatorType": "CollectOperator",
+ // ...
+ // "children": [
+ // { "planNodeId": "<id>", "nodeType": "ExchangeNode", ... },
+ // { "planNodeId": "<id>", "nodeType": "ExchangeNode", ... }
+ // ]
+ // }
+ // ]
+ // }
+ // },
+ // {
+ // "id": "...", "dataRegion": "4", "state": "FINISHED",
+ // ...
+ // "operators": {
+ // "nodeType": "IdentitySinkNode", ...
+ // "children": [
+ // { "nodeType": "DeviceTableScanNode", "operatorType": "TableScanOperator", ... }
+ // ]
+ // }
+ // },
+ // {
+ // "id": "...", "dataRegion": "3", "state": "FINISHED",
+ // ...
+ // "operators": { ... }
+ // }
+ // ]
+ // }
String sql = "EXPLAIN ANALYZE (FORMAT JSON) SELECT * FROM testtb";
try (Connection conn = EnvFactory.getEnv().getConnection(BaseEnv.TABLE_SQL_DIALECT);
Statement statement = conn.createStatement()) {
@@ -168,6 +286,47 @@ public class IoTExplainJsonFormatIT {
@Test
public void testExplainAnalyzeVerboseJsonFormat() {
+ // Expected output (same structure as testExplainAnalyzeJsonFormat, but queryStatistics
+ // includes verbose fields like bloom filter, metadata, chunk reader, and page decoder stats):
+ // {
+ // "planStatistics": { ... },
+ // "fragmentInstancesCount": 3,
+ // "fragmentInstances": [
+ // {
+ // "id": "...", "dataRegion": "virtual_data_region", ...
+ // "queryStatistics": {
+ // "loadBloomFilterFromCacheCount": 0,
+ // "loadBloomFilterFromDiskCount": 0,
+ // "loadBloomFilterActualIOSize": 0,
+ // "loadBloomFilterTimeMs": 0.0,
+ // "loadTimeSeriesMetadataFromCacheCount": 0,
+ // "loadTimeSeriesMetadataFromDiskCount": 0,
+ // "loadTimeSeriesMetadataActualIOSize": 0,
+ // "loadChunkFromCacheCount": 0,
+ // "loadChunkFromDiskCount": 0,
+ // "loadChunkActualIOSize": 0,
+ // "timeSeriesIndexFilteredRows": 0,
+ // "chunkIndexFilteredRows": 0,
+ // "pageIndexFilteredRows": 0,
+ // "rowScanFilteredRows": 0
+ // },
+ // "operators": { ... }
+ // },
+ // {
+ // "id": "...", "dataRegion": "4", ...
+ // "queryStatistics": {
+ // ... (same as above, plus non-zero fields like:)
+ // "loadTimeSeriesMetadataAlignedMemSeqCount": 2,
+ // "loadTimeSeriesMetadataAlignedMemSeqTimeMs": <ms>,
+ // "pageReadersDecodeAlignedMemCount": 2,
+ // "pageReadersDecodeAlignedMemTimeMs": <ms>,
+ // ...
+ // },
+ // "operators": { ... }
+ // },
+ // { "id": "...", "dataRegion": "3", ... }
+ // ]
+ // }
String sql = "EXPLAIN ANALYZE VERBOSE (FORMAT JSON) SELECT * FROM testtb";
try (Connection conn = EnvFactory.getEnv().getConnection(BaseEnv.TABLE_SQL_DIALECT);
Statement statement = conn.createStatement()) {
@@ -266,6 +425,17 @@ public class IoTExplainJsonFormatIT {
@Test
public void testExplainAnalyzeJsonMultipleFragmentInstances() {
+ // Expected output (same structure as testExplainAnalyzeJsonFormat):
+ // {
+ // "planStatistics": { ... },
+ // "fragmentInstancesCount": 3, // >= 2 due to multi-partition data
+ // "fragmentInstances": [
+ // { "id": "...", "dataRegion": "virtual_data_region", "state": "FINISHED", ... },
+ // { "id": "...", "dataRegion": "4", "state": "FINISHED", ... },
+ // { "id": "...", "dataRegion": "3", "state": "FINISHED", ... }
+ // ]
+ // }
+ // Each fragment instance must have "id", "state", and "dataRegion" fields.
String sql = "EXPLAIN ANALYZE (FORMAT JSON) SELECT * FROM testtb";
try (Connection conn = EnvFactory.getEnv().getConnection(BaseEnv.TABLE_SQL_DIALECT);
Statement statement = conn.createStatement()) {
@@ -303,6 +473,42 @@ public class IoTExplainJsonFormatIT {
@Test
public void testExplainJsonWithCte() {
+ // Expected output (when CTE is present, the JSON wraps cteQueries + mainQuery):
+ // {
+ // "cteQueries": [
+ // {
+ // "name": "cte1",
+ // "plan": <plan node for CTE query>
+ // }
+ // ],
+ // "mainQuery": {
+ // "name": "OutputNode-<id>",
+ // "id": "<id>",
+ // "properties": {
+ // "OutputColumns": ["time", "deviceid", "voltage"],
+ // "OutputSymbols": ["time", "deviceid", "voltage"]
+ // },
+ // "children": [
+ // {
+ // "name": "ProjectNode-<id>", ...
+ // "children": [
+ // {
+ // "name": "FilterNode-<id>", ...
+ // "children": [
+ // {
+ // "name": "SemiJoinNode-<id>", ...
+ // "children": [
+ // { "name": "ExchangeNode-<id>", ... }, // main table scan branch
+ // { "name": "ExchangeNode-<id>", ... } // CTE scan branch
+ // ]
+ // }
+ // ]
+ // }
+ // ]
+ // }
+ // ]
+ // }
+ // }
try (Connection conn = EnvFactory.getEnv().getConnection(BaseEnv.TABLE_SQL_DIALECT);
Statement statement = conn.createStatement()) {
statement.execute("USE " + DATABASE_NAME);
@@ -344,6 +550,43 @@ public class IoTExplainJsonFormatIT {
@Test
public void testExplainJsonWithScalarSubquery() {
+ // Expected output (scalar subquery is inlined during optimization, so the plan is a simple
+ // tree with the constant predicate pushed down to DeviceTableScanNode):
+ // {
+ // "name": "OutputNode-<id>",
+ // "id": "<id>",
+ // "properties": {
+ // "OutputColumns": ["time", "deviceid", "voltage"],
+ // "OutputSymbols": ["time", "deviceid", "voltage"]
+ // },
+ // "children": [
+ // {
+ // "name": "CollectNode-<id>",
+ // "id": "<id>",
+ // "children": [
+ // {
+ // "name": "ExchangeNode-<id>", ...
+ // "children": [
+ // {
+ // "name": "DeviceTableScanNode-<id>",
+ // "properties": {
+ // "QualifiedTableName": "testdb_json.testtb",
+ // "PushDownPredicate": "(\"voltage\" > 2E2)",
+ // ...
+ // }
+ // }
+ // ]
+ // },
+ // {
+ // "name": "ExchangeNode-<id>", ...
+ // "children": [
+ // { "name": "DeviceTableScanNode-<id>", ... }
+ // ]
+ // }
+ // ]
+ // }
+ // ]
+ // }
String sql =
"EXPLAIN (FORMAT JSON) SELECT * FROM testtb "
+ "WHERE voltage > (SELECT avg(voltage) FROM testtb)";
@@ -369,6 +612,56 @@ public class IoTExplainJsonFormatIT {
@Test
public void testExplainAnalyzeJsonWithScalarSubquery() {
+ // Expected output (same EXPLAIN ANALYZE JSON structure, but the scalar subquery is resolved
+ // at planning time, so the executed plan only scans with a constant predicate):
+ // {
+ // "planStatistics": {
+ // "analyzeCostMs": <ms>,
+ // "fetchPartitionCostMs": <ms>,
+ // "fetchSchemaCostMs": <ms>,
+ // "logicalPlanCostMs": <ms>, // higher than simple query due to subquery planning
+ // "logicalOptimizationCostMs": <ms>,
+ // "distributionPlanCostMs": <ms>,
+ // "dispatchCostMs": <ms>
+ // },
+ // "fragmentInstancesCount": 3,
+ // "fragmentInstances": [
+ // {
+ // "id": "...", "dataRegion": "virtual_data_region", "state": "FINISHED",
+ // ...
+ // "operators": {
+ // "nodeType": "IdentitySinkNode", ...
+ // "children": [
+ // {
+ // "nodeType": "CollectNode", ...
+ // "children": [
+ // { "nodeType": "ExchangeNode", "outputRows": 2, ... },
+ // { "nodeType": "ExchangeNode", "outputRows": 0, ... }
+ // ]
+ // }
+ // ]
+ // }
+ // },
+ // {
+ // "dataRegion": "4", ...
+ // "operators": {
+ // "nodeType": "IdentitySinkNode", ...
+ // "children": [
+ // { "nodeType": "DeviceTableScanNode", "operatorType": "TableScanOperator", ... }
+ // ]
+ // }
+ // },
+ // {
+ // "dataRegion": "3", ...
+ // "operators": {
+ // "nodeType": "IdentitySinkNode", ...
+ // "children": [
+ // { "nodeType": "DeviceTableScanNode", "outputRows": 0, ... }
+ // ]
+ // }
+ // }
+ // ]
+ // }
String sql =
"EXPLAIN ANALYZE (FORMAT JSON) SELECT * FROM testtb "
+ "WHERE voltage > (SELECT avg(voltage) FROM testtb)";
diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/UnaliasSymbolReferences.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/UnaliasSymbolReferences.java
index 24adb746df4..4b7ab3f3ab4 100644
--- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/UnaliasSymbolReferences.java
+++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/UnaliasSymbolReferences.java
@@ -385,7 +385,8 @@ public class UnaliasSymbolReferences implements PlanOptimizer {
node.getQueryId(),
node.getTimeout(),
node.getOutputSymbols().get(0),
- newChildPermittedOutputs),
+ newChildPermittedOutputs,
+ node.getOutputFormat()),
mapping);
}