Skip to content

fix: escape migration names in history statements - #1759

Open
Creatixpy wants to merge 1 commit into
salsita:mainfrom
Creatixpy:fix/migration-name-bookkeeping
Open

Creatixpy wants to merge 1 commit into
salsita:mainfrom
Creatixpy:fix/migration-name-bookkeeping

Conversation

@Creatixpy

Copy link
Copy Markdown
Collaborator

Migration filenames such as 0001_user's-table.cjs now retain their exact names when recorded in or removed from migration history. Previously, apostrophes generated invalid history SQL, while repeated apostrophes could silently change the recorded name. With pgm.noTransaction(), schema changes could succeed before history recording failed.

Reuse escapeValue for both history insertion and deletion, covering normal execution, explicit and automatic rollback, and fake migrations. Accepted filenames, public APIs, types, schema/table quoting, transaction behavior, and dry-run guarantees are preserved.

Also fix a related delimiter collision in escapeValue: a value ending in $pga could combine with the closing delimiter's first $ and terminate the literal early. Delimiter selection now rejects these overlaps as well as complete delimiters within values. Regression tests cover scalar and nested-array values, and existing SQL snapshots change only in history-name quoting. Schema changes left unrecorded by earlier failed runs still require manual reconciliation.

Validation for 63e230f1b053c27e20c4d4aa6e6dd5653b590e6e on Node 24.21.0 / pnpm 12.4.1:

  • Before implementation, 32 focused unit cases and 14 PostgreSQL integration cases failed; they pass after the fixes.
  • All 2,586 unit tests pass. Coverage is 94.61% statements, 89.40% branches, 95.93% functions, and 94.56% lines, above unchanged thresholds.
  • All 95 new integration cases pass across PostgreSQL 14–18, covering CLI-generated names, exact history values, nontransactional migrations, fake/dry runs, and preservation of unrelated history and data.
  • Full CI passes all 790 integration cases on each of Node 22/24/26 across PostgreSQL 14–18.
  • Build, type checking, lint, formatting, documentation build, and documentation browser tests pass.
  • Dedicated PostgreSQL and CockroachDB checks, Windows/macOS unit matrices, minimum-runtime and package-consumer checks, strict coverage, and its report pass for this revision.

Fixes #1753.

@github-actions

Copy link
Copy Markdown

Coverage Report

Status Category Percentage Covered / Total
🟢 Lines 94.56% (🎯 90%)
🟰 ±0%
1689 / 1786
🟢 Statements 94.61% (🎯 90%)
🟰 ±0%
1704 / 1801
🟢 Functions 95.93% (🎯 90%)
🟰 ±0%
378 / 394
🟢 Branches 89.4% (🎯 85%)
⬆️ +0.01%
1131 / 1265
File Coverage
File Stmts Branches Functions Lines Uncovered Lines
Changed Files
src/migration.ts 76.69%
🟰 ±0%
70.83%
🟰 ±0%
80.95%
🟰 ±0%
76.47%
🟰 ±0%
33-35, 141-143, 165-179, 244-280, 345, 370-372, 384, 402-404
src/utils/escapeValue.ts 100%
🟰 ±0%
100%
🟰 ±0%
100%
🟰 ±0%
100%
🟰 ±0%
Unchanged Files
src/db.ts 80% 75% 78.57% 79.62% 102-104, 137-149, 193, 197-198
src/index.ts 100% 100% 100% 100%
src/logger.ts 100% 100% 100% 100%
src/migrationBuilder.ts 97.32% 77.77% 100% 97.32% 843-846, 986
src/migrationLoader.ts 91.26% 84.74% 90.9% 91.17% 251, 256, 261, 266, 301, 320, 328-332, 451
src/migrationOptions.ts 100% 100% 100% 100%
src/pgType.ts 100% 100% 100% 100%
src/runner.ts 91.66% 85.31% 92.85% 91.45% 252-254, 273-275, 466-468, 682, 692-695, 711, 723-725, 873-881, 892, 906
src/sqlMigration.ts 100% 100% 100% 100%
src/operations/createRenameOperation.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.55% 92% 100% 96.55% 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/renameIndex.ts 100% 100% 100% 100%
src/operations/indexes/shared.ts 100% 94.44% 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/formatIndexMethod.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 91.66% 83.33% 100% 91.66% 24
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% 70, 77-84
src/operations/tables/alterTable.ts 100% 100% 100% 100%
src/operations/tables/createTable.ts 89.65% 83.33% 100% 89.65% 52-55, 101
src/operations/tables/dropColumns.ts 100% 100% 100% 100%
src/operations/tables/dropConstraint.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.22% 76.47% 86.91% 159, 242, 262, 290, 294-300, 304, 457-478
src/operations/triggers/createTrigger.ts 88.57% 71.79% 100% 88.57% 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 100% 100% 100% 100%
src/utils/createTransformer.ts 100% 100% 100% 100%
src/utils/decamelize.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% 90% 100% 100%
src/utils/formatPartitionColumns.ts 100% 100% 100% 100%
src/utils/formatSql.ts 100% 100% 100% 100%
src/utils/getMigrationTableName.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/isSingleIdentifier.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 #4148 for commit 63e230f by the Vitest Coverage Report Action

@Creatixpy

Copy link
Copy Markdown
Collaborator Author

Most of the diff comes from regression tests and SQL snapshots repeated across PostgreSQL 14–18. The production fix touches only two files.

It fixes migration-history insertion and deletion when apostrophes cause SQL errors or alter stored names, plus a related dollar-delimiter collision in escapeValue. Tests verify exact history values and correct behavior during rollback, retries, nontransactional migrations, and fake/dry runs.

@Creatixpy Creatixpy added the c: bug Something isn't working label Sep 22, 2026
@Creatixpy Creatixpy self-assigned this Sep 22, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

c: bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Apostrophes in migration filenames break history recording and can leave changes unrecorded

1 participant