knex 3.2.10 → 3.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/CHANGELOG.md +35 -1
- package/README.md +2 -2
- package/bin/cli.js +40 -12
- package/lib/client.js +78 -7
- package/lib/constants.js +2 -0
- package/lib/dialects/index.js +1 -0
- package/lib/dialects/mariadb/index.js +55 -0
- package/lib/dialects/mariadb/query/mariadb-querycompiler.js +98 -0
- package/lib/dialects/mariadb/schema/mariadb-columncompiler.js +17 -0
- package/lib/dialects/mariadb/transaction.js +44 -0
- package/lib/dialects/mssql/index.js +3 -0
- package/lib/dialects/mssql/query/mssql-querycompiler.js +4 -4
- package/lib/dialects/mysql/index.js +42 -0
- package/lib/dialects/mysql/query/mysql-querycompiler.js +11 -8
- package/lib/dialects/mysql2/index.js +7 -0
- package/lib/dialects/oracledb/index.js +45 -1
- package/lib/dialects/postgres/index.js +36 -0
- package/lib/dialects/postgres/query/pg-querycompiler.js +30 -35
- package/lib/dialects/sqlite3/query/sqlite-querycompiler.js +2 -0
- package/lib/execution/runner.js +8 -2
- package/lib/formatter/wrappingFormatter.js +52 -13
- package/lib/knex-builder/FunctionHelper.js +1 -0
- package/lib/knex-builder/Knex.js +2 -0
- package/lib/knex-builder/internal/config-resolver.js +7 -0
- package/lib/migrations/migrate/Migrator.js +130 -0
- package/lib/pool.js +24 -0
- package/lib/schema/tablecompiler.js +1 -0
- package/package.json +14 -6
- package/types/index.d.ts +78 -4
|
@@ -4,6 +4,7 @@ const each = require('lodash/each');
|
|
|
4
4
|
const flatten = require('lodash/flatten');
|
|
5
5
|
const isEmpty = require('lodash/isEmpty');
|
|
6
6
|
const map = require('lodash/map');
|
|
7
|
+
const uniqueId = require('lodash/uniqueId');
|
|
7
8
|
|
|
8
9
|
const Formatter = require('../../formatter');
|
|
9
10
|
const QueryCompiler = require('./query/oracledb-querycompiler');
|
|
@@ -165,8 +166,51 @@ class Client_Oracledb extends Client_Oracle {
|
|
|
165
166
|
|
|
166
167
|
// Used to explicitly close a connection, called internally by the pool
|
|
167
168
|
// when a connection times out or the pool is shutdown.
|
|
169
|
+
/**
|
|
170
|
+
* @param {import('oracledb').Connection} connection
|
|
171
|
+
*/
|
|
168
172
|
destroyRawConnection(connection) {
|
|
169
|
-
return connection.release();
|
|
173
|
+
return connection.release({ drop: true });
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Wraps a native oracledb pool with the interface knex expects from this.pool.
|
|
177
|
+
/**
|
|
178
|
+
* @param {import('oracledb').Pool} nativePool
|
|
179
|
+
*/
|
|
180
|
+
wrapNativePool(nativePool) {
|
|
181
|
+
const client = this;
|
|
182
|
+
return {
|
|
183
|
+
acquire() {
|
|
184
|
+
return {
|
|
185
|
+
promise: nativePool.getConnection().then((connection) => {
|
|
186
|
+
connection.__knexUid = uniqueId('__knexUid');
|
|
187
|
+
monkeyPatchConnection(connection, client);
|
|
188
|
+
return connection;
|
|
189
|
+
}),
|
|
190
|
+
};
|
|
191
|
+
},
|
|
192
|
+
/**
|
|
193
|
+
* @param {import('oracledb').Connection} connection
|
|
194
|
+
*/
|
|
195
|
+
release(connection) {
|
|
196
|
+
// connection.close() returns the connection to the native pool
|
|
197
|
+
// (or closes it if standalone). Works for both healthy and disposed connections.
|
|
198
|
+
if (typeof connection.close === 'function') {
|
|
199
|
+
const opts = connection.__knex__disposed ? { drop: true } : {};
|
|
200
|
+
connection.close(opts, (err) => {
|
|
201
|
+
if (err) {
|
|
202
|
+
console.error(`Error closing oracle connection`, err);
|
|
203
|
+
}
|
|
204
|
+
});
|
|
205
|
+
return true;
|
|
206
|
+
}
|
|
207
|
+
return false;
|
|
208
|
+
},
|
|
209
|
+
destroy() {
|
|
210
|
+
// No-op: knex does not own the native pool
|
|
211
|
+
return Promise.resolve();
|
|
212
|
+
},
|
|
213
|
+
};
|
|
170
214
|
}
|
|
171
215
|
|
|
172
216
|
// Handle oracle version resolution on acquiring connection from pool instead of connection creation.
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
// -------
|
|
3
3
|
const extend = require('lodash/extend');
|
|
4
4
|
const map = require('lodash/map');
|
|
5
|
+
const uniqueId = require('lodash/uniqueId');
|
|
5
6
|
const { promisify } = require('util');
|
|
6
7
|
const Client = require('../../client');
|
|
7
8
|
|
|
@@ -120,6 +121,41 @@ class Client_PG extends Client {
|
|
|
120
121
|
return end();
|
|
121
122
|
}
|
|
122
123
|
|
|
124
|
+
// Wraps a native pg.Pool with the interface knex expects from this.pool.
|
|
125
|
+
wrapNativePool(nativePool) {
|
|
126
|
+
const client = this;
|
|
127
|
+
return {
|
|
128
|
+
acquire() {
|
|
129
|
+
return {
|
|
130
|
+
promise: nativePool.connect().then(async (connection) => {
|
|
131
|
+
connection.__knexUid = uniqueId('__knexUid');
|
|
132
|
+
if (!client.version) {
|
|
133
|
+
try {
|
|
134
|
+
client.version = await client.checkVersion(connection);
|
|
135
|
+
} catch (_e) {
|
|
136
|
+
// version check is best-effort
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return connection;
|
|
140
|
+
}),
|
|
141
|
+
};
|
|
142
|
+
},
|
|
143
|
+
release(connection) {
|
|
144
|
+
if (typeof connection.release === 'function') {
|
|
145
|
+
// pg: release(true) destroys the connection instead of returning to pool
|
|
146
|
+
const shouldDestroy = !!connection.__knex__disposed;
|
|
147
|
+
connection.release(shouldDestroy);
|
|
148
|
+
return true;
|
|
149
|
+
}
|
|
150
|
+
return false;
|
|
151
|
+
},
|
|
152
|
+
destroy() {
|
|
153
|
+
// No-op: knex does not own the native pool
|
|
154
|
+
return Promise.resolve();
|
|
155
|
+
},
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
|
|
123
159
|
// In PostgreSQL, we need to do a version check to do some feature
|
|
124
160
|
// checking on the database.
|
|
125
161
|
checkVersion(connection) {
|
|
@@ -49,14 +49,15 @@ class QueryCompiler_PG extends QueryCompiler {
|
|
|
49
49
|
update() {
|
|
50
50
|
const withSQL = this.with();
|
|
51
51
|
const updateData = this._prepUpdate(this.single.update);
|
|
52
|
-
const wheres = this.where();
|
|
53
52
|
const { returning, updateFrom } = this.single;
|
|
53
|
+
const updateFromSQL = this._updateFrom(updateFrom);
|
|
54
|
+
const wheres = this.where();
|
|
54
55
|
return {
|
|
55
56
|
sql:
|
|
56
57
|
withSQL +
|
|
57
58
|
`update ${this.single.only ? 'only ' : ''}${this.tableName} ` +
|
|
58
59
|
`set ${updateData.join(', ')}` +
|
|
59
|
-
|
|
60
|
+
updateFromSQL +
|
|
60
61
|
(wheres ? ` ${wheres}` : '') +
|
|
61
62
|
this._returning(returning),
|
|
62
63
|
returning,
|
|
@@ -84,11 +85,11 @@ class QueryCompiler_PG extends QueryCompiler {
|
|
|
84
85
|
// Make sure tableName is processed by the formatter first.
|
|
85
86
|
const { tableName } = this;
|
|
86
87
|
const withSQL = this.with();
|
|
87
|
-
let wheres = this.where() || '';
|
|
88
88
|
let using = this.using() || '';
|
|
89
89
|
const joins = this.grouped.join;
|
|
90
90
|
|
|
91
91
|
const tableJoins = [];
|
|
92
|
+
const joinWhereStatements = [];
|
|
92
93
|
if (Array.isArray(joins)) {
|
|
93
94
|
for (const join of joins) {
|
|
94
95
|
tableJoins.push(
|
|
@@ -101,19 +102,13 @@ class QueryCompiler_PG extends QueryCompiler {
|
|
|
101
102
|
)
|
|
102
103
|
);
|
|
103
104
|
|
|
104
|
-
const joinWheres = [];
|
|
105
105
|
for (const clause of join.clauses) {
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
})
|
|
113
|
-
);
|
|
114
|
-
}
|
|
115
|
-
if (joinWheres.length > 0) {
|
|
116
|
-
wheres += (wheres ? ' and ' : 'where ') + joinWheres.join(' and ');
|
|
106
|
+
joinWhereStatements.push({
|
|
107
|
+
column: clause.column,
|
|
108
|
+
operator: '=',
|
|
109
|
+
value: clause.value,
|
|
110
|
+
asColumn: true,
|
|
111
|
+
});
|
|
117
112
|
}
|
|
118
113
|
}
|
|
119
114
|
if (tableJoins.length > 0) {
|
|
@@ -121,6 +116,16 @@ class QueryCompiler_PG extends QueryCompiler {
|
|
|
121
116
|
}
|
|
122
117
|
}
|
|
123
118
|
|
|
119
|
+
// Delete joins are rewritten into USING plus WHERE, so defer join predicates
|
|
120
|
+
// until after this.where() to keep bindings aligned with SQL placeholder order.
|
|
121
|
+
let wheres = this.where() || '';
|
|
122
|
+
const joinWhereClauses = joinWhereStatements.map((joinWhere) =>
|
|
123
|
+
this.whereBasic(joinWhere)
|
|
124
|
+
);
|
|
125
|
+
if (joinWhereClauses.length > 0) {
|
|
126
|
+
wheres += (wheres ? ' and ' : 'where ') + joinWhereClauses.join(' and ');
|
|
127
|
+
}
|
|
128
|
+
|
|
124
129
|
// With 'using' syntax, no tablename between DELETE and FROM.
|
|
125
130
|
const sql =
|
|
126
131
|
withSQL +
|
|
@@ -197,29 +202,19 @@ class QueryCompiler_PG extends QueryCompiler {
|
|
|
197
202
|
}
|
|
198
203
|
}
|
|
199
204
|
|
|
200
|
-
// Join array of table names and apply default schema.
|
|
201
|
-
_tableNames(tables) {
|
|
202
|
-
const schemaName = this.single.schema;
|
|
203
|
-
const sql = [];
|
|
204
|
-
|
|
205
|
-
for (let i = 0; i < tables.length; i++) {
|
|
206
|
-
let tableName = tables[i];
|
|
207
|
-
|
|
208
|
-
if (tableName) {
|
|
209
|
-
if (schemaName) {
|
|
210
|
-
tableName = `${schemaName}.${tableName}`;
|
|
211
|
-
}
|
|
212
|
-
sql.push(this.formatter.wrap(tableName));
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
return sql.join(', ');
|
|
217
|
-
}
|
|
218
|
-
|
|
219
205
|
_lockingClause(lockMode) {
|
|
220
206
|
const tables = this.single.lockTables || [];
|
|
221
207
|
|
|
222
|
-
return
|
|
208
|
+
return (
|
|
209
|
+
lockMode +
|
|
210
|
+
(tables.length
|
|
211
|
+
? ' of ' +
|
|
212
|
+
tables
|
|
213
|
+
.filter(Boolean)
|
|
214
|
+
.map((table) => this.formatter.wrap(table))
|
|
215
|
+
.join(', ')
|
|
216
|
+
: '')
|
|
217
|
+
);
|
|
223
218
|
}
|
|
224
219
|
|
|
225
220
|
_groupOrder(item, type) {
|
|
@@ -140,6 +140,8 @@ class QueryCompiler_SQLite3 extends QueryCompiler {
|
|
|
140
140
|
else if (onConflict && merge) {
|
|
141
141
|
sql +=
|
|
142
142
|
' where true' + this._merge(merge.updates, onConflict, insertValues);
|
|
143
|
+
const wheres = this.where();
|
|
144
|
+
if (wheres) sql += ` ${wheres}`;
|
|
143
145
|
}
|
|
144
146
|
|
|
145
147
|
const { returning } = this.single;
|
package/lib/execution/runner.js
CHANGED
|
@@ -65,11 +65,17 @@ class Runner {
|
|
|
65
65
|
const stream = new Transform({
|
|
66
66
|
objectMode: true,
|
|
67
67
|
transform: (chunk, _, callback) => {
|
|
68
|
-
|
|
68
|
+
try {
|
|
69
|
+
callback(null, this.client.postProcessResponse(chunk, queryContext));
|
|
70
|
+
} catch (err) {
|
|
71
|
+
callback(err);
|
|
72
|
+
}
|
|
69
73
|
},
|
|
70
74
|
});
|
|
71
75
|
stream.on('close', () => {
|
|
72
|
-
|
|
76
|
+
if (this.connection) {
|
|
77
|
+
this.client.releaseConnection(this.connection);
|
|
78
|
+
}
|
|
73
79
|
});
|
|
74
80
|
|
|
75
81
|
// If the stream is manually destroyed, the close event is not
|
|
@@ -67,7 +67,11 @@ const operators = transform(
|
|
|
67
67
|
|
|
68
68
|
// Accepts a string or array of columns to wrap as appropriate. Column can be raw
|
|
69
69
|
function columnize(target, builder, client, bindingHolder) {
|
|
70
|
-
|
|
70
|
+
if (!Array.isArray(target)) {
|
|
71
|
+
return wrap(target, undefined, builder, client, bindingHolder);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const columns = target;
|
|
71
75
|
let str = '',
|
|
72
76
|
i = -1;
|
|
73
77
|
while (++i < columns.length) {
|
|
@@ -137,9 +141,27 @@ function operator(value, builder, client, bindingsHolder) {
|
|
|
137
141
|
return operator;
|
|
138
142
|
}
|
|
139
143
|
|
|
144
|
+
// Find a case-insensitive " as " without allocating a lowercased string.
|
|
145
|
+
// ASCII uppercase letters differ from lowercase by bit 0x20, so `| 32`
|
|
146
|
+
// folds A/S to a/s before comparing to 97 ("a") and 115 ("s").
|
|
147
|
+
function getAliasSeparatorIndex(value) {
|
|
148
|
+
let i = -1;
|
|
149
|
+
while ((i = value.indexOf(' ', i + 1)) !== -1) {
|
|
150
|
+
if (
|
|
151
|
+
i + 3 < value.length &&
|
|
152
|
+
(value.charCodeAt(i + 1) | 32) === 97 &&
|
|
153
|
+
(value.charCodeAt(i + 2) | 32) === 115 &&
|
|
154
|
+
value.charCodeAt(i + 3) === 32
|
|
155
|
+
) {
|
|
156
|
+
return i;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
return -1;
|
|
160
|
+
}
|
|
161
|
+
|
|
140
162
|
// Coerce to string to prevent strange errors when it's not a string.
|
|
141
163
|
function wrapString(value, builder, client) {
|
|
142
|
-
const asIndex = value
|
|
164
|
+
const asIndex = getAliasSeparatorIndex(value);
|
|
143
165
|
if (asIndex !== -1) {
|
|
144
166
|
const first = value.slice(0, asIndex);
|
|
145
167
|
const second = value.slice(asIndex + 4);
|
|
@@ -148,18 +170,34 @@ function wrapString(value, builder, client) {
|
|
|
148
170
|
wrapAsIdentifier(second, builder, client)
|
|
149
171
|
);
|
|
150
172
|
}
|
|
151
|
-
|
|
152
|
-
let
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
wrapped.push(wrapString((value || '').trim(), builder, client));
|
|
158
|
-
} else {
|
|
159
|
-
wrapped.push(wrapAsIdentifier(value, builder, client));
|
|
160
|
-
}
|
|
173
|
+
|
|
174
|
+
let segmentStart = 0;
|
|
175
|
+
let dotIndex = value.indexOf('.');
|
|
176
|
+
|
|
177
|
+
if (dotIndex === -1) {
|
|
178
|
+
return wrapAsIdentifier(value, builder, client);
|
|
161
179
|
}
|
|
162
|
-
|
|
180
|
+
|
|
181
|
+
let wrapped = '';
|
|
182
|
+
let i = 0;
|
|
183
|
+
|
|
184
|
+
while (dotIndex !== -1) {
|
|
185
|
+
const segment = value.slice(segmentStart, dotIndex);
|
|
186
|
+
if (i > 0) wrapped += '.';
|
|
187
|
+
wrapped +=
|
|
188
|
+
i === 0
|
|
189
|
+
? wrapString((segment || '').trim(), builder, client)
|
|
190
|
+
: wrapAsIdentifier(segment, builder, client);
|
|
191
|
+
segmentStart = dotIndex + 1;
|
|
192
|
+
dotIndex = value.indexOf('.', segmentStart);
|
|
193
|
+
i++;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
return `${wrapped}.${wrapAsIdentifier(
|
|
197
|
+
value.slice(segmentStart),
|
|
198
|
+
builder,
|
|
199
|
+
client
|
|
200
|
+
)}`;
|
|
163
201
|
}
|
|
164
202
|
|
|
165
203
|
// Key-value notation for alias
|
|
@@ -246,6 +284,7 @@ module.exports = {
|
|
|
246
284
|
direction,
|
|
247
285
|
operator,
|
|
248
286
|
outputQuery,
|
|
287
|
+
getAliasSeparatorIndex,
|
|
249
288
|
rawOrFn,
|
|
250
289
|
unwrapRaw,
|
|
251
290
|
wrap,
|
package/lib/knex-builder/Knex.js
CHANGED
|
@@ -4,6 +4,7 @@ const QueryInterface = require('../query/method-constants');
|
|
|
4
4
|
|
|
5
5
|
const makeKnex = require('./make-knex');
|
|
6
6
|
const { KnexTimeoutError } = require('../util/timeout');
|
|
7
|
+
const { KnexPool } = require('../pool');
|
|
7
8
|
const { resolveConfig } = require('./internal/config-resolver');
|
|
8
9
|
const SchemaBuilder = require('../schema/builder');
|
|
9
10
|
const ViewBuilder = require('../schema/viewbuilder');
|
|
@@ -24,6 +25,7 @@ function knex(config) {
|
|
|
24
25
|
knex.Client = Client;
|
|
25
26
|
|
|
26
27
|
knex.KnexTimeoutError = KnexTimeoutError;
|
|
28
|
+
knex.KnexPool = KnexPool;
|
|
27
29
|
|
|
28
30
|
knex.QueryBuilder = {
|
|
29
31
|
extend: function (methodName, fn) {
|
|
@@ -37,6 +37,13 @@ function resolveConfig(config) {
|
|
|
37
37
|
Dialect = getDialectByNameOrAlias(clientName);
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
+
// connectionPool and connection are mutually exclusive
|
|
41
|
+
if (parsedConfig.connectionPool && parsedConfig.connection) {
|
|
42
|
+
throw new Error(
|
|
43
|
+
`knex: 'connectionPool' and 'connection' are mutually exclusive. Use one or the other.`
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
40
47
|
// If config connection parameter is passed as string, try to parse it
|
|
41
48
|
if (typeof parsedConfig.connection === 'string') {
|
|
42
49
|
resolvedConfig = Object.assign({}, parsedConfig, {
|
|
@@ -245,6 +245,136 @@ class Migrator {
|
|
|
245
245
|
});
|
|
246
246
|
}
|
|
247
247
|
|
|
248
|
+
// Migrate up to a specific migration (inclusive).
|
|
249
|
+
// Runs all pending migrations up to and including the specified one.
|
|
250
|
+
async to(config) {
|
|
251
|
+
this._disableProcessing();
|
|
252
|
+
this.config = getMergedConfig(config, this.config, this.knex.client.logger);
|
|
253
|
+
|
|
254
|
+
const name = this.config.name;
|
|
255
|
+
if (!name) {
|
|
256
|
+
throw new Error(
|
|
257
|
+
'A migration name must be specified when using migrate.to()'
|
|
258
|
+
);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
const allAndCompleted = await migrationListResolver.listAllAndCompleted(
|
|
262
|
+
this.config,
|
|
263
|
+
this.knex
|
|
264
|
+
);
|
|
265
|
+
|
|
266
|
+
if (!this.config.disableMigrationsListValidation) {
|
|
267
|
+
validateMigrationList(this.config.migrationSource, allAndCompleted);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
const [all, completed] = allAndCompleted;
|
|
271
|
+
|
|
272
|
+
const targetIndex = all.findIndex(
|
|
273
|
+
(migration) =>
|
|
274
|
+
this.config.migrationSource.getMigrationName(migration) === name
|
|
275
|
+
);
|
|
276
|
+
|
|
277
|
+
if (targetIndex === -1) {
|
|
278
|
+
throw new Error(`Migration "${name}" not found.`);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
const completedNames = completed.map((m) => m.name);
|
|
282
|
+
|
|
283
|
+
const migrationsToRun = all
|
|
284
|
+
.slice(0, targetIndex + 1)
|
|
285
|
+
.filter((migration) => {
|
|
286
|
+
return !completedNames.includes(
|
|
287
|
+
this.config.migrationSource.getMigrationName(migration)
|
|
288
|
+
);
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
const transactionForAll =
|
|
292
|
+
!this.config.disableTransactions &&
|
|
293
|
+
!(
|
|
294
|
+
await Promise.all(
|
|
295
|
+
migrationsToRun.map(async (migration) => {
|
|
296
|
+
const migrationContents =
|
|
297
|
+
await this.config.migrationSource.getMigration(migration);
|
|
298
|
+
return !this._useTransaction(migrationContents);
|
|
299
|
+
})
|
|
300
|
+
)
|
|
301
|
+
).some((isTransactionUsed) => isTransactionUsed);
|
|
302
|
+
|
|
303
|
+
if (transactionForAll) {
|
|
304
|
+
return this.knex.transaction((trx) => {
|
|
305
|
+
return this._runBatch(migrationsToRun, 'up', trx);
|
|
306
|
+
});
|
|
307
|
+
} else {
|
|
308
|
+
return this._runBatch(migrationsToRun, 'up');
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// Migrate up to just before a specific migration (exclusive).
|
|
313
|
+
// Runs all pending migrations before the specified one.
|
|
314
|
+
async before(config) {
|
|
315
|
+
this._disableProcessing();
|
|
316
|
+
this.config = getMergedConfig(config, this.config, this.knex.client.logger);
|
|
317
|
+
|
|
318
|
+
const name = this.config.name;
|
|
319
|
+
if (!name) {
|
|
320
|
+
throw new Error(
|
|
321
|
+
'A migration name must be specified when using migrate.before()'
|
|
322
|
+
);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
const allAndCompleted = await migrationListResolver.listAllAndCompleted(
|
|
326
|
+
this.config,
|
|
327
|
+
this.knex
|
|
328
|
+
);
|
|
329
|
+
|
|
330
|
+
if (!this.config.disableMigrationsListValidation) {
|
|
331
|
+
validateMigrationList(this.config.migrationSource, allAndCompleted);
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
const [all, completed] = allAndCompleted;
|
|
335
|
+
|
|
336
|
+
const targetIndex = all.findIndex(
|
|
337
|
+
(migration) =>
|
|
338
|
+
this.config.migrationSource.getMigrationName(migration) === name
|
|
339
|
+
);
|
|
340
|
+
|
|
341
|
+
if (targetIndex === -1) {
|
|
342
|
+
throw new Error(`Migration "${name}" not found.`);
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
if (targetIndex === 0) {
|
|
346
|
+
return [0, []];
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
const completedNames = completed.map((m) => m.name);
|
|
350
|
+
|
|
351
|
+
const migrationsToRun = all.slice(0, targetIndex).filter((migration) => {
|
|
352
|
+
return !completedNames.includes(
|
|
353
|
+
this.config.migrationSource.getMigrationName(migration)
|
|
354
|
+
);
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
const transactionForAll =
|
|
358
|
+
!this.config.disableTransactions &&
|
|
359
|
+
!(
|
|
360
|
+
await Promise.all(
|
|
361
|
+
migrationsToRun.map(async (migration) => {
|
|
362
|
+
const migrationContents =
|
|
363
|
+
await this.config.migrationSource.getMigration(migration);
|
|
364
|
+
return !this._useTransaction(migrationContents);
|
|
365
|
+
})
|
|
366
|
+
)
|
|
367
|
+
).some((isTransactionUsed) => isTransactionUsed);
|
|
368
|
+
|
|
369
|
+
if (transactionForAll) {
|
|
370
|
+
return this.knex.transaction((trx) => {
|
|
371
|
+
return this._runBatch(migrationsToRun, 'up', trx);
|
|
372
|
+
});
|
|
373
|
+
} else {
|
|
374
|
+
return this._runBatch(migrationsToRun, 'up');
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
248
378
|
status(config) {
|
|
249
379
|
this._disableProcessing();
|
|
250
380
|
this.config = getMergedConfig(config, this.config, this.knex.client.logger);
|
package/lib/pool.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
const { Pool } = require('tarn');
|
|
2
|
+
|
|
3
|
+
class KnexPool extends Pool {
|
|
4
|
+
constructor(config) {
|
|
5
|
+
super(config);
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
// Check if an object is a tarn Pool (or KnexPool) vs a native driver pool.
|
|
10
|
+
// Uses instanceof plus a duck-type fallback checking tarn-specific methods.
|
|
11
|
+
function isTarnPool(obj) {
|
|
12
|
+
if (obj instanceof Pool) return true;
|
|
13
|
+
// Duck-type: tarn pools expose these numeric methods that native pools don't
|
|
14
|
+
return (
|
|
15
|
+
typeof obj.acquire === 'function' &&
|
|
16
|
+
typeof obj.release === 'function' &&
|
|
17
|
+
typeof obj.destroy === 'function' &&
|
|
18
|
+
typeof obj.numFree === 'function' &&
|
|
19
|
+
typeof obj.numUsed === 'function' &&
|
|
20
|
+
typeof obj.numPendingAcquires === 'function'
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
module.exports = { KnexPool, isTarnPool };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "knex",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.3.0",
|
|
4
4
|
"description": "A batteries-included SQL query & schema builder for PostgresSQL, MySQL, CockroachDB, MSSQL and SQLite3",
|
|
5
5
|
"main": "knex.js",
|
|
6
6
|
"types": "types/index.d.ts",
|
|
@@ -25,11 +25,12 @@
|
|
|
25
25
|
"test:unit-only": "mocha --exit -t 10000 --config test/mocha-unit-config-test.js",
|
|
26
26
|
"test:db": "mocha --exit -t 10000 --config test/mocha-integration-config-test.js",
|
|
27
27
|
"test:db:coverage": "nyc mocha --exit --check-leaks -t 10000 --config test/mocha-integration-config-test.js && npm run test:tape",
|
|
28
|
-
"test:db:no-oracle": "cross-env DB=\"mssql mysql mysql2 postgres sqlite3\" mocha --exit -t 10000 --config test/mocha-integration-config-test.js && npm run test:tape",
|
|
28
|
+
"test:db:no-oracle": "cross-env DB=\"mssql mysql mysql2 mariadb postgres sqlite3\" mocha --exit -t 10000 --config test/mocha-integration-config-test.js && npm run test:tape",
|
|
29
29
|
"test": "mocha --exit -t 10000 --config test/mocha-all-config-test.js && npm run test:tape && npm run test:cli",
|
|
30
30
|
"test:coverage": "nyc mocha --exit --check-leaks -t 10000 --config test/mocha-all-config-test.js && npm run test:tape && npm run test:cli",
|
|
31
31
|
"test:everything": "npm run lint:everything && npm run test:coverage",
|
|
32
32
|
"test:mssql": "cross-env DB=mssql npm run test:db",
|
|
33
|
+
"test:mariadb": "cross-env DB=mariadb npm run test:db",
|
|
33
34
|
"test:mysql": "cross-env DB=mysql npm run test:db",
|
|
34
35
|
"test:mysql2": "cross-env DB=mysql2 npm run test:db",
|
|
35
36
|
"test:oracledb": "cross-env DB=oracledb npm run test:db",
|
|
@@ -40,8 +41,8 @@
|
|
|
40
41
|
"test:pgnative": "cross-env DB=pgnative npm run test:db",
|
|
41
42
|
"test:tape": "node test/tape/index.js | faucet",
|
|
42
43
|
"test:cli": "cross-env KNEX_PATH=../knex.js KNEX=bin/cli.js jake -f test/jake/Jakefile && mocha --exit -t 10000 test/cli-tests-suite.js",
|
|
43
|
-
"db:start": "docker compose -f scripts/docker-compose.yml up --build -d mysql oracledb postgres mssql cockroachdb pgnative && docker compose -f scripts/docker-compose.yml up waitmssql waitmysql waitpostgres waitoracledb",
|
|
44
|
-
"db:start:no-oracle": "docker compose -f scripts/docker-compose.yml up --build -d mysql postgres mssql cockroachdb pgnative && docker compose -f scripts/docker-compose.yml up waitmssql waitmysql waitpostgres",
|
|
44
|
+
"db:start": "docker compose -f scripts/docker-compose.yml up --build -d mysql oracledb postgres mssql mariadb cockroachdb pgnative && docker compose -f scripts/docker-compose.yml up waitmssql waitmysql waitmariadb waitpostgres waitoracledb",
|
|
45
|
+
"db:start:no-oracle": "docker compose -f scripts/docker-compose.yml up --build -d mysql postgres mssql mariadb cockroachdb pgnative && docker compose -f scripts/docker-compose.yml up waitmssql waitmysql waitmariadb waitpostgres",
|
|
45
46
|
"db:stop": "docker compose -f scripts/docker-compose.yml down",
|
|
46
47
|
"db:start:postgres": "docker compose -f scripts/docker-compose.yml up --build -d postgres && docker compose -f scripts/docker-compose.yml up waitpostgres",
|
|
47
48
|
"db:stop:postgres": "docker compose -f scripts/docker-compose.yml down",
|
|
@@ -49,6 +50,8 @@
|
|
|
49
50
|
"db:stop:pgnative": "docker compose -f scripts/docker-compose.yml down",
|
|
50
51
|
"db:start:mysql": "docker compose -f scripts/docker-compose.yml up --build -d mysql && docker compose -f scripts/docker-compose.yml up waitmysql",
|
|
51
52
|
"db:stop:mysql": "docker compose -f scripts/docker-compose.yml down",
|
|
53
|
+
"db:start:mariadb": "docker compose -f scripts/docker-compose.yml up --build -d mariadb && docker compose -f scripts/docker-compose.yml up waitmariadb",
|
|
54
|
+
"db:stop:mariadb": "docker compose -f scripts/docker-compose.yml down",
|
|
52
55
|
"db:start:mssql": "docker compose -f scripts/docker-compose.yml up --build -d mssql && docker compose -f scripts/docker-compose.yml up waitmssql",
|
|
53
56
|
"db:stop:mssql": "docker compose -f scripts/docker-compose.yml down",
|
|
54
57
|
"db:start:cockroachdb": "docker compose -f scripts/docker-compose.yml up --build -d cockroachdb && docker compose -f scripts/docker-compose.yml up waitcockroachdb",
|
|
@@ -72,7 +75,7 @@
|
|
|
72
75
|
"pg-connection-string": "2.6.2",
|
|
73
76
|
"rechoir": "^0.8.0",
|
|
74
77
|
"resolve-from": "^5.0.0",
|
|
75
|
-
"tarn": "^3.0
|
|
78
|
+
"tarn": "^3.1.0",
|
|
76
79
|
"tildify": "2.0.0"
|
|
77
80
|
},
|
|
78
81
|
"peerDependencies": {
|
|
@@ -82,6 +85,9 @@
|
|
|
82
85
|
"tedious": {
|
|
83
86
|
"optional": true
|
|
84
87
|
},
|
|
88
|
+
"mariadb": {
|
|
89
|
+
"optional": true
|
|
90
|
+
},
|
|
85
91
|
"mysql": {
|
|
86
92
|
"optional": true
|
|
87
93
|
},
|
|
@@ -131,6 +137,7 @@
|
|
|
131
137
|
"jake": "^10.8.5",
|
|
132
138
|
"JSONStream": "^1.3.5",
|
|
133
139
|
"lint-staged": "^16.2.7",
|
|
140
|
+
"mariadb": "^3.5.2",
|
|
134
141
|
"mocha": "^10.0.0",
|
|
135
142
|
"mock-fs": "^5.1.4",
|
|
136
143
|
"mysql": "^2.18.1",
|
|
@@ -164,7 +171,7 @@
|
|
|
164
171
|
"type": "git",
|
|
165
172
|
"url": "git://github.com/knex/knex.git"
|
|
166
173
|
},
|
|
167
|
-
"homepage": "https://
|
|
174
|
+
"homepage": "https://knexjs.org/",
|
|
168
175
|
"keywords": [
|
|
169
176
|
"sql",
|
|
170
177
|
"query",
|
|
@@ -213,6 +220,7 @@
|
|
|
213
220
|
"./lib/bin/cli.js": "./lib/util/noop.js",
|
|
214
221
|
"./lib/migrations/seed/Seeder.js": "./lib/util/noop.js",
|
|
215
222
|
"tedious": false,
|
|
223
|
+
"mariadb": false,
|
|
216
224
|
"mysql": false,
|
|
217
225
|
"mysql2": false,
|
|
218
226
|
"pg": false,
|