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"`
+}