From b49d49365398f06376e53d86e6c8c5dc15f67e57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Ovejero?= Date: Mon, 30 May 2022 11:33:17 +0200 Subject: [PATCH] fix(core): Fix migrations on non-public Postgres schema (#3356) * :bug: Fix UM migration * :zap: Account for schema in `search_path` * :fire: Remove unneeded schema refs * :test_tube: Account for alt schema in DB testing * :zap: Add schema to `IncreaseTypeVarcharLimit` * :zap: Set `search_path` in every migration * :zap: Set `search_path` in down migrations --- packages/cli/package.json | 7 +-- .../1587669153312-InitialMigration.ts | 4 ++ .../migrations/1589476000887-WebhookModel.ts | 3 ++ .../1594828256133-CreateIndexStoppedAt.ts | 12 ++++- .../1607431743768-MakeStoppedAtNullable.ts | 3 ++ .../migrations/1611144599516-AddWebhookId.ts | 3 ++ .../1617270242566-CreateTagEntity.ts | 4 ++ .../1620824779533-UniqueWorkflowNames.ts | 4 ++ .../migrations/1626176912946-AddwaitTill.ts | 4 ++ ...1630419189837-UpdateWorkflowCredentials.ts | 4 ++ ...1644422880309-AddExecutionEntityIndexes.ts | 14 +++--- .../1646834195327-IncreaseTypeVarcharLimit.ts | 9 +++- .../1646992772331-CreateUserManagement.ts | 9 ++-- .../1648740597343-LowerCaseUserEmail.ts | 2 + .../1652367743993-AddUserSettings.ts | 3 ++ .../cli/test/integration/shared/testDb.ts | 46 +++++++++++++++---- .../cli/test/integration/shared/types.d.ts | 4 ++ packages/cli/test/integration/shared/utils.ts | 14 +++++- 18 files changed, 124 insertions(+), 25 deletions(-) diff --git a/packages/cli/package.json b/packages/cli/package.json index dd6eea0d5b..f7d7e12002 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -32,9 +32,10 @@ "start:default": "cd bin && ./n8n", "start:windows": "cd bin && n8n", "test": "npm run test:sqlite", - "test:sqlite": "export N8N_LOG_LEVEL='silent'; export DB_TYPE=sqlite; jest", - "test:postgres": "export N8N_LOG_LEVEL='silent'; export DB_TYPE=postgresdb && jest", - "test:mysql": "export N8N_LOG_LEVEL='silent'; export DB_TYPE=mysqldb && jest", + "test:sqlite": "export N8N_LOG_LEVEL=silent; export DB_TYPE=sqlite; jest", + "test:postgres": "export N8N_LOG_LEVEL=silent; export DB_TYPE=postgresdb; jest", + "test:postgres:alt-schema": "export DB_POSTGRESDB_SCHEMA=alt_schema; npm run test:postgres", + "test:mysql": "export N8N_LOG_LEVEL=silent; export DB_TYPE=mysqldb; jest", "watch": "tsc --watch", "typeorm": "ts-node ../../node_modules/typeorm/cli.js" }, diff --git a/packages/cli/src/databases/postgresdb/migrations/1587669153312-InitialMigration.ts b/packages/cli/src/databases/postgresdb/migrations/1587669153312-InitialMigration.ts index 4f2bb8ce64..85c1050be2 100644 --- a/packages/cli/src/databases/postgresdb/migrations/1587669153312-InitialMigration.ts +++ b/packages/cli/src/databases/postgresdb/migrations/1587669153312-InitialMigration.ts @@ -14,6 +14,8 @@ export class InitialMigration1587669153312 implements MigrationInterface { tablePrefix = schema + '.' + tablePrefix; } + await queryRunner.query(`SET search_path TO ${schema};`); + await queryRunner.query(`CREATE TABLE IF NOT EXISTS ${tablePrefix}credentials_entity ("id" SERIAL NOT NULL, "name" character varying(128) NOT NULL, "data" text NOT NULL, "type" character varying(32) NOT NULL, "nodesAccess" json NOT NULL, "createdAt" TIMESTAMP NOT NULL, "updatedAt" TIMESTAMP NOT NULL, CONSTRAINT PK_${tablePrefixIndex}814c3d3c36e8a27fa8edb761b0e PRIMARY KEY ("id"))`, undefined); await queryRunner.query(`CREATE INDEX IF NOT EXISTS IDX_${tablePrefixIndex}07fde106c0b471d8cc80a64fc8 ON ${tablePrefix}credentials_entity (type) `, undefined); await queryRunner.query(`CREATE TABLE IF NOT EXISTS ${tablePrefix}execution_entity ("id" SERIAL NOT NULL, "data" text NOT NULL, "finished" boolean NOT NULL, "mode" character varying NOT NULL, "retryOf" character varying, "retrySuccessId" character varying, "startedAt" TIMESTAMP NOT NULL, "stoppedAt" TIMESTAMP NOT NULL, "workflowData" json NOT NULL, "workflowId" character varying, CONSTRAINT PK_${tablePrefixIndex}e3e63bbf986767844bbe1166d4e PRIMARY KEY ("id"))`, undefined); @@ -29,6 +31,8 @@ export class InitialMigration1587669153312 implements MigrationInterface { tablePrefix = schema + '.' + tablePrefix; } + await queryRunner.query(`SET search_path TO ${schema};`); + await queryRunner.query(`DROP TABLE ${tablePrefix}workflow_entity`, undefined); await queryRunner.query(`DROP INDEX IDX_${tablePrefixIndex}c4d999a5e90784e8caccf5589d`, undefined); await queryRunner.query(`DROP TABLE ${tablePrefix}execution_entity`, undefined); diff --git a/packages/cli/src/databases/postgresdb/migrations/1589476000887-WebhookModel.ts b/packages/cli/src/databases/postgresdb/migrations/1589476000887-WebhookModel.ts index 0162b78dcf..9596e32196 100644 --- a/packages/cli/src/databases/postgresdb/migrations/1589476000887-WebhookModel.ts +++ b/packages/cli/src/databases/postgresdb/migrations/1589476000887-WebhookModel.ts @@ -16,6 +16,8 @@ export class WebhookModel1589476000887 implements MigrationInterface { tablePrefix = schema + '.' + tablePrefix; } + await queryRunner.query(`SET search_path TO ${schema};`); + await queryRunner.query(`CREATE TABLE IF NOT EXISTS ${tablePrefix}webhook_entity ("workflowId" integer NOT NULL, "webhookPath" character varying NOT NULL, "method" character varying NOT NULL, "node" character varying NOT NULL, CONSTRAINT "PK_${tablePrefixIndex}b21ace2e13596ccd87dc9bf4ea6" PRIMARY KEY ("webhookPath", "method"))`, undefined); } @@ -25,6 +27,7 @@ export class WebhookModel1589476000887 implements MigrationInterface { if (schema) { tablePrefix = schema + '.' + tablePrefix; } + await queryRunner.query(`SET search_path TO ${schema};`); await queryRunner.query(`DROP TABLE ${tablePrefix}webhook_entity`, undefined); } diff --git a/packages/cli/src/databases/postgresdb/migrations/1594828256133-CreateIndexStoppedAt.ts b/packages/cli/src/databases/postgresdb/migrations/1594828256133-CreateIndexStoppedAt.ts index 3874ee03d0..6c0106c41f 100644 --- a/packages/cli/src/databases/postgresdb/migrations/1594828256133-CreateIndexStoppedAt.ts +++ b/packages/cli/src/databases/postgresdb/migrations/1594828256133-CreateIndexStoppedAt.ts @@ -13,13 +13,21 @@ export class CreateIndexStoppedAt1594828256133 implements MigrationInterface { tablePrefix = schema + '.' + tablePrefix; } + await queryRunner.query(`SET search_path TO ${schema};`); + await queryRunner.query(`CREATE INDEX IF NOT EXISTS IDX_${tablePrefixPure}33228da131bb1112247cf52a42 ON ${tablePrefix}execution_entity ("stoppedAt") `); } async down(queryRunner: QueryRunner): Promise { - const tablePrefix = config.getEnv('database.tablePrefix'); + let tablePrefix = config.getEnv('database.tablePrefix'); - await queryRunner.query(`DROP INDEX IDX_${tablePrefix}33228da131bb1112247cf52a42`); + const tablePrefixPure = tablePrefix; + const schema = config.getEnv('database.postgresdb.schema'); + if (schema) { + tablePrefix = schema + '.' + tablePrefix; + } + await queryRunner.query(`SET search_path TO ${schema};`); + await queryRunner.query(`DROP INDEX IDX_${tablePrefixPure}33228da131bb1112247cf52a42`); } } diff --git a/packages/cli/src/databases/postgresdb/migrations/1607431743768-MakeStoppedAtNullable.ts b/packages/cli/src/databases/postgresdb/migrations/1607431743768-MakeStoppedAtNullable.ts index 0baaf34256..d5ffb1d9af 100644 --- a/packages/cli/src/databases/postgresdb/migrations/1607431743768-MakeStoppedAtNullable.ts +++ b/packages/cli/src/databases/postgresdb/migrations/1607431743768-MakeStoppedAtNullable.ts @@ -11,6 +11,9 @@ export class MakeStoppedAtNullable1607431743768 implements MigrationInterface { if (schema) { tablePrefix = schema + '.' + tablePrefix; } + + await queryRunner.query(`SET search_path TO ${schema};`); + await queryRunner.query('ALTER TABLE ' + tablePrefix + 'execution_entity ALTER COLUMN "stoppedAt" DROP NOT NULL', undefined); } diff --git a/packages/cli/src/databases/postgresdb/migrations/1611144599516-AddWebhookId.ts b/packages/cli/src/databases/postgresdb/migrations/1611144599516-AddWebhookId.ts index a2d4a47baa..0c9a5bb3f1 100644 --- a/packages/cli/src/databases/postgresdb/migrations/1611144599516-AddWebhookId.ts +++ b/packages/cli/src/databases/postgresdb/migrations/1611144599516-AddWebhookId.ts @@ -12,6 +12,8 @@ export class AddWebhookId1611144599516 implements MigrationInterface { tablePrefix = schema + '.' + tablePrefix; } + await queryRunner.query(`SET search_path TO ${schema};`); + await queryRunner.query(`ALTER TABLE ${tablePrefix}webhook_entity ADD "webhookId" character varying`); await queryRunner.query(`ALTER TABLE ${tablePrefix}webhook_entity ADD "pathLength" integer`); await queryRunner.query(`CREATE INDEX IF NOT EXISTS IDX_${tablePrefixPure}16f4436789e804e3e1c9eeb240 ON ${tablePrefix}webhook_entity ("webhookId", "method", "pathLength") `); @@ -24,6 +26,7 @@ export class AddWebhookId1611144599516 implements MigrationInterface { if (schema) { tablePrefix = schema + '.' + tablePrefix; } + await queryRunner.query(`SET search_path TO ${schema};`); await queryRunner.query(`DROP INDEX IDX_${tablePrefixPure}16f4436789e804e3e1c9eeb240`); await queryRunner.query(`ALTER TABLE ${tablePrefix}webhook_entity DROP COLUMN "pathLength"`); diff --git a/packages/cli/src/databases/postgresdb/migrations/1617270242566-CreateTagEntity.ts b/packages/cli/src/databases/postgresdb/migrations/1617270242566-CreateTagEntity.ts index d5f2246d37..737a27cf02 100644 --- a/packages/cli/src/databases/postgresdb/migrations/1617270242566-CreateTagEntity.ts +++ b/packages/cli/src/databases/postgresdb/migrations/1617270242566-CreateTagEntity.ts @@ -12,6 +12,8 @@ export class CreateTagEntity1617270242566 implements MigrationInterface { tablePrefix = schema + '.' + tablePrefix; } + await queryRunner.query(`SET search_path TO ${schema};`); + // create tags table + relationship with workflow entity await queryRunner.query(`CREATE TABLE ${tablePrefix}tag_entity ("id" SERIAL NOT NULL, "name" character varying(24) NOT NULL, "createdAt" TIMESTAMP NOT NULL, "updatedAt" TIMESTAMP NOT NULL, CONSTRAINT "PK_${tablePrefixPure}7a50a9b74ae6855c0dcaee25052" PRIMARY KEY ("id"))`); @@ -47,6 +49,8 @@ export class CreateTagEntity1617270242566 implements MigrationInterface { tablePrefix = schema + '.' + tablePrefix; } + await queryRunner.query(`SET search_path TO ${schema};`); + // `createdAt` and `updatedAt` await queryRunner.query(`ALTER TABLE ${tablePrefix}workflow_entity ALTER COLUMN "updatedAt" DROP DEFAULT`); diff --git a/packages/cli/src/databases/postgresdb/migrations/1620824779533-UniqueWorkflowNames.ts b/packages/cli/src/databases/postgresdb/migrations/1620824779533-UniqueWorkflowNames.ts index e57a7adf6b..40f7451a5a 100644 --- a/packages/cli/src/databases/postgresdb/migrations/1620824779533-UniqueWorkflowNames.ts +++ b/packages/cli/src/databases/postgresdb/migrations/1620824779533-UniqueWorkflowNames.ts @@ -12,6 +12,8 @@ export class UniqueWorkflowNames1620824779533 implements MigrationInterface { tablePrefix = schema + '.' + tablePrefix; } + await queryRunner.query(`SET search_path TO ${schema};`); + const workflowNames = await queryRunner.query(` SELECT name FROM ${tablePrefix}workflow_entity @@ -65,6 +67,8 @@ export class UniqueWorkflowNames1620824779533 implements MigrationInterface { tablePrefix = schema + '.' + tablePrefix; } + await queryRunner.query(`SET search_path TO ${schema};`); + await queryRunner.query(`DROP INDEX "IDX_${tablePrefixPure}a252c527c4c89237221fe2c0ab"`); } } diff --git a/packages/cli/src/databases/postgresdb/migrations/1626176912946-AddwaitTill.ts b/packages/cli/src/databases/postgresdb/migrations/1626176912946-AddwaitTill.ts index 22414ff0df..81abce11fd 100644 --- a/packages/cli/src/databases/postgresdb/migrations/1626176912946-AddwaitTill.ts +++ b/packages/cli/src/databases/postgresdb/migrations/1626176912946-AddwaitTill.ts @@ -12,6 +12,8 @@ export class AddwaitTill1626176912946 implements MigrationInterface { tablePrefix = schema + '.' + tablePrefix; } + await queryRunner.query(`SET search_path TO ${schema};`); + await queryRunner.query(`ALTER TABLE ${tablePrefix}execution_entity ADD "waitTill" TIMESTAMP`); await queryRunner.query(`CREATE INDEX IF NOT EXISTS IDX_${tablePrefixPure}ca4a71b47f28ac6ea88293a8e2 ON ${tablePrefix}execution_entity ("waitTill")`); } @@ -24,6 +26,8 @@ export class AddwaitTill1626176912946 implements MigrationInterface { tablePrefix = schema + '.' + tablePrefix; } + await queryRunner.query(`SET search_path TO ${schema};`); + await queryRunner.query(`DROP INDEX IDX_${tablePrefixPure}ca4a71b47f28ac6ea88293a8e2`); await queryRunner.query(`ALTER TABLE ${tablePrefix}webhook_entity DROP COLUMN "waitTill"`); } diff --git a/packages/cli/src/databases/postgresdb/migrations/1630419189837-UpdateWorkflowCredentials.ts b/packages/cli/src/databases/postgresdb/migrations/1630419189837-UpdateWorkflowCredentials.ts index df58994d25..9edb94cebc 100644 --- a/packages/cli/src/databases/postgresdb/migrations/1630419189837-UpdateWorkflowCredentials.ts +++ b/packages/cli/src/databases/postgresdb/migrations/1630419189837-UpdateWorkflowCredentials.ts @@ -14,6 +14,9 @@ export class UpdateWorkflowCredentials1630419189837 implements MigrationInterfac if (schema) { tablePrefix = schema + '.' + tablePrefix; } + + await queryRunner.query(`SET search_path TO ${schema};`); + const helpers = new MigrationHelpers(queryRunner); const credentialsEntities = await queryRunner.query(` @@ -157,6 +160,7 @@ export class UpdateWorkflowCredentials1630419189837 implements MigrationInterfac if (schema) { tablePrefix = schema + '.' + tablePrefix; } + await queryRunner.query(`SET search_path TO ${schema};`); const helpers = new MigrationHelpers(queryRunner); const credentialsEntities = await queryRunner.query(` diff --git a/packages/cli/src/databases/postgresdb/migrations/1644422880309-AddExecutionEntityIndexes.ts b/packages/cli/src/databases/postgresdb/migrations/1644422880309-AddExecutionEntityIndexes.ts index 777609a366..9fd2ea87fe 100644 --- a/packages/cli/src/databases/postgresdb/migrations/1644422880309-AddExecutionEntityIndexes.ts +++ b/packages/cli/src/databases/postgresdb/migrations/1644422880309-AddExecutionEntityIndexes.ts @@ -13,6 +13,8 @@ export class AddExecutionEntityIndexes1644422880309 implements MigrationInterfac tablePrefix = schema + '.' + tablePrefix; } + await queryRunner.query(`SET search_path TO ${schema};`); + await queryRunner.query( `DROP INDEX IF EXISTS "${schema}".IDX_${tablePrefixPure}c4d999a5e90784e8caccf5589d`, ); @@ -49,22 +51,22 @@ export class AddExecutionEntityIndexes1644422880309 implements MigrationInterfac } await queryRunner.query( - `DROP INDEX "${schema}"."IDX_${tablePrefixPure}d160d4771aba5a0d78943edbe3"`, + `DROP INDEX "IDX_${tablePrefixPure}d160d4771aba5a0d78943edbe3"`, ); await queryRunner.query( - `DROP INDEX "${schema}"."IDX_${tablePrefixPure}85b981df7b444f905f8bf50747"`, + `DROP INDEX "IDX_${tablePrefixPure}85b981df7b444f905f8bf50747"`, ); await queryRunner.query( - `DROP INDEX "${schema}"."IDX_${tablePrefixPure}72ffaaab9f04c2c1f1ea86e662"`, + `DROP INDEX "IDX_${tablePrefixPure}72ffaaab9f04c2c1f1ea86e662"`, ); await queryRunner.query( - `DROP INDEX "${schema}"."IDX_${tablePrefixPure}4f474ac92be81610439aaad61e"`, + `DROP INDEX "IDX_${tablePrefixPure}4f474ac92be81610439aaad61e"`, ); await queryRunner.query( - `DROP INDEX "${schema}"."IDX_${tablePrefixPure}58154df94c686818c99fb754ce"`, + `DROP INDEX "IDX_${tablePrefixPure}58154df94c686818c99fb754ce"`, ); await queryRunner.query( - `DROP INDEX "${schema}"."IDX_${tablePrefixPure}33228da131bb1112247cf52a42"`, + `DROP INDEX "IDX_${tablePrefixPure}33228da131bb1112247cf52a42"`, ); await queryRunner.query( `CREATE INDEX "IDX_${tablePrefixPure}ca4a71b47f28ac6ea88293a8e2" ON ${tablePrefix}execution_entity ("waitTill") `, diff --git a/packages/cli/src/databases/postgresdb/migrations/1646834195327-IncreaseTypeVarcharLimit.ts b/packages/cli/src/databases/postgresdb/migrations/1646834195327-IncreaseTypeVarcharLimit.ts index c09ce161d1..5fb2b04cb3 100644 --- a/packages/cli/src/databases/postgresdb/migrations/1646834195327-IncreaseTypeVarcharLimit.ts +++ b/packages/cli/src/databases/postgresdb/migrations/1646834195327-IncreaseTypeVarcharLimit.ts @@ -9,7 +9,14 @@ export class IncreaseTypeVarcharLimit1646834195327 implements MigrationInterface name = 'IncreaseTypeVarcharLimit1646834195327'; async up(queryRunner: QueryRunner): Promise { - const tablePrefix = config.getEnv('database.tablePrefix'); + let tablePrefix = config.getEnv('database.tablePrefix'); + const schema = config.getEnv('database.postgresdb.schema'); + if (schema) { + tablePrefix = schema + '.' + tablePrefix; + } + + await queryRunner.query(`SET search_path TO ${schema};`); + await queryRunner.query(`ALTER TABLE ${tablePrefix}credentials_entity ALTER COLUMN "type" TYPE VARCHAR(128)`); } diff --git a/packages/cli/src/databases/postgresdb/migrations/1646992772331-CreateUserManagement.ts b/packages/cli/src/databases/postgresdb/migrations/1646992772331-CreateUserManagement.ts index 109c7476e7..562f3e0ee6 100644 --- a/packages/cli/src/databases/postgresdb/migrations/1646992772331-CreateUserManagement.ts +++ b/packages/cli/src/databases/postgresdb/migrations/1646992772331-CreateUserManagement.ts @@ -14,6 +14,8 @@ export class CreateUserManagement1646992772331 implements MigrationInterface { tablePrefix = schema + '.' + tablePrefix; } + await queryRunner.query(`SET search_path TO ${schema};`); + await queryRunner.query( `CREATE TABLE ${tablePrefix}role ( "id" serial NOT NULL, @@ -56,12 +58,12 @@ export class CreateUserManagement1646992772331 implements MigrationInterface { CONSTRAINT "FK_${tablePrefixPure}3540da03964527aa24ae014b780" FOREIGN KEY ("roleId") REFERENCES ${tablePrefix}role ("id") ON DELETE NO ACTION ON UPDATE NO ACTION, CONSTRAINT "FK_${tablePrefixPure}82b2fd9ec4e3e24209af8160282" FOREIGN KEY ("userId") REFERENCES ${tablePrefix}user ("id") ON DELETE CASCADE ON UPDATE NO ACTION, CONSTRAINT "FK_${tablePrefixPure}b83f8d2530884b66a9c848c8b88" FOREIGN KEY ("workflowId") REFERENCES - ${tablePrefixPure}workflow_entity ("id") ON DELETE CASCADE ON UPDATE NO ACTION + ${tablePrefix}workflow_entity ("id") ON DELETE CASCADE ON UPDATE NO ACTION );`, ); await queryRunner.query( - `CREATE INDEX "IDX_${tablePrefixPure}65a0933c0f19d278881653bf81d35064" ON "shared_workflow" ("workflowId");`, + `CREATE INDEX "IDX_${tablePrefixPure}65a0933c0f19d278881653bf81d35064" ON ${tablePrefix}shared_workflow ("workflowId");`, ); await queryRunner.query( @@ -131,7 +133,7 @@ export class CreateUserManagement1646992772331 implements MigrationInterface { ); await queryRunner.query( - `INSERT INTO ${tablePrefix}shared_credentials ("createdAt", "updatedAt", "roleId", "userId", "credentialsId") SELECT NOW(), NOW(), '${credentialOwnerRole[0].insertId}', '${ownerUserId}', "id" FROM ${tablePrefix} credentials_entity`, + `INSERT INTO ${tablePrefix}shared_credentials ("createdAt", "updatedAt", "roleId", "userId", "credentialsId") SELECT NOW(), NOW(), '${credentialOwnerRole[0].insertId}', '${ownerUserId}', "id" FROM ${tablePrefix}credentials_entity`, ); await queryRunner.query( @@ -146,6 +148,7 @@ export class CreateUserManagement1646992772331 implements MigrationInterface { if (schema) { tablePrefix = schema + '.' + tablePrefix; } + await queryRunner.query(`SET search_path TO ${schema};`); await queryRunner.query( `CREATE UNIQUE INDEX "IDX_${tablePrefixPure}a252c527c4c89237221fe2c0ab" ON ${tablePrefix}workflow_entity ("name")`, diff --git a/packages/cli/src/databases/postgresdb/migrations/1648740597343-LowerCaseUserEmail.ts b/packages/cli/src/databases/postgresdb/migrations/1648740597343-LowerCaseUserEmail.ts index 02a12d047f..306bf13ea5 100644 --- a/packages/cli/src/databases/postgresdb/migrations/1648740597343-LowerCaseUserEmail.ts +++ b/packages/cli/src/databases/postgresdb/migrations/1648740597343-LowerCaseUserEmail.ts @@ -11,6 +11,8 @@ export class LowerCaseUserEmail1648740597343 implements MigrationInterface { tablePrefix = schema + '.' + tablePrefix; } + await queryRunner.query(`SET search_path TO ${schema};`); + await queryRunner.query(` UPDATE ${tablePrefix}user SET email = LOWER(email); diff --git a/packages/cli/src/databases/postgresdb/migrations/1652367743993-AddUserSettings.ts b/packages/cli/src/databases/postgresdb/migrations/1652367743993-AddUserSettings.ts index 1f2470d43a..b22a1ec8b8 100644 --- a/packages/cli/src/databases/postgresdb/migrations/1652367743993-AddUserSettings.ts +++ b/packages/cli/src/databases/postgresdb/migrations/1652367743993-AddUserSettings.ts @@ -11,6 +11,8 @@ export class AddUserSettings1652367743993 implements MigrationInterface { tablePrefix = schema + '.' + tablePrefix; } + await queryRunner.query(`SET search_path TO ${schema};`); + await queryRunner.query(`ALTER TABLE ${tablePrefix}user ADD COLUMN settings json`); await queryRunner.query( @@ -24,6 +26,7 @@ export class AddUserSettings1652367743993 implements MigrationInterface { if (schema) { tablePrefix = schema + '.' + tablePrefix; } + await queryRunner.query(`SET search_path TO ${schema};`); await queryRunner.query(`ALTER TABLE ${tablePrefix}user DROP COLUMN settings`); } diff --git a/packages/cli/test/integration/shared/testDb.ts b/packages/cli/test/integration/shared/testDb.ts index e1f5113793..55ad557eca 100644 --- a/packages/cli/test/integration/shared/testDb.ts +++ b/packages/cli/test/integration/shared/testDb.ts @@ -1,3 +1,6 @@ +import { exec as callbackExec } from 'child_process'; +import { promisify } from 'util'; + import { createConnection, getConnection, ConnectionOptions, Connection } from 'typeorm'; import { Credentials, UserSettings } from 'n8n-core'; @@ -12,12 +15,14 @@ import { entities } from '../../../src/databases/entities'; import { mysqlMigrations } from '../../../src/databases/mysqldb/migrations'; import { postgresMigrations } from '../../../src/databases/postgresdb/migrations'; import { sqliteMigrations } from '../../../src/databases/sqlite/migrations'; -import { categorize } from './utils'; +import { categorize, getPostgresSchemaSection } from './utils'; import type { Role } from '../../../src/databases/entities/Role'; import type { User } from '../../../src/databases/entities/User'; import type { CollectionName, CredentialPayload } from './types'; +const exec = promisify(callbackExec); + /** * Initialize one test DB per suite run, with bootstrap connection if needed. */ @@ -35,21 +40,42 @@ export async function init() { if (dbType === 'postgresdb') { let bootstrapPostgres; - const bootstrapPostgresOptions = getBootstrapPostgresOptions(); + const pgOptions = getBootstrapPostgresOptions(); try { - bootstrapPostgres = await createConnection(bootstrapPostgresOptions); + bootstrapPostgres = await createConnection(pgOptions); } catch (error) { - const { username, password, host, port, schema } = bootstrapPostgresOptions; - console.error( - `ERROR: Failed to connect to Postgres default DB 'postgres'.\nPlease review your Postgres connection options:\n\thost: "${host}"\n\tusername: "${username}"\n\tpassword: "${password}"\n\tport: "${port}"\n\tschema: "${schema}"\nFix by setting correct values via environment variables:\n\texport DB_POSTGRESDB_HOST=value\n\texport DB_POSTGRESDB_USER=value\n\texport DB_POSTGRESDB_PASSWORD=value\n\texport DB_POSTGRESDB_PORT=value\n\texport DB_POSTGRESDB_SCHEMA=value`, - ); + const pgConfig = getPostgresSchemaSection(); + + if (!pgConfig) throw new Error("Failed to find config schema section for 'postgresdb'"); + + const message = [ + "ERROR: Failed to connect to Postgres default DB 'postgres'", + 'Please review your Postgres connection options:', + `host: ${pgOptions.host} | port: ${pgOptions.port} | schema: ${pgOptions.schema} | username: ${pgOptions.username} | password: ${pgOptions.password}`, + 'Fix by setting correct values via environment variables:', + `${pgConfig.host.env} | ${pgConfig.port.env} | ${pgConfig.schema.env} | ${pgConfig.user.env} | ${pgConfig.password.env}`, + 'Otherwise, make sure your Postgres server is running.' + ].join('\n'); + + console.error(message); + process.exit(1); } const testDbName = `pg_${randomString(6, 10)}_${Date.now()}_n8n_test`; await bootstrapPostgres.query(`CREATE DATABASE ${testDbName};`); + try { + const schema = config.getEnv('database.postgresdb.schema'); + await exec(`psql -d ${testDbName} -c "CREATE SCHEMA IF NOT EXISTS ${schema}";`); + } catch (error) { + if (error instanceof Error && error.message.includes('command not found')) { + console.error('psql command not found. Make sure psql is installed and added to your PATH.'); + } + process.exit(1); + } + await Db.init(getPostgresOptions({ name: testDbName })); return { testDbName }; @@ -116,8 +142,10 @@ export async function truncate(collections: CollectionName[], testDbName: string if (dbType === 'postgresdb') { return Promise.all( collections.map((collection) => { - const tableName = toTableName(collection); - testDb.query(`TRUNCATE TABLE "${tableName}" RESTART IDENTITY CASCADE;`); + const schema = config.getEnv('database.postgresdb.schema'); + const fullTableName = `${schema}.${toTableName(collection)}`; + + testDb.query(`TRUNCATE TABLE ${fullTableName} RESTART IDENTITY CASCADE;`); }), ); } diff --git a/packages/cli/test/integration/shared/types.d.ts b/packages/cli/test/integration/shared/types.d.ts index bca1c86fbc..6d90bf64cc 100644 --- a/packages/cli/test/integration/shared/types.d.ts +++ b/packages/cli/test/integration/shared/types.d.ts @@ -28,3 +28,7 @@ export type SaveCredentialFunction = ( credentialPayload: CredentialPayload, { user }: { user: User }, ) => Promise; + +export type PostgresSchemaSection = { + [K in 'host' | 'port' | 'schema' | 'user' | 'password']: { env: string }; +}; diff --git a/packages/cli/test/integration/shared/utils.ts b/packages/cli/test/integration/shared/utils.ts index 3d756061d3..021fdc27cb 100644 --- a/packages/cli/test/integration/shared/utils.ts +++ b/packages/cli/test/integration/shared/utils.ts @@ -24,7 +24,7 @@ import { issueJWT } from '../../../src/UserManagement/auth/jwt'; import { getLogger } from '../../../src/Logger'; import { credentialsController } from '../../../src/api/credentials.api'; import type { User } from '../../../src/databases/entities/User'; -import type { EndpointGroup, SmtpTestAccount } from './types'; +import type { EndpointGroup, PostgresSchemaSection, SmtpTestAccount } from './types'; import type { N8nApp } from '../../../src/UserManagement/Interfaces'; import * as UserManagementMailer from '../../../src/UserManagement/email/UserManagementMailer'; @@ -262,3 +262,15 @@ export const categorize = (arr: T[], test: (str: T) => boolean) => { { pass: [], fail: [] }, ); }; + +export function getPostgresSchemaSection( + schema = config.getSchema(), +): PostgresSchemaSection | null { + for (const [key, value] of Object.entries(schema)) { + if (key === 'postgresdb') { + return value._cvtProperties; + } + } + + return null; +}