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 7e03a0bd feat(view metadata): implement the View spec (#600)
7e03a0bd is described below

commit 7e03a0bd6fbfc702b3cffe1415e98d5a459cf342
Author: Alessandro Nori <[email protected]>
AuthorDate: Wed Oct 22 18:22:08 2025 +0200

    feat(view metadata): implement the View spec (#600)
    
    Implement the [View
    Spec](https://iceberg.apache.org/view-spec/#view-metadata):
    - created a new package for `view`
    - moved existing related methods to new package
    - added a test to parse a view metadata file
---
 catalog/internal/utils.go        | 159 ---------------------------------------
 catalog/rest/rest.go             |  31 ++------
 catalog/sql/sql.go               |  11 +--
 catalog/sql/sql_test.go          |   5 +-
 view/metadata.go                 | 136 +++++++++++++++++++++++++++++++++
 view/metadata_test.go            |  35 +++++++++
 view/testdata/view-metadata.json |  63 ++++++++++++++++
 view/view.go                     | 120 +++++++++++++++++++++++++++++
 8 files changed, 370 insertions(+), 190 deletions(-)

diff --git a/catalog/internal/utils.go b/catalog/internal/utils.go
index 99f6bfa5..d899eb96 100644
--- a/catalog/internal/utils.go
+++ b/catalog/internal/utils.go
@@ -28,11 +28,9 @@ import (
        "regexp"
        "strconv"
        "strings"
-       "time"
 
        "github.com/apache/iceberg-go"
        "github.com/apache/iceberg-go/catalog"
-       "github.com/apache/iceberg-go/internal"
        "github.com/apache/iceberg-go/io"
        "github.com/apache/iceberg-go/table"
        "github.com/google/uuid"
@@ -220,160 +218,3 @@ func UpdateAndStageTable(ctx context.Context, current 
*table.Table, ident table.
                ),
        }, nil
 }
-
-func CreateViewMetadata(
-       ctx context.Context,
-       catalogName string,
-       nsIdent []string,
-       schema *iceberg.Schema,
-       viewSQL string,
-       loc string,
-       props iceberg.Properties,
-) (metadataLocation string, err error) {
-       versionId := int64(1)
-       timestampMs := time.Now().UnixMilli()
-
-       viewVersion := struct {
-               VersionID       int64             `json:"version-id"`
-               TimestampMs     int64             `json:"timestamp-ms"`
-               SchemaID        int               `json:"schema-id"`
-               Summary         map[string]string `json:"summary"`
-               Operation       string            `json:"operation"`
-               Representations []struct {
-                       Type    string `json:"type"`
-                       SQL     string `json:"sql"`
-                       Dialect string `json:"dialect"`
-               } `json:"representations"`
-               DefaultCatalog   string   `json:"default-catalog"`
-               DefaultNamespace []string `json:"default-namespace"`
-       }{
-               VersionID:   versionId,
-               TimestampMs: timestampMs,
-               SchemaID:    schema.ID,
-               Summary:     map[string]string{"sql": viewSQL},
-               Operation:   "create",
-               Representations: []struct {
-                       Type    string `json:"type"`
-                       SQL     string `json:"sql"`
-                       Dialect string `json:"dialect"`
-               }{
-                       {Type: "sql", SQL: viewSQL, Dialect: "default"},
-               },
-               DefaultCatalog:   catalogName,
-               DefaultNamespace: nsIdent,
-       }
-
-       viewVersionBytes, err := json.Marshal(viewVersion)
-       if err != nil {
-               return "", fmt.Errorf("failed to marshal view version: %w", err)
-       }
-
-       if props == nil {
-               props = iceberg.Properties{}
-       }
-       props["view-version"] = string(viewVersionBytes)
-       props["view-format"] = "iceberg"
-       props["view-sql"] = viewSQL
-
-       metadataLocation = loc + "/metadata/view-" + uuid.New().String() + 
".metadata.json"
-
-       viewUUID := uuid.New().String()
-       props["view-uuid"] = viewUUID
-
-       viewMetadata := map[string]interface{}{
-               "view-uuid":          viewUUID,
-               "format-version":     1,
-               "location":           loc,
-               "schema":             schema,
-               "current-version-id": versionId,
-               "versions": map[string]interface{}{
-                       "1": viewVersion,
-               },
-               "properties": props,
-               "version-log": []map[string]interface{}{
-                       {
-                               "timestamp-ms": timestampMs,
-                               "version-id":   versionId,
-                       },
-               },
-       }
-
-       viewMetadataBytes, err := json.Marshal(viewMetadata)
-       if err != nil {
-               return "", fmt.Errorf("failed to marshal view metadata: %w", 
err)
-       }
-
-       fs, err := io.LoadFS(ctx, props, metadataLocation)
-       if err != nil {
-               return "", fmt.Errorf("failed to load filesystem for view 
metadata: %w", err)
-       }
-
-       wfs, ok := fs.(io.WriteFileIO)
-       if !ok {
-               return "", errors.New("filesystem IO does not support writing")
-       }
-
-       out, err := wfs.Create(metadataLocation)
-       if err != nil {
-               return "", fmt.Errorf("failed to create view metadata file: 
%w", err)
-       }
-       defer internal.CheckedClose(out, &err)
-
-       if _, err := out.Write(viewMetadataBytes); err != nil {
-               return "", fmt.Errorf("failed to write view metadata: %w", err)
-       }
-
-       return metadataLocation, nil
-}
-
-func LoadViewMetadata(ctx context.Context,
-       props iceberg.Properties,
-       metadataLocation string,
-       viewName string,
-       namespace string,
-) (_ map[string]interface{}, err error) {
-       // Initial metadata with basic information
-       viewMetadata := map[string]interface{}{
-               "name":              viewName,
-               "namespace":         namespace,
-               "metadata-location": metadataLocation,
-       }
-
-       // Load the filesystem
-       fs, err := io.LoadFS(ctx, props, metadataLocation)
-       if err != nil {
-               return nil, fmt.Errorf("error loading view metadata: %w", err)
-       }
-
-       // Open the metadata file
-       inputFile, err := fs.Open(metadataLocation)
-       if err != nil {
-               return viewMetadata, fmt.Errorf("error encountered loading view 
metadata: %w", err)
-       }
-       defer internal.CheckedClose(inputFile, &err)
-
-       // Decode the complete metadata
-       var fullViewMetadata map[string]interface{}
-       if err := json.NewDecoder(inputFile).Decode(&fullViewMetadata); err != 
nil {
-               return viewMetadata, fmt.Errorf("error encountered decoding 
view metadata: %w", err)
-       }
-
-       // Update the metadata with name, namespace and location
-       fullViewMetadata["name"] = viewName
-       fullViewMetadata["namespace"] = namespace
-       fullViewMetadata["metadata-location"] = metadataLocation
-
-       if props, ok := 
fullViewMetadata["properties"].(map[string]interface{}); ok {
-               strProps := make(map[string]string)
-               for k, v := range props {
-                       if str, ok := v.(string); ok {
-                               strProps[k] = str
-                       } else if vJson, err := json.Marshal(v); err == nil {
-                               strProps[k] = string(vJson)
-                       }
-               }
-               fullViewMetadata["properties"] = strProps
-       }
-
-       return fullViewMetadata, nil
-}
diff --git a/catalog/rest/rest.go b/catalog/rest/rest.go
index 0c40089b..e288d77b 100644
--- a/catalog/rest/rest.go
+++ b/catalog/rest/rest.go
@@ -40,6 +40,7 @@ import (
        "github.com/apache/iceberg-go/catalog"
        iceio "github.com/apache/iceberg-go/io"
        "github.com/apache/iceberg-go/table"
+       "github.com/apache/iceberg-go/view"
        "github.com/aws/aws-sdk-go-v2/aws"
        v4 "github.com/aws/aws-sdk-go-v2/aws/signer/v4"
        "github.com/aws/aws-sdk-go-v2/config"
@@ -1153,27 +1154,13 @@ func (r *Catalog) CheckViewExists(ctx context.Context, 
identifier table.Identifi
        return true, nil
 }
 
-type viewVersion struct {
-       VersionID       int64             `json:"version-id"`
-       TimestampMs     int64             `json:"timestamp-ms"`
-       SchemaID        int               `json:"schema-id"`
-       Summary         map[string]string `json:"summary"`
-       Representations []struct {
-               Type    string `json:"type"`
-               SQL     string `json:"sql"`
-               Dialect string `json:"dialect"`
-       } `json:"representations"`
-       DefaultCatalog   string   `json:"default-catalog"`
-       DefaultNamespace []string `json:"default-namespace"`
-}
-
 type createViewRequest struct {
        Name        string             `json:"name"`
        Schema      *iceberg.Schema    `json:"schema"`
        Location    string             `json:"location,omitempty"`
        Props       iceberg.Properties `json:"properties,omitempty"`
        SQL         string             `json:"sql"`
-       ViewVersion viewVersion        `json:"view-version"`
+       ViewVersion view.Version       `json:"view-version"`
 }
 
 type viewResponse struct {
@@ -1185,7 +1172,7 @@ type viewResponse struct {
 
 // CreateView creates a new view in the catalog.
 func (r *Catalog) CreateView(ctx context.Context, identifier table.Identifier, 
schema *iceberg.Schema, sql string, props iceberg.Properties) error {
-       ns, view, err := splitIdentForPath(identifier)
+       ns, v, err := splitIdentForPath(identifier)
        if err != nil {
                return err
        }
@@ -1196,23 +1183,19 @@ func (r *Catalog) CreateView(ctx context.Context, 
identifier table.Identifier, s
        }
 
        payload := createViewRequest{
-               Name:   view,
+               Name:   v,
                Schema: freshSchema,
                SQL:    sql,
                Props:  props,
-               ViewVersion: viewVersion{
+               ViewVersion: view.Version{
                        VersionID:   1,
                        TimestampMs: time.Now().UnixMilli(),
                        SchemaID:    freshSchema.ID,
                        Summary:     map[string]string{"sql": sql},
-                       Representations: []struct {
-                               Type    string `json:"type"`
-                               SQL     string `json:"sql"`
-                               Dialect string `json:"dialect"`
-                       }{
+                       Representations: []view.SQLRepresentation{
                                {Type: "sql", SQL: sql, Dialect: "default"},
                        },
-                       DefaultCatalog:   r.name,
+                       DefaultCatalog:   &r.name,
                        DefaultNamespace: strings.Split(ns, namespaceSeparator),
                },
        }
diff --git a/catalog/sql/sql.go b/catalog/sql/sql.go
index dad36e3d..0057bf15 100644
--- a/catalog/sql/sql.go
+++ b/catalog/sql/sql.go
@@ -34,6 +34,7 @@ import (
        "github.com/apache/iceberg-go/catalog/internal"
        "github.com/apache/iceberg-go/io"
        "github.com/apache/iceberg-go/table"
+       "github.com/apache/iceberg-go/view"
        "github.com/uptrace/bun"
        "github.com/uptrace/bun/dialect/feature"
        "github.com/uptrace/bun/dialect/mssqldialect"
@@ -846,7 +847,7 @@ func (c *Catalog) CreateView(ctx context.Context, 
identifier table.Identifier, s
                return err
        }
 
-       metadataLocation, err := internal.CreateViewMetadata(ctx, c.name, 
nsIdent, schema, viewSQL, loc, props)
+       metadataLocation, err := view.CreateMetadata(ctx, c.name, nsIdent, 
schema, viewSQL, loc, props)
        if err != nil {
                return err
        }
@@ -1008,11 +1009,11 @@ func (c *Catalog) CheckViewExists(ctx context.Context, 
identifier table.Identifi
 }
 
 // LoadView loads a view from the catalog.
-func (c *Catalog) LoadView(ctx context.Context, identifier table.Identifier) 
(map[string]interface{}, error) {
+func (c *Catalog) LoadView(ctx context.Context, identifier table.Identifier) 
(view.Metadata, error) {
        ns := strings.Join(catalog.NamespaceFromIdent(identifier), ".")
        viewName := catalog.TableNameFromIdent(identifier)
 
-       view, err := withReadTx(ctx, c.db, func(ctx context.Context, tx bun.Tx) 
(*sqlIcebergTable, error) {
+       v, err := withReadTx(ctx, c.db, func(ctx context.Context, tx bun.Tx) 
(*sqlIcebergTable, error) {
                v := new(sqlIcebergTable)
                err := tx.NewSelect().Model(v).
                        Where("catalog_name = ?", c.name).
@@ -1033,11 +1034,11 @@ func (c *Catalog) LoadView(ctx context.Context, 
identifier table.Identifier) (ma
                return nil, err
        }
 
-       if !view.MetadataLocation.Valid {
+       if !v.MetadataLocation.Valid {
                return nil, fmt.Errorf("%w: %s, metadata location is missing", 
catalog.ErrNoSuchView, identifier)
        }
 
-       viewMetadata, err := internal.LoadViewMetadata(ctx, c.props, 
view.MetadataLocation.String, viewName, ns)
+       viewMetadata, err := view.LoadMetadata(ctx, c.props, 
v.MetadataLocation.String, viewName, ns)
        if err != nil {
                return nil, err
        }
diff --git a/catalog/sql/sql_test.go b/catalog/sql/sql_test.go
index 8767bcb0..2e0a0a59 100644
--- a/catalog/sql/sql_test.go
+++ b/catalog/sql/sql_test.go
@@ -1179,8 +1179,9 @@ func (s *SqliteCatalogTestSuite) TestLoadView() {
        viewInfo, err := db.LoadView(context.Background(), []string{nsName, 
viewName})
        s.Require().NoError(err)
 
-       s.Equal(viewName, viewInfo["name"])
-       s.Equal(nsName, viewInfo["namespace"])
+       s.Equal(1, viewInfo.FormatVersion())
+       s.Contains(viewInfo.Properties(), "comment")
+       s.Equal("Test view", viewInfo.Properties()["comment"])
 
        _, err = db.LoadView(context.Background(), []string{nsName, 
"nonexistent"})
        s.Error(err)
diff --git a/view/metadata.go b/view/metadata.go
new file mode 100644
index 00000000..26659e50
--- /dev/null
+++ b/view/metadata.go
@@ -0,0 +1,136 @@
+// 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 (
+       "context"
+       "encoding/json"
+       "errors"
+       "fmt"
+       "time"
+
+       "github.com/apache/iceberg-go"
+       "github.com/apache/iceberg-go/internal"
+       "github.com/apache/iceberg-go/io"
+       "github.com/google/uuid"
+)
+
+// LoadMetadata reads and parses a view metadata file from the specified 
location.
+//
+// Returns the loaded Metadata or an error if the file cannot be read or 
parsed.
+func LoadMetadata(ctx context.Context,
+       props iceberg.Properties,
+       metadataLocation string,
+       name string,
+       namespace string,
+) (_ Metadata, err error) {
+       fs, err := io.LoadFS(ctx, props, metadataLocation)
+       if err != nil {
+               return nil, fmt.Errorf("error loading view metadata: %w", err)
+       }
+
+       inputFile, err := fs.Open(metadataLocation)
+       if err != nil {
+               return nil, fmt.Errorf("%s.%s: error encountered loading view 
metadata: %w", namespace, name, err)
+       }
+       defer internal.CheckedClose(inputFile, &err)
+
+       var m metadata
+       if err := json.NewDecoder(inputFile).Decode(&m); err != nil {
+               return nil, fmt.Errorf("error encountered decoding view 
metadata: %w", err)
+       }
+
+       return &m, nil
+}
+
+// CreateMetadata creates a new view metadata file and writes it to storage.
+//
+// Returns the full path to the created metadata file, or an error if creation 
fails.
+//
+// Note: This function only supports creating new views with format version 1.
+// It does not support updating existing view metadata.
+func CreateMetadata(
+       ctx context.Context,
+       catalogName string,
+       nsIdent []string,
+       schema *iceberg.Schema,
+       viewSQL string,
+       loc string,
+       props iceberg.Properties,
+) (metadataLocation string, err error) {
+       versionId := int64(1)
+       timestampMs := time.Now().UnixMilli()
+
+       viewVersion := Version{
+               VersionID:   versionId,
+               TimestampMs: timestampMs,
+               SchemaID:    schema.ID,
+               Summary:     map[string]string{"sql": viewSQL},
+               Representations: []SQLRepresentation{
+                       {Type: "sql", SQL: viewSQL, Dialect: "default"},
+               },
+               DefaultCatalog:   &catalogName,
+               DefaultNamespace: nsIdent,
+       }
+
+       metadataLocation = loc + "/metadata/view-" + uuid.New().String() + 
".metadata.json"
+
+       viewUUID := uuid.New().String()
+       viewMetadata := metadata{
+               UUID:             viewUUID,
+               FmtVersion:       1,
+               Loc:              loc,
+               SchemaList:       []*iceberg.Schema{schema},
+               CurrentVersionId: versionId,
+               VersionList:      []Version{viewVersion},
+               VersionLogList: []VersionLogEntry{
+                       {
+                               TimestampMs: timestampMs,
+                               VersionID:   versionId,
+                       },
+               },
+               Props: props,
+       }
+
+       viewMetadataBytes, err := json.Marshal(viewMetadata)
+       if err != nil {
+               return "", fmt.Errorf("failed to marshal view metadata: %w", 
err)
+       }
+
+       fs, err := io.LoadFS(ctx, props, metadataLocation)
+       if err != nil {
+               return "", fmt.Errorf("failed to load filesystem for view 
metadata: %w", err)
+       }
+
+       wfs, ok := fs.(io.WriteFileIO)
+       if !ok {
+               return "", errors.New("filesystem IO does not support writing")
+       }
+
+       out, err := wfs.Create(metadataLocation)
+       if err != nil {
+               return "", fmt.Errorf("failed to create view metadata file: 
%w", err)
+       }
+       defer internal.CheckedClose(out, &err)
+
+       if _, err := out.Write(viewMetadataBytes); err != nil {
+               return "", fmt.Errorf("failed to write view metadata: %w", err)
+       }
+
+       return metadataLocation, nil
+}
diff --git a/view/metadata_test.go b/view/metadata_test.go
new file mode 100644
index 00000000..ea32d625
--- /dev/null
+++ b/view/metadata_test.go
@@ -0,0 +1,35 @@
+// 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 (
+       "testing"
+
+       "github.com/stretchr/testify/require"
+)
+
+func TestLoadMetadata(t *testing.T) {
+       props := make(map[string]string)
+       m, err := LoadMetadata(t.Context(), props, 
"testdata/view-metadata.json", "test", "test")
+       require.NoError(t, err)
+
+       require.Equal(t, "fa6506c3-7681-40c8-86dc-e36561f83385", m.ViewUUID())
+       require.Equal(t, 1, m.FormatVersion())
+       require.Equal(t, 2, len(m.(*metadata).VersionList))
+       require.Equal(t, "Daily event counts", m.Properties()["comment"])
+}
diff --git a/view/testdata/view-metadata.json b/view/testdata/view-metadata.json
new file mode 100644
index 00000000..d6a10925
--- /dev/null
+++ b/view/testdata/view-metadata.json
@@ -0,0 +1,63 @@
+{
+  "view-uuid": "fa6506c3-7681-40c8-86dc-e36561f83385",
+  "format-version" : 1,
+  "location" : "s3://bucket/warehouse/default.db/event_agg",
+  "current-version-id" : 2,
+  "properties" : {
+    "comment" : "Daily event counts"
+  },
+  "versions" : [ {
+    "version-id" : 1,
+    "timestamp-ms" : 1573518431292,
+    "schema-id" : 1,
+    "default-catalog" : "prod",
+    "default-namespace" : [ "default" ],
+    "summary" : {
+      "engine-name" : "Spark",
+      "engine-version" : "3.3.2"
+    },
+    "representations" : [ {
+      "type" : "sql",
+      "sql" : "SELECT\n    COUNT(1), CAST(event_ts AS DATE)\nFROM 
events\nGROUP BY 2",
+      "dialect" : "spark"
+    } ]
+  }, {
+    "version-id" : 2,
+    "timestamp-ms" : 1573518981593,
+    "schema-id" : 1,
+    "default-catalog" : "prod",
+    "default-namespace" : [ "default" ],
+    "summary" : {
+      "engine-name" : "Spark",
+      "engine-version" : "3.3.2"
+    },
+    "representations" : [ {
+      "type" : "sql",
+      "sql" : "SELECT\n    COUNT(1), CAST(event_ts AS DATE)\nFROM 
prod.default.events\nGROUP BY 2",
+      "dialect" : "spark"
+    } ]
+  } ],
+  "schemas": [ {
+    "schema-id": 1,
+    "type" : "struct",
+    "fields" : [ {
+      "id" : 1,
+      "name" : "event_count",
+      "required" : false,
+      "type" : "int",
+      "doc" : "Count of events"
+    }, {
+      "id" : 2,
+      "name" : "event_date",
+      "required" : false,
+      "type" : "date"
+    } ]
+  } ],
+  "version-log" : [ {
+    "timestamp-ms" : 1573518431292,
+    "version-id" : 1
+  }, {
+    "timestamp-ms" : 1573518981593,
+    "version-id" : 2
+  } ]
+}
diff --git a/view/view.go b/view/view.go
new file mode 100644
index 00000000..18db042a
--- /dev/null
+++ b/view/view.go
@@ -0,0 +1,120 @@
+// 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 (
+       "iter"
+       "slices"
+
+       "github.com/apache/iceberg-go"
+)
+
+// Metadata defines the format for view metadata,
+// similar to how Iceberg supports a common table format for tables
+type Metadata interface {
+       // ViewUUID identifies the view, generated when the view is created
+       ViewUUID() string
+       // FormatVersion is the version number for the view format; must be 1
+       FormatVersion() int
+       // Location is the view's base location; used to create metadata file 
locations
+       Location() string
+       // Schemas is a list of known schemas
+       Schemas() iter.Seq[*iceberg.Schema]
+       // CurrentVersion is the current version of the view
+       CurrentVersion() *Version
+       // Versions is a list of known versions of the view
+       Versions() iter.Seq[Version]
+       // VersionLog is a list of version log entries with the timestamp and 
version-id for every change to current-version-id
+       VersionLog() iter.Seq[VersionLogEntry]
+       // Properties is a string to string map of view properties
+       Properties() iceberg.Properties
+}
+
+type metadata struct {
+       UUID             string             `json:"view-uuid"`
+       FmtVersion       int                `json:"format-version"`
+       Loc              string             `json:"location"`
+       SchemaList       []*iceberg.Schema  `json:"schemas"`
+       CurrentVersionId int64              `json:"current-version-id"`
+       VersionList      []Version          `json:"versions"`
+       VersionLogList   []VersionLogEntry  `json:"version-log"`
+       Props            iceberg.Properties `json:"properties"`
+}
+
+func (m *metadata) ViewUUID() string {
+       return m.UUID
+}
+
+func (m *metadata) FormatVersion() int {
+       return m.FmtVersion
+}
+
+func (m *metadata) Location() string {
+       return m.Loc
+}
+
+func (m *metadata) Schemas() iter.Seq[*iceberg.Schema] {
+       return slices.Values(m.SchemaList)
+}
+
+func (m *metadata) CurrentVersion() *Version {
+       for i := range m.VersionLogList {
+               if m.VersionList[i].VersionID == m.CurrentVersionId {
+                       return &m.VersionList[i]
+               }
+       }
+
+       return nil
+}
+
+func (m *metadata) Versions() iter.Seq[Version] {
+       return slices.Values(m.VersionList)
+}
+
+func (m *metadata) VersionLog() iter.Seq[VersionLogEntry] {
+       return slices.Values(m.VersionLogList)
+}
+
+func (m *metadata) Properties() iceberg.Properties {
+       return m.Props
+}
+
+// Version represents the view definition at a point in time
+type Version struct {
+       VersionID        int64               `json:"version-id"`
+       SchemaID         int                 `json:"schema-id"`
+       TimestampMs      int64               `json:"timestamp-ms"`
+       Summary          map[string]string   `json:"summary"`
+       Representations  []SQLRepresentation `json:"representations"`
+       DefaultCatalog   *string             `json:"default-catalog"`
+       DefaultNamespace []string            `json:"default-namespace"`
+}
+
+// SQLRepresentation is a view in SQL with a given dialect
+type SQLRepresentation struct {
+       Type    string `json:"type"`
+       SQL     string `json:"sql"`
+       Dialect string `json:"dialect"`
+}
+
+// VersionLogEntry contains a change to the view state.
+// At the given timestamp, the current version was set to the given version ID.
+type VersionLogEntry struct {
+       TimestampMs int64 `json:"timestamp-ms"`
+       VersionID   int64 `json:"version-id"`
+}

Reply via email to