vv-db-connection 1.0.1 → 1.0.2

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.
@@ -1,116 +1,36 @@
1
- import Firebird from 'node-firebird';
2
- import { createRequire } from 'module';
3
- // Windows-1251 → Unicode code point map for bytes 0x80–0xFF
4
- const WIN1251 = [
5
- 0x0402, 0x0403, 0x201A, 0x0453, 0x201E, 0x2026, 0x2020, 0x2021, // 0x80-0x87
6
- 0x20AC, 0x2030, 0x0409, 0x2039, 0x040A, 0x040C, 0x040B, 0x040F, // 0x88-0x8F
7
- 0x0452, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, // 0x90-0x97
8
- 0xFFFD, 0x2122, 0x0459, 0x203A, 0x045A, 0x045C, 0x045B, 0x045F, // 0x98-0x9F
9
- 0x00A0, 0x040E, 0x045E, 0x0408, 0x00A4, 0x0490, 0x00A6, 0x00A7, // 0xA0-0xA7
10
- 0x0401, 0x00A9, 0x0404, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x0407, // 0xA8-0xAF
11
- 0x00B0, 0x00B1, 0x0406, 0x0456, 0x0491, 0x00B5, 0x00B6, 0x00B7, // 0xB0-0xB7
12
- 0x0451, 0x2116, 0x0454, 0x00BB, 0x0458, 0x0405, 0x0455, 0x0457, // 0xB8-0xBF
13
- 0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417, // 0xC0-0xC7
14
- 0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E, 0x041F, // 0xC8-0xCF
15
- 0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427, // 0xD0-0xD7
16
- 0x0428, 0x0429, 0x042A, 0x042B, 0x042C, 0x042D, 0x042E, 0x042F, // 0xD8-0xDF
17
- 0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437, // 0xE0-0xE7
18
- 0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, 0x043F, // 0xE8-0xEF
19
- 0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447, // 0xF0-0xF7
20
- 0x0448, 0x0449, 0x044A, 0x044B, 0x044C, 0x044D, 0x044E, 0x044F, // 0xF8-0xFF
21
- ];
22
- function win1251Decode(buf) {
23
- let s = '';
24
- for (let i = 0; i < buf.length; i++) {
25
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
26
- const b = buf[i];
27
- s += String.fromCodePoint(b < 0x80 ? b : WIN1251[b - 0x80]);
28
- }
29
- return s;
30
- }
31
- // Module-level charset state — set once per adapter init, used by XdrReader patch below
32
- let fbDecodeCharset = 'UTF8';
33
- (() => {
34
- const _require = createRequire(import.meta.url);
35
- const FbWireConn = _require('node-firebird/lib/wire/connection');
36
- const FbConst = _require('node-firebird/lib/wire/const');
37
- FbWireConn.prototype.closeBlob = function (blob, callback) {
38
- const msg = this._msg;
39
- msg.pos = 0;
40
- msg.addInt(FbConst.op_close_blob);
41
- msg.addInt(blob.handle);
42
- this._queueEvent(callback); // no defer — op_close_blob response must be consumed
43
- };
44
- const FbSerialize = _require('node-firebird/lib/wire/serialize');
45
- FbSerialize.XdrReader.prototype.readText = function (len, _encoding) {
46
- if (len <= 0)
47
- return '';
48
- const bytes = this.buffer.slice(this.pos, this.pos + len);
49
- this.pos += (len + 3) & ~3;
50
- return fbDecodeCharset === 'WIN1251' ? win1251Decode(bytes) : bytes.toString('utf8');
51
- };
52
- const readSegment = FbSerialize.BlrReader.prototype.readSegment;
53
- FbSerialize.BlrReader.prototype.readSegment = function () {
54
- const segment = readSegment.call(this);
55
- if (fbDecodeCharset !== 'WIN1251' || !Buffer.isBuffer(segment))
56
- return segment;
57
- return Buffer.from(win1251Decode(segment), 'utf8');
58
- };
59
- // fetchAll patch, see the note above the block for why it is here.
60
- FbWireConn.prototype.fetchAll = function (statement, transaction, callback) {
1
+ import * as Firebird from 'node-firebird';
2
+ import Connection from 'node-firebird/lib/wire/connection.js';
3
+ import { mapPlaceholders, SQL_ATOM } from '../placeholders.js';
4
+ const fbAttachProto = Connection.prototype;
5
+ if (!fbAttachProto.attach.__vvPatched) {
6
+ const origAttach = fbAttachProto.attach;
7
+ const patched = function (options, callback, db) {
61
8
  const self = this;
62
- const asStream = Boolean(statement.custom?.asStream && statement.custom?.on);
63
- const all = [];
64
- let index = 0;
65
- const next = () => self.fetch(statement, transaction, FbConst.DEFAULT_FETCHSIZE, loop);
66
- const loop = (err, ret) => {
9
+ return origAttach.call(this, options, function (err, ret) {
67
10
  if (err) {
68
- callback(err);
69
- return;
70
- }
71
- if (!ret?.data?.length) {
72
- if (ret?.fetched)
73
- callback(undefined, all);
74
- else
75
- next();
76
- return;
77
- }
78
- void (async () => {
11
+ // Mark the close as wanted (skips the driver's self-reconnect) and
12
+ // rip the socket so the descriptor is released at once.
79
13
  try {
80
- for (const readBlob of (ret.arrBlob ?? [])) {
81
- const blob = await readBlob(transaction);
82
- ret.data[blob.row][blob.column] = blob.value;
83
- }
84
- for (const row of ret.data) {
85
- if (asStream)
86
- statement.custom.on(row, index++);
87
- else
88
- all.push(row);
89
- }
90
- if (ret.fetched)
91
- callback(undefined, all);
92
- else
93
- next();
14
+ self._isDetach = true;
15
+ clearTimeout(self._retry_connection_id);
16
+ self._socket?.destroy?.();
94
17
  }
95
- catch (e) {
96
- callback(e);
97
- }
98
- })();
99
- };
100
- next();
18
+ catch { /* cleanup must never mask the attach error */ }
19
+ }
20
+ if (callback)
21
+ callback(err, ret);
22
+ }, db);
101
23
  };
102
- })();
103
- /* eslint-enable @typescript-eslint/no-explicit-any */
104
- // A string literal, a quoted identifier, a line or a block comment - the places where
105
- // what looks like a placeholder is just text. Everything else is code.
106
- const FB_LITERAL = /'(?:[^']|'')*'|"(?:[^"]|"")*"|--[^\n]*|\/\*[\s\S]*?\*\//;
24
+ patched.__vvPatched = true;
25
+ fbAttachProto.attach = patched;
26
+ }
107
27
  // Index of the AS that closes the header of an EXECUTE BLOCK, or -1. Both lists the
