Skip to content

feat: add Iceberg read support via source_format - #1100

Open
jjcfrancisco wants to merge 2 commits into
stac-utils:mainfrom
jjcfrancisco:iceberg-read-support
Open

feat: add Iceberg read support via source_format#1100
jjcfrancisco wants to merge 2 commits into
stac-utils:mainfrom
jjcfrancisco:iceberg-read-support

Conversation

@jjcfrancisco

@jjcfrancisco jjcfrancisco commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

Closes #942

Description

Add support for reading Iceberg via DuckDB. Driven by a new enum to choose between source format Parquet and Iceberg. Some hiccups around default enable_geoparquet_conversion as Iceberg (v2) declares geometry binary but DuckDB transforms to native geometry per geoparquet 1.2 (see apply_source_format_settings).

Checklist

  • Unit tests
  • Documentation, including doctests
  • Pull request title follows conventional commits
  • Pre-commit hooks pass (prek run --all-files)

@jjcfrancisco
jjcfrancisco force-pushed the iceberg-read-support branch from 1b722c2 to a937950 Compare August 8, 2026 20:32
@jjcfrancisco
jjcfrancisco marked this pull request as ready for review August 8, 2026 20:34
@jjcfrancisco
jjcfrancisco requested a review from gadomski as a code owner August 8, 2026 20:34

@gadomski gadomski left a comment

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 is great, thanks! One refactor (using a setter), then some nit-picky suggestions to remove inline comments and a couple other tweaks — thanks for tolerating my nits 🙇🏼!

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 don't think we need two scripts, we can use a #!/usr/bin/env python shebang on this script and put the Python here.

Comment on lines +8 to +9
import pyarrow as pa
import pyarrow.parquet as pq

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.

Nit: I know most folks (and their docs) use these abbreviations, but I prefer to just use the full names — letters are cheap 😄



def read_subset() -> pa.Table:
table = pq.read_table(SOURCE_PARQUET, columns=["id", "datetime", "collection", "eo:cloud_cover", "platform", "geometry"]).slice(0, 5) # Take 5 rows for the fixture

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.

The slice is clear enough, no comment needed.

Suggested change
table = pq.read_table(SOURCE_PARQUET, columns=["id", "datetime", "collection", "eo:cloud_cover", "platform", "geometry"]).slice(0, 5) # Take 5 rows for the fixture
table = pq.read_table(SOURCE_PARQUET, columns=["id", "datetime", "collection", "eo:cloud_cover", "platform", "geometry"]).slice(0, 5)


def main() -> None:
build_dir = FIXTURE_DIR.parent / "build"
# Checks for previous builds and deletes if so

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.

Suggested change
# Checks for previous builds and deletes if so

table_dir = build_iceberg_table(build_dir, data)
rewrite_paths(table_dir, FIXTURE_REL)

# Move the build to the fixture location and clean up the build dir

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.

Suggested change
# Move the build to the fixture location and clean up the build dir

Comment on lines +87 to +88
connection.execute("INSTALL iceberg", [])?;
connection.execute("LOAD iceberg", [])?;

@gadomski gadomski Aug 11, 2026

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 don't think we want to install iceberg unless we need it — maybe we make source_format a private attribute and use a setter to install the extension if the source-format is iceberg?

This has the added benefit of making this change non-API-breaking (adding a public attribute to a struct is a breaking change).

href: &str,
search: Search,
) -> Result<SearchArrowBatchIter<'conn>> {
self.apply_source_format_settings()?;

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.

Do we need this? Isn't this done in build_query?

Comment on lines +466 to +477
/// `enable_geoparquet_conversion` is disabled for Iceberg to avoid schema
/// mismatch and subsequent crash. For Parquet, the setting is reset to the default.
fn apply_source_format_settings(&self) -> Result<()> {
match self.source_format {
SourceFormat::Iceberg => {
self.execute("SET enable_geoparquet_conversion = false", [])?;
}
SourceFormat::Parquet => {
self.execute("RESET enable_geoparquet_conversion", [])?;
}
}
Ok(())

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.

Per the comment above, if we use a getter/setter for source_format, we can put this operation in that setter.

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.

Apache Iceberg read support

2 participants