Skip to content

Commit 982ddef

Browse files
committed
libsql: add unsafe Database::connect_raw
This adds a `connect_raw` function that can be called in an unsafe manner. This will bypass `libsql`'s safety checks that enforce that sqlite3's serialized thread safety mode is enabled. This is a use at your own risk type of function.
1 parent e10af22 commit 982ddef

File tree

2 files changed

+119
-2
lines changed

2 files changed

+119
-2
lines changed

libsql/src/database.rs

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -666,6 +666,91 @@ impl Database {
666666
_ => unreachable!("no database type set"),
667667
}
668668
}
669+
670+
/// Connect to a database without safety checks. Using `connect` will enforce that sqlite is
671+
/// compiled and configured correctly. This can conflict if you have other sqlite3 processes
672+
/// due to sqlite3's global config. This function allows a user to bypass those checks in
673+
/// exchanged for the user enforcing that sqlite3 is compiled AND enabled for serailized thread
674+
/// saftey mode.
675+
///
676+
/// SAFTEY: Users must ensure that sqlite3 has SERIALIZED thread safe mode enabled or else this
677+
/// will produce UBs.
678+
#[allow(unreachable_patterns)]
679+
pub unsafe fn connect_raw(&self) -> Result<Connection> {
680+
match &self.db_type {
681+
#[cfg(feature = "core")]
682+
DbType::Memory { db: _ } => {
683+
unimplemented!("memory dbs are not supported in connect_raw")
684+
}
685+
686+
#[cfg(feature = "core")]
687+
DbType::File {
688+
path,
689+
flags,
690+
encryption_config,
691+
} => {
692+
use crate::local::impls::LibsqlConnection;
693+
694+
let db = crate::local::Database::open_raw(path, *flags)?;
695+
let conn = db.connect()?;
696+
697+
if !cfg!(feature = "encryption") && encryption_config.is_some() {
698+
return Err(crate::Error::Misuse(
699+
"Encryption is not enabled: enable the `encryption` feature in order to enable encryption-at-rest".to_string(),
700+
));
701+
}
702+
703+
#[cfg(feature = "encryption")]
704+
if let Some(cfg) = encryption_config {
705+
if unsafe {
706+
libsql_sys::connection::set_encryption_cipher(conn.raw, cfg.cipher_id())
707+
} == -1
708+
{
709+
return Err(crate::Error::Misuse(
710+
"failed to set encryption cipher".to_string(),
711+
));
712+
}
713+
if unsafe {
714+
libsql_sys::connection::set_encryption_key(conn.raw, &cfg.encryption_key)
715+
} != crate::ffi::SQLITE_OK
716+
{
717+
return Err(crate::Error::Misuse(
718+
"failed to set encryption key".to_string(),
719+
));
720+
}
721+
}
722+
723+
let conn = std::sync::Arc::new(LibsqlConnection { conn });
724+
725+
Ok(Connection { conn })
726+
}
727+
728+
#[cfg(feature = "replication")]
729+
DbType::Sync {
730+
db: _,
731+
encryption_config: _,
732+
} => {
733+
unimplemented!("embedded replica is not supported in connect_raw")
734+
}
735+
736+
#[cfg(feature = "sync")]
737+
DbType::Offline { db: _ } => {
738+
unimplemented!("offline sync not supported in connect_raw")
739+
}
740+
741+
#[cfg(feature = "remote")]
742+
DbType::Remote {
743+
url: _,
744+
auth_token: _,
745+
connector: _,
746+
version: _,
747+
} => {
748+
unimplemented!("remote connections are not supported in connect_raw")
749+
}
750+
751+
_ => unreachable!("no database type set"),
752+
}
753+
}
669754
}
670755

671756
#[cfg(any(

libsql/src/local/database.rs

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,30 @@ impl Database {
5353
}
5454
}
5555

56+
/// Safety: this is like `open` but does not enfoce that sqlite_config has THREADSAFE set to
57+
/// `SQLITE_CONFIG_SERIALIZED`, calling
58+
pub unsafe fn open_raw<S: Into<String>>(db_path: S, flags: OpenFlags) -> Result<Database> {
59+
let db_path = db_path.into();
60+
61+
if db_path.starts_with("libsql:")
62+
|| db_path.starts_with("http:")
63+
|| db_path.starts_with("https:")
64+
{
65+
Err(ConnectionFailed(format!(
66+
"Unable to open local database {db_path} with Database::open()"
67+
)))
68+
} else {
69+
Ok(Database {
70+
db_path,
71+
flags,
72+
#[cfg(feature = "replication")]
73+
replication_ctx: None,
74+
#[cfg(feature = "sync")]
75+
sync_ctx: None,
76+
})
77+
}
78+
}
79+
5680
#[cfg(feature = "replication")]
5781
pub async fn open_http_sync(
5882
connector: crate::util::ConnectorService,
@@ -420,7 +444,11 @@ impl Database {
420444
}
421445

422446
#[cfg(feature = "sync")]
423-
async fn try_push(&self, sync_ctx: &mut SyncContext, conn: &Connection) -> Result<crate::database::Replicated> {
447+
async fn try_push(
448+
&self,
449+
sync_ctx: &mut SyncContext,
450+
conn: &Connection,
451+
) -> Result<crate::database::Replicated> {
424452
let page_size = {
425453
let rows = conn
426454
.query("PRAGMA page_size", crate::params::Params::None)?
@@ -471,7 +499,11 @@ impl Database {
471499
}
472500

473501
#[cfg(feature = "sync")]
474-
async fn try_pull(&self, sync_ctx: &mut SyncContext, conn: &Connection) -> Result<crate::database::Replicated> {
502+
async fn try_pull(
503+
&self,
504+
sync_ctx: &mut SyncContext,
505+
conn: &Connection,
506+
) -> Result<crate::database::Replicated> {
475507
let generation = sync_ctx.generation();
476508
let mut frame_no = sync_ctx.durable_frame_num() + 1;
477509
conn.wal_insert_begin()?;

0 commit comments

Comments
 (0)