node-firebird 2.3.4 → 2.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +544 -6
- package/lib/callback.d.ts +20 -0
- package/lib/callback.js +6 -16
- package/lib/firebird.msg.json +1370 -1370
- package/lib/gdscodes.d.ts +1519 -1520
- package/lib/gdscodes.js +5 -5
- package/lib/ieee754-decimal.d.ts +38 -0
- package/lib/ieee754-decimal.js +71 -118
- package/lib/index.d.ts +37 -338
- package/lib/index.js +97 -86
- package/lib/messages.d.ts +6 -0
- package/lib/messages.js +135 -162
- package/lib/pool.d.ts +23 -0
- package/lib/pool.js +34 -47
- package/lib/srp.d.ts +51 -0
- package/lib/srp.js +62 -72
- package/lib/types.d.ts +375 -0
- package/lib/types.js +22 -0
- package/lib/unix-crypt.d.ts +1 -0
- package/lib/unix-crypt.js +126 -152
- package/lib/utils.d.ts +16 -0
- package/lib/utils.js +24 -48
- package/lib/wire/connection.d.ts +100 -0
- package/lib/wire/connection.js +1029 -1009
- package/lib/wire/const.d.ts +567 -0
- package/lib/wire/const.js +620 -599
- package/lib/wire/database.d.ts +57 -0
- package/lib/wire/database.js +111 -108
- package/lib/wire/eventConnection.d.ts +16 -0
- package/lib/wire/eventConnection.js +29 -31
- package/lib/wire/fbEventManager.d.ts +45 -0
- package/lib/wire/fbEventManager.js +38 -57
- package/lib/wire/serialize.d.ts +97 -0
- package/lib/wire/serialize.js +42 -141
- package/lib/wire/service.d.ts +66 -0
- package/lib/wire/service.js +374 -401
- package/lib/wire/socket.d.ts +59 -0
- package/lib/wire/socket.js +79 -40
- package/lib/wire/statement.d.ts +25 -0
- package/lib/wire/statement.js +13 -9
- package/lib/wire/transaction.d.ts +21 -0
- package/lib/wire/transaction.js +33 -61
- package/lib/wire/xsqlvar.d.ts +175 -0
- package/lib/wire/xsqlvar.js +169 -269
- package/package.json +12 -4
- package/src/callback.ts +55 -0
- package/{poc/node_modules/node-firebird/lib/gdscodes.js → src/gdscodes.ts} +3 -3
- package/{poc/node_modules/node-firebird/lib/ieee754-decimal.js → src/ieee754-decimal.ts} +7 -14
- package/src/index.ts +157 -0
- package/{poc/node_modules/node-firebird/lib/messages.js → src/messages.ts} +163 -162
- package/src/pool.ts +209 -0
- package/{poc/node_modules/node-firebird/lib/srp.js → src/srp.ts} +74 -33
- package/src/types.ts +419 -0
- package/{poc/node_modules/node-firebird/lib/unix-crypt.js → src/unix-crypt.ts} +6 -12
- package/{poc/node_modules/node-firebird/lib/utils.js → src/utils.ts} +11 -20
- package/{poc/node_modules/node-firebird/lib/wire/connection.js → src/wire/connection.ts} +611 -129
- package/{poc/node_modules/node-firebird/lib/wire/const.js → src/wire/const.ts} +96 -9
- package/{poc/node_modules/node-firebird/lib/wire/database.js → src/wire/database.ts} +110 -44
- package/{poc/node_modules/node-firebird/lib/wire/eventConnection.js → src/wire/eventConnection.ts} +29 -14
- package/{poc/node_modules/node-firebird/lib/wire/fbEventManager.js → src/wire/fbEventManager.ts} +39 -30
- package/{poc/node_modules/node-firebird/lib/wire/serialize.js → src/wire/serialize.ts} +78 -59
- package/{poc/node_modules/node-firebird/lib/wire/service.js → src/wire/service.ts} +90 -88
- package/src/wire/socket.ts +262 -0
- package/src/wire/statement.ts +71 -0
- package/{poc/node_modules/node-firebird/lib/wire/transaction.js → src/wire/transaction.ts} +25 -19
- package/{poc/node_modules/node-firebird/lib/wire/xsqlvar.js → src/wire/xsqlvar.ts} +244 -143
- package/.eslintrc.json +0 -12
- package/.github/workflows/codeql.yml +0 -76
- package/.github/workflows/node.js.yml +0 -95
- package/BIGINT_MIGRATION.md +0 -374
- package/CI_DEBUGGING_GUIDE.md +0 -148
- package/ENCRYPTION_CALLBACK.md +0 -152
- package/FIREBIRD_LOG_FEATURE.md +0 -145
- package/MINIMAL_CHANGES_SUMMARY.md +0 -136
- package/PR_SUMMARY.md +0 -96
- package/ROADMAP.md +0 -223
- package/SRP_PROTOCOL.md +0 -482
- package/poc/README.md +0 -160
- package/poc/helpers.js +0 -59
- package/poc/node_modules/.package-lock.json +0 -14
- package/poc/node_modules/node-firebird/.eslintrc.json +0 -12
- package/poc/node_modules/node-firebird/.github/workflows/codeql.yml +0 -76
- package/poc/node_modules/node-firebird/.github/workflows/node.js.yml +0 -95
- package/poc/node_modules/node-firebird/BIGINT_MIGRATION.md +0 -374
- package/poc/node_modules/node-firebird/CI_DEBUGGING_GUIDE.md +0 -148
- package/poc/node_modules/node-firebird/ENCRYPTION_CALLBACK.md +0 -152
- package/poc/node_modules/node-firebird/FIREBIRD_LOG_FEATURE.md +0 -145
- package/poc/node_modules/node-firebird/LICENSE +0 -373
- package/poc/node_modules/node-firebird/MINIMAL_CHANGES_SUMMARY.md +0 -136
- package/poc/node_modules/node-firebird/PR_SUMMARY.md +0 -96
- package/poc/node_modules/node-firebird/README.md +0 -794
- package/poc/node_modules/node-firebird/ROADMAP.md +0 -223
- package/poc/node_modules/node-firebird/SRP_PROTOCOL.md +0 -482
- package/poc/node_modules/node-firebird/lib/callback.js +0 -38
- package/poc/node_modules/node-firebird/lib/gdscodes.d.ts +0 -1524
- package/poc/node_modules/node-firebird/lib/index.d.ts +0 -316
- package/poc/node_modules/node-firebird/lib/index.js +0 -128
- package/poc/node_modules/node-firebird/lib/pool.js +0 -108
- package/poc/node_modules/node-firebird/lib/wire/socket.js +0 -175
- package/poc/node_modules/node-firebird/lib/wire/statement.js +0 -48
- package/poc/node_modules/node-firebird/package.json +0 -38
- package/poc/node_modules/node-firebird/vitest.config.js +0 -24
- package/poc/package-lock.json +0 -21
- package/poc/package.json +0 -12
- package/poc/reproduce-fixed.js +0 -150
- package/poc/reproduce.js +0 -133
- package/vitest.config.js +0 -27
- /package/{poc/node_modules/node-firebird/lib → src}/firebird.msg +0 -0
- /package/{poc/node_modules/node-firebird/lib → src}/firebird.msg.json +0 -0
|
@@ -1,19 +1,30 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
1
|
+
import Events from 'events';
|
|
2
|
+
import os from 'os';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
|
|
5
|
+
import { XdrWriter, BlrWriter, XdrReader, BitSet, BlrReader } from './serialize';
|
|
6
|
+
import { doCallback, doError } from '../callback';
|
|
7
|
+
import * as srp from '../srp';
|
|
8
|
+
import * as crypt from '../unix-crypt';
|
|
9
|
+
import Const from './const';
|
|
10
|
+
import * as Xsql from './xsqlvar';
|
|
11
|
+
import ServiceManager from './service';
|
|
12
|
+
import Database from './database';
|
|
13
|
+
import Statement from './statement';
|
|
14
|
+
import Transaction from './transaction';
|
|
15
|
+
import { lookupMessages, noop, parseDate } from '../utils';
|
|
16
|
+
import Socket from './socket';
|
|
17
|
+
|
|
18
|
+
function parseValueIfJson(value: any, options: any) {
|
|
19
|
+
if (options && options.jsonAsObject && typeof value === 'string' && (value.startsWith('{') || value.startsWith('['))) {
|
|
20
|
+
try {
|
|
21
|
+
return JSON.parse(value);
|
|
22
|
+
} catch (e) {
|
|
23
|
+
// Ignore parse error and keep as string
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return value;
|
|
27
|
+
}
|
|
17
28
|
|
|
18
29
|
/***************************************
|
|
19
30
|
*
|
|
@@ -22,7 +33,47 @@ const Socket = require("./socket");
|
|
|
22
33
|
***************************************/
|
|
23
34
|
|
|
24
35
|
class Connection {
|
|
25
|
-
|
|
36
|
+
static decodeResponse: typeof decodeResponse;
|
|
37
|
+
static fetch_blob_async_transaction: typeof fetch_blob_async_transaction;
|
|
38
|
+
static fetch_blob_async: typeof fetch_blob_async;
|
|
39
|
+
static parseValueIfJson: typeof parseValueIfJson;
|
|
40
|
+
static describe: typeof describe;
|
|
41
|
+
|
|
42
|
+
db: any;
|
|
43
|
+
svc: any;
|
|
44
|
+
options: any;
|
|
45
|
+
accept: any;
|
|
46
|
+
error: any;
|
|
47
|
+
dbhandle: number | undefined;
|
|
48
|
+
svchandle: number | undefined;
|
|
49
|
+
clientKeys: srp.KeyPair | undefined;
|
|
50
|
+
serverKeys: { salt: string; public: bigint; pluginName: string } | undefined;
|
|
51
|
+
|
|
52
|
+
_msg: XdrWriter;
|
|
53
|
+
_blr: BlrWriter;
|
|
54
|
+
_queue: any[];
|
|
55
|
+
_pending: string[];
|
|
56
|
+
_socket: any;
|
|
57
|
+
_xdr: any;
|
|
58
|
+
_isOpened: boolean;
|
|
59
|
+
_isClosed: boolean;
|
|
60
|
+
_isDetach: boolean;
|
|
61
|
+
_isUsed: boolean;
|
|
62
|
+
_pooled: boolean;
|
|
63
|
+
_lowercase_keys: boolean | undefined;
|
|
64
|
+
_detachTimeout: any;
|
|
65
|
+
_detachCallback: any;
|
|
66
|
+
_detachAuto: any;
|
|
67
|
+
_retry_connection_id: any;
|
|
68
|
+
_retry_connection_interval: number;
|
|
69
|
+
_max_cached_query: number;
|
|
70
|
+
_cache_query: Record<string, any> | null;
|
|
71
|
+
_messageFile: string;
|
|
72
|
+
_authStartTime: number | undefined;
|
|
73
|
+
_pendingAccept: any;
|
|
74
|
+
_inlineBlobs: Map<string, Buffer> | undefined;
|
|
75
|
+
|
|
76
|
+
constructor(host: string, port: number, callback: any, options: any, db?: any, svc?: any) {
|
|
26
77
|
var self = this;
|
|
27
78
|
this.db = db;
|
|
28
79
|
this.svc = svc
|
|
@@ -40,6 +91,7 @@ class Connection {
|
|
|
40
91
|
this._isUsed = false;
|
|
41
92
|
this._pooled = options.isPool||false;
|
|
42
93
|
if (options && options.blobChunkSize > 65535) options.blobChunkSize = 65535;
|
|
94
|
+
if (options && options.blobReadChunkSize > 65535) options.blobReadChunkSize = 65535;
|
|
43
95
|
this.options = options;
|
|
44
96
|
this._bind_events(host, port, callback);
|
|
45
97
|
this.error;
|
|
@@ -67,55 +119,76 @@ class Connection {
|
|
|
67
119
|
}
|
|
68
120
|
|
|
69
121
|
|
|
122
|
+
// Reject every request that is still awaiting a server response (e.g. a
|
|
123
|
+
// transaction.commit() sent right before the server went away) instead of
|
|
124
|
+
// leaving its callback to hang forever. Requests already in flight cannot
|
|
125
|
+
// be transparently resumed on a reconnect: a fresh attach() hands out new
|
|
126
|
+
// transaction/statement handles, so nothing the server sends back could
|
|
127
|
+
// ever match a callback queued against the old connection.
|
|
128
|
+
_rejectPending(err) {
|
|
129
|
+
var queue = this._queue;
|
|
130
|
+
this._queue = [];
|
|
131
|
+
this._pending = [];
|
|
132
|
+
|
|
133
|
+
for (var i = 0; i < queue.length; i++) {
|
|
134
|
+
doError(err, queue[i]);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
|
|
70
139
|
_bind_events(host, port, callback) {
|
|
71
|
-
|
|
140
|
+
|
|
72
141
|
var self = this;
|
|
73
|
-
|
|
142
|
+
|
|
74
143
|
self._socket.on('close', function() {
|
|
75
|
-
|
|
144
|
+
|
|
76
145
|
if (!self._isOpened || self._isDetach) {
|
|
77
146
|
return;
|
|
78
147
|
}
|
|
79
|
-
|
|
148
|
+
|
|
80
149
|
self._isOpened = false;
|
|
81
|
-
|
|
150
|
+
self._isClosed = true;
|
|
151
|
+
|
|
152
|
+
var lostError = self.error || new Error('Connection to Firebird server was lost.');
|
|
153
|
+
|
|
82
154
|
if (!self.db) {
|
|
155
|
+
self._rejectPending(lostError);
|
|
83
156
|
if (callback)
|
|
84
157
|
callback(self.error);
|
|
85
158
|
return;
|
|
86
159
|
}
|
|
87
|
-
|
|
160
|
+
|
|
161
|
+
self._rejectPending(lostError);
|
|
162
|
+
|
|
88
163
|
self._retry_connection_id = setTimeout(function() {
|
|
89
164
|
self._socket.removeAllListeners();
|
|
90
165
|
self._socket = null;
|
|
91
|
-
|
|
166
|
+
|
|
92
167
|
var ctx = new Connection(host, port, function(err) {
|
|
93
168
|
ctx.connect(self.options, function(err) {
|
|
94
|
-
|
|
169
|
+
|
|
95
170
|
if (err) {
|
|
96
171
|
self.db.emit('error', err);
|
|
97
172
|
return;
|
|
98
173
|
}
|
|
99
|
-
|
|
174
|
+
|
|
100
175
|
ctx.attach(self.options, function(err) {
|
|
101
|
-
|
|
176
|
+
|
|
102
177
|
if (err) {
|
|
103
178
|
self.db.emit('error', err);
|
|
104
179
|
return;
|
|
105
180
|
}
|
|
106
|
-
|
|
107
|
-
ctx._queue = ctx._queue.concat(self._queue);
|
|
108
|
-
ctx._pending = ctx._pending.concat(self._pending);
|
|
181
|
+
|
|
109
182
|
self.db.emit('reconnect');
|
|
110
|
-
|
|
183
|
+
|
|
111
184
|
}, self.db);
|
|
112
185
|
});
|
|
113
|
-
|
|
186
|
+
|
|
114
187
|
Object.assign(self, ctx);
|
|
115
|
-
|
|
188
|
+
|
|
116
189
|
}, self.options, self.db);
|
|
117
190
|
}, self._retry_connection_interval);
|
|
118
|
-
|
|
191
|
+
|
|
119
192
|
});
|
|
120
193
|
|
|
121
194
|
self._socket.on('error', function(e) {
|
|
@@ -155,6 +228,7 @@ class Connection {
|
|
|
155
228
|
|
|
156
229
|
while (xdr.pos < xdr.buffer.length) {
|
|
157
230
|
var cb = self._queue[0], pos = xdr.pos;
|
|
231
|
+
var lazySnapshot = cb ? cb.lazy_count : undefined;
|
|
158
232
|
|
|
159
233
|
decodeResponse(xdr, cb, self, self._lowercase_keys, function (err, obj) {
|
|
160
234
|
|
|
@@ -171,8 +245,17 @@ class Connection {
|
|
|
171
245
|
xdr.buffer.length, pos, self._queue.length);
|
|
172
246
|
}
|
|
173
247
|
|
|
174
|
-
|
|
175
|
-
|
|
248
|
+
// Restore lazy_count to its value before this (failed)
|
|
249
|
+
// decode attempt. Forcing lazy_count = 2 here made the
|
|
250
|
+
// decoder expect a second op_response that never arrives
|
|
251
|
+
// whenever a single large response (e.g. op_get_segment
|
|
252
|
+
// with a big buffer) spans multiple TCP packets.
|
|
253
|
+
if (cb) {
|
|
254
|
+
if (lazySnapshot === undefined) {
|
|
255
|
+
delete cb.lazy_count;
|
|
256
|
+
} else {
|
|
257
|
+
cb.lazy_count = lazySnapshot;
|
|
258
|
+
}
|
|
176
259
|
}
|
|
177
260
|
} else {
|
|
178
261
|
// Any other error (truly unknown opcode not handled above).
|
|
@@ -307,7 +390,7 @@ class Connection {
|
|
|
307
390
|
|
|
308
391
|
|
|
309
392
|
connect(options, callback) {
|
|
310
|
-
var pluginName = options.
|
|
393
|
+
var pluginName = options.pluginName || Const.AUTH_PLUGIN_LIST[0];
|
|
311
394
|
var msg = this._msg;
|
|
312
395
|
var blr = this._blr;
|
|
313
396
|
|
|
@@ -347,10 +430,16 @@ class Connection {
|
|
|
347
430
|
msg.addInt(Const.CONNECT_VERSION3);
|
|
348
431
|
msg.addInt(Const.ARCHITECTURE_GENERIC);
|
|
349
432
|
msg.addString(options.database || options.filename, Const.DEFAULT_ENCODING);
|
|
350
|
-
|
|
433
|
+
var maxProtocols = options.maxNegotiatedProtocols !== undefined ? options.maxNegotiatedProtocols : 10;
|
|
434
|
+
var protocolsToSend = Const.SUPPORTED_PROTOCOL;
|
|
435
|
+
if (protocolsToSend.length > maxProtocols) {
|
|
436
|
+
protocolsToSend = protocolsToSend.slice(-maxProtocols);
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
msg.addInt(protocolsToSend.length); // Count of Protocol version understood count.
|
|
351
440
|
msg.addBlr(this._blr);
|
|
352
441
|
|
|
353
|
-
for (var protocol of
|
|
442
|
+
for (var protocol of protocolsToSend) {
|
|
354
443
|
msg.addInt(protocol[0]); // Version
|
|
355
444
|
msg.addInt(protocol[1]); // Architecture
|
|
356
445
|
msg.addInt(protocol[2]); // Min type
|
|
@@ -380,21 +469,59 @@ class Connection {
|
|
|
380
469
|
// Wire encryption: send op_crypt if SRP session key is available
|
|
381
470
|
if (ret.sessionKey && ret.protocolVersion >= Const.PROTOCOL_VERSION13 &&
|
|
382
471
|
options.wireCrypt !== Const.WIRE_CRYPT_DISABLE) {
|
|
383
|
-
var
|
|
384
|
-
|
|
385
|
-
|
|
472
|
+
var padLen = 40;
|
|
473
|
+
var keyBuf = Buffer.from(ret.sessionKey.toString(16).padStart(padLen, '0'), 'hex');
|
|
474
|
+
|
|
475
|
+
var selectedPlugin = 'Arc4';
|
|
476
|
+
if (ret.keys) {
|
|
477
|
+
var serverPlugins = ret.keys.split(',').map(function(s) { return s.trim().toLowerCase(); });
|
|
478
|
+
var preferred = ['chacha64', 'chacha', 'arc4'];
|
|
479
|
+
for (var i = 0; i < preferred.length; i++) {
|
|
480
|
+
if (serverPlugins.indexOf(preferred[i]) !== -1) {
|
|
481
|
+
var mapping = {
|
|
482
|
+
chacha64: 'ChaCha64',
|
|
483
|
+
chacha: 'ChaCha',
|
|
484
|
+
arc4: 'Arc4'
|
|
485
|
+
};
|
|
486
|
+
selectedPlugin = mapping[preferred[i]];
|
|
487
|
+
break;
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
// Send op_crypt BEFORE enabling encryption (op_crypt is sent plaintext)
|
|
493
|
+
// The server then enables encryption from its side.
|
|
494
|
+
// After the op_response to op_crypt, both sides are encrypted.
|
|
495
|
+
self.sendOpCrypt(selectedPlugin);
|
|
496
|
+
|
|
497
|
+
if (selectedPlugin === 'Arc4') {
|
|
498
|
+
self._socket.enableEncryption(keyBuf, 'Arc4');
|
|
499
|
+
}
|
|
500
|
+
|
|
386
501
|
self._pending.push('crypt');
|
|
387
|
-
self._queue.push(function(cryptErr) {
|
|
502
|
+
self._queue.push(function(cryptErr, response) {
|
|
388
503
|
if (cryptErr) {
|
|
389
504
|
doError(cryptErr, callback);
|
|
390
505
|
return;
|
|
391
506
|
}
|
|
507
|
+
|
|
508
|
+
if (selectedPlugin !== 'Arc4') {
|
|
509
|
+
var iv = response && response.buffer;
|
|
510
|
+
if (!iv || (selectedPlugin === 'ChaCha64' && iv.length < 8) || (selectedPlugin === 'ChaCha' && iv.length < 12)) {
|
|
511
|
+
var errIv = new Error('Invalid or missing IV for ' + selectedPlugin + ' encryption');
|
|
512
|
+
doError(errIv, callback);
|
|
513
|
+
return;
|
|
514
|
+
}
|
|
515
|
+
self._socket.enableEncryption(keyBuf, selectedPlugin, iv);
|
|
516
|
+
}
|
|
517
|
+
|
|
392
518
|
if (callback)
|
|
393
519
|
callback(undefined, ret);
|
|
394
520
|
});
|
|
395
521
|
return;
|
|
396
522
|
}
|
|
397
523
|
|
|
524
|
+
|
|
398
525
|
if (callback)
|
|
399
526
|
callback(undefined, ret);
|
|
400
527
|
}
|
|
@@ -406,7 +533,7 @@ class Connection {
|
|
|
406
533
|
}
|
|
407
534
|
|
|
408
535
|
|
|
409
|
-
attach(options, callback, db) {
|
|
536
|
+
attach(options: any, callback?: any, db?: any) {
|
|
410
537
|
this._lowercase_keys = options.lowercase_keys || Const.DEFAULT_LOWERCASE_KEYS;
|
|
411
538
|
|
|
412
539
|
var database = options.database || options.filename;
|
|
@@ -460,6 +587,34 @@ class Connection {
|
|
|
460
587
|
if (options.sessionTimeZone) {
|
|
461
588
|
blr.addString(Const.isc_dpb_session_time_zone, options.sessionTimeZone, Const.DEFAULT_ENCODING);
|
|
462
589
|
}
|
|
590
|
+
|
|
591
|
+
if (options.parallelWorkers !== undefined) {
|
|
592
|
+
blr.addNumeric(Const.isc_dpb_parallel_workers, options.parallelWorkers);
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
if (options.maxInlineBlobSize !== undefined) {
|
|
596
|
+
blr.addNumeric(Const.isc_dpb_max_inline_blob_size, options.maxInlineBlobSize);
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
// Firebird 6.0 SQL Schema parameters (Protocol 20+).
|
|
600
|
+
// These DPB tags configure the session's current schema and the
|
|
601
|
+
// schema search path for unqualified object name resolution.
|
|
602
|
+
if (this.accept.protocolVersion >= Const.PROTOCOL_VERSION20) {
|
|
603
|
+
if (options.defaultSchema) {
|
|
604
|
+
// Sets CURRENT_SCHEMA for the session. Equivalent to issuing
|
|
605
|
+
// SET SCHEMA <name> immediately after connecting.
|
|
606
|
+
blr.addString(Const.isc_dpb_default_schema, options.defaultSchema, Const.DEFAULT_ENCODING);
|
|
607
|
+
}
|
|
608
|
+
if (options.searchPath) {
|
|
609
|
+
// Comma-separated ordered schema name list, like PostgreSQL's
|
|
610
|
+
// search_path. Unqualified object references are resolved by
|
|
611
|
+
// scanning schemas in this order.
|
|
612
|
+
const sp = Array.isArray(options.searchPath)
|
|
613
|
+
? options.searchPath.join(',')
|
|
614
|
+
: String(options.searchPath);
|
|
615
|
+
blr.addString(Const.isc_dpb_search_path, sp, Const.DEFAULT_ENCODING);
|
|
616
|
+
}
|
|
617
|
+
}
|
|
463
618
|
|
|
464
619
|
msg.addInt(Const.op_attach);
|
|
465
620
|
msg.addInt(0); // Database Object ID
|
|
@@ -494,11 +649,11 @@ class Connection {
|
|
|
494
649
|
|
|
495
650
|
|
|
496
651
|
detach(callback) {
|
|
497
|
-
|
|
652
|
+
|
|
498
653
|
var self = this;
|
|
499
|
-
|
|
654
|
+
|
|
500
655
|
if (self._isClosed)
|
|
501
|
-
return;
|
|
656
|
+
return this.throwClosed(callback);
|
|
502
657
|
|
|
503
658
|
self._isUsed = false;
|
|
504
659
|
self._isDetach = true;
|
|
@@ -519,6 +674,10 @@ class Connection {
|
|
|
519
674
|
|
|
520
675
|
|
|
521
676
|
createDatabase(options, callback) {
|
|
677
|
+
// Mirror attach(): honour the lowercase_keys option so that db.query()
|
|
678
|
+
// called on a freshly-created database returns the expected column case.
|
|
679
|
+
this._lowercase_keys = options.lowercase_keys || Const.DEFAULT_LOWERCASE_KEYS;
|
|
680
|
+
|
|
522
681
|
var database = options.database || options.filename;
|
|
523
682
|
if (database == null || database.length === 0) {
|
|
524
683
|
doError(new Error('No database specified'), callback);
|
|
@@ -566,6 +725,27 @@ class Connection {
|
|
|
566
725
|
if (options.sessionTimeZone) {
|
|
567
726
|
blr.addString(Const.isc_dpb_session_time_zone, options.sessionTimeZone, Const.DEFAULT_ENCODING);
|
|
568
727
|
}
|
|
728
|
+
|
|
729
|
+
if (options.parallelWorkers !== undefined) {
|
|
730
|
+
blr.addNumeric(Const.isc_dpb_parallel_workers, options.parallelWorkers);
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
if (options.maxInlineBlobSize !== undefined) {
|
|
734
|
+
blr.addNumeric(Const.isc_dpb_max_inline_blob_size, options.maxInlineBlobSize);
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
// Firebird 6.0 SQL Schema parameters (Protocol 20+).
|
|
738
|
+
if (this.accept.protocolVersion >= Const.PROTOCOL_VERSION20) {
|
|
739
|
+
if (options.defaultSchema) {
|
|
740
|
+
blr.addString(Const.isc_dpb_default_schema, options.defaultSchema, Const.DEFAULT_ENCODING);
|
|
741
|
+
}
|
|
742
|
+
if (options.searchPath) {
|
|
743
|
+
const sp = Array.isArray(options.searchPath)
|
|
744
|
+
? options.searchPath.join(',')
|
|
745
|
+
: String(options.searchPath);
|
|
746
|
+
blr.addString(Const.isc_dpb_search_path, sp, Const.DEFAULT_ENCODING);
|
|
747
|
+
}
|
|
748
|
+
}
|
|
569
749
|
|
|
570
750
|
blr.addNumeric(Const.isc_dpb_sql_dialect, 3);
|
|
571
751
|
blr.addNumeric(Const.isc_dpb_force_write, 1);
|
|
@@ -743,9 +923,9 @@ class Connection {
|
|
|
743
923
|
|
|
744
924
|
|
|
745
925
|
commitRetaining(transaction, callback) {
|
|
746
|
-
|
|
926
|
+
|
|
747
927
|
if (this._isClosed)
|
|
748
|
-
|
|
928
|
+
return this.throwClosed(callback);
|
|
749
929
|
|
|
750
930
|
// for auto detach
|
|
751
931
|
this._pending.push('commitRetaining');
|
|
@@ -829,7 +1009,7 @@ class Connection {
|
|
|
829
1009
|
|
|
830
1010
|
allocateAndPrepareStatement(transaction, query, plan, callback) {
|
|
831
1011
|
var self = this;
|
|
832
|
-
var mainCallback = function(err, ret) {
|
|
1012
|
+
var mainCallback: any = function(err: any, ret: any) {
|
|
833
1013
|
if (!err) {
|
|
834
1014
|
mainCallback.response.handle = ret.handle;
|
|
835
1015
|
describe(ret.buffer, mainCallback.response);
|
|
@@ -856,7 +1036,11 @@ class Connection {
|
|
|
856
1036
|
msg.addInt(this.dbhandle);
|
|
857
1037
|
mainCallback.lazy_count = 1;
|
|
858
1038
|
|
|
859
|
-
|
|
1039
|
+
const describeBytes = this.accept.protocolVersion >= Const.PROTOCOL_VERSION20 ? Const.DESCRIBE_WITH_SCHEMA : Const.DESCRIBE;
|
|
1040
|
+
if (process.env.FIREBIRD_DEBUG) {
|
|
1041
|
+
console.log('[fb-debug] describeBytes:', describeBytes);
|
|
1042
|
+
}
|
|
1043
|
+
blr.addBytes(describeBytes);
|
|
860
1044
|
if (plan)
|
|
861
1045
|
blr.addByte(Const.isc_info_sql_get_plan);
|
|
862
1046
|
|
|
@@ -909,7 +1093,8 @@ class Connection {
|
|
|
909
1093
|
plan = false;
|
|
910
1094
|
}
|
|
911
1095
|
|
|
912
|
-
|
|
1096
|
+
const describeBytes = this.accept.protocolVersion >= Const.PROTOCOL_VERSION20 ? Const.DESCRIBE_WITH_SCHEMA : Const.DESCRIBE;
|
|
1097
|
+
blr.addBytes(describeBytes);
|
|
913
1098
|
|
|
914
1099
|
if (plan)
|
|
915
1100
|
blr.addByte(Const.isc_info_sql_get_plan);
|
|
@@ -1050,6 +1235,12 @@ class Connection {
|
|
|
1050
1235
|
value = params[i];
|
|
1051
1236
|
meta = input[i];
|
|
1052
1237
|
|
|
1238
|
+
if (statement.connection.options && statement.connection.options.jsonAsObject && value !== null && value !== undefined && typeof value === 'object' && !(value instanceof Date) && !(value instanceof Buffer)) {
|
|
1239
|
+
if (typeof value.pipe !== 'function') {
|
|
1240
|
+
value = JSON.stringify(value);
|
|
1241
|
+
}
|
|
1242
|
+
}
|
|
1243
|
+
|
|
1053
1244
|
if (value === null || value === undefined) {
|
|
1054
1245
|
switch (meta.type) {
|
|
1055
1246
|
case Const.SQL_VARYING:
|
|
@@ -1110,29 +1301,33 @@ class Connection {
|
|
|
1110
1301
|
break;
|
|
1111
1302
|
|
|
1112
1303
|
default:
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1304
|
+
if (Buffer.isBuffer(value)) {
|
|
1305
|
+
ret[i] = new Xsql.SQLParamBuffer(value);
|
|
1306
|
+
} else {
|
|
1307
|
+
switch (typeof value) {
|
|
1308
|
+
case 'bigint':
|
|
1309
|
+
ret[i] = new Xsql.SQLParamInt128(value);
|
|
1310
|
+
break;
|
|
1311
|
+
case 'number':
|
|
1312
|
+
if (value % 1 === 0) {
|
|
1313
|
+
if (value >= Const.MIN_INT && value <= Const.MAX_INT)
|
|
1314
|
+
ret[i] = new Xsql.SQLParamInt(value);
|
|
1315
|
+
else
|
|
1316
|
+
ret[i] = new Xsql.SQLParamInt64(value);
|
|
1317
|
+
} else
|
|
1318
|
+
ret[i] = new Xsql.SQLParamDouble(value);
|
|
1319
|
+
break;
|
|
1320
|
+
case 'string':
|
|
1321
|
+
ret[i] = new Xsql.SQLParamString(value);
|
|
1322
|
+
break;
|
|
1323
|
+
case 'boolean':
|
|
1324
|
+
ret[i] = new Xsql.SQLParamBool(value);
|
|
1325
|
+
break;
|
|
1326
|
+
default:
|
|
1327
|
+
//throw new Error('Unexpected parametter: ' + JSON.stringify(params) + ' - ' + JSON.stringify(input));
|
|
1328
|
+
ret[i] = new Xsql.SQLParamString(value.toString());
|
|
1329
|
+
break;
|
|
1330
|
+
}
|
|
1136
1331
|
}
|
|
1137
1332
|
step(i + 1);
|
|
1138
1333
|
}
|
|
@@ -1147,10 +1342,39 @@ class Connection {
|
|
|
1147
1342
|
if (input.length) {
|
|
1148
1343
|
|
|
1149
1344
|
if (!(params instanceof Array)) {
|
|
1150
|
-
if (params !== undefined)
|
|
1345
|
+
if (params !== undefined && typeof params === 'object' && params !== null) {
|
|
1346
|
+
var mappedParams = [];
|
|
1347
|
+
for (var i = 0; i < input.length; i++) {
|
|
1348
|
+
mappedParams.push(undefined);
|
|
1349
|
+
}
|
|
1350
|
+
var matchedCount = 0;
|
|
1351
|
+
var nameMap = {};
|
|
1352
|
+
for (var i = 0; i < input.length; i++) {
|
|
1353
|
+
var name = input[i].alias || input[i].field;
|
|
1354
|
+
if (name) {
|
|
1355
|
+
nameMap[name.toUpperCase()] = i;
|
|
1356
|
+
}
|
|
1357
|
+
}
|
|
1358
|
+
for (var key in params) {
|
|
1359
|
+
if (Object.prototype.hasOwnProperty.call(params, key)) {
|
|
1360
|
+
var cleanKey = key.startsWith(':') ? key.substring(1) : key;
|
|
1361
|
+
var index = nameMap[cleanKey.toUpperCase()];
|
|
1362
|
+
if (index !== undefined) {
|
|
1363
|
+
mappedParams[index] = params[key];
|
|
1364
|
+
matchedCount++;
|
|
1365
|
+
}
|
|
1366
|
+
}
|
|
1367
|
+
}
|
|
1368
|
+
if (matchedCount > 0) {
|
|
1369
|
+
params = mappedParams;
|
|
1370
|
+
} else {
|
|
1371
|
+
params = [params];
|
|
1372
|
+
}
|
|
1373
|
+
} else if (params !== undefined) {
|
|
1151
1374
|
params = [params];
|
|
1152
|
-
else
|
|
1375
|
+
} else {
|
|
1153
1376
|
params = [];
|
|
1377
|
+
}
|
|
1154
1378
|
}
|
|
1155
1379
|
|
|
1156
1380
|
if (params.length !== input.length) {
|
|
@@ -1170,7 +1394,7 @@ class Connection {
|
|
|
1170
1394
|
}
|
|
1171
1395
|
|
|
1172
1396
|
|
|
1173
|
-
sendExecute(op, statement, transaction, callback, parameters) {
|
|
1397
|
+
sendExecute(op: number, statement: any, transaction: any, callback: any, parameters?: any[]) {
|
|
1174
1398
|
var msg = this._msg;
|
|
1175
1399
|
var blr = this._blr;
|
|
1176
1400
|
msg.pos = 0;
|
|
@@ -1191,7 +1415,7 @@ class Connection {
|
|
|
1191
1415
|
var nullBits = new BitSet();
|
|
1192
1416
|
|
|
1193
1417
|
for (var i = 0; i < parameters.length; i++) {
|
|
1194
|
-
nullBits.set(i,
|
|
1418
|
+
nullBits.set(i, parameters[i].value === null ? 1 : 0);
|
|
1195
1419
|
}
|
|
1196
1420
|
|
|
1197
1421
|
var nullBuffer = nullBits.toBuffer();
|
|
@@ -1241,6 +1465,14 @@ class Connection {
|
|
|
1241
1465
|
// TODO impl statement timout
|
|
1242
1466
|
msg.addInt(statement.options?.timeout || 0); // p_sqldata_timeout
|
|
1243
1467
|
}
|
|
1468
|
+
|
|
1469
|
+
if (this.accept.protocolVersion >= Const.PROTOCOL_VERSION18) {
|
|
1470
|
+
msg.addInt(statement.options?.scrollable ? 1 : 0); // p_sqldata_cursor_flags (1 = CURSOR_TYPE_SCROLLABLE)
|
|
1471
|
+
}
|
|
1472
|
+
|
|
1473
|
+
if (this.accept.protocolVersion >= Const.PROTOCOL_VERSION19) {
|
|
1474
|
+
msg.addInt(statement.options?.maxInlineBlobSize !== undefined ? statement.options.maxInlineBlobSize : (this.options?.maxInlineBlobSize || 0)); // p_sqldata_inline_blob_size
|
|
1475
|
+
}
|
|
1244
1476
|
|
|
1245
1477
|
callback.statement = statement;
|
|
1246
1478
|
this._queueEvent(callback);
|
|
@@ -1274,6 +1506,57 @@ class Connection {
|
|
|
1274
1506
|
}
|
|
1275
1507
|
|
|
1276
1508
|
|
|
1509
|
+
fetchScroll(statement, transaction, direction, offset, count, callback) {
|
|
1510
|
+
if (typeof count === 'function') {
|
|
1511
|
+
callback = count;
|
|
1512
|
+
count = undefined;
|
|
1513
|
+
}
|
|
1514
|
+
if (typeof offset === 'function') {
|
|
1515
|
+
callback = offset;
|
|
1516
|
+
offset = undefined;
|
|
1517
|
+
count = undefined;
|
|
1518
|
+
}
|
|
1519
|
+
|
|
1520
|
+
var msg = this._msg;
|
|
1521
|
+
var blr = this._blr;
|
|
1522
|
+
|
|
1523
|
+
msg.pos = 0;
|
|
1524
|
+
blr.pos = 0;
|
|
1525
|
+
|
|
1526
|
+
let dirInt = 0;
|
|
1527
|
+
if (typeof direction === 'number') {
|
|
1528
|
+
dirInt = direction;
|
|
1529
|
+
} else if (typeof direction === 'string') {
|
|
1530
|
+
const dirUpper = direction.toUpperCase();
|
|
1531
|
+
switch (dirUpper) {
|
|
1532
|
+
case 'NEXT': dirInt = 0; break;
|
|
1533
|
+
case 'PRIOR': dirInt = 1; break;
|
|
1534
|
+
case 'FIRST': dirInt = 2; break;
|
|
1535
|
+
case 'LAST': dirInt = 3; break;
|
|
1536
|
+
case 'ABSOLUTE': dirInt = 4; break;
|
|
1537
|
+
case 'RELATIVE': dirInt = 5; break;
|
|
1538
|
+
default:
|
|
1539
|
+
throw new Error('Invalid fetch direction: ' + direction);
|
|
1540
|
+
}
|
|
1541
|
+
}
|
|
1542
|
+
|
|
1543
|
+
const offsetVal = offset || 0;
|
|
1544
|
+
const fetchCount = count || 1;
|
|
1545
|
+
|
|
1546
|
+
msg.addInt(Const.op_fetch_scroll);
|
|
1547
|
+
msg.addInt(statement.handle);
|
|
1548
|
+
CalcBlr(blr, statement.output);
|
|
1549
|
+
msg.addBlr(blr);
|
|
1550
|
+
msg.addInt(0); // message number
|
|
1551
|
+
msg.addInt(fetchCount); // fetch count
|
|
1552
|
+
msg.addInt(dirInt); // fetch operation
|
|
1553
|
+
msg.addInt(offsetVal); // fetch position (offset)
|
|
1554
|
+
|
|
1555
|
+
callback.statement = statement;
|
|
1556
|
+
this._queueEvent(callback);
|
|
1557
|
+
}
|
|
1558
|
+
|
|
1559
|
+
|
|
1277
1560
|
fetchAll(statement, transaction, callback) {
|
|
1278
1561
|
const self = this;
|
|
1279
1562
|
const custom = statement.options || {};
|
|
@@ -1287,12 +1570,25 @@ class Connection {
|
|
|
1287
1570
|
}
|
|
1288
1571
|
|
|
1289
1572
|
if (ret && ret.data && ret.data.length) {
|
|
1290
|
-
|
|
1573
|
+
// Read blobs sequentially instead of in parallel to avoid
|
|
1574
|
+
// exceeding Firebird's per-connection open-blob-handle limit,
|
|
1575
|
+
// which causes a server-side deadlock when many rows contain
|
|
1576
|
+
// BLOBs and blobAsText is true. See issue #387.
|
|
1577
|
+
const arrBlobFns = ret.arrBlob || [];
|
|
1578
|
+
const readBlobsSequentially = (index, results) => {
|
|
1579
|
+
if (index >= arrBlobFns.length) {
|
|
1580
|
+
return Promise.resolve(results);
|
|
1581
|
+
}
|
|
1582
|
+
return arrBlobFns[index](transaction).then((v) => {
|
|
1583
|
+
results.push(v);
|
|
1584
|
+
return readBlobsSequentially(index + 1, results);
|
|
1585
|
+
});
|
|
1586
|
+
};
|
|
1291
1587
|
|
|
1292
|
-
|
|
1588
|
+
readBlobsSequentially(0, []).then((arrBlob) => {
|
|
1293
1589
|
for (let i = 0; i < arrBlob.length; i++) {
|
|
1294
1590
|
const blob = arrBlob[i];
|
|
1295
|
-
ret.data[blob.row][blob.column] = blob.value;
|
|
1591
|
+
ret.data[blob.row][blob.column] = parseValueIfJson(blob.value, statement.connection.options);
|
|
1296
1592
|
}
|
|
1297
1593
|
|
|
1298
1594
|
doSynchronousLoop(ret.data, (row, _i, next) => {
|
|
@@ -1354,7 +1650,7 @@ class Connection {
|
|
|
1354
1650
|
msg.pos = 0;
|
|
1355
1651
|
msg.addInt(Const.op_get_segment);
|
|
1356
1652
|
msg.addInt(blob.handle);
|
|
1357
|
-
msg.addInt(1024); // buffer length
|
|
1653
|
+
msg.addInt(this.options.blobReadChunkSize || 1024); // buffer length (max 65535)
|
|
1358
1654
|
msg.addInt(0); // ???
|
|
1359
1655
|
this._queueEvent(callback);
|
|
1360
1656
|
}
|
|
@@ -1386,7 +1682,7 @@ class Connection {
|
|
|
1386
1682
|
}
|
|
1387
1683
|
|
|
1388
1684
|
|
|
1389
|
-
svcattach(options, callback, svc) {
|
|
1685
|
+
svcattach(options: any, callback?: any, svc?: any) {
|
|
1390
1686
|
this._lowercase_keys = options.lowercase_keys || Const.DEFAULT_LOWERCASE_KEYS;
|
|
1391
1687
|
var database = options.database || options.filename;
|
|
1392
1688
|
var user = options.user || Const.DEFAULT_USER;
|
|
@@ -1485,9 +1781,11 @@ class Connection {
|
|
|
1485
1781
|
|
|
1486
1782
|
svcdetach(callback) {
|
|
1487
1783
|
var self = this;
|
|
1488
|
-
|
|
1489
|
-
if (self._isClosed)
|
|
1784
|
+
|
|
1785
|
+
if (self._isClosed) {
|
|
1786
|
+
doError(new Error('Connection is closed.'), callback);
|
|
1490
1787
|
return;
|
|
1788
|
+
}
|
|
1491
1789
|
|
|
1492
1790
|
self._isUsed = false;
|
|
1493
1791
|
self._isDetach = true;
|
|
@@ -1507,7 +1805,13 @@ class Connection {
|
|
|
1507
1805
|
|
|
1508
1806
|
|
|
1509
1807
|
|
|
1510
|
-
auxConnection(callback) {
|
|
1808
|
+
auxConnection(eventid, callback) {
|
|
1809
|
+
if (typeof eventid === 'function') {
|
|
1810
|
+
// Preserve the older auxConnection(callback) call shape; plain
|
|
1811
|
+
// auxiliary connections historically used event id 0.
|
|
1812
|
+
callback = eventid;
|
|
1813
|
+
eventid = 0;
|
|
1814
|
+
}
|
|
1511
1815
|
var self = this;
|
|
1512
1816
|
if (self._isClosed)
|
|
1513
1817
|
return this.throwClosed(callback);
|
|
@@ -1516,10 +1820,10 @@ class Connection {
|
|
|
1516
1820
|
msg.addInt(Const.op_connect_request);
|
|
1517
1821
|
msg.addInt(1); // async
|
|
1518
1822
|
msg.addInt(self.dbhandle);
|
|
1519
|
-
msg.addInt(
|
|
1823
|
+
msg.addInt(eventid);
|
|
1520
1824
|
if (process.env.FIREBIRD_DEBUG) {
|
|
1521
|
-
console.log('[fb-debug] auxConnection: sending op_connect_request(53) dbhandle=%d queue_before=%d xdr_saved=%s',
|
|
1522
|
-
self.dbhandle, self._queue.length, Boolean(self._xdr));
|
|
1825
|
+
console.log('[fb-debug] auxConnection: sending op_connect_request(53) dbhandle=%d eventid=%d queue_before=%d xdr_saved=%s',
|
|
1826
|
+
self.dbhandle, eventid, self._queue.length, Boolean(self._xdr));
|
|
1523
1827
|
}
|
|
1524
1828
|
function cb(err, ret) {
|
|
1525
1829
|
|
|
@@ -1561,7 +1865,7 @@ class Connection {
|
|
|
1561
1865
|
// prepare EPB
|
|
1562
1866
|
blr.addByte(1) // epb_version
|
|
1563
1867
|
for (var event in events) {
|
|
1564
|
-
var event_buffer = Buffer.from(event, '
|
|
1868
|
+
var event_buffer = Buffer.from(event, 'utf8');
|
|
1565
1869
|
blr.addByte(event_buffer.length);
|
|
1566
1870
|
blr.addBytes(event_buffer);
|
|
1567
1871
|
blr.addInt32(events[event]);
|
|
@@ -1613,10 +1917,30 @@ const opcodeNames = Object.fromEntries(
|
|
|
1613
1917
|
Object.entries(Const).filter(([k]) => k.startsWith('op_')).map(([k, v]) => [v, k])
|
|
1614
1918
|
);
|
|
1615
1919
|
|
|
1616
|
-
function decodeResponse(data, callback, cnx, lowercase_keys, cb) {
|
|
1920
|
+
function decodeResponse(data: any, callback: any, cnx: any, lowercase_keys: any, cb: (err?: any, obj?: any) => void) {
|
|
1617
1921
|
try {
|
|
1618
1922
|
do {
|
|
1619
1923
|
var r = data.r || data.readInt();
|
|
1924
|
+
if (data.r) {
|
|
1925
|
+
data.r = null;
|
|
1926
|
+
}
|
|
1927
|
+
if (r === Const.op_dummy) {
|
|
1928
|
+
continue;
|
|
1929
|
+
}
|
|
1930
|
+
if (r === Const.op_inline_blob) {
|
|
1931
|
+
if (process.env.FIREBIRD_DEBUG) {
|
|
1932
|
+
console.log('[fb-debug] decodeResponse: opcode=op_inline_blob');
|
|
1933
|
+
}
|
|
1934
|
+
var tran_id = data.readInt();
|
|
1935
|
+
var blob_id = data.readQuad();
|
|
1936
|
+
var blob_data = data.readArray();
|
|
1937
|
+
if (!cnx._inlineBlobs) {
|
|
1938
|
+
cnx._inlineBlobs = new Map();
|
|
1939
|
+
}
|
|
1940
|
+
const cacheKey = `${blob_id.high}:${blob_id.low}`;
|
|
1941
|
+
cnx._inlineBlobs.set(cacheKey, blob_data);
|
|
1942
|
+
r = Const.op_dummy; // Continue loop to read next opcode
|
|
1943
|
+
}
|
|
1620
1944
|
} while (r === Const.op_dummy);
|
|
1621
1945
|
|
|
1622
1946
|
if (process.env.FIREBIRD_DEBUG) {
|
|
@@ -1718,7 +2042,7 @@ function decodeResponse(data, callback, cnx, lowercase_keys, cb) {
|
|
|
1718
2042
|
_xdrpos = data.pos;
|
|
1719
2043
|
const key = custom.asObject ? data.fcols[data.fcolumn] : data.fcolumn;
|
|
1720
2044
|
const row = data.frows.length;
|
|
1721
|
-
let value = item.decode(data, lowerV13);
|
|
2045
|
+
let value = item.decode(data, lowerV13, cnx.options);
|
|
1722
2046
|
|
|
1723
2047
|
if (item.type === Const.SQL_BLOB && value !== null) {
|
|
1724
2048
|
if (item.subType === Const.isc_blob_text && cnx.options.blobAsText) {
|
|
@@ -1729,7 +2053,7 @@ function decodeResponse(data, callback, cnx, lowercase_keys, cb) {
|
|
|
1729
2053
|
}
|
|
1730
2054
|
}
|
|
1731
2055
|
|
|
1732
|
-
data.frow[key] = value;
|
|
2056
|
+
data.frow[key] = parseValueIfJson(value, cnx.options);
|
|
1733
2057
|
} catch (e) {
|
|
1734
2058
|
// uncomplete packet read
|
|
1735
2059
|
data.pos = _xdrpos;
|
|
@@ -1783,7 +2107,7 @@ function decodeResponse(data, callback, cnx, lowercase_keys, cb) {
|
|
|
1783
2107
|
case Const.op_accept:
|
|
1784
2108
|
case Const.op_cond_accept:
|
|
1785
2109
|
case Const.op_accept_data:
|
|
1786
|
-
let accept = {
|
|
2110
|
+
let accept: any = {
|
|
1787
2111
|
protocolVersion: data.readInt(),
|
|
1788
2112
|
protocolArchitecture: data.readInt(),
|
|
1789
2113
|
protocolMinimumType: data.readInt(),
|
|
@@ -1805,29 +2129,39 @@ function decodeResponse(data, callback, cnx, lowercase_keys, cb) {
|
|
|
1805
2129
|
accept.pluginName = data.readString(Const.DEFAULT_ENCODING);
|
|
1806
2130
|
var is_authenticated = data.readInt();
|
|
1807
2131
|
var keys = data.readString(Const.DEFAULT_ENCODING); // keys
|
|
2132
|
+
accept.keys = keys;
|
|
1808
2133
|
|
|
1809
2134
|
if (is_authenticated === 0) {
|
|
1810
2135
|
if (cnx.options.pluginName && cnx.options.pluginName !== accept.pluginName) {
|
|
1811
|
-
|
|
2136
|
+
var errPlugin = new Error('Server don\'t accept plugin : ' + cnx.options.pluginName + ', but support : ' + accept.pluginName);
|
|
2137
|
+
doError(errPlugin, callback);
|
|
2138
|
+
return cb(errPlugin);
|
|
2139
|
+
}
|
|
2140
|
+
|
|
2141
|
+
if (Const.AUTH_PLUGIN_SRP_LIST.indexOf(accept.pluginName) !== -1 && !cnx.clientKeys) {
|
|
2142
|
+
var errPlugin = new Error('Server accepted plugin : ' + accept.pluginName + ', but client did not initialize SRP keys');
|
|
2143
|
+
doError(errPlugin, callback);
|
|
2144
|
+
return cb(errPlugin);
|
|
1812
2145
|
}
|
|
1813
2146
|
|
|
1814
2147
|
if (Const.AUTH_PLUGIN_SRP_LIST.indexOf(accept.pluginName) !== -1) {
|
|
1815
2148
|
var crypto = {
|
|
1816
2149
|
Srp: 'sha1',
|
|
1817
|
-
Srp256: 'sha256'
|
|
2150
|
+
Srp256: 'sha256',
|
|
2151
|
+
Srp384: 'sha384',
|
|
2152
|
+
Srp512: 'sha512'
|
|
1818
2153
|
};
|
|
1819
2154
|
accept.srpAlgo = crypto[accept.pluginName];
|
|
1820
2155
|
|
|
1821
|
-
|
|
1822
|
-
|
|
2156
|
+
if (!d.buffer) {
|
|
2157
|
+
cnx._pendingAccept = accept;
|
|
1823
2158
|
cnx.sendOpContAuth(
|
|
1824
2159
|
cnx.clientKeys.public.toString(16),
|
|
1825
|
-
DEFAULT_ENCODING,
|
|
2160
|
+
Const.DEFAULT_ENCODING,
|
|
1826
2161
|
accept.pluginName
|
|
1827
2162
|
);
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
}*/
|
|
2163
|
+
return;
|
|
2164
|
+
}
|
|
1831
2165
|
|
|
1832
2166
|
// Check buffer contains salt
|
|
1833
2167
|
var saltLen = d.buffer.readUInt16LE(0);
|
|
@@ -1837,17 +2171,27 @@ function decodeResponse(data, callback, cnx, lowercase_keys, cb) {
|
|
|
1837
2171
|
|
|
1838
2172
|
// Check buffer contains key
|
|
1839
2173
|
var keyLen = d.buffer.readUInt16LE(saltLen + 2);
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
2174
|
+
if (d.buffer.length < saltLen + 4) {
|
|
2175
|
+
var errBuf = new Error('Invalid buffer size for ' + accept.pluginName + ' login');
|
|
2176
|
+
doError(errBuf, callback);
|
|
2177
|
+
return cb(errBuf);
|
|
1843
2178
|
}
|
|
2179
|
+
var keyStart = (saltLen + 2 + 3) & ~3;
|
|
1844
2180
|
|
|
1845
2181
|
// Server keys
|
|
1846
2182
|
cnx.serverKeys = {
|
|
1847
2183
|
salt: d.buffer.slice(2, saltLen + 2).toString('utf8'),
|
|
1848
|
-
public: BigInt('0x' + d.buffer.slice(keyStart, d.buffer.length).toString('utf8'))
|
|
2184
|
+
public: BigInt('0x' + d.buffer.slice(keyStart, d.buffer.length).toString('utf8')),
|
|
2185
|
+
pluginName: accept.pluginName
|
|
1849
2186
|
};
|
|
1850
2187
|
|
|
2188
|
+
console.log('--- DEBUG SRP Handshake ---');
|
|
2189
|
+
console.log('salt:', cnx.serverKeys.salt);
|
|
2190
|
+
console.log('server public key:', cnx.serverKeys.public.toString(16));
|
|
2191
|
+
console.log('client public key:', cnx.clientKeys.public.toString(16));
|
|
2192
|
+
console.log('client private key:', cnx.clientKeys.private.toString(16));
|
|
2193
|
+
console.log('hashAlgo:', accept.srpAlgo);
|
|
2194
|
+
|
|
1851
2195
|
const _t1 = Date.now();
|
|
1852
2196
|
var proof = srp.clientProof(
|
|
1853
2197
|
cnx.options.user.toUpperCase(),
|
|
@@ -1858,6 +2202,10 @@ function decodeResponse(data, callback, cnx, lowercase_keys, cb) {
|
|
|
1858
2202
|
cnx.clientKeys.private,
|
|
1859
2203
|
accept.srpAlgo
|
|
1860
2204
|
);
|
|
2205
|
+
|
|
2206
|
+
console.log('client proof M1:', proof.authData.toString(16));
|
|
2207
|
+
console.log('client session key K:', proof.clientSessionKey.toString(16));
|
|
2208
|
+
|
|
1861
2209
|
if (process.env.FIREBIRD_DEBUG) {
|
|
1862
2210
|
console.log('[fb-debug] srp.clientProof(%s): %dms', accept.srpAlgo, Date.now() - _t1);
|
|
1863
2211
|
}
|
|
@@ -1917,14 +2265,88 @@ function decodeResponse(data, callback, cnx, lowercase_keys, cb) {
|
|
|
1917
2265
|
|
|
1918
2266
|
// During SRP mutual authentication, the server sends op_cont_auth
|
|
1919
2267
|
// with its proof (M2) after receiving the client's proof (M1).
|
|
1920
|
-
// When we have an active auth exchange for this plugin
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
2268
|
+
// When we have an active auth exchange for this plugin:
|
|
2269
|
+
if (cnx._pendingAccept && (cnx._pendingAccept.pluginName === pluginName || Const.AUTH_PLUGIN_SRP_LIST.indexOf(pluginName) !== -1)) {
|
|
2270
|
+
if (cnx._pendingAccept.pluginName !== pluginName) {
|
|
2271
|
+
cnx._pendingAccept.pluginName = pluginName;
|
|
2272
|
+
}
|
|
2273
|
+
// The server can switch SRP hash variants mid-handshake: after
|
|
2274
|
+
// the client answers with e.g. Srp256, some accounts make the
|
|
2275
|
+
// server come back with a fresh op_cont_auth naming a different
|
|
2276
|
+
// SRP plugin (e.g. plain Srp/sha1), reusing the same salt/B.
|
|
2277
|
+
// That must be treated as a brand-new challenge - recomputing
|
|
2278
|
+
// the proof with the new plugin's hash algorithm - rather than
|
|
2279
|
+
// as the server's M2 proof, otherwise the client silently waits
|
|
2280
|
+
// forever for an op_accept the server will never send (#254).
|
|
2281
|
+
if (!cnx.serverKeys || cnx.serverKeys.pluginName !== pluginName) {
|
|
2282
|
+
// Check buffer contains salt
|
|
2283
|
+
var saltLen = d.buffer.readUInt16LE(0);
|
|
2284
|
+
if (saltLen > 32 * 2) {
|
|
2285
|
+
console.log('salt to long'); // TODO : Throw error
|
|
2286
|
+
}
|
|
2287
|
+
|
|
2288
|
+
// Check buffer contains key
|
|
2289
|
+
var keyLen = d.buffer.readUInt16LE(saltLen + 2);
|
|
2290
|
+
if (d.buffer.length < saltLen + 4) {
|
|
2291
|
+
var errBuf = new Error('Invalid buffer size for ' + pluginName + ' login');
|
|
2292
|
+
doError(errBuf, callback);
|
|
2293
|
+
return cb(errBuf);
|
|
2294
|
+
}
|
|
2295
|
+
var keyStart = (saltLen + 2 + 3) & ~3;
|
|
2296
|
+
|
|
2297
|
+
// Server keys
|
|
2298
|
+
cnx.serverKeys = {
|
|
2299
|
+
salt: d.buffer.slice(2, saltLen + 2).toString('utf8'),
|
|
2300
|
+
public: BigInt('0x' + d.buffer.slice(keyStart, d.buffer.length).toString('utf8')),
|
|
2301
|
+
pluginName: pluginName
|
|
2302
|
+
};
|
|
2303
|
+
|
|
2304
|
+
var crypto = {
|
|
2305
|
+
Srp: 'sha1',
|
|
2306
|
+
Srp256: 'sha256',
|
|
2307
|
+
Srp384: 'sha384',
|
|
2308
|
+
Srp512: 'sha512'
|
|
2309
|
+
};
|
|
2310
|
+
var srpAlgo = crypto[pluginName];
|
|
2311
|
+
|
|
2312
|
+
console.log('--- DEBUG SRP Handshake ---');
|
|
2313
|
+
console.log('salt:', cnx.serverKeys.salt);
|
|
2314
|
+
console.log('server public key:', cnx.serverKeys.public.toString(16));
|
|
2315
|
+
console.log('client public key:', cnx.clientKeys.public.toString(16));
|
|
2316
|
+
console.log('client private key:', cnx.clientKeys.private.toString(16));
|
|
2317
|
+
console.log('hashAlgo:', srpAlgo);
|
|
2318
|
+
|
|
2319
|
+
const _t1 = Date.now();
|
|
2320
|
+
var proof = srp.clientProof(
|
|
2321
|
+
cnx.options.user.toUpperCase(),
|
|
2322
|
+
cnx.options.password,
|
|
2323
|
+
cnx.serverKeys.salt,
|
|
2324
|
+
cnx.clientKeys.public,
|
|
2325
|
+
cnx.serverKeys.public,
|
|
2326
|
+
cnx.clientKeys.private,
|
|
2327
|
+
srpAlgo
|
|
2328
|
+
);
|
|
2329
|
+
|
|
2330
|
+
if (process.env.FIREBIRD_DEBUG) {
|
|
2331
|
+
console.log('[fb-debug] srp.clientProof(%s): %dms', srpAlgo, Date.now() - _t1);
|
|
2332
|
+
}
|
|
2333
|
+
|
|
2334
|
+
cnx._pendingAccept.authData = proof.authData.toString(16);
|
|
2335
|
+
cnx._pendingAccept.sessionKey = proof.clientSessionKey;
|
|
2336
|
+
|
|
2337
|
+
cnx.sendOpContAuth(
|
|
2338
|
+
cnx._pendingAccept.authData,
|
|
2339
|
+
Const.DEFAULT_ENCODING,
|
|
2340
|
+
pluginName
|
|
2341
|
+
);
|
|
2342
|
+
return; // wait for server SRP proof (M2) and/or op_accept
|
|
2343
|
+
} else {
|
|
2344
|
+
if (process.env.FIREBIRD_DEBUG) {
|
|
2345
|
+
console.log('[fb-debug] auth: server SRP proof (M2) received, waiting for op_accept t=%dms',
|
|
2346
|
+
cnx._authStartTime ? Date.now() - cnx._authStartTime : -1);
|
|
2347
|
+
}
|
|
2348
|
+
return; // Server SRP proof received - wait for op_accept
|
|
1926
2349
|
}
|
|
1927
|
-
return; // Server SRP proof received - wait for op_accept
|
|
1928
2350
|
}
|
|
1929
2351
|
|
|
1930
2352
|
// Firebird 4/5 (protocols 16/17) chained-auth: after the client sends
|
|
@@ -1976,6 +2398,7 @@ function decodeResponse(data, callback, cnx, lowercase_keys, cb) {
|
|
|
1976
2398
|
// Database encryption key callback
|
|
1977
2399
|
// Read server data (plugin data sent by server)
|
|
1978
2400
|
var serverPluginData = data.readArray();
|
|
2401
|
+
data.readInt(); // p_cc_reply
|
|
1979
2402
|
|
|
1980
2403
|
// Get client response from dbCryptConfig option
|
|
1981
2404
|
var clientPluginData = parseDbCryptConfig(cnx.options.dbCryptConfig);
|
|
@@ -2055,7 +2478,7 @@ function decodeResponse(data, callback, cnx, lowercase_keys, cb) {
|
|
|
2055
2478
|
}
|
|
2056
2479
|
}
|
|
2057
2480
|
|
|
2058
|
-
function parseOpResponse(data, response, cb) {
|
|
2481
|
+
function parseOpResponse(data: any, response: any, cb?: (err?: any, response?: any) => void) {
|
|
2059
2482
|
var handle = data.readInt();
|
|
2060
2483
|
|
|
2061
2484
|
if (!response.handle) {
|
|
@@ -2072,7 +2495,7 @@ function parseOpResponse(data, response, cb) {
|
|
|
2072
2495
|
response.buffer = buf;
|
|
2073
2496
|
}
|
|
2074
2497
|
|
|
2075
|
-
var num, op, item = {};
|
|
2498
|
+
var num: any, op: any, item: any = {};
|
|
2076
2499
|
while (true) {
|
|
2077
2500
|
op = data.readInt();
|
|
2078
2501
|
|
|
@@ -2129,10 +2552,10 @@ function parseOpResponse(data, response, cb) {
|
|
|
2129
2552
|
}
|
|
2130
2553
|
}
|
|
2131
2554
|
|
|
2132
|
-
function describe(buff, statement) {
|
|
2555
|
+
function describe(buff: Buffer, statement: any) {
|
|
2133
2556
|
var br = new BlrReader(buff);
|
|
2134
|
-
var parameters = null;
|
|
2135
|
-
var type, param;
|
|
2557
|
+
var parameters: any = null;
|
|
2558
|
+
var type: any, param: any;
|
|
2136
2559
|
|
|
2137
2560
|
while (br.pos < br.buffer.length) {
|
|
2138
2561
|
switch (br.readByteCode()) {
|
|
@@ -2215,6 +2638,12 @@ function describe(buff, statement) {
|
|
|
2215
2638
|
case Const.isc_info_sql_relation:
|
|
2216
2639
|
param.relation = br.readString(Const.DEFAULT_ENCODING);
|
|
2217
2640
|
break;
|
|
2641
|
+
case Const.isc_info_sql_relation_schema:
|
|
2642
|
+
// Firebird 6.0 (Protocol 20+): schema that owns the
|
|
2643
|
+
// source relation. Empty string means the default
|
|
2644
|
+
// (PUBLIC) schema or a non-schema-aware server.
|
|
2645
|
+
param.relationSchema = br.readString(Const.DEFAULT_ENCODING);
|
|
2646
|
+
break;
|
|
2218
2647
|
case Const.isc_info_sql_owner:
|
|
2219
2648
|
param.owner = br.readString(Const.DEFAULT_ENCODING);
|
|
2220
2649
|
break;
|
|
@@ -2233,9 +2662,24 @@ function describe(buff, statement) {
|
|
|
2233
2662
|
}
|
|
2234
2663
|
}
|
|
2235
2664
|
}
|
|
2665
|
+
|
|
2666
|
+
function unpackCharSetCollation(params: any[]) {
|
|
2667
|
+
if (!params) return;
|
|
2668
|
+
for (let i = 0; i < params.length; i++) {
|
|
2669
|
+
const p = params[i];
|
|
2670
|
+
if (p && (p.type === Const.SQL_TEXT || p.type === Const.SQL_VARYING)) {
|
|
2671
|
+
if (p.subType !== undefined) {
|
|
2672
|
+
p.charSetId = p.subType & 0xFF;
|
|
2673
|
+
p.collationId = p.subType >> 8;
|
|
2674
|
+
}
|
|
2675
|
+
}
|
|
2676
|
+
}
|
|
2677
|
+
}
|
|
2678
|
+
unpackCharSetCollation(statement.input);
|
|
2679
|
+
unpackCharSetCollation(statement.output);
|
|
2236
2680
|
}
|
|
2237
2681
|
|
|
2238
|
-
function CalcBlr(blr, xsqlda) {
|
|
2682
|
+
function CalcBlr(blr: BlrWriter, xsqlda: any[]) {
|
|
2239
2683
|
blr.addBytes([Const.blr_version5, Const.blr_begin, Const.blr_message, 0]); // + message number
|
|
2240
2684
|
blr.addWord(xsqlda.length * 2);
|
|
2241
2685
|
|
|
@@ -2249,10 +2693,17 @@ function CalcBlr(blr, xsqlda) {
|
|
|
2249
2693
|
blr.addByte(Const.blr_eoc);
|
|
2250
2694
|
}
|
|
2251
2695
|
|
|
2252
|
-
function fetch_blob_async_transaction(statement, id, column, row) {
|
|
2696
|
+
function fetch_blob_async_transaction(statement: any, id: any, column: any, row: any) {
|
|
2253
2697
|
const infoValue = { row, column, value: '' };
|
|
2254
2698
|
|
|
2255
2699
|
return (transactionArg) => {
|
|
2700
|
+
const cacheKey = `${id.high}:${id.low}`;
|
|
2701
|
+
if (statement.connection._inlineBlobs && statement.connection._inlineBlobs.has(cacheKey)) {
|
|
2702
|
+
const data = statement.connection._inlineBlobs.get(cacheKey);
|
|
2703
|
+
infoValue.value = data ? data.toString(Const.DEFAULT_ENCODING) : '';
|
|
2704
|
+
return Promise.resolve(infoValue);
|
|
2705
|
+
}
|
|
2706
|
+
|
|
2256
2707
|
const singleTransaction = transactionArg === undefined;
|
|
2257
2708
|
|
|
2258
2709
|
let promiseTransaction;
|
|
@@ -2324,11 +2775,11 @@ function fetch_blob_async_transaction(statement, id, column, row) {
|
|
|
2324
2775
|
};
|
|
2325
2776
|
}
|
|
2326
2777
|
|
|
2327
|
-
function fetch_blob_async(statement, id, name, row) {
|
|
2778
|
+
function fetch_blob_async(statement: any, id: any, name: any, row: any) {
|
|
2328
2779
|
const cbTransaction = (transaction, close, callback) => {
|
|
2329
2780
|
statement.connection._pending.push('openBlob');
|
|
2330
2781
|
statement.connection.openBlob(id, transaction, (err, blob) => {
|
|
2331
|
-
let e = new Events.EventEmitter();
|
|
2782
|
+
let e: any = new Events.EventEmitter();
|
|
2332
2783
|
|
|
2333
2784
|
e.pipe = (stream) => {
|
|
2334
2785
|
e.on('data', (chunk) => {
|
|
@@ -2390,6 +2841,32 @@ function fetch_blob_async(statement, id, name, row) {
|
|
|
2390
2841
|
return (transaction, callback) => {
|
|
2391
2842
|
// callback(error, nameField, eventEmitter, row)
|
|
2392
2843
|
const singleTransaction = callback === undefined;
|
|
2844
|
+
const actualCallback = singleTransaction ? transaction : callback;
|
|
2845
|
+
|
|
2846
|
+
const cacheKey = `${id.high}:${id.low}`;
|
|
2847
|
+
if (statement.connection._inlineBlobs && statement.connection._inlineBlobs.has(cacheKey)) {
|
|
2848
|
+
const data = statement.connection._inlineBlobs.get(cacheKey);
|
|
2849
|
+
let e: any = new Events.EventEmitter();
|
|
2850
|
+
e.pipe = (stream) => {
|
|
2851
|
+
e.on('data', (chunk) => {
|
|
2852
|
+
stream.write(chunk);
|
|
2853
|
+
});
|
|
2854
|
+
e.on('end', () => {
|
|
2855
|
+
stream.end();
|
|
2856
|
+
});
|
|
2857
|
+
};
|
|
2858
|
+
|
|
2859
|
+
actualCallback(null, name, e, row);
|
|
2860
|
+
|
|
2861
|
+
setImmediate(() => {
|
|
2862
|
+
if (data) {
|
|
2863
|
+
e.emit('data', data);
|
|
2864
|
+
}
|
|
2865
|
+
e.emit('end');
|
|
2866
|
+
});
|
|
2867
|
+
return;
|
|
2868
|
+
}
|
|
2869
|
+
|
|
2393
2870
|
if (singleTransaction) {
|
|
2394
2871
|
callback = transaction;
|
|
2395
2872
|
statement.connection.startTransaction(Const.ISOLATION_READ_UNCOMMITTED, (err, transaction) => {
|
|
@@ -2405,7 +2882,7 @@ function fetch_blob_async(statement, id, name, row) {
|
|
|
2405
2882
|
};
|
|
2406
2883
|
}
|
|
2407
2884
|
|
|
2408
|
-
function doSynchronousLoop(data, processData, done) {
|
|
2885
|
+
function doSynchronousLoop(data: any[], processData: (row: any, index: number, next: (err?: any) => void) => void, done: (err?: any) => void) {
|
|
2409
2886
|
if (!data || !data.length) {
|
|
2410
2887
|
done();
|
|
2411
2888
|
return;
|
|
@@ -2430,9 +2907,9 @@ function doSynchronousLoop(data, processData, done) {
|
|
|
2430
2907
|
loop(0);
|
|
2431
2908
|
}
|
|
2432
2909
|
|
|
2433
|
-
function executeStreamRow(custom, row, index, output, next) {
|
|
2910
|
+
function executeStreamRow(custom: any, row: any, index: number, output: any, next: (err?: any) => void) {
|
|
2434
2911
|
let done = false;
|
|
2435
|
-
const finish = (err) => {
|
|
2912
|
+
const finish = (err?: any) => {
|
|
2436
2913
|
if (done) {
|
|
2437
2914
|
return;
|
|
2438
2915
|
}
|
|
@@ -2456,7 +2933,7 @@ function executeStreamRow(custom, row, index, output, next) {
|
|
|
2456
2933
|
}
|
|
2457
2934
|
}
|
|
2458
2935
|
|
|
2459
|
-
function bufferReader(buffer, max, writer, cb, beg, end) {
|
|
2936
|
+
function bufferReader(buffer: Buffer, max: number, writer: (b: Buffer, next: () => void) => void, cb: () => void, beg?: number, end?: number) {
|
|
2460
2937
|
|
|
2461
2938
|
if (!beg)
|
|
2462
2939
|
beg = 0;
|
|
@@ -2487,7 +2964,7 @@ function bufferReader(buffer, max, writer, cb, beg, end) {
|
|
|
2487
2964
|
* - plain string - encodes as UTF-8
|
|
2488
2965
|
* - undefined/null/empty - returns empty buffer
|
|
2489
2966
|
*/
|
|
2490
|
-
function parseDbCryptConfig(config) {
|
|
2967
|
+
function parseDbCryptConfig(config: string | null | undefined): Buffer {
|
|
2491
2968
|
if (!config) {
|
|
2492
2969
|
return Buffer.alloc(0);
|
|
2493
2970
|
}
|
|
@@ -2507,4 +2984,9 @@ function parseDbCryptConfig(config) {
|
|
|
2507
2984
|
return Buffer.from(config, 'utf8');
|
|
2508
2985
|
}
|
|
2509
2986
|
|
|
2510
|
-
|
|
2987
|
+
Connection.decodeResponse = decodeResponse;
|
|
2988
|
+
Connection.fetch_blob_async_transaction = fetch_blob_async_transaction;
|
|
2989
|
+
Connection.fetch_blob_async = fetch_blob_async;
|
|
2990
|
+
Connection.parseValueIfJson = parseValueIfJson;
|
|
2991
|
+
Connection.describe = describe;
|
|
2992
|
+
export = Connection;
|