Describe the bug
The CLI accepts migration names containing apostrophes, but applying the generated migration fails when its name is inserted into the migration history table.
With a transactional migration, this prevents application. With a migration using pgm.noTransaction(), schema changes can succeed before the history insert fails, leaving the database changed without a corresponding migration record.
Steps to reproduce
In a new consumer project, install:
npm install node-pg-migrate@9.0.0 pg@8.23.0
Set DATABASE_URL explicitly to an empty disposable PostgreSQL database, then run:
npx node-pg-migrate create "user's-table" \
-j cjs --migration-filename-format index
npx node-pg-migrate up
Scaffolding succeeds and creates 0001_user's-table.cjs. Applying even the empty generated migration exits with status 1 and PostgreSQL error 42601.
The generated history statement contains:
INSERT INTO "public"."pgmigrations" (name, run_on)
VALUES ('0001_user's-table', NOW());
The apostrophe in the filename terminates the SQL string literal.
I also verified the following nontransactional variant on both versions, using a migration named 001_user's.cjs in a separate migration directory against a fresh database:
exports.up = (pgm) => {
pgm.noTransaction();
pgm.createTable('unrecorded_table', { id: 'integer' });
};
Apply it with the same npx node-pg-migrate up command. After the failed run:
SELECT to_regclass('public.unrecorded_table');
-- unrecorded_table
SELECT count(*) FROM public.pgmigrations;
-- 0
The table exists, but the migration is still considered pending.
Expected behavior
Migration names accepted by scaffolding should be recorded as literal values, including apostrophes.
If a filename restriction is intended, validation should reject the name before executing migration SQL.
Impact and technical context
The failure occurs during bookkeeping, after migration statements have already executed. In the nontransactional case, retrying attempts to apply changes that are already present.
Migration._getMarkAsRun() interpolates the migration name directly into SQL string literals. The same helper constructs the deletion statement used during rollback; the executed reproductions above cover normal and nontransactional up migrations.
Useful regression coverage would include CLI-generated names containing apostrophes, up/down bookkeeping, fake migrations, and a nontransactional migration whose history record is successfully written.
System info
- node-pg-migrate: npm
9.0.0 and upstream commit ad9421519df827b1910e3a47611d6589c45246c4.
- The upstream build's manifest says
10.0.0-alpha.2; this was a source build, not the published alpha package.
- Node.js
24.21.0, pg 8.23.0, Ubuntu 24.04 ARM64.
- Basic failure reproduced on PostgreSQL
16.15 with both versions and PostgreSQL 18.6 with current main.
- The unrecorded-table variant was also verified on PostgreSQL
16.15 with both versions and PostgreSQL 18.6 with current main.
Describe the bug
The CLI accepts migration names containing apostrophes, but applying the generated migration fails when its name is inserted into the migration history table.
With a transactional migration, this prevents application. With a migration using
pgm.noTransaction(), schema changes can succeed before the history insert fails, leaving the database changed without a corresponding migration record.Steps to reproduce
In a new consumer project, install:
Set
DATABASE_URLexplicitly to an empty disposable PostgreSQL database, then run:npx node-pg-migrate create "user's-table" \ -j cjs --migration-filename-format index npx node-pg-migrate upScaffolding succeeds and creates
0001_user's-table.cjs. Applying even the empty generated migration exits with status1and PostgreSQL error42601.The generated history statement contains:
The apostrophe in the filename terminates the SQL string literal.
I also verified the following nontransactional variant on both versions, using a migration named
001_user's.cjsin a separate migration directory against a fresh database:Apply it with the same
npx node-pg-migrate upcommand. After the failed run:The table exists, but the migration is still considered pending.
Expected behavior
Migration names accepted by scaffolding should be recorded as literal values, including apostrophes.
If a filename restriction is intended, validation should reject the name before executing migration SQL.
Impact and technical context
The failure occurs during bookkeeping, after migration statements have already executed. In the nontransactional case, retrying attempts to apply changes that are already present.
Migration._getMarkAsRun()interpolates the migration name directly into SQL string literals. The same helper constructs the deletion statement used during rollback; the executed reproductions above cover normal and nontransactional up migrations.Useful regression coverage would include CLI-generated names containing apostrophes, up/down bookkeeping, fake migrations, and a nontransactional migration whose history record is successfully written.
System info
9.0.0and upstream commitad9421519df827b1910e3a47611d6589c45246c4.10.0.0-alpha.2; this was a source build, not the published alpha package.24.21.0,pg8.23.0, Ubuntu24.04ARM64.16.15with both versions and PostgreSQL18.6with current main.16.15with both versions and PostgreSQL18.6with current main.