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/08 07:57:48 UTC
(iceberg-go) branch main updated: fix(view): replace metadata state during JSON decoding (#1646)
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 bb959163d fix(view): replace metadata state during JSON decoding (#1646)
bb959163d is described below
commit bb959163dde5ec2a8455f795b7bbfdfb868868bc
Author: Minh Vu <vu...@gmail.com>
AuthorDate: Sat Aug 8 09:57:42 2026 +0200
fix(view): replace metadata state during JSON decoding (#1646)
* fix(view): replace metadata state during JSON decoding
* fix(view): reinitialize metadata caches after decoding
* test(view): cover metadata decode reuse
---
view/metadata.go | 19 ++++++++---
view/metadata_test.go | 88 +++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 102 insertions(+), 5 deletions(-)
diff --git a/view/metadata.go b/view/metadata.go
index d78698858..fd280ec88 100644
--- a/view/metadata.go
+++ b/view/metadata.go
@@ -654,18 +654,27 @@ func (m *metadata) init() {
func (m *metadata) UnmarshalJSON(b []byte) error {
type Alias metadata
- aux := (*Alias)(m)
-
- aux.FormatVersionValue = -1
- aux.CurrentVersionIDValue = -1
+ next := metadata{
+ FormatVersionValue: -1,
+ CurrentVersionIDValue: -1,
+ }
+ aux := (*Alias)(&next)
if err := json.Unmarshal(b, aux); err != nil {
return err
}
+ next.init()
+
+ if err := next.validate(); err != nil {
+ return err
+ }
+
+ *m = next
+ // init() rebinds the lazy-index closures to m after the value copy.
m.init()
- return m.validate()
+ return nil
}
// NewMetadata creates a new view metadata object using the provided version, schema, location, and props,
diff --git a/view/metadata_test.go b/view/metadata_test.go
index 7a622bc07..7b46a8669 100644
--- a/view/metadata_test.go
+++ b/view/metadata_test.go
@@ -22,6 +22,7 @@ import (
"errors"
"fmt"
"slices"
+ "strings"
"testing"
"github.com/apache/iceberg-go"
@@ -143,6 +144,93 @@ func TestUnmarshalViewMetadata(t *testing.T) {
assert.Equal(t, iceberg.Properties{"prop": "value"}, md.Properties())
}
+func TestMetadataUnmarshalReplacesReceiverState(t *testing.T) {
+ var metadata metadata
+ require.NoError(t, json.Unmarshal([]byte(exampleViewJSON), &metadata))
+
+ var reduced map[string]json.RawMessage
+ require.NoError(t, json.Unmarshal([]byte(exampleViewJSON), &reduced))
+ delete(reduced, "properties")
+ delete(reduced, "version-log")
+ reducedData, err := json.Marshal(reduced)
+ require.NoError(t, err)
+
+ require.NoError(t, json.Unmarshal(reducedData, &metadata))
+ assert.Empty(t, metadata.Props)
+ assert.Empty(t, metadata.VersionLogList)
+
+ beforeFailure, err := json.Marshal(&metadata)
+ require.NoError(t, err)
+ invalid := strings.Replace(exampleViewJSON, `"current-version-id": 1`, `"current-version-id": 99`, 1)
+ require.Error(t, json.Unmarshal([]byte(invalid), &metadata))
+ afterFailure, err := json.Marshal(&metadata)
+ require.NoError(t, err)
+ assert.Equal(t, beforeFailure, afterFailure)
+}
+
+func TestMetadataUnmarshalDoesNotMutateInputAndRoundTripsAfterReuse(t *testing.T) {
+ input := []byte(exampleViewJSON)
+ originalInput := append([]byte(nil), input...)
+
+ var md metadata
+ require.NoError(t, json.Unmarshal(input, &md))
+ assert.Equal(t, originalInput, input)
+
+ var replacement map[string]any
+ require.NoError(t, json.Unmarshal([]byte(exampleViewJSON), &replacement))
+ replacement["current-version-id"] = int64(2)
+ replacement["versions"].([]any)[0].(map[string]any)["version-id"] = int64(2)
+ replacement["versions"].([]any)[0].(map[string]any)["schema-id"] = 2
+ replacement["schemas"].([]any)[0].(map[string]any)["schema-id"] = 2
+ replacementData, err := json.Marshal(replacement)
+ require.NoError(t, err)
+ require.NoError(t, json.Unmarshal(replacementData, &md))
+
+ encoded, err := json.Marshal(&md)
+ require.NoError(t, err)
+ var roundTripped metadata
+ require.NoError(t, json.Unmarshal(encoded, &roundTripped))
+ reencoded, err := json.Marshal(&roundTripped)
+ require.NoError(t, err)
+ assert.JSONEq(t, string(encoded), string(reencoded))
+}
+
+func TestMetadataUnmarshalReplacesLookupCaches(t *testing.T) {
+ var metadata metadata
+ require.NoError(t, json.Unmarshal([]byte(exampleViewJSON), &metadata))
+
+ // Prime every lookup cache before replacing the metadata.
+ assert.Equal(t, int64(1), metadata.CurrentVersion().VersionID)
+ assert.Equal(t, 0, metadata.CurrentSchema().ID)
+ assert.Contains(t, metadata.SchemasByID(), 0)
+
+ var replacement map[string]any
+ require.NoError(t, json.Unmarshal([]byte(exampleViewJSON), &replacement))
+ replacement["current-version-id"] = int64(2)
+ replacement["versions"].([]any)[0].(map[string]any)["version-id"] = int64(2)
+ replacement["versions"].([]any)[0].(map[string]any)["schema-id"] = 2
+ replacement["schemas"].([]any)[0].(map[string]any)["schema-id"] = 2
+ replacement["version-log"].([]any)[0].(map[string]any)["version-id"] = int64(2)
+ replacementData, err := json.Marshal(replacement)
+ require.NoError(t, err)
+
+ require.NoError(t, json.Unmarshal(replacementData, &metadata))
+ assert.Equal(t, int64(2), metadata.CurrentVersion().VersionID)
+ assert.Equal(t, 2, metadata.CurrentSchema().ID)
+ assert.NotContains(t, metadata.SchemasByID(), 0)
+ assert.Contains(t, metadata.SchemasByID(), 2)
+
+ _, oldVersionExists := metadata.lazyVersionsByID()[1]
+ _, newVersionExists := metadata.lazyVersionsByID()[2]
+ assert.False(t, oldVersionExists)
+ assert.True(t, newVersionExists)
+
+ invalid := strings.Replace(string(replacementData), `"current-version-id":2`, `"current-version-id":99`, 1)
+ require.Error(t, json.Unmarshal([]byte(invalid), &metadata))
+ assert.Equal(t, int64(2), metadata.CurrentVersion().VersionID)
+ assert.Equal(t, 2, metadata.CurrentSchema().ID)
+}
+
func TestValidMetadataDeserialization(t *testing.T) {
validJSON := `{
"view-uuid": "fa6506c3-7681-40c8-86dc-e36561f83385",