This is an automated email from the ASF dual-hosted git repository.
zeroshade 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 cc92ce59 feat(view): view updates & MetadataBuilder stubs (#621)
cc92ce59 is described below
commit cc92ce59a54d0444ddfe21b8f15be65234b4a39a
Author: Tobias Pütz <[email protected]>
AuthorDate: Mon Nov 17 19:45:43 2025 +0100
feat(view): view updates & MetadataBuilder stubs (#621)
- view updates + deserialization
- ViewMetadataBuilder stubs
---
table/metadata.go | 4 +
table/updates.go | 2 +-
view/metadata_builder.go | 57 +++++++++++
view/updates.go | 251 +++++++++++++++++++++++++++++++++++++++++++++++
view/updates_test.go | 107 ++++++++++++++++++++
5 files changed, 420 insertions(+), 1 deletion(-)
diff --git a/table/metadata.go b/table/metadata.go
index 01b8cc4c..88e6607f 100644
--- a/table/metadata.go
+++ b/table/metadata.go
@@ -153,6 +153,10 @@ type Metadata interface {
PartitionStatistics() iter.Seq[PartitionStatisticsFile]
}
+// MetadataBuilder is a struct used for building and updating Iceberg table
metadata.
+//
+// It keeps track of applied changes in the `updates` field. This can be used
to commit changes made to a table to the
+// catalog.
type MetadataBuilder struct {
base Metadata
updates []Update
diff --git a/table/updates.go b/table/updates.go
index c8eb1c5a..db31da01 100644
--- a/table/updates.go
+++ b/table/updates.go
@@ -113,7 +113,7 @@ func (u *Updates) UnmarshalJSON(data []byte) error {
case UpdateRemoveSchemas:
upd = &removeSchemasUpdate{}
default:
- return fmt.Errorf("unknown update action: %s",
base.ActionName)
+ return fmt.Errorf("%w: unknown update action: %s",
iceberg.ErrInvalidArgument, base.ActionName)
}
if err := json.Unmarshal(raw, upd); err != nil {
diff --git a/view/metadata_builder.go b/view/metadata_builder.go
new file mode 100644
index 00000000..c87f7a9e
--- /dev/null
+++ b/view/metadata_builder.go
@@ -0,0 +1,57 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package view
+
+import (
+ "github.com/apache/iceberg-go"
+)
+
+// MetadataBuilder is a struct used for building and updating Iceberg view
metadata.
+type MetadataBuilder struct{}
+
+func (b *MetadataBuilder) AssignUUID(_ string) error {
+ return iceberg.ErrNotImplemented
+}
+
+func (b *MetadataBuilder) UpgradeFormatVersion(_ int) error {
+ return iceberg.ErrNotImplemented
+}
+
+func (b *MetadataBuilder) AddSchema(_ *iceberg.Schema) error {
+ return iceberg.ErrNotImplemented
+}
+
+func (b *MetadataBuilder) SetLocation(_ string) error {
+ return iceberg.ErrNotImplemented
+}
+
+func (b *MetadataBuilder) SetProperties(_ map[string]string) error {
+ return iceberg.ErrNotImplemented
+}
+
+func (b *MetadataBuilder) RemoveProperties(_ []string) error {
+ return iceberg.ErrNotImplemented
+}
+
+func (b *MetadataBuilder) AddVersion(_ *Version) error {
+ return iceberg.ErrNotImplemented
+}
+
+func (b *MetadataBuilder) SetCurrentVersionID(_ int64) error {
+ return iceberg.ErrNotImplemented
+}
diff --git a/view/updates.go b/view/updates.go
new file mode 100644
index 00000000..87566325
--- /dev/null
+++ b/view/updates.go
@@ -0,0 +1,251 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package view
+
+import (
+ "encoding/json"
+ "fmt"
+
+ "github.com/apache/iceberg-go"
+)
+
+const (
+ UpdateActionAssignUUID = "assign-uuid"
+ UpdateActionUpgradeFormatVersion = "upgrade-format-version"
+ UpdateActionAddSchema = "add-schema"
+ UpdateActionSetLocation = "set-location"
+ UpdateActionSetProperties = "set-properties"
+ UpdateActionRemoveProperties = "remove-properties"
+ UpdateActionAddViewVersion = "add-view-version"
+ UpdateActionSetCurrentViewVersion = "set-current-view-version"
+)
+
+// ViewUpdate represents a change to view metadata.
+type ViewUpdate interface {
+ // Action returns the name of the action that the update represents.
+ Action() string
+ // Apply applies the update to the given metadata builder.
+ Apply(*MetadataBuilder) error
+}
+
+type baseViewUpdate struct {
+ ActionName string `json:"action"`
+}
+
+func (u *baseViewUpdate) Action() string {
+ return u.ActionName
+}
+
+// ViewUpdates represents a list of view updates.
+type ViewUpdates []ViewUpdate
+
+func (u *ViewUpdates) UnmarshalJSON(data []byte) error {
+ var rawUpdates []json.RawMessage
+ if err := json.Unmarshal(data, &rawUpdates); err != nil {
+ return err
+ }
+
+ for _, raw := range rawUpdates {
+ var base baseViewUpdate
+ if err := json.Unmarshal(raw, &base); err != nil {
+ return err
+ }
+
+ var upd ViewUpdate
+ switch base.ActionName {
+ case UpdateActionAssignUUID:
+ upd = &AssignUUIDUpdate{}
+ case UpdateActionUpgradeFormatVersion:
+ upd = &UpgradeFormatVersionUpdate{}
+ case UpdateActionAddSchema:
+ upd = &AddSchemaUpdate{}
+ case UpdateActionSetLocation:
+ upd = &SetLocationUpdate{}
+ case UpdateActionSetProperties:
+ upd = &SetPropertiesUpdate{}
+ case UpdateActionRemoveProperties:
+ upd = &RemovePropertiesUpdate{}
+ case UpdateActionAddViewVersion:
+ upd = &AddViewVersionUpdate{}
+ case UpdateActionSetCurrentViewVersion:
+ upd = &SetCurrentViewVersionUpdate{}
+ default:
+ return fmt.Errorf("%w: unknown update action: %s",
iceberg.ErrInvalidArgument, base.ActionName)
+ }
+
+ if err := json.Unmarshal(raw, upd); err != nil {
+ return err
+ }
+ *u = append(*u, upd)
+ }
+
+ return nil
+}
+
+// AssignUUIDUpdate assigns a UUID to the view MetadataBuilder.
+type AssignUUIDUpdate struct {
+ baseViewUpdate
+ UUID string `json:"uuid"`
+}
+
+// Apply assigns the UUID to the view MetadataBuilder.
+func (u *AssignUUIDUpdate) Apply(b *MetadataBuilder) error {
+ return b.AssignUUID(u.UUID)
+}
+
+// NewAssignUUIDUpdate creates a new update that assigns a UUID to the view
MetadataBuilder.
+func NewAssignUUIDUpdate(uuid string) *AssignUUIDUpdate {
+ return &AssignUUIDUpdate{
+ baseViewUpdate: baseViewUpdate{ActionName:
UpdateActionAssignUUID},
+ UUID: uuid,
+ }
+}
+
+// UpgradeFormatVersionUpdate upgrades the format version of the view
MetadataBuilder to the given version.
+type UpgradeFormatVersionUpdate struct {
+ baseViewUpdate
+ FormatVersion int `json:"format-version"`
+}
+
+// Apply upgrades the format version of the view MetadataBuilder to the given
version.
+func (u *UpgradeFormatVersionUpdate) Apply(b *MetadataBuilder) error {
+ return b.UpgradeFormatVersion(u.FormatVersion)
+}
+
+// NewUpgradeFormatVersionUpdate creates a new update that upgrades the format
version.
+func NewUpgradeFormatVersionUpdate(version int) *UpgradeFormatVersionUpdate {
+ return &UpgradeFormatVersionUpdate{
+ baseViewUpdate: baseViewUpdate{ActionName:
UpdateActionUpgradeFormatVersion},
+ FormatVersion: version,
+ }
+}
+
+// AddSchemaUpdate adds a new schema to the view MetadataBuilder.
+type AddSchemaUpdate struct {
+ baseViewUpdate
+ Schema *iceberg.Schema `json:"schema"`
+ LastColumnID *int `json:"last-column-id,omitempty"`
+}
+
+func (u *AddSchemaUpdate) Apply(b *MetadataBuilder) error {
+ return b.AddSchema(u.Schema)
+}
+
+// NewAddSchemaUpdate creates a new update that adds a new schema.
+func NewAddSchemaUpdate(schema *iceberg.Schema) *AddSchemaUpdate {
+ return &AddSchemaUpdate{
+ baseViewUpdate: baseViewUpdate{ActionName:
UpdateActionAddSchema},
+ Schema: schema,
+ }
+}
+
+// SetLocationUpdate updates the view location in the MetadataBuilder.
+type SetLocationUpdate struct {
+ baseViewUpdate
+ Location string `json:"location"`
+}
+
+// Apply updates the view location of the MetadataBuilder.
+func (u *SetLocationUpdate) Apply(b *MetadataBuilder) error {
+ return b.SetLocation(u.Location)
+}
+
+// NewSetLocationUpdate creates a new update that updates the view location in
the MetadataBuilder.
+func NewSetLocationUpdate(location string) *SetLocationUpdate {
+ return &SetLocationUpdate{
+ baseViewUpdate: baseViewUpdate{ActionName:
UpdateActionSetLocation},
+ Location: location,
+ }
+}
+
+// SetPropertiesUpdate sets view properties.
+type SetPropertiesUpdate struct {
+ baseViewUpdate
+ Updates map[string]string `json:"updates"`
+}
+
+// Apply sets view properties in the MetadataBuilder.
+func (u *SetPropertiesUpdate) Apply(b *MetadataBuilder) error {
+ return b.SetProperties(u.Updates)
+}
+
+// NewSetPropertiesUpdate creates a new update that sets view properties in
the view MetadataBuilder.
+func NewSetPropertiesUpdate(updates map[string]string) *SetPropertiesUpdate {
+ return &SetPropertiesUpdate{
+ baseViewUpdate: baseViewUpdate{ActionName:
UpdateActionSetProperties},
+ Updates: updates,
+ }
+}
+
+// RemovePropertiesUpdate removes view properties in the view MetadataBuilder.
+type RemovePropertiesUpdate struct {
+ baseViewUpdate
+ Removals []string `json:"removals"`
+}
+
+// Apply removes view properties from the view MetadataBuilder.
+func (u *RemovePropertiesUpdate) Apply(b *MetadataBuilder) error {
+ return b.RemoveProperties(u.Removals)
+}
+
+// NewRemovePropertiesUpdate creates a new update that removes view properties
from the view MetadataBuilder.
+func NewRemovePropertiesUpdate(removals []string) *RemovePropertiesUpdate {
+ return &RemovePropertiesUpdate{
+ baseViewUpdate: baseViewUpdate{ActionName:
UpdateActionRemoveProperties},
+ Removals: removals,
+ }
+}
+
+// AddViewVersionUpdate adds a new view version to the view MetadataBuilder.
+type AddViewVersionUpdate struct {
+ baseViewUpdate
+ ViewVersion *Version `json:"view-version"`
+}
+
+// Apply adds a new view version to the view MetadataBuilder.
+func (u *AddViewVersionUpdate) Apply(b *MetadataBuilder) error {
+ return b.AddVersion(u.ViewVersion)
+}
+
+// NewAddViewVersionUpdate creates a new update that adds a new view version
to the view MetadataBuilder.
+func NewAddViewVersionUpdate(version *Version) *AddViewVersionUpdate {
+ return &AddViewVersionUpdate{
+ baseViewUpdate: baseViewUpdate{ActionName:
UpdateActionAddViewVersion},
+ ViewVersion: version,
+ }
+}
+
+// SetCurrentViewVersionUpdate sets the current view version of the view
MetadataBuilder.
+// VersionID can be -1 to reference the last added version.
+type SetCurrentViewVersionUpdate struct {
+ baseViewUpdate
+ VersionID int64 `json:"version-id"`
+}
+
+// Apply sets the current view version of the view MetadataBuilder.
+func (u *SetCurrentViewVersionUpdate) Apply(b *MetadataBuilder) error {
+ return b.SetCurrentVersionID(u.VersionID)
+}
+
+// NewSetCurrentViewVersionUpdate creates a new update that sets the current
view version of the view MetadataBuilder.
+func NewSetCurrentViewVersionUpdate(versionID int64)
*SetCurrentViewVersionUpdate {
+ return &SetCurrentViewVersionUpdate{
+ baseViewUpdate: baseViewUpdate{ActionName:
UpdateActionSetCurrentViewVersion},
+ VersionID: versionID,
+ }
+}
diff --git a/view/updates_test.go b/view/updates_test.go
new file mode 100644
index 00000000..80a05ccc
--- /dev/null
+++ b/view/updates_test.go
@@ -0,0 +1,107 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package view_test
+
+import (
+ "encoding/json"
+ "testing"
+
+ "github.com/apache/iceberg-go/view"
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+)
+
+func TestViewUpdatesUnmarshalJSON(t *testing.T) {
+ jsonData := `[
+ {
+ "action": "assign-uuid",
+ "uuid": "550e8400-e29b-41d4-a716-446655440000"
+ },
+ {
+ "action": "upgrade-format-version",
+ "format-version": 2
+ },
+ {
+ "action": "set-location",
+ "location": "s3://bucket/warehouse/view"
+ },
+ {
+ "action": "set-properties",
+ "updates": {
+ "key1": "value1",
+ "key2": "value2"
+ }
+ },
+ {
+ "action": "remove-properties",
+ "removals": ["old-key"]
+ }
+ ]`
+
+ var updates view.ViewUpdates
+ err := json.Unmarshal([]byte(jsonData), &updates)
+ require.NoError(t, err)
+ require.Len(t, updates, 5)
+
+ assignUUID, ok := updates[0].(*view.AssignUUIDUpdate)
+ require.True(t, ok)
+ assert.Equal(t, "assign-uuid", assignUUID.Action())
+ assert.Equal(t, "550e8400-e29b-41d4-a716-446655440000", assignUUID.UUID)
+
+ upgradeVersion, ok := updates[1].(*view.UpgradeFormatVersionUpdate)
+ require.True(t, ok)
+ assert.Equal(t, "upgrade-format-version", upgradeVersion.Action())
+ assert.Equal(t, 2, upgradeVersion.FormatVersion)
+
+ setLocation, ok := updates[2].(*view.SetLocationUpdate)
+ require.True(t, ok)
+ assert.Equal(t, "set-location", setLocation.Action())
+ assert.Equal(t, "s3://bucket/warehouse/view", setLocation.Location)
+
+ setProps, ok := updates[3].(*view.SetPropertiesUpdate)
+ require.True(t, ok)
+ assert.Equal(t, "set-properties", setProps.Action())
+ assert.Equal(t, map[string]string{"key1": "value1", "key2": "value2"},
setProps.Updates)
+
+ removeProps, ok := updates[4].(*view.RemovePropertiesUpdate)
+ require.True(t, ok)
+ assert.Equal(t, "remove-properties", removeProps.Action())
+ assert.Equal(t, []string{"old-key"}, removeProps.Removals)
+}
+
+func TestViewUpdatesUnmarshalJSONUnknownAction(t *testing.T) {
+ jsonData := `[
+ {
+ "action": "unknown-action",
+ "field": "value"
+ }
+ ]`
+
+ var updates view.ViewUpdates
+ err := json.Unmarshal([]byte(jsonData), &updates)
+ require.Error(t, err)
+ assert.Contains(t, err.Error(), "unknown update action: unknown-action")
+}
+
+func TestViewUpdatesUnmarshalJSONInvalidJSON(t *testing.T) {
+ jsonData := `invalid json`
+
+ var updates view.ViewUpdates
+ err := json.Unmarshal([]byte(jsonData), &updates)
+ require.Error(t, err)
+}