From ef1705bdd133b0f649d04c1c1ead5cfb4818ee90 Mon Sep 17 00:00:00 2001 From: Dan Schultz Date: Wed, 13 Jul 2022 12:47:49 -0400 Subject: [PATCH] Check table exists in current search path * If a `migrations` table did exist in a schema outside of the search path, `doesTableExist` would return true but the migration would then fail with 'relation "migrations" does not exist' * See https://dba.stackexchange.com/a/86098 for the details on the query * This new query makes it possible to have one `migrations` table per schema Issue #92 Migrations can't be run on multiple schemas in the same database Co-authored-by: Remi Koenig --- .../fixtures/multiple-schemas-2/1_another.sql | 3 ++ .../fixtures/multiple-schemas/1_success.sql | 3 ++ src/__tests__/migrate.ts | 37 +++++++++++++++++++ src/migrate.ts | 15 ++++---- 4 files changed, 50 insertions(+), 8 deletions(-) create mode 100644 src/__tests__/fixtures/multiple-schemas-2/1_another.sql create mode 100644 src/__tests__/fixtures/multiple-schemas/1_success.sql diff --git a/src/__tests__/fixtures/multiple-schemas-2/1_another.sql b/src/__tests__/fixtures/multiple-schemas-2/1_another.sql new file mode 100644 index 0000000..20ef1db --- /dev/null +++ b/src/__tests__/fixtures/multiple-schemas-2/1_another.sql @@ -0,0 +1,3 @@ +CREATE TABLE another ( + id integer +); diff --git a/src/__tests__/fixtures/multiple-schemas/1_success.sql b/src/__tests__/fixtures/multiple-schemas/1_success.sql new file mode 100644 index 0000000..598172f --- /dev/null +++ b/src/__tests__/fixtures/multiple-schemas/1_success.sql @@ -0,0 +1,3 @@ +CREATE TABLE success ( + id integer +); diff --git a/src/__tests__/migrate.ts b/src/__tests__/migrate.ts index 0503395..bd1f757 100644 --- a/src/__tests__/migrate.ts +++ b/src/__tests__/migrate.ts @@ -75,6 +75,35 @@ test("concurrent migrations - index concurrently", async (t) => { t.truthy(exists) }) +test("multiple schemas", async (t) => { + const databaseName = "migration-test-multiple-schemas" + const dbConfig = { + database: databaseName, + user: "postgres", + password: PASSWORD, + host: "localhost", + port, + } + + await createDb(databaseName, dbConfig) + const client = new pg.Client(dbConfig) + await client.connect() + + await createSchema(client, "schema_1") + await useSchema(client, "schema_1") + await migrate({ client }, "src/__tests__/fixtures/multiple-schemas") + const firstMigrationSucceeded = await doesTableExist(dbConfig, "success") + t.truthy(firstMigrationSucceeded) + + await createSchema(client, "schema_2") + await useSchema(client, "schema_2") + await migrate({ client }, "src/__tests__/fixtures/multiple-schemas-2") + const secondMigrationSucceeded = await doesTableExist(dbConfig, "another") + t.truthy(secondMigrationSucceeded) + + await client.end() +}) + // can't test with unconnected client because `pg` just hangs on the first query... test("with connected client", async (t) => { const databaseName = "migration-test-with-connected-client" @@ -720,3 +749,11 @@ function doesTableExist(dbConfig: pg.ClientConfig, tableName: string) { } }) } + +async function createSchema(client: pg.Client, schemaName: string): Promise { + await client.query(`CREATE SCHEMA ${schemaName};`) +} + +async function useSchema(client: pg.Client, schemaName: string): Promise { + await client.query(`SET search_path TO ${schemaName};`) +} diff --git a/src/migrate.ts b/src/migrate.ts index e00807b..966329c 100644 --- a/src/migrate.ts +++ b/src/migrate.ts @@ -200,14 +200,13 @@ function logResult(completedMigrations: Array, log: Logger) { } } -/** Check whether table exists in postgres - http://stackoverflow.com/a/24089729 */ +/** Check whether table exists in postgres in the current search path + * http://stackoverflow.com/a/24089729 + * https://dba.stackexchange.com/a/86098 + */ async function doesTableExist(client: BasicPgClient, tableName: string) { - const result = await client.query(SQL`SELECT EXISTS ( - SELECT 1 - FROM pg_catalog.pg_class c - WHERE c.relname = ${tableName} - AND c.relkind = 'r' -);`) - + const result = await client.query( + SQL`SELECT to_regclass(${tableName}) is not NULL as exists;`, + ) return result.rows.length > 0 && result.rows[0].exists }