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