bind/js: Add proper exec() method

This commit is contained in:
Diego Reis 2025-05-29 15:59:07 -03:00
parent 482eb4aa9a
commit 1367b453e9
4 changed files with 72 additions and 2 deletions

View file

@ -0,0 +1,3 @@
CREATE TABLE users (name TEXT, age INTEGER);
INSERT INTO users (name, age) VALUES ('Bob', 24);
INSERT INTO users (name, age) VALUES ('Alice', 42);

View file

@ -1,4 +1,7 @@
import test from "ava";
import fs from "node:fs";
import { fileURLToPath } from "url";
import path from "node:path"
import Database from "better-sqlite3";
@ -77,6 +80,21 @@ test("Test bind()", async (t) => {
);
});
test("Test exec()", async (t) => {
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const [db] = await connect(":memory:");
const file = fs.readFileSync(path.resolve(__dirname, "./artifacts/basic-test.sql"), "utf8");
db.exec(file);
let rows = db.prepare("SELECT * FROM users").iterate();
for (const row of rows) {
t.truthy(row.name);
t.true(typeof row.age === "number");
}
});
const connect = async (path) => {
const db = new Database(path);
return [db];

View file

@ -1,4 +1,7 @@
import test from "ava";
import fs from "node:fs";
import { fileURLToPath } from "url";
import path from "node:path";
import { Database } from "../wrapper.js";
@ -103,6 +106,20 @@ test("Test pluck(): Rows should only have the values of the first column", async
}
});
test("Test exec()", async (t) => {
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const [db] = await connect(":memory:");
const file = fs.readFileSync(path.resolve(__dirname, "./artifacts/basic-test.sql"), "utf8");
db.exec(file);
let rows = db.prepare("SELECT * FROM users").iterate();
for (const row of rows) {
t.truthy(row.name);
t.true(typeof row.age === "number");
}
});
const connect = async (path) => {
const db = new Database(path);
return [db];

View file

@ -7,7 +7,7 @@ use std::rc::Rc;
use std::sync::Arc;
use limbo_core::types::Text;
use limbo_core::{maybe_init_database_file, LimboError};
use limbo_core::{maybe_init_database_file, LimboError, StepResult};
use napi::iterator::Generator;
use napi::JsObject;
use napi::{bindgen_prelude::ObjectFinalize, Env, JsUnknown};
@ -119,7 +119,39 @@ impl Database {
#[napi]
pub fn exec(&self, sql: String) -> napi::Result<()> {
self.conn.execute(sql).map_err(into_napi_error)?;
let query_runner = self.conn.query_runner(sql.as_bytes());
// Since exec doesn't return any values, we can just iterate over the results
for output in query_runner {
match output {
Ok(Some(mut stmt)) => loop {
match stmt.step() {
Ok(StepResult::Row) => continue,
Ok(StepResult::IO) => self.io.run_once().map_err(into_napi_error)?,
Ok(StepResult::Done) => break,
Ok(StepResult::Interrupt | StepResult::Busy) => {
return Err(napi::Error::new(
napi::Status::GenericFailure,
"Statement execution interrupted or busy".to_string(),
));
}
Err(err) => {
return Err(napi::Error::new(
napi::Status::GenericFailure,
format!("Error executing SQL: {}", err),
));
}
}
},
Ok(None) => continue,
Err(err) => {
return Err(napi::Error::new(
napi::Status::GenericFailure,
format!("Error executing SQL: {}", err),
));
}
}
}
Ok(())
}