108
28
  // header may carry - the input parameters and RETURNS - are parenthesized, so the AS
109
29
  // we want is the first one outside any parentheses, and outside literals and comments.
110
30
  function execBlockBodyAt(query) {
111
31
  if (!/^\s*EXECUTE\s+BLOCK\b/i.test(query))
112
32
  return -1;
113
- const scan = new RegExp(`${FB_LITERAL.source}|[()]|\\bAS\\b`, 'gi');
33
+ const scan = new RegExp(`${SQL_ATOM.source}|[()]|\\bAS\\b`, 'gi');
114
34
  let depth = 0;
115
35
  for (let m = scan.exec(query); m !== null; m = scan.exec(query)) {
116
36
  const token = m[0];
@@ -137,29 +57,24 @@ function execBlockBodyAt(query) {
137
57
  function buildQuery(query, params) {
138
58
  const values = [];
139
59
  const bodyAt = execBlockBodyAt(query);
140
- const codeTo = bodyAt < 0 ? query.length : bodyAt;
141
- const scan = new RegExp(`${FB_LITERAL.source}|:(\\w+)`, 'g');
142
- const sql = query.replace(scan, (token, name, at) => {
143
- if (name === undefined || at >= codeTo)
144
- return token;
60
+ const sql = mapPlaceholders(query, (name) => {
145
61
  const found = params.find((p) => p.name === name);
146
62
  if (!found)
147
63
  throw new Error(`fb: param not found: ${name}`);
148
64
  values.push(found.value);
149
65
  return '?';
150
- });
66
+ }, bodyAt < 0 ? undefined : bodyAt);
151
67
  return { sql, values };
152
68
  }
153
- // A lost connection must end the query with an error, and node-firebird does not let it.
154
- // On a closed socket it waits a second, quietly attaches anew and moves the callbacks
155
- // still waiting for an answer onto the new connection - but the requests behind them
156
- // were sent on the old one, so the answer never comes. The read stops without an error
157
- // and the job never ends: a restart of the server, an attachment killed on its side, a
158
- // network failure - it is the same silent hang every time. Here the close is seen right
159
- // after the driver has scheduled that reconnect, the reconnect is cancelled, and the
160
- // query ends with an error. The "error" and "reconnect" events of the database are taken
161
- // as the same loss. The "error" listener also stays for good: the driver emits it from
162
- // the socket handler, and with nobody listening it would be thrown from there.
69
+ // A lost connection must end the query with an error. node-firebird 2 rejects the
70
+ // pending operations on a closed socket (so a running query does end), but then still
71
+ // schedules a self-reconnect a second later - a fresh attach that nobody asked for and
72
+ // nobody tracks, on which the statement handles of the dropped connection are never
73
+ // valid. Here the close is seen right after the driver has scheduled that reconnect,
74
+ // the timer is cancelled, and the query ends with an error. The "error" and "reconnect"
75
+ // events of the database are taken as the same loss. The "error" listener stays for
76
+ // good: the driver emits it from the socket handler, and without a listener the loss is
77
+ // silently swallowed instead of surfacing.
163
78
  /* eslint-disable @typescript-eslint/no-explicit-any */
164
79
  function watchConnection(db, onLost) {
165
80
  // The typings leave out that Database is an EventEmitter.
@@ -192,7 +107,6 @@ export class FbAdapter {
192
107
  p;
193
108
  constructor(p) {
194
109
  this.p = p;
195
- fbDecodeCharset = p.charset ?? 'UTF8';
196
110
  }
197
111
  kind() { return 'fb'; }
198
112
  title() { return `${this.p.host}:${this.p.port || 'DEFAULT'} [${this.p.database}]`; }
@@ -297,13 +211,7 @@ export class FbAdapter {
297
211
  const exec = (query, params) => {
298
212
  const { sql: sqlStr, values } = buildQuery(query, params);
299
213
  return new Promise((resolve, reject) => {
300
- // node-firebird may throw inside its native code before calling our
301
- // callback (e.g. when processing BLOB params). Catch that here so
302
- // the promise always settles and the try/catch above can rollback.
303
- const onUncaught = (err) => reject(err);
304
- process.once('uncaughtException', onUncaught);
305
214
  transaction.query(sqlStr, values, (err, rows) => {
306
- process.removeListener('uncaughtException', onUncaught);
307
215
  if (err)
308
216
  reject(err);
309
217
  else
@@ -1 +1 @@
1
- {"version":3,"file":"fb.js","sourceRoot":"","sources":["../../../../src/db/adapters/fb.ts"],"names":[],"mappings":"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","sourcesContent":["import Firebird from 'node-firebird'\nimport { createRequire } from 'module'\nimport type { IDbAdapter, TDbCol, TDbParam, TDbStreamEvent, TDbTxCb, TDbTxResult } from '../types'\nimport type { TConnFb } from '../connection'\nimport type { TResult } from '../../tresult'\n\n// Windows-1251 → Unicode code point map for bytes 0x80–0xFF\nconst WIN1251: readonly number[] = [\n\t0x0402, 0x0403, 0x201A, 0x0453, 0x201E, 0x2026, 0x2020, 0x2021, // 0x80-0x87\n\t0x20AC, 0x2030, 0x0409, 0x2039, 0x040A, 0x040C, 0x040B, 0x040F, // 0x88-0x8F\n\t0x0452, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, // 0x90-0x97\n\t0xFFFD, 0x2122, 0x0459, 0x203A, 0x045A, 0x045C, 0x045B, 0x045F, // 0x98-0x9F\n\t0x00A0, 0x040E, 0x045E, 0x0408, 0x00A4, 0x0490, 0x00A6, 0x00A7, // 0xA0-0xA7\n\t0x0401, 0x00A9, 0x0404, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x0407, // 0xA8-0xAF\n\t0x00B0, 0x00B1, 0x0406, 0x0456, 0x0491, 0x00B5, 0x00B6, 0x00B7, // 0xB0-0xB7\n\t0x0451, 0x2116, 0x0454, 0x00BB, 0x0458, 0x0405, 0x0455, 0x0457, // 0xB8-0xBF\n\t0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417, // 0xC0-0xC7\n\t0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E, 0x041F, // 0xC8-0xCF\n\t0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427, // 0xD0-0xD7\n\t0x0428, 0x0429, 0x042A, 0x042B, 0x042C, 0x042D, 0x042E, 0x042F, // 0xD8-0xDF\n\t0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437, // 0xE0-0xE7\n\t0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, 0x043F, // 0xE8-0xEF\n\t0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447, // 0xF0-0xF7\n\t0x0448, 0x0449, 0x044A, 0x044B, 0x044C, 0x044D, 0x044E, 0x044F, // 0xF8-0xFF\n]\n\nfunction win1251Decode(buf: Buffer): string {\n\tlet s = ''\n\tfor (let i = 0; i < buf.length; i++) {\n\t\t// eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n\t\tconst b = buf[i]!\n\t\ts += String.fromCodePoint(b < 0x80 ? b : WIN1251[b - 0x80]!)\n\t}\n\treturn s\n}\n\n// Module-level charset state — set once per adapter init, used by XdrReader patch below\nlet fbDecodeCharset: 'UTF8' | 'WIN1251' = 'UTF8'\n\n// node-firebird bug with Firebird 6 (protocol >= V11): Connection.prototype.closeBlob\n// uses _queueEvent(callback, defer=true), which calls the callback immediately without\n// pushing to the response queue. The server still sends op_response for op_close_blob,\n// leaving an unconsumed response in the XDR stream. This shifts all subsequent response\n// reads by one: allocateAndPrepareStatement's lazy_count=2 consumes the close_blob\n// response + allocate response instead of allocate + prepare, so ret.buffer is undefined\n// and describe() crashes with \"Cannot read properties of undefined (reading 'length')\".\n// Fix: override closeBlob to queue the response so it is consumed before the next op.\n//\n// XdrReader.readText patch: node-firebird hardcodes DEFAULT_ENCODING='UTF8' for all text\n// columns, ignoring the column charset. Firebird databases with NONE-charset columns send\n// raw bytes (e.g. WIN1251). We intercept readText to decode with the correct charset\n// instead of blindly calling buffer.toString('utf8'), which produces U+FFFD for WIN1251.\n//\n// Connection.fetchAll patch: with blobAsText the driver reads every text blob of a\n// fetch batch (200 rows) at once - Promise.all over arrBlob - so up to 200 sequences of\n// open_blob / get_segment... / close_blob are interleaved on one connection. The driver\n// is not built for that: one FIFO of response callbacks and one shared write buffer for\n// all of them. Responses start going missing, the fetch loop is never called again, and\n// the read hangs for good with no error at all - the export just stops after the first\n// batches and the job never ends. Here the blobs of a batch are read one after another,\n// which is not only sound but far faster (3000 rows with a 16 KB blob: hangs vs 3.7 s).\n// Rows are also no longer accumulated in a growing array in stream mode - execStream\n// throws that array away, and on a large export it is a copy of the whole data set,\n// blobs included, held in memory next to the file being written.\n//\n// BlrReader.readSegment patch: text blobs never reach readText - node-firebird reads them\n// segment by segment and decodes every segment with the same hardcoded UTF8. Here we\n// cannot change the decoding, so we re-encode WIN1251 bytes into UTF-8 and let that\n// hardcoded decode produce the right text. readSegment is used for blobs only; on a\n// WIN1251 connection a binary blob would be re-encoded too, but those are not read - they\n// come as reader functions and are dropped before they reach the data files.\n/* eslint-disable @typescript-eslint/no-explicit-any */\n;((): void => {\n\tconst _require = createRequire(import.meta.url)\n\tconst FbWireConn: any = _require('node-firebird/lib/wire/connection')\n\tconst FbConst: any = _require('node-firebird/lib/wire/const')\n\tFbWireConn.prototype.closeBlob = function (blob: any, callback: any): void {\n\t\tconst msg: any = this._msg\n\t\tmsg.pos = 0\n\t\tmsg.addInt(FbConst.op_close_blob)\n\t\tmsg.addInt(blob.handle)\n\t\tthis._queueEvent(callback) // no defer — op_close_blob response must be consumed\n\t}\n\n\tconst FbSerialize: any = _require('node-firebird/lib/wire/serialize')\n\tFbSerialize.XdrReader.prototype.readText = function (len: number, _encoding: string): string {\n\t\tif (len <= 0) return ''\n\t\tconst bytes: Buffer = this.buffer.slice(this.pos, this.pos + len)\n\t\tthis.pos += (len + 3) & ~3\n\t\treturn fbDecodeCharset === 'WIN1251' ? win1251Decode(bytes) : bytes.toString('utf8')\n\t}\n\n\tconst readSegment = FbSerialize.BlrReader.prototype.readSegment\n\tFbSerialize.BlrReader.prototype.readSegment = function (): Buffer {\n\t\tconst segment: Buffer = readSegment.call(this)\n\t\tif (fbDecodeCharset !== 'WIN1251' || !Buffer.isBuffer(segment)) return segment\n\t\treturn Buffer.from(win1251Decode(segment), 'utf8')\n\t}\n\n\t// fetchAll patch, see the note above the block for why it is here.\n\tFbWireConn.prototype.fetchAll = function (statement: any, transaction: any, callback: any): void {\n\t\tconst self = this\n\t\tconst asStream = Boolean(statement.custom?.asStream && statement.custom?.on)\n\t\tconst all: any[] = []\n\t\tlet index = 0\n\n\t\tconst next = (): void => self.fetch(statement, transaction, FbConst.DEFAULT_FETCHSIZE, loop)\n\n\t\tconst loop = (err: any, ret: any): void => {\n\t\t\tif (err) { callback(err); return }\n\t\t\tif (!ret?.data?.length) {\n\t\t\t\tif (ret?.fetched) callback(undefined, all)\n\t\t\t\telse next()\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tvoid (async (): Promise<void> => {\n\t\t\t\ttry {\n\t\t\t\t\tfor (const readBlob of (ret.arrBlob ?? [])) {\n\t\t\t\t\t\tconst blob = await readBlob(transaction)\n\t\t\t\t\t\tret.data[blob.row][blob.column] = blob.value\n\t\t\t\t\t}\n\t\t\t\t\tfor (const row of ret.data) {\n\t\t\t\t\t\tif (asStream) statement.custom.on(row, index++)\n\t\t\t\t\t\telse all.push(row)\n\t\t\t\t\t}\n\t\t\t\t\tif (ret.fetched) callback(undefined, all)\n\t\t\t\t\telse next()\n\t\t\t\t} catch (e) {\n\t\t\t\t\tcallback(e)\n\t\t\t\t}\n\t\t\t})()\n\t\t}\n\n\t\tnext()\n\t}\n})()\n/* eslint-enable @typescript-eslint/no-explicit-any */\n\n// A string literal, a quoted identifier, a line or a block comment - the places where\n// what looks like a placeholder is just text. Everything else is code.\nconst FB_LITERAL = /'(?:[^']|'')*'|\"(?:[^\"]|\"\")*\"|--[^\\n]*|\\/\\*[\\s\\S]*?\\*\\//\n\n// Index of the AS that closes the header of an EXECUTE BLOCK, or -1. Both lists the\n// header may carry - the input parameters and RETURNS - are parenthesized, so the AS\n// we want is the first one outside any parentheses, and outside literals and comments.\nfunction execBlockBodyAt(query: string): number {\n\tif (!/^\\s*EXECUTE\\s+BLOCK\\b/i.test(query)) return -1\n\tconst scan = new RegExp(`${FB_LITERAL.source}|[()]|\\\\bAS\\\\b`, 'gi')\n\tlet depth = 0\n\tfor (let m = scan.exec(query); m !== null; m = scan.exec(query)) {\n\t\tconst token = m[0]\n\t\tif (token === '(') { depth++; continue }\n\t\tif (token === ')') { depth--; continue }\n\t\tif (depth === 0 && /^as$/i.test(token)) return m.index\n\t}\n\treturn -1\n}\n\n// Converts :name placeholders to ? (positional) and collects values in order.\n// For EXECUTE BLOCK statements only the header is processed - past the AS begins PSQL,\n// where :name is a reference to a local variable or to a parameter of the block itself\n// and must be left alone. That AS is not always followed by BEGIN: declarations of local\n// variables sit between the two, and a header that ends with \"as declare variable x\"\n// used to be missed, which turned every variable reference in the body into a parameter\n// nobody had passed and failed the query with \"param not found\".\nfunction buildQuery(query: string, params: TDbParam[]): { sql: string; values: unknown[] } {\n\tconst values: unknown[] = []\n\tconst bodyAt = execBlockBodyAt(query)\n\tconst codeTo = bodyAt < 0 ? query.length : bodyAt\n\tconst scan = new RegExp(`${FB_LITERAL.source}|:(\\\\w+)`, 'g')\n\n\tconst sql = query.replace(scan, (token: string, name: string | undefined, at: number) => {\n\t\tif (name === undefined || at >= codeTo) return token\n\t\tconst found = params.find((p) => p.name === name)\n\t\tif (!found) throw new Error(`fb: param not found: ${name}`)\n\t\tvalues.push(found.value)\n\t\treturn '?'\n\t})\n\treturn { sql, values }\n}\n\n// A lost connection must end the query with an error, and node-firebird does not let it.\n// On a closed socket it waits a second, quietly attaches anew and moves the callbacks\n// still waiting for an answer onto the new connection - but the requests behind them\n// were sent on the old one, so the answer never comes. The read stops without an error\n// and the job never ends: a restart of the server, an attachment killed on its side, a\n// network failure - it is the same silent hang every time. Here the close is seen right\n// after the driver has scheduled that reconnect, the reconnect is cancelled, and the\n// query ends with an error. The \"error\" and \"reconnect\" events of the database are taken\n// as the same loss. The \"error\" listener also stays for good: the driver emits it from\n// the socket handler, and with nobody listening it would be thrown from there.\n/* eslint-disable @typescript-eslint/no-explicit-any */\nfunction watchConnection(db: Firebird.Database, onLost: (error: string) => void): void {\n\t// The typings leave out that Database is an EventEmitter.\n\tconst events: any = db\n\tconst conn: any = events.connection\n\tevents.on('error', (err: unknown) => onLost(`fb: connection lost: ${String(err)}`))\n\tevents.on('reconnect', () => onLost('fb: connection lost, the driver reattached on its own'))\n\tconn?._socket?.on('close', () => {\n\t\t// Set by our own detach at the end of the query - a close that was asked for.\n\t\tif (conn._isDetach) return\n\t\tclearTimeout(conn._retry_connection_id)\n\t\tonLost('fb: connection lost')\n\t})\n}\n\n// Drops the connection at once, without a detach the server would have to answer.\nfunction dropConnection(db: Firebird.Database): void {\n\tconst conn: any = (db as any).connection\n\tif (!conn) return\n\tconn._isDetach = true\n\tclearTimeout(conn._retry_connection_id)\n\ttry { conn._socket?.destroy() } catch { }\n}\n/* eslint-enable @typescript-eslint/no-explicit-any */\n\nexport class FbAdapter implements IDbAdapter {\n\tconstructor(private readonly p: TConnFb) {\n\t\tfbDecodeCharset = p.charset ?? 'UTF8'\n\t}\n\n\tkind(): 'fb' { return 'fb' }\n\ttitle(): string { return `${this.p.host}:${this.p.port || 'DEFAULT'} [${this.p.database}]` }\n\tuser(): string { return this.p.user }\n\tversionQuery(): string { return \"SELECT rdb$get_context('SYSTEM', 'ENGINE_VERSION') AS ver FROM rdb$database\" }\n\tpingQuery(): string { return 'SELECT 1 AS p FROM rdb$database' }\n\n\tasync execStream(query: string, params: TDbParam[], callback: (event: TDbStreamEvent) => void, signal?: AbortSignal): Promise<void> {\n\t\tconst { sql, values } = buildQuery(query, params)\n\t\tconst options: Firebird.Options = {\n\t\t\thost: this.p.host,\n\t\t\tport: this.p.port ?? 3050,\n\t\t\tdatabase: this.p.database,\n\t\t\tuser: this.p.user,\n\t\t\tpassword: this.p.password,\n\t\t\tencoding: (this.p.charset ?? 'UTF8') as Firebird.SupportedCharacterSet,\n\t\t\t// Without it a text blob arrives as an async reader function instead of a\n\t\t\t// value, and JSON.stringify silently drops such a field from the row.\n\t\t\tblobAsText: true,\n\t\t}\n\t\treturn new Promise<void>((resolve) => {\n\t\t\tlet done = false\n\t\t\tlet db: Firebird.Database | undefined\n\t\t\tconst finish = (error?: string): void => {\n\t\t\t\tif (done) return\n\t\t\t\tdone = true\n\t\t\t\tsignal?.removeEventListener('abort', onAbort)\n\t\t\t\tcallback({ kind: 'finish', error })\n\t\t\t\tresolve()\n\t\t\t}\n\t\t\tconst onAbort = (): void => {\n\t\t\t\tif (db) dropConnection(db)\n\t\t\t\tfinish(String(signal?.reason ?? 'aborted'))\n\t\t\t}\n\t\t\tif (signal?.aborted) { finish(String(signal.reason ?? 'aborted')); return }\n\t\t\tsignal?.addEventListener('abort', onAbort)\n\n\t\t\tFirebird.attach(options, (attachErr, attached) => {\n\t\t\t\tif (attachErr) { finish(String(attachErr)); return }\n\t\t\t\t// Given up while the attach was on its way - nobody is waiting for this one.\n\t\t\t\tif (done) { dropConnection(attached); return }\n\t\t\t\tdb = attached\n\t\t\t\twatchConnection(db, (error) => {\n\t\t\t\t\tdropConnection(attached)\n\t\t\t\t\tfinish(error)\n\t\t\t\t})\n\t\t\t\tlet schemaEmitted = false\n\t\t\t\tdb.sequentially(\n\t\t\t\t\tsql,\n\t\t\t\t\tvalues,\n\t\t\t\t\t(row: Record<string, unknown>, _index: number) => {\n\t\t\t\t\t\tif (done) return\n\t\t\t\t\t\tif (!schemaEmitted) {\n\t\t\t\t\t\t\tschemaEmitted = true\n\t\t\t\t\t\t\tconst schema: TDbCol[] = Object.keys(row).map((name) => ({ name, kind: 'unknown' }))\n\t\t\t\t\t\t\tcallback({ kind: 'schema', schema })\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcallback({ kind: 'row', row })\n\t\t\t\t\t},\n\t\t\t\t\t(seqErr?: Error) => {\n\t\t\t\t\t\tif (done) return\n\t\t\t\t\t\tattached.detach()\n\t\t\t\t\t\tfinish(seqErr ? String(seqErr) : undefined)\n\t\t\t\t\t},\n\t\t\t\t)\n\t\t\t})\n\t\t})\n\t}\n\n\tasync execTxDynamic(cb: TDbTxCb, timeoutMs?: number): Promise<TResult<void>> {\n\t\tconst options: Firebird.Options = {\n\t\t\thost: this.p.host,\n\t\t\tport: this.p.port ?? 3050,\n\t\t\tdatabase: this.p.database,\n\t\t\tuser: this.p.user,\n\t\t\tpassword: this.p.password,\n\t\t\tencoding: (this.p.charset ?? 'UTF8') as Firebird.SupportedCharacterSet,\n\t\t\t// Without it a text blob arrives as an async reader function instead of a\n\t\t\t// value, and JSON.stringify silently drops such a field from the row.\n\t\t\tblobAsText: true,\n\t\t}\n\t\tconst db = await new Promise<Firebird.Database>((resolve, reject) => {\n\t\t\tFirebird.attach(options, (err, d) => { if (err) reject(err); else resolve(d) })\n\t\t})\n\t\tconst txWork = async (): Promise<TResult<void>> => {\n\t\t\ttry {\n\t\t\t\tconst transaction = await new Promise<Firebird.Transaction>((resolve, reject) => {\n\t\t\t\t\tdb.transaction(Firebird.ISOLATION_READ_COMMITTED, (err, tx) => { if (err) reject(err); else resolve(tx) })\n\t\t\t\t})\n\t\t\t\ttry {\n\t\t\t\t\tconst exec = (query: string, params: TDbParam[]): Promise<TDbTxResult> => {\n\t\t\t\t\t\tconst { sql: sqlStr, values } = buildQuery(query, params)\n\t\t\t\t\t\treturn new Promise<TDbTxResult>((resolve, reject) => {\n\t\t\t\t\t\t\t// node-firebird may throw inside its native code before calling our\n\t\t\t\t\t\t\t// callback (e.g. when processing BLOB params). Catch that here so\n\t\t\t\t\t\t\t// the promise always settles and the try/catch above can rollback.\n\t\t\t\t\t\t\tconst onUncaught = (err: Error) => reject(err)\n\t\t\t\t\t\t\tprocess.once('uncaughtException', onUncaught)\n\t\t\t\t\t\t\ttransaction.query(sqlStr, values, (err: Error | null, rows: Record<string, unknown>[]) => {\n\t\t\t\t\t\t\t\tprocess.removeListener('uncaughtException', onUncaught)\n\t\t\t\t\t\t\t\tif (err) reject(err)\n\t\t\t\t\t\t\t\telse resolve({ rows: rows ?? [] })\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t})\n\t\t\t\t\t}\n\t\t\t\t\tawait cb(exec)\n\t\t\t\t\tawait new Promise<void>((resolve, reject) => {\n\t\t\t\t\t\ttransaction.commit((err: Error | null) => { if (err) reject(err); else resolve() })\n\t\t\t\t\t})\n\t\t\t\t\tdb.detach()\n\t\t\t\t\treturn { ok: true, result: undefined }\n\t\t\t\t} catch (err) {\n\t\t\t\t\ttry { await new Promise<void>((resolve) => { transaction.rollback(() => resolve()) }) } catch {}\n\t\t\t\t\tdb.detach()\n\t\t\t\t\treturn { ok: false, error: String(err) }\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\ttry { db.detach() } catch {}\n\t\t\t\treturn { ok: false, error: String(err) }\n\t\t\t}\n\t\t}\n\t\tif (!timeoutMs) return txWork()\n\t\tlet timer: ReturnType<typeof setTimeout> | undefined\n\t\tconst timeoutPromise = new Promise<TResult<void>>((resolve) => {\n\t\t\ttimer = setTimeout(() => {\n\t\t\t\ttry { db.detach() } catch {}\n\t\t\t\tresolve({ ok: false, error: 'TIMEOUT' })\n\t\t\t}, timeoutMs)\n\t\t})\n\t\ttry {\n\t\t\treturn await Promise.race([txWork(), timeoutPromise])\n\t\t} finally {\n\t\t\tclearTimeout(timer)\n\t\t}\n\t}\n}\n"]}
1
+ {"version":3,"file":"fb.js","sourceRoot":"","sources":["../../../../src/db/adapters/fb.ts"],"names":[],"mappings":"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","sourcesContent":["import * as Firebird from 'node-firebird'\nimport Connection from 'node-firebird/lib/wire/connection'\nimport type { IDbAdapter, TDbCol, TDbParam, TDbStreamEvent, TDbTxCb, TDbTxResult } from '../types'\nimport type { TConnFb } from '../connection'\nimport type { TResult } from '../../tresult'\nimport { mapPlaceholders, SQL_ATOM } from '../placeholders'\n\n// ---- monkey-patch: node-firebird@2 leaks the TCP socket on a failed attach ----\n// Connection.attach() answers an attach error with doError(err, cb) and never\n// disconnects, and the Firebird server does not close the connection after a\n// failed op_attach either - so a failed attach (bad credentials, wrong path, DB\n// down) leaves a live socket behind. A long-running process then leaks file\n// descriptors up to EMFILE, and short-lived scripts/tests never exit. The driver\n// does not hand the connection out on the error path, so this is patched at the\n// prototype, with the same teardown dropConnection() uses. The driver version is\n// pinned in package.json; see .hermes.md and re-check this patch on upgrade.\ntype FbConnInternal = {\n\t_isDetach?: boolean\n\t_retry_connection_id?: ReturnType<typeof setTimeout>\n\t_socket?: { destroy?: () => void } | null\n}\ntype FbAttachFn = (\n\tthis: FbConnInternal,\n\toptions: unknown,\n\tcallback?: (err: unknown, ret: unknown) => void,\n\tdb?: unknown,\n) => unknown\n\nconst fbAttachProto = Connection.prototype as unknown as { attach: FbAttachFn & { __vvPatched?: true } }\nif (!fbAttachProto.attach.__vvPatched) {\n\tconst origAttach = fbAttachProto.attach\n\tconst patched = function (this: FbConnInternal, options: unknown, callback?: (err: unknown, ret: unknown) => void, db?: unknown): unknown {\n\t\tconst self = this\n\t\treturn origAttach.call(this, options, function (err: unknown, ret: unknown): void {\n\t\t\tif (err) {\n\t\t\t\t// Mark the close as wanted (skips the driver's self-reconnect) and\n\t\t\t\t// rip the socket so the descriptor is released at once.\n\t\t\t\ttry {\n\t\t\t\t\tself._isDetach = true\n\t\t\t\t\tclearTimeout(self._retry_connection_id)\n\t\t\t\t\tself._socket?.destroy?.()\n\t\t\t\t} catch { /* cleanup must never mask the attach error */ }\n\t\t\t}\n\t\t\tif (callback) callback(err, ret)\n\t\t}, db)\n\t} as FbAttachFn & { __vvPatched?: true }\n\tpatched.__vvPatched = true\n\tfbAttachProto.attach = patched\n}\n\n\n// Index of the AS that closes the header of an EXECUTE BLOCK, or -1. Both lists the\n// header may carry - the input parameters and RETURNS - are parenthesized, so the AS\n// we want is the first one outside any parentheses, and outside literals and comments.\nfunction execBlockBodyAt(query: string): number {\n\tif (!/^\\s*EXECUTE\\s+BLOCK\\b/i.test(query)) return -1\n\tconst scan = new RegExp(`${SQL_ATOM.source}|[()]|\\\\bAS\\\\b`, 'gi')\n\tlet depth = 0\n\tfor (let m = scan.exec(query); m !== null; m = scan.exec(query)) {\n\t\tconst token = m[0]\n\t\tif (token === '(') { depth++; continue }\n\t\tif (token === ')') { depth--; continue }\n\t\tif (depth === 0 && /^as$/i.test(token)) return m.index\n\t}\n\treturn -1\n}\n\n// Converts :name placeholders to ? (positional) and collects values in order.\n// For EXECUTE BLOCK statements only the header is processed - past the AS begins PSQL,\n// where :name is a reference to a local variable or to a parameter of the block itself\n// and must be left alone. That AS is not always followed by BEGIN: declarations of local\n// variables sit between the two, and a header that ends with \"as declare variable x\"\n// used to be missed, which turned every variable reference in the body into a parameter\n// nobody had passed and failed the query with \"param not found\".\nfunction buildQuery(query: string, params: TDbParam[]): { sql: string; values: unknown[] } {\n\tconst values: unknown[] = []\n\tconst bodyAt = execBlockBodyAt(query)\n\tconst sql = mapPlaceholders(query, (name) => {\n\t\tconst found = params.find((p) => p.name === name)\n\t\tif (!found) throw new Error(`fb: param not found: ${name}`)\n\t\tvalues.push(found.value)\n\t\treturn '?'\n\t}, bodyAt < 0 ? undefined : bodyAt)\n\treturn { sql, values }\n}\n\n// A lost connection must end the query with an error. node-firebird 2 rejects the\n// pending operations on a closed socket (so a running query does end), but then still\n// schedules a self-reconnect a second later - a fresh attach that nobody asked for and\n// nobody tracks, on which the statement handles of the dropped connection are never\n// valid. Here the close is seen right after the driver has scheduled that reconnect,\n// the timer is cancelled, and the query ends with an error. The \"error\" and \"reconnect\"\n// events of the database are taken as the same loss. The \"error\" listener stays for\n// good: the driver emits it from the socket handler, and without a listener the loss is\n// silently swallowed instead of surfacing.\n/* eslint-disable @typescript-eslint/no-explicit-any */\nfunction watchConnection(db: Firebird.Database, onLost: (error: string) => void): void {\n\t// The typings leave out that Database is an EventEmitter.\n\tconst events: any = db\n\tconst conn: any = events.connection\n\tevents.on('error', (err: unknown) => onLost(`fb: connection lost: ${String(err)}`))\n\tevents.on('reconnect', () => onLost('fb: connection lost, the driver reattached on its own'))\n\tconn?._socket?.on('close', () => {\n\t\t// Set by our own detach at the end of the query - a close that was asked for.\n\t\tif (conn._isDetach) return\n\t\tclearTimeout(conn._retry_connection_id)\n\t\tonLost('fb: connection lost')\n\t})\n}\n\n// Drops the connection at once, without a detach the server would have to answer.\nfunction dropConnection(db: Firebird.Database): void {\n\tconst conn: any = (db as any).connection\n\tif (!conn) return\n\tconn._isDetach = true\n\tclearTimeout(conn._retry_connection_id)\n\ttry { conn._socket?.destroy() } catch { }\n}\n/* eslint-enable @typescript-eslint/no-explicit-any */\n\nexport class FbAdapter implements IDbAdapter {\n\tconstructor(private readonly p: TConnFb) {}\n\n\tkind(): 'fb' { return 'fb' }\n\ttitle(): string { return `${this.p.host}:${this.p.port || 'DEFAULT'} [${this.p.database}]` }\n\tuser(): string { return this.p.user }\n\tversionQuery(): string { return \"SELECT rdb$get_context('SYSTEM', 'ENGINE_VERSION') AS ver FROM rdb$database\" }\n\tpingQuery(): string { return 'SELECT 1 AS p FROM rdb$database' }\n\n\tasync execStream(query: string, params: TDbParam[], callback: (event: TDbStreamEvent) => void, signal?: AbortSignal): Promise<void> {\n\t\tconst { sql, values } = buildQuery(query, params)\n\t\tconst options: Firebird.Options = {\n\t\t\thost: this.p.host,\n\t\t\tport: this.p.port ?? 3050,\n\t\t\tdatabase: this.p.database,\n\t\t\tuser: this.p.user,\n\t\t\tpassword: this.p.password,\n\t\t\tencoding: (this.p.charset ?? 'UTF8') as Firebird.SupportedCharacterSet,\n\t\t\t// Without it a text blob arrives as an async reader function instead of a\n\t\t\t// value, and JSON.stringify silently drops such a field from the row.\n\t\t\tblobAsText: true,\n\t\t}\n\t\treturn new Promise<void>((resolve) => {\n\t\t\tlet done = false\n\t\t\tlet db: Firebird.Database | undefined\n\t\t\tconst finish = (error?: string): void => {\n\t\t\t\tif (done) return\n\t\t\t\tdone = true\n\t\t\t\tsignal?.removeEventListener('abort', onAbort)\n\t\t\t\tcallback({ kind: 'finish', error })\n\t\t\t\tresolve()\n\t\t\t}\n\t\t\tconst onAbort = (): void => {\n\t\t\t\tif (db) dropConnection(db)\n\t\t\t\tfinish(String(signal?.reason ?? 'aborted'))\n\t\t\t}\n\t\t\tif (signal?.aborted) { finish(String(signal.reason ?? 'aborted')); return }\n\t\t\tsignal?.addEventListener('abort', onAbort)\n\n\t\t\tFirebird.attach(options, (attachErr, attached) => {\n\t\t\t\tif (attachErr) { finish(String(attachErr)); return }\n\t\t\t\t// Given up while the attach was on its way - nobody is waiting for this one.\n\t\t\t\tif (done) { dropConnection(attached); return }\n\t\t\t\tdb = attached\n\t\t\t\twatchConnection(db, (error) => {\n\t\t\t\t\tdropConnection(attached)\n\t\t\t\t\tfinish(error)\n\t\t\t\t})\n\t\t\t\tlet schemaEmitted = false\n\t\t\t\tdb.sequentially(\n\t\t\t\t\tsql,\n\t\t\t\t\tvalues,\n\t\t\t\t\t(row: Record<string, unknown>, _index: number) => {\n\t\t\t\t\t\tif (done) return\n\t\t\t\t\t\tif (!schemaEmitted) {\n\t\t\t\t\t\t\tschemaEmitted = true\n\t\t\t\t\t\t\tconst schema: TDbCol[] = Object.keys(row).map((name) => ({ name, kind: 'unknown' }))\n\t\t\t\t\t\t\tcallback({ kind: 'schema', schema })\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcallback({ kind: 'row', row })\n\t\t\t\t\t},\n\t\t\t\t\t(seqErr?: Error) => {\n\t\t\t\t\t\tif (done) return\n\t\t\t\t\t\tattached.detach()\n\t\t\t\t\t\tfinish(seqErr ? String(seqErr) : undefined)\n\t\t\t\t\t},\n\t\t\t\t)\n\t\t\t})\n\t\t})\n\t}\n\n\tasync execTxDynamic(cb: TDbTxCb, timeoutMs?: number): Promise<TResult<void>> {\n\t\tconst options: Firebird.Options = {\n\t\t\thost: this.p.host,\n\t\t\tport: this.p.port ?? 3050,\n\t\t\tdatabase: this.p.database,\n\t\t\tuser: this.p.user,\n\t\t\tpassword: this.p.password,\n\t\t\tencoding: (this.p.charset ?? 'UTF8') as Firebird.SupportedCharacterSet,\n\t\t\t// Without it a text blob arrives as an async reader function instead of a\n\t\t\t// value, and JSON.stringify silently drops such a field from the row.\n\t\t\tblobAsText: true,\n\t\t}\n\t\tconst db = await new Promise<Firebird.Database>((resolve, reject) => {\n\t\t\tFirebird.attach(options, (err, d) => { if (err) reject(err); else resolve(d) })\n\t\t})\n\t\tconst txWork = async (): Promise<TResult<void>> => {\n\t\t\ttry {\n\t\t\t\tconst transaction = await new Promise<Firebird.Transaction>((resolve, reject) => {\n\t\t\t\t\tdb.transaction(Firebird.ISOLATION_READ_COMMITTED, (err, tx) => { if (err) reject(err); else resolve(tx) })\n\t\t\t\t})\n\t\t\t\ttry {\n\t\t\t\t\tconst exec = (query: string, params: TDbParam[]): Promise<TDbTxResult> => {\n\t\t\t\t\t\tconst { sql: sqlStr, values } = buildQuery(query, params)\n\t\t\t\t\t\treturn new Promise<TDbTxResult>((resolve, reject) => {\n\t\t\t\t\t\t\ttransaction.query(sqlStr, values, (err: Error | null, rows: Record<string, unknown>[]) => {\n\t\t\t\t\t\t\t\tif (err) reject(err)\n\t\t\t\t\t\t\t\telse resolve({ rows: rows ?? [] })\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t})\n\t\t\t\t\t}\n\t\t\t\t\tawait cb(exec)\n\t\t\t\t\tawait new Promise<void>((resolve, reject) => {\n\t\t\t\t\t\ttransaction.commit((err: Error | null) => { if (err) reject(err); else resolve() })\n\t\t\t\t\t})\n\t\t\t\t\tdb.detach()\n\t\t\t\t\treturn { ok: true, result: undefined }\n\t\t\t\t} catch (err) {\n\t\t\t\t\ttry { await new Promise<void>((resolve) => { transaction.rollback(() => resolve()) }) } catch {}\n\t\t\t\t\tdb.detach()\n\t\t\t\t\treturn { ok: false, error: String(err) }\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\ttry { db.detach() } catch {}\n\t\t\t\treturn { ok: false, error: String(err) }\n\t\t\t}\n\t\t}\n\t\tif (!timeoutMs) return txWork()\n\t\tlet timer: ReturnType<typeof setTimeout> | undefined\n\t\tconst timeoutPromise = new Promise<TResult<void>>((resolve) => {\n\t\t\ttimer = setTimeout(() => {\n\t\t\t\ttry { db.detach() } catch {}\n\t\t\t\tresolve({ ok: false, error: 'TIMEOUT' })\n\t\t\t}, timeoutMs)\n\t\t})\n\t\ttry {\n\t\t\treturn await Promise.race([txWork(), timeoutPromise])\n\t\t} finally {\n\t\t\tclearTimeout(timer)\n\t\t}\n\t}\n}\n"]}
@@ -1 +1,2 @@
1
- export declare function mapPlaceholders(query: string, fn: (name: string) => string): string;
1
+ export declare const SQL_ATOM: RegExp;
2
+ export declare function mapPlaceholders(query: string, fn: (name: string) => string, to?: number): string;
@@ -1,16 +1,20 @@
1
1
  // A string literal, a quoted identifier, a line comment or a block comment - the places
2
2
  // where what looks like a placeholder is just text. Everything else is code. Both escapes
3
3
  // are the SQL-standard doubled quote ('' inside a string, "" inside a quoted identifier).
4
- const SQL_ATOM = /'(?:[^']|'')*'|"(?:[^"]|"")*"|--[^\n]*|\/\*[\s\S]*?\*\//;
4
+ export const SQL_ATOM = /'(?:[^']|'')*'|"(?:[^"]|"")*"|--[^\n]*|\/\*[\s\S]*?\*\//;
5
5
  // Calls fn for every :name placeholder that is real code - not inside a string literal, a
6
6
  // quoted identifier or a comment - and replaces it with whatever fn returns. A colon that
7
7
  // follows another colon is a cast/scope operator (::), never a placeholder. Everything
8
- // else is left byte for byte as it was.
9
- export function mapPlaceholders(query, fn) {
8
+ // else is left byte for byte as it was. `to` optionally bounds the scan: a placeholder at
9
+ // or past that offset is left alone (EXECUTE BLOCK passes the index of its AS - the PSQL
10
+ // body after it must not be rewritten).
11
+ export function mapPlaceholders(query, fn, to) {
10
12
  const scan = new RegExp(`${SQL_ATOM.source}|(?<!:):(\\w+)`, 'g');
11
- return query.replace(scan, (token, name) => {
13
+ return query.replace(scan, (token, name, offset) => {
12
14
  if (name === undefined)
13
15
  return token;
16
+ if (to !== undefined && offset >= to)
17
+ return token;
14
18
  return fn(name);
15
19
  });
16
20
  }
@@ -1 +1 @@
1
- {"version":3,"file":"placeholders.js","sourceRoot":"","sources":["../../../src/db/placeholders.ts"],"names":[],"mappings":"AAAA,wFAAwF;AACxF,0FAA0F;AAC1F,0FAA0F;AAC1F,MAAM,QAAQ,GAAG,yDAAyD,CAAA;AAE1E,0FAA0F;AAC1F,0FAA0F;AAC1F,uFAAuF;AACvF,wCAAwC;AACxC,MAAM,UAAU,eAAe,CAAC,KAAa,EAAE,EAA4B;IAC1E,MAAM,IAAI,GAAG,IAAI,MAAM,CAAC,GAAG,QAAQ,CAAC,MAAM,gBAAgB,EAAE,GAAG,CAAC,CAAA;IAChE,OAAO,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,KAAa,EAAE,IAAwB,EAAE,EAAE;QACtE,IAAI,IAAI,KAAK,SAAS;YAAE,OAAO,KAAK,CAAA;QACpC,OAAO,EAAE,CAAC,IAAI,CAAC,CAAA;IAChB,CAAC,CAAC,CAAA;AACH,CAAC","sourcesContent":["// A string literal, a quoted identifier, a line comment or a block comment - the places\n// where what looks like a placeholder is just text. Everything else is code. Both escapes\n// are the SQL-standard doubled quote ('' inside a string, \"\" inside a quoted identifier).\nconst SQL_ATOM = /'(?:[^']|'')*'|\"(?:[^\"]|\"\")*\"|--[^\\n]*|\\/\\*[\\s\\S]*?\\*\\//\n\n// Calls fn for every :name placeholder that is real code - not inside a string literal, a\n// quoted identifier or a comment - and replaces it with whatever fn returns. A colon that\n// follows another colon is a cast/scope operator (::), never a placeholder. Everything\n// else is left byte for byte as it was.\nexport function mapPlaceholders(query: string, fn: (name: string) => string): string {\n\tconst scan = new RegExp(`${SQL_ATOM.source}|(?<!:):(\\\\w+)`, 'g')\n\treturn query.replace(scan, (token: string, name: string | undefined) => {\n\t\tif (name === undefined) return token\n\t\treturn fn(name)\n\t})\n}\n"]}
1
+ {"version":3,"file":"placeholders.js","sourceRoot":"","sources":["../../../src/db/placeholders.ts"],"names":[],"mappings":"AAAA,wFAAwF;AACxF,0FAA0F;AAC1F,0FAA0F;AAC1F,MAAM,CAAC,MAAM,QAAQ,GAAG,yDAAyD,CAAA;AAEjF,0FAA0F;AAC1F,0FAA0F;AAC1F,uFAAuF;AACvF,0FAA0F;AAC1F,yFAAyF;AACzF,wCAAwC;AACxC,MAAM,UAAU,eAAe,CAAC,KAAa,EAAE,EAA4B,EAAE,EAAW;IACvF,MAAM,IAAI,GAAG,IAAI,MAAM,CAAC,GAAG,QAAQ,CAAC,MAAM,gBAAgB,EAAE,GAAG,CAAC,CAAA;IAChE,OAAO,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,KAAa,EAAE,IAAwB,EAAE,MAAc,EAAE,EAAE;QACtF,IAAI,IAAI,KAAK,SAAS;YAAE,OAAO,KAAK,CAAA;QACpC,IAAI,EAAE,KAAK,SAAS,IAAI,MAAM,IAAI,EAAE;YAAE,OAAO,KAAK,CAAA;QAClD,OAAO,EAAE,CAAC,IAAI,CAAC,CAAA;IAChB,CAAC,CAAC,CAAA;AACH,CAAC","sourcesContent":["// A string literal, a quoted identifier, a line comment or a block comment - the places\n// where what looks like a placeholder is just text. Everything else is code. Both escapes\n// are the SQL-standard doubled quote ('' inside a string, \"\" inside a quoted identifier).\nexport const SQL_ATOM = /'(?:[^']|'')*'|\"(?:[^\"]|\"\")*\"|--[^\\n]*|\\/\\*[\\s\\S]*?\\*\\//\n\n// Calls fn for every :name placeholder that is real code - not inside a string literal, a\n// quoted identifier or a comment - and replaces it with whatever fn returns. A colon that\n// follows another colon is a cast/scope operator (::), never a placeholder. Everything\n// else is left byte for byte as it was. `to` optionally bounds the scan: a placeholder at\n// or past that offset is left alone (EXECUTE BLOCK passes the index of its AS - the PSQL\n// body after it must not be rewritten).\nexport function mapPlaceholders(query: string, fn: (name: string) => string, to?: number): string {\n\tconst scan = new RegExp(`${SQL_ATOM.source}|(?<!:):(\\\\w+)`, 'g')\n\treturn query.replace(scan, (token: string, name: string | undefined, offset: number) => {\n\t\tif (name === undefined) return token\n\t\tif (to !== undefined && offset >= to) return token\n\t\treturn fn(name)\n\t})\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vv-db-connection",
3
- "version": "1.0.1",
3
+ "version": "1.0.2",
4
4
  "description": "The wrapper for connecting to various database servers",
5
5
  "keywords": [
6
6
  "MSSQL",
@@ -38,7 +38,7 @@
38
38
  },
39
39
  "dependencies": {
40
40
  "mssql": "^12.5.0",
41
- "node-firebird": "^1.1.5",
41
+ "node-firebird": "2.19.1",
42
42
  "oracledb": "^26.0.0",
43
43
  "pg": "^8.20.0"
44
44
  },