-
Notifications
You must be signed in to change notification settings - Fork 4.6k
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
HIVE-28259: Common table expression detection and rewrites using CBO #5249
base: master
Are you sure you want to change the base?
Conversation
|
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.
LGTM, pretty elegant approach cleanly implemented. I have left few minor comments but nothing blocking, hence the approval.
I have only skimmed few out files for the TPCDS test suite, I haven't checked them all.
ql/src/java/org/apache/hadoop/hive/ql/optimizer/calcite/rules/CommonRelSubExprRegisterRule.java
Outdated
Show resolved
Hide resolved
ql/src/java/org/apache/hadoop/hive/ql/optimizer/calcite/rules/HivePartitionPruneRule.java
Outdated
Show resolved
Hide resolved
ql/src/java/org/apache/hadoop/hive/ql/optimizer/calcite/rules/RemoveUnusedCteRule.java
Outdated
Show resolved
Hide resolved
ql/src/java/org/apache/hadoop/hive/ql/optimizer/calcite/rules/RemoveUnusedCteRule.java
Outdated
Show resolved
Hide resolved
ql/src/java/org/apache/hadoop/hive/ql/optimizer/calcite/rules/RemoveUnusedCteRule.java
Outdated
Show resolved
Hide resolved
ql/src/java/org/apache/hadoop/hive/ql/optimizer/calcite/stats/AggregatedColumns.java
Show resolved
Hide resolved
ql/src/java/org/apache/hadoop/hive/ql/optimizer/calcite/translator/ASTBuilder.java
Outdated
Show resolved
Hide resolved
ql/src/java/org/apache/hadoop/hive/ql/optimizer/calcite/translator/ASTConverter.java
Show resolved
Hide resolved
EXPLAIN FORMATTED CBO | ||
SELECT name FROM emps e WHERE salary > 50000 | ||
UNION | ||
SELECT name FROM emps e WHERE salary > 50000; |
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.
Nit: missing newline at the end of the file
Hey @asolimando , many thanks for reviewing the PR!! I was busy and didn't manage to reply to your comments. In the meantime the PR got a bit stale since master diverged. Would you mind if I rebase this PR cause merging with master may look messy? |
1. Add `applyCteRewriting` phase in `CalcitePlanner` for detecting and using CTEs; ensure rewrite logic is consistent with existing `hive.optimize.cte.materialize.*` properties. 2. Model CTEs as materialized views (MVs) and add utility method in `HiveMaterializedViewUtils` for mapping a CTE to a `RelOptMaterialization`. 3. Refactor core MV rewrite logic in `CalcitePlanner` to use during CTE rewrite and exploit CTEs in a cost-based manner. 4. Add `HiveTableSpool` operator to represent CTEs and handle them in the plan using new rules: `TableScanToSpoolRule` and `RemoveUnusedCteRule`. 5. Add `TableScanToSpoolRule`, and `RemoveUnusedCteRule` to add/remove spools from the plan. 6. Enhance/Enrich metadata handlers for handling the Spool operator. 7. Add `AggregatedColumns` metadata (and respective handler and metadata query), for controlling if a CTE is a "full aggregate" at the CBO (RelNode) level to ensure consistent behavior with `hive.optimize.cte.materialize.full.aggregate.only` property. 8. Add `HiveSqlTypeUtil.containsSqlType` for detecting and skipping the creation of CTEs with untyped nulls since they are not supported (HIVE-11217). 9. Add `hive.optimize.cte.suggester.class` and CommonTableExpressionSuggester interface to provide pluggable CTE detection logic. Given that CTE detection logic can range from basic tree traversal algorithms to complex workload analysis frameworks this part needs to be configurable since there is no one-size-fits-all implementation. The configuration property also allows proprietary algorithms to be integrated in HiveServer2 by implementing the necessary APIs and adding the jars in the classpath. 10. Add prototype implementation for CTE detection logic in CommonTableExpressionIdentitySuggester using CommonRelSubExprRegisterRule and CommonTableExpressionRegistry. Although the implementation is rather simple it can discover various interesting CTEs as demonstrated by the tests and can be indeed useful in a prod environment. 11. Map spool(s) to `WITH` clauses during the RelNode to AST conversion (ASTConverter, ASTBuilder, PlanModifierForASTConv) to exploit existing CTE materialization feature (HIVE-11752). 12. Modify (slighly) `SemanticAnalyzer`/`CalcitePlanner` to enable AST-based CTE materialization (getMetadata) post CBO run. 13. Tests for: * CTE detection logic using the `CommonTableExpressionPrintSuggester` (`TestTezTPCDS30TBPerfCliDriver`) * demonstrating (end-to-end) the CTE feature (cte_cbo_rewrite_0.q) * verify coherence of CTE rewrite with `hive.optimize.cte.materialize.full.aggregate.only` (cte_mat_12.q) * spool JSON serialization (cte_cbo_plan_json.q)
A temporary table is not necessarily a materialized CTE so that condition alone is not enough to perform CTE specific processing. On the other hand, when materializedTable is true then it is certain that we are dealing with materialized CTEs since that field was introduced exactly for this purpose.
If the registry is null then the rule is useless so the check does not make much sense at the moment.
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.
Thanks for the PR for this important performance feature. I have a few comments, some of which are potential future enhancements. Overall, this looks fairly good.
CTE Suggestion 3:HiveProject(d_date_sk=[$0]) | ||
HiveFilter(condition=[AND(=($10, 1), =($6, 2000))]) | ||
HiveTableScan(table=[[default, date_dim]], table:alias=[date_dim]) | ||
|
||
CTE Suggestion 4:HiveProject(d_date_sk=[$0]) | ||
HiveFilter(condition=[AND(=($10, 3), =($6, 2000))]) | ||
HiveTableScan(table=[[default, date_dim]], table:alias=[date_dim]) | ||
|
||
CTE Suggestion 5:HiveProject(d_date_sk=[$0]) | ||
HiveFilter(condition=[AND(=($10, 2), =($6, 2000))]) | ||
HiveTableScan(table=[[default, date_dim]], table:alias=[date_dim]) | ||
|
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 suppose a more sophisticated suggester would combine CTEs 3, 4, 5 into a single one with an IN clause for the HiveFilter, i.e. $10 IN (1, 2, 3) AND $6 = 2000
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.
Indeed a different suggester could opt to merge these CTEs together by creating a disjunction of the three conditions. At this stage, I wanted to keep CommonTableExpressionIdentitySuggester
rather simple and leave more elaborate logic for follow-up tickets.
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.
For instance, in 3ae07ca I had a WIP suggester that tries to create suggestions by combining predicates (with disjunction) over a table that appears multiple times in a query.
@@ -1,3 +1,4 @@ | |||
set hive.optimize.cte.suggester.class=org.apache.hadoop.hive.ql.optimizer.calcite.CommonTableExpressionPrintSuggester; |
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.
In terms of the q file changes, this setting is replicated in all the cbo_queryxx files but would it make sense to just have a separate folder perf/cte with targeted set of tpcds tests where this config option is set ? It is possible we may add other cte specific configurations (e.g some limit on the number of CTEs to consider to reduce planning time), and it may be better to have an isolated suite.
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 came up with the CommonTableExpressionPrintSuggester
as a convenient way to (unit) test the CommonTableExpressionIdentitySuggester
on TPC-DS queries. I considered some other alternatives like 0ed2f5c but these required more changes and were less realistic.
In terms of code duplication indeed the set hive.optimize.cte.suggester.class
appears in ~100 files/lines. If we go for a separate suite the overall duplication may be larger. With the current design we would need to duplicate the driver class (TestTezTPCDS30TBPerfCliDriver
), the respective config (TezTPCDS30TBCliConfig
), potentially the queries that we want to test, xml config files, etc. Furthermore, if we want to test CTE specific configurations for certain TPC-DS queries we can always copy the respective file (e.g., cte_cbo_query11.q) and set additional properties as required.
I am OK with all the options. If you feel a new CTE driver would be needed/beneficial for this work I can do the necessary changes.
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.
Yeah, if the driver and config and associated artifacts have to be duplicated, then we are better off to leave it in the current suite, so I am good with what you have in the PR.
super(operand(TableScan.class, none())); | ||
this.referenceThreshold = referenceThreshold; | ||
this.tableOccurrences = tableOccurrences; | ||
assert referenceThreshold > 0; |
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.
nit: should this assert for > 1 ? Since CTE should only be applicable if there are more than 1 references.
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.
The Spool will be introduced (resp. CTE will be materialized) when tableOccurences > referenceThreshold
(the condition is evaluated in the matches
method) thus referenceThreshold = 1
is a perfectly legal value and it means "more than 1 references".
new RelOptHiveTable(null, cluster.getTypeFactory(), fullName, body.getRowType(), hiveTable, columns, | ||
Collections.emptyList(), Collections.emptyList(), new HiveConf(), Hive.getThreadLocal(), | ||
new QueryTables(true), new HashMap<>(), new HashMap<>(), new AtomicInteger()); | ||
optTable.setRowCount(cluster.getMetadataQuery().getRowCount(body)); |
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.
Besides the rowcount, do we want to propagate other properties to the CTE such as distribution and collation ? If these are not expected to be used for the TableSpool, then it does not matter. It could be a future area of improvement.
* <p>The expressions may contain internal planning concepts such as {@link org.apache.calcite.plan.hep.HepRelVertex}. | ||
* </p> | ||
*/ | ||
private final Map<String, RelNode> ctes = new HashMap<>(); |
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.
Currently, all CTEs are weighted equally. A potentially future enhancement could be one where there is weight/rank associated with it which would feed into the optimizer decisions. This only matters if we want to restrict the number of CTEs to be considered (for shorter compilation time).
* Rule for saving relational expressions that appear more than once in a query tree to the planner context. | ||
*/ | ||
public final class CommonRelSubExprRegisterRule extends CommonRelSubExprRule { | ||
public static final CommonRelSubExprRegisterRule JOIN = new CommonRelSubExprRegisterRule(operand(Join.class, any())); |
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.
Are we supporting any type of joins in the CTE ? IIRC for MVs, there was some restriction around full outer join (need to confirm this).
@@ -1737,6 +1747,14 @@ public RelNode apply(RelOptCluster cluster, RelOptSchema relOptSchema, SchemaPlu | |||
perfLogger.perfLogBegin(this.getClass().getName(), PerfLogger.POSTJOIN_ORDERING); | |||
calcitePlan = applyPostJoinOrderingTransform(calcitePlan, mdProvider.getMetadataProvider(), executorProvider); | |||
perfLogger.perfLogEnd(this.getClass().getName(), PerfLogger.POSTJOIN_ORDERING); | |||
// Perform the CTE rewriting near the end of CBO transformations to avoid interference of the new HiveTableSpool | |||
// operator with other rules (especially those related to constant folding and branch pruning). |
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.
It would be useful to have a simple q test with branch pruning on both sides of a UNION ALL (both sides have CTE with FALSE predicate).. in that case the branches should be pruned first and the CTE part should be skipped altogether.
Without this change some cte_mat*.q tests throw: SemanticException: Line 0:-1 Invalid table alias or column reference
The integer range is sufficient for holding the occurrences of tables in the context of a single query.
|
Approving latest changes, there are 2 diff in qout files but it seems minor |
What changes were proposed in this pull request?
applyCteRewriting
phase inCalcitePlanner
for detecting and using CTEs; ensure rewrite logic is consistent with existinghive.optimize.cte.materialize.*
properties.HiveMaterializedViewUtils
for mapping a CTE to aRelOptMaterialization
.CalcitePlanner
to use during CTE rewrite and exploit CTEs in a cost-based manner.HiveTableSpool
operator to represent CTEs and handle them in the plan using new rules:TableScanToSpoolRule
andRemoveUnusedCteRule
.TableScanToSpoolRule
, andRemoveUnusedCteRule
to add/remove spools from the plan.AggregatedColumns
metadata (and respective handler and metadata query), for controlling if a CTE is a "full aggregate" at the CBO (RelNode) level to ensure consistent behavior withhive.optimize.cte.materialize.full.aggregate.only
property.HiveSqlTypeUtil.containsSqlType
for detecting and skipping the creation of CTEs with untyped nulls since they are not supported (HIVE-11217).hive.optimize.cte.suggester.class
and CommonTableExpressionSuggester interface to provide pluggable CTE detection logic. Given that CTE detection logic can range from basic tree traversal algorithms to complex workload analysis frameworks this part needs to be configurable since there is no one-size-fits-all implementation. The configuration property also allows proprietary algorithms to be integrated in HiveServer2 by implementing the necessary APIs and adding the jars in the classpath.WITH
clauses during the RelNode to AST conversion (ASTConverter, ASTBuilder, PlanModifierForASTConv) to exploit existing CTE materialization feature (HIVE-11752).SemanticAnalyzer
/CalcitePlanner
to enable AST-based CTE materialization (getMetadata) post CBO run.Why are the changes needed?
Does this PR introduce any user-facing change?
By default no. After tuning the new/existing cte properties query plans may change affecting performance.
Is the change a dependency upgrade?
No
How was this patch tested?
Tests for:
CommonTableExpressionPrintSuggester
(TestTezTPCDS30TBPerfCliDriver
)hive.optimize.cte.materialize.full.aggregate.only
(cte_mat_12.q)