This post was written in collaboration with the Oracle Autonomous AI Database development team, with special thanks to Ming Yang for his guidance and technical contributions.

In the previous two posts, we showed two ways to query Databricks data from Oracle Autonomous AI Database.

First, we queried a Databricks Delta table through UniForm. That pattern is useful when Databricks remains Delta-first, while Oracle reads Iceberg-compatible metadata generated for the Delta table.

Second, we queried a native Apache Iceberg table created in Databricks. That pattern is useful when the table is Iceberg from day one.

In this post, we take the next step. Instead of creating one Oracle external table for every Databricks table, we mount the Databricks Unity Catalog Iceberg REST catalog once. After that, Oracle users can discover and query multiple Databricks tables through the mounted catalog.

This is the natural model for real lakehouse environments. Users do not want to manage a separate Oracle external table definition every time a Databricks team publishes a new data product. They want to see the catalog, find the tables they can access, and query them with SQL.

Databricks Unity Catalog exposes eligible tables through the Iceberg REST catalog endpoint, including managed Iceberg tables and Delta tables with Iceberg reads enabled through UniForm. Oracle Autonomous AI Database supports mounted catalogs through DBMS_CATALOG, including Iceberg REST catalogs, and lets users list tables through ALL_TABLES@catalog and query data using schema.table@catalog.

Use case

A data engineering team manages several curated tables in Databricks.

Some tables are Delta tables with UniForm enabled. Other tables are native Apache Iceberg tables. From the Databricks side, both are governed by Unity Catalog and exposed through the Iceberg REST interface.

An Oracle application team wants to query all approved Databricks data products from Oracle Autonomous AI Database.

The old approach is workable, but operationally heavy:

CREATE_EXTERNAL_TABLE for customers
CREATE_EXTERNAL_TABLE for orders
CREATE_EXTERNAL_TABLE for products
...

The mounted catalog approach is simpler:

Mount Databricks Unity Catalog once
List available tables
Query any authorized table through @catalog

When new eligible tables are added in Databricks and the service principal has access, they become discoverable through the mounted catalog. Oracle may cache catalog metadata, so users can refresh the catalog cache when they want new tables to appear immediately.

What we build

In Databricks, we create two tables:

  1. mount_customers_uniform, a Delta table with UniForm Iceberg reads enabled.
  2. mount_orders_iceberg, a native Apache Iceberg table.

In Oracle Autonomous Database, we mount the Databricks Unity Catalog Iceberg REST endpoint once as DBX_ICEBERG_BLOG_DEMO

Then we list and query both tables from Oracle without creating Oracle external tables for each one.

Databricks setup

Step 1. Create a Delta table with UniForm enabled

CREATE TABLE data_lake_pm.default.mount_customers_uniform (
  customer_id BIGINT,
  first_name  STRING,
  last_name   STRING,
  email       STRING,
  city        STRING,
  state       STRING,
  country     STRING,
  signup_date DATE,
  spend       DECIMAL(10,2)
)
TBLPROPERTIES (
  'delta.columnMapping.mode' = 'id',
  'delta.enableIcebergCompatV2' = 'true',
  'delta.universalFormat.enabledFormats' = 'iceberg'
);

INSERT INTO data_lake_pm.default.mount_customers_uniform VALUES
  (1, 'John', 'Smith', 'john.smith@example.com', 'Seattle', 'WA', 'US', DATE '2026-06-01', 100.25),
  (2, 'Mary', 'Johnson', 'mary.johnson@example.com', 'San Francisco', 'CA', 'US', DATE '2026-06-02', 250.00),
  (3, 'Alex', 'Brown', 'alex.brown@example.com', 'Austin', 'TX', 'US', DATE '2026-06-03', 175.50);

UniForm enables Iceberg reads by generating Iceberg metadata for a Delta table, without rewriting the data files. Databricks documents the required table properties delta.enableIcebergCompatV2=true and delta.universalFormat.enabledFormats=iceberg.

Step 2. Create a native Iceberg table

CREATE TABLE data_lake_pm.default.mount_orders_iceberg (
  order_id    BIGINT,
  customer_id BIGINT,
  order_date  DATE,
  amount      DECIMAL(10,2)
)
USING ICEBERG;

