Iceberg Views in AIStor Tables
With general availability, AIStor Tables will become is now the industry’s first on-prem and hybrid-enabled embedded catalog to implement Iceberg REST Catalog views. This is a notable addition. When AWS launched S3 Tables in late 2024, they shipped table operations but left views out of their REST catalog implementation. AIStor Tables closes that gap, giving self-hosted deployments the same view capabilities that the Iceberg specification defines but few catalogs actually deliver.
Why Views Matter
Views in Iceberg are virtual tables defined by SQL queries. They store metadata about the query definition, not the data itself. This enables data abstraction, fine-grained access control, and simplified querying without duplicating storage.
Unlike engine-specific views (Athena views, Trino views), Iceberg views live in the catalog alongside your tables. Any engine that connects to the catalog can discover and query them.
View Endpoints
AIStor Tables implements all view operations from the Iceberg REST Catalog OpenAPI spec.
List Views
Retrieve all views in a namespace:
GET /v1/{warehouse}/namespaces/{namespace}/views
Response:
{
"identifiers": [
{
"name": "active_customers",
"namespace": ["analytics"]
},
{
"name": "daily_revenue",
"namespace": ["analytics"]
},
{
"name": "regional_summary",
"namespace": ["analytics"]
}
]
}
Create View
Create a view with its schema, SQL definition, and properties:
POST /v1/{warehouse}/namespaces/{namespace}/views
Content-Type: application/json
Request:
{
"name": "daily_revenue",
"schema": {
"type": "struct",
"fields": [
{"id": 1, "name": "order_date", "type": "date", "required": true},
{"id": 2, "name": "total_revenue", "type": "double", "required": true},
{"id": 3, "name": "order_count", "type": "long", "required": true}
]
},
"view-version": {
"version-id": 1,
"schema-id": 0,
"default-catalog": "warehouse",
"default-namespace": ["analytics"],
"summary": {
"engine-name": "spark",
"engine-version": "3.5.0"
},
"representations": [
{
"type": "sql",
"sql": "SELECT order_date, SUM(amount) as total_revenue, COUNT(*) as order_count FROM analytics.orders WHERE status = 'completed' GROUP BY order_date",
"dialect": "spark"
}
]
},
"properties": {
"comment": "Daily revenue aggregation from completed orders"
}
}
Response:
{
"metadata": {
"format-version": 1,
"view-uuid": "0ae2a50c-587e-4e61-9d59-ab3542bac3ae",
"location": "s3://warehouse/95b285cd-43d4-49fe-86d8-5b4ecb29ed66",
"current-version-id": 1,
"versions": [
{
"version-id": 1,
"schema-id": 0,
"timestamp-ms": 0,
"summary": {
"engine-name": "spark",
"engine-version": "3.5.0"
},
"representations": [
{
"type": "sql",
"sql": "SELECT order_date, SUM(amount) as total_revenue, COUNT(*) as order_count FROM analytics.orders WHERE status = 'completed' GROUP BY order_date",
"dialect": "spark"
}
],
"default-namespace": ["analytics"],
"default-catalog": "warehouse"
}
],
"schemas": [
{
"type": "struct",
"fields": [
{"type": "date", "id": 1, "name": "order_date", "required": true},
{"type": "double", "id": 2, "name": "total_revenue", "required": true},
{"type": "long", "id": 3, "name": "order_count", "required": true}
],
"schema-id": 0,
"identifier-field-ids": []
}
],
"version-log": [
{"timestamp-ms": 0, "version-id": 1}
],
"properties": {
"comment": "Daily revenue aggregation from completed orders"
}
},
"metadata-location": "s3://warehouse/95b285cd-43d4-49fe-86d8-5b4ecb29ed66/metadata/00000-458f3e46-95f7-4be2-bc57-abac42910872.metadata.json"
}
The representations array holds the SQL definition. Views can include multiple representations for different dialects (Spark, Trino, Presto), and engines select the one matching their dialect.
Load View
Retrieve full view metadata including version history:
GET /v1/{warehouse}/namespaces/{namespace}/views/{view}
Response:
{
"metadata": {
"format-version": 1,
"view-uuid": "0ae2a50c-587e-4e61-9d59-ab3542bac3ae",
"location": "s3://warehouse/95b285cd-43d4-49fe-86d8-5b4ecb29ed66",
"current-version-id": 2,
"versions": [
{
"version-id": 1,
"schema-id": 0,
"timestamp-ms": 0,
"summary": {
"engine-name": "spark",
"engine-version": "3.5.0"
},
"representations": [
{
"type": "sql",
"sql": "SELECT order_date, SUM(amount) as total_revenue, COUNT(*) as order_count FROM analytics.orders WHERE status = 'completed' GROUP BY order_date",
"dialect": "spark"
}
],
"default-namespace": ["analytics"],
"default-catalog": "warehouse"
},
{
"version-id": 2,
"schema-id": 0,
"timestamp-ms": 0,
"summary": {
"engine-name": "spark",
"engine-version": "3.5.0"
},
"representations": [
{
"type": "sql",
"sql": "SELECT order_date, SUM(amount) as total_revenue, COUNT(*) as order_count, AVG(amount) as avg_order_value FROM analytics.orders WHERE status = 'completed' GROUP BY order_date ORDER BY order_date",
"dialect": "spark"
}
],
"default-namespace": ["analytics"],
"default-catalog": "warehouse"
}
],
"schemas": [
{
"type": "struct",
"fields": [
{"type": "date", "id": 1, "name": "order_date", "required": true},
{"type": "double", "id": 2, "name": "total_revenue", "required": true},
{"type": "long", "id": 3, "name": "order_count", "required": true}
],
"schema-id": 0,
"identifier-field-ids": []
}
],
"version-log": [
{"timestamp-ms": 0, "version-id": 1},
{"timestamp-ms": 0, "version-id": 2}
],
"properties": {
"comment": "Daily revenue aggregation from completed orders"
}
},
"metadata-location": "s3://warehouse/95b285cd-43d4-49fe-86d8-5b4ecb29ed66/metadata/00001-0665845a-e17a-4d30-851d-bde04218806b.metadata.json"
}
Each metadata file contains the complete version history. You can roll back to any previous view definition.
View Exists
Check if a view exists without loading its metadata:
HEAD /v1/{warehouse}/namespaces/{namespace}/views/{view}
Returns 204 if the view exists, 404 if not.
Replace View
Update a view's SQL definition. This creates a new version while preserving history:
POST /v1/{warehouse}/namespaces/{namespace}/views/{view}
Content-Type: application/json
Request:
{
"identifier": {"namespace": ["analytics"], "name": "daily_revenue"},
"requirements": [
{"type": "assert-view-uuid", "uuid": "0ae2a50c-587e-4e61-9d59-ab3542bac3ae"}
],
"updates": [
{
"action": "add-view-version",
"view-version": {
"version-id": 2,
"schema-id": 0,
"default-catalog": "warehouse",
"default-namespace": ["analytics"],
"summary": {
"engine-name": "spark",
"engine-version": "3.5.0"
},
"representations": [
{
"type": "sql",
"sql": "SELECT order_date, SUM(amount) as total_revenue, COUNT(*) as order_count, AVG(amount) as avg_order_value FROM analytics.orders WHERE status = 'completed' GROUP BY order_date ORDER BY order_date",
"dialect": "spark"
}
]
}
},
{"action": "set-current-view-version", "view-version-id": 2}
]
}
The requirements array enables optimistic concurrency control. The assert-view-uuid requirement ensures you're updating the expected view, preventing conflicts when multiple clients modify the same view.
Rename View
Move a view within or across namespaces:
POST /v1/{warehouse}/views/rename
Content-Type: application/json
Request:
{
"source": {"namespace": ["analytics"], "name": "regional_summary"},
"destination": {"namespace": ["analytics"], "name": "region_metrics"}
}
Returns 204 on success.
Drop View
Remove a view from the catalog:
DELETE /v1/{warehouse}/namespaces/{namespace}/views/{view}
Returns 204 on success.
Metadata Storage
View metadata follows the same storage pattern as tables:
s3://bucket/warehouse/{view-uuid}/metadata/00001-{uuid}.metadata.json
Updates create new metadata files. AIStor atomically swaps the current pointer, ensuring consistent reads across all connected query engines.
Query Engine Integration
Any engine supporting the Iceberg REST Catalog can query AIStor views.
Spark
spark = SparkSession.builder \
.config("spark.sql.catalog.aistor", "org.apache.iceberg.spark.SparkCatalog") \
.config("spark.sql.catalog.aistor.type", "rest") \
.config("spark.sql.catalog.aistor.uri", "https://aistor-endpoint/_iceberg") \
.config("spark.sql.catalog.aistor.warehouse", "warehouse") \
.getOrCreate()
df = spark.sql("SELECT * FROM aistor.analytics.daily_revenue WHERE order_date > '2024-01-01'")
PyIceberg
from pyiceberg.catalog.rest import RestCatalog
catalog = RestCatalog(
name="aistor",
uri="https://aistor-endpoint/_iceberg",
warehouse="warehouse",
**{
"rest.sigv4-enabled": "true",
"rest.signing-name": "s3tables",
"rest.signing-region": "us-east-1",
"s3.access-key-id": "YOUR_ACCESS_KEY",
"s3.secret-access-key": "YOUR_SECRET_KEY",
"s3.endpoint": "https://aistor-endpoint",
"s3.path-style-access": "true"
}
)
# List views in a namespace
views = catalog.list_views(("analytics",))
for view in views:
print(view) # ('analytics', 'daily_revenue')
Getting Started
View support is available in the latest AIStor Tables release. The endpoints follow the Iceberg REST Catalog specification, so existing Iceberg tooling works without modification.
Here is the link to Download now
"Apache Iceberg, Iceberg, Apache, the Apache feather logo, and the Apache Iceberg project logo are either registered trademarks or trademarks of The Apache Software Foundation. Copyright © 2025 The Apache Software Foundation, Licensed under the Apache License, Version 2.0."
