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
|
|
3
|
-
|
|
4
|
-
const
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
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
|
-
|
|
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
|
-
|
|
69
|
-
|
|
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
|
-
|
|
81
|
-
|
|
82
|
-
|
|
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
|
|
96
|
-
|
|
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
|
-
|
|
104
|
-
|
|
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(`${
|
|
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
|
|
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
|
|
154
|
-
//
|
|
155
|
-
//
|
|
156
|
-
//
|
|
157
|
-
//
|
|
158
|
-
//
|
|
159
|
-
//
|
|
160
|
-
//
|
|
161
|
-
//
|
|
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
|
|
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
|
-
|
|
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;
|
|
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.
|
|
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": "
|
|
41
|
+
"node-firebird": "2.19.1",
|
|
42
42
|
"oracledb": "^26.0.0",
|
|
43
43
|
"pg": "^8.20.0"
|
|
44
44
|
},
|