diff --git a/bindings/javascript/bind.ts b/bindings/javascript/bind.ts index 5c05ce265..2484fe51e 100644 --- a/bindings/javascript/bind.ts +++ b/bindings/javascript/bind.ts @@ -18,6 +18,10 @@ export function bindParams(stmt, params) { bindNamedParams(stmt, param); return; } + if (Array.isArray(param)) { + bindPositionalParams(stmt, [param]); + return; + } bindValue(stmt, 1, param); return; } @@ -65,4 +69,4 @@ function bindPositionalParams(stmt, params) { function bindValue(stmt, index, value) { stmt.bindAt(index, value); -} \ No newline at end of file +} diff --git a/bindings/javascript/src/lib.rs b/bindings/javascript/src/lib.rs index 1c0218eeb..64590f000 100644 --- a/bindings/javascript/src/lib.rs +++ b/bindings/javascript/src/lib.rs @@ -289,7 +289,6 @@ impl Statement { let non_zero_idx = NonZeroUsize::new(index as usize).ok_or_else(|| { Error::new(Status::InvalidArg, "Parameter index must be greater than 0") })?; - let value_type = value.get_type()?; let turso_value = match value_type { ValueType::Null => turso_core::Value::Null, @@ -320,16 +319,22 @@ impl Statement { turso_core::Value::Integer(if b { 1 } else { 0 }) } ValueType::Object => { - // Try to cast as Buffer first, fallback to string conversion - if let Ok(buffer) = unsafe { value.cast::() } { - turso_core::Value::Blob(buffer.to_vec()) + let obj = value.coerce_to_object()?; + + if obj.is_buffer()? || obj.is_typedarray()? { + let length = obj.get_named_property::("length")?; + let mut bytes = Vec::with_capacity(length as usize); + for i in 0..length { + let byte = obj.get_element::(i)?; + bytes.push(byte as u8); + } + turso_core::Value::Blob(bytes) } else { let s = value.coerce_to_string()?.into_utf8()?; turso_core::Value::Text(s.as_str()?.to_owned().into()) } } _ => { - // Fallback to string conversion for unknown types let s = value.coerce_to_string()?.into_utf8()?; turso_core::Value::Text(s.as_str()?.to_owned().into()) } @@ -537,7 +542,10 @@ fn to_js_value<'a>( } turso_core::Value::Float(f) => ToNapiValue::into_unknown(*f, env), turso_core::Value::Text(s) => ToNapiValue::into_unknown(s.as_str(), env), - turso_core::Value::Blob(b) => ToNapiValue::into_unknown(b, env), + turso_core::Value::Blob(b) => { + let buffer = Buffer::from(b.as_slice()); + ToNapiValue::into_unknown(buffer, env) + } } } diff --git a/packages/turso-serverless/src/protocol.ts b/packages/turso-serverless/src/protocol.ts index e10f52356..27a887284 100644 --- a/packages/turso-serverless/src/protocol.ts +++ b/packages/turso-serverless/src/protocol.ts @@ -147,7 +147,7 @@ export function decodeValue(value: Value, safeIntegers: boolean = false): any { for (let i = 0; i < binaryString.length; i++) { bytes[i] = binaryString.charCodeAt(i); } - return bytes; + return Buffer.from(bytes); } return null; default: diff --git a/testing/javascript/__test__/async.test.js b/testing/javascript/__test__/async.test.js index c27c1c3c0..c69aab047 100644 --- a/testing/javascript/__test__/async.test.js +++ b/testing/javascript/__test__/async.test.js @@ -290,6 +290,26 @@ test.serial("Statement.get() values", async (t) => { t.deepEqual(await stmt.get(9007199254740991n), [9007199254740991]); }); +test.serial("Statement.get() [blob]", async (t) => { + const db = t.context.db; + + // Create table with blob column + await db.exec("CREATE TABLE IF NOT EXISTS blobs (id INTEGER PRIMARY KEY, data BLOB)"); + + // Test inserting and retrieving blob data + const binaryData = Buffer.from([0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x57, 0x6f, 0x72, 0x6c, 0x64]); // "Hello World" + const insertStmt = await db.prepare("INSERT INTO blobs (data) VALUES (?)"); + await insertStmt.run([binaryData]); + + // Retrieve the blob data + const selectStmt = await db.prepare("SELECT data FROM blobs WHERE id = 1"); + const result = await selectStmt.get(); + + t.truthy(result, "Should return a result"); + t.true(Buffer.isBuffer(result.data), "Should return Buffer for blob data"); + t.deepEqual(result.data, binaryData, "Blob data should match original"); +}); + // ========================================================================== // Statement.iterate() // ========================================================================== diff --git a/testing/javascript/__test__/sync.test.js b/testing/javascript/__test__/sync.test.js index 5a839663b..3520a7f77 100644 --- a/testing/javascript/__test__/sync.test.js +++ b/testing/javascript/__test__/sync.test.js @@ -345,6 +345,26 @@ test.serial("Statement.get() values", async (t) => { t.deepEqual(stmt.get(9007199254740991n), [9007199254740991]); }); +test.serial("Statement.get() [blob]", (t) => { + const db = t.context.db; + + // Create table with blob column + db.exec("CREATE TABLE IF NOT EXISTS blobs (id INTEGER PRIMARY KEY, data BLOB)"); + + // Test inserting and retrieving blob data + const binaryData = Buffer.from([0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x57, 0x6f, 0x72, 0x6c, 0x64]); // "Hello World" + const insertStmt = db.prepare("INSERT INTO blobs (data) VALUES (?)"); + insertStmt.run([binaryData]); + + // Retrieve the blob data + const selectStmt = db.prepare("SELECT data FROM blobs WHERE id = 1"); + const result = selectStmt.get(); + + t.truthy(result, "Should return a result"); + t.true(Buffer.isBuffer(result.data), "Should return Buffer for blob data"); + t.deepEqual(result.data, binaryData, "Blob data should match original"); +}); + // ========================================================================== // Statement.iterate() // ==========================================================================