Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion packages/db-postgres/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ import {
execute,
init,
insert,
makePostgresDropsIdempotent,
requireDrizzleKit,
} from '@payloadcms/drizzle/postgres'
import { pgEnum, pgSchema, pgTable } from 'drizzle-orm/pg-core'
Expand Down Expand Up @@ -104,7 +105,7 @@ export function postgresAdapter(args: Args): DatabaseAdapterObj<PostgresAdapter>
sqlExecute: string
statements: string[]
}): string => {
return `${sqlExecute}\n ${statements.join('\n')}\`)`
return `${sqlExecute}\n ${makePostgresDropsIdempotent(statements).join('\n')}\`)`
}

const executeMethod = 'execute'
Expand Down
3 changes: 2 additions & 1 deletion packages/db-vercel-postgres/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ import {
execute,
init,
insert,
makePostgresDropsIdempotent,
requireDrizzleKit,
} from '@payloadcms/drizzle/postgres'
import { pgEnum, pgSchema, pgTable } from 'drizzle-orm/pg-core'
Expand Down Expand Up @@ -100,7 +101,7 @@ export function vercelPostgresAdapter(args: Args = {}): DatabaseAdapterObj<Verce
}: {
sqlExecute: string
statements: string[]
}) => `${sqlExecute}\n ${statements.join('\n')}\`)`
}) => `${sqlExecute}\n ${makePostgresDropsIdempotent(statements).join('\n')}\`)`

const adapter = createDatabaseAdapter<VercelPostgresAdapter>({
name: 'postgres',
Expand Down
1 change: 1 addition & 0 deletions packages/drizzle/src/exports/postgres.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export { dropDatabase } from '../postgres/dropDatabase.js'
export { execute } from '../postgres/execute.js'
export { init } from '../postgres/init.js'
export { insert } from '../postgres/insert.js'
export { makePostgresDropsIdempotent } from '../utilities/makePostgresDropsIdempotent.js'
export { migratePostgresLocalizeStatus } from '../postgres/predefinedMigrations/localize-status/index.js'
export { migratePostgresV2toV3 } from '../postgres/predefinedMigrations/v2-v3/index.js'
export { requireDrizzleKit } from '../postgres/requireDrizzleKit.js'
Expand Down
43 changes: 43 additions & 0 deletions packages/drizzle/src/utilities/makePostgresDropsIdempotent.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { describe, expect, it } from 'vitest'

import { makePostgresDropsIdempotent } from './makePostgresDropsIdempotent.js'

describe('makePostgresDropsIdempotent', () => {
it('rewrites DROP CONSTRAINT to DROP CONSTRAINT IF EXISTS', () => {
const input = ['ALTER TABLE "posts_rels" DROP CONSTRAINT "posts_rels_posts_fk";']
expect(makePostgresDropsIdempotent(input)).toEqual([
'ALTER TABLE "posts_rels" DROP CONSTRAINT IF EXISTS "posts_rels_posts_fk";',
])
})

it('rewrites DROP INDEX to DROP INDEX IF EXISTS', () => {
const input = ['DROP INDEX "posts_title_idx";']
expect(makePostgresDropsIdempotent(input)).toEqual(['DROP INDEX IF EXISTS "posts_title_idx";'])
})

it('does not double-rewrite statements that already use IF EXISTS', () => {
const input = [
'ALTER TABLE "posts_rels" DROP CONSTRAINT IF EXISTS "posts_rels_posts_fk";',
'DROP INDEX IF EXISTS "posts_title_idx";',
]
expect(makePostgresDropsIdempotent(input)).toEqual(input)
})

it('leaves CREATE INDEX and other DDL untouched', () => {
const input = [
'CREATE INDEX IF NOT EXISTS "posts_title_idx" ON "posts" USING btree ("title");',
'DROP TABLE "posts_rels" CASCADE;',
'ALTER TABLE "posts" ADD COLUMN "slug" varchar;',
]
expect(makePostgresDropsIdempotent(input)).toEqual(input)
})

it('handles multiple drops in a single statement', () => {
const input = [
'ALTER TABLE "posts_rels" DROP CONSTRAINT "posts_rels_posts_fk", DROP CONSTRAINT "posts_rels_categories_fk";',
]
expect(makePostgresDropsIdempotent(input)).toEqual([
'ALTER TABLE "posts_rels" DROP CONSTRAINT IF EXISTS "posts_rels_posts_fk", DROP CONSTRAINT IF EXISTS "posts_rels_categories_fk";',
])
})
})
21 changes: 21 additions & 0 deletions packages/drizzle/src/utilities/makePostgresDropsIdempotent.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/**
* Rewrites `DROP CONSTRAINT` and `DROP INDEX` statements emitted by drizzle-kit so they
* include `IF EXISTS`. Postgres resolves dependent foreign keys when a referenced table
* is dropped with `CASCADE`, which causes follow-up explicit drops in the same migration
* to fail. Making these drops idempotent keeps the generated up/down migrations runnable
* end-to-end.
*
* Only rewrites statements that don't already contain `IF EXISTS` to stay safe against
* future drizzle-kit output changes.
*/
export const makePostgresDropsIdempotent = (statements: string[]): string[] =>
statements.map((statement) => {
let next = statement.replace(
/\bDROP CONSTRAINT\b(?! IF EXISTS\b)/gi,
'DROP CONSTRAINT IF EXISTS',
)

next = next.replace(/\bDROP INDEX\b(?! IF EXISTS\b)/gi, 'DROP INDEX IF EXISTS')

return next
})
Loading