node-firebird 2.14.4 → 2.15.1
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 +46 -4
- package/lib/index.d.ts +6 -0
- package/lib/index.js +9 -3
- package/lib/pool.js +16 -5
- package/lib/types.d.ts +13 -0
- package/lib/uri.js +8 -1
- package/lib/wire/const.d.ts +3 -0
- package/lib/wire/const.js +6 -0
- package/lib/wire/serialize.d.ts +2 -0
- package/lib/wire/serialize.js +11 -1
- package/lib/wire/xsqlvar.d.ts +6 -3
- package/lib/wire/xsqlvar.js +46 -19
- package/package.json +2 -2
- package/src/index.ts +7 -0
- package/src/pool.ts +15 -5
- package/src/types.ts +14 -0
- package/src/uri.ts +8 -1
- package/src/wire/const.ts +7 -0
- package/src/wire/serialize.ts +16 -2
- package/src/wire/xsqlvar.ts +52 -22
package/README.md
CHANGED
|
@@ -210,6 +210,7 @@ options.jsonAsObject = false; // optional; automatically stringify parameters an
|
|
|
210
210
|
options.namedPlaceholders = false; // set to true to allow :name placeholders in SQL with a { name: value } params object (see Named placeholders)
|
|
211
211
|
options.nestTables = false; // true nests object rows by source table (row[table][column]); a string separator flattens to 'table<sep>column' keys — see Nested result tables (nestTables). Overridable per query
|
|
212
212
|
options.transformKeys = undefined; // 'camel' (FIRST_NAME → firstName) or a (key) => key mapper for object-row keys — see Transforming row keys (transformKeys). Overridable per query
|
|
213
|
+
options.numericMode = Firebird.NUMERIC_MODE_LOSSY; // INT64/INT128 result policy: LOSSY (default), SAFE, or STRING
|
|
213
214
|
options.typeCast = undefined; // optional; custom type parser called for every result column value (see Custom type parsers)
|
|
214
215
|
options.statementCacheSize = 0; // optional; per-connection LRU cache of prepared statements, 0 = disabled (see Prepared-statement cache)
|
|
215
216
|
```
|
|
@@ -738,6 +739,48 @@ on databases and transactions alike. It is ignored by the streaming APIs
|
|
|
738
739
|
PROCEDURE`, `affectedRows` reflects DML the procedure performed — a
|
|
739
740
|
procedure that only returns values reports 0 alongside its row.
|
|
740
741
|
|
|
742
|
+
### Fixed-point numeric results (numericMode)
|
|
743
|
+
|
|
744
|
+
Firebird sends `BIGINT`, `INT128`, and the `NUMERIC`/`DECIMAL` types backed by
|
|
745
|
+
them as signed integer coefficients plus a decimal scale. JavaScript numbers
|
|
746
|
+
cannot represent every INT64 or INT128 coefficient exactly. The connection
|
|
747
|
+
option `numericMode` controls how those result values are exposed:
|
|
748
|
+
|
|
749
|
+
| Mode | Result policy |
|
|
750
|
+
| :--- | :--- |
|
|
751
|
+
| `Firebird.NUMERIC_MODE_LOSSY` | INT64-backed values are returned as `number`; INT128 uses a mixed `number`/`string` path. Unsafe coefficients may lose precision. |
|
|
752
|
+
| `Firebird.NUMERIC_MODE_SAFE` | Safe coefficients are returned as `number`; unsafe coefficients as exact scaled `string`. |
|
|
753
|
+
| `Firebird.NUMERIC_MODE_STRING` | All values are returned as exact scaled `string`. |
|
|
754
|
+
|
|
755
|
+
`LOSSY` decodes INT64-backed values through JavaScript `Number`. INT128 uses a
|
|
756
|
+
mixed number/string decoding path. For coefficients outside JavaScript's safe
|
|
757
|
+
integer range, the result type can depend on the Firebird wire type and value,
|
|
758
|
+
and numeric precision is not guaranteed. `LOSSY` remains the default so that
|
|
759
|
+
adding `numericMode` does not silently change result types for applications
|
|
760
|
+
upgrading from earlier node-firebird releases.
|
|
761
|
+
|
|
762
|
+
`SAFE` tests the raw integer coefficient against JavaScript's inclusive safe
|
|
763
|
+
range (`Number.MIN_SAFE_INTEGER` through `Number.MAX_SAFE_INTEGER`) before
|
|
764
|
+
applying its scale. `STRING` provides a stable result type and retains zeroes
|
|
765
|
+
implied by the declared scale:
|
|
766
|
+
|
|
767
|
+
```js
|
|
768
|
+
const db = await Firebird.attachAsync({
|
|
769
|
+
...options,
|
|
770
|
+
numericMode: Firebird.NUMERIC_MODE_STRING,
|
|
771
|
+
});
|
|
772
|
+
|
|
773
|
+
// BIGINT 42 -> '42'
|
|
774
|
+
// DECIMAL coefficient 420000,-4 -> '42.0000'
|
|
775
|
+
```
|
|
776
|
+
|
|
777
|
+
The string literals `'lossy'`, `'safe'`, and `'string'` are accepted too,
|
|
778
|
+
including in connection URIs (`?numericMode=safe`). `NULL` remains `null` in
|
|
779
|
+
every mode. The option does not change `FLOAT`, `DOUBLE`, `DECFLOAT`, or input
|
|
780
|
+
parameter encoding. A `SAFE` result returned as a number still has the normal
|
|
781
|
+
IEEE-754 behavior of JavaScript fractional numbers; use `STRING` when the
|
|
782
|
+
decimal representation itself must remain exact.
|
|
783
|
+
|
|
741
784
|
### Custom type parsers (typeCast)
|
|
742
785
|
|
|
743
786
|
The `typeCast` connection option lets you override how column values are
|
|
@@ -758,10 +801,6 @@ Firebird.attach({
|
|
|
758
801
|
const v = next();
|
|
759
802
|
return v === null ? null : v.toISOString().slice(0, 10);
|
|
760
803
|
}
|
|
761
|
-
// BIGINT columns as strings
|
|
762
|
-
if (column.type === Firebird.SQL_TYPES.SQL_INT64 && !column.scale) {
|
|
763
|
-
return String(next());
|
|
764
|
-
}
|
|
765
804
|
return next(); // everything else: default decoding
|
|
766
805
|
},
|
|
767
806
|
}, (err, db) => { /* ... */ });
|
|
@@ -782,6 +821,9 @@ Firebird.attach({
|
|
|
782
821
|
|
|
783
822
|
Notes:
|
|
784
823
|
|
|
824
|
+
- The hook runs after [`numericMode`](#fixed-point-numeric-results-numericmode).
|
|
825
|
+
Calling `String(next())` cannot recover digits already lost by lossy
|
|
826
|
+
numeric decoding; select `SAFE` or `STRING` when exact coefficients matter.
|
|
785
827
|
- Non-text BLOB columns reach the hook as the usual asynchronous fetch
|
|
786
828
|
function; text BLOBs with `blobAsText: true` reach it as the resolved
|
|
787
829
|
string.
|
package/lib/index.d.ts
CHANGED
|
@@ -13,6 +13,12 @@ export declare const AUTH_PLUGIN_SRP384: string;
|
|
|
13
13
|
export declare const AUTH_PLUGIN_SRP512: string;
|
|
14
14
|
export declare const WIRE_CRYPT_DISABLE: number;
|
|
15
15
|
export declare const WIRE_CRYPT_ENABLE: number;
|
|
16
|
+
/** Decode through JavaScript Number where applicable; unsafe coefficients may lose precision. */
|
|
17
|
+
export declare const NUMERIC_MODE_LOSSY: "lossy";
|
|
18
|
+
/** Return safe INT64/INT128 coefficients as numbers and unsafe ones as exact strings. */
|
|
19
|
+
export declare const NUMERIC_MODE_SAFE: "safe";
|
|
20
|
+
/** Return every INT64/INT128-backed fixed-point value as an exact string. */
|
|
21
|
+
export declare const NUMERIC_MODE_STRING: "string";
|
|
16
22
|
/** A transaction sees changes done by uncommitted transactions. */
|
|
17
23
|
export declare const ISOLATION_READ_UNCOMMITTED: number[];
|
|
18
24
|
/** A transaction sees only data committed before the statement has been executed. */
|
package/lib/index.js
CHANGED
|
@@ -17,9 +17,9 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
17
17
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
18
18
|
};
|
|
19
19
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
20
|
-
exports.
|
|
21
|
-
exports.
|
|
22
|
-
exports.parseNamedPlaceholders = exports.parseConnectionString = exports.parseConnectionUri = exports.connection = exports.SQL_TYPES = exports.escape = exports.isc_dpb_search_path = exports.isc_dpb_max_inline_blob_size = exports.isc_dpb_max_blob_cache_size = exports.isc_dpb_owner = exports.isc_dpb_worker_attach = exports.isc_dpb_parallel_workers = exports.isc_dpb_upgrade_db = exports.isc_dpb_clear_map = exports.isc_dpb_decfloat_traps = exports.isc_dpb_decfloat_round = exports.isc_dpb_set_bind = void 0;
|
|
20
|
+
exports.isc_dpb_interp = exports.isc_dpb_sys_user_name_enc = exports.isc_dpb_password_enc = exports.isc_dpb_password = exports.isc_dpb_user_name = exports.isc_dpb_no_reserve = exports.isc_dpb_quit_log = exports.isc_dpb_begin_log = exports.isc_dpb_force_write = exports.isc_dpb_delete_shadow = exports.isc_dpb_sweep_interval = exports.isc_dpb_activate_shadow = exports.isc_dpb_encrypt_key = exports.isc_dpb_sys_user_name = exports.isc_dpb_license = exports.isc_dpb_damaged = exports.isc_dpb_no_garbage_collect = exports.isc_dpb_trace = exports.isc_dpb_number_of_users = exports.isc_dpb_dbkey_scope = exports.isc_dpb_disable_journal = exports.isc_dpb_enable_journal = exports.isc_dpb_sweep = exports.isc_dpb_verify = exports.isc_dpb_garbage_collect = exports.isc_dpb_debug = exports.isc_dpb_buffer_length = exports.isc_dpb_num_buffers = exports.isc_dpb_page_size = exports.isc_dpb_journal = exports.isc_dpb_allocation = exports.isc_dpb_cdd_pathname = exports.isc_dpb_version2 = exports.isc_dpb_version1 = exports.ISOLATION_READ_COMMITTED_READ_ONLY = exports.ISOLATION_SERIALIZABLE = exports.ISOLATION_REPEATABLE_READ = exports.ISOLATION_READ_COMMITTED = exports.ISOLATION_READ_UNCOMMITTED = exports.NUMERIC_MODE_STRING = exports.NUMERIC_MODE_SAFE = exports.NUMERIC_MODE_LOSSY = exports.WIRE_CRYPT_ENABLE = exports.WIRE_CRYPT_DISABLE = exports.AUTH_PLUGIN_SRP512 = exports.AUTH_PLUGIN_SRP384 = exports.AUTH_PLUGIN_SRP256 = exports.AUTH_PLUGIN_SRP = exports.AUTH_PLUGIN_LEGACY = exports.GDSCode = void 0;
|
|
21
|
+
exports.isc_dpb_reset_icu = exports.isc_dpb_nolinger = exports.isc_dpb_config = exports.isc_dpb_auth_plugin_name = exports.isc_dpb_auth_plugin_list = exports.isc_dpb_specific_auth_data = exports.isc_dpb_os_user = exports.isc_dpb_host_name = exports.isc_dpb_remote_protocol = exports.isc_dpb_client_version = exports.isc_dpb_auth_block = exports.isc_dpb_ext_call_depth = exports.isc_dpb_utf8_filename = exports.isc_dpb_org_filename = exports.isc_dpb_trusted_role = exports.isc_dpb_process_name = exports.isc_dpb_trusted_auth = exports.isc_dpb_no_db_triggers = exports.isc_dpb_process_id = exports.isc_dpb_address_path = exports.isc_dpb_gsec_attach = exports.isc_dpb_set_db_charset = exports.isc_dpb_gstat_attach = exports.isc_dpb_gfix_attach = exports.isc_dpb_set_db_sql_dialect = exports.isc_dpb_set_db_readonly = exports.isc_dpb_sql_dialect = exports.isc_dpb_working_directory = exports.isc_dpb_set_page_buffers = exports.isc_dpb_sql_role_name = exports.isc_dpb_gbak_attach = exports.isc_dpb_dummy_packet_interval = exports.isc_dpb_connect_timeout = exports.isc_dpb_sec_attach = exports.isc_dpb_overwrite = exports.isc_dpb_reserved = exports.isc_dpb_shutdown_delay = exports.isc_dpb_online = exports.isc_dpb_shutdown = exports.isc_dpb_cache_manager = exports.isc_dpb_lc_ctype = exports.isc_dpb_lc_messages = exports.isc_dpb_old_dump_id = exports.isc_dpb_old_start_file = exports.isc_dpb_old_start_seqno = exports.isc_dpb_old_start_page = exports.isc_dpb_old_file = exports.isc_dpb_old_num_files = exports.isc_dpb_old_file_size = exports.isc_dpb_online_dump = void 0;
|
|
22
|
+
exports.parseNamedPlaceholders = exports.parseConnectionString = exports.parseConnectionUri = exports.connection = exports.SQL_TYPES = exports.escape = exports.isc_dpb_search_path = exports.isc_dpb_max_inline_blob_size = exports.isc_dpb_max_blob_cache_size = exports.isc_dpb_owner = exports.isc_dpb_worker_attach = exports.isc_dpb_parallel_workers = exports.isc_dpb_upgrade_db = exports.isc_dpb_clear_map = exports.isc_dpb_decfloat_traps = exports.isc_dpb_decfloat_round = exports.isc_dpb_set_bind = exports.isc_dpb_set_db_replica = exports.isc_dpb_session_time_zone = exports.isc_dpb_map_attach = void 0;
|
|
23
23
|
exports.attach = attach;
|
|
24
24
|
exports.drop = drop;
|
|
25
25
|
exports.create = create;
|
|
@@ -54,6 +54,12 @@ exports.AUTH_PLUGIN_SRP384 = const_1.default.AUTH_PLUGIN_SRP384;
|
|
|
54
54
|
exports.AUTH_PLUGIN_SRP512 = const_1.default.AUTH_PLUGIN_SRP512;
|
|
55
55
|
exports.WIRE_CRYPT_DISABLE = const_1.default.WIRE_CRYPT_DISABLE;
|
|
56
56
|
exports.WIRE_CRYPT_ENABLE = const_1.default.WIRE_CRYPT_ENABLE;
|
|
57
|
+
/** Decode through JavaScript Number where applicable; unsafe coefficients may lose precision. */
|
|
58
|
+
exports.NUMERIC_MODE_LOSSY = const_1.default.NUMERIC_MODE_LOSSY;
|
|
59
|
+
/** Return safe INT64/INT128 coefficients as numbers and unsafe ones as exact strings. */
|
|
60
|
+
exports.NUMERIC_MODE_SAFE = const_1.default.NUMERIC_MODE_SAFE;
|
|
61
|
+
/** Return every INT64/INT128-backed fixed-point value as an exact string. */
|
|
62
|
+
exports.NUMERIC_MODE_STRING = const_1.default.NUMERIC_MODE_STRING;
|
|
57
63
|
/** A transaction sees changes done by uncommitted transactions. */
|
|
58
64
|
exports.ISOLATION_READ_UNCOMMITTED = const_1.default.ISOLATION_READ_UNCOMMITTED;
|
|
59
65
|
/** A transaction sees only data committed before the statement has been executed. */
|
package/lib/pool.js
CHANGED
|
@@ -258,6 +258,12 @@ class Pool extends events_1.default.EventEmitter {
|
|
|
258
258
|
self.internaldb.splice(self.internaldb.indexOf(db), 1);
|
|
259
259
|
self.emit('remove', db);
|
|
260
260
|
}
|
|
261
|
+
else if (self._destroyed) {
|
|
262
|
+
// destroy() deliberately does not wait for checked-out
|
|
263
|
+
// connections. Close one for good when its caller later
|
|
264
|
+
// releases it instead of returning it to a stopped pool.
|
|
265
|
+
self._retire(db);
|
|
266
|
+
}
|
|
261
267
|
else if (self._isExpired(db)) {
|
|
262
268
|
// worn out (maxUses / maxLifetimeMillis): close it
|
|
263
269
|
// for good instead of returning it to the idle
|
|
@@ -318,7 +324,7 @@ class Pool extends events_1.default.EventEmitter {
|
|
|
318
324
|
callback();
|
|
319
325
|
}
|
|
320
326
|
}
|
|
321
|
-
this.internaldb.forEach(function (db) {
|
|
327
|
+
this.internaldb.slice().forEach(function (db) {
|
|
322
328
|
if (db.connection._pooled === false) {
|
|
323
329
|
detachCallback();
|
|
324
330
|
return;
|
|
@@ -326,15 +332,20 @@ class Pool extends events_1.default.EventEmitter {
|
|
|
326
332
|
// check if the db is not free into the pool otherwise user should manual detach it
|
|
327
333
|
var _db_in_pool = self.pooldb.indexOf(db);
|
|
328
334
|
if (_db_in_pool !== -1) {
|
|
329
|
-
self.pooldb.splice(_db_in_pool, 1);
|
|
330
335
|
db.connection._pooled = false;
|
|
331
|
-
|
|
332
|
-
|
|
336
|
+
// Keep the connection marked idle until physical detach emits
|
|
337
|
+
// its detach event. The listener above will then ignore that
|
|
338
|
+
// event instead of counting it as an active release.
|
|
339
|
+
db.detach(function (err) {
|
|
340
|
+
self._forget(db);
|
|
341
|
+
detachCallback(err);
|
|
342
|
+
});
|
|
333
343
|
}
|
|
334
344
|
else {
|
|
335
345
|
// [Fix 5] Connection is currently in use (dbinuse > 0).
|
|
336
346
|
// The caller is responsible for releasing it via detach().
|
|
337
|
-
// Count it down here so the destroy() callback is not blocked forever
|
|
347
|
+
// Count it down here so the destroy() callback is not blocked forever;
|
|
348
|
+
// the detach listener retires it when the caller releases it.
|
|
338
349
|
detachCallback();
|
|
339
350
|
}
|
|
340
351
|
});
|
package/lib/types.d.ts
CHANGED
|
@@ -58,6 +58,8 @@ export interface ColumnMetadata {
|
|
|
58
58
|
collationId?: number;
|
|
59
59
|
}
|
|
60
60
|
export type Isolation = number[];
|
|
61
|
+
/** Result conversion policy for INT64/INT128-backed fixed-point values. */
|
|
62
|
+
export type NumericMode = 'lossy' | 'safe' | 'string';
|
|
61
63
|
export type TransactionOptions = {
|
|
62
64
|
autoCommit?: boolean;
|
|
63
65
|
autoUndo?: boolean;
|
|
@@ -373,6 +375,17 @@ export interface Options {
|
|
|
373
375
|
*/
|
|
374
376
|
blobReadChunkSize?: number;
|
|
375
377
|
wireCrypt?: number;
|
|
378
|
+
/**
|
|
379
|
+
* Result conversion policy for BIGINT/INT128 and fixed-point
|
|
380
|
+
* NUMERIC/DECIMAL values backed by them.
|
|
381
|
+
*
|
|
382
|
+
* - `lossy` (default) decodes through JavaScript `Number` where
|
|
383
|
+
* applicable; unsafe coefficients may lose precision.
|
|
384
|
+
* - `safe` returns a number when the raw integer coefficient is within
|
|
385
|
+
* JavaScript's safe range, otherwise an exact scaled string.
|
|
386
|
+
* - `string` always returns an exact scaled string.
|
|
387
|
+
*/
|
|
388
|
+
numericMode?: NumericMode;
|
|
376
389
|
wireCompression?: boolean;
|
|
377
390
|
/**
|
|
378
391
|
* Enable named placeholders: SQL may use `:name` markers and params may
|
package/lib/uri.js
CHANGED
|
@@ -189,7 +189,14 @@ function normalizeOptions(options) {
|
|
|
189
189
|
if (typeof options === 'string') {
|
|
190
190
|
options = parseConnectionString(options);
|
|
191
191
|
}
|
|
192
|
-
|
|
192
|
+
const normalized = applyEnvDefaults(options);
|
|
193
|
+
const numericMode = normalized && normalized.numericMode;
|
|
194
|
+
if (numericMode !== undefined && numericMode !== 'lossy' &&
|
|
195
|
+
numericMode !== 'safe' && numericMode !== 'string') {
|
|
196
|
+
throw new Error('Invalid numericMode option: ' + numericMode +
|
|
197
|
+
' (expected "lossy", "safe", or "string")');
|
|
198
|
+
}
|
|
199
|
+
return normalized;
|
|
193
200
|
}
|
|
194
201
|
/**
|
|
195
202
|
* Fall back to environment variables for connection settings the caller
|
package/lib/wire/const.d.ts
CHANGED
|
@@ -16,6 +16,9 @@ declare const Const: Readonly<{
|
|
|
16
16
|
MAX_BUFFER_SIZE: number;
|
|
17
17
|
MAX_INT: number;
|
|
18
18
|
MIN_INT: number;
|
|
19
|
+
NUMERIC_MODE_LOSSY: 'lossy';
|
|
20
|
+
NUMERIC_MODE_SAFE: 'safe';
|
|
21
|
+
NUMERIC_MODE_STRING: 'string';
|
|
19
22
|
op_void: number;
|
|
20
23
|
op_connect: number;
|
|
21
24
|
op_exit: number;
|
package/lib/wire/const.js
CHANGED
|
@@ -22,6 +22,11 @@ const int = {
|
|
|
22
22
|
MAX_INT: Math.pow(2, 31) - 1,
|
|
23
23
|
MIN_INT: -Math.pow(2, 31),
|
|
24
24
|
};
|
|
25
|
+
const numericMode = {
|
|
26
|
+
NUMERIC_MODE_LOSSY: 'lossy',
|
|
27
|
+
NUMERIC_MODE_SAFE: 'safe',
|
|
28
|
+
NUMERIC_MODE_STRING: 'string',
|
|
29
|
+
};
|
|
25
30
|
const op = {
|
|
26
31
|
op_void: 0, // Packet has been voided
|
|
27
32
|
op_connect: 1, // Connect to remote server
|
|
@@ -861,6 +866,7 @@ const Const = Object.freeze({
|
|
|
861
866
|
...int,
|
|
862
867
|
...iscAction,
|
|
863
868
|
...iscError,
|
|
869
|
+
...numericMode,
|
|
864
870
|
...op,
|
|
865
871
|
...protocol,
|
|
866
872
|
...service,
|
package/lib/wire/serialize.d.ts
CHANGED
|
@@ -93,7 +93,9 @@ export declare class XdrReader {
|
|
|
93
93
|
readInt(): number;
|
|
94
94
|
readUInt(): number;
|
|
95
95
|
readInt64(): number;
|
|
96
|
+
readInt64BigInt(): bigint;
|
|
96
97
|
readInt128(): bigint;
|
|
98
|
+
readInt128Signed(): bigint;
|
|
97
99
|
readDecFloat16(): string | number;
|
|
98
100
|
readDecFloat34(): string | number;
|
|
99
101
|
readShort(): number;
|
package/lib/wire/serialize.js
CHANGED
|
@@ -370,7 +370,10 @@ class XdrReader {
|
|
|
370
370
|
// Note: precision is limited to Number.MAX_SAFE_INTEGER (±2^53-1).
|
|
371
371
|
// Values outside this range lose precision, which matches the previous
|
|
372
372
|
// Long(low, high).toNumber() behaviour.
|
|
373
|
-
|
|
373
|
+
return Number(this.readInt64BigInt());
|
|
374
|
+
}
|
|
375
|
+
readInt64BigInt() {
|
|
376
|
+
const result = this.buffer.readBigInt64BE(this.pos);
|
|
374
377
|
this.pos += 8;
|
|
375
378
|
return result;
|
|
376
379
|
}
|
|
@@ -381,6 +384,13 @@ class XdrReader {
|
|
|
381
384
|
this.pos += 8;
|
|
382
385
|
return (BigInt(high) << BigInt(64)) + BigInt(low);
|
|
383
386
|
}
|
|
387
|
+
readInt128Signed() {
|
|
388
|
+
var high = this.buffer.readBigInt64BE(this.pos);
|
|
389
|
+
this.pos += 8;
|
|
390
|
+
var low = this.buffer.readBigUInt64BE(this.pos);
|
|
391
|
+
this.pos += 8;
|
|
392
|
+
return (BigInt(high) << BigInt(64)) + BigInt(low);
|
|
393
|
+
}
|
|
384
394
|
readDecFloat16() {
|
|
385
395
|
// DECFLOAT(16) - IEEE 754 Decimal64 - 8 bytes
|
|
386
396
|
// Full IEEE 754-2008 Decimal64 implementation
|
package/lib/wire/xsqlvar.d.ts
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import type { TextCodec } from './codepages';
|
|
2
2
|
import type { XdrReader, XdrWriter, BlrWriter } from './serialize';
|
|
3
|
-
import type { RecordCounts } from '../types';
|
|
3
|
+
import type { NumericMode, RecordCounts } from '../types';
|
|
4
|
+
type NumericDecodeOptions = {
|
|
5
|
+
numericMode?: NumericMode;
|
|
6
|
+
};
|
|
4
7
|
export declare function getFirebirdCharsetWidth(charset?: string): number;
|
|
5
8
|
/**
|
|
6
9
|
* Resolve the Node.js Buffer encoding to use when decoding text from a
|
|
@@ -197,11 +200,11 @@ export declare class SQLVarShort extends SQLVarInt {
|
|
|
197
200
|
calcBlr(blr: BlrWriter): void;
|
|
198
201
|
}
|
|
199
202
|
export declare class SQLVarInt64 extends SQLVarBase {
|
|
200
|
-
decode(data: XdrReader, lowerV13: boolean): number | null;
|
|
203
|
+
decode(data: XdrReader, lowerV13: boolean, options?: NumericDecodeOptions): string | number | null;
|
|
201
204
|
calcBlr(blr: BlrWriter): void;
|
|
202
205
|
}
|
|
203
206
|
export declare class SQLVarInt128 extends SQLVarBase {
|
|
204
|
-
decode(data: XdrReader, lowerV13: boolean): string | number | null;
|
|
207
|
+
decode(data: XdrReader, lowerV13: boolean, options?: NumericDecodeOptions): string | number | null;
|
|
205
208
|
calcBlr(blr: BlrWriter): void;
|
|
206
209
|
}
|
|
207
210
|
export declare class SQLVarDecFloat16 extends SQLVarBase {
|
package/lib/wire/xsqlvar.js
CHANGED
|
@@ -30,6 +30,37 @@ const codepages_1 = require("./codepages");
|
|
|
30
30
|
const ScaleDivisor = [1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000, 10000000000, 100000000000, 1000000000000, 10000000000000, 100000000000000, 1000000000000000];
|
|
31
31
|
const DateOffset = 40587, TimeCoeff = 86400000, MsPerMinute = 60000;
|
|
32
32
|
const EMPTY_BUFFER = Buffer.alloc(0);
|
|
33
|
+
const MAX_SAFE_BIGINT = BigInt(Number.MAX_SAFE_INTEGER);
|
|
34
|
+
const MIN_SAFE_BIGINT = BigInt(Number.MIN_SAFE_INTEGER);
|
|
35
|
+
/** Format a signed Firebird integer coefficient without passing through Number. */
|
|
36
|
+
function formatScaledBigInt(value, scale) {
|
|
37
|
+
const negative = value < 0n;
|
|
38
|
+
let digits = (negative ? -value : value).toString();
|
|
39
|
+
const sign = negative ? '-' : '';
|
|
40
|
+
if (scale === 0)
|
|
41
|
+
return sign + digits;
|
|
42
|
+
if (scale > 0)
|
|
43
|
+
return sign + digits + '0'.repeat(scale);
|
|
44
|
+
const places = -scale;
|
|
45
|
+
if (digits.length <= places)
|
|
46
|
+
digits = digits.padStart(places + 1, '0');
|
|
47
|
+
return sign + digits.slice(0, -places) + '.' + digits.slice(-places);
|
|
48
|
+
}
|
|
49
|
+
function decodeExactNumeric(value, scale, mode) {
|
|
50
|
+
if (mode === 'string' || value > MAX_SAFE_BIGINT || value < MIN_SAFE_BIGINT) {
|
|
51
|
+
return formatScaledBigInt(value, scale);
|
|
52
|
+
}
|
|
53
|
+
return scale < 0
|
|
54
|
+
? Number(value) / Math.pow(10, -scale)
|
|
55
|
+
: Number(value) * Math.pow(10, scale);
|
|
56
|
+
}
|
|
57
|
+
/** Decode INT128 using the mixed number/string policy of lossy mode. */
|
|
58
|
+
function decodeLossyInt128(value, scale) {
|
|
59
|
+
if (value > MAX_SAFE_BIGINT) {
|
|
60
|
+
return formatScaledBigInt(value, scale);
|
|
61
|
+
}
|
|
62
|
+
return Number(value) / ScaleDivisor[Math.abs(scale)];
|
|
63
|
+
}
|
|
33
64
|
/**
|
|
34
65
|
* Maps Firebird character-set names (upper-case) to the Node.js Buffer
|
|
35
66
|
* encoding string used by Buffer.toString() / Buffer.from().
|
|
@@ -480,10 +511,16 @@ class SQLVarShort extends SQLVarInt {
|
|
|
480
511
|
exports.SQLVarShort = SQLVarShort;
|
|
481
512
|
//------------------------------------------------------
|
|
482
513
|
class SQLVarInt64 extends SQLVarBase {
|
|
483
|
-
decode(data, lowerV13) {
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
514
|
+
decode(data, lowerV13, options) {
|
|
515
|
+
const mode = options?.numericMode || const_1.default.NUMERIC_MODE_LOSSY;
|
|
516
|
+
let ret;
|
|
517
|
+
if (mode === const_1.default.NUMERIC_MODE_LOSSY) {
|
|
518
|
+
ret = data.readInt64();
|
|
519
|
+
if (this.scale)
|
|
520
|
+
ret = ret / ScaleDivisor[Math.abs(this.scale)];
|
|
521
|
+
}
|
|
522
|
+
else {
|
|
523
|
+
ret = decodeExactNumeric(data.readInt64BigInt(), this.scale, mode);
|
|
487
524
|
}
|
|
488
525
|
if (!lowerV13 || !data.readInt()) {
|
|
489
526
|
return ret;
|
|
@@ -498,21 +535,11 @@ class SQLVarInt64 extends SQLVarBase {
|
|
|
498
535
|
exports.SQLVarInt64 = SQLVarInt64;
|
|
499
536
|
//------------------------------------------------------
|
|
500
537
|
class SQLVarInt128 extends SQLVarBase {
|
|
501
|
-
decode(data, lowerV13) {
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
var integerPart = ret.slice(0, Math.abs(this.scale) * -1);
|
|
507
|
-
var decimalPart = ret.slice(Math.abs(this.scale) * -1);
|
|
508
|
-
if (integerPart === '')
|
|
509
|
-
integerPart = '0';
|
|
510
|
-
ret = `${integerPart}.${decimalPart}`;
|
|
511
|
-
}
|
|
512
|
-
else {
|
|
513
|
-
ret = Number(retBigInt);
|
|
514
|
-
ret = ret / ScaleDivisor[Math.abs(this.scale)];
|
|
515
|
-
}
|
|
538
|
+
decode(data, lowerV13, options) {
|
|
539
|
+
const mode = options?.numericMode || const_1.default.NUMERIC_MODE_LOSSY;
|
|
540
|
+
const ret = mode === const_1.default.NUMERIC_MODE_LOSSY
|
|
541
|
+
? decodeLossyInt128(data.readInt128(), this.scale)
|
|
542
|
+
: decodeExactNumeric(data.readInt128Signed(), this.scale, mode);
|
|
516
543
|
if (!lowerV13 || !data.readInt()) {
|
|
517
544
|
return ret;
|
|
518
545
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "node-firebird",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.15.1",
|
|
4
4
|
"description": "Pure JavaScript and Asynchronous Firebird client for Node.js.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"firebird",
|
|
@@ -58,6 +58,6 @@
|
|
|
58
58
|
"@types/node": "^26.1.1",
|
|
59
59
|
"oxlint": "^0.15.11",
|
|
60
60
|
"typescript": "^7.0.2",
|
|
61
|
-
"vitest": "^4.
|
|
61
|
+
"vitest": "^4.1.11"
|
|
62
62
|
}
|
|
63
63
|
}
|
package/src/index.ts
CHANGED
|
@@ -35,6 +35,13 @@ export const AUTH_PLUGIN_SRP512: string = Const.AUTH_PLUGIN_SRP512;
|
|
|
35
35
|
export const WIRE_CRYPT_DISABLE: number = Const.WIRE_CRYPT_DISABLE;
|
|
36
36
|
export const WIRE_CRYPT_ENABLE: number = Const.WIRE_CRYPT_ENABLE;
|
|
37
37
|
|
|
38
|
+
/** Decode through JavaScript Number where applicable; unsafe coefficients may lose precision. */
|
|
39
|
+
export const NUMERIC_MODE_LOSSY = Const.NUMERIC_MODE_LOSSY;
|
|
40
|
+
/** Return safe INT64/INT128 coefficients as numbers and unsafe ones as exact strings. */
|
|
41
|
+
export const NUMERIC_MODE_SAFE = Const.NUMERIC_MODE_SAFE;
|
|
42
|
+
/** Return every INT64/INT128-backed fixed-point value as an exact string. */
|
|
43
|
+
export const NUMERIC_MODE_STRING = Const.NUMERIC_MODE_STRING;
|
|
44
|
+
|
|
38
45
|
/** A transaction sees changes done by uncommitted transactions. */
|
|
39
46
|
export const ISOLATION_READ_UNCOMMITTED: number[] = Const.ISOLATION_READ_UNCOMMITTED;
|
|
40
47
|
/** A transaction sees only data committed before the statement has been executed. */
|
package/src/pool.ts
CHANGED
|
@@ -281,6 +281,11 @@ class Pool extends Events.EventEmitter {
|
|
|
281
281
|
if (db.connection._isClosed || db.connection._isDetach || db.connection._pooled === false) {
|
|
282
282
|
self.internaldb.splice(self.internaldb.indexOf(db), 1);
|
|
283
283
|
self.emit('remove', db);
|
|
284
|
+
} else if (self._destroyed) {
|
|
285
|
+
// destroy() deliberately does not wait for checked-out
|
|
286
|
+
// connections. Close one for good when its caller later
|
|
287
|
+
// releases it instead of returning it to a stopped pool.
|
|
288
|
+
self._retire(db);
|
|
284
289
|
} else if (self._isExpired(db)) {
|
|
285
290
|
// worn out (maxUses / maxLifetimeMillis): close it
|
|
286
291
|
// for good instead of returning it to the idle
|
|
@@ -348,7 +353,7 @@ class Pool extends Events.EventEmitter {
|
|
|
348
353
|
}
|
|
349
354
|
}
|
|
350
355
|
|
|
351
|
-
this.internaldb.forEach(function(db) {
|
|
356
|
+
this.internaldb.slice().forEach(function(db) {
|
|
352
357
|
if (db.connection._pooled === false) {
|
|
353
358
|
detachCallback();
|
|
354
359
|
return;
|
|
@@ -356,14 +361,19 @@ class Pool extends Events.EventEmitter {
|
|
|
356
361
|
// check if the db is not free into the pool otherwise user should manual detach it
|
|
357
362
|
var _db_in_pool = self.pooldb.indexOf(db);
|
|
358
363
|
if (_db_in_pool !== -1) {
|
|
359
|
-
self.pooldb.splice(_db_in_pool, 1);
|
|
360
364
|
db.connection._pooled = false;
|
|
361
|
-
|
|
362
|
-
|
|
365
|
+
// Keep the connection marked idle until physical detach emits
|
|
366
|
+
// its detach event. The listener above will then ignore that
|
|
367
|
+
// event instead of counting it as an active release.
|
|
368
|
+
db.detach(function(err?: any) {
|
|
369
|
+
self._forget(db);
|
|
370
|
+
detachCallback(err);
|
|
371
|
+
});
|
|
363
372
|
} else {
|
|
364
373
|
// [Fix 5] Connection is currently in use (dbinuse > 0).
|
|
365
374
|
// The caller is responsible for releasing it via detach().
|
|
366
|
-
// Count it down here so the destroy() callback is not blocked forever
|
|
375
|
+
// Count it down here so the destroy() callback is not blocked forever;
|
|
376
|
+
// the detach listener retires it when the caller releases it.
|
|
367
377
|
detachCallback();
|
|
368
378
|
}
|
|
369
379
|
});
|
package/src/types.ts
CHANGED
|
@@ -70,6 +70,9 @@ export interface ColumnMetadata {
|
|
|
70
70
|
|
|
71
71
|
export type Isolation = number[];
|
|
72
72
|
|
|
73
|
+
/** Result conversion policy for INT64/INT128-backed fixed-point values. */
|
|
74
|
+
export type NumericMode = 'lossy' | 'safe' | 'string';
|
|
75
|
+
|
|
73
76
|
export type TransactionOptions = {
|
|
74
77
|
autoCommit?: boolean;
|
|
75
78
|
autoUndo?: boolean;
|
|
@@ -433,6 +436,17 @@ export interface Options {
|
|
|
433
436
|
*/
|
|
434
437
|
blobReadChunkSize?: number;
|
|
435
438
|
wireCrypt?: number; // WIRE_CRYPT_DISABLE or WIRE_CRYPT_ENABLE
|
|
439
|
+
/**
|
|
440
|
+
* Result conversion policy for BIGINT/INT128 and fixed-point
|
|
441
|
+
* NUMERIC/DECIMAL values backed by them.
|
|
442
|
+
*
|
|
443
|
+
* - `lossy` (default) decodes through JavaScript `Number` where
|
|
444
|
+
* applicable; unsafe coefficients may lose precision.
|
|
445
|
+
* - `safe` returns a number when the raw integer coefficient is within
|
|
446
|
+
* JavaScript's safe range, otherwise an exact scaled string.
|
|
447
|
+
* - `string` always returns an exact scaled string.
|
|
448
|
+
*/
|
|
449
|
+
numericMode?: NumericMode;
|
|
436
450
|
wireCompression?: boolean;
|
|
437
451
|
/**
|
|
438
452
|
* Enable named placeholders: SQL may use `:name` markers and params may
|
package/src/uri.ts
CHANGED
|
@@ -200,7 +200,14 @@ export function normalizeOptions<T>(options: T | string): T {
|
|
|
200
200
|
if (typeof options === 'string') {
|
|
201
201
|
options = parseConnectionString(options) as T;
|
|
202
202
|
}
|
|
203
|
-
|
|
203
|
+
const normalized = applyEnvDefaults(options as any);
|
|
204
|
+
const numericMode = normalized && normalized.numericMode;
|
|
205
|
+
if (numericMode !== undefined && numericMode !== 'lossy' &&
|
|
206
|
+
numericMode !== 'safe' && numericMode !== 'string') {
|
|
207
|
+
throw new Error('Invalid numericMode option: ' + numericMode +
|
|
208
|
+
' (expected "lossy", "safe", or "string")');
|
|
209
|
+
}
|
|
210
|
+
return normalized;
|
|
204
211
|
}
|
|
205
212
|
|
|
206
213
|
/**
|
package/src/wire/const.ts
CHANGED
|
@@ -25,6 +25,12 @@ const int = {
|
|
|
25
25
|
MIN_INT : -Math.pow(2, 31),
|
|
26
26
|
};
|
|
27
27
|
|
|
28
|
+
const numericMode = {
|
|
29
|
+
NUMERIC_MODE_LOSSY : 'lossy',
|
|
30
|
+
NUMERIC_MODE_SAFE : 'safe',
|
|
31
|
+
NUMERIC_MODE_STRING : 'string',
|
|
32
|
+
} as const;
|
|
33
|
+
|
|
28
34
|
const op = {
|
|
29
35
|
op_void : 0, // Packet has been voided
|
|
30
36
|
op_connect : 1, // Connect to remote server
|
|
@@ -929,6 +935,7 @@ const Const = Object.freeze({
|
|
|
929
935
|
...int,
|
|
930
936
|
...iscAction,
|
|
931
937
|
...iscError,
|
|
938
|
+
...numericMode,
|
|
932
939
|
...op,
|
|
933
940
|
...protocol,
|
|
934
941
|
...service,
|
package/src/wire/serialize.ts
CHANGED
|
@@ -481,12 +481,16 @@ export class XdrReader {
|
|
|
481
481
|
// Note: precision is limited to Number.MAX_SAFE_INTEGER (±2^53-1).
|
|
482
482
|
// Values outside this range lose precision, which matches the previous
|
|
483
483
|
// Long(low, high).toNumber() behaviour.
|
|
484
|
-
|
|
484
|
+
return Number(this.readInt64BigInt());
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
readInt64BigInt(): bigint {
|
|
488
|
+
const result = this.buffer.readBigInt64BE(this.pos);
|
|
485
489
|
this.pos += 8;
|
|
486
490
|
return result;
|
|
487
491
|
}
|
|
488
492
|
|
|
489
|
-
readInt128() {
|
|
493
|
+
readInt128(): bigint {
|
|
490
494
|
var high = this.buffer.readBigUInt64BE(this.pos)
|
|
491
495
|
this.pos += 8
|
|
492
496
|
|
|
@@ -496,6 +500,16 @@ export class XdrReader {
|
|
|
496
500
|
return (BigInt(high) << BigInt(64)) + BigInt(low)
|
|
497
501
|
}
|
|
498
502
|
|
|
503
|
+
readInt128Signed(): bigint {
|
|
504
|
+
var high = this.buffer.readBigInt64BE(this.pos)
|
|
505
|
+
this.pos += 8
|
|
506
|
+
|
|
507
|
+
var low = this.buffer.readBigUInt64BE(this.pos)
|
|
508
|
+
this.pos += 8
|
|
509
|
+
|
|
510
|
+
return (BigInt(high) << BigInt(64)) + BigInt(low)
|
|
511
|
+
}
|
|
512
|
+
|
|
499
513
|
readDecFloat16() {
|
|
500
514
|
// DECFLOAT(16) - IEEE 754 Decimal64 - 8 bytes
|
|
501
515
|
// Full IEEE 754-2008 Decimal64 implementation
|
package/src/wire/xsqlvar.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { BlrReader } from './serialize';
|
|
|
3
3
|
import { getCodec } from './codepages';
|
|
4
4
|
import type { TextCodec } from './codepages';
|
|
5
5
|
import type { XdrReader, XdrWriter, BlrWriter } from './serialize';
|
|
6
|
-
import type { RecordCounts } from '../types';
|
|
6
|
+
import type { NumericMode, RecordCounts } from '../types';
|
|
7
7
|
|
|
8
8
|
/***************************************
|
|
9
9
|
*
|
|
@@ -19,6 +19,44 @@ const
|
|
|
19
19
|
MsPerMinute = 60000;
|
|
20
20
|
|
|
21
21
|
const EMPTY_BUFFER = Buffer.alloc(0);
|
|
22
|
+
const MAX_SAFE_BIGINT = BigInt(Number.MAX_SAFE_INTEGER);
|
|
23
|
+
const MIN_SAFE_BIGINT = BigInt(Number.MIN_SAFE_INTEGER);
|
|
24
|
+
|
|
25
|
+
type NumericDecodeOptions = {
|
|
26
|
+
numericMode?: NumericMode;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
/** Format a signed Firebird integer coefficient without passing through Number. */
|
|
30
|
+
function formatScaledBigInt(value: bigint, scale: number): string {
|
|
31
|
+
const negative = value < 0n;
|
|
32
|
+
let digits = (negative ? -value : value).toString();
|
|
33
|
+
const sign = negative ? '-' : '';
|
|
34
|
+
|
|
35
|
+
if (scale === 0) return sign + digits;
|
|
36
|
+
if (scale > 0) return sign + digits + '0'.repeat(scale);
|
|
37
|
+
|
|
38
|
+
const places = -scale;
|
|
39
|
+
if (digits.length <= places) digits = digits.padStart(places + 1, '0');
|
|
40
|
+
return sign + digits.slice(0, -places) + '.' + digits.slice(-places);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function decodeExactNumeric(value: bigint, scale: number, mode: 'safe' | 'string'): number | string {
|
|
44
|
+
if (mode === 'string' || value > MAX_SAFE_BIGINT || value < MIN_SAFE_BIGINT) {
|
|
45
|
+
return formatScaledBigInt(value, scale);
|
|
46
|
+
}
|
|
47
|
+
return scale < 0
|
|
48
|
+
? Number(value) / Math.pow(10, -scale)
|
|
49
|
+
: Number(value) * Math.pow(10, scale);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** Decode INT128 using the mixed number/string policy of lossy mode. */
|
|
53
|
+
function decodeLossyInt128(value: bigint, scale: number): number | string {
|
|
54
|
+
if (value > MAX_SAFE_BIGINT) {
|
|
55
|
+
return formatScaledBigInt(value, scale);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return Number(value) / ScaleDivisor[Math.abs(scale)];
|
|
59
|
+
}
|
|
22
60
|
|
|
23
61
|
/**
|
|
24
62
|
* Maps Firebird character-set names (upper-case) to the Node.js Buffer
|
|
@@ -555,11 +593,15 @@ export class SQLVarShort extends SQLVarInt {
|
|
|
555
593
|
//------------------------------------------------------
|
|
556
594
|
|
|
557
595
|
export class SQLVarInt64 extends SQLVarBase {
|
|
558
|
-
decode(data: XdrReader, lowerV13: boolean) {
|
|
559
|
-
|
|
596
|
+
decode(data: XdrReader, lowerV13: boolean, options?: NumericDecodeOptions) {
|
|
597
|
+
const mode = options?.numericMode || Const.NUMERIC_MODE_LOSSY;
|
|
598
|
+
let ret: number | string;
|
|
560
599
|
|
|
561
|
-
if (
|
|
562
|
-
ret =
|
|
600
|
+
if (mode === Const.NUMERIC_MODE_LOSSY) {
|
|
601
|
+
ret = data.readInt64();
|
|
602
|
+
if (this.scale) ret = ret / ScaleDivisor[Math.abs(this.scale)];
|
|
603
|
+
} else {
|
|
604
|
+
ret = decodeExactNumeric(data.readInt64BigInt(), this.scale, mode);
|
|
563
605
|
}
|
|
564
606
|
|
|
565
607
|
if (!lowerV13 || !data.readInt()) {
|
|
@@ -577,23 +619,11 @@ export class SQLVarInt64 extends SQLVarBase {
|
|
|
577
619
|
//------------------------------------------------------
|
|
578
620
|
|
|
579
621
|
export class SQLVarInt128 extends SQLVarBase {
|
|
580
|
-
decode(data: XdrReader, lowerV13: boolean) {
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
ret = retBigInt.toString();
|
|
586
|
-
|
|
587
|
-
var integerPart = ret.slice(0, Math.abs(this.scale) * -1)
|
|
588
|
-
var decimalPart = ret.slice(Math.abs(this.scale) * -1)
|
|
589
|
-
|
|
590
|
-
if (integerPart === '') integerPart = '0'
|
|
591
|
-
|
|
592
|
-
ret = `${integerPart}.${decimalPart}`
|
|
593
|
-
} else {
|
|
594
|
-
ret = Number(retBigInt);
|
|
595
|
-
ret = ret / ScaleDivisor[Math.abs(this.scale)];
|
|
596
|
-
}
|
|
622
|
+
decode(data: XdrReader, lowerV13: boolean, options?: NumericDecodeOptions) {
|
|
623
|
+
const mode = options?.numericMode || Const.NUMERIC_MODE_LOSSY;
|
|
624
|
+
const ret = mode === Const.NUMERIC_MODE_LOSSY
|
|
625
|
+
? decodeLossyInt128(data.readInt128(), this.scale)
|
|
626
|
+
: decodeExactNumeric(data.readInt128Signed(), this.scale, mode);
|
|
597
627
|
|
|
598
628
|
if (!lowerV13 || !data.readInt()) {
|
|
599
629
|
return ret;
|