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}