Skip to content

Core: Add metadata table for discovering metadata tables#17134

Open
yangshangqing95 wants to merge 1 commit into
apache:mainfrom
yangshangqing95:feat/add-metadata-tables-table
Open

Core: Add metadata table for discovering metadata tables#17134
yangshangqing95 wants to merge 1 commit into
apache:mainfrom
yangshangqing95:feat/add-metadata-tables-table

Conversation

@yangshangqing95

Copy link
Copy Markdown

close #17132

Summary

This PR adds a new Iceberg metadata table, metadata_tables, to make available metadata tables discoverable through the existing metadata table mechanism.

For example, in Spark users can query:

SELECT * FROM catalog.db.table.metadata_tables;

Result:

entries
files
data_files
delete_files
history
metadata_log_entries
snapshots
refs
manifests
partitions
all_data_files
all_delete_files
all_files
all_manifests
all_entries
position_deletes
metadata_tables

The new metadata table returns the supported metadata table names

This avoids requiring users to know all metadata table names ahead of time from documentation or source code.

Also, replace selected hard-coded metadata table suffixes in tests and metadata table implementations with MetadataTableType#tableName, enhance code robustness.

@yangshangqing95 yangshangqing95 force-pushed the feat/add-metadata-tables-table branch 6 times, most recently from 44676e5 to 4c79db9 Compare July 8, 2026 19:59

AllDataFilesTable(Table table) {
this(table, table.name() + ".all_data_files");
this(table, table.name() + "." + MetadataTableType.ALL_DATA_FILES.tableName());

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

To reduce the size of this PR, would you mind moving this refactor and all of the similar replacements into a separate PR?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Yes, sure. These refactors are related to and required by the changes in this PR, though. Another option is that I can wait until this PR is merged, then submit the refactors separately in a follow-up PR. Would that work for you?

@gaborkaszab gaborkaszab left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Hi @yangshangqing95 ,
I took a Quick Look and left some comments wrt refactors and the new metadata scan implementation.

A general note: querying static data such as metadata table names through SELECT * FROM catalog.db.table.metadata_tables seems somewhat odd to me. We'd expect the same answer regardless the table we use for the query, right?
Anyway, I recall for Iceberg V4 there are consideration to not allow some of the current metadata table and have new ones because the physical representation of a table changes. So this might make sense to have different answers based on the table's version.
Let's see what others think!

}

private static final Schema METADATA_TABLES_SCHEMA =
new Schema(Types.NestedField.required(1, "metadata_table", Types.StringType.get()));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

"metadata_table_name" ?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

good idea.

return new StaticDataTask(metadataFile, schema(), scan.schema(), rows);
}

private class MetadataTableScan extends BaseMetadataTableScan {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Instead of MetadataTableScan maybe MetadataTablesScan or MetadataTableNameScan

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

okay

.map(metadataType -> StaticDataTask.Row.of(metadataType.tableName()))
.toArray(StaticDataTask.Row[]::new);

DataFile metadataFile =

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I randomly checked other metadata tables and they seem to use the following InputFile:
table().io().newInputFile(table().operations().current().metadataFileLocation()),

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

That’s a good point. For listing the available metadata tables, at least at this stage, we don’t actually need to read any physical file.

Using a directly constructed empty DataFile has a couple of benefits here:

  1. It avoids unnecessary file operations.
  2. It makes the intent clearer in the code, since otherwise it may look like the metadata table information is stored in or read from the table metadata file.

So I think using an empty DataFile is more explicit for this particular metadata table.

.withFileSizeInBytes(0)
.build();

return new StaticDataTask(metadataFile, schema(), scan.schema(), rows);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Other metadata tables use StaticDataTask.of()

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

same reason as the above one

}

private DataTask task(BaseTableScan scan) {
StaticDataTask.Row[] rows =

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Checking RefsTable instead of constructing StaticDataTask.Row[]', we can simply use Collectionwith a transform function to convert it to row viaStaticDataTask.Row.of()`

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Hi @gaborkaszab same reason with the usage of empty Datafile, as constructor accept parameter Row[]

StaticDataTask(
      DataFile metadataFile, 
      Schema tableSchema, 
      Schema projectedSchema, 
      StructLike[] rows)

}

private class MetadataTableScan extends BaseMetadataTableScan {
MetadataTableScan(Table table, Schema schema) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I don't think any of these constructors need the Schema parameter. See RefsTable.RefsTableScan

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

make sense, fixed

protected CloseableIterable<FileScanTask> doPlanFiles() {
return CloseableIterable.withNoopClose(task(this));
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

After reding this Scan class and comparing to others can't we simply do what PartitionsScan does?

PartitionsScan(Table table) {
      super(
          table,
          PartitionsTable.this.schema(),
          MetadataTableType.PARTITIONS,
          PartitionsTable.this::task);

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

make sense, fixed


// Expect that the new data is changed in the meantime
assertThat(deserializedFiles).isNotEqualTo(newFiles);
if (type == MetadataTableType.METADATA_TABLES) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

not entirely sure why the new table is any different than the other in this test.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Since reading the metadata tables does not require reading any actual files, the file path would remain unchanged regardless of how many commits are made. Therefore, this case needs to be handled separately.

Set.of(DataFile.FIRST_ROW_ID.fieldId()));
Schema filesTableSchema =
Spark3Util.loadIcebergTable(spark, tableName + ".all_data_files").schema();
Spark3Util.loadIcebergTable(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

These changes are pure refactors. Move to separate PR to reduce the noise as advised above.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

sure for the refactors I can submit a separate one when this PR get merged.

@yangshangqing95

Copy link
Copy Markdown
Author

Hi @yangshangqing95 , I took a Quick Look and left some comments wrt refactors and the new metadata scan implementation.

A general note: querying static data such as metadata table names through SELECT * FROM catalog.db.table.metadata_tables seems somewhat odd to me. We'd expect the same answer regardless the table we use for the query, right? Anyway, I recall for Iceberg V4 there are consideration to not allow some of the current metadata table and have new ones because the physical representation of a table changes. So this might make sense to have different answers based on the table's version. Let's see what others think!

Hi @gaborkaszab thanks for taking a look and leaving the comments!

Regarding the metadata table query, I did consider this initially as well. From both an implementation perspective and the current project structure, I’m still leaning toward exposing it under each table, mainly for a few reasons:

  1. We don’t currently have a default metadata catalog to store this kind of information, and introducing one just for this metadata feels a bit heavy.
  2. Given the current code structure, putting this in MetadataTableType keeps the change smaller and less intrusive.
  3. There may be more metadata table types in the future, and the supported set could differ across Iceberg versions such as V1, V2, V3, and V4. Keeping this under each table leaves room for future extension, where each table version can expose the metadata tables that are meaningful for it.

So from that perspective, it may not necessarily be true that all tables should always return the exact same metadata tables.

@yangshangqing95 yangshangqing95 force-pushed the feat/add-metadata-tables-table branch from 4c79db9 to fbd4c32 Compare July 9, 2026 20:17
@yangshangqing95 yangshangqing95 force-pushed the feat/add-metadata-tables-table branch from fbd4c32 to ef31403 Compare July 9, 2026 20:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Core: Add metadata table for discovering metadata tables

2 participants