diff --git a/presto-main/src/main/java/com/facebook/presto/SystemSessionProperties.java b/presto-main/src/main/java/com/facebook/presto/SystemSessionProperties.java index a0e57e47f7a06..af42b26d692c1 100644 --- a/presto-main/src/main/java/com/facebook/presto/SystemSessionProperties.java +++ b/presto-main/src/main/java/com/facebook/presto/SystemSessionProperties.java @@ -34,6 +34,7 @@ import com.facebook.presto.sql.analyzer.FeaturesConfig.PartialMergePushdownStrategy; import com.facebook.presto.sql.analyzer.FeaturesConfig.PartitioningPrecisionStrategy; import com.facebook.presto.sql.analyzer.FeaturesConfig.SingleStreamSpillerChoice; +import com.facebook.presto.sql.planner.CompilerConfig; import com.facebook.presto.tracing.TracingConfig; import com.google.common.collect.ImmutableList; import io.airlift.units.DataSize; @@ -232,6 +233,8 @@ public final class SystemSessionProperties public static final String SEGMENTED_AGGREGATION_ENABLED = "segmented_aggregation_enabled"; public static final String USE_HISTORY_BASED_PLAN_STATISTICS = "use_history_based_plan_statistics"; public static final String TRACK_HISTORY_BASED_PLAN_STATISTICS = "track_history_based_plan_statistics"; + public static final String MAX_LEAF_NODES_IN_PLAN = "max_leaf_nodes_in_plan"; + public static final String LEAF_NODE_LIMIT_ENABLED = "leaf_node_limit_enabled"; //TODO: Prestissimo related session properties that are temporarily put here. They will be relocated in the future public static final String PRESTISSIMO_SIMPLIFIED_EXPRESSION_EVALUATION_ENABLED = "simplified_expression_evaluation_enabled"; @@ -255,7 +258,8 @@ public SystemSessionProperties() new WarningCollectorConfig(), new NodeSchedulerConfig(), new NodeSpillConfig(), - new TracingConfig()); + new TracingConfig(), + new CompilerConfig()); } @Inject @@ -268,7 +272,8 @@ public SystemSessionProperties( WarningCollectorConfig warningCollectorConfig, NodeSchedulerConfig nodeSchedulerConfig, NodeSpillConfig nodeSpillConfig, - TracingConfig tracingConfig) + TracingConfig tracingConfig, + CompilerConfig compilerConfig) { sessionProperties = ImmutableList.of( stringProperty( @@ -1319,6 +1324,20 @@ public SystemSessionProperties( TRACK_HISTORY_BASED_PLAN_STATISTICS, "Track history based plan statistics service in query optimizer", featuresConfig.isTrackHistoryBasedPlanStatistics(), + false), + new PropertyMetadata<>( + MAX_LEAF_NODES_IN_PLAN, + "Maximum number of leaf nodes in the logical plan of SQL statement", + INTEGER, + Integer.class, + compilerConfig.getLeafNodeLimit(), + false, + value -> validateIntegerValue(value, MAX_LEAF_NODES_IN_PLAN, 0, false), + object -> object), + booleanProperty( + LEAF_NODE_LIMIT_ENABLED, + "Throw exception if the number of leaf nodes in logical plan exceeds threshold set in max_leaf_nodes_in_plan", + compilerConfig.getLeafNodeLimitEnabled(), false)); } @@ -2165,6 +2184,16 @@ public static boolean isVerboseRuntimeStatsEnabled(Session session) return session.getSystemProperty(VERBOSE_RUNTIME_STATS_ENABLED, Boolean.class); } + public static boolean isLeafNodeLimitEnabled(Session session) + { + return session.getSystemProperty(LEAF_NODE_LIMIT_ENABLED, Boolean.class); + } + + public static int getMaxLeafNodesInPlan(Session session) + { + return session.getSystemProperty(MAX_LEAF_NODES_IN_PLAN, Integer.class); + } + public static boolean isStreamingForPartialAggregationEnabled(Session session) { return session.getSystemProperty(STREAMING_FOR_PARTIAL_AGGREGATION_ENABLED, Boolean.class); diff --git a/presto-main/src/main/java/com/facebook/presto/sql/planner/CompilerConfig.java b/presto-main/src/main/java/com/facebook/presto/sql/planner/CompilerConfig.java index 1856cc2ffa0aa..77348c13508f1 100644 --- a/presto-main/src/main/java/com/facebook/presto/sql/planner/CompilerConfig.java +++ b/presto-main/src/main/java/com/facebook/presto/sql/planner/CompilerConfig.java @@ -14,6 +14,7 @@ package com.facebook.presto.sql.planner; import com.facebook.airlift.configuration.Config; +import com.facebook.airlift.configuration.ConfigDescription; import com.facebook.airlift.configuration.DefunctConfig; import com.facebook.presto.spi.function.Description; @@ -23,6 +24,8 @@ public class CompilerConfig { private int expressionCacheSize = 10_000; + private int leafNodeLimit = 10_000; + private boolean leafNodeLimitEnabled; @Min(0) public int getExpressionCacheSize() @@ -37,4 +40,30 @@ public CompilerConfig setExpressionCacheSize(int expressionCacheSize) this.expressionCacheSize = expressionCacheSize; return this; } + + public int getLeafNodeLimit() + { + return this.leafNodeLimit; + } + + @Config("planner.max-leaf-nodes-in-plan") + @ConfigDescription("Maximum number of leaf nodes in logical plan, throw an exception when exceed if leaf-node-limit-enabled is set true") + public CompilerConfig setLeafNodeLimit(int num) + { + this.leafNodeLimit = num; + return this; + } + + public boolean getLeafNodeLimitEnabled() + { + return this.leafNodeLimitEnabled; + } + + @Config("planner.leaf-node-limit-enabled") + @ConfigDescription("Throw an exception if number of leaf nodes in logical plan exceeds threshold set in max-leaf-nodes-in-plan") + public CompilerConfig setLeafNodeLimitEnabled(boolean enabled) + { + this.leafNodeLimitEnabled = enabled; + return this; + } } diff --git a/presto-main/src/main/java/com/facebook/presto/sql/planner/LogicalPlanner.java b/presto-main/src/main/java/com/facebook/presto/sql/planner/LogicalPlanner.java index acd79941ead80..c5ddb9ca1bf0c 100644 --- a/presto-main/src/main/java/com/facebook/presto/sql/planner/LogicalPlanner.java +++ b/presto-main/src/main/java/com/facebook/presto/sql/planner/LogicalPlanner.java @@ -544,7 +544,8 @@ private RelationPlan createTableWriterPlan( private RelationPlan createDeletePlan(Analysis analysis, Delete node) { - DeleteNode deleteNode = new QueryPlanner(analysis, variableAllocator, idAllocator, buildLambdaDeclarationToVariableMap(analysis, variableAllocator), metadata, session) + SqlPlannerContext context = new SqlPlannerContext(0); + DeleteNode deleteNode = new QueryPlanner(analysis, variableAllocator, idAllocator, buildLambdaDeclarationToVariableMap(analysis, variableAllocator), metadata, session, context) .plan(node); TableHandle handle = analysis.getTableHandle(node.getTable()); @@ -584,8 +585,9 @@ private PlanNode createOutputPlan(RelationPlan plan, Analysis analysis) private RelationPlan createRelationPlan(Analysis analysis, Query query) { + SqlPlannerContext context = new SqlPlannerContext(0); return new RelationPlanner(analysis, variableAllocator, idAllocator, buildLambdaDeclarationToVariableMap(analysis, variableAllocator), metadata, session) - .process(query, null); + .process(query, context); } private ConnectorTableMetadata createTableMetadata(QualifiedObjectName table, List columns, Map propertyExpressions, Map, Expression> parameters, Optional comment) diff --git a/presto-main/src/main/java/com/facebook/presto/sql/planner/QueryPlanner.java b/presto-main/src/main/java/com/facebook/presto/sql/planner/QueryPlanner.java index f994c1d35b328..1997fdc5ee693 100644 --- a/presto-main/src/main/java/com/facebook/presto/sql/planner/QueryPlanner.java +++ b/presto-main/src/main/java/com/facebook/presto/sql/planner/QueryPlanner.java @@ -115,6 +115,7 @@ class QueryPlanner private final Metadata metadata; private final Session session; private final SubqueryPlanner subqueryPlanner; + private final SqlPlannerContext sqlPlannerContext; QueryPlanner( Analysis analysis, @@ -122,7 +123,8 @@ class QueryPlanner PlanNodeIdAllocator idAllocator, Map, VariableReferenceExpression> lambdaDeclarationToVariableMap, Metadata metadata, - Session session) + Session session, + SqlPlannerContext sqlPlannerContext) { requireNonNull(analysis, "analysis is null"); requireNonNull(variableAllocator, "variableAllocator is null"); @@ -138,6 +140,7 @@ class QueryPlanner this.metadata = metadata; this.session = session; this.subqueryPlanner = new SubqueryPlanner(analysis, variableAllocator, idAllocator, lambdaDeclarationToVariableMap, metadata, session); + this.sqlPlannerContext = sqlPlannerContext; } public RelationPlan plan(Query query) @@ -266,7 +269,7 @@ private static List computeOutputs(PlanBuilder buil private PlanBuilder planQueryBody(Query query) { RelationPlan relationPlan = new RelationPlanner(analysis, variableAllocator, idAllocator, lambdaDeclarationToVariableMap, metadata, session) - .process(query.getQueryBody(), null); + .process(query.getQueryBody(), sqlPlannerContext); return planBuilderFor(relationPlan); } @@ -277,7 +280,7 @@ private PlanBuilder planFrom(QuerySpecification node) if (node.getFrom().isPresent()) { relationPlan = new RelationPlanner(analysis, variableAllocator, idAllocator, lambdaDeclarationToVariableMap, metadata, session) - .process(node.getFrom().get(), null); + .process(node.getFrom().get(), sqlPlannerContext); } else { relationPlan = planImplicitTable(); @@ -330,7 +333,7 @@ private PlanBuilder filter(PlanBuilder subPlan, Expression predicate, Node node) // rewrite expressions which contain already handled subqueries Expression rewrittenBeforeSubqueries = subPlan.rewrite(predicate); - subPlan = subqueryPlanner.handleSubqueries(subPlan, rewrittenBeforeSubqueries, node); + subPlan = subqueryPlanner.handleSubqueries(subPlan, rewrittenBeforeSubqueries, node, sqlPlannerContext); Expression rewrittenAfterSubqueries = subPlan.rewrite(predicate); return subPlan.withNewRoot(new FilterNode(getSourceLocation(node), idAllocator.getNextId(), subPlan.getRoot(), castToRowExpression(rewrittenAfterSubqueries))); @@ -872,7 +875,7 @@ private PlanBuilder window(PlanBuilder subPlan, List windowFunctio private PlanBuilder handleSubqueries(PlanBuilder subPlan, Node node, Iterable inputs) { for (Expression input : inputs) { - subPlan = subqueryPlanner.handleSubqueries(subPlan, subPlan.rewrite(input), node); + subPlan = subqueryPlanner.handleSubqueries(subPlan, subPlan.rewrite(input), node, sqlPlannerContext); } return subPlan; } diff --git a/presto-main/src/main/java/com/facebook/presto/sql/planner/RelationPlanner.java b/presto-main/src/main/java/com/facebook/presto/sql/planner/RelationPlanner.java index 15a7beb46f468..61d8445866fe1 100644 --- a/presto-main/src/main/java/com/facebook/presto/sql/planner/RelationPlanner.java +++ b/presto-main/src/main/java/com/facebook/presto/sql/planner/RelationPlanner.java @@ -123,7 +123,7 @@ import static java.util.Objects.requireNonNull; class RelationPlanner - extends DefaultTraversalVisitor + extends DefaultTraversalVisitor { private final Analysis analysis; private final PlanVariableAllocator variableAllocator; @@ -158,7 +158,7 @@ class RelationPlanner } @Override - public RelationPlan process(Node node, @Nullable Void context) + public RelationPlan process(Node node, @Nullable SqlPlannerContext context) { // Check if relation planner timeout checkInterruption(); @@ -166,13 +166,13 @@ public RelationPlan process(Node node, @Nullable Void context) } @Override - protected RelationPlan visitTable(Table node, Void context) + protected RelationPlan visitTable(Table node, SqlPlannerContext context) { Query namedQuery = analysis.getNamedQuery(node); Scope scope = analysis.getScope(node); if (namedQuery != null) { - RelationPlan subPlan = process(namedQuery, null); + RelationPlan subPlan = process(namedQuery, context); // Add implicit coercions if view query produces types that don't match the declared output types // of the view (e.g., if the underlying tables referenced by the view changed) @@ -193,13 +193,14 @@ protected RelationPlan visitTable(Table node, Void context) List outputVariables = outputVariablesBuilder.build(); List> tableConstraints = metadata.getTableMetadata(session, handle).getMetadata().getTableConstraints(); + context.incrementLeafNodes(session); PlanNode root = new TableScanNode(getSourceLocation(node.getLocation()), idAllocator.getNextId(), handle, outputVariables, columns.build(), tableConstraints, TupleDomain.all(), TupleDomain.all()); return new RelationPlan(root, scope, outputVariables); } @Override - protected RelationPlan visitAliasedRelation(AliasedRelation node, Void context) + protected RelationPlan visitAliasedRelation(AliasedRelation node, SqlPlannerContext context) { RelationPlan subPlan = process(node.getRelation(), context); @@ -228,7 +229,7 @@ protected RelationPlan visitAliasedRelation(AliasedRelation node, Void context) } @Override - protected RelationPlan visitSampledRelation(SampledRelation node, Void context) + protected RelationPlan visitSampledRelation(SampledRelation node, SqlPlannerContext context) { RelationPlan subPlan = process(node.getRelation(), context); @@ -243,7 +244,7 @@ protected RelationPlan visitSampledRelation(SampledRelation node, Void context) } @Override - protected RelationPlan visitJoin(Join node, Void context) + protected RelationPlan visitJoin(Join node, SqlPlannerContext context) { // TODO: translate the RIGHT join into a mirrored LEFT join when we refactor (@martint) RelationPlan leftPlan = process(node.getLeft(), context); @@ -261,7 +262,7 @@ protected RelationPlan visitJoin(Join node, Void context) if (node.getType() != Join.Type.CROSS && node.getType() != Join.Type.IMPLICIT) { throw notSupportedException(lateral.get(), "LATERAL on other than the right side of CROSS JOIN"); } - return planLateralJoin(node, leftPlan, lateral.get()); + return planLateralJoin(node, leftPlan, lateral.get(), context); } RelationPlan rightPlan = process(node.getRight(), context); @@ -337,8 +338,8 @@ else if (firstDependencies.stream().allMatch(right::canResolve) && secondDepende } } - leftPlanBuilder = subqueryPlanner.handleSubqueries(leftPlanBuilder, leftComparisonExpressions, node); - rightPlanBuilder = subqueryPlanner.handleSubqueries(rightPlanBuilder, rightComparisonExpressions, node); + leftPlanBuilder = subqueryPlanner.handleSubqueries(leftPlanBuilder, leftComparisonExpressions, node, context); + rightPlanBuilder = subqueryPlanner.handleSubqueries(rightPlanBuilder, rightComparisonExpressions, node, context); // Add projections for join criteria leftPlanBuilder = leftPlanBuilder.appendProjections(leftComparisonExpressions, variableAllocator, idAllocator); @@ -386,7 +387,7 @@ else if (firstDependencies.stream().allMatch(right::canResolve) && secondDepende } // subqueries can be applied only to one side of join - left side is selected in arbitrary way - leftPlanBuilder = subqueryPlanner.handleUncorrelatedSubqueries(leftPlanBuilder, complexJoinExpressions, node); + leftPlanBuilder = subqueryPlanner.handleUncorrelatedSubqueries(leftPlanBuilder, complexJoinExpressions, node, context); } RelationPlan intermediateRootRelationPlan = new RelationPlan(root, analysis.getScope(node), outputs); @@ -419,7 +420,7 @@ else if (firstDependencies.stream().allMatch(right::canResolve) && secondDepende if (node.getType() == INNER) { // rewrite all the other conditions using output variables from left + right plan node. PlanBuilder rootPlanBuilder = new PlanBuilder(translationMap, root); - rootPlanBuilder = subqueryPlanner.handleSubqueries(rootPlanBuilder, complexJoinExpressions, node); + rootPlanBuilder = subqueryPlanner.handleSubqueries(rootPlanBuilder, complexJoinExpressions, node, context); for (Expression expression : complexJoinExpressions) { postInnerJoinConditions.add(rootPlanBuilder.rewrite(expression)); @@ -606,9 +607,9 @@ private Optional getLateral(Relation relation) return Optional.empty(); } - private RelationPlan planLateralJoin(Join join, RelationPlan leftPlan, Lateral lateral) + private RelationPlan planLateralJoin(Join join, RelationPlan leftPlan, Lateral lateral, SqlPlannerContext context) { - RelationPlan rightPlan = process(lateral.getQuery(), null); + RelationPlan rightPlan = process(lateral.getQuery(), context); PlanBuilder leftPlanBuilder = initializePlanBuilder(leftPlan); PlanBuilder rightPlanBuilder = initializePlanBuilder(rightPlan); @@ -671,27 +672,27 @@ else if (type instanceof MapType) { } @Override - protected RelationPlan visitTableSubquery(TableSubquery node, Void context) + protected RelationPlan visitTableSubquery(TableSubquery node, SqlPlannerContext context) { return process(node.getQuery(), context); } @Override - protected RelationPlan visitQuery(Query node, Void context) + protected RelationPlan visitQuery(Query node, SqlPlannerContext context) { - return new QueryPlanner(analysis, variableAllocator, idAllocator, lambdaDeclarationToVariableMap, metadata, session) + return new QueryPlanner(analysis, variableAllocator, idAllocator, lambdaDeclarationToVariableMap, metadata, session, context) .plan(node); } @Override - protected RelationPlan visitQuerySpecification(QuerySpecification node, Void context) + protected RelationPlan visitQuerySpecification(QuerySpecification node, SqlPlannerContext context) { - return new QueryPlanner(analysis, variableAllocator, idAllocator, lambdaDeclarationToVariableMap, metadata, session) + return new QueryPlanner(analysis, variableAllocator, idAllocator, lambdaDeclarationToVariableMap, metadata, session, context) .plan(node); } @Override - protected RelationPlan visitValues(Values node, Void context) + protected RelationPlan visitValues(Values node, SqlPlannerContext context) { Scope scope = analysis.getScope(node); ImmutableList.Builder outputVariablesBuilder = ImmutableList.builder(); @@ -713,6 +714,7 @@ protected RelationPlan visitValues(Values node, Void context) rowsBuilder.add(values.build()); } + context.incrementLeafNodes(session); ValuesNode valuesNode = new ValuesNode(getSourceLocation(node), idAllocator.getNextId(), outputVariablesBuilder.build(), rowsBuilder.build(), Optional.empty()); return new RelationPlan(valuesNode, scope, outputVariablesBuilder.build()); } @@ -739,7 +741,7 @@ public Expression rewriteDereferenceExpression(DereferenceExpression node, Void } @Override - protected RelationPlan visitUnnest(Unnest node, Void context) + protected RelationPlan visitUnnest(Unnest node, SqlPlannerContext context) { Scope scope = analysis.getScope(node); ImmutableList.Builder outputVariablesBuilder = ImmutableList.builder(); @@ -794,7 +796,7 @@ else if (type instanceof MapType) { return new RelationPlan(unnestNode, scope, unnestedVariables); } - private RelationPlan processAndCoerceIfNecessary(Relation node, Void context) + private RelationPlan processAndCoerceIfNecessary(Relation node, SqlPlannerContext context) { Type[] coerceToTypes = analysis.getRelationCoercion(node); @@ -850,11 +852,11 @@ private RelationPlan addCoercions(RelationPlan plan, Type[] targetColumnTypes) } @Override - protected RelationPlan visitUnion(Union node, Void context) + protected RelationPlan visitUnion(Union node, SqlPlannerContext context) { checkArgument(!node.getRelations().isEmpty(), "No relations specified for UNION"); - SetOperationPlan setOperationPlan = process(node); + SetOperationPlan setOperationPlan = process(node, context); PlanNode planNode = new UnionNode(getSourceLocation(node), idAllocator.getNextId(), setOperationPlan.getSources(), setOperationPlan.getOutputVariables(), setOperationPlan.getVariableMapping()); if (node.isDistinct().orElse(true)) { @@ -864,35 +866,35 @@ protected RelationPlan visitUnion(Union node, Void context) } @Override - protected RelationPlan visitIntersect(Intersect node, Void context) + protected RelationPlan visitIntersect(Intersect node, SqlPlannerContext context) { checkArgument(!node.getRelations().isEmpty(), "No relations specified for INTERSECT"); - SetOperationPlan setOperationPlan = process(node); + SetOperationPlan setOperationPlan = process(node, context); PlanNode planNode = new IntersectNode(getSourceLocation(node), idAllocator.getNextId(), setOperationPlan.getSources(), setOperationPlan.getOutputVariables(), setOperationPlan.getVariableMapping()); return new RelationPlan(planNode, analysis.getScope(node), planNode.getOutputVariables()); } @Override - protected RelationPlan visitExcept(Except node, Void context) + protected RelationPlan visitExcept(Except node, SqlPlannerContext context) { checkArgument(!node.getRelations().isEmpty(), "No relations specified for EXCEPT"); - SetOperationPlan setOperationPlan = process(node); + SetOperationPlan setOperationPlan = process(node, context); PlanNode planNode = new ExceptNode(getSourceLocation(node), idAllocator.getNextId(), setOperationPlan.getSources(), setOperationPlan.getOutputVariables(), setOperationPlan.getVariableMapping()); return new RelationPlan(planNode, analysis.getScope(node), planNode.getOutputVariables()); } - private SetOperationPlan process(SetOperation node) + private SetOperationPlan process(SetOperation node, SqlPlannerContext context) { List outputs = null; ImmutableList.Builder sources = ImmutableList.builder(); ImmutableListMultimap.Builder variableMapping = ImmutableListMultimap.builder(); List subPlans = node.getRelations().stream() - .map(relation -> processAndCoerceIfNecessary(relation, null)) + .map(relation -> processAndCoerceIfNecessary(relation, context)) .collect(toImmutableList()); for (RelationPlan relationPlan : subPlans) { diff --git a/presto-main/src/main/java/com/facebook/presto/sql/planner/SqlPlannerContext.java b/presto-main/src/main/java/com/facebook/presto/sql/planner/SqlPlannerContext.java new file mode 100644 index 0000000000000..fe53450d1a270 --- /dev/null +++ b/presto-main/src/main/java/com/facebook/presto/sql/planner/SqlPlannerContext.java @@ -0,0 +1,44 @@ +/* + * Licensed 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 com.facebook.presto.sql.planner; + +import com.facebook.presto.Session; +import com.facebook.presto.spi.PrestoException; + +import static com.facebook.presto.SystemSessionProperties.getMaxLeafNodesInPlan; +import static com.facebook.presto.SystemSessionProperties.isLeafNodeLimitEnabled; +import static com.facebook.presto.spi.StandardErrorCode.EXCEEDED_PLAN_NODE_LIMIT; +import static java.lang.String.format; + +public class SqlPlannerContext +{ + // Record the current number of leaf nodes (table scan and value nodes) in query plan during planning + private int leafNodesInLogicalPlan; + + public SqlPlannerContext(int leafNodesInLogicalPlan) + { + this.leafNodesInLogicalPlan = leafNodesInLogicalPlan; + } + + public void incrementLeafNodes(Session session) + { + leafNodesInLogicalPlan += 1; + if (isLeafNodeLimitEnabled(session)) { + if (leafNodesInLogicalPlan > getMaxLeafNodesInPlan(session)) { + throw new PrestoException(EXCEEDED_PLAN_NODE_LIMIT, format("Number of leaf nodes in logical plan exceeds threshold %s set in max_leaf_nodes_in_plan", + getMaxLeafNodesInPlan(session))); + } + } + } +} diff --git a/presto-main/src/main/java/com/facebook/presto/sql/planner/SubqueryPlanner.java b/presto-main/src/main/java/com/facebook/presto/sql/planner/SubqueryPlanner.java index 928b811db8f4c..747968ca4729f 100644 --- a/presto-main/src/main/java/com/facebook/presto/sql/planner/SubqueryPlanner.java +++ b/presto-main/src/main/java/com/facebook/presto/sql/planner/SubqueryPlanner.java @@ -109,33 +109,33 @@ class SubqueryPlanner this.session = session; } - public PlanBuilder handleSubqueries(PlanBuilder builder, Collection expressions, Node node) + public PlanBuilder handleSubqueries(PlanBuilder builder, Collection expressions, Node node, SqlPlannerContext context) { for (Expression expression : expressions) { - builder = handleSubqueries(builder, expression, node, true); + builder = handleSubqueries(builder, expression, node, true, context); } return builder; } - public PlanBuilder handleUncorrelatedSubqueries(PlanBuilder builder, Collection expressions, Node node) + public PlanBuilder handleUncorrelatedSubqueries(PlanBuilder builder, Collection expressions, Node node, SqlPlannerContext context) { for (Expression expression : expressions) { - builder = handleSubqueries(builder, expression, node, false); + builder = handleSubqueries(builder, expression, node, false, context); } return builder; } - public PlanBuilder handleSubqueries(PlanBuilder builder, Expression expression, Node node) + public PlanBuilder handleSubqueries(PlanBuilder builder, Expression expression, Node node, SqlPlannerContext context) { - return handleSubqueries(builder, expression, node, true); + return handleSubqueries(builder, expression, node, true, context); } - private PlanBuilder handleSubqueries(PlanBuilder builder, Expression expression, Node node, boolean correlationAllowed) + private PlanBuilder handleSubqueries(PlanBuilder builder, Expression expression, Node node, boolean correlationAllowed, SqlPlannerContext context) { - builder = appendInPredicateApplyNodes(builder, collectInPredicateSubqueries(expression, node), correlationAllowed, node); - builder = appendScalarSubqueryApplyNodes(builder, collectScalarSubqueries(expression, node), correlationAllowed); - builder = appendExistsSubqueryApplyNodes(builder, collectExistsSubqueries(expression, node), correlationAllowed); - builder = appendQuantifiedComparisonApplyNodes(builder, collectQuantifiedComparisonSubqueries(expression, node), correlationAllowed, node); + builder = appendInPredicateApplyNodes(builder, collectInPredicateSubqueries(expression, node), correlationAllowed, node, context); + builder = appendScalarSubqueryApplyNodes(builder, collectScalarSubqueries(expression, node), correlationAllowed, context); + builder = appendExistsSubqueryApplyNodes(builder, collectExistsSubqueries(expression, node), correlationAllowed, context); + builder = appendQuantifiedComparisonApplyNodes(builder, collectQuantifiedComparisonSubqueries(expression, node), correlationAllowed, node, context); return builder; } @@ -171,29 +171,29 @@ public Set collectQuantifiedComparisonSubqueries .collect(toImmutableSet()); } - private PlanBuilder appendInPredicateApplyNodes(PlanBuilder subPlan, Set inPredicates, boolean correlationAllowed, Node node) + private PlanBuilder appendInPredicateApplyNodes(PlanBuilder subPlan, Set inPredicates, boolean correlationAllowed, Node node, SqlPlannerContext context) { for (InPredicate inPredicate : inPredicates) { - subPlan = appendInPredicateApplyNode(subPlan, inPredicate, correlationAllowed, node); + subPlan = appendInPredicateApplyNode(subPlan, inPredicate, correlationAllowed, node, context); } return subPlan; } - private PlanBuilder appendInPredicateApplyNode(PlanBuilder subPlan, InPredicate inPredicate, boolean correlationAllowed, Node node) + private PlanBuilder appendInPredicateApplyNode(PlanBuilder subPlan, InPredicate inPredicate, boolean correlationAllowed, Node node, SqlPlannerContext context) { if (subPlan.canTranslate(inPredicate)) { // given subquery is already appended return subPlan; } - subPlan = handleSubqueries(subPlan, inPredicate.getValue(), node); + subPlan = handleSubqueries(subPlan, inPredicate.getValue(), node, context); subPlan = subPlan.appendProjections(ImmutableList.of(inPredicate.getValue()), variableAllocator, idAllocator); checkState(inPredicate.getValueList() instanceof SubqueryExpression); SubqueryExpression valueListSubquery = (SubqueryExpression) inPredicate.getValueList(); SubqueryExpression uncoercedValueListSubquery = uncoercedSubquery(valueListSubquery); - PlanBuilder subqueryPlan = createPlanBuilder(uncoercedValueListSubquery); + PlanBuilder subqueryPlan = createPlanBuilder(uncoercedValueListSubquery, context); subqueryPlan = subqueryPlan.appendProjections(ImmutableList.of(valueListSubquery), variableAllocator, idAllocator); SymbolReference valueList = createSymbolReference(subqueryPlan.translate(valueListSubquery)); @@ -207,15 +207,15 @@ private PlanBuilder appendInPredicateApplyNode(PlanBuilder subPlan, InPredicate return appendApplyNode(subPlan, inPredicate, subqueryPlan.getRoot(), Assignments.of(inPredicateSubqueryVariable, castToRowExpression(inPredicateSubqueryExpression)), correlationAllowed); } - private PlanBuilder appendScalarSubqueryApplyNodes(PlanBuilder builder, Set scalarSubqueries, boolean correlationAllowed) + private PlanBuilder appendScalarSubqueryApplyNodes(PlanBuilder builder, Set scalarSubqueries, boolean correlationAllowed, SqlPlannerContext context) { for (SubqueryExpression scalarSubquery : scalarSubqueries) { - builder = appendScalarSubqueryApplyNode(builder, scalarSubquery, correlationAllowed); + builder = appendScalarSubqueryApplyNode(builder, scalarSubquery, correlationAllowed, context); } return builder; } - private PlanBuilder appendScalarSubqueryApplyNode(PlanBuilder subPlan, SubqueryExpression scalarSubquery, boolean correlationAllowed) + private PlanBuilder appendScalarSubqueryApplyNode(PlanBuilder subPlan, SubqueryExpression scalarSubquery, boolean correlationAllowed, SqlPlannerContext context) { if (subPlan.canTranslate(scalarSubquery)) { // given subquery is already appended @@ -225,7 +225,7 @@ private PlanBuilder appendScalarSubqueryApplyNode(PlanBuilder subPlan, SubqueryE List coercions = coercionsFor(scalarSubquery); SubqueryExpression uncoercedScalarSubquery = uncoercedSubquery(scalarSubquery); - PlanBuilder subqueryPlan = createPlanBuilder(uncoercedScalarSubquery); + PlanBuilder subqueryPlan = createPlanBuilder(uncoercedScalarSubquery, context); subqueryPlan = subqueryPlan.withNewRoot(new EnforceSingleRowNode(subPlan.getRoot().getSourceLocation(), idAllocator.getNextId(), subqueryPlan.getRoot())); subqueryPlan = subqueryPlan.appendProjections(coercions, variableAllocator, idAllocator); @@ -261,10 +261,10 @@ public PlanBuilder appendLateralJoin(PlanBuilder subPlan, PlanBuilder subqueryPl subQueryNotSupportedError(query, "Given correlated subquery"))); } - private PlanBuilder appendExistsSubqueryApplyNodes(PlanBuilder builder, Set existsPredicates, boolean correlationAllowed) + private PlanBuilder appendExistsSubqueryApplyNodes(PlanBuilder builder, Set existsPredicates, boolean correlationAllowed, SqlPlannerContext context) { for (ExistsPredicate existsPredicate : existsPredicates) { - builder = appendExistSubqueryApplyNode(builder, existsPredicate, correlationAllowed); + builder = appendExistSubqueryApplyNode(builder, existsPredicate, correlationAllowed, context); } return builder; } @@ -278,14 +278,14 @@ private PlanBuilder appendExistsSubqueryApplyNodes(PlanBuilder builder, Set */ - private PlanBuilder appendExistSubqueryApplyNode(PlanBuilder subPlan, ExistsPredicate existsPredicate, boolean correlationAllowed) + private PlanBuilder appendExistSubqueryApplyNode(PlanBuilder subPlan, ExistsPredicate existsPredicate, boolean correlationAllowed, SqlPlannerContext context) { if (subPlan.canTranslate(existsPredicate)) { // given subquery is already appended return subPlan; } - PlanBuilder subqueryPlan = createPlanBuilder(existsPredicate.getSubquery()); + PlanBuilder subqueryPlan = createPlanBuilder(existsPredicate.getSubquery(), context); PlanNode subqueryPlanRoot = subqueryPlan.getRoot(); if (isAggregationWithEmptyGroupBy(subqueryPlanRoot)) { @@ -307,15 +307,15 @@ private PlanBuilder appendExistSubqueryApplyNode(PlanBuilder subPlan, ExistsPred correlationAllowed); } - private PlanBuilder appendQuantifiedComparisonApplyNodes(PlanBuilder subPlan, Set quantifiedComparisons, boolean correlationAllowed, Node node) + private PlanBuilder appendQuantifiedComparisonApplyNodes(PlanBuilder subPlan, Set quantifiedComparisons, boolean correlationAllowed, Node node, SqlPlannerContext context) { for (QuantifiedComparisonExpression quantifiedComparison : quantifiedComparisons) { - subPlan = appendQuantifiedComparisonApplyNode(subPlan, quantifiedComparison, correlationAllowed, node); + subPlan = appendQuantifiedComparisonApplyNode(subPlan, quantifiedComparison, correlationAllowed, node, context); } return subPlan; } - private PlanBuilder appendQuantifiedComparisonApplyNode(PlanBuilder subPlan, QuantifiedComparisonExpression quantifiedComparison, boolean correlationAllowed, Node node) + private PlanBuilder appendQuantifiedComparisonApplyNode(PlanBuilder subPlan, QuantifiedComparisonExpression quantifiedComparison, boolean correlationAllowed, Node node, SqlPlannerContext context) { if (subPlan.canTranslate(quantifiedComparison)) { // given subquery is already appended @@ -325,12 +325,12 @@ private PlanBuilder appendQuantifiedComparisonApplyNode(PlanBuilder subPlan, Qua case EQUAL: switch (quantifiedComparison.getQuantifier()) { case ALL: - return planQuantifiedApplyNode(subPlan, quantifiedComparison, correlationAllowed); + return planQuantifiedApplyNode(subPlan, quantifiedComparison, correlationAllowed, context); case ANY: case SOME: // A = ANY B <=> A IN B InPredicate inPredicate = new InPredicate(quantifiedComparison.getValue(), quantifiedComparison.getSubquery()); - subPlan = appendInPredicateApplyNode(subPlan, inPredicate, correlationAllowed, node); + subPlan = appendInPredicateApplyNode(subPlan, inPredicate, correlationAllowed, node, context); subPlan.getTranslations().put(quantifiedComparison, subPlan.translate(inPredicate)); return subPlan; } @@ -349,7 +349,7 @@ private PlanBuilder appendQuantifiedComparisonApplyNode(PlanBuilder subPlan, Qua // "A <> ALL B" is equivalent to "NOT (A = ANY B)" so add a rewrite for the initial quantifiedComparison to notAny subPlan.getTranslations().put(quantifiedComparison, subPlan.getTranslations().rewrite(notAny)); // now plan "A = ANY B" part by calling ourselves for rewrittenAny - return appendQuantifiedComparisonApplyNode(subPlan, rewrittenAny, correlationAllowed, node); + return appendQuantifiedComparisonApplyNode(subPlan, rewrittenAny, correlationAllowed, node, context); case ANY: case SOME: // A <> ANY B <=> min B <> max B || A <> min B <=> !(min B = max B && A = min B) <=> !(A = ALL B) @@ -362,7 +362,7 @@ private PlanBuilder appendQuantifiedComparisonApplyNode(PlanBuilder subPlan, Qua // "A <> ANY B" is equivalent to "NOT (A = ALL B)" so add a rewrite for the initial quantifiedComparison to notAll subPlan.getTranslations().put(quantifiedComparison, subPlan.getTranslations().rewrite(notAll)); // now plan "A = ALL B" part by calling ourselves for rewrittenAll - return appendQuantifiedComparisonApplyNode(subPlan, rewrittenAll, correlationAllowed, node); + return appendQuantifiedComparisonApplyNode(subPlan, rewrittenAll, correlationAllowed, node, context); } break; @@ -370,14 +370,14 @@ private PlanBuilder appendQuantifiedComparisonApplyNode(PlanBuilder subPlan, Qua case LESS_THAN_OR_EQUAL: case GREATER_THAN: case GREATER_THAN_OR_EQUAL: - return planQuantifiedApplyNode(subPlan, quantifiedComparison, correlationAllowed); + return planQuantifiedApplyNode(subPlan, quantifiedComparison, correlationAllowed, context); } // all cases are checked, so this exception should never be thrown throw new IllegalArgumentException( format("Unexpected quantified comparison: '%s %s'", quantifiedComparison.getOperator().getValue(), quantifiedComparison.getQuantifier())); } - private PlanBuilder planQuantifiedApplyNode(PlanBuilder subPlan, QuantifiedComparisonExpression quantifiedComparison, boolean correlationAllowed) + private PlanBuilder planQuantifiedApplyNode(PlanBuilder subPlan, QuantifiedComparisonExpression quantifiedComparison, boolean correlationAllowed, SqlPlannerContext context) { subPlan = subPlan.appendProjections(ImmutableList.of(quantifiedComparison.getValue()), variableAllocator, idAllocator); @@ -385,7 +385,7 @@ private PlanBuilder planQuantifiedApplyNode(PlanBuilder subPlan, QuantifiedCompa SubqueryExpression quantifiedSubquery = (SubqueryExpression) quantifiedComparison.getSubquery(); SubqueryExpression uncoercedQuantifiedSubquery = uncoercedSubquery(quantifiedSubquery); - PlanBuilder subqueryPlan = createPlanBuilder(uncoercedQuantifiedSubquery); + PlanBuilder subqueryPlan = createPlanBuilder(uncoercedQuantifiedSubquery, context); subqueryPlan = subqueryPlan.appendProjections(ImmutableList.of(quantifiedSubquery), variableAllocator, idAllocator); QuantifiedComparisonExpression coercedQuantifiedComparison = new QuantifiedComparisonExpression( @@ -482,10 +482,10 @@ private static Optional tryResolveMissingExpression(PlanBuilder subP return Optional.empty(); } - private PlanBuilder createPlanBuilder(Node node) + private PlanBuilder createPlanBuilder(Node node, SqlPlannerContext context) { RelationPlan relationPlan = new RelationPlanner(analysis, variableAllocator, idAllocator, lambdaDeclarationToVariableMap, metadata, session) - .process(node, null); + .process(node, context); TranslationMap translations = new TranslationMap(relationPlan, analysis, lambdaDeclarationToVariableMap); // Make field->symbol mapping from underlying relation plan available for translations diff --git a/presto-main/src/main/java/com/facebook/presto/testing/LocalQueryRunner.java b/presto-main/src/main/java/com/facebook/presto/testing/LocalQueryRunner.java index fd93abac9bae5..c3a2a57967f3c 100644 --- a/presto-main/src/main/java/com/facebook/presto/testing/LocalQueryRunner.java +++ b/presto-main/src/main/java/com/facebook/presto/testing/LocalQueryRunner.java @@ -158,6 +158,7 @@ import com.facebook.presto.sql.gen.PageFunctionCompiler; import com.facebook.presto.sql.gen.RowExpressionPredicateCompiler; import com.facebook.presto.sql.parser.SqlParser; +import com.facebook.presto.sql.planner.CompilerConfig; import com.facebook.presto.sql.planner.ConnectorPlanOptimizerManager; import com.facebook.presto.sql.planner.LocalExecutionPlanner; import com.facebook.presto.sql.planner.LocalExecutionPlanner.LocalExecutionPlan; @@ -395,7 +396,8 @@ private LocalQueryRunner(Session defaultSession, FeaturesConfig featuresConfig, new WarningCollectorConfig(), new NodeSchedulerConfig(), new NodeSpillConfig(), - new TracingConfig())), + new TracingConfig(), + new CompilerConfig())), new SchemaPropertyManager(), new TablePropertyManager(), new ColumnPropertyManager(), diff --git a/presto-main/src/test/java/com/facebook/presto/execution/scheduler/TestAdaptivePhasedExecutionPolicy.java b/presto-main/src/test/java/com/facebook/presto/execution/scheduler/TestAdaptivePhasedExecutionPolicy.java index 8a21ed99a6efd..85b58caa4c734 100644 --- a/presto-main/src/test/java/com/facebook/presto/execution/scheduler/TestAdaptivePhasedExecutionPolicy.java +++ b/presto-main/src/test/java/com/facebook/presto/execution/scheduler/TestAdaptivePhasedExecutionPolicy.java @@ -39,6 +39,7 @@ import com.facebook.presto.spi.relation.VariableReferenceExpression; import com.facebook.presto.spiller.NodeSpillConfig; import com.facebook.presto.sql.analyzer.FeaturesConfig; +import com.facebook.presto.sql.planner.CompilerConfig; import com.facebook.presto.sql.planner.Partitioning; import com.facebook.presto.sql.planner.PartitioningScheme; import com.facebook.presto.sql.planner.PlanFragment; @@ -99,7 +100,8 @@ public void testCreateExecutionSchedule() new WarningCollectorConfig(), new NodeSchedulerConfig(), new NodeSpillConfig(), - new TracingConfig()))).build(); + new TracingConfig(), + new CompilerConfig()))).build(); AdaptivePhasedExecutionPolicy policy = new AdaptivePhasedExecutionPolicy(); Collection schedulers = getStageExecutionAndSchedulers(4); diff --git a/presto-main/src/test/java/com/facebook/presto/sql/analyzer/TestAnalyzer.java b/presto-main/src/test/java/com/facebook/presto/sql/analyzer/TestAnalyzer.java index 13a6560d1ec86..bf59292c8afcc 100644 --- a/presto-main/src/test/java/com/facebook/presto/sql/analyzer/TestAnalyzer.java +++ b/presto-main/src/test/java/com/facebook/presto/sql/analyzer/TestAnalyzer.java @@ -26,6 +26,7 @@ import com.facebook.presto.spi.StandardWarningCode; import com.facebook.presto.spi.WarningCollector; import com.facebook.presto.spiller.NodeSpillConfig; +import com.facebook.presto.sql.planner.CompilerConfig; import com.facebook.presto.tracing.TracingConfig; import org.testng.annotations.Test; @@ -164,7 +165,8 @@ public void testWindowOrderByAnalysis() new WarningCollectorConfig(), new NodeSchedulerConfig(), new NodeSpillConfig(), - new TracingConfig()))).build(); + new TracingConfig(), + new CompilerConfig()))).build(); assertFails(session, WINDOW_FUNCTION_ORDERBY_LITERAL, "SELECT SUM(x) OVER (PARTITION BY y ORDER BY 1) AS s\n" + "FROM (values (1,10), (2, 10)) AS T(x, y)"); @@ -557,7 +559,8 @@ public void testTooManyGroupingElements() new WarningCollectorConfig(), new NodeSchedulerConfig(), new NodeSpillConfig(), - new TracingConfig()))).build(); + new TracingConfig(), + new CompilerConfig()))).build(); analyze(session, "SELECT a, b, c, d, e, f, g, h, i, j, k, SUM(l)" + "FROM (VALUES (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12))\n" + "t (a, b, c, d, e, f, g, h, i, j, k, l)\n" + diff --git a/presto-main/src/test/java/com/facebook/presto/sql/planner/TestCompilerConfig.java b/presto-main/src/test/java/com/facebook/presto/sql/planner/TestCompilerConfig.java index 1768bec96fc52..95b5310e6996c 100644 --- a/presto-main/src/test/java/com/facebook/presto/sql/planner/TestCompilerConfig.java +++ b/presto-main/src/test/java/com/facebook/presto/sql/planner/TestCompilerConfig.java @@ -28,7 +28,9 @@ public class TestCompilerConfig public void testDefaults() { assertRecordedDefaults(recordDefaults(CompilerConfig.class) - .setExpressionCacheSize(10_000)); + .setExpressionCacheSize(10_000) + .setLeafNodeLimitEnabled(false) + .setLeafNodeLimit(10_000)); } @Test @@ -36,10 +38,14 @@ public void testExplicitPropertyMappings() { Map properties = new ImmutableMap.Builder() .put("compiler.expression-cache-size", "52") + .put("planner.max-leaf-nodes-in-plan", "100") + .put("planner.leaf-node-limit-enabled", "true") .build(); CompilerConfig expected = new CompilerConfig() - .setExpressionCacheSize(52); + .setExpressionCacheSize(52) + .setLeafNodeLimit(100) + .setLeafNodeLimitEnabled(true); assertFullMapping(properties, expected); } diff --git a/presto-main/src/test/java/com/facebook/presto/sql/planner/TestLogicalPlanner.java b/presto-main/src/test/java/com/facebook/presto/sql/planner/TestLogicalPlanner.java index 02e86ad21bf77..fc3a2da79f583 100644 --- a/presto-main/src/test/java/com/facebook/presto/sql/planner/TestLogicalPlanner.java +++ b/presto-main/src/test/java/com/facebook/presto/sql/planner/TestLogicalPlanner.java @@ -57,11 +57,14 @@ import static com.facebook.presto.SystemSessionProperties.FORCE_SINGLE_NODE_OUTPUT; import static com.facebook.presto.SystemSessionProperties.JOIN_DISTRIBUTION_TYPE; import static com.facebook.presto.SystemSessionProperties.JOIN_REORDERING_STRATEGY; +import static com.facebook.presto.SystemSessionProperties.LEAF_NODE_LIMIT_ENABLED; +import static com.facebook.presto.SystemSessionProperties.MAX_LEAF_NODES_IN_PLAN; import static com.facebook.presto.SystemSessionProperties.OFFSET_CLAUSE_ENABLED; import static com.facebook.presto.SystemSessionProperties.OPTIMIZE_HASH_GENERATION; import static com.facebook.presto.SystemSessionProperties.OPTIMIZE_JOINS_WITH_EMPTY_SOURCES; import static com.facebook.presto.SystemSessionProperties.OPTIMIZE_NULLS_IN_JOINS; import static com.facebook.presto.SystemSessionProperties.TASK_CONCURRENCY; +import static com.facebook.presto.SystemSessionProperties.getMaxLeafNodesInPlan; import static com.facebook.presto.common.block.SortOrder.ASC_NULLS_LAST; import static com.facebook.presto.common.predicate.Domain.singleValue; import static com.facebook.presto.common.type.BigintType.BIGINT; @@ -1534,4 +1537,61 @@ public void testRedundantDistinctLimitNodeRemoval() anyTree( tableScan("orders"))))); } + + @Test + public void testLeafNodeInPlanExceedException() + { + Session enableLeafNodeInPlanExceedException = Session.builder(this.getQueryRunner().getDefaultSession()) + .setSystemProperty(MAX_LEAF_NODES_IN_PLAN, Integer.toString(10)) + .setSystemProperty(LEAF_NODE_LIMIT_ENABLED, Boolean.toString(true)) + .build(); + + String expectedMessageRegExp = format("Number of leaf nodes in logical plan exceeds threshold %s set in max_leaf_nodes_in_plan", + getMaxLeafNodesInPlan(enableLeafNodeInPlanExceedException)); + + String joinQuery = "WITH t1 AS " + + "(" + + " SELECT * FROM ( VALUES (1, 'a'), (2, 'b'), (3, 'c') ) AS t (id, name) " + + "), " + + "t2 AS ( " + + " SELECT A.id, B.name FROM t1 A Join t1 B On A.id = B.id " + + "), " + + "t3 AS ( " + + " SELECT A.id, B.name FROM t2 A Join t2 B On A.id = B.id " + + "), " + + "t4 AS ( " + + " SELECT A.id, B.name FROM t3 A Join t3 B On A.id = B.id " + + "), " + + "t5 AS ( " + + " SELECT A.id, B.name FROM t4 A Join t4 B On A.id = B.id " + + ") " + + "SELECT * FROM t5"; + + assertPlanFailedWithException(joinQuery, enableLeafNodeInPlanExceedException, expectedMessageRegExp); + + enableLeafNodeInPlanExceedException = Session.builder(this.getQueryRunner().getDefaultSession()) + .setSystemProperty(MAX_LEAF_NODES_IN_PLAN, Integer.toString(100)) + .setSystemProperty(LEAF_NODE_LIMIT_ENABLED, Boolean.toString(true)) + .build(); + + String joinQuery2 = "WITH t1 AS " + + "(" + + " SELECT * FROM ( VALUES (1, 'a'), (2, 'b'), (3, 'c') ) AS t (id, name) " + + "), " + + "t2 AS ( " + + " SELECT A.id, B.name FROM t1 A Join t1 B On A.id = B.id " + + "), " + + "t3 AS ( " + + " SELECT A.id, B.name FROM t2 A Join t2 B On A.id = B.id " + + "), " + + "t4 AS ( " + + " SELECT A.id, B.name FROM t3 A Join t3 B On A.id = B.id " + + "), " + + "t5 AS ( " + + " SELECT A.id, B.name FROM t4 A Join t4 B On A.id = B.id " + + ") " + + "SELECT * FROM t5"; + + assertPlanSucceeded(joinQuery2, enableLeafNodeInPlanExceedException); + } } diff --git a/presto-main/src/test/java/com/facebook/presto/sql/planner/assertions/BasePlanTest.java b/presto-main/src/test/java/com/facebook/presto/sql/planner/assertions/BasePlanTest.java index 6b80fb52d3c9c..c07c0b448f3c3 100644 --- a/presto-main/src/test/java/com/facebook/presto/sql/planner/assertions/BasePlanTest.java +++ b/presto-main/src/test/java/com/facebook/presto/sql/planner/assertions/BasePlanTest.java @@ -53,10 +53,14 @@ import java.util.function.Predicate; import static com.facebook.airlift.testing.Closeables.closeAllRuntimeException; +import static com.facebook.presto.sql.planner.LogicalPlanner.Stage.CREATED; import static com.facebook.presto.testing.TestingSession.testSessionBuilder; import static com.fasterxml.jackson.databind.SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS; +import static com.google.common.base.Strings.nullToEmpty; +import static java.lang.String.format; import static java.util.Objects.requireNonNull; import static java.util.stream.Collectors.toList; +import static org.testng.Assert.fail; public class BasePlanTest { @@ -240,6 +244,29 @@ protected void assertPlanValidatorWithSession(@Language("SQL") String sql, Sessi }); } + protected void assertPlanFailedWithException(String sql, Session session, @Language("RegExp") String expectedExceptionRegex) + { + try { + queryRunner.inTransaction(session, transactionSession -> queryRunner.createPlan(transactionSession, sql, CREATED, true, WarningCollector.NOOP)); + fail(format("Expected query to fail: %s", sql)); + } + catch (RuntimeException ex) { + if (!nullToEmpty(ex.getMessage()).matches(expectedExceptionRegex)) { + fail(format("Expected exception message '%s' to match '%s' for query: %s", ex.getMessage(), expectedExceptionRegex, sql), ex); + } + } + } + + protected void assertPlanSucceeded(String sql, Session session) + { + try { + queryRunner.inTransaction(session, transactionSession -> queryRunner.createPlan(transactionSession, sql, CREATED, true, WarningCollector.NOOP)); + } + catch (RuntimeException ex) { + fail(format("Query %s failed with exception message '%s'", sql, ex.getMessage()), ex); + } + } + protected Plan plan(String sql) { return plan(sql, LogicalPlanner.Stage.OPTIMIZED_AND_VALIDATED); diff --git a/presto-spi/src/main/java/com/facebook/presto/spi/StandardErrorCode.java b/presto-spi/src/main/java/com/facebook/presto/spi/StandardErrorCode.java index 613f646f2ea8f..12f9f951c9678 100644 --- a/presto-spi/src/main/java/com/facebook/presto/spi/StandardErrorCode.java +++ b/presto-spi/src/main/java/com/facebook/presto/spi/StandardErrorCode.java @@ -66,6 +66,7 @@ public enum StandardErrorCode GENERATED_BYTECODE_TOO_LARGE(0x0000_002B, USER_ERROR), WARNING_AS_ERROR(0x0000_002C, USER_ERROR), INVALID_ARGUMENTS(0x0000_002D, USER_ERROR), + EXCEEDED_PLAN_NODE_LIMIT(0x0000_002E, USER_ERROR), GENERIC_INTERNAL_ERROR(0x0001_0000, INTERNAL_ERROR), TOO_MANY_REQUESTS_FAILED(0x0001_0001, INTERNAL_ERROR, true),