1use columnar::{Columnar, Index};
34use differential_dataflow::{AsCollection, Collection, VecCollection};
35use mz_repr::{DatumVec, DatumVecBorrow, Diff, Row};
36use mz_timely_util::columnar::Column;
37use mz_timely_util::columnar::builder::ColumnBuilder;
38use mz_timely_util::operator::CollectionExt;
39use timely::ContainerBuilder;
40use timely::container::CapacityContainerBuilder;
41use timely::dataflow::channels::pact::Pipeline;
42use timely::dataflow::operators::generic::builder_rc::OperatorBuilder;
43use timely::dataflow::operators::generic::{Operator, OutputBuilder};
44use timely::dataflow::{Scope, Stream, StreamVec};
45
46use crate::render::RenderTimestamp;
47use crate::render::context::{ECB, Session};
48use crate::render::errors::DataflowErrorSer;
49use crate::typedefs::KeyBatcher;
50
51pub type ColumnarCollection<'scope, T, D, R> = Collection<'scope, T, Column<(D, T, R)>>;
57
58#[derive(Clone)]
64pub enum CollectionEdge<'scope, T: RenderTimestamp> {
65 Vec(VecCollection<'scope, T, Row, Diff>),
67 Columnar(ColumnarCollection<'scope, T, Row, Diff>),
70}
71
72impl<'scope, T: RenderTimestamp> CollectionEdge<'scope, T> {
73 pub fn scope(&self) -> Scope<'scope, T> {
75 match self {
76 CollectionEdge::Vec(c) => c.inner.scope(),
77 CollectionEdge::Columnar(c) => c.inner.scope(),
78 }
79 }
80
81 pub fn enter_region<'inner>(self, region: Scope<'inner, T>) -> CollectionEdge<'inner, T> {
83 match self {
84 CollectionEdge::Vec(c) => CollectionEdge::Vec(c.enter_region(region)),
85 CollectionEdge::Columnar(c) => CollectionEdge::Columnar(c.enter_region(region)),
86 }
87 }
88
89 pub fn leave_region<'outer>(self, outer: Scope<'outer, T>) -> CollectionEdge<'outer, T> {
91 match self {
92 CollectionEdge::Vec(c) => CollectionEdge::Vec(c.leave_region(outer)),
93 CollectionEdge::Columnar(c) => CollectionEdge::Columnar(c.leave_region(outer)),
94 }
95 }
96
97 pub fn into_vec(self) -> VecCollection<'scope, T, Row, Diff> {
104 match self {
105 CollectionEdge::Vec(c) => c,
106 CollectionEdge::Columnar(c) => columnar_to_vec(c),
107 }
108 }
109
110 pub fn negate(self) -> Self {
115 match self {
116 CollectionEdge::Vec(c) => CollectionEdge::Vec(c.negate()),
117 CollectionEdge::Columnar(c) => CollectionEdge::Columnar(columnar_negate(c)),
118 }
119 }
120
121 pub fn concat_many<I>(scope: Scope<'scope, T>, edges: I) -> Self
128 where
129 I: IntoIterator<Item = Self>,
130 {
131 let mut vecs = Vec::new();
132 let mut cols = Vec::new();
133 for edge in edges {
134 match edge {
135 CollectionEdge::Vec(c) => vecs.push(c),
136 CollectionEdge::Columnar(c) => cols.push(c),
137 }
138 }
139 if cols.is_empty() {
140 CollectionEdge::Vec(differential_dataflow::collection::concatenate(scope, vecs))
141 } else {
142 cols.extend(vecs.into_iter().map(vec_to_columnar));
143 CollectionEdge::Columnar(differential_dataflow::collection::concatenate(scope, cols))
144 }
145 }
146
147 pub fn flat_map_datums<DCB, L>(
159 self,
160 max_demand: usize,
161 mut logic: L,
162 ) -> (
163 Stream<'scope, T, DCB::Container>,
164 StreamVec<'scope, T, (DataflowErrorSer, T, Diff)>,
165 )
166 where
167 DCB: ContainerBuilder,
168 L: for<'a> FnMut(
169 &'a mut DatumVecBorrow<'_>,
170 T,
171 Diff,
172 &mut Session<T, DCB>,
173 &mut Session<T, ECB<T>>,
174 ) -> usize
175 + 'static,
176 {
177 match self {
178 CollectionEdge::Vec(c) => {
179 let scope = c.inner.scope();
180 let mut builder = OperatorBuilder::new("CollectionFlatMap".to_string(), scope);
181 let (ok_output, ok_stream) = builder.new_output();
182 let mut ok_output = OutputBuilder::<_, DCB>::from(ok_output);
183 let (err_output, err_stream) = builder.new_output();
184 let mut err_output = OutputBuilder::<_, ECB<T>>::from(err_output);
185 let mut input = builder.new_input(c.inner, Pipeline);
186 builder.build(move |_capabilities| {
187 let mut datums = DatumVec::new();
188 move |_frontiers| {
189 let mut ok_output = ok_output.activate();
190 let mut err_output = err_output.activate();
191 input.for_each(|time, data| {
192 let ok_cap = time.retain(0);
195 let err_cap = time.retain(1);
196 let mut ok_session = ok_output.session_with_builder(&ok_cap);
197 let mut err_session = err_output.session_with_builder(&err_cap);
198 for (v, t, d) in data.drain(..) {
199 logic(
200 &mut datums.borrow_with_limit(&v, max_demand),
201 t,
202 d,
203 &mut ok_session,
204 &mut err_session,
205 );
206 }
207 });
208 }
209 });
210 (ok_stream, err_stream)
211 }
212 CollectionEdge::Columnar(c) => {
213 let scope = c.inner.scope();
214 let mut builder = OperatorBuilder::new("CollectionFlatMap".to_string(), scope);
215 let (ok_output, ok_stream) = builder.new_output();
216 let mut ok_output = OutputBuilder::<_, DCB>::from(ok_output);
217 let (err_output, err_stream) = builder.new_output();
218 let mut err_output = OutputBuilder::<_, ECB<T>>::from(err_output);
219 let mut input = builder.new_input(c.inner, Pipeline);
220 builder.build(move |_capabilities| {
221 let mut datums = DatumVec::new();
222 move |_frontiers| {
223 let mut ok_output = ok_output.activate();
224 let mut err_output = err_output.activate();
225 input.for_each(|time, data| {
226 let ok_cap = time.retain(0);
229 let err_cap = time.retain(1);
230 let mut ok_session = ok_output.session_with_builder(&ok_cap);
231 let mut err_session = err_output.session_with_builder(&err_cap);
232 for (v, t, d) in data.borrow().into_index_iter() {
235 logic(
236 &mut datums.borrow_with_limit(v, max_demand),
237 Columnar::into_owned(t),
238 Columnar::into_owned(d),
239 &mut ok_session,
240 &mut err_session,
241 );
242 }
243 });
244 }
245 });
246 (ok_stream, err_stream)
247 }
248 }
249 }
250
251 pub fn consolidate_named(self, name: &str) -> Self {
253 match self {
254 CollectionEdge::Vec(c) => CollectionEdge::Vec(CollectionExt::consolidate_named::<
255 KeyBatcher<_, _, _>,
256 >(c, name)),
257 CollectionEdge::Columnar(c) => {
258 let c = columnar_to_vec(c);
262 let c = CollectionExt::consolidate_named::<KeyBatcher<_, _, _>>(c, name);
263 CollectionEdge::Columnar(vec_to_columnar(c))
264 }
265 }
266 }
267}
268
269pub fn columnar_negate<'scope, T>(
280 collection: ColumnarCollection<'scope, T, Row, Diff>,
281) -> ColumnarCollection<'scope, T, Row, Diff>
282where
283 T: RenderTimestamp,
284{
285 collection
286 .inner
287 .unary::<ColumnBuilder<(Row, T, Diff)>, _, _, _>(
288 Pipeline,
289 "ColumnarNegate",
290 |_cap, _info| {
291 move |input, output| {
292 input.for_each(|time, data| {
293 let mut session = output.session_with_builder(&time);
294 for (v, t, d) in data.borrow().into_index_iter() {
295 let d = -Diff::into_owned(d);
296 session.give((v, t, &d));
297 }
298 });
299 }
300 },
301 )
302 .as_collection()
303}
304
305pub fn vec_to_columnar<'scope, T>(
311 collection: VecCollection<'scope, T, Row, Diff>,
312) -> ColumnarCollection<'scope, T, Row, Diff>
313where
314 T: RenderTimestamp,
315{
316 collection
317 .inner
318 .unary::<ColumnBuilder<(Row, T, Diff)>, _, _, _>(
319 Pipeline,
320 "VecToColumnar",
321 |_cap, _info| {
322 move |input, output| {
323 input.for_each(|time, data| {
324 let mut session = output.session_with_builder(&time);
325 for (v, t, d) in data.drain(..) {
326 session.give((&v, &t, &d));
327 }
328 });
329 }
330 },
331 )
332 .as_collection()
333}
334
335pub fn columnar_to_vec<'scope, T>(
342 collection: ColumnarCollection<'scope, T, Row, Diff>,
343) -> VecCollection<'scope, T, Row, Diff>
344where
345 T: RenderTimestamp,
346{
347 collection
348 .inner
349 .unary::<CapacityContainerBuilder<Vec<(Row, T, Diff)>>, _, _, _>(
350 Pipeline,
351 "ColumnarToVec",
352 |_cap, _info| {
353 move |input, output| {
354 input.for_each(|time, data| {
355 let mut session = output.session(&time);
356 for (v, t, d) in data.borrow().into_index_iter() {
357 session.give((
358 Columnar::into_owned(v),
359 Columnar::into_owned(t),
360 Columnar::into_owned(d),
361 ));
362 }
363 });
364 }
365 },
366 )
367 .as_collection()
368}
369
370#[cfg(test)]
371mod tests {
372 use differential_dataflow::input::Input;
373 use mz_ore::cast::CastFrom;
374 use mz_repr::{Datum, Timestamp};
375 use timely::dataflow::operators::Capture;
376 use timely::dataflow::operators::capture::{Event, Extract};
377
378 use super::*;
379
380 type RowBuilder = CapacityContainerBuilder<Vec<(Row, Timestamp, Diff)>>;
381 type CapturedRows = std::sync::mpsc::Receiver<Event<Timestamp, Vec<(Row, Timestamp, Diff)>>>;
382
383 fn extract_sorted(captured: CapturedRows) -> Vec<(Row, Timestamp, Diff)> {
384 let mut updates: Vec<_> = captured
385 .extract()
386 .into_iter()
387 .flat_map(|(_, data)| data)
388 .collect();
389 updates.sort();
390 updates
391 }
392
393 fn test_rows() -> Vec<Row> {
394 vec![
395 Row::pack_slice(&[Datum::Int32(42), Datum::String("hello")]),
396 Row::pack_slice(&[Datum::Int64(100), Datum::Null]),
397 Row::pack_slice(&[Datum::True, Datum::False, Datum::Null]),
398 Row::default(),
399 ]
400 }
401
402 #[mz_ore::test]
403 fn round_trip_through_columnar() {
404 let rows = test_rows();
405 let expected: Vec<_> = {
406 let mut updates: Vec<_> = rows
407 .iter()
408 .enumerate()
409 .map(|(i, r)| (r.clone(), Timestamp::from(u64::cast_from(i / 2)), Diff::ONE))
410 .collect();
411 updates.sort();
412 updates
413 };
414 let captured = timely::execute_directly(move |worker| {
415 worker.dataflow::<Timestamp, _, _>(|scope| {
416 let (mut input, collection) = scope.new_collection();
417 let captured = columnar_to_vec(vec_to_columnar(collection)).inner.capture();
418 for (i, row) in rows.into_iter().enumerate() {
419 input.advance_to(Timestamp::from(u64::cast_from(i / 2)));
420 input.update(row, Diff::ONE);
421 }
422 input.advance_to(Timestamp::from(2_u64));
423 input.flush();
424 captured
425 })
426 });
427 assert_eq!(extract_sorted(captured), expected);
428 }
429
430 #[mz_ore::test]
431 fn negate_flips_diffs_on_columnar_arm() {
432 let rows = test_rows();
433 let expected: Vec<_> = {
434 let mut updates: Vec<_> = rows
435 .iter()
436 .map(|r| (r.clone(), Timestamp::from(0_u64), -Diff::ONE))
437 .collect();
438 updates.sort();
439 updates
440 };
441 let captured = timely::execute_directly(move |worker| {
442 worker.dataflow::<Timestamp, _, _>(|scope| {
443 let (mut input, collection) = scope.new_collection();
444 let edge = CollectionEdge::Columnar(vec_to_columnar(collection)).negate();
445 assert!(matches!(edge, CollectionEdge::Columnar(_)));
446 let captured = edge.into_vec().inner.capture();
447 for row in rows {
448 input.update(row, Diff::ONE);
449 }
450 input.advance_to(Timestamp::from(1_u64));
451 input.flush();
452 captured
453 })
454 });
455 assert_eq!(extract_sorted(captured), expected);
456 }
457
458 #[mz_ore::test]
459 fn concat_many_mixed_upgrades_to_columnar() {
460 let rows = test_rows();
461 let expected: Vec<_> = {
462 let mut updates: Vec<_> = rows
463 .iter()
464 .map(|r| (r.clone(), Timestamp::from(0_u64), Diff::ONE))
465 .collect();
466 updates.push((rows[0].clone(), Timestamp::from(0_u64), Diff::ONE));
468 updates.sort();
469 updates
470 };
471 let captured = timely::execute_directly(move |worker| {
472 worker.dataflow::<Timestamp, _, _>(|scope| {
473 let (mut input1, collection1) = scope.new_collection();
474 let (mut input2, collection2) = scope.new_collection();
475 let edge = CollectionEdge::concat_many(
476 scope,
477 [
478 CollectionEdge::Vec(collection1),
479 CollectionEdge::Columnar(vec_to_columnar(collection2)),
480 ],
481 );
482 assert!(matches!(edge, CollectionEdge::Columnar(_)));
483 let captured = edge.into_vec().inner.capture();
484 let (first, rest) = rows.split_first().unwrap();
485 input1.update(first.clone(), Diff::ONE);
486 input2.update(first.clone(), Diff::ONE);
487 for row in rest {
488 input1.update(row.clone(), Diff::ONE);
489 }
490 for input in [&mut input1, &mut input2] {
491 input.advance_to(Timestamp::from(1_u64));
492 input.flush();
493 }
494 captured
495 })
496 });
497 assert_eq!(extract_sorted(captured), expected);
498 }
499
500 #[mz_ore::test]
501 fn flat_map_datums_arms_agree() {
502 let rows = test_rows();
505 let (vec_captured, col_captured) = timely::execute_directly(move |worker| {
506 worker.dataflow::<Timestamp, _, _>(|scope| {
507 let (mut input, collection) = scope.new_collection();
508 let mut captures = Vec::new();
509 for edge in [
510 CollectionEdge::Vec(collection.clone()),
511 CollectionEdge::Columnar(vec_to_columnar(collection)),
512 ] {
513 let (oks, _errs) = edge.flat_map_datums::<RowBuilder, _>(
514 1,
515 |datums, t, d, ok_session, _err_session| {
516 ok_session.give((Row::pack(datums.iter()), t, d));
517 1
518 },
519 );
520 captures.push(oks.capture());
521 }
522 let col = captures.pop().unwrap();
523 let vec = captures.pop().unwrap();
524 for row in rows {
525 input.update(row, Diff::ONE);
526 }
527 input.advance_to(Timestamp::from(1_u64));
528 input.flush();
529 (vec, col)
530 })
531 });
532 let vec_updates = extract_sorted(vec_captured);
533 assert_eq!(vec_updates, extract_sorted(col_captured));
534 assert!(vec_updates.iter().all(|(r, _, _)| r.iter().count() <= 1));
536 }
537
538 #[mz_ore::test]
539 fn consolidate_named_preserves_columnar() {
540 let row1 = Row::pack_slice(&[Datum::Int32(1)]);
541 let row2 = Row::pack_slice(&[Datum::Int32(2)]);
542 let expected = vec![(row1.clone(), Timestamp::from(0_u64), Diff::from(2))];
543 let captured = timely::execute_directly(move |worker| {
544 worker.dataflow::<Timestamp, _, _>(|scope| {
545 let (mut input, collection) = scope.new_collection();
546 let edge =
547 CollectionEdge::Columnar(vec_to_columnar(collection)).consolidate_named("Test");
548 assert!(matches!(edge, CollectionEdge::Columnar(_)));
549 let captured = edge.into_vec().inner.capture();
550 input.update(row1.clone(), Diff::ONE);
552 input.update(row1, Diff::ONE);
553 input.update(row2.clone(), Diff::ONE);
554 input.update(row2, -Diff::ONE);
555 input.advance_to(Timestamp::from(1_u64));
556 input.flush();
557 captured
558 })
559 });
560 assert_eq!(extract_sorted(captured), expected);
561 }
562}