mirror of
https://github.com/denoland/deno.git
synced 2025-02-14 17:47:35 -05:00
fix(ext/node): support read-only database in node:sqlite
(#27930)
Implements the `readOnly` option for `DatabaseSync`. Permissions: `--allow-read=test.db --allow-write=test.db` => all works `--allow-read=test.db` => only `readOnly` dbs work, cannot create new db
This commit is contained in:
parent
7107c358a1
commit
b5c3f4f782
2 changed files with 55 additions and 14 deletions
|
@ -20,6 +20,7 @@ struct DatabaseSyncOptions {
|
|||
open: bool,
|
||||
#[serde(default = "true_fn")]
|
||||
enable_foreign_key_constraints: bool,
|
||||
read_only: bool,
|
||||
}
|
||||
|
||||
fn true_fn() -> bool {
|
||||
|
@ -31,6 +32,7 @@ impl Default for DatabaseSyncOptions {
|
|||
DatabaseSyncOptions {
|
||||
open: true,
|
||||
enable_foreign_key_constraints: true,
|
||||
read_only: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -43,17 +45,31 @@ pub struct DatabaseSync {
|
|||
|
||||
impl GarbageCollected for DatabaseSync {}
|
||||
|
||||
fn check_perms(state: &mut OpState, location: &str) -> Result<(), SqliteError> {
|
||||
if location != ":memory:" {
|
||||
state
|
||||
.borrow::<PermissionsContainer>()
|
||||
.check_read_with_api_name(location, Some("node:sqlite"))?;
|
||||
state
|
||||
.borrow::<PermissionsContainer>()
|
||||
.check_write_with_api_name(location, Some("node:sqlite"))?;
|
||||
fn open_db(
|
||||
state: &mut OpState,
|
||||
readonly: bool,
|
||||
location: &str,
|
||||
) -> Result<rusqlite::Connection, SqliteError> {
|
||||
if location == ":memory:" {
|
||||
return Ok(rusqlite::Connection::open_in_memory()?);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
state
|
||||
.borrow::<PermissionsContainer>()
|
||||
.check_read_with_api_name(location, Some("node:sqlite"))?;
|
||||
|
||||
if readonly {
|
||||
return Ok(rusqlite::Connection::open_with_flags(
|
||||
location,
|
||||
rusqlite::OpenFlags::SQLITE_OPEN_READ_ONLY,
|
||||
)?);
|
||||
}
|
||||
|
||||
state
|
||||
.borrow::<PermissionsContainer>()
|
||||
.check_write_with_api_name(location, Some("node:sqlite"))?;
|
||||
|
||||
Ok(rusqlite::Connection::open(location)?)
|
||||
}
|
||||
|
||||
// Represents a single connection to a SQLite database.
|
||||
|
@ -75,9 +91,8 @@ impl DatabaseSync {
|
|||
let options = options.unwrap_or_default();
|
||||
|
||||
let db = if options.open {
|
||||
check_perms(state, &location)?;
|
||||
let db = open_db(state, options.read_only, &location)?;
|
||||
|
||||
let db = rusqlite::Connection::open(&location)?;
|
||||
if options.enable_foreign_key_constraints {
|
||||
db.execute("PRAGMA foreign_keys = ON", [])?;
|
||||
}
|
||||
|
@ -104,8 +119,7 @@ impl DatabaseSync {
|
|||
return Err(SqliteError::AlreadyOpen);
|
||||
}
|
||||
|
||||
check_perms(state, &self.location)?;
|
||||
let db = rusqlite::Connection::open(&self.location)?;
|
||||
let db = open_db(state, self.options.read_only, &self.location)?;
|
||||
if self.options.enable_foreign_key_constraints {
|
||||
db.execute("PRAGMA foreign_keys = ON", [])?;
|
||||
}
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
import { DatabaseSync } from "node:sqlite";
|
||||
import { assert, assertEquals, assertThrows } from "@std/assert";
|
||||
|
||||
const tempDir = Deno.makeTempDirSync();
|
||||
|
||||
Deno.test("[node/sqlite] in-memory databases", () => {
|
||||
const db1 = new DatabaseSync(":memory:");
|
||||
const db2 = new DatabaseSync(":memory:");
|
||||
|
@ -40,7 +42,6 @@ Deno.test(
|
|||
name: "[node/sqlite] PRAGMAs are supported",
|
||||
},
|
||||
() => {
|
||||
const tempDir = Deno.makeTempDirSync();
|
||||
const db = new DatabaseSync(`${tempDir}/test.db`);
|
||||
|
||||
assertEquals(db.prepare("PRAGMA journal_mode = WAL").get(), {
|
||||
|
@ -99,5 +100,31 @@ Deno.test({
|
|||
assertThrows(() => {
|
||||
new DatabaseSync("test.db");
|
||||
}, Deno.errors.NotCapable);
|
||||
assertThrows(() => {
|
||||
new DatabaseSync("test.db", { readOnly: true });
|
||||
}, Deno.errors.NotCapable);
|
||||
},
|
||||
});
|
||||
|
||||
Deno.test({
|
||||
name: "[node/sqlite] readOnly database",
|
||||
permissions: { read: true, write: true },
|
||||
fn() {
|
||||
{
|
||||
const db = new DatabaseSync(`${tempDir}/test3.db`);
|
||||
db.exec("CREATE TABLE foo (id INTEGER PRIMARY KEY)");
|
||||
db.close();
|
||||
}
|
||||
{
|
||||
const db = new DatabaseSync(`${tempDir}/test3.db`, { readOnly: true });
|
||||
assertThrows(
|
||||
() => {
|
||||
db.exec("CREATE TABLE test(key INTEGER PRIMARY KEY)");
|
||||
},
|
||||
Error,
|
||||
"attempt to write a readonly database",
|
||||
);
|
||||
db.close();
|
||||
}
|
||||
},
|
||||
});
|
||||
|
|
Loading…
Add table
Reference in a new issue