manifoldb_query/exec/
result.rs

1//! Query result types.
2//!
3//! This module defines the types used to represent query results.
4
5use std::sync::Arc;
6
7use manifoldb_core::Value;
8
9use super::row::{Row, Schema};
10
11/// The result of a query execution.
12#[derive(Debug)]
13pub enum QueryResult {
14    /// A result set with rows.
15    Select(ResultSet),
16    /// The number of rows affected by an INSERT/UPDATE/DELETE.
17    Affected(u64),
18    /// An empty result (e.g., from DDL statements).
19    Empty,
20}
21
22impl QueryResult {
23    /// Creates a new select result.
24    #[must_use]
25    pub fn select(result_set: ResultSet) -> Self {
26        Self::Select(result_set)
27    }
28
29    /// Creates an affected rows result.
30    #[must_use]
31    pub const fn affected(count: u64) -> Self {
32        Self::Affected(count)
33    }
34
35    /// Creates an empty result.
36    #[must_use]
37    pub const fn empty() -> Self {
38        Self::Empty
39    }
40
41    /// Returns true if this is a select result.
42    #[must_use]
43    pub const fn is_select(&self) -> bool {
44        matches!(self, Self::Select(_))
45    }
46
47    /// Returns the result set if this is a select result.
48    #[must_use]
49    pub fn as_select(&self) -> Option<&ResultSet> {
50        match self {
51            Self::Select(rs) => Some(rs),
52            _ => None,
53        }
54    }
55
56    /// Consumes and returns the result set if this is a select result.
57    #[must_use]
58    pub fn into_select(self) -> Option<ResultSet> {
59        match self {
60            Self::Select(rs) => Some(rs),
61            _ => None,
62        }
63    }
64
65    /// Returns the affected row count if this is an affected result.
66    #[must_use]
67    pub const fn affected_rows(&self) -> Option<u64> {
68        match self {
69            Self::Affected(n) => Some(*n),
70            _ => None,
71        }
72    }
73}
74
75/// A set of result rows from a SELECT query.
76#[derive(Debug, Clone)]
77pub struct ResultSet {
78    /// The schema of the result set.
79    schema: Arc<Schema>,
80    /// The rows in the result set.
81    rows: Vec<Row>,
82}
83
84impl ResultSet {
85    /// Creates a new result set.
86    #[must_use]
87    pub fn new(schema: Arc<Schema>) -> Self {
88        Self { schema, rows: Vec::new() }
89    }
90
91    /// Creates a result set with the given rows.
92    #[must_use]
93    pub fn with_rows(schema: Arc<Schema>, rows: Vec<Row>) -> Self {
94        Self { schema, rows }
95    }
96
97    /// Returns the schema.
98    #[must_use]
99    pub fn schema(&self) -> &Schema {
100        &self.schema
101    }
102
103    /// Returns the shared schema reference.
104    #[must_use]
105    pub fn schema_arc(&self) -> Arc<Schema> {
106        Arc::clone(&self.schema)
107    }
108
109    /// Returns the column names.
110    #[must_use]
111    pub fn columns(&self) -> Vec<&str> {
112        self.schema.columns()
113    }
114
115    /// Returns the rows.
116    #[must_use]
117    pub fn rows(&self) -> &[Row] {
118        &self.rows
119    }
120
121    /// Returns the number of rows.
122    #[must_use]
123    pub fn len(&self) -> usize {
124        self.rows.len()
125    }
126
127    /// Returns true if there are no rows.
128    #[must_use]
129    pub fn is_empty(&self) -> bool {
130        self.rows.is_empty()
131    }
132
133    /// Adds a row to the result set.
134    pub fn push(&mut self, row: Row) {
135        self.rows.push(row);
136    }
137
138    /// Gets a row by index.
139    #[must_use]
140    pub fn get(&self, index: usize) -> Option<&Row> {
141        self.rows.get(index)
142    }
143
144    /// Consumes the result set and returns the rows.
145    #[must_use]
146    pub fn into_rows(self) -> Vec<Row> {
147        self.rows
148    }
149
150    /// Returns an iterator over the rows.
151    pub fn iter(&self) -> impl Iterator<Item = &Row> {
152        self.rows.iter()
153    }
154
155    /// Converts to a vector of value arrays.
156    #[must_use]
157    pub fn to_values(&self) -> Vec<Vec<Value>> {
158        self.rows.iter().map(|r| r.values().to_vec()).collect()
159    }
160}
161
162impl IntoIterator for ResultSet {
163    type Item = Row;
164    type IntoIter = std::vec::IntoIter<Row>;
165
166    fn into_iter(self) -> Self::IntoIter {
167        self.rows.into_iter()
168    }
169}
170
171impl<'a> IntoIterator for &'a ResultSet {
172    type Item = &'a Row;
173    type IntoIter = std::slice::Iter<'a, Row>;
174
175    fn into_iter(self) -> Self::IntoIter {
176        self.rows.iter()
177    }
178}
179
180/// Builder for constructing result sets incrementally.
181///
182/// Provides a convenient way to build a `ResultSet` row by row.
183pub struct ResultSetBuilder {
184    schema: Arc<Schema>,
185    rows: Vec<Row>,
186}
187
188impl ResultSetBuilder {
189    /// Creates a new builder with the given schema.
190    #[must_use]
191    pub fn new(schema: Arc<Schema>) -> Self {
192        Self { schema, rows: Vec::new() }
193    }
194
195    /// Creates a builder with pre-allocated capacity.
196    #[must_use]
197    pub fn with_capacity(schema: Arc<Schema>, capacity: usize) -> Self {
198        Self { schema, rows: Vec::with_capacity(capacity) }
199    }
200
201    /// Adds a row to the result set.
202    pub fn push(&mut self, row: Row) {
203        self.rows.push(row);
204    }
205
206    /// Adds a row from values.
207    pub fn push_values(&mut self, values: Vec<Value>) {
208        let row = Row::new(Arc::clone(&self.schema), values);
209        self.rows.push(row);
210    }
211
212    /// Returns the current number of rows.
213    #[must_use]
214    pub fn len(&self) -> usize {
215        self.rows.len()
216    }
217
218    /// Returns true if no rows have been added.
219    #[must_use]
220    pub fn is_empty(&self) -> bool {
221        self.rows.is_empty()
222    }
223
224    /// Builds the result set.
225    #[must_use]
226    pub fn build(self) -> ResultSet {
227        ResultSet::with_rows(self.schema, self.rows)
228    }
229}
230
231#[cfg(test)]
232mod tests {
233    use super::*;
234
235    #[test]
236    fn query_result_types() {
237        let result = QueryResult::affected(5);
238        assert!(!result.is_select());
239        assert_eq!(result.affected_rows(), Some(5));
240
241        let schema = Arc::new(Schema::new(vec!["id".to_string()]));
242        let result = QueryResult::select(ResultSet::new(schema));
243        assert!(result.is_select());
244        assert_eq!(result.affected_rows(), None);
245    }
246
247    #[test]
248    fn result_set_basic() {
249        let schema = Arc::new(Schema::new(vec!["id".to_string(), "name".to_string()]));
250        let mut rs = ResultSet::new(Arc::clone(&schema));
251
252        rs.push(Row::new(Arc::clone(&schema), vec![Value::Int(1), Value::from("Alice")]));
253        rs.push(Row::new(Arc::clone(&schema), vec![Value::Int(2), Value::from("Bob")]));
254
255        assert_eq!(rs.len(), 2);
256        assert_eq!(rs.columns(), &["id", "name"]);
257        assert_eq!(rs.get(0).and_then(|r| r.get(0)), Some(&Value::Int(1)));
258    }
259
260    #[test]
261    fn result_set_builder() {
262        let schema = Arc::new(Schema::new(vec!["x".to_string()]));
263        let mut builder = ResultSetBuilder::new(Arc::clone(&schema));
264
265        builder.push_values(vec![Value::Int(1)]);
266        builder.push_values(vec![Value::Int(2)]);
267
268        let rs = builder.build();
269        assert_eq!(rs.len(), 2);
270    }
271
272    #[test]
273    fn result_set_iterator() {
274        let schema = Arc::new(Schema::new(vec!["n".to_string()]));
275        let rs = ResultSet::with_rows(
276            Arc::clone(&schema),
277            vec![
278                Row::new(Arc::clone(&schema), vec![Value::Int(1)]),
279                Row::new(Arc::clone(&schema), vec![Value::Int(2)]),
280            ],
281        );
282
283        let sum: i64 = rs
284            .iter()
285            .filter_map(|r| match r.get(0) {
286                Some(Value::Int(n)) => Some(*n),
287                _ => None,
288            })
289            .sum();
290
291        assert_eq!(sum, 3);
292    }
293}