Skip to content

Commit 844e5c3

Browse files
committed
docs(stack): spike confirms information_schema reports domain_name for EQL v3 columns
udt_name is the base type (jsonb) for domain columns, so the v2 eql_v2_encrypted detection cannot be adapted by swapping the compared string. domain_name is unqualified; domain_schema is separate. Also records that the stock postgres-eql:17-2.3.1 image carries no public.* domains, so live introspection tests must apply the branch's install SQL.
1 parent 1a398c0 commit 844e5c3

1 file changed

Lines changed: 35 additions & 14 deletions

File tree

docs/superpowers/specs/2026-07-09-supabase-v3-introspection-design.md

Lines changed: 35 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -72,10 +72,31 @@ does not.
7272
### 1. Load the type
7373

7474
Every v3 domain is `CREATE DOMAIN public.<name> AS jsonb`. For a domain column,
75-
`information_schema.columns` populates `domain_schema` and `domain_name`; the
76-
`data_type` column reports the *base* type. This is why the v2 detection —
77-
`udt_name === 'eql_v2_encrypted'` in `packages/cli/src/commands/init/lib/introspect.ts:88`
78-
— does not carry over: `eql_v2_encrypted` is a composite type, not a domain.
75+
`information_schema.columns` populates `domain_schema` and `domain_name`; both
76+
`data_type` and `udt_name` report the *base* type.
77+
78+
Measured against Postgres 17 (spike, 2026-07-09):
79+
80+
| column | `data_type` | `udt_name` | `domain_schema` | `domain_name` |
81+
|---|---|---|---|---|
82+
| `id serial` | integer | int4 |||
83+
| `email spike.text_search` | jsonb | jsonb | spike | text_search |
84+
| `note text` | text | text |||
85+
| `meta jsonb` | jsonb | jsonb |||
86+
87+
Three consequences:
88+
89+
- **`domain_name` is unqualified**, with `domain_schema` returned separately.
90+
- **`udt_name` is `jsonb`**, so the v2 detection
91+
(`udt_name === 'eql_v2_encrypted'`,
92+
`packages/cli/src/commands/init/lib/introspect.ts:88`) cannot be adapted by
93+
swapping the compared string — it would compare against `jsonb` forever.
94+
`eql_v2_encrypted` has `typtype = 'c'` (composite), which is why it surfaces
95+
in `udt_name`; domains surface in `domain_name`.
96+
- **A plain `jsonb` column has `domain_name` NULL**, so encrypted and
97+
unencrypted jsonb columns are cleanly distinguishable. A domain carrying a
98+
`CHECK` constraint — as every EQL domain does — reports identically to one
99+
without.
79100

80101
```sql
81102
SELECT c.table_name, c.column_name, c.domain_name
@@ -87,13 +108,6 @@ WHERE c.table_schema = 'public'
87108
ORDER BY c.table_name, c.ordinal_position
88109
```
89110

90-
**Verify this first.** Postgres' exact reporting of `data_type` / `udt_name` /
91-
`domain_name` for a domain-over-jsonb column is asserted, not measured. The
92-
first implementation task is a live-Postgres test pinning the shape of these
93-
rows for a `public.text_search` column. If `domain_name` is not populated as
94-
expected, the query changes to `pg_catalog` (`pg_attribute``pg_type` where
95-
`typtype = 'd'`) and the rest of this design is unaffected.
96-
97111
Plaintext columns are retained, not filtered out. This buys three things:
98112

99113
- `from('unknown_table')` throws rather than silently passing through.
@@ -340,9 +354,16 @@ encryptedSupabaseV3(url, key, { schemas? })
340354
`columns.ts`. Rows → `EncryptedTable[]` grouping. Verification: match,
341355
wrong-domain, missing column, missing table. Declared-over-synthesized
342356
override, including that a declared `TextSearch` retains its tuner options.
343-
- **Live Postgres** — the shape of `information_schema.columns` for a domain
344-
column (the assumption flagged in §1). Introspection against a table with
345-
mixed encrypted and plaintext columns.
357+
- **Live Postgres** — introspection against a table with mixed encrypted and
358+
plaintext columns, asserting the domain→builder round-trip.
359+
360+
**Setup dependency.** The compose image (`postgres-eql:17-2.3.1`,
361+
`local/docker-compose.yml`) reports `eql_v3.version()` as `DEV` but contains
362+
**zero domains** in `public` — the concrete-domain surface is absent. The 47
363+
domains exist only after applying this branch's
364+
`packages/cli/src/sql/cipherstash-encrypt-v3.sql`. Live introspection tests
365+
must apply that SQL first, as the existing v3 pg tests do; they cannot run
366+
against the stock image.
346367
- **Wire encoding** — existing `supabase-v3-builder.test.ts` mock-client tests
347368
continue to pass unchanged, with the builder constructed from a synthesized
348369
table rather than a declared one.

0 commit comments

Comments
 (0)