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::Hash;
29
30use columnar::Borrow;
31use columnar::bytes::indexed;
32use columnar::common::IterOwn;
33use columnar::{Clear, FromBytes, Index, Len};
34use columnar::{Columnar, Ref};
35use differential_dataflow::Hashable;
36use differential_dataflow::trace::implementations::merge_batcher::MergeBatcher;
37use timely::Accountable;
38use timely::bytes::arc::Bytes;
39use timely::container::{DrainContainer, PushInto, SizableContainer};
40use timely::dataflow::channels::ContainerBytes;
41
42use crate::columnation::ColInternalMerger;
43
44pub type Col2ValBatcher<K, V, T, R> = MergeBatcher<ColInternalMerger<(K, V), T, R>>;
51pub type Col2KeyBatcher<K, T, R> = Col2ValBatcher<K, (), T, R>;
53
54pub type Col2ValPagedBatcher<K, V, T, R> = merge_batcher::ColumnMergeBatcher<(K, V), T, R>;
66
67pub type Col2ValColBatcher<K, V, T, R> = MergeBatcher<batcher::ColumnMerger<(K, V), T, R>>;
75
76pub enum Column<C: Columnar> {
82 Typed(C::Container),
84 Bytes(Bytes),
86 Align(Vec<u64>),
93}
94
95impl<C: Columnar> Column<C> {
96 #[inline]
103 pub fn clear(&mut self) {
104 match self {
105 Column::Typed(t) => t.clear(),
106 Column::Bytes(_) | Column::Align(_) => *self = Default::default(),
107 }
108 }
109
110 #[inline]
116 pub fn is_empty(&self) -> bool {
117 match self {
118 Column::Typed(t) => t.is_empty(),
119 Column::Bytes(_) | Column::Align(_) => self.borrow().is_empty(),
120 }
121 }
122
123 #[inline]
125 pub fn borrow(&self) -> <C::Container as Borrow>::Borrowed<'_> {
126 match self {
127 Column::Typed(t) => t.borrow(),
128 Column::Bytes(b) => <<C::Container as Borrow>::Borrowed<'_>>::from_bytes(
129 &mut indexed::decode(bytemuck::cast_slice(b)),
130 ),
131 Column::Align(a) => {
132 <<C::Container as Borrow>::Borrowed<'_>>::from_bytes(&mut indexed::decode(a))
133 }
134 }
135 }
136}
137
138impl<C: Columnar> Default for Column<C> {
139 fn default() -> Self {
140 Self::Typed(Default::default())
141 }
142}
143
144impl<C: Columnar> Clone for Column<C>
145where
146 C::Container: Clone,
147{
148 fn clone(&self) -> Self {
149 match self {
150 Column::Typed(t) => Column::Typed(t.clone()),
153 Column::Bytes(b) => {
154 assert_eq!(b.len() % 8, 0);
155 Self::Align(bytemuck::allocation::pod_collect_to_vec(b))
156 }
157 Column::Align(a) => Column::Align(a.clone()),
158 }
159 }
160}
161
162impl<C: Columnar> Accountable for Column<C> {
163 #[inline]
164 fn record_count(&self) -> i64 {
165 self.borrow().len().try_into().expect("Must fit")
166 }
167}
168impl<C: Columnar> DrainContainer for Column<C> {
169 type Item<'a> = Ref<'a, C>;
170 type DrainIter<'a> = IterOwn<<C::Container as Borrow>::Borrowed<'a>>;
171 #[inline]
172 fn drain(&mut self) -> Self::DrainIter<'_> {
173 self.borrow().into_index_iter()
174 }
175}
176
177impl<C: Columnar, T> PushInto<T> for Column<C>
178where
179 C::Container: columnar::Push<T>,
180{
181 #[inline]
182 fn push_into(&mut self, item: T) {
183 use columnar::Push;
184 match self {
185 Column::Typed(t) => t.push(item),
186 Column::Align(_) | Column::Bytes(_) => {
187 unimplemented!("Pushing into Column::Bytes without first clearing");
190 }
191 }
192 }
193}
194
195const SHIP_WORDS: usize = 1 << 18;
200
201#[inline]
212pub(crate) fn at_serialized_capacity<'a, A>(borrow: &A) -> bool
213where
214 A: columnar::AsBytes<'a>,
215{
216 indexed::length_in_words(borrow) >= SHIP_WORDS - SHIP_WORDS / 10
217}
218
219impl<C: Columnar> SizableContainer for Column<C> {
220 fn at_capacity(&self) -> bool {
221 match self {
229 Column::Typed(c) => at_serialized_capacity(&c.borrow()),
230 Column::Bytes(_) | Column::Align(_) => true,
231 }
232 }
233
234 fn ensure_capacity(&mut self, _stash: &mut Option<Self>) {
235 }
242}
243
244impl<C: Columnar> ContainerBytes for Column<C> {
245 #[inline]
246 fn from_bytes(bytes: Bytes) -> Self {
247 assert_eq!(bytes.len() % 8, 0);
253 if let Ok(_) = bytemuck::try_cast_slice::<_, u64>(&bytes) {
254 Self::Bytes(bytes)
255 } else {
256 Self::Align(bytemuck::allocation::pod_collect_to_vec(&bytes[..]))
259 }
260 }
261
262 #[inline]
263 fn length_in_bytes(&self) -> usize {
264 match self {
265 Column::Typed(t) => indexed::length_in_bytes(&t.borrow()),
266 Column::Bytes(b) => b.len(),
267 Column::Align(a) => 8 * a.len(),
268 }
269 }
270
271 #[inline]
272 fn into_bytes<W: ::std::io::Write>(&self, writer: &mut W) {
273 match self {
274 Column::Typed(t) => indexed::write(writer, &t.borrow()).unwrap(),
275 Column::Bytes(b) => writer.write_all(b).unwrap(),
276 Column::Align(a) => writer.write_all(bytemuck::cast_slice(a)).unwrap(),
277 }
278 }
279}
280
281#[inline(always)]
285pub fn columnar_exchange<K, V, T, D>(((k, _), _, _): &Ref<'_, ((K, V), T, D)>) -> u64
286where
287 K: Columnar,
288 for<'a> Ref<'a, K>: Hash,
289 V: Columnar,
290 D: Columnar,
291 T: Columnar,
292{
293 k.hashed()
294}
295
296#[cfg(test)]
297mod tests {
298 use timely::bytes::arc::BytesMut;
299 use timely::container::PushInto;
300 use timely::dataflow::channels::ContainerBytes;
301
302 use super::*;
303
304 fn raw_columnar_bytes() -> Vec<u8> {
306 let mut raw = Vec::new();
307 raw.extend(16_u64.to_le_bytes()); raw.extend(28_u64.to_le_bytes()); raw.extend(1_i32.to_le_bytes());
310 raw.extend(2_i32.to_le_bytes());
311 raw.extend(3_i32.to_le_bytes());
312 raw.extend([0, 0, 0, 0]); raw
314 }
315
316 #[mz_ore::test]
317 fn test_column_clone() {
318 let columns = Columnar::as_columns([1, 2, 3].iter());
319 let column_typed: Column<i32> = Column::Typed(columns);
320 let column_typed2 = column_typed.clone();
321
322 assert_eq!(
323 column_typed2.borrow().into_index_iter().collect::<Vec<_>>(),
324 vec![&1, &2, &3]
325 );
326
327 let bytes = BytesMut::from(raw_columnar_bytes()).freeze();
328 let column_bytes: Column<i32> = Column::Bytes(bytes);
329 let column_bytes2 = column_bytes.clone();
330
331 assert_eq!(
332 column_bytes2.borrow().into_index_iter().collect::<Vec<_>>(),
333 vec![&1, &2, &3]
334 );
335
336 let raw = raw_columnar_bytes();
337 let mut region: Vec<u64> = vec![0; raw.len() / 8];
338 let region_bytes = bytemuck::cast_slice_mut(&mut region[..]);
339 region_bytes[..raw.len()].copy_from_slice(&raw);
340 let column_align: Column<i32> = Column::Align(region);
341 let column_align2 = column_align.clone();
342
343 assert_eq!(
344 column_align2.borrow().into_index_iter().collect::<Vec<_>>(),
345 vec![&1, &2, &3]
346 );
347 }
348
349 #[mz_ore::test]
352 fn test_column_known_bytes() {
353 let mut column: Column<i32> = Default::default();
354 column.push_into(1);
355 column.push_into(2);
356 column.push_into(3);
357 let mut data = Vec::new();
358 column.into_bytes(&mut std::io::Cursor::new(&mut data));
359 assert_eq!(data, raw_columnar_bytes());
360 }
361
362 #[mz_ore::test]
363 fn test_column_from_bytes() {
364 let raw = raw_columnar_bytes();
365
366 let buf = vec![0; raw.len() + 8];
367 let align = buf.as_ptr().align_offset(std::mem::size_of::<u64>());
368 let mut bytes_mut = BytesMut::from(buf);
369 let _ = bytes_mut.extract_to(align);
370 bytes_mut[..raw.len()].copy_from_slice(&raw);
371 let aligned_bytes = bytes_mut.extract_to(raw.len());
372
373 let column: Column<i32> = Column::from_bytes(aligned_bytes);
374 assert!(matches!(column, Column::Bytes(_)));
375 assert_eq!(
376 column.borrow().into_index_iter().collect::<Vec<_>>(),
377 vec![&1, &2, &3]
378 );
379
380 let buf = vec![0; raw.len() + 8];
381 let align = buf.as_ptr().align_offset(std::mem::size_of::<u64>());
382 let mut bytes_mut = BytesMut::from(buf);
383 let _ = bytes_mut.extract_to(align + 1);
384 bytes_mut[..raw.len()].copy_from_slice(&raw);
385 let unaligned_bytes = bytes_mut.extract_to(raw.len());
386
387 let column: Column<i32> = Column::from_bytes(unaligned_bytes);
388 assert!(matches!(column, Column::Align(_)));
389 assert_eq!(
390 column.borrow().into_index_iter().collect::<Vec<_>>(),
391 vec![&1, &2, &3]
392 );
393 }
394
395 #[mz_ore::test]
398 #[cfg_attr(miri, ignore)] fn ship_threshold_monotone() {
400 use columnar::Push;
401 let mut container = <Vec<u64> as Columnar>::Container::default();
402 let wide: Vec<u64> = vec![0u64; 50_000];
404 let mut fired = false;
405 for pushes in 1..=64 {
406 container.push(&wide);
407 let now = at_serialized_capacity(&container.borrow());
408 if fired {
409 assert!(now, "ship signal un-fired at {pushes} records");
410 }
411 fired = fired || now;
412 }
413 assert!(fired, "ship signal never fired");
414 }
415}