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.
@@ -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
- this._updateFrom(updateFrom) +
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
- joinWheres.push(
107
- this.whereBasic({
108
- column: clause.column,
109
- operator: '=',
110
- value: clause.value,
111
- asColumn: true,
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 lockMode + (tables.length ? ' of ' + this._tableNames(tables) : '');
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;
@@ -65,11 +65,17 @@ class Runner {
65
65
  const stream = new Transform({
66
66
  objectMode: true,
67
67
  transform: (chunk, _, callback) => {
68
- callback(null, this.client.postProcessResponse(chunk, queryContext));
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
- this.client.releaseConnection(this.connection);
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
- const columns = Array.isArray(target) ? target : [target];
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.toLowerCase().indexOf(' as ');
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
- const wrapped = [];
152
- let i = -1;
153
- const segments = value.split('.');
154
- while (++i < segments.length) {
155
- value = segments[i];
156
- if (i === 0 && segments.length > 1) {
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
- return wrapped.join('.');
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,
@@ -32,6 +32,7 @@ class FunctionHelper {
32
32
  return this.client.raw('(random_uuid())');
33
33
  case 'mysql':
34
34
  case 'mysql2':
35
+ case 'mariadb':
35
36
  return this.client.raw('(UUID())');
36
37
  default:
37
38
  throw new Error(
@@ -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 };
@@ -328,6 +328,7 @@ class TableCompiler {
328
328
  output: () => {
329
329
  return this.client
330
330
  .queryBuilder()
331
+ .withSchema(this.schemaNameRaw)
331
332
  .from(this.tableNameRaw)
332
333
  .columnInfo(column)
333
334
  .then((columnInfo) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "knex",
3
- "version": "3.2.10",
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.2",
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://knex.github.io/documentation/",
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,