node-firebird 2.4.2 → 2.6.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 +94 -1
- package/lib/callback.d.ts +13 -0
- package/lib/callback.js +31 -0
- package/lib/index.d.ts +6 -1
- package/lib/index.js +16 -0
- package/lib/pool.d.ts +7 -0
- package/lib/pool.js +25 -0
- package/lib/types.d.ts +49 -0
- package/lib/wire/connection.d.ts +7 -0
- package/lib/wire/connection.js +45 -14
- package/lib/wire/const.d.ts +4 -0
- package/lib/wire/const.js +8 -0
- package/lib/wire/database.d.ts +24 -0
- package/lib/wire/database.js +85 -0
- package/lib/wire/statement.d.ts +7 -0
- package/lib/wire/statement.js +30 -0
- package/lib/wire/transaction.d.ts +8 -5
- package/lib/wire/transaction.js +76 -0
- package/package.json +1 -1
- package/src/callback.ts +36 -0
- package/src/index.ts +28 -1
- package/src/pool.ts +28 -0
- package/src/types.ts +58 -0
- package/src/wire/connection.ts +51 -14
- package/src/wire/const.ts +9 -0
- package/src/wire/database.ts +97 -1
- package/src/wire/statement.ts +39 -0
- package/src/wire/transaction.ts +89 -1
package/README.md
CHANGED
|
@@ -10,10 +10,11 @@
|
|
|
10
10
|
|
|
11
11
|
- [Installation](#installation)
|
|
12
12
|
- [Usage](#usage) — including [developing the driver](#developing-the-driver)
|
|
13
|
+
- [Promises and async/await](#promises-and-asyncawait) — the `*Async` API plus `withConnection` / `withTransaction` helpers
|
|
13
14
|
- [Connection types](#connection-types) — connection options, classic connections, pooling
|
|
14
15
|
- [Database object (db)](#database-object-db) — database, transaction and statement methods/options
|
|
15
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
|
-
- [Extensive Examples](#extensive-examples) — DECFLOAT/INT128, statement timeouts, scrollable cursors, RETURNING multiple rows, SKIP LOCKED, advanced pooling
|
|
17
|
+
- [Extensive Examples](#extensive-examples) — DECFLOAT/INT128, query cancellation (AbortSignal), statement timeouts, scrollable cursors, RETURNING multiple rows, SKIP LOCKED, advanced pooling
|
|
17
18
|
- [Using node-firebird with Express.js](#using-node-firebird-with-expressjs)
|
|
18
19
|
- [FAQ](#faq)
|
|
19
20
|
- [Contributing](#contributing) · [Contributors](#contributors)
|
|
@@ -98,6 +99,55 @@ npm test # build + run the vitest suite (unit + integration)
|
|
|
98
99
|
- `Firebird.create(options, function(err, db))` create a database
|
|
99
100
|
- `Firebird.attachOrCreate(options, function(err, db))` attach or create database
|
|
100
101
|
- `Firebird.pool(max, options) -> return {Object}` create a connection pooling
|
|
102
|
+
- `Firebird.attachAsync(options) -> Promise<Database>`, `createAsync`, `attachOrCreateAsync`, `dropAsync` — promise counterparts, see [Promises and async/await](#promises-and-asyncawait)
|
|
103
|
+
|
|
104
|
+
## Promises and async/await
|
|
105
|
+
|
|
106
|
+
Every callback API has a promise-returning counterpart with an `Async`
|
|
107
|
+
suffix, plus two higher-level helpers: `pool.withConnection()` and
|
|
108
|
+
`db.withTransaction()`. The callback API is unchanged and the two styles can
|
|
109
|
+
be mixed freely, though sticking to one per project keeps code readable.
|
|
110
|
+
|
|
111
|
+
```js
|
|
112
|
+
const Firebird = require('node-firebird');
|
|
113
|
+
|
|
114
|
+
const pool = Firebird.pool(5, options);
|
|
115
|
+
|
|
116
|
+
// acquire → work → always release, even when `work` throws
|
|
117
|
+
const users = await pool.withConnection((db) =>
|
|
118
|
+
db.queryAsync('SELECT id, name FROM users WHERE plan = ?', ['pro'])
|
|
119
|
+
);
|
|
120
|
+
|
|
121
|
+
// commit on success, rollback on error
|
|
122
|
+
await pool.withConnection((db) =>
|
|
123
|
+
db.withTransaction(async (transaction) => {
|
|
124
|
+
await transaction.executeAsync('INSERT INTO audit (msg) VALUES (?)', ['signup']);
|
|
125
|
+
await transaction.executeAsync('UPDATE stats SET signups = signups + 1');
|
|
126
|
+
})
|
|
127
|
+
);
|
|
128
|
+
|
|
129
|
+
await pool.destroyAsync();
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
Available wrappers:
|
|
133
|
+
|
|
134
|
+
- **module** — `Firebird.attachAsync(options)`, `createAsync`, `attachOrCreateAsync`, `dropAsync`; resolve with a `Database` (or a `ServiceManager` when `options.manager` is `true`)
|
|
135
|
+
- **pool** — `pool.getAsync()`, `pool.destroyAsync()`, `pool.withConnection(work)`
|
|
136
|
+
- **database** — `db.queryAsync(sql, params?, options?)`, `executeAsync`, `sequentiallyAsync(sql, params?, onRow, options?)`, `transactionAsync(options?)`, `newStatementAsync(sql)`, `attachEventAsync()`, `detachAsync()`, `dropAsync()`, `db.withTransaction(work, options?)`
|
|
137
|
+
- **transaction** — `queryAsync`, `executeAsync`, `sequentiallyAsync`, `newStatementAsync`, `commitAsync`, `rollbackAsync`, `commitRetainingAsync`, `rollbackRetainingAsync`
|
|
138
|
+
- **statement** — `executeAsync(transaction, params?, options?)`, `fetchAsync`, `fetchScrollAsync`, `fetchAllAsync`, `closeAsync`, `dropAsync`, `releaseAsync`
|
|
139
|
+
|
|
140
|
+
Notes:
|
|
141
|
+
|
|
142
|
+
- Rejections are always `Error` instances carrying the usual Firebird
|
|
143
|
+
properties (`err.gdscode`, `err.gdsparams`) — see [Using GDS codes](#using-gds-codes).
|
|
144
|
+
- `queryAsync` / `executeAsync` resolve with the rows only; column metadata
|
|
145
|
+
is currently available through the callback API only.
|
|
146
|
+
- An un-`await`ed rejected promise becomes an unhandled rejection instead of
|
|
147
|
+
a callback error — prefer the `withConnection` / `withTransaction` helpers,
|
|
148
|
+
which guarantee cleanup on every path.
|
|
149
|
+
- TypeScript: the async methods accept a row-shape generic, e.g.
|
|
150
|
+
`db.queryAsync<User>(sql, params)` returns `Promise<User[]>`.
|
|
101
151
|
|
|
102
152
|
## Connection types
|
|
103
153
|
|
|
@@ -1101,6 +1151,47 @@ Firebird.attach({
|
|
|
1101
1151
|
});
|
|
1102
1152
|
```
|
|
1103
1153
|
|
|
1154
|
+
### Query Cancellation with AbortSignal (Firebird 2.5+)
|
|
1155
|
+
|
|
1156
|
+
Any query can be cancelled while it is executing on the server. Pass an
|
|
1157
|
+
`AbortSignal` in the query options — when it fires, the driver sends an
|
|
1158
|
+
out-of-band `op_cancel` packet and the running statement fails with
|
|
1159
|
+
`err.gdscode === GDSCode.CANCELLED`. The connection itself stays healthy and
|
|
1160
|
+
can run further queries immediately.
|
|
1161
|
+
|
|
1162
|
+
```js
|
|
1163
|
+
const { GDSCode } = require('node-firebird/lib/gdscodes');
|
|
1164
|
+
|
|
1165
|
+
const controller = new AbortController();
|
|
1166
|
+
setTimeout(() => controller.abort(), 5000); // or req.on('close', ...) in Express
|
|
1167
|
+
|
|
1168
|
+
try {
|
|
1169
|
+
const rows = await db.queryAsync('SELECT /* expensive */ ...', [], { signal: controller.signal });
|
|
1170
|
+
} catch (err) {
|
|
1171
|
+
if (err.gdscode === GDSCode.CANCELLED) {
|
|
1172
|
+
// cancelled server-side; the connection remains usable
|
|
1173
|
+
} else if (err.name === 'AbortError') {
|
|
1174
|
+
// the signal was already aborted — the query was never sent
|
|
1175
|
+
} else {
|
|
1176
|
+
throw err;
|
|
1177
|
+
}
|
|
1178
|
+
}
|
|
1179
|
+
```
|
|
1180
|
+
|
|
1181
|
+
The option works with the callback API as well (`db.query(sql, params, cb,
|
|
1182
|
+
{ signal })`) and on transaction-level queries. A running operation can also
|
|
1183
|
+
be cancelled manually from anywhere with `db.cancel()` / `await
|
|
1184
|
+
db.cancelAsync()`.
|
|
1185
|
+
|
|
1186
|
+
Notes:
|
|
1187
|
+
|
|
1188
|
+
- Cancellation is **per attachment** (that is how the Firebird protocol
|
|
1189
|
+
defines it): it cancels whatever is currently executing on that
|
|
1190
|
+
connection. With a pool this is naturally scoped to the request holding
|
|
1191
|
+
the connection.
|
|
1192
|
+
- A signal that is already aborted rejects immediately with an `AbortError`
|
|
1193
|
+
without contacting the server; cancelling an idle connection is harmless.
|
|
1194
|
+
|
|
1104
1195
|
### Statement Timeouts (Firebird 4.0+)
|
|
1105
1196
|
Setting a statement timeout allows the client to automatically abort queries that take too long on the server.
|
|
1106
1197
|
```js
|
|
@@ -1230,6 +1321,8 @@ process.on('SIGTERM', function() {
|
|
|
1230
1321
|
|
|
1231
1322
|
node-firebird works well with Express, but because the driver is connection/pool based rather than an ORM with automatic connection management, a few request-lifecycle patterns keep connections from leaking under load. This section documents the recommended architecture, referenced from the [roadmap](ROADMAP.md#2-expressjs-support-first-class-integration).
|
|
1232
1323
|
|
|
1324
|
+
> The driver now ships native `pool.withConnection()` and `db.withTransaction()` helpers (see [Promises and async/await](#promises-and-asyncawait)) that supersede the hand-rolled `withConnection` / `transactional` helpers shown in the examples below. The examples remain valid and show what the helpers do under the hood.
|
|
1325
|
+
|
|
1233
1326
|
### Recommended architecture: one pool per app, created at startup
|
|
1234
1327
|
|
|
1235
1328
|
Create a single `Firebird.pool(...)` when the app boots and reuse it for the lifetime of the process. Do **not** call `Firebird.pool()` or `Firebird.attach()` inside a request handler — that opens a new socket (or an entire new pool) on every request and will exhaust server and database resources under load.
|
package/lib/callback.d.ts
CHANGED
|
@@ -16,5 +16,18 @@ export interface FbError extends Error {
|
|
|
16
16
|
}
|
|
17
17
|
export type SimpleCallback = (err?: any) => void;
|
|
18
18
|
export type Callback<T = any> = (err?: any, result?: T) => void;
|
|
19
|
+
/**
|
|
20
|
+
* Normalize the values the driver passes as the callback error argument.
|
|
21
|
+
* Most code paths already deliver Error instances, but a few older ones
|
|
22
|
+
* pass plain objects (status vectors, `{error, message}` wrappers). A
|
|
23
|
+
* Promise must reject with an Error, so wrap those while preserving all
|
|
24
|
+
* their properties (gdscode, gdsparams, status, sqlcode, ...).
|
|
25
|
+
*/
|
|
26
|
+
export declare function toError(err: any): Error;
|
|
27
|
+
/**
|
|
28
|
+
* Run a callback-style operation and return a Promise for its result.
|
|
29
|
+
* Usage: fromCallback<Database>(cb => attach(options, cb))
|
|
30
|
+
*/
|
|
31
|
+
export declare function fromCallback<T = any>(executor: (cb: Callback<T>) => void): Promise<T>;
|
|
19
32
|
export declare function doError(obj: any, callback?: (...args: any[]) => void): void;
|
|
20
33
|
export declare function doCallback<T>(obj: T, callback?: Callback<T>): void;
|
package/lib/callback.js
CHANGED
|
@@ -1,7 +1,38 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.toError = toError;
|
|
4
|
+
exports.fromCallback = fromCallback;
|
|
3
5
|
exports.doError = doError;
|
|
4
6
|
exports.doCallback = doCallback;
|
|
7
|
+
/**
|
|
8
|
+
* Normalize the values the driver passes as the callback error argument.
|
|
9
|
+
* Most code paths already deliver Error instances, but a few older ones
|
|
10
|
+
* pass plain objects (status vectors, `{error, message}` wrappers). A
|
|
11
|
+
* Promise must reject with an Error, so wrap those while preserving all
|
|
12
|
+
* their properties (gdscode, gdsparams, status, sqlcode, ...).
|
|
13
|
+
*/
|
|
14
|
+
function toError(err) {
|
|
15
|
+
if (err instanceof Error)
|
|
16
|
+
return err;
|
|
17
|
+
var error = new Error(err != null && typeof err === 'object' && err.message ? err.message : String(err));
|
|
18
|
+
if (err != null && typeof err === 'object')
|
|
19
|
+
Object.assign(error, err);
|
|
20
|
+
return error;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Run a callback-style operation and return a Promise for its result.
|
|
24
|
+
* Usage: fromCallback<Database>(cb => attach(options, cb))
|
|
25
|
+
*/
|
|
26
|
+
function fromCallback(executor) {
|
|
27
|
+
return new Promise(function (resolve, reject) {
|
|
28
|
+
executor(function (err, result) {
|
|
29
|
+
if (err)
|
|
30
|
+
reject(toError(err));
|
|
31
|
+
else
|
|
32
|
+
resolve(result);
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
}
|
|
5
36
|
function doError(obj, callback) {
|
|
6
37
|
if (callback)
|
|
7
38
|
callback(obj);
|
package/lib/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import Connection from './wire/connection';
|
|
2
2
|
import { escape as escapeValue } from './utils';
|
|
3
|
-
import type { Options, SvcMgrOptions, DatabaseCallback, ServiceManagerCallback, SimpleCallback, ConnectionPool } from './types';
|
|
3
|
+
import type { Options, SvcMgrOptions, DatabaseCallback, ServiceManagerCallback, SimpleCallback, ConnectionPool, Database, ServiceManager } from './types';
|
|
4
4
|
export * from './types';
|
|
5
5
|
export { GDSCode } from './gdscodes';
|
|
6
6
|
export declare const AUTH_PLUGIN_LEGACY: string;
|
|
@@ -35,3 +35,8 @@ export declare function drop(options: Options, callback: SimpleCallback): void;
|
|
|
35
35
|
export declare function create(options: Options, callback: DatabaseCallback): void;
|
|
36
36
|
export declare function attachOrCreate(options: Options, callback: DatabaseCallback): void;
|
|
37
37
|
export declare function pool(max: number, options: Options): ConnectionPool;
|
|
38
|
+
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>;
|
package/lib/index.js
CHANGED
|
@@ -23,6 +23,10 @@ exports.drop = drop;
|
|
|
23
23
|
exports.create = create;
|
|
24
24
|
exports.attachOrCreate = attachOrCreate;
|
|
25
25
|
exports.pool = pool;
|
|
26
|
+
exports.attachAsync = attachAsync;
|
|
27
|
+
exports.createAsync = createAsync;
|
|
28
|
+
exports.attachOrCreateAsync = attachOrCreateAsync;
|
|
29
|
+
exports.dropAsync = dropAsync;
|
|
26
30
|
const const_1 = __importDefault(require("./wire/const"));
|
|
27
31
|
const callback_1 = require("./callback");
|
|
28
32
|
const connection_1 = __importDefault(require("./wire/connection"));
|
|
@@ -137,3 +141,15 @@ function attachOrCreate(options, callback) {
|
|
|
137
141
|
function pool(max, options) {
|
|
138
142
|
return new pool_1.default(attach, max, Object.assign({}, options, { isPool: true }));
|
|
139
143
|
}
|
|
144
|
+
function attachAsync(options) {
|
|
145
|
+
return (0, callback_1.fromCallback)(function (cb) { attach(options, cb); });
|
|
146
|
+
}
|
|
147
|
+
function createAsync(options) {
|
|
148
|
+
return (0, callback_1.fromCallback)(function (cb) { create(options, cb); });
|
|
149
|
+
}
|
|
150
|
+
function attachOrCreateAsync(options) {
|
|
151
|
+
return (0, callback_1.fromCallback)(function (cb) { attachOrCreate(options, cb); });
|
|
152
|
+
}
|
|
153
|
+
function dropAsync(options) {
|
|
154
|
+
return (0, callback_1.fromCallback)(function (cb) { drop(options, cb); });
|
|
155
|
+
}
|
package/lib/pool.d.ts
CHANGED
|
@@ -19,5 +19,12 @@ declare class Pool {
|
|
|
19
19
|
get(callback: Callback): this;
|
|
20
20
|
check(): this;
|
|
21
21
|
destroy(callback?: (err?: any) => void): void;
|
|
22
|
+
getAsync(): Promise<any>;
|
|
23
|
+
destroyAsync(): Promise<void>;
|
|
24
|
+
/**
|
|
25
|
+
* Run `work` with a connection from the pool, returning it to the pool
|
|
26
|
+
* (detach) when the returned promise settles — success or failure.
|
|
27
|
+
*/
|
|
28
|
+
withConnection<T>(work: (db: any) => Promise<T> | T): Promise<T>;
|
|
22
29
|
}
|
|
23
30
|
export = Pool;
|
package/lib/pool.js
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* Simple Pooling
|
|
5
5
|
*
|
|
6
6
|
***************************************/
|
|
7
|
+
const callback_1 = require("./callback");
|
|
7
8
|
class Pool {
|
|
8
9
|
constructor(attach, max, options) {
|
|
9
10
|
this.attach = attach;
|
|
@@ -178,5 +179,29 @@ class Pool {
|
|
|
178
179
|
}
|
|
179
180
|
});
|
|
180
181
|
}
|
|
182
|
+
/* Promise / async-await API — wrappers over the callback methods above. */
|
|
183
|
+
getAsync() {
|
|
184
|
+
var self = this;
|
|
185
|
+
return (0, callback_1.fromCallback)(function (cb) { self.get(cb); });
|
|
186
|
+
}
|
|
187
|
+
destroyAsync() {
|
|
188
|
+
var self = this;
|
|
189
|
+
return (0, callback_1.fromCallback)(function (cb) { self.destroy(cb); });
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Run `work` with a connection from the pool, returning it to the pool
|
|
193
|
+
* (detach) when the returned promise settles — success or failure.
|
|
194
|
+
*/
|
|
195
|
+
async withConnection(work) {
|
|
196
|
+
const db = await this.getAsync();
|
|
197
|
+
try {
|
|
198
|
+
return await work(db);
|
|
199
|
+
}
|
|
200
|
+
finally {
|
|
201
|
+
// A pooled detach only returns the connection to the pool; do not
|
|
202
|
+
// let a detach hiccup mask the outcome of `work`.
|
|
203
|
+
await new Promise(function (resolve) { db.detach(function () { resolve(); }); });
|
|
204
|
+
}
|
|
205
|
+
}
|
|
181
206
|
}
|
|
182
207
|
module.exports = Pool;
|
package/lib/types.d.ts
CHANGED
|
@@ -67,6 +67,15 @@ export type TransactionOptions = {
|
|
|
67
67
|
export type QueryOptions = {
|
|
68
68
|
timeout?: number;
|
|
69
69
|
scrollable?: boolean;
|
|
70
|
+
/**
|
|
71
|
+
* Abort the query when the signal fires (Firebird 2.5+ / protocol 12+).
|
|
72
|
+
* If the signal is already aborted the query is not sent at all and the
|
|
73
|
+
* callback/promise fails with an `AbortError`. If it fires mid-flight an
|
|
74
|
+
* out-of-band op_cancel is sent and the query fails with
|
|
75
|
+
* `err.gdscode === GDSCode.CANCELLED`. Cancellation is per-attachment:
|
|
76
|
+
* it cancels whatever is currently executing on the connection.
|
|
77
|
+
*/
|
|
78
|
+
signal?: AbortSignal;
|
|
70
79
|
};
|
|
71
80
|
export interface Database {
|
|
72
81
|
detach(callback?: SimpleCallback): Database;
|
|
@@ -82,6 +91,26 @@ export interface Database {
|
|
|
82
91
|
alterTablespace(name: string, filePath: string, callback?: QueryCallback): Database;
|
|
83
92
|
dropTablespace(name: string, callback?: QueryCallback): Database;
|
|
84
93
|
createSchema(schemaName: string, tablespaceName?: string | QueryCallback, callback?: QueryCallback): Database;
|
|
94
|
+
queryAsync<T = any>(query: string, params?: any[], options?: QueryOptions): Promise<T[]>;
|
|
95
|
+
executeAsync<T = any>(query: string, params?: any[], options?: QueryOptions): Promise<T[]>;
|
|
96
|
+
sequentiallyAsync(query: string, params: any[] | undefined, rowCallback: SequentialCallback, options?: QueryOptions | boolean): Promise<void>;
|
|
97
|
+
sequentiallyAsync(query: string, rowCallback: SequentialCallback, options?: QueryOptions | boolean): Promise<void>;
|
|
98
|
+
transactionAsync(options?: TransactionOptions | Isolation): Promise<Transaction>;
|
|
99
|
+
startTransactionAsync(options?: TransactionOptions | Isolation): Promise<Transaction>;
|
|
100
|
+
newStatementAsync(query: string): Promise<Statement>;
|
|
101
|
+
detachAsync(force?: boolean): Promise<void>;
|
|
102
|
+
dropAsync(): Promise<void>;
|
|
103
|
+
attachEventAsync(): Promise<any>;
|
|
104
|
+
/** Starts a transaction, commits when `work` resolves, rolls back when it rejects. */
|
|
105
|
+
withTransaction<T>(work: (transaction: Transaction) => Promise<T> | T, options?: TransactionOptions | Isolation): Promise<T>;
|
|
106
|
+
/**
|
|
107
|
+
* Cancel the operation currently executing on this connection
|
|
108
|
+
* (Firebird 2.5+ / protocol 12+). The cancelled operation fails through
|
|
109
|
+
* its own callback/promise with `err.gdscode === GDSCode.CANCELLED`.
|
|
110
|
+
*/
|
|
111
|
+
cancel(callback?: SimpleCallback): Database;
|
|
112
|
+
cancel(kind: number, callback?: SimpleCallback): Database;
|
|
113
|
+
cancelAsync(kind?: number): Promise<void>;
|
|
85
114
|
}
|
|
86
115
|
export interface Transaction {
|
|
87
116
|
newStatement(query: string, callback: (err: Error | null, statement: Statement) => void): void;
|
|
@@ -92,6 +121,15 @@ export interface Transaction {
|
|
|
92
121
|
commitRetaining(callback?: SimpleCallback): void;
|
|
93
122
|
rollback(callback?: SimpleCallback): void;
|
|
94
123
|
rollbackRetaining(callback?: SimpleCallback): void;
|
|
124
|
+
queryAsync<T = any>(query: string, params?: any[], options?: QueryOptions): Promise<T[]>;
|
|
125
|
+
executeAsync<T = any>(query: string, params?: any[], options?: QueryOptions): Promise<T[]>;
|
|
126
|
+
sequentiallyAsync(query: string, params: any[] | undefined, rowCallback: SequentialCallback, options?: QueryOptions | boolean): Promise<void>;
|
|
127
|
+
sequentiallyAsync(query: string, rowCallback: SequentialCallback, options?: QueryOptions | boolean): Promise<void>;
|
|
128
|
+
newStatementAsync(query: string): Promise<Statement>;
|
|
129
|
+
commitAsync(): Promise<void>;
|
|
130
|
+
commitRetainingAsync(): Promise<void>;
|
|
131
|
+
rollbackAsync(): Promise<void>;
|
|
132
|
+
rollbackRetainingAsync(): Promise<void>;
|
|
95
133
|
}
|
|
96
134
|
export interface Statement {
|
|
97
135
|
close(callback?: SimpleCallback): void;
|
|
@@ -101,6 +139,13 @@ export interface Statement {
|
|
|
101
139
|
fetch(transaction: Transaction, count: number, callback: QueryCallback): void;
|
|
102
140
|
fetchScroll(transaction: Transaction, direction: 'NEXT' | 'PRIOR' | 'FIRST' | 'LAST' | 'ABSOLUTE' | 'RELATIVE' | number, offset: number, count: number, callback: QueryCallback): void;
|
|
103
141
|
fetchAll(transaction: Transaction, callback: QueryCallback): void;
|
|
142
|
+
executeAsync(transaction: Transaction, params?: any[], options?: QueryOptions): Promise<any>;
|
|
143
|
+
fetchAsync(transaction: Transaction, count: number | 'all'): Promise<any>;
|
|
144
|
+
fetchScrollAsync(transaction: Transaction, direction: 'NEXT' | 'PRIOR' | 'FIRST' | 'LAST' | 'ABSOLUTE' | 'RELATIVE' | number, offset?: number, count?: number): Promise<any>;
|
|
145
|
+
fetchAllAsync(transaction: Transaction): Promise<any>;
|
|
146
|
+
closeAsync(): Promise<void>;
|
|
147
|
+
dropAsync(): Promise<void>;
|
|
148
|
+
releaseAsync(): Promise<void>;
|
|
104
149
|
}
|
|
105
150
|
export type SupportedCharacterSet = 'NONE' | 'CP943C' | 'DOS737' | 'DOS775' | 'DOS858' | 'DOS862' | 'DOS864' | 'DOS866' | 'DOS869' | 'GB18030' | 'GBK' | 'ISO8859_1' | 'ISO8859_2' | 'ISO8859_3' | 'ISO8859_4' | 'ISO8859_5' | 'ISO8859_6' | 'ISO8859_7' | 'ISO8859_8' | 'ISO8859_9' | 'ISO8859_13' | 'KOI8R' | 'KOI8U' | 'TIS620' | 'UTF8' | 'WIN1251' | 'WIN1252' | 'WIN1253' | 'WIN1254' | 'WIN1255' | 'WIN1256' | 'WIN1257' | 'WIN1258' | 'WIN_1258';
|
|
106
151
|
export interface Options {
|
|
@@ -188,6 +233,10 @@ export interface SvcMgrOptions extends Options {
|
|
|
188
233
|
export interface ConnectionPool {
|
|
189
234
|
get(callback: DatabaseCallback): void;
|
|
190
235
|
destroy(callback?: SimpleCallback): void;
|
|
236
|
+
getAsync(): Promise<Database>;
|
|
237
|
+
destroyAsync(): Promise<void>;
|
|
238
|
+
/** Acquire a connection, run `work`, always return the connection to the pool. */
|
|
239
|
+
withConnection<T>(work: (db: Database) => Promise<T> | T): Promise<T>;
|
|
191
240
|
}
|
|
192
241
|
export interface ReadableOptions {
|
|
193
242
|
optread?: 'byline' | 'buffer';
|
package/lib/wire/connection.d.ts
CHANGED
|
@@ -57,6 +57,13 @@ declare class Connection {
|
|
|
57
57
|
sendOpContAuth(authData: any, authDataEnc: any, pluginName: any): void;
|
|
58
58
|
sendOpCrypt(encryptPlugin: any): void;
|
|
59
59
|
sendOpCryptKeyCallback(pluginData: any): void;
|
|
60
|
+
/**
|
|
61
|
+
* Send an out-of-band op_cancel packet (protocol 12+ / Firebird 2.5+).
|
|
62
|
+
* The server reads it asynchronously while an operation is executing and
|
|
63
|
+
* makes that operation fail with isc_cancelled (GDSCode.CANCELLED); the
|
|
64
|
+
* op_cancel packet itself has no response, so nothing is queued here.
|
|
65
|
+
*/
|
|
66
|
+
cancelOperation(kind: any, callback: any): this;
|
|
60
67
|
_queueEvent(callback: any, defer?: boolean): void;
|
|
61
68
|
connect(options: any, callback: any): void;
|
|
62
69
|
attach(options: any, callback?: any, db?: any): void;
|
package/lib/wire/connection.js
CHANGED
|
@@ -294,6 +294,32 @@ class Connection {
|
|
|
294
294
|
msg.addBlr(pluginData); // Send the callback response data as a buffer
|
|
295
295
|
this._socket.write(msg.getData());
|
|
296
296
|
}
|
|
297
|
+
/**
|
|
298
|
+
* Send an out-of-band op_cancel packet (protocol 12+ / Firebird 2.5+).
|
|
299
|
+
* The server reads it asynchronously while an operation is executing and
|
|
300
|
+
* makes that operation fail with isc_cancelled (GDSCode.CANCELLED); the
|
|
301
|
+
* op_cancel packet itself has no response, so nothing is queued here.
|
|
302
|
+
*/
|
|
303
|
+
cancelOperation(kind, callback) {
|
|
304
|
+
if (typeof kind === 'function') {
|
|
305
|
+
callback = kind;
|
|
306
|
+
kind = undefined;
|
|
307
|
+
}
|
|
308
|
+
kind = kind || const_1.default.fb_cancel_raise;
|
|
309
|
+
if (this._isClosed)
|
|
310
|
+
return this.throwClosed(callback);
|
|
311
|
+
if (!this.accept || this.accept.protocolVersion < const_1.default.PROTOCOL_VERSION12) {
|
|
312
|
+
(0, callback_1.doError)(new Error('Query cancellation requires protocol 12+ (Firebird 2.5 or newer)'), callback);
|
|
313
|
+
return;
|
|
314
|
+
}
|
|
315
|
+
var msg = this._msg;
|
|
316
|
+
msg.pos = 0;
|
|
317
|
+
msg.addInt(const_1.default.op_cancel);
|
|
318
|
+
msg.addInt(kind);
|
|
319
|
+
this._socket.write(msg.getData());
|
|
320
|
+
if (callback)
|
|
321
|
+
callback();
|
|
322
|
+
}
|
|
297
323
|
_queueEvent(callback, defer = false) {
|
|
298
324
|
var self = this;
|
|
299
325
|
if (self._isClosed) {
|
|
@@ -1823,16 +1849,20 @@ function decodeResponse(data, callback, cnx, lowercase_keys, cb) {
|
|
|
1823
1849
|
public: BigInt('0x' + d.buffer.slice(keyStart, d.buffer.length).toString('utf8')),
|
|
1824
1850
|
pluginName: accept.pluginName
|
|
1825
1851
|
};
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1852
|
+
if (process.env.FIREBIRD_DEBUG) {
|
|
1853
|
+
console.log('--- DEBUG SRP Handshake ---');
|
|
1854
|
+
console.log('salt:', cnx.serverKeys.salt);
|
|
1855
|
+
console.log('server public key:', cnx.serverKeys.public.toString(16));
|
|
1856
|
+
console.log('client public key:', cnx.clientKeys.public.toString(16));
|
|
1857
|
+
console.log('hashAlgo:', accept.srpAlgo);
|
|
1858
|
+
}
|
|
1832
1859
|
const _t1 = Date.now();
|
|
1833
1860
|
var proof = srp.clientProof(cnx.options.user.toUpperCase(), cnx.options.password, cnx.serverKeys.salt, cnx.clientKeys.public, cnx.serverKeys.public, cnx.clientKeys.private, accept.srpAlgo);
|
|
1834
|
-
|
|
1835
|
-
|
|
1861
|
+
if (process.env.FIREBIRD_DEBUG) {
|
|
1862
|
+
// Never log the private key or the session key: the
|
|
1863
|
+
// session key is the wire-encryption key material.
|
|
1864
|
+
console.log('client proof M1:', proof.authData.toString(16));
|
|
1865
|
+
}
|
|
1836
1866
|
if (process.env.FIREBIRD_DEBUG) {
|
|
1837
1867
|
console.log('[fb-debug] srp.clientProof(%s): %dms', accept.srpAlgo, Date.now() - _t1);
|
|
1838
1868
|
}
|
|
@@ -1917,12 +1947,13 @@ function decodeResponse(data, callback, cnx, lowercase_keys, cb) {
|
|
|
1917
1947
|
Srp512: 'sha512'
|
|
1918
1948
|
};
|
|
1919
1949
|
var srpAlgo = crypto[pluginName];
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1950
|
+
if (process.env.FIREBIRD_DEBUG) {
|
|
1951
|
+
console.log('--- DEBUG SRP Handshake ---');
|
|
1952
|
+
console.log('salt:', cnx.serverKeys.salt);
|
|
1953
|
+
console.log('server public key:', cnx.serverKeys.public.toString(16));
|
|
1954
|
+
console.log('client public key:', cnx.clientKeys.public.toString(16));
|
|
1955
|
+
console.log('hashAlgo:', srpAlgo);
|
|
1956
|
+
}
|
|
1926
1957
|
const _t1 = Date.now();
|
|
1927
1958
|
var proof = srp.clientProof(cnx.options.user.toUpperCase(), cnx.options.password, cnx.serverKeys.salt, cnx.clientKeys.public, cnx.serverKeys.public, cnx.clientKeys.private, srpAlgo);
|
|
1928
1959
|
if (process.env.FIREBIRD_DEBUG) {
|
package/lib/wire/const.d.ts
CHANGED
|
@@ -104,6 +104,10 @@ declare const Const: Readonly<{
|
|
|
104
104
|
DSQL_close: number;
|
|
105
105
|
DSQL_drop: number;
|
|
106
106
|
DSQL_unprepare: number;
|
|
107
|
+
fb_cancel_disable: number;
|
|
108
|
+
fb_cancel_enable: number;
|
|
109
|
+
fb_cancel_raise: number;
|
|
110
|
+
fb_cancel_abort: number;
|
|
107
111
|
fetch_next: number;
|
|
108
112
|
fetch_prior: number;
|
|
109
113
|
fetch_first: number;
|
package/lib/wire/const.js
CHANGED
|
@@ -117,6 +117,13 @@ const dsql = {
|
|
|
117
117
|
DSQL_drop: 2,
|
|
118
118
|
DSQL_unprepare: 4, // >: 2.5
|
|
119
119
|
};
|
|
120
|
+
// fb_cancel_operation kinds sent with op_cancel (protocol 12+ / Firebird 2.5+)
|
|
121
|
+
const cancelKind = {
|
|
122
|
+
fb_cancel_disable: 1, // disable execution of fb_cancel_raise requests
|
|
123
|
+
fb_cancel_enable: 2, // re-enable delivery of cancel requests
|
|
124
|
+
fb_cancel_raise: 3, // cancel the operation currently executing on the attachment
|
|
125
|
+
fb_cancel_abort: 4, // forcibly abort the attachment's current activity
|
|
126
|
+
};
|
|
120
127
|
const fetchOp = {
|
|
121
128
|
fetch_next: 0,
|
|
122
129
|
fetch_prior: 1,
|
|
@@ -794,6 +801,7 @@ const Const = Object.freeze({
|
|
|
794
801
|
...blr,
|
|
795
802
|
...blobType,
|
|
796
803
|
...buffer,
|
|
804
|
+
...cancelKind,
|
|
797
805
|
...cnct,
|
|
798
806
|
...common,
|
|
799
807
|
...connect,
|
package/lib/wire/database.d.ts
CHANGED
|
@@ -12,6 +12,14 @@ declare class Database extends Events.EventEmitter {
|
|
|
12
12
|
sequentially(query: string, params?: any, on?: any, callback?: any, options?: any): this;
|
|
13
13
|
query(query: string, params?: any, callback?: any, options?: any): this;
|
|
14
14
|
drop(callback?: (err?: any) => void): void;
|
|
15
|
+
/**
|
|
16
|
+
* Cancel the operation currently executing on this connection by sending
|
|
17
|
+
* an out-of-band op_cancel (Firebird 2.5+ / protocol 12+). The cancelled
|
|
18
|
+
* operation fails through its own callback with err.gdscode ===
|
|
19
|
+
* GDSCode.CANCELLED. `kind` defaults to fb_cancel_raise; cancellation is
|
|
20
|
+
* per-attachment, not per-statement.
|
|
21
|
+
*/
|
|
22
|
+
cancel(kind?: number | ((err?: any) => void), callback?: (err?: any) => void): this;
|
|
15
23
|
attachEvent(callback: (err: any, evt?: any) => void): this;
|
|
16
24
|
/**
|
|
17
25
|
* Create a physical tablespace.
|
|
@@ -53,5 +61,21 @@ declare class Database extends Events.EventEmitter {
|
|
|
53
61
|
* @returns {Database}
|
|
54
62
|
*/
|
|
55
63
|
createSchema(schemaName: string, tablespaceName?: string | ((err?: any) => void), callback?: any): this;
|
|
64
|
+
queryAsync(query: string, params?: any, options?: any): Promise<any[]>;
|
|
65
|
+
executeAsync(query: string, params?: any, options?: any): Promise<any[]>;
|
|
66
|
+
sequentiallyAsync(query: string, params?: any, on?: any, options?: any): Promise<void>;
|
|
67
|
+
transactionAsync(options?: any): Promise<any>;
|
|
68
|
+
startTransactionAsync(options?: any): Promise<any>;
|
|
69
|
+
newStatementAsync(query: string): Promise<any>;
|
|
70
|
+
detachAsync(force?: boolean): Promise<void>;
|
|
71
|
+
dropAsync(): Promise<void>;
|
|
72
|
+
attachEventAsync(): Promise<any>;
|
|
73
|
+
cancelAsync(kind?: number): Promise<void>;
|
|
74
|
+
/**
|
|
75
|
+
* Run `work` inside a transaction: commits when the returned promise
|
|
76
|
+
* resolves, rolls back when it rejects (the original error is rethrown,
|
|
77
|
+
* even if the rollback itself fails).
|
|
78
|
+
*/
|
|
79
|
+
withTransaction<T>(work: (transaction: any) => Promise<T> | T, options?: any): Promise<T>;
|
|
56
80
|
}
|
|
57
81
|
export = Database;
|
package/lib/wire/database.js
CHANGED
|
@@ -273,6 +273,21 @@ class Database extends events_1.default.EventEmitter {
|
|
|
273
273
|
drop(callback) {
|
|
274
274
|
return this.connection.dropDatabase(callback);
|
|
275
275
|
}
|
|
276
|
+
/**
|
|
277
|
+
* Cancel the operation currently executing on this connection by sending
|
|
278
|
+
* an out-of-band op_cancel (Firebird 2.5+ / protocol 12+). The cancelled
|
|
279
|
+
* operation fails through its own callback with err.gdscode ===
|
|
280
|
+
* GDSCode.CANCELLED. `kind` defaults to fb_cancel_raise; cancellation is
|
|
281
|
+
* per-attachment, not per-statement.
|
|
282
|
+
*/
|
|
283
|
+
cancel(kind, callback) {
|
|
284
|
+
if (typeof kind === 'function') {
|
|
285
|
+
callback = kind;
|
|
286
|
+
kind = undefined;
|
|
287
|
+
}
|
|
288
|
+
this.connection.cancelOperation(kind, callback);
|
|
289
|
+
return this;
|
|
290
|
+
}
|
|
276
291
|
attachEvent(callback) {
|
|
277
292
|
var self = this;
|
|
278
293
|
const eventid = self.eventid++;
|
|
@@ -377,5 +392,75 @@ class Database extends events_1.default.EventEmitter {
|
|
|
377
392
|
}
|
|
378
393
|
return this.execute(sql, [], callback);
|
|
379
394
|
}
|
|
395
|
+
/*
|
|
396
|
+
* Promise / async-await API.
|
|
397
|
+
* Each *Async method wraps its callback counterpart; the callback API
|
|
398
|
+
* stays untouched. Result metadata is only available through the
|
|
399
|
+
* callback API — the promises resolve with the rows alone.
|
|
400
|
+
*/
|
|
401
|
+
queryAsync(query, params, options) {
|
|
402
|
+
var self = this;
|
|
403
|
+
return (0, callback_1.fromCallback)(function (cb) { self.query(query, params, cb, options); });
|
|
404
|
+
}
|
|
405
|
+
executeAsync(query, params, options) {
|
|
406
|
+
var self = this;
|
|
407
|
+
return (0, callback_1.fromCallback)(function (cb) { self.execute(query, params, cb, options); });
|
|
408
|
+
}
|
|
409
|
+
sequentiallyAsync(query, params, on, options) {
|
|
410
|
+
if (params instanceof Function) {
|
|
411
|
+
options = on;
|
|
412
|
+
on = params;
|
|
413
|
+
params = undefined;
|
|
414
|
+
}
|
|
415
|
+
var self = this;
|
|
416
|
+
return (0, callback_1.fromCallback)(function (cb) { self.sequentially(query, params, on, cb, options); });
|
|
417
|
+
}
|
|
418
|
+
transactionAsync(options) {
|
|
419
|
+
var self = this;
|
|
420
|
+
return (0, callback_1.fromCallback)(function (cb) { self.startTransaction(options, cb); });
|
|
421
|
+
}
|
|
422
|
+
startTransactionAsync(options) {
|
|
423
|
+
return this.transactionAsync(options);
|
|
424
|
+
}
|
|
425
|
+
newStatementAsync(query) {
|
|
426
|
+
var self = this;
|
|
427
|
+
return (0, callback_1.fromCallback)(function (cb) { self.newStatement(query, cb); });
|
|
428
|
+
}
|
|
429
|
+
detachAsync(force) {
|
|
430
|
+
var self = this;
|
|
431
|
+
return (0, callback_1.fromCallback)(function (cb) { self.detach(cb, force); });
|
|
432
|
+
}
|
|
433
|
+
dropAsync() {
|
|
434
|
+
var self = this;
|
|
435
|
+
return (0, callback_1.fromCallback)(function (cb) { self.drop(cb); });
|
|
436
|
+
}
|
|
437
|
+
attachEventAsync() {
|
|
438
|
+
var self = this;
|
|
439
|
+
return (0, callback_1.fromCallback)(function (cb) { self.attachEvent(cb); });
|
|
440
|
+
}
|
|
441
|
+
cancelAsync(kind) {
|
|
442
|
+
var self = this;
|
|
443
|
+
return (0, callback_1.fromCallback)(function (cb) { self.cancel(kind, cb); });
|
|
444
|
+
}
|
|
445
|
+
/**
|
|
446
|
+
* Run `work` inside a transaction: commits when the returned promise
|
|
447
|
+
* resolves, rolls back when it rejects (the original error is rethrown,
|
|
448
|
+
* even if the rollback itself fails).
|
|
449
|
+
*/
|
|
450
|
+
async withTransaction(work, options) {
|
|
451
|
+
const transaction = await this.transactionAsync(options);
|
|
452
|
+
try {
|
|
453
|
+
const result = await work(transaction);
|
|
454
|
+
await (0, callback_1.fromCallback)(function (cb) { transaction.commit(cb); });
|
|
455
|
+
return result;
|
|
456
|
+
}
|
|
457
|
+
catch (err) {
|
|
458
|
+
try {
|
|
459
|
+
await (0, callback_1.fromCallback)(function (cb) { transaction.rollback(cb); });
|
|
460
|
+
}
|
|
461
|
+
catch { /* surface the original error, not the rollback failure */ }
|
|
462
|
+
throw err;
|
|
463
|
+
}
|
|
464
|
+
}
|
|
380
465
|
}
|
|
381
466
|
module.exports = Database;
|
package/lib/wire/statement.d.ts
CHANGED
|
@@ -21,5 +21,12 @@ declare class Statement {
|
|
|
21
21
|
fetch(transaction: any, count: number | string, callback: (err: any, result?: any) => void): void;
|
|
22
22
|
fetchScroll(transaction: any, direction: string | number, offset?: any, count?: any, callback?: any): void;
|
|
23
23
|
fetchAll(transaction: any, callback: (err: any, result?: any) => void): void;
|
|
24
|
+
executeAsync(transaction: any, params?: any, options?: any): Promise<any>;
|
|
25
|
+
fetchAsync(transaction: any, count: number | string): Promise<any>;
|
|
26
|
+
fetchScrollAsync(transaction: any, direction: string | number, offset?: any, count?: any): Promise<any>;
|
|
27
|
+
fetchAllAsync(transaction: any): Promise<any>;
|
|
28
|
+
closeAsync(): Promise<void>;
|
|
29
|
+
dropAsync(): Promise<void>;
|
|
30
|
+
releaseAsync(): Promise<void>;
|
|
24
31
|
}
|
|
25
32
|
export = Statement;
|