1use std::error::Error;
14use std::sync::Arc;
15
16use arrow_array::builder::{
17 BooleanBuilder, Float64Builder, Int32Builder, StringDictionaryBuilder,
18 TimestampMillisecondBuilder,
19};
20use arrow_array::cast::AsArray;
21use arrow_array::types::Int32Type;
22use arrow_array::{
23 Array, ArrayRef, BooleanArray, Date32Array, Date64Array, Decimal128Array, DictionaryArray,
24 Float16Array, Float32Array, Float64Array, Int8Array, Int16Array, Int32Array, Int64Array,
25 LargeStringArray, RecordBatch, RecordBatchOptions, StringArray, Time32MillisecondArray,
26 Time32SecondArray, Time64MicrosecondArray, Time64NanosecondArray, TimestampMicrosecondArray,
27 TimestampMillisecondArray, TimestampNanosecondArray, TimestampSecondArray, UInt8Array,
28 UInt16Array, UInt32Array, UInt64Array,
29};
30use arrow_ipc::reader::{FileReader, StreamReader};
31use arrow_ipc::writer::StreamWriter;
32use arrow_schema::{DataType, Field, Schema, TimeUnit};
33use indexmap::IndexMap;
34use serde::Serialize;
35
36use crate::config::{GroupRollupMode, Scalar, ViewConfig};
37
38pub enum ColumnBuilder {
41 Boolean(BooleanBuilder),
42 String(StringDictionaryBuilder<Int32Type>),
43 Float(Float64Builder),
44 Integer(Int32Builder),
45 Datetime(TimestampMillisecondBuilder),
46}
47
48fn dict_data_type() -> DataType {
49 DataType::Dictionary(Box::new(DataType::Int32), Box::new(DataType::Utf8))
50}
51
52fn dict_str_value(col: &ArrayRef, row_idx: usize) -> Option<&str> {
55 if col.is_null(row_idx) {
56 return None;
57 }
58
59 let typed = col
60 .as_any()
61 .downcast_ref::<DictionaryArray<Int32Type>>()
62 .and_then(|dict| {
63 let values = dict.values().as_any().downcast_ref::<StringArray>()?;
64 Some((dict, values))
65 });
66
67 match typed {
68 Some((dict, values)) => {
69 let key = dict.keys().value(row_idx) as usize;
70 (key < values.len() && !values.is_null(key)).then(|| values.value(key))
71 },
72 None => {
73 tracing::error!("Non-canonical dictionary column {}", col.data_type());
74 None
75 },
76 }
77}
78
79#[derive(Debug, Serialize)]
84#[serde(untagged)]
85pub enum VirtualDataCell {
86 Boolean(Option<bool>),
87 String(Option<String>),
88 Float(Option<f64>),
89 Integer(Option<i32>),
90 Datetime(Option<i64>),
91 RowPath(Vec<Scalar>),
92}
93
94#[derive(Copy, Clone, Debug, PartialEq, Eq)]
95pub enum RowPathStyle {
96 Sidecar,
102
103 PerLevel,
107}
108
109pub trait SetVirtualDataColumn {
115 fn write_to(self, col: &mut ColumnBuilder) -> Result<(), &'static str>;
119
120 fn new_builder() -> ColumnBuilder;
123
124 fn to_scalar(self) -> Scalar;
126}
127
128impl SetVirtualDataColumn for Option<String> {
129 fn write_to(self, col: &mut ColumnBuilder) -> Result<(), &'static str> {
130 if let ColumnBuilder::String(builder) = col {
131 match self {
132 Some(s) => builder.append_value(&s),
133 None => builder.append_null(),
134 }
135 Ok(())
136 } else {
137 Err("Bad type")
138 }
139 }
140
141 fn new_builder() -> ColumnBuilder {
142 ColumnBuilder::String(StringDictionaryBuilder::new())
143 }
144
145 fn to_scalar(self) -> Scalar {
146 if let Some(x) = self {
147 Scalar::String(x)
148 } else {
149 Scalar::Null
150 }
151 }
152}
153
154impl SetVirtualDataColumn for Option<f64> {
155 fn write_to(self, col: &mut ColumnBuilder) -> Result<(), &'static str> {
156 if let ColumnBuilder::Float(builder) = col {
157 match self {
158 Some(v) => builder.append_value(v),
159 None => builder.append_null(),
160 }
161 Ok(())
162 } else {
163 Err("Bad type")
164 }
165 }
166
167 fn new_builder() -> ColumnBuilder {
168 ColumnBuilder::Float(Float64Builder::new())
169 }
170
171 fn to_scalar(self) -> Scalar {
172 if let Some(x) = self {
173 Scalar::Float(x)
174 } else {
175 Scalar::Null
176 }
177 }
178}
179
180impl SetVirtualDataColumn for Option<i32> {
181 fn write_to(self, col: &mut ColumnBuilder) -> Result<(), &'static str> {
182 if let ColumnBuilder::Integer(builder) = col {
183 match self {
184 Some(v) => builder.append_value(v),
185 None => builder.append_null(),
186 }
187 Ok(())
188 } else {
189 Err("Bad type")
190 }
191 }
192
193 fn new_builder() -> ColumnBuilder {
194 ColumnBuilder::Integer(Int32Builder::new())
195 }
196
197 fn to_scalar(self) -> Scalar {
198 if let Some(x) = self {
199 Scalar::Float(x as f64)
200 } else {
201 Scalar::Null
202 }
203 }
204}
205
206impl SetVirtualDataColumn for Option<i64> {
207 fn write_to(self, col: &mut ColumnBuilder) -> Result<(), &'static str> {
208 if let ColumnBuilder::Datetime(builder) = col {
209 match self {
210 Some(v) => builder.append_value(v),
211 None => builder.append_null(),
212 }
213 Ok(())
214 } else {
215 Err("Bad type")
216 }
217 }
218
219 fn new_builder() -> ColumnBuilder {
220 ColumnBuilder::Datetime(TimestampMillisecondBuilder::new())
221 }
222
223 fn to_scalar(self) -> Scalar {
224 if let Some(x) = self {
225 Scalar::Float(x as f64)
226 } else {
227 Scalar::Null
228 }
229 }
230}
231
232impl SetVirtualDataColumn for Option<bool> {
233 fn write_to(self, col: &mut ColumnBuilder) -> Result<(), &'static str> {
234 if let ColumnBuilder::Boolean(builder) = col {
235 match self {
236 Some(v) => builder.append_value(v),
237 None => builder.append_null(),
238 }
239 Ok(())
240 } else {
241 Err("Bad type")
242 }
243 }
244
245 fn new_builder() -> ColumnBuilder {
246 ColumnBuilder::Boolean(BooleanBuilder::new())
247 }
248
249 fn to_scalar(self) -> Scalar {
250 if let Some(x) = self {
251 Scalar::Bool(x)
252 } else {
253 Scalar::Null
254 }
255 }
256}
257
258#[derive(Debug)]
264pub struct VirtualDataSlice {
265 config: ViewConfig,
266 builders: IndexMap<String, ColumnBuilder>,
267 row_path: Option<Vec<Vec<Scalar>>>,
268 frozen: Option<RecordBatch>,
269}
270
271impl std::fmt::Debug for ColumnBuilder {
272 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
273 match self {
274 ColumnBuilder::Boolean(_) => write!(f, "ColumnBuilder::Boolean(..)"),
275 ColumnBuilder::String(_) => write!(f, "ColumnBuilder::String(..)"),
276 ColumnBuilder::Float(_) => write!(f, "ColumnBuilder::Float(..)"),
277 ColumnBuilder::Integer(_) => write!(f, "ColumnBuilder::Integer(..)"),
278 ColumnBuilder::Datetime(_) => write!(f, "ColumnBuilder::Datetime(..)"),
279 }
280 }
281}
282
283fn cast_to_int64(array: &ArrayRef) -> Result<Vec<i64>, Box<dyn Error>> {
285 let num_rows = array.len();
286 let mut result = Vec::with_capacity(num_rows);
287 match array.data_type() {
288 DataType::Int32 => {
289 let arr = array.as_any().downcast_ref::<Int32Array>().unwrap();
290 for i in 0..num_rows {
291 result.push(if arr.is_null(i) {
292 0
293 } else {
294 arr.value(i) as i64
295 });
296 }
297 },
298 DataType::Int64 => {
299 let arr = array.as_any().downcast_ref::<Int64Array>().unwrap();
300 for i in 0..num_rows {
301 result.push(if arr.is_null(i) { 0 } else { arr.value(i) });
302 }
303 },
304 DataType::Float64 => {
305 let arr = array.as_any().downcast_ref::<Float64Array>().unwrap();
306 for i in 0..num_rows {
307 result.push(if arr.is_null(i) {
308 0
309 } else {
310 arr.value(i) as i64
311 });
312 }
313 },
314 dt => return Err(format!("Cannot cast {} to Int64", dt).into()),
315 }
316 Ok(result)
317}
318
319fn extract_scalar(array: &ArrayRef, row_idx: usize) -> Scalar {
321 if array.is_null(row_idx) {
322 return Scalar::Null;
323 }
324 match array.data_type() {
325 DataType::Dictionary(..) => dict_str_value(array, row_idx)
326 .map(|x| Scalar::String(x.to_string()))
327 .unwrap_or(Scalar::Null),
328 DataType::Float64 => {
329 let arr = array.as_any().downcast_ref::<Float64Array>().unwrap();
330 Scalar::Float(arr.value(row_idx))
331 },
332 DataType::Int32 => {
333 let arr = array.as_any().downcast_ref::<Int32Array>().unwrap();
334 Scalar::Float(arr.value(row_idx) as f64)
335 },
336 DataType::Boolean => {
337 let arr = array.as_any().downcast_ref::<BooleanArray>().unwrap();
338 Scalar::Bool(arr.value(row_idx))
339 },
340 DataType::Timestamp(TimeUnit::Millisecond, _) => {
341 let arr = array
342 .as_any()
343 .downcast_ref::<TimestampMillisecondArray>()
344 .unwrap();
345 Scalar::Float(arr.value(row_idx) as f64)
346 },
347 DataType::Date32 => {
348 let arr = array.as_any().downcast_ref::<Date32Array>().unwrap();
349 Scalar::Float(arr.value(row_idx) as f64 * 86_400_000.0)
350 },
351 dt => {
352 tracing::error!("Non-canonical row path type {}", dt);
353 Scalar::Null
354 },
355 }
356}
357
358fn timestamp_to_millis(array: &ArrayRef, unit: &TimeUnit) -> ArrayRef {
362 let millis: TimestampMillisecondArray = match unit {
363 TimeUnit::Second => {
364 let arr = array
365 .as_any()
366 .downcast_ref::<TimestampSecondArray>()
367 .unwrap();
368 arr.iter().map(|v| v.map(|v| v * 1_000)).collect()
369 },
370 TimeUnit::Microsecond => {
371 let arr = array
372 .as_any()
373 .downcast_ref::<TimestampMicrosecondArray>()
374 .unwrap();
375 arr.iter().map(|v| v.map(|v| v / 1_000)).collect()
376 },
377 TimeUnit::Nanosecond => {
378 let arr = array
379 .as_any()
380 .downcast_ref::<TimestampNanosecondArray>()
381 .unwrap();
382 arr.iter().map(|v| v.map(|v| v / 1_000_000)).collect()
383 },
384 TimeUnit::Millisecond => {
385 let arr = array
386 .as_any()
387 .downcast_ref::<TimestampMillisecondArray>()
388 .unwrap();
389
390 return Arc::new(arr.clone().with_timezone_opt(None::<Arc<str>>)) as ArrayRef;
391 },
392 };
393 Arc::new(millis) as ArrayRef
394}
395
396fn coerce_column(
397 name: &str,
398 field: &Field,
399 array: &ArrayRef,
400) -> Result<(Field, ArrayRef), Box<dyn Error>> {
401 match field.data_type() {
402 DataType::Boolean | DataType::Float64 | DataType::Int32 | DataType::Date32 => Ok((
403 Field::new(name, field.data_type().clone(), true),
404 array.clone(),
405 )),
406 DataType::Dictionary(key, value) => {
407 if key.as_ref() == &DataType::Int32 && value.as_ref() == &DataType::Utf8 {
408 return Ok((Field::new(name, dict_data_type(), true), array.clone()));
409 }
410
411 let dict = array
412 .as_any_dictionary_opt()
413 .ok_or_else(|| format!("Column '{}' is not a dictionary array", name))?;
414
415 let values = arrow_select::take::take(dict.values(), dict.keys(), None)?;
416 let field = Field::new(name, values.data_type().clone(), true);
417 coerce_column(name, &field, &values)
418 },
419 DataType::Utf8 => {
420 let arr = array.as_any().downcast_ref::<StringArray>().unwrap();
421 let mut builder = StringDictionaryBuilder::<Int32Type>::new();
422 for i in 0..arr.len() {
423 if arr.is_null(i) {
424 builder.append_null();
425 } else {
426 builder.append_value(arr.value(i));
427 }
428 }
429 Ok((
430 Field::new(name, dict_data_type(), true),
431 Arc::new(builder.finish()) as ArrayRef,
432 ))
433 },
434 DataType::Timestamp(TimeUnit::Millisecond, None) => Ok((
435 Field::new(name, DataType::Timestamp(TimeUnit::Millisecond, None), true),
436 array.clone(),
437 )),
438 DataType::Int8 => {
439 let arr = array.as_any().downcast_ref::<Int8Array>().unwrap();
440 let result: Int32Array = arr.iter().map(|v| v.map(|v| v as i32)).collect();
441 Ok((
442 Field::new(name, DataType::Int32, true),
443 Arc::new(result) as ArrayRef,
444 ))
445 },
446 DataType::Int16 => {
447 let arr = array.as_any().downcast_ref::<Int16Array>().unwrap();
448 let result: Int32Array = arr.iter().map(|v| v.map(|v| v as i32)).collect();
449 Ok((
450 Field::new(name, DataType::Int32, true),
451 Arc::new(result) as ArrayRef,
452 ))
453 },
454 DataType::UInt8 => {
455 let arr = array.as_any().downcast_ref::<UInt8Array>().unwrap();
456 let result: Int32Array = arr.iter().map(|v| v.map(|v| v as i32)).collect();
457 Ok((
458 Field::new(name, DataType::Int32, true),
459 Arc::new(result) as ArrayRef,
460 ))
461 },
462 DataType::UInt16 => {
463 let arr = array.as_any().downcast_ref::<UInt16Array>().unwrap();
464 let result: Int32Array = arr.iter().map(|v| v.map(|v| v as i32)).collect();
465 Ok((
466 Field::new(name, DataType::Int32, true),
467 Arc::new(result) as ArrayRef,
468 ))
469 },
470 DataType::UInt32 => {
471 let arr = array.as_any().downcast_ref::<UInt32Array>().unwrap();
472 let result: Int64Array = arr.iter().map(|v| v.map(|v| v as i64)).collect();
473 let result: Float64Array = result.iter().map(|v| v.map(|v| v as f64)).collect();
474 Ok((
475 Field::new(name, DataType::Float64, true),
476 Arc::new(result) as ArrayRef,
477 ))
478 },
479 DataType::Int64 => {
480 let arr = array.as_any().downcast_ref::<Int64Array>().unwrap();
481 let result: Float64Array = arr.iter().map(|v| v.map(|v| v as f64)).collect();
482 Ok((
483 Field::new(name, DataType::Float64, true),
484 Arc::new(result) as ArrayRef,
485 ))
486 },
487 DataType::UInt64 => {
488 let arr = array.as_any().downcast_ref::<UInt64Array>().unwrap();
489 let result: Float64Array = arr.iter().map(|v| v.map(|v| v as f64)).collect();
490 Ok((
491 Field::new(name, DataType::Float64, true),
492 Arc::new(result) as ArrayRef,
493 ))
494 },
495 DataType::Float32 => {
496 let arr = array.as_any().downcast_ref::<Float32Array>().unwrap();
497 let result: Float64Array = arr.iter().map(|v| v.map(|v| v as f64)).collect();
498 Ok((
499 Field::new(name, DataType::Float64, true),
500 Arc::new(result) as ArrayRef,
501 ))
502 },
503 DataType::Float16 => {
504 let arr = array.as_any().downcast_ref::<Float16Array>().unwrap();
505 let result: Float64Array = arr.iter().map(|v| v.map(|v| v.to_f64())).collect();
506 Ok((
507 Field::new(name, DataType::Float64, true),
508 Arc::new(result) as ArrayRef,
509 ))
510 },
511 DataType::Decimal128(_, scale) => {
512 let scale = *scale;
513 let arr = array.as_any().downcast_ref::<Decimal128Array>().unwrap();
514 let divisor = 10_f64.powi(scale as i32);
515 let result: Float64Array = arr.iter().map(|v| v.map(|v| v as f64 / divisor)).collect();
516 Ok((
517 Field::new(name, DataType::Float64, true),
518 Arc::new(result) as ArrayRef,
519 ))
520 },
521 DataType::Date64 => {
522 let arr = array.as_any().downcast_ref::<Date64Array>().unwrap();
523 let result: Date32Array = arr
524 .iter()
525 .map(|v| v.map(|v| (v / 86_400_000) as i32))
526 .collect();
527 Ok((
528 Field::new(name, DataType::Date32, true),
529 Arc::new(result) as ArrayRef,
530 ))
531 },
532 DataType::Timestamp(unit, _) => {
533 let casted = timestamp_to_millis(array, unit);
534 Ok((
535 Field::new(name, DataType::Timestamp(TimeUnit::Millisecond, None), true),
536 casted,
537 ))
538 },
539 DataType::Time32(TimeUnit::Second) => {
540 let arr = array.as_any().downcast_ref::<Time32SecondArray>().unwrap();
541 let result: TimestampMillisecondArray =
542 arr.iter().map(|v| v.map(|v| v as i64 * 1_000)).collect();
543 Ok((
544 Field::new(name, DataType::Timestamp(TimeUnit::Millisecond, None), true),
545 Arc::new(result) as ArrayRef,
546 ))
547 },
548 DataType::Time32(TimeUnit::Millisecond) => {
549 let arr = array
550 .as_any()
551 .downcast_ref::<Time32MillisecondArray>()
552 .unwrap();
553 let result: TimestampMillisecondArray =
554 arr.iter().map(|v| v.map(|v| v as i64)).collect();
555 Ok((
556 Field::new(name, DataType::Timestamp(TimeUnit::Millisecond, None), true),
557 Arc::new(result) as ArrayRef,
558 ))
559 },
560 DataType::Time64(TimeUnit::Microsecond) => {
561 let arr = array
562 .as_any()
563 .downcast_ref::<Time64MicrosecondArray>()
564 .unwrap();
565 let result: TimestampMillisecondArray =
566 arr.iter().map(|v| v.map(|v| v / 1_000)).collect();
567 Ok((
568 Field::new(name, DataType::Timestamp(TimeUnit::Millisecond, None), true),
569 Arc::new(result) as ArrayRef,
570 ))
571 },
572 DataType::Time64(TimeUnit::Nanosecond) => {
573 let arr = array
574 .as_any()
575 .downcast_ref::<Time64NanosecondArray>()
576 .unwrap();
577 let result: TimestampMillisecondArray =
578 arr.iter().map(|v| v.map(|v| v / 1_000_000)).collect();
579 Ok((
580 Field::new(name, DataType::Timestamp(TimeUnit::Millisecond, None), true),
581 Arc::new(result) as ArrayRef,
582 ))
583 },
584 DataType::LargeUtf8 => {
585 let arr = array.as_any().downcast_ref::<LargeStringArray>().unwrap();
586 let mut builder = StringDictionaryBuilder::<Int32Type>::new();
587 for i in 0..arr.len() {
588 if arr.is_null(i) {
589 builder.append_null();
590 } else {
591 builder.append_value(arr.value(i));
592 }
593 }
594 Ok((
595 Field::new(name, dict_data_type(), true),
596 Arc::new(builder.finish()) as ArrayRef,
597 ))
598 },
599 dt => {
600 tracing::warn!(
601 "Coercing unknown Arrow type {} to Dictionary for column '{}'",
602 dt,
603 name
604 );
605 let num_rows = array.len();
606 let mut builder = StringDictionaryBuilder::<Int32Type>::new();
607 for i in 0..num_rows {
608 if array.is_null(i) {
609 builder.append_null();
610 } else {
611 let scalar_arr = array.slice(i, 1);
612 builder.append_value(format!("{:?}", scalar_arr));
613 }
614 }
615 Ok((
616 Field::new(name, dict_data_type(), true),
617 Arc::new(builder.finish()) as ArrayRef,
618 ))
619 },
620 }
621}
622
623impl VirtualDataSlice {
624 pub fn new(config: ViewConfig) -> Self {
625 VirtualDataSlice {
626 config,
627 builders: IndexMap::default(),
628 row_path: None,
629 frozen: None,
630 }
631 }
632
633 pub fn from_arrow_ipc(&mut self, ipc: &[u8]) -> Result<(), Box<dyn Error>> {
646 let cursor = std::io::Cursor::new(ipc);
647 let (ipc_schema, batches) = if &ipc[0..6] == "ARROW1".as_bytes() {
648 let reader = FileReader::try_new(cursor, None)?;
649 let schema = reader.schema();
650 (schema, reader.collect::<Result<Vec<_>, _>>()?)
651 } else {
652 let reader = StreamReader::try_new(cursor, None)?;
653 let schema = reader.schema();
654 (schema, reader.collect::<Result<Vec<_>, _>>()?)
655 };
656
657 let batch = match batches.len() {
658 0 => RecordBatch::new_empty(ipc_schema),
659 1 => batches.into_iter().next().unwrap(),
660 _ => arrow_select::concat::concat_batches(&batches[0].schema(), &batches)?,
661 };
662
663 let has_group_by = !self.config.group_by.is_empty();
664 let num_rows = batch.num_rows();
665 let schema = batch.schema();
666
667 let coerced = schema
668 .fields()
669 .iter()
670 .enumerate()
671 .map(|(col_idx, field)| coerce_column(field.name(), field, batch.column(col_idx)))
672 .collect::<Result<Vec<_>, _>>()?;
673
674 if has_group_by {
676 let group_by_len = self.config.group_by.len();
677 let is_flat = self.config.group_rollup_mode == GroupRollupMode::Flat;
678 let grouping_ids = if is_flat {
679 None
680 } else {
681 let grouping_id_idx = schema
682 .index_of("__GROUPING_ID__")
683 .map_err(|_| "Missing __GROUPING_ID__ column")?;
684 Some(cast_to_int64(&coerced[grouping_id_idx].1)?)
685 };
686
687 let mut row_paths: Vec<Vec<Scalar>> = (0..num_rows).map(|_| Vec::new()).collect();
688 for gidx in 0..group_by_len {
689 let col_name = format!("__ROW_PATH_{}__", gidx);
690 let col_idx = schema
691 .index_of(&col_name)
692 .map_err(|_| format!("Missing {} column", col_name))?;
693
694 let col = &coerced[col_idx].1;
695
696 if is_flat {
698 #[allow(clippy::needless_range_loop)]
702 for row_idx in 0..num_rows {
703 row_paths[row_idx].push(extract_scalar(col, row_idx));
704 }
705 } else {
706 let gids = grouping_ids.as_ref().unwrap();
707 let max_grouping_id = 2_i64.pow(group_by_len as u32 - gidx as u32) - 1;
708 for row_idx in 0..num_rows {
709 if gids[row_idx] < max_grouping_id {
710 row_paths[row_idx].push(extract_scalar(col, row_idx));
711 }
712 }
713 }
714 }
715
716 self.row_path = Some(row_paths);
717 }
718
719 let mut new_fields = Vec::new();
720 let mut new_arrays: Vec<ArrayRef> = Vec::new();
721 for (field, array) in coerced {
722 let name = field.name();
723 if name == "__GROUPING_ID__" {
739 continue;
740 }
741
742 new_fields.push(field);
743 new_arrays.push(array);
744 }
745
746 let new_schema = Arc::new(Schema::new(new_fields));
747 self.frozen = Some(if new_arrays.is_empty() {
748 let options = RecordBatchOptions::new().with_row_count(Some(num_rows));
749 RecordBatch::try_new_with_options(new_schema, new_arrays, &options)?
750 } else {
751 RecordBatch::try_new(new_schema, new_arrays)?
752 });
753
754 Ok(())
755 }
756
757 pub(crate) fn freeze(&mut self) -> &RecordBatch {
760 if self.frozen.is_none() {
761 let mut fields = Vec::new();
762 let mut arrays: Vec<ArrayRef> = Vec::new();
763
764 for (name, builder) in &mut self.builders {
765 let (field, array): (Field, ArrayRef) = match builder {
766 ColumnBuilder::Boolean(b) => (
767 Field::new(name, DataType::Boolean, true),
768 Arc::new(b.finish()),
769 ),
770 ColumnBuilder::String(b) => (
771 Field::new(name, dict_data_type(), true),
772 Arc::new(b.finish()),
773 ),
774 ColumnBuilder::Float(b) => (
775 Field::new(name, DataType::Float64, true),
776 Arc::new(b.finish()),
777 ),
778 ColumnBuilder::Integer(b) => (
779 Field::new(name, DataType::Int32, true),
780 Arc::new(b.finish()),
781 ),
782 ColumnBuilder::Datetime(b) => (
783 Field::new(name, DataType::Timestamp(TimeUnit::Millisecond, None), true),
784 Arc::new(b.finish()),
785 ),
786 };
787 fields.push(field);
788 arrays.push(array);
789 }
790
791 let schema = Arc::new(Schema::new(fields));
792 let batch = if arrays.is_empty() {
793 let num_rows = self.row_path.as_ref().map(|x| x.len()).unwrap_or(0);
794 let options = RecordBatchOptions::new().with_row_count(Some(num_rows));
795 RecordBatch::try_new_with_options(schema, arrays, &options)
796 } else {
797 RecordBatch::try_new(schema, arrays)
798 };
799
800 self.frozen = Some(
801 batch.expect("RecordBatch construction should not fail for well-formed builders"),
802 );
803 }
804
805 self.frozen.as_ref().unwrap()
806 }
807
808 pub(crate) fn render_to_arrow_ipc(&mut self) -> Result<Vec<u8>, Box<dyn Error>> {
810 let batch = self.freeze().clone();
811 let schema = batch.schema();
812 let mut buf = Vec::new();
813 {
814 let mut writer = StreamWriter::try_new(&mut buf, &schema)?;
815 writer.write(&batch)?;
816 writer.finish()?;
817 }
818 Ok(buf)
819 }
820
821 pub(crate) fn render_to_rows(
829 &mut self,
830 style: RowPathStyle,
831 ) -> Vec<IndexMap<String, VirtualDataCell>> {
832 let batch = self.freeze().clone();
833 let num_rows = batch.num_rows();
834 let schema = batch.schema();
835
836 let synthesize_row_path = style == RowPathStyle::PerLevel
837 && self.row_path.is_some()
838 && !schema
839 .fields()
840 .iter()
841 .any(|x| x.name().starts_with("__ROW_PATH_"));
842
843 (0..num_rows)
844 .map(|row_idx| {
845 let mut row = IndexMap::new();
846 if style == RowPathStyle::Sidecar
847 && let Some(ref rp) = self.row_path
848 && row_idx < rp.len()
849 {
850 row.insert(
851 "__ROW_PATH__".to_string(),
852 VirtualDataCell::RowPath(rp[row_idx].clone()),
853 );
854 }
855
856 if synthesize_row_path
857 && let Some(ref rp) = self.row_path
858 && row_idx < rp.len()
859 {
860 for level in 0..self.config.group_by.len() {
861 row.insert(
862 format!("__ROW_PATH_{}__", level),
863 match rp[row_idx].get(level) {
864 Some(Scalar::String(x)) => VirtualDataCell::String(Some(x.clone())),
865 Some(Scalar::Float(x)) => VirtualDataCell::Float(Some(*x)),
866 Some(Scalar::Bool(x)) => VirtualDataCell::Boolean(Some(*x)),
867 Some(Scalar::Null) | None => VirtualDataCell::String(None),
868 },
869 );
870 }
871 }
872
873 for (col_idx, field) in schema.fields().iter().enumerate() {
874 if style == RowPathStyle::Sidecar && field.name().starts_with("__ROW_PATH_") {
875 continue;
876 }
877
878 let col = batch.column(col_idx);
879 let cell = if col.is_null(row_idx) {
880 match field.data_type() {
881 DataType::Boolean => VirtualDataCell::Boolean(None),
882 DataType::Utf8 | DataType::Dictionary(..) => {
883 VirtualDataCell::String(None)
884 },
885 DataType::Float64 => VirtualDataCell::Float(None),
886 DataType::Int32 => VirtualDataCell::Integer(None),
887 DataType::Timestamp(TimeUnit::Millisecond, _) => {
888 VirtualDataCell::Datetime(None)
889 },
890 _ => continue,
891 }
892 } else {
893 match field.data_type() {
894 DataType::Boolean => {
895 let arr = col.as_any().downcast_ref::<BooleanArray>().unwrap();
896 VirtualDataCell::Boolean(Some(arr.value(row_idx)))
897 },
898 DataType::Utf8 => {
899 let arr = col.as_any().downcast_ref::<StringArray>().unwrap();
900 VirtualDataCell::String(Some(arr.value(row_idx).to_string()))
901 },
902 DataType::Dictionary(..) => VirtualDataCell::String(
903 dict_str_value(col, row_idx).map(|x| x.to_string()),
904 ),
905 DataType::Float64 => {
906 let arr = col.as_any().downcast_ref::<Float64Array>().unwrap();
907 VirtualDataCell::Float(Some(arr.value(row_idx)))
908 },
909 DataType::Int32 => {
910 let arr = col.as_any().downcast_ref::<Int32Array>().unwrap();
911 VirtualDataCell::Integer(Some(arr.value(row_idx)))
912 },
913 DataType::Int64 => {
914 let arr = col.as_any().downcast_ref::<Int64Array>().unwrap();
916 VirtualDataCell::Float(Some(arr.value(row_idx) as f64))
917 },
918 DataType::Time64(TimeUnit::Microsecond) => {
919 let arr = col
920 .as_any()
921 .downcast_ref::<Time64MicrosecondArray>()
922 .unwrap();
923 VirtualDataCell::Float(Some(arr.value(row_idx) as f64))
924 },
925 DataType::Timestamp(TimeUnit::Microsecond, _) => {
926 let arr = col
927 .as_any()
928 .downcast_ref::<Time64MicrosecondArray>()
929 .unwrap();
930 VirtualDataCell::Datetime(Some(arr.value(row_idx) * 1000))
931 },
932 DataType::Timestamp(TimeUnit::Millisecond, _) => {
933 let arr = col
934 .as_any()
935 .downcast_ref::<TimestampMillisecondArray>()
936 .unwrap();
937 VirtualDataCell::Datetime(Some(arr.value(row_idx)))
938 },
939 DataType::Date32 => {
940 let arr = col.as_any().downcast_ref::<Date32Array>().unwrap();
941 VirtualDataCell::Datetime(Some(
942 arr.value(row_idx) as i64 * 86_400_000,
943 ))
944 },
945 x => {
946 tracing::error!("Unknown Arrow IPC type {}", x);
947 continue;
948 },
949 }
950 };
951 row.insert(field.name().clone(), cell);
952 }
953
954 row
955 })
956 .collect()
957 }
958
959 pub fn render_to_columns_json(
973 &mut self,
974 style: RowPathStyle,
975 id: bool,
976 ) -> Result<String, Box<dyn Error>> {
977 let batch = self.freeze().clone();
978 let schema = batch.schema();
979 let mut map = serde_json::Map::new();
980
981 if let Some(ref rp) = self.row_path {
982 if style == RowPathStyle::Sidecar {
983 map.insert("__ROW_PATH__".to_string(), serde_json::to_value(rp)?);
984 }
985
986 if id {
987 map.insert("__ID__".to_string(), serde_json::to_value(rp)?);
988 }
989 }
990
991 for (col_idx, field) in schema.fields().iter().enumerate() {
992 if style == RowPathStyle::Sidecar && field.name().starts_with("__ROW_PATH_") {
993 continue;
994 }
995
996 let col = batch.column(col_idx);
997 let num_rows = col.len();
998 let values: serde_json::Value = match field.data_type() {
999 DataType::Boolean => {
1000 let arr = col.as_any().downcast_ref::<BooleanArray>().unwrap();
1001 serde_json::to_value(
1002 (0..num_rows)
1003 .map(|i| {
1004 if arr.is_null(i) {
1005 None
1006 } else {
1007 Some(arr.value(i))
1008 }
1009 })
1010 .collect::<Vec<_>>(),
1011 )?
1012 },
1013 DataType::Utf8 => {
1014 let arr = col.as_any().downcast_ref::<StringArray>().unwrap();
1015 serde_json::to_value(
1016 (0..num_rows)
1017 .map(|i| {
1018 if arr.is_null(i) {
1019 None
1020 } else {
1021 Some(arr.value(i))
1022 }
1023 })
1024 .collect::<Vec<_>>(),
1025 )?
1026 },
1027 DataType::Dictionary(..) => serde_json::to_value(
1028 (0..num_rows)
1029 .map(|i| dict_str_value(col, i))
1030 .collect::<Vec<_>>(),
1031 )?,
1032 DataType::Float64 => {
1033 let arr = col.as_any().downcast_ref::<Float64Array>().unwrap();
1034 serde_json::to_value(
1035 (0..num_rows)
1036 .map(|i| {
1037 if arr.is_null(i) {
1038 None
1039 } else {
1040 Some(arr.value(i))
1041 }
1042 })
1043 .collect::<Vec<_>>(),
1044 )?
1045 },
1046 DataType::Int32 => {
1047 let arr = col.as_any().downcast_ref::<Int32Array>().unwrap();
1048 serde_json::to_value(
1049 (0..num_rows)
1050 .map(|i| {
1051 if arr.is_null(i) {
1052 None
1053 } else {
1054 Some(arr.value(i))
1055 }
1056 })
1057 .collect::<Vec<_>>(),
1058 )?
1059 },
1060 DataType::Int64 => {
1061 let arr = col.as_any().downcast_ref::<Int64Array>().unwrap();
1062 serde_json::to_value(
1063 (0..num_rows)
1064 .map(|i| {
1065 if arr.is_null(i) {
1066 None
1067 } else {
1068 Some(arr.value(i) as f64)
1069 }
1070 })
1071 .collect::<Vec<_>>(),
1072 )?
1073 },
1074 DataType::Timestamp(TimeUnit::Millisecond, _) => {
1075 let arr = col
1076 .as_any()
1077 .downcast_ref::<TimestampMillisecondArray>()
1078 .unwrap();
1079 serde_json::to_value(
1080 (0..num_rows)
1081 .map(|i| {
1082 if arr.is_null(i) {
1083 None
1084 } else {
1085 Some(arr.value(i))
1086 }
1087 })
1088 .collect::<Vec<_>>(),
1089 )?
1090 },
1091 DataType::Time64(TimeUnit::Microsecond) => {
1092 let arr = col
1093 .as_any()
1094 .downcast_ref::<Time64MicrosecondArray>()
1095 .unwrap();
1096 serde_json::to_value(
1097 (0..num_rows)
1098 .map(|i| {
1099 if arr.is_null(i) {
1100 None
1101 } else {
1102 Some(arr.value(i) as f64)
1103 }
1104 })
1105 .collect::<Vec<_>>(),
1106 )?
1107 },
1108 DataType::Date32 => {
1109 let arr = col.as_any().downcast_ref::<Date32Array>().unwrap();
1110 serde_json::to_value(
1111 (0..num_rows)
1112 .map(|i| {
1113 if arr.is_null(i) {
1114 None
1115 } else {
1116 Some(arr.value(i) as i64 * 86_400_000)
1117 }
1118 })
1119 .collect::<Vec<_>>(),
1120 )?
1121 },
1122 x => {
1123 tracing::error!("Unknown Arrow IPC type {}", x);
1124 continue;
1125 },
1126 };
1127 map.insert(field.name().clone(), values);
1128 }
1129
1130 Ok(serde_json::to_string(&map)?)
1131 }
1132
1133 pub fn set_col<T: SetVirtualDataColumn>(
1141 &mut self,
1142 name: &str,
1143 grouping_id: Option<usize>,
1144 index: usize,
1145 value: T,
1146 ) -> Result<(), Box<dyn Error>> {
1147 if name == "__GROUPING_ID__" {
1148 return Ok(());
1149 }
1150
1151 if name.starts_with("__ROW_PATH_") {
1152 let group_by_index: u32 = name[11..name.len() - 2].parse()?;
1153 let max_grouping_id =
1154 2_i32.pow((self.config.group_by.len() as u32) - group_by_index) - 1;
1155
1156 if grouping_id.map(|x| x as i32).unwrap_or(i32::MAX) < max_grouping_id {
1157 let col = self.row_path.get_or_insert_with(Vec::new);
1158 if let Some(row) = col.get_mut(index) {
1159 let scalar = value.to_scalar();
1160 row.push(scalar);
1161 } else {
1162 while col.len() < index {
1163 col.push(vec![])
1164 }
1165
1166 let scalar = value.to_scalar();
1167 col.push(vec![scalar]);
1168 }
1169 }
1170
1171 Ok(())
1172 } else {
1173 if !self.builders.contains_key(name) {
1174 self.builders.insert(name.to_owned(), T::new_builder());
1175 }
1176
1177 let col = self
1178 .builders
1179 .get_mut(name)
1180 .ok_or_else(|| format!("Column '{}' not found after insertion", name))?;
1181
1182 Ok(value.write_to(col)?)
1183 }
1184 }
1185}