1#![allow(clippy::op_ref)]
15
16use std::collections::BTreeSet;
17use std::rc::Rc;
18
19use differential_dataflow::consolidation::ConsolidatingContainerBuilder;
20use differential_dataflow::operators::arrange::Arranged;
21use differential_dataflow::trace::cursor::BatchCursor;
22use differential_dataflow::trace::implementations::BatchContainer;
23use differential_dataflow::trace::{Cursor, Navigable, TraceReader};
24use differential_dataflow::{AsCollection, VecCollection};
25use mz_compute_types::dyncfgs::ENABLE_HALF_JOIN2;
26use mz_compute_types::plan::join::JoinClosure;
27use mz_compute_types::plan::join::delta_join::{DeltaJoinPlan, DeltaPathPlan, DeltaStagePlan};
28use mz_compute_types::plan::scalar::LirScalarExpr;
29use mz_dyncfg::ConfigSet;
30use mz_expr::Eval;
31use mz_repr::fixed_length::ExtendDatums;
32use mz_repr::{DatumVec, Diff, Row, RowArena, SharedRow};
33use mz_timely_util::operator::{CollectionExt, StreamExt};
34use timely::container::CapacityContainerBuilder;
35use timely::dataflow::channels::pact::Pipeline;
36use timely::dataflow::operators::OkErr;
37use timely::dataflow::operators::generic::Session;
38use timely::dataflow::operators::vec::Map;
39use timely::progress::Antichain;
40
41use crate::render::RenderTimestamp;
42use crate::render::context::{ArrangementFlavor, CollectionBundle, Context};
43use crate::render::errors::DataflowErrorSer;
44use crate::typedefs::{RowRowAgent, RowRowEnter};
45
46impl<'scope, T: RenderTimestamp> Context<'scope, T> {
47 pub fn render_delta_join(
52 &self,
53 inputs: Vec<CollectionBundle<'scope, T>>,
54 join_plan: DeltaJoinPlan,
55 ) -> CollectionBundle<'scope, T> {
56 let (oks, errs) = self.scope.clone().region_named("Join(Delta)", |inner| {
58 let mut join_results = Vec::new();
62
63 let mut arrangements = vec![BTreeSet::new(); inputs.len()];
68 let mut raw = vec![false; inputs.len()];
69 for path_plan in &join_plan.path_plans {
70 record_path_arrangements(
71 &mut arrangements,
72 &mut raw,
73 path_plan.source_relation,
74 path_plan.source_key.as_ref(),
75 &path_plan.stage_plans,
76 );
77 }
78 let inputs = inputs
79 .iter()
80 .enumerate()
81 .map(|(index, cb)| {
82 prune_bundle(cb, raw[index], &arrangements[index]).enter_region(inner)
83 })
84 .collect::<Vec<_>>();
85
86 let mut inner_errs: Vec<_> = inputs.iter().flat_map(bundle_errs).collect();
90
91 for path_plan in join_plan.path_plans {
92 let DeltaPathPlan {
94 source_relation,
95 initial_closure,
96 stage_plans,
97 final_closure,
98 source_key,
99 } = path_plan;
100
101 let name = format!("delta path {}", source_relation);
105 let path_results = inner.clone().region_named(&name, |region| {
106 let mut path_arrangements = vec![BTreeSet::new(); inputs.len()];
122 let mut path_raw = vec![false; inputs.len()];
123 record_path_arrangements(
124 &mut path_arrangements,
125 &mut path_raw,
126 source_relation,
127 source_key.as_ref(),
128 &stage_plans,
129 );
130 let bundles = inputs
131 .iter()
132 .enumerate()
133 .map(|(index, cb)| {
134 prune_bundle(cb, path_raw[index], &path_arrangements[index])
135 .enter_region(region)
136 })
137 .collect::<Vec<_>>();
138
139 let mut region_errs = Vec::with_capacity(inputs.len());
141
142 let (update_stream, err_stream) = build_update_stream(
144 &bundles[source_relation],
145 self.as_of_frontier.clone(),
146 source_key,
147 source_relation,
148 initial_closure,
149 );
150 region_errs.push(err_stream);
151
152 let mut update_stream = update_stream
158 .inner
159 .map(|(v, t, d)| ((v, t.clone()), t, d))
160 .as_collection();
161
162 for stage_plan in stage_plans {
165 let DeltaStagePlan {
166 lookup_relation,
167 stream_key,
168 stream_thinning,
169 lookup_key,
170 closure,
171 } = stage_plan;
172
173 let (oks, errs) = build_halfjoin(
181 update_stream,
182 stream_key,
183 stream_thinning,
184 &bundles[lookup_relation],
185 lookup_key,
186 source_relation < lookup_relation,
187 closure,
188 Rc::clone(&self.config_set),
189 );
190 update_stream = oks;
191 region_errs.push(errs);
192 }
193
194 let mut update_stream = update_stream
201 .inner
202 .map(|((row, time), _, diff)| (row, time, diff))
203 .as_collection();
204
205 if let Some(final_closure) = final_closure {
209 let name = "DeltaJoinFinalization";
210 type CB<C> = ConsolidatingContainerBuilder<C>;
211 let (updates, errors) = update_stream
212 .flat_map_fallible::<CB<_>, CB<_>, _, _, _, _>(name, {
213 let mut datums = DatumVec::new();
215 move |row| {
216 let mut row_builder = SharedRow::get();
217 let temp_storage = RowArena::new();
218 let mut datums_local = datums.borrow_with(&row);
219 final_closure
221 .apply(&mut datums_local, &temp_storage, &mut row_builder)
222 .map(|row| row.cloned())
223 .map_err(DataflowErrorSer::from)
224 .transpose()
225 }
226 });
227
228 update_stream = updates;
229 region_errs.push(errors);
230 }
231
232 inner_errs.push(
233 differential_dataflow::collection::concatenate(region, region_errs)
234 .leave_region(inner),
235 );
236 update_stream.leave_region(inner)
237 });
238
239 join_results.push(path_results);
240 }
241
242 (
244 differential_dataflow::collection::concatenate(inner, join_results)
245 .leave_region(self.scope),
246 differential_dataflow::collection::concatenate(inner, inner_errs)
247 .leave_region(self.scope),
248 )
249 });
250 CollectionBundle::from_collections(oks, errs)
251 }
252}
253
254fn record_path_arrangements(
258 arrangements: &mut [BTreeSet<Vec<LirScalarExpr>>],
259 raw: &mut [bool],
260 source_relation: usize,
261 source_key: Option<&Vec<LirScalarExpr>>,
262 stage_plans: &[DeltaStagePlan],
263) {
264 match source_key {
265 Some(source_key) => {
266 arrangements[source_relation].insert(source_key.clone());
267 }
268 None => raw[source_relation] = true,
269 }
270 for stage_plan in stage_plans {
271 arrangements[stage_plan.lookup_relation].insert(stage_plan.lookup_key.clone());
272 }
273}
274
275fn prune_bundle<'scope, T: RenderTimestamp>(
278 bundle: &CollectionBundle<'scope, T>,
279 raw: bool,
280 keys: &BTreeSet<Vec<LirScalarExpr>>,
281) -> CollectionBundle<'scope, T> {
282 CollectionBundle {
283 collection: if raw { bundle.collection.clone() } else { None },
284 arranged: bundle
285 .arranged
286 .iter()
287 .filter(|(key, _)| keys.contains(*key))
288 .map(|(key, flavor)| (key.clone(), flavor.clone()))
289 .collect(),
290 }
291}
292
293fn bundle_errs<'scope, T: RenderTimestamp>(
310 bundle: &CollectionBundle<'scope, T>,
311) -> Vec<VecCollection<'scope, T, DataflowErrorSer, Diff>> {
312 let mut collected = Vec::with_capacity(bundle.arranged.len() + 1);
313 if let Some((_oks, errs)) = &bundle.collection {
314 collected.push(errs.clone());
315 }
316 for flavor in bundle.arranged.values() {
317 let errs = match flavor {
318 ArrangementFlavor::Local(_oks, errs) => errs.clone().as_collection(|k, _v| k.clone()),
319 ArrangementFlavor::Trace(_id, _oks, errs) => {
320 errs.clone().as_collection(|k, _v| k.clone())
321 }
322 };
323 collected.push(errs);
324 }
325 collected
326}
327
328fn build_halfjoin<'scope, T>(
339 updates: VecCollection<'scope, T, (Row, T), Diff>,
340 prev_key: Vec<LirScalarExpr>,
341 prev_thinning: Vec<usize>,
342 bundle: &CollectionBundle<'scope, T>,
343 lookup_key: Vec<LirScalarExpr>,
344 source_precedes_lookup: bool,
345 closure: JoinClosure,
346 config_set: Rc<ConfigSet>,
347) -> (
348 VecCollection<'scope, T, (Row, T), Diff>,
349 VecCollection<'scope, T, DataflowErrorSer, Diff>,
350)
351where
352 T: RenderTimestamp,
353{
354 match bundle.arrangement(&lookup_key) {
355 Some(ArrangementFlavor::Local(oks, _errs)) => {
356 let (oks, errs2) = if source_precedes_lookup {
357 build_halfjoin_trace::<_, RowRowAgent<_, _>, _>(
358 updates,
359 oks,
360 prev_key,
361 prev_thinning,
362 |t1, t2| t1.le(t2),
363 closure,
364 config_set,
365 )
366 } else {
367 build_halfjoin_trace::<_, RowRowAgent<_, _>, _>(
368 updates,
369 oks,
370 prev_key,
371 prev_thinning,
372 |t1, t2| t1.lt(t2),
373 closure,
374 config_set,
375 )
376 };
377 (oks, errs2)
378 }
379 Some(ArrangementFlavor::Trace(_, oks, _errs)) => {
380 let (oks, errs2) = if source_precedes_lookup {
381 build_halfjoin_trace::<_, RowRowEnter<_, _, _>, _>(
382 updates,
383 oks,
384 prev_key,
385 prev_thinning,
386 |t1, t2| t1.le(t2),
387 closure,
388 config_set,
389 )
390 } else {
391 build_halfjoin_trace::<_, RowRowEnter<_, _, _>, _>(
392 updates,
393 oks,
394 prev_key,
395 prev_thinning,
396 |t1, t2| t1.lt(t2),
397 closure,
398 config_set,
399 )
400 };
401 (oks, errs2)
402 }
403 None => panic!("Arrangement promised by the planner is absent!"),
404 }
405}
406
407fn build_halfjoin_trace<'scope, T, Tr, CF>(
418 updates: VecCollection<'scope, T, (Row, T), Diff>,
419 trace: Arranged<'scope, Tr>,
420 prev_key: Vec<LirScalarExpr>,
421 prev_thinning: Vec<usize>,
422 comparison: CF,
423 closure: JoinClosure,
424 config_set: Rc<ConfigSet>,
425) -> (
426 VecCollection<'scope, T, (Row, T), Diff>,
427 VecCollection<'scope, T, DataflowErrorSer, Diff>,
428)
429where
430 T: RenderTimestamp,
431 Tr: TraceReader<Batch: Navigable, Time = T> + Clone + 'static,
432 for<'a> BatchCursor<Tr>: Cursor<
433 Val<'a>: ExtendDatums,
434 KeyContainer: BatchContainer<Owned = Row>,
435 Time = T,
436 Diff = Diff,
437 >,
438 CF: Fn(<BatchCursor<Tr> as Cursor>::TimeGat<'_>, &T) -> bool + 'static,
439{
440 let use_half_join2 = ENABLE_HALF_JOIN2.get(&config_set);
441
442 let name = "DeltaJoinKeyPreparation";
443 type CB<C> = CapacityContainerBuilder<C>;
444 let (updates, errs) = updates.map_fallible::<CB<_>, CB<_>, _, _, _>(name, {
445 let mut datums = DatumVec::new();
447 move |(row, time)| {
448 let temp_storage = RowArena::new();
449 let datums_local = datums.borrow_with(&row);
450 let mut row_builder = SharedRow::get();
451 row_builder.packer().try_extend(
452 prev_key
453 .iter()
454 .map(|e| e.eval(&datums_local, &temp_storage)),
455 )?;
456 let key = row_builder.clone();
457 row_builder
458 .packer()
459 .extend(prev_thinning.iter().map(|&c| datums_local[c]));
460 let row_value = row_builder.clone();
461
462 Ok((key, row_value, time))
463 }
464 });
465 let datums = DatumVec::new();
466
467 if use_half_join2 {
468 build_halfjoin2(updates, trace, comparison, closure, datums, errs)
469 } else {
470 build_halfjoin1(updates, trace, comparison, closure, datums, errs)
471 }
472}
473
474fn build_halfjoin2<'scope, T, Tr, CF>(
476 updates: VecCollection<'scope, T, (Row, Row, T), Diff>,
477 trace: Arranged<'scope, Tr>,
478 comparison: CF,
479 closure: JoinClosure,
480 mut datums: DatumVec,
481 errs: VecCollection<'scope, T, DataflowErrorSer, Diff>,
482) -> (
483 VecCollection<'scope, T, (Row, T), Diff>,
484 VecCollection<'scope, T, DataflowErrorSer, Diff>,
485)
486where
487 T: RenderTimestamp,
488 Tr: TraceReader<Batch: Navigable, Time = T> + Clone + 'static,
489 for<'a> BatchCursor<Tr>: Cursor<
490 Val<'a>: ExtendDatums,
491 KeyContainer: BatchContainer<Owned = Row>,
492 Time = T,
493 Diff = Diff,
494 >,
495 CF: Fn(<BatchCursor<Tr> as Cursor>::TimeGat<'_>, &T) -> bool + 'static,
496{
497 type CB<C> = CapacityContainerBuilder<C>;
498
499 if closure.could_error() {
500 let (oks, errs2) = differential_dogs3::operators::half_join2::half_join_internal_unsafe(
501 updates,
502 trace,
503 |time, antichain| {
504 antichain.insert(time.step_back());
505 },
506 comparison,
507 |_timer, count| count > 1_000_000,
510 move |session: &mut CB<Vec<_>>, key, stream_row, lookup_row, initial, diff1, output| {
512 let mut row_builder = SharedRow::get();
513 let temp_storage = RowArena::new();
514
515 let mut datums_local = datums.borrow();
516 datums_local.extend(key.iter());
517 datums_local.extend(stream_row.iter());
518 lookup_row.extend_datums(&temp_storage, &mut datums_local, None);
519
520 let row = closure.apply(&mut datums_local, &temp_storage, &mut row_builder);
521
522 for (time, diff2) in output.drain(..) {
523 let row = row.as_ref().map(|row| row.cloned()).map_err(Clone::clone);
524 let diff = diff1.clone() * diff2.clone();
525 let data = ((row, time.clone()), initial.clone(), diff);
526 use timely::container::PushInto;
527 session.push_into(data);
528 }
529 },
530 )
531 .ok_err(|(data_time, init_time, diff)| {
532 match data_time {
534 (Ok(data), time) => Ok((data.map(|data| (data, time)), init_time, diff)),
535 (Err(err), _time) => Err((DataflowErrorSer::from(err), init_time, diff)),
536 }
537 });
538
539 (
540 oks.as_collection().flat_map(|x| x),
541 errs.concat(errs2.as_collection()),
542 )
543 } else {
544 let oks = differential_dogs3::operators::half_join2::half_join_internal_unsafe(
545 updates,
546 trace,
547 |time, antichain| {
548 antichain.insert(time.step_back());
549 },
550 comparison,
551 |_timer, count| count > 1_000_000,
554 move |session: &mut CB<Vec<_>>, key, stream_row, lookup_row, initial, diff1, output| {
556 if output.is_empty() {
557 return;
558 }
559
560 let mut row_builder = SharedRow::get();
561 let temp_storage = RowArena::new();
562
563 let mut datums_local = datums.borrow();
564 datums_local.extend(key.iter());
565 datums_local.extend(stream_row.iter());
566 lookup_row.extend_datums(&temp_storage, &mut datums_local, None);
567
568 if let Some(row) = closure
569 .apply(&mut datums_local, &temp_storage, &mut row_builder)
570 .expect("Closure claimed to never error")
571 {
572 for (time, diff2) in output.drain(..) {
573 let diff = diff1.clone() * diff2.clone();
574 use timely::container::PushInto;
575 session.push_into(((row.clone(), time.clone()), initial.clone(), diff));
576 }
577 }
578 },
579 );
580
581 (oks.as_collection(), errs)
582 }
583}
584
585fn build_halfjoin1<'scope, T, Tr, CF>(
587 updates: VecCollection<'scope, T, (Row, Row, T), Diff>,
588 trace: Arranged<'scope, Tr>,
589 comparison: CF,
590 closure: JoinClosure,
591 mut datums: DatumVec,
592 errs: VecCollection<'scope, T, DataflowErrorSer, Diff>,
593) -> (
594 VecCollection<'scope, T, (Row, T), Diff>,
595 VecCollection<'scope, T, DataflowErrorSer, Diff>,
596)
597where
598 T: RenderTimestamp,
599 Tr: TraceReader<Batch: Navigable, Time = T> + Clone + 'static,
600 for<'a> BatchCursor<Tr>: Cursor<
601 Val<'a>: ExtendDatums,
602 KeyContainer: BatchContainer<Owned = Row>,
603 Time = T,
604 Diff = Diff,
605 >,
606 CF: Fn(<BatchCursor<Tr> as Cursor>::TimeGat<'_>, &T) -> bool + 'static,
607{
608 type CB<C> = CapacityContainerBuilder<C>;
609
610 if closure.could_error() {
611 let (oks, errs2) = differential_dogs3::operators::half_join::half_join_internal_unsafe(
612 updates,
613 trace,
614 |time, antichain| {
615 antichain.insert(time.step_back());
616 },
617 comparison,
618 |_timer, count| count > 1_000_000,
619 move |session: &mut Session<'_, '_, T, CB<Vec<_>>, _>,
620 key,
621 stream_row: &Row,
622 lookup_row,
623 initial,
624 diff1,
625 output| {
626 let mut row_builder = SharedRow::get();
627 let temp_storage = RowArena::new();
628
629 let mut datums_local = datums.borrow();
630 datums_local.extend(key.iter());
631 datums_local.extend(stream_row.iter());
632 lookup_row.extend_datums(&temp_storage, &mut datums_local, None);
633
634 let row = closure.apply(&mut datums_local, &temp_storage, &mut row_builder);
635
636 for (time, diff2) in output.drain(..) {
637 let row = row.as_ref().map(|row| row.cloned()).map_err(Clone::clone);
638 let diff = diff1.clone() * diff2.clone();
639 let data = ((row, time.clone()), initial.clone(), diff);
640 session.give(data);
641 }
642 },
643 )
644 .ok_err(|(data_time, init_time, diff)| match data_time {
645 (Ok(data), time) => Ok((data.map(|data| (data, time)), init_time, diff)),
646 (Err(err), _time) => Err((DataflowErrorSer::from(err), init_time, diff)),
647 });
648
649 (
650 oks.as_collection().flat_map(|x| x),
651 errs.concat(errs2.as_collection()),
652 )
653 } else {
654 let oks = differential_dogs3::operators::half_join::half_join_internal_unsafe(
655 updates,
656 trace,
657 |time, antichain| {
658 antichain.insert(time.step_back());
659 },
660 comparison,
661 |_timer, count| count > 1_000_000,
662 move |session: &mut Session<'_, '_, T, CB<Vec<_>>, _>,
663 key,
664 stream_row: &Row,
665 lookup_row,
666 initial,
667 diff1,
668 output| {
669 if output.is_empty() {
670 return;
671 }
672
673 let mut row_builder = SharedRow::get();
674 let temp_storage = RowArena::new();
675
676 let mut datums_local = datums.borrow();
677 datums_local.extend(key.iter());
678 datums_local.extend(stream_row.iter());
679 lookup_row.extend_datums(&temp_storage, &mut datums_local, None);
680
681 if let Some(row) = closure
682 .apply(&mut datums_local, &temp_storage, &mut row_builder)
683 .expect("Closure claimed to never error")
684 {
685 for (time, diff2) in output.drain(..) {
686 let diff = diff1.clone() * diff2.clone();
687 session.give(((row.clone(), time.clone()), initial.clone(), diff));
688 }
689 }
690 },
691 );
692
693 (oks.as_collection(), errs)
694 }
695}
696
697fn build_update_stream<'scope, T>(
707 bundle: &CollectionBundle<'scope, T>,
708 as_of: Antichain<mz_repr::Timestamp>,
709 source_key: Option<Vec<LirScalarExpr>>,
710 source_relation: usize,
711 initial_closure: JoinClosure,
712) -> (
713 VecCollection<'scope, T, Row, Diff>,
714 VecCollection<'scope, T, DataflowErrorSer, Diff>,
715)
716where
717 T: RenderTimestamp,
718{
719 let Some(source_key) = source_key else {
720 let (oks, _errs) = bundle
724 .collection
725 .clone()
726 .expect("The unarranged collection doesn't exist.");
727 return build_update_stream_stream(oks.into_vec(), as_of, source_relation, initial_closure);
728 };
729 match bundle.arrangement(&source_key) {
730 Some(ArrangementFlavor::Local(oks, _errs)) => {
731 build_update_stream_trace::<_, RowRowAgent<_, _>>(
732 oks,
733 as_of,
734 source_relation,
735 initial_closure,
736 )
737 }
738 Some(ArrangementFlavor::Trace(_, oks, _errs)) => {
739 build_update_stream_trace::<_, RowRowEnter<_, _, _>>(
740 oks,
741 as_of,
742 source_relation,
743 initial_closure,
744 )
745 }
746 None => panic!("Arrangement promised by the planner is absent!"),
747 }
748}
749
750fn build_update_stream_trace<'scope, T, Tr>(
756 trace: Arranged<'scope, Tr>,
757 as_of: Antichain<mz_repr::Timestamp>,
758 source_relation: usize,
759 initial_closure: JoinClosure,
760) -> (
761 VecCollection<'scope, T, Row, Diff>,
762 VecCollection<'scope, T, DataflowErrorSer, Diff>,
763)
764where
765 T: RenderTimestamp,
766 for<'a, 'b> &'a T: PartialEq<<BatchCursor<Tr> as Cursor>::TimeGat<'b>>,
767 Tr: TraceReader<Batch: Navigable, Time = T> + Clone + 'static,
768 for<'a> BatchCursor<Tr>:
769 Cursor<Key<'a>: ExtendDatums, Val<'a>: ExtendDatums, Time = T, Diff = Diff>,
770{
771 let mut inner_as_of = Antichain::new();
772 for event_time in as_of.elements().iter() {
773 inner_as_of.insert(<T>::to_inner(event_time.clone()));
774 }
775
776 let (ok_stream, err_stream) =
777 trace
778 .stream
779 .unary_fallible(Pipeline, "UpdateStream", move |_, _| {
780 let mut datums = DatumVec::new();
781 Box::new(move |input, ok_output, err_output| {
782 let mut times_diffs = Vec::default();
784 input.for_each(|time, data| {
785 let mut row_builder = SharedRow::get();
786 let mut ok_session = ok_output.session(&time);
787 let mut err_session = err_output.session(&time);
788
789 for wrapper in data.iter() {
790 let batch = &wrapper;
791 let mut cursor = batch.cursor();
792 while let Some(key) = cursor.get_key(batch) {
793 while let Some(val) = cursor.get_val(batch) {
794 cursor.map_times(batch, |time, diff| {
796 if source_relation == 0
797 || inner_as_of.elements().iter().all(|e| e != time)
798 {
799 times_diffs.push((
801 <BatchCursor<Tr> as Cursor>::owned_time(time),
802 <BatchCursor<Tr> as Cursor>::owned_diff(diff),
803 ));
804 }
805 });
806 differential_dataflow::consolidation::consolidate(
807 &mut times_diffs,
808 );
809 if !times_diffs.is_empty() {
811 let temp_storage = RowArena::new();
812
813 let mut datums_local = datums.borrow();
814 key.extend_datums(&temp_storage, &mut datums_local, None);
815 val.extend_datums(&temp_storage, &mut datums_local, None);
816
817 if !initial_closure.is_identity() {
818 match initial_closure
819 .apply(
820 &mut datums_local,
821 &temp_storage,
822 &mut row_builder,
823 )
824 .map(|row| row.cloned())
825 .transpose()
826 {
827 Some(Ok(row)) => {
828 for (time, diff) in times_diffs.drain(..) {
829 ok_session.give((row.clone(), time, diff))
830 }
831 }
832 Some(Err(err)) => {
833 for (time, diff) in times_diffs.drain(..) {
834 err_session.give((err.clone(), time, diff))
835 }
836 }
837 None => {}
838 }
839 } else {
840 let row = {
841 row_builder.packer().extend(&*datums_local);
842 row_builder.clone()
843 };
844 for (time, diff) in times_diffs.drain(..) {
845 ok_session.give((row.clone(), time, diff));
846 }
847 }
848 }
849 times_diffs.clear();
850
851 cursor.step_val(batch);
852 }
853 cursor.step_key(batch);
854 }
855 }
856 });
857 })
858 });
859
860 (
861 ok_stream.as_collection(),
862 err_stream.as_collection().map(DataflowErrorSer::from),
863 )
864}
865
866fn build_update_stream_stream<'scope, T>(
872 stream: VecCollection<'scope, T, Row, Diff>,
873 _as_of: Antichain<mz_repr::Timestamp>,
874 source_relation: usize,
875 initial_closure: JoinClosure,
876) -> (
877 VecCollection<'scope, T, Row, Diff>,
878 VecCollection<'scope, T, DataflowErrorSer, Diff>,
879)
880where
881 T: RenderTimestamp,
882{
883 assert_eq!(source_relation, 0);
886
887 type CB<C> = ConsolidatingContainerBuilder<C>;
888 stream.flat_map_fallible::<CB<_>, CB<_>, _, _, _, _>("UpdateStream", {
889 let mut datums = DatumVec::new();
891 move |row| {
892 let mut row_builder = SharedRow::get();
893 let temp_storage = RowArena::new();
894 let mut datums_local = datums.borrow_with(&row);
895 initial_closure
896 .apply(&mut datums_local, &temp_storage, &mut row_builder)
897 .map(|row| row.cloned())
898 .map_err(DataflowErrorSer::from)
899 .transpose()
900 }
901 })
902}