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 7939bd96 feat: load view (#640)
7939bd96 is described below
commit 7939bd96eda59c47739e8172dd27db25efe8fb42
Author: M Alvee <[email protected]>
AuthorDate: Tue Dec 9 23:36:20 2025 +0600
feat: load view (#640)
load view metadata
---
catalog/rest/rest.go | 30 +++++++++++++
catalog/rest/rest_test.go | 112 ++++++++++++++++++++++++++++++++++++++++++++--
2 files changed, 138 insertions(+), 4 deletions(-)
diff --git a/catalog/rest/rest.go b/catalog/rest/rest.go
index c5ae8382..1633ffb7 100644
--- a/catalog/rest/rest.go
+++ b/catalog/rest/rest.go
@@ -1269,3 +1269,33 @@ func (r *Catalog) UpdateView(ctx context.Context, ident
table.Identifier, requir
return view.New(ident, ret.Metadata, ret.MetadataLoc), nil
}
+
+// loadViewResponse contains the response from loading a view
+type loadViewResponse struct {
+ MetadataLoc string `json:"metadata-location"`
+ RawMetadata json.RawMessage `json:"metadata"`
+ Config iceberg.Properties `json:"config"`
+}
+
+// LoadView loads a view from the catalog.
+func (r *Catalog) LoadView(ctx context.Context, identifier table.Identifier)
(*view.View, error) {
+ ns, v, err := splitIdentForPath(identifier)
+ if err != nil {
+ return nil, err
+ }
+
+ rsp, err := doGet[loadViewResponse](ctx, r.baseURI,
[]string{"namespaces", ns, "views", v},
+ r.cl, map[int]error{
+ http.StatusNotFound: catalog.ErrNoSuchView,
+ })
+ if err != nil {
+ return nil, err
+ }
+
+ metadata, err := view.ParseMetadataBytes(rsp.RawMetadata)
+ if err != nil {
+ return nil, fmt.Errorf("failed to parse view metadata: %w", err)
+ }
+
+ return view.New(identifier, metadata, rsp.MetadataLoc), nil
+}
diff --git a/catalog/rest/rest_test.go b/catalog/rest/rest_test.go
index 0a842205..c515b360 100644
--- a/catalog/rest/rest_test.go
+++ b/catalog/rest/rest_test.go
@@ -1102,7 +1102,7 @@ func (r *RestCatalogSuite) TestLoadTable200() {
r.Equal(v, req.Header.Values(k))
}
- w.Write([]byte(`{
+ w.Write([]byte(`{
"metadata-location":
"s3://warehouse/database/table/metadata/00001-5f2f8166-244c-4eae-ac36-384ecdec81fc.gz.metadata.json",
"metadata": {
"format-version": 1,
@@ -1387,14 +1387,14 @@ func (r *RestCatalogSuite) TestRegisterTable200() {
}
],
"partition-spec": [
-
+
],
"default-spec-id": 0,
"partition-specs": [
{
"spec-id": 0,
"fields": [
-
+
]
}
],
@@ -1404,7 +1404,7 @@ func (r *RestCatalogSuite) TestRegisterTable200() {
{
"order-id": 0,
"fields": [
-
+
]
}
],
@@ -1872,6 +1872,110 @@ func (r *RestCatalogSuite) TestCheckViewExists404() {
r.False(exists)
}
+func (r *RestCatalogSuite) TestLoadView200() {
+ r.mux.HandleFunc("/v1/namespaces/fokko/views/myview", func(w
http.ResponseWriter, req *http.Request) {
+ r.Require().Equal(http.MethodGet, req.Method)
+
+ for k, v := range TestHeaders {
+ r.Equal(v, req.Header.Values(k))
+ }
+
+ w.Write([]byte(`{
+ "metadata-location":
"s3://bucket/warehouse/default.db/event_agg/metadata/00001.metadata.json",
+ "metadata": {
+ "view-uuid":
"fa6506c3-7681-40c8-86dc-e36561f83385",
+ "format-version": 1,
+ "location":
"s3://bucket/warehouse/default.db/event_agg",
+ "current-version-id": 1,
+ "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 COUNT(1),
CAST(event_ts AS DATE) FROM events GROUP BY 2",
+ "dialect": "spark"
+ }]
+ }],
+ "schemas": [{
+ "schema-id": 1,
+ "type": "struct",
+ "fields": [
+ {"id": 1, "name":
"event_count", "required": false, "type": "int"},
+ {"id": 2, "name": "event_date",
"required": false, "type": "date"}
+ ]
+ }],
+ "version-log": [{
+ "timestamp-ms": 1573518431292,
+ "version-id": 1
+ }]
+ },
+ "config": {}
+ }`))
+ })
+
+ cat, err := rest.NewCatalog(context.Background(), "rest", r.srv.URL,
rest.WithOAuthToken(TestToken))
+ r.Require().NoError(err)
+
+ v, err := cat.LoadView(context.Background(),
catalog.ToIdentifier("fokko", "myview"))
+ r.Require().NoError(err)
+
+ r.Equal(table.Identifier{"fokko", "myview"}, v.Identifier())
+
r.Equal("s3://bucket/warehouse/default.db/event_agg/metadata/00001.metadata.json",
v.MetadataLocation())
+
+ metadata := v.Metadata()
+ r.Equal(uuid.MustParse("fa6506c3-7681-40c8-86dc-e36561f83385"),
metadata.ViewUUID())
+ r.Equal(1, metadata.FormatVersion())
+ r.Equal("s3://bucket/warehouse/default.db/event_agg",
metadata.Location())
+ r.Equal("Daily event counts", metadata.Properties()["comment"])
+
+ currentVersion := metadata.CurrentVersion()
+ r.NotNil(currentVersion)
+ r.EqualValues(1, currentVersion.VersionID)
+ r.Equal(1, currentVersion.SchemaID)
+ r.EqualValues(1573518431292, currentVersion.TimestampMS)
+ r.Equal("Spark", currentVersion.Summary["engine-name"])
+ r.Len(currentVersion.Representations, 1)
+ r.Equal("sql", currentVersion.Representations[0].Type)
+ r.Equal("spark", currentVersion.Representations[0].Dialect)
+}
+
+func (r *RestCatalogSuite) TestLoadView404() {
+ r.mux.HandleFunc("/v1/namespaces/fokko/views/nonexistent", func(w
http.ResponseWriter, req *http.Request) {
+ r.Require().Equal(http.MethodGet, req.Method)
+
+ for k, v := range TestHeaders {
+ r.Equal(v, req.Header.Values(k))
+ }
+
+ w.WriteHeader(http.StatusNotFound)
+ json.NewEncoder(w).Encode(map[string]any{
+ "error": map[string]any{
+ "message": "View does not exist:
fokko.nonexistent",
+ "type": "NoSuchViewException",
+ "code": 404,
+ },
+ })
+ })
+
+ cat, err := rest.NewCatalog(context.Background(), "rest", r.srv.URL,
rest.WithOAuthToken(TestToken))
+ r.Require().NoError(err)
+
+ _, err = cat.LoadView(context.Background(),
catalog.ToIdentifier("fokko", "nonexistent"))
+ r.Error(err)
+ r.ErrorIs(err, catalog.ErrNoSuchView)
+ r.ErrorContains(err, "View does not exist: fokko.nonexistent")
+}
+
type RestTLSCatalogSuite struct {
suite.Suite