node-firebird 2.3.4 → 2.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (109) hide show
  1. package/README.md +544 -6
  2. package/lib/callback.d.ts +20 -0
  3. package/lib/callback.js +6 -16
  4. package/lib/firebird.msg.json +1370 -1370
  5. package/lib/gdscodes.d.ts +1519 -1520
  6. package/lib/gdscodes.js +5 -5
  7. package/lib/ieee754-decimal.d.ts +38 -0
  8. package/lib/ieee754-decimal.js +71 -118
  9. package/lib/index.d.ts +37 -338
  10. package/lib/index.js +97 -86
  11. package/lib/messages.d.ts +6 -0
  12. package/lib/messages.js +135 -162
  13. package/lib/pool.d.ts +23 -0
  14. package/lib/pool.js +34 -47
  15. package/lib/srp.d.ts +51 -0
  16. package/lib/srp.js +62 -72
  17. package/lib/types.d.ts +375 -0
  18. package/lib/types.js +22 -0
  19. package/lib/unix-crypt.d.ts +1 -0
  20. package/lib/unix-crypt.js +126 -152
  21. package/lib/utils.d.ts +16 -0
  22. package/lib/utils.js +24 -48
  23. package/lib/wire/connection.d.ts +100 -0
  24. package/lib/wire/connection.js +1029 -1009
  25. package/lib/wire/const.d.ts +567 -0
  26. package/lib/wire/const.js +620 -599
  27. package/lib/wire/database.d.ts +57 -0
  28. package/lib/wire/database.js +111 -108
  29. package/lib/wire/eventConnection.d.ts +16 -0
  30. package/lib/wire/eventConnection.js +29 -31
  31. package/lib/wire/fbEventManager.d.ts +45 -0
  32. package/lib/wire/fbEventManager.js +38 -57
  33. package/lib/wire/serialize.d.ts +97 -0
  34. package/lib/wire/serialize.js +42 -141
  35. package/lib/wire/service.d.ts +66 -0
  36. package/lib/wire/service.js +374 -401
  37. package/lib/wire/socket.d.ts +59 -0
  38. package/lib/wire/socket.js +79 -40
  39. package/lib/wire/statement.d.ts +25 -0
  40. package/lib/wire/statement.js +13 -9
  41. package/lib/wire/transaction.d.ts +21 -0
  42. package/lib/wire/transaction.js +33 -61
  43. package/lib/wire/xsqlvar.d.ts +175 -0
  44. package/lib/wire/xsqlvar.js +169 -269
  45. package/package.json +12 -4
  46. package/src/callback.ts +55 -0
  47. package/{poc/node_modules/node-firebird/lib/gdscodes.js → src/gdscodes.ts} +3 -3
  48. package/{poc/node_modules/node-firebird/lib/ieee754-decimal.js → src/ieee754-decimal.ts} +7 -14
  49. package/src/index.ts +157 -0
  50. package/{poc/node_modules/node-firebird/lib/messages.js → src/messages.ts} +163 -162
  51. package/src/pool.ts +209 -0
  52. package/{poc/node_modules/node-firebird/lib/srp.js → src/srp.ts} +74 -33
  53. package/src/types.ts +419 -0
  54. package/{poc/node_modules/node-firebird/lib/unix-crypt.js → src/unix-crypt.ts} +6 -12
  55. package/{poc/node_modules/node-firebird/lib/utils.js → src/utils.ts} +11 -20
  56. package/{poc/node_modules/node-firebird/lib/wire/connection.js → src/wire/connection.ts} +611 -129
  57. package/{poc/node_modules/node-firebird/lib/wire/const.js → src/wire/const.ts} +96 -9
  58. package/{poc/node_modules/node-firebird/lib/wire/database.js → src/wire/database.ts} +110 -44
  59. package/{poc/node_modules/node-firebird/lib/wire/eventConnection.js → src/wire/eventConnection.ts} +29 -14
  60. package/{poc/node_modules/node-firebird/lib/wire/fbEventManager.js → src/wire/fbEventManager.ts} +39 -30
  61. package/{poc/node_modules/node-firebird/lib/wire/serialize.js → src/wire/serialize.ts} +78 -59
  62. package/{poc/node_modules/node-firebird/lib/wire/service.js → src/wire/service.ts} +90 -88
  63. package/src/wire/socket.ts +262 -0
  64. package/src/wire/statement.ts +71 -0
  65. package/{poc/node_modules/node-firebird/lib/wire/transaction.js → src/wire/transaction.ts} +25 -19
  66. package/{poc/node_modules/node-firebird/lib/wire/xsqlvar.js → src/wire/xsqlvar.ts} +244 -143
  67. package/.eslintrc.json +0 -12
  68. package/.github/workflows/codeql.yml +0 -76
  69. package/.github/workflows/node.js.yml +0 -95
  70. package/BIGINT_MIGRATION.md +0 -374
  71. package/CI_DEBUGGING_GUIDE.md +0 -148
  72. package/ENCRYPTION_CALLBACK.md +0 -152
  73. package/FIREBIRD_LOG_FEATURE.md +0 -145
  74. package/MINIMAL_CHANGES_SUMMARY.md +0 -136
  75. package/PR_SUMMARY.md +0 -96
  76. package/ROADMAP.md +0 -223
  77. package/SRP_PROTOCOL.md +0 -482
  78. package/poc/README.md +0 -160
  79. package/poc/helpers.js +0 -59
  80. package/poc/node_modules/.package-lock.json +0 -14
  81. package/poc/node_modules/node-firebird/.eslintrc.json +0 -12
  82. package/poc/node_modules/node-firebird/.github/workflows/codeql.yml +0 -76
  83. package/poc/node_modules/node-firebird/.github/workflows/node.js.yml +0 -95
  84. package/poc/node_modules/node-firebird/BIGINT_MIGRATION.md +0 -374
  85. package/poc/node_modules/node-firebird/CI_DEBUGGING_GUIDE.md +0 -148
  86. package/poc/node_modules/node-firebird/ENCRYPTION_CALLBACK.md +0 -152
  87. package/poc/node_modules/node-firebird/FIREBIRD_LOG_FEATURE.md +0 -145
  88. package/poc/node_modules/node-firebird/LICENSE +0 -373
  89. package/poc/node_modules/node-firebird/MINIMAL_CHANGES_SUMMARY.md +0 -136
  90. package/poc/node_modules/node-firebird/PR_SUMMARY.md +0 -96
  91. package/poc/node_modules/node-firebird/README.md +0 -794
  92. package/poc/node_modules/node-firebird/ROADMAP.md +0 -223
  93. package/poc/node_modules/node-firebird/SRP_PROTOCOL.md +0 -482
  94. package/poc/node_modules/node-firebird/lib/callback.js +0 -38
  95. package/poc/node_modules/node-firebird/lib/gdscodes.d.ts +0 -1524
  96. package/poc/node_modules/node-firebird/lib/index.d.ts +0 -316
  97. package/poc/node_modules/node-firebird/lib/index.js +0 -128
  98. package/poc/node_modules/node-firebird/lib/pool.js +0 -108
  99. package/poc/node_modules/node-firebird/lib/wire/socket.js +0 -175
  100. package/poc/node_modules/node-firebird/lib/wire/statement.js +0 -48
  101. package/poc/node_modules/node-firebird/package.json +0 -38
  102. package/poc/node_modules/node-firebird/vitest.config.js +0 -24
  103. package/poc/package-lock.json +0 -21
  104. package/poc/package.json +0 -12
  105. package/poc/reproduce-fixed.js +0 -150
  106. package/poc/reproduce.js +0 -133
  107. package/vitest.config.js +0 -27
  108. /package/{poc/node_modules/node-firebird/lib → src}/firebird.msg +0 -0
  109. /package/{poc/node_modules/node-firebird/lib → src}/firebird.msg.json +0 -0
@@ -1,165 +1,193 @@
1
- const Events = require('events');
2
- const os = require('os');
3
- const path = require('path');
4
-
5
- const {XdrWriter, BlrWriter, XdrReader, BitSet, BlrReader} = require('./serialize');
6
- const {doCallback, doError} = require('../callback');
7
- const srp = require('../srp');
8
- const crypt = require('../unix-crypt');
9
- const Const = require('./const');
10
- const Xsql = require('./xsqlvar');
11
- const ServiceManager = require('./service');
12
- const Database = require('./database');
13
- const Statement = require('./statement');
14
- const Transaction = require('./transaction');
15
- const {lookupMessages, noop, parseDate} = require('../utils');
16
- const Socket = require("./socket");
17
-
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ const events_1 = __importDefault(require("events"));
39
+ const os_1 = __importDefault(require("os"));
40
+ const path_1 = __importDefault(require("path"));
41
+ const serialize_1 = require("./serialize");
42
+ const callback_1 = require("../callback");
43
+ const srp = __importStar(require("../srp"));
44
+ const crypt = __importStar(require("../unix-crypt"));
45
+ const const_1 = __importDefault(require("./const"));
46
+ const Xsql = __importStar(require("./xsqlvar"));
47
+ const service_1 = __importDefault(require("./service"));
48
+ const database_1 = __importDefault(require("./database"));
49
+ const statement_1 = __importDefault(require("./statement"));
50
+ const transaction_1 = __importDefault(require("./transaction"));
51
+ const utils_1 = require("../utils");
52
+ const socket_1 = __importDefault(require("./socket"));
53
+ function parseValueIfJson(value, options) {
54
+ if (options && options.jsonAsObject && typeof value === 'string' && (value.startsWith('{') || value.startsWith('['))) {
55
+ try {
56
+ return JSON.parse(value);
57
+ }
58
+ catch (e) {
59
+ // Ignore parse error and keep as string
60
+ }
61
+ }
62
+ return value;
63
+ }
18
64
  /***************************************
19
65
  *
20
66
  * Connection
21
67
  *
22
68
  ***************************************/
