fix: skip comment-only migration SQL
This commit is contained in:
@@ -268,7 +268,7 @@
|
||||
},
|
||||
"packages/cli": {
|
||||
"name": "@wrnexus/cli",
|
||||
"version": "0.8.21",
|
||||
"version": "0.8.22",
|
||||
"bin": {
|
||||
"wrnexus": "src/index.ts",
|
||||
},
|
||||
@@ -324,7 +324,7 @@
|
||||
},
|
||||
"packages/db": {
|
||||
"name": "@wrnexus/db",
|
||||
"version": "0.8.9",
|
||||
"version": "0.8.10",
|
||||
"devDependencies": {
|
||||
"@types/bun": "^1.3.14",
|
||||
"typescript": "^5.9.2",
|
||||
@@ -332,7 +332,7 @@
|
||||
},
|
||||
"packages/dev-server": {
|
||||
"name": "@wrnexus/dev-server",
|
||||
"version": "0.8.20",
|
||||
"version": "0.8.21",
|
||||
"dependencies": {
|
||||
"@wrnexus/authz": "workspace:*",
|
||||
"@wrnexus/cache": "workspace:*",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/cli",
|
||||
"version": "0.8.21",
|
||||
"version": "0.8.22",
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
"exports": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/db",
|
||||
"version": "0.8.9",
|
||||
"version": "0.8.10",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"main": "./src/index.ts",
|
||||
|
||||
@@ -51,6 +51,50 @@ function section(content: string, which: "up" | "down"): string {
|
||||
return (next ? rest.slice(0, next.index) : rest).trim();
|
||||
}
|
||||
|
||||
function hasExecutableSql(sql: string): boolean {
|
||||
let quote = "";
|
||||
let lineComment = false;
|
||||
let blockComment = false;
|
||||
for (let index = 0; index < sql.length; index++) {
|
||||
const char = sql[index]!;
|
||||
const next = sql[index + 1] ?? "";
|
||||
if (lineComment) {
|
||||
if (char === "\n" || char === "\r") lineComment = false;
|
||||
continue;
|
||||
}
|
||||
if (blockComment) {
|
||||
if (char === "*" && next === "/") {
|
||||
blockComment = false;
|
||||
index++;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (quote) {
|
||||
if (char === quote) {
|
||||
if (next === quote) index++;
|
||||
else quote = "";
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (char === "-" && next === "-") {
|
||||
lineComment = true;
|
||||
index++;
|
||||
continue;
|
||||
}
|
||||
if (char === "/" && next === "*") {
|
||||
blockComment = true;
|
||||
index++;
|
||||
continue;
|
||||
}
|
||||
if (char === "'" || char === '"' || char === "`") {
|
||||
quote = char;
|
||||
return true;
|
||||
}
|
||||
if (!/\s|;/.test(char)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Load and parse all migration files in a directory, sorted by filename. */
|
||||
export function loadMigrations(dir: string): Migration[] {
|
||||
if (!existsSync(dir)) return [];
|
||||
@@ -136,7 +180,7 @@ export async function applyMigrations(
|
||||
for (const migration of pending.filter(({ name }) => !current.has(name))) {
|
||||
throwIfAborted(options.signal);
|
||||
await db.tx(async (tx) => {
|
||||
if (migration.up) await tx.exec(migration.up);
|
||||
if (hasExecutableSql(migration.up)) await tx.exec(migration.up);
|
||||
await tx.exec(`INSERT INTO ${MIGRATIONS_TABLE} (name) VALUES (?)`, [migration.name]);
|
||||
});
|
||||
done.push(migration.name);
|
||||
@@ -175,7 +219,7 @@ export async function rollback(
|
||||
try {
|
||||
throwIfAborted(options.signal);
|
||||
await db.tx(async (tx) => {
|
||||
if (migration?.down) await tx.exec(migration.down);
|
||||
if (migration && hasExecutableSql(migration.down)) await tx.exec(migration.down);
|
||||
await tx.exec(`DELETE FROM ${MIGRATIONS_TABLE} WHERE name = ?`, [last]);
|
||||
});
|
||||
return last;
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
createTableSql,
|
||||
parseMigration,
|
||||
applyMigrations,
|
||||
appliedMigrations,
|
||||
migrate,
|
||||
status,
|
||||
rollback,
|
||||
@@ -144,6 +145,22 @@ test("an empty migration set does not touch the database", async () => {
|
||||
expect(await applyMigrations(db, [])).toEqual([]);
|
||||
});
|
||||
|
||||
test("comment-only migrations are recorded without executing empty SQL", async () => {
|
||||
const db = createDb(sqlite());
|
||||
const migrations = [
|
||||
{
|
||||
name: "0001_placeholder",
|
||||
up: "-- Create application tables here.\n/* No schema yet. */\n;",
|
||||
down: "-- Nothing to roll back.",
|
||||
},
|
||||
];
|
||||
|
||||
expect(await applyMigrations(db, migrations)).toEqual(["0001_placeholder"]);
|
||||
expect(await appliedMigrations(db)).toEqual(["0001_placeholder"]);
|
||||
expect(await rollback(db, ".", { lock: false })).toBe("0001_placeholder");
|
||||
await db.close();
|
||||
});
|
||||
|
||||
test("migration dry-run plans changes without applying schema and honors cancellation", async () => {
|
||||
const db = createDb(sqlite());
|
||||
const migrations = [
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/dev-server",
|
||||
"version": "0.8.20",
|
||||
"version": "0.8.21",
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
"exports": {
|
||||
|
||||
Reference in New Issue
Block a user