vibesql_executor/evaluator/window/
partitioning.rs1use vibesql_ast::Expression;
6use vibesql_storage::Row;
7use vibesql_types::SqlValue;
8
9#[derive(Debug, Clone)]
11pub struct Partition {
12 pub rows: Vec<Row>,
13 pub original_indices: Vec<usize>,
15 pub column_map: std::collections::HashMap<String, usize>,
17}
18
19impl Partition {
20 pub fn new(rows: Vec<Row>) -> Self {
21 let original_indices = (0..rows.len()).collect();
22 Self { rows, original_indices, column_map: std::collections::HashMap::new() }
23 }
24
25 pub fn with_indices(rows: Vec<Row>, original_indices: Vec<usize>) -> Self {
26 Self { rows, original_indices, column_map: std::collections::HashMap::new() }
27 }
28
29 pub fn with_column_map(
30 rows: Vec<Row>,
31 original_indices: Vec<usize>,
32 column_map: std::collections::HashMap<String, usize>,
33 ) -> Self {
34 Self { rows, original_indices, column_map }
35 }
36
37 pub fn len(&self) -> usize {
38 self.rows.len()
39 }
40
41 pub fn is_empty(&self) -> bool {
42 self.rows.is_empty()
43 }
44
45 pub fn get_column_index(&self, name: &str) -> Option<usize> {
47 if let Some(&idx) = self.column_map.get(name) {
49 return Some(idx);
50 }
51 let lower = name.to_lowercase();
53 self.column_map.get(&lower).copied()
54 }
55}
56
57pub fn partition_rows<F>(
68 rows: Vec<Row>,
69 partition_by: &Option<Vec<Expression>>,
70 eval_fn: F,
71) -> Result<Vec<Partition>, String>
72where
73 F: Fn(&Expression, &Row) -> Result<SqlValue, String>,
74{
75 partition_rows_with_collations(rows, partition_by, &[], eval_fn)
76}
77
78pub fn partition_rows_with_collations<F>(
86 rows: Vec<Row>,
87 partition_by: &Option<Vec<Expression>>,
88 collations: &[Option<String>],
89 eval_fn: F,
90) -> Result<Vec<Partition>, String>
91where
92 F: Fn(&Expression, &Row) -> Result<SqlValue, String>,
93{
94 let Some(partition_exprs) = partition_by else {
96 return Ok(vec![Partition::new(rows)]);
97 };
98
99 if partition_exprs.is_empty() {
100 return Ok(vec![Partition::new(rows)]);
101 }
102
103 let mut partitions_map: std::collections::BTreeMap<Vec<SqlValue>, Vec<(usize, Row)>> =
107 std::collections::BTreeMap::new();
108
109 for (original_idx, row) in rows.into_iter().enumerate() {
110 let mut partition_key = Vec::new();
112
113 for (i, expr) in partition_exprs.iter().enumerate() {
114 let value = eval_fn(expr, &row)?;
118 let collation = collations.get(i).and_then(|c| c.as_deref());
120 if let Some(coll) = collation {
121 if coll.eq_ignore_ascii_case("nocase") {
122 let normalized = match &value {
123 SqlValue::Varchar(s) => {
124 SqlValue::Varchar(arcstr::ArcStr::from(s.to_uppercase()))
125 }
126 SqlValue::Character(s) => {
127 SqlValue::Character(arcstr::ArcStr::from(s.to_uppercase()))
128 }
129 other => other.clone(),
130 };
131 partition_key.push(normalized);
132 continue;
133 }
134 }
135 partition_key.push(value);
136 }
137
138 partitions_map.entry(partition_key).or_default().push((original_idx, row));
139 }
140
141 Ok(partitions_map
143 .into_values()
144 .map(|rows_with_indices| {
145 let (indices, rows): (Vec<_>, Vec<_>) = rows_with_indices.into_iter().unzip();
146 Partition::with_indices(rows, indices)
147 })
148 .collect())
149}