Reading Unity Catalog tables
To read a Unity Catalog-managed Delta table, you resolve the table name through
the UC REST API, fetch temporary storage credentials, load a Snapshot through
the UCKernelClient, and then build a Scan exactly as you would for a
filesystem-managed table.
Before reading this page, make sure you understand Catalog-Managed Tables and the Unity Catalog Integration overview.
Note
This example uses the
delta-kernel-unity-catalogandunity-catalog-delta-rest-clientcrates, which are not part of thedelta_kernelcrate itself. Add them as dependencies alongsidedelta_kernel.
Dependencies
Add the following to your Cargo.toml:
[dependencies]
delta_kernel = { version = "...", features = ["default-engine-rustls"] }
delta-kernel-unity-catalog = "..."
unity-catalog-delta-rest-client = "..."
unity-catalog-delta-client-api = "..."
url = "2"
tokio = { version = "1", features = ["full"] }
Use default-engine-rustls for a pure-Rust TLS stack or default-engine-native-tls
to link against the system’s TLS implementation. You need
unity-catalog-delta-client-api directly to import types like Operation, since
the REST client crate does not re-export them.
Step 1: Build the UC clients
The UC integration uses two clients that share a ClientConfig:
UCClienthandles table resolution (get_table) and credential vending (get_credentials).UCCommitsRestClientimplements theGetCommitsClienttrait, whichUCKernelClientuses to fetch ratified commits from the catalog.
use unity_catalog_delta_rest_client::{ClientConfig, UCClient, UCCommitsRestClient};
let config = ClientConfig::build(&endpoint, &token).build()?;
let uc_client = UCClient::new(config.clone())?;
let commits_client = UCCommitsRestClient::new(config)?;
The endpoint is your UC workspace URL (e.g. "my-workspace.cloud.databricks.com"),
and token is a valid authentication token.
Step 2: Resolve the table name
Call get_table with the three-level table name to get the table’s storage
location and table ID. The table_id identifies the table in UC’s commits API,
and the storage_location points to the table’s root directory in cloud
storage.
let table_info = uc_client.get_table("my_catalog.my_schema.my_table").await?;
let table_id = &table_info.table_id;
let table_uri = &table_info.storage_location;
The TablesResponse also includes metadata like catalog_name, schema_name,
data_source_format, and table_type. You can verify the table is a Delta
table by calling table_info.is_delta_table().
Step 3: Fetch temporary credentials
UC vends short-lived cloud storage credentials scoped to the table’s storage
location. For a read operation, request Operation::Read:
use unity_catalog_delta_client_api::Operation;
let creds = uc_client.get_credentials(table_id, Operation::Read).await?;
The returned TemporaryTableCredentials contains cloud-provider-specific
credentials. For AWS, extract the temporary credentials:
let aws_creds = creds.aws_temp_credentials
.ok_or("No AWS temporary credentials in response")?;
Warning
Vended credentials expire. The
TemporaryTableCredentialsstruct providesexpiration_time,is_expired(), andtime_until_expiry()to check validity. If your scan takes longer than the credential lifetime, you need to refresh credentials and rebuild the Engine before continuing. See Credential refresh below.
Note
Today
TemporaryTableCredentialsonly exposesaws_temp_credentials. Azure and GCP credential vending are tracked in delta-io/delta-kernel-rs#2434.
Step 4: Build an Engine with vended credentials
Pass the temporary credentials as storage options when constructing the object
store, then wrap it in a DefaultEngineBuilder:
use std::sync::Arc;
use delta_kernel::engine::default::DefaultEngineBuilder;
use delta_kernel::object_store;
let table_url = url::Url::parse(table_uri)?;
let options = [
("region", "us-west-2"),
("access_key_id", &aws_creds.access_key_id),
("secret_access_key", &aws_creds.secret_access_key),
("session_token", &aws_creds.session_token),
];
let (store, _path) = object_store::parse_url_opts(&table_url, options)?;
let engine = DefaultEngineBuilder::new(store.into()).build();
The .into() converts the Box<dyn ObjectStore> returned by parse_url_opts
into the Arc<dyn ObjectStore> that DefaultEngineBuilder::new expects. Set
the region to match the bucket’s actual AWS region.
Step 5: Load a Snapshot through UCKernelClient
UCKernelClient wraps a GetCommitsClient and handles the full snapshot
loading flow: it calls the UC commits API to get the latest ratified commits,
translates them into LogPath entries, and builds a
Snapshot with the log tail.
use delta_kernel_unity_catalog::UCKernelClient;
let catalog = UCKernelClient::new(&commits_client);
let snapshot = catalog.load_snapshot(table_id, table_uri, &engine).await?;
println!("Table version: {}", snapshot.version());
To read a specific version, use load_snapshot_at:
let snapshot = catalog.load_snapshot_at(table_id, table_uri, 5, &engine).await?;
The requested version must not exceed the latest version the catalog has
ratified. If it does, load_snapshot_at returns an error.
Step 6: Build and execute a Scan
From this point on, reading works identically to a filesystem-managed table. Build a Scan from the Snapshot and iterate over the results:
let scan = snapshot.scan_builder().build()?;
for data in scan.execute(Arc::new(engine))? {
let batch = data?;
// process each batch of data
}
You can use all the same Scan features: column selection, filter pushdown, and scan metadata for distributed reads.
Complete example
This example puts all the steps together. It connects to Unity Catalog, resolves a table, fetches credentials, loads a Snapshot, and reads the data.
use std::sync::Arc;
use delta_kernel::engine::default::DefaultEngineBuilder;
use delta_kernel::object_store;
use delta_kernel_unity_catalog::UCKernelClient;
use unity_catalog_delta_client_api::Operation;
use unity_catalog_delta_rest_client::{ClientConfig, UCClient, UCCommitsRestClient};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// 1. Build UC clients
let config = ClientConfig::build(&endpoint, &token).build()?;
let uc_client = UCClient::new(config.clone())?;
let commits_client = UCCommitsRestClient::new(config)?;
// 2. Resolve the table name
let table_info = uc_client.get_table("my_catalog.my_schema.my_table").await?;
let table_id = &table_info.table_id;
let table_uri = &table_info.storage_location;
// 3. Fetch temporary credentials
let creds = uc_client.get_credentials(table_id, Operation::Read).await?;
let aws_creds = creds.aws_temp_credentials
.ok_or("No AWS temporary credentials")?;
// 4. Build an Engine with the vended credentials
let table_url = url::Url::parse(table_uri)?;
let (store, _) = object_store::parse_url_opts(&table_url, [
("region", "us-west-2"),
("access_key_id", &aws_creds.access_key_id),
("secret_access_key", &aws_creds.secret_access_key),
("session_token", &aws_creds.session_token),
])?;
let engine = DefaultEngineBuilder::new(store.into()).build();
// 5. Load the Snapshot via UCKernelClient
let catalog = UCKernelClient::new(&commits_client);
let snapshot = catalog.load_snapshot(table_id, table_uri, &engine).await?;
println!("Loaded table at version {}", snapshot.version());
// 6. Build and execute the Scan
let scan = snapshot.scan_builder().build()?;
for data in scan.execute(Arc::new(engine))? {
let batch = data?;
// process each batch of data
}
Ok(())
}
Credential refresh for long-running operations
UC vends temporary credentials with a limited lifetime. For short-lived scans,
you don’t need to worry about expiration. For long-running operations (large
table scans, streaming reads), check creds.is_expired() or
creds.time_until_expiry() before starting a new phase of work.
If credentials have expired, call get_credentials again and rebuild the
Engine with the fresh credentials before continuing. Kernel does not manage
credential lifecycle for you.
What’s next
- Writing to UC Tables: how to append data and commit through Unity Catalog
- Building a Scan: scan configuration options that apply to both filesystem and catalog-managed tables
- Filter Pushdown and File Skipping: reduce the data you read with predicates