fix: prevent enum name truncation in schema-qualified type resolution (#521) - #522
Conversation
…#521) PostgreSQL's CASE expression type resolution could resolve to the `name` type (63-char limit) when branches mixed `name`-typed catalog columns with `text` expressions. This caused schema-qualified enum/composite type names to be silently truncated when the temporary schema prefix plus type name exceeded 63 characters, producing spurious ALTER TYPE statements in migration plans. Add explicit `::text` casts to bare `name`-typed columns inside CASE branches (typname, nspname, udt_name) across all 9 type-resolution queries to ensure the CASE resolves to `text` (unlimited length). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
| -- for non-array fixed-length types like name (typelem points to char). | ||
| -- Use format_type to preserve typmod for element types (e.g., varchar(128)[] for character varying(128)[]) | ||
| CASE | ||
| WHEN en.nspname = 'pg_catalog' THEN et.typname | ||
| WHEN en.nspname = 'pg_catalog' THEN et.typname::text | ||
| ELSE quote_ident(en.nspname) || '.' || quote_ident(et.typname) | ||
| END || COALESCE(substring(format_type(a.atttypid, a.atttypmod) FROM '\([^)]*\)'), '') || '[]' | ||
| WHEN dt.typtype = 'b' THEN | ||
| -- Non-array base types: qualify if not in pg_catalog or table's schema | ||
| -- Use format_type to preserve typmod for extension types (e.g., vector(384) for pgvector) | ||
| CASE | ||
| WHEN dn.nspname = 'pg_catalog' THEN c.udt_name | ||
| WHEN dn.nspname = 'pg_catalog' THEN c.udt_name::text | ||
| WHEN dn.nspname = c.table_schema THEN | ||
| dt.typname || COALESCE(substring(format_type(a.atttypid, a.atttypmod) FROM '\([^)]*\)'), '') | ||
| dt.typname::text || COALESCE(substring(format_type(a.atttypid, a.atttypmod) FROM '\([^)]*\)'), '') | ||
| ELSE | ||
| dn.nspname || '.' || dt.typname || COALESCE(substring(format_type(a.atttypid, a.atttypmod) FROM '\([^)]*\)'), '') | ||
| dn.nspname::text || '.' || dt.typname::text || COALESCE(substring(format_type(a.atttypid, a.atttypmod) FROM '\([^)]*\)'), '') |
There was a problem hiding this comment.
Generated output is not reproducible
The changed sqlc-generated constants omit comments that remain in queries.sql, so the next regeneration restores them and creates unrelated generated-file churn that obscures future semantic query changes.
Context Used: CLAUDE.md (source)
Knowledge Base Used: IR Model
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
Greptile SummaryThe PR prevents PostgreSQL catalog
Confidence Score: 4/5The PR appears safe to merge, with only non-blocking generated-file reproducibility cleanup recommended. The runtime SQL includes the intended text casts and the regression fixture checks the affected comparison path; the remaining issue is that two generated constants no longer exactly reflect reproducible sqlc output from the source file. Files Needing Attention: ir/queries/queries.sql.go Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart LR
Catalog[PostgreSQL catalog name values] --> Cast[Cast CASE branches to text]
Cast --> Resolve[Resolve qualified type name]
Resolve --> IR[Build normalized IR]
IR --> Diff[Compare current and desired schemas]
Diff --> Plan[Emit only the intended migration]
Reviews (1): Last reviewed commit: "fix: prevent enum name truncation in sch..." | Re-trigger Greptile |
There was a problem hiding this comment.
Pull request overview
Fixes a PostgreSQL name-type truncation bug in IR type resolution by ensuring CASE expressions return text (not name) when mixing catalog name columns (e.g., typname, nspname, udt_name) with text expressions. This prevents schema-qualified enum/composite names from being truncated when a long temporary schema prefix is involved, avoiding spurious type-change diffs during plan generation.
Changes:
- Add explicit
::textcasts insideCASEbranches across the type-resolution SQL queries to preventname-typedCASEresults (63-char truncation). - Regenerate/update the corresponding sqlc-generated Go query file to match the SQL changes.
- Add a regression diff fixture for issue #521 to ensure only the real schema change (ADD COLUMN) is planned.
Reviewed changes
Copilot reviewed 7 out of 8 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
ir/queries/queries.sql |
Adds ::text casts inside CASE branches so type-resolution expressions evaluate as text and don’t truncate. |
ir/queries/queries.sql.go |
Updates generated query strings to reflect the SQL ::text casting changes. |
testdata/diff/create_table/issue_521_enum_truncation/old.sql |
Regression fixture: baseline schema with enums whose schema-qualified names exceed 63 chars under temp-schema prefixing. |
testdata/diff/create_table/issue_521_enum_truncation/new.sql |
Adds the intended column change while keeping enum usage stable. |
testdata/diff/create_table/issue_521_enum_truncation/diff.sql |
Expected diff: only ADD COLUMN, no unintended ALTER TYPE/column type changes. |
testdata/diff/create_table/issue_521_enum_truncation/plan.sql |
Expected planned SQL output for the fixture. |
testdata/diff/create_table/issue_521_enum_truncation/plan.txt |
Expected human-readable plan output for the fixture. |
testdata/diff/create_table/issue_521_enum_truncation/plan.json |
Expected structured plan output for the fixture. |
Files not reviewed (1)
- ir/queries/queries.sql.go: Generated file
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Summary
pgschema_tmp_*, 37 chars) plus type name exceeded PostgreSQL'snametype limitname-typed catalog columns (typname,nspname,udt_name) withtextexpressions, causing PostgreSQL to resolve the CASE result type asname(63-char limit) — truncation happened inside the CASE evaluation, before any outer::textcast::textcasts to barename-typed columns inside CASE branches across all 9 type-resolution queries inqueries.sqland the generatedqueries.sql.goissue_521_enum_truncationwith enum names that exceed 63 chars when schema-qualifiedCloses #521
Test plan
testdata/diff/create_table/issue_521_enum_truncation/passes — only the expected ADD COLUMN change, no spurious ALTER TYPEcreate_table/integration tests passTestPlanAndApplysuite passes (594s)ir/,internal/)🤖 Generated with Claude Code