Skip to content
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

Support table handles with columns in JDBC applyProjection #14770

Merged
merged 1 commit into from
Oct 28, 2022

Conversation

kokosing
Copy link
Member

Support table handles with columns in JDBC applyProjection

It may happen fresh table handle comes with a columns prepared
already. The BaseJdbcClient#getTableHandle may return table handle with
columns included. That way it is possible to skip another remote metadata call
in BaseJdbcClient#getColumns.

However, now typically JDBC connectors do not provide table columns in
table handle by default so the verify in applyProjection was very
strict and assumed that applyProjection is not going to add any new
column to table handle. However it may happen that applyProjection
may want to add UPDATE_ROW_ID id, which is created later during the
planning for UPDATE and DELETE statements. This column is artificial,
and it is used only in planning.

@cla-bot cla-bot bot added the cla-signed label Oct 26, 2022
@kokosing kokosing requested review from ssheikin and findepi October 26, 2022 15:30
Comment on lines 265 to 269
Set<JdbcColumnHandle> newColumnSet = newColumns.stream()
// It may happen fresh table handle comes with a columns prepared already.
// In such case it may happen that applyProjection may want to add UPDATE_ROW_ID id, which is created later during the planning.
.filter(column -> !column.getColumnName().equals(UPDATE_ROW_ID))
.collect(toImmutableSet());
Copy link
Contributor

Choose a reason for hiding this comment

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

Move your changes just before verify.

Why?
2022-10-26T13:13:38.567-0500	ERROR	pool-3-thread-1	io.trino.testng.services.ProgressLoggingListener	[TEST FAILURE] io.trino.faulttolerant.postgresql.TestPostgresTaskFailureRecoveryTest.testDelete; (took: 3 minutes and 1 seconds)
io.trino.testing.QueryFailedException: The optimizer exhausted the time limit of 180000 ms: Top rules: {
  	io.trino.sql.planner.iterative.rule.PushProjectionIntoTableScan@3ed486ed: 51209 ms, 2133950 invocations, 2133950 applications,
  	io.trino.sql.planner.iterative.rule.PruneTableScanColumns@54c3ba96: 5112 ms, 2133950 invocations, 0 applications,
  	io.trino.sql.planner.iterative.rule.PushProjectionThroughUnion@16d94047: 2772 ms, 2133950 invocations, 0 applications,
  	io.trino.sql.planner.iterative.rule.PruneAggregationColumns@53236a7d: 2770 ms, 2133950 invocations, 0 applications,
  	io.trino.sql.planner.iterative.rule.PushDownDereferenceThroughJoin@c0241fd: 2327 ms, 2133950 invocations, 0 applications }
  at io.trino.testing.AbstractTestingTrinoClient.execute(AbstractTestingTrinoClient.java:122)
  at io.trino.testing.DistributedQueryRunner.executeWithQueryId(DistributedQueryRunner.java:496)
  at io.trino.faulttolerant.BaseFailureRecoveryTest$FailureRecoveryAssert.execute(BaseFailureRecoveryTest.java:599)
  at io.trino.faulttolerant.BaseFailureRecoveryTest$FailureRecoveryAssert.executeExpected(BaseFailureRecoveryTest.java:560)
  at io.trino.faulttolerant.BaseFailureRecoveryTest$FailureRecoveryAssert.isCoordinatorOnly(BaseFailureRecoveryTest.java:638)
  at io.trino.faulttolerant.jdbc.BaseJdbcFailureRecoveryTest.testDelete(BaseJdbcFailureRecoveryTest.java:73)
  at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
  at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
  at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
  at java.base/java.lang.reflect.Method.invoke(Method.java:568)
  at org.testng.internal.MethodInvocationHelper.invokeMethod(MethodInvocationHelper.java:104)
  at org.testng.internal.Invoker.invokeMethod(Invoker.java:645)
  at org.testng.internal.Invoker.invokeTestMethod(Invoker.java:851)
  at org.testng.internal.Invoker.invokeTestMethods(Invoker.java:1177)
  at org.testng.internal.TestMethodWorker.invokeTestMethods(TestMethodWorker.java:129)
  at org.testng.internal.TestMethodWorker.run(TestMethodWorker.java:112)
  at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136)
  at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
  at java.base/java.lang.Thread.run(Thread.java:833)
