sql_middleware/results/result_set.rs
1use super::row::CustomDbRow;
2use crate::types::RowValues;
3
4type ColumnCacheMap = std::sync::LazyLock<
5 std::sync::Mutex<
6 std::collections::HashMap<usize, std::sync::Arc<std::collections::HashMap<String, usize>>>,
7 >,
8>;
9
10/// A result set from a database query
11///
12/// This struct represents the result of a database query,
13/// containing the rows returned by the query and metadata.
14#[derive(Debug, Clone, Default)]
15pub struct ResultSet {
16 /// The rows returned by the query
17 pub results: Vec<CustomDbRow>,
18 /// The number of rows affected (for DML statements)
19 pub rows_affected: usize,
20 /// Column names shared by all rows (to avoid duplicating in each row)
21 column_names: Option<std::sync::Arc<Vec<String>>>,
22}
23
24impl ResultSet {
25 /// Create a new result set with a known capacity
26 ///
27 /// # Arguments
28 ///
29 /// * `capacity` - The initial capacity for the result rows
30 ///
31 /// # Returns
32 ///
33 /// A new `ResultSet` instance with preallocated capacity
34 #[must_use]
35 pub fn with_capacity(capacity: usize) -> ResultSet {
36 ResultSet {
37 results: Vec::with_capacity(capacity),
38 rows_affected: 0,
39 column_names: None,
40 }
41 }
42
43 /// Set the column names for this result set (to be shared by all rows)
44 pub fn set_column_names(&mut self, column_names: std::sync::Arc<Vec<String>>) {
45 self.column_names = Some(column_names);
46 }
47
48 /// Get the column names for this result set
49 #[must_use]
50 pub fn get_column_names(&self) -> Option<&std::sync::Arc<Vec<String>>> {
51 self.column_names.as_ref()
52 }
53
54 /// Add a row to the result set
55 ///
56 /// # Arguments
57 ///
58 /// * `row_values` - The values for this row
59 pub fn add_row_values(&mut self, row_values: Vec<RowValues>) {
60 if let Some(column_names) = &self.column_names {
61 // Build a cache of column name to index for faster lookups
62 // We only need to build this cache once and reuse it
63 static CACHE_MAP: ColumnCacheMap =
64 std::sync::LazyLock::new(
65 || std::sync::Mutex::new(std::collections::HashMap::new()),
66 );
67
68 // Use the pointer to column_names as a key for the cache
69 let ptr = column_names.as_ref().as_ptr() as usize;
70 let cache = {
71 let mut cache_map = match CACHE_MAP.lock() {
72 Ok(guard) => guard,
73 Err(poisoned) => {
74 // Clear the poison and continue with the recovered data
75 poisoned.into_inner()
76 }
77 };
78 let cache_entry = cache_map.entry(ptr).or_insert_with(|| {
79 std::sync::Arc::new(
80 column_names
81 .iter()
82 .enumerate()
83 .map(|(i, name)| (name.clone(), i))
84 .collect::<std::collections::HashMap<_, _>>(),
85 )
86 });
87 cache_entry.clone()
88 };
89
90 let row = CustomDbRow {
91 column_names: column_names.clone(),
92 rows: row_values,
93 column_index_cache: cache,
94 };
95
96 self.results.push(row);
97 self.rows_affected += 1;
98 }
99 }
100
101 /// Add a row to the result set (legacy method, less efficient)
102 ///
103 /// # Arguments
104 ///
105 /// * `row` - The row to add
106 pub fn add_row(&mut self, row: CustomDbRow) {
107 // If column names haven't been set yet, use the ones from this row
108 if self.column_names.is_none() {
109 self.column_names = Some(row.column_names.clone());
110 }
111
112 self.results.push(row);
113 self.rows_affected += 1;
114 }
115}