INSERT INTO data_lake_pm.default.mount_orders_iceberg VALUES
  (101, 1, DATE '2026-06-04', 120.00),
  (102, 2, DATE '2026-06-05', 250.50),
  (103, 3, DATE '2026-06-06', 75.25);

Now the Databricks catalog contains both types of tables: a Delta table exposed through UniForm and a native Iceberg table.

Step 3. Grant access to the service principal

Oracle will connect to Databricks using a Databricks service principal. Grant access at the schema level so the service principal can see current and future tables in the schema.

GRANT USE CATALOG ON CATALOG data_lake_pm
TO `databricks-service-principal-application-id`;

GRANT USE SCHEMA ON SCHEMA data_lake_pm.default
TO `databricks-service-principal-application-id`;

GRANT SELECT ON SCHEMA data_lake_pm.default
TO `databricks-service-principal-application-id`;

GRANT EXTERNAL USE SCHEMA ON SCHEMA data_lake_pm.default
TO `databricks-service-principal-application-id`;

GRANT BROWSE ON CATALOG data_lake_pm
TO `databricks-service-principal-application-id`;

Databricks requires External data access to be enabled, EXTERNAL USE SCHEMA on the schema, and authentication using a Databricks personal access token or OAuth for Iceberg REST catalog clients.

Oracle Autonomous Database setup

Oracle needs two credentials:

  1. UNITY_BEARER_CRED, used to authenticate to Databricks Unity Catalog Iceberg REST.
  2. AZURE_BLOB_CRED, used to access the cloud object storage files behind the Databricks tables.

AZURE_BLOB_CRED is the same storage credential used in the previous posts.

Step 1. Create the Databricks bearer credential

BEGIN
  BEGIN
    DBMS_CLOUD.DROP_CREDENTIAL('UNITY_BEARER_CRED');
  EXCEPTION
    WHEN OTHERS THEN NULL;
  END;

  DBMS_SHARE.CREATE_BEARER_TOKEN_CREDENTIAL(
    credential_name => 'UNITY_BEARER_CRED',
    token_endpoint  => 'https://databricks-workspace-host/oidc/v1/token',
    client_id       => 'databricks-service-principal-application-id',
    client_secret   => 'databricks-oauth-client-secret',
    token_scope     => 'all-apis',
    grant_type      => 'client_credentials'
  );
END;
/

Step 2. Mount the Databricks Unity Catalog Iceberg REST catalog

The key detail is the endpoint. For Databricks Unity Catalog, include the Unity Catalog name in the Iceberg REST path:

/api/2.1/unity-catalog/iceberg-rest/v1/catalogs/<uc-catalog-name>
BEGIN
  DBMS_CATALOG.UNMOUNT('DBX_ICEBERG_BLOG_DEMO');
EXCEPTION
  WHEN OTHERS THEN NULL;
END;
/

BEGIN
  DBMS_CATALOG.MOUNT_ICEBERG(
    catalog_name             => 'DBX_ICEBERG_BLOG_DEMO',
    endpoint                 => 'https://databricks-workspace-host/api/2.1/unity-catalog/iceberg-rest/v1/catalogs/data_lake_pm',
    catalog_credential       => 'UNITY_BEARER_CRED',
    data_storage_credential  => 'AZURE_BLOB_CRED',
    enabled                  => TRUE,
    catalog_type             => 'ICEBERG_UNITY'
  );
END;
/

Step 3. Refresh catalog metadata

BEGIN
  DBMS_CATALOG.FLUSH_CATALOG_CACHE('DBX_ICEBERG_BLOG_DEMO');
  DBMS_CATALOG.PREFILL_CATALOG_CACHE('DBX_ICEBERG_BLOG_DEMO', 'DEFAULT');
END;
/

Step 4. List tables

SELECT owner, table_name
FROM all_tables@DBX_ICEBERG_BLOG_DEMO
ORDER BY owner, table_name;

Example result:

OWNER    TABLE_NAME
DEFAULT  MOUNT_CUSTOMERS_UNIFORM
DEFAULT  MOUNT_ORDERS_ICEBERG

