node-firebird 1.1.9 → 2.0.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.
@@ -1,5 +1,4 @@
1
1
  const Events = require('events');
2
- const net = require('net');
3
2
  const os = require('os');
4
3
  const path = require('path');
5
4
  const BigInt = require('big-integer');
@@ -15,6 +14,7 @@ const Database = require('./database');
15
14
  const Statement = require('./statement');
16
15
  const Transaction = require('./transaction');
17
16
  const {lookupMessages, noop, parseDate} = require('../utils');
17
+ const Socket = require("./socket");
18
18
 
19
19
  /***************************************
20
20
  *
@@ -22,182 +22,1518 @@ const {lookupMessages, noop, parseDate} = require('../utils');
22
22
  *
23
23
  ***************************************/
24
24
 
25
- var Connection = function (host, port, callback, options, db, svc) {
26
- var self = this;
27
- this.db = db;
28
- this.svc = svc
29
- this._msg = new XdrWriter(32);
30
- this._blr = new BlrWriter(32);
31
- this._queue = [];
32
- this._detachTimeout;
33
- this._detachCallback;
34
- this._detachAuto;
35
- this._socket = net.createConnection(port, host);
36
- this._pending = [];
37
- this._isOpened = false;
38
- this._isClosed = false;
39
- this._isDetach = false;
40
- this._isUsed = false;
41
- this._pooled = options.isPool||false;
42
- this.options = options;
43
- this._bind_events(host, port, callback);
44
- this.error;
45
- this._retry_connection_id;
46
- this._retry_connection_interval = options.retryConnectionInterval || 1000;
47
- this._max_cached_query = options.maxCachedQuery || -1;
48
- this._cache_query = options.cacheQuery?{}:null;
49
- this._messageFile = options.messageFile || path.join(__dirname, 'firebird.msg');
50
- };
51
-
52
- Connection.prototype._setcachedquery = function (query, statement) {
53
- if (this._cache_query){
54
- if (this._max_cached_query === -1 || this._max_cached_query > Object.keys(this._cache_query).length){
55
- this._cache_query[query] = statement;
25
+ class Connection {
26
+ constructor(host, port, callback, options, db, svc) {
27
+ var self = this;
28
+ this.db = db;
29
+ this.svc = svc
30
+ this._msg = new XdrWriter(32);
31
+ this._blr = new BlrWriter(32);
32
+ this._queue = [];
33
+ this._detachTimeout;
34
+ this._detachCallback;
35
+ this._detachAuto;
36
+ this._socket = new Socket(port, host);
37
+ this._pending = [];
38
+ this._isOpened = false;
39
+ this._isClosed = false;
40
+ this._isDetach = false;
41
+ this._isUsed = false;
42
+ this._pooled = options.isPool||false;
43
+ if (options && options.blobChunkSize > 65535) options.blobChunkSize = 65535;
44
+ this.options = options;
45
+ this._bind_events(host, port, callback);
46
+ this.error;
47
+ this._retry_connection_id;
48
+ this._retry_connection_interval = options.retryConnectionInterval || 1000;
49
+ this._max_cached_query = options.maxCachedQuery || -1;
50
+ this._cache_query = options.cacheQuery?{}:null;
51
+ this._messageFile = options.messageFile || path.join(__dirname, 'firebird.msg');
52
+ }
53
+
54
+
55
+ _setcachedquery(query, statement) {
56
+ if (this._cache_query){
57
+ if (this._max_cached_query === -1 || this._max_cached_query > Object.keys(this._cache_query).length){
58
+ this._cache_query[query] = statement;
59
+ }
56
60
  }
61
+
62
+
63
+ }
64
+
65
+
66
+ getCachedQuery(query) {
67
+ return this._cache_query ? this._cache_query[query] : null;
57
68
  }
58
69
 
59
70
 
60
- };
71
+ _bind_events(host, port, callback) {
72
+
73
+ var self = this;
74
+
75
+ self._socket.on('close', function() {
76
+
77
+ if (!self._isOpened || self._isDetach) {
78
+ return;
79
+ }
80
+
81
+ self._isOpened = false;
82
+
83
+ if (!self.db) {
84
+ if (callback)
85
+ callback(self.error);
86
+ return;
87
+ }
88
+
89
+ self._retry_connection_id = setTimeout(function() {
90
+ self._socket.removeAllListeners();
91
+ self._socket = null;
92
+
93
+ var ctx = new Connection(host, port, function(err) {
94
+ ctx.connect(self.options, function(err) {
95
+
96
+ if (err) {
97
+ self.db.emit('error', err);
98
+ return;
99
+ }
100
+
101
+ ctx.attach(self.options, function(err) {
102
+
103
+ if (err) {
104
+ self.db.emit('error', err);
105
+ return;
106
+ }
107
+
108
+ ctx._queue = ctx._queue.concat(self._queue);
109
+ ctx._pending = ctx._pending.concat(self._pending);
110
+ self.db.emit('reconnect');
111
+
112
+ }, self.db);
113
+ });
114
+
115
+ Object.assign(self, ctx);
116
+
117
+ }, self.options, self.db);
118
+ }, self._retry_connection_interval);
119
+
120
+ });
121
+
122
+ self._socket.on('error', function(e) {
123
+
124
+ self.error = e;
125
+
126
+ if (self.db)
127
+ self.db.emit('error', e)
128
+
129
+ if (callback)
130
+ callback(e);
131
+
132
+ });
133
+
134
+ self._socket.on('connect', function() {
135
+ self._isClosed = false;
136
+ self._isOpened = true;
137
+ if (callback)
138
+ callback();
139
+ });
140
+
141
+ self._socket.on('data', function (data) {
142
+ var xdr;
143
+
144
+ if (!self._xdr) {
145
+ xdr = new XdrReader(data);
146
+ } else {
147
+ xdr = new XdrReader(Buffer.concat([self._xdr.buffer, data], self._xdr.buffer.length + data.length));
148
+ delete (self._xdr);
149
+ }
150
+
151
+ while (xdr.pos < xdr.buffer.length) {
152
+ var cb = self._queue[0], pos = xdr.pos;
153
+
154
+ decodeResponse(xdr, cb, self, self._lowercase_keys, function (err, obj) {
155
+
156
+ if (err) {
157
+ xdr.buffer = xdr.buffer.slice(pos);
158
+ xdr.pos = 0;
159
+ self._xdr = xdr;
160
+
161
+ if (self.accept.protocolMinimumType === Const.ptype_lazy_send && self._queue.length > 0) {
162
+ self._queue[0].lazy_count = 2;
163
+ }
164
+ return;
165
+ }
166
+
167
+ // remove the op flag, needed for partial packet
168
+ if (xdr.r) {
169
+ delete (xdr.r);
170
+ }
171
+
172
+ self._queue.shift();
173
+ self._pending.shift();
174
+
175
+ if (obj && obj.status) {
176
+ obj.message = lookupMessages(obj.status);
177
+ doCallback(obj, cb);
178
+ } else {
179
+ doCallback(obj, cb);
180
+ }
181
+
182
+ });
183
+
184
+ if (xdr.pos === 0) {
185
+ break;
186
+ }
187
+ }
188
+
189
+ if (!self._detachAuto || self._pending.length !== 0) {
190
+ return;
191
+ }
192
+
193
+ clearTimeout(self._detachTimeout);
194
+ self._detachTimeout = setTimeout(function () {
195
+ self.db.detach(self._detachCallback);
196
+ self._detachAuto = false;
197
+ }, 100);
198
+
199
+ });
200
+ }
201
+
202
+
203
+ disconnect() {
204
+ this._socket.end();
205
+ }
206
+
207
+
208
+
209
+
210
+
211
+ sendOpContAuth(authData, authDataEnc, pluginName) {
212
+ var msg = this._msg;
213
+ msg.pos = 0;
214
+
215
+ msg.addInt(Const.op_cont_auth);
216
+ msg.addString(authData, authDataEnc);
217
+ msg.addString(pluginName, Const.DEFAULT_ENCODING)
218
+ msg.addString(Const.AUTH_PLUGIN_LIST.join(','), Const.DEFAULT_ENCODING);
219
+ // msg.addInt(0); // p_list
220
+ msg.addInt(0); // keys
221
+
222
+ this._socket.write(msg.getData());
223
+ }
224
+
225
+
226
+ sendOpCrypt(encryptPlugin) {
227
+ var msg = this._msg;
228
+ msg.pos = 0;
229
+
230
+ msg.addInt(Const.op_crypt);
231
+ msg.addString(encryptPlugin || 'Arc4', Const.DEFAULT_ENCODING);
232
+ msg.addString('Symmetric', Const.DEFAULT_ENCODING);
233
+
234
+ this._socket.write(msg.getData());
235
+ }
236
+
237
+
238
+ sendOpCryptKeyCallback(pluginData) {
239
+ var msg = this._msg;
240
+ msg.pos = 0;
241
+
242
+ msg.addInt(Const.op_crypt_key_callback);
243
+ msg.addBlr(pluginData); // Send the callback response data as a buffer
244
+
245
+ this._socket.write(msg.getData());
246
+ }
247
+
248
+
249
+ _queueEvent(callback, defer = false) {
250
+ var self = this;
251
+
252
+ if (self._isClosed) {
253
+ if (callback)
254
+ callback(new Error('Connection is closed.'));
255
+ return;
256
+ }
257
+
258
+ const canDefer = defer && this.accept.protocolVersion >= Const.PROTOCOL_VERSION11;
259
+
260
+ self._socket.write(self._msg.getData(), canDefer);
261
+ if (canDefer && callback) {
262
+ callback();
263
+ } else {
264
+ self._queue.push(callback);
265
+ }
266
+ }
267
+
268
+
269
+ connect(options, callback) {
270
+ var pluginName = options.manager ? Const.AUTH_PLUGIN_LEGACY : options.pluginName || Const.AUTH_PLUGIN_LIST[0]; // TODO Srp for service
271
+ var msg = this._msg;
272
+ var blr = this._blr;
273
+
274
+ this._pending.push('connect');
275
+
276
+ msg.pos = 0;
277
+ blr.pos = 0;
278
+
279
+ blr.addString(Const.CNCT_login, options.user, Const.DEFAULT_ENCODING);
280
+ blr.addString(Const.CNCT_plugin_name, pluginName, Const.DEFAULT_ENCODING);
281
+ blr.addString(Const.CNCT_plugin_list, Const.AUTH_PLUGIN_LIST.join(','), Const.DEFAULT_ENCODING);
282
+
283
+ var specificData = '';
284
+ if (Const.AUTH_PLUGIN_SRP_LIST.indexOf(pluginName) > -1) {
285
+ this.clientKeys = srp.clientSeed();
286
+ specificData = this.clientKeys.public.toString(16);
287
+ blr.addMultiblockPart(Const.CNCT_specific_data, specificData, Const.DEFAULT_ENCODING);
288
+ } else if (pluginName === Const.AUTH_PLUGIN_LEGACY) {
289
+ specificData = crypt.crypt(options.password, Const.LEGACY_AUTH_SALT).substring(2);
290
+ blr.addMultiblockPart(Const.CNCT_specific_data, specificData, Const.DEFAULT_ENCODING);
291
+ } else {
292
+ doError(new Error('Invalide auth plugin \'' + pluginName + '\''), callback);
293
+ return;
294
+ }
295
+ blr.addBytes([Const.CNCT_client_crypt, 4, options.wireCrypt !== undefined ? options.wireCrypt : Const.WIRE_CRYPT_ENABLE, 0, 0, 0]);
296
+ blr.addString(Const.CNCT_user, os.userInfo().username || 'Unknown', Const.DEFAULT_ENCODING);
297
+ blr.addString(Const.CNCT_host, os.hostname(), Const.DEFAULT_ENCODING);
298
+ blr.addBytes([Const.CNCT_user_verification, 0]);
299
+
300
+ msg.addInt(Const.op_connect);
301
+ msg.addInt(Const.op_attach);
302
+ msg.addInt(Const.CONNECT_VERSION3);
303
+ msg.addInt(Const.ARCHITECTURE_GENERIC);
304
+ msg.addString(options.database || options.filename, Const.DEFAULT_ENCODING);
305
+ msg.addInt(Const.SUPPORTED_PROTOCOL.length); // Count of Protocol version understood count.
306
+ msg.addBlr(this._blr);
307
+
308
+ for (var protocol of Const.SUPPORTED_PROTOCOL) {
309
+ msg.addInt(protocol[0]); // Version
310
+ msg.addInt(protocol[1]); // Architecture
311
+ msg.addInt(protocol[2]); // Min type
312
+ if (protocol[0] >= Const.PROTOCOL_VERSION13 && options.wireCompression) {
313
+ msg.addInt(protocol[3] | Const.pflag_compress); // Max type with compress flag
314
+ } else {
315
+ msg.addInt(protocol[3]); // Max type
316
+ }
317
+ msg.addInt(protocol[4]); // Preference weight
318
+ }
319
+
320
+ var self = this;
321
+ function cb(err, ret) {
322
+ if (err) {
323
+ doError(err, callback);
324
+ return;
325
+ }
61
326
 
62
- Connection.prototype.getCachedQuery = function (query) {
63
- return this._cache_query ? this._cache_query[query] : null;
64
- };
327
+ // Check for pending accept from op_cond_accept flow
328
+ if (self._pendingAccept) {
329
+ ret = self._pendingAccept;
330
+ delete self._pendingAccept;
331
+ }
65
332
 
66
- Connection.prototype._bind_events = function(host, port, callback) {
333
+ self.accept = ret;
334
+
335
+ // Wire encryption: send op_crypt if SRP session key is available
336
+ if (ret.sessionKey && ret.protocolVersion >= Const.PROTOCOL_VERSION13 &&
337
+ options.wireCrypt !== Const.WIRE_CRYPT_DISABLE) {
338
+ var keyBuf = Buffer.from(srp.hexPad(ret.sessionKey.toString(16)), 'hex');
339
+ self.sendOpCrypt('Arc4');
340
+ self._socket.enableEncryption(keyBuf);
341
+ self._pending.push('crypt');
342
+ self._queue.push(function(cryptErr) {
343
+ if (cryptErr) {
344
+ doError(cryptErr, callback);
345
+ return;
346
+ }
347
+ if (callback)
348
+ callback(undefined, ret);
349
+ });
350
+ return;
351
+ }
67
352
 
68
- var self = this;
353
+ if (callback)
354
+ callback(undefined, ret);
355
+ }
356
+
357
+ this._queueEvent(cb);
358
+ }
69
359
 
70
- self._socket.on('close', function() {
71
360
 
72
- if (!self._isOpened || self._isDetach) {
361
+ attach(options, callback, db) {
362
+ this._lowercase_keys = options.lowercase_keys || Const.DEFAULT_LOWERCASE_KEYS;
363
+
364
+ var database = options.database || options.filename;
365
+ if (database == null || database.length === 0) {
366
+ doError(new Error('No database specified'), callback);
73
367
  return;
74
368
  }
369
+
370
+ var user = options.user || Const.DEFAULT_USER;
371
+ var password = options.password || Const.DEFAULT_PASSWORD;
372
+ var role = options.role;
373
+ var self = this;
374
+ var msg = this._msg;
375
+ var blr = this._blr;
376
+ msg.pos = 0;
377
+ blr.pos = 0;
378
+
379
+ blr.addByte(Const.isc_dpb_version1);
380
+ blr.addString(Const.isc_dpb_lc_ctype, options.encoding || 'UTF8', Const.DEFAULT_ENCODING);
381
+
382
+ // For Firebird 3+ (protocol 13+), add UTF-8 filename flag to ensure all DPB strings are handled with UTF-8
383
+ if (this.accept.protocolVersion >= Const.PROTOCOL_VERSION13) {
384
+ blr.addByte(Const.isc_dpb_utf8_filename);
385
+ blr.addByte(0);
386
+ }
387
+
388
+ blr.addString(Const.isc_dpb_user_name, user, Const.DEFAULT_ENCODING);
389
+ if (options.password && !this.accept.authData) {
390
+ if (this.accept.protocolVersion < Const.PROTOCOL_VERSION13) {
391
+ if (this.accept.protocolVersion === Const.PROTOCOL_VERSION10) {
392
+ blr.addString(Const.isc_dpb_password, password, Const.DEFAULT_ENCODING);
393
+ } else {
394
+ blr.addString(Const.isc_dpb_password_enc, crypt.crypt(password, Const.LEGACY_AUTH_SALT).substring(2), Const.DEFAULT_ENCODING);
395
+ }
396
+ }
397
+ }
398
+
399
+ if (role)
400
+ blr.addString(Const.isc_dpb_sql_role_name, role, Const.DEFAULT_ENCODING);
401
+
402
+ blr.addBytes([Const.isc_dpb_process_id, 4]);
403
+ blr.addInt32(process.pid);
404
+
405
+ let processName = process.title || "";
406
+ blr.addString(Const.isc_dpb_process_name, processName.length > 255 ? processName.substring(processName.length - 255, processName.length) : processName, Const.DEFAULT_ENCODING);
407
+
408
+ if (this.accept.authData) {
409
+ blr.addString(Const.isc_dpb_specific_auth_data, this.accept.authData, Const.DEFAULT_ENCODING);
410
+ }
411
+
412
+ msg.addInt(Const.op_attach);
413
+ msg.addInt(0); // Database Object ID
414
+ msg.addString(database, Const.DEFAULT_ENCODING);
415
+ msg.addBlr(this._blr);
416
+
417
+ function cb(err, ret) {
418
+ if (err) {
419
+ doError(err, callback);
420
+ return;
421
+ }
422
+
423
+ self.dbhandle = ret.handle;
424
+ if (callback)
425
+ callback(undefined, ret);
426
+ }
427
+
428
+ // For reconnect
429
+ if (db) {
430
+ db.connection = this;
431
+ cb.response = db;
432
+ } else {
433
+ cb.response = new Database(this);
434
+ cb.response.removeAllListeners('error');
435
+ cb.response.on('error', noop);
436
+ }
437
+
438
+ this._queueEvent(cb);
439
+ }
75
440
 
76
- self._isOpened = false;
77
441
 
78
- if (!self.db) {
442
+ detach(callback) {
443
+
444
+ var self = this;
445
+
446
+ if (self._isClosed)
447
+ return;
448
+
449
+ self._isUsed = false;
450
+ self._isDetach = true;
451
+
452
+ var msg = self._msg;
453
+
454
+ msg.pos = 0;
455
+ msg.addInt(Const.op_detach);
456
+ msg.addInt(0); // Database Object ID
457
+
458
+ self._queueEvent(function(err, ret) {
459
+ clearTimeout(self._retry_connection_id);
460
+ delete(self.dbhandle);
79
461
  if (callback)
80
- callback(self.error);
462
+ callback(err, ret);
463
+ });
464
+ }
465
+
466
+
467
+ createDatabase(options, callback) {
468
+ var database = options.database || options.filename;
469
+ if (database == null || database.length === 0) {
470
+ doError(new Error('No database specified'), callback);
81
471
  return;
82
472
  }
473
+
474
+ var user = options.user || Const.DEFAULT_USER;
475
+ var password = options.password || Const.DEFAULT_PASSWORD;
476
+ var pageSize = options.pageSize || Const.DEFAULT_PAGE_SIZE;
477
+ var role = options.role;
478
+ var blr = this._blr;
479
+
480
+ blr.pos = 0;
481
+ blr.addByte(Const.isc_dpb_version1);
482
+ blr.addString(Const.isc_dpb_set_db_charset, 'UTF8', Const.DEFAULT_ENCODING);
483
+ blr.addString(Const.isc_dpb_lc_ctype, 'UTF8', Const.DEFAULT_ENCODING);
484
+
485
+ // For Firebird 3+ (protocol 13+), add UTF-8 filename flag to ensure all DPB strings are handled with UTF-8
486
+ if (this.accept.protocolVersion >= Const.PROTOCOL_VERSION13) {
487
+ blr.addByte(Const.isc_dpb_utf8_filename);
488
+ blr.addByte(0);
489
+ }
490
+
491
+ blr.addString(Const.isc_dpb_user_name, user, Const.DEFAULT_ENCODING);
492
+ if (this.accept.protocolVersion < Const.PROTOCOL_VERSION13) {
493
+ if (this.accept.protocolVersion === Const.PROTOCOL_VERSION10) {
494
+ blr.addString(Const.isc_dpb_password, password, Const.DEFAULT_ENCODING);
495
+ } else {
496
+ blr.addString(Const.isc_dpb_password_enc, crypt.crypt(password, Const.LEGACY_AUTH_SALT).substring(2), Const.DEFAULT_ENCODING);
497
+ }
498
+ }
499
+ if (role)
500
+ blr.addString(Const.isc_dpb_sql_role_name, role, Const.DEFAULT_ENCODING);
501
+
502
+ blr.addBytes([Const.isc_dpb_process_id, 4]);
503
+ blr.addInt32(process.pid);
504
+
505
+ let processName = process.title || "";
506
+ blr.addString(Const.isc_dpb_process_name, processName.length > 255 ? processName.substring(processName.length - 255, processName.length) : processName, Const.DEFAULT_ENCODING);
507
+
508
+ if (this.accept.authData) {
509
+ blr.addString(Const.isc_dpb_specific_auth_data, this.accept.authData, Const.DEFAULT_ENCODING);
510
+ }
511
+
512
+ blr.addNumeric(Const.isc_dpb_sql_dialect, 3);
513
+ blr.addNumeric(Const.isc_dpb_force_write, 1);
514
+ blr.addNumeric(Const.isc_dpb_overwrite, 1);
515
+ blr.addNumeric(Const.isc_dpb_page_size, pageSize);
516
+
517
+ var msg = this._msg;
518
+ msg.pos = 0;
519
+ msg.addInt(Const.op_create); // op_create
520
+ msg.addInt(0); // Database Object ID
521
+ msg.addString(database, Const.DEFAULT_ENCODING);
522
+ msg.addBlr(blr);
523
+
524
+ var self = this;
525
+
526
+ function cb(err, ret) {
527
+
528
+ if (ret)
529
+ self.dbhandle = ret.handle;
530
+
531
+ setImmediate(function() {
532
+ if (self.db)
533
+ self.db.emit('attach', ret);
534
+ });
535
+
536
+ if (callback)
537
+ callback(err, ret);
538
+ }
539
+
540
+ cb.response = new Database(this);
541
+ this._queueEvent(cb);
542
+ }
543
+
83
544
 
84
- self._retry_connection_id = setTimeout(function() {
85
- self._socket.removeAllListeners();
86
- self._socket = null;
545
+ dropDatabase(callback) {
546
+ var msg = this._msg;
547
+ msg.pos = 0;
548
+
549
+ msg.addInt(Const.op_drop_database);
550
+ msg.addInt(this.dbhandle);
551
+
552
+ var self = this;
553
+ this._queueEvent(function(err) {
554
+ self.detach(function() {
555
+ self.disconnect();
556
+
557
+ if (callback)
558
+ callback(err);
559
+ });
560
+ });
561
+ }
87
562
 
88
- var ctx = new Connection(host, port, function(err) {
89
- ctx.connect(self.options, function(err) {
90
563
 
91
- if (err) {
92
- self.db.emit('error', err);
564
+ throwClosed(callback) {
565
+ var err = new Error('Connection is closed.');
566
+ this.db.emit('error', err);
567
+ if (callback)
568
+ callback(err);
569
+ return this;
570
+ }
571
+
572
+
573
+ startTransaction(options, callback) {
574
+
575
+ if (typeof(options) === 'function') {
576
+ var tmp = options;
577
+ options = callback;
578
+ callback = tmp;
579
+ }
580
+
581
+ // Compatibility
582
+ if (Array.isArray(options)) {
583
+ options = {
584
+ isolation: options,
585
+ readOnly: (options === Const.ISOLATION_READ_COMMITTED_READ_ONLY),
586
+ };
587
+ }
588
+
589
+ // Default options
590
+ options = Object.assign({
591
+ autoCommit: false,
592
+ autoUndo: true,
593
+ isolation: Const.ISOLATION_READ_COMMITTED,
594
+ ignoreLimbo: false,
595
+ //lock: [],
596
+ readOnly: false,
597
+ wait: true,
598
+ waitTimeout: 0,
599
+ }, options);
600
+
601
+ if (this._isClosed)
602
+ return this.throwClosed(callback);
603
+
604
+ // for auto detach
605
+ this._pending.push('startTransaction');
606
+
607
+ var blr = this._blr;
608
+ var msg = this._msg;
609
+
610
+ blr.pos = 0;
611
+ msg.pos = 0;
612
+
613
+ blr.addByte(Const.isc_tpb_version3);
614
+ blr.addBytes(options.isolation);
615
+ blr.addByte(options.readOnly ? Const.isc_tpb_read : Const.isc_tpb_write);
616
+ if (options.wait) {
617
+ blr.addByte(Const.isc_tpb_wait);
618
+
619
+ if (options.waitTimeout) {
620
+ blr.addNumeric(Const.isc_tpb_lock_timeout, options.waitTimeout);
621
+ }
622
+ } else {
623
+ blr.addByte(Const.isc_tpb_nowait);
624
+ }
625
+ if (!options.autoUndo) {
626
+ blr.addByte(Const.isc_tpb_no_auto_undo);
627
+ }
628
+ if (options.autoCommit) {
629
+ blr.addByte(Const.isc_tpb_autocommit);
630
+ }
631
+ if (options.ignoreLimbo) {
632
+ blr.addByte(Const.isc_tpb_ignore_limbo);
633
+ }
634
+ // TODO
635
+ /*if (options.lock.length) {
636
+ for (let table of options.lock) {
637
+ const lockMode = table.write ? Const.isc_tpb_lock_write : Const.isc_tpb_lock_read;
638
+ const lockType = table.protected ? Const.isc_tpb_protected : Const.isc_tpb_shared;
639
+
640
+ blr.addString(lockMode, table.table || table, Const.DEFAULT_ENCODING);
641
+ blr.addByte(lockType);
642
+ }
643
+ }*/
644
+
645
+ msg.addInt(Const.op_transaction);
646
+ msg.addInt(this.dbhandle);
647
+ msg.addBlr(blr);
648
+ callback.response = new Transaction(this);
649
+
650
+ this.db.emit('transaction', options);
651
+ this._queueEvent(callback);
652
+ }
653
+
654
+
655
+ commit(transaction, callback) {
656
+
657
+ if (this._isClosed)
658
+ return this.throwClosed(callback);
659
+
660
+ // for auto detach
661
+ this._pending.push('commit');
662
+
663
+ var msg = this._msg;
664
+ msg.pos = 0;
665
+ msg.addInt(Const.op_commit);
666
+ msg.addInt(transaction.handle);
667
+ this.db.emit('commit');
668
+ this._queueEvent(callback);
669
+ }
670
+
671
+
672
+ rollback(transaction, callback) {
673
+
674
+ if (this._isClosed)
675
+ return this.throwClosed(callback);
676
+
677
+ // for auto detach
678
+ this._pending.push('rollback');
679
+
680
+ var msg = this._msg;
681
+ msg.pos = 0;
682
+ msg.addInt(Const.op_rollback);
683
+ msg.addInt(transaction.handle);
684
+ this.db.emit('rollback');
685
+ this._queueEvent(callback);
686
+ }
687
+
688
+
689
+ commitRetaining(transaction, callback) {
690
+
691
+ if (this._isClosed)
692
+ throw new Error('Connection is closed.');
693
+
694
+ // for auto detach
695
+ this._pending.push('commitRetaining');
696
+
697
+ var msg = this._msg;
698
+ msg.pos = 0;
699
+ msg.addInt(Const.op_commit_retaining);
700
+ msg.addInt(transaction.handle);
701
+ this._queueEvent(callback);
702
+ }
703
+
704
+
705
+ rollbackRetaining(transaction, callback) {
706
+
707
+ if (this._isClosed)
708
+ return this.throwClosed(callback);
709
+
710
+ // for auto detach
711
+ this._pending.push('rollbackRetaining');
712
+
713
+ var msg = this._msg;
714
+ msg.pos = 0;
715
+ msg.addInt(Const.op_rollback_retaining);
716
+ msg.addInt(transaction.handle);
717
+ this._queueEvent(callback);
718
+ }
719
+
720
+
721
+ allocateStatement(callback) {
722
+
723
+ if (this._isClosed)
724
+ return this.throwClosed(callback);
725
+
726
+ // for auto detach
727
+ this._pending.push('allocateStatement');
728
+
729
+ var msg = this._msg;
730
+ msg.pos = 0;
731
+ msg.addInt(Const.op_allocate_statement);
732
+ msg.addInt(this.dbhandle);
733
+ callback.response = new Statement(this);
734
+ this._queueEvent(callback);
735
+ }
736
+
737
+
738
+ dropStatement(statement, callback) {
739
+
740
+ if (this._isClosed)
741
+ return this.throwClosed(callback);
742
+
743
+ // for auto detach
744
+ this._pending.push('dropStatement');
745
+
746
+ var msg = this._msg;
747
+ msg.pos = 0;
748
+ msg.addInt(Const.op_free_statement);
749
+ msg.addInt(statement.handle);
750
+ msg.addInt(Const.DSQL_drop);
751
+
752
+ this._queueEvent(callback, true);
753
+ }
754
+
755
+
756
+ closeStatement(statement, callback) {
757
+
758
+ if (this._isClosed)
759
+ return this.throwClosed(callback);
760
+
761
+ // for auto detach
762
+ this._pending.push('closeStatement');
763
+
764
+ var msg = this._msg;
765
+ msg.pos = 0;
766
+ msg.addInt(Const.op_free_statement);
767
+ msg.addInt(statement.handle);
768
+ msg.addInt(Const.DSQL_close);
769
+
770
+ this._queueEvent(callback, true);
771
+ }
772
+
773
+
774
+ allocateAndPrepareStatement(transaction, query, plan, callback) {
775
+ var self = this;
776
+ var mainCallback = function(err, ret) {
777
+ if (!err) {
778
+ mainCallback.response.handle = ret.handle;
779
+ describe(ret.buffer, mainCallback.response);
780
+ mainCallback.response.query = query;
781
+ self.db.emit('query', query);
782
+ ret = mainCallback.response;
783
+ self._setcachedquery(query, ret);
784
+ }
785
+
786
+ if (callback)
787
+ callback(err, ret);
788
+ };
789
+
790
+ // for auto detach
791
+ this._pending.push('allocateAndPrepareStatement');
792
+
793
+ var msg = this._msg;
794
+ var blr = this._blr;
795
+
796
+ msg.pos = 0;
797
+ blr.pos = 0;
798
+
799
+ msg.addInt(Const.op_allocate_statement);
800
+ msg.addInt(this.dbhandle);
801
+ mainCallback.lazy_count = 1;
802
+
803
+ blr.addBytes(Const.DESCRIBE);
804
+ if (plan)
805
+ blr.addByte(Const.isc_info_sql_get_plan);
806
+
807
+ msg.addInt(Const.op_prepare_statement);
808
+ msg.addInt(transaction.handle);
809
+ msg.addInt(0xFFFF);
810
+ msg.addInt(3); // dialect = 3
811
+ msg.addString(query, Const.DEFAULT_ENCODING);
812
+ msg.addBlr(blr);
813
+ msg.addInt(65535); // buffer_length
814
+ mainCallback.lazy_count += 1;
815
+
816
+ mainCallback.response = new Statement(this);
817
+ this._queueEvent(mainCallback);
818
+ }
819
+
820
+
821
+ prepare(transaction, query, plan, callback) {
822
+ var self = this;
823
+
824
+ if (this.accept.protocolMinimumType === Const.ptype_lazy_send) { // V11 Statement or higher
825
+ self.allocateAndPrepareStatement(transaction, query, plan, callback);
826
+ } else { // V10 Statement
827
+ self.allocateStatement(function (err, statement) {
828
+ if (err) {
829
+ doError(err, callback);
830
+ return;
831
+ }
832
+
833
+ self.prepareStatement(transaction, statement, query, plan, callback);
834
+ });
835
+ }
836
+ }
837
+
838
+
839
+
840
+ prepareStatement(transaction, statement, query, plan, callback) {
841
+
842
+ if (this._isClosed)
843
+ return this.throwClosed(callback);
844
+
845
+ var msg = this._msg;
846
+ var blr = this._blr;
847
+
848
+ msg.pos = 0;
849
+ blr.pos = 0;
850
+
851
+ if (plan instanceof Function) {
852
+ callback = plan;
853
+ plan = false;
854
+ }
855
+
856
+ blr.addBytes(Const.DESCRIBE);
857
+
858
+ if (plan)
859
+ blr.addByte(Const.isc_info_sql_get_plan);
860
+
861
+ msg.addInt(Const.op_prepare_statement);
862
+ msg.addInt(transaction.handle);
863
+ msg.addInt(statement.handle);
864
+ msg.addInt(3); // dialect = 3
865
+ msg.addString(query, Const.DEFAULT_ENCODING);
866
+ msg.addBlr(blr);
867
+ msg.addInt(65535); // buffer_length
868
+
869
+ var self = this;
870
+ this._queueEvent(function(err, ret) {
871
+
872
+ if (!err) {
873
+ describe(ret.buffer, statement);
874
+ statement.query = query;
875
+ self.db.emit('query', query);
876
+ ret = statement;
877
+ self._setcachedquery(query, ret);
878
+ }
879
+
880
+ if (callback)
881
+ callback(err, ret);
882
+ });
883
+
884
+ }
885
+
886
+
887
+
888
+ executeStatement(transaction, statement, params, callback, custom) {
889
+
890
+ if (this._isClosed)
891
+ return this.throwClosed(callback);
892
+
893
+ // for auto detach
894
+ this._pending.push('executeStatement');
895
+
896
+ if (params instanceof Function) {
897
+ callback = params;
898
+ params = undefined;
899
+ }
900
+
901
+ var self = this;
902
+
903
+ var op = Const.op_execute;
904
+ if (
905
+ this.accept.protocolVersion >= Const.PROTOCOL_VERSION13 &&
906
+ statement.type === Const.isc_info_sql_stmt_exec_procedure &&
907
+ statement.output.length
908
+ ) {
909
+ op = Const.op_execute2;
910
+ }
911
+
912
+ function PrepareParams(params, input, callback) {
913
+
914
+ var value, meta;
915
+ var ret = new Array(params.length);
916
+ var wait = params.length;
917
+
918
+ function done() {
919
+ wait--;
920
+ if (wait === 0)
921
+ callback(ret);
922
+ }
923
+
924
+ function putBlobData(index, value, callback) {
925
+
926
+ self.createBlob2(transaction, function(err, blob) {
927
+
928
+ var b;
929
+ var isStream = value.readable;
930
+
931
+ if (Buffer.isBuffer(value))
932
+ b = value;
933
+ else if (typeof(value) === 'string')
934
+ b = Buffer.from(value, Const.DEFAULT_ENCODING);
935
+ else if (!isStream)
936
+ b = Buffer.from(JSON.stringify(value), Const.DEFAULT_ENCODING);
937
+
938
+ // Use configured transfer size or default to 1024
939
+ var chunkSize = self.options.blobChunkSize || 1024;
940
+
941
+ if (Buffer.isBuffer(b)) {
942
+ bufferReader(b, chunkSize, function (b, next) {
943
+ self.batchSegments(blob, b, next);
944
+ }, function() {
945
+ ret[index] = new Xsql.SQLParamQuad(blob.oid);
946
+ self.closeBlob(blob, callback);
947
+ });
93
948
  return;
94
949
  }
950
+
951
+ var isReading = false;
952
+ var isEnd = false;
953
+
954
+ value.on('data', function(chunk) {
955
+ // Optimization: If chunk is smaller than transfer size, send directly
956
+ if (chunk.length <= chunkSize) {
957
+ self.batchSegments(blob, chunk, function () {
958
+ if (isEnd && !isReading) {
959
+ ret[index] = new Xsql.SQLParamQuad(blob.oid);
960
+ self.closeBlob(blob, callback);
961
+ }
962
+ });
963
+ return;
964
+ }
965
+
966
+ value.pause();
967
+ isReading = true;
968
+ bufferReader(chunk, chunkSize, function (b, next) {
969
+ self.batchSegments(blob, b, next);
970
+ }, function() {
971
+ isReading = false;
972
+
973
+ if (isEnd) {
974
+ ret[index] = new Xsql.SQLParamQuad(blob.oid);
975
+ self.closeBlob(blob, callback);
976
+ } else
977
+ value.resume();
978
+ });
979
+ });
980
+
981
+ value.on('end', function() {
982
+ isEnd = true;
983
+ if (isReading)
984
+ return;
985
+ // If we are not currently reading (paused), close immediately
986
+ // If we are reading, the callback in batchSegments/bufferReader will handle closure
987
+ if (!isReading) {
988
+ ret[index] = new Xsql.SQLParamQuad(blob.oid);
989
+ self.closeBlob(blob, callback);
990
+ }
991
+ });
992
+ });
993
+ }
994
+
995
+ for (var i = 0, length = params.length; i < length; i++) {
996
+ value = params[i];
997
+ meta = input[i];
998
+
999
+ if (value === null || value === undefined) {
1000
+ switch (meta.type) {
1001
+ case Const.SQL_VARYING:
1002
+ case Const.SQL_NULL:
1003
+ case Const.SQL_TEXT:
1004
+ ret[i] = new Xsql.SQLParamString(null);
1005
+ break;
1006
+ case Const.SQL_DOUBLE:
1007
+ case Const.SQL_FLOAT:
1008
+ case Const.SQL_D_FLOAT:
1009
+ ret[i] = new Xsql.SQLParamDouble(null);
1010
+ break;
1011
+ case Const.SQL_TYPE_DATE:
1012
+ case Const.SQL_TYPE_TIME:
1013
+ case Const.SQL_TIMESTAMP:
1014
+ ret[i] = new Xsql.SQLParamDate(null);
1015
+ break;
1016
+ case Const.SQL_BLOB:
1017
+ case Const.SQL_ARRAY:
1018
+ case Const.SQL_QUAD:
1019
+ ret[i] = new Xsql.SQLParamQuad(null);
1020
+ break;
1021
+ case Const.SQL_LONG:
1022
+ case Const.SQL_SHORT:
1023
+ case Const.SQL_INT64:
1024
+ case Const.SQL_BOOLEAN:
1025
+ ret[i] = new Xsql.SQLParamInt(null);
1026
+ break;
1027
+ default:
1028
+ ret[i] = null;
1029
+ }
1030
+ done();
1031
+ } else {
1032
+ switch (meta.type) {
1033
+ case Const.SQL_BLOB:
1034
+ putBlobData(i, value, done);
1035
+ break;
1036
+
1037
+ case Const.SQL_TIMESTAMP:
1038
+ case Const.SQL_TYPE_DATE:
1039
+ case Const.SQL_TYPE_TIME:
1040
+
1041
+ if (value instanceof Date)
1042
+ ret[i] = new Xsql.SQLParamDate(value);
1043
+ else if (typeof(value) === 'string')
1044
+ ret[i] = new Xsql.SQLParamDate(parseDate(value));
1045
+ else
1046
+ ret[i] = new Xsql.SQLParamDate(new Date(value));
1047
+
1048
+ done();
1049
+ break;
1050
+
1051
+ default:
1052
+ switch (typeof value) {
1053
+ case 'bigint':
1054
+ ret[i] = new Xsql.SQLParamInt128(value);
1055
+ break;
1056
+ case 'number':
1057
+ if (value % 1 === 0) {
1058
+ if (value >= Const.MIN_INT && value <= Const.MAX_INT)
1059
+ ret[i] = new Xsql.SQLParamInt(value);
1060
+ else
1061
+ ret[i] = new Xsql.SQLParamInt64(value);
1062
+ } else
1063
+ ret[i] = new Xsql.SQLParamDouble(value);
1064
+ break;
1065
+ case 'string':
1066
+ ret[i] = new Xsql.SQLParamString(value);
1067
+ break;
1068
+ case 'boolean':
1069
+ ret[i] = new Xsql.SQLParamBool(value);
1070
+ break;
1071
+ default:
1072
+ //throw new Error('Unexpected parametter: ' + JSON.stringify(params) + ' - ' + JSON.stringify(input));
1073
+ ret[i] = new Xsql.SQLParamString(value.toString());
1074
+ break;
1075
+ }
1076
+ done();
1077
+ }
1078
+ }
1079
+ }
1080
+ }
1081
+
1082
+ var input = statement.input;
1083
+
1084
+ if (input.length) {
1085
+
1086
+ if (!(params instanceof Array)) {
1087
+ if (params !== undefined)
1088
+ params = [params];
1089
+ else
1090
+ params = [];
1091
+ }
1092
+
1093
+ if (params.length !== input.length) {
1094
+ self._pending.pop();
1095
+ callback(new Error('Expected parameters: (params=' + params.length + ' vs. expected=' + input.length + ') - ' + statement.query));
1096
+ return;
1097
+ }
1098
+
1099
+ PrepareParams(params, input, function(prms) {
1100
+ self.sendExecute(op, statement, transaction, callback, prms);
1101
+ });
1102
+
1103
+ return;
1104
+ }
1105
+
1106
+ this.sendExecute(op, statement, transaction, callback);
1107
+ }
95
1108
 
96
- ctx.attach(self.options, function(err) {
97
1109
 
98
- if (err) {
99
- self.db.emit('error', err);
1110
+ sendExecute(op, statement, transaction, callback, parameters) {
1111
+ var msg = this._msg;
1112
+ var blr = this._blr;
1113
+ msg.pos = 0;
1114
+ blr.pos = 0;
1115
+
1116
+ msg.addInt(op);
1117
+ msg.addInt(statement.handle);
1118
+ msg.addInt(transaction.handle);
1119
+
1120
+ if (parameters && parameters.length) {
1121
+ CalcBlr(blr, parameters);
1122
+ msg.addBlr(blr); // params blr
1123
+ msg.addInt(0); // message number
1124
+ msg.addInt(1); // param count
1125
+
1126
+ if (this.accept.protocolVersion >= Const.PROTOCOL_VERSION13) {
1127
+ // start with null indicator bitmap
1128
+ var nullBits = new BitSet();
1129
+
1130
+ for (var i = 0; i < parameters.length; i++) {
1131
+ nullBits.set(i, (parameters[i].value === null) & 1);
1132
+ }
1133
+
1134
+ var nullBuffer = nullBits.toBuffer();
1135
+ var requireBytes = Math.floor((parameters.length + 7) / 8);
1136
+ var remainingBytes = requireBytes - nullBuffer.length;
1137
+
1138
+ if (nullBuffer.length) {
1139
+ msg.addBuffer(nullBuffer);
1140
+ }
1141
+ if (remainingBytes > 0) {
1142
+ msg.addBuffer(Buffer.alloc(remainingBytes));
1143
+ }
1144
+ msg.addAlignment(requireBytes);
1145
+
1146
+ for(var i = 0; i < parameters.length; i++) {
1147
+ if (parameters[i].value !== null) {
1148
+ parameters[i].encode(msg);
1149
+ }
1150
+ }
1151
+ } else {
1152
+ for(var i = 0; i < parameters.length; i++) {
1153
+ parameters[i].encode(msg);
1154
+ if (parameters[i].value !== null) {
1155
+ msg.addInt(0);
1156
+ }
1157
+ }
1158
+ }
1159
+ } else {
1160
+ msg.addBlr(blr); // empty
1161
+ msg.addInt(0); // message number
1162
+ msg.addInt(0); // param count
1163
+ }
1164
+
1165
+ if (op === Const.op_execute2) {
1166
+ var outputBlr = new BlrWriter(32);
1167
+
1168
+ if (statement.output && statement.output.length) {
1169
+ CalcBlr(outputBlr, statement.output);
1170
+ msg.addBlr(outputBlr);
1171
+ } else {
1172
+ msg.addBlr(outputBlr); // empty
1173
+ }
1174
+ msg.addInt(0); // out_message_number = out_message_type
1175
+ }
1176
+
1177
+ callback.statement = statement;
1178
+ this._queueEvent(callback);
1179
+ }
1180
+
1181
+
1182
+
1183
+
1184
+ fetch(statement, transaction, count, callback) {
1185
+
1186
+ var msg = this._msg;
1187
+ var blr = this._blr;
1188
+
1189
+ msg.pos = 0;
1190
+ blr.pos = 0;
1191
+
1192
+ if (count instanceof Function) {
1193
+ callback = count;
1194
+ count = Const.DEFAULT_FETCHSIZE;
1195
+ }
1196
+
1197
+ msg.addInt(Const.op_fetch);
1198
+ msg.addInt(statement.handle);
1199
+ CalcBlr(blr, statement.output);
1200
+ msg.addBlr(blr);
1201
+ msg.addInt(0); // message number
1202
+ msg.addInt(count || Const.DEFAULT_FETCHSIZE); // fetch count
1203
+
1204
+ callback.statement = statement;
1205
+ this._queueEvent(callback);
1206
+ }
1207
+
1208
+
1209
+ fetchAll(statement, transaction, callback) {
1210
+ const self = this;
1211
+ const data = [];
1212
+ const loop = (err, ret) => {
1213
+ if (err) {
1214
+ callback(err);
1215
+ return;
1216
+ }
1217
+
1218
+ if (ret && ret.data && ret.data.length) {
1219
+ const arrPromise = (ret.arrBlob || []).map((value) => value(transaction));
1220
+
1221
+ Promise.all(arrPromise).then((arrBlob) => {
1222
+ for (let i = 0; i < arrBlob.length; i++) {
1223
+ const blob = arrBlob[i];
1224
+ ret.data[blob.row][blob.column] = blob.value;
1225
+ }
1226
+
1227
+ const custom = statement.custom || {};
1228
+ const asStream = custom.asStream && custom.on;
1229
+
1230
+ doSynchronousLoop(ret.data, (row, i, next) => {
1231
+ const pos = data.push(row) - 1;
1232
+ if (asStream) {
1233
+ executeStreamRow(custom, row, pos, statement.output, next);
1234
+ } else {
1235
+ next();
1236
+ }
1237
+ }, (streamErr) => {
1238
+ if (streamErr) {
1239
+ callback(streamErr);
100
1240
  return;
101
1241
  }
102
1242
 
103
- ctx._queue = ctx._queue.concat(self._queue);
104
- ctx._pending = ctx._pending.concat(self._pending);
105
- self.db.emit('reconnect');
1243
+ if (ret.fetched) {
1244
+ callback(undefined, data);
1245
+ } else {
1246
+ self.fetch(statement, transaction, Const.DEFAULT_FETCHSIZE, loop);
1247
+ }
1248
+ });
1249
+ }).catch(callback);
1250
+ return;
1251
+ }
106
1252
 
107
- }, self.db);
108
- });
1253
+ if (ret && ret.fetched) {
1254
+ callback(undefined, data);
1255
+ } else {
1256
+ self.fetch(statement, transaction, Const.DEFAULT_FETCHSIZE, loop);
1257
+ }
1258
+ };
109
1259
 
110
- Object.assign(self, ctx);
1260
+ this.fetch(statement, transaction, Const.DEFAULT_FETCHSIZE, loop);
1261
+ }
111
1262
 
112
- }, self.options, self.db);
113
- }, self._retry_connection_interval);
114
1263
 
115
- });
116
1264
 
117
- self._socket.on('error', function(e) {
1265
+ openBlob(blob, transaction, callback) {
1266
+ var msg = this._msg;
1267
+ msg.pos = 0;
1268
+ msg.addInt(Const.op_open_blob);
1269
+ msg.addInt(transaction.handle);
1270
+ msg.addQuad(blob);
1271
+ this._queueEvent(callback);
1272
+ }
1273
+
118
1274
 
119
- self.error = e;
1275
+ closeBlob(blob, callback) {
1276
+ var msg = this._msg;
1277
+ msg.pos = 0;
1278
+ msg.addInt(Const.op_close_blob);
1279
+ msg.addInt(blob.handle);
1280
+ this._queueEvent(callback, true);
1281
+ }
120
1282
 
121
- if (self.db)
122
- self.db.emit('error', e)
123
1283
 
124
- if (callback)
125
- callback(e);
1284
+ getSegment(blob, callback) {
1285
+ var msg = this._msg;
1286
+ msg.pos = 0;
1287
+ msg.addInt(Const.op_get_segment);
1288
+ msg.addInt(blob.handle);
1289
+ msg.addInt(1024); // buffer length
1290
+ msg.addInt(0); // ???
1291
+ this._queueEvent(callback);
1292
+ }
126
1293
 
127
- });
128
1294
 
129
- self._socket.on('connect', function() {
130
- self._isClosed = false;
131
- self._isOpened = true;
132
- if (callback)
133
- callback();
134
- });
1295
+ createBlob2(transaction, callback) {
1296
+ var msg = this._msg;
1297
+ msg.pos = 0;
1298
+ msg.addInt(Const.op_create_blob2);
1299
+ msg.addInt(0);
1300
+ msg.addInt(transaction.handle);
1301
+ msg.addInt(0);
1302
+ msg.addInt(0);
1303
+ this._queueEvent(callback);
1304
+ }
135
1305
 
136
- self._socket.on('data', function (data) {
137
- var xdr;
138
1306
 
139
- if (!self._xdr) {
140
- xdr = new XdrReader(data);
1307
+ batchSegments(blob, buffer, callback) {
1308
+ var msg = this._msg;
1309
+ var blr = this._blr;
1310
+ msg.pos = 0;
1311
+ blr.pos = 0;
1312
+ msg.addInt(Const.op_batch_segments);
1313
+ msg.addInt(blob.handle);
1314
+ msg.addInt(buffer.length + 2);
1315
+ blr.addBuffer(buffer);
1316
+ msg.addBlr(blr);
1317
+ this._queueEvent(callback);
1318
+ }
1319
+
1320
+
1321
+ svcattach(options, callback, svc) {
1322
+ this._lowercase_keys = options.lowercase_keys || Const.DEFAULT_LOWERCASE_KEYS;
1323
+ var database = options.database || options.filename;
1324
+ var user = options.user || Const.DEFAULT_USER;
1325
+ var password = options.password || Const.DEFAULT_PASSWORD;
1326
+ var role = options.role;
1327
+ var msg = this._msg;
1328
+ var blr = this._blr;
1329
+ msg.pos = 0;
1330
+ blr.pos = 0;
1331
+
1332
+ blr.addBytes([Const.isc_dpb_version2, Const.isc_dpb_version2]);
1333
+ blr.addString(Const.isc_dpb_lc_ctype, 'UTF8', Const.DEFAULT_ENCODING);
1334
+
1335
+ // For Firebird 3+ (protocol 13+), add UTF-8 filename flag to ensure all DPB strings are handled with UTF-8
1336
+ if (this.accept.protocolVersion >= Const.PROTOCOL_VERSION13) {
1337
+ blr.addByte(Const.isc_dpb_utf8_filename);
1338
+ blr.addByte(0);
1339
+ }
1340
+
1341
+ blr.addString(Const.isc_dpb_user_name, user, Const.DEFAULT_ENCODING);
1342
+ blr.addString(Const.isc_dpb_password, password, Const.DEFAULT_ENCODING);
1343
+ blr.addByte(Const.isc_dpb_dummy_packet_interval);
1344
+ blr.addByte(4);
1345
+ blr.addBytes([120, 10, 0, 0]); // FROM DOT NET PROVIDER
1346
+ if (role)
1347
+ blr.addString(Const.isc_dpb_sql_role_name, role, Const.DEFAULT_ENCODING);
1348
+
1349
+ msg.addInt(Const.op_service_attach);
1350
+ msg.addInt(0);
1351
+ msg.addString(Const.DEFAULT_SVC_NAME, Const.DEFAULT_ENCODING); // only local for moment
1352
+ msg.addBlr(this._blr);
1353
+
1354
+ var self = this;
1355
+
1356
+ function cb(err, ret) {
1357
+
1358
+ if (err) {
1359
+ doError(err, callback);
1360
+ return;
1361
+ }
1362
+
1363
+ self.svchandle = ret.handle;
1364
+ if (callback)
1365
+ callback(undefined, ret);
1366
+ }
1367
+
1368
+ // For reconnect
1369
+ if (svc) {
1370
+ svc.connection = this;
1371
+ cb.response = svc;
141
1372
  } else {
142
- xdr = new XdrReader(Buffer.concat([self._xdr.buffer, data], self._xdr.buffer.length + data.length));
143
- delete (self._xdr);
1373
+ cb.response = new ServiceManager(this);
1374
+ cb.response.removeAllListeners('error');
1375
+ cb.response.on('error', noop);
144
1376
  }
1377
+
1378
+ this._queueEvent(cb);
1379
+ }
145
1380
 
146
- while (xdr.pos < xdr.buffer.length) {
147
- var cb = self._queue[0], pos = xdr.pos;
148
1381
 
149
- decodeResponse(xdr, cb, self, self._lowercase_keys, function (err, obj) {
1382
+ svcstart(spbaction, callback) {
1383
+ var msg = this._msg;
1384
+ var blr = this._blr;
1385
+ msg.pos = 0;
1386
+ msg.addInt(Const.op_service_start);
1387
+ msg.addInt(this.svchandle);
1388
+ msg.addInt(0)
1389
+ msg.addBlr(spbaction);
1390
+ this._queueEvent(callback);
1391
+ }
150
1392
 
151
- if (err) {
152
- xdr.buffer = xdr.buffer.slice(pos);
153
- xdr.pos = 0;
154
- self._xdr = xdr;
155
1393
 
156
- if (self.accept.protocolMinimumType === Const.ptype_lazy_send && self._queue.length > 0) {
157
- self._queue[0].lazy_count = 2;
158
- }
159
- return;
160
- }
1394
+ svcquery(spbquery, resultbuffersize, timeout,callback) {
1395
+ if (resultbuffersize > Const.MAX_BUFFER_SIZE) {
1396
+ doError(new Error('Buffer is too big'), callback);
1397
+ return;
1398
+ }
1399
+
1400
+ var msg = this._msg;
1401
+ var blr = this._blr;
1402
+ msg.pos = 0;
1403
+ blr.pos = 0;
1404
+ blr.addByte(Const.isc_spb_current_version);
1405
+ //blr.addByteInt32(Const.isc_info_svc_timeout, timeout);
1406
+ msg.addInt(Const.op_service_info);
1407
+ msg.addInt(this.svchandle);
1408
+ msg.addInt(0);
1409
+ msg.addBlr(blr);
1410
+ blr.pos = 0
1411
+ blr.addBytes(spbquery);
1412
+ msg.addBlr(blr);
1413
+ msg.addInt(resultbuffersize);
1414
+ this._queueEvent(callback);
1415
+ }
161
1416
 
162
- // remove the op flag, needed for partial packet
163
- if (xdr.r) {
164
- delete (xdr.r);
165
- }
166
1417
 
167
- self._queue.shift();
168
- self._pending.shift();
1418
+ svcdetach(callback) {
1419
+ var self = this;
1420
+
1421
+ if (self._isClosed)
1422
+ return;
1423
+
1424
+ self._isUsed = false;
1425
+ self._isDetach = true;
1426
+
1427
+ var msg = self._msg;
1428
+
1429
+ msg.pos = 0;
1430
+ msg.addInt(Const.op_service_detach);
1431
+ msg.addInt(this.svchandle); // Database Object ID
1432
+
1433
+ self._queueEvent(function (err, ret) {
1434
+ delete (self.svchandle);
1435
+ if (callback)
1436
+ callback(err, ret);
1437
+ });
1438
+ }
169
1439
 
170
- if (obj && obj.status) {
171
- obj.message = lookupMessages(obj.status);
172
- doCallback(obj, cb);
173
- } else {
174
- doCallback(obj, cb);
175
- }
176
1440
 
177
- });
178
1441
 
179
- if (xdr.pos === 0) {
180
- break;
1442
+ auxConnection(callback) {
1443
+ var self = this;
1444
+ if (self._isClosed)
1445
+ return this.throwClosed(callback);
1446
+ var msg = self._msg;
1447
+ msg.pos = 0;
1448
+ msg.addInt(Const.op_connect_request);
1449
+ msg.addInt(1); // async
1450
+ msg.addInt(self.dbhandle);
1451
+ msg.addInt(0);
1452
+ function cb(err, ret) {
1453
+
1454
+ if (err) {
1455
+ doError(err, callback);
1456
+ return;
181
1457
  }
1458
+
1459
+ var socket_info = {
1460
+ family: ret.buffer.readInt16BE(0),
1461
+ port: ret.buffer.readUInt16BE(2),
1462
+ host: ret.buffer.readUInt8(4) + '.' + ret.buffer.readUInt8(5) + '.' + ret.buffer.readUInt8(6) + '.' + ret.buffer.readUInt8(7)
1463
+ }
1464
+
1465
+ callback(undefined, socket_info);
182
1466
  }
1467
+ this._queueEvent(cb);
1468
+ }
183
1469
 
184
- if (!self._detachAuto || self._pending.length !== 0) {
185
- return;
186
- }
187
1470
 
188
- clearTimeout(self._detachTimeout);
189
- self._detachTimeout = setTimeout(function () {
190
- self.db.detach(self._detachCallback);
191
- self._detachAuto = false;
192
- }, 100);
1471
+ queEvents(events, eventid, callback) {
1472
+ var self = this;
1473
+ console.log('[Connection] queEvents called, eventid:', eventid, 'events:', Object.keys(events), 'isClosed:', this._isClosed);
1474
+ if (this._isClosed)
1475
+ return this.throwClosed(callback);
1476
+ var msg = this._msg;
1477
+ var blr = this._blr;
1478
+ blr.pos = 0;
1479
+ msg.pos = 0;
1480
+ msg.addInt(Const.op_que_events);
1481
+ msg.addInt(this.dbhandle);
1482
+ // prepare EPB
1483
+ blr.addByte(1) // epb_version
1484
+ for (var event in events) {
1485
+ var event_buffer = Buffer.from(event, 'UTF8');
1486
+ blr.addByte(event_buffer.length);
1487
+ blr.addBytes(event_buffer);
1488
+ blr.addInt32(events[event]);
1489
+ }
1490
+ msg.addBlr(blr); // epb
1491
+ msg.addInt(0); // ast
1492
+ msg.addInt(0); // args
1493
+ msg.addInt(eventid);
1494
+
1495
+ console.log('[Connection] queEvents: Message prepared, queuing event');
1496
+ function cb(err, ret) {
1497
+ console.log('[Connection] queEvents callback invoked, err:', err, 'ret:', ret);
1498
+ if (err) {
1499
+ doError(err, callback);
1500
+ return;
1501
+ }
1502
+
1503
+ callback(null, ret);
1504
+ }
1505
+
1506
+ this._queueEvent(cb);
1507
+ console.log('[Connection] queEvents: Event queued');
1508
+ }
193
1509
 
194
- });
195
- }
196
1510
 
197
- Connection.prototype.disconnect = function() {
198
- this._socket.end();
199
- };
1511
+ closeEvents(eventid, callback) {
1512
+ var self = this;
1513
+ console.log('[Connection] closeEvents called, eventid:', eventid, 'isClosed:', this._isClosed);
1514
+ if (this._isClosed)
1515
+ return this.throwClosed(callback);
1516
+ var msg = self._msg;
1517
+ msg.pos = 0;
1518
+ msg.addInt(Const.op_cancel_events);
1519
+ msg.addInt(self.dbhandle);
1520
+ msg.addInt(eventid);
1521
+
1522
+ function cb(err, ret) {
1523
+ console.log('[Connection] closeEvents callback invoked, err:', err);
1524
+ if (err) {
1525
+ doError(err, callback);
1526
+ return;
1527
+ }
1528
+
1529
+ callback(null);
1530
+ }
1531
+
1532
+ this._queueEvent(cb);
1533
+ console.log('[Connection] closeEvents: Event queued');
1534
+ }
200
1535
 
1536
+ }
201
1537
 
202
1538
  function decodeResponse(data, callback, cnx, lowercase_keys, cb) {
203
1539
  try {
@@ -368,12 +1704,14 @@ function decodeResponse(data, callback, cnx, lowercase_keys, cb) {
368
1704
  protocolVersion: data.readInt(),
369
1705
  protocolArchitecture: data.readInt(),
370
1706
  protocolMinimumType: data.readInt(),
1707
+ compress: false,
371
1708
  pluginName: '',
372
1709
  authData: '',
373
1710
  sessionKey: ''
374
1711
  };
375
1712
 
376
- accept.protocolMinimumType = accept.protocolMinimumType & 0xFF;
1713
+ accept.compress = (accept.protocolMinimumType & Const.pflag_compress) !== 0;
1714
+ accept.protocolMinimumType = accept.protocolMinimumType & Const.ptype_mask;
377
1715
  //accept.compress = (accept.acceptType & pflag_compress) !== 0; // TODO Handle zlib compression
378
1716
  if (accept.protocolVersion < 0) {
379
1717
  accept.protocolVersion = (accept.protocolVersion & Const.FB_PROTOCOL_MASK) | Const.FB_PROTOCOL_FLAG;
@@ -450,6 +1788,21 @@ function decodeResponse(data, callback, cnx, lowercase_keys, cb) {
450
1788
  }
451
1789
  }
452
1790
 
1791
+ if (accept.compress) {
1792
+ cnx._socket.enableCompression();
1793
+ }
1794
+
1795
+ // For op_cond_accept: send op_cont_auth and wait for response
1796
+ if (r === Const.op_cond_accept && accept.authData) {
1797
+ cnx._pendingAccept = accept;
1798
+ cnx.sendOpContAuth(
1799
+ accept.authData,
1800
+ Const.DEFAULT_ENCODING,
1801
+ accept.pluginName
1802
+ );
1803
+ return; // Don't call cb - queue stays for op_response
1804
+ }
1805
+
453
1806
  return cb(undefined, accept);
454
1807
  case Const.op_cont_auth:
455
1808
  var d = new BlrReader(data.readArray());
@@ -459,595 +1812,127 @@ function decodeResponse(data, callback, cnx, lowercase_keys, cb) {
459
1812
 
460
1813
  if (!cnx.options.pluginName) {
461
1814
  if (cnx.accept.pluginName === pluginName) {
462
- // Erreur plugin not able to connect
463
- return cb(new Error("Unable to connect with plugin " + cnx.accept.pluginName));
464
- }
465
-
466
- if (pluginName === Const.AUTH_PLUGIN_LEGACY) { // Fallback to LegacyAuth
467
- cnx.accept.pluginName = pluginName;
468
- cnx.accept.authData = crypt.crypt(cnx.options.password, Const.LEGACY_AUTH_SALT).substring(2);
469
-
470
- cnx.sendOpContAuth(
471
- cnx.accept.authData,
472
- Const.DEFAULT_ENCODING,
473
- pluginName
474
- );
475
-
476
- return {error: new Error('login')};
477
- }
478
- }
479
-
480
- return data.accept;
481
- default:
482
- return cb(new Error('Unexpected:' + r));
483
- }
484
- } catch (err) {
485
- if (err instanceof RangeError) {
486
- return cb(err);
487
- }
488
- throw err;
489
- }
490
- }
491
-
492
- function parseOpResponse(data, response, cb) {
493
- var handle = data.readInt();
494
-
495
- if (!response.handle) {
496
- response.handle = handle;
497
- }
498
-
499
- var oid = data.readQuad();
500
- if (oid.low || oid.high) {
501
- response.oid = oid;
502
- }
503
-
504
- var buf = data.readArray();
505
- if (buf) {
506
- response.buffer = buf;
507
- }
508
-
509
- var num, op, item = {};
510
- while (true) {
511
- op = data.readInt();
512
-
513
- switch (op) {
514
- case Const.isc_arg_end:
515
- return cb ? cb(undefined, response) : response;
516
- case Const.isc_arg_gds:
517
- num = data.readInt();
518
- if (!num) {
519
- break;
520
- }
521
-
522
- item = {gdscode: num};
523
-
524
- if (response.status) {
525
- response.status.push(item);
526
- } else {
527
- response.status = [item];
528
- }
529
-
530
- break;
531
- case Const.isc_arg_string:
532
- case Const.isc_arg_interpreted:
533
- case Const.isc_arg_sql_state:
534
- if (item.params) {
535
- var str = data.readString(Const.DEFAULT_ENCODING);
536
- item.params.push(str);
537
- } else {
538
- item.params = [data.readString(Const.DEFAULT_ENCODING)];
539
- }
540
-
541
- break;
542
- case Const.isc_arg_number:
543
- num = data.readInt();
544
-
545
- if (item.params) {
546
- item.params.push(num);
547
- } else {
548
- item.params = [num];
549
- }
550
-
551
- if (item.gdscode === Const.isc_sqlerr) {
552
- response.sqlcode = num;
553
- }
554
-
555
- break;
556
- default:
557
- if (cb) {
558
- cb(new Error('Unexpected: ' + op))
559
- } else {
560
- throw new Error('Unexpected: ' + op);
561
- }
562
- }
563
- }
564
- }
565
-
566
- Connection.prototype.sendOpContAuth = function(authData, authDataEnc, pluginName) {
567
- var msg = this._msg;
568
- msg.pos = 0;
569
-
570
- msg.addInt(Const.op_cont_auth);
571
- msg.addString(authData, authDataEnc);
572
- msg.addString(pluginName, Const.DEFAULT_ENCODING)
573
- msg.addString(Const.AUTH_PLUGIN_LIST.join(','), Const.DEFAULT_ENCODING);
574
- // msg.addInt(0); // p_list
575
- msg.addInt(0); // keys
576
-
577
- this._socket.write(msg.getData());
578
- }
579
-
580
- Connection.prototype._queueEvent = function(callback){
581
- var self = this;
582
-
583
- if (self._isClosed) {
584
- if (callback)
585
- callback(new Error('Connection is closed.'));
586
- return;
587
- }
588
-
589
- self._queue.push(callback);
590
- self._socket.write(self._msg.getData());
591
- };
592
-
593
- Connection.prototype.connect = function (options, callback) {
594
- var pluginName = options.manager ? Const.AUTH_PLUGIN_LEGACY : options.pluginName || Const.AUTH_PLUGIN_LIST[0]; // TODO Srp for service
595
- var msg = this._msg;
596
- var blr = this._blr;
597
-
598
- this._pending.push('connect');
599
-
600
- msg.pos = 0;
601
- blr.pos = 0;
602
-
603
- blr.addString(Const.CNCT_login, options.user, Const.DEFAULT_ENCODING);
604
- blr.addString(Const.CNCT_plugin_name, pluginName, Const.DEFAULT_ENCODING);
605
- blr.addString(Const.CNCT_plugin_list, Const.AUTH_PLUGIN_LIST.join(','), Const.DEFAULT_ENCODING);
606
-
607
- var specificData = '';
608
- if (Const.AUTH_PLUGIN_SRP_LIST.indexOf(pluginName) > -1) {
609
- this.clientKeys = srp.clientSeed();
610
- specificData = this.clientKeys.public.toString(16);
611
- blr.addMultiblockPart(Const.CNCT_specific_data, specificData, Const.DEFAULT_ENCODING);
612
- } else if (pluginName === Const.AUTH_PLUGIN_LEGACY) {
613
- specificData = crypt.crypt(options.password, Const.LEGACY_AUTH_SALT).substring(2);
614
- blr.addMultiblockPart(Const.CNCT_specific_data, specificData, Const.DEFAULT_ENCODING);
615
- } else {
616
- doError(new Error('Invalide auth plugin \'' + pluginName + '\''), callback);
617
- return;
618
- }
619
- blr.addBytes([Const.CNCT_client_crypt, 4, Const.WIRE_CRYPT_DISABLE, 0, 0, 0]); // WireCrypt = Disabled
620
- blr.addString(Const.CNCT_user, os.userInfo().username || 'Unknown', Const.DEFAULT_ENCODING);
621
- blr.addString(Const.CNCT_host, os.hostname(), Const.DEFAULT_ENCODING);
622
- blr.addBytes([Const.CNCT_user_verification, 0]);
623
-
624
- msg.addInt(Const.op_connect);
625
- msg.addInt(Const.op_attach);
626
- msg.addInt(Const.CONNECT_VERSION3);
627
- msg.addInt(Const.ARCHITECTURE_GENERIC);
628
- msg.addString(options.database || options.filename, Const.DEFAULT_ENCODING);
629
- msg.addInt(Const.SUPPORTED_PROTOCOL.length); // Count of Protocol version understood count.
630
- msg.addBlr(this._blr);
631
-
632
- for (var protocol of Const.SUPPORTED_PROTOCOL) {
633
- msg.addInt(protocol[0]); // Version
634
- msg.addInt(protocol[1]); // Architecture
635
- msg.addInt(protocol[2]); // Min type
636
- msg.addInt(protocol[3]); // Max type
637
- msg.addInt(protocol[4]); // Preference weight
638
- }
639
-
640
- var self = this;
641
- function cb(err, ret) {
642
- if (err) {
643
- doError(err, callback);
644
- return;
645
- }
646
-
647
- self.accept = ret;
648
- if (callback)
649
- callback(undefined, ret);
650
- }
651
-
652
- this._queueEvent(cb);
653
- };
654
-
655
- Connection.prototype.attach = function (options, callback, db) {
656
- this._lowercase_keys = options.lowercase_keys || Const.DEFAULT_LOWERCASE_KEYS;
657
-
658
- var database = options.database || options.filename;
659
- if (database == null || database.length === 0) {
660
- doError(new Error('No database specified'), callback);
661
- return;
662
- }
663
-
664
- var user = options.user || Const.DEFAULT_USER;
665
- var password = options.password || Const.DEFAULT_PASSWORD;
666
- var role = options.role;
667
- var self = this;
668
- var msg = this._msg;
669
- var blr = this._blr;
670
- msg.pos = 0;
671
- blr.pos = 0;
672
-
673
- blr.addByte(Const.isc_dpb_version1);
674
- blr.addString(Const.isc_dpb_lc_ctype, options.encoding || 'UTF8', Const.DEFAULT_ENCODING);
675
- blr.addString(Const.isc_dpb_user_name, user, Const.DEFAULT_ENCODING);
676
- if (options.password && !this.accept.authData) {
677
- if (this.accept.protocolVersion < Const.PROTOCOL_VERSION13) {
678
- if (this.accept.protocolVersion === Const.PROTOCOL_VERSION10) {
679
- blr.addString(Const.isc_dpb_password, password, Const.DEFAULT_ENCODING);
680
- } else {
681
- blr.addString(Const.isc_dpb_password_enc, crypt.crypt(password, Const.LEGACY_AUTH_SALT).substring(2), Const.DEFAULT_ENCODING);
682
- }
683
- }
684
- }
685
-
686
- if (role)
687
- blr.addString(Const.isc_dpb_sql_role_name, role, Const.DEFAULT_ENCODING);
688
-
689
- blr.addBytes([Const.isc_dpb_process_id, 4]);
690
- blr.addInt32(process.pid);
691
-
692
- let processName = process.title || "";
693
- blr.addString(Const.isc_dpb_process_name, processName.length > 255 ? processName.substring(processName.length - 255, processName.length) : processName, Const.DEFAULT_ENCODING);
694
-
695
- if (this.accept.authData) {
696
- blr.addString(Const.isc_dpb_specific_auth_data, this.accept.authData, Const.DEFAULT_ENCODING);
697
- }
698
-
699
- msg.addInt(Const.op_attach);
700
- msg.addInt(0); // Database Object ID
701
- msg.addString(database, Const.DEFAULT_ENCODING);
702
- msg.addBlr(this._blr);
703
-
704
- function cb(err, ret) {
705
- if (err) {
706
- doError(err, callback);
707
- return;
708
- }
709
-
710
- self.dbhandle = ret.handle;
711
- if (callback)
712
- callback(undefined, ret);
713
- }
714
-
715
- // For reconnect
716
- if (db) {
717
- db.connection = this;
718
- cb.response = db;
719
- } else {
720
- cb.response = new Database(this);
721
- cb.response.removeAllListeners('error');
722
- cb.response.on('error', noop);
723
- }
724
-
725
- this._queueEvent(cb);
726
- };
727
-
728
- Connection.prototype.detach = function (callback) {
729
-
730
- var self = this;
731
-
732
- if (self._isClosed)
733
- return;
734
-
735
- self._isUsed = false;
736
- self._isDetach = true;
737
-
738
- var msg = self._msg;
739
-
740
- msg.pos = 0;
741
- msg.addInt(Const.op_detach);
742
- msg.addInt(0); // Database Object ID
743
-
744
- self._queueEvent(function(err, ret) {
745
- clearTimeout(self._retry_connection_id);
746
- delete(self.dbhandle);
747
- if (callback)
748
- callback(err, ret);
749
- });
750
- };
751
-
752
- Connection.prototype.createDatabase = function (options, callback) {
753
- var database = options.database || options.filename;
754
- if (database == null || database.length === 0) {
755
- doError(new Error('No database specified'), callback);
756
- return;
757
- }
758
-
759
- var user = options.user || Const.DEFAULT_USER;
760
- var password = options.password || Const.DEFAULT_PASSWORD;
761
- var pageSize = options.pageSize || Const.DEFAULT_PAGE_SIZE;
762
- var role = options.role;
763
- var blr = this._blr;
764
-
765
- blr.pos = 0;
766
- blr.addByte(Const.isc_dpb_version1);
767
- blr.addString(Const.isc_dpb_set_db_charset, 'UTF8', Const.DEFAULT_ENCODING);
768
- blr.addString(Const.isc_dpb_lc_ctype, 'UTF8', Const.DEFAULT_ENCODING);
769
- blr.addString(Const.isc_dpb_user_name, user, Const.DEFAULT_ENCODING);
770
- if (this.accept.protocolVersion < Const.PROTOCOL_VERSION13) {
771
- if (this.accept.protocolVersion === Const.PROTOCOL_VERSION10) {
772
- blr.addString(Const.isc_dpb_password, password, Const.DEFAULT_ENCODING);
773
- } else {
774
- blr.addString(Const.isc_dpb_password_enc, crypt.crypt(password, Const.LEGACY_AUTH_SALT).substring(2), Const.DEFAULT_ENCODING);
775
- }
776
- }
777
- if (role)
778
- blr.addString(Const.isc_dpb_sql_role_name, role, Const.DEFAULT_ENCODING);
779
-
780
- blr.addBytes([Const.isc_dpb_process_id, 4]);
781
- blr.addInt32(process.pid);
782
-
783
- let processName = process.title || "";
784
- blr.addString(Const.isc_dpb_process_name, processName.length > 255 ? processName.substring(processName.length - 255, processName.length) : processName, Const.DEFAULT_ENCODING);
785
-
786
- if (this.accept.authData) {
787
- blr.addString(Const.isc_dpb_specific_auth_data, this.accept.authData, Const.DEFAULT_ENCODING);
788
- }
789
-
790
- blr.addNumeric(Const.isc_dpb_sql_dialect, 3);
791
- blr.addNumeric(Const.isc_dpb_force_write, 1);
792
- blr.addNumeric(Const.isc_dpb_overwrite, 1);
793
- blr.addNumeric(Const.isc_dpb_page_size, pageSize);
794
-
795
- var msg = this._msg;
796
- msg.pos = 0;
797
- msg.addInt(Const.op_create); // op_create
798
- msg.addInt(0); // Database Object ID
799
- msg.addString(database, Const.DEFAULT_ENCODING);
800
- msg.addBlr(blr);
801
-
802
- var self = this;
803
-
804
- function cb(err, ret) {
805
-
806
- if (ret)
807
- self.dbhandle = ret.handle;
808
-
809
- setImmediate(function() {
810
- if (self.db)
811
- self.db.emit('attach', ret);
812
- });
813
-
814
- if (callback)
815
- callback(err, ret);
816
- }
817
-
818
- cb.response = new Database(this);
819
- this._queueEvent(cb);
820
- };
821
-
822
- Connection.prototype.dropDatabase = function (callback) {
823
- var msg = this._msg;
824
- msg.pos = 0;
825
-
826
- msg.addInt(Const.op_drop_database);
827
- msg.addInt(this.dbhandle);
828
-
829
- var self = this;
830
- this._queueEvent(function(err) {
831
- self.detach(function() {
832
- self.disconnect();
833
-
834
- if (callback)
835
- callback(err);
836
- });
837
- });
838
- };
839
-
840
- Connection.prototype.throwClosed = function(callback) {
841
- var err = new Error('Connection is closed.');
842
- this.db.emit('error', err);
843
- if (callback)
844
- callback(err);
845
- return this;
846
- };
847
-
848
- Connection.prototype.startTransaction = function(isolation, callback) {
849
-
850
- if (typeof(isolation) === 'function') {
851
- var tmp = isolation;
852
- isolation = callback;
853
- callback = tmp;
854
- }
855
-
856
- if (this._isClosed)
857
- return this.throwClosed(callback);
858
-
859
- // for auto detach
860
- this._pending.push('startTransaction');
861
-
862
- var blr = this._blr;
863
- var msg = this._msg;
864
-
865
- blr.pos = 0;
866
- msg.pos = 0;
867
-
868
- blr.addBytes(isolation || Const.ISOLATION_REPEATABLE_READ);
869
- msg.addInt(Const.op_transaction);
870
- msg.addInt(this.dbhandle);
871
- msg.addBlr(blr);
872
- callback.response = new Transaction(this);
873
-
874
- this.db.emit('transaction', isolation);
875
- this._queueEvent(callback);
876
- };
877
-
878
- Connection.prototype.commit = function (transaction, callback) {
879
-
880
- if (this._isClosed)
881
- return this.throwClosed(callback);
882
-
883
- // for auto detach
884
- this._pending.push('commit');
885
-
886
- var msg = this._msg;
887
- msg.pos = 0;
888
- msg.addInt(Const.op_commit);
889
- msg.addInt(transaction.handle);
890
- this.db.emit('commit');
891
- this._queueEvent(callback);
892
- };
893
-
894
- Connection.prototype.rollback = function (transaction, callback) {
895
-
896
- if (this._isClosed)
897
- return this.throwClosed(callback);
898
-
899
- // for auto detach
900
- this._pending.push('rollback');
901
-
902
- var msg = this._msg;
903
- msg.pos = 0;
904
- msg.addInt(Const.op_rollback);
905
- msg.addInt(transaction.handle);
906
- this.db.emit('rollback');
907
- this._queueEvent(callback);
908
- };
909
-
910
- Connection.prototype.commitRetaining = function (transaction, callback) {
911
-
912
- if (this._isClosed)
913
- throw new Error('Connection is closed.');
914
-
915
- // for auto detach
916
- this._pending.push('commitRetaining');
917
-
918
- var msg = this._msg;
919
- msg.pos = 0;
920
- msg.addInt(Const.op_commit_retaining);
921
- msg.addInt(transaction.handle);
922
- this._queueEvent(callback);
923
- };
924
-
925
- Connection.prototype.rollbackRetaining = function (transaction, callback) {
926
-
927
- if (this._isClosed)
928
- return this.throwClosed(callback);
929
-
930
- // for auto detach
931
- this._pending.push('rollbackRetaining');
932
-
933
- var msg = this._msg;
934
- msg.pos = 0;
935
- msg.addInt(Const.op_rollback_retaining);
936
- msg.addInt(transaction.handle);
937
- this._queueEvent(callback);
938
- };
939
-
940
- Connection.prototype.allocateStatement = function (callback) {
941
-
942
- if (this._isClosed)
943
- return this.throwClosed(callback);
944
-
945
- // for auto detach
946
- this._pending.push('allocateStatement');
1815
+ // Erreur plugin not able to connect
1816
+ return cb(new Error("Unable to connect with plugin " + cnx.accept.pluginName));
1817
+ }
947
1818
 
948
- var msg = this._msg;
949
- msg.pos = 0;
950
- msg.addInt(Const.op_allocate_statement);
951
- msg.addInt(this.dbhandle);
952
- callback.response = new Statement(this);
953
- this._queueEvent(callback);
954
- };
1819
+ if (pluginName === Const.AUTH_PLUGIN_LEGACY) { // Fallback to LegacyAuth
1820
+ cnx.accept.pluginName = pluginName;
1821
+ cnx.accept.authData = crypt.crypt(cnx.options.password, Const.LEGACY_AUTH_SALT).substring(2);
955
1822
 
956
- Connection.prototype.dropStatement = function (statement, callback) {
1823
+ cnx.sendOpContAuth(
1824
+ cnx.accept.authData,
1825
+ Const.DEFAULT_ENCODING,
1826
+ pluginName
1827
+ );
957
1828
 
958
- if (this._isClosed)
959
- return this.throwClosed(callback);
1829
+ return {error: new Error('login')};
1830
+ }
1831
+ }
960
1832
 
961
- // for auto detach
962
- this._pending.push('dropStatement');
963
-
964
- var msg = this._msg;
965
- msg.pos = 0;
966
- msg.addInt(Const.op_free_statement);
967
- msg.addInt(statement.handle);
968
- msg.addInt(Const.DSQL_drop);
969
- this._queueEvent(callback);
970
- };
971
-
972
- Connection.prototype.closeStatement = function (statement, callback) {
1833
+ return data.accept;
1834
+ case Const.op_crypt_key_callback:
1835
+ // Database encryption key callback
1836
+ // Read server data (plugin data sent by server)
1837
+ var serverPluginData = data.readArray();
1838
+
1839
+ // Get client response from dbCryptConfig option
1840
+ var clientPluginData = parseDbCryptConfig(cnx.options.dbCryptConfig);
1841
+
1842
+ // Create a BlrWriter to send the response
1843
+ // Note: BlrWriter needs initial buffer size allocation
1844
+ var responseBlr = new BlrWriter(clientPluginData.length + 4);
1845
+ responseBlr.addBytes(clientPluginData);
1846
+
1847
+ // Send the response back to the server
1848
+ cnx.sendOpCryptKeyCallback(responseBlr);
1849
+
1850
+ // Don't call cb - wait for next operation (likely op_response or another op_crypt_key_callback)
1851
+ return;
1852
+ default:
1853
+ return cb(new Error('Unexpected:' + r));
1854
+ }
1855
+ } catch (err) {
1856
+ if (err instanceof RangeError) {
1857
+ return cb(err);
1858
+ }
1859
+ throw err;
1860
+ }
1861
+ }
973
1862
 
974
- if (this._isClosed)
975
- return this.throwClosed(callback);
1863
+ function parseOpResponse(data, response, cb) {
1864
+ var handle = data.readInt();
976
1865
 
977
- // for auto detach
978
- this._pending.push('closeStatement');
979
-
980
- var msg = this._msg;
981
- msg.pos = 0;
982
- msg.addInt(Const.op_free_statement);
983
- msg.addInt(statement.handle);
984
- msg.addInt(Const.DSQL_close);
985
-
986
- this._queueEvent(callback);
987
- };
988
-
989
- Connection.prototype.allocateAndPrepareStatement = function (transaction, query, plan, callback) {
990
- var self = this;
991
- var mainCallback = function(err, ret) {
992
- if (!err) {
993
- mainCallback.response.handle = ret.handle;
994
- describe(ret.buffer, mainCallback.response);
995
- mainCallback.response.query = query;
996
- self.db.emit('query', query);
997
- ret = mainCallback.response;
998
- self._setcachedquery(query, ret);
999
- }
1866
+ if (!response.handle) {
1867
+ response.handle = handle;
1868
+ }
1000
1869
 
1001
- if (callback)
1002
- callback(err, ret);
1003
- };
1870
+ var oid = data.readQuad();
1871
+ if (oid.low || oid.high) {
1872
+ response.oid = oid;
1873
+ }
1004
1874
 
1005
- // for auto detach
1006
- this._pending.push('allocateAndPrepareStatement');
1875
+ var buf = data.readArray();
1876
+ if (buf) {
1877
+ response.buffer = buf;
1878
+ }
1007
1879
 
1008
- var msg = this._msg;
1009
- var blr = this._blr;
1880
+ var num, op, item = {};
1881
+ while (true) {
1882
+ op = data.readInt();
1010
1883
 
1011
- msg.pos = 0;
1012
- blr.pos = 0;
1884
+ switch (op) {
1885
+ case Const.isc_arg_end:
1886
+ return cb ? cb(undefined, response) : response;
1887
+ case Const.isc_arg_gds:
1888
+ num = data.readInt();
1889
+ if (!num) {
1890
+ break;
1891
+ }
1013
1892
 
1014
- msg.addInt(Const.op_allocate_statement);
1015
- msg.addInt(this.dbhandle);
1016
- mainCallback.lazy_count = 1;
1893
+ item = {gdscode: num};
1017
1894
 
1018
- blr.addBytes(Const.DESCRIBE);
1019
- if (plan)
1020
- blr.addByte(Const.isc_info_sql_get_plan);
1895
+ if (response.status) {
1896
+ response.status.push(item);
1897
+ } else {
1898
+ response.status = [item];
1899
+ }
1021
1900
 
1022
- msg.addInt(Const.op_prepare_statement);
1023
- msg.addInt(transaction.handle);
1024
- msg.addInt(0xFFFF);
1025
- msg.addInt(3); // dialect = 3
1026
- msg.addString(query, Const.DEFAULT_ENCODING);
1027
- msg.addBlr(blr);
1028
- msg.addInt(65535); // buffer_length
1029
- mainCallback.lazy_count += 1;
1901
+ break;
1902
+ case Const.isc_arg_string:
1903
+ case Const.isc_arg_interpreted:
1904
+ case Const.isc_arg_sql_state:
1905
+ if (item.params) {
1906
+ var str = data.readString(Const.DEFAULT_ENCODING);
1907
+ item.params.push(str);
1908
+ } else {
1909
+ item.params = [data.readString(Const.DEFAULT_ENCODING)];
1910
+ }
1030
1911
 
1031
- mainCallback.response = new Statement(this);
1032
- this._queueEvent(mainCallback);
1033
- };
1912
+ break;
1913
+ case Const.isc_arg_number:
1914
+ num = data.readInt();
1034
1915
 
1035
- Connection.prototype.prepare = function (transaction, query, plan, callback) {
1036
- var self = this;
1916
+ if (item.params) {
1917
+ item.params.push(num);
1918
+ } else {
1919
+ item.params = [num];
1920
+ }
1037
1921
 
1038
- if (this.accept.protocolMinimumType === Const.ptype_lazy_send) { // V11 Statement or higher
1039
- self.allocateAndPrepareStatement(transaction, query, plan, callback);
1040
- } else { // V10 Statement
1041
- self.allocateStatement(function (err, statement) {
1042
- if (err) {
1043
- doError(err, callback);
1044
- return;
1045
- }
1922
+ if (item.gdscode === Const.isc_sqlerr) {
1923
+ response.sqlcode = num;
1924
+ }
1046
1925
 
1047
- self.prepareStatement(transaction, statement, query, plan, callback);
1048
- });
1926
+ break;
1927
+ default:
1928
+ if (cb) {
1929
+ cb(new Error('Unexpected: ' + op))
1930
+ } else {
1931
+ throw new Error('Unexpected: ' + op);
1932
+ }
1933
+ }
1049
1934
  }
1050
- };
1935
+ }
1051
1936
 
1052
1937
  function describe(buff, statement) {
1053
1938
  var br = new BlrReader(buff);
@@ -1149,52 +2034,6 @@ function describe(buff, statement) {
1149
2034
  }
1150
2035
  }
1151
2036
 
1152
- Connection.prototype.prepareStatement = function (transaction, statement, query, plan, callback) {
1153
-
1154
- if (this._isClosed)
1155
- return this.throwClosed(callback);
1156
-
1157
- var msg = this._msg;
1158
- var blr = this._blr;
1159
-
1160
- msg.pos = 0;
1161
- blr.pos = 0;
1162
-
1163
- if (plan instanceof Function) {
1164
- callback = plan;
1165
- plan = false;
1166
- }
1167
-
1168
- blr.addBytes(Const.DESCRIBE);
1169
-
1170
- if (plan)
1171
- blr.addByte(Const.isc_info_sql_get_plan);
1172
-
1173
- msg.addInt(Const.op_prepare_statement);
1174
- msg.addInt(transaction.handle);
1175
- msg.addInt(statement.handle);
1176
- msg.addInt(3); // dialect = 3
1177
- msg.addString(query, Const.DEFAULT_ENCODING);
1178
- msg.addBlr(blr);
1179
- msg.addInt(65535); // buffer_length
1180
-
1181
- var self = this;
1182
- this._queueEvent(function(err, ret) {
1183
-
1184
- if (!err) {
1185
- describe(ret.buffer, statement);
1186
- statement.query = query;
1187
- self.db.emit('query', query);
1188
- ret = statement;
1189
- self._setcachedquery(query, ret);
1190
- }
1191
-
1192
- if (callback)
1193
- callback(err, ret);
1194
- });
1195
-
1196
- };
1197
-
1198
2037
  function CalcBlr(blr, xsqlda) {
1199
2038
  blr.addBytes([Const.blr_version5, Const.blr_begin, Const.blr_message, 0]); // + message number
1200
2039
  blr.addWord(xsqlda.length * 2);
@@ -1209,280 +2048,6 @@ function CalcBlr(blr, xsqlda) {
1209
2048
  blr.addByte(Const.blr_eoc);
1210
2049
  }
1211
2050
 
1212
- Connection.prototype.executeStatement = function(transaction, statement, params, callback, custom) {
1213
-
1214
- if (this._isClosed)
1215
- return this.throwClosed(callback);
1216
-
1217
- // for auto detach
1218
- this._pending.push('executeStatement');
1219
-
1220
- if (params instanceof Function) {
1221
- callback = params;
1222
- params = undefined;
1223
- }
1224
-
1225
- var self = this;
1226
-
1227
- var op = Const.op_execute;
1228
- if (
1229
- this.accept.protocolVersion >= Const.PROTOCOL_VERSION13 &&
1230
- statement.type === Const.isc_info_sql_stmt_exec_procedure &&
1231
- statement.output.length
1232
- ) {
1233
- op = Const.op_execute2;
1234
- }
1235
-
1236
- function PrepareParams(params, input, callback) {
1237
-
1238
- var value, meta;
1239
- var ret = new Array(params.length);
1240
- var wait = params.length;
1241
-
1242
- function done() {
1243
- wait--;
1244
- if (wait === 0)
1245
- callback(ret);
1246
- }
1247
-
1248
- function putBlobData(index, value, callback) {
1249
-
1250
- self.createBlob2(transaction, function(err, blob) {
1251
-
1252
- var b;
1253
- var isStream = value.readable;
1254
-
1255
- if (Buffer.isBuffer(value))
1256
- b = value;
1257
- else if (typeof(value) === 'string')
1258
- b = Buffer.from(value, Const.DEFAULT_ENCODING);
1259
- else if (!isStream)
1260
- b = Buffer.from(JSON.stringify(value), Const.DEFAULT_ENCODING);
1261
-
1262
- if (Buffer.isBuffer(b)) {
1263
- bufferReader(b, 1024, function(b, next) {
1264
- self.batchSegments(blob, b, next);
1265
- }, function() {
1266
- ret[index] = new Xsql.SQLParamQuad(blob.oid);
1267
- self.closeBlob(blob, callback);
1268
- });
1269
- return;
1270
- }
1271
-
1272
- var isReading = false;
1273
- var isEnd = false;
1274
-
1275
- value.on('data', function(chunk) {
1276
- value.pause();
1277
- isReading = true;
1278
- bufferReader(chunk, 1024, function(b, next) {
1279
- self.batchSegments(blob, b, next);
1280
- }, function() {
1281
- isReading = false;
1282
-
1283
- if (isEnd) {
1284
- ret[index] = new Xsql.SQLParamQuad(blob.oid);
1285
- self.closeBlob(blob, callback);
1286
- } else
1287
- value.resume();
1288
- });
1289
- });
1290
-
1291
- value.on('end', function() {
1292
- isEnd = true;
1293
- if (isReading)
1294
- return;
1295
- ret[index] = new Xsql.SQLParamQuad(blob.oid);
1296
- self.closeBlob(blob, callback);
1297
- });
1298
- });
1299
- }
1300
-
1301
- for (var i = 0, length = params.length; i < length; i++) {
1302
- value = params[i];
1303
- meta = input[i];
1304
-
1305
- if (value === null || value === undefined) {
1306
- switch (meta.type) {
1307
- case Const.SQL_VARYING:
1308
- case Const.SQL_NULL:
1309
- case Const.SQL_TEXT:
1310
- ret[i] = new Xsql.SQLParamString(null);
1311
- break;
1312
- case Const.SQL_DOUBLE:
1313
- case Const.SQL_FLOAT:
1314
- case Const.SQL_D_FLOAT:
1315
- ret[i] = new Xsql.SQLParamDouble(null);
1316
- break;
1317
- case Const.SQL_TYPE_DATE:
1318
- case Const.SQL_TYPE_TIME:
1319
- case Const.SQL_TIMESTAMP:
1320
- ret[i] = new Xsql.SQLParamDate(null);
1321
- break;
1322
- case Const.SQL_BLOB:
1323
- case Const.SQL_ARRAY:
1324
- case Const.SQL_QUAD:
1325
- ret[i] = new Xsql.SQLParamQuad(null);
1326
- break;
1327
- case Const.SQL_LONG:
1328
- case Const.SQL_SHORT:
1329
- case Const.SQL_INT64:
1330
- case Const.SQL_BOOLEAN:
1331
- ret[i] = new Xsql.SQLParamInt(null);
1332
- break;
1333
- default:
1334
- ret[i] = null;
1335
- }
1336
- done();
1337
- } else {
1338
- switch (meta.type) {
1339
- case Const.SQL_BLOB:
1340
- putBlobData(i, value, done);
1341
- break;
1342
-
1343
- case Const.SQL_TIMESTAMP:
1344
- case Const.SQL_TYPE_DATE:
1345
- case Const.SQL_TYPE_TIME:
1346
-
1347
- if (value instanceof Date)
1348
- ret[i] = new Xsql.SQLParamDate(value);
1349
- else if (typeof(value) === 'string')
1350
- ret[i] = new Xsql.SQLParamDate(parseDate(value));
1351
- else
1352
- ret[i] = new Xsql.SQLParamDate(new Date(value));
1353
-
1354
- done();
1355
- break;
1356
-
1357
- default:
1358
- switch (typeof value) {
1359
- case 'bigint':
1360
- ret[i] = new Xsql.SQLParamInt128(value);
1361
- break;
1362
- case 'number':
1363
- if (value % 1 === 0) {
1364
- if (value >= Const.MIN_INT && value <= Const.MAX_INT)
1365
- ret[i] = new Xsql.SQLParamInt(value);
1366
- else
1367
- ret[i] = new Xsql.SQLParamInt64(value);
1368
- } else
1369
- ret[i] = new Xsql.SQLParamDouble(value);
1370
- break;
1371
- case 'string':
1372
- ret[i] = new Xsql.SQLParamString(value);
1373
- break;
1374
- case 'boolean':
1375
- ret[i] = new Xsql.SQLParamBool(value);
1376
- break;
1377
- default:
1378
- //throw new Error('Unexpected parametter: ' + JSON.stringify(params) + ' - ' + JSON.stringify(input));
1379
- ret[i] = new Xsql.SQLParamString(value.toString());
1380
- break;
1381
- }
1382
- done();
1383
- }
1384
- }
1385
- }
1386
- }
1387
-
1388
- var input = statement.input;
1389
-
1390
- if (input.length) {
1391
-
1392
- if (!(params instanceof Array)) {
1393
- if (params !== undefined)
1394
- params = [params];
1395
- else
1396
- params = [];
1397
- }
1398
-
1399
- if (params.length !== input.length) {
1400
- self._pending.pop();
1401
- callback(new Error('Expected parameters: (params=' + params.length + ' vs. expected=' + input.length + ') - ' + statement.query));
1402
- return;
1403
- }
1404
-
1405
- PrepareParams(params, input, function(prms) {
1406
- self.sendExecute(op, statement, transaction, callback, prms);
1407
- });
1408
-
1409
- return;
1410
- }
1411
-
1412
- this.sendExecute(op, statement, transaction, callback);
1413
- };
1414
-
1415
- Connection.prototype.sendExecute = function (op, statement, transaction, callback, parameters) {
1416
- var msg = this._msg;
1417
- var blr = this._blr;
1418
- msg.pos = 0;
1419
- blr.pos = 0;
1420
-
1421
- msg.addInt(op);
1422
- msg.addInt(statement.handle);
1423
- msg.addInt(transaction.handle);
1424
-
1425
- if (parameters && parameters.length) {
1426
- CalcBlr(blr, parameters);
1427
- msg.addBlr(blr); // params blr
1428
- msg.addInt(0); // message number
1429
- msg.addInt(1); // param count
1430
-
1431
- if (this.accept.protocolVersion >= Const.PROTOCOL_VERSION13) {
1432
- // start with null indicator bitmap
1433
- var nullBits = new BitSet();
1434
-
1435
- for (var i = 0; i < parameters.length; i++) {
1436
- nullBits.set(i, (parameters[i].value === null) & 1);
1437
- }
1438
-
1439
- var nullBuffer = nullBits.toBuffer();
1440
- var requireBytes = Math.floor((parameters.length + 7) / 8);
1441
- var remainingBytes = requireBytes - nullBuffer.length;
1442
-
1443
- if (nullBuffer.length) {
1444
- msg.addBuffer(nullBuffer);
1445
- }
1446
- if (remainingBytes > 0) {
1447
- msg.addBuffer(Buffer.alloc(remainingBytes));
1448
- }
1449
- msg.addAlignment(requireBytes);
1450
-
1451
- for(var i = 0; i < parameters.length; i++) {
1452
- if (parameters[i].value !== null) {
1453
- parameters[i].encode(msg);
1454
- }
1455
- }
1456
- } else {
1457
- for(var i = 0; i < parameters.length; i++) {
1458
- parameters[i].encode(msg);
1459
- if (parameters[i].value !== null) {
1460
- msg.addInt(0);
1461
- }
1462
- }
1463
- }
1464
- } else {
1465
- msg.addBlr(blr); // empty
1466
- msg.addInt(0); // message number
1467
- msg.addInt(0); // param count
1468
- }
1469
-
1470
- if (op === Const.op_execute2) {
1471
- var outputBlr = new BlrWriter(32);
1472
-
1473
- if (statement.output && statement.output.length) {
1474
- CalcBlr(outputBlr, statement.output);
1475
- msg.addBlr(outputBlr);
1476
- } else {
1477
- msg.addBlr(outputBlr); // empty
1478
- }
1479
- msg.addInt(0); // out_message_number = out_message_type
1480
- }
1481
-
1482
- callback.statement = statement;
1483
- this._queueEvent(callback);
1484
- }
1485
-
1486
2051
  function fetch_blob_async_transaction(statement, id, column, row) {
1487
2052
  const infoValue = { row, column, value: '' };
1488
2053
 
@@ -1639,228 +2204,55 @@ function fetch_blob_async(statement, id, name, row) {
1639
2204
  };
1640
2205
  }
1641
2206
 
1642
- Connection.prototype.fetch = function(statement, transaction, count, callback) {
1643
-
1644
- var msg = this._msg;
1645
- var blr = this._blr;
1646
-
1647
- msg.pos = 0;
1648
- blr.pos = 0;
1649
-
1650
- if (count instanceof Function) {
1651
- callback = count;
1652
- count = Const.DEFAULT_FETCHSIZE;
1653
- }
1654
-
1655
- msg.addInt(Const.op_fetch);
1656
- msg.addInt(statement.handle);
1657
- CalcBlr(blr, statement.output);
1658
- msg.addBlr(blr);
1659
- msg.addInt(0); // message number
1660
- msg.addInt(count || Const.DEFAULT_FETCHSIZE); // fetch count
1661
-
1662
- callback.statement = statement;
1663
- this._queueEvent(callback);
1664
- };
1665
-
1666
- Connection.prototype.fetchAll = function (statement, transaction, callback) {
1667
- const self = this, data = [];
1668
- const loop = (err, ret) => {
1669
- if (err) {
1670
- return callback(err);
1671
- } else if (ret && ret.data && ret.data.length) {
1672
- const arrPromise = (ret.arrBlob || []).map(value => value(transaction));
1673
-
1674
- Promise.all(arrPromise).then((arrBlob) => {
1675
- for (let i = 0; i < arrBlob.length; i++) {
1676
- const blob = arrBlob[i];
1677
- ret.data[blob.row][blob.column] = blob.value;
1678
- }
1679
-
1680
- const lastIndex = ret.data.length - 1;
1681
- for (let i = 0; i < ret.data.length; i++) {
1682
- const pos = data.push(ret.data[i]);
1683
- if (statement.custom && statement.custom.asStream && statement.custom.on) {
1684
- statement.custom.on(ret.data[i], pos - 1);
1685
- }
1686
- if (i === lastIndex) {
1687
- if (ret.fetched) {
1688
- return callback(undefined, data);
1689
- } else {
1690
- self.fetch(statement, transaction, Const.DEFAULT_FETCHSIZE, loop);
1691
- }
1692
- }
1693
- }
1694
- }).catch(callback);
1695
- } else if (ret.fetched) {
1696
- callback(undefined, data);
1697
- } else {
1698
- self.fetch(statement, transaction, Const.DEFAULT_FETCHSIZE, loop);
1699
- }
2207
+ function doSynchronousLoop(data, processData, done) {
2208
+ if (!data || !data.length) {
2209
+ done();
2210
+ return;
1700
2211
  }
1701
2212
 
1702
- this.fetch(statement, transaction, Const.DEFAULT_FETCHSIZE, loop);
1703
- };
1704
-
1705
-
1706
- Connection.prototype.openBlob = function(blob, transaction, callback) {
1707
- var msg = this._msg;
1708
- msg.pos = 0;
1709
- msg.addInt(Const.op_open_blob);
1710
- msg.addInt(transaction.handle);
1711
- msg.addQuad(blob);
1712
- this._queueEvent(callback);
1713
- };
1714
-
1715
- Connection.prototype.closeBlob = function(blob, callback) {
1716
- var msg = this._msg;
1717
- msg.pos = 0;
1718
- msg.addInt(Const.op_close_blob);
1719
- msg.addInt(blob.handle);
1720
- this._queueEvent(callback);
1721
- };
1722
-
1723
- Connection.prototype.getSegment = function(blob, callback) {
1724
- var msg = this._msg;
1725
- msg.pos = 0;
1726
- msg.addInt(Const.op_get_segment);
1727
- msg.addInt(blob.handle);
1728
- msg.addInt(1024); // buffer length
1729
- msg.addInt(0); // ???
1730
- this._queueEvent(callback);
1731
- };
1732
-
1733
- Connection.prototype.createBlob2 = function (transaction, callback) {
1734
- var msg = this._msg;
1735
- msg.pos = 0;
1736
- msg.addInt(Const.op_create_blob2);
1737
- msg.addInt(0);
1738
- msg.addInt(transaction.handle);
1739
- msg.addInt(0);
1740
- msg.addInt(0);
1741
- this._queueEvent(callback);
1742
- };
1743
-
1744
- Connection.prototype.batchSegments = function(blob, buffer, callback){
1745
- var msg = this._msg;
1746
- var blr = this._blr;
1747
- msg.pos = 0;
1748
- blr.pos = 0;
1749
- msg.addInt(Const.op_batch_segments);
1750
- msg.addInt(blob.handle);
1751
- msg.addInt(buffer.length + 2);
1752
- blr.addBuffer(buffer);
1753
- msg.addBlr(blr);
1754
- this._queueEvent(callback);
1755
- };
1756
-
1757
- Connection.prototype.svcattach = function (options, callback, svc) {
1758
- this._lowercase_keys = options.lowercase_keys || Const.DEFAULT_LOWERCASE_KEYS;
1759
- var database = options.database || options.filename;
1760
- var user = options.user || Const.DEFAULT_USER;
1761
- var password = options.password || Const.DEFAULT_PASSWORD;
1762
- var role = options.role;
1763
- var msg = this._msg;
1764
- var blr = this._blr;
1765
- msg.pos = 0;
1766
- blr.pos = 0;
1767
-
1768
- blr.addBytes([Const.isc_dpb_version2, Const.isc_dpb_version2]);
1769
- blr.addString(Const.isc_dpb_lc_ctype, 'UTF8', Const.DEFAULT_ENCODING);
1770
- blr.addString(Const.isc_dpb_user_name, user, Const.DEFAULT_ENCODING);
1771
- blr.addString(Const.isc_dpb_password, password, Const.DEFAULT_ENCODING);
1772
- blr.addByte(Const.isc_dpb_dummy_packet_interval);
1773
- blr.addByte(4);
1774
- blr.addBytes([120, 10, 0, 0]); // FROM DOT NET PROVIDER
1775
- if (role)
1776
- blr.addString(Const.isc_dpb_sql_role_name, role, Const.DEFAULT_ENCODING);
1777
-
1778
- msg.addInt(Const.op_service_attach);
1779
- msg.addInt(0);
1780
- msg.addString(Const.DEFAULT_SVC_NAME, Const.DEFAULT_ENCODING); // only local for moment
1781
- msg.addBlr(this._blr);
1782
-
1783
- var self = this;
1784
-
1785
- function cb(err, ret) {
1786
-
1787
- if (err) {
1788
- doError(err, callback);
1789
- return;
1790
- }
1791
-
1792
- self.svchandle = ret.handle;
1793
- if (callback)
1794
- callback(undefined, ret);
1795
- }
2213
+ const loop = (index) => {
2214
+ processData(data[index], index, (err) => {
2215
+ if (err) {
2216
+ done(err);
2217
+ return;
2218
+ }
1796
2219
 
1797
- // For reconnect
1798
- if (svc) {
1799
- svc.connection = this;
1800
- cb.response = svc;
1801
- } else {
1802
- cb.response = new ServiceManager(this);
1803
- cb.response.removeAllListeners('error');
1804
- cb.response.on('error', noop);
1805
- }
2220
+ const nextIndex = index + 1;
2221
+ if (nextIndex < data.length) {
2222
+ loop(nextIndex);
2223
+ } else {
2224
+ done();
2225
+ }
2226
+ });
2227
+ };
1806
2228
 
1807
- this._queueEvent(cb);
2229
+ loop(0);
1808
2230
  }
1809
2231
 
1810
- Connection.prototype.svcstart = function (spbaction, callback) {
1811
- var msg = this._msg;
1812
- var blr = this._blr;
1813
- msg.pos = 0;
1814
- msg.addInt(Const.op_service_start);
1815
- msg.addInt(this.svchandle);
1816
- msg.addInt(0)
1817
- msg.addBlr(spbaction);
1818
- this._queueEvent(callback);
1819
- }
2232
+ function executeStreamRow(custom, row, index, output, next) {
2233
+ let done = false;
2234
+ const finish = (err) => {
2235
+ if (done) {
2236
+ return;
2237
+ }
2238
+ done = true;
2239
+ next(err);
2240
+ };
1820
2241
 
1821
- Connection.prototype.svcquery = function (spbquery, resultbuffersize, timeout,callback) {
1822
- if (resultbuffersize > Const.MAX_BUFFER_SIZE) {
1823
- doError(new Error('Buffer is too big'), callback);
1824
- return;
2242
+ try {
2243
+ const ret = custom.on(row, index, output, finish);
2244
+ if (custom.on.length < 4) {
2245
+ if (ret && typeof ret.then === 'function') {
2246
+ ret.then(() => finish()).catch(finish);
2247
+ } else {
2248
+ finish();
2249
+ }
2250
+ } else if (ret && typeof ret.then === 'function') {
2251
+ ret.catch(finish);
2252
+ }
2253
+ } catch (err) {
2254
+ finish(err);
1825
2255
  }
1826
-
1827
- var msg = this._msg;
1828
- var blr = this._blr;
1829
- msg.pos = 0;
1830
- blr.pos = 0;
1831
- blr.addByte(Const.isc_spb_current_version);
1832
- //blr.addByteInt32(Const.isc_info_svc_timeout, timeout);
1833
- msg.addInt(Const.op_service_info);
1834
- msg.addInt(this.svchandle);
1835
- msg.addInt(0);
1836
- msg.addBlr(blr);
1837
- blr.pos = 0
1838
- blr.addBytes(spbquery);
1839
- msg.addBlr(blr);
1840
- msg.addInt(resultbuffersize);
1841
- this._queueEvent(callback);
1842
- }
1843
-
1844
- Connection.prototype.svcdetach = function (callback) {
1845
- var self = this;
1846
-
1847
- if (self._isClosed)
1848
- return;
1849
-
1850
- self._isUsed = false;
1851
- self._isDetach = true;
1852
-
1853
- var msg = self._msg;
1854
-
1855
- msg.pos = 0;
1856
- msg.addInt(Const.op_service_detach);
1857
- msg.addInt(this.svchandle); // Database Object ID
1858
-
1859
- self._queueEvent(function (err, ret) {
1860
- delete (self.svchandle);
1861
- if (callback)
1862
- callback(err, ret);
1863
- });
1864
2256
  }
1865
2257
 
1866
2258
  function bufferReader(buffer, max, writer, cb, beg, end) {
@@ -1887,79 +2279,31 @@ function bufferReader(buffer, max, writer, cb, beg, end) {
1887
2279
  });
1888
2280
  }
1889
2281
 
1890
- Connection.prototype.auxConnection = function (callback) {
1891
- var self = this;
1892
- if (self._isClosed)
1893
- return this.throwClosed(callback);
1894
- var msg = self._msg;
1895
- msg.pos = 0;
1896
- msg.addInt(Const.op_connect_request);
1897
- msg.addInt(1); // async
1898
- msg.addInt(self.dbhandle);
1899
- msg.addInt(0);
1900
- function cb(err, ret) {
1901
-
1902
- if (err) {
1903
- doError(err, callback);
1904
- return;
1905
- }
1906
-
1907
- var socket_info = {
1908
- family: ret.buffer.readInt16BE(0),
1909
- port: ret.buffer.readUInt16BE(2),
1910
- host: ret.buffer.readUInt8(4) + '.' + ret.buffer.readUInt8(5) + '.' + ret.buffer.readUInt8(6) + '.' + ret.buffer.readUInt8(7)
1911
- }
1912
-
1913
- callback(undefined, socket_info);
1914
- }
1915
- this._queueEvent(cb);
1916
- }
1917
-
1918
- Connection.prototype.queEvents = function (events, eventid, callback) {
1919
- var self = this;
1920
- if (this._isClosed)
1921
- return this.throwClosed(callback);
1922
- var msg = this._msg;
1923
- var blr = this._blr;
1924
- blr.pos = 0;
1925
- msg.pos = 0;
1926
- msg.addInt(Const.op_que_events);
1927
- msg.addInt(this.dbhandle);
1928
- // prepare EPB
1929
- blr.addByte(1) // epb_version
1930
- for (var event in events) {
1931
- var event_buffer = new Buffer(event, 'UTF8');
1932
- blr.addByte(event_buffer.length);
1933
- blr.addBytes(event_buffer);
1934
- blr.addInt32(events[event]);
2282
+ /**
2283
+ * Parse dbCryptConfig option and convert to bytes.
2284
+ * Supports:
2285
+ * - "base64:<value>" - decodes base64 to bytes
2286
+ * - plain string - encodes as UTF-8
2287
+ * - undefined/null/empty - returns empty buffer
2288
+ */
2289
+ function parseDbCryptConfig(config) {
2290
+ if (!config) {
2291
+ return Buffer.alloc(0);
1935
2292
  }
1936
- msg.addBlr(blr); // epb
1937
- msg.addInt(0); // ast
1938
- msg.addInt(0); // args
1939
- msg.addInt(eventid);
1940
- this._queueEvent(callback);
1941
- }
1942
2293
 
1943
- Connection.prototype.closeEvents = function (eventid, callback) {
1944
- var self = this;
1945
- if (this._isClosed)
1946
- return this.throwClosed(callback);
1947
- var msg = self._msg;
1948
- msg.pos = 0;
1949
- msg.addInt(Const.op_cancel_events);
1950
- msg.addInt(self.dbhandle);
1951
- msg.addInt(eventid);
1952
-
1953
- function cb(err, ret) {
1954
- if (err) {
1955
- doError(err, callback);
1956
- return;
2294
+ // Check if it's a base64 encoded value
2295
+ if (config.startsWith('base64:')) {
2296
+ const base64Value = config.substring(7);
2297
+ try {
2298
+ return Buffer.from(base64Value, 'base64');
2299
+ } catch (e) {
2300
+ console.error('Failed to decode base64 dbCryptConfig, returning empty buffer:', e);
2301
+ return Buffer.alloc(0);
1957
2302
  }
1958
-
1959
- callback(err);
1960
2303
  }
1961
2304
 
1962
- this._queueEvent(cb);
2305
+ // Plain string - encode as UTF-8
2306
+ return Buffer.from(config, 'utf8');
1963
2307
  }
1964
2308
 
1965
2309
  module.exports = Connection;