This is an automated email from the ASF dual-hosted git repository. jackietien pushed a commit to branch ty/object_type in repository https://gitbox.apache.org/repos/asf/iotdb.git
commit 590f3b03d87fa723f581f7593a7ccf4f1a927520 Author: Weihao Li <[email protected]> AuthorDate: Mon Dec 15 18:05:43 2025 +0800 Add state predicate push down for system table current_queries (#16904) (cherry picked from commit 2710cb3db653d8c674495647bd34cd5afdbc3544) --- .../informationschema/IoTDBCurrentQueriesIT.java | 9 +- .../InformationSchemaContentSupplierFactory.java | 30 ++++++- .../iotdb/db/queryengine/plan/Coordinator.java | 33 +++++++ .../plan/planner/TableOperatorGenerator.java | 1 + .../optimizations/PushPredicateIntoTableScan.java | 100 +++++++++++++++++++++ .../planner/assertions/ColumnReference.java | 10 +-- .../planner/assertions/PlanMatchPattern.java | 20 +++-- .../informationschema/CurrentQueriesTest.java | 77 ++++++++++++++++ 8 files changed, 259 insertions(+), 21 deletions(-) diff --git a/integration-test/src/test/java/org/apache/iotdb/relational/it/query/recent/informationschema/IoTDBCurrentQueriesIT.java b/integration-test/src/test/java/org/apache/iotdb/relational/it/query/recent/informationschema/IoTDBCurrentQueriesIT.java index a68bd92b50b..4f9c8cab794 100644 --- a/integration-test/src/test/java/org/apache/iotdb/relational/it/query/recent/informationschema/IoTDBCurrentQueriesIT.java +++ b/integration-test/src/test/java/org/apache/iotdb/relational/it/query/recent/informationschema/IoTDBCurrentQueriesIT.java @@ -83,7 +83,7 @@ public class IoTDBCurrentQueriesIT { statement.execute("set configuration \"query_cost_stat_window\"='1'"); // 1. query current_queries table - String sql = "SELECT * FROM current_queries"; + String sql = "SELECT * FROM current_queries WHERE state='RUNNING'"; ResultSet resultSet = statement.executeQuery(sql); ResultSetMetaData metaData = resultSet.getMetaData(); Assert.assertEquals(CURRENT_QUERIES_COLUMN_NUM, metaData.getColumnCount()); @@ -116,7 +116,7 @@ public class IoTDBCurrentQueriesIT { Assert.assertEquals(61, rowNum); // 3. requery current_queries table - sql = "SELECT * FROM current_queries"; + sql = "SELECT * FROM current_queries WHERE state='FINISHED'"; resultSet = statement.executeQuery(sql); metaData = resultSet.getMetaData(); Assert.assertEquals(CURRENT_QUERIES_COLUMN_NUM, metaData.getColumnCount()); @@ -128,13 +128,14 @@ public class IoTDBCurrentQueriesIT { } rowNum++; } - // three rows in the result, 2 FINISHED and 1 RUNNING - Assert.assertEquals(3, rowNum); + // two rows in the result, 2 FINISHED + Assert.assertEquals(2, rowNum); Assert.assertEquals(2, finishedQueries); resultSet.close(); // 4. test the expired QueryInfo was evicted Thread.sleep(61_001); + sql = "SELECT * FROM current_queries"; resultSet = statement.executeQuery(sql); rowNum = 0; while (resultSet.next()) { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/source/relational/InformationSchemaContentSupplierFactory.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/source/relational/InformationSchemaContentSupplierFactory.java index 76d84c741de..fe0727ec3f2 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/source/relational/InformationSchemaContentSupplierFactory.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/source/relational/InformationSchemaContentSupplierFactory.java @@ -69,11 +69,15 @@ import org.apache.iotdb.db.protocol.session.IClientSession; import org.apache.iotdb.db.protocol.session.SessionManager; import org.apache.iotdb.db.queryengine.common.ConnectionInfo; import org.apache.iotdb.db.queryengine.common.QueryId; +import org.apache.iotdb.db.queryengine.execution.QueryState; import org.apache.iotdb.db.queryengine.plan.Coordinator; import org.apache.iotdb.db.queryengine.plan.execution.IQueryExecution; import org.apache.iotdb.db.queryengine.plan.execution.config.metadata.relational.ShowCreateViewTask; import org.apache.iotdb.db.queryengine.plan.relational.function.TableBuiltinTableFunction; import org.apache.iotdb.db.queryengine.plan.relational.security.AccessControl; +import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ComparisonExpression; +import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.Expression; +import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.StringLiteral; import org.apache.iotdb.db.queryengine.plan.relational.sql.util.ReservedIdentifiers; import org.apache.iotdb.db.relational.grammar.sql.RelationalSqlKeywords; import org.apache.iotdb.db.schemaengine.table.InformationSchemaUtils; @@ -130,7 +134,10 @@ public class InformationSchemaContentSupplierFactory { private InformationSchemaContentSupplierFactory() {} public static Iterator<TsBlock> getSupplier( - final String tableName, final List<TSDataType> dataTypes, final UserEntity userEntity) { + final String tableName, + final List<TSDataType> dataTypes, + Expression predicate, + final UserEntity userEntity) { try { switch (tableName) { case InformationSchema.QUERIES: @@ -168,7 +175,7 @@ public class InformationSchemaContentSupplierFactory { case InformationSchema.CONNECTIONS: return new ConnectionsSupplier(dataTypes, userEntity); case InformationSchema.CURRENT_QUERIES: - return new CurrentQueriesSupplier(dataTypes, userEntity); + return new CurrentQueriesSupplier(dataTypes, predicate, userEntity); case InformationSchema.QUERIES_COSTS_HISTOGRAM: return new QueriesCostsHistogramSupplier(dataTypes, userEntity); default: @@ -1188,9 +1195,24 @@ public class InformationSchemaContentSupplierFactory { private int nextConsumedIndex; private List<Coordinator.StatedQueriesInfo> queriesInfo; - private CurrentQueriesSupplier(final List<TSDataType> dataTypes, final UserEntity userEntity) { + private CurrentQueriesSupplier( + final List<TSDataType> dataTypes, Expression predicate, final UserEntity userEntity) { super(dataTypes); - queriesInfo = Coordinator.getInstance().getCurrentQueriesInfo(); + + if (predicate == null) { + queriesInfo = Coordinator.getInstance().getCurrentQueriesInfo(); + } else if (QueryState.RUNNING + .toString() + .equals(((StringLiteral) ((ComparisonExpression) predicate).getRight()).getValue())) { + queriesInfo = Coordinator.getInstance().getRunningQueriesInfos(); + } else if (QueryState.FINISHED + .toString() + .equals(((StringLiteral) ((ComparisonExpression) predicate).getRight()).getValue())) { + queriesInfo = Coordinator.getInstance().getFinishedQueriesInfos(); + } else { + queriesInfo = Collections.emptyList(); + } + try { accessControl.checkUserGlobalSysPrivilege(userEntity); } catch (final AccessDeniedException e) { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/Coordinator.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/Coordinator.java index 78cdee720da..57e3d071074 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/Coordinator.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/Coordinator.java @@ -865,6 +865,39 @@ public class Coordinator { } } + public List<StatedQueriesInfo> getRunningQueriesInfos() { + long currentTime = System.currentTimeMillis(); + return getAllQueryExecutions().stream() + .map( + queryExecution -> + new StatedQueriesInfo( + QueryState.RUNNING, + queryExecution.getQueryId(), + queryExecution.getStartExecutionTime(), + DEFAULT_END_TIME, + (currentTime - queryExecution.getStartExecutionTime()) / 1000, + queryExecution.getExecuteSQL().orElse("UNKNOWN"), + queryExecution.getUser(), + queryExecution.getClientHostname())) + .collect(Collectors.toList()); + } + + public List<StatedQueriesInfo> getFinishedQueriesInfos() { + long currentTime = System.currentTimeMillis(); + List<StatedQueriesInfo> result = new ArrayList<>(); + Iterator<QueryInfo> historyQueriesIterator = currentQueriesInfo.iterator(); + long needRecordTime = currentTime - CONFIG.getQueryCostStatWindow() * 60 * 1_000L; + while (historyQueriesIterator.hasNext()) { + QueryInfo queryInfo = historyQueriesIterator.next(); + if (queryInfo.endTime < needRecordTime) { + // out of time window, ignore it + } else { + result.add(new StatedQueriesInfo(QueryState.FINISHED, queryInfo)); + } + } + return result; + } + public List<StatedQueriesInfo> getCurrentQueriesInfo() { List<IQueryExecution> runningQueries = getAllQueryExecutions(); Set<String> runningQueryIdSet = diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/TableOperatorGenerator.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/TableOperatorGenerator.java index c29ceaff04f..68036f9d514 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/TableOperatorGenerator.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/TableOperatorGenerator.java @@ -1296,6 +1296,7 @@ public class TableOperatorGenerator extends PlanVisitor<Operator, LocalExecution getSupplier( node.getQualifiedObjectName().getObjectName(), dataTypes, + node.getPushDownPredicate(), context .getDriverContext() .getFragmentInstanceContext() 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 fab09a2e3f5..55905f67682 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 @@ -56,6 +56,7 @@ import org.apache.iotdb.db.queryengine.plan.relational.planner.node.AggregationN import org.apache.iotdb.db.queryengine.plan.relational.planner.node.AssignUniqueId; import org.apache.iotdb.db.queryengine.plan.relational.planner.node.DeviceTableScanNode; import org.apache.iotdb.db.queryengine.plan.relational.planner.node.FilterNode; +import org.apache.iotdb.db.queryengine.plan.relational.planner.node.InformationSchemaTableScanNode; import org.apache.iotdb.db.queryengine.plan.relational.planner.node.JoinNode; import org.apache.iotdb.db.queryengine.plan.relational.planner.node.ProjectNode; import org.apache.iotdb.db.queryengine.plan.relational.planner.node.SemiJoinNode; @@ -68,6 +69,7 @@ import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.FunctionCall; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.LogicalExpression; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.Node; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.NullLiteral; +import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.StringLiteral; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.SymbolReference; import org.apache.iotdb.db.utils.TimestampPrecisionUtils; @@ -99,6 +101,8 @@ import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkState; import static com.google.common.collect.ImmutableMap.toImmutableMap; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.commons.schema.column.ColumnHeaderConstant.STATE_TABLE_MODEL; +import static org.apache.iotdb.commons.schema.table.InformationSchema.CURRENT_QUERIES; import static org.apache.iotdb.commons.schema.table.column.TsTableColumnCategory.ATTRIBUTE; import static org.apache.iotdb.commons.schema.table.column.TsTableColumnCategory.FIELD; import static org.apache.iotdb.commons.schema.table.column.TsTableColumnCategory.TIME; @@ -440,6 +444,102 @@ public class PushPredicateIntoTableScan implements PlanOptimizer { return output; } + @Override + public PlanNode visitInformationSchemaTableScan( + InformationSchemaTableScanNode node, RewriteContext context) { + // no predicate or table is not current_queries, just return dierectly + if (TRUE_LITERAL.equals(context.inheritedPredicate)) { + return node; + } + + // push-down for CURRENT_QUERIES + if (CURRENT_QUERIES.equals(node.getQualifiedObjectName().getObjectName())) { + SplitExpression splitExpression = splitCurrentQueriesPredicate(context.inheritedPredicate); + // exist expressions can push down to scan operator + if (!splitExpression.getExpressionsCanPushDown().isEmpty()) { + List<Expression> expressions = splitExpression.getExpressionsCanPushDown(); + checkState(expressions.size() == 1, "Unexpected number of expressions in table scan"); + node.setPushDownPredicate(expressions.get(0)); + } + + // exist expressions cannot push down + if (!splitExpression.getExpressionsCannotPushDown().isEmpty()) { + List<Expression> expressions = splitExpression.getExpressionsCannotPushDown(); + return new FilterNode( + queryId.genPlanNodeId(), + node, + expressions.size() == 1 + ? expressions.get(0) + : new LogicalExpression(LogicalExpression.Operator.AND, expressions)); + } + return node; + } + + FilterNode filterNode = + new FilterNode(queryId.genPlanNodeId(), node, context.inheritedPredicate); + context.inheritedPredicate = TRUE_LITERAL; + return filterNode; + } + + private SplitExpression splitCurrentQueriesPredicate(Expression predicate) { + List<Expression> expressionsCanPushDown = new ArrayList<>(); + List<Expression> expressionsCannotPushDown = new ArrayList<>(); + + if (predicate instanceof LogicalExpression + && ((LogicalExpression) predicate).getOperator() == LogicalExpression.Operator.AND) { + + // predicate like state = 'xxx' can be push down + // Note: the optimizer CanonicalizeExpressionRewriter will ensure the predicate like 'xxx' = + // state will be canonicalized to state = 'xxx' + boolean hasExpressionPushDown = false; + for (Expression expression : ((LogicalExpression) predicate).getTerms()) { + if (isStateComparedWithConstant(expression) && !hasExpressionPushDown) { + // if there are more than one state = 'xxx' terms, only add first to push-down candidate + expressionsCanPushDown.add(expression); + hasExpressionPushDown = true; + } else { + expressionsCannotPushDown.add(expression); + } + } + + return new SplitExpression( + Collections.emptyList(), expressionsCanPushDown, expressionsCannotPushDown, null); + } + + if (isStateComparedWithConstant(predicate)) { + expressionsCanPushDown.add(predicate); + } else { + expressionsCannotPushDown.add(predicate); + } + + return new SplitExpression( + Collections.emptyList(), expressionsCanPushDown, expressionsCannotPushDown, null); + } + + private boolean isStateComparedWithConstant(Expression expression) { + if (!(expression instanceof ComparisonExpression)) { + return false; + } + + ComparisonExpression comparisonExpression = (ComparisonExpression) expression; + + if (ComparisonExpression.Operator.EQUAL != comparisonExpression.getOperator()) { + return false; + } + + if (!(comparisonExpression.getLeft() instanceof SymbolReference) + || !STATE_TABLE_MODEL.equals( + ((SymbolReference) comparisonExpression.getLeft()).getName())) { + return false; + } + + if (!(comparisonExpression.getRight() instanceof StringLiteral)) { + return false; + } + + return true; + } + @Override public PlanNode visitDeviceTableScan( DeviceTableScanNode tableScanNode, RewriteContext context) { diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/ColumnReference.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/ColumnReference.java index 6dc2e6d9f96..a93176c19a8 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/ColumnReference.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/ColumnReference.java @@ -25,7 +25,7 @@ import org.apache.iotdb.db.queryengine.plan.relational.metadata.ColumnSchema; import org.apache.iotdb.db.queryengine.plan.relational.metadata.Metadata; import org.apache.iotdb.db.queryengine.plan.relational.planner.Symbol; import org.apache.iotdb.db.queryengine.plan.relational.planner.node.AggregationTableScanNode; -import org.apache.iotdb.db.queryengine.plan.relational.planner.node.DeviceTableScanNode; +import org.apache.iotdb.db.queryengine.plan.relational.planner.node.TableScanNode; import java.util.Map; import java.util.Optional; @@ -49,10 +49,10 @@ public class ColumnReference implements RvalueMatcher { String actualTableName; Map<Symbol, ColumnSchema> assignments; - if (node instanceof DeviceTableScanNode) { - DeviceTableScanNode deviceTableScanNode = (DeviceTableScanNode) node; - actualTableName = deviceTableScanNode.getQualifiedObjectName().toString(); - assignments = deviceTableScanNode.getAssignments(); + if (node instanceof TableScanNode) { + TableScanNode tableScanNode = (TableScanNode) node; + actualTableName = tableScanNode.getQualifiedObjectName().toString(); + assignments = tableScanNode.getAssignments(); } /*else if (node instanceof IndexSourceNode indexSourceNode) { tableHandle = indexSourceNode.getTableHandle(); diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/PlanMatchPattern.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/PlanMatchPattern.java index 9146f46eeca..3ec60536aec 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/PlanMatchPattern.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/PlanMatchPattern.java @@ -149,14 +149,18 @@ public final class PlanMatchPattern { public static PlanMatchPattern infoSchemaTableScan( String expectedTableName, Optional<Integer> dataNodeId, List<String> outputSymbols) { - return node(InformationSchemaTableScanNode.class) - .with( - new InformationSchemaTableScanMatcher( - expectedTableName, - Optional.empty(), - outputSymbols, - Collections.emptySet(), - dataNodeId)); + PlanMatchPattern pattern = + node(InformationSchemaTableScanNode.class) + .with( + new InformationSchemaTableScanMatcher( + expectedTableName, + Optional.empty(), + outputSymbols, + Collections.emptySet(), + dataNodeId)); + outputSymbols.forEach( + symbol -> pattern.withAlias(symbol, new ColumnReference(expectedTableName, symbol))); + return pattern; } public static PlanMatchPattern treeDeviceViewTableScan( diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/informationschema/CurrentQueriesTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/informationschema/CurrentQueriesTest.java index 1e3163321ec..c2f4eb68399 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/informationschema/CurrentQueriesTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/informationschema/CurrentQueriesTest.java @@ -20,6 +20,9 @@ package org.apache.iotdb.db.queryengine.plan.relational.planner.informationschem import org.apache.iotdb.db.queryengine.plan.planner.plan.LogicalQueryPlan; import org.apache.iotdb.db.queryengine.plan.relational.planner.PlanTester; +import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ComparisonExpression; +import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.StringLiteral; +import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.SymbolReference; import com.google.common.collect.ImmutableList; import org.junit.Test; @@ -40,8 +43,10 @@ import static org.apache.iotdb.commons.schema.column.ColumnHeaderConstant.USER_T import static org.apache.iotdb.db.queryengine.plan.relational.planner.assertions.PlanAssert.assertPlan; import static org.apache.iotdb.db.queryengine.plan.relational.planner.assertions.PlanMatchPattern.collect; import static org.apache.iotdb.db.queryengine.plan.relational.planner.assertions.PlanMatchPattern.exchange; +import static org.apache.iotdb.db.queryengine.plan.relational.planner.assertions.PlanMatchPattern.filter; import static org.apache.iotdb.db.queryengine.plan.relational.planner.assertions.PlanMatchPattern.infoSchemaTableScan; import static org.apache.iotdb.db.queryengine.plan.relational.planner.assertions.PlanMatchPattern.output; +import static org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ComparisonExpression.Operator.EQUAL; public class CurrentQueriesTest { private final PlanTester planTester = new PlanTester(); @@ -80,6 +85,78 @@ public class CurrentQueriesTest { infoSchemaTableScan("information_schema.current_queries", Optional.of(2))); } + @Test + public void testCurrentQueriesFilterPushDown() { + // Normal case + LogicalQueryPlan logicalQueryPlan = + planTester.createPlan( + "select * from information_schema.current_queries where state='RUNNING'"); + assertPlan( + logicalQueryPlan, + output( + infoSchemaTableScan( + "information_schema.current_queries", + Optional.empty(), + ImmutableList.of( + QUERY_ID_TABLE_MODEL, + STATE_TABLE_MODEL, + START_TIME_TABLE_MODEL, + END_TIME_TABLE_MODEL, + DATA_NODE_ID_TABLE_MODEL, + COST_TIME, + STATEMENT_TABLE_MODEL, + USER_TABLE_MODEL, + CLIENT_IP)))); + + // mixed push down and cannot push down term + logicalQueryPlan = + planTester.createPlan( + "select * from information_schema.current_queries where state='RUNNING' and query_id='1'"); + assertPlan( + logicalQueryPlan, + output( + filter( + new ComparisonExpression( + EQUAL, new SymbolReference(QUERY_ID_TABLE_MODEL), new StringLiteral("1")), + infoSchemaTableScan( + "information_schema.current_queries", + Optional.empty(), + ImmutableList.of( + QUERY_ID_TABLE_MODEL, + STATE_TABLE_MODEL, + START_TIME_TABLE_MODEL, + END_TIME_TABLE_MODEL, + DATA_NODE_ID_TABLE_MODEL, + COST_TIME, + STATEMENT_TABLE_MODEL, + USER_TABLE_MODEL, + CLIENT_IP))))); + + // More than one state='xxx' terms + logicalQueryPlan = + planTester.createPlan( + "select * from information_schema.current_queries where state='RUNNING' and state='xx'"); + assertPlan( + logicalQueryPlan, + output( + filter( + new ComparisonExpression( + EQUAL, new SymbolReference(STATE_TABLE_MODEL), new StringLiteral("xx")), + infoSchemaTableScan( + "information_schema.current_queries", + Optional.empty(), + ImmutableList.of( + QUERY_ID_TABLE_MODEL, + STATE_TABLE_MODEL, + START_TIME_TABLE_MODEL, + END_TIME_TABLE_MODEL, + DATA_NODE_ID_TABLE_MODEL, + COST_TIME, + STATEMENT_TABLE_MODEL, + USER_TABLE_MODEL, + CLIENT_IP))))); + } + @Test public void testQueriesCostsHistogram() { LogicalQueryPlan logicalQueryPlan =
