@blackglory/pg-migrations 0.2.2 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -21,22 +21,40 @@ interface IMigration {
21
21
  }
22
22
  ```
23
23
 
24
+ You may need [migration-files].
25
+
26
+ [migration-files]: https://github.com/BlackGlory/migration-files
27
+
24
28
  ### migrate
25
29
  ```ts
26
30
  function migrate(
27
31
  client: Client
28
32
  , migrations: IMigration[]
29
- , targetVersion = getMaximumVersion(migrations)
30
- , migrationsTable: string = 'migrations'
31
- , advisoryLockKey: bigint = BigInt('-9223372036854775808') // The smallest bigint for postgres
33
+ , options?: {
34
+ targetVersion?: number
35
+ throwOnNewerVersion?: boolean = false
36
+
37
+ migrationsTable?: string = 'migrations'
38
+ advisoryLockKey?: bigint = BigInt('-9223372036854775808') // The smallest bigint for postgres
39
+ }
32
40
  ): Promise<void>
33
41
  ```
34
42
 
35
- ## FAQ
36
- ### Can multiple instances migrate in parallel?
43
+ If `options.targetVersion` is `undefined`,
44
+ the maximum version of the `migrations` is used.
45
+
46
+ When the maximum known migration version is less than the database schema version,
47
+ it means the current instance is outdated.
48
+ - When `options.throwOnNewerVersion` is `false` (default),
49
+ it will skip the migration,
50
+ so your outdated instance continues to run.
51
+ - When `options.throwOnNewerVersion` is `true`,
52
+ it will throw an error,
53
+ so your outdated instance fails immediately.
54
+
55
+ #### Can multiple instances migrate in parallel?
37
56
  Yes, it uses advisory lock to ensure that only one instance is migrating at a time.
38
- When the maximum migration version is less than the database schema version (which means it is an obsolete instance), it will skip the migration.
39
57
 
40
- ### What if my migration requires more than one connection?
58
+ #### What if my migration requires more than one connection?
41
59
  You can get all connection configurations through properties to create a new `pg.Client`.
42
60
  It is important to note that the custom client you create is not part of the migration transaction.
package/lib/index.d.ts ADDED
@@ -0,0 +1 @@
1
+ export * from './migrate.js';
package/lib/index.js ADDED
@@ -0,0 +1,2 @@
1
+ export * from './migrate.js';
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAA"}
@@ -0,0 +1,14 @@
1
+ import type { Client } from 'pg';
2
+ export interface IMigration {
3
+ version: number;
4
+ up: string | ((client: Client) => PromiseLike<void>);
5
+ down: string | ((client: Client) => PromiseLike<void>);
6
+ }
7
+ interface IMigrateOptions {
8
+ targetVersion?: number;
9
+ throwOnNewerVersion?: boolean;
10
+ migrationsTable?: string;
11
+ advisoryLockKey?: bigint;
12
+ }
13
+ export declare function migrate(client: Client, migrations: IMigration[], { targetVersion, throwOnNewerVersion, migrationsTable, advisoryLockKey }?: IMigrateOptions): Promise<void>;
14
+ export {};
package/lib/migrate.js ADDED
@@ -0,0 +1,150 @@
1
+ import { assert, isFunction } from '@blackglory/prelude';
2
+ import { first, isntUndefined, isUndefined, max } from 'extra-utils';
3
+ export async function migrate(client, migrations, { targetVersion = getMaximumVersion(migrations), throwOnNewerVersion = false, migrationsTable = 'migrations', advisoryLockKey = BigInt('-9223372036854775808') } = {}) {
4
+ const maxVersion = getMaximumVersion(migrations);
5
+ await lock(client, advisoryLockKey);
6
+ try {
7
+ await ensureMigrationsTable(client, migrationsTable);
8
+ while (true) {
9
+ const done = await transaction(client, async () => {
10
+ const currentVersion = await getDatabaseVersion(client, migrationsTable);
11
+ if (maxVersion < currentVersion) {
12
+ if (throwOnNewerVersion) {
13
+ throw new Error(`Database version ${currentVersion} is higher than the maximum known migration version.`);
14
+ }
15
+ else {
16
+ return true;
17
+ }
18
+ }
19
+ else {
20
+ if (currentVersion === targetVersion) {
21
+ return true;
22
+ }
23
+ else if (currentVersion < targetVersion) {
24
+ await upgrade();
25
+ return false;
26
+ }
27
+ else {
28
+ await downgrade();
29
+ return false;
30
+ }
31
+ }
32
+ });
33
+ if (done)
34
+ break;
35
+ }
36
+ }
37
+ finally {
38
+ await unlock(client, advisoryLockKey);
39
+ }
40
+ async function upgrade() {
41
+ const currentVersion = await getDatabaseVersion(client, migrationsTable);
42
+ const targetVersion = currentVersion + 1;
43
+ try {
44
+ const migration = migrations.find(x => x.version === targetVersion);
45
+ assert(migration, `Cannot find migration for version ${targetVersion}`);
46
+ if (isFunction(migration.up)) {
47
+ await migration.up(client);
48
+ }
49
+ else {
50
+ await client.query(migration.up);
51
+ }
52
+ await setDatabaseVersion(client, migrationsTable, targetVersion);
53
+ }
54
+ catch (e) {
55
+ throw new Error(`Upgrade from version ${currentVersion} to version ${targetVersion} failed.`, { cause: e });
56
+ }
57
+ }
58
+ async function downgrade() {
59
+ const currentVersion = await getDatabaseVersion(client, migrationsTable);
60
+ const targetVersion = currentVersion - 1;
61
+ try {
62
+ const migration = migrations.find(x => x.version === currentVersion);
63
+ assert(migration, `Cannot find migration for version ${targetVersion}`);
64
+ if (isFunction(migration.down)) {
65
+ await migration.down(client);
66
+ }
67
+ else {
68
+ await client.query(migration.down);
69
+ }
70
+ await setDatabaseVersion(client, migrationsTable, targetVersion);
71
+ }
72
+ catch (e) {
73
+ throw new Error(`Downgrade from version ${currentVersion} to version ${targetVersion} failed.`, { cause: e });
74
+ }
75
+ }
76
+ }
77
+ function getMaximumVersion(migrations) {
78
+ return migrations
79
+ .map(x => x.version)
80
+ .reduce(max, 0);
81
+ }
82
+ async function getDatabaseVersion(client, migrationTable) {
83
+ const version = await tryGetDatabaseVersion(client, migrationTable);
84
+ assert(isntUndefined(version));
85
+ return version;
86
+ }
87
+ async function tryGetDatabaseVersion(client, migrationTable) {
88
+ const result = await client.query(`
89
+ SELECT schema_version
90
+ FROM "${migrationTable}"
91
+ LIMIT 1;
92
+ `);
93
+ return first(result.rows)?.schema_version;
94
+ }
95
+ async function ensureMigrationsTable(client, migrationTable) {
96
+ await transaction(client, async () => {
97
+ await client.query(`
98
+ CREATE TABLE IF NOT EXISTS "${migrationTable}" (
99
+ schema_version INTEGER NOT NULL
100
+ );
101
+ `);
102
+ if (isUndefined(await tryGetDatabaseVersion(client, migrationTable))) {
103
+ await client.query(`
104
+ INSERT INTO "${migrationTable}" (schema_version)
105
+ VALUES (0);
106
+ `);
107
+ }
108
+ });
109
+ }
110
+ async function setDatabaseVersion(client, migrationTable, version) {
111
+ await client.query(`
112
+ UPDATE "${migrationTable}"
113
+ SET schema_version = $1;
114
+ `, [version]);
115
+ }
116
+ async function lock(client, key) {
117
+ await client.query(`
118
+ SELECT pg_advisory_lock(${key});
119
+ `);
120
+ }
121
+ async function unlock(client, key) {
122
+ await client.query(`
123
+ SELECT pg_advisory_unlock(${key});
124
+ `);
125
+ }
126
+ const inTransactionClients = new WeakSet();
127
+ async function transaction(client, fn) {
128
+ if (inTransactionClients.has(client)) {
129
+ throw new Error('PostgreSQL does not support nested transactions.');
130
+ }
131
+ else {
132
+ inTransactionClients.add(client);
133
+ }
134
+ try {
135
+ await client.query('BEGIN');
136
+ try {
137
+ const result = await fn();
138
+ await client.query('COMMIT');
139
+ return result;
140
+ }
141
+ catch (e) {
142
+ await client.query('ROLLBACK');
143
+ throw e;
144
+ }
145
+ }
146
+ finally {
147
+ inTransactionClients.delete(client);
148
+ }
149
+ }
150
+ //# sourceMappingURL=migrate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"migrate.js","sourceRoot":"","sources":["../src/migrate.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blackglory/pg-migrations",
3
- "version": "0.2.2",
3
+ "version": "0.3.0",
4
4
  "description": "A utility for database migrations with pg",
5
5
  "keywords": [
6
6
  "migration",
@@ -11,63 +11,65 @@
11
11
  "sql"
12
12
  ],
13
13
  "files": [
14
- "lib"
14
+ "lib",
15
+ "src"
15
16
  ],
16
- "main": "lib/es2018/index.js",
17
- "types": "lib/es2018/index.d.ts",
17
+ "type": "module",
18
+ "main": "lib/index.js",
19
+ "types": "lib/index.d.ts",
18
20
  "repository": "git@github.com:BlackGlory/pg-migrations.git",
19
21
  "author": "BlackGlory <woshenmedoubuzhidao@blackglory.me>",
20
22
  "license": "MIT",
21
23
  "sideEffects": false,
24
+ "engines": {
25
+ "node": ">=22"
26
+ },
22
27
  "scripts": {
28
+ "prepare": "ts-patch install -s",
23
29
  "lint": "eslint --ext .js,.jsx,.ts,.tsx --quiet src __tests__",
24
- "test": "jest --runInBand --config jest.config.js",
25
- "test:debug": "node --inspect-brk node_modules/.bin/jest --runInBand",
26
- "test:coverage": "jest --coverage --config jest.config.js",
27
- "prepublishOnly": "run-s clean build",
30
+ "test": "vitest run",
31
+ "prepublishOnly": "run-s prepare clean build",
28
32
  "clean": "rimraf lib",
29
- "build": "run-p build:*",
30
- "build:es2015": "run-s build:es2015:*",
31
- "build:es2015:compile": "tsc --project tsconfig.build.json --target es2015 --outDir lib/es2015",
32
- "build:es2015:patch": "tscpaths -p tsconfig.build.json -s ./src -o ./lib/es2015",
33
- "build:es2018": "run-s build:es2018:*",
34
- "build:es2018:compile": "tsc --project tsconfig.build.json --target es2018 --outDir lib/es2018",
35
- "build:es2018:patch": "tscpaths -p tsconfig.build.json -s ./src -o ./lib/es2018",
36
- "docker:test": "run-s docker:test:build docker:test:run; run-s docker:test:clean",
37
- "docker:test:build": "docker-compose --file docker-compose.test.yml build",
38
- "docker:test:run": "docker-compose --file docker-compose.test.yml run --rm sut",
39
- "docker:test:clean": "docker-compose --file docker-compose.test.yml down",
33
+ "build": "tsc --project tsconfig.build.json",
34
+ "docker:test": "run-s docker:test:clean docker:test:build docker:test:run docker:test:clean",
35
+ "docker:test:build": "docker compose --file docker-compose.test.yml build",
36
+ "docker:test:run": "docker compose --file docker-compose.test.yml run --rm test",
37
+ "docker:test:clean": "docker compose --file docker-compose.test.yml down",
40
38
  "release": "standard-version"
41
39
  },
42
40
  "husky": {
43
41
  "hooks": {
44
- "pre-commit": "run-s lint docker:test",
42
+ "pre-commit": "run-s prepare lint build docker:test",
45
43
  "commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
46
44
  }
47
45
  },
48
46
  "devDependencies": {
49
- "@commitlint/cli": "^17.0.3",
50
- "@commitlint/config-conventional": "^17.0.3",
51
- "@types/jest": "^27.4.0",
52
- "@types/pg": "^8.6.5",
53
- "@typescript-eslint/eslint-plugin": "^5.32.0",
54
- "@typescript-eslint/parser": "^5.32.0",
55
- "eslint": "^8.21.0",
47
+ "@commitlint/cli": "^20.4.1",
48
+ "@commitlint/config-conventional": "^20.4.1",
49
+ "@eslint/js": "^10.0.1",
50
+ "@types/pg": "^8.16.0",
51
+ "@typescript-eslint/eslint-plugin": "^8.55.0",
52
+ "@typescript-eslint/parser": "^8.55.0",
53
+ "eslint": "^10.0.0",
56
54
  "husky": "^4.3.0",
57
- "jest": "^27.5.1",
58
55
  "npm-run-all": "^4.1.5",
59
- "pg": "^8.7.3",
60
- "rimraf": "^3.0.2",
56
+ "pg": "^8.18.0",
57
+ "return-style": "^3.0.1",
58
+ "rimraf": "^6.1.2",
61
59
  "standard-version": "^9.5.0",
62
- "ts-jest": "^27.1.3",
63
- "tscpaths": "^0.0.9",
64
- "typescript": "^4.7.4"
60
+ "ts-patch": "^3.3.0",
61
+ "typescript": "^5.9.3",
62
+ "typescript-eslint": "^8.55.0",
63
+ "typescript-transform-paths": "^3.5.6",
64
+ "vite": "^7.3.1",
65
+ "vite-tsconfig-paths": "^6.1.0",
66
+ "vitest": "^4.0.18"
65
67
  },
66
68
  "dependencies": {
67
- "@blackglory/errors": "^2.2.2",
68
- "@blackglory/types": "^1.2.1"
69
+ "@blackglory/prelude": "^0.4.0",
70
+ "extra-utils": "^5.20.0"
69
71
  },
70
72
  "peerDependencies": {
71
- "pg": "8"
73
+ "pg": "^8.0.0"
72
74
  }
73
75
  }
package/src/index.ts ADDED
@@ -0,0 +1 @@
1
+ export * from './migrate.js'
package/src/migrate.ts ADDED
@@ -0,0 +1,205 @@
1
+ import type { Client } from 'pg'
2
+ import { assert, isFunction } from '@blackglory/prelude'
3
+ import { first, isntUndefined, isUndefined, max } from 'extra-utils'
4
+
5
+ export interface IMigration {
6
+ version: number
7
+ up: string | ((client: Client) => PromiseLike<void>)
8
+ down: string | ((client: Client) => PromiseLike<void>)
9
+ }
10
+
11
+ interface IMigrateOptions {
12
+ targetVersion?: number
13
+ throwOnNewerVersion?: boolean
14
+
15
+ migrationsTable?: string
16
+ advisoryLockKey?: bigint
17
+ }
18
+
19
+ export async function migrate(
20
+ client: Client
21
+ , migrations: IMigration[]
22
+ , {
23
+ targetVersion = getMaximumVersion(migrations)
24
+ , throwOnNewerVersion = false
25
+ , migrationsTable = 'migrations'
26
+ , advisoryLockKey = BigInt('-9223372036854775808') // The smallest bigint for postgres
27
+ }: IMigrateOptions = {}
28
+ ): Promise<void> {
29
+ const maxVersion = getMaximumVersion(migrations)
30
+
31
+ await lock(client, advisoryLockKey)
32
+ try {
33
+
34
+ await ensureMigrationsTable(client, migrationsTable)
35
+
36
+ while (true) {
37
+ const done = await transaction(client, async () => {
38
+ const currentVersion = await getDatabaseVersion(client, migrationsTable)
39
+
40
+ if (maxVersion < currentVersion) {
41
+ if (throwOnNewerVersion) {
42
+ throw new Error(`Database version ${currentVersion} is higher than the maximum known migration version.`)
43
+ } else {
44
+ return true
45
+ }
46
+ } else {
47
+ if (currentVersion === targetVersion) {
48
+ return true
49
+ } else if (currentVersion < targetVersion) {
50
+ await upgrade()
51
+ return false
52
+ } else {
53
+ await downgrade()
54
+ return false
55
+ }
56
+ }
57
+ })
58
+
59
+ if (done) break
60
+ }
61
+ } finally {
62
+ await unlock(client, advisoryLockKey)
63
+ }
64
+
65
+ async function upgrade(): Promise<void> {
66
+ const currentVersion: number = await getDatabaseVersion(client, migrationsTable)
67
+ const targetVersion = currentVersion + 1
68
+ try {
69
+ const migration = migrations.find(x => x.version === targetVersion)
70
+ assert(migration, `Cannot find migration for version ${targetVersion}`)
71
+
72
+ if (isFunction(migration.up)) {
73
+ await migration.up(client)
74
+ } else {
75
+ await client.query(migration.up)
76
+ }
77
+ await setDatabaseVersion(client, migrationsTable, targetVersion)
78
+ } catch (e) {
79
+ throw new Error(
80
+ `Upgrade from version ${currentVersion} to version ${targetVersion} failed.`
81
+ , { cause: e }
82
+ )
83
+ }
84
+ }
85
+
86
+ async function downgrade(): Promise<void> {
87
+ const currentVersion = await getDatabaseVersion(client, migrationsTable)
88
+ const targetVersion = currentVersion - 1
89
+ try {
90
+ const migration = migrations.find(x => x.version === currentVersion)
91
+ assert(migration, `Cannot find migration for version ${targetVersion}`)
92
+
93
+ if (isFunction(migration.down)) {
94
+ await migration.down(client)
95
+ } else {
96
+ await client.query(migration.down)
97
+ }
98
+ await setDatabaseVersion(client, migrationsTable, targetVersion)
99
+ } catch (e) {
100
+ throw new Error(
101
+ `Downgrade from version ${currentVersion} to version ${targetVersion} failed.`
102
+ , { cause: e }
103
+ )
104
+ }
105
+ }
106
+ }
107
+
108
+ function getMaximumVersion(migrations: IMigration[]): number {
109
+ return migrations
110
+ .map(x => x.version)
111
+ .reduce(max, 0)
112
+ }
113
+
114
+ async function getDatabaseVersion(
115
+ client: Client
116
+ , migrationTable: string
117
+ ): Promise<number> {
118
+ const version = await tryGetDatabaseVersion(client, migrationTable)
119
+ assert(isntUndefined(version))
120
+
121
+ return version
122
+ }
123
+
124
+ async function tryGetDatabaseVersion(
125
+ client: Client
126
+ , migrationTable: string
127
+ ): Promise<number | undefined> {
128
+ const result = await client.query<{ schema_version: number }>(`
129
+ SELECT schema_version
130
+ FROM "${migrationTable}"
131
+ LIMIT 1;
132
+ `)
133
+
134
+ return first(result.rows)?.schema_version
135
+ }
136
+
137
+ async function ensureMigrationsTable(
138
+ client: Client
139
+ , migrationTable: string
140
+ ): Promise<void> {
141
+ await transaction(client, async () => {
142
+ await client.query(`
143
+ CREATE TABLE IF NOT EXISTS "${migrationTable}" (
144
+ schema_version INTEGER NOT NULL
145
+ );
146
+ `)
147
+
148
+ if (isUndefined(await tryGetDatabaseVersion(client, migrationTable))) {
149
+ await client.query(`
150
+ INSERT INTO "${migrationTable}" (schema_version)
151
+ VALUES (0);
152
+ `)
153
+ }
154
+ })
155
+ }
156
+
157
+ async function setDatabaseVersion(
158
+ client: Client
159
+ , migrationTable: string
160
+ , version: number
161
+ ): Promise<void> {
162
+ await client.query(`
163
+ UPDATE "${migrationTable}"
164
+ SET schema_version = $1;
165
+ `, [version])
166
+ }
167
+
168
+ async function lock(client: Client, key: bigint): Promise<void> {
169
+ await client.query(`
170
+ SELECT pg_advisory_lock(${key});
171
+ `)
172
+ }
173
+
174
+ async function unlock(client: Client, key: bigint): Promise<void> {
175
+ await client.query(`
176
+ SELECT pg_advisory_unlock(${key});
177
+ `)
178
+ }
179
+
180
+ const inTransactionClients = new WeakSet<Client>()
181
+ async function transaction<T>(client: Client, fn: () => Promise<T>): Promise<T> {
182
+ if (inTransactionClients.has(client)) {
183
+ throw new Error('PostgreSQL does not support nested transactions.')
184
+ } else {
185
+ inTransactionClients.add(client)
186
+ }
187
+
188
+ try {
189
+ await client.query('BEGIN')
190
+
191
+ try {
192
+ const result = await fn()
193
+
194
+ await client.query('COMMIT')
195
+
196
+ return result
197
+ } catch (e) {
198
+ await client.query('ROLLBACK')
199
+
200
+ throw e
201
+ }
202
+ } finally {
203
+ inTransactionClients.delete(client)
204
+ }
205
+ }
@@ -1 +0,0 @@
1
- export * from './migrate';
@@ -1,18 +0,0 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
- for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
- };
16
- Object.defineProperty(exports, "__esModule", { value: true });
17
- __exportStar(require("./migrate"), exports);
18
- //# sourceMappingURL=index.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,4CAAyB"}
@@ -1,7 +0,0 @@
1
- import type { Client } from 'pg';
2
- export interface IMigration {
3
- version: number;
4
- up: string | ((client: Client) => PromiseLike<void>);
5
- down: string | ((client: Client) => PromiseLike<void>);
6
- }
7
- export declare function migrate(client: Client, migrations: IMigration[], targetVersion?: number, migrationsTable?: string, advisoryLockKey?: bigint): Promise<void>;
@@ -1,145 +0,0 @@
1
- "use strict";
2
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
- return new (P || (P = Promise))(function (resolve, reject) {
5
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
- step((generator = generator.apply(thisArg, _arguments || [])).next());
9
- });
10
- };
11
- Object.defineProperty(exports, "__esModule", { value: true });
12
- exports.migrate = void 0;
13
- const types_1 = require("@blackglory/types");
14
- const errors_1 = require("@blackglory/errors");
15
- function migrate(client, migrations, targetVersion = getMaximumVersion(migrations), migrationsTable = 'migrations', advisoryLockKey = BigInt('-9223372036854775808')) {
16
- return __awaiter(this, void 0, void 0, function* () {
17
- const maxVersion = getMaximumVersion(migrations);
18
- yield lock(client, advisoryLockKey);
19
- try {
20
- while (true) {
21
- const currentVersion = yield getDatabaseVersion(client, migrationsTable);
22
- if (maxVersion < currentVersion) {
23
- break;
24
- }
25
- else {
26
- if (currentVersion === targetVersion) {
27
- break;
28
- }
29
- else if (currentVersion < targetVersion) {
30
- yield upgrade();
31
- }
32
- else {
33
- yield downgrade();
34
- }
35
- }
36
- }
37
- }
38
- finally {
39
- yield unlock(client, advisoryLockKey);
40
- }
41
- function upgrade() {
42
- return __awaiter(this, void 0, void 0, function* () {
43
- yield client.query('BEGIN');
44
- const currentVersion = yield getDatabaseVersion(client, migrationsTable);
45
- const targetVersion = currentVersion + 1;
46
- try {
47
- const migration = migrations.find(x => x.version === targetVersion);
48
- (0, errors_1.assert)(migration, `Cannot find migration for version ${targetVersion}`);
49
- if ((0, types_1.isFunction)(migration.up)) {
50
- yield migration.up(client);
51
- }
52
- else {
53
- yield client.query(migration.up);
54
- }
55
- yield setDatabaseVersion(client, migrationsTable, targetVersion);
56
- yield client.query('COMMIT');
57
- }
58
- catch (e) {
59
- console.error(`Upgrade from version ${currentVersion} to version ${targetVersion} failed.`);
60
- yield client.query('ROLLBACK');
61
- throw e;
62
- }
63
- });
64
- }
65
- function downgrade() {
66
- return __awaiter(this, void 0, void 0, function* () {
67
- yield client.query('BEGIN');
68
- const currentVersion = yield getDatabaseVersion(client, migrationsTable);
69
- const targetVersion = currentVersion - 1;
70
- try {
71
- const migration = migrations.find(x => x.version === currentVersion);
72
- (0, errors_1.assert)(migration, `Cannot find migration for version ${targetVersion}`);
73
- if ((0, types_1.isFunction)(migration.down)) {
74
- yield migration.down(client);
75
- }
76
- else {
77
- yield client.query(migration.down);
78
- }
79
- yield setDatabaseVersion(client, migrationsTable, targetVersion);
80
- yield client.query('COMMIT');
81
- }
82
- catch (e) {
83
- console.error(`Downgrade from version ${currentVersion} to version ${targetVersion} failed.`);
84
- yield client.query('ROLLBACK');
85
- throw e;
86
- }
87
- });
88
- }
89
- });
90
- }
91
- exports.migrate = migrate;
92
- function getMaximumVersion(migrations) {
93
- return migrations.reduce((max, cur) => Math.max(cur.version, max), 0);
94
- }
95
- function getDatabaseVersion(client, migrationTable) {
96
- return __awaiter(this, void 0, void 0, function* () {
97
- yield ensureMigrationsTable(client, migrationTable);
98
- const result = yield client.query(`
99
- SELECT schema_version
100
- FROM "${migrationTable}";
101
- `);
102
- if (result.rows.length) {
103
- return result.rows[0].schema_version;
104
- }
105
- else {
106
- yield client.query(`
107
- INSERT INTO "${migrationTable}" (schema_version)
108
- VALUES (0);
109
- `);
110
- return 0;
111
- }
112
- });
113
- }
114
- function ensureMigrationsTable(client, migrationTable) {
115
- return __awaiter(this, void 0, void 0, function* () {
116
- yield client.query(`
117
- CREATE TABLE IF NOT EXISTS "${migrationTable}" (
118
- schema_version INTEGER NOT NULL
119
- );
120
- `);
121
- });
122
- }
123
- function setDatabaseVersion(client, migrationTable, version) {
124
- return __awaiter(this, void 0, void 0, function* () {
125
- yield client.query(`
126
- UPDATE ${migrationTable}
127
- SET schema_version = ${version};
128
- `);
129
- });
130
- }
131
- function lock(client, key) {
132
- return __awaiter(this, void 0, void 0, function* () {
133
- yield client.query(`
134
- SELECT pg_advisory_lock(${key});
135
- `);
136
- });
137
- }
138
- function unlock(client, key) {
139
- return __awaiter(this, void 0, void 0, function* () {
140
- yield client.query(`
141
- SELECT pg_advisory_unlock(${key});
142
- `);
143
- });
144
- }
145
- //# sourceMappingURL=migrate.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"migrate.js","sourceRoot":"","sources":["../../src/migrate.ts"],"names":[],"mappings":"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"}
@@ -1 +0,0 @@
1
- export * from './migrate';
@@ -1,18 +0,0 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
- for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
- };
16
- Object.defineProperty(exports, "__esModule", { value: true });
17
- __exportStar(require("./migrate"), exports);
18
- //# sourceMappingURL=index.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,4CAAyB"}
@@ -1,7 +0,0 @@
1
- import type { Client } from 'pg';
2
- export interface IMigration {
3
- version: number;
4
- up: string | ((client: Client) => PromiseLike<void>);
5
- down: string | ((client: Client) => PromiseLike<void>);
6
- }
7
- export declare function migrate(client: Client, migrations: IMigration[], targetVersion?: number, migrationsTable?: string, advisoryLockKey?: bigint): Promise<void>;
@@ -1,120 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.migrate = void 0;
4
- const types_1 = require("@blackglory/types");
5
- const errors_1 = require("@blackglory/errors");
6
- async function migrate(client, migrations, targetVersion = getMaximumVersion(migrations), migrationsTable = 'migrations', advisoryLockKey = BigInt('-9223372036854775808')) {
7
- const maxVersion = getMaximumVersion(migrations);
8
- await lock(client, advisoryLockKey);
9
- try {
10
- while (true) {
11
- const currentVersion = await getDatabaseVersion(client, migrationsTable);
12
- if (maxVersion < currentVersion) {
13
- break;
14
- }
15
- else {
16
- if (currentVersion === targetVersion) {
17
- break;
18
- }
19
- else if (currentVersion < targetVersion) {
20
- await upgrade();
21
- }
22
- else {
23
- await downgrade();
24
- }
25
- }
26
- }
27
- }
28
- finally {
29
- await unlock(client, advisoryLockKey);
30
- }
31
- async function upgrade() {
32
- await client.query('BEGIN');
33
- const currentVersion = await getDatabaseVersion(client, migrationsTable);
34
- const targetVersion = currentVersion + 1;
35
- try {
36
- const migration = migrations.find(x => x.version === targetVersion);
37
- (0, errors_1.assert)(migration, `Cannot find migration for version ${targetVersion}`);
38
- if ((0, types_1.isFunction)(migration.up)) {
39
- await migration.up(client);
40
- }
41
- else {
42
- await client.query(migration.up);
43
- }
44
- await setDatabaseVersion(client, migrationsTable, targetVersion);
45
- await client.query('COMMIT');
46
- }
47
- catch (e) {
48
- console.error(`Upgrade from version ${currentVersion} to version ${targetVersion} failed.`);
49
- await client.query('ROLLBACK');
50
- throw e;
51
- }
52
- }
53
- async function downgrade() {
54
- await client.query('BEGIN');
55
- const currentVersion = await getDatabaseVersion(client, migrationsTable);
56
- const targetVersion = currentVersion - 1;
57
- try {
58
- const migration = migrations.find(x => x.version === currentVersion);
59
- (0, errors_1.assert)(migration, `Cannot find migration for version ${targetVersion}`);
60
- if ((0, types_1.isFunction)(migration.down)) {
61
- await migration.down(client);
62
- }
63
- else {
64
- await client.query(migration.down);
65
- }
66
- await setDatabaseVersion(client, migrationsTable, targetVersion);
67
- await client.query('COMMIT');
68
- }
69
- catch (e) {
70
- console.error(`Downgrade from version ${currentVersion} to version ${targetVersion} failed.`);
71
- await client.query('ROLLBACK');
72
- throw e;
73
- }
74
- }
75
- }
76
- exports.migrate = migrate;
77
- function getMaximumVersion(migrations) {
78
- return migrations.reduce((max, cur) => Math.max(cur.version, max), 0);
79
- }
80
- async function getDatabaseVersion(client, migrationTable) {
81
- await ensureMigrationsTable(client, migrationTable);
82
- const result = await client.query(`
83
- SELECT schema_version
84
- FROM "${migrationTable}";
85
- `);
86
- if (result.rows.length) {
87
- return result.rows[0].schema_version;
88
- }
89
- else {
90
- await client.query(`
91
- INSERT INTO "${migrationTable}" (schema_version)
92
- VALUES (0);
93
- `);
94
- return 0;
95
- }
96
- }
97
- async function ensureMigrationsTable(client, migrationTable) {
98
- await client.query(`
99
- CREATE TABLE IF NOT EXISTS "${migrationTable}" (
100
- schema_version INTEGER NOT NULL
101
- );
102
- `);
103
- }
104
- async function setDatabaseVersion(client, migrationTable, version) {
105
- await client.query(`
106
- UPDATE ${migrationTable}
107
- SET schema_version = ${version};
108
- `);
109
- }
110
- async function lock(client, key) {
111
- await client.query(`
112
- SELECT pg_advisory_lock(${key});
113
- `);
114
- }
115
- async function unlock(client, key) {
116
- await client.query(`
117
- SELECT pg_advisory_unlock(${key});
118
- `);
119
- }
120
- //# sourceMappingURL=migrate.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"migrate.js","sourceRoot":"","sources":["../../src/migrate.ts"],"names":[],"mappings":"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"}