diff --git a/bindings/javascript/__test__/better-sqlite3.spec.mjs b/bindings/javascript/__test__/better-sqlite3.spec.mjs index c481126f4..370123a5b 100644 --- a/bindings/javascript/__test__/better-sqlite3.spec.mjs +++ b/bindings/javascript/__test__/better-sqlite3.spec.mjs @@ -22,6 +22,16 @@ test("Property .name of database", async (t) => { t.is(db.name,name); }); +test("Property .readonly of database if set", async (t) => { + const db = new Database("foobar.db", { readonly: true }); + t.is(db.readonly, true); +}); + +test("Property .readonly of database if not set", async (t) => { + const db = new Database("foobar.db"); + t.is(db.readonly, false); +}); + test("Statement.get() returns data", async (t) => { const [db] = await connect(":memory:"); const stmt = db.prepare("SELECT 1"); diff --git a/bindings/javascript/src/lib.rs b/bindings/javascript/src/lib.rs index 1dc1a9230..4f389a8e0 100644 --- a/bindings/javascript/src/lib.rs +++ b/bindings/javascript/src/lib.rs @@ -11,6 +11,7 @@ use napi::iterator::Generator; use napi::{bindgen_prelude::ObjectFinalize, Env, JsUnknown}; use napi_derive::napi; +#[derive(Default)] #[napi(object)] pub struct OpenDatabaseOptions { pub readonly: bool, @@ -30,9 +31,8 @@ pub struct Database { #[napi(writable = false)] pub memory: bool, - // TODO: implement each property - // #[napi(writable = false)] - // pub readonly: bool, + #[napi(writable = false)] + pub readonly: bool, // #[napi(writable = false)] // pub in_transaction: bool, // #[napi(writable = false)] @@ -55,7 +55,7 @@ impl ObjectFinalize for Database { #[napi] impl Database { #[napi(constructor)] - pub fn new(path: String, _options: Option) -> napi::Result { + pub fn new(path: String, options: Option) -> napi::Result { let memory = path == ":memory:"; let io: Arc = if memory { Arc::new(limbo_core::MemoryIO::new()) @@ -65,12 +65,22 @@ impl Database { let file = io .open_file(&path, limbo_core::OpenFlags::Create, false) .map_err(into_napi_error)?; + + let opts = options.unwrap_or_default(); + + let flag = if opts.readonly { + limbo_core::OpenFlags::ReadOnly + } else { + limbo_core::OpenFlags::Create + }; + let db_file = Arc::new(DatabaseFile::new(file)); let db = limbo_core::Database::open(io.clone(), &path, db_file, false) .map_err(into_napi_error)?; let conn = db.connect().map_err(into_napi_error)?; Ok(Self { + readonly: opts.readonly, memory, _db: db, conn, @@ -115,6 +125,11 @@ impl Database { } } + #[napi] + pub fn readonly(&self) -> bool { + self.readonly + } + #[napi] pub fn backup(&self) { todo!() diff --git a/bindings/javascript/wrapper.js b/bindings/javascript/wrapper.js index 5b5dd2342..dea2d9042 100644 --- a/bindings/javascript/wrapper.js +++ b/bindings/javascript/wrapper.js @@ -11,11 +11,20 @@ class Database { * * @constructor * @param {string} path - Path to the database file. + * @param {Object} opts - Options for database behavior. + * @param {boolean} [opts.readonly=false] - Open the database in read-only mode. + * @param {boolean} [opts.fileMustExist=false] - If true, throws if database file does not exist. + * @param {number} [opts.timeout=0] - Timeout duration in milliseconds for database operations. Defaults to 0 (no timeout). */ - constructor(path, opts) { + constructor(path, opts = {}) { + opts.readonly = opts.readonly === undefined ? false : opts.readonly; + opts.fileMustExist = opts.fileMustExist === undefined ? false : opts.fileMustExist; + opts.timeout = opts.timeout === undefined ? 0 : opts.timeout; + this.db = new NativeDB(path, opts); this.memory = this.db.memory; const db = this.db; + Object.defineProperties(this, { inTransaction: { get() { @@ -27,6 +36,11 @@ class Database { return path; }, }, + readonly: { + get() { + return opts.readonly; + }, + }, }); }