node-firebird 2.14.2 → 2.14.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -10
- package/lib/wire/codepages.d.ts +4 -2
- package/lib/wire/codepages.js +52 -16
- package/lib/wire/connection.js +6 -2
- package/lib/wire/xsqlvar.js +3 -3
- package/package.json +1 -1
- package/src/wire/codepages.ts +56 -16
- package/src/wire/connection.ts +5 -1
- package/src/wire/xsqlvar.ts +3 -3
package/README.md
CHANGED
|
@@ -1558,14 +1558,15 @@ fb.attach(_connection, function (err, svc) {
|
|
|
1558
1558
|
|
|
1559
1559
|
Node-Firebird defaults to `UTF-8` for database connections, but fully supports custom client character sets. You can set the connection encoding by specifying `options.encoding` (e.g. `'UTF8'`, `'WIN1252'`, `'ISO8859_1'`, `'LATIN1'`, `'ASCII'`, or `'NONE'`).
|
|
1560
1560
|
|
|
1561
|
-
Commonly used Firebird character sets are
|
|
1561
|
+
Commonly used Firebird character sets are handled through the corresponding Node.js encoding or ICU codec:
|
|
1562
1562
|
|
|
1563
|
-
| Firebird Character Set | Node.js
|
|
1564
|
-
| ---------------------- |
|
|
1565
|
-
| `UTF8`, `UNICODE_FSS` | `utf8`
|
|
1566
|
-
| `WIN1252
|
|
1567
|
-
| `
|
|
1568
|
-
| `
|
|
1563
|
+
| Firebird Character Set | Node.js encoding / ICU codec | Description / Notes |
|
|
1564
|
+
| ---------------------- | ---------------------------- | ------------------- |
|
|
1565
|
+
| `UTF8`, `UNICODE_FSS` | `utf8` | Unicode. Handles character-level truncation automatically based on charset width. |
|
|
1566
|
+
| `WIN1252` | ICU `windows-1252` codec | Windows Western European encoding, including the printable characters in bytes `0x80`–`0x9F`. |
|
|
1567
|
+
| `ISO8859_1`, `LATIN1` | `latin1` | ISO-8859-1-compatible byte mapping; intentionally distinct from Windows-1252. |
|
|
1568
|
+
| `ASCII` | `ascii` | 7-bit ASCII. |
|
|
1569
|
+
| `NONE` | `latin1` | Raw/unspecified character set. Treated as binary-safe 8-bit characters. |
|
|
1569
1570
|
|
|
1570
1571
|
Beyond Node's native encodings, the driver ships **codepage codecs** for the
|
|
1571
1572
|
single-byte charsets (decode *and* encode — columns, parameters, SQL
|
|
@@ -1581,7 +1582,9 @@ await db.queryAsync('INSERT INTO T VALUES (?)', ['Привет']); // encoded as
|
|
|
1581
1582
|
```
|
|
1582
1583
|
|
|
1583
1584
|
The codecs are built from Node's ICU tables at first use (present in every
|
|
1584
|
-
official Node build).
|
|
1585
|
+
official Node build). If a constrained runtime does not provide a requested
|
|
1586
|
+
codec, the driver throws a descriptive error instead of silently falling back
|
|
1587
|
+
to UTF-8 and corrupting text. `attachOrCreate`/`create` honour `options.encoding`
|
|
1585
1588
|
for the new database's default charset too. Accented characters and
|
|
1586
1589
|
fixed-length `CHAR(N)` whitespace/truncation are handled automatically per
|
|
1587
1590
|
the charset width — and single-byte columns (including charset `NONE`) are
|
|
@@ -1597,7 +1600,7 @@ var options = {
|
|
|
1597
1600
|
database: 'win1252_db.fdb',
|
|
1598
1601
|
user: 'SYSDBA',
|
|
1599
1602
|
password: 'masterkey',
|
|
1600
|
-
encoding: 'WIN1252' //
|
|
1603
|
+
encoding: 'WIN1252' // Uses the WHATWG/ICU Windows-1252 codec
|
|
1601
1604
|
};
|
|
1602
1605
|
|
|
1603
1606
|
Firebird.attach(options, function (err, db) {
|
|
@@ -2296,7 +2299,7 @@ options.blobReadChunkSize = 65535;
|
|
|
2296
2299
|
|
|
2297
2300
|
If your server and client are on the same host, this won't matter much — the slowdown is latency-bound, not throughput-bound.
|
|
2298
2301
|
|
|
2299
|
-
#### How do I use an encoding other than UTF-8 (e.g. WIN1252
|
|
2302
|
+
#### How do I use an encoding other than UTF-8 (e.g. WIN1252 or Latin1)?
|
|
2300
2303
|
|
|
2301
2304
|
Set `options.encoding` — no source changes required (see [Character Set & Encoding Support](#character-set--encoding-support) for the full mapping table):
|
|
2302
2305
|
|
package/lib/wire/codepages.d.ts
CHANGED
|
@@ -17,7 +17,9 @@ export interface TextCodec {
|
|
|
17
17
|
}
|
|
18
18
|
export declare function charsetWidthById(id: number | undefined): number;
|
|
19
19
|
/**
|
|
20
|
-
* Codec for a Firebird charset name, or null when the charset is unknown
|
|
21
|
-
* natively handled by Buffer
|
|
20
|
+
* Codec for a Firebird charset name, or null when the charset is unknown or
|
|
21
|
+
* natively handled by Buffer. A known codepage whose ICU table is unavailable
|
|
22
|
+
* throws instead of silently falling back to UTF-8. Successful and unknown
|
|
23
|
+
* lookups are cached; failures are not.
|
|
22
24
|
*/
|
|
23
25
|
export declare function getCodec(charsetName: string | undefined): TextCodec | null;
|
package/lib/wire/codepages.js
CHANGED
|
@@ -17,6 +17,7 @@ exports.getCodec = getCodec;
|
|
|
17
17
|
const ICU_LABELS = Object.freeze({
|
|
18
18
|
WIN1250: 'windows-1250',
|
|
19
19
|
WIN1251: 'windows-1251',
|
|
20
|
+
WIN1252: 'windows-1252',
|
|
20
21
|
WIN1253: 'windows-1253',
|
|
21
22
|
WIN1254: 'windows-1254',
|
|
22
23
|
WIN1255: 'windows-1255',
|
|
@@ -60,30 +61,63 @@ function charsetWidthById(id) {
|
|
|
60
61
|
return CHARSET_WIDTH_BY_ID[id] || 1;
|
|
61
62
|
}
|
|
62
63
|
const cache = new Map();
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
64
|
+
/**
|
|
65
|
+
* WHATWG windows-1252 code points for bytes 0x80–0x9F (the only range
|
|
66
|
+
* where it differs from Latin-1). Node's TextDecoder cannot be trusted
|
|
67
|
+
* here: through at least Node 20 the 'windows-1252' label is routed
|
|
68
|
+
* through a latin1 fast path, decoding this range as C1 controls, so
|
|
69
|
+
* the WIN1252 table is built from this fixed spec table instead of the
|
|
70
|
+
* runtime decoder. Later Node majors agree with this table exactly.
|
|
71
|
+
*/
|
|
72
|
+
const WIN1252_C1 = Object.freeze([
|
|
73
|
+
0x20AC, 0x0081, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021,
|
|
74
|
+
0x02C6, 0x2030, 0x0160, 0x2039, 0x0152, 0x008D, 0x017D, 0x008F,
|
|
75
|
+
0x0090, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014,
|
|
76
|
+
0x02DC, 0x2122, 0x0161, 0x203A, 0x0153, 0x009D, 0x017E, 0x0178,
|
|
77
|
+
]);
|
|
78
|
+
function buildByteToCodeTable(name, label) {
|
|
79
|
+
const toCode = new Uint16Array(256);
|
|
80
|
+
if (name === 'WIN1252') {
|
|
81
|
+
// Latin-1 identity outside 0x80–0x9F; spec table inside. Needs no
|
|
82
|
+
// ICU support at all, so WIN1252 works even on small-icu builds.
|
|
83
|
+
for (let i = 0; i < 256; i++) {
|
|
84
|
+
toCode[i] = (i & 0xE0) === 0x80 ? WIN1252_C1[i - 0x80] : i;
|
|
85
|
+
}
|
|
86
|
+
return toCode;
|
|
67
87
|
}
|
|
68
88
|
let decoder;
|
|
69
89
|
try {
|
|
70
90
|
decoder = new TextDecoder(label);
|
|
71
91
|
}
|
|
72
92
|
catch {
|
|
73
|
-
//
|
|
74
|
-
|
|
93
|
+
// Falling through to DEFAULT_ENCODING (UTF-8) would silently write
|
|
94
|
+
// different bytes from the explicitly requested Firebird codepage.
|
|
95
|
+
// Official Node builds include these ICU tables; constrained builds
|
|
96
|
+
// must fail clearly instead of corrupting text.
|
|
97
|
+
throw new Error(`The requested Firebird encoding ${name} requires the ${label} ICU codec, ` +
|
|
98
|
+
'but this Node.js runtime does not provide it. Use an official full-ICU ' +
|
|
99
|
+
'Node.js build, or connect with encoding NONE and pass explicitly encoded Buffer values.');
|
|
75
100
|
}
|
|
76
|
-
// Build both directions from the decoder, one byte at a time — every
|
|
77
|
-
// byte of a single-byte codepage maps to exactly one BMP character
|
|
78
|
-
// (undefined bytes decode to U+FFFD, which is kept for decoding but
|
|
79
|
-
// never used for the reverse map).
|
|
80
|
-
const toCode = new Uint16Array(256);
|
|
81
|
-
const toByte = new Map();
|
|
82
101
|
const one = Buffer.alloc(1);
|
|
83
102
|
for (let i = 0; i < 256; i++) {
|
|
84
103
|
one[0] = i;
|
|
85
|
-
|
|
86
|
-
|
|
104
|
+
toCode[i] = decoder.decode(one).charCodeAt(0);
|
|
105
|
+
}
|
|
106
|
+
return toCode;
|
|
107
|
+
}
|
|
108
|
+
function buildCodec(name) {
|
|
109
|
+
const label = ICU_LABELS[name];
|
|
110
|
+
if (!label) {
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
// Build both directions from one byte→code table — every byte of a
|
|
114
|
+
// single-byte codepage maps to exactly one BMP character (undefined
|
|
115
|
+
// bytes decode to U+FFFD, which is kept for decoding but never used
|
|
116
|
+
// for the reverse map).
|
|
117
|
+
const toCode = buildByteToCodeTable(name, label);
|
|
118
|
+
const toByte = new Map();
|
|
119
|
+
for (let i = 0; i < 256; i++) {
|
|
120
|
+
const ch = String.fromCharCode(toCode[i]);
|
|
87
121
|
if (ch !== '�' && !toByte.has(ch)) {
|
|
88
122
|
toByte.set(ch, i);
|
|
89
123
|
}
|
|
@@ -120,8 +154,10 @@ function buildCodec(name) {
|
|
|
120
154
|
};
|
|
121
155
|
}
|
|
122
156
|
/**
|
|
123
|
-
* Codec for a Firebird charset name, or null when the charset is unknown
|
|
124
|
-
* natively handled by Buffer
|
|
157
|
+
* Codec for a Firebird charset name, or null when the charset is unknown or
|
|
158
|
+
* natively handled by Buffer. A known codepage whose ICU table is unavailable
|
|
159
|
+
* throws instead of silently falling back to UTF-8. Successful and unknown
|
|
160
|
+
* lookups are cached; failures are not.
|
|
125
161
|
*/
|
|
126
162
|
function getCodec(charsetName) {
|
|
127
163
|
if (!charsetName) {
|
package/lib/wire/connection.js
CHANGED
|
@@ -1422,9 +1422,13 @@ class Connection {
|
|
|
1422
1422
|
}
|
|
1423
1423
|
var self = this;
|
|
1424
1424
|
var op = const_1.default.op_execute;
|
|
1425
|
-
if (
|
|
1426
|
-
statement.type === const_1.default.isc_info_sql_stmt_exec_procedure &&
|
|
1425
|
+
if (statement.type === const_1.default.isc_info_sql_stmt_exec_procedure &&
|
|
1427
1426
|
statement.output.length) {
|
|
1427
|
+
// op_execute2 returns output parameters via op_sql_response for all
|
|
1428
|
+
// supported protocol versions (including V10/V11/V12 / Firebird 1.5–2.5).
|
|
1429
|
+
// Using op_execute for procedures with outputs on legacy protocols caused
|
|
1430
|
+
// the fallback fetch path to return 0 rows and deliver undefined to the
|
|
1431
|
+
// caller (issue #424).
|
|
1428
1432
|
op = const_1.default.op_execute2;
|
|
1429
1433
|
}
|
|
1430
1434
|
function PrepareParams(params, input, callback) {
|
package/lib/wire/xsqlvar.js
CHANGED
|
@@ -39,13 +39,13 @@ const EMPTY_BUFFER = Buffer.alloc(0);
|
|
|
39
39
|
* We must decode raw bytes with the matching Node.js encoding so that
|
|
40
40
|
* characters outside ASCII are reproduced correctly.
|
|
41
41
|
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
42
|
+
* Other recognized single-byte character sets are handled by the ICU-backed
|
|
43
|
+
* codec path. Only unknown character-set names fall back to the
|
|
44
|
+
* connection-level DEFAULT_ENCODING, typically UTF-8.
|
|
44
45
|
*/
|
|
45
46
|
const FirebirdToNodeEncoding = Object.freeze({
|
|
46
47
|
UTF8: 'utf8',
|
|
47
48
|
UNICODE_FSS: 'utf8',
|
|
48
|
-
WIN1252: 'latin1',
|
|
49
49
|
ISO8859_1: 'latin1',
|
|
50
50
|
LATIN1: 'latin1',
|
|
51
51
|
ASCII: 'ascii',
|
package/package.json
CHANGED
package/src/wire/codepages.ts
CHANGED
|
@@ -21,6 +21,7 @@ export interface TextCodec {
|
|
|
21
21
|
const ICU_LABELS: Readonly<Record<string, string>> = Object.freeze({
|
|
22
22
|
WIN1250: 'windows-1250',
|
|
23
23
|
WIN1251: 'windows-1251',
|
|
24
|
+
WIN1252: 'windows-1252',
|
|
24
25
|
WIN1253: 'windows-1253',
|
|
25
26
|
WIN1254: 'windows-1254',
|
|
26
27
|
WIN1255: 'windows-1255',
|
|
@@ -68,30 +69,67 @@ export function charsetWidthById(id: number | undefined): number {
|
|
|
68
69
|
|
|
69
70
|
const cache = new Map<string, TextCodec | null>();
|
|
70
71
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
72
|
+
/**
|
|
73
|
+
* WHATWG windows-1252 code points for bytes 0x80–0x9F (the only range
|
|
74
|
+
* where it differs from Latin-1). Node's TextDecoder cannot be trusted
|
|
75
|
+
* here: through at least Node 20 the 'windows-1252' label is routed
|
|
76
|
+
* through a latin1 fast path, decoding this range as C1 controls, so
|
|
77
|
+
* the WIN1252 table is built from this fixed spec table instead of the
|
|
78
|
+
* runtime decoder. Later Node majors agree with this table exactly.
|
|
79
|
+
*/
|
|
80
|
+
const WIN1252_C1 = Object.freeze([
|
|
81
|
+
0x20AC, 0x0081, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021,
|
|
82
|
+
0x02C6, 0x2030, 0x0160, 0x2039, 0x0152, 0x008D, 0x017D, 0x008F,
|
|
83
|
+
0x0090, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014,
|
|
84
|
+
0x02DC, 0x2122, 0x0161, 0x203A, 0x0153, 0x009D, 0x017E, 0x0178,
|
|
85
|
+
]);
|
|
86
|
+
|
|
87
|
+
function buildByteToCodeTable(name: string, label: string): Uint16Array {
|
|
88
|
+
const toCode = new Uint16Array(256);
|
|
89
|
+
if (name === 'WIN1252') {
|
|
90
|
+
// Latin-1 identity outside 0x80–0x9F; spec table inside. Needs no
|
|
91
|
+
// ICU support at all, so WIN1252 works even on small-icu builds.
|
|
92
|
+
for (let i = 0; i < 256; i++) {
|
|
93
|
+
toCode[i] = (i & 0xE0) === 0x80 ? WIN1252_C1[i - 0x80] : i;
|
|
94
|
+
}
|
|
95
|
+
return toCode;
|
|
75
96
|
}
|
|
76
97
|
let decoder: TextDecoder;
|
|
77
98
|
try {
|
|
78
99
|
decoder = new TextDecoder(label);
|
|
79
100
|
} catch {
|
|
80
|
-
//
|
|
101
|
+
// Falling through to DEFAULT_ENCODING (UTF-8) would silently write
|
|
102
|
+
// different bytes from the explicitly requested Firebird codepage.
|
|
103
|
+
// Official Node builds include these ICU tables; constrained builds
|
|
104
|
+
// must fail clearly instead of corrupting text.
|
|
105
|
+
throw new Error(
|
|
106
|
+
`The requested Firebird encoding ${name} requires the ${label} ICU codec, ` +
|
|
107
|
+
'but this Node.js runtime does not provide it. Use an official full-ICU ' +
|
|
108
|
+
'Node.js build, or connect with encoding NONE and pass explicitly encoded Buffer values.'
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
const one = Buffer.alloc(1);
|
|
112
|
+
for (let i = 0; i < 256; i++) {
|
|
113
|
+
one[0] = i;
|
|
114
|
+
toCode[i] = decoder.decode(one).charCodeAt(0);
|
|
115
|
+
}
|
|
116
|
+
return toCode;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function buildCodec(name: string): TextCodec | null {
|
|
120
|
+
const label = ICU_LABELS[name];
|
|
121
|
+
if (!label) {
|
|
81
122
|
return null;
|
|
82
123
|
}
|
|
83
124
|
|
|
84
|
-
// Build both directions from
|
|
85
|
-
//
|
|
86
|
-
//
|
|
87
|
-
//
|
|
88
|
-
const toCode =
|
|
125
|
+
// Build both directions from one byte→code table — every byte of a
|
|
126
|
+
// single-byte codepage maps to exactly one BMP character (undefined
|
|
127
|
+
// bytes decode to U+FFFD, which is kept for decoding but never used
|
|
128
|
+
// for the reverse map).
|
|
129
|
+
const toCode = buildByteToCodeTable(name, label);
|
|
89
130
|
const toByte = new Map<string, number>();
|
|
90
|
-
const one = Buffer.alloc(1);
|
|
91
131
|
for (let i = 0; i < 256; i++) {
|
|
92
|
-
|
|
93
|
-
const ch = decoder.decode(one);
|
|
94
|
-
toCode[i] = ch.charCodeAt(0);
|
|
132
|
+
const ch = String.fromCharCode(toCode[i]);
|
|
95
133
|
if (ch !== '�' && !toByte.has(ch)) {
|
|
96
134
|
toByte.set(ch, i);
|
|
97
135
|
}
|
|
@@ -130,8 +168,10 @@ function buildCodec(name: string): TextCodec | null {
|
|
|
130
168
|
}
|
|
131
169
|
|
|
132
170
|
/**
|
|
133
|
-
* Codec for a Firebird charset name, or null when the charset is unknown
|
|
134
|
-
* natively handled by Buffer
|
|
171
|
+
* Codec for a Firebird charset name, or null when the charset is unknown or
|
|
172
|
+
* natively handled by Buffer. A known codepage whose ICU table is unavailable
|
|
173
|
+
* throws instead of silently falling back to UTF-8. Successful and unknown
|
|
174
|
+
* lookups are cached; failures are not.
|
|
135
175
|
*/
|
|
136
176
|
export function getCodec(charsetName: string | undefined): TextCodec | null {
|
|
137
177
|
if (!charsetName) {
|
package/src/wire/connection.ts
CHANGED
|
@@ -1710,10 +1710,14 @@ class Connection {
|
|
|
1710
1710
|
|
|
1711
1711
|
var op = Const.op_execute;
|
|
1712
1712
|
if (
|
|
1713
|
-
this.accept.protocolVersion >= Const.PROTOCOL_VERSION13 &&
|
|
1714
1713
|
statement.type === Const.isc_info_sql_stmt_exec_procedure &&
|
|
1715
1714
|
statement.output.length
|
|
1716
1715
|
) {
|
|
1716
|
+
// op_execute2 returns output parameters via op_sql_response for all
|
|
1717
|
+
// supported protocol versions (including V10/V11/V12 / Firebird 1.5–2.5).
|
|
1718
|
+
// Using op_execute for procedures with outputs on legacy protocols caused
|
|
1719
|
+
// the fallback fetch path to return 0 rows and deliver undefined to the
|
|
1720
|
+
// caller (issue #424).
|
|
1717
1721
|
op = Const.op_execute2;
|
|
1718
1722
|
}
|
|
1719
1723
|
|
package/src/wire/xsqlvar.ts
CHANGED
|
@@ -29,13 +29,13 @@ const EMPTY_BUFFER = Buffer.alloc(0);
|
|
|
29
29
|
* We must decode raw bytes with the matching Node.js encoding so that
|
|
30
30
|
* characters outside ASCII are reproduced correctly.
|
|
31
31
|
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
32
|
+
* Other recognized single-byte character sets are handled by the ICU-backed
|
|
33
|
+
* codec path. Only unknown character-set names fall back to the
|
|
34
|
+
* connection-level DEFAULT_ENCODING, typically UTF-8.
|
|
34
35
|
*/
|
|
35
36
|
const FirebirdToNodeEncoding: Readonly<Record<string, string>> = Object.freeze({
|
|
36
37
|
UTF8: 'utf8',
|
|
37
38
|
UNICODE_FSS: 'utf8',
|
|
38
|
-
WIN1252: 'latin1',
|
|
39
39
|
ISO8859_1: 'latin1',
|
|
40
40
|
LATIN1: 'latin1',
|
|
41
41
|
ASCII: 'ascii',
|