@@ -72,10 +72,31 @@ does not.
7272### 1. Load the type
7373
7474Every 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
81102SELECT c .table_name , c .column_name , c .domain_name
@@ -87,13 +108,6 @@ WHERE c.table_schema = 'public'
87108ORDER 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-
97111Plaintext 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