linkrs_query/executor/streaming/
result_utils.rs1use std::sync::Arc;
4
5use super::chunk::DataChunk;
6use super::chunk::LocalChunkCollector;
7use super::spill::SpillManager;
8use crate::executor::base::ExecutionResult;
9use linkrs_core::error::QueryError;
10use linkrs_core::DataSet;
11
12pub fn convert_chunks_to_dataset(
24 chunks: Vec<DataChunk>,
25 col_names: Option<Vec<String>>,
26) -> Result<DataSet, QueryError> {
27 convert_chunks_to_dataset_with_spill(chunks, col_names, None).map(|(dataset, _, _, _)| dataset)
28}
29
30pub fn convert_chunks_to_dataset_with_spill(
36 chunks: Vec<DataChunk>,
37 col_names: Option<Vec<String>>,
38 spill_manager: Option<Arc<SpillManager>>,
39) -> Result<(DataSet, u64, u64, u64), QueryError> {
40 if chunks.is_empty() {
41 let names = col_names.unwrap_or_default();
42 return Ok((DataSet::with_columns(names), 0, 0, 0));
43 }
44
45 let col_names = match col_names {
46 Some(names) if !names.is_empty() => names,
47 _ => chunks[0].col_names(),
48 };
49
50 let mut collector = LocalChunkCollector::new(col_names.clone());
51 if let Some(manager) = spill_manager {
52 collector.attach_spill_manager(manager);
53 }
54 let expected_cols = col_names.len();
55 for mut chunk in chunks {
56 if chunk.num_columns() != expected_cols {
57 return Err(QueryError::execution(format!(
58 "Chunk has {} columns, expected {}",
59 chunk.num_columns(),
60 expected_cols
61 )));
62 }
63 collector.push_chunk(&mut chunk)?;
65 }
66
67 collector.finish_spill()?;
68 let spilled_rows = collector.spilled_rows();
69 let spilled_bytes = collector.spilled_bytes();
70 let spilled_runs = collector.spilled_run_count() as u64;
71 let (all_rows, _) = collector.into_rows()?;
72 Ok((
73 DataSet::from_rows(all_rows, col_names),
74 spilled_rows,
75 spilled_bytes,
76 spilled_runs,
77 ))
78}
79
80pub fn chunks_to_execution_result(
89 chunks: Vec<DataChunk>,
90 col_names: Option<Vec<String>>,
91) -> Result<ExecutionResult, QueryError> {
92 chunks_to_execution_result_with_spill(chunks, col_names, None)
93}
94
95pub fn chunks_to_execution_result_with_spill(
102 chunks: Vec<DataChunk>,
103 col_names: Option<Vec<String>>,
104 spill_manager: Option<Arc<SpillManager>>,
105) -> Result<ExecutionResult, QueryError> {
106 let (dataset, _, _, _) =
107 convert_chunks_to_dataset_with_spill(chunks, col_names, spill_manager)?;
108 Ok(ExecutionResult::DataSet { data: dataset })
109}
110
111#[cfg(test)]
112mod tests {
113 use super::*;
114 use crate::executor::streaming::chunk::DataChunk;
115 use linkrs_core::Value;
116
117 fn create_test_chunk(rows: Vec<Vec<Value>>) -> DataChunk {
118 DataChunk::from_rows(rows)
119 }
120
121 #[test]
122 fn test_empty_chunks() {
123 let result = convert_chunks_to_dataset(vec![], None);
124 assert!(result.is_ok());
125 let ds = result.unwrap();
126 assert!(ds.is_empty());
127 assert_eq!(ds.col_count(), 0);
128 }
129
130 #[test]
131 fn test_single_chunk() {
132 let rows = vec![
133 vec![Value::Int(1), Value::string("a")],
134 vec![Value::Int(2), Value::string("b")],
135 ];
136 let chunk = create_test_chunk(rows);
137 let result = convert_chunks_to_dataset(vec![chunk], None);
138 assert!(result.is_ok());
139 let ds = result.unwrap();
140 assert_eq!(ds.row_count(), 2);
141 assert_eq!(ds.col_count(), 2);
142 }
143
144 #[test]
145 fn test_multiple_chunks() {
146 let chunk1 = create_test_chunk(vec![
147 vec![Value::Int(1), Value::string("a")],
148 vec![Value::Int(2), Value::string("b")],
149 ]);
150 let chunk2 = create_test_chunk(vec![
151 vec![Value::Int(3), Value::string("c")],
152 vec![Value::Int(4), Value::string("d")],
153 ]);
154
155 let result = convert_chunks_to_dataset(vec![chunk1, chunk2], None);
156 assert!(result.is_ok());
157 let ds = result.unwrap();
158 assert_eq!(ds.row_count(), 4);
159 assert_eq!(ds.col_count(), 2);
160 }
161
162 #[test]
163 fn test_execution_result_conversion() {
164 let chunk = create_test_chunk(vec![vec![Value::Int(42)]]);
165 let result = chunks_to_execution_result(vec![chunk], None);
166 assert!(result.is_ok());
167 if let ExecutionResult::DataSet { data: ds, .. } = result.unwrap() {
168 assert_eq!(ds.row_count(), 1);
169 } else {
170 panic!("Expected DataSet result");
171 }
172 }
173
174 #[test]
175 fn test_custom_col_names() {
176 let rows = vec![vec![Value::Int(1), Value::string("test")]];
177 let chunk = create_test_chunk(rows);
178 let col_names = vec!["id".to_string(), "name".to_string()];
179 let result = convert_chunks_to_dataset(vec![chunk], Some(col_names.clone()));
180 assert!(result.is_ok());
181 let ds = result.unwrap();
182 assert_eq!(ds.col_names, col_names);
183 }
184}