1use std::collections::HashMap;
16use std::slice;
17use std::sync::Arc;
18
19use datafusion::arrow::util::pretty::pretty_format_batches;
20use datafusion_common::arrow::array::ArrayRef;
21use datafusion_common::arrow::compute;
22use datafusion_common::arrow::datatypes::{DataType as ArrowDataType, SchemaRef as ArrowSchemaRef};
23use datatypes::arrow::array::{Array, AsArray, RecordBatchOptions};
24use datatypes::extension::json::is_structured_json_field;
25use datatypes::prelude::DataType;
26use datatypes::schema::SchemaRef;
27use datatypes::vectors::json::array::JsonArray;
28use datatypes::vectors::{Helper, VectorRef};
29use serde::ser::{Error, SerializeStruct};
30use serde::{Serialize, Serializer};
31use snafu::{OptionExt, ResultExt, ensure};
32
33use crate::DfRecordBatch;
34use crate::error::{
35 self, ArrowComputeSnafu, ColumnNotExistsSnafu, DataTypesSnafu, ProjectArrowRecordBatchSnafu,
36 Result,
37};
38
39#[derive(Clone, Debug, PartialEq)]
41pub struct RecordBatch {
42 pub schema: SchemaRef,
43 df_record_batch: DfRecordBatch,
44}
45
46impl RecordBatch {
47 pub fn new<I: IntoIterator<Item = VectorRef>>(
49 schema: SchemaRef,
50 columns: I,
51 ) -> Result<RecordBatch> {
52 let columns: Vec<_> = columns.into_iter().collect();
53 let arrow_arrays = columns.iter().map(|v| v.to_arrow_array()).collect();
54
55 let arrow_arrays = Self::cast_view_arrays(schema.arrow_schema(), arrow_arrays)?;
63
64 let arrow_arrays = maybe_align_json_array_with_schema(schema.arrow_schema(), arrow_arrays)?;
65
66 let df_record_batch = DfRecordBatch::try_new(schema.arrow_schema().clone(), arrow_arrays)
67 .context(error::NewDfRecordBatchSnafu)?;
68
69 Ok(RecordBatch {
70 schema,
71 df_record_batch,
72 })
73 }
74
75 pub fn to_df_record_batch<I: IntoIterator<Item = VectorRef>>(
76 arrow_schema: ArrowSchemaRef,
77 columns: I,
78 ) -> Result<DfRecordBatch> {
79 let columns: Vec<_> = columns.into_iter().collect();
80 let arrow_arrays = columns.iter().map(|v| v.to_arrow_array()).collect();
81
82 let arrow_arrays = Self::cast_view_arrays(&arrow_schema, arrow_arrays)?;
90
91 let arrow_arrays = maybe_align_json_array_with_schema(&arrow_schema, arrow_arrays)?;
92
93 let df_record_batch = DfRecordBatch::try_new(arrow_schema, arrow_arrays)
94 .context(error::NewDfRecordBatchSnafu)?;
95
96 Ok(df_record_batch)
97 }
98
99 fn cast_view_arrays(
100 schema: &ArrowSchemaRef,
101 mut arrays: Vec<ArrayRef>,
102 ) -> Result<Vec<ArrayRef>> {
103 for (f, a) in schema.fields().iter().zip(arrays.iter_mut()) {
104 let expected = f.data_type();
105 let actual = a.data_type();
106 if matches!(
107 (expected, actual),
108 (ArrowDataType::Utf8View, ArrowDataType::Utf8)
109 | (ArrowDataType::BinaryView, ArrowDataType::Binary)
110 ) {
111 *a = compute::cast(a, expected).context(ArrowComputeSnafu)?;
112 }
113 }
114 Ok(arrays)
115 }
116
117 pub fn new_empty(schema: SchemaRef) -> RecordBatch {
119 let df_record_batch = DfRecordBatch::new_empty(schema.arrow_schema().clone());
120 RecordBatch {
121 schema,
122 df_record_batch,
123 }
124 }
125
126 pub fn new_with_count(schema: SchemaRef, num_rows: usize) -> Result<Self> {
128 let df_record_batch = DfRecordBatch::try_new_with_options(
129 schema.arrow_schema().clone(),
130 vec![],
131 &RecordBatchOptions::new().with_row_count(Some(num_rows)),
132 )
133 .context(error::NewDfRecordBatchSnafu)?;
134 Ok(RecordBatch {
135 schema,
136 df_record_batch,
137 })
138 }
139
140 pub fn try_project(&self, indices: &[usize]) -> Result<Self> {
141 let schema = Arc::new(self.schema.try_project(indices).context(DataTypesSnafu)?);
142 let df_record_batch = self.df_record_batch.project(indices).with_context(|_| {
143 ProjectArrowRecordBatchSnafu {
144 schema: self.schema.clone(),
145 projection: indices.to_vec(),
146 }
147 })?;
148
149 Ok(Self {
150 schema,
151 df_record_batch,
152 })
153 }
154
155 pub fn from_df_record_batch(schema: SchemaRef, df_record_batch: DfRecordBatch) -> RecordBatch {
159 RecordBatch {
160 schema,
161 df_record_batch,
162 }
163 }
164
165 #[inline]
166 pub fn df_record_batch(&self) -> &DfRecordBatch {
167 &self.df_record_batch
168 }
169
170 #[inline]
171 pub fn into_df_record_batch(self) -> DfRecordBatch {
172 self.df_record_batch
173 }
174
175 #[inline]
176 pub fn columns(&self) -> &[ArrayRef] {
177 self.df_record_batch.columns()
178 }
179
180 #[inline]
181 pub fn column(&self, idx: usize) -> &ArrayRef {
182 self.df_record_batch.column(idx)
183 }
184
185 pub fn column_by_name(&self, name: &str) -> Option<&ArrayRef> {
186 self.df_record_batch.column_by_name(name)
187 }
188
189 #[inline]
190 pub fn num_columns(&self) -> usize {
191 self.df_record_batch.num_columns()
192 }
193
194 #[inline]
195 pub fn num_rows(&self) -> usize {
196 self.df_record_batch.num_rows()
197 }
198
199 pub fn column_vectors(
200 &self,
201 table_name: &str,
202 table_schema: SchemaRef,
203 ) -> Result<HashMap<String, VectorRef>> {
204 let mut vectors = HashMap::with_capacity(self.num_columns());
205
206 for (field, array) in self
208 .df_record_batch
209 .schema()
210 .fields()
211 .iter()
212 .zip(self.df_record_batch.columns().iter())
213 {
214 let column_name = field.name();
215 let column_schema =
216 table_schema
217 .column_schema_by_name(column_name)
218 .context(ColumnNotExistsSnafu {
219 table_name,
220 column_name,
221 })?;
222 let vector = if field.data_type() != &column_schema.data_type.as_arrow_type() {
223 let array = compute::cast(array, &column_schema.data_type.as_arrow_type())
224 .context(ArrowComputeSnafu)?;
225 Helper::try_into_vector(array).context(DataTypesSnafu)?
226 } else {
227 Helper::try_into_vector(array).context(DataTypesSnafu)?
228 };
229
230 let _ = vectors.insert(column_name.clone(), vector);
231 }
232
233 Ok(vectors)
234 }
235
236 pub fn pretty_print(&self) -> String {
238 pretty_format_batches(slice::from_ref(&self.df_record_batch))
239 .map(|t| t.to_string())
240 .unwrap_or("failed to pretty display a record batch".to_string())
241 }
242
243 pub fn slice(&self, offset: usize, len: usize) -> Result<RecordBatch> {
245 ensure!(
246 offset + len <= self.num_rows(),
247 error::RecordBatchSliceIndexOverflowSnafu {
248 size: self.num_rows(),
249 visit_index: offset + len
250 }
251 );
252 let sliced = self.df_record_batch.slice(offset, len);
253 Ok(RecordBatch::from_df_record_batch(
254 self.schema.clone(),
255 sliced,
256 ))
257 }
258
259 pub fn buffer_memory_size(&self) -> usize {
265 self.df_record_batch
266 .columns()
267 .iter()
268 .map(|array| array.get_buffer_memory_size())
269 .sum()
270 }
271
272 pub fn logical_slice_memory_size(&self) -> usize {
282 self.df_record_batch
283 .columns()
284 .iter()
285 .fold(0, |total, array| {
286 let array_size = array
287 .to_data()
288 .get_slice_memory_size()
289 .unwrap_or_else(|_| array.get_buffer_memory_size());
290 total.saturating_add(array_size)
291 })
292 }
293
294 pub fn iter_column_as_string(&self, i: usize) -> Box<dyn Iterator<Item = Option<String>> + '_> {
302 macro_rules! iter {
303 ($column: ident) => {
304 Box::new(
305 (0..$column.len())
306 .map(|i| $column.is_valid(i).then(|| $column.value(i).to_string())),
307 )
308 };
309 }
310
311 let column = self.df_record_batch.column(i);
312 match column.data_type() {
313 ArrowDataType::Utf8 => {
314 let column = column.as_string::<i32>();
315 let iter = iter!(column);
316 iter as _
317 }
318 ArrowDataType::LargeUtf8 => {
319 let column = column.as_string::<i64>();
320 iter!(column)
321 }
322 ArrowDataType::Utf8View => {
323 let column = column.as_string_view();
324 iter!(column)
325 }
326 _ => {
327 if let Ok(column) = Helper::try_into_vector(column) {
328 Box::new(
329 (0..column.len())
330 .map(move |i| (!column.is_null(i)).then(|| column.get(i).to_string())),
331 )
332 } else {
333 Box::new(std::iter::empty())
334 }
335 }
336 }
337 }
338}
339
340impl Serialize for RecordBatch {
341 fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
342 where
343 S: Serializer,
344 {
345 let mut s = serializer.serialize_struct("record", 2)?;
348 s.serialize_field("schema", &**self.schema.arrow_schema())?;
349
350 let columns = self.df_record_batch.columns();
351 let columns = Helper::try_into_vectors(columns).map_err(Error::custom)?;
352 let vec = columns
353 .iter()
354 .map(|c| c.serialize_to_json())
355 .collect::<std::result::Result<Vec<_>, _>>()
356 .map_err(S::Error::custom)?;
357
358 s.serialize_field("columns", &vec)?;
359 s.end()
360 }
361}
362
363pub fn merge_record_batches(schema: SchemaRef, batches: &[RecordBatch]) -> Result<RecordBatch> {
365 let batches_len = batches.len();
366 if batches_len == 0 {
367 return Ok(RecordBatch::new_empty(schema));
368 }
369
370 let record_batch = compute::concat_batches(
371 schema.arrow_schema(),
372 batches.iter().map(|x| x.df_record_batch()),
373 )
374 .context(ArrowComputeSnafu)?;
375
376 Ok(RecordBatch::from_df_record_batch(schema, record_batch))
378}
379
380fn maybe_align_json_array_with_schema(
381 schema: &ArrowSchemaRef,
382 arrays: Vec<ArrayRef>,
383) -> Result<Vec<ArrayRef>> {
384 if schema.fields().iter().all(|f| !is_structured_json_field(f)) {
385 return Ok(arrays);
386 }
387
388 let mut aligned = Vec::with_capacity(arrays.len());
389 for (field, array) in schema.fields().iter().zip(arrays) {
390 if !is_structured_json_field(field) {
391 aligned.push(array);
392 continue;
393 }
394
395 let json_array = JsonArray::from(&array)
396 .try_align(field.data_type())
397 .context(DataTypesSnafu)?;
398 aligned.push(json_array);
399 }
400 Ok(aligned)
401}
402
403#[cfg(test)]
404mod tests {
405 use std::sync::Arc;
406
407 use datatypes::arrow::array::{
408 AsArray, BinaryArray, StringArray, StringViewArray, UInt32Array,
409 };
410 use datatypes::arrow::datatypes::{DataType, Field, Schema as ArrowSchema, UInt32Type};
411 use datatypes::data_type::ConcreteDataType;
412 use datatypes::extension::json::{JsonExtensionType, JsonMetadata};
413 use datatypes::schema::{ColumnSchema, Schema};
414 use datatypes::vectors::{BinaryVector, StringVector, UInt32Vector};
415
416 use super::*;
417
418 #[test]
419 fn test_record_batch() {
420 let arrow_schema = Arc::new(ArrowSchema::new(vec![
421 Field::new("c1", DataType::UInt32, false),
422 Field::new("c2", DataType::UInt32, false),
423 ]));
424 let schema = Arc::new(Schema::try_from(arrow_schema).unwrap());
425
426 let c1 = Arc::new(UInt32Vector::from_slice([1, 2, 3]));
427 let c2 = Arc::new(UInt32Vector::from_slice([4, 5, 6]));
428 let columns: Vec<VectorRef> = vec![c1, c2];
429
430 let expected = vec![
431 Arc::new(UInt32Array::from_iter_values([1, 2, 3])) as ArrayRef,
432 Arc::new(UInt32Array::from_iter_values([4, 5, 6])),
433 ];
434
435 let batch = RecordBatch::new(schema.clone(), columns.clone()).unwrap();
436 assert_eq!(3, batch.num_rows());
437 assert_eq!(expected, batch.df_record_batch().columns());
438 assert_eq!(schema, batch.schema);
439
440 assert_eq!(&expected[0], batch.column_by_name("c1").unwrap());
441 assert_eq!(&expected[1], batch.column_by_name("c2").unwrap());
442 assert!(batch.column_by_name("c3").is_none());
443
444 let converted = RecordBatch::from_df_record_batch(schema, batch.df_record_batch().clone());
445 assert_eq!(batch, converted);
446 assert_eq!(*batch.df_record_batch(), converted.into_df_record_batch());
447 }
448
449 #[test]
450 pub fn test_serialize_recordbatch() {
451 let column_schemas = vec![ColumnSchema::new(
452 "number",
453 ConcreteDataType::uint32_datatype(),
454 false,
455 )];
456 let schema = Arc::new(Schema::try_new(column_schemas).unwrap());
457
458 let numbers: Vec<u32> = (0..10).collect();
459 let columns = vec![Arc::new(UInt32Vector::from_slice(numbers)) as VectorRef];
460 let batch = RecordBatch::new(schema, columns).unwrap();
461
462 let output = serde_json::to_string(&batch).unwrap();
463 assert_eq!(
464 r#"{"schema":{"fields":[{"name":"number","data_type":"UInt32","nullable":false,"dict_id":0,"dict_is_ordered":false,"metadata":{}}],"metadata":{"greptime:version":"0"}},"columns":[[0,1,2,3,4,5,6,7,8,9]]}"#,
465 output
466 );
467 }
468
469 #[test]
470 fn test_record_batch_slice() {
471 let column_schemas = vec![
472 ColumnSchema::new("numbers", ConcreteDataType::uint32_datatype(), false),
473 ColumnSchema::new("strings", ConcreteDataType::string_datatype(), true),
474 ];
475 let schema = Arc::new(Schema::new(column_schemas));
476 let columns: Vec<VectorRef> = vec![
477 Arc::new(UInt32Vector::from_slice(vec![1, 2, 3, 4])),
478 Arc::new(StringVector::from(vec![
479 None,
480 Some("hello"),
481 Some("greptime"),
482 None,
483 ])),
484 ];
485 let recordbatch = RecordBatch::new(schema, columns).unwrap();
486 let recordbatch = recordbatch.slice(1, 2).expect("recordbatch slice");
487
488 let expected = &UInt32Array::from_iter_values([2u32, 3]);
489 let array = recordbatch.column(0);
490 let actual = array.as_primitive::<UInt32Type>();
491 assert_eq!(expected, actual);
492
493 let expected = &StringArray::from(vec!["hello", "greptime"]);
494 let array = recordbatch.column(1);
495 let actual = array.as_string::<i32>();
496 assert_eq!(expected, actual);
497
498 assert!(recordbatch.slice(1, 5).is_err());
499 }
500
501 #[test]
502 fn test_logical_slice_memory_size_for_visible_primitive_string_binary_slices() {
503 let schema = Arc::new(Schema::new(vec![
504 ColumnSchema::new("numbers", ConcreteDataType::uint32_datatype(), false),
505 ColumnSchema::new("strings", ConcreteDataType::string_datatype(), true),
506 ColumnSchema::new("binary", ConcreteDataType::binary_datatype(), true),
507 ]));
508 let numbers: Vec<_> = (0..1024).collect();
509 let strings = (0..1024)
510 .map(|value| (value % 3 != 0).then(|| format!("value-{value}")))
511 .collect::<Vec<_>>();
512 let binary = (0_u32..1024)
513 .map(|value| (value % 3 != 0).then(|| value.to_le_bytes().to_vec()))
514 .collect::<Vec<_>>();
515 let columns: Vec<VectorRef> = vec![
516 Arc::new(UInt32Vector::from_slice(numbers)),
517 Arc::new(StringVector::from(strings)),
518 Arc::new(BinaryVector::from(binary)),
519 ];
520 let batch = RecordBatch::new(schema, columns).unwrap();
521 let slice = batch.slice(511, 3).unwrap();
522
523 assert!(slice.columns().iter().any(|column| column.null_count() > 0));
524 assert!(slice.logical_slice_memory_size() < slice.buffer_memory_size());
525 assert_eq!(
526 slice.logical_slice_memory_size(),
527 slice
528 .columns()
529 .iter()
530 .map(|column| column.to_data().get_slice_memory_size().unwrap())
531 .sum::<usize>()
532 );
533 }
534
535 #[test]
536 fn test_logical_slice_memory_size_for_many_shared_buffer_slices() {
537 let schema = Arc::new(Schema::new(vec![ColumnSchema::new(
538 "strings",
539 ConcreteDataType::string_datatype(),
540 false,
541 )]));
542 let strings = (0..1024)
543 .map(|value| format!("shared-value-{value}"))
544 .collect::<Vec<_>>();
545 let backing = RecordBatch::new(
546 schema,
547 vec![Arc::new(StringVector::from(strings)) as VectorRef],
548 )
549 .unwrap();
550 let slices = (0..128)
551 .map(|index| backing.slice(index * 4, 2).unwrap())
552 .collect::<Vec<_>>();
553 let logical_total = slices
554 .iter()
555 .map(RecordBatch::logical_slice_memory_size)
556 .sum::<usize>();
557 let buffer_total = slices
558 .iter()
559 .map(RecordBatch::buffer_memory_size)
560 .sum::<usize>();
561
562 assert!(logical_total < buffer_total);
563 assert!(slices.iter().all(|slice| {
564 slice.logical_slice_memory_size()
565 == slice.column(0).to_data().get_slice_memory_size().unwrap()
566 }));
567 }
568
569 #[test]
570 fn test_logical_slice_memory_size_uses_arrow_slice_scope_for_views() {
571 let schema = Arc::new(Schema::new(vec![ColumnSchema::new(
572 "strings",
573 ConcreteDataType::utf8_view_datatype(),
574 false,
575 )]));
576 let columns: Vec<VectorRef> =
577 vec![Arc::new(StringVector::from(StringViewArray::from(vec![
578 "unrelated backing payload",
579 "visible string view payload",
580 ])))];
581 let batch = RecordBatch::new(schema, columns)
582 .unwrap()
583 .slice(1, 1)
584 .unwrap();
585
586 assert_eq!(
587 batch.column(0).to_data().get_slice_memory_size().unwrap(),
588 batch.logical_slice_memory_size()
589 );
590 }
591
592 #[test]
593 fn test_merge_record_batch() {
594 let column_schemas = vec![
595 ColumnSchema::new("numbers", ConcreteDataType::uint32_datatype(), false),
596 ColumnSchema::new("strings", ConcreteDataType::string_datatype(), true),
597 ];
598 let schema = Arc::new(Schema::new(column_schemas));
599 let columns: Vec<VectorRef> = vec![
600 Arc::new(UInt32Vector::from_slice(vec![1, 2, 3, 4])),
601 Arc::new(StringVector::from(vec![
602 None,
603 Some("hello"),
604 Some("greptime"),
605 None,
606 ])),
607 ];
608 let recordbatch = RecordBatch::new(schema.clone(), columns).unwrap();
609
610 let columns: Vec<VectorRef> = vec![
611 Arc::new(UInt32Vector::from_slice(vec![1, 2, 3, 4])),
612 Arc::new(StringVector::from(vec![
613 None,
614 Some("hello"),
615 Some("greptime"),
616 None,
617 ])),
618 ];
619 let recordbatch2 = RecordBatch::new(schema.clone(), columns).unwrap();
620
621 let merged = merge_record_batches(schema.clone(), &[recordbatch, recordbatch2])
622 .expect("merge recordbatch");
623 assert_eq!(merged.num_rows(), 8);
624 }
625
626 #[test]
627 fn test_legacy_json_with_extension_does_not_align_as_structured_json() {
628 let field = Field::new("j", DataType::Binary, true)
629 .with_extension_type(JsonExtensionType::new(Arc::new(JsonMetadata::default())));
630 let arrow_schema = Arc::new(ArrowSchema::new(vec![field]));
631 let schema = Arc::new(Schema::try_from(arrow_schema).unwrap());
632 let arrays =
633 vec![Arc::new(BinaryArray::from(vec![Some(br#"{"a":1}"#.as_slice())])) as ArrayRef];
634
635 let aligned = maybe_align_json_array_with_schema(schema.arrow_schema(), arrays).unwrap();
636 assert_eq!(aligned[0].data_type(), &DataType::Binary);
637 }
638}