use crate::error::AybError;
use crate::hosted_db::daemon_registry::DaemonRegistry;
use crate::hosted_db::{QueryMode, QueryResult};
use rusqlite;
use rusqlite::config::DbConfig;
use rusqlite::limits::Limit;
use rusqlite::types::ValueRef;
use std::path::PathBuf;
pub fn query_sqlite(
path: &PathBuf,
query: &str,
allow_unsafe: bool,
query_mode: QueryMode,
) -> Result<QueryResult, AybError> {
let mut open_flags =
rusqlite::OpenFlags::SQLITE_OPEN_URI | rusqlite::OpenFlags::SQLITE_OPEN_NO_MUTEX;
open_flags |= match query_mode {
QueryMode::ReadOnly => rusqlite::OpenFlags::SQLITE_OPEN_READ_ONLY,
QueryMode::ReadWrite => {
rusqlite::OpenFlags::SQLITE_OPEN_CREATE | rusqlite::OpenFlags::SQLITE_OPEN_READ_WRITE
}
};
let conn = rusqlite::Connection::open_with_flags(path, open_flags)?;
conn.pragma_update(None, "busy_timeout", 5000)?;
if matches!(query_mode, QueryMode::ReadWrite) {
let _mode: String = conn.query_row("PRAGMA journal_mode=WAL", [], |row| row.get(0))?;
conn.pragma_update(None, "synchronous", "FULL")?;
conn.pragma_update(None, "foreign_keys", true)?;
}
if !allow_unsafe {
conn.set_limit(Limit::SQLITE_LIMIT_ATTACHED, 0);
conn.db_config(DbConfig::SQLITE_DBCONFIG_DEFENSIVE)?;
}
let mut prepared = conn.prepare(query)?;
let num_columns = prepared.column_count();
let mut fields: Vec<String> = Vec::new();
for column_index in 0..num_columns {
fields.push(String::from(prepared.column_name(column_index)?))
}
let mut rows = prepared.query([])?;
let mut results: Vec<Vec<Option<String>>> = Vec::new();
while let Some(row) = rows.next().map_err(|err| match err {
rusqlite::Error::SqliteFailure(ref code, _)
if code.code == rusqlite::ErrorCode::ReadOnly && code.extended_code == 8 =>
{
AybError::NoWriteAccessError {
message: "Attempted to write to database while in read-only mode".to_string(),
}
}
_ => AybError::from(err),
})? {
let mut result: Vec<Option<String>> = Vec::new();
for column_index in 0..num_columns {
let column_value = row.get_ref(column_index)?;
result.push(match column_value {
ValueRef::Null => None,
ValueRef::Integer(i) => Some(i.to_string()),
ValueRef::Real(f) => Some(f.to_string()),
ValueRef::Text(_t) => Some(column_value.as_str()?.to_string()),
ValueRef::Blob(_b) => {
Some(std::str::from_utf8(column_value.as_bytes()?)?.to_string())
}
});
}
results.push(result);
}
Ok(QueryResult {
fields,
rows: results,
})
}
pub async fn run_sqlite_query(
daemon_registry: &DaemonRegistry,
path: &PathBuf,
query: &str,
query_mode: QueryMode,
) -> Result<QueryResult, AybError> {
daemon_registry.execute_query(path, query, query_mode).await
}