bind/js: Add support for raw() Statements

This commit is contained in:
Diego Reis
2025-05-30 15:44:34 -03:00
parent 1367b453e9
commit 0f685c5b9a
3 changed files with 142 additions and 17 deletions

View File

@@ -66,6 +66,7 @@ test("Test bind()", async (t) => {
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");
console.log(db.prepare("SELECT * FROM users").raw().get());
for (const row of stmt.iterate()) {
t.truthy(row.name);

View File

@@ -98,7 +98,7 @@ test("Test pluck(): Rows should only have the values of the first column", async
db.prepare("CREATE TABLE users (name TEXT, age INTEGER)").run();
db.prepare("INSERT INTO users (name, age) VALUES (?, ?)").run("Alice", 42);
db.prepare("INSERT INTO users (name, age) VALUES (?, ?)").run("Bob", 24);
let stmt = db.prepare("SELECT * FROM users").pluck();
let stmt = db.prepare("SELECT * FROM users").raw().pluck();
for (const row of stmt.iterate()) {
t.truthy(row.name);
@@ -106,6 +106,41 @@ test("Test pluck(): Rows should only have the values of the first column", async
}
});
test("Test raw()", 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);
db.prepare("INSERT INTO users (name, age) VALUES (?, ?)").run("Bob", 24);
// Pluck and raw should be exclusive
let stmt = db.prepare("SELECT * FROM users").pluck().raw();
for (const row of stmt.iterate()) {
t.true(Array.isArray(row));
t.true(typeof row[0] === "string");
t.true(typeof row[1] === "number");
}
stmt = db.prepare("SELECT * FROM users WHERE name = ?").raw();
const row = stmt.get("Alice");
t.true(Array.isArray(row));
t.is(row.length, 2);
t.is(row[0], "Alice");
t.is(row[1], 42);
const noRow = stmt.get("Charlie");
t.is(noRow, undefined);
stmt = db.prepare("SELECT * FROM users").raw();
const rows = stmt.all();
t.true(Array.isArray(rows));
t.is(rows.length, 2);
t.deepEqual(rows[0], ["Alice", 42]);
t.deepEqual(rows[1], ["Bob", 24]);
});
test("Test exec()", async (t) => {
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

View File

@@ -216,6 +216,7 @@ pub struct Statement {
pub source: String,
database: Database,
raw: bool,
pluck: bool,
binded: bool,
inner: Rc<RefCell<limbo_core::Statement>>,
@@ -230,6 +231,7 @@ impl Statement {
source,
pluck: false,
binded: false,
raw: false,
}
}
@@ -241,16 +243,43 @@ impl Statement {
match step {
limbo_core::StepResult::Row => {
let row = stmt.row().unwrap();
if self.raw {
assert!(!self.pluck, "Cannot use raw mode with pluck mode");
let mut raw_obj = env.create_array(row.len() as u32)?;
for (idx, value) in row.get_values().enumerate() {
let js_value = to_js_value(&env, value);
raw_obj.set(idx as u32, js_value)?;
}
return Ok(raw_obj.coerce_to_object()?.into_unknown());
}
let mut obj = env.create_object()?;
for (idx, value) in row.get_values().enumerate() {
if self.pluck {
assert!(!self.raw, "Cannot use pluck mode with raw mode");
let (idx, value) =
row.get_values().enumerate().next().ok_or(napi::Error::new(
napi::Status::GenericFailure,
"Pluck mode requires at least one column in the result",
))?;
let key = stmt.get_column_name(idx);
let js_value = to_js_value(&env, value);
obj.set_named_property(&key, js_value)?;
if self.pluck {
return Ok(obj.into_unknown());
}
return Ok(obj.into_unknown());
}
for (idx, value) in row.get_values().enumerate() {
let key = stmt.get_column_name(idx);
let js_value = to_js_value(&env, value);
obj.set_named_property(&key, js_value)?;
}
Ok(obj.into_unknown())
}
limbo_core::StepResult::Done => Ok(env.get_undefined()?.into_unknown()),
@@ -276,12 +305,20 @@ impl Statement {
args: Option<Vec<JsUnknown>>,
) -> napi::Result<IteratorStatement> {
self.check_and_bind(args)?;
if self.raw {
assert!(!self.pluck, "Cannot use raw mode with pluck mode");
}
if self.pluck {
assert!(!self.raw, "Cannot use pluck mode with raw mode");
}
Ok(IteratorStatement {
stmt: Rc::clone(&self.inner),
database: self.database.clone(),
env,
plucked: self.pluck,
pluck: self.pluck,
raw: self.raw,
})
}
@@ -304,14 +341,40 @@ impl Statement {
limbo_core::StepResult::Row => {
let row = stmt.row().unwrap();
let mut obj = env.create_object()?;
if self.raw {
assert!(!self.pluck, "Cannot use raw mode with pluck mode");
let mut raw_array = env.create_array(row.len() as u32)?;
for (idx, value) in row.get_values().enumerate() {
let js_value = to_js_value(&env, value)?;
raw_array.set(idx as u32, js_value)?;
}
results.set_element(index, raw_array.coerce_to_object()?)?;
index += 1;
continue;
}
if self.pluck {
assert!(!self.raw, "Cannot use pluck mode with raw mode");
let (idx, value) =
row.get_values().enumerate().next().ok_or(napi::Error::new(
napi::Status::GenericFailure,
"Pluck mode requires at least one column in the result",
))?;
let key = stmt.get_column_name(idx);
let js_value = to_js_value(&env, value)?;
obj.set_named_property(&key, js_value)?;
results.set_element(index, obj)?;
index += 1;
continue;
}
for (idx, value) in row.get_values().enumerate() {
let key = stmt.get_column_name(idx);
let js_value = to_js_value(&env, value);
obj.set_named_property(&key, js_value)?;
if self.pluck {
break;
}
}
results.set_element(index, obj)?;
index += 1;
@@ -340,6 +403,7 @@ impl Statement {
self.pluck = false;
}
self.raw = false;
self.pluck = true;
}
@@ -349,8 +413,13 @@ impl Statement {
}
#[napi]
pub fn raw() {
todo!()
pub fn raw(&mut self, raw: Option<bool>) {
if let Some(false) = raw {
self.raw = false;
}
self.pluck = false;
self.raw = true;
}
#[napi]
@@ -397,7 +466,8 @@ pub struct IteratorStatement {
stmt: Rc<RefCell<limbo_core::Statement>>,
database: Database,
env: Env,
plucked: bool,
pluck: bool,
raw: bool,
}
impl Generator for IteratorStatement {
@@ -415,14 +485,33 @@ impl Generator for IteratorStatement {
let row = stmt.row().unwrap();
let mut js_row = self.env.create_object().ok()?;
if self.raw {
assert!(!self.pluck, "Cannot use raw mode with pluck mode");
let mut raw_array = self.env.create_array(row.len() as u32).ok()?;
for (idx, value) in row.get_values().enumerate() {
let js_value = to_js_value(&self.env, value);
raw_array.set(idx as u32, js_value).ok()?;
}
// TODO: fix this unwrap
return Some(raw_array.coerce_to_object().unwrap());
}
if self.pluck {
assert!(!self.raw, "Cannot use pluck mode with raw mode");
let (idx, value) = row.get_values().enumerate().next()?;
let key = stmt.get_column_name(idx);
let js_value = to_js_value(&self.env, value);
js_row.set_named_property(&key, js_value).ok()?;
return Some(js_row);
}
for (idx, value) in row.get_values().enumerate() {
let key = stmt.get_column_name(idx);
let js_value = to_js_value(&self.env, value);
js_row.set_named_property(&key, js_value).ok()?;
if self.plucked {
break;
}
}
Some(js_row)