diff --git a/bindings/javascript/__test__/better-sqlite3.spec.mjs b/bindings/javascript/__test__/better-sqlite3.spec.mjs index 94f3570dd..998155562 100644 --- a/bindings/javascript/__test__/better-sqlite3.spec.mjs +++ b/bindings/javascript/__test__/better-sqlite3.spec.mjs @@ -58,6 +58,25 @@ test("Test pragma", async (t) => { t.deepEqual(typeof db.pragma("cache_size", { simple: true }), "number"); }); +test("Test bind()", async (t) => { + const [db] = await connect(":memory:"); + db.prepare("CREATE TABLE users (name TEXT, age INTEGER)").run(); + db.prepare("INSERT INTO users (name, age) VALUES (?, ?)").run("Alice", 42); + let stmt = db.prepare("SELECT * FROM users WHERE name = ?").bind("Alice"); + + for (const row of stmt.iterate()) { + t.truthy(row.name); + t.true(typeof row.age === "number"); + } + + t.throws( + () => { + db.bind("Bob"); + }, + { instanceOf: Error }, + ); +}); + const connect = async (path) => { const db = new Database(path); return [db]; diff --git a/bindings/javascript/__test__/limbo.spec.mjs b/bindings/javascript/__test__/limbo.spec.mjs index 46047ab04..9740064c5 100644 --- a/bindings/javascript/__test__/limbo.spec.mjs +++ b/bindings/javascript/__test__/limbo.spec.mjs @@ -1,5 +1,4 @@ import test from "ava"; -import fs from "fs"; import { Database } from "../wrapper.js"; @@ -72,6 +71,25 @@ test("Test pragma", async (t) => { t.true(typeof db.pragma("cache_size", { simple: true }) === "number"); }); +test("Test bind()", async (t) => { + const [db] = await connect(":memory:"); + db.prepare("CREATE TABLE users (name TEXT, age INTEGER)").run(); + db.prepare("INSERT INTO users (name, age) VALUES (?, ?)").run("Alice", 42); + let stmt = db.prepare("SELECT * FROM users WHERE name = ?").bind("Alice"); + + for (const row of stmt.iterate()) { + t.truthy(row.name); + t.true(typeof row.age === "number"); + } + + t.throws( + () => { + db.bind("Bob"); + }, + { instanceOf: Error }, + ); +}); + const connect = async (path) => { const db = new Database(path); return [db]; diff --git a/bindings/javascript/src/lib.rs b/bindings/javascript/src/lib.rs index 7d84d6cc3..7b79fef86 100644 --- a/bindings/javascript/src/lib.rs +++ b/bindings/javascript/src/lib.rs @@ -170,8 +170,8 @@ impl Database { } } -// TODO: Add the (parent) 'database' property #[napi] +#[derive(Clone)] pub struct Statement { // TODO: implement each property when core supports it // #[napi(able = false)] @@ -183,8 +183,9 @@ pub struct Statement { #[napi(writable = false)] pub source: String, - toggle: bool, database: Database, + pluck: bool, + binded: bool, inner: Rc>, } @@ -195,7 +196,8 @@ impl Statement { inner: Rc::new(inner), database, source, - toggle: false, + pluck: false, + binded: false, } } @@ -321,29 +323,49 @@ impl Statement { } #[napi] - pub fn pluck(&mut self, toggle: Option) { - if let Some(false) = toggle { - self.toggle = false; + pub fn pluck(&mut self, pluck: Option) { + if let Some(false) = pluck { + self.pluck = false; } - self.toggle = true; + self.pluck = true; } #[napi] pub fn expand() { todo!() } + #[napi] pub fn raw() { todo!() } + #[napi] pub fn columns() { todo!() } + #[napi] - pub fn bind() { - todo!() + pub fn bind(&mut self, args: Option>) -> napi::Result { + let mut stmt = self.inner.borrow_mut(); + stmt.reset(); + if self.binded { + return Err(napi::Error::new( + napi::Status::InvalidArg, + "This statement already has bound parameters", + )); + } + if let Some(args) = args { + for (i, elem) in args.into_iter().enumerate() { + let value = from_js_value(elem)?; + stmt.bind_at(NonZeroUsize::new(i + 1).unwrap(), value); + } + } + + self.binded = true; + + Ok(self.clone()) } } diff --git a/bindings/javascript/wrapper.js b/bindings/javascript/wrapper.js index 0eb18e3da..c25b7bc97 100644 --- a/bindings/javascript/wrapper.js +++ b/bindings/javascript/wrapper.js @@ -224,6 +224,16 @@ class Statement { columns() { return this.stmt.columns(); } + + /** + * Binds the given parameters to the statement _permanently_ + * + * @param bindParameters - The bind parameters for binding the statement. + * @returns this - Statement with binded parameters + */ + bind(...bindParameters) { + return this.stmt.bind(bindParameters.flat()); + } } module.exports.Database = Database;