Skip to content

Support persistent CTE's#20887

Merged
jaystarshot merged 3 commits intoprestodb:masterfrom
jaystarshot:presto-cte
Dec 13, 2023
Merged

Support persistent CTE's#20887
jaystarshot merged 3 commits intoprestodb:masterfrom
jaystarshot:presto-cte

Conversation

@jaystarshot
Copy link
Member

@jaystarshot jaystarshot commented Sep 15, 2023

Description

Support persistent query-level CTEs

The goal is to allow users to define persistent ctes and support them
Session property cte_materialization_strategy is added which when set to ALL will materialize all ctes to HDFS

This will be the first step for 19744.

Motivation and Context

This change is based on the design. The eventual goal is to materialize or stream the CTEs (either in local memory or remote) using CBO rules and the architecture reflects that.
The temporary tables created for each cte will be partitioned on the first input column
In the current implementation, all CTEs will be evaluated before the query is run.

This change has 5 main commits

Add session property and ast changes

This change adds the session property which allows CTE materialization. This information is also added to the CTEInformation collector.
The relational planner has the main change where a CTEReference Node is added if a CTE is persistent. This reference node will be used by the LogicalCteOptimizer to process.

Planner changes

Main changes
LogicalCteOptimizer - Processes the CTE references into CteConsumers and CteProducers. It also creates a sequence node which is added as a child below the topmost node. The sequence node has a list of leftSources and a right source. The leftSources list has the ctes ordered in a topological order of execution. {a,b,c,d} --> order of execution {d->c->b->a}.

PhysicalCteOptimizer - Processes the CteConsumers and CteProducers into table writes and temporary table scan. The processing code is based on basePlanFragmenter. From logical to physical optimizers, the CteProducer, CteConsumers, and Sequence Node are present to enable further logical optimizations. Physical optimization is applied just before adding exchanges. Post this optimization stage, CteConsumers and CteProducers are no longer present.

Code change in other optimizers and visitors

AddExchanges.java - Handling Sequence Node
HashGenerationOptimizer.java - Handling Sequence Node
StreamPropertyDerivations.java - Handling Sequence Node
PropertyDerivations.java - Handling Sequence Node
PruneUnreferencedOutputs.java - Handling CteConsumer, CteProducer and Sequnce
PushdownSubfields.java - handling CteProducer Node
StreamPropertyDerivations.java - handling sequence
UnaliasSymbolReferences.java - Handling CteConsumer, CteProducer and Sequnce

Scheduling changes

BasePlanFragmenter - Handle and transform sequence nodes. {a,b,c,d} --> order of execution {d->c->b->a}
We will create a chain of different sections of plans {d->c->b->a} and add it as a child of the main query section.

StreamingPlanSection will make sure that the children are scheduled and executed before the parent

Tests

Integration test to test end to end and result correctness
Testing logical cte optimizer

Current wips

  • Handle UnaliasSymbolReferences.java.
  • Handle ValidateDependenciesChecker.java
  • Check if result correctness can be done at a record level
  • Add parser and planner unit tests

Impact

Test Plan

Production tested with ALL CTEs materialized

Contributor checklist

  • Please make sure your submission complies with our development, formatting, commit message, and attribution guidelines.
  • PR description addresses the issue accurately and concisely. If the change is non-trivial, a GitHub Issue is referenced.
  • Documented new properties (with its default value), SQL syntax, functions, or other functionality.
  • If release notes are required, they follow the release notes guidelines.
  • Adequate tests were added if applicable.
  • CI passed.

Todo in future PRs

  1. Integrate with HBO and cost model
  2. Local auto replacement of cte in the optimizer
  3. Select the top five candidate strategies from the optimizer, execute the planning process for each from the logical to the physical stage within the optimizer, and determine the most optimal one.
  4. Support non partitioned temporary tables

Final Quality ToDos

  • Fix cases where the bucketing is done on unsupported buckets
  • Production test again with changes

Release Notes

Please follow release notes guidelines and fill in the release notes below.

== RELEASE NOTES ==

