knex 0.19.1 → 0.19.5

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 CHANGED
@@ -1,5 +1,71 @@
1
1
  # Master (Unreleased)
2
2
 
3
+ # 0.19.5 - 06 October, 2019
4
+
5
+ ### New features:
6
+
7
+ - CLI: Migrations up/down commands - filename parameter #3416
8
+ - Oracle: Support stored procedures #3449
9
+
10
+ ### Bug fixes:
11
+
12
+ - MSSQL: Escape column ids correctly in all cases (reported by Snyk Security Research Team) #3382
13
+ - SQLite: Fix handling of multiline SQL in SQLite3 schema #3411
14
+ - Fix concurrent child transactions failing #2213 #3440
15
+
16
+ ### Typings:
17
+
18
+ - Add missing Migrator.list typing #3460
19
+ - Fix Typescript type inference for to better support wildcard (*) calls #3444
20
+ - Make options argument optional in timeout #3442
21
+
22
+ ### Test / internal changes:
23
+
24
+ - Enable linting in CI #3450
25
+
26
+ # 0.19.4 - 09 September, 2019
27
+
28
+ ### New features:
29
+
30
+ - Add undefined columns to undefined binding(s) error #3425
31
+
32
+ ### Typings:
33
+
34
+ - Add `specific` to SeederConfig type #3429
35
+ - Fix some issues with QueryBuilder types #3427
36
+
37
+ # 0.19.3 - 25 August, 2019
38
+
39
+ ### Bug fixes:
40
+
41
+ - Fix migrations for native enums to use table schema #3307
42
+
43
+ ### New features:
44
+
45
+ - Add ability to manually define schema for native enums #3307
46
+ - Add SSL/TLS support for Postgres connection string #3410
47
+ - CLI: new command that lists all migrations with status #3390
48
+
49
+ ### Typings:
50
+
51
+ - Include schemaName in EnumOptions #3415
52
+ - Allow `ColumnBuilder.defaultTo()` to be `null` #3407
53
+
54
+ ### Changes:
55
+
56
+ - migrate: Refactor _lockMigrations to avoid forUpdate - makes migrations compatible with CockroachDB #3395
57
+
58
+ # 0.19.2 - 17 August, 2019
59
+
60
+ ### Changes:
61
+
62
+ - Make transaction rejection consistent across dialects #3399
63
+ - More consistent handling of nested transactions #3393
64
+
65
+ ### New features:
66
+
67
+ - Fallback to JSON when using JSONB in MySQL #3394
68
+
3
69
  # 0.19.1 - 23 July, 2019
4
70
 
5
71
  ### New features:
@@ -8,7 +74,7 @@
8
74
  - Add .isCompleted() to transaction #3368
9
75
  - Minor enhancements around aliasing of aggregates #3354
10
76
 
11
- ### Typings
77
+ ### Typings:
12
78
 
13
79
  - Update configuration typings to allow for oracle db connectionstring #3361
14
80
  - Update Knex.raw type to be any by default because the actual type is dialect specific #3349
@@ -82,7 +148,7 @@
82
148
  - Remove Bluebird #3290 #3287 #3285 #3267 #3266 #3263
83
149
  - Fix comments that were modified by find & replace #3308
84
150
 
85
- ### Typings
151
+ ### Typings:
86
152
 
87
153
  - Add workarounds for degraded inference when strictNullChecks is set to false #3275
88
154
  - Add stub type definition for Migrator config #3279
@@ -92,13 +158,13 @@
92
158
 
93
159
  # 0.17.5 - 8 June, 2019
94
160
 
95
- ### Typings
161
+ ### Typings:
96
162
 
97
163
  - Include result.d.ts in published package #3271
98
164
 
99
165
  # 0.17.4 - 8 June, 2019
100
166
 
101
- ### Typings
167
+ ### Typings:
102
168
 
103
169
  - Fix some cases of left-to-right inference causing type mismatch #3265
104
170
  - Improve count typings #3249
@@ -109,7 +175,7 @@
109
175
 
110
176
  # 0.17.3 - 2 June, 2019
111
177
 
112
- ### Typings
178
+ ### Typings:
113
179
 
114
180
  - Improve typings for aggregations #3245
115
181
  - Add decimalNumbers to MySqlConnectionConfig interface #3244
@@ -130,7 +196,7 @@
130
196
 
131
197
  - Add migrate:down functionality #3228
132
198
 
133
- ### Typings
199
+ ### Typings:
134
200
 
135
201
  - Update type of aggregation results to not be arrays when first has been invoked before #3237
136
202
  - Include undefined in type of single row results #3231
@@ -150,7 +216,7 @@
150
216
  - Unify object options handling for datetime/timestamp across dialects #3181
151
217
  - Add "up" command for migrations #3205
152
218
 
153
- ### Typings
219
+ ### Typings:
154
220
 
155
221
  - Add default values for generic types (fixes backwards compatibility broken by 0.16.6) #3189
156
222
  - Make function types generic in type definitions #3168
@@ -283,7 +349,7 @@
283
349
  - Knex client knexfile resolution fix #2923
284
350
  - Add queryContext to type declarations #2931
285
351
 
286
- ### Test / internal changes
352
+ ### Test / internal changes:
287
353
 
288
354
  - Add tests for multiple union arguments with callbacks and builders #2749
289
355
  - Update dependencies #2772 #2810 #2842 #2848 #2893 #2904
@@ -351,7 +417,7 @@
351
417
  - Add support for native enums on Postgres #2632
