| 31 | } |
| 32 | |
| 33 | pub fn execute(select: &VSelect, input: VTable) -> Result<ResultSet, ExecError> { |
| 34 | // 1. Apply WHERE. |
| 35 | let mut filtered: Vec<Vec<VValue>> = Vec::with_capacity(input.rows.len()); |
| 36 | for row in &input.rows { |
| 37 | let keep = match &select.filter { |
| 38 | Some(predicate) => { |
| 39 | let v = eval(predicate, row, &input)?; |
| 40 | truthy(&v) |
| 41 | } |
| 42 | None => true, |
| 43 | }; |
| 44 | if keep { |
| 45 | filtered.push(row.clone()); |
| 46 | } |
| 47 | } |
| 48 | |
| 49 | // 2. Projection — aggregate vs. row-wise. |
| 50 | let (mut out_cols, mut out_rows) = if select.has_aggregate { |
| 51 | project_aggregate(select, &filtered, &input)? |
| 52 | } else { |
| 53 | project_rowwise(select, &filtered, &input)? |
| 54 | }; |
| 55 | |
| 56 | // 3. ORDER BY. Aggregate result is a single row; sorting it is a no-op |
| 57 | // but harmless. |
| 58 | if !select.order_by.is_empty() && !select.has_aggregate { |
| 59 | sort_rows(&mut out_rows, &select.order_by, &input)?; |
| 60 | } |
| 61 | |
| 62 | // 4. OFFSET / LIMIT. |
| 63 | if select.offset > 0 { |
| 64 | let skip = select.offset.min(out_rows.len()); |
| 65 | out_rows.drain(..skip); |
| 66 | } |
| 67 | if let Some(limit) = select.limit |
| 68 | && out_rows.len() > limit |
| 69 | { |
| 70 | out_rows.truncate(limit); |
| 71 | } |
| 72 | |
| 73 | // out_cols built above already reflects projection; trim unused mut. |
| 74 | let _ = &mut out_cols; |
| 75 | Ok(ResultSet { |
| 76 | columns: out_cols, |
| 77 | rows: out_rows, |
| 78 | }) |
| 79 | } |
| 80 | |
| 81 | fn project_rowwise( |
| 82 | select: &VSelect, |