sql_middleware/results/
result_set.rs1use super::row::CustomDbRow;
2use crate::error::SqlMiddlewareDbError;
3use crate::types::RowValues;
4
5type ColumnCacheMap = std::sync::LazyLock<
6 std::sync::Mutex<
7 std::collections::HashMap<usize, std::sync::Arc<std::collections::HashMap<String, usize>>>,
8 >,
9>;
10
11#[derive(Debug, Clone, Default)]
16pub struct ResultSet {
17 pub results: Vec<CustomDbRow>,
19 pub rows_affected: usize,
21 column_names: Option<std::sync::Arc<Vec<String>>>,
23}
24
25impl ResultSet {
26 #[must_use]
36 pub fn with_capacity(capacity: usize) -> ResultSet {
37 ResultSet {
38 results: Vec::with_capacity(capacity),
39 rows_affected: 0,
40 column_names: None,
41 }
42 }
43
44 pub fn set_column_names(&mut self, column_names: std::sync::Arc<Vec<String>>) {
46 self.column_names = Some(column_names);
47 }
48
49 #[must_use]
51 pub fn get_column_names(&self) -> Option<&std::sync::Arc<Vec<String>>> {
52 self.column_names.as_ref()
53 }
54
55 pub fn add_row_values(&mut self, row_values: Vec<RowValues>) {
61 if let Some(column_names) = &self.column_names {
62 static CACHE_MAP: ColumnCacheMap =
65 std::sync::LazyLock::new(
66 || std::sync::Mutex::new(std::collections::HashMap::new()),
67 );
68
69 let ptr = column_names.as_ref().as_ptr() as usize;
71 let cache = {
72 let mut cache_map = match CACHE_MAP.lock() {
73 Ok(guard) => guard,
74 Err(poisoned) => {
75 poisoned.into_inner()
77 }
78 };
79 let cache_entry = cache_map.entry(ptr).or_insert_with(|| {
80 std::sync::Arc::new(
81 column_names
82 .iter()
83 .enumerate()
84 .map(|(i, name)| (name.clone(), i))
85 .collect::<std::collections::HashMap<_, _>>(),
86 )
87 });
88 cache_entry.clone()
89 };
90
91 let row = CustomDbRow {
92 column_names: column_names.clone(),
93 rows: row_values,
94 column_index_cache: cache,
95 };
96
97 self.results.push(row);
98 self.rows_affected += 1;
99 }
100 }
101
102 pub fn add_row(&mut self, row: CustomDbRow) {
108 if self.column_names.is_none() {
110 self.column_names = Some(row.column_names.clone());
111 }
112
113 self.results.push(row);
114 self.rows_affected += 1;
115 }
116
117 #[must_use]
119 pub fn into_optional(mut self) -> Option<CustomDbRow> {
120 if self.results.is_empty() {
121 None
122 } else {
123 Some(self.results.remove(0))
124 }
125 }
126
127 pub fn into_one(self) -> Result<CustomDbRow, SqlMiddlewareDbError> {
132 self.into_optional().ok_or_else(|| {
133 SqlMiddlewareDbError::ExecutionError("query returned no rows".to_string())
134 })
135 }
136
137 pub fn map_optional<T, F>(self, mapper: F) -> Result<Option<T>, SqlMiddlewareDbError>
142 where
143 F: FnOnce(&CustomDbRow) -> Result<T, SqlMiddlewareDbError>,
144 {
145 self.into_optional().map(|row| mapper(&row)).transpose()
146 }
147
148 pub fn map_one<T, F>(self, mapper: F) -> Result<T, SqlMiddlewareDbError>
154 where
155 F: FnOnce(&CustomDbRow) -> Result<T, SqlMiddlewareDbError>,
156 {
157 let row = self.into_one()?;
158 mapper(&row)
159 }
160}