fix: skip comment-only migration SQL
This commit is contained in:
@@ -268,7 +268,7 @@
|
|||||||
},
|
},
|
||||||
"packages/cli": {
|
"packages/cli": {
|
||||||
"name": "@wrnexus/cli",
|
"name": "@wrnexus/cli",
|
||||||
"version": "0.8.21",
|
"version": "0.8.22",
|
||||||
"bin": {
|
"bin": {
|
||||||
"wrnexus": "src/index.ts",
|
"wrnexus": "src/index.ts",
|
||||||
},
|
},
|
||||||
@@ -324,7 +324,7 @@
|
|||||||
},
|
},
|
||||||
"packages/db": {
|
"packages/db": {
|
||||||
"name": "@wrnexus/db",
|
"name": "@wrnexus/db",
|
||||||
"version": "0.8.9",
|
"version": "0.8.10",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/bun": "^1.3.14",
|
"@types/bun": "^1.3.14",
|
||||||
"typescript": "^5.9.2",
|
"typescript": "^5.9.2",
|
||||||
@@ -332,7 +332,7 @@
|
|||||||
},
|
},
|
||||||
"packages/dev-server": {
|
"packages/dev-server": {
|
||||||
"name": "@wrnexus/dev-server",
|
"name": "@wrnexus/dev-server",
|
||||||
"version": "0.8.20",
|
"version": "0.8.21",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@wrnexus/authz": "workspace:*",
|
"@wrnexus/authz": "workspace:*",
|
||||||
"@wrnexus/cache": "workspace:*",
|
"@wrnexus/cache": "workspace:*",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@wrnexus/cli",
|
"name": "@wrnexus/cli",
|
||||||
"version": "0.8.21",
|
"version": "0.8.22",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "src/index.ts",
|
"main": "src/index.ts",
|
||||||
"exports": {
|
"exports": {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@wrnexus/db",
|
"name": "@wrnexus/db",
|
||||||
"version": "0.8.9",
|
"version": "0.8.10",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "./src/index.ts",
|
"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();
|
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. */
|
/** Load and parse all migration files in a directory, sorted by filename. */
|
||||||
export function loadMigrations(dir: string): Migration[] {
|
export function loadMigrations(dir: string): Migration[] {
|
||||||
if (!existsSync(dir)) return [];
|
if (!existsSync(dir)) return [];
|
||||||
@@ -136,7 +180,7 @@ export async function applyMigrations(
|
|||||||
for (const migration of pending.filter(({ name }) => !current.has(name))) {
|
for (const migration of pending.filter(({ name }) => !current.has(name))) {
|
||||||
throwIfAborted(options.signal);
|
throwIfAborted(options.signal);
|
||||||
await db.tx(async (tx) => {
|
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]);
|
await tx.exec(`INSERT INTO ${MIGRATIONS_TABLE} (name) VALUES (?)`, [migration.name]);
|
||||||
});
|
});
|
||||||
done.push(migration.name);
|
done.push(migration.name);
|
||||||
@@ -175,7 +219,7 @@ export async function rollback(
|
|||||||
try {
|
try {
|
||||||
throwIfAborted(options.signal);
|
throwIfAborted(options.signal);
|
||||||
await db.tx(async (tx) => {
|
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]);
|
await tx.exec(`DELETE FROM ${MIGRATIONS_TABLE} WHERE name = ?`, [last]);
|
||||||
});
|
});
|
||||||
return last;
|
return last;
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import {
|
|||||||
createTableSql,
|
createTableSql,
|
||||||
parseMigration,
|
parseMigration,
|
||||||
applyMigrations,
|
applyMigrations,
|
||||||
|
appliedMigrations,
|
||||||
migrate,
|
migrate,
|
||||||
status,
|
status,
|
||||||
rollback,
|
rollback,
|
||||||
@@ -144,6 +145,22 @@ test("an empty migration set does not touch the database", async () => {
|
|||||||
expect(await applyMigrations(db, [])).toEqual([]);
|
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 () => {
|
test("migration dry-run plans changes without applying schema and honors cancellation", async () => {
|
||||||
const db = createDb(sqlite());
|
const db = createDb(sqlite());
|
||||||
const migrations = [
|
const migrations = [
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@wrnexus/dev-server",
|
"name": "@wrnexus/dev-server",
|
||||||
"version": "0.8.20",
|
"version": "0.8.21",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "src/index.ts",
|
"main": "src/index.ts",
|
||||||
"exports": {
|
"exports": {
|
||||||
|
|||||||
Reference in New Issue
Block a user