Caused by: io.trino.spi.TrinoException: The optimizer exhausted the time limit of 180000 ms: Top rules: {
  	io.trino.sql.planner.iterative.rule.PushProjectionIntoTableScan@3ed486ed: 51209 ms, 2133950 invocations, 2133950 applications,
  	io.trino.sql.planner.iterative.rule.PruneTableScanColumns@54c3ba96: 5112 ms, 2133950 invocations, 0 applications,
  	io.trino.sql.planner.iterative.rule.PushProjectionThroughUnion@16d94047: 2772 ms, 2133950 invocations, 0 applications,
  	io.trino.sql.planner.iterative.rule.PruneAggregationColumns@53236a7d: 2770 ms, 2133950 invocations, 0 applications,
  	io.trino.sql.planner.iterative.rule.PushDownDereferenceThroughJoin@c0241fd: 2327 ms, 2133950 invocations, 0 applications }
  at io.trino.sql.planner.iterative.IterativeOptimizer$Context.checkTimeoutNotExhausted(IterativeOptimizer.java:380)
  at io.trino.sql.planner.iterative.IterativeOptimizer.exploreNode(IterativeOptimizer.java:157)
  at io.trino.sql.planner.iterative.IterativeOptimizer.exploreGroup(IterativeOptimizer.java:133)
  at io.trino.sql.planner.iterative.IterativeOptimizer.exploreChildren(IterativeOptimizer.java:252)
  at io.trino.sql.planner.iterative.IterativeOptimizer.exploreGroup(IterativeOptimizer.java:135)
  at io.trino.sql.planner.iterative.IterativeOptimizer.exploreChildren(IterativeOptimizer.java:252)
  at io.trino.sql.planner.iterative.IterativeOptimizer.exploreGroup(IterativeOptimizer.java:135)
  at io.trino.sql.planner.iterative.IterativeOptimizer.exploreChildren(IterativeOptimizer.java:252)
  at io.trino.sql.planner.iterative.IterativeOptimizer.exploreGroup(IterativeOptimizer.java:135)
  at io.trino.sql.planner.iterative.IterativeOptimizer.optimize(IterativeOptimizer.java:118)
  at io.trino.sql.planner.LogicalPlanner.plan(LogicalPlanner.java:247)
  at io.trino.sql.planner.LogicalPlanner.plan(LogicalPlanner.java:222)
  at io.trino.sql.planner.LogicalPlanner.plan(LogicalPlanner.java:217)
  at io.trino.execution.SqlQueryExecution.doPlanQuery(SqlQueryExecution.java:475)
  at io.trino.execution.SqlQueryExecution.planQuery(SqlQueryExecution.java:456)
  at io.trino.execution.SqlQueryExecution.start(SqlQueryExecution.java:397)
  at io.trino.execution.SqlQueryManager.createQuery(SqlQueryManager.java:249)
  at io.trino.dispatcher.LocalDispatchQuery.lambda$startExecution$7(LocalDispatchQuery.java:143)
  at io.trino.$gen.Trino_testversion____20221026_180846_142.run(Unknown Source)
  at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136)
  at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
  at java.base/java.lang.Thread.run(Thread.java:833)

