node-firebird 2.12.0 → 2.14.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/lib/types.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { Readable } from 'stream';
1
+ import type { Readable, Writable } from 'stream';
2
2
  import type { SqlTag } from './sql-template';
3
3
  export type { SqlTag, SqlQuery, SqlIdentifier, CompiledQuery } from './sql-template';
4
4
  export type DatabaseCallback = (err: any, db: Database) => void;
@@ -193,6 +193,20 @@ export interface QueryResult<T = any> {
193
193
  recordCounts?: RecordCounts;
194
194
  warnings: ServerWarning[];
195
195
  }
196
+ /** Options for batchStream: the executeBatch options plus stream tuning. */
197
+ export type BatchStreamOptions = BatchOptions & {
198
+ /** Rows buffered per executeBatch flush (default 1000). */
199
+ flushRows?: number;
200
+ /** Writable highWaterMark in rows (default: flushRows). */
201
+ highWaterMark?: number;
202
+ };
203
+ /** The Writable returned by batchStream, with totals valid after 'finish'. */
204
+ export interface BatchStream extends Writable {
205
+ /** Records the server processed so far. */
206
+ recordCount: number;
207
+ /** Sum of per-record update counts so far. */
208
+ affectedRows: number;
209
+ }
196
210
  export type QueryStreamOptions = QueryOptions & {
197
211
  /**
198
212
  * Rows buffered internally before fetching pauses (object-mode
@@ -213,8 +227,8 @@ export interface Database {
213
227
  detach(callback?: SimpleCallback): Database;
214
228
  transaction(options: TransactionOptions | Isolation | TransactionCallback, callback?: TransactionCallback): Database;
215
229
  newStatement(query: string, callback: (err: Error | null, statement: Statement) => void): Database;
216
- query(query: string, params: QueryParams, callback: QueryCallback, options?: QueryOptions): Database;
217
- execute(query: string, params: QueryParams, callback: QueryCallback, options?: QueryOptions): Database;
230
+ query<T = any>(query: string, params: QueryParams, callback: (err: any, result: T[], meta?: any[], isSelect?: boolean) => void, options?: QueryOptions): Database;
231
+ execute<T = any>(query: string, params: QueryParams, callback: (err: any, result: T[], meta?: any[], isSelect?: boolean) => void, options?: QueryOptions): Database;
218
232
  /** Bulk-execute in its own transaction, all-or-nothing (Firebird 4.0+). */
219
233
  executeBatch(query: string, rows: QueryParams[], callback?: (err: any, result: BatchResult) => void, options?: BatchOptions): Database;
220
234
  sequentially(query: string, params: QueryParams, rowCallback: SequentialCallback, callback: SimpleCallback, options?: QueryOptions | boolean): Database;
@@ -225,6 +239,13 @@ export interface Database {
225
239
  * fetch and releases the statement.
226
240
  */
227
241
  queryStream(query: string, params?: QueryParams, options?: QueryStreamOptions): Readable;
242
+ /**
243
+ * Bulk-insert Writable (COPY FROM analogue, Firebird 4.0+): write
244
+ * parameter-array rows; they are flushed in chunks through the batch
245
+ * API. Runs its own transaction — committed on finish, rolled back on
246
+ * error/destroy. BLOB columns accept Buffers/strings.
247
+ */
248
+ batchStream(query: string, options?: BatchStreamOptions): BatchStream;
228
249
  drop(callback: SimpleCallback): void;
229
250
  escape(value: any): string;
230
251
  attachEvent(callback: any): this;
@@ -270,8 +291,8 @@ export interface Transaction {
270
291
  */
271
292
  savepoint<T>(work: (transaction: Transaction) => Promise<T> | T): Promise<T>;
272
293
  newStatement(query: string, callback: (err: Error | null, statement: Statement) => void): void;
273
- query(query: string, params: QueryParams, callback: QueryCallback, options?: QueryOptions): void;
274
- execute(query: string, params: QueryParams, callback: QueryCallback, options?: QueryOptions): void;
294
+ query<T = any>(query: string, params: QueryParams, callback: (err: any, result: T[], meta?: any[], isSelect?: boolean) => void, options?: QueryOptions): void;
295
+ execute<T = any>(query: string, params: QueryParams, callback: (err: any, result: T[], meta?: any[], isSelect?: boolean) => void, options?: QueryOptions): void;
275
296
  /** Bulk-execute within this transaction; per-record failures do not roll back (Firebird 4.0+). */
276
297
  executeBatch(query: string, rows: QueryParams[], callback?: (err: any, result: BatchResult) => void, options?: BatchOptions): void;
277
298
  sequentially(query: string, params: QueryParams, rowCallback: SequentialCallback, callback: SimpleCallback, options?: QueryOptions | boolean): Database;
@@ -281,6 +302,11 @@ export interface Transaction {
281
302
  * transaction is NOT committed when the stream ends.
282
303
  */
283
304
  queryStream(query: string, params?: QueryParams, options?: QueryStreamOptions): Readable;
305
+ /**
306
+ * Bulk-insert Writable inside this transaction (see
307
+ * Database.batchStream); commit/rollback stays with the caller.
308
+ */
309
+ batchStream(query: string, options?: BatchStreamOptions): BatchStream;
284
310
  commit(callback?: SimpleCallback): void;
285
311
  commitRetaining(callback?: SimpleCallback): void;
286
312
  rollback(callback?: SimpleCallback): void;
@@ -358,6 +384,13 @@ export interface Options {
358
384
  * per-query `namedPlaceholders: false` override.
359
385
  */
360
386
  namedPlaceholders?: boolean;
387
+ /**
388
+ * Default character set of a NEWLY CREATED database (create /
389
+ * attachOrCreate only). Falls back to the connection `encoding`, then
390
+ * UTF8 — pass e.g. `defaultCharset: 'UTF8'` to keep a modern database
391
+ * default while connecting with a legacy codepage `encoding`.
392
+ */
393
+ defaultCharset?: string;
361
394
  /**
362
395
  * Qualify object-row keys by source table (same option as mysql2), so
363
396
  * JOINed columns with the same name stop overwriting each other:
package/lib/utils.d.ts CHANGED
@@ -6,6 +6,18 @@ export declare const parseDate: (str: string) => Date;
6
6
  /**
7
7
  * Get Error Message per gdscode
8
8
  */
9
+ /**
10
+ * Turn a failed executeBatch completion into the all-or-nothing error
11
+ * shape shared by database.executeBatch and batchStream: the first
12
+ * record's own error (or a synthesized summary), with the full
13
+ * completion attached as err.batchCompletion.
14
+ */
15
+ export declare const batchResultToError: (result: {
16
+ errors: {
17
+ error: any;
18
+ }[];
19
+ errorRecordNumbers: number[];
20
+ }) => any;
9
21
  export declare const lookupMessages: (status: FbStatusItem[]) => string;
10
22
  /**
11
23
  * Escape value
package/lib/utils.js CHANGED
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.escape = exports.lookupMessages = exports.parseDate = void 0;
6
+ exports.escape = exports.lookupMessages = exports.batchResultToError = exports.parseDate = void 0;
7
7
  exports.noop = noop;
8
8
  const firebird_msg_json_1 = __importDefault(require("./firebird.msg.json"));
9
9
  const const_1 = __importDefault(require("./wire/const"));
@@ -97,6 +97,21 @@ exports.parseDate = parseDate;
97
97
  /**
98
98
  * Get Error Message per gdscode
99
99
  */
100
+ /**
101
+ * Turn a failed executeBatch completion into the all-or-nothing error
102
+ * shape shared by database.executeBatch and batchStream: the first
103
+ * record's own error (or a synthesized summary), with the full
104
+ * completion attached as err.batchCompletion.
105
+ */
106
+ const batchResultToError = (result) => {
107
+ const first = result.errors.length ? result.errors[0] : null;
108
+ const err = first
109
+ ? first.error
110
+ : new Error('Batch failed for record(s) ' + result.errorRecordNumbers.join(', '));
111
+ err.batchCompletion = result;
112
+ return err;
113
+ };
114
+ exports.batchResultToError = batchResultToError;
100
115
  const lookupMessages = (status) => {
101
116
  const messages = status.map((item) => {
102
117
  let text = MessagesError[item.gdscode];
@@ -130,7 +145,10 @@ const escape = function (value, protocolVersion) {
130
145
  case 'number':
131
146
  return value.toString();
132
147
  case 'string':
133
- return "'" + value.replace(/'/g, "''").replace(/\\/g, '\\\\') + "'";
148
+ // Firebird string literals have NO backslash escapes — only the
149
+ // quote is doubled. Doubling backslashes corrupted the data
150
+ // (issue #156: '\' arrived as '\\').
151
+ return "'" + value.replace(/'/g, "''") + "'";
134
152
  }
135
153
  if (value instanceof Date)
136
154
  return "'" + value.getFullYear() + '-' + (value.getMonth() + 1).toString().padStart(2, '0') + '-' + value.getDate().toString().padStart(2, '0') + ' ' + value.getHours().toString().padStart(2, '0') + ':' + value.getMinutes().toString().padStart(2, '0') + ':' + value.getSeconds().toString().padStart(2, '0') + '.' + value.getMilliseconds().toString().padStart(3, '0') + "'";
@@ -0,0 +1,26 @@
1
+ /***************************************
2
+ *
3
+ * batchStream — object-mode Writable over the Firebird 4 batch API
4
+ *
5
+ * The COPY FROM analogue: write parameter rows, they are flushed in
6
+ * chunks through statement.executeBatch (single prepared statement,
7
+ * protocol-level batching, BLOB values included). Backpressure is the
8
+ * Writable machinery itself: a write callback is held while a chunk
9
+ * is in flight.
10
+ *
11
+ ***************************************/
12
+ import { Writable } from 'stream';
13
+ /**
14
+ * Build the Writable for Database.batchStream / Transaction.batchStream.
15
+ * With `ownsTransaction` (the Database form) the stream runs its own
16
+ * transaction: committed on finish, rolled back on error/destroy —
17
+ * all-or-nothing for the whole stream. The Transaction form leaves
18
+ * commit/rollback to the caller.
19
+ *
20
+ * Rows accumulate up to options.flushRows (default 1000) per
21
+ * executeBatch flush; the remaining executeBatch options (chunkSize,
22
+ * bufferSize, …) pass through. After 'finish', stream.recordCount and
23
+ * stream.affectedRows carry the totals.
24
+ */
25
+ declare function makeBatchStream(target: any, query: string, options: any, ownsTransaction: boolean): Writable;
26
+ export = makeBatchStream;
@@ -0,0 +1,109 @@
1
+ "use strict";
2
+ /***************************************
3
+ *
4
+ * batchStream — object-mode Writable over the Firebird 4 batch API
5
+ *
6
+ * The COPY FROM analogue: write parameter rows, they are flushed in
7
+ * chunks through statement.executeBatch (single prepared statement,
8
+ * protocol-level batching, BLOB values included). Backpressure is the
9
+ * Writable machinery itself: a write callback is held while a chunk
10
+ * is in flight.
11
+ *
12
+ ***************************************/
13
+ const stream_1 = require("stream");
14
+ const callback_1 = require("../callback");
15
+ const utils_1 = require("../utils");
16
+ /**
17
+ * Build the Writable for Database.batchStream / Transaction.batchStream.
18
+ * With `ownsTransaction` (the Database form) the stream runs its own
19
+ * transaction: committed on finish, rolled back on error/destroy —
20
+ * all-or-nothing for the whole stream. The Transaction form leaves
21
+ * commit/rollback to the caller.
22
+ *
23
+ * Rows accumulate up to options.flushRows (default 1000) per
24
+ * executeBatch flush; the remaining executeBatch options (chunkSize,
25
+ * bufferSize, …) pass through. After 'finish', stream.recordCount and
26
+ * stream.affectedRows carry the totals.
27
+ */
28
+ function makeBatchStream(target, query, options, ownsTransaction) {
29
+ options = options || {};
30
+ const flushRows = options.flushRows > 0 ? Math.floor(options.flushRows) : 1000;
31
+ const batchOptions = { ...options };
32
+ delete batchOptions.flushRows;
33
+ delete batchOptions.highWaterMark;
34
+ let transaction = null;
35
+ let statement = null;
36
+ let buffered = [];
37
+ const init = async () => {
38
+ if (statement) {
39
+ return;
40
+ }
41
+ transaction = ownsTransaction ? await target.transactionAsync() : target;
42
+ statement = await (0, callback_1.fromCallback)((cb) => transaction.newStatement(query, cb));
43
+ };
44
+ const flush = async () => {
45
+ if (!buffered.length) {
46
+ return;
47
+ }
48
+ await init();
49
+ const chunk = buffered;
50
+ buffered = [];
51
+ const result = await (0, callback_1.fromCallback)((cb) => statement.executeBatch(transaction, chunk, cb, batchOptions));
52
+ if (!result.success) {
53
+ // the same all-or-nothing error shape database.executeBatch uses
54
+ throw (0, utils_1.batchResultToError)(result);
55
+ }
56
+ stream.recordCount += result.recordCount;
57
+ for (const count of result.updateCounts) {
58
+ stream.affectedRows += count;
59
+ }
60
+ };
61
+ const cleanup = async (commit) => {
62
+ if (statement) {
63
+ const stmt = statement;
64
+ statement = null;
65
+ await new Promise((resolve) => stmt.release(() => resolve()));
66
+ }
67
+ if (ownsTransaction && transaction) {
68
+ const tx = transaction;
69
+ transaction = null;
70
+ await (commit ? tx.commitAsync() : tx.rollbackAsync());
71
+ }
72
+ };
73
+ const stream = new stream_1.Writable({
74
+ objectMode: true,
75
+ highWaterMark: options.highWaterMark > 0 ? options.highWaterMark : flushRows,
76
+ write(row, _enc, cb) {
77
+ if (!Array.isArray(row)) {
78
+ cb(new Error('batchStream expects parameter-array rows'));
79
+ return;
80
+ }
81
+ buffered.push(row);
82
+ if (buffered.length >= flushRows) {
83
+ flush().then(() => cb(), cb);
84
+ }
85
+ else {
86
+ cb();
87
+ }
88
+ },
89
+ final(cb) {
90
+ // an empty stream finishes without touching the server at all
91
+ // (flush() early-returns and init never runs)
92
+ flush()
93
+ .then(() => cleanup(true))
94
+ .then(() => cb(), (err) => {
95
+ // the failed stream must not commit half a bulk load
96
+ cleanup(false).catch(() => { });
97
+ cb(err);
98
+ });
99
+ },
100
+ destroy(err, cb) {
101
+ cleanup(false)
102
+ .then(() => cb(err), () => cb(err));
103
+ },
104
+ });
105
+ stream.recordCount = 0;
106
+ stream.affectedRows = 0;
107
+ return stream;
108
+ }
109
+ module.exports = makeBatchStream;
@@ -0,0 +1,23 @@
1
+ /***************************************
2
+ *
3
+ * Single-byte codepage codecs (WIN125x, ISO8859_x, KOI8, DOS866)
4
+ *
5
+ * Node's Buffer only decodes utf8/latin1/ascii natively. These
6
+ * codepages are decoded through the WHATWG TextDecoder (backed by
7
+ * ICU — present in every official Node build) and encoded through
8
+ * reverse tables built from the same decoder at first use, so the
9
+ * two directions can never disagree. Issues #319/#301/#422.
10
+ *
11
+ ***************************************/
12
+ export interface TextCodec {
13
+ /** Firebird charset name (upper case). */
14
+ name: string;
15
+ decode(buffer: Buffer): string;
16
+ encode(value: string): Buffer;
17
+ }
18
+ export declare function charsetWidthById(id: number | undefined): number;
19
+ /**
20
+ * Codec for a Firebird charset name, or null when the charset is unknown,
21
+ * natively handled by Buffer, or the ICU tables are unavailable. Cached.
22
+ */
23
+ export declare function getCodec(charsetName: string | undefined): TextCodec | null;
@@ -0,0 +1,137 @@
1
+ "use strict";
2
+ /***************************************
3
+ *
4
+ * Single-byte codepage codecs (WIN125x, ISO8859_x, KOI8, DOS866)
5
+ *
6
+ * Node's Buffer only decodes utf8/latin1/ascii natively. These
7
+ * codepages are decoded through the WHATWG TextDecoder (backed by
8
+ * ICU — present in every official Node build) and encoded through
9
+ * reverse tables built from the same decoder at first use, so the
10
+ * two directions can never disagree. Issues #319/#301/#422.
11
+ *
12
+ ***************************************/
13
+ Object.defineProperty(exports, "__esModule", { value: true });
14
+ exports.charsetWidthById = charsetWidthById;
15
+ exports.getCodec = getCodec;
16
+ /** Firebird charset name → WHATWG encoding label (single-byte only). */
17
+ const ICU_LABELS = Object.freeze({
18
+ WIN1250: 'windows-1250',
19
+ WIN1251: 'windows-1251',
20
+ WIN1253: 'windows-1253',
21
+ WIN1254: 'windows-1254',
22
+ WIN1255: 'windows-1255',
23
+ WIN1256: 'windows-1256',
24
+ WIN1257: 'windows-1257',
25
+ WIN1258: 'windows-1258',
26
+ ISO8859_2: 'iso-8859-2',
27
+ ISO8859_3: 'iso-8859-3',
28
+ ISO8859_4: 'iso-8859-4',
29
+ ISO8859_5: 'iso-8859-5',
30
+ ISO8859_6: 'iso-8859-6',
31
+ ISO8859_7: 'iso-8859-7',
32
+ ISO8859_8: 'iso-8859-8',
33
+ ISO8859_9: 'iso-8859-9',
34
+ ISO8859_13: 'iso-8859-13',
35
+ KOI8R: 'koi8-r',
36
+ KOI8U: 'koi8-u',
37
+ DOS866: 'ibm866',
38
+ });
39
+ /**
40
+ * Bytes-per-character by Firebird charset id (RDB$CHARACTER_SETS —
41
+ * verified against a live server). Everything not listed (NONE, ASCII,
42
+ * ISO8859_x, WIN125x, DOS*, KOI8*, CYRL, TIS620, …) is single-byte.
43
+ */
44
+ const CHARSET_WIDTH_BY_ID = Object.freeze({
45
+ 3: 3, // UNICODE_FSS
46
+ 4: 4, // UTF8
47
+ 5: 2, // SJIS_0208
48
+ 6: 2, // EUCJ_0208
49
+ 44: 2, // KSC_5601
50
+ 56: 2, // BIG_5
51
+ 57: 2, // GB_2312
52
+ 67: 2, // GBK
53
+ 68: 2, // CP943C
54
+ 69: 4, // GB18030
55
+ });
56
+ function charsetWidthById(id) {
57
+ if (id === undefined) {
58
+ return 1;
59
+ }
60
+ return CHARSET_WIDTH_BY_ID[id] || 1;
61
+ }
62
+ const cache = new Map();
63
+ function buildCodec(name) {
64
+ const label = ICU_LABELS[name];
65
+ if (!label) {
66
+ return null;
67
+ }
68
+ let decoder;
69
+ try {
70
+ decoder = new TextDecoder(label);
71
+ }
72
+ catch {
73
+ // Node built with small-icu: legacy encodings unavailable
74
+ return null;
75
+ }
76
+ // Build both directions from the decoder, one byte at a time — every
77
+ // byte of a single-byte codepage maps to exactly one BMP character
78
+ // (undefined bytes decode to U+FFFD, which is kept for decoding but
79
+ // never used for the reverse map).
80
+ const toCode = new Uint16Array(256);
81
+ const toByte = new Map();
82
+ const one = Buffer.alloc(1);
83
+ for (let i = 0; i < 256; i++) {
84
+ one[0] = i;
85
+ const ch = decoder.decode(one);
86
+ toCode[i] = ch.charCodeAt(0);
87
+ if (ch !== '�' && !toByte.has(ch)) {
88
+ toByte.set(ch, i);
89
+ }
90
+ }
91
+ return {
92
+ name,
93
+ decode(buffer) {
94
+ // batch through fromCharCode instead of per-byte string concat —
95
+ // wide CHAR columns and text blobs decode in O(chunks) allocations
96
+ const codes = new Array(buffer.length);
97
+ for (let i = 0; i < buffer.length; i++) {
98
+ codes[i] = toCode[buffer[i]];
99
+ }
100
+ const CHUNK = 4096;
101
+ if (codes.length <= CHUNK) {
102
+ return String.fromCharCode(...codes);
103
+ }
104
+ let out = '';
105
+ for (let i = 0; i < codes.length; i += CHUNK) {
106
+ out += String.fromCharCode(...codes.slice(i, i + CHUNK));
107
+ }
108
+ return out;
109
+ },
110
+ encode(value) {
111
+ const out = Buffer.alloc(value.length);
112
+ for (let i = 0; i < value.length; i++) {
113
+ const b = toByte.get(value[i]);
114
+ // unmappable characters become '?' — the convention every
115
+ // codepage transcoder (incl. iconv) uses by default
116
+ out[i] = b === undefined ? 0x3f : b;
117
+ }
118
+ return out;
119
+ },
120
+ };
121
+ }
122
+ /**
123
+ * Codec for a Firebird charset name, or null when the charset is unknown,
124
+ * natively handled by Buffer, or the ICU tables are unavailable. Cached.
125
+ */
126
+ function getCodec(charsetName) {
127
+ if (!charsetName) {
128
+ return null;
129
+ }
130
+ const name = String(charsetName).toUpperCase();
131
+ let codec = cache.get(name);
132
+ if (codec === undefined) {
133
+ codec = buildCodec(name);
134
+ cache.set(name, codec);
135
+ }
136
+ return codec;
137
+ }
@@ -78,6 +78,17 @@ declare class Connection {
78
78
  */
79
79
  releaseStatement(statement: Statement, callback?: QueueCallback): void;
80
80
  _rejectPending(err: any): void;
81
+ /**
82
+ * Deliver a connection-level error to 'error' listeners — and ONLY to
83
+ * listeners. Emitting an unlistened 'error' makes Node throw the error
84
+ * object as an uncaught exception; for errors that originate in
85
+ * background contexts (the reconnect timer, socket-level failures whose
86
+ * operations are separately rejected via _rejectPending) that crashes
87
+ * the process — or, under a test runner, fails whatever unrelated test
88
+ * happens to be running. The failing operations themselves always
89
+ * still receive their error through their own callbacks.
90
+ */
91
+ _emitError(err: any): void;
81
92
  _bind_events(host: string, port: number, callback: SimpleCallback | undefined): void;
82
93
  disconnect(): void;
83
94
  sendOpContAuth(authData: string, authDataEnc: BufferEncoding, pluginName: string): void;
@@ -120,12 +131,18 @@ declare class Connection {
120
131
  * gets an in-order response (op_batch_cs for exec), so the regular
121
132
  * response queue keeps everything in sync.
122
133
  *
123
- * rows: array of parameter arrays, one per record. BLOB/ARRAY columns
124
- * are not supported yet. The callback receives a completion object:
125
- * { recordCount, updateCounts, errors: [{recordNumber, error}],
126
- * errorRecordNumbers, success }.
134
+ * rows: array of parameter arrays, one per record. BLOB columns accept
135
+ * Buffers, strings, JSON-able objects or pre-created blob quad ids
136
+ * values are uploaded as transaction blobs first (all initiated
137
+ * back-to-back so they pipeline) and the batch messages reference their
138
+ * ids. ARRAY columns are not supported. The callback receives a
139
+ * completion object: { recordCount, updateCounts, errors:
140
+ * [{recordNumber, error}], errorRecordNumbers, success }.
127
141
  */
128
142
  executeBatch(transaction: Transaction, statement: Statement, rows: QueryParams[], callback: BatchCb | undefined, options?: BatchOptions): this | undefined;
143
+ /** Encode and send the batch packets (rows are fully materialized:
144
+ * blob values already replaced by quad ids by executeBatch). */
145
+ _executeBatchEncoded(transaction: Transaction, statement: Statement, rows: any[][], callback: BatchCb | undefined, options: BatchOptions): void;
129
146
  /** `params` may be the callback itself when the statement has no parameters. */
130
147
  executeStatement(transaction: Transaction, statement: Statement, params: any, callback?: QueueCallback, custom?: InternalQueryOptions): this | undefined;
131
148
  sendExecute(op: number, statement: Statement, transaction: Transaction, callback: QueueCallback | undefined, parameters?: any[]): void;
@@ -138,12 +155,29 @@ declare class Connection {
138
155
  * response is a plain op_response whose buffer holds the info clusters.
139
156
  */
140
157
  statementInfo(statement: Statement, items: number[], callback?: QueueCallback): this | undefined;
158
+ /**
159
+ * Resolve the pending blobAsText fetches of a decoded row batch
160
+ * (ret.arrBlob) and write the text back into ret.data. Reads run
161
+ * sequentially to respect Firebird's per-connection open-blob-handle
162
+ * limit (issue #387). Used by fetchAll for cursors and by
163
+ * transaction.execute for op_execute2 singletons (EXECUTE PROCEDURE /
164
+ * RETURNING — issue #305, whose blobs never resolved before).
165
+ */
166
+ resolveTextBlobs(transaction: Transaction, ret: any, callback: (err?: any) => void): void;
141
167
  fetchAll(statement: Statement, transaction: Transaction, callback: Callback<any[]>): void;
142
168
  openBlob(blob: Quad, transaction: Transaction, callback: QueueCallback): void;
143
169
  closeBlob(blob: any, callback?: QueueCallback, defer?: boolean): void;
144
170
  getSegment(blob: any, callback: QueueCallback): void;
145
171
  createBlob2(transaction: Transaction, callback: QueueCallback): void;
146
172
  batchSegments(blob: any, buffer: Buffer, callback: QueueCallback): void;
173
+ /**
174
+ * Create a transaction blob, upload `value` (Buffer, string, or a
175
+ * JSON-able object) and deliver its quad id. executeBatch's blob
176
+ * pre-pass uses this: batch messages reference pre-created transaction
177
+ * blobs (the batch parameter buffer's default BLOB_NONE policy), just
178
+ * like the classic execute path stores blob params.
179
+ */
180
+ uploadBlob(transaction: Transaction, value: any, callback: (err: any, oid?: any) => void): void;
147
181
  svcattach(options: InternalOptions, callback?: Callback<ServiceManager>, svc?: ServiceManager): void;
148
182
  svcstart(spbaction: BlrWriter, callback: QueueCallback | undefined): void;
149
183
  svcquery(spbquery: number[], resultbuffersize: number, timeout: number | undefined, callback: QueueCallback | undefined): void;