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::sparse::SparsePrimaryKeyValidator;
49use mito_codec::row_converter::{PrimaryKeyCodec, SortField, build_primary_key_codec_with_fields};
50use parquet::arrow::ArrowWriter;
51use parquet::basic::{Compression, ZstdLevel};
52use parquet::file::metadata::ParquetMetaData;
53use parquet::file::properties::WriterProperties;
54use smallvec::SmallVec;
55use snafu::{OptionExt, ResultExt, ensure};
56use store_api::codec::PrimaryKeyEncoding;
57use store_api::metadata::{RegionMetadata, RegionMetadataRef};
58use store_api::mito_engine_options::FloatFieldEncoding;
59use store_api::storage::consts::PRIMARY_KEY_COLUMN_NAME;
60use store_api::storage::{ColumnId, FileId, SequenceNumber, SequenceRange};
61
62use crate::compaction::{collect_json2_rewrite_plans, rewrite_json2_batch, rewrite_json2_schema};
63use crate::error::{
64 self, ColumnNotFoundSnafu, ComputeArrowSnafu, CreateDefaultSnafu, DataTypeMismatchSnafu,
65 EncodeMemtableSnafu, EncodeSnafu, InvalidMetadataSnafu, InvalidRequestSnafu,
66 NewRecordBatchSnafu, Result,
67};
68use crate::memtable::bulk::context::{BulkIterContext, BulkIterContextRef};
69use crate::memtable::bulk::part_reader::EncodedBulkPartIter;
70use crate::memtable::time_series::{ValueBuilder, Values};
71use crate::memtable::{BoxedRecordBatchIterator, MemScanMetrics, MemtableStats};
72use crate::sst::SeriesEstimator;
73use crate::sst::index::IndexOutput;
74use crate::sst::parquet::flat_format::primary_key_column_index;
75use crate::sst::parquet::format::{PrimaryKeyArray, PrimaryKeyArrayBuilder};
76use crate::sst::parquet::{
77 COLUMN_INDEX_TRUNCATE_LENGTH, PARQUET_METADATA_KEY, SstInfo, apply_column_compression,
78 apply_float_field_encoding, apply_internal_column_encoding,
79};
80
81const INIT_DICT_VALUE_CAPACITY: usize = 8;
82
83#[derive(Clone)]
85pub struct BulkPart {
86 pub batch: RecordBatch,
87 pub max_timestamp: i64,
88 pub min_timestamp: i64,
89 pub sequence: u64,
92 pub min_sequence: SequenceNumber,
95 pub timestamp_index: usize,
96 pub raw_data: Option<ArrowIpc>,
97}
98
99impl TryFrom<BulkWalEntry> for BulkPart {
100 type Error = error::Error;
101
102 fn try_from(value: BulkWalEntry) -> std::result::Result<Self, Self::Error> {
103 match value.body.expect("Entry payload should be present") {
104 Body::ArrowIpc(ipc) => {
105 let mut decoder = FlightDecoder::try_from_schema_bytes(&ipc.schema)
106 .context(error::ConvertBulkWalEntrySnafu)?;
107 let batch = decoder
108 .try_decode_record_batch(&ipc.data_header, &ipc.payload)
109 .context(error::ConvertBulkWalEntrySnafu)?;
110 Ok(Self {
111 batch,
112 max_timestamp: value.max_ts,
113 min_timestamp: value.min_ts,
114 sequence: value.sequence,
115 min_sequence: value.sequence,
118 timestamp_index: value.timestamp_index as usize,
119 raw_data: Some(ipc),
120 })
121 }
122 }
123 }
124}
125
126impl TryFrom<&BulkPart> for BulkWalEntry {
127 type Error = error::Error;
128
129 fn try_from(value: &BulkPart) -> Result<Self> {
130 if let Some(ipc) = &value.raw_data {
131 Ok(BulkWalEntry {
132 sequence: value.sequence,
133 max_ts: value.max_timestamp,
134 min_ts: value.min_timestamp,
135 timestamp_index: value.timestamp_index as u32,
136 body: Some(Body::ArrowIpc(ipc.clone())),
137 })
138 } else {
139 let mut encoder = FlightEncoder::default();
140 let schema_bytes = encoder
141 .encode_schema(value.batch.schema().as_ref())
142 .data_header;
143 let [rb_data] = encoder
144 .encode(FlightMessage::RecordBatch(value.batch.clone()))
145 .try_into()
146 .map_err(|_| {
147 error::UnsupportedOperationSnafu {
148 err_msg: "create BulkWalEntry from RecordBatch with dictionary arrays",
149 }
150 .build()
151 })?;
152 Ok(BulkWalEntry {
153 sequence: value.sequence,
154 max_ts: value.max_timestamp,
155 min_ts: value.min_timestamp,
156 timestamp_index: value.timestamp_index as u32,
157 body: Some(Body::ArrowIpc(ArrowIpc {
158 schema: schema_bytes,
159 data_header: rb_data.data_header,
160 payload: rb_data.data_body,
161 })),
162 })
163 }
164 }
165}
166
167impl BulkPart {
168 pub(crate) fn schema(&self) -> SchemaRef {
169 self.batch.schema()
170 }
171
172 pub(crate) fn estimated_size(&self) -> usize {
173 record_batch_estimated_size(&self.batch)
174 }
175
176 pub fn estimated_series_count(&self) -> usize {
179 let pk_column_idx = primary_key_column_index(self.batch.num_columns());
180 let pk_column = self.batch.column(pk_column_idx);
181 if let Some(dict_array) = pk_column.as_any().downcast_ref::<PrimaryKeyArray>() {
182 dict_array.values().len()
183 } else {
184 0
185 }
186 }
187
188 pub fn to_memtable_stats(&self, region_metadata: &RegionMetadataRef) -> MemtableStats {
190 let ts_type = region_metadata.time_index_type();
191 let min_ts = ts_type.create_timestamp(self.min_timestamp);
192 let max_ts = ts_type.create_timestamp(self.max_timestamp);
193
194 MemtableStats {
195 estimated_bytes: self.estimated_size(),
196 time_range: Some((min_ts, max_ts)),
197 num_rows: self.num_rows(),
198 num_ranges: 1,
199 max_sequence: self.sequence,
200 min_sequence: self.min_sequence,
201 series_count: self.estimated_series_count(),
202 }
203 }
204
205 pub fn fill_missing_columns(&mut self, region_metadata: &RegionMetadata) -> Result<()> {
215 let batch_schema = self.batch.schema();
217 let batch_columns: HashSet<_> = batch_schema
218 .fields()
219 .iter()
220 .map(|f| f.name().as_str())
221 .collect();
222
223 let mut columns_to_fill = Vec::new();
225 for column_meta in ®ion_metadata.column_metadatas {
226 if batch_columns.contains(PRIMARY_KEY_COLUMN_NAME)
228 && column_meta.semantic_type == SemanticType::Tag
229 {
230 continue;
231 }
232 if !batch_columns.contains(column_meta.column_schema.name.as_str()) {
235 columns_to_fill.push(column_meta);
236 }
237 }
238
239 if columns_to_fill.is_empty() {
240 return Ok(());
241 }
242
243 let num_rows = self.batch.num_rows();
244
245 let mut new_columns = Vec::new();
246 let mut new_fields = Vec::new();
247
248 new_fields.extend(batch_schema.fields().iter().cloned());
250 new_columns.extend_from_slice(self.batch.columns());
251
252 let region_id = region_metadata.region_id;
253 for column_meta in columns_to_fill {
255 let default_vector = column_meta
256 .column_schema
257 .create_default_vector(num_rows)
258 .context(CreateDefaultSnafu {
259 region_id,
260 column: &column_meta.column_schema.name,
261 })?
262 .with_context(|| InvalidRequestSnafu {
263 region_id,
264 reason: format!(
265 "column {} does not have default value",
266 column_meta.column_schema.name
267 ),
268 })?;
269 let arrow_array = default_vector.to_arrow_array();
270 new_fields.push(Arc::new(
271 Field::new(
272 column_meta.column_schema.name.clone(),
273 arrow_array.data_type().clone(),
274 column_meta.column_schema.is_nullable(),
275 )
276 .with_metadata(column_meta.column_schema.metadata().clone()),
277 ));
278 new_columns.push(arrow_array);
279 }
280
281 let new_schema = Arc::new(Schema::new(new_fields));
283 let new_batch =
284 RecordBatch::try_new(new_schema, new_columns).context(NewRecordBatchSnafu)?;
285
286 self.batch = new_batch;
288
289 self.raw_data = None;
294
295 Ok(())
296 }
297
298 pub(crate) fn to_mutation(&self, region_metadata: &RegionMetadataRef) -> Result<Mutation> {
300 let vectors = region_metadata
301 .schema
302 .column_schemas()
303 .iter()
304 .map(|col| match self.batch.column_by_name(&col.name) {
305 None => Ok(None),
306 Some(col) => Helper::try_into_vector(col).map(Some),
307 })
308 .collect::<datatypes::error::Result<Vec<_>>>()
309 .context(error::ComputeVectorSnafu)?;
310
311 let rows = (0..self.num_rows())
312 .map(|row_idx| {
313 let values = (0..self.batch.num_columns())
314 .map(|col_idx| {
315 if let Some(v) = &vectors[col_idx] {
316 to_grpc_value(v.get(row_idx))
317 } else {
318 api::v1::Value { value_data: None }
319 }
320 })
321 .collect::<Vec<_>>();
322 api::v1::Row { values }
323 })
324 .collect::<Vec<_>>();
325
326 let schema = region_metadata
327 .column_metadatas
328 .iter()
329 .map(|c| {
330 let data_type_wrapper =
331 ColumnDataTypeWrapper::try_from(c.column_schema.data_type.clone())?;
332 Ok(api::v1::ColumnSchema {
333 column_name: c.column_schema.name.clone(),
334 datatype: data_type_wrapper.datatype() as i32,
335 semantic_type: c.semantic_type as i32,
336 ..Default::default()
337 })
338 })
339 .collect::<api::error::Result<Vec<_>>>()
340 .context(error::ConvertColumnDataTypeSnafu {
341 reason: "failed to convert region metadata to column schema",
342 })?;
343
344 let rows = api::v1::Rows { schema, rows };
345
346 Ok(Mutation {
347 op_type: OpType::Put as i32,
348 sequence: self.sequence,
349 rows: Some(rows),
350 write_hint: None,
351 })
352 }
353
354 pub fn timestamps(&self) -> &ArrayRef {
355 self.batch.column(self.timestamp_index)
356 }
357
358 pub fn num_rows(&self) -> usize {
359 self.batch.num_rows()
360 }
361}
362
363pub struct UnorderedPart {
366 parts: Vec<BulkPart>,
368 total_rows: usize,
370 total_bytes: usize,
372 min_timestamp: i64,
374 max_timestamp: i64,
376 max_sequence: u64,
378 min_sequence: SequenceNumber,
380 threshold: usize,
382 compact_threshold: usize,
384}
385
386impl Default for UnorderedPart {
387 fn default() -> Self {
388 Self::new()
389 }
390}
391
392impl UnorderedPart {
393 pub fn new() -> Self {
395 Self {
396 parts: Vec::new(),
397 total_rows: 0,
398 total_bytes: 0,
399 min_timestamp: i64::MAX,
400 max_timestamp: i64::MIN,
401 max_sequence: 0,
402 min_sequence: SequenceNumber::MAX,
403 threshold: 1024,
404 compact_threshold: 4096,
405 }
406 }
407
408 pub fn set_threshold(&mut self, threshold: usize) {
410 self.threshold = threshold;
411 }
412
413 pub fn set_compact_threshold(&mut self, compact_threshold: usize) {
415 self.compact_threshold = compact_threshold;
416 }
417
418 pub fn threshold(&self) -> usize {
420 self.threshold
421 }
422
423 pub fn compact_threshold(&self) -> usize {
425 self.compact_threshold
426 }
427
428 pub fn should_accept(&self, num_rows: usize) -> bool {
430 num_rows < self.threshold
431 }
432
433 pub fn should_compact(&self) -> bool {
435 self.total_rows >= self.compact_threshold
436 }
437
438 pub(super) fn estimated_bytes(&self) -> usize {
440 self.total_bytes
441 }
442
443 pub(super) fn sequence_bounds(&self) -> Option<(SequenceNumber, SequenceNumber)> {
445 (!self.is_empty()).then_some((self.min_sequence, self.max_sequence))
446 }
447
448 pub fn push(&mut self, part: BulkPart) {
450 self.total_rows += part.num_rows();
451 self.total_bytes = self.total_bytes.saturating_add(part.estimated_size());
452 self.min_timestamp = self.min_timestamp.min(part.min_timestamp);
453 self.max_timestamp = self.max_timestamp.max(part.max_timestamp);
454 self.max_sequence = self.max_sequence.max(part.sequence);
455 self.min_sequence = self.min_sequence.min(part.min_sequence);
456 self.parts.push(part);
457 }
458
459 pub fn num_rows(&self) -> usize {
461 self.total_rows
462 }
463
464 pub fn is_empty(&self) -> bool {
466 self.parts.is_empty()
467 }
468
469 pub fn num_parts(&self) -> usize {
471 self.parts.len()
472 }
473
474 pub fn concat_and_sort(&self, metadata: &RegionMetadataRef) -> Result<Option<RecordBatch>> {
477 if self.parts.is_empty() {
478 return Ok(None);
479 }
480
481 if self.parts.len() == 1 {
482 return Ok(Some(self.parts[0].batch.clone()));
484 }
485
486 let schemas = self
487 .parts
488 .iter()
489 .map(|x| (x.batch.schema(), x.num_rows() as u64))
490 .collect::<Vec<_>>();
491 let plans = collect_json2_rewrite_plans(metadata, &schemas)?;
492
493 debug_assert!(self.parts.windows(2).all(|w| rewrite_json2_schema(
494 &w[0].batch.schema(),
495 &plans
496 ) == rewrite_json2_schema(
497 &w[1].batch.schema(),
498 &plans
499 )));
500 let schema = rewrite_json2_schema(&self.parts[0].batch.schema(), &plans);
501
502 let batches = self
503 .parts
504 .iter()
505 .map(|x| rewrite_json2_batch(x.batch.clone(), &plans))
506 .collect::<Result<Vec<_>>>()?;
507 let concatenated = concat_batches(&schema, &batches).context(ComputeArrowSnafu)?;
508
509 let sorted_batch = sort_primary_key_record_batch(&concatenated)?;
511
512 Ok(Some(sorted_batch))
513 }
514
515 pub fn to_bulk_part(&self, metadata: &RegionMetadataRef) -> Result<Option<BulkPart>> {
518 let Some(sorted_batch) = self.concat_and_sort(metadata)? else {
519 return Ok(None);
520 };
521
522 let timestamp_index = self.parts[0].timestamp_index;
523
524 Ok(Some(BulkPart {
525 batch: sorted_batch,
526 max_timestamp: self.max_timestamp,
527 min_timestamp: self.min_timestamp,
528 sequence: self.max_sequence,
529 min_sequence: self.min_sequence,
530 timestamp_index,
531 raw_data: None,
532 }))
533 }
534
535 pub fn clear(&mut self) {
537 self.parts.clear();
538 self.total_rows = 0;
539 self.total_bytes = 0;
540 self.min_timestamp = i64::MAX;
541 self.max_timestamp = i64::MIN;
542 self.max_sequence = 0;
543 self.min_sequence = SequenceNumber::MAX;
544 }
545}
546
547pub fn record_batch_estimated_size(batch: &RecordBatch) -> usize {
549 batch
550 .columns()
551 .iter()
552 .map(|c| c.to_data().get_slice_memory_size().unwrap_or(0))
554 .sum()
555}
556
557enum PrimaryKeyColumnBuilder {
559 StringDict(StringDictionaryBuilder<UInt32Type>),
561 Vector(Box<dyn MutableVector>),
563}
564
565impl PrimaryKeyColumnBuilder {
566 fn push_value_ref(&mut self, value: ValueRef) -> Result<()> {
568 match self {
569 PrimaryKeyColumnBuilder::StringDict(builder) => {
570 if let Some(s) = value.try_into_string().context(DataTypeMismatchSnafu)? {
571 builder.append_value(s);
573 } else {
574 builder.append_null();
575 }
576 }
577 PrimaryKeyColumnBuilder::Vector(builder) => {
578 builder.push_value_ref(&value);
579 }
580 }
581 Ok(())
582 }
583
584 fn into_arrow_array(self) -> ArrayRef {
586 match self {
587 PrimaryKeyColumnBuilder::StringDict(mut builder) => Arc::new(builder.finish()),
588 PrimaryKeyColumnBuilder::Vector(mut builder) => builder.to_vector().to_arrow_array(),
589 }
590 }
591}
592
593pub struct BulkPartConverter {
595 schema: SchemaRef,
597 primary_key_codec: Arc<dyn PrimaryKeyCodec>,
599 key_buf: Vec<u8>,
601 key_array_builder: PrimaryKeyArrayBuilder,
603 has_pre_encoded_keys: bool,
605 value_builder: ValueBuilder,
607 primary_key_column_builders: Vec<PrimaryKeyColumnBuilder>,
610
611 max_ts: i64,
613 min_ts: i64,
615 max_sequence: SequenceNumber,
617 min_sequence: SequenceNumber,
619}
620
621impl BulkPartConverter {
622 pub fn new(
627 region_metadata: &RegionMetadataRef,
628 schema: SchemaRef,
629 capacity: usize,
630 primary_key_codec: Arc<dyn PrimaryKeyCodec>,
631 store_primary_key_columns: bool,
632 ) -> Self {
633 debug_assert_eq!(
634 region_metadata.primary_key_encoding,
635 primary_key_codec.encoding()
636 );
637
638 let primary_key_column_builders = if store_primary_key_columns
639 && region_metadata.primary_key_encoding != PrimaryKeyEncoding::Sparse
640 {
641 new_primary_key_column_builders(region_metadata, capacity)
642 } else {
643 Vec::new()
644 };
645
646 Self {
647 schema,
648 primary_key_codec,
649 key_buf: Vec::new(),
650 key_array_builder: PrimaryKeyArrayBuilder::new(),
651 has_pre_encoded_keys: false,
652 value_builder: ValueBuilder::new(region_metadata, capacity),
653 primary_key_column_builders,
654 min_ts: i64::MAX,
655 max_ts: i64::MIN,
656 max_sequence: SequenceNumber::MIN,
657 min_sequence: SequenceNumber::MAX,
658 }
659 }
660
661 pub fn append_key_values(&mut self, key_values: &KeyValues) -> Result<()> {
663 for kv in key_values.iter() {
664 self.append_key_value(&kv)?;
665 }
666
667 Ok(())
668 }
669
670 fn append_key_value(&mut self, kv: &KeyValue) -> Result<()> {
674 if kv.primary_key_encoding() == PrimaryKeyEncoding::Sparse {
676 ensure!(
677 self.primary_key_codec.encoding() == PrimaryKeyEncoding::Sparse,
678 error::InvalidRecordBatchSnafu {
679 reason: "pre-encoded primary keys require a sparse destination layout",
680 }
681 );
682 let mut primary_keys = kv.primary_keys();
685 if let Some(encoded) = primary_keys
686 .next()
687 .context(ColumnNotFoundSnafu {
688 column: PRIMARY_KEY_COLUMN_NAME,
689 })?
690 .try_into_binary()
691 .context(DataTypeMismatchSnafu)?
692 {
693 self.key_array_builder
694 .append(encoded)
695 .context(ComputeArrowSnafu)?;
696 self.has_pre_encoded_keys = true;
697 } else {
698 return error::InvalidRecordBatchSnafu {
699 reason: "encoded metric primary key must not be null",
700 }
701 .fail();
702 }
703 } else {
704 self.key_buf.clear();
706 self.primary_key_codec
707 .encode_key_value(kv, &mut self.key_buf)
708 .context(EncodeSnafu)?;
709 self.key_array_builder
710 .append(&self.key_buf)
711 .context(ComputeArrowSnafu)?;
712 };
713
714 if !self.primary_key_column_builders.is_empty() {
716 for (builder, pk_value) in self
717 .primary_key_column_builders
718 .iter_mut()
719 .zip(kv.primary_keys())
720 {
721 builder.push_value_ref(pk_value)?;
722 }
723 }
724
725 self.value_builder.push(
727 kv.timestamp(),
728 kv.sequence(),
729 kv.op_type() as u8,
730 kv.fields(),
731 );
732
733 let ts = kv
736 .timestamp()
737 .try_into_timestamp()
738 .unwrap()
739 .unwrap()
740 .value();
741 self.min_ts = self.min_ts.min(ts);
742 self.max_ts = self.max_ts.max(ts);
743 self.max_sequence = self.max_sequence.max(kv.sequence());
744 self.min_sequence = self.min_sequence.min(kv.sequence());
745
746 Ok(())
747 }
748
749 pub fn convert(mut self) -> Result<BulkPart> {
753 let values = Values::from(self.value_builder);
754 let mut columns =
755 Vec::with_capacity(4 + values.fields.len() + self.primary_key_column_builders.len());
756
757 for builder in self.primary_key_column_builders {
759 columns.push(builder.into_arrow_array());
760 }
761 columns.extend(values.fields.iter().map(|field| field.to_arrow_array()));
763 let timestamp_index = columns.len();
765 columns.push(values.timestamp.to_arrow_array());
766 let pk_array = self.key_array_builder.finish();
768 if self.has_pre_encoded_keys {
769 validate_sparse_dictionary(&pk_array)?;
770 }
771 columns.push(Arc::new(pk_array));
772 columns.push(values.sequence.to_arrow_array());
774 columns.push(values.op_type.to_arrow_array());
775
776 let schema = align_schema_with_json_array(self.schema, &columns);
779 let batch = RecordBatch::try_new(schema, columns).context(NewRecordBatchSnafu)?;
780 let batch = sort_primary_key_record_batch(&batch)?;
782
783 Ok(BulkPart {
784 batch,
785 max_timestamp: self.max_ts,
786 min_timestamp: self.min_ts,
787 sequence: self.max_sequence,
788 min_sequence: self.min_sequence,
789 timestamp_index,
790 raw_data: None,
791 })
792 }
793}
794
795fn validate_sparse_dictionary(keys: &PrimaryKeyArray) -> Result<()> {
797 let values = keys
798 .values()
799 .as_any()
800 .downcast_ref::<BinaryArray>()
801 .context(error::InvalidRecordBatchSnafu {
802 reason: "encoded primary keys must be binary",
803 })?;
804 ensure!(
805 keys.logical_null_count() == 0,
806 error::InvalidRecordBatchSnafu {
807 reason: "encoded primary key must not be null",
808 }
809 );
810 let mut validated = vec![false; values.len()];
811 let mut validator = SparsePrimaryKeyValidator::default();
812 for &index in keys.keys().values() {
813 let index = index as usize;
814 if !validated[index] {
815 validator
816 .validate(values.value(index))
817 .context(EncodeSnafu)?;
818 validated[index] = true;
819 }
820 }
821 Ok(())
822}
823
824fn new_primary_key_column_builders(
825 metadata: &RegionMetadata,
826 capacity: usize,
827) -> Vec<PrimaryKeyColumnBuilder> {
828 metadata
829 .primary_key_columns()
830 .map(|col| {
831 if col.column_schema.data_type.is_string() {
832 PrimaryKeyColumnBuilder::StringDict(StringDictionaryBuilder::with_capacity(
833 capacity,
834 INIT_DICT_VALUE_CAPACITY,
835 capacity,
836 ))
837 } else {
838 PrimaryKeyColumnBuilder::Vector(
839 col.column_schema.data_type.create_mutable_vector(capacity),
840 )
841 }
842 })
843 .collect()
844}
845
846pub fn sort_primary_key_record_batch(batch: &RecordBatch) -> Result<RecordBatch> {
848 let indices = if let Some(indices) = try_sort_primary_key_indices(batch)? {
849 indices
850 } else {
851 lexsort_primary_key_indices(batch)?
852 };
853
854 datatypes::arrow::compute::take_record_batch(batch, &indices).context(ComputeArrowSnafu)
855}
856
857fn try_sort_primary_key_indices(batch: &RecordBatch) -> Result<Option<UInt32Array>> {
861 let total_columns = batch.num_columns();
862 let timestamp = batch.column(total_columns - 4);
863 let primary_key = batch.column(total_columns - 3);
864 let sequence = batch.column(total_columns - 2);
865
866 let Some(primary_key) = primary_key
867 .as_any()
868 .downcast_ref::<DictionaryArray<UInt32Type>>()
869 else {
870 return Ok(None);
871 };
872 let Some(sequence) = sequence.as_any().downcast_ref::<UInt64Array>() else {
873 return Ok(None);
874 };
875
876 if batch.num_rows() > u32::MAX as usize
877 || primary_key.null_count() != 0
878 || primary_key.values().data_type() != &ArrowDataType::Binary
879 || primary_key.values().null_count() != 0
880 || primary_key.values().len() > batch.num_rows()
881 || timestamp.null_count() != 0
882 || sequence.null_count() != 0
883 {
884 return Ok(None);
885 }
886
887 let indices = match timestamp.data_type() {
888 ArrowDataType::Timestamp(TimeUnit::Second, _) => timestamp
889 .as_any()
890 .downcast_ref::<TimestampSecondArray>()
891 .map(|timestamp| sort_primary_key_indices(primary_key, timestamp.values(), sequence)),
892 ArrowDataType::Timestamp(TimeUnit::Millisecond, _) => timestamp
893 .as_any()
894 .downcast_ref::<TimestampMillisecondArray>()
895 .map(|timestamp| sort_primary_key_indices(primary_key, timestamp.values(), sequence)),
896 ArrowDataType::Timestamp(TimeUnit::Microsecond, _) => timestamp
897 .as_any()
898 .downcast_ref::<TimestampMicrosecondArray>()
899 .map(|timestamp| sort_primary_key_indices(primary_key, timestamp.values(), sequence)),
900 ArrowDataType::Timestamp(TimeUnit::Nanosecond, _) => timestamp
901 .as_any()
902 .downcast_ref::<TimestampNanosecondArray>()
903 .map(|timestamp| sort_primary_key_indices(primary_key, timestamp.values(), sequence)),
904 _ => None,
905 };
906
907 indices.transpose()
908}
909
910fn sort_primary_key_indices(
913 primary_key: &PrimaryKeyArray,
914 timestamps: &[i64],
915 sequences: &UInt64Array,
916) -> Result<UInt32Array> {
917 debug_assert_eq!(primary_key.len(), timestamps.len());
918 debug_assert_eq!(primary_key.len(), sequences.len());
919 debug_assert_eq!(primary_key.null_count(), 0);
920 debug_assert_eq!(primary_key.values().null_count(), 0);
921 debug_assert_eq!(sequences.null_count(), 0);
922
923 let value_ranks = datatypes::arrow::compute::rank(
927 primary_key.values().as_ref(),
928 Some(SortOptions {
929 descending: false,
930 nulls_first: true,
931 }),
932 )
933 .context(ComputeArrowSnafu)?;
934
935 let mut counts = vec![0usize; value_ranks.len() + 1];
938 for &key in primary_key.keys().values() {
939 counts[value_ranks[key as usize] as usize] += 1;
940 }
941
942 let mut bucket_offsets = vec![0usize; counts.len()];
943 let mut offset = 0;
944 for (bucket_offset, &count) in bucket_offsets.iter_mut().zip(&counts) {
945 *bucket_offset = offset;
946 offset += count;
947 }
948
949 let mut next_offsets = bucket_offsets.clone();
951 let mut indices = vec![0u32; primary_key.len()];
952 for (row, &key) in primary_key.keys().values().iter().enumerate() {
953 let rank = value_ranks[key as usize] as usize;
954 indices[next_offsets[rank]] = row as u32;
955 next_offsets[rank] += 1;
956 }
957
958 let sequences = sequences.values();
959 for (&start, count) in bucket_offsets.iter().zip(counts) {
960 let end = start + count;
961 let bucket = &mut indices[start..end];
962 if !bucket.is_sorted_by(|left, right| {
963 compare_time_sequence(timestamps, sequences, *left, *right).is_le()
964 }) {
965 bucket.sort_unstable_by(|left, right| {
966 compare_time_sequence(timestamps, sequences, *left, *right)
967 });
968 }
969 }
970
971 Ok(UInt32Array::from(indices))
972}
973
974#[inline]
975fn compare_time_sequence(
976 timestamps: &[i64],
977 sequences: &[u64],
978 left: u32,
979 right: u32,
980) -> std::cmp::Ordering {
981 let left = left as usize;
982 let right = right as usize;
983 timestamps[left]
984 .cmp(×tamps[right])
985 .then_with(|| sequences[right].cmp(&sequences[left]))
986}
987
988fn lexsort_primary_key_indices(batch: &RecordBatch) -> Result<UInt32Array> {
989 let total_columns = batch.num_columns();
990 let sort_columns = vec![
991 SortColumn {
993 values: batch.column(total_columns - 3).clone(),
994 options: Some(SortOptions {
995 descending: false,
996 nulls_first: true,
997 }),
998 },
999 SortColumn {
1001 values: batch.column(total_columns - 4).clone(),
1002 options: Some(SortOptions {
1003 descending: false,
1004 nulls_first: true,
1005 }),
1006 },
1007 SortColumn {
1009 values: batch.column(total_columns - 2).clone(),
1010 options: Some(SortOptions {
1011 descending: true,
1012 nulls_first: true,
1013 }),
1014 },
1015 ];
1016
1017 datatypes::arrow::compute::lexsort_to_indices(&sort_columns, None).context(ComputeArrowSnafu)
1018}
1019
1020pub fn convert_bulk_part(
1046 part: BulkPart,
1047 region_metadata: &RegionMetadataRef,
1048 primary_key_codec: Arc<dyn PrimaryKeyCodec>,
1049 schema: SchemaRef,
1050 store_primary_key_columns: bool,
1051) -> Result<Option<BulkPart>> {
1052 if part.num_rows() == 0 {
1053 return Ok(None);
1054 }
1055
1056 let num_rows = part.num_rows();
1057 let is_sparse = region_metadata.primary_key_encoding == PrimaryKeyEncoding::Sparse;
1058
1059 let input_schema = part.batch.schema();
1061 let column_indices: HashMap<&str, usize> = input_schema
1062 .fields()
1063 .iter()
1064 .enumerate()
1065 .map(|(idx, field)| (field.name().as_str(), idx))
1066 .collect();
1067
1068 let mut output_columns = Vec::new();
1070
1071 let pk_dictionary: ArrayRef = if column_indices.contains_key(PRIMARY_KEY_COLUMN_NAME) {
1073 ensure!(
1074 is_sparse,
1075 error::InvalidRecordBatchSnafu {
1076 reason: "pre-encoded bulk keys require a sparse destination layout"
1077 }
1078 );
1079 let column = part.batch.column(column_indices[PRIMARY_KEY_COLUMN_NAME]);
1080 ensure!(
1081 column.data_type() == &ArrowDataType::Binary
1082 || matches!(column.data_type(), ArrowDataType::Dictionary(_, value) if value.as_ref() == &ArrowDataType::Binary),
1083 error::InvalidRecordBatchSnafu {
1084 reason: "encoded primary keys must be binary"
1085 }
1086 );
1087 let target_type = ArrowDataType::Dictionary(
1088 Box::new(ArrowDataType::UInt32),
1089 Box::new(ArrowDataType::Binary),
1090 );
1091 let column = arrow::compute::cast(column, &target_type).context(ComputeArrowSnafu)?;
1092 let keys = column.as_any().downcast_ref::<PrimaryKeyArray>().context(
1093 error::InvalidRecordBatchSnafu {
1094 reason: "encoded primary keys must be a UInt32 binary dictionary",
1095 },
1096 )?;
1097 validate_sparse_dictionary(keys)?;
1098 column
1099 } else {
1100 let pk_vectors: Result<Vec<_>> = region_metadata
1102 .primary_key_columns()
1103 .map(|col_meta| {
1104 let col_idx = column_indices
1105 .get(col_meta.column_schema.name.as_str())
1106 .context(ColumnNotFoundSnafu {
1107 column: &col_meta.column_schema.name,
1108 })?;
1109 let col = part.batch.column(*col_idx);
1110 Helper::try_into_vector(col).context(error::ComputeVectorSnafu)
1111 })
1112 .collect();
1113 let pk_vectors = pk_vectors?;
1114
1115 let mut key_array_builder = PrimaryKeyArrayBuilder::new();
1116 let mut encode_buf = Vec::new();
1117
1118 for row_idx in 0..num_rows {
1119 encode_buf.clear();
1120
1121 let pk_values_with_ids: Vec<_> = region_metadata
1123 .primary_key
1124 .iter()
1125 .zip(pk_vectors.iter())
1126 .map(|(col_id, vector)| (*col_id, vector.get_ref(row_idx)))
1127 .collect();
1128
1129 primary_key_codec
1131 .encode_value_refs(&pk_values_with_ids, &mut encode_buf)
1132 .context(EncodeSnafu)?;
1133
1134 key_array_builder
1135 .append(&encode_buf)
1136 .context(ComputeArrowSnafu)?;
1137 }
1138
1139 Arc::new(key_array_builder.finish())
1140 };
1141
1142 if store_primary_key_columns && !is_sparse {
1144 for col_meta in region_metadata.primary_key_columns() {
1145 let col_idx = column_indices
1146 .get(col_meta.column_schema.name.as_str())
1147 .context(ColumnNotFoundSnafu {
1148 column: &col_meta.column_schema.name,
1149 })?;
1150 let col = part.batch.column(*col_idx);
1151
1152 let col = if col_meta.column_schema.data_type.is_string() {
1154 let target_type = ArrowDataType::Dictionary(
1155 Box::new(ArrowDataType::UInt32),
1156 Box::new(ArrowDataType::Utf8),
1157 );
1158 arrow::compute::cast(col, &target_type).context(ComputeArrowSnafu)?
1159 } else {
1160 col.clone()
1161 };
1162 output_columns.push(col);
1163 }
1164 }
1165
1166 for col_meta in region_metadata.field_columns() {
1168 let col_idx = column_indices
1169 .get(col_meta.column_schema.name.as_str())
1170 .context(ColumnNotFoundSnafu {
1171 column: &col_meta.column_schema.name,
1172 })?;
1173 output_columns.push(part.batch.column(*col_idx).clone());
1174 }
1175
1176 let new_timestamp_index = output_columns.len();
1178 let ts_col_idx = column_indices
1179 .get(
1180 region_metadata
1181 .time_index_column()
1182 .column_schema
1183 .name
1184 .as_str(),
1185 )
1186 .context(ColumnNotFoundSnafu {
1187 column: ®ion_metadata.time_index_column().column_schema.name,
1188 })?;
1189 output_columns.push(part.batch.column(*ts_col_idx).clone());
1190
1191 output_columns.push(pk_dictionary);
1193
1194 let sequence_array = UInt64Array::from(vec![part.sequence; num_rows]);
1195 output_columns.push(Arc::new(sequence_array) as ArrayRef);
1196
1197 let op_type_array = UInt8Array::from(vec![OpType::Put as u8; num_rows]);
1198 output_columns.push(Arc::new(op_type_array) as ArrayRef);
1199
1200 let schema = if region_metadata
1203 .field_columns()
1204 .any(|c| c.column_schema.data_type.is_json2())
1205 {
1206 let fields = schema
1207 .fields()
1208 .iter()
1209 .map(|field| {
1210 if !datatypes::extension::json::is_json2_extension_type(field) {
1211 return field.clone();
1212 }
1213 let source = input_schema.field(column_indices[field.name().as_str()]);
1214 let mut metadata = field.metadata().clone();
1215 if datatypes::extension::json::is_json2_extension_type(source) {
1216 for key in [
1217 arrow_schema::extension::EXTENSION_TYPE_NAME_KEY,
1218 arrow_schema::extension::EXTENSION_TYPE_METADATA_KEY,
1219 ] {
1220 if let Some(value) = source.metadata().get(key) {
1221 metadata.insert(key.to_string(), value.clone());
1222 } else {
1223 metadata.remove(key);
1224 }
1225 }
1226 }
1227 Arc::new(
1228 field
1229 .as_ref()
1230 .clone()
1231 .with_data_type(source.data_type().clone())
1232 .with_metadata(metadata),
1233 )
1234 })
1235 .collect::<Vec<_>>();
1236 Arc::new(Schema::new_with_metadata(fields, schema.metadata().clone()))
1237 } else {
1238 schema
1239 };
1240 let batch = RecordBatch::try_new(schema, output_columns).context(NewRecordBatchSnafu)?;
1241
1242 let sorted_batch = sort_primary_key_record_batch(&batch)?;
1244
1245 Ok(Some(BulkPart {
1246 batch: sorted_batch,
1247 max_timestamp: part.max_timestamp,
1248 min_timestamp: part.min_timestamp,
1249 sequence: part.sequence,
1250 min_sequence: part.sequence,
1251 timestamp_index: new_timestamp_index,
1252 raw_data: None,
1253 }))
1254}
1255
1256#[derive(Debug, Clone)]
1257pub struct EncodedBulkPart {
1258 data: Bytes,
1259 metadata: BulkPartMeta,
1260 schema: SchemaRef,
1262}
1263
1264impl EncodedBulkPart {
1265 pub fn new(data: Bytes, metadata: BulkPartMeta, schema: SchemaRef) -> Self {
1266 Self {
1267 data,
1268 metadata,
1269 schema,
1270 }
1271 }
1272
1273 pub fn metadata(&self) -> &BulkPartMeta {
1274 &self.metadata
1275 }
1276
1277 pub(crate) fn schema(&self) -> SchemaRef {
1278 self.schema.clone()
1279 }
1280
1281 pub(crate) fn size_bytes(&self) -> usize {
1283 self.data.len()
1284 }
1285
1286 pub fn data(&self) -> &Bytes {
1288 &self.data
1289 }
1290
1291 pub fn to_memtable_stats(&self) -> MemtableStats {
1293 let meta = &self.metadata;
1294 let ts_type = meta.region_metadata.time_index_type();
1295 let min_ts = ts_type.create_timestamp(meta.min_timestamp);
1296 let max_ts = ts_type.create_timestamp(meta.max_timestamp);
1297
1298 MemtableStats {
1299 estimated_bytes: self.size_bytes(),
1300 time_range: Some((min_ts, max_ts)),
1301 num_rows: meta.num_rows,
1302 num_ranges: 1,
1303 max_sequence: meta.max_sequence,
1304 min_sequence: meta.min_sequence,
1305 series_count: meta.num_series as usize,
1306 }
1307 }
1308
1309 pub(crate) fn to_sst_info(&self, file_id: FileId) -> SstInfo {
1317 let unit = self.metadata.region_metadata.time_index_type().unit();
1318 let max_row_group_uncompressed_size: u64 = self
1319 .metadata
1320 .parquet_metadata
1321 .row_groups()
1322 .iter()
1323 .map(|rg| {
1324 rg.columns()
1325 .iter()
1326 .map(|c| c.uncompressed_size() as u64)
1327 .sum::<u64>()
1328 })
1329 .max()
1330 .unwrap_or(0);
1331 SstInfo {
1332 file_id,
1333 time_range: (
1334 Timestamp::new(self.metadata.min_timestamp, unit),
1335 Timestamp::new(self.metadata.max_timestamp, unit),
1336 ),
1337 file_size: self.data.len() as u64,
1338 max_row_group_uncompressed_size,
1339 num_rows: self.metadata.num_rows,
1340 num_row_groups: self.metadata.parquet_metadata.num_row_groups() as u64,
1341 file_metadata: Some(self.metadata.parquet_metadata.clone()),
1342 index_metadata: IndexOutput::default(),
1343 num_series: self.metadata.num_series,
1344 }
1345 }
1346
1347 pub(crate) fn read(
1348 &self,
1349 context: BulkIterContextRef,
1350 sequence: Option<SequenceRange>,
1351 mem_scan_metrics: Option<MemScanMetrics>,
1352 ) -> Result<Option<BoxedRecordBatchIterator>> {
1353 let skip_fields_for_pruning = context.pre_filter_mode().skip_fields();
1355
1356 let row_groups_to_read =
1358 context.row_groups_to_read(&self.metadata.parquet_metadata, skip_fields_for_pruning);
1359
1360 if row_groups_to_read.is_empty() {
1361 return Ok(None);
1363 }
1364
1365 let iter = EncodedBulkPartIter::try_new(
1366 self,
1367 context,
1368 row_groups_to_read,
1369 sequence,
1370 mem_scan_metrics,
1371 )?;
1372 Ok(Some(Box::new(iter) as BoxedRecordBatchIterator))
1373 }
1374}
1375
1376#[derive(Debug, Clone)]
1378pub struct BulkPartMeta {
1379 pub num_rows: usize,
1381 pub max_timestamp: i64,
1383 pub min_timestamp: i64,
1385 pub parquet_metadata: Arc<ParquetMetaData>,
1387 pub region_metadata: RegionMetadataRef,
1389 pub num_series: u64,
1391 pub min_sequence: SequenceNumber,
1393 pub max_sequence: u64,
1395}
1396
1397#[derive(Default, Debug)]
1399pub struct BulkPartEncodeMetrics {
1400 pub iter_cost: Duration,
1402 pub write_cost: Duration,
1404 pub raw_size: usize,
1406 pub encoded_size: usize,
1408 pub num_rows: usize,
1410}
1411
1412pub struct BulkPartEncoder {
1413 metadata: RegionMetadataRef,
1414 writer_props: Option<WriterProperties>,
1415}
1416
1417impl BulkPartEncoder {
1418 pub fn new(metadata: RegionMetadataRef, row_group_size: usize) -> Result<BulkPartEncoder> {
1419 Self::new_with_float_field_encoding(metadata, row_group_size, FloatFieldEncoding::default())
1420 }
1421
1422 pub(super) fn new_with_float_field_encoding(
1423 metadata: RegionMetadataRef,
1424 row_group_size: usize,
1425 float_field_encoding: FloatFieldEncoding,
1426 ) -> Result<BulkPartEncoder> {
1427 let json = metadata.to_json().context(InvalidMetadataSnafu)?;
1429 let key_value_meta =
1430 parquet::file::metadata::KeyValue::new(PARQUET_METADATA_KEY.to_string(), json);
1431
1432 let mut props = WriterProperties::builder()
1433 .set_key_value_metadata(Some(vec![key_value_meta]))
1434 .set_write_batch_size(row_group_size)
1435 .set_max_row_group_row_count(Some(row_group_size))
1436 .set_compression(Compression::ZSTD(ZstdLevel::default()))
1437 .set_column_index_truncate_length(COLUMN_INDEX_TRUNCATE_LENGTH)
1438 .set_statistics_truncate_length(None);
1439 props = apply_internal_column_encoding(props, &metadata);
1440 props = apply_float_field_encoding(props, &metadata, float_field_encoding);
1441 props = apply_column_compression(props, &metadata)?;
1442 let writer_props = Some(props.build());
1443
1444 Ok(Self {
1445 metadata,
1446 writer_props,
1447 })
1448 }
1449}
1450
1451impl BulkPartEncoder {
1452 pub fn encode_record_batch_iter(
1454 &self,
1455 iter: BoxedRecordBatchIterator,
1456 arrow_schema: SchemaRef,
1457 min_timestamp: i64,
1458 max_timestamp: i64,
1459 sequence_bounds: RangeInclusive<SequenceNumber>,
1460 metrics: &mut BulkPartEncodeMetrics,
1461 ) -> Result<Option<EncodedBulkPart>> {
1462 let mut buf = Vec::with_capacity(4096);
1463 let mut writer =
1464 ArrowWriter::try_new(&mut buf, arrow_schema.clone(), self.writer_props.clone())
1465 .context(EncodeMemtableSnafu)?;
1466 let mut total_rows = 0;
1467 let mut series_estimator = SeriesEstimator::default();
1468
1469 let mut iter_start = Instant::now();
1471 for batch_result in iter {
1472 metrics.iter_cost += iter_start.elapsed();
1473 let batch = batch_result?;
1474 if batch.num_rows() == 0 {
1475 continue;
1476 }
1477
1478 series_estimator.update_flat(&batch);
1479 metrics.raw_size += record_batch_estimated_size(&batch);
1480 let write_start = Instant::now();
1481 writer.write(&batch).context(EncodeMemtableSnafu)?;
1482 metrics.write_cost += write_start.elapsed();
1483 total_rows += batch.num_rows();
1484 iter_start = Instant::now();
1485 }
1486 metrics.iter_cost += iter_start.elapsed();
1487
1488 if total_rows == 0 {
1489 return Ok(None);
1490 }
1491
1492 let close_start = Instant::now();
1493 let file_metadata = writer.close().context(EncodeMemtableSnafu)?;
1494 metrics.write_cost += close_start.elapsed();
1495 metrics.encoded_size += buf.len();
1496 metrics.num_rows += total_rows;
1497
1498 let buf = Bytes::from(buf);
1499 let parquet_metadata = Arc::new(file_metadata);
1500 let num_series = series_estimator.finish();
1501
1502 Ok(Some(EncodedBulkPart {
1503 data: buf,
1504 metadata: BulkPartMeta {
1505 num_rows: total_rows,
1506 max_timestamp,
1507 min_timestamp,
1508 parquet_metadata,
1509 region_metadata: self.metadata.clone(),
1510 num_series,
1511 min_sequence: *sequence_bounds.start(),
1512 max_sequence: *sequence_bounds.end(),
1513 },
1514 schema: arrow_schema,
1515 }))
1516 }
1517
1518 pub fn encode_part(&self, part: &BulkPart) -> Result<Option<EncodedBulkPart>> {
1520 if part.batch.num_rows() == 0 {
1521 return Ok(None);
1522 }
1523
1524 let mut buf = Vec::with_capacity(4096);
1525 let arrow_schema = part.batch.schema();
1526
1527 let file_metadata = {
1528 let mut writer =
1529 ArrowWriter::try_new(&mut buf, arrow_schema.clone(), self.writer_props.clone())
1530 .context(EncodeMemtableSnafu)?;
1531 writer.write(&part.batch).context(EncodeMemtableSnafu)?;
1532 writer.finish().context(EncodeMemtableSnafu)?
1533 };
1534
1535 let buf = Bytes::from(buf);
1536 let parquet_metadata = Arc::new(file_metadata);
1537
1538 Ok(Some(EncodedBulkPart {
1539 data: buf,
1540 metadata: BulkPartMeta {
1541 num_rows: part.batch.num_rows(),
1542 max_timestamp: part.max_timestamp,
1543 min_timestamp: part.min_timestamp,
1544 parquet_metadata,
1545 region_metadata: self.metadata.clone(),
1546 num_series: part.estimated_series_count() as u64,
1547 max_sequence: part.sequence,
1548 min_sequence: part.min_sequence,
1549 },
1550 schema: arrow_schema,
1551 }))
1552 }
1553}
1554
1555#[derive(Debug, Clone)]
1561struct BatchStats {
1562 num_batches: usize,
1564 first_tag_id: ColumnId,
1566 min_values: ArrayRef,
1568 max_values: ArrayRef,
1570}
1571
1572impl BatchStats {
1573 fn compute(batches: &[RecordBatch], metadata: &RegionMetadata) -> Option<Self> {
1578 let first_tag_id = *metadata.primary_key.first()?;
1583 let first_tag_column = metadata.column_by_id(first_tag_id)?;
1584 let data_type = &first_tag_column.column_schema.data_type;
1585
1586 let converter = build_primary_key_codec_with_fields(
1587 metadata.primary_key_encoding,
1588 [(first_tag_id, SortField::new(data_type.clone()))].into_iter(),
1589 );
1590 let pk_index = primary_key_column_index(batches.first()?.num_columns());
1591
1592 let mut min_builder = data_type.create_mutable_vector(batches.len());
1593 let mut max_builder = data_type.create_mutable_vector(batches.len());
1594
1595 for batch in batches {
1596 match Self::extract_first_tag_bounds(batch, pk_index, &*converter) {
1597 Some((min_val, max_val)) => {
1598 min_builder.push_value_ref(&min_val.as_value_ref());
1599 max_builder.push_value_ref(&max_val.as_value_ref());
1600 }
1601 None => {
1602 min_builder.push_null();
1603 max_builder.push_null();
1604 }
1605 }
1606 }
1607
1608 Some(Self {
1609 num_batches: batches.len(),
1610 first_tag_id,
1611 min_values: min_builder.to_vector().to_arrow_array(),
1612 max_values: max_builder.to_vector().to_arrow_array(),
1613 })
1614 }
1615
1616 fn extract_first_tag_bounds(
1618 batch: &RecordBatch,
1619 pk_index: usize,
1620 converter: &dyn PrimaryKeyCodec,
1621 ) -> Option<(datatypes::value::Value, datatypes::value::Value)> {
1622 if batch.num_rows() == 0 {
1623 return None;
1624 }
1625
1626 let pk_dict = batch
1627 .column(pk_index)
1628 .as_any()
1629 .downcast_ref::<PrimaryKeyArray>()?;
1630 let pk_values = pk_dict.values().as_any().downcast_ref::<BinaryArray>()?;
1631
1632 let keys = pk_dict.keys();
1633 let min_key = keys.value(0);
1634 let max_key = keys.value(batch.num_rows() - 1);
1635 let min_bytes = pk_values.value(min_key as usize);
1636 let max_bytes = pk_values.value(max_key as usize);
1637
1638 Some((
1639 converter.decode_leftmost(min_bytes).ok()??,
1640 converter.decode_leftmost(max_bytes).ok()??,
1641 ))
1642 }
1643}
1644
1645struct BatchPruningStats<'a> {
1650 stats: &'a BatchStats,
1651 metadata: &'a RegionMetadataRef,
1652}
1653
1654impl PruningStatistics for BatchPruningStats<'_> {
1655 fn min_values(&self, column: &Column) -> Option<ArrayRef> {
1656 let col = self.metadata.column_by_name(&column.name)?;
1657 if col.column_id == self.stats.first_tag_id {
1658 Some(self.stats.min_values.clone())
1659 } else {
1660 None
1661 }
1662 }
1663
1664 fn max_values(&self, column: &Column) -> Option<ArrayRef> {
1665 let col = self.metadata.column_by_name(&column.name)?;
1666 if col.column_id == self.stats.first_tag_id {
1667 Some(self.stats.max_values.clone())
1668 } else {
1669 None
1670 }
1671 }
1672
1673 fn num_containers(&self) -> usize {
1674 self.stats.num_batches
1675 }
1676
1677 fn null_counts(&self, _column: &Column) -> Option<ArrayRef> {
1678 None
1679 }
1680
1681 fn row_counts(&self) -> Option<ArrayRef> {
1682 None
1683 }
1684
1685 fn contained(
1686 &self,
1687 _column: &Column,
1688 _values: &std::collections::HashSet<datafusion_common::ScalarValue>,
1689 ) -> Option<BooleanArray> {
1690 None
1691 }
1692}
1693
1694fn predicate_references_column(predicate: &table::predicate::Predicate, column_name: &str) -> bool {
1696 let mut columns = HashSet::new();
1697 for expr in predicate.exprs() {
1698 let _ = expr_to_columns(expr, &mut columns);
1699 }
1700 columns.iter().any(|col| col.name == column_name)
1701}
1702
1703pub(crate) fn should_prune_bulk_part(
1707 batch: &RecordBatch,
1708 context: &BulkIterContext,
1709 metadata: &RegionMetadata,
1710) -> bool {
1711 let predicate = match &context.predicate {
1712 Some(p) => p,
1713 None => return false,
1714 };
1715 let first_tag_id = match metadata.primary_key.first() {
1718 Some(id) => *id,
1719 None => return false,
1720 };
1721 let first_tag_name = &metadata
1723 .column_by_id(first_tag_id)
1724 .unwrap()
1725 .column_schema
1726 .name;
1727 if !predicate_references_column(predicate, first_tag_name) {
1728 return false;
1729 }
1730 let stats = match BatchStats::compute(std::slice::from_ref(batch), metadata) {
1731 Some(s) => s,
1732 None => return false,
1733 };
1734 let region_meta = context.read_format().metadata();
1735 let pruning_stats = BatchPruningStats {
1736 stats: &stats,
1737 metadata: region_meta,
1738 };
1739 let mask = predicate.prune_with_stats(&pruning_stats, region_meta.schema.arrow_schema());
1740 !mask.first().copied().unwrap_or(true)
1741}
1742
1743#[derive(Debug, Clone)]
1749pub struct MultiBulkPart {
1750 batches: SmallVec<[RecordBatch; 4]>,
1752 total_rows: usize,
1754 max_timestamp: i64,
1756 min_timestamp: i64,
1758 min_sequence: SequenceNumber,
1760 max_sequence: SequenceNumber,
1762 series_count: usize,
1764 batch_stats: Option<BatchStats>,
1767}
1768
1769impl MultiBulkPart {
1770 pub fn from_bulk_part(part: BulkPart, metadata: &RegionMetadata) -> Self {
1772 let num_rows = part.num_rows();
1773 let series_count = part.estimated_series_count();
1774 let batch_stats = BatchStats::compute(std::slice::from_ref(&part.batch), metadata);
1775 let mut batches = SmallVec::new();
1776 batches.push(part.batch);
1777
1778 Self {
1779 batches,
1780 total_rows: num_rows,
1781 max_timestamp: part.max_timestamp,
1782 min_timestamp: part.min_timestamp,
1783 min_sequence: part.min_sequence,
1784 max_sequence: part.sequence,
1785 series_count,
1786 batch_stats,
1787 }
1788 }
1789
1790 pub fn new(
1804 batches: Vec<RecordBatch>,
1805 min_timestamp: i64,
1806 max_timestamp: i64,
1807 min_sequence: SequenceNumber,
1808 max_sequence: SequenceNumber,
1809 series_count: usize,
1810 metadata: &RegionMetadata,
1811 ) -> Self {
1812 assert!(!batches.is_empty(), "batches must not be empty");
1813
1814 let total_rows = batches.iter().map(|b| b.num_rows()).sum();
1815 let batch_stats = BatchStats::compute(&batches, metadata);
1816
1817 Self {
1818 batches: SmallVec::from_vec(batches),
1819 total_rows,
1820 max_timestamp,
1821 min_timestamp,
1822 min_sequence,
1823 max_sequence,
1824 series_count,
1825 batch_stats,
1826 }
1827 }
1828
1829 pub fn num_rows(&self) -> usize {
1831 self.total_rows
1832 }
1833
1834 pub(crate) fn schemas(&self) -> impl Iterator<Item = SchemaRef> + '_ {
1835 self.batches.iter().map(|batch| batch.schema())
1836 }
1837
1838 pub fn min_timestamp(&self) -> i64 {
1840 self.min_timestamp
1841 }
1842
1843 pub fn max_timestamp(&self) -> i64 {
1845 self.max_timestamp
1846 }
1847
1848 pub fn min_sequence(&self) -> SequenceNumber {
1850 self.min_sequence
1851 }
1852
1853 pub fn max_sequence(&self) -> SequenceNumber {
1855 self.max_sequence
1856 }
1857
1858 pub fn series_count(&self) -> usize {
1860 self.series_count
1861 }
1862
1863 pub fn num_batches(&self) -> usize {
1865 self.batches.len()
1866 }
1867
1868 pub(crate) fn estimated_size(&self) -> usize {
1870 self.batches.iter().map(record_batch_estimated_size).sum()
1871 }
1872
1873 pub(crate) fn read(
1879 &self,
1880 context: BulkIterContextRef,
1881 sequence: Option<SequenceRange>,
1882 mem_scan_metrics: Option<MemScanMetrics>,
1883 ) -> Result<Option<BoxedRecordBatchIterator>> {
1884 if self.batches.is_empty() {
1885 return Ok(None);
1886 }
1887
1888 let batches_to_read = self.prune_batches(&context);
1889
1890 if batches_to_read.is_empty() {
1891 return Ok(None);
1892 }
1893
1894 let iter = crate::memtable::bulk::part_reader::BulkPartBatchIter::new(
1895 batches_to_read,
1896 context,
1897 sequence,
1898 self.series_count,
1899 mem_scan_metrics,
1900 );
1901 Ok(Some(Box::new(iter) as BoxedRecordBatchIterator))
1902 }
1903
1904 fn prune_batches(&self, context: &BulkIterContextRef) -> Vec<RecordBatch> {
1907 if let Some(stats) = &self.batch_stats
1908 && let Some(predicate) = &context.predicate
1909 {
1910 let region_meta = context.read_format().metadata();
1911 let pruning_stats = BatchPruningStats {
1912 stats,
1913 metadata: region_meta,
1914 };
1915 let mask =
1916 predicate.prune_with_stats(&pruning_stats, region_meta.schema.arrow_schema());
1917 self.batches
1918 .iter()
1919 .zip(mask.iter())
1920 .filter_map(
1921 |(batch, &selected)| {
1922 if selected { Some(batch.clone()) } else { None }
1923 },
1924 )
1925 .collect()
1926 } else {
1927 self.batches.iter().cloned().collect()
1928 }
1929 }
1930
1931 pub fn to_memtable_stats(&self, region_metadata: &RegionMetadataRef) -> MemtableStats {
1933 let ts_type = region_metadata.time_index_type();
1934 let min_ts = ts_type.create_timestamp(self.min_timestamp);
1935 let max_ts = ts_type.create_timestamp(self.max_timestamp);
1936
1937 MemtableStats {
1938 estimated_bytes: self.estimated_size(),
1939 time_range: Some((min_ts, max_ts)),
1940 num_rows: self.num_rows(),
1941 num_ranges: 1,
1942 max_sequence: self.max_sequence,
1943 min_sequence: self.min_sequence,
1944 series_count: self.series_count,
1945 }
1946 }
1947}
1948
1949#[cfg(test)]
1950mod tests {
1951 use api::v1::{Row, WriteHint};
1952 use datafusion_common::ScalarValue;
1953 use datatypes::arrow::array::{
1954 BinaryArray, DictionaryArray, Float64Array, TimestampMillisecondArray,
1955 };
1956 use datatypes::arrow::datatypes::UInt32Type;
1957 use datatypes::prelude::{ConcreteDataType, Value};
1958 use mito_codec::row_converter::build_primary_key_codec;
1959 use rand::rngs::StdRng;
1960 use rand::{Rng, SeedableRng};
1961 use store_api::metadata::RegionMetadataBuilder;
1962 use store_api::storage::consts::ReservedColumnId;
1963 use table::predicate::Predicate;
1964
1965 use super::*;
1966 use crate::memtable::bulk::context::BulkIterContext;
1967 use crate::sst::{FlatSchemaOptions, to_flat_sst_arrow_schema};
1968 use crate::test_util::memtable_util::{build_key_values_with_ts_seq_values, metadata_for_test};
1969
1970 struct MutationInput<'a> {
1971 k0: &'a str,
1972 k1: u32,
1973 timestamps: &'a [i64],
1974 v1: &'a [Option<f64>],
1975 sequence: u64,
1976 }
1977
1978 #[test]
1979 fn test_sort_primary_key_record_batch_with_duplicate_dictionary_values() {
1980 let primary_key = DictionaryArray::try_new(
1983 UInt32Array::from(vec![0, 1, 0, 2, 2]),
1984 Arc::new(BinaryArray::from_vec(vec![
1985 b"series_b".as_slice(),
1986 b"series_a".as_slice(),
1987 b"series_a".as_slice(),
1988 ])),
1989 )
1990 .unwrap();
1991 let timestamps = TimestampMillisecondArray::from(vec![20, 30, 10, 30, 10]);
1992 let sequences = UInt64Array::from(vec![5, 6, 4, 8, 9]);
1993 let row_ids = UInt32Array::from_iter_values(0..5);
1994 let op_types = UInt8Array::from_value(OpType::Put as u8, 5);
1995 let schema = Arc::new(Schema::new(vec![
1996 Field::new("row_id", ArrowDataType::UInt32, false),
1997 Field::new(
1998 "ts",
1999 ArrowDataType::Timestamp(TimeUnit::Millisecond, None),
2000 false,
2001 ),
2002 Field::new(
2003 PRIMARY_KEY_COLUMN_NAME,
2004 ArrowDataType::Dictionary(
2005 Box::new(ArrowDataType::UInt32),
2006 Box::new(ArrowDataType::Binary),
2007 ),
2008 false,
2009 ),
2010 Field::new("__sequence", ArrowDataType::UInt64, false),
2011 Field::new("__op_type", ArrowDataType::UInt8, false),
2012 ]));
2013 let batch = RecordBatch::try_new(
2014 schema,
2015 vec![
2016 Arc::new(row_ids),
2017 Arc::new(timestamps),
2018 Arc::new(primary_key),
2019 Arc::new(sequences),
2020 Arc::new(op_types),
2021 ],
2022 )
2023 .unwrap();
2024
2025 let expected_indices = lexsort_primary_key_indices(&batch).unwrap();
2026 let expected =
2027 datatypes::arrow::compute::take_record_batch(&batch, &expected_indices).unwrap();
2028 let actual = sort_primary_key_record_batch(&batch).unwrap();
2029
2030 let expected_row_ids = expected
2031 .column(0)
2032 .as_any()
2033 .downcast_ref::<UInt32Array>()
2034 .unwrap();
2035 let actual_row_ids = actual
2036 .column(0)
2037 .as_any()
2038 .downcast_ref::<UInt32Array>()
2039 .unwrap();
2040 assert_eq!(expected_row_ids, actual_row_ids);
2041 assert_eq!(actual_row_ids.values(), &[4, 3, 1, 2, 0]);
2042 }
2043
2044 #[test]
2045 fn test_sort_primary_key_record_batch_against_lexsort() {
2046 let mut rng = StdRng::seed_from_u64(0x5eed);
2047 for _ in 0..100 {
2048 let num_rows = rng.random_range(0..128);
2049 let keys = UInt32Array::from_iter_values((0..num_rows).map(|_| rng.random_range(0..8)));
2050 let primary_key = DictionaryArray::try_new(
2051 keys,
2052 Arc::new(BinaryArray::from_vec(vec![
2053 b"d".as_slice(),
2054 b"a".as_slice(),
2055 b"c".as_slice(),
2056 b"b".as_slice(),
2057 b"a".as_slice(),
2058 b"d".as_slice(),
2059 b"b".as_slice(),
2060 b"c".as_slice(),
2061 ])),
2062 )
2063 .unwrap();
2064 let timestamps = TimestampMillisecondArray::from_iter_values(
2065 (0..num_rows).map(|_| rng.random_range(-1000..1000)),
2066 );
2067 let sequences = UInt64Array::from_iter_values(0..num_rows as u64);
2069 let row_ids = UInt32Array::from_iter_values(0..num_rows as u32);
2070 let op_types = UInt8Array::from_value(OpType::Put as u8, num_rows);
2071 let schema = Arc::new(Schema::new(vec![
2072 Field::new("row_id", ArrowDataType::UInt32, false),
2073 Field::new(
2074 "ts",
2075 ArrowDataType::Timestamp(TimeUnit::Millisecond, None),
2076 false,
2077 ),
2078 Field::new(
2079 PRIMARY_KEY_COLUMN_NAME,
2080 ArrowDataType::Dictionary(
2081 Box::new(ArrowDataType::UInt32),
2082 Box::new(ArrowDataType::Binary),
2083 ),
2084 false,
2085 ),
2086 Field::new("__sequence", ArrowDataType::UInt64, false),
2087 Field::new("__op_type", ArrowDataType::UInt8, false),
2088 ]));
2089 let batch = RecordBatch::try_new(
2090 schema,
2091 vec![
2092 Arc::new(row_ids),
2093 Arc::new(timestamps),
2094 Arc::new(primary_key),
2095 Arc::new(sequences),
2096 Arc::new(op_types),
2097 ],
2098 )
2099 .unwrap();
2100
2101 let expected_indices = lexsort_primary_key_indices(&batch).unwrap();
2102 let expected =
2103 datatypes::arrow::compute::take_record_batch(&batch, &expected_indices).unwrap();
2104 let actual = sort_primary_key_record_batch(&batch).unwrap();
2105 assert_eq!(expected.column(0), actual.column(0));
2106 }
2107 }
2108
2109 #[test]
2110 fn test_unordered_part_tracks_estimated_bytes() {
2111 let mut part = UnorderedPart::new();
2112 let bulk_part = BulkPart {
2113 batch: RecordBatch::new_empty(Arc::new(arrow::datatypes::Schema::empty())),
2114 max_timestamp: 0,
2115 min_timestamp: 0,
2116 sequence: 0,
2117 min_sequence: 0,
2118 timestamp_index: 0,
2119 raw_data: None,
2120 };
2121 let estimated_size = bulk_part.estimated_size();
2122
2123 part.push(bulk_part);
2124 assert_eq!(estimated_size, part.estimated_bytes());
2125 part.clear();
2126 assert_eq!(0, part.estimated_bytes());
2127 assert!(part.is_empty());
2128 }
2129
2130 #[test]
2131 fn test_unordered_part_should_accept() {
2132 let mut part = UnorderedPart::new();
2133 part.set_threshold(10);
2134 assert!(part.should_accept(9));
2135 assert!(!part.should_accept(10));
2136 }
2137
2138 fn encode(input: &[MutationInput]) -> EncodedBulkPart {
2139 let metadata = metadata_for_test();
2140 let kvs = input
2141 .iter()
2142 .map(|m| {
2143 build_key_values_with_ts_seq_values(
2144 &metadata,
2145 m.k0.to_string(),
2146 m.k1,
2147 m.timestamps.iter().copied(),
2148 m.v1.iter().copied(),
2149 m.sequence,
2150 )
2151 })
2152 .collect::<Vec<_>>();
2153 let schema = to_flat_sst_arrow_schema(&metadata, &FlatSchemaOptions::default());
2154 let primary_key_codec = build_primary_key_codec(&metadata);
2155 let mut converter = BulkPartConverter::new(&metadata, schema, 64, primary_key_codec, true);
2156 for kv in kvs {
2157 converter.append_key_values(&kv).unwrap();
2158 }
2159 let part = converter.convert().unwrap();
2160 let encoder = BulkPartEncoder::new(metadata, 1024).unwrap();
2161 encoder.encode_part(&part).unwrap().unwrap()
2162 }
2163
2164 #[test]
2165 fn test_write_and_read_part_projection() {
2166 let part = encode(&[
2167 MutationInput {
2168 k0: "a",
2169 k1: 0,
2170 timestamps: &[1],
2171 v1: &[Some(0.1)],
2172 sequence: 0,
2173 },
2174 MutationInput {
2175 k0: "b",
2176 k1: 0,
2177 timestamps: &[1],
2178 v1: &[Some(0.0)],
2179 sequence: 0,
2180 },
2181 MutationInput {
2182 k0: "a",
2183 k1: 0,
2184 timestamps: &[2],
2185 v1: &[Some(0.2)],
2186 sequence: 1,
2187 },
2188 ]);
2189
2190 let projection = &[4u32];
2191 let reader = part
2192 .read(
2193 Arc::new(
2194 BulkIterContext::new(
2195 part.metadata.region_metadata.clone(),
2196 Some(projection.as_slice()),
2197 None,
2198 false,
2199 crate::sst::parquet::DEFAULT_READ_BATCH_SIZE,
2200 )
2201 .unwrap(),
2202 ),
2203 None,
2204 None,
2205 )
2206 .unwrap()
2207 .expect("expect at least one row group");
2208
2209 let mut total_rows_read = 0;
2210 let mut field: Vec<f64> = vec![];
2211 for res in reader {
2212 let batch = res.unwrap();
2213 assert_eq!(5, batch.num_columns());
2214 field.extend_from_slice(
2215 batch
2216 .column(0)
2217 .as_any()
2218 .downcast_ref::<Float64Array>()
2219 .unwrap()
2220 .values(),
2221 );
2222 total_rows_read += batch.num_rows();
2223 }
2224 assert_eq!(3, total_rows_read);
2225 assert_eq!(vec![0.1, 0.2, 0.0], field);
2226 }
2227
2228 fn prepare(key_values: Vec<(&str, u32, (i64, i64), u64)>) -> EncodedBulkPart {
2229 let metadata = metadata_for_test();
2230 let kvs = key_values
2231 .into_iter()
2232 .map(|(k0, k1, (start, end), sequence)| {
2233 let ts = start..end;
2234 let v1 = (start..end).map(|_| None);
2235 build_key_values_with_ts_seq_values(&metadata, k0.to_string(), k1, ts, v1, sequence)
2236 })
2237 .collect::<Vec<_>>();
2238 let schema = to_flat_sst_arrow_schema(&metadata, &FlatSchemaOptions::default());
2239 let primary_key_codec = build_primary_key_codec(&metadata);
2240 let mut converter = BulkPartConverter::new(&metadata, schema, 64, primary_key_codec, true);
2241 for kv in kvs {
2242 converter.append_key_values(&kv).unwrap();
2243 }
2244 let part = converter.convert().unwrap();
2245 let encoder = BulkPartEncoder::new(metadata, 1024).unwrap();
2246 encoder.encode_part(&part).unwrap().unwrap()
2247 }
2248
2249 fn check_prune_row_group(
2250 part: &EncodedBulkPart,
2251 predicate: Option<Predicate>,
2252 expected_rows: usize,
2253 ) {
2254 let context = Arc::new(
2255 BulkIterContext::new(
2256 part.metadata.region_metadata.clone(),
2257 None,
2258 predicate,
2259 false,
2260 crate::sst::parquet::DEFAULT_READ_BATCH_SIZE,
2261 )
2262 .unwrap(),
2263 );
2264 let reader = part
2265 .read(context, None, None)
2266 .unwrap()
2267 .expect("expect at least one row group");
2268 let mut total_rows_read = 0;
2269 for res in reader {
2270 let batch = res.unwrap();
2271 total_rows_read += batch.num_rows();
2272 }
2273 assert_eq!(expected_rows, total_rows_read);
2275 }
2276
2277 #[test]
2278 fn test_prune_row_groups() {
2279 let part = prepare(vec![
2280 ("a", 0, (0, 40), 1),
2281 ("a", 1, (0, 60), 1),
2282 ("b", 0, (0, 100), 2),
2283 ("b", 1, (100, 180), 3),
2284 ("b", 1, (180, 210), 4),
2285 ]);
2286
2287 let context = Arc::new(
2288 BulkIterContext::new(
2289 part.metadata.region_metadata.clone(),
2290 None,
2291 Some(Predicate::new(vec![datafusion_expr::col("ts").eq(
2292 datafusion_expr::lit(ScalarValue::TimestampMillisecond(Some(300), None)),
2293 )])),
2294 false,
2295 crate::sst::parquet::DEFAULT_READ_BATCH_SIZE,
2296 )
2297 .unwrap(),
2298 );
2299 assert!(part.read(context, None, None).unwrap().is_none());
2300
2301 check_prune_row_group(&part, None, 310);
2302
2303 check_prune_row_group(
2304 &part,
2305 Some(Predicate::new(vec![
2306 datafusion_expr::col("k0").eq(datafusion_expr::lit("a")),
2307 datafusion_expr::col("k1").eq(datafusion_expr::lit(0u32)),
2308 ])),
2309 40,
2310 );
2311
2312 check_prune_row_group(
2313 &part,
2314 Some(Predicate::new(vec![
2315 datafusion_expr::col("k0").eq(datafusion_expr::lit("a")),
2316 datafusion_expr::col("k1").eq(datafusion_expr::lit(1u32)),
2317 ])),
2318 60,
2319 );
2320
2321 check_prune_row_group(
2322 &part,
2323 Some(Predicate::new(vec![
2324 datafusion_expr::col("k0").eq(datafusion_expr::lit("a")),
2325 ])),
2326 100,
2327 );
2328
2329 check_prune_row_group(
2330 &part,
2331 Some(Predicate::new(vec![
2332 datafusion_expr::col("k0").eq(datafusion_expr::lit("b")),
2333 datafusion_expr::col("k1").eq(datafusion_expr::lit(0u32)),
2334 ])),
2335 100,
2336 );
2337
2338 check_prune_row_group(
2340 &part,
2341 Some(Predicate::new(vec![
2342 datafusion_expr::col("v0").eq(datafusion_expr::lit(150i64)),
2343 ])),
2344 1,
2345 );
2346 }
2347
2348 #[test]
2349 fn test_bulk_part_converter_append_and_convert() {
2350 let metadata = metadata_for_test();
2351 let capacity = 100;
2352 let primary_key_codec = build_primary_key_codec(&metadata);
2353 let schema = to_flat_sst_arrow_schema(
2354 &metadata,
2355 &FlatSchemaOptions::from_encoding(metadata.primary_key_encoding),
2356 );
2357
2358 let mut converter =
2359 BulkPartConverter::new(&metadata, schema, capacity, primary_key_codec, true);
2360
2361 let key_values1 = build_key_values_with_ts_seq_values(
2362 &metadata,
2363 "key1".to_string(),
2364 1u32,
2365 vec![1000, 2000].into_iter(),
2366 vec![Some(1.0), Some(2.0)].into_iter(),
2367 1,
2368 );
2369
2370 let key_values2 = build_key_values_with_ts_seq_values(
2371 &metadata,
2372 "key2".to_string(),
2373 2u32,
2374 vec![1500].into_iter(),
2375 vec![Some(3.0)].into_iter(),
2376 2,
2377 );
2378
2379 converter.append_key_values(&key_values1).unwrap();
2380 converter.append_key_values(&key_values2).unwrap();
2381
2382 let bulk_part = converter.convert().unwrap();
2383
2384 assert_eq!(bulk_part.num_rows(), 3);
2385 assert_eq!(bulk_part.min_timestamp, 1000);
2386 assert_eq!(bulk_part.max_timestamp, 2000);
2387 assert_eq!(bulk_part.sequence, 2);
2388 assert_eq!(bulk_part.timestamp_index, bulk_part.batch.num_columns() - 4);
2389
2390 let schema = bulk_part.batch.schema();
2393 let field_names: Vec<&str> = schema.fields().iter().map(|f| f.name().as_str()).collect();
2394 assert_eq!(
2395 field_names,
2396 vec![
2397 "k0",
2398 "k1",
2399 "v0",
2400 "v1",
2401 "ts",
2402 "__primary_key",
2403 "__sequence",
2404 "__op_type"
2405 ]
2406 );
2407 }
2408
2409 #[test]
2410 fn test_bulk_part_converter_sorting() {
2411 let metadata = metadata_for_test();
2412 let capacity = 100;
2413 let primary_key_codec = build_primary_key_codec(&metadata);
2414 let schema = to_flat_sst_arrow_schema(
2415 &metadata,
2416 &FlatSchemaOptions::from_encoding(metadata.primary_key_encoding),
2417 );
2418
2419 let mut converter =
2420 BulkPartConverter::new(&metadata, schema, capacity, primary_key_codec, true);
2421
2422 let key_values1 = build_key_values_with_ts_seq_values(
2423 &metadata,
2424 "z_key".to_string(),
2425 3u32,
2426 vec![3000].into_iter(),
2427 vec![Some(3.0)].into_iter(),
2428 3,
2429 );
2430
2431 let key_values2 = build_key_values_with_ts_seq_values(
2432 &metadata,
2433 "a_key".to_string(),
2434 1u32,
2435 vec![1000].into_iter(),
2436 vec![Some(1.0)].into_iter(),
2437 1,
2438 );
2439
2440 let key_values3 = build_key_values_with_ts_seq_values(
2441 &metadata,
2442 "m_key".to_string(),
2443 2u32,
2444 vec![2000].into_iter(),
2445 vec![Some(2.0)].into_iter(),
2446 2,
2447 );
2448
2449 converter.append_key_values(&key_values1).unwrap();
2450 converter.append_key_values(&key_values2).unwrap();
2451 converter.append_key_values(&key_values3).unwrap();
2452
2453 let bulk_part = converter.convert().unwrap();
2454
2455 assert_eq!(bulk_part.num_rows(), 3);
2456
2457 let ts_column = bulk_part.batch.column(bulk_part.timestamp_index);
2458 let seq_column = bulk_part.batch.column(bulk_part.batch.num_columns() - 2);
2459
2460 let ts_array = ts_column
2461 .as_any()
2462 .downcast_ref::<TimestampMillisecondArray>()
2463 .unwrap();
2464 let seq_array = seq_column.as_any().downcast_ref::<UInt64Array>().unwrap();
2465
2466 assert_eq!(ts_array.values(), &[1000, 2000, 3000]);
2467 assert_eq!(seq_array.values(), &[1, 2, 3]);
2468
2469 let schema = bulk_part.batch.schema();
2471 let field_names: Vec<&str> = schema.fields().iter().map(|f| f.name().as_str()).collect();
2472 assert_eq!(
2473 field_names,
2474 vec![
2475 "k0",
2476 "k1",
2477 "v0",
2478 "v1",
2479 "ts",
2480 "__primary_key",
2481 "__sequence",
2482 "__op_type"
2483 ]
2484 );
2485 }
2486
2487 #[test]
2488 fn test_bulk_part_converter_empty() {
2489 let metadata = metadata_for_test();
2490 let capacity = 10;
2491 let primary_key_codec = build_primary_key_codec(&metadata);
2492 let schema = to_flat_sst_arrow_schema(
2493 &metadata,
2494 &FlatSchemaOptions::from_encoding(metadata.primary_key_encoding),
2495 );
2496
2497 let converter =
2498 BulkPartConverter::new(&metadata, schema, capacity, primary_key_codec, true);
2499
2500 let bulk_part = converter.convert().unwrap();
2501
2502 assert_eq!(bulk_part.num_rows(), 0);
2503 assert_eq!(bulk_part.min_timestamp, i64::MAX);
2504 assert_eq!(bulk_part.max_timestamp, i64::MIN);
2505 assert_eq!(bulk_part.sequence, SequenceNumber::MIN);
2506
2507 let schema = bulk_part.batch.schema();
2509 let field_names: Vec<&str> = schema.fields().iter().map(|f| f.name().as_str()).collect();
2510 assert_eq!(
2511 field_names,
2512 vec![
2513 "k0",
2514 "k1",
2515 "v0",
2516 "v1",
2517 "ts",
2518 "__primary_key",
2519 "__sequence",
2520 "__op_type"
2521 ]
2522 );
2523 }
2524
2525 #[test]
2526 fn test_bulk_part_converter_without_primary_key_columns() {
2527 let metadata = metadata_for_test();
2528 let primary_key_codec = build_primary_key_codec(&metadata);
2529 let schema = to_flat_sst_arrow_schema(
2530 &metadata,
2531 &FlatSchemaOptions {
2532 raw_pk_columns: false,
2533 string_pk_use_dict: true,
2534 ..Default::default()
2535 },
2536 );
2537
2538 let capacity = 100;
2539 let mut converter =
2540 BulkPartConverter::new(&metadata, schema, capacity, primary_key_codec, false);
2541
2542 let key_values1 = build_key_values_with_ts_seq_values(
2543 &metadata,
2544 "key1".to_string(),
2545 1u32,
2546 vec![1000, 2000].into_iter(),
2547 vec![Some(1.0), Some(2.0)].into_iter(),
2548 1,
2549 );
2550
2551 let key_values2 = build_key_values_with_ts_seq_values(
2552 &metadata,
2553 "key2".to_string(),
2554 2u32,
2555 vec![1500].into_iter(),
2556 vec![Some(3.0)].into_iter(),
2557 2,
2558 );
2559
2560 converter.append_key_values(&key_values1).unwrap();
2561 converter.append_key_values(&key_values2).unwrap();
2562
2563 let bulk_part = converter.convert().unwrap();
2564
2565 assert_eq!(bulk_part.num_rows(), 3);
2566 assert_eq!(bulk_part.min_timestamp, 1000);
2567 assert_eq!(bulk_part.max_timestamp, 2000);
2568 assert_eq!(bulk_part.sequence, 2);
2569 assert_eq!(bulk_part.timestamp_index, bulk_part.batch.num_columns() - 4);
2570
2571 let schema = bulk_part.batch.schema();
2573 let field_names: Vec<&str> = schema.fields().iter().map(|f| f.name().as_str()).collect();
2574 assert_eq!(
2575 field_names,
2576 vec!["v0", "v1", "ts", "__primary_key", "__sequence", "__op_type"]
2577 );
2578 }
2579
2580 #[allow(clippy::too_many_arguments)]
2581 fn build_key_values_with_sparse_encoding(
2582 metadata: &RegionMetadataRef,
2583 table_id: u32,
2584 tsid: u64,
2585 k0: String,
2586 k1: String,
2587 timestamps: impl Iterator<Item = i64>,
2588 values: impl Iterator<Item = Option<f64>>,
2589 sequence: SequenceNumber,
2590 ) -> KeyValues {
2591 let pk_values = vec![
2593 (ReservedColumnId::table_id(), Value::UInt32(table_id)),
2594 (ReservedColumnId::tsid(), Value::UInt64(tsid)),
2595 (0, Value::String(k0.clone().into())),
2596 (1, Value::String(k1.clone().into())),
2597 ];
2598 let mut encoded_key = Vec::new();
2599 mito_codec::row_converter::SparsePrimaryKeyCodec::schemaless()
2600 .encode_values(&pk_values, &mut encoded_key)
2601 .unwrap();
2602 assert!(!encoded_key.is_empty());
2603
2604 let column_schema = vec![
2606 api::v1::ColumnSchema {
2607 column_name: PRIMARY_KEY_COLUMN_NAME.to_string(),
2608 datatype: api::helper::ColumnDataTypeWrapper::try_from(
2609 ConcreteDataType::binary_datatype(),
2610 )
2611 .unwrap()
2612 .datatype() as i32,
2613 semantic_type: api::v1::SemanticType::Tag as i32,
2614 ..Default::default()
2615 },
2616 api::v1::ColumnSchema {
2617 column_name: "ts".to_string(),
2618 datatype: api::helper::ColumnDataTypeWrapper::try_from(
2619 ConcreteDataType::timestamp_millisecond_datatype(),
2620 )
2621 .unwrap()
2622 .datatype() as i32,
2623 semantic_type: api::v1::SemanticType::Timestamp as i32,
2624 ..Default::default()
2625 },
2626 api::v1::ColumnSchema {
2627 column_name: "v0".to_string(),
2628 datatype: api::helper::ColumnDataTypeWrapper::try_from(
2629 ConcreteDataType::int64_datatype(),
2630 )
2631 .unwrap()
2632 .datatype() as i32,
2633 semantic_type: api::v1::SemanticType::Field as i32,
2634 ..Default::default()
2635 },
2636 api::v1::ColumnSchema {
2637 column_name: "v1".to_string(),
2638 datatype: api::helper::ColumnDataTypeWrapper::try_from(
2639 ConcreteDataType::float64_datatype(),
2640 )
2641 .unwrap()
2642 .datatype() as i32,
2643 semantic_type: api::v1::SemanticType::Field as i32,
2644 ..Default::default()
2645 },
2646 ];
2647
2648 let rows = timestamps
2649 .zip(values)
2650 .map(|(ts, v)| Row {
2651 values: vec![
2652 api::v1::Value {
2653 value_data: Some(api::v1::value::ValueData::BinaryValue(
2654 encoded_key.clone(),
2655 )),
2656 },
2657 api::v1::Value {
2658 value_data: Some(api::v1::value::ValueData::TimestampMillisecondValue(ts)),
2659 },
2660 api::v1::Value {
2661 value_data: Some(api::v1::value::ValueData::I64Value(ts)),
2662 },
2663 api::v1::Value {
2664 value_data: v.map(api::v1::value::ValueData::F64Value),
2665 },
2666 ],
2667 })
2668 .collect();
2669
2670 let mutation = api::v1::Mutation {
2671 op_type: 1,
2672 sequence,
2673 rows: Some(api::v1::Rows {
2674 schema: column_schema,
2675 rows,
2676 }),
2677 write_hint: Some(WriteHint {
2678 primary_key_encoding: api::v1::PrimaryKeyEncoding::Sparse.into(),
2679 }),
2680 };
2681 KeyValues::new(metadata.as_ref(), mutation).unwrap()
2682 }
2683
2684 fn sparse_metadata_for_test() -> RegionMetadataRef {
2685 let mut metadata = metadata_for_test().as_ref().clone();
2686 metadata.column_metadatas[1].column_schema.data_type = ConcreteDataType::string_datatype();
2687 let mut builder = RegionMetadataBuilder::from_existing(metadata);
2688 let reserved = crate::test_util::sst_util::sst_region_metadata_with_encoding(
2689 PrimaryKeyEncoding::Sparse,
2690 );
2691 for column in reserved.column_metadatas.into_iter().take(2) {
2692 builder.push_column_metadata(column);
2693 }
2694 builder
2695 .primary_key(vec![
2696 ReservedColumnId::table_id(),
2697 ReservedColumnId::tsid(),
2698 0,
2699 1,
2700 ])
2701 .primary_key_encoding(PrimaryKeyEncoding::Sparse);
2702 Arc::new(builder.build().unwrap())
2703 }
2704
2705 #[test]
2706 fn test_bulk_part_converter_sparse_primary_key_encoding() {
2707 let metadata = sparse_metadata_for_test();
2708
2709 let primary_key_codec = build_primary_key_codec(&metadata);
2710 let schema = to_flat_sst_arrow_schema(
2711 &metadata,
2712 &FlatSchemaOptions::from_encoding(metadata.primary_key_encoding),
2713 );
2714
2715 assert_eq!(metadata.primary_key_encoding, PrimaryKeyEncoding::Sparse);
2716 assert_eq!(primary_key_codec.encoding(), PrimaryKeyEncoding::Sparse);
2717
2718 let capacity = 100;
2719 let mut converter = BulkPartConverter::new(
2720 &metadata,
2721 schema.clone(),
2722 capacity,
2723 primary_key_codec.clone(),
2724 true,
2725 );
2726
2727 let key_values1 = build_key_values_with_sparse_encoding(
2728 &metadata,
2729 2048u32, 100u64, "key11".to_string(),
2732 "key21".to_string(),
2733 vec![1000, 2000].into_iter(),
2734 vec![Some(1.0), Some(2.0)].into_iter(),
2735 1,
2736 );
2737
2738 let mut key_values2 = build_key_values_with_sparse_encoding(
2739 &metadata,
2740 4096u32, 200u64, "key12".to_string(),
2743 "key22".to_string(),
2744 vec![1500].into_iter(),
2745 vec![Some(3.0)].into_iter(),
2746 2,
2747 );
2748 key_values2.mutation.op_type = OpType::Delete as i32;
2749
2750 let mut raw_mutation = key_values1.mutation.clone();
2751 raw_mutation.write_hint = None;
2752 let rows = raw_mutation.rows.as_mut().unwrap();
2753 rows.schema.splice(
2754 0..1,
2755 ["k0", "k1"].map(|name| api::v1::ColumnSchema {
2756 column_name: name.to_string(),
2757 datatype: api::v1::ColumnDataType::String as i32,
2758 semantic_type: SemanticType::Tag as i32,
2759 ..Default::default()
2760 }),
2761 );
2762 for row in &mut rows.rows {
2763 row.values.splice(
2764 0..1,
2765 ["raw🦀", ""]
2766 .map(|value| api::v1::value::ValueData::StringValue(value.to_string()).into()),
2767 );
2768 }
2769 rows.schema.extend(
2770 [
2771 ("__table_id", api::v1::ColumnDataType::Uint32),
2772 ("__tsid", api::v1::ColumnDataType::Uint64),
2773 ]
2774 .map(|(name, datatype)| api::v1::ColumnSchema {
2775 column_name: name.into(),
2776 datatype: datatype as i32,
2777 semantic_type: SemanticType::Tag as i32,
2778 ..Default::default()
2779 }),
2780 );
2781 for row in &mut rows.rows {
2782 row.values.extend([
2783 api::v1::value::ValueData::U32Value(2048).into(),
2784 api::v1::value::ValueData::U64Value(100).into(),
2785 ]);
2786 }
2787 let raw = KeyValues::new(&metadata, raw_mutation).unwrap();
2788 let mut raw_converter = BulkPartConverter::new(
2789 &metadata,
2790 schema.clone(),
2791 capacity,
2792 primary_key_codec.clone(),
2793 true,
2794 );
2795 raw_converter.append_key_values(&raw).unwrap();
2796 let raw_part = raw_converter.convert().unwrap();
2797 assert_eq!(2, raw_part.num_rows());
2798 let keys = arrow::compute::cast(
2799 raw_part
2800 .batch
2801 .column_by_name(PRIMARY_KEY_COLUMN_NAME)
2802 .unwrap(),
2803 &ArrowDataType::Binary,
2804 )
2805 .unwrap();
2806 let keys = keys.as_any().downcast_ref::<BinaryArray>().unwrap();
2807 for key in keys.iter().flatten() {
2808 let decoded = primary_key_codec.decode(key).unwrap().into_sparse();
2809 assert_eq!(
2810 Some(&Value::UInt32(2048)),
2811 decoded.get(&ReservedColumnId::table_id())
2812 );
2813 assert_eq!(
2814 Some(&Value::UInt64(100)),
2815 decoded.get(&ReservedColumnId::tsid())
2816 );
2817 assert_eq!(Some(&Value::from("raw🦀")), decoded.get(&0));
2818 assert_eq!(Some(&Value::from("")), decoded.get(&1));
2819 }
2820
2821 for op_type in [OpType::Put, OpType::Delete] {
2822 let mut mutation = key_values1.mutation.clone();
2823 mutation.op_type = op_type as i32;
2824 mutation.rows.as_mut().unwrap().rows[1].values[0].value_data = Some(
2825 api::v1::value::ValueData::BinaryValue(b"truncated".to_vec()),
2826 );
2827 let invalid = KeyValues::new(&metadata, mutation).unwrap();
2828 for batches in [vec![&invalid], vec![&raw, &invalid], vec![&invalid, &raw]] {
2830 let mut invalid_converter = BulkPartConverter::new(
2831 &metadata,
2832 schema.clone(),
2833 capacity,
2834 primary_key_codec.clone(),
2835 true,
2836 );
2837 for batch in batches {
2838 invalid_converter.append_key_values(batch).unwrap();
2839 }
2840 assert!(invalid_converter.convert().is_err());
2841 }
2842 }
2843
2844 converter.append_key_values(&key_values1).unwrap();
2845 converter.append_key_values(&key_values2).unwrap();
2846
2847 let bulk_part = converter.convert().unwrap();
2848
2849 assert_eq!(bulk_part.num_rows(), 3);
2850 assert_eq!(bulk_part.min_timestamp, 1000);
2851 assert_eq!(bulk_part.max_timestamp, 2000);
2852 assert_eq!(bulk_part.sequence, 2);
2853 assert_eq!(bulk_part.timestamp_index, bulk_part.batch.num_columns() - 4);
2854 assert_eq!(
2855 bulk_part
2856 .batch
2857 .column_by_name("__sequence")
2858 .unwrap()
2859 .as_ref(),
2860 &UInt64Array::from(vec![1, 2, 2]) as &dyn Array,
2861 );
2862 assert_eq!(
2863 bulk_part
2864 .batch
2865 .column_by_name("__op_type")
2866 .unwrap()
2867 .as_ref(),
2868 &UInt8Array::from(vec![
2869 OpType::Put as u8,
2870 OpType::Put as u8,
2871 OpType::Delete as u8
2872 ]) as &dyn Array,
2873 );
2874
2875 let schema = bulk_part.batch.schema();
2879 let field_names: Vec<&str> = schema.fields().iter().map(|f| f.name().as_str()).collect();
2880 assert_eq!(
2881 field_names,
2882 vec!["v0", "v1", "ts", "__primary_key", "__sequence", "__op_type"]
2883 );
2884
2885 let primary_key_column = bulk_part.batch.column_by_name("__primary_key").unwrap();
2887 let dict_array = primary_key_column
2888 .as_any()
2889 .downcast_ref::<DictionaryArray<UInt32Type>>()
2890 .unwrap();
2891
2892 assert!(!dict_array.is_empty());
2894 assert_eq!(dict_array.len(), 3); let values = dict_array
2898 .values()
2899 .as_any()
2900 .downcast_ref::<BinaryArray>()
2901 .unwrap();
2902 for i in 0..values.len() {
2903 assert!(
2904 !values.value(i).is_empty(),
2905 "Encoded primary key should not be empty"
2906 );
2907 }
2908 }
2909
2910 #[test]
2911 fn test_convert_bulk_part_empty() {
2912 let metadata = metadata_for_test();
2913 let schema = to_flat_sst_arrow_schema(
2914 &metadata,
2915 &FlatSchemaOptions::from_encoding(metadata.primary_key_encoding),
2916 );
2917 let primary_key_codec = build_primary_key_codec(&metadata);
2918
2919 let empty_batch = RecordBatch::new_empty(schema.clone());
2921 let empty_part = BulkPart {
2922 batch: empty_batch,
2923 max_timestamp: 0,
2924 min_timestamp: 0,
2925 sequence: 0,
2926 min_sequence: 0,
2927 timestamp_index: 0,
2928 raw_data: None,
2929 };
2930
2931 let result =
2932 convert_bulk_part(empty_part, &metadata, primary_key_codec, schema, true).unwrap();
2933 assert!(result.is_none());
2934 }
2935
2936 #[test]
2937 fn test_convert_bulk_part_dense_with_pk_columns() {
2938 let metadata = metadata_for_test();
2939 let primary_key_codec = build_primary_key_codec(&metadata);
2940
2941 let k0_array = Arc::new(arrow::array::StringArray::from(vec![
2942 "key1", "key2", "key1",
2943 ]));
2944 let k1_array = Arc::new(arrow::array::UInt32Array::from(vec![1, 2, 1]));
2945 let v0_array = Arc::new(arrow::array::Int64Array::from(vec![100, 200, 300]));
2946 let v1_array = Arc::new(arrow::array::Float64Array::from(vec![1.0, 2.0, 3.0]));
2947 let ts_array = Arc::new(TimestampMillisecondArray::from(vec![1000, 2000, 1500]));
2948
2949 let input_schema = Arc::new(Schema::new(vec![
2950 Field::new("k0", ArrowDataType::Utf8, false),
2951 Field::new("k1", ArrowDataType::UInt32, false),
2952 Field::new("v0", ArrowDataType::Int64, true),
2953 Field::new("v1", ArrowDataType::Float64, true),
2954 Field::new(
2955 "ts",
2956 ArrowDataType::Timestamp(arrow::datatypes::TimeUnit::Millisecond, None),
2957 false,
2958 ),
2959 ]));
2960
2961 let input_batch = RecordBatch::try_new(
2962 input_schema,
2963 vec![k0_array, k1_array, v0_array, v1_array, ts_array],
2964 )
2965 .unwrap();
2966
2967 let part = BulkPart {
2968 batch: input_batch,
2969 max_timestamp: 2000,
2970 min_timestamp: 1000,
2971 sequence: 5,
2972 min_sequence: 5,
2973 timestamp_index: 4,
2974 raw_data: None,
2975 };
2976
2977 let output_schema = to_flat_sst_arrow_schema(
2978 &metadata,
2979 &FlatSchemaOptions::from_encoding(metadata.primary_key_encoding),
2980 );
2981
2982 let result = convert_bulk_part(
2983 part,
2984 &metadata,
2985 primary_key_codec,
2986 output_schema,
2987 true, )
2989 .unwrap();
2990
2991 let converted = result.unwrap();
2992
2993 assert_eq!(converted.num_rows(), 3);
2994 assert_eq!(converted.max_timestamp, 2000);
2995 assert_eq!(converted.min_timestamp, 1000);
2996 assert_eq!(converted.sequence, 5);
2997 assert_eq!(converted.min_sequence, 5);
2998
2999 let schema = converted.batch.schema();
3000 let field_names: Vec<&str> = schema.fields().iter().map(|f| f.name().as_str()).collect();
3001 assert_eq!(
3002 field_names,
3003 vec![
3004 "k0",
3005 "k1",
3006 "v0",
3007 "v1",
3008 "ts",
3009 "__primary_key",
3010 "__sequence",
3011 "__op_type"
3012 ]
3013 );
3014
3015 let k0_col = converted.batch.column_by_name("k0").unwrap();
3016 assert!(matches!(
3017 k0_col.data_type(),
3018 ArrowDataType::Dictionary(_, _)
3019 ));
3020
3021 let pk_col = converted.batch.column_by_name("__primary_key").unwrap();
3022 let dict_array = pk_col
3023 .as_any()
3024 .downcast_ref::<DictionaryArray<UInt32Type>>()
3025 .unwrap();
3026 let keys = dict_array.keys();
3027
3028 assert_eq!(keys.len(), 3);
3029 }
3030
3031 #[test]
3032 fn test_convert_bulk_part_dense_without_pk_columns() {
3033 let metadata = metadata_for_test();
3034 let primary_key_codec = build_primary_key_codec(&metadata);
3035
3036 let k0_array = Arc::new(arrow::array::StringArray::from(vec!["key1", "key2"]));
3038 let k1_array = Arc::new(arrow::array::UInt32Array::from(vec![1, 2]));
3039 let v0_array = Arc::new(arrow::array::Int64Array::from(vec![100, 200]));
3040 let v1_array = Arc::new(arrow::array::Float64Array::from(vec![1.0, 2.0]));
3041 let ts_array = Arc::new(TimestampMillisecondArray::from(vec![1000, 2000]));
3042
3043 let input_schema = Arc::new(Schema::new(vec![
3044 Field::new("k0", ArrowDataType::Utf8, false),
3045 Field::new("k1", ArrowDataType::UInt32, false),
3046 Field::new("v0", ArrowDataType::Int64, true),
3047 Field::new("v1", ArrowDataType::Float64, true),
3048 Field::new(
3049 "ts",
3050 ArrowDataType::Timestamp(arrow::datatypes::TimeUnit::Millisecond, None),
3051 false,
3052 ),
3053 ]));
3054
3055 let input_batch = RecordBatch::try_new(
3056 input_schema,
3057 vec![k0_array, k1_array, v0_array, v1_array, ts_array],
3058 )
3059 .unwrap();
3060
3061 let part = BulkPart {
3062 batch: input_batch,
3063 max_timestamp: 2000,
3064 min_timestamp: 1000,
3065 sequence: 3,
3066 min_sequence: 3,
3067 timestamp_index: 4,
3068 raw_data: None,
3069 };
3070
3071 let output_schema = to_flat_sst_arrow_schema(
3072 &metadata,
3073 &FlatSchemaOptions {
3074 raw_pk_columns: false,
3075 string_pk_use_dict: true,
3076 ..Default::default()
3077 },
3078 );
3079
3080 let result = convert_bulk_part(
3081 part,
3082 &metadata,
3083 primary_key_codec,
3084 output_schema,
3085 false, )
3087 .unwrap();
3088
3089 let converted = result.unwrap();
3090
3091 assert_eq!(converted.num_rows(), 2);
3092 assert_eq!(converted.max_timestamp, 2000);
3093 assert_eq!(converted.min_timestamp, 1000);
3094 assert_eq!(converted.sequence, 3);
3095
3096 let schema = converted.batch.schema();
3098 let field_names: Vec<&str> = schema.fields().iter().map(|f| f.name().as_str()).collect();
3099 assert_eq!(
3100 field_names,
3101 vec!["v0", "v1", "ts", "__primary_key", "__sequence", "__op_type"]
3102 );
3103
3104 let pk_col = converted.batch.column_by_name("__primary_key").unwrap();
3106 assert!(matches!(
3107 pk_col.data_type(),
3108 ArrowDataType::Dictionary(_, _)
3109 ));
3110 }
3111
3112 #[test]
3113 fn test_convert_bulk_part_sparse_encoding() {
3114 let metadata = sparse_metadata_for_test();
3115
3116 let primary_key_codec = build_primary_key_codec(&metadata);
3117
3118 let keys = (1..=2)
3120 .map(|tsid| {
3121 let mut key = Vec::new();
3122 mito_codec::row_converter::SparsePrimaryKeyCodec::schemaless()
3123 .encode_internal(123, tsid, &mut key)
3124 .unwrap();
3125 key
3126 })
3127 .collect::<Vec<_>>();
3128 let pk_array = Arc::new(BinaryArray::from_iter_values(&keys));
3129 let v0_array = Arc::new(arrow::array::Int64Array::from(vec![100, 200]));
3130 let v1_array = Arc::new(arrow::array::Float64Array::from(vec![1.0, 2.0]));
3131 let ts_array = Arc::new(TimestampMillisecondArray::from(vec![1000, 2000]));
3132
3133 let input_schema = Arc::new(Schema::new(vec![
3134 Field::new("__primary_key", ArrowDataType::Binary, false),
3135 Field::new("v0", ArrowDataType::Int64, true),
3136 Field::new("v1", ArrowDataType::Float64, true),
3137 Field::new(
3138 "ts",
3139 ArrowDataType::Timestamp(arrow::datatypes::TimeUnit::Millisecond, None),
3140 false,
3141 ),
3142 ]));
3143
3144 let input_batch =
3145 RecordBatch::try_new(input_schema, vec![pk_array, v0_array, v1_array, ts_array])
3146 .unwrap();
3147
3148 let part = BulkPart {
3149 batch: input_batch,
3150 max_timestamp: 2000,
3151 min_timestamp: 1000,
3152 sequence: 7,
3153 min_sequence: 7,
3154 timestamp_index: 3,
3155 raw_data: None,
3156 };
3157
3158 let output_schema = to_flat_sst_arrow_schema(
3159 &metadata,
3160 &FlatSchemaOptions::from_encoding(metadata.primary_key_encoding),
3161 );
3162
3163 let dictionary_values = Arc::new(BinaryArray::from(vec![
3164 Some(keys[0].as_slice()),
3165 Some(keys[1].as_slice()),
3166 Some(b"truncated".as_slice()),
3167 None,
3168 Some(keys[0].as_slice()),
3169 ]));
3170 let with_keys = |keys: ArrayRef| {
3171 let mut input = part.clone();
3172 let mut fields = input.batch.schema().fields().to_vec();
3173 fields[0] = Arc::new(Field::new(
3174 PRIMARY_KEY_COLUMN_NAME,
3175 keys.data_type().clone(),
3176 true,
3177 ));
3178 let mut columns = input.batch.columns().to_vec();
3179 columns[0] = keys;
3180 input.batch = RecordBatch::try_new(Arc::new(Schema::new(fields)), columns).unwrap();
3181 input
3182 };
3183 let sliced = DictionaryArray::try_new(
3185 UInt32Array::from(vec![2, 0, 1, 3]),
3186 dictionary_values.clone(),
3187 )
3188 .unwrap()
3189 .slice(1, 2);
3190 for key_type in [
3191 ArrowDataType::Int8,
3192 ArrowDataType::Int16,
3193 ArrowDataType::Int32,
3194 ArrowDataType::Int64,
3195 ArrowDataType::UInt8,
3196 ArrowDataType::UInt16,
3197 ArrowDataType::UInt32,
3198 ArrowDataType::UInt64,
3199 ] {
3200 let keys = arrow::compute::cast(
3201 &sliced,
3202 &ArrowDataType::Dictionary(Box::new(key_type), Box::new(ArrowDataType::Binary)),
3203 )
3204 .unwrap();
3205 let converted = convert_bulk_part(
3206 with_keys(keys),
3207 &metadata,
3208 primary_key_codec.clone(),
3209 output_schema.clone(),
3210 true,
3211 )
3212 .unwrap()
3213 .unwrap();
3214 let keys = arrow::compute::cast(
3215 converted
3216 .batch
3217 .column_by_name(PRIMARY_KEY_COLUMN_NAME)
3218 .unwrap(),
3219 &ArrowDataType::Binary,
3220 )
3221 .unwrap();
3222 assert_eq!(keys.as_ref(), part.batch.column(0).as_ref());
3223 for (name, index) in [("v0", 1), ("v1", 2), ("ts", 3)] {
3224 assert_eq!(
3225 converted.batch.column_by_name(name).unwrap().as_ref(),
3226 part.batch.column(index).as_ref()
3227 );
3228 }
3229 assert_eq!(
3230 converted
3231 .batch
3232 .column_by_name("__sequence")
3233 .unwrap()
3234 .as_ref(),
3235 &UInt64Array::from(vec![7, 7]) as &dyn Array
3236 );
3237 assert_eq!(
3238 converted
3239 .batch
3240 .column_by_name("__op_type")
3241 .unwrap()
3242 .as_ref(),
3243 &UInt8Array::from(vec![OpType::Put as u8; 2]) as &dyn Array
3244 );
3245 }
3246 let repeated =
3248 DictionaryArray::try_new(UInt32Array::from(vec![0, 4]), dictionary_values.clone())
3249 .unwrap();
3250 assert_eq!(
3251 convert_bulk_part(
3252 with_keys(Arc::new(repeated)),
3253 &metadata,
3254 primary_key_codec.clone(),
3255 output_schema.clone(),
3256 true
3257 )
3258 .unwrap()
3259 .unwrap()
3260 .num_rows(),
3261 2
3262 );
3263 for indices in [
3264 vec![Some(0), Some(2)],
3265 vec![Some(0), Some(3)],
3266 vec![Some(0), None],
3267 ] {
3268 let dictionary =
3269 DictionaryArray::try_new(UInt32Array::from(indices), dictionary_values.clone())
3270 .unwrap();
3271 assert!(
3272 convert_bulk_part(
3273 with_keys(Arc::new(dictionary)),
3274 &metadata,
3275 primary_key_codec.clone(),
3276 output_schema.clone(),
3277 true
3278 )
3279 .is_err()
3280 );
3281 }
3282
3283 for invalid_keys in [
3284 Arc::new(arrow::array::StringArray::from(vec!["invalid"; 2])) as ArrayRef,
3285 Arc::new(BinaryArray::from(vec![None::<&[u8]>; 2])),
3286 Arc::new(BinaryArray::from_iter_values([b"truncated"; 2])),
3287 ] {
3288 let mut invalid = part.clone();
3289 let mut fields = invalid.batch.schema().fields().to_vec();
3290 fields[0] = Arc::new(Field::new(
3291 PRIMARY_KEY_COLUMN_NAME,
3292 invalid_keys.data_type().clone(),
3293 true,
3294 ));
3295 let mut columns = invalid.batch.columns().to_vec();
3296 columns[0] = invalid_keys;
3297 invalid.batch = RecordBatch::try_new(Arc::new(Schema::new(fields)), columns).unwrap();
3298 assert!(
3299 convert_bulk_part(
3300 invalid,
3301 &metadata,
3302 primary_key_codec.clone(),
3303 output_schema.clone(),
3304 true,
3305 )
3306 .is_err()
3307 );
3308 }
3309
3310 let result = convert_bulk_part(
3311 part,
3312 &metadata,
3313 primary_key_codec,
3314 output_schema,
3315 true, )
3317 .unwrap();
3318
3319 let converted = result.unwrap();
3320
3321 assert_eq!(converted.num_rows(), 2);
3322 assert_eq!(converted.max_timestamp, 2000);
3323 assert_eq!(converted.min_timestamp, 1000);
3324 assert_eq!(converted.sequence, 7);
3325
3326 let schema = converted.batch.schema();
3328 let field_names: Vec<&str> = schema.fields().iter().map(|f| f.name().as_str()).collect();
3329 assert_eq!(
3330 field_names,
3331 vec!["v0", "v1", "ts", "__primary_key", "__sequence", "__op_type"]
3332 );
3333
3334 let pk_col = converted.batch.column_by_name("__primary_key").unwrap();
3336 assert!(matches!(
3337 pk_col.data_type(),
3338 ArrowDataType::Dictionary(_, _)
3339 ));
3340 }
3341
3342 #[test]
3343 fn test_convert_bulk_part_sorting_with_multiple_series() {
3344 let metadata = metadata_for_test();
3345 let primary_key_codec = build_primary_key_codec(&metadata);
3346
3347 let k0_array = Arc::new(arrow::array::StringArray::from(vec![
3349 "series_b", "series_a", "series_b", "series_a",
3350 ]));
3351 let k1_array = Arc::new(arrow::array::UInt32Array::from(vec![2, 1, 2, 1]));
3352 let v0_array = Arc::new(arrow::array::Int64Array::from(vec![200, 100, 400, 300]));
3353 let v1_array = Arc::new(arrow::array::Float64Array::from(vec![2.0, 1.0, 4.0, 3.0]));
3354 let ts_array = Arc::new(TimestampMillisecondArray::from(vec![
3355 2000, 1000, 4000, 3000,
3356 ]));
3357
3358 let input_schema = Arc::new(Schema::new(vec![
3359 Field::new("k0", ArrowDataType::Utf8, false),
3360 Field::new("k1", ArrowDataType::UInt32, false),
3361 Field::new("v0", ArrowDataType::Int64, true),
3362 Field::new("v1", ArrowDataType::Float64, true),
3363 Field::new(
3364 "ts",
3365 ArrowDataType::Timestamp(arrow::datatypes::TimeUnit::Millisecond, None),
3366 false,
3367 ),
3368 ]));
3369
3370 let input_batch = RecordBatch::try_new(
3371 input_schema,
3372 vec![k0_array, k1_array, v0_array, v1_array, ts_array],
3373 )
3374 .unwrap();
3375
3376 let part = BulkPart {
3377 batch: input_batch,
3378 max_timestamp: 4000,
3379 min_timestamp: 1000,
3380 sequence: 10,
3381 min_sequence: 10,
3382 timestamp_index: 4,
3383 raw_data: None,
3384 };
3385
3386 let output_schema = to_flat_sst_arrow_schema(
3387 &metadata,
3388 &FlatSchemaOptions::from_encoding(metadata.primary_key_encoding),
3389 );
3390
3391 let result =
3392 convert_bulk_part(part, &metadata, primary_key_codec, output_schema, true).unwrap();
3393
3394 let converted = result.unwrap();
3395
3396 assert_eq!(converted.num_rows(), 4);
3397
3398 let ts_col = converted.batch.column(converted.timestamp_index);
3400 let ts_array = ts_col
3401 .as_any()
3402 .downcast_ref::<TimestampMillisecondArray>()
3403 .unwrap();
3404
3405 let timestamps: Vec<i64> = ts_array.values().to_vec();
3409 assert_eq!(timestamps, vec![1000, 3000, 2000, 4000]);
3410 }
3411
3412 fn build_converted_bulk_part(inputs: &[MutationInput]) -> BulkPart {
3414 let metadata = metadata_for_test();
3415 let kvs = inputs
3416 .iter()
3417 .map(|m| {
3418 build_key_values_with_ts_seq_values(
3419 &metadata,
3420 m.k0.to_string(),
3421 m.k1,
3422 m.timestamps.iter().copied(),
3423 m.v1.iter().copied(),
3424 m.sequence,
3425 )
3426 })
3427 .collect::<Vec<_>>();
3428 let schema = to_flat_sst_arrow_schema(&metadata, &FlatSchemaOptions::default());
3429 let primary_key_codec = build_primary_key_codec(&metadata);
3430 let mut converter = BulkPartConverter::new(&metadata, schema, 64, primary_key_codec, true);
3431 for kv in kvs {
3432 converter.append_key_values(&kv).unwrap();
3433 }
3434 converter.convert().unwrap()
3435 }
3436
3437 #[test]
3438 fn test_min_sequence_survives_slicing_and_unordered_part_reuse() {
3439 let metadata = metadata_for_test();
3440 let make_part = |sequence| {
3441 build_converted_bulk_part(&[MutationInput {
3442 k0: "a",
3443 k1: 0,
3444 timestamps: &[2000, 1000],
3445 v1: &[Some(1.0), Some(2.0)],
3446 sequence,
3447 }])
3448 };
3449 let original = make_part(100);
3450 let sliced = crate::memtable::time_partition::filter_record_batch(&original, 1000, 1001)
3451 .unwrap()
3452 .unwrap();
3453 assert_eq!(1, sliced.num_rows());
3456 assert_eq!(100, sliced.min_sequence);
3457 assert_eq!(101, sliced.sequence);
3458
3459 let mut unordered = UnorderedPart::new();
3460 unordered.push(sliced.clone());
3461 unordered.push(make_part(10));
3462 let merged = unordered.to_bulk_part(&metadata).unwrap().unwrap();
3463 assert_eq!(3, merged.num_rows());
3464 assert_eq!(10, merged.min_sequence);
3465 assert_eq!(101, merged.sequence);
3466 assert_eq!(10, merged.to_memtable_stats(&metadata).min_sequence);
3467
3468 unordered.clear();
3469 unordered.push(sliced);
3470 let reused = unordered.to_bulk_part(&metadata).unwrap().unwrap();
3471 assert_eq!(1, reused.num_rows());
3472 assert_eq!(100, reused.min_sequence);
3473 }
3474
3475 fn build_multi_bulk_part(groups: &[&[MutationInput]]) -> (MultiBulkPart, RegionMetadataRef) {
3477 let metadata = metadata_for_test();
3478 let mut all_batches = Vec::new();
3479 let mut min_ts = i64::MAX;
3480 let mut max_ts = i64::MIN;
3481 let mut min_seq = u64::MAX;
3482 let mut max_seq = 0u64;
3483
3484 for inputs in groups {
3485 let part = build_converted_bulk_part(inputs);
3486 min_ts = min_ts.min(part.min_timestamp);
3487 max_ts = max_ts.max(part.max_timestamp);
3488 min_seq = min_seq.min(part.min_sequence);
3489 max_seq = max_seq.max(part.sequence);
3490 all_batches.push(part.batch);
3491 }
3492
3493 let multi = MultiBulkPart::new(
3494 all_batches,
3495 min_ts,
3496 max_ts,
3497 min_seq,
3498 max_seq,
3499 groups.len(),
3500 &metadata,
3501 );
3502 (multi, metadata)
3503 }
3504
3505 #[test]
3506 fn test_multi_bulk_part_prune_batches() {
3507 let (multi, metadata) = build_multi_bulk_part(&[
3509 &[MutationInput {
3510 k0: "a",
3511 k1: 0,
3512 timestamps: &[1, 2],
3513 v1: &[Some(1.0), Some(2.0)],
3514 sequence: 0,
3515 }],
3516 &[MutationInput {
3517 k0: "m",
3518 k1: 0,
3519 timestamps: &[3, 4],
3520 v1: &[Some(3.0), Some(4.0)],
3521 sequence: 1,
3522 }],
3523 &[MutationInput {
3524 k0: "z",
3525 k1: 0,
3526 timestamps: &[5, 6],
3527 v1: &[Some(5.0), Some(6.0)],
3528 sequence: 2,
3529 }],
3530 ]);
3531 assert_eq!(multi.num_rows(), 6);
3532 assert_eq!(multi.num_batches(), 3);
3533
3534 let context = Arc::new(
3536 BulkIterContext::new(
3537 metadata.clone(),
3538 None,
3539 Some(Predicate::new(vec![
3540 datafusion_expr::col("k0").eq(datafusion_expr::lit("m")),
3541 ])),
3542 false,
3543 crate::sst::parquet::DEFAULT_READ_BATCH_SIZE,
3544 )
3545 .unwrap(),
3546 );
3547 let reader = multi
3548 .read(context, None, None)
3549 .unwrap()
3550 .expect("should have results");
3551 let total_rows: usize = reader.map(|r| r.unwrap().num_rows()).sum();
3552 assert_eq!(total_rows, 2);
3553
3554 let context = Arc::new(
3556 BulkIterContext::new(
3557 metadata.clone(),
3558 None,
3559 Some(Predicate::new(vec![
3560 datafusion_expr::col("k0").eq(datafusion_expr::lit("nonexistent")),
3561 ])),
3562 false,
3563 crate::sst::parquet::DEFAULT_READ_BATCH_SIZE,
3564 )
3565 .unwrap(),
3566 );
3567 assert!(multi.read(context, None, None).unwrap().is_none());
3568
3569 let context = Arc::new(
3571 BulkIterContext::new(
3572 metadata.clone(),
3573 None,
3574 None,
3575 false,
3576 crate::sst::parquet::DEFAULT_READ_BATCH_SIZE,
3577 )
3578 .unwrap(),
3579 );
3580 let reader = multi
3581 .read(context, None, None)
3582 .unwrap()
3583 .expect("should have results");
3584 let total_rows: usize = reader.map(|r| r.unwrap().num_rows()).sum();
3585 assert_eq!(total_rows, 6);
3586 }
3587
3588 #[test]
3589 fn test_fill_missing_columns_resets_raw_data() {
3590 let metadata = metadata_for_test();
3591
3592 let k0_array = Arc::new(arrow::array::StringArray::from(vec!["key1", "key2"]));
3595 let k1_array = Arc::new(arrow::array::UInt32Array::from(vec![1u32, 2]));
3596 let v0_array = Arc::new(arrow::array::Int64Array::from(vec![100i64, 200]));
3597 let ts_array = Arc::new(TimestampMillisecondArray::from(vec![1000i64, 2000]));
3598 let input_schema = Arc::new(Schema::new(vec![
3599 Field::new("k0", ArrowDataType::Utf8, false),
3600 Field::new("k1", ArrowDataType::UInt32, false),
3601 Field::new("v0", ArrowDataType::Int64, true),
3602 Field::new(
3603 "ts",
3604 ArrowDataType::Timestamp(arrow::datatypes::TimeUnit::Millisecond, None),
3605 false,
3606 ),
3607 ]));
3608 let batch =
3609 RecordBatch::try_new(input_schema, vec![k0_array, k1_array, v0_array, ts_array])
3610 .unwrap();
3611
3612 let mut encoder = FlightEncoder::default();
3614 let schema_bytes = encoder.encode_schema(batch.schema().as_ref()).data_header;
3615 let [flight_data] = encoder
3616 .encode(FlightMessage::RecordBatch(batch.clone()))
3617 .try_into()
3618 .unwrap();
3619 let mut part = BulkPart {
3620 batch,
3621 max_timestamp: 2000,
3622 min_timestamp: 1000,
3623 sequence: 5,
3624 min_sequence: 5,
3625 timestamp_index: 3,
3626 raw_data: Some(ArrowIpc {
3627 schema: schema_bytes,
3628 data_header: flight_data.data_header,
3629 payload: flight_data.data_body,
3630 }),
3631 };
3632
3633 part.fill_missing_columns(&metadata).unwrap();
3634 assert!(part.batch.column_by_name("v1").is_some());
3635 assert!(part.raw_data.is_none());
3639
3640 let entry = BulkWalEntry::try_from(&part).unwrap();
3642 let replayed = BulkPart::try_from(entry).unwrap();
3643 assert_eq!(part.sequence, replayed.min_sequence);
3644 assert_eq!(2, replayed.num_rows());
3645 assert!(replayed.batch.column_by_name("v1").is_some());
3646 }
3647}