1#![warn(missing_docs)]
13
14use std::collections::btree_map::Entry;
15use std::collections::{BTreeMap, BTreeSet};
16use std::future::Future;
17use std::mem;
18use std::net::IpAddr;
19use std::pin::Pin;
20use std::sync::Arc;
21
22use chrono::{DateTime, Utc};
23use derivative::Derivative;
24use itertools::Itertools;
25use mz_adapter_types::connection::ConnectionId;
26use mz_auth::AuthenticatorKind;
27use mz_build_info::{BuildInfo, DUMMY_BUILD_INFO};
28use mz_controller_types::ClusterId;
29use mz_ore::metrics::{MetricsFutureExt, MetricsRegistry};
30use mz_ore::now::{EpochMillis, NowFn};
31use mz_pgwire_common::Format;
32use mz_repr::role_id::RoleId;
33use mz_repr::user::{ExternalUserMetadata, InternalUserMetadata};
34use mz_repr::{CatalogItemId, Datum, Row, RowIterator, SqlScalarType, Timestamp};
35use mz_sql::ast::{AstInfo, Raw, Statement, TransactionAccessMode};
36use mz_sql::plan::{Params, PlanContext, QueryWhen, StatementDesc};
37use mz_sql::session::metadata::SessionMetadata;
38use mz_sql::session::user::{
39 INTERNAL_USER_NAME_TO_DEFAULT_CLUSTER, RoleMetadata, SYSTEM_USER, User,
40};
41use mz_sql::session::vars::IsolationLevel;
42pub use mz_sql::session::vars::{
43 DEFAULT_DATABASE_NAME, EndTransactionAction, SERVER_MAJOR_VERSION, SERVER_MINOR_VERSION,
44 SERVER_PATCH_VERSION, SessionVars, Var,
45};
46use mz_sql_parser::ast::TransactionIsolationLevel;
47use mz_storage_client::client::TableData;
48use mz_storage_types::sources::Timeline;
49use qcell::{QCell, QCellOwner};
50use timely::progress::Timestamp as _;
51use tokio::sync::mpsc::{self, UnboundedSender};
52use tokio::sync::watch;
53use uuid::Uuid;
54
55use crate::catalog::CatalogState;
56use crate::client::RecordFirstRowStream;
57use crate::coord::appends::BuiltinTableAppendNotify;
58use crate::coord::in_memory_oracle::InMemoryTimestampOracle;
59use crate::coord::peek::PeekResponseUnary;
60use crate::coord::timestamp_selection::{TimestampContext, TimestampDetermination};
61use crate::coord::{Coordinator, ExplainContext};
62use crate::error::AdapterError;
63use crate::metrics::{Metrics, SessionMetrics};
64use crate::statement_logging::PreparedStatementLoggingInfo;
65use crate::{AdapterNotice, ExecuteContext};
66use mz_catalog::durable::Snapshot;
67
68const DUMMY_CONNECTION_ID: ConnectionId = ConnectionId::Static(0);
69
70#[derive(Derivative)]
72#[derivative(Debug)]
73pub struct Session {
74 conn_id: ConnectionId,
75 uuid: Uuid,
78 prepared_statements: BTreeMap<String, PreparedStatement>,
79 portals: BTreeMap<String, Portal>,
80 transaction: TransactionStatus,
81 pcx: Option<PlanContext>,
82 metrics: SessionMetrics,
83 #[derivative(Debug = "ignore")]
84 builtin_updates: Option<BuiltinTableAppendNotify>,
85
86 role_metadata: Option<RoleMetadata>,
99 client_ip: Option<IpAddr>,
100 vars: SessionVars,
101 notices_tx: mpsc::UnboundedSender<AdapterNotice>,
102 notices_rx: mpsc::UnboundedReceiver<AdapterNotice>,
103 next_transaction_id: TransactionId,
104 secret_key: u32,
105 external_metadata_rx: Option<watch::Receiver<ExternalUserMetadata>>,
106 #[derivative(Debug = "ignore")]
118 qcell_owner: QCellOwner,
119 session_oracles: BTreeMap<Timeline, InMemoryTimestampOracle>,
120 state_revision: u64,
126}
127
128impl SessionMetadata for Session {
129 fn conn_id(&self) -> &ConnectionId {
130 &self.conn_id
131 }
132
133 fn client_ip(&self) -> Option<&IpAddr> {
134 self.client_ip.as_ref()
135 }
136
137 fn pcx(&self) -> &PlanContext {
138 &self
139 .transaction()
140 .inner()
141 .expect("no active transaction")
142 .pcx
143 }
144
145 fn role_metadata(&self) -> &RoleMetadata {
146 self.role_metadata
147 .as_ref()
148 .expect("role_metadata invariant violated")
149 }
150
151 fn vars(&self) -> &SessionVars {
152 &self.vars
153 }
154}
155
156#[derive(Debug)]
159pub struct SessionMeta {
160 conn_id: ConnectionId,
161 client_ip: Option<IpAddr>,
162 pcx: PlanContext,
163 role_metadata: RoleMetadata,
164 vars: SessionVars,
165}
166
167impl SessionMetadata for SessionMeta {
168 fn vars(&self) -> &SessionVars {
169 &self.vars
170 }
171
172 fn conn_id(&self) -> &ConnectionId {
173 &self.conn_id
174 }
175
176 fn client_ip(&self) -> Option<&IpAddr> {
177 self.client_ip.as_ref()
178 }
179
180 fn pcx(&self) -> &PlanContext {
181 &self.pcx
182 }
183
184 fn role_metadata(&self) -> &RoleMetadata {
185 &self.role_metadata
186 }
187}
188
189#[derive(Debug, Clone)]
191pub struct SessionConfig {
192 pub conn_id: ConnectionId,
196 pub uuid: Uuid,
201 pub client_ip: Option<IpAddr>,
203 pub user: String,
205 pub external_metadata_rx: Option<watch::Receiver<ExternalUserMetadata>>,
208 pub helm_chart_version: Option<String>,
210 pub authenticator_kind: AuthenticatorKind,
212 pub groups: Option<Vec<String>>,
214}
215
216impl Session {
217 pub(crate) fn new(
219 build_info: &'static BuildInfo,
220 config: SessionConfig,
221 metrics: SessionMetrics,
222 ) -> Session {
223 assert_ne!(config.conn_id, DUMMY_CONNECTION_ID);
224 Self::new_internal(build_info, config, metrics)
225 }
226
227 pub fn meta(&self) -> SessionMeta {
230 SessionMeta {
231 conn_id: self.conn_id().clone(),
232 client_ip: self.client_ip().copied(),
233 pcx: self.pcx().clone(),
234 role_metadata: self.role_metadata().clone(),
235 vars: self.vars.clone(),
236 }
237
238 }
240
241 pub(crate) fn mint_logging<A: AstInfo>(
251 &self,
252 raw_sql: String,
253 stmt: Option<&Statement<A>>,
254 now: EpochMillis,
255 ) -> Arc<QCell<PreparedStatementLoggingInfo>> {
256 Arc::new(QCell::new(
257 &self.qcell_owner,
258 PreparedStatementLoggingInfo::still_to_log(
259 raw_sql,
260 stmt,
261 now,
262 "".to_string(),
263 self.uuid,
264 false,
265 ),
266 ))
267 }
268
269 pub(crate) fn qcell_ro<'a, T2: 'a>(&'a self, cell: &'a Arc<QCell<T2>>) -> &'a T2 {
270 self.qcell_owner.ro(&*cell)
271 }
272
273 pub(crate) fn qcell_rw<'a, T2: 'a>(&'a mut self, cell: &'a Arc<QCell<T2>>) -> &'a mut T2 {
274 self.qcell_owner.rw(&*cell)
275 }
276
277 pub fn uuid(&self) -> Uuid {
280 self.uuid
281 }
282
283 pub fn dummy() -> Session {
288 let registry = MetricsRegistry::new();
289 let metrics = Metrics::register_into(®istry);
290 let metrics = metrics.session_metrics();
291 let mut dummy = Self::new_internal(
292 &DUMMY_BUILD_INFO,
293 SessionConfig {
294 conn_id: DUMMY_CONNECTION_ID,
295 uuid: Uuid::new_v4(),
296 user: SYSTEM_USER.name.clone(),
297 client_ip: None,
298 external_metadata_rx: None,
299 helm_chart_version: None,
300 authenticator_kind: AuthenticatorKind::None,
301 groups: None,
302 },
303 metrics,
304 );
305 dummy.initialize_role_metadata(RoleId::User(0));
306 dummy
307 }
308
309 fn new_internal(
310 build_info: &'static BuildInfo,
311 SessionConfig {
312 conn_id,
313 uuid,
314 user,
315 client_ip,
316 mut external_metadata_rx,
317 helm_chart_version,
318 authenticator_kind,
319 groups,
320 }: SessionConfig,
321 metrics: SessionMetrics,
322 ) -> Session {
323 let (notices_tx, notices_rx) = mpsc::unbounded_channel();
324 let default_cluster = INTERNAL_USER_NAME_TO_DEFAULT_CLUSTER.get(&user);
325 let user = User {
326 name: user,
327 internal_metadata: None,
328 external_metadata: external_metadata_rx
329 .as_mut()
330 .map(|rx| rx.borrow_and_update().clone()),
331 authenticator_kind: Some(authenticator_kind),
332 groups,
333 };
334 let mut vars = SessionVars::new_unchecked(build_info, user, helm_chart_version);
335 if let Some(default_cluster) = default_cluster {
336 vars.set_cluster(default_cluster.clone());
337 }
338 Session {
339 conn_id,
340 uuid,
341 transaction: TransactionStatus::Default,
342 pcx: None,
343 metrics,
344 builtin_updates: None,
345 prepared_statements: BTreeMap::new(),
346 portals: BTreeMap::new(),
347 role_metadata: None,
348 client_ip,
349 vars,
350 notices_tx,
351 notices_rx,
352 next_transaction_id: 0,
353 secret_key: rand::random(),
354 external_metadata_rx,
355 qcell_owner: QCellOwner::new(),
356 session_oracles: BTreeMap::new(),
357 state_revision: 0,
358 }
359 }
360
361 pub fn secret_key(&self) -> u32 {
363 self.secret_key
364 }
365
366 fn new_pcx(&self, mut wall_time: DateTime<Utc>) -> PlanContext {
367 if let Some(mock_time) = self.vars().unsafe_new_transaction_wall_time() {
368 wall_time = *mock_time;
369 }
370 PlanContext::new(wall_time)
371 }
372
373 pub fn start_transaction(
376 &mut self,
377 wall_time: DateTime<Utc>,
378 access: Option<TransactionAccessMode>,
379 isolation_level: Option<TransactionIsolationLevel>,
380 ) -> Result<(), AdapterError> {
381 if let Some(txn) = self.transaction.inner() {
383 let read_write_prohibited = match txn.ops {
387 TransactionOps::Peeks { .. } | TransactionOps::Subscribe => {
388 txn.access == Some(TransactionAccessMode::ReadOnly)
389 }
390 TransactionOps::None
391 | TransactionOps::Writes(_)
392 | TransactionOps::SingleStatement { .. }
393 | TransactionOps::DDL { .. } => false,
394 };
395
396 if read_write_prohibited && access == Some(TransactionAccessMode::ReadWrite) {
397 return Err(AdapterError::ReadWriteUnavailable);
398 }
399 }
400
401 match std::mem::take(&mut self.transaction) {
402 TransactionStatus::Default => {
403 let id = self.next_transaction_id;
404 self.next_transaction_id = self.next_transaction_id.wrapping_add(1);
405 self.transaction = TransactionStatus::InTransaction(Transaction {
406 pcx: self.new_pcx(wall_time),
407 ops: TransactionOps::None,
408 write_lock_guards: None,
409 access,
410 id,
411 });
412 }
413 TransactionStatus::Started(mut txn)
414 | TransactionStatus::InTransactionImplicit(mut txn)
415 | TransactionStatus::InTransaction(mut txn) => {
416 if access.is_some() {
417 txn.access = access;
418 }
419 self.transaction = TransactionStatus::InTransaction(txn);
420 }
421 TransactionStatus::Failed(_) => unreachable!(),
422 };
423
424 if let Some(isolation_level) = isolation_level {
425 self.vars
426 .set_local_transaction_isolation(isolation_level.into());
427 }
428
429 Ok(())
430 }
431
432 pub fn start_transaction_implicit(&mut self, wall_time: DateTime<Utc>, stmts: usize) {
435 if let TransactionStatus::Default = self.transaction {
436 let id = self.next_transaction_id;
437 self.next_transaction_id = self.next_transaction_id.wrapping_add(1);
438 let txn = Transaction {
439 pcx: self.new_pcx(wall_time),
440 ops: TransactionOps::None,
441 write_lock_guards: None,
442 access: None,
443 id,
444 };
445 match stmts {
446 1 => self.transaction = TransactionStatus::Started(txn),
447 n if n > 1 => self.transaction = TransactionStatus::InTransactionImplicit(txn),
448 _ => {}
449 }
450 }
451 }
452
453 pub fn start_transaction_single_stmt(&mut self, wall_time: DateTime<Utc>) {
455 self.start_transaction_implicit(wall_time, 1);
456 }
457
458 #[must_use]
468 pub fn clear_transaction(&mut self) -> TransactionStatus {
469 self.portals.clear();
470 self.pcx = None;
471 self.state_revision += 1;
472 mem::take(&mut self.transaction)
473 }
474
475 pub fn fail_transaction(mut self) -> Self {
477 match self.transaction {
478 TransactionStatus::Default => unreachable!(),
479 TransactionStatus::Started(txn)
480 | TransactionStatus::InTransactionImplicit(txn)
481 | TransactionStatus::InTransaction(txn) => {
482 self.transaction = TransactionStatus::Failed(txn);
483 }
484 TransactionStatus::Failed(_) => {}
485 };
486 self
487 }
488
489 pub fn transaction(&self) -> &TransactionStatus {
491 &self.transaction
492 }
493
494 pub fn transaction_mut(&mut self) -> &mut TransactionStatus {
496 &mut self.transaction
497 }
498
499 pub fn transaction_code(&self) -> TransactionCode {
501 self.transaction().into()
502 }
503
504 pub fn add_transaction_ops(&mut self, add_ops: TransactionOps) -> Result<(), AdapterError> {
508 self.transaction.add_ops(add_ops)
509 }
510
511 pub fn retain_notice_transmitter(&self) -> UnboundedSender<AdapterNotice> {
513 self.notices_tx.clone()
514 }
515
516 pub fn add_notice(&self, notice: AdapterNotice) {
518 self.add_notices([notice])
519 }
520
521 pub fn add_notices(&self, notices: impl IntoIterator<Item = AdapterNotice>) {
523 for notice in notices {
524 let _ = self.notices_tx.send(notice);
525 }
526 }
527
528 pub async fn recv_notice(&mut self) -> AdapterNotice {
532 loop {
534 let notice = self
535 .notices_rx
536 .recv()
537 .await
538 .expect("Session also holds a sender, so recv won't ever return None");
539 match self.notice_filter(notice) {
540 Some(notice) => return notice,
541 None => continue,
542 }
543 }
544 }
545
546 pub fn drain_notices(&mut self) -> Vec<AdapterNotice> {
548 let mut notices = Vec::new();
549 while let Ok(notice) = self.notices_rx.try_recv() {
550 if let Some(notice) = self.notice_filter(notice) {
551 notices.push(notice);
552 }
553 }
554 notices
555 }
556
557 fn notice_filter(&self, notice: AdapterNotice) -> Option<AdapterNotice> {
559 let minimum_client_severity = self.vars.client_min_messages();
561 let sev = notice.severity();
562 if !minimum_client_severity.should_output_to_client(&sev) {
563 return None;
564 }
565 if let AdapterNotice::ClusterReplicaStatusChanged { cluster, .. } = ¬ice {
567 if cluster != self.vars.cluster() {
568 return None;
569 }
570 }
571 Some(notice)
572 }
573
574 pub fn clear_transaction_ops(&mut self) {
577 if let Some(txn) = self.transaction.inner_mut() {
578 txn.ops = TransactionOps::None;
579 }
580 }
581
582 pub fn take_transaction_timestamp_context(&mut self) -> Option<TimestampContext> {
587 if let Some(Transaction { ops, .. }) = self.transaction.inner_mut() {
588 if let TransactionOps::Peeks { .. } = ops {
589 let ops = std::mem::take(ops);
590 Some(
591 ops.timestamp_determination()
592 .expect("checked above")
593 .timestamp_context,
594 )
595 } else {
596 None
597 }
598 } else {
599 None
600 }
601 }
602
603 pub fn get_transaction_timestamp_determination(&self) -> Option<TimestampDetermination> {
608 match self.transaction.inner() {
609 Some(Transaction {
610 pcx: _,
611 ops: TransactionOps::Peeks { determination, .. },
612 write_lock_guards: _,
613 access: _,
614 id: _,
615 }) => Some(determination.clone()),
616 _ => None,
617 }
618 }
619
620 pub fn contains_read_timestamp(&self) -> bool {
622 matches!(
623 self.transaction.inner(),
624 Some(Transaction {
625 pcx: _,
626 ops: TransactionOps::Peeks {
627 determination: TimestampDetermination {
628 timestamp_context: TimestampContext::TimelineTimestamp { .. },
629 ..
630 },
631 ..
632 },
633 write_lock_guards: _,
634 access: _,
635 id: _,
636 })
637 )
638 }
639
640 pub fn set_prepared_statement(
642 &mut self,
643 name: String,
644 stmt: Option<Statement<Raw>>,
645 raw_sql: String,
646 desc: StatementDesc,
647 state_revision: StateRevision,
648 now: EpochMillis,
649 ) {
650 let logging = PreparedStatementLoggingInfo::still_to_log(
651 raw_sql,
652 stmt.as_ref(),
653 now,
654 name.clone(),
655 self.uuid,
656 false,
657 );
658 let statement = PreparedStatement {
659 stmt,
660 desc,
661 state_revision,
662 logging: Arc::new(QCell::new(&self.qcell_owner, logging)),
663 };
664 self.prepared_statements.insert(name, statement);
665 }
666
667 pub fn remove_prepared_statement(&mut self, name: &str) -> bool {
671 self.prepared_statements.remove(name).is_some()
672 }
673
674 pub fn remove_all_prepared_statements(&mut self) {
676 self.prepared_statements.clear();
677 }
678
679 pub fn get_prepared_statement_unverified(&self, name: &str) -> Option<&PreparedStatement> {
684 self.prepared_statements.get(name)
685 }
686
687 pub fn get_prepared_statement_mut_unverified(
692 &mut self,
693 name: &str,
694 ) -> Option<&mut PreparedStatement> {
695 self.prepared_statements.get_mut(name)
696 }
697
698 pub fn prepared_statements(&self) -> &BTreeMap<String, PreparedStatement> {
700 &self.prepared_statements
701 }
702
703 pub fn portals(&self) -> &BTreeMap<String, Portal> {
705 &self.portals
706 }
707
708 pub fn set_portal(
718 &mut self,
719 portal_name: String,
720 desc: StatementDesc,
721 stmt: Option<Statement<Raw>>,
722 logging: Arc<QCell<PreparedStatementLoggingInfo>>,
723 params: Vec<(Datum, SqlScalarType)>,
724 result_formats: Vec<Format>,
725 state_revision: StateRevision,
726 ) -> Result<(), AdapterError> {
727 if !portal_name.is_empty() && self.portals.contains_key(&portal_name) {
729 return Err(AdapterError::DuplicateCursor(portal_name));
730 }
731 self.state_revision += 1;
732 let param_types = desc.param_types.clone();
733 self.portals.insert(
734 portal_name,
735 Portal {
736 stmt: stmt.map(Arc::new),
737 desc,
738 state_revision,
739 parameters: Params {
740 datums: Row::pack(params.iter().map(|(d, _t)| d)),
741 execute_types: params.into_iter().map(|(_d, t)| t).collect(),
742 expected_types: param_types,
743 },
744 result_formats,
745 state: PortalState::NotStarted,
746 logging,
747 lifecycle_timestamps: None,
748 },
749 );
750 Ok(())
751 }
752
753 pub fn remove_portal(&mut self, portal_name: &str) -> bool {
757 self.state_revision += 1;
758 self.portals.remove(portal_name).is_some()
759 }
760
761 pub fn get_portal_unverified(&self, portal_name: &str) -> Option<&Portal> {
765 self.portals.get(portal_name)
766 }
767
768 pub fn get_portal_unverified_mut(&mut self, portal_name: &str) -> Option<PortalRefMut<'_>> {
775 self.portals.get_mut(portal_name).map(|p| PortalRefMut {
776 stmt: &p.stmt,
777 desc: &p.desc,
778 state_revision: &mut p.state_revision,
779 parameters: &mut p.parameters,
780 result_formats: &mut p.result_formats,
781 logging: &mut p.logging,
782 state: &mut p.state,
783 lifecycle_timestamps: &mut p.lifecycle_timestamps,
784 })
785 }
786
787 pub fn create_new_portal(
789 &mut self,
790 stmt: Option<Statement<Raw>>,
791 logging: Arc<QCell<PreparedStatementLoggingInfo>>,
792 desc: StatementDesc,
793 parameters: Params,
794 result_formats: Vec<Format>,
795 state_revision: StateRevision,
796 ) -> Result<String, AdapterError> {
797 self.state_revision += 1;
798
799 for i in 0usize.. {
801 let name = format!("<unnamed portal {}>", i);
802 match self.portals.entry(name.clone()) {
803 Entry::Occupied(_) => continue,
804 Entry::Vacant(entry) => {
805 entry.insert(Portal {
806 stmt: stmt.map(Arc::new),
807 desc,
808 state_revision,
809 parameters,
810 result_formats,
811 state: PortalState::NotStarted,
812 logging,
813 lifecycle_timestamps: None,
814 });
815 return Ok(name);
816 }
817 }
818 }
819
820 coord_bail!("unable to create a new portal");
821 }
822
823 pub fn reset(&mut self) {
826 let _ = self.clear_transaction();
827 self.prepared_statements.clear();
828 self.vars.reset_all();
829 }
830
831 pub fn application_name(&self) -> &str {
835 self.vars.application_name()
836 }
837
838 pub fn vars(&self) -> &SessionVars {
840 &self.vars
841 }
842
843 pub fn vars_mut(&mut self) -> &mut SessionVars {
845 &mut self.vars
846 }
847
848 pub fn try_grant_write_locks(&mut self, guards: WriteLocks) -> Result<(), &WriteLocks> {
854 self.transaction.try_grant_write_locks(guards)
855 }
856
857 pub fn apply_external_metadata_updates(&mut self) {
859 let Some(rx) = &mut self.external_metadata_rx else {
861 return;
862 };
863
864 if !rx.has_changed().unwrap_or(false) {
866 return;
867 }
868
869 let metadata = rx.borrow_and_update().clone();
872 self.vars.set_external_user_metadata(metadata);
873 }
874
875 pub fn apply_internal_user_metadata(&mut self, metadata: InternalUserMetadata) {
877 self.vars.set_internal_user_metadata(metadata);
878 }
879
880 pub fn initialize_role_metadata(&mut self, role_id: RoleId) {
882 self.role_metadata = Some(RoleMetadata::new(role_id));
883 }
884
885 pub fn ensure_timestamp_oracle(&mut self, timeline: Timeline) -> &mut InMemoryTimestampOracle {
888 self.session_oracles.entry(timeline).or_insert_with(|| {
889 InMemoryTimestampOracle::new(Timestamp::minimum(), NowFn::from(Timestamp::minimum))
890 })
891 }
892
893 pub fn ensure_local_timestamp_oracle(&mut self) -> &mut InMemoryTimestampOracle {
896 self.ensure_timestamp_oracle(Timeline::EpochMilliseconds)
897 }
898
899 pub fn get_timestamp_oracle(&self, timeline: &Timeline) -> Option<&InMemoryTimestampOracle> {
901 self.session_oracles.get(timeline)
902 }
903
904 pub fn apply_write(&mut self, timestamp: Timestamp) {
907 if self.vars().transaction_isolation() == &IsolationLevel::StrongSessionSerializable {
908 self.ensure_local_timestamp_oracle().apply_write(timestamp);
909 }
910 }
911
912 pub fn metrics(&self) -> &SessionMetrics {
914 &self.metrics
915 }
916
917 pub fn set_builtin_table_updates(&mut self, fut: BuiltinTableAppendNotify) {
919 let prev = self.builtin_updates.replace(fut);
920 mz_ore::soft_assert_or_log!(prev.is_none(), "replacing old builtin table notify");
921 }
922
923 pub fn clear_builtin_table_updates(&mut self) -> Option<impl Future<Output = ()> + 'static> {
926 if let Some(fut) = self.builtin_updates.take() {
927 let histogram = self
929 .metrics()
930 .session_startup_table_writes_seconds()
931 .clone();
932 Some(async move {
933 fut.wall_time().observe(histogram).await;
934 })
935 } else {
936 None
937 }
938 }
939
940 pub fn state_revision(&self) -> u64 {
943 self.state_revision
944 }
945}
946
947#[derive(Derivative, Clone)]
949#[derivative(Debug)]
950pub struct PreparedStatement {
951 stmt: Option<Statement<Raw>>,
952 desc: StatementDesc,
953 pub state_revision: StateRevision,
955 #[derivative(Debug = "ignore")]
956 logging: Arc<QCell<PreparedStatementLoggingInfo>>,
957}
958
959impl PreparedStatement {
960 pub fn stmt(&self) -> Option<&Statement<Raw>> {
963 self.stmt.as_ref()
964 }
965
966 pub fn desc(&self) -> &StatementDesc {
968 &self.desc
969 }
970
971 pub fn logging(&self) -> &Arc<QCell<PreparedStatementLoggingInfo>> {
973 &self.logging
974 }
975}
976
977#[derive(Derivative)]
979#[derivative(Debug)]
980pub struct Portal {
981 pub stmt: Option<Arc<Statement<Raw>>>,
983 pub desc: StatementDesc,
985 pub state_revision: StateRevision,
987 pub parameters: Params,
989 pub result_formats: Vec<Format>,
991 #[derivative(Debug = "ignore")]
993 pub logging: Arc<QCell<PreparedStatementLoggingInfo>>,
994 #[derivative(Debug = "ignore")]
996 pub state: PortalState,
997 pub lifecycle_timestamps: Option<LifecycleTimestamps>,
999}
1000
1001pub struct PortalRefMut<'a> {
1006 pub stmt: &'a Option<Arc<Statement<Raw>>>,
1008 pub desc: &'a StatementDesc,
1010 pub state_revision: &'a mut StateRevision,
1012 pub parameters: &'a mut Params,
1014 pub result_formats: &'a mut Vec<Format>,
1016 pub logging: &'a mut Arc<QCell<PreparedStatementLoggingInfo>>,
1018 pub state: &'a mut PortalState,
1020 pub lifecycle_timestamps: &'a mut Option<LifecycleTimestamps>,
1022}
1023
1024#[derive(Debug, Clone, Copy, PartialEq)]
1028pub struct StateRevision {
1029 pub catalog_revision: u64,
1031 pub session_state_revision: u64,
1033}
1034
1035pub enum PortalState {
1037 NotStarted,
1039 InProgress(Option<InProgressRows>),
1042 Completed(Option<String>),
1046}
1047
1048pub struct InProgressRows {
1050 pub current: Option<Box<dyn RowIterator + Send + Sync>>,
1052 pub remaining: RecordFirstRowStream,
1054}
1055
1056impl InProgressRows {
1057 pub fn new(remaining: RecordFirstRowStream) -> Self {
1059 Self {
1060 current: None,
1061 remaining,
1062 }
1063 }
1064
1065 pub fn no_more_rows(&self) -> bool {
1068 self.remaining.no_more_rows && self.current.is_none()
1069 }
1070}
1071
1072pub type RowBatchStream = Box<dyn futures::Stream<Item = PeekResponseUnary> + Unpin + Send + Sync>;
1080
1081#[derive(Debug, Clone)]
1084pub struct LifecycleTimestamps {
1085 pub received: EpochMillis,
1090}
1091
1092impl LifecycleTimestamps {
1093 pub fn new(received: EpochMillis) -> Self {
1095 Self { received }
1096 }
1097}
1098
1099#[derive(Debug)]
1103pub enum TransactionStatus {
1104 Default,
1106 Started(Transaction),
1114 InTransaction(Transaction),
1116 InTransactionImplicit(Transaction),
1119 Failed(Transaction),
1121}
1122
1123impl TransactionStatus {
1124 pub fn into_ops_and_lock_guard(self) -> (Option<TransactionOps>, Option<WriteLocks>) {
1126 match self {
1127 TransactionStatus::Default | TransactionStatus::Failed(_) => (None, None),
1128 TransactionStatus::Started(txn)
1129 | TransactionStatus::InTransaction(txn)
1130 | TransactionStatus::InTransactionImplicit(txn) => {
1131 (Some(txn.ops), txn.write_lock_guards)
1132 }
1133 }
1134 }
1135
1136 pub fn inner(&self) -> Option<&Transaction> {
1138 match self {
1139 TransactionStatus::Default => None,
1140 TransactionStatus::Started(txn)
1141 | TransactionStatus::InTransaction(txn)
1142 | TransactionStatus::InTransactionImplicit(txn)
1143 | TransactionStatus::Failed(txn) => Some(txn),
1144 }
1145 }
1146
1147 pub fn inner_mut(&mut self) -> Option<&mut Transaction> {
1149 match self {
1150 TransactionStatus::Default => None,
1151 TransactionStatus::Started(txn)
1152 | TransactionStatus::InTransaction(txn)
1153 | TransactionStatus::InTransactionImplicit(txn)
1154 | TransactionStatus::Failed(txn) => Some(txn),
1155 }
1156 }
1157
1158 pub fn is_ddl(&self) -> bool {
1160 match self {
1161 TransactionStatus::Default => false,
1162 TransactionStatus::Started(txn)
1163 | TransactionStatus::InTransaction(txn)
1164 | TransactionStatus::InTransactionImplicit(txn)
1165 | TransactionStatus::Failed(txn) => {
1166 matches!(txn.ops, TransactionOps::DDL { .. })
1167 }
1168 }
1169 }
1170
1171 pub fn is_implicit(&self) -> bool {
1174 match self {
1175 TransactionStatus::Started(_) | TransactionStatus::InTransactionImplicit(_) => true,
1176 TransactionStatus::Default
1177 | TransactionStatus::InTransaction(_)
1178 | TransactionStatus::Failed(_) => false,
1179 }
1180 }
1181
1182 pub fn may_span_pipeline(&self) -> bool {
1190 match self {
1191 TransactionStatus::Started(txn) => match &txn.ops {
1192 TransactionOps::Writes(_) => true,
1193 TransactionOps::None
1194 | TransactionOps::Peeks { .. }
1195 | TransactionOps::Subscribe
1196 | TransactionOps::SingleStatement { .. }
1197 | TransactionOps::DDL { .. } => false,
1198 },
1199 TransactionStatus::Default
1200 | TransactionStatus::InTransaction(_)
1201 | TransactionStatus::InTransactionImplicit(_)
1202 | TransactionStatus::Failed(_) => false,
1203 }
1204 }
1205
1206 pub fn is_in_multi_statement_transaction(&self) -> bool {
1208 match self {
1209 TransactionStatus::InTransaction(_) | TransactionStatus::InTransactionImplicit(_) => {
1210 true
1211 }
1212 TransactionStatus::Default
1213 | TransactionStatus::Started(_)
1214 | TransactionStatus::Failed(_) => false,
1215 }
1216 }
1217
1218 pub fn may_share_transaction_with_other_statements(&self) -> bool {
1238 self.is_in_multi_statement_transaction() || self.contains_ops()
1239 }
1240
1241 pub fn in_immediate_multi_stmt_txn(&self, when: &QueryWhen) -> bool {
1243 self.is_in_multi_statement_transaction() && when == &QueryWhen::Immediately
1244 }
1245
1246 pub fn try_grant_write_locks(&mut self, guards: WriteLocks) -> Result<(), &WriteLocks> {
1255 match self {
1256 TransactionStatus::Default => panic!("cannot grant write lock to txn not yet started"),
1257 TransactionStatus::Started(txn)
1258 | TransactionStatus::InTransaction(txn)
1259 | TransactionStatus::InTransactionImplicit(txn)
1260 | TransactionStatus::Failed(txn) => txn.try_grant_write_locks(guards),
1261 }
1262 }
1263
1264 pub fn write_locks(&self) -> Option<&WriteLocks> {
1266 match self {
1267 TransactionStatus::Default => None,
1268 TransactionStatus::Started(txn)
1269 | TransactionStatus::InTransaction(txn)
1270 | TransactionStatus::InTransactionImplicit(txn)
1271 | TransactionStatus::Failed(txn) => txn.write_lock_guards.as_ref(),
1272 }
1273 }
1274
1275 pub fn timeline(&self) -> Option<Timeline> {
1277 match self {
1278 TransactionStatus::Default => None,
1279 TransactionStatus::Started(txn)
1280 | TransactionStatus::InTransaction(txn)
1281 | TransactionStatus::InTransactionImplicit(txn)
1282 | TransactionStatus::Failed(txn) => txn.timeline(),
1283 }
1284 }
1285
1286 pub fn cluster(&self) -> Option<ClusterId> {
1288 match self {
1289 TransactionStatus::Default => None,
1290 TransactionStatus::Started(txn)
1291 | TransactionStatus::InTransaction(txn)
1292 | TransactionStatus::InTransactionImplicit(txn)
1293 | TransactionStatus::Failed(txn) => txn.cluster(),
1294 }
1295 }
1296
1297 pub fn catalog_state(&self) -> Option<&CatalogState> {
1299 match self.inner() {
1300 Some(Transaction {
1301 ops: TransactionOps::DDL { state, .. },
1302 ..
1303 }) => Some(state),
1304 _ => None,
1305 }
1306 }
1307
1308 pub fn contains_ops(&self) -> bool {
1310 match self.inner() {
1311 Some(txn) => txn.contains_ops(),
1312 None => false,
1313 }
1314 }
1315
1316 pub fn allows_writes(&self) -> bool {
1319 match self {
1320 TransactionStatus::Started(Transaction { ops, access, .. })
1321 | TransactionStatus::InTransaction(Transaction { ops, access, .. })
1322 | TransactionStatus::InTransactionImplicit(Transaction { ops, access, .. }) => {
1323 match ops {
1324 TransactionOps::None => access != &Some(TransactionAccessMode::ReadOnly),
1325 TransactionOps::Peeks { determination, .. } => {
1326 access != &Some(TransactionAccessMode::ReadOnly)
1331 && !determination.timestamp_context.contains_timestamp()
1332 }
1333 TransactionOps::Subscribe => false,
1334 TransactionOps::Writes(_) => true,
1335 TransactionOps::SingleStatement { .. } => false,
1336 TransactionOps::DDL { .. } => false,
1337 }
1338 }
1339 TransactionStatus::Default | TransactionStatus::Failed(_) => {
1340 unreachable!()
1341 }
1342 }
1343 }
1344
1345 pub fn add_ops(&mut self, add_ops: TransactionOps) -> Result<(), AdapterError> {
1358 match self {
1359 TransactionStatus::Started(Transaction { ops, access, .. })
1360 | TransactionStatus::InTransaction(Transaction { ops, access, .. })
1361 | TransactionStatus::InTransactionImplicit(Transaction { ops, access, .. }) => {
1362 match ops {
1363 TransactionOps::None => {
1364 if matches!(access, Some(TransactionAccessMode::ReadOnly))
1365 && matches!(add_ops, TransactionOps::Writes(_))
1366 {
1367 return Err(AdapterError::ReadOnlyTransaction);
1368 }
1369 *ops = add_ops;
1370 }
1371 TransactionOps::Peeks {
1372 determination,
1373 cluster_id,
1374 requires_linearization,
1375 } => match add_ops {
1376 TransactionOps::Peeks {
1377 determination: add_timestamp_determination,
1378 cluster_id: add_cluster_id,
1379 requires_linearization: add_requires_linearization,
1380 } => {
1381 assert_eq!(*cluster_id, add_cluster_id);
1382 match (
1383 &determination.timestamp_context,
1384 &add_timestamp_determination.timestamp_context,
1385 ) {
1386 (
1387 TimestampContext::TimelineTimestamp {
1388 timeline: txn_timeline,
1389 chosen_ts: txn_ts,
1390 oracle_ts: _,
1391 },
1392 TimestampContext::TimelineTimestamp {
1393 timeline: add_timeline,
1394 chosen_ts: add_ts,
1395 oracle_ts: _,
1396 },
1397 ) => {
1398 assert_eq!(txn_timeline, add_timeline);
1399 assert_eq!(txn_ts, add_ts);
1400 }
1401 (TimestampContext::NoTimestamp, _) => {
1402 *determination = add_timestamp_determination
1403 }
1404 (_, TimestampContext::NoTimestamp) => {}
1405 };
1406 if matches!(requires_linearization, RequireLinearization::NotRequired)
1407 && matches!(
1408 add_requires_linearization,
1409 RequireLinearization::Required
1410 )
1411 {
1412 *requires_linearization = add_requires_linearization;
1413 }
1414 }
1415 writes @ TransactionOps::Writes(..)
1424 if !determination.timestamp_context.contains_timestamp() =>
1425 {
1426 if matches!(access, Some(TransactionAccessMode::ReadOnly)) {
1427 return Err(AdapterError::ReadOnlyTransaction);
1428 }
1429 *ops = writes;
1430 }
1431 _ => return Err(AdapterError::ReadOnlyTransaction),
1432 },
1433 TransactionOps::Subscribe => {
1434 return Err(AdapterError::SubscribeOnlyTransaction);
1435 }
1436 TransactionOps::Writes(txn_writes) => match add_ops {
1437 TransactionOps::Writes(mut add_writes) => {
1438 assert!(!matches!(access, Some(TransactionAccessMode::ReadOnly)));
1441 txn_writes.append(&mut add_writes);
1442 }
1443 TransactionOps::Peeks { determination, .. }
1446 if !determination.timestamp_context.contains_timestamp() => {}
1447 _ => {
1448 return Err(AdapterError::WriteOnlyTransaction);
1449 }
1450 },
1451 TransactionOps::SingleStatement { .. } => {
1452 return Err(AdapterError::SingleStatementTransaction);
1453 }
1454 TransactionOps::DDL {
1455 ops: og_ops,
1456 revision: og_revision,
1457 state: og_state,
1458 side_effects,
1459 snapshot: og_snapshot,
1460 } => match add_ops {
1461 TransactionOps::DDL {
1462 ops: new_ops,
1463 revision: new_revision,
1464 side_effects: mut net_new_side_effects,
1465 state: new_state,
1466 snapshot: new_snapshot,
1467 } => {
1468 if *og_revision != new_revision {
1469 return Err(AdapterError::DDLTransactionRace);
1470 }
1471 if !new_ops.is_empty() {
1473 *og_ops = new_ops;
1474 *og_state = new_state;
1475 *og_snapshot = new_snapshot;
1476 }
1477 side_effects.append(&mut net_new_side_effects);
1478 }
1479 _ => return Err(AdapterError::DDLOnlyTransaction),
1480 },
1481 }
1482 }
1483 TransactionStatus::Default | TransactionStatus::Failed(_) => {
1484 unreachable!()
1485 }
1486 }
1487 Ok(())
1488 }
1489}
1490
1491pub type TransactionId = u64;
1493
1494impl Default for TransactionStatus {
1495 fn default() -> Self {
1496 TransactionStatus::Default
1497 }
1498}
1499
1500#[derive(Debug)]
1502pub struct Transaction {
1503 pub pcx: PlanContext,
1505 pub ops: TransactionOps,
1507 pub id: TransactionId,
1512 write_lock_guards: Option<WriteLocks>,
1514 access: Option<TransactionAccessMode>,
1516}
1517
1518impl Transaction {
1519 fn try_grant_write_locks(&mut self, guards: WriteLocks) -> Result<(), &WriteLocks> {
1522 match &mut self.write_lock_guards {
1523 Some(existing) => Err(existing),
1524 locks @ None => {
1525 *locks = Some(guards);
1526 Ok(())
1527 }
1528 }
1529 }
1530
1531 fn timeline(&self) -> Option<Timeline> {
1533 match &self.ops {
1534 TransactionOps::Peeks {
1535 determination:
1536 TimestampDetermination {
1537 timestamp_context: TimestampContext::TimelineTimestamp { timeline, .. },
1538 ..
1539 },
1540 ..
1541 } => Some(timeline.clone()),
1542 TransactionOps::Peeks { .. }
1543 | TransactionOps::None
1544 | TransactionOps::Subscribe
1545 | TransactionOps::Writes(_)
1546 | TransactionOps::SingleStatement { .. }
1547 | TransactionOps::DDL { .. } => None,
1548 }
1549 }
1550
1551 pub fn cluster(&self) -> Option<ClusterId> {
1553 match &self.ops {
1554 TransactionOps::Peeks { cluster_id, .. } => Some(cluster_id.clone()),
1555 TransactionOps::None
1556 | TransactionOps::Subscribe
1557 | TransactionOps::Writes(_)
1558 | TransactionOps::SingleStatement { .. }
1559 | TransactionOps::DDL { .. } => None,
1560 }
1561 }
1562
1563 fn contains_ops(&self) -> bool {
1565 !matches!(self.ops, TransactionOps::None)
1566 }
1567}
1568
1569#[derive(Debug, Clone, Copy)]
1571pub enum TransactionCode {
1572 Idle,
1574 InTransaction,
1576 Failed,
1578}
1579
1580impl From<TransactionCode> for u8 {
1581 fn from(code: TransactionCode) -> Self {
1582 match code {
1583 TransactionCode::Idle => b'I',
1584 TransactionCode::InTransaction => b'T',
1585 TransactionCode::Failed => b'E',
1586 }
1587 }
1588}
1589
1590impl From<TransactionCode> for String {
1591 fn from(code: TransactionCode) -> Self {
1592 char::from(u8::from(code)).to_string()
1593 }
1594}
1595
1596impl From<&TransactionStatus> for TransactionCode {
1597 fn from(status: &TransactionStatus) -> TransactionCode {
1599 match status {
1600 TransactionStatus::Default => TransactionCode::Idle,
1601 TransactionStatus::Started(_) => TransactionCode::InTransaction,
1602 TransactionStatus::InTransaction(_) => TransactionCode::InTransaction,
1603 TransactionStatus::InTransactionImplicit(_) => TransactionCode::InTransaction,
1604 TransactionStatus::Failed(_) => TransactionCode::Failed,
1605 }
1606 }
1607}
1608
1609#[derive(Derivative)]
1615#[derivative(Debug)]
1616pub enum TransactionOps {
1617 None,
1620 Peeks {
1625 determination: TimestampDetermination,
1627 cluster_id: ClusterId,
1629 requires_linearization: RequireLinearization,
1631 },
1632 Subscribe,
1634 Writes(Vec<WriteOp>),
1637 SingleStatement {
1639 stmt: Arc<Statement<Raw>>,
1641 params: mz_sql::plan::Params,
1643 },
1644 DDL {
1648 ops: Vec<crate::catalog::Op>,
1650 state: CatalogState,
1652 #[derivative(Debug = "ignore")]
1654 side_effects: Vec<
1655 Box<
1656 dyn for<'a> FnOnce(
1657 &'a mut Coordinator,
1658 Option<&'a mut ExecuteContext>,
1659 ) -> Pin<Box<dyn Future<Output = ()> + 'a>>
1660 + Send
1661 + Sync,
1662 >,
1663 >,
1664 revision: u64,
1666 snapshot: Option<Snapshot>,
1671 },
1672}
1673
1674impl TransactionOps {
1675 fn timestamp_determination(self) -> Option<TimestampDetermination> {
1676 match self {
1677 TransactionOps::Peeks { determination, .. } => Some(determination),
1678 TransactionOps::None
1679 | TransactionOps::Subscribe
1680 | TransactionOps::Writes(_)
1681 | TransactionOps::SingleStatement { .. }
1682 | TransactionOps::DDL { .. } => None,
1683 }
1684 }
1685}
1686
1687impl Default for TransactionOps {
1688 fn default() -> Self {
1689 Self::None
1690 }
1691}
1692
1693#[derive(Debug, Clone, PartialEq)]
1695pub struct WriteOp {
1696 pub id: CatalogItemId,
1698 pub rows: TableData,
1700}
1701
1702#[derive(Debug)]
1704pub enum RequireLinearization {
1705 Required,
1707 NotRequired,
1709}
1710
1711impl From<&ExplainContext> for RequireLinearization {
1712 fn from(ctx: &ExplainContext) -> Self {
1713 match ctx {
1714 ExplainContext::None | ExplainContext::PlanInsightsNotice(_) => {
1715 RequireLinearization::Required
1716 }
1717 _ => RequireLinearization::NotRequired,
1718 }
1719 }
1720}
1721
1722#[derive(Debug)]
1726pub struct WriteLocks {
1727 locks: BTreeMap<CatalogItemId, tokio::sync::OwnedMutexGuard<()>>,
1728 conn_id: ConnectionId,
1730}
1731
1732impl WriteLocks {
1733 pub fn builder(sources: impl IntoIterator<Item = CatalogItemId>) -> WriteLocksBuilder {
1738 let locks = sources.into_iter().map(|gid| (gid, None)).collect();
1739 WriteLocksBuilder { locks }
1740 }
1741
1742 pub fn validate(
1745 self,
1746 collections: impl Iterator<Item = CatalogItemId>,
1747 ) -> Result<Self, BTreeSet<CatalogItemId>> {
1748 let mut missing = BTreeSet::new();
1749 for collection in collections {
1750 if !self.locks.contains_key(&collection) {
1751 missing.insert(collection);
1752 }
1753 }
1754
1755 if missing.is_empty() {
1756 Ok(self)
1757 } else {
1758 drop(self);
1760 Err(missing)
1761 }
1762 }
1763}
1764
1765impl Drop for WriteLocks {
1766 fn drop(&mut self) {
1767 if !self.locks.is_empty() {
1769 tracing::info!(
1770 conn_id = %self.conn_id,
1771 locks = ?self.locks,
1772 "dropping write locks",
1773 );
1774 }
1775 }
1776}
1777
1778#[derive(Debug)]
1782pub struct WriteLocksBuilder {
1783 locks: BTreeMap<CatalogItemId, Option<tokio::sync::OwnedMutexGuard<()>>>,
1784}
1785
1786impl WriteLocksBuilder {
1787 pub fn insert_lock(&mut self, id: CatalogItemId, lock: tokio::sync::OwnedMutexGuard<()>) {
1789 self.locks.insert(id, Some(lock));
1790 }
1791
1792 pub fn all_or_nothing(self, conn_id: &ConnectionId) -> Result<WriteLocks, CatalogItemId> {
1797 let (locks, missing): (BTreeMap<_, _>, BTreeSet<_>) =
1798 self.locks
1799 .into_iter()
1800 .partition_map(|(gid, lock)| match lock {
1801 Some(lock) => itertools::Either::Left((gid, lock)),
1802 None => itertools::Either::Right(gid),
1803 });
1804
1805 match missing.iter().next() {
1806 None => {
1807 tracing::info!(%conn_id, ?locks, "acquired write locks");
1808 Ok(WriteLocks {
1809 locks,
1810 conn_id: conn_id.clone(),
1811 })
1812 }
1813 Some(gid) => {
1814 tracing::info!(?missing, "failed to acquire write locks");
1815 drop(locks);
1817 Err(*gid)
1818 }
1819 }
1820 }
1821}
1822
1823#[derive(Debug, Default)]
1872pub(crate) struct GroupCommitWriteLocks {
1873 locks: BTreeMap<CatalogItemId, tokio::sync::OwnedMutexGuard<()>>,
1874}
1875
1876impl GroupCommitWriteLocks {
1877 pub fn merge(&mut self, mut locks: WriteLocks) {
1879 let existing = std::mem::take(&mut locks.locks);
1883 self.locks.extend(existing);
1884 }
1885
1886 pub fn extend(&mut self, mut other: GroupCommitWriteLocks) {
1888 assert!(
1889 self.locks.keys().all(|id| !other.locks.contains_key(id)),
1890 "separately staged group commits must have disjoint lock sets"
1891 );
1892 self.locks.extend(std::mem::take(&mut other.locks));
1893 }
1894
1895 pub fn insert_lock(&mut self, id: CatalogItemId, lock: tokio::sync::OwnedMutexGuard<()>) {
1897 self.locks.insert(id, lock);
1898 }
1899
1900 pub fn missing_locks(
1902 &self,
1903 writes: impl Iterator<Item = CatalogItemId>,
1904 ) -> BTreeSet<CatalogItemId> {
1905 let mut missing = BTreeSet::new();
1906 for write in writes {
1907 if !self.locks.contains_key(&write) {
1908 missing.insert(write);
1909 }
1910 }
1911 missing
1912 }
1913}
1914
1915impl Drop for GroupCommitWriteLocks {
1916 fn drop(&mut self) {
1917 if !self.locks.is_empty() {
1918 tracing::info!(
1919 locks = ?self.locks,
1920 "dropping group commit write locks",
1921 );
1922 }
1923 }
1924}