node-firebird 1.1.6 → 1.1.9

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