1#![allow(clippy::pub_use)]
12
13use std::collections::{BTreeMap, BTreeSet};
16use std::str::FromStr;
17use std::sync::Arc;
18use std::time::Duration;
19
20use futures::FutureExt;
21use futures::future::LocalBoxFuture;
22use futures::stream::FuturesOrdered;
23use http::Uri;
24use inner::return_if_err;
25use maplit::btreemap;
26use mz_catalog::memory::objects::Cluster;
27use mz_controller_types::ReplicaId;
28use mz_expr::row::RowCollection;
29use mz_expr::{Eval, MapFilterProject, MirRelationExpr, ResultSpec, RowSetFinishing};
30use mz_ore::cast::CastFrom;
31use mz_ore::tracing::OpenTelemetryContext;
32use mz_persist_client::stats::SnapshotPartStats;
33use mz_repr::explain::{ExprHumanizerExt, TransientItem};
34use mz_repr::{CatalogItemId, Datum, Diff, GlobalId, IntoRowIterator, Row, RowArena, Timestamp};
35use mz_sql::catalog::{CatalogError, SessionCatalog};
36use mz_sql::names::ResolvedIds;
37use mz_sql::plan::{
38 self, AbortTransactionPlan, CommitTransactionPlan, CreateRolePlan, CreateSourcePlanBundle,
39 FetchPlan, HirScalarExpr, MutationKind, Params, Plan, PlanKind, RaisePlan, SideEffectingFunc,
40};
41use mz_sql::rbac;
42use mz_sql::session::metadata::SessionMetadata;
43use mz_sql::session::vars;
44use mz_sql::session::vars::SessionVars;
45use mz_sql_parser::ast::{Raw, Statement};
46use mz_storage_client::client::TableData;
47use mz_storage_client::storage_collections::StorageCollections;
48use mz_storage_types::connections::inline::IntoInlineConnection;
49use mz_storage_types::controller::StorageError;
50use mz_storage_types::stats::RelationPartStats;
51use mz_transform::dataflow::DataflowMetainfo;
52use mz_transform::notice::{OptimizerNoticeApi, OptimizerNoticeKind, RawOptimizerNotice};
53use mz_transform::{EmptyStatisticsOracle, StatisticsOracle};
54use timely::progress::Antichain;
55use tokio::sync::oneshot;
56use tracing::{Instrument, Level, Span, event, warn};
57
58use crate::ExecuteContext;
59use crate::catalog::{Catalog, CatalogState};
60use crate::command::{Command, ExecuteResponse, Response};
61use crate::coord::appends::{DeferredOp, DeferredPlan};
62use crate::coord::validity::PlanValidity;
63use crate::coord::{
64 Coordinator, DeferredPlanStatement, ExplainPlanContext, Message, PlanStatement, TargetCluster,
65 catalog_serving,
66};
67use crate::error::AdapterError;
68use crate::explain::insights::PlanInsightsContext;
69use crate::notice::AdapterNotice;
70use crate::optimize::dataflows::{EvalTime, ExprPrep, ExprPrepOneShot};
71use crate::optimize::peek;
72use crate::session::{
73 EndTransactionAction, Session, StateRevision, TransactionOps, TransactionStatus, WriteOp,
74};
75use crate::util::ClientTransmitter;
76
77mod inner;
97
98impl Coordinator {
99 pub(crate) fn sequence_plan(
103 &mut self,
104 mut ctx: ExecuteContext,
105 plan: Plan,
106 resolved_ids: ResolvedIds,
107 sql_impl_resolved_ids: ResolvedIds,
108 ) -> LocalBoxFuture<'_, ()> {
109 async move {
110 let responses = ExecuteResponse::generated_from(&PlanKind::from(&plan));
111 ctx.tx_mut().set_allowed(responses);
112
113 if self.controller.read_only() && !plan.allowed_in_read_only() {
114 ctx.retire(Err(AdapterError::ReadOnly));
115 return;
116 }
117
118 if let Some((dependencies, wait_future)) =
121 super::appends::waiting_on_startup_appends(self.catalog(), ctx.session_mut(), &plan)
122 {
123 let conn_id = ctx.session().conn_id();
124 tracing::debug!(%conn_id, "deferring plan for startup appends");
125
126 let role_metadata = ctx.session().role_metadata().clone();
127 let validity =
128 PlanValidity::new(&self.catalog, dependencies, None, None, role_metadata);
129 let deferred_plan = DeferredPlan {
130 ctx,
131 plan,
132 validity,
133 requires_locks: BTreeSet::default(),
134 resolved_ids,
135 sql_impl_resolved_ids,
136 };
137 let acquire_future = wait_future.map(|()| None);
140
141 self.defer_op(acquire_future, DeferredOp::Plan(deferred_plan));
142
143 return;
146 };
147
148 let target_cluster = match ctx.session().transaction().cluster() {
150 Some(cluster_id) => TargetCluster::Transaction(cluster_id),
152 None => {
154 let session_catalog = self.catalog.for_session(ctx.session());
155 catalog_serving::auto_run_on_catalog_server(
156 &session_catalog,
157 ctx.session(),
158 &plan,
159 )
160 }
161 };
162 let (target_cluster_id, target_cluster_name) = match self
163 .catalog()
164 .resolve_target_cluster(target_cluster, ctx.session())
165 {
166 Ok(cluster) => (Some(cluster.id), Some(cluster.name.clone())),
167 Err(_) => (None, None),
168 };
169
170 if let (Some(cluster_id), Some(cluster_name), Some(statement_id)) = (
171 target_cluster_id,
172 target_cluster_name.clone(),
173 ctx.extra().contents(),
174 ) {
175 self.set_statement_execution_cluster(statement_id, cluster_id, cluster_name);
176 }
177
178 let session_catalog = self.catalog.for_session(ctx.session());
179
180 if let Some(cluster_name) = &target_cluster_name {
181 if let Err(e) = catalog_serving::check_cluster_restrictions(
182 cluster_name,
183 &session_catalog,
184 &plan,
185 ) {
186 return ctx.retire(Err(e));
187 }
188 }
189
190 let target_conn_role = match &plan {
195 Plan::SideEffectingFunc(SideEffectingFunc::PgCancelBackend {
196 connection_id: Some(connection_id),
197 }) => self
198 .active_conns()
199 .into_iter()
200 .find(|(conn_id, _)| conn_id.unhandled() == *connection_id)
201 .map(|(_, conn_meta)| *conn_meta.authenticated_role_id()),
202 _ => None,
203 };
204
205 if let Err(e) = rbac::check_plan(
206 &session_catalog,
207 target_conn_role,
208 ctx.session(),
209 &plan,
210 target_cluster_id,
211 &resolved_ids,
212 &sql_impl_resolved_ids,
213 ) {
214 return ctx.retire(Err(e.into()));
215 }
216
217 match plan {
218 Plan::CreateSource(plan) => {
219 let (item_id, global_id) = return_if_err!(self.allocate_user_id().await, ctx);
220 let result = self
221 .sequence_create_source(
222 &mut ctx,
223 vec![CreateSourcePlanBundle {
224 item_id,
225 global_id,
226 plan,
227 resolved_ids,
228 available_source_references: None,
229 }],
230 )
231 .await;
232 ctx.retire(result);
233 }
234 Plan::CreateSources(plans) => {
235 assert!(
236 resolved_ids.is_empty(),
237 "each plan has separate resolved_ids"
238 );
239 let result = self.sequence_create_source(&mut ctx, plans).await;
240 ctx.retire(result);
241 }
242 Plan::CreateConnection(plan) => {
243 self.sequence_create_connection(ctx, plan, resolved_ids)
244 .await;
245 }
246 Plan::CreateDatabase(plan) => {
247 let result = self.sequence_create_database(ctx.session_mut(), plan).await;
248 ctx.retire(result);
249 }
250 Plan::CreateSchema(plan) => {
251 let result = self.sequence_create_schema(ctx.session_mut(), plan).await;
252 ctx.retire(result);
253 }
254 Plan::CreateRole(plan) => {
255 let result = self
256 .sequence_create_role(Some(ctx.session().conn_id()), plan)
257 .await;
258 if let Some(notice) = self.should_emit_rbac_notice(ctx.session()) {
259 ctx.session().add_notice(notice);
260 }
261 ctx.retire(result);
262 }
263 Plan::CreateCluster(plan) => {
264 let result = self.sequence_create_cluster(ctx.session(), plan).await;
265 ctx.retire(result);
266 }
267 Plan::CreateClusterReplica(plan) => {
268 let result = self
269 .sequence_create_cluster_replica(ctx.session(), plan)
270 .await;
271 ctx.retire(result);
272 }
273 Plan::CreateTable(plan) => {
274 let result = self
275 .sequence_create_table(&mut ctx, plan, resolved_ids)
276 .await;
277 ctx.retire(result);
278 }
279 Plan::CreateSecret(plan) => {
280 self.sequence_create_secret(ctx, plan).await;
281 }
282 Plan::CreateSink(plan) => {
283 self.sequence_create_sink(ctx, plan, resolved_ids).await;
284 }
285 Plan::CreateView(plan) => {
286 self.sequence_create_view(ctx, plan, resolved_ids).await;
287 }
288 Plan::CreateMaterializedView(plan) => {
289 self.sequence_create_materialized_view(ctx, plan, resolved_ids)
290 .await;
291 }
292 Plan::CreateIndex(plan) => {
293 self.sequence_create_index(ctx, plan, resolved_ids).await;
294 }
295 Plan::CreateMetricSink(plan) => {
296 self.sequence_create_metric_sink(ctx, plan, resolved_ids)
297 .await;
298 }
299 Plan::CreateType(plan) => {
300 let result = self
301 .sequence_create_type(ctx.session(), plan, resolved_ids)
302 .await;
303 ctx.retire(result);
304 }
305 Plan::CreateNetworkPolicy(plan) => {
306 let res = self
307 .sequence_create_network_policy(ctx.session(), plan)
308 .await;
309 ctx.retire(res);
310 }
311 Plan::Comment(plan) => {
312 let result = self.sequence_comment_on(ctx.session(), plan).await;
313 ctx.retire(result);
314 }
315 Plan::CopyTo(plan) => {
316 self.sequence_copy_to(ctx, plan, target_cluster).await;
317 }
318 Plan::DropObjects(plan) => {
319 let result = self.sequence_drop_objects(&mut ctx, plan).await;
320 ctx.retire(result);
321 }
322 Plan::DropOwned(plan) => {
323 let result = self.sequence_drop_owned(ctx.session_mut(), plan).await;
324 ctx.retire(result);
325 }
326 Plan::EmptyQuery => {
327 ctx.retire(Ok(ExecuteResponse::EmptyQuery));
328 }
329 Plan::ShowAllVariables => {
330 let result = self.sequence_show_all_variables(ctx.session());
331 ctx.retire(result);
332 }
333 Plan::ShowVariable(plan) => {
334 let result = self.sequence_show_variable(ctx.session(), plan);
335 ctx.retire(result);
336 }
337 Plan::InspectShard(plan) => {
338 let result = self.sequence_inspect_shard(ctx.session(), plan).await;
340 ctx.retire(result);
341 }
342 Plan::SetVariable(plan) => {
343 let result = self.sequence_set_variable(ctx.session_mut(), plan);
344 ctx.retire(result);
345 }
346 Plan::ResetVariable(plan) => {
347 let result = self.sequence_reset_variable(ctx.session_mut(), plan);
348 ctx.retire(result);
349 }
350 Plan::SetTransaction(plan) => {
351 let result = self.sequence_set_transaction(ctx.session_mut(), plan);
352 ctx.retire(result);
353 }
354 Plan::StartTransaction(plan) => {
355 if matches!(
356 ctx.session().transaction(),
357 TransactionStatus::InTransaction(_)
358 ) {
359 ctx.session()
360 .add_notice(AdapterNotice::ExistingTransactionInProgress);
361 }
362 let result = ctx.session_mut().start_transaction(
363 self.now_datetime(),
364 plan.access,
365 plan.isolation_level,
366 );
367 ctx.retire(result.map(|_| ExecuteResponse::StartedTransaction))
368 }
369 Plan::CommitTransaction(CommitTransactionPlan {
370 ref transaction_type,
371 })
372 | Plan::AbortTransaction(AbortTransactionPlan {
373 ref transaction_type,
374 }) => {
375 if ctx.session().transaction().is_ddl() {
378 if let Ok(guard) = self.serialized_ddl.try_lock_owned() {
379 let prev = self
380 .active_conns
381 .get_mut(ctx.session().conn_id())
382 .expect("connection must exist")
383 .deferred_lock
384 .replace(guard);
385 assert!(
386 prev.is_none(),
387 "connections should have at most one lock guard"
388 );
389 } else {
390 self.serialized_ddl.push_back(DeferredPlanStatement {
391 ctx,
392 ps: PlanStatement::Plan {
393 plan,
394 resolved_ids,
395 sql_impl_resolved_ids,
396 },
397 });
398 return;
399 }
400 }
401
402 let action = match &plan {
403 Plan::CommitTransaction(_) => EndTransactionAction::Commit,
404 Plan::AbortTransaction(_) => EndTransactionAction::Rollback,
405 _ => unreachable!(),
406 };
407 if ctx.session().transaction().is_implicit() && !transaction_type.is_implicit()
408 {
409 ctx.session().add_notice(
414 AdapterNotice::ExplicitTransactionControlInImplicitTransaction,
415 );
416 }
417 self.sequence_end_transaction(ctx, action).await;
418 }
419 Plan::Select(plan) => {
420 let max = Some(ctx.session().vars().max_query_result_size());
421 self.sequence_peek(ctx, plan, target_cluster, max).await;
422 }
423 Plan::Subscribe(plan) => {
424 self.sequence_subscribe(ctx, plan, target_cluster).await;
425 }
426 Plan::SideEffectingFunc(plan) => {
427 self.sequence_side_effecting_func(ctx, plan).await;
428 }
429 Plan::ShowCreate(plan) => {
430 ctx.retire(Ok(Self::send_immediate_rows(plan.row)));
431 }
432 Plan::ShowColumns(show_columns_plan) => {
433 let max = Some(ctx.session().vars().max_query_result_size());
434 self.sequence_peek(ctx, show_columns_plan.select_plan, target_cluster, max)
435 .await;
436 }
437 Plan::CopyFrom(plan) => {
438 self.sequence_copy_from(ctx, plan, target_cluster).await;
439 }
440 Plan::ExplainPlan(plan) => {
441 self.sequence_explain_plan(ctx, plan, target_cluster).await;
442 }
443 Plan::ExplainPushdown(plan) => {
444 self.sequence_explain_pushdown(ctx, plan, target_cluster)
445 .await;
446 }
447 Plan::ExplainSinkSchema(plan) => {
448 let result = self.sequence_explain_schema(plan);
449 ctx.retire(result);
450 }
451 Plan::ExplainTimestamp(plan) => {
452 self.sequence_explain_timestamp(ctx, plan, target_cluster)
453 .await;
454 }
455 Plan::Insert(plan) => {
456 self.sequence_insert(ctx, plan).await;
457 }
458 Plan::ReadThenWrite(plan) => {
459 self.sequence_read_then_write(ctx, plan).await;
460 }
461 Plan::AlterNoop(plan) => {
462 ctx.retire(Ok(ExecuteResponse::AlteredObject(plan.object_type)));
463 }
464 Plan::AlterCluster(plan) => {
465 self.sequence_alter_cluster_staged(ctx, plan).await;
466 }
467 Plan::AlterClusterRename(plan) => {
468 let result = self.sequence_alter_cluster_rename(&mut ctx, plan).await;
469 ctx.retire(result);
470 }
471 Plan::AlterClusterSwap(plan) => {
472 let result = self.sequence_alter_cluster_swap(&mut ctx, plan).await;
473 ctx.retire(result);
474 }
475 Plan::AlterClusterReplicaRename(plan) => {
476 let result = self
477 .sequence_alter_cluster_replica_rename(ctx.session(), plan)
478 .await;
479 ctx.retire(result);
480 }
481 Plan::AlterConnection(plan) => {
482 self.sequence_alter_connection(ctx, plan).await;
483 }
484 Plan::AlterSetCluster(plan) => {
485 let result = self.sequence_alter_set_cluster(ctx.session(), plan).await;
486 ctx.retire(result);
487 }
488 Plan::AlterRetainHistory(plan) => {
489 let result = self.sequence_alter_retain_history(&mut ctx, plan).await;
490 ctx.retire(result);
491 }
492 Plan::AlterSourceTimestampInterval(plan) => {
493 let result = self
494 .sequence_alter_source_timestamp_interval(&mut ctx, plan)
495 .await;
496 ctx.retire(result);
497 }
498 Plan::AlterItemRename(plan) => {
499 let result = self.sequence_alter_item_rename(&mut ctx, plan).await;
500 ctx.retire(result);
501 }
502 Plan::AlterSchemaRename(plan) => {
503 let result = self.sequence_alter_schema_rename(&mut ctx, plan).await;
504 ctx.retire(result);
505 }
506 Plan::AlterSchemaSwap(plan) => {
507 let result = self.sequence_alter_schema_swap(&mut ctx, plan).await;
508 ctx.retire(result);
509 }
510 Plan::AlterRole(plan) => {
511 let result = self.sequence_alter_role(ctx.session_mut(), plan).await;
512 ctx.retire(result);
513 }
514 Plan::AlterSecret(plan) => {
515 self.sequence_alter_secret(ctx, plan).await;
516 }
517 Plan::AlterSink(plan) => {
518 self.sequence_alter_sink_prepare(ctx, plan).await;
519 }
520 Plan::AlterSource(plan) => {
521 let result = self.sequence_alter_source(ctx.session_mut(), plan).await;
522 ctx.retire(result);
523 }
524 Plan::AlterSystemSet(plan) => {
525 let result = self.sequence_alter_system_set(ctx.session(), plan).await;
526 ctx.retire(result);
527 }
528 Plan::AlterSystemReset(plan) => {
529 let result = self.sequence_alter_system_reset(ctx.session(), plan).await;
530 ctx.retire(result);
531 }
532 Plan::AlterSystemResetAll(plan) => {
533 let result = self
534 .sequence_alter_system_reset_all(ctx.session(), plan)
535 .await;
536 ctx.retire(result);
537 }
538 Plan::AlterTableAddColumn(plan) => {
539 let result = self.sequence_alter_table(&mut ctx, plan).await;
540 ctx.retire(result);
541 }
542 Plan::AlterMaterializedViewApplyReplacement(plan) => {
543 self.sequence_alter_materialized_view_apply_replacement_prepare(ctx, plan)
544 .await;
545 }
546 Plan::AlterNetworkPolicy(plan) => {
547 let res = self
548 .sequence_alter_network_policy(ctx.session(), plan)
549 .await;
550 ctx.retire(res);
551 }
552 Plan::DiscardTemp => {
553 self.drop_temp_items(ctx.session().conn_id()).await;
554 ctx.retire(Ok(ExecuteResponse::DiscardedTemp));
555 }
556 Plan::DiscardAll => {
557 let txn = ctx.session().transaction();
560 let discardable =
561 matches!(txn, TransactionStatus::Started(_)) && !txn.contains_ops();
562 let ret = if discardable {
563 let (_, retire_notify) = self.clear_transaction(ctx.session_mut()).await;
564 ctx.delay_response_until(retire_notify);
565 self.drop_temp_items(ctx.session().conn_id()).await;
566 ctx.session_mut().reset();
572 Ok(ExecuteResponse::DiscardedAll)
573 } else {
574 Err(AdapterError::OperationProhibitsTransaction(
575 "DISCARD ALL".into(),
576 ))
577 };
578 ctx.retire(ret);
579 }
580 Plan::Declare(plan) => {
581 self.declare(ctx, plan.name, plan.stmt, plan.sql, plan.params);
582 }
583 Plan::Fetch(FetchPlan {
584 name,
585 count,
586 timeout,
587 }) => {
588 let ctx_extra = std::mem::take(ctx.extra_mut());
589 ctx.retire(Ok(ExecuteResponse::Fetch {
590 name,
591 count,
592 timeout,
593 ctx_extra,
594 }));
595 }
596 Plan::Close(plan) => {
597 if ctx.session_mut().remove_portal(&plan.name) {
598 ctx.retire(Ok(ExecuteResponse::ClosedCursor));
599 } else {
600 ctx.retire(Err(AdapterError::UnknownCursor(plan.name)));
601 }
602 }
603 Plan::Prepare(plan) => {
604 if ctx
605 .session()
606 .get_prepared_statement_unverified(&plan.name)
607 .is_some()
608 {
609 ctx.retire(Err(AdapterError::PreparedStatementExists(plan.name)));
610 } else {
611 let state_revision = StateRevision {
612 catalog_revision: self.catalog().transient_revision(),
613 session_state_revision: ctx.session().state_revision(),
614 };
615 ctx.session_mut().set_prepared_statement(
616 plan.name,
617 Some(plan.stmt),
618 plan.sql,
619 plan.desc,
620 state_revision,
621 self.now(),
622 );
623 ctx.retire(Ok(ExecuteResponse::Prepare));
624 }
625 }
626 Plan::Execute(plan) => {
627 match self.sequence_execute(ctx.session_mut(), plan) {
628 Ok(portal_name) => {
629 let (tx, _, session, extra, response_barriers) = ctx.into_parts();
630 let command = Message::Command(
637 OpenTelemetryContext::obtain(),
638 Command::Execute {
639 portal_name,
640 session,
641 tx: tx.take(),
642 outer_ctx_extra: Some(extra.defuse()),
643 },
644 );
645 if response_barriers.is_empty() {
646 self.internal_cmd_tx
647 .send(command)
648 .expect("sending to self.internal_cmd_tx cannot fail");
649 } else {
650 let internal_cmd_tx = self.internal_cmd_tx.clone();
651 mz_ore::task::spawn(
652 || "execute_after_response_barriers",
653 async move {
654 for barrier in response_barriers {
655 barrier.await;
656 }
657 let _ = internal_cmd_tx.send(command);
658 },
659 );
660 }
661 }
662 Err(err) => ctx.retire(Err(err)),
663 };
664 }
665 Plan::Deallocate(plan) => match plan.name {
666 Some(name) => {
667 if ctx.session_mut().remove_prepared_statement(&name) {
668 ctx.retire(Ok(ExecuteResponse::Deallocate { all: false }));
669 } else {
670 ctx.retire(Err(AdapterError::UnknownPreparedStatement(name)));
671 }
672 }
673 None => {
674 ctx.session_mut().remove_all_prepared_statements();
675 ctx.retire(Ok(ExecuteResponse::Deallocate { all: true }));
676 }
677 },
678 Plan::Raise(RaisePlan { severity }) => {
679 ctx.session()
680 .add_notice(AdapterNotice::UserRequested { severity });
681 ctx.retire(Ok(ExecuteResponse::Raised));
682 }
683 Plan::GrantPrivileges(plan) => {
684 let result = self
685 .sequence_grant_privileges(ctx.session_mut(), plan)
686 .await;
687 ctx.retire(result);
688 }
689 Plan::RevokePrivileges(plan) => {
690 let result = self
691 .sequence_revoke_privileges(ctx.session_mut(), plan)
692 .await;
693 ctx.retire(result);
694 }
695 Plan::AlterDefaultPrivileges(plan) => {
696 let result = self
697 .sequence_alter_default_privileges(ctx.session_mut(), plan)
698 .await;
699 ctx.retire(result);
700 }
701 Plan::GrantRole(plan) => {
702 let result = self.sequence_grant_role(ctx.session_mut(), plan).await;
703 ctx.retire(result);
704 }
705 Plan::RevokeRole(plan) => {
706 let result = self.sequence_revoke_role(ctx.session_mut(), plan).await;
707 ctx.retire(result);
708 }
709 Plan::AlterOwner(plan) => {
710 let result = self.sequence_alter_owner(ctx.session_mut(), plan).await;
711 ctx.retire(result);
712 }
713 Plan::ReassignOwned(plan) => {
714 let result = self.sequence_reassign_owned(ctx.session_mut(), plan).await;
715 ctx.retire(result);
716 }
717 Plan::ValidateConnection(plan) => {
718 let connection = plan
719 .connection
720 .into_inline_connection(self.catalog().state());
721 let current_storage_configuration = self.controller.storage.config().clone();
722 mz_ore::task::spawn(|| "coord::validate_connection", async move {
723 let res = match connection
724 .validate(plan.id, ¤t_storage_configuration)
725 .await
726 {
727 Ok(()) => Ok(ExecuteResponse::ValidatedConnection),
728 Err(err) => Err(err.into()),
729 };
730 ctx.retire(res);
731 });
732 }
733 }
734 }
735 .instrument(tracing::debug_span!("coord::sequencer::sequence_plan"))
736 .boxed_local()
737 }
738
739 #[mz_ore::instrument(level = "debug")]
740 pub(crate) async fn sequence_execute_single_statement_transaction(
741 &mut self,
742 ctx: ExecuteContext,
743 stmt: Arc<Statement<Raw>>,
744 params: Params,
745 ) {
746 let (tx, internal_cmd_tx, mut session, extra, response_barriers) = ctx.into_parts();
748 assert!(matches!(session.transaction(), TransactionStatus::Default));
749 session.start_transaction_single_stmt(self.now_datetime());
750 let conn_id = session.conn_id().unhandled();
751
752 let (sub_tx, sub_rx) = oneshot::channel();
754 let sub_ct = ClientTransmitter::new(sub_tx, self.internal_cmd_tx.clone());
755 let sub_ctx = ExecuteContext::from_parts_with_response_barriers(
756 sub_ct,
757 internal_cmd_tx,
758 session,
759 extra,
760 response_barriers,
761 );
762 self.handle_execute_inner(stmt, params, sub_ctx).await;
763
764 let internal_cmd_tx = self.internal_cmd_tx.clone();
767 mz_ore::task::spawn(
768 || format!("execute_single_statement:{conn_id}"),
769 async move {
770 let Ok(Response {
771 result,
772 session,
773 otel_ctx,
774 }) = sub_rx.await
775 else {
776 return;
778 };
779 otel_ctx.attach_as_parent();
780 let (sub_tx, sub_rx) = oneshot::channel();
781 let _ = internal_cmd_tx.send(Message::Command(
782 otel_ctx,
783 Command::Commit {
784 action: EndTransactionAction::Commit,
785 session,
786 tx: sub_tx,
787 },
788 ));
789 let Ok(commit_response) = sub_rx.await else {
790 return;
792 };
793 assert!(matches!(
794 commit_response.session.transaction(),
795 TransactionStatus::Default
796 ));
797 let result = match (result, commit_response.result) {
803 (Ok(_), commit) => commit,
804 (Err(result), _) => Err(result),
805 };
806 tx.send(result, commit_response.session);
809 }
810 .instrument(Span::current()),
811 );
812 }
813
814 #[mz_ore::instrument(level = "debug")]
818 pub(crate) async fn sequence_create_role_for_startup(
819 &mut self,
820 plan: CreateRolePlan,
821 ) -> Result<ExecuteResponse, AdapterError> {
822 self.sequence_create_role(None, plan).await
829 }
830
831 pub(crate) fn allocate_transient_id(&self) -> (CatalogItemId, GlobalId) {
832 self.transient_id_gen.allocate_id()
833 }
834
835 fn should_emit_rbac_notice(&self, session: &Session) -> Option<AdapterNotice> {
836 if !rbac::is_rbac_enabled_for_session(self.catalog.system_config(), session) {
837 Some(AdapterNotice::RbacUserDisabled)
838 } else {
839 None
840 }
841 }
842
843 pub(crate) fn insert_constant(
850 catalog: &Catalog,
851 session: &mut Session,
852 target_id: CatalogItemId,
853 constants: MirRelationExpr,
854 ) -> Result<ExecuteResponse, AdapterError> {
855 let desc = match catalog.try_get_entry(&target_id) {
857 Some(table) => {
858 table.relation_desc_latest().expect("table has desc")
860 }
861 None => {
862 return Err(AdapterError::Catalog(mz_catalog::memory::error::Error {
863 kind: mz_catalog::memory::error::ErrorKind::Sql(CatalogError::UnknownItem(
864 target_id.to_string(),
865 )),
866 }));
867 }
868 };
869
870 match constants.as_const() {
871 Some((rows, ..)) => {
872 let rows = rows.clone()?;
873 for (row, _) in &rows {
874 for (i, datum) in row.iter().enumerate() {
875 desc.constraints_met(i, &datum)?;
876 }
877 }
878 let diffs_plan = plan::SendDiffsPlan {
879 id: target_id,
880 updates: rows,
881 kind: MutationKind::Insert,
882 returning: Vec::new(),
883 max_result_size: catalog.system_config().max_result_size(),
884 };
885 Self::send_diffs(session, diffs_plan)
886 }
887 None => panic!(
888 "tried using sequence_insert_constant on non-constant MirRelationExpr\n{}",
889 constants.pretty(),
890 ),
891 }
892 }
893
894 #[mz_ore::instrument(level = "debug")]
895 pub(crate) fn send_diffs(
896 session: &mut Session,
897 mut plan: plan::SendDiffsPlan,
898 ) -> Result<ExecuteResponse, AdapterError> {
899 let affected_rows = {
900 let mut affected_rows = Diff::from(0);
901 let mut all_positive_diffs = true;
902 for (_, diff) in plan.updates.iter() {
905 if diff.is_negative() {
906 all_positive_diffs = false;
907 break;
908 }
909
910 affected_rows += diff;
911 }
912
913 if !all_positive_diffs {
914 differential_dataflow::consolidation::consolidate(&mut plan.updates);
926
927 affected_rows = Diff::ZERO;
928 for (_, diff) in plan.updates.iter() {
933 affected_rows += diff.abs();
934 }
935 }
936
937 usize::try_from(affected_rows.into_inner()).expect("positive Diff must fit")
938 };
939 event!(
940 Level::TRACE,
941 affected_rows,
942 id = format!("{:?}", plan.id),
943 kind = format!("{:?}", plan.kind),
944 updates = plan.updates.len(),
945 returning = plan.returning.len(),
946 );
947
948 session.add_transaction_ops(TransactionOps::Writes(vec![WriteOp {
949 id: plan.id,
950 rows: TableData::Rows(plan.updates),
951 }]))?;
952 if !plan.returning.is_empty() {
953 let finishing = RowSetFinishing {
954 order_by: Vec::new(),
955 limit: None,
956 offset: 0,
957 project: (0..plan.returning[0].0.iter().count()).collect(),
958 };
959 let max_returned_query_size = session.vars().max_query_result_size();
960 let duration_histogram = session.metrics().row_set_finishing_seconds();
961
962 return match finishing.finish(
963 RowCollection::new(plan.returning, &finishing.order_by),
964 plan.max_result_size,
965 Some(max_returned_query_size),
966 duration_histogram,
967 ) {
968 Ok((rows, _size_bytes)) => Ok(Self::send_immediate_rows(rows)),
969 Err(e) => Err(AdapterError::ResultSize(e)),
970 };
971 }
972 Ok(match plan.kind {
973 MutationKind::Delete => ExecuteResponse::Deleted(affected_rows),
974 MutationKind::Insert => ExecuteResponse::Inserted(affected_rows),
975 MutationKind::Update => ExecuteResponse::Updated(affected_rows / 2),
976 })
977 }
978}
979
980pub(crate) fn check_log_reads(
989 catalog: &Catalog,
990 cluster: &Cluster,
991 source_ids: &BTreeSet<GlobalId>,
992 target_replica: &mut Option<ReplicaId>,
993 vars: &SessionVars,
994) -> Result<impl IntoIterator<Item = AdapterNotice>, AdapterError>
995where
996{
997 let log_names = source_ids
998 .iter()
999 .map(|gid| catalog.resolve_item_id(gid))
1000 .flat_map(|item_id| catalog.introspection_dependencies(item_id))
1001 .map(|item_id| catalog.get_entry(&item_id).name().item.clone())
1002 .collect::<Vec<_>>();
1003
1004 if log_names.is_empty() {
1005 return Ok(None);
1006 }
1007
1008 let num_replicas = cluster.replicas().count();
1012 if target_replica.is_none() {
1013 if num_replicas == 1 {
1014 *target_replica = cluster.replicas().map(|r| r.replica_id).next();
1015 } else {
1016 return Err(AdapterError::UntargetedLogRead { log_names });
1017 }
1018 }
1019
1020 let replica_id = target_replica.expect("set to `Some` above");
1023 let replica = &cluster.replica(replica_id).expect("Replica must exist");
1024 if !replica.config.compute.logging.enabled() {
1025 return Err(AdapterError::IntrospectionDisabled { log_names });
1026 }
1027
1028 Ok(vars
1029 .emit_introspection_query_notice()
1030 .then_some(AdapterNotice::PerReplicaLogRead { log_names }))
1031}
1032
1033pub(crate) fn emit_optimizer_notices(
1035 catalog: &Catalog,
1036 session: &Session,
1037 notices: &[RawOptimizerNotice],
1038) {
1039 if notices.is_empty() {
1041 return;
1042 }
1043 let humanizer = catalog.for_session(session);
1044 let system_vars = catalog.system_config();
1045 for notice in notices {
1046 let kind = OptimizerNoticeKind::from(notice);
1047 let notice_enabled = match kind {
1048 OptimizerNoticeKind::EqualsNull => system_vars.enable_notices_for_equals_null(),
1049 OptimizerNoticeKind::IndexAlreadyExists => {
1050 system_vars.enable_notices_for_index_already_exists()
1051 }
1052 OptimizerNoticeKind::IndexTooWideForLiteralConstraints => {
1053 system_vars.enable_notices_for_index_too_wide_for_literal_constraints()
1054 }
1055 OptimizerNoticeKind::IndexKeyEmpty => system_vars.enable_notices_for_index_empty_key(),
1056 };
1057 if notice_enabled {
1058 session.add_notice(AdapterNotice::OptimizerNotice {
1062 notice: notice.message(&humanizer, false).to_string(),
1063 hint: notice.hint(&humanizer, false).to_string(),
1064 });
1065 }
1066 session
1067 .metrics()
1068 .optimization_notices(&[kind.metric_label()])
1069 .inc_by(1);
1070 }
1071}
1072
1073pub fn eval_copy_to_uri(
1078 to: HirScalarExpr,
1079 session: &Session,
1080 catalog_state: &CatalogState,
1081) -> Result<Uri, AdapterError> {
1082 let style = ExprPrepOneShot {
1083 logical_time: EvalTime::NotAvailable,
1084 session,
1085 catalog_state,
1086 };
1087 let mut to = to.lower_uncorrelated(catalog_state.system_config())?;
1088 style.prep_scalar_expr(&mut to)?;
1089 let temp_storage = RowArena::new();
1090 let evaled = to.eval(&[], &temp_storage)?;
1091 if evaled == Datum::Null {
1092 coord_bail!("COPY TO target value can not be null");
1093 }
1094 let to_url = match Uri::from_str(evaled.unwrap_str()) {
1095 Ok(url) => {
1096 if url.scheme_str() != Some("s3") && url.scheme_str() != Some("gs") {
1097 coord_bail!("only 's3://...' and 'gs://...' urls are supported as COPY TO target");
1098 }
1099 url
1100 }
1101 Err(e) => coord_bail!("could not parse COPY TO target url: {}", e),
1102 };
1103 Ok(to_url)
1104}
1105
1106pub(crate) async fn explain_pushdown_future_inner<
1113 I: IntoIterator<Item = (GlobalId, MapFilterProject)>,
1114>(
1115 session: &Session,
1116 catalog: &Catalog,
1117 storage_collections: &Arc<dyn StorageCollections + Send + Sync>,
1118 as_of: Antichain<Timestamp>,
1119 mz_now: ResultSpec<'static>,
1120 imports: I,
1121) -> impl Future<Output = Result<ExecuteResponse, AdapterError>> + use<I> {
1122 let mut explain_timeout = *session.vars().statement_timeout();
1123 if explain_timeout == Duration::ZERO {
1125 explain_timeout = Duration::MAX;
1126 }
1127 let mut futures = FuturesOrdered::new();
1128 for (id, mfp) in imports {
1129 let catalog_entry = catalog.get_entry_by_global_id(&id);
1130 let full_name = catalog
1131 .for_session(session)
1132 .resolve_full_name(&catalog_entry.name);
1133 let name = format!("{}", full_name);
1134 let relation_desc = catalog_entry
1135 .relation_desc()
1136 .expect("source should have a proper desc")
1137 .into_owned();
1138 let stats_future = storage_collections
1139 .snapshot_parts_stats(id, as_of.clone())
1140 .await;
1141
1142 let mz_now = mz_now.clone();
1143 futures.push_back(async move {
1148 let snapshot_stats = match stats_future.await {
1149 Ok(stats) => stats,
1150 Err(e) => return Err(e),
1151 };
1152 let mut total_bytes = 0;
1153 let mut total_parts = 0;
1154 let mut selected_bytes = 0;
1155 let mut selected_parts = 0;
1156 for SnapshotPartStats {
1157 encoded_size_bytes: bytes,
1158 stats,
1159 } in &snapshot_stats.parts
1160 {
1161 let bytes = u64::cast_from(*bytes);
1162 total_bytes += bytes;
1163 total_parts += 1u64;
1164 let selected = match stats.as_ref().and_then(|x| x.try_decode().ok()) {
1165 None => true,
1169 Some(stats) => {
1170 let stats = RelationPartStats::new(
1171 name.as_str(),
1172 &snapshot_stats.metrics.pushdown.part_stats,
1173 &relation_desc,
1174 &stats,
1175 );
1176 stats.may_match_mfp(mz_now.clone(), &mfp)
1177 }
1178 };
1179
1180 if selected {
1181 selected_bytes += bytes;
1182 selected_parts += 1u64;
1183 }
1184 }
1185 Ok(Row::pack_slice(&[
1186 name.as_str().into(),
1187 total_bytes.into(),
1188 selected_bytes.into(),
1189 total_parts.into(),
1190 selected_parts.into(),
1191 ]))
1192 });
1193 }
1194
1195 let fut = async move {
1196 match tokio::time::timeout(
1197 explain_timeout,
1198 futures::TryStreamExt::try_collect::<Vec<_>>(futures),
1199 )
1200 .await
1201 {
1202 Ok(Ok(rows)) => Ok(ExecuteResponse::SendingRowsImmediate {
1203 rows: Box::new(rows.into_row_iter()),
1204 }),
1205 Ok(Err(err)) => Err(err.into()),
1206 Err(_) => Err(AdapterError::StatementTimeout),
1207 }
1208 };
1209 fut
1210}
1211
1212pub(crate) async fn explain_plan_inner(
1215 session: &Session,
1216 catalog: &Catalog,
1217 df_meta: DataflowMetainfo,
1218 explain_ctx: ExplainPlanContext,
1219 optimizer: peek::Optimizer,
1220 insights_ctx: Option<Box<PlanInsightsContext>>,
1221) -> Result<Vec<Row>, AdapterError> {
1222 let ExplainPlanContext {
1223 config,
1224 format,
1225 stage,
1226 desc,
1227 optimizer_trace,
1228 ..
1229 } = explain_ctx;
1230
1231 let desc = desc.expect("RelationDesc for SelectPlan in EXPLAIN mode");
1232
1233 let session_catalog = catalog.for_session(session);
1234 let expr_humanizer = {
1235 let transient_items = btreemap! {
1236 optimizer.select_id() => TransientItem::new(
1237 Some(vec![GlobalId::Explain.to_string()]),
1238 Some(desc.iter_names().map(|c| c.to_string()).collect()),
1239 )
1240 };
1241 ExprHumanizerExt::new(transient_items, &session_catalog)
1242 };
1243
1244 let finishing = if optimizer.finishing().is_trivial(desc.arity()) {
1245 None
1246 } else {
1247 Some(optimizer.finishing().clone())
1248 };
1249
1250 let target_cluster = catalog.get_cluster(optimizer.cluster_id());
1251 let features = optimizer.config().features.clone();
1252
1253 let rows = optimizer_trace
1254 .into_rows(
1255 format,
1256 &config,
1257 &features,
1258 &expr_humanizer,
1259 finishing,
1260 Some(target_cluster),
1261 df_meta,
1262 stage,
1263 plan::ExplaineeStatementKind::Select,
1264 insights_ctx,
1265 )
1266 .await?;
1267
1268 Ok(rows)
1269}
1270
1271pub(crate) async fn statistics_oracle(
1276 session: &Session,
1277 source_ids: &BTreeSet<GlobalId>,
1278 query_as_of: &Antichain<Timestamp>,
1279 is_oneshot: bool,
1280 system_config: &vars::SystemVars,
1281 storage_collections: &dyn StorageCollections,
1282) -> Result<Box<dyn StatisticsOracle>, AdapterError> {
1283 if !session.vars().enable_session_cardinality_estimates() {
1284 let stats: Box<dyn StatisticsOracle> = Box::new(EmptyStatisticsOracle);
1285 return Ok(stats);
1286 }
1287
1288 let timeout = if is_oneshot {
1289 system_config.optimizer_oneshot_stats_timeout()
1291 } else {
1292 system_config.optimizer_stats_timeout()
1293 };
1294
1295 let cached_stats = mz_ore::future::timeout(
1296 timeout,
1297 CachedStatisticsOracle::new(source_ids, query_as_of, storage_collections),
1298 )
1299 .await;
1300
1301 match cached_stats {
1302 Ok(stats) => Ok(Box::new(stats)),
1303 Err(mz_ore::future::TimeoutError::DeadlineElapsed) => {
1304 warn!(
1305 is_oneshot = is_oneshot,
1306 "optimizer statistics collection timed out after {}ms",
1307 timeout.as_millis()
1308 );
1309
1310 Ok(Box::new(EmptyStatisticsOracle))
1311 }
1312 Err(mz_ore::future::TimeoutError::Inner(e)) => Err(AdapterError::Storage(e)),
1313 }
1314}
1315
1316#[derive(Debug)]
1317struct CachedStatisticsOracle {
1318 cache: BTreeMap<GlobalId, usize>,
1319}
1320
1321impl CachedStatisticsOracle {
1322 pub async fn new(
1323 ids: &BTreeSet<GlobalId>,
1324 as_of: &Antichain<Timestamp>,
1325 storage_collections: &dyn StorageCollections,
1326 ) -> Result<Self, StorageError> {
1327 let mut cache = BTreeMap::new();
1328
1329 for id in ids {
1330 let stats = storage_collections.snapshot_stats(*id, as_of.clone()).await;
1331
1332 match stats {
1333 Ok(stats) => {
1334 cache.insert(*id, stats.num_updates);
1335 }
1336 Err(StorageError::IdentifierMissing(id)) => {
1337 ::tracing::debug!("no statistics for {id}")
1338 }
1339 Err(e) => return Err(e),
1340 }
1341 }
1342
1343 Ok(Self { cache })
1344 }
1345}
1346
1347impl StatisticsOracle for CachedStatisticsOracle {
1348 fn cardinality_estimate(&self, id: GlobalId) -> Option<usize> {
1349 self.cache.get(&id).map(|estimate| *estimate)
1350 }
1351
1352 fn as_map(&self) -> BTreeMap<GlobalId, usize> {
1353 self.cache.clone()
1354 }
1355}