firebird-wasm 0.7.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (99) hide show
  1. package/CHANGELOG.md +266 -0
  2. package/README.md +31 -15
  3. package/dist/.tsbuildinfo.build +1 -1
  4. package/dist/browser/engine-transport.d.ts +2 -0
  5. package/dist/browser/engine-transport.d.ts.map +1 -1
  6. package/dist/browser/engine-transport.js +43 -11
  7. package/dist/browser/engine-transport.js.map +1 -1
  8. package/dist/browser/firebird-browser.d.ts.map +1 -1
  9. package/dist/browser/firebird-browser.js +59 -20
  10. package/dist/browser/firebird-browser.js.map +1 -1
  11. package/dist/browser/index.d.ts +3 -3
  12. package/dist/browser/index.d.ts.map +1 -1
  13. package/dist/browser/index.js +6 -3
  14. package/dist/browser/index.js.map +1 -1
  15. package/dist/browser/params.d.ts +8 -2
  16. package/dist/browser/params.d.ts.map +1 -1
  17. package/dist/browser/params.js +14 -5
  18. package/dist/browser/params.js.map +1 -1
  19. package/dist/browser/serve-engine.d.ts +30 -0
  20. package/dist/browser/serve-engine.d.ts.map +1 -0
  21. package/dist/browser/serve-engine.js +93 -0
  22. package/dist/browser/serve-engine.js.map +1 -0
  23. package/dist/browser/worker-entry.d.ts +8 -22
  24. package/dist/browser/worker-entry.d.ts.map +1 -1
  25. package/dist/browser/worker-entry.js +18 -79
  26. package/dist/browser/worker-entry.js.map +1 -1
  27. package/dist/electric/apply.d.ts +53 -0
  28. package/dist/electric/apply.d.ts.map +1 -0
  29. package/dist/electric/apply.js +88 -0
  30. package/dist/electric/apply.js.map +1 -0
  31. package/dist/electric/index.d.ts +133 -0
  32. package/dist/electric/index.d.ts.map +1 -0
  33. package/dist/electric/index.js +378 -0
  34. package/dist/electric/index.js.map +1 -0
  35. package/dist/electric/serial.d.ts +20 -0
  36. package/dist/electric/serial.d.ts.map +1 -0
  37. package/dist/electric/serial.js +29 -0
  38. package/dist/electric/serial.js.map +1 -0
  39. package/dist/electric/values.d.ts +65 -0
  40. package/dist/electric/values.d.ts.map +1 -0
  41. package/dist/electric/values.js +257 -0
  42. package/dist/electric/values.js.map +1 -0
  43. package/dist/electric/writes.d.ts +99 -0
  44. package/dist/electric/writes.d.ts.map +1 -0
  45. package/dist/electric/writes.js +282 -0
  46. package/dist/electric/writes.js.map +1 -0
  47. package/dist/electric-server/capture.d.ts +51 -0
  48. package/dist/electric-server/capture.d.ts.map +1 -0
  49. package/dist/electric-server/capture.js +224 -0
  50. package/dist/electric-server/capture.js.map +1 -0
  51. package/dist/electric-server/catalog.d.ts +61 -0
  52. package/dist/electric-server/catalog.d.ts.map +1 -0
  53. package/dist/electric-server/catalog.js +137 -0
  54. package/dist/electric-server/catalog.js.map +1 -0
  55. package/dist/electric-server/index.d.ts +85 -0
  56. package/dist/electric-server/index.d.ts.map +1 -0
  57. package/dist/electric-server/index.js +528 -0
  58. package/dist/electric-server/index.js.map +1 -0
  59. package/dist/electric-server/where.d.ts +30 -0
  60. package/dist/electric-server/where.d.ts.map +1 -0
  61. package/dist/electric-server/where.js +243 -0
  62. package/dist/electric-server/where.js.map +1 -0
  63. package/dist/firebird.d.ts +3 -1
  64. package/dist/firebird.d.ts.map +1 -1
  65. package/dist/firebird.js +259 -22
  66. package/dist/firebird.js.map +1 -1
  67. package/dist/frameworks/query-store.d.ts +99 -0
  68. package/dist/frameworks/query-store.d.ts.map +1 -0
  69. package/dist/frameworks/query-store.js +166 -0
  70. package/dist/frameworks/query-store.js.map +1 -0
  71. package/dist/index.d.ts +1 -1
  72. package/dist/index.d.ts.map +1 -1
  73. package/dist/index.js +2 -1
  74. package/dist/index.js.map +1 -1
  75. package/dist/react/index.d.ts +68 -0
  76. package/dist/react/index.d.ts.map +1 -0
  77. package/dist/react/index.js +92 -0
  78. package/dist/react/index.js.map +1 -0
  79. package/dist/repl/index.d.ts +76 -0
  80. package/dist/repl/index.d.ts.map +1 -0
  81. package/dist/repl/index.js +328 -0
  82. package/dist/repl/index.js.map +1 -0
  83. package/dist/types.d.ts +2 -1
  84. package/dist/types.d.ts.map +1 -1
  85. package/dist/version.d.ts +1 -1
  86. package/dist/version.js +1 -1
  87. package/dist/vue/index.d.ts +68 -0
  88. package/dist/vue/index.d.ts.map +1 -0
  89. package/dist/vue/index.js +107 -0
  90. package/dist/vue/index.js.map +1 -0
  91. package/dist/wasm/firebird-embedded-st.js +1 -1
  92. package/dist/wasm/firebird-embedded-st.wasm +0 -0
  93. package/dist/wasm/firebird-embedded.js +1 -1
  94. package/dist/wasm/firebird-embedded.wasm +0 -0
  95. package/dist/wasm-loader.d.ts +57 -7
  96. package/dist/wasm-loader.d.ts.map +1 -1
  97. package/dist/wasm-loader.js +106 -21
  98. package/dist/wasm-loader.js.map +1 -1
  99. package/package.json +50 -3
