node-firebird 2.15.1 → 2.16.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.
- package/README.md +16 -1
- package/lib/types.d.ts +6 -0
- package/lib/uri.d.ts +9 -5
- package/lib/uri.js +40 -9
- package/lib/wire/connection.js +1 -1
- package/lib/wire/socket.d.ts +1 -1
- package/lib/wire/socket.js +3 -2
- package/package.json +1 -1
- package/src/types.ts +6 -0
- package/src/uri.ts +43 -9
- package/src/wire/connection.ts +2 -1
- package/src/wire/socket.ts +3 -2
package/README.md
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
- [Installation](#installation)
|
|
12
12
|
- [Usage](#usage) — including [developing the driver](#developing-the-driver)
|
|
13
13
|
- [Promises and async/await](#promises-and-asyncawait) — the `*Async` API plus `withConnection` / `withTransaction` helpers
|
|
14
|
-
- [Connection types](#connection-types) — connection options, `firebird://` URIs and traditional connection strings, classic connections, pooling
|
|
14
|
+
- [Connection types](#connection-types) — connection options, `firebird://` / `inet://` URIs and traditional connection strings, classic connections, pooling
|
|
15
15
|
- [Database object (db)](#database-object-db) — database, transaction and statement methods/options
|
|
16
16
|
- [Examples](#examples) — parametrized queries, tagged-template queries (sql), named placeholders, nested result tables (nestTables), row-key transforms (transformKeys), result metadata / affected rows (withMeta), custom type parsers (typeCast), BLOBs, streaming big data, transactions, driver events, database events (POST_EVENT), service manager, charsets/encoding, Firebird 3.0–6.0 features
|
|
17
17
|
- [Extensive Examples](#extensive-examples) — DECFLOAT/INT128, query cancellation (AbortSignal), batch execution (bulk inserts incl. BLOBs), bulk-insert stream (batchStream), statement timeouts, scrollable cursors, RETURNING multiple rows, SKIP LOCKED, advanced pooling
|
|
@@ -200,6 +200,7 @@ options.dbCryptConfig = undefined; // optional; database encryption key for encr
|
|
|
200
200
|
options.connectTimeout = 10000; // optional; timeout in ms for a single pool.get() attach operation (default: no timeout)
|
|
201
201
|
options.enableKeepAlive = true; // TCP keepalive probing to detect dead/stale connections (same option names as mysql2); set to false to disable
|
|
202
202
|
options.keepAliveInitialDelay = 60000; // ms a socket must be idle before the first keepalive probe (ignored when enableKeepAlive is false)
|
|
203
|
+
options.ipFamily = 4; // optional; force IPv4 (4) or IPv6 (6) when resolving host — also set by inet4:// / inet6:// URIs
|
|
203
204
|
options.parallelWorkers = undefined; // optional; request multiple thread workers for maintenance/index tasks (FB >= 5)
|
|
204
205
|
options.maxInlineBlobSize = undefined; // optional; threshold size in bytes for inline blob transmission (default 65535, FB >= 5.0.3)
|
|
205
206
|
options.maxNegotiatedProtocols = undefined; // optional; cap how many protocol versions are offered, oldest first (default: all, up to Protocol 20; set to 10 to stop at Protocol 19)
|
|
@@ -248,6 +249,20 @@ be passed as query parameters. IPv6 hosts use brackets:
|
|
|
248
249
|
`firebird://[::1]:3050/employee`. The parser is exported as
|
|
249
250
|
`Firebird.parseConnectionUri(uri)` if you need the resulting options object.
|
|
250
251
|
|
|
252
|
+
Firebird's own URL-style connection strings (Firebird 3+) are accepted with
|
|
253
|
+
the same rules, so a string that works with isql works here unchanged:
|
|
254
|
+
|
|
255
|
+
| URI | meaning |
|
|
256
|
+
| :--- | :--- |
|
|
257
|
+
| `inet://host/employee` | same as `firebird://host/employee` |
|
|
258
|
+
| `inet://host:3051//var/fb/prod.fdb` | host, port and an absolute path |
|
|
259
|
+
| `inet4://host/employee` | resolve `host` as IPv4 only (sets `ipFamily: 4`) |
|
|
260
|
+
| `inet6://[::1]/employee` | IPv6 only (sets `ipFamily: 6`) |
|
|
261
|
+
|
|
262
|
+
`inet://` strings may carry credentials and query options exactly like
|
|
263
|
+
`firebird://` ones. The local-only transports `xnet://` and `wnet://` are
|
|
264
|
+
rejected — this driver speaks TCP only.
|
|
265
|
+
|
|
251
266
|
### Traditional connection strings (old style)
|
|
252
267
|
|
|
253
268
|
The classic Firebird connection string format — the same
|
package/lib/types.d.ts
CHANGED
|
@@ -432,6 +432,12 @@ export interface Options {
|
|
|
432
432
|
* probe is sent (default 60000). Ignored when enableKeepAlive is false.
|
|
433
433
|
*/
|
|
434
434
|
keepAliveInitialDelay?: number;
|
|
435
|
+
/**
|
|
436
|
+
* Force the TCP socket to IPv4 (4) or IPv6 (6) when resolving `host`.
|
|
437
|
+
* Unset lets the resolver choose. Set automatically by `inet4://` /
|
|
438
|
+
* `inet6://` connection URIs.
|
|
439
|
+
*/
|
|
440
|
+
ipFamily?: 4 | 6;
|
|
435
441
|
pluginName?: string;
|
|
436
442
|
parallelWorkers?: number;
|
|
437
443
|
maxInlineBlobSize?: number;
|
package/lib/uri.d.ts
CHANGED
|
@@ -5,9 +5,11 @@
|
|
|
5
5
|
***************************************/
|
|
6
6
|
import type { Options } from './types';
|
|
7
7
|
/**
|
|
8
|
-
* Parse a firebird:// connection URI into an options object.
|
|
8
|
+
* Parse a firebird:// or inet:// connection URI into an options object.
|
|
9
9
|
*
|
|
10
10
|
* firebird://user:password@host:port/database?option=value&...
|
|
11
|
+
* inet://host:port/database (Firebird's own URL style;
|
|
12
|
+
* inet4://... / inet6://... also force IPv4 / IPv6)
|
|
11
13
|
*
|
|
12
14
|
* The database part:
|
|
13
15
|
* firebird://host/employee → alias "employee"
|
|
@@ -45,13 +47,15 @@ export declare function parseConnectionUri(uri: string): Options;
|
|
|
45
47
|
*/
|
|
46
48
|
export declare function parseOldStyleConnectionString(str: string): Options;
|
|
47
49
|
/**
|
|
48
|
-
* Parse any connection string the driver accepts: a firebird://
|
|
49
|
-
* traditional [host[/port]:]database string when
|
|
50
|
+
* Parse any connection string the driver accepts: a firebird:// or
|
|
51
|
+
* inet[4|6]:// URI, or a traditional [host[/port]:]database string when
|
|
52
|
+
* there is no scheme.
|
|
50
53
|
*/
|
|
51
54
|
export declare function parseConnectionString(str: string): Options;
|
|
52
55
|
/**
|
|
53
|
-
* Accept either an options object or a connection string (firebird://
|
|
54
|
-
* or traditional host[/port]:database) everywhere options are
|
|
56
|
+
* Accept either an options object or a connection string (firebird:// or
|
|
57
|
+
* inet:// URI, or traditional host[/port]:database) everywhere options are
|
|
58
|
+
* taken.
|
|
55
59
|
* Strings are parsed; objects pass through unchanged.
|
|
56
60
|
*/
|
|
57
61
|
export declare function normalizeOptions<T>(options: T | string): T;
|
package/lib/uri.js
CHANGED
|
@@ -22,7 +22,7 @@ const NUMBER_KEYS = new Set([
|
|
|
22
22
|
'port', 'pageSize', 'timeout', 'retryConnectionInterval',
|
|
23
23
|
'blobChunkSize', 'blobReadChunkSize', 'wireCrypt', 'parallelWorkers',
|
|
24
24
|
'maxInlineBlobSize', 'maxNegotiatedProtocols', 'connectTimeout',
|
|
25
|
-
'min', 'idleTimeoutMillis', 'keepAliveInitialDelay',
|
|
25
|
+
'min', 'idleTimeoutMillis', 'keepAliveInitialDelay', 'ipFamily',
|
|
26
26
|
]);
|
|
27
27
|
function coerce(key, value) {
|
|
28
28
|
if (BOOLEAN_KEYS.has(key)) {
|
|
@@ -38,9 +38,29 @@ function coerce(key, value) {
|
|
|
38
38
|
return value;
|
|
39
39
|
}
|
|
40
40
|
/**
|
|
41
|
-
*
|
|
41
|
+
* URI schemes accepted by parseConnectionUri. `firebird://` is this driver's
|
|
42
|
+
* own scheme; `inet://`, `inet4://` and `inet6://` are Firebird's native
|
|
43
|
+
* URL-style connection strings (Firebird 3+), so a string that works with
|
|
44
|
+
* isql works here too. inet4/inet6 pin the socket's IP family (the
|
|
45
|
+
* `ipFamily` option); inet and firebird let the resolver pick.
|
|
46
|
+
*/
|
|
47
|
+
const URI_SCHEMES = {
|
|
48
|
+
'firebird:': undefined,
|
|
49
|
+
'inet:': undefined,
|
|
50
|
+
'inet4:': 4,
|
|
51
|
+
'inet6:': 6,
|
|
52
|
+
};
|
|
53
|
+
/**
|
|
54
|
+
* Firebird protocols this driver cannot speak: they are local-machine IPC
|
|
55
|
+
* transports (shared memory / named pipes), not TCP.
|
|
56
|
+
*/
|
|
57
|
+
const LOCAL_SCHEMES = /^(xnet|wnet):$/;
|
|
58
|
+
/**
|
|
59
|
+
* Parse a firebird:// or inet:// connection URI into an options object.
|
|
42
60
|
*
|
|
43
61
|
* firebird://user:password@host:port/database?option=value&...
|
|
62
|
+
* inet://host:port/database (Firebird's own URL style;
|
|
63
|
+
* inet4://... / inet6://... also force IPv4 / IPv6)
|
|
44
64
|
*
|
|
45
65
|
* The database part:
|
|
46
66
|
* firebird://host/employee → alias "employee"
|
|
@@ -64,11 +84,20 @@ function parseConnectionUri(uri) {
|
|
|
64
84
|
catch (e) {
|
|
65
85
|
throw new Error('Invalid connection URI: ' + uri);
|
|
66
86
|
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
87
|
+
var scheme = url.protocol.toLowerCase();
|
|
88
|
+
if (LOCAL_SCHEMES.test(scheme)) {
|
|
89
|
+
throw new Error('Unsupported connection URI scheme "' + scheme.replace(/:$/, '') +
|
|
90
|
+
'" (local IPC transports are not available over the wire — use inet:// or firebird://)');
|
|
91
|
+
}
|
|
92
|
+
if (!Object.prototype.hasOwnProperty.call(URI_SCHEMES, scheme)) {
|
|
93
|
+
throw new Error('Unsupported connection URI scheme "' + scheme.replace(/:$/, '') +
|
|
94
|
+
'" (expected firebird://, inet://, inet4:// or inet6://)');
|
|
70
95
|
}
|
|
71
96
|
var options = {};
|
|
97
|
+
var ipFamily = URI_SCHEMES[scheme];
|
|
98
|
+
if (ipFamily !== undefined) {
|
|
99
|
+
options.ipFamily = ipFamily;
|
|
100
|
+
}
|
|
72
101
|
if (url.hostname) {
|
|
73
102
|
// URL keeps IPv6 hostnames bracketed ([::1]); net.connect wants them bare
|
|
74
103
|
options.host = url.hostname.replace(/^\[(.*)\]$/, '$1');
|
|
@@ -172,8 +201,9 @@ function parseOldStyleConnectionString(str) {
|
|
|
172
201
|
}
|
|
173
202
|
const URI_SCHEME = /^[A-Za-z][A-Za-z0-9+.-]*:\/\//;
|
|
174
203
|
/**
|
|
175
|
-
* Parse any connection string the driver accepts: a firebird://
|
|
176
|
-
* traditional [host[/port]:]database string when
|
|
204
|
+
* Parse any connection string the driver accepts: a firebird:// or
|
|
205
|
+
* inet[4|6]:// URI, or a traditional [host[/port]:]database string when
|
|
206
|
+
* there is no scheme.
|
|
177
207
|
*/
|
|
178
208
|
function parseConnectionString(str) {
|
|
179
209
|
return URI_SCHEME.test(str)
|
|
@@ -181,8 +211,9 @@ function parseConnectionString(str) {
|
|
|
181
211
|
: parseOldStyleConnectionString(str);
|
|
182
212
|
}
|
|
183
213
|
/**
|
|
184
|
-
* Accept either an options object or a connection string (firebird://
|
|
185
|
-
* or traditional host[/port]:database) everywhere options are
|
|
214
|
+
* Accept either an options object or a connection string (firebird:// or
|
|
215
|
+
* inet:// URI, or traditional host[/port]:database) everywhere options are
|
|
216
|
+
* taken.
|
|
186
217
|
* Strings are parsed; objects pass through unchanged.
|
|
187
218
|
*/
|
|
188
219
|
function normalizeOptions(options) {
|
package/lib/wire/connection.js
CHANGED
|
@@ -168,7 +168,7 @@ class Connection {
|
|
|
168
168
|
this._detachTimeout;
|
|
169
169
|
this._detachCallback;
|
|
170
170
|
this._detachAuto;
|
|
171
|
-
this._socket = new socket_1.default(port, host, options.enableKeepAlive !== false, options.keepAliveInitialDelay);
|
|
171
|
+
this._socket = new socket_1.default(port, host, options.enableKeepAlive !== false, options.keepAliveInitialDelay, options.ipFamily);
|
|
172
172
|
this._pending = [];
|
|
173
173
|
this._isOpened = false;
|
|
174
174
|
this._isClosed = false;
|
package/lib/wire/socket.d.ts
CHANGED
|
@@ -32,7 +32,7 @@ declare class Socket {
|
|
|
32
32
|
encrypt: boolean;
|
|
33
33
|
encryptCipher: any;
|
|
34
34
|
decryptCipher: any;
|
|
35
|
-
constructor(port: number, host: string, enableKeepAlive?: boolean, keepAliveInitialDelay?: number);
|
|
35
|
+
constructor(port: number, host: string, enableKeepAlive?: boolean, keepAliveInitialDelay?: number, ipFamily?: 4 | 6);
|
|
36
36
|
/**
|
|
37
37
|
* Decompress and/or decrypt data when received.
|
|
38
38
|
* Override on data event.
|
package/lib/wire/socket.js
CHANGED
|
@@ -82,9 +82,10 @@ class ChaChaCipher {
|
|
|
82
82
|
*/
|
|
83
83
|
class Socket {
|
|
84
84
|
static { this.Arc4 = Arc4; }
|
|
85
|
-
constructor(port, host, enableKeepAlive = true, keepAliveInitialDelay = 60000) {
|
|
85
|
+
constructor(port, host, enableKeepAlive = true, keepAliveInitialDelay = 60000, ipFamily) {
|
|
86
86
|
this.compress = false;
|
|
87
|
-
|
|
87
|
+
// family: 0 is "either" (Node's default); 4/6 pin the resolver
|
|
88
|
+
this._socket = net_1.default.createConnection({ port: port, host: host, family: ipFamily || 0 });
|
|
88
89
|
this._socket.setNoDelay(true);
|
|
89
90
|
// TCP keepalive probing detects dead/stale connections; the delay is
|
|
90
91
|
// how long the socket must be idle before the first probe.
|
package/package.json
CHANGED
package/src/types.ts
CHANGED
|
@@ -493,6 +493,12 @@ export interface Options {
|
|
|
493
493
|
* probe is sent (default 60000). Ignored when enableKeepAlive is false.
|
|
494
494
|
*/
|
|
495
495
|
keepAliveInitialDelay?: number;
|
|
496
|
+
/**
|
|
497
|
+
* Force the TCP socket to IPv4 (4) or IPv6 (6) when resolving `host`.
|
|
498
|
+
* Unset lets the resolver choose. Set automatically by `inet4://` /
|
|
499
|
+
* `inet6://` connection URIs.
|
|
500
|
+
*/
|
|
501
|
+
ipFamily?: 4 | 6;
|
|
496
502
|
pluginName?: string;
|
|
497
503
|
parallelWorkers?: number;
|
|
498
504
|
maxInlineBlobSize?: number;
|
package/src/uri.ts
CHANGED
|
@@ -20,7 +20,7 @@ const NUMBER_KEYS = new Set([
|
|
|
20
20
|
'port', 'pageSize', 'timeout', 'retryConnectionInterval',
|
|
21
21
|
'blobChunkSize', 'blobReadChunkSize', 'wireCrypt', 'parallelWorkers',
|
|
22
22
|
'maxInlineBlobSize', 'maxNegotiatedProtocols', 'connectTimeout',
|
|
23
|
-
'min', 'idleTimeoutMillis', 'keepAliveInitialDelay',
|
|
23
|
+
'min', 'idleTimeoutMillis', 'keepAliveInitialDelay', 'ipFamily',
|
|
24
24
|
]);
|
|
25
25
|
|
|
26
26
|
function coerce(key: string, value: string): any {
|
|
@@ -38,9 +38,31 @@ function coerce(key: string, value: string): any {
|
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
/**
|
|
41
|
-
*
|
|
41
|
+
* URI schemes accepted by parseConnectionUri. `firebird://` is this driver's
|
|
42
|
+
* own scheme; `inet://`, `inet4://` and `inet6://` are Firebird's native
|
|
43
|
+
* URL-style connection strings (Firebird 3+), so a string that works with
|
|
44
|
+
* isql works here too. inet4/inet6 pin the socket's IP family (the
|
|
45
|
+
* `ipFamily` option); inet and firebird let the resolver pick.
|
|
46
|
+
*/
|
|
47
|
+
const URI_SCHEMES: Record<string, 4 | 6 | undefined> = {
|
|
48
|
+
'firebird:': undefined,
|
|
49
|
+
'inet:': undefined,
|
|
50
|
+
'inet4:': 4,
|
|
51
|
+
'inet6:': 6,
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Firebird protocols this driver cannot speak: they are local-machine IPC
|
|
56
|
+
* transports (shared memory / named pipes), not TCP.
|
|
57
|
+
*/
|
|
58
|
+
const LOCAL_SCHEMES = /^(xnet|wnet):$/;
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Parse a firebird:// or inet:// connection URI into an options object.
|
|
42
62
|
*
|
|
43
63
|
* firebird://user:password@host:port/database?option=value&...
|
|
64
|
+
* inet://host:port/database (Firebird's own URL style;
|
|
65
|
+
* inet4://... / inet6://... also force IPv4 / IPv6)
|
|
44
66
|
*
|
|
45
67
|
* The database part:
|
|
46
68
|
* firebird://host/employee → alias "employee"
|
|
@@ -64,13 +86,23 @@ export function parseConnectionUri(uri: string): Options {
|
|
|
64
86
|
throw new Error('Invalid connection URI: ' + uri);
|
|
65
87
|
}
|
|
66
88
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
89
|
+
var scheme = url.protocol.toLowerCase();
|
|
90
|
+
if (LOCAL_SCHEMES.test(scheme)) {
|
|
91
|
+
throw new Error('Unsupported connection URI scheme "' + scheme.replace(/:$/, '') +
|
|
92
|
+
'" (local IPC transports are not available over the wire — use inet:// or firebird://)');
|
|
93
|
+
}
|
|
94
|
+
if (!Object.prototype.hasOwnProperty.call(URI_SCHEMES, scheme)) {
|
|
95
|
+
throw new Error('Unsupported connection URI scheme "' + scheme.replace(/:$/, '') +
|
|
96
|
+
'" (expected firebird://, inet://, inet4:// or inet6://)');
|
|
70
97
|
}
|
|
71
98
|
|
|
72
99
|
var options: any = {};
|
|
73
100
|
|
|
101
|
+
var ipFamily = URI_SCHEMES[scheme];
|
|
102
|
+
if (ipFamily !== undefined) {
|
|
103
|
+
options.ipFamily = ipFamily;
|
|
104
|
+
}
|
|
105
|
+
|
|
74
106
|
if (url.hostname) {
|
|
75
107
|
// URL keeps IPv6 hostnames bracketed ([::1]); net.connect wants them bare
|
|
76
108
|
options.host = url.hostname.replace(/^\[(.*)\]$/, '$1');
|
|
@@ -182,8 +214,9 @@ export function parseOldStyleConnectionString(str: string): Options {
|
|
|
182
214
|
const URI_SCHEME = /^[A-Za-z][A-Za-z0-9+.-]*:\/\//;
|
|
183
215
|
|
|
184
216
|
/**
|
|
185
|
-
* Parse any connection string the driver accepts: a firebird://
|
|
186
|
-
* traditional [host[/port]:]database string when
|
|
217
|
+
* Parse any connection string the driver accepts: a firebird:// or
|
|
218
|
+
* inet[4|6]:// URI, or a traditional [host[/port]:]database string when
|
|
219
|
+
* there is no scheme.
|
|
187
220
|
*/
|
|
188
221
|
export function parseConnectionString(str: string): Options {
|
|
189
222
|
return URI_SCHEME.test(str)
|
|
@@ -192,8 +225,9 @@ export function parseConnectionString(str: string): Options {
|
|
|
192
225
|
}
|
|
193
226
|
|
|
194
227
|
/**
|
|
195
|
-
* Accept either an options object or a connection string (firebird://
|
|
196
|
-
* or traditional host[/port]:database) everywhere options are
|
|
228
|
+
* Accept either an options object or a connection string (firebird:// or
|
|
229
|
+
* inet:// URI, or traditional host[/port]:database) everywhere options are
|
|
230
|
+
* taken.
|
|
197
231
|
* Strings are parsed; objects pass through unchanged.
|
|
198
232
|
*/
|
|
199
233
|
export function normalizeOptions<T>(options: T | string): T {
|
package/src/wire/connection.ts
CHANGED
|
@@ -186,7 +186,8 @@ class Connection {
|
|
|
186
186
|
this._detachAuto;
|
|
187
187
|
this._socket = new Socket(port, host,
|
|
188
188
|
options.enableKeepAlive !== false,
|
|
189
|
-
options.keepAliveInitialDelay
|
|
189
|
+
options.keepAliveInitialDelay,
|
|
190
|
+
options.ipFamily);
|
|
190
191
|
this._pending = [];
|
|
191
192
|
this._isOpened = false;
|
|
192
193
|
this._isClosed = false;
|
package/src/wire/socket.ts
CHANGED
|
@@ -109,8 +109,9 @@ class Socket {
|
|
|
109
109
|
encryptCipher: any;
|
|
110
110
|
decryptCipher: any;
|
|
111
111
|
|
|
112
|
-
constructor(port: number, host: string, enableKeepAlive = true, keepAliveInitialDelay = 60000) {
|
|
113
|
-
|
|
112
|
+
constructor(port: number, host: string, enableKeepAlive = true, keepAliveInitialDelay = 60000, ipFamily?: 4 | 6) {
|
|
113
|
+
// family: 0 is "either" (Node's default); 4/6 pin the resolver
|
|
114
|
+
this._socket = net.createConnection({ port: port, host: host, family: ipFamily || 0 });
|
|
114
115
|
this._socket.setNoDelay(true);
|
|
115
116
|
// TCP keepalive probing detects dead/stale connections; the delay is
|
|
116
117
|
// how long the socket must be idle before the first probe.
|