node-firebird 2.9.0 → 2.10.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 +166 -6
- package/lib/index.d.ts +5 -0
- package/lib/index.js +32 -1
- package/lib/pool.js +1 -1
- package/lib/srp.d.ts +3 -3
- package/lib/types.d.ts +143 -5
- package/lib/uri.js +2 -2
- package/lib/wire/connection.d.ts +92 -59
- package/lib/wire/connection.js +267 -53
- package/lib/wire/const.d.ts +9 -1
- package/lib/wire/const.js +21 -9
- package/lib/wire/database.d.ts +51 -26
- package/lib/wire/database.js +26 -8
- package/lib/wire/eventConnection.js +5 -3
- package/lib/wire/query-stream.d.ts +18 -0
- package/lib/wire/query-stream.js +73 -0
- package/lib/wire/serialize.d.ts +18 -2
- package/lib/wire/serialize.js +7 -0
- package/lib/wire/service.d.ts +42 -0
- package/lib/wire/service.js +145 -0
- package/lib/wire/socket.d.ts +3 -1
- package/lib/wire/socket.js +5 -2
- package/lib/wire/statement.d.ts +31 -19
- package/lib/wire/statement.js +1 -5
- package/lib/wire/transaction.d.ts +30 -18
- package/lib/wire/transaction.js +23 -4
- package/lib/wire/wire-types.d.ts +116 -0
- package/lib/wire/wire-types.js +10 -0
- package/lib/wire/xsqlvar.d.ts +18 -18
- package/package.json +1 -1
- package/src/index.ts +36 -4
- package/src/messages.ts +1 -1
- package/src/pool.ts +1 -1
- package/src/srp.ts +6 -6
- package/src/types.ts +140 -5
- package/src/unix-crypt.ts +9 -9
- package/src/uri.ts +2 -2
- package/src/wire/connection.ts +464 -232
- package/src/wire/const.ts +21 -9
- package/src/wire/database.ts +75 -43
- package/src/wire/eventConnection.ts +8 -5
- package/src/wire/query-stream.ts +80 -0
- package/src/wire/serialize.ts +29 -0
- package/src/wire/service.ts +188 -6
- package/src/wire/socket.ts +17 -8
- package/src/wire/statement.ts +37 -29
- package/src/wire/transaction.ts +57 -32
- package/src/wire/wire-types.ts +127 -0
- package/src/wire/xsqlvar.ts +9 -7
package/README.md
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
- [Promises and async/await](#promises-and-asyncawait) — the `*Async` API plus `withConnection` / `withTransaction` helpers
|
|
14
14
|
- [Connection types](#connection-types) — connection options, `firebird://` URIs and traditional connection strings, classic connections, pooling
|
|
15
15
|
- [Database object (db)](#database-object-db) — database, transaction and statement methods/options
|
|
16
|
-
- [Examples](#examples) — parametrized queries, named placeholders, BLOBs, streaming big data, transactions, driver events, database events (POST_EVENT), service manager, charsets/encoding, Firebird 3.0–6.0 features
|
|
16
|
+
- [Examples](#examples) — parametrized queries, named placeholders, 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), statement timeouts, scrollable cursors, RETURNING multiple rows, SKIP LOCKED, advanced pooling
|
|
18
18
|
- [Using node-firebird with Express.js](#using-node-firebird-with-expressjs)
|
|
19
19
|
- [FAQ](#faq)
|
|
@@ -136,6 +136,7 @@ Available wrappers:
|
|
|
136
136
|
- **database** — `db.queryAsync(sql, params?, options?)`, `executeAsync`, `executeBatchAsync(sql, rows, options?)`, `sequentiallyAsync(sql, params?, onRow, options?)`, `transactionAsync(options?)`, `newStatementAsync(sql)`, `attachEventAsync()`, `detachAsync()`, `dropAsync()`, `db.withTransaction(work, options?)`
|
|
137
137
|
- **transaction** — `queryAsync`, `executeAsync`, `executeBatchAsync`, `sequentiallyAsync`, `newStatementAsync`, `commitAsync`, `rollbackAsync`, `commitRetainingAsync`, `rollbackRetainingAsync`
|
|
138
138
|
- **statement** — `executeAsync(transaction, params?, options?)`, `executeBatchAsync(transaction, rows, options?)`, `fetchAsync`, `fetchScrollAsync`, `fetchAllAsync`, `closeAsync`, `dropAsync`, `releaseAsync`
|
|
139
|
+
- **service manager** — every [Service Manager function](#service-manager-functions) has an `*Async` counterpart (`backupAsync`, `restoreAsync`, `getUsersAsync`, `addUserAsync`, `getFbserverInfosAsync`, `startTraceAsync`, …); stream-producing functions resolve with the `Readable`, info functions with the info object
|
|
139
140
|
|
|
140
141
|
Notes:
|
|
141
142
|
|
|
@@ -174,13 +175,18 @@ options.wireCrypt = Firebird.WIRE_CRYPT_ENABLE; // default; set to Firebird.WIRE
|
|
|
174
175
|
options.pluginName = undefined; // optional, auto-negotiated; can be set to Firebird.AUTH_PLUGIN_SRP256, Firebird.AUTH_PLUGIN_SRP, or Firebird.AUTH_PLUGIN_LEGACY
|
|
175
176
|
options.dbCryptConfig = undefined; // optional; database encryption key for encrypted databases. Use 'base64:<value>' for base64-encoded keys or plain text
|
|
176
177
|
options.connectTimeout = 10000; // optional; timeout in ms for a single pool.get() attach operation (default: no timeout)
|
|
178
|
+
options.enableKeepAlive = true; // TCP keepalive probing to detect dead/stale connections (same option names as mysql2); set to false to disable
|
|
179
|
+
options.keepAliveInitialDelay = 60000; // ms a socket must be idle before the first keepalive probe (ignored when enableKeepAlive is false)
|
|
177
180
|
options.parallelWorkers = undefined; // optional; request multiple thread workers for maintenance/index tasks (FB >= 5)
|
|
178
181
|
options.maxInlineBlobSize = undefined; // optional; threshold size in bytes for inline blob transmission (default 65535, FB >= 5.0.3)
|
|
179
|
-
options.maxNegotiatedProtocols =
|
|
180
|
-
options.defaultSchema = undefined; // optional; sets session CURRENT_SCHEMA at connect time (FB >= 6.0)
|
|
182
|
+
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)
|
|
183
|
+
options.defaultSchema = undefined; // optional; sets session CURRENT_SCHEMA at connect time by putting the schema first in the search path (FB >= 6.0)
|
|
181
184
|
options.searchPath = undefined; // optional; ordered list/array of schemas to resolve unqualified object references (FB >= 6.0)
|
|
185
|
+
options.owner = undefined; // optional; owner of a newly created database — lets a superuser create a database for another user (create only, FB >= 6.0)
|
|
182
186
|
options.jsonAsObject = false; // optional; automatically stringify parameters and parse query results that contain JSON (FB >= 6.0)
|
|
183
187
|
options.namedPlaceholders = false; // set to true to allow :name placeholders in SQL with a { name: value } params object (see Named placeholders)
|
|
188
|
+
options.typeCast = undefined; // optional; custom type parser called for every result column value (see Custom type parsers)
|
|
189
|
+
options.statementCacheSize = 0; // optional; per-connection LRU cache of prepared statements, 0 = disabled (see Prepared-statement cache)
|
|
184
190
|
```
|
|
185
191
|
|
|
186
192
|
### Connection URI strings
|
|
@@ -486,6 +492,134 @@ the per-query option:
|
|
|
486
492
|
await db.queryAsync(execBlockSql, [], { namedPlaceholders: false });
|
|
487
493
|
```
|
|
488
494
|
|
|
495
|
+
### Custom type parsers (typeCast)
|
|
496
|
+
|
|
497
|
+
The `typeCast` connection option lets you override how column values are
|
|
498
|
+
decoded, per SQL type or per column — the same idea as mysql2's `typeCast`
|
|
499
|
+
and pg's `setTypeParser`. The hook is called for **every column value of
|
|
500
|
+
every result row** (including `NULL`s); whatever it returns becomes the
|
|
501
|
+
value in the row. Call `next()` to get the value the driver would produce
|
|
502
|
+
by default (after `blobAsText` / `jsonAsObject` are applied).
|
|
503
|
+
|
|
504
|
+
```js
|
|
505
|
+
const Firebird = require('node-firebird');
|
|
506
|
+
|
|
507
|
+
Firebird.attach({
|
|
508
|
+
...options,
|
|
509
|
+
typeCast: (column, next) => {
|
|
510
|
+
// dates as ISO strings instead of Date objects
|
|
511
|
+
if (column.typeName === 'DATE') {
|
|
512
|
+
const v = next();
|
|
513
|
+
return v === null ? null : v.toISOString().slice(0, 10);
|
|
514
|
+
}
|
|
515
|
+
// BIGINT columns as strings
|
|
516
|
+
if (column.type === Firebird.SQL_TYPES.SQL_INT64 && !column.scale) {
|
|
517
|
+
return String(next());
|
|
518
|
+
}
|
|
519
|
+
return next(); // everything else: default decoding
|
|
520
|
+
},
|
|
521
|
+
}, (err, db) => { /* ... */ });
|
|
522
|
+
```
|
|
523
|
+
|
|
524
|
+
`column` describes the result column:
|
|
525
|
+
|
|
526
|
+
| Property | Meaning |
|
|
527
|
+
| :--------- | :------------------------------------------------------------------ |
|
|
528
|
+
| `type` | Firebird SQL type code — compare against `Firebird.SQL_TYPES.*` |
|
|
529
|
+
| `typeName` | Friendly name: `'VARYING'`, `'INT64'`, `'DATE'`, `'BLOB'`, ... |
|
|
530
|
+
| `subType` | Column subtype (`1` = text for BLOBs) |
|
|
531
|
+
| `scale` | Negative decimal scale for `NUMERIC`/`DECIMAL` (e.g. `-2`) |
|
|
532
|
+
| `length` | Declared length in bytes |
|
|
533
|
+
| `field` | Column name in the table |
|
|
534
|
+
| `relation` | Table name |
|
|
535
|
+
| `alias` | SELECT-list alias (the row key for object rows) |
|
|
536
|
+
|
|
537
|
+
Notes:
|
|
538
|
+
|
|
539
|
+
- Non-text BLOB columns reach the hook as the usual asynchronous fetch
|
|
540
|
+
function; text BLOBs with `blobAsText: true` reach it as the resolved
|
|
541
|
+
string.
|
|
542
|
+
- The hook must be a **pure function** of its inputs: when a response
|
|
543
|
+
spans multiple TCP packets the affected rows can be decoded more than
|
|
544
|
+
once, calling the hook again for the same value.
|
|
545
|
+
- The hook runs for every value on the hot row-decoding path — keep it
|
|
546
|
+
cheap, and prefer dispatching on `column.type`/`column.typeName` early.
|
|
547
|
+
- Exceptions thrown by the hook are caught: the default value is used and
|
|
548
|
+
a warning is printed. A throw cannot be allowed to escape into the wire
|
|
549
|
+
decoder, so validate inside the hook and encode failures in the value.
|
|
550
|
+
|
|
551
|
+
### Prepared-statement cache
|
|
552
|
+
|
|
553
|
+
Setting `statementCacheSize` keeps a per-connection LRU cache of prepared
|
|
554
|
+
statements (like mysql2's statement cache): running the same SQL string
|
|
555
|
+
again transparently reuses the already-prepared server-side statement,
|
|
556
|
+
skipping the prepare round-trip. No API changes are needed — `db.query`,
|
|
557
|
+
`tx.query`, `sequentially`, `executeBatch` and the `*Async` wrappers all
|
|
558
|
+
benefit automatically.
|
|
559
|
+
|
|
560
|
+
```js
|
|
561
|
+
Firebird.attach({ ...options, statementCacheSize: 100 }, (err, db) => {
|
|
562
|
+
// the second identical query reuses the prepared statement
|
|
563
|
+
db.query('SELECT * FROM t WHERE id = ?', [1], () => {
|
|
564
|
+
db.query('SELECT * FROM t WHERE id = ?', [2], () => { /* ... */ });
|
|
565
|
+
});
|
|
566
|
+
});
|
|
567
|
+
```
|
|
568
|
+
|
|
569
|
+
How it works:
|
|
570
|
+
|
|
571
|
+
- The number is the maximum of **idle** statements kept per connection;
|
|
572
|
+
the least-recently-used statement is dropped when the limit is exceeded.
|
|
573
|
+
- A cached statement leaves the cache while in use, so concurrent runs of
|
|
574
|
+
the same SQL never share a server-side cursor — extra preparations run
|
|
575
|
+
in parallel and only one goes back into the cache.
|
|
576
|
+
- Statements that failed and DDL statements are never cached.
|
|
577
|
+
- Cache keys are exact SQL strings (after the `namedPlaceholders`
|
|
578
|
+
rewrite), so use parametrized queries to get hits.
|
|
579
|
+
- The legacy `cacheQuery: true` / `maxCachedQuery` options remain
|
|
580
|
+
supported and now map onto the same LRU cache (with a default limit of
|
|
581
|
+
100 instead of the old unbounded map).
|
|
582
|
+
|
|
583
|
+
> **Note (DDL):** a statement prepared before a metadata change (e.g.
|
|
584
|
+
> `ALTER TABLE`) may fail when reused. If you mix DDL with hot queries on
|
|
585
|
+
> the same connection, keep the cache small or disabled.
|
|
586
|
+
|
|
587
|
+
### Streaming rows with queryStream
|
|
588
|
+
|
|
589
|
+
`db.queryStream(sql, params, options)` returns an **object-mode
|
|
590
|
+
`Readable`** emitting one row per chunk — the counterpart of
|
|
591
|
+
`pg-query-stream` and mysql2's `.stream()`. It is built on
|
|
592
|
+
`sequentially()`'s backpressure: fetching from the server pauses while
|
|
593
|
+
the stream's buffer is full and resumes as the consumer drains it, so
|
|
594
|
+
constant memory is used regardless of the result size.
|
|
595
|
+
|
|
596
|
+
```js
|
|
597
|
+
const { pipeline } = require('stream/promises');
|
|
598
|
+
|
|
599
|
+
// async iteration
|
|
600
|
+
for await (const row of db.queryStream('SELECT * FROM big_table')) {
|
|
601
|
+
console.log(row.ID);
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
// or piping into any Writable/Transform (HTTP response, CSV encoder, ...)
|
|
605
|
+
await pipeline(
|
|
606
|
+
db.queryStream('SELECT * FROM big_table WHERE grp = ?', [42]),
|
|
607
|
+
myCsvTransform,
|
|
608
|
+
res);
|
|
609
|
+
```
|
|
610
|
+
|
|
611
|
+
- `db.queryStream` runs in its own transaction (like `db.query`);
|
|
612
|
+
`transaction.queryStream` runs inside your transaction, which is *not*
|
|
613
|
+
committed when the stream ends.
|
|
614
|
+
- Destroying the stream early — including an error mid-`pipeline()` —
|
|
615
|
+
aborts the fetch and releases the statement; the connection stays
|
|
616
|
+
usable.
|
|
617
|
+
- Options: everything `query` accepts (e.g. `signal`), plus
|
|
618
|
+
`highWaterMark` (rows buffered before fetching pauses, default 16) and
|
|
619
|
+
`asObject: false` for array rows.
|
|
620
|
+
- Rows go through the regular decode path, so `typeCast`, `blobAsText`
|
|
621
|
+
and `jsonAsObject` all apply.
|
|
622
|
+
|
|
489
623
|
### Tablespaces and Schema Partitioning (Firebird 6.0+)
|
|
490
624
|
|
|
491
625
|
For Firebird 6.0+ (Protocol 20+), you can create and manage physical tablespace locations and logical schema namespaces, optionally partitioning schemas into specific physical tablespaces.
|
|
@@ -1020,6 +1154,26 @@ var fbsvc = {
|
|
|
1020
1154
|
|
|
1021
1155
|
```
|
|
1022
1156
|
|
|
1157
|
+
Every function also has a promise-returning `*Async` counterpart (no
|
|
1158
|
+
callback argument): stream-producing functions resolve with the
|
|
1159
|
+
`Readable`, info functions with the info object.
|
|
1160
|
+
|
|
1161
|
+
```js
|
|
1162
|
+
const svc = await Firebird.attachAsync({ ...options, manager: true });
|
|
1163
|
+
try {
|
|
1164
|
+
const info = await svc.getFbserverInfosAsync();
|
|
1165
|
+
console.log(info.fbversion);
|
|
1166
|
+
|
|
1167
|
+
const backup = await svc.backupAsync({
|
|
1168
|
+
database: '/DB/MYDB.FDB',
|
|
1169
|
+
files: [{ filename: '/DB/MYDB.FBK' }]
|
|
1170
|
+
});
|
|
1171
|
+
for await (const line of backup) console.log(line);
|
|
1172
|
+
} finally {
|
|
1173
|
+
await svc.detachAsync();
|
|
1174
|
+
}
|
|
1175
|
+
```
|
|
1176
|
+
|
|
1023
1177
|
### Backup Service example
|
|
1024
1178
|
|
|
1025
1179
|
```js
|
|
@@ -1758,13 +1912,13 @@ db.transaction(function (err, tx) {
|
|
|
1758
1912
|
|
|
1759
1913
|
#### Is the wire protocol version hard-coded?
|
|
1760
1914
|
|
|
1761
|
-
No. node-firebird negotiates the highest protocol version both the client and server support
|
|
1915
|
+
No. node-firebird negotiates the highest protocol version both the client and server support — up to Protocol 20 (Firebird 6.0) by default; see [Protocol Implementation Status](ROADMAP.md#4-protocol-implementation-status) in the roadmap for the full version table. Servers ignore protocol versions they do not know, so offering the full list is safe on old servers too. To cap negotiation at an older protocol, limit how many versions are offered (the list is ordered oldest first):
|
|
1762
1916
|
|
|
1763
1917
|
```js
|
|
1764
|
-
options.maxNegotiatedProtocols =
|
|
1918
|
+
options.maxNegotiatedProtocols = 10; // stop at Protocol 19 (pre-Firebird 6 behavior)
|
|
1765
1919
|
```
|
|
1766
1920
|
|
|
1767
|
-
|
|
1921
|
+
Historical note: Protocol 20 used to be excluded by default because of a query-preparation hang — the client did not send the `p_sqlst_flags` field that protocol 20 added to `op_prepare_statement`, leaving the server blocked mid-packet. The field is now sent and Protocol 20 is fully negotiated.
|
|
1768
1922
|
|
|
1769
1923
|
#### BLOB reads/writes are very slow, especially for large files over a remote connection
|
|
1770
1924
|
|
|
@@ -1821,6 +1975,12 @@ db.query('SELECT * FROM ACTORS WHERE NAME LIKE ?', ['James Wick%'], function (er
|
|
|
1821
1975
|
});
|
|
1822
1976
|
```
|
|
1823
1977
|
|
|
1978
|
+
#### attach() *sometimes* fails with gdscode 335544472 ("Your user name and password are not defined") even though the credentials are correct
|
|
1979
|
+
|
|
1980
|
+
If the failure is intermittent — the same code with the same credentials succeeds on most attempts and fails on others — update the driver: versions before 2.8.1 had two serialization mismatches in the SRP (Srp/Srp256/384/512) proof-of-password computation that made roughly 1 in 80 attaches fail with exactly this error (whenever the ephemeral SRP values happened to have a leading zero byte). Fixed in 2.8.1; see [issue #421](https://github.com/hgourvest/node-firebird/issues/421) and [issue #347](https://github.com/hgourvest/node-firebird/issues/347). Switching the user to `Legacy_UserManager`/`Legacy_Auth` "fixed" it on older versions only because that avoids the SRP code path entirely — with 2.8.1+ this workaround is no longer needed.
|
|
1981
|
+
|
|
1982
|
+
If the failure is consistent, the credentials really don't match an account for the authentication plugin in use: check `AuthServer`/`UserManager` in `firebird.conf` and remember that SRP and Legacy user managers keep separate password stores — a user created under one plugin does not automatically exist for the other.
|
|
1983
|
+
|
|
1824
1984
|
## Contributing
|
|
1825
1985
|
|
|
1826
1986
|
Contributions are welcome — code, documentation, and bug reports alike.
|
package/lib/index.d.ts
CHANGED
|
@@ -24,6 +24,11 @@ export declare const ISOLATION_REPEATABLE_READ: number[];
|
|
|
24
24
|
export declare const ISOLATION_SERIALIZABLE: number[];
|
|
25
25
|
export declare const ISOLATION_READ_COMMITTED_READ_ONLY: number[];
|
|
26
26
|
export declare const escape: typeof escapeValue;
|
|
27
|
+
/**
|
|
28
|
+
* Firebird SQL type codes, as seen in `column.type` inside a `typeCast`
|
|
29
|
+
* hook (each code also has a friendly `column.typeName`).
|
|
30
|
+
*/
|
|
31
|
+
export declare const SQL_TYPES: Readonly<Record<string, number>>;
|
|
27
32
|
/**
|
|
28
33
|
* The most recent Connection created by attach()/create()/attachOrCreate().
|
|
29
34
|
* Kept for backwards compatibility with the previous CommonJS module where
|
package/lib/index.js
CHANGED
|
@@ -17,7 +17,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
17
17
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
18
18
|
};
|
|
19
19
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
20
|
-
exports.parseNamedPlaceholders = exports.parseConnectionString = exports.parseConnectionUri = exports.connection = exports.escape = exports.ISOLATION_READ_COMMITTED_READ_ONLY = exports.ISOLATION_SERIALIZABLE = exports.ISOLATION_REPEATABLE_READ = exports.ISOLATION_READ_COMMITTED = exports.ISOLATION_READ_UNCOMMITTED = exports.WIRE_CRYPT_ENABLE = exports.WIRE_CRYPT_DISABLE = exports.AUTH_PLUGIN_SRP512 = exports.AUTH_PLUGIN_SRP384 = exports.AUTH_PLUGIN_SRP256 = exports.AUTH_PLUGIN_SRP = exports.AUTH_PLUGIN_LEGACY = exports.GDSCode = void 0;
|
|
20
|
+
exports.parseNamedPlaceholders = exports.parseConnectionString = exports.parseConnectionUri = exports.connection = exports.SQL_TYPES = exports.escape = exports.ISOLATION_READ_COMMITTED_READ_ONLY = exports.ISOLATION_SERIALIZABLE = exports.ISOLATION_REPEATABLE_READ = exports.ISOLATION_READ_COMMITTED = exports.ISOLATION_READ_UNCOMMITTED = exports.WIRE_CRYPT_ENABLE = exports.WIRE_CRYPT_DISABLE = exports.AUTH_PLUGIN_SRP512 = exports.AUTH_PLUGIN_SRP384 = exports.AUTH_PLUGIN_SRP256 = exports.AUTH_PLUGIN_SRP = exports.AUTH_PLUGIN_LEGACY = exports.GDSCode = void 0;
|
|
21
21
|
exports.attach = attach;
|
|
22
22
|
exports.drop = drop;
|
|
23
23
|
exports.create = create;
|
|
@@ -63,6 +63,35 @@ exports.ISOLATION_REPEATABLE_READ = const_1.default.ISOLATION_REPEATABLE_READ;
|
|
|
63
63
|
exports.ISOLATION_SERIALIZABLE = const_1.default.ISOLATION_SERIALIZABLE;
|
|
64
64
|
exports.ISOLATION_READ_COMMITTED_READ_ONLY = const_1.default.ISOLATION_READ_COMMITTED_READ_ONLY;
|
|
65
65
|
exports.escape = utils_1.escape;
|
|
66
|
+
/**
|
|
67
|
+
* Firebird SQL type codes, as seen in `column.type` inside a `typeCast`
|
|
68
|
+
* hook (each code also has a friendly `column.typeName`).
|
|
69
|
+
*/
|
|
70
|
+
exports.SQL_TYPES = Object.freeze({
|
|
71
|
+
SQL_TEXT: const_1.default.SQL_TEXT,
|
|
72
|
+
SQL_VARYING: const_1.default.SQL_VARYING,
|
|
73
|
+
SQL_SHORT: const_1.default.SQL_SHORT,
|
|
74
|
+
SQL_LONG: const_1.default.SQL_LONG,
|
|
75
|
+
SQL_FLOAT: const_1.default.SQL_FLOAT,
|
|
76
|
+
SQL_DOUBLE: const_1.default.SQL_DOUBLE,
|
|
77
|
+
SQL_D_FLOAT: const_1.default.SQL_D_FLOAT,
|
|
78
|
+
SQL_TIMESTAMP: const_1.default.SQL_TIMESTAMP,
|
|
79
|
+
SQL_BLOB: const_1.default.SQL_BLOB,
|
|
80
|
+
SQL_ARRAY: const_1.default.SQL_ARRAY,
|
|
81
|
+
SQL_QUAD: const_1.default.SQL_QUAD,
|
|
82
|
+
SQL_TYPE_TIME: const_1.default.SQL_TYPE_TIME,
|
|
83
|
+
SQL_TYPE_DATE: const_1.default.SQL_TYPE_DATE,
|
|
84
|
+
SQL_INT64: const_1.default.SQL_INT64,
|
|
85
|
+
SQL_INT128: const_1.default.SQL_INT128,
|
|
86
|
+
SQL_TIMESTAMP_TZ: const_1.default.SQL_TIMESTAMP_TZ,
|
|
87
|
+
SQL_TIMESTAMP_TZ_EX: const_1.default.SQL_TIMESTAMP_TZ_EX,
|
|
88
|
+
SQL_TIME_TZ: const_1.default.SQL_TIME_TZ,
|
|
89
|
+
SQL_TIME_TZ_EX: const_1.default.SQL_TIME_TZ_EX,
|
|
90
|
+
SQL_DEC16: const_1.default.SQL_DEC16,
|
|
91
|
+
SQL_DEC34: const_1.default.SQL_DEC34,
|
|
92
|
+
SQL_BOOLEAN: const_1.default.SQL_BOOLEAN,
|
|
93
|
+
SQL_NULL: const_1.default.SQL_NULL,
|
|
94
|
+
});
|
|
66
95
|
function attach(options, callback) {
|
|
67
96
|
options = (0, uri_1.normalizeOptions)(options);
|
|
68
97
|
var host = options.host || const_1.default.DEFAULT_HOST;
|
|
@@ -135,6 +164,8 @@ function attachOrCreate(options, callback) {
|
|
|
135
164
|
if (!err) {
|
|
136
165
|
if (self.db)
|
|
137
166
|
self.db.emit('connect', ret);
|
|
167
|
+
// DatabaseCallback stays permissive (db non-optional) for
|
|
168
|
+
// API users; internally the error path passes no db
|
|
138
169
|
(0, callback_1.doCallback)(ret, callback);
|
|
139
170
|
return;
|
|
140
171
|
}
|
package/lib/pool.js
CHANGED
|
@@ -141,7 +141,7 @@ class Pool extends events_1.default.EventEmitter {
|
|
|
141
141
|
return self;
|
|
142
142
|
if ((self.dbinuse + self._creating) >= self.max)
|
|
143
143
|
return self;
|
|
144
|
-
|
|
144
|
+
const cb = self.pending.shift();
|
|
145
145
|
if (!cb)
|
|
146
146
|
return self;
|
|
147
147
|
if (self.pooldb.length) {
|
package/lib/srp.d.ts
CHANGED
|
@@ -22,7 +22,7 @@ export declare function clientSeed(a?: bigint): KeyPair;
|
|
|
22
22
|
* @param b BigInt Server private key.
|
|
23
23
|
* @returns {{private: BigInt, public: BigInt}}
|
|
24
24
|
*/
|
|
25
|
-
export declare function serverSeed(user: string, password: string, salt: Buffer, b?: bigint | string, hashAlgo?: string): KeyPair;
|
|
25
|
+
export declare function serverSeed(user: string, password: string, salt: Buffer | string, b?: bigint | string, hashAlgo?: string): KeyPair;
|
|
26
26
|
/**
|
|
27
27
|
* Server session secret.
|
|
28
28
|
*
|
|
@@ -34,11 +34,11 @@ export declare function serverSeed(user: string, password: string, salt: Buffer,
|
|
|
34
34
|
* @param b BigInt Server private key.
|
|
35
35
|
* @returns {BigInt}
|
|
36
36
|
*/
|
|
37
|
-
export declare function serverSession(user: string, password: string, salt: Buffer, A: bigint, B: bigint, b: bigint, hashAlgo?: string): bigint;
|
|
37
|
+
export declare function serverSession(user: string, password: string, salt: Buffer | string, A: bigint, B: bigint, b: bigint, hashAlgo?: string): bigint;
|
|
38
38
|
/**
|
|
39
39
|
* M = H(H(N) xor H(g), H(I), s, A, B, K)
|
|
40
40
|
*/
|
|
41
|
-
export declare function clientProof(user: string, password: string, salt: Buffer, A: bigint, B: bigint, a: bigint, hashAlgo?: string): ClientProof;
|
|
41
|
+
export declare function clientProof(user: string, password: string, salt: Buffer | string, A: bigint, B: bigint, a: bigint, hashAlgo?: string): ClientProof;
|
|
42
42
|
/**
|
|
43
43
|
* Pad hex string.
|
|
44
44
|
*/
|
package/lib/types.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { Readable } from 'stream';
|
|
1
2
|
export type DatabaseCallback = (err: any, db: Database) => void;
|
|
2
3
|
export type TransactionCallback = (err: any, transaction: Transaction) => void;
|
|
3
4
|
export type QueryCallback = (err: any, result: any[]) => void;
|
|
@@ -115,6 +116,15 @@ export type QueryOptions = {
|
|
|
115
116
|
*/
|
|
116
117
|
signal?: AbortSignal;
|
|
117
118
|
};
|
|
119
|
+
export type QueryStreamOptions = QueryOptions & {
|
|
120
|
+
/**
|
|
121
|
+
* Rows buffered internally before fetching pauses (object-mode
|
|
122
|
+
* Readable highWaterMark, default 16).
|
|
123
|
+
*/
|
|
124
|
+
highWaterMark?: number;
|
|
125
|
+
/** Emit array rows instead of objects (like db.execute). */
|
|
126
|
+
asObject?: boolean;
|
|
127
|
+
};
|
|
118
128
|
export interface Database {
|
|
119
129
|
detach(callback?: SimpleCallback): Database;
|
|
120
130
|
transaction(options: TransactionOptions | Isolation | TransactionCallback, callback?: TransactionCallback): Database;
|
|
@@ -124,6 +134,13 @@ export interface Database {
|
|
|
124
134
|
/** Bulk-execute in its own transaction, all-or-nothing (Firebird 4.0+). */
|
|
125
135
|
executeBatch(query: string, rows: QueryParams[], callback?: (err: any, result: BatchResult) => void, options?: BatchOptions): Database;
|
|
126
136
|
sequentially(query: string, params: QueryParams, rowCallback: SequentialCallback, callback: SimpleCallback, options?: QueryOptions | boolean): Database;
|
|
137
|
+
/**
|
|
138
|
+
* Run `query` and return an object-mode Readable emitting one row per
|
|
139
|
+
* chunk, with backpressure (fetching pauses while the buffer is full).
|
|
140
|
+
* Runs in its own transaction. Destroying the stream early aborts the
|
|
141
|
+
* fetch and releases the statement.
|
|
142
|
+
*/
|
|
143
|
+
queryStream(query: string, params?: QueryParams, options?: QueryStreamOptions): Readable;
|
|
127
144
|
drop(callback: SimpleCallback): void;
|
|
128
145
|
escape(value: any): string;
|
|
129
146
|
attachEvent(callback: any): this;
|
|
@@ -160,6 +177,12 @@ export interface Transaction {
|
|
|
160
177
|
/** Bulk-execute within this transaction; per-record failures do not roll back (Firebird 4.0+). */
|
|
161
178
|
executeBatch(query: string, rows: QueryParams[], callback?: (err: any, result: BatchResult) => void, options?: BatchOptions): void;
|
|
162
179
|
sequentially(query: string, params: QueryParams, rowCallback: SequentialCallback, callback: SimpleCallback, options?: QueryOptions | boolean): Database;
|
|
180
|
+
/**
|
|
181
|
+
* Run `query` inside this transaction and return an object-mode
|
|
182
|
+
* Readable emitting one row per chunk, with backpressure. The
|
|
183
|
+
* transaction is NOT committed when the stream ends.
|
|
184
|
+
*/
|
|
185
|
+
queryStream(query: string, params?: QueryParams, options?: QueryStreamOptions): Readable;
|
|
163
186
|
commit(callback?: SimpleCallback): void;
|
|
164
187
|
commitRetaining(callback?: SimpleCallback): void;
|
|
165
188
|
rollback(callback?: SimpleCallback): void;
|
|
@@ -231,6 +254,16 @@ export interface Options {
|
|
|
231
254
|
* per-query `namedPlaceholders: false` override.
|
|
232
255
|
*/
|
|
233
256
|
namedPlaceholders?: boolean;
|
|
257
|
+
/**
|
|
258
|
+
* TCP keepalive probing to detect dead/stale connections (same option
|
|
259
|
+
* names as mysql2). On by default; set false to disable.
|
|
260
|
+
*/
|
|
261
|
+
enableKeepAlive?: boolean;
|
|
262
|
+
/**
|
|
263
|
+
* Milliseconds a socket must be idle before the first TCP keepalive
|
|
264
|
+
* probe is sent (default 60000). Ignored when enableKeepAlive is false.
|
|
265
|
+
*/
|
|
266
|
+
keepAliveInitialDelay?: number;
|
|
234
267
|
pluginName?: string;
|
|
235
268
|
parallelWorkers?: number;
|
|
236
269
|
maxInlineBlobSize?: number;
|
|
@@ -262,11 +295,11 @@ export interface Options {
|
|
|
262
295
|
/**
|
|
263
296
|
* **Firebird 6.0+ only (Protocol 20+)**
|
|
264
297
|
*
|
|
265
|
-
* Sets the session's current schema at connection time.
|
|
266
|
-
*
|
|
267
|
-
*
|
|
268
|
-
*
|
|
269
|
-
*
|
|
298
|
+
* Sets the session's current schema at connection time. `CURRENT_SCHEMA`
|
|
299
|
+
* in Firebird is the first existing schema of the search path, so this
|
|
300
|
+
* option is implemented by putting the schema at the front of the
|
|
301
|
+
* `searchPath` sent to the server (with `PUBLIC` kept as a fallback when
|
|
302
|
+
* no explicit `searchPath` is given).
|
|
270
303
|
*
|
|
271
304
|
* Example: `defaultSchema: 'myapp'`
|
|
272
305
|
*/
|
|
@@ -288,6 +321,18 @@ export interface Options {
|
|
|
288
321
|
* (typically `PUBLIC` then `SYSTEM`).
|
|
289
322
|
*/
|
|
290
323
|
searchPath?: string | string[];
|
|
324
|
+
/**
|
|
325
|
+
* **Firebird 6.0+ only**
|
|
326
|
+
*
|
|
327
|
+
* Owner of a newly created database (`isc_dpb_owner`), allowing a
|
|
328
|
+
* superuser to create a database owned by another user
|
|
329
|
+
* ([firebird#7718](https://github.com/FirebirdSQL/firebird/issues/7718)).
|
|
330
|
+
* Only honored by `create`/`attachOrCreate` when the database is
|
|
331
|
+
* created; ignored on plain attach and by older servers.
|
|
332
|
+
*
|
|
333
|
+
* Example: `owner: 'APP_OWNER'`
|
|
334
|
+
*/
|
|
335
|
+
owner?: string;
|
|
291
336
|
/**
|
|
292
337
|
* **Firebird 6.0+ only (Protocol 20+)**
|
|
293
338
|
*
|
|
@@ -296,7 +341,55 @@ export interface Options {
|
|
|
296
341
|
* text/BLOB columns back into JavaScript objects/arrays.
|
|
297
342
|
*/
|
|
298
343
|
jsonAsObject?: boolean;
|
|
344
|
+
/**
|
|
345
|
+
* Custom type parser (mysql2-style). Called for every column value of
|
|
346
|
+
* every result row (including NULLs); whatever it returns becomes the
|
|
347
|
+
* value in the row. Call `next()` to get the value the driver would
|
|
348
|
+
* produce by default (after `blobAsText`/`jsonAsObject` are applied).
|
|
349
|
+
*
|
|
350
|
+
* ```js
|
|
351
|
+
* typeCast: (column, next) =>
|
|
352
|
+
* column.typeName === 'INT64' ? Number(next()) : next()
|
|
353
|
+
* ```
|
|
354
|
+
*
|
|
355
|
+
* Non-text BLOB columns reach the hook as the usual fetch function;
|
|
356
|
+
* text BLOBs with `blobAsText` reach it as the resolved string. The
|
|
357
|
+
* hook must be a pure function: a row can be decoded more than once
|
|
358
|
+
* when a response spans TCP packets.
|
|
359
|
+
*/
|
|
360
|
+
typeCast?: TypeCastFunction;
|
|
361
|
+
/**
|
|
362
|
+
* Per-connection LRU cache of prepared statements (like mysql2's
|
|
363
|
+
* statement cache). `db.query`/`tx.query` and friends transparently
|
|
364
|
+
* reuse the prepared handle for a repeated SQL string, skipping the
|
|
365
|
+
* prepare round-trip on hot paths. The number is the maximum of idle
|
|
366
|
+
* cached statements; least-recently-used ones are dropped over the
|
|
367
|
+
* limit. 0 / unset = disabled. Statements that failed and DDL are
|
|
368
|
+
* never cached; concurrent runs of the same SQL never share a
|
|
369
|
+
* statement (extra preparations are simply not cached).
|
|
370
|
+
*/
|
|
371
|
+
statementCacheSize?: number;
|
|
299
372
|
}
|
|
373
|
+
/** Column metadata passed to the {@link Options.typeCast} hook. */
|
|
374
|
+
export interface TypeCastColumn {
|
|
375
|
+
/** Firebird SQL type code (see the exported `SQL_TYPES` map). */
|
|
376
|
+
type: number;
|
|
377
|
+
/** Friendly name of the type code: 'VARYING', 'INT64', 'BLOB', ... */
|
|
378
|
+
typeName: string;
|
|
379
|
+
/** Column subtype (e.g. 1 = text for BLOBs; charset id for strings). */
|
|
380
|
+
subType?: number;
|
|
381
|
+
/** Negative decimal scale for NUMERIC/DECIMAL columns (e.g. -2). */
|
|
382
|
+
scale?: number;
|
|
383
|
+
/** Declared length in bytes. */
|
|
384
|
+
length?: number;
|
|
385
|
+
/** Column name in the table. */
|
|
386
|
+
field?: string;
|
|
387
|
+
/** Table (relation) name. */
|
|
388
|
+
relation?: string;
|
|
389
|
+
/** Alias used in the SELECT list (the row key for object rows). */
|
|
390
|
+
alias?: string;
|
|
391
|
+
}
|
|
392
|
+
export type TypeCastFunction = (column: TypeCastColumn, next: () => any) => any;
|
|
300
393
|
export interface SvcMgrOptions extends Options {
|
|
301
394
|
manager: true;
|
|
302
395
|
}
|
|
@@ -506,4 +599,49 @@ export interface ServiceManager {
|
|
|
506
599
|
hasRunningAction(options: ReadableOptions, callback: ReadableCallback): void;
|
|
507
600
|
readusers(options: ReadableOptions, callback: ReadableCallback): void;
|
|
508
601
|
readlimbo(options: ReadableOptions, callback: ReadableCallback): void;
|
|
602
|
+
detachAsync(force?: boolean): Promise<void>;
|
|
603
|
+
backupAsync(options: BackupOptions): Promise<NodeJS.ReadableStream>;
|
|
604
|
+
nbackupAsync(options: BackupOptions): Promise<NodeJS.ReadableStream>;
|
|
605
|
+
restoreAsync(options: RestoreOptions): Promise<NodeJS.ReadableStream>;
|
|
606
|
+
nrestoreAsync(options: NRestoreOptions): Promise<NodeJS.ReadableStream>;
|
|
607
|
+
setDialectAsync(db: string, dialect: 1 | 3): Promise<NodeJS.ReadableStream>;
|
|
608
|
+
setSweepintervalAsync(db: string, interval: number): Promise<any>;
|
|
609
|
+
setCachebufferAsync(db: string, nbpages: any): Promise<NodeJS.ReadableStream>;
|
|
610
|
+
BringOnlineAsync(db: string): Promise<NodeJS.ReadableStream>;
|
|
611
|
+
ShutdownAsync(db: string, kind: ShutdownKind, delay: number, mode?: ShutdownMode): Promise<NodeJS.ReadableStream>;
|
|
612
|
+
setShadowAsync(db: string, val: boolean): Promise<NodeJS.ReadableStream>;
|
|
613
|
+
setForcewriteAsync(db: string, val: boolean): Promise<NodeJS.ReadableStream>;
|
|
614
|
+
setReservespaceAsync(db: string, val: boolean): Promise<NodeJS.ReadableStream>;
|
|
615
|
+
setReadonlyModeAsync(db: string): Promise<NodeJS.ReadableStream>;
|
|
616
|
+
setReadwriteModeAsync(db: string): Promise<NodeJS.ReadableStream>;
|
|
617
|
+
validateAsync(options: ValidateOptions): Promise<NodeJS.ReadableStream>;
|
|
618
|
+
commitAsync(db: string, transactid: number): Promise<NodeJS.ReadableStream>;
|
|
619
|
+
rollbackAsync(db: string, transactid: number): Promise<NodeJS.ReadableStream>;
|
|
620
|
+
recoverAsync(db: string, transactid: number): Promise<NodeJS.ReadableStream>;
|
|
621
|
+
getStatsAsync(options: StatsOptions): Promise<NodeJS.ReadableStream>;
|
|
622
|
+
getLogAsync(options: ReadableOptions): Promise<NodeJS.ReadableStream>;
|
|
623
|
+
getUsersAsync(username?: string | null): Promise<ServerInfo>;
|
|
624
|
+
addUserAsync(username: string, password: string, info?: UserInfo): Promise<NodeJS.ReadableStream>;
|
|
625
|
+
editUserAsync(username: string, info: UserInfo): Promise<NodeJS.ReadableStream>;
|
|
626
|
+
removeUserAsync(username: string, rolename?: string | null): Promise<NodeJS.ReadableStream>;
|
|
627
|
+
getFbserverInfosAsync(infos?: ServerInfoReq, options?: {
|
|
628
|
+
buffersize?: number;
|
|
629
|
+
timeout?: number;
|
|
630
|
+
}): Promise<ServerInfo>;
|
|
631
|
+
startTraceAsync(options: TraceOptions): Promise<NodeJS.ReadableStream>;
|
|
632
|
+
suspendTraceAsync(options: TraceOptions): Promise<NodeJS.ReadableStream>;
|
|
633
|
+
resumeTraceAsync(options: TraceOptions): Promise<NodeJS.ReadableStream>;
|
|
634
|
+
stopTraceAsync(options: TraceOptions): Promise<NodeJS.ReadableStream>;
|
|
635
|
+
getTraceListAsync(options?: ReadableOptions): Promise<NodeJS.ReadableStream>;
|
|
636
|
+
readlineAsync(options?: ReadableOptions): Promise<{
|
|
637
|
+
result: number;
|
|
638
|
+
line: string;
|
|
639
|
+
}>;
|
|
640
|
+
readeofAsync(options?: ReadableOptions): Promise<{
|
|
641
|
+
result: number;
|
|
642
|
+
line: string;
|
|
643
|
+
}>;
|
|
644
|
+
hasRunningActionAsync(options?: ReadableOptions): Promise<any>;
|
|
645
|
+
readusersAsync(options?: ReadableOptions): Promise<any>;
|
|
646
|
+
readlimboAsync(options?: ReadableOptions): Promise<any>;
|
|
509
647
|
}
|
package/lib/uri.js
CHANGED
|
@@ -15,14 +15,14 @@ exports.normalizeOptions = normalizeOptions;
|
|
|
15
15
|
*/
|
|
16
16
|
const BOOLEAN_KEYS = new Set([
|
|
17
17
|
'lowercase_keys', 'blobAsText', 'wireCompression', 'manager',
|
|
18
|
-
'namedPlaceholders',
|
|
18
|
+
'namedPlaceholders', 'enableKeepAlive',
|
|
19
19
|
]);
|
|
20
20
|
/** Option keys coerced to number when they arrive as URI query parameters. */
|
|
21
21
|
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',
|
|
25
|
+
'min', 'idleTimeoutMillis', 'keepAliveInitialDelay',
|
|
26
26
|
]);
|
|
27
27
|
function coerce(key, value) {
|
|
28
28
|
if (BOOLEAN_KEYS.has(key)) {
|