@@ -44,59 +44,9 @@ interface UpgradeResult {
4444 currentVersion ?: number ;
4545}
4646
47- /**
48- * Detects the current version of the migrations table schema and upgrades it if needed.
49- *
50- * Version 0: Original schema (id, hash, created_at)
51- * Version 1: Extended schema (id, hash, created_at, name, applied_at, version)
52- */
53- async function upgradeIfNeeded (
54- migrationsSchema : string ,
55- migrationsTable : string ,
56- session : CockroachSession ,
57- localMigrations : MigrationMeta [ ] ,
58- ) : Promise < UpgradeResult > {
59- // Check if the table exists at all
60- const tableExists = await session . all < { exists : boolean } > (
61- sql `SELECT EXISTS (
62- SELECT FROM information_schema.tables
63- WHERE table_schema = ${ migrationsSchema }
64- AND table_name = ${ migrationsTable }
65- )` ,
66- ) ;
67-
68- if ( ! tableExists [ 0 ] ?. exists ) {
69- return { newDb : true } ;
70- }
71-
72- // Table exists, check if there are any rows
73- const rows = await session . all < { id : number ; hash : string ; created_at : string ; version : number | undefined } > (
74- sql `SELECT * FROM ${ sql . identifier ( migrationsSchema ) } .${ sql . identifier ( migrationsTable ) } ORDER BY id ASC LIMIT 1` ,
75- ) ;
76-
77- let prevVersion ;
78-
79- if ( rows . length === 0 ) {
80- // Empty table - check if it has a version column
81- const hasVersionColumn = await session . all < { exists : boolean } > (
82- sql `SELECT EXISTS (
83- SELECT FROM information_schema.columns
84- WHERE table_schema = ${ migrationsSchema }
85- AND table_name = ${ migrationsTable }
86- AND column_name = 'version'
87- )` ,
88- ) ;
89-
90- prevVersion = hasVersionColumn [ 0 ] ?. exists ? 1 : 0 ;
91- } else {
92- prevVersion = rows [ 0 ] ?. version ?? 0 ;
93- }
94-
95- if ( prevVersion < CURRENT_MIGRATION_TABLE_VERSION ) {
96- await runUpgrades ( migrationsSchema , migrationsTable , session , prevVersion , localMigrations ) ;
97- }
98-
99- return { prevVersion, currentVersion : CURRENT_MIGRATION_TABLE_VERSION } ;
47+ function getVersion ( columns : string [ ] ) {
48+ if ( columns . includes ( 'name' ) ) return 1 ;
49+ return 0 ;
10050}
10151
10252/**
@@ -116,11 +66,9 @@ const upgradeFunctions: Record<
11666 * Upgrade from version 0 to version 1:
11767 * 1. Add `name` column (text)
11868 * 2. Add `applied_at` column (timestamp with time zone, defaults to now())
119- * 3. Add `version` column (integer)
120- * 4. Backfill `name` for existing rows by matching `created_at` (millis) to local migration folder timestamps
121- * 5. If multiple migrations share the same second, use hash matching as a tiebreaker
122- * Not implemented for now -> 6. If hash matching fails, fall back to serial id ordering
123- * 7. Set `version` to 1 on all rows
69+ * 3. Backfill `name` for existing rows by matching `created_at` (millis) to local migration folder timestamps
70+ * 4. If multiple migrations share the same second, use hash matching as a tiebreaker
71+ * Not implemented for now -> 5. If hash matching fails, fall back to serial id ordering
12472 */
12573 0 : async ( migrationsSchema , migrationsTable , session , localMigrations ) => {
12674 const table = sql `${ sql . identifier ( migrationsSchema ) } .${ sql . identifier ( migrationsTable ) } ` ;
@@ -130,7 +78,6 @@ const upgradeFunctions: Record<
13078 await session . execute (
13179 sql `ALTER TABLE ${ table } ADD COLUMN IF NOT EXISTS "applied_at" timestamp with time zone DEFAULT now()` ,
13280 ) ;
133- await session . execute ( sql `ALTER TABLE ${ table } ADD COLUMN IF NOT EXISTS "version" integer` ) ;
13481
13582 // 2. Read all existing DB migrations
13683 // Sort them by ids asc (order how they were applied)
@@ -175,31 +122,67 @@ const upgradeFunctions: Record<
175122 }
176123
177124 await session . execute (
178- sql `UPDATE ${ table } SET name = ${
179- matched ?. name ?? null
180- } , version = ${ 1 } , applied_at = NULL WHERE id = ${ dbRow . id } `,
125+ sql `UPDATE ${ table } SET name = ${ matched ?. name ?? null } , applied_at = NULL WHERE id = ${ dbRow . id } ` ,
181126 ) ;
182127 }
183128 } ,
184129} ;
185130
186131/**
187- * Runs all upgrade functions sequentially from `fromVersion` to CURRENT_MIGRATION_TABLE_VERSION.
132+ * Detects the current version of the migrations table schema and upgrades it if needed.
133+ *
134+ * Version 0: Original schema (id, hash, created_at)
135+ * Version 1: Extended schema (id, hash, created_at, name, applied_at)
188136 */
189- async function runUpgrades (
137+ async function upgradeIfNeeded (
190138 migrationsSchema : string ,
191139 migrationsTable : string ,
192140 session : CockroachSession ,
193- fromVersion : number ,
194141 localMigrations : MigrationMeta [ ] ,
195- ) : Promise < void > {
196- for ( let v = fromVersion ; v < CURRENT_MIGRATION_TABLE_VERSION ; v ++ ) {
142+ ) : Promise < UpgradeResult > {
143+ // Check if the table exists at all
144+ const tableExists = await session . all < { exists : boolean } > (
145+ sql `SELECT EXISTS (
146+ SELECT FROM information_schema.tables
147+ WHERE table_schema = ${ migrationsSchema }
148+ AND table_name = ${ migrationsTable }
149+ )` ,
150+ ) ;
151+
152+ if ( ! tableExists [ 0 ] ?. exists ) {
153+ return { newDb : true } ;
154+ }
155+
156+ // Table exists, check table shape
157+ const rows = await session . all < { schema : string ; table_name : string ; column_name : string ; type : string } > (
158+ sql `SELECT
159+ n.nspname AS "schema",
160+ c.relname AS "table_name",
161+ a.attname AS "column_name",
162+ pg_catalog.format_type(a.atttypid, a.atttypmod) AS "type"
163+ FROM
164+ pg_catalog.pg_attribute a
165+ JOIN pg_catalog.pg_class c ON c.oid = a.attrelid
166+ JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
167+ WHERE
168+ a.attnum > 0
169+ AND NOT a.attisdropped
170+ AND n.nspname = ${ migrationsSchema }
171+ AND c.relname = ${ migrationsTable }
172+ ORDER BY a.attnum;` ,
173+ ) ;
174+
175+ let version = getVersion ( rows . map ( ( r ) => r . column_name ) ) ;
176+
177+ for ( let v = version ; v < CURRENT_MIGRATION_TABLE_VERSION ; v ++ ) {
197178 const upgradeFn = upgradeFunctions [ v ] ;
198179 if ( ! upgradeFn ) {
199180 throw new Error ( `No upgrade path from migration table version ${ v } to ${ v + 1 } ` ) ;
200181 }
201182 await upgradeFn ( migrationsSchema , migrationsTable , session , localMigrations ) ;
202183 }
184+
185+ return { prevVersion : version , currentVersion : CURRENT_MIGRATION_TABLE_VERSION } ;
203186}
204187
205188export class CockroachDialect {
@@ -233,8 +216,7 @@ export class CockroachDialect {
233216 hash text NOT NULL,
234217 created_at bigint,
235218 name text,
236- applied_at timestamp with time zone DEFAULT now(),
237- version int4
219+ applied_at timestamp with time zone DEFAULT now()
238220 )
239221 ` ;
240222
@@ -263,8 +245,8 @@ export class CockroachDialect {
263245 await session . execute (
264246 sql `insert into ${ sql . identifier ( migrationsSchema ) } .${
265247 sql . identifier ( migrationsTable )
266- } ("hash", "created_at", "name", "version" )
267- values (${ migration . hash } , ${ migration . folderMillis } , ${ migration . name } , ${ CURRENT_MIGRATION_TABLE_VERSION } )` ,
248+ } ("hash", "created_at", "name")
249+ values (${ migration . hash } , ${ migration . folderMillis } , ${ migration . name } )` ,
268250 ) ;
269251
270252 return ;
@@ -279,7 +261,7 @@ export class CockroachDialect {
279261 await tx . execute (
280262 sql `insert into ${ sql . identifier ( migrationsSchema ) } .${
281263 sql . identifier ( migrationsTable )
282- } ("hash", "created_at") values(${ migration . hash } , ${ migration . folderMillis } )`,
264+ } ("hash", "created_at", "name" ) values(${ migration . hash } , ${ migration . folderMillis } , ${ migration . name } )`,
283265 ) ;
284266 }
285267 } ) ;
0 commit comments