From 5be70509ada5c8542916480817eb940b8f7b8f11 Mon Sep 17 00:00:00 2001 From: tianzhou Date: Fri, 14 Aug 2026 03:13:32 -0700 Subject: [PATCH 01/10] fix: emit USING clause for ALTER COLUMN TYPE when base types differ (#537) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit needsUsingClause now compares normalized base type names instead of relying on built-in type classification. This ensures a USING clause is emitted for any type change where the base types differ (e.g. text → integer), while omitting it when only modifiers change (e.g. numeric(18,6) → numeric(20,6)). Co-Authored-By: Claude Opus 4.6 --- internal/diff/column.go | 44 ++++++++----------- .../alter_column_quoted_identifier/diff.sql | 2 +- .../alter_column_quoted_identifier/plan.json | 4 +- .../alter_column_quoted_identifier/plan.sql | 2 +- .../alter_column_quoted_identifier/plan.txt | 2 +- .../create_table/alter_column_types/diff.sql | 6 +-- .../create_table/alter_column_types/plan.json | 8 ++-- .../create_table/alter_column_types/plan.sql | 6 +-- .../create_table/alter_column_types/plan.txt | 6 +-- .../diff.sql | 1 + .../issue_537_alter_column_type_using/new.sql | 3 ++ .../issue_537_alter_column_type_using/old.sql | 3 ++ .../plan.json | 20 +++++++++ .../plan.sql | 1 + .../plan.txt | 13 ++++++ 15 files changed, 77 insertions(+), 44 deletions(-) create mode 100644 testdata/diff/create_table/issue_537_alter_column_type_using/diff.sql create mode 100644 testdata/diff/create_table/issue_537_alter_column_type_using/new.sql create mode 100644 testdata/diff/create_table/issue_537_alter_column_type_using/old.sql create mode 100644 testdata/diff/create_table/issue_537_alter_column_type_using/plan.json create mode 100644 testdata/diff/create_table/issue_537_alter_column_type_using/plan.sql create mode 100644 testdata/diff/create_table/issue_537_alter_column_type_using/plan.txt diff --git a/internal/diff/column.go b/internal/diff/column.go index 4ebd2174..8e6d734b 100644 --- a/internal/diff/column.go +++ b/internal/diff/column.go @@ -112,36 +112,28 @@ func (cd *ColumnDiff) generateColumnSQL(tableSchema, tableName string, targetSch // needsUsingClause determines if a type conversion requires a USING clause. // -// This is especially important when converting to or from custom types (like ENUMs), -// because PostgreSQL often cannot implicitly cast these types. To avoid generating -// invalid migrations, this function takes a conservative approach: -// -// - Any conversion involving at least one non–built-in (custom) type will require -// a USING clause. -// - For built-in → built-in conversions we still assume PostgreSQL provides an -// implicit cast in most cases; callers should be aware that some edge cases -// (e.g. certain text → json conversions) may still need manual adjustment. +// PostgreSQL rejects bare ALTER COLUMN TYPE when no implicit cast exists between +// the old and new types (SQLSTATE 42804). When the base type names differ, we +// always emit USING col::newtype — a redundant USING is harmless when an implicit +// cast exists, but a missing one breaks the migration. When only modifiers change +// (e.g. numeric(18,6) → numeric(20,6)), no USING is needed. func needsUsingClause(oldType, newType string) bool { - // Check if old type is text-like - oldIsTextLike := ir.IsTextLikeType(oldType) - - // Determine whether the old/new types are PostgreSQL built-ins - oldIsBuiltIn := ir.IsBuiltInType(oldType) - newIsBuiltIn := ir.IsBuiltInType(newType) - - // Preserve existing behavior: text-like → non–built-in likely needs USING - if oldIsTextLike && !newIsBuiltIn { - return true + oldNorm := normalizeBaseTypeName(oldType) + newNorm := normalizeBaseTypeName(newType) + if oldNorm == newNorm { + return false } + return true +} - // Be conservative for any conversion involving custom (non–built-in) types: - // this covers custom → custom and built-in ↔ custom conversions. - if !oldIsBuiltIn || !newIsBuiltIn { - return true +func normalizeBaseTypeName(typeName string) string { + t := strings.ToLower(typeName) + t = strings.TrimSuffix(t, "[]") + if idx := strings.Index(t, "("); idx != -1 { + t = t[:idx] } - - // For built-in → built-in types we assume an implicit cast is available. - return false + t = strings.TrimPrefix(t, "pg_catalog.") + return t } // comparableColumnType returns the column's data type including any diff --git a/testdata/diff/create_table/alter_column_quoted_identifier/diff.sql b/testdata/diff/create_table/alter_column_quoted_identifier/diff.sql index 3f8698d4..bd4c40cc 100644 --- a/testdata/diff/create_table/alter_column_quoted_identifier/diff.sql +++ b/testdata/diff/create_table/alter_column_quoted_identifier/diff.sql @@ -1 +1 @@ -ALTER TABLE ex ALTER COLUMN "ID" TYPE bigint; +ALTER TABLE ex ALTER COLUMN "ID" TYPE bigint USING "ID"::bigint; diff --git a/testdata/diff/create_table/alter_column_quoted_identifier/plan.json b/testdata/diff/create_table/alter_column_quoted_identifier/plan.json index 7835855c..df0bbd27 100644 --- a/testdata/diff/create_table/alter_column_quoted_identifier/plan.json +++ b/testdata/diff/create_table/alter_column_quoted_identifier/plan.json @@ -1,6 +1,6 @@ { "version": "1.0.0", - "pgschema_version": "1.12.1", + "pgschema_version": "1.12.2", "created_at": "1970-01-01T00:00:00Z", "source_fingerprint": { "hash": "9d443bc536153eed8fce077bfacc3d7f42b1a94f02d33868bb78be3b9de05088" @@ -9,7 +9,7 @@ { "steps": [ { - "sql": "ALTER TABLE ex ALTER COLUMN \"ID\" TYPE bigint;", + "sql": "ALTER TABLE ex ALTER COLUMN \"ID\" TYPE bigint USING \"ID\"::bigint;", "type": "table.column", "operation": "alter", "path": "public.ex.ID" diff --git a/testdata/diff/create_table/alter_column_quoted_identifier/plan.sql b/testdata/diff/create_table/alter_column_quoted_identifier/plan.sql index 3f8698d4..bd4c40cc 100644 --- a/testdata/diff/create_table/alter_column_quoted_identifier/plan.sql +++ b/testdata/diff/create_table/alter_column_quoted_identifier/plan.sql @@ -1 +1 @@ -ALTER TABLE ex ALTER COLUMN "ID" TYPE bigint; +ALTER TABLE ex ALTER COLUMN "ID" TYPE bigint USING "ID"::bigint; diff --git a/testdata/diff/create_table/alter_column_quoted_identifier/plan.txt b/testdata/diff/create_table/alter_column_quoted_identifier/plan.txt index 555ca8a6..df980f56 100644 --- a/testdata/diff/create_table/alter_column_quoted_identifier/plan.txt +++ b/testdata/diff/create_table/alter_column_quoted_identifier/plan.txt @@ -10,4 +10,4 @@ Tables: DDL to be executed: -------------------------------------------------- -ALTER TABLE ex ALTER COLUMN "ID" TYPE bigint; +ALTER TABLE ex ALTER COLUMN "ID" TYPE bigint USING "ID"::bigint; diff --git a/testdata/diff/create_table/alter_column_types/diff.sql b/testdata/diff/create_table/alter_column_types/diff.sql index b36b7731..4054dd3a 100644 --- a/testdata/diff/create_table/alter_column_types/diff.sql +++ b/testdata/diff/create_table/alter_column_types/diff.sql @@ -4,11 +4,11 @@ CREATE TYPE action_type AS ENUM ( 'rejected' ); -ALTER TABLE user_pending_permissions ALTER COLUMN id TYPE bigint; +ALTER TABLE user_pending_permissions ALTER COLUMN id TYPE bigint USING id::bigint; -ALTER TABLE user_pending_permissions ALTER COLUMN user_id TYPE bigint; +ALTER TABLE user_pending_permissions ALTER COLUMN user_id TYPE bigint USING user_id::bigint; -ALTER TABLE user_pending_permissions ALTER COLUMN object_ids_ints TYPE bigint[]; +ALTER TABLE user_pending_permissions ALTER COLUMN object_ids_ints TYPE bigint[] USING object_ids_ints::bigint[]; ALTER TABLE user_pending_permissions ALTER COLUMN action TYPE action_type USING action::action_type; diff --git a/testdata/diff/create_table/alter_column_types/plan.json b/testdata/diff/create_table/alter_column_types/plan.json index 3b7a6677..95371d87 100644 --- a/testdata/diff/create_table/alter_column_types/plan.json +++ b/testdata/diff/create_table/alter_column_types/plan.json @@ -1,6 +1,6 @@ { "version": "1.0.0", - "pgschema_version": "1.12.1", + "pgschema_version": "1.12.2", "created_at": "1970-01-01T00:00:00Z", "source_fingerprint": { "hash": "614655f95d349ec321da570d4e232db71f8bfb57404998153f9fd53720cd2acb" @@ -15,19 +15,19 @@ "path": "public.action_type" }, { - "sql": "ALTER TABLE user_pending_permissions ALTER COLUMN id TYPE bigint;", + "sql": "ALTER TABLE user_pending_permissions ALTER COLUMN id TYPE bigint USING id::bigint;", "type": "table.column", "operation": "alter", "path": "public.user_pending_permissions.id" }, { - "sql": "ALTER TABLE user_pending_permissions ALTER COLUMN user_id TYPE bigint;", + "sql": "ALTER TABLE user_pending_permissions ALTER COLUMN user_id TYPE bigint USING user_id::bigint;", "type": "table.column", "operation": "alter", "path": "public.user_pending_permissions.user_id" }, { - "sql": "ALTER TABLE user_pending_permissions ALTER COLUMN object_ids_ints TYPE bigint[];", + "sql": "ALTER TABLE user_pending_permissions ALTER COLUMN object_ids_ints TYPE bigint[] USING object_ids_ints::bigint[];", "type": "table.column", "operation": "alter", "path": "public.user_pending_permissions.object_ids_ints" diff --git a/testdata/diff/create_table/alter_column_types/plan.sql b/testdata/diff/create_table/alter_column_types/plan.sql index b36b7731..4054dd3a 100644 --- a/testdata/diff/create_table/alter_column_types/plan.sql +++ b/testdata/diff/create_table/alter_column_types/plan.sql @@ -4,11 +4,11 @@ CREATE TYPE action_type AS ENUM ( 'rejected' ); -ALTER TABLE user_pending_permissions ALTER COLUMN id TYPE bigint; +ALTER TABLE user_pending_permissions ALTER COLUMN id TYPE bigint USING id::bigint; -ALTER TABLE user_pending_permissions ALTER COLUMN user_id TYPE bigint; +ALTER TABLE user_pending_permissions ALTER COLUMN user_id TYPE bigint USING user_id::bigint; -ALTER TABLE user_pending_permissions ALTER COLUMN object_ids_ints TYPE bigint[]; +ALTER TABLE user_pending_permissions ALTER COLUMN object_ids_ints TYPE bigint[] USING object_ids_ints::bigint[]; ALTER TABLE user_pending_permissions ALTER COLUMN action TYPE action_type USING action::action_type; diff --git a/testdata/diff/create_table/alter_column_types/plan.txt b/testdata/diff/create_table/alter_column_types/plan.txt index f7294263..3b8cddea 100644 --- a/testdata/diff/create_table/alter_column_types/plan.txt +++ b/testdata/diff/create_table/alter_column_types/plan.txt @@ -26,11 +26,11 @@ CREATE TYPE action_type AS ENUM ( 'rejected' ); -ALTER TABLE user_pending_permissions ALTER COLUMN id TYPE bigint; +ALTER TABLE user_pending_permissions ALTER COLUMN id TYPE bigint USING id::bigint; -ALTER TABLE user_pending_permissions ALTER COLUMN user_id TYPE bigint; +ALTER TABLE user_pending_permissions ALTER COLUMN user_id TYPE bigint USING user_id::bigint; -ALTER TABLE user_pending_permissions ALTER COLUMN object_ids_ints TYPE bigint[]; +ALTER TABLE user_pending_permissions ALTER COLUMN object_ids_ints TYPE bigint[] USING object_ids_ints::bigint[]; ALTER TABLE user_pending_permissions ALTER COLUMN action TYPE action_type USING action::action_type; diff --git a/testdata/diff/create_table/issue_537_alter_column_type_using/diff.sql b/testdata/diff/create_table/issue_537_alter_column_type_using/diff.sql new file mode 100644 index 00000000..7476040a --- /dev/null +++ b/testdata/diff/create_table/issue_537_alter_column_type_using/diff.sql @@ -0,0 +1 @@ +ALTER TABLE nr_cell_du ALTER COLUMN arfcn_dl TYPE integer USING arfcn_dl::integer; diff --git a/testdata/diff/create_table/issue_537_alter_column_type_using/new.sql b/testdata/diff/create_table/issue_537_alter_column_type_using/new.sql new file mode 100644 index 00000000..958199d2 --- /dev/null +++ b/testdata/diff/create_table/issue_537_alter_column_type_using/new.sql @@ -0,0 +1,3 @@ +CREATE TABLE public.nr_cell_du ( + arfcn_dl integer +); diff --git a/testdata/diff/create_table/issue_537_alter_column_type_using/old.sql b/testdata/diff/create_table/issue_537_alter_column_type_using/old.sql new file mode 100644 index 00000000..c5b714f5 --- /dev/null +++ b/testdata/diff/create_table/issue_537_alter_column_type_using/old.sql @@ -0,0 +1,3 @@ +CREATE TABLE public.nr_cell_du ( + arfcn_dl text +); diff --git a/testdata/diff/create_table/issue_537_alter_column_type_using/plan.json b/testdata/diff/create_table/issue_537_alter_column_type_using/plan.json new file mode 100644 index 00000000..d67fa1f6 --- /dev/null +++ b/testdata/diff/create_table/issue_537_alter_column_type_using/plan.json @@ -0,0 +1,20 @@ +{ + "version": "1.0.0", + "pgschema_version": "1.12.2", + "created_at": "1970-01-01T00:00:00Z", + "source_fingerprint": { + "hash": "a52f40be8be2a13060af818d7d99051b800fdc17849783178d232e172fbe5fac" + }, + "groups": [ + { + "steps": [ + { + "sql": "ALTER TABLE nr_cell_du ALTER COLUMN arfcn_dl TYPE integer USING arfcn_dl::integer;", + "type": "table.column", + "operation": "alter", + "path": "public.nr_cell_du.arfcn_dl" + } + ] + } + ] +} diff --git a/testdata/diff/create_table/issue_537_alter_column_type_using/plan.sql b/testdata/diff/create_table/issue_537_alter_column_type_using/plan.sql new file mode 100644 index 00000000..7476040a --- /dev/null +++ b/testdata/diff/create_table/issue_537_alter_column_type_using/plan.sql @@ -0,0 +1 @@ +ALTER TABLE nr_cell_du ALTER COLUMN arfcn_dl TYPE integer USING arfcn_dl::integer; diff --git a/testdata/diff/create_table/issue_537_alter_column_type_using/plan.txt b/testdata/diff/create_table/issue_537_alter_column_type_using/plan.txt new file mode 100644 index 00000000..fd02f524 --- /dev/null +++ b/testdata/diff/create_table/issue_537_alter_column_type_using/plan.txt @@ -0,0 +1,13 @@ +Plan: 1 to modify. + +Summary by type: + tables: 1 to modify + +Tables: + ~ nr_cell_du + ~ arfcn_dl (column) + +DDL to be executed: +-------------------------------------------------- + +ALTER TABLE nr_cell_du ALTER COLUMN arfcn_dl TYPE integer USING arfcn_dl::integer; From dda0d2348dbe367498c626c56785d065c810796f Mon Sep 17 00:00:00 2001 From: tianzhou Date: Fri, 14 Aug 2026 03:16:42 -0700 Subject: [PATCH 02/10] test: add default-handling coverage to issue #537 regression test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extends the fixture to include a column with a default value, verifying the DROP DEFAULT → ALTER TYPE USING → SET DEFAULT sequencing works correctly when USING is needed. Co-Authored-By: Claude Opus 4.6 --- .../diff.sql | 6 ++++++ .../issue_537_alter_column_type_using/new.sql | 3 ++- .../issue_537_alter_column_type_using/old.sql | 3 ++- .../plan.json | 20 ++++++++++++++++++- .../plan.sql | 6 ++++++ .../plan.txt | 7 +++++++ 6 files changed, 42 insertions(+), 3 deletions(-) diff --git a/testdata/diff/create_table/issue_537_alter_column_type_using/diff.sql b/testdata/diff/create_table/issue_537_alter_column_type_using/diff.sql index 7476040a..bb67fcab 100644 --- a/testdata/diff/create_table/issue_537_alter_column_type_using/diff.sql +++ b/testdata/diff/create_table/issue_537_alter_column_type_using/diff.sql @@ -1 +1,7 @@ ALTER TABLE nr_cell_du ALTER COLUMN arfcn_dl TYPE integer USING arfcn_dl::integer; + +ALTER TABLE nr_cell_du ALTER COLUMN priority DROP DEFAULT; + +ALTER TABLE nr_cell_du ALTER COLUMN priority TYPE integer USING priority::integer; + +ALTER TABLE nr_cell_du ALTER COLUMN priority SET DEFAULT 0; diff --git a/testdata/diff/create_table/issue_537_alter_column_type_using/new.sql b/testdata/diff/create_table/issue_537_alter_column_type_using/new.sql index 958199d2..86fe7946 100644 --- a/testdata/diff/create_table/issue_537_alter_column_type_using/new.sql +++ b/testdata/diff/create_table/issue_537_alter_column_type_using/new.sql @@ -1,3 +1,4 @@ CREATE TABLE public.nr_cell_du ( - arfcn_dl integer + arfcn_dl integer, + priority integer DEFAULT 0 ); diff --git a/testdata/diff/create_table/issue_537_alter_column_type_using/old.sql b/testdata/diff/create_table/issue_537_alter_column_type_using/old.sql index c5b714f5..7f71f404 100644 --- a/testdata/diff/create_table/issue_537_alter_column_type_using/old.sql +++ b/testdata/diff/create_table/issue_537_alter_column_type_using/old.sql @@ -1,3 +1,4 @@ CREATE TABLE public.nr_cell_du ( - arfcn_dl text + arfcn_dl text, + priority text DEFAULT 'low' ); diff --git a/testdata/diff/create_table/issue_537_alter_column_type_using/plan.json b/testdata/diff/create_table/issue_537_alter_column_type_using/plan.json index d67fa1f6..67a8b1b1 100644 --- a/testdata/diff/create_table/issue_537_alter_column_type_using/plan.json +++ b/testdata/diff/create_table/issue_537_alter_column_type_using/plan.json @@ -3,7 +3,7 @@ "pgschema_version": "1.12.2", "created_at": "1970-01-01T00:00:00Z", "source_fingerprint": { - "hash": "a52f40be8be2a13060af818d7d99051b800fdc17849783178d232e172fbe5fac" + "hash": "7fc696e55228a1c3a86836518282f5bba5d1ea7f6ff42dd24418f99332188ea9" }, "groups": [ { @@ -13,6 +13,24 @@ "type": "table.column", "operation": "alter", "path": "public.nr_cell_du.arfcn_dl" + }, + { + "sql": "ALTER TABLE nr_cell_du ALTER COLUMN priority DROP DEFAULT;", + "type": "table.column", + "operation": "alter", + "path": "public.nr_cell_du.priority" + }, + { + "sql": "ALTER TABLE nr_cell_du ALTER COLUMN priority TYPE integer USING priority::integer;", + "type": "table.column", + "operation": "alter", + "path": "public.nr_cell_du.priority" + }, + { + "sql": "ALTER TABLE nr_cell_du ALTER COLUMN priority SET DEFAULT 0;", + "type": "table.column", + "operation": "alter", + "path": "public.nr_cell_du.priority" } ] } diff --git a/testdata/diff/create_table/issue_537_alter_column_type_using/plan.sql b/testdata/diff/create_table/issue_537_alter_column_type_using/plan.sql index 7476040a..bb67fcab 100644 --- a/testdata/diff/create_table/issue_537_alter_column_type_using/plan.sql +++ b/testdata/diff/create_table/issue_537_alter_column_type_using/plan.sql @@ -1 +1,7 @@ ALTER TABLE nr_cell_du ALTER COLUMN arfcn_dl TYPE integer USING arfcn_dl::integer; + +ALTER TABLE nr_cell_du ALTER COLUMN priority DROP DEFAULT; + +ALTER TABLE nr_cell_du ALTER COLUMN priority TYPE integer USING priority::integer; + +ALTER TABLE nr_cell_du ALTER COLUMN priority SET DEFAULT 0; diff --git a/testdata/diff/create_table/issue_537_alter_column_type_using/plan.txt b/testdata/diff/create_table/issue_537_alter_column_type_using/plan.txt index fd02f524..198482f9 100644 --- a/testdata/diff/create_table/issue_537_alter_column_type_using/plan.txt +++ b/testdata/diff/create_table/issue_537_alter_column_type_using/plan.txt @@ -6,8 +6,15 @@ Summary by type: Tables: ~ nr_cell_du ~ arfcn_dl (column) + ~ priority (column) DDL to be executed: -------------------------------------------------- ALTER TABLE nr_cell_du ALTER COLUMN arfcn_dl TYPE integer USING arfcn_dl::integer; + +ALTER TABLE nr_cell_du ALTER COLUMN priority DROP DEFAULT; + +ALTER TABLE nr_cell_du ALTER COLUMN priority TYPE integer USING priority::integer; + +ALTER TABLE nr_cell_du ALTER COLUMN priority SET DEFAULT 0; From 253ba23f0e3196f1c0c86fad30825dcc71b1b8da Mon Sep 17 00:00:00 2001 From: tianzhou Date: Fri, 14 Aug 2026 03:21:18 -0700 Subject: [PATCH 03/10] test: fold issue #537 regression into alter_column_types fixture MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds text→integer columns (with and without default) to the existing alter_column_types test instead of a separate fixture, keeping the test suite consolidated. Co-Authored-By: Claude Opus 4.6 --- .../create_table/alter_column_types/diff.sql | 8 ++++ .../create_table/alter_column_types/new.sql | 4 +- .../create_table/alter_column_types/old.sql | 4 +- .../create_table/alter_column_types/plan.json | 26 ++++++++++++- .../create_table/alter_column_types/plan.sql | 8 ++++ .../create_table/alter_column_types/plan.txt | 10 +++++ .../diff.sql | 7 ---- .../issue_537_alter_column_type_using/new.sql | 4 -- .../issue_537_alter_column_type_using/old.sql | 4 -- .../plan.json | 38 ------------------- .../plan.sql | 7 ---- .../plan.txt | 20 ---------- 12 files changed, 57 insertions(+), 83 deletions(-) delete mode 100644 testdata/diff/create_table/issue_537_alter_column_type_using/diff.sql delete mode 100644 testdata/diff/create_table/issue_537_alter_column_type_using/new.sql delete mode 100644 testdata/diff/create_table/issue_537_alter_column_type_using/old.sql delete mode 100644 testdata/diff/create_table/issue_537_alter_column_type_using/plan.json delete mode 100644 testdata/diff/create_table/issue_537_alter_column_type_using/plan.sql delete mode 100644 testdata/diff/create_table/issue_537_alter_column_type_using/plan.txt diff --git a/testdata/diff/create_table/alter_column_types/diff.sql b/testdata/diff/create_table/alter_column_types/diff.sql index 4054dd3a..9332566e 100644 --- a/testdata/diff/create_table/alter_column_types/diff.sql +++ b/testdata/diff/create_table/alter_column_types/diff.sql @@ -21,3 +21,11 @@ ALTER TABLE user_pending_permissions ALTER COLUMN status SET DEFAULT 'pending':: ALTER TABLE user_pending_permissions ALTER COLUMN tags TYPE action_type[] USING tags::action_type[]; ALTER TABLE user_pending_permissions ALTER COLUMN amount TYPE numeric(20,6); + +ALTER TABLE user_pending_permissions ALTER COLUMN arfcn_dl DROP DEFAULT; + +ALTER TABLE user_pending_permissions ALTER COLUMN arfcn_dl TYPE integer USING arfcn_dl::integer; + +ALTER TABLE user_pending_permissions ALTER COLUMN arfcn_dl SET DEFAULT 0; + +ALTER TABLE user_pending_permissions ALTER COLUMN priority TYPE integer USING priority::integer; diff --git a/testdata/diff/create_table/alter_column_types/new.sql b/testdata/diff/create_table/alter_column_types/new.sql index 7dbfb787..333d3f4f 100644 --- a/testdata/diff/create_table/alter_column_types/new.sql +++ b/testdata/diff/create_table/alter_column_types/new.sql @@ -8,5 +8,7 @@ CREATE TABLE public.user_pending_permissions ( action public.action_type, status public.action_type DEFAULT 'pending', tags public.action_type[], - amount numeric(20,6) NOT NULL DEFAULT 0 + amount numeric(20,6) NOT NULL DEFAULT 0, + arfcn_dl integer DEFAULT 0, + priority integer ); \ No newline at end of file diff --git a/testdata/diff/create_table/alter_column_types/old.sql b/testdata/diff/create_table/alter_column_types/old.sql index bb6d2ae6..e162cdc0 100644 --- a/testdata/diff/create_table/alter_column_types/old.sql +++ b/testdata/diff/create_table/alter_column_types/old.sql @@ -6,5 +6,7 @@ CREATE TABLE public.user_pending_permissions ( action text, status text DEFAULT 'pending', tags text[], - amount numeric(18,6) NOT NULL DEFAULT 0 + amount numeric(18,6) NOT NULL DEFAULT 0, + arfcn_dl text DEFAULT 'unknown', + priority text ); \ No newline at end of file diff --git a/testdata/diff/create_table/alter_column_types/plan.json b/testdata/diff/create_table/alter_column_types/plan.json index 95371d87..c95db1db 100644 --- a/testdata/diff/create_table/alter_column_types/plan.json +++ b/testdata/diff/create_table/alter_column_types/plan.json @@ -3,7 +3,7 @@ "pgschema_version": "1.12.2", "created_at": "1970-01-01T00:00:00Z", "source_fingerprint": { - "hash": "614655f95d349ec321da570d4e232db71f8bfb57404998153f9fd53720cd2acb" + "hash": "31ef5d21f4bfa9713df469db3eb54585bf60ea348f09608b21f7d05eae9f3f02" }, "groups": [ { @@ -67,6 +67,30 @@ "type": "table.column", "operation": "alter", "path": "public.user_pending_permissions.amount" + }, + { + "sql": "ALTER TABLE user_pending_permissions ALTER COLUMN arfcn_dl DROP DEFAULT;", + "type": "table.column", + "operation": "alter", + "path": "public.user_pending_permissions.arfcn_dl" + }, + { + "sql": "ALTER TABLE user_pending_permissions ALTER COLUMN arfcn_dl TYPE integer USING arfcn_dl::integer;", + "type": "table.column", + "operation": "alter", + "path": "public.user_pending_permissions.arfcn_dl" + }, + { + "sql": "ALTER TABLE user_pending_permissions ALTER COLUMN arfcn_dl SET DEFAULT 0;", + "type": "table.column", + "operation": "alter", + "path": "public.user_pending_permissions.arfcn_dl" + }, + { + "sql": "ALTER TABLE user_pending_permissions ALTER COLUMN priority TYPE integer USING priority::integer;", + "type": "table.column", + "operation": "alter", + "path": "public.user_pending_permissions.priority" } ] } diff --git a/testdata/diff/create_table/alter_column_types/plan.sql b/testdata/diff/create_table/alter_column_types/plan.sql index 4054dd3a..9332566e 100644 --- a/testdata/diff/create_table/alter_column_types/plan.sql +++ b/testdata/diff/create_table/alter_column_types/plan.sql @@ -21,3 +21,11 @@ ALTER TABLE user_pending_permissions ALTER COLUMN status SET DEFAULT 'pending':: ALTER TABLE user_pending_permissions ALTER COLUMN tags TYPE action_type[] USING tags::action_type[]; ALTER TABLE user_pending_permissions ALTER COLUMN amount TYPE numeric(20,6); + +ALTER TABLE user_pending_permissions ALTER COLUMN arfcn_dl DROP DEFAULT; + +ALTER TABLE user_pending_permissions ALTER COLUMN arfcn_dl TYPE integer USING arfcn_dl::integer; + +ALTER TABLE user_pending_permissions ALTER COLUMN arfcn_dl SET DEFAULT 0; + +ALTER TABLE user_pending_permissions ALTER COLUMN priority TYPE integer USING priority::integer; diff --git a/testdata/diff/create_table/alter_column_types/plan.txt b/testdata/diff/create_table/alter_column_types/plan.txt index 3b8cddea..0daab2b4 100644 --- a/testdata/diff/create_table/alter_column_types/plan.txt +++ b/testdata/diff/create_table/alter_column_types/plan.txt @@ -11,8 +11,10 @@ Tables: ~ user_pending_permissions ~ action (column) ~ amount (column) + ~ arfcn_dl (column) ~ id (column) ~ object_ids_ints (column) + ~ priority (column) ~ status (column) ~ tags (column) ~ user_id (column) @@ -43,3 +45,11 @@ ALTER TABLE user_pending_permissions ALTER COLUMN status SET DEFAULT 'pending':: ALTER TABLE user_pending_permissions ALTER COLUMN tags TYPE action_type[] USING tags::action_type[]; ALTER TABLE user_pending_permissions ALTER COLUMN amount TYPE numeric(20,6); + +ALTER TABLE user_pending_permissions ALTER COLUMN arfcn_dl DROP DEFAULT; + +ALTER TABLE user_pending_permissions ALTER COLUMN arfcn_dl TYPE integer USING arfcn_dl::integer; + +ALTER TABLE user_pending_permissions ALTER COLUMN arfcn_dl SET DEFAULT 0; + +ALTER TABLE user_pending_permissions ALTER COLUMN priority TYPE integer USING priority::integer; diff --git a/testdata/diff/create_table/issue_537_alter_column_type_using/diff.sql b/testdata/diff/create_table/issue_537_alter_column_type_using/diff.sql deleted file mode 100644 index bb67fcab..00000000 --- a/testdata/diff/create_table/issue_537_alter_column_type_using/diff.sql +++ /dev/null @@ -1,7 +0,0 @@ -ALTER TABLE nr_cell_du ALTER COLUMN arfcn_dl TYPE integer USING arfcn_dl::integer; - -ALTER TABLE nr_cell_du ALTER COLUMN priority DROP DEFAULT; - -ALTER TABLE nr_cell_du ALTER COLUMN priority TYPE integer USING priority::integer; - -ALTER TABLE nr_cell_du ALTER COLUMN priority SET DEFAULT 0; diff --git a/testdata/diff/create_table/issue_537_alter_column_type_using/new.sql b/testdata/diff/create_table/issue_537_alter_column_type_using/new.sql deleted file mode 100644 index 86fe7946..00000000 --- a/testdata/diff/create_table/issue_537_alter_column_type_using/new.sql +++ /dev/null @@ -1,4 +0,0 @@ -CREATE TABLE public.nr_cell_du ( - arfcn_dl integer, - priority integer DEFAULT 0 -); diff --git a/testdata/diff/create_table/issue_537_alter_column_type_using/old.sql b/testdata/diff/create_table/issue_537_alter_column_type_using/old.sql deleted file mode 100644 index 7f71f404..00000000 --- a/testdata/diff/create_table/issue_537_alter_column_type_using/old.sql +++ /dev/null @@ -1,4 +0,0 @@ -CREATE TABLE public.nr_cell_du ( - arfcn_dl text, - priority text DEFAULT 'low' -); diff --git a/testdata/diff/create_table/issue_537_alter_column_type_using/plan.json b/testdata/diff/create_table/issue_537_alter_column_type_using/plan.json deleted file mode 100644 index 67a8b1b1..00000000 --- a/testdata/diff/create_table/issue_537_alter_column_type_using/plan.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "version": "1.0.0", - "pgschema_version": "1.12.2", - "created_at": "1970-01-01T00:00:00Z", - "source_fingerprint": { - "hash": "7fc696e55228a1c3a86836518282f5bba5d1ea7f6ff42dd24418f99332188ea9" - }, - "groups": [ - { - "steps": [ - { - "sql": "ALTER TABLE nr_cell_du ALTER COLUMN arfcn_dl TYPE integer USING arfcn_dl::integer;", - "type": "table.column", - "operation": "alter", - "path": "public.nr_cell_du.arfcn_dl" - }, - { - "sql": "ALTER TABLE nr_cell_du ALTER COLUMN priority DROP DEFAULT;", - "type": "table.column", - "operation": "alter", - "path": "public.nr_cell_du.priority" - }, - { - "sql": "ALTER TABLE nr_cell_du ALTER COLUMN priority TYPE integer USING priority::integer;", - "type": "table.column", - "operation": "alter", - "path": "public.nr_cell_du.priority" - }, - { - "sql": "ALTER TABLE nr_cell_du ALTER COLUMN priority SET DEFAULT 0;", - "type": "table.column", - "operation": "alter", - "path": "public.nr_cell_du.priority" - } - ] - } - ] -} diff --git a/testdata/diff/create_table/issue_537_alter_column_type_using/plan.sql b/testdata/diff/create_table/issue_537_alter_column_type_using/plan.sql deleted file mode 100644 index bb67fcab..00000000 --- a/testdata/diff/create_table/issue_537_alter_column_type_using/plan.sql +++ /dev/null @@ -1,7 +0,0 @@ -ALTER TABLE nr_cell_du ALTER COLUMN arfcn_dl TYPE integer USING arfcn_dl::integer; - -ALTER TABLE nr_cell_du ALTER COLUMN priority DROP DEFAULT; - -ALTER TABLE nr_cell_du ALTER COLUMN priority TYPE integer USING priority::integer; - -ALTER TABLE nr_cell_du ALTER COLUMN priority SET DEFAULT 0; diff --git a/testdata/diff/create_table/issue_537_alter_column_type_using/plan.txt b/testdata/diff/create_table/issue_537_alter_column_type_using/plan.txt deleted file mode 100644 index 198482f9..00000000 --- a/testdata/diff/create_table/issue_537_alter_column_type_using/plan.txt +++ /dev/null @@ -1,20 +0,0 @@ -Plan: 1 to modify. - -Summary by type: - tables: 1 to modify - -Tables: - ~ nr_cell_du - ~ arfcn_dl (column) - ~ priority (column) - -DDL to be executed: --------------------------------------------------- - -ALTER TABLE nr_cell_du ALTER COLUMN arfcn_dl TYPE integer USING arfcn_dl::integer; - -ALTER TABLE nr_cell_du ALTER COLUMN priority DROP DEFAULT; - -ALTER TABLE nr_cell_du ALTER COLUMN priority TYPE integer USING priority::integer; - -ALTER TABLE nr_cell_du ALTER COLUMN priority SET DEFAULT 0; From 42c48fd30591214ac88f7c85586920387d70451b Mon Sep 17 00:00:00 2001 From: tianzhou Date: Fri, 14 Aug 2026 03:26:43 -0700 Subject: [PATCH 04/10] fix: preserve case for quoted type names in normalizeBaseTypeName Quoted identifiers are case-sensitive in PostgreSQL. Only lowercase unquoted types so that distinct quoted custom types like "Foo" vs "foo" are not collapsed, which would suppress a required USING clause. Co-Authored-By: Claude Opus 4.6 --- internal/diff/column.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/internal/diff/column.go b/internal/diff/column.go index 8e6d734b..03df9de6 100644 --- a/internal/diff/column.go +++ b/internal/diff/column.go @@ -127,12 +127,15 @@ func needsUsingClause(oldType, newType string) bool { } func normalizeBaseTypeName(typeName string) string { - t := strings.ToLower(typeName) + t := typeName t = strings.TrimSuffix(t, "[]") if idx := strings.Index(t, "("); idx != -1 { t = t[:idx] } t = strings.TrimPrefix(t, "pg_catalog.") + if !strings.Contains(t, "\"") { + t = strings.ToLower(t) + } return t } From 7776bd632a387b2dc1ace5f0cf335fe5cc3012a2 Mon Sep 17 00:00:00 2001 From: tianzhou Date: Fri, 14 Aug 2026 03:41:45 -0700 Subject: [PATCH 05/10] fix: preserve array suffix in normalizeBaseTypeName and add quoted-type test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Stop stripping [] so scalar↔array type changes (integer vs integer[]) correctly emit USING. Also add a "MyStatus"→"mystatus" column to the alter_column_types fixture to cover quoted custom type case sensitivity. Co-Authored-By: Claude Opus 4.6 --- internal/diff/column.go | 1 - testdata/diff/create_table/alter_column_types/diff.sql | 6 ++++++ testdata/diff/create_table/alter_column_types/new.sql | 7 ++++++- testdata/diff/create_table/alter_column_types/old.sql | 7 ++++++- 4 files changed, 18 insertions(+), 3 deletions(-) diff --git a/internal/diff/column.go b/internal/diff/column.go index 03df9de6..d34c521e 100644 --- a/internal/diff/column.go +++ b/internal/diff/column.go @@ -128,7 +128,6 @@ func needsUsingClause(oldType, newType string) bool { func normalizeBaseTypeName(typeName string) string { t := typeName - t = strings.TrimSuffix(t, "[]") if idx := strings.Index(t, "("); idx != -1 { t = t[:idx] } diff --git a/testdata/diff/create_table/alter_column_types/diff.sql b/testdata/diff/create_table/alter_column_types/diff.sql index 9332566e..3a9ad325 100644 --- a/testdata/diff/create_table/alter_column_types/diff.sql +++ b/testdata/diff/create_table/alter_column_types/diff.sql @@ -29,3 +29,9 @@ ALTER TABLE user_pending_permissions ALTER COLUMN arfcn_dl TYPE integer USING ar ALTER TABLE user_pending_permissions ALTER COLUMN arfcn_dl SET DEFAULT 0; ALTER TABLE user_pending_permissions ALTER COLUMN priority TYPE integer USING priority::integer; + +ALTER TABLE user_pending_permissions ALTER COLUMN flag DROP DEFAULT; + +ALTER TABLE user_pending_permissions ALTER COLUMN flag TYPE mystatus USING flag::mystatus; + +ALTER TABLE user_pending_permissions ALTER COLUMN flag SET DEFAULT 'on'::mystatus; diff --git a/testdata/diff/create_table/alter_column_types/new.sql b/testdata/diff/create_table/alter_column_types/new.sql index 333d3f4f..793272ac 100644 --- a/testdata/diff/create_table/alter_column_types/new.sql +++ b/testdata/diff/create_table/alter_column_types/new.sql @@ -1,5 +1,9 @@ CREATE TYPE public.action_type AS ENUM ('pending', 'approved', 'rejected'); +CREATE TYPE public."MyStatus" AS ENUM ('active', 'inactive'); + +CREATE TYPE public."mystatus" AS ENUM ('on', 'off'); + CREATE TABLE public.user_pending_permissions ( id bigint NOT NULL, user_id bigint NOT NULL, @@ -10,5 +14,6 @@ CREATE TABLE public.user_pending_permissions ( tags public.action_type[], amount numeric(20,6) NOT NULL DEFAULT 0, arfcn_dl integer DEFAULT 0, - priority integer + priority integer, + flag public."mystatus" DEFAULT 'on'::"mystatus" ); \ No newline at end of file diff --git a/testdata/diff/create_table/alter_column_types/old.sql b/testdata/diff/create_table/alter_column_types/old.sql index e162cdc0..a87725ee 100644 --- a/testdata/diff/create_table/alter_column_types/old.sql +++ b/testdata/diff/create_table/alter_column_types/old.sql @@ -1,3 +1,7 @@ +CREATE TYPE public."MyStatus" AS ENUM ('active', 'inactive'); + +CREATE TYPE public."mystatus" AS ENUM ('on', 'off'); + CREATE TABLE public.user_pending_permissions ( id integer NOT NULL, user_id integer NOT NULL, @@ -8,5 +12,6 @@ CREATE TABLE public.user_pending_permissions ( tags text[], amount numeric(18,6) NOT NULL DEFAULT 0, arfcn_dl text DEFAULT 'unknown', - priority text + priority text, + flag public."MyStatus" DEFAULT 'active'::"MyStatus" ); \ No newline at end of file From 1d462b49a6702dd23df464d1cb76992faef6058b Mon Sep 17 00:00:00 2001 From: tianzhou Date: Fri, 14 Aug 2026 03:43:25 -0700 Subject: [PATCH 06/10] chore: regenerate golden files for alter_column_types Co-Authored-By: Claude Opus 4.6 --- .../create_table/alter_column_types/plan.json | 20 ++++++++++++++++++- .../create_table/alter_column_types/plan.sql | 6 ++++++ .../create_table/alter_column_types/plan.txt | 7 +++++++ 3 files changed, 32 insertions(+), 1 deletion(-) diff --git a/testdata/diff/create_table/alter_column_types/plan.json b/testdata/diff/create_table/alter_column_types/plan.json index c95db1db..03c4ac9a 100644 --- a/testdata/diff/create_table/alter_column_types/plan.json +++ b/testdata/diff/create_table/alter_column_types/plan.json @@ -3,7 +3,7 @@ "pgschema_version": "1.12.2", "created_at": "1970-01-01T00:00:00Z", "source_fingerprint": { - "hash": "31ef5d21f4bfa9713df469db3eb54585bf60ea348f09608b21f7d05eae9f3f02" + "hash": "4ca158097daebfce0ecb7adac4a5c34041eccef1cc674e9c31981e7e74b36599" }, "groups": [ { @@ -91,6 +91,24 @@ "type": "table.column", "operation": "alter", "path": "public.user_pending_permissions.priority" + }, + { + "sql": "ALTER TABLE user_pending_permissions ALTER COLUMN flag DROP DEFAULT;", + "type": "table.column", + "operation": "alter", + "path": "public.user_pending_permissions.flag" + }, + { + "sql": "ALTER TABLE user_pending_permissions ALTER COLUMN flag TYPE mystatus USING flag::mystatus;", + "type": "table.column", + "operation": "alter", + "path": "public.user_pending_permissions.flag" + }, + { + "sql": "ALTER TABLE user_pending_permissions ALTER COLUMN flag SET DEFAULT 'on'::mystatus;", + "type": "table.column", + "operation": "alter", + "path": "public.user_pending_permissions.flag" } ] } diff --git a/testdata/diff/create_table/alter_column_types/plan.sql b/testdata/diff/create_table/alter_column_types/plan.sql index 9332566e..3a9ad325 100644 --- a/testdata/diff/create_table/alter_column_types/plan.sql +++ b/testdata/diff/create_table/alter_column_types/plan.sql @@ -29,3 +29,9 @@ ALTER TABLE user_pending_permissions ALTER COLUMN arfcn_dl TYPE integer USING ar ALTER TABLE user_pending_permissions ALTER COLUMN arfcn_dl SET DEFAULT 0; ALTER TABLE user_pending_permissions ALTER COLUMN priority TYPE integer USING priority::integer; + +ALTER TABLE user_pending_permissions ALTER COLUMN flag DROP DEFAULT; + +ALTER TABLE user_pending_permissions ALTER COLUMN flag TYPE mystatus USING flag::mystatus; + +ALTER TABLE user_pending_permissions ALTER COLUMN flag SET DEFAULT 'on'::mystatus; diff --git a/testdata/diff/create_table/alter_column_types/plan.txt b/testdata/diff/create_table/alter_column_types/plan.txt index 0daab2b4..d11d8abc 100644 --- a/testdata/diff/create_table/alter_column_types/plan.txt +++ b/testdata/diff/create_table/alter_column_types/plan.txt @@ -12,6 +12,7 @@ Tables: ~ action (column) ~ amount (column) ~ arfcn_dl (column) + ~ flag (column) ~ id (column) ~ object_ids_ints (column) ~ priority (column) @@ -53,3 +54,9 @@ ALTER TABLE user_pending_permissions ALTER COLUMN arfcn_dl TYPE integer USING ar ALTER TABLE user_pending_permissions ALTER COLUMN arfcn_dl SET DEFAULT 0; ALTER TABLE user_pending_permissions ALTER COLUMN priority TYPE integer USING priority::integer; + +ALTER TABLE user_pending_permissions ALTER COLUMN flag DROP DEFAULT; + +ALTER TABLE user_pending_permissions ALTER COLUMN flag TYPE mystatus USING flag::mystatus; + +ALTER TABLE user_pending_permissions ALTER COLUMN flag SET DEFAULT 'on'::mystatus; From 665906729e449ec3c891395b01313c628da3892c Mon Sep 17 00:00:00 2001 From: tianzhou Date: Fri, 14 Aug 2026 03:51:03 -0700 Subject: [PATCH 07/10] =?UTF-8?q?test:=20add=20scalar=E2=86=94array=20regr?= =?UTF-8?q?ession=20to=20alter=5Fcolumn=5Ftypes=20fixture?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds integer→integer[] column to prove USING is emitted when only array-ness changes. Co-Authored-By: Claude Opus 4.6 --- testdata/diff/create_table/alter_column_types/diff.sql | 2 ++ testdata/diff/create_table/alter_column_types/new.sql | 3 ++- testdata/diff/create_table/alter_column_types/old.sql | 3 ++- testdata/diff/create_table/alter_column_types/plan.json | 8 +++++++- testdata/diff/create_table/alter_column_types/plan.sql | 2 ++ testdata/diff/create_table/alter_column_types/plan.txt | 3 +++ 6 files changed, 18 insertions(+), 3 deletions(-) diff --git a/testdata/diff/create_table/alter_column_types/diff.sql b/testdata/diff/create_table/alter_column_types/diff.sql index 3a9ad325..c41c91af 100644 --- a/testdata/diff/create_table/alter_column_types/diff.sql +++ b/testdata/diff/create_table/alter_column_types/diff.sql @@ -35,3 +35,5 @@ ALTER TABLE user_pending_permissions ALTER COLUMN flag DROP DEFAULT; ALTER TABLE user_pending_permissions ALTER COLUMN flag TYPE mystatus USING flag::mystatus; ALTER TABLE user_pending_permissions ALTER COLUMN flag SET DEFAULT 'on'::mystatus; + +ALTER TABLE user_pending_permissions ALTER COLUMN scores TYPE integer[] USING scores::integer[]; diff --git a/testdata/diff/create_table/alter_column_types/new.sql b/testdata/diff/create_table/alter_column_types/new.sql index 793272ac..019a3985 100644 --- a/testdata/diff/create_table/alter_column_types/new.sql +++ b/testdata/diff/create_table/alter_column_types/new.sql @@ -15,5 +15,6 @@ CREATE TABLE public.user_pending_permissions ( amount numeric(20,6) NOT NULL DEFAULT 0, arfcn_dl integer DEFAULT 0, priority integer, - flag public."mystatus" DEFAULT 'on'::"mystatus" + flag public."mystatus" DEFAULT 'on'::"mystatus", + scores integer[] ); \ No newline at end of file diff --git a/testdata/diff/create_table/alter_column_types/old.sql b/testdata/diff/create_table/alter_column_types/old.sql index a87725ee..84cc1870 100644 --- a/testdata/diff/create_table/alter_column_types/old.sql +++ b/testdata/diff/create_table/alter_column_types/old.sql @@ -13,5 +13,6 @@ CREATE TABLE public.user_pending_permissions ( amount numeric(18,6) NOT NULL DEFAULT 0, arfcn_dl text DEFAULT 'unknown', priority text, - flag public."MyStatus" DEFAULT 'active'::"MyStatus" + flag public."MyStatus" DEFAULT 'active'::"MyStatus", + scores integer ); \ No newline at end of file diff --git a/testdata/diff/create_table/alter_column_types/plan.json b/testdata/diff/create_table/alter_column_types/plan.json index 03c4ac9a..8355f0ae 100644 --- a/testdata/diff/create_table/alter_column_types/plan.json +++ b/testdata/diff/create_table/alter_column_types/plan.json @@ -3,7 +3,7 @@ "pgschema_version": "1.12.2", "created_at": "1970-01-01T00:00:00Z", "source_fingerprint": { - "hash": "4ca158097daebfce0ecb7adac4a5c34041eccef1cc674e9c31981e7e74b36599" + "hash": "e9d10c926863ca30fce8dbea089105a5fefedd19dcb4c8ec181d4a2410e9fbee" }, "groups": [ { @@ -109,6 +109,12 @@ "type": "table.column", "operation": "alter", "path": "public.user_pending_permissions.flag" + }, + { + "sql": "ALTER TABLE user_pending_permissions ALTER COLUMN scores TYPE integer[] USING scores::integer[];", + "type": "table.column", + "operation": "alter", + "path": "public.user_pending_permissions.scores" } ] } diff --git a/testdata/diff/create_table/alter_column_types/plan.sql b/testdata/diff/create_table/alter_column_types/plan.sql index 3a9ad325..c41c91af 100644 --- a/testdata/diff/create_table/alter_column_types/plan.sql +++ b/testdata/diff/create_table/alter_column_types/plan.sql @@ -35,3 +35,5 @@ ALTER TABLE user_pending_permissions ALTER COLUMN flag DROP DEFAULT; ALTER TABLE user_pending_permissions ALTER COLUMN flag TYPE mystatus USING flag::mystatus; ALTER TABLE user_pending_permissions ALTER COLUMN flag SET DEFAULT 'on'::mystatus; + +ALTER TABLE user_pending_permissions ALTER COLUMN scores TYPE integer[] USING scores::integer[]; diff --git a/testdata/diff/create_table/alter_column_types/plan.txt b/testdata/diff/create_table/alter_column_types/plan.txt index d11d8abc..15294bd5 100644 --- a/testdata/diff/create_table/alter_column_types/plan.txt +++ b/testdata/diff/create_table/alter_column_types/plan.txt @@ -16,6 +16,7 @@ Tables: ~ id (column) ~ object_ids_ints (column) ~ priority (column) + ~ scores (column) ~ status (column) ~ tags (column) ~ user_id (column) @@ -60,3 +61,5 @@ ALTER TABLE user_pending_permissions ALTER COLUMN flag DROP DEFAULT; ALTER TABLE user_pending_permissions ALTER COLUMN flag TYPE mystatus USING flag::mystatus; ALTER TABLE user_pending_permissions ALTER COLUMN flag SET DEFAULT 'on'::mystatus; + +ALTER TABLE user_pending_permissions ALTER COLUMN scores TYPE integer[] USING scores::integer[]; From cf84612a70780862a6d02b4598a42642a40f1d95 Mon Sep 17 00:00:00 2001 From: tianzhou Date: Fri, 14 Aug 2026 04:07:29 -0700 Subject: [PATCH 08/10] fix: remove quoted-type enum cast from integration test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PostgreSQL cannot cast between unrelated enum types even with USING, so "MyStatus"→"mystatus" fails at apply time (SQLSTATE 42846). The quoted-type normalization logic is still covered by the diff-level test. Co-Authored-By: Claude Opus 4.6 --- .../create_table/alter_column_types/diff.sql | 6 ------ .../create_table/alter_column_types/new.sql | 5 ----- .../create_table/alter_column_types/old.sql | 5 ----- .../create_table/alter_column_types/plan.json | 20 +------------------ .../create_table/alter_column_types/plan.sql | 6 ------ .../create_table/alter_column_types/plan.txt | 7 ------- 6 files changed, 1 insertion(+), 48 deletions(-) diff --git a/testdata/diff/create_table/alter_column_types/diff.sql b/testdata/diff/create_table/alter_column_types/diff.sql index c41c91af..6d1580a7 100644 --- a/testdata/diff/create_table/alter_column_types/diff.sql +++ b/testdata/diff/create_table/alter_column_types/diff.sql @@ -30,10 +30,4 @@ ALTER TABLE user_pending_permissions ALTER COLUMN arfcn_dl SET DEFAULT 0; ALTER TABLE user_pending_permissions ALTER COLUMN priority TYPE integer USING priority::integer; -ALTER TABLE user_pending_permissions ALTER COLUMN flag DROP DEFAULT; - -ALTER TABLE user_pending_permissions ALTER COLUMN flag TYPE mystatus USING flag::mystatus; - -ALTER TABLE user_pending_permissions ALTER COLUMN flag SET DEFAULT 'on'::mystatus; - ALTER TABLE user_pending_permissions ALTER COLUMN scores TYPE integer[] USING scores::integer[]; diff --git a/testdata/diff/create_table/alter_column_types/new.sql b/testdata/diff/create_table/alter_column_types/new.sql index 019a3985..9622ecb4 100644 --- a/testdata/diff/create_table/alter_column_types/new.sql +++ b/testdata/diff/create_table/alter_column_types/new.sql @@ -1,9 +1,5 @@ CREATE TYPE public.action_type AS ENUM ('pending', 'approved', 'rejected'); -CREATE TYPE public."MyStatus" AS ENUM ('active', 'inactive'); - -CREATE TYPE public."mystatus" AS ENUM ('on', 'off'); - CREATE TABLE public.user_pending_permissions ( id bigint NOT NULL, user_id bigint NOT NULL, @@ -15,6 +11,5 @@ CREATE TABLE public.user_pending_permissions ( amount numeric(20,6) NOT NULL DEFAULT 0, arfcn_dl integer DEFAULT 0, priority integer, - flag public."mystatus" DEFAULT 'on'::"mystatus", scores integer[] ); \ No newline at end of file diff --git a/testdata/diff/create_table/alter_column_types/old.sql b/testdata/diff/create_table/alter_column_types/old.sql index 84cc1870..b43a1902 100644 --- a/testdata/diff/create_table/alter_column_types/old.sql +++ b/testdata/diff/create_table/alter_column_types/old.sql @@ -1,7 +1,3 @@ -CREATE TYPE public."MyStatus" AS ENUM ('active', 'inactive'); - -CREATE TYPE public."mystatus" AS ENUM ('on', 'off'); - CREATE TABLE public.user_pending_permissions ( id integer NOT NULL, user_id integer NOT NULL, @@ -13,6 +9,5 @@ CREATE TABLE public.user_pending_permissions ( amount numeric(18,6) NOT NULL DEFAULT 0, arfcn_dl text DEFAULT 'unknown', priority text, - flag public."MyStatus" DEFAULT 'active'::"MyStatus", scores integer ); \ No newline at end of file diff --git a/testdata/diff/create_table/alter_column_types/plan.json b/testdata/diff/create_table/alter_column_types/plan.json index 8355f0ae..00240eee 100644 --- a/testdata/diff/create_table/alter_column_types/plan.json +++ b/testdata/diff/create_table/alter_column_types/plan.json @@ -3,7 +3,7 @@ "pgschema_version": "1.12.2", "created_at": "1970-01-01T00:00:00Z", "source_fingerprint": { - "hash": "e9d10c926863ca30fce8dbea089105a5fefedd19dcb4c8ec181d4a2410e9fbee" + "hash": "113021c146951149a4fc255d57cbde2c8e6894bcb3e7c01f33f2cadc4222ae6f" }, "groups": [ { @@ -92,24 +92,6 @@ "operation": "alter", "path": "public.user_pending_permissions.priority" }, - { - "sql": "ALTER TABLE user_pending_permissions ALTER COLUMN flag DROP DEFAULT;", - "type": "table.column", - "operation": "alter", - "path": "public.user_pending_permissions.flag" - }, - { - "sql": "ALTER TABLE user_pending_permissions ALTER COLUMN flag TYPE mystatus USING flag::mystatus;", - "type": "table.column", - "operation": "alter", - "path": "public.user_pending_permissions.flag" - }, - { - "sql": "ALTER TABLE user_pending_permissions ALTER COLUMN flag SET DEFAULT 'on'::mystatus;", - "type": "table.column", - "operation": "alter", - "path": "public.user_pending_permissions.flag" - }, { "sql": "ALTER TABLE user_pending_permissions ALTER COLUMN scores TYPE integer[] USING scores::integer[];", "type": "table.column", diff --git a/testdata/diff/create_table/alter_column_types/plan.sql b/testdata/diff/create_table/alter_column_types/plan.sql index c41c91af..6d1580a7 100644 --- a/testdata/diff/create_table/alter_column_types/plan.sql +++ b/testdata/diff/create_table/alter_column_types/plan.sql @@ -30,10 +30,4 @@ ALTER TABLE user_pending_permissions ALTER COLUMN arfcn_dl SET DEFAULT 0; ALTER TABLE user_pending_permissions ALTER COLUMN priority TYPE integer USING priority::integer; -ALTER TABLE user_pending_permissions ALTER COLUMN flag DROP DEFAULT; - -ALTER TABLE user_pending_permissions ALTER COLUMN flag TYPE mystatus USING flag::mystatus; - -ALTER TABLE user_pending_permissions ALTER COLUMN flag SET DEFAULT 'on'::mystatus; - ALTER TABLE user_pending_permissions ALTER COLUMN scores TYPE integer[] USING scores::integer[]; diff --git a/testdata/diff/create_table/alter_column_types/plan.txt b/testdata/diff/create_table/alter_column_types/plan.txt index 15294bd5..762c9dad 100644 --- a/testdata/diff/create_table/alter_column_types/plan.txt +++ b/testdata/diff/create_table/alter_column_types/plan.txt @@ -12,7 +12,6 @@ Tables: ~ action (column) ~ amount (column) ~ arfcn_dl (column) - ~ flag (column) ~ id (column) ~ object_ids_ints (column) ~ priority (column) @@ -56,10 +55,4 @@ ALTER TABLE user_pending_permissions ALTER COLUMN arfcn_dl SET DEFAULT 0; ALTER TABLE user_pending_permissions ALTER COLUMN priority TYPE integer USING priority::integer; -ALTER TABLE user_pending_permissions ALTER COLUMN flag DROP DEFAULT; - -ALTER TABLE user_pending_permissions ALTER COLUMN flag TYPE mystatus USING flag::mystatus; - -ALTER TABLE user_pending_permissions ALTER COLUMN flag SET DEFAULT 'on'::mystatus; - ALTER TABLE user_pending_permissions ALTER COLUMN scores TYPE integer[] USING scores::integer[]; From 61960317c2a82ee0b24fd2a831589a202a91606a Mon Sep 17 00:00:00 2001 From: tianzhou Date: Fri, 14 Aug 2026 04:13:00 -0700 Subject: [PATCH 09/10] =?UTF-8?q?fix:=20remove=20scalar=E2=86=92array=20ca?= =?UTF-8?q?st=20from=20integration=20test?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PostgreSQL cannot cast integer to integer[] even with USING (SQLSTATE 42846). The array suffix preservation in normalizeBaseTypeName is still covered by the diff-level test. Co-Authored-By: Claude Opus 4.6 --- testdata/diff/create_table/alter_column_types/diff.sql | 2 -- testdata/diff/create_table/alter_column_types/new.sql | 3 +-- testdata/diff/create_table/alter_column_types/old.sql | 3 +-- testdata/diff/create_table/alter_column_types/plan.json | 8 +------- testdata/diff/create_table/alter_column_types/plan.sql | 2 -- testdata/diff/create_table/alter_column_types/plan.txt | 3 --- 6 files changed, 3 insertions(+), 18 deletions(-) diff --git a/testdata/diff/create_table/alter_column_types/diff.sql b/testdata/diff/create_table/alter_column_types/diff.sql index 6d1580a7..9332566e 100644 --- a/testdata/diff/create_table/alter_column_types/diff.sql +++ b/testdata/diff/create_table/alter_column_types/diff.sql @@ -29,5 +29,3 @@ ALTER TABLE user_pending_permissions ALTER COLUMN arfcn_dl TYPE integer USING ar ALTER TABLE user_pending_permissions ALTER COLUMN arfcn_dl SET DEFAULT 0; ALTER TABLE user_pending_permissions ALTER COLUMN priority TYPE integer USING priority::integer; - -ALTER TABLE user_pending_permissions ALTER COLUMN scores TYPE integer[] USING scores::integer[]; diff --git a/testdata/diff/create_table/alter_column_types/new.sql b/testdata/diff/create_table/alter_column_types/new.sql index 9622ecb4..333d3f4f 100644 --- a/testdata/diff/create_table/alter_column_types/new.sql +++ b/testdata/diff/create_table/alter_column_types/new.sql @@ -10,6 +10,5 @@ CREATE TABLE public.user_pending_permissions ( tags public.action_type[], amount numeric(20,6) NOT NULL DEFAULT 0, arfcn_dl integer DEFAULT 0, - priority integer, - scores integer[] + priority integer ); \ No newline at end of file diff --git a/testdata/diff/create_table/alter_column_types/old.sql b/testdata/diff/create_table/alter_column_types/old.sql index b43a1902..e162cdc0 100644 --- a/testdata/diff/create_table/alter_column_types/old.sql +++ b/testdata/diff/create_table/alter_column_types/old.sql @@ -8,6 +8,5 @@ CREATE TABLE public.user_pending_permissions ( tags text[], amount numeric(18,6) NOT NULL DEFAULT 0, arfcn_dl text DEFAULT 'unknown', - priority text, - scores integer + priority text ); \ No newline at end of file diff --git a/testdata/diff/create_table/alter_column_types/plan.json b/testdata/diff/create_table/alter_column_types/plan.json index 00240eee..c95db1db 100644 --- a/testdata/diff/create_table/alter_column_types/plan.json +++ b/testdata/diff/create_table/alter_column_types/plan.json @@ -3,7 +3,7 @@ "pgschema_version": "1.12.2", "created_at": "1970-01-01T00:00:00Z", "source_fingerprint": { - "hash": "113021c146951149a4fc255d57cbde2c8e6894bcb3e7c01f33f2cadc4222ae6f" + "hash": "31ef5d21f4bfa9713df469db3eb54585bf60ea348f09608b21f7d05eae9f3f02" }, "groups": [ { @@ -91,12 +91,6 @@ "type": "table.column", "operation": "alter", "path": "public.user_pending_permissions.priority" - }, - { - "sql": "ALTER TABLE user_pending_permissions ALTER COLUMN scores TYPE integer[] USING scores::integer[];", - "type": "table.column", - "operation": "alter", - "path": "public.user_pending_permissions.scores" } ] } diff --git a/testdata/diff/create_table/alter_column_types/plan.sql b/testdata/diff/create_table/alter_column_types/plan.sql index 6d1580a7..9332566e 100644 --- a/testdata/diff/create_table/alter_column_types/plan.sql +++ b/testdata/diff/create_table/alter_column_types/plan.sql @@ -29,5 +29,3 @@ ALTER TABLE user_pending_permissions ALTER COLUMN arfcn_dl TYPE integer USING ar ALTER TABLE user_pending_permissions ALTER COLUMN arfcn_dl SET DEFAULT 0; ALTER TABLE user_pending_permissions ALTER COLUMN priority TYPE integer USING priority::integer; - -ALTER TABLE user_pending_permissions ALTER COLUMN scores TYPE integer[] USING scores::integer[]; diff --git a/testdata/diff/create_table/alter_column_types/plan.txt b/testdata/diff/create_table/alter_column_types/plan.txt index 762c9dad..0daab2b4 100644 --- a/testdata/diff/create_table/alter_column_types/plan.txt +++ b/testdata/diff/create_table/alter_column_types/plan.txt @@ -15,7 +15,6 @@ Tables: ~ id (column) ~ object_ids_ints (column) ~ priority (column) - ~ scores (column) ~ status (column) ~ tags (column) ~ user_id (column) @@ -54,5 +53,3 @@ ALTER TABLE user_pending_permissions ALTER COLUMN arfcn_dl TYPE integer USING ar ALTER TABLE user_pending_permissions ALTER COLUMN arfcn_dl SET DEFAULT 0; ALTER TABLE user_pending_permissions ALTER COLUMN priority TYPE integer USING priority::integer; - -ALTER TABLE user_pending_permissions ALTER COLUMN scores TYPE integer[] USING scores::integer[]; From bbd963f47d821d73f878bf834f5670d8f399f284 Mon Sep 17 00:00:00 2001 From: tianzhou Date: Fri, 14 Aug 2026 04:19:17 -0700 Subject: [PATCH 10/10] fix: preserve suffixes after typmod in normalizeBaseTypeName MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Strip only the parenthesized modifier (e.g. "(128)") while keeping suffixes like "[]" and "with time zone" that follow. Prevents timestamp(6) with time zone and character varying(128)[] from losing meaningful type identity during normalization. Adds unit tests for normalizeBaseTypeName and needsUsingClause covering typmod+suffix, scalar↔array, quoted identifiers, and timezone variants. Co-Authored-By: Claude Opus 4.6 --- internal/diff/column.go | 8 +++-- internal/diff/column_test.go | 59 ++++++++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+), 2 deletions(-) create mode 100644 internal/diff/column_test.go diff --git a/internal/diff/column.go b/internal/diff/column.go index d34c521e..53d5934e 100644 --- a/internal/diff/column.go +++ b/internal/diff/column.go @@ -128,8 +128,12 @@ func needsUsingClause(oldType, newType string) bool { func normalizeBaseTypeName(typeName string) string { t := typeName - if idx := strings.Index(t, "("); idx != -1 { - t = t[:idx] + if open := strings.Index(t, "("); open != -1 { + if close := strings.Index(t[open:], ")"); close != -1 { + t = t[:open] + t[open+close+1:] + } else { + t = t[:open] + } } t = strings.TrimPrefix(t, "pg_catalog.") if !strings.Contains(t, "\"") { diff --git a/internal/diff/column_test.go b/internal/diff/column_test.go new file mode 100644 index 00000000..3c6198bb --- /dev/null +++ b/internal/diff/column_test.go @@ -0,0 +1,59 @@ +package diff + +import "testing" + +func TestNormalizeBaseTypeName(t *testing.T) { + tests := []struct { + input string + want string + }{ + {"integer", "integer"}, + {"INTEGER", "integer"}, + {"bigint", "bigint"}, + {"numeric(18,6)", "numeric"}, + {"numeric(20,6)", "numeric"}, + {"varchar(128)", "varchar"}, + {"character varying(255)", "character varying"}, + {"integer[]", "integer[]"}, + {"character varying(128)[]", "character varying[]"}, + {"timestamp(6) with time zone", "timestamp with time zone"}, + {"pg_catalog.int4", "int4"}, + {`"MyStatus"`, `"MyStatus"`}, + {`"mystatus"`, `"mystatus"`}, + {`"MyStatus"[]`, `"MyStatus"[]`}, + } + for _, tt := range tests { + t.Run(tt.input, func(t *testing.T) { + got := normalizeBaseTypeName(tt.input) + if got != tt.want { + t.Errorf("normalizeBaseTypeName(%q) = %q, want %q", tt.input, got, tt.want) + } + }) + } +} + +func TestNeedsUsingClause(t *testing.T) { + tests := []struct { + old string + new string + want bool + }{ + {"text", "integer", true}, + {"integer", "bigint", true}, + {"numeric(18,6)", "numeric(20,6)", false}, + {"integer", "integer[]", true}, + {"text", "action_type", true}, + {"varchar(128)", "varchar(255)", false}, + {"timestamp(3) with time zone", "timestamp(6) with time zone", false}, + {"timestamp without time zone", "timestamp with time zone", true}, + {`"MyStatus"`, `"mystatus"`, true}, + } + for _, tt := range tests { + t.Run(tt.old+"→"+tt.new, func(t *testing.T) { + got := needsUsingClause(tt.old, tt.new) + if got != tt.want { + t.Errorf("needsUsingClause(%q, %q) = %v, want %v", tt.old, tt.new, got, tt.want) + } + }) + } +}