1#![deny(missing_docs)]
19
20pub mod batcher;
21pub mod body;
22pub mod builder;
23pub mod builder_input;
24pub mod chunk;
25pub mod consolidate;
26pub mod merge_batcher;
27pub mod unload;
28
29use std::hash::{BuildHasher, Hash, Hasher};
30use std::sync::LazyLock;
31
32use columnar::Borrow;
33use columnar::bytes::indexed;
34use columnar::common::IterOwn;
35use columnar::{Clear, FromBytes, Index, Len};
36use columnar::{Columnar, Ref};
37use differential_dataflow::Hashable;
38use differential_dataflow::collection::containers::{Enter, ResultsIn};
39use differential_dataflow::trace::implementations::merge_batcher::MergeBatcher;
40use itertools::Itertools;
41use timely::Accountable;
42use timely::bytes::arc::Bytes;
43use timely::container::{DrainContainer, PushInto, SizableContainer};
44use timely::dataflow::channels::ContainerBytes;
45use timely::progress::Timestamp;
46use timely::progress::timestamp::Refines;
47
48use crate::columnation::ColInternalMerger;
49
50pub type Col2ValBatcher<K, V, T, R, Chu, Se> =
59 MergeBatcher<Chu, ColInternalMerger<(K, V), T, R>, Se>;
60pub type Col2KeyBatcher<K, T, R, Chu, Se> = Col2ValBatcher<K, (), T, R, Chu, Se>;
62
63pub type Col2ValPagedBatcher<K, V, T, R, Chu, Se> =
73 merge_batcher::ColumnMergeBatcher<Chu, (K, V), T, R, Se>;
74
75pub type Col2ValColBatcher<K, V, T, R, Chu, Se> =
83 MergeBatcher<Chu, batcher::ColumnMerger<(K, V), T, R>, Se>;
84
85pub enum Column<C: Columnar> {
95 Typed(C::Container),
97 Bytes(Bytes),
99 Align(Vec<u64>),
106}
107
108impl<C: Columnar> Column<C> {
109 #[inline]
116 pub fn clear(&mut self) {
117 match self {
118 Column::Typed(t) => t.clear(),
119 Column::Bytes(_) | Column::Align(_) => *self = Default::default(),
120 }
121 }
122
123 #[inline]
129 pub fn is_empty(&self) -> bool {
130 match self {
131 Column::Typed(t) => t.is_empty(),
132 Column::Bytes(_) | Column::Align(_) => self.borrow().is_empty(),
133 }
134 }
135
136 #[inline(always)]
140 pub fn borrow(&self) -> <C::Container as Borrow>::Borrowed<'_> {
141 match self {
142 Column::Typed(t) => t.borrow(),
143 Column::Bytes(b) => <<C::Container as Borrow>::Borrowed<'_>>::from_bytes(
144 &mut indexed::decode(bytemuck::cast_slice(b)),
145 ),
146 Column::Align(a) => {
147 <<C::Container as Borrow>::Borrowed<'_>>::from_bytes(&mut indexed::decode(a))
148 }
149 }
150 }
151}
152
153impl<C: Columnar> Default for Column<C> {
154 fn default() -> Self {
155 Self::Typed(Default::default())
156 }
157}
158
159impl<C: Columnar> Clone for Column<C>
160where
161 C::Container: Clone,
162{
163 fn clone(&self) -> Self {
164 match self {
165 Column::Typed(t) => Column::Typed(t.clone()),
168 Column::Bytes(b) => {
169 assert_eq!(b.len() % 8, 0);
170 Self::Align(bytemuck::allocation::pod_collect_to_vec(b))
171 }
172 Column::Align(a) => Column::Align(a.clone()),
173 }
174 }
175}
176
177impl<C: Columnar> Accountable for Column<C> {
178 #[inline]
179 fn record_count(&self) -> i64 {
180 self.borrow().len().try_into().expect("Must fit")
181 }
182}
183impl<C: Columnar> DrainContainer for Column<C> {
184 type Item<'a> = Ref<'a, C>;
185 type DrainIter<'a> = IterOwn<<C::Container as Borrow>::Borrowed<'a>>;
186 #[inline]
187 fn drain(&mut self) -> Self::DrainIter<'_> {
188 self.borrow().into_index_iter()
189 }
190}
191
192impl<C: Columnar, T> PushInto<T> for Column<C>
193where
194 C::Container: columnar::Push<T>,
195{
196 #[inline]
197 fn push_into(&mut self, item: T) {
198 use columnar::Push;
199 match self {
200 Column::Typed(t) => t.push(item),
201 Column::Align(_) | Column::Bytes(_) => {
202 unimplemented!("Pushing into Column::Bytes without first clearing");
205 }
206 }
207 }
208}
209
210impl<D, T1, T2, R> Enter<T1, T2> for Column<(D, T1, R)>
213where
214 D: Columnar,
215 T1: Columnar + Timestamp,
216 T2: Columnar + Refines<T1>,
217 R: Columnar,
218 (D, T1, R): Columnar<Container = (D::Container, T1::Container, R::Container)>,
219 (D, T2, R): Columnar<Container = (D::Container, T2::Container, R::Container)>,
220 for<'a> D::Container: columnar::Push<Ref<'a, D>>,
221 for<'a> T2::Container: columnar::Push<&'a T2>,
222 for<'a> R::Container: columnar::Push<Ref<'a, R>>,
223{
224 type InnerContainer = Column<(D, T2, R)>;
225
226 fn enter(self) -> Self::InnerContainer {
227 use columnar::Push;
228 match self {
229 Column::Typed((data, times, diffs)) => {
231 let mut inner = T2::Container::default();
232 for time in times.borrow().into_index_iter() {
233 inner.push(&T2::to_inner(T1::into_owned(time)));
234 }
235 Column::Typed((data, inner, diffs))
236 }
237 serialized => {
242 let (borrowed_data, borrowed_times, borrowed_diffs) = serialized.borrow();
243 let mut times = T2::Container::default();
244 for time in borrowed_times.into_index_iter() {
245 times.push(&T2::to_inner(T1::into_owned(time)));
246 }
247 let view = (borrowed_data, times.borrow(), borrowed_diffs);
248 let words = indexed::length_in_words(&view);
249 let mut alloc: Vec<u64> = Vec::with_capacity(words);
250 indexed::encode(&mut alloc, &view);
251 Column::Align(alloc)
252 }
253 }
254 }
255}
256
257impl<D, T, R> ResultsIn<T::Summary> for Column<(D, T, R)>
264where
265 D: Columnar,
266 T: Columnar + Timestamp,
267 R: Columnar,
268 (D, T, R): Columnar<Container = (D::Container, T::Container, R::Container)>,
269 for<'a> D::Container: columnar::Push<Ref<'a, D>>,
270 for<'a> T::Container: columnar::Push<&'a T>,
271 for<'a> R::Container: columnar::Push<Ref<'a, R>>,
272{
273 fn results_in(self, step: &T::Summary) -> Self {
274 use columnar::Push;
275 use timely::progress::PathSummary;
276
277 let (times, kept) = {
281 let (_, borrowed_times, _) = self.borrow();
282 let mut times = T::Container::default();
283 let mut time = T::minimum();
284 let kept: Vec<bool> = borrowed_times
285 .into_index_iter()
286 .map(|reference| {
287 time.copy_from(reference);
288 match step.results_in(&time) {
289 Some(time) => {
290 times.push(&time);
291 true
292 }
293 None => false,
294 }
295 })
296 .collect();
297 (times, kept)
298 };
299
300 if kept.iter().all(|kept| *kept) {
301 match self {
302 Column::Typed((data, _, diffs)) => Column::Typed((data, times, diffs)),
304 serialized => {
307 let (borrowed_data, _, borrowed_diffs) = serialized.borrow();
308 let view = (borrowed_data, times.borrow(), borrowed_diffs);
309 let words = indexed::length_in_words(&view);
310 let mut alloc: Vec<u64> = Vec::with_capacity(words);
311 indexed::encode(&mut alloc, &view);
312 Column::Align(alloc)
313 }
314 }
315 } else {
316 let (borrowed_data, _, borrowed_diffs) = self.borrow();
317 let mut data = D::Container::default();
318 let mut diffs = R::Container::default();
319 let records = borrowed_data
320 .into_index_iter()
321 .zip_eq(borrowed_diffs.into_index_iter())
322 .zip_eq(kept.iter());
323 for ((datum, diff), _) in records.filter(|(_, kept)| **kept) {
324 data.push(datum);
325 diffs.push(diff);
326 }
327 Column::Typed((data, times, diffs))
328 }
329 }
330}
331
332const SHIP_WORDS: usize = 1 << 18;
337
338#[inline]
349pub(crate) fn at_serialized_capacity<'a, A>(borrow: &A) -> bool
350where
351 A: columnar::AsBytes<'a>,
352{
353 indexed::length_in_words(borrow) >= SHIP_WORDS - SHIP_WORDS / 10
354}
355
356impl<C: Columnar> SizableContainer for Column<C> {
357 fn at_capacity(&self) -> bool {
358 match self {
366 Column::Typed(c) => at_serialized_capacity(&c.borrow()),
367 Column::Bytes(_) | Column::Align(_) => true,
368 }
369 }
370
371 fn ensure_capacity(&mut self, _stash: &mut Option<Self>) {
372 }
379}
380
381impl<C: Columnar> ContainerBytes for Column<C> {
382 #[inline]
383 fn from_bytes(bytes: Bytes) -> Self {
384 assert_eq!(bytes.len() % 8, 0);
390 if let Ok(_) = bytemuck::try_cast_slice::<_, u64>(&bytes) {
391 Self::Bytes(bytes)
392 } else {
393 Self::Align(bytemuck::allocation::pod_collect_to_vec(&bytes[..]))
396 }
397 }
398
399 #[inline]
400 fn length_in_bytes(&self) -> usize {
401 match self {
402 Column::Typed(t) => indexed::length_in_bytes(&t.borrow()),
403 Column::Bytes(b) => b.len(),
404 Column::Align(a) => 8 * a.len(),
405 }
406 }
407
408 #[inline]
409 fn into_bytes<W: ::std::io::Write>(&self, writer: &mut W) {
410 match self {
411 Column::Typed(t) => indexed::write(writer, &t.borrow()).unwrap(),
412 Column::Bytes(b) => writer.write_all(b).unwrap(),
413 Column::Align(a) => writer.write_all(bytemuck::cast_slice(a)).unwrap(),
414 }
415 }
416}
417
418#[inline(always)]
422pub fn columnar_exchange<K, V, T, D>(((k, _), _, _): &Ref<'_, ((K, V), T, D)>) -> u64
423where
424 K: Columnar,
425 for<'a> Ref<'a, K>: Hash,
426 V: Columnar,
427 D: Columnar,
428 T: Columnar,
429{
430 k.hashed()
431}
432
433pub fn columnar_exchange_data<D, T, R>((d, _, _): &Ref<'_, (D, T, R)>) -> u64
439where
440 D: Columnar,
441 for<'a> Ref<'a, D>: Hash,
442 T: Columnar,
443 R: Columnar,
444{
445 d.hashed()
446}
447
448pub fn columnar_consolidate_exchange<D, T, R>((d, _, _): &Ref<'_, (D, T, R)>) -> u64
457where
458 D: Columnar,
459 for<'a> Ref<'a, D>: Hash,
460 T: Columnar,
461 R: Columnar,
462{
463 static STATE: LazyLock<ahash::RandomState> = LazyLock::new(crate::hash::fixed_state);
464 let mut hasher = STATE.build_hasher();
465 d.hash(&mut hasher);
466 hasher.finish()
467}
468
469#[cfg(test)]
470mod tests {
471 use timely::bytes::arc::BytesMut;
472 use timely::container::PushInto;
473 use timely::dataflow::channels::ContainerBytes;
474
475 use super::*;
476
477 fn raw_columnar_bytes() -> Vec<u8> {
479 let mut raw = Vec::new();
480 raw.extend(16_u64.to_le_bytes()); raw.extend(28_u64.to_le_bytes()); raw.extend(1_i32.to_le_bytes());
483 raw.extend(2_i32.to_le_bytes());
484 raw.extend(3_i32.to_le_bytes());
485 raw.extend([0, 0, 0, 0]); raw
487 }
488
489 #[mz_ore::test]
490 fn test_column_clone() {
491 let columns = Columnar::as_columns([1, 2, 3].iter());
492 let column_typed: Column<i32> = Column::Typed(columns);
493 let column_typed2 = column_typed.clone();
494
495 assert_eq!(
496 column_typed2.borrow().into_index_iter().collect::<Vec<_>>(),
497 vec![&1, &2, &3]
498 );
499
500 let bytes = BytesMut::from(raw_columnar_bytes()).freeze();
501 let column_bytes: Column<i32> = Column::Bytes(bytes);
502 let column_bytes2 = column_bytes.clone();
503
504 assert_eq!(
505 column_bytes2.borrow().into_index_iter().collect::<Vec<_>>(),
506 vec![&1, &2, &3]
507 );
508
509 let raw = raw_columnar_bytes();
510 let mut region: Vec<u64> = vec![0; raw.len() / 8];
511 let region_bytes = bytemuck::cast_slice_mut(&mut region[..]);
512 region_bytes[..raw.len()].copy_from_slice(&raw);
513 let column_align: Column<i32> = Column::Align(region);
514 let column_align2 = column_align.clone();
515
516 assert_eq!(
517 column_align2.borrow().into_index_iter().collect::<Vec<_>>(),
518 vec![&1, &2, &3]
519 );
520 }
521
522 #[mz_ore::test]
525 fn test_column_known_bytes() {
526 let mut column: Column<i32> = Default::default();
527 column.push_into(1);
528 column.push_into(2);
529 column.push_into(3);
530 let mut data = Vec::new();
531 column.into_bytes(&mut std::io::Cursor::new(&mut data));
532 assert_eq!(data, raw_columnar_bytes());
533 }
534
535 #[mz_ore::test]
536 fn test_column_from_bytes() {
537 let raw = raw_columnar_bytes();
538
539 let buf = vec![0; raw.len() + 8];
540 let align = buf.as_ptr().align_offset(std::mem::size_of::<u64>());
541 let mut bytes_mut = BytesMut::from(buf);
542 let _ = bytes_mut.extract_to(align);
543 bytes_mut[..raw.len()].copy_from_slice(&raw);
544 let aligned_bytes = bytes_mut.extract_to(raw.len());
545
546 let column: Column<i32> = Column::from_bytes(aligned_bytes);
547 assert!(matches!(column, Column::Bytes(_)));
548 assert_eq!(
549 column.borrow().into_index_iter().collect::<Vec<_>>(),
550 vec![&1, &2, &3]
551 );
552
553 let buf = vec![0; raw.len() + 8];
554 let align = buf.as_ptr().align_offset(std::mem::size_of::<u64>());
555 let mut bytes_mut = BytesMut::from(buf);
556 let _ = bytes_mut.extract_to(align + 1);
557 bytes_mut[..raw.len()].copy_from_slice(&raw);
558 let unaligned_bytes = bytes_mut.extract_to(raw.len());
559
560 let column: Column<i32> = Column::from_bytes(unaligned_bytes);
561 assert!(matches!(column, Column::Align(_)));
562 assert_eq!(
563 column.borrow().into_index_iter().collect::<Vec<_>>(),
564 vec![&1, &2, &3]
565 );
566 }
567
568 #[mz_ore::test]
571 #[cfg_attr(miri, ignore)] fn ship_threshold_monotone() {
573 use columnar::Push;
574 let mut container = <Vec<u64> as Columnar>::Container::default();
575 let wide: Vec<u64> = vec![0u64; 50_000];
577 let mut fired = false;
578 for pushes in 1..=64 {
579 container.push(&wide);
580 let now = at_serialized_capacity(&container.borrow());
581 if fired {
582 assert!(now, "ship signal un-fired at {pushes} records");
583 }
584 fired = fired || now;
585 }
586 assert!(fired, "ship signal never fired");
587 }
588}