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.
Files changed (50) hide show
  1. package/README.md +211 -6
  2. package/lib/index.d.ts +5 -0
  3. package/lib/index.js +32 -1
  4. package/lib/pool.js +1 -1
  5. package/lib/srp.d.ts +3 -3
  6. package/lib/types.d.ts +165 -5
  7. package/lib/uri.js +2 -2
  8. package/lib/wire/connection.d.ts +92 -59
  9. package/lib/wire/connection.js +279 -58
  10. package/lib/wire/const.d.ts +9 -1
  11. package/lib/wire/const.js +23 -9
  12. package/lib/wire/database.d.ts +51 -26
  13. package/lib/wire/database.js +53 -20
  14. package/lib/wire/eventConnection.js +5 -3
  15. package/lib/wire/query-stream.d.ts +18 -0
  16. package/lib/wire/query-stream.js +73 -0
  17. package/lib/wire/serialize.d.ts +20 -2
  18. package/lib/wire/serialize.js +7 -0
  19. package/lib/wire/service.d.ts +42 -0
  20. package/lib/wire/service.js +145 -0
  21. package/lib/wire/socket.d.ts +3 -1
  22. package/lib/wire/socket.js +5 -2
  23. package/lib/wire/statement.d.ts +31 -19
  24. package/lib/wire/statement.js +1 -5
  25. package/lib/wire/transaction.d.ts +30 -18
  26. package/lib/wire/transaction.js +23 -4
  27. package/lib/wire/wire-types.d.ts +116 -0
  28. package/lib/wire/wire-types.js +10 -0
  29. package/lib/wire/xsqlvar.d.ts +57 -18
  30. package/lib/wire/xsqlvar.js +59 -0
  31. package/package.json +1 -1
  32. package/src/index.ts +36 -4
  33. package/src/messages.ts +1 -1
  34. package/src/pool.ts +1 -1
  35. package/src/srp.ts +6 -6
  36. package/src/types.ts +162 -5
  37. package/src/unix-crypt.ts +9 -9
  38. package/src/uri.ts +2 -2
  39. package/src/wire/connection.ts +475 -234
  40. package/src/wire/const.ts +23 -9
  41. package/src/wire/database.ts +101 -54
  42. package/src/wire/eventConnection.ts +8 -5
  43. package/src/wire/query-stream.ts +80 -0
  44. package/src/wire/serialize.ts +31 -0
  45. package/src/wire/service.ts +188 -6
  46. package/src/wire/socket.ts +17 -8
  47. package/src/wire/statement.ts +37 -29
  48. package/src/wire/transaction.ts +57 -32
  49. package/src/wire/wire-types.ts +127 -0
  50. package/src/wire/xsqlvar.ts +85 -7
@@ -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: any;
16
+ connection: Connection;
4
17
  eventid: number;
5
- constructor(connection: any);
18
+ constructor(connection: Connection);
6
19
  escape(value: any): string;
7
- detach(callback?: (err?: any, obj?: any) => void, force?: boolean): this;
8
- transaction(options: any, callback?: (err: any, transaction?: any) => void): this;
9
- startTransaction(options: any, callback?: (err: any, transaction?: any) => void): this;
10
- newStatement(query: string, callback: (err: any, statement?: any) => void): this;
11
- execute(query: string, params?: any, callback?: any, options?: any): this;
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: any[][], callback?: any, options?: any): this;
22
- sequentially(query: string, params?: any, on?: any, callback?: any, options?: any): this;
23
- query(query: string, params?: any, callback?: any, options?: any): this;
24
- drop(callback?: (err?: any) => void): void;
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 | ((err?: any) => void), callback?: (err?: any) => void): this;
33
- attachEvent(callback: (err: any, evt?: any) => void): this;
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?: any): this;
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?: any): this;
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?: any): this;
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 | ((err?: any) => void), callback?: any): this;
74
- queryAsync(query: string, params?: any, options?: any): Promise<any[]>;
75
- executeAsync(query: string, params?: any, options?: any): Promise<any[]>;
76
- executeBatchAsync(query: string, rows: any[][], options?: any): Promise<any>;
77
- sequentiallyAsync(query: string, params?: any, on?: any, options?: any): Promise<void>;
78
- transactionAsync(options?: any): Promise<any>;
79
- startTransactionAsync(options?: any): Promise<any>;
80
- newStatementAsync(query: string): Promise<any>;
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<any>;
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: any) => Promise<T> | T, options?: any): Promise<T>;
115
+ withTransaction<T>(work: (transaction: Transaction) => Promise<T> | T, options?: TransactionOptions | Isolation): Promise<T>;
91
116
  }
92
117
  export = Database;
@@ -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
- const rowKeys = Object.keys(row);
83
- const blobColumns = [];
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] && meta[i].type === const_1.default.SQL_BLOB && rowKeys[i] !== undefined) {
86
- blobColumns.push(rowKeys[i]);
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 (!blobColumns.length) {
103
+ if (!blobCells.length) {
90
104
  callback(null, row);
91
105
  return;
92
106
  }
93
- let pending = blobColumns.length;
107
+ let pending = blobCells.length;
94
108
  let blobErr;
95
- blobColumns.forEach(function (columnName) {
96
- readblob(row[columnName], function (err, data) {
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
- row[columnName] = data;
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
- }, force);
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
- callback(err);
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
- callback(err);
172
+ cb(err);
156
173
  return;
157
174
  }
158
175
  transaction.commit(function (err) {
159
- callback(err, statement);
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
- fetchBlobSyncRow(row, meta, function (blobErr) {
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;
@@ -27,7 +27,7 @@ export declare class BlrReader {
27
27
  constructor(buffer: Buffer);
28
28
  readByteCode(): number;
29
29
  readInt32(): number;
30
- readInt(): any;
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;
@@ -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;
@@ -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;