node-firebird 2.8.1 → 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 +268 -7
- package/lib/index.d.ts +17 -9
- package/lib/index.js +42 -3
- package/lib/named-params.d.ts +42 -0
- package/lib/named-params.js +133 -0
- package/lib/pool.js +1 -1
- package/lib/srp.d.ts +3 -3
- package/lib/types.d.ts +185 -25
- package/lib/uri.d.ts +57 -0
- package/lib/uri.js +193 -0
- package/lib/wire/connection.d.ts +92 -59
- package/lib/wire/connection.js +286 -55
- 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 +40 -20
- package/lib/wire/statement.js +26 -6
- package/lib/wire/transaction.d.ts +32 -18
- package/lib/wire/transaction.js +50 -8
- 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 +54 -15
- package/src/messages.ts +1 -1
- package/src/named-params.ts +145 -0
- package/src/pool.ts +1 -1
- package/src/srp.ts +6 -6
- package/src/types.ts +183 -25
- package/src/unix-crypt.ts +9 -9
- package/src/uri.ts +204 -0
- package/src/wire/connection.ts +481 -234
- 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 +68 -31
- package/src/wire/transaction.ts +85 -33
- package/src/wire/wire-types.ts +127 -0
- package/src/wire/xsqlvar.ts +9 -7
package/README.md
CHANGED
|
@@ -11,9 +11,9 @@
|
|
|
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, classic connections, pooling
|
|
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, 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,14 +175,80 @@ 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)
|
|
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)
|
|
183
190
|
```
|
|
184
191
|
|
|
192
|
+
### Connection URI strings
|
|
193
|
+
|
|
194
|
+
Everywhere an options object is accepted — `attach`, `create`,
|
|
195
|
+
`attachOrCreate`, `drop`, `Firebird.pool()` and their `*Async`
|
|
196
|
+
counterparts — a `firebird://` URI string works too, which is handy for
|
|
197
|
+
12-factor apps and containers that configure the database via a single
|
|
198
|
+
environment variable:
|
|
199
|
+
|
|
200
|
+
```js
|
|
201
|
+
const db = await Firebird.attachAsync(process.env.DATABASE_URL);
|
|
202
|
+
// e.g. DATABASE_URL=firebird://SYSDBA:masterkey@db.example.com:3050//var/fb/prod.fdb?encoding=UTF8
|
|
203
|
+
|
|
204
|
+
const pool = Firebird.pool(10,
|
|
205
|
+
'firebird://app:secret@localhost/appdb?lowercase_keys=true&idleTimeoutMillis=30000');
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
The database part after `host[:port]/` can be:
|
|
209
|
+
|
|
210
|
+
| URI | database |
|
|
211
|
+
| :--- | :--- |
|
|
212
|
+
| `firebird://host/employee` | the alias `employee` |
|
|
213
|
+
| `firebird://host//var/fb/prod.fdb` | `/var/fb/prod.fdb` (explicit absolute path) |
|
|
214
|
+
| `firebird://host/var/fb/prod.fdb` | `/var/fb/prod.fdb` (a database part with `/` is a path — aliases cannot contain slashes) |
|
|
215
|
+
| `firebird://host/C:/fbdata/prod.fdb` | the Windows path `C:/fbdata/prod.fdb` |
|
|
216
|
+
|
|
217
|
+
Query parameters map 1:1 onto the connection options above and are coerced
|
|
218
|
+
to the right type (`?pageSize=8192&lowercase_keys=true&wireCompression=1`).
|
|
219
|
+
Credentials and paths are URL-decoded, so reserved characters can be
|
|
220
|
+
percent-encoded (`p%40ss` for `p@ss`); `user`/`password` may alternatively
|
|
221
|
+
be passed as query parameters. IPv6 hosts use brackets:
|
|
222
|
+
`firebird://[::1]:3050/employee`. The parser is exported as
|
|
223
|
+
`Firebird.parseConnectionUri(uri)` if you need the resulting options object.
|
|
224
|
+
|
|
225
|
+
### Traditional connection strings (old style)
|
|
226
|
+
|
|
227
|
+
The classic Firebird connection string format — the same
|
|
228
|
+
`[host[/port]:]{path | alias}` strings isql and the other Firebird tools
|
|
229
|
+
use — is accepted everywhere too:
|
|
230
|
+
|
|
231
|
+
```js
|
|
232
|
+
const db = await Firebird.attachAsync('db.example.com/3051:/var/fb/prod.fdb');
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
| Connection string | meaning |
|
|
236
|
+
| :--- | :--- |
|
|
237
|
+
| `employee` | the alias `employee` on `127.0.0.1:3050` |
|
|
238
|
+
| `/var/fb/prod.fdb` | a path on `127.0.0.1:3050` |
|
|
239
|
+
| `db.example.com:employee` | the alias `employee` on `db.example.com:3050` |
|
|
240
|
+
| `db.example.com/3051:/var/fb/prod.fdb` | host and explicit port |
|
|
241
|
+
| `myserver:C:\fbdata\prod.fdb` | a Windows path behind a host |
|
|
242
|
+
| `C:\fbdata\prod.fdb` | a single character before `:` is a drive letter, not a host (same rule as Firebird) |
|
|
243
|
+
| `[::1]/3050:employee` | IPv6 hosts use brackets |
|
|
244
|
+
|
|
245
|
+
Unlike `firebird://` URIs, traditional strings carry no credentials or
|
|
246
|
+
options — the driver defaults apply (`SYSDBA`/`masterkey`, port 3050), and
|
|
247
|
+
the port must be numeric (`/etc/services` names are not resolved). Use the
|
|
248
|
+
URI form or an options object when you need to set anything else.
|
|
249
|
+
`Firebird.parseConnectionString(str)` parses both forms and is what
|
|
250
|
+
`attach`/`create`/`pool` use internally for string arguments.
|
|
251
|
+
|
|
185
252
|
### Classic
|
|
186
253
|
|
|
187
254
|
```js
|
|
@@ -385,6 +452,174 @@ Firebird.attach(options, function (err, db) {
|
|
|
385
452
|
});
|
|
386
453
|
```
|
|
387
454
|
|
|
455
|
+
### Named placeholders
|
|
456
|
+
|
|
457
|
+
With the `namedPlaceholders: true` connection option, SQL may use `:name`
|
|
458
|
+
markers and parameters may be passed as a values-by-name object instead of
|
|
459
|
+
a positional array. The rewrite happens client-side before the statement is
|
|
460
|
+
prepared, so it works on every Firebird version; positional `?` arrays keep
|
|
461
|
+
working unchanged on the same connection.
|
|
462
|
+
|
|
463
|
+
```js
|
|
464
|
+
const db = await Firebird.attachAsync({ ...options, namedPlaceholders: true });
|
|
465
|
+
// or: firebird://user:pass@host/db?namedPlaceholders=true
|
|
466
|
+
|
|
467
|
+
const rows = await db.queryAsync(
|
|
468
|
+
'SELECT * FROM USERS WHERE ALIAS = :alias AND CREATED > :since',
|
|
469
|
+
{ alias: 'Peter', since: new Date(2026, 0, 1) });
|
|
470
|
+
|
|
471
|
+
// A name may repeat — it binds once per occurrence:
|
|
472
|
+
await db.queryAsync(
|
|
473
|
+
'SELECT * FROM T WHERE A = :v OR B = :v', { v: 42 });
|
|
474
|
+
|
|
475
|
+
// Batch rows can be objects too (Firebird 4.0+):
|
|
476
|
+
await db.executeBatchAsync(
|
|
477
|
+
'INSERT INTO USERS (ID, ALIAS) VALUES (:id, :alias)',
|
|
478
|
+
[{ id: 1, alias: 'a' }, { id: 2, alias: 'b' }]);
|
|
479
|
+
```
|
|
480
|
+
|
|
481
|
+
Placeholders inside string literals (`'...'`), quoted identifiers (`"..."`),
|
|
482
|
+
comments and `q'{...}'` alternative literals are left untouched. A key
|
|
483
|
+
present with value `null` binds SQL `NULL`; a *missing* key raises
|
|
484
|
+
`Missing value for named placeholder(s): ...`.
|
|
485
|
+
|
|
486
|
+
The scanner has no SQL grammar, so inside an `EXECUTE BLOCK` body every
|
|
487
|
+
PSQL `:variable` reference looks like a placeholder too. The option is
|
|
488
|
+
therefore off by default — and can be disabled for a single statement with
|
|
489
|
+
the per-query option:
|
|
490
|
+
|
|
491
|
+
```js
|
|
492
|
+
await db.queryAsync(execBlockSql, [], { namedPlaceholders: false });
|
|
493
|
+
```
|
|
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
|
+
|
|
388
623
|
### Tablespaces and Schema Partitioning (Firebird 6.0+)
|
|
389
624
|
|
|
390
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.
|
|
@@ -919,6 +1154,26 @@ var fbsvc = {
|
|
|
919
1154
|
|
|
920
1155
|
```
|
|
921
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
|
+
|
|
922
1177
|
### Backup Service example
|
|
923
1178
|
|
|
924
1179
|
```js
|
|
@@ -1657,13 +1912,13 @@ db.transaction(function (err, tx) {
|
|
|
1657
1912
|
|
|
1658
1913
|
#### Is the wire protocol version hard-coded?
|
|
1659
1914
|
|
|
1660
|
-
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):
|
|
1661
1916
|
|
|
1662
1917
|
```js
|
|
1663
|
-
options.maxNegotiatedProtocols =
|
|
1918
|
+
options.maxNegotiatedProtocols = 10; // stop at Protocol 19 (pre-Firebird 6 behavior)
|
|
1664
1919
|
```
|
|
1665
1920
|
|
|
1666
|
-
|
|
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.
|
|
1667
1922
|
|
|
1668
1923
|
#### BLOB reads/writes are very slow, especially for large files over a remote connection
|
|
1669
1924
|
|
|
@@ -1720,6 +1975,12 @@ db.query('SELECT * FROM ACTORS WHERE NAME LIKE ?', ['James Wick%'], function (er
|
|
|
1720
1975
|
});
|
|
1721
1976
|
```
|
|
1722
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
|
+
|
|
1723
1984
|
## Contributing
|
|
1724
1985
|
|
|
1725
1986
|
Contributions are welcome — code, documentation, and bug reports alike.
|
package/lib/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import Connection from './wire/connection';
|
|
2
2
|
import { escape as escapeValue } from './utils';
|
|
3
|
+
import { parseConnectionUri, parseConnectionString } from './uri';
|
|
3
4
|
import type { Options, SvcMgrOptions, DatabaseCallback, ServiceManagerCallback, SimpleCallback, ConnectionPool, Database, ServiceManager } from './types';
|
|
4
5
|
export * from './types';
|
|
5
6
|
export { GDSCode } from './gdscodes';
|
|
@@ -23,20 +24,27 @@ export declare const ISOLATION_REPEATABLE_READ: number[];
|
|
|
23
24
|
export declare const ISOLATION_SERIALIZABLE: number[];
|
|
24
25
|
export declare const ISOLATION_READ_COMMITTED_READ_ONLY: number[];
|
|
25
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>>;
|
|
26
32
|
/**
|
|
27
33
|
* The most recent Connection created by attach()/create()/attachOrCreate().
|
|
28
34
|
* Kept for backwards compatibility with the previous CommonJS module where
|
|
29
35
|
* the connection was stored on the module object itself.
|
|
30
36
|
*/
|
|
31
37
|
export declare let connection: Connection | undefined;
|
|
32
|
-
export declare function attach(options: Options, callback: DatabaseCallback): void;
|
|
38
|
+
export declare function attach(options: Options | string, callback: DatabaseCallback): void;
|
|
33
39
|
export declare function attach(options: SvcMgrOptions, callback: ServiceManagerCallback): void;
|
|
34
|
-
export declare function drop(options: Options, callback: SimpleCallback): void;
|
|
35
|
-
export declare function create(options: Options, callback: DatabaseCallback): void;
|
|
36
|
-
export declare function attachOrCreate(options: Options, callback: DatabaseCallback): void;
|
|
37
|
-
export declare function pool(max: number, options: Options): ConnectionPool;
|
|
40
|
+
export declare function drop(options: Options | string, callback: SimpleCallback): void;
|
|
41
|
+
export declare function create(options: Options | string, callback: DatabaseCallback): void;
|
|
42
|
+
export declare function attachOrCreate(options: Options | string, callback: DatabaseCallback): void;
|
|
43
|
+
export declare function pool(max: number, options: Options | string): ConnectionPool;
|
|
44
|
+
export { parseConnectionUri, parseConnectionString };
|
|
45
|
+
export { parseNamedPlaceholders } from './named-params';
|
|
38
46
|
export declare function attachAsync(options: SvcMgrOptions): Promise<ServiceManager>;
|
|
39
|
-
export declare function attachAsync(options: Options): Promise<Database>;
|
|
40
|
-
export declare function createAsync(options: Options): Promise<Database>;
|
|
41
|
-
export declare function attachOrCreateAsync(options: Options): Promise<Database>;
|
|
42
|
-
export declare function dropAsync(options: Options): Promise<void>;
|
|
47
|
+
export declare function attachAsync(options: Options | string): Promise<Database>;
|
|
48
|
+
export declare function createAsync(options: Options | string): Promise<Database>;
|
|
49
|
+
export declare function attachOrCreateAsync(options: Options | string): Promise<Database>;
|
|
50
|
+
export declare function dropAsync(options: Options | string): Promise<void>;
|
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.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;
|
|
@@ -32,6 +32,9 @@ const callback_1 = require("./callback");
|
|
|
32
32
|
const connection_1 = __importDefault(require("./wire/connection"));
|
|
33
33
|
const pool_1 = __importDefault(require("./pool"));
|
|
34
34
|
const utils_1 = require("./utils");
|
|
35
|
+
const uri_1 = require("./uri");
|
|
36
|
+
Object.defineProperty(exports, "parseConnectionUri", { enumerable: true, get: function () { return uri_1.parseConnectionUri; } });
|
|
37
|
+
Object.defineProperty(exports, "parseConnectionString", { enumerable: true, get: function () { return uri_1.parseConnectionString; } });
|
|
35
38
|
__exportStar(require("./types"), exports);
|
|
36
39
|
var gdscodes_1 = require("./gdscodes");
|
|
37
40
|
Object.defineProperty(exports, "GDSCode", { enumerable: true, get: function () { return gdscodes_1.GDSCode; } });
|
|
@@ -60,7 +63,37 @@ exports.ISOLATION_REPEATABLE_READ = const_1.default.ISOLATION_REPEATABLE_READ;
|
|
|
60
63
|
exports.ISOLATION_SERIALIZABLE = const_1.default.ISOLATION_SERIALIZABLE;
|
|
61
64
|
exports.ISOLATION_READ_COMMITTED_READ_ONLY = const_1.default.ISOLATION_READ_COMMITTED_READ_ONLY;
|
|
62
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
|
+
});
|
|
63
95
|
function attach(options, callback) {
|
|
96
|
+
options = (0, uri_1.normalizeOptions)(options);
|
|
64
97
|
var host = options.host || const_1.default.DEFAULT_HOST;
|
|
65
98
|
var port = options.port || const_1.default.DEFAULT_PORT;
|
|
66
99
|
var manager = options.manager || false;
|
|
@@ -83,7 +116,7 @@ function attach(options, callback) {
|
|
|
83
116
|
}, options);
|
|
84
117
|
}
|
|
85
118
|
function drop(options, callback) {
|
|
86
|
-
attach(options, function (err, db) {
|
|
119
|
+
attach((0, uri_1.normalizeOptions)(options), function (err, db) {
|
|
87
120
|
if (err) {
|
|
88
121
|
callback({ error: err, message: "Drop error" });
|
|
89
122
|
return;
|
|
@@ -92,6 +125,7 @@ function drop(options, callback) {
|
|
|
92
125
|
});
|
|
93
126
|
}
|
|
94
127
|
function create(options, callback) {
|
|
128
|
+
options = (0, uri_1.normalizeOptions)(options);
|
|
95
129
|
var host = options.host || const_1.default.DEFAULT_HOST;
|
|
96
130
|
var port = options.port || const_1.default.DEFAULT_PORT;
|
|
97
131
|
var cnx = exports.connection = new connection_1.default(host, port, function (err) {
|
|
@@ -112,6 +146,7 @@ function create(options, callback) {
|
|
|
112
146
|
}, options);
|
|
113
147
|
}
|
|
114
148
|
function attachOrCreate(options, callback) {
|
|
149
|
+
options = (0, uri_1.normalizeOptions)(options);
|
|
115
150
|
var host = options.host || const_1.default.DEFAULT_HOST;
|
|
116
151
|
var port = options.port || const_1.default.DEFAULT_PORT;
|
|
117
152
|
var cnx = exports.connection = new connection_1.default(host, port, function (err) {
|
|
@@ -129,6 +164,8 @@ function attachOrCreate(options, callback) {
|
|
|
129
164
|
if (!err) {
|
|
130
165
|
if (self.db)
|
|
131
166
|
self.db.emit('connect', ret);
|
|
167
|
+
// DatabaseCallback stays permissive (db non-optional) for
|
|
168
|
+
// API users; internally the error path passes no db
|
|
132
169
|
(0, callback_1.doCallback)(ret, callback);
|
|
133
170
|
return;
|
|
134
171
|
}
|
|
@@ -139,8 +176,10 @@ function attachOrCreate(options, callback) {
|
|
|
139
176
|
}
|
|
140
177
|
// Pooling
|
|
141
178
|
function pool(max, options) {
|
|
142
|
-
return new pool_1.default(attach, max, Object.assign({}, options, { isPool: true }));
|
|
179
|
+
return new pool_1.default(attach, max, Object.assign({}, (0, uri_1.normalizeOptions)(options), { isPool: true }));
|
|
143
180
|
}
|
|
181
|
+
var named_params_1 = require("./named-params");
|
|
182
|
+
Object.defineProperty(exports, "parseNamedPlaceholders", { enumerable: true, get: function () { return named_params_1.parseNamedPlaceholders; } });
|
|
144
183
|
function attachAsync(options) {
|
|
145
184
|
return (0, callback_1.fromCallback)(function (cb) { attach(options, cb); });
|
|
146
185
|
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/***************************************
|
|
2
|
+
*
|
|
3
|
+
* Named placeholders (:name → ?)
|
|
4
|
+
*
|
|
5
|
+
***************************************/
|
|
6
|
+
/**
|
|
7
|
+
* Result of scanning a SQL string for named placeholders.
|
|
8
|
+
*/
|
|
9
|
+
export interface ParsedNamedPlaceholders {
|
|
10
|
+
/** SQL with every named placeholder replaced by a positional "?". */
|
|
11
|
+
sql: string;
|
|
12
|
+
/**
|
|
13
|
+
* Placeholder names in positional order (a repeated name appears once
|
|
14
|
+
* per occurrence), or null when the SQL contains none.
|
|
15
|
+
*/
|
|
16
|
+
names: string[] | null;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Scan `sql` for named placeholders (`:name`) and rewrite them to positional
|
|
20
|
+
* `?` markers, returning the rewritten SQL and the names in positional
|
|
21
|
+
* order. Placeholders inside string literals ('...'), quoted identifiers
|
|
22
|
+
* ("..."), line comments (--), block comments and Firebird alternative
|
|
23
|
+
* string literals (q'{...}') are left untouched.
|
|
24
|
+
*
|
|
25
|
+
* Note: the scanner has no SQL grammar — inside an EXECUTE BLOCK body every
|
|
26
|
+
* `:variable` reference looks like a placeholder too. Use positional params
|
|
27
|
+
* (or per-call `namedPlaceholders: false`) for EXECUTE BLOCK.
|
|
28
|
+
*/
|
|
29
|
+
export declare function parseNamedPlaceholders(sql: string): ParsedNamedPlaceholders;
|
|
30
|
+
/**
|
|
31
|
+
* True when `params` is a plain values-by-name object (and not one of the
|
|
32
|
+
* values the driver accepts as a single positional parameter, like Date or
|
|
33
|
+
* Buffer).
|
|
34
|
+
*/
|
|
35
|
+
export declare function isNamedParamsObject(params: any): params is Record<string, any>;
|
|
36
|
+
/**
|
|
37
|
+
* Map a values-by-name object onto the positional order collected by
|
|
38
|
+
* parseNamedPlaceholders. A name may be bound multiple times; every name
|
|
39
|
+
* must be an own property of `params` (a present key holding null is a
|
|
40
|
+
* NULL parameter, a missing key is an error).
|
|
41
|
+
*/
|
|
42
|
+
export declare function bindNamedParams(names: string[], params: Record<string, any>): any[];
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/***************************************
|
|
3
|
+
*
|
|
4
|
+
* Named placeholders (:name → ?)
|
|
5
|
+
*
|
|
6
|
+
***************************************/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.parseNamedPlaceholders = parseNamedPlaceholders;
|
|
9
|
+
exports.isNamedParamsObject = isNamedParamsObject;
|
|
10
|
+
exports.bindNamedParams = bindNamedParams;
|
|
11
|
+
const IDENT_START = /[A-Za-z_]/;
|
|
12
|
+
const IDENT_PART = /[A-Za-z0-9_$]/;
|
|
13
|
+
// Parsing is pure string work, so identical SQL (the common case with
|
|
14
|
+
// query builders and hot paths) is scanned only once.
|
|
15
|
+
const CACHE_MAX = 100;
|
|
16
|
+
const cache = new Map();
|
|
17
|
+
/**
|
|
18
|
+
* Scan `sql` for named placeholders (`:name`) and rewrite them to positional
|
|
19
|
+
* `?` markers, returning the rewritten SQL and the names in positional
|
|
20
|
+
* order. Placeholders inside string literals ('...'), quoted identifiers
|
|
21
|
+
* ("..."), line comments (--), block comments and Firebird alternative
|
|
22
|
+
* string literals (q'{...}') are left untouched.
|
|
23
|
+
*
|
|
24
|
+
* Note: the scanner has no SQL grammar — inside an EXECUTE BLOCK body every
|
|
25
|
+
* `:variable` reference looks like a placeholder too. Use positional params
|
|
26
|
+
* (or per-call `namedPlaceholders: false`) for EXECUTE BLOCK.
|
|
27
|
+
*/
|
|
28
|
+
function parseNamedPlaceholders(sql) {
|
|
29
|
+
var cached = cache.get(sql);
|
|
30
|
+
if (cached)
|
|
31
|
+
return cached;
|
|
32
|
+
var out = '';
|
|
33
|
+
var names = [];
|
|
34
|
+
var i = 0;
|
|
35
|
+
var n = sql.length;
|
|
36
|
+
while (i < n) {
|
|
37
|
+
var c = sql[i];
|
|
38
|
+
if (c === "'" || c === '"') {
|
|
39
|
+
// string literal or quoted identifier; doubled quotes escape
|
|
40
|
+
var quote = c;
|
|
41
|
+
var end = i + 1;
|
|
42
|
+
while (end < n) {
|
|
43
|
+
if (sql[end] === quote) {
|
|
44
|
+
if (sql[end + 1] === quote) {
|
|
45
|
+
end += 2;
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
end++;
|
|
49
|
+
break;
|
|
50
|
+
}
|
|
51
|
+
end++;
|
|
52
|
+
}
|
|
53
|
+
out += sql.slice(i, end);
|
|
54
|
+
i = end;
|
|
55
|
+
}
|
|
56
|
+
else if (c === '-' && sql[i + 1] === '-') {
|
|
57
|
+
var eol = sql.indexOf('\n', i);
|
|
58
|
+
if (eol === -1)
|
|
59
|
+
eol = n;
|
|
60
|
+
out += sql.slice(i, eol);
|
|
61
|
+
i = eol;
|
|
62
|
+
}
|
|
63
|
+
else if (c === '/' && sql[i + 1] === '*') {
|
|
64
|
+
var close = sql.indexOf('*/', i + 2);
|
|
65
|
+
close = close === -1 ? n : close + 2;
|
|
66
|
+
out += sql.slice(i, close);
|
|
67
|
+
i = close;
|
|
68
|
+
}
|
|
69
|
+
else if ((c === 'q' || c === 'Q') && sql[i + 1] === "'" && i + 2 < n &&
|
|
70
|
+
(i === 0 || !IDENT_PART.test(sql[i - 1]))) {
|
|
71
|
+
// Firebird 3+ alternative string literal: q'{...}' / q'!...!'
|
|
72
|
+
var open = sql[i + 2];
|
|
73
|
+
var closer = open === '(' ? ')'
|
|
74
|
+
: open === '[' ? ']'
|
|
75
|
+
: open === '{' ? '}'
|
|
76
|
+
: open === '<' ? '>'
|
|
77
|
+
: open;
|
|
78
|
+
var stop = sql.indexOf(closer + "'", i + 3);
|
|
79
|
+
stop = stop === -1 ? n : stop + 2;
|
|
80
|
+
out += sql.slice(i, stop);
|
|
81
|
+
i = stop;
|
|
82
|
+
}
|
|
83
|
+
else if (c === ':' && i + 1 < n && IDENT_START.test(sql[i + 1])) {
|
|
84
|
+
var end2 = i + 2;
|
|
85
|
+
while (end2 < n && IDENT_PART.test(sql[end2]))
|
|
86
|
+
end2++;
|
|
87
|
+
names.push(sql.slice(i + 1, end2));
|
|
88
|
+
out += '?';
|
|
89
|
+
i = end2;
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
out += c;
|
|
93
|
+
i++;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
var result = names.length
|
|
97
|
+
? { sql: out, names: names }
|
|
98
|
+
: { sql: sql, names: null };
|
|
99
|
+
if (cache.size >= CACHE_MAX) {
|
|
100
|
+
cache.delete(cache.keys().next().value);
|
|
101
|
+
}
|
|
102
|
+
cache.set(sql, result);
|
|
103
|
+
return result;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* True when `params` is a plain values-by-name object (and not one of the
|
|
107
|
+
* values the driver accepts as a single positional parameter, like Date or
|
|
108
|
+
* Buffer).
|
|
109
|
+
*/
|
|
110
|
+
function isNamedParamsObject(params) {
|
|
111
|
+
return params !== null &&
|
|
112
|
+
typeof params === 'object' &&
|
|
113
|
+
!Array.isArray(params) &&
|
|
114
|
+
!Buffer.isBuffer(params) &&
|
|
115
|
+
!(params instanceof Date);
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Map a values-by-name object onto the positional order collected by
|
|
119
|
+
* parseNamedPlaceholders. A name may be bound multiple times; every name
|
|
120
|
+
* must be an own property of `params` (a present key holding null is a
|
|
121
|
+
* NULL parameter, a missing key is an error).
|
|
122
|
+
*/
|
|
123
|
+
function bindNamedParams(names, params) {
|
|
124
|
+
var missing = [];
|
|
125
|
+
var values = names.map(function (name) {
|
|
126
|
+
if (!Object.prototype.hasOwnProperty.call(params, name))
|
|
127
|
+
missing.push(name);
|
|
128
|
+
return params[name];
|
|
129
|
+
});
|
|
130
|
+
if (missing.length)
|
|
131
|
+
throw new Error('Missing value for named placeholder(s): ' + missing.join(', '));
|
|
132
|
+
return values;
|
|
133
|
+
}
|
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) {
|