From 6b59bcd51eae7618b8f7026f9eccf5dd183e1caf Mon Sep 17 00:00:00 2001 From: Pekka Enberg Date: Tue, 19 Aug 2025 14:00:01 +0300 Subject: [PATCH] javascript: Fix Statement.get() for boundary values --- bindings/javascript/src/lib.rs | 9 ++++++++- packages/turso-serverless/src/protocol.ts | 12 ++++++++++-- testing/javascript/__test__/async.test.js | 11 +++++++++++ testing/javascript/__test__/sync.test.js | 2 +- 4 files changed, 30 insertions(+), 4 deletions(-) diff --git a/bindings/javascript/src/lib.rs b/bindings/javascript/src/lib.rs index 7e7aa1ef8..009dcd70c 100644 --- a/bindings/javascript/src/lib.rs +++ b/bindings/javascript/src/lib.rs @@ -281,12 +281,19 @@ impl Statement { ValueType::Null => turso_core::Value::Null, ValueType::Number => { let n: f64 = unsafe { value.cast()? }; - if n.fract() == 0.0 { + if n.fract() == 0.0 && n >= i64::MIN as f64 && n <= i64::MAX as f64 { turso_core::Value::Integer(n as i64) } else { turso_core::Value::Float(n) } } + ValueType::BigInt => { + let bigint_str = value.coerce_to_string()?.into_utf8()?.as_str()?.to_owned(); + let bigint_value = bigint_str.parse::().map_err(|e| { + Error::new(Status::NumberExpected, format!("Failed to parse BigInt: {e}")) + })?; + turso_core::Value::Integer(bigint_value) + } ValueType::String => { let s = value.coerce_to_string()?.into_utf8()?; turso_core::Value::Text(s.as_str()?.to_owned().into()) diff --git a/packages/turso-serverless/src/protocol.ts b/packages/turso-serverless/src/protocol.ts index 1c7654b39..a7d5c25f3 100644 --- a/packages/turso-serverless/src/protocol.ts +++ b/packages/turso-serverless/src/protocol.ts @@ -89,12 +89,20 @@ export function encodeValue(value: any): Value { } if (typeof value === 'number') { - if (Number.isInteger(value)) { - return { type: 'integer', value: value.toString() }; + if (!Number.isFinite(value)) { + throw new Error("Only finite numbers (not Infinity or NaN) can be passed as arguments"); } return { type: 'float', value }; } + if (typeof value === 'bigint') { + return { type: 'integer', value: value.toString() }; + } + + if (typeof value === 'boolean') { + return { type: 'integer', value: value ? '1' : '0' }; + } + if (typeof value === 'string') { return { type: 'text', value }; } diff --git a/testing/javascript/__test__/async.test.js b/testing/javascript/__test__/async.test.js index 0e1a5bcb3..69acf4b4e 100644 --- a/testing/javascript/__test__/async.test.js +++ b/testing/javascript/__test__/async.test.js @@ -279,6 +279,17 @@ test.serial("Statement.get() [raw]", async (t) => { t.deepEqual(await stmt.raw().get(1), [1, "Alice", "alice@example.org"]); }); +test.serial("Statement.get() values", async (t) => { + const db = t.context.db; + + const stmt = (await db.prepare("SELECT ?")).raw(); + t.deepEqual(await stmt.get(1), [1]); + t.deepEqual(await stmt.get(Number.MIN_VALUE), [Number.MIN_VALUE]); + t.deepEqual(await stmt.get(Number.MAX_VALUE), [Number.MAX_VALUE]); + t.deepEqual(await stmt.get(Number.MAX_SAFE_INTEGER), [Number.MAX_SAFE_INTEGER]); + t.deepEqual(await stmt.get(9007199254740991n), [9007199254740991]); +}); + // ========================================================================== // Statement.iterate() // ========================================================================== diff --git a/testing/javascript/__test__/sync.test.js b/testing/javascript/__test__/sync.test.js index 4d27f4217..8339ab844 100644 --- a/testing/javascript/__test__/sync.test.js +++ b/testing/javascript/__test__/sync.test.js @@ -334,7 +334,7 @@ test.serial("Statement.get() [raw]", async (t) => { t.deepEqual(stmt.raw().get(1), [1, "Alice", "alice@example.org"]); }); -test.skip("Statement.get() values", async (t) => { +test.serial("Statement.get() values", async (t) => { const db = t.context.db; const stmt = db.prepare("SELECT ?").raw();