node-firebird 2.3.1 → 2.3.3

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 (55) hide show
  1. package/README.md +218 -31
  2. package/lib/index.d.ts +22 -0
  3. package/lib/pool.js +97 -10
  4. package/lib/srp.js +12 -1
  5. package/lib/wire/connection.js +40 -7
  6. package/lib/wire/socket.js +13 -1
  7. package/lib/wire/xsqlvar.js +69 -6
  8. package/package.json +1 -1
  9. package/poc/README.md +160 -0
  10. package/poc/helpers.js +59 -0
  11. package/poc/node_modules/.package-lock.json +14 -0
  12. package/poc/node_modules/node-firebird/.eslintrc.json +12 -0
  13. package/poc/node_modules/node-firebird/.github/workflows/codeql.yml +76 -0
  14. package/poc/node_modules/node-firebird/.github/workflows/node.js.yml +95 -0
  15. package/poc/node_modules/node-firebird/BIGINT_MIGRATION.md +374 -0
  16. package/poc/node_modules/node-firebird/CI_DEBUGGING_GUIDE.md +148 -0
  17. package/poc/node_modules/node-firebird/ENCRYPTION_CALLBACK.md +152 -0
  18. package/poc/node_modules/node-firebird/FIREBIRD_LOG_FEATURE.md +145 -0
  19. package/poc/node_modules/node-firebird/LICENSE +373 -0
  20. package/poc/node_modules/node-firebird/MINIMAL_CHANGES_SUMMARY.md +136 -0
  21. package/poc/node_modules/node-firebird/PR_SUMMARY.md +96 -0
  22. package/poc/node_modules/node-firebird/README.md +794 -0
  23. package/poc/node_modules/node-firebird/ROADMAP.md +223 -0
  24. package/poc/node_modules/node-firebird/SRP_PROTOCOL.md +482 -0
  25. package/poc/node_modules/node-firebird/lib/callback.js +38 -0
  26. package/poc/node_modules/node-firebird/lib/firebird.msg +0 -0
  27. package/poc/node_modules/node-firebird/lib/firebird.msg.json +1371 -0
  28. package/poc/node_modules/node-firebird/lib/gdscodes.d.ts +1524 -0
  29. package/poc/node_modules/node-firebird/lib/gdscodes.js +1531 -0
  30. package/poc/node_modules/node-firebird/lib/ieee754-decimal.js +500 -0
  31. package/poc/node_modules/node-firebird/lib/index.d.ts +316 -0
  32. package/poc/node_modules/node-firebird/lib/index.js +128 -0
  33. package/poc/node_modules/node-firebird/lib/messages.js +162 -0
  34. package/poc/node_modules/node-firebird/lib/pool.js +108 -0
  35. package/poc/node_modules/node-firebird/lib/srp.js +299 -0
  36. package/poc/node_modules/node-firebird/lib/unix-crypt.js +343 -0
  37. package/poc/node_modules/node-firebird/lib/utils.js +164 -0
  38. package/poc/node_modules/node-firebird/lib/wire/connection.js +2510 -0
  39. package/poc/node_modules/node-firebird/lib/wire/const.js +807 -0
  40. package/poc/node_modules/node-firebird/lib/wire/database.js +378 -0
  41. package/poc/node_modules/node-firebird/lib/wire/eventConnection.js +118 -0
  42. package/poc/node_modules/node-firebird/lib/wire/fbEventManager.js +326 -0
  43. package/poc/node_modules/node-firebird/lib/wire/serialize.js +588 -0
  44. package/poc/node_modules/node-firebird/lib/wire/service.js +1058 -0
  45. package/poc/node_modules/node-firebird/lib/wire/socket.js +175 -0
  46. package/poc/node_modules/node-firebird/lib/wire/statement.js +48 -0
  47. package/poc/node_modules/node-firebird/lib/wire/transaction.js +206 -0
  48. package/poc/node_modules/node-firebird/lib/wire/xsqlvar.js +703 -0
  49. package/poc/node_modules/node-firebird/package.json +38 -0
  50. package/poc/node_modules/node-firebird/vitest.config.js +24 -0
  51. package/poc/package-lock.json +21 -0
  52. package/poc/package.json +12 -0
  53. package/poc/reproduce-fixed.js +150 -0
  54. package/poc/reproduce.js +133 -0
  55. package/vitest.config.js +4 -1
