1use std::marker::PhantomData;
13use std::sync::Arc;
14use std::time::{Duration, Instant};
15
16use differential_dataflow::lattice::Lattice;
17use mz_compute_types::ComputeInstanceId;
18use mz_compute_types::plan::LirRelationExpr;
19use mz_compute_types::sinks::{ComputeSinkConnection, ComputeSinkDesc, SubscribeSinkConnection};
20use mz_expr::{ColumnOrder, MirRelationExpr};
21use mz_ore::soft_assert_or_log;
22use mz_repr::{GlobalId, RelationDesc, Timestamp};
23use mz_sql::optimizer_metrics::OptimizerMetrics;
24use mz_sql::plan::{HirToMirConfig, SubscribeFrom, SubscribePlan};
25use mz_transform::TransformCtx;
26use mz_transform::dataflow::{DataflowMetainfo, optimize_dataflow_snapshot};
27use mz_transform::normalize_lets::normalize_lets;
28use mz_transform::typecheck::{SharedTypecheckingContext, empty_typechecking_context};
29use timely::progress::Antichain;
30
31use crate::CollectionIdBundle;
32use crate::optimize::dataflows::{
33 ComputeInstanceSnapshot, DataflowBuilder, ExprPrep, ExprPrepMaintained,
34 dataflow_import_id_bundle,
35};
36use crate::optimize::{
37 LirDataflowDescription, MirDataflowDescription, Optimize, OptimizeMode, OptimizerCatalog,
38 OptimizerConfig, OptimizerError, optimize_mir_local, trace_plan,
39};
40
41pub struct Optimizer {
42 typecheck_ctx: SharedTypecheckingContext,
44 catalog: Arc<dyn OptimizerCatalog>,
46 compute_instance: ComputeInstanceSnapshot,
48 sink_id: GlobalId,
50 view_id: GlobalId,
53 with_snapshot: bool,
55 up_to: Option<Timestamp>,
57 debug_name: String,
59 config: OptimizerConfig,
61 metrics: OptimizerMetrics,
63 duration: Duration,
65}
66
67impl std::fmt::Debug for Optimizer {
73 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
74 f.debug_struct("Optimizer")
75 .field("config", &self.config)
76 .finish_non_exhaustive()
77 }
78}
79
80impl Optimizer {
81 pub fn new(
82 catalog: Arc<dyn OptimizerCatalog>,
83 compute_instance: ComputeInstanceSnapshot,
84 view_id: GlobalId,
85 sink_id: GlobalId,
86 with_snapshot: bool,
87 up_to: Option<Timestamp>,
88 debug_name: String,
89 config: OptimizerConfig,
90 metrics: OptimizerMetrics,
91 ) -> Self {
92 Self {
93 typecheck_ctx: empty_typechecking_context(),
94 catalog,
95 compute_instance,
96 view_id,
97 sink_id,
98 with_snapshot,
99 up_to,
100 debug_name,
101 config,
102 metrics,
103 duration: Default::default(),
104 }
105 }
106
107 pub fn cluster_id(&self) -> ComputeInstanceId {
108 self.compute_instance.instance_id()
109 }
110
111 pub fn up_to(&self) -> Option<Timestamp> {
112 self.up_to.clone()
113 }
114
115 pub fn sink_id(&self) -> GlobalId {
116 self.sink_id
117 }
118
119 pub fn optimize_query(
127 &mut self,
128 expr: MirRelationExpr,
129 from_desc: RelationDesc,
130 output: Vec<ColumnOrder>,
131 ) -> Result<GlobalMirPlan<Unresolved>, OptimizerError> {
132 self.optimize_inner(SubscribeSource::Query { expr, from_desc }, output)
133 }
134
135 fn optimize_inner(
139 &mut self,
140 source: SubscribeSource,
141 output: Vec<ColumnOrder>,
142 ) -> Result<GlobalMirPlan<Unresolved>, OptimizerError> {
143 let time = Instant::now();
144
145 let mut df_builder = {
146 let compute = self.compute_instance.clone();
147 DataflowBuilder::new(&*self.catalog, compute).with_config(&self.config)
148 };
149 let mut df_desc = MirDataflowDescription::new(self.debug_name.clone());
150 let mut df_meta = DataflowMetainfo::default();
151
152 let (from, from_desc) = match source {
153 SubscribeSource::Id(from_id) => {
154 let from_desc = self
155 .catalog
156 .get_entry(&from_id)
157 .relation_desc()
158 .expect("subscribes can only be run on items with descs")
159 .into_owned();
160
161 df_builder.import_into_dataflow(&from_id, &mut df_desc, &self.config.features)?;
162
163 (from_id, from_desc)
164 }
165 SubscribeSource::Query { expr, from_desc } => {
166 let mut transform_ctx = TransformCtx::local(
168 &self.config.features,
169 &self.typecheck_ctx,
170 &mut df_meta,
171 Some(&mut self.metrics),
172 Some(self.view_id),
173 );
174 let expr = optimize_mir_local(expr, &mut transform_ctx)?;
175
176 df_builder.import_view_into_dataflow(
177 &self.view_id,
178 &expr,
179 &mut df_desc,
180 &self.config.features,
181 )?;
182
183 (self.view_id, from_desc)
184 }
185 };
186 df_builder.maybe_reoptimize_imported_views(&mut df_desc, &self.config)?;
187
188 let sink_description = ComputeSinkDesc {
190 from,
191 from_desc,
192 connection: ComputeSinkConnection::Subscribe(SubscribeSinkConnection { output }),
193 with_snapshot: self.with_snapshot,
194 up_to: self.up_to.map(Antichain::from_elem).unwrap_or_default(),
195 non_null_assertions: vec![],
197 refresh_schedule: None,
199 from_arrangement: None,
201 };
202 df_desc.export_sink(self.sink_id, sink_description);
203
204 let style = ExprPrepMaintained;
206 df_desc.visit_children(
207 |r| style.prep_relation_expr(r),
208 |s| style.prep_scalar_expr(s),
209 )?;
210
211 let mut transform_ctx = TransformCtx::global(
213 &df_builder,
214 &mz_transform::EmptyStatisticsOracle, &self.config.features,
216 &self.typecheck_ctx,
217 &mut df_meta,
218 Some(&mut self.metrics),
219 );
220 mz_transform::optimize_dataflow(&mut df_desc, &mut transform_ctx, false)?;
222
223 if self.config.mode == OptimizeMode::Explain {
224 trace_plan!(at: "global", &df_meta.used_indexes(&df_desc));
226 }
227
228 self.duration += time.elapsed();
229
230 Ok(GlobalMirPlan {
232 df_desc,
233 df_meta,
234 phantom: PhantomData::<Unresolved>,
235 })
236 }
237}
238
239enum SubscribeSource {
241 Id(GlobalId),
243 Query {
245 expr: MirRelationExpr,
246 from_desc: RelationDesc,
247 },
248}
249
250#[derive(Clone, Debug)]
256pub struct GlobalMirPlan<T: Clone> {
257 df_desc: MirDataflowDescription,
258 df_meta: DataflowMetainfo,
259 phantom: PhantomData<T>,
260}
261
262impl<T: Clone> GlobalMirPlan<T> {
263 pub fn id_bundle(&self, compute_instance_id: ComputeInstanceId) -> CollectionIdBundle {
265 dataflow_import_id_bundle(&self.df_desc, compute_instance_id)
266 }
267}
268
269#[derive(Clone, Debug)]
272pub struct GlobalLirPlan {
273 df_desc: LirDataflowDescription,
274 df_meta: DataflowMetainfo,
275}
276
277impl GlobalLirPlan {
278 pub fn sink_id(&self) -> GlobalId {
284 self.df_desc.sink_id()
285 }
286}
287
288#[derive(Clone, Debug)]
291pub struct Unresolved;
292
293#[derive(Clone, Debug)]
299pub struct Resolved;
300
301impl Optimize<SubscribePlan> for Optimizer {
302 type To = GlobalMirPlan<Unresolved>;
303
304 fn optimize(&mut self, plan: SubscribePlan) -> Result<Self::To, OptimizerError> {
305 let output = plan.output.row_order().to_vec();
306
307 match plan.from {
308 SubscribeFrom::Id(from_id) => self.optimize_inner(SubscribeSource::Id(from_id), output),
309 SubscribeFrom::Query { expr, desc } => {
310 let time = Instant::now();
319 let expr = expr.lower(HirToMirConfig::from(&self.config), None)?;
320 self.duration += time.elapsed();
321
322 self.optimize_inner(
323 SubscribeSource::Query {
324 expr,
325 from_desc: desc,
326 },
327 output,
328 )
329 }
330 }
331 }
332}
333
334impl GlobalMirPlan<Unresolved> {
335 pub fn resolve(mut self, as_of: Antichain<Timestamp>) -> GlobalMirPlan<Resolved> {
341 soft_assert_or_log!(
344 self.df_desc.index_exports.is_empty(),
345 "unexpectedly setting until for a DataflowDescription with an index",
346 );
347
348 self.df_desc.set_as_of(as_of);
350
351 self.df_desc.until = Antichain::from_elem(Timestamp::MIN);
355 for (_, sink) in &self.df_desc.sink_exports {
356 self.df_desc.until.join_assign(&sink.up_to);
357 }
358
359 GlobalMirPlan {
360 df_desc: self.df_desc,
361 df_meta: self.df_meta,
362 phantom: PhantomData::<Resolved>,
363 }
364 }
365}
366
367impl Optimize<GlobalMirPlan<Resolved>> for Optimizer {
368 type To = GlobalLirPlan;
369
370 fn optimize(&mut self, plan: GlobalMirPlan<Resolved>) -> Result<Self::To, OptimizerError> {
371 let time = Instant::now();
372
373 let GlobalMirPlan {
374 mut df_desc,
375 df_meta,
376 phantom: _,
377 } = plan;
378
379 for build in df_desc.objects_to_build.iter_mut() {
381 normalize_lets(&mut build.plan.0, &self.config.features)?
382 }
383
384 if self.config.subscribe_snapshot_optimization {
385 optimize_dataflow_snapshot(&mut df_desc)?;
387 }
388
389 let df_desc = LirRelationExpr::finalize_dataflow(
393 df_desc,
394 &self.config.features,
395 Some(self.metrics.lowering()),
396 )?;
397
398 self.duration += time.elapsed();
399 self.metrics
400 .observe_e2e_optimization_time("subscribe", self.duration);
401
402 Ok(GlobalLirPlan { df_desc, df_meta })
404 }
405}
406
407impl GlobalLirPlan {
408 pub fn unapply(self) -> (LirDataflowDescription, DataflowMetainfo) {
410 (self.df_desc, self.df_meta)
411 }
412}