Skip to main content
A Warehouse is a named, credentialed connection to the compute engine your transformations run on. Popsink relays SQL statements to it, and the queries run on your own compute: no Popsink worker is launched for a warehouse, and Popsink never reads or stores the data the queries touch. The SQL you run on a warehouse can read and write any table the warehouse can access, whether Popsink delivers it or not. Databricks is the first supported engine. A warehouse lives under Transform data → Warehouses in the data plane.

How it works

Prerequisites

Everything below is set up in Databricks, by a workspace admin, before you create the warehouse in Popsink.
1

A workspace with Unity Catalog

A Databricks workspace (AWS, Azure or GCP) attached to a Unity Catalog metastore. The tables your SQL reads and writes are addressed as catalog.schema.table.
2

A SQL warehouse

In SQL Warehouses, create a SQL warehouse or pick an existing one. Serverless is recommended: it starts in seconds and stops when idle, so a task scheduled every few minutes only bills while it runs.An all-purpose cluster cannot be used: Popsink only accepts a SQL warehouse’s HTTP path.
3

A service principal

In Settings → Identity and access → Service principals, add a service principal to the workspace. Use a service principal rather than a person’s account, so the connection keeps working when someone leaves.On the service principal’s Configurations tab, enable the Databricks SQL access entitlement: without it, the service principal cannot run statements on a SQL warehouse.
4

An access token for the service principal

Popsink authenticates with a personal access token belonging to the service principal.
  1. In Settings → Advanced → Personal Access Tokens, make sure tokens are enabled, and under Permission Settings give the service principal Can Use.
  2. Generate the token as the service principal, for example with the Databricks CLI authenticated with its OAuth secret:
Copy the token value: it is shown only once. Note its expiry date, and replace it in Popsink before it expires.
5

Permission to use the SQL warehouse

In SQL Warehouses → your warehouse → Permissions, give the service principal Can use.
6

Unity Catalog privileges

Grant the service principal what your SQL needs, and nothing more. Typically, read access on the tables you transform and write access on the schema you build into:
If you set a default catalog and schema on the warehouse in Popsink, the service principal also needs USE CATALOG and USE SCHEMA on them: the connection test reads both.
7

Network access

If the workspace restricts inbound traffic with IP access lists, add Popsink’s egress IP addresses for your region. On a self-hosted data plane, allow the addresses your cluster leaves from instead.
You now have everything Popsink asks for:
The connection test checks the warehouse, the catalog and the schema, but creates nothing. A missing SELECT, CREATE TABLE or MODIFY grant is only caught the first time a statement needs it.

Create a warehouse

1

Open the wizard

In Popsink, go to Transform data → Warehouses and click New Warehouse. Select Databricks as the Type, give it a Warehouse Name and pick the Owner domain.
2

Fill in the settings

Catalog and schema names accept letters, digits, underscores and hyphens only.
3

Test and save

Click Test credentials, then Continue once the test passes. If any setting changes after a green test, the test has to run again.

What the connection test does

Three read-only metadata calls, in the order a failure is easiest to explain: The test runs no SQL statement: a SELECT 1 would start a stopped warehouse, bill you for compute and take minutes to answer. You can re-run it at any time with Test connection from the warehouses list to refresh a warehouse’s status.

SQL examples

The examples run on a Databricks SQL warehouse, as a task or from the SQL editor. They use two tables in main.sales; replace the names with your own:
  • orders: one row per order (id, customer_id, status, amount, created_at);
  • orders_history: one row per change made to an order, with the order’s columns plus:
History tables delivered by Popsink have this shape, under other column names: op is __op, event_ts is __ts_ms (epoch milliseconds, so compare with unix_millis(...) or read it with timestamp_millis(...)), and seq is the Kafka position: __partition, __offset on a Unity Catalog target (table <T>_APPEND), __kafka_partition, __kafka_offset on an Iceberg target (table <T>).

Build a reporting table

The most common task: rebuild an aggregate on a schedule.

Full history of a row

Every change made to order 42, with the previous status beside the new one:

Table as it was at a point in time

The latest change of each order before a given instant. The WHERE runs before the window, which is what makes this a point-in-time read; the delete filter sits outside so a deleted row is not replaced by its previous version:

Build a merge table when creates arrive after updates

Rebuilding current state from a history table usually applies a simple rule: the latest change wins. That is right as long as changes arrive in the order they happened. Some sources, and some reloads, break that assumption: a create (c) or a snapshot read (r) lands in the history after updates for the same key, with a later timestamp. With “latest change wins”, that late create overwrites the updates. For example, orders_history holds three changes for order 42: The rule used below: for a given key, an update or a delete always outranks a create or a snapshot read. Among changes of the same rank, the latest wins (event_ts, then seq). The merge table keeps op and event_ts, which the incremental step needs.
1

Build the merge table from the full history

This statement is complete on its own: re-running it rebuilds the table from scratch. On a small history, scheduling it as a task is all you need.
2

Keep it current with an incremental MERGE

On a large history, apply only recent changes instead of rebuilding. The source elects one change per key with the same rule, over a look-back window:
What each case does:The MERGE is idempotent: re-applying the same window changes nothing, so overlapping runs are safe. Schedule it as a task, and size the look-back window to cover the task’s interval plus the latest a change can arrive. If your history table records when each row was written, filter the window on that column rather than on event_ts, so a change that arrives late is still picked up.
3

Check which keys the rule changed

The keys whose latest change is a create or a snapshot read that the rule ignored, beside the change that was kept:
With a composite primary key, list every key column in PARTITION BY and in the ON clause, e.g. PARTITION BY order_id, line_no and ON t.order_id = s.order_id AND t.line_no = s.line_no.
With this rule, a delete also outranks a later create. If your source re-uses keys (a row deleted, then re-inserted with the same key), the re-insert stays deleted. For such tables, use the “latest change wins” rule instead.