Skip to main content

0003. How a catalog reaches Trino (the Trino binder)

  • Status: Accepted (owner decisions D3, D4)
  • Date: 2026-10-14
  • Issues: #9, #31, #37, #38, #39, #80

Context​

Trino loads catalogs through the baleia-trino-catalog-store plugin, which reads trino_catalog_registry in Baleia's Postgres. The plugin must be able to boot Trino without Baleia running, so the registry holds complete, usable configuration. Trino also has catalogs that Baleia did not create (from files, or created by an operator), and Baleia must not destroy them.

Decision​

The Trino binder is one implementation of bindings.Binder (0002).

  • Registry row = desired configuration with real values. Rows are protected by the dedicated baleia_trino database role. (D4, Phase A by default: secrets referenced as @baleia-secret[env:…] or @baleia-secret[file:…] are stored as references, never resolved into the database; catalog-origin secrets are stored resolved. Phase B only on owner request; see #31.)
  • DDL text carries placeholders only. CREATE CATALOG statements sent to Trino contain @baleia-secret[...] placeholders, never secret values; the plugin resolves them.
  • The plugin persists on CREATE CATALOG / DROP CATALOG and loads at boot. There is no polling.
  • catalog_version is SHA-256 over the resolved values, byte-compatible with Trino's FileCatalogStore.computeCatalogVersion. The algorithm is pinned by shared golden vectors (catalog_version_vectors.json) tested in both repositories (#39).
  • Provenance (D3, managed-only authority). A binding is managed (Baleia wrote it) or adopted (it exists in Trino, Baleia did not create it). Baleia creates, updates and drops only managed catalogs. adopted catalogs are reported and never mutated or dropped, including by reconciliation (#37).
  • State. Desired (registry/binding), effective (what the engine loaded) and observed (last check) are separate, and lifecycle procedures are idempotent (#38).
  • Changing the contract. A change to the registry schema, the version algorithm, reserved names or the placeholder syntax is released in the plugin first, then the pin in Baleia is bumped (#39).

Consequences​

  • Trino boots with its catalogs even when Baleia is down.
  • The registry is a credential store and must be treated as one (role separation, no logging).
  • Reconciliation can report drift without risking an operator's catalogs.

Current compliance​

Partially compliant.

  • Registry, placeholders and the version algorithm exist (db/migrations/00018_trino_catalog_store.sql, internal/trino/secret.go, internal/bindings/trino/version.go); the Trino binder (internal/bindings/trino, #80) is the only code that writes the registry and catalog DDL.
  • The shared fixtures (version vectors, reserved names, placeholder cases, the plugin's registry statements) live only in the plugin's contract/ and ship in each release; internal/contract checks Baleia against the release it pins (make contract-fetch, CI job Contract), including that every plugin statement prepares as baleia_trino against Baleia's schema and that the registry refuses reserved names (migration 00029). Changing the contract means a plugin release first, then bumping BALEIA_PLUGIN_VERSION (#39).
  • Provenance: bindings are managed, adopted or native (#13, #80, #38); the registry's updated_by = 'trino' is the plugin's confirmation, which Baleia never writes.
  • Desired, effective and observed state are separate (#38): catalog_bindings holds the desired state and a lifecycle Baleia owns (provisioning, active, updating, error, deprovisioning, disabled; guarded transitions in internal/bindings/lifecycle.go); the effective state is the binder's confirmation (Trino: the plugin rewrote the registry row with the desired version); the observed state is engine_catalog_observations. The API's sync_status is derived by binding_status_v; Baleia no longer writes sync_status/sync_error to the registry, which would overwrite the plugin's confirmation. Trino 483 has no in-place ALTER CATALOG besides SET AUTHORIZATION, so updates are DROP then CREATE and renames create then drop.
  • Golden vectors are copied into internal/bindings/trino/testdata/ rather than fetched from the plugin's contract fixtures (#39).
  • SecretPlaceholder emits only the catalog-origin form; env/file references are #31.

Alternatives considered​

  • Registry with placeholders only, secrets fetched from Baleia at load. Rejected: Trino could not boot without Baleia.
  • Plugin polls the registry. Rejected: DDL already notifies the plugin; polling adds load and a window of inconsistency.
  • Baleia owns every Trino catalog. Rejected: it would drop catalogs operators created outside Baleia.