pub mod abuse;
pub mod agents;
pub mod ann;
pub mod approvals;
pub mod backend;
pub mod cache;
pub mod checkpoint;
pub mod cron;
pub mod delegation;
pub mod delivery_queue;
pub mod efficiency;
pub mod embeddings;
mod ext;
pub use ext::*;
pub mod hippocampus;
pub mod hygiene_log;
pub mod learned_skills;
pub mod memory;
pub mod memory_index;
pub mod metrics;
pub mod model_selection;
pub mod policy;
pub mod revenue_accounting;
pub mod revenue_feedback;
pub mod revenue_introspection;
pub mod revenue_opportunity_queries;
pub mod revenue_scoring;
pub mod revenue_strategy_summary;
pub mod revenue_swap_tasks;
pub mod revenue_tax_tasks;
pub mod routing_dataset;
pub mod schema;
pub mod service_revenue;
pub mod sessions;
pub mod shadow_routing;
pub mod skills;
pub mod task_events;
pub mod tasks;
pub mod tool_embeddings;
pub mod tools;
pub mod traces;
pub mod treasury;
pub use rusqlite::params_from_iter;
use roboticus_core::Result;
const POOL_SIZE: u32 = 8;
const POOL_TIMEOUT_SECS: u64 = 5;
pub type PooledConnection = r2d2::PooledConnection<r2d2_sqlite::SqliteConnectionManager>;
#[derive(Clone)]
pub struct Database {
pool: r2d2::Pool<r2d2_sqlite::SqliteConnectionManager>,
}
impl Database {
pub fn new(path: &str) -> Result<Self> {
let is_memory = path == ":memory:";
let manager = if is_memory {
r2d2_sqlite::SqliteConnectionManager::memory()
} else {
r2d2_sqlite::SqliteConnectionManager::file(path)
};
let manager = manager.with_init(|conn| {
conn.execute_batch(
"PRAGMA journal_mode=WAL; \
PRAGMA foreign_keys=ON; \
PRAGMA synchronous=NORMAL; \
PRAGMA auto_vacuum=INCREMENTAL;",
)?;
Ok(())
});
let size = if is_memory { 1 } else { POOL_SIZE };
let pool = r2d2::Pool::builder()
.max_size(size)
.connection_timeout(std::time::Duration::from_secs(POOL_TIMEOUT_SECS))
.build(manager)
.map_err(|e| {
roboticus_core::RoboticusError::Database(format!(
"failed to create connection pool: {e}"
))
})?;
{
let conn = pool.get().map_err(|e| {
roboticus_core::RoboticusError::Database(format!("pool get failed: {e}"))
})?;
let current_auto_vacuum: i64 = conn
.query_row("PRAGMA auto_vacuum", [], |row| row.get(0))
.unwrap_or(0);
if current_auto_vacuum == 0 {
let _ = conn.execute_batch("PRAGMA auto_vacuum=INCREMENTAL; VACUUM;");
}
}
let db = Self { pool };
schema::initialize_db(&db)?;
Ok(db)
}
pub fn conn(&self) -> PooledConnection {
self.pool.get().unwrap_or_else(|e| {
panic!("database connection pool exhausted: {e}");
})
}
}
impl std::fmt::Debug for Database {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Database").finish()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn database_debug_impl() {
let db = Database::new(":memory:").expect("in-memory db");
let s = format!("{:?}", db);
assert_eq!(s, "Database");
}
#[test]
fn database_new_in_memory() {
let db = Database::new(":memory:").expect("in-memory db");
let _guard = db.conn();
}
#[test]
fn database_new_invalid_path_returns_error() {
let result = Database::new("/");
assert!(result.is_err(), "opening \"/\" as database should fail");
}
}