From 68134fa18634ac38553e69c0d205e1788d1f5e72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mika=C3=ABl=20Francoeur?= Date: Mon, 14 Jul 2025 15:27:19 -0400 Subject: [PATCH] support named bind parameters --- bindings/javascript/__test__/sync.spec.mjs | 2 +- bindings/javascript/src/lib.rs | 41 +++++++++++++++++++--- 2 files changed, 37 insertions(+), 6 deletions(-) diff --git a/bindings/javascript/__test__/sync.spec.mjs b/bindings/javascript/__test__/sync.spec.mjs index 693388951..165930b71 100644 --- a/bindings/javascript/__test__/sync.spec.mjs +++ b/bindings/javascript/__test__/sync.spec.mjs @@ -58,7 +58,7 @@ dualTest.both("Statement.run() [positional]", async (t) => { t.is(stmt2.get().email, "carol@example.net"); }); -dualTest.onlySqlitePasses("Statement.run() [named]", async (t) => { +dualTest.both("Statement.run() [named]", async (t) => { const db = t.context.db; const stmt = db.prepare("INSERT INTO users(name, email) VALUES (@name, @email);"); diff --git a/bindings/javascript/src/lib.rs b/bindings/javascript/src/lib.rs index ed18b5411..38d52aad4 100644 --- a/bindings/javascript/src/lib.rs +++ b/bindings/javascript/src/lib.rs @@ -1,7 +1,7 @@ #![deny(clippy::all)] use std::cell::{RefCell, RefMut}; -use std::num::NonZeroUsize; +use std::num::{NonZero, NonZeroUsize}; use std::rc::Rc; use std::sync::Arc; @@ -486,7 +486,7 @@ impl Statement { } /// Check if the Statement is already binded by the `bind()` method - /// and bind values do variables. The expected type for args is `Option>` + /// and bind values to variables. fn check_and_bind( &self, env: Env, @@ -507,9 +507,10 @@ impl Statement { return Err(napi::Error::from_status(napi::Status::PendingException)); } - for (i, elem) in args.into_iter().enumerate() { - let value = from_js_value(elem)?; - stmt.bind_at(NonZeroUsize::new(i + 1).unwrap(), value); + if args.len() == 1 && matches!(args[0].get_type()?, napi::ValueType::Object) { + bind_named_parameters(&mut stmt, args)?; + } else { + bind_parameters(&mut stmt, args)?; } } @@ -517,6 +518,36 @@ impl Statement { } } +fn bind_parameters( + stmt: &mut RefMut<'_, turso_core::Statement>, + args: Vec, +) -> Result<(), napi::Error> { + for (i, elem) in args.into_iter().enumerate() { + let value = from_js_value(elem)?; + stmt.bind_at(NonZeroUsize::new(i + 1).unwrap(), value); + } + Ok(()) +} + +fn bind_named_parameters( + stmt: &mut RefMut<'_, turso_core::Statement>, + args: Vec, +) -> Result<(), napi::Error> { + let obj: napi::JsObject = args.into_iter().next().unwrap().coerce_to_object()?; + for idx in 1..stmt.parameters_count() { + let non_zero_idx = NonZero::new(idx).unwrap(); + + let param = stmt.parameters().name(non_zero_idx); + let Some(name) = param else { + return Err(napi::Error::from_status(napi::Status::GenericFailure)); + }; + + let value = obj.get_named_property::(&name)?; + stmt.bind_at(non_zero_idx, from_js_value(value)?); + } + Ok(()) +} + #[napi(iterator)] pub struct IteratorStatement { stmt: Rc>,