352
418
  - Allow overwriting log functions #2625
353
419
 
354
- ### Test / internal changes
420
+ ### Test / internal changes:
355
421
 
356
422
  - chore: cache node_modules #2595
357
423
  - Remove babel-plugin-lodash #2634
@@ -381,7 +447,7 @@
381
447
  - Checks for an empty, undefined or null object on transacting #2494
382
448
  - countDistinct with multiple columns #2449
383
449
 
384
- ### Test / internal changes
450
+ ### Test / internal changes:
385
451
 
386
452
  - Added npm run test:oracledb command that runs oracledb tests in docker #2491
387
453
  - Runnin mssql tests in docker #2496
@@ -401,7 +467,7 @@
401
467
  - Allow calling lock procedures (such as forUpdate) outside of transaction. Fixes #2403. (#2475)
402
468
  - Added test and documentation for Event 'start' (#2488)
403
469
 
404
- ### Test / internal changes
470
+ ### Test / internal changes:
405
471
 
406
472
  - Added stress test, which uses TCP proxy to simulate flaky connection #2460
407
473
  - Removed old docker tests, new stress test setup (#2474)
@@ -429,7 +495,7 @@
429
495
  - Added redshift dialect #2233
430
496
  - Added warning when one uses .createTableIfNotExist and deprecated it from docs #2458
431
497
 
432
- ### Test / internal changes
498
+ ### Test / internal changes:
433
499
 
434
500
  - Update dependencies and fix ESLint warnings accordingly #2433
435
501
  - Disable oracledb tests from non LTS nodes #2407
@@ -492,7 +558,7 @@
492
558
  - Fix mssql wrong binding order of queries that combine a limit with select raw or update #2066
493
559
  - Fixed mysql alter table attributes order #2062
494
560
 
495
- ### Test / internal changes
561
+ ### Test / internal changes:
496
562
 
497
563
  - Update each out-of-date dependency according to david-dm.org #2297
498
564
  - Update v8flags to version 3.0.0 #2288
@@ -817,17 +883,17 @@
817
883
 
818
884
  # 0.7.0 - Oct 1, 2014
819
885
 
820
- ### New Features
886
+ ### New Features:
821
887
 
822
888
  - Oracle support, #419
823
889
  - Database seed file support, #391
824
890
  - Improved support for sub-raw queries within raw statements
825
891
 
826
- ### Breaking Changes
892
+ ### Breaking Changes:
827
893
 
828
894
  - "collate nocase" no longer used by default in sqlite3 #396
829
895
 
830
- ### Other Changes
896
+ ### Other Changes:
831
897
 
832
898
  - Bumping Bluebird to ^2.x
833
899
  - Transactions in websql are now a no-op (unsupported) #375
package/bin/cli.js CHANGED
@@ -21,6 +21,7 @@ const {
21
21
  getStubPath,
22
22
  } = require('./utils/cli-config-utils');
23
23
  const { DEFAULT_EXT } = require('./utils/constants');
24
+ const { listMigrations } = require('./utils/migrationsLister');
24
25
 
25
26
  function initKnex(env, opts) {
26
27
  checkLocalModule(env);
@@ -189,11 +190,13 @@ function invoke(env) {
189
190
  });
190
191
 
191
192
  commander
192
- .command('migrate:up')
193
- .description(' Run the next migration that has not yet been run.')
194
- .action(() => {
193
+ .command('migrate:up [<name>]')
194
+ .description(
195
+ ' Run the next or the specified migration that has not yet been run.'
196
+ )
197
+ .action((name) => {
195
198
  pending = initKnex(env, commander.opts())
196
- .migrate.up()
199
+ .migrate.up({ name })
197
200
  .then(([batchNo, log]) => {
198
201
  if (log.length === 0) {
199
202
  success(color.cyan('Already up to date'));
@@ -234,11 +237,13 @@ function invoke(env) {
234
237
  });
235
238
 
236
239
  commander
237
- .command('migrate:down')
238
- .description(' Undo the last migration performed.')
239
- .action(() => {
240
+ .command('migrate:down [<name>]')
241
+ .description(
242
+ ' Undo the last or the specified migration that was already run.'
243
+ )
244
+ .action((name) => {
240
245
  pending = initKnex(env, commander.opts())
241
- .migrate.down()
246
+ .migrate.down({ name })
242
247
  .then(([batchNo, log]) => {
243
248
  if (log.length === 0) {
244
249
  success(color.cyan('Already at the base migration'));
@@ -267,6 +272,19 @@ function invoke(env) {
267
272
  .catch(exit);
268
273
  });
269
274
 
275
+ commander
276
+ .command('migrate:list')
277
+ .alias('migrate:status')
278
+ .description(' List all migrations files with status.')
279
+ .action(() => {
280
+ pending = initKnex(env, commander.opts())
281
+ .migrate.list()
282
+ .then(([completed, newMigrations]) => {
283
+ listMigrations(completed, newMigrations);
284
+ })
285
+ .catch(exit);
286
+ });
287
+
270
288
  commander
271
289
  .command('seed:make <name>')
272
290
  .description(' Create a named seed file.')
@@ -0,0 +1,37 @@
1
+ const color = require('colorette');
2
+
3
+ const { success } = require('./cli-config-utils');
4
+
5
+ function listMigrations(completed, newMigrations) {
6
+ let message = '';
7
+
8
+ if (completed.length === 0) {
9
+ message += color.red('No Completed Migration files Found. \n');
10
+ } else {
11
+ message = color.green(
12
+ `Found ${completed.length} Completed Migration file/files.\n`
13
+ );
14
+
15
+ for (let i = 0; i < completed.length; i++) {
16
+ const file = completed[i];
17
+ message += color.cyan(`${file} \n`);
18
+ }
19
+ }
20
+
21
+ if (newMigrations.length === 0) {
22
+ message += color.red('No Pending Migration files Found.\n');
23
+ } else {
24
+ message += color.green(
25
+ `Found ${newMigrations.length} Pending Migration file/files.\n`
26
+ );
27
+
28
+ for (let i = 0; i < newMigrations.length; i++) {
29
+ const file = newMigrations[i];
30
+ message += color.cyan(`${file.file} \n`);
31
+ }
32
+ }
33
+
34
+ success(message);
35
+ }
36
+
37
+ module.exports = { listMigrations };
@@ -207,7 +207,11 @@ Object.assign(Client_MSSQL.prototype, {
207
207
  },
208
208
 
209
209
  wrapIdentifierImpl(value) {
210
- return value !== '*' ? `[${value.replace(/\[/g, '[')}]` : '*';
210
+ if (value === '*') {
211
+ return '*';
212
+ }
213
+
214
+ return `[${value.replace(/[[\]']+/g, '')}]`;
211
215
  },
212
216
 
213
217
  // Get a raw connection, called by the `pool` whenever a new
@@ -113,6 +113,10 @@ Object.assign(ColumnCompiler_MySQL.prototype, {
113
113
  return 'json';
114
114
  },
115
115
 
116
+ jsonb() {
117
+ return 'json';
118
+ },
119
+
116
120
  // Modifiers
117
121
  // ------
118
122
 
@@ -30,11 +30,7 @@ Object.assign(Transaction_MySQL.prototype, {
30
30
  if (status === 1) t._resolver(value);
31
31
  if (status === 2) {
32
32
  if (isUndefined(value)) {
33
- if (
34
- sql &&
35
- sql.toUpperCase() === 'ROLLBACK' &&
36
- t.doNotRejectOnRollback
37
- ) {
33
+ if (t.doNotRejectOnRollback && /^ROLLBACK\b/i.test(sql)) {
38
34
  t._resolver();
39
35
  return;
40
36
  }
@@ -29,11 +29,7 @@ Object.assign(Transaction_MySQL2.prototype, {
29
29
  if (status === 1) t._resolver(value);
30
30
  if (status === 2) {
31
31
  if (isUndefined(value)) {
32
- if (
33
- sql &&
34
- sql.toUpperCase() === 'ROLLBACK' &&
35
- t.doNotRejectOnRollback
36
- ) {
32
+ if (t.doNotRejectOnRollback && /^ROLLBACK\b/i.test(sql)) {
37
33
  t._resolver();
38
34
  return;
39
35
  }
@@ -263,6 +263,13 @@ Client_Oracledb.prototype._query = function(connection, obj) {
263
263
  ? response.rows.rowsAffected
264
264
  : response.rowsAffected;
265
265
 
266
+ //added for outBind parameter
267
+ if (obj.method === 'raw' && outBinds.length > 0) {
268
+ return resolver({
269
+ response: outBinds,
270
+ });
271
+ }
272
+
266
273
  if (obj.method === 'update') {
267
274
  const modifiedRowsCount = obj.rowsAffected.length || obj.rowsAffected;
268
275
  const updatedObjOutBinding = [];
@@ -35,13 +35,22 @@ Object.assign(ColumnCompiler_PG.prototype, {
35
35
  : allowed.join("', '");
36
36
 
37
37
  if (options.useNative) {
38
+ let enumName = '';
39
+ const schemaName = options.schemaName || this.tableCompiler.schemaNameRaw;
40
+
41
+ if (schemaName) {
42
+ enumName += `"${schemaName}".`;
43
+ }
44
+
45
+ enumName += `"${options.enumName}"`;
46
+
38
47
  if (!options.existingType) {
39
48
  this.tableCompiler.unshiftQuery(
40
- `create type "${options.enumName}" as enum ('${values}')`
49
+ `create type ${enumName} as enum ('${values}')`
41
50
  );
42
51
  }
43
52
 
44
- return `"${options.enumName}"`;
53
+ return enumName;
45
54
  }
46
55
  return `text check (${this.formatter.wrap(this.args[0])} in ('${values}'))`;
47
56
  },
@@ -129,7 +129,8 @@ assign(SQLite3_DDL.prototype, {
129
129
  },
130
130
 
131
131
  _doReplace(sql, from, to) {
132
- const matched = sql.match(/^CREATE TABLE (\S+) \((.*)\)/);
132
+ const oneLineSql = sql.replace(/\s+/g, ' ');
133
+ const matched = oneLineSql.match(/^CREATE TABLE (\S+) \((.*)\)/);
133
134
 
134
135
  const tableName = matched[1];
135
136
  const defs = matched[2];
@@ -166,23 +167,27 @@ assign(SQLite3_DDL.prototype, {
166
167
  }
167
168
  args.push(defs.slice(ptr, i));
168
169
 
169
- // Backwards compatible for double quoted sqlite databases
170
- // Detect CREATE TABLE "accounts" ("id"...)
171
- // The "from" and "to" field use backsticks, because this is the default notation for
172
- // SQlite3 since Knex 0.14.
173
- // e.g. from: `about`
174
- //
175
- // We have to replace the from+to field with double slashes in case you created your SQlite3
176
- // database with Knex < 0.14.
177
- if (sql.match(/CREATE\sTABLE\s".*"\s\("/)) {
178
- from = from.replace(/[`]/g, '"');
179
- to = to.replace(/[`]/g, '"');
180
- }
170
+ const fromIdentifier = from.replace(/[`"]/g, '');
181
171
 
182
172
  args = args.map(function(item) {
183
- let split = item.split(' ');
173
+ let split = item.trim().split(' ');
174
+
175
+ let normalizedFrom = from;
184
176
 
185
- if (split[0] === from) {
177
+ // Backwards compatible for double quoted sqlite databases
178
+ // The "from" and "to" field use backsticks, because this is the default notation for
179
+ // SQlite3 since Knex 0.14.
180
+ // e.g. from: `about`
181
+ //
182
+ // We have to replace the from+to field with double slashes in case you created your SQlite3
183
+ // database with Knex < 0.14.
184
+ if (item.match(`"${fromIdentifier}"`)) {
185
+ normalizedFrom = `"${fromIdentifier}"`;
186
+ } else if (item.match(`\`${fromIdentifier}\``)) {
187
+ normalizedFrom = `\`${fromIdentifier}\``;
188
+ }
189
+
190
+ if (split[0] === normalizedFrom) {
186
191
  // column definition
187
192
  if (to) {
188
193
  split[0] = to;
@@ -198,7 +203,9 @@ assign(SQLite3_DDL.prototype, {
198
203
  // columns from this table listed between (); replace
199
204
  // one if it matches
200
205
  if (/primary|unique/i.test(split[idx])) {
201
- return item.replace(/\(.*\)/, (columns) => columns.replace(from, to));
206
+ return item.replace(/\(.*\)/, (columns) =>
207
+ columns.replace(normalizedFrom, to)
208
+ );
202
209
  }
203
210
 
204
211
  // foreign keys have one or more columns from this table
@@ -210,11 +217,11 @@ assign(SQLite3_DDL.prototype, {
210
217
  split = item.split(/ references /i);
211
218
  // the quoted column names save us from having to do anything
212
219
  // other than a straight replace here
213
- split[0] = split[0].replace(from, to);
220
+ split[0] = split[0].replace(normalizedFrom, to);
214
221
 
215
222
  if (split[1].slice(0, tableName.length) === tableName) {
216
223
  split[1] = split[1].replace(/\(.*\)/, (columns) =>
217
- columns.replace(from, to)
224
+ columns.replace(normalizedFrom, to)
218
225
  );
219
226
  }
220
227
  return split.join(' references ');
@@ -222,7 +229,7 @@ assign(SQLite3_DDL.prototype, {
222
229
 
223
230
  return item;
224
231
  });
225
- return sql
232
+ return oneLineSql
226
233
  .replace(/\(.*\)/, () => `(${args.join(', ')})`)
227
234
  .replace(/,\s*([,)])/, '$1');
228
235
  },
package/lib/helpers.js CHANGED
@@ -36,12 +36,12 @@ function containsUndefined(mixed) {
36
36
  if (isArray(mixed)) {
37
37
  for (let i = 0; i < mixed.length; i++) {
38
38
  if (argContainsUndefined) break;
39
- argContainsUndefined = this.containsUndefined(mixed[i]);
39
+ argContainsUndefined = containsUndefined(mixed[i]);
40
40
  }
41
41
  } else if (isPlainObject(mixed)) {
42
42
  Object.keys(mixed).forEach((key) => {
43
43
  if (!argContainsUndefined) {
44
- argContainsUndefined = this.containsUndefined(mixed[key]);
44
+ argContainsUndefined = containsUndefined(mixed[key]);
45
45
  }
46
46
  });
47
47
  } else {
@@ -51,6 +51,28 @@ function containsUndefined(mixed) {
51
51
  return argContainsUndefined;
52
52
  }
53
53
 
54
+ function getUndefinedIndices(mixed) {
55
+ const indices = [];
56
+
57
+ if (Array.isArray(mixed)) {
58
+ mixed.forEach((item, index) => {
59
+ if (containsUndefined(item)) {
60
+ indices.push(index);
61
+ }
62
+ });
63
+ } else if (isPlainObject(mixed)) {
64
+ Object.keys(mixed).forEach((key) => {
65
+ if (containsUndefined(mixed[key])) {
66
+ indices.push(key);
67
+ }
68
+ });
69
+ } else {
70
+ indices.push(0);
71
+ }
72
+
73
+ return indices;
74
+ }
75
+
54
76
  function addQueryContext(Target) {
55
77
  // Stores or returns (if called with no arguments) context passed to
56
78
  // wrapIdentifier and postProcessResponse hooks
@@ -72,4 +94,5 @@ module.exports = {
72
94
  containsUndefined,
73
95
  normalizeArr,
74
96
  resolveClientNameWithAliases,
97
+ getUndefinedIndices,
75
98
  };
@@ -108,16 +108,38 @@ class Migrator {
108
108
  return value;
109
109
  })
110
110
  .then(([all, completed]) => {
111
- const migrationToRun = getNewMigrations(
111
+ const newMigrations = getNewMigrations(
112
112
  this.config.migrationSource,
113
113
  all,
114
114
  completed
115
- ).slice(0, 1);
115
+ );
116
+
117
+ let migrationToRun;
118
+ const name = this.config.name;
119
+ if (name) {
120
+ if (!completed.includes(name)) {
121
+ migrationToRun = newMigrations.find((migration) => {
122
+ return (
123
+ this.config.migrationSource.getMigrationName(migration) === name
124
+ );
125
+ });
126
+ if (!migrationToRun) {
127
+ throw new Error(`Migration "${name}" not found.`);
128
+ }
129
+ }
130
+ } else {
131
+ migrationToRun = newMigrations[0];
132
+ }
133
+
134
+ const migrationsToRun = [];
135
+ if (migrationToRun) {
136
+ migrationsToRun.push(migrationToRun);
137
+ }
116
138
 
117
139
  const transactionForAll =
118
140
  !this.config.disableTransactions &&
119
141
  isEmpty(
120
- filter(migrationToRun, (migration) => {
142
+ filter(migrationsToRun, (migration) => {
121
143
  const migrationContents = this.config.migrationSource.getMigration(
122
144
  migration
123
145
  );
@@ -128,10 +150,10 @@ class Migrator {
128
150
 
129
151
  if (transactionForAll) {
130
152
  return this.knex.transaction((trx) => {
131
- return this._runBatch(migrationToRun, 'up', trx);
153
+ return this._runBatch(migrationsToRun, 'up', trx);
132
154
  });
133
155
  } else {
134
- return this._runBatch(migrationToRun, 'up');
156
+ return this._runBatch(migrationsToRun, 'up');
135
157
  }
136
158
  });
137
159
  }
@@ -180,16 +202,33 @@ class Migrator {
180
202
  return value;
181
203
  })
182
204
  .then(([all, completed]) => {
183
- const migrationToRun = all
184
- .filter((migration) => {
185
- return completed.includes(
186
- this.config.migrationSource.getMigrationName(migration)
205
+ const completedMigrations = all.filter((migration) => {
206
+ return completed.includes(
207
+ this.config.migrationSource.getMigrationName(migration)
208
+ );
209
+ });
210
+
211
+ let migrationToRun;
212
+ const name = this.config.name;
213
+ if (name) {
214
+ migrationToRun = completedMigrations.find((migration) => {
215
+ return (
216
+ this.config.migrationSource.getMigrationName(migration) === name
187
217
  );
188
- })
189
- .reverse()
190
- .slice(0, 1);
218
+ });
219
+ if (!migrationToRun) {
220
+ throw new Error(`Migration "${name}" was not run.`);
221
+ }
222
+ } else {
223
+ migrationToRun = completedMigrations[completedMigrations.length - 1];
224
+ }
225
+
226
+ const migrationsToRun = [];
227
+ if (migrationToRun) {
228
+ migrationsToRun.push(migrationToRun);
229
+ }
191
230
 
192
- return this._runBatch(migrationToRun, 'down');
231
+ return this._runBatch(migrationsToRun, 'down');
193
232
  });
194
233
  }
195
234
 
@@ -219,6 +258,25 @@ class Migrator {
219
258
  });
220
259
  }
221
260
 
261
+ // list all migrations
262
+ async list(config) {
263
+ this._disableProcessing();
264
+ this.config = getMergedConfig(config, this.config);
265
+
266
+ const [all, completed] = await migrationListResolver.listAllAndCompleted(
267
+ this.config,
268
+ this.knex
269
+ );
270
+ validateMigrationList(this.config.migrationSource, [all, completed]);
271
+
272
+ const newMigrations = getNewMigrations(
273
+ this.config.migrationSource,
274
+ all,
275
+ completed
276
+ );
277
+ return [completed, newMigrations];
278
+ }
279
+
222
280
  forceFreeMigrationsLock(config) {
223
281
  this.config = getMergedConfig(config, this.config);
224
282
 
@@ -240,32 +298,23 @@ class Migrator {
240
298
  }
241
299
  }
242
300
 
243
- _isLocked(trx) {
244
- const tableName = getLockTableName(this.config.tableName);
245
- return getTable(this.knex, tableName, this.config.schemaName)
246
- .transacting(trx)
247
- .forUpdate()
248
- .select('*')
249
- .then((data) => data[0].is_locked);
250
- }
251
-
252
301
  _lockMigrations(trx) {
253
302
  const tableName = getLockTableName(this.config.tableName);
254
303
  return getTable(this.knex, tableName, this.config.schemaName)
255
304
  .transacting(trx)
256
- .update({ is_locked: 1 });
305
+ .where('is_locked', '=', 0)
306
+ .update({ is_locked: 1 })
307
+ .then((rowCount) => {
308
+ if (rowCount != 1) {
309
+ throw new Error('Migration table is already locked');
310
+ }
311
+ });
257
312
  }
258
313
 
259
314
  _getLock(trx) {
260
315
  const transact = trx ? (fn) => fn(trx) : (fn) => this.knex.transaction(fn);
261
316
  return transact((trx) => {
262
- return this._isLocked(trx)
263
- .then((isLocked) => {
264
- if (isLocked) {
265
- throw new Error('Migration table is already locked');
266
- }
267
- })
268
- .then(() => this._lockMigrations(trx));
317
+ return this._lockMigrations(trx);
269
318
  }).catch((err) => {
270
319
  throw new LockError(err.message);
271
320
  });
@@ -1271,8 +1271,8 @@ Builder.prototype.del = Builder.prototype.delete;
1271
1271
  require('../interface')(Builder);
1272
1272
  helpers.addQueryContext(Builder);
1273
1273
 
1274
- Builder.extend = function(methodName, fn) {
1275
- if (Builder.prototype.hasOwnProperty(methodName)) {
1274
+ Builder.extend = (methodName, fn) => {
1275
+ if (Object.prototype.hasOwnProperty.call(Builder.prototype, methodName)) {
1276
1276
  throw new Error(
1277
1277
  `Can't extend QueryBuilder with existing method ('${methodName}').`
1278
1278
  );
@@ -58,6 +58,7 @@ assign(QueryCompiler.prototype, {
58
58
  // Collapse the builder into a single object
59
59
  toSQL(method, tz) {
60
60
  this._undefinedInWhereClause = false;
61
+ this.undefinedBindingsInfo = [];
61
62
 
62
63
  method = method || this.method;
63
64
  const val = this[method]() || '';
@@ -99,7 +100,9 @@ assign(QueryCompiler.prototype, {
99
100
  debugBindings(query.bindings);
100
101
  throw new Error(
101
102
  `Undefined binding(s) detected when compiling ` +
102
- `${method.toUpperCase()} query: ${query.sql}`
103
+ `${method.toUpperCase()}. Undefined column(s): [${this.undefinedBindingsInfo.join(
104
+ ', '
105
+ )}] query: ${query.sql}`
103
106
  );
104
107
  }
105
108
 
@@ -372,6 +375,7 @@ assign(QueryCompiler.prototype, {
372
375
  Object.prototype.hasOwnProperty.call(stmt, 'value') &&
373
376
  helpers.containsUndefined(stmt.value)
374
377
  ) {
378
+ this.undefinedBindingsInfo.push(stmt.column);
375
379
  this._undefinedInWhereClause = true;
376
380
  }
377
381
  const val = this[stmt.type](stmt);
package/lib/raw.js CHANGED
@@ -104,9 +104,12 @@ assign(Raw.prototype, {
104
104
 
105
105
  obj.bindings = obj.bindings || [];
106
106
  if (helpers.containsUndefined(obj.bindings)) {
107
+ const undefinedBindingIndices = helpers.getUndefinedIndices(
108
+ this.bindings
109
+ );
107
110
  debugBindings(obj.bindings);
108
111
  throw new Error(
109
- `Undefined binding(s) detected when compiling RAW query: ` + obj.sql
112
+ `Undefined binding(s) detected for keys [${undefinedBindingIndices}] when compiling RAW query: ${obj.sql}`
110
113
  );
111
114
  }
112
115
 
@@ -210,6 +210,7 @@ class Transaction extends EventEmitter {
210
210
  // the original promise is marked completed.
211
211
  acquireConnection(client, config, txid) {
212
212
  const configConnection = config && config.connection;
213
+ const trx = this;
213
214
  return new Bluebird((resolve, reject) => {
214
215
  try {
215
216
  resolve(configConnection || client.acquireConnection());
@@ -220,7 +221,12 @@ class Transaction extends EventEmitter {
220
221
  .then(function(connection) {
221
222
  connection.__knexTxId = txid;
222
223
 
223
- return connection;
224
+ return (trx._previousSibling
225
+ ? trx._previousSibling.reflect()
226
+ : Promise.resolve()
227
+ ).then(function() {
228
+ return connection;
229
+ });
224
230
  })
225
231
  .disposer(function(connection) {
226
232
  if (!configConnection) {
@@ -250,7 +256,25 @@ function makeTransactor(trx, connection, trxClient) {
250
256
  transactor.userParams = trx.userParams || {};
251
257
 
252
258
  transactor.transaction = function(container, options) {
253
- return trxClient.transaction(container, options, trx);
259
+ if (!options) {
260
+ options = { doNotRejectOnRollback: true };
261
+ } else if (isUndefined(options.doNotRejectOnRollback)) {
262
+ options.doNotRejectOnRollback = true;
263
+ }
264
+
265
+ if (container) {
266
+ return trxClient.transaction(container, options, trx);
267
+ } else {
268
+ return new Promise((resolve, _reject) => {
269
+ trxClient.transaction(
270
+ (nestedTrx) => {
271
+ resolve(nestedTrx);
272
+ },
273
+ options,
274
+ trx
275
+ );
276
+ });
277
+ }
254
278
  };
255
279
  transactor.savepoint = function(container, options) {
256
280
  return transactor.transaction(container, options);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "knex",
3
- "version": "0.19.1",
3
+ "version": "0.19.5",
4
4
  "description": "A batteries-included SQL query & schema builder for Postgres, MySQL and SQLite3 and the Browser",
5
5
  "main": "knex.js",
6
6
  "types": "types/index.d.ts",
@@ -14,8 +14,10 @@
14
14
  "coveralls": "nyc report --reporter=text-lcov | coveralls",
15
15
  "lint": "eslint \"lib/**/*.js\" \"test/**/*.js\"",
16
16
  "lint:types": "dtslint types",
17
+ "lint:everything": "npm run lint:types && npm run lint",
17
18
  "test": "mocha --exit -t 10000 test/index.js && npm run test:tape && npm run test:cli",
18
- "test:nyc": "nyc mocha --exit --check-leaks --globals __core-js_shared__ -t 10000 test/index.js && npm run test:tape && npm run test:cli",
19
+ "test:coverage": "nyc mocha --exit --check-leaks --globals __core-js_shared__ -t 10000 test/index.js && npm run test:tape && npm run test:cli",
20
+ "test:everything": "npm run lint:everything && npm run test:coverage",
19
21
  "test:sqlite": "cross-env DB=sqlite3 npm test",
20
22
  "test:tape": "node test/tape/index.js | tap-spec",
21
23
  "test:cli": "cross-env KNEX_PATH=../knex.js KNEX=bin/cli.js jake -f test/jake/Jakefile",
@@ -26,20 +28,20 @@
26
28
  "stress:destroy": "docker-compose -f scripts/stress-test/docker-compose.yml stop"
27
29
  },
28
30
  "dependencies": {
29
- "bluebird": "^3.5.5",
30
- "colorette": "1.0.8",
31
- "commander": "^2.20.0",
31
+ "bluebird": "^3.7.0",
32
+ "colorette": "1.1.0",
33
+ "commander": "^3.0.2",
32
34
  "debug": "4.1.1",
33
- "getopts": "2.2.4",
35
+ "getopts": "2.2.5",
34
36
  "inherits": "~2.0.4",
35
37
  "interpret": "^1.2.0",
36
38
  "liftoff": "3.1.0",
37
39
  "lodash": "^4.17.15",
38
40
  "mkdirp": "^0.5.1",
39
- "pg-connection-string": "2.0.0",
41
+ "pg-connection-string": "2.1.0",
40
42
  "tarn": "^2.0.0",
41
43
  "tildify": "2.0.0",
42
- "uuid": "^3.3.2",
44
+ "uuid": "^3.3.3",
43
45
  "v8flags": "^3.1.3"
44
46
  },
45
47
  "lint-staged": {
@@ -49,39 +51,39 @@
49
51
  ]
50
52
  },
51
53
  "devDependencies": {
52
- "@types/node": "^10.14.13",
54
+ "@types/node": "^10.14.20",
53
55
  "JSONStream": "^1.3.5",
54
56
  "chai": "^4.2.0",
55
57
  "chai-subset-in-order": "^2.1.3",
56
- "cli-testlab": "^1.7.0",
57
- "coveralls": "^3.0.5",
58
- "cross-env": "^5.2.0",
59
- "dtslint": "^0.9.0",
60
- "eslint": "^6.1.0",
61
- "eslint-config-prettier": "^6.0.0",
58
+ "cli-testlab": "^1.8.0",
59
+ "coveralls": "^3.0.6",
60
+ "cross-env": "^6.0.3",
61
+ "dtslint": "^0.9.8",
62
+ "eslint": "^6.5.1",
63
+ "eslint-config-prettier": "^6.4.0",
62
64
  "eslint-plugin-import": "^2.18.2",
63
- "husky": "^3.0.1",
65
+ "husky": "^3.0.8",
64
66
  "jake": "^8.1.1",
65
- "lint-staged": "^9.2.0",
66
- "mocha": "^6.2.0",
67
+ "lint-staged": "^9.4.1",
68
+ "mocha": "^6.2.1",
67
69
  "mock-fs": "^4.10.1",
68
70
  "mssql": "^5.1.0",
69
71
  "mysql": "^2.17.1",
70
- "mysql2": "^1.6.5",
72
+ "mysql2": "^1.7.0",
71
73
  "nyc": "^14.1.1",
72
- "pg": "^7.11.0",
74
+ "pg": "^7.12.1",
73
75
  "pg-query-stream": "^2.0.0",
74
76
  "prettier": "^1.18.2",
75
- "rimraf": "^2.6.3",
76
- "sinon": "^7.3.2",
77
+ "rimraf": "^3.0.0",
78
+ "sinon": "^7.5.0",
77
79
  "sinon-chai": "^3.3.0",
78
- "source-map-support": "^0.5.12",
79
- "sqlite3": "^4.0.9",
80
+ "source-map-support": "^0.5.13",
81
+ "sqlite3": "^4.1.0",
80
82
  "tap-spec": "^5.0.0",
81
83
  "tape": "^4.11.0",
82
84
  "toxiproxy-node-client": "^2.0.6",
83
- "typescript": "^3.5.3",
84
- "webpack-cli": "^3.3.6"
85
+ "typescript": "^3.6.3",
86
+ "webpack-cli": "^3.3.9"
85
87
  },
86
88
  "buildDependencies": [
87
89
  "rimraf"
package/types/index.d.ts CHANGED
@@ -548,6 +548,10 @@ declare namespace Knex {
548
548
  ): QueryBuilder<TRecord, TRecord[K][]>;
549
549
  pluck<TResult2 extends {}>(column: string): QueryBuilder<TRecord, TResult2>;
550
550
 
551
+ insert(
552
+ data: MaybeArray<SafePartial<TRecord>>,
553
+ returning: '*'
554
+ ): QueryBuilder<TRecord, DeferredKeySelection<TRecord, never>[]>;
551
555
  insert<
552
556
  TKey extends StrKey<TRecord>,
553
557
  TResult2 = DeferredIndex.Augment<
@@ -601,6 +605,10 @@ declare namespace Knex {
601
605
  ...args: any[]
602
606
  ): QueryBuilder<TRecord2, TResult2>;
603
607
 
608
+ update(
609
+ data: MaybeArray<SafePartial<TRecord>>,
610
+ returning: '*'
611
+ ): QueryBuilder<TRecord, DeferredKeySelection<TRecord, never>[]>;
604
612
  update<
605
613
  TKey extends StrKey<TRecord>,
606
614
  TResult2 = DeferredIndex.Augment<
@@ -685,6 +693,7 @@ declare namespace Knex {
685
693
  ): QueryBuilder<TRecord, TResult2>;
686
694
  update<TResult2 = number>(columnName: string, value: Value): QueryBuilder<TRecord, TResult2>;
687
695
 
696
+ returning(column: '*'): QueryBuilder<TRecord, DeferredKeySelection<TRecord, never>[]>;
688
697
  returning<
689
698
  TKey extends StrKey<TRecord>,
690
699
  TResult2 = DeferredIndex.Augment<
@@ -708,6 +717,9 @@ declare namespace Knex {
708
717
  column: string | string[]
709
718
  ): QueryBuilder<TRecord, TResult2>;
710
719
 
720
+ del(
721
+ returning: '*'
722
+ ): QueryBuilder<TRecord, DeferredKeySelection<TRecord, never>[]>;
711
723
  del<
712
724
  TKey extends StrKey<TRecord>,
713
725
  TResult2 = DeferredIndex.Augment<
@@ -733,6 +745,9 @@ declare namespace Knex {
733
745
  ): QueryBuilder<TRecord, TResult2>;
734
746
  del<TResult2 = number>(): QueryBuilder<TRecord, TResult2>;
735
747
 
748
+ delete(
749
+ returning: '*'
750
+ ): QueryBuilder<TRecord, DeferredKeySelection<TRecord, never>[]>;
736
751
  delete<
737
752
  TKey extends StrKey<TRecord>,
738
753
  TResult2 = DeferredIndex.Augment<
@@ -759,8 +774,6 @@ declare namespace Knex {
759
774
  delete<TResult2 = number>(): QueryBuilder<TRecord, TResult2>;
760
775
 
761
776
  truncate(): QueryBuilder<TRecord, void>;
762
-
763
- clone(): QueryBuilder<TRecord, TResult>;
764
777
  }
765
778
 
766
779
  interface As<TRecord, TResult> {
@@ -1342,9 +1355,8 @@ declare namespace Knex {
1342
1355
  >
1343
1356
  extends QueryInterface<TRecord, TResult>,
1344
1357
  ChainableInterface<ResolveResult<TResult>> {
1345
- // [TODO] Doesn't seem to be available
1346
- // or: QueryBuilder;
1347
-
1358
+ or: QueryBuilder<TRecord, TResult>;
1359
+ not: QueryBuilder<TRecord, TResult>;
1348
1360
  and: QueryBuilder<TRecord, TResult>;
1349
1361
 
1350
1362
  // TODO: Promise?
@@ -1364,6 +1376,9 @@ declare namespace Knex {
1364
1376
  on(event: string, callback: Function): QueryBuilder<TRecord, TResult>;
1365
1377
 
1366
1378
  queryContext(context: any): QueryBuilder<TRecord, TResult>;
1379
+
1380
+ clone(): QueryBuilder<TRecord, TResult>;
1381
+ timeout(ms: number, options?: {cancel?: boolean}): QueryBuilder<TRecord, TResult>;
1367
1382
  }
1368
1383
 
1369
1384
  interface Sql {
@@ -1543,7 +1558,7 @@ declare namespace Knex {
1543
1558
  references(columnName: string): ReferencingColumnBuilder;
1544
1559
  onDelete(command: string): ColumnBuilder;
1545
1560
  onUpdate(command: string): ColumnBuilder;
1546
- defaultTo(value: Value): ColumnBuilder;
1561
+ defaultTo(value: Value | null): ColumnBuilder;
1547
1562
  unsigned(): ColumnBuilder;
1548
1563
  notNullable(): ColumnBuilder;
1549
1564
  nullable(): ColumnBuilder;
@@ -1724,7 +1739,7 @@ declare namespace Knex {
1724
1739
  ssl?: string | MariaSslConfiguration;
1725
1740
  decimalNumbers?: boolean;
1726
1741
  }
1727
-
1742
+
1728
1743
  interface OracleDbConnectionConfig {
1729
1744
  host: string;
1730
1745
  user: string;
@@ -1811,6 +1826,7 @@ declare namespace Knex {
1811
1826
  rollback(config?: MigratorConfig, all?: boolean): Promise<any>;
1812
1827
  status(config?: MigratorConfig): Promise<number>;
1813
1828
  currentVersion(config?: MigratorConfig): Promise<string>;
1829
+ list(config?: MigratorConfig): Promise<any>;
1814
1830
  up(config?: MigratorConfig): Promise<any>;
1815
1831
  down(config?: MigratorConfig): Promise<any>;
1816
1832
  }
@@ -1819,6 +1835,7 @@ declare namespace Knex {
1819
1835
  extension?: string;
1820
1836
  directory?: string;
1821
1837
  loadExtensions?: string[];
1838
+ specific?: string;
1822
1839
  }
1823
1840
 
1824
1841
  class Seeder {
@@ -1834,7 +1851,8 @@ declare namespace Knex {
1834
1851
 
1835
1852
  interface EnumOptions {
1836
1853
  useNative: boolean;
1837
- existingType: boolean;
1854
+ existingType?: boolean;
1855
+ schemaName?: string;
1838
1856
  enumName: string;
1839
1857
  }
1840
1858
 
@@ -1855,7 +1873,7 @@ declare namespace Knex {
1855
1873
  }
1856
1874
 
1857
1875
  class QueryBuilder {
1858
- public static extend(
1876
+ static extend(
1859
1877
  methodName: string,
1860
1878
  fn: <TRecord extends {} = any, TResult = unknown[]>(
1861
1879
  this: Knex<TRecord, TResult>,