Skip to content

feat: support IF EXISTS table guard for dropConstraint#1654

Merged
Shinigami92 merged 4 commits into
salsita:mainfrom
dosacha:feat/drop-constraint-if-table-exists
Jun 22, 2026
Merged

feat: support IF EXISTS table guard for dropConstraint#1654
Shinigami92 merged 4 commits into
salsita:mainfrom
dosacha:feat/drop-constraint-if-table-exists

Conversation

@dosacha

@dosacha dosacha commented Jun 21, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Add an explicit ifTableExists option to dropConstraint
  • Emit ALTER TABLE IF EXISTS when the option is set
  • Preserve the existing ifExists behavior for DROP CONSTRAINT IF EXISTS

Why

dropConstraint already supports ifExists, but that only applies to the constraint itself:

ALTER TABLE "my_table" DROP CONSTRAINT IF EXISTS "my_table_userId_fkey";

This still fails if the table no longer exists.

The issue describes a retry case for non-transactional migrations, where a previous migration attempt may have already dropped the table but failed before the migration was recorded as complete. In that case, retrying can fail at an earlier dropConstraint call.

PostgreSQL supports guarding the table separately:

ALTER TABLE IF EXISTS "my_table"
DROP CONSTRAINT IF EXISTS "my_table_userId_fkey";

So this PR adds a separate ifTableExists option for that case.

Notes

I kept ifTableExists separate from ifExists on purpose.

Changing ifExists to also emit ALTER TABLE IF EXISTS would be a behavior change. A missing table can still be useful as an error signal, for example when there is a typo, a wrong schema name, or a migration-order issue.

ifTableExists only guards the table part of the statement. If callers also want to ignore a missing constraint, they still need to pass ifExists: true.

I also avoided adding this option to the shared DropOptions type, since that would expose it to unrelated operations such as dropTable. Instead, DropConstraintOptions now extends DropOptions and adds only this option.

addConstraint already combines forward constraint options with reverse dropConstraint options, so I kept that existing structure and added a small test for the reverse pass-through path.

This does not make non-transactional migrations fully safe. It only lets dropConstraint emit ALTER TABLE IF EXISTS when the caller explicitly asks for it.

Tests

  • Added SQL generation coverage for ifTableExists
  • Added coverage for ifTableExists with ifExists
  • Added coverage for ifTableExists with ifExists and cascade
  • Added coverage for reverse pass-through from addConstraint to dropConstraint
  • Ran pnpm test
  • Ran pnpm test:unit test/operations/constraints/dropConstraint.spec.ts test/operations/constraints/addConstraint.spec.ts
  • Ran pnpm ts-check
  • Ran pnpm lint (0 errors; existing warnings are unrelated to this change)
  • Ran pnpm format:check
  • Ran pnpm docs:build
  • Added integration coverage under test/migrations
  • Updated migration snapshots/counts

Closes #1628

@Shinigami92 Shinigami92 added the c: feature Request for new feature label Jun 21, 2026

@Shinigami92 Shinigami92 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This requires an integration test in test/migrations

@github-actions

github-actions Bot commented Jun 21, 2026

Copy link
Copy Markdown

Coverage Report

