1use std::collections::{BTreeMap, BTreeSet};
11use std::sync::Arc;
12
13use mz_adapter_types::dyncfgs::PLAN_INSIGHTS_NOTICE_FAST_PATH_CLUSTERS_OPTIMIZE_DURATION;
14use mz_compute_types::sinks::ComputeSinkConnection;
15use mz_controller_types::ClusterId;
16use mz_expr::{CollectionPlan, ResultSpec};
17use mz_ore::cast::CastFrom;
18use mz_ore::instrument;
19use mz_repr::optimize::{OptimizerFeatures, OverrideFrom};
20use mz_repr::{Datum, GlobalId, Timestamp};
21use mz_sql::ast::{ExplainStage, Statement};
22use mz_sql::catalog::CatalogCluster;
23use mz_sql::plan::QueryWhen;
25use mz_sql::plan::{self};
26use mz_sql::session::metadata::SessionMetadata;
27use mz_transform::EmptyStatisticsOracle;
28use tokio::sync::oneshot;
29use tracing::warn;
30use tracing::{Instrument, Span};
31
32use crate::active_compute_sink::{ActiveComputeSink, ActiveCopyTo};
33use crate::command::ExecuteResponse;
34use crate::coord::id_bundle::CollectionIdBundle;
35use crate::coord::peek::{self, PeekDataflowPlan, PeekPlan, PlannedPeek};
36use crate::coord::sequencer::inner::{return_if_err, spawn_linearized_read_ts};
37use crate::coord::sequencer::{check_log_reads, emit_optimizer_notices, eval_copy_to_uri};
38use crate::coord::timeline::{TimelineContext, timedomain_for};
39use crate::coord::timestamp_selection::{TimestampContext, TimestampDetermination};
40use crate::coord::{
41 Coordinator, CopyToContext, ExecuteContext, ExplainContext, ExplainPlanContext, Message,
42 PeekStage, PeekStageCopyTo, PeekStageExplainPlan, PeekStageExplainPushdown, PeekStageFinish,
43 PeekStageLinearizeTimestamp, PeekStageOptimize, PeekStageRealTimeRecency,
44 PeekStageTimestampReadHold, PlanValidity, StageResult, Staged, TargetCluster,
45};
46use crate::error::AdapterError;
47use crate::explain::insights::PlanInsightsContext;
48use crate::explain::optimizer_trace::OptimizerTrace;
49use crate::notice::AdapterNotice;
50use crate::optimize;
51use crate::session::{RequireLinearization, Session, TransactionOps, TransactionStatus};
52use crate::statement_logging::StatementLifecycleEvent;
53use crate::statement_logging::WatchSetCreation;
54
55impl Staged for PeekStage {
56 type Ctx = ExecuteContext;
57
58 fn validity(&mut self) -> &mut PlanValidity {
59 match self {
60 PeekStage::LinearizeTimestamp(stage) => &mut stage.validity,
61 PeekStage::RealTimeRecency(stage) => &mut stage.validity,
62 PeekStage::TimestampReadHold(stage) => &mut stage.validity,
63 PeekStage::Optimize(stage) => &mut stage.validity,
64 PeekStage::Finish(stage) => &mut stage.validity,
65 PeekStage::ExplainPlan(stage) => &mut stage.validity,
66 PeekStage::ExplainPushdown(stage) => &mut stage.validity,
67 PeekStage::CopyToPreflight(stage) => &mut stage.validity,
68 PeekStage::CopyToDataflow(stage) => &mut stage.validity,
69 }
70 }
71
72 async fn stage(
73 self,
74 coord: &mut Coordinator,
75 ctx: &mut ExecuteContext,
76 ) -> Result<StageResult<Box<Self>>, AdapterError> {
77 match self {
78 PeekStage::LinearizeTimestamp(stage) => {
79 coord.peek_linearize_timestamp(ctx.session(), stage).await
80 }
81 PeekStage::RealTimeRecency(stage) => {
82 coord.peek_real_time_recency(ctx.session(), stage).await
83 }
84 PeekStage::TimestampReadHold(stage) => {
85 coord.peek_timestamp_read_hold(ctx.session_mut(), stage)
86 }
87 PeekStage::Optimize(stage) => coord.peek_optimize(ctx.session(), stage).await,
88 PeekStage::Finish(stage) => coord.peek_finish(ctx, stage).await,
89 PeekStage::ExplainPlan(stage) => coord.peek_explain_plan(ctx.session(), stage).await,
90 PeekStage::ExplainPushdown(stage) => {
91 coord.peek_explain_pushdown(ctx.session(), stage).await
92 }
93 PeekStage::CopyToPreflight(stage) => coord.peek_copy_to_preflight(stage).await,
94 PeekStage::CopyToDataflow(stage) => coord.peek_copy_to_dataflow(ctx, stage).await,
95 }
96 }
97
98 fn message(self, ctx: ExecuteContext, span: Span) -> Message {
99 Message::PeekStageReady {
100 ctx,
101 span,
102 stage: self,
103 }
104 }
105
106 fn cancel_enabled(&self) -> bool {
107 true
108 }
109}
110
111impl Coordinator {
112 #[instrument]
119 pub(crate) async fn sequence_peek(
120 &mut self,
121 ctx: ExecuteContext,
122 plan: plan::SelectPlan,
123 target_cluster: TargetCluster,
124 max_query_result_size: Option<u64>,
125 ) {
126 let explain_ctx = if ctx.session().vars().emit_plan_insights_notice() {
127 let optimizer_trace = OptimizerTrace::new(ExplainStage::PlanInsights.paths());
128 ExplainContext::PlanInsightsNotice(optimizer_trace)
129 } else {
130 ExplainContext::None
131 };
132
133 let stage = return_if_err!(
134 self.peek_validate(
135 ctx.session(),
136 plan,
137 target_cluster,
138 None,
139 explain_ctx,
140 max_query_result_size
141 ),
142 ctx
143 );
144 self.sequence_staged(ctx, Span::current(), stage).await;
145 }
146
147 #[instrument]
148 pub(crate) async fn sequence_copy_to(
149 &mut self,
150 ctx: ExecuteContext,
151 plan::CopyToPlan {
152 select_plan,
153 desc,
154 to,
155 connection,
156 connection_id,
157 format,
158 max_file_size,
159 }: plan::CopyToPlan,
160 target_cluster: TargetCluster,
161 ) {
162 let uri = return_if_err!(
163 eval_copy_to_uri(to, ctx.session(), self.catalog().state()),
164 ctx
165 );
166
167 let stage = return_if_err!(
168 self.peek_validate(
169 ctx.session(),
170 select_plan,
171 target_cluster,
172 Some(CopyToContext {
173 desc,
174 uri,
175 connection,
176 connection_id,
177 format,
178 max_file_size,
179 output_batch_count: None,
181 }),
182 ExplainContext::None,
183 Some(ctx.session().vars().max_query_result_size()),
184 ),
185 ctx
186 );
187 self.sequence_staged(ctx, Span::current(), stage).await;
188 }
189
190 #[instrument]
191 pub(crate) async fn explain_peek(
192 &mut self,
193 ctx: ExecuteContext,
194 plan::ExplainPlanPlan {
195 stage,
196 format,
197 config,
198 explainee,
199 }: plan::ExplainPlanPlan,
200 target_cluster: TargetCluster,
201 ) {
202 let plan::Explainee::Statement(stmt) = explainee else {
203 unreachable!()
206 };
207 let plan::ExplaineeStatement::Select { broken, plan, desc } = stmt else {
208 unreachable!()
211 };
212
213 let optimizer_trace = OptimizerTrace::new(stage.paths());
216
217 let stage = return_if_err!(
218 self.peek_validate(
219 ctx.session(),
220 plan,
221 target_cluster,
222 None,
223 ExplainContext::Plan(ExplainPlanContext {
224 broken,
225 config,
226 format,
227 stage,
228 replan: None,
229 desc: Some(desc),
230 optimizer_trace,
231 }),
232 Some(ctx.session().vars().max_query_result_size()),
233 ),
234 ctx
235 );
236 self.sequence_staged(ctx, Span::current(), stage).await;
237 }
238
239 #[instrument]
241 pub fn peek_validate(
242 &self,
243 session: &Session,
244 plan: mz_sql::plan::SelectPlan,
245 target_cluster: TargetCluster,
246 copy_to_ctx: Option<CopyToContext>,
247 explain_ctx: ExplainContext,
248 max_query_result_size: Option<u64>,
249 ) -> Result<PeekStage, AdapterError> {
250 let catalog = self.owned_catalog();
252 let cluster = catalog.resolve_target_cluster(target_cluster, session)?;
253 let compute_instance = self
254 .instance_snapshot(cluster.id())
255 .expect("compute instance does not exist");
256 let optimizer_config = optimize::OptimizerConfig::from(self.catalog().system_config())
257 .override_from(&self.catalog.get_cluster(cluster.id()).config.features())
258 .override_from(&self.cluster_scoped_optimizer_overrides(cluster.id()))
259 .override_from(&explain_ctx);
260
261 if cluster.replicas().next().is_none() && explain_ctx.needs_cluster() {
262 return Err(AdapterError::NoClusterReplicasAvailable {
263 name: cluster.name.clone(),
264 is_managed: cluster.is_managed(),
265 });
266 }
267
268 let optimizer = match copy_to_ctx {
269 None => {
270 let (_, view_id) = self.allocate_transient_id();
272 let (_, index_id) = self.allocate_transient_id();
273
274 optimize::PeekOptimizer::Select(optimize::peek::Optimizer::new(
276 Arc::clone(&catalog),
277 compute_instance,
278 plan.finishing.clone(),
279 view_id,
280 index_id,
281 optimizer_config,
282 self.optimizer_metrics(),
283 ))
284 }
285 Some(mut copy_to_ctx) => {
286 let worker_counts = cluster.replicas().map(|r| {
290 let loc = &r.config.location;
291 loc.workers().unwrap_or_else(|| loc.num_processes())
292 });
293 let max_worker_count = match worker_counts.max() {
294 Some(count) => u64::cast_from(count),
295 None => {
296 return Err(AdapterError::NoClusterReplicasAvailable {
297 name: cluster.name.clone(),
298 is_managed: cluster.is_managed(),
299 });
300 }
301 };
302 copy_to_ctx.output_batch_count = Some(max_worker_count);
303 let (_, view_id) = self.allocate_transient_id();
304 optimize::PeekOptimizer::CopyTo(optimize::copy_to::Optimizer::new(
306 Arc::clone(&catalog),
307 compute_instance,
308 view_id,
309 copy_to_ctx,
310 optimizer_config,
311 self.optimizer_metrics(),
312 ))
313 }
314 };
315
316 let target_replica_name = session.vars().cluster_replica();
317 let mut target_replica = target_replica_name
318 .map(|name| {
319 cluster
320 .replica_id(name)
321 .ok_or(AdapterError::UnknownClusterReplica {
322 cluster_name: cluster.name.clone(),
323 replica_name: name.to_string(),
324 })
325 })
326 .transpose()?;
327
328 let source_ids = plan.source.depends_on();
329 let mut timeline_context = self
330 .catalog()
331 .validate_timeline_context(source_ids.iter().copied())?;
332 if matches!(timeline_context, TimelineContext::TimestampIndependent)
333 && plan.source.contains_temporal()
334 {
335 timeline_context = TimelineContext::TimestampDependent;
339 }
340
341 let notices = check_log_reads(
342 &catalog,
343 cluster,
344 &source_ids,
345 &mut target_replica,
346 session.vars(),
347 )?;
348 session.add_notices(notices);
349
350 let dependencies = source_ids
351 .iter()
352 .map(|id| self.catalog.resolve_item_id(id))
353 .collect();
354 let validity = PlanValidity::new(
355 &self.catalog,
356 dependencies,
357 Some(cluster.id()),
358 target_replica,
359 session.role_metadata().clone(),
360 );
361
362 Ok(PeekStage::LinearizeTimestamp(PeekStageLinearizeTimestamp {
363 validity,
364 plan,
365 max_query_result_size,
366 source_ids,
367 target_replica,
368 timeline_context,
369 optimizer,
370 explain_ctx,
371 }))
372 }
373
374 #[instrument]
376 async fn peek_linearize_timestamp(
377 &self,
378 session: &Session,
379 PeekStageLinearizeTimestamp {
380 validity,
381 source_ids,
382 plan,
383 max_query_result_size,
384 target_replica,
385 timeline_context,
386 optimizer,
387 explain_ctx,
388 }: PeekStageLinearizeTimestamp,
389 ) -> Result<StageResult<Box<PeekStage>>, AdapterError> {
390 let oracle = self.linearized_read_ts_oracle(session, &timeline_context, &plan.when);
391
392 let build_stage = move |oracle_read_ts: Option<Timestamp>| PeekStageRealTimeRecency {
393 validity,
394 plan,
395 max_query_result_size,
396 source_ids,
397 target_replica,
398 timeline_context,
399 oracle_read_ts,
400 optimizer,
401 explain_ctx,
402 };
403
404 Ok(spawn_linearized_read_ts(
405 oracle,
406 "linearize timestamp",
407 move |oracle_read_ts| PeekStage::RealTimeRecency(build_stage(oracle_read_ts)),
408 ))
409 }
410
411 #[instrument]
413 fn peek_timestamp_read_hold(
414 &mut self,
415 session: &mut Session,
416 PeekStageTimestampReadHold {
417 mut validity,
418 plan,
419 max_query_result_size,
420 source_ids,
421 target_replica,
422 timeline_context,
423 oracle_read_ts,
424 real_time_recency_ts,
425 optimizer,
426 explain_ctx,
427 }: PeekStageTimestampReadHold,
428 ) -> Result<StageResult<Box<PeekStage>>, AdapterError> {
429 let cluster_id = optimizer.cluster_id();
430 let id_bundle = self
431 .dataflow_builder(cluster_id)
432 .sufficient_collections(source_ids.iter().copied());
433
434 let item_ids = id_bundle
437 .iter()
438 .map(|id| self.catalog().resolve_item_id(&id));
439 validity.extend_dependencies(self.catalog(), item_ids);
440
441 let determination = self.sequence_peek_timestamp(
442 session,
443 &plan.when,
444 cluster_id,
445 timeline_context,
446 oracle_read_ts,
447 &id_bundle,
448 &source_ids,
449 real_time_recency_ts,
450 (&explain_ctx).into(),
451 )?;
452
453 let stage = PeekStage::Optimize(PeekStageOptimize {
454 validity,
455 plan,
456 max_query_result_size,
457 source_ids,
458 id_bundle,
459 target_replica,
460 determination,
461 optimizer,
462 explain_ctx,
463 });
464 Ok(StageResult::Immediate(Box::new(stage)))
465 }
466
467 #[instrument]
468 async fn peek_optimize(
469 &self,
470 session: &Session,
471 PeekStageOptimize {
472 validity,
473 plan,
474 max_query_result_size,
475 source_ids,
476 id_bundle,
477 target_replica,
478 determination,
479 mut optimizer,
480 explain_ctx,
481 }: PeekStageOptimize,
482 ) -> Result<StageResult<Box<PeekStage>>, AdapterError> {
483 let timestamp_context = determination.timestamp_context.clone();
486 let stats = self
487 .statistics_oracle(session, &source_ids, ×tamp_context.antichain(), true)
488 .await
489 .unwrap_or_else(|_| Box::new(EmptyStatisticsOracle));
490 let session = session.meta();
491 let now = self.catalog().config().now.clone();
492 let catalog = self.owned_catalog();
493 let mut compute_instances = BTreeMap::new();
494 if explain_ctx.needs_plan_insights() {
495 for cluster in self.catalog().user_clusters() {
498 let snapshot = self.instance_snapshot(cluster.id).expect("must exist");
499 compute_instances.insert(cluster.name.clone(), snapshot);
500 }
501 }
502
503 let span = Span::current();
504 Ok(StageResult::Handle(mz_ore::task::spawn_blocking(
505 || "optimize peek",
506 move || {
507 span.in_scope(|| {
508 let pipeline_result = {
514 let _dispatch_guard = explain_ctx.dispatch_guard();
515
516 let raw_expr = plan.source.clone();
517
518 optimizer
519 .optimize(raw_expr, timestamp_context.clone(), &session, stats)
520 .map_err(AdapterError::from)
521 };
522
523 let optimization_finished_at = now();
524
525 let stage = match pipeline_result {
526 Ok(optimize::PeekGlobalLirPlan::Select(global_lir_plan)) => {
527 let optimizer =
528 optimizer.into_select().expect("a SELECT/EXPLAIN optimizer");
529 let needs_plan_insights = explain_ctx.needs_plan_insights();
531 let opt_limit =
536 PLAN_INSIGHTS_NOTICE_FAST_PATH_CLUSTERS_OPTIMIZE_DURATION
537 .get(catalog.system_config().dyncfgs());
538 let target_instance =
539 catalog.get_cluster(optimizer.cluster_id()).name.clone();
540 let enable_re_optimize =
541 !(matches!(explain_ctx, ExplainContext::PlanInsightsNotice(_))
542 && optimizer.duration() > opt_limit);
543 let insights_ctx = needs_plan_insights
544 .then(|| PlanInsightsContext {
545 stmt: plan
546 .select
547 .as_deref()
548 .map(Clone::clone)
549 .map(Statement::Select),
550 raw_expr: plan.source.clone(),
551 catalog,
552 compute_instances,
553 target_instance,
554 metrics: optimizer.metrics().clone(),
555 finishing: optimizer.finishing().clone(),
556 optimizer_config: optimizer.config().clone(),
557 session,
558 timestamp_context,
559 view_id: optimizer.select_id(),
560 index_id: optimizer.index_id(),
561 enable_re_optimize,
562 })
563 .map(Box::new);
564 match explain_ctx {
565 ExplainContext::Plan(explain_ctx) => {
566 let (_, df_meta, _) = global_lir_plan.unapply();
567 PeekStage::ExplainPlan(PeekStageExplainPlan {
568 validity,
569 optimizer,
570 df_meta,
571 explain_ctx,
572 insights_ctx,
573 })
574 }
575 ExplainContext::PlanInsightsNotice(optimizer_trace) => {
576 PeekStage::Finish(PeekStageFinish {
577 validity,
578 plan,
579 max_query_result_size,
580 id_bundle,
581 target_replica,
582 source_ids,
583 determination,
584 cluster_id: optimizer.cluster_id(),
585 finishing: optimizer.finishing().clone(),
586 plan_insights_optimizer_trace: Some(optimizer_trace),
587 global_lir_plan,
588 optimization_finished_at,
589 insights_ctx,
590 })
591 }
592 ExplainContext::None => PeekStage::Finish(PeekStageFinish {
593 validity,
594 plan,
595 max_query_result_size,
596 id_bundle,
597 target_replica,
598 source_ids,
599 determination,
600 cluster_id: optimizer.cluster_id(),
601 finishing: optimizer.finishing().clone(),
602 plan_insights_optimizer_trace: None,
603 global_lir_plan,
604 optimization_finished_at,
605 insights_ctx,
606 }),
607 ExplainContext::Pushdown => {
608 let (plan, _, _) = global_lir_plan.unapply();
609 let imports = match plan {
610 PeekPlan::SlowPath(plan) => plan
611 .desc
612 .source_imports
613 .into_iter()
614 .filter_map(|(id, import)| {
615 import.desc.arguments.operators.map(|mfp| (id, mfp))
616 })
617 .collect(),
618 PeekPlan::FastPath(_) => BTreeMap::default(),
619 };
620 PeekStage::ExplainPushdown(PeekStageExplainPushdown {
621 validity,
622 determination,
623 imports,
624 })
625 }
626 }
627 }
628 Ok(optimize::PeekGlobalLirPlan::CopyTo(global_lir_plan)) => {
629 let optimizer = optimizer.into_copy_to().expect("a COPY TO optimizer");
630 PeekStage::CopyToPreflight(PeekStageCopyTo {
631 validity,
632 optimizer,
633 global_lir_plan,
634 optimization_finished_at,
635 target_replica,
636 source_ids,
637 })
638 }
639 Err(err) => {
642 let Some(optimizer) = optimizer.into_select() else {
643 return Err(err);
646 };
647 let ExplainContext::Plan(explain_ctx) = explain_ctx else {
648 return Err(err);
651 };
652
653 if explain_ctx.broken {
654 tracing::error!("error while handling EXPLAIN statement: {}", err);
658 PeekStage::ExplainPlan(PeekStageExplainPlan {
659 validity,
660 optimizer,
661 df_meta: Default::default(),
662 explain_ctx,
663 insights_ctx: None,
664 })
665 } else {
666 return Err(err);
669 }
670 }
671 };
672 Ok(Box::new(stage))
673 })
674 },
675 )))
676 }
677
678 #[instrument]
679 async fn peek_real_time_recency(
680 &self,
681 session: &Session,
682 PeekStageRealTimeRecency {
683 validity,
684 plan,
685 max_query_result_size,
686 source_ids,
687 target_replica,
688 timeline_context,
689 oracle_read_ts,
690 optimizer,
691 explain_ctx,
692 }: PeekStageRealTimeRecency,
693 ) -> Result<StageResult<Box<PeekStage>>, AdapterError> {
694 let fut = self
695 .determine_real_time_recent_timestamp_if_needed(session, source_ids.iter().copied())
696 .await?;
697
698 match fut {
699 Some(fut) => {
700 let catalog = Arc::clone(&self.catalog);
701 let span = Span::current();
702 Ok(StageResult::Handle(mz_ore::task::spawn(
703 || "peek real time recency",
704 async move {
705 let real_time_recency_ts =
706 Coordinator::await_real_time_recent_timestamp(catalog, fut).await?;
707 let stage = PeekStage::TimestampReadHold(PeekStageTimestampReadHold {
708 validity,
709 plan,
710 max_query_result_size,
711 target_replica,
712 timeline_context,
713 source_ids,
714 optimizer,
715 explain_ctx,
716 oracle_read_ts,
717 real_time_recency_ts: Some(real_time_recency_ts),
718 });
719 Ok(Box::new(stage))
720 }
721 .instrument(span),
722 )))
723 }
724 None => Ok(StageResult::Immediate(Box::new(
725 PeekStage::TimestampReadHold(PeekStageTimestampReadHold {
726 validity,
727 plan,
728 max_query_result_size,
729 target_replica,
730 timeline_context,
731 source_ids,
732 optimizer,
733 explain_ctx,
734 oracle_read_ts,
735 real_time_recency_ts: None,
736 }),
737 ))),
738 }
739 }
740
741 #[instrument]
742 async fn peek_finish(
743 &mut self,
744 ctx: &mut ExecuteContext,
745 PeekStageFinish {
746 validity: _,
747 plan,
748 max_query_result_size,
749 id_bundle,
750 target_replica,
751 source_ids,
752 determination,
753 cluster_id,
754 finishing,
755 plan_insights_optimizer_trace,
756 global_lir_plan,
757 optimization_finished_at,
758 insights_ctx,
759 }: PeekStageFinish,
760 ) -> Result<StageResult<Box<PeekStage>>, AdapterError> {
761 if let Some(id) = ctx.extra.contents() {
762 self.record_statement_lifecycle_event(
763 &id,
764 &StatementLifecycleEvent::OptimizationFinished,
765 optimization_finished_at,
766 );
767 }
768
769 let session = ctx.session_mut();
770 let conn_id = session.conn_id().clone();
771
772 let (peek_plan, df_meta, typ) = global_lir_plan.unapply();
773 let source_arity = typ.arity();
774
775 emit_optimizer_notices(&*self.catalog, &*session, &df_meta.optimizer_notices);
776
777 if let Some(trace) = plan_insights_optimizer_trace {
778 let target_cluster = self.catalog().get_cluster(cluster_id);
779 let features = OptimizerFeatures::from(self.catalog().system_config())
780 .override_from(&target_cluster.config.features())
781 .override_from(&self.cluster_scoped_optimizer_overrides(cluster_id));
782 let insights = trace
783 .into_plan_insights(
784 &features,
785 &self.catalog().for_session(session),
786 Some(plan.finishing),
787 Some(target_cluster),
788 df_meta,
789 insights_ctx,
790 )
791 .await?;
792 session.add_notice(AdapterNotice::PlanInsights(insights));
793 }
794
795 let planned_peek = PlannedPeek {
796 plan: peek_plan,
797 determination: determination.clone(),
798 conn_id: conn_id.clone(),
799 intermediate_result_type: typ,
800 source_arity,
801 source_ids,
802 };
803
804 if let Some(transient_index_id) = match &planned_peek.plan {
805 peek::PeekPlan::FastPath(_) => None,
806 peek::PeekPlan::SlowPath(PeekDataflowPlan { id, .. }) => Some(id),
807 } {
808 if let Some(statement_logging_id) = ctx.extra.contents() {
809 self.set_transient_index_id(statement_logging_id, *transient_index_id);
810 }
811 }
812
813 if let Some(logging_id) = ctx.extra().contents() {
814 let watch_set = WatchSetCreation::new(
815 logging_id,
816 self.catalog.state(),
817 &id_bundle,
818 determination.timestamp_context.timestamp_or_default(),
819 );
820 self.install_peek_watch_sets(conn_id.clone(), watch_set).expect("the old peek sequencing re-verifies the dependencies' existence before installing the new watch sets");
821 }
822
823 let max_result_size = self.catalog().system_config().max_result_size();
824
825 let resp = self
827 .implement_peek_plan(
828 ctx.extra_mut(),
829 planned_peek,
830 finishing,
831 cluster_id,
832 target_replica,
833 max_result_size,
834 max_query_result_size,
835 )
836 .await?;
837
838 if ctx.session().vars().emit_timestamp_notice() {
839 let explanation = self.explain_timestamp(
840 ctx.session().conn_id(),
841 ctx.session().pcx().wall_time,
842 cluster_id,
843 &id_bundle,
844 determination,
845 );
846 ctx.session()
847 .add_notice(AdapterNotice::QueryTimestamp { explanation });
848 }
849
850 let resp = match plan.copy_to {
851 None => resp,
852 Some(format) => ExecuteResponse::CopyTo {
853 format,
854 resp: Box::new(resp),
855 },
856 };
857 Ok(StageResult::Response(resp))
858 }
859
860 #[instrument]
861 async fn peek_copy_to_preflight(
862 &self,
863 copy_to: PeekStageCopyTo,
864 ) -> Result<StageResult<Box<PeekStage>>, AdapterError> {
865 let connection_context = self.connection_context().clone();
866 let enforce_external_addresses = mz_storage_types::dyncfgs::ENFORCE_EXTERNAL_ADDRESSES
867 .get(self.controller.storage.config().config_set());
868 Ok(StageResult::Handle(mz_ore::task::spawn(
869 || "peek copy to preflight",
870 async move {
871 let sinks = ©_to.global_lir_plan.df_desc().sink_exports;
872 if sinks.len() != 1 {
873 return Err(AdapterError::Internal(
874 "expected exactly one copy to s3 sink".into(),
875 ));
876 }
877 let (sink_id, sink_desc) = sinks
878 .first_key_value()
879 .expect("known to be exactly one copy to s3 sink");
880 match &sink_desc.connection {
881 ComputeSinkConnection::CopyToS3Oneshot(conn) => {
882 mz_storage_types::sinks::s3_oneshot_sink::preflight(
883 connection_context,
884 &conn.aws_connection,
885 &conn.upload_info,
886 conn.connection_id,
887 *sink_id,
888 enforce_external_addresses,
889 )
890 .await?;
891 Ok(Box::new(PeekStage::CopyToDataflow(copy_to)))
892 }
893 _ => Err(AdapterError::Internal(
894 "expected copy to s3 oneshot sink".into(),
895 )),
896 }
897 },
898 )))
899 }
900
901 #[instrument]
902 async fn peek_copy_to_dataflow(
903 &mut self,
904 ctx: &ExecuteContext,
905 PeekStageCopyTo {
906 validity: _,
907 optimizer,
908 global_lir_plan,
909 optimization_finished_at,
910 target_replica,
911 source_ids,
912 }: PeekStageCopyTo,
913 ) -> Result<StageResult<Box<PeekStage>>, AdapterError> {
914 if let Some(id) = ctx.extra.contents() {
915 self.record_statement_lifecycle_event(
916 &id,
917 &StatementLifecycleEvent::OptimizationFinished,
918 optimization_finished_at,
919 );
920 }
921
922 let sink_id = global_lir_plan.sink_id();
923 let cluster_id = optimizer.cluster_id();
924
925 let (df_desc, df_meta) = global_lir_plan.unapply();
926
927 emit_optimizer_notices(&*self.catalog, ctx.session(), &df_meta.optimizer_notices);
928
929 let (tx, rx) = oneshot::channel();
931 let active_copy_to = ActiveCopyTo {
932 conn_id: ctx.session().conn_id().clone(),
933 tx,
934 cluster_id,
935 depends_on: source_ids,
936 };
937 drop(self.add_active_compute_sink(sink_id, ActiveComputeSink::CopyTo(active_copy_to)));
939
940 self.ship_dataflow(df_desc, cluster_id, target_replica)
942 .await;
943
944 let span = Span::current();
945 Ok(StageResult::HandleRetire(mz_ore::task::spawn(
946 || "peek copy to dataflow",
947 async {
948 let res = rx.await;
949 match res {
950 Ok(res) => res,
951 Err(_) => Err(AdapterError::Internal("copy to sender dropped".into())),
952 }
953 }
954 .instrument(span),
955 )))
956 }
957
958 #[instrument]
959 async fn peek_explain_plan(
960 &self,
961 session: &Session,
962 PeekStageExplainPlan {
963 optimizer,
964 insights_ctx,
965 df_meta,
966 explain_ctx,
967 ..
968 }: PeekStageExplainPlan,
969 ) -> Result<StageResult<Box<PeekStage>>, AdapterError> {
970 let rows = super::super::explain_plan_inner(
971 session,
972 self.catalog(),
973 df_meta,
974 explain_ctx,
975 optimizer,
976 insights_ctx,
977 )
978 .await?;
979
980 Ok(StageResult::Response(Self::send_immediate_rows(rows)))
981 }
982
983 #[instrument]
984 async fn peek_explain_pushdown(
985 &self,
986 session: &Session,
987 stage: PeekStageExplainPushdown,
988 ) -> Result<StageResult<Box<PeekStage>>, AdapterError> {
989 let as_of = stage.determination.timestamp_context.antichain();
990 let mz_now = stage
991 .determination
992 .timestamp_context
993 .timestamp()
994 .map(|t| ResultSpec::value(Datum::MzTimestamp(*t)))
995 .unwrap_or_else(ResultSpec::value_all);
996 let fut = self
997 .explain_pushdown_future(session, as_of, mz_now, stage.imports)
998 .await;
999 let span = Span::current();
1000 Ok(StageResult::HandleRetire(mz_ore::task::spawn(
1001 || "peek explain pushdown",
1002 fut.instrument(span),
1003 )))
1004 }
1005
1006 #[instrument]
1009 pub(super) fn sequence_peek_timestamp(
1010 &mut self,
1011 session: &mut Session,
1012 when: &QueryWhen,
1013 cluster_id: ClusterId,
1014 timeline_context: TimelineContext,
1015 oracle_read_ts: Option<Timestamp>,
1016 source_bundle: &CollectionIdBundle,
1017 source_ids: &BTreeSet<GlobalId>,
1018 real_time_recency_ts: Option<Timestamp>,
1019 requires_linearization: RequireLinearization,
1020 ) -> Result<TimestampDetermination, AdapterError> {
1021 let in_immediate_multi_stmt_txn = session.transaction().in_immediate_multi_stmt_txn(when);
1022 let timedomain_bundle;
1023
1024 let (determination, read_holds) = match session.get_transaction_timestamp_determination() {
1027 Some(
1029 determination @ TimestampDetermination {
1030 timestamp_context: TimestampContext::TimelineTimestamp { .. },
1031 ..
1032 },
1033 ) if in_immediate_multi_stmt_txn => (determination, None),
1034 _ => {
1035 let determine_bundle = if in_immediate_multi_stmt_txn {
1036 timedomain_bundle = timedomain_for(
1039 self.catalog(),
1040 &self.index_oracle(cluster_id),
1041 source_ids,
1042 &timeline_context,
1043 session.conn_id(),
1044 cluster_id,
1045 )?;
1046
1047 &timedomain_bundle
1048 } else {
1049 source_bundle
1051 };
1052 let (determination, read_holds) = self.determine_timestamp(
1053 session,
1054 determine_bundle,
1055 when,
1056 cluster_id,
1057 &timeline_context,
1058 oracle_read_ts,
1059 real_time_recency_ts,
1060 )?;
1061 let read_holds = match determination.timestamp_context.timestamp() {
1063 Some(_ts) => Some(read_holds),
1064 None => {
1065 drop(read_holds);
1070 None
1071 }
1072 };
1073 (determination, read_holds)
1074 }
1075 };
1076
1077 if let Some(txn_reads) = self.txn_read_holds.get(session.conn_id()) {
1086 let allowed_id_bundle = txn_reads.id_bundle();
1090
1091 drop(read_holds);
1094
1095 let outside = source_bundle.difference(&allowed_id_bundle);
1096 if determination.timestamp_context.contains_timestamp() && !outside.is_empty() {
1098 let valid_names =
1099 allowed_id_bundle.resolve_names(self.catalog(), session.conn_id());
1100 let invalid_names = outside.resolve_names(self.catalog(), session.conn_id());
1101 return Err(AdapterError::RelationOutsideTimeDomain {
1102 relations: invalid_names,
1103 names: valid_names,
1104 });
1105 }
1106 } else if let Some(read_holds) = read_holds {
1107 self.store_transaction_read_holds(session.conn_id().clone(), read_holds);
1108 }
1109
1110 let mut transaction_determination = determination.clone();
1121 if when.is_transactional() {
1122 session.add_transaction_ops(TransactionOps::Peeks {
1123 determination: transaction_determination,
1124 cluster_id,
1125 requires_linearization,
1126 })?;
1127 } else if matches!(session.transaction(), &TransactionStatus::InTransaction(_)) {
1128 transaction_determination.timestamp_context = TimestampContext::NoTimestamp;
1130 session.add_transaction_ops(TransactionOps::Peeks {
1131 determination: transaction_determination,
1132 cluster_id,
1133 requires_linearization,
1134 })?;
1135 };
1136
1137 Ok(determination)
1138 }
1139}