Merge 'Fix blob type handling in JavaScript' from Pekka Enberg

Closes #2732
This commit is contained in:
Pekka Enberg
2025-08-22 15:40:24 +03:00
committed by GitHub
5 changed files with 60 additions and 8 deletions

View File

@@ -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);
}
}

View File

@@ -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::<Buffer>() } {
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::<u32>("length")?;
let mut bytes = Vec::with_capacity(length as usize);
for i in 0..length {
let byte = obj.get_element::<u32>(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)
}
}
}

View File

@@ -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:

View File

@@ -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()
// ==========================================================================

View File

@@ -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()
// ==========================================================================