-
Notifications
You must be signed in to change notification settings - Fork 5.5k
Add Optimizer for validating and optimizing #19050
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| 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 | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Okay will change it to OptimizerDriver.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 |
|---|---|---|
|
|
@@ -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; | ||
|
|
@@ -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; | ||
|
|
@@ -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( | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. logicalPlanNode?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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) | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
perhaps call this logicalPlanNode
There was a problem hiding this comment.
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.