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