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,489 +1,489 @@
1
- // Oracledb Client
2
- // -------
3
- const each = require('lodash/each');
4
- const flatten = require('lodash/flatten');
5
- const isEmpty = require('lodash/isEmpty');
6
- const map = require('lodash/map');
7
- const values = require('lodash/values');
8
- const QueryCompiler = require('./query/compiler');
9
- const ColumnCompiler = require('./schema/columncompiler');
10
- const { BlobHelper, ReturningHelper, isConnectionError } = require('./utils');
11
- const stream = require('stream');
12
- const { promisify, inherits } = require('util');
13
- const Transaction = require('./transaction');
14
- const Client_Oracle = require('../oracle');
15
- const Oracle_Formatter = require('../oracle/formatter');
16
- const { isString } = require('../../util/is');
17
-
18
- function Client_Oracledb() {
19
- Client_Oracle.apply(this, arguments);
20
- // Node.js only have 4 background threads by default, oracledb needs one by connection
21
- if (this.driver) {
22
- process.env.UV_THREADPOOL_SIZE = process.env.UV_THREADPOOL_SIZE || 1;
23
- process.env.UV_THREADPOOL_SIZE =
24
- parseInt(process.env.UV_THREADPOOL_SIZE) + this.driver.poolMax;
25
- }
26
- }
27
- inherits(Client_Oracledb, Client_Oracle);
28
-
29
- Client_Oracledb.prototype.driverName = 'oracledb';
30
-
31
- Client_Oracledb.prototype._driver = function () {
32
- const client = this;
33
- const oracledb = require('oracledb');
34
- client.fetchAsString = [];
35
- if (this.config.fetchAsString && Array.isArray(this.config.fetchAsString)) {
36
- this.config.fetchAsString.forEach(function (type) {
37
- if (!isString(type)) return;
38
- type = type.toUpperCase();
39
- if (oracledb[type]) {
40
- if (
41
- type !== 'NUMBER' &&
42
- type !== 'DATE' &&
43
- type !== 'CLOB' &&
44
- type !== 'BUFFER'
45
- ) {
46
- this.logger.warn(
47
- 'Only "date", "number", "clob" and "buffer" are supported for fetchAsString'
48
- );
49
- }
50
- client.fetchAsString.push(oracledb[type]);
51
- }
52
- });
53
- }
54
- return oracledb;
55
- };
56
-
57
- Client_Oracledb.prototype.queryCompiler = function () {
58
- return new QueryCompiler(this, ...arguments);
59
- };
60
- Client_Oracledb.prototype.columnCompiler = function () {
61
- return new ColumnCompiler(this, ...arguments);
62
- };
63
- Client_Oracledb.prototype.formatter = function () {
64
- return new Oracledb_Formatter(this, ...arguments);
65
- };
66
- Client_Oracledb.prototype.transaction = function () {
67
- return new Transaction(this, ...arguments);
68
- };
69
-
70
- Client_Oracledb.prototype.prepBindings = function (bindings) {
71
- return map(bindings, (value) => {
72
- if (value instanceof BlobHelper && this.driver) {
73
- return { type: this.driver.BLOB, dir: this.driver.BIND_OUT };
74
- // Returning helper always use ROWID as string
75
- } else if (value instanceof ReturningHelper && this.driver) {
76
- return { type: this.driver.STRING, dir: this.driver.BIND_OUT };
77
- } else if (typeof value === 'boolean') {
78
- return value ? 1 : 0;
79
- }
80
- return value;
81
- });
82
- };
83
-
84
- function resolveConnectString(connectionSettings) {
85
- if (connectionSettings.connectString) {
86
- return connectionSettings.connectString;
87
- }
88
-
89
- if (!connectionSettings.port) {
90
- return connectionSettings.host + '/' + connectionSettings.database;
91
- }
92
-
93
- return (
94
- connectionSettings.host +
95
- ':' +
96
- connectionSettings.port +
97
- '/' +
98
- connectionSettings.database
99
- );
100
- }
101
-
102
- // Get a raw connection, called by the `pool` whenever a new
103
- // connection needs to be added to the pool.
104
- Client_Oracledb.prototype.acquireRawConnection = function () {
105
- const client = this;
106
- const asyncConnection = new Promise(function (resolver, rejecter) {
107
- // If external authentication don't have to worry about username/password and
108
- // if not need to set the username and password
109
- const oracleDbConfig = client.connectionSettings.externalAuth
110
- ? { externalAuth: client.connectionSettings.externalAuth }
111
- : {
112
- user: client.connectionSettings.user,
113
- password: client.connectionSettings.password,
114
- };
115
-
116
- // In the case of external authentication connection string will be given
117
- oracleDbConfig.connectString = resolveConnectString(
118
- client.connectionSettings
119
- );
120
-
121
- if (client.connectionSettings.prefetchRowCount) {
122
- oracleDbConfig.prefetchRows = client.connectionSettings.prefetchRowCount;
123
- }
124
-
125
- if (client.connectionSettings.stmtCacheSize !== undefined) {
126
- oracleDbConfig.stmtCacheSize = client.connectionSettings.stmtCacheSize;
127
- }
128
-
129
- client.driver.fetchAsString = client.fetchAsString;
130
-
131
- client.driver.getConnection(oracleDbConfig, function (err, connection) {
132
- if (err) {
133
- return rejecter(err);
134
- }
135
- connection.commitAsync = function () {
136
- return new Promise((commitResolve, commitReject) => {
137
- this.commit(function (err) {
138
- if (err) {
139
- return commitReject(err);
140
- }
141
- commitResolve();
142
- });
143
- });
144
- };
145
- connection.rollbackAsync = function () {
146
- return new Promise((rollbackResolve, rollbackReject) => {
147
- this.rollback(function (err) {
148
- if (err) {
149
- return rollbackReject(err);
150
- }
151
- rollbackResolve();
152
- });
153
- });
154
- };
155
- const fetchAsync = promisify(function (sql, bindParams, options, cb) {
156
- options = options || {};
157
- options.outFormat =
158
- client.driver.OUT_FORMAT_OBJECT || client.driver.OBJECT;
159
- if (!options.outFormat) {
160
- throw new Error('not found oracledb.outFormat constants');
161
- }
162
- if (options.resultSet) {
163
- connection.execute(
164
- sql,
165
- bindParams || [],
166
- options,
167
- function (err, result) {
168
- if (err) {
169
- if (isConnectionError(err)) {
170
- connection.close().catch(function (err) {});
171
- connection.__knex__disposed = err;
172
- }
173
- return cb(err);
174
- }
175
- const fetchResult = { rows: [], resultSet: result.resultSet };
176
- const numRows = 100;
177
- const fetchRowsFromRS = function (
178
- connection,
179
- resultSet,
180
- numRows
181
- ) {
182
- resultSet.getRows(numRows, function (err, rows) {
183
- if (err) {
184
- if (isConnectionError(err)) {
185
- connection.close().catch(function (err) {});
186
- connection.__knex__disposed = err;
187
- }
188
- resultSet.close(function () {
189
- return cb(err);
190
- });
191
- } else if (rows.length === 0) {
192
- return cb(null, fetchResult);
193
- } else if (rows.length > 0) {
194
- if (rows.length === numRows) {
195
- fetchResult.rows = fetchResult.rows.concat(rows);
196
- fetchRowsFromRS(connection, resultSet, numRows);
197
- } else {
198
- fetchResult.rows = fetchResult.rows.concat(rows);
199
- return cb(null, fetchResult);
200
- }
201
- }
202
- });
203
- };
204
- fetchRowsFromRS(connection, result.resultSet, numRows);
205
- }
206
- );
207
- } else {
208
- connection.execute(
209
- sql,
210
- bindParams || [],
211
- options,
212
- function (err, result) {
213
- if (err) {
214
- // dispose the connection on connection error
215
- if (isConnectionError(err)) {
216
- connection.close().catch(function (err) {});
217
- connection.__knex__disposed = err;
218
- }
219
- return cb(err);
220
- }
221
-
222
- return cb(null, result);
223
- }
224
- );
225
- }
226
- });
227
- connection.executeAsync = function (sql, bindParams, options) {
228
- // Read all lob
229
- return fetchAsync(sql, bindParams, options).then(async (results) => {
230
- const closeResultSet = () => {
231
- return results.resultSet
232
- ? promisify(results.resultSet.close).call(results.resultSet)
233
- : Promise.resolve();
234
- };
235
-
236
- // Collect LOBs to read
237
- const lobs = [];
238
- if (results.rows) {
239
- if (Array.isArray(results.rows)) {
240
- for (let i = 0; i < results.rows.length; i++) {
241
- // Iterate through the rows
242
- const row = results.rows[i];
243
- for (const column in row) {
244
- if (row[column] instanceof stream.Readable) {
245
- lobs.push({ index: i, key: column, stream: row[column] });
246
- }
247
- }
248
- }
249
- }
250
- }
251
-
252
- try {
253
- for (const lob of lobs) {
254
- // todo should be fetchAsString/fetchAsBuffer polyfill only
255
- results.rows[lob.index][lob.key] = await lobProcessing(
256
- lob.stream
257
- );
258
- }
259
- } catch (e) {
260
- await closeResultSet().catch(() => {});
261
-
262
- throw e;
263
- }
264
-
265
- await closeResultSet();
266
-
267
- return results;
268
- });
269
- };
270
- resolver(connection);
271
- });
272
- });
273
- return asyncConnection;
274
- };
275
-
276
- // Used to explicitly close a connection, called internally by the pool
277
- // when a connection times out or the pool is shutdown.
278
- Client_Oracledb.prototype.destroyRawConnection = function (connection) {
279
- return connection.release();
280
- };
281
-
282
- // Runs the query on the specified connection, providing the bindings
283
- // and any other necessary prep work.
284
- Client_Oracledb.prototype._query = function (connection, obj) {
285
- if (!obj.sql) throw new Error('The query is empty');
286
-
287
- const options = { autoCommit: false };
288
- if (obj.method === 'select') {
289
- options.resultSet = true;
290
- }
291
- return connection
292
- .executeAsync(obj.sql, obj.bindings, options)
293
- .then(async function (response) {
294
- // Flatten outBinds
295
- let outBinds = flatten(response.outBinds);
296
- obj.response = response.rows || [];
297
- obj.rowsAffected = response.rows
298
- ? response.rows.rowsAffected
299
- : response.rowsAffected;
300
-
301
- //added for outBind parameter
302
- if (obj.method === 'raw' && outBinds.length > 0) {
303
- return {
304
- response: outBinds,
305
- };
306
- }
307
-
308
- if (obj.method === 'update') {
309
- const modifiedRowsCount = obj.rowsAffected.length || obj.rowsAffected;
310
- const updatedObjOutBinding = [];
311
- const updatedOutBinds = [];
312
- const updateOutBinds = (i) =>
313
- function (value, index) {
314
- const OutBindsOffset = index * modifiedRowsCount;
315
- updatedOutBinds.push(outBinds[i + OutBindsOffset]);
316
- };
317
-
318
- for (let i = 0; i < modifiedRowsCount; i++) {
319
- updatedObjOutBinding.push(obj.outBinding[0]);
320
- each(obj.outBinding[0], updateOutBinds(i));
321
- }
322
- outBinds = updatedOutBinds;
323
- obj.outBinding = updatedObjOutBinding;
324
- }
325
-
326
- if (!obj.returning && outBinds.length === 0) {
327
- if (!connection.isTransaction) {
328
- await connection.commitAsync();
329
- }
330
- return obj;
331
- }
332
- const rowIds = [];
333
- let offset = 0;
334
-
335
- for (let line = 0; line < obj.outBinding.length; line++) {
336
- const ret = obj.outBinding[line];
337
-
338
- offset =
339
- offset +
340
- (obj.outBinding[line - 1] ? obj.outBinding[line - 1].length : 0);
341
-
342
- for (let index = 0; index < ret.length; index++) {
343
- const out = ret[index];
344
-
345
- await new Promise(function (bindResolver, bindRejecter) {
346
- if (out instanceof BlobHelper) {
347
- const blob = outBinds[index + offset];
348
- if (out.returning) {
349
- obj.response[line] = obj.response[line] || {};
350
- obj.response[line][out.columnName] = out.value;
351
- }
352
- blob.on('error', function (err) {
353
- bindRejecter(err);
354
- });
355
- blob.on('finish', function () {
356
- bindResolver();
357
- });
358
- blob.write(out.value);
359
- blob.end();
360
- } else if (obj.outBinding[line][index] === 'ROWID') {
361
- rowIds.push(outBinds[index + offset]);
362
- bindResolver();
363
- } else {
364
- obj.response[line] = obj.response[line] || {};
365
- obj.response[line][out] = outBinds[index + offset];
366
- bindResolver();
367
- }
368
- });
369
- }
370
- }
371
- if (connection.isTransaction) {
372
- return obj;
373
- }
374
- await connection.commitAsync();
375
- if (obj.returningSql) {
376
- const response = await connection.executeAsync(
377
- obj.returningSql(),
378
- rowIds,
379
- { resultSet: true }
380
- );
381
- obj.response = response.rows;
382
- }
383
- return obj;
384
- });
385
- };
386
-
387
- /**
388
- * @param stream
389
- * @param {'string' | 'buffer'} type
390
- */
391
- function readStream(stream, type) {
392
- return new Promise((resolve, reject) => {
393
- let data = type === 'string' ? '' : Buffer.alloc(0);
394
-
395
- stream.on('error', function (err) {
396
- reject(err);
397
- });
398
- stream.on('data', function (chunk) {
399
- if (type === 'string') {
400
- data += chunk;
401
- } else {
402
- data = Buffer.concat([data, chunk]);
403
- }
404
- });
405
- stream.on('end', function () {
406
- resolve(data);
407
- });
408
- });
409
- }
410
-
411
- // Process the response as returned from the query.
412
- Client_Oracledb.prototype.processResponse = function (obj, runner) {
413
- let response = obj.response;
414
- const method = obj.method;
415
- if (obj.output) {
416
- return obj.output.call(runner, response);
417
- }
418
- switch (method) {
419
- case 'select':
420
- case 'pluck':
421
- case 'first':
422
- if (obj.method === 'pluck') {
423
- response = map(response, obj.pluck);
424
- }
425
- return obj.method === 'first' ? response[0] : response;
426
- case 'insert':
427
- case 'del':
428
- case 'update':
429
- case 'counter':
430
- if (obj.returning && !isEmpty(obj.returning)) {
431
- if (obj.returning.length === 1 && obj.returning[0] !== '*') {
432
- return flatten(map(response, values));
433
- }
434
- return response;
435
- } else if (obj.rowsAffected !== undefined) {
436
- return obj.rowsAffected;
437
- } else {
438
- return 1;
439
- }
440
- default:
441
- return response;
442
- }
443
- };
444
-
445
- const lobProcessing = function (stream) {
446
- const oracledb = require('oracledb');
447
-
448
- /**
449
- * @type 'string' | 'buffer'
450
- */
451
- let type;
452
-
453
- if (stream.type) {
454
- // v1.2-v4
455
- if (stream.type === oracledb.BLOB) {
456
- type = 'buffer';
457
- } else if (stream.type === oracledb.CLOB) {
458
- type = 'string';
459
- }
460
- } else if (stream.iLob) {
461
- // v1
462
- if (stream.iLob.type === oracledb.CLOB) {
463
- type = 'string';
464
- } else if (stream.iLob.type === oracledb.BLOB) {
465
- type = 'buffer';
466
- }
467
- } else {
468
- throw new Error('Unrecognized oracledb lob stream type');
469
- }
470
- if (type === 'string') {
471
- stream.setEncoding('utf-8');
472
- }
473
- return readStream(stream, type);
474
- };
475
-
476
- class Oracledb_Formatter extends Oracle_Formatter {
477
- // Checks whether a value is a function... if it is, we compile it
478
- // otherwise we check whether it's a raw
479
- parameter(value) {
480
- if (typeof value === 'function') {
481
- return this.outputQuery(this.compileCallback(value), true);
482
- } else if (value instanceof BlobHelper) {
483
- return 'EMPTY_BLOB()';
484
- }
485
- return this.unwrapRaw(value, true) || '?';
486
- }
487
- }
488
-
489
- module.exports = Client_Oracledb;
1
+ // Oracledb Client
2
+ // -------
3
+ const each = require('lodash/each');
4
+ const flatten = require('lodash/flatten');
5
+ const isEmpty = require('lodash/isEmpty');
6
+ const map = require('lodash/map');
7
+ const values = require('lodash/values');
8
+ const QueryCompiler = require('./query/compiler');
9
+ const ColumnCompiler = require('./schema/columncompiler');
10
+ const { BlobHelper, ReturningHelper, isConnectionError } = require('./utils');
11
+ const stream = require('stream');
12
+ const { promisify, inherits } = require('util');
13
+ const Transaction = require('./transaction');
14
+ const Client_Oracle = require('../oracle');
15
+ const Oracle_Formatter = require('../oracle/formatter');
16
+ const { isString } = require('../../util/is');
17
+
18
+ function Client_Oracledb() {
19
+ Client_Oracle.apply(this, arguments);
20
+ // Node.js only have 4 background threads by default, oracledb needs one by connection
21
+ if (this.driver) {
22
+ process.env.UV_THREADPOOL_SIZE = process.env.UV_THREADPOOL_SIZE || 1;
23
+ process.env.UV_THREADPOOL_SIZE =
24
+ parseInt(process.env.UV_THREADPOOL_SIZE) + this.driver.poolMax;
25
+ }
26
+ }
27
+ inherits(Client_Oracledb, Client_Oracle);
28
+
29
+ Client_Oracledb.prototype.driverName = 'oracledb';
30
+
31
+ Client_Oracledb.prototype._driver = function () {
32
+ const client = this;
33
+ const oracledb = require('oracledb');
34
+ client.fetchAsString = [];
35
+ if (this.config.fetchAsString && Array.isArray(this.config.fetchAsString)) {
36
+ this.config.fetchAsString.forEach(function (type) {
37
+ if (!isString(type)) return;
38
+ type = type.toUpperCase();
39
+ if (oracledb[type]) {
40
+ if (
41
+ type !== 'NUMBER' &&
42
+ type !== 'DATE' &&
43
+ type !== 'CLOB' &&
44
+ type !== 'BUFFER'
45
+ ) {
46
+ this.logger.warn(
47
+ 'Only "date", "number", "clob" and "buffer" are supported for fetchAsString'
48
+ );
49
+ }
50
+ client.fetchAsString.push(oracledb[type]);
51
+ }
52
+ });
53
+ }
54
+ return oracledb;
55
+ };
56
+
57
+ Client_Oracledb.prototype.queryCompiler = function () {
58
+ return new QueryCompiler(this, ...arguments);
59
+ };
60
+ Client_Oracledb.prototype.columnCompiler = function () {
61
+ return new ColumnCompiler(this, ...arguments);
62
+ };
63
+ Client_Oracledb.prototype.formatter = function () {
64
+ return new Oracledb_Formatter(this, ...arguments);
65
+ };
66
+ Client_Oracledb.prototype.transaction = function () {
67
+ return new Transaction(this, ...arguments);
68
+ };
69
+
70
+ Client_Oracledb.prototype.prepBindings = function (bindings) {
71
+ return map(bindings, (value) => {
72
+ if (value instanceof BlobHelper && this.driver) {
73
+ return { type: this.driver.BLOB, dir: this.driver.BIND_OUT };
74
+ // Returning helper always use ROWID as string
75
+ } else if (value instanceof ReturningHelper && this.driver) {
76
+ return { type: this.driver.STRING, dir: this.driver.BIND_OUT };
77
+ } else if (typeof value === 'boolean') {
78
+ return value ? 1 : 0;
79
+ }
80
+ return value;
81
+ });
82
+ };
83
+
84
+ function resolveConnectString(connectionSettings) {
85
+ if (connectionSettings.connectString) {
86
+ return connectionSettings.connectString;
87
+ }
88
+
89
+ if (!connectionSettings.port) {
90
+ return connectionSettings.host + '/' + connectionSettings.database;
91
+ }
92
+
93
+ return (
94
+ connectionSettings.host +
95
+ ':' +
96
+ connectionSettings.port +
97
+ '/' +
98
+ connectionSettings.database
99
+ );
100
+ }
101
+
102
+ // Get a raw connection, called by the `pool` whenever a new
103
+ // connection needs to be added to the pool.
104
+ Client_Oracledb.prototype.acquireRawConnection = function () {
105
+ const client = this;
106
+ const asyncConnection = new Promise(function (resolver, rejecter) {
107
+ // If external authentication don't have to worry about username/password and
108
+ // if not need to set the username and password
109
+ const oracleDbConfig = client.connectionSettings.externalAuth
110
+ ? { externalAuth: client.connectionSettings.externalAuth }
111
+ : {
112
+ user: client.connectionSettings.user,
113
+ password: client.connectionSettings.password,
114
+ };
115
+
116
+ // In the case of external authentication connection string will be given
117
+ oracleDbConfig.connectString = resolveConnectString(
118
+ client.connectionSettings
119
+ );
120
+
121
+ if (client.connectionSettings.prefetchRowCount) {
122
+ oracleDbConfig.prefetchRows = client.connectionSettings.prefetchRowCount;
123
+ }
124
+
125
+ if (client.connectionSettings.stmtCacheSize !== undefined) {
126
+ oracleDbConfig.stmtCacheSize = client.connectionSettings.stmtCacheSize;
127
+ }
128
+
129
+ client.driver.fetchAsString = client.fetchAsString;
130
+
131
+ client.driver.getConnection(oracleDbConfig, function (err, connection) {
132
+ if (err) {
133
+ return rejecter(err);
134
+ }
135
+ connection.commitAsync = function () {
136
+ return new Promise((commitResolve, commitReject) => {
137
+ this.commit(function (err) {
138
+ if (err) {
139
+ return commitReject(err);
140
+ }
141
+ commitResolve();
142
+ });
143
+ });
144
+ };
145
+ connection.rollbackAsync = function () {
146
+ return new Promise((rollbackResolve, rollbackReject) => {
147
+ this.rollback(function (err) {
148
+ if (err) {
149
+ return rollbackReject(err);
150
+ }
151
+ rollbackResolve();
152
+ });
153
+ });
154
+ };
155
+ const fetchAsync = promisify(function (sql, bindParams, options, cb) {
156
+ options = options || {};
157
+ options.outFormat =
158
+ client.driver.OUT_FORMAT_OBJECT || client.driver.OBJECT;
159
+ if (!options.outFormat) {
160
+ throw new Error('not found oracledb.outFormat constants');
161
+ }
162
+ if (options.resultSet) {
163
+ connection.execute(
164
+ sql,
165
+ bindParams || [],
166
+ options,
167
+ function (err, result) {
168
+ if (err) {
169
+ if (isConnectionError(err)) {
170
+ connection.close().catch(function (err) {});
171
+ connection.__knex__disposed = err;
172
+ }
173
+ return cb(err);
174
+ }
175
+ const fetchResult = { rows: [], resultSet: result.resultSet };
176
+ const numRows = 100;
177
+ const fetchRowsFromRS = function (
178
+ connection,
179
+ resultSet,
180
+ numRows
181
+ ) {
182
+ resultSet.getRows(numRows, function (err, rows) {
183
+ if (err) {
184
+ if (isConnectionError(err)) {
185
+ connection.close().catch(function (err) {});
186
+ connection.__knex__disposed = err;
187
+ }
188
+ resultSet.close(function () {
189
+ return cb(err);
190
+ });
191
+ } else if (rows.length === 0) {
192
+ return cb(null, fetchResult);
193
+ } else if (rows.length > 0) {
194
+ if (rows.length === numRows) {
195
+ fetchResult.rows = fetchResult.rows.concat(rows);
196
+ fetchRowsFromRS(connection, resultSet, numRows);
197
+ } else {
198
+ fetchResult.rows = fetchResult.rows.concat(rows);
199
+ return cb(null, fetchResult);
200
+ }
201
+ }
202
+ });
203
+ };
204
+ fetchRowsFromRS(connection, result.resultSet, numRows);
205
+ }
206
+ );
207
+ } else {
208
+ connection.execute(
209
+ sql,
210
+ bindParams || [],
211
+ options,
212
+ function (err, result) {
213
+ if (err) {
214
+ // dispose the connection on connection error
215
+ if (isConnectionError(err)) {
216
+ connection.close().catch(function (err) {});
217
+ connection.__knex__disposed = err;
218
+ }
219
+ return cb(err);
220
+ }
221
+
222
+ return cb(null, result);
223
+ }
224
+ );
225
+ }
226
+ });
227
+ connection.executeAsync = function (sql, bindParams, options) {
228
+ // Read all lob
229
+ return fetchAsync(sql, bindParams, options).then(async (results) => {
230
+ const closeResultSet = () => {
231
+ return results.resultSet
232
+ ? promisify(results.resultSet.close).call(results.resultSet)
233
+ : Promise.resolve();
234
+ };
235
+
236
+ // Collect LOBs to read
237
+ const lobs = [];
238
+ if (results.rows) {
239
+ if (Array.isArray(results.rows)) {
240
+ for (let i = 0; i < results.rows.length; i++) {
241
+ // Iterate through the rows
242
+ const row = results.rows[i];
243
+ for (const column in row) {
244
+ if (row[column] instanceof stream.Readable) {
245
+ lobs.push({ index: i, key: column, stream: row[column] });
246
+ }
247
+ }
248
+ }
249
+ }
250
+ }
251
+
252
+ try {
253
+ for (const lob of lobs) {
254
+ // todo should be fetchAsString/fetchAsBuffer polyfill only
255
+ results.rows[lob.index][lob.key] = await lobProcessing(
256
+ lob.stream
257
+ );
258
+ }
259
+ } catch (e) {
260
+ await closeResultSet().catch(() => {});
261
+
262
+ throw e;
263
+ }
264
+
265
+ await closeResultSet();
266
+
267
+ return results;
268
+ });
269
+ };
270
+ resolver(connection);
271
+ });
272
+ });
273
+ return asyncConnection;
274
+ };
275
+
276
+ // Used to explicitly close a connection, called internally by the pool
277
+ // when a connection times out or the pool is shutdown.
278
+ Client_Oracledb.prototype.destroyRawConnection = function (connection) {
279
+ return connection.release();
280
+ };
281
+
282
+ // Runs the query on the specified connection, providing the bindings
283
+ // and any other necessary prep work.
284
+ Client_Oracledb.prototype._query = function (connection, obj) {
285
+ if (!obj.sql) throw new Error('The query is empty');
286
+
287
+ const options = { autoCommit: false };
288
+ if (obj.method === 'select') {
289
+ options.resultSet = true;
290
+ }
291
+ return connection
292
+ .executeAsync(obj.sql, obj.bindings, options)
293
+ .then(async function (response) {
294
+ // Flatten outBinds
295
+ let outBinds = flatten(response.outBinds);
296
+ obj.response = response.rows || [];
297
+ obj.rowsAffected = response.rows
298
+ ? response.rows.rowsAffected
299
+ : response.rowsAffected;
300
+
301
+ //added for outBind parameter
302
+ if (obj.method === 'raw' && outBinds.length > 0) {
303
+ return {
304
+ response: outBinds,
305
+ };
306
+ }
307
+
308
+ if (obj.method === 'update') {
309
+ const modifiedRowsCount = obj.rowsAffected.length || obj.rowsAffected;
310
+ const updatedObjOutBinding = [];
311
+ const updatedOutBinds = [];
312
+ const updateOutBinds = (i) =>
313
+ function (value, index) {
314
+ const OutBindsOffset = index * modifiedRowsCount;
315
+ updatedOutBinds.push(outBinds[i + OutBindsOffset]);
316
+ };
317
+
318
+ for (let i = 0; i < modifiedRowsCount; i++) {
319
+ updatedObjOutBinding.push(obj.outBinding[0]);
320
+ each(obj.outBinding[0], updateOutBinds(i));
321
+ }
322
+ outBinds = updatedOutBinds;
323
+ obj.outBinding = updatedObjOutBinding;
324
+ }
325
+
326
+ if (!obj.returning && outBinds.length === 0) {
327
+ if (!connection.isTransaction) {
328
+ await connection.commitAsync();
329
+ }
330
+ return obj;
331
+ }
332
+ const rowIds = [];
333
+ let offset = 0;
334
+
335
+ for (let line = 0; line < obj.outBinding.length; line++) {
336
+ const ret = obj.outBinding[line];
337
+
338
+ offset =
339
+ offset +
340
+ (obj.outBinding[line - 1] ? obj.outBinding[line - 1].length : 0);
341
+
342
+ for (let index = 0; index < ret.length; index++) {
343
+ const out = ret[index];
344
+
345
+ await new Promise(function (bindResolver, bindRejecter) {
346
+ if (out instanceof BlobHelper) {
347
+ const blob = outBinds[index + offset];
348
+ if (out.returning) {
349
+ obj.response[line] = obj.response[line] || {};
350
+ obj.response[line][out.columnName] = out.value;
351
+ }
352
+ blob.on('error', function (err) {
353
+ bindRejecter(err);
354
+ });
355
+ blob.on('finish', function () {
356
+ bindResolver();
357
+ });
358
+ blob.write(out.value);
359
+ blob.end();
360
+ } else if (obj.outBinding[line][index] === 'ROWID') {
361
+ rowIds.push(outBinds[index + offset]);
362
+ bindResolver();
363
+ } else {
364
+ obj.response[line] = obj.response[line] || {};
365
+ obj.response[line][out] = outBinds[index + offset];
366
+ bindResolver();
367
+ }
368
+ });
369
+ }
370
+ }
371
+ if (connection.isTransaction) {
372
+ return obj;
373
+ }
374
+ await connection.commitAsync();
375
+ if (obj.returningSql) {
376
+ const response = await connection.executeAsync(
377
+ obj.returningSql(),
378
+ rowIds,
379
+ { resultSet: true }
380
+ );
381
+ obj.response = response.rows;
382
+ }
383
+ return obj;
384
+ });
385
+ };
386
+
387
+ /**
388
+ * @param stream
389
+ * @param {'string' | 'buffer'} type
390
+ */
391
+ function readStream(stream, type) {
392
+ return new Promise((resolve, reject) => {
393
+ let data = type === 'string' ? '' : Buffer.alloc(0);
394
+
395
+ stream.on('error', function (err) {
396
+ reject(err);
397
+ });
398
+ stream.on('data', function (chunk) {
399
+ if (type === 'string') {
400
+ data += chunk;
401
+ } else {
402
+ data = Buffer.concat([data, chunk]);
403
+ }
404
+ });
405
+ stream.on('end', function () {
406
+ resolve(data);
407
+ });
408
+ });
409
+ }
410
+
411
+ // Process the response as returned from the query.
412
+ Client_Oracledb.prototype.processResponse = function (obj, runner) {
413
+ let response = obj.response;
414
+ const method = obj.method;
415
+ if (obj.output) {
416
+ return obj.output.call(runner, response);
417
+ }
418
+ switch (method) {
419
+ case 'select':
420
+ case 'pluck':
421
+ case 'first':
422
+ if (obj.method === 'pluck') {
423
+ response = map(response, obj.pluck);
424
+ }
425
+ return obj.method === 'first' ? response[0] : response;
426
+ case 'insert':
427
+ case 'del':
428
+ case 'update':
429
+ case 'counter':
430
+ if (obj.returning && !isEmpty(obj.returning)) {
431
+ if (obj.returning.length === 1 && obj.returning[0] !== '*') {
432
+ return flatten(map(response, values));
433
+ }
434
+ return response;
435
+ } else if (obj.rowsAffected !== undefined) {
436
+ return obj.rowsAffected;
437
+ } else {
438
+ return 1;
439
+ }
440
+ default:
441
+ return response;
442
+ }
443
+ };
444
+
445
+ const lobProcessing = function (stream) {
446
+ const oracledb = require('oracledb');
447
+
448
+ /**
449
+ * @type 'string' | 'buffer'
450
+ */
451
+ let type;
452
+
453
+ if (stream.type) {
454
+ // v1.2-v4
455
+ if (stream.type === oracledb.BLOB) {
456
+ type = 'buffer';
457
+ } else if (stream.type === oracledb.CLOB) {
458
+ type = 'string';
459
+ }
460
+ } else if (stream.iLob) {
461
+ // v1
462
+ if (stream.iLob.type === oracledb.CLOB) {
463
+ type = 'string';
464
+ } else if (stream.iLob.type === oracledb.BLOB) {
465
+ type = 'buffer';
466
+ }
467
+ } else {
468
+ throw new Error('Unrecognized oracledb lob stream type');
469
+ }
470
+ if (type === 'string') {
471
+ stream.setEncoding('utf-8');
472
+ }
473
+ return readStream(stream, type);
474
+ };
475
+
476
+ class Oracledb_Formatter extends Oracle_Formatter {
477
+ // Checks whether a value is a function... if it is, we compile it
478
+ // otherwise we check whether it's a raw
479
+ parameter(value) {
480
+ if (typeof value === 'function') {
481
+ return this.outputQuery(this.compileCallback(value), true);
482
+ } else if (value instanceof BlobHelper) {
483
+ return 'EMPTY_BLOB()';
484
+ }
485
+ return this.unwrapRaw(value, true) || '?';
486
+ }
487
+ }
488
+
489
+ module.exports = Client_Oracledb;