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::sync::Arc;
20
21use chrono::{DateTime, Utc};
22use derivative::Derivative;
23use mz_adapter_types::connection::ConnectionId;
24use mz_auth::AuthenticatorKind;
25use mz_auth::user::ExternalUserMetadata;
26use mz_build_info::{BuildInfo, DUMMY_BUILD_INFO};
27use mz_controller_types::ClusterId;
28use mz_ore::metrics::{MetricsFutureExt, MetricsRegistry};
29use mz_ore::now::{EpochMillis, NowFn};
30use mz_pgwire_common::Format;
31use mz_repr::role_id::RoleId;
32use mz_repr::user::InternalUserMetadata;
33use mz_repr::{CatalogItemId, Datum, Row, RowIterator, SqlScalarType, Timestamp};
34use mz_sql::ast::{AstInfo, Raw, Statement, TransactionAccessMode};
35use mz_sql::plan::{Params, PlanContext, QueryWhen, StatementDesc};
36use mz_sql::session::metadata::SessionMetadata;
37use mz_sql::session::user::{
38 INTERNAL_USER_NAME_TO_DEFAULT_CLUSTER, RoleMetadata, SYSTEM_USER, User,
39};
40use mz_sql::session::vars::IsolationLevel;
41pub use mz_sql::session::vars::{
42 DEFAULT_DATABASE_NAME, EndTransactionAction, SERVER_MAJOR_VERSION, SERVER_MINOR_VERSION,
43 SERVER_PATCH_VERSION, SessionVars, Var,
44};
45use mz_sql_parser::ast::TransactionIsolationLevel;
46use mz_storage_client::client::TableData;
47use mz_storage_types::sources::Timeline;
48use qcell::{QCell, QCellOwner};
49use timely::progress::Timestamp as _;
50use tokio::sync::mpsc::{self, UnboundedSender};
51use tokio::sync::watch;
52use uuid::Uuid;
53
54use crate::AdapterNotice;
55use crate::ExecutionTime;
56use crate::catalog::CatalogState;
57use crate::client::RecordFirstRowStream;
58use crate::coord::ExplainContext;
59use crate::coord::appends::BuiltinTableAppendNotify;
60use crate::coord::in_memory_oracle::InMemoryTimestampOracle;
61use crate::coord::peek::PeekResponseUnary;
62use crate::coord::timestamp_selection::{TimestampContext, TimestampDetermination};
63use crate::error::AdapterError;
64use crate::metrics::{Metrics, SessionMetrics};
65use crate::notice::ExecutionTimeKind;
66use crate::statement_logging::PreparedStatementLoggingInfo;
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 completion_owner: TransactionCompletionOwner::Coordinator,
408 pcx: self.new_pcx(wall_time),
409 ops: TransactionOps::None,
410 pending_ddl_inspections: Vec::new(),
411 contains_explain: false,
412 access,
413 id,
414 });
415 }
416 TransactionStatus::Started(mut txn)
417 | TransactionStatus::InTransactionImplicit(mut txn)
418 | TransactionStatus::InTransaction(mut txn) => {
419 if access.is_some() {
420 txn.access = access;
421 }
422 self.transaction = TransactionStatus::InTransaction(txn);
423 }
424 TransactionStatus::Failed(_) => unreachable!(),
425 };
426
427 if let Some(isolation_level) = isolation_level {
428 self.vars
429 .set_local_transaction_isolation(isolation_level.into());
430 }
431
432 Ok(())
433 }
434
435 pub fn start_transaction_implicit(&mut self, wall_time: DateTime<Utc>, stmts: usize) {
438 if let TransactionStatus::Default = self.transaction {
439 let id = self.next_transaction_id;
440 self.next_transaction_id = self.next_transaction_id.wrapping_add(1);
441 let txn = Transaction {
442 completion_owner: TransactionCompletionOwner::Session,
443 pcx: self.new_pcx(wall_time),
444 ops: TransactionOps::None,
445 pending_ddl_inspections: Vec::new(),
446 contains_explain: false,
447 access: None,
448 id,
449 };
450 match stmts {
451 1 => self.transaction = TransactionStatus::Started(txn),
452 n if n > 1 => self.transaction = TransactionStatus::InTransactionImplicit(txn),
453 _ => {}
454 }
455 }
456 }
457
458 pub fn start_transaction_single_stmt(&mut self, wall_time: DateTime<Utc>) {
460 self.start_transaction_implicit(wall_time, 1);
461 }
462
463 #[must_use]
473 pub fn clear_transaction(&mut self) -> TransactionStatus {
474 self.portals.clear();
475 self.pcx = None;
476 self.state_revision += 1;
477 mem::take(&mut self.transaction)
478 }
479
480 pub(crate) fn require_coordinator_completion(&mut self) {
483 if let Some(txn) = self.transaction.inner_mut() {
484 txn.completion_owner = TransactionCompletionOwner::Coordinator;
485 }
486 }
487
488 pub(crate) fn read_only_completion(
490 &self,
491 mut action: EndTransactionAction,
492 ) -> Option<ReadOnlyCompletion> {
493 let txn = self.transaction.inner()?;
494 if txn.completion_owner != TransactionCompletionOwner::Session {
495 return None;
496 }
497 let timestamp = match &txn.ops {
498 TransactionOps::None => None,
499 TransactionOps::Peeks {
500 determination,
501 requires_linearization,
502 ..
503 } => matches!(requires_linearization, RequireLinearization::Required)
504 .then(|| determination.timestamp_context.clone()),
505 TransactionOps::Writes(_)
506 | TransactionOps::Subscribe
507 | TransactionOps::SingleStatement { .. }
508 | TransactionOps::DDL { .. } => return None,
509 };
510 if matches!(self.transaction, TransactionStatus::Failed(_)) {
511 action = EndTransactionAction::Rollback;
512 }
513 Some(ReadOnlyCompletion {
514 action,
515 timestamp: if action == EndTransactionAction::Commit {
516 timestamp
517 } else {
518 None
519 },
520 })
521 }
522
523 pub fn fail_transaction(mut self) -> Self {
525 match self.transaction {
526 TransactionStatus::Default => unreachable!(),
527 TransactionStatus::Started(txn)
528 | TransactionStatus::InTransactionImplicit(txn)
529 | TransactionStatus::InTransaction(txn) => {
530 self.transaction = TransactionStatus::Failed(txn);
531 }
532 TransactionStatus::Failed(_) => {}
533 };
534 self
535 }
536
537 pub fn transaction(&self) -> &TransactionStatus {
539 &self.transaction
540 }
541
542 pub fn transaction_mut(&mut self) -> &mut TransactionStatus {
544 &mut self.transaction
545 }
546
547 pub fn transaction_code(&self) -> TransactionCode {
549 self.transaction().into()
550 }
551
552 pub(crate) fn retain_catalog_result(
554 &mut self,
555 command: Arc<crate::catalog::command::CatalogResultCommand>,
556 ) {
557 self.retain_ddl_inspection(DdlInspection::CatalogResult(command));
558 }
559
560 fn retain_ddl_inspection(&mut self, inspection: DdlInspection) {
561 if !self.transaction().can_retain_ddl_inspections() {
562 return;
563 }
564 let txn = self
565 .transaction_mut()
566 .inner_mut()
567 .expect("active transaction");
568 match &mut txn.ops {
569 TransactionOps::DDL { commands, .. } => commands.push(inspection.into_command()),
570 _ => txn.pending_ddl_inspections.push(inspection),
571 }
572 }
573
574 pub(crate) fn retain_description(
576 &mut self,
577 statement: mz_sql::ast::Statement<mz_sql::ast::Raw>,
578 param_types: Vec<Option<mz_repr::SqlScalarType>>,
579 expected: mz_sql::plan::StatementDesc,
580 ) {
581 if !self.transaction().can_retain_ddl_inspections()
582 || (expected.relation_desc.is_none() && expected.param_types.is_empty())
583 {
584 return;
585 }
586 let mut described = &statement;
589 while let mz_sql::ast::Statement::Declare(declare) = described {
590 described = &declare.stmt;
591 }
592 if matches!(
593 described,
594 mz_sql::ast::Statement::Execute(_) | mz_sql::ast::Statement::Fetch(_)
595 ) {
596 return;
597 }
598 let command = crate::catalog::command::DescriptionCommand::new(
599 statement,
600 param_types,
601 self.meta(),
602 expected,
603 );
604 self.retain_ddl_inspection(DdlInspection::Description(Arc::new(command)));
605 }
606
607 pub fn add_transaction_ops(&mut self, add_ops: TransactionOps) -> Result<(), AdapterError> {
609 self.transaction.add_ops(add_ops)
610 }
611
612 pub fn retain_notice_transmitter(&self) -> UnboundedSender<AdapterNotice> {
614 self.notices_tx.clone()
615 }
616
617 pub fn add_notice(&self, notice: AdapterNotice) {
619 self.add_notices([notice])
620 }
621
622 pub fn add_notices(&self, notices: impl IntoIterator<Item = AdapterNotice>) {
624 for notice in notices {
625 let _ = self.notices_tx.send(notice);
626 }
627 }
628
629 pub async fn recv_notice(&mut self) -> AdapterNotice {
633 loop {
635 let notice = self
636 .notices_rx
637 .recv()
638 .await
639 .expect("Session also holds a sender, so recv won't ever return None");
640 match self.notice_filter(notice) {
641 Some(notice) => return notice,
642 None => continue,
643 }
644 }
645 }
646
647 pub fn drain_notices(&mut self) -> Vec<AdapterNotice> {
649 let mut notices = Vec::new();
650 while let Ok(notice) = self.notices_rx.try_recv() {
651 if let Some(notice) = self.notice_filter(notice) {
652 notices.push(notice);
653 }
654 }
655 notices
656 }
657
658 pub fn execution_time_notice(&self, time: ExecutionTime) -> Option<AdapterNotice> {
660 if !self.vars.emit_execution_time_notice() {
661 return None;
662 }
663 self.notice_filter(AdapterNotice::ExecutionTime(time))
664 }
665
666 pub fn maybe_mark_staged(&self, time: ExecutionTime) -> ExecutionTime {
672 let staged = self
673 .transaction()
674 .inner()
675 .is_some_and(|txn| txn.ops.stages_work());
676 if staged {
677 ExecutionTime {
678 kind: ExecutionTimeKind::Staged,
679 ..time
680 }
681 } else {
682 time
683 }
684 }
685
686 fn notice_filter(&self, notice: AdapterNotice) -> Option<AdapterNotice> {
688 let minimum_client_severity = self.vars.client_min_messages();
690 let sev = notice.severity();
691 if !minimum_client_severity.should_output_to_client(&sev) {
692 return None;
693 }
694 if let AdapterNotice::ClusterReplicaStatusChanged { cluster, .. } = ¬ice {
696 if cluster != self.vars.cluster() {
697 return None;
698 }
699 }
700 Some(notice)
701 }
702
703 pub fn clear_transaction_ops(&mut self) {
706 if let Some(txn) = self.transaction.inner_mut() {
707 txn.ops = TransactionOps::None;
708 }
709 }
710
711 pub fn get_transaction_timestamp_determination(&self) -> Option<TimestampDetermination> {
716 match self.transaction.inner() {
717 Some(Transaction {
718 pcx: _,
719 ops: TransactionOps::Peeks { determination, .. },
720 access: _,
721 id: _,
722 pending_ddl_inspections: _,
723 contains_explain: _,
724 completion_owner: _,
725 }) => Some(determination.clone()),
726 _ => None,
727 }
728 }
729
730 pub fn contains_read_timestamp(&self) -> bool {
732 matches!(
733 self.transaction.inner(),
734 Some(Transaction {
735 pcx: _,
736 ops: TransactionOps::Peeks {
737 determination: TimestampDetermination {
738 timestamp_context: TimestampContext::TimelineTimestamp { .. },
739 ..
740 },
741 ..
742 },
743 access: _,
744 id: _,
745 pending_ddl_inspections: _,
746 contains_explain: _,
747 completion_owner: _,
748 })
749 )
750 }
751
752 pub fn set_prepared_statement(
754 &mut self,
755 name: String,
756 stmt: Option<Statement<Raw>>,
757 raw_sql: String,
758 desc: StatementDesc,
759 state_revision: StateRevision,
760 now: EpochMillis,
761 ) {
762 let logging = PreparedStatementLoggingInfo::still_to_log(
763 raw_sql,
764 stmt.as_ref(),
765 now,
766 name.clone(),
767 self.uuid,
768 false,
769 );
770 let statement = PreparedStatement {
771 stmt,
772 desc,
773 state_revision,
774 logging: Arc::new(QCell::new(&self.qcell_owner, logging)),
775 };
776 self.prepared_statements.insert(name, statement);
777 }
778
779 pub fn remove_prepared_statement(&mut self, name: &str) -> bool {
783 self.prepared_statements.remove(name).is_some()
784 }
785
786 pub fn remove_all_prepared_statements(&mut self) {
788 self.prepared_statements.clear();
789 }
790
791 pub fn get_prepared_statement_unverified(&self, name: &str) -> Option<&PreparedStatement> {
796 self.prepared_statements.get(name)
797 }
798
799 pub fn get_prepared_statement_mut_unverified(
804 &mut self,
805 name: &str,
806 ) -> Option<&mut PreparedStatement> {
807 self.prepared_statements.get_mut(name)
808 }
809
810 pub fn prepared_statements(&self) -> &BTreeMap<String, PreparedStatement> {
812 &self.prepared_statements
813 }
814
815 pub fn portals(&self) -> &BTreeMap<String, Portal> {
817 &self.portals
818 }
819
820 pub fn set_portal(
830 &mut self,
831 portal_name: String,
832 desc: StatementDesc,
833 stmt: Option<Statement<Raw>>,
834 logging: Arc<QCell<PreparedStatementLoggingInfo>>,
835 params: Vec<(Datum, SqlScalarType)>,
836 result_formats: Vec<Format>,
837 state_revision: StateRevision,
838 ) -> Result<(), AdapterError> {
839 if !portal_name.is_empty() && self.portals.contains_key(&portal_name) {
841 return Err(AdapterError::DuplicateCursor(portal_name));
842 }
843 self.state_revision += 1;
844 let param_types = desc.param_types.clone();
845 self.portals.insert(
846 portal_name,
847 Portal {
848 stmt: stmt.map(Arc::new),
849 desc,
850 state_revision,
851 parameters: Params {
852 datums: Row::pack(params.iter().map(|(d, _t)| d)),
853 execute_types: params.into_iter().map(|(_d, t)| t).collect(),
854 expected_types: param_types,
855 },
856 result_formats,
857 state: PortalState::NotStarted,
858 logging,
859 lifecycle_timestamps: None,
860 },
861 );
862 Ok(())
863 }
864
865 pub fn remove_portal(&mut self, portal_name: &str) -> bool {
869 self.state_revision += 1;
870 self.portals.remove(portal_name).is_some()
871 }
872
873 pub fn get_portal_unverified(&self, portal_name: &str) -> Option<&Portal> {
877 self.portals.get(portal_name)
878 }
879
880 pub fn get_portal_unverified_mut(&mut self, portal_name: &str) -> Option<PortalRefMut<'_>> {
887 self.portals.get_mut(portal_name).map(|p| PortalRefMut {
888 stmt: &p.stmt,
889 desc: &p.desc,
890 state_revision: &mut p.state_revision,
891 parameters: &mut p.parameters,
892 result_formats: &mut p.result_formats,
893 logging: &mut p.logging,
894 state: &mut p.state,
895 lifecycle_timestamps: &mut p.lifecycle_timestamps,
896 })
897 }
898
899 pub fn create_new_portal(
901 &mut self,
902 stmt: Option<Statement<Raw>>,
903 logging: Arc<QCell<PreparedStatementLoggingInfo>>,
904 desc: StatementDesc,
905 parameters: Params,
906 result_formats: Vec<Format>,
907 state_revision: StateRevision,
908 ) -> Result<String, AdapterError> {
909 self.state_revision += 1;
910
911 for i in 0usize.. {
913 let name = format!("<unnamed portal {}>", i);
914 match self.portals.entry(name.clone()) {
915 Entry::Occupied(_) => continue,
916 Entry::Vacant(entry) => {
917 entry.insert(Portal {
918 stmt: stmt.map(Arc::new),
919 desc,
920 state_revision,
921 parameters,
922 result_formats,
923 state: PortalState::NotStarted,
924 logging,
925 lifecycle_timestamps: None,
926 });
927 return Ok(name);
928 }
929 }
930 }
931
932 coord_bail!("unable to create a new portal");
933 }
934
935 pub fn reset(&mut self) -> BTreeMap<&'static str, String> {
939 let _ = self.clear_transaction();
940 self.prepared_statements.clear();
941 self.vars.reset_all()
942 }
943
944 pub fn application_name(&self) -> &str {
948 self.vars.application_name()
949 }
950
951 pub fn vars(&self) -> &SessionVars {
953 &self.vars
954 }
955
956 pub fn vars_mut(&mut self) -> &mut SessionVars {
958 &mut self.vars
959 }
960
961 pub fn apply_external_metadata_updates(&mut self) {
963 let Some(rx) = &mut self.external_metadata_rx else {
965 return;
966 };
967
968 if !rx.has_changed().unwrap_or(false) {
970 return;
971 }
972
973 let metadata = rx.borrow_and_update().clone();
976 self.vars.set_external_user_metadata(metadata);
977 }
978
979 pub fn apply_internal_user_metadata(&mut self, metadata: InternalUserMetadata) {
981 self.vars.set_internal_user_metadata(metadata);
982 }
983
984 pub fn initialize_role_metadata(&mut self, role_id: RoleId) {
986 self.role_metadata = Some(RoleMetadata::new(role_id));
987 }
988
989 pub fn ensure_timestamp_oracle(&mut self, timeline: Timeline) -> &mut InMemoryTimestampOracle {
992 self.session_oracles.entry(timeline).or_insert_with(|| {
993 InMemoryTimestampOracle::new(Timestamp::minimum(), NowFn::from(Timestamp::minimum))
994 })
995 }
996
997 pub fn ensure_local_timestamp_oracle(&mut self) -> &mut InMemoryTimestampOracle {
1000 self.ensure_timestamp_oracle(Timeline::EpochMilliseconds)
1001 }
1002
1003 pub fn get_timestamp_oracle(&self, timeline: &Timeline) -> Option<&InMemoryTimestampOracle> {
1005 self.session_oracles.get(timeline)
1006 }
1007
1008 pub fn apply_write(&mut self, timestamp: Timestamp) {
1011 if self.vars().transaction_isolation() == &IsolationLevel::StrongSessionSerializable {
1012 self.ensure_local_timestamp_oracle().apply_write(timestamp);
1013 }
1014 }
1015
1016 pub fn metrics(&self) -> &SessionMetrics {
1018 &self.metrics
1019 }
1020
1021 pub fn set_builtin_table_updates(&mut self, fut: BuiltinTableAppendNotify) {
1023 let prev = self.builtin_updates.replace(fut);
1024 mz_ore::soft_assert_or_log!(prev.is_none(), "replacing old builtin table notify");
1025 }
1026
1027 pub fn clear_builtin_table_updates(&mut self) -> Option<impl Future<Output = ()> + 'static> {
1030 if let Some(fut) = self.builtin_updates.take() {
1031 let histogram = self
1033 .metrics()
1034 .session_startup_table_writes_seconds()
1035 .clone();
1036 Some(async move {
1037 fut.wall_time().observe(histogram).await;
1038 })
1039 } else {
1040 None
1041 }
1042 }
1043
1044 pub fn state_revision(&self) -> u64 {
1047 self.state_revision
1048 }
1049}
1050
1051#[derive(Derivative, Clone)]
1053#[derivative(Debug)]
1054pub struct PreparedStatement {
1055 stmt: Option<Statement<Raw>>,
1056 desc: StatementDesc,
1057 pub state_revision: StateRevision,
1059 #[derivative(Debug = "ignore")]
1060 logging: Arc<QCell<PreparedStatementLoggingInfo>>,
1061}
1062
1063impl PreparedStatement {
1064 pub fn stmt(&self) -> Option<&Statement<Raw>> {
1067 self.stmt.as_ref()
1068 }
1069
1070 pub fn desc(&self) -> &StatementDesc {
1072 &self.desc
1073 }
1074
1075 pub fn logging(&self) -> &Arc<QCell<PreparedStatementLoggingInfo>> {
1077 &self.logging
1078 }
1079}
1080
1081#[derive(Derivative)]
1083#[derivative(Debug)]
1084pub struct Portal {
1085 pub stmt: Option<Arc<Statement<Raw>>>,
1087 pub desc: StatementDesc,
1089 pub state_revision: StateRevision,
1091 pub parameters: Params,
1093 pub result_formats: Vec<Format>,
1095 #[derivative(Debug = "ignore")]
1097 pub logging: Arc<QCell<PreparedStatementLoggingInfo>>,
1098 #[derivative(Debug = "ignore")]
1100 pub state: PortalState,
1101 pub lifecycle_timestamps: Option<LifecycleTimestamps>,
1103}
1104
1105pub struct PortalRefMut<'a> {
1110 pub stmt: &'a Option<Arc<Statement<Raw>>>,
1112 pub desc: &'a StatementDesc,
1114 pub state_revision: &'a mut StateRevision,
1116 pub parameters: &'a mut Params,
1118 pub result_formats: &'a mut Vec<Format>,
1120 pub logging: &'a mut Arc<QCell<PreparedStatementLoggingInfo>>,
1122 pub state: &'a mut PortalState,
1124 pub lifecycle_timestamps: &'a mut Option<LifecycleTimestamps>,
1126}
1127
1128#[derive(Debug, Clone, Copy, PartialEq)]
1132pub struct StateRevision {
1133 pub catalog_revision: u64,
1135 pub session_state_revision: u64,
1137}
1138
1139pub enum PortalState {
1141 NotStarted,
1143 InProgress(Option<InProgressRows>),
1146 Completed(Option<String>),
1150}
1151
1152pub struct InProgressRows {
1154 pub current: Option<Box<dyn RowIterator + Send + Sync>>,
1156 pub remaining: RecordFirstRowStream,
1158}
1159
1160impl InProgressRows {
1161 pub fn new(remaining: RecordFirstRowStream) -> Self {
1163 Self {
1164 current: None,
1165 remaining,
1166 }
1167 }
1168
1169 pub fn no_more_rows(&self) -> bool {
1172 self.remaining.no_more_rows && self.current.is_none()
1173 }
1174}
1175
1176pub type RowBatchStream = Box<dyn futures::Stream<Item = PeekResponseUnary> + Unpin + Send + Sync>;
1184
1185#[derive(Debug, Clone)]
1188pub struct LifecycleTimestamps {
1189 pub received: EpochMillis,
1194}
1195
1196impl LifecycleTimestamps {
1197 pub fn new(received: EpochMillis) -> Self {
1199 Self { received }
1200 }
1201}
1202
1203#[derive(Debug)]
1207pub enum TransactionStatus {
1208 Default,
1210 Started(Transaction),
1218 InTransaction(Transaction),
1220 InTransactionImplicit(Transaction),
1223 Failed(Transaction),
1225}
1226
1227impl TransactionStatus {
1228 pub fn into_ops(self) -> Option<TransactionOps> {
1230 match self {
1231 TransactionStatus::Default | TransactionStatus::Failed(_) => None,
1232 TransactionStatus::Started(txn)
1233 | TransactionStatus::InTransaction(txn)
1234 | TransactionStatus::InTransactionImplicit(txn) => Some(txn.ops),
1235 }
1236 }
1237
1238 pub fn inner(&self) -> Option<&Transaction> {
1240 match self {
1241 TransactionStatus::Default => None,
1242 TransactionStatus::Started(txn)
1243 | TransactionStatus::InTransaction(txn)
1244 | TransactionStatus::InTransactionImplicit(txn)
1245 | TransactionStatus::Failed(txn) => Some(txn),
1246 }
1247 }
1248
1249 pub fn inner_mut(&mut self) -> Option<&mut Transaction> {
1251 match self {
1252 TransactionStatus::Default => None,
1253 TransactionStatus::Started(txn)
1254 | TransactionStatus::InTransaction(txn)
1255 | TransactionStatus::InTransactionImplicit(txn)
1256 | TransactionStatus::Failed(txn) => Some(txn),
1257 }
1258 }
1259
1260 fn can_retain_ddl_inspections(&self) -> bool {
1266 match self {
1267 Self::Started(txn) | Self::InTransaction(txn) | Self::InTransactionImplicit(txn) => {
1268 !txn.contains_explain
1269 && match txn.ops {
1270 TransactionOps::None | TransactionOps::DDL { .. } => true,
1271 TransactionOps::Peeks { .. }
1272 | TransactionOps::Writes(_)
1273 | TransactionOps::Subscribe
1274 | TransactionOps::SingleStatement { .. } => false,
1275 }
1276 }
1277 Self::Default | Self::Failed(_) => false,
1278 }
1279 }
1280
1281 pub(crate) fn record_explain(&mut self) -> Result<(), AdapterError> {
1283 let txn = self.inner_mut().expect("active transaction");
1284 if matches!(txn.ops, TransactionOps::DDL { .. }) {
1285 return Err(AdapterError::DDLOnlyTransaction);
1286 }
1287 txn.contains_explain = true;
1290 txn.pending_ddl_inspections.clear();
1291 Ok(())
1292 }
1293
1294 pub fn is_ddl(&self) -> bool {
1296 match self {
1297 TransactionStatus::Default => false,
1298 TransactionStatus::Started(txn)
1299 | TransactionStatus::InTransaction(txn)
1300 | TransactionStatus::InTransactionImplicit(txn)
1301 | TransactionStatus::Failed(txn) => {
1302 matches!(txn.ops, TransactionOps::DDL { .. })
1303 }
1304 }
1305 }
1306
1307 pub fn is_implicit(&self) -> bool {
1310 match self {
1311 TransactionStatus::Started(_) | TransactionStatus::InTransactionImplicit(_) => true,
1312 TransactionStatus::Default
1313 | TransactionStatus::InTransaction(_)
1314 | TransactionStatus::Failed(_) => false,
1315 }
1316 }
1317
1318 pub fn may_span_pipeline(&self) -> bool {
1326 match self {
1327 TransactionStatus::Started(txn) => match &txn.ops {
1328 TransactionOps::Writes(_) => true,
1329 TransactionOps::None
1330 | TransactionOps::Peeks { .. }
1331 | TransactionOps::Subscribe
1332 | TransactionOps::SingleStatement { .. }
1333 | TransactionOps::DDL { .. } => false,
1334 },
1335 TransactionStatus::Default
1336 | TransactionStatus::InTransaction(_)
1337 | TransactionStatus::InTransactionImplicit(_)
1338 | TransactionStatus::Failed(_) => false,
1339 }
1340 }
1341
1342 pub fn is_in_multi_statement_transaction(&self) -> bool {
1344 match self {
1345 TransactionStatus::InTransaction(_) | TransactionStatus::InTransactionImplicit(_) => {
1346 true
1347 }
1348 TransactionStatus::Default
1349 | TransactionStatus::Started(_)
1350 | TransactionStatus::Failed(_) => false,
1351 }
1352 }
1353
1354 pub fn may_share_transaction_with_other_statements(&self) -> bool {
1374 self.is_in_multi_statement_transaction() || self.contains_ops()
1375 }
1376
1377 pub fn in_immediate_multi_stmt_txn(&self, when: &QueryWhen) -> bool {
1379 self.is_in_multi_statement_transaction() && when == &QueryWhen::Immediately
1380 }
1381
1382 pub fn timeline(&self) -> Option<Timeline> {
1384 match self {
1385 TransactionStatus::Default => None,
1386 TransactionStatus::Started(txn)
1387 | TransactionStatus::InTransaction(txn)
1388 | TransactionStatus::InTransactionImplicit(txn)
1389 | TransactionStatus::Failed(txn) => txn.timeline(),
1390 }
1391 }
1392
1393 pub fn cluster(&self) -> Option<ClusterId> {
1395 match self {
1396 TransactionStatus::Default => None,
1397 TransactionStatus::Started(txn)
1398 | TransactionStatus::InTransaction(txn)
1399 | TransactionStatus::InTransactionImplicit(txn)
1400 | TransactionStatus::Failed(txn) => txn.cluster(),
1401 }
1402 }
1403
1404 pub fn catalog_state(&self) -> Option<&CatalogState> {
1406 match self.inner() {
1407 Some(Transaction {
1408 ops: TransactionOps::DDL { state, .. },
1409 ..
1410 }) => Some(state),
1411 _ => None,
1412 }
1413 }
1414
1415 pub fn contains_ops(&self) -> bool {
1417 match self.inner() {
1418 Some(txn) => txn.contains_ops(),
1419 None => false,
1420 }
1421 }
1422
1423 pub fn allows_writes(&self) -> bool {
1426 match self {
1427 TransactionStatus::Started(Transaction { ops, access, .. })
1428 | TransactionStatus::InTransaction(Transaction { ops, access, .. })
1429 | TransactionStatus::InTransactionImplicit(Transaction { ops, access, .. }) => {
1430 match ops {
1431 TransactionOps::None => access != &Some(TransactionAccessMode::ReadOnly),
1432 TransactionOps::Peeks { determination, .. } => {
1433 access != &Some(TransactionAccessMode::ReadOnly)
1438 && !determination.timestamp_context.contains_timestamp()
1439 }
1440 TransactionOps::Subscribe => false,
1441 TransactionOps::Writes(_) => true,
1442 TransactionOps::SingleStatement { .. } => false,
1443 TransactionOps::DDL { .. } => false,
1444 }
1445 }
1446 TransactionStatus::Default | TransactionStatus::Failed(_) => {
1447 unreachable!()
1448 }
1449 }
1450 }
1451
1452 pub fn add_ops(&mut self, mut add_ops: TransactionOps) -> Result<(), AdapterError> {
1465 if matches!(add_ops, TransactionOps::DDL { .. })
1466 && self.inner().is_some_and(|txn| txn.contains_explain)
1467 {
1468 return Err(AdapterError::DDLOnlyTransaction);
1469 }
1470 match self {
1471 TransactionStatus::Started(Transaction {
1472 ops,
1473 access,
1474 pending_ddl_inspections,
1475 ..
1476 })
1477 | TransactionStatus::InTransaction(Transaction {
1478 ops,
1479 access,
1480 pending_ddl_inspections,
1481 ..
1482 })
1483 | TransactionStatus::InTransactionImplicit(Transaction {
1484 ops,
1485 access,
1486 pending_ddl_inspections,
1487 ..
1488 }) => {
1489 match ops {
1490 TransactionOps::None => {
1491 if matches!(access, Some(TransactionAccessMode::ReadOnly))
1492 && matches!(add_ops, TransactionOps::Writes(_))
1493 {
1494 return Err(AdapterError::ReadOnlyTransaction);
1495 }
1496 match &mut add_ops {
1497 TransactionOps::DDL { commands, .. } => {
1498 commands.splice(
1501 0..0,
1502 pending_ddl_inspections
1503 .drain(..)
1504 .map(DdlInspection::into_command),
1505 );
1506 }
1507 TransactionOps::None => {}
1508 TransactionOps::Peeks { .. }
1509 | TransactionOps::Writes(_)
1510 | TransactionOps::Subscribe
1511 | TransactionOps::SingleStatement { .. } => {
1512 pending_ddl_inspections.clear();
1513 }
1514 }
1515 *ops = add_ops;
1516 }
1517 TransactionOps::Peeks {
1518 determination,
1519 cluster_id,
1520 requires_linearization,
1521 } => match add_ops {
1522 TransactionOps::Peeks {
1523 determination: add_timestamp_determination,
1524 cluster_id: add_cluster_id,
1525 requires_linearization: add_requires_linearization,
1526 } => {
1527 assert_eq!(*cluster_id, add_cluster_id);
1528 match (
1529 &determination.timestamp_context,
1530 &add_timestamp_determination.timestamp_context,
1531 ) {
1532 (
1533 TimestampContext::TimelineTimestamp {
1534 timeline: txn_timeline,
1535 chosen_ts: txn_ts,
1536 oracle_ts: _,
1537 },
1538 TimestampContext::TimelineTimestamp {
1539 timeline: add_timeline,
1540 chosen_ts: add_ts,
1541 oracle_ts: _,
1542 },
1543 ) => {
1544 assert_eq!(txn_timeline, add_timeline);
1545 assert_eq!(txn_ts, add_ts);
1546 }
1547 (TimestampContext::NoTimestamp, _) => {
1548 *determination = add_timestamp_determination
1549 }
1550 (_, TimestampContext::NoTimestamp) => {}
1551 };
1552 if matches!(requires_linearization, RequireLinearization::NotRequired)
1553 && matches!(
1554 add_requires_linearization,
1555 RequireLinearization::Required
1556 )
1557 {
1558 *requires_linearization = add_requires_linearization;
1559 }
1560 }
1561 writes @ TransactionOps::Writes(..)
1570 if !determination.timestamp_context.contains_timestamp() =>
1571 {
1572 if matches!(access, Some(TransactionAccessMode::ReadOnly)) {
1573 return Err(AdapterError::ReadOnlyTransaction);
1574 }
1575 *ops = writes;
1576 }
1577 _ => return Err(AdapterError::ReadOnlyTransaction),
1578 },
1579 TransactionOps::Subscribe => {
1580 return Err(AdapterError::SubscribeOnlyTransaction);
1581 }
1582 TransactionOps::Writes(txn_writes) => match add_ops {
1583 TransactionOps::Writes(mut add_writes) => {
1584 assert!(!matches!(access, Some(TransactionAccessMode::ReadOnly)));
1587 txn_writes.append(&mut add_writes);
1588 }
1589 TransactionOps::Peeks { determination, .. }
1592 if !determination.timestamp_context.contains_timestamp() => {}
1593 _ => {
1594 return Err(AdapterError::WriteOnlyTransaction);
1595 }
1596 },
1597 TransactionOps::SingleStatement { .. } => {
1598 return Err(AdapterError::SingleStatementTransaction);
1599 }
1600 TransactionOps::DDL {
1601 commands,
1602 state,
1603 snapshot,
1604 } => match add_ops {
1605 TransactionOps::DDL {
1606 commands: new_commands,
1607 state: new_state,
1608 snapshot: new_snapshot,
1609 } => {
1610 if !new_commands.is_empty() {
1611 commands.extend(new_commands);
1612 *state = new_state;
1613 *snapshot = new_snapshot;
1614 }
1615 }
1616 _ => return Err(AdapterError::DDLOnlyTransaction),
1617 },
1618 }
1619 }
1620 TransactionStatus::Default | TransactionStatus::Failed(_) => {
1621 unreachable!()
1622 }
1623 }
1624 Ok(())
1625 }
1626}
1627
1628pub type TransactionId = u64;
1630
1631impl Default for TransactionStatus {
1632 fn default() -> Self {
1633 TransactionStatus::Default
1634 }
1635}
1636
1637#[derive(Debug)]
1644enum DdlInspection {
1645 CatalogResult(Arc<crate::catalog::command::CatalogResultCommand>),
1646 Description(Arc<crate::catalog::command::DescriptionCommand>),
1647}
1648
1649impl DdlInspection {
1650 fn into_command(self) -> crate::catalog::command::CatalogCommand {
1651 match self {
1652 Self::CatalogResult(command) => {
1653 crate::catalog::command::CatalogCommand::CatalogResult(command)
1654 }
1655 Self::Description(command) => {
1656 crate::catalog::command::CatalogCommand::Description(command)
1657 }
1658 }
1659 }
1660}
1661
1662#[derive(Debug)]
1664pub struct Transaction {
1665 completion_owner: TransactionCompletionOwner,
1666 pub pcx: PlanContext,
1668 pub ops: TransactionOps,
1670 pending_ddl_inspections: Vec<DdlInspection>,
1672 contains_explain: bool,
1673 pub id: TransactionId,
1678 access: Option<TransactionAccessMode>,
1680}
1681
1682#[derive(Debug, PartialEq)]
1683enum TransactionCompletionOwner {
1684 Session,
1685 Coordinator,
1686}
1687
1688pub(crate) struct ReadOnlyCompletion {
1690 pub action: EndTransactionAction,
1691 pub timestamp: Option<TimestampContext>,
1692}
1693
1694impl Transaction {
1695 fn timeline(&self) -> Option<Timeline> {
1697 match &self.ops {
1698 TransactionOps::Peeks {
1699 determination:
1700 TimestampDetermination {
1701 timestamp_context: TimestampContext::TimelineTimestamp { timeline, .. },
1702 ..
1703 },
1704 ..
1705 } => Some(timeline.clone()),
1706 TransactionOps::Peeks { .. }
1707 | TransactionOps::None
1708 | TransactionOps::Subscribe
1709 | TransactionOps::Writes(_)
1710 | TransactionOps::SingleStatement { .. }
1711 | TransactionOps::DDL { .. } => None,
1712 }
1713 }
1714
1715 pub fn cluster(&self) -> Option<ClusterId> {
1717 match &self.ops {
1718 TransactionOps::Peeks { cluster_id, .. } => Some(cluster_id.clone()),
1719 TransactionOps::None
1720 | TransactionOps::Subscribe
1721 | TransactionOps::Writes(_)
1722 | TransactionOps::SingleStatement { .. }
1723 | TransactionOps::DDL { .. } => None,
1724 }
1725 }
1726
1727 fn contains_ops(&self) -> bool {
1729 !matches!(self.ops, TransactionOps::None)
1730 }
1731}
1732
1733#[derive(Debug, Clone, Copy)]
1735pub enum TransactionCode {
1736 Idle,
1738 InTransaction,
1740 Failed,
1742}
1743
1744impl From<TransactionCode> for u8 {
1745 fn from(code: TransactionCode) -> Self {
1746 match code {
1747 TransactionCode::Idle => b'I',
1748 TransactionCode::InTransaction => b'T',
1749 TransactionCode::Failed => b'E',
1750 }
1751 }
1752}
1753
1754impl From<TransactionCode> for String {
1755 fn from(code: TransactionCode) -> Self {
1756 char::from(u8::from(code)).to_string()
1757 }
1758}
1759
1760impl From<&TransactionStatus> for TransactionCode {
1761 fn from(status: &TransactionStatus) -> TransactionCode {
1763 match status {
1764 TransactionStatus::Default => TransactionCode::Idle,
1765 TransactionStatus::Started(_) => TransactionCode::InTransaction,
1766 TransactionStatus::InTransaction(_) => TransactionCode::InTransaction,
1767 TransactionStatus::InTransactionImplicit(_) => TransactionCode::InTransaction,
1768 TransactionStatus::Failed(_) => TransactionCode::Failed,
1769 }
1770 }
1771}
1772
1773#[derive(Derivative)]
1779#[derivative(Debug)]
1780pub enum TransactionOps {
1781 None,
1784 Peeks {
1789 determination: TimestampDetermination,
1791 cluster_id: ClusterId,
1793 requires_linearization: RequireLinearization,
1795 },
1796 Subscribe,
1798 Writes(Vec<WriteOp>),
1801 SingleStatement {
1803 stmt: Arc<Statement<Raw>>,
1805 params: mz_sql::plan::Params,
1807 },
1808 DDL {
1812 commands: Vec<crate::catalog::command::CatalogCommand>,
1814 state: CatalogState,
1816 snapshot: Snapshot,
1818 },
1819}
1820
1821impl TransactionOps {
1822 pub fn stages_work(&self) -> bool {
1826 matches!(
1827 self,
1828 TransactionOps::Writes(_)
1829 | TransactionOps::DDL { .. }
1830 | TransactionOps::SingleStatement { .. }
1831 )
1832 }
1833}
1834
1835impl Default for TransactionOps {
1836 fn default() -> Self {
1837 Self::None
1838 }
1839}
1840
1841#[derive(Debug, Clone, PartialEq)]
1843pub struct WriteOp {
1844 pub id: CatalogItemId,
1846 pub rows: TableData,
1848}
1849
1850#[derive(Debug)]
1852pub enum RequireLinearization {
1853 Required,
1855 NotRequired,
1857}
1858
1859impl From<&ExplainContext> for RequireLinearization {
1860 fn from(ctx: &ExplainContext) -> Self {
1861 match ctx {
1862 ExplainContext::None | ExplainContext::PlanInsightsNotice(_) => {
1863 RequireLinearization::Required
1864 }
1865 _ => RequireLinearization::NotRequired,
1866 }
1867 }
1868}
1869#[cfg(test)]
1870mod tests {
1871 use super::*;
1872 use mz_sql::session::vars::{SystemVars, VarInput};
1873 use timely::progress::Antichain;
1874
1875 fn read_session() -> Session {
1876 let mut session = Session::dummy();
1877 session.start_transaction_single_stmt(Utc::now());
1878 session
1879 .add_transaction_ops(TransactionOps::Peeks {
1880 determination: TimestampDetermination {
1881 timestamp_context: TimestampContext::TimelineTimestamp {
1882 timeline: Timeline::EpochMilliseconds,
1883 chosen_ts: 10.into(),
1884 oracle_ts: Some(10.into()),
1885 },
1886 since: Antichain::from_elem(0.into()),
1887 upper: Antichain::new(),
1888 largest_not_in_advance_of_upper: Timestamp::MAX,
1889 oracle_read_ts: Some(10.into()),
1890 session_oracle_read_ts: None,
1891 real_time_recency_ts: None,
1892 constraints: Default::default(),
1893 },
1894 cluster_id: ClusterId::User(1),
1895 requires_linearization: RequireLinearization::Required,
1896 })
1897 .expect("read transaction");
1898 session
1899 }
1900
1901 #[mz_ore::test]
1902 fn read_completion_tracks_ownership_not_just_ops() {
1903 let mut session = read_session();
1904 let completion = session
1905 .read_only_completion(EndTransactionAction::Commit)
1906 .expect("local read");
1907 assert_eq!(completion.action, EndTransactionAction::Commit);
1908 assert!(completion.timestamp.is_some());
1909 session.require_coordinator_completion();
1910 assert!(
1911 session
1912 .read_only_completion(EndTransactionAction::Commit)
1913 .is_none()
1914 );
1915 let _ = session.clear_transaction();
1916 session.start_transaction_single_stmt(Utc::now());
1917 assert!(
1918 session
1919 .read_only_completion(EndTransactionAction::Commit)
1920 .is_some()
1921 );
1922 }
1923
1924 #[mz_ore::test]
1925 fn completion_skips_timestamp_for_failed_and_rolled_back_reads() {
1926 let session = read_session();
1927 let rollback = session
1928 .read_only_completion(EndTransactionAction::Rollback)
1929 .expect("local read");
1930 assert!(rollback.timestamp.is_none());
1931 let session = session.fail_transaction();
1932 let failed = session
1933 .read_only_completion(EndTransactionAction::Commit)
1934 .expect("failed local read");
1935 assert_eq!(failed.action, EndTransactionAction::Rollback);
1936 assert!(failed.timestamp.is_none());
1937 }
1938
1939 #[mz_ore::test]
1940 fn completion_rejects_resource_bearing_transactions() {
1941 for ops in [
1942 TransactionOps::Subscribe,
1943 TransactionOps::Writes(Vec::new()),
1944 ] {
1945 let mut session = Session::dummy();
1946 session.start_transaction_single_stmt(Utc::now());
1947 session.add_transaction_ops(ops).expect("first operation");
1948 assert!(
1949 session
1950 .read_only_completion(EndTransactionAction::Commit)
1951 .is_none()
1952 );
1953 assert!(
1954 session
1955 .fail_transaction()
1956 .read_only_completion(EndTransactionAction::Rollback)
1957 .is_none()
1958 );
1959 }
1960 }
1961
1962 #[mz_ore::test]
1963 fn read_completion_preserves_transactional_parameter_changes() {
1964 for action in [EndTransactionAction::Commit, EndTransactionAction::Rollback] {
1965 let mut session = read_session();
1966 session
1967 .vars_mut()
1968 .set_default("application_name", VarInput::Flat("session"))
1969 .expect("default");
1970 session
1971 .vars_mut()
1972 .set(
1973 &SystemVars::new(),
1974 "application_name",
1975 VarInput::Flat("local"),
1976 true,
1977 )
1978 .expect("local setting");
1979 let completion = session.read_only_completion(action).expect("local read");
1980 let _ = session.clear_transaction();
1981 let params = session.vars_mut().end_transaction(completion.action);
1982 assert_eq!(
1983 params.get("application_name").map(String::as_str),
1984 Some("session")
1985 );
1986 assert_eq!(session.vars().application_name(), "session");
1987 assert!(matches!(session.transaction(), TransactionStatus::Default));
1988 }
1989 }
1990
1991 #[mz_ore::test]
1992 fn ddl_inspections_are_released_when_ddl_becomes_impossible() {
1993 use mz_sql::plan::{Params, StatementDesc};
1994 use timely::progress::Antichain;
1995
1996 let statement = mz_sql_parser::parser::parse_statements("SELECT 1")
1997 .expect("valid SQL")
1998 .remove(0)
1999 .ast;
2000 let modes = [
2001 TransactionOps::Peeks {
2002 determination: TimestampDetermination {
2003 timestamp_context: TimestampContext::NoTimestamp,
2004 since: Antichain::new(),
2005 upper: Antichain::new(),
2006 largest_not_in_advance_of_upper: mz_repr::Timestamp::MIN,
2007 oracle_read_ts: None,
2008 session_oracle_read_ts: None,
2009 real_time_recency_ts: None,
2010 constraints: Default::default(),
2011 },
2012 cluster_id: mz_controller_types::ClusterId::User(1),
2013 requires_linearization: RequireLinearization::NotRequired,
2014 },
2015 TransactionOps::Writes(vec![]),
2016 TransactionOps::Subscribe,
2017 TransactionOps::SingleStatement {
2018 stmt: Arc::new(statement.clone()),
2019 params: Params::empty(),
2020 },
2021 ];
2022 for mode in modes.into_iter().map(Some).chain([None]) {
2024 let mut session = Session::dummy();
2025 session.start_transaction_implicit(Utc::now(), 2);
2026 let command = Arc::new(crate::catalog::command::DescriptionCommand::new(
2027 statement.clone(),
2028 vec![],
2029 session.meta(),
2030 StatementDesc::new(Some(mz_repr::RelationDesc::empty())),
2031 ));
2032 let retained = Arc::downgrade(&command);
2033 session.retain_ddl_inspection(DdlInspection::Description(command));
2034 assert!(retained.upgrade().is_some(), "DDL is still possible");
2035 if let Some(mode) = mode {
2036 session.add_transaction_ops(mode).expect("valid transition");
2037 } else {
2038 session
2039 .transaction_mut()
2040 .record_explain()
2041 .expect("EXPLAIN allowed");
2042 }
2043 assert!(retained.upgrade().is_none(), "release earlier inspections");
2044 let command = Arc::new(crate::catalog::command::DescriptionCommand::new(
2045 statement.clone(),
2046 vec![],
2047 session.meta(),
2048 StatementDesc::new(Some(mz_repr::RelationDesc::empty())),
2049 ));
2050 let discarded = Arc::downgrade(&command);
2051 session.retain_ddl_inspection(DdlInspection::Description(command));
2052 assert!(
2053 discarded.upgrade().is_none(),
2054 "do not retain later inspections"
2055 );
2056 }
2057 }
2058}