mirror of
https://github.com/tursodatabase/limbo.git
synced 2025-08-04 18:18:03 +00:00
Merge 'bindings/javascript: Add pragma() support' from Anton Harniakou
Some checks failed
Build and push limbo-sim image / deploy (push) Waiting to run
Go Tests / test (push) Waiting to run
Java Tests / test (push) Waiting to run
JavaScript / stable - aarch64-apple-darwin - node@20 (push) Waiting to run
JavaScript / stable - x86_64-apple-darwin - node@20 (push) Waiting to run
JavaScript / stable - x86_64-pc-windows-msvc - node@20 (push) Waiting to run
JavaScript / stable - x86_64-unknown-linux-gnu - node@20 (push) Waiting to run
JavaScript / Test bindings on x86_64-apple-darwin - node@18 (push) Blocked by required conditions
JavaScript / Test bindings on x86_64-apple-darwin - node@20 (push) Blocked by required conditions
JavaScript / Test bindings on Linux-x64-gnu - node@18 (push) Blocked by required conditions
JavaScript / Test bindings on Linux-x64-gnu - node@20 (push) Blocked by required conditions
JavaScript / Build universal macOS binary (push) Blocked by required conditions
JavaScript / Publish (push) Blocked by required conditions
Python / check-requirements (push) Waiting to run
Python / configure-strategy (push) Waiting to run
Python / test (push) Blocked by required conditions
Python / lint (push) Waiting to run
Python / linux (x86_64) (push) Waiting to run
Python / macos-x86_64 (x86_64) (push) Waiting to run
Python / macos-arm64 (aarch64) (push) Waiting to run
Python / sdist (push) Waiting to run
Python / Release (push) Blocked by required conditions
Rust / cargo-fmt-check (push) Waiting to run
Rust / build-native (blacksmith-4vcpu-ubuntu-2404) (push) Waiting to run
Rust / build-native (macos-latest) (push) Waiting to run
Rust / build-native (windows-latest) (push) Waiting to run
Rust / clippy (push) Waiting to run
Rust / build-wasm (push) Waiting to run
Rust / test-limbo (push) Waiting to run
Rust / test-sqlite (push) Waiting to run
Rust Benchmarks+Nyrkiö / tpc-h (push) Waiting to run
Rust Benchmarks+Nyrkiö / bench (push) Waiting to run
Rust Benchmarks+Nyrkiö / clickbench (push) Waiting to run
Rust Benchmarks+Nyrkiö / tpc-h-criterion (push) Waiting to run
Run long fuzz tests on Btree / run-long-tests (push) Has been cancelled
Run long fuzz tests on Btree / simple-stress-test (push) Has been cancelled
Some checks failed
Build and push limbo-sim image / deploy (push) Waiting to run
Go Tests / test (push) Waiting to run
Java Tests / test (push) Waiting to run
JavaScript / stable - aarch64-apple-darwin - node@20 (push) Waiting to run
JavaScript / stable - x86_64-apple-darwin - node@20 (push) Waiting to run
JavaScript / stable - x86_64-pc-windows-msvc - node@20 (push) Waiting to run
JavaScript / stable - x86_64-unknown-linux-gnu - node@20 (push) Waiting to run
JavaScript / Test bindings on x86_64-apple-darwin - node@18 (push) Blocked by required conditions
JavaScript / Test bindings on x86_64-apple-darwin - node@20 (push) Blocked by required conditions
JavaScript / Test bindings on Linux-x64-gnu - node@18 (push) Blocked by required conditions
JavaScript / Test bindings on Linux-x64-gnu - node@20 (push) Blocked by required conditions
JavaScript / Build universal macOS binary (push) Blocked by required conditions
JavaScript / Publish (push) Blocked by required conditions
Python / check-requirements (push) Waiting to run
Python / configure-strategy (push) Waiting to run
Python / test (push) Blocked by required conditions
Python / lint (push) Waiting to run
Python / linux (x86_64) (push) Waiting to run
Python / macos-x86_64 (x86_64) (push) Waiting to run
Python / macos-arm64 (aarch64) (push) Waiting to run
Python / sdist (push) Waiting to run
Python / Release (push) Blocked by required conditions
Rust / cargo-fmt-check (push) Waiting to run
Rust / build-native (blacksmith-4vcpu-ubuntu-2404) (push) Waiting to run
Rust / build-native (macos-latest) (push) Waiting to run
Rust / build-native (windows-latest) (push) Waiting to run
Rust / clippy (push) Waiting to run
Rust / build-wasm (push) Waiting to run
Rust / test-limbo (push) Waiting to run
Rust / test-sqlite (push) Waiting to run
Rust Benchmarks+Nyrkiö / tpc-h (push) Waiting to run
Rust Benchmarks+Nyrkiö / bench (push) Waiting to run
Rust Benchmarks+Nyrkiö / clickbench (push) Waiting to run
Rust Benchmarks+Nyrkiö / tpc-h-criterion (push) Waiting to run
Run long fuzz tests on Btree / run-long-tests (push) Has been cancelled
Run long fuzz tests on Btree / simple-stress-test (push) Has been cancelled
This PR adds column names to the ouput of js pragma function. Reviewed-by: Diego Reis (@el-yawd) Reviewed-by: Jussi Saurio <jussi.saurio@gmail.com> Closes #1608
This commit is contained in:
commit
72058da9dc
7 changed files with 118 additions and 44 deletions
|
@ -84,6 +84,35 @@ test("Test pragma()", async (t) => {
|
|||
t.deepEqual(typeof db.pragma("cache_size", { simple: true }), "number");
|
||||
});
|
||||
|
||||
test("pragma query", async (t) => {
|
||||
const [db] = await connect(":memory:");
|
||||
let page_size = db.pragma("page_size");
|
||||
let expectedValue = [{page_size: 4096}];
|
||||
t.deepEqual(page_size, expectedValue);
|
||||
});
|
||||
|
||||
test("pragma table_list", async (t) => {
|
||||
const [db] = await connect(":memory:");
|
||||
let param = "sqlite_schema";
|
||||
let actual = db.pragma(`table_info(${param})`);
|
||||
let expectedValue = [
|
||||
{cid: 0, name: "type", type: "TEXT", notnull: 0, dflt_value: null, pk: 0},
|
||||
{cid: 1, name: "name", type: "TEXT", notnull: 0, dflt_value: null, pk: 0},
|
||||
{cid: 2, name: "tbl_name", type: "TEXT", notnull: 0, dflt_value: null, pk: 0},
|
||||
{cid: 3, name: "rootpage", type: "INT", notnull: 0, dflt_value: null, pk: 0},
|
||||
{cid: 4, name: "sql", type: "TEXT", notnull: 0, dflt_value: null, pk: 0},
|
||||
];
|
||||
t.deepEqual(actual, expectedValue);
|
||||
});
|
||||
|
||||
test("simple pragma table_list", async (t) => {
|
||||
const [db] = await connect(":memory:");
|
||||
let param = "sqlite_schema";
|
||||
let actual = db.pragma(`table_info(${param})`, {simple: true});
|
||||
let expectedValue = 0;
|
||||
t.deepEqual(actual, expectedValue);
|
||||
});
|
||||
|
||||
test("Statement shouldn't bind twice with bind()", async (t) => {
|
||||
const [db] = await connect(":memory:");
|
||||
db.prepare("CREATE TABLE users (name TEXT, age INTEGER)").run();
|
||||
|
|
|
@ -209,6 +209,27 @@ test("Test exec(): Should correctly load multiple statements from file", async (
|
|||
}
|
||||
});
|
||||
|
||||
test("pragma query", async (t) => {
|
||||
const [db] = await connect(":memory:");
|
||||
let page_size = db.pragma("page_size");
|
||||
let expectedValue = [{page_size: 4096}];
|
||||
t.deepEqual(page_size, expectedValue);
|
||||
});
|
||||
|
||||
test("pragma table_list", async (t) => {
|
||||
const [db] = await connect(":memory:");
|
||||
let param = "sqlite_schema";
|
||||
let actual = db.pragma(`table_info(${param})`);
|
||||
let expectedValue = [
|
||||
{cid: 0, name: "type", type: "TEXT", notnull: 0, dflt_value: null, pk: 0},
|
||||
{cid: 1, name: "name", type: "TEXT", notnull: 0, dflt_value: null, pk: 0},
|
||||
{cid: 2, name: "tbl_name", type: "TEXT", notnull: 0, dflt_value: null, pk: 0},
|
||||
{cid: 3, name: "rootpage", type: "INT", notnull: 0, dflt_value: null, pk: 0},
|
||||
{cid: 4, name: "sql", type: "TEXT", notnull: 0, dflt_value: null, pk: 0},
|
||||
];
|
||||
t.deepEqual(actual, expectedValue);
|
||||
});
|
||||
|
||||
test("Test Statement.database gets the database object", async t => {
|
||||
const [db] = await connect(":memory:");
|
||||
let stmt = db.prepare("SELECT 1");
|
||||
|
@ -222,6 +243,14 @@ test("Test Statement.source", async t => {
|
|||
t.is(stmt.source, sql);
|
||||
});
|
||||
|
||||
test("simple pragma table_list", async (t) => {
|
||||
const [db] = await connect(":memory:");
|
||||
let param = "sqlite_schema";
|
||||
let actual = db.pragma(`table_info(${param})`, {simple: true});
|
||||
let expectedValue = 0;
|
||||
t.deepEqual(actual, expectedValue);
|
||||
});
|
||||
|
||||
const connect = async (path) => {
|
||||
const db = new Database(path);
|
||||
return [db];
|
||||
|
|
|
@ -20,6 +20,11 @@ pub struct OpenDatabaseOptions {
|
|||
// verbose => Callback,
|
||||
}
|
||||
|
||||
#[napi(object)]
|
||||
pub struct PragmaOptions {
|
||||
pub simple: bool,
|
||||
}
|
||||
|
||||
#[napi(custom_finalize)]
|
||||
#[derive(Clone)]
|
||||
pub struct Database {
|
||||
|
@ -82,6 +87,36 @@ impl Database {
|
|||
Ok(Statement::new(RefCell::new(stmt), self.clone(), sql))
|
||||
}
|
||||
|
||||
#[napi]
|
||||
pub fn pragma(
|
||||
&self,
|
||||
env: Env,
|
||||
pragma_name: String,
|
||||
options: Option<PragmaOptions>,
|
||||
) -> napi::Result<JsUnknown> {
|
||||
let sql = format!("PRAGMA {}", pragma_name);
|
||||
let stmt = self.prepare(sql)?;
|
||||
match options {
|
||||
Some(PragmaOptions { simple: true, .. }) => {
|
||||
let mut stmt = stmt.inner.borrow_mut();
|
||||
match stmt.step().map_err(into_napi_error)? {
|
||||
limbo_core::StepResult::Row => {
|
||||
let row: Vec<_> = stmt.row().unwrap().get_values().cloned().collect();
|
||||
to_js_value(&env, &row[0])
|
||||
}
|
||||
limbo_core::StepResult::Done => Ok(env.get_undefined()?.into_unknown()),
|
||||
limbo_core::StepResult::IO => todo!(),
|
||||
step @ limbo_core::StepResult::Interrupt
|
||||
| step @ limbo_core::StepResult::Busy => Err(napi::Error::new(
|
||||
napi::Status::GenericFailure,
|
||||
format!("{:?}", step),
|
||||
)),
|
||||
}
|
||||
}
|
||||
_ => stmt.run(env, None),
|
||||
}
|
||||
}
|
||||
|
||||
#[napi]
|
||||
pub fn backup(&self) {
|
||||
todo!()
|
||||
|
@ -159,46 +194,6 @@ impl Database {
|
|||
self.conn.close().map_err(into_napi_error)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// We assume that every pragma only returns one result, which isn't
|
||||
// true.
|
||||
#[napi]
|
||||
pub fn pragma(&self, env: Env, pragma: String, simple: bool) -> napi::Result<JsUnknown> {
|
||||
let stmt = self.prepare(pragma.clone())?;
|
||||
let mut stmt = stmt.inner.borrow_mut();
|
||||
let pragma_name = pragma
|
||||
.split("PRAGMA")
|
||||
.find(|s| !s.trim().is_empty())
|
||||
.unwrap()
|
||||
.trim();
|
||||
|
||||
let mut results = env.create_empty_array()?;
|
||||
|
||||
let step = stmt.step().map_err(into_napi_error)?;
|
||||
match step {
|
||||
limbo_core::StepResult::Row => {
|
||||
let row = stmt.row().unwrap();
|
||||
let mut obj = env.create_object()?;
|
||||
for value in row.get_values() {
|
||||
let js_value = to_js_value(&env, value)?;
|
||||
|
||||
if simple {
|
||||
return Ok(js_value);
|
||||
}
|
||||
|
||||
obj.set_named_property(pragma_name, js_value)?;
|
||||
}
|
||||
|
||||
results.set_element(0, obj)?;
|
||||
Ok(results.into_unknown())
|
||||
}
|
||||
limbo_core::StepResult::Done => Ok(env.get_undefined()?.into_unknown()),
|
||||
limbo_core::StepResult::IO => todo!(),
|
||||
limbo_core::StepResult::Interrupt | limbo_core::StepResult::Busy => Err(
|
||||
napi::Error::new(napi::Status::GenericFailure, format!("{:?}", step)),
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
|
|
|
@ -93,8 +93,8 @@ class Database {
|
|||
const pragma = `PRAGMA ${source}`;
|
||||
|
||||
return simple
|
||||
? this.db.pragma(pragma, true)
|
||||
: this.db.pragma(pragma, false);
|
||||
? this.db.pragma(source, { simple: true })
|
||||
: this.db.pragma(source);
|
||||
}
|
||||
|
||||
backup(filename, options) {
|
||||
|
|
|
@ -789,7 +789,7 @@ impl Statement {
|
|||
}
|
||||
|
||||
pub fn get_column_name(&self, idx: usize) -> Cow<str> {
|
||||
let column = &self.program.result_columns[idx];
|
||||
let column = &self.program.result_columns.get(idx).expect("No column");
|
||||
match column.name(&self.program.table_references) {
|
||||
Some(name) => Cow::Borrowed(name),
|
||||
None => Cow::Owned(column.expr.to_string()),
|
||||
|
|
|
@ -23,7 +23,7 @@ fn list_pragmas(program: &mut ProgramBuilder) {
|
|||
let register = program.emit_string8_new_reg(x.to_string());
|
||||
program.emit_result_row(register, 1);
|
||||
}
|
||||
|
||||
program.add_pragma_result_column("pragma_list".into());
|
||||
program.epilogue(crate::translate::emitter::TransactionMode::None);
|
||||
}
|
||||
|
||||
|
@ -279,10 +279,12 @@ fn query_pragma(
|
|||
register,
|
||||
);
|
||||
program.emit_result_row(register, 1);
|
||||
program.add_pragma_result_column(pragma.to_string());
|
||||
}
|
||||
PragmaName::JournalMode => {
|
||||
program.emit_string8("wal".into(), register);
|
||||
program.emit_result_row(register, 1);
|
||||
program.add_pragma_result_column(pragma.to_string());
|
||||
}
|
||||
PragmaName::LegacyFileFormat => {}
|
||||
PragmaName::WalCheckpoint => {
|
||||
|
@ -303,6 +305,7 @@ fn query_pragma(
|
|||
dest: register,
|
||||
});
|
||||
program.emit_result_row(register, 1);
|
||||
program.add_pragma_result_column(pragma.to_string());
|
||||
}
|
||||
PragmaName::TableInfo => {
|
||||
let table = match value {
|
||||
|
@ -348,6 +351,10 @@ fn query_pragma(
|
|||
program.emit_result_row(base_reg, 6);
|
||||
}
|
||||
}
|
||||
let col_names = ["cid", "name", "type", "notnull", "dflt_value", "pk"];
|
||||
for name in col_names {
|
||||
program.add_pragma_result_column(name.into());
|
||||
}
|
||||
}
|
||||
PragmaName::UserVersion => {
|
||||
program.emit_insn(Insn::ReadCookie {
|
||||
|
@ -355,6 +362,7 @@ fn query_pragma(
|
|||
dest: register,
|
||||
cookie: Cookie::UserVersion,
|
||||
});
|
||||
program.add_pragma_result_column(pragma.to_string());
|
||||
program.emit_result_row(register, 1);
|
||||
}
|
||||
PragmaName::SchemaVersion => {
|
||||
|
@ -363,11 +371,13 @@ fn query_pragma(
|
|||
dest: register,
|
||||
cookie: Cookie::SchemaVersion,
|
||||
});
|
||||
program.add_pragma_result_column(pragma.to_string());
|
||||
program.emit_result_row(register, 1);
|
||||
}
|
||||
PragmaName::PageSize => {
|
||||
program.emit_int(database_header.lock().get_page_size().into(), register);
|
||||
program.emit_result_row(register, 1);
|
||||
program.add_pragma_result_column(pragma.to_string());
|
||||
}
|
||||
PragmaName::AutoVacuum => {
|
||||
let auto_vacuum_mode = pager.get_auto_vacuum_mode();
|
||||
|
|
|
@ -285,6 +285,17 @@ impl ProgramBuilder {
|
|||
cursor
|
||||
}
|
||||
|
||||
pub fn add_pragma_result_column(&mut self, col_name: String) {
|
||||
// TODO figure out a better type definition for ResultSetColumn
|
||||
// or invent another way to set pragma result columns
|
||||
let expr = ast::Expr::Id(ast::Id("".to_string()));
|
||||
self.result_columns.push(ResultSetColumn {
|
||||
expr,
|
||||
alias: Some(col_name),
|
||||
contains_aggregates: false,
|
||||
});
|
||||
}
|
||||
|
||||
#[instrument(skip(self), level = Level::TRACE)]
|
||||
pub fn emit_insn(&mut self, insn: Insn) {
|
||||
let function = insn.to_function();
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue