node-firebird 1.1.10 → 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.
@@ -22,184 +22,1519 @@ const Socket = require("./socket");
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 = new Socket(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
- if (options && options.blobChunkSize > 65535) options.blobChunkSize = 65535;
43
- this.options = options;
44
- this._bind_events(host, port, callback);
45
- this.error;
46
- this._retry_connection_id;
47
- this._retry_connection_interval = options.retryConnectionInterval || 1000;
48
- this._max_cached_query = options.maxCachedQuery || -1;
49
- this._cache_query = options.cacheQuery?{}:null;
50
- this._messageFile = options.messageFile || path.join(__dirname, 'firebird.msg');
51
- };
52
-
53
- Connection.prototype._setcachedquery = function (query, statement) {
54
- if (this._cache_query){
55
- if (this._max_cached_query === -1 || this._max_cached_query > Object.keys(this._cache_query).length){
56
- 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
+ }
57
60
  }
61
+
62
+
63
+ }
64
+
65
+
66
+ getCachedQuery(query) {
67
+ return this._cache_query ? this._cache_query[query] : null;
68
+ }
69
+
70
+
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
+ });
58
200
  }
59
201
 
60
202
 
61
- };
203
+ disconnect() {
204
+ this._socket.end();
205
+ }
206
+
62
207
 
63
- Connection.prototype.getCachedQuery = function (query) {
64
- return this._cache_query ? this._cache_query[query] : null;
65
- };
66
208
 
67
- Connection.prototype._bind_events = function(host, port, callback) {
68
209
 
69
- var self = this;
70
210
 
71
- self._socket.on('close', function() {
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
+ }
72
236
 
73
- if (!self._isOpened || self._isDetach) {
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.'));
74
255
  return;
75
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
+ }
326
+
327
+ // Check for pending accept from op_cond_accept flow
328
+ if (self._pendingAccept) {
329
+ ret = self._pendingAccept;
330
+ delete self._pendingAccept;
331
+ }
76
332
 
77
- self._isOpened = false;
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
+ }
78
352
 
79
- if (!self.db) {
80
353
  if (callback)
81
- callback(self.error);
354
+ callback(undefined, ret);
355
+ }
356
+
357
+ this._queueEvent(cb);
358
+ }
359
+
360
+
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);
82
367
  return;
83
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
+ }
84
440
 
85
- self._retry_connection_id = setTimeout(function() {
86
- self._socket.removeAllListeners();
87
- self._socket = null;
88
441
 
89
- var ctx = new Connection(host, port, function(err) {
90
- ctx.connect(self.options, function(err) {
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);
461
+ if (callback)
462
+ callback(err, ret);
463
+ });
464
+ }
91
465
 
92
- if (err) {
93
- self.db.emit('error', err);
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);
471
+ return;
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
+
544
+
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
+ }
562
+
563
+
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
+ });
94
948
  return;
95
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
+ }
96
1108
 
97
- ctx.attach(self.options, function(err) {
98
1109
 
99
- if (err) {
100
- 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);
101
1240
  return;
102
1241
  }
103
1242
 
104
- ctx._queue = ctx._queue.concat(self._queue);
105
- ctx._pending = ctx._pending.concat(self._pending);
106
- 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
+ }
107
1252
 
108
- }, self.db);
109
- });
1253
+ if (ret && ret.fetched) {
1254
+ callback(undefined, data);
1255
+ } else {
1256
+ self.fetch(statement, transaction, Const.DEFAULT_FETCHSIZE, loop);
1257
+ }
1258
+ };
110
1259
 
111
- Object.assign(self, ctx);
1260
+ this.fetch(statement, transaction, Const.DEFAULT_FETCHSIZE, loop);
1261
+ }
112
1262
 
113
- }, self.options, self.db);
114
- }, self._retry_connection_interval);
115
1263
 
116
- });
117
1264
 
118
- 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
+ }
119
1273
 
120
- self.error = e;
121
1274
 
122
- if (self.db)
123
- self.db.emit('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
+ }
124
1282
 
125
- if (callback)
126
- callback(e);
127
1283
 
128
- });
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
+ }
129
1293
 
130
- self._socket.on('connect', function() {
131
- self._isClosed = false;
132
- self._isOpened = true;
133
- if (callback)
134
- callback();
135
- });
136
1294
 
137
- self._socket.on('data', function (data) {
138
- var xdr;
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
+ }
1305
+
139
1306
 
140
- if (!self._xdr) {
141
- 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;
142
1372
  } else {
143
- xdr = new XdrReader(Buffer.concat([self._xdr.buffer, data], self._xdr.buffer.length + data.length));
144
- delete (self._xdr);
1373
+ cb.response = new ServiceManager(this);
1374
+ cb.response.removeAllListeners('error');
1375
+ cb.response.on('error', noop);
145
1376
  }
1377
+
1378
+ this._queueEvent(cb);
1379
+ }
146
1380
 
147
- while (xdr.pos < xdr.buffer.length) {
148
- var cb = self._queue[0], pos = xdr.pos;
149
1381
 
150
- 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
+ }
151
1392
 
152
- if (err) {
153
- xdr.buffer = xdr.buffer.slice(pos);
154
- xdr.pos = 0;
155
- self._xdr = xdr;
156
1393
 
157
- if (self.accept.protocolMinimumType === Const.ptype_lazy_send && self._queue.length > 0) {
158
- self._queue[0].lazy_count = 2;
159
- }
160
- return;
161
- }
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
+ }
1416
+
1417
+
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
+ }
162
1439
 
163
- // remove the op flag, needed for partial packet
164
- if (xdr.r) {
165
- delete (xdr.r);
166
- }
167
1440
 
168
- self._queue.shift();
169
- self._pending.shift();
170
1441
 
171
- if (obj && obj.status) {
172
- obj.message = lookupMessages(obj.status);
173
- doCallback(obj, cb);
174
- } else {
175
- doCallback(obj, cb);
176
- }
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;
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);
1466
+ }
1467
+ this._queueEvent(cb);
1468
+ }
177
1469
 
178
- });
179
1470
 
180
- if (xdr.pos === 0) {
181
- break;
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;
182
1501
  }
1502
+
1503
+ callback(null, ret);
183
1504
  }
1505
+
1506
+ this._queueEvent(cb);
1507
+ console.log('[Connection] queEvents: Event queued');
1508
+ }
184
1509
 
185
- if (!self._detachAuto || self._pending.length !== 0) {
186
- return;
187
- }
188
1510
 
189
- clearTimeout(self._detachTimeout);
190
- self._detachTimeout = setTimeout(function () {
191
- self.db.detach(self._detachCallback);
192
- self._detachAuto = false;
193
- }, 100);
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
+ }
194
1535
 
195
- });
196
1536
  }
197
1537
 
