1#![deny(missing_docs)]
19
20pub mod batcher;
21pub mod builder;
22pub mod builder_input;
23pub mod chunk;
24pub mod consolidate;
25pub mod merge_batcher;
26pub mod unload;
27
28use std::hash::{BuildHasher, Hash, Hasher};
29use std::sync::LazyLock;
30
31use columnar::Borrow;
32use columnar::bytes::indexed;
33use columnar::common::IterOwn;
34use columnar::{Clear, FromBytes, Index, Len};
35use columnar::{Columnar, Ref};
36use differential_dataflow::Hashable;
37use differential_dataflow::collection::containers::Enter;
38use differential_dataflow::trace::implementations::merge_batcher::MergeBatcher;
39use timely::Accountable;
40use timely::bytes::arc::Bytes;
41use timely::container::{DrainContainer, PushInto, SizableContainer};
42use timely::dataflow::channels::ContainerBytes;
43use timely::progress::Timestamp;
44use timely::progress::timestamp::Refines;
45
46use crate::columnation::ColInternalMerger;
47
48pub type Col2ValBatcher<K, V, T, R> = MergeBatcher<ColInternalMerger<(K, V), T, R>>;
55pub type Col2KeyBatcher<K, T, R> = Col2ValBatcher<K, (), T, R>;
57
58pub type Col2ValPagedBatcher<K, V, T, R> = merge_batcher::ColumnMergeBatcher<(K, V), T, R>;
70
71pub type Col2ValColBatcher<K, V, T, R> = MergeBatcher<batcher::ColumnMerger<(K, V), T, R>>;
79
80pub enum Column<C: Columnar> {
86 Typed(C::Container),
88 Bytes(Bytes),
90 Align(Vec<u64>),
97}
98
99impl<C: Columnar> Column<C> {
100 #[inline]
107 pub fn clear(&mut self) {
108 match self {
109 Column::Typed(t) => t.clear(),
110 Column::Bytes(_) | Column::Align(_) => *self = Default::default(),
111 }
112 }
113
114 #[inline]
120 pub fn is_empty(&self) -> bool {
121 match self {
122 Column::Typed(t) => t.is_empty(),
123 Column::Bytes(_) | Column::Align(_) => self.borrow().is_empty(),
124 }
125 }
126
127 #[inline]
129 pub fn borrow(&self) -> <C::Container as Borrow>::Borrowed<'_> {
130 match self {
131 Column::Typed(t) => t.borrow(),
132 Column::Bytes(b) => <<C::Container as Borrow>::Borrowed<'_>>::from_bytes(
133 &mut indexed::decode(bytemuck::cast_slice(b)),
134 ),
135 Column::Align(a) => {
136 <<C::Container as Borrow>::Borrowed<'_>>::from_bytes(&mut indexed::decode(a))
137 }
138 }
139 }
140}
141
142impl<C: Columnar> Default for Column<C> {
143 fn default() -> Self {
144 Self::Typed(Default::default())
145 }
146}
147
148impl<C: Columnar> Clone for Column<C>
149where
150 C::Container: Clone,
151{
152 fn clone(&self) -> Self {
153 match self {
154 Column::Typed(t) => Column::Typed(t.clone()),
157 Column::Bytes(b) => {
158 assert_eq!(b.len() % 8, 0);
159 Self::Align(bytemuck::allocation::pod_collect_to_vec(b))
160 }
161 Column::Align(a) => Column::Align(a.clone()),
162 }
163 }
164}
165
166impl<C: Columnar> Accountable for Column<C> {
167 #[inline]
168 fn record_count(&self) -> i64 {
169 self.borrow().len().try_into().expect("Must fit")
170 }
171}
172impl<C: Columnar> DrainContainer for Column<C> {
173 type Item<'a> = Ref<'a, C>;
174 type DrainIter<'a> = IterOwn<<C::Container as Borrow>::Borrowed<'a>>;
175 #[inline]
176 fn drain(&mut self) -> Self::DrainIter<'_> {
177 self.borrow().into_index_iter()
178 }
179}
180
181impl<C: Columnar, T> PushInto<T> for Column<C>
182where
183 C::Container: columnar::Push<T>,
184{
185 #[inline]
186 fn push_into(&mut self, item: T) {
187 use columnar::Push;
188 match self {
189 Column::Typed(t) => t.push(item),
190 Column::Align(_) | Column::Bytes(_) => {
191 unimplemented!("Pushing into Column::Bytes without first clearing");
194 }
195 }
196 }
197}
198
199impl<D, T1, T2, R> Enter<T1, T2> for Column<(D, T1, R)>
202where
203 D: Columnar,
204 T1: Columnar + Timestamp,
205 T2: Columnar + Refines<T1>,
206 R: Columnar,
207 (D, T1, R): Columnar<Container = (D::Container, T1::Container, R::Container)>,
208 (D, T2, R): Columnar<Container = (D::Container, T2::Container, R::Container)>,
209 for<'a> D::Container: columnar::Push<Ref<'a, D>>,
210 for<'a> T2::Container: columnar::Push<&'a T2>,
211 for<'a> R::Container: columnar::Push<Ref<'a, R>>,
212{
213 type InnerContainer = Column<(D, T2, R)>;
214
215 fn enter(self) -> Self::InnerContainer {
216 use columnar::Push;
217 match self {
218 Column::Typed((data, times, diffs)) => {
220 let mut inner = T2::Container::default();
221 for time in times.borrow().into_index_iter() {
222 inner.push(&T2::to_inner(T1::into_owned(time)));
223 }
224 Column::Typed((data, inner, diffs))
225 }
226 serialized => {
231 let (borrowed_data, borrowed_times, borrowed_diffs) = serialized.borrow();
232 let mut times = T2::Container::default();
233 for time in borrowed_times.into_index_iter() {
234 times.push(&T2::to_inner(T1::into_owned(time)));
235 }
236 let view = (borrowed_data, times.borrow(), borrowed_diffs);
237 let words = indexed::length_in_words(&view);
238 let mut alloc: Vec<u64> = Vec::with_capacity(words);
239 indexed::encode(&mut alloc, &view);
240 Column::Align(alloc)
241 }
242 }
243 }
244}
245
246const SHIP_WORDS: usize = 1 << 18;
251
252#[inline]
263pub(crate) fn at_serialized_capacity<'a, A>(borrow: &A) -> bool
264where
265 A: columnar::AsBytes<'a>,
266{
267 indexed::length_in_words(borrow) >= SHIP_WORDS - SHIP_WORDS / 10
268}
269
270impl<C: Columnar> SizableContainer for Column<C> {
271 fn at_capacity(&self) -> bool {
272 match self {
280 Column::Typed(c) => at_serialized_capacity(&c.borrow()),
281 Column::Bytes(_) | Column::Align(_) => true,
282 }
283 }
284
285 fn ensure_capacity(&mut self, _stash: &mut Option<Self>) {
286 }
293}
294
295impl<C: Columnar> ContainerBytes for Column<C> {
296 #[inline]
297 fn from_bytes(bytes: Bytes) -> Self {
298 assert_eq!(bytes.len() % 8, 0);
304 if let Ok(_) = bytemuck::try_cast_slice::<_, u64>(&bytes) {
305 Self::Bytes(bytes)
306 } else {
307 Self::Align(bytemuck::allocation::pod_collect_to_vec(&bytes[..]))
310 }
311 }
312
313 #[inline]
314 fn length_in_bytes(&self) -> usize {
315 match self {
316 Column::Typed(t) => indexed::length_in_bytes(&t.borrow()),
317 Column::Bytes(b) => b.len(),
318 Column::Align(a) => 8 * a.len(),
319 }
320 }
321
322 #[inline]
323 fn into_bytes<W: ::std::io::Write>(&self, writer: &mut W) {
324 match self {
325 Column::Typed(t) => indexed::write(writer, &t.borrow()).unwrap(),
326 Column::Bytes(b) => writer.write_all(b).unwrap(),
327 Column::Align(a) => writer.write_all(bytemuck::cast_slice(a)).unwrap(),
328 }
329 }
330}
331
332#[inline(always)]
336pub fn columnar_exchange<K, V, T, D>(((k, _), _, _): &Ref<'_, ((K, V), T, D)>) -> u64
337where
338 K: Columnar,
339 for<'a> Ref<'a, K>: Hash,
340 V: Columnar,
341 D: Columnar,
342 T: Columnar,
343{
344 k.hashed()
345}
346
347pub fn columnar_exchange_data<D, T, R>((d, _, _): &Ref<'_, (D, T, R)>) -> u64
353where
354 D: Columnar,
355 for<'a> Ref<'a, D>: Hash,
356 T: Columnar,
357 R: Columnar,
358{
359 d.hashed()
360}
361
362pub fn columnar_consolidate_exchange<D, T, R>((d, _, _): &Ref<'_, (D, T, R)>) -> u64
371where
372 D: Columnar,
373 for<'a> Ref<'a, D>: Hash,
374 T: Columnar,
375 R: Columnar,
376{
377 static STATE: LazyLock<ahash::RandomState> = LazyLock::new(crate::hash::fixed_state);
378 let mut hasher = STATE.build_hasher();
379 d.hash(&mut hasher);
380 hasher.finish()
381}
382
383#[cfg(test)]
384mod tests {
385 use timely::bytes::arc::BytesMut;
386 use timely::container::PushInto;
387 use timely::dataflow::channels::ContainerBytes;
388
389 use super::*;
390
391 fn raw_columnar_bytes() -> Vec<u8> {
393 let mut raw = Vec::new();
394 raw.extend(16_u64.to_le_bytes()); raw.extend(28_u64.to_le_bytes()); raw.extend(1_i32.to_le_bytes());
397 raw.extend(2_i32.to_le_bytes());
398 raw.extend(3_i32.to_le_bytes());
399 raw.extend([0, 0, 0, 0]); raw
401 }
402
403 #[mz_ore::test]
404 fn test_column_clone() {
405 let columns = Columnar::as_columns([1, 2, 3].iter());
406 let column_typed: Column<i32> = Column::Typed(columns);
407 let column_typed2 = column_typed.clone();
408
409 assert_eq!(
410 column_typed2.borrow().into_index_iter().collect::<Vec<_>>(),
411 vec![&1, &2, &3]
412 );
413
414 let bytes = BytesMut::from(raw_columnar_bytes()).freeze();
415 let column_bytes: Column<i32> = Column::Bytes(bytes);
416 let column_bytes2 = column_bytes.clone();
417
418 assert_eq!(
419 column_bytes2.borrow().into_index_iter().collect::<Vec<_>>(),
420 vec![&1, &2, &3]
421 );
422
423 let raw = raw_columnar_bytes();
424 let mut region: Vec<u64> = vec![0; raw.len() / 8];
425 let region_bytes = bytemuck::cast_slice_mut(&mut region[..]);
426 region_bytes[..raw.len()].copy_from_slice(&raw);
427 let column_align: Column<i32> = Column::Align(region);
428 let column_align2 = column_align.clone();
429
430 assert_eq!(
431 column_align2.borrow().into_index_iter().collect::<Vec<_>>(),
432 vec![&1, &2, &3]
433 );
434 }
435
436 #[mz_ore::test]
439 fn test_column_known_bytes() {
440 let mut column: Column<i32> = Default::default();
441 column.push_into(1);
442 column.push_into(2);
443 column.push_into(3);
444 let mut data = Vec::new();
445 column.into_bytes(&mut std::io::Cursor::new(&mut data));
446 assert_eq!(data, raw_columnar_bytes());
447 }
448
449 #[mz_ore::test]
450 fn test_column_from_bytes() {
451 let raw = raw_columnar_bytes();
452
453 let buf = vec![0; raw.len() + 8];
454 let align = buf.as_ptr().align_offset(std::mem::size_of::<u64>());
455 let mut bytes_mut = BytesMut::from(buf);
456 let _ = bytes_mut.extract_to(align);
457 bytes_mut[..raw.len()].copy_from_slice(&raw);
458 let aligned_bytes = bytes_mut.extract_to(raw.len());
459
460 let column: Column<i32> = Column::from_bytes(aligned_bytes);
461 assert!(matches!(column, Column::Bytes(_)));
462 assert_eq!(
463 column.borrow().into_index_iter().collect::<Vec<_>>(),
464 vec![&1, &2, &3]
465 );
466
467 let buf = vec![0; raw.len() + 8];
468 let align = buf.as_ptr().align_offset(std::mem::size_of::<u64>());
469 let mut bytes_mut = BytesMut::from(buf);
470 let _ = bytes_mut.extract_to(align + 1);
471 bytes_mut[..raw.len()].copy_from_slice(&raw);
472 let unaligned_bytes = bytes_mut.extract_to(raw.len());
473
474 let column: Column<i32> = Column::from_bytes(unaligned_bytes);
475 assert!(matches!(column, Column::Align(_)));
476 assert_eq!(
477 column.borrow().into_index_iter().collect::<Vec<_>>(),
478 vec![&1, &2, &3]
479 );
480 }
481
482 #[mz_ore::test]
485 #[cfg_attr(miri, ignore)] fn ship_threshold_monotone() {
487 use columnar::Push;
488 let mut container = <Vec<u64> as Columnar>::Container::default();
489 let wide: Vec<u64> = vec![0u64; 50_000];
491 let mut fired = false;
492 for pushes in 1..=64 {
493 container.push(&wide);
494 let now = at_serialized_capacity(&container.borrow());
495 if fired {
496 assert!(now, "ship signal un-fired at {pushes} records");
497 }
498 fired = fired || now;
499 }
500 assert!(fired, "ship signal never fired");
501 }
502}