General Changes
* New Feature: Materialization of Common Table Expressions (CTEs) in Queries
* The relevant user connectors must support creating temporary tables 
* By materializing CTEs, users can take advantage of reusing common table expressions within their queries, which can lead to reductions in both storage operations and computational costs

## Recommended Configurations

1. CTE Materialization Strategy:
   - Enable by setting the session property ``cte_materialization_strategy`` or the configuration property ``cte-materialization-strategy`` to ``ALL``.
2. Temporary Table Storage Format:
   - For faster processing, set the configuration ``hive.temporary-table-storage-format`` or the session property ``temporary_table_storage_format`` to ``PAGEFILE``.
3. Temporary Table Schema:
   - It is recommended to set the configuration ``hive.temporary-table-schema`` or the session property ``temporary_table_schema`` to use the user's schema.
 4. Partitioning Provider Catalog
 - The config property ``query.partitioning-provider-catalog`` ``partitioning_provider_catalog`` session property must be set to enable cte materialization. The catalog must support providing a custom partitioning and ability to store temporary tables.

@jaystarshot jaystarshot force-pushed the presto-cte branch 2 times, most recently from a226e08 to 61c61cb Compare September 24, 2023 23:15
@vivek-bharathan vivek-bharathan self-requested a review October 18, 2023 17:58
@jaystarshot jaystarshot force-pushed the presto-cte branch 4 times, most recently from deb4054 to 49dd17c Compare October 20, 2023 09:12
@tdcmeehan tdcmeehan self-assigned this Oct 20, 2023
@jaystarshot jaystarshot force-pushed the presto-cte branch 13 times, most recently from 85ae4bd to 6d1c216 Compare October 24, 2023 11:52
@jaystarshot jaystarshot marked this pull request as ready for review October 24, 2023 12:54
@jaystarshot jaystarshot requested a review from a team as a code owner October 24, 2023 12:54
@github-actions
Copy link

github-actions bot commented Oct 24, 2023

Codenotify: Notifying subscribers in CODENOTIFY files for diff 3b8e8cc...8993b02.

No notifications.

@jaystarshot jaystarshot requested a review from mlyublena October 24, 2023 12:58
@mlyublena
Copy link
Contributor

General comment: I wonder if it would have been possible to achieve the same without introducing new syntax (for example through a session parameter). I know if't not as flexible as allowing specification per CTE but also not as disruptive as new syntax

@jaystarshot
Copy link
Member Author

It won't because the canonical hashes won't be generated and properly linked with the execution.
I have 3 follow up prs'

  1. Add these new nodes in cost frameworks - link
  2. Support HBO with these new nodes - link
  3. Locally auto materialize the ctes - link

@mlyublena
Copy link
Contributor

It won't because the canonical hashes won't be generated and properly linked with the execution. I have 3 follow up prs'

  1. Add these new nodes in cost frameworks - link
  2. Support HBO with these new nodes - link
  3. Locally auto materialize the ctes - link

makes sense.
what about the opposite: if the CTE is not materialized, would HBO work as before?

@jaystarshot
Copy link
Member Author

Yes, the optimizer and entire flow remains unchanged unless the relational planner adds a cteReferenceNode, which only occurs if the specific flag is enabled.

@jaystarshot
Copy link
Member Author

jaystarshot commented Nov 28, 2023

Does this PR look good to merge? Will need a committer approval

@jaystarshot
Copy link
Member Author

As a follow up PR, I will also add a materialization strategy to only materialize ctes if references > threshold times and have a join or aggregate and maybe reduce data size

@tdcmeehan
Copy link
Contributor

Does this PR look good to merge? Will need a committer approval

I'll have a look this week, other committers are free to get to it sooner!

Copy link
Contributor

Choose a reason for hiding this comment

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

should we add this check to is isCteMaterializable?

Copy link
Member Author

Choose a reason for hiding this comment

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

I think here its just a catch for the metadata.createTemporaryTable() which cannot be invoked in the parser since we do not have partition metadata and other info there

Copy link
Contributor

@mlyublena mlyublena left a comment

Choose a reason for hiding this comment

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

Looks good!

@jaystarshot
Copy link
Member Author

Adding a nit (using partitioningProviderCatalog) instead of hardcoded "hive" string in a use case.

