knex 0.21.20 → 0.21.21

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.
Files changed (141) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/CONTRIBUTING.md +184 -184
  3. package/LICENSE +22 -22
  4. package/README.md +95 -95
  5. package/bin/cli.js +414 -414
  6. package/bin/utils/cli-config-utils.js +151 -151
  7. package/bin/utils/constants.js +7 -7
  8. package/bin/utils/migrationsLister.js +37 -37
  9. package/knex.js +8 -8
  10. package/lib/client.js +413 -413
  11. package/lib/config-resolver.js +61 -61
  12. package/lib/constants.js +44 -44
  13. package/lib/dialects/mssql/index.js +390 -390
  14. package/lib/dialects/mssql/query/compiler.js +444 -444
  15. package/lib/dialects/mssql/schema/columncompiler.js +103 -103
  16. package/lib/dialects/mssql/schema/compiler.js +59 -59
  17. package/lib/dialects/mssql/schema/tablecompiler.js +245 -245
  18. package/lib/dialects/mssql/transaction.js +97 -97
  19. package/lib/dialects/mysql/index.js +191 -191
  20. package/lib/dialects/mysql/query/compiler.js +142 -142
  21. package/lib/dialects/mysql/schema/columncompiler.js +171 -171
  22. package/lib/dialects/mysql/schema/compiler.js +60 -60
  23. package/lib/dialects/mysql/schema/tablecompiler.js +262 -262
  24. package/lib/dialects/mysql/transaction.js +48 -48
  25. package/lib/dialects/mysql2/index.js +35 -35
  26. package/lib/dialects/mysql2/transaction.js +46 -46
  27. package/lib/dialects/oracle/DEAD_CODE.md +5 -5
  28. package/lib/dialects/oracle/formatter.js +20 -20
  29. package/lib/dialects/oracle/index.js +79 -79
  30. package/lib/dialects/oracle/query/compiler.js +327 -327
  31. package/lib/dialects/oracle/schema/columnbuilder.js +18 -18
  32. package/lib/dialects/oracle/schema/columncompiler.js +139 -139
  33. package/lib/dialects/oracle/schema/compiler.js +81 -81
  34. package/lib/dialects/oracle/schema/tablecompiler.js +165 -165
  35. package/lib/dialects/oracle/schema/trigger.js +126 -126
  36. package/lib/dialects/oracle/utils.js +86 -86
  37. package/lib/dialects/oracledb/index.js +489 -489
  38. package/lib/dialects/oracledb/query/compiler.js +363 -363
  39. package/lib/dialects/oracledb/schema/columncompiler.js +35 -35
  40. package/lib/dialects/oracledb/transaction.js +76 -76
  41. package/lib/dialects/oracledb/utils.js +14 -14
  42. package/lib/dialects/postgres/index.js +319 -319
  43. package/lib/dialects/postgres/query/compiler.js +206 -206
  44. package/lib/dialects/postgres/schema/columncompiler.js +125 -125
  45. package/lib/dialects/postgres/schema/compiler.js +109 -109
  46. package/lib/dialects/postgres/schema/tablecompiler.js +183 -183
  47. package/lib/dialects/redshift/index.js +73 -73
  48. package/lib/dialects/redshift/query/compiler.js +119 -119
  49. package/lib/dialects/redshift/schema/columnbuilder.js +20 -20
  50. package/lib/dialects/redshift/schema/columncompiler.js +60 -60
  51. package/lib/dialects/redshift/schema/compiler.js +14 -14
  52. package/lib/dialects/redshift/schema/tablecompiler.js +123 -123
  53. package/lib/dialects/redshift/transaction.js +18 -18
  54. package/lib/dialects/sqlite3/formatter.js +21 -21
  55. package/lib/dialects/sqlite3/index.js +169 -169
  56. package/lib/dialects/sqlite3/query/compiler.js +222 -222
  57. package/lib/dialects/sqlite3/schema/columncompiler.js +27 -27
  58. package/lib/dialects/sqlite3/schema/compiler.js +49 -49
  59. package/lib/dialects/sqlite3/schema/ddl.js +525 -525
  60. package/lib/dialects/sqlite3/schema/tablecompiler.js +238 -238
  61. package/lib/formatter.js +295 -295
  62. package/lib/functionhelper.js +14 -14
  63. package/lib/helpers.js +92 -92
  64. package/lib/index.js +3 -3
  65. package/lib/interface.js +115 -115
  66. package/lib/knex.js +42 -42
  67. package/lib/logger.js +76 -76
  68. package/lib/migrate/MigrationGenerator.js +82 -82
  69. package/lib/migrate/Migrator.js +611 -611
  70. package/lib/migrate/configuration-merger.js +60 -60
  71. package/lib/migrate/migrate-stub.js +17 -17
  72. package/lib/migrate/migration-list-resolver.js +36 -36
  73. package/lib/migrate/sources/fs-migrations.js +99 -99
  74. package/lib/migrate/stub/cjs.stub +15 -15
  75. package/lib/migrate/stub/coffee.stub +13 -13
  76. package/lib/migrate/stub/eg.stub +14 -14
  77. package/lib/migrate/stub/js.stub +15 -15
  78. package/lib/migrate/stub/knexfile-coffee.stub +34 -34
  79. package/lib/migrate/stub/knexfile-eg.stub +43 -43
  80. package/lib/migrate/stub/knexfile-js.stub +44 -44
  81. package/lib/migrate/stub/knexfile-ls.stub +35 -35
  82. package/lib/migrate/stub/knexfile-ts.stub +44 -44
  83. package/lib/migrate/stub/ls.stub +14 -14
  84. package/lib/migrate/stub/ts.stub +21 -21
  85. package/lib/migrate/table-creator.js +67 -67
  86. package/lib/migrate/table-resolver.js +27 -27
  87. package/lib/query/builder.js +1372 -1372
  88. package/lib/query/compiler.js +889 -889
  89. package/lib/query/constants.js +13 -13
  90. package/lib/query/joinclause.js +263 -263
  91. package/lib/query/methods.js +92 -92
  92. package/lib/query/string.js +190 -190
  93. package/lib/raw.js +188 -188
  94. package/lib/ref.js +39 -39
  95. package/lib/runner.js +285 -285
  96. package/lib/schema/builder.js +82 -82
  97. package/lib/schema/columnbuilder.js +117 -117
  98. package/lib/schema/columncompiler.js +177 -177
  99. package/lib/schema/compiler.js +101 -101
  100. package/lib/schema/helpers.js +51 -51
  101. package/lib/schema/tablebuilder.js +288 -288
  102. package/lib/schema/tablecompiler.js +296 -296
  103. package/lib/seed/Seeder.js +203 -203
  104. package/lib/seed/seed-stub.js +13 -13
  105. package/lib/seed/stub/coffee.stub +9 -9
  106. package/lib/seed/stub/eg.stub +11 -11
  107. package/lib/seed/stub/js.stub +13 -13
  108. package/lib/seed/stub/ls.stub +11 -11
  109. package/lib/seed/stub/ts.stub +13 -13
  110. package/lib/transaction.js +363 -363
  111. package/lib/util/batchInsert.js +59 -59
  112. package/lib/util/delay.js +6 -6
  113. package/lib/util/fake-client.js +9 -9
  114. package/lib/util/finally-mixin.js +13 -13
  115. package/lib/util/fs.js +76 -76
  116. package/lib/util/import-file.js +13 -13
  117. package/lib/util/is-module-type.js +14 -14
  118. package/lib/util/is.js +32 -32
  119. package/lib/util/make-knex.js +338 -338
  120. package/lib/util/nanoid.js +29 -29
  121. package/lib/util/noop.js +1 -1
  122. package/lib/util/parse-connection.js +66 -66
  123. package/lib/util/save-async-stack.js +14 -14
  124. package/lib/util/template.js +52 -52
  125. package/lib/util/timeout.js +29 -29
  126. package/lib/util/timestamp.js +16 -16
  127. package/package.json +1 -1
  128. package/scripts/build.js +125 -125
  129. package/scripts/docker-compose.yml +111 -111
  130. package/scripts/next-release-howto.md +24 -24
  131. package/scripts/release.sh +34 -34
  132. package/scripts/runkit-example.js +34 -34
  133. package/scripts/stress-test/README.txt +18 -18
  134. package/scripts/stress-test/docker-compose.yml +47 -47
  135. package/scripts/stress-test/knex-stress-test.js +196 -196
  136. package/scripts/stress-test/mysql2-random-hanging-every-now-and-then.js +145 -145
  137. package/scripts/stress-test/mysql2-sudden-exit-without-error.js +100 -100
  138. package/scripts/stress-test/reconnect-test-mysql-based-drivers.js +184 -184
  139. package/types/index.d.ts +2249 -2249
  140. package/types/result.d.ts +27 -27
  141. package/types/tables.d.ts +4 -4
