alopex_sql/executor/
result.rs1use crate::catalog::ColumnMetadata;
10use crate::executor::evaluator::EvalContext;
11use crate::executor::query::iterator::RowIterator;
12use crate::planner::ResolvedType;
13use crate::planner::typed_expr::Projection;
14use crate::storage::SqlValue;
15
16use super::ExecutorError;
17
18#[derive(Debug, Clone, PartialEq)]
20pub enum ExecutionResult {
21 Success,
23
24 RowsAffected(u64),
26
27 Query(QueryResult),
29}
30
31#[derive(Debug, Clone, PartialEq)]
33pub struct QueryResult {
34 pub columns: Vec<ColumnInfo>,
36
37 pub rows: Vec<Vec<SqlValue>>,
39}
40
41impl QueryResult {
42 pub fn new(columns: Vec<ColumnInfo>, rows: Vec<Vec<SqlValue>>) -> Self {
44 Self { columns, rows }
45 }
46
47 pub fn empty(columns: Vec<ColumnInfo>) -> Self {
49 Self {
50 columns,
51 rows: Vec::new(),
52 }
53 }
54
55 pub fn row_count(&self) -> usize {
57 self.rows.len()
58 }
59
60 pub fn column_count(&self) -> usize {
62 self.columns.len()
63 }
64
65 pub fn is_empty(&self) -> bool {
67 self.rows.is_empty()
68 }
69}
70
71#[derive(Debug, Clone, PartialEq, Eq)]
73pub struct ColumnInfo {
74 pub name: String,
76
77 pub data_type: ResolvedType,
79}
80
81impl ColumnInfo {
82 pub fn new(name: impl Into<String>, data_type: ResolvedType) -> Self {
84 Self {
85 name: name.into(),
86 data_type,
87 }
88 }
89}
90
91#[derive(Debug, Clone, PartialEq)]
93pub struct Row {
94 pub row_id: u64,
96
97 pub values: Vec<SqlValue>,
99}
100
101impl Row {
102 pub fn new(row_id: u64, values: Vec<SqlValue>) -> Self {
104 Self { row_id, values }
105 }
106
107 pub fn get(&self, index: usize) -> Option<&SqlValue> {
109 self.values.get(index)
110 }
111
112 pub fn len(&self) -> usize {
114 self.values.len()
115 }
116
117 pub fn is_empty(&self) -> bool {
119 self.values.is_empty()
120 }
121}
122
123pub struct QueryRowIterator<'a> {
141 inner: Box<dyn RowIterator + 'a>,
143 projection: Projection,
145 #[allow(dead_code)]
147 schema: Vec<ColumnMetadata>,
148 columns: Vec<ColumnInfo>,
150}
151
152impl<'a> QueryRowIterator<'a> {
153 pub fn new(
155 inner: Box<dyn RowIterator + 'a>,
156 projection: Projection,
157 schema: Vec<ColumnMetadata>,
158 ) -> Self {
159 let columns = match &projection {
161 Projection::All(_) => schema
162 .iter()
163 .map(|col| ColumnInfo::new(&col.name, col.data_type.clone()))
164 .collect(),
165 Projection::Columns(cols) => cols
166 .iter()
167 .map(|col| {
168 let name = col.alias.clone().unwrap_or_else(|| match &col.expr.kind {
169 crate::planner::typed_expr::TypedExprKind::ColumnRef { column, .. } => {
170 column.clone()
171 }
172 _ => "?column?".to_string(),
173 });
174 ColumnInfo::new(name, col.expr.resolved_type.clone())
175 })
176 .collect(),
177 };
178
179 Self {
180 inner,
181 projection,
182 schema,
183 columns,
184 }
185 }
186
187 pub fn columns(&self) -> &[ColumnInfo] {
189 &self.columns
190 }
191
192 pub fn next_row(&mut self) -> Result<Option<Vec<SqlValue>>, ExecutorError> {
198 match self.inner.next_row() {
199 Some(Ok(row)) => {
200 let projected = self.project_row(&row)?;
201 Ok(Some(projected))
202 }
203 Some(Err(e)) => Err(e),
204 None => Ok(None),
205 }
206 }
207
208 fn project_row(&self, row: &Row) -> Result<Vec<SqlValue>, ExecutorError> {
210 match &self.projection {
211 Projection::All(_) => Ok(row.values.clone()),
212 Projection::Columns(proj_cols) => {
213 let mut output = Vec::with_capacity(proj_cols.len());
214 let ctx = EvalContext::new(&row.values);
215 for col in proj_cols {
216 let value = crate::executor::evaluator::evaluate(&col.expr, &ctx)?;
217 output.push(value);
218 }
219 Ok(output)
220 }
221 }
222 }
223}
224
225#[cfg(test)]
226mod tests {
227 use super::*;
228
229 #[test]
230 fn test_execution_result_success() {
231 let result = ExecutionResult::Success;
232 assert!(matches!(result, ExecutionResult::Success));
233 }
234
235 #[test]
236 fn test_execution_result_rows_affected() {
237 let result = ExecutionResult::RowsAffected(5);
238 if let ExecutionResult::RowsAffected(count) = result {
239 assert_eq!(count, 5);
240 } else {
241 panic!("Expected RowsAffected variant");
242 }
243 }
244
245 #[test]
246 fn test_query_result_new() {
247 let columns = vec![
248 ColumnInfo::new("id", ResolvedType::Integer),
249 ColumnInfo::new("name", ResolvedType::Text),
250 ];
251 let rows = vec![
252 vec![SqlValue::Integer(1), SqlValue::Text("Alice".into())],
253 vec![SqlValue::Integer(2), SqlValue::Text("Bob".into())],
254 ];
255 let result = QueryResult::new(columns, rows);
256
257 assert_eq!(result.row_count(), 2);
258 assert_eq!(result.column_count(), 2);
259 assert!(!result.is_empty());
260 }
261
262 #[test]
263 fn test_query_result_empty() {
264 let columns = vec![ColumnInfo::new("id", ResolvedType::Integer)];
265 let result = QueryResult::empty(columns);
266
267 assert_eq!(result.row_count(), 0);
268 assert_eq!(result.column_count(), 1);
269 assert!(result.is_empty());
270 }
271
272 #[test]
273 fn test_row_new() {
274 let row = Row::new(
275 42,
276 vec![SqlValue::Integer(1), SqlValue::Text("test".into())],
277 );
278
279 assert_eq!(row.row_id, 42);
280 assert_eq!(row.len(), 2);
281 assert!(!row.is_empty());
282 assert_eq!(row.get(0), Some(&SqlValue::Integer(1)));
283 assert_eq!(row.get(1), Some(&SqlValue::Text("test".into())));
284 assert_eq!(row.get(2), None);
285 }
286
287 #[test]
288 fn test_column_info_new() {
289 let info = ColumnInfo::new("age", ResolvedType::Integer);
290 assert_eq!(info.name, "age");
291 assert_eq!(info.data_type, ResolvedType::Integer);
292 }
293}