198
- Connection.prototype.disconnect = function() {
199
- this._socket.end();
200
- };
201
-
202
-
203
1538
  function decodeResponse(data, callback, cnx, lowercase_keys, cb) {
204
1539
  try {
205
1540
  do {
@@ -457,6 +1792,17 @@ function decodeResponse(data, callback, cnx, lowercase_keys, cb) {
457
1792
  cnx._socket.enableCompression();
458
1793
  }
459
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
+
460
1806
  return cb(undefined, accept);
461
1807
  case Const.op_cont_auth:
462
1808
  var d = new BlrReader(data.readArray());
@@ -477,646 +1823,116 @@ function decodeResponse(data, callback, cnx, lowercase_keys, cb) {
477
1823
  cnx.sendOpContAuth(
478
1824
  cnx.accept.authData,
479
1825
  Const.DEFAULT_ENCODING,
480
- pluginName
481
- );
482
-
483
- return {error: new Error('login')};
484
- }
485
- }
486
-
487
- return data.accept;
488
- default:
489
- return cb(new Error('Unexpected:' + r));
490
- }
491
- } catch (err) {
492
- if (err instanceof RangeError) {
493
- return cb(err);
494
- }
495
- throw err;
496
- }
497
- }
498
-
499
- function parseOpResponse(data, response, cb) {
500
- var handle = data.readInt();
501
-
502
- if (!response.handle) {
503
- response.handle = handle;
504
- }
505
-
506
- var oid = data.readQuad();
507
- if (oid.low || oid.high) {
508
- response.oid = oid;
509
- }
510
-
511
- var buf = data.readArray();
512
- if (buf) {
513
- response.buffer = buf;
514
- }
515
-
516
- var num, op, item = {};
517
- while (true) {
518
- op = data.readInt();
519
-
520
- switch (op) {
521
- case Const.isc_arg_end:
522
- return cb ? cb(undefined, response) : response;
523
- case Const.isc_arg_gds:
524
- num = data.readInt();
525
- if (!num) {
526
- break;
527
- }
528
-
529
- item = {gdscode: num};
530
-
531
- if (response.status) {
532
- response.status.push(item);
533
- } else {
534
- response.status = [item];
535
- }
536
-
537
- break;
538
- case Const.isc_arg_string:
539
- case Const.isc_arg_interpreted:
540
- case Const.isc_arg_sql_state:
541
- if (item.params) {
542
- var str = data.readString(Const.DEFAULT_ENCODING);
543
- item.params.push(str);
544
- } else {
545
- item.params = [data.readString(Const.DEFAULT_ENCODING)];
546
- }
547
-
548
- break;
549
- case Const.isc_arg_number:
550
- num = data.readInt();
551
-
552
- if (item.params) {
553
- item.params.push(num);
554
- } else {
555
- item.params = [num];
556
- }
557
-
558
- if (item.gdscode === Const.isc_sqlerr) {
559
- response.sqlcode = num;
560
- }
561
-
562
- break;
563
- default:
564
- if (cb) {
565
- cb(new Error('Unexpected: ' + op))
566
- } else {
567
- throw new Error('Unexpected: ' + op);
568
- }
569
- }
570
- }
571
- }
572
-
573
- Connection.prototype.sendOpContAuth = function(authData, authDataEnc, pluginName) {
574
- var msg = this._msg;
575
- msg.pos = 0;
576
-
577
- msg.addInt(Const.op_cont_auth);
578
- msg.addString(authData, authDataEnc);
579
- msg.addString(pluginName, Const.DEFAULT_ENCODING)
580
- msg.addString(Const.AUTH_PLUGIN_LIST.join(','), Const.DEFAULT_ENCODING);
581
- // msg.addInt(0); // p_list
582
- msg.addInt(0); // keys
583
-
584
- this._socket.write(msg.getData());
585
- }
586
-
587
- Connection.prototype._queueEvent = function(callback, defer = false){
588
- var self = this;
589
-
590
- if (self._isClosed) {
591
- if (callback)
592
- callback(new Error('Connection is closed.'));
593
- return;
594
- }
595
-
596
- const canDefer = defer && this.accept.protocolVersion >= Const.PROTOCOL_VERSION11;
597
-
598
- self._socket.write(self._msg.getData(), canDefer);
599
- if (canDefer && callback) {
600
- callback();
601
- } else {
602
- self._queue.push(callback);
603
- }
604
- };
605
-
606
- Connection.prototype.connect = function (options, callback) {
607
- var pluginName = options.manager ? Const.AUTH_PLUGIN_LEGACY : options.pluginName || Const.AUTH_PLUGIN_LIST[0]; // TODO Srp for service
608
- var msg = this._msg;
609
- var blr = this._blr;
610
-
611
- this._pending.push('connect');
612
-
613
- msg.pos = 0;
614
- blr.pos = 0;
615
-
616
- blr.addString(Const.CNCT_login, options.user, Const.DEFAULT_ENCODING);
617
- blr.addString(Const.CNCT_plugin_name, pluginName, Const.DEFAULT_ENCODING);
618
- blr.addString(Const.CNCT_plugin_list, Const.AUTH_PLUGIN_LIST.join(','), Const.DEFAULT_ENCODING);
619
-
620
- var specificData = '';
621
- if (Const.AUTH_PLUGIN_SRP_LIST.indexOf(pluginName) > -1) {
622
- this.clientKeys = srp.clientSeed();
623
- specificData = this.clientKeys.public.toString(16);
624
- blr.addMultiblockPart(Const.CNCT_specific_data, specificData, Const.DEFAULT_ENCODING);
625
- } else if (pluginName === Const.AUTH_PLUGIN_LEGACY) {
626
- specificData = crypt.crypt(options.password, Const.LEGACY_AUTH_SALT).substring(2);
627
- blr.addMultiblockPart(Const.CNCT_specific_data, specificData, Const.DEFAULT_ENCODING);
628
- } else {
629
- doError(new Error('Invalide auth plugin \'' + pluginName + '\''), callback);
630
- return;
631
- }
632
- blr.addBytes([Const.CNCT_client_crypt, 4, Const.WIRE_CRYPT_DISABLE, 0, 0, 0]); // WireCrypt = Disabled
633
- blr.addString(Const.CNCT_user, os.userInfo().username || 'Unknown', Const.DEFAULT_ENCODING);
634
- blr.addString(Const.CNCT_host, os.hostname(), Const.DEFAULT_ENCODING);
635
- blr.addBytes([Const.CNCT_user_verification, 0]);
636
-
637
- msg.addInt(Const.op_connect);
638
- msg.addInt(Const.op_attach);
639
- msg.addInt(Const.CONNECT_VERSION3);
640
- msg.addInt(Const.ARCHITECTURE_GENERIC);
641
- msg.addString(options.database || options.filename, Const.DEFAULT_ENCODING);
642
- msg.addInt(Const.SUPPORTED_PROTOCOL.length); // Count of Protocol version understood count.
643
- msg.addBlr(this._blr);
644
-
645
- for (var protocol of Const.SUPPORTED_PROTOCOL) {
646
- msg.addInt(protocol[0]); // Version
647
- msg.addInt(protocol[1]); // Architecture
648
- msg.addInt(protocol[2]); // Min type
649
- if (protocol[0] >= Const.PROTOCOL_VERSION13 && options.wireCompression) {
650
- msg.addInt(protocol[3] | Const.pflag_compress); // Max type with compress flag
651
- } else {
652
- msg.addInt(protocol[3]); // Max type
653
- }
654
- msg.addInt(protocol[4]); // Preference weight
655
- }
656
-
657
- var self = this;
658
- function cb(err, ret) {
659
- if (err) {
660
- doError(err, callback);
661
- return;
662
- }
663
-
664
- self.accept = ret;
665
- if (callback)
666
- callback(undefined, ret);
667
- }
668
-
669
- this._queueEvent(cb);
670
- };
671
-
672
- Connection.prototype.attach = function (options, callback, db) {
673
- this._lowercase_keys = options.lowercase_keys || Const.DEFAULT_LOWERCASE_KEYS;
674
-
675
- var database = options.database || options.filename;
676
- if (database == null || database.length === 0) {
677
- doError(new Error('No database specified'), callback);
678
- return;
679
- }
680
-
681
- var user = options.user || Const.DEFAULT_USER;
682
- var password = options.password || Const.DEFAULT_PASSWORD;
683
- var role = options.role;
684
- var self = this;
685
- var msg = this._msg;
686
- var blr = this._blr;
687
- msg.pos = 0;
688
- blr.pos = 0;
689
-
690
- blr.addByte(Const.isc_dpb_version1);
691
- blr.addString(Const.isc_dpb_lc_ctype, options.encoding || 'UTF8', Const.DEFAULT_ENCODING);
692
- blr.addString(Const.isc_dpb_user_name, user, Const.DEFAULT_ENCODING);
693
- if (options.password && !this.accept.authData) {
694
- if (this.accept.protocolVersion < Const.PROTOCOL_VERSION13) {
695
- if (this.accept.protocolVersion === Const.PROTOCOL_VERSION10) {
696
- blr.addString(Const.isc_dpb_password, password, Const.DEFAULT_ENCODING);
697
- } else {
698
- blr.addString(Const.isc_dpb_password_enc, crypt.crypt(password, Const.LEGACY_AUTH_SALT).substring(2), Const.DEFAULT_ENCODING);
699
- }
700
- }
701
- }
702
-
703
- if (role)
704
- blr.addString(Const.isc_dpb_sql_role_name, role, Const.DEFAULT_ENCODING);
705
-
706
- blr.addBytes([Const.isc_dpb_process_id, 4]);
707
- blr.addInt32(process.pid);
708
-
709
- let processName = process.title || "";
710
- blr.addString(Const.isc_dpb_process_name, processName.length > 255 ? processName.substring(processName.length - 255, processName.length) : processName, Const.DEFAULT_ENCODING);
711
-
712
- if (this.accept.authData) {
713
- blr.addString(Const.isc_dpb_specific_auth_data, this.accept.authData, Const.DEFAULT_ENCODING);
714
- }
715
-
716
- msg.addInt(Const.op_attach);
717
- msg.addInt(0); // Database Object ID
718
- msg.addString(database, Const.DEFAULT_ENCODING);
719
- msg.addBlr(this._blr);
720
-
721
- function cb(err, ret) {
722
- if (err) {
723
- doError(err, callback);
724
- return;
725
- }
726
-
727
- self.dbhandle = ret.handle;
728
- if (callback)
729
- callback(undefined, ret);
730
- }
731
-
732
- // For reconnect
733
- if (db) {
734
- db.connection = this;
735
- cb.response = db;
736
- } else {
737
- cb.response = new Database(this);
738
- cb.response.removeAllListeners('error');
739
- cb.response.on('error', noop);
740
- }
741
-
742
- this._queueEvent(cb);
743
- };
744
-
745
- Connection.prototype.detach = function (callback) {
746
-
747
- var self = this;
748
-
749
- if (self._isClosed)
750
- return;
751
-
752
- self._isUsed = false;
753
- self._isDetach = true;
754
-
755
- var msg = self._msg;
756
-
757
- msg.pos = 0;
758
- msg.addInt(Const.op_detach);
759
- msg.addInt(0); // Database Object ID
760
-
761
- self._queueEvent(function(err, ret) {
762
- clearTimeout(self._retry_connection_id);
763
- delete(self.dbhandle);
764
- if (callback)
765
- callback(err, ret);
766
- });
767
- };
768
-
769
- Connection.prototype.createDatabase = function (options, callback) {
770
- var database = options.database || options.filename;
771
- if (database == null || database.length === 0) {
772
- doError(new Error('No database specified'), callback);
773
- return;
774
- }
775
-
776
- var user = options.user || Const.DEFAULT_USER;
777
- var password = options.password || Const.DEFAULT_PASSWORD;
778
- var pageSize = options.pageSize || Const.DEFAULT_PAGE_SIZE;
779
- var role = options.role;
780
- var blr = this._blr;
781
-
782
- blr.pos = 0;
783
- blr.addByte(Const.isc_dpb_version1);
784
- blr.addString(Const.isc_dpb_set_db_charset, 'UTF8', Const.DEFAULT_ENCODING);
785
- blr.addString(Const.isc_dpb_lc_ctype, 'UTF8', Const.DEFAULT_ENCODING);
786
- blr.addString(Const.isc_dpb_user_name, user, Const.DEFAULT_ENCODING);
787
- if (this.accept.protocolVersion < Const.PROTOCOL_VERSION13) {
788
- if (this.accept.protocolVersion === Const.PROTOCOL_VERSION10) {
789
- blr.addString(Const.isc_dpb_password, password, Const.DEFAULT_ENCODING);
790
- } else {
791
- blr.addString(Const.isc_dpb_password_enc, crypt.crypt(password, Const.LEGACY_AUTH_SALT).substring(2), Const.DEFAULT_ENCODING);
792
- }
793
- }
794
- if (role)
795
- blr.addString(Const.isc_dpb_sql_role_name, role, Const.DEFAULT_ENCODING);
796
-
797
- blr.addBytes([Const.isc_dpb_process_id, 4]);
798
- blr.addInt32(process.pid);
799
-
800
- let processName = process.title || "";
801
- blr.addString(Const.isc_dpb_process_name, processName.length > 255 ? processName.substring(processName.length - 255, processName.length) : processName, Const.DEFAULT_ENCODING);
802
-
803
- if (this.accept.authData) {
804
- blr.addString(Const.isc_dpb_specific_auth_data, this.accept.authData, Const.DEFAULT_ENCODING);
805
- }
806
-
807
- blr.addNumeric(Const.isc_dpb_sql_dialect, 3);
808
- blr.addNumeric(Const.isc_dpb_force_write, 1);
809
- blr.addNumeric(Const.isc_dpb_overwrite, 1);
810
- blr.addNumeric(Const.isc_dpb_page_size, pageSize);
811
-
812
- var msg = this._msg;
813
- msg.pos = 0;
814
- msg.addInt(Const.op_create); // op_create
815
- msg.addInt(0); // Database Object ID
816
- msg.addString(database, Const.DEFAULT_ENCODING);
817
- msg.addBlr(blr);
818
-
819
- var self = this;
820
-
821
- function cb(err, ret) {
822
-
823
- if (ret)
824
- self.dbhandle = ret.handle;
825
-
826
- setImmediate(function() {
827
- if (self.db)
828
- self.db.emit('attach', ret);
829
- });
830
-
831
- if (callback)
832
- callback(err, ret);
833
- }
834
-
835
- cb.response = new Database(this);
836
- this._queueEvent(cb);
837
- };
838
-
839
- Connection.prototype.dropDatabase = function (callback) {
840
- var msg = this._msg;
841
- msg.pos = 0;
842
-
843
- msg.addInt(Const.op_drop_database);
844
- msg.addInt(this.dbhandle);
845
-
846
- var self = this;
847
- this._queueEvent(function(err) {
848
- self.detach(function() {
849
- self.disconnect();
850
-
851
- if (callback)
852
- callback(err);
853
- });
854
- });
855
- };
856
-
857
- Connection.prototype.throwClosed = function(callback) {
858
- var err = new Error('Connection is closed.');
859
- this.db.emit('error', err);
860
- if (callback)
861
- callback(err);
862
- return this;
863
- };
864
-
865
- Connection.prototype.startTransaction = function(options, callback) {
866
-
867
- if (typeof(options) === 'function') {
868
- var tmp = options;
869
- options = callback;
870
- callback = tmp;
871
- }
872
-
873
- // Compatibility
874
- if (Array.isArray(options)) {
875
- options = {
876
- isolation: options,
877
- readOnly: (options === Const.ISOLATION_READ_COMMITTED_READ_ONLY),
878
- };
879
- }
880
-
881
- // Default options
882
- options = Object.assign({
883
- autoCommit: false,
884
- autoUndo: true,
885
- isolation: Const.ISOLATION_READ_COMMITTED,
886
- ignoreLimbo: false,
887
- //lock: [],
888
- readOnly: false,
889
- wait: true,
890
- waitTimeout: 0,
891
- }, options);
892
-
893
- if (this._isClosed)
894
- return this.throwClosed(callback);
895
-
896
- // for auto detach
897
- this._pending.push('startTransaction');
898
-
899
- var blr = this._blr;
900
- var msg = this._msg;
901
-
902
- blr.pos = 0;
903
- msg.pos = 0;
904
-
905
- blr.addByte(Const.isc_tpb_version3);
906
- blr.addBytes(options.isolation);
907
- blr.addByte(options.readOnly ? Const.isc_tpb_read : Const.isc_tpb_write);
908
- if (options.wait) {
909
- blr.addByte(Const.isc_tpb_wait);
910
-
911
- if (options.waitTimeout) {
912
- blr.addNumeric(Const.isc_tpb_lock_timeout, options.waitTimeout);
913
- }
914
- } else {
915
- blr.addByte(Const.isc_tpb_nowait);
916
- }
917
- if (!options.autoUndo) {
918
- blr.addByte(Const.isc_tpb_no_auto_undo);
919
- }
920
- if (options.autoCommit) {
921
- blr.addByte(Const.isc_tpb_autocommit);
922
- }
923
- if (options.ignoreLimbo) {
924
- blr.addByte(Const.isc_tpb_ignore_limbo);
925
- }
926
- // TODO
927
- /*if (options.lock.length) {
928
- for (let table of options.lock) {
929
- const lockMode = table.write ? Const.isc_tpb_lock_write : Const.isc_tpb_lock_read;
930
- const lockType = table.protected ? Const.isc_tpb_protected : Const.isc_tpb_shared;
931
-
932
- blr.addString(lockMode, table.table || table, Const.DEFAULT_ENCODING);
933
- blr.addByte(lockType);
934
- }
935
- }*/
936
-
937
- msg.addInt(Const.op_transaction);
938
- msg.addInt(this.dbhandle);
939
- msg.addBlr(blr);
940
- callback.response = new Transaction(this);
941
-
942
- this.db.emit('transaction', options);
943
- this._queueEvent(callback);
944
- };
945
-
946
- Connection.prototype.commit = function (transaction, callback) {
947
-
948
- if (this._isClosed)
949
- return this.throwClosed(callback);
950
-
951
- // for auto detach
952
- this._pending.push('commit');
953
-
954
- var msg = this._msg;
955
- msg.pos = 0;
956
- msg.addInt(Const.op_commit);
957
- msg.addInt(transaction.handle);
958
- this.db.emit('commit');
959
- this._queueEvent(callback);
960
- };
961
-
962
- Connection.prototype.rollback = function (transaction, callback) {
963
-
964
- if (this._isClosed)
965
- return this.throwClosed(callback);
966
-
967
- // for auto detach
968
- this._pending.push('rollback');
969
-
970
- var msg = this._msg;
971
- msg.pos = 0;
972
- msg.addInt(Const.op_rollback);
973
- msg.addInt(transaction.handle);
974
- this.db.emit('rollback');
975
- this._queueEvent(callback);
976
- };
977
-
978
- Connection.prototype.commitRetaining = function (transaction, callback) {
979
-
980
- if (this._isClosed)
981
- throw new Error('Connection is closed.');
982
-
983
- // for auto detach
984
- this._pending.push('commitRetaining');
985
-
986
- var msg = this._msg;
987
- msg.pos = 0;
988
- msg.addInt(Const.op_commit_retaining);
989
- msg.addInt(transaction.handle);
990
- this._queueEvent(callback);
991
- };
992
-
993
- Connection.prototype.rollbackRetaining = function (transaction, callback) {
994
-
995
- if (this._isClosed)
996
- return this.throwClosed(callback);
997
-
998
- // for auto detach
999
- this._pending.push('rollbackRetaining');
1000
-
1001
- var msg = this._msg;
1002
- msg.pos = 0;
1003
- msg.addInt(Const.op_rollback_retaining);
1004
- msg.addInt(transaction.handle);
1005
- this._queueEvent(callback);
1006
- };
1007
-
1008
- Connection.prototype.allocateStatement = function (callback) {
1009
-
1010
- if (this._isClosed)
1011
- return this.throwClosed(callback);
1012
-
1013
- // for auto detach
1014
- this._pending.push('allocateStatement');
1015
-
1016
- var msg = this._msg;
1017
- msg.pos = 0;
1018
- msg.addInt(Const.op_allocate_statement);
1019
- msg.addInt(this.dbhandle);
1020
- callback.response = new Statement(this);
1021
- this._queueEvent(callback);
1022
- };
1023
-
1024
- Connection.prototype.dropStatement = function (statement, callback) {
1025
-
1026
- if (this._isClosed)
1027
- return this.throwClosed(callback);
1028
-
1029
- // for auto detach
1030
- this._pending.push('dropStatement');
1031
-
1032
- var msg = this._msg;
1033
- msg.pos = 0;
1034
- msg.addInt(Const.op_free_statement);
1035
- msg.addInt(statement.handle);
1036
- msg.addInt(Const.DSQL_drop);
1037
-
1038
- this._queueEvent(callback, true);
1039
- };
1826
+ pluginName
1827
+ );
1040
1828
 
