mirror of
https://github.com/n8n-io/n8n.git
synced 2024-09-19 22:37:31 -07:00
fix(core): All migrations should run in a transaction (#6519)
This commit is contained in:
parent
ddfb24b325
commit
e152cfe27c
|
@ -95,7 +95,8 @@
|
||||||
"element-ui@2.15.12": "patches/element-ui@2.15.12.patch",
|
"element-ui@2.15.12": "patches/element-ui@2.15.12.patch",
|
||||||
"typedi@0.10.0": "patches/typedi@0.10.0.patch",
|
"typedi@0.10.0": "patches/typedi@0.10.0.patch",
|
||||||
"@sentry/cli@2.17.0": "patches/@sentry__cli@2.17.0.patch",
|
"@sentry/cli@2.17.0": "patches/@sentry__cli@2.17.0.patch",
|
||||||
"pkce-challenge@3.0.0": "patches/pkce-challenge@3.0.0.patch"
|
"pkce-challenge@3.0.0": "patches/pkce-challenge@3.0.0.patch",
|
||||||
|
"typeorm@0.3.12": "patches/typeorm@0.3.12.patch"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
import type { MigrationContext, ReversibleMigration } from '@db/types';
|
import type { MigrationContext, IrreversibleMigration } from '@db/types';
|
||||||
import config from '@/config';
|
import config from '@/config';
|
||||||
|
|
||||||
const COLLATION_57 = 'utf8mb4_general_ci';
|
const COLLATION_57 = 'utf8mb4_general_ci';
|
||||||
const COLLATION_80 = 'utf8mb4_0900_ai_ci';
|
const COLLATION_80 = 'utf8mb4_0900_ai_ci';
|
||||||
|
|
||||||
export class MigrateIntegerKeysToString1690000000001 implements ReversibleMigration {
|
export class MigrateIntegerKeysToString1690000000001 implements IrreversibleMigration {
|
||||||
async up({ queryRunner, tablePrefix }: MigrationContext) {
|
async up({ queryRunner, tablePrefix }: MigrationContext) {
|
||||||
const databaseType = config.get('database.type');
|
const databaseType = config.get('database.type');
|
||||||
let collation: string;
|
let collation: string;
|
||||||
|
@ -272,7 +272,4 @@ export class MigrateIntegerKeysToString1690000000001 implements ReversibleMigrat
|
||||||
);
|
);
|
||||||
await queryRunner.query(`ALTER TABLE ${tablePrefix}variables DROP COLUMN \`tmp_id\`;`);
|
await queryRunner.query(`ALTER TABLE ${tablePrefix}variables DROP COLUMN \`tmp_id\`;`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-empty-function, @typescript-eslint/no-unused-vars
|
|
||||||
async down({ queryRunner, tablePrefix }: MigrationContext) {}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
/* eslint-disable n8n-local-rules/no-unneeded-backticks */
|
/* eslint-disable n8n-local-rules/no-unneeded-backticks */
|
||||||
import type { MigrationContext, ReversibleMigration } from '@db/types';
|
import type { MigrationContext, IrreversibleMigration } from '@db/types';
|
||||||
|
|
||||||
export class MigrateIntegerKeysToString1690000000000 implements ReversibleMigration {
|
export class MigrateIntegerKeysToString1690000000000 implements IrreversibleMigration {
|
||||||
async up({ queryRunner, tablePrefix }: MigrationContext) {
|
async up({ queryRunner, tablePrefix }: MigrationContext) {
|
||||||
await queryRunner.query(
|
await queryRunner.query(
|
||||||
`ALTER TABLE ${tablePrefix}workflow_entity RENAME COLUMN id to tmp_id;`,
|
`ALTER TABLE ${tablePrefix}workflow_entity RENAME COLUMN id to tmp_id;`,
|
||||||
|
@ -260,7 +260,4 @@ export class MigrateIntegerKeysToString1690000000000 implements ReversibleMigrat
|
||||||
await queryRunner.query(`ALTER TABLE ${tablePrefix}variables DROP COLUMN tmp_id;`);
|
await queryRunner.query(`ALTER TABLE ${tablePrefix}variables DROP COLUMN tmp_id;`);
|
||||||
await queryRunner.query(`ALTER TABLE ${tablePrefix}variables ADD PRIMARY KEY (id);`);
|
await queryRunner.query(`ALTER TABLE ${tablePrefix}variables ADD PRIMARY KEY (id);`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-empty-function, @typescript-eslint/no-unused-vars
|
|
||||||
async down({ queryRunner, tablePrefix }: MigrationContext) {}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,7 @@
|
||||||
import type { MigrationContext, ReversibleMigration } from '@db/types';
|
import type { MigrationContext, ReversibleMigration } from '@db/types';
|
||||||
|
|
||||||
export class AddUserSettings1652367743993 implements ReversibleMigration {
|
export class AddUserSettings1652367743993 implements ReversibleMigration {
|
||||||
transaction = false as const;
|
|
||||||
|
|
||||||
async up({ queryRunner, tablePrefix }: MigrationContext) {
|
async up({ queryRunner, tablePrefix }: MigrationContext) {
|
||||||
await queryRunner.query('PRAGMA foreign_keys=OFF');
|
|
||||||
|
|
||||||
await queryRunner.query(
|
await queryRunner.query(
|
||||||
`CREATE TABLE "temporary_user" ("id" varchar PRIMARY KEY NOT NULL, "email" varchar(255), "firstName" varchar(32), "lastName" varchar(32), "password" varchar, "resetPasswordToken" varchar, "resetPasswordTokenExpiration" integer DEFAULT NULL, "personalizationAnswers" text, "createdAt" datetime(3) NOT NULL DEFAULT (STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW')), "updatedAt" datetime(3) NOT NULL DEFAULT (STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW')), "globalRoleId" integer NOT NULL, "settings" text, CONSTRAINT "FK_${tablePrefix}f0609be844f9200ff4365b1bb3d" FOREIGN KEY ("globalRoleId") REFERENCES "${tablePrefix}role" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION)`,
|
`CREATE TABLE "temporary_user" ("id" varchar PRIMARY KEY NOT NULL, "email" varchar(255), "firstName" varchar(32), "lastName" varchar(32), "password" varchar, "resetPasswordToken" varchar, "resetPasswordTokenExpiration" integer DEFAULT NULL, "personalizationAnswers" text, "createdAt" datetime(3) NOT NULL DEFAULT (STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW')), "updatedAt" datetime(3) NOT NULL DEFAULT (STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW')), "globalRoleId" integer NOT NULL, "settings" text, CONSTRAINT "FK_${tablePrefix}f0609be844f9200ff4365b1bb3d" FOREIGN KEY ("globalRoleId") REFERENCES "${tablePrefix}role" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION)`,
|
||||||
);
|
);
|
||||||
|
@ -18,8 +14,6 @@ export class AddUserSettings1652367743993 implements ReversibleMigration {
|
||||||
await queryRunner.query(
|
await queryRunner.query(
|
||||||
`CREATE UNIQUE INDEX "UQ_${tablePrefix}e12875dfb3b1d92d7d7c5377e2" ON "${tablePrefix}user" ("email")`,
|
`CREATE UNIQUE INDEX "UQ_${tablePrefix}e12875dfb3b1d92d7d7c5377e2" ON "${tablePrefix}user" ("email")`,
|
||||||
);
|
);
|
||||||
|
|
||||||
await queryRunner.query('PRAGMA foreign_keys=ON');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async down({ queryRunner, tablePrefix }: MigrationContext) {
|
async down({ queryRunner, tablePrefix }: MigrationContext) {
|
||||||
|
|
|
@ -1,11 +1,7 @@
|
||||||
import type { MigrationContext, ReversibleMigration } from '@db/types';
|
import type { MigrationContext, ReversibleMigration } from '@db/types';
|
||||||
|
|
||||||
export class AddAPIKeyColumn1652905585850 implements ReversibleMigration {
|
export class AddAPIKeyColumn1652905585850 implements ReversibleMigration {
|
||||||
transaction = false as const;
|
|
||||||
|
|
||||||
async up({ queryRunner, tablePrefix }: MigrationContext) {
|
async up({ queryRunner, tablePrefix }: MigrationContext) {
|
||||||
await queryRunner.query('PRAGMA foreign_keys=OFF');
|
|
||||||
|
|
||||||
await queryRunner.query(
|
await queryRunner.query(
|
||||||
`CREATE TABLE "temporary_user" ("id" varchar PRIMARY KEY NOT NULL, "email" varchar(255), "firstName" varchar(32), "lastName" varchar(32), "password" varchar, "resetPasswordToken" varchar, "resetPasswordTokenExpiration" integer DEFAULT NULL, "personalizationAnswers" text, "createdAt" datetime(3) NOT NULL DEFAULT (STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW')), "updatedAt" datetime(3) NOT NULL DEFAULT (STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW')), "globalRoleId" integer NOT NULL, "settings" text, "apiKey" varchar, CONSTRAINT "FK_${tablePrefix}f0609be844f9200ff4365b1bb3d" FOREIGN KEY ("globalRoleId") REFERENCES "${tablePrefix}role" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION)`,
|
`CREATE TABLE "temporary_user" ("id" varchar PRIMARY KEY NOT NULL, "email" varchar(255), "firstName" varchar(32), "lastName" varchar(32), "password" varchar, "resetPasswordToken" varchar, "resetPasswordTokenExpiration" integer DEFAULT NULL, "personalizationAnswers" text, "createdAt" datetime(3) NOT NULL DEFAULT (STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW')), "updatedAt" datetime(3) NOT NULL DEFAULT (STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW')), "globalRoleId" integer NOT NULL, "settings" text, "apiKey" varchar, CONSTRAINT "FK_${tablePrefix}f0609be844f9200ff4365b1bb3d" FOREIGN KEY ("globalRoleId") REFERENCES "${tablePrefix}role" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION)`,
|
||||||
);
|
);
|
||||||
|
@ -21,8 +17,6 @@ export class AddAPIKeyColumn1652905585850 implements ReversibleMigration {
|
||||||
await queryRunner.query(
|
await queryRunner.query(
|
||||||
`CREATE UNIQUE INDEX "UQ_${tablePrefix}ie0zomxves9w3p774drfrkxtj5" ON "${tablePrefix}user" ("apiKey")`,
|
`CREATE UNIQUE INDEX "UQ_${tablePrefix}ie0zomxves9w3p774drfrkxtj5" ON "${tablePrefix}user" ("apiKey")`,
|
||||||
);
|
);
|
||||||
|
|
||||||
await queryRunner.query('PRAGMA foreign_keys=ON');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async down({ queryRunner, tablePrefix }: MigrationContext) {
|
async down({ queryRunner, tablePrefix }: MigrationContext) {
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
import type { MigrationContext, ReversibleMigration } from '@db/types';
|
import type { MigrationContext, ReversibleMigration } from '@db/types';
|
||||||
|
|
||||||
export class DeleteExecutionsWithWorkflows1673268682475 implements ReversibleMigration {
|
export class DeleteExecutionsWithWorkflows1673268682475 implements ReversibleMigration {
|
||||||
transaction = false as const;
|
|
||||||
|
|
||||||
async up({ queryRunner, tablePrefix }: MigrationContext) {
|
async up({ queryRunner, tablePrefix }: MigrationContext) {
|
||||||
const workflowIds = (await queryRunner.query(`
|
const workflowIds = (await queryRunner.query(`
|
||||||
SELECT id FROM "${tablePrefix}workflow_entity"
|
SELECT id FROM "${tablePrefix}workflow_entity"
|
||||||
|
@ -18,8 +16,6 @@ export class DeleteExecutionsWithWorkflows1673268682475 implements ReversibleMig
|
||||||
}`,
|
}`,
|
||||||
);
|
);
|
||||||
|
|
||||||
await queryRunner.query('PRAGMA foreign_keys=OFF');
|
|
||||||
|
|
||||||
await queryRunner.query(`DROP TABLE IF EXISTS "${tablePrefix}temporary_execution_entity"`);
|
await queryRunner.query(`DROP TABLE IF EXISTS "${tablePrefix}temporary_execution_entity"`);
|
||||||
await queryRunner.query(
|
await queryRunner.query(
|
||||||
`CREATE TABLE "${tablePrefix}temporary_execution_entity" (
|
`CREATE TABLE "${tablePrefix}temporary_execution_entity" (
|
||||||
|
@ -53,8 +49,6 @@ export class DeleteExecutionsWithWorkflows1673268682475 implements ReversibleMig
|
||||||
await queryRunner.query(
|
await queryRunner.query(
|
||||||
`CREATE INDEX "IDX_${tablePrefix}ca4a71b47f28ac6ea88293a8e2" ON "${tablePrefix}execution_entity" ("waitTill")`,
|
`CREATE INDEX "IDX_${tablePrefix}ca4a71b47f28ac6ea88293a8e2" ON "${tablePrefix}execution_entity" ("waitTill")`,
|
||||||
);
|
);
|
||||||
|
|
||||||
await queryRunner.query('PRAGMA foreign_keys=ON');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async down({ queryRunner, tablePrefix }: MigrationContext) {
|
async down({ queryRunner, tablePrefix }: MigrationContext) {
|
||||||
|
|
|
@ -1,11 +1,7 @@
|
||||||
import type { MigrationContext, ReversibleMigration } from '@db/types';
|
import type { MigrationContext, IrreversibleMigration } from '@db/types';
|
||||||
|
|
||||||
export class MigrateIntegerKeysToString1690000000002 implements ReversibleMigration {
|
|
||||||
transaction = false as const;
|
|
||||||
|
|
||||||
|
export class MigrateIntegerKeysToString1690000000002 implements IrreversibleMigration {
|
||||||
async up({ queryRunner, tablePrefix }: MigrationContext) {
|
async up({ queryRunner, tablePrefix }: MigrationContext) {
|
||||||
await queryRunner.query('PRAGMA foreign_keys=OFF');
|
|
||||||
await queryRunner.startTransaction();
|
|
||||||
await queryRunner.query(`
|
await queryRunner.query(`
|
||||||
CREATE TABLE "${tablePrefix}TMP_workflow_entity" ("id" varchar(36) PRIMARY KEY NOT NULL, "name" varchar(128) NOT NULL, "active" boolean NOT NULL, "nodes" text, "connections" text NOT NULL, "createdAt" datetime(3) NOT NULL DEFAULT (STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW')), "updatedAt" datetime(3) NOT NULL DEFAULT (STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW')), "settings" text, "staticData" text, "pinData" text, "versionId" varchar(36), "triggerCount" integer NOT NULL DEFAULT 0);`);
|
CREATE TABLE "${tablePrefix}TMP_workflow_entity" ("id" varchar(36) PRIMARY KEY NOT NULL, "name" varchar(128) NOT NULL, "active" boolean NOT NULL, "nodes" text, "connections" text NOT NULL, "createdAt" datetime(3) NOT NULL DEFAULT (STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW')), "updatedAt" datetime(3) NOT NULL DEFAULT (STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW')), "settings" text, "staticData" text, "pinData" text, "versionId" varchar(36), "triggerCount" integer NOT NULL DEFAULT 0);`);
|
||||||
await queryRunner.query(
|
await queryRunner.query(
|
||||||
|
@ -174,12 +170,8 @@ CREATE TABLE "${tablePrefix}TMP_workflows_tags" ("workflowId" varchar(36) NOT NU
|
||||||
await queryRunner.query(
|
await queryRunner.query(
|
||||||
`ALTER TABLE "${tablePrefix}TMP_variables" RENAME TO "${tablePrefix}variables";`,
|
`ALTER TABLE "${tablePrefix}TMP_variables" RENAME TO "${tablePrefix}variables";`,
|
||||||
);
|
);
|
||||||
await queryRunner.query(`CREATE UNIQUE INDEX "idx_${tablePrefix}variables_key" ON "${tablePrefix}variables" ("key");
|
await queryRunner.query(
|
||||||
`);
|
`CREATE UNIQUE INDEX "idx_${tablePrefix}variables_key" ON "${tablePrefix}variables" ("key")`,
|
||||||
await queryRunner.commitTransaction();
|
);
|
||||||
await queryRunner.query('PRAGMA foreign_keys=ON');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-empty-function, @typescript-eslint/no-unused-vars
|
|
||||||
async down({ queryRunner, tablePrefix }: MigrationContext) {}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,6 @@ type MigrationFn = (ctx: MigrationContext) => Promise<void>;
|
||||||
export interface ReversibleMigration {
|
export interface ReversibleMigration {
|
||||||
up: MigrationFn;
|
up: MigrationFn;
|
||||||
down: MigrationFn;
|
down: MigrationFn;
|
||||||
transaction?: false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IrreversibleMigration {
|
export interface IrreversibleMigration {
|
||||||
|
|
31
patches/typeorm@0.3.12.patch
Normal file
31
patches/typeorm@0.3.12.patch
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
diff --git a/migration/MigrationExecutor.js b/migration/MigrationExecutor.js
|
||||||
|
index 5d37b9cf9ca2505242f05160f05ff683e00c1e5d..4a768819f86b8f176bd3b826a649afe54ab39598 100644
|
||||||
|
--- a/migration/MigrationExecutor.js
|
||||||
|
+++ b/migration/MigrationExecutor.js
|
||||||
|
@@ -216,15 +216,17 @@ class MigrationExecutor {
|
||||||
|
// nothing else needs to be done, continue to next migration
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
+ await queryRunner.beforeMigration();
|
||||||
|
if (migration.transaction && !queryRunner.isTransactionActive) {
|
||||||
|
await queryRunner.startTransaction();
|
||||||
|
transactionStartedByUs = true;
|
||||||
|
}
|
||||||
|
await migration
|
||||||
|
.instance.up(queryRunner)
|
||||||
|
- .catch((error) => {
|
||||||
|
+ .catch(async (error) => {
|
||||||
|
// informative log about migration failure
|
||||||
|
this.connection.logger.logMigration(`Migration "${migration.name}" failed, error: ${error === null || error === void 0 ? void 0 : error.message}`);
|
||||||
|
+ await queryRunner.afterMigration(queryRunner);
|
||||||
|
throw error;
|
||||||
|
})
|
||||||
|
.then(async () => {
|
||||||
|
@@ -233,6 +235,7 @@ class MigrationExecutor {
|
||||||
|
// commit transaction if we started it
|
||||||
|
if (migration.transaction && transactionStartedByUs)
|
||||||
|
await queryRunner.commitTransaction();
|
||||||
|
+ await queryRunner.afterMigration(queryRunner);
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
// informative log about migration success
|
|
@ -38,6 +38,9 @@ patchedDependencies:
|
||||||
typedi@0.10.0:
|
typedi@0.10.0:
|
||||||
hash: 62r6bc2crgimafeyruodhqlgo4
|
hash: 62r6bc2crgimafeyruodhqlgo4
|
||||||
path: patches/typedi@0.10.0.patch
|
path: patches/typedi@0.10.0.patch
|
||||||
|
typeorm@0.3.12:
|
||||||
|
hash: yav7zi22hnry26k2lwg6jcumde
|
||||||
|
path: patches/typeorm@0.3.12.patch
|
||||||
|
|
||||||
importers:
|
importers:
|
||||||
|
|
||||||
|
@ -439,7 +442,7 @@ importers:
|
||||||
version: 0.10.0(patch_hash=62r6bc2crgimafeyruodhqlgo4)
|
version: 0.10.0(patch_hash=62r6bc2crgimafeyruodhqlgo4)
|
||||||
typeorm:
|
typeorm:
|
||||||
specifier: ^0.3.12
|
specifier: ^0.3.12
|
||||||
version: 0.3.12(ioredis@5.2.4)(mysql2@2.3.3)(pg@8.8.0)(sqlite3@5.1.6)
|
version: 0.3.12(patch_hash=yav7zi22hnry26k2lwg6jcumde)(ioredis@5.2.4)(mysql2@2.3.3)(pg@8.8.0)(sqlite3@5.1.6)
|
||||||
uuid:
|
uuid:
|
||||||
specifier: ^8.3.2
|
specifier: ^8.3.2
|
||||||
version: 8.3.2
|
version: 8.3.2
|
||||||
|
@ -21385,7 +21388,7 @@ packages:
|
||||||
dev: false
|
dev: false
|
||||||
patched: true
|
patched: true
|
||||||
|
|
||||||
/typeorm@0.3.12(ioredis@5.2.4)(mysql2@2.3.3)(pg@8.8.0)(sqlite3@5.1.6):
|
/typeorm@0.3.12(patch_hash=yav7zi22hnry26k2lwg6jcumde)(ioredis@5.2.4)(mysql2@2.3.3)(pg@8.8.0)(sqlite3@5.1.6):
|
||||||
resolution: {integrity: sha512-sYSxBmCf1nJLLTcYtwqZ+lQIRtLPyUoO93rHTOKk9vJCyT4UfRtU7oRsJvfvKP3nnZTD1hzz2SEy2zwPEN6OyA==}
|
resolution: {integrity: sha512-sYSxBmCf1nJLLTcYtwqZ+lQIRtLPyUoO93rHTOKk9vJCyT4UfRtU7oRsJvfvKP3nnZTD1hzz2SEy2zwPEN6OyA==}
|
||||||
engines: {node: '>= 12.9.0'}
|
engines: {node: '>= 12.9.0'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
@ -21467,6 +21470,7 @@ packages:
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
dev: false
|
dev: false
|
||||||
|
patched: true
|
||||||
|
|
||||||
/typescript@5.1.3:
|
/typescript@5.1.3:
|
||||||
resolution: {integrity: sha512-XH627E9vkeqhlZFQuL+UsyAXEnibT0kWR2FWONlr4sTjvxyJYnyefgrkyECLzM5NenmKzRAy2rR/OlYLA1HkZw==}
|
resolution: {integrity: sha512-XH627E9vkeqhlZFQuL+UsyAXEnibT0kWR2FWONlr4sTjvxyJYnyefgrkyECLzM5NenmKzRAy2rR/OlYLA1HkZw==}
|
||||||
|
|
Loading…
Reference in a new issue