1use std::sync::Arc;
18
19use api::v1::OpType;
20use datatypes::arrow::array::{Array, BinaryArray, UInt8Array, UInt32Array};
21use datatypes::arrow::compute::{concat_batches, take_record_batch};
22use datatypes::arrow::record_batch::RecordBatch;
23use futures::{Stream, TryStreamExt};
24use snafu::ResultExt;
25use store_api::storage::{FileId, TimeSeriesRowSelector};
26
27use crate::cache::{
28 CacheStrategy, SelectorResult, SelectorResultKey, SelectorResultValue,
29 selector_result_cache_hit, selector_result_cache_miss,
30};
31use crate::error::{ComputeArrowSnafu, Result};
32use crate::read::read_columns::JsonTargetTypes;
33use crate::read::{BoxedRecordBatchStream, timestamp_array_to_i64_slice};
34use crate::sst::parquet::DEFAULT_READ_BATCH_SIZE;
35use crate::sst::parquet::flat_format::{
36 op_type_column_index, primary_key_column_index, time_index_column_index,
37};
38use crate::sst::parquet::format::{PrimaryKeyArray, primary_key_offsets};
39use crate::sst::parquet::read_columns::ParquetReadColumns;
40use crate::sst::parquet::reader::FlatRowGroupReader;
41
42pub(crate) enum FlatRowGroupLastRowCachedReader {
46 Hit(FlatLastRowCacheReader),
48 Miss(FlatRowGroupLastRowReader),
50}
51
52impl FlatRowGroupLastRowCachedReader {
53 pub(crate) fn new(
54 file_id: FileId,
55 row_group_idx: usize,
56 cache_strategy: CacheStrategy,
57 read_cols: &ParquetReadColumns,
58 json_target_types: JsonTargetTypes,
59 reader: FlatRowGroupReader,
60 ) -> Self {
61 let key = SelectorResultKey {
62 file_id,
63 row_group_idx,
64 selector: TimeSeriesRowSelector::LastRow { after_merge: false },
65 };
66
67 if let Some(value) = cache_strategy.get_selector_result(&key) {
68 let is_flat = matches!(&value.result, SelectorResult::Flat(_));
69 let schema_matches = value.read_cols == *read_cols;
70 let json_target_types_matches = value.json_target_types == json_target_types;
71 if is_flat && schema_matches && json_target_types_matches {
72 Self::new_hit(value)
73 } else {
74 Self::new_miss(key, read_cols, json_target_types, reader, cache_strategy)
75 }
76 } else {
77 Self::new_miss(key, read_cols, json_target_types, reader, cache_strategy)
78 }
79 }
80
81 pub(crate) async fn next_batch(&mut self) -> Result<Option<RecordBatch>> {
83 match self {
84 FlatRowGroupLastRowCachedReader::Hit(r) => r.next_batch(),
85 FlatRowGroupLastRowCachedReader::Miss(r) => r.next_batch().await,
86 }
87 }
88
89 fn new_hit(value: Arc<SelectorResultValue>) -> Self {
90 selector_result_cache_hit();
91 Self::Hit(FlatLastRowCacheReader { value, idx: 0 })
92 }
93
94 fn new_miss(
95 key: SelectorResultKey,
96 read_cols: &ParquetReadColumns,
97 json_target_types: JsonTargetTypes,
98 reader: FlatRowGroupReader,
99 cache_strategy: CacheStrategy,
100 ) -> Self {
101 selector_result_cache_miss();
102 Self::Miss(FlatRowGroupLastRowReader::new(
103 key,
104 read_cols.clone(),
105 json_target_types,
106 reader,
107 cache_strategy,
108 ))
109 }
110}
111
112pub(crate) struct FlatLastRowCacheReader {
114 value: Arc<SelectorResultValue>,
115 idx: usize,
116}
117
118impl FlatLastRowCacheReader {
119 fn next_batch(&mut self) -> Result<Option<RecordBatch>> {
120 let batches = match &self.value.result {
121 SelectorResult::Flat(batches) => batches,
122 SelectorResult::PrimaryKey(_) => unreachable!(),
123 };
124 if self.idx < batches.len() {
125 let res = Ok(Some(batches[self.idx].clone()));
126 self.idx += 1;
127 res
128 } else {
129 Ok(None)
130 }
131 }
132}
133
134pub(crate) struct BatchBuffer {
136 batches: Vec<RecordBatch>,
137 num_rows: usize,
138}
139
140impl BatchBuffer {
141 fn new() -> Self {
142 Self {
143 batches: Vec::new(),
144 num_rows: 0,
145 }
146 }
147
148 fn is_full(&self) -> bool {
150 self.num_rows >= DEFAULT_READ_BATCH_SIZE
151 }
152
153 fn extend_from_slice(&mut self, batches: &[RecordBatch]) {
155 for batch in batches {
156 self.num_rows += batch.num_rows();
157 }
158 self.batches.extend_from_slice(batches);
159 }
160
161 fn push(&mut self, batch: RecordBatch) {
162 self.num_rows += batch.num_rows();
163 self.batches.push(batch);
164 }
165
166 fn is_empty(&self) -> bool {
168 self.batches.is_empty()
169 }
170
171 fn concat(&mut self) -> Result<RecordBatch> {
173 debug_assert!(!self.batches.is_empty());
174 let schema = self.batches[0].schema();
175 let merged = concat_batches(&schema, &self.batches).context(ComputeArrowSnafu)?;
176 self.batches.clear();
177 self.num_rows = 0;
178 Ok(merged)
179 }
180}
181
182pub(crate) struct FlatRowGroupLastRowReader {
184 key: SelectorResultKey,
185 reader: FlatRowGroupReader,
186 selector: FlatLastTimestampSelector,
187 yielded_batches: Vec<RecordBatch>,
188 cache_strategy: CacheStrategy,
189 read_cols: ParquetReadColumns,
190 json_target_types: JsonTargetTypes,
191 pending: BatchBuffer,
193}
194
195impl FlatRowGroupLastRowReader {
196 fn new(
197 key: SelectorResultKey,
198 read_cols: ParquetReadColumns,
199 json_target_types: JsonTargetTypes,
200 reader: FlatRowGroupReader,
201 cache_strategy: CacheStrategy,
202 ) -> Self {
203 Self {
204 key,
205 reader,
206 selector: FlatLastTimestampSelector::default(),
207 yielded_batches: vec![],
208 cache_strategy,
209 read_cols,
210 json_target_types,
211 pending: BatchBuffer::new(),
212 }
213 }
214
215 fn flush_pending(&mut self) -> Result<Option<RecordBatch>> {
217 if self.pending.is_empty() {
218 return Ok(None);
219 }
220 let merged = self.pending.concat()?;
221 self.yielded_batches.push(merged.clone());
222 Ok(Some(merged))
223 }
224
225 async fn next_batch(&mut self) -> Result<Option<RecordBatch>> {
226 if self.pending.is_full() {
227 return self.flush_pending();
228 }
229
230 while let Some(batch) = self.reader.next_batch().await? {
231 self.selector.on_next(batch, &mut self.pending)?;
232 if self.pending.is_full() {
233 return self.flush_pending();
234 }
235 }
236
237 self.selector.finish(&mut self.pending)?;
239 if !self.pending.is_empty() {
240 let result = self.flush_pending();
241 self.maybe_update_cache();
243 return result;
244 }
245
246 self.maybe_update_cache();
248 Ok(None)
249 }
250
251 fn maybe_update_cache(&mut self) {
252 if self.yielded_batches.is_empty() {
253 return;
254 }
255 let batches = std::mem::take(&mut self.yielded_batches);
256 let value = Arc::new(SelectorResultValue::new_flat(
257 batches,
258 self.read_cols.clone(),
259 self.json_target_types.clone(),
260 ));
261 self.cache_strategy.put_selector_result(self.key, value);
262 }
263}
264
265#[derive(Default)]
270pub(crate) struct FlatLastTimestampSelector {
271 current_key: Option<LastKeyState>,
273}
274
275#[derive(Debug)]
276struct LastKeyState {
277 key: Vec<u8>,
278 last_timestamp: i64,
279 slices: Vec<RecordBatch>,
280}
281
282impl LastKeyState {
283 fn new(key: Vec<u8>, last_timestamp: i64, first_slice: RecordBatch) -> Self {
284 Self {
285 key,
286 last_timestamp,
287 slices: vec![first_slice],
288 }
289 }
290}
291
292impl FlatLastTimestampSelector {
293 pub(crate) fn select_batch(batch: RecordBatch) -> Result<RecordBatch> {
295 if batch.num_rows() <= 1 {
296 return Ok(batch);
297 }
298 let Some(op_types) = batch
299 .column(op_type_column_index(batch.num_columns()))
300 .as_any()
301 .downcast_ref::<UInt8Array>()
302 else {
303 return Ok(batch);
304 };
305 if op_types.values().iter().any(|op| *op != OpType::Put as u8) {
306 return Ok(batch);
307 }
308 let mut selector = Self::default();
309 let mut pending = BatchBuffer::new();
310 selector.on_next(batch.clone(), &mut pending)?;
311 selector.finish(&mut pending)?;
312 if pending.num_rows == batch.num_rows() {
313 return Ok(batch);
314 }
315 pending.concat()
316 }
317
318 pub(crate) fn on_next(
320 &mut self,
321 batch: RecordBatch,
322 output_buffer: &mut BatchBuffer,
323 ) -> Result<()> {
324 if batch.num_rows() == 0 {
325 return Ok(());
326 }
327
328 let num_columns = batch.num_columns();
329 let pk_col_idx = primary_key_column_index(num_columns);
330 let ts_col_idx = time_index_column_index(num_columns);
331
332 let pk_array = batch
333 .column(pk_col_idx)
334 .as_any()
335 .downcast_ref::<PrimaryKeyArray>()
336 .unwrap();
337 let offsets = primary_key_offsets(pk_array)?;
338 if offsets.is_empty() {
339 return Ok(());
340 }
341
342 let mut series_ranges: Vec<(usize, usize)> = Vec::with_capacity(offsets.len() - 1);
345 for window in offsets.windows(2) {
346 let (start, end) = (window[0], window[1]);
347 match series_ranges.last_mut() {
348 Some(last)
349 if primary_key_bytes_at(&batch, pk_col_idx, last.0)
350 == primary_key_bytes_at(&batch, pk_col_idx, start) =>
351 {
352 last.1 = end;
353 }
354 _ => series_ranges.push((start, end)),
355 }
356 }
357
358 let ts_values = timestamp_array_to_i64_slice(batch.column(ts_col_idx));
359 let num_ranges = series_ranges.len();
360 let mut completed_rows = Vec::new();
364 for (i, &(range_start, range_end)) in series_ranges.iter().enumerate() {
365 let range_key = primary_key_bytes_at(&batch, pk_col_idx, range_start);
366 let range_last_ts = ts_values[range_end - 1];
367 let range_last_ts_start = last_timestamp_start(ts_values, range_start, range_end);
368 let is_last_range = i + 1 == num_ranges;
369
370 if let Some(state) = self.current_key.as_mut()
372 && state.key.as_slice() == range_key
373 {
374 let range_slice = batch.slice(range_last_ts_start, range_end - range_last_ts_start);
375 if range_last_ts > state.last_timestamp {
376 state.last_timestamp = range_last_ts;
377 state.slices.clear();
378 state.slices.push(range_slice);
379 } else if range_last_ts == state.last_timestamp {
380 state.slices.push(range_slice);
381 }
382 if !is_last_range {
383 self.flush_current_key(output_buffer);
384 }
385 continue;
386 }
387
388 self.flush_current_key(output_buffer);
390 if is_last_range {
391 self.current_key = Some(LastKeyState::new(
393 range_key.to_vec(),
394 range_last_ts,
395 batch.slice(range_last_ts_start, range_end - range_last_ts_start),
396 ));
397 } else {
398 completed_rows.extend(range_last_ts_start as u32..range_end as u32);
399 }
400 }
401
402 if !completed_rows.is_empty() {
403 let indices = UInt32Array::from(completed_rows);
404 let selected = take_record_batch(&batch, &indices).context(ComputeArrowSnafu)?;
405 output_buffer.push(selected);
406 }
407
408 Ok(())
409 }
410
411 pub(crate) fn finish(&mut self, output_buffer: &mut BatchBuffer) -> Result<()> {
413 self.flush_current_key(output_buffer);
414 Ok(())
415 }
416
417 fn flush_current_key(&mut self, output_buffer: &mut BatchBuffer) {
418 let Some(state) = self.current_key.take() else {
419 return;
420 };
421 output_buffer.extend_from_slice(&state.slices);
422 }
423}
424
425pub(crate) struct FlatLastRowReader {
428 stream: BoxedRecordBatchStream,
429 selector: FlatLastTimestampSelector,
430 pending: BatchBuffer,
431}
432
433impl FlatLastRowReader {
434 pub(crate) fn new(stream: BoxedRecordBatchStream) -> Self {
436 Self {
437 stream,
438 selector: FlatLastTimestampSelector::default(),
439 pending: BatchBuffer::new(),
440 }
441 }
442
443 pub(crate) fn into_stream(mut self) -> impl Stream<Item = Result<RecordBatch>> {
445 async_stream::try_stream! {
446 while let Some(batch) = self.stream.try_next().await? {
447 self.selector.on_next(batch, &mut self.pending)?;
448 if self.pending.is_full() {
449 yield self.pending.concat()?;
450 }
451 }
452 self.selector.finish(&mut self.pending)?;
453 if !self.pending.is_empty() {
454 yield self.pending.concat()?;
455 }
456 }
457 }
458}
459
460fn primary_key_bytes_at(batch: &RecordBatch, pk_col_idx: usize, index: usize) -> &[u8] {
462 let pk_dict = batch
463 .column(pk_col_idx)
464 .as_any()
465 .downcast_ref::<PrimaryKeyArray>()
466 .unwrap();
467 let key = pk_dict.keys().value(index);
468 let binary_values = pk_dict
469 .values()
470 .as_any()
471 .downcast_ref::<BinaryArray>()
472 .unwrap();
473 binary_values.value(key as usize)
474}
475
476fn last_timestamp_start(ts_values: &[i64], range_start: usize, range_end: usize) -> usize {
479 debug_assert!(range_start < range_end);
480
481 let last_ts = ts_values[range_end - 1];
482 let mut start = range_end - 1;
483 while start > range_start && ts_values[start - 1] == last_ts {
484 start -= 1;
485 }
486 start
487}
488
489#[cfg(test)]
490mod tests {
491 use std::sync::Arc;
492
493 use datatypes::arrow::array::{
494 ArrayRef, BinaryDictionaryBuilder, DictionaryArray, Int64Array, TimestampMillisecondArray,
495 UInt8Array, UInt64Array,
496 };
497 use datatypes::arrow::datatypes::{DataType, Field, Schema, SchemaRef, TimeUnit, UInt32Type};
498 use datatypes::arrow::record_batch::RecordBatch;
499
500 use super::*;
501
502 #[test]
503 fn test_select_batch_preserves_timestamp_ties_and_deletes()
504 -> std::result::Result<(), Box<dyn std::error::Error>> {
505 let input = new_flat_batch(
506 &[b"a", b"a", b"a", b"b", b"b", b"c"],
507 &[1, 2, 2, 1, 2, 1],
508 &[1, 2, 3, 4, 5, 6],
509 );
510 let expected = take_record_batch(&input, &UInt32Array::from(vec![1, 2, 4, 5]))?;
511 assert_eq!(
512 expected,
513 FlatLastTimestampSelector::select_batch(input.clone())?
514 );
515 let empty = input.slice(0, 0);
516 assert_eq!(
517 empty,
518 FlatLastTimestampSelector::select_batch(empty.clone())?
519 );
520 let mut columns = input.columns().to_vec();
521 columns[op_type_column_index(input.num_columns())] = Arc::new(UInt8Array::from(vec![
522 OpType::Delete as u8,
523 OpType::Put as u8,
524 OpType::Put as u8,
525 OpType::Put as u8,
526 OpType::Put as u8,
527 OpType::Put as u8,
528 ]));
529 let with_delete = RecordBatch::try_new(input.schema(), columns)?;
530 assert_eq!(
531 with_delete,
532 FlatLastTimestampSelector::select_batch(with_delete.clone())?
533 );
534 Ok(())
535 }
536
537 fn new_flat_batch(primary_keys: &[&[u8]], timestamps: &[i64], fields: &[i64]) -> RecordBatch {
539 let num_rows = timestamps.len();
540 assert_eq!(primary_keys.len(), num_rows);
541 assert_eq!(fields.len(), num_rows);
542
543 let columns: Vec<ArrayRef> = vec![
544 Arc::new(Int64Array::from_iter_values(fields.iter().copied())),
546 Arc::new(TimestampMillisecondArray::from_iter_values(
548 timestamps.iter().copied(),
549 )),
550 {
552 let mut builder = BinaryDictionaryBuilder::<UInt32Type>::new();
553 for &pk in primary_keys {
554 builder.append(pk).unwrap();
555 }
556 Arc::new(builder.finish())
557 },
558 Arc::new(UInt64Array::from_iter_values(vec![1u64; num_rows])),
560 Arc::new(UInt8Array::from_iter_values(vec![1u8; num_rows])),
562 ];
563
564 RecordBatch::try_new(test_flat_schema(), columns).unwrap()
565 }
566
567 fn test_flat_schema() -> SchemaRef {
568 let fields = vec![
569 Field::new("field0", DataType::Int64, false),
570 Field::new(
571 "ts",
572 DataType::Timestamp(TimeUnit::Millisecond, None),
573 false,
574 ),
575 Field::new(
576 "__primary_key",
577 DataType::Dictionary(Box::new(DataType::UInt32), Box::new(DataType::Binary)),
578 false,
579 ),
580 Field::new("__sequence", DataType::UInt64, false),
581 Field::new("__op_type", DataType::UInt8, false),
582 ];
583 Arc::new(Schema::new(fields))
584 }
585
586 fn collect_flat_results(
588 selector: &mut FlatLastTimestampSelector,
589 batches: Vec<RecordBatch>,
590 ) -> Vec<(Vec<u8>, i64)> {
591 let mut output_buffer = BatchBuffer::new();
592 let mut results = Vec::new();
593 for batch in batches {
594 selector.on_next(batch, &mut output_buffer).unwrap();
595 for r in output_buffer.batches.drain(..) {
596 extract_flat_rows(&r, &mut results);
597 }
598 output_buffer.num_rows = 0;
599 }
600 selector.finish(&mut output_buffer).unwrap();
601 for r in output_buffer.batches.drain(..) {
602 extract_flat_rows(&r, &mut results);
603 }
604 results
605 }
606
607 fn extract_flat_rows(batch: &RecordBatch, out: &mut Vec<(Vec<u8>, i64)>) {
609 let ts_col = batch
610 .column(1)
611 .as_any()
612 .downcast_ref::<TimestampMillisecondArray>()
613 .unwrap();
614 let pk_col = batch
615 .column(2)
616 .as_any()
617 .downcast_ref::<PrimaryKeyArray>()
618 .unwrap();
619 let binary_values = pk_col
620 .values()
621 .as_any()
622 .downcast_ref::<BinaryArray>()
623 .unwrap();
624
625 for i in 0..batch.num_rows() {
626 let key_idx = pk_col.keys().value(i);
627 let pk = binary_values.value(key_idx as usize).to_vec();
628 let ts = ts_col.value(i);
629 out.push((pk, ts));
630 }
631 }
632
633 #[test]
634 fn test_flat_single_batch_one_key() {
635 let mut selector = FlatLastTimestampSelector::default();
636 let batch = new_flat_batch(&[b"k1", b"k1", b"k1"], &[1, 2, 3], &[10, 20, 30]);
637 let results = collect_flat_results(&mut selector, vec![batch]);
638 assert_eq!(vec![(b"k1".to_vec(), 3)], results);
639 }
640
641 #[test]
642 fn test_flat_single_batch_multiple_keys() {
643 let mut selector = FlatLastTimestampSelector::default();
644 let batch = new_flat_batch(
645 &[b"k1", b"k1", b"k2", b"k2", b"k3"],
646 &[1, 2, 3, 4, 5],
647 &[10, 20, 30, 40, 50],
648 );
649 let results = collect_flat_results(&mut selector, vec![batch]);
650 assert_eq!(
651 vec![
652 (b"k1".to_vec(), 2),
653 (b"k2".to_vec(), 4),
654 (b"k3".to_vec(), 5),
655 ],
656 results
657 );
658 }
659
660 #[test]
661 fn test_flat_key_spans_batches() {
662 let mut selector = FlatLastTimestampSelector::default();
663 let batches = vec![
664 new_flat_batch(&[b"k1", b"k1"], &[1, 2], &[10, 20]),
665 new_flat_batch(&[b"k1", b"k2"], &[3, 4], &[30, 40]),
666 new_flat_batch(&[b"k2", b"k3"], &[5, 6], &[50, 60]),
667 ];
668 let results = collect_flat_results(&mut selector, batches);
669 assert_eq!(
670 vec![
671 (b"k1".to_vec(), 3),
672 (b"k2".to_vec(), 5),
673 (b"k3".to_vec(), 6),
674 ],
675 results
676 );
677 }
678
679 #[test]
680 fn test_flat_duplicate_last_timestamps() {
681 let mut selector = FlatLastTimestampSelector::default();
682 let batch = new_flat_batch(
684 &[b"k1", b"k1", b"k1", b"k2"],
685 &[1, 3, 3, 5],
686 &[10, 20, 30, 40],
687 );
688 let results = collect_flat_results(&mut selector, vec![batch]);
689 assert_eq!(
690 vec![
691 (b"k1".to_vec(), 3),
692 (b"k1".to_vec(), 3),
693 (b"k2".to_vec(), 5),
694 ],
695 results
696 );
697 }
698
699 #[test]
700 fn test_flat_duplicate_last_timestamps_across_batches() {
701 let mut selector = FlatLastTimestampSelector::default();
702 let batches = vec![
704 new_flat_batch(&[b"k1", b"k1"], &[1, 3], &[10, 20]),
705 new_flat_batch(&[b"k1", b"k2"], &[3, 5], &[30, 40]),
706 ];
707 let results = collect_flat_results(&mut selector, batches);
708 assert_eq!(
709 vec![
710 (b"k1".to_vec(), 3),
711 (b"k1".to_vec(), 3),
712 (b"k2".to_vec(), 5),
713 ],
714 results
715 );
716 }
717
718 #[test]
719 fn test_flat_pending_chain_dropped_by_higher_timestamp() {
720 let mut selector = FlatLastTimestampSelector::default();
721 let batches = vec![
722 new_flat_batch(&[b"k1", b"k1"], &[1, 3], &[10, 20]),
723 new_flat_batch(&[b"k1", b"k1"], &[3, 3], &[21, 22]),
724 new_flat_batch(&[b"k1", b"k1"], &[4, 4], &[23, 24]),
725 ];
726 let results = collect_flat_results(&mut selector, batches);
727 assert_eq!(vec![(b"k1".to_vec(), 4), (b"k1".to_vec(), 4)], results);
728 }
729
730 #[test]
731 fn test_flat_concat_across_parent_dictionaries() {
732 let mut selector = FlatLastTimestampSelector::default();
733 let mut output_buffer = BatchBuffer::new();
734 let batches = vec![
735 new_flat_batch(&[b"a", b"b", b"b", b"c"], &[1, 1, 2, 3], &[10, 20, 21, 30]),
736 new_flat_batch(
737 &[b"c", b"d", b"d", b"e", b"e"],
738 &[3, 4, 4, 5, 5],
739 &[31, 40, 41, 50, 51],
740 ),
741 new_flat_batch(&[b"f", b"g"], &[6, 7], &[60, 70]),
742 ];
743 for batch in batches {
744 selector.on_next(batch, &mut output_buffer).unwrap();
745 }
746 selector.finish(&mut output_buffer).unwrap();
747 let merged = output_buffer.concat().unwrap();
748
749 let mut rows = Vec::new();
750 extract_flat_rows(&merged, &mut rows);
751 let fields = merged
752 .column(0)
753 .as_any()
754 .downcast_ref::<Int64Array>()
755 .unwrap()
756 .values()
757 .to_vec();
758 assert_eq!(
759 vec![
760 (b"a".to_vec(), 1),
761 (b"b".to_vec(), 2),
762 (b"c".to_vec(), 3),
763 (b"c".to_vec(), 3),
764 (b"d".to_vec(), 4),
765 (b"d".to_vec(), 4),
766 (b"e".to_vec(), 5),
767 (b"e".to_vec(), 5),
768 (b"f".to_vec(), 6),
769 (b"g".to_vec(), 7),
770 ],
771 rows
772 );
773 assert_eq!(vec![10, 21, 30, 31, 40, 41, 50, 51, 60, 70], fields);
774 }
775
776 fn with_identity_pk_keys(batch: RecordBatch) -> RecordBatch {
779 let pk_col_idx = primary_key_column_index(batch.num_columns());
780 let values = (0..batch.num_rows())
781 .map(|i| Some(primary_key_bytes_at(&batch, pk_col_idx, i)))
782 .collect::<BinaryArray>();
783 let keys = UInt32Array::from_iter_values(0..batch.num_rows() as u32);
784 let mut columns = batch.columns().to_vec();
785 columns[pk_col_idx] = Arc::new(DictionaryArray::new(keys, Arc::new(values)));
786 RecordBatch::try_new(batch.schema(), columns).unwrap()
787 }
788
789 #[test]
790 fn test_flat_identity_dictionary_keys() {
791 let mut selector = FlatLastTimestampSelector::default();
792 let batches = vec![
793 with_identity_pk_keys(new_flat_batch(&[b"a", b"a", b"b"], &[1, 2, 3], &[0; 3])),
794 with_identity_pk_keys(new_flat_batch(&[b"b", b"c", b"c"], &[4, 5, 6], &[0; 3])),
795 ];
796 let results = collect_flat_results(&mut selector, batches);
797 assert_eq!(
798 vec![(b"a".to_vec(), 2), (b"b".to_vec(), 4), (b"c".to_vec(), 6)],
799 results
800 );
801 }
802
803 #[test]
804 fn test_flat_finish_is_one_shot() {
805 let mut selector = FlatLastTimestampSelector::default();
806 let batch = new_flat_batch(&[b"k1", b"k1", b"k2"], &[1, 2, 3], &[10, 20, 30]);
807 let mut output_buffer = BatchBuffer::new();
808
809 selector.on_next(batch, &mut output_buffer).unwrap();
811 let mut pre_finish = Vec::new();
812 for r in output_buffer.batches.drain(..) {
813 extract_flat_rows(&r, &mut pre_finish);
814 }
815 output_buffer.num_rows = 0;
816 assert_eq!(vec![(b"k1".to_vec(), 2)], pre_finish);
817
818 selector.finish(&mut output_buffer).unwrap();
820 assert!(!output_buffer.is_empty());
821 output_buffer.batches.clear();
822 output_buffer.num_rows = 0;
823
824 selector.finish(&mut output_buffer).unwrap();
826 assert!(output_buffer.is_empty());
827 }
828}