Use the exact OWNER and TABLE_NAME values returned by ALL_TABLES@catalog. Oracle’s remote catalog examples use quoted schema and table names, for example "SH"."SALES"@catalogname.

Step 5. Query the UniForm Delta table

SELECT customer_id, first_name, last_name, city, state, spend
FROM "DEFAULT"."MOUNT_CUSTOMERS_UNIFORM"@DBX_ICEBERG_BLOG_DEMO
ORDER BY customer_id;

Step 6. Query the native Iceberg table

SELECT order_id, customer_id, order_date, amount
FROM "DEFAULT"."MOUNT_ORDERS_ICEBERG"@DBX_ICEBERG_BLOG_DEMO
ORDER BY order_id;

Step 7. Join both Databricks tables from Oracle

SELECT c.customer_id,
       c.first_name,
       c.last_name,
       SUM(o.amount) AS total_amount
FROM "DEFAULT"."MOUNT_CUSTOMERS_UNIFORM"@DBX_ICEBERG_BLOG_DEMO c
JOIN "DEFAULT"."MOUNT_ORDERS_ICEBERG"@DBX_ICEBERG_BLOG_DEMO o
  ON c.customer_id = o.customer_id
GROUP BY c.customer_id, c.first_name, c.last_name
ORDER BY c.customer_id;

From the Oracle user’s point of view, both tables are just SQL-accessible catalog objects. One is a Delta table exposed through UniForm. The other is native Iceberg. The access pattern is the same.

Add a new table in Databricks

Now create a third table in Databricks.

CREATE TABLE data_lake_pm.default.mount_products_iceberg (
  product_id BIGINT,
  name       STRING,
  category   STRING
)
USING ICEBERG;

INSERT INTO data_lake_pm.default.mount_products_iceberg VALUES
  (1, 'Laptop', 'Electronics'),
  (2, 'Desk',   'Furniture');

No Oracle external table is required.

Refresh the mounted catalog metadata if you want Oracle to see the table immediately (or wait for configured auto-refresh interval):

BEGIN
  DBMS_CATALOG.FLUSH_CATALOG_CACHE('DBX_ICEBERG_BLOG_DEMO');
  DBMS_CATALOG.PREFILL_CATALOG_CACHE('DBX_ICEBERG_BLOG_DEMO', 'DEFAULT');
END;
/

Then query the new table:

SELECT product_id, name, category
FROM "DEFAULT"."MOUNT_PRODUCTS_ICEBERG"@DBX_ICEBERG_BLOG_DEMO
ORDER BY product_id;

This is the main benefit of the mounted catalog pattern: when Databricks teams publish new authorized data products, Oracle users do not need a new CREATE_EXTERNAL_TABLE for each table. They query through the mounted catalog.

Why this matters

The first two posts focused on table-level access:

BlogPatternBest for
Part 1Delta + UniFormDatabricks teams that remain Delta-first
Part 2Native IcebergTeams creating Iceberg tables directly in Databricks
Part 3Mounted catalogOracle users who want to discover and query many Databricks tables

The mounted catalog pattern changes the operating model.

Instead of treating each table as a separate integration task, Oracle Autonomous AI Database treats Databricks Unity Catalog as a catalog. Users can list available tables, query multiple tables, and pick up new authorized tables through catalog discovery.

This is especially useful when Databricks is the publishing platform for data products and Oracle Autonomous AI Database is one of the consuming SQL engines.

Conclusion

Delta UniForm and native Iceberg solve the table-format interoperability problem. Mounted catalogs solve the operational problem.

With DBMS_CATALOG.MOUNT_ICEBERG, Oracle Autonomous AI Database can mount a Databricks Unity Catalog Iceberg REST endpoint once and use it as a governed metadata layer. Oracle users can then query both UniForm-enabled Delta tables and native Iceberg tables through the same @catalog syntax.

For customers building multi-engine lakehouse architectures, this is the cleanest pattern: Databricks owns and governs the data products, Unity Catalog exposes the metadata, and Oracle Autonomous Database queries the authorized tables without copying data or creating one external table per table.