@@ -0,0 +1,378 @@
1
+ const Events = require('events');
2
+ const { doError } = require('../callback');
3
+ const { escape } = require('../utils');
4
+ const Const = require('./const');
5
+ const EventConnection = require('./eventConnection');
6
+ const FbEventManager = require('./fbEventManager');
7
+
8
+ /***************************************
9
+ *
10
+ * Database
11
+ *
12
+ * Driver events (emitted on the Database instance itself)
13
+ * --------------------------------------------------------
14
+ * These are synchronous notifications from the driver about connection-level
15
+ * operations. Subscribe with db.on(eventName, handler).
16
+ *
17
+ * 'attach' – fired synchronously after the user callback returns,
18
+ * once the database is attached.
19
+ * 'detach' – fired when the database connection is detached.
20
+ * 'reconnect' – fired after the driver successfully reconnects a dropped socket.
21
+ * 'error' – fired for connection-level errors (socket errors, closed
22
+ * connection attempts, etc.).
23
+ * 'transaction' – fired when a transaction is started (before server response),
24
+ * with the resolved transaction options object as the argument.
25
+ * 'commit' – fired when a transaction commit is sent (before server response).
26
+ * 'rollback' – fired when a transaction rollback is sent (before server response).
27
+ * 'query' – fired with the SQL string when a statement is prepared.
28
+ * 'row' – fired with each individual row as it is decoded.
29
+ * 'result' – fired with the full rows array once all rows are fetched.
30
+ *
31
+ * Firebird database events (POST_EVENT)
32
+ * ----------------------------------------
33
+ * Real Firebird asynchronous notifications triggered by POST_EVENT inside
34
+ * PSQL triggers or stored procedures are handled through a separate channel:
35
+ * 1. Call db.attachEvent(callback) to obtain a FbEventManager instance.
36
+ * 2. Call evtmgr.registerEvent(names, callback) to subscribe to event names.
37
+ * 3. Listen for evtmgr.on('post_event', (name, count) => {}) to receive them.
38
+ * 4. Call evtmgr.unregisterEvent(names, callback) to cancel a subscription.
39
+ * 5. Call evtmgr.close(callback) when done to release the aux connection.
40
+ *
41
+ ***************************************/
42
+
43
+ function readblob(blob, callback) {
44
+ if (blob === undefined || blob === null) {
45
+ callback(null, blob);
46
+ return;
47
+ }
48
+
49
+ if (typeof blob !== 'function') {
50
+ callback(null, blob);
51
+ return;
52
+ }
53
+
54
+ blob(function(err, name, e) {
55
+ if (err) {
56
+ callback(err);
57
+ return;
58
+ }
59
+
60
+ if (!e) {
61
+ callback(null, null);
62
+ return;
63
+ }
64
+
65
+ const chunks = [];
66
+ let chunksLength = 0;
67
+
68
+ e.on('data', function(chunk) {
69
+ chunksLength += chunk.length;
70
+ chunks.push(chunk);
71
+ });
72
+
73
+ e.on('end', function() {
74
+ callback(null, Buffer.concat(chunks, chunksLength));
75
+ });
76
+
77
+ e.on('error', function(streamErr) {
78
+ callback(streamErr);
79
+ });
80
+ });
81
+ }
82
+
83
+ function fetchBlobSyncRow(row, meta, callback) {
84
+ if (!row || !meta || !meta.length) {
85
+ callback(null, row);
86
+ return;
87
+ }
88
+
89
+ const rowKeys = Object.keys(row);
90
+ const blobColumns = [];
91
+
92
+ for (let i = 0; i < meta.length; i++) {
93
+ if (meta[i] && meta[i].type === Const.SQL_BLOB && rowKeys[i] !== undefined) {
94
+ blobColumns.push(rowKeys[i]);
95
+ }
96
+ }
97
+
98
+ if (!blobColumns.length) {
99
+ callback(null, row);
100
+ return;
101
+ }
102
+
103
+ let pending = blobColumns.length;
104
+ let blobErr;
105
+
106
+ blobColumns.forEach(function(columnName) {
107
+ readblob(row[columnName], function(err, data) {
108
+ if (err && !blobErr) {
109
+ blobErr = err;
110
+ }
111
+ row[columnName] = data;
112
+ pending--;
113
+ if (pending === 0) {
114
+ callback(blobErr, row);
115
+ }
116
+ });
117
+ });
118
+ }
119
+
120
+ class Database extends Events.EventEmitter {
121
+ constructor(connection) {
122
+ super();
123
+ this.connection = connection;
124
+ connection.db = this;
125
+ this.eventid = 1;
126
+ }
127
+
128
+ escape(value) {
129
+ return escape(value, this.connection.accept.protocolVersion);
130
+ }
131
+
132
+ detach(callback, force) {
133
+ var self = this;
134
+
135
+ if (!force && self.connection._pending.length > 0) {
136
+ self.connection._detachAuto = true;
137
+ self.connection._detachCallback = callback;
138
+ return self;
139
+ }
140
+
141
+ if (self.connection._pooled === false) {
142
+ self.connection.detach(function (err, obj) {
143
+
144
+ self.connection.disconnect();
145
+ self.emit('detach', false);
146
+
147
+ if (callback)
148
+ callback(err, obj);
149
+
150
+ }, force);
151
+ } else {
152
+ self.emit('detach', false);
153
+ if (callback)
154
+ callback();
155
+ }
156
+
157
+ return self;
158
+ }
159
+
160
+ transaction(options, callback) {
161
+ return this.startTransaction(options, callback);
162
+ }
163
+
164
+ startTransaction(options, callback) {
165
+ this.connection.startTransaction(options, callback);
166
+ return this;
167
+ }
168
+
169
+ newStatement(query, callback) {
170
+ this.startTransaction(function(err, transaction) {
171
+
172
+ if (err) {
173
+ callback(err);
174
+ return;
175
+ }
176
+
177
+ transaction.newStatement(query, function(err, statement) {
178
+
179
+ if (err) {
180
+ callback(err);
181
+ return;
182
+ }
183
+
184
+ transaction.commit(function(err) {
185
+ callback(err, statement);
186
+ });
187
+ });
188
+ });
189
+
190
+ return this;
191
+ }
192
+
193
+ execute(query, params, callback, options) {
194
+ if (params instanceof Function) {
195
+ options = callback;
196
+ callback = params;
197
+ params = undefined;
198
+ }
199
+
200
+ var self = this;
201
+
202
+ self.connection.startTransaction(function(err, transaction) {
203
+
204
+ if (err) {
205
+ doError(err, callback);
206
+ return;
207
+ }
208
+
209
+ transaction.execute(query, params, function(err, result, meta, isSelect) {
210
+
211
+ if (err) {
212
+ transaction.rollback(function() {
213
+ doError(err, callback);
214
+ });
215
+ return;
216
+ }
217
+
218
+ transaction.commit(function(err) {
219
+ if (callback)
220
+ callback(err, result, meta, isSelect);
221
+ });
222
+
223
+ }, options);
224
+ });
225
+
226
+ return self;
227
+ }
228
+
229
+ sequentially(query, params, on, callback, options = {}) {
230
+ if (params instanceof Function) {
231
+ options = callback;
232
+ callback = on;
233
+ on = params;
234
+ params = undefined;
235
+ }
236
+
237
+ if (on === undefined){
238
+ throw new Error('Expected "on" delegate.');
239
+ }
240
+
241
+ if (callback instanceof Boolean) {
242
+ options = callback;
243
+ callback = undefined;
244
+ }
245
+
246
+ var self = this;
247
+ var _on = function(row, i, meta, next) {
248
+ var done = false;
249
+ var finish = function(err) {
250
+ if (done) {
251
+ return;
252
+ }
253
+ done = true;
254
+ next(err);
255
+ };
256
+
257
+ fetchBlobSyncRow(row, meta, function(blobErr) {
258
+ if (blobErr) {
259
+ finish(blobErr);
260
+ return;
261
+ }
262
+
263
+ try {
264
+ var ret;
265
+ if (on.length >= 3) {
266
+ ret = on(row, i, finish);
267
+ } else {
268
+ ret = on(row, i);
269
+ }
270
+
271
+ if (ret && typeof ret.then === 'function') {
272
+ ret.then(function() {
273
+ finish();
274
+ }).catch(finish);
275
+ } else if (on.length < 3) {
276
+ finish();
277
+ }
278
+ } catch (err) {
279
+ finish(err);
280
+ }
281
+ });
282
+ };
283
+
284
+ // back compatibility - options parameter is a boolean
285
+ if (typeof options === 'boolean') {
286
+ options = { asObject: !options, asStream: true, on: _on };
287
+ } else {
288
+ options = {
289
+ asObject: true,
290
+ asStream: true,
291
+ on: _on,
292
+ ...options,
293
+ };
294
+ }
295
+
296
+ self.execute(query, params, callback, options);
297
+ return self;
298
+ }
299
+
300
+ query(query, params, callback, options = {}) {
301
+ if (params instanceof Function) {
302
+ options = callback || {};
303
+ callback = params;
304
+ params = undefined;
305
+ }
306
+
307
+ options = {
308
+ asObject: true,
309
+ asStream: callback === undefined || callback === null,
310
+ ...options
311
+ };
312
+
313
+ var self = this;
314
+ self.execute(query, params, callback, options);
315
+ return self;
316
+ }
317
+
318
+ drop(callback) {
319
+ return this.connection.dropDatabase(callback);
320
+ }
321
+
322
+ attachEvent(callback) {
323
+ var self = this;
324
+ if (process.env.FIREBIRD_DEBUG) {
325
+ console.log('[fb-debug] Database.attachEvent: calling auxConnection, eventid=%d queue=%d', self.eventid, self.connection._queue.length);
326
+ }
327
+ this.connection.auxConnection(function (err, socket_info) {
328
+
329
+ if (err) {
330
+ if (process.env.FIREBIRD_DEBUG) {
331
+ console.log('[fb-debug] Database.attachEvent: auxConnection error:', err.message);
332
+ }
333
+ doError(err, callback);
334
+ return;
335
+ }
336
+
337
+ if (process.env.FIREBIRD_DEBUG) {
338
+ console.log('[fb-debug] Database.attachEvent: auxConnection ok, connecting to aux port %s:%d', socket_info.host, socket_info.port);
339
+ }
340
+
341
+ const host = (socket_info.host === '0.0.0.0' || socket_info.host === '::')
342
+ ? self.connection.options.host
343
+ : socket_info.host;
344
+
345
+ const eventConnection = new EventConnection(
346
+ host, socket_info.port, function(err) {
347
+ if (err) {
348
+ if (process.env.FIREBIRD_DEBUG) {
349
+ console.log('[fb-debug] Database.attachEvent: EventConnection error:', err.message);
350
+ }
351
+ doError(err, callback);
352
+ return;
353
+ }
354
+
355
+ if (process.env.FIREBIRD_DEBUG) {
356
+ console.log('[fb-debug] Database.attachEvent: EventConnection connected, creating FbEventManager eventid=%d', self.eventid);
357
+ }
358
+
359
+ const evt = new FbEventManager(self, eventConnection, self.eventid++, function (err) {
360
+ if (err) {
361
+ doError(err, callback);
362
+ return;
363
+ }
364
+
365
+ if (process.env.FIREBIRD_DEBUG) {
366
+ console.log('[fb-debug] Database.attachEvent: FbEventManager ready, eventid=%d', evt.eventid);
367
+ }
368
+
369
+ callback(err, evt);
370
+ });
371
+ }, self);
372
+ });
373
+
374
+ return this;
375
+ }
376
+ }
377
+
378
+ module.exports = Database;
@@ -0,0 +1,118 @@
1
+ const net = require('net');
2
+ const { XdrReader } = require('./serialize');
3
+ const DEFAULT_ENCODING = 'utf8';
4
+ const Const = require('./const');
5
+
6
+ class EventConnection {
7
+ constructor(host, port, callback, db) {
8
+ var self = this;
9
+ this.db = db;
10
+ this.emgr = null;
11
+ this._isClosed = false;
12
+ this._isOpened = false;
13
+ this._socket = net.createConnection(port, host);
14
+ this._bind_events(host, port, callback);
15
+ this.error;
16
+ this.eventcallback;
17
+ }
18
+
19
+ _bind_events(host, port, callback) {
20
+ var self = this;
21
+
22
+ self._socket.on('close', function () {
23
+
24
+ self._isClosed = true;
25
+ })
26
+
27
+ self._socket.on('error', function (e) {
28
+
29
+ self.error = e;
30
+ })
31
+
32
+ self._socket.on('connect', function () {
33
+ self._isClosed = false;
34
+ self._isOpened = true;
35
+ if (callback)
36
+ callback();
37
+ });
38
+
39
+ self._socket.on('data', function (data) {
40
+ var xdr, buf;
41
+
42
+ if (!self._xdr) {
43
+ xdr = new XdrReader(data);
44
+ } else {
45
+ xdr = self._xdr;
46
+ delete (self._xdr);
47
+ buf = Buffer.from(data.length + xdr.buffer.length);
48
+ xdr.buffer.copy(buf);
49
+ data.copy(buf, xdr.buffer.length);
50
+ xdr.buffer = buf;
51
+ }
52
+
53
+ try {
54
+ var item, op;
55
+ var op_pos = xdr.pos;
56
+ var tmp_event;
57
+ while (xdr.pos < xdr.buffer.length) {
58
+ do {
59
+ var r = xdr.readInt();
60
+ } while (r === Const.op_dummy);
61
+
62
+ switch (r) {
63
+ case Const.op_event:
64
+ xdr.readInt(); // db handle
65
+ var buf = xdr.readArray();
66
+ // first byte is always set to 1
67
+ tmp_event = {};
68
+ var lst_event = [];
69
+ var eventname = '';
70
+ var eventcount = 0;
71
+ var pos = 1;
72
+ while (pos < buf.length) {
73
+ var len = buf.readInt8(pos++);
74
+ eventname = buf.toString(DEFAULT_ENCODING, pos, pos + len);
75
+ var prevcount = self.emgr.events[eventname] || 0;
76
+ pos += len;
77
+ eventcount = buf.readInt32LE(pos);
78
+ tmp_event[eventname] = eventcount;
79
+ pos += 4;
80
+ if (prevcount !== eventcount)
81
+ lst_event.push({ name: eventname, count: eventcount });
82
+ }
83
+ xdr.readInt64(); // ignore AST INFO
84
+ var event_id = xdr.readInt();
85
+ // set the new count in global event hash
86
+ for (var evt in tmp_event) {
87
+ self.emgr.events[evt] = tmp_event[evt];
88
+ }
89
+ if (self.eventcallback)
90
+ self.eventcallback(null, { eventid: event_id, events: lst_event });
91
+ break;
92
+ default:
93
+ // Unknown opcode on the event connection – stop processing.
94
+ return;
95
+ }
96
+ }
97
+ } catch (err) {
98
+ if (err instanceof RangeError) { // incomplete packet case
99
+ xdr.buffer = xdr.buffer = xdr.buffer.slice(op_pos);
100
+ xdr.pos = 0;
101
+ self._xdr = xdr;
102
+ } else {
103
+ throw err;
104
+ }
105
+ }
106
+ })
107
+ }
108
+
109
+ throwClosed(callback) {
110
+ var err = new Error('Event Connection is closed.');
111
+ this.db.emit('error', err);
112
+ if (callback)
113
+ callback(err);
114
+ return this;
115
+ }
116
+ }
117
+
118
+ module.exports = EventConnection;