node-firebird 1.1.9 → 1.1.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -8,7 +8,7 @@ jobs:
8
8
 
9
9
  strategy:
10
10
  matrix:
11
- node: [18, 20]
11
+ node: [20, 22]
12
12
  firebird-version: ['v3']
13
13
 
14
14
  steps:
@@ -17,11 +17,13 @@ jobs:
17
17
  fetch-depth: 10
18
18
 
19
19
  - name: Setup FirebirdSQL container
20
- uses: juarezr/firebirdsql-github-action@v1.2.0
20
+ uses: juarezr/firebirdsql-github-action@v2.0.1
21
21
  with:
22
22
  version: ${{ matrix.firebird-version }}
23
- isc_password: "masterkey"
24
- enable_legacy_client_auth: "true"
23
+ firebird_root_password: "masterkey"
24
+ firebird_conf: |
25
+ WireCrypt = Enabled
26
+ AuthServer = Legacy_Auth,Srp,Win_Sspi
25
27
 
26
28
  - name: Use Node.js ${{ matrix.node }}
27
29
  uses: actions/setup-node@v3
package/README.md CHANGED
@@ -68,6 +68,7 @@ options.pageSize = 4096; // default when creating database
68
68
  options.retryConnectionInterval = 1000; // reconnect interval in case of connection drop
69
69
  options.blobAsText = false; // set to true to get blob as text, only affects blob subtype 1
70
70
  options.encoding = 'UTF8'; // default encoding for connection is UTF-8
71
+ options.wireCompression = false; // set to true for enable firebird compression on the wire (Work only on FB >= 3 and compression is enable on server (WireCompression = true in firebird.conf))
71
72
  ```
72
73
 
73
74
  ### Classic
@@ -113,7 +114,21 @@ pool.destroy();
113
114
  - `db.execute(query, [params], function(err, result))` - classic query, returns Array of Array
114
115
  - `db.sequentially(query, [params], function(row, index), function(err))` - sequentially query
115
116
  - `db.detach(function(err))` detach a database
116
- - `db.transaction(isolation, function(err, transaction))` create transaction
117
+ - `db.transaction(options, function(err, transaction))` create transaction
118
+
119
+ ### Transaction options
120
+
121
+ ```js
122
+ const options = {
123
+ autoCommit: false,
124
+ autoUndo: true,
125
+ isolation: Firebird.ISOLATION_READ_COMMITTED,
126
+ ignoreLimbo: false,
127
+ readOnly: false,
128
+ wait: true,
129
+ waitTimeout: 0,
130
+ };
131
+ ```
117
132
 
118
133
  ### Transaction methods
119
134
 
@@ -581,7 +596,7 @@ for Firebird 3.0 you need to add the following in firebird.conf according to Fir
581
596
 
582
597
  ```bash
583
598
  AuthServer = Srp, Legacy_Auth
584
- WireCrypt = Disabled
599
+ WireCrypt = Enabled
585
600
  UserManager = Legacy_UserManager
586
601
  ```
587
602
 
@@ -591,13 +606,27 @@ for Firebird 4.0 you need to add the following in firebird.conf according to Fir
591
606
 
592
607
  ```bash
593
608
  AuthServer = Srp256, Srp, Legacy_Auth
594
- WireCrypt = Disabled
609
+ WireCrypt = Enabled
595
610
  UserManager = Legacy_UserManager
