1use columnar::{Borrow, Columnar, Container, Index, Len, Push};
24use differential_dataflow::{AsCollection, Collection, VecCollection};
25use mz_repr::{DatumVec, DatumVecBorrow, Diff, Row};
26use mz_timely_util::columnar::Column;
27use mz_timely_util::columnar::batcher::ColumnChunker;
28use mz_timely_util::columnar::builder::ColumnBuilder;
29use mz_timely_util::columnar::columnar_consolidate_exchange;
30use mz_timely_util::columnar::merge_batcher::ColumnMergeBatcher;
31use mz_timely_util::operator::consolidate_pact;
32use timely::ContainerBuilder;
33use timely::container::CapacityContainerBuilder;
34use timely::dataflow::channels::pact::{ExchangeCore, Pipeline};
35use timely::dataflow::operators::generic::builder_rc::OperatorBuilder;
36use timely::dataflow::operators::generic::{Operator, OutputBuilder};
37use timely::dataflow::{Scope, Stream, StreamVec};
38
39use crate::render::RenderTimestamp;
40use crate::render::context::{ECB, Session};
41use crate::render::errors::DataflowErrorSer;
42
43pub type ColumnarCollection<'scope, T, D, R> = Collection<'scope, T, Column<(D, T, R)>>;
49
50pub type CollectionEdge<'scope, T> = ColumnarCollection<'scope, T, Row, Diff>;
52
53pub fn concat_many<'scope, T, I>(scope: Scope<'scope, T>, edges: I) -> CollectionEdge<'scope, T>
55where
56 T: RenderTimestamp,
57 I: IntoIterator<Item = CollectionEdge<'scope, T>>,
58{
59 let cols: Vec<_> = edges.into_iter().collect();
60 differential_dataflow::collection::concatenate(scope, cols)
61}
62
63pub fn flat_map_datums<'scope, T, DCB, L>(
73 edge: CollectionEdge<'scope, T>,
74 max_demand: usize,
75 mut logic: L,
76) -> (
77 Stream<'scope, T, DCB::Container>,
78 StreamVec<'scope, T, (DataflowErrorSer, T, Diff)>,
79)
80where
81 T: RenderTimestamp,
82 DCB: ContainerBuilder,
83 L: for<'a> FnMut(
84 &'a mut DatumVecBorrow<'_>,
85 T,
86 Diff,
87 &mut Session<T, DCB>,
88 &mut Session<T, ECB<T>>,
89 ) -> usize
90 + 'static,
91{
92 let scope = edge.inner.scope();
93 let mut builder = OperatorBuilder::new("CollectionFlatMap".to_string(), scope);
94 let (ok_output, ok_stream) = builder.new_output();
95 let mut ok_output = OutputBuilder::<_, DCB>::from(ok_output);
96 let (err_output, err_stream) = builder.new_output();
97 let mut err_output = OutputBuilder::<_, ECB<T>>::from(err_output);
98 let mut input = builder.new_input(edge.inner, Pipeline);
99 builder.build(move |_capabilities| {
100 let mut datums = DatumVec::new();
101 move |_frontiers| {
102 let mut ok_output = ok_output.activate();
103 let mut err_output = err_output.activate();
104 input.for_each(|time, data| {
105 let ok_cap = time.retain(0);
108 let err_cap = time.retain(1);
109 let mut ok_session = ok_output.session_with_builder(&ok_cap);
110 let mut err_session = err_output.session_with_builder(&err_cap);
111 for (v, t, d) in data.borrow().into_index_iter() {
114 logic(
115 &mut datums.borrow_with_limit(v, max_demand),
116 Columnar::into_owned(t),
117 Columnar::into_owned(d),
118 &mut ok_session,
119 &mut err_session,
120 );
121 }
122 });
123 }
124 });
125 (ok_stream, err_stream)
126}
127
128fn negate_column<T>(column: Column<(Row, T, Diff)>) -> Column<(Row, T, Diff)>
144where
145 T: RenderTimestamp,
146{
147 fn negated_diffs<'a, D>(diffs: &'a D) -> <Diff as Columnar>::Container
149 where
150 D: Len + Index<Ref = Diff> + 'a,
151 {
152 let mut negated = <Diff as Columnar>::Container::default();
153 for index in 0..diffs.len() {
154 negated.push(-diffs.get(index));
155 }
156 negated
157 }
158
159 match column {
160 Column::Typed((rows, times, diffs)) => {
161 let negated = negated_diffs(&diffs.borrow());
162 Column::Typed((rows, times, negated))
163 }
164 column => {
165 let view = column.borrow();
166 let len = view.len();
167 let mut negated = <(Row, T, Diff) as Columnar>::Container::default();
168 let (rows, times, diffs) = &mut negated;
169 rows.extend_from_self(view.0, 0..len);
170 times.extend_from_self(view.1, 0..len);
171 *diffs = negated_diffs(&view.2);
172 Column::Typed(negated)
173 }
174 }
175}
176
177pub fn columnar_negate<'scope, T>(
179 collection: ColumnarCollection<'scope, T, Row, Diff>,
180) -> ColumnarCollection<'scope, T, Row, Diff>
181where
182 T: RenderTimestamp,
183{
184 collection
185 .inner
186 .unary::<CapacityContainerBuilder<Column<(Row, T, Diff)>>, _, _, _>(
187 Pipeline,
188 "ColumnarNegate",
189 |_cap, _info| {
190 move |input, output| {
191 input.for_each(|time, data| {
192 let mut negated = negate_column(std::mem::take(data));
193 output.session(&time).give_container(&mut negated);
194 });
195 }
196 },
197 )
198 .as_collection()
199}
200
201pub fn columnar_consolidate<'scope, T>(
211 collection: ColumnarCollection<'scope, T, Row, Diff>,
212 name: &str,
213) -> ColumnarCollection<'scope, T, Row, Diff>
214where
215 T: RenderTimestamp,
216{
217 let exchange = ExchangeCore::<ColumnBuilder<_>, _>::new_core(
221 columnar_consolidate_exchange::<Row, T, Diff>,
222 );
223 let consolidated = consolidate_pact::<
224 ColumnChunker<(Row, T, Diff)>,
225 ColumnMergeBatcher<Row, T, Diff>,
226 _,
227 _,
228 >(collection.inner, exchange, name);
229
230 consolidated
237 .unary::<CapacityContainerBuilder<Column<(Row, T, Diff)>>, _, _, _>(
238 Pipeline,
239 &format!("Flatten {name}"),
240 |_cap, _info| {
241 move |input, output| {
242 input.for_each(|time, data| {
243 let mut session = output.session(&time);
244 for mut chunk in data.drain(..).flatten() {
245 session.give_container(&mut chunk);
246 }
247 });
248 }
249 },
250 )
251 .as_collection()
252}
253
254pub fn vec_to_columnar<'scope, T>(
259 collection: VecCollection<'scope, T, Row, Diff>,
260) -> ColumnarCollection<'scope, T, Row, Diff>
261where
262 T: RenderTimestamp,
263{
264 collection
265 .inner
266 .unary::<ColumnBuilder<(Row, T, Diff)>, _, _, _>(
267 Pipeline,
268 "VecToColumnar",
269 |_cap, _info| {
270 move |input, output| {
271 input.for_each(|time, data| {
272 let mut session = output.session_with_builder(&time);
273 for (v, t, d) in data.drain(..) {
274 session.give((&v, &t, &d));
275 }
276 });
277 }
278 },
279 )
280 .as_collection()
281}
282
283pub fn columnar_to_vec<'scope, T>(
289 collection: ColumnarCollection<'scope, T, Row, Diff>,
290) -> VecCollection<'scope, T, Row, Diff>
291where
292 T: RenderTimestamp,
293{
294 collection
295 .inner
296 .unary::<CapacityContainerBuilder<Vec<(Row, T, Diff)>>, _, _, _>(
297 Pipeline,
298 "ColumnarToVec",
299 |_cap, _info| {
300 move |input, output| {
301 input.for_each(|time, data| {
302 let mut session = output.session(&time);
303 for (v, t, d) in data.borrow().into_index_iter() {
304 session.give((
305 Columnar::into_owned(v),
306 Columnar::into_owned(t),
307 Columnar::into_owned(d),
308 ));
309 }
310 });
311 }
312 },
313 )
314 .as_collection()
315}
316
317#[cfg(test)]
318mod tests {
319 use differential_dataflow::input::Input;
320 use mz_ore::cast::CastFrom;
321 use mz_repr::{Datum, Timestamp};
322 use timely::dataflow::operators::Capture;
323 use timely::dataflow::operators::capture::{Event, Extract};
324
325 use super::*;
326
327 type RowBuilder = CapacityContainerBuilder<Vec<(Row, Timestamp, Diff)>>;
328 type CapturedRows = std::sync::mpsc::Receiver<Event<Timestamp, Vec<(Row, Timestamp, Diff)>>>;
329
330 fn extract_sorted(captured: CapturedRows) -> Vec<(Row, Timestamp, Diff)> {
331 let mut updates: Vec<_> = captured
332 .extract()
333 .into_iter()
334 .flat_map(|(_, data)| data)
335 .collect();
336 updates.sort();
337 updates
338 }
339
340 fn test_rows() -> Vec<Row> {
341 vec![
342 Row::pack_slice(&[Datum::Int32(42), Datum::String("hello")]),
343 Row::pack_slice(&[Datum::Int64(100), Datum::Null]),
344 Row::pack_slice(&[Datum::True, Datum::False, Datum::Null]),
345 Row::default(),
346 ]
347 }
348
349 #[mz_ore::test]
350 fn round_trip_through_columnar() {
351 let rows = test_rows();
352 let expected: Vec<_> = {
353 let mut updates: Vec<_> = rows
354 .iter()
355 .enumerate()
356 .map(|(i, r)| (r.clone(), Timestamp::from(u64::cast_from(i / 2)), Diff::ONE))
357 .collect();
358 updates.sort();
359 updates
360 };
361 let captured = timely::execute_directly(move |worker| {
362 worker.dataflow::<Timestamp, _, _>(|scope| {
363 let (mut input, collection) = scope.new_collection();
364 let captured = columnar_to_vec(vec_to_columnar(collection)).inner.capture();
365 for (i, row) in rows.into_iter().enumerate() {
366 input.advance_to(Timestamp::from(u64::cast_from(i / 2)));
367 input.update(row, Diff::ONE);
368 }
369 input.advance_to(Timestamp::from(2_u64));
370 input.flush();
371 captured
372 })
373 });
374 assert_eq!(extract_sorted(captured), expected);
375 }
376
377 #[mz_ore::test]
378 fn columnar_negate_flips_diffs() {
379 let rows = test_rows();
380 let expected: Vec<_> = {
381 let mut updates: Vec<_> = rows
382 .iter()
383 .map(|r| (r.clone(), Timestamp::from(0_u64), -Diff::ONE))
384 .collect();
385 updates.sort();
386 updates
387 };
388 let captured = timely::execute_directly(move |worker| {
389 worker.dataflow::<Timestamp, _, _>(|scope| {
390 let (mut input, collection) = scope.new_collection();
391 let edge = columnar_negate(vec_to_columnar(collection));
392 let captured = columnar_to_vec(edge).inner.capture();
393 for row in rows {
394 input.update(row, Diff::ONE);
395 }
396 input.advance_to(Timestamp::from(1_u64));
397 input.flush();
398 captured
399 })
400 });
401 assert_eq!(extract_sorted(captured), expected);
402 }
403
404 #[mz_ore::test]
405 fn concat_many_concatenates_columnar() {
406 let rows = test_rows();
407 let expected: Vec<_> = {
408 let mut updates: Vec<_> = rows
409 .iter()
410 .map(|r| (r.clone(), Timestamp::from(0_u64), Diff::ONE))
411 .collect();
412 updates.push((rows[0].clone(), Timestamp::from(0_u64), Diff::ONE));
414 updates.sort();
415 updates
416 };
417 let captured = timely::execute_directly(move |worker| {
418 worker.dataflow::<Timestamp, _, _>(|scope| {
419 let (mut input1, collection1) = scope.new_collection();
420 let (mut input2, collection2) = scope.new_collection();
421 let edge = concat_many(
422 scope,
423 [vec_to_columnar(collection1), vec_to_columnar(collection2)],
424 );
425 let captured = columnar_to_vec(edge).inner.capture();
426 let (first, rest) = rows.split_first().unwrap();
427 input1.update(first.clone(), Diff::ONE);
428 input2.update(first.clone(), Diff::ONE);
429 for row in rest {
430 input1.update(row.clone(), Diff::ONE);
431 }
432 for input in [&mut input1, &mut input2] {
433 input.advance_to(Timestamp::from(1_u64));
434 input.flush();
435 }
436 captured
437 })
438 });
439 assert_eq!(extract_sorted(captured), expected);
440 }
441
442 #[mz_ore::test]
443 fn flat_map_datums_arms_agree() {
444 let rows = test_rows();
446 let captured = timely::execute_directly(move |worker| {
447 worker.dataflow::<Timestamp, _, _>(|scope| {
448 let (mut input, collection) = scope.new_collection();
449 let (oks, _errs) = flat_map_datums::<_, RowBuilder, _>(
450 vec_to_columnar(collection),
451 1,
452 |datums, t, d, ok_session, _err_session| {
453 ok_session.give((Row::pack(datums.iter()), t, d));
454 1
455 },
456 );
457 let captured = oks.capture();
458 for row in rows {
459 input.update(row, Diff::ONE);
460 }
461 input.advance_to(Timestamp::from(1_u64));
462 input.flush();
463 captured
464 })
465 });
466 let updates = extract_sorted(captured);
467 assert!(!updates.is_empty());
468 assert!(updates.iter().all(|(r, _, _)| r.iter().count() <= 1));
470 }
471
472 #[mz_ore::test]
473 fn columnar_consolidate_accumulates_and_cancels() {
474 let row1 = Row::pack_slice(&[Datum::Int32(1)]);
475 let row2 = Row::pack_slice(&[Datum::Int32(2)]);
476 let row3 = Row::pack_slice(&[Datum::Int32(3)]);
477 let expected = vec![
480 (row1.clone(), Timestamp::from(0_u64), Diff::from(2)),
481 (row1.clone(), Timestamp::from(1_u64), Diff::ONE),
482 ];
483
484 let captured = timely::execute_directly(move |worker| {
485 worker.dataflow::<Timestamp, _, _>(|scope| {
486 let (mut input, collection) = scope.new_collection();
487 let edge = columnar_consolidate(vec_to_columnar(collection), "Test");
488 let captured = columnar_to_vec(edge).inner.capture();
489 input.advance_to(Timestamp::from(0_u64));
491 input.update(row1.clone(), Diff::ONE);
492 input.update(row1.clone(), Diff::ONE);
493 input.update(row2.clone(), Diff::ONE);
494 input.update(row2, -Diff::ONE);
495 input.advance_to(Timestamp::from(1_u64));
497 input.update(row1, Diff::ONE);
498 input.update(row3.clone(), Diff::ONE);
499 input.update(row3, -Diff::ONE);
500 input.advance_to(Timestamp::from(2_u64));
501 input.flush();
502 captured
503 })
504 });
505 assert_eq!(extract_sorted(captured), expected);
506 }
507}