1use std::collections::BTreeMap;
14use std::rc::Rc;
15
16use columnar::{Columnar, Index};
17use differential_dataflow::consolidation::ConsolidatingContainerBuilder;
18use differential_dataflow::operators::arrange::Arranged;
19use differential_dataflow::trace::cursor::{BatchCursor, BatchKey, BatchVal};
20use differential_dataflow::trace::implementations::BatchContainer;
21use differential_dataflow::trace::{Cursor, Navigable, TraceReader};
22use differential_dataflow::{AsCollection, VecCollection};
23use mz_compute_types::dataflows::DataflowDescription;
24use mz_compute_types::dyncfgs::TEMPORAL_BUCKETING_SUMMARY;
25use mz_compute_types::plan::scalar::LirScalarExpr;
26use mz_compute_types::plan::{ArrangementStrategy, AvailableCollections};
27use mz_dyncfg::ConfigSet;
28use mz_expr::{Eval, Id, MfpPlan};
29use mz_ore::soft_assert_or_log;
30use mz_repr::fixed_length::ExtendDatums;
31use mz_repr::{DatumVec, DatumVecBorrow, Diff, GlobalId, Row, RowArena, SharedRow, StableRow};
32use mz_storage_types::controller::CollectionMetadata;
33use mz_timely_util::columnar::Column;
34use mz_timely_util::columnar::batcher;
35use mz_timely_util::columnar::builder::ColumnBuilder;
36use mz_timely_util::columnar::chunk::{
37 AccountedChunkBatcher, ChunkChunker, ComputeSpill, UnchunkBuilder,
38};
39use mz_timely_util::columnar::consolidate::ConsolidatingColumnBuilder;
40use mz_timely_util::columnar::{Col2ValBatcher, Col2ValColBatcher, columnar_exchange};
41use mz_timely_util::columnation::ColumnationChunker;
42use timely::ContainerBuilder;
43use timely::container::NoopBuilder;
44use timely::dataflow::channels::pact::{ExchangeCore, Pipeline};
45use timely::dataflow::operators::Capability;
46use timely::dataflow::operators::generic::builder_rc::OperatorBuilder;
47use timely::dataflow::operators::generic::{OutputBuilder, OutputBuilderSession};
48use timely::dataflow::{Scope, Stream};
49use timely::progress::operate::FrontierInterest;
50use timely::progress::{Antichain, Timestamp};
51
52use crate::compute_state::ComputeState;
53use crate::extensions::arrange::{ArrangementBatcher, KeyCollection, MzArrange, MzArrangeCore};
54use crate::extensions::reduce::MzReduce;
55use crate::render::columnar::{ColCollection, flat_map_datums};
56use crate::render::errors::{DataflowErrorSer, ErrorLogger};
57use crate::render::{LinearJoinSpec, MaybeBucketByTime, RenderTimestamp};
58use crate::typedefs::{
59 ErrAgent, ErrBatcher, ErrBuilder, ErrEnter, ErrSpine, RowRowAgent, RowRowEnter, RowRowSpine,
60};
61use mz_row_spine::{RowRowBuilder, RowRowColPagedBuilder};
62
63pub struct Context<'scope, T: RenderTimestamp> {
71 pub(crate) scope: Scope<'scope, T>,
75 pub debug_name: String,
77 pub dataflow_id: usize,
79 pub export_ids: Vec<GlobalId>,
81 pub as_of_frontier: Antichain<mz_repr::Timestamp>,
86 pub until: Antichain<mz_repr::Timestamp>,
89 pub bindings: BTreeMap<Id, CollectionBundle<'scope, T>>,
91 pub(super) compute_logger: Option<crate::logging::compute::Logger>,
93 pub(super) linear_join_spec: LinearJoinSpec,
95 pub dataflow_expiration: Antichain<mz_repr::Timestamp>,
98 pub config_set: Rc<ConfigSet>,
100}
101
102impl<'scope, T: RenderTimestamp> Context<'scope, T> {
103 pub fn for_dataflow_in<Plan, O>(
105 dataflow: &DataflowDescription<Plan, CollectionMetadata, O>,
106 scope: Scope<'scope, T>,
107 compute_state: &ComputeState,
108 until: Antichain<mz_repr::Timestamp>,
109 dataflow_expiration: Antichain<mz_repr::Timestamp>,
110 ) -> Self {
111 use mz_ore::collections::CollectionExt as IteratorExt;
112 let dataflow_id = *scope.addr().into_first();
113 let as_of_frontier = dataflow
114 .as_of
115 .clone()
116 .unwrap_or_else(|| Antichain::from_elem(Timestamp::minimum()));
117
118 let export_ids = dataflow.export_ids().collect();
119
120 let compute_logger = if dataflow.is_transient() {
124 None
125 } else {
126 compute_state.compute_logger.clone()
127 };
128
129 Self {
130 scope,
131 debug_name: dataflow.debug_name.clone(),
132 dataflow_id,
133 export_ids,
134 as_of_frontier,
135 until,
136 bindings: BTreeMap::new(),
137 compute_logger,
138 linear_join_spec: compute_state.linear_join_spec,
139 dataflow_expiration,
140 config_set: Rc::clone(&compute_state.worker_config),
141 }
142 }
143}
144
145impl<'scope, T: RenderTimestamp> Context<'scope, T> {
146 pub fn insert_id(
151 &mut self,
152 id: Id,
153 collection: CollectionBundle<'scope, T>,
154 ) -> Option<CollectionBundle<'scope, T>> {
155 self.bindings.insert(id, collection)
156 }
157 pub fn remove_id(&mut self, id: Id) -> Option<CollectionBundle<'scope, T>> {
161 self.bindings.remove(&id)
162 }
163 pub fn update_id(&mut self, id: Id, collection: CollectionBundle<'scope, T>) {
165 if !self.bindings.contains_key(&id) {
166 self.bindings.insert(id, collection);
167 } else {
168 let binding = self
169 .bindings
170 .get_mut(&id)
171 .expect("Binding verified to exist");
172 if collection.collection.is_some() {
173 binding.collection = collection.collection;
174 }
175 for (key, flavor) in collection.arranged.into_iter() {
176 binding.arranged.insert(key, flavor);
177 }
178 }
179 }
180 pub fn lookup_id(&self, id: Id) -> Option<CollectionBundle<'scope, T>> {
182 self.bindings.get(&id).cloned()
183 }
184
185 pub(super) fn error_logger(&self) -> ErrorLogger {
186 ErrorLogger::new(self.debug_name.clone())
187 }
188}
189
190impl<'scope, T: RenderTimestamp> Context<'scope, T> {
191 pub fn enter_region<'a>(
193 &self,
194 region: Scope<'a, T>,
195 bindings: Option<&std::collections::BTreeSet<Id>>,
196 ) -> Context<'a, T> {
197 let bindings = self
198 .bindings
199 .iter()
200 .filter(|(key, _)| bindings.as_ref().map(|b| b.contains(key)).unwrap_or(true))
201 .map(|(key, bundle)| (*key, bundle.enter_region(region)))
202 .collect();
203
204 Context {
205 scope: region,
206 debug_name: self.debug_name.clone(),
207 dataflow_id: self.dataflow_id.clone(),
208 export_ids: self.export_ids.clone(),
209 as_of_frontier: self.as_of_frontier.clone(),
210 until: self.until.clone(),
211 compute_logger: self.compute_logger.clone(),
212 linear_join_spec: self.linear_join_spec.clone(),
213 bindings,
214 dataflow_expiration: self.dataflow_expiration.clone(),
215 config_set: Rc::clone(&self.config_set),
216 }
217 }
218}
219
220#[derive(Clone)]
222pub enum ArrangementFlavor<'scope, T: RenderTimestamp> {
223 Local(
225 Arranged<'scope, RowRowAgent<T, Diff>>,
226 Arranged<'scope, ErrAgent<T, Diff>>,
227 ),
228 Trace(
233 GlobalId,
234 Arranged<'scope, RowRowEnter<mz_repr::Timestamp, Diff, T>>,
235 Arranged<'scope, ErrEnter<mz_repr::Timestamp, T>>,
236 ),
237}
238
239impl<'scope, T: RenderTimestamp> ArrangementFlavor<'scope, T> {
240 pub fn flat_map<DCB, L>(
273 &self,
274 key: Option<&Row>,
275 max_demand: usize,
276 logic: L,
277 ) -> (
278 Stream<'scope, T, DCB::Container>,
279 VecCollection<'scope, T, DataflowErrorSer, Diff>,
280 )
281 where
282 DCB: ContainerBuilder,
283 L: for<'a, 'b> FnMut(
284 &'a mut DatumVecBorrow<'b>,
285 T,
286 Diff,
287 &mut Session<T, DCB>,
288 &mut Session<T, ECB<T>>,
289 ) -> usize
290 + 'static,
291 {
292 match &self {
295 ArrangementFlavor::Local(oks, errs) => {
296 let (oks, mfp_errs) = CollectionBundle::<T>::flat_map_core_fallible::<_, DCB, _>(
297 oks.clone(),
298 key,
299 max_demand,
300 logic,
301 REFUEL,
302 );
303 let errs = errs.clone().as_collection(|k, &()| k.clone());
304 let errs = errs.concat(mfp_errs.as_collection());
305 (oks, errs)
306 }
307 ArrangementFlavor::Trace(_, oks, errs) => {
308 let (oks, mfp_errs) = CollectionBundle::<T>::flat_map_core_fallible::<_, DCB, _>(
309 oks.clone(),
310 key,
311 max_demand,
312 logic,
313 REFUEL,
314 );
315 let errs = errs.clone().as_collection(|k, &()| k.clone());
316 let errs = errs.concat(mfp_errs.as_collection());
317 (oks, errs)
318 }
319 }
320 }
321
322 pub fn flat_map_ok<DCB, L>(
327 &self,
328 key: Option<&Row>,
329 max_demand: usize,
330 logic: L,
331 ) -> (
332 Stream<'scope, T, DCB::Container>,
333 VecCollection<'scope, T, DataflowErrorSer, Diff>,
334 )
335 where
336 DCB: ContainerBuilder,
339 L: for<'a, 'b> FnMut(&'a mut DatumVecBorrow<'b>, T, Diff, &mut Session<T, DCB>) -> usize
340 + 'static,
341 {
342 match &self {
343 ArrangementFlavor::Local(oks, errs) => {
344 let oks = CollectionBundle::<T>::flat_map_core_ok::<_, DCB, _>(
345 oks.clone(),
346 key,
347 max_demand,
348 logic,
349 REFUEL,
350 );
351 let errs = errs.clone().as_collection(|k, &()| k.clone());
352 (oks, errs)
353 }
354 ArrangementFlavor::Trace(_, oks, errs) => {
355 let oks = CollectionBundle::<T>::flat_map_core_ok::<_, DCB, _>(
356 oks.clone(),
357 key,
358 max_demand,
359 logic,
360 REFUEL,
361 );
362 let errs = errs.clone().as_collection(|k, &()| k.clone());
363 (oks, errs)
364 }
365 }
366 }
367}
368impl<'scope, T: RenderTimestamp> ArrangementFlavor<'scope, T> {
369 pub fn scope(&self) -> Scope<'scope, T> {
371 match self {
372 ArrangementFlavor::Local(oks, _errs) => oks.stream.scope(),
373 ArrangementFlavor::Trace(_gid, oks, _errs) => oks.stream.scope(),
374 }
375 }
376
377 pub fn enter_region<'a>(&self, region: Scope<'a, T>) -> ArrangementFlavor<'a, T> {
379 match self {
380 ArrangementFlavor::Local(oks, errs) => ArrangementFlavor::Local(
381 oks.clone().enter_region(region),
382 errs.clone().enter_region(region),
383 ),
384 ArrangementFlavor::Trace(gid, oks, errs) => ArrangementFlavor::Trace(
385 *gid,
386 oks.clone().enter_region(region),
387 errs.clone().enter_region(region),
388 ),
389 }
390 }
391}
392impl<'scope, T: RenderTimestamp> ArrangementFlavor<'scope, T> {
393 pub fn leave_region<'outer>(&self, outer: Scope<'outer, T>) -> ArrangementFlavor<'outer, T> {
395 match self {
396 ArrangementFlavor::Local(oks, errs) => ArrangementFlavor::Local(
397 oks.clone().leave_region(outer),
398 errs.clone().leave_region(outer),
399 ),
400 ArrangementFlavor::Trace(gid, oks, errs) => ArrangementFlavor::Trace(
401 *gid,
402 oks.clone().leave_region(outer),
403 errs.clone().leave_region(outer),
404 ),
405 }
406 }
407}
408pub(crate) fn distinct_arranged_errs<'a, T: RenderTimestamp>(
420 errs: Arranged<'a, ErrAgent<T, Diff>>,
421 name: &str,
422) -> Arranged<'a, ErrAgent<T, Diff>> {
423 errs.mz_reduce_abelian::<_, ErrBuilder<_, _>, ErrSpine<_, _>, _>(
424 name,
425 |_err, _input, output| output.push(((), Diff::ONE)),
426 )
427}
428
429pub(crate) fn distinct_errs_collection<'a, T: RenderTimestamp>(
434 errs: VecCollection<'a, T, DataflowErrorSer, Diff>,
435) -> VecCollection<'a, T, DataflowErrorSer, Diff> {
436 let errs: KeyCollection<_, _, _> = errs.into();
437 let errs = errs
438 .mz_arrange::<ColumnationChunker<_>, ErrBatcher<_, _>, ErrBuilder<_, _>, ErrSpine<_, _>>(
439 "Arrange errors",
440 );
441 distinct_arranged_errs(errs, "Distinct errors").as_collection(|err, _| err.clone())
442}
443
444#[derive(Clone)]
449pub struct CollectionBundle<'scope, T: RenderTimestamp> {
450 pub collection: Option<(
451 ColCollection<'scope, T>,
452 VecCollection<'scope, T, DataflowErrorSer, Diff>,
453 )>,
454 pub arranged: BTreeMap<Vec<LirScalarExpr>, ArrangementFlavor<'scope, T>>,
455}
456
457impl<'scope, T: RenderTimestamp> CollectionBundle<'scope, T> {
458 pub fn from_edge(
460 oks: ColCollection<'scope, T>,
461 errs: VecCollection<'scope, T, DataflowErrorSer, Diff>,
462 ) -> Self {
463 Self {
464 collection: Some((oks, errs)),
465 arranged: BTreeMap::default(),
466 }
467 }
468
469 pub fn from_expressions(
471 exprs: Vec<LirScalarExpr>,
472 arrangements: ArrangementFlavor<'scope, T>,
473 ) -> Self {
474 let mut arranged = BTreeMap::new();
475 arranged.insert(exprs, arrangements);
476 Self {
477 collection: None,
478 arranged,
479 }
480 }
481
482 pub fn from_columns<I: IntoIterator<Item = usize>>(
484 columns: I,
485 arrangements: ArrangementFlavor<'scope, T>,
486 ) -> Self {
487 let mut keys = Vec::new();
488 for column in columns {
489 keys.push(LirScalarExpr::column(column));
490 }
491 Self::from_expressions(keys, arrangements)
492 }
493
494 pub fn scope(&self) -> Scope<'scope, T> {
496 if let Some((oks, _errs)) = &self.collection {
497 oks.scope()
498 } else {
499 self.arranged
500 .values()
501 .next()
502 .expect("Must contain a valid collection")
503 .scope()
504 }
505 }
506
507 pub fn distinct_errs(mut self) -> Self {
533 if let Some((oks, errs)) = self.collection.take() {
534 self.collection = Some((oks, distinct_errs_collection(errs)));
535 }
536 for (key, flavor) in std::mem::take(&mut self.arranged) {
537 let flavor = match flavor {
538 ArrangementFlavor::Local(oks, errs) => {
539 let name = format!("Distinct errors[{key:?}]");
542 ArrangementFlavor::Local(oks, distinct_arranged_errs(errs, &name))
543 }
544 flavor @ ArrangementFlavor::Trace(..) => flavor,
545 };
546 self.arranged.insert(key, flavor);
547 }
548 self
549 }
550
551 pub fn enter_region<'inner>(&self, region: Scope<'inner, T>) -> CollectionBundle<'inner, T> {
553 CollectionBundle {
554 collection: self.collection.as_ref().map(|(oks, errs)| {
555 (
556 oks.clone().enter_region(region),
557 errs.clone().enter_region(region),
558 )
559 }),
560 arranged: self
561 .arranged
562 .iter()
563 .map(|(key, bundle)| (key.clone(), bundle.enter_region(region)))
564 .collect(),
565 }
566 }
567}
568
569impl<'scope, T: RenderTimestamp> CollectionBundle<'scope, T> {
570 pub fn leave_region<'outer>(&self, outer: Scope<'outer, T>) -> CollectionBundle<'outer, T> {
572 CollectionBundle {
573 collection: self.collection.as_ref().map(|(oks, errs)| {
574 (
575 oks.clone().leave_region(outer),
576 errs.clone().leave_region(outer),
577 )
578 }),
579 arranged: self
580 .arranged
581 .iter()
582 .map(|(key, bundle)| (key.clone(), bundle.leave_region(outer)))
583 .collect(),
584 }
585 }
586}
587
588impl<'scope, T: RenderTimestamp> CollectionBundle<'scope, T> {
589 pub fn as_specific_collection(
604 &self,
605 key: Option<&[LirScalarExpr]>,
606 ) -> (
607 ColCollection<'scope, T>,
608 VecCollection<'scope, T, DataflowErrorSer, Diff>,
609 ) {
610 match key {
616 None => self
617 .collection
618 .clone()
619 .expect("The unarranged collection doesn't exist."),
620 Some(key) => {
621 let arranged = self.arranged.get(key).unwrap_or_else(|| {
622 panic!("The collection arranged by {:?} doesn't exist.", key)
623 });
624 let (ok, err) =
628 arranged.flat_map_ok::<ColumnBuilder<(Row, T, Diff)>, _>(None, usize::MAX, {
629 let mut row_buf = Row::default();
632 move |borrow, t, r, ok_session| {
633 row_buf.packer().extend(borrow.iter());
634 ok_session.give((&row_buf, &t, &r));
635 1
636 }
637 });
638 (ok.as_collection(), err)
639 }
640 }
641 }
642
643 pub fn flat_map<DCB, L>(
659 &self,
660 key_val: Option<(Vec<LirScalarExpr>, Option<Row>)>,
661 max_demand: usize,
662 logic: L,
663 ) -> (
664 Stream<'scope, T, DCB::Container>,
665 VecCollection<'scope, T, DataflowErrorSer, Diff>,
666 )
667 where
668 DCB: ContainerBuilder,
669 L: for<'a> FnMut(
670 &'a mut DatumVecBorrow<'_>,
671 T,
672 Diff,
673 &mut Session<T, DCB>,
674 &mut Session<T, ECB<T>>,
675 ) -> usize
676 + 'static,
677 {
678 if let Some((key, val)) = key_val {
682 self.arrangement(&key)
683 .expect("Should have ensured during planning that this arrangement exists.")
684 .flat_map::<DCB, _>(val.as_ref(), max_demand, logic)
685 } else {
686 let (oks, errs) = self
687 .collection
688 .clone()
689 .expect("Invariant violated: CollectionBundle contains no collection.");
690 let (ok_stream, err_stream) =
691 flat_map_datums::<_, DCB, _>(oks, "CollectionFlatMap", max_demand, logic);
692 let errs = errs.concat(err_stream.as_collection());
693 (ok_stream, errs)
694 }
695 }
696
697 fn flat_map_core_fallible<Tr, DCB, L>(
708 trace: Arranged<'scope, Tr>,
709 key: Option<&<<BatchCursor<Tr> as Cursor>::KeyContainer as BatchContainer>::Owned>,
710 max_demand: usize,
711 mut logic: L,
712 refuel: usize,
713 ) -> (
714 Stream<'scope, T, DCB::Container>,
715 Stream<'scope, T, Vec<(DataflowErrorSer, T, Diff)>>,
716 )
717 where
718 Tr: TraceReader<Batch: Navigable, Time = T> + Clone + 'static,
719 for<'a> BatchCursor<Tr>:
720 Cursor<Key<'a>: ExtendDatums, Val<'a>: ExtendDatums, Time = T, Diff = mz_repr::Diff>,
721 <<BatchCursor<Tr> as Cursor>::KeyContainer as BatchContainer>::Owned: PartialEq,
722 DCB: ContainerBuilder,
723 L: for<'a, 'b> FnMut(
727 &'a mut DatumVecBorrow<'b>,
728 T,
729 mz_repr::Diff,
730 &mut Session<T, DCB>,
731 &mut Session<T, ECB<T>>,
732 ) -> usize
733 + 'static,
734 {
735 let scope = trace.stream.scope();
736
737 let mut key_con = <BatchCursor<Tr> as Cursor>::KeyContainer::with_capacity(1);
738 if let Some(key) = &key {
739 key_con.push_own(key);
740 }
741 let mode = if key.is_some() { "index" } else { "scan" };
742 let name = format!("ArrangementFlatMap({})", mode);
743
744 let mut builder = OperatorBuilder::new(name, scope.clone());
745 let (ok_output, ok_stream) = builder.new_output();
746 let mut ok_output = OutputBuilder::<_, DCB>::from(ok_output);
747 let (err_output, err_stream) = builder.new_output();
748 let mut err_output = OutputBuilder::<_, ECB<T>>::from(err_output);
749 let mut input = builder.new_input(trace.stream.clone(), Pipeline);
750 let operator_info = builder.operator_info();
751
752 builder.build(move |_capabilities| {
753 let activator = scope.activator_for(operator_info.address);
755 let mut todo = std::collections::VecDeque::new();
757 move |_frontiers| {
758 let key = key_con.get(0);
759 let mut ok_output = ok_output.activate();
760 let mut err_output = err_output.activate();
761
762 input.for_each(|time, data| {
764 let ok_cap = time.retain(0);
767 let err_cap = time.retain(1);
768 for batch in data.iter() {
769 todo.push_back(PendingWork::new(
770 ok_cap.clone(),
771 err_cap.clone(),
772 batch.cursor(),
773 batch.clone(),
774 ));
775 }
776 });
777
778 let mut temp_storage = RowArena::new();
783 let mut datums = DatumVec::new();
784 let mut decode_logic =
785 |k: BatchKey<'_, Tr>,
786 v: BatchVal<'_, Tr>,
787 t: T,
788 d: mz_repr::Diff,
789 ok_session: &mut Session<T, DCB>,
790 err_session: &mut Session<T, ECB<T>>| {
791 temp_storage.clear();
792 let mut datums_borrow = datums.borrow();
793 k.extend_datums(&temp_storage, &mut datums_borrow, Some(max_demand));
794 let remaining = max_demand.saturating_sub(datums_borrow.len());
795 v.extend_datums(&temp_storage, &mut datums_borrow, Some(remaining));
796 logic(&mut datums_borrow, t, d, ok_session, err_session)
797 };
798
799 let mut fuel = refuel;
801 while !todo.is_empty() && fuel > 0 {
802 todo.front_mut().unwrap().do_work(
803 key.as_ref(),
804 &mut decode_logic,
805 &mut fuel,
806 &mut ok_output,
807 &mut err_output,
808 );
809 if fuel > 0 {
810 todo.pop_front();
811 }
812 }
813 if !todo.is_empty() {
815 activator.activate();
816 }
817 }
818 });
819
820 (ok_stream, err_stream)
821 }
822
823 fn flat_map_core_ok<Tr, DCB, L>(
829 trace: Arranged<'scope, Tr>,
830 key: Option<&<<BatchCursor<Tr> as Cursor>::KeyContainer as BatchContainer>::Owned>,
831 max_demand: usize,
832 mut logic: L,
833 refuel: usize,
834 ) -> Stream<'scope, T, DCB::Container>
835 where
836 Tr: TraceReader<Batch: Navigable, Time = T> + Clone + 'static,
837 for<'a> BatchCursor<Tr>:
838 Cursor<Key<'a>: ExtendDatums, Val<'a>: ExtendDatums, Time = T, Diff = mz_repr::Diff>,
839 <<BatchCursor<Tr> as Cursor>::KeyContainer as BatchContainer>::Owned: PartialEq,
840 DCB: ContainerBuilder,
841 L: for<'a, 'b> FnMut(
842 &'a mut DatumVecBorrow<'b>,
843 T,
844 mz_repr::Diff,
845 &mut Session<T, DCB>,
846 ) -> usize
847 + 'static,
848 {
849 let scope = trace.stream.scope();
850
851 let mut key_con = <BatchCursor<Tr> as Cursor>::KeyContainer::with_capacity(1);
852 if let Some(key) = &key {
853 key_con.push_own(key);
854 }
855 let mode = if key.is_some() { "index" } else { "scan" };
856 let name = format!("ArrangementFlatMapOk({})", mode);
857
858 let mut builder = OperatorBuilder::new(name, scope.clone());
859 let (ok_output, ok_stream) = builder.new_output();
860 let mut ok_output = OutputBuilder::<_, DCB>::from(ok_output);
861 let mut input = builder.new_input(trace.stream.clone(), Pipeline);
862 let operator_info = builder.operator_info();
863
864 builder.build(move |_capabilities| {
865 let activator = scope.activator_for(operator_info.address);
866 let mut todo = std::collections::VecDeque::new();
867 move |_frontiers| {
868 let key = key_con.get(0);
869 let mut ok_output = ok_output.activate();
870
871 input.for_each(|time, data| {
872 let cap = time.retain(0);
873 for batch in data.iter() {
874 todo.push_back(PendingWorkOk::new(
875 cap.clone(),
876 batch.cursor(),
877 batch.clone(),
878 ));
879 }
880 });
881
882 let mut temp_storage = RowArena::new();
884 let mut datums = DatumVec::new();
885 let mut decode_logic =
886 |k: BatchKey<'_, Tr>,
887 v: BatchVal<'_, Tr>,
888 t: T,
889 d: mz_repr::Diff,
890 ok_session: &mut Session<T, DCB>| {
891 temp_storage.clear();
892 let mut datums_borrow = datums.borrow();
893 k.extend_datums(&temp_storage, &mut datums_borrow, Some(max_demand));
894 let remaining = max_demand.saturating_sub(datums_borrow.len());
895 v.extend_datums(&temp_storage, &mut datums_borrow, Some(remaining));
896 logic(&mut datums_borrow, t, d, ok_session)
897 };
898
899 let mut fuel = refuel;
900 while !todo.is_empty() && fuel > 0 {
901 todo.front_mut().unwrap().do_work(
902 key.as_ref(),
903 &mut decode_logic,
904 &mut fuel,
905 &mut ok_output,
906 );
907 if fuel > 0 {
908 todo.pop_front();
909 }
910 }
911 if !todo.is_empty() {
912 activator.activate();
913 }
914 }
915 });
916
917 ok_stream
918 }
919
920 pub fn arrangement(&self, key: &[LirScalarExpr]) -> Option<ArrangementFlavor<'scope, T>> {
925 self.arranged.get(key).map(|x| x.clone())
926 }
927}
928
929impl<'scope, T: RenderTimestamp> CollectionBundle<'scope, T> {
930 pub fn as_collection_core(
939 &self,
940 mut mfp_plan: MfpPlan<LirScalarExpr>,
941 key_val: Option<(Vec<LirScalarExpr>, Option<StableRow>)>,
942 until: Antichain<mz_repr::Timestamp>,
943 ) -> (
944 ColCollection<'scope, T>,
945 VecCollection<'scope, T, DataflowErrorSer, Diff>,
946 ) {
947 let key_val = key_val.map(|(key, val)| (key, val.map(|val| val.0)));
950 let has_key_val = if let Some((_key, Some(_val))) = &key_val {
956 true
957 } else {
958 false
959 };
960
961 if mfp_plan.is_identity() && !has_key_val {
962 let key = key_val.map(|(k, _v)| k);
963 return match key {
964 None => self
967 .collection
968 .clone()
969 .expect("The unarranged collection doesn't exist."),
970 Some(key) => self.as_specific_collection(Some(&key)),
971 };
972 }
973
974 let max_demand = mfp_plan.demand().last().map(|x| *x + 1).unwrap_or(0);
975 mfp_plan.permute_fn(|c| c, max_demand);
976
977 let mut datum_vec = DatumVec::new();
978 let until = std::rc::Rc::new(until);
980
981 let (stream, errors) = self.flat_map::<ConsolidatingColumnBuilder<Row, T, Diff>, _>(
985 key_val,
986 max_demand,
987 move |row_datums, time, diff, ok_session, err_session| {
988 let mut row_builder = SharedRow::get();
989 let until = std::rc::Rc::clone(&until);
990 let temp_storage = RowArena::new();
991 let row_iter = row_datums.iter();
992 let mut datums_local = datum_vec.borrow();
993 datums_local.extend(row_iter);
994 let event_time = time.event_time();
995 let mut work: usize = 0;
996 for result in mfp_plan.evaluate(
997 &mut datums_local,
998 &temp_storage,
999 event_time,
1000 diff.clone(),
1001 move |time| !until.less_equal(time),
1002 &mut row_builder,
1003 ) {
1004 work += 1;
1005 match result {
1006 Ok((row, event_time, diff)) => {
1007 let mut time: T = time.clone();
1009 *time.event_time_mut() = event_time;
1010 ok_session.give((row, time, diff));
1011 }
1012 Err((e, event_time, diff)) => {
1013 let mut time: T = time.clone();
1015 *time.event_time_mut() = event_time;
1016 err_session.give((e, time, diff));
1017 }
1018 }
1019 }
1020 work
1021 },
1022 );
1023
1024 (stream.as_collection(), errors)
1025 }
1026 pub fn ensure_collections(
1027 mut self,
1028 collections: AvailableCollections,
1029 input_key: Option<Vec<LirScalarExpr>>,
1030 input_mfp: MfpPlan<LirScalarExpr>,
1031 as_of: Antichain<mz_repr::Timestamp>,
1032 until: Antichain<mz_repr::Timestamp>,
1033 config_set: &ConfigSet,
1034 strategy: ArrangementStrategy,
1035 ) -> Self
1036 where
1037 T: MaybeBucketByTime,
1038 {
1039 if collections == Default::default() {
1040 return self;
1041 }
1042 for (key, _, _) in collections.arranged.iter() {
1051 soft_assert_or_log!(
1052 !self.arranged.contains_key(key),
1053 "LIR ArrangeBy tried to create an existing arrangement"
1054 );
1055 }
1056
1057 let mut bucketed = false;
1060
1061 let will_create_arrangement = collections
1065 .arranged
1066 .iter()
1067 .any(|(key, _, _)| !self.arranged.contains_key(key));
1068
1069 let form_raw_collection = collections.raw || will_create_arrangement;
1071 if form_raw_collection && self.collection.is_none() {
1072 let (oks, errs) =
1073 self.as_collection_core(input_mfp, input_key.map(|k| (k, None)), until);
1074 let effective_strategy = if will_create_arrangement {
1078 strategy
1079 } else {
1080 ArrangementStrategy::Direct
1081 };
1082 let oks = if matches!(effective_strategy, ArrangementStrategy::TemporalBucketing) {
1083 let summary: mz_repr::Timestamp = TEMPORAL_BUCKETING_SUMMARY
1084 .get(config_set)
1085 .try_into()
1086 .expect("must fit");
1087 bucketed = true;
1088 T::maybe_apply_temporal_bucketing(oks.inner, as_of.clone(), summary)
1090 } else {
1091 oks
1092 };
1093 self.collection = Some((oks, errs));
1094 }
1095 for (key, _, thinning) in collections.arranged {
1096 if !self.arranged.contains_key(&key) {
1097 let name = format!("ArrangeBy[{:?}]", key);
1099
1100 let (oks, errs) = self
1101 .collection
1102 .take()
1103 .expect("Collection constructed above");
1104 let effective_strategy = if bucketed {
1109 ArrangementStrategy::Direct
1110 } else {
1111 strategy
1112 };
1113 let oks = if matches!(effective_strategy, ArrangementStrategy::TemporalBucketing) {
1114 let summary: mz_repr::Timestamp = TEMPORAL_BUCKETING_SUMMARY
1115 .get(config_set)
1116 .try_into()
1117 .expect("must fit");
1118 bucketed = true;
1119 T::maybe_apply_temporal_bucketing(oks.inner, as_of.clone(), summary)
1121 } else {
1122 oks
1123 };
1124 let batcher = ArrangementBatcher::from_config(config_set);
1125 let (oks, errs_keyed, passthrough) =
1126 Self::arrange_collection(&name, oks, key.clone(), thinning.clone(), batcher);
1127 let errs_concat: KeyCollection<_, _, _> = errs.clone().concat(errs_keyed).into();
1128 self.collection = Some((passthrough, errs));
1129 let errs =
1130 errs_concat.mz_arrange::<
1131 ColumnationChunker<_>,
1132 ErrBatcher<_, _>,
1133 ErrBuilder<_, _>,
1134 ErrSpine<_, _>,
1135 >(
1136 &format!("{}-errors", name),
1137 );
1138 self.arranged
1139 .insert(key, ArrangementFlavor::Local(oks, errs));
1140 }
1141 }
1142 self
1143 }
1144
1145 fn arrange_collection(
1156 name: &String,
1157 oks: ColCollection<'scope, T>,
1158 key: Vec<LirScalarExpr>,
1159 thinning: Vec<usize>,
1160 batcher: ArrangementBatcher,
1161 ) -> (
1162 Arranged<'scope, RowRowAgent<T, Diff>>,
1163 VecCollection<'scope, T, DataflowErrorSer, Diff>,
1164 ColCollection<'scope, T>,
1165 ) {
1166 let (ok_stream, err_stream, passthrough) = {
1172 let mut builder =
1173 OperatorBuilder::new("FormArrangementKey".to_string(), oks.inner.scope());
1174 let (ok_output, ok_stream) = builder.new_output();
1175 let mut ok_output =
1176 OutputBuilder::<_, ColumnBuilder<((Row, Row), T, Diff)>>::from(ok_output);
1177 let (err_output, err_stream) = builder.new_output();
1178 let mut err_output = OutputBuilder::from(err_output);
1179 let (passthrough_output, passthrough_stream) = builder.new_output();
1180 let mut passthrough_output =
1181 OutputBuilder::<_, NoopBuilder<Column<(Row, T, Diff)>>>::from(passthrough_output);
1182 let mut input = builder.new_input(oks.inner, Pipeline);
1183 builder.set_notify_for(0, FrontierInterest::Never);
1184 builder.build(move |_capabilities| {
1185 let mut key_buf = Row::default();
1186 let mut val_buf = Row::default();
1187 let mut datums = DatumVec::new();
1188 move |_frontiers| {
1189 let mut temp_storage = RowArena::new();
1193 let mut ok_output = ok_output.activate();
1194 let mut err_output = err_output.activate();
1195 let mut passthrough_output = passthrough_output.activate();
1196 input.for_each(|time, data| {
1197 let mut ok_session = ok_output.session_with_builder(&time);
1198 let mut err_session = err_output.session(&time);
1199 for (row, t, d) in data.borrow().into_index_iter() {
1202 temp_storage.clear();
1203 let datums = datums.borrow_with(row);
1204 let key_iter = key.iter().map(|k| k.eval(&datums, &temp_storage));
1205 match key_buf.packer().try_extend(key_iter) {
1206 Ok(()) => {
1207 let val_datum_iter = thinning.iter().map(|c| datums[*c]);
1208 val_buf.packer().extend(val_datum_iter);
1209 ok_session.give(((&*key_buf, &*val_buf), t, d));
1210 }
1211 Err(e) => {
1212 err_session.give((
1213 e.into(),
1214 Columnar::into_owned(t),
1215 Columnar::into_owned(d),
1216 ));
1217 }
1218 }
1219 }
1220 passthrough_output
1221 .session_with_builder(&time)
1222 .give_container(data);
1223 });
1224 }
1225 });
1226 (ok_stream, err_stream, passthrough_stream.as_collection())
1227 };
1228
1229 let exchange =
1230 ExchangeCore::<ColumnBuilder<_>, _>::new_core(columnar_exchange::<Row, Row, T, Diff>);
1231 let oks = match batcher {
1232 ArrangementBatcher::Chunked => ok_stream.mz_arrange_core::<
1233 _,
1234 ChunkChunker<(Row, Row), T, Diff, ComputeSpill>,
1235 AccountedChunkBatcher<(Row, Row), T, Diff, ComputeSpill>,
1236 UnchunkBuilder<RowRowColPagedBuilder<T, Diff>, (Row, Row), T, Diff, ComputeSpill>,
1237 RowRowSpine<_, _>,
1238 >(exchange, name),
1239 ArrangementBatcher::Columnar => ok_stream.mz_arrange_core::<
1240 _,
1241 batcher::ColumnChunker<_>,
1242 Col2ValColBatcher<_, _, _, _>,
1243 RowRowColPagedBuilder<_, _>,
1244 RowRowSpine<_, _>,
1245 >(exchange, name),
1246 ArrangementBatcher::Columnation => ok_stream.mz_arrange_core::<
1247 _,
1248 batcher::Chunker<_>,
1249 Col2ValBatcher<_, _, _, _>,
1250 RowRowBuilder<_, _>,
1251 RowRowSpine<_, _>,
1252 >(exchange, name),
1253 };
1254 (oks, err_stream.as_collection(), passthrough)
1255 }
1256}
1257
1258pub(crate) type Session<'a, 'b, T, CB> =
1262 timely::dataflow::operators::generic::Session<'a, 'b, T, CB, Capability<T>>;
1263
1264pub(crate) type ECB<T> = ConsolidatingContainerBuilder<Vec<(DataflowErrorSer, T, Diff)>>;
1269
1270const REFUEL: usize = 1_000_000;
1274
1275struct PendingWork<C>
1276where
1277 C: Cursor,
1278{
1279 ok_capability: Capability<C::Time>,
1281 err_capability: Capability<C::Time>,
1283 cursor: C,
1284 batch: C::Storage,
1285}
1286
1287impl<C> PendingWork<C>
1288where
1289 C: Cursor<KeyContainer: BatchContainer<Owned: PartialEq + Sized>>,
1290{
1291 fn new(
1294 ok_capability: Capability<C::Time>,
1295 err_capability: Capability<C::Time>,
1296 cursor: C,
1297 batch: C::Storage,
1298 ) -> Self {
1299 Self {
1300 ok_capability,
1301 err_capability,
1302 cursor,
1303 batch,
1304 }
1305 }
1306 fn do_work<DCB, L>(
1309 &mut self,
1310 key: Option<&C::Key<'_>>,
1311 logic: &mut L,
1312 fuel: &mut usize,
1313 ok_output: &mut OutputBuilderSession<'_, C::Time, DCB>,
1314 err_output: &mut OutputBuilderSession<'_, C::Time, ECB<C::Time>>,
1315 ) where
1316 DCB: ContainerBuilder,
1317 L: FnMut(
1318 C::Key<'_>,
1319 C::Val<'_>,
1320 C::Time,
1321 C::Diff,
1322 &mut Session<C::Time, DCB>,
1323 &mut Session<C::Time, ECB<C::Time>>,
1324 ) -> usize,
1325 {
1326 let mut ok_session = ok_output.session_with_builder(&self.ok_capability);
1327 let mut err_session = err_output.session_with_builder(&self.err_capability);
1328 walk_cursor(&mut self.cursor, &self.batch, key, fuel, |k, v, t, d| {
1329 logic(k, v, t, d, &mut ok_session, &mut err_session)
1330 });
1331 }
1332}
1333
1334struct PendingWorkOk<C>
1337where
1338 C: Cursor,
1339{
1340 capability: Capability<C::Time>,
1341 cursor: C,
1342 batch: C::Storage,
1343}
1344
1345impl<C> PendingWorkOk<C>
1346where
1347 C: Cursor<KeyContainer: BatchContainer<Owned: PartialEq + Sized>>,
1348{
1349 fn new(capability: Capability<C::Time>, cursor: C, batch: C::Storage) -> Self {
1350 Self {
1351 capability,
1352 cursor,
1353 batch,
1354 }
1355 }
1356
1357 fn do_work<DCB, L>(
1360 &mut self,
1361 key: Option<&C::Key<'_>>,
1362 logic: &mut L,
1363 fuel: &mut usize,
1364 ok_output: &mut OutputBuilderSession<'_, C::Time, DCB>,
1365 ) where
1366 DCB: ContainerBuilder,
1367 L: FnMut(C::Key<'_>, C::Val<'_>, C::Time, C::Diff, &mut Session<C::Time, DCB>) -> usize,
1368 {
1369 let mut ok_session = ok_output.session_with_builder(&self.capability);
1370 walk_cursor(&mut self.cursor, &self.batch, key, fuel, |k, v, t, d| {
1371 logic(k, v, t, d, &mut ok_session)
1372 });
1373 }
1374}
1375
1376fn walk_cursor<C, F>(
1386 cursor: &mut C,
1387 batch: &C::Storage,
1388 key: Option<&C::Key<'_>>,
1389 fuel: &mut usize,
1390 mut emit: F,
1391) where
1392 C: Cursor<KeyContainer: BatchContainer<Owned: PartialEq + Sized>>,
1393 F: FnMut(C::Key<'_>, C::Val<'_>, C::Time, C::Diff) -> usize,
1394{
1395 use differential_dataflow::consolidation::consolidate;
1396
1397 let mut work: usize = 0;
1398 let mut buffer = Vec::new();
1399 if let Some(key) = key {
1400 let key = C::KeyContainer::reborrow(*key);
1401 if cursor.get_key(batch).map(|k| k == key) != Some(true) {
1402 cursor.seek_key(batch, key);
1403 }
1404 if cursor.get_key(batch).map(|k| k == key) == Some(true) {
1405 let key = cursor.key(batch);
1406 while let Some(val) = cursor.get_val(batch) {
1407 cursor.map_times(batch, |time, diff| {
1408 buffer.push((C::owned_time(time), C::owned_diff(diff)));
1409 });
1410 consolidate(&mut buffer);
1411 for (time, diff) in buffer.drain(..) {
1412 work += emit(key, val, time, diff);
1413 }
1414 cursor.step_val(batch);
1415 if work >= *fuel {
1416 *fuel = 0;
1417 return;
1418 }
1419 }
1420 }
1421 } else {
1422 while let Some(key) = cursor.get_key(batch) {
1423 while let Some(val) = cursor.get_val(batch) {
1424 cursor.map_times(batch, |time, diff| {
1425 buffer.push((C::owned_time(time), C::owned_diff(diff)));
1426 });
1427 consolidate(&mut buffer);
1428 for (time, diff) in buffer.drain(..) {
1429 work += emit(key, val, time, diff);
1430 }
1431 cursor.step_val(batch);
1432 if work >= *fuel {
1433 *fuel = 0;
1434 return;
1435 }
1436 }
1437 cursor.step_key(batch);
1438 }
1439 }
1440 *fuel -= work;
1441}
1442
1443#[cfg(test)]
1444mod tests {
1445 use differential_dataflow::input::Input;
1446 use mz_expr::{EvalError, MapFilterProject};
1447 use mz_repr::{Datum, ReprScalarType, Timestamp};
1448 use timely::dataflow::operators::Capture;
1449 use timely::dataflow::operators::capture::{Event, Extract};
1450
1451 use super::*;
1452 use crate::render::columnar::{columnar_to_vec, vec_to_columnar};
1453
1454 type OkUpdate = ((Row, Row), Timestamp, Diff);
1455 type ErrUpdate = (DataflowErrorSer, Timestamp, Diff);
1456 type Captured<D> = std::sync::mpsc::Receiver<Event<Timestamp, Vec<D>>>;
1457
1458 fn extract_ok(captured: Captured<OkUpdate>) -> Vec<OkUpdate> {
1459 let mut updates: Vec<_> = captured
1460 .extract()
1461 .into_iter()
1462 .flat_map(|(_, data)| data)
1463 .collect();
1464 updates.sort();
1465 updates
1466 }
1467
1468 fn extract_err(captured: Captured<ErrUpdate>) -> Vec<(String, Timestamp, Diff)> {
1470 let mut updates: Vec<_> = captured
1471 .extract()
1472 .into_iter()
1473 .flat_map(|(_, data)| data)
1474 .map(|(e, t, d)| (format!("{e:?}"), t, d))
1475 .collect();
1476 updates.sort();
1477 updates
1478 }
1479
1480 fn arrange_columnar(
1482 rows: Vec<(Row, u64)>,
1483 key: Vec<LirScalarExpr>,
1484 ) -> (Vec<OkUpdate>, Vec<(String, Timestamp, Diff)>) {
1485 let thinning = vec![0, 1];
1486 let (ok, err) = timely::execute_directly(move |worker| {
1487 worker.dataflow::<Timestamp, _, _>(|scope| {
1488 let (mut input, collection) = scope.new_collection();
1489 let (arranged, errs, _passthrough) =
1490 CollectionBundle::<Timestamp>::arrange_collection(
1491 &"col".to_string(),
1492 vec_to_columnar(collection),
1493 key,
1494 thinning,
1495 ArrangementBatcher::Columnation,
1496 );
1497 let ok = arranged
1498 .as_collection(|k, v| (k.to_row(), v.to_row()))
1499 .inner
1500 .capture();
1501 let err = errs.inner.capture();
1502
1503 let max_time = rows.iter().map(|(_, t)| *t).max().unwrap_or(0);
1504 for (row, time) in rows {
1505 input.update_at(row, Timestamp::from(time), Diff::ONE);
1506 }
1507 input.advance_to(Timestamp::from(max_time + 1));
1508 input.flush();
1509 (ok, err)
1510 })
1511 });
1512 (extract_ok(ok), extract_err(err))
1513 }
1514
1515 fn test_rows() -> Vec<(Row, u64)> {
1518 vec![
1519 (Row::pack_slice(&[Datum::Int32(1), Datum::String("a")]), 0),
1520 (Row::pack_slice(&[Datum::Int32(2), Datum::String("b")]), 1),
1521 (Row::pack_slice(&[Datum::Int32(1), Datum::String("a")]), 1),
1522 (Row::pack_slice(&[Datum::Int32(3), Datum::Null]), 2),
1523 ]
1524 }
1525
1526 #[mz_ore::test]
1529 fn arrange_collection_keys_correctly() {
1530 let rows = test_rows();
1531 let mut expected: Vec<OkUpdate> = rows
1532 .iter()
1533 .map(|(row, t)| {
1534 let key = Row::pack_slice(&[row.iter().next().unwrap()]);
1535 ((key, row.clone()), Timestamp::from(*t), Diff::ONE)
1536 })
1537 .collect();
1538 expected.sort();
1539
1540 let (ok, err) = arrange_columnar(rows, vec![LirScalarExpr::column(0)]);
1541 assert_eq!(ok, expected);
1542 assert!(err.is_empty());
1543 }
1544
1545 #[mz_ore::test]
1547 fn arrange_collection_error_path() {
1548 let key = vec![LirScalarExpr::literal(
1549 Err(EvalError::DivisionByZero),
1550 ReprScalarType::Int32,
1551 )];
1552 let (ok, err) = arrange_columnar(test_rows(), key);
1553
1554 assert!(ok.is_empty());
1555 assert!(!err.is_empty());
1556 }
1557
1558 #[mz_ore::test]
1562 fn arrange_collection_passthrough_forwards_input() {
1563 let rows = test_rows();
1564 let mut expected: Vec<(Row, Timestamp, Diff)> = rows
1565 .iter()
1566 .map(|(row, t)| (row.clone(), Timestamp::from(*t), Diff::ONE))
1567 .collect();
1568 expected.sort();
1569
1570 let key = vec![LirScalarExpr::literal(
1571 Err(EvalError::DivisionByZero),
1572 ReprScalarType::Int32,
1573 )];
1574 let captured = timely::execute_directly(move |worker| {
1575 worker.dataflow::<Timestamp, _, _>(|scope| {
1576 let (mut input, collection) = scope.new_collection();
1577 let (_arranged, _errs, passthrough) =
1578 CollectionBundle::<Timestamp>::arrange_collection(
1579 &"col".to_string(),
1580 vec_to_columnar(collection),
1581 key,
1582 vec![0, 1],
1583 ArrangementBatcher::Columnation,
1584 );
1585 let captured = columnar_to_vec(passthrough).inner.capture();
1586
1587 let max_time = rows.iter().map(|(_, t)| *t).max().unwrap_or(0);
1588 for (row, time) in rows {
1589 input.update_at(row, Timestamp::from(time), Diff::ONE);
1590 }
1591 input.advance_to(Timestamp::from(max_time + 1));
1592 input.flush();
1593 captured
1594 })
1595 });
1596
1597 assert_eq!(extract_row_updates(captured), expected);
1598 }
1599
1600 fn extract_row_updates(
1601 captured: Captured<(Row, Timestamp, Diff)>,
1602 ) -> Vec<(Row, Timestamp, Diff)> {
1603 let mut updates: Vec<_> = captured
1604 .extract()
1605 .into_iter()
1606 .flat_map(|(_, data)| data)
1607 .collect();
1608 updates.sort();
1609 updates
1610 }
1611
1612 #[mz_ore::test]
1615 fn get_arrange_by_produces_projected_rows() {
1616 let rows = vec![
1617 (Row::pack_slice(&[Datum::Int64(1), Datum::Int64(10)]), 0u64),
1618 (Row::pack_slice(&[Datum::Int64(2), Datum::Int64(20)]), 1),
1619 (Row::pack_slice(&[Datum::Int64(1), Datum::Int64(10)]), 1),
1620 ];
1621 let mfp = MapFilterProject::<LirScalarExpr>::new(2)
1624 .project(vec![0])
1625 .into_plan()
1626 .expect("mfp");
1627 let mut expected: Vec<(Row, Timestamp, Diff)> = rows
1628 .iter()
1629 .map(|(r, t)| {
1630 let col0 = r.iter().next().unwrap();
1631 (Row::pack_slice(&[col0]), Timestamp::from(*t), Diff::ONE)
1632 })
1633 .collect();
1634 expected.sort();
1635
1636 let produced = timely::execute_directly(move |worker| {
1637 worker.dataflow::<Timestamp, _, _>(|scope| {
1638 let (mut input, collection) = scope.new_collection();
1639 let (_err_input, errs) = scope.new_collection::<DataflowErrorSer, Diff>();
1640 let bundle = CollectionBundle::from_edge(vec_to_columnar(collection), errs);
1641 let (edge, _errs) = bundle.as_collection_core(mfp, None, Antichain::new());
1642 let produced = columnar_to_vec(edge.clone()).inner.capture();
1643 let (_arranged, _arrange_errs, _passthrough) =
1644 CollectionBundle::<Timestamp>::arrange_collection(
1645 &"arrange".to_string(),
1646 edge,
1647 vec![LirScalarExpr::column(0)],
1648 vec![0],
1649 ArrangementBatcher::Columnation,
1650 );
1651
1652 let max_time = rows.iter().map(|(_, t)| *t).max().unwrap();
1653 for (row, time) in rows {
1654 input.update_at(row, Timestamp::from(time), Diff::ONE);
1655 }
1656 input.advance_to(Timestamp::from(max_time + 1));
1657 input.flush();
1658 produced
1659 })
1660 });
1661
1662 assert_eq!(extract_row_updates(produced), expected);
1663 }
1664
1665 #[mz_ore::test]
1668 fn as_collection_core_identity_passes_edge_through() {
1669 let expected = vec![(
1670 Row::pack_slice(&[Datum::Int64(1)]),
1671 Timestamp::from(0u64),
1672 Diff::ONE,
1673 )];
1674 let captured = timely::execute_directly(move |worker| {
1675 worker.dataflow::<Timestamp, _, _>(|scope| {
1676 let (mut input, collection) = scope.new_collection::<Row, Diff>();
1677 let (_err_input, errs) = scope.new_collection::<DataflowErrorSer, Diff>();
1678 let bundle = CollectionBundle::from_edge(vec_to_columnar(collection), errs);
1679 let identity = MapFilterProject::<LirScalarExpr>::new(1)
1680 .into_plan()
1681 .expect("identity mfp");
1682 let (out, _errs) = bundle.as_collection_core(identity, None, Antichain::new());
1683 let captured = columnar_to_vec(out).inner.capture();
1684 input.update_at(
1685 Row::pack_slice(&[Datum::Int64(1)]),
1686 Timestamp::from(0u64),
1687 Diff::ONE,
1688 );
1689 input.advance_to(Timestamp::from(1u64));
1690 input.flush();
1691 captured
1692 })
1693 });
1694 assert_eq!(extract_row_updates(captured), expected);
1695 }
1696
1697 #[mz_ore::test]
1700 fn as_collection_core_consolidates_within_batch() {
1701 let rows = vec![
1705 Row::pack_slice(&[Datum::Int64(1), Datum::Int64(10)]),
1706 Row::pack_slice(&[Datum::Int64(1), Datum::Int64(20)]),
1707 Row::pack_slice(&[Datum::Int64(2), Datum::Int64(30)]),
1708 ];
1709 let mfp = MapFilterProject::<LirScalarExpr>::new(2)
1710 .project(vec![0])
1711 .into_plan()
1712 .expect("mfp");
1713 let expected = vec![
1714 (
1715 Row::pack_slice(&[Datum::Int64(1)]),
1716 Timestamp::from(0u64),
1717 Diff::from(2),
1718 ),
1719 (
1720 Row::pack_slice(&[Datum::Int64(2)]),
1721 Timestamp::from(0u64),
1722 Diff::ONE,
1723 ),
1724 ];
1725
1726 let captured = timely::execute_directly(move |worker| {
1727 worker.dataflow::<Timestamp, _, _>(|scope| {
1728 let (mut input, collection) = scope.new_collection();
1729 let (_err_input, errs) = scope.new_collection::<DataflowErrorSer, Diff>();
1730 let bundle = CollectionBundle::from_edge(vec_to_columnar(collection), errs);
1731 let (edge, _errs) = bundle.as_collection_core(mfp, None, Antichain::new());
1732 let captured = columnar_to_vec(edge).inner.capture();
1733 for row in rows {
1736 input.update_at(row, Timestamp::from(0u64), Diff::ONE);
1737 }
1738 input.advance_to(Timestamp::from(1u64));
1739 input.flush();
1740 captured
1741 })
1742 });
1743
1744 assert_eq!(extract_row_updates(captured), expected);
1745 }
1746
1747 #[mz_ore::test]
1750 fn as_specific_collection_materializes_columnar() {
1751 let rows = test_rows();
1752 let key = vec![LirScalarExpr::column(0)];
1753 let mut expected: Vec<(Row, Timestamp, Diff)> = rows
1754 .iter()
1755 .map(|(r, t)| (r.clone(), Timestamp::from(*t), Diff::ONE))
1756 .collect();
1757 expected.sort();
1758
1759 let captured = timely::execute_directly(move |worker| {
1760 worker.dataflow::<Timestamp, _, _>(|scope| {
1761 let (mut input, collection) = scope.new_collection();
1762 let (arranged, arr_errs, _passthrough) =
1763 CollectionBundle::<Timestamp>::arrange_collection(
1764 &"agg".to_string(),
1765 vec_to_columnar(collection),
1766 key.clone(),
1767 vec![1],
1768 ArrangementBatcher::Columnation,
1769 );
1770 let err_arranged = {
1771 let kc: KeyCollection<_, _, _> = arr_errs.into();
1772 kc.mz_arrange::<
1773 ColumnationChunker<_>,
1774 ErrBatcher<_, _>,
1775 ErrBuilder<_, _>,
1776 ErrSpine<_, _>,
1777 >("agg-errs")
1778 };
1779 let bundle = CollectionBundle::from_columns(
1781 0..1,
1782 ArrangementFlavor::Local(arranged, err_arranged),
1783 );
1784 let (edge, _errs) = bundle.as_specific_collection(Some(&key));
1785 let captured = columnar_to_vec(edge).inner.capture();
1786
1787 let max_time = rows.iter().map(|(_, t)| *t).max().unwrap();
1788 for (row, time) in rows {
1789 input.update_at(row, Timestamp::from(time), Diff::ONE);
1790 }
1791 input.advance_to(Timestamp::from(max_time + 1));
1792 input.flush();
1793 captured
1794 })
1795 });
1796
1797 assert_eq!(extract_row_updates(captured), expected);
1798 }
1799}