node-firebird 2.10.0 → 2.12.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/wire/const.js CHANGED
@@ -540,6 +540,12 @@ const sqlInfo = {
540
540
  isc_info_sql_stmt_type: 21,
541
541
  isc_info_sql_get_plan: 22,
542
542
  isc_info_sql_records: 23,
543
+ // per-verb row counts nested inside an isc_info_sql_records cluster
544
+ // (inf_pub.h isc_info_req_*)
545
+ isc_info_req_select_count: 13,
546
+ isc_info_req_insert_count: 14,
547
+ isc_info_req_update_count: 15,
548
+ isc_info_req_delete_count: 16,
543
549
  isc_info_sql_batch_fetch: 24,
544
550
  isc_info_sql_relation_alias: 25, // >: 2.0
545
551
  isc_info_sql_explain_plan: 26, // >= 3.0
@@ -577,6 +583,7 @@ const DESCRIBE = [
577
583
  sqlInfo.isc_info_sql_length,
578
584
  sqlInfo.isc_info_sql_field,
579
585
  sqlInfo.isc_info_sql_relation,
586
+ sqlInfo.isc_info_sql_relation_alias, // FB 2.0+: query alias of the source relation (nestTables)
580
587
  //isc_info_sql_owner,
581
588
  sqlInfo.isc_info_sql_alias,
582
589
  sqlInfo.isc_info_sql_describe_end,
@@ -604,6 +611,7 @@ const DESCRIBE_WITH_SCHEMA = [
604
611
  sqlInfo.isc_info_sql_field,
605
612
  sqlInfo.isc_info_sql_relation,
606
613
  sqlInfo.isc_info_sql_relation_schema, // FB 6.0: schema of source relation
614
+ sqlInfo.isc_info_sql_relation_alias, // query alias of the source relation (nestTables)
607
615
  //isc_info_sql_owner,
608
616
  sqlInfo.isc_info_sql_alias,
609
617
  sqlInfo.isc_info_sql_describe_end,
@@ -616,6 +624,11 @@ const DESCRIBE_WITH_SCHEMA = [
616
624
  sqlInfo.isc_info_sql_length,
617
625
  sqlInfo.isc_info_sql_describe_end
618
626
  ];
627
+ // op_info_sql request for the per-verb DML row counts of an executed
628
+ // statement (withMeta / affectedRows).
629
+ const RECORDS_INFO = [
630
+ sqlInfo.isc_info_sql_records,
631
+ ];
619
632
  /***********************/
620
633
  /* ISC Services */
621
634
  /***********************/
@@ -841,6 +854,7 @@ const Const = Object.freeze({
841
854
  ...defaultOptions,
842
855
  DESCRIBE,
843
856
  DESCRIBE_WITH_SCHEMA,
857
+ RECORDS_INFO,
844
858
  ...dpb,
845
859
  ...dsql,
846
860
  ...fetchOp,
@@ -1,5 +1,6 @@
1
1
  import Events from 'events';
2
2
  import { type Callback, type SimpleCallback } from '../callback';
3
+ import { type SqlTag } from '../sql-template';
3
4
  import FbEventManager from './fbEventManager';
4
5
  import type Connection from './connection';
5
6
  import type Transaction from './transaction';
@@ -15,7 +16,15 @@ type TransactionArg = TransactionOptions | Isolation | TransactionCb | undefined
15
16
  declare class Database extends Events.EventEmitter {
16
17
  connection: Connection;
17
18
  eventid: number;
19
+ private _sql?;
18
20
  constructor(connection: Connection);
21
+ /**
22
+ * Tagged-template query API: db.sql`SELECT ... ${value}` (see README).
23
+ * Built lazily on first access; the compiled text is positional-only,
24
+ * so the namedPlaceholders rewriter is disabled — any `:token` in the
25
+ * template is PSQL (EXECUTE BLOCK), not a placeholder.
26
+ */
27
+ get sql(): SqlTag;
19
28
  escape(value: any): string;
20
29
  detach(callback?: Callback, force?: boolean): this;
21
30
  transaction(options: TransactionArg, callback?: TransactionCb): this;
@@ -6,6 +6,8 @@ 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 sql_template_1 = require("../sql-template");
10
+ const xsqlvar_1 = require("./xsqlvar");
9
11
  const eventConnection_1 = __importDefault(require("./eventConnection"));
10
12
  const fbEventManager_1 = __importDefault(require("./fbEventManager"));
11
13
  const query_stream_1 = __importDefault(require("./query-stream"));
@@ -75,30 +77,42 @@ function readblob(blob, callback) {
75
77
  });
76
78
  });
77
79
  }
78
- function fetchBlobSyncRow(row, meta, callback) {
79
- if (!row || !meta || !meta.length) {
80
+ function fetchBlobSyncRow(row, meta, nestTables, lowercaseKeys, transform, callback) {
81
+ if (!row || !meta || !meta.length || !meta.some((m) => m && m.type === const_1.default.SQL_BLOB)) {
80
82
  callback(null, row);
81
83
  return;
82
84
  }
83
- const rowKeys = Object.keys(row);
84
- const blobColumns = [];
85
+ // locate blob cells by the same key computation the fetch decoder used,
86
+ // rather than assuming Object.keys(row) is index-aligned with meta —
87
+ // duplicate JOIN column names (and nested rows) break that alignment.
88
+ // Array rows (sequentially's legacy boolean form) are keyed by index.
89
+ const isArrayRow = Array.isArray(row);
90
+ const keys = isArrayRow ? null : (0, xsqlvar_1.computeColumnKeys)(meta, nestTables, lowercaseKeys, transform);
91
+ const blobCells = [];
85
92
  for (let i = 0; i < meta.length; i++) {
86
- if (meta[i] && meta[i].type === const_1.default.SQL_BLOB && rowKeys[i] !== undefined) {
87
- blobColumns.push(rowKeys[i]);
93
+ if (!meta[i] || meta[i].type !== const_1.default.SQL_BLOB) {
94
+ continue;
95
+ }
96
+ const target = keys ? (0, xsqlvar_1.nestCell)(row, keys[i].table) : row;
97
+ const key = keys ? keys[i].key : i;
98
+ // duplicate aliases collapse onto one cell — read it only once
99
+ if (typeof target[key] === 'function' &&
100
+ !blobCells.some((cell) => cell.target === target && cell.key === key)) {
101
+ blobCells.push({ target, key });
88
102
  }
89
103
  }
90
- if (!blobColumns.length) {
104
+ if (!blobCells.length) {
91
105
  callback(null, row);
92
106
  return;
93
107
  }
94
- let pending = blobColumns.length;
108
+ let pending = blobCells.length;
95
109
  let blobErr;
96
- blobColumns.forEach(function (columnName) {
97
- readblob(row[columnName], function (err, data) {
110
+ blobCells.forEach(function (cell) {
111
+ readblob(cell.target[cell.key], function (err, data) {
98
112
  if (err && !blobErr) {
99
113
  blobErr = err;
100
114
  }
101
- row[columnName] = data;
115
+ cell.target[cell.key] = data;
102
116
  pending--;
103
117
  if (pending === 0) {
104
118
  callback(blobErr, row);
@@ -113,6 +127,15 @@ class Database extends events_1.default.EventEmitter {
113
127
  connection.db = this;
114
128
  this.eventid = 1;
115
129
  }
130
+ /**
131
+ * Tagged-template query API: db.sql`SELECT ... ${value}` (see README).
132
+ * Built lazily on first access; the compiled text is positional-only,
133
+ * so the namedPlaceholders rewriter is disabled — any `:token` in the
134
+ * template is PSQL (EXECUTE BLOCK), not a placeholder.
135
+ */
136
+ get sql() {
137
+ return this._sql || (this._sql = (0, sql_template_1.makeSqlTag)((text, params, options) => this.queryAsync(text, params, { ...options, namedPlaceholders: false })));
138
+ }
116
139
  escape(value) {
117
140
  return (0, utils_1.escape)(value, this.connection.accept.protocolVersion);
118
141
  }
@@ -250,6 +273,9 @@ class Database extends events_1.default.EventEmitter {
250
273
  callback = undefined;
251
274
  }
252
275
  var self = this;
276
+ var keyResolutionDone = false;
277
+ var resolvedNest;
278
+ var resolvedTransform;
253
279
  var _on = function (row, i, meta, next) {
254
280
  var done = false;
255
281
  var finish = function (err) {
@@ -259,7 +285,15 @@ class Database extends events_1.default.EventEmitter {
259
285
  done = true;
260
286
  next(err);
261
287
  };
262
- fetchBlobSyncRow(row, meta, function (blobErr) {
288
+ // options is read at call time, after the normalization below;
289
+ // both values are query-invariant, so resolve them once on the
290
+ // first row instead of allocating per row
291
+ if (!keyResolutionDone) {
292
+ resolvedNest = (0, xsqlvar_1.resolveNestTables)(options, self.connection.options);
293
+ resolvedTransform = (0, xsqlvar_1.resolveKeyTransform)(options, self.connection.options);
294
+ keyResolutionDone = true;
295
+ }
296
+ fetchBlobSyncRow(row, meta, resolvedNest, self.connection._lowercase_keys, resolvedTransform, function (blobErr) {
263
297
  if (blobErr) {
264
298
  finish(blobErr);
265
299
  return;
@@ -453,8 +487,9 @@ class Database extends events_1.default.EventEmitter {
453
487
  /*
454
488
  * Promise / async-await API.
455
489
  * Each *Async method wraps its callback counterpart; the callback API
456
- * stays untouched. Result metadata is only available through the
457
- * callback API — the promises resolve with the rows alone.
490
+ * stays untouched. The promises resolve with the rows alone unless
491
+ * { withMeta: true } is passed, which resolves the full
492
+ * { rows, fields, affectedRows, recordCounts, warnings } result.
458
493
  */
459
494
  queryAsync(query, params, options) {
460
495
  var self = this;
@@ -83,8 +83,10 @@ export declare class XdrReader {
83
83
  frow?: any;
84
84
  /** rows decoded so far in this call */
85
85
  frows?: any[];
86
- /** cached object-row keys (column aliases) */
86
+ /** cached object-row keys (column aliases, qualified when nestTables is set) */
87
87
  fcols?: string[];
88
+ /** cached per-column table keys when nestTables === true */
89
+ ftables?: string[];
88
90
  constructor(buffer: Buffer);
89
91
  readInt(): number;
90
92
  readUInt(): number;
@@ -160,6 +160,10 @@ class BlrReader {
160
160
  break;
161
161
  case 4:
162
162
  value = this.buffer.readInt32LE(this.pos);
163
+ break;
164
+ case 8:
165
+ // e.g. record counts above 2^31 (isc_info_sql_records)
166
+ value = Number(this.buffer.readBigInt64LE(this.pos));
163
167
  }
164
168
  this.pos += len;
165
169
  return value;
@@ -1,4 +1,5 @@
1
1
  import { type Callback, type SimpleCallback } from '../callback';
2
+ import { type SqlTag } from '../sql-template';
2
3
  import type Connection from './connection';
3
4
  import type Database from './database';
4
5
  import type Statement from './statement';
@@ -8,7 +9,33 @@ declare class Transaction {
8
9
  connection: Connection;
9
10
  db: Database;
10
11
  handle: number;
12
+ private _sql?;
11
13
  constructor(connection: Connection);
14
+ /**
15
+ * Tagged-template query API: tx.sql`SELECT ... ${value}` (see README).
16
+ * Built lazily — transactions are created per-query internally, and
17
+ * those throwaway instances must not pay for the tag. The compiled text
18
+ * is positional-only, so the namedPlaceholders rewriter is disabled:
19
+ * any `:token` in the template is PSQL (EXECUTE BLOCK), not a
20
+ * placeholder.
21
+ */
22
+ get sql(): SqlTag;
23
+ /** Current savepoint nesting depth (names savepoints, see savepoint()). */
24
+ private _savepointDepth;
25
+ /**
26
+ * Run `work` inside a savepoint (Firebird 1.5+): on resolve the
27
+ * savepoint is released, on reject the transaction rolls back TO the
28
+ * savepoint — undoing only work's changes — and the error is rethrown,
29
+ * leaving the transaction itself usable. Nestable (each call generates
30
+ * a fresh NF_SP_n name), mirroring db.withTransaction's style and
31
+ * Postgres.js's sql.savepoint().
32
+ *
33
+ * Do NOT run sibling savepoints concurrently on one transaction
34
+ * (Promise.all): Firebird's RELEASE SAVEPOINT also releases every
35
+ * savepoint created after it, so interleaved siblings release each
36
+ * other. Nested (awaited) savepoints are fine.
37
+ */
38
+ savepoint<T>(work: (transaction: this) => Promise<T> | T): Promise<T>;
12
39
  /** Per-call options.namedPlaceholders overrides the connection option. */
13
40
  private namedPlaceholdersEnabled;
14
41
  newStatement(query: string, callback: StatementCb, options?: InternalQueryOptions): void;
@@ -6,6 +6,8 @@ const callback_1 = require("../callback");
6
6
  const named_params_1 = require("../named-params");
7
7
  const utils_1 = require("../utils");
8
8
  const const_1 = __importDefault(require("./const"));
9
+ const sql_template_1 = require("../sql-template");
10
+ const xsqlvar_1 = require("./xsqlvar");
9
11
  const query_stream_1 = __importDefault(require("./query-stream"));
10
12
  /***************************************
11
13
  *
@@ -44,9 +46,73 @@ function hookAbortSignal(connection, signal, callback) {
44
46
  }
45
47
  class Transaction {
46
48
  constructor(connection) {
49
+ /** Current savepoint nesting depth (names savepoints, see savepoint()). */
50
+ this._savepointDepth = 0;
47
51
  this.connection = connection;
48
52
  this.db = connection.db;
49
53
  }
54
+ /**
55
+ * Tagged-template query API: tx.sql`SELECT ... ${value}` (see README).
56
+ * Built lazily — transactions are created per-query internally, and
57
+ * those throwaway instances must not pay for the tag. The compiled text
58
+ * is positional-only, so the namedPlaceholders rewriter is disabled:
59
+ * any `:token` in the template is PSQL (EXECUTE BLOCK), not a
60
+ * placeholder.
61
+ */
62
+ get sql() {
63
+ return this._sql || (this._sql = (0, sql_template_1.makeSqlTag)((text, params, options) => this.queryAsync(text, params, { ...options, namedPlaceholders: false })));
64
+ }
65
+ /**
66
+ * Run `work` inside a savepoint (Firebird 1.5+): on resolve the
67
+ * savepoint is released, on reject the transaction rolls back TO the
68
+ * savepoint — undoing only work's changes — and the error is rethrown,
69
+ * leaving the transaction itself usable. Nestable (each call generates
70
+ * a fresh NF_SP_n name), mirroring db.withTransaction's style and
71
+ * Postgres.js's sql.savepoint().
72
+ *
73
+ * Do NOT run sibling savepoints concurrently on one transaction
74
+ * (Promise.all): Firebird's RELEASE SAVEPOINT also releases every
75
+ * savepoint created after it, so interleaved siblings release each
76
+ * other. Nested (awaited) savepoints are fine.
77
+ */
78
+ async savepoint(work) {
79
+ if (typeof work !== 'function') {
80
+ throw new Error('savepoint(work) expects a function');
81
+ }
82
+ // named by nesting depth, not a global counter: sequential
83
+ // savepoints at the same depth reuse the same three SQL strings, so
84
+ // the statement cache serves them instead of accumulating
85
+ // single-use entries (redefining a released savepoint name is legal)
86
+ const name = 'NF_SP_' + (++this._savepointDepth);
87
+ try {
88
+ await this.queryAsync('SAVEPOINT ' + name);
89
+ let result;
90
+ try {
91
+ result = await work(this);
92
+ }
93
+ catch (err) {
94
+ // only a work() failure rolls back to the savepoint — a
95
+ // RELEASE failure below must NOT undo work's successful
96
+ // changes
97
+ try {
98
+ await this.queryAsync('ROLLBACK TO SAVEPOINT ' + name);
99
+ }
100
+ catch (rollbackErr) {
101
+ // the original failure matters more; keep the rollback
102
+ // failure attached for diagnosis
103
+ if (err && typeof err === 'object') {
104
+ err.savepointRollbackError = rollbackErr;
105
+ }
106
+ }
107
+ throw err;
108
+ }
109
+ await this.queryAsync('RELEASE SAVEPOINT ' + name);
110
+ return result;
111
+ }
112
+ finally {
113
+ this._savepointDepth--;
114
+ }
115
+ }
50
116
  /** Per-call options.namedPlaceholders overrides the connection option. */
51
117
  namedPlaceholdersEnabled(options) {
52
118
  if (options && options.namedPlaceholders !== undefined)
@@ -116,6 +182,66 @@ class Transaction {
116
182
  dropError(err);
117
183
  return;
118
184
  }
185
+ // withMeta applies to query/execute only: in streaming mode
186
+ // (sequentially/queryStream, which spread user options) rows
187
+ // bypass fetchAll's array, so a result object here would
188
+ // carry rows: [] and a meaningless affectedRows
189
+ var withMeta = Boolean(options && typeof options === 'object' &&
190
+ options.withMeta && !options.asStream);
191
+ // Deliver the historic result shape, or — when options.withMeta
192
+ // is set — request the per-verb DML row counts while the
193
+ // statement handle is still open and wrap everything in a
194
+ // { rows, fields, affectedRows, recordCounts, warnings } object.
195
+ function deliver(rows, isSelect, plainDml) {
196
+ if (!withMeta) {
197
+ statement.release();
198
+ if (callback) {
199
+ if (plainDml) {
200
+ // plain DML historically calls back with no args
201
+ callback();
202
+ }
203
+ else {
204
+ callback(undefined, rows, statement.output, isSelect);
205
+ }
206
+ }
207
+ return;
208
+ }
209
+ var execWarnings = (ret && ret.warnings) || [];
210
+ var finalize = function (counts) {
211
+ statement.release();
212
+ if (!callback) {
213
+ return;
214
+ }
215
+ // DML: what the server actually changed; SELECT: rows
216
+ // returned (pg's rowCount convention)
217
+ var affectedRows = counts
218
+ ? counts.insertCount + counts.updateCount + counts.deleteCount
219
+ : (Array.isArray(rows) ? rows.length : (rows !== undefined ? 1 : 0));
220
+ callback(undefined, {
221
+ rows: rows,
222
+ fields: (0, xsqlvar_1.describeFields)(statement.output),
223
+ affectedRows: affectedRows,
224
+ recordCounts: counts,
225
+ warnings: execWarnings,
226
+ }, statement.output, isSelect);
227
+ };
228
+ var t = statement.type;
229
+ var isDml = t === const_1.default.isc_info_sql_stmt_insert ||
230
+ t === const_1.default.isc_info_sql_stmt_update ||
231
+ t === const_1.default.isc_info_sql_stmt_delete ||
232
+ t === const_1.default.isc_info_sql_stmt_exec_procedure;
233
+ if (!isDml) {
234
+ finalize();
235
+ return;
236
+ }
237
+ self.connection.statementInfo(statement, const_1.default.RECORDS_INFO, function (err, info) {
238
+ if (err) {
239
+ dropError(err);
240
+ return;
241
+ }
242
+ finalize((0, xsqlvar_1.parseRecordCounts)(info && info.buffer));
243
+ });
244
+ }
119
245
  switch (statement.type) {
120
246
  case const_1.default.isc_info_sql_stmt_select:
121
247
  statement.fetchAll(self, function (err, r) {
@@ -123,35 +249,27 @@ class Transaction {
123
249
  dropError(err);
124
250
  return;
125
251
  }
126
- statement.release();
127
- if (callback)
128
- callback(undefined, r, statement.output, true);
252
+ deliver(r, true);
129
253
  });
130
254
  break;
131
255
  case const_1.default.isc_info_sql_stmt_exec_procedure:
132
256
  if (ret && ret.data && ret.data.length > 0) {
133
- statement.release();
134
- if (callback)
135
- callback(undefined, ret.data[0], statement.output, true);
257
+ deliver(ret.data[0], true);
136
258
  break;
137
259
  }
138
260
  else if (statement.output.length) {
139
- statement.fetch(self, 1, function (err, ret) {
261
+ statement.fetch(self, 1, function (err, fret) {
140
262
  if (err) {
141
263
  dropError(err);
142
264
  return;
143
265
  }
144
- statement.release();
145
- if (callback)
146
- callback(undefined, ret.data[0], statement.output, false);
266
+ deliver(fret.data[0], false);
147
267
  });
148
268
  break;
149
269
  }
150
270
  // Fall through is normal
151
271
  default:
152
- statement.release();
153
- if (callback)
154
- callback();
272
+ deliver(undefined, false, true);
155
273
  break;
156
274
  }
157
275
  }, options);
@@ -1,4 +1,5 @@
1
1
  import type { XdrReader, XdrWriter, BlrWriter } from './serialize';
2
+ import type { RecordCounts } from '../types';
2
3
  /**
3
4
  * Common shape of all SQLVar descriptor objects. The metadata properties
4
5
  * are populated externally (in connection.ts) from the op_prepare_statement
@@ -21,6 +22,105 @@ export declare abstract class SQLVarBase {
21
22
  abstract decode(data: XdrReader, lowerV13: boolean, options?: any): any;
22
23
  abstract calcBlr(blr: BlrWriter): void;
23
24
  }
25
+ /** Effective object-row key(s) of one output column (see computeColumnKeys). */
26
+ export interface ColumnKey {
27
+ /** Top-level table key when nestTables === true; undefined otherwise. */
28
+ table?: string;
29
+ /** Property key: the column alias, or 'table<sep>alias' in separator mode. */
30
+ key: string;
31
+ }
32
+ /**
33
+ * Compute the object-row property keys for a statement's output columns,
34
+ * honouring the nestTables and lowercase_keys options. The table qualifier
35
+ * is the query's relation alias when one is used (relationAlias, requested
36
+ * via isc_info_sql_relation_alias), the relation name otherwise, so
37
+ * self-joins nest under their query aliases. Expression columns (no source
38
+ * relation) qualify as '' exactly like mysql2: they nest under the '' key,
39
+ * and in separator mode become '<sep>alias' — always prefixing keeps
40
+ * qualified keys collision-free (a bare expression alias could otherwise
41
+ * collide with a real column's 'table<sep>column' key). Used by the fetch
42
+ * decoder and by fetchBlobSyncRow, which must agree on where each column
43
+ * landed in the row.
44
+ */
45
+ export declare function computeColumnKeys(output: SQLVarBase[], nestTables: boolean | string | undefined, lowercaseKeys: boolean | undefined, transform?: (key: string) => string): ColumnKey[];
46
+ /** transformKeys option value: the built-in 'camel', or a custom mapper. */
47
+ export type KeyTransform = 'camel' | ((key: string) => string);
48
+ /** FIRST_NAME → firstName (the transformKeys: 'camel' built-in). */
49
+ export declare function camelizeKey(key: string): string;
50
+ /**
51
+ * Resolve the effective transformKeys value (per-query wins over the
52
+ * connection option) into a callable mapper, or undefined when off.
53
+ * A custom mapper is guarded like the typeCast hook: a throw inside the
54
+ * row-decode loop would be mistaken for an incomplete packet and desync
55
+ * the response queue, so failures fall back to the untransformed key.
56
+ */
57
+ export declare function resolveKeyTransform(queryOptions: {
58
+ transformKeys?: KeyTransform;
59
+ } | undefined, connectionOptions: {
60
+ transformKeys?: KeyTransform;
61
+ } | undefined): ((key: string) => string) | undefined;
62
+ /**
63
+ * Resolve the effective nestTables value: the per-query option wins over
64
+ * the connection option. The decoder and fetchBlobSyncRow both use this —
65
+ * they must agree on whether nesting is active or blob cells are looked
66
+ * up in the wrong place.
67
+ */
68
+ export declare function resolveNestTables(queryOptions: {
69
+ nestTables?: boolean | string;
70
+ } | undefined, connectionOptions: {
71
+ nestTables?: boolean | string;
72
+ } | undefined): boolean | string | undefined;
73
+ /**
74
+ * The object a column's value lives in: the row itself, or — when the
75
+ * column carries a nestTables table qualifier — the row's per-table
76
+ * sub-object, created on first use. Every site that reads or writes a
77
+ * cell by ColumnKey must resolve it through here.
78
+ */
79
+ export declare function nestCell(row: any, table: string | undefined): any;
80
+ /** Human-readable names for the SQL_* wire type codes. */
81
+ export declare const SQL_TYPE_NAMES: Record<number, string>;
82
+ /**
83
+ * Public column-metadata shape for one output descriptor: the vocabulary
84
+ * both the typeCast hook and withMeta `fields` deliver. Keep the two in
85
+ * lockstep by building both through here.
86
+ */
87
+ export declare function describeField(meta: Partial<SQLVarBase>): {
88
+ type: number;
89
+ typeName: string;
90
+ subType: number | undefined;
91
+ scale: number | undefined;
92
+ length: number | undefined;
93
+ nullable: boolean | undefined;
94
+ field: string | undefined;
95
+ relation: string | undefined;
96
+ relationAlias: string | undefined;
97
+ relationSchema: string | undefined;
98
+ alias: string | undefined;
99
+ };
100
+ /**
101
+ * Map a statement's output descriptors to the column-metadata array
102
+ * delivered in withMeta results ({ rows, fields, ... }).
103
+ */
104
+ export declare function describeFields(output: SQLVarBase[]): {
105
+ type: number;
106
+ typeName: string;
107
+ subType: number | undefined;
108
+ scale: number | undefined;
109
+ length: number | undefined;
110
+ nullable: boolean | undefined;
111
+ field: string | undefined;
112
+ relation: string | undefined;
113
+ relationAlias: string | undefined;
114
+ relationSchema: string | undefined;
115
+ alias: string | undefined;
116
+ }[];
117
+ /**
118
+ * Parse the op_info_sql response buffer of a Const.RECORDS_INFO request
119
+ * into per-verb row counts. The buffer holds an isc_info_sql_records
120
+ * cluster (2-byte total length, then nested isc_info_req_*_count items,
121
+ * each 2-byte length + little-endian integer) terminated by isc_info_end.
122
+ */
123
+ export declare function parseRecordCounts(buffer: Buffer | undefined): RecordCounts;
24
124
  export declare class SQLVarText extends SQLVarBase {
25
125
  decode(data: XdrReader, lowerV13: boolean, options?: any): string | Buffer<ArrayBufferLike> | null | undefined;
26
126
  calcBlr(blr: BlrWriter): void;