pub struct Sql {
pub(crate) dbfile: PathBuf,
write_mtx: Mutex<()>,
pool: RwLock<Option<Pool>>,
is_encrypted: RwLock<Option<bool>>,
pub(crate) config_cache: RwLock<HashMap<String, Option<String>>>,
}
Expand description
A wrapper around the underlying Sqlite3 object.
Fields§
§dbfile: PathBuf
Database file path
write_mtx: Mutex<()>
Write transactions mutex.
See Self::write_lock
.
pool: RwLock<Option<Pool>>
SQL connection pool.
is_encrypted: RwLock<Option<bool>>
None if the database is not open, true if it is open with passphrase and false if it is open without a passphrase.
config_cache: RwLock<HashMap<String, Option<String>>>
Cache of config
table.
Implementations§
source§impl Sql
impl Sql
async fn set_db_version(&self, version: i32) -> Result<()>
fn set_db_version_trans( transaction: &mut Transaction<'_>, version: i32 ) -> Result<()>
async fn set_db_version_in_cache(&self, version: i32) -> Result<()>
async fn execute_migration(&self, query: &str, version: i32) -> Result<()>
source§impl Sql
impl Sql
sourcepub async fn check_passphrase(&self, passphrase: String) -> Result<bool>
pub async fn check_passphrase(&self, passphrase: String) -> Result<bool>
Tests SQLCipher passphrase.
Returns true if passphrase is correct, i.e. the database is new or can be unlocked with this passphrase, and false if the database is already encrypted with another passphrase or corrupted.
Fails if database is already open.
sourcepub async fn is_open(&self) -> bool
pub async fn is_open(&self) -> bool
Checks if there is currently a connection to the underlying Sqlite database.
sourcepub(crate) async fn is_encrypted(&self) -> Option<bool>
pub(crate) async fn is_encrypted(&self) -> Option<bool>
Returns true if the database is encrypted.
If database is not open, returns None
.
sourcepub(crate) async fn import(&self, path: &Path, passphrase: String) -> Result<()>
pub(crate) async fn import(&self, path: &Path, passphrase: String) -> Result<()>
Imports the database from a separate file with the given passphrase.
async fn try_open( &self, context: &Context, dbfile: &Path, passphrase: String ) -> Result<()>
sourcepub async fn run_migrations(&self, context: &Context) -> Result<()>
pub async fn run_migrations(&self, context: &Context) -> Result<()>
Updates SQL schema to the latest version.
sourcepub async fn open(&self, context: &Context, passphrase: String) -> Result<()>
pub async fn open(&self, context: &Context, passphrase: String) -> Result<()>
Opens the provided database and runs any necessary migrations. If a database is already open, this will return an error.
sourcepub async fn change_passphrase(&self, passphrase: String) -> Result<()>
pub async fn change_passphrase(&self, passphrase: String) -> Result<()>
Changes the passphrase of encrypted database.
The database must already be encrypted and the passphrase cannot be empty. It is impossible to turn encrypted database into unencrypted and vice versa this way, use import/export for this.
sourcepub async fn write_lock(&self) -> MutexGuard<'_, ()>
pub async fn write_lock(&self) -> MutexGuard<'_, ()>
Locks the write transactions mutex in order to make sure that there never are multiple write transactions at once.
Doing the locking ourselves instead of relying on SQLite has these reasons:
- SQLite’s locking mechanism is non-async, blocking a thread
- SQLite’s locking mechanism just sleeps in a loop, which is really inefficient
More considerations on alternatives to the current approach:
We use DEFERRED transactions.
In order to never get concurrency issues, we could make all transactions IMMEDIATE, but this would mean that there can never be two simultaneous transactions.
Read transactions can simply be made DEFERRED to run in parallel w/o any drawbacks.
DEFERRED write transactions without doing the locking ourselves would have these drawbacks:
- As mentioned above, SQLite’s locking mechanism is non-async and sleeps in a loop.
- If there are other write transactions, we block the db connection until upgraded. If some reader comes then, it has to get the next, less used connection with a worse per-connection page cache (SQLite allows one write and any number of reads in parallel).
- If a transaction is blocked for more than
busy_timeout
, it fails with SQLITE_BUSY. - If upon a successful upgrade to a write transaction the db has been modified, the transaction has to be rolled back and retried, which means extra work in terms of CPU/battery.
The only pro of making write transactions DEFERRED w/o the external locking would be some parallelism between them.
Another option would be to make write transactions IMMEDIATE, also w/o the external locking. But then cons 1. - 3. above would still be valid.
sourceasync fn call<'a, F, R>(&'a self, function: F) -> Result<R>
async fn call<'a, F, R>(&'a self, function: F) -> Result<R>
Allocates a connection and calls function
with the connection. If function
does write
queries,
- either first take a lock using
write_lock()
- or use
call_write()
instead.
Returns the result of the function.
sourcepub async fn call_write<'a, F, R>(&'a self, function: F) -> Result<R>
pub async fn call_write<'a, F, R>(&'a self, function: F) -> Result<R>
Allocates a connection and calls given function, assuming it does write queries, with the connection.
Returns the result of the function.
sourcepub async fn execute(
&self,
query: &str,
params: impl Params + Send
) -> Result<usize>
pub async fn execute( &self, query: &str, params: impl Params + Send ) -> Result<usize>
Execute query
assuming it is a write query, returning the number of affected rows.
sourcepub async fn insert(
&self,
query: &str,
params: impl Params + Send
) -> Result<i64>
pub async fn insert( &self, query: &str, params: impl Params + Send ) -> Result<i64>
Executes the given query, returning the last inserted row ID.
sourcepub async fn query_map<T, F, G, H>(
&self,
sql: &str,
params: impl Params + Send,
f: F,
g: G
) -> Result<H>
pub async fn query_map<T, F, G, H>( &self, sql: &str, params: impl Params + Send, f: F, g: G ) -> Result<H>
Prepares and executes the statement and maps a function over the resulting rows. Then executes the second function over the returned iterator and returns the result of that function.
sourcepub async fn count(
&self,
query: &str,
params: impl Params + Send
) -> Result<usize>
pub async fn count( &self, query: &str, params: impl Params + Send ) -> Result<usize>
Used for executing SELECT COUNT
statements only. Returns the resulting count.
sourcepub async fn exists(
&self,
sql: &str,
params: impl Params + Send
) -> Result<bool>
pub async fn exists( &self, sql: &str, params: impl Params + Send ) -> Result<bool>
Used for executing SELECT COUNT
statements only. Returns true
, if the count is at least
one, false
otherwise.
sourcepub async fn query_row<T, F>(
&self,
query: &str,
params: impl Params + Send,
f: F
) -> Result<T>
pub async fn query_row<T, F>( &self, query: &str, params: impl Params + Send, f: F ) -> Result<T>
Execute a query which is expected to return one row.
sourcepub async fn transaction<G, H>(&self, callback: G) -> Result<H>
pub async fn transaction<G, H>(&self, callback: G) -> Result<H>
Execute the function inside a transaction assuming that it does write queries.
If the function returns an error, the transaction will be rolled back. If it does not return an error, the transaction will be committed.
sourcepub async fn table_exists(&self, name: &str) -> Result<bool>
pub async fn table_exists(&self, name: &str) -> Result<bool>
Query the database if the requested table already exists.
sourcepub async fn col_exists(&self, table_name: &str, col_name: &str) -> Result<bool>
pub async fn col_exists(&self, table_name: &str, col_name: &str) -> Result<bool>
Check if a column exists in a given table.
sourcepub async fn query_row_optional<T, F>(
&self,
sql: &str,
params: impl Params + Send,
f: F
) -> Result<Option<T>>
pub async fn query_row_optional<T, F>( &self, sql: &str, params: impl Params + Send, f: F ) -> Result<Option<T>>
Execute a query which is expected to return zero or one row.
sourcepub async fn query_get_value<T>(
&self,
query: &str,
params: impl Params + Send
) -> Result<Option<T>>where
T: FromSql + Send + 'static,
pub async fn query_get_value<T>(
&self,
query: &str,
params: impl Params + Send
) -> Result<Option<T>>where
T: FromSql + Send + 'static,
Executes a query which is expected to return one row and one
column. If the query does not return a value or returns SQL
NULL
, returns Ok(None)
.
sourcepub async fn set_raw_config(&self, key: &str, value: Option<&str>) -> Result<()>
pub async fn set_raw_config(&self, key: &str, value: Option<&str>) -> Result<()>
Set private configuration options.
Setting None
deletes the value. On failure an error message
will already have been logged.
sourcepub async fn get_raw_config(&self, key: &str) -> Result<Option<String>>
pub async fn get_raw_config(&self, key: &str) -> Result<Option<String>>
Get configuration options from the database.
sourcepub async fn set_raw_config_int(&self, key: &str, value: i32) -> Result<()>
pub async fn set_raw_config_int(&self, key: &str, value: i32) -> Result<()>
Sets configuration for the given key to 32-bit signed integer value.
sourcepub async fn get_raw_config_int(&self, key: &str) -> Result<Option<i32>>
pub async fn get_raw_config_int(&self, key: &str) -> Result<Option<i32>>
Returns 32-bit signed integer configuration value for the given key.
sourcepub async fn get_raw_config_u32(&self, key: &str) -> Result<Option<u32>>
pub async fn get_raw_config_u32(&self, key: &str) -> Result<Option<u32>>
Returns 32-bit unsigned integer configuration value for the given key.
sourcepub async fn get_raw_config_bool(&self, key: &str) -> Result<bool>
pub async fn get_raw_config_bool(&self, key: &str) -> Result<bool>
Returns boolean configuration value for the given key.
sourcepub async fn set_raw_config_bool(&self, key: &str, value: bool) -> Result<()>
pub async fn set_raw_config_bool(&self, key: &str, value: bool) -> Result<()>
Sets configuration for the given key to boolean value.