1#![warn(missing_docs)]
13
14use std::collections::BTreeMap;
15use std::collections::btree_map::Entry;
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 mz_adapter_types::connection::ConnectionId;
25use mz_auth::AuthenticatorKind;
26use mz_auth::user::ExternalUserMetadata;
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::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 access,
409 id,
410 });
411 }
412 TransactionStatus::Started(mut txn)
413 | TransactionStatus::InTransactionImplicit(mut txn)
414 | TransactionStatus::InTransaction(mut txn) => {
415 if access.is_some() {
416 txn.access = access;
417 }
418 self.transaction = TransactionStatus::InTransaction(txn);
419 }
420 TransactionStatus::Failed(_) => unreachable!(),
421 };
422
423 if let Some(isolation_level) = isolation_level {
424 self.vars
425 .set_local_transaction_isolation(isolation_level.into());
426 }
427
428 Ok(())
429 }
430
431 pub fn start_transaction_implicit(&mut self, wall_time: DateTime<Utc>, stmts: usize) {
434 if let TransactionStatus::Default = self.transaction {
435 let id = self.next_transaction_id;
436 self.next_transaction_id = self.next_transaction_id.wrapping_add(1);
437 let txn = Transaction {
438 pcx: self.new_pcx(wall_time),
439 ops: TransactionOps::None,
440 access: None,
441 id,
442 };
443 match stmts {
444 1 => self.transaction = TransactionStatus::Started(txn),
445 n if n > 1 => self.transaction = TransactionStatus::InTransactionImplicit(txn),
446 _ => {}
447 }
448 }
449 }
450
451 pub fn start_transaction_single_stmt(&mut self, wall_time: DateTime<Utc>) {
453 self.start_transaction_implicit(wall_time, 1);
454 }
455
456 #[must_use]
466 pub fn clear_transaction(&mut self) -> TransactionStatus {
467 self.portals.clear();
468 self.pcx = None;
469 self.state_revision += 1;
470 mem::take(&mut self.transaction)
471 }
472
473 pub fn fail_transaction(mut self) -> Self {
475 match self.transaction {
476 TransactionStatus::Default => unreachable!(),
477 TransactionStatus::Started(txn)
478 | TransactionStatus::InTransactionImplicit(txn)
479 | TransactionStatus::InTransaction(txn) => {
480 self.transaction = TransactionStatus::Failed(txn);
481 }
482 TransactionStatus::Failed(_) => {}
483 };
484 self
485 }
486
487 pub fn transaction(&self) -> &TransactionStatus {
489 &self.transaction
490 }
491
492 pub fn transaction_mut(&mut self) -> &mut TransactionStatus {
494 &mut self.transaction
495 }
496
497 pub fn transaction_code(&self) -> TransactionCode {
499 self.transaction().into()
500 }
501
502 pub fn add_transaction_ops(&mut self, add_ops: TransactionOps) -> Result<(), AdapterError> {
506 self.transaction.add_ops(add_ops)
507 }
508
509 pub fn retain_notice_transmitter(&self) -> UnboundedSender<AdapterNotice> {
511 self.notices_tx.clone()
512 }
513
514 pub fn add_notice(&self, notice: AdapterNotice) {
516 self.add_notices([notice])
517 }
518
519 pub fn add_notices(&self, notices: impl IntoIterator<Item = AdapterNotice>) {
521 for notice in notices {
522 let _ = self.notices_tx.send(notice);
523 }
524 }
525
526 pub async fn recv_notice(&mut self) -> AdapterNotice {
530 loop {
532 let notice = self
533 .notices_rx
534 .recv()
535 .await
536 .expect("Session also holds a sender, so recv won't ever return None");
537 match self.notice_filter(notice) {
538 Some(notice) => return notice,
539 None => continue,
540 }
541 }
542 }
543
544 pub fn drain_notices(&mut self) -> Vec<AdapterNotice> {
546 let mut notices = Vec::new();
547 while let Ok(notice) = self.notices_rx.try_recv() {
548 if let Some(notice) = self.notice_filter(notice) {
549 notices.push(notice);
550 }
551 }
552 notices
553 }
554
555 fn notice_filter(&self, notice: AdapterNotice) -> Option<AdapterNotice> {
557 let minimum_client_severity = self.vars.client_min_messages();
559 let sev = notice.severity();
560 if !minimum_client_severity.should_output_to_client(&sev) {
561 return None;
562 }
563 if let AdapterNotice::ClusterReplicaStatusChanged { cluster, .. } = ¬ice {
565 if cluster != self.vars.cluster() {
566 return None;
567 }
568 }
569 Some(notice)
570 }
571
572 pub fn clear_transaction_ops(&mut self) {
575 if let Some(txn) = self.transaction.inner_mut() {
576 txn.ops = TransactionOps::None;
577 }
578 }
579
580 pub fn get_transaction_timestamp_determination(&self) -> Option<TimestampDetermination> {
585 match self.transaction.inner() {
586 Some(Transaction {
587 pcx: _,
588 ops: TransactionOps::Peeks { determination, .. },
589 access: _,
590 id: _,
591 }) => Some(determination.clone()),
592 _ => None,
593 }
594 }
595
596 pub fn contains_read_timestamp(&self) -> bool {
598 matches!(
599 self.transaction.inner(),
600 Some(Transaction {
601 pcx: _,
602 ops: TransactionOps::Peeks {
603 determination: TimestampDetermination {
604 timestamp_context: TimestampContext::TimelineTimestamp { .. },
605 ..
606 },
607 ..
608 },
609 access: _,
610 id: _,
611 })
612 )
613 }
614
615 pub fn set_prepared_statement(
617 &mut self,
618 name: String,
619 stmt: Option<Statement<Raw>>,
620 raw_sql: String,
621 desc: StatementDesc,
622 state_revision: StateRevision,
623 now: EpochMillis,
624 ) {
625 let logging = PreparedStatementLoggingInfo::still_to_log(
626 raw_sql,
627 stmt.as_ref(),
628 now,
629 name.clone(),
630 self.uuid,
631 false,
632 );
633 let statement = PreparedStatement {
634 stmt,
635 desc,
636 state_revision,
637 logging: Arc::new(QCell::new(&self.qcell_owner, logging)),
638 };
639 self.prepared_statements.insert(name, statement);
640 }
641
642 pub fn remove_prepared_statement(&mut self, name: &str) -> bool {
646 self.prepared_statements.remove(name).is_some()
647 }
648
649 pub fn remove_all_prepared_statements(&mut self) {
651 self.prepared_statements.clear();
652 }
653
654 pub fn get_prepared_statement_unverified(&self, name: &str) -> Option<&PreparedStatement> {
659 self.prepared_statements.get(name)
660 }
661
662 pub fn get_prepared_statement_mut_unverified(
667 &mut self,
668 name: &str,
669 ) -> Option<&mut PreparedStatement> {
670 self.prepared_statements.get_mut(name)
671 }
672
673 pub fn prepared_statements(&self) -> &BTreeMap<String, PreparedStatement> {
675 &self.prepared_statements
676 }
677
678 pub fn portals(&self) -> &BTreeMap<String, Portal> {
680 &self.portals
681 }
682
683 pub fn set_portal(
693 &mut self,
694 portal_name: String,
695 desc: StatementDesc,
696 stmt: Option<Statement<Raw>>,
697 logging: Arc<QCell<PreparedStatementLoggingInfo>>,
698 params: Vec<(Datum, SqlScalarType)>,
699 result_formats: Vec<Format>,
700 state_revision: StateRevision,
701 ) -> Result<(), AdapterError> {
702 if !portal_name.is_empty() && self.portals.contains_key(&portal_name) {
704 return Err(AdapterError::DuplicateCursor(portal_name));
705 }
706 self.state_revision += 1;
707 let param_types = desc.param_types.clone();
708 self.portals.insert(
709 portal_name,
710 Portal {
711 stmt: stmt.map(Arc::new),
712 desc,
713 state_revision,
714 parameters: Params {
715 datums: Row::pack(params.iter().map(|(d, _t)| d)),
716 execute_types: params.into_iter().map(|(_d, t)| t).collect(),
717 expected_types: param_types,
718 },
719 result_formats,
720 state: PortalState::NotStarted,
721 logging,
722 lifecycle_timestamps: None,
723 },
724 );
725 Ok(())
726 }
727
728 pub fn remove_portal(&mut self, portal_name: &str) -> bool {
732 self.state_revision += 1;
733 self.portals.remove(portal_name).is_some()
734 }
735
736 pub fn get_portal_unverified(&self, portal_name: &str) -> Option<&Portal> {
740 self.portals.get(portal_name)
741 }
742
743 pub fn get_portal_unverified_mut(&mut self, portal_name: &str) -> Option<PortalRefMut<'_>> {
750 self.portals.get_mut(portal_name).map(|p| PortalRefMut {
751 stmt: &p.stmt,
752 desc: &p.desc,
753 state_revision: &mut p.state_revision,
754 parameters: &mut p.parameters,
755 result_formats: &mut p.result_formats,
756 logging: &mut p.logging,
757 state: &mut p.state,
758 lifecycle_timestamps: &mut p.lifecycle_timestamps,
759 })
760 }
761
762 pub fn create_new_portal(
764 &mut self,
765 stmt: Option<Statement<Raw>>,
766 logging: Arc<QCell<PreparedStatementLoggingInfo>>,
767 desc: StatementDesc,
768 parameters: Params,
769 result_formats: Vec<Format>,
770 state_revision: StateRevision,
771 ) -> Result<String, AdapterError> {
772 self.state_revision += 1;
773
774 for i in 0usize.. {
776 let name = format!("<unnamed portal {}>", i);
777 match self.portals.entry(name.clone()) {
778 Entry::Occupied(_) => continue,
779 Entry::Vacant(entry) => {
780 entry.insert(Portal {
781 stmt: stmt.map(Arc::new),
782 desc,
783 state_revision,
784 parameters,
785 result_formats,
786 state: PortalState::NotStarted,
787 logging,
788 lifecycle_timestamps: None,
789 });
790 return Ok(name);
791 }
792 }
793 }
794
795 coord_bail!("unable to create a new portal");
796 }
797
798 pub fn reset(&mut self) -> BTreeMap<&'static str, String> {
802 let _ = self.clear_transaction();
803 self.prepared_statements.clear();
804 self.vars.reset_all()
805 }
806
807 pub fn application_name(&self) -> &str {
811 self.vars.application_name()
812 }
813
814 pub fn vars(&self) -> &SessionVars {
816 &self.vars
817 }
818
819 pub fn vars_mut(&mut self) -> &mut SessionVars {
821 &mut self.vars
822 }
823
824 pub fn apply_external_metadata_updates(&mut self) {
826 let Some(rx) = &mut self.external_metadata_rx else {
828 return;
829 };
830
831 if !rx.has_changed().unwrap_or(false) {
833 return;
834 }
835
836 let metadata = rx.borrow_and_update().clone();
839 self.vars.set_external_user_metadata(metadata);
840 }
841
842 pub fn apply_internal_user_metadata(&mut self, metadata: InternalUserMetadata) {
844 self.vars.set_internal_user_metadata(metadata);
845 }
846
847 pub fn initialize_role_metadata(&mut self, role_id: RoleId) {
849 self.role_metadata = Some(RoleMetadata::new(role_id));
850 }
851
852 pub fn ensure_timestamp_oracle(&mut self, timeline: Timeline) -> &mut InMemoryTimestampOracle {
855 self.session_oracles.entry(timeline).or_insert_with(|| {
856 InMemoryTimestampOracle::new(Timestamp::minimum(), NowFn::from(Timestamp::minimum))
857 })
858 }
859
860 pub fn ensure_local_timestamp_oracle(&mut self) -> &mut InMemoryTimestampOracle {
863 self.ensure_timestamp_oracle(Timeline::EpochMilliseconds)
864 }
865
866 pub fn get_timestamp_oracle(&self, timeline: &Timeline) -> Option<&InMemoryTimestampOracle> {
868 self.session_oracles.get(timeline)
869 }
870
871 pub fn apply_write(&mut self, timestamp: Timestamp) {
874 if self.vars().transaction_isolation() == &IsolationLevel::StrongSessionSerializable {
875 self.ensure_local_timestamp_oracle().apply_write(timestamp);
876 }
877 }
878
879 pub fn metrics(&self) -> &SessionMetrics {
881 &self.metrics
882 }
883
884 pub fn set_builtin_table_updates(&mut self, fut: BuiltinTableAppendNotify) {
886 let prev = self.builtin_updates.replace(fut);
887 mz_ore::soft_assert_or_log!(prev.is_none(), "replacing old builtin table notify");
888 }
889
890 pub fn clear_builtin_table_updates(&mut self) -> Option<impl Future<Output = ()> + 'static> {
893 if let Some(fut) = self.builtin_updates.take() {
894 let histogram = self
896 .metrics()
897 .session_startup_table_writes_seconds()
898 .clone();
899 Some(async move {
900 fut.wall_time().observe(histogram).await;
901 })
902 } else {
903 None
904 }
905 }
906
907 pub fn state_revision(&self) -> u64 {
910 self.state_revision
911 }
912}
913
914#[derive(Derivative, Clone)]
916#[derivative(Debug)]
917pub struct PreparedStatement {
918 stmt: Option<Statement<Raw>>,
919 desc: StatementDesc,
920 pub state_revision: StateRevision,
922 #[derivative(Debug = "ignore")]
923 logging: Arc<QCell<PreparedStatementLoggingInfo>>,
924}
925
926impl PreparedStatement {
927 pub fn stmt(&self) -> Option<&Statement<Raw>> {
930 self.stmt.as_ref()
931 }
932
933 pub fn desc(&self) -> &StatementDesc {
935 &self.desc
936 }
937
938 pub fn logging(&self) -> &Arc<QCell<PreparedStatementLoggingInfo>> {
940 &self.logging
941 }
942}
943
944#[derive(Derivative)]
946#[derivative(Debug)]
947pub struct Portal {
948 pub stmt: Option<Arc<Statement<Raw>>>,
950 pub desc: StatementDesc,
952 pub state_revision: StateRevision,
954 pub parameters: Params,
956 pub result_formats: Vec<Format>,
958 #[derivative(Debug = "ignore")]
960 pub logging: Arc<QCell<PreparedStatementLoggingInfo>>,
961 #[derivative(Debug = "ignore")]
963 pub state: PortalState,
964 pub lifecycle_timestamps: Option<LifecycleTimestamps>,
966}
967
968pub struct PortalRefMut<'a> {
973 pub stmt: &'a Option<Arc<Statement<Raw>>>,
975 pub desc: &'a StatementDesc,
977 pub state_revision: &'a mut StateRevision,
979 pub parameters: &'a mut Params,
981 pub result_formats: &'a mut Vec<Format>,
983 pub logging: &'a mut Arc<QCell<PreparedStatementLoggingInfo>>,
985 pub state: &'a mut PortalState,
987 pub lifecycle_timestamps: &'a mut Option<LifecycleTimestamps>,
989}
990
991#[derive(Debug, Clone, Copy, PartialEq)]
995pub struct StateRevision {
996 pub catalog_revision: u64,
998 pub session_state_revision: u64,
1000}
1001
1002pub enum PortalState {
1004 NotStarted,
1006 InProgress(Option<InProgressRows>),
1009 Completed(Option<String>),
1013}
1014
1015pub struct InProgressRows {
1017 pub current: Option<Box<dyn RowIterator + Send + Sync>>,
1019 pub remaining: RecordFirstRowStream,
1021}
1022
1023impl InProgressRows {
1024 pub fn new(remaining: RecordFirstRowStream) -> Self {
1026 Self {
1027 current: None,
1028 remaining,
1029 }
1030 }
1031
1032 pub fn no_more_rows(&self) -> bool {
1035 self.remaining.no_more_rows && self.current.is_none()
1036 }
1037}
1038
1039pub type RowBatchStream = Box<dyn futures::Stream<Item = PeekResponseUnary> + Unpin + Send + Sync>;
1047
1048#[derive(Debug, Clone)]
1051pub struct LifecycleTimestamps {
1052 pub received: EpochMillis,
1057}
1058
1059impl LifecycleTimestamps {
1060 pub fn new(received: EpochMillis) -> Self {
1062 Self { received }
1063 }
1064}
1065
1066#[derive(Debug)]
1070pub enum TransactionStatus {
1071 Default,
1073 Started(Transaction),
1081 InTransaction(Transaction),
1083 InTransactionImplicit(Transaction),
1086 Failed(Transaction),
1088}
1089
1090impl TransactionStatus {
1091 pub fn into_ops(self) -> Option<TransactionOps> {
1093 match self {
1094 TransactionStatus::Default | TransactionStatus::Failed(_) => None,
1095 TransactionStatus::Started(txn)
1096 | TransactionStatus::InTransaction(txn)
1097 | TransactionStatus::InTransactionImplicit(txn) => Some(txn.ops),
1098 }
1099 }
1100
1101 pub fn inner(&self) -> Option<&Transaction> {
1103 match self {
1104 TransactionStatus::Default => None,
1105 TransactionStatus::Started(txn)
1106 | TransactionStatus::InTransaction(txn)
1107 | TransactionStatus::InTransactionImplicit(txn)
1108 | TransactionStatus::Failed(txn) => Some(txn),
1109 }
1110 }
1111
1112 pub fn inner_mut(&mut self) -> Option<&mut Transaction> {
1114 match self {
1115 TransactionStatus::Default => None,
1116 TransactionStatus::Started(txn)
1117 | TransactionStatus::InTransaction(txn)
1118 | TransactionStatus::InTransactionImplicit(txn)
1119 | TransactionStatus::Failed(txn) => Some(txn),
1120 }
1121 }
1122
1123 pub fn is_ddl(&self) -> bool {
1125 match self {
1126 TransactionStatus::Default => false,
1127 TransactionStatus::Started(txn)
1128 | TransactionStatus::InTransaction(txn)
1129 | TransactionStatus::InTransactionImplicit(txn)
1130 | TransactionStatus::Failed(txn) => {
1131 matches!(txn.ops, TransactionOps::DDL { .. })
1132 }
1133 }
1134 }
1135
1136 pub fn is_implicit(&self) -> bool {
1139 match self {
1140 TransactionStatus::Started(_) | TransactionStatus::InTransactionImplicit(_) => true,
1141 TransactionStatus::Default
1142 | TransactionStatus::InTransaction(_)
1143 | TransactionStatus::Failed(_) => false,
1144 }
1145 }
1146
1147 pub fn may_span_pipeline(&self) -> bool {
1155 match self {
1156 TransactionStatus::Started(txn) => match &txn.ops {
1157 TransactionOps::Writes(_) => true,
1158 TransactionOps::None
1159 | TransactionOps::Peeks { .. }
1160 | TransactionOps::Subscribe
1161 | TransactionOps::SingleStatement { .. }
1162 | TransactionOps::DDL { .. } => false,
1163 },
1164 TransactionStatus::Default
1165 | TransactionStatus::InTransaction(_)
1166 | TransactionStatus::InTransactionImplicit(_)
1167 | TransactionStatus::Failed(_) => false,
1168 }
1169 }
1170
1171 pub fn is_in_multi_statement_transaction(&self) -> bool {
1173 match self {
1174 TransactionStatus::InTransaction(_) | TransactionStatus::InTransactionImplicit(_) => {
1175 true
1176 }
1177 TransactionStatus::Default
1178 | TransactionStatus::Started(_)
1179 | TransactionStatus::Failed(_) => false,
1180 }
1181 }
1182
1183 pub fn may_share_transaction_with_other_statements(&self) -> bool {
1203 self.is_in_multi_statement_transaction() || self.contains_ops()
1204 }
1205
1206 pub fn in_immediate_multi_stmt_txn(&self, when: &QueryWhen) -> bool {
1208 self.is_in_multi_statement_transaction() && when == &QueryWhen::Immediately
1209 }
1210
1211 pub fn timeline(&self) -> Option<Timeline> {
1213 match self {
1214 TransactionStatus::Default => None,
1215 TransactionStatus::Started(txn)
1216 | TransactionStatus::InTransaction(txn)
1217 | TransactionStatus::InTransactionImplicit(txn)
1218 | TransactionStatus::Failed(txn) => txn.timeline(),
1219 }
1220 }
1221
1222 pub fn cluster(&self) -> Option<ClusterId> {
1224 match self {
1225 TransactionStatus::Default => None,
1226 TransactionStatus::Started(txn)
1227 | TransactionStatus::InTransaction(txn)
1228 | TransactionStatus::InTransactionImplicit(txn)
1229 | TransactionStatus::Failed(txn) => txn.cluster(),
1230 }
1231 }
1232
1233 pub fn catalog_state(&self) -> Option<&CatalogState> {
1235 match self.inner() {
1236 Some(Transaction {
1237 ops: TransactionOps::DDL { state, .. },
1238 ..
1239 }) => Some(state),
1240 _ => None,
1241 }
1242 }
1243
1244 pub fn contains_ops(&self) -> bool {
1246 match self.inner() {
1247 Some(txn) => txn.contains_ops(),
1248 None => false,
1249 }
1250 }
1251
1252 pub fn allows_writes(&self) -> bool {
1255 match self {
1256 TransactionStatus::Started(Transaction { ops, access, .. })
1257 | TransactionStatus::InTransaction(Transaction { ops, access, .. })
1258 | TransactionStatus::InTransactionImplicit(Transaction { ops, access, .. }) => {
1259 match ops {
1260 TransactionOps::None => access != &Some(TransactionAccessMode::ReadOnly),
1261 TransactionOps::Peeks { determination, .. } => {
1262 access != &Some(TransactionAccessMode::ReadOnly)
1267 && !determination.timestamp_context.contains_timestamp()
1268 }
1269 TransactionOps::Subscribe => false,
1270 TransactionOps::Writes(_) => true,
1271 TransactionOps::SingleStatement { .. } => false,
1272 TransactionOps::DDL { .. } => false,
1273 }
1274 }
1275 TransactionStatus::Default | TransactionStatus::Failed(_) => {
1276 unreachable!()
1277 }
1278 }
1279 }
1280
1281 pub fn add_ops(&mut self, add_ops: TransactionOps) -> Result<(), AdapterError> {
1294 match self {
1295 TransactionStatus::Started(Transaction { ops, access, .. })
1296 | TransactionStatus::InTransaction(Transaction { ops, access, .. })
1297 | TransactionStatus::InTransactionImplicit(Transaction { ops, access, .. }) => {
1298 match ops {
1299 TransactionOps::None => {
1300 if matches!(access, Some(TransactionAccessMode::ReadOnly))
1301 && matches!(add_ops, TransactionOps::Writes(_))
1302 {
1303 return Err(AdapterError::ReadOnlyTransaction);
1304 }
1305 *ops = add_ops;
1306 }
1307 TransactionOps::Peeks {
1308 determination,
1309 cluster_id,
1310 requires_linearization,
1311 } => match add_ops {
1312 TransactionOps::Peeks {
1313 determination: add_timestamp_determination,
1314 cluster_id: add_cluster_id,
1315 requires_linearization: add_requires_linearization,
1316 } => {
1317 assert_eq!(*cluster_id, add_cluster_id);
1318 match (
1319 &determination.timestamp_context,
1320 &add_timestamp_determination.timestamp_context,
1321 ) {
1322 (
1323 TimestampContext::TimelineTimestamp {
1324 timeline: txn_timeline,
1325 chosen_ts: txn_ts,
1326 oracle_ts: _,
1327 },
1328 TimestampContext::TimelineTimestamp {
1329 timeline: add_timeline,
1330 chosen_ts: add_ts,
1331 oracle_ts: _,
1332 },
1333 ) => {
1334 assert_eq!(txn_timeline, add_timeline);
1335 assert_eq!(txn_ts, add_ts);
1336 }
1337 (TimestampContext::NoTimestamp, _) => {
1338 *determination = add_timestamp_determination
1339 }
1340 (_, TimestampContext::NoTimestamp) => {}
1341 };
1342 if matches!(requires_linearization, RequireLinearization::NotRequired)
1343 && matches!(
1344 add_requires_linearization,
1345 RequireLinearization::Required
1346 )
1347 {
1348 *requires_linearization = add_requires_linearization;
1349 }
1350 }
1351 writes @ TransactionOps::Writes(..)
1360 if !determination.timestamp_context.contains_timestamp() =>
1361 {
1362 if matches!(access, Some(TransactionAccessMode::ReadOnly)) {
1363 return Err(AdapterError::ReadOnlyTransaction);
1364 }
1365 *ops = writes;
1366 }
1367 _ => return Err(AdapterError::ReadOnlyTransaction),
1368 },
1369 TransactionOps::Subscribe => {
1370 return Err(AdapterError::SubscribeOnlyTransaction);
1371 }
1372 TransactionOps::Writes(txn_writes) => match add_ops {
1373 TransactionOps::Writes(mut add_writes) => {
1374 assert!(!matches!(access, Some(TransactionAccessMode::ReadOnly)));
1377 txn_writes.append(&mut add_writes);
1378 }
1379 TransactionOps::Peeks { determination, .. }
1382 if !determination.timestamp_context.contains_timestamp() => {}
1383 _ => {
1384 return Err(AdapterError::WriteOnlyTransaction);
1385 }
1386 },
1387 TransactionOps::SingleStatement { .. } => {
1388 return Err(AdapterError::SingleStatementTransaction);
1389 }
1390 TransactionOps::DDL {
1391 ops: og_ops,
1392 revision: og_revision,
1393 state: og_state,
1394 side_effects,
1395 snapshot: og_snapshot,
1396 } => match add_ops {
1397 TransactionOps::DDL {
1398 ops: new_ops,
1399 revision: new_revision,
1400 side_effects: mut net_new_side_effects,
1401 state: new_state,
1402 snapshot: new_snapshot,
1403 } => {
1404 if *og_revision != new_revision {
1405 return Err(AdapterError::DDLTransactionRace);
1406 }
1407 if !new_ops.is_empty() {
1409 *og_ops = new_ops;
1410 *og_state = new_state;
1411 *og_snapshot = new_snapshot;
1412 }
1413 side_effects.append(&mut net_new_side_effects);
1414 }
1415 _ => return Err(AdapterError::DDLOnlyTransaction),
1416 },
1417 }
1418 }
1419 TransactionStatus::Default | TransactionStatus::Failed(_) => {
1420 unreachable!()
1421 }
1422 }
1423 Ok(())
1424 }
1425}
1426
1427pub type TransactionId = u64;
1429
1430impl Default for TransactionStatus {
1431 fn default() -> Self {
1432 TransactionStatus::Default
1433 }
1434}
1435
1436#[derive(Debug)]
1438pub struct Transaction {
1439 pub pcx: PlanContext,
1441 pub ops: TransactionOps,
1443 pub id: TransactionId,
1448 access: Option<TransactionAccessMode>,
1450}
1451
1452impl Transaction {
1453 fn timeline(&self) -> Option<Timeline> {
1455 match &self.ops {
1456 TransactionOps::Peeks {
1457 determination:
1458 TimestampDetermination {
1459 timestamp_context: TimestampContext::TimelineTimestamp { timeline, .. },
1460 ..
1461 },
1462 ..
1463 } => Some(timeline.clone()),
1464 TransactionOps::Peeks { .. }
1465 | TransactionOps::None
1466 | TransactionOps::Subscribe
1467 | TransactionOps::Writes(_)
1468 | TransactionOps::SingleStatement { .. }
1469 | TransactionOps::DDL { .. } => None,
1470 }
1471 }
1472
1473 pub fn cluster(&self) -> Option<ClusterId> {
1475 match &self.ops {
1476 TransactionOps::Peeks { cluster_id, .. } => Some(cluster_id.clone()),
1477 TransactionOps::None
1478 | TransactionOps::Subscribe
1479 | TransactionOps::Writes(_)
1480 | TransactionOps::SingleStatement { .. }
1481 | TransactionOps::DDL { .. } => None,
1482 }
1483 }
1484
1485 fn contains_ops(&self) -> bool {
1487 !matches!(self.ops, TransactionOps::None)
1488 }
1489}
1490
1491#[derive(Debug, Clone, Copy)]
1493pub enum TransactionCode {
1494 Idle,
1496 InTransaction,
1498 Failed,
1500}
1501
1502impl From<TransactionCode> for u8 {
1503 fn from(code: TransactionCode) -> Self {
1504 match code {
1505 TransactionCode::Idle => b'I',
1506 TransactionCode::InTransaction => b'T',
1507 TransactionCode::Failed => b'E',
1508 }
1509 }
1510}
1511
1512impl From<TransactionCode> for String {
1513 fn from(code: TransactionCode) -> Self {
1514 char::from(u8::from(code)).to_string()
1515 }
1516}
1517
1518impl From<&TransactionStatus> for TransactionCode {
1519 fn from(status: &TransactionStatus) -> TransactionCode {
1521 match status {
1522 TransactionStatus::Default => TransactionCode::Idle,
1523 TransactionStatus::Started(_) => TransactionCode::InTransaction,
1524 TransactionStatus::InTransaction(_) => TransactionCode::InTransaction,
1525 TransactionStatus::InTransactionImplicit(_) => TransactionCode::InTransaction,
1526 TransactionStatus::Failed(_) => TransactionCode::Failed,
1527 }
1528 }
1529}
1530
1531#[derive(Derivative)]
1537#[derivative(Debug)]
1538pub enum TransactionOps {
1539 None,
1542 Peeks {
1547 determination: TimestampDetermination,
1549 cluster_id: ClusterId,
1551 requires_linearization: RequireLinearization,
1553 },
1554 Subscribe,
1556 Writes(Vec<WriteOp>),
1559 SingleStatement {
1561 stmt: Arc<Statement<Raw>>,
1563 params: mz_sql::plan::Params,
1565 },
1566 DDL {
1570 ops: Vec<crate::catalog::Op>,
1572 state: CatalogState,
1574 #[derivative(Debug = "ignore")]
1576 side_effects: Vec<
1577 Box<
1578 dyn for<'a> FnOnce(
1579 &'a mut Coordinator,
1580 Option<&'a mut ExecuteContext>,
1581 ) -> Pin<Box<dyn Future<Output = ()> + 'a>>
1582 + Send
1583 + Sync,
1584 >,
1585 >,
1586 revision: u64,
1588 snapshot: Option<Snapshot>,
1593 },
1594}
1595
1596impl Default for TransactionOps {
1597 fn default() -> Self {
1598 Self::None
1599 }
1600}
1601
1602#[derive(Debug, Clone, PartialEq)]
1604pub struct WriteOp {
1605 pub id: CatalogItemId,
1607 pub rows: TableData,
1609}
1610
1611#[derive(Debug)]
1613pub enum RequireLinearization {
1614 Required,
1616 NotRequired,
1618}
1619
1620impl From<&ExplainContext> for RequireLinearization {
1621 fn from(ctx: &ExplainContext) -> Self {
1622 match ctx {
1623 ExplainContext::None | ExplainContext::PlanInsightsNotice(_) => {
1624 RequireLinearization::Required
1625 }
1626 _ => RequireLinearization::NotRequired,
1627 }
1628 }
1629}