23
-
24
69
  class Connection {
25
70
  constructor(host, port, callback, options, db, svc) {
26
71
  var self = this;
27
72
  this.db = db;
28
- this.svc = svc
29
- this._msg = new XdrWriter(32);
30
- this._blr = new BlrWriter(32);
73
+ this.svc = svc;
74
+ this._msg = new serialize_1.XdrWriter(32);
75
+ this._blr = new serialize_1.BlrWriter(32);
31
76
  this._queue = [];
32
77
  this._detachTimeout;
33
78
  this._detachCallback;
34
79
  this._detachAuto;
35
- this._socket = new Socket(port, host);
80
+ this._socket = new socket_1.default(port, host);
36
81
  this._pending = [];
37
82
  this._isOpened = false;
38
83
  this._isClosed = false;
39
84
  this._isDetach = false;
40
85
  this._isUsed = false;
41
- this._pooled = options.isPool||false;
42
- if (options && options.blobChunkSize > 65535) options.blobChunkSize = 65535;
43
- if (options && options.blobReadChunkSize > 65535) options.blobReadChunkSize = 65535;
86
+ this._pooled = options.isPool || false;
87
+ if (options && options.blobChunkSize > 65535)
88
+ options.blobChunkSize = 65535;
89
+ if (options && options.blobReadChunkSize > 65535)
90
+ options.blobReadChunkSize = 65535;
44
91
  this.options = options;
45
92
  this._bind_events(host, port, callback);
46
93
  this.error;
47
94
  this._retry_connection_id;
48
95
  this._retry_connection_interval = options.retryConnectionInterval || 1000;
49
96
  this._max_cached_query = options.maxCachedQuery || -1;
50
- this._cache_query = options.cacheQuery?{}:null;
51
- this._messageFile = options.messageFile || path.join(__dirname, 'firebird.msg');
97
+ this._cache_query = options.cacheQuery ? {} : null;
98
+ this._messageFile = options.messageFile || path_1.default.join(__dirname, 'firebird.msg');
52
99
  }
53
-
54
-
55
100
  _setcachedquery(query, statement) {
56
- if (this._cache_query){
57
- if (this._max_cached_query === -1 || this._max_cached_query > Object.keys(this._cache_query).length){
101
+ if (this._cache_query) {
102
+ if (this._max_cached_query === -1 || this._max_cached_query > Object.keys(this._cache_query).length) {
58
103
  this._cache_query[query] = statement;
59
104
  }
60
105
  }
61
-
62
-
63
106
  }
64
-
65
-
66
107
  getCachedQuery(query) {
67
108
  return this._cache_query ? this._cache_query[query] : null;
68
109
  }
69
-
70
-
110
+ // Reject every request that is still awaiting a server response (e.g. a
111
+ // transaction.commit() sent right before the server went away) instead of
112
+ // leaving its callback to hang forever. Requests already in flight cannot
113
+ // be transparently resumed on a reconnect: a fresh attach() hands out new
114
+ // transaction/statement handles, so nothing the server sends back could
115
+ // ever match a callback queued against the old connection.
116
+ _rejectPending(err) {
117
+ var queue = this._queue;
118
+ this._queue = [];
119
+ this._pending = [];
120
+ for (var i = 0; i < queue.length; i++) {
121
+ (0, callback_1.doError)(err, queue[i]);
122
+ }
123
+ }
71
124
  _bind_events(host, port, callback) {
72
-
73
125
  var self = this;
74
-
75
- self._socket.on('close', function() {
76
-
126
+ self._socket.on('close', function () {
77
127
  if (!self._isOpened || self._isDetach) {
78
128
  return;
79
129
  }
80
-
81
130
  self._isOpened = false;
82
-
131
+ self._isClosed = true;
132
+ var lostError = self.error || new Error('Connection to Firebird server was lost.');
83
133
  if (!self.db) {
134
+ self._rejectPending(lostError);
84
135
  if (callback)
85
136
  callback(self.error);
86
137
  return;
87
138
  }
88
-
89
- self._retry_connection_id = setTimeout(function() {
139
+ self._rejectPending(lostError);
140
+ self._retry_connection_id = setTimeout(function () {
90
141
  self._socket.removeAllListeners();
91
142
  self._socket = null;
92
-
93
- var ctx = new Connection(host, port, function(err) {
94
- ctx.connect(self.options, function(err) {
95
-
143
+ var ctx = new Connection(host, port, function (err) {
144
+ ctx.connect(self.options, function (err) {
96
145
  if (err) {
97
146
  self.db.emit('error', err);
98
147
  return;
99
148
  }
100
-
101
- ctx.attach(self.options, function(err) {
102
-
149
+ ctx.attach(self.options, function (err) {
103
150
  if (err) {
104
151
  self.db.emit('error', err);
105
152
  return;
106
153
  }
107
-
108
- ctx._queue = ctx._queue.concat(self._queue);
109
- ctx._pending = ctx._pending.concat(self._pending);
110
154
  self.db.emit('reconnect');
111
-
112
155
  }, self.db);
113
156
  });
114
-
115
157
  Object.assign(self, ctx);
116
-
117
158
  }, self.options, self.db);
118
159
  }, self._retry_connection_interval);
119
-
120
160
  });
121
-
122
- self._socket.on('error', function(e) {
123
-
161
+ self._socket.on('error', function (e) {
124
162
  self.error = e;
125
-
126
163
  if (self.db)
127
- self.db.emit('error', e)
128
-
164
+ self.db.emit('error', e);
129
165
  if (callback)
130
166
  callback(e);
131
-
132
167
  });
133
-
134
- self._socket.on('connect', function() {
168
+ self._socket.on('connect', function () {
135
169
  self._isClosed = false;
136
170
  self._isOpened = true;
137
171
  if (callback)
138
172
  callback();
139
173
  });
140
-
141
174
  self._socket.on('data', function (data) {
142
175
  var xdr;
143
176
  var hadSavedBuffer = Boolean(self._xdr);
144
-
145
177
  if (!self._xdr) {
146
- xdr = new XdrReader(data);
147
- } else {
148
- xdr = new XdrReader(Buffer.concat([self._xdr.buffer, data], self._xdr.buffer.length + data.length));
178
+ xdr = new serialize_1.XdrReader(data);
179
+ }
180
+ else {
181
+ xdr = new serialize_1.XdrReader(Buffer.concat([self._xdr.buffer, data], self._xdr.buffer.length + data.length));
149
182
  delete (self._xdr);
150
183
  }
151
-
152
184
  if (process.env.FIREBIRD_DEBUG) {
153
- console.log('[fb-debug] data event: bytes=%d queue=%d pending=%d xdr.pos=%d savedBuf=%s',
154
- xdr.buffer.length, self._queue.length, self._pending.length, xdr.pos, hadSavedBuffer);
185
+ console.log('[fb-debug] data event: bytes=%d queue=%d pending=%d xdr.pos=%d savedBuf=%s', xdr.buffer.length, self._queue.length, self._pending.length, xdr.pos, hadSavedBuffer);
155
186
  }
156
-
157
187
  while (xdr.pos < xdr.buffer.length) {
158
188
  var cb = self._queue[0], pos = xdr.pos;
159
189
  var lazySnapshot = cb ? cb.lazy_count : undefined;
160
-
161
190
  decodeResponse(xdr, cb, self, self._lowercase_keys, function (err, obj) {
162
-
163
191
  if (err) {
164
192
  if (err instanceof RangeError) {
165
193
  // Genuinely incomplete packet – buffer the remaining bytes
@@ -167,12 +195,9 @@ class Connection {
167
195
  xdr.buffer = xdr.buffer.slice(pos);
168
196
  xdr.pos = 0;
169
197
  self._xdr = xdr;
170
-
171
198
  if (process.env.FIREBIRD_DEBUG) {
172
- console.log('[fb-debug] incomplete packet: saved %d bytes at pos=%d queue=%d',
173
- xdr.buffer.length, pos, self._queue.length);
199
+ console.log('[fb-debug] incomplete packet: saved %d bytes at pos=%d queue=%d', xdr.buffer.length, pos, self._queue.length);
174
200
  }
175
-
176
201
  // Restore lazy_count to its value before this (failed)
177
202
  // decode attempt. Forcing lazy_count = 2 here made the
178
203
  // decoder expect a second op_response that never arrives
@@ -181,11 +206,13 @@ class Connection {
181
206
  if (cb) {
182
207
  if (lazySnapshot === undefined) {
183
208
  delete cb.lazy_count;
184
- } else {
209
+ }
210
+ else {
185
211
  cb.lazy_count = lazySnapshot;
186
212
  }
187
213
  }
188
- } else {
214
+ }
215
+ else {
189
216
  // Any other error (truly unknown opcode not handled above).
190
217
  // Save the buffer so it can be retried, but log a warning.
191
218
  if (process.env.FIREBIRD_DEBUG) {
@@ -197,437 +224,429 @@ class Connection {
197
224
  }
198
225
  return;
199
226
  }
200
-
201
227
  // remove the op flag, needed for partial packet
202
228
  if (xdr.r) {
203
229
  delete (xdr.r);
204
230
  }
205
-
206
231
  // op_event / op_response_piggyback received on the main connection:
207
232
  // data has been consumed by decodeResponse but it does not belong to
208
233
  // any queued request – do NOT shift the queue or invoke any pending
209
234
  // callback.
210
235
  if (obj && obj._isOpEvent) {
211
236
  if (process.env.FIREBIRD_DEBUG) {
212
- console.log('[fb-debug] async opcode consumed (ignored): queue=%d xdr.pos=%d remaining=%d',
213
- self._queue.length, xdr.pos, xdr.buffer.length - xdr.pos);
237
+ console.log('[fb-debug] async opcode consumed (ignored): queue=%d xdr.pos=%d remaining=%d', self._queue.length, xdr.pos, xdr.buffer.length - xdr.pos);
214
238
  }
215
239
  return;
216
240
  }
217
-
218
241
  self._queue.shift();
219
242
  self._pending.shift();
220
-
221
243
  if (process.env.FIREBIRD_DEBUG) {
222
- console.log('[fb-debug] response dispatched: queue remaining=%d pending remaining=%d xdr.pos=%d',
223
- self._queue.length, self._pending.length, xdr.pos);
244
+ console.log('[fb-debug] response dispatched: queue remaining=%d pending remaining=%d xdr.pos=%d', self._queue.length, self._pending.length, xdr.pos);
224
245
  }
225
-
226
246
  if (obj && obj.status) {
227
- obj.message = lookupMessages(obj.status);
228
- doCallback(obj, cb);
229
- } else {
230
- doCallback(obj, cb);
247
+ obj.message = (0, utils_1.lookupMessages)(obj.status);
248
+ (0, callback_1.doCallback)(obj, cb);
249
+ }
250
+ else {
251
+ (0, callback_1.doCallback)(obj, cb);
231
252
  }
232
-
233
253
  });
234
-
235
254
  if (xdr.pos === 0) {
236
255
  break;
237
256
  }
238
257
  }
239
-
240
258
  if (!self._detachAuto || self._pending.length !== 0) {
241
259
  return;
242
260
  }
243
-
244
261
  clearTimeout(self._detachTimeout);
245
262
  self._detachTimeout = setTimeout(function () {
246
263
  self.db.detach(self._detachCallback);
247
264
  self._detachAuto = false;
248
265
  }, 100);
249
-
250
266
  });
251
267
  }
252
-
253
-
254
268
  disconnect() {
255
269
  this._socket.end();
256
270
  }
257
-
258
-
259
-
260
-
261
-
262
271
  sendOpContAuth(authData, authDataEnc, pluginName) {
263
272
  var msg = this._msg;
264
273
  msg.pos = 0;
265
-
266
- msg.addInt(Const.op_cont_auth);
274
+ msg.addInt(const_1.default.op_cont_auth);
267
275
  msg.addString(authData, authDataEnc);
268
- msg.addString(pluginName, Const.DEFAULT_ENCODING)
269
- msg.addString(Const.AUTH_PLUGIN_LIST.join(','), Const.DEFAULT_ENCODING);
276
+ msg.addString(pluginName, const_1.default.DEFAULT_ENCODING);
277
+ msg.addString(const_1.default.AUTH_PLUGIN_LIST.join(','), const_1.default.DEFAULT_ENCODING);
270
278
  // msg.addInt(0); // p_list
271
279
  msg.addInt(0); // keys
272
-
273
280
  this._socket.write(msg.getData());
274
281
  }
275
-
276
-
277
282
  sendOpCrypt(encryptPlugin) {
278
283
  var msg = this._msg;
279
284
  msg.pos = 0;
280
-
281
- msg.addInt(Const.op_crypt);
282
- msg.addString(encryptPlugin || 'Arc4', Const.DEFAULT_ENCODING);
283
- msg.addString('Symmetric', Const.DEFAULT_ENCODING);
284
-
285
+ msg.addInt(const_1.default.op_crypt);
286
+ msg.addString(encryptPlugin || 'Arc4', const_1.default.DEFAULT_ENCODING);
287
+ msg.addString('Symmetric', const_1.default.DEFAULT_ENCODING);
285
288
  this._socket.write(msg.getData());
286
289
  }
287
-
288
-
289
290
  sendOpCryptKeyCallback(pluginData) {
290
291
  var msg = this._msg;
291
292
  msg.pos = 0;
292
-
293
- msg.addInt(Const.op_crypt_key_callback);
293
+ msg.addInt(const_1.default.op_crypt_key_callback);
294
294
  msg.addBlr(pluginData); // Send the callback response data as a buffer
295
-
296
295
  this._socket.write(msg.getData());
297
296
  }
298
-
299
-
300
297
  _queueEvent(callback, defer = false) {
301
298
  var self = this;
302
-
303
299
  if (self._isClosed) {
304
300
  if (callback)
305
301
  callback(new Error('Connection is closed.'));
306
302
  return;
307
303
  }
308
-
309
- const canDefer = defer && this.accept.protocolVersion >= Const.PROTOCOL_VERSION11;
310
-
304
+ const canDefer = defer && this.accept.protocolVersion >= const_1.default.PROTOCOL_VERSION11;
311
305
  self._socket.write(self._msg.getData(), canDefer);
312
306
  if (canDefer && callback) {
313
307
  callback();
314
- } else {
308
+ }
309
+ else {
315
310
  self._queue.push(callback);
316
311
  }
317
312
  }
318
-
319
-
320
313
  connect(options, callback) {
321
- var pluginName = options.manager ? Const.AUTH_PLUGIN_LEGACY : options.pluginName || Const.AUTH_PLUGIN_LIST[0]; // TODO Srp for service
314
+ var pluginName = options.pluginName || const_1.default.AUTH_PLUGIN_LIST[0];
322
315
  var msg = this._msg;
323
316
  var blr = this._blr;
324
-
325
317
  this._pending.push('connect');
326
318
  this._authStartTime = Date.now();
327
-
328
319
  msg.pos = 0;
329
320
  blr.pos = 0;
330
-
331
- blr.addString(Const.CNCT_login, options.user, Const.DEFAULT_ENCODING);
332
- blr.addString(Const.CNCT_plugin_name, pluginName, Const.DEFAULT_ENCODING);
333
- blr.addString(Const.CNCT_plugin_list, Const.AUTH_PLUGIN_LIST.join(','), Const.DEFAULT_ENCODING);
334
-
321
+ blr.addString(const_1.default.CNCT_login, options.user, const_1.default.DEFAULT_ENCODING);
322
+ blr.addString(const_1.default.CNCT_plugin_name, pluginName, const_1.default.DEFAULT_ENCODING);
323
+ blr.addString(const_1.default.CNCT_plugin_list, const_1.default.AUTH_PLUGIN_LIST.join(','), const_1.default.DEFAULT_ENCODING);
335
324
  var specificData = '';
336
- if (Const.AUTH_PLUGIN_SRP_LIST.indexOf(pluginName) > -1) {
325
+ if (const_1.default.AUTH_PLUGIN_SRP_LIST.indexOf(pluginName) > -1) {
337
326
  const _t0 = Date.now();
338
327
  this.clientKeys = srp.clientSeed();
339
328
  if (process.env.FIREBIRD_DEBUG) {
340
329
  console.log('[fb-debug] srp.clientSeed: %dms', Date.now() - _t0);
341
330
  }
342
331
  specificData = this.clientKeys.public.toString(16);
343
- blr.addMultiblockPart(Const.CNCT_specific_data, specificData, Const.DEFAULT_ENCODING);
344
- } else if (pluginName === Const.AUTH_PLUGIN_LEGACY) {
345
- specificData = crypt.crypt(options.password, Const.LEGACY_AUTH_SALT).substring(2);
346
- blr.addMultiblockPart(Const.CNCT_specific_data, specificData, Const.DEFAULT_ENCODING);
347
- } else {
348
- doError(new Error('Invalide auth plugin \'' + pluginName + '\''), callback);
332
+ blr.addMultiblockPart(const_1.default.CNCT_specific_data, specificData, const_1.default.DEFAULT_ENCODING);
333
+ }
334
+ else if (pluginName === const_1.default.AUTH_PLUGIN_LEGACY) {
335
+ specificData = crypt.crypt(options.password, const_1.default.LEGACY_AUTH_SALT).substring(2);
336
+ blr.addMultiblockPart(const_1.default.CNCT_specific_data, specificData, const_1.default.DEFAULT_ENCODING);
337
+ }
338
+ else {
339
+ (0, callback_1.doError)(new Error('Invalide auth plugin \'' + pluginName + '\''), callback);
349
340
  return;
350
341
  }
351
- blr.addBytes([Const.CNCT_client_crypt, 4, options.wireCrypt !== undefined ? options.wireCrypt : Const.WIRE_CRYPT_ENABLE, 0, 0, 0]);
352
- blr.addString(Const.CNCT_user, os.userInfo().username || 'Unknown', Const.DEFAULT_ENCODING);
353
- blr.addString(Const.CNCT_host, os.hostname(), Const.DEFAULT_ENCODING);
354
- blr.addBytes([Const.CNCT_user_verification, 0]);
355
-
356
- msg.addInt(Const.op_connect);
357
- msg.addInt(Const.op_attach);
358
- msg.addInt(Const.CONNECT_VERSION3);
359
- msg.addInt(Const.ARCHITECTURE_GENERIC);
360
- msg.addString(options.database || options.filename, Const.DEFAULT_ENCODING);
361
- msg.addInt(Const.SUPPORTED_PROTOCOL.length); // Count of Protocol version understood count.
342
+ blr.addBytes([const_1.default.CNCT_client_crypt, 4, options.wireCrypt !== undefined ? options.wireCrypt : const_1.default.WIRE_CRYPT_ENABLE, 0, 0, 0]);
343
+ blr.addString(const_1.default.CNCT_user, os_1.default.userInfo().username || 'Unknown', const_1.default.DEFAULT_ENCODING);
344
+ blr.addString(const_1.default.CNCT_host, os_1.default.hostname(), const_1.default.DEFAULT_ENCODING);
345
+ blr.addBytes([const_1.default.CNCT_user_verification, 0]);
346
+ msg.addInt(const_1.default.op_connect);
347
+ msg.addInt(const_1.default.op_attach);
348
+ msg.addInt(const_1.default.CONNECT_VERSION3);
349
+ msg.addInt(const_1.default.ARCHITECTURE_GENERIC);
350
+ msg.addString(options.database || options.filename, const_1.default.DEFAULT_ENCODING);
351
+ var maxProtocols = options.maxNegotiatedProtocols !== undefined ? options.maxNegotiatedProtocols : 10;
352
+ var protocolsToSend = const_1.default.SUPPORTED_PROTOCOL;
353
+ if (protocolsToSend.length > maxProtocols) {
354
+ protocolsToSend = protocolsToSend.slice(-maxProtocols);
355
+ }
356
+ msg.addInt(protocolsToSend.length); // Count of Protocol version understood count.
362
357
  msg.addBlr(this._blr);
363
-
364
- for (var protocol of Const.SUPPORTED_PROTOCOL) {
358
+ for (var protocol of protocolsToSend) {
365
359
  msg.addInt(protocol[0]); // Version
366
360
  msg.addInt(protocol[1]); // Architecture
367
361
  msg.addInt(protocol[2]); // Min type
368
- if (protocol[0] >= Const.PROTOCOL_VERSION13 && options.wireCompression) {
369
- msg.addInt(protocol[3] | Const.pflag_compress); // Max type with compress flag
370
- } else {
362
+ if (protocol[0] >= const_1.default.PROTOCOL_VERSION13 && options.wireCompression) {
363
+ msg.addInt(protocol[3] | const_1.default.pflag_compress); // Max type with compress flag
364
+ }
365
+ else {
371
366
  msg.addInt(protocol[3]); // Max type
372
367
  }
373
368
  msg.addInt(protocol[4]); // Preference weight
374
369
  }
375
-
376
370
  var self = this;
377
371
  function cb(err, ret) {
378
372
  if (err) {
379
- doError(err, callback);
373
+ (0, callback_1.doError)(err, callback);
380
374
  return;
381
375
  }
382
-
383
376
  // Check for pending accept from op_cond_accept flow
384
377
  if (self._pendingAccept) {
385
378
  ret = self._pendingAccept;
386
379
  delete self._pendingAccept;
387
380
  }
388
-
389
381
  self.accept = ret;
390
-
391
382
  // Wire encryption: send op_crypt if SRP session key is available
392
- if (ret.sessionKey && ret.protocolVersion >= Const.PROTOCOL_VERSION13 &&
393
- options.wireCrypt !== Const.WIRE_CRYPT_DISABLE) {
394
- var keyBuf = Buffer.from(ret.sessionKey.toString(16).padStart(40, '0'), 'hex');
395
- self.sendOpCrypt('Arc4');
396
- self._socket.enableEncryption(keyBuf);
383
+ if (ret.sessionKey && ret.protocolVersion >= const_1.default.PROTOCOL_VERSION13 &&
384
+ options.wireCrypt !== const_1.default.WIRE_CRYPT_DISABLE) {
385
+ var padLen = 40;
386
+ var keyBuf = Buffer.from(ret.sessionKey.toString(16).padStart(padLen, '0'), 'hex');
387
+ var selectedPlugin = 'Arc4';
388
+ if (ret.keys) {
389
+ var serverPlugins = ret.keys.split(',').map(function (s) { return s.trim().toLowerCase(); });
390
+ var preferred = ['chacha64', 'chacha', 'arc4'];
391
+ for (var i = 0; i < preferred.length; i++) {
392
+ if (serverPlugins.indexOf(preferred[i]) !== -1) {
393
+ var mapping = {
394
+ chacha64: 'ChaCha64',
395
+ chacha: 'ChaCha',
396
+ arc4: 'Arc4'
397
+ };
398
+ selectedPlugin = mapping[preferred[i]];
399
+ break;
400
+ }
401
+ }
402
+ }
403
+ // Send op_crypt BEFORE enabling encryption (op_crypt is sent plaintext)
404
+ // The server then enables encryption from its side.
405
+ // After the op_response to op_crypt, both sides are encrypted.
406
+ self.sendOpCrypt(selectedPlugin);
407
+ if (selectedPlugin === 'Arc4') {
408
+ self._socket.enableEncryption(keyBuf, 'Arc4');
409
+ }
397
410
  self._pending.push('crypt');
398
- self._queue.push(function(cryptErr) {
411
+ self._queue.push(function (cryptErr, response) {
399
412
  if (cryptErr) {
400
- doError(cryptErr, callback);
413
+ (0, callback_1.doError)(cryptErr, callback);
401
414
  return;
402
415
  }
416
+ if (selectedPlugin !== 'Arc4') {
417
+ var iv = response && response.buffer;
418
+ if (!iv || (selectedPlugin === 'ChaCha64' && iv.length < 8) || (selectedPlugin === 'ChaCha' && iv.length < 12)) {
419
+ var errIv = new Error('Invalid or missing IV for ' + selectedPlugin + ' encryption');
420
+ (0, callback_1.doError)(errIv, callback);
421
+ return;
422
+ }
423
+ self._socket.enableEncryption(keyBuf, selectedPlugin, iv);
424
+ }
403
425
  if (callback)
404
426
  callback(undefined, ret);
405
427
  });
406
428
  return;
407
429
  }
408
-
409
430
  if (callback)
410
431
  callback(undefined, ret);
411
432
  }
412
-
413
433
  if (process.env.FIREBIRD_DEBUG) {
414
434
  console.log('[fb-debug] auth: op_connect sent plugin=%s t=%dms', pluginName, Date.now() - this._authStartTime);
415
435
  }
416
436
  this._queueEvent(cb);
417
437
  }
418
-
419
-
420
438
  attach(options, callback, db) {
421
- this._lowercase_keys = options.lowercase_keys || Const.DEFAULT_LOWERCASE_KEYS;
422
-
439
+ this._lowercase_keys = options.lowercase_keys || const_1.default.DEFAULT_LOWERCASE_KEYS;
423
440
  var database = options.database || options.filename;
424
441
  if (database == null || database.length === 0) {
425
- doError(new Error('No database specified'), callback);
442
+ (0, callback_1.doError)(new Error('No database specified'), callback);
426
443
  return;
427
444
  }
428
-
429
- var user = options.user || Const.DEFAULT_USER;
430
- var password = options.password || Const.DEFAULT_PASSWORD;
445
+ var user = options.user || const_1.default.DEFAULT_USER;
446
+ var password = options.password || const_1.default.DEFAULT_PASSWORD;
431
447
  var role = options.role;
432
448
  var self = this;
433
449
  var msg = this._msg;
434
450
  var blr = this._blr;
435
451
  msg.pos = 0;
436
452
  blr.pos = 0;
437
-
438
- blr.addByte(Const.isc_dpb_version1);
439
- blr.addString(Const.isc_dpb_lc_ctype, options.encoding || 'UTF8', Const.DEFAULT_ENCODING);
440
-
453
+ blr.addByte(const_1.default.isc_dpb_version1);
454
+ blr.addString(const_1.default.isc_dpb_lc_ctype, options.encoding || 'UTF8', const_1.default.DEFAULT_ENCODING);
441
455
  // For Firebird 3+ (protocol 13+), add UTF-8 filename flag to ensure all DPB strings are handled with UTF-8
442
- if (this.accept.protocolVersion >= Const.PROTOCOL_VERSION13) {
443
- blr.addByte(Const.isc_dpb_utf8_filename);
456
+ if (this.accept.protocolVersion >= const_1.default.PROTOCOL_VERSION13) {
457
+ blr.addByte(const_1.default.isc_dpb_utf8_filename);
444
458
  blr.addByte(0);
445
459
  }
446
-
447
- blr.addString(Const.isc_dpb_user_name, user, Const.DEFAULT_ENCODING);
460
+ blr.addString(const_1.default.isc_dpb_user_name, user, const_1.default.DEFAULT_ENCODING);
448
461
  if (options.password && !this.accept.authData) {
449
- if (this.accept.protocolVersion < Const.PROTOCOL_VERSION13) {
450
- if (this.accept.protocolVersion === Const.PROTOCOL_VERSION10) {
451
- blr.addString(Const.isc_dpb_password, password, Const.DEFAULT_ENCODING);
452
- } else {
453
- blr.addString(Const.isc_dpb_password_enc, crypt.crypt(password, Const.LEGACY_AUTH_SALT).substring(2), Const.DEFAULT_ENCODING);
462
+ if (this.accept.protocolVersion < const_1.default.PROTOCOL_VERSION13) {
463
+ if (this.accept.protocolVersion === const_1.default.PROTOCOL_VERSION10) {
464
+ blr.addString(const_1.default.isc_dpb_password, password, const_1.default.DEFAULT_ENCODING);
465
+ }
466
+ else {
467
+ blr.addString(const_1.default.isc_dpb_password_enc, crypt.crypt(password, const_1.default.LEGACY_AUTH_SALT).substring(2), const_1.default.DEFAULT_ENCODING);
454
468
  }
455
469
  }
456
470
  }
457
-
458
471
  if (role)
459
- blr.addString(Const.isc_dpb_sql_role_name, role, Const.DEFAULT_ENCODING);
460
-
461
- blr.addBytes([Const.isc_dpb_process_id, 4]);
472
+ blr.addString(const_1.default.isc_dpb_sql_role_name, role, const_1.default.DEFAULT_ENCODING);
473
+ blr.addBytes([const_1.default.isc_dpb_process_id, 4]);
462
474
  blr.addInt32(process.pid);
463
-
464
- let processName = process.title || "";
465
- blr.addString(Const.isc_dpb_process_name, processName.length > 255 ? processName.substring(processName.length - 255, processName.length) : processName, Const.DEFAULT_ENCODING);
466
-
475
+ let processName = process.title || "";
476
+ blr.addString(const_1.default.isc_dpb_process_name, processName.length > 255 ? processName.substring(processName.length - 255, processName.length) : processName, const_1.default.DEFAULT_ENCODING);
467
477
  if (this.accept.authData) {
468
- blr.addString(Const.isc_dpb_specific_auth_data, this.accept.authData, Const.DEFAULT_ENCODING);
478
+ blr.addString(const_1.default.isc_dpb_specific_auth_data, this.accept.authData, const_1.default.DEFAULT_ENCODING);
469
479
  }
470
-
471
480
  if (options.sessionTimeZone) {
472
- blr.addString(Const.isc_dpb_session_time_zone, options.sessionTimeZone, Const.DEFAULT_ENCODING);
481
+ blr.addString(const_1.default.isc_dpb_session_time_zone, options.sessionTimeZone, const_1.default.DEFAULT_ENCODING);
473
482
  }
474
-
475
- msg.addInt(Const.op_attach);
476
- msg.addInt(0); // Database Object ID
477
- msg.addString(database, Const.DEFAULT_ENCODING);
483
+ if (options.parallelWorkers !== undefined) {
484
+ blr.addNumeric(const_1.default.isc_dpb_parallel_workers, options.parallelWorkers);
485
+ }
486
+ if (options.maxInlineBlobSize !== undefined) {
487
+ blr.addNumeric(const_1.default.isc_dpb_max_inline_blob_size, options.maxInlineBlobSize);
488
+ }
489
+ // Firebird 6.0 SQL Schema parameters (Protocol 20+).
490
+ // These DPB tags configure the session's current schema and the
491
+ // schema search path for unqualified object name resolution.
492
+ if (this.accept.protocolVersion >= const_1.default.PROTOCOL_VERSION20) {
493
+ if (options.defaultSchema) {
494
+ // Sets CURRENT_SCHEMA for the session. Equivalent to issuing
495
+ // SET SCHEMA <name> immediately after connecting.
496
+ blr.addString(const_1.default.isc_dpb_default_schema, options.defaultSchema, const_1.default.DEFAULT_ENCODING);
497
+ }
498
+ if (options.searchPath) {
499
+ // Comma-separated ordered schema name list, like PostgreSQL's
500
+ // search_path. Unqualified object references are resolved by
501
+ // scanning schemas in this order.
502
+ const sp = Array.isArray(options.searchPath)
503
+ ? options.searchPath.join(',')
504
+ : String(options.searchPath);
505
+ blr.addString(const_1.default.isc_dpb_search_path, sp, const_1.default.DEFAULT_ENCODING);
506
+ }
507
+ }
508
+ msg.addInt(const_1.default.op_attach);
509
+ msg.addInt(0); // Database Object ID
510
+ msg.addString(database, const_1.default.DEFAULT_ENCODING);
478
511
  msg.addBlr(this._blr);
479
-
480
512
  function cb(err, ret) {
481
513
  if (err) {
482
- doError(err, callback);
514
+ (0, callback_1.doError)(err, callback);
483
515
  return;
484
516
  }
485
-
486
517
  self.dbhandle = ret.handle;
487
518
  if (callback)
488
519
  callback(undefined, ret);
489
520
  if (!db)
490
521
  ret.emit('attach', ret);
491
522
  }
492
-
493
523
  // For reconnect
494
524
  if (db) {
495
525
  db.connection = this;
496
526
  cb.response = db;
497
- } else {
498
- cb.response = new Database(this);
527
+ }
528
+ else {
529
+ cb.response = new database_1.default(this);
499
530
  cb.response.removeAllListeners('error');
500
- cb.response.on('error', noop);
531
+ cb.response.on('error', utils_1.noop);
501
532
  }
502
-
503
533
  this._queueEvent(cb);
504
534
  }
505
-
506
-
507
535
  detach(callback) {
508
-
509
536
  var self = this;
510
-
511
537
  if (self._isClosed)
512
- return;
513
-
538
+ return this.throwClosed(callback);
514
539
  self._isUsed = false;
515
540
  self._isDetach = true;
516
-
517
541
  var msg = self._msg;
518
-
519
542
  msg.pos = 0;
520
- msg.addInt(Const.op_detach);
543
+ msg.addInt(const_1.default.op_detach);
521
544
  msg.addInt(0); // Database Object ID
522
-
523
- self._queueEvent(function(err, ret) {
545
+ self._queueEvent(function (err, ret) {
524
546
  clearTimeout(self._retry_connection_id);
525
- delete(self.dbhandle);
547
+ delete (self.dbhandle);
526
548
  if (callback)
527
549
  callback(err, ret);
528
550
  });
529
551
  }
530
-
531
-
532
552
  createDatabase(options, callback) {
553
+ // Mirror attach(): honour the lowercase_keys option so that db.query()
554
+ // called on a freshly-created database returns the expected column case.
555
+ this._lowercase_keys = options.lowercase_keys || const_1.default.DEFAULT_LOWERCASE_KEYS;
533
556
  var database = options.database || options.filename;
534
557
  if (database == null || database.length === 0) {
535
- doError(new Error('No database specified'), callback);
558
+ (0, callback_1.doError)(new Error('No database specified'), callback);
536
559
  return;
537
560
  }
538
-
539
- var user = options.user || Const.DEFAULT_USER;
540
- var password = options.password || Const.DEFAULT_PASSWORD;
541
- var pageSize = options.pageSize || Const.DEFAULT_PAGE_SIZE;
561
+ var user = options.user || const_1.default.DEFAULT_USER;
562
+ var password = options.password || const_1.default.DEFAULT_PASSWORD;
563
+ var pageSize = options.pageSize || const_1.default.DEFAULT_PAGE_SIZE;
542
564
  var role = options.role;
543
565
  var blr = this._blr;
544
-
545
566
  blr.pos = 0;
546
- blr.addByte(Const.isc_dpb_version1);
547
- blr.addString(Const.isc_dpb_set_db_charset, 'UTF8', Const.DEFAULT_ENCODING);
548
- blr.addString(Const.isc_dpb_lc_ctype, 'UTF8', Const.DEFAULT_ENCODING);
549
-
567
+ blr.addByte(const_1.default.isc_dpb_version1);
568
+ blr.addString(const_1.default.isc_dpb_set_db_charset, 'UTF8', const_1.default.DEFAULT_ENCODING);
569
+ blr.addString(const_1.default.isc_dpb_lc_ctype, 'UTF8', const_1.default.DEFAULT_ENCODING);
550
570
  // For Firebird 3+ (protocol 13+), add UTF-8 filename flag to ensure all DPB strings are handled with UTF-8
551
- if (this.accept.protocolVersion >= Const.PROTOCOL_VERSION13) {
552
- blr.addByte(Const.isc_dpb_utf8_filename);
571
+ if (this.accept.protocolVersion >= const_1.default.PROTOCOL_VERSION13) {
572
+ blr.addByte(const_1.default.isc_dpb_utf8_filename);
553
573
  blr.addByte(0);
554
574
  }
555
-
556
- blr.addString(Const.isc_dpb_user_name, user, Const.DEFAULT_ENCODING);
557
- if (this.accept.protocolVersion < Const.PROTOCOL_VERSION13) {
558
- if (this.accept.protocolVersion === Const.PROTOCOL_VERSION10) {
559
- blr.addString(Const.isc_dpb_password, password, Const.DEFAULT_ENCODING);
560
- } else {
561
- blr.addString(Const.isc_dpb_password_enc, crypt.crypt(password, Const.LEGACY_AUTH_SALT).substring(2), Const.DEFAULT_ENCODING);
575
+ blr.addString(const_1.default.isc_dpb_user_name, user, const_1.default.DEFAULT_ENCODING);
576
+ if (this.accept.protocolVersion < const_1.default.PROTOCOL_VERSION13) {
577
+ if (this.accept.protocolVersion === const_1.default.PROTOCOL_VERSION10) {
578
+ blr.addString(const_1.default.isc_dpb_password, password, const_1.default.DEFAULT_ENCODING);
579
+ }
580
+ else {
581
+ blr.addString(const_1.default.isc_dpb_password_enc, crypt.crypt(password, const_1.default.LEGACY_AUTH_SALT).substring(2), const_1.default.DEFAULT_ENCODING);
562
582
  }
563
583
  }
564
584
  if (role)
565
- blr.addString(Const.isc_dpb_sql_role_name, role, Const.DEFAULT_ENCODING);
566
-
567
- blr.addBytes([Const.isc_dpb_process_id, 4]);
585
+ blr.addString(const_1.default.isc_dpb_sql_role_name, role, const_1.default.DEFAULT_ENCODING);
586
+ blr.addBytes([const_1.default.isc_dpb_process_id, 4]);
568
587
  blr.addInt32(process.pid);
569
-
570
- let processName = process.title || "";
571
- blr.addString(Const.isc_dpb_process_name, processName.length > 255 ? processName.substring(processName.length - 255, processName.length) : processName, Const.DEFAULT_ENCODING);
572
-
588
+ let processName = process.title || "";
589
+ blr.addString(const_1.default.isc_dpb_process_name, processName.length > 255 ? processName.substring(processName.length - 255, processName.length) : processName, const_1.default.DEFAULT_ENCODING);
573
590
  if (this.accept.authData) {
574
- blr.addString(Const.isc_dpb_specific_auth_data, this.accept.authData, Const.DEFAULT_ENCODING);
591
+ blr.addString(const_1.default.isc_dpb_specific_auth_data, this.accept.authData, const_1.default.DEFAULT_ENCODING);
575
592
  }
576
-
577
593
  if (options.sessionTimeZone) {
578
- blr.addString(Const.isc_dpb_session_time_zone, options.sessionTimeZone, Const.DEFAULT_ENCODING);
594
+ blr.addString(const_1.default.isc_dpb_session_time_zone, options.sessionTimeZone, const_1.default.DEFAULT_ENCODING);
579
595
  }
580
-
581
- blr.addNumeric(Const.isc_dpb_sql_dialect, 3);
582
- blr.addNumeric(Const.isc_dpb_force_write, 1);
583
- blr.addNumeric(Const.isc_dpb_overwrite, 1);
584
- blr.addNumeric(Const.isc_dpb_page_size, pageSize);
585
-
596
+ if (options.parallelWorkers !== undefined) {
597
+ blr.addNumeric(const_1.default.isc_dpb_parallel_workers, options.parallelWorkers);
598
+ }
599
+ if (options.maxInlineBlobSize !== undefined) {
600
+ blr.addNumeric(const_1.default.isc_dpb_max_inline_blob_size, options.maxInlineBlobSize);
601
+ }
602
+ // Firebird 6.0 SQL Schema parameters (Protocol 20+).
603
+ if (this.accept.protocolVersion >= const_1.default.PROTOCOL_VERSION20) {
604
+ if (options.defaultSchema) {
605
+ blr.addString(const_1.default.isc_dpb_default_schema, options.defaultSchema, const_1.default.DEFAULT_ENCODING);
606
+ }
607
+ if (options.searchPath) {
608
+ const sp = Array.isArray(options.searchPath)
609
+ ? options.searchPath.join(',')
610
+ : String(options.searchPath);
611
+ blr.addString(const_1.default.isc_dpb_search_path, sp, const_1.default.DEFAULT_ENCODING);
612
+ }
613
+ }
614
+ blr.addNumeric(const_1.default.isc_dpb_sql_dialect, 3);
615
+ blr.addNumeric(const_1.default.isc_dpb_force_write, 1);
616
+ blr.addNumeric(const_1.default.isc_dpb_overwrite, 1);
617
+ blr.addNumeric(const_1.default.isc_dpb_page_size, pageSize);
586
618
  var msg = this._msg;
587
619
  msg.pos = 0;
588
- msg.addInt(Const.op_create); // op_create
589
- msg.addInt(0); // Database Object ID
590
- msg.addString(database, Const.DEFAULT_ENCODING);
620
+ msg.addInt(const_1.default.op_create); // op_create
621
+ msg.addInt(0); // Database Object ID
622
+ msg.addString(database, const_1.default.DEFAULT_ENCODING);
591
623
  msg.addBlr(blr);
592
-
593
624
  var self = this;
594
-
595
625
  function cb(err, ret) {
596
-
597
626
  if (ret)
598
627
  self.dbhandle = ret.handle;
599
-
600
628
  if (callback)
601
629
  callback(err, ret);
602
-
603
630
  if (!err && ret)
604
631
  ret.emit('attach', ret);
605
632
  }
606
-
607
- cb.response = new Database(this);
633
+ cb.response = new database_1.default(this);
608
634
  this._queueEvent(cb);
609
635
  }
610
-
611
-
612
636
  dropDatabase(callback) {
613
637
  var msg = this._msg;
614
638
  msg.pos = 0;
615
-
616
- msg.addInt(Const.op_drop_database);
639
+ msg.addInt(const_1.default.op_drop_database);
617
640
  msg.addInt(this.dbhandle);
618
-
619
641
  var self = this;
620
- this._queueEvent(function(err) {
621
- self.detach(function() {
642
+ this._queueEvent(function (err) {
643
+ self.detach(function () {
622
644
  self.disconnect();
623
-
624
645
  if (callback)
625
646
  callback(err);
626
647
  });
627
648
  });
628
649
  }
629
-
630
-
631
650
  throwClosed(callback) {
632
651
  var err = new Error('Connection is closed.');
633
652
  this.db.emit('error', err);
@@ -635,68 +654,58 @@ class Connection {
635
654
  callback(err);
636
655
  return this;
637
656
  }
638
-
639
-
640
657
  startTransaction(options, callback) {
641
-
642
- if (typeof(options) === 'function') {
658
+ if (typeof (options) === 'function') {
643
659
  var tmp = options;
644
660
  options = callback;
645
661
  callback = tmp;
646
662
  }
647
-
648
663
  // Compatibility
649
664
  if (Array.isArray(options)) {
650
665
  options = {
651
666
  isolation: options,
652
- readOnly: (options === Const.ISOLATION_READ_COMMITTED_READ_ONLY),
667
+ readOnly: (options === const_1.default.ISOLATION_READ_COMMITTED_READ_ONLY),
653
668
  };
654
669
  }
655
-
656
670
  // Default options
657
671
  options = Object.assign({
658
672
  autoCommit: false,
659
673
  autoUndo: true,
660
- isolation: Const.ISOLATION_READ_COMMITTED,
674
+ isolation: const_1.default.ISOLATION_READ_COMMITTED,
661
675
  ignoreLimbo: false,
662
676
  //lock: [],
663
677
  readOnly: false,
664
678
  wait: true,
665
679
  waitTimeout: 0,
666
680
  }, options);
667
-
668
681
  if (this._isClosed)
669
682
  return this.throwClosed(callback);
670
-
671
683
  // for auto detach
672
684
  this._pending.push('startTransaction');
673
-
674
685
  var blr = this._blr;
675
686
  var msg = this._msg;
676
-
677
687
  blr.pos = 0;
678
688
  msg.pos = 0;
679
-
680
- blr.addByte(Const.isc_tpb_version3);
689
+ blr.addByte(const_1.default.isc_tpb_version3);
681
690
  blr.addBytes(options.isolation);
682
- blr.addByte(options.readOnly ? Const.isc_tpb_read : Const.isc_tpb_write);
691
+ blr.addByte(options.readOnly ? const_1.default.isc_tpb_read : const_1.default.isc_tpb_write);
683
692
  if (options.wait) {
684
- blr.addByte(Const.isc_tpb_wait);
685
-
693
+ blr.addByte(const_1.default.isc_tpb_wait);
686
694
  if (options.waitTimeout) {
687
- blr.addNumeric(Const.isc_tpb_lock_timeout, options.waitTimeout);
695
+ blr.addNumeric(const_1.default.isc_tpb_lock_timeout, options.waitTimeout);
688
696
  }
689
- } else {
690
- blr.addByte(Const.isc_tpb_nowait);
697
+ }
698
+ else {
699
+ blr.addByte(const_1.default.isc_tpb_nowait);
691
700
  }
692
701
  if (!options.autoUndo) {
693
- blr.addByte(Const.isc_tpb_no_auto_undo);
702
+ blr.addByte(const_1.default.isc_tpb_no_auto_undo);
694
703
  }
695
704
  if (options.autoCommit) {
696
- blr.addByte(Const.isc_tpb_autocommit);
705
+ blr.addByte(const_1.default.isc_tpb_autocommit);
697
706
  }
698
707
  if (options.ignoreLimbo) {
699
- blr.addByte(Const.isc_tpb_ignore_limbo);
708
+ blr.addByte(const_1.default.isc_tpb_ignore_limbo);
700
709
  }
701
710
  // TODO
702
711
  /*if (options.lock.length) {
@@ -708,139 +717,98 @@ class Connection {
708
717
  blr.addByte(lockType);
709
718
  }
710
719
  }*/
711
-
712
- msg.addInt(Const.op_transaction);
720
+ msg.addInt(const_1.default.op_transaction);
713
721
  msg.addInt(this.dbhandle);
714
722
  msg.addBlr(blr);
715
- callback.response = new Transaction(this);
716
-
723
+ callback.response = new transaction_1.default(this);
717
724
  this.db.emit('transaction', options);
718
725
  this._queueEvent(callback);
719
726
  }
720
-
721
-
722
727
  commit(transaction, callback) {
723
-
724
728
  if (this._isClosed)
725
729
  return this.throwClosed(callback);
726
-
727
730
  // for auto detach
728
731
  this._pending.push('commit');
729
-
730
732
  var msg = this._msg;
731
733
  msg.pos = 0;
732
- msg.addInt(Const.op_commit);
734
+ msg.addInt(const_1.default.op_commit);
733
735
  msg.addInt(transaction.handle);
734
736
  this.db.emit('commit');
735
737
  this._queueEvent(callback);
736
738
  }
737
-
738
-
739
739
  rollback(transaction, callback) {
740
-
741
740
  if (this._isClosed)
742
741
  return this.throwClosed(callback);
743
-
744
742
  // for auto detach
745
743
  this._pending.push('rollback');
746
-
747
744
  var msg = this._msg;
748
745
  msg.pos = 0;
749
- msg.addInt(Const.op_rollback);
746
+ msg.addInt(const_1.default.op_rollback);
750
747
  msg.addInt(transaction.handle);
751
748
  this.db.emit('rollback');
752
749
  this._queueEvent(callback);
753
750
  }
754
-
755
-
756
751
  commitRetaining(transaction, callback) {
757
-
758
752
  if (this._isClosed)
759
- throw new Error('Connection is closed.');
760
-
753
+ return this.throwClosed(callback);
761
754
  // for auto detach
762
755
  this._pending.push('commitRetaining');
763
-
764
756
  var msg = this._msg;
765
757
  msg.pos = 0;
766
- msg.addInt(Const.op_commit_retaining);
758
+ msg.addInt(const_1.default.op_commit_retaining);
767
759
  msg.addInt(transaction.handle);
768
760
  this._queueEvent(callback);
769
761
  }
770
-
771
-
772
762
  rollbackRetaining(transaction, callback) {
773
-
774
763
  if (this._isClosed)
775
764
  return this.throwClosed(callback);
776
-
777
765
  // for auto detach
778
766
  this._pending.push('rollbackRetaining');
779
-
780
767
  var msg = this._msg;
781
768
  msg.pos = 0;
782
- msg.addInt(Const.op_rollback_retaining);
769
+ msg.addInt(const_1.default.op_rollback_retaining);
783
770
  msg.addInt(transaction.handle);
784
771
  this._queueEvent(callback);
785
772
  }
786
-
787
-
788
773
  allocateStatement(callback) {
789
-
790
774
  if (this._isClosed)
791
775
  return this.throwClosed(callback);
792
-
793
776
  // for auto detach
794
777
  this._pending.push('allocateStatement');
795
-
796
778
  var msg = this._msg;
797
779
  msg.pos = 0;
798
- msg.addInt(Const.op_allocate_statement);
780
+ msg.addInt(const_1.default.op_allocate_statement);
799
781
  msg.addInt(this.dbhandle);
800
- callback.response = new Statement(this);
782
+ callback.response = new statement_1.default(this);
801
783
  this._queueEvent(callback);
802
784
  }
803
-
804
-
805
785
  dropStatement(statement, callback) {
806
-
807
786
  if (this._isClosed)
808
787
  return this.throwClosed(callback);
809
-
810
788
  // for auto detach
811
789
  this._pending.push('dropStatement');
812
-
813
790
  var msg = this._msg;
814
791
  msg.pos = 0;
815
- msg.addInt(Const.op_free_statement);
792
+ msg.addInt(const_1.default.op_free_statement);
816
793
  msg.addInt(statement.handle);
817
- msg.addInt(Const.DSQL_drop);
818
-
794
+ msg.addInt(const_1.default.DSQL_drop);
819
795
  this._queueEvent(callback, true);
820
796
  }
821
-
822
-
823
797
  closeStatement(statement, callback) {
824
-
825
798
  if (this._isClosed)
826
799
  return this.throwClosed(callback);
827
-
828
800
  // for auto detach
829
801
  this._pending.push('closeStatement');
830
-
831
802
  var msg = this._msg;
832
803
  msg.pos = 0;
833
- msg.addInt(Const.op_free_statement);
804
+ msg.addInt(const_1.default.op_free_statement);
834
805
  msg.addInt(statement.handle);
835
- msg.addInt(Const.DSQL_close);
836
-
806
+ msg.addInt(const_1.default.DSQL_close);
837
807
  this._queueEvent(callback, true);
838
808
  }
839
-
840
-
841
809
  allocateAndPrepareStatement(transaction, query, plan, callback) {
842
810
  var self = this;
843
- var mainCallback = function(err, ret) {
811
+ var mainCallback = function (err, ret) {
844
812
  if (!err) {
845
813
  mainCallback.response.handle = ret.handle;
846
814
  describe(ret.buffer, mainCallback.response);
@@ -849,93 +817,75 @@ class Connection {
849
817
  ret = mainCallback.response;
850
818
  self._setcachedquery(query, ret);
851
819
  }
852
-
853
820
  if (callback)
854
821
  callback(err, ret);
855
822
  };
856
-
857
823
  // for auto detach
858
824
  this._pending.push('allocateAndPrepareStatement');
859
-
860
825
  var msg = this._msg;
861
826
  var blr = this._blr;
862
-
863
827
  msg.pos = 0;
864
828
  blr.pos = 0;
865
-
866
- msg.addInt(Const.op_allocate_statement);
829
+ msg.addInt(const_1.default.op_allocate_statement);
867
830
  msg.addInt(this.dbhandle);
868
831
  mainCallback.lazy_count = 1;
869
-
870
- blr.addBytes(Const.DESCRIBE);
832
+ const describeBytes = this.accept.protocolVersion >= const_1.default.PROTOCOL_VERSION20 ? const_1.default.DESCRIBE_WITH_SCHEMA : const_1.default.DESCRIBE;
833
+ if (process.env.FIREBIRD_DEBUG) {
834
+ console.log('[fb-debug] describeBytes:', describeBytes);
835
+ }
836
+ blr.addBytes(describeBytes);
871
837
  if (plan)
872
- blr.addByte(Const.isc_info_sql_get_plan);
873
-
874
- msg.addInt(Const.op_prepare_statement);
838
+ blr.addByte(const_1.default.isc_info_sql_get_plan);
839
+ msg.addInt(const_1.default.op_prepare_statement);
875
840
  msg.addInt(transaction.handle);
876
841
  msg.addInt(0xFFFF);
877
842
  msg.addInt(3); // dialect = 3
878
- msg.addString(query, Const.DEFAULT_ENCODING);
843
+ msg.addString(query, const_1.default.DEFAULT_ENCODING);
879
844
  msg.addBlr(blr);
880
845
  msg.addInt(65535); // buffer_length
881
846
  mainCallback.lazy_count += 1;
882
-
883
- mainCallback.response = new Statement(this);
847
+ mainCallback.response = new statement_1.default(this);
884
848
  this._queueEvent(mainCallback);
885
849
  }
886
-
887
-
888
850
  prepare(transaction, query, plan, callback) {
889
851
  var self = this;
890
-
891
- if (this.accept.protocolMinimumType === Const.ptype_lazy_send) { // V11 Statement or higher
852
+ if (this.accept.protocolMinimumType === const_1.default.ptype_lazy_send) { // V11 Statement or higher
892
853
  self.allocateAndPrepareStatement(transaction, query, plan, callback);
893
- } else { // V10 Statement
854
+ }
855
+ else { // V10 Statement
894
856
  self.allocateStatement(function (err, statement) {
895
857
  if (err) {
896
- doError(err, callback);
858
+ (0, callback_1.doError)(err, callback);
897
859
  return;
898
860
  }
899
-
900
861
  self.prepareStatement(transaction, statement, query, plan, callback);
901
862
  });
902
863
  }
903
864
  }
904
-
905
-
906
-
907
865
  prepareStatement(transaction, statement, query, plan, callback) {
908
-
909
866
  if (this._isClosed)
910
867
  return this.throwClosed(callback);
911
-
912
868
  var msg = this._msg;
913
869
  var blr = this._blr;
914
-
915
870
  msg.pos = 0;
916
871
  blr.pos = 0;
917
-
918
872
  if (plan instanceof Function) {
919
873
  callback = plan;
920
874
  plan = false;
921
875
  }
922
-
923
- blr.addBytes(Const.DESCRIBE);
924
-
876
+ const describeBytes = this.accept.protocolVersion >= const_1.default.PROTOCOL_VERSION20 ? const_1.default.DESCRIBE_WITH_SCHEMA : const_1.default.DESCRIBE;
877
+ blr.addBytes(describeBytes);
925
878
  if (plan)
926
- blr.addByte(Const.isc_info_sql_get_plan);
927
-
928
- msg.addInt(Const.op_prepare_statement);
879
+ blr.addByte(const_1.default.isc_info_sql_get_plan);
880
+ msg.addInt(const_1.default.op_prepare_statement);
929
881
  msg.addInt(transaction.handle);
930
882
  msg.addInt(statement.handle);
931
883
  msg.addInt(3); // dialect = 3
932
- msg.addString(query, Const.DEFAULT_ENCODING);
884
+ msg.addString(query, const_1.default.DEFAULT_ENCODING);
933
885
  msg.addBlr(blr);
934
886
  msg.addInt(65535); // buffer_length
935
-
936
887
  var self = this;
937
- this._queueEvent(function(err, ret) {
938
-
888
+ this._queueEvent(function (err, ret) {
939
889
  if (!err) {
940
890
  describe(ret.buffer, statement);
941
891
  statement.query = query;
@@ -943,75 +893,53 @@ class Connection {
943
893
  ret = statement;
944
894
  self._setcachedquery(query, ret);
945
895
  }
946
-
947
896
  if (callback)
948
897
  callback(err, ret);
949
898
  });
950
-
951
899
  }
952
-
953
-
954
-
955
900
  executeStatement(transaction, statement, params, callback, custom) {
956
-
957
901
  if (this._isClosed)
958
902
  return this.throwClosed(callback);
959
-
960
903
  // for auto detach
961
904
  this._pending.push('executeStatement');
962
-
963
905
  if (params instanceof Function) {
964
906
  callback = params;
965
907
  params = undefined;
966
908
  }
967
-
968
909
  var self = this;
969
-
970
- var op = Const.op_execute;
971
- if (
972
- this.accept.protocolVersion >= Const.PROTOCOL_VERSION13 &&
973
- statement.type === Const.isc_info_sql_stmt_exec_procedure &&
974
- statement.output.length
975
- ) {
976
- op = Const.op_execute2;
910
+ var op = const_1.default.op_execute;
911
+ if (this.accept.protocolVersion >= const_1.default.PROTOCOL_VERSION13 &&
912
+ statement.type === const_1.default.isc_info_sql_stmt_exec_procedure &&
913
+ statement.output.length) {
914
+ op = const_1.default.op_execute2;
977
915
  }
978
-
979
916
  function PrepareParams(params, input, callback) {
980
-
981
917
  var value, meta;
982
918
  var ret = new Array(params.length);
983
-
984
919
  function putBlobData(index, value, callback) {
985
-
986
- self.createBlob2(transaction, function(err, blob) {
987
-
920
+ self.createBlob2(transaction, function (err, blob) {
988
921
  var b;
989
922
  var isStream = value.readable;
990
-
991
923
  if (Buffer.isBuffer(value))
992
924
  b = value;
993
- else if (typeof(value) === 'string')
994
- b = Buffer.from(value, Const.DEFAULT_ENCODING);
925
+ else if (typeof (value) === 'string')
926
+ b = Buffer.from(value, const_1.default.DEFAULT_ENCODING);
995
927
  else if (!isStream)
996
- b = Buffer.from(JSON.stringify(value), Const.DEFAULT_ENCODING);
997
-
928
+ b = Buffer.from(JSON.stringify(value), const_1.default.DEFAULT_ENCODING);
998
929
  // Use configured transfer size or default to 1024
999
930
  var chunkSize = self.options.blobChunkSize || 1024;
1000
-
1001
931
  if (Buffer.isBuffer(b)) {
1002
932
  bufferReader(b, chunkSize, function (b, next) {
1003
933
  self.batchSegments(blob, b, next);
1004
- }, function() {
934
+ }, function () {
1005
935
  ret[index] = new Xsql.SQLParamQuad(blob.oid);
1006
936
  self.closeBlob(blob, callback, false);
1007
937
  });
1008
938
  return;
1009
939
  }
1010
-
1011
940
  var isReading = false;
1012
941
  var isEnd = false;
1013
-
1014
- value.on('data', function(chunk) {
942
+ value.on('data', function (chunk) {
1015
943
  // Optimization: If chunk is smaller than transfer size, send directly
1016
944
  if (chunk.length <= chunkSize) {
1017
945
  self.batchSegments(blob, chunk, function () {
@@ -1022,23 +950,21 @@ class Connection {
1022
950
  });
1023
951
  return;
1024
952
  }
1025
-
1026
953
  value.pause();
1027
954
  isReading = true;
1028
955
  bufferReader(chunk, chunkSize, function (b, next) {
1029
956
  self.batchSegments(blob, b, next);
1030
- }, function() {
957
+ }, function () {
1031
958
  isReading = false;
1032
-
1033
959
  if (isEnd) {
1034
960
  ret[index] = new Xsql.SQLParamQuad(blob.oid);
1035
961
  self.closeBlob(blob, callback, false);
1036
- } else
962
+ }
963
+ else
1037
964
  value.resume();
1038
965
  });
1039
966
  });
1040
-
1041
- value.on('end', function() {
967
+ value.on('end', function () {
1042
968
  isEnd = true;
1043
969
  if (isReading)
1044
970
  return;
@@ -1051,164 +977,186 @@ class Connection {
1051
977
  });
1052
978
  });
1053
979
  }
1054
-
1055
980
  function step(i) {
1056
981
  if (i === params.length) {
1057
982
  callback(ret);
1058
983
  return;
1059
984
  }
1060
-
1061
985
  value = params[i];
1062
986
  meta = input[i];
1063
-
987
+ if (statement.connection.options && statement.connection.options.jsonAsObject && value !== null && value !== undefined && typeof value === 'object' && !(value instanceof Date) && !(value instanceof Buffer)) {
988
+ if (typeof value.pipe !== 'function') {
989
+ value = JSON.stringify(value);
990
+ }
991
+ }
1064
992
  if (value === null || value === undefined) {
1065
993
  switch (meta.type) {
1066
- case Const.SQL_VARYING:
1067
- case Const.SQL_NULL:
1068
- case Const.SQL_TEXT:
994
+ case const_1.default.SQL_VARYING:
995
+ case const_1.default.SQL_NULL:
996
+ case const_1.default.SQL_TEXT:
1069
997
  ret[i] = new Xsql.SQLParamString(null);
1070
998
  break;
1071
- case Const.SQL_DOUBLE:
1072
- case Const.SQL_FLOAT:
1073
- case Const.SQL_D_FLOAT:
999
+ case const_1.default.SQL_DOUBLE:
1000
+ case const_1.default.SQL_FLOAT:
1001
+ case const_1.default.SQL_D_FLOAT:
1074
1002
  ret[i] = new Xsql.SQLParamDouble(null);
1075
1003
  break;
1076
- case Const.SQL_TYPE_DATE:
1077
- case Const.SQL_TYPE_TIME:
1078
- case Const.SQL_TIMESTAMP:
1079
- case Const.SQL_TIME_TZ:
1080
- case Const.SQL_TIME_TZ_EX:
1081
- case Const.SQL_TIMESTAMP_TZ:
1082
- case Const.SQL_TIMESTAMP_TZ_EX:
1004
+ case const_1.default.SQL_TYPE_DATE:
1005
+ case const_1.default.SQL_TYPE_TIME:
1006
+ case const_1.default.SQL_TIMESTAMP:
1007
+ case const_1.default.SQL_TIME_TZ:
1008
+ case const_1.default.SQL_TIME_TZ_EX:
1009
+ case const_1.default.SQL_TIMESTAMP_TZ:
1010
+ case const_1.default.SQL_TIMESTAMP_TZ_EX:
1083
1011
  ret[i] = new Xsql.SQLParamDate(null);
1084
1012
  break;
1085
- case Const.SQL_BLOB:
1086
- case Const.SQL_ARRAY:
1087
- case Const.SQL_QUAD:
1013
+ case const_1.default.SQL_BLOB:
1014
+ case const_1.default.SQL_ARRAY:
1015
+ case const_1.default.SQL_QUAD:
1088
1016
  ret[i] = new Xsql.SQLParamQuad(null);
1089
1017
  break;
1090
- case Const.SQL_LONG:
1091
- case Const.SQL_SHORT:
1092
- case Const.SQL_INT64:
1093
- case Const.SQL_BOOLEAN:
1018
+ case const_1.default.SQL_LONG:
1019
+ case const_1.default.SQL_SHORT:
1020
+ case const_1.default.SQL_INT64:
1021
+ case const_1.default.SQL_BOOLEAN:
1094
1022
  ret[i] = new Xsql.SQLParamInt(null);
1095
1023
  break;
1096
1024
  default:
1097
1025
  ret[i] = null;
1098
1026
  }
1099
1027
  step(i + 1);
1100
- } else {
1028
+ }
1029
+ else {
1101
1030
  switch (meta.type) {
1102
- case Const.SQL_BLOB:
1103
- putBlobData(i, value, function() { step(i + 1); });
1031
+ case const_1.default.SQL_BLOB:
1032
+ putBlobData(i, value, function () { step(i + 1); });
1104
1033
  break;
1105
-
1106
- case Const.SQL_TIMESTAMP:
1107
- case Const.SQL_TYPE_DATE:
1108
- case Const.SQL_TYPE_TIME:
1109
- case Const.SQL_TIME_TZ:
1110
- case Const.SQL_TIME_TZ_EX:
1111
- case Const.SQL_TIMESTAMP_TZ:
1112
- case Const.SQL_TIMESTAMP_TZ_EX:
1034
+ case const_1.default.SQL_TIMESTAMP:
1035
+ case const_1.default.SQL_TYPE_DATE:
1036
+ case const_1.default.SQL_TYPE_TIME:
1037
+ case const_1.default.SQL_TIME_TZ:
1038
+ case const_1.default.SQL_TIME_TZ_EX:
1039
+ case const_1.default.SQL_TIMESTAMP_TZ:
1040
+ case const_1.default.SQL_TIMESTAMP_TZ_EX:
1113
1041
  if (value instanceof Date)
1114
1042
  ret[i] = new Xsql.SQLParamDate(value);
1115
- else if (typeof(value) === 'string')
1116
- ret[i] = new Xsql.SQLParamDate(parseDate(value));
1043
+ else if (typeof (value) === 'string')
1044
+ ret[i] = new Xsql.SQLParamDate((0, utils_1.parseDate)(value));
1117
1045
  else
1118
1046
  ret[i] = new Xsql.SQLParamDate(new Date(value));
1119
-
1120
1047
  step(i + 1);
1121
1048
  break;
1122
-
1123
1049
  default:
1124
- switch (typeof value) {
1125
- case 'bigint':
1126
- ret[i] = new Xsql.SQLParamInt128(value);
1127
- break;
1128
- case 'number':
1129
- if (value % 1 === 0) {
1130
- if (value >= Const.MIN_INT && value <= Const.MAX_INT)
1131
- ret[i] = new Xsql.SQLParamInt(value);
1050
+ if (Buffer.isBuffer(value)) {
1051
+ ret[i] = new Xsql.SQLParamBuffer(value);
1052
+ }
1053
+ else {
1054
+ switch (typeof value) {
1055
+ case 'bigint':
1056
+ ret[i] = new Xsql.SQLParamInt128(value);
1057
+ break;
1058
+ case 'number':
1059
+ if (value % 1 === 0) {
1060
+ if (value >= const_1.default.MIN_INT && value <= const_1.default.MAX_INT)
1061
+ ret[i] = new Xsql.SQLParamInt(value);
1062
+ else
1063
+ ret[i] = new Xsql.SQLParamInt64(value);
1064
+ }
1132
1065
  else
1133
- ret[i] = new Xsql.SQLParamInt64(value);
1134
- } else
1135
- ret[i] = new Xsql.SQLParamDouble(value);
1136
- break;
1137
- case 'string':
1138
- ret[i] = new Xsql.SQLParamString(value);
1139
- break;
1140
- case 'boolean':
1141
- ret[i] = new Xsql.SQLParamBool(value);
1142
- break;
1143
- default:
1144
- //throw new Error('Unexpected parametter: ' + JSON.stringify(params) + ' - ' + JSON.stringify(input));
1145
- ret[i] = new Xsql.SQLParamString(value.toString());
1146
- break;
1066
+ ret[i] = new Xsql.SQLParamDouble(value);
1067
+ break;
1068
+ case 'string':
1069
+ ret[i] = new Xsql.SQLParamString(value);
1070
+ break;
1071
+ case 'boolean':
1072
+ ret[i] = new Xsql.SQLParamBool(value);
1073
+ break;
1074
+ default:
1075
+ //throw new Error('Unexpected parametter: ' + JSON.stringify(params) + ' - ' + JSON.stringify(input));
1076
+ ret[i] = new Xsql.SQLParamString(value.toString());
1077
+ break;
1078
+ }
1147
1079
  }
1148
1080
  step(i + 1);
1149
1081
  }
1150
1082
  }
1151
1083
  }
1152
-
1153
1084
  step(0);
1154
1085
  }
1155
-
1156
1086
  var input = statement.input;
1157
-
1158
1087
  if (input.length) {
1159
-
1160
1088
  if (!(params instanceof Array)) {
1161
- if (params !== undefined)
1089
+ if (params !== undefined && typeof params === 'object' && params !== null) {
1090
+ var mappedParams = [];
1091
+ for (var i = 0; i < input.length; i++) {
1092
+ mappedParams.push(undefined);
1093
+ }
1094
+ var matchedCount = 0;
1095
+ var nameMap = {};
1096
+ for (var i = 0; i < input.length; i++) {
1097
+ var name = input[i].alias || input[i].field;
1098
+ if (name) {
1099
+ nameMap[name.toUpperCase()] = i;
1100
+ }
1101
+ }
1102
+ for (var key in params) {
1103
+ if (Object.prototype.hasOwnProperty.call(params, key)) {
1104
+ var cleanKey = key.startsWith(':') ? key.substring(1) : key;
1105
+ var index = nameMap[cleanKey.toUpperCase()];
1106
+ if (index !== undefined) {
1107
+ mappedParams[index] = params[key];
1108
+ matchedCount++;
1109
+ }
1110
+ }
1111
+ }
1112
+ if (matchedCount > 0) {
1113
+ params = mappedParams;
1114
+ }
1115
+ else {
1116
+ params = [params];
1117
+ }
1118
+ }
1119
+ else if (params !== undefined) {
1162
1120
  params = [params];
1163
- else
1121
+ }
1122
+ else {
1164
1123
  params = [];
1124
+ }
1165
1125
  }
1166
-
1167
1126
  if (params.length !== input.length) {
1168
1127
  self._pending.pop();
1169
1128
  callback(new Error('Expected parameters: (params=' + params.length + ' vs. expected=' + input.length + ') - ' + statement.query));
1170
1129
  return;
1171
1130
  }
1172
-
1173
- PrepareParams(params, input, function(prms) {
1131
+ PrepareParams(params, input, function (prms) {
1174
1132
  self.sendExecute(op, statement, transaction, callback, prms);
1175
1133
  });
1176
-
1177
1134
  return;
1178
1135
  }
1179
-
1180
1136
  this.sendExecute(op, statement, transaction, callback);
1181
1137
  }
1182
-
1183
-
1184
1138
  sendExecute(op, statement, transaction, callback, parameters) {
1185
1139
  var msg = this._msg;
1186
1140
  var blr = this._blr;
1187
1141
  msg.pos = 0;
1188
1142
  blr.pos = 0;
1189
-
1190
1143
  msg.addInt(op);
1191
1144
  msg.addInt(statement.handle);
1192
1145
  msg.addInt(transaction.handle);
1193
-
1194
1146
  if (parameters && parameters.length) {
1195
1147
  CalcBlr(blr, parameters);
1196
- msg.addBlr(blr); // params blr
1148
+ msg.addBlr(blr); // params blr
1197
1149
  msg.addInt(0); // message number
1198
1150
  msg.addInt(1); // param count
1199
-
1200
- if (this.accept.protocolVersion >= Const.PROTOCOL_VERSION13) {
1151
+ if (this.accept.protocolVersion >= const_1.default.PROTOCOL_VERSION13) {
1201
1152
  // start with null indicator bitmap
1202
- var nullBits = new BitSet();
1203
-
1153
+ var nullBits = new serialize_1.BitSet();
1204
1154
  for (var i = 0; i < parameters.length; i++) {
1205
- nullBits.set(i, (parameters[i].value === null) & 1);
1155
+ nullBits.set(i, parameters[i].value === null ? 1 : 0);
1206
1156
  }
1207
-
1208
1157
  var nullBuffer = nullBits.toBuffer();
1209
1158
  var requireBytes = Math.floor((parameters.length + 7) / 8);
1210
1159
  var remainingBytes = requireBytes - nullBuffer.length;
1211
-
1212
1160
  if (nullBuffer.length) {
1213
1161
  msg.addBuffer(nullBuffer);
1214
1162
  }
@@ -1216,75 +1164,124 @@ class Connection {
1216
1164
  msg.addBuffer(Buffer.alloc(remainingBytes));
1217
1165
  }
1218
1166
  msg.addAlignment(requireBytes);
1219
-
1220
- for(var i = 0; i < parameters.length; i++) {
1167
+ for (var i = 0; i < parameters.length; i++) {
1221
1168
  if (parameters[i].value !== null) {
1222
1169
  parameters[i].encode(msg);
1223
1170
  }
1224
1171
  }
1225
- } else {
1226
- for(var i = 0; i < parameters.length; i++) {
1172
+ }
1173
+ else {
1174
+ for (var i = 0; i < parameters.length; i++) {
1227
1175
  parameters[i].encode(msg);
1228
1176
  if (parameters[i].value !== null) {
1229
1177
  msg.addInt(0);
1230
1178
  }
1231
1179
  }
1232
1180
  }
1233
- } else {
1234
- msg.addBlr(blr); // empty
1181
+ }
1182
+ else {
1183
+ msg.addBlr(blr); // empty
1235
1184
  msg.addInt(0); // message number
1236
1185
  msg.addInt(0); // param count
1237
1186
  }
1238
-
1239
- if (op === Const.op_execute2) {
1240
- var outputBlr = new BlrWriter(32);
1241
-
1187
+ if (op === const_1.default.op_execute2) {
1188
+ var outputBlr = new serialize_1.BlrWriter(32);
1242
1189
  if (statement.output && statement.output.length) {
1243
1190
  CalcBlr(outputBlr, statement.output);
1244
1191
  msg.addBlr(outputBlr);
1245
- } else {
1192
+ }
1193
+ else {
1246
1194
  msg.addBlr(outputBlr); // empty
1247
1195
  }
1248
1196
  msg.addInt(0); // out_message_number = out_message_type
1249
1197
  }
1250
-
1251
- if (this.accept.protocolVersion >= Const.PROTOCOL_VERSION16) {
1198
+ if (this.accept.protocolVersion >= const_1.default.PROTOCOL_VERSION16) {
1252
1199
  // TODO impl statement timout
1253
1200
  msg.addInt(statement.options?.timeout || 0); // p_sqldata_timeout
1254
1201
  }
1255
-
1202
+ if (this.accept.protocolVersion >= const_1.default.PROTOCOL_VERSION18) {
1203
+ msg.addInt(statement.options?.scrollable ? 1 : 0); // p_sqldata_cursor_flags (1 = CURSOR_TYPE_SCROLLABLE)
1204
+ }
1205
+ if (this.accept.protocolVersion >= const_1.default.PROTOCOL_VERSION19) {
1206
+ msg.addInt(statement.options?.maxInlineBlobSize !== undefined ? statement.options.maxInlineBlobSize : (this.options?.maxInlineBlobSize || 0)); // p_sqldata_inline_blob_size
1207
+ }
1256
1208
  callback.statement = statement;
1257
1209
  this._queueEvent(callback);
1258
1210
  }
1259
-
1260
-
1261
-
1262
-
1263
1211
  fetch(statement, transaction, count, callback) {
1264
-
1265
1212
  var msg = this._msg;
1266
1213
  var blr = this._blr;
1267
-
1268
1214
  msg.pos = 0;
1269
1215
  blr.pos = 0;
1270
-
1271
1216
  if (count instanceof Function) {
1272
1217
  callback = count;
1273
- count = Const.DEFAULT_FETCHSIZE;
1218
+ count = const_1.default.DEFAULT_FETCHSIZE;
1274
1219
  }
1275
-
1276
- msg.addInt(Const.op_fetch);
1220
+ msg.addInt(const_1.default.op_fetch);
1277
1221
  msg.addInt(statement.handle);
1278
1222
  CalcBlr(blr, statement.output);
1279
1223
  msg.addBlr(blr);
1280
1224
  msg.addInt(0); // message number
1281
- msg.addInt(count || Const.DEFAULT_FETCHSIZE); // fetch count
1282
-
1225
+ msg.addInt(count || const_1.default.DEFAULT_FETCHSIZE); // fetch count
1226
+ callback.statement = statement;
1227
+ this._queueEvent(callback);
1228
+ }
1229
+ fetchScroll(statement, transaction, direction, offset, count, callback) {
1230
+ if (typeof count === 'function') {
1231
+ callback = count;
1232
+ count = undefined;
1233
+ }
1234
+ if (typeof offset === 'function') {
1235
+ callback = offset;
1236
+ offset = undefined;
1237
+ count = undefined;
1238
+ }
1239
+ var msg = this._msg;
1240
+ var blr = this._blr;
1241
+ msg.pos = 0;
1242
+ blr.pos = 0;
1243
+ let dirInt = 0;
1244
+ if (typeof direction === 'number') {
1245
+ dirInt = direction;
1246
+ }
1247
+ else if (typeof direction === 'string') {
1248
+ const dirUpper = direction.toUpperCase();
1249
+ switch (dirUpper) {
1250
+ case 'NEXT':
1251
+ dirInt = 0;
1252
+ break;
1253
+ case 'PRIOR':
1254
+ dirInt = 1;
1255
+ break;
1256
+ case 'FIRST':
1257
+ dirInt = 2;
1258
+ break;
1259
+ case 'LAST':
1260
+ dirInt = 3;
1261
+ break;
1262
+ case 'ABSOLUTE':
1263
+ dirInt = 4;
1264
+ break;
1265
+ case 'RELATIVE':
1266
+ dirInt = 5;
1267
+ break;
1268
+ default:
1269
+ throw new Error('Invalid fetch direction: ' + direction);
1270
+ }
1271
+ }
1272
+ const offsetVal = offset || 0;
1273
+ const fetchCount = count || 1;
1274
+ msg.addInt(const_1.default.op_fetch_scroll);
1275
+ msg.addInt(statement.handle);
1276
+ CalcBlr(blr, statement.output);
1277
+ msg.addBlr(blr);
1278
+ msg.addInt(0); // message number
1279
+ msg.addInt(fetchCount); // fetch count
1280
+ msg.addInt(dirInt); // fetch operation
1281
+ msg.addInt(offsetVal); // fetch position (offset)
1283
1282
  callback.statement = statement;
1284
1283
  this._queueEvent(callback);
1285
1284
  }
1286
-
1287
-
1288
1285
  fetchAll(statement, transaction, callback) {
1289
1286
  const self = this;
1290
1287
  const custom = statement.options || {};
@@ -1296,7 +1293,6 @@ class Connection {
1296
1293
  callback(err);
1297
1294
  return;
1298
1295
  }
1299
-
1300
1296
  if (ret && ret.data && ret.data.length) {
1301
1297
  // Read blobs sequentially instead of in parallel to avoid
1302
1298
  // exceeding Firebird's per-connection open-blob-handle limit,
@@ -1312,18 +1308,17 @@ class Connection {
1312
1308
  return readBlobsSequentially(index + 1, results);
1313
1309
  });
1314
1310
  };
1315
-
1316
1311
  readBlobsSequentially(0, []).then((arrBlob) => {
1317
1312
  for (let i = 0; i < arrBlob.length; i++) {
1318
1313
  const blob = arrBlob[i];
1319
- ret.data[blob.row][blob.column] = blob.value;
1314
+ ret.data[blob.row][blob.column] = parseValueIfJson(blob.value, statement.connection.options);
1320
1315
  }
1321
-
1322
1316
  doSynchronousLoop(ret.data, (row, _i, next) => {
1323
1317
  const pos = asStream ? streamIndex++ : (data.push(row) - 1);
1324
1318
  if (asStream) {
1325
1319
  executeStreamRow(custom, row, pos, statement.output, next);
1326
- } else {
1320
+ }
1321
+ else {
1327
1322
  next();
1328
1323
  }
1329
1324
  }, (streamErr) => {
@@ -1331,247 +1326,209 @@ class Connection {
1331
1326
  callback(streamErr);
1332
1327
  return;
1333
1328
  }
1334
-
1335
1329
  if (ret.fetched) {
1336
1330
  callback(undefined, data || []);
1337
- } else {
1338
- self.fetch(statement, transaction, Const.DEFAULT_FETCHSIZE, loop);
1331
+ }
1332
+ else {
1333
+ self.fetch(statement, transaction, const_1.default.DEFAULT_FETCHSIZE, loop);
1339
1334
  }
1340
1335
  });
1341
1336
  }).catch(callback);
1342
1337
  return;
1343
1338
  }
1344
-
1345
1339
  if (ret && ret.fetched) {
1346
1340
  callback(undefined, data || []);
1347
- } else {
1348
- self.fetch(statement, transaction, Const.DEFAULT_FETCHSIZE, loop);
1341
+ }
1342
+ else {
1343
+ self.fetch(statement, transaction, const_1.default.DEFAULT_FETCHSIZE, loop);
1349
1344
  }
1350
1345
  };
1351
-
1352
- this.fetch(statement, transaction, Const.DEFAULT_FETCHSIZE, loop);
1346
+ this.fetch(statement, transaction, const_1.default.DEFAULT_FETCHSIZE, loop);
1353
1347
  }
1354
-
1355
-
1356
-
1357
1348
  openBlob(blob, transaction, callback) {
1358
1349
  var msg = this._msg;
1359
1350
  msg.pos = 0;
1360
- msg.addInt(Const.op_open_blob);
1351
+ msg.addInt(const_1.default.op_open_blob);
1361
1352
  msg.addInt(transaction.handle);
1362
1353
  msg.addQuad(blob);
1363
1354
  this._queueEvent(callback);
1364
1355
  }
1365
-
1366
-
1367
1356
  closeBlob(blob, callback, defer = true) {
1368
1357
  var msg = this._msg;
1369
1358
  msg.pos = 0;
1370
- msg.addInt(Const.op_close_blob);
1359
+ msg.addInt(const_1.default.op_close_blob);
1371
1360
  msg.addInt(blob.handle);
1372
1361
  this._queueEvent(callback, defer);
1373
1362
  }
1374
-
1375
-
1376
1363
  getSegment(blob, callback) {
1377
1364
  var msg = this._msg;
1378
1365
  msg.pos = 0;
1379
- msg.addInt(Const.op_get_segment);
1366
+ msg.addInt(const_1.default.op_get_segment);
1380
1367
  msg.addInt(blob.handle);
1381
1368
  msg.addInt(this.options.blobReadChunkSize || 1024); // buffer length (max 65535)
1382
1369
  msg.addInt(0); // ???
1383
1370
  this._queueEvent(callback);
1384
1371
  }
1385
-
1386
-
1387
1372
  createBlob2(transaction, callback) {
1388
1373
  var msg = this._msg;
1389
1374
  msg.pos = 0;
1390
- msg.addInt(Const.op_create_blob2);
1375
+ msg.addInt(const_1.default.op_create_blob2);
1391
1376
  msg.addInt(0);
1392
1377
  msg.addInt(transaction.handle);
1393
1378
  msg.addInt(0);
1394
1379
  msg.addInt(0);
1395
1380
  this._queueEvent(callback);
1396
1381
  }
1397
-
1398
-
1399
1382
  batchSegments(blob, buffer, callback) {
1400
1383
  var msg = this._msg;
1401
1384
  var blr = this._blr;
1402
1385
  msg.pos = 0;
1403
1386
  blr.pos = 0;
1404
- msg.addInt(Const.op_batch_segments);
1387
+ msg.addInt(const_1.default.op_batch_segments);
1405
1388
  msg.addInt(blob.handle);
1406
1389
  msg.addInt(buffer.length + 2);
1407
1390
  blr.addBuffer(buffer);
1408
1391
  msg.addBlr(blr);
1409
1392
  this._queueEvent(callback);
1410
1393
  }
1411
-
1412
-
1413
1394
  svcattach(options, callback, svc) {
1414
- this._lowercase_keys = options.lowercase_keys || Const.DEFAULT_LOWERCASE_KEYS;
1395
+ this._lowercase_keys = options.lowercase_keys || const_1.default.DEFAULT_LOWERCASE_KEYS;
1415
1396
  var database = options.database || options.filename;
1416
- var user = options.user || Const.DEFAULT_USER;
1417
- var password = options.password || Const.DEFAULT_PASSWORD;
1397
+ var user = options.user || const_1.default.DEFAULT_USER;
1398
+ var password = options.password || const_1.default.DEFAULT_PASSWORD;
1418
1399
  var role = options.role;
1419
1400
  var msg = this._msg;
1420
1401
  var blr = this._blr;
1421
1402
  msg.pos = 0;
1422
1403
  blr.pos = 0;
1423
-
1424
- blr.addBytes([Const.isc_dpb_version2, Const.isc_dpb_version2]);
1425
- blr.addString(Const.isc_dpb_lc_ctype, 'UTF8', Const.DEFAULT_ENCODING);
1426
-
1404
+ blr.addBytes([const_1.default.isc_dpb_version2, const_1.default.isc_dpb_version2]);
1405
+ blr.addString(const_1.default.isc_dpb_lc_ctype, 'UTF8', const_1.default.DEFAULT_ENCODING);
1427
1406
  // For Firebird 3+ (protocol 13+), add UTF-8 filename flag to ensure all DPB strings are handled with UTF-8
1428
- if (this.accept.protocolVersion >= Const.PROTOCOL_VERSION13) {
1429
- blr.addByte(Const.isc_dpb_utf8_filename);
1407
+ if (this.accept.protocolVersion >= const_1.default.PROTOCOL_VERSION13) {
1408
+ blr.addByte(const_1.default.isc_dpb_utf8_filename);
1430
1409
  blr.addByte(0);
1431
1410
  }
1432
-
1433
- blr.addString(Const.isc_dpb_user_name, user, Const.DEFAULT_ENCODING);
1434
- blr.addString(Const.isc_dpb_password, password, Const.DEFAULT_ENCODING);
1435
- blr.addByte(Const.isc_dpb_dummy_packet_interval);
1411
+ blr.addString(const_1.default.isc_dpb_user_name, user, const_1.default.DEFAULT_ENCODING);
1412
+ blr.addString(const_1.default.isc_dpb_password, password, const_1.default.DEFAULT_ENCODING);
1413
+ blr.addByte(const_1.default.isc_dpb_dummy_packet_interval);
1436
1414
  blr.addByte(4);
1437
1415
  blr.addBytes([120, 10, 0, 0]); // FROM DOT NET PROVIDER
1438
1416
  if (role)
1439
- blr.addString(Const.isc_dpb_sql_role_name, role, Const.DEFAULT_ENCODING);
1440
-
1441
- msg.addInt(Const.op_service_attach);
1417
+ blr.addString(const_1.default.isc_dpb_sql_role_name, role, const_1.default.DEFAULT_ENCODING);
1418
+ msg.addInt(const_1.default.op_service_attach);
1442
1419
  msg.addInt(0);
1443
- msg.addString(Const.DEFAULT_SVC_NAME, Const.DEFAULT_ENCODING); // only local for moment
1420
+ msg.addString(const_1.default.DEFAULT_SVC_NAME, const_1.default.DEFAULT_ENCODING); // only local for moment
1444
1421
  msg.addBlr(this._blr);
1445
-
1446
1422
  var self = this;
1447
-
1448
1423
  function cb(err, ret) {
1449
-
1450
1424
  if (err) {
1451
- doError(err, callback);
1425
+ (0, callback_1.doError)(err, callback);
1452
1426
  return;
1453
1427
  }
1454
-
1455
1428
  self.svchandle = ret.handle;
1456
1429
  if (callback)
1457
1430
  callback(undefined, ret);
1458
1431
  }
1459
-
1460
1432
  // For reconnect
1461
1433
  if (svc) {
1462
1434
  svc.connection = this;
1463
1435
  cb.response = svc;
1464
- } else {
1465
- cb.response = new ServiceManager(this);
1436
+ }
1437
+ else {
1438
+ cb.response = new service_1.default(this);
1466
1439
  cb.response.removeAllListeners('error');
1467
- cb.response.on('error', noop);
1440
+ cb.response.on('error', utils_1.noop);
1468
1441
  }
1469
-
1470
1442
  this._queueEvent(cb);
1471
1443
  }
1472
-
1473
-
1474
1444
  svcstart(spbaction, callback) {
1475
1445
  var msg = this._msg;
1476
1446
  var blr = this._blr;
1477
1447
  msg.pos = 0;
1478
- msg.addInt(Const.op_service_start);
1448
+ msg.addInt(const_1.default.op_service_start);
1479
1449
  msg.addInt(this.svchandle);
1480
- msg.addInt(0)
1450
+ msg.addInt(0);
1481
1451
  msg.addBlr(spbaction);
1482
1452
  this._queueEvent(callback);
1483
1453
  }
1484
-
1485
-
1486
- svcquery(spbquery, resultbuffersize, timeout,callback) {
1487
- if (resultbuffersize > Const.MAX_BUFFER_SIZE) {
1488
- doError(new Error('Buffer is too big'), callback);
1454
+ svcquery(spbquery, resultbuffersize, timeout, callback) {
1455
+ if (resultbuffersize > const_1.default.MAX_BUFFER_SIZE) {
1456
+ (0, callback_1.doError)(new Error('Buffer is too big'), callback);
1489
1457
  return;
1490
1458
  }
1491
-
1492
1459
  var msg = this._msg;
1493
1460
  var blr = this._blr;
1494
1461
  msg.pos = 0;
1495
1462
  blr.pos = 0;
1496
- blr.addByte(Const.isc_spb_current_version);
1463
+ blr.addByte(const_1.default.isc_spb_current_version);
1497
1464
  //blr.addByteInt32(Const.isc_info_svc_timeout, timeout);
1498
- msg.addInt(Const.op_service_info);
1465
+ msg.addInt(const_1.default.op_service_info);
1499
1466
  msg.addInt(this.svchandle);
1500
1467
  msg.addInt(0);
1501
1468
  msg.addBlr(blr);
1502
- blr.pos = 0
1469
+ blr.pos = 0;
1503
1470
  blr.addBytes(spbquery);
1504
1471
  msg.addBlr(blr);
1505
1472
  msg.addInt(resultbuffersize);
1506
1473
  this._queueEvent(callback);
1507
1474
  }
1508
-
1509
-
1510
1475
  svcdetach(callback) {
1511
1476
  var self = this;
1512
-
1513
- if (self._isClosed)
1477
+ if (self._isClosed) {
1478
+ (0, callback_1.doError)(new Error('Connection is closed.'), callback);
1514
1479
  return;
1515
-
1480
+ }
1516
1481
  self._isUsed = false;
1517
1482
  self._isDetach = true;
1518
-
1519
1483
  var msg = self._msg;
1520
-
1521
1484
  msg.pos = 0;
1522
- msg.addInt(Const.op_service_detach);
1485
+ msg.addInt(const_1.default.op_service_detach);
1523
1486
  msg.addInt(this.svchandle); // Database Object ID
1524
-
1525
1487
  self._queueEvent(function (err, ret) {
1526
1488
  delete (self.svchandle);
1527
1489
  if (callback)
1528
1490
  callback(err, ret);
1529
1491
  });
1530
1492
  }
1531
-
1532
-
1533
-
1534
- auxConnection(callback) {
1493
+ auxConnection(eventid, callback) {
1494
+ if (typeof eventid === 'function') {
1495
+ // Preserve the older auxConnection(callback) call shape; plain
1496
+ // auxiliary connections historically used event id 0.
1497
+ callback = eventid;
1498
+ eventid = 0;
1499
+ }
1535
1500
  var self = this;
1536
1501
  if (self._isClosed)
1537
1502
  return this.throwClosed(callback);
1538
1503
  var msg = self._msg;
1539
1504
  msg.pos = 0;
1540
- msg.addInt(Const.op_connect_request);
1505
+ msg.addInt(const_1.default.op_connect_request);
1541
1506
  msg.addInt(1); // async
1542
1507
  msg.addInt(self.dbhandle);
1543
- msg.addInt(0);
1508
+ msg.addInt(eventid);
1544
1509
  if (process.env.FIREBIRD_DEBUG) {
1545
- console.log('[fb-debug] auxConnection: sending op_connect_request(53) dbhandle=%d queue_before=%d xdr_saved=%s',
1546
- self.dbhandle, self._queue.length, Boolean(self._xdr));
1510
+ console.log('[fb-debug] auxConnection: sending op_connect_request(53) dbhandle=%d eventid=%d queue_before=%d xdr_saved=%s', self.dbhandle, eventid, self._queue.length, Boolean(self._xdr));
1547
1511
  }
1548
1512
  function cb(err, ret) {
1549
-
1550
1513
  if (err) {
1551
1514
  if (process.env.FIREBIRD_DEBUG) {
1552
1515
  console.log('[fb-debug] auxConnection: op_connect_request error: %s queue=%d', err.message, self._queue.length);
1553
1516
  }
1554
- doError(err, callback);
1517
+ (0, callback_1.doError)(err, callback);
1555
1518
  return;
1556
1519
  }
1557
-
1558
1520
  var socket_info = {
1559
1521
  family: ret.buffer.readInt16BE(0),
1560
1522
  port: ret.buffer.readUInt16BE(2),
1561
1523
  host: ret.buffer.readUInt8(4) + '.' + ret.buffer.readUInt8(5) + '.' + ret.buffer.readUInt8(6) + '.' + ret.buffer.readUInt8(7)
1562
- }
1563
-
1524
+ };
1564
1525
  if (process.env.FIREBIRD_DEBUG) {
1565
- console.log('[fb-debug] auxConnection: op_response ok → aux family=%d port=%d host=%s queue=%d',
1566
- socket_info.family, socket_info.port, socket_info.host, self._queue.length);
1526
+ console.log('[fb-debug] auxConnection: op_response ok → aux family=%d port=%d host=%s queue=%d', socket_info.family, socket_info.port, socket_info.host, self._queue.length);
1567
1527
  }
1568
-
1569
1528
  callback(undefined, socket_info);
1570
1529
  }
1571
1530
  this._queueEvent(cb);
1572
1531
  }
1573
-
1574
-
1575
1532
  queEvents(events, eventid, callback) {
1576
1533
  var self = this;
1577
1534
  if (this._isClosed)
@@ -1580,189 +1537,184 @@ class Connection {
1580
1537
  var blr = this._blr;
1581
1538
  blr.pos = 0;
1582
1539
  msg.pos = 0;
1583
- msg.addInt(Const.op_que_events);
1540
+ msg.addInt(const_1.default.op_que_events);
1584
1541
  msg.addInt(this.dbhandle);
1585
1542
  // prepare EPB
1586
- blr.addByte(1) // epb_version
1543
+ blr.addByte(1); // epb_version
1587
1544
  for (var event in events) {
1588
- var event_buffer = Buffer.from(event, 'UTF8');
1545
+ var event_buffer = Buffer.from(event, 'utf8');
1589
1546
  blr.addByte(event_buffer.length);
1590
1547
  blr.addBytes(event_buffer);
1591
1548
  blr.addInt32(events[event]);
1592
1549
  }
1593
- msg.addBlr(blr); // epb
1594
- msg.addInt(0); // ast
1595
- msg.addInt(0); // args
1550
+ msg.addBlr(blr); // epb
1551
+ msg.addInt(0); // ast
1552
+ msg.addInt(0); // args
1596
1553
  msg.addInt(eventid);
1597
-
1598
1554
  function cb(err, ret) {
1599
1555
  if (err) {
1600
- doError(err, callback);
1556
+ (0, callback_1.doError)(err, callback);
1601
1557
  return;
1602
1558
  }
1603
-
1604
1559
  callback(null, ret);
1605
1560
  }
1606
-
1607
1561
  this._queueEvent(cb);
1608
1562
  }
1609
-
1610
-
1611
1563
  closeEvents(eventid, callback) {
1612
1564
  var self = this;
1613
1565
  if (this._isClosed)
1614
1566
  return this.throwClosed(callback);
1615
1567
  var msg = self._msg;
1616
1568
  msg.pos = 0;
1617
- msg.addInt(Const.op_cancel_events);
1569
+ msg.addInt(const_1.default.op_cancel_events);
1618
1570
  msg.addInt(self.dbhandle);
1619
1571
  msg.addInt(eventid);
1620
-
1621
1572
  function cb(err, ret) {
1622
1573
  if (err) {
1623
- doError(err, callback);
1574
+ (0, callback_1.doError)(err, callback);
1624
1575
  return;
1625
1576
  }
1626
-
1627
1577
  callback(null);
1628
1578
  }
1629
-
1630
1579
  this._queueEvent(cb);
1631
1580
  }
1632
-
1633
1581
  }
1634
-
1635
1582
  // Reverse-lookup table: opcode number → name for FIREBIRD_DEBUG trace logging.
1636
- const opcodeNames = Object.fromEntries(
1637
- Object.entries(Const).filter(([k]) => k.startsWith('op_')).map(([k, v]) => [v, k])
1638
- );
1639
-
1583
+ const opcodeNames = Object.fromEntries(Object.entries(const_1.default).filter(([k]) => k.startsWith('op_')).map(([k, v]) => [v, k]));
1640
1584
  function decodeResponse(data, callback, cnx, lowercase_keys, cb) {
1641
1585
  try {
1642
1586
  do {
1643
1587
  var r = data.r || data.readInt();
1644
- } while (r === Const.op_dummy);
1645
-
1588
+ if (data.r) {
1589
+ data.r = null;
1590
+ }
1591
+ if (r === const_1.default.op_dummy) {
1592
+ continue;
1593
+ }
1594
+ if (r === const_1.default.op_inline_blob) {
1595
+ if (process.env.FIREBIRD_DEBUG) {
1596
+ console.log('[fb-debug] decodeResponse: opcode=op_inline_blob');
1597
+ }
1598
+ var tran_id = data.readInt();
1599
+ var blob_id = data.readQuad();
1600
+ var blob_data = data.readArray();
1601
+ if (!cnx._inlineBlobs) {
1602
+ cnx._inlineBlobs = new Map();
1603
+ }
1604
+ const cacheKey = `${blob_id.high}:${blob_id.low}`;
1605
+ cnx._inlineBlobs.set(cacheKey, blob_data);
1606
+ r = const_1.default.op_dummy; // Continue loop to read next opcode
1607
+ }
1608
+ } while (r === const_1.default.op_dummy);
1646
1609
  if (process.env.FIREBIRD_DEBUG) {
1647
- console.log('[fb-debug] decodeResponse: opcode=%d(%s) pos=%d buflen=%d',
1648
- r, opcodeNames[r] || 'unknown', data.pos, data.buffer.length);
1610
+ console.log('[fb-debug] decodeResponse: opcode=%d(%s) pos=%d buflen=%d', r, opcodeNames[r] || 'unknown', data.pos, data.buffer.length);
1649
1611
  }
1650
-
1651
1612
  var item, op, response;
1652
-
1653
1613
  switch (r) {
1654
- case Const.op_response:
1655
-
1614
+ case const_1.default.op_response:
1656
1615
  if (callback) {
1657
1616
  response = callback.response || {};
1658
- } else {
1617
+ }
1618
+ else {
1659
1619
  response = {};
1660
1620
  }
1661
-
1662
1621
  let loop = function (err) {
1663
1622
  if (err) {
1664
1623
  return cb(err);
1665
- } else {
1624
+ }
1625
+ else {
1666
1626
  if (callback && callback.lazy_count) {
1667
1627
  callback.lazy_count--;
1668
1628
  if (callback.lazy_count > 0) {
1669
1629
  r = data.readInt(); // Read new op
1670
1630
  parseOpResponse(data, response, loop);
1671
- } else {
1631
+ }
1632
+ else {
1672
1633
  cb(null, response);
1673
1634
  }
1674
- } else {
1635
+ }
1636
+ else {
1675
1637
  cb(null, response);
1676
1638
  }
1677
1639
  }
1678
1640
  };
1679
1641
  // Parse normal and lazy response
1680
1642
  return parseOpResponse(data, response, loop);
1681
- case Const.op_fetch_response:
1682
- case Const.op_sql_response:
1643
+ case const_1.default.op_fetch_response:
1644
+ case const_1.default.op_sql_response:
1683
1645
  var statement = callback.statement;
1684
1646
  var output = statement.output;
1685
1647
  var custom = statement.options || {};
1686
- var isOpFetch = r === Const.op_fetch_response;
1648
+ var isOpFetch = r === const_1.default.op_fetch_response;
1687
1649
  var _xdrpos;
1688
1650
  statement.nbrowsfetched = statement.nbrowsfetched || 0;
1689
-
1690
1651
  if (isOpFetch && data.fop) { // could be set when a packet is not complete
1691
1652
  data.readBuffer(68); // ??
1692
1653
  op = data.readInt(); // ??
1693
1654
  data.fop = false;
1694
- if (op === Const.op_response) {
1655
+ if (op === const_1.default.op_response) {
1695
1656
  return parseOpResponse(data, {}, cb);
1696
1657
  }
1697
1658
  }
1698
-
1699
1659
  if (!isOpFetch) {
1700
1660
  data.fstatus = 0;
1701
1661
  }
1702
-
1703
1662
  data.fstatus = data.fstatus !== undefined ? data.fstatus : data.readInt();
1704
1663
  data.fcount = data.fcount !== undefined ? data.fcount : data.readInt();
1705
1664
  data.fcolumn = data.fcolumn || 0;
1706
1665
  data.frow = data.frow || (custom.asObject ? {} : new Array(output.length));
1707
1666
  data.frows = data.frows || [];
1708
-
1709
1667
  if (custom.asObject && !data.fcols) {
1710
1668
  if (lowercase_keys) {
1711
1669
  data.fcols = output.map((column) => column.alias.toLowerCase());
1712
- } else {
1670
+ }
1671
+ else {
1713
1672
  data.fcols = output.map((column) => column.alias);
1714
1673
  }
1715
1674
  }
1716
-
1717
1675
  const arrBlob = [];
1718
- const lowerV13 = statement.connection.accept.protocolVersion < Const.PROTOCOL_VERSION13;
1719
-
1676
+ const lowerV13 = statement.connection.accept.protocolVersion < const_1.default.PROTOCOL_VERSION13;
1720
1677
  while (data.fcount && (data.fstatus !== 100)) {
1721
1678
  let nullBitSet;
1722
1679
  if (!lowerV13) {
1723
1680
  const nullBitsLen = Math.floor((output.length + 7) / 8);
1724
- nullBitSet = new BitSet(data.readBuffer(nullBitsLen, false));
1681
+ nullBitSet = new serialize_1.BitSet(data.readBuffer(nullBitsLen, false));
1725
1682
  data.readBuffer((4 - nullBitsLen) & 3, false); // Skip padding
1726
1683
  }
1727
-
1728
1684
  for (let length = output.length; data.fcolumn < length; data.fcolumn++) {
1729
1685
  item = output[data.fcolumn];
1730
-
1731
1686
  if (!lowerV13 && nullBitSet.get(data.fcolumn)) {
1732
1687
  if (custom.asObject) {
1733
1688
  data.frow[data.fcols[data.fcolumn]] = null;
1734
- } else {
1689
+ }
1690
+ else {
1735
1691
  data.frow[data.fcolumn] = null;
1736
1692
  }
1737
-
1738
1693
  continue;
1739
1694
  }
1740
-
1741
1695
  try {
1742
1696
  _xdrpos = data.pos;
1743
1697
  const key = custom.asObject ? data.fcols[data.fcolumn] : data.fcolumn;
1744
1698
  const row = data.frows.length;
1745
1699
  let value = item.decode(data, lowerV13, cnx.options);
1746
-
1747
- if (item.type === Const.SQL_BLOB && value !== null) {
1748
- if (item.subType === Const.isc_blob_text && cnx.options.blobAsText) {
1700
+ if (item.type === const_1.default.SQL_BLOB && value !== null) {
1701
+ if (item.subType === const_1.default.isc_blob_text && cnx.options.blobAsText) {
1749
1702
  value = fetch_blob_async_transaction(statement, value, key, row);
1750
1703
  arrBlob.push(value);
1751
- } else {
1704
+ }
1705
+ else {
1752
1706
  value = fetch_blob_async(statement, value, key, row);
1753
1707
  }
1754
1708
  }
1755
-
1756
- data.frow[key] = value;
1757
- } catch (e) {
1709
+ data.frow[key] = parseValueIfJson(value, cnx.options);
1710
+ }
1711
+ catch (e) {
1758
1712
  // uncomplete packet read
1759
1713
  data.pos = _xdrpos;
1760
1714
  data.r = r;
1761
1715
  return cb(new Error('Packet is not complete'));
1762
1716
  }
1763
-
1764
1717
  }
1765
-
1766
1718
  data.fcolumn = 0;
1767
1719
  // ToDo: emit "row" with blob subtype string decoded
1768
1720
  // use: data.frow['fieldBlob'](transaction?).then(({ value }) => console.log(value))
@@ -1770,28 +1722,29 @@ function decodeResponse(data, callback, cnx, lowercase_keys, cb) {
1770
1722
  statement.connection.db.emit('row', data.frow, statement.nbrowsfetched, custom.asObject);
1771
1723
  data.frows.push(data.frow);
1772
1724
  data.frow = custom.asObject ? {} : new Array(output.length);
1773
-
1774
1725
  try {
1775
1726
  _xdrpos = data.pos;
1776
1727
  if (isOpFetch) {
1777
1728
  delete data.fstatus;
1778
1729
  delete data.fcount;
1779
1730
  op = data.readInt(); // ??
1780
- if (op === Const.op_response) {
1731
+ if (op === const_1.default.op_response) {
1781
1732
  return parseOpResponse(data, {}, cb);
1782
1733
  }
1783
1734
  data.fstatus = data.readInt();
1784
1735
  data.fcount = data.readInt();
1785
- } else {
1736
+ }
1737
+ else {
1786
1738
  data.fcount--;
1787
- if (r === Const.op_sql_response) {
1739
+ if (r === const_1.default.op_sql_response) {
1788
1740
  op = data.readInt();
1789
- if (op === Const.op_response) {
1741
+ if (op === const_1.default.op_response) {
1790
1742
  parseOpResponse(data, {});
1791
1743
  }
1792
1744
  }
1793
1745
  }
1794
- } catch (e) {
1746
+ }
1747
+ catch (e) {
1795
1748
  if (_xdrpos === data.pos) {
1796
1749
  data.fop = true;
1797
1750
  }
@@ -1800,13 +1753,12 @@ function decodeResponse(data, callback, cnx, lowercase_keys, cb) {
1800
1753
  }
1801
1754
  statement.nbrowsfetched++;
1802
1755
  }
1803
-
1804
1756
  // ToDo: emit "result" with blob subtype string decoded
1805
1757
  statement.connection.db.emit('result', data.frows, arrBlob);
1806
- return cb(null, {data: data.frows, fetched: Boolean(!isOpFetch || data.fstatus === 100), arrBlob});
1807
- case Const.op_accept:
1808
- case Const.op_cond_accept:
1809
- case Const.op_accept_data:
1758
+ return cb(null, { data: data.frows, fetched: Boolean(!isOpFetch || data.fstatus === 100), arrBlob });
1759
+ case const_1.default.op_accept:
1760
+ case const_1.default.op_cond_accept:
1761
+ case const_1.default.op_accept_data:
1810
1762
  let accept = {
1811
1763
  protocolVersion: data.readInt(),
1812
1764
  protocolArchitecture: data.readInt(),
@@ -1816,214 +1768,224 @@ function decodeResponse(data, callback, cnx, lowercase_keys, cb) {
1816
1768
  authData: '',
1817
1769
  sessionKey: ''
1818
1770
  };
1819
-
1820
- accept.compress = (accept.protocolMinimumType & Const.pflag_compress) !== 0;
1821
- accept.protocolMinimumType = accept.protocolMinimumType & Const.ptype_mask;
1771
+ accept.compress = (accept.protocolMinimumType & const_1.default.pflag_compress) !== 0;
1772
+ accept.protocolMinimumType = accept.protocolMinimumType & const_1.default.ptype_mask;
1822
1773
  //accept.compress = (accept.acceptType & pflag_compress) !== 0; // TODO Handle zlib compression
1823
1774
  if (accept.protocolVersion < 0) {
1824
- accept.protocolVersion = (accept.protocolVersion & Const.FB_PROTOCOL_MASK) | Const.FB_PROTOCOL_FLAG;
1775
+ accept.protocolVersion = (accept.protocolVersion & const_1.default.FB_PROTOCOL_MASK) | const_1.default.FB_PROTOCOL_FLAG;
1825
1776
  }
1826
-
1827
- if (r === Const.op_cond_accept || r === Const.op_accept_data) {
1828
- var d = new BlrReader(data.readArray());
1829
- accept.pluginName = data.readString(Const.DEFAULT_ENCODING);
1777
+ if (r === const_1.default.op_cond_accept || r === const_1.default.op_accept_data) {
1778
+ var d = new serialize_1.BlrReader(data.readArray());
1779
+ accept.pluginName = data.readString(const_1.default.DEFAULT_ENCODING);
1830
1780
  var is_authenticated = data.readInt();
1831
- var keys = data.readString(Const.DEFAULT_ENCODING); // keys
1832
-
1781
+ var keys = data.readString(const_1.default.DEFAULT_ENCODING); // keys
1782
+ accept.keys = keys;
1833
1783
  if (is_authenticated === 0) {
1834
1784
  if (cnx.options.pluginName && cnx.options.pluginName !== accept.pluginName) {
1835
1785
  var errPlugin = new Error('Server don\'t accept plugin : ' + cnx.options.pluginName + ', but support : ' + accept.pluginName);
1836
- doError(errPlugin, callback);
1786
+ (0, callback_1.doError)(errPlugin, callback);
1837
1787
  return cb(errPlugin);
1838
1788
  }
1839
-
1840
- if (Const.AUTH_PLUGIN_SRP_LIST.indexOf(accept.pluginName) !== -1 && !cnx.clientKeys) {
1789
+ if (const_1.default.AUTH_PLUGIN_SRP_LIST.indexOf(accept.pluginName) !== -1 && !cnx.clientKeys) {
1841
1790
  var errPlugin = new Error('Server accepted plugin : ' + accept.pluginName + ', but client did not initialize SRP keys');
1842
- doError(errPlugin, callback);
1791
+ (0, callback_1.doError)(errPlugin, callback);
1843
1792
  return cb(errPlugin);
1844
1793
  }
1845
-
1846
- if (Const.AUTH_PLUGIN_SRP_LIST.indexOf(accept.pluginName) !== -1) {
1794
+ if (const_1.default.AUTH_PLUGIN_SRP_LIST.indexOf(accept.pluginName) !== -1) {
1847
1795
  var crypto = {
1848
1796
  Srp: 'sha1',
1849
- Srp256: 'sha256'
1797
+ Srp256: 'sha256',
1798
+ Srp384: 'sha384',
1799
+ Srp512: 'sha512'
1850
1800
  };
1851
1801
  accept.srpAlgo = crypto[accept.pluginName];
1852
-
1853
- // TODO : Fallback Srp256 to Srp ?
1854
- /*if (!d.buffer) {
1855
- cnx.sendOpContAuth(
1856
- cnx.clientKeys.public.toString(16),
1857
- DEFAULT_ENCODING,
1858
- accept.pluginName
1859
- );
1860
-
1861
- return cb(new Error('login'));
1862
- }*/
1863
-
1802
+ if (!d.buffer) {
1803
+ cnx._pendingAccept = accept;
1804
+ cnx.sendOpContAuth(cnx.clientKeys.public.toString(16), const_1.default.DEFAULT_ENCODING, accept.pluginName);
1805
+ return;
1806
+ }
1864
1807
  // Check buffer contains salt
1865
1808
  var saltLen = d.buffer.readUInt16LE(0);
1866
1809
  if (saltLen > 32 * 2) {
1867
1810
  console.log('salt to long'); // TODO : Throw error
1868
1811
  }
1869
-
1870
1812
  // Check buffer contains key
1871
1813
  var keyLen = d.buffer.readUInt16LE(saltLen + 2);
1872
- var keyStart = saltLen + 4;
1873
- if (d.buffer.length - keyStart !== keyLen) {
1874
- console.log('key error'); // TODO : Throw error
1814
+ if (d.buffer.length < saltLen + 4) {
1815
+ var errBuf = new Error('Invalid buffer size for ' + accept.pluginName + ' login');
1816
+ (0, callback_1.doError)(errBuf, callback);
1817
+ return cb(errBuf);
1875
1818
  }
1876
-
1819
+ var keyStart = (saltLen + 2 + 3) & ~3;
1877
1820
  // Server keys
1878
1821
  cnx.serverKeys = {
1879
1822
  salt: d.buffer.slice(2, saltLen + 2).toString('utf8'),
1880
- public: BigInt('0x' + d.buffer.slice(keyStart, d.buffer.length).toString('utf8'))
1823
+ public: BigInt('0x' + d.buffer.slice(keyStart, d.buffer.length).toString('utf8')),
1824
+ pluginName: accept.pluginName
1881
1825
  };
1882
-
1826
+ console.log('--- DEBUG SRP Handshake ---');
1827
+ console.log('salt:', cnx.serverKeys.salt);
1828
+ console.log('server public key:', cnx.serverKeys.public.toString(16));
1829
+ console.log('client public key:', cnx.clientKeys.public.toString(16));
1830
+ console.log('client private key:', cnx.clientKeys.private.toString(16));
1831
+ console.log('hashAlgo:', accept.srpAlgo);
1883
1832
  const _t1 = Date.now();
1884
- var proof = srp.clientProof(
1885
- cnx.options.user.toUpperCase(),
1886
- cnx.options.password,
1887
- cnx.serverKeys.salt,
1888
- cnx.clientKeys.public,
1889
- cnx.serverKeys.public,
1890
- cnx.clientKeys.private,
1891
- accept.srpAlgo
1892
- );
1833
+ var proof = srp.clientProof(cnx.options.user.toUpperCase(), cnx.options.password, cnx.serverKeys.salt, cnx.clientKeys.public, cnx.serverKeys.public, cnx.clientKeys.private, accept.srpAlgo);
1834
+ console.log('client proof M1:', proof.authData.toString(16));
1835
+ console.log('client session key K:', proof.clientSessionKey.toString(16));
1893
1836
  if (process.env.FIREBIRD_DEBUG) {
1894
1837
  console.log('[fb-debug] srp.clientProof(%s): %dms', accept.srpAlgo, Date.now() - _t1);
1895
1838
  }
1896
-
1897
1839
  accept.authData = proof.authData.toString(16);
1898
1840
  accept.sessionKey = proof.clientSessionKey;
1899
- } else if (accept.pluginName === Const.AUTH_PLUGIN_LEGACY) {
1900
- accept.authData = crypt.crypt(cnx.options.password, Const.LEGACY_AUTH_SALT).substring(2);
1901
- } else {
1841
+ }
1842
+ else if (accept.pluginName === const_1.default.AUTH_PLUGIN_LEGACY) {
1843
+ accept.authData = crypt.crypt(cnx.options.password, const_1.default.LEGACY_AUTH_SALT).substring(2);
1844
+ }
1845
+ else {
1902
1846
  return cb(new Error('Unknow auth plugin : ' + accept.pluginName));
1903
1847
  }
1904
- } else {
1848
+ }
1849
+ else {
1905
1850
  accept.authData = '';
1906
1851
  accept.sessionKey = '';
1907
1852
  }
1908
1853
  }
1909
-
1910
1854
  if (accept.compress) {
1911
1855
  cnx._socket.enableCompression();
1912
1856
  }
1913
-
1914
1857
  if (process.env.FIREBIRD_DEBUG) {
1915
- console.log('[fb-debug] auth: %s received plugin=%s proto=%d t=%dms',
1916
- r === Const.op_cond_accept ? 'op_cond_accept' : r === Const.op_accept_data ? 'op_accept_data' : 'op_accept',
1917
- accept.pluginName, accept.protocolVersion,
1918
- cnx._authStartTime ? Date.now() - cnx._authStartTime : -1);
1858
+ console.log('[fb-debug] auth: %s received plugin=%s proto=%d t=%dms', r === const_1.default.op_cond_accept ? 'op_cond_accept' : r === const_1.default.op_accept_data ? 'op_accept_data' : 'op_accept', accept.pluginName, accept.protocolVersion, cnx._authStartTime ? Date.now() - cnx._authStartTime : -1);
1919
1859
  }
1920
-
1921
1860
  // For op_cond_accept: send op_cont_auth and wait for response
1922
- if (r === Const.op_cond_accept && accept.authData) {
1861
+ if (r === const_1.default.op_cond_accept && accept.authData) {
1923
1862
  cnx._pendingAccept = accept;
1924
1863
  if (process.env.FIREBIRD_DEBUG) {
1925
- console.log('[fb-debug] auth: sending op_cont_auth plugin=%s t=%dms',
1926
- accept.pluginName, cnx._authStartTime ? Date.now() - cnx._authStartTime : -1);
1864
+ console.log('[fb-debug] auth: sending op_cont_auth plugin=%s t=%dms', accept.pluginName, cnx._authStartTime ? Date.now() - cnx._authStartTime : -1);
1927
1865
  }
1928
- cnx.sendOpContAuth(
1929
- accept.authData,
1930
- Const.DEFAULT_ENCODING,
1931
- accept.pluginName
1932
- );
1866
+ cnx.sendOpContAuth(accept.authData, const_1.default.DEFAULT_ENCODING, accept.pluginName);
1933
1867
  return; // Don't call cb - queue stays for op_response
1934
1868
  }
1935
-
1936
1869
  return cb(undefined, accept);
1937
- case Const.op_cont_auth:
1938
- var d = new BlrReader(data.readArray());
1939
- var pluginName = data.readString(Const.DEFAULT_ENCODING);
1940
- data.readString(Const.DEFAULT_ENCODING); // plist
1941
- data.readString(Const.DEFAULT_ENCODING); // pkey
1942
-
1870
+ case const_1.default.op_cont_auth:
1871
+ var d = new serialize_1.BlrReader(data.readArray());
1872
+ var pluginName = data.readString(const_1.default.DEFAULT_ENCODING);
1873
+ data.readString(const_1.default.DEFAULT_ENCODING); // plist
1874
+ data.readString(const_1.default.DEFAULT_ENCODING); // pkey
1943
1875
  if (process.env.FIREBIRD_DEBUG) {
1944
- console.log('[fb-debug] auth: op_cont_auth received plugin=%s pendingAccept=%s t=%dms',
1945
- pluginName,
1946
- cnx._pendingAccept ? cnx._pendingAccept.pluginName : 'none',
1947
- cnx._authStartTime ? Date.now() - cnx._authStartTime : -1);
1876
+ console.log('[fb-debug] auth: op_cont_auth received plugin=%s pendingAccept=%s t=%dms', pluginName, cnx._pendingAccept ? cnx._pendingAccept.pluginName : 'none', cnx._authStartTime ? Date.now() - cnx._authStartTime : -1);
1948
1877
  }
1949
-
1950
1878
  // During SRP mutual authentication, the server sends op_cont_auth
1951
1879
  // with its proof (M2) after receiving the client's proof (M1).
1952
- // When we have an active auth exchange for this plugin, just wait
1953
- // for the subsequent op_accept instead of treating it as an error.
1954
- if (cnx._pendingAccept && cnx._pendingAccept.pluginName === pluginName) {
1955
- if (process.env.FIREBIRD_DEBUG) {
1956
- console.log('[fb-debug] auth: server SRP proof (M2) received, waiting for op_accept t=%dms',
1957
- cnx._authStartTime ? Date.now() - cnx._authStartTime : -1);
1880
+ // When we have an active auth exchange for this plugin:
1881
+ if (cnx._pendingAccept && (cnx._pendingAccept.pluginName === pluginName || const_1.default.AUTH_PLUGIN_SRP_LIST.indexOf(pluginName) !== -1)) {
1882
+ if (cnx._pendingAccept.pluginName !== pluginName) {
1883
+ cnx._pendingAccept.pluginName = pluginName;
1884
+ }
1885
+ // The server can switch SRP hash variants mid-handshake: after
1886
+ // the client answers with e.g. Srp256, some accounts make the
1887
+ // server come back with a fresh op_cont_auth naming a different
1888
+ // SRP plugin (e.g. plain Srp/sha1), reusing the same salt/B.
1889
+ // That must be treated as a brand-new challenge - recomputing
1890
+ // the proof with the new plugin's hash algorithm - rather than
1891
+ // as the server's M2 proof, otherwise the client silently waits
1892
+ // forever for an op_accept the server will never send (#254).
1893
+ if (!cnx.serverKeys || cnx.serverKeys.pluginName !== pluginName) {
1894
+ // Check buffer contains salt
1895
+ var saltLen = d.buffer.readUInt16LE(0);
1896
+ if (saltLen > 32 * 2) {
1897
+ console.log('salt to long'); // TODO : Throw error
1898
+ }
1899
+ // Check buffer contains key
1900
+ var keyLen = d.buffer.readUInt16LE(saltLen + 2);
1901
+ if (d.buffer.length < saltLen + 4) {
1902
+ var errBuf = new Error('Invalid buffer size for ' + pluginName + ' login');
1903
+ (0, callback_1.doError)(errBuf, callback);
1904
+ return cb(errBuf);
1905
+ }
1906
+ var keyStart = (saltLen + 2 + 3) & ~3;
1907
+ // Server keys
1908
+ cnx.serverKeys = {
1909
+ salt: d.buffer.slice(2, saltLen + 2).toString('utf8'),
1910
+ public: BigInt('0x' + d.buffer.slice(keyStart, d.buffer.length).toString('utf8')),
1911
+ pluginName: pluginName
1912
+ };
1913
+ var crypto = {
1914
+ Srp: 'sha1',
1915
+ Srp256: 'sha256',
1916
+ Srp384: 'sha384',
1917
+ Srp512: 'sha512'
1918
+ };
1919
+ var srpAlgo = crypto[pluginName];
1920
+ console.log('--- DEBUG SRP Handshake ---');
1921
+ console.log('salt:', cnx.serverKeys.salt);
1922
+ console.log('server public key:', cnx.serverKeys.public.toString(16));
1923
+ console.log('client public key:', cnx.clientKeys.public.toString(16));
1924
+ console.log('client private key:', cnx.clientKeys.private.toString(16));
1925
+ console.log('hashAlgo:', srpAlgo);
1926
+ const _t1 = Date.now();
1927
+ var proof = srp.clientProof(cnx.options.user.toUpperCase(), cnx.options.password, cnx.serverKeys.salt, cnx.clientKeys.public, cnx.serverKeys.public, cnx.clientKeys.private, srpAlgo);
1928
+ if (process.env.FIREBIRD_DEBUG) {
1929
+ console.log('[fb-debug] srp.clientProof(%s): %dms', srpAlgo, Date.now() - _t1);
1930
+ }
1931
+ cnx._pendingAccept.authData = proof.authData.toString(16);
1932
+ cnx._pendingAccept.sessionKey = proof.clientSessionKey;
1933
+ cnx.sendOpContAuth(cnx._pendingAccept.authData, const_1.default.DEFAULT_ENCODING, pluginName);
1934
+ return; // wait for server SRP proof (M2) and/or op_accept
1935
+ }
1936
+ else {
1937
+ if (process.env.FIREBIRD_DEBUG) {
1938
+ console.log('[fb-debug] auth: server SRP proof (M2) received, waiting for op_accept t=%dms', cnx._authStartTime ? Date.now() - cnx._authStartTime : -1);
1939
+ }
1940
+ return; // Server SRP proof received - wait for op_accept
1958
1941
  }
1959
- return; // Server SRP proof received - wait for op_accept
1960
1942
  }
1961
-
1962
1943
  // Firebird 4/5 (protocols 16/17) chained-auth: after the client sends
1963
1944
  // the SRP M1 proof, the server sends op_cont_auth with Legacy_Auth.
1964
1945
  // SRP has already established the session key; the server additionally
1965
1946
  // requires a Legacy_Auth verification step before sending op_accept.
1966
1947
  // Respond with Legacy_Auth credentials and wait for op_accept.
1967
- if (cnx._pendingAccept && pluginName === Const.AUTH_PLUGIN_LEGACY) {
1948
+ if (cnx._pendingAccept && pluginName === const_1.default.AUTH_PLUGIN_LEGACY) {
1968
1949
  if (process.env.FIREBIRD_DEBUG) {
1969
- console.log('[fb-debug] auth: SRP+Legacy_Auth chained-auth (proto %d), sending Legacy_Auth credentials t=%dms',
1970
- cnx._pendingAccept.protocolVersion,
1971
- cnx._authStartTime ? Date.now() - cnx._authStartTime : -1);
1950
+ console.log('[fb-debug] auth: SRP+Legacy_Auth chained-auth (proto %d), sending Legacy_Auth credentials t=%dms', cnx._pendingAccept.protocolVersion, cnx._authStartTime ? Date.now() - cnx._authStartTime : -1);
1972
1951
  }
1973
- var legacyAuthData = crypt.crypt(cnx.options.password, Const.LEGACY_AUTH_SALT).substring(2);
1974
- cnx.sendOpContAuth(legacyAuthData, Const.DEFAULT_ENCODING, pluginName);
1952
+ var legacyAuthData = crypt.crypt(cnx.options.password, const_1.default.LEGACY_AUTH_SALT).substring(2);
1953
+ cnx.sendOpContAuth(legacyAuthData, const_1.default.DEFAULT_ENCODING, pluginName);
1975
1954
  return; // wait for op_accept
1976
1955
  }
1977
-
1978
1956
  if (!cnx.options.pluginName) {
1979
1957
  if (cnx.accept && cnx.accept.pluginName === pluginName) {
1980
1958
  // Erreur plugin not able to connect
1981
1959
  return cb(new Error("Unable to connect with plugin " + cnx.accept.pluginName));
1982
1960
  }
1983
-
1984
- if (pluginName === Const.AUTH_PLUGIN_LEGACY) { // Fallback to LegacyAuth
1961
+ if (pluginName === const_1.default.AUTH_PLUGIN_LEGACY) { // Fallback to LegacyAuth
1985
1962
  cnx.accept.pluginName = pluginName;
1986
- cnx.accept.authData = crypt.crypt(cnx.options.password, Const.LEGACY_AUTH_SALT).substring(2);
1987
-
1988
- cnx.sendOpContAuth(
1989
- cnx.accept.authData,
1990
- Const.DEFAULT_ENCODING,
1991
- pluginName
1992
- );
1993
-
1994
- return {error: new Error('login')};
1963
+ cnx.accept.authData = crypt.crypt(cnx.options.password, const_1.default.LEGACY_AUTH_SALT).substring(2);
1964
+ cnx.sendOpContAuth(cnx.accept.authData, const_1.default.DEFAULT_ENCODING, pluginName);
1965
+ return { error: new Error('login') };
1995
1966
  }
1996
1967
  }
1997
-
1998
1968
  // Server sent op_cont_auth but we don't know how to handle it.
1999
1969
  if (process.env.FIREBIRD_DEBUG) {
2000
- console.warn('[fb-debug] auth: op_cont_auth unhandled plugin=%s pendingAccept=%s options.plugin=%s t=%dms',
2001
- pluginName,
2002
- cnx._pendingAccept ? cnx._pendingAccept.pluginName : 'none',
2003
- cnx.options.pluginName || 'none',
2004
- cnx._authStartTime ? Date.now() - cnx._authStartTime : -1);
1970
+ console.warn('[fb-debug] auth: op_cont_auth unhandled plugin=%s pendingAccept=%s options.plugin=%s t=%dms', pluginName, cnx._pendingAccept ? cnx._pendingAccept.pluginName : 'none', cnx.options.pluginName || 'none', cnx._authStartTime ? Date.now() - cnx._authStartTime : -1);
2005
1971
  }
2006
1972
  return cb(new Error('Unhandled server op_cont_auth for plugin: ' + pluginName));
2007
- case Const.op_crypt_key_callback:
1973
+ case const_1.default.op_crypt_key_callback:
2008
1974
  // Database encryption key callback
2009
1975
  // Read server data (plugin data sent by server)
2010
1976
  var serverPluginData = data.readArray();
2011
1977
  data.readInt(); // p_cc_reply
2012
-
2013
1978
  // Get client response from dbCryptConfig option
2014
1979
  var clientPluginData = parseDbCryptConfig(cnx.options.dbCryptConfig);
2015
-
2016
1980
  // Create a BlrWriter to send the response
2017
1981
  // Note: BlrWriter needs initial buffer size allocation
2018
- var responseBlr = new BlrWriter(clientPluginData.length + 4);
1982
+ var responseBlr = new serialize_1.BlrWriter(clientPluginData.length + 4);
2019
1983
  responseBlr.addBytes(clientPluginData);
2020
-
2021
1984
  // Send the response back to the server
2022
1985
  cnx.sendOpCryptKeyCallback(responseBlr);
2023
-
2024
1986
  // Don't call cb - wait for next operation (likely op_response or another op_crypt_key_callback)
2025
1987
  return;
2026
- case Const.op_event:
1988
+ case const_1.default.op_event:
2027
1989
  // op_event may occasionally arrive on the main connection
2028
1990
  // (e.g. Firebird routing an async notification here instead of
2029
1991
  // the dedicated aux socket). Consume all its fields so the
@@ -2036,16 +1998,16 @@ function decodeResponse(data, callback, cnx, lowercase_keys, cb) {
2036
1998
  // p_event_ast : Int64 – AST routine pointer (0 for remote)
2037
1999
  // p_event_rid : Int32 – remote event ID
2038
2000
  {
2039
- const evtDb = data.readInt(); // p_event_database
2040
- data.readArray(); // p_event_items (EPB buffer)
2041
- data.readInt64(); // p_event_ast
2042
- const evtRid = data.readInt(); // p_event_rid
2001
+ const evtDb = data.readInt(); // p_event_database
2002
+ data.readArray(); // p_event_items (EPB buffer)
2003
+ data.readInt64(); // p_event_ast
2004
+ const evtRid = data.readInt(); // p_event_rid
2043
2005
  if (process.env.FIREBIRD_DEBUG) {
2044
2006
  console.log('[fb-debug] op_event on main connection: db=%d rid=%d (consumed, not queued)', evtDb, evtRid);
2045
2007
  }
2046
2008
  }
2047
2009
  return cb(null, { _isOpEvent: true });
2048
- case Const.op_response_piggyback:
2010
+ case const_1.default.op_response_piggyback:
2049
2011
  // Firebird 5 (Protocol 16/17) sends op_response_piggyback (72)
2050
2012
  // as an unsolicited cleanup notification after certain operations
2051
2013
  // (e.g. after the EventConnection aux socket is torn down).
@@ -2059,11 +2021,12 @@ function decodeResponse(data, callback, cnx, lowercase_keys, cb) {
2059
2021
  // object : Quad (2x Int32)
2060
2022
  // data : Array
2061
2023
  // status : status-vector ending with isc_arg_end
2062
- parseOpResponse(data, {}, function(err) {
2024
+ parseOpResponse(data, {}, function (err) {
2063
2025
  if (process.env.FIREBIRD_DEBUG) {
2064
2026
  if (err) {
2065
2027
  console.warn('[fb-debug] op_response_piggyback parse error:', err.message);
2066
- } else {
2028
+ }
2029
+ else {
2067
2030
  console.log('[fb-debug] op_response_piggyback consumed (unsolicited Firebird 5 cleanup)');
2068
2031
  }
2069
2032
  }
@@ -2071,15 +2034,14 @@ function decodeResponse(data, callback, cnx, lowercase_keys, cb) {
2071
2034
  return cb(null, { _isOpEvent: true });
2072
2035
  default:
2073
2036
  if (process.env.FIREBIRD_DEBUG) {
2074
- console.warn('[fb-debug] unknown opcode=%d at pos=%d buflen=%d queue=%d',
2075
- r, data.pos, data.buffer.length, cnx && cnx._queue ? cnx._queue.length : 0);
2037
+ console.warn('[fb-debug] unknown opcode=%d at pos=%d buflen=%d queue=%d', r, data.pos, data.buffer.length, cnx && cnx._queue ? cnx._queue.length : 0);
2076
2038
  }
2077
2039
  return cb(new Error('Unexpected:' + r));
2078
2040
  }
2079
- } catch (err) {
2041
+ }
2042
+ catch (err) {
2080
2043
  if (process.env.FIREBIRD_DEBUG) {
2081
- console.warn('[fb-debug] decodeResponse exception: %s (RangeError=%s) pos=%d buflen=%d',
2082
- err.message, err instanceof RangeError, data.pos, data.buffer.length);
2044
+ console.warn('[fb-debug] decodeResponse exception: %s (RangeError=%s) pos=%d buflen=%d', err.message, err instanceof RangeError, data.pos, data.buffer.length);
2083
2045
  }
2084
2046
  if (err instanceof RangeError) {
2085
2047
  return cb(err);
@@ -2087,177 +2049,218 @@ function decodeResponse(data, callback, cnx, lowercase_keys, cb) {
2087
2049
  throw err;
2088
2050
  }
2089
2051
  }
2090
-
2091
2052
  function parseOpResponse(data, response, cb) {
2092
2053
  var handle = data.readInt();
2093
-
2094
2054
  if (!response.handle) {
2095
2055
  response.handle = handle;
2096
2056
  }
2097
-
2098
2057
  var oid = data.readQuad();
2099
2058
  if (oid.low || oid.high) {
2100
2059
  response.oid = oid;
2101
2060
  }
2102
-
2103
2061
  var buf = data.readArray();
2104
2062
  if (buf) {
2105
2063
  response.buffer = buf;
2106
2064
  }
2107
-
2108
2065
  var num, op, item = {};
2109
2066
  while (true) {
2110
2067
  op = data.readInt();
2111
-
2112
2068
  switch (op) {
2113
- case Const.isc_arg_end:
2069
+ case const_1.default.isc_arg_end:
2114
2070
  return cb ? cb(undefined, response) : response;
2115
- case Const.isc_arg_gds:
2071
+ case const_1.default.isc_arg_gds:
2116
2072
  num = data.readInt();
2117
2073
  if (!num) {
2118
2074
  break;
2119
2075
  }
2120
-
2121
- item = {gdscode: num};
2122
-
2076
+ item = { gdscode: num };
2123
2077
  if (response.status) {
2124
2078
  response.status.push(item);
2125
- } else {
2079
+ }
2080
+ else {
2126
2081
  response.status = [item];
2127
2082
  }
2128
-
2129
2083
  break;
2130
- case Const.isc_arg_string:
2131
- case Const.isc_arg_interpreted:
2132
- case Const.isc_arg_sql_state:
2084
+ case const_1.default.isc_arg_string:
2085
+ case const_1.default.isc_arg_interpreted:
2086
+ case const_1.default.isc_arg_sql_state:
2133
2087
  if (item.params) {
2134
- var str = data.readString(Const.DEFAULT_ENCODING);
2088
+ var str = data.readString(const_1.default.DEFAULT_ENCODING);
2135
2089
  item.params.push(str);
2136
- } else {
2137
- item.params = [data.readString(Const.DEFAULT_ENCODING)];
2138
2090
  }
2139
-
2091
+ else {
2092
+ item.params = [data.readString(const_1.default.DEFAULT_ENCODING)];
2093
+ }
2140
2094
  break;
2141
- case Const.isc_arg_number:
2095
+ case const_1.default.isc_arg_number:
2142
2096
  num = data.readInt();
2143
-
2144
2097
  if (item.params) {
2145
2098
  item.params.push(num);
2146
- } else {
2099
+ }
2100
+ else {
2147
2101
  item.params = [num];
2148
2102
  }
2149
-
2150
- if (item.gdscode === Const.isc_sqlerr) {
2103
+ if (item.gdscode === const_1.default.isc_sqlerr) {
2151
2104
  response.sqlcode = num;
2152
2105
  }
2153
-
2154
2106
  break;
2155
2107
  default:
2156
2108
  if (cb) {
2157
- cb(new Error('Unexpected: ' + op))
2158
- } else {
2109
+ cb(new Error('Unexpected: ' + op));
2110
+ }
2111
+ else {
2159
2112
  throw new Error('Unexpected: ' + op);
2160
2113
  }
2161
2114
  }
2162
2115
  }
2163
2116
  }
2164
-
2165
2117
  function describe(buff, statement) {
2166
- var br = new BlrReader(buff);
2118
+ var br = new serialize_1.BlrReader(buff);
2167
2119
  var parameters = null;
2168
2120
  var type, param;
2169
-
2170
2121
  while (br.pos < br.buffer.length) {
2171
2122
  switch (br.readByteCode()) {
2172
- case Const.isc_info_sql_stmt_type:
2123
+ case const_1.default.isc_info_sql_stmt_type:
2173
2124
  statement.type = br.readInt();
2174
2125
  break;
2175
- case Const.isc_info_sql_get_plan:
2176
- statement.plan = br.readString(Const.DEFAULT_ENCODING);
2126
+ case const_1.default.isc_info_sql_get_plan:
2127
+ statement.plan = br.readString(const_1.default.DEFAULT_ENCODING);
2177
2128
  break;
2178
- case Const.isc_info_sql_select:
2129
+ case const_1.default.isc_info_sql_select:
2179
2130
  statement.output = parameters = [];
2180
2131
  break;
2181
- case Const.isc_info_sql_bind:
2132
+ case const_1.default.isc_info_sql_bind:
2182
2133
  statement.input = parameters = [];
2183
2134
  break;
2184
- case Const.isc_info_sql_num_variables:
2135
+ case const_1.default.isc_info_sql_num_variables:
2185
2136
  br.readInt(); // eat int
2186
2137
  break;
2187
- case Const.isc_info_sql_describe_vars:
2188
- if (!parameters) {return}
2138
+ case const_1.default.isc_info_sql_describe_vars:
2139
+ if (!parameters) {
2140
+ return;
2141
+ }
2189
2142
  br.readInt(); // eat int ?
2190
2143
  var finishDescribe = false;
2191
2144
  param = null;
2192
- while (!finishDescribe){
2145
+ while (!finishDescribe) {
2193
2146
  switch (br.readByteCode()) {
2194
- case Const.isc_info_sql_describe_end:
2147
+ case const_1.default.isc_info_sql_describe_end:
2195
2148
  break;
2196
- case Const.isc_info_sql_sqlda_seq:
2149
+ case const_1.default.isc_info_sql_sqlda_seq:
2197
2150
  var num = br.readInt();
2198
2151
  break;
2199
- case Const.isc_info_sql_type:
2152
+ case const_1.default.isc_info_sql_type:
2200
2153
  type = br.readInt();
2201
- switch (type&~1) {
2202
- case Const.SQL_VARYING: param = new Xsql.SQLVarString(); break;
2203
- case Const.SQL_NULL: param = new Xsql.SQLVarNull(); break;
2204
- case Const.SQL_TEXT: param = new Xsql.SQLVarText(); break;
2205
- case Const.SQL_DOUBLE: param = new Xsql.SQLVarDouble(); break;
2206
- case Const.SQL_FLOAT:
2207
- case Const.SQL_D_FLOAT: param = new Xsql.SQLVarFloat(); break;
2208
- case Const.SQL_TYPE_DATE: param = new Xsql.SQLVarDate(); break;
2209
- case Const.SQL_TYPE_TIME: param = new Xsql.SQLVarTime(); break;
2210
- case Const.SQL_TIMESTAMP: param = new Xsql.SQLVarTimeStamp(); break;
2211
- case Const.SQL_TIME_TZ: param = new Xsql.SQLVarTimeTz(); break;
2212
- case Const.SQL_TIME_TZ_EX: param = new Xsql.SQLVarTimeTzEx(); break;
2213
- case Const.SQL_TIMESTAMP_TZ: param = new Xsql.SQLVarTimeStampTz(); break;
2214
- case Const.SQL_TIMESTAMP_TZ_EX: param = new Xsql.SQLVarTimeStampTzEx(); break;
2215
- case Const.SQL_BLOB: param = new Xsql.SQLVarBlob(); break;
2216
- case Const.SQL_ARRAY: param = new Xsql.SQLVarArray(); break;
2217
- case Const.SQL_QUAD: param = new Xsql.SQLVarQuad(); break;
2218
- case Const.SQL_LONG: param = new Xsql.SQLVarInt(); break;
2219
- case Const.SQL_SHORT: param = new Xsql.SQLVarShort(); break;
2220
- case Const.SQL_INT64: param = new Xsql.SQLVarInt64(); break;
2221
- case Const.SQL_INT128: param = new Xsql.SQLVarInt128(); break;
2222
- case Const.SQL_DEC16: param = new Xsql.SQLVarDecFloat16(); break;
2223
- case Const.SQL_DEC34: param = new Xsql.SQLVarDecFloat34(); break;
2224
- case Const.SQL_BOOLEAN: param = new Xsql.SQLVarBoolean(); break;
2154
+ switch (type & ~1) {
2155
+ case const_1.default.SQL_VARYING:
2156
+ param = new Xsql.SQLVarString();
2157
+ break;
2158
+ case const_1.default.SQL_NULL:
2159
+ param = new Xsql.SQLVarNull();
2160
+ break;
2161
+ case const_1.default.SQL_TEXT:
2162
+ param = new Xsql.SQLVarText();
2163
+ break;
2164
+ case const_1.default.SQL_DOUBLE:
2165
+ param = new Xsql.SQLVarDouble();
2166
+ break;
2167
+ case const_1.default.SQL_FLOAT:
2168
+ case const_1.default.SQL_D_FLOAT:
2169
+ param = new Xsql.SQLVarFloat();
2170
+ break;
2171
+ case const_1.default.SQL_TYPE_DATE:
2172
+ param = new Xsql.SQLVarDate();
2173
+ break;
2174
+ case const_1.default.SQL_TYPE_TIME:
2175
+ param = new Xsql.SQLVarTime();
2176
+ break;
2177
+ case const_1.default.SQL_TIMESTAMP:
2178
+ param = new Xsql.SQLVarTimeStamp();
2179
+ break;
2180
+ case const_1.default.SQL_TIME_TZ:
2181
+ param = new Xsql.SQLVarTimeTz();
2182
+ break;
2183
+ case const_1.default.SQL_TIME_TZ_EX:
2184
+ param = new Xsql.SQLVarTimeTzEx();
2185
+ break;
2186
+ case const_1.default.SQL_TIMESTAMP_TZ:
2187
+ param = new Xsql.SQLVarTimeStampTz();
2188
+ break;
2189
+ case const_1.default.SQL_TIMESTAMP_TZ_EX:
2190
+ param = new Xsql.SQLVarTimeStampTzEx();
2191
+ break;
2192
+ case const_1.default.SQL_BLOB:
2193
+ param = new Xsql.SQLVarBlob();
2194
+ break;
2195
+ case const_1.default.SQL_ARRAY:
2196
+ param = new Xsql.SQLVarArray();
2197
+ break;
2198
+ case const_1.default.SQL_QUAD:
2199
+ param = new Xsql.SQLVarQuad();
2200
+ break;
2201
+ case const_1.default.SQL_LONG:
2202
+ param = new Xsql.SQLVarInt();
2203
+ break;
2204
+ case const_1.default.SQL_SHORT:
2205
+ param = new Xsql.SQLVarShort();
2206
+ break;
2207
+ case const_1.default.SQL_INT64:
2208
+ param = new Xsql.SQLVarInt64();
2209
+ break;
2210
+ case const_1.default.SQL_INT128:
2211
+ param = new Xsql.SQLVarInt128();
2212
+ break;
2213
+ case const_1.default.SQL_DEC16:
2214
+ param = new Xsql.SQLVarDecFloat16();
2215
+ break;
2216
+ case const_1.default.SQL_DEC34:
2217
+ param = new Xsql.SQLVarDecFloat34();
2218
+ break;
2219
+ case const_1.default.SQL_BOOLEAN:
2220
+ param = new Xsql.SQLVarBoolean();
2221
+ break;
2225
2222
  default:
2226
2223
  throw new Error('Unexpected');
2227
2224
  }
2228
- parameters[num-1] = param;
2225
+ parameters[num - 1] = param;
2229
2226
  param.type = type;
2230
2227
  param.nullable = Boolean(param.type & 1);
2231
2228
  param.type &= ~1;
2232
2229
  break;
2233
- case Const.isc_info_sql_sub_type:
2230
+ case const_1.default.isc_info_sql_sub_type:
2234
2231
  param.subType = br.readInt();
2235
2232
  break;
2236
- case Const.isc_info_sql_scale:
2233
+ case const_1.default.isc_info_sql_scale:
2237
2234
  param.scale = br.readInt();
2238
2235
  break;
2239
- case Const.isc_info_sql_length:
2236
+ case const_1.default.isc_info_sql_length:
2240
2237
  param.length = br.readInt();
2241
2238
  break;
2242
- case Const.isc_info_sql_null_ind:
2239
+ case const_1.default.isc_info_sql_null_ind:
2243
2240
  param.nullable = Boolean(br.readInt());
2244
2241
  break;
2245
- case Const.isc_info_sql_field:
2246
- param.field = br.readString(Const.DEFAULT_ENCODING);
2242
+ case const_1.default.isc_info_sql_field:
2243
+ param.field = br.readString(const_1.default.DEFAULT_ENCODING);
2247
2244
  break;
2248
- case Const.isc_info_sql_relation:
2249
- param.relation = br.readString(Const.DEFAULT_ENCODING);
2245
+ case const_1.default.isc_info_sql_relation:
2246
+ param.relation = br.readString(const_1.default.DEFAULT_ENCODING);
2250
2247
  break;
2251
- case Const.isc_info_sql_owner:
2252
- param.owner = br.readString(Const.DEFAULT_ENCODING);
2248
+ case const_1.default.isc_info_sql_relation_schema:
2249
+ // Firebird 6.0 (Protocol 20+): schema that owns the
2250
+ // source relation. Empty string means the default
2251
+ // (PUBLIC) schema or a non-schema-aware server.
2252
+ param.relationSchema = br.readString(const_1.default.DEFAULT_ENCODING);
2253
2253
  break;
2254
- case Const.isc_info_sql_alias:
2255
- param.alias = br.readString(Const.DEFAULT_ENCODING);
2254
+ case const_1.default.isc_info_sql_owner:
2255
+ param.owner = br.readString(const_1.default.DEFAULT_ENCODING);
2256
2256
  break;
2257
- case Const.isc_info_sql_relation_alias:
2258
- param.relationAlias = br.readString(Const.DEFAULT_ENCODING);
2257
+ case const_1.default.isc_info_sql_alias:
2258
+ param.alias = br.readString(const_1.default.DEFAULT_ENCODING);
2259
2259
  break;
2260
- case Const.isc_info_truncated:
2260
+ case const_1.default.isc_info_sql_relation_alias:
2261
+ param.relationAlias = br.readString(const_1.default.DEFAULT_ENCODING);
2262
+ break;
2263
+ case const_1.default.isc_info_truncated:
2261
2264
  throw new Error('Truncated');
2262
2265
  default:
2263
2266
  finishDescribe = true;
@@ -2266,103 +2269,112 @@ function describe(buff, statement) {
2266
2269
  }
2267
2270
  }
2268
2271
  }
2272
+ function unpackCharSetCollation(params) {
2273
+ if (!params)
2274
+ return;
2275
+ for (let i = 0; i < params.length; i++) {
2276
+ const p = params[i];
2277
+ if (p && (p.type === const_1.default.SQL_TEXT || p.type === const_1.default.SQL_VARYING)) {
2278
+ if (p.subType !== undefined) {
2279
+ p.charSetId = p.subType & 0xFF;
2280
+ p.collationId = p.subType >> 8;
2281
+ }
2282
+ }
2283
+ }
2284
+ }
2285
+ unpackCharSetCollation(statement.input);
2286
+ unpackCharSetCollation(statement.output);
2269
2287
  }
2270
-
2271
2288
  function CalcBlr(blr, xsqlda) {
2272
- blr.addBytes([Const.blr_version5, Const.blr_begin, Const.blr_message, 0]); // + message number
2289
+ blr.addBytes([const_1.default.blr_version5, const_1.default.blr_begin, const_1.default.blr_message, 0]); // + message number
2273
2290
  blr.addWord(xsqlda.length * 2);
2274
-
2275
2291
  for (var i = 0, length = xsqlda.length; i < length; i++) {
2276
2292
  xsqlda[i].calcBlr(blr);
2277
- blr.addByte(Const.blr_short);
2293
+ blr.addByte(const_1.default.blr_short);
2278
2294
  blr.addByte(0);
2279
2295
  }
2280
-
2281
- blr.addByte(Const.blr_end);
2282
- blr.addByte(Const.blr_eoc);
2296
+ blr.addByte(const_1.default.blr_end);
2297
+ blr.addByte(const_1.default.blr_eoc);
2283
2298
  }
2284
-
2285
2299
  function fetch_blob_async_transaction(statement, id, column, row) {
2286
2300
  const infoValue = { row, column, value: '' };
2287
-
2288
2301
  return (transactionArg) => {
2302
+ const cacheKey = `${id.high}:${id.low}`;
2303
+ if (statement.connection._inlineBlobs && statement.connection._inlineBlobs.has(cacheKey)) {
2304
+ const data = statement.connection._inlineBlobs.get(cacheKey);
2305
+ infoValue.value = data ? data.toString(const_1.default.DEFAULT_ENCODING) : '';
2306
+ return Promise.resolve(infoValue);
2307
+ }
2289
2308
  const singleTransaction = transactionArg === undefined;
2290
-
2291
2309
  let promiseTransaction;
2292
2310
  if (singleTransaction) {
2293
2311
  promiseTransaction = new Promise((resolve, reject) => {
2294
- statement.connection.startTransaction(Const.ISOLATION_READ_UNCOMMITTED, (err, transaction) => {
2312
+ statement.connection.startTransaction(const_1.default.ISOLATION_READ_UNCOMMITTED, (err, transaction) => {
2295
2313
  if (err) {
2296
2314
  return reject(err);
2297
2315
  }
2298
2316
  resolve(transaction);
2299
2317
  });
2300
2318
  });
2301
- } else {
2319
+ }
2320
+ else {
2302
2321
  promiseTransaction = Promise.resolve(transactionArg);
2303
2322
  }
2304
-
2305
2323
  return promiseTransaction.then((transaction) => {
2306
2324
  return new Promise((resolve, reject) => {
2307
2325
  statement.connection._pending.push('openBlob');
2308
2326
  statement.connection.openBlob(id, transaction, (err, blob) => {
2309
-
2310
2327
  if (err) {
2311
2328
  reject(err);
2312
2329
  return;
2313
2330
  }
2314
-
2315
2331
  const read = () => {
2316
2332
  statement.connection.getSegment(blob, (err, ret) => {
2317
-
2318
2333
  if (err) {
2319
2334
  if (singleTransaction) {
2320
2335
  transaction.rollback(() => reject(err));
2321
- } else {
2336
+ }
2337
+ else {
2322
2338
  reject(err);
2323
2339
  }
2324
2340
  return;
2325
2341
  }
2326
-
2327
2342
  if (ret.buffer) {
2328
- const blr = new BlrReader(ret.buffer);
2343
+ const blr = new serialize_1.BlrReader(ret.buffer);
2329
2344
  const data = blr.readSegment();
2330
- infoValue.value += data.toString(Const.DEFAULT_ENCODING);
2345
+ infoValue.value += data.toString(const_1.default.DEFAULT_ENCODING);
2331
2346
  }
2332
-
2333
2347
  if (ret.handle !== 2) {
2334
2348
  read();
2335
2349
  return;
2336
2350
  }
2337
-
2338
2351
  statement.connection.closeBlob(blob);
2339
2352
  if (singleTransaction) {
2340
2353
  transaction.commit((err) => {
2341
2354
  if (err) {
2342
2355
  reject(err);
2343
- } else {
2356
+ }
2357
+ else {
2344
2358
  resolve(infoValue);
2345
2359
  }
2346
2360
  });
2347
- } else {
2361
+ }
2362
+ else {
2348
2363
  resolve(infoValue);
2349
2364
  }
2350
2365
  });
2351
2366
  };
2352
-
2353
2367
  read();
2354
2368
  });
2355
2369
  });
2356
2370
  });
2357
2371
  };
2358
2372
  }
2359
-
2360
2373
  function fetch_blob_async(statement, id, name, row) {
2361
2374
  const cbTransaction = (transaction, close, callback) => {
2362
2375
  statement.connection._pending.push('openBlob');
2363
2376
  statement.connection.openBlob(id, transaction, (err, blob) => {
2364
- let e = new Events.EventEmitter();
2365
-
2377
+ let e = new events_1.default.EventEmitter();
2366
2378
  e.pipe = (stream) => {
2367
2379
  e.on('data', (chunk) => {
2368
2380
  stream.write(chunk);
@@ -2371,98 +2383,110 @@ function fetch_blob_async(statement, id, name, row) {
2371
2383
  stream.end();
2372
2384
  });
2373
2385
  };
2374
-
2375
2386
  if (err) {
2376
2387
  return callback(err, name, e, row);
2377
2388
  }
2378
-
2379
2389
  const read = () => {
2380
2390
  statement.connection.getSegment(blob, (err, ret) => {
2381
-
2382
2391
  if (err) {
2383
2392
  transaction.rollback(() => {
2384
2393
  e.emit('error', err);
2385
2394
  });
2386
2395
  return;
2387
2396
  }
2388
-
2389
2397
  if (ret.buffer) {
2390
- const blr = new BlrReader(ret.buffer);
2398
+ const blr = new serialize_1.BlrReader(ret.buffer);
2391
2399
  const data = blr.readSegment();
2392
-
2393
2400
  e.emit('data', data);
2394
2401
  }
2395
-
2396
2402
  if (ret.handle !== 2) {
2397
2403
  read();
2398
2404
  return;
2399
2405
  }
2400
-
2401
2406
  statement.connection.closeBlob(blob);
2402
2407
  if (close) {
2403
2408
  transaction.commit((err) => {
2404
2409
  if (err) {
2405
2410
  e.emit('error', err);
2406
- } else {
2411
+ }
2412
+ else {
2407
2413
  e.emit('end');
2408
2414
  }
2409
2415
  e = null;
2410
2416
  });
2411
- } else {
2417
+ }
2418
+ else {
2412
2419
  e.emit('end');
2413
2420
  e = null;
2414
2421
  }
2415
2422
  });
2416
2423
  };
2417
-
2418
2424
  callback(err, name, e, row);
2419
2425
  read();
2420
2426
  });
2421
2427
  };
2422
-
2423
2428
  return (transaction, callback) => {
2424
2429
  // callback(error, nameField, eventEmitter, row)
2425
2430
  const singleTransaction = callback === undefined;
2431
+ const actualCallback = singleTransaction ? transaction : callback;
2432
+ const cacheKey = `${id.high}:${id.low}`;
2433
+ if (statement.connection._inlineBlobs && statement.connection._inlineBlobs.has(cacheKey)) {
2434
+ const data = statement.connection._inlineBlobs.get(cacheKey);
2435
+ let e = new events_1.default.EventEmitter();
2436
+ e.pipe = (stream) => {
2437
+ e.on('data', (chunk) => {
2438
+ stream.write(chunk);
2439
+ });
2440
+ e.on('end', () => {
2441
+ stream.end();
2442
+ });
2443
+ };
2444
+ actualCallback(null, name, e, row);
2445
+ setImmediate(() => {
2446
+ if (data) {
2447
+ e.emit('data', data);
2448
+ }
2449
+ e.emit('end');
2450
+ });
2451
+ return;
2452
+ }
2426
2453
  if (singleTransaction) {
2427
2454
  callback = transaction;
2428
- statement.connection.startTransaction(Const.ISOLATION_READ_UNCOMMITTED, (err, transaction) => {
2455
+ statement.connection.startTransaction(const_1.default.ISOLATION_READ_UNCOMMITTED, (err, transaction) => {
2429
2456
  if (err) {
2430
2457
  callback(err);
2431
2458
  return;
2432
2459
  }
2433
2460
  cbTransaction(transaction, singleTransaction, callback);
2434
2461
  });
2435
- } else {
2462
+ }
2463
+ else {
2436
2464
  cbTransaction(transaction, singleTransaction, callback);
2437
2465
  }
2438
2466
  };
2439
2467
  }
2440
-
2441
2468
  function doSynchronousLoop(data, processData, done) {
2442
2469
  if (!data || !data.length) {
2443
2470
  done();
2444
2471
  return;
2445
2472
  }
2446
-
2447
2473
  const loop = (index) => {
2448
2474
  processData(data[index], index, (err) => {
2449
2475
  if (err) {
2450
2476
  done(err);
2451
2477
  return;
2452
2478
  }
2453
-
2454
2479
  const nextIndex = index + 1;
2455
2480
  if (nextIndex < data.length) {
2456
2481
  loop(nextIndex);
2457
- } else {
2482
+ }
2483
+ else {
2458
2484
  done();
2459
2485
  }
2460
2486
  });
2461
2487
  };
2462
-
2463
2488
  loop(0);
2464
2489
  }
2465
-
2466
2490
  function executeStreamRow(custom, row, index, output, next) {
2467
2491
  let done = false;
2468
2492
  const finish = (err) => {
@@ -2472,47 +2496,40 @@ function executeStreamRow(custom, row, index, output, next) {
2472
2496
  done = true;
2473
2497
  next(err);
2474
2498
  };
2475
-
2476
2499
  try {
2477
2500
  const ret = custom.on(row, index, output, finish);
2478
2501
  if (custom.on.length < 4) {
2479
2502
  if (ret && typeof ret.then === 'function') {
2480
2503
  ret.then(() => finish()).catch(finish);
2481
- } else {
2504
+ }
2505
+ else {
2482
2506
  finish();
2483
2507
  }
2484
- } else if (ret && typeof ret.then === 'function') {
2508
+ }
2509
+ else if (ret && typeof ret.then === 'function') {
2485
2510
  ret.catch(finish);
2486
2511
  }
2487
- } catch (err) {
2512
+ }
2513
+ catch (err) {
2488
2514
  finish(err);
2489
2515
  }
2490
2516
  }
2491
-
2492
2517
  function bufferReader(buffer, max, writer, cb, beg, end) {
2493
-
2494
2518
  if (!beg)
2495
2519
  beg = 0;
2496
-
2497
2520
  if (!end)
2498
2521
  end = max;
2499
-
2500
2522
  if (end >= buffer.length)
2501
2523
  end = undefined;
2502
-
2503
2524
  var b = buffer.slice(beg, end);
2504
-
2505
- writer(b, function() {
2506
-
2525
+ writer(b, function () {
2507
2526
  if (end === undefined) {
2508
2527
  cb();
2509
2528
  return;
2510
2529
  }
2511
-
2512
2530
  bufferReader(buffer, max, writer, cb, beg + max, end + max);
2513
2531
  });
2514
2532
  }
2515
-
2516
2533
  /**
2517
2534
  * Parse dbCryptConfig option and convert to bytes.
2518
2535
  * Supports:
@@ -2524,20 +2541,23 @@ function parseDbCryptConfig(config) {
2524
2541
  if (!config) {
2525
2542
  return Buffer.alloc(0);
2526
2543
  }
2527
-
2528
2544
  // Check if it's a base64 encoded value
2529
2545
  if (config.startsWith('base64:')) {
2530
2546
  const base64Value = config.substring(7);
2531
2547
  try {
2532
2548
  return Buffer.from(base64Value, 'base64');
2533
- } catch (e) {
2549
+ }
2550
+ catch (e) {
2534
2551
  console.error('Failed to decode base64 dbCryptConfig, returning empty buffer:', e);
2535
2552
  return Buffer.alloc(0);
2536
2553
  }
2537
2554
  }
2538
-
2539
2555
  // Plain string - encode as UTF-8
2540
2556
  return Buffer.from(config, 'utf8');
2541
2557
  }
2542
-
2558
+ Connection.decodeResponse = decodeResponse;
2559
+ Connection.fetch_blob_async_transaction = fetch_blob_async_transaction;
2560
+ Connection.fetch_blob_async = fetch_blob_async;
2561
+ Connection.parseValueIfJson = parseValueIfJson;
2562
+ Connection.describe = describe;
2543
2563
  module.exports = Connection;