1041
- Connection.prototype.closeStatement = function (statement, callback) {
1829
+ return {error: new Error('login')};
1830
+ }
1831
+ }
1042
1832
 
1043
- if (this._isClosed)
1044
- return this.throwClosed(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
+ }
1045
1862
 
1046
- // for auto detach
1047
- this._pending.push('closeStatement');
1048
-
1049
- var msg = this._msg;
1050
- msg.pos = 0;
1051
- msg.addInt(Const.op_free_statement);
1052
- msg.addInt(statement.handle);
1053
- msg.addInt(Const.DSQL_close);
1054
-
1055
- this._queueEvent(callback, true);
1056
- };
1863
+ function parseOpResponse(data, response, cb) {
1864
+ var handle = data.readInt();
1057
1865
 
1058
- Connection.prototype.allocateAndPrepareStatement = function (transaction, query, plan, callback) {
1059
- var self = this;
1060
- var mainCallback = function(err, ret) {
1061
- if (!err) {
1062
- mainCallback.response.handle = ret.handle;
1063
- describe(ret.buffer, mainCallback.response);
1064
- mainCallback.response.query = query;
1065
- self.db.emit('query', query);
1066
- ret = mainCallback.response;
1067
- self._setcachedquery(query, ret);
1068
- }
1866
+ if (!response.handle) {
1867
+ response.handle = handle;
1868
+ }
1069
1869
 
1070
- if (callback)
1071
- callback(err, ret);
1072
- };
1870
+ var oid = data.readQuad();
1871
+ if (oid.low || oid.high) {
1872
+ response.oid = oid;
1873
+ }
1073
1874
 
1074
- // for auto detach
1075
- this._pending.push('allocateAndPrepareStatement');
1875
+ var buf = data.readArray();
1876
+ if (buf) {
1877
+ response.buffer = buf;
1878
+ }
1076
1879
 
1077
- var msg = this._msg;
1078
- var blr = this._blr;
1880
+ var num, op, item = {};
1881
+ while (true) {
1882
+ op = data.readInt();
1079
1883
 
1080
- msg.pos = 0;
1081
- 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
+ }
1082
1892
 
1083
- msg.addInt(Const.op_allocate_statement);
1084
- msg.addInt(this.dbhandle);
1085
- mainCallback.lazy_count = 1;
1893
+ item = {gdscode: num};
1086
1894
 
1087
- blr.addBytes(Const.DESCRIBE);
1088
- if (plan)
1089
- blr.addByte(Const.isc_info_sql_get_plan);
1895
+ if (response.status) {
1896
+ response.status.push(item);
1897
+ } else {
1898
+ response.status = [item];
1899
+ }
1090
1900
 
1091
- msg.addInt(Const.op_prepare_statement);
1092
- msg.addInt(transaction.handle);
1093
- msg.addInt(0xFFFF);
1094
- msg.addInt(3); // dialect = 3
1095
- msg.addString(query, Const.DEFAULT_ENCODING);
1096
- msg.addBlr(blr);
1097
- msg.addInt(65535); // buffer_length
1098
- 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
+ }
1099
1911
 
1100
- mainCallback.response = new Statement(this);
1101
- this._queueEvent(mainCallback);
1102
- };
1912
+ break;
1913
+ case Const.isc_arg_number:
1914
+ num = data.readInt();
1103
1915
 