@@ -0,0 +1,243 @@
1
+ "use strict";
2
+ /**
3
+ * where.ts – a shape's `where`, as Electric's clients write it (Postgres
4
+ * SQL), read and written again as Firebird SQL with every value bound.
5
+ *
6
+ * Only a small, closed part of SQL is taken: column names, literals,
7
+ * Electric's positional parameters (`$1`, given as `params[1]=…`),
8
+ * comparisons, `AND` / `OR` / `NOT`, `IS [NOT] NULL`, `[NOT] IN (…)`,
9
+ * `[NOT] LIKE` / `ILIKE`, `[NOT] BETWEEN … AND …`, and parentheses. Anything
10
+ * else (a function, a subquery, a cast, a column of another table) is
11
+ * refused by name, never passed on: the text comes from a client, and the
12
+ * server runs what it writes. Every literal and parameter is bound, so no
13
+ * text a client sends ever becomes SQL.
14
+ */
15
+ Object.defineProperty(exports, "__esModule", { value: true });
16
+ exports.WhereError = void 0;
17
+ exports.translateWhere = translateWhere;
18
+ /** Thrown for a `where` this server does not take; the server answers 400. */
19
+ class WhereError extends Error {
20
+ }
21
+ exports.WhereError = WhereError;
22
+ function tokenize(text) {
23
+ const tokens = [];
24
+ let i = 0;
25
+ while (i < text.length) {
26
+ const ch = text[i];
27
+ if (/\s/.test(ch)) {
28
+ i++;
29
+ }
30
+ else if (/[A-Za-z_]/.test(ch)) {
31
+ const m = /^[A-Za-z_][A-Za-z0-9_$]*/.exec(text.slice(i));
32
+ tokens.push({ kind: 'word', text: m[0] });
33
+ i += m[0].length;
34
+ }
35
+ else if (/[0-9]/.test(ch) || (ch === '.' && /[0-9]/.test(text[i + 1] ?? ''))) {
36
+ const m = /^(\d+(\.\d*)?|\.\d+)([eE][+-]?\d+)?/.exec(text.slice(i));
37
+ tokens.push({ kind: 'number', text: m[0] });
38
+ i += m[0].length;
39
+ }
40
+ else if (ch === "'" || ch === '"') {
41
+ // Doubled to escape, in both.
42
+ let value = '';
43
+ let j = i + 1;
44
+ for (;;) {
45
+ if (j >= text.length)
46
+ throw new WhereError(`where: unterminated ${ch === "'" ? 'string' : 'name'}`);
47
+ if (text[j] === ch) {
48
+ if (text[j + 1] === ch) {
49
+ value += ch;
50
+ j += 2;
51
+ continue;
52
+ }
53
+ break;
54
+ }
55
+ value += text[j];
56
+ j++;
57
+ }
58
+ tokens.push(ch === "'" ? { kind: 'string', text: value } : { kind: 'quoted', text: value });
59
+ i = j + 1;
60
+ }
61
+ else if (ch === '$') {
62
+ const m = /^\$(\d+)/.exec(text.slice(i));
63
+ if (!m)
64
+ throw new WhereError('where: $ must be followed by a parameter number');
65
+ tokens.push({ kind: 'param', index: Number(m[1]) });
66
+ i += m[0].length;
67
+ }
68
+ else {
69
+ const m = /^(<>|!=|<=|>=|=|<|>|\(|\)|,|-)/.exec(text.slice(i));
70
+ if (!m)
71
+ throw new WhereError(`where: ${JSON.stringify(text.slice(i, i + 10))} is not supported`);
72
+ tokens.push({ kind: 'op', text: m[0] });
73
+ i += m[0].length;
74
+ }
75
+ }
76
+ return tokens;
77
+ }
78
+ const KEYWORDS = new Set(['AND', 'OR', 'NOT', 'IS', 'NULL', 'IN', 'LIKE', 'ILIKE', 'BETWEEN', 'TRUE', 'FALSE']);
79
+ /**
80
+ * Translate `text` into Firebird SQL.
81
+ *
82
+ * `columns` maps the names a client writes (the shape's names: `title` for
83
+ * Firebird's `TITLE`) to the Firebird column, quoted. `params` are the
84
+ * values of `$1`, `$2`…, as text.
85
+ */
86
+ function translateWhere(text, columns, params) {
87
+ const tokens = tokenize(text);
88
+ const bound = [];
89
+ let pos = 0;
90
+ const peek = () => tokens[pos];
91
+ const isWord = (t, word) => t?.kind === 'word' && t.text.toUpperCase() === word;
92
+ const isOp = (t, op) => t?.kind === 'op' && t.text === op;
93
+ const expectOp = (op) => {
94
+ if (!isOp(peek(), op))
95
+ throw new WhereError(`where: expected ${op}`);
96
+ pos++;
97
+ };
98
+ const column = (name, quoted) => {
99
+ // Unquoted, Postgres folds to lower case, and the shape's names are lower
100
+ // case where Firebird's were upper.
101
+ const found = columns.get(quoted ? name : name.toLowerCase());
102
+ if (!found)
103
+ throw new WhereError(`where: ${name} is not a column of this shape's table`);
104
+ return found;
105
+ };
106
+ const bind = (value) => {
107
+ bound.push(value);
108
+ return '?';
109
+ };
110
+ // operand := column | literal | $n | '(' expr ')' | '-' number
111
+ const operand = () => {
112
+ const t = peek();
113
+ if (!t)
114
+ throw new WhereError('where: ends too soon');
115
+ if (isOp(t, '(')) {
116
+ pos++;
117
+ const inner = or();
118
+ expectOp(')');
119
+ return `(${inner})`;
120
+ }
121
+ if (isOp(t, '-')) {
122
+ pos++;
123
+ const n = peek();
124
+ if (n?.kind !== 'number')
125
+ throw new WhereError('where: - must be followed by a number');
126
+ pos++;
127
+ return bind(`-${n.text}`);
128
+ }
129
+ pos++;
130
+ switch (t.kind) {
131
+ case 'number': return bind(t.text);
132
+ case 'string': return bind(t.text);
133
+ case 'param': {
134
+ const value = params.get(t.index);
135
+ if (value === undefined)
136
+ throw new WhereError(`where: $${t.index} has no value (params[${t.index}])`);
137
+ return bind(value);
138
+ }
139
+ case 'quoted': return column(t.text, true);
140
+ case 'word': {
141
+ const upper = t.text.toUpperCase();
142
+ if (upper === 'TRUE' || upper === 'FALSE')
143
+ return upper;
144
+ if (upper === 'NULL')
145
+ return 'NULL';
146
+ if (isOp(peek(), '('))
147
+ throw new WhereError(`where: functions (${t.text}) are not supported`);
148
+ if (KEYWORDS.has(upper))
149
+ throw new WhereError(`where: ${t.text} is out of place`);
150
+ return column(t.text, false);
151
+ }
152
+ default:
153
+ throw new WhereError(`where: ${'text' in t ? t.text : 'this'} is out of place`);
154
+ }
155
+ };
156
+ // predicate := operand [comparison | IS [NOT] NULL | [NOT] IN (…) | [NOT] LIKE … | [NOT] BETWEEN … AND …]
157
+ const predicate = () => {
158
+ const left = operand();
159
+ const t = peek();
160
+ if (t?.kind === 'op' && ['=', '<>', '!=', '<', '<=', '>', '>='].includes(t.text)) {
161
+ pos++;
162
+ return `${left} ${t.text === '!=' ? '<>' : t.text} ${operand()}`;
163
+ }
164
+ if (isWord(t, 'IS')) {
165
+ pos++;
166
+ const not = isWord(peek(), 'NOT');
167
+ if (not)
168
+ pos++;
169
+ if (!isWord(peek(), 'NULL'))
170
+ throw new WhereError('where: IS takes only [NOT] NULL');
171
+ pos++;
172
+ return `${left} IS ${not ? 'NOT ' : ''}NULL`;
173
+ }
174
+ const not = isWord(t, 'NOT') && (isWord(tokens[pos + 1], 'IN') || isWord(tokens[pos + 1], 'LIKE')
175
+ || isWord(tokens[pos + 1], 'ILIKE') || isWord(tokens[pos + 1], 'BETWEEN'));
176
+ if (not)
177
+ pos++;
178
+ const neg = not ? 'NOT ' : '';
179
+ if (isWord(peek(), 'IN')) {
180
+ pos++;
181
+ expectOp('(');
182
+ const items = [operand()];
183
+ while (isOp(peek(), ',')) {
184
+ pos++;
185
+ items.push(operand());
186
+ }
187
+ expectOp(')');
188
+ return `${left} ${neg}IN (${items.join(', ')})`;
189
+ }
190
+ if (isWord(peek(), 'LIKE')) {
191
+ pos++;
192
+ return `${left} ${neg}LIKE ${operand()}`;
193
+ }
194
+ if (isWord(peek(), 'ILIKE')) {
195
+ pos++;
196
+ return `UPPER(${left}) ${neg}LIKE UPPER(${operand()})`;
197
+ }
198
+ if (isWord(peek(), 'BETWEEN')) {
199
+ pos++;
200
+ const low = operand();
201
+ if (!isWord(peek(), 'AND'))
202
+ throw new WhereError('where: BETWEEN needs AND');
203
+ pos++;
204
+ return `${left} ${neg}BETWEEN ${low} AND ${operand()}`;
205
+ }
206
+ if (not)
207
+ throw new WhereError('where: NOT is out of place');
208
+ // A column or value on its own: a boolean.
209
+ return left;
210
+ };
211
+ const notExpr = () => {
212
+ if (isWord(peek(), 'NOT')) {
213
+ pos++;
214
+ return `NOT (${notExpr()})`;
215
+ }
216
+ return predicate();
217
+ };
218
+ const and = () => {
219
+ const parts = [notExpr()];
220
+ while (isWord(peek(), 'AND')) {
221
+ pos++;
222
+ parts.push(notExpr());
223
+ }
224
+ return parts.join(' AND ');
225
+ };
226
+ function or() {
227
+ const parts = [and()];
228
+ while (isWord(peek(), 'OR')) {
229
+ pos++;
230
+ parts.push(and());
231
+ }
232
+ return parts.length === 1 ? parts[0] : parts.map((p) => `(${p})`).join(' OR ');
233
+ }
234
+ if (tokens.length === 0)
235
+ throw new WhereError('where is empty');
236
+ const sql = or();
237
+ if (pos < tokens.length) {
238
+ const t = tokens[pos];
239
+ throw new WhereError(`where: ${'text' in t ? t.text : `$${t.index}`} is out of place`);
240
+ }
241
+ return { sql, params: bound };
242
+ }
243
+ //# sourceMappingURL=where.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"where.js","sourceRoot":"","sources":["../../src/electric-server/where.ts"],"names":[],"mappings":"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"}
@@ -60,7 +60,9 @@ export declare class FirebirdLite {
60
60
  * Execute a (parameterised) SQL statement and return the result rows.
61
61
  *
62
62
  * For SELECT queries the rows are returned as plain objects keyed by the
63
- * (upper-cased) column name, mirroring the PGlite convention.
63
+ * column name as Firebird reports it, mirroring the PGlite convention: an
64
+ * unquoted name in upper case (`SELECT title` gives `TITLE`), a quoted alias
65
+ * as written (`SELECT title AS "title"` gives `title`).
64
66
  *
65
67
  * ```ts
66
68
  * await db.query('SELECT * FROM items WHERE id = ?', [id]);
@@ -1 +1 @@
1
- {"version":3,"file":"firebird.d.ts","sourceRoot":"","sources":["../src/firebird.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"firebird.d.ts","sourceRoot":"","sources":["../src/firebird.ts"],"names":[],"mappings":"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"}
package/dist/firebird.js CHANGED
@@ -144,7 +144,7 @@ class FirebirdLite {
144
144
  let failed = true;
145
145
  try {
146
146
  const columnLabels = await stmt.columnLabels;
147
- fields = columnLabels.map((label) => ({ name: label.toUpperCase() }));
147
+ fields = columnLabels.map((label) => ({ name: label }));
148
148
  rows = shapeRows(await fetchRows(stmt, transaction, statement.params, fields.length), fields, rowMode);
149
149
  failed = false;
150
150
  }
@@ -195,7 +195,7 @@ class FirebirdLite {
195
195
  return undefined;
196
196
  }
197
197
  const fields = (await stmt.columnLabels).map((label) => ({
198
- name: label.toUpperCase(),
198
+ name: label,
199
199
  }));
200
200
  const rawRows = await fetchRows(stmt, transaction, params, fields.length);
201
201
  return { rows: shapeRows(rawRows, fields, rowMode), fields };
@@ -265,7 +265,7 @@ class FirebirdLite {
265
265
  const type = await stmt.type;
266
266
  return {
267
267
  params: undefined,
268
- fields: columnLabels.map((label) => ({ name: label.toUpperCase() })),
268
+ fields: columnLabels.map((label) => ({ name: label })),
269
269
  statementType: (0, statement_types_1.statementKind)(type),
270
270
  hasResultSet: stmt.hasResultSet,
271
271
  };
@@ -383,7 +383,7 @@ class FirebirdTransaction {
383
383
  const lease = await this.statements.acquire(this.transaction, statement.sql);
384
384
  let failed = true;
385
385
  try {
386
- await lease.stmt.execute(this.transaction, statement.params);
386
+ await lease.stmt.execute(this.transaction, await bindParams(lease.stmt, this.transaction, statement.params));
387
387
  failed = false;
388
388
  }
389
389
  finally {
@@ -415,7 +415,7 @@ class FirebirdTransaction {
415
415
  }
416
416
  else {
417
417
  for (const row of rows) {
418
- await lease.stmt.execute(this.transaction, row);
418
+ await lease.stmt.execute(this.transaction, await bindParams(lease.stmt, this.transaction, row));
419
419
  }
420
420
  }
421
421
  failed = false;
@@ -463,7 +463,7 @@ class FirebirdTransaction {
463
463
  for (let at = 0; at < rows.length;) {
464
464
  const chunk = rows.slice(at, at + size);
465
465
  if (chunk.length === 1) {
466
- await single.execute(this.transaction, chunk[0]);
466
+ await single.execute(this.transaction, await bindParams(single, this.transaction, chunk[0]));
467
467
  at += 1;
468
468
  continue;
469
469
  }
@@ -482,11 +482,11 @@ class FirebirdTransaction {
482
482
  }
483
483
  }
484
484
  try {
485
- await current.lease.stmt.execute(this.transaction, chunk.flat());
485
+ await current.lease.stmt.execute(this.transaction, await bindParams(current.lease.stmt, this.transaction, chunk.flat()));
486
486
  }
487
487
  catch {
488
488
  for (const row of chunk) {
489
- await single.execute(this.transaction, row);
489
+ await single.execute(this.transaction, await bindParams(single, this.transaction, row));
490
490
  }
491
491
  }
492
492
  at += chunk.length;
@@ -510,7 +510,7 @@ class FirebirdTransaction {
510
510
  try {
511
511
  const columnLabels = await stmt.columnLabels;
512
512
  const fields = columnLabels.map((label) => ({
513
- name: label.toUpperCase(),
513
+ name: label,
514
514
  }));
515
515
  const rawRows = await fetchRows(stmt, this.transaction, statement.params, fields.length);
516
516
  failed = false;
@@ -528,14 +528,31 @@ exports.FirebirdTransaction = FirebirdTransaction;
528
528
  /** Rows per `EXECUTE BLOCK` in `execBatch`: a single-table statement uses
529
529
  * one of the engine's 256 contexts per row, so 200 leaves room. */
530
530
  const BLOCK_ROWS = 200;
531
+ const bindings = new WeakMap();
532
+ async function bindingOf(stmt) {
533
+ let binding = bindings.get(stmt);
534
+ if (!binding) {
535
+ // Read before anything here rewrites the message; a statement is used
536
+ // by one call at a time, so nothing else reads it meanwhile.
537
+ binding = { params: await readInputParams(stmt), variants: new Map(), current: '' };
538
+ bindings.set(stmt, binding);
539
+ }
540
+ return binding;
541
+ }
531
542
  /**
532
- * The driver's description of a statement's `?` parameters: its input
533
- * metadata, after the driver's own rewrites (integers bound as doubles,
534
- * CHAR as VARCHAR). Not public API: read from the native statement's
535
- * fields, and `undefined` whenever they are not there, so a driver that
536
- * changes shape makes `execBatch` fall back to one execute per row.
543
+ * A statement's `?` parameters: its input metadata as the driver rewrote it
544
+ * (CHAR as VARCHAR, the zoned types in their `_EX` forms), except that an
545
+ * integer or NUMERIC parameter, which the driver binds as a double, is
546
+ * described as prepared: SMALLINT, INTEGER or BIGINT, with its scale. That is
547
+ * what `execBatch` declares a block's parameters as, and what tells a string
548
+ * to send as text. `undefined` whenever the internals are not there, so a
549
+ * driver that changes shape makes `execBatch` fall back to one execute per
550
+ * row and strings go to the driver as they are.
537
551
  */
538
552
  async function inputParams(stmt) {
553
+ return (await bindingOf(stmt)).params;
554
+ }
555
+ async function readInputParams(stmt) {
539
556
  const internal = stmt;
540
557
  const meta = internal.inMetadata;
541
558
  const client = internal.attachment?.client;
@@ -544,23 +561,242 @@ async function inputParams(stmt) {
544
561
  }
545
562
  try {
546
563
  return await client.statusAction(async (status) => {
564
+ const read = (m, i) => ({
565
+ type: m.getTypeSync(status, i),
566
+ subType: m.getSubTypeSync(status, i),
567
+ length: m.getLengthSync(status, i),
568
+ scale: m.getScaleSync(status, i),
569
+ charSet: m.getCharSetSync(status, i),
570
+ });
547
571
  const params = [];
548
- for (let i = 0, n = meta.getCountSync(status); i < n; i++) {
549
- params.push({
550
- type: meta.getTypeSync(status, i),
551
- subType: meta.getSubTypeSync(status, i),
552
- length: meta.getLengthSync(status, i),
553
- scale: meta.getScaleSync(status, i),
554
- charSet: meta.getCharSetSync(status, i),
572
+ for (let i = 0, n = meta.getCountSync(status); i < n; i++)
573
+ params.push(read(meta, i));
574
+ if (!params.some((p) => (p.type & ~1) === SQL_DOUBLE))
575
+ return params;
576
+ // The metadata as prepared, before the driver's rewrite.
577
+ const prepared = await internal.statementHandle?.getInputMetadataAsync?.(status);
578
+ if (!prepared)
579
+ return params;
580
+ try {
581
+ return params.map((p, i) => {
582
+ if ((p.type & ~1) !== SQL_DOUBLE)
583
+ return p;
584
+ const as = read(prepared, i);
585
+ return INTEGER_TYPES.has(as.type & ~1) ? as : p;
555
586
  });
556
587
  }
557
- return params;
588
+ finally {
589
+ prepared.releaseSync();
590
+ }
558
591
  });
559
592
  }
560
593
  catch {
561
594
  return undefined;
562
595
  }
563
596
  }
597
+ /** `SQL_BLOB`, without the nullable bit. */
598
+ const SQL_BLOB = 520;
599
+ /** `SQL_DOUBLE`: what the driver binds every integer and NUMERIC as. */
600
+ const SQL_DOUBLE = 480;
601
+ /** SMALLINT, INTEGER, BIGINT, and NUMERIC / DECIMAL kept in them. */
602
+ const INTEGER_TYPES = new Set([500, 496, 580]);
603
+ /** `SQL_VARYING`. */
604
+ const SQL_VARYING = 448;
605
+ /** Character set id of UTF8. */
606
+ const CS_UTF8 = 4;
607
+ /**
608
+ * DATE, TIME, TIMESTAMP, and TIME / TIMESTAMP WITH TIME ZONE in both forms
609
+ * (the driver rewrites the zoned ones to their `_EX` forms).
610
+ */
611
+ const DATE_TIME_TYPES = new Set([570, 560, 510, 32756, 32754, 32750, 32748]);
612
+ /** The types a string is sent to as text, for Firebird to convert. */
613
+ const TEXT_TYPES = new Set([...DATE_TIME_TYPES, ...INTEGER_TYPES]);
614
+ /** Bytes a value takes as text: a timestamp with the longest zone name fits twice. */
615
+ const TEXT_LENGTH = 128;
616
+ /**
617
+ * The parameters as the driver will take them, the statement made ready to
618
+ * take them.
619
+ *
620
+ * A `Uint8Array` or `ArrayBuffer` goes on as a Buffer, the driver's bytes.
621
+ *
622
+ * A string or Buffer bound to a BLOB parameter is written to a new blob here,
623
+ * and the blob passed on. The driver takes a Buffer or a Blob there and
624
+ * refused a string ("Unrecognized type used as BLOB"); and given a Buffer,
625
+ * it writes the blob while encoding the other parameters, under a
626
+ * `Promise.all` that rejects as soon as one of them fails, with the blob's
627
+ * native calls still running. The transaction is then rolled back under
628
+ * them, and the process died with a segmentation fault: a date given as a
629
+ * string beside a BLOB did it, so did a string too long for its VARCHAR.
630
+ * Written first, the blob is done before the driver encodes anything, and
631
+ * nothing is in flight when a parameter fails.
632
+ *
633
+ * A string bound to a DATE, TIME or TIMESTAMP parameter (with or without a
634
+ * time zone) is sent as text, and Firebird converts it: the driver takes a
635
+ * JS `Date` there (`date.getFullYear is not a function` for a string),
636
+ * which holds milliseconds where Firebird keeps 1/10000 s, and is read in
637
+ * the process's time zone. The browser engine binds every parameter as text,
638
+ * so the same call now means the same there and here, to the last digit.
639
+ * A `Date` still goes the driver's way.
640
+ *
641
+ * So does a string bound to a SMALLINT, INTEGER, BIGINT or NUMERIC parameter:
642
+ * the driver binds those as doubles, and `'9007199254740993'` arrived as
643
+ * 9007199254740992, `'0.1'` in a NUMERIC(18,17) as 0.10000000000000001.
644
+ * A JS number still goes as a double (it is one); a string to a DOUBLE
645
+ * PRECISION or FLOAT parameter is still converted by JS, which rounds
646
+ * correctly.
647
+ */
648
+ async function bindParams(stmt, transaction, given) {
649
+ // Bytes as the driver takes them: a Buffer (the browser engine takes a
650
+ // Uint8Array or an ArrayBuffer, so the same call works on both).
651
+ const params = given.some((p) => p instanceof Uint8Array || p instanceof ArrayBuffer)
652
+ ? given.map((p) => p instanceof ArrayBuffer
653
+ ? Buffer.from(p)
654
+ : p instanceof Uint8Array && !Buffer.isBuffer(p) ? Buffer.from(p.buffer, p.byteOffset, p.byteLength) : p)
655
+ : given;
656
+ const binding = bindings.get(stmt);
657
+ if (!params.some((p) => typeof p === 'string' || Buffer.isBuffer(p)) && (!binding || binding.current === '')) {
658
+ return params;
659
+ }
660
+ const described = (binding ?? (await bindingOf(stmt))).params;
661
+ if (!described)
662
+ return params;
663
+ const text = [];
664
+ const bound = [...params];
665
+ for (let i = 0; i < bound.length; i++) {
666
+ const p = bound[i];
667
+ const type = (described[i]?.type ?? 0) & ~1;
668
+ if (type === SQL_BLOB && (typeof p === 'string' || Buffer.isBuffer(p))) {
669
+ bound[i] = await writeBlob(stmt, transaction, typeof p === 'string' ? Buffer.from(p, 'utf8') : p);
670
+ }
671
+ else if (typeof p === 'string' && TEXT_TYPES.has(type)) {
672
+ text.push(i);
673
+ }
674
+ }
675
+ if (!(await useMessage(stmt, text)))
676
+ return params;
677
+ return bound;
678
+ }
679
+ /** A new blob holding `bytes`, in `transaction`; `bytes` itself where the driver's internals are not there. */
680
+ async function writeBlob(stmt, transaction, bytes) {
681
+ const attachment = stmt.attachment;
682
+ if (typeof attachment?.createBlob !== 'function')
683
+ return bytes;
684
+ const stream = await attachment.createBlob(transaction);
685
+ try {
686
+ await stream.write(bytes);
687
+ }
688
+ catch (err) {
689
+ await stream.cancel().catch(() => undefined);
690
+ throw err;
691
+ }
692
+ await stream.close();
693
+ return stream.blob;
694
+ }
695
+ /**
696
+ * Put in place the input message that takes the parameters at `positions`
697
+ * as VARCHAR text and every other as the driver prepared it, making it the
698
+ * first time. False when that cannot be done: the driver's internals are not
699
+ * what this expects.
700
+ *
701
+ * The message is what the driver encodes into and what Firebird reads from;
702
+ * Firebird converts each value from the message's type to the parameter's,
703
+ * which is how the driver itself binds every integer as a double. A
704
+ * statement is used by one call at a time (the cache leases it), so the
705
+ * message in place is the one this call chose.
706
+ */
707
+ async function useMessage(stmt, positions) {
708
+ const binding = await bindingOf(stmt);
709
+ const key = positions.join(',');
710
+ if (binding.current === key)
711
+ return true;
712
+ const internal = stmt;
713
+ let message = key === '' ? binding.original : binding.variants.get(key);
714
+ if (!message) {
715
+ const made = await makeTextMessage(internal, binding, positions);
716
+ if (!made)
717
+ return positions.length === 0;
718
+ if (!binding.original) {
719
+ binding.original = {
720
+ inMetadata: internal.inMetadata,
721
+ inBuffer: internal.inBuffer,
722
+ dataWriter: internal.dataWriter,
723
+ };
724
+ releaseMessagesOnDispose(internal, binding);
725
+ }
726
+ binding.variants.set(key, made);
727
+ message = made;
728
+ }
729
+ internal.inMetadata = message.inMetadata;
730
+ internal.inBuffer = message.inBuffer;
731
+ internal.dataWriter = message.dataWriter;
732
+ binding.current = key;
733
+ return true;
734
+ }
735
+ let driverUtil;
736
+ async function makeTextMessage(internal, binding, positions) {
737
+ if (driverUtil === undefined) {
738
+ try {
739
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
740
+ driverUtil = require('node-firebird-driver-native/dist/lib/fb-util');
741
+ if (typeof driverUtil?.createDataWriter !== 'function' || typeof driverUtil.createDescriptors !== 'function') {
742
+ driverUtil = null;
743
+ }
744
+ }
745
+ catch {
746
+ driverUtil = null;
747
+ }
748
+ }
749
+ const util = driverUtil;
750
+ // Made from the driver's own message, whichever is in place.
751
+ const base = binding.original?.inMetadata ?? internal.inMetadata;
752
+ const client = internal.attachment?.client;
753
+ if (!util || !base || typeof base.getBuilderSync !== 'function' || typeof client?.statusAction !== 'function') {
754
+ return undefined;
755
+ }
756
+ return client.statusAction(async (status) => {
757
+ const builder = base.getBuilderSync(status);
758
+ let inMetadata;
759
+ try {
760
+ for (const i of positions) {
761
+ builder.setTypeSync(status, i, SQL_VARYING);
762
+ builder.setSubTypeSync(status, i, 0);
763
+ builder.setLengthSync(status, i, TEXT_LENGTH);
764
+ builder.setCharSetSync(status, i, CS_UTF8);
765
+ builder.setScaleSync(status, i, 0);
766
+ }
767
+ inMetadata = builder.getMetadataSync(status);
768
+ }
769
+ finally {
770
+ builder.releaseSync();
771
+ }
772
+ return {
773
+ inMetadata,
774
+ inBuffer: new Uint8Array(inMetadata.getMessageLengthSync(status)),
775
+ dataWriter: util.createDataWriter(util.createDescriptors(status, inMetadata)),
776
+ };
777
+ });
778
+ }
779
+ /**
780
+ * Hand the driver back its own message when the statement is disposed, for
781
+ * it to release as it always does, and release the ones made here.
782
+ */
783
+ function releaseMessagesOnDispose(internal, binding) {
784
+ const dispose = internal.internalDispose;
785
+ if (typeof dispose !== 'function')
786
+ return;
787
+ internal.internalDispose = async function () {
788
+ if (binding.original) {
789
+ internal.inMetadata = binding.original.inMetadata;
790
+ internal.inBuffer = binding.original.inBuffer;
791
+ internal.dataWriter = binding.original.dataWriter;
792
+ }
793
+ for (const message of binding.variants.values())
794
+ message.inMetadata.releaseSync();
795
+ binding.variants.clear();
796
+ binding.current = '';
797
+ return dispose.call(this);
798
+ };
799
+ }
564
800
  /** Character sets by id, read once per connection. */
565
801
  const charsetCache = new WeakMap();
566
802
  async function characterSets(attachment, transaction) {
@@ -685,6 +921,7 @@ class StatementCache {
685
921
  * runs and returns nothing.
686
922
  */
687
923
  async function fetchRows(stmt, transaction, params, columnCount) {
924
+ params = await bindParams(stmt, transaction, params);
688
925
  if (stmt.hasResultSet) {
689
926
  const resultSet = await stmt.executeQuery(transaction, params);
690
927
  // A failing fetch must still close.