linkrs_query/executor/streaming/chunk/columnar_batch/
collector.rs1use std::cmp::Ordering;
2
3use crate::executor::streaming::chunk::core::DataChunk;
4use linkrs_core::value::NullType;
5use linkrs_core::Value;
6
7use super::BatchColumn;
8
9#[derive(Debug, Clone, Default)]
12pub struct ColumnarBatch {
13 columns: Vec<BatchColumn>,
14 num_rows: usize,
15}
16
17impl ColumnarBatch {
18 pub fn new(num_columns: usize) -> Self {
20 Self {
21 columns: vec![BatchColumn::Empty; num_columns],
22 num_rows: 0,
23 }
24 }
25
26 pub fn num_columns(&self) -> usize {
27 self.columns.len()
28 }
29
30 pub fn num_rows(&self) -> usize {
31 self.num_rows
32 }
33
34 pub fn is_empty(&self) -> bool {
35 self.num_rows == 0
36 }
37
38 pub fn column(&self, idx: usize) -> &BatchColumn {
39 &self.columns[idx]
40 }
41
42 pub fn append_chunk(&mut self, chunk: &DataChunk) -> usize {
48 let indices = chunk.visible_indices();
49 if indices.is_empty() {
50 return self.num_rows;
51 }
52 let num_cols = chunk.num_columns();
53 if self.columns.is_empty() {
54 self.columns = vec![BatchColumn::Empty; num_cols];
55 }
56 if self.columns.len() < num_cols {
57 self.columns.resize(num_cols, BatchColumn::Empty);
58 }
59 for j in 0..num_cols {
60 let column = &mut self.columns[j];
61 if let Some(typed) = chunk.typed_column(j) {
62 column.append_typed(typed, &indices);
63 } else {
64 let is_typed = column.is_typed();
66 if is_typed {
67 for &i in &indices {
68 let value = chunk
69 .rows
70 .get(i)
71 .and_then(|r| r.get(j))
72 .cloned()
73 .unwrap_or_else(|| Value::Null(NullType::Null));
74 column.append_row_value(&value);
75 }
76 } else {
77 for &i in &indices {
78 let value = chunk
79 .rows
80 .get(i)
81 .and_then(|r| r.get(j))
82 .cloned()
83 .unwrap_or_else(|| Value::Null(NullType::Null));
84 if matches!(column, BatchColumn::Empty) {
85 *column = BatchColumn::Fallback(vec![value]);
86 } else {
87 column.append_row_value(&value);
88 }
89 }
90 }
91 }
92 }
93 self.num_rows += indices.len();
94 self.num_rows
95 }
96
97 pub fn append_chunk_row(&mut self, chunk: &DataChunk, idx: usize) {
103 let num_cols = chunk.num_columns();
104 if self.columns.is_empty() {
105 self.columns = vec![BatchColumn::Empty; num_cols];
106 }
107 if self.columns.len() < num_cols {
108 self.columns.resize(num_cols, BatchColumn::Empty);
109 }
110 for (j, column) in self.columns.iter_mut().enumerate().take(num_cols) {
111 if let Some(typed) = chunk.typed_column(j) {
112 column.append_typed(typed, std::slice::from_ref(&idx));
113 } else {
114 let value = chunk
115 .rows
116 .get(idx)
117 .and_then(|r| r.get(j))
118 .cloned()
119 .unwrap_or_else(|| Value::Null(NullType::Null));
120 if matches!(column, BatchColumn::Empty) {
121 *column = BatchColumn::Fallback(vec![value]);
122 } else {
123 column.append_row_value(&value);
124 }
125 }
126 }
127 self.num_rows += 1;
128 }
129
130 pub fn append_row(&mut self, row: &[Value]) {
132 if self.columns.is_empty() {
133 self.columns = vec![BatchColumn::Empty; row.len()];
134 }
135 if self.columns.len() < row.len() {
136 self.columns.resize(row.len(), BatchColumn::Empty);
137 }
138 for (j, column) in self.columns.iter_mut().enumerate() {
139 let value = row
140 .get(j)
141 .cloned()
142 .unwrap_or_else(|| Value::Null(NullType::Null));
143 column.append_row_value(&value);
144 }
145 self.num_rows += 1;
146 }
147
148 pub fn to_rows(&self) -> Vec<Vec<Value>> {
150 let mut rows = Vec::with_capacity(self.num_rows);
151 for i in 0..self.num_rows {
152 let mut row = Vec::with_capacity(self.columns.len());
153 for column in &self.columns {
154 row.push(column.value_at(i));
155 }
156 rows.push(row);
157 }
158 rows
159 }
160
161 pub fn compare_rows_at(&self, col: usize, a: usize, b: usize) -> Ordering {
163 self.columns[col].compare_at(a, b)
164 }
165
166 pub fn compare_value_at(&self, col: usize, v: &Value, idx: usize) -> Ordering {
168 self.columns[col].compare_value_at(v, idx)
169 }
170
171 pub fn permute(&mut self, perm: &[usize]) {
173 for column in &mut self.columns {
174 column.permute(perm);
175 }
176 }
177
178 pub fn truncate(&mut self, len: usize) {
181 for column in &mut self.columns {
182 column.truncate(len);
183 }
184 self.num_rows = len.min(self.num_rows);
185 }
186
187 pub fn clear(&mut self) {
189 for column in &mut self.columns {
190 *column = BatchColumn::Empty;
191 }
192 self.num_rows = 0;
193 }
194
195 pub fn estimated_size(&self) -> usize {
197 self.columns.iter().map(|c| c.estimated_size()).sum()
198 }
199}