1104
- Connection.prototype.prepare = function (transaction, query, plan, callback) {
1105
- var self = this;
1916
+ if (item.params) {
1917
+ item.params.push(num);
1918
+ } else {
1919
+ item.params = [num];
1920
+ }
1106
1921
 
1107
- if (this.accept.protocolMinimumType === Const.ptype_lazy_send) { // V11 Statement or higher
1108
- self.allocateAndPrepareStatement(transaction, query, plan, callback);
1109
- } else { // V10 Statement
1110
- self.allocateStatement(function (err, statement) {
1111
- if (err) {
1112
- doError(err, callback);
1113
- return;
1114
- }
1922
+ if (item.gdscode === Const.isc_sqlerr) {
1923
+ response.sqlcode = num;
1924
+ }
1115
1925
 
1116
- self.prepareStatement(transaction, statement, query, plan, callback);
1117
- });
1926
+ break;
1927
+ default:
1928
+ if (cb) {
1929
+ cb(new Error('Unexpected: ' + op))
1930
+ } else {
1931
+ throw new Error('Unexpected: ' + op);
1932
+ }
1933
+ }
1118
1934
  }
1119
- };
1935
+ }
1120
1936
 
1121
1937
  function describe(buff, statement) {
1122
1938
  var br = new BlrReader(buff);
@@ -1218,52 +2034,6 @@ function describe(buff, statement) {
1218
2034
  }
1219
2035
  }
