1use std::collections::{HashMap, HashSet};
18use std::ops::RangeInclusive;
19use std::sync::Arc;
20use std::time::{Duration, Instant};
21
22use api::helper::{ColumnDataTypeWrapper, to_grpc_value};
23use api::v1::bulk_wal_entry::Body;
24use api::v1::{ArrowIpc, BulkWalEntry, Mutation, OpType, SemanticType};
25use bytes::Bytes;
26use common_grpc::flight::{FlightDecoder, FlightEncoder, FlightMessage};
27use common_recordbatch::DfRecordBatch as RecordBatch;
28use common_time::Timestamp;
29use datafusion_common::Column;
30use datafusion_common::pruning::PruningStatistics;
31use datafusion_expr::utils::expr_to_columns;
32use datatypes::arrow;
33use datatypes::arrow::array::{
34 Array, ArrayRef, BinaryArray, BooleanArray, DictionaryArray, StringDictionaryBuilder,
35 TimestampMicrosecondArray, TimestampMillisecondArray, TimestampNanosecondArray,
36 TimestampSecondArray, UInt8Array, UInt32Array, UInt64Array,
37};
38use datatypes::arrow::compute::{SortColumn, SortOptions, concat_batches};
39use datatypes::arrow::datatypes::{
40 DataType as ArrowDataType, Field, Schema, SchemaRef, TimeUnit, UInt32Type,
41};
42use datatypes::data_type::DataType;
43use datatypes::extension::json::align_schema_with_json_array;
44use datatypes::prelude::{MutableVector, Vector};
45use datatypes::value::ValueRef;
46use datatypes::vectors::Helper;
47use mito_codec::key_values::{KeyValue, KeyValues};
48use mito_codec::row_converter::{PrimaryKeyCodec, SortField, build_primary_key_codec_with_fields};
49use parquet::arrow::ArrowWriter;
50use parquet::basic::{Compression, ZstdLevel};
51use parquet::file::metadata::ParquetMetaData;
52use parquet::file::properties::WriterProperties;
53use smallvec::SmallVec;
54use snafu::{OptionExt, ResultExt};
55use store_api::codec::PrimaryKeyEncoding;
56use store_api::metadata::{RegionMetadata, RegionMetadataRef};
57use store_api::mito_engine_options::FloatFieldEncoding;
58use store_api::storage::consts::PRIMARY_KEY_COLUMN_NAME;
59use store_api::storage::{ColumnId, FileId, SequenceNumber, SequenceRange};
60
61use crate::compaction::{collect_json2_rewrite_plans, rewrite_json2_batch, rewrite_json2_schema};
62use crate::error::{
63 self, ColumnNotFoundSnafu, ComputeArrowSnafu, CreateDefaultSnafu, DataTypeMismatchSnafu,
64 EncodeMemtableSnafu, EncodeSnafu, InvalidMetadataSnafu, InvalidRequestSnafu,
65 NewRecordBatchSnafu, Result,
66};
67use crate::memtable::bulk::context::{BulkIterContext, BulkIterContextRef};
68use crate::memtable::bulk::part_reader::EncodedBulkPartIter;
69use crate::memtable::time_series::{ValueBuilder, Values};
70use crate::memtable::{BoxedRecordBatchIterator, MemScanMetrics, MemtableStats};
71use crate::sst::SeriesEstimator;
72use crate::sst::index::IndexOutput;
73use crate::sst::parquet::flat_format::primary_key_column_index;
74use crate::sst::parquet::format::{PrimaryKeyArray, PrimaryKeyArrayBuilder};
75use crate::sst::parquet::{
76 COLUMN_INDEX_TRUNCATE_LENGTH, PARQUET_METADATA_KEY, SstInfo, apply_column_compression,
77 apply_float_field_encoding, apply_internal_column_encoding,
78};
79
80const INIT_DICT_VALUE_CAPACITY: usize = 8;
81
82#[derive(Clone)]
84pub struct BulkPart {
85 pub batch: RecordBatch,
86 pub max_timestamp: i64,
87 pub min_timestamp: i64,
88 pub sequence: u64,
91 pub min_sequence: SequenceNumber,
94 pub timestamp_index: usize,
95 pub raw_data: Option<ArrowIpc>,
96}
97
98impl TryFrom<BulkWalEntry> for BulkPart {
99 type Error = error::Error;
100
101 fn try_from(value: BulkWalEntry) -> std::result::Result<Self, Self::Error> {
102 match value.body.expect("Entry payload should be present") {
103 Body::ArrowIpc(ipc) => {
104 let mut decoder = FlightDecoder::try_from_schema_bytes(&ipc.schema)
105 .context(error::ConvertBulkWalEntrySnafu)?;
106 let batch = decoder
107 .try_decode_record_batch(&ipc.data_header, &ipc.payload)
108 .context(error::ConvertBulkWalEntrySnafu)?;
109 Ok(Self {
110 batch,
111 max_timestamp: value.max_ts,
112 min_timestamp: value.min_ts,
113 sequence: value.sequence,
114 min_sequence: value.sequence,
117 timestamp_index: value.timestamp_index as usize,
118 raw_data: Some(ipc),
119 })
120 }
121 }
122 }
123}
124
125impl TryFrom<&BulkPart> for BulkWalEntry {
126 type Error = error::Error;
127
128 fn try_from(value: &BulkPart) -> Result<Self> {
129 if let Some(ipc) = &value.raw_data {
130 Ok(BulkWalEntry {
131 sequence: value.sequence,
132 max_ts: value.max_timestamp,
133 min_ts: value.min_timestamp,
134 timestamp_index: value.timestamp_index as u32,
135 body: Some(Body::ArrowIpc(ipc.clone())),
136 })
137 } else {
138 let mut encoder = FlightEncoder::default();
139 let schema_bytes = encoder
140 .encode_schema(value.batch.schema().as_ref())
141 .data_header;
142 let [rb_data] = encoder
143 .encode(FlightMessage::RecordBatch(value.batch.clone()))
144 .try_into()
145 .map_err(|_| {
146 error::UnsupportedOperationSnafu {
147 err_msg: "create BulkWalEntry from RecordBatch with dictionary arrays",
148 }
149 .build()
150 })?;
151 Ok(BulkWalEntry {
152 sequence: value.sequence,
153 max_ts: value.max_timestamp,
154 min_ts: value.min_timestamp,
155 timestamp_index: value.timestamp_index as u32,
156 body: Some(Body::ArrowIpc(ArrowIpc {
157 schema: schema_bytes,
158 data_header: rb_data.data_header,
159 payload: rb_data.data_body,
160 })),
161 })
162 }
163 }
164}
165
166impl BulkPart {
167 pub(crate) fn schema(&self) -> SchemaRef {
168 self.batch.schema()
169 }
170
171 pub(crate) fn estimated_size(&self) -> usize {
172 record_batch_estimated_size(&self.batch)
173 }
174
175 pub fn estimated_series_count(&self) -> usize {
178 let pk_column_idx = primary_key_column_index(self.batch.num_columns());
179 let pk_column = self.batch.column(pk_column_idx);
180 if let Some(dict_array) = pk_column.as_any().downcast_ref::<PrimaryKeyArray>() {
181 dict_array.values().len()
182 } else {
183 0
184 }
185 }
186
187 pub fn to_memtable_stats(&self, region_metadata: &RegionMetadataRef) -> MemtableStats {
189 let ts_type = region_metadata.time_index_type();
190 let min_ts = ts_type.create_timestamp(self.min_timestamp);
191 let max_ts = ts_type.create_timestamp(self.max_timestamp);
192
193 MemtableStats {
194 estimated_bytes: self.estimated_size(),
195 time_range: Some((min_ts, max_ts)),
196 num_rows: self.num_rows(),
197 num_ranges: 1,
198 max_sequence: self.sequence,
199 min_sequence: self.min_sequence,
200 series_count: self.estimated_series_count(),
201 }
202 }
203
204 pub fn fill_missing_columns(&mut self, region_metadata: &RegionMetadata) -> Result<()> {
214 let batch_schema = self.batch.schema();
216 let batch_columns: HashSet<_> = batch_schema
217 .fields()
218 .iter()
219 .map(|f| f.name().as_str())
220 .collect();
221
222 let mut columns_to_fill = Vec::new();
224 for column_meta in ®ion_metadata.column_metadatas {
225 if region_metadata.primary_key_encoding == PrimaryKeyEncoding::Sparse
227 && column_meta.semantic_type == SemanticType::Tag
228 {
229 continue;
230 }
231 if !batch_columns.contains(column_meta.column_schema.name.as_str()) {
234 columns_to_fill.push(column_meta);
235 }
236 }
237
238 if columns_to_fill.is_empty() {
239 return Ok(());
240 }
241
242 let num_rows = self.batch.num_rows();
243
244 let mut new_columns = Vec::new();
245 let mut new_fields = Vec::new();
246
247 new_fields.extend(batch_schema.fields().iter().cloned());
249 new_columns.extend_from_slice(self.batch.columns());
250
251 let region_id = region_metadata.region_id;
252 for column_meta in columns_to_fill {
254 let default_vector = column_meta
255 .column_schema
256 .create_default_vector(num_rows)
257 .context(CreateDefaultSnafu {
258 region_id,
259 column: &column_meta.column_schema.name,
260 })?
261 .with_context(|| InvalidRequestSnafu {
262 region_id,
263 reason: format!(
264 "column {} does not have default value",
265 column_meta.column_schema.name
266 ),
267 })?;
268 let arrow_array = default_vector.to_arrow_array();
269 column_meta.column_schema.data_type.as_arrow_type();
270
271 new_fields.push(Arc::new(Field::new(
272 column_meta.column_schema.name.clone(),
273 column_meta.column_schema.data_type.as_arrow_type(),
274 column_meta.column_schema.is_nullable(),
275 )));
276 new_columns.push(arrow_array);
277 }
278
279 let new_schema = Arc::new(Schema::new(new_fields));
281 let new_batch =
282 RecordBatch::try_new(new_schema, new_columns).context(NewRecordBatchSnafu)?;
283
284 self.batch = new_batch;
286
287 self.raw_data = None;
292
293 Ok(())
294 }
295
296 pub(crate) fn to_mutation(&self, region_metadata: &RegionMetadataRef) -> Result<Mutation> {
298 let vectors = region_metadata
299 .schema
300 .column_schemas()
301 .iter()
302 .map(|col| match self.batch.column_by_name(&col.name) {
303 None => Ok(None),
304 Some(col) => Helper::try_into_vector(col).map(Some),
305 })
306 .collect::<datatypes::error::Result<Vec<_>>>()
307 .context(error::ComputeVectorSnafu)?;
308
309 let rows = (0..self.num_rows())
310 .map(|row_idx| {
311 let values = (0..self.batch.num_columns())
312 .map(|col_idx| {
313 if let Some(v) = &vectors[col_idx] {
314 to_grpc_value(v.get(row_idx))
315 } else {
316 api::v1::Value { value_data: None }
317 }
318 })
319 .collect::<Vec<_>>();
320 api::v1::Row { values }
321 })
322 .collect::<Vec<_>>();
323
324 let schema = region_metadata
325 .column_metadatas
326 .iter()
327 .map(|c| {
328 let data_type_wrapper =
329 ColumnDataTypeWrapper::try_from(c.column_schema.data_type.clone())?;
330 Ok(api::v1::ColumnSchema {
331 column_name: c.column_schema.name.clone(),
332 datatype: data_type_wrapper.datatype() as i32,
333 semantic_type: c.semantic_type as i32,
334 ..Default::default()
335 })
336 })
337 .collect::<api::error::Result<Vec<_>>>()
338 .context(error::ConvertColumnDataTypeSnafu {
339 reason: "failed to convert region metadata to column schema",
340 })?;
341
342 let rows = api::v1::Rows { schema, rows };
343
344 Ok(Mutation {
345 op_type: OpType::Put as i32,
346 sequence: self.sequence,
347 rows: Some(rows),
348 write_hint: None,
349 })
350 }
351
352 pub fn timestamps(&self) -> &ArrayRef {
353 self.batch.column(self.timestamp_index)
354 }
355
356 pub fn num_rows(&self) -> usize {
357 self.batch.num_rows()
358 }
359}
360
361pub struct UnorderedPart {
364 parts: Vec<BulkPart>,
366 total_rows: usize,
368 total_bytes: usize,
370 min_timestamp: i64,
372 max_timestamp: i64,
374 max_sequence: u64,
376 min_sequence: SequenceNumber,
378 threshold: usize,
380 compact_threshold: usize,
382}
383
384impl Default for UnorderedPart {
385 fn default() -> Self {
386 Self::new()
387 }
388}
389
390impl UnorderedPart {
391 pub fn new() -> Self {
393 Self {
394 parts: Vec::new(),
395 total_rows: 0,
396 total_bytes: 0,
397 min_timestamp: i64::MAX,
398 max_timestamp: i64::MIN,
399 max_sequence: 0,
400 min_sequence: SequenceNumber::MAX,
401 threshold: 1024,
402 compact_threshold: 4096,
403 }
404 }
405
406 pub fn set_threshold(&mut self, threshold: usize) {
408 self.threshold = threshold;
409 }
410
411 pub fn set_compact_threshold(&mut self, compact_threshold: usize) {
413 self.compact_threshold = compact_threshold;
414 }
415
416 pub fn threshold(&self) -> usize {
418 self.threshold
419 }
420
421 pub fn compact_threshold(&self) -> usize {
423 self.compact_threshold
424 }
425
426 pub fn should_accept(&self, num_rows: usize) -> bool {
428 num_rows < self.threshold
429 }
430
431 pub fn should_compact(&self) -> bool {
433 self.total_rows >= self.compact_threshold
434 }
435
436 pub(super) fn estimated_bytes(&self) -> usize {
438 self.total_bytes
439 }
440
441 pub(super) fn sequence_bounds(&self) -> Option<(SequenceNumber, SequenceNumber)> {
443 (!self.is_empty()).then_some((self.min_sequence, self.max_sequence))
444 }
445
446 pub fn push(&mut self, part: BulkPart) {
448 self.total_rows += part.num_rows();
449 self.total_bytes = self.total_bytes.saturating_add(part.estimated_size());
450 self.min_timestamp = self.min_timestamp.min(part.min_timestamp);
451 self.max_timestamp = self.max_timestamp.max(part.max_timestamp);
452 self.max_sequence = self.max_sequence.max(part.sequence);
453 self.min_sequence = self.min_sequence.min(part.min_sequence);
454 self.parts.push(part);
455 }
456
457 pub fn num_rows(&self) -> usize {
459 self.total_rows
460 }
461
462 pub fn is_empty(&self) -> bool {
464 self.parts.is_empty()
465 }
466
467 pub fn num_parts(&self) -> usize {
469 self.parts.len()
470 }
471
472 pub fn concat_and_sort(&self, metadata: &RegionMetadataRef) -> Result<Option<RecordBatch>> {
475 if self.parts.is_empty() {
476 return Ok(None);
477 }
478
479 if self.parts.len() == 1 {
480 return Ok(Some(self.parts[0].batch.clone()));
482 }
483
484 let schemas = self
485 .parts
486 .iter()
487 .map(|x| (x.batch.schema(), x.num_rows() as u64))
488 .collect::<Vec<_>>();
489 let plans = collect_json2_rewrite_plans(metadata, &schemas)?;
490
491 debug_assert!(self.parts.windows(2).all(|w| rewrite_json2_schema(
492 &w[0].batch.schema(),
493 &plans
494 ) == rewrite_json2_schema(
495 &w[1].batch.schema(),
496 &plans
497 )));
498 let schema = rewrite_json2_schema(&self.parts[0].batch.schema(), &plans);
499
500 let batches = self
501 .parts
502 .iter()
503 .map(|x| rewrite_json2_batch(x.batch.clone(), &plans))
504 .collect::<Result<Vec<_>>>()?;
505 let concatenated = concat_batches(&schema, &batches).context(ComputeArrowSnafu)?;
506
507 let sorted_batch = sort_primary_key_record_batch(&concatenated)?;
509
510 Ok(Some(sorted_batch))
511 }
512
513 pub fn to_bulk_part(&self, metadata: &RegionMetadataRef) -> Result<Option<BulkPart>> {
516 let Some(sorted_batch) = self.concat_and_sort(metadata)? else {
517 return Ok(None);
518 };
519
520 let timestamp_index = self.parts[0].timestamp_index;
521
522 Ok(Some(BulkPart {
523 batch: sorted_batch,
524 max_timestamp: self.max_timestamp,
525 min_timestamp: self.min_timestamp,
526 sequence: self.max_sequence,
527 min_sequence: self.min_sequence,
528 timestamp_index,
529 raw_data: None,
530 }))
531 }
532
533 pub fn clear(&mut self) {
535 self.parts.clear();
536 self.total_rows = 0;
537 self.total_bytes = 0;
538 self.min_timestamp = i64::MAX;
539 self.max_timestamp = i64::MIN;
540 self.max_sequence = 0;
541 self.min_sequence = SequenceNumber::MAX;
542 }
543}
544
545pub fn record_batch_estimated_size(batch: &RecordBatch) -> usize {
547 batch
548 .columns()
549 .iter()
550 .map(|c| c.to_data().get_slice_memory_size().unwrap_or(0))
552 .sum()
553}
554
555enum PrimaryKeyColumnBuilder {
557 StringDict(StringDictionaryBuilder<UInt32Type>),
559 Vector(Box<dyn MutableVector>),
561}
562
563impl PrimaryKeyColumnBuilder {
564 fn push_value_ref(&mut self, value: ValueRef) -> Result<()> {
566 match self {
567 PrimaryKeyColumnBuilder::StringDict(builder) => {
568 if let Some(s) = value.try_into_string().context(DataTypeMismatchSnafu)? {
569 builder.append_value(s);
571 } else {
572 builder.append_null();
573 }
574 }
575 PrimaryKeyColumnBuilder::Vector(builder) => {
576 builder.push_value_ref(&value);
577 }
578 }
579 Ok(())
580 }
581
582 fn into_arrow_array(self) -> ArrayRef {
584 match self {
585 PrimaryKeyColumnBuilder::StringDict(mut builder) => Arc::new(builder.finish()),
586 PrimaryKeyColumnBuilder::Vector(mut builder) => builder.to_vector().to_arrow_array(),
587 }
588 }
589}
590
591pub struct BulkPartConverter {
593 schema: SchemaRef,
595 primary_key_codec: Arc<dyn PrimaryKeyCodec>,
597 key_buf: Vec<u8>,
599 key_array_builder: PrimaryKeyArrayBuilder,
601 value_builder: ValueBuilder,
603 primary_key_column_builders: Vec<PrimaryKeyColumnBuilder>,
606
607 max_ts: i64,
609 min_ts: i64,
611 max_sequence: SequenceNumber,
613 min_sequence: SequenceNumber,
615}
616
617impl BulkPartConverter {
618 pub fn new(
623 region_metadata: &RegionMetadataRef,
624 schema: SchemaRef,
625 capacity: usize,
626 primary_key_codec: Arc<dyn PrimaryKeyCodec>,
627 store_primary_key_columns: bool,
628 ) -> Self {
629 debug_assert_eq!(
630 region_metadata.primary_key_encoding,
631 primary_key_codec.encoding()
632 );
633
634 let primary_key_column_builders = if store_primary_key_columns
635 && region_metadata.primary_key_encoding != PrimaryKeyEncoding::Sparse
636 {
637 new_primary_key_column_builders(region_metadata, capacity)
638 } else {
639 Vec::new()
640 };
641
642 Self {
643 schema,
644 primary_key_codec,
645 key_buf: Vec::new(),
646 key_array_builder: PrimaryKeyArrayBuilder::new(),
647 value_builder: ValueBuilder::new(region_metadata, capacity),
648 primary_key_column_builders,
649 min_ts: i64::MAX,
650 max_ts: i64::MIN,
651 max_sequence: SequenceNumber::MIN,
652 min_sequence: SequenceNumber::MAX,
653 }
654 }
655
656 pub fn append_key_values(&mut self, key_values: &KeyValues) -> Result<()> {
658 for kv in key_values.iter() {
659 self.append_key_value(&kv)?;
660 }
661
662 Ok(())
663 }
664
665 fn append_key_value(&mut self, kv: &KeyValue) -> Result<()> {
669 if self.primary_key_codec.encoding() == PrimaryKeyEncoding::Sparse {
671 let mut primary_keys = kv.primary_keys();
674 if let Some(encoded) = primary_keys
675 .next()
676 .context(ColumnNotFoundSnafu {
677 column: PRIMARY_KEY_COLUMN_NAME,
678 })?
679 .try_into_binary()
680 .context(DataTypeMismatchSnafu)?
681 {
682 self.key_array_builder
683 .append(encoded)
684 .context(ComputeArrowSnafu)?;
685 } else {
686 self.key_array_builder
687 .append("")
688 .context(ComputeArrowSnafu)?;
689 }
690 } else {
691 self.key_buf.clear();
693 self.primary_key_codec
694 .encode_key_value(kv, &mut self.key_buf)
695 .context(EncodeSnafu)?;
696 self.key_array_builder
697 .append(&self.key_buf)
698 .context(ComputeArrowSnafu)?;
699 };
700
701 if !self.primary_key_column_builders.is_empty() {
703 for (builder, pk_value) in self
704 .primary_key_column_builders
705 .iter_mut()
706 .zip(kv.primary_keys())
707 {
708 builder.push_value_ref(pk_value)?;
709 }
710 }
711
712 self.value_builder.push(
714 kv.timestamp(),
715 kv.sequence(),
716 kv.op_type() as u8,
717 kv.fields(),
718 );
719
720 let ts = kv
723 .timestamp()
724 .try_into_timestamp()
725 .unwrap()
726 .unwrap()
727 .value();
728 self.min_ts = self.min_ts.min(ts);
729 self.max_ts = self.max_ts.max(ts);
730 self.max_sequence = self.max_sequence.max(kv.sequence());
731 self.min_sequence = self.min_sequence.min(kv.sequence());
732
733 Ok(())
734 }
735
736 pub fn convert(mut self) -> Result<BulkPart> {
740 let values = Values::from(self.value_builder);
741 let mut columns =
742 Vec::with_capacity(4 + values.fields.len() + self.primary_key_column_builders.len());
743
744 for builder in self.primary_key_column_builders {
746 columns.push(builder.into_arrow_array());
747 }
748 columns.extend(values.fields.iter().map(|field| field.to_arrow_array()));
750 let timestamp_index = columns.len();
752 columns.push(values.timestamp.to_arrow_array());
753 let pk_array = self.key_array_builder.finish();
755 columns.push(Arc::new(pk_array));
756 columns.push(values.sequence.to_arrow_array());
758 columns.push(values.op_type.to_arrow_array());
759
760 let schema = align_schema_with_json_array(self.schema, &columns);
763 let batch = RecordBatch::try_new(schema, columns).context(NewRecordBatchSnafu)?;
764 let batch = sort_primary_key_record_batch(&batch)?;
766
767 Ok(BulkPart {
768 batch,
769 max_timestamp: self.max_ts,
770 min_timestamp: self.min_ts,
771 sequence: self.max_sequence,
772 min_sequence: self.min_sequence,
773 timestamp_index,
774 raw_data: None,
775 })
776 }
777}
778
779fn new_primary_key_column_builders(
780 metadata: &RegionMetadata,
781 capacity: usize,
782) -> Vec<PrimaryKeyColumnBuilder> {
783 metadata
784 .primary_key_columns()
785 .map(|col| {
786 if col.column_schema.data_type.is_string() {
787 PrimaryKeyColumnBuilder::StringDict(StringDictionaryBuilder::with_capacity(
788 capacity,
789 INIT_DICT_VALUE_CAPACITY,
790 capacity,
791 ))
792 } else {
793 PrimaryKeyColumnBuilder::Vector(
794 col.column_schema.data_type.create_mutable_vector(capacity),
795 )
796 }
797 })
798 .collect()
799}
800
801pub fn sort_primary_key_record_batch(batch: &RecordBatch) -> Result<RecordBatch> {
803 let indices = if let Some(indices) = try_sort_primary_key_indices(batch)? {
804 indices
805 } else {
806 lexsort_primary_key_indices(batch)?
807 };
808
809 datatypes::arrow::compute::take_record_batch(batch, &indices).context(ComputeArrowSnafu)
810}
811
812fn try_sort_primary_key_indices(batch: &RecordBatch) -> Result<Option<UInt32Array>> {
816 let total_columns = batch.num_columns();
817 let timestamp = batch.column(total_columns - 4);
818 let primary_key = batch.column(total_columns - 3);
819 let sequence = batch.column(total_columns - 2);
820
821 let Some(primary_key) = primary_key
822 .as_any()
823 .downcast_ref::<DictionaryArray<UInt32Type>>()
824 else {
825 return Ok(None);
826 };
827 let Some(sequence) = sequence.as_any().downcast_ref::<UInt64Array>() else {
828 return Ok(None);
829 };
830
831 if batch.num_rows() > u32::MAX as usize
832 || primary_key.null_count() != 0
833 || primary_key.values().data_type() != &ArrowDataType::Binary
834 || primary_key.values().null_count() != 0
835 || primary_key.values().len() > batch.num_rows()
836 || timestamp.null_count() != 0
837 || sequence.null_count() != 0
838 {
839 return Ok(None);
840 }
841
842 let indices = match timestamp.data_type() {
843 ArrowDataType::Timestamp(TimeUnit::Second, _) => timestamp
844 .as_any()
845 .downcast_ref::<TimestampSecondArray>()
846 .map(|timestamp| sort_primary_key_indices(primary_key, timestamp.values(), sequence)),
847 ArrowDataType::Timestamp(TimeUnit::Millisecond, _) => timestamp
848 .as_any()
849 .downcast_ref::<TimestampMillisecondArray>()
850 .map(|timestamp| sort_primary_key_indices(primary_key, timestamp.values(), sequence)),
851 ArrowDataType::Timestamp(TimeUnit::Microsecond, _) => timestamp
852 .as_any()
853 .downcast_ref::<TimestampMicrosecondArray>()
854 .map(|timestamp| sort_primary_key_indices(primary_key, timestamp.values(), sequence)),
855 ArrowDataType::Timestamp(TimeUnit::Nanosecond, _) => timestamp
856 .as_any()
857 .downcast_ref::<TimestampNanosecondArray>()
858 .map(|timestamp| sort_primary_key_indices(primary_key, timestamp.values(), sequence)),
859 _ => None,
860 };
861
862 indices.transpose()
863}
864
865fn sort_primary_key_indices(
868 primary_key: &PrimaryKeyArray,
869 timestamps: &[i64],
870 sequences: &UInt64Array,
871) -> Result<UInt32Array> {
872 debug_assert_eq!(primary_key.len(), timestamps.len());
873 debug_assert_eq!(primary_key.len(), sequences.len());
874 debug_assert_eq!(primary_key.null_count(), 0);
875 debug_assert_eq!(primary_key.values().null_count(), 0);
876 debug_assert_eq!(sequences.null_count(), 0);
877
878 let value_ranks = datatypes::arrow::compute::rank(
882 primary_key.values().as_ref(),
883 Some(SortOptions {
884 descending: false,
885 nulls_first: true,
886 }),
887 )
888 .context(ComputeArrowSnafu)?;
889
890 let mut counts = vec![0usize; value_ranks.len() + 1];
893 for &key in primary_key.keys().values() {
894 counts[value_ranks[key as usize] as usize] += 1;
895 }
896
897 let mut bucket_offsets = vec![0usize; counts.len()];
898 let mut offset = 0;
899 for (bucket_offset, &count) in bucket_offsets.iter_mut().zip(&counts) {
900 *bucket_offset = offset;
901 offset += count;
902 }
903
904 let mut next_offsets = bucket_offsets.clone();
906 let mut indices = vec![0u32; primary_key.len()];
907 for (row, &key) in primary_key.keys().values().iter().enumerate() {
908 let rank = value_ranks[key as usize] as usize;
909 indices[next_offsets[rank]] = row as u32;
910 next_offsets[rank] += 1;
911 }
912
913 let sequences = sequences.values();
914 for (&start, count) in bucket_offsets.iter().zip(counts) {
915 let end = start + count;
916 let bucket = &mut indices[start..end];
917 if !bucket.is_sorted_by(|left, right| {
918 compare_time_sequence(timestamps, sequences, *left, *right).is_le()
919 }) {
920 bucket.sort_unstable_by(|left, right| {
921 compare_time_sequence(timestamps, sequences, *left, *right)
922 });
923 }
924 }
925
926 Ok(UInt32Array::from(indices))
927}
928
929#[inline]
930fn compare_time_sequence(
931 timestamps: &[i64],
932 sequences: &[u64],
933 left: u32,
934 right: u32,
935) -> std::cmp::Ordering {
936 let left = left as usize;
937 let right = right as usize;
938 timestamps[left]
939 .cmp(×tamps[right])
940 .then_with(|| sequences[right].cmp(&sequences[left]))
941}
942
943fn lexsort_primary_key_indices(batch: &RecordBatch) -> Result<UInt32Array> {
944 let total_columns = batch.num_columns();
945 let sort_columns = vec![
946 SortColumn {
948 values: batch.column(total_columns - 3).clone(),
949 options: Some(SortOptions {
950 descending: false,
951 nulls_first: true,
952 }),
953 },
954 SortColumn {
956 values: batch.column(total_columns - 4).clone(),
957 options: Some(SortOptions {
958 descending: false,
959 nulls_first: true,
960 }),
961 },
962 SortColumn {
964 values: batch.column(total_columns - 2).clone(),
965 options: Some(SortOptions {
966 descending: true,
967 nulls_first: true,
968 }),
969 },
970 ];
971
972 datatypes::arrow::compute::lexsort_to_indices(&sort_columns, None).context(ComputeArrowSnafu)
973}
974
975pub fn convert_bulk_part(
1001 part: BulkPart,
1002 region_metadata: &RegionMetadataRef,
1003 primary_key_codec: Arc<dyn PrimaryKeyCodec>,
1004 schema: SchemaRef,
1005 store_primary_key_columns: bool,
1006) -> Result<Option<BulkPart>> {
1007 if part.num_rows() == 0 {
1008 return Ok(None);
1009 }
1010
1011 let num_rows = part.num_rows();
1012 let is_sparse = region_metadata.primary_key_encoding == PrimaryKeyEncoding::Sparse;
1013
1014 let input_schema = part.batch.schema();
1016 let column_indices: HashMap<&str, usize> = input_schema
1017 .fields()
1018 .iter()
1019 .enumerate()
1020 .map(|(idx, field)| (field.name().as_str(), idx))
1021 .collect();
1022
1023 let mut output_columns = Vec::new();
1025
1026 let pk_array = if is_sparse {
1028 None
1031 } else {
1032 let pk_vectors: Result<Vec<_>> = region_metadata
1034 .primary_key_columns()
1035 .map(|col_meta| {
1036 let col_idx = column_indices
1037 .get(col_meta.column_schema.name.as_str())
1038 .context(ColumnNotFoundSnafu {
1039 column: &col_meta.column_schema.name,
1040 })?;
1041 let col = part.batch.column(*col_idx);
1042 Helper::try_into_vector(col).context(error::ComputeVectorSnafu)
1043 })
1044 .collect();
1045 let pk_vectors = pk_vectors?;
1046
1047 let mut key_array_builder = PrimaryKeyArrayBuilder::new();
1048 let mut encode_buf = Vec::new();
1049
1050 for row_idx in 0..num_rows {
1051 encode_buf.clear();
1052
1053 let pk_values_with_ids: Vec<_> = region_metadata
1055 .primary_key
1056 .iter()
1057 .zip(pk_vectors.iter())
1058 .map(|(col_id, vector)| (*col_id, vector.get_ref(row_idx)))
1059 .collect();
1060
1061 primary_key_codec
1063 .encode_value_refs(&pk_values_with_ids, &mut encode_buf)
1064 .context(EncodeSnafu)?;
1065
1066 key_array_builder
1067 .append(&encode_buf)
1068 .context(ComputeArrowSnafu)?;
1069 }
1070
1071 Some(key_array_builder.finish())
1072 };
1073
1074 if store_primary_key_columns && !is_sparse {
1076 for col_meta in region_metadata.primary_key_columns() {
1077 let col_idx = column_indices
1078 .get(col_meta.column_schema.name.as_str())
1079 .context(ColumnNotFoundSnafu {
1080 column: &col_meta.column_schema.name,
1081 })?;
1082 let col = part.batch.column(*col_idx);
1083
1084 let col = if col_meta.column_schema.data_type.is_string() {
1086 let target_type = ArrowDataType::Dictionary(
1087 Box::new(ArrowDataType::UInt32),
1088 Box::new(ArrowDataType::Utf8),
1089 );
1090 arrow::compute::cast(col, &target_type).context(ComputeArrowSnafu)?
1091 } else {
1092 col.clone()
1093 };
1094 output_columns.push(col);
1095 }
1096 }
1097
1098 for col_meta in region_metadata.field_columns() {
1100 let col_idx = column_indices
1101 .get(col_meta.column_schema.name.as_str())
1102 .context(ColumnNotFoundSnafu {
1103 column: &col_meta.column_schema.name,
1104 })?;
1105 output_columns.push(part.batch.column(*col_idx).clone());
1106 }
1107
1108 let new_timestamp_index = output_columns.len();
1110 let ts_col_idx = column_indices
1111 .get(
1112 region_metadata
1113 .time_index_column()
1114 .column_schema
1115 .name
1116 .as_str(),
1117 )
1118 .context(ColumnNotFoundSnafu {
1119 column: ®ion_metadata.time_index_column().column_schema.name,
1120 })?;
1121 output_columns.push(part.batch.column(*ts_col_idx).clone());
1122
1123 let pk_dictionary = if let Some(pk_dict_array) = pk_array {
1125 Arc::new(pk_dict_array) as ArrayRef
1126 } else {
1127 let pk_col_idx =
1128 column_indices
1129 .get(PRIMARY_KEY_COLUMN_NAME)
1130 .context(ColumnNotFoundSnafu {
1131 column: PRIMARY_KEY_COLUMN_NAME,
1132 })?;
1133 let col = part.batch.column(*pk_col_idx);
1134
1135 let target_type = ArrowDataType::Dictionary(
1137 Box::new(ArrowDataType::UInt32),
1138 Box::new(ArrowDataType::Binary),
1139 );
1140 arrow::compute::cast(col, &target_type).context(ComputeArrowSnafu)?
1141 };
1142 output_columns.push(pk_dictionary);
1143
1144 let sequence_array = UInt64Array::from(vec![part.sequence; num_rows]);
1145 output_columns.push(Arc::new(sequence_array) as ArrayRef);
1146
1147 let op_type_array = UInt8Array::from(vec![OpType::Put as u8; num_rows]);
1148 output_columns.push(Arc::new(op_type_array) as ArrayRef);
1149
1150 let batch = RecordBatch::try_new(schema, output_columns).context(NewRecordBatchSnafu)?;
1151
1152 let sorted_batch = sort_primary_key_record_batch(&batch)?;
1154
1155 Ok(Some(BulkPart {
1156 batch: sorted_batch,
1157 max_timestamp: part.max_timestamp,
1158 min_timestamp: part.min_timestamp,
1159 sequence: part.sequence,
1160 min_sequence: part.sequence,
1161 timestamp_index: new_timestamp_index,
1162 raw_data: None,
1163 }))
1164}
1165
1166#[derive(Debug, Clone)]
1167pub struct EncodedBulkPart {
1168 data: Bytes,
1169 metadata: BulkPartMeta,
1170 schema: SchemaRef,
1172}
1173
1174impl EncodedBulkPart {
1175 pub fn new(data: Bytes, metadata: BulkPartMeta, schema: SchemaRef) -> Self {
1176 Self {
1177 data,
1178 metadata,
1179 schema,
1180 }
1181 }
1182
1183 pub fn metadata(&self) -> &BulkPartMeta {
1184 &self.metadata
1185 }
1186
1187 pub(crate) fn schema(&self) -> SchemaRef {
1188 self.schema.clone()
1189 }
1190
1191 pub(crate) fn size_bytes(&self) -> usize {
1193 self.data.len()
1194 }
1195
1196 pub fn data(&self) -> &Bytes {
1198 &self.data
1199 }
1200
1201 pub fn to_memtable_stats(&self) -> MemtableStats {
1203 let meta = &self.metadata;
1204 let ts_type = meta.region_metadata.time_index_type();
1205 let min_ts = ts_type.create_timestamp(meta.min_timestamp);
1206 let max_ts = ts_type.create_timestamp(meta.max_timestamp);
1207
1208 MemtableStats {
1209 estimated_bytes: self.size_bytes(),
1210 time_range: Some((min_ts, max_ts)),
1211 num_rows: meta.num_rows,
1212 num_ranges: 1,
1213 max_sequence: meta.max_sequence,
1214 min_sequence: meta.min_sequence,
1215 series_count: meta.num_series as usize,
1216 }
1217 }
1218
1219 pub(crate) fn to_sst_info(&self, file_id: FileId) -> SstInfo {
1227 let unit = self.metadata.region_metadata.time_index_type().unit();
1228 let max_row_group_uncompressed_size: u64 = self
1229 .metadata
1230 .parquet_metadata
1231 .row_groups()
1232 .iter()
1233 .map(|rg| {
1234 rg.columns()
1235 .iter()
1236 .map(|c| c.uncompressed_size() as u64)
1237 .sum::<u64>()
1238 })
1239 .max()
1240 .unwrap_or(0);
1241 SstInfo {
1242 file_id,
1243 time_range: (
1244 Timestamp::new(self.metadata.min_timestamp, unit),
1245 Timestamp::new(self.metadata.max_timestamp, unit),
1246 ),
1247 file_size: self.data.len() as u64,
1248 max_row_group_uncompressed_size,
1249 num_rows: self.metadata.num_rows,
1250 num_row_groups: self.metadata.parquet_metadata.num_row_groups() as u64,
1251 file_metadata: Some(self.metadata.parquet_metadata.clone()),
1252 index_metadata: IndexOutput::default(),
1253 num_series: self.metadata.num_series,
1254 }
1255 }
1256
1257 pub(crate) fn read(
1258 &self,
1259 context: BulkIterContextRef,
1260 sequence: Option<SequenceRange>,
1261 mem_scan_metrics: Option<MemScanMetrics>,
1262 ) -> Result<Option<BoxedRecordBatchIterator>> {
1263 let skip_fields_for_pruning = context.pre_filter_mode().skip_fields();
1265
1266 let row_groups_to_read =
1268 context.row_groups_to_read(&self.metadata.parquet_metadata, skip_fields_for_pruning);
1269
1270 if row_groups_to_read.is_empty() {
1271 return Ok(None);
1273 }
1274
1275 let iter = EncodedBulkPartIter::try_new(
1276 self,
1277 context,
1278 row_groups_to_read,
1279 sequence,
1280 mem_scan_metrics,
1281 )?;
1282 Ok(Some(Box::new(iter) as BoxedRecordBatchIterator))
1283 }
1284}
1285
1286#[derive(Debug, Clone)]
1288pub struct BulkPartMeta {
1289 pub num_rows: usize,
1291 pub max_timestamp: i64,
1293 pub min_timestamp: i64,
1295 pub parquet_metadata: Arc<ParquetMetaData>,
1297 pub region_metadata: RegionMetadataRef,
1299 pub num_series: u64,
1301 pub min_sequence: SequenceNumber,
1303 pub max_sequence: u64,
1305}
1306
1307#[derive(Default, Debug)]
1309pub struct BulkPartEncodeMetrics {
1310 pub iter_cost: Duration,
1312 pub write_cost: Duration,
1314 pub raw_size: usize,
1316 pub encoded_size: usize,
1318 pub num_rows: usize,
1320}
1321
1322pub struct BulkPartEncoder {
1323 metadata: RegionMetadataRef,
1324 writer_props: Option<WriterProperties>,
1325}
1326
1327impl BulkPartEncoder {
1328 pub fn new(metadata: RegionMetadataRef, row_group_size: usize) -> Result<BulkPartEncoder> {
1329 Self::new_with_float_field_encoding(metadata, row_group_size, FloatFieldEncoding::default())
1330 }
1331
1332 pub(super) fn new_with_float_field_encoding(
1333 metadata: RegionMetadataRef,
1334 row_group_size: usize,
1335 float_field_encoding: FloatFieldEncoding,
1336 ) -> Result<BulkPartEncoder> {
1337 let json = metadata.to_json().context(InvalidMetadataSnafu)?;
1339 let key_value_meta =
1340 parquet::file::metadata::KeyValue::new(PARQUET_METADATA_KEY.to_string(), json);
1341
1342 let mut props = WriterProperties::builder()
1343 .set_key_value_metadata(Some(vec![key_value_meta]))
1344 .set_write_batch_size(row_group_size)
1345 .set_max_row_group_row_count(Some(row_group_size))
1346 .set_compression(Compression::ZSTD(ZstdLevel::default()))
1347 .set_column_index_truncate_length(COLUMN_INDEX_TRUNCATE_LENGTH)
1348 .set_statistics_truncate_length(None);
1349 props = apply_internal_column_encoding(props, &metadata);
1350 props = apply_float_field_encoding(props, &metadata, float_field_encoding);
1351 props = apply_column_compression(props, &metadata)?;
1352 let writer_props = Some(props.build());
1353
1354 Ok(Self {
1355 metadata,
1356 writer_props,
1357 })
1358 }
1359}
1360
1361impl BulkPartEncoder {
1362 pub fn encode_record_batch_iter(
1364 &self,
1365 iter: BoxedRecordBatchIterator,
1366 arrow_schema: SchemaRef,
1367 min_timestamp: i64,
1368 max_timestamp: i64,
1369 sequence_bounds: RangeInclusive<SequenceNumber>,
1370 metrics: &mut BulkPartEncodeMetrics,
1371 ) -> Result<Option<EncodedBulkPart>> {
1372 let mut buf = Vec::with_capacity(4096);
1373 let mut writer =
1374 ArrowWriter::try_new(&mut buf, arrow_schema.clone(), self.writer_props.clone())
1375 .context(EncodeMemtableSnafu)?;
1376 let mut total_rows = 0;
1377 let mut series_estimator = SeriesEstimator::default();
1378
1379 let mut iter_start = Instant::now();
1381 for batch_result in iter {
1382 metrics.iter_cost += iter_start.elapsed();
1383 let batch = batch_result?;
1384 if batch.num_rows() == 0 {
1385 continue;
1386 }
1387
1388 series_estimator.update_flat(&batch);
1389 metrics.raw_size += record_batch_estimated_size(&batch);
1390 let write_start = Instant::now();
1391 writer.write(&batch).context(EncodeMemtableSnafu)?;
1392 metrics.write_cost += write_start.elapsed();
1393 total_rows += batch.num_rows();
1394 iter_start = Instant::now();
1395 }
1396 metrics.iter_cost += iter_start.elapsed();
1397
1398 if total_rows == 0 {
1399 return Ok(None);
1400 }
1401
1402 let close_start = Instant::now();
1403 let file_metadata = writer.close().context(EncodeMemtableSnafu)?;
1404 metrics.write_cost += close_start.elapsed();
1405 metrics.encoded_size += buf.len();
1406 metrics.num_rows += total_rows;
1407
1408 let buf = Bytes::from(buf);
1409 let parquet_metadata = Arc::new(file_metadata);
1410 let num_series = series_estimator.finish();
1411
1412 Ok(Some(EncodedBulkPart {
1413 data: buf,
1414 metadata: BulkPartMeta {
1415 num_rows: total_rows,
1416 max_timestamp,
1417 min_timestamp,
1418 parquet_metadata,
1419 region_metadata: self.metadata.clone(),
1420 num_series,
1421 min_sequence: *sequence_bounds.start(),
1422 max_sequence: *sequence_bounds.end(),
1423 },
1424 schema: arrow_schema,
1425 }))
1426 }
1427
1428 pub fn encode_part(&self, part: &BulkPart) -> Result<Option<EncodedBulkPart>> {
1430 if part.batch.num_rows() == 0 {
1431 return Ok(None);
1432 }
1433
1434 let mut buf = Vec::with_capacity(4096);
1435 let arrow_schema = part.batch.schema();
1436
1437 let file_metadata = {
1438 let mut writer =
1439 ArrowWriter::try_new(&mut buf, arrow_schema.clone(), self.writer_props.clone())
1440 .context(EncodeMemtableSnafu)?;
1441 writer.write(&part.batch).context(EncodeMemtableSnafu)?;
1442 writer.finish().context(EncodeMemtableSnafu)?
1443 };
1444
1445 let buf = Bytes::from(buf);
1446 let parquet_metadata = Arc::new(file_metadata);
1447
1448 Ok(Some(EncodedBulkPart {
1449 data: buf,
1450 metadata: BulkPartMeta {
1451 num_rows: part.batch.num_rows(),
1452 max_timestamp: part.max_timestamp,
1453 min_timestamp: part.min_timestamp,
1454 parquet_metadata,
1455 region_metadata: self.metadata.clone(),
1456 num_series: part.estimated_series_count() as u64,
1457 max_sequence: part.sequence,
1458 min_sequence: part.min_sequence,
1459 },
1460 schema: arrow_schema,
1461 }))
1462 }
1463}
1464
1465#[derive(Debug, Clone)]
1471struct BatchStats {
1472 num_batches: usize,
1474 first_tag_id: ColumnId,
1476 min_values: ArrayRef,
1478 max_values: ArrayRef,
1480}
1481
1482impl BatchStats {
1483 fn compute(batches: &[RecordBatch], metadata: &RegionMetadata) -> Option<Self> {
1488 let first_tag_id = *metadata.primary_key.first()?;
1493 let first_tag_column = metadata.column_by_id(first_tag_id)?;
1494 let data_type = &first_tag_column.column_schema.data_type;
1495
1496 let converter = build_primary_key_codec_with_fields(
1497 metadata.primary_key_encoding,
1498 [(first_tag_id, SortField::new(data_type.clone()))].into_iter(),
1499 );
1500 let pk_index = primary_key_column_index(batches.first()?.num_columns());
1501
1502 let mut min_builder = data_type.create_mutable_vector(batches.len());
1503 let mut max_builder = data_type.create_mutable_vector(batches.len());
1504
1505 for batch in batches {
1506 match Self::extract_first_tag_bounds(batch, pk_index, &*converter) {
1507 Some((min_val, max_val)) => {
1508 min_builder.push_value_ref(&min_val.as_value_ref());
1509 max_builder.push_value_ref(&max_val.as_value_ref());
1510 }
1511 None => {
1512 min_builder.push_null();
1513 max_builder.push_null();
1514 }
1515 }
1516 }
1517
1518 Some(Self {
1519 num_batches: batches.len(),
1520 first_tag_id,
1521 min_values: min_builder.to_vector().to_arrow_array(),
1522 max_values: max_builder.to_vector().to_arrow_array(),
1523 })
1524 }
1525
1526 fn extract_first_tag_bounds(
1528 batch: &RecordBatch,
1529 pk_index: usize,
1530 converter: &dyn PrimaryKeyCodec,
1531 ) -> Option<(datatypes::value::Value, datatypes::value::Value)> {
1532 if batch.num_rows() == 0 {
1533 return None;
1534 }
1535
1536 let pk_dict = batch
1537 .column(pk_index)
1538 .as_any()
1539 .downcast_ref::<PrimaryKeyArray>()?;
1540 let pk_values = pk_dict.values().as_any().downcast_ref::<BinaryArray>()?;
1541
1542 let keys = pk_dict.keys();
1543 let min_key = keys.value(0);
1544 let max_key = keys.value(batch.num_rows() - 1);
1545 let min_bytes = pk_values.value(min_key as usize);
1546 let max_bytes = pk_values.value(max_key as usize);
1547
1548 Some((
1549 converter.decode_leftmost(min_bytes).ok()??,
1550 converter.decode_leftmost(max_bytes).ok()??,
1551 ))
1552 }
1553}
1554
1555struct BatchPruningStats<'a> {
1560 stats: &'a BatchStats,
1561 metadata: &'a RegionMetadataRef,
1562}
1563
1564impl PruningStatistics for BatchPruningStats<'_> {
1565 fn min_values(&self, column: &Column) -> Option<ArrayRef> {
1566 let col = self.metadata.column_by_name(&column.name)?;
1567 if col.column_id == self.stats.first_tag_id {
1568 Some(self.stats.min_values.clone())
1569 } else {
1570 None
1571 }
1572 }
1573
1574 fn max_values(&self, column: &Column) -> Option<ArrayRef> {
1575 let col = self.metadata.column_by_name(&column.name)?;
1576 if col.column_id == self.stats.first_tag_id {
1577 Some(self.stats.max_values.clone())
1578 } else {
1579 None
1580 }
1581 }
1582
1583 fn num_containers(&self) -> usize {
1584 self.stats.num_batches
1585 }
1586
1587 fn null_counts(&self, _column: &Column) -> Option<ArrayRef> {
1588 None
1589 }
1590
1591 fn row_counts(&self) -> Option<ArrayRef> {
1592 None
1593 }
1594
1595 fn contained(
1596 &self,
1597 _column: &Column,
1598 _values: &std::collections::HashSet<datafusion_common::ScalarValue>,
1599 ) -> Option<BooleanArray> {
1600 None
1601 }
1602}
1603
1604fn predicate_references_column(predicate: &table::predicate::Predicate, column_name: &str) -> bool {
1606 let mut columns = HashSet::new();
1607 for expr in predicate.exprs() {
1608 let _ = expr_to_columns(expr, &mut columns);
1609 }
1610 columns.iter().any(|col| col.name == column_name)
1611}
1612
1613pub(crate) fn should_prune_bulk_part(
1617 batch: &RecordBatch,
1618 context: &BulkIterContext,
1619 metadata: &RegionMetadata,
1620) -> bool {
1621 let predicate = match &context.predicate {
1622 Some(p) => p,
1623 None => return false,
1624 };
1625 let first_tag_id = match metadata.primary_key.first() {
1628 Some(id) => *id,
1629 None => return false,
1630 };
1631 let first_tag_name = &metadata
1633 .column_by_id(first_tag_id)
1634 .unwrap()
1635 .column_schema
1636 .name;
1637 if !predicate_references_column(predicate, first_tag_name) {
1638 return false;
1639 }
1640 let stats = match BatchStats::compute(std::slice::from_ref(batch), metadata) {
1641 Some(s) => s,
1642 None => return false,
1643 };
1644 let region_meta = context.read_format().metadata();
1645 let pruning_stats = BatchPruningStats {
1646 stats: &stats,
1647 metadata: region_meta,
1648 };
1649 let mask = predicate.prune_with_stats(&pruning_stats, region_meta.schema.arrow_schema());
1650 !mask.first().copied().unwrap_or(true)
1651}
1652
1653#[derive(Debug, Clone)]
1659pub struct MultiBulkPart {
1660 batches: SmallVec<[RecordBatch; 4]>,
1662 total_rows: usize,
1664 max_timestamp: i64,
1666 min_timestamp: i64,
1668 min_sequence: SequenceNumber,
1670 max_sequence: SequenceNumber,
1672 series_count: usize,
1674 batch_stats: Option<BatchStats>,
1677}
1678
1679impl MultiBulkPart {
1680 pub fn from_bulk_part(part: BulkPart, metadata: &RegionMetadata) -> Self {
1682 let num_rows = part.num_rows();
1683 let series_count = part.estimated_series_count();
1684 let batch_stats = BatchStats::compute(std::slice::from_ref(&part.batch), metadata);
1685 let mut batches = SmallVec::new();
1686 batches.push(part.batch);
1687
1688 Self {
1689 batches,
1690 total_rows: num_rows,
1691 max_timestamp: part.max_timestamp,
1692 min_timestamp: part.min_timestamp,
1693 min_sequence: part.min_sequence,
1694 max_sequence: part.sequence,
1695 series_count,
1696 batch_stats,
1697 }
1698 }
1699
1700 pub fn new(
1714 batches: Vec<RecordBatch>,
1715 min_timestamp: i64,
1716 max_timestamp: i64,
1717 min_sequence: SequenceNumber,
1718 max_sequence: SequenceNumber,
1719 series_count: usize,
1720 metadata: &RegionMetadata,
1721 ) -> Self {
1722 assert!(!batches.is_empty(), "batches must not be empty");
1723
1724 let total_rows = batches.iter().map(|b| b.num_rows()).sum();
1725 let batch_stats = BatchStats::compute(&batches, metadata);
1726
1727 Self {
1728 batches: SmallVec::from_vec(batches),
1729 total_rows,
1730 max_timestamp,
1731 min_timestamp,
1732 min_sequence,
1733 max_sequence,
1734 series_count,
1735 batch_stats,
1736 }
1737 }
1738
1739 pub fn num_rows(&self) -> usize {
1741 self.total_rows
1742 }
1743
1744 pub(crate) fn schemas(&self) -> impl Iterator<Item = SchemaRef> + '_ {
1745 self.batches.iter().map(|batch| batch.schema())
1746 }
1747
1748 pub fn min_timestamp(&self) -> i64 {
1750 self.min_timestamp
1751 }
1752
1753 pub fn max_timestamp(&self) -> i64 {
1755 self.max_timestamp
1756 }
1757
1758 pub fn min_sequence(&self) -> SequenceNumber {
1760 self.min_sequence
1761 }
1762
1763 pub fn max_sequence(&self) -> SequenceNumber {
1765 self.max_sequence
1766 }
1767
1768 pub fn series_count(&self) -> usize {
1770 self.series_count
1771 }
1772
1773 pub fn num_batches(&self) -> usize {
1775 self.batches.len()
1776 }
1777
1778 pub(crate) fn estimated_size(&self) -> usize {
1780 self.batches.iter().map(record_batch_estimated_size).sum()
1781 }
1782
1783 pub(crate) fn read(
1789 &self,
1790 context: BulkIterContextRef,
1791 sequence: Option<SequenceRange>,
1792 mem_scan_metrics: Option<MemScanMetrics>,
1793 ) -> Result<Option<BoxedRecordBatchIterator>> {
1794 if self.batches.is_empty() {
1795 return Ok(None);
1796 }
1797
1798 let batches_to_read = self.prune_batches(&context);
1799
1800 if batches_to_read.is_empty() {
1801 return Ok(None);
1802 }
1803
1804 let iter = crate::memtable::bulk::part_reader::BulkPartBatchIter::new(
1805 batches_to_read,
1806 context,
1807 sequence,
1808 self.series_count,
1809 mem_scan_metrics,
1810 );
1811 Ok(Some(Box::new(iter) as BoxedRecordBatchIterator))
1812 }
1813
1814 fn prune_batches(&self, context: &BulkIterContextRef) -> Vec<RecordBatch> {
1817 if let Some(stats) = &self.batch_stats
1818 && let Some(predicate) = &context.predicate
1819 {
1820 let region_meta = context.read_format().metadata();
1821 let pruning_stats = BatchPruningStats {
1822 stats,
1823 metadata: region_meta,
1824 };
1825 let mask =
1826 predicate.prune_with_stats(&pruning_stats, region_meta.schema.arrow_schema());
1827 self.batches
1828 .iter()
1829 .zip(mask.iter())
1830 .filter_map(
1831 |(batch, &selected)| {
1832 if selected { Some(batch.clone()) } else { None }
1833 },
1834 )
1835 .collect()
1836 } else {
1837 self.batches.iter().cloned().collect()
1838 }
1839 }
1840
1841 pub fn to_memtable_stats(&self, region_metadata: &RegionMetadataRef) -> MemtableStats {
1843 let ts_type = region_metadata.time_index_type();
1844 let min_ts = ts_type.create_timestamp(self.min_timestamp);
1845 let max_ts = ts_type.create_timestamp(self.max_timestamp);
1846
1847 MemtableStats {
1848 estimated_bytes: self.estimated_size(),
1849 time_range: Some((min_ts, max_ts)),
1850 num_rows: self.num_rows(),
1851 num_ranges: 1,
1852 max_sequence: self.max_sequence,
1853 min_sequence: self.min_sequence,
1854 series_count: self.series_count,
1855 }
1856 }
1857}
1858
1859#[cfg(test)]
1860mod tests {
1861 use api::v1::{Row, SemanticType, WriteHint};
1862 use datafusion_common::ScalarValue;
1863 use datatypes::arrow::array::{
1864 BinaryArray, DictionaryArray, Float64Array, TimestampMillisecondArray,
1865 };
1866 use datatypes::arrow::datatypes::UInt32Type;
1867 use datatypes::prelude::{ConcreteDataType, Value};
1868 use datatypes::schema::ColumnSchema;
1869 use mito_codec::row_converter::build_primary_key_codec;
1870 use rand::rngs::StdRng;
1871 use rand::{Rng, SeedableRng};
1872 use store_api::metadata::{ColumnMetadata, RegionMetadataBuilder};
1873 use store_api::storage::RegionId;
1874 use store_api::storage::consts::ReservedColumnId;
1875 use table::predicate::Predicate;
1876
1877 use super::*;
1878 use crate::memtable::bulk::context::BulkIterContext;
1879 use crate::sst::{FlatSchemaOptions, to_flat_sst_arrow_schema};
1880 use crate::test_util::memtable_util::{build_key_values_with_ts_seq_values, metadata_for_test};
1881
1882 struct MutationInput<'a> {
1883 k0: &'a str,
1884 k1: u32,
1885 timestamps: &'a [i64],
1886 v1: &'a [Option<f64>],
1887 sequence: u64,
1888 }
1889
1890 #[test]
1891 fn test_sort_primary_key_record_batch_with_duplicate_dictionary_values() {
1892 let primary_key = DictionaryArray::try_new(
1895 UInt32Array::from(vec![0, 1, 0, 2, 2]),
1896 Arc::new(BinaryArray::from_vec(vec![
1897 b"series_b".as_slice(),
1898 b"series_a".as_slice(),
1899 b"series_a".as_slice(),
1900 ])),
1901 )
1902 .unwrap();
1903 let timestamps = TimestampMillisecondArray::from(vec![20, 30, 10, 30, 10]);
1904 let sequences = UInt64Array::from(vec![5, 6, 4, 8, 9]);
1905 let row_ids = UInt32Array::from_iter_values(0..5);
1906 let op_types = UInt8Array::from_value(OpType::Put as u8, 5);
1907 let schema = Arc::new(Schema::new(vec![
1908 Field::new("row_id", ArrowDataType::UInt32, false),
1909 Field::new(
1910 "ts",
1911 ArrowDataType::Timestamp(TimeUnit::Millisecond, None),
1912 false,
1913 ),
1914 Field::new(
1915 PRIMARY_KEY_COLUMN_NAME,
1916 ArrowDataType::Dictionary(
1917 Box::new(ArrowDataType::UInt32),
1918 Box::new(ArrowDataType::Binary),
1919 ),
1920 false,
1921 ),
1922 Field::new("__sequence", ArrowDataType::UInt64, false),
1923 Field::new("__op_type", ArrowDataType::UInt8, false),
1924 ]));
1925 let batch = RecordBatch::try_new(
1926 schema,
1927 vec![
1928 Arc::new(row_ids),
1929 Arc::new(timestamps),
1930 Arc::new(primary_key),
1931 Arc::new(sequences),
1932 Arc::new(op_types),
1933 ],
1934 )
1935 .unwrap();
1936
1937 let expected_indices = lexsort_primary_key_indices(&batch).unwrap();
1938 let expected =
1939 datatypes::arrow::compute::take_record_batch(&batch, &expected_indices).unwrap();
1940 let actual = sort_primary_key_record_batch(&batch).unwrap();
1941
1942 let expected_row_ids = expected
1943 .column(0)
1944 .as_any()
1945 .downcast_ref::<UInt32Array>()
1946 .unwrap();
1947 let actual_row_ids = actual
1948 .column(0)
1949 .as_any()
1950 .downcast_ref::<UInt32Array>()
1951 .unwrap();
1952 assert_eq!(expected_row_ids, actual_row_ids);
1953 assert_eq!(actual_row_ids.values(), &[4, 3, 1, 2, 0]);
1954 }
1955
1956 #[test]
1957 fn test_sort_primary_key_record_batch_against_lexsort() {
1958 let mut rng = StdRng::seed_from_u64(0x5eed);
1959 for _ in 0..100 {
1960 let num_rows = rng.random_range(0..128);
1961 let keys = UInt32Array::from_iter_values((0..num_rows).map(|_| rng.random_range(0..8)));
1962 let primary_key = DictionaryArray::try_new(
1963 keys,
1964 Arc::new(BinaryArray::from_vec(vec![
1965 b"d".as_slice(),
1966 b"a".as_slice(),
1967 b"c".as_slice(),
1968 b"b".as_slice(),
1969 b"a".as_slice(),
1970 b"d".as_slice(),
1971 b"b".as_slice(),
1972 b"c".as_slice(),
1973 ])),
1974 )
1975 .unwrap();
1976 let timestamps = TimestampMillisecondArray::from_iter_values(
1977 (0..num_rows).map(|_| rng.random_range(-1000..1000)),
1978 );
1979 let sequences = UInt64Array::from_iter_values(0..num_rows as u64);
1981 let row_ids = UInt32Array::from_iter_values(0..num_rows as u32);
1982 let op_types = UInt8Array::from_value(OpType::Put as u8, num_rows);
1983 let schema = Arc::new(Schema::new(vec![
1984 Field::new("row_id", ArrowDataType::UInt32, false),
1985 Field::new(
1986 "ts",
1987 ArrowDataType::Timestamp(TimeUnit::Millisecond, None),
1988 false,
1989 ),
1990 Field::new(
1991 PRIMARY_KEY_COLUMN_NAME,
1992 ArrowDataType::Dictionary(
1993 Box::new(ArrowDataType::UInt32),
1994 Box::new(ArrowDataType::Binary),
1995 ),
1996 false,
1997 ),
1998 Field::new("__sequence", ArrowDataType::UInt64, false),
1999 Field::new("__op_type", ArrowDataType::UInt8, false),
2000 ]));
2001 let batch = RecordBatch::try_new(
2002 schema,
2003 vec![
2004 Arc::new(row_ids),
2005 Arc::new(timestamps),
2006 Arc::new(primary_key),
2007 Arc::new(sequences),
2008 Arc::new(op_types),
2009 ],
2010 )
2011 .unwrap();
2012
2013 let expected_indices = lexsort_primary_key_indices(&batch).unwrap();
2014 let expected =
2015 datatypes::arrow::compute::take_record_batch(&batch, &expected_indices).unwrap();
2016 let actual = sort_primary_key_record_batch(&batch).unwrap();
2017 assert_eq!(expected.column(0), actual.column(0));
2018 }
2019 }
2020
2021 #[test]
2022 fn test_unordered_part_tracks_estimated_bytes() {
2023 let mut part = UnorderedPart::new();
2024 let bulk_part = BulkPart {
2025 batch: RecordBatch::new_empty(Arc::new(arrow::datatypes::Schema::empty())),
2026 max_timestamp: 0,
2027 min_timestamp: 0,
2028 sequence: 0,
2029 min_sequence: 0,
2030 timestamp_index: 0,
2031 raw_data: None,
2032 };
2033 let estimated_size = bulk_part.estimated_size();
2034
2035 part.push(bulk_part);
2036 assert_eq!(estimated_size, part.estimated_bytes());
2037 part.clear();
2038 assert_eq!(0, part.estimated_bytes());
2039 assert!(part.is_empty());
2040 }
2041
2042 #[test]
2043 fn test_unordered_part_should_accept() {
2044 let mut part = UnorderedPart::new();
2045 part.set_threshold(10);
2046 assert!(part.should_accept(9));
2047 assert!(!part.should_accept(10));
2048 }
2049
2050 fn encode(input: &[MutationInput]) -> EncodedBulkPart {
2051 let metadata = metadata_for_test();
2052 let kvs = input
2053 .iter()
2054 .map(|m| {
2055 build_key_values_with_ts_seq_values(
2056 &metadata,
2057 m.k0.to_string(),
2058 m.k1,
2059 m.timestamps.iter().copied(),
2060 m.v1.iter().copied(),
2061 m.sequence,
2062 )
2063 })
2064 .collect::<Vec<_>>();
2065 let schema = to_flat_sst_arrow_schema(&metadata, &FlatSchemaOptions::default());
2066 let primary_key_codec = build_primary_key_codec(&metadata);
2067 let mut converter = BulkPartConverter::new(&metadata, schema, 64, primary_key_codec, true);
2068 for kv in kvs {
2069 converter.append_key_values(&kv).unwrap();
2070 }
2071 let part = converter.convert().unwrap();
2072 let encoder = BulkPartEncoder::new(metadata, 1024).unwrap();
2073 encoder.encode_part(&part).unwrap().unwrap()
2074 }
2075
2076 #[test]
2077 fn test_write_and_read_part_projection() {
2078 let part = encode(&[
2079 MutationInput {
2080 k0: "a",
2081 k1: 0,
2082 timestamps: &[1],
2083 v1: &[Some(0.1)],
2084 sequence: 0,
2085 },
2086 MutationInput {
2087 k0: "b",
2088 k1: 0,
2089 timestamps: &[1],
2090 v1: &[Some(0.0)],
2091 sequence: 0,
2092 },
2093 MutationInput {
2094 k0: "a",
2095 k1: 0,
2096 timestamps: &[2],
2097 v1: &[Some(0.2)],
2098 sequence: 1,
2099 },
2100 ]);
2101
2102 let projection = &[4u32];
2103 let reader = part
2104 .read(
2105 Arc::new(
2106 BulkIterContext::new(
2107 part.metadata.region_metadata.clone(),
2108 Some(projection.as_slice()),
2109 None,
2110 false,
2111 crate::sst::parquet::DEFAULT_READ_BATCH_SIZE,
2112 )
2113 .unwrap(),
2114 ),
2115 None,
2116 None,
2117 )
2118 .unwrap()
2119 .expect("expect at least one row group");
2120
2121 let mut total_rows_read = 0;
2122 let mut field: Vec<f64> = vec![];
2123 for res in reader {
2124 let batch = res.unwrap();
2125 assert_eq!(5, batch.num_columns());
2126 field.extend_from_slice(
2127 batch
2128 .column(0)
2129 .as_any()
2130 .downcast_ref::<Float64Array>()
2131 .unwrap()
2132 .values(),
2133 );
2134 total_rows_read += batch.num_rows();
2135 }
2136 assert_eq!(3, total_rows_read);
2137 assert_eq!(vec![0.1, 0.2, 0.0], field);
2138 }
2139
2140 fn prepare(key_values: Vec<(&str, u32, (i64, i64), u64)>) -> EncodedBulkPart {
2141 let metadata = metadata_for_test();
2142 let kvs = key_values
2143 .into_iter()
2144 .map(|(k0, k1, (start, end), sequence)| {
2145 let ts = start..end;
2146 let v1 = (start..end).map(|_| None);
2147 build_key_values_with_ts_seq_values(&metadata, k0.to_string(), k1, ts, v1, sequence)
2148 })
2149 .collect::<Vec<_>>();
2150 let schema = to_flat_sst_arrow_schema(&metadata, &FlatSchemaOptions::default());
2151 let primary_key_codec = build_primary_key_codec(&metadata);
2152 let mut converter = BulkPartConverter::new(&metadata, schema, 64, primary_key_codec, true);
2153 for kv in kvs {
2154 converter.append_key_values(&kv).unwrap();
2155 }
2156 let part = converter.convert().unwrap();
2157 let encoder = BulkPartEncoder::new(metadata, 1024).unwrap();
2158 encoder.encode_part(&part).unwrap().unwrap()
2159 }
2160
2161 fn check_prune_row_group(
2162 part: &EncodedBulkPart,
2163 predicate: Option<Predicate>,
2164 expected_rows: usize,
2165 ) {
2166 let context = Arc::new(
2167 BulkIterContext::new(
2168 part.metadata.region_metadata.clone(),
2169 None,
2170 predicate,
2171 false,
2172 crate::sst::parquet::DEFAULT_READ_BATCH_SIZE,
2173 )
2174 .unwrap(),
2175 );
2176 let reader = part
2177 .read(context, None, None)
2178 .unwrap()
2179 .expect("expect at least one row group");
2180 let mut total_rows_read = 0;
2181 for res in reader {
2182 let batch = res.unwrap();
2183 total_rows_read += batch.num_rows();
2184 }
2185 assert_eq!(expected_rows, total_rows_read);
2187 }
2188
2189 #[test]
2190 fn test_prune_row_groups() {
2191 let part = prepare(vec![
2192 ("a", 0, (0, 40), 1),
2193 ("a", 1, (0, 60), 1),
2194 ("b", 0, (0, 100), 2),
2195 ("b", 1, (100, 180), 3),
2196 ("b", 1, (180, 210), 4),
2197 ]);
2198
2199 let context = Arc::new(
2200 BulkIterContext::new(
2201 part.metadata.region_metadata.clone(),
2202 None,
2203 Some(Predicate::new(vec![datafusion_expr::col("ts").eq(
2204 datafusion_expr::lit(ScalarValue::TimestampMillisecond(Some(300), None)),
2205 )])),
2206 false,
2207 crate::sst::parquet::DEFAULT_READ_BATCH_SIZE,
2208 )
2209 .unwrap(),
2210 );
2211 assert!(part.read(context, None, None).unwrap().is_none());
2212
2213 check_prune_row_group(&part, None, 310);
2214
2215 check_prune_row_group(
2216 &part,
2217 Some(Predicate::new(vec![
2218 datafusion_expr::col("k0").eq(datafusion_expr::lit("a")),
2219 datafusion_expr::col("k1").eq(datafusion_expr::lit(0u32)),
2220 ])),
2221 40,
2222 );
2223
2224 check_prune_row_group(
2225 &part,
2226 Some(Predicate::new(vec![
2227 datafusion_expr::col("k0").eq(datafusion_expr::lit("a")),
2228 datafusion_expr::col("k1").eq(datafusion_expr::lit(1u32)),
2229 ])),
2230 60,
2231 );
2232
2233 check_prune_row_group(
2234 &part,
2235 Some(Predicate::new(vec![
2236 datafusion_expr::col("k0").eq(datafusion_expr::lit("a")),
2237 ])),
2238 100,
2239 );
2240
2241 check_prune_row_group(
2242 &part,
2243 Some(Predicate::new(vec![
2244 datafusion_expr::col("k0").eq(datafusion_expr::lit("b")),
2245 datafusion_expr::col("k1").eq(datafusion_expr::lit(0u32)),
2246 ])),
2247 100,
2248 );
2249
2250 check_prune_row_group(
2252 &part,
2253 Some(Predicate::new(vec![
2254 datafusion_expr::col("v0").eq(datafusion_expr::lit(150i64)),
2255 ])),
2256 1,
2257 );
2258 }
2259
2260 #[test]
2261 fn test_bulk_part_converter_append_and_convert() {
2262 let metadata = metadata_for_test();
2263 let capacity = 100;
2264 let primary_key_codec = build_primary_key_codec(&metadata);
2265 let schema = to_flat_sst_arrow_schema(
2266 &metadata,
2267 &FlatSchemaOptions::from_encoding(metadata.primary_key_encoding),
2268 );
2269
2270 let mut converter =
2271 BulkPartConverter::new(&metadata, schema, capacity, primary_key_codec, true);
2272
2273 let key_values1 = build_key_values_with_ts_seq_values(
2274 &metadata,
2275 "key1".to_string(),
2276 1u32,
2277 vec![1000, 2000].into_iter(),
2278 vec![Some(1.0), Some(2.0)].into_iter(),
2279 1,
2280 );
2281
2282 let key_values2 = build_key_values_with_ts_seq_values(
2283 &metadata,
2284 "key2".to_string(),
2285 2u32,
2286 vec![1500].into_iter(),
2287 vec![Some(3.0)].into_iter(),
2288 2,
2289 );
2290
2291 converter.append_key_values(&key_values1).unwrap();
2292 converter.append_key_values(&key_values2).unwrap();
2293
2294 let bulk_part = converter.convert().unwrap();
2295
2296 assert_eq!(bulk_part.num_rows(), 3);
2297 assert_eq!(bulk_part.min_timestamp, 1000);
2298 assert_eq!(bulk_part.max_timestamp, 2000);
2299 assert_eq!(bulk_part.sequence, 2);
2300 assert_eq!(bulk_part.timestamp_index, bulk_part.batch.num_columns() - 4);
2301
2302 let schema = bulk_part.batch.schema();
2305 let field_names: Vec<&str> = schema.fields().iter().map(|f| f.name().as_str()).collect();
2306 assert_eq!(
2307 field_names,
2308 vec![
2309 "k0",
2310 "k1",
2311 "v0",
2312 "v1",
2313 "ts",
2314 "__primary_key",
2315 "__sequence",
2316 "__op_type"
2317 ]
2318 );
2319 }
2320
2321 #[test]
2322 fn test_bulk_part_converter_sorting() {
2323 let metadata = metadata_for_test();
2324 let capacity = 100;
2325 let primary_key_codec = build_primary_key_codec(&metadata);
2326 let schema = to_flat_sst_arrow_schema(
2327 &metadata,
2328 &FlatSchemaOptions::from_encoding(metadata.primary_key_encoding),
2329 );
2330
2331 let mut converter =
2332 BulkPartConverter::new(&metadata, schema, capacity, primary_key_codec, true);
2333
2334 let key_values1 = build_key_values_with_ts_seq_values(
2335 &metadata,
2336 "z_key".to_string(),
2337 3u32,
2338 vec![3000].into_iter(),
2339 vec![Some(3.0)].into_iter(),
2340 3,
2341 );
2342
2343 let key_values2 = build_key_values_with_ts_seq_values(
2344 &metadata,
2345 "a_key".to_string(),
2346 1u32,
2347 vec![1000].into_iter(),
2348 vec![Some(1.0)].into_iter(),
2349 1,
2350 );
2351
2352 let key_values3 = build_key_values_with_ts_seq_values(
2353 &metadata,
2354 "m_key".to_string(),
2355 2u32,
2356 vec![2000].into_iter(),
2357 vec![Some(2.0)].into_iter(),
2358 2,
2359 );
2360
2361 converter.append_key_values(&key_values1).unwrap();
2362 converter.append_key_values(&key_values2).unwrap();
2363 converter.append_key_values(&key_values3).unwrap();
2364
2365 let bulk_part = converter.convert().unwrap();
2366
2367 assert_eq!(bulk_part.num_rows(), 3);
2368
2369 let ts_column = bulk_part.batch.column(bulk_part.timestamp_index);
2370 let seq_column = bulk_part.batch.column(bulk_part.batch.num_columns() - 2);
2371
2372 let ts_array = ts_column
2373 .as_any()
2374 .downcast_ref::<TimestampMillisecondArray>()
2375 .unwrap();
2376 let seq_array = seq_column.as_any().downcast_ref::<UInt64Array>().unwrap();
2377
2378 assert_eq!(ts_array.values(), &[1000, 2000, 3000]);
2379 assert_eq!(seq_array.values(), &[1, 2, 3]);
2380
2381 let schema = bulk_part.batch.schema();
2383 let field_names: Vec<&str> = schema.fields().iter().map(|f| f.name().as_str()).collect();
2384 assert_eq!(
2385 field_names,
2386 vec![
2387 "k0",
2388 "k1",
2389 "v0",
2390 "v1",
2391 "ts",
2392 "__primary_key",
2393 "__sequence",
2394 "__op_type"
2395 ]
2396 );
2397 }
2398
2399 #[test]
2400 fn test_bulk_part_converter_empty() {
2401 let metadata = metadata_for_test();
2402 let capacity = 10;
2403 let primary_key_codec = build_primary_key_codec(&metadata);
2404 let schema = to_flat_sst_arrow_schema(
2405 &metadata,
2406 &FlatSchemaOptions::from_encoding(metadata.primary_key_encoding),
2407 );
2408
2409 let converter =
2410 BulkPartConverter::new(&metadata, schema, capacity, primary_key_codec, true);
2411
2412 let bulk_part = converter.convert().unwrap();
2413
2414 assert_eq!(bulk_part.num_rows(), 0);
2415 assert_eq!(bulk_part.min_timestamp, i64::MAX);
2416 assert_eq!(bulk_part.max_timestamp, i64::MIN);
2417 assert_eq!(bulk_part.sequence, SequenceNumber::MIN);
2418
2419 let schema = bulk_part.batch.schema();
2421 let field_names: Vec<&str> = schema.fields().iter().map(|f| f.name().as_str()).collect();
2422 assert_eq!(
2423 field_names,
2424 vec![
2425 "k0",
2426 "k1",
2427 "v0",
2428 "v1",
2429 "ts",
2430 "__primary_key",
2431 "__sequence",
2432 "__op_type"
2433 ]
2434 );
2435 }
2436
2437 #[test]
2438 fn test_bulk_part_converter_without_primary_key_columns() {
2439 let metadata = metadata_for_test();
2440 let primary_key_codec = build_primary_key_codec(&metadata);
2441 let schema = to_flat_sst_arrow_schema(
2442 &metadata,
2443 &FlatSchemaOptions {
2444 raw_pk_columns: false,
2445 string_pk_use_dict: true,
2446 ..Default::default()
2447 },
2448 );
2449
2450 let capacity = 100;
2451 let mut converter =
2452 BulkPartConverter::new(&metadata, schema, capacity, primary_key_codec, false);
2453
2454 let key_values1 = build_key_values_with_ts_seq_values(
2455 &metadata,
2456 "key1".to_string(),
2457 1u32,
2458 vec![1000, 2000].into_iter(),
2459 vec![Some(1.0), Some(2.0)].into_iter(),
2460 1,
2461 );
2462
2463 let key_values2 = build_key_values_with_ts_seq_values(
2464 &metadata,
2465 "key2".to_string(),
2466 2u32,
2467 vec![1500].into_iter(),
2468 vec![Some(3.0)].into_iter(),
2469 2,
2470 );
2471
2472 converter.append_key_values(&key_values1).unwrap();
2473 converter.append_key_values(&key_values2).unwrap();
2474
2475 let bulk_part = converter.convert().unwrap();
2476
2477 assert_eq!(bulk_part.num_rows(), 3);
2478 assert_eq!(bulk_part.min_timestamp, 1000);
2479 assert_eq!(bulk_part.max_timestamp, 2000);
2480 assert_eq!(bulk_part.sequence, 2);
2481 assert_eq!(bulk_part.timestamp_index, bulk_part.batch.num_columns() - 4);
2482
2483 let schema = bulk_part.batch.schema();
2485 let field_names: Vec<&str> = schema.fields().iter().map(|f| f.name().as_str()).collect();
2486 assert_eq!(
2487 field_names,
2488 vec!["v0", "v1", "ts", "__primary_key", "__sequence", "__op_type"]
2489 );
2490 }
2491
2492 #[allow(clippy::too_many_arguments)]
2493 fn build_key_values_with_sparse_encoding(
2494 metadata: &RegionMetadataRef,
2495 primary_key_codec: &Arc<dyn PrimaryKeyCodec>,
2496 table_id: u32,
2497 tsid: u64,
2498 k0: String,
2499 k1: String,
2500 timestamps: impl Iterator<Item = i64>,
2501 values: impl Iterator<Item = Option<f64>>,
2502 sequence: SequenceNumber,
2503 ) -> KeyValues {
2504 let pk_values = vec![
2506 (ReservedColumnId::table_id(), Value::UInt32(table_id)),
2507 (ReservedColumnId::tsid(), Value::UInt64(tsid)),
2508 (0, Value::String(k0.clone().into())),
2509 (1, Value::String(k1.clone().into())),
2510 ];
2511 let mut encoded_key = Vec::new();
2512 primary_key_codec
2513 .encode_values(&pk_values, &mut encoded_key)
2514 .unwrap();
2515 assert!(!encoded_key.is_empty());
2516
2517 let column_schema = vec![
2519 api::v1::ColumnSchema {
2520 column_name: PRIMARY_KEY_COLUMN_NAME.to_string(),
2521 datatype: api::helper::ColumnDataTypeWrapper::try_from(
2522 ConcreteDataType::binary_datatype(),
2523 )
2524 .unwrap()
2525 .datatype() as i32,
2526 semantic_type: api::v1::SemanticType::Tag as i32,
2527 ..Default::default()
2528 },
2529 api::v1::ColumnSchema {
2530 column_name: "ts".to_string(),
2531 datatype: api::helper::ColumnDataTypeWrapper::try_from(
2532 ConcreteDataType::timestamp_millisecond_datatype(),
2533 )
2534 .unwrap()
2535 .datatype() as i32,
2536 semantic_type: api::v1::SemanticType::Timestamp as i32,
2537 ..Default::default()
2538 },
2539 api::v1::ColumnSchema {
2540 column_name: "v0".to_string(),
2541 datatype: api::helper::ColumnDataTypeWrapper::try_from(
2542 ConcreteDataType::int64_datatype(),
2543 )
2544 .unwrap()
2545 .datatype() as i32,
2546 semantic_type: api::v1::SemanticType::Field as i32,
2547 ..Default::default()
2548 },
2549 api::v1::ColumnSchema {
2550 column_name: "v1".to_string(),
2551 datatype: api::helper::ColumnDataTypeWrapper::try_from(
2552 ConcreteDataType::float64_datatype(),
2553 )
2554 .unwrap()
2555 .datatype() as i32,
2556 semantic_type: api::v1::SemanticType::Field as i32,
2557 ..Default::default()
2558 },
2559 ];
2560
2561 let rows = timestamps
2562 .zip(values)
2563 .map(|(ts, v)| Row {
2564 values: vec![
2565 api::v1::Value {
2566 value_data: Some(api::v1::value::ValueData::BinaryValue(
2567 encoded_key.clone(),
2568 )),
2569 },
2570 api::v1::Value {
2571 value_data: Some(api::v1::value::ValueData::TimestampMillisecondValue(ts)),
2572 },
2573 api::v1::Value {
2574 value_data: Some(api::v1::value::ValueData::I64Value(ts)),
2575 },
2576 api::v1::Value {
2577 value_data: v.map(api::v1::value::ValueData::F64Value),
2578 },
2579 ],
2580 })
2581 .collect();
2582
2583 let mutation = api::v1::Mutation {
2584 op_type: 1,
2585 sequence,
2586 rows: Some(api::v1::Rows {
2587 schema: column_schema,
2588 rows,
2589 }),
2590 write_hint: Some(WriteHint {
2591 primary_key_encoding: api::v1::PrimaryKeyEncoding::Sparse.into(),
2592 }),
2593 };
2594 KeyValues::new(metadata.as_ref(), mutation).unwrap()
2595 }
2596
2597 #[test]
2598 fn test_bulk_part_converter_sparse_primary_key_encoding() {
2599 use api::v1::SemanticType;
2600 use datatypes::schema::ColumnSchema;
2601 use store_api::metadata::{ColumnMetadata, RegionMetadataBuilder};
2602 use store_api::storage::RegionId;
2603
2604 let mut builder = RegionMetadataBuilder::new(RegionId::new(123, 456));
2605 builder
2606 .push_column_metadata(ColumnMetadata {
2607 column_schema: ColumnSchema::new("k0", ConcreteDataType::string_datatype(), false),
2608 semantic_type: SemanticType::Tag,
2609 column_id: 0,
2610 })
2611 .push_column_metadata(ColumnMetadata {
2612 column_schema: ColumnSchema::new("k1", ConcreteDataType::string_datatype(), false),
2613 semantic_type: SemanticType::Tag,
2614 column_id: 1,
2615 })
2616 .push_column_metadata(ColumnMetadata {
2617 column_schema: ColumnSchema::new(
2618 "ts",
2619 ConcreteDataType::timestamp_millisecond_datatype(),
2620 false,
2621 ),
2622 semantic_type: SemanticType::Timestamp,
2623 column_id: 2,
2624 })
2625 .push_column_metadata(ColumnMetadata {
2626 column_schema: ColumnSchema::new("v0", ConcreteDataType::int64_datatype(), true),
2627 semantic_type: SemanticType::Field,
2628 column_id: 3,
2629 })
2630 .push_column_metadata(ColumnMetadata {
2631 column_schema: ColumnSchema::new("v1", ConcreteDataType::float64_datatype(), true),
2632 semantic_type: SemanticType::Field,
2633 column_id: 4,
2634 })
2635 .primary_key(vec![0, 1])
2636 .primary_key_encoding(PrimaryKeyEncoding::Sparse);
2637 let metadata = Arc::new(builder.build().unwrap());
2638
2639 let primary_key_codec = build_primary_key_codec(&metadata);
2640 let schema = to_flat_sst_arrow_schema(
2641 &metadata,
2642 &FlatSchemaOptions::from_encoding(metadata.primary_key_encoding),
2643 );
2644
2645 assert_eq!(metadata.primary_key_encoding, PrimaryKeyEncoding::Sparse);
2646 assert_eq!(primary_key_codec.encoding(), PrimaryKeyEncoding::Sparse);
2647
2648 let capacity = 100;
2649 let mut converter =
2650 BulkPartConverter::new(&metadata, schema, capacity, primary_key_codec.clone(), true);
2651
2652 let key_values1 = build_key_values_with_sparse_encoding(
2653 &metadata,
2654 &primary_key_codec,
2655 2048u32, 100u64, "key11".to_string(),
2658 "key21".to_string(),
2659 vec![1000, 2000].into_iter(),
2660 vec![Some(1.0), Some(2.0)].into_iter(),
2661 1,
2662 );
2663
2664 let key_values2 = build_key_values_with_sparse_encoding(
2665 &metadata,
2666 &primary_key_codec,
2667 4096u32, 200u64, "key12".to_string(),
2670 "key22".to_string(),
2671 vec![1500].into_iter(),
2672 vec![Some(3.0)].into_iter(),
2673 2,
2674 );
2675
2676 converter.append_key_values(&key_values1).unwrap();
2677 converter.append_key_values(&key_values2).unwrap();
2678
2679 let bulk_part = converter.convert().unwrap();
2680
2681 assert_eq!(bulk_part.num_rows(), 3);
2682 assert_eq!(bulk_part.min_timestamp, 1000);
2683 assert_eq!(bulk_part.max_timestamp, 2000);
2684 assert_eq!(bulk_part.sequence, 2);
2685 assert_eq!(bulk_part.timestamp_index, bulk_part.batch.num_columns() - 4);
2686
2687 let schema = bulk_part.batch.schema();
2691 let field_names: Vec<&str> = schema.fields().iter().map(|f| f.name().as_str()).collect();
2692 assert_eq!(
2693 field_names,
2694 vec!["v0", "v1", "ts", "__primary_key", "__sequence", "__op_type"]
2695 );
2696
2697 let primary_key_column = bulk_part.batch.column_by_name("__primary_key").unwrap();
2699 let dict_array = primary_key_column
2700 .as_any()
2701 .downcast_ref::<DictionaryArray<UInt32Type>>()
2702 .unwrap();
2703
2704 assert!(!dict_array.is_empty());
2706 assert_eq!(dict_array.len(), 3); let values = dict_array
2710 .values()
2711 .as_any()
2712 .downcast_ref::<BinaryArray>()
2713 .unwrap();
2714 for i in 0..values.len() {
2715 assert!(
2716 !values.value(i).is_empty(),
2717 "Encoded primary key should not be empty"
2718 );
2719 }
2720 }
2721
2722 #[test]
2723 fn test_convert_bulk_part_empty() {
2724 let metadata = metadata_for_test();
2725 let schema = to_flat_sst_arrow_schema(
2726 &metadata,
2727 &FlatSchemaOptions::from_encoding(metadata.primary_key_encoding),
2728 );
2729 let primary_key_codec = build_primary_key_codec(&metadata);
2730
2731 let empty_batch = RecordBatch::new_empty(schema.clone());
2733 let empty_part = BulkPart {
2734 batch: empty_batch,
2735 max_timestamp: 0,
2736 min_timestamp: 0,
2737 sequence: 0,
2738 min_sequence: 0,
2739 timestamp_index: 0,
2740 raw_data: None,
2741 };
2742
2743 let result =
2744 convert_bulk_part(empty_part, &metadata, primary_key_codec, schema, true).unwrap();
2745 assert!(result.is_none());
2746 }
2747
2748 #[test]
2749 fn test_convert_bulk_part_dense_with_pk_columns() {
2750 let metadata = metadata_for_test();
2751 let primary_key_codec = build_primary_key_codec(&metadata);
2752
2753 let k0_array = Arc::new(arrow::array::StringArray::from(vec![
2754 "key1", "key2", "key1",
2755 ]));
2756 let k1_array = Arc::new(arrow::array::UInt32Array::from(vec![1, 2, 1]));
2757 let v0_array = Arc::new(arrow::array::Int64Array::from(vec![100, 200, 300]));
2758 let v1_array = Arc::new(arrow::array::Float64Array::from(vec![1.0, 2.0, 3.0]));
2759 let ts_array = Arc::new(TimestampMillisecondArray::from(vec![1000, 2000, 1500]));
2760
2761 let input_schema = Arc::new(Schema::new(vec![
2762 Field::new("k0", ArrowDataType::Utf8, false),
2763 Field::new("k1", ArrowDataType::UInt32, false),
2764 Field::new("v0", ArrowDataType::Int64, true),
2765 Field::new("v1", ArrowDataType::Float64, true),
2766 Field::new(
2767 "ts",
2768 ArrowDataType::Timestamp(arrow::datatypes::TimeUnit::Millisecond, None),
2769 false,
2770 ),
2771 ]));
2772
2773 let input_batch = RecordBatch::try_new(
2774 input_schema,
2775 vec![k0_array, k1_array, v0_array, v1_array, ts_array],
2776 )
2777 .unwrap();
2778
2779 let part = BulkPart {
2780 batch: input_batch,
2781 max_timestamp: 2000,
2782 min_timestamp: 1000,
2783 sequence: 5,
2784 min_sequence: 5,
2785 timestamp_index: 4,
2786 raw_data: None,
2787 };
2788
2789 let output_schema = to_flat_sst_arrow_schema(
2790 &metadata,
2791 &FlatSchemaOptions::from_encoding(metadata.primary_key_encoding),
2792 );
2793
2794 let result = convert_bulk_part(
2795 part,
2796 &metadata,
2797 primary_key_codec,
2798 output_schema,
2799 true, )
2801 .unwrap();
2802
2803 let converted = result.unwrap();
2804
2805 assert_eq!(converted.num_rows(), 3);
2806 assert_eq!(converted.max_timestamp, 2000);
2807 assert_eq!(converted.min_timestamp, 1000);
2808 assert_eq!(converted.sequence, 5);
2809 assert_eq!(converted.min_sequence, 5);
2810
2811 let schema = converted.batch.schema();
2812 let field_names: Vec<&str> = schema.fields().iter().map(|f| f.name().as_str()).collect();
2813 assert_eq!(
2814 field_names,
2815 vec![
2816 "k0",
2817 "k1",
2818 "v0",
2819 "v1",
2820 "ts",
2821 "__primary_key",
2822 "__sequence",
2823 "__op_type"
2824 ]
2825 );
2826
2827 let k0_col = converted.batch.column_by_name("k0").unwrap();
2828 assert!(matches!(
2829 k0_col.data_type(),
2830 ArrowDataType::Dictionary(_, _)
2831 ));
2832
2833 let pk_col = converted.batch.column_by_name("__primary_key").unwrap();
2834 let dict_array = pk_col
2835 .as_any()
2836 .downcast_ref::<DictionaryArray<UInt32Type>>()
2837 .unwrap();
2838 let keys = dict_array.keys();
2839
2840 assert_eq!(keys.len(), 3);
2841 }
2842
2843 #[test]
2844 fn test_convert_bulk_part_dense_without_pk_columns() {
2845 let metadata = metadata_for_test();
2846 let primary_key_codec = build_primary_key_codec(&metadata);
2847
2848 let k0_array = Arc::new(arrow::array::StringArray::from(vec!["key1", "key2"]));
2850 let k1_array = Arc::new(arrow::array::UInt32Array::from(vec![1, 2]));
2851 let v0_array = Arc::new(arrow::array::Int64Array::from(vec![100, 200]));
2852 let v1_array = Arc::new(arrow::array::Float64Array::from(vec![1.0, 2.0]));
2853 let ts_array = Arc::new(TimestampMillisecondArray::from(vec![1000, 2000]));
2854
2855 let input_schema = Arc::new(Schema::new(vec![
2856 Field::new("k0", ArrowDataType::Utf8, false),
2857 Field::new("k1", ArrowDataType::UInt32, false),
2858 Field::new("v0", ArrowDataType::Int64, true),
2859 Field::new("v1", ArrowDataType::Float64, true),
2860 Field::new(
2861 "ts",
2862 ArrowDataType::Timestamp(arrow::datatypes::TimeUnit::Millisecond, None),
2863 false,
2864 ),
2865 ]));
2866
2867 let input_batch = RecordBatch::try_new(
2868 input_schema,
2869 vec![k0_array, k1_array, v0_array, v1_array, ts_array],
2870 )
2871 .unwrap();
2872
2873 let part = BulkPart {
2874 batch: input_batch,
2875 max_timestamp: 2000,
2876 min_timestamp: 1000,
2877 sequence: 3,
2878 min_sequence: 3,
2879 timestamp_index: 4,
2880 raw_data: None,
2881 };
2882
2883 let output_schema = to_flat_sst_arrow_schema(
2884 &metadata,
2885 &FlatSchemaOptions {
2886 raw_pk_columns: false,
2887 string_pk_use_dict: true,
2888 ..Default::default()
2889 },
2890 );
2891
2892 let result = convert_bulk_part(
2893 part,
2894 &metadata,
2895 primary_key_codec,
2896 output_schema,
2897 false, )
2899 .unwrap();
2900
2901 let converted = result.unwrap();
2902
2903 assert_eq!(converted.num_rows(), 2);
2904 assert_eq!(converted.max_timestamp, 2000);
2905 assert_eq!(converted.min_timestamp, 1000);
2906 assert_eq!(converted.sequence, 3);
2907
2908 let schema = converted.batch.schema();
2910 let field_names: Vec<&str> = schema.fields().iter().map(|f| f.name().as_str()).collect();
2911 assert_eq!(
2912 field_names,
2913 vec!["v0", "v1", "ts", "__primary_key", "__sequence", "__op_type"]
2914 );
2915
2916 let pk_col = converted.batch.column_by_name("__primary_key").unwrap();
2918 assert!(matches!(
2919 pk_col.data_type(),
2920 ArrowDataType::Dictionary(_, _)
2921 ));
2922 }
2923
2924 #[test]
2925 fn test_convert_bulk_part_sparse_encoding() {
2926 let mut builder = RegionMetadataBuilder::new(RegionId::new(123, 456));
2927 builder
2928 .push_column_metadata(ColumnMetadata {
2929 column_schema: ColumnSchema::new("k0", ConcreteDataType::string_datatype(), false),
2930 semantic_type: SemanticType::Tag,
2931 column_id: 0,
2932 })
2933 .push_column_metadata(ColumnMetadata {
2934 column_schema: ColumnSchema::new("k1", ConcreteDataType::string_datatype(), false),
2935 semantic_type: SemanticType::Tag,
2936 column_id: 1,
2937 })
2938 .push_column_metadata(ColumnMetadata {
2939 column_schema: ColumnSchema::new(
2940 "ts",
2941 ConcreteDataType::timestamp_millisecond_datatype(),
2942 false,
2943 ),
2944 semantic_type: SemanticType::Timestamp,
2945 column_id: 2,
2946 })
2947 .push_column_metadata(ColumnMetadata {
2948 column_schema: ColumnSchema::new("v0", ConcreteDataType::int64_datatype(), true),
2949 semantic_type: SemanticType::Field,
2950 column_id: 3,
2951 })
2952 .push_column_metadata(ColumnMetadata {
2953 column_schema: ColumnSchema::new("v1", ConcreteDataType::float64_datatype(), true),
2954 semantic_type: SemanticType::Field,
2955 column_id: 4,
2956 })
2957 .primary_key(vec![0, 1])
2958 .primary_key_encoding(PrimaryKeyEncoding::Sparse);
2959 let metadata = Arc::new(builder.build().unwrap());
2960
2961 let primary_key_codec = build_primary_key_codec(&metadata);
2962
2963 let pk_array = Arc::new(arrow::array::BinaryArray::from(vec![
2965 b"encoded_key_1".as_slice(),
2966 b"encoded_key_2".as_slice(),
2967 ]));
2968 let v0_array = Arc::new(arrow::array::Int64Array::from(vec![100, 200]));
2969 let v1_array = Arc::new(arrow::array::Float64Array::from(vec![1.0, 2.0]));
2970 let ts_array = Arc::new(TimestampMillisecondArray::from(vec![1000, 2000]));
2971
2972 let input_schema = Arc::new(Schema::new(vec![
2973 Field::new("__primary_key", ArrowDataType::Binary, false),
2974 Field::new("v0", ArrowDataType::Int64, true),
2975 Field::new("v1", ArrowDataType::Float64, true),
2976 Field::new(
2977 "ts",
2978 ArrowDataType::Timestamp(arrow::datatypes::TimeUnit::Millisecond, None),
2979 false,
2980 ),
2981 ]));
2982
2983 let input_batch =
2984 RecordBatch::try_new(input_schema, vec![pk_array, v0_array, v1_array, ts_array])
2985 .unwrap();
2986
2987 let part = BulkPart {
2988 batch: input_batch,
2989 max_timestamp: 2000,
2990 min_timestamp: 1000,
2991 sequence: 7,
2992 min_sequence: 7,
2993 timestamp_index: 3,
2994 raw_data: None,
2995 };
2996
2997 let output_schema = to_flat_sst_arrow_schema(
2998 &metadata,
2999 &FlatSchemaOptions::from_encoding(metadata.primary_key_encoding),
3000 );
3001
3002 let result = convert_bulk_part(
3003 part,
3004 &metadata,
3005 primary_key_codec,
3006 output_schema,
3007 true, )
3009 .unwrap();
3010
3011 let converted = result.unwrap();
3012
3013 assert_eq!(converted.num_rows(), 2);
3014 assert_eq!(converted.max_timestamp, 2000);
3015 assert_eq!(converted.min_timestamp, 1000);
3016 assert_eq!(converted.sequence, 7);
3017
3018 let schema = converted.batch.schema();
3020 let field_names: Vec<&str> = schema.fields().iter().map(|f| f.name().as_str()).collect();
3021 assert_eq!(
3022 field_names,
3023 vec!["v0", "v1", "ts", "__primary_key", "__sequence", "__op_type"]
3024 );
3025
3026 let pk_col = converted.batch.column_by_name("__primary_key").unwrap();
3028 assert!(matches!(
3029 pk_col.data_type(),
3030 ArrowDataType::Dictionary(_, _)
3031 ));
3032 }
3033
3034 #[test]
3035 fn test_convert_bulk_part_sorting_with_multiple_series() {
3036 let metadata = metadata_for_test();
3037 let primary_key_codec = build_primary_key_codec(&metadata);
3038
3039 let k0_array = Arc::new(arrow::array::StringArray::from(vec![
3041 "series_b", "series_a", "series_b", "series_a",
3042 ]));
3043 let k1_array = Arc::new(arrow::array::UInt32Array::from(vec![2, 1, 2, 1]));
3044 let v0_array = Arc::new(arrow::array::Int64Array::from(vec![200, 100, 400, 300]));
3045 let v1_array = Arc::new(arrow::array::Float64Array::from(vec![2.0, 1.0, 4.0, 3.0]));
3046 let ts_array = Arc::new(TimestampMillisecondArray::from(vec![
3047 2000, 1000, 4000, 3000,
3048 ]));
3049
3050 let input_schema = Arc::new(Schema::new(vec![
3051 Field::new("k0", ArrowDataType::Utf8, false),
3052 Field::new("k1", ArrowDataType::UInt32, false),
3053 Field::new("v0", ArrowDataType::Int64, true),
3054 Field::new("v1", ArrowDataType::Float64, true),
3055 Field::new(
3056 "ts",
3057 ArrowDataType::Timestamp(arrow::datatypes::TimeUnit::Millisecond, None),
3058 false,
3059 ),
3060 ]));
3061
3062 let input_batch = RecordBatch::try_new(
3063 input_schema,
3064 vec![k0_array, k1_array, v0_array, v1_array, ts_array],
3065 )
3066 .unwrap();
3067
3068 let part = BulkPart {
3069 batch: input_batch,
3070 max_timestamp: 4000,
3071 min_timestamp: 1000,
3072 sequence: 10,
3073 min_sequence: 10,
3074 timestamp_index: 4,
3075 raw_data: None,
3076 };
3077
3078 let output_schema = to_flat_sst_arrow_schema(
3079 &metadata,
3080 &FlatSchemaOptions::from_encoding(metadata.primary_key_encoding),
3081 );
3082
3083 let result =
3084 convert_bulk_part(part, &metadata, primary_key_codec, output_schema, true).unwrap();
3085
3086 let converted = result.unwrap();
3087
3088 assert_eq!(converted.num_rows(), 4);
3089
3090 let ts_col = converted.batch.column(converted.timestamp_index);
3092 let ts_array = ts_col
3093 .as_any()
3094 .downcast_ref::<TimestampMillisecondArray>()
3095 .unwrap();
3096
3097 let timestamps: Vec<i64> = ts_array.values().to_vec();
3101 assert_eq!(timestamps, vec![1000, 3000, 2000, 4000]);
3102 }
3103
3104 fn build_converted_bulk_part(inputs: &[MutationInput]) -> BulkPart {
3106 let metadata = metadata_for_test();
3107 let kvs = inputs
3108 .iter()
3109 .map(|m| {
3110 build_key_values_with_ts_seq_values(
3111 &metadata,
3112 m.k0.to_string(),
3113 m.k1,
3114 m.timestamps.iter().copied(),
3115 m.v1.iter().copied(),
3116 m.sequence,
3117 )
3118 })
3119 .collect::<Vec<_>>();
3120 let schema = to_flat_sst_arrow_schema(&metadata, &FlatSchemaOptions::default());
3121 let primary_key_codec = build_primary_key_codec(&metadata);
3122 let mut converter = BulkPartConverter::new(&metadata, schema, 64, primary_key_codec, true);
3123 for kv in kvs {
3124 converter.append_key_values(&kv).unwrap();
3125 }
3126 converter.convert().unwrap()
3127 }
3128
3129 #[test]
3130 fn test_min_sequence_survives_slicing_and_unordered_part_reuse() {
3131 let metadata = metadata_for_test();
3132 let make_part = |sequence| {
3133 build_converted_bulk_part(&[MutationInput {
3134 k0: "a",
3135 k1: 0,
3136 timestamps: &[2000, 1000],
3137 v1: &[Some(1.0), Some(2.0)],
3138 sequence,
3139 }])
3140 };
3141 let original = make_part(100);
3142 let sliced = crate::memtable::time_partition::filter_record_batch(&original, 1000, 1001)
3143 .unwrap()
3144 .unwrap();
3145 assert_eq!(1, sliced.num_rows());
3148 assert_eq!(100, sliced.min_sequence);
3149 assert_eq!(101, sliced.sequence);
3150
3151 let mut unordered = UnorderedPart::new();
3152 unordered.push(sliced.clone());
3153 unordered.push(make_part(10));
3154 let merged = unordered.to_bulk_part(&metadata).unwrap().unwrap();
3155 assert_eq!(3, merged.num_rows());
3156 assert_eq!(10, merged.min_sequence);
3157 assert_eq!(101, merged.sequence);
3158 assert_eq!(10, merged.to_memtable_stats(&metadata).min_sequence);
3159
3160 unordered.clear();
3161 unordered.push(sliced);
3162 let reused = unordered.to_bulk_part(&metadata).unwrap().unwrap();
3163 assert_eq!(1, reused.num_rows());
3164 assert_eq!(100, reused.min_sequence);
3165 }
3166
3167 fn build_multi_bulk_part(groups: &[&[MutationInput]]) -> (MultiBulkPart, RegionMetadataRef) {
3169 let metadata = metadata_for_test();
3170 let mut all_batches = Vec::new();
3171 let mut min_ts = i64::MAX;
3172 let mut max_ts = i64::MIN;
3173 let mut min_seq = u64::MAX;
3174 let mut max_seq = 0u64;
3175
3176 for inputs in groups {
3177 let part = build_converted_bulk_part(inputs);
3178 min_ts = min_ts.min(part.min_timestamp);
3179 max_ts = max_ts.max(part.max_timestamp);
3180 min_seq = min_seq.min(part.min_sequence);
3181 max_seq = max_seq.max(part.sequence);
3182 all_batches.push(part.batch);
3183 }
3184
3185 let multi = MultiBulkPart::new(
3186 all_batches,
3187 min_ts,
3188 max_ts,
3189 min_seq,
3190 max_seq,
3191 groups.len(),
3192 &metadata,
3193 );
3194 (multi, metadata)
3195 }
3196
3197 #[test]
3198 fn test_multi_bulk_part_prune_batches() {
3199 let (multi, metadata) = build_multi_bulk_part(&[
3201 &[MutationInput {
3202 k0: "a",
3203 k1: 0,
3204 timestamps: &[1, 2],
3205 v1: &[Some(1.0), Some(2.0)],
3206 sequence: 0,
3207 }],
3208 &[MutationInput {
3209 k0: "m",
3210 k1: 0,
3211 timestamps: &[3, 4],
3212 v1: &[Some(3.0), Some(4.0)],
3213 sequence: 1,
3214 }],
3215 &[MutationInput {
3216 k0: "z",
3217 k1: 0,
3218 timestamps: &[5, 6],
3219 v1: &[Some(5.0), Some(6.0)],
3220 sequence: 2,
3221 }],
3222 ]);
3223 assert_eq!(multi.num_rows(), 6);
3224 assert_eq!(multi.num_batches(), 3);
3225
3226 let context = Arc::new(
3228 BulkIterContext::new(
3229 metadata.clone(),
3230 None,
3231 Some(Predicate::new(vec![
3232 datafusion_expr::col("k0").eq(datafusion_expr::lit("m")),
3233 ])),
3234 false,
3235 crate::sst::parquet::DEFAULT_READ_BATCH_SIZE,
3236 )
3237 .unwrap(),
3238 );
3239 let reader = multi
3240 .read(context, None, None)
3241 .unwrap()
3242 .expect("should have results");
3243 let total_rows: usize = reader.map(|r| r.unwrap().num_rows()).sum();
3244 assert_eq!(total_rows, 2);
3245
3246 let context = Arc::new(
3248 BulkIterContext::new(
3249 metadata.clone(),
3250 None,
3251 Some(Predicate::new(vec![
3252 datafusion_expr::col("k0").eq(datafusion_expr::lit("nonexistent")),
3253 ])),
3254 false,
3255 crate::sst::parquet::DEFAULT_READ_BATCH_SIZE,
3256 )
3257 .unwrap(),
3258 );
3259 assert!(multi.read(context, None, None).unwrap().is_none());
3260
3261 let context = Arc::new(
3263 BulkIterContext::new(
3264 metadata.clone(),
3265 None,
3266 None,
3267 false,
3268 crate::sst::parquet::DEFAULT_READ_BATCH_SIZE,
3269 )
3270 .unwrap(),
3271 );
3272 let reader = multi
3273 .read(context, None, None)
3274 .unwrap()
3275 .expect("should have results");
3276 let total_rows: usize = reader.map(|r| r.unwrap().num_rows()).sum();
3277 assert_eq!(total_rows, 6);
3278 }
3279
3280 #[test]
3281 fn test_fill_missing_columns_resets_raw_data() {
3282 let metadata = metadata_for_test();
3283
3284 let k0_array = Arc::new(arrow::array::StringArray::from(vec!["key1", "key2"]));
3287 let k1_array = Arc::new(arrow::array::UInt32Array::from(vec![1u32, 2]));
3288 let v0_array = Arc::new(arrow::array::Int64Array::from(vec![100i64, 200]));
3289 let ts_array = Arc::new(TimestampMillisecondArray::from(vec![1000i64, 2000]));
3290 let input_schema = Arc::new(Schema::new(vec![
3291 Field::new("k0", ArrowDataType::Utf8, false),
3292 Field::new("k1", ArrowDataType::UInt32, false),
3293 Field::new("v0", ArrowDataType::Int64, true),
3294 Field::new(
3295 "ts",
3296 ArrowDataType::Timestamp(arrow::datatypes::TimeUnit::Millisecond, None),
3297 false,
3298 ),
3299 ]));
3300 let batch =
3301 RecordBatch::try_new(input_schema, vec![k0_array, k1_array, v0_array, ts_array])
3302 .unwrap();
3303
3304 let mut encoder = FlightEncoder::default();
3306 let schema_bytes = encoder.encode_schema(batch.schema().as_ref()).data_header;
3307 let [flight_data] = encoder
3308 .encode(FlightMessage::RecordBatch(batch.clone()))
3309 .try_into()
3310 .unwrap();
3311 let mut part = BulkPart {
3312 batch,
3313 max_timestamp: 2000,
3314 min_timestamp: 1000,
3315 sequence: 5,
3316 min_sequence: 5,
3317 timestamp_index: 3,
3318 raw_data: Some(ArrowIpc {
3319 schema: schema_bytes,
3320 data_header: flight_data.data_header,
3321 payload: flight_data.data_body,
3322 }),
3323 };
3324
3325 part.fill_missing_columns(&metadata).unwrap();
3326 assert!(part.batch.column_by_name("v1").is_some());
3327 assert!(part.raw_data.is_none());
3331
3332 let entry = BulkWalEntry::try_from(&part).unwrap();
3334 let replayed = BulkPart::try_from(entry).unwrap();
3335 assert_eq!(part.sequence, replayed.min_sequence);
3336 assert_eq!(2, replayed.num_rows());
3337 assert!(replayed.batch.column_by_name("v1").is_some());
3338 }
3339}