Skip to content

fix(metadata): drop removed schema ids from the builder's schema index - #1012

Merged
wgtmac merged 2 commits into
apache:mainfrom
LuciferYang:fix/sweep-19-removeschemas-stale-index
Oct 21, 2026
Merged

wgtmac merged 2 commits into
apache:mainfrom
LuciferYang:fix/sweep-19-removeschemas-stale-index

Conversation

@LuciferYang

@LuciferYang LuciferYang commented Oct 14, 2026 •

Copy link
Copy Markdown
Contributor

What

TableMetadataBuilder::Impl::RemoveSchemas removed schemas from metadata_.schemas but left them in schemas_by_id_, so later calls on the same builder still resolved the removed ids.

Transaction applies all of its updates to one builder, so this was reachable through the public API. Take a table whose current schema is 0 and whose schema 1 is unused, for example because a column was added and then dropped. In one transaction, ExpireSnapshots with CleanExpiredMetadata(true) removes schema 1, and UpdateSchema::AddColumn then produces a new schema that gets the freed id 1. AddSchema finds id 1 in the stale index and does not add the new schema to schemas, and SetCurrentSchema(1) picks up the removed schema instead. Without this fix the commit succeeds and writes current-schema-id: 1 with only schema 0 in schemas, and the next LoadTable fails with Cannot find schema with current-schema-id=1.

The same stale entry let SetCurrentSchema accept a removed id directly, so a catalog commit carrying [remove-schemas, set-current-schema] for that id wrote the same kind of file.

Closes #1011.

How

Erase the removed ids from schemas_by_id_ alongside the existing filter of metadata_.schemas.

This is stricter than Java. Java's Builder.removeSchemas leaves schemasById stale, so setCurrentSchema on a removed id still succeeds there, and build() returns metadata whose current-schema-id is not in schemas. In C++ the same sequence makes Build() fail with Cannot set current schema to unknown schema.

Testing

  • ExpireUnusedSchemaThenAddColumn in transaction_test.cc runs the transaction above against InMemoryCatalog. Without the fix Commit() succeeds and reloading the table fails with Cannot find schema with current-schema-id=1. With the fix the reloaded current schema is 1 and has the added column.
  • RemoveSchemasDropsIndexEntry in table_metadata_builder_test.cc: after RemoveSchemas({1}), SetCurrentSchema(1) makes Build() fail with Cannot set current schema to unknown schema: 1. Without the fix Build() returns metadata with the dangling id.
  • AddSchemaAfterRemoveNotConfusedByStaleIndex in table_metadata_builder_test.cc: re-adding the removed schema puts it back in schemas. Without the fix the stale index entry makes AddSchema skip it and schemas stays at one entry.

RemovePartitionSpecs has the same gap with specs_by_id_; I'll send that separately.

RemoveSchemas filtered metadata_.schemas but left schemas_by_id_
untouched, so within one builder session the removed ids stayed
resolvable: SetCurrentSchema on a removed id passed its existence
check and Build() emitted metadata whose current-schema-id dangles
outside schemas, and a later AddSchema reuse of the id took the
update path against a schema the metadata no longer contains. Erase
the removed ids from the index alongside the vector filter.
Copilot AI balanced review requested due to automatic review settings October 14, 2026 05:05

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@shangxinli

Copy link
Copy Markdown
Contributor

lgtm

}) |
std::ranges::to<std::vector<std::shared_ptr<Schema>>>();
for (const auto schema_id : schema_ids) {
schemas_by_id_.erase(schema_id);

@shangxinli shangxinli Oct 20, 2026 •

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.

This departs from Java - TableMetadata.java 1169-1183 leaves schemasById stale, so setCurrentSchema on a removed id still succeeds there. Looks right to me, just worth noting in the description.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Updated the How section. Java's build() doesn't catch the dangling id either, so the whole sequence goes through there.

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 you want to fix this on the Java side as well? @LuciferYang

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yes, I'll take the Java side too. I want to reproduce it with a Java test first, then I'll open an issue and PR on apache/iceberg and link them here.

Copilot AI balanced review requested due to automatic review settings October 21, 2026 06:20

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

}) |
std::ranges::to<std::vector<std::shared_ptr<Schema>>>();
for (const auto schema_id : schema_ids) {
schemas_by_id_.erase(schema_id);

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 you want to fix this on the Java side as well? @LuciferYang

@wgtmac
wgtmac merged commit 29900ca into apache:main Oct 21, 2026
15 checks passed
@wgtmac

wgtmac commented Oct 21, 2026

Copy link
Copy Markdown
Member

Thanks @LuciferYang for fixing this and @shangxinli for the review!

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.

bug: TableMetadataBuilder::RemoveSchemas leaves removed ids in the schema index

4 participants