Status Category Percentage Covered / Total
🔵 Lines 93.68% (🎯 90%)
🟰 ±0%
1558 / 1663
🔵 Statements 93.59% (🎯 90%)
🟰 ±0%
1578 / 1686
🔵 Functions 95.06% (🎯 90%)
🟰 ±0%
347 / 365
🔵 Branches 87.33% (🎯 85%)
⬆️ +0.03%
993 / 1137
File Coverage
File Stmts Branches Functions Lines Uncovered Lines
Changed Files
src/operations/tables/dropConstraint.ts 100%
🟰 ±0%
100%
🟰 ±0%
100%
🟰 ±0%
100%
🟰 ±0%
Unchanged Files
src/db.ts 78.43% 75% 76.92% 78% 88-90, 123-135, 171, 175-176
src/index.ts 100% 100% 100% 100%
src/logger.ts 100% 100% 100% 100%
src/migration.ts 76.53% 69.11% 80% 76.28% 129-131, 153-168, 233-268, 334, 361-363, 371, 389-391, 421
src/migrationBuilder.ts 95.49% 62.5% 90% 95.49% 833-836, 973-977
src/migrationLoader.ts 91.26% 84.74% 90.9% 93.87% 277, 417, 421, 424, 427, 461, 480, 487-488
src/migrationOptions.ts 100% 100% 100% 100%
src/pgType.ts 100% 100% 100% 100%
src/runner.ts 78.86% 63.88% 82.6% 78.15% 217-219, 238-240, 292, 337, 347-350, 364, 376-378, 402, 443-454, 459-461, 482-483, 493, 495-503
src/sqlMigration.ts 100% 100% 100% 100%
src/operations/sql.ts 100% 50% 100% 100%
src/operations/casts/createCast.ts 100% 100% 100% 100%
src/operations/casts/dropCast.ts 100% 100% 100% 100%
src/operations/casts/index.ts 100% 100% 100% 100%
src/operations/domains/alterDomain.ts 100% 100% 100% 100%
src/operations/domains/createDomain.ts 100% 95.45% 100% 100%
src/operations/domains/dropDomain.ts 100% 100% 100% 100%
src/operations/domains/index.ts 100% 100% 100% 100%
src/operations/domains/renameDomain.ts 100% 100% 100% 100%
src/operations/domains/shared.ts 100% 100% 100% 100%
src/operations/extensions/createExtension.ts 100% 100% 100% 100%
src/operations/extensions/dropExtension.ts 100% 100% 100% 100%
src/operations/extensions/index.ts 100% 100% 100% 100%
src/operations/extensions/shared.ts 100% 100% 100% 100%
src/operations/functions/createFunction.ts 96.29% 92% 100% 96.29% 71-73
src/operations/functions/dropFunction.ts 100% 100% 100% 100%
src/operations/functions/index.ts 100% 100% 100% 100%
src/operations/functions/renameFunction.ts 100% 100% 100% 100%
src/operations/functions/shared.ts 100% 100% 100% 100%
src/operations/grants/grantOnSchemas.ts 100% 100% 100% 100%
src/operations/grants/grantOnTables.ts 100% 100% 100% 100%
src/operations/grants/grantRoles.ts 100% 100% 100% 100%
src/operations/grants/index.ts 100% 100% 100% 100%
src/operations/grants/revokeOnSchemas.ts 100% 100% 100% 100%
src/operations/grants/revokeOnTables.ts 100% 100% 100% 100%
src/operations/grants/revokeRoles.ts 100% 100% 100% 100%
src/operations/grants/shared.ts 100% 75% 100% 100%
src/operations/indexes/createIndex.ts 100% 100% 100% 100%
src/operations/indexes/dropIndex.ts 100% 100% 100% 100%
src/operations/indexes/index.ts 100% 100% 100% 100%
src/operations/indexes/shared.ts 100% 88.23% 100% 100%
src/operations/materializedViews/alterMaterializedView.ts 100% 100% 100% 100%
src/operations/materializedViews/createMaterializedView.ts 100% 100% 100% 100%
src/operations/materializedViews/dropMaterializedView.ts 100% 100% 100% 100%
src/operations/materializedViews/index.ts 100% 100% 100% 100%
src/operations/materializedViews/refreshMaterializedView.ts 100% 100% 100% 100%
src/operations/materializedViews/renameMaterializedView.ts 100% 100% 100% 100%
src/operations/materializedViews/renameMaterializedViewColumn.ts 100% 100% 100% 100%
src/operations/materializedViews/shared.ts 100% 83.33% 100% 100%
src/operations/operators/addToOperatorFamily.ts 100% 100% 100% 100%
src/operations/operators/createOperator.ts 100% 88.88% 100% 100%
src/operations/operators/createOperatorClass.ts 100% 75% 100% 100%
src/operations/operators/createOperatorFamily.ts 100% 100% 100% 100%
src/operations/operators/dropOperator.ts 100% 100% 100% 100%
src/operations/operators/dropOperatorClass.ts 100% 100% 100% 100%
src/operations/operators/dropOperatorFamily.ts 100% 100% 100% 100%
src/operations/operators/index.ts 100% 100% 100% 100%
src/operations/operators/removeFromOperatorFamily.ts 100% 100% 100% 100%
src/operations/operators/renameOperatorClass.ts 100% 100% 100% 100%
src/operations/operators/renameOperatorFamily.ts 100% 100% 100% 100%
src/operations/operators/shared.ts 81.81% 77.77% 100% 81.81% 24, 38
src/operations/policies/alterPolicy.ts 100% 100% 100% 100%
src/operations/policies/createPolicy.ts 100% 100% 100% 100%
src/operations/policies/dropPolicy.ts 100% 100% 100% 100%
src/operations/policies/index.ts 100% 100% 100% 100%
src/operations/policies/renamePolicy.ts 100% 100% 100% 100%
src/operations/policies/shared.ts 100% 100% 100% 100%
src/operations/roles/alterRole.ts 100% 100% 100% 100%
src/operations/roles/createRole.ts 100% 92.3% 100% 100%
src/operations/roles/dropRole.ts 100% 100% 100% 100%
src/operations/roles/index.ts 100% 100% 100% 100%
src/operations/roles/renameRole.ts 100% 100% 100% 100%
src/operations/roles/shared.ts 100% 86.66% 100% 100%
src/operations/schemas/createSchema.ts 100% 100% 100% 100%
src/operations/schemas/dropSchema.ts 100% 100% 100% 100%
src/operations/schemas/index.ts 100% 100% 100% 100%
src/operations/schemas/renameSchema.ts 100% 100% 100% 100%
src/operations/sequences/alterSequence.ts 90% 83.33% 100% 90% 23
src/operations/sequences/createSequence.ts 100% 100% 100% 100%
src/operations/sequences/dropSequence.ts 100% 100% 100% 100%
src/operations/sequences/index.ts 100% 100% 100% 100%
src/operations/sequences/renameSequence.ts 100% 100% 100% 100%
src/operations/sequences/shared.ts 81.48% 83.33% 100% 81.48% 41, 43, 49, 63, 69
src/operations/tables/addColumns.ts 100% 85.71% 100% 100%
src/operations/tables/addConstraint.ts 100% 100% 100% 100%
src/operations/tables/alterColumn.ts 91.17% 81.25% 100% 91.17% 69, 76-83
src/operations/tables/alterTable.ts 100% 100% 100% 100%
src/operations/tables/createTable.ts 88% 82.14% 100% 88% 51-54, 92
src/operations/tables/dropColumns.ts 100% 100% 100% 100%
src/operations/tables/dropTable.ts 100% 100% 100% 100%
src/operations/tables/index.ts 100% 100% 100% 100%
src/operations/tables/renameColumn.ts 100% 100% 100% 100%
src/operations/tables/renameConstraint.ts 100% 100% 100% 100%
src/operations/tables/renameTable.ts 100% 100% 100% 100%
src/operations/tables/shared.ts 86.91% 77.66% 76.47% 86.91% 159, 242, 262, 288, 292-298, 302, 450-471
src/operations/triggers/createTrigger.ts 88.23% 71.79% 100% 88.23% 53, 66, 70, 74-76
src/operations/triggers/dropTrigger.ts 100% 100% 100% 100%
src/operations/triggers/index.ts 100% 100% 100% 100%
src/operations/triggers/renameTrigger.ts 100% 100% 100% 100%
src/operations/triggers/shared.ts 100% 100% 100% 100%
src/operations/types/addTypeAttribute.ts 100% 100% 100% 100%
src/operations/types/addTypeValue.ts 100% 100% 100% 100%
src/operations/types/createType.ts 100% 100% 100% 100%
src/operations/types/dropType.ts 100% 100% 100% 100%
src/operations/types/dropTypeAttribute.ts 100% 100% 100% 100%
src/operations/types/index.ts 100% 100% 100% 100%
src/operations/types/renameType.ts 100% 100% 100% 100%
src/operations/types/renameTypeAttribute.ts 100% 100% 100% 100%
src/operations/types/renameTypeValue.ts 100% 100% 100% 100%
src/operations/types/setTypeAttribute.ts 100% 100% 100% 100%
src/operations/views/alterView.ts 100% 100% 100% 100%
src/operations/views/alterViewColumn.ts 100% 100% 100% 100%
src/operations/views/createView.ts 100% 100% 100% 100%
src/operations/views/dropView.ts 100% 100% 100% 100%
src/operations/views/index.ts 100% 100% 100% 100%
src/operations/views/renameView.ts 100% 100% 100% 100%
src/operations/views/shared.ts 100% 50% 100% 100%
src/utils/PgLiteral.ts 100% 100% 100% 100%
src/utils/comparators.ts 100% 100% 100% 100%
src/utils/createSchemalize.ts 96.42% 93.54% 100% 96.29% 84
src/utils/createTransformer.ts 100% 100% 100% 100%
src/utils/decamelize.ts 100% 100% 100% 100%
src/utils/escapeValue.ts 100% 100% 100% 100%
src/utils/fileNameUtils.ts 100% 100% 100% 100%
src/utils/formatLines.ts 100% 100% 100% 100%
src/utils/formatParams.ts 100% 87.5% 100% 100%
src/utils/formatPartitionColumns.ts 100% 100% 100% 100%
src/utils/getMigrationTableSchema.ts 100% 100% 100% 100%
src/utils/getSchemas.ts 100% 100% 100% 100%
src/utils/identity.ts 100% 100% 100% 100%
src/utils/index.ts 100% 100% 100% 100%
src/utils/intersection.ts 100% 100% 100% 100%
src/utils/makeComment.ts 100% 100% 100% 100%
src/utils/quote.ts 100% 100% 100% 100%
src/utils/stringIdGenerator.ts 100% 100% 100% 100%
src/utils/toArray.ts 100% 100% 100% 100%
src/utils/types.ts 100% 100% 100% 100%
Generated in workflow #3459 for commit fa98d67 by the Vitest Coverage Report Action