@@ -1,363 +1,363 @@
1
- // Transaction
2
- // -------
3
- const { EventEmitter } = require('events');
4
- const Debug = require('debug');
5
- const uniqueId = require('lodash/uniqueId');
6
- const { callbackify } = require('util');
7
-
8
- const makeKnex = require('./util/make-knex');
9
- const { timeout, KnexTimeoutError } = require('./util/timeout');
10
- const finallyMixin = require('./util/finally-mixin');
11
-
12
- const debug = Debug('knex:tx');
13
-
14
- // FYI: This is defined as a function instead of a constant so that
15
- // each Transactor can have its own copy of the default config.
16
- // This will minimize the impact of bugs that might be introduced
17
- // if a Transactor ever mutates its config.
18
- function DEFAULT_CONFIG() {
19
- return {
20
- userParams: {},
21
- doNotRejectOnRollback: true,
22
- };
23
- }
24
-
25
- // Acts as a facade for a Promise, keeping the internal state
26
- // and managing any child transactions.
27
- class Transaction extends EventEmitter {
28
- constructor(client, container, config = DEFAULT_CONFIG(), outerTx = null) {
29
- super();
30
- this.userParams = config.userParams;
31
- this.doNotRejectOnRollback = config.doNotRejectOnRollback;
32
-
33
- const txid = (this.txid = uniqueId('trx'));
34
-
35
- this.client = client;
36
- this.logger = client.logger;
37
- this.outerTx = outerTx;
38
- this.trxClient = undefined;
39
- this._completed = false;
40
- this._debug = client.config && client.config.debug;
41
-
42
- debug(
43
- '%s: Starting %s transaction',
44
- txid,
45
- outerTx ? 'nested' : 'top level'
46
- );
47
-
48
- // `this` can potentially serve as an `outerTx` for another
49
- // Transaction. So, go ahead and establish `_lastChild` now.
50
- this._lastChild = Promise.resolve();
51
-
52
- const _previousSibling = outerTx ? outerTx._lastChild : Promise.resolve();
53
-
54
- // FYI: As you will see in a moment, this Promise will be used to construct
55
- // 2 separate Promise Chains. This ensures that each Promise Chain
56
- // can establish its error-handling semantics without interfering
57
- // with the other Promise Chain.
58
- const basePromise = _previousSibling.then(() =>
59
- this._evaluateContainer(config, container)
60
- );
61
-
62
- // FYI: This is the Promise Chain for EXTERNAL use. It ensures that the
63
- // caller must handle any exceptions that result from `basePromise`.
64
- this._promise = basePromise.then((x) => x);
65
-
66
- if (outerTx) {
67
- // FYI: This is the Promise Chain for INTERNAL use. It serves as a signal
68
- // for when the next sibling should begin its execution. Therefore,
69
- // exceptions are caught and ignored.
70
- outerTx._lastChild = basePromise.catch(() => {});
71
- }
72
- }
73
-
74
- isCompleted() {
75
- return (
76
- this._completed || (this.outerTx && this.outerTx.isCompleted()) || false
77
- );
78
- }
79
-
80
- begin(conn) {
81
- return this.query(conn, 'BEGIN;');
82
- }
83
-
84
- savepoint(conn) {
85
- return this.query(conn, `SAVEPOINT ${this.txid};`);
86
- }
87
-
88
- commit(conn, value) {
89
- return this.query(conn, 'COMMIT;', 1, value);
90
- }
91
-
92
- release(conn, value) {
93
- return this.query(conn, `RELEASE SAVEPOINT ${this.txid};`, 1, value);
94
- }
95
-
96
- rollback(conn, error) {
97
- return timeout(this.query(conn, 'ROLLBACK', 2, error), 5000).catch(
98
- (err) => {
99
- if (!(err instanceof KnexTimeoutError)) {
100
- return Promise.reject(err);
101
- }
102
- this._rejecter(error);
103
- }
104
- );
105
- }
106
-
107
- rollbackTo(conn, error) {
108
- return timeout(
109
- this.query(conn, `ROLLBACK TO SAVEPOINT ${this.txid}`, 2, error),
110
- 5000
111
- ).catch((err) => {
112
- if (!(err instanceof KnexTimeoutError)) {
113
- return Promise.reject(err);
114
- }
115
- this._rejecter(error);
116
- });
117
- }
118
-
119
- query(conn, sql, status, value) {
120
- const q = this.trxClient
121
- .query(conn, sql)
122
- .catch((err) => {
123
- status = 2;
124
- value = err;
125
- this._completed = true;
126
- debug('%s error running transaction query', this.txid);
127
- })
128
- .then((res) => {
129
- if (status === 1) {
130
- this._resolver(value);
131
- }
132
- if (status === 2) {
133
- if (value === undefined) {
134
- if (this.doNotRejectOnRollback && /^ROLLBACK\b/i.test(sql)) {
135
- this._resolver();
136
- return;
137
- }
138
-
139
- value = new Error(`Transaction rejected with non-error: ${value}`);
140
- }
141
- this._rejecter(value);
142
- }
143
- return res;
144
- });
145
- if (status === 1 || status === 2) {
146
- this._completed = true;
147
- }
148
- return q;
149
- }
150
-
151
- debug(enabled) {
152
- this._debug = arguments.length ? enabled : true;
153
- return this;
154
- }
155
-
156
- async _evaluateContainer(config, container) {
157
- return this.acquireConnection(config, (connection) => {
158
- const trxClient = (this.trxClient = makeTxClient(
159
- this,
160
- this.client,
161
- connection
162
- ));
163
- const init = this.client.transacting
164
- ? this.savepoint(connection)
165
- : this.begin(connection);
166
- const executionPromise = new Promise((resolver, rejecter) => {
167
- this._resolver = resolver;
168
- this._rejecter = rejecter;
169
- });
170
-
171
- init
172
- .then(() => {
173
- return makeTransactor(this, connection, trxClient);
174
- })
175
- .then((transactor) => {
176
- transactor.executionPromise = executionPromise;
177
-
178
- // If we've returned a "thenable" from the transaction container, assume
179
- // the rollback and commit are chained to this object's success / failure.
180
- // Directly thrown errors are treated as automatic rollbacks.
181
- let result;
182
- try {
183
- result = container(transactor);
184
- } catch (err) {
185
- result = Promise.reject(err);
186
- }
187
- if (result && result.then && typeof result.then === 'function') {
188
- result
189
- .then((val) => {
190
- return transactor.commit(val);
191
- })
192
- .catch((err) => {
193
- return transactor.rollback(err);
194
- });
195
- }
196
- return null;
197
- })
198
- .catch((e) => {
199
- return this._rejecter(e);
200
- });
201
-
202
- return executionPromise;
203
- });
204
- }
205
-
206
- // Acquire a connection and create a disposer - either using the one passed
207
- // via config or getting one off the client. The disposer will be called once
208
- // the original promise is marked completed.
209
- async acquireConnection(config, cb) {
210
- const configConnection = config && config.connection;
211
- const connection =
212
- configConnection || (await this.client.acquireConnection());
213
-
214
- try {
215
- connection.__knexTxId = this.txid;
216
- return await cb(connection);
217
- } finally {
218
- if (!configConnection) {
219
- debug('%s: releasing connection', this.txid);
220
- this.client.releaseConnection(connection);
221
- } else {
222
- debug('%s: not releasing external connection', this.txid);
223
- }
224
- }
225
- }
226
-
227
- then(onResolve, onReject) {
228
- return this._promise.then(onResolve, onReject);
229
- }
230
-
231
- catch(onReject) {
232
- return this._promise.catch(onReject);
233
- }
234
-
235
- asCallback(cb) {
236
- callbackify(() => this._promise)(cb);
237
- return this._promise;
238
- }
239
- }
240
- finallyMixin(Transaction.prototype);
241
-
242
- // The transactor is a full featured knex object, with a "commit", a "rollback"
243
- // and a "savepoint" function. The "savepoint" is just sugar for creating a new
244
- // transaction. If the rollback is run inside a savepoint, it rolls back to the
245
- // last savepoint - otherwise it rolls back the transaction.
246
- function makeTransactor(trx, connection, trxClient) {
247
- const transactor = makeKnex(trxClient);
248
-
249
- transactor.context.withUserParams = () => {
250
- throw new Error(
251
- 'Cannot set user params on a transaction - it can only inherit params from main knex instance'
252
- );
253
- };
254
-
255
- transactor.isTransaction = true;
256
- transactor.userParams = trx.userParams || {};
257
-
258
- transactor.context.transaction = function (container, options) {
259
- if (!options) {
260
- options = { doNotRejectOnRollback: true };
261
- } else if (options.doNotRejectOnRollback === undefined) {
262
- options.doNotRejectOnRollback = true;
263
- }
264
-
265
- return this._transaction(container, options, trx);
266
- };
267
-
268
- transactor.savepoint = function (container, options) {
269
- return transactor.transaction(container, options);
270
- };
271
-
272
- if (trx.client.transacting) {
273
- transactor.commit = (value) => trx.release(connection, value);
274
- transactor.rollback = (error) => trx.rollbackTo(connection, error);
275
- } else {
276
- transactor.commit = (value) => trx.commit(connection, value);
277
- transactor.rollback = (error) => trx.rollback(connection, error);
278
- }
279
-
280
- transactor.isCompleted = () => trx.isCompleted();
281
-
282
- return transactor;
283
- }
284
-
285
- // We need to make a client object which always acquires the same
286
- // connection and does not release back into the pool.
287
- function makeTxClient(trx, client, connection) {
288
- const trxClient = Object.create(client.constructor.prototype);
289
- trxClient.version = client.version;
290
- trxClient.config = client.config;
291
- trxClient.driver = client.driver;
292
- trxClient.connectionSettings = client.connectionSettings;
293
- trxClient.transacting = true;
294
- trxClient.valueForUndefined = client.valueForUndefined;
295
- trxClient.logger = client.logger;
296
-
297
- trxClient.on('start', function (arg) {
298
- trx.emit('start', arg);
299
- client.emit('start', arg);
300
- });
301
-
302
- trxClient.on('query', function (arg) {
303
- trx.emit('query', arg);
304
- client.emit('query', arg);
305
- });
306
-
307
- trxClient.on('query-error', function (err, obj) {
308
- trx.emit('query-error', err, obj);
309
- client.emit('query-error', err, obj);
310
- });
311
-
312
- trxClient.on('query-response', function (response, obj, builder) {
313
- trx.emit('query-response', response, obj, builder);
314
- client.emit('query-response', response, obj, builder);
315
- });
316
-
317
- const _query = trxClient.query;
318
- trxClient.query = function (conn, obj) {
319
- const completed = trx.isCompleted();
320
- return new Promise(function (resolve, reject) {
321
- try {
322
- if (conn !== connection)
323
- throw new Error('Invalid connection for transaction query.');
324
- if (completed) completedError(trx, obj);
325
- resolve(_query.call(trxClient, conn, obj));
326
- } catch (e) {
327
- reject(e);
328
- }
329
- });
330
- };
331
- const _stream = trxClient.stream;
332
- trxClient.stream = function (conn, obj, stream, options) {
333
- const completed = trx.isCompleted();
334
- return new Promise(function (resolve, reject) {
335
- try {
336
- if (conn !== connection)
337
- throw new Error('Invalid connection for transaction query.');
338
- if (completed) completedError(trx, obj);
339
- resolve(_stream.call(trxClient, conn, obj, stream, options));
340
- } catch (e) {
341
- reject(e);
342
- }
343
- });
344
- };
345
- trxClient.acquireConnection = function () {
346
- return Promise.resolve(connection);
347
- };
348
- trxClient.releaseConnection = function () {
349
- return Promise.resolve();
350
- };
351
-
352
- return trxClient;
353
- }
354
-
355
- function completedError(trx, obj) {
356
- const sql = typeof obj === 'string' ? obj : obj && obj.sql;
357
- debug('%s: Transaction completed: %s', trx.txid, sql);
358
- throw new Error(
359
- 'Transaction query already complete, run with DEBUG=knex:tx for more info'
360
- );
361
- }
362
-
363
- module.exports = Transaction;
1
+ // Transaction
2
+ // -------
3
+ const { EventEmitter } = require('events');
4
+ const Debug = require('debug');
5
+ const uniqueId = require('lodash/uniqueId');
6
+ const { callbackify } = require('util');
7
+
8
+ const makeKnex = require('./util/make-knex');
9
+ const { timeout, KnexTimeoutError } = require('./util/timeout');
10
+ const finallyMixin = require('./util/finally-mixin');
11
+
12
+ const debug = Debug('knex:tx');
13
+
14
+ // FYI: This is defined as a function instead of a constant so that
15
+ // each Transactor can have its own copy of the default config.
16
+ // This will minimize the impact of bugs that might be introduced
17
+ // if a Transactor ever mutates its config.
18
+ function DEFAULT_CONFIG() {
19
+ return {
20
+ userParams: {},
21
+ doNotRejectOnRollback: true,
22
+ };
23
+ }
24
+
25
+ // Acts as a facade for a Promise, keeping the internal state
26
+ // and managing any child transactions.
27
+ class Transaction extends EventEmitter {
28
+ constructor(client, container, config = DEFAULT_CONFIG(), outerTx = null) {
29
+ super();
30
+ this.userParams = config.userParams;
31
+ this.doNotRejectOnRollback = config.doNotRejectOnRollback;
32
+
33
+ const txid = (this.txid = uniqueId('trx'));
34
+
35
+ this.client = client;
36
+ this.logger = client.logger;
37
+ this.outerTx = outerTx;
38
+ this.trxClient = undefined;
39
+ this._completed = false;
40
+ this._debug = client.config && client.config.debug;
41
+
42
+ debug(
43
+ '%s: Starting %s transaction',
44
+ txid,
45
+ outerTx ? 'nested' : 'top level'
46
+ );
47
+
48
+ // `this` can potentially serve as an `outerTx` for another
49
+ // Transaction. So, go ahead and establish `_lastChild` now.
50
+ this._lastChild = Promise.resolve();
51
+
52
+ const _previousSibling = outerTx ? outerTx._lastChild : Promise.resolve();
53
+
54
+ // FYI: As you will see in a moment, this Promise will be used to construct
55
+ // 2 separate Promise Chains. This ensures that each Promise Chain
56
+ // can establish its error-handling semantics without interfering
57
+ // with the other Promise Chain.
58
+ const basePromise = _previousSibling.then(() =>
59
+ this._evaluateContainer(config, container)
60
+ );
61
+
62
+ // FYI: This is the Promise Chain for EXTERNAL use. It ensures that the
63
+ // caller must handle any exceptions that result from `basePromise`.
64
+ this._promise = basePromise.then((x) => x);
65
+
66
+ if (outerTx) {
67
+ // FYI: This is the Promise Chain for INTERNAL use. It serves as a signal
68
+ // for when the next sibling should begin its execution. Therefore,
69
+ // exceptions are caught and ignored.
70
+ outerTx._lastChild = basePromise.catch(() => {});
71
+ }
72
+ }
73
+
74
+ isCompleted() {
75
+ return (
76
+ this._completed || (this.outerTx && this.outerTx.isCompleted()) || false
77
+ );
78
+ }
79
+
80
+ begin(conn) {
81
+ return this.query(conn, 'BEGIN;');
82
+ }
83
+
84
+ savepoint(conn) {
85
+ return this.query(conn, `SAVEPOINT ${this.txid};`);
86
+ }
87
+
88
+ commit(conn, value) {
89
+ return this.query(conn, 'COMMIT;', 1, value);
90
+ }
91
+
92
+ release(conn, value) {
93
+ return this.query(conn, `RELEASE SAVEPOINT ${this.txid};`, 1, value);
94
+ }
95
+
96
+ rollback(conn, error) {
97
+ return timeout(this.query(conn, 'ROLLBACK', 2, error), 5000).catch(
98
+ (err) => {
99
+ if (!(err instanceof KnexTimeoutError)) {
100
+ return Promise.reject(err);
101
+ }
102
+ this._rejecter(error);
103
+ }
104
+ );
105
+ }
106
+
107
+ rollbackTo(conn, error) {
108
+ return timeout(
109
+ this.query(conn, `ROLLBACK TO SAVEPOINT ${this.txid}`, 2, error),
110
+ 5000
111
+ ).catch((err) => {
112
+ if (!(err instanceof KnexTimeoutError)) {
113
+ return Promise.reject(err);
114
+ }
115
+ this._rejecter(error);
116
+ });
117
+ }
118
+
119
+ query(conn, sql, status, value) {
120
+ const q = this.trxClient
121
+ .query(conn, sql)
122
+ .catch((err) => {
123
+ status = 2;
124
+ value = err;
125
+ this._completed = true;
126
+ debug('%s error running transaction query', this.txid);
127
+ })
128
+ .then((res) => {
129
+ if (status === 1) {
130
+ this._resolver(value);
131
+ }
132
+ if (status === 2) {
133
+ if (value === undefined) {
134
+ if (this.doNotRejectOnRollback && /^ROLLBACK\b/i.test(sql)) {
135
+ this._resolver();
136
+ return;
137
+ }
138
+
139
+ value = new Error(`Transaction rejected with non-error: ${value}`);
140
+ }
141
+ this._rejecter(value);
142
+ }
143
+ return res;
144
+ });
145
+ if (status === 1 || status === 2) {
146
+ this._completed = true;
147
+ }
148
+ return q;
149
+ }
150
+
151
+ debug(enabled) {
152
+ this._debug = arguments.length ? enabled : true;
153
+ return this;
154
+ }
155
+
156
+ async _evaluateContainer(config, container) {
157
+ return this.acquireConnection(config, (connection) => {
158
+ const trxClient = (this.trxClient = makeTxClient(
159
+ this,
160
+ this.client,
161
+ connection
162
+ ));
163
+ const init = this.client.transacting
164
+ ? this.savepoint(connection)
165
+ : this.begin(connection);
166
+ const executionPromise = new Promise((resolver, rejecter) => {
167
+ this._resolver = resolver;
168
+ this._rejecter = rejecter;
169
+ });
170
+
171
+ init
172
+ .then(() => {
173
+ return makeTransactor(this, connection, trxClient);
174
+ })
175
+ .then((transactor) => {
176
+ transactor.executionPromise = executionPromise;
177
+
178
+ // If we've returned a "thenable" from the transaction container, assume
179
+ // the rollback and commit are chained to this object's success / failure.
180
+ // Directly thrown errors are treated as automatic rollbacks.
181
+ let result;
182
+ try {
183
+ result = container(transactor);
184
+ } catch (err) {
185
+ result = Promise.reject(err);
186
+ }
187
+ if (result && result.then && typeof result.then === 'function') {
188
+ result
189
+ .then((val) => {
190
+ return transactor.commit(val);
191
+ })
192
+ .catch((err) => {
193
+ return transactor.rollback(err);
194
+ });
195
+ }
196
+ return null;
197
+ })
198
+ .catch((e) => {
199
+ return this._rejecter(e);
200
+ });
201
+
202
+ return executionPromise;
203
+ });
204
+ }
205
+
206
+ // Acquire a connection and create a disposer - either using the one passed
207
+ // via config or getting one off the client. The disposer will be called once
208
+ // the original promise is marked completed.
209
+ async acquireConnection(config, cb) {
210
+ const configConnection = config && config.connection;
211
+ const connection =
212
+ configConnection || (await this.client.acquireConnection());
213
+
214
+ try {
215
+ connection.__knexTxId = this.txid;
216
+ return await cb(connection);
217
+ } finally {
218
+ if (!configConnection) {
219
+ debug('%s: releasing connection', this.txid);
220
+ this.client.releaseConnection(connection);
221
+ } else {
222
+ debug('%s: not releasing external connection', this.txid);
223
+ }
224
+ }
225
+ }
226
+
227
+ then(onResolve, onReject) {
228
+ return this._promise.then(onResolve, onReject);
229
+ }
230
+
231
+ catch(onReject) {
232
+ return this._promise.catch(onReject);
233
+ }
234
+
235
+ asCallback(cb) {
236
+ callbackify(() => this._promise)(cb);
237
+ return this._promise;
238
+ }
239
+ }
240
+ finallyMixin(Transaction.prototype);
241
+
242
+ // The transactor is a full featured knex object, with a "commit", a "rollback"
243
+ // and a "savepoint" function. The "savepoint" is just sugar for creating a new
244
+ // transaction. If the rollback is run inside a savepoint, it rolls back to the
245
+ // last savepoint - otherwise it rolls back the transaction.
246
+ function makeTransactor(trx, connection, trxClient) {
247
+ const transactor = makeKnex(trxClient);
248
+
249
+ transactor.context.withUserParams = () => {
250
+ throw new Error(
251
+ 'Cannot set user params on a transaction - it can only inherit params from main knex instance'
252
+ );
253
+ };
254
+
255
+ transactor.isTransaction = true;
256
+ transactor.userParams = trx.userParams || {};
257
+
258
+ transactor.context.transaction = function (container, options) {
259
+ if (!options) {
260
+ options = { doNotRejectOnRollback: true };
261
+ } else if (options.doNotRejectOnRollback === undefined) {
262
+ options.doNotRejectOnRollback = true;
263
+ }
264
+
265
+ return this._transaction(container, options, trx);
266
+ };
267
+
268
+ transactor.savepoint = function (container, options) {
269
+ return transactor.transaction(container, options);
270
+ };
271
+
272
+ if (trx.client.transacting) {
273
+ transactor.commit = (value) => trx.release(connection, value);
274
+ transactor.rollback = (error) => trx.rollbackTo(connection, error);
275
+ } else {
276
+ transactor.commit = (value) => trx.commit(connection, value);
277
+ transactor.rollback = (error) => trx.rollback(connection, error);
278
+ }
279
+
280
+ transactor.isCompleted = () => trx.isCompleted();
281
+
282
+ return transactor;
283
+ }
284
+
285
+ // We need to make a client object which always acquires the same
286
+ // connection and does not release back into the pool.
287
+ function makeTxClient(trx, client, connection) {
288
+ const trxClient = Object.create(client.constructor.prototype);
289
+ trxClient.version = client.version;
290
+ trxClient.config = client.config;
291
+ trxClient.driver = client.driver;
292
+ trxClient.connectionSettings = client.connectionSettings;
293
+ trxClient.transacting = true;
294
+ trxClient.valueForUndefined = client.valueForUndefined;
295
+ trxClient.logger = client.logger;
296
+
297
+ trxClient.on('start', function (arg) {
298
+ trx.emit('start', arg);
299
+ client.emit('start', arg);
300
+ });
301
+
302
+ trxClient.on('query', function (arg) {
303
+ trx.emit('query', arg);
304
+ client.emit('query', arg);
305
+ });
306
+
307
+ trxClient.on('query-error', function (err, obj) {
308
+ trx.emit('query-error', err, obj);
309
+ client.emit('query-error', err, obj);
310
+ });
311
+
312
+ trxClient.on('query-response', function (response, obj, builder) {
313
+ trx.emit('query-response', response, obj, builder);
314
+ client.emit('query-response', response, obj, builder);
315
+ });
316
+
317
+ const _query = trxClient.query;
318
+ trxClient.query = function (conn, obj) {
319
+ const completed = trx.isCompleted();
320
+ return new Promise(function (resolve, reject) {
321
+ try {
322
+ if (conn !== connection)
323
+ throw new Error('Invalid connection for transaction query.');
324
+ if (completed) completedError(trx, obj);
325
+ resolve(_query.call(trxClient, conn, obj));
326
+ } catch (e) {
327
+ reject(e);
328
+ }
329
+ });
330
+ };
331
+ const _stream = trxClient.stream;
332
+ trxClient.stream = function (conn, obj, stream, options) {
333
+ const completed = trx.isCompleted();
334
+ return new Promise(function (resolve, reject) {
335
+ try {
336
+ if (conn !== connection)
337
+ throw new Error('Invalid connection for transaction query.');
338
+ if (completed) completedError(trx, obj);
339
+ resolve(_stream.call(trxClient, conn, obj, stream, options));
340
+ } catch (e) {
341
+ reject(e);
342
+ }
343
+ });
344
+ };
345
+ trxClient.acquireConnection = function () {
346
+ return Promise.resolve(connection);
347
+ };
348
+ trxClient.releaseConnection = function () {
349
+ return Promise.resolve();
350
+ };
351
+
352
+ return trxClient;
353
+ }
354
+
355
+ function completedError(trx, obj) {
356
+ const sql = typeof obj === 'string' ? obj : obj && obj.sql;
357
+ debug('%s: Transaction completed: %s', trx.txid, sql);
358
+ throw new Error(
359
+ 'Transaction query already complete, run with DEBUG=knex:tx for more info'
360
+ );
361
+ }
362
+
363
+ module.exports = Transaction;