Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
import java.nio.file.Paths;
import java.util.stream.Stream;

import static com.facebook.presto.sql.Optimizer.PlanStage.OPTIMIZED_AND_VALIDATED;
import static com.facebook.presto.sql.planner.plan.JoinNode.DistributionType.REPLICATED;
import static com.facebook.presto.sql.planner.plan.JoinNode.Type.INNER;
import static com.facebook.presto.testing.TestngUtils.toDataProvider;
Expand Down Expand Up @@ -121,7 +122,7 @@ private String generateQueryPlan(String query)
String sql = query.replaceAll("\\s+;\\s+$", "")
.replace("${database}.${schema}.", "")
.replace("\"${database}\".\"${schema}\".\"${prefix}", "\"");
Plan plan = plan(sql, LogicalPlanner.Stage.OPTIMIZED_AND_VALIDATED, false);
Plan plan = plan(sql, OPTIMIZED_AND_VALIDATED, false);

JoinOrderPrinter joinOrderPrinter = new JoinOrderPrinter();
plan.getRoot().accept(joinOrderPrinter, 0);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ private RuntimeMetricName()
public static final String GET_COLUMN_METADATA_TIME_NANOS = "getColumnMetadataTimeNanos";
public static final String GET_SPLITS_TIME_NANOS = "getSplitsTimeNanos";
public static final String LOGICAL_PLANNER_TIME_NANOS = "logicalPlannerTimeNanos";
public static final String OPTIMIZER_TIME_NANOS = "optimizerTimeNanos";
public static final String FRAGMENT_PLAN_TIME_NANOS = "fragmentPlanTimeNanos";
public static final String GET_LAYOUT_TIME_NANOS = "getLayoutTimeNanos";
public static final String REWRITE_AGGREGATION_IF_TO_FILTER_APPLIED = "rewriteAggregationIfToFilterApplied";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,13 @@
import com.facebook.presto.spi.TableHandle;
import com.facebook.presto.spi.WarningCollector;
import com.facebook.presto.spi.function.FunctionKind;
import com.facebook.presto.spi.plan.PlanNode;
import com.facebook.presto.spi.plan.PlanNodeIdAllocator;
import com.facebook.presto.spi.resourceGroups.ResourceGroupQueryLimits;
import com.facebook.presto.spi.security.AccessControl;
import com.facebook.presto.split.CloseableSplitSourceProvider;
import com.facebook.presto.split.SplitManager;
import com.facebook.presto.sql.Optimizer;
import com.facebook.presto.sql.analyzer.Analysis;
import com.facebook.presto.sql.analyzer.Analyzer;
import com.facebook.presto.sql.analyzer.BuiltInQueryPreparer.BuiltInPreparedQuery;
Expand Down Expand Up @@ -88,10 +90,12 @@
import static com.facebook.presto.SystemSessionProperties.isUseLegacyScheduler;
import static com.facebook.presto.common.RuntimeMetricName.FRAGMENT_PLAN_TIME_NANOS;
import static com.facebook.presto.common.RuntimeMetricName.LOGICAL_PLANNER_TIME_NANOS;
import static com.facebook.presto.common.RuntimeMetricName.OPTIMIZER_TIME_NANOS;
import static com.facebook.presto.execution.buffer.OutputBuffers.BROADCAST_PARTITION_ID;
import static com.facebook.presto.execution.buffer.OutputBuffers.createInitialEmptyOutputBuffers;
import static com.facebook.presto.execution.buffer.OutputBuffers.createSpoolingOutputBuffers;
import static com.facebook.presto.spi.StandardErrorCode.NOT_SUPPORTED;
import static com.facebook.presto.sql.Optimizer.PlanStage.OPTIMIZED_AND_VALIDATED;
import static com.facebook.presto.sql.analyzer.utils.ParameterUtils.parameterExtractor;
import static com.facebook.presto.sql.planner.PlanNodeCanonicalInfo.getCanonicalInfo;
import static com.google.common.base.Preconditions.checkArgument;
Expand Down Expand Up @@ -523,20 +527,34 @@ private PlanRoot createLogicalPlan()
stateMachine.beginAnalysis();

// plan query
final PlanVariableAllocator planVariableAllocator = new PlanVariableAllocator();
LogicalPlanner logicalPlanner = new LogicalPlanner(
false,
stateMachine.getSession(),
planOptimizers,
idAllocator,
metadata,
planVariableAllocator);

PlanNode planNode = getSession().getRuntimeStats().profileNanos(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

perhaps call this logicalPlanNode

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel planNode is sufficient, and its used at many other places. If we want to use logicalPlanNode then it would be better to change all other places as well. I will keep it planNode as of now, let me know if you feel strongly about it.

LOGICAL_PLANNER_TIME_NANOS,
() -> logicalPlanner.plan(analysis));

Optimizer optimizer = new Optimizer(
stateMachine.getSession(),
metadata,
planOptimizers,
planChecker,
sqlParser,
planVariableAllocator,
idAllocator,
stateMachine.getWarningCollector(),
statsCalculator,
costCalculator,
stateMachine.getWarningCollector(),
planChecker);
false);

