use std::collections::VecDeque;
use std::sync::Arc;
use std::time::Instant;
use parking_lot::Mutex;
use tokio::sync::OwnedSemaphorePermit;
use tokio_rusqlite::Connection;
use tracing::trace;
use crate::error::{SqliteError, SqliteResult};
pub(crate) struct PooledConnection {
pub conn: Connection,
pub created_at: Instant,
pub last_used: Instant,
}
impl PooledConnection {
pub fn new(conn: Connection) -> Self {
let now = Instant::now();
Self {
conn,
created_at: now,
last_used: now,
}
}
}
pub struct SqliteConnection {
conn: Option<Connection>,
#[allow(dead_code)]
permit: OwnedSemaphorePermit,
return_to_pool: Option<Arc<Mutex<VecDeque<PooledConnection>>>>,
created_at: Instant,
}
impl SqliteConnection {
pub fn new(conn: Connection, permit: OwnedSemaphorePermit) -> Self {
Self {
conn: Some(conn),
permit,
return_to_pool: None,
created_at: Instant::now(),
}
}
pub(crate) fn new_pooled(
conn: Connection,
permit: OwnedSemaphorePermit,
return_to_pool: Option<Arc<Mutex<VecDeque<PooledConnection>>>>,
) -> Self {
Self {
conn: Some(conn),
permit,
return_to_pool,
created_at: Instant::now(),
}
}
fn conn(&self) -> &Connection {
self.conn.as_ref().expect("Connection already taken")
}
pub async fn query(&self, sql: &str) -> SqliteResult<Vec<serde_json::Value>> {
let sql = sql.to_string();
trace!(sql = %sql, "Executing query");
self.conn()
.call(move |conn| {
let mut stmt = conn.prepare(&sql)?;
let columns: Vec<String> =
stmt.column_names().iter().map(|s| s.to_string()).collect();
let rows = stmt.query_map([], |row| {
let mut map = serde_json::Map::new();
for (i, col) in columns.iter().enumerate() {
let value = crate::types::get_value_at_index(row, i);
map.insert(col.clone(), value);
}
Ok(serde_json::Value::Object(map))
})?;
let results: Result<Vec<_>, _> = rows.collect();
Ok(results?)
})
.await
.map_err(SqliteError::from)
}
pub async fn query_params(
&self,
sql: &str,
params: Vec<rusqlite::types::Value>,
) -> SqliteResult<Vec<serde_json::Value>> {
let sql = sql.to_string();
trace!(sql = %sql, "Executing parameterized query");
self.conn()
.call(move |conn| {
let mut stmt = conn.prepare(&sql)?;
let columns: Vec<String> =
stmt.column_names().iter().map(|s| s.to_string()).collect();
let params_ref: Vec<&dyn rusqlite::ToSql> =
params.iter().map(|v| v as &dyn rusqlite::ToSql).collect();
let rows = stmt.query_map(params_ref.as_slice(), |row| {
let mut map = serde_json::Map::new();
for (i, col) in columns.iter().enumerate() {
let value = crate::types::get_value_at_index(row, i);
map.insert(col.clone(), value);
}
Ok(serde_json::Value::Object(map))
})?;
let results: Result<Vec<_>, _> = rows.collect();
Ok(results?)
})
.await
.map_err(SqliteError::from)
}
pub async fn query_one(&self, sql: &str) -> SqliteResult<serde_json::Value> {
let sql = sql.to_string();
trace!(sql = %sql, "Executing query_one");
self.conn()
.call(move |conn| {
let mut stmt = conn.prepare(&sql)?;
let columns: Vec<String> =
stmt.column_names().iter().map(|s| s.to_string()).collect();
Ok(stmt.query_row([], |row| {
let mut map = serde_json::Map::new();
for (i, col) in columns.iter().enumerate() {
let value = crate::types::get_value_at_index(row, i);
map.insert(col.clone(), value);
}
Ok(serde_json::Value::Object(map))
})?)
})
.await
.map_err(SqliteError::from)
}
pub async fn query_optional(&self, sql: &str) -> SqliteResult<Option<serde_json::Value>> {
let sql = sql.to_string();
trace!(sql = %sql, "Executing query_optional");
self.conn()
.call(move |conn| {
let mut stmt = conn.prepare(&sql)?;
let columns: Vec<String> =
stmt.column_names().iter().map(|s| s.to_string()).collect();
let result = stmt.query_row([], |row| {
let mut map = serde_json::Map::new();
for (i, col) in columns.iter().enumerate() {
let value = crate::types::get_value_at_index(row, i);
map.insert(col.clone(), value);
}
Ok(serde_json::Value::Object(map))
});
match result {
Ok(row) => Ok(Some(row)),
Err(rusqlite::Error::QueryReturnedNoRows) => Ok(None),
Err(e) => Err(tokio_rusqlite::Error::Rusqlite(e)),
}
})
.await
.map_err(SqliteError::from)
}
pub async fn execute(&self, sql: &str) -> SqliteResult<usize> {
let sql = sql.to_string();
trace!(sql = %sql, "Executing statement");
self.conn()
.call(move |conn| Ok(conn.execute(&sql, [])?))
.await
.map_err(SqliteError::from)
}
pub async fn execute_params(
&self,
sql: &str,
params: Vec<rusqlite::types::Value>,
) -> SqliteResult<usize> {
let sql = sql.to_string();
trace!(sql = %sql, "Executing parameterized statement");
self.conn()
.call(move |conn| {
let params_ref: Vec<&dyn rusqlite::ToSql> =
params.iter().map(|v| v as &dyn rusqlite::ToSql).collect();
Ok(conn.execute(&sql, params_ref.as_slice())?)
})
.await
.map_err(SqliteError::from)
}
pub async fn execute_insert(&self, sql: &str) -> SqliteResult<i64> {
let sql = sql.to_string();
trace!(sql = %sql, "Executing insert");
self.conn()
.call(move |conn| {
conn.execute(&sql, [])?;
Ok(conn.last_insert_rowid())
})
.await
.map_err(SqliteError::from)
}
pub async fn execute_insert_params(
&self,
sql: &str,
params: Vec<rusqlite::types::Value>,
) -> SqliteResult<i64> {
let sql = sql.to_string();
trace!(sql = %sql, "Executing parameterized insert");
self.conn()
.call(move |conn| {
let params_ref: Vec<&dyn rusqlite::ToSql> =
params.iter().map(|v| v as &dyn rusqlite::ToSql).collect();
conn.execute(&sql, params_ref.as_slice())?;
Ok(conn.last_insert_rowid())
})
.await
.map_err(SqliteError::from)
}
pub async fn execute_batch(&self, sql: &str) -> SqliteResult<()> {
let sql = sql.to_string();
trace!(sql = %sql, "Executing batch");
self.conn()
.call(move |conn| Ok(conn.execute_batch(&sql)?))
.await
.map_err(SqliteError::from)
}
pub fn inner(&self) -> &Connection {
self.conn()
}
}
impl Drop for SqliteConnection {
fn drop(&mut self) {
if let Some(pool) = self.return_to_pool.take() {
if let Some(conn) = self.conn.take() {
trace!("Returning connection to pool");
let mut idle: parking_lot::MutexGuard<'_, VecDeque<PooledConnection>> = pool.lock();
idle.push_back(PooledConnection {
conn,
created_at: self.created_at,
last_used: Instant::now(),
});
}
}
}
}