Delta / Unity Catalog Connector¶
Overview¶
connectors/catalogs/delta/ implements a connector targeting Databricks Unity Catalog-powered Delta Lake
warehouses. It uses the Delta Kernel, Unity Catalog REST APIs, and AWS S3
(through the v2 client) to enumerate tables, collect statistics, and plan files.
The primary implementation is DeltaConnector (abstract) with source-specific subclasses for Unity
Catalog, AWS Glue, and filesystem-backed tables, exposed via DeltaConnectorProvider. Supporting classes manage
OAuth2 bearer token usage (including CLI, service principal, and WIF flows resolved upstream), a
typed Unity Catalog client boundary, and custom file readers for S3.
Architecture & Responsibilities¶
DeltaConnector– AbstractFloecatConnectorthat centralizes snapshot/stat logic.UnityDeltaConnector– Unity Catalog-backed connector that:- Uses
UnityCatalogClientto list catalogs/schemas/tables. - Vends table-scoped storage credentials through the client's temporary-credentials operation.
- Uses Delta Kernel (
io.delta.kernel.Table) for schema and snapshot access. - Reads Parquet data with
S3V2FileSystemClientandParquetS3V2InputFilefor NDV/statistics. - Plans files using
DeltaPlanner, emittingScanFiles for data/delete manifests. DeltaFilesystemConnector– Single-table connector fordelta.table-rootplus optionalexternal.namespace/external.table-nameoverrides.DeltaGlueConnector– AWS Glue-backed connector that:- Lists databases and Delta-registered tables from Glue.
- Resolves table
storage_locationfrom Glue metadata and reads table snapshots via Delta Kernel. DeltaConnectorFactory– Selects Unity, Glue, or filesystem sources and wires engine/auth/IO.UnityCatalogClient/HttpUnityCatalogClient– Typed client boundary and HTTP adapter for Unity Catalog metadata, pagination, error classification, and credential vending. The adapter lives inconnectors/clients/unity-catalog/; connector domain code does not depend on Java HTTP types.DeltaTypeMapper– Maps Delta/Parquet logical types into Floecat logical types for stats.
Public API / Surface Area¶
DeltaConnector and subclasses implement the SPI methods:
listNamespaces()– Fetches catalogs via/api/2.1/unity-catalog/catalogs, then enumerates schemas per catalog, returningcatalog.schemapairs.listTables(namespace)– Calls/api/2.1/unity-catalog/tablesfiltered by catalog/schema, then filters todata_source_format == DELTA.describe(namespace, table)– Fetches table metadata from Unity Catalog, reads the Delta schema via Delta Kernel, and returns aTableDescriptorcontaining location, partition keys, and properties.enumerateSnapshots(...)– Iterates Delta snapshots and emitsSnapshotBundles for snapshot lineage/metadata. In incremental mode, the connector enumerates all Delta versions that Floecat has not already ingested. WhenSnapshotEnumerationOptions.targetSnapshotIdsis supplied, enumeration is limited to that explicit version set even whenfullRescan=true.planSnapshotFiles(...)– Produces immutable data-file membership, on-disk deletion-vector attachments, execution schema, and a physical content identity for every Delta add-file.capturePlannedFileGroup(...)– Captures the missing stats and page-index outputs for an immutable planned subset of the snapshot's data files.captureSnapshotTargetStats(...)– Captures table/column/file stats for one snapshot and optional selector scope, optionally sampling Parquet files for NDV (SamplingNdvProvider,ParquetNdvProvider).
Important Internal Details¶
- Authentication – Uses an OAuth2 bearer token supplied in the resolved connector config or
the Databricks CLI cache. Token exchange and secret handling happen earlier in the service layer,
except for CLI cache refresh which is handled in the connector.
For
delta.source=glueanddelta.source=filesystem, AWS temporary credentials fromaws-assume-roleoraws-web-identityare preserved as a shared refreshable provider so native Glue catalog access and S3 reads use the same AWS identity. - Unity Catalog client –
HttpUnityCatalogClientcentralizes base URI, connect/read timeouts, pagination, JSON decoding, and semantic error mapping behind theUnityCatalogClientinterface. - S3 integration – Uses AWS SDK v2 (
S3Client) with region from connector properties to read data files.S3RangeReaderprovides efficient range reads for Parquet file access. - NDV sampling – Controlled by
stats.ndv.enabled,stats.ndv.sample_fraction, andstats.ndv.max_files. Samples combine streaming NDV with Parquet footers for accuracy. - Type mapping –
DeltaTypeMapperconverts Delta Kernel types to canonical logical types, recursing into array/map/struct so element/key/value/field types andcontainsNull/valueContainsNullare preserved in theLogicalTypetree (seedocs/types.md, "Complex types"). - Reuse identity – Delta data-file content identity is derived from add-file modification time, base row ID, and default row commit version. It is computed from Delta log metadata without reading the data file, stored stats, or page-index sidecars. Deletion-vector path, offset, size, and cardinality are incorporated separately into the stats source fingerprint.
- Constraint mapping – Snapshot constraints currently emit metadata that is reliably exposed by Delta snapshots/table metadata:
CT_NOT_NULLfrom non-nullable schema fields (including nested struct leaves).CT_CHECKfrom table properties usingdelta.constraints.<name>=<sql_expression>.CT_PRIMARY_KEY,CT_FOREIGN_KEY, andCT_UNIQUEare not emitted from core Delta metadata because no portable source is defined for them.- Source-specific extraction path:
- Unity Catalog: merge of snapshot metadata + UC table properties from
/api/2.1/unity-catalog/tables/{full_name}. Snapshot metadata wins on key collisions. - Glue: merge of snapshot metadata + Glue table parameters. Snapshot metadata wins on key collisions.
- Filesystem: snapshot metadata only.
- Unity Catalog: merge of snapshot metadata + UC table properties from
- Connector matrix (current behavior):
- Unity:
CT_NOT_NULL,CT_CHECK(delta.constraints.*) from merged snapshot + UC metadata. - Glue:
CT_NOT_NULL,CT_CHECK(delta.constraints.*) from merged snapshot + Glue metadata. - Filesystem:
CT_NOT_NULL,CT_CHECK(delta.constraints.*) from snapshot metadata only.
- Unity:
Data Flow & Lifecycle¶
ConnectorFactory.create(cfg)
→ DeltaConnectorFactory.create(uri, options, authProvider)
→ Select Unity vs filesystem source
→ Instantiate S3 client + Delta Kernel engine
→ Configure the UnityCatalogClient HTTP adapter when needed
→ listNamespaces/listTables via Unity Catalog REST
→ describe via REST + Delta Kernel schema inspection
→ enumerateSnapshots
→ Delta Kernel snapshot lineage
→ planSnapshotFiles
→ DeltaPlanner traverses _delta_log → immutable data-file/deletion-vector plan
→ capturePlannedFileGroup
→ read only the planned data files needed for missing stats/index outputs
→ captureSnapshotTargetStats
→ Delta Kernel Snapshot → Parquet stats engine → TargetStatsRecord (table/column/file stats)
Source-specific resources follow the FloecatConnector.close() lifecycle:
UnityDeltaConnector.close() releases the UnityCatalogClient transport, which matters because the
storage service builds a connector per vend (once per scan session and once per file group).
Per-file S3 range readers are closed by their consumers.
Configuration & Extensibility¶
Important connector properties:
delta.source– Selects backend (unity,glue,filesystem). Defaults tounity.delta.table-root– Required fordelta.source=filesystem, pointing at a Delta table root.external.namespace,external.table-name– Optional overrides for filesystem connector naming.http.connect.ms,http.read.ms– Timeout controls for Unity Catalog HTTP calls.- Unity Catalog connector URIs must use
https://. Cleartexthttp://is rejected unless the host is a loopback address and the operator has setfloecat.security.allow-loopback-catalog-endpoints=true(orFLOECAT_SECURITY_ALLOW_LOOPBACK_CATALOG_ENDPOINTS=true). It defaults to deny, so a tenant-supplied connector URI cannot grant itself cleartext transport; this mirrorsfloecat.security.allow-loopback-token-endpointsfor OAuth token endpoints. Private-CA HTTPS endpoints need no flag -- only the JVM truststore. - A Unity Catalog connector URI naming a private address literal (10/8, 172.16/12, 192.168/16,
IPv6 unique-local) is rejected unless
floecat.security.allow-private-catalog-endpoints=true(orFLOECAT_SECURITY_ALLOW_PRIVATE_CATALOG_ENDPOINTS=true). Set it for a catalog on an internal network. Link-local, wildcard and multicast literals -- including the cloud metadata address169.254.169.254-- are always rejected, with no opt-in. These checks apply only to address literals: a hostname is never resolved during validation, deliberately, because a resolver in the gate would disagree with the resolution the HTTP client performs at connect time. A hostname pointing at a private address is therefore out of scope here and belongs to network policy. unity.temporary-table-vend-path– Route for temporary-table-credential vending. Defaults to the Databricks path/api/2.0/unity-catalog/temporary-table-credentials. OSS Unity Catalog 0.6.0 and later serve the otherwise-compatible operation under/api/2.1/unity-catalog/temporary-table-credentials, so point this at the 2.1 route for an OSS endpoint -- otherwise vending fails against it:INVALID_REQUESTwhere the workspace names the route in its error envelope, as Databricks does withENDPOINT_NOT_FOUND, andNOT_FOUNDwhere it sends no envelope, as OSS Unity Catalog does. Every other Unity operation is 2.1 on both. Response-body suppression on that path follows whatever route is configured.floecat.unity.max-pages(system property) – Maximum pages fetched by one Unity Catalog listing, default 10,000. Raise it for an unusually large catalog; exceeding it is reported as anINVALID_RESPONSErather than allowing an unbounded listing.floecat.unity.max-response-bytes(system property) – Cap on a single Unity Catalog response body, default 32 MiB. A larger body is refused rather than buffered, since a connector URI is tenant-supplied: on a success the call fails asINVALID_RESPONSE; on an error response the body is dropped for diagnostics while the HTTP status still decides the classification. Raise it for a catalog whose listing pages exceed the default.databricks.access-delegation=vended-credentials– Explicitly enables table-scoped temporary AWS credentials from Unity Catalog when no storage authority matches the table. The Databricks metastore must allow external access and the caller needsEXTERNAL USE SCHEMAon the parent schema. Azure, GCP, and R2 credential shapes are not yet consumed and fall back to a configured storage authority. Accepted values arevended-credentials,true,1,yes(enabled) andfalse,0,no,none(disabled), with underscores accepted in place of hyphens so the spelling DuckDB sends works here too; anything else is rejected at create/update time rather than read as "disabled". Honored only fordelta.source=unity(the default): ondelta.source=glueordelta.source=filesystemthe option is accepted and then ignored, since neither has a Unity credential endpoint to vend from. If the credentials response carries anaccess_pointARN, it is dropped and the rest of the tuple is used against the bucket named in the object URI, logging the table at INFO. Nothing here addresses an access point, and the grant behind one commonly permits bucket addressing anyway. Where it does not, reads fail at storage with a 403; that log line is what points at the cause. Recover by having the workspace vend bucket-scoped credentials for the external location, or by configuring a storage authority covering it -- vending is reached only when no authority matches.s3.region/aws.region– Region for the S3 client used to read Parquet files.stats.ndv.*– Sampling knobs identical to the Iceberg connector.- Authentication-specific options (
auth.scheme,auth.properties) –auth.scheme=oauth2works with resolved bearer-style credentials oroauth.mode=cli(to read the Databricks CLI cache). Secret-bearing auth values must be supplied viaAuthCredentials, not persisted inauth.properties. Service principal and WIF are expressed asAuthCredentialsand resolved upstream. Fordelta.source=glue, use resolved AWS credentials or non-secretauth.propertiesprofile settings and setauth.scheme=aws-sigv4ornone.
Auth credential types (--cred-type) are documented in the CLI reference listed below.
For delta.source=unity, the relevant types are bearer, client (SP), cli,
token-exchange (WIF), token-exchange-entra, and token-exchange-gcp. Entra/GCP exchanges only
work if the Databricks workspace is configured to trust those IdPs. Use the Databricks workspace
host for uri (for example https://dbc-<workspace-id>.cloud.databricks.com); for Databricks
Unity Catalog, token exchange endpoints typically use https://<workspace-host>/oidc/v1/token.
For delta.source=glue and delta.source=filesystem, this Databricks OIDC token endpoint pattern
does not apply. Shared outbound token endpoint validation behavior is documented in the operations
guide listed below.
For delta.source=glue, the relevant credential types are aws, cli (provider=aws),
aws-web-identity, and aws-assume-role.
Extensibility points:
- Implement new auth schemes by extending
AuthProviderand wiring them in the connector provider. - Plug in additional NDV providers if Delta tables store custom sketches.
- Extend
DeltaPlannerto emit additional metadata (for example z-order hints) when the upstream API exposes them.
Examples & Scenarios¶
- Connector Spec – A Unity Catalog connector might specify:
{
"display_name":"delta-unity",
"kind":"CK_DELTA",
"uri":"https://dbc-1234.cloud.databricks.com",
"properties":{
"delta.source":"unity",
"s3.region":"us-west-2",
"stats.ndv.enabled":"true"
},
"auth":{
"scheme":"oauth2",
"credentials":{"bearer":{"token":"<access-token>"}},
"properties":{}
}
}
- CLI examples
-
Service principal (SP) – For
delta.source=unity, useclientcredentials. Resolve via client credentials exchange (service layer), connector sees a bearer token. Token endpoint is the workspace OIDC URL:https://<workspace-host>/oidc/v1/token.connector create "Unity Delta SP" DELTA https://dbc-d382c535-b2a9.cloud.databricks.com \ "cusack.ext_tpcds" tpcds --dest-ns federated --source-table store_sales \ --auth-scheme oauth2 \ --cred-type client \ --cred endpoint=https://dbc-d382c535-b2a9.cloud.databricks.com/oidc/v1/token \ --cred client_id=3d9b2f0f-7f1a-4b6e-9f0a-2f1b6c9a1234 \ --cred client_secret=ddbsp-9f1c2a3b4c5d6e7f8a9b \ --auth scope=all-apis -
WIF (token exchange) – For
delta.source=unity, usetoken-exchange. Resolve via RFC 8693 exchange (service layer), connector sees a bearer token. Token endpoint is the workspace OIDC URL:https://<workspace-host>/oidc/v1/token.connector create "Unity Delta WIF" DELTA https://dbc-d382c535-b2a9.cloud.databricks.com \ "cusack.ext_tpcds" tpcds --dest-ns federated --source-table store_sales \ --auth-scheme oauth2 \ --cred-type token-exchange \ --cred endpoint=https://dbc-d382c535-b2a9.cloud.databricks.com/oidc/v1/token \ --cred client_id=3d9b2f0f-7f1a-4b6e-9f0a-2f1b6c9a1234 \ --cred client_secret=ddbsp-9f1c2a3b4c5d6e7f8a9b \ --cred subject_token_type=urn:ietf:params:oauth:token-type:jwt \ --cred requested_token_type=urn:ietf:params:oauth:token-type:access_token \ --cred scope="all-apis offline_access" -
CLI cache – Connector reads the Databricks CLI cache directly:
connector create "Unity Delta CLI" DELTA https://dbc-d382c535-b2a9.cloud.databricks.com \ "cusack.ext_tpcds" tpcds --dest-ns federated --source-table store_sales \ --auth-scheme oauth2 \ --cred-type cli \ --cred cache_path=~/.databricks/token-cache.json -
Bearer token (PAT) – Using the
connectorCLI with a resolved token or Databricks personal access token:
connector create "Unity Delta Token" DELTA https://dbc-d382c535-b2a9.cloud.databricks.com \
"cusack.ext_tpcds" tpcds --dest-ns federated --source-table store_sales \
--auth-scheme oauth2 --cred-type bearer --cred token=<access-token>
- Full reconciliation –
ReconcilerServiceenters full-rescan mode (fullRescan=true), so the connector lists every table in the namespace, creates missing namespaces in the destination catalog, updatesDestinationTargetpointers, and ingests snapshot stats for each table.
Compose credential-vending smoke¶
COMPOSE_SMOKE_MODES=localstack-remote make compose-smoke runs an OSS Unity Catalog 0.6.0 server
against LocalStack STS and a Delta fixture in floecat-delta-vended. The fixture bucket is
intentionally excluded from the storage-authority setup, so metadata capture and the leased query
scan can succeed only with the temporary AWS session credentials returned by Unity Catalog. The
smoke first validates the credential response shape without logging its secret fields, then imports
the table with databricks.access-delegation=vended-credentials and checks stats, indexes, and the
remote scan-file path.
OSS Unity Catalog exposes temporary table credentials under its 2.1 API, whereas Databricks uses
the 2.0 route consumed by HttpUnityCatalogClient. The compose-only TLS proxy translates that one
route; production code remains aligned with the Databricks API.