node-firebird 2.9.0 → 2.11.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 +211 -6
- package/lib/index.d.ts +5 -0
- package/lib/index.js +32 -1
- package/lib/pool.js +1 -1
- package/lib/srp.d.ts +3 -3
- package/lib/types.d.ts +165 -5
- package/lib/uri.js +2 -2
- package/lib/wire/connection.d.ts +92 -59
- package/lib/wire/connection.js +279 -58
- package/lib/wire/const.d.ts +9 -1
- package/lib/wire/const.js +23 -9
- package/lib/wire/database.d.ts +51 -26
- package/lib/wire/database.js +53 -20
- 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 +20 -2
- package/lib/wire/serialize.js +7 -0
- package/lib/wire/service.d.ts +42 -0
- package/lib/wire/service.js +145 -0
- package/lib/wire/socket.d.ts +3 -1
- package/lib/wire/socket.js +5 -2
- package/lib/wire/statement.d.ts +31 -19
- package/lib/wire/statement.js +1 -5
- package/lib/wire/transaction.d.ts +30 -18
- package/lib/wire/transaction.js +23 -4
- package/lib/wire/wire-types.d.ts +116 -0
- package/lib/wire/wire-types.js +10 -0
- package/lib/wire/xsqlvar.d.ts +57 -18
- package/lib/wire/xsqlvar.js +59 -0
- package/package.json +1 -1
- package/src/index.ts +36 -4
- package/src/messages.ts +1 -1
- package/src/pool.ts +1 -1
- package/src/srp.ts +6 -6
- package/src/types.ts +162 -5
- package/src/unix-crypt.ts +9 -9
- package/src/uri.ts +2 -2
- package/src/wire/connection.ts +475 -234
- package/src/wire/const.ts +23 -9
- package/src/wire/database.ts +101 -54
- package/src/wire/eventConnection.ts +8 -5
- package/src/wire/query-stream.ts +80 -0
- package/src/wire/serialize.ts +31 -0
- package/src/wire/service.ts +188 -6
- package/src/wire/socket.ts +17 -8
- package/src/wire/statement.ts +37 -29
- package/src/wire/transaction.ts +57 -32
- package/src/wire/wire-types.ts +127 -0
- package/src/wire/xsqlvar.ts +85 -7
package/lib/wire/database.d.ts
CHANGED
|
@@ -1,14 +1,27 @@
|
|
|
1
1
|
import Events from 'events';
|
|
2
|
+
import { type Callback, type SimpleCallback } from '../callback';
|
|
3
|
+
import FbEventManager from './fbEventManager';
|
|
4
|
+
import type Connection from './connection';
|
|
5
|
+
import type Transaction from './transaction';
|
|
6
|
+
import type Statement from './statement';
|
|
7
|
+
import type { BatchCb, StatementCb, InternalQueryOptions } from './wire-types';
|
|
8
|
+
import type { BatchOptions, BatchResult, Isolation, QueryParams, QueryStreamOptions, TransactionCallback, TransactionOptions } from '../types';
|
|
9
|
+
/** Callback for startTransaction: the internal optional-args shape, or the
|
|
10
|
+
* public TransactionCallback (non-optional transaction) from types.ts. */
|
|
11
|
+
type TransactionCb = Callback<Transaction> | TransactionCallback;
|
|
12
|
+
/** startTransaction options: resolved options object, a bare isolation
|
|
13
|
+
* array, or omitted entirely (callback in the options position). */
|
|
14
|
+
type TransactionArg = TransactionOptions | Isolation | TransactionCb | undefined;
|
|
2
15
|
declare class Database extends Events.EventEmitter {
|
|
3
|
-
connection:
|
|
16
|
+
connection: Connection;
|
|
4
17
|
eventid: number;
|
|
5
|
-
constructor(connection:
|
|
18
|
+
constructor(connection: Connection);
|
|
6
19
|
escape(value: any): string;
|
|
7
|
-
detach(callback?:
|
|
8
|
-
transaction(options:
|
|
9
|
-
startTransaction(options:
|
|
10
|
-
newStatement(query: string, callback:
|
|
11
|
-
execute(query: string, params?:
|
|
20
|
+
detach(callback?: Callback, force?: boolean): this;
|
|
21
|
+
transaction(options: TransactionArg, callback?: TransactionCb): this;
|
|
22
|
+
startTransaction(options: TransactionArg, callback?: TransactionCb): this;
|
|
23
|
+
newStatement(query: string, callback: StatementCb): this;
|
|
24
|
+
execute(query: string, params?: QueryParams | Callback, callback?: any, options?: InternalQueryOptions): this;
|
|
12
25
|
/**
|
|
13
26
|
* Bulk-execute `query` once per row via the Firebird 4 batch API
|
|
14
27
|
* (protocol 16+) with all-or-nothing semantics: the batch runs in its
|
|
@@ -18,10 +31,20 @@ declare class Database extends Events.EventEmitter {
|
|
|
18
31
|
* err.batchCompletion. Use transaction.executeBatch for partial-success
|
|
19
32
|
* handling.
|
|
20
33
|
*/
|
|
21
|
-
executeBatch(query: string, rows:
|
|
22
|
-
sequentially(query: string, params?: any, on?: any, callback?: any, options?:
|
|
23
|
-
|
|
24
|
-
|
|
34
|
+
executeBatch(query: string, rows: QueryParams[], callback?: BatchCb, options?: BatchOptions): this;
|
|
35
|
+
sequentially(query: string, params?: any, on?: any, callback?: any, options?: InternalQueryOptions | boolean): this;
|
|
36
|
+
/**
|
|
37
|
+
* Run `query` and return an object-mode Readable emitting one row per
|
|
38
|
+
* chunk (what pg-query-stream / mysql2 .stream() return), with real
|
|
39
|
+
* backpressure: fetching pauses while the stream buffer is full. Runs
|
|
40
|
+
* in its own transaction, like db.query. Destroying the stream early
|
|
41
|
+
* (e.g. a pipeline() teardown) aborts the fetch and releases the
|
|
42
|
+
* statement. Rows go through the regular decode path, so typeCast,
|
|
43
|
+
* blobAsText and jsonAsObject all apply.
|
|
44
|
+
*/
|
|
45
|
+
queryStream(query: string, params?: QueryParams, options?: QueryStreamOptions): import("node:stream").Readable;
|
|
46
|
+
query(query: string, params?: QueryParams | Callback, callback?: any, options?: InternalQueryOptions): this;
|
|
47
|
+
drop(callback?: SimpleCallback): void;
|
|
25
48
|
/**
|
|
26
49
|
* Cancel the operation currently executing on this connection by sending
|
|
27
50
|
* an out-of-band op_cancel (Firebird 2.5+ / protocol 12+). The cancelled
|
|
@@ -29,8 +52,8 @@ declare class Database extends Events.EventEmitter {
|
|
|
29
52
|
* GDSCode.CANCELLED. `kind` defaults to fb_cancel_raise; cancellation is
|
|
30
53
|
* per-attachment, not per-statement.
|
|
31
54
|
*/
|
|
32
|
-
cancel(kind?: number |
|
|
33
|
-
attachEvent(callback:
|
|
55
|
+
cancel(kind?: number | SimpleCallback, callback?: SimpleCallback): this;
|
|
56
|
+
attachEvent(callback: Callback<FbEventManager>): this;
|
|
34
57
|
/**
|
|
35
58
|
* Create a physical tablespace.
|
|
36
59
|
* Supported in Firebird 6.0+ (Protocol 20+).
|
|
@@ -40,7 +63,7 @@ declare class Database extends Events.EventEmitter {
|
|
|
40
63
|
* @param {function} [callback] - Asynchronous completion callback.
|
|
41
64
|
* @returns {Database}
|
|
42
65
|
*/
|
|
43
|
-
createTablespace(name: string, filePath: string, callback?:
|
|
66
|
+
createTablespace(name: string, filePath: string, callback?: Callback): this;
|
|
44
67
|
/**
|
|
45
68
|
* Alter an existing tablespace physical location.
|
|
46
69
|
* Supported in Firebird 6.0+ (Protocol 20+).
|
|
@@ -50,7 +73,7 @@ declare class Database extends Events.EventEmitter {
|
|
|
50
73
|
* @param {function} [callback] - Asynchronous completion callback.
|
|
51
74
|
* @returns {Database}
|
|
52
75
|
*/
|
|
53
|
-
alterTablespace(name: string, filePath: string, callback?:
|
|
76
|
+
alterTablespace(name: string, filePath: string, callback?: Callback): this;
|
|
54
77
|
/**
|
|
55
78
|
* Drop a tablespace.
|
|
56
79
|
* Supported in Firebird 6.0+ (Protocol 20+).
|
|
@@ -59,7 +82,7 @@ declare class Database extends Events.EventEmitter {
|
|
|
59
82
|
* @param {function} [callback] - Asynchronous completion callback.
|
|
60
83
|
* @returns {Database}
|
|
61
84
|
*/
|
|
62
|
-
dropTablespace(name: string, callback?:
|
|
85
|
+
dropTablespace(name: string, callback?: Callback): this;
|
|
63
86
|
/**
|
|
64
87
|
* Create a schema/namespace. Can optionally partition/map the namespace
|
|
65
88
|
* to a physical tablespace.
|
|
@@ -70,23 +93,25 @@ declare class Database extends Events.EventEmitter {
|
|
|
70
93
|
* @param {function} [callback] - Asynchronous completion callback.
|
|
71
94
|
* @returns {Database}
|
|
72
95
|
*/
|
|
73
|
-
createSchema(schemaName: string, tablespaceName?: string |
|
|
74
|
-
queryAsync(query: string, params?:
|
|
75
|
-
executeAsync(query: string, params?:
|
|
76
|
-
executeBatchAsync(query: string, rows:
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
96
|
+
createSchema(schemaName: string, tablespaceName?: string | Callback, callback?: Callback): this;
|
|
97
|
+
queryAsync(query: string, params?: QueryParams, options?: InternalQueryOptions): Promise<any[]>;
|
|
98
|
+
executeAsync(query: string, params?: QueryParams, options?: InternalQueryOptions): Promise<any[]>;
|
|
99
|
+
executeBatchAsync(query: string, rows: QueryParams[], options?: BatchOptions): Promise<BatchResult>;
|
|
100
|
+
/** `on` may hold the options when the params argument is the row callback
|
|
101
|
+
* (public overload: sequentiallyAsync(query, rowCallback, options)). */
|
|
102
|
+
sequentiallyAsync(query: string, params?: any, on?: any, options?: InternalQueryOptions | boolean): Promise<void>;
|
|
103
|
+
transactionAsync(options?: TransactionOptions | Isolation): Promise<Transaction>;
|
|
104
|
+
startTransactionAsync(options?: TransactionOptions | Isolation): Promise<Transaction>;
|
|
105
|
+
newStatementAsync(query: string): Promise<Statement>;
|
|
81
106
|
detachAsync(force?: boolean): Promise<void>;
|
|
82
107
|
dropAsync(): Promise<void>;
|
|
83
|
-
attachEventAsync(): Promise<
|
|
108
|
+
attachEventAsync(): Promise<FbEventManager>;
|
|
84
109
|
cancelAsync(kind?: number): Promise<void>;
|
|
85
110
|
/**
|
|
86
111
|
* Run `work` inside a transaction: commits when the returned promise
|
|
87
112
|
* resolves, rolls back when it rejects (the original error is rethrown,
|
|
88
113
|
* even if the rollback itself fails).
|
|
89
114
|
*/
|
|
90
|
-
withTransaction<T>(work: (transaction:
|
|
115
|
+
withTransaction<T>(work: (transaction: Transaction) => Promise<T> | T, options?: TransactionOptions | Isolation): Promise<T>;
|
|
91
116
|
}
|
|
92
117
|
export = Database;
|
package/lib/wire/database.js
CHANGED
|
@@ -6,8 +6,10 @@ const events_1 = __importDefault(require("events"));
|
|
|
6
6
|
const callback_1 = require("../callback");
|
|
7
7
|
const utils_1 = require("../utils");
|
|
8
8
|
const const_1 = __importDefault(require("./const"));
|
|
9
|
+
const xsqlvar_1 = require("./xsqlvar");
|
|
9
10
|
const eventConnection_1 = __importDefault(require("./eventConnection"));
|
|
10
11
|
const fbEventManager_1 = __importDefault(require("./fbEventManager"));
|
|
12
|
+
const query_stream_1 = __importDefault(require("./query-stream"));
|
|
11
13
|
/***************************************
|
|
12
14
|
*
|
|
13
15
|
* Database
|
|
@@ -74,30 +76,42 @@ function readblob(blob, callback) {
|
|
|
74
76
|
});
|
|
75
77
|
});
|
|
76
78
|
}
|
|
77
|
-
function fetchBlobSyncRow(row, meta, callback) {
|
|
78
|
-
if (!row || !meta || !meta.length) {
|
|
79
|
+
function fetchBlobSyncRow(row, meta, nestTables, lowercaseKeys, callback) {
|
|
80
|
+
if (!row || !meta || !meta.length || !meta.some((m) => m && m.type === const_1.default.SQL_BLOB)) {
|
|
79
81
|
callback(null, row);
|
|
80
82
|
return;
|
|
81
83
|
}
|
|
82
|
-
|
|
83
|
-
|
|
84
|
+
// locate blob cells by the same key computation the fetch decoder used,
|
|
85
|
+
// rather than assuming Object.keys(row) is index-aligned with meta —
|
|
86
|
+
// duplicate JOIN column names (and nested rows) break that alignment.
|
|
87
|
+
// Array rows (sequentially's legacy boolean form) are keyed by index.
|
|
88
|
+
const isArrayRow = Array.isArray(row);
|
|
89
|
+
const keys = isArrayRow ? null : (0, xsqlvar_1.computeColumnKeys)(meta, nestTables, lowercaseKeys);
|
|
90
|
+
const blobCells = [];
|
|
84
91
|
for (let i = 0; i < meta.length; i++) {
|
|
85
|
-
if (meta[i]
|
|
86
|
-
|
|
92
|
+
if (!meta[i] || meta[i].type !== const_1.default.SQL_BLOB) {
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
const target = keys ? (0, xsqlvar_1.nestCell)(row, keys[i].table) : row;
|
|
96
|
+
const key = keys ? keys[i].key : i;
|
|
97
|
+
// duplicate aliases collapse onto one cell — read it only once
|
|
98
|
+
if (typeof target[key] === 'function' &&
|
|
99
|
+
!blobCells.some((cell) => cell.target === target && cell.key === key)) {
|
|
100
|
+
blobCells.push({ target, key });
|
|
87
101
|
}
|
|
88
102
|
}
|
|
89
|
-
if (!
|
|
103
|
+
if (!blobCells.length) {
|
|
90
104
|
callback(null, row);
|
|
91
105
|
return;
|
|
92
106
|
}
|
|
93
|
-
let pending =
|
|
107
|
+
let pending = blobCells.length;
|
|
94
108
|
let blobErr;
|
|
95
|
-
|
|
96
|
-
readblob(
|
|
109
|
+
blobCells.forEach(function (cell) {
|
|
110
|
+
readblob(cell.target[cell.key], function (err, data) {
|
|
97
111
|
if (err && !blobErr) {
|
|
98
112
|
blobErr = err;
|
|
99
113
|
}
|
|
100
|
-
|
|
114
|
+
cell.target[cell.key] = data;
|
|
101
115
|
pending--;
|
|
102
116
|
if (pending === 0) {
|
|
103
117
|
callback(blobErr, row);
|
|
@@ -128,7 +142,7 @@ class Database extends events_1.default.EventEmitter {
|
|
|
128
142
|
self.emit('detach', false);
|
|
129
143
|
if (callback)
|
|
130
144
|
callback(err, obj);
|
|
131
|
-
}
|
|
145
|
+
});
|
|
132
146
|
}
|
|
133
147
|
else {
|
|
134
148
|
self.emit('detach', false);
|
|
@@ -145,18 +159,21 @@ class Database extends events_1.default.EventEmitter {
|
|
|
145
159
|
return this;
|
|
146
160
|
}
|
|
147
161
|
newStatement(query, callback) {
|
|
162
|
+
// the public strict callback shape and the internal optional-args
|
|
163
|
+
// shape only differ in optionality; treat it as the internal one
|
|
164
|
+
const cb = callback;
|
|
148
165
|
this.startTransaction(function (err, transaction) {
|
|
149
|
-
if (err) {
|
|
150
|
-
|
|
166
|
+
if (err || !transaction) {
|
|
167
|
+
cb(err);
|
|
151
168
|
return;
|
|
152
169
|
}
|
|
153
170
|
transaction.newStatement(query, function (err, statement) {
|
|
154
171
|
if (err) {
|
|
155
|
-
|
|
172
|
+
cb(err);
|
|
156
173
|
return;
|
|
157
174
|
}
|
|
158
175
|
transaction.commit(function (err) {
|
|
159
|
-
|
|
176
|
+
cb(err, statement);
|
|
160
177
|
});
|
|
161
178
|
});
|
|
162
179
|
});
|
|
@@ -170,7 +187,7 @@ class Database extends events_1.default.EventEmitter {
|
|
|
170
187
|
}
|
|
171
188
|
var self = this;
|
|
172
189
|
self.connection.startTransaction(function (err, transaction) {
|
|
173
|
-
if (err) {
|
|
190
|
+
if (err || !transaction) {
|
|
174
191
|
(0, callback_1.doError)(err, callback);
|
|
175
192
|
return;
|
|
176
193
|
}
|
|
@@ -201,12 +218,12 @@ class Database extends events_1.default.EventEmitter {
|
|
|
201
218
|
executeBatch(query, rows, callback, options) {
|
|
202
219
|
var self = this;
|
|
203
220
|
self.connection.startTransaction(function (err, transaction) {
|
|
204
|
-
if (err) {
|
|
221
|
+
if (err || !transaction) {
|
|
205
222
|
(0, callback_1.doError)(err, callback);
|
|
206
223
|
return;
|
|
207
224
|
}
|
|
208
225
|
transaction.executeBatch(query, rows, function (err, result) {
|
|
209
|
-
if (err) {
|
|
226
|
+
if (err || !result) {
|
|
210
227
|
transaction.rollback(function () {
|
|
211
228
|
(0, callback_1.doError)(err, callback);
|
|
212
229
|
});
|
|
@@ -255,7 +272,9 @@ class Database extends events_1.default.EventEmitter {
|
|
|
255
272
|
done = true;
|
|
256
273
|
next(err);
|
|
257
274
|
};
|
|
258
|
-
|
|
275
|
+
// options is read at call time, after the normalization below
|
|
276
|
+
const nest = (0, xsqlvar_1.resolveNestTables)(options, self.connection.options);
|
|
277
|
+
fetchBlobSyncRow(row, meta, nest, self.connection._lowercase_keys, function (blobErr) {
|
|
259
278
|
if (blobErr) {
|
|
260
279
|
finish(blobErr);
|
|
261
280
|
return;
|
|
@@ -297,6 +316,18 @@ class Database extends events_1.default.EventEmitter {
|
|
|
297
316
|
self.execute(query, params, callback, options);
|
|
298
317
|
return self;
|
|
299
318
|
}
|
|
319
|
+
/**
|
|
320
|
+
* Run `query` and return an object-mode Readable emitting one row per
|
|
321
|
+
* chunk (what pg-query-stream / mysql2 .stream() return), with real
|
|
322
|
+
* backpressure: fetching pauses while the stream buffer is full. Runs
|
|
323
|
+
* in its own transaction, like db.query. Destroying the stream early
|
|
324
|
+
* (e.g. a pipeline() teardown) aborts the fetch and releases the
|
|
325
|
+
* statement. Rows go through the regular decode path, so typeCast,
|
|
326
|
+
* blobAsText and jsonAsObject all apply.
|
|
327
|
+
*/
|
|
328
|
+
queryStream(query, params, options) {
|
|
329
|
+
return (0, query_stream_1.default)(this, query, params, options);
|
|
330
|
+
}
|
|
300
331
|
query(query, params, callback, options = {}) {
|
|
301
332
|
if (params instanceof Function) {
|
|
302
333
|
options = callback || {};
|
|
@@ -452,6 +483,8 @@ class Database extends events_1.default.EventEmitter {
|
|
|
452
483
|
var self = this;
|
|
453
484
|
return (0, callback_1.fromCallback)(function (cb) { self.executeBatch(query, rows, cb, options); });
|
|
454
485
|
}
|
|
486
|
+
/** `on` may hold the options when the params argument is the row callback
|
|
487
|
+
* (public overload: sequentiallyAsync(query, rowCallback, options)). */
|
|
455
488
|
sequentiallyAsync(query, params, on, options) {
|
|
456
489
|
if (params instanceof Function) {
|
|
457
490
|
options = on;
|
|
@@ -15,8 +15,8 @@ class EventConnection {
|
|
|
15
15
|
this._isOpened = false;
|
|
16
16
|
this._socket = net_1.default.createConnection(port, host);
|
|
17
17
|
this._bind_events(host, port, callback);
|
|
18
|
-
this.error;
|
|
19
|
-
this.eventcallback;
|
|
18
|
+
this.error = null;
|
|
19
|
+
this.eventcallback = null;
|
|
20
20
|
}
|
|
21
21
|
_bind_events(host, port, callback) {
|
|
22
22
|
var self = this;
|
|
@@ -45,8 +45,8 @@ class EventConnection {
|
|
|
45
45
|
data.copy(buf, xdr.buffer.length);
|
|
46
46
|
xdr.buffer = buf;
|
|
47
47
|
}
|
|
48
|
+
var op_pos = xdr.pos;
|
|
48
49
|
try {
|
|
49
|
-
var op_pos = xdr.pos;
|
|
50
50
|
var tmp_event;
|
|
51
51
|
while (xdr.pos < xdr.buffer.length) {
|
|
52
52
|
do {
|
|
@@ -55,6 +55,8 @@ class EventConnection {
|
|
|
55
55
|
switch (r) {
|
|
56
56
|
case const_1.default.op_event:
|
|
57
57
|
xdr.readInt(); // db handle
|
|
58
|
+
// op_event always carries a payload; readArray only
|
|
59
|
+
// returns undefined for zero-length arrays
|
|
58
60
|
buf = xdr.readArray();
|
|
59
61
|
// first byte is always set to 1
|
|
60
62
|
tmp_event = {};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/***************************************
|
|
2
|
+
*
|
|
3
|
+
* queryStream — object-mode Readable over sequentially()
|
|
4
|
+
*
|
|
5
|
+
***************************************/
|
|
6
|
+
import { Readable } from 'stream';
|
|
7
|
+
/**
|
|
8
|
+
* Build an object-mode Readable that emits one row per chunk, implemented
|
|
9
|
+
* on top of `target.sequentially()`'s next()-based backpressure: fetching
|
|
10
|
+
* pauses whenever the stream's internal buffer is full and resumes when
|
|
11
|
+
* the consumer drains it. Shared by Database.queryStream and
|
|
12
|
+
* Transaction.queryStream.
|
|
13
|
+
*
|
|
14
|
+
* Destroying the stream early (including a pipeline() teardown) aborts the
|
|
15
|
+
* row loop, which releases the statement server-side.
|
|
16
|
+
*/
|
|
17
|
+
declare function makeQueryStream(target: any, query: string, params?: any, options?: any): Readable;
|
|
18
|
+
export = makeQueryStream;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/***************************************
|
|
3
|
+
*
|
|
4
|
+
* queryStream — object-mode Readable over sequentially()
|
|
5
|
+
*
|
|
6
|
+
***************************************/
|
|
7
|
+
const stream_1 = require("stream");
|
|
8
|
+
/**
|
|
9
|
+
* Build an object-mode Readable that emits one row per chunk, implemented
|
|
10
|
+
* on top of `target.sequentially()`'s next()-based backpressure: fetching
|
|
11
|
+
* pauses whenever the stream's internal buffer is full and resumes when
|
|
12
|
+
* the consumer drains it. Shared by Database.queryStream and
|
|
13
|
+
* Transaction.queryStream.
|
|
14
|
+
*
|
|
15
|
+
* Destroying the stream early (including a pipeline() teardown) aborts the
|
|
16
|
+
* row loop, which releases the statement server-side.
|
|
17
|
+
*/
|
|
18
|
+
function makeQueryStream(target, query, params, options) {
|
|
19
|
+
options = options || {};
|
|
20
|
+
const streamOptions = { objectMode: true };
|
|
21
|
+
if (options.highWaterMark !== undefined) {
|
|
22
|
+
streamOptions.highWaterMark = options.highWaterMark;
|
|
23
|
+
}
|
|
24
|
+
// forwarded to sequentially(); these keys would break its plumbing
|
|
25
|
+
const queryOptions = { ...options };
|
|
26
|
+
delete queryOptions.on;
|
|
27
|
+
delete queryOptions.asStream;
|
|
28
|
+
delete queryOptions.highWaterMark;
|
|
29
|
+
// sentinel used to stop the row loop on early destroy; recognized by
|
|
30
|
+
// identity in the completion callback and never surfaced to the user
|
|
31
|
+
const abortError = new Error('queryStream destroyed');
|
|
32
|
+
let pendingNext = null;
|
|
33
|
+
const resume = (err) => {
|
|
34
|
+
if (pendingNext) {
|
|
35
|
+
const next = pendingNext;
|
|
36
|
+
pendingNext = null;
|
|
37
|
+
next(err);
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
const stream = new stream_1.Readable({
|
|
41
|
+
...streamOptions,
|
|
42
|
+
read() {
|
|
43
|
+
resume();
|
|
44
|
+
},
|
|
45
|
+
destroy(err, cb) {
|
|
46
|
+
// if the row loop is paused waiting for us, abort it so the
|
|
47
|
+
// statement is released instead of leaking mid-fetch
|
|
48
|
+
resume(abortError);
|
|
49
|
+
cb(err);
|
|
50
|
+
},
|
|
51
|
+
});
|
|
52
|
+
target.sequentially(query, params, function (row, _index, next) {
|
|
53
|
+
if (stream.destroyed) {
|
|
54
|
+
next(abortError);
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
if (stream.push(row)) {
|
|
58
|
+
next();
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
pendingNext = next;
|
|
62
|
+
}
|
|
63
|
+
}, function (err) {
|
|
64
|
+
if (err && err !== abortError) {
|
|
65
|
+
stream.destroy(err);
|
|
66
|
+
}
|
|
67
|
+
else if (!stream.destroyed) {
|
|
68
|
+
stream.push(null);
|
|
69
|
+
}
|
|
70
|
+
}, queryOptions);
|
|
71
|
+
return stream;
|
|
72
|
+
}
|
|
73
|
+
module.exports = makeQueryStream;
|
package/lib/wire/serialize.d.ts
CHANGED
|
@@ -27,7 +27,7 @@ export declare class BlrReader {
|
|
|
27
27
|
constructor(buffer: Buffer);
|
|
28
28
|
readByteCode(): number;
|
|
29
29
|
readInt32(): number;
|
|
30
|
-
readInt():
|
|
30
|
+
readInt(): number | undefined;
|
|
31
31
|
readString(encoding?: BufferEncoding): string;
|
|
32
32
|
readSegment(): Buffer;
|
|
33
33
|
}
|
|
@@ -69,6 +69,24 @@ export declare class XdrWriter {
|
|
|
69
69
|
export declare class XdrReader {
|
|
70
70
|
buffer: Buffer;
|
|
71
71
|
pos: number;
|
|
72
|
+
/** opcode carried over for a resumed decode (vestigial, see connection.ts) */
|
|
73
|
+
r?: number | null;
|
|
74
|
+
/** partial fetch-op flag (vestigial) */
|
|
75
|
+
fop?: boolean;
|
|
76
|
+
/** fetch status of the current row batch (100 = end of cursor) */
|
|
77
|
+
fstatus?: number;
|
|
78
|
+
/** rows remaining in the current packet */
|
|
79
|
+
fcount?: number;
|
|
80
|
+
/** column index the row decode stopped at */
|
|
81
|
+
fcolumn?: number;
|
|
82
|
+
/** row currently being decoded (object or array) */
|
|
83
|
+
frow?: any;
|
|
84
|
+
/** rows decoded so far in this call */
|
|
85
|
+
frows?: any[];
|
|
86
|
+
/** cached object-row keys (column aliases, qualified when nestTables is set) */
|
|
87
|
+
fcols?: string[];
|
|
88
|
+
/** cached per-column table keys when nestTables === true */
|
|
89
|
+
ftables?: string[];
|
|
72
90
|
constructor(buffer: Buffer);
|
|
73
91
|
readInt(): number;
|
|
74
92
|
readUInt(): number;
|
|
@@ -83,7 +101,7 @@ export declare class XdrReader {
|
|
|
83
101
|
};
|
|
84
102
|
readFloat(): number;
|
|
85
103
|
readDouble(): number;
|
|
86
|
-
readArray(): Buffer<ArrayBuffer
|
|
104
|
+
readArray(): Buffer<ArrayBuffer> | undefined;
|
|
87
105
|
readBuffer(len?: number, toAlign?: boolean): Buffer | undefined;
|
|
88
106
|
readString(encoding: BufferEncoding): string;
|
|
89
107
|
readText(len: number, encoding: BufferEncoding): string;
|
package/lib/wire/serialize.js
CHANGED
|
@@ -408,6 +408,13 @@ class XdrReader {
|
|
|
408
408
|
var len = this.readInt();
|
|
409
409
|
if (!len)
|
|
410
410
|
return;
|
|
411
|
+
// Firebird 2.5 sign-extends XDR opaque lengths above 32767: a
|
|
412
|
+
// 32768-byte array arrives as length 0xFFFF8000. A negative length
|
|
413
|
+
// is never valid, so recover the real length from the low 16 bits
|
|
414
|
+
// instead of corrupting the read position (issue #312 — hang when
|
|
415
|
+
// preparing a statement with very many parameters on FB 2.5).
|
|
416
|
+
if (len < 0)
|
|
417
|
+
len &= 0xFFFF;
|
|
411
418
|
var r = this.buffer.slice(this.pos, this.pos + len);
|
|
412
419
|
this.pos += align(len);
|
|
413
420
|
return r;
|
package/lib/wire/service.d.ts
CHANGED
|
@@ -62,5 +62,47 @@ declare class ServiceManager extends Events.EventEmitter {
|
|
|
62
62
|
hasRunningAction(options: any, callback: any): void;
|
|
63
63
|
readusers(options: any, callback: any): void;
|
|
64
64
|
readlimbo(options: any, callback: any): void;
|
|
65
|
+
detachAsync(force?: boolean): Promise<void>;
|
|
66
|
+
backupAsync(options: any): Promise<stream.Readable>;
|
|
67
|
+
nbackupAsync(options: any): Promise<stream.Readable>;
|
|
68
|
+
restoreAsync(options: any): Promise<stream.Readable>;
|
|
69
|
+
nrestoreAsync(options: any): Promise<stream.Readable>;
|
|
70
|
+
setDialectAsync(db: string, dialect: number): Promise<any>;
|
|
71
|
+
setSweepintervalAsync(db: string, sweepinterval: number): Promise<any>;
|
|
72
|
+
setCachebufferAsync(db: string, nbpages: number): Promise<any>;
|
|
73
|
+
BringOnlineAsync(db: string): Promise<any>;
|
|
74
|
+
ShutdownAsync(db: string, kind: number, delay: number, mode?: any): Promise<any>;
|
|
75
|
+
setShadowAsync(db: string, val: boolean): Promise<any>;
|
|
76
|
+
setForcewriteAsync(db: string, val: boolean): Promise<any>;
|
|
77
|
+
setReservespaceAsync(db: string, val: boolean): Promise<any>;
|
|
78
|
+
setReadonlyModeAsync(db: string): Promise<any>;
|
|
79
|
+
setReadwriteModeAsync(db: string): Promise<any>;
|
|
80
|
+
validateAsync(options: any): Promise<stream.Readable>;
|
|
81
|
+
commitAsync(db: string, transactid: number): Promise<any>;
|
|
82
|
+
rollbackAsync(db: string, transactid: number): Promise<any>;
|
|
83
|
+
recoverAsync(db: string, transactid: number): Promise<any>;
|
|
84
|
+
getStatsAsync(options: any): Promise<stream.Readable>;
|
|
85
|
+
getLogAsync(options: any): Promise<stream.Readable>;
|
|
86
|
+
getUsersAsync(username?: string | null): Promise<any>;
|
|
87
|
+
addUserAsync(username: string, password: string, options?: any): Promise<any>;
|
|
88
|
+
editUserAsync(username: string, options: any): Promise<any>;
|
|
89
|
+
removeUserAsync(username: string, rolename?: string | null): Promise<any>;
|
|
90
|
+
getFbserverInfosAsync(infos?: any, options?: any): Promise<any>;
|
|
91
|
+
startTraceAsync(options: any): Promise<stream.Readable>;
|
|
92
|
+
suspendTraceAsync(options: any): Promise<stream.Readable>;
|
|
93
|
+
resumeTraceAsync(options: any): Promise<stream.Readable>;
|
|
94
|
+
stopTraceAsync(options: any): Promise<stream.Readable>;
|
|
95
|
+
getTraceListAsync(options?: any): Promise<stream.Readable>;
|
|
96
|
+
readlineAsync(options?: any): Promise<{
|
|
97
|
+
result: number;
|
|
98
|
+
line: string;
|
|
99
|
+
}>;
|
|
100
|
+
readeofAsync(options?: any): Promise<{
|
|
101
|
+
result: number;
|
|
102
|
+
line: string;
|
|
103
|
+
}>;
|
|
104
|
+
hasRunningActionAsync(options?: any): Promise<any>;
|
|
105
|
+
readusersAsync(options?: any): Promise<any>;
|
|
106
|
+
readlimboAsync(options?: any): Promise<any>;
|
|
65
107
|
}
|
|
66
108
|
export = ServiceManager;
|