@@ -28,8 +28,54 @@ interface JournalEntry {
2828}
2929
3030/**
31- * If Drizzle's journal table is empty but app tables already exist (from db:push),
32- * seed the journal with hashes for all existing migration files so Drizzle skips them.
31+ * Check whether a single migration's schema changes have already been applied.
32+ * Supports CREATE TABLE and ALTER TABLE ADD COLUMN statements.
33+ * Returns true only if every statement in the migration is already reflected in the DB.
34+ */
35+ async function isMigrationApplied ( pool : mysql . Pool , migrationSql : string ) : Promise < boolean > {
36+ const statements = migrationSql
37+ . split ( ';' )
38+ . map ( ( s ) => s . trim ( ) )
39+ . filter ( Boolean )
40+
41+ for ( const stmt of statements ) {
42+ // ALTER TABLE `x` ADD `y` ...
43+ const addCol = stmt . match (
44+ / A L T E R \s + T A B L E \s + ` ( \w + ) ` \s + A D D \s + (?: C O L U M N \s + ) ? ` ( \w + ) ` / i,
45+ )
46+ if ( addCol ) {
47+ const [ rows ] = await pool . query (
48+ 'SELECT COUNT(*) AS cnt FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = ? AND COLUMN_NAME = ?' ,
49+ [ addCol [ 1 ] , addCol [ 2 ] ] ,
50+ )
51+ if ( ( rows as Array < { cnt : number } > ) [ 0 ] ?. cnt === 0 ) return false
52+ continue
53+ }
54+
55+ // CREATE TABLE `x` ...
56+ const createTbl = stmt . match (
57+ / C R E A T E \s + T A B L E \s + (?: I F \s + N O T \s + E X I S T S \s + ) ? ` ( \w + ) ` / i,
58+ )
59+ if ( createTbl ) {
60+ const [ rows ] = await pool . query ( 'SHOW TABLES LIKE ?' , [ createTbl [ 1 ] ] )
61+ if ( ( rows as unknown [ ] ) . length === 0 ) return false
62+ continue
63+ }
64+
65+ // Statement type we can't verify — assume not applied
66+ return false
67+ }
68+
69+ return statements . length > 0
70+ }
71+
72+ /**
73+ * Ensure the Drizzle journal reflects migrations whose schema changes are
74+ * already present in the database (e.g. applied via `db:push`).
75+ *
76+ * Handles both empty journals and partially populated ones — any migration
77+ * whose hash is missing from the journal but whose changes already exist in
78+ * the DB will be seeded so Drizzle doesn't try to re-run it.
3379 */
3480async function seedDrizzleJournalIfNeeded ( pool : mysql . Pool ) : Promise < void > {
3581 // Ensure the journal table exists
@@ -41,14 +87,16 @@ async function seedDrizzleJournalIfNeeded(pool: mysql.Pool): Promise<void> {
4187 )
4288 ` )
4389
44- const [ rows ] = await pool . query ( 'SELECT COUNT(*) as cnt FROM `__drizzle_migrations`' )
45- const count = ( rows as Array < { cnt : number } > ) [ 0 ] ?. cnt ?? 0
46- if ( count > 0 ) return // journal already populated
47-
4890 // Check if app tables exist (canary: sandboxes)
4991 const [ tables ] = await pool . query ( `SHOW TABLES LIKE 'sandboxes'` )
5092 if ( ( tables as unknown [ ] ) . length === 0 ) return // fresh DB, nothing to seed
5193
94+ // Collect hashes already in the journal
95+ const [ existingRows ] = await pool . query ( 'SELECT `hash` FROM `__drizzle_migrations`' )
96+ const existingHashes = new Set (
97+ ( existingRows as Array < { hash : string } > ) . map ( ( r ) => r . hash ) ,
98+ )
99+
52100 // Read the Drizzle journal to find all migration entries
53101 const journalPath = resolve ( __dirname , '..' , 'drizzle' , 'meta' , '_journal.json' )
54102 const journal = JSON . parse ( readFileSync ( journalPath , 'utf-8' ) ) as {
@@ -59,6 +107,13 @@ async function seedDrizzleJournalIfNeeded(pool: mysql.Pool): Promise<void> {
59107 const sqlPath = resolve ( __dirname , '..' , 'drizzle' , `${ entry . tag } .sql` )
60108 const sql = readFileSync ( sqlPath , 'utf-8' )
61109 const hash = createHash ( 'sha256' ) . update ( sql ) . digest ( 'hex' )
110+
111+ if ( existingHashes . has ( hash ) ) continue // already recorded
112+
113+ // Only seed if the migration's changes are already in the DB
114+ const applied = await isMigrationApplied ( pool , sql )
115+ if ( ! applied ) continue
116+
62117 await pool . query (
63118 'INSERT INTO `__drizzle_migrations` (`hash`, `created_at`) VALUES (?, ?)' ,
64119 [ hash , entry . when ] ,
0 commit comments