Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Split queries out of multi-command string #82

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,24 @@ Running in a transaction ensures each migration is atomic. Either it completes s

An exception is made when `-- postgres-migrations disable-transaction` is included at the top of the migration file. This allows migrations such as `CREATE INDEX CONCURRENTLY` which cannot be run inside a transaction.

### Each migration is run as a multi-command string

The entire migration file is run as a single command string no matter how many queries there are.

An exception is made when `-- postgres-migrations split-queries` is commented at the top of the migration file. This allows migrations to include multiple query string blocks to be run separately. Each block of queries that start with the comment `-- split-query` will be run as its own command string.

Example: adding values to enums can't be run multi-command strings so rather than adding a single migration per value added the following migration will run each alter statement separately.
```
-- postgres-migrations disable-transaction
-- postgres-migrations split-queries

-- split-query
ALTER TYPE eggs ADD VALUE IF NOT EXISTS 'Fried';

-- split-query
ALTER TYPE eggs ADD VALUE IF NOT EXISTS 'Scrambled';
```

### Abort on errors

If anything fails, the migration in progress is rolled back and an exception is thrown.
Expand Down
8 changes: 8 additions & 0 deletions src/__unit__/run-migration/fixtures/split-queries.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
-- postgres-migrations disable-transaction
-- postgres-migrations split-queries

-- split-query
ALTER TYPE eggs ADD VALUE IF NOT EXISTS 'Fried';

-- split-query
ALTER TYPE eggs ADD VALUE IF NOT EXISTS 'Scrambled';
40 changes: 40 additions & 0 deletions src/__unit__/run-migration/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ const readFile = promisify(fsReadFile)
let normalSqlFile: string
let normalJsFile: string
let noTransactionSqlFile: string
let splitQueriesSqlFile: string

test.before(async () => {
await Promise.all([
Expand All @@ -25,6 +26,12 @@ test.before(async () => {
},
),

readFile(__dirname + "/fixtures/split-queries.sql", "utf8").then(
(contents) => {
splitQueriesSqlFile = contents
},
),

Promise.resolve().then(() => {
normalJsFile = loadSqlFromJs(__dirname + "/fixtures/normal.sql.js")
}),
Expand Down Expand Up @@ -152,3 +159,36 @@ test("does not roll back when there is an error inside a transactiony migration"
)
})
})

test("runs each query separately when instructed", (t) => {
const query = sinon.stub().resolves()
const run = runMigration(migrationTableName, {query})

const migration = buildMigration(splitQueriesSqlFile)
const splitQueries = migration.sql.split("-- split-query")

return run(migration).then(() => {
t.is(query.callCount, 4)
t.is(
query.firstCall.args[0],
splitQueries[0],
"could ignore the first entry (comments only)",
)
t.is(
query.secondCall.args[0],
splitQueries[1],
"should run the first migration",
)
t.is(
query.thirdCall.args[0],
splitQueries[2],
"should run the second migration",
)

t.deepEqual(
query.lastCall.args[0].values,
[migration.id, migration.name, migration.hash],
"should record the running of the migration in the database",
)
})
})
16 changes: 15 additions & 1 deletion src/run-migration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,13 @@ import {Logger, Migration, BasicPgClient} from "./types"
const noop = () => {
//
}

const split = async (migration: Migration, client: BasicPgClient) => {
const queries = migration.sql.split("-- split-query")

await Promise.all(queries.map(async (query: string) => client.query(query)))
}

const insertMigration = async (
migrationTableName: string,
client: BasicPgClient,
Expand All @@ -30,6 +37,9 @@ export const runMigration =
migration.sql.includes("-- postgres-migrations disable-transaction") ===
false

const splitQueries =
migration.sql.includes("-- postgres-migrations split-queries") === false

log(`Running migration in transaction: ${inTransaction}`)

const begin = inTransaction ? () => client.query("START TRANSACTION") : noop
Expand All @@ -38,9 +48,13 @@ export const runMigration =

const cleanup = inTransaction ? () => client.query("ROLLBACK") : noop

const run = splitQueries
? () => client.query(migration.sql)
: () => split(migration, client)

try {
await begin()
await client.query(migration.sql)
await run()
await insertMigration(migrationTableName, client, migration, log)
await end()

Expand Down