1220
2036
 
1221
- Connection.prototype.prepareStatement = function (transaction, statement, query, plan, callback) {
1222
-
1223
- if (this._isClosed)
1224
- return this.throwClosed(callback);
1225
-
1226
- var msg = this._msg;
1227
- var blr = this._blr;
1228
-
1229
- msg.pos = 0;
1230
- blr.pos = 0;
1231
-
1232
- if (plan instanceof Function) {
1233
- callback = plan;
1234
- plan = false;
1235
- }
1236
-
1237
- blr.addBytes(Const.DESCRIBE);
1238
-
1239
- if (plan)
1240
- blr.addByte(Const.isc_info_sql_get_plan);
1241
-
1242
- msg.addInt(Const.op_prepare_statement);
1243
- msg.addInt(transaction.handle);
1244
- msg.addInt(statement.handle);
1245
- msg.addInt(3); // dialect = 3
1246
- msg.addString(query, Const.DEFAULT_ENCODING);
1247
- msg.addBlr(blr);
1248
- msg.addInt(65535); // buffer_length
1249
-
1250
- var self = this;
1251
- this._queueEvent(function(err, ret) {
1252
-
1253
- if (!err) {
1254
- describe(ret.buffer, statement);
1255
- statement.query = query;
1256
- self.db.emit('query', query);
1257
- ret = statement;
1258
- self._setcachedquery(query, ret);
1259
- }
1260
-
1261
- if (callback)
1262
- callback(err, ret);
1263
- });
1264
-
1265
- };
1266
-
1267
2037
  function CalcBlr(blr, xsqlda) {
1268
2038
  blr.addBytes([Const.blr_version5, Const.blr_begin, Const.blr_message, 0]); // + message number
1269
2039
  blr.addWord(xsqlda.length * 2);
@@ -1278,298 +2048,6 @@ function CalcBlr(blr, xsqlda) {
1278
2048
  blr.addByte(Const.blr_eoc);
1279
2049
  }
1280
2050
 
1281
- Connection.prototype.executeStatement = function(transaction, statement, params, callback, custom) {
1282
-
1283
- if (this._isClosed)
1284
- return this.throwClosed(callback);
1285
-
1286
- // for auto detach
1287
- this._pending.push('executeStatement');
1288
-
1289
- if (params instanceof Function) {
1290
- callback = params;
1291
- params = undefined;
1292
- }
1293
-
1294
- var self = this;
1295
-
1296
- var op = Const.op_execute;
1297
- if (
1298
- this.accept.protocolVersion >= Const.PROTOCOL_VERSION13 &&
1299
- statement.type === Const.isc_info_sql_stmt_exec_procedure &&
1300
- statement.output.length
1301
- ) {
1302
- op = Const.op_execute2;
1303
- }
1304
-
1305
- function PrepareParams(params, input, callback) {
1306
-
1307
- var value, meta;
1308
- var ret = new Array(params.length);
1309
- var wait = params.length;
1310
-
1311
- function done() {
1312
- wait--;
1313
- if (wait === 0)
1314
- callback(ret);
1315
- }
1316
-
1317
- function putBlobData(index, value, callback) {
1318
-
1319
- self.createBlob2(transaction, function(err, blob) {
1320
-
1321
- var b;
1322
- var isStream = value.readable;
1323
-
1324
- if (Buffer.isBuffer(value))
1325
- b = value;
1326
- else if (typeof(value) === 'string')
1327
- b = Buffer.from(value, Const.DEFAULT_ENCODING);
1328
- else if (!isStream)
1329
- b = Buffer.from(JSON.stringify(value), Const.DEFAULT_ENCODING);
1330
-
1331
- // Use configured transfer size or default to 1024
1332
- var chunkSize = self.options.blobChunkSize || 1024;
1333
-
1334
- if (Buffer.isBuffer(b)) {
1335
- bufferReader(b, chunkSize, function (b, next) {
1336
- self.batchSegments(blob, b, next);
1337
- }, function() {
1338
- ret[index] = new Xsql.SQLParamQuad(blob.oid);
1339
- self.closeBlob(blob, callback);
1340
- });
1341
- return;
1342
- }
1343
-
1344
- var isReading = false;
1345
- var isEnd = false;
1346
-
1347
- value.on('data', function(chunk) {
1348
- // Optimization: If chunk is smaller than transfer size, send directly
1349
- if (chunk.length <= chunkSize) {
1350
- self.batchSegments(blob, chunk, function () {
1351
- if (isEnd && !isReading) {
1352
- ret[index] = new Xsql.SQLParamQuad(blob.oid);
1353
- self.closeBlob(blob, callback);
1354
- }
1355
- });
1356
- return;
1357
- }
1358
-
1359
- value.pause();
1360
- isReading = true;
1361
- bufferReader(chunk, chunkSize, function (b, next) {
1362
- self.batchSegments(blob, b, next);
1363
- }, function() {
1364
- isReading = false;
1365
-
1366
- if (isEnd) {
1367
- ret[index] = new Xsql.SQLParamQuad(blob.oid);
1368
- self.closeBlob(blob, callback);
1369
- } else
1370
- value.resume();
1371
- });
1372
- });
1373
-
1374
- value.on('end', function() {
1375
- isEnd = true;
1376
- if (isReading)
1377
- return;
1378
- // If we are not currently reading (paused), close immediately
1379
- // If we are reading, the callback in batchSegments/bufferReader will handle closure
1380
- if (!isReading) {
1381
- ret[index] = new Xsql.SQLParamQuad(blob.oid);
1382
- self.closeBlob(blob, callback);
1383
- }
1384
- });
1385
- });
1386
- }
1387
-
1388
- for (var i = 0, length = params.length; i < length; i++) {
1389
- value = params[i];
1390
- meta = input[i];
1391
-
1392
- if (value === null || value === undefined) {
1393
- switch (meta.type) {
1394
- case Const.SQL_VARYING:
1395
- case Const.SQL_NULL:
1396
- case Const.SQL_TEXT:
1397
- ret[i] = new Xsql.SQLParamString(null);
1398
- break;
1399
- case Const.SQL_DOUBLE:
1400
- case Const.SQL_FLOAT:
1401
- case Const.SQL_D_FLOAT:
1402
- ret[i] = new Xsql.SQLParamDouble(null);
1403
- break;
1404
- case Const.SQL_TYPE_DATE:
1405
- case Const.SQL_TYPE_TIME:
1406
- case Const.SQL_TIMESTAMP:
1407
- ret[i] = new Xsql.SQLParamDate(null);
1408
- break;
1409
- case Const.SQL_BLOB:
1410
- case Const.SQL_ARRAY:
1411
- case Const.SQL_QUAD:
1412
- ret[i] = new Xsql.SQLParamQuad(null);
1413
- break;
1414
- case Const.SQL_LONG:
1415
- case Const.SQL_SHORT:
1416
- case Const.SQL_INT64:
1417
- case Const.SQL_BOOLEAN:
1418
- ret[i] = new Xsql.SQLParamInt(null);
1419
- break;
1420
- default:
1421
- ret[i] = null;
1422
- }
1423
- done();
1424
- } else {
1425
- switch (meta.type) {
1426
- case Const.SQL_BLOB:
1427
- putBlobData(i, value, done);
1428
- break;
1429
-
1430
- case Const.SQL_TIMESTAMP:
1431
- case Const.SQL_TYPE_DATE:
1432
- case Const.SQL_TYPE_TIME:
1433
-
1434
- if (value instanceof Date)
1435
- ret[i] = new Xsql.SQLParamDate(value);
1436
- else if (typeof(value) === 'string')
1437
- ret[i] = new Xsql.SQLParamDate(parseDate(value));
1438
- else
1439
- ret[i] = new Xsql.SQLParamDate(new Date(value));
1440
-
1441
- done();
1442
- break;
1443
-
1444
- default:
1445
- switch (typeof value) {
1446
- case 'bigint':
1447
- ret[i] = new Xsql.SQLParamInt128(value);
1448
- break;
1449
- case 'number':
1450
- if (value % 1 === 0) {
1451
- if (value >= Const.MIN_INT && value <= Const.MAX_INT)
1452
- ret[i] = new Xsql.SQLParamInt(value);
1453
- else
1454
- ret[i] = new Xsql.SQLParamInt64(value);
1455
- } else
1456
- ret[i] = new Xsql.SQLParamDouble(value);
1457
- break;
1458
- case 'string':
1459
- ret[i] = new Xsql.SQLParamString(value);
1460
- break;
1461
- case 'boolean':
1462
- ret[i] = new Xsql.SQLParamBool(value);
1463
- break;
1464
- default:
1465
- //throw new Error('Unexpected parametter: ' + JSON.stringify(params) + ' - ' + JSON.stringify(input));
1466
- ret[i] = new Xsql.SQLParamString(value.toString());
1467
- break;
1468
- }
1469
- done();
1470
- }
1471
- }
1472
- }
1473
- }
1474
-
1475
- var input = statement.input;
1476
-
1477
- if (input.length) {
1478
-
1479
- if (!(params instanceof Array)) {
1480
- if (params !== undefined)
1481
- params = [params];
1482
- else
1483
- params = [];
1484
- }
1485
-
1486
- if (params.length !== input.length) {
1487
- self._pending.pop();
1488
- callback(new Error('Expected parameters: (params=' + params.length + ' vs. expected=' + input.length + ') - ' + statement.query));
1489
- return;
1490
- }
1491
-
1492
- PrepareParams(params, input, function(prms) {
1493
- self.sendExecute(op, statement, transaction, callback, prms);
1494
- });
1495
-
1496
- return;
1497
- }
1498
-
1499
- this.sendExecute(op, statement, transaction, callback);
1500
- };
1501
-
1502
- Connection.prototype.sendExecute = function (op, statement, transaction, callback, parameters) {
1503
- var msg = this._msg;
1504
- var blr = this._blr;
1505
- msg.pos = 0;
1506
- blr.pos = 0;
1507
-
1508
- msg.addInt(op);
1509
- msg.addInt(statement.handle);
1510
- msg.addInt(transaction.handle);
1511
-
1512
- if (parameters && parameters.length) {
1513
- CalcBlr(blr, parameters);
1514
- msg.addBlr(blr); // params blr
1515
- msg.addInt(0); // message number
1516
- msg.addInt(1); // param count
1517
-
1518
- if (this.accept.protocolVersion >= Const.PROTOCOL_VERSION13) {
1519
- // start with null indicator bitmap
1520
- var nullBits = new BitSet();
1521
-
1522
- for (var i = 0; i < parameters.length; i++) {
1523
- nullBits.set(i, (parameters[i].value === null) & 1);
1524
- }
1525
-
1526
- var nullBuffer = nullBits.toBuffer();
1527
- var requireBytes = Math.floor((parameters.length + 7) / 8);
1528
- var remainingBytes = requireBytes - nullBuffer.length;
1529
-
1530
- if (nullBuffer.length) {
1531
- msg.addBuffer(nullBuffer);
1532
- }
1533
- if (remainingBytes > 0) {
1534
- msg.addBuffer(Buffer.alloc(remainingBytes));
1535
- }
1536
- msg.addAlignment(requireBytes);
1537
-
1538
- for(var i = 0; i < parameters.length; i++) {
1539
- if (parameters[i].value !== null) {
1540
- parameters[i].encode(msg);
1541
- }
1542
- }
1543
- } else {
1544
- for(var i = 0; i < parameters.length; i++) {
1545
- parameters[i].encode(msg);
1546
- if (parameters[i].value !== null) {
1547
- msg.addInt(0);
1548
- }
1549
- }
1550
- }
1551
- } else {
1552
- msg.addBlr(blr); // empty
1553
- msg.addInt(0); // message number
1554
- msg.addInt(0); // param count
1555
- }
1556
-
1557
- if (op === Const.op_execute2) {
1558
- var outputBlr = new BlrWriter(32);
1559
-
1560
- if (statement.output && statement.output.length) {
1561
- CalcBlr(outputBlr, statement.output);
1562
- msg.addBlr(outputBlr);
1563
- } else {
1564
- msg.addBlr(outputBlr); // empty
1565
- }
1566
- msg.addInt(0); // out_message_number = out_message_type
1567
- }
1568
-
1569
- callback.statement = statement;
1570
- this._queueEvent(callback);
1571
- }
1572
-
1573
2051
  function fetch_blob_async_transaction(statement, id, column, row) {
1574
2052
  const infoValue = { row, column, value: '' };
1575
2053
 
@@ -1726,228 +2204,55 @@ function fetch_blob_async(statement, id, name, row) {
1726
2204
  };
1727
2205
  }
1728
2206
 
1729
- Connection.prototype.fetch = function(statement, transaction, count, callback) {
1730
-
1731
- var msg = this._msg;
1732
- var blr = this._blr;
1733
-
1734
- msg.pos = 0;
1735
- blr.pos = 0;
1736
-
1737
- if (count instanceof Function) {
1738
- callback = count;
1739
- count = Const.DEFAULT_FETCHSIZE;
1740
- }
1741
-
1742
- msg.addInt(Const.op_fetch);
1743
- msg.addInt(statement.handle);
1744
- CalcBlr(blr, statement.output);
1745
- msg.addBlr(blr);
1746
- msg.addInt(0); // message number
1747
- msg.addInt(count || Const.DEFAULT_FETCHSIZE); // fetch count
1748
-
1749
- callback.statement = statement;
1750
- this._queueEvent(callback);
1751
- };
1752
-
1753
- Connection.prototype.fetchAll = function (statement, transaction, callback) {
1754
- const self = this, data = [];
1755
- const loop = (err, ret) => {
1756
- if (err) {
1757
- return callback(err);
1758
- } else if (ret && ret.data && ret.data.length) {
1759
- const arrPromise = (ret.arrBlob || []).map(value => value(transaction));
1760
-
1761
- Promise.all(arrPromise).then((arrBlob) => {
1762
- for (let i = 0; i < arrBlob.length; i++) {
1763
- const blob = arrBlob[i];
1764
- ret.data[blob.row][blob.column] = blob.value;
1765
- }
1766
-
1767
- const lastIndex = ret.data.length - 1;
1768
- for (let i = 0; i < ret.data.length; i++) {
1769
- const pos = data.push(ret.data[i]);
1770
- if (statement.custom && statement.custom.asStream && statement.custom.on) {
1771
- statement.custom.on(ret.data[i], pos - 1);
1772
- }
1773
- if (i === lastIndex) {
1774
- if (ret.fetched) {
1775
- return callback(undefined, data);
1776
- } else {
1777
- self.fetch(statement, transaction, Const.DEFAULT_FETCHSIZE, loop);
1778
- }
1779
- }
1780
- }
1781
- }).catch(callback);
1782
- } else if (ret.fetched) {
1783
- callback(undefined, data);
1784
- } else {
1785
- self.fetch(statement, transaction, Const.DEFAULT_FETCHSIZE, loop);
1786
- }
2207
+ function doSynchronousLoop(data, processData, done) {
2208
+ if (!data || !data.length) {
2209
+ done();
2210
+ return;
1787
2211
  }
1788
2212
 
1789
- this.fetch(statement, transaction, Const.DEFAULT_FETCHSIZE, loop);
1790
- };
1791
-
1792
-
1793
- Connection.prototype.openBlob = function(blob, transaction, callback) {
1794
- var msg = this._msg;
1795
- msg.pos = 0;
1796
- msg.addInt(Const.op_open_blob);
1797
- msg.addInt(transaction.handle);
1798
- msg.addQuad(blob);
1799
- this._queueEvent(callback);
1800
- };
1801
-
1802
- Connection.prototype.closeBlob = function(blob, callback) {
1803
- var msg = this._msg;
1804
- msg.pos = 0;
1805
- msg.addInt(Const.op_close_blob);
1806
- msg.addInt(blob.handle);
1807
- this._queueEvent(callback, true);
1808
- };
1809
-
1810
- Connection.prototype.getSegment = function(blob, callback) {
1811
- var msg = this._msg;
1812
- msg.pos = 0;
1813
- msg.addInt(Const.op_get_segment);
1814
- msg.addInt(blob.handle);
1815
- msg.addInt(1024); // buffer length
1816
- msg.addInt(0); // ???
1817
- this._queueEvent(callback);
1818
- };
1819
-
1820
- Connection.prototype.createBlob2 = function (transaction, callback) {
1821
- var msg = this._msg;
1822
- msg.pos = 0;
1823
- msg.addInt(Const.op_create_blob2);
1824
- msg.addInt(0);
1825
- msg.addInt(transaction.handle);
1826
- msg.addInt(0);
1827
- msg.addInt(0);
1828
- this._queueEvent(callback);
1829
- };
1830
-
1831
- Connection.prototype.batchSegments = function(blob, buffer, callback){
1832
- var msg = this._msg;
1833
- var blr = this._blr;
1834
- msg.pos = 0;
1835
- blr.pos = 0;
1836
- msg.addInt(Const.op_batch_segments);
1837
- msg.addInt(blob.handle);
1838
- msg.addInt(buffer.length + 2);
1839
- blr.addBuffer(buffer);
1840
- msg.addBlr(blr);
1841
- this._queueEvent(callback);
1842
- };
1843
-
1844
- Connection.prototype.svcattach = function (options, callback, svc) {
1845
- this._lowercase_keys = options.lowercase_keys || Const.DEFAULT_LOWERCASE_KEYS;
1846
- var database = options.database || options.filename;
1847
- var user = options.user || Const.DEFAULT_USER;
1848
- var password = options.password || Const.DEFAULT_PASSWORD;
1849
- var role = options.role;
1850
- var msg = this._msg;
1851
- var blr = this._blr;
1852
- msg.pos = 0;
1853
- blr.pos = 0;
1854
-
1855
- blr.addBytes([Const.isc_dpb_version2, Const.isc_dpb_version2]);
1856
- blr.addString(Const.isc_dpb_lc_ctype, 'UTF8', Const.DEFAULT_ENCODING);
1857
- blr.addString(Const.isc_dpb_user_name, user, Const.DEFAULT_ENCODING);
1858
- blr.addString(Const.isc_dpb_password, password, Const.DEFAULT_ENCODING);
1859
- blr.addByte(Const.isc_dpb_dummy_packet_interval);
1860
- blr.addByte(4);
1861
- blr.addBytes([120, 10, 0, 0]); // FROM DOT NET PROVIDER
1862
- if (role)
1863
- blr.addString(Const.isc_dpb_sql_role_name, role, Const.DEFAULT_ENCODING);
1864
-
1865
- msg.addInt(Const.op_service_attach);
1866
- msg.addInt(0);
1867
- msg.addString(Const.DEFAULT_SVC_NAME, Const.DEFAULT_ENCODING); // only local for moment
1868
- msg.addBlr(this._blr);
1869
-
1870
- var self = this;
1871
-
1872
- function cb(err, ret) {
1873
-
1874
- if (err) {
1875
- doError(err, callback);
1876
- return;
1877
- }
1878
-
1879
- self.svchandle = ret.handle;
1880
- if (callback)
1881
- callback(undefined, ret);
1882
- }
2213
+ const loop = (index) => {
2214
+ processData(data[index], index, (err) => {
2215
+ if (err) {
2216
+ done(err);
2217
+ return;
2218
+ }
1883
2219
 
1884
- // For reconnect
1885
- if (svc) {
1886
- svc.connection = this;
1887
- cb.response = svc;
1888
- } else {
1889
- cb.response = new ServiceManager(this);
1890
- cb.response.removeAllListeners('error');
1891
- cb.response.on('error', noop);
1892
- }
2220
+ const nextIndex = index + 1;
2221
+ if (nextIndex < data.length) {
2222
+ loop(nextIndex);
2223
+ } else {
2224
+ done();
2225
+ }
2226
+ });
2227
+ };
1893
2228
 
1894
- this._queueEvent(cb);
2229
+ loop(0);
1895
2230
  }
1896
2231
 
1897
- Connection.prototype.svcstart = function (spbaction, callback) {
1898
- var msg = this._msg;
1899
- var blr = this._blr;
1900
- msg.pos = 0;
1901
- msg.addInt(Const.op_service_start);
1902
- msg.addInt(this.svchandle);
1903
- msg.addInt(0)
1904
- msg.addBlr(spbaction);
1905
- this._queueEvent(callback);
1906
- }
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
+ };
1907
2241
 
1908
- Connection.prototype.svcquery = function (spbquery, resultbuffersize, timeout,callback) {
1909
- if (resultbuffersize > Const.MAX_BUFFER_SIZE) {
1910
- doError(new Error('Buffer is too big'), callback);
1911
- 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);
1912
2255
  }
1913
-
1914
- var msg = this._msg;
1915
- var blr = this._blr;
1916
- msg.pos = 0;
1917
- blr.pos = 0;
1918
- blr.addByte(Const.isc_spb_current_version);
1919
- //blr.addByteInt32(Const.isc_info_svc_timeout, timeout);
1920
- msg.addInt(Const.op_service_info);
1921
- msg.addInt(this.svchandle);
1922
- msg.addInt(0);
1923
- msg.addBlr(blr);
1924
- blr.pos = 0
1925
- blr.addBytes(spbquery);
1926
- msg.addBlr(blr);
1927
- msg.addInt(resultbuffersize);
1928
- this._queueEvent(callback);
1929
- }
1930
-
1931
- Connection.prototype.svcdetach = function (callback) {
1932
- var self = this;
1933
-
1934
- if (self._isClosed)
1935
- return;
1936
-
1937
- self._isUsed = false;
1938
- self._isDetach = true;
1939
-
1940
- var msg = self._msg;
1941
-
1942
- msg.pos = 0;
1943
- msg.addInt(Const.op_service_detach);
1944
- msg.addInt(this.svchandle); // Database Object ID
1945
-
1946
- self._queueEvent(function (err, ret) {
1947
- delete (self.svchandle);
1948
- if (callback)
1949
- callback(err, ret);
1950
- });
1951
2256
  }
1952
2257
 
1953
2258
  function bufferReader(buffer, max, writer, cb, beg, end) {
@@ -1974,79 +2279,31 @@ function bufferReader(buffer, max, writer, cb, beg, end) {
1974
2279
  });
1975
2280
  }
