ImmutableList.Builder<PlanOptimizer> builder = ImmutableList.builder();
builder.add(new ImplementSampleAsFilter(),
new CanonicalizeExpressions(),
new SimplifyExpressions(metadata, sqlParser),
new UnaliasSymbolReferences(),
new PruneRedundantProjections(),
new SetFlatteningOptimizer(),
new MaterializeSamplePullUp(),
new LimitPushDown(), // Run the LimitPushDown after flattening set operators to make it easier to do the set flattening
new PredicatePushDown(metadata, sqlParser, splitManager, featuresConfig.isExperimentalSyntaxEnabled()),
new PredicatePushDown(metadata, sqlParser, splitManager, featuresConfig.isExperimentalSyntaxEnabled()), // Run predicate push down one more time in case we can leverage new information from generated partitions
new MergeProjections(),
new SimplifyExpressions(metadata, sqlParser), // Re-run the SimplifyExpressions to simplify any recomposed expressions from other optimizations
new UnaliasSymbolReferences(), // Run again because predicate pushdown might add more projections
new IndexJoinOptimizer(indexManager), // Run this after projections and filters have been fully simplified and pushed down
new PruneUnreferencedOutputs(), // Make sure to run this at the end to help clean the plan for logging/execution and not remove info that other optimizers might need at an earlier point
new PruneRedundantProjections()); // This MUST run after PruneUnreferencedOutputs as it may introduce new redundant projections
// TODO: consider adding a formal final plan sanitization optimizer that prepares the plan for transmission/execution/logging
// TODO: figure out how to improve the set flattening optimizer so that it can run at any point