Skip to content

Iceberg support partitioning on a nested field#9337

Closed
Buktoria wants to merge 1 commit intotrinodb:masterfrom
Buktoria:support-nested-partition-col-iceberg
Closed

Iceberg support partitioning on a nested field#9337
Buktoria wants to merge 1 commit intotrinodb:masterfrom
Buktoria:support-nested-partition-col-iceberg

Conversation

@Buktoria
Copy link
Copy Markdown
Member

@Buktoria Buktoria commented Sep 22, 2021

Problem

When querying an iceberg table which is partitioned, you can get the following error.
java.lang.IllegalArgumentException: columns is empty.

java.lang.IllegalArgumentException: columns is empty
	at io.trino.spi.connector.DiscretePredicates.<init>(DiscretePredicates.java:31)
	at io.trino.plugin.iceberg.IcebergMetadata.getTableProperties(IcebergMetadata.java:368)
	at io.trino.plugin.base.classloader.ClassLoaderSafeConnectorMetadata.getTableProperties(ClassLoaderSafeConnectorMetadata.java:764)
	at io.trino.metadata.MetadataManager.getTableProperties(MetadataManager.java:489)
	at io.trino.sql.planner.iterative.rule.DetermineTableScanNodePartitioning.apply(DetermineTableScanNodePartitioning.java:59)
	at io.trino.sql.planner.iterative.rule.DetermineTableScanNodePartitioning.apply(DetermineTableScanNodePartitioning.java:33)

...

Deep Dive into the problem

This columns value is returning an empty list

  // Extract identity partition columns
  Map<Integer, IcebergColumnHandle> columns = getColumns(icebergTable.schema(), typeManager).stream()
          .filter(column -> partitionSourceIds.contains(column.getId()))
          .collect(toImmutableMap(IcebergColumnHandle::getId, Function.identity()));

If we take a look at the getColumns we can see that this is a simple iteration over the return value of the schema.columns() from the iceberg api. The problem is that some of these fields can be nested. This method is not unpacking those columns.

public static List<IcebergColumnHandle> getColumns(Schema schema, TypeManager typeManager)
{
    return schema.columns().stream()
            .map(column -> IcebergColumnHandle.create(column, typeManager))
            .collect(toImmutableList());
}

Now go back to to see how that original columns variable is being generated we see a filter being applied, to the top level column that we got from getColumns.

  // Extract identity partition columns
  Map<Integer, IcebergColumnHandle> columns = getColumns(icebergTable.schema(), typeManager).stream()
          .filter(column -> partitionSourceIds.contains(column.getId()))
          .collect(toImmutableMap(IcebergColumnHandle::getId, Function.identity()));

It means that all columns get filtered out because none of those columns are the partition field, hence getting the columns is empty when building

discretePredicates = new DiscretePredicates(
        columns.values().stream()
                .map(ColumnHandle.class::cast)
                .collect(toImmutableList()),
        discreteTupleDomain);

and then error thrown here

  public DiscretePredicates(List<ColumnHandle> columns, Iterable<TupleDomain<ColumnHandle>> predicates)
  {
      requireNonNull(columns, "columns is null");
      if (columns.isEmpty()) {
          throw new IllegalArgumentException("columns is empty");

Solution

This pr does three things main things.

  • When getting table column handles from IcebergMetadata we get all column, including the ones that are nested.
  • When looking for Iceberg column partitions we dig out the partition column and store the index positions to get that column from the schema (sourceIds).
  • The Iceberg page partitioner also digs out the partition column block from a page with the stored sourceIds.

Closes: #5458

@cla-bot cla-bot bot added the cla-signed label Sep 22, 2021
@Buktoria Buktoria force-pushed the support-nested-partition-col-iceberg branch 4 times, most recently from 0fad4cf to 8113685 Compare September 23, 2021 19:09
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

use fixed time in tests to ensure the test is reproducible

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

please inline createTable and insertSql -- they are used once

(same below)

Comment on lines 1968 to 1969
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

assert on the result values, not just count

assert(query("...."))
  .matches("SELECT val1, val2")

Comment on lines 1944 to 1954
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

i would assume that in this test the partition field is all that matters; do we need to have all these data columns?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

how is second table different from the first?
ie what is the important difference?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

thanks for the test. please also add one in TestIcebergSparkCompatibility to make sure our nested-field-partitioned writes are correctly readable by spark

we should also test the reverse.

note that TestIcebergSparkCompatibility is a product test, so to run it you need

./mvnw clean install -DskipTests # project needs to be built first
bin/ptl test run --environment singlenode-spark-iceberg -- -t TestIcebergSparkCompatibility

Copy link
Copy Markdown
Member Author

@Buktoria Buktoria Sep 29, 2021

Choose a reason for hiding this comment

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

what is this bin/ptl command? I this something that should be created by that ./mvnw clean install -DskipTests command? There is no bin directory created after running the instal of the project on my side locally.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

It was moved into the testing directory ./testing/bin/ptl

Copy link
Copy Markdown
Member

@alexjo2144 alexjo2144 Sep 29, 2021

Choose a reason for hiding this comment

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

It'd be good to have a query with a WHERE clause in this test as well, to verify that the predicate pushdown on the nested column works properly. Pushdown on partition columns is guaranteed by the connector so the engine won't repeat the filter.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This should be more strict. For example, shouldn't match a...b.

IDENTIFIER = "[a-z_][a-z0-9_]*";
NAME = IDENTIFIER + "(\\." + NAME + ")*";

also, since we thus allow partitioning on a function from a field (like year(rec.timestamp)), we should add some test coverage for such usage too

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

in #9354 @djsstarburst argues that using getChildren is incorrect.

@djsstarburst would you want to comment here?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

same as above

@findepi
Copy link
Copy Markdown
Member

findepi commented Sep 23, 2021

@Buktoria than you for your PR!
added couple initial comments.

cc @losipiuk @alexjo2144

@Buktoria Buktoria force-pushed the support-nested-partition-col-iceberg branch from bddab80 to 64d4144 Compare January 7, 2022 19:51
@mosabua
Copy link
Copy Markdown
Member

mosabua commented Oct 28, 2022

👋 @Buktoria - this PR has become inactive. If you're still interested in working on it, please let us know, and we can try to get reviewers to help with that.

We're working on closing out old and inactive PRs, so if you're too busy or this has too many merge conflicts to be worth picking back up, we'll be making another pass to close it out in a few weeks.

@Buktoria
Copy link
Copy Markdown
Member Author

Buktoria commented Nov 1, 2022

Closing this, its too far behind to be able to merge.

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.

Add Iceberg tests for partition transforms on structured types

4 participants