Iceberg Views in AIStor Tables

Bill Miller Bill Miller on |
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."