knex-migrator 5.4.0 → 6.0.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 +176 -251
- package/bin/knex-migrator +2 -2
- package/bin/knex-migrator-health +29 -24
- package/bin/knex-migrator-init +34 -28
- package/bin/knex-migrator-migrate +38 -32
- package/bin/knex-migrator-reset +31 -26
- package/bin/knex-migrator-rollback +28 -23
- package/lib/database.js +158 -92
- package/lib/errors.js +83 -38
- package/lib/index.js +455 -438
- package/lib/locking.js +73 -73
- package/lib/utils.js +37 -34
- package/loggingrc.js +2 -2
- package/migrations/add-primary-key-to-lock-table.js +23 -23
- package/migrations/field-length.js +13 -12
- package/migrations/index.js +5 -6
- package/migrations/lock-table.js +29 -29
- package/migrations/use-index.js +14 -14
- package/package.json +29 -26
- package/CHANGELOG.md +0 -171
- package/test/config/env/config.testing-better-sqlite3.json +0 -9
- package/test/config/env/config.testing-mysql.json +0 -12
- package/test/config/env/config.testing.json +0 -9
|
@@ -1,38 +1,44 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
utils.getKnexMigrator({path: process.cwd()})
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
return knexMigrator.migrate({
|
|
27
|
-
version: program.v,
|
|
28
|
-
only: program.only,
|
|
29
|
-
force: program.force,
|
|
30
|
-
init: program.init
|
|
31
|
-
}).then(function () {
|
|
32
|
-
logging.info('Finished database migration!');
|
|
3
|
+
const program = require('commander').program;
|
|
4
|
+
const utils = require('../lib/utils');
|
|
5
|
+
|
|
6
|
+
const logging = require('@tryghost/logging');
|
|
7
|
+
|
|
8
|
+
async function main() {
|
|
9
|
+
const KnexMigrator = await utils.getKnexMigrator({path: process.cwd()});
|
|
10
|
+
|
|
11
|
+
program
|
|
12
|
+
.option('--v <item>')
|
|
13
|
+
.option('--only <item>')
|
|
14
|
+
.option('--mgpath <path>')
|
|
15
|
+
.option('--force')
|
|
16
|
+
.option('--init')
|
|
17
|
+
.parse(process.argv);
|
|
18
|
+
|
|
19
|
+
const options = program.opts();
|
|
20
|
+
let knexMigrator;
|
|
21
|
+
|
|
22
|
+
try {
|
|
23
|
+
knexMigrator = new KnexMigrator({
|
|
24
|
+
knexMigratorFilePath: options.mgpath,
|
|
25
|
+
executedFromShell: true,
|
|
33
26
|
});
|
|
34
|
-
})
|
|
35
|
-
|
|
27
|
+
} catch (err) {
|
|
28
|
+
logging.error(err);
|
|
29
|
+
process.exit(1);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
await knexMigrator.migrate({
|
|
33
|
+
version: options.v,
|
|
34
|
+
only: options.only,
|
|
35
|
+
force: options.force,
|
|
36
|
+
init: options.init,
|
|
37
|
+
});
|
|
38
|
+
logging.info('Finished database migration!');
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
main().catch(function (err) {
|
|
36
42
|
logging.error(err);
|
|
37
43
|
process.exit(1);
|
|
38
44
|
});
|
package/bin/knex-migrator-reset
CHANGED
|
@@ -1,31 +1,36 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
utils.getKnexMigrator({path: process.cwd()})
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
}
|
|
28
|
-
|
|
3
|
+
const program = require('commander').program;
|
|
4
|
+
const utils = require('../lib/utils');
|
|
5
|
+
|
|
6
|
+
const logging = require('@tryghost/logging');
|
|
7
|
+
|
|
8
|
+
async function main() {
|
|
9
|
+
const KnexMigrator = await utils.getKnexMigrator({path: process.cwd()});
|
|
10
|
+
|
|
11
|
+
program
|
|
12
|
+
.option('--mgpath <path>')
|
|
13
|
+
.option('--force')
|
|
14
|
+
.parse(process.argv);
|
|
15
|
+
|
|
16
|
+
const options = program.opts();
|
|
17
|
+
let knexMigrator;
|
|
18
|
+
|
|
19
|
+
try {
|
|
20
|
+
knexMigrator = new KnexMigrator({
|
|
21
|
+
knexMigratorFilePath: options.mgpath,
|
|
22
|
+
executedFromShell: true,
|
|
23
|
+
});
|
|
24
|
+
} catch (err) {
|
|
25
|
+
logging.error(err);
|
|
26
|
+
process.exit(1);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
await knexMigrator.reset({force: options.force});
|
|
30
|
+
logging.info('Finished database reset!');
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
main().catch(function (err) {
|
|
29
34
|
logging.error(err);
|
|
30
35
|
process.exit(1);
|
|
31
36
|
});
|
|
@@ -1,32 +1,37 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
const program = require('commander');
|
|
3
|
+
const program = require('commander').program;
|
|
4
4
|
const utils = require('../lib/utils');
|
|
5
5
|
|
|
6
6
|
const logging = require('@tryghost/logging');
|
|
7
|
-
let knexMigrator;
|
|
8
|
-
|
|
9
|
-
utils.getKnexMigrator({path: process.cwd()})
|
|
10
|
-
.then(function (KnexMigrator) {
|
|
11
|
-
program
|
|
12
|
-
.option('--mgpath <path>')
|
|
13
|
-
.option('--force')
|
|
14
|
-
.option('--v <version>', 'The version to rollback to.')
|
|
15
|
-
.parse(process.argv);
|
|
16
|
-
|
|
17
|
-
try {
|
|
18
|
-
knexMigrator = new KnexMigrator({knexMigratorFilePath: program.mgpath, executedFromShell: true});
|
|
19
|
-
} catch (err) {
|
|
20
|
-
logging.error(err);
|
|
21
|
-
process.exit(1);
|
|
22
|
-
}
|
|
23
7
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
8
|
+
async function main() {
|
|
9
|
+
const KnexMigrator = await utils.getKnexMigrator({path: process.cwd()});
|
|
10
|
+
|
|
11
|
+
program
|
|
12
|
+
.option('--mgpath <path>')
|
|
13
|
+
.option('--force')
|
|
14
|
+
.option('--v <version>', 'The version to rollback to.')
|
|
15
|
+
.parse(process.argv);
|
|
16
|
+
|
|
17
|
+
const options = program.opts();
|
|
18
|
+
let knexMigrator;
|
|
19
|
+
|
|
20
|
+
try {
|
|
21
|
+
knexMigrator = new KnexMigrator({
|
|
22
|
+
knexMigratorFilePath: options.mgpath,
|
|
23
|
+
executedFromShell: true,
|
|
24
|
+
});
|
|
25
|
+
} catch (err) {
|
|
26
|
+
logging.error(err);
|
|
27
|
+
process.exit(1);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
await knexMigrator.rollback({force: options.force, version: options.v});
|
|
31
|
+
logging.info('Rollback was successful.');
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
main().catch(function (err) {
|
|
30
35
|
logging.error(err.message);
|
|
31
36
|
|
|
32
37
|
if (err.help) {
|
package/lib/database.js
CHANGED
|
@@ -1,9 +1,51 @@
|
|
|
1
|
-
const
|
|
1
|
+
const path = require('path'),
|
|
2
|
+
bundledKnex = require('knex'),
|
|
2
3
|
omit = require('lodash/omit'),
|
|
3
4
|
debug = require('debug')('knex-migrator:database'),
|
|
4
5
|
errors = require('./errors');
|
|
5
6
|
const DatabaseInfo = require('@tryghost/database-info');
|
|
6
|
-
const {sequence} = require('@tryghost/promise');
|
|
7
|
+
const { sequence } = require('@tryghost/promise');
|
|
8
|
+
|
|
9
|
+
function destroyConnection(connection) {
|
|
10
|
+
return new Promise(function (resolve, reject) {
|
|
11
|
+
connection.destroy(function (err) {
|
|
12
|
+
if (err) {
|
|
13
|
+
reject(err);
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
resolve();
|
|
18
|
+
});
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function loadKnex(options) {
|
|
23
|
+
options = options || {};
|
|
24
|
+
|
|
25
|
+
if (options.knexModulePath) {
|
|
26
|
+
let resolvedKnexPath;
|
|
27
|
+
|
|
28
|
+
try {
|
|
29
|
+
resolvedKnexPath = require.resolve('knex', {
|
|
30
|
+
paths: [path.resolve(options.knexModulePath)],
|
|
31
|
+
});
|
|
32
|
+
} catch (err) {
|
|
33
|
+
if (err.code !== 'MODULE_NOT_FOUND') {
|
|
34
|
+
throw err;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
debug(
|
|
38
|
+
'Could not load project knex from ' + options.knexModulePath + ': ' + err.message,
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (resolvedKnexPath) {
|
|
43
|
+
return require(resolvedKnexPath);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return bundledKnex;
|
|
48
|
+
}
|
|
7
49
|
|
|
8
50
|
/**
|
|
9
51
|
* @NOTE: Knex-migrator only supports knex query builder.
|
|
@@ -11,7 +53,7 @@ const {sequence} = require('@tryghost/promise');
|
|
|
11
53
|
* @param options
|
|
12
54
|
* @returns {Knex.QueryBuilder | Knex}
|
|
13
55
|
*/
|
|
14
|
-
exports.connect = function connect(options) {
|
|
56
|
+
exports.connect = function connect(options, connectionOptions) {
|
|
15
57
|
options = options || {};
|
|
16
58
|
|
|
17
59
|
// Alias `mysql` to `mysql2` so we can maintain backwards compatibility
|
|
@@ -19,9 +61,14 @@ exports.connect = function connect(options) {
|
|
|
19
61
|
options.client = 'mysql2';
|
|
20
62
|
}
|
|
21
63
|
|
|
64
|
+
// Alias `sqlite3` to `better-sqlite3` so we can maintain backwards compatibility
|
|
65
|
+
if (options.client === 'sqlite3') {
|
|
66
|
+
options.client = 'better-sqlite3';
|
|
67
|
+
}
|
|
68
|
+
|
|
22
69
|
const client = options.client;
|
|
23
70
|
|
|
24
|
-
if (client === 'sqlite3') {
|
|
71
|
+
if (client === 'better-sqlite3') {
|
|
25
72
|
options.useNullAsDefault = options.useNullAsDefault || false;
|
|
26
73
|
}
|
|
27
74
|
|
|
@@ -33,7 +80,7 @@ exports.connect = function connect(options) {
|
|
|
33
80
|
delete options.connection.filename;
|
|
34
81
|
}
|
|
35
82
|
|
|
36
|
-
return
|
|
83
|
+
return loadKnex(connectionOptions)(options);
|
|
37
84
|
};
|
|
38
85
|
|
|
39
86
|
/**
|
|
@@ -43,23 +90,24 @@ exports.connect = function connect(options) {
|
|
|
43
90
|
* @param connection
|
|
44
91
|
* @returns {Promise<R> | Promise<any> | Promise<T>}
|
|
45
92
|
*/
|
|
46
|
-
exports.ensureConnectionWorks = (connection)
|
|
47
|
-
|
|
48
|
-
.
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
message: 'Invalid database host.',
|
|
52
|
-
help: 'Please double check your database config.',
|
|
53
|
-
err: err
|
|
54
|
-
});
|
|
55
|
-
}
|
|
56
|
-
|
|
93
|
+
exports.ensureConnectionWorks = async function ensureConnectionWorks(connection) {
|
|
94
|
+
try {
|
|
95
|
+
await connection.raw('SELECT 1+1 as RESULT;');
|
|
96
|
+
} catch (err) {
|
|
97
|
+
if (err.code === 'ENOTFOUND' || err.code === 'ETIMEDOUT' || err.code === 'EAI_AGAIN') {
|
|
57
98
|
throw new errors.DatabaseError({
|
|
58
|
-
message:
|
|
59
|
-
help: '
|
|
60
|
-
err: err
|
|
99
|
+
message: 'Invalid database host.',
|
|
100
|
+
help: 'Please double check your database config.',
|
|
101
|
+
err: err,
|
|
61
102
|
});
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
throw new errors.DatabaseError({
|
|
106
|
+
message: err.message,
|
|
107
|
+
help: 'Unknown database error',
|
|
108
|
+
err: err,
|
|
62
109
|
});
|
|
110
|
+
}
|
|
63
111
|
};
|
|
64
112
|
|
|
65
113
|
/**
|
|
@@ -101,54 +149,52 @@ exports.createMigrationsTable = async function createMigrationsTable(connection)
|
|
|
101
149
|
* @param dbConfig
|
|
102
150
|
* @returns {*}
|
|
103
151
|
*/
|
|
104
|
-
exports.createDatabaseIfNotExist = function createDatabaseIfNotExist(
|
|
105
|
-
|
|
106
|
-
|
|
152
|
+
exports.createDatabaseIfNotExist = async function createDatabaseIfNotExist(
|
|
153
|
+
dbConfig,
|
|
154
|
+
connectionOptions,
|
|
155
|
+
) {
|
|
156
|
+
const name = dbConfig.connection.database;
|
|
157
|
+
const charset = dbConfig.connection.charset || 'utf8mb4';
|
|
107
158
|
|
|
108
159
|
// @NOTE: Skip, because sqlite3 is a file based database.
|
|
109
160
|
if (DatabaseInfo.isSQLiteConfig(dbConfig)) {
|
|
110
|
-
return
|
|
111
|
-
} else if (!DatabaseInfo.isMySQLConfig(dbConfig)) {
|
|
112
|
-
return Promise.reject(new errors.KnexMigrateError({
|
|
113
|
-
message: 'Database is not supported.'
|
|
114
|
-
}));
|
|
161
|
+
return;
|
|
115
162
|
}
|
|
116
163
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
164
|
+
if (!DatabaseInfo.isMySQLConfig(dbConfig)) {
|
|
165
|
+
throw new errors.KnexMigrateError({
|
|
166
|
+
message: 'Database is not supported.',
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const connection = exports.connect(
|
|
171
|
+
{
|
|
172
|
+
client: dbConfig.client,
|
|
173
|
+
connection: omit(dbConfig.connection, ['database']),
|
|
174
|
+
},
|
|
175
|
+
connectionOptions,
|
|
176
|
+
);
|
|
121
177
|
|
|
122
178
|
debug('Create database', name);
|
|
123
179
|
|
|
124
|
-
|
|
125
|
-
.
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
}
|
|
180
|
+
try {
|
|
181
|
+
await exports.ensureConnectionWorks(connection);
|
|
182
|
+
await connection.raw('CREATE DATABASE `' + name + '` CHARACTER SET ' + charset + ';');
|
|
183
|
+
} catch (err) {
|
|
184
|
+
// CASE: DB exists
|
|
185
|
+
if (err.errno === 1007) {
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
133
188
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
});
|
|
139
|
-
})
|
|
140
|
-
.finally(function () {
|
|
141
|
-
return new Promise(function (resolve, reject) {
|
|
142
|
-
connection.destroy(function (err) {
|
|
143
|
-
if (err) {
|
|
144
|
-
return reject(err);
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
debug('Destroy connection');
|
|
148
|
-
resolve();
|
|
149
|
-
});
|
|
150
|
-
});
|
|
189
|
+
throw new errors.DatabaseError({
|
|
190
|
+
message: err.message,
|
|
191
|
+
err: err,
|
|
192
|
+
code: 'DATABASE_CREATION_FAILED',
|
|
151
193
|
});
|
|
194
|
+
} finally {
|
|
195
|
+
await destroyConnection(connection);
|
|
196
|
+
debug('Destroy connection');
|
|
197
|
+
}
|
|
152
198
|
};
|
|
153
199
|
|
|
154
200
|
/**
|
|
@@ -157,54 +203,74 @@ exports.createDatabaseIfNotExist = function createDatabaseIfNotExist(dbConfig) {
|
|
|
157
203
|
* @param options
|
|
158
204
|
* @returns {*}
|
|
159
205
|
*/
|
|
160
|
-
exports.drop = function drop(options) {
|
|
206
|
+
exports.drop = async function drop(options) {
|
|
161
207
|
options = options || {};
|
|
162
208
|
|
|
163
|
-
const connection = options.connection
|
|
164
|
-
|
|
209
|
+
const connection = options.connection;
|
|
210
|
+
const dbConfig = options.dbConfig;
|
|
165
211
|
|
|
166
212
|
if (DatabaseInfo.isMySQL(connection)) {
|
|
167
213
|
debug('Drop database: ' + dbConfig.connection.database);
|
|
168
214
|
|
|
169
|
-
|
|
170
|
-
.
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
215
|
+
try {
|
|
216
|
+
await connection.raw('DROP DATABASE `' + dbConfig.connection.database + '`;');
|
|
217
|
+
} catch (err) {
|
|
218
|
+
// CASE: database does not exist, skip
|
|
219
|
+
if (err.errno === 1049) {
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
175
222
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
}));
|
|
223
|
+
throw new errors.KnexMigrateError({
|
|
224
|
+
err: err,
|
|
179
225
|
});
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
if (DatabaseInfo.isSQLite(connection)) {
|
|
232
|
+
// @NOTE: SQLite does not support "DROP DATABASE". We have to drop each table instead.
|
|
233
|
+
// @NOTE: We cannot just remove the database file, because any database connection will get invalid.
|
|
234
|
+
// @NOTE: better-sqlite3 (which `sqlite3` is aliased to) enforces foreign key constraints, so
|
|
235
|
+
// dropping a parent table before its referencing children trips an FK error. Disable foreign
|
|
236
|
+
// keys for the duration of the drop and restore afterwards. SQLite uses a single connection
|
|
237
|
+
// here, so the PRAGMA persists across the drops.
|
|
238
|
+
try {
|
|
239
|
+
await connection.raw('PRAGMA foreign_keys = OFF;');
|
|
240
|
+
|
|
241
|
+
const tables = await connection.raw(
|
|
242
|
+
`SELECT name FROM sqlite_master WHERE type='table';`,
|
|
243
|
+
);
|
|
244
|
+
|
|
245
|
+
await sequence(
|
|
246
|
+
tables.map((table) => async () => {
|
|
186
247
|
if (table.name === 'sqlite_sequence') {
|
|
187
248
|
debug('Skip drop table: ' + table.name);
|
|
188
|
-
return
|
|
249
|
+
return;
|
|
189
250
|
}
|
|
190
251
|
|
|
191
252
|
debug('Drop table: ' + table.name);
|
|
192
|
-
|
|
193
|
-
})
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
}));
|
|
253
|
+
await connection.schema.dropTableIfExists(table.name);
|
|
254
|
+
}),
|
|
255
|
+
);
|
|
256
|
+
} catch (err) {
|
|
257
|
+
// CASE: database file was never initialised
|
|
258
|
+
if (err.errno === 10) {
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
throw new errors.KnexMigrateError({
|
|
263
|
+
err: err,
|
|
204
264
|
});
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
}
|
|
265
|
+
} finally {
|
|
266
|
+
// Best-effort restore; never let a failed PRAGMA mask the drop result.
|
|
267
|
+
await connection.raw('PRAGMA foreign_keys = ON;').catch(function () {});
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
return;
|
|
209
271
|
}
|
|
272
|
+
|
|
273
|
+
throw new errors.KnexMigrateError({
|
|
274
|
+
message: 'Database client not supported: ' + dbConfig.client,
|
|
275
|
+
});
|
|
210
276
|
};
|