From 6a39da3630159c66a107859c09dc17e952d08aeb Mon Sep 17 00:00:00 2001 From: Minh Vu Date: Sat, 27 Jun 2026 12:29:34 +0200 Subject: [PATCH 1/2] fix(table): reject nil uuid assignment updates --- table/metadata.go | 12 ++++++++---- table/metadata_builder_internal_test.go | 14 ++++++++++++++ table/updates_test.go | 12 ++++++++++++ 3 files changed, 34 insertions(+), 4 deletions(-) diff --git a/table/metadata.go b/table/metadata.go index 35a2e3f68..51bd753f8 100644 --- a/table/metadata.go +++ b/table/metadata.go @@ -902,13 +902,17 @@ func (b *MetadataBuilder) RemoveSnapshotRef(name string) error { return nil } -func (b *MetadataBuilder) SetUUID(uuid uuid.UUID) error { - if b.uuid == uuid { +func (b *MetadataBuilder) SetUUID(newUUID uuid.UUID) error { + if newUUID == uuid.Nil { + return errors.New("cannot set uuid to null") + } + + if b.uuid == newUUID { return nil } - b.updates = append(b.updates, NewAssignUUIDUpdate(uuid)) - b.uuid = uuid + b.updates = append(b.updates, NewAssignUUIDUpdate(newUUID)) + b.uuid = newUUID return nil } diff --git a/table/metadata_builder_internal_test.go b/table/metadata_builder_internal_test.go index 327d18077..d4af6d351 100644 --- a/table/metadata_builder_internal_test.go +++ b/table/metadata_builder_internal_test.go @@ -2317,6 +2317,20 @@ func TestSetFormatVersionPreservesExistingUUID(t *testing.T) { require.Equal(t, existingUUID, meta.TableUUID()) } +func TestSetUUIDRejectsNil(t *testing.T) { + builder := builderWithoutChanges(2) + originalUUID := builder.uuid + + err := builder.SetUUID(uuid.Nil) + require.ErrorContains(t, err, "cannot set uuid to null") + require.False(t, builder.HasChanges()) + require.Equal(t, originalUUID, builder.uuid) + + meta, err := builder.Build() + require.NoError(t, err) + require.Equal(t, originalUUID, meta.TableUUID()) +} + func TestSetFormatVersionDowngradeNotAllowed(t *testing.T) { builder := builderWithoutChanges(2) err := builder.SetFormatVersion(1) diff --git a/table/updates_test.go b/table/updates_test.go index 3273713b0..ccd37d228 100644 --- a/table/updates_test.go +++ b/table/updates_test.go @@ -569,6 +569,18 @@ func buildFromBase(t *testing.T) *MetadataBuilder { return b } +func TestAssignUUIDUpdate_ApplyRejectsNilUUID(t *testing.T) { + b := buildFromBase(t) + + err := NewAssignUUIDUpdate(uuid.Nil).Apply(b) + require.ErrorContains(t, err, "cannot set uuid to null") + require.False(t, b.HasChanges()) + + meta, err := b.Build() + require.NoError(t, err) + require.NotEqual(t, uuid.Nil, meta.TableUUID()) +} + func TestSetStatisticsUpdate_Unmarshal(t *testing.T) { data := []byte(`[{ "action": "set-statistics", From 33c5a220743f11bb4c3ad7018e1dfb0768203d60 Mon Sep 17 00:00:00 2001 From: Minh Vu Date: Mon, 29 Jun 2026 23:54:56 +0200 Subject: [PATCH 2/2] fix(table): classify nil uuid assignment errors --- table/metadata.go | 2 +- table/metadata_builder_internal_test.go | 12 +++++++++++- table/updates_test.go | 5 +++-- 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/table/metadata.go b/table/metadata.go index 51bd753f8..e393214d6 100644 --- a/table/metadata.go +++ b/table/metadata.go @@ -904,7 +904,7 @@ func (b *MetadataBuilder) RemoveSnapshotRef(name string) error { func (b *MetadataBuilder) SetUUID(newUUID uuid.UUID) error { if newUUID == uuid.Nil { - return errors.New("cannot set uuid to null") + return fmt.Errorf("%w: cannot set uuid to nil", iceberg.ErrInvalidArgument) } if b.uuid == newUUID { diff --git a/table/metadata_builder_internal_test.go b/table/metadata_builder_internal_test.go index d4af6d351..a0ca6cda1 100644 --- a/table/metadata_builder_internal_test.go +++ b/table/metadata_builder_internal_test.go @@ -2322,7 +2322,7 @@ func TestSetUUIDRejectsNil(t *testing.T) { originalUUID := builder.uuid err := builder.SetUUID(uuid.Nil) - require.ErrorContains(t, err, "cannot set uuid to null") + require.ErrorIs(t, err, iceberg.ErrInvalidArgument) require.False(t, builder.HasChanges()) require.Equal(t, originalUUID, builder.uuid) @@ -2331,6 +2331,16 @@ func TestSetUUIDRejectsNil(t *testing.T) { require.Equal(t, originalUUID, meta.TableUUID()) } +func TestNewMetadataWithUUIDGeneratesUUIDForNil(t *testing.T) { + tableSchema := schema() + partSpec := partitionSpec() + order := sortOrder() + + meta, err := NewMetadataWithUUID(&tableSchema, &partSpec, order, "s3://bucket/test/location", nil, uuid.Nil) + require.NoError(t, err) + require.NotEqual(t, uuid.Nil, meta.TableUUID()) +} + func TestSetFormatVersionDowngradeNotAllowed(t *testing.T) { builder := builderWithoutChanges(2) err := builder.SetFormatVersion(1) diff --git a/table/updates_test.go b/table/updates_test.go index ccd37d228..24a60e7e0 100644 --- a/table/updates_test.go +++ b/table/updates_test.go @@ -571,14 +571,15 @@ func buildFromBase(t *testing.T) *MetadataBuilder { func TestAssignUUIDUpdate_ApplyRejectsNilUUID(t *testing.T) { b := buildFromBase(t) + originalUUID := b.uuid err := NewAssignUUIDUpdate(uuid.Nil).Apply(b) - require.ErrorContains(t, err, "cannot set uuid to null") + require.ErrorIs(t, err, iceberg.ErrInvalidArgument) require.False(t, b.HasChanges()) meta, err := b.Build() require.NoError(t, err) - require.NotEqual(t, uuid.Nil, meta.TableUUID()) + require.Equal(t, originalUUID, meta.TableUUID()) } func TestSetStatisticsUpdate_Unmarshal(t *testing.T) {