596
611
  ```
597
612
 
598
613
  Please read also Authorization with Firebird 2.5 client library from Firebird 4 migration guide
599
614
  <https://ib-aid.com/download/docs/fb4migrationguide.html#_authorization_with_firebird_2_5_client_library_fbclient_dll>
600
615
 
616
+ Firebird 5 wire protocol is not supported yet so
617
+ for Firebird 5.0 you need to add the following in firebird.conf according to Firebird release notes
618
+ <https://firebirdsql.org/file/documentation/release_notes/html/en/4_0/rlsnotes40.html#rnfb40-config-srp256>
619
+
620
+ ```bash
621
+ AuthServer = Srp256, Srp, Legacy_Auth
622
+ WireCrypt = Enabled
623
+ UserManager = Legacy_UserManager
624
+ ```
625
+
626
+ Please read also Authorization with Firebird 2.5 client library from Firebird 5 migration guide
627
+ <https://ib-aid.com/download/docs/fb5migrationguide.html#_authorization_from_firebird_2_5_client_libraries>
628
+
629
+
601
630
  ## Contributors
602
631
 
603
632
  - Henri Gourvest, <https://github.com/hgourvest>
package/Roadmap.md ADDED
@@ -0,0 +1,80 @@
1
+ # Node-Firebird Roadmap
2
+
3
+ This document outlines the future development direction for the `node-firebird` library. Our primary goals are to modernize the codebase, implement support for the latest Firebird features, and improve the overall developer experience.
4
+
5
+ ## Protocol Implementation Status
6
+
7
+ The following table summarizes the current and planned implementation status of the Firebird wire protocol for each major version.
8
+
9
+ | Firebird Version | Protocol Versions | Status |
10
+ | :--- | :--- | :--- |
11
+ | 2.5 | 10, 11, 12, 13 | ✅ Implemented |
12
+ | 3.0 | 14, 15 | ❌ Not Implemented |
13
+ | 4.0 | 16, 17 | ❌ Not Implemented |
14
+ | 5.0 | N/A | ❌ Not Implemented |
15
+ | 6.0 | N/A | ❌ Not Implemented |
16
+
17
+ ## Firebird 3 Support
18
+
19
+ Firebird 3 introduced Protocol 13, which brought significant changes focusing on security and performance. While the base protocol is implemented, several key features are still missing. To fully support Firebird 3, we need to implement the following:
20
+
21
+ - **Protocol Versions 14 and 15:** Implement the newer wire protocol versions.
22
+ - **Enhanced Authentication:** Fully support the new authentication mechanisms and plugin architecture.
23
+ - **Wire Protocol Encryption:** Implement support for encrypting all network traffic.
24
+ - **Wire Protocol Compression:** Add support for data compression.
25
+ - **Database Encryption Callback:** Support the new callback mechanism for handling database encryption keys.
26
+ - **Packed (NULL-aware) Row Data:** Implement support for the optimized row format.
27
+ - **Performance Optimizations:**
28
+ - Implement support for the denser data stream and improved prefetch logic.
29
+ - Utilize the new bitmap for transmitting NULL flags to reduce network traffic.
30
+ - **UTF-8 User Identification:** Ensure all user identification is properly handled with UTF-8 encoding.
31
+
32
+ ## Firebird 4 Support
33
+
34
+ Firebird 4 introduced Protocol versions 16 and 17, continuing to build upon the foundation of Firebird 3. Key features to implement include:
35
+
36
+ - **Protocol Versions 16 and 17:** Implement the latest protocol versions to support Firebird 4 features.
37
+
38
+ ## Firebird 5 Support
39
+
40
+ Firebird 5 introduces a host of new SQL features and performance improvements that will require significant client-side implementation:
41
+
42
+ - **Bidirectional Cursors:** Implement support for scrollable cursors for remote database access.
43
+ - **`RETURNING` Multiple Rows:** Enhance DML operations to support returning multiple rows.
44
+ - **`SKIP LOCKED`:** Add support for the `SKIP LOCKED` clause in `SELECT WITH LOCK`, `UPDATE`, and `DELETE` statements.
45
+ - **New Data Types and Functions:** Add support for new built-in functions and packages.
46
+
47
+ ## Firebird 6 and Beyond
48
+
49
+ As Firebird 6 and future versions are released, we will continue to monitor new features and plan for their implementation. Key areas of interest include:
50
+
51
+ - **JSON Support:** Implement client-side support for the new SQL-compliant JSON functions.
52
+ - **Tablespaces:** Add support for tablespaces.
53
+ - **SQL Schemas:** Implement support for SQL schemas.
54
+
55
+ ## Codebase Refactoring
56
+
57
+ The current codebase is functional but could be significantly improved by adopting modern JavaScript and TypeScript features.
58
+
59
+ ### Modern JavaScript Classes
60
+
61
+ The existing codebase is written in a prototype-based style. We plan to refactor the entire library to use modern JavaScript classes (`class` syntax). This will improve readability, maintainability, and make the code easier to understand for new contributors.
62
+
63
+ **Benefits:**
64
+
65
+ - Improved code structure and organization.
66
+ - Easier to reason about inheritance and object-oriented patterns.
67
+ - Better alignment with modern JavaScript best practices.
68
+
69
+ ### TypeScript Rewrite
70
+
71
+ A full rewrite of the library in TypeScript is a long-term goal. This would bring the benefits of static typing, improved developer tooling, and a more robust codebase.
72
+
73
+ **Benefits:**
74
+
75
+ - **Type Safety:** Catch errors at compile-time instead of runtime.
76
+ - **Improved Autocomplete:** Better editor support and developer experience.
77
+ - **Self-documenting Code:** Types make the code easier to understand and use.
78
+ - **Easier Refactoring:** Static analysis makes it easier to refactor code with confidence.
79
+
80
+ We believe these changes will make `node-firebird` a more robust, modern, and developer-friendly library for accessing Firebird databases.
package/lib/index.d.ts CHANGED
@@ -31,9 +31,19 @@ declare module 'node-firebird' {
31
31
 
32
32
  export type Isolation = number[];
33
33
 
34
+ export type TransactionOptions = {
35
+ autoCommit?: boolean;
36
+ autoUndo?: boolean;
37
+ isolation?: Isolation;
38
+ ignoreLimbo?: boolean;
39
+ readOnly?: boolean;
40
+ wait?: boolean;
41
+ waitTimeout?: number;
42
+ };
43
+
34
44
  export interface Database {
35
45
  detach(callback?: SimpleCallback): Database;
36
- transaction(isolation: Isolation, callback: TransactionCallback): Database;
46
+ transaction(options: TransactionOptions|Isolation|TransactionCallback, callback?: TransactionCallback): Database;
37
47
  query(query: string, params: any[], callback: QueryCallback): Database;
38
48
  execute(query: string, params: any[], callback: QueryCallback): Database;
39
49
  sequentially(query: string, params: any[], rowCallback: SequentialCallback, callback: SimpleCallback, asArray?: boolean): Database;
@@ -1,5 +1,4 @@
1
1
  const Events = require('events');
2
- const net = require('net');
3
2
  const os = require('os');
4
3
  const path = require('path');
5
4
  const BigInt = require('big-integer');
@@ -15,6 +14,7 @@ const Database = require('./database');
15
14
  const Statement = require('./statement');
16
15
  const Transaction = require('./transaction');
17
16
  const {lookupMessages, noop, parseDate} = require('../utils');
17
+ const Socket = require("./socket");
18
18
 
19
19
  /***************************************
20
20
  *
@@ -32,13 +32,14 @@ var Connection = function (host, port, callback, options, db, svc) {
32
32
  this._detachTimeout;
33
33
  this._detachCallback;
34
34
  this._detachAuto;
35
- this._socket = net.createConnection(port, host);
35
+ this._socket = new Socket(port, host);
36
36
  this._pending = [];
37
37
  this._isOpened = false;
38
38
  this._isClosed = false;
39
39
  this._isDetach = false;
40
40
  this._isUsed = false;
41
41
  this._pooled = options.isPool||false;
42
+ if (options && options.blobChunkSize > 65535) options.blobChunkSize = 65535;
42
43
  this.options = options;
43
44
  this._bind_events(host, port, callback);
44
45
  this.error;
@@ -368,12 +369,14 @@ function decodeResponse(data, callback, cnx, lowercase_keys, cb) {
368
369
  protocolVersion: data.readInt(),
369
370
  protocolArchitecture: data.readInt(),
370
371
  protocolMinimumType: data.readInt(),
372
+ compress: false,
371
373
  pluginName: '',
372
374
  authData: '',
373
375
  sessionKey: ''
374
376
  };
375
377
 
376
- accept.protocolMinimumType = accept.protocolMinimumType & 0xFF;
378
+ accept.compress = (accept.protocolMinimumType & Const.pflag_compress) !== 0;
379
+ accept.protocolMinimumType = accept.protocolMinimumType & Const.ptype_mask;
377
380
  //accept.compress = (accept.acceptType & pflag_compress) !== 0; // TODO Handle zlib compression
378
381
  if (accept.protocolVersion < 0) {
379
382
  accept.protocolVersion = (accept.protocolVersion & Const.FB_PROTOCOL_MASK) | Const.FB_PROTOCOL_FLAG;
@@ -450,6 +453,10 @@ function decodeResponse(data, callback, cnx, lowercase_keys, cb) {
450
453
  }
451
454
  }
452
455
 
456
+ if (accept.compress) {
457
+ cnx._socket.enableCompression();
458
+ }
459
+
453
460
  return cb(undefined, accept);
454
461
  case Const.op_cont_auth:
455
462
  var d = new BlrReader(data.readArray());
@@ -577,7 +584,7 @@ Connection.prototype.sendOpContAuth = function(authData, authDataEnc, pluginName
577
584
  this._socket.write(msg.getData());
578
585
  }
579
586
 
580
- Connection.prototype._queueEvent = function(callback){
587
+ Connection.prototype._queueEvent = function(callback, defer = false){
581
588
  var self = this;
582
589
 
583
590
  if (self._isClosed) {
@@ -586,8 +593,14 @@ Connection.prototype._queueEvent = function(callback){
586
593
  return;
587
594
  }
588
595
 
589
- self._queue.push(callback);
590
- self._socket.write(self._msg.getData());
596
+ const canDefer = defer && this.accept.protocolVersion >= Const.PROTOCOL_VERSION11;
597
+
598
+ self._socket.write(self._msg.getData(), canDefer);
599
+ if (canDefer && callback) {
600
+ callback();
601
+ } else {
602
+ self._queue.push(callback);
603
+ }
591
604
  };
592
605
 
593
606
  Connection.prototype.connect = function (options, callback) {
@@ -633,7 +646,11 @@ Connection.prototype.connect = function (options, callback) {
633
646
  msg.addInt(protocol[0]); // Version
634
647
  msg.addInt(protocol[1]); // Architecture
635
648
  msg.addInt(protocol[2]); // Min type
636
- msg.addInt(protocol[3]); // Max type
649
+ if (protocol[0] >= Const.PROTOCOL_VERSION13 && options.wireCompression) {
650
+ msg.addInt(protocol[3] | Const.pflag_compress); // Max type with compress flag
651
+ } else {
652
+ msg.addInt(protocol[3]); // Max type
653
+ }
637
654
  msg.addInt(protocol[4]); // Preference weight
638
655
  }
639
656
 
@@ -845,14 +862,34 @@ Connection.prototype.throwClosed = function(callback) {
845
862
  return this;
846
863
  };
847
864
 
848
- Connection.prototype.startTransaction = function(isolation, callback) {
865
+ Connection.prototype.startTransaction = function(options, callback) {
849
866
 
850
- if (typeof(isolation) === 'function') {
851
- var tmp = isolation;
852
- isolation = callback;
867
+ if (typeof(options) === 'function') {
868
+ var tmp = options;
869
+ options = callback;
853
870
  callback = tmp;
854
871
  }
855
872
 
873
+ // Compatibility
874
+ if (Array.isArray(options)) {
875
+ options = {
876
+ isolation: options,
877
+ readOnly: (options === Const.ISOLATION_READ_COMMITTED_READ_ONLY),
878
+ };
879
+ }
880
+
881
+ // Default options
882
+ options = Object.assign({
883
+ autoCommit: false,
884
+ autoUndo: true,
885
+ isolation: Const.ISOLATION_READ_COMMITTED,
886
+ ignoreLimbo: false,
887
+ //lock: [],
888
+ readOnly: false,
889
+ wait: true,
890
+ waitTimeout: 0,
891
+ }, options);
892
+
856
893
  if (this._isClosed)
857
894
  return this.throwClosed(callback);
858
895
 
@@ -865,13 +902,44 @@ Connection.prototype.startTransaction = function(isolation, callback) {
865
902
  blr.pos = 0;
866
903
  msg.pos = 0;
867
904
 
868
- blr.addBytes(isolation || Const.ISOLATION_REPEATABLE_READ);
905
+ blr.addByte(Const.isc_tpb_version3);
906
+ blr.addBytes(options.isolation);
907
+ blr.addByte(options.readOnly ? Const.isc_tpb_read : Const.isc_tpb_write);
908
+ if (options.wait) {
909
+ blr.addByte(Const.isc_tpb_wait);
910
+
911
+ if (options.waitTimeout) {
912
+ blr.addNumeric(Const.isc_tpb_lock_timeout, options.waitTimeout);
913
+ }
914
+ } else {
915
+ blr.addByte(Const.isc_tpb_nowait);
916
+ }
917
+ if (!options.autoUndo) {
918
+ blr.addByte(Const.isc_tpb_no_auto_undo);
919
+ }
920
+ if (options.autoCommit) {
921
+ blr.addByte(Const.isc_tpb_autocommit);
922
+ }
923
+ if (options.ignoreLimbo) {
924
+ blr.addByte(Const.isc_tpb_ignore_limbo);
925
+ }
926
+ // TODO
927
+ /*if (options.lock.length) {
928
+ for (let table of options.lock) {
929
+ const lockMode = table.write ? Const.isc_tpb_lock_write : Const.isc_tpb_lock_read;
930
+ const lockType = table.protected ? Const.isc_tpb_protected : Const.isc_tpb_shared;
931
+
932
+ blr.addString(lockMode, table.table || table, Const.DEFAULT_ENCODING);
933
+ blr.addByte(lockType);
934
+ }
935
+ }*/
936
+
869
937
  msg.addInt(Const.op_transaction);
870
938
  msg.addInt(this.dbhandle);
871
939
  msg.addBlr(blr);
872
940
  callback.response = new Transaction(this);
873
941
 
874
- this.db.emit('transaction', isolation);
942
+ this.db.emit('transaction', options);
875
943
  this._queueEvent(callback);
876
944
  };
877
945
 
@@ -966,7 +1034,8 @@ Connection.prototype.dropStatement = function (statement, callback) {
966
1034
  msg.addInt(Const.op_free_statement);
967
1035
  msg.addInt(statement.handle);
968
1036
  msg.addInt(Const.DSQL_drop);
969
- this._queueEvent(callback);
1037
+
1038
+ this._queueEvent(callback, true);
970
1039
  };
971
1040
 
972
1041
  Connection.prototype.closeStatement = function (statement, callback) {
@@ -983,7 +1052,7 @@ Connection.prototype.closeStatement = function (statement, callback) {
983
1052
  msg.addInt(statement.handle);
984
1053
  msg.addInt(Const.DSQL_close);
985
1054
 
986
- this._queueEvent(callback);
1055
+ this._queueEvent(callback, true);
987
1056
  };
988
1057
 
989
1058
  Connection.prototype.allocateAndPrepareStatement = function (transaction, query, plan, callback) {
@@ -1259,8 +1328,11 @@ Connection.prototype.executeStatement = function(transaction, statement, params,
1259
1328
  else if (!isStream)
1260
1329
  b = Buffer.from(JSON.stringify(value), Const.DEFAULT_ENCODING);
1261
1330
 
1331
+ // Use configured transfer size or default to 1024
1332
+ var chunkSize = self.options.blobChunkSize || 1024;
1333
+
1262
1334
  if (Buffer.isBuffer(b)) {
1263
- bufferReader(b, 1024, function(b, next) {
1335
+ bufferReader(b, chunkSize, function (b, next) {
1264
1336
  self.batchSegments(blob, b, next);
1265
1337
  }, function() {
1266
1338
  ret[index] = new Xsql.SQLParamQuad(blob.oid);
@@ -1273,9 +1345,20 @@ Connection.prototype.executeStatement = function(transaction, statement, params,
1273
1345
  var isEnd = false;
1274
1346
 
1275
1347
  value.on('data', function(chunk) {
1348
+ // Optimization: If chunk is smaller than transfer size, send directly
1349
+ if (chunk.length <= chunkSize) {
1350
+ self.batchSegments(blob, chunk, function () {
1351
+ if (isEnd && !isReading) {
1352
+ ret[index] = new Xsql.SQLParamQuad(blob.oid);
1353
+ self.closeBlob(blob, callback);
1354
+ }
1355
+ });
1356
+ return;
1357
+ }
1358
+
1276
1359
  value.pause();
1277
1360
  isReading = true;
1278
- bufferReader(chunk, 1024, function(b, next) {
1361
+ bufferReader(chunk, chunkSize, function (b, next) {
1279
1362
  self.batchSegments(blob, b, next);
1280
1363
  }, function() {
1281
1364
  isReading = false;
@@ -1292,8 +1375,12 @@ Connection.prototype.executeStatement = function(transaction, statement, params,
1292
1375
  isEnd = true;
1293
1376
  if (isReading)
1294
1377
  return;
1295
- ret[index] = new Xsql.SQLParamQuad(blob.oid);
1296
- self.closeBlob(blob, callback);
1378
+ // If we are not currently reading (paused), close immediately
1379
+ // If we are reading, the callback in batchSegments/bufferReader will handle closure
1380
+ if (!isReading) {
1381
+ ret[index] = new Xsql.SQLParamQuad(blob.oid);
1382
+ self.closeBlob(blob, callback);
1383
+ }
1297
1384
  });
1298
1385
  });
1299
1386
  }
@@ -1717,7 +1804,7 @@ Connection.prototype.closeBlob = function(blob, callback) {
1717
1804
  msg.pos = 0;
1718
1805
  msg.addInt(Const.op_close_blob);
1719
1806
  msg.addInt(blob.handle);
1720
- this._queueEvent(callback);
1807
+ this._queueEvent(callback, true);
1721
1808
  };
1722
1809
 
1723
1810
  Connection.prototype.getSegment = function(blob, callback) {
package/lib/wire/const.js CHANGED
@@ -435,11 +435,11 @@ const tpb = {
435
435
  };
436
436
 
437
437
  const transactionIsolation = {
438
- ISOLATION_READ_UNCOMMITTED : [tpb.isc_tpb_version3, tpb.isc_tpb_write, tpb.isc_tpb_wait, tpb.isc_tpb_read_committed, tpb.isc_tpb_rec_version],
439
- ISOLATION_READ_COMMITTED : [tpb.isc_tpb_version3, tpb.isc_tpb_write, tpb.isc_tpb_wait, tpb.isc_tpb_read_committed, tpb.isc_tpb_no_rec_version],
440
- ISOLATION_REPEATABLE_READ : [tpb.isc_tpb_version3, tpb.isc_tpb_write, tpb.isc_tpb_wait, tpb.isc_tpb_concurrency],
441
- ISOLATION_SERIALIZABLE : [tpb.isc_tpb_version3, tpb.isc_tpb_write, tpb.isc_tpb_wait, tpb.isc_tpb_consistency],
442
- ISOLATION_READ_COMMITTED_READ_ONLY : [tpb.isc_tpb_version3, tpb.isc_tpb_read, tpb.isc_tpb_wait, tpb.isc_tpb_read_committed, tpb.isc_tpb_no_rec_version],
438
+ ISOLATION_READ_UNCOMMITTED : [tpb.isc_tpb_read_committed, tpb.isc_tpb_rec_version],
439
+ ISOLATION_READ_COMMITTED : [tpb.isc_tpb_read_committed, tpb.isc_tpb_no_rec_version],
440
+ ISOLATION_REPEATABLE_READ : [tpb.isc_tpb_concurrency],
441
+ ISOLATION_SERIALIZABLE : [tpb.isc_tpb_consistency],
442
+ ISOLATION_READ_COMMITTED_READ_ONLY : [tpb.isc_tpb_read_committed, tpb.isc_tpb_no_rec_version],
443
443
  };
444
444
 
445
445
  /*************************/
@@ -56,12 +56,12 @@ Database.prototype.detach = function(callback, force) {
56
56
  return self;
57
57
  };
58
58
 
59
- Database.prototype.transaction = function(isolation, callback) {
60
- return this.startTransaction(isolation, callback);
59
+ Database.prototype.transaction = function(options, callback) {
60
+ return this.startTransaction(options, callback);
61
61
  };
62
62
 
63
- Database.prototype.startTransaction = function(isolation, callback) {
64
- this.connection.startTransaction(isolation, callback);
63
+ Database.prototype.startTransaction = function(options, callback) {
64
+ this.connection.startTransaction(options, callback);
65
65
  return this;
66
66
  };
67
67
 
@@ -102,7 +102,7 @@ BlrWriter.prototype.addString = function (c, s, encoding) {
102
102
  };
103
103
 
104
104
  BlrWriter.prototype.addBuffer = function (b) {
105
- this.addSmall(b.length);
105
+ this.addWord(b.length);
106
106
  this.ensure(b.length);
107
107
  b.copy(this.buffer, this.pos);
108
108
  this.pos += b.length;
@@ -0,0 +1,100 @@
1
+ const net = require("net");
2
+ const zlib = require("zlib");
3
+
4
+ /**
5
+ * Socket proxy.
6
+ */
7
+ class Socket {
8
+ constructor(port, host) {
9
+ this._socket = net.createConnection(port, host);
10
+ this._socket.setNoDelay(true);
11
+ this.compressor = null;
12
+ this.compressorBuffer = [];
13
+ this.decompressor = null;
14
+ this.decompressorBuffer = [];
15
+ this.buffer = null;
16
+
17
+ return new Proxy(this._socket, this);
18
+ }
19
+
20
+ /**
21
+ * Decompress data when receive it if compression is enabled.
22
+ * Override on data event.
23
+ */
24
+ on(event, cb) {
25
+ if (event === 'data') {
26
+ const mainCb = cb;
27
+ cb = (data) => {
28
+ if (this.compress) {
29
+ this.decompressor.write(data, () => {
30
+ mainCb(Buffer.concat(this.decompressorBuffer));
31
+ this.decompressorBuffer = []; // Reset buffer
32
+ });
33
+ } else {
34
+ mainCb(data);
35
+ }
36
+ };
37
+ }
38
+
39
+ this._socket.on(event, cb);
40
+ }
41
+
42
+ /**
43
+ * Compress data before sending to socket if compression is enabled.
44
+ */
45
+ write(data, defer = false) {
46
+ if (defer) {
47
+ this.buffer = Buffer.from(data);
48
+ return;
49
+ }
50
+
51
+ if (!defer && this.buffer) {
52
+ data = Buffer.concat([this.buffer, data]);
53
+ this.buffer = null;
54
+ }
55
+
56
+ if (this.compress) {
57
+ this.compressor.write(data, () => {
58
+ this._socket.write(Buffer.concat(this.compressorBuffer));
59
+ this.compressorBuffer = []; // Reset buffer
60
+ });
61
+ } else {
62
+ this._socket.write(data);
63
+ }
64
+ }
65
+
66
+ /**
67
+ * Enable compression/decompression on the fly.
68
+ */
69
+ enableCompression() {
70
+ this.compress = true;
71
+
72
+ // Create decompressor instance
73
+ this.decompressor = zlib.createInflate();
74
+ this.decompressor.on('data', (inflate) => {
75
+ this.decompressorBuffer.push(inflate);
76
+ });
77
+
78
+ // Create compressor instance
79
+ this.compressor = zlib.createDeflate({
80
+ flush: zlib.constants.Z_FULL_FLUSH,
81
+ finishFlush: zlib.constants.Z_SYNC_FLUSH
82
+ });
83
+ this.compressor.on('data', (deflate) => {
84
+ this.compressorBuffer.push(deflate);
85
+ });
86
+ }
87
+
88
+ /**
89
+ * Proxy trap.
90
+ */
91
+ get(target, field) {
92
+ if (field in this) {
93
+ return this[field].bind(this);
94
+ }
95
+
96
+ return target[field];
97
+ }
98
+ }
99
+
100
+ module.exports = Socket;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-firebird",
3
- "version": "1.1.9",
3
+ "version": "1.1.10",
4
4
  "description": "Pure JavaScript and Asynchronous Firebird client for Node.js.",
5
5
  "keywords": [
6
6
  "firebird",
@@ -11,7 +11,7 @@
11
11
  "homepage": "https://github.com/hgourvest/node-firebird",
12
12
  "repository": {
13
13
  "type": "git",
14
- "url": "https://github.com/hgourvest/node-firebird"
14
+ "url": "git+https://github.com/hgourvest/node-firebird.git"
15
15
  },
16
16
  "author": {
17
17
  "name": "Henri Gourvest",
package/.travis.yml DELETED
@@ -1,23 +0,0 @@
1
- language: node_js
2
-
3
- node_js:
4
- - lts/*
5
- - v10
6
-
7
- notifications:
8
- email:
9
- - mapopa@gmail.com
10
-
11
- env:
12
- - FIREBIRD_DATA="/var/lib/firebird/2.5/data"
13
-
14
- before_install:
15
- - sudo apt-get update -qq
16
- - sudo DEBIAN_FRONTEND=noninteractive apt-get install -qq firebird2.5-superclassic
17
- - sudo gsec -modify SYSDBA -pw masterkey
18
-
19
- script:
20
- - nyc npm test
21
-
22
- after_script:
23
- - nyc report --reporter=text-lcov | coveralls