1use std::fmt;
16use std::fmt::Display;
17use std::hash::BuildHasher;
18use std::sync::Arc;
19
20use bytes::Bytes;
21use comfy_table::{ContentLineStyle, LineStyle, TableStyle};
22use either::Either;
23use itertools::Itertools;
24use rand::rngs::SmallRng;
25use rand::{Rng, SeedableRng};
26use risingwave_common_estimate_size::EstimateSize;
27use risingwave_pb::data::PbDataChunk;
28
29use super::{Array, ArrayImpl, ArrayRef, ArrayResult, StructArray};
30use crate::array::ArrayBuilderImpl;
31use crate::array::data_chunk_iter::RowRef;
32use crate::bitmap::{Bitmap, BitmapBuilder};
33use crate::field_generator::{FieldGeneratorImpl, VarcharProperty};
34use crate::hash::HashCode;
35use crate::row::Row;
36use crate::types::{DataType, DatumRef, MapType, StructType, ToOwnedDatum, ToText};
37use crate::util::chunk_coalesce::DataChunkBuilder;
38use crate::util::hash_util::finalize_hashers;
39use crate::util::iter_util::ZipEqFast;
40use crate::util::value_encoding::{
41 ValueRowSerializer, estimate_serialize_datum_size, serialize_datum_into,
42 try_get_exact_serialize_datum_size,
43};
44
45#[derive(Clone, PartialEq)]
65#[must_use]
66pub struct DataChunk {
67 columns: Arc<[ArrayRef]>,
68 visibility: Bitmap,
69}
70
71impl DataChunk {
72 pub(crate) const PRETTY_TABLE_STYLE: TableStyle = TableStyle::new()
73 .top_border(LineStyle::new('+', '-', '+', '+'))
74 .header_lines(ContentLineStyle::new('|', '|', '|'))
75 .header_separator(LineStyle::new('+', '-', '+', '+'))
76 .content_lines(ContentLineStyle::new('|', '|', '|'))
77 .bottom_border(LineStyle::new('+', '-', '+', '+'));
78
79 pub fn new(columns: Vec<ArrayRef>, visibility: impl Into<Bitmap>) -> Self {
83 let visibility = visibility.into();
84 let capacity = visibility.len();
85 for column in &columns {
86 assert_eq!(capacity, column.len());
87 }
88
89 DataChunk {
90 columns: columns.into(),
91 visibility,
92 }
93 }
94
95 pub fn new_dummy(cardinality: usize) -> Self {
97 DataChunk {
98 columns: Arc::new([]),
99 visibility: Bitmap::ones(cardinality),
100 }
101 }
102
103 pub fn from_rows(rows: &[impl Row], data_types: &[DataType]) -> Self {
110 let mut builder = DataChunkBuilder::new(data_types.to_vec(), rows.len() + 1);
115
116 for row in rows {
117 let none = builder.append_one_row(row);
118 debug_assert!(none.is_none());
119 }
120
121 builder.consume_all().expect("chunk should not be empty")
122 }
123
124 pub fn next_visible_row_idx(&self, row_idx: usize) -> Option<usize> {
126 self.visibility.next_set_bit(row_idx)
127 }
128
129 pub fn into_parts(self) -> (Vec<ArrayRef>, Bitmap) {
130 (self.columns.to_vec(), self.visibility)
131 }
132
133 pub fn into_parts_v2(self) -> (Arc<[ArrayRef]>, Bitmap) {
134 (self.columns, self.visibility)
135 }
136
137 pub fn from_parts(columns: Arc<[ArrayRef]>, visibilities: Bitmap) -> Self {
138 Self {
139 columns,
140 visibility: visibilities,
141 }
142 }
143
144 pub fn dimension(&self) -> usize {
145 self.columns.len()
146 }
147
148 pub fn cardinality(&self) -> usize {
151 self.visibility.count_ones()
152 }
153
154 pub fn has_visible_rows(&self) -> bool {
156 self.visibility.any()
157 }
158
159 pub fn rate_limit_permits(&self) -> u64 {
161 self.cardinality() as _
162 }
163
164 pub fn capacity(&self) -> usize {
167 self.visibility.len()
168 }
169
170 pub fn selectivity(&self) -> f64 {
171 if self.visibility.is_empty() {
172 0.0
173 } else if self.visibility.all() {
174 1.0
175 } else {
176 self.visibility.count_ones() as f64 / self.visibility.len() as f64
177 }
178 }
179
180 pub fn with_visibility(&self, visibility: impl Into<Bitmap>) -> Self {
181 DataChunk {
182 columns: self.columns.clone(),
183 visibility: visibility.into(),
184 }
185 }
186
187 pub fn visibility(&self) -> &Bitmap {
188 &self.visibility
189 }
190
191 pub fn set_visibility(&mut self, visibility: Bitmap) {
192 assert_eq!(visibility.len(), self.capacity());
193 self.visibility = visibility;
194 }
195
196 pub fn is_vis_compacted(&self) -> bool {
199 self.visibility.all()
200 }
201
202 pub fn column_at(&self, idx: usize) -> &ArrayRef {
203 &self.columns[idx]
204 }
205
206 pub fn columns(&self) -> &[ArrayRef] {
207 &self.columns
208 }
209
210 pub fn data_types(&self) -> Vec<DataType> {
212 self.columns.iter().map(|col| col.data_type()).collect()
213 }
214
215 pub fn split_column_at(&self, idx: usize) -> (Self, Self) {
221 let (left, right) = self.columns.split_at(idx);
222 let left = DataChunk::new(left.to_vec(), self.visibility.clone());
223 let right = DataChunk::new(right.to_vec(), self.visibility.clone());
224 (left, right)
225 }
226
227 pub fn to_protobuf(&self) -> PbDataChunk {
228 assert!(self.visibility.all(), "must be compacted before transfer");
229 let mut proto = PbDataChunk {
230 cardinality: self.cardinality() as u32,
231 columns: Default::default(),
232 };
233 let column_ref = &mut proto.columns;
234 for array in &*self.columns {
235 column_ref.push(array.to_protobuf());
236 }
237 proto
238 }
239
240 pub fn compact_vis(self) -> Self {
253 if self.visibility.all() {
254 return self;
255 }
256 let cardinality = self.visibility.count_ones();
257 let columns = self
258 .columns
259 .iter()
260 .map(|col| {
261 let array = col;
262 array.compact_vis(&self.visibility, cardinality).into()
263 })
264 .collect::<Vec<_>>();
265 Self::new(columns, Bitmap::ones(cardinality))
266 }
267
268 pub fn expand_vis(self, vis: Bitmap) -> Self {
270 let mut uncompact_builders: Vec<_> = self
271 .columns
272 .iter()
273 .map(|c| c.create_builder(vis.len()))
274 .collect();
275 let mut last_u = None;
276
277 for (idx, u) in vis.iter_ones().enumerate() {
278 let zeros = if let Some(last_u) = last_u {
280 u - last_u - 1
281 } else {
282 u
283 };
284 for _ in 0..zeros {
285 uncompact_builders
286 .iter_mut()
287 .for_each(|builder| builder.append_null());
288 }
289 uncompact_builders
290 .iter_mut()
291 .zip_eq_fast(self.columns.iter())
292 .for_each(|(builder, c)| builder.append(c.datum_at(idx)));
293 last_u = Some(u);
294 }
295 let zeros = if let Some(last_u) = last_u {
296 vis.len() - last_u - 1
297 } else {
298 vis.len()
299 };
300 for _ in 0..zeros {
301 uncompact_builders
302 .iter_mut()
303 .for_each(|builder| builder.append_null());
304 }
305 let array: Vec<_> = uncompact_builders
306 .into_iter()
307 .map(|builder| Arc::new(builder.finish()))
308 .collect();
309
310 Self::new(array, vis)
311 }
312
313 pub fn from_protobuf(proto: &PbDataChunk) -> ArrayResult<Self> {
314 let mut columns = vec![];
315 for any_col in proto.get_columns() {
316 let cardinality = proto.get_cardinality() as usize;
317 columns.push(ArrayImpl::from_protobuf(any_col, cardinality)?.into());
318 }
319
320 let chunk = DataChunk::new(columns, proto.cardinality as usize);
321 Ok(chunk)
322 }
323
324 pub fn rechunk(chunks: &[DataChunk], each_size_limit: usize) -> ArrayResult<Vec<DataChunk>> {
328 let Some(data_types) = chunks.first().map(|c| c.data_types()) else {
329 return Ok(Vec::new());
330 };
331
332 let mut builder = DataChunkBuilder::new(data_types, each_size_limit);
333 let mut outputs = Vec::new();
334
335 for chunk in chunks {
336 for output in builder.append_chunk(chunk.clone()) {
337 outputs.push(output);
338 }
339 }
340 if let Some(output) = builder.consume_all() {
341 outputs.push(output);
342 }
343
344 Ok(outputs)
345 }
346
347 pub fn get_hash_values<H: BuildHasher>(
350 &self,
351 column_idxes: &[usize],
352 hasher_builder: H,
353 ) -> Vec<HashCode<H>> {
354 let len = self.capacity();
355 let mut states = Vec::with_capacity(len);
356 states.resize_with(len, || hasher_builder.build_hasher());
357 for column_idx in column_idxes {
359 let array = self.column_at(*column_idx);
360 array.hash_vec(&mut states[..], self.visibility());
361 }
362 finalize_hashers(&states[..])
363 .into_iter()
364 .map(|hash_code| hash_code.into())
365 .collect_vec()
366 }
367
368 pub fn row_at(&self, pos: usize) -> (RowRef<'_>, bool) {
374 let row = self.row_at_unchecked_vis(pos);
375 let vis = self.visibility.is_set(pos);
376 (row, vis)
377 }
378
379 pub fn row_at_unchecked_vis(&self, pos: usize) -> RowRef<'_> {
384 RowRef::new(self, pos)
385 }
386
387 pub fn to_pretty(&self) -> impl Display + use<> {
389 use comfy_table::Table;
390
391 if self.cardinality() == 0 {
392 return Either::Left("(empty)");
393 }
394
395 let mut table = Table::new();
396 table.load_style(Self::PRETTY_TABLE_STYLE);
397
398 for row in self.rows() {
399 let cells: Vec<_> = row
400 .iter()
401 .map(|v| {
402 match v {
403 None => "".to_owned(), Some(scalar) => scalar.to_text(),
405 }
406 })
407 .collect();
408 table.add_row(cells);
409 }
410
411 Either::Right(table)
412 }
413
414 pub fn keep_columns(&self, column_indices: &[usize]) -> Self {
424 let capacity: usize = self.capacity();
425 let columns = (self.columns.iter().enumerate())
426 .map(|(i, column)| {
427 if column_indices.contains(&i) {
428 column.clone()
429 } else {
430 let mut builder = column.create_builder(capacity);
431 builder.append_n(capacity, None as DatumRef<'_>);
432 builder.finish().into()
433 }
434 })
435 .collect();
436 DataChunk {
437 columns,
438 visibility: self.visibility.clone(),
439 }
440 }
441
442 pub fn project(&self, indices: &[usize]) -> Self {
448 Self {
449 columns: indices.iter().map(|i| self.columns[*i].clone()).collect(),
450 visibility: self.visibility.clone(),
451 }
452 }
453
454 pub fn project_with_vis(&self, indices: &[usize], visibility: Bitmap) -> Self {
456 assert_eq!(visibility.len(), self.capacity());
457 Self {
458 columns: indices.iter().map(|i| self.columns[*i].clone()).collect(),
459 visibility,
460 }
461 }
462
463 pub fn reorder_rows(&self, indexes: &[usize]) -> Self {
465 let mut array_builders: Vec<ArrayBuilderImpl> = self
466 .columns
467 .iter()
468 .map(|col| col.create_builder(indexes.len()))
469 .collect();
470 for &i in indexes {
471 for (builder, col) in array_builders.iter_mut().zip_eq_fast(self.columns.iter()) {
472 builder.append(col.value_at(i));
473 }
474 }
475 let columns = array_builders
476 .into_iter()
477 .map(|builder| builder.finish().into())
478 .collect();
479 DataChunk::new(columns, indexes.len())
480 }
481
482 fn partition_sizes(&self) -> (usize, Vec<&ArrayRef>) {
491 let mut col_variable: Vec<&ArrayRef> = vec![];
492 let mut row_len_fixed: usize = 0;
493 for c in &*self.columns {
494 if let Some(field_len) = try_get_exact_serialize_datum_size(c) {
495 row_len_fixed += field_len;
496 } else {
497 col_variable.push(c);
498 }
499 }
500 (row_len_fixed, col_variable)
501 }
502
503 unsafe fn compute_size_of_variable_cols_in_row(
504 variable_cols: &[&ArrayRef],
505 row_idx: usize,
506 ) -> usize {
507 unsafe {
508 variable_cols
509 .iter()
510 .map(|col| estimate_serialize_datum_size(col.value_at_unchecked(row_idx)))
511 .sum::<usize>()
512 }
513 }
514
515 unsafe fn init_buffer(
516 row_len_fixed: usize,
517 variable_cols: &[&ArrayRef],
518 row_idx: usize,
519 ) -> Vec<u8> {
520 unsafe {
521 Vec::with_capacity(
522 row_len_fixed + Self::compute_size_of_variable_cols_in_row(variable_cols, row_idx),
523 )
524 }
525 }
526
527 pub fn serialize(&self) -> Vec<Bytes> {
534 let buffers = if !self.visibility.all() {
535 let rows_num = self.visibility.len();
536 let mut buffers: Vec<Vec<u8>> = vec![];
537 let (row_len_fixed, col_variable) = self.partition_sizes();
538
539 for i in 0..rows_num {
541 unsafe {
543 if self.visibility.is_set_unchecked(i) {
544 buffers.push(Self::init_buffer(row_len_fixed, &col_variable, i));
545 } else {
546 buffers.push(vec![]);
547 }
548 }
549 }
550
551 for c in &*self.columns {
553 assert_eq!(c.len(), rows_num);
554 for (i, buffer) in buffers.iter_mut().enumerate() {
555 unsafe {
557 if self.visibility.is_set_unchecked(i) {
558 serialize_datum_into(c.value_at_unchecked(i), buffer);
559 }
560 }
561 }
562 }
563 buffers
564 } else {
565 let mut buffers: Vec<Vec<u8>> = vec![];
566 let (row_len_fixed, col_variable) = self.partition_sizes();
567 for i in 0..self.visibility.len() {
568 unsafe {
569 buffers.push(Self::init_buffer(row_len_fixed, &col_variable, i));
570 }
571 }
572 for c in &*self.columns {
573 assert_eq!(c.len(), self.visibility.len());
574 for (i, buffer) in buffers.iter_mut().enumerate() {
575 unsafe {
577 serialize_datum_into(c.value_at_unchecked(i), buffer);
578 }
579 }
580 }
581 buffers
582 };
583
584 buffers.into_iter().map(|item| item.into()).collect_vec()
585 }
586
587 pub fn serialize_with(&self, serializer: &impl ValueRowSerializer) -> Vec<Bytes> {
592 let mut results = Vec::with_capacity(self.capacity());
593 for row in self.rows_with_holes() {
594 results.push(if let Some(row) = row {
595 serializer.serialize(row).into()
596 } else {
597 Bytes::new()
598 });
599 }
600 results
601 }
602
603 pub fn estimate_value_encoding_size(&self, column_indices: &[usize]) -> usize {
606 if self.capacity() == 0 {
607 0
608 } else {
609 column_indices
610 .iter()
611 .map(|idx| {
612 let datum = self.column_at(*idx).datum_at(0);
613 estimate_serialize_datum_size(datum)
614 })
615 .sum()
616 }
617 }
618}
619
620impl fmt::Debug for DataChunk {
621 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
622 write!(
623 f,
624 "DataChunk {{ cardinality = {}, capacity = {}, data = \n{} }}",
625 self.cardinality(),
626 self.capacity(),
627 self.to_pretty()
628 )
629 }
630}
631
632impl<'a> From<&'a StructArray> for DataChunk {
633 fn from(array: &'a StructArray) -> Self {
634 Self {
635 columns: array.fields().cloned().collect(),
636 visibility: Bitmap::ones(array.len()),
637 }
638 }
639}
640
641impl EstimateSize for DataChunk {
642 fn estimated_heap_size(&self) -> usize {
643 self.columns
644 .iter()
645 .map(|a| a.estimated_heap_size())
646 .sum::<usize>()
647 + self.visibility.estimated_heap_size()
648 }
649}
650
651pub trait DataChunkTestExt {
653 const SEED: u64 = 0xFF67FEABBAEF76FF;
655
656 fn from_pretty(s: &str) -> Self;
688
689 fn with_invisible_holes(self) -> Self
691 where
692 Self: Sized;
693
694 fn assert_valid(&self);
696
697 fn gen_data_chunk(
699 chunk_offset: usize,
700 chunk_size: usize,
701 data_types: &[DataType],
702 string_properties: &VarcharProperty,
703 visibility_ratio: f64,
704 ) -> Self;
705
706 fn gen_data_chunks(
708 num_of_chunks: usize,
709 chunk_size: usize,
710 data_types: &[DataType],
711 string_properties: &VarcharProperty,
712 visibility_ratio: f64,
713 ) -> Vec<Self>
714 where
715 Self: Sized;
716}
717
718impl DataChunkTestExt for DataChunk {
719 fn from_pretty(s: &str) -> Self {
720 use crate::types::ScalarImpl;
721 fn parse_type(s: &str) -> DataType {
722 if let Some(s) = s.strip_suffix("[]") {
723 return DataType::list(parse_type(s));
724 }
725
726 if let Some(inner) = s.strip_prefix("map<").and_then(|s| s.strip_suffix('>')) {
729 let mut parts = inner.split(',');
730 let key_type = parts.next().expect("Key type expected");
731 let value_type = parts.next().expect("Value type expected");
732 return DataType::Map(MapType::from_kv(
733 parse_type(key_type),
734 parse_type(value_type),
735 ));
736 }
737
738 match s {
739 "B" => DataType::Boolean,
740 "I" => DataType::Int64,
741 "i" => DataType::Int32,
742 "F" => DataType::Float64,
743 "f" => DataType::Float32,
744 "TS" => DataType::Timestamp,
745 "TZ" => DataType::Timestamptz,
746 "T" => DataType::Varchar,
747 "SRL" => DataType::Serial,
748 "D" => DataType::Date,
749 array if array.starts_with('<') && array.ends_with('>') => DataType::Struct(
750 StructType::unnamed(array[1..array.len() - 1].split(',').map(parse_type)),
751 ),
752 _ => todo!("unsupported type: {s:?}"),
753 }
754 }
755
756 let mut lines = s.split('\n').filter(|l| !l.trim().is_empty());
757 let header = lines.next().unwrap().trim();
759 let datatypes = header
760 .split_ascii_whitespace()
761 .take_while(|c| *c != "//")
762 .map(parse_type)
763 .collect::<Vec<_>>();
764 let mut array_builders = datatypes
765 .iter()
766 .map(|ty| ty.create_array_builder(1))
767 .collect::<Vec<_>>();
768 let mut visibility = vec![];
769 for line in lines {
770 let mut token = line.trim().split_ascii_whitespace();
771 #[expect(clippy::disallowed_methods)]
773 for ((builder, ty), val_str) in
774 array_builders.iter_mut().zip(&datatypes).zip(&mut token)
775 {
776 let datum = match val_str {
777 "." => None,
778 "(empty)" => Some("".into()),
779 _ => Some(ScalarImpl::from_text_for_test(val_str, ty).unwrap()),
781 };
782 builder.append(datum);
783 }
784 let visible = match token.next() {
785 None | Some("//") => true,
786 Some("D") => false,
787 Some(t) => panic!("invalid token: {t:?}"),
788 };
789 visibility.push(visible);
790 }
791 let columns = array_builders
792 .into_iter()
793 .map(|builder| builder.finish().into())
794 .collect();
795 let vis = Bitmap::from_iter(visibility);
796 let chunk = DataChunk::new(columns, vis);
797 chunk.assert_valid();
798 chunk
799 }
800
801 fn with_invisible_holes(self) -> Self
802 where
803 Self: Sized,
804 {
805 let (cols, vis) = self.into_parts();
806 let n = vis.len();
807 let mut new_vis = BitmapBuilder::with_capacity(n * 2);
808 for b in vis.iter() {
809 new_vis.append(b);
810 new_vis.append(false);
811 }
812 let new_cols = cols
813 .into_iter()
814 .map(|col| {
815 let arr = col;
816 let mut builder = arr.create_builder(n * 2);
817 for v in arr.iter() {
818 builder.append(v.to_owned_datum());
819 builder.append_null();
820 }
821
822 builder.finish().into()
823 })
824 .collect();
825 let chunk = DataChunk::new(new_cols, new_vis.finish());
826 chunk.assert_valid();
827 chunk
828 }
829
830 fn assert_valid(&self) {
831 let cols = self.columns();
832 let vis = &self.visibility;
833 let n = vis.len();
834 for col in cols {
835 assert_eq!(col.len(), n);
836 }
837 }
838
839 fn gen_data_chunk(
840 chunk_offset: usize,
841 chunk_size: usize,
842 data_types: &[DataType],
843 varchar_properties: &VarcharProperty,
844 visibility_percent: f64,
845 ) -> Self {
846 let vis = if visibility_percent == 0.0 {
847 Bitmap::zeros(chunk_size)
848 } else if visibility_percent == 1.0 {
849 Bitmap::ones(chunk_size)
850 } else {
851 let mut rng = SmallRng::from_seed([0; 32]);
852 let mut vis_builder = BitmapBuilder::with_capacity(chunk_size);
853 for _i in 0..chunk_size {
854 vis_builder.append(rng.random_bool(visibility_percent));
855 }
856 vis_builder.finish()
857 };
858
859 let mut columns = Vec::new();
860 for data_type in data_types {
862 let mut array_builder = data_type.create_array_builder(chunk_size);
863 for j in 0..chunk_size {
864 let offset = ((chunk_offset + 1) * (j + 1)) as u64;
865 match data_type {
866 DataType::Varchar => {
867 let datum =
868 FieldGeneratorImpl::with_varchar(varchar_properties, Self::SEED)
869 .generate_datum(offset);
870 array_builder.append(&datum);
871 }
872 DataType::Timestamp => {
873 let datum =
874 FieldGeneratorImpl::with_timestamp(None, None, None, Self::SEED)
875 .expect("create timestamp generator should succeed")
876 .generate_datum(offset);
877 array_builder.append(datum);
878 }
879 DataType::Timestamptz => {
880 let datum =
881 FieldGeneratorImpl::with_timestamptz(None, None, None, Self::SEED)
882 .expect("create timestamptz generator should succeed")
883 .generate_datum(offset);
884 array_builder.append(datum);
885 }
886 _ if data_type.is_numeric() => {
887 let mut data_gen = FieldGeneratorImpl::with_number_random(
888 data_type.clone(),
889 None,
890 None,
891 Self::SEED,
892 )
893 .unwrap();
894 let datum = data_gen.generate_datum(offset);
895 array_builder.append(datum);
896 }
897 _ => todo!("unsupported type: {data_type:?}"),
898 }
899 }
900 columns.push(array_builder.finish().into());
901 }
902 DataChunk::new(columns, vis)
903 }
904
905 fn gen_data_chunks(
906 num_of_chunks: usize,
907 chunk_size: usize,
908 data_types: &[DataType],
909 varchar_properties: &VarcharProperty,
910 visibility_percent: f64,
911 ) -> Vec<Self> {
912 (0..num_of_chunks)
913 .map(|i| {
914 Self::gen_data_chunk(
915 i,
916 chunk_size,
917 data_types,
918 varchar_properties,
919 visibility_percent,
920 )
921 })
922 .collect()
923 }
924}
925
926#[cfg(test)]
927mod tests {
928 use crate::array::*;
929 use crate::row::Row;
930
931 #[test]
932 fn test_rechunk() {
933 let test_case = |num_chunks: usize, chunk_size: usize, new_chunk_size: usize| {
934 let mut chunks = vec![];
935 for chunk_idx in 0..num_chunks {
936 let mut builder = PrimitiveArrayBuilder::<i32>::new(0);
937 for i in chunk_size * chunk_idx..chunk_size * (chunk_idx + 1) {
938 builder.append(Some(i as i32));
939 }
940 let chunk = DataChunk::new(vec![Arc::new(builder.finish().into())], chunk_size);
941 chunks.push(chunk);
942 }
943
944 let total_size = num_chunks * chunk_size;
945 let num_full_new_chunk = total_size / new_chunk_size;
946 let mut chunk_sizes = vec![new_chunk_size; num_full_new_chunk];
947 let remainder = total_size % new_chunk_size;
948 if remainder != 0 {
949 chunk_sizes.push(remainder);
950 }
951
952 let new_chunks = DataChunk::rechunk(&chunks, new_chunk_size).unwrap();
953 assert_eq!(new_chunks.len(), chunk_sizes.len());
954 for (idx, chunk_size) in chunk_sizes.iter().enumerate() {
956 assert_eq!(*chunk_size, new_chunks[idx].capacity());
957 }
958
959 let mut chunk_idx = 0;
960 let mut cur_idx = 0;
961 for val in 0..total_size {
962 if cur_idx >= chunk_sizes[chunk_idx] {
963 cur_idx = 0;
964 chunk_idx += 1;
965 }
966 assert_eq!(
967 new_chunks[chunk_idx]
968 .column_at(0)
969 .as_int32()
970 .value_at(cur_idx)
971 .unwrap(),
972 val as i32
973 );
974 cur_idx += 1;
975 }
976 };
977
978 test_case(0, 0, 1);
979 test_case(0, 10, 1);
980 test_case(10, 0, 1);
981 test_case(1, 1, 6);
982 test_case(1, 10, 11);
983 test_case(2, 3, 6);
984 test_case(5, 5, 6);
985 test_case(10, 10, 7);
986 }
987
988 #[test]
989 fn test_chunk_iter() {
990 let num_of_columns: usize = 2;
991 let length = 5;
992 let mut columns = vec![];
993 for i in 0..num_of_columns {
994 let mut builder = PrimitiveArrayBuilder::<i32>::new(length);
995 for _ in 0..length {
996 builder.append(Some(i as i32));
997 }
998 let arr = builder.finish();
999 columns.push(Arc::new(arr.into()))
1000 }
1001 let chunk: DataChunk = DataChunk::new(columns, length);
1002 for row in chunk.rows() {
1003 for i in 0..num_of_columns {
1004 let val = row.datum_at(i).unwrap();
1005 assert_eq!(val.into_int32(), i as i32);
1006 }
1007 }
1008 }
1009
1010 #[test]
1011 fn test_to_pretty_string() {
1012 let chunk = DataChunk::new(
1013 vec![
1014 Arc::new(I64Array::from_iter([1, 2, 3, 4]).into()),
1015 Arc::new(I64Array::from_iter([Some(6), None, Some(7), None]).into()),
1016 ],
1017 4,
1018 );
1019 assert_eq!(
1020 chunk.to_pretty().to_string(),
1021 "\
1022+---+---+
1023| 1 | 6 |
1024| 2 | |
1025| 3 | 7 |
1026| 4 | |
1027+---+---+"
1028 );
1029 }
1030
1031 #[test]
1032 fn test_no_column_chunk() {
1033 let chunk = DataChunk::new_dummy(10);
1034 assert_eq!(chunk.rows().count(), 10);
1035
1036 let chunk_after_serde = DataChunk::from_protobuf(&chunk.to_protobuf()).unwrap();
1037 assert_eq!(chunk_after_serde.rows().count(), 10);
1038 assert_eq!(chunk_after_serde.cardinality(), 10);
1039 }
1040
1041 #[test]
1042 fn reorder_columns() {
1043 let chunk = DataChunk::from_pretty(
1044 "I I I
1045 2 5 1
1046 4 9 2
1047 6 9 3",
1048 );
1049 assert_eq!(
1050 chunk.project(&[2, 1, 0]),
1051 DataChunk::from_pretty(
1052 "I I I
1053 1 5 2
1054 2 9 4
1055 3 9 6",
1056 )
1057 );
1058 assert_eq!(
1059 chunk.project(&[2, 0]),
1060 DataChunk::from_pretty(
1061 "I I
1062 1 2
1063 2 4
1064 3 6",
1065 )
1066 );
1067 assert_eq!(chunk.project(&[0, 1, 2]), chunk);
1068 assert_eq!(chunk.project(&[]).cardinality(), 3);
1069 }
1070
1071 #[test]
1072 fn test_chunk_estimated_size() {
1073 assert_eq!(
1074 72,
1075 DataChunk::from_pretty(
1076 "I I I
1077 1 5 2
1078 2 9 4
1079 3 9 6",
1080 )
1081 .estimated_heap_size()
1082 );
1083 assert_eq!(
1084 48,
1085 DataChunk::from_pretty(
1086 "I I
1087 1 2
1088 2 4
1089 3 6",
1090 )
1091 .estimated_heap_size()
1092 );
1093 }
1094}