node-firebird 2.5.0 → 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 +42 -1
- package/lib/types.d.ts +17 -0
- package/lib/wire/connection.d.ts +7 -0
- package/lib/wire/connection.js +26 -0
- package/lib/wire/const.d.ts +4 -0
- package/lib/wire/const.js +8 -0
- package/lib/wire/database.d.ts +9 -0
- package/lib/wire/database.js +19 -0
- package/lib/wire/transaction.d.ts +0 -5
- package/lib/wire/transaction.js +38 -0
- package/package.json +1 -1
- package/src/types.ts +17 -0
- package/src/wire/connection.ts +32 -0
- package/src/wire/const.ts +9 -0
- package/src/wire/database.ts +21 -0
- package/src/wire/transaction.ts +41 -0
package/README.md
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
- [Connection types](#connection-types) — connection options, classic connections, pooling
|
|
15
15
|
- [Database object (db)](#database-object-db) — database, transaction and statement methods/options
|
|
16
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
|
|
17
|
-
- [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
|
|
18
18
|
- [Using node-firebird with Express.js](#using-node-firebird-with-expressjs)
|
|
19
19
|
- [FAQ](#faq)
|
|
20
20
|
- [Contributing](#contributing) · [Contributors](#contributors)
|
|
@@ -1151,6 +1151,47 @@ Firebird.attach({
|
|
|
1151
1151
|
});
|
|
1152
1152
|
```
|
|
1153
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
|
+
|
|
1154
1195
|
### Statement Timeouts (Firebird 4.0+)
|
|
1155
1196
|
Setting a statement timeout allows the client to automatically abort queries that take too long on the server.
|
|
1156
1197
|
```js
|
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;
|
|
@@ -94,6 +103,14 @@ export interface Database {
|
|
|
94
103
|
attachEventAsync(): Promise<any>;
|
|
95
104
|
/** Starts a transaction, commits when `work` resolves, rolls back when it rejects. */
|
|
96
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>;
|
|
97
114
|
}
|
|
98
115
|
export interface Transaction {
|
|
99
116
|
newStatement(query: string, callback: (err: Error | null, statement: Statement) => void): void;
|
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) {
|
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.
|
|
@@ -62,6 +70,7 @@ declare class Database extends Events.EventEmitter {
|
|
|
62
70
|
detachAsync(force?: boolean): Promise<void>;
|
|
63
71
|
dropAsync(): Promise<void>;
|
|
64
72
|
attachEventAsync(): Promise<any>;
|
|
73
|
+
cancelAsync(kind?: number): Promise<void>;
|
|
65
74
|
/**
|
|
66
75
|
* Run `work` inside a transaction: commits when the returned promise
|
|
67
76
|
* resolves, rolls back when it rejects (the original error is rethrown,
|
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++;
|
|
@@ -423,6 +438,10 @@ class Database extends events_1.default.EventEmitter {
|
|
|
423
438
|
var self = this;
|
|
424
439
|
return (0, callback_1.fromCallback)(function (cb) { self.attachEvent(cb); });
|
|
425
440
|
}
|
|
441
|
+
cancelAsync(kind) {
|
|
442
|
+
var self = this;
|
|
443
|
+
return (0, callback_1.fromCallback)(function (cb) { self.cancel(kind, cb); });
|
|
444
|
+
}
|
|
426
445
|
/**
|
|
427
446
|
* Run `work` inside a transaction: commits when the returned promise
|
|
428
447
|
* resolves, rolls back when it rejects (the original error is rethrown,
|
package/lib/wire/transaction.js
CHANGED
|
@@ -10,6 +10,36 @@ const const_1 = __importDefault(require("./const"));
|
|
|
10
10
|
* Transaction
|
|
11
11
|
*
|
|
12
12
|
***************************************/
|
|
13
|
+
/** The error delivered when options.signal was already aborted on entry. */
|
|
14
|
+
function abortError(signal) {
|
|
15
|
+
if (signal && signal.reason instanceof Error)
|
|
16
|
+
return signal.reason;
|
|
17
|
+
var err = new Error('The operation was aborted');
|
|
18
|
+
err.name = 'AbortError';
|
|
19
|
+
err.code = 'ABORT_ERR';
|
|
20
|
+
return err;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Wire an AbortSignal to a running statement: on abort, send an out-of-band
|
|
24
|
+
* op_cancel so the server fails the executing operation with isc_cancelled
|
|
25
|
+
* (surfaced through the statement's own callback as err.gdscode ===
|
|
26
|
+
* GDSCode.CANCELLED). Returns the wrapped callback that detaches the
|
|
27
|
+
* listener once the operation settles.
|
|
28
|
+
*/
|
|
29
|
+
function hookAbortSignal(connection, signal, callback) {
|
|
30
|
+
var settled = false;
|
|
31
|
+
var onAbort = function () {
|
|
32
|
+
if (!settled)
|
|
33
|
+
connection.cancelOperation(const_1.default.fb_cancel_raise);
|
|
34
|
+
};
|
|
35
|
+
signal.addEventListener('abort', onAbort, { once: true });
|
|
36
|
+
return function (err, result, meta, isSelect) {
|
|
37
|
+
settled = true;
|
|
38
|
+
signal.removeEventListener('abort', onAbort);
|
|
39
|
+
if (callback)
|
|
40
|
+
callback(err, result, meta, isSelect);
|
|
41
|
+
};
|
|
42
|
+
}
|
|
13
43
|
class Transaction {
|
|
14
44
|
constructor(connection) {
|
|
15
45
|
this.connection = connection;
|
|
@@ -32,6 +62,14 @@ class Transaction {
|
|
|
32
62
|
callback = params;
|
|
33
63
|
params = undefined;
|
|
34
64
|
}
|
|
65
|
+
var signal = options && options.signal;
|
|
66
|
+
if (signal) {
|
|
67
|
+
if (signal.aborted) {
|
|
68
|
+
(0, callback_1.doError)(abortError(signal), callback);
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
callback = hookAbortSignal(this.connection, signal, callback);
|
|
72
|
+
}
|
|
35
73
|
var self = this;
|
|
36
74
|
this.newStatement(query, function (err, statement) {
|
|
37
75
|
if (err) {
|
package/package.json
CHANGED
package/src/types.ts
CHANGED
|
@@ -78,6 +78,15 @@ export type TransactionOptions = {
|
|
|
78
78
|
export type QueryOptions = {
|
|
79
79
|
timeout?: number;
|
|
80
80
|
scrollable?: boolean;
|
|
81
|
+
/**
|
|
82
|
+
* Abort the query when the signal fires (Firebird 2.5+ / protocol 12+).
|
|
83
|
+
* If the signal is already aborted the query is not sent at all and the
|
|
84
|
+
* callback/promise fails with an `AbortError`. If it fires mid-flight an
|
|
85
|
+
* out-of-band op_cancel is sent and the query fails with
|
|
86
|
+
* `err.gdscode === GDSCode.CANCELLED`. Cancellation is per-attachment:
|
|
87
|
+
* it cancels whatever is currently executing on the connection.
|
|
88
|
+
*/
|
|
89
|
+
signal?: AbortSignal;
|
|
81
90
|
}
|
|
82
91
|
|
|
83
92
|
export interface Database {
|
|
@@ -109,6 +118,14 @@ export interface Database {
|
|
|
109
118
|
attachEventAsync(): Promise<any>;
|
|
110
119
|
/** Starts a transaction, commits when `work` resolves, rolls back when it rejects. */
|
|
111
120
|
withTransaction<T>(work: (transaction: Transaction) => Promise<T> | T, options?: TransactionOptions | Isolation): Promise<T>;
|
|
121
|
+
/**
|
|
122
|
+
* Cancel the operation currently executing on this connection
|
|
123
|
+
* (Firebird 2.5+ / protocol 12+). The cancelled operation fails through
|
|
124
|
+
* its own callback/promise with `err.gdscode === GDSCode.CANCELLED`.
|
|
125
|
+
*/
|
|
126
|
+
cancel(callback?: SimpleCallback): Database;
|
|
127
|
+
cancel(kind: number, callback?: SimpleCallback): Database;
|
|
128
|
+
cancelAsync(kind?: number): Promise<void>;
|
|
112
129
|
}
|
|
113
130
|
|
|
114
131
|
export interface Transaction {
|
package/src/wire/connection.ts
CHANGED
|
@@ -369,6 +369,38 @@ class Connection {
|
|
|
369
369
|
}
|
|
370
370
|
|
|
371
371
|
|
|
372
|
+
/**
|
|
373
|
+
* Send an out-of-band op_cancel packet (protocol 12+ / Firebird 2.5+).
|
|
374
|
+
* The server reads it asynchronously while an operation is executing and
|
|
375
|
+
* makes that operation fail with isc_cancelled (GDSCode.CANCELLED); the
|
|
376
|
+
* op_cancel packet itself has no response, so nothing is queued here.
|
|
377
|
+
*/
|
|
378
|
+
cancelOperation(kind, callback) {
|
|
379
|
+
if (typeof kind === 'function') {
|
|
380
|
+
callback = kind;
|
|
381
|
+
kind = undefined;
|
|
382
|
+
}
|
|
383
|
+
kind = kind || Const.fb_cancel_raise;
|
|
384
|
+
|
|
385
|
+
if (this._isClosed)
|
|
386
|
+
return this.throwClosed(callback);
|
|
387
|
+
|
|
388
|
+
if (!this.accept || this.accept.protocolVersion < Const.PROTOCOL_VERSION12) {
|
|
389
|
+
doError(new Error('Query cancellation requires protocol 12+ (Firebird 2.5 or newer)'), callback);
|
|
390
|
+
return;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
var msg = this._msg;
|
|
394
|
+
msg.pos = 0;
|
|
395
|
+
msg.addInt(Const.op_cancel);
|
|
396
|
+
msg.addInt(kind);
|
|
397
|
+
this._socket.write(msg.getData());
|
|
398
|
+
|
|
399
|
+
if (callback)
|
|
400
|
+
callback();
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
|
|
372
404
|
_queueEvent(callback, defer = false) {
|
|
373
405
|
var self = this;
|
|
374
406
|
|
package/src/wire/const.ts
CHANGED
|
@@ -132,6 +132,14 @@ const dsql = {
|
|
|
132
132
|
DSQL_unprepare : 4, // >: 2.5
|
|
133
133
|
};
|
|
134
134
|
|
|
135
|
+
// fb_cancel_operation kinds sent with op_cancel (protocol 12+ / Firebird 2.5+)
|
|
136
|
+
const cancelKind = {
|
|
137
|
+
fb_cancel_disable : 1, // disable execution of fb_cancel_raise requests
|
|
138
|
+
fb_cancel_enable : 2, // re-enable delivery of cancel requests
|
|
139
|
+
fb_cancel_raise : 3, // cancel the operation currently executing on the attachment
|
|
140
|
+
fb_cancel_abort : 4, // forcibly abort the attachment's current activity
|
|
141
|
+
};
|
|
142
|
+
|
|
135
143
|
const fetchOp = {
|
|
136
144
|
fetch_next : 0,
|
|
137
145
|
fetch_prior : 1,
|
|
@@ -859,6 +867,7 @@ const Const = Object.freeze({
|
|
|
859
867
|
...blr,
|
|
860
868
|
...blobType,
|
|
861
869
|
...buffer,
|
|
870
|
+
...cancelKind,
|
|
862
871
|
...cnct,
|
|
863
872
|
...common,
|
|
864
873
|
...connect,
|
package/src/wire/database.ts
CHANGED
|
@@ -322,6 +322,22 @@ class Database extends Events.EventEmitter {
|
|
|
322
322
|
return this.connection.dropDatabase(callback);
|
|
323
323
|
}
|
|
324
324
|
|
|
325
|
+
/**
|
|
326
|
+
* Cancel the operation currently executing on this connection by sending
|
|
327
|
+
* an out-of-band op_cancel (Firebird 2.5+ / protocol 12+). The cancelled
|
|
328
|
+
* operation fails through its own callback with err.gdscode ===
|
|
329
|
+
* GDSCode.CANCELLED. `kind` defaults to fb_cancel_raise; cancellation is
|
|
330
|
+
* per-attachment, not per-statement.
|
|
331
|
+
*/
|
|
332
|
+
cancel(kind?: number | ((err?: any) => void), callback?: (err?: any) => void): this {
|
|
333
|
+
if (typeof kind === 'function') {
|
|
334
|
+
callback = kind;
|
|
335
|
+
kind = undefined;
|
|
336
|
+
}
|
|
337
|
+
this.connection.cancelOperation(kind, callback);
|
|
338
|
+
return this;
|
|
339
|
+
}
|
|
340
|
+
|
|
325
341
|
attachEvent(callback: (err: any, evt?: any) => void): this {
|
|
326
342
|
var self = this;
|
|
327
343
|
const eventid = self.eventid++;
|
|
@@ -496,6 +512,11 @@ class Database extends Events.EventEmitter {
|
|
|
496
512
|
return fromCallback(function(cb) { self.attachEvent(cb); });
|
|
497
513
|
}
|
|
498
514
|
|
|
515
|
+
cancelAsync(kind?: number): Promise<void> {
|
|
516
|
+
var self = this;
|
|
517
|
+
return fromCallback(function(cb) { self.cancel(kind, cb); });
|
|
518
|
+
}
|
|
519
|
+
|
|
499
520
|
/**
|
|
500
521
|
* Run `work` inside a transaction: commits when the returned promise
|
|
501
522
|
* resolves, rolls back when it rejects (the original error is rethrown,
|
package/src/wire/transaction.ts
CHANGED
|
@@ -8,6 +8,38 @@ import Const from './const';
|
|
|
8
8
|
*
|
|
9
9
|
***************************************/
|
|
10
10
|
|
|
11
|
+
/** The error delivered when options.signal was already aborted on entry. */
|
|
12
|
+
function abortError(signal: any): Error {
|
|
13
|
+
if (signal && signal.reason instanceof Error)
|
|
14
|
+
return signal.reason;
|
|
15
|
+
var err: any = new Error('The operation was aborted');
|
|
16
|
+
err.name = 'AbortError';
|
|
17
|
+
err.code = 'ABORT_ERR';
|
|
18
|
+
return err;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Wire an AbortSignal to a running statement: on abort, send an out-of-band
|
|
23
|
+
* op_cancel so the server fails the executing operation with isc_cancelled
|
|
24
|
+
* (surfaced through the statement's own callback as err.gdscode ===
|
|
25
|
+
* GDSCode.CANCELLED). Returns the wrapped callback that detaches the
|
|
26
|
+
* listener once the operation settles.
|
|
27
|
+
*/
|
|
28
|
+
function hookAbortSignal(connection: any, signal: any, callback: any): any {
|
|
29
|
+
var settled = false;
|
|
30
|
+
var onAbort = function() {
|
|
31
|
+
if (!settled)
|
|
32
|
+
connection.cancelOperation(Const.fb_cancel_raise);
|
|
33
|
+
};
|
|
34
|
+
signal.addEventListener('abort', onAbort, { once: true });
|
|
35
|
+
return function(err?: any, result?: any, meta?: any, isSelect?: boolean) {
|
|
36
|
+
settled = true;
|
|
37
|
+
signal.removeEventListener('abort', onAbort);
|
|
38
|
+
if (callback)
|
|
39
|
+
callback(err, result, meta, isSelect);
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
11
43
|
class Transaction {
|
|
12
44
|
connection: any;
|
|
13
45
|
db: any;
|
|
@@ -38,6 +70,15 @@ class Transaction {
|
|
|
38
70
|
params = undefined;
|
|
39
71
|
}
|
|
40
72
|
|
|
73
|
+
var signal = options && options.signal;
|
|
74
|
+
if (signal) {
|
|
75
|
+
if (signal.aborted) {
|
|
76
|
+
doError(abortError(signal), callback);
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
callback = hookAbortSignal(this.connection, signal, callback);
|
|
80
|
+
}
|
|
81
|
+
|
|
41
82
|
var self = this;
|
|
42
83
|
this.newStatement(query, function(err: any, statement: any) {
|
|
43
84
|
|