Copy link
Contributor

@tdcmeehan tdcmeehan left a comment

Choose a reason for hiding this comment

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

Please make sure to consistently validate inputs. Please squash the Tests commit.

Copy link
Contributor

Choose a reason for hiding this comment

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

Please also validate that the input is not null with Objects.requireNonNull.

Copy link
Contributor

Choose a reason for hiding this comment

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

Here and elsewhere: Please validate inputs are not null

Copy link
Member Author

Choose a reason for hiding this comment

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

Thanks added in other cases, not adding here because not really needed at this static function.

Copy link
Contributor

Choose a reason for hiding this comment

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

Use immutable list

Copy link
Contributor

Choose a reason for hiding this comment

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

Can this return an immutable copy

Copy link
Contributor

Choose a reason for hiding this comment

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

Here and elsewhere--please always validate the inputs are not null

Copy link
Contributor

Choose a reason for hiding this comment

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

Ditto

Copy link
Contributor

Choose a reason for hiding this comment

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

Ditto

Copy link
Contributor

Choose a reason for hiding this comment

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

In general we prefer // comments

Copy link
Contributor

Choose a reason for hiding this comment

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

Please check inputs are not null, and create an immutable copy of the original output variables (no cost if the input is immutable as well)

Copy link
Contributor

Choose a reason for hiding this comment

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

Please do not use single letter variables. cteProducerCount or something similar

@tdcmeehan
Copy link
Contributor

Please look into the merge conflicts.

@jaystarshot jaystarshot force-pushed the presto-cte branch 3 times, most recently from 4e97080 to a268f5e Compare December 12, 2023 18:48
Copy link
Contributor

@tdcmeehan tdcmeehan left a comment

Choose a reason for hiding this comment

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

Just one last nit, and I think good to merge.

@jaystarshot jaystarshot merged commit 77dfcc8 into prestodb:master Dec 13, 2023
@wanglinsong wanglinsong mentioned this pull request Feb 12, 2024
64 tasks
Copy link
Contributor

@rschlussel rschlussel left a comment

Choose a reason for hiding this comment

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

Left a few comments for Presto style and best practices that are likely to come up elsewhere in the future.

String cteName)
{
super(sourceLocation, id, statsEquivalentPlanNode);
this.cteName = cteName;
Copy link
Contributor

Choose a reason for hiding this comment

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

Always check requireNonNull() when setting Object fields in a constructor. (sometimes other argument checks are appropriate as well, but we almost never allow nulls. Generally we enforce this in constructors, and then elsewhere assume that nothing is null.)

public class NestedCteStack
{
@VisibleForTesting
public static final String delimiter = "_*%$_";
Copy link
Contributor

Choose a reason for hiding this comment

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

naming convention note: static final fields should be in all caps (so DELIMETER)


public NestedCteStack()
{
this.cteStack = new Stack<>();
Copy link
Contributor

Choose a reason for hiding this comment

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

initialize these where they are declared. They don't need anything from the constructor.


public void push(String cteName, Query query)
{
this.cteStack.push(cteName);
Copy link
Contributor

Choose a reason for hiding this comment

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

style note: we generally only use the "this" prefix in the constructor or setters. Otherwise, we refer to fields without the "this" prefix as long as there is no conflict requiring it. (not sure why, i think just to be concise).

PlanNode primarySource)
{
super(sourceLocation, planNodeId, statsEquivalentPlanNode);
this.cteProducers = leftList;
Copy link
Contributor

Choose a reason for hiding this comment

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

when setting a field to a collection passed in as input, make a defensive copy using ImmutableList.copyOf(). If it's already an immutable list this will be a no-op. (these should have requireNonNullChecks too)

{
List<PlanNode> children = new ArrayList<>(cteProducers);
children.add(primarySource);
return children;
Copy link
Contributor

Choose a reason for hiding this comment

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

use immutable collections whenever possible. It prevents bugs caused by things being modified unexpectedly. mutable collections should only be used when you plan something to be modified, and should generally stay local to wherever they are.

Here you could do

return ImmutableList.builder()
.addAll(cteProducers);
.add(primarySource)
.build();

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants