knex 3.2.9 → 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 +53 -2
- 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/query/querycompiler.js +1 -1
- package/lib/schema/tablecompiler.js +1 -0
- package/package.json +15 -7
- package/types/index.d.ts +78 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,4 +1,55 @@
|
|
|
1
|
-
# Master (Unreleased)
|
|
1
|
+
# [Master (Unreleased)](https://github.com/knex/knex/compare/3.3.0...master)
|
|
2
|
+
|
|
3
|
+
# 3.3.0 - 26 June, 2026
|
|
4
|
+
|
|
5
|
+
### New features
|
|
6
|
+
|
|
7
|
+
- feat: add support for returning in mariadb [#4572](https://github.com/knex/knex/pull/4572)
|
|
8
|
+
- feat: mariadb driver support [#6415](https://github.com/knex/knex/pull/6415)
|
|
9
|
+
- Fixes \_setNullableState not respecting schema [#6025](https://github.com/knex/knex/pull/6025)
|
|
10
|
+
- feat: add connectionPool option for bringing an external pool [#6414](https://github.com/knex/knex/pull/6414)
|
|
11
|
+
- Added knex.migrate.to and knex.migrate.before [#6420](https://github.com/knex/knex/pull/6420)
|
|
12
|
+
- feat: set error.cause for tarn acquire connection error [#5681](https://github.com/knex/knex/pull/5681)
|
|
13
|
+
|
|
14
|
+
### Bug fixes
|
|
15
|
+
|
|
16
|
+
- Fix FOR UPDATE OF with explicit schema [#5791](https://github.com/knex/knex/pull/5791)
|
|
17
|
+
- Fix sqlite conditional insert/merge when inserting multiple rows [#6185](https://github.com/knex/knex/pull/6185)
|
|
18
|
+
- Fix: Stream postProcessResponse error is not catchable with .on('error') [#6033](https://github.com/knex/knex/pull/6033)
|
|
19
|
+
- fix(pg): preserve updateFrom binding order [#6454](https://github.com/knex/knex/pull/6454)
|
|
20
|
+
- fix: #6451, support token-credential in mssql auth [#6465](https://github.com/knex/knex/pull/6465)
|
|
21
|
+
- fix(types): #6452 - .where type regression for invalid types [#6463](https://github.com/knex/knex/pull/6463)
|
|
22
|
+
- fix: #6460 unhandled error on connection timeout with stream [#6462](https://github.com/knex/knex/pull/6462)
|
|
23
|
+
- fix: #6455, correctly state tedious as dependency needed for mssql [#6464](https://github.com/knex/knex/pull/6464)
|
|
24
|
+
- fix(pg,mssql): preserve binding order in delete and update queries [#6438](https://github.com/knex/knex/pull/6438)
|
|
25
|
+
|
|
26
|
+
### Misc
|
|
27
|
+
|
|
28
|
+
- chore: bump tarn@3.1.0 [#6492](https://github.com/knex/knex/pull/6492)
|
|
29
|
+
- micro-optimization in wrappingFormatter [#6456](https://github.com/knex/knex/pull/6456)
|
|
30
|
+
- cleanup flake in the cancellation tests [#6486](https://github.com/knex/knex/pull/6486)
|
|
31
|
+
- Update docs: timeout section [#6471](https://github.com/knex/knex/pull/6471)
|
|
32
|
+
- ci: make npm install resilient to transient network failures [#6468](https://github.com/knex/knex/pull/6468)
|
|
33
|
+
- Update homepage urls [#6450](https://github.com/knex/knex/pull/6450)
|
|
34
|
+
- chore: add mariadb to docker-compose [#6466](https://github.com/knex/knex/pull/6466)
|
|
35
|
+
- chore: set codecov to default coverage provider [#6448](https://github.com/knex/knex/pull/6448)
|
|
36
|
+
|
|
37
|
+
# 3.2.10 - 2 May, 2026
|
|
38
|
+
|
|
39
|
+
### Bug fixes
|
|
40
|
+
|
|
41
|
+
- fix: bump lodash to ^4.18.1, close #6433 [#6446](https://github.com/knex/knex/pull/6446)
|
|
42
|
+
- Fix: Properly Escape Aliases in Analytic Functions [#6392](https://github.com/knex/knex/pull/6392)
|
|
43
|
+
|
|
44
|
+
### Misc
|
|
45
|
+
|
|
46
|
+
- chore: auto-update the docs' knex version on publish [#6447](https://github.com/knex/knex/pull/6447)
|
|
47
|
+
- chore: skip re-running tests on automated release commit [#6443](https://github.com/knex/knex/pull/6443)
|
|
48
|
+
- chore: sync docker images we use to ghcr [#6445](https://github.com/knex/knex/pull/6445)
|
|
49
|
+
- chore: fixes for release-drafter workflow [#6442](https://github.com/knex/knex/pull/6442)
|
|
50
|
+
- chore: new publish/release workflow [#6441](https://github.com/knex/knex/pull/6441)
|
|
51
|
+
- docs: Update changelog for version 3.2.9 [#6440](https://github.com/knex/knex/pull/6440)
|
|
52
|
+
- docs: sync website changelog from 3.0.0 to 3.2.8 [#6426](https://github.com/knex/knex/pull/6426)
|
|
2
53
|
|
|
3
54
|
# 3.2.9 - 3 April, 2026
|
|
4
55
|
|
|
@@ -7,7 +58,7 @@
|
|
|
7
58
|
- fix: support DELETE... LIMIT in dialects that support it (mysql), but continue to disallow ones that don't [#6429](https://github.com/knex/knex/issues/6429)
|
|
8
59
|
- fix(postgres): escape double quotes in searchPath to prevent SQL injection [#6411](https://github.com/knex/knex/issues/6411)
|
|
9
60
|
- fix(sqlite): append RETURNING statement when insert empty row [#5471](https://github.com/knex/knex/issues/5471)
|
|
10
|
-
- fix: add type support for Array<Buffer
|
|
61
|
+
- fix: add type support for `Array<Buffer>` [#6428](https://github.com/knex/knex/issues/6428)
|
|
11
62
|
|
|
12
63
|
# 3.2.8 - 30 March, 2026
|
|
13
64
|
|
package/README.md
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
# [knex.js](https://
|
|
1
|
+
# [knex.js](https://knexjs.org/)
|
|
2
2
|
|
|
3
3
|
[](https://npmjs.org/package/knex)
|
|
4
4
|
[](https://npmjs.org/package/knex)
|
|
5
5
|

|
|
6
|
-
[](https://codecov.io/gh/knex/knex)
|
|
7
7
|
[](https://libraries.io/npm/knex)
|
|
8
8
|
[](https://gitter.im/tgriesser/knex)
|
|
9
9
|
|
package/bin/cli.js
CHANGED
|
@@ -280,19 +280,13 @@ function invoke() {
|
|
|
280
280
|
}
|
|
281
281
|
});
|
|
282
282
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
.option(
|
|
286
|
-
'--disable-transactions',
|
|
287
|
-
'run migrations without an implicit transaction'
|
|
288
|
-
)
|
|
289
|
-
.description(
|
|
290
|
-
' Run the next or the specified migration that has not yet been run.'
|
|
291
|
-
)
|
|
292
|
-
.action((name, cmd) => {
|
|
283
|
+
function migrateUpAction(method) {
|
|
284
|
+
return (name, cmd) => {
|
|
293
285
|
const disableTransactions = !!cmd.disableTransactions;
|
|
294
286
|
initKnex(env, commander.opts())
|
|
295
|
-
.then((instance) =>
|
|
287
|
+
.then((instance) =>
|
|
288
|
+
instance.migrate[method]({ name, disableTransactions })
|
|
289
|
+
)
|
|
296
290
|
.then(([batchNo, log]) => {
|
|
297
291
|
if (log.length === 0) {
|
|
298
292
|
success(color.cyan('Already up to date'));
|
|
@@ -307,7 +301,19 @@ function invoke() {
|
|
|
307
301
|
);
|
|
308
302
|
})
|
|
309
303
|
.catch(exit);
|
|
310
|
-
}
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
commander
|
|
308
|
+
.command('migrate:up [<name>]')
|
|
309
|
+
.option(
|
|
310
|
+
'--disable-transactions',
|
|
311
|
+
'run migrations without an implicit transaction'
|
|
312
|
+
)
|
|
313
|
+
.description(
|
|
314
|
+
' Run the next or the specified migration that has not yet been run.'
|
|
315
|
+
)
|
|
316
|
+
.action(migrateUpAction('up'));
|
|
311
317
|
|
|
312
318
|
commander
|
|
313
319
|
.command('migrate:rollback')
|
|
@@ -371,6 +377,28 @@ function invoke() {
|
|
|
371
377
|
.catch(exit);
|
|
372
378
|
});
|
|
373
379
|
|
|
380
|
+
commander
|
|
381
|
+
.command('migrate:to <name>')
|
|
382
|
+
.option(
|
|
383
|
+
'--disable-transactions',
|
|
384
|
+
'run migrations without an implicit transaction'
|
|
385
|
+
)
|
|
386
|
+
.description(
|
|
387
|
+
' Run all migrations up to and including the specified migration.'
|
|
388
|
+
)
|
|
389
|
+
.action(migrateUpAction('to'));
|
|
390
|
+
|
|
391
|
+
commander
|
|
392
|
+
.command('migrate:before <name>')
|
|
393
|
+
.option(
|
|
394
|
+
'--disable-transactions',
|
|
395
|
+
'run migrations without an implicit transaction'
|
|
396
|
+
)
|
|
397
|
+
.description(
|
|
398
|
+
' Run all migrations before the specified migration (exclusive).'
|
|
399
|
+
)
|
|
400
|
+
.action(migrateUpAction('before'));
|
|
401
|
+
|
|
374
402
|
commander
|
|
375
403
|
.command('migrate:currentVersion')
|
|
376
404
|
.description(' View the current version for the migration.')
|
package/lib/client.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
const {
|
|
1
|
+
const { TimeoutError } = require('tarn');
|
|
2
|
+
const { KnexPool, isTarnPool } = require('./pool');
|
|
2
3
|
const { EventEmitter } = require('events');
|
|
3
4
|
const { promisify } = require('util');
|
|
4
5
|
const { makeEscape } = require('./util/string');
|
|
5
|
-
const
|
|
6
|
+
const cloneDeepWith = require('lodash/cloneDeepWith');
|
|
6
7
|
const defaults = require('lodash/defaults');
|
|
7
8
|
const uniqueId = require('lodash/uniqueId');
|
|
8
9
|
|
|
@@ -35,6 +36,17 @@ const { setHiddenProperty } = require('./util/security.js');
|
|
|
35
36
|
|
|
36
37
|
const debug = require('debug')('knex:client');
|
|
37
38
|
|
|
39
|
+
function preserveClassInstances(value) {
|
|
40
|
+
if (
|
|
41
|
+
value !== null &&
|
|
42
|
+
typeof value === 'object' &&
|
|
43
|
+
!Array.isArray(value) &&
|
|
44
|
+
!isPlainObject(value)
|
|
45
|
+
) {
|
|
46
|
+
return value;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
38
50
|
// The base client provides the general structure
|
|
39
51
|
// for a dialect specific client object.
|
|
40
52
|
|
|
@@ -72,11 +84,14 @@ class Client extends EventEmitter {
|
|
|
72
84
|
this.version = config.version;
|
|
73
85
|
}
|
|
74
86
|
|
|
75
|
-
if (config.connection && config.connection instanceof Function) {
|
|
87
|
+
if (this.config.connection && this.config.connection instanceof Function) {
|
|
76
88
|
this.connectionConfigProvider = this.config.connection;
|
|
77
89
|
this.connectionConfigExpirationChecker = () => true; // causes the provider to be called on first use
|
|
78
90
|
} else {
|
|
79
|
-
this.connectionSettings =
|
|
91
|
+
this.connectionSettings = cloneDeepWith(
|
|
92
|
+
this.config.connection || {},
|
|
93
|
+
preserveClassInstances
|
|
94
|
+
);
|
|
80
95
|
if (config.connection && config.connection.password) {
|
|
81
96
|
setHiddenProperty(this.connectionSettings, this.config.connection);
|
|
82
97
|
}
|
|
@@ -84,6 +99,10 @@ class Client extends EventEmitter {
|
|
|
84
99
|
}
|
|
85
100
|
if (this.driverName && config.connection) {
|
|
86
101
|
this.initializeDriver();
|
|
102
|
+
}
|
|
103
|
+
const shouldInitPool =
|
|
104
|
+
this.driverName && (config.connection || config.connectionPool);
|
|
105
|
+
if (shouldInitPool) {
|
|
87
106
|
if (!config.pool || (config.pool && config.pool.max !== 0)) {
|
|
88
107
|
this.initializePool(this.config);
|
|
89
108
|
}
|
|
@@ -200,7 +219,8 @@ class Client extends EventEmitter {
|
|
|
200
219
|
try {
|
|
201
220
|
this.driver = this._driver();
|
|
202
221
|
} catch (e) {
|
|
203
|
-
const
|
|
222
|
+
const packageName = this._packageName || this.driverName;
|
|
223
|
+
const message = `Knex: run\n$ npm install ${packageName} --save`;
|
|
204
224
|
this.logger.error(`${message}\n${e.message}\n${e.stack}`);
|
|
205
225
|
throw new Error(`${message}\n${e.message}`);
|
|
206
226
|
}
|
|
@@ -390,8 +410,46 @@ class Client extends EventEmitter {
|
|
|
390
410
|
this.logger.warn('The pool has already been initialized');
|
|
391
411
|
return;
|
|
392
412
|
}
|
|
413
|
+
if (config.connectionPool) {
|
|
414
|
+
// External pool mode — delegate to a tarn-compatible or native pool
|
|
415
|
+
this.connectionSettings = {};
|
|
416
|
+
this.connectionConfigExpirationChecker = null;
|
|
417
|
+
if (this.driverName) {
|
|
418
|
+
this.initializeDriver();
|
|
419
|
+
}
|
|
420
|
+
this.initializeExternalPool(config.connectionPool);
|
|
421
|
+
} else {
|
|
422
|
+
this.pool = new KnexPool(this.getPoolSettings(config.pool));
|
|
423
|
+
this._ownsPool = true;
|
|
424
|
+
}
|
|
425
|
+
}
|
|
393
426
|
|
|
394
|
-
|
|
427
|
+
// Accept an external pool — either a tarn-compatible pool (KnexPool, tarn Pool)
|
|
428
|
+
// or a native driver pool (pg.Pool, mysql.createPool(), etc.)
|
|
429
|
+
initializeExternalPool(externalPool) {
|
|
430
|
+
if (this.pool) {
|
|
431
|
+
this.logger.warn('The pool has already been initialized');
|
|
432
|
+
return;
|
|
433
|
+
}
|
|
434
|
+
if (isTarnPool(externalPool)) {
|
|
435
|
+
// Tarn-compatible pool — use directly
|
|
436
|
+
this.pool = externalPool;
|
|
437
|
+
} else {
|
|
438
|
+
// Native driver pool — wrap via dialect-specific adapter
|
|
439
|
+
this.pool = this.wrapNativePool(externalPool);
|
|
440
|
+
}
|
|
441
|
+
this._ownsPool = false;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
// Override in dialects that support native driver pools.
|
|
445
|
+
// Must return an object with { acquire(), release(), destroy() } matching tarn's interface.
|
|
446
|
+
wrapNativePool(_nativePool) {
|
|
447
|
+
throw new Error(
|
|
448
|
+
`The '${
|
|
449
|
+
this.driverName || 'base'
|
|
450
|
+
}' dialect does not support native driver pools in 'connectionPool'. ` +
|
|
451
|
+
`Pass a tarn Pool or KnexPool instance instead.`
|
|
452
|
+
);
|
|
395
453
|
}
|
|
396
454
|
|
|
397
455
|
validateConnection(connection) {
|
|
@@ -420,12 +478,23 @@ class Client extends EventEmitter {
|
|
|
420
478
|
}
|
|
421
479
|
return connection;
|
|
422
480
|
} catch (error) {
|
|
481
|
+
// Tarn only gives us the propagated error's message, not its stack. However, for the "original"
|
|
482
|
+
// promise that rejected, the stack will be present when Tarn passes us the error directly (not
|
|
483
|
+
// as a timeout). The stack is likely of little use, but let's give all the information we can.
|
|
484
|
+
const withStack =
|
|
485
|
+
error instanceof Error ? error.stack ?? error.message : String(error);
|
|
486
|
+
|
|
487
|
+
// Errors like this can be noisy; use logger.warn to make it easier for operators to opt out of
|
|
488
|
+
// noise but still surface more critical problems
|
|
489
|
+
this.logger.warn(`Acquire connection error: ${withStack}`);
|
|
490
|
+
|
|
423
491
|
let convertedError = error;
|
|
424
492
|
if (error instanceof TimeoutError) {
|
|
425
493
|
convertedError = new KnexTimeoutError(
|
|
426
494
|
'Knex: Timeout acquiring a connection. The pool is probably full. ' +
|
|
427
495
|
'Are you missing a .transacting(trx) call?'
|
|
428
496
|
);
|
|
497
|
+
convertedError.cause = error;
|
|
429
498
|
}
|
|
430
499
|
throw convertedError;
|
|
431
500
|
}
|
|
@@ -445,9 +514,11 @@ class Client extends EventEmitter {
|
|
|
445
514
|
}
|
|
446
515
|
|
|
447
516
|
// Destroy the current connection pool for the client.
|
|
517
|
+
// When the pool is externally provided (_ownsPool === false), we only drop
|
|
518
|
+
// our reference — the caller is responsible for tearing it down.
|
|
448
519
|
async destroy(callback) {
|
|
449
520
|
try {
|
|
450
|
-
if (this.pool && this.pool.destroy) {
|
|
521
|
+
if (this.pool && this.pool.destroy && this._ownsPool !== false) {
|
|
451
522
|
await this.pool.destroy();
|
|
452
523
|
}
|
|
453
524
|
this.pool = undefined;
|
package/lib/constants.js
CHANGED
|
@@ -7,6 +7,7 @@ const CLIENT_ALIASES = Object.freeze({
|
|
|
7
7
|
|
|
8
8
|
const SUPPORTED_CLIENTS = Object.freeze(
|
|
9
9
|
[
|
|
10
|
+
'mariadb',
|
|
10
11
|
'mssql',
|
|
11
12
|
'mysql',
|
|
12
13
|
'mysql2',
|
|
@@ -21,6 +22,7 @@ const SUPPORTED_CLIENTS = Object.freeze(
|
|
|
21
22
|
);
|
|
22
23
|
|
|
23
24
|
const DRIVER_NAMES = Object.freeze({
|
|
25
|
+
MariaDB: 'mariadb',
|
|
24
26
|
MsSQL: 'mssql',
|
|
25
27
|
MySQL: 'mysql',
|
|
26
28
|
MySQL2: 'mysql2',
|
package/lib/dialects/index.js
CHANGED
|
@@ -7,6 +7,7 @@ const dbNameToDialectLoader = Object.freeze({
|
|
|
7
7
|
cockroachdb: () => require('./cockroachdb'),
|
|
8
8
|
mssql: () => require('./mssql'),
|
|
9
9
|
mysql: () => require('./mysql'),
|
|
10
|
+
mariadb: () => require('./mariadb'),
|
|
10
11
|
mysql2: () => require('./mysql2'),
|
|
11
12
|
oracle: () => require('./oracle'),
|
|
12
13
|
oracledb: () => require('./oracledb'),
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
// MariaDB Client
|
|
2
|
+
// -------
|
|
3
|
+
const Client_MySQL = require('../mysql');
|
|
4
|
+
const Transaction = require('./transaction');
|
|
5
|
+
const QueryCompiler = require('./query/mariadb-querycompiler');
|
|
6
|
+
const ColumnCompiler = require('./schema/mariadb-columncompiler');
|
|
7
|
+
|
|
8
|
+
class Client_MariaDB extends Client_MySQL {
|
|
9
|
+
transaction() {
|
|
10
|
+
return new Transaction(this, ...arguments);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
queryCompiler(builder, formatter) {
|
|
14
|
+
return new QueryCompiler(this, builder, formatter);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
columnCompiler() {
|
|
18
|
+
return new ColumnCompiler(this, ...arguments);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
_driver() {
|
|
22
|
+
return require('mariadb/callback');
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
processResponse(obj, runner) {
|
|
26
|
+
if (obj == null) return;
|
|
27
|
+
if (obj.returning) {
|
|
28
|
+
const rows = obj.response[0];
|
|
29
|
+
const fields = obj.response[1];
|
|
30
|
+
if (obj.output) {
|
|
31
|
+
return obj.output.call(runner, rows, fields);
|
|
32
|
+
}
|
|
33
|
+
return rows;
|
|
34
|
+
}
|
|
35
|
+
return super.processResponse(obj, runner);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
validateConnection(connection) {
|
|
39
|
+
if (typeof connection.isValid === 'function') {
|
|
40
|
+
return connection.isValid();
|
|
41
|
+
}
|
|
42
|
+
return (
|
|
43
|
+
connection &&
|
|
44
|
+
!connection._fatalError &&
|
|
45
|
+
!connection._protocolError &&
|
|
46
|
+
!connection._closing
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
Object.assign(Client_MariaDB.prototype, {
|
|
52
|
+
driverName: 'mariadb',
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
module.exports = Client_MariaDB;
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
// MariaDB Query Compiler
|
|
2
|
+
// -------
|
|
3
|
+
const QueryCompiler_MySQL = require('../../mysql/query/mysql-querycompiler');
|
|
4
|
+
const { wrap: wrap_ } = require('../../../formatter/wrappingFormatter');
|
|
5
|
+
|
|
6
|
+
class QueryCompiler_MariaDB extends QueryCompiler_MySQL {
|
|
7
|
+
// On MariaDB, json_extract() yields a quoted JSON scalar (e.g. "cold") whose
|
|
8
|
+
// values do not compare equal with `=`. json_unquote() is required for the
|
|
9
|
+
// comparison to behave like MySQL's native JSON type
|
|
10
|
+
onJsonPathEquals(clause) {
|
|
11
|
+
const extract = (column, path) =>
|
|
12
|
+
`json_unquote(json_extract(${wrap_(
|
|
13
|
+
column,
|
|
14
|
+
undefined,
|
|
15
|
+
this.builder,
|
|
16
|
+
this.client,
|
|
17
|
+
this.bindingsHolder
|
|
18
|
+
)}, ${this.client.parameter(path, this.builder, this.bindingsHolder)}))`;
|
|
19
|
+
|
|
20
|
+
return `${extract(clause.columnFirst, clause.jsonPathFirst)} = ${extract(
|
|
21
|
+
clause.columnSecond,
|
|
22
|
+
clause.jsonPathSecond
|
|
23
|
+
)}`;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
insert() {
|
|
27
|
+
const sql = super.insert();
|
|
28
|
+
if (sql === '') return sql;
|
|
29
|
+
const { returning } = this.single;
|
|
30
|
+
if (returning && this._supportsReturning('insert')) {
|
|
31
|
+
return { sql: sql + this._returning(returning), returning };
|
|
32
|
+
}
|
|
33
|
+
return sql;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
update() {
|
|
37
|
+
const sql = super.update();
|
|
38
|
+
if (sql === '') return sql;
|
|
39
|
+
const { returning } = this.single;
|
|
40
|
+
if (returning && this._supportsReturning('update')) {
|
|
41
|
+
return { sql: sql + this._returning(returning), returning };
|
|
42
|
+
}
|
|
43
|
+
return sql;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
del() {
|
|
47
|
+
const sql = super.del();
|
|
48
|
+
if (sql === '') return sql;
|
|
49
|
+
const { returning } = this.single;
|
|
50
|
+
if (returning && this._supportsReturning('del')) {
|
|
51
|
+
return { sql: sql + this._returning(returning), returning };
|
|
52
|
+
}
|
|
53
|
+
return sql;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
_returning(value) {
|
|
57
|
+
return value ? ` returning ${this.formatter.columnize(value)}` : '';
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
_supportsReturning(method) {
|
|
61
|
+
// Assume that if the version doesn't exist, we're on the latest
|
|
62
|
+
// (fail open and assume the user is building queries correctly)
|
|
63
|
+
const version = parseFloat(this.client.version) ?? 13;
|
|
64
|
+
if (version < 10.5) {
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
if (method === 'update') {
|
|
68
|
+
return version >= 13 && (this.grouped.join || []).length === 0;
|
|
69
|
+
}
|
|
70
|
+
if (method === 'del') {
|
|
71
|
+
return (this.grouped.join || []).length === 0;
|
|
72
|
+
}
|
|
73
|
+
return true;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
_returningCheck() {
|
|
77
|
+
const { returning } = this.single;
|
|
78
|
+
if (!returning || this._supportsReturning(this.method)) {
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
let reason;
|
|
82
|
+
const hasJoin = (this.grouped.join || []).length;
|
|
83
|
+
if (this.method === 'update') {
|
|
84
|
+
reason = (this.grouped.join || []).length
|
|
85
|
+
? 'multi-table update statements'
|
|
86
|
+
: 'mariadb versions older than 13.0';
|
|
87
|
+
} else if (this.method === 'del' && hasJoin) {
|
|
88
|
+
reason = 'multi-table delete statements';
|
|
89
|
+
} else {
|
|
90
|
+
reason = 'mariadb versions older than 10.5';
|
|
91
|
+
}
|
|
92
|
+
this.client.logger.warn(
|
|
93
|
+
`.returning() is not supported for ${reason} and will not have any effect.`
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
module.exports = QueryCompiler_MariaDB;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// MariaDB Column Compiler
|
|
2
|
+
// -------
|
|
3
|
+
const ColumnCompiler_MySQL = require('../../mysql/schema/mysql-columncompiler');
|
|
4
|
+
|
|
5
|
+
class ColumnCompiler_MariaDB extends ColumnCompiler_MySQL {
|
|
6
|
+
// MariaDB does not accept a named CHECK constraint inline in a column
|
|
7
|
+
// definition
|
|
8
|
+
_check(checkPredicate, constraintName) {
|
|
9
|
+
if (constraintName && this.columnBuilder._method !== 'alter') {
|
|
10
|
+
this._pushAlterCheckQuery(checkPredicate, constraintName);
|
|
11
|
+
return '';
|
|
12
|
+
}
|
|
13
|
+
return super._check(checkPredicate, constraintName);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
module.exports = ColumnCompiler_MariaDB;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
const Transaction = require('../../execution/transaction');
|
|
2
|
+
const debug = require('debug')('knex:tx');
|
|
3
|
+
|
|
4
|
+
class Transaction_MariaDB extends Transaction {
|
|
5
|
+
query(conn, sql, status, value) {
|
|
6
|
+
const t = this;
|
|
7
|
+
const q = this.trxClient
|
|
8
|
+
.query(conn, sql)
|
|
9
|
+
.catch((err) => {
|
|
10
|
+
if (err.code === 'ER_SP_DOES_NOT_EXIST') {
|
|
11
|
+
this.trxClient.logger.warn(
|
|
12
|
+
'Transaction was implicitly committed, do not mix transactions and ' +
|
|
13
|
+
'DDL with MySQL (#805)'
|
|
14
|
+
);
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
status = 2;
|
|
19
|
+
value = err;
|
|
20
|
+
t._completed = true;
|
|
21
|
+
debug('%s error running transaction query', t.txid);
|
|
22
|
+
})
|
|
23
|
+
.then(function (res) {
|
|
24
|
+
if (status === 1) t._resolver(value);
|
|
25
|
+
if (status === 2) {
|
|
26
|
+
if (value === undefined) {
|
|
27
|
+
if (t.doNotRejectOnRollback && /^ROLLBACK\b/i.test(sql)) {
|
|
28
|
+
t._resolver();
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
value = new Error(`Transaction rejected with non-error: ${value}`);
|
|
32
|
+
}
|
|
33
|
+
t._rejecter(value);
|
|
34
|
+
return res;
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
if (status === 1 || status === 2) {
|
|
38
|
+
t._completed = true;
|
|
39
|
+
}
|
|
40
|
+
return q;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
module.exports = Transaction_MariaDB;
|
|
@@ -46,6 +46,7 @@ class Client_MSSQL extends Client {
|
|
|
46
46
|
clientSecret: settings.clientSecret,
|
|
47
47
|
tenantId: settings.tenantId,
|
|
48
48
|
msiEndpoint: settings.msiEndpoint,
|
|
49
|
+
credential: settings.credential,
|
|
49
50
|
},
|
|
50
51
|
},
|
|
51
52
|
server: settings.server || settings.host,
|
|
@@ -493,6 +494,8 @@ Object.assign(Client_MSSQL.prototype, {
|
|
|
493
494
|
dialect: 'mssql',
|
|
494
495
|
|
|
495
496
|
driverName: 'mssql',
|
|
497
|
+
|
|
498
|
+
_packageName: 'tedious',
|
|
496
499
|
});
|
|
497
500
|
|
|
498
501
|
module.exports = Client_MSSQL;
|
|
@@ -179,8 +179,8 @@ class QueryCompiler_MSSQL extends QueryCompiler {
|
|
|
179
179
|
}
|
|
180
180
|
|
|
181
181
|
updateWithTriggers() {
|
|
182
|
-
const top = this.top();
|
|
183
182
|
const withSQL = this.with();
|
|
183
|
+
const top = this.top();
|
|
184
184
|
const updates = this._prepUpdate(this.single.update);
|
|
185
185
|
const join = this.join();
|
|
186
186
|
const where = this.where();
|
|
@@ -221,8 +221,8 @@ class QueryCompiler_MSSQL extends QueryCompiler {
|
|
|
221
221
|
}
|
|
222
222
|
|
|
223
223
|
standardUpdate() {
|
|
224
|
-
const top = this.top();
|
|
225
224
|
const withSQL = this.with();
|
|
225
|
+
const top = this.top();
|
|
226
226
|
const updates = this._prepUpdate(this.single.update);
|
|
227
227
|
const join = this.join();
|
|
228
228
|
const where = this.where();
|
|
@@ -261,8 +261,8 @@ class QueryCompiler_MSSQL extends QueryCompiler {
|
|
|
261
261
|
// Make sure tableName is processed by the formatter first.
|
|
262
262
|
const withSQL = this.with();
|
|
263
263
|
const { tableName } = this;
|
|
264
|
-
const wheres = this.where();
|
|
265
264
|
const joins = this.join();
|
|
265
|
+
const wheres = this.where();
|
|
266
266
|
const { returning } = this.single;
|
|
267
267
|
const returningStr = returning
|
|
268
268
|
? ` ${this._returning('del', returning, true)}`
|
|
@@ -288,8 +288,8 @@ class QueryCompiler_MSSQL extends QueryCompiler {
|
|
|
288
288
|
// Make sure tableName is processed by the formatter first.
|
|
289
289
|
const withSQL = this.with();
|
|
290
290
|
const { tableName } = this;
|
|
291
|
-
const wheres = this.where();
|
|
292
291
|
const joins = this.join();
|
|
292
|
+
const wheres = this.where();
|
|
293
293
|
const { returning } = this.single;
|
|
294
294
|
const returningStr = returning
|
|
295
295
|
? ` ${this._returning('del', returning)}`
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
// -------
|
|
3
3
|
const defer = require('lodash/defer');
|
|
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
|
|
|
@@ -109,6 +110,47 @@ class Client_MySQL extends Client {
|
|
|
109
110
|
);
|
|
110
111
|
}
|
|
111
112
|
|
|
113
|
+
// Wraps a native mysql pool with the interface knex expects from this.pool.
|
|
114
|
+
/**
|
|
115
|
+
* @param {import('mysql').Pool | import('mysql2').Pool} nativePool
|
|
116
|
+
*/
|
|
117
|
+
wrapNativePool(nativePool) {
|
|
118
|
+
return {
|
|
119
|
+
acquire() {
|
|
120
|
+
return {
|
|
121
|
+
promise: new Promise((resolve, reject) => {
|
|
122
|
+
nativePool.getConnection((err, connection) => {
|
|
123
|
+
if (err) return reject(err);
|
|
124
|
+
connection.__knexUid = uniqueId('__knexUid');
|
|
125
|
+
resolve(connection);
|
|
126
|
+
});
|
|
127
|
+
}),
|
|
128
|
+
};
|
|
129
|
+
},
|
|
130
|
+
/**
|
|
131
|
+
* @param {import('mysql').Connection | import('mysql2').Connection} connection
|
|
132
|
+
*/
|
|
133
|
+
release(connection) {
|
|
134
|
+
if (connection.__knex__disposed) {
|
|
135
|
+
// Disposed connection — remove from pool without sending COM_QUIT
|
|
136
|
+
if (typeof connection.destroy === 'function') {
|
|
137
|
+
connection.destroy();
|
|
138
|
+
}
|
|
139
|
+
return true;
|
|
140
|
+
}
|
|
141
|
+
if (typeof connection.release === 'function') {
|
|
142
|
+
connection.release();
|
|
143
|
+
return true;
|
|
144
|
+
}
|
|
145
|
+
return false;
|
|
146
|
+
},
|
|
147
|
+
destroy() {
|
|
148
|
+
// No-op: knex does not own the native pool
|
|
149
|
+
return Promise.resolve();
|
|
150
|
+
},
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
|
|
112
154
|
// Grab a connection, run the query via the MySQL streaming interface,
|
|
113
155
|
// and pass that through to the stream we've sent back to the client.
|
|
114
156
|
_stream(connection, obj, stream, options) {
|