-
Notifications
You must be signed in to change notification settings - Fork 3k
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
Introduce projection pushdown framework for JDBC connectors #22203
Introduce projection pushdown framework for JDBC connectors #22203
Conversation
ImmutableSet.Builder<JdbcColumnHandle> columnsBuilder = ImmutableSet.builder(); | ||
ImmutableList.Builder<Assignment> assignmentBuilder = ImmutableList.builder(); | ||
ImmutableMap.Builder<ConnectorExpression, Variable> syntheticVariablesBuilder = ImmutableMap.builder(); | ||
ImmutableMap.Builder<String, ParameterizedExpression> columnExpressionsBuilder = ImmutableMap.builder(); | ||
for (ConnectorExpression child : projection.getChildren()) { | ||
RewrittenExpression rewrittenExpression = rewriteExpression( | ||
session, | ||
nextSyntheticColumnId, | ||
child, | ||
assignments, | ||
translatedExpression); | ||
nextSyntheticColumnId = rewrittenExpression.nextSyntheticColumnId; | ||
columnsBuilder.addAll(rewrittenExpression.columnHandles); | ||
assignmentBuilder.addAll(rewrittenExpression.assignments); | ||
syntheticVariablesBuilder.putAll(rewrittenExpression.syntheticVariables); | ||
columnExpressionsBuilder.putAll(rewrittenExpression.columnExpressions); |
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.
Too many objects might be created - I'm not sure if we could pass builder as arguments here.
This PR is quite timely as I was looking at the possibility to do something similar today. Thanks @Praveen2112. My primary use case is pushdown of JSON field extraction which causes a significant speedup to the overall query. However ideally it'd be good to enable pushdown of all functions that the connector source supports where the expression matches that of Trino's own. It'd be really helpful if function pushdown could be easily reusable between |
...o-postgresql/src/main/java/io/trino/plugin/postgresql/rule/RewriteStringReverseFunction.java
Outdated
Show resolved
Hide resolved
...o-postgresql/src/main/java/io/trino/plugin/postgresql/rule/RewriteStringReverseFunction.java
Show resolved
Hide resolved
...o-postgresql/src/main/java/io/trino/plugin/postgresql/rule/RewriteStringReverseFunction.java
Show resolved
Hide resolved
ImmutableMap.Builder<String, ParameterizedExpression> columnExpressionsBuilder = ImmutableMap.builder(); | ||
Set<ConnectorExpression> translatedExpression = new HashSet<>(); | ||
|
||
if (isComplexExpressionPushdown(session)) { |
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.
This could be a first statement in the method, and if false - return Optional.empty();
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 don't think we need to return Optional#empty
as we need to additional changes for normal expression.
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.
return Optional.empty();
if if (!newVariables.isEmpty()) {
is false.
if (!newVariables.isEmpty()) {
is true only if newVariables
is populated.
newVariables
is populated only if newVariablesBuilder
is populated.
newVariablesBuilder
is populated only when if (isComplexExpressionPushdown(session)) {
is true.
What do I miss?
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.
Okay !! I get it - Previously I was a bit confused as this code snippet was a part of applyProjection
. Thanks for pointing it out.
nextSyntheticColumnId = rewrittenExpression.nextSyntheticColumnId; | ||
newColumnsBuilder.addAll(rewrittenExpression.columnHandles); | ||
assignmentBuilder.addAll(rewrittenExpression.assignments); | ||
newVariablesBuilder.putAll(rewrittenExpression.syntheticVariables); | ||
columnExpressionsBuilder.putAll(rewrittenExpression.columnExpressions); |
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.
no need in this assignments and builders. Just use rewrittenExpression
if (isComplexExpressionPushdown(session)) { | ||
for (ConnectorExpression projection : projections) { | ||
RewrittenExpression rewrittenExpression = rewriteExpression(session, nextSyntheticColumnId, projection, assignments, translatedExpression); | ||
nextSyntheticColumnId = rewrittenExpression.nextSyntheticColumnId; |
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.
nextSyntheticColumnId -> nextSyntheticColumnId()
plugin/trino-base-jdbc/src/main/java/io/trino/plugin/jdbc/DefaultJdbcMetadata.java
Outdated
Show resolved
Hide resolved
ImmutableList.Builder<Assignment> assignmentBuilder = ImmutableList.builder(); | ||
ImmutableMap.Builder<ConnectorExpression, Variable> newVariablesBuilder = ImmutableMap.builder(); | ||
ImmutableMap.Builder<String, ParameterizedExpression> columnExpressionsBuilder = ImmutableMap.builder(); | ||
Set<ConnectorExpression> translatedExpression = new HashSet<>(); |
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 didn't grasp the PR fully yet, but for now it looks like this variable could have a little bit more ugly name, to gather more attention. E.g. translatedExpressionAccumulator
plugin/trino-base-jdbc/src/main/java/io/trino/plugin/jdbc/DefaultJdbcMetadata.java
Outdated
Show resolved
Hide resolved
// If the parent expression cannot be translated try translating its argument | ||
ImmutableSet.Builder<JdbcColumnHandle> columnsBuilder = ImmutableSet.builder(); | ||
ImmutableList.Builder<Assignment> assignmentBuilder = ImmutableList.builder(); | ||
ImmutableMap.Builder<ConnectorExpression, Variable> syntheticVariablesBuilder = ImmutableMap.builder(); | ||
ImmutableMap.Builder<String, ParameterizedExpression> columnExpressionsBuilder = ImmutableMap.builder(); | ||
for (ConnectorExpression child : projection.getChildren()) { | ||
RewrittenExpression rewrittenExpression = rewriteExpression( | ||
session, | ||
nextSyntheticColumnId, | ||
child, | ||
assignments, | ||
translatedExpression); | ||
nextSyntheticColumnId = rewrittenExpression.nextSyntheticColumnId; | ||
columnsBuilder.addAll(rewrittenExpression.columnHandles); | ||
assignmentBuilder.addAll(rewrittenExpression.assignments); | ||
syntheticVariablesBuilder.putAll(rewrittenExpression.syntheticVariables); | ||
columnExpressionsBuilder.putAll(rewrittenExpression.columnExpressions); | ||
} | ||
|
||
return new RewrittenExpression( | ||
nextSyntheticColumnId, | ||
syntheticVariablesBuilder.buildOrThrow(), | ||
columnExpressionsBuilder.buildOrThrow(), | ||
columnsBuilder.build(), | ||
assignmentBuilder.build()); | ||
} |
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.
Looks like it can be extacted to a separate commit
assignmentBuilder.build()); | ||
} | ||
|
||
public record RewrittenExpression( |
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.
Should we move it to the very bottom of the class?
lib/trino-plugin-toolkit/src/main/java/io/trino/plugin/base/projection/ProjectFunctionRule.java
Show resolved
Hide resolved
skimmed |
31d24f6
to
45a08c7
Compare
@ssheikin Thanks a lot for the review. AC |
45a08c7
to
31c973c
Compare
The change may cause regression when |
The same can be restricted via rules right ? |
31c973c
to
7735be0
Compare
@ebyhr Thank you for pointing the regression. Have fixed it and also added a test case to verify it doesn't break for columns with special data type. |
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
ImmutableMap.Builder<ConnectorExpression, Variable> newVariablesBuilder = ImmutableMap.builder(); | ||
ImmutableMap.Builder<String, ParameterizedExpression> columnExpressionsBuilder = ImmutableMap.builder(); | ||
|
||
for (ConnectorExpression projection : ImmutableSet.copyOf(projections)) { |
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: Maybe it's cheaper to copy to ImmutableList, instead of Set, as projections
is a list, which may be ImmutableList too, avoiding actual copying.
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.
This would allow to skip processing redundant expression - if propagated
plugin/trino-base-jdbc/src/main/java/io/trino/plugin/jdbc/DefaultJdbcMetadata.java
Outdated
Show resolved
Hide resolved
plugin/trino-base-jdbc/src/main/java/io/trino/plugin/jdbc/DefaultJdbcMetadata.java
Outdated
Show resolved
Hide resolved
...n/trino-postgresql/src/test/java/io/trino/plugin/postgresql/TestPostgreSqlConnectorTest.java
Outdated
Show resolved
Hide resolved
plugin/trino-base-jdbc/src/main/java/io/trino/plugin/jdbc/DefaultJdbcMetadata.java
Outdated
Show resolved
Hide resolved
plugin/trino-base-jdbc/src/main/java/io/trino/plugin/jdbc/DefaultJdbcMetadata.java
Outdated
Show resolved
Hide resolved
@Praveen2112 would you mind adding more hands-on details on the description of the PR about the problem that you're solving? Also helpful would be potentially to share an EXPLAIN plan of a query with and without your changes. |
7735be0
to
054aa3e
Compare
@findinpath Yeah sure !! Would add the |
|
||
List<Assignment> outputAssignments = assignmentBuilder.build(); | ||
|
||
if (newVariables.isEmpty()) { |
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.
may be moved even upper.
054aa3e
to
6662a68
Compare
@ssheikin Thanks for the review. AC |
...o-postgresql/src/main/java/io/trino/plugin/postgresql/rule/RewriteStringReverseFunction.java
Outdated
Show resolved
Hide resolved
...o-postgresql/src/main/java/io/trino/plugin/postgresql/rule/RewriteStringReverseFunction.java
Outdated
Show resolved
Hide resolved
We use this framework for pushing down `REVERSE` function to PostgreSql connector. This framework doesn't support partial pushdown of expressions.
6662a68
to
696532c
Compare
@ebyhr Thanks a lot for the review - AC |
@findinpath I have updated the description with the changes in the plan as well |
Description
This framework would allow us to pushdown specific functions closer to the JDBC datasource. We use this function to pushdown
reverse
function in case of PostgreSQL connector. In order to push down a specific function to the underlying JDBC datasource, we need two partsconvertProjection
ProjectFunctionRule
specific to the function to be pushed down.We also support partial pushdown of expression i.e
LOWER(REVERSE(varchar_col))
will be translated intoLOWER(synthetic_col_from_query)
Spl thanks to @SemionPar for inspiring me to use
reverse
function for this framework.Additional context and related issues
Without this change when we try to run a query like this
Current master
With this optimization
Release notes
( ) This is not user-visible or is docs only, and no release notes are required.
(x) Release notes are required. Please propose a release note for me.
( ) Release notes are required, with the following suggested text: