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 config(&self) -> &OptimizerConfig {
120 &self.config
121 }
122
123 pub fn optimize_query(
131 &mut self,
132 expr: MirRelationExpr,
133 from_desc: RelationDesc,
134 output: Vec<ColumnOrder>,
135 ) -> Result<GlobalMirPlan<Unresolved>, OptimizerError> {
136 self.optimize_inner(SubscribeSource::Query { expr, from_desc }, output)
137 }
138
139 fn optimize_inner(
143 &mut self,
144 source: SubscribeSource,
145 output: Vec<ColumnOrder>,
146 ) -> Result<GlobalMirPlan<Unresolved>, OptimizerError> {
147 let time = Instant::now();
148
149 let mut df_builder = {
150 let compute = self.compute_instance.clone();
151 DataflowBuilder::new(&*self.catalog, compute).with_config(&self.config)
152 };
153 let mut df_desc = MirDataflowDescription::new(self.debug_name.clone());
154 let mut df_meta = DataflowMetainfo::default();
155
156 let (from, from_desc) = match source {
157 SubscribeSource::Id(from_id) => {
158 let from_desc = self
159 .catalog
160 .get_entry(&from_id)
161 .relation_desc()
162 .expect("subscribes can only be run on items with descs")
163 .into_owned();
164
165 df_builder.import_into_dataflow(&from_id, &mut df_desc, &self.config.features)?;
166
167 (from_id, from_desc)
168 }
169 SubscribeSource::Query { expr, from_desc } => {
170 let mut transform_ctx = TransformCtx::local(
172 &self.config.features,
173 &self.typecheck_ctx,
174 &mut df_meta,
175 Some(&mut self.metrics),
176 Some(self.view_id),
177 );
178 let expr = optimize_mir_local(expr, &mut transform_ctx)?;
179
180 df_builder.import_view_into_dataflow(
181 &self.view_id,
182 &expr,
183 &mut df_desc,
184 &self.config.features,
185 )?;
186
187 (self.view_id, from_desc)
188 }
189 };
190 df_builder.maybe_reoptimize_imported_views(&mut df_desc, &self.config)?;
191
192 let sink_description = ComputeSinkDesc {
194 from,
195 from_desc,
196 connection: ComputeSinkConnection::Subscribe(SubscribeSinkConnection { output }),
197 with_snapshot: self.with_snapshot,
198 up_to: self.up_to.map(Antichain::from_elem).unwrap_or_default(),
199 non_null_assertions: vec![],
201 refresh_schedule: None,
203 from_arrangement: None,
205 };
206 df_desc.export_sink(self.sink_id, sink_description);
207
208 let style = ExprPrepMaintained;
210 df_desc.visit_children(
211 |r| style.prep_relation_expr(r),
212 |s| style.prep_scalar_expr(s),
213 )?;
214
215 let mut transform_ctx = TransformCtx::global(
217 &df_builder,
218 &mz_transform::EmptyStatisticsOracle, &self.config.features,
220 &self.typecheck_ctx,
221 &mut df_meta,
222 Some(&mut self.metrics),
223 );
224 mz_transform::optimize_dataflow(&mut df_desc, &mut transform_ctx, false)?;
226
227 if self.config.mode == OptimizeMode::Explain {
228 trace_plan!(at: "global", &df_meta.used_indexes(&df_desc));
230 }
231
232 self.duration += time.elapsed();
233
234 Ok(GlobalMirPlan {
236 df_desc,
237 df_meta,
238 phantom: PhantomData::<Unresolved>,
239 })
240 }
241}
242
243enum SubscribeSource {
245 Id(GlobalId),
247 Query {
249 expr: MirRelationExpr,
250 from_desc: RelationDesc,
251 },
252}
253
254#[derive(Clone, Debug)]
260pub struct GlobalMirPlan<T: Clone> {
261 df_desc: MirDataflowDescription,
262 df_meta: DataflowMetainfo,
263 phantom: PhantomData<T>,
264}
265
266impl<T: Clone> GlobalMirPlan<T> {
267 pub fn id_bundle(&self, compute_instance_id: ComputeInstanceId) -> CollectionIdBundle {
269 dataflow_import_id_bundle(&self.df_desc, compute_instance_id)
270 }
271}
272
273#[derive(Clone, Debug)]
276pub struct GlobalLirPlan {
277 df_desc: LirDataflowDescription,
278 df_meta: DataflowMetainfo,
279}
280
281impl GlobalLirPlan {
282 pub fn sink_id(&self) -> GlobalId {
288 self.df_desc.sink_id()
289 }
290}
291
292#[derive(Clone, Debug)]
295pub struct Unresolved;
296
297#[derive(Clone, Debug)]
303pub struct Resolved;
304
305impl Optimize<SubscribePlan> for Optimizer {
306 type To = GlobalMirPlan<Unresolved>;
307
308 fn optimize(&mut self, plan: SubscribePlan) -> Result<Self::To, OptimizerError> {
309 let output = plan.output.row_order().to_vec();
310
311 match plan.from {
312 SubscribeFrom::Id(from_id) => self.optimize_inner(SubscribeSource::Id(from_id), output),
313 SubscribeFrom::Query { expr, desc } => {
314 let time = Instant::now();
323 let expr = expr.lower(HirToMirConfig::from(&self.config), None)?;
324 self.duration += time.elapsed();
325
326 self.optimize_inner(
327 SubscribeSource::Query {
328 expr,
329 from_desc: desc,
330 },
331 output,
332 )
333 }
334 }
335 }
336}
337
338impl GlobalMirPlan<Unresolved> {
339 pub fn resolve(mut self, as_of: Antichain<Timestamp>) -> GlobalMirPlan<Resolved> {
345 soft_assert_or_log!(
348 self.df_desc.index_exports.is_empty(),
349 "unexpectedly setting until for a DataflowDescription with an index",
350 );
351
352 self.df_desc.set_as_of(as_of);
354
355 self.df_desc.until = Antichain::from_elem(Timestamp::MIN);
359 for (_, sink) in &self.df_desc.sink_exports {
360 self.df_desc.until.join_assign(&sink.up_to);
361 }
362
363 GlobalMirPlan {
364 df_desc: self.df_desc,
365 df_meta: self.df_meta,
366 phantom: PhantomData::<Resolved>,
367 }
368 }
369}
370
371impl Optimize<GlobalMirPlan<Resolved>> for Optimizer {
372 type To = GlobalLirPlan;
373
374 fn optimize(&mut self, plan: GlobalMirPlan<Resolved>) -> Result<Self::To, OptimizerError> {
375 let time = Instant::now();
376
377 let GlobalMirPlan {
378 mut df_desc,
379 df_meta,
380 phantom: _,
381 } = plan;
382
383 for build in df_desc.objects_to_build.iter_mut() {
385 normalize_lets(&mut build.plan.0, &self.config.features)?
386 }
387
388 if self.config.subscribe_snapshot_optimization {
389 optimize_dataflow_snapshot(&mut df_desc)?;
391 }
392
393 let df_desc = LirRelationExpr::finalize_dataflow(
397 df_desc,
398 &self.config.features,
399 Some(self.metrics.lowering()),
400 )?;
401
402 self.duration += time.elapsed();
403 self.metrics
404 .observe_e2e_optimization_time("subscribe", self.duration);
405
406 Ok(GlobalLirPlan { df_desc, df_meta })
408 }
409}
410
411impl GlobalLirPlan {
412 pub fn unapply(self) -> (LirDataflowDescription, DataflowMetainfo) {
414 (self.df_desc, self.df_meta)
415 }
416}