You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@iceberg.apache.org by at...@apache.org on 2026/08/10 09:27:33 UTC
(iceberg-go) branch main updated: fix(table): read legacy property update field names (#1725)
This is an automated email from the ASF dual-hosted git repository.
laskoviymishka pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/iceberg-go.git
The following commit(s) were added to refs/heads/main by this push:
new 517c5dea5 fix(table): read legacy property update field names (#1725)
517c5dea5 is described below
commit 517c5dea5ddb5f4a707024004298f36ebfaa44c3
Author: Minh Vu <vu...@gmail.com>
AuthorDate: Mon Aug 10 11:27:27 2026 +0200
fix(table): read legacy property update field names (#1725)
* fix(table): accept legacy property update fields
* test(table): cover legacy property update edge cases
---
table/updates.go | 31 ++++++++++++++++++++++
table/updates_test.go | 71 +++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 102 insertions(+)
diff --git a/table/updates.go b/table/updates.go
index d27f8b31b..d59c66a47 100644
--- a/table/updates.go
+++ b/table/updates.go
@@ -100,6 +100,32 @@ var requiredUpdateFields = map[string][]string{
UpdateRemoveEncryptionKey: {"key-id"},
}
+func normalizeLegacyPropertyFields(action string, raw json.RawMessage) (json.RawMessage, error) {
+ var modern, legacy string
+ switch action {
+ case UpdateSetProperties:
+ modern, legacy = "updates", "updated"
+ case UpdateRemoveProperties:
+ modern, legacy = "removals", "removed"
+ default:
+ return raw, nil
+ }
+
+ var object map[string]json.RawMessage
+ if err := json.Unmarshal(raw, &object); err != nil {
+ return nil, err
+ }
+
+ if _, ok := object[modern]; !ok {
+ if value, ok := object[legacy]; ok {
+ object[modern] = value
+ }
+ }
+ delete(object, legacy)
+
+ return json.Marshal(object)
+}
+
func validateRequiredUpdateFields(action string, raw json.RawMessage) error {
fields := requiredUpdateFields[action]
if len(fields) == 0 {
@@ -195,6 +221,11 @@ func (u *Updates) UnmarshalJSON(data []byte) error {
default:
return fmt.Errorf("%w: unknown update action: %s", iceberg.ErrInvalidArgument, base.ActionName)
}
+ normalized, err := normalizeLegacyPropertyFields(base.ActionName, raw)
+ if err != nil {
+ return err
+ }
+ raw = normalized
if err := validateRequiredUpdateFields(base.ActionName, raw); err != nil {
return err
}
diff --git a/table/updates_test.go b/table/updates_test.go
index f8b27f164..abf81fe63 100644
--- a/table/updates_test.go
+++ b/table/updates_test.go
@@ -549,6 +549,77 @@ func TestUnmarshalAddSpecWithoutIDUsesInitialSpecID(t *testing.T) {
assert.Equal(t, iceberg.InitialPartitionSpecID, update.Spec.ID())
}
+func TestUnmarshalUpdatesAcceptsLegacyPropertyFields(t *testing.T) {
+ data := []byte(`[
+ {"action":"set-properties","updated":{"key":"legacy"}},
+ {"action":"remove-properties","removed":["key"]},
+ {"action":"set-properties","updated":{}},
+ {"action":"remove-properties","removed":[]},
+ {"action":"set-properties","updated":{"key":"legacy"},"updates":{"key":"modern"}},
+ {"action":"remove-properties","removed":["legacy"],"removals":["modern"]}
+ ]`)
+
+ var updates Updates
+ require.NoError(t, json.Unmarshal(data, &updates))
+ assert.Equal(t, Updates{
+ NewSetPropertiesUpdate(iceberg.Properties{"key": "legacy"}),
+ NewRemovePropertiesUpdate([]string{"key"}),
+ NewSetPropertiesUpdate(iceberg.Properties{}),
+ NewRemovePropertiesUpdate([]string{}),
+ NewSetPropertiesUpdate(iceberg.Properties{"key": "modern"}),
+ NewRemovePropertiesUpdate([]string{"modern"}),
+ }, updates)
+
+ encoded, err := json.Marshal(updates)
+ require.NoError(t, err)
+ assert.JSONEq(t, `[
+ {"action":"set-properties","updates":{"key":"legacy"}},
+ {"action":"remove-properties","removals":["key"]},
+ {"action":"set-properties","updates":{}},
+ {"action":"remove-properties","removals":[]},
+ {"action":"set-properties","updates":{"key":"modern"}},
+ {"action":"remove-properties","removals":["modern"]}
+ ]`, string(encoded))
+}
+
+func TestUnmarshalUpdatesRejectsNullLegacyPropertyFields(t *testing.T) {
+ tests := []struct {
+ name string
+ data string
+ field string
+ }{
+ {
+ name: "set-properties legacy field",
+ data: `{"action":"set-properties","updated":null}`,
+ field: "updates",
+ },
+ {
+ name: "remove-properties legacy field",
+ data: `{"action":"remove-properties","removed":null}`,
+ field: "removals",
+ },
+ {
+ name: "set-properties canonical field takes precedence",
+ data: `{"action":"set-properties","updated":{"key":"legacy"},"updates":null}`,
+ field: "updates",
+ },
+ {
+ name: "remove-properties canonical field takes precedence",
+ data: `{"action":"remove-properties","removed":["legacy"],"removals":null}`,
+ field: "removals",
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ var updates Updates
+ err := json.Unmarshal([]byte("["+tt.data+"]"), &updates)
+ require.Error(t, err)
+ assert.Contains(t, err.Error(), tt.field)
+ })
+ }
+}
+
func TestUnmarshalUpdatesReplacesExistingSlice(t *testing.T) {
var updates Updates
require.NoError(t, json.Unmarshal([]byte(`[