@@ -100,6 +101,7 @@
implements JdbcMetadata
{
private static final String SYNTHETIC_COLUMN_NAME_PREFIX = "_pfgnrtd_";
private static final String UPDATE_ROW_ID = "_trino_artificial_column_handle_for_update_row_id_";
Copy link
Contributor

Choose a reason for hiding this comment

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

Isn't it too long?

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 don't think so. It is that long to make sure that we don't hit a conflict.

Comment on lines 267 to 268
// In such case it may happen that applyProjection may want to add UPDATE_ROW_ID id, which is created later during the planning.
.filter(column -> !column.getColumnName().equals(UPDATE_ROW_ID))
Copy link
Member

Choose a reason for hiding this comment

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

What about other synthetic handles like DELETE_ROW_ID, MERGE_ROW_ID? We don't care yet since DefaultJdbcMetadata doesn't provide them (via getDeleteRowIdColumnHandle)?

Copy link
Contributor

Choose a reason for hiding this comment

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

There is no constant name for them, because they don't have implementation.

Copy link
Member Author

Choose a reason for hiding this comment

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

It is not yet implemented yet.

Set<JdbcColumnHandle> newColumnSet = newColumns.stream()
// It may happen fresh table handle comes with a columns prepared already.
// In such case it may happen that applyProjection may want to add UPDATE_ROW_ID id, which is created later during the planning.
.filter(column -> !column.getColumnName().equals(UPDATE_ROW_ID))
Copy link
Contributor

Choose a reason for hiding this comment

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

UPDATE_ROW_ID could be different for different connectors. So this will not work for some, which have getDeleteRowIdColumnHandle overriden.
Please consider

            Set<JdbcColumnHandle> tableSyntheticColumnSet = ImmutableSet.<JdbcColumnHandle>builder()
                    .addAll(tableColumnSet)
                    .add((JdbcColumnHandle) getDeleteRowIdColumnHandle(session, table))
                    .add((JdbcColumnHandle) getMergeRowIdColumnHandle(session, table))
                    .add((JdbcColumnHandle) getUpdateRowIdColumnHandle(session, table, ImmutableList.of())) // not sure about ImmutableList.of()
                    .build();

            verify(tableSyntheticColumnSet.containsAll(newColumnSet), "applyProjection called with columns %s and some are not available in existing query: %s", newColumnSet, tableSyntheticColumnSet);

Copy link
Contributor

Choose a reason for hiding this comment

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

and actually these will fail:

          .add((JdbcColumnHandle) getMergeRowIdColumnHandle(session, table))
          .add((JdbcColumnHandle) getUpdateRowIdColumnHandle(session, table, ImmutableList.of())) // not sure about ImmutableList.of()

because they throw new TrinoException(NOT_SUPPORTED

Copy link
Contributor

@ssheikin ssheikin Oct 27, 2022

Choose a reason for hiding this comment

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

So may be add a new public method List<String> getSyntacticColumnNames()
and .filter(column -> !getSyntacticColumnNames().contains(column.getColumnName())

Copy link
Member Author

Choose a reason for hiding this comment

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

Let's not overcomplicate things

It may happen fresh table handle comes with a columns prepared
already. The BaseJdbcClient#getTableHandle may return table handle with
columns included. That way it is possible to skip another remote metadata call
in BaseJdbcClient#getColumns.

However, now typically JDBC connectors do not provide table columns in
table handle by default so the verify in applyProjection was very
strict and assumed that applyProjection is not going to add any new
column to table handle. However it may happen that applyProjection
may want to add UPDATE_ROW_ID id, which is created later during the
planning for UPDATE and DELETE statements. This column is artificial,
and it is used only in planning.

Co-authored-by: Sasha Sheikin <myminitrue@gmail.com>
@kokosing kokosing force-pushed the origin/master/156_verify branch from 2eea471 to 814d8cf Compare October 27, 2022 11:21
@kokosing kokosing merged commit 3d6db08 into trinodb:master Oct 28, 2022
@kokosing kokosing deleted the origin/master/156_verify branch October 28, 2022 10:57
@github-actions github-actions bot added this to the 402 milestone Oct 28, 2022
@colebow
Copy link
Member

colebow commented Nov 2, 2022

Does this require a release note?

@kokosing
Copy link
Member Author

kokosing commented Nov 2, 2022

No release notes.

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

Successfully merging this pull request may close these issues.

5 participants