@dosacha

dosacha commented Jun 22, 2026

Copy link
Copy Markdown
Contributor Author

Thanks😀 added a migration case under test/migrations for the missing-table path

I also updated the migration fixture counts and snapshots after adding the new migration file. The full test suite now passes locally with 'pnpm test'

@Shinigami92 Shinigami92 self-requested a review June 22, 2026 11:38
@Shinigami92 Shinigami92 force-pushed the feat/drop-constraint-if-table-exists branch from 1f86eb1 to e4ae3d0 Compare June 22, 2026 11:40
Shinigami92
Shinigami92 previously approved these changes Jun 22, 2026
@Shinigami92 Shinigami92 dismissed their stale review June 22, 2026 11:48

failing integration tests 👀

@Shinigami92

Copy link
Copy Markdown
Collaborator

I will regenerate snapshots with PGM_VERSIONS='13,14,15,16,17,18' pnpm test:update-snapshots

@Shinigami92 Shinigami92 added this pull request to the merge queue Jun 22, 2026
Merged via the queue into salsita:main with commit 7254aea Jun 22, 2026
28 checks passed
@dosacha dosacha deleted the feat/drop-constraint-if-table-exists branch July 2, 2026 07:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

c: feature Request for new feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

dropConstraint: add ifTableExists option for idempotent retries

2 participants