node-firebird 1.1.8 → 1.1.10

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.
@@ -0,0 +1,2052 @@
1
+ const Events = require('events');
2
+ const os = require('os');
3
+ const path = require('path');
4
+ const BigInt = require('big-integer');
5
+
6
+ const {XdrWriter, BlrWriter, XdrReader, BitSet, BlrReader} = require('./serialize');
7
+ const {doCallback, doError} = require('../callback');
8
+ const srp = require('../srp');
9
+ const crypt = require('../unix-crypt');
10
+ const Const = require('./const');
11
+ const Xsql = require('./xsqlvar');
12
+ const ServiceManager = require('./service');
13
+ const Database = require('./database');
14
+ const Statement = require('./statement');
15
+ const Transaction = require('./transaction');
16
+ const {lookupMessages, noop, parseDate} = require('../utils');
17
+ const Socket = require("./socket");
18
+
19
+ /***************************************
20
+ *
21
+ * Connection
22
+ *
23
+ ***************************************/
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;
57
+ }
58
+ }
59
+
60
+
61
+ };
62
+
63
+ Connection.prototype.getCachedQuery = function (query) {
64
+ return this._cache_query ? this._cache_query[query] : null;
65
+ };
66
+
67
+ Connection.prototype._bind_events = function(host, port, callback) {
68
+
69
+ var self = this;
70
+
71
+ self._socket.on('close', function() {
72
+
73
+ if (!self._isOpened || self._isDetach) {
74
+ return;
75
+ }
76
+
77
+ self._isOpened = false;
78
+
79
+ if (!self.db) {
80
+ if (callback)
81
+ callback(self.error);
82
+ return;
83
+ }
84
+
85
+ self._retry_connection_id = setTimeout(function() {
86
+ self._socket.removeAllListeners();
87
+ self._socket = null;
88
+
89
+ var ctx = new Connection(host, port, function(err) {
90
+ ctx.connect(self.options, function(err) {
91
+
92
+ if (err) {
93
+ self.db.emit('error', err);
94
+ return;
95
+ }
96
+
97
+ ctx.attach(self.options, function(err) {
98
+
99
+ if (err) {
100
+ self.db.emit('error', err);
101
+ return;
102
+ }
103
+
104
+ ctx._queue = ctx._queue.concat(self._queue);
105
+ ctx._pending = ctx._pending.concat(self._pending);
106
+ self.db.emit('reconnect');
107
+
108
+ }, self.db);
109
+ });
110
+
111
+ Object.assign(self, ctx);
112
+
113
+ }, self.options, self.db);
114
+ }, self._retry_connection_interval);
115
+
116
+ });
117
+
118
+ self._socket.on('error', function(e) {
119
+
120
+ self.error = e;
121
+
122
+ if (self.db)
123
+ self.db.emit('error', e)
124
+
125
+ if (callback)
126
+ callback(e);
127
+
128
+ });
129
+
130
+ self._socket.on('connect', function() {
131
+ self._isClosed = false;
132
+ self._isOpened = true;
133
+ if (callback)
134
+ callback();
135
+ });
136
+
137
+ self._socket.on('data', function (data) {
138
+ var xdr;
139
+
140
+ if (!self._xdr) {
141
+ xdr = new XdrReader(data);
142
+ } else {
143
+ xdr = new XdrReader(Buffer.concat([self._xdr.buffer, data], self._xdr.buffer.length + data.length));
144
+ delete (self._xdr);
145
+ }
146
+
147
+ while (xdr.pos < xdr.buffer.length) {
148
+ var cb = self._queue[0], pos = xdr.pos;
149
+
150
+ decodeResponse(xdr, cb, self, self._lowercase_keys, function (err, obj) {
151
+
152
+ if (err) {
153
+ xdr.buffer = xdr.buffer.slice(pos);
154
+ xdr.pos = 0;
155
+ self._xdr = xdr;
156
+
157
+ if (self.accept.protocolMinimumType === Const.ptype_lazy_send && self._queue.length > 0) {
158
+ self._queue[0].lazy_count = 2;
159
+ }
160
+ return;
161
+ }
162
+
163
+ // remove the op flag, needed for partial packet
164
+ if (xdr.r) {
165
+ delete (xdr.r);
166
+ }
167
+
168
+ self._queue.shift();
169
+ self._pending.shift();
170
+
171
+ if (obj && obj.status) {
172
+ obj.message = lookupMessages(obj.status);
173
+ doCallback(obj, cb);
174
+ } else {
175
+ doCallback(obj, cb);
176
+ }
177
+
178
+ });
179
+
180
+ if (xdr.pos === 0) {
181
+ break;
182
+ }
183
+ }
184
+
185
+ if (!self._detachAuto || self._pending.length !== 0) {
186
+ return;
187
+ }
188
+
189
+ clearTimeout(self._detachTimeout);
190
+ self._detachTimeout = setTimeout(function () {
191
+ self.db.detach(self._detachCallback);
192
+ self._detachAuto = false;
193
+ }, 100);
194
+
195
+ });
196
+ }
197
+
198
+ Connection.prototype.disconnect = function() {
199
+ this._socket.end();
200
+ };
201
+
202
+
203
+ function decodeResponse(data, callback, cnx, lowercase_keys, cb) {
204
+ try {
205
+ do {
206
+ var r = data.r || data.readInt();
207
+ } while (r === Const.op_dummy);
208
+
209
+ var item, op, response;
210
+
211
+ switch (r) {
212
+ case Const.op_response:
213
+
214
+ if (callback) {
215
+ response = callback.response || {};
216
+ } else {
217
+ response = {};
218
+ }
219
+
220
+ let loop = function (err) {
221
+ if (err) {
222
+ return cb(err);
223
+ } else {
224
+ if (callback && callback.lazy_count) {
225
+ callback.lazy_count--;
226
+ if (callback.lazy_count > 0) {
227
+ r = data.readInt(); // Read new op
228
+ parseOpResponse(data, response, loop);
229
+ } else {
230
+ cb(null, response);
231
+ }
232
+ } else {
233
+ cb(null, response);
234
+ }
235
+ }
236
+ };
237
+ // Parse normal and lazy response
238
+ return parseOpResponse(data, response, loop);
239
+ case Const.op_fetch_response:
240
+ case Const.op_sql_response:
241
+ var statement = callback.statement;
242
+ var output = statement.output;
243
+ var custom = statement.custom || {};
244
+ var isOpFetch = r === Const.op_fetch_response;
245
+ var _xdrpos;
246
+ statement.nbrowsfetched = statement.nbrowsfetched || 0;
247
+
248
+ if (isOpFetch && data.fop) { // could be set when a packet is not complete
249
+ data.readBuffer(68); // ??
250
+ op = data.readInt(); // ??
251
+ data.fop = false;
252
+ if (op === Const.op_response) {
253
+ return parseOpResponse(data, {}, cb);
254
+ }
255
+ }
256
+
257
+ if (!isOpFetch) {
258
+ data.fstatus = 0;
259
+ }
260
+
261
+ data.fstatus = data.fstatus !== undefined ? data.fstatus : data.readInt();
262
+ data.fcount = data.fcount !== undefined ? data.fcount : data.readInt();
263
+ data.fcolumn = data.fcolumn || 0;
264
+ data.frow = data.frow || (custom.asObject ? {} : new Array(output.length));
265
+ data.frows = data.frows || [];
266
+
267
+ if (custom.asObject && !data.fcols) {
268
+ if (lowercase_keys) {
269
+ data.fcols = output.map((column) => column.alias.toLowerCase());
270
+ } else {
271
+ data.fcols = output.map((column) => column.alias);
272
+ }
273
+ }
274
+
275
+ const arrBlob = [];
276
+ const lowerV13 = statement.connection.accept.protocolVersion < Const.PROTOCOL_VERSION13;
277
+
278
+ while (data.fcount && (data.fstatus !== 100)) {
279
+ let nullBitSet;
280
+ if (!lowerV13) {
281
+ const nullBitsLen = Math.floor((output.length + 7) / 8);
282
+ nullBitSet = new BitSet(data.readBuffer(nullBitsLen, false));
283
+ data.readBuffer((4 - nullBitsLen) & 3, false); // Skip padding
284
+ }
285
+
286
+ for (let length = output.length; data.fcolumn < length; data.fcolumn++) {
287
+ item = output[data.fcolumn];
288
+
289
+ if (!lowerV13 && nullBitSet.get(data.fcolumn)) {
290
+ if (custom.asObject) {
291
+ data.frow[data.fcols[data.fcolumn]] = null;
292
+ } else {
293
+ data.frow[data.fcolumn] = null;
294
+ }
295
+
296
+ continue;
297
+ }
298
+
299
+ try {
300
+ _xdrpos = data.pos;
301
+ const key = custom.asObject ? data.fcols[data.fcolumn] : data.fcolumn;
302
+ const row = data.frows.length;
303
+ let value = item.decode(data, lowerV13);
304
+
305
+ if (item.type === Const.SQL_BLOB && value !== null) {
306
+ if (item.subType === Const.isc_blob_text && cnx.options.blobAsText) {
307
+ value = fetch_blob_async_transaction(statement, value, key, row);
308
+ arrBlob.push(value);
309
+ } else {
310
+ value = fetch_blob_async(statement, value, key, row);
311
+ }
312
+ }
313
+
314
+ data.frow[key] = value;
315
+ } catch (e) {
316
+ // uncomplete packet read
317
+ data.pos = _xdrpos;
318
+ data.r = r;
319
+ return cb(new Error('Packet is not complete'));
320
+ }
321
+
322
+ }
323
+
324
+ data.fcolumn = 0;
325
+ // ToDo: emit "row" with blob subtype string decoded
326
+ // use: data.frow['fieldBlob'](transaction?).then(({ value }) => console.log(value))
327
+ // arg "transaction" is optional
328
+ statement.connection.db.emit('row', data.frow, statement.nbrowsfetched, custom.asObject);
329
+ data.frows.push(data.frow);
330
+ data.frow = custom.asObject ? {} : new Array(output.length);
331
+
332
+ try {
333
+ _xdrpos = data.pos;
334
+ if (isOpFetch) {
335
+ delete data.fstatus;
336
+ delete data.fcount;
337
+ op = data.readInt(); // ??
338
+ if (op === Const.op_response) {
339
+ return parseOpResponse(data, {}, cb);
340
+ }
341
+ data.fstatus = data.readInt();
342
+ data.fcount = data.readInt();
343
+ } else {
344
+ data.fcount--;
345
+ if (r === Const.op_sql_response) {
346
+ op = data.readInt();
347
+ if (op === Const.op_response) {
348
+ parseOpResponse(data, {});
349
+ }
350
+ }
351
+ }
352
+ } catch (e) {
353
+ if (_xdrpos === data.pos) {
354
+ data.fop = true;
355
+ }
356
+ data.r = r;
357
+ return cb(new Error("Packet is not complete"));
358
+ }
359
+ statement.nbrowsfetched++;
360
+ }
361
+
362
+ // ToDo: emit "result" with blob subtype string decoded
363
+ statement.connection.db.emit('result', data.frows, arrBlob);
364
+ return cb(null, {data: data.frows, fetched: Boolean(!isOpFetch || data.fstatus === 100), arrBlob});
365
+ case Const.op_accept:
366
+ case Const.op_cond_accept:
367
+ case Const.op_accept_data:
368
+ let accept = {
369
+ protocolVersion: data.readInt(),
370
+ protocolArchitecture: data.readInt(),
371
+ protocolMinimumType: data.readInt(),
372
+ compress: false,
373
+ pluginName: '',
374
+ authData: '',
375
+ sessionKey: ''
376
+ };
377
+
378
+ accept.compress = (accept.protocolMinimumType & Const.pflag_compress) !== 0;
379
+ accept.protocolMinimumType = accept.protocolMinimumType & Const.ptype_mask;
380
+ //accept.compress = (accept.acceptType & pflag_compress) !== 0; // TODO Handle zlib compression
381
+ if (accept.protocolVersion < 0) {
382
+ accept.protocolVersion = (accept.protocolVersion & Const.FB_PROTOCOL_MASK) | Const.FB_PROTOCOL_FLAG;
383
+ }
384
+
385
+ if (r === Const.op_cond_accept || r === Const.op_accept_data) {
386
+ var d = new BlrReader(data.readArray());
387
+ accept.pluginName = data.readString(Const.DEFAULT_ENCODING);
388
+ var is_authenticated = data.readInt();
389
+ var keys = data.readString(Const.DEFAULT_ENCODING); // keys
390
+
391
+ if (is_authenticated === 0) {
392
+ if (cnx.options.pluginName && cnx.options.pluginName !== accept.pluginName) {
393
+ doError(new Error('Server don\'t accept plugin : ' + cnx.options.pluginName + ', but support : ' + accept.pluginName), callback);
394
+ }
395
+
396
+ if (Const.AUTH_PLUGIN_SRP_LIST.indexOf(accept.pluginName) !== -1) {
397
+ var crypto = {
398
+ Srp: 'sha1',
399
+ Srp256: 'sha256'
400
+ };
401
+ accept.srpAlgo = crypto[accept.pluginName];
402
+
403
+ // TODO : Fallback Srp256 to Srp ?
404
+ /*if (!d.buffer) {
405
+ cnx.sendOpContAuth(
406
+ cnx.clientKeys.public.toString(16),
407
+ DEFAULT_ENCODING,
408
+ accept.pluginName
409
+ );
410
+
411
+ return cb(new Error('login'));
412
+ }*/
413
+
414
+ // Check buffer contains salt
415
+ var saltLen = d.buffer.readUInt16LE(0);
416
+ if (saltLen > 32 * 2) {
417
+ console.log('salt to long'); // TODO : Throw error
418
+ }
419
+
420
+ // Check buffer contains key
421
+ var keyLen = d.buffer.readUInt16LE(saltLen + 2);
422
+ var keyStart = saltLen + 4;
423
+ if (d.buffer.length - keyStart !== keyLen) {
424
+ console.log('key error'); // TODO : Throw error
425
+ }
426
+
427
+ // Server keys
428
+ cnx.serverKeys = {
429
+ salt: d.buffer.slice(2, saltLen + 2).toString('utf8'),
430
+ public: BigInt(d.buffer.slice(keyStart, d.buffer.length).toString('utf8'), 16)
431
+ };
432
+
433
+ var proof = srp.clientProof(
434
+ cnx.options.user.toUpperCase(),
435
+ cnx.options.password,
436
+ cnx.serverKeys.salt,
437
+ cnx.clientKeys.public,
438
+ cnx.serverKeys.public,
439
+ cnx.clientKeys.private,
440
+ accept.srpAlgo
441
+ );
442
+
443
+ accept.authData = proof.authData.toString(16);
444
+ accept.sessionKey = proof.clientSessionKey;
445
+ } else if (accept.pluginName === Const.AUTH_PLUGIN_LEGACY) {
446
+ accept.authData = crypt.crypt(cnx.options.password, Const.LEGACY_AUTH_SALT).substring(2);
447
+ } else {
448
+ return cb(new Error('Unknow auth plugin : ' + accept.pluginName));
449
+ }
450
+ } else {
451
+ accept.authData = '';
452
+ accept.sessionKey = '';
453
+ }
454
+ }
455
+
456
+ if (accept.compress) {
457
+ cnx._socket.enableCompression();
458
+ }
459
+
460
+ return cb(undefined, accept);
461
+ case Const.op_cont_auth:
462
+ var d = new BlrReader(data.readArray());
463
+ var pluginName = data.readString(Const.DEFAULT_ENCODING);
464
+ data.readString(Const.DEFAULT_ENCODING); // plist
465
+ data.readString(Const.DEFAULT_ENCODING); // pkey
466
+
467
+ if (!cnx.options.pluginName) {
468
+ if (cnx.accept.pluginName === pluginName) {
469
+ // Erreur plugin not able to connect
470
+ return cb(new Error("Unable to connect with plugin " + cnx.accept.pluginName));
471
+ }
472
+
473
+ if (pluginName === Const.AUTH_PLUGIN_LEGACY) { // Fallback to LegacyAuth
474
+ cnx.accept.pluginName = pluginName;
475
+ cnx.accept.authData = crypt.crypt(cnx.options.password, Const.LEGACY_AUTH_SALT).substring(2);
476
+
477
+ cnx.sendOpContAuth(
478
+ cnx.accept.authData,
479
+ 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
+ };
1040
+
1041
+ Connection.prototype.closeStatement = function (statement, callback) {
1042
+
1043
+ if (this._isClosed)
1044
+ return this.throwClosed(callback);
1045
+
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
+ };
1057
+
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
+ }
1069
+
1070
+ if (callback)
1071
+ callback(err, ret);
1072
+ };
1073
+
1074
+ // for auto detach
1075
+ this._pending.push('allocateAndPrepareStatement');
1076
+
1077
+ var msg = this._msg;
1078
+ var blr = this._blr;
1079
+
1080
+ msg.pos = 0;
1081
+ blr.pos = 0;
1082
+
1083
+ msg.addInt(Const.op_allocate_statement);
1084
+ msg.addInt(this.dbhandle);
1085
+ mainCallback.lazy_count = 1;
1086
+
1087
+ blr.addBytes(Const.DESCRIBE);
1088
+ if (plan)
1089
+ blr.addByte(Const.isc_info_sql_get_plan);
1090
+
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;
1099
+
1100
+ mainCallback.response = new Statement(this);
1101
+ this._queueEvent(mainCallback);
1102
+ };
1103
+
1104
+ Connection.prototype.prepare = function (transaction, query, plan, callback) {
1105
+ var self = this;
1106
+
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
+ }
1115
+
1116
+ self.prepareStatement(transaction, statement, query, plan, callback);
1117
+ });
1118
+ }
1119
+ };
1120
+
1121
+ function describe(buff, statement) {
1122
+ var br = new BlrReader(buff);
1123
+ var parameters = null;
1124
+ var type, param;
1125
+
1126
+ while (br.pos < br.buffer.length) {
1127
+ switch (br.readByteCode()) {
1128
+ case Const.isc_info_sql_stmt_type:
1129
+ statement.type = br.readInt();
1130
+ break;
1131
+ case Const.isc_info_sql_get_plan:
1132
+ statement.plan = br.readString(Const.DEFAULT_ENCODING);
1133
+ break;
1134
+ case Const.isc_info_sql_select:
1135
+ statement.output = parameters = [];
1136
+ break;
1137
+ case Const.isc_info_sql_bind:
1138
+ statement.input = parameters = [];
1139
+ break;
1140
+ case Const.isc_info_sql_num_variables:
1141
+ br.readInt(); // eat int
1142
+ break;
1143
+ case Const.isc_info_sql_describe_vars:
1144
+ if (!parameters) {return}
1145
+ br.readInt(); // eat int ?
1146
+ var finishDescribe = false;
1147
+ param = null;
1148
+ while (!finishDescribe){
1149
+ switch (br.readByteCode()) {
1150
+ case Const.isc_info_sql_describe_end:
1151
+ break;
1152
+ case Const.isc_info_sql_sqlda_seq:
1153
+ var num = br.readInt();
1154
+ break;
1155
+ case Const.isc_info_sql_type:
1156
+ type = br.readInt();
1157
+ switch (type&~1) {
1158
+ case Const.SQL_VARYING: param = new Xsql.SQLVarString(); break;
1159
+ case Const.SQL_NULL: param = new Xsql.SQLVarNull(); break;
1160
+ case Const.SQL_TEXT: param = new Xsql.SQLVarText(); break;
1161
+ case Const.SQL_DOUBLE: param = new Xsql.SQLVarDouble(); break;
1162
+ case Const.SQL_FLOAT:
1163
+ case Const.SQL_D_FLOAT: param = new Xsql.SQLVarFloat(); break;
1164
+ case Const.SQL_TYPE_DATE: param = new Xsql.SQLVarDate(); break;
1165
+ case Const.SQL_TYPE_TIME: param = new Xsql.SQLVarTime(); break;
1166
+ case Const.SQL_TIMESTAMP: param = new Xsql.SQLVarTimeStamp(); break;
1167
+ case Const.SQL_BLOB: param = new Xsql.SQLVarBlob(); break;
1168
+ case Const.SQL_ARRAY: param = new Xsql.SQLVarArray(); break;
1169
+ case Const.SQL_QUAD: param = new Xsql.SQLVarQuad(); break;
1170
+ case Const.SQL_LONG: param = new Xsql.SQLVarInt(); break;
1171
+ case Const.SQL_SHORT: param = new Xsql.SQLVarShort(); break;
1172
+ case Const.SQL_INT64: param = new Xsql.SQLVarInt64(); break;
1173
+ case Const.SQL_INT128: param = new Xsql.SQLVarInt128(); break;
1174
+ case Const.SQL_BOOLEAN: param = new Xsql.SQLVarBoolean(); break;
1175
+ default:
1176
+ throw new Error('Unexpected');
1177
+ }
1178
+ parameters[num-1] = param;
1179
+ param.type = type;
1180
+ param.nullable = Boolean(param.type & 1);
1181
+ param.type &= ~1;
1182
+ break;
1183
+ case Const.isc_info_sql_sub_type:
1184
+ param.subType = br.readInt();
1185
+ break;
1186
+ case Const.isc_info_sql_scale:
1187
+ param.scale = br.readInt();
1188
+ break;
1189
+ case Const.isc_info_sql_length:
1190
+ param.length = br.readInt();
1191
+ break;
1192
+ case Const.isc_info_sql_null_ind:
1193
+ param.nullable = Boolean(br.readInt());
1194
+ break;
1195
+ case Const.isc_info_sql_field:
1196
+ param.field = br.readString(Const.DEFAULT_ENCODING);
1197
+ break;
1198
+ case Const.isc_info_sql_relation:
1199
+ param.relation = br.readString(Const.DEFAULT_ENCODING);
1200
+ break;
1201
+ case Const.isc_info_sql_owner:
1202
+ param.owner = br.readString(Const.DEFAULT_ENCODING);
1203
+ break;
1204
+ case Const.isc_info_sql_alias:
1205
+ param.alias = br.readString(Const.DEFAULT_ENCODING);
1206
+ break;
1207
+ case Const.isc_info_sql_relation_alias:
1208
+ param.relationAlias = br.readString(Const.DEFAULT_ENCODING);
1209
+ break;
1210
+ case Const.isc_info_truncated:
1211
+ throw new Error('Truncated');
1212
+ default:
1213
+ finishDescribe = true;
1214
+ br.pos--;
1215
+ }
1216
+ }
1217
+ }
1218
+ }
1219
+ }
1220
+
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
+ function CalcBlr(blr, xsqlda) {
1268
+ blr.addBytes([Const.blr_version5, Const.blr_begin, Const.blr_message, 0]); // + message number
1269
+ blr.addWord(xsqlda.length * 2);
1270
+
1271
+ for (var i = 0, length = xsqlda.length; i < length; i++) {
1272
+ xsqlda[i].calcBlr(blr);
1273
+ blr.addByte(Const.blr_short);
1274
+ blr.addByte(0);
1275
+ }
1276
+
1277
+ blr.addByte(Const.blr_end);
1278
+ blr.addByte(Const.blr_eoc);
1279
+ }
1280
+
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
+ function fetch_blob_async_transaction(statement, id, column, row) {
1574
+ const infoValue = { row, column, value: '' };
1575
+
1576
+ return (transactionArg) => {
1577
+ const singleTransaction = transactionArg === undefined;
1578
+
1579
+ let promiseTransaction;
1580
+ if (singleTransaction) {
1581
+ promiseTransaction = new Promise((resolve, reject) => {
1582
+ statement.connection.startTransaction(Const.ISOLATION_READ_UNCOMMITTED, (err, transaction) => {
1583
+ if (err) {
1584
+ return reject(err);
1585
+ }
1586
+ resolve(transaction);
1587
+ });
1588
+ });
1589
+ } else {
1590
+ promiseTransaction = Promise.resolve(transactionArg);
1591
+ }
1592
+
1593
+ return promiseTransaction.then((transaction) => {
1594
+ return new Promise((resolve, reject) => {
1595
+ statement.connection._pending.push('openBlob');
1596
+ statement.connection.openBlob(id, transaction, (err, blob) => {
1597
+
1598
+ if (err) {
1599
+ reject(err);
1600
+ return;
1601
+ }
1602
+
1603
+ const read = () => {
1604
+ statement.connection.getSegment(blob, (err, ret) => {
1605
+
1606
+ if (err) {
1607
+ if (singleTransaction) {
1608
+ transaction.rollback(() => reject(err));
1609
+ } else {
1610
+ reject(err);
1611
+ }
1612
+ return;
1613
+ }
1614
+
1615
+ if (ret.buffer) {
1616
+ const blr = new BlrReader(ret.buffer);
1617
+ const data = blr.readSegment();
1618
+ infoValue.value += data.toString(Const.DEFAULT_ENCODING);
1619
+ }
1620
+
1621
+ if (ret.handle !== 2) {
1622
+ read();
1623
+ return;
1624
+ }
1625
+
1626
+ statement.connection.closeBlob(blob);
1627
+ if (singleTransaction) {
1628
+ transaction.commit((err) => {
1629
+ if (err) {
1630
+ reject(err);
1631
+ } else {
1632
+ resolve(infoValue);
1633
+ }
1634
+ });
1635
+ } else {
1636
+ resolve(infoValue);
1637
+ }
1638
+ });
1639
+ };
1640
+
1641
+ read();
1642
+ });
1643
+ });
1644
+ });
1645
+ };
1646
+ }
1647
+
1648
+ function fetch_blob_async(statement, id, name, row) {
1649
+ const cbTransaction = (transaction, close, callback) => {
1650
+ statement.connection._pending.push('openBlob');
1651
+ statement.connection.openBlob(id, transaction, (err, blob) => {
1652
+ let e = new Events.EventEmitter();
1653
+
1654
+ e.pipe = (stream) => {
1655
+ e.on('data', (chunk) => {
1656
+ stream.write(chunk);
1657
+ });
1658
+ e.on('end', () => {
1659
+ stream.end();
1660
+ });
1661
+ };
1662
+
1663
+ if (err) {
1664
+ return callback(err, name, e, row);
1665
+ }
1666
+
1667
+ const read = () => {
1668
+ statement.connection.getSegment(blob, (err, ret) => {
1669
+
1670
+ if (err) {
1671
+ transaction.rollback(() => {
1672
+ e.emit('error', err);
1673
+ });
1674
+ return;
1675
+ }
1676
+
1677
+ if (ret.buffer) {
1678
+ const blr = new BlrReader(ret.buffer);
1679
+ const data = blr.readSegment();
1680
+
1681
+ e.emit('data', data);
1682
+ }
1683
+
1684
+ if (ret.handle !== 2) {
1685
+ read();
1686
+ return;
1687
+ }
1688
+
1689
+ statement.connection.closeBlob(blob);
1690
+ if (close) {
1691
+ transaction.commit((err) => {
1692
+ if (err) {
1693
+ e.emit('error', err);
1694
+ } else {
1695
+ e.emit('end');
1696
+ }
1697
+ e = null;
1698
+ });
1699
+ } else {
1700
+ e.emit('end');
1701
+ e = null;
1702
+ }
1703
+ });
1704
+ };
1705
+
1706
+ callback(err, name, e, row);
1707
+ read();
1708
+ });
1709
+ };
1710
+
1711
+ return (transaction, callback) => {
1712
+ // callback(error, nameField, eventEmitter, row)
1713
+ const singleTransaction = callback === undefined;
1714
+ if (singleTransaction) {
1715
+ callback = transaction;
1716
+ statement.connection.startTransaction(Const.ISOLATION_READ_UNCOMMITTED, (err, transaction) => {
1717
+ if (err) {
1718
+ callback(err);
1719
+ return;
1720
+ }
1721
+ cbTransaction(transaction, singleTransaction, callback);
1722
+ });
1723
+ } else {
1724
+ cbTransaction(transaction, singleTransaction, callback);
1725
+ }
1726
+ };
1727
+ }
1728
+
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
+ }
1787
+ }
1788
+
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
+ }
1883
+
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
+ }
1893
+
1894
+ this._queueEvent(cb);
1895
+ }
1896
+
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
+ }
1907
+
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;
1912
+ }
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
+ }
1952
+
1953
+ function bufferReader(buffer, max, writer, cb, beg, end) {
1954
+
1955
+ if (!beg)
1956
+ beg = 0;
1957
+
1958
+ if (!end)
1959
+ end = max;
1960
+
1961
+ if (end >= buffer.length)
1962
+ end = undefined;
1963
+
1964
+ var b = buffer.slice(beg, end);
1965
+
1966
+ writer(b, function() {
1967
+
1968
+ if (end === undefined) {
1969
+ cb();
1970
+ return;
1971
+ }
1972
+
1973
+ bufferReader(buffer, max, writer, cb, beg + max, end + max);
1974
+ });
1975
+ }
1976
+
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]);
2022
+ }
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
+
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;
2044
+ }
2045
+
2046
+ callback(err);
2047
+ }
2048
+
2049
+ this._queueEvent(cb);
2050
+ }
2051
+
2052
+ module.exports = Connection;