use super::row::CustomDbRow;
use crate::error::SqlMiddlewareDbError;
use crate::types::RowValues;
type ColumnCacheMap = std::sync::LazyLock<
std::sync::Mutex<
std::collections::HashMap<usize, std::sync::Arc<std::collections::HashMap<String, usize>>>,
>,
>;
#[derive(Debug, Clone, Default)]
pub struct ResultSet {
pub results: Vec<CustomDbRow>,
pub rows_affected: usize,
column_names: Option<std::sync::Arc<Vec<String>>>,
}
impl ResultSet {
#[must_use]
pub fn with_capacity(capacity: usize) -> ResultSet {
ResultSet {
results: Vec::with_capacity(capacity),
rows_affected: 0,
column_names: None,
}
}
pub fn set_column_names(&mut self, column_names: std::sync::Arc<Vec<String>>) {
self.column_names = Some(column_names);
}
#[must_use]
pub fn get_column_names(&self) -> Option<&std::sync::Arc<Vec<String>>> {
self.column_names.as_ref()
}
pub fn add_row_values(&mut self, row_values: Vec<RowValues>) {
if let Some(column_names) = &self.column_names {
static CACHE_MAP: ColumnCacheMap =
std::sync::LazyLock::new(
|| std::sync::Mutex::new(std::collections::HashMap::new()),
);
let ptr = column_names.as_ref().as_ptr() as usize;
let cache = {
let mut cache_map = match CACHE_MAP.lock() {
Ok(guard) => guard,
Err(poisoned) => {
poisoned.into_inner()
}
};
let cache_entry = cache_map.entry(ptr).or_insert_with(|| {
std::sync::Arc::new(
column_names
.iter()
.enumerate()
.map(|(i, name)| (name.clone(), i))
.collect::<std::collections::HashMap<_, _>>(),
)
});
cache_entry.clone()
};
let row = CustomDbRow {
column_names: column_names.clone(),
rows: row_values,
column_index_cache: cache,
};
self.results.push(row);
self.rows_affected += 1;
}
}
pub fn add_row(&mut self, row: CustomDbRow) {
if self.column_names.is_none() {
self.column_names = Some(row.column_names.clone());
}
self.results.push(row);
self.rows_affected += 1;
}
#[must_use]
pub fn into_optional(mut self) -> Option<CustomDbRow> {
if self.results.is_empty() {
None
} else {
Some(self.results.remove(0))
}
}
pub fn into_one(self) -> Result<CustomDbRow, SqlMiddlewareDbError> {
self.into_optional().ok_or_else(|| {
SqlMiddlewareDbError::ExecutionError("query returned no rows".to_string())
})
}
pub fn map_optional<T, F>(self, mapper: F) -> Result<Option<T>, SqlMiddlewareDbError>
where
F: FnOnce(&CustomDbRow) -> Result<T, SqlMiddlewareDbError>,
{
self.into_optional().map(|row| mapper(&row)).transpose()
}
pub fn map_one<T, F>(self, mapper: F) -> Result<T, SqlMiddlewareDbError>
where
F: FnOnce(&CustomDbRow) -> Result<T, SqlMiddlewareDbError>,
{
let row = self.into_one()?;
mapper(&row)
}
}