Skip to content

Commit

Permalink
Check table exists in current search path
Browse files Browse the repository at this point in the history
* 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 ThomWright#92 Migrations can't be run on multiple schemas in the same database

Co-authored-by: Remi Koenig <r.koenig@agricool.co>
  • Loading branch information
slifty and Remi Koenig committed Jul 13, 2022
1 parent dbfc5cc commit ef1705b
Show file tree
Hide file tree
Showing 4 changed files with 50 additions and 8 deletions.
3 changes: 3 additions & 0 deletions src/__tests__/fixtures/multiple-schemas-2/1_another.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
CREATE TABLE another (
id integer
);
3 changes: 3 additions & 0 deletions src/__tests__/fixtures/multiple-schemas/1_success.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
CREATE TABLE success (
id integer
);
37 changes: 37 additions & 0 deletions src/__tests__/migrate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -720,3 +749,11 @@ function doesTableExist(dbConfig: pg.ClientConfig, tableName: string) {
}
})
}

async function createSchema(client: pg.Client, schemaName: string): Promise<void> {
await client.query(`CREATE SCHEMA ${schemaName};`)
}

async function useSchema(client: pg.Client, schemaName: string): Promise<void> {
await client.query(`SET search_path TO ${schemaName};`)
}
15 changes: 7 additions & 8 deletions src/migrate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -200,14 +200,13 @@ function logResult(completedMigrations: Array<Migration>, 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
}

0 comments on commit ef1705b

Please sign in to comment.