fix(stack-drizzle): emit unqualified eql_v3 domain so drizzle-kit DDL is valid (rc.2 M5)#688
Conversation
… is valid (rc.2 M5) A v3 encrypted column declared its SQL type as the schema-qualified domain (`public.eql_v3_text_search`). drizzle-kit wraps a customType's whole `dataType()` string in one pair of double quotes, so `generate`/`push` emitted the invalid identifier `"public.eql_v3_text_search"` — Postgres reads that as a single dotted type name and rejects it (`type "public.eql_v3_text_search" does not exist`). Reproduced against drizzle-kit 0.30.6: CREATE TABLE and ADD COLUMN both emit the broken quoted form; generated migrations needed hand repair. Fix: `makeEqlV3Column` now emits the BARE domain (`eql_v3_text_search`) via `stripDomainSchema`, which drizzle-kit renders as the valid `"eql_v3_text_search"` and which resolves through the search_path (the domains live in `public`). This mirrors the v2 `encryptedType` surface, which already emits its bare type for the same reason, and it matches what drizzle-kit introspection reads back on a `push` diff so the two sides stop disagreeing (the `"undefined"."public.eql_v3_*"` mode). Builder recovery is unchanged in identity: `getSqlType()` re-qualifies a bare name back to the canonical `public.eql_v3_*` before matching the recovery keys, and it still accepts an already-qualified name (test doubles / introspected snapshots), so operators and schema extraction see the same domain as before. - Regression guard: every V3_MATRIX domain's `getSQLType()` is asserted dot-free. - Bare-getSQLType recovery test added alongside the existing qualified one. - stash-drizzle skill updated to describe the unqualified generated type + the search-path requirement. Tests: stack-drizzle 370 unit green, test:types clean, build + biome clean. Changeset: stack-drizzle patch + stash patch (skill ships in the tarball). Claude-Session: https://claude.ai/code/session_01MxTTPaPP16m6br7Hoab94w
🦋 Changeset detectedLatest commit: 4956900 The changes in this PR will be included in the next version bump. This PR includes changesets to release 11 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (2)
🚧 Files skipped from review as they are similar to previous changes (2)
📝 WalkthroughWalkthroughEQL v3 Drizzle columns now emit unqualified domain names for generated DDL, while column recovery normalizes them to canonical ChangesEQL v3 DDL domain handling
Estimated code review effort: 2 (Simple) | ~10 minutes Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
freshtonic
left a comment
There was a problem hiding this comment.
Overview
Fixes invalid DDL from drizzle-kit generate/push for EQL v3 encrypted columns (rc.2 M5). A v3 column declared its SQL type as the schema-qualified domain public.eql_v3_text_search, but drizzle-kit wraps a customType's whole dataType() string in a single pair of double quotes — emitting "public.eql_v3_text_search", which Postgres reads as one dotted identifier and rejects. The fix emits the bare domain (eql_v3_text_search) via stripDomainSchema, which renders as valid "eql_v3_text_search" and resolves through search_path (the domains live in public). Builder recovery re-qualifies the bare name back to the canonical public.eql_v3_* identity, so operators and schema extraction are untouched.
Small, correct, well-reasoned. LGTM / approvable — details below; the only notes are nits.
Why this is right
- The root-cause diagnosis is accurate and the fix is minimal. drizzle-kit's quote-the-whole-
dataType()behaviour is real and is already documented on the v2 surface (packages/stack-drizzle/src/index.ts:15— "unconditionally wraps the dataType() return in double-quotes"). This brings v3 in line with that established v2 convention. - It aligns the column with the rest of the v3 surface, which already stripped the schema.
operators.ts:134already callsstripDomainSchema(eqlType)(public.eql_v3_text_search → eql_v3_text_search), andstack-supabase/src/verify.tscompares againststripDomainSchema(builder.getEqlType()). SodataType()was the odd one out emitting the qualified form; this closes that gap rather than inventing a new convention. - The recovery round-trip is sound.
getSqlType()now re-qualifies viaqualifyDomainin both branches (thegetSQLType()path and thedataType()/sqlNamefallback), and an already-qualified name (test double / introspected snapshot) passes through unchanged — sobuildersByDomain/EQL_V3_DOMAINSlookups on the canonicalpublic.eql_v3_*keys still match.stripDomainSchema(strip leadingpublic.) andqualifyDomain(prefixpublic.when bare) are exact inverses over the domain set. - Good regression guard. The new test asserts every
V3_MATRIXdomain'sgetSQLType()is dot-free and equals the bare slug — that's precisely the string drizzle-kit quote-wraps into the migration, so a future re-qualification can't silently reintroduce an un-runnable CREATE/ALTER. Bare-name recovery is covered alongside the existing qualified case. - Skill (
stash-drizzle) updated to describe the unqualified type and the search-path requirement;@cipherstash/stack-drizzlepatch +stashpatch changeset present (correct, since the skill ships in the tarball).
Notes — nits, non-blocking
-
Search-path coupling is now a documented requirement. The bare name only resolves if
publicis onsearch_path. That's the Postgres default and matches v2's behaviour, and the skill now spells it out — so this is acceptable, just worth being conscious that a user who dropspublicfromsearch_path(or installs theeql_v3_*domains in a non-publicschema) gets a "type does not exist" at migrate time.EQL_V3_DOMAIN_SCHEMA/stripDomainSchemaboth hardcodepublic, consistent with the installer, so no code change is warranted — noting only. -
stripDomainSchemastrips onlypublic.;qualifyDomainre-qualifies any dot-free name topublic.They're inverses because every v3 domain lives inpublic. If agetEqlType()ever returned a non-publicqualified name,stripDomainSchemawould pass it through and drizzle-kit would emit the same invalid dotted identifier this PR fixes. Can't happen today (registry ispublic-only), but a one-line assertion or comment tying the two helpers to that invariant would make the coupling explicit for a future reader.
Verdict
Correct, consistent with the existing v2 and v3-operator conventions, and well-guarded against regression. I'd approve this. Both notes are optional. Say the word and I'll flip this to a formal approval.
…ping schema `makeEqlV3Column` emits the bare domain name because drizzle-kit wraps a customType's whole `dataType()` return in one pair of quotes, so a qualified `public.eql_v3_text_search` would emit the invalid identifier `"public.eql_v3_text_search"`. Stripping the `public.` schema is only safe because every domain lives in `public` — `stripDomainSchema`/`qualifyDomain` are inverses over exactly that set. If a domain ever lived in another schema, `stripDomainSchema` would pass its qualified name through untouched and drizzle-kit would silently emit the invalid dotted identifier again. Assert the invariant at column construction so that day fails loudly with an actionable message instead of shipping a broken migration. Addresses the schema-qualification review notes on #688. Claude-Session: https://claude.ai/code/session_01MxTTPaPP16m6br7Hoab94w
|
Addressed both schema-qualification notes in 4956900. On the substance: I dug into whether we could honor "always fully qualify" at the What I did change is note 2 — the fragility you flagged. Also filed #693 for a gap this surfaced beyond the PR's scope: the CLI's |
What
drizzle-kit generate/pushemitted invalid DDL for EQL v3 encrypted columns. A v3 column declared its SQL type as the schema-qualified domainpublic.eql_v3_text_search, but drizzle-kit wraps acustomType's wholedataType()string in one pair of double quotes:Postgres reads
"public.eql_v3_text_search"as a single type name containing a dot and rejects it (type "public.eql_v3_text_search" does not exist), so every generated migration needed hand repair.Fix
makeEqlV3Columnnow emits the bare domain name (eql_v3_text_search) viastripDomainSchema. This:"eql_v3_text_search"in generated DDL, resolving through the search path (theeql_v3_*domains are created inpublic, always in-path);encryptedTypesurface, which already emits its bare type for exactly this reason (see the comment block inpackages/stack-drizzle/src/index.ts);pushdiff, so the code side and DB side stop disagreeing (the reported"undefined"."public.eql_v3_*"mode).Builder recovery keeps the canonical identity:
getSqlType()re-qualifies a bare name back topublic.eql_v3_*before matching the recovery keys, and still accepts an already-qualified name (test doubles / introspected snapshots) — so operators and schema extraction are unchanged.Reproduction
Confirmed against
drizzle-kit@0.30.6viadrizzle-kit/apigenerateMigration: the qualified name produced"public.eql_v3_text_search"on both CREATE TABLE and ADD COLUMN; the bare name produces the valid quoted form. (In 0.30.6 the ALTER path already emits an unquoted, valid name; the"undefined".double-break is an older-drizzle-kit ALTER artifact and shares this root cause.)Tests
V3_MATRIXdomain'sgetSQLType()asserted dot-free.getSQLTyperecovery test alongside the existing qualified one.getSQLType()to the bare form.test:typesclean, build + biome clean.Skill
skills/stash-drizzle/SKILL.mdupdated to describe the unqualified generated type and the search-path requirement (hence thestashpatch changeset — the skill ships in the tarball).Changeset
@cipherstash/stack-drizzlepatch +stashpatch.https://claude.ai/code/session_01MxTTPaPP16m6br7Hoab94w
Summary by CodeRabbit
generate/pushso v3 column domain types are emitted correctly.public.