1976
2281
 
1977
- Connection.prototype.auxConnection = function (callback) {
1978
- var self = this;
1979
- if (self._isClosed)
1980
- return this.throwClosed(callback);
1981
- var msg = self._msg;
1982
- msg.pos = 0;
1983
- msg.addInt(Const.op_connect_request);
1984
- msg.addInt(1); // async
1985
- msg.addInt(self.dbhandle);
1986
- msg.addInt(0);
1987
- function cb(err, ret) {
1988
-
1989
- if (err) {
1990
- doError(err, callback);
1991
- return;
1992
- }
1993
-
1994
- var socket_info = {
1995
- family: ret.buffer.readInt16BE(0),
1996
- port: ret.buffer.readUInt16BE(2),
1997
- host: ret.buffer.readUInt8(4) + '.' + ret.buffer.readUInt8(5) + '.' + ret.buffer.readUInt8(6) + '.' + ret.buffer.readUInt8(7)
1998
- }
1999
-
2000
- callback(undefined, socket_info);
2001
- }
2002
- this._queueEvent(cb);
2003
- }
2004
-
2005
- Connection.prototype.queEvents = function (events, eventid, callback) {
2006
- var self = this;
2007
- if (this._isClosed)
2008
- return this.throwClosed(callback);
2009
- var msg = this._msg;
2010
- var blr = this._blr;
2011
- blr.pos = 0;
2012
- msg.pos = 0;
2013
- msg.addInt(Const.op_que_events);
2014
- msg.addInt(this.dbhandle);
2015
- // prepare EPB
2016
- blr.addByte(1) // epb_version
2017
- for (var event in events) {
2018
- var event_buffer = new Buffer(event, 'UTF8');
2019
- blr.addByte(event_buffer.length);
2020
- blr.addBytes(event_buffer);
2021
- 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);
2022
2292
  }
2023
- msg.addBlr(blr); // epb
2024
- msg.addInt(0); // ast
2025
- msg.addInt(0); // args
2026
- msg.addInt(eventid);
2027
- this._queueEvent(callback);
2028
- }
2029
2293
 
2030
- Connection.prototype.closeEvents = function (eventid, callback) {
2031
- var self = this;
2032
- if (this._isClosed)
2033
- return this.throwClosed(callback);
2034
- var msg = self._msg;
2035
- msg.pos = 0;
2036
- msg.addInt(Const.op_cancel_events);
2037
- msg.addInt(self.dbhandle);
2038
- msg.addInt(eventid);
2039
-
2040
- function cb(err, ret) {
2041
- if (err) {
2042
- doError(err, callback);
2043
- 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);
2044
2302
  }
2045
-
2046
- callback(err);
2047
2303
  }
2048
2304
 
2049
- this._queueEvent(cb);
2305
+ // Plain string - encode as UTF-8
2306
+ return Buffer.from(config, 'utf8');
2050
2307
  }
2051
2308
 
2052
2309
  module.exports = Connection;