knex 3.2.2 → 3.2.4
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/CHANGELOG.md +2447 -2441
- package/CONTRIBUTING.md +190 -190
- package/LICENSE +22 -22
- package/README.md +156 -156
- package/UPGRADING.md +245 -245
- package/bin/cli.js +516 -516
- package/bin/knexfile-runtime-error.js +27 -27
- package/bin/utils/cli-config-utils.js +217 -217
- package/bin/utils/constants.js +7 -7
- package/bin/utils/migrationsLister.js +37 -37
- package/knex.js +23 -23
- package/knex.mjs +11 -11
- package/lib/builder-interface-augmenter.js +120 -120
- package/lib/client.js +585 -585
- package/lib/constants.js +61 -61
- package/lib/dialects/better-sqlite3/index.js +101 -101
- package/lib/dialects/cockroachdb/crdb-columncompiler.js +14 -14
- package/lib/dialects/cockroachdb/crdb-querybuilder.js +11 -11
- package/lib/dialects/cockroachdb/crdb-querycompiler.js +122 -122
- package/lib/dialects/cockroachdb/crdb-tablecompiler.js +46 -46
- package/lib/dialects/cockroachdb/crdb-viewcompiler.js +15 -15
- package/lib/dialects/cockroachdb/index.js +86 -86
- package/lib/dialects/index.js +34 -34
- package/lib/dialects/mssql/index.js +498 -498
- package/lib/dialects/mssql/mssql-formatter.js +34 -34
- package/lib/dialects/mssql/query/mssql-querycompiler.js +601 -601
- package/lib/dialects/mssql/schema/mssql-columncompiler.js +185 -185
- package/lib/dialects/mssql/schema/mssql-compiler.js +91 -91
- package/lib/dialects/mssql/schema/mssql-tablecompiler.js +393 -393
- package/lib/dialects/mssql/schema/mssql-viewcompiler.js +55 -55
- package/lib/dialects/mssql/transaction.js +176 -176
- package/lib/dialects/mysql/index.js +317 -317
- package/lib/dialects/mysql/query/mysql-querybuilder.js +14 -14
- package/lib/dialects/mysql/query/mysql-querycompiler.js +292 -292
- package/lib/dialects/mysql/schema/mysql-columncompiler.js +193 -193
- package/lib/dialects/mysql/schema/mysql-compiler.js +60 -60
- package/lib/dialects/mysql/schema/mysql-tablecompiler.js +426 -426
- package/lib/dialects/mysql/schema/mysql-viewbuilder.js +21 -21
- package/lib/dialects/mysql/schema/mysql-viewcompiler.js +15 -15
- package/lib/dialects/mysql/transaction.js +46 -46
- package/lib/dialects/mysql2/index.js +53 -53
- package/lib/dialects/mysql2/transaction.js +44 -44
- package/lib/dialects/oracle/DEAD_CODE.md +5 -5
- package/lib/dialects/oracle/index.js +92 -92
- package/lib/dialects/oracle/query/oracle-querycompiler.js +343 -343
- package/lib/dialects/oracle/schema/internal/incrementUtils.js +22 -22
- package/lib/dialects/oracle/schema/internal/trigger.js +155 -155
- package/lib/dialects/oracle/schema/oracle-columnbuilder.js +17 -17
- package/lib/dialects/oracle/schema/oracle-columncompiler.js +126 -126
- package/lib/dialects/oracle/schema/oracle-compiler.js +124 -124
- package/lib/dialects/oracle/schema/oracle-tablecompiler.js +210 -210
- package/lib/dialects/oracle/utils.js +107 -107
- package/lib/dialects/oracledb/index.js +381 -381
- package/lib/dialects/oracledb/query/oracledb-querycompiler.js +481 -481
- package/lib/dialects/oracledb/schema/oracledb-columncompiler.js +61 -61
- package/lib/dialects/oracledb/schema/oracledb-tablecompiler.js +19 -19
- package/lib/dialects/oracledb/schema/oracledb-viewbuilder.js +13 -13
- package/lib/dialects/oracledb/schema/oracledb-viewcompiler.js +19 -19
- package/lib/dialects/oracledb/transaction.js +98 -98
- package/lib/dialects/oracledb/utils.js +208 -208
- package/lib/dialects/pgnative/index.js +60 -60
- package/lib/dialects/postgres/execution/pg-transaction.js +19 -19
- package/lib/dialects/postgres/index.js +373 -373
- package/lib/dialects/postgres/query/pg-querybuilder.js +43 -43
- package/lib/dialects/postgres/query/pg-querycompiler.js +400 -400
- package/lib/dialects/postgres/schema/pg-columncompiler.js +162 -162
- package/lib/dialects/postgres/schema/pg-compiler.js +138 -138
- package/lib/dialects/postgres/schema/pg-tablecompiler.js +331 -331
- package/lib/dialects/postgres/schema/pg-viewbuilder.js +21 -21
- package/lib/dialects/postgres/schema/pg-viewcompiler.js +35 -35
- package/lib/dialects/redshift/index.js +86 -86
- package/lib/dialects/redshift/query/redshift-querycompiler.js +163 -163
- package/lib/dialects/redshift/schema/redshift-columnbuilder.js +22 -22
- package/lib/dialects/redshift/schema/redshift-columncompiler.js +67 -67
- package/lib/dialects/redshift/schema/redshift-compiler.js +14 -14
- package/lib/dialects/redshift/schema/redshift-tablecompiler.js +134 -134
- package/lib/dialects/redshift/schema/redshift-viewcompiler.js +11 -11
- package/lib/dialects/redshift/transaction.js +32 -32
- package/lib/dialects/sqlite3/execution/sqlite-transaction.js +172 -172
- package/lib/dialects/sqlite3/index.js +263 -263
- package/lib/dialects/sqlite3/query/sqlite-querybuilder.js +33 -33
- package/lib/dialects/sqlite3/query/sqlite-querycompiler.js +341 -341
- package/lib/dialects/sqlite3/schema/ddl.js +380 -380
- package/lib/dialects/sqlite3/schema/internal/compiler.js +327 -327
- package/lib/dialects/sqlite3/schema/internal/parser-combinator.js +161 -161
- package/lib/dialects/sqlite3/schema/internal/parser.js +638 -638
- package/lib/dialects/sqlite3/schema/internal/sqlite-ddl-operations.js +41 -41
- package/lib/dialects/sqlite3/schema/internal/tokenizer.js +38 -38
- package/lib/dialects/sqlite3/schema/internal/utils.js +12 -12
- package/lib/dialects/sqlite3/schema/sqlite-columncompiler.js +50 -50
- package/lib/dialects/sqlite3/schema/sqlite-compiler.js +80 -80
- package/lib/dialects/sqlite3/schema/sqlite-tablecompiler.js +364 -364
- package/lib/dialects/sqlite3/schema/sqlite-viewcompiler.js +40 -40
- package/lib/execution/batch-insert.js +51 -51
- package/lib/execution/internal/delay.js +6 -6
- package/lib/execution/internal/ensure-connection-callback.js +41 -41
- package/lib/execution/internal/query-executioner.js +62 -62
- package/lib/execution/runner.js +325 -325
- package/lib/execution/transaction.js +417 -417
- package/lib/formatter/formatterUtils.js +42 -42
- package/lib/formatter/rawFormatter.js +84 -84
- package/lib/formatter/wrappingFormatter.js +253 -253
- package/lib/formatter.js +25 -25
- package/lib/index.js +3 -3
- package/lib/knex-builder/FunctionHelper.js +80 -80
- package/lib/knex-builder/Knex.js +59 -59
- package/lib/knex-builder/internal/config-resolver.js +57 -57
- package/lib/knex-builder/internal/parse-connection.js +87 -87
- package/lib/knex-builder/make-knex.js +345 -345
- package/lib/logger.js +76 -76
- package/lib/migrations/common/MigrationsLoader.js +36 -36
- package/lib/migrations/migrate/MigrationGenerator.js +84 -84
- package/lib/migrations/migrate/Migrator.js +632 -632
- package/lib/migrations/migrate/migrate-stub.js +17 -17
- package/lib/migrations/migrate/migration-list-resolver.js +33 -33
- package/lib/migrations/migrate/migrator-configuration-merger.js +58 -58
- package/lib/migrations/migrate/sources/fs-migrations.js +74 -74
- package/lib/migrations/migrate/stub/cjs.stub +15 -15
- package/lib/migrations/migrate/stub/coffee.stub +13 -13
- package/lib/migrations/migrate/stub/eg.stub +14 -14
- package/lib/migrations/migrate/stub/js-schema.stub +22 -22
- package/lib/migrations/migrate/stub/js.stub +22 -22
- package/lib/migrations/migrate/stub/knexfile-coffee.stub +34 -34
- package/lib/migrations/migrate/stub/knexfile-eg.stub +43 -43
- package/lib/migrations/migrate/stub/knexfile-js.stub +47 -47
- package/lib/migrations/migrate/stub/knexfile-ls.stub +35 -35
- package/lib/migrations/migrate/stub/knexfile-ts.stub +47 -47
- package/lib/migrations/migrate/stub/ls.stub +14 -14
- package/lib/migrations/migrate/stub/mjs.stub +23 -23
- package/lib/migrations/migrate/stub/ts-schema.stub +21 -21
- package/lib/migrations/migrate/stub/ts.stub +21 -21
- package/lib/migrations/migrate/table-creator.js +77 -77
- package/lib/migrations/migrate/table-resolver.js +27 -27
- package/lib/migrations/seed/Seeder.js +137 -137
- package/lib/migrations/seed/seed-stub.js +13 -13
- package/lib/migrations/seed/seeder-configuration-merger.js +60 -60
- package/lib/migrations/seed/sources/fs-seeds.js +65 -65
- package/lib/migrations/seed/stub/coffee.stub +9 -9
- package/lib/migrations/seed/stub/eg.stub +11 -11
- package/lib/migrations/seed/stub/js.stub +13 -13
- package/lib/migrations/seed/stub/ls.stub +11 -11
- package/lib/migrations/seed/stub/mjs.stub +12 -12
- package/lib/migrations/seed/stub/ts.stub +13 -13
- package/lib/migrations/util/fs.js +86 -86
- package/lib/migrations/util/import-file.js +12 -12
- package/lib/migrations/util/is-module-type.js +9 -9
- package/lib/migrations/util/template.js +52 -52
- package/lib/migrations/util/timestamp.js +14 -14
- package/lib/query/analytic.js +52 -52
- package/lib/query/constants.js +15 -15
- package/lib/query/joinclause.js +270 -270
- package/lib/query/method-constants.js +136 -136
- package/lib/query/querybuilder.js +1793 -1793
- package/lib/query/querycompiler.js +1634 -1634
- package/lib/raw.js +139 -139
- package/lib/ref.js +39 -39
- package/lib/schema/builder.js +115 -115
- package/lib/schema/columnbuilder.js +146 -146
- package/lib/schema/columncompiler.js +307 -307
- package/lib/schema/compiler.js +187 -187
- package/lib/schema/internal/helpers.js +55 -55
- package/lib/schema/tablebuilder.js +379 -379
- package/lib/schema/tablecompiler.js +450 -450
- package/lib/schema/viewbuilder.js +92 -92
- package/lib/schema/viewcompiler.js +138 -138
- package/lib/util/finally-mixin.js +13 -13
- package/lib/util/helpers.js +95 -95
- package/lib/util/is.js +32 -32
- package/lib/util/nanoid.js +40 -40
- package/lib/util/noop.js +1 -1
- package/lib/util/save-async-stack.js +14 -14
- package/lib/util/security.js +32 -32
- package/lib/util/string.js +190 -190
- package/lib/util/timeout.js +29 -29
- package/package.json +294 -296
- package/scripts/act-testing/act.sh +19 -19
- package/scripts/act-testing/merged-no-label.json +11 -11
- package/scripts/act-testing/merged-patch-labeled.json +12 -12
- package/scripts/act-testing/merged-skip-labeled.json +12 -12
- package/scripts/act-testing/not-merged-patch-labeled.json +12 -12
- package/scripts/build-for-release.sh +121 -121
- package/scripts/build.js +125 -125
- package/scripts/clean.js +31 -31
- package/scripts/docker-compose.yml +150 -150
- package/scripts/format-changelog.js +55 -55
- package/scripts/next-release-howto.md +24 -24
- package/scripts/oracledb-install-driver-libs.sh +82 -82
- package/scripts/release.sh +36 -36
- package/scripts/runkit-example.js +35 -35
- package/scripts/stress-test/README.txt +18 -18
- package/scripts/stress-test/docker-compose.yml +55 -55
- package/scripts/stress-test/knex-stress-test.js +212 -212
- package/scripts/stress-test/mysql2-random-hanging-every-now-and-then.js +149 -149
- package/scripts/stress-test/mysql2-sudden-exit-without-error.js +101 -101
- package/scripts/stress-test/reconnect-test-mysql-based-drivers.js +188 -188
- package/types/index.d.mts +11 -0
- package/types/index.d.ts +3321 -3321
- package/types/result.d.ts +27 -27
- package/types/tables.d.ts +4 -4
|
@@ -1,632 +1,632 @@
|
|
|
1
|
-
// Migrator
|
|
2
|
-
// -------
|
|
3
|
-
const differenceWith = require('lodash/differenceWith');
|
|
4
|
-
const get = require('lodash/get');
|
|
5
|
-
const isEmpty = require('lodash/isEmpty');
|
|
6
|
-
const max = require('lodash/max');
|
|
7
|
-
const {
|
|
8
|
-
getLockTableName,
|
|
9
|
-
getTable,
|
|
10
|
-
getTableName,
|
|
11
|
-
} = require('./table-resolver');
|
|
12
|
-
const { getSchemaBuilder } = require('./table-creator');
|
|
13
|
-
const migrationListResolver = require('./migration-list-resolver');
|
|
14
|
-
const MigrationGenerator = require('./MigrationGenerator');
|
|
15
|
-
const { getMergedConfig } = require('./migrator-configuration-merger');
|
|
16
|
-
const { isBoolean, isFunction } = require('../../util/is');
|
|
17
|
-
|
|
18
|
-
class LockError extends Error {
|
|
19
|
-
constructor(msg) {
|
|
20
|
-
super(msg);
|
|
21
|
-
this.name = 'MigrationLocked';
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
// The new migration we're performing, typically called from the `knex.migrate`
|
|
26
|
-
// interface on the main `knex` object. Passes the `knex` instance performing
|
|
27
|
-
// the migration.
|
|
28
|
-
class Migrator {
|
|
29
|
-
constructor(knex) {
|
|
30
|
-
// Clone knex instance and remove post-processing that is unnecessary for internal queries from a cloned config
|
|
31
|
-
if (isFunction(knex)) {
|
|
32
|
-
if (!knex.isTransaction) {
|
|
33
|
-
this.knex = knex.withUserParams({
|
|
34
|
-
...knex.userParams,
|
|
35
|
-
});
|
|
36
|
-
} else {
|
|
37
|
-
this.knex = knex;
|
|
38
|
-
}
|
|
39
|
-
} else {
|
|
40
|
-
this.knex = Object.assign({}, knex);
|
|
41
|
-
this.knex.userParams = this.knex.userParams || {};
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
this.config = getMergedConfig(
|
|
45
|
-
this.knex.client.config.migrations,
|
|
46
|
-
undefined,
|
|
47
|
-
this.knex.client.logger
|
|
48
|
-
);
|
|
49
|
-
this.generator = new MigrationGenerator(
|
|
50
|
-
this.knex.client.config.migrations,
|
|
51
|
-
this.knex.client.logger
|
|
52
|
-
);
|
|
53
|
-
this._activeMigration = {
|
|
54
|
-
fileName: null,
|
|
55
|
-
};
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
// Migrators to the latest configuration.
|
|
59
|
-
async latest(config) {
|
|
60
|
-
this._disableProcessing();
|
|
61
|
-
this.config = getMergedConfig(config, this.config, this.knex.client.logger);
|
|
62
|
-
|
|
63
|
-
const allAndCompleted = await migrationListResolver.listAllAndCompleted(
|
|
64
|
-
this.config,
|
|
65
|
-
this.knex
|
|
66
|
-
);
|
|
67
|
-
|
|
68
|
-
if (!this.config.disableMigrationsListValidation) {
|
|
69
|
-
validateMigrationList(this.config.migrationSource, allAndCompleted);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
const [all, completed] = allAndCompleted;
|
|
73
|
-
|
|
74
|
-
const migrations = getNewMigrations(
|
|
75
|
-
this.config.migrationSource,
|
|
76
|
-
all,
|
|
77
|
-
completed
|
|
78
|
-
);
|
|
79
|
-
|
|
80
|
-
const transactionForAll =
|
|
81
|
-
!this.config.disableTransactions &&
|
|
82
|
-
!(
|
|
83
|
-
await Promise.all(
|
|
84
|
-
migrations.map(async (migration) => {
|
|
85
|
-
const migrationContents =
|
|
86
|
-
await this.config.migrationSource.getMigration(migration);
|
|
87
|
-
return !this._useTransaction(migrationContents);
|
|
88
|
-
})
|
|
89
|
-
)
|
|
90
|
-
).some((isTransactionUsed) => isTransactionUsed);
|
|
91
|
-
|
|
92
|
-
if (transactionForAll) {
|
|
93
|
-
return this.knex.transaction((trx) => {
|
|
94
|
-
return this._runBatch(migrations, 'up', trx);
|
|
95
|
-
});
|
|
96
|
-
} else {
|
|
97
|
-
return this._runBatch(migrations, 'up');
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
// Runs the next migration that has not yet been run
|
|
102
|
-
async up(config) {
|
|
103
|
-
this._disableProcessing();
|
|
104
|
-
this.config = getMergedConfig(config, this.config, this.knex.client.logger);
|
|
105
|
-
|
|
106
|
-
const allAndCompleted = await migrationListResolver.listAllAndCompleted(
|
|
107
|
-
this.config,
|
|
108
|
-
this.knex
|
|
109
|
-
);
|
|
110
|
-
|
|
111
|
-
if (!this.config.disableMigrationsListValidation) {
|
|
112
|
-
validateMigrationList(this.config.migrationSource, allAndCompleted);
|
|
113
|
-
}
|
|
114
|
-
const [all, completed] = allAndCompleted;
|
|
115
|
-
|
|
116
|
-
const newMigrations = getNewMigrations(
|
|
117
|
-
this.config.migrationSource,
|
|
118
|
-
all,
|
|
119
|
-
completed
|
|
120
|
-
);
|
|
121
|
-
|
|
122
|
-
let migrationToRun;
|
|
123
|
-
const name = this.config.name;
|
|
124
|
-
if (name) {
|
|
125
|
-
if (!completed.map((m) => m.name).includes(name)) {
|
|
126
|
-
migrationToRun = newMigrations.find((migration) => {
|
|
127
|
-
return (
|
|
128
|
-
this.config.migrationSource.getMigrationName(migration) === name
|
|
129
|
-
);
|
|
130
|
-
});
|
|
131
|
-
if (!migrationToRun) {
|
|
132
|
-
throw new Error(`Migration "${name}" not found.`);
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
} else {
|
|
136
|
-
migrationToRun = newMigrations[0];
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
const useTransaction =
|
|
140
|
-
!migrationToRun ||
|
|
141
|
-
this._useTransaction(
|
|
142
|
-
await this.config.migrationSource.getMigration(migrationToRun)
|
|
143
|
-
);
|
|
144
|
-
|
|
145
|
-
const migrationsToRun = [];
|
|
146
|
-
if (migrationToRun) {
|
|
147
|
-
migrationsToRun.push(migrationToRun);
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
const transactionForAll =
|
|
151
|
-
!this.config.disableTransactions && (!migrationToRun || useTransaction);
|
|
152
|
-
|
|
153
|
-
if (transactionForAll) {
|
|
154
|
-
return await this.knex.transaction((trx) => {
|
|
155
|
-
return this._runBatch(migrationsToRun, 'up', trx);
|
|
156
|
-
});
|
|
157
|
-
} else {
|
|
158
|
-
return await this._runBatch(migrationsToRun, 'up');
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
// Rollback the last "batch", or all, of migrations that were run.
|
|
163
|
-
rollback(config, all = false) {
|
|
164
|
-
this._disableProcessing();
|
|
165
|
-
return new Promise((resolve, reject) => {
|
|
166
|
-
try {
|
|
167
|
-
this.config = getMergedConfig(
|
|
168
|
-
config,
|
|
169
|
-
this.config,
|
|
170
|
-
this.knex.client.logger
|
|
171
|
-
);
|
|
172
|
-
} catch (e) {
|
|
173
|
-
reject(e);
|
|
174
|
-
}
|
|
175
|
-
migrationListResolver
|
|
176
|
-
.listAllAndCompleted(this.config, this.knex)
|
|
177
|
-
.then((value) => {
|
|
178
|
-
if (!this.config.disableMigrationsListValidation) {
|
|
179
|
-
validateMigrationList(this.config.migrationSource, value);
|
|
180
|
-
}
|
|
181
|
-
return value;
|
|
182
|
-
})
|
|
183
|
-
.then((val) => {
|
|
184
|
-
const [allMigrations, completedMigrations] = val;
|
|
185
|
-
|
|
186
|
-
return all
|
|
187
|
-
? allMigrations
|
|
188
|
-
.filter((migration) => {
|
|
189
|
-
return completedMigrations
|
|
190
|
-
.map((migration) => migration.name)
|
|
191
|
-
.includes(
|
|
192
|
-
this.config.migrationSource.getMigrationName(migration)
|
|
193
|
-
);
|
|
194
|
-
})
|
|
195
|
-
.reverse()
|
|
196
|
-
: this._getLastBatch(val);
|
|
197
|
-
})
|
|
198
|
-
.then((migrations) => {
|
|
199
|
-
return this._runBatch(migrations, 'down');
|
|
200
|
-
})
|
|
201
|
-
.then(resolve, reject);
|
|
202
|
-
});
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
down(config) {
|
|
206
|
-
this._disableProcessing();
|
|
207
|
-
this.config = getMergedConfig(config, this.config, this.knex.client.logger);
|
|
208
|
-
|
|
209
|
-
return migrationListResolver
|
|
210
|
-
.listAllAndCompleted(this.config, this.knex)
|
|
211
|
-
.then((value) => {
|
|
212
|
-
if (!this.config.disableMigrationsListValidation) {
|
|
213
|
-
validateMigrationList(this.config.migrationSource, value);
|
|
214
|
-
}
|
|
215
|
-
return value;
|
|
216
|
-
})
|
|
217
|
-
.then(([all, completed]) => {
|
|
218
|
-
const completedMigrations = all.filter((migration) => {
|
|
219
|
-
return completed
|
|
220
|
-
.map((migration) => migration.name)
|
|
221
|
-
.includes(this.config.migrationSource.getMigrationName(migration));
|
|
222
|
-
});
|
|
223
|
-
|
|
224
|
-
let migrationToRun;
|
|
225
|
-
const name = this.config.name;
|
|
226
|
-
if (name) {
|
|
227
|
-
migrationToRun = completedMigrations.find((migration) => {
|
|
228
|
-
return (
|
|
229
|
-
this.config.migrationSource.getMigrationName(migration) === name
|
|
230
|
-
);
|
|
231
|
-
});
|
|
232
|
-
if (!migrationToRun) {
|
|
233
|
-
throw new Error(`Migration "${name}" was not run.`);
|
|
234
|
-
}
|
|
235
|
-
} else {
|
|
236
|
-
migrationToRun = completedMigrations[completedMigrations.length - 1];
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
const migrationsToRun = [];
|
|
240
|
-
if (migrationToRun) {
|
|
241
|
-
migrationsToRun.push(migrationToRun);
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
return this._runBatch(migrationsToRun, 'down');
|
|
245
|
-
});
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
status(config) {
|
|
249
|
-
this._disableProcessing();
|
|
250
|
-
this.config = getMergedConfig(config, this.config, this.knex.client.logger);
|
|
251
|
-
|
|
252
|
-
return Promise.all([
|
|
253
|
-
getTable(this.knex, this.config.tableName, this.config.schemaName).select(
|
|
254
|
-
'*'
|
|
255
|
-
),
|
|
256
|
-
migrationListResolver.listAll(this.config.migrationSource),
|
|
257
|
-
]).then(([db, code]) => db.length - code.length);
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
// Retrieves and returns the current migration version we're on, as a promise.
|
|
261
|
-
// If no migrations have been run yet, return "none".
|
|
262
|
-
currentVersion(config) {
|
|
263
|
-
this._disableProcessing();
|
|
264
|
-
this.config = getMergedConfig(config, this.config, this.knex.client.logger);
|
|
265
|
-
|
|
266
|
-
return migrationListResolver
|
|
267
|
-
.listCompleted(this.config.tableName, this.config.schemaName, this.knex)
|
|
268
|
-
.then((completed) => {
|
|
269
|
-
const val = max(completed.map((value) => value.name.split('_')[0]));
|
|
270
|
-
return val === undefined ? 'none' : val;
|
|
271
|
-
});
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
// list all migrations
|
|
275
|
-
async list(config) {
|
|
276
|
-
this._disableProcessing();
|
|
277
|
-
this.config = getMergedConfig(config, this.config, this.knex.client.logger);
|
|
278
|
-
|
|
279
|
-
const [all, completed] = await migrationListResolver.listAllAndCompleted(
|
|
280
|
-
this.config,
|
|
281
|
-
this.knex
|
|
282
|
-
);
|
|
283
|
-
|
|
284
|
-
if (!this.config.disableMigrationsListValidation) {
|
|
285
|
-
validateMigrationList(this.config.migrationSource, [all, completed]);
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
const newMigrations = getNewMigrations(
|
|
289
|
-
this.config.migrationSource,
|
|
290
|
-
all,
|
|
291
|
-
completed
|
|
292
|
-
);
|
|
293
|
-
return [completed, newMigrations];
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
async forceFreeMigrationsLock(config) {
|
|
297
|
-
this._disableProcessing();
|
|
298
|
-
this.config = getMergedConfig(config, this.config, this.knex.client.logger);
|
|
299
|
-
const { schemaName, tableName } = this.config;
|
|
300
|
-
const lockTableName = getLockTableName(tableName);
|
|
301
|
-
const { knex } = this;
|
|
302
|
-
const getLockTable = () => getTable(knex, lockTableName, schemaName);
|
|
303
|
-
const tableExists = await getSchemaBuilder(knex, schemaName).hasTable(
|
|
304
|
-
lockTableName
|
|
305
|
-
);
|
|
306
|
-
if (tableExists) {
|
|
307
|
-
await getLockTable().del();
|
|
308
|
-
await getLockTable().insert({
|
|
309
|
-
is_locked: 0,
|
|
310
|
-
});
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
// Creates a new migration, with a given name.
|
|
315
|
-
make(name, config) {
|
|
316
|
-
return this.generator.make(name, config, this.knex.client.logger);
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
_disableProcessing() {
|
|
320
|
-
if (this.knex.disableProcessing) {
|
|
321
|
-
this.knex.disableProcessing();
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
_lockMigrations(trx) {
|
|
326
|
-
const tableName = getLockTableName(this.config.tableName);
|
|
327
|
-
return getTable(this.knex, tableName, this.config.schemaName)
|
|
328
|
-
.transacting(trx)
|
|
329
|
-
.where('is_locked', '=', 0)
|
|
330
|
-
.update({ is_locked: 1 })
|
|
331
|
-
.then((rowCount) => {
|
|
332
|
-
if (rowCount !== 1) {
|
|
333
|
-
throw new Error('Migration table is already locked');
|
|
334
|
-
}
|
|
335
|
-
});
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
_getLock(trx) {
|
|
339
|
-
const transact = trx ? (fn) => fn(trx) : (fn) => this.knex.transaction(fn);
|
|
340
|
-
return transact((trx) => {
|
|
341
|
-
return this._lockMigrations(trx);
|
|
342
|
-
}).catch((err) => {
|
|
343
|
-
throw new LockError(err.message);
|
|
344
|
-
});
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
_freeLock(trx = this.knex) {
|
|
348
|
-
const tableName = getLockTableName(this.config.tableName);
|
|
349
|
-
return getTable(trx, tableName, this.config.schemaName).update({
|
|
350
|
-
is_locked: 0,
|
|
351
|
-
});
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
// Run a batch of current migrations, in sequence.
|
|
355
|
-
async _runBatch(migrations, direction, trx) {
|
|
356
|
-
const canGetLockInTransaction =
|
|
357
|
-
this.knex.client.driverName !== 'cockroachdb';
|
|
358
|
-
try {
|
|
359
|
-
await this._getLock(canGetLockInTransaction ? trx : undefined);
|
|
360
|
-
// When there is a wrapping transaction, some migrations
|
|
361
|
-
// could have been done while waiting for the lock:
|
|
362
|
-
const completed = trx
|
|
363
|
-
? await migrationListResolver.listCompleted(
|
|
364
|
-
this.config.tableName,
|
|
365
|
-
this.config.schemaName,
|
|
366
|
-
trx
|
|
367
|
-
)
|
|
368
|
-
: [];
|
|
369
|
-
|
|
370
|
-
migrations = getNewMigrations(
|
|
371
|
-
this.config.migrationSource,
|
|
372
|
-
migrations,
|
|
373
|
-
completed
|
|
374
|
-
);
|
|
375
|
-
|
|
376
|
-
await Promise.all(
|
|
377
|
-
migrations.map(this._validateMigrationStructure.bind(this))
|
|
378
|
-
);
|
|
379
|
-
|
|
380
|
-
let batchNo = await this._latestBatchNumber(trx);
|
|
381
|
-
|
|
382
|
-
// Run any hooks before/after this batch
|
|
383
|
-
const beforeAll = this.config.beforeAll || (() => {});
|
|
384
|
-
const afterAll = this.config.afterAll || (() => {});
|
|
385
|
-
|
|
386
|
-
// Maintain the same return value shape as _waterfallBatch:
|
|
387
|
-
// [batchNo, [...migrationFile]]
|
|
388
|
-
//
|
|
389
|
-
// The result here affects at minimum the CLI handling, which
|
|
390
|
-
// will break if the return value does not conform to the
|
|
391
|
-
// expected shape.
|
|
392
|
-
//
|
|
393
|
-
// currently, batchNo is not actually consumed when the "log" array
|
|
394
|
-
// (second value of tuple) is empty; however, this default value
|
|
395
|
-
// accurately reflects what it _would_ have returned if no migrations
|
|
396
|
-
// were run.
|
|
397
|
-
let res = [batchNo, []];
|
|
398
|
-
if (migrations.length > 0) {
|
|
399
|
-
// this._waterfallBatch() records successful up migrations by batch number,
|
|
400
|
-
// or deletes the entry for successful down migrations. new entries should
|
|
401
|
-
// be monotonically-increasing, so increment the batch number when migrating
|
|
402
|
-
// up so that _waterfallBatch() records the correct value -- but don't
|
|
403
|
-
// increment it when migrating down, so that _waterfallBatch() removes the
|
|
404
|
-
// correct value.
|
|
405
|
-
if (direction === 'up') batchNo++;
|
|
406
|
-
|
|
407
|
-
await beforeAll(trx || this.knex, migrations);
|
|
408
|
-
res = await this._waterfallBatch(batchNo, migrations, direction, trx);
|
|
409
|
-
await afterAll(trx || this.knex, migrations);
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
await this._freeLock(canGetLockInTransaction ? trx : undefined);
|
|
413
|
-
return res;
|
|
414
|
-
} catch (error) {
|
|
415
|
-
let cleanupReady = Promise.resolve();
|
|
416
|
-
|
|
417
|
-
if (error instanceof LockError) {
|
|
418
|
-
// If locking error do not free the lock.
|
|
419
|
-
this.knex.client.logger.warn(
|
|
420
|
-
`Can't take lock to run migrations: ${error.message}`
|
|
421
|
-
);
|
|
422
|
-
this.knex.client.logger.warn(
|
|
423
|
-
'If you are sure migrations are not running you can release the ' +
|
|
424
|
-
"lock manually by running 'knex migrate:unlock'"
|
|
425
|
-
);
|
|
426
|
-
} else {
|
|
427
|
-
if (this._activeMigration.fileName) {
|
|
428
|
-
this.knex.client.logger.warn(
|
|
429
|
-
`migration file "${this._activeMigration.fileName}" failed`
|
|
430
|
-
);
|
|
431
|
-
}
|
|
432
|
-
this.knex.client.logger.warn(
|
|
433
|
-
`migration failed with error: ${error.message}`
|
|
434
|
-
);
|
|
435
|
-
// If the error was not due to a locking issue, then remove the lock.
|
|
436
|
-
cleanupReady = this._freeLock(
|
|
437
|
-
canGetLockInTransaction ? trx : undefined
|
|
438
|
-
);
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
try {
|
|
442
|
-
await cleanupReady;
|
|
443
|
-
// eslint-disable-next-line no-empty
|
|
444
|
-
} catch (e) {}
|
|
445
|
-
throw error;
|
|
446
|
-
}
|
|
447
|
-
}
|
|
448
|
-
|
|
449
|
-
// Validates some migrations by requiring and checking for an `up` and `down`
|
|
450
|
-
// function.
|
|
451
|
-
async _validateMigrationStructure(migration) {
|
|
452
|
-
const migrationName =
|
|
453
|
-
this.config.migrationSource.getMigrationName(migration);
|
|
454
|
-
// maybe promise
|
|
455
|
-
const migrationContent = await this.config.migrationSource.getMigration(
|
|
456
|
-
migration
|
|
457
|
-
);
|
|
458
|
-
if (
|
|
459
|
-
typeof migrationContent.up !== 'function' ||
|
|
460
|
-
typeof migrationContent.down !== 'function'
|
|
461
|
-
) {
|
|
462
|
-
throw new Error(
|
|
463
|
-
`Invalid migration: ${migrationName} must have both an up and down function`
|
|
464
|
-
);
|
|
465
|
-
}
|
|
466
|
-
|
|
467
|
-
return migration;
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
// Get the last batch of migrations, by name, ordered by insert id in reverse
|
|
471
|
-
// order.
|
|
472
|
-
async _getLastBatch([allMigrations]) {
|
|
473
|
-
const { tableName, schemaName } = this.config;
|
|
474
|
-
const migrationNames = await getTable(this.knex, tableName, schemaName)
|
|
475
|
-
.where('batch', function (qb) {
|
|
476
|
-
qb.max('batch').from(getTableName(tableName, schemaName));
|
|
477
|
-
})
|
|
478
|
-
.orderBy('id', 'desc');
|
|
479
|
-
|
|
480
|
-
const lastBatchMigrations = migrationNames.map((migration) => {
|
|
481
|
-
return allMigrations.find((entry) => {
|
|
482
|
-
return (
|
|
483
|
-
this.config.migrationSource.getMigrationName(entry) === migration.name
|
|
484
|
-
);
|
|
485
|
-
});
|
|
486
|
-
});
|
|
487
|
-
return Promise.all(lastBatchMigrations);
|
|
488
|
-
}
|
|
489
|
-
|
|
490
|
-
// Returns the latest batch number.
|
|
491
|
-
_latestBatchNumber(trx = this.knex) {
|
|
492
|
-
return trx
|
|
493
|
-
.from(getTableName(this.config.tableName, this.config.schemaName))
|
|
494
|
-
.max('batch as max_batch')
|
|
495
|
-
.then((obj) => obj[0].max_batch || 0);
|
|
496
|
-
}
|
|
497
|
-
|
|
498
|
-
// If transaction config for a single migration is defined, use that.
|
|
499
|
-
// Otherwise, rely on the common config. This allows enabling/disabling
|
|
500
|
-
// transaction for a single migration at will, regardless of the common
|
|
501
|
-
// config.
|
|
502
|
-
_useTransaction(migrationContent, allTransactionsDisabled) {
|
|
503
|
-
const singleTransactionValue = get(migrationContent, 'config.transaction');
|
|
504
|
-
|
|
505
|
-
const useTransaction = isBoolean(singleTransactionValue)
|
|
506
|
-
? singleTransactionValue
|
|
507
|
-
: !allTransactionsDisabled;
|
|
508
|
-
|
|
509
|
-
return useTransaction;
|
|
510
|
-
}
|
|
511
|
-
|
|
512
|
-
// Runs a batch of `migrations` in a specified `direction`, saving the
|
|
513
|
-
// appropriate database information as the migrations are run.
|
|
514
|
-
_waterfallBatch(batchNo, migrations, direction, trx) {
|
|
515
|
-
const trxOrKnex = trx || this.knex;
|
|
516
|
-
const { tableName, schemaName, disableTransactions } = this.config;
|
|
517
|
-
let current = Promise.resolve();
|
|
518
|
-
const log = [];
|
|
519
|
-
migrations.forEach((migration) => {
|
|
520
|
-
const name = this.config.migrationSource.getMigrationName(migration);
|
|
521
|
-
this._activeMigration.fileName = name;
|
|
522
|
-
const migrationContent =
|
|
523
|
-
this.config.migrationSource.getMigration(migration);
|
|
524
|
-
|
|
525
|
-
const beforeEach = this.config.beforeEach || (() => {});
|
|
526
|
-
const afterEach = this.config.afterEach || (() => {});
|
|
527
|
-
|
|
528
|
-
// We're going to run each of the migrations in the current "up".
|
|
529
|
-
current = current
|
|
530
|
-
.then(async () => await migrationContent) //maybe promise
|
|
531
|
-
.then(async (migrationContent) => {
|
|
532
|
-
this._activeMigration.fileName = name;
|
|
533
|
-
if (
|
|
534
|
-
!trx &&
|
|
535
|
-
this._useTransaction(migrationContent, disableTransactions)
|
|
536
|
-
) {
|
|
537
|
-
this.knex.enableProcessing();
|
|
538
|
-
return await this.knex.transaction(async (trx) => {
|
|
539
|
-
await beforeEach(trx, [migration]);
|
|
540
|
-
const migrationResult = await checkPromise(
|
|
541
|
-
this.knex.client.logger,
|
|
542
|
-
migrationContent[direction](trx),
|
|
543
|
-
name
|
|
544
|
-
);
|
|
545
|
-
await afterEach(trx, [migration]);
|
|
546
|
-
return migrationResult;
|
|
547
|
-
});
|
|
548
|
-
}
|
|
549
|
-
|
|
550
|
-
trxOrKnex.enableProcessing();
|
|
551
|
-
await beforeEach(trxOrKnex, [migration]);
|
|
552
|
-
const migrationResult = await checkPromise(
|
|
553
|
-
this.knex.client.logger,
|
|
554
|
-
migrationContent[direction](trxOrKnex),
|
|
555
|
-
name
|
|
556
|
-
);
|
|
557
|
-
await afterEach(trxOrKnex, [migration]);
|
|
558
|
-
return migrationResult;
|
|
559
|
-
})
|
|
560
|
-
.then(() => {
|
|
561
|
-
trxOrKnex.disableProcessing();
|
|
562
|
-
this.knex.disableProcessing();
|
|
563
|
-
log.push(name);
|
|
564
|
-
if (direction === 'up') {
|
|
565
|
-
return trxOrKnex.into(getTableName(tableName, schemaName)).insert({
|
|
566
|
-
name,
|
|
567
|
-
batch: batchNo,
|
|
568
|
-
migration_time: new Date(),
|
|
569
|
-
});
|
|
570
|
-
}
|
|
571
|
-
if (direction === 'down') {
|
|
572
|
-
return trxOrKnex
|
|
573
|
-
.from(getTableName(tableName, schemaName))
|
|
574
|
-
.where({ name })
|
|
575
|
-
.del();
|
|
576
|
-
}
|
|
577
|
-
});
|
|
578
|
-
});
|
|
579
|
-
|
|
580
|
-
return current.then(() => [batchNo, log]);
|
|
581
|
-
}
|
|
582
|
-
|
|
583
|
-
_transaction(knex, migrationContent, direction, name) {
|
|
584
|
-
return knex.transaction((trx) => {
|
|
585
|
-
return checkPromise(
|
|
586
|
-
knex.client.logger,
|
|
587
|
-
migrationContent[direction](trx),
|
|
588
|
-
name,
|
|
589
|
-
() => {
|
|
590
|
-
trx.commit();
|
|
591
|
-
}
|
|
592
|
-
);
|
|
593
|
-
});
|
|
594
|
-
}
|
|
595
|
-
}
|
|
596
|
-
|
|
597
|
-
// Validates that migrations are present in the appropriate directories.
|
|
598
|
-
function validateMigrationList(migrationSource, migrations) {
|
|
599
|
-
const [all, completed] = migrations;
|
|
600
|
-
const diff = getMissingMigrations(migrationSource, completed, all);
|
|
601
|
-
if (!isEmpty(diff)) {
|
|
602
|
-
const names = diff.map((d) => d.name);
|
|
603
|
-
throw new Error(
|
|
604
|
-
`The migration directory is corrupt, the following files are missing: ${names.join(
|
|
605
|
-
', '
|
|
606
|
-
)}`
|
|
607
|
-
);
|
|
608
|
-
}
|
|
609
|
-
}
|
|
610
|
-
|
|
611
|
-
function getMissingMigrations(migrationSource, completed, all) {
|
|
612
|
-
return differenceWith(completed, all, (c, a) => {
|
|
613
|
-
return c.name === migrationSource.getMigrationName(a);
|
|
614
|
-
});
|
|
615
|
-
}
|
|
616
|
-
|
|
617
|
-
function getNewMigrations(migrationSource, all, completed) {
|
|
618
|
-
return differenceWith(all, completed, (a, c) => {
|
|
619
|
-
return c.name === migrationSource.getMigrationName(a);
|
|
620
|
-
});
|
|
621
|
-
}
|
|
622
|
-
|
|
623
|
-
function checkPromise(logger, migrationPromise, name) {
|
|
624
|
-
if (!migrationPromise || typeof migrationPromise.then !== 'function') {
|
|
625
|
-
logger.warn(`migration ${name} did not return a promise`);
|
|
626
|
-
}
|
|
627
|
-
return migrationPromise;
|
|
628
|
-
}
|
|
629
|
-
|
|
630
|
-
module.exports = {
|
|
631
|
-
Migrator,
|
|
632
|
-
};
|
|
1
|
+
// Migrator
|
|
2
|
+
// -------
|
|
3
|
+
const differenceWith = require('lodash/differenceWith');
|
|
4
|
+
const get = require('lodash/get');
|
|
5
|
+
const isEmpty = require('lodash/isEmpty');
|
|
6
|
+
const max = require('lodash/max');
|
|
7
|
+
const {
|
|
8
|
+
getLockTableName,
|
|
9
|
+
getTable,
|
|
10
|
+
getTableName,
|
|
11
|
+
} = require('./table-resolver');
|
|
12
|
+
const { getSchemaBuilder } = require('./table-creator');
|
|
13
|
+
const migrationListResolver = require('./migration-list-resolver');
|
|
14
|
+
const MigrationGenerator = require('./MigrationGenerator');
|
|
15
|
+
const { getMergedConfig } = require('./migrator-configuration-merger');
|
|
16
|
+
const { isBoolean, isFunction } = require('../../util/is');
|
|
17
|
+
|
|
18
|
+
class LockError extends Error {
|
|
19
|
+
constructor(msg) {
|
|
20
|
+
super(msg);
|
|
21
|
+
this.name = 'MigrationLocked';
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// The new migration we're performing, typically called from the `knex.migrate`
|
|
26
|
+
// interface on the main `knex` object. Passes the `knex` instance performing
|
|
27
|
+
// the migration.
|
|
28
|
+
class Migrator {
|
|
29
|
+
constructor(knex) {
|
|
30
|
+
// Clone knex instance and remove post-processing that is unnecessary for internal queries from a cloned config
|
|
31
|
+
if (isFunction(knex)) {
|
|
32
|
+
if (!knex.isTransaction) {
|
|
33
|
+
this.knex = knex.withUserParams({
|
|
34
|
+
...knex.userParams,
|
|
35
|
+
});
|
|
36
|
+
} else {
|
|
37
|
+
this.knex = knex;
|
|
38
|
+
}
|
|
39
|
+
} else {
|
|
40
|
+
this.knex = Object.assign({}, knex);
|
|
41
|
+
this.knex.userParams = this.knex.userParams || {};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
this.config = getMergedConfig(
|
|
45
|
+
this.knex.client.config.migrations,
|
|
46
|
+
undefined,
|
|
47
|
+
this.knex.client.logger
|
|
48
|
+
);
|
|
49
|
+
this.generator = new MigrationGenerator(
|
|
50
|
+
this.knex.client.config.migrations,
|
|
51
|
+
this.knex.client.logger
|
|
52
|
+
);
|
|
53
|
+
this._activeMigration = {
|
|
54
|
+
fileName: null,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Migrators to the latest configuration.
|
|
59
|
+
async latest(config) {
|
|
60
|
+
this._disableProcessing();
|
|
61
|
+
this.config = getMergedConfig(config, this.config, this.knex.client.logger);
|
|
62
|
+
|
|
63
|
+
const allAndCompleted = await migrationListResolver.listAllAndCompleted(
|
|
64
|
+
this.config,
|
|
65
|
+
this.knex
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
if (!this.config.disableMigrationsListValidation) {
|
|
69
|
+
validateMigrationList(this.config.migrationSource, allAndCompleted);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const [all, completed] = allAndCompleted;
|
|
73
|
+
|
|
74
|
+
const migrations = getNewMigrations(
|
|
75
|
+
this.config.migrationSource,
|
|
76
|
+
all,
|
|
77
|
+
completed
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
const transactionForAll =
|
|
81
|
+
!this.config.disableTransactions &&
|
|
82
|
+
!(
|
|
83
|
+
await Promise.all(
|
|
84
|
+
migrations.map(async (migration) => {
|
|
85
|
+
const migrationContents =
|
|
86
|
+
await this.config.migrationSource.getMigration(migration);
|
|
87
|
+
return !this._useTransaction(migrationContents);
|
|
88
|
+
})
|
|
89
|
+
)
|
|
90
|
+
).some((isTransactionUsed) => isTransactionUsed);
|
|
91
|
+
|
|
92
|
+
if (transactionForAll) {
|
|
93
|
+
return this.knex.transaction((trx) => {
|
|
94
|
+
return this._runBatch(migrations, 'up', trx);
|
|
95
|
+
});
|
|
96
|
+
} else {
|
|
97
|
+
return this._runBatch(migrations, 'up');
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Runs the next migration that has not yet been run
|
|
102
|
+
async up(config) {
|
|
103
|
+
this._disableProcessing();
|
|
104
|
+
this.config = getMergedConfig(config, this.config, this.knex.client.logger);
|
|
105
|
+
|
|
106
|
+
const allAndCompleted = await migrationListResolver.listAllAndCompleted(
|
|
107
|
+
this.config,
|
|
108
|
+
this.knex
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
if (!this.config.disableMigrationsListValidation) {
|
|
112
|
+
validateMigrationList(this.config.migrationSource, allAndCompleted);
|
|
113
|
+
}
|
|
114
|
+
const [all, completed] = allAndCompleted;
|
|
115
|
+
|
|
116
|
+
const newMigrations = getNewMigrations(
|
|
117
|
+
this.config.migrationSource,
|
|
118
|
+
all,
|
|
119
|
+
completed
|
|
120
|
+
);
|
|
121
|
+
|
|
122
|
+
let migrationToRun;
|
|
123
|
+
const name = this.config.name;
|
|
124
|
+
if (name) {
|
|
125
|
+
if (!completed.map((m) => m.name).includes(name)) {
|
|
126
|
+
migrationToRun = newMigrations.find((migration) => {
|
|
127
|
+
return (
|
|
128
|
+
this.config.migrationSource.getMigrationName(migration) === name
|
|
129
|
+
);
|
|
130
|
+
});
|
|
131
|
+
if (!migrationToRun) {
|
|
132
|
+
throw new Error(`Migration "${name}" not found.`);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
} else {
|
|
136
|
+
migrationToRun = newMigrations[0];
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const useTransaction =
|
|
140
|
+
!migrationToRun ||
|
|
141
|
+
this._useTransaction(
|
|
142
|
+
await this.config.migrationSource.getMigration(migrationToRun)
|
|
143
|
+
);
|
|
144
|
+
|
|
145
|
+
const migrationsToRun = [];
|
|
146
|
+
if (migrationToRun) {
|
|
147
|
+
migrationsToRun.push(migrationToRun);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const transactionForAll =
|
|
151
|
+
!this.config.disableTransactions && (!migrationToRun || useTransaction);
|
|
152
|
+
|
|
153
|
+
if (transactionForAll) {
|
|
154
|
+
return await this.knex.transaction((trx) => {
|
|
155
|
+
return this._runBatch(migrationsToRun, 'up', trx);
|
|
156
|
+
});
|
|
157
|
+
} else {
|
|
158
|
+
return await this._runBatch(migrationsToRun, 'up');
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Rollback the last "batch", or all, of migrations that were run.
|
|
163
|
+
rollback(config, all = false) {
|
|
164
|
+
this._disableProcessing();
|
|
165
|
+
return new Promise((resolve, reject) => {
|
|
166
|
+
try {
|
|
167
|
+
this.config = getMergedConfig(
|
|
168
|
+
config,
|
|
169
|
+
this.config,
|
|
170
|
+
this.knex.client.logger
|
|
171
|
+
);
|
|
172
|
+
} catch (e) {
|
|
173
|
+
reject(e);
|
|
174
|
+
}
|
|
175
|
+
migrationListResolver
|
|
176
|
+
.listAllAndCompleted(this.config, this.knex)
|
|
177
|
+
.then((value) => {
|
|
178
|
+
if (!this.config.disableMigrationsListValidation) {
|
|
179
|
+
validateMigrationList(this.config.migrationSource, value);
|
|
180
|
+
}
|
|
181
|
+
return value;
|
|
182
|
+
})
|
|
183
|
+
.then((val) => {
|
|
184
|
+
const [allMigrations, completedMigrations] = val;
|
|
185
|
+
|
|
186
|
+
return all
|
|
187
|
+
? allMigrations
|
|
188
|
+
.filter((migration) => {
|
|
189
|
+
return completedMigrations
|
|
190
|
+
.map((migration) => migration.name)
|
|
191
|
+
.includes(
|
|
192
|
+
this.config.migrationSource.getMigrationName(migration)
|
|
193
|
+
);
|
|
194
|
+
})
|
|
195
|
+
.reverse()
|
|
196
|
+
: this._getLastBatch(val);
|
|
197
|
+
})
|
|
198
|
+
.then((migrations) => {
|
|
199
|
+
return this._runBatch(migrations, 'down');
|
|
200
|
+
})
|
|
201
|
+
.then(resolve, reject);
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
down(config) {
|
|
206
|
+
this._disableProcessing();
|
|
207
|
+
this.config = getMergedConfig(config, this.config, this.knex.client.logger);
|
|
208
|
+
|
|
209
|
+
return migrationListResolver
|
|
210
|
+
.listAllAndCompleted(this.config, this.knex)
|
|
211
|
+
.then((value) => {
|
|
212
|
+
if (!this.config.disableMigrationsListValidation) {
|
|
213
|
+
validateMigrationList(this.config.migrationSource, value);
|
|
214
|
+
}
|
|
215
|
+
return value;
|
|
216
|
+
})
|
|
217
|
+
.then(([all, completed]) => {
|
|
218
|
+
const completedMigrations = all.filter((migration) => {
|
|
219
|
+
return completed
|
|
220
|
+
.map((migration) => migration.name)
|
|
221
|
+
.includes(this.config.migrationSource.getMigrationName(migration));
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
let migrationToRun;
|
|
225
|
+
const name = this.config.name;
|
|
226
|
+
if (name) {
|
|
227
|
+
migrationToRun = completedMigrations.find((migration) => {
|
|
228
|
+
return (
|
|
229
|
+
this.config.migrationSource.getMigrationName(migration) === name
|
|
230
|
+
);
|
|
231
|
+
});
|
|
232
|
+
if (!migrationToRun) {
|
|
233
|
+
throw new Error(`Migration "${name}" was not run.`);
|
|
234
|
+
}
|
|
235
|
+
} else {
|
|
236
|
+
migrationToRun = completedMigrations[completedMigrations.length - 1];
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
const migrationsToRun = [];
|
|
240
|
+
if (migrationToRun) {
|
|
241
|
+
migrationsToRun.push(migrationToRun);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
return this._runBatch(migrationsToRun, 'down');
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
status(config) {
|
|
249
|
+
this._disableProcessing();
|
|
250
|
+
this.config = getMergedConfig(config, this.config, this.knex.client.logger);
|
|
251
|
+
|
|
252
|
+
return Promise.all([
|
|
253
|
+
getTable(this.knex, this.config.tableName, this.config.schemaName).select(
|
|
254
|
+
'*'
|
|
255
|
+
),
|
|
256
|
+
migrationListResolver.listAll(this.config.migrationSource),
|
|
257
|
+
]).then(([db, code]) => db.length - code.length);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// Retrieves and returns the current migration version we're on, as a promise.
|
|
261
|
+
// If no migrations have been run yet, return "none".
|
|
262
|
+
currentVersion(config) {
|
|
263
|
+
this._disableProcessing();
|
|
264
|
+
this.config = getMergedConfig(config, this.config, this.knex.client.logger);
|
|
265
|
+
|
|
266
|
+
return migrationListResolver
|
|
267
|
+
.listCompleted(this.config.tableName, this.config.schemaName, this.knex)
|
|
268
|
+
.then((completed) => {
|
|
269
|
+
const val = max(completed.map((value) => value.name.split('_')[0]));
|
|
270
|
+
return val === undefined ? 'none' : val;
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// list all migrations
|
|
275
|
+
async list(config) {
|
|
276
|
+
this._disableProcessing();
|
|
277
|
+
this.config = getMergedConfig(config, this.config, this.knex.client.logger);
|
|
278
|
+
|
|
279
|
+
const [all, completed] = await migrationListResolver.listAllAndCompleted(
|
|
280
|
+
this.config,
|
|
281
|
+
this.knex
|
|
282
|
+
);
|
|
283
|
+
|
|
284
|
+
if (!this.config.disableMigrationsListValidation) {
|
|
285
|
+
validateMigrationList(this.config.migrationSource, [all, completed]);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
const newMigrations = getNewMigrations(
|
|
289
|
+
this.config.migrationSource,
|
|
290
|
+
all,
|
|
291
|
+
completed
|
|
292
|
+
);
|
|
293
|
+
return [completed, newMigrations];
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
async forceFreeMigrationsLock(config) {
|
|
297
|
+
this._disableProcessing();
|
|
298
|
+
this.config = getMergedConfig(config, this.config, this.knex.client.logger);
|
|
299
|
+
const { schemaName, tableName } = this.config;
|
|
300
|
+
const lockTableName = getLockTableName(tableName);
|
|
301
|
+
const { knex } = this;
|
|
302
|
+
const getLockTable = () => getTable(knex, lockTableName, schemaName);
|
|
303
|
+
const tableExists = await getSchemaBuilder(knex, schemaName).hasTable(
|
|
304
|
+
lockTableName
|
|
305
|
+
);
|
|
306
|
+
if (tableExists) {
|
|
307
|
+
await getLockTable().del();
|
|
308
|
+
await getLockTable().insert({
|
|
309
|
+
is_locked: 0,
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
// Creates a new migration, with a given name.
|
|
315
|
+
make(name, config) {
|
|
316
|
+
return this.generator.make(name, config, this.knex.client.logger);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
_disableProcessing() {
|
|
320
|
+
if (this.knex.disableProcessing) {
|
|
321
|
+
this.knex.disableProcessing();
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
_lockMigrations(trx) {
|
|
326
|
+
const tableName = getLockTableName(this.config.tableName);
|
|
327
|
+
return getTable(this.knex, tableName, this.config.schemaName)
|
|
328
|
+
.transacting(trx)
|
|
329
|
+
.where('is_locked', '=', 0)
|
|
330
|
+
.update({ is_locked: 1 })
|
|
331
|
+
.then((rowCount) => {
|
|
332
|
+
if (rowCount !== 1) {
|
|
333
|
+
throw new Error('Migration table is already locked');
|
|
334
|
+
}
|
|
335
|
+
});
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
_getLock(trx) {
|
|
339
|
+
const transact = trx ? (fn) => fn(trx) : (fn) => this.knex.transaction(fn);
|
|
340
|
+
return transact((trx) => {
|
|
341
|
+
return this._lockMigrations(trx);
|
|
342
|
+
}).catch((err) => {
|
|
343
|
+
throw new LockError(err.message);
|
|
344
|
+
});
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
_freeLock(trx = this.knex) {
|
|
348
|
+
const tableName = getLockTableName(this.config.tableName);
|
|
349
|
+
return getTable(trx, tableName, this.config.schemaName).update({
|
|
350
|
+
is_locked: 0,
|
|
351
|
+
});
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
// Run a batch of current migrations, in sequence.
|
|
355
|
+
async _runBatch(migrations, direction, trx) {
|
|
356
|
+
const canGetLockInTransaction =
|
|
357
|
+
this.knex.client.driverName !== 'cockroachdb';
|
|
358
|
+
try {
|
|
359
|
+
await this._getLock(canGetLockInTransaction ? trx : undefined);
|
|
360
|
+
// When there is a wrapping transaction, some migrations
|
|
361
|
+
// could have been done while waiting for the lock:
|
|
362
|
+
const completed = trx
|
|
363
|
+
? await migrationListResolver.listCompleted(
|
|
364
|
+
this.config.tableName,
|
|
365
|
+
this.config.schemaName,
|
|
366
|
+
trx
|
|
367
|
+
)
|
|
368
|
+
: [];
|
|
369
|
+
|
|
370
|
+
migrations = getNewMigrations(
|
|
371
|
+
this.config.migrationSource,
|
|
372
|
+
migrations,
|
|
373
|
+
completed
|
|
374
|
+
);
|
|
375
|
+
|
|
376
|
+
await Promise.all(
|
|
377
|
+
migrations.map(this._validateMigrationStructure.bind(this))
|
|
378
|
+
);
|
|
379
|
+
|
|
380
|
+
let batchNo = await this._latestBatchNumber(trx);
|
|
381
|
+
|
|
382
|
+
// Run any hooks before/after this batch
|
|
383
|
+
const beforeAll = this.config.beforeAll || (() => {});
|
|
384
|
+
const afterAll = this.config.afterAll || (() => {});
|
|
385
|
+
|
|
386
|
+
// Maintain the same return value shape as _waterfallBatch:
|
|
387
|
+
// [batchNo, [...migrationFile]]
|
|
388
|
+
//
|
|
389
|
+
// The result here affects at minimum the CLI handling, which
|
|
390
|
+
// will break if the return value does not conform to the
|
|
391
|
+
// expected shape.
|
|
392
|
+
//
|
|
393
|
+
// currently, batchNo is not actually consumed when the "log" array
|
|
394
|
+
// (second value of tuple) is empty; however, this default value
|
|
395
|
+
// accurately reflects what it _would_ have returned if no migrations
|
|
396
|
+
// were run.
|
|
397
|
+
let res = [batchNo, []];
|
|
398
|
+
if (migrations.length > 0) {
|
|
399
|
+
// this._waterfallBatch() records successful up migrations by batch number,
|
|
400
|
+
// or deletes the entry for successful down migrations. new entries should
|
|
401
|
+
// be monotonically-increasing, so increment the batch number when migrating
|
|
402
|
+
// up so that _waterfallBatch() records the correct value -- but don't
|
|
403
|
+
// increment it when migrating down, so that _waterfallBatch() removes the
|
|
404
|
+
// correct value.
|
|
405
|
+
if (direction === 'up') batchNo++;
|
|
406
|
+
|
|
407
|
+
await beforeAll(trx || this.knex, migrations);
|
|
408
|
+
res = await this._waterfallBatch(batchNo, migrations, direction, trx);
|
|
409
|
+
await afterAll(trx || this.knex, migrations);
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
await this._freeLock(canGetLockInTransaction ? trx : undefined);
|
|
413
|
+
return res;
|
|
414
|
+
} catch (error) {
|
|
415
|
+
let cleanupReady = Promise.resolve();
|
|
416
|
+
|
|
417
|
+
if (error instanceof LockError) {
|
|
418
|
+
// If locking error do not free the lock.
|
|
419
|
+
this.knex.client.logger.warn(
|
|
420
|
+
`Can't take lock to run migrations: ${error.message}`
|
|
421
|
+
);
|
|
422
|
+
this.knex.client.logger.warn(
|
|
423
|
+
'If you are sure migrations are not running you can release the ' +
|
|
424
|
+
"lock manually by running 'knex migrate:unlock'"
|
|
425
|
+
);
|
|
426
|
+
} else {
|
|
427
|
+
if (this._activeMigration.fileName) {
|
|
428
|
+
this.knex.client.logger.warn(
|
|
429
|
+
`migration file "${this._activeMigration.fileName}" failed`
|
|
430
|
+
);
|
|
431
|
+
}
|
|
432
|
+
this.knex.client.logger.warn(
|
|
433
|
+
`migration failed with error: ${error.message}`
|
|
434
|
+
);
|
|
435
|
+
// If the error was not due to a locking issue, then remove the lock.
|
|
436
|
+
cleanupReady = this._freeLock(
|
|
437
|
+
canGetLockInTransaction ? trx : undefined
|
|
438
|
+
);
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
try {
|
|
442
|
+
await cleanupReady;
|
|
443
|
+
// eslint-disable-next-line no-empty
|
|
444
|
+
} catch (e) {}
|
|
445
|
+
throw error;
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
// Validates some migrations by requiring and checking for an `up` and `down`
|
|
450
|
+
// function.
|
|
451
|
+
async _validateMigrationStructure(migration) {
|
|
452
|
+
const migrationName =
|
|
453
|
+
this.config.migrationSource.getMigrationName(migration);
|
|
454
|
+
// maybe promise
|
|
455
|
+
const migrationContent = await this.config.migrationSource.getMigration(
|
|
456
|
+
migration
|
|
457
|
+
);
|
|
458
|
+
if (
|
|
459
|
+
typeof migrationContent.up !== 'function' ||
|
|
460
|
+
typeof migrationContent.down !== 'function'
|
|
461
|
+
) {
|
|
462
|
+
throw new Error(
|
|
463
|
+
`Invalid migration: ${migrationName} must have both an up and down function`
|
|
464
|
+
);
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
return migration;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
// Get the last batch of migrations, by name, ordered by insert id in reverse
|
|
471
|
+
// order.
|
|
472
|
+
async _getLastBatch([allMigrations]) {
|
|
473
|
+
const { tableName, schemaName } = this.config;
|
|
474
|
+
const migrationNames = await getTable(this.knex, tableName, schemaName)
|
|
475
|
+
.where('batch', function (qb) {
|
|
476
|
+
qb.max('batch').from(getTableName(tableName, schemaName));
|
|
477
|
+
})
|
|
478
|
+
.orderBy('id', 'desc');
|
|
479
|
+
|
|
480
|
+
const lastBatchMigrations = migrationNames.map((migration) => {
|
|
481
|
+
return allMigrations.find((entry) => {
|
|
482
|
+
return (
|
|
483
|
+
this.config.migrationSource.getMigrationName(entry) === migration.name
|
|
484
|
+
);
|
|
485
|
+
});
|
|
486
|
+
});
|
|
487
|
+
return Promise.all(lastBatchMigrations);
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
// Returns the latest batch number.
|
|
491
|
+
_latestBatchNumber(trx = this.knex) {
|
|
492
|
+
return trx
|
|
493
|
+
.from(getTableName(this.config.tableName, this.config.schemaName))
|
|
494
|
+
.max('batch as max_batch')
|
|
495
|
+
.then((obj) => obj[0].max_batch || 0);
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
// If transaction config for a single migration is defined, use that.
|
|
499
|
+
// Otherwise, rely on the common config. This allows enabling/disabling
|
|
500
|
+
// transaction for a single migration at will, regardless of the common
|
|
501
|
+
// config.
|
|
502
|
+
_useTransaction(migrationContent, allTransactionsDisabled) {
|
|
503
|
+
const singleTransactionValue = get(migrationContent, 'config.transaction');
|
|
504
|
+
|
|
505
|
+
const useTransaction = isBoolean(singleTransactionValue)
|
|
506
|
+
? singleTransactionValue
|
|
507
|
+
: !allTransactionsDisabled;
|
|
508
|
+
|
|
509
|
+
return useTransaction;
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
// Runs a batch of `migrations` in a specified `direction`, saving the
|
|
513
|
+
// appropriate database information as the migrations are run.
|
|
514
|
+
_waterfallBatch(batchNo, migrations, direction, trx) {
|
|
515
|
+
const trxOrKnex = trx || this.knex;
|
|
516
|
+
const { tableName, schemaName, disableTransactions } = this.config;
|
|
517
|
+
let current = Promise.resolve();
|
|
518
|
+
const log = [];
|
|
519
|
+
migrations.forEach((migration) => {
|
|
520
|
+
const name = this.config.migrationSource.getMigrationName(migration);
|
|
521
|
+
this._activeMigration.fileName = name;
|
|
522
|
+
const migrationContent =
|
|
523
|
+
this.config.migrationSource.getMigration(migration);
|
|
524
|
+
|
|
525
|
+
const beforeEach = this.config.beforeEach || (() => {});
|
|
526
|
+
const afterEach = this.config.afterEach || (() => {});
|
|
527
|
+
|
|
528
|
+
// We're going to run each of the migrations in the current "up".
|
|
529
|
+
current = current
|
|
530
|
+
.then(async () => await migrationContent) //maybe promise
|
|
531
|
+
.then(async (migrationContent) => {
|
|
532
|
+
this._activeMigration.fileName = name;
|
|
533
|
+
if (
|
|
534
|
+
!trx &&
|
|
535
|
+
this._useTransaction(migrationContent, disableTransactions)
|
|
536
|
+
) {
|
|
537
|
+
this.knex.enableProcessing();
|
|
538
|
+
return await this.knex.transaction(async (trx) => {
|
|
539
|
+
await beforeEach(trx, [migration]);
|
|
540
|
+
const migrationResult = await checkPromise(
|
|
541
|
+
this.knex.client.logger,
|
|
542
|
+
migrationContent[direction](trx),
|
|
543
|
+
name
|
|
544
|
+
);
|
|
545
|
+
await afterEach(trx, [migration]);
|
|
546
|
+
return migrationResult;
|
|
547
|
+
});
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
trxOrKnex.enableProcessing();
|
|
551
|
+
await beforeEach(trxOrKnex, [migration]);
|
|
552
|
+
const migrationResult = await checkPromise(
|
|
553
|
+
this.knex.client.logger,
|
|
554
|
+
migrationContent[direction](trxOrKnex),
|
|
555
|
+
name
|
|
556
|
+
);
|
|
557
|
+
await afterEach(trxOrKnex, [migration]);
|
|
558
|
+
return migrationResult;
|
|
559
|
+
})
|
|
560
|
+
.then(() => {
|
|
561
|
+
trxOrKnex.disableProcessing();
|
|
562
|
+
this.knex.disableProcessing();
|
|
563
|
+
log.push(name);
|
|
564
|
+
if (direction === 'up') {
|
|
565
|
+
return trxOrKnex.into(getTableName(tableName, schemaName)).insert({
|
|
566
|
+
name,
|
|
567
|
+
batch: batchNo,
|
|
568
|
+
migration_time: new Date(),
|
|
569
|
+
});
|
|
570
|
+
}
|
|
571
|
+
if (direction === 'down') {
|
|
572
|
+
return trxOrKnex
|
|
573
|
+
.from(getTableName(tableName, schemaName))
|
|
574
|
+
.where({ name })
|
|
575
|
+
.del();
|
|
576
|
+
}
|
|
577
|
+
});
|
|
578
|
+
});
|
|
579
|
+
|
|
580
|
+
return current.then(() => [batchNo, log]);
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
_transaction(knex, migrationContent, direction, name) {
|
|
584
|
+
return knex.transaction((trx) => {
|
|
585
|
+
return checkPromise(
|
|
586
|
+
knex.client.logger,
|
|
587
|
+
migrationContent[direction](trx),
|
|
588
|
+
name,
|
|
589
|
+
() => {
|
|
590
|
+
trx.commit();
|
|
591
|
+
}
|
|
592
|
+
);
|
|
593
|
+
});
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
// Validates that migrations are present in the appropriate directories.
|
|
598
|
+
function validateMigrationList(migrationSource, migrations) {
|
|
599
|
+
const [all, completed] = migrations;
|
|
600
|
+
const diff = getMissingMigrations(migrationSource, completed, all);
|
|
601
|
+
if (!isEmpty(diff)) {
|
|
602
|
+
const names = diff.map((d) => d.name);
|
|
603
|
+
throw new Error(
|
|
604
|
+
`The migration directory is corrupt, the following files are missing: ${names.join(
|
|
605
|
+
', '
|
|
606
|
+
)}`
|
|
607
|
+
);
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
function getMissingMigrations(migrationSource, completed, all) {
|
|
612
|
+
return differenceWith(completed, all, (c, a) => {
|
|
613
|
+
return c.name === migrationSource.getMigrationName(a);
|
|
614
|
+
});
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
function getNewMigrations(migrationSource, all, completed) {
|
|
618
|
+
return differenceWith(all, completed, (a, c) => {
|
|
619
|
+
return c.name === migrationSource.getMigrationName(a);
|
|
620
|
+
});
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
function checkPromise(logger, migrationPromise, name) {
|
|
624
|
+
if (!migrationPromise || typeof migrationPromise.then !== 'function') {
|
|
625
|
+
logger.warn(`migration ${name} did not return a promise`);
|
|
626
|
+
}
|
|
627
|
+
return migrationPromise;
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
module.exports = {
|
|
631
|
+
Migrator,
|
|
632
|
+
};
|