Plan plan = getSession().getRuntimeStats().profileNanos(
LOGICAL_PLANNER_TIME_NANOS,
() -> logicalPlanner.plan(analysis));
OPTIMIZER_TIME_NANOS,
() -> optimizer.validateAndOptimizePlan(planNode, OPTIMIZED_AND_VALIDATED));

queryPlan.set(plan);
stateMachine.setPlanStatsAndCosts(plan.getStatsAndCosts());
stateMachine.setPlanCanonicalInfo(getCanonicalInfo(getSession(), plan.getRoot(), planCanonicalInfoProvider));
Expand Down
130 changes: 130 additions & 0 deletions presto-main/src/main/java/com/facebook/presto/sql/Optimizer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
/*
* 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;

import com.facebook.presto.Session;
import com.facebook.presto.SystemSessionProperties;
import com.facebook.presto.cost.CachingCostProvider;
import com.facebook.presto.cost.CachingStatsProvider;
import com.facebook.presto.cost.CostCalculator;
import com.facebook.presto.cost.CostProvider;
import com.facebook.presto.cost.StatsAndCosts;
import com.facebook.presto.cost.StatsCalculator;
import com.facebook.presto.cost.StatsProvider;
import com.facebook.presto.metadata.Metadata;
import com.facebook.presto.spi.WarningCollector;
import com.facebook.presto.spi.plan.PlanNode;
import com.facebook.presto.spi.plan.PlanNodeIdAllocator;
import com.facebook.presto.sql.parser.SqlParser;
import com.facebook.presto.sql.planner.Plan;
import com.facebook.presto.sql.planner.PlanVariableAllocator;
import com.facebook.presto.sql.planner.TypeProvider;
import com.facebook.presto.sql.planner.optimizations.PlanNodeSearcher;
import com.facebook.presto.sql.planner.optimizations.PlanOptimizer;
import com.facebook.presto.sql.planner.plan.JoinNode;
import com.facebook.presto.sql.planner.plan.SemiJoinNode;
import com.facebook.presto.sql.planner.sanity.PlanChecker;

import java.util.List;
import java.util.Optional;

import static com.facebook.presto.SystemSessionProperties.isPrintStatsForNonJoinQuery;
import static com.facebook.presto.common.RuntimeUnit.NANO;
import static com.facebook.presto.sql.Optimizer.PlanStage.OPTIMIZED;
import static com.facebook.presto.sql.Optimizer.PlanStage.OPTIMIZED_AND_VALIDATED;
import static java.lang.String.format;
import static java.util.Objects.requireNonNull;

public class Optimizer
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not really a fan of this name. As this class is also validating plans. I wonder what could be a good name for this class. e.g. QueryPlanner? suggestions? @rschlussel @mlyublena

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the name is ok wrt to the fact that it both optimizes and validates.
I think there might be some confusion between Optimizer and PlanOptimizer, but if anything, PlanOptimizer is the confusing one (as it describes a single optimization rule vs the whole process of query optimization)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe just call it OpitmizerDriver or OptimizeAndValidate - Optimizer definitely looks wrong to me

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just wanted to add more context. In my later PR, the logical planner related usage would be abstracted behind analyzer interfaces. Only Optimizer class would be used by the SQLQueryExecution

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Driver that runs optimizers one by one - more apt name for this class. This is definitely not an opitmizer

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay will change it to OptimizerDriver.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm maybe Optimizer is OK - let me think more.

{
public enum PlanStage
{
CREATED, OPTIMIZED, OPTIMIZED_AND_VALIDATED
}

private final List<PlanOptimizer> planOptimizers;
private final PlanChecker planChecker;
private final Session session;
private final Metadata metadata;
private final SqlParser sqlParser;
private final PlanVariableAllocator variableAllocator;
private final PlanNodeIdAllocator idAllocator;
private final WarningCollector warningCollector;
private final StatsCalculator statsCalculator;
private final CostCalculator costCalculator;
private final boolean explain;

public Optimizer(
Session session,
Metadata metadata,
List<PlanOptimizer> planOptimizers,
PlanChecker planChecker,
SqlParser sqlParser,
PlanVariableAllocator variableAllocator,
PlanNodeIdAllocator idAllocator,
WarningCollector warningCollector,
StatsCalculator statsCalculator,
CostCalculator costCalculator,
boolean explain)
{
this.session = requireNonNull(session, "session is null");
this.planOptimizers = requireNonNull(planOptimizers, "planOptimizers is null");
this.planChecker = requireNonNull(planChecker, "planChecker is null");
this.metadata = requireNonNull(metadata, "metadata is null");
this.sqlParser = requireNonNull(sqlParser, "sqlParser is null");
this.variableAllocator = requireNonNull(variableAllocator, "variableAllocator is null");
this.idAllocator = requireNonNull(idAllocator, "idAllocator is null");
this.warningCollector = requireNonNull(warningCollector, "warningCollector is null");
this.statsCalculator = requireNonNull(statsCalculator, "statsCalculator is null");
this.costCalculator = requireNonNull(costCalculator, "costCalculator is null");
this.explain = explain;
}

public Plan validateAndOptimizePlan(PlanNode root, PlanStage stage)
{
planChecker.validateIntermediatePlan(root, session, metadata, sqlParser, variableAllocator.getTypes(), warningCollector);

boolean enableVerboseRuntimeStats = SystemSessionProperties.isVerboseRuntimeStatsEnabled(session);
if (stage.ordinal() >= OPTIMIZED.ordinal()) {
for (PlanOptimizer optimizer : planOptimizers) {
long start = System.nanoTime();
root = optimizer.optimize(root, session, variableAllocator.getTypes(), variableAllocator, idAllocator, warningCollector);
requireNonNull(root, format("%s returned a null plan", optimizer.getClass().getName()));
if (enableVerboseRuntimeStats) {
session.getRuntimeStats().addMetricValue(String.format("optimizer%sTimeNanos", optimizer.getClass().getSimpleName()), NANO, System.nanoTime() - start);
}
}
}

if (stage.ordinal() >= OPTIMIZED_AND_VALIDATED.ordinal()) {
// make sure we produce a valid plan after optimizations run. This is mainly to catch programming errors
planChecker.validateFinalPlan(root, session, metadata, sqlParser, variableAllocator.getTypes(), warningCollector);
}

TypeProvider types = variableAllocator.getTypes();
return new Plan(root, types, computeStats(root, types));
}

private StatsAndCosts computeStats(PlanNode root, TypeProvider types)
{
if (explain || isPrintStatsForNonJoinQuery(session) ||
PlanNodeSearcher.searchFrom(root).where(node ->
(node instanceof JoinNode) || (node instanceof SemiJoinNode)).matches()) {
StatsProvider statsProvider = new CachingStatsProvider(statsCalculator, session, types);
CostProvider costProvider = new CachingCostProvider(costCalculator, statsProvider, Optional.empty(), session);
return StatsAndCosts.create(root, statsProvider, costProvider);
}
return StatsAndCosts.empty();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,16 @@
import com.facebook.presto.metadata.Metadata;
import com.facebook.presto.spi.PrestoException;
import com.facebook.presto.spi.WarningCollector;
import com.facebook.presto.spi.plan.PlanNode;
import com.facebook.presto.spi.plan.PlanNodeIdAllocator;
import com.facebook.presto.spi.security.AccessControl;
import com.facebook.presto.sql.Optimizer;
import com.facebook.presto.sql.parser.SqlParser;
import com.facebook.presto.sql.planner.LogicalPlanner;
import com.facebook.presto.sql.planner.Plan;
import com.facebook.presto.sql.planner.PlanFragmenter;
import com.facebook.presto.sql.planner.PlanOptimizers;
import com.facebook.presto.sql.planner.PlanVariableAllocator;
import com.facebook.presto.sql.planner.SubPlan;
import com.facebook.presto.sql.planner.optimizations.PlanOptimizer;
import com.facebook.presto.sql.planner.planPrinter.IOPlanPrinter;
Expand All @@ -43,7 +46,10 @@
import java.util.Map;
import java.util.Optional;

import static com.facebook.presto.common.RuntimeMetricName.LOGICAL_PLANNER_TIME_NANOS;
import static com.facebook.presto.common.RuntimeMetricName.OPTIMIZER_TIME_NANOS;
import static com.facebook.presto.spi.StandardErrorCode.NOT_SUPPORTED;
import static com.facebook.presto.sql.Optimizer.PlanStage.OPTIMIZED_AND_VALIDATED;
import static com.facebook.presto.sql.analyzer.utils.ParameterUtils.parameterExtractor;
import static com.facebook.presto.sql.planner.planPrinter.IOPlanPrinter.textIOPlan;
import static com.facebook.presto.sql.planner.planPrinter.PlanPrinter.graphvizDistributedPlan;
Expand Down Expand Up @@ -194,19 +200,34 @@ public Plan getLogicalPlan(Session session, Statement statement, List<Expression
{
// analyze statement
Analysis analysis = analyze(session, statement, parameters, warningCollector);
// plan statement

final PlanVariableAllocator planVariableAllocator = new PlanVariableAllocator();
LogicalPlanner logicalPlanner = new LogicalPlanner(
true,
session,
planOptimizers,
idAllocator,
metadata,
planVariableAllocator);

PlanNode planNode = session.getRuntimeStats().profileNanos(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logicalPlanNode?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same as above.

LOGICAL_PLANNER_TIME_NANOS,
() -> logicalPlanner.plan(analysis));

Optimizer optimizer = new Optimizer(
session,
metadata,
planOptimizers,
planChecker,
sqlParser,
planVariableAllocator,
idAllocator,
warningCollector,
statsCalculator,
costCalculator,
warningCollector,
planChecker);
return logicalPlanner.plan(analysis);
true);

return session.getRuntimeStats().profileNanos(
OPTIMIZER_TIME_NANOS,
() -> optimizer.validateAndOptimizePlan(planNode, OPTIMIZED_AND_VALIDATED));
}

public SubPlan getDistributedPlan(Session session, Statement statement, List<Expression> parameters, WarningCollector warningCollector)
Expand Down
Loading