mongreldb_protocol/
session.rs1use std::collections::BTreeMap;
13
14use mongreldb_types::hlc::HlcTimestamp;
15use mongreldb_types::ids::{DatabaseId, TransactionId};
16
17use crate::prepared::{PreparedStatementBinding, StatementId};
18use crate::request::{AuthenticatedIdentity, IsolationLevel, SessionId};
19
20#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
22pub enum TransactionState {
23 Idle,
25 Active {
27 transaction_id: TransactionId,
29 isolation: IsolationLevel,
31 },
32}
33
34impl TransactionState {
35 pub fn is_active(&self) -> bool {
37 matches!(self, Self::Active { .. })
38 }
39}
40
41#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
46pub struct Session {
47 pub session_id: SessionId,
49 pub principal: AuthenticatedIdentity,
51 pub current_database: DatabaseId,
53 pub transaction_state: TransactionState,
55 pub prepared_statements: BTreeMap<StatementId, PreparedStatementBinding>,
57 pub settings: BTreeMap<String, String>,
60 pub read_your_writes_token: Option<HlcTimestamp>,
63 pub last_activity_unix_micros: u64,
67}
68
69impl Session {
70 pub fn new(
73 session_id: SessionId,
74 principal: AuthenticatedIdentity,
75 current_database: DatabaseId,
76 now_unix_micros: u64,
77 ) -> Self {
78 Self {
79 session_id,
80 principal,
81 current_database,
82 transaction_state: TransactionState::Idle,
83 prepared_statements: BTreeMap::new(),
84 settings: BTreeMap::new(),
85 read_your_writes_token: None,
86 last_activity_unix_micros: now_unix_micros,
87 }
88 }
89}
90
91#[cfg(test)]
92mod tests {
93 use super::*;
94 use crate::test_support::assert_serde_round_trip;
95
96 fn sample_session() -> Session {
97 let mut session = Session::new(
98 SessionId::from_bytes([0x33; 16]),
99 AuthenticatedIdentity::CatalogUser {
100 username: "alice".to_owned(),
101 user_id: 42,
102 created_version: 7,
103 },
104 DatabaseId::new_random(),
105 1_758_000_000_000_000,
106 );
107 session.transaction_state = TransactionState::Active {
108 transaction_id: TransactionId::new_random(),
109 isolation: IsolationLevel::Snapshot,
110 };
111 session.prepared_statements.insert(
112 StatementId::new(1),
113 PreparedStatementBinding {
114 statement_id: StatementId::new(1),
115 sql: "SELECT 1".to_owned(),
116 parameter_types: vec![],
117 catalog_version: mongreldb_types::ids::MetadataVersion::new(100),
118 schema_versions: BTreeMap::new(),
119 feature_set: std::collections::BTreeSet::new(),
120 },
121 );
122 session
123 .settings
124 .insert("timezone".to_owned(), "UTC".to_owned());
125 session.read_your_writes_token = Some(HlcTimestamp {
126 physical_micros: 1_758_000_000_000_001,
127 logical: 3,
128 node_tiebreaker: 1,
129 });
130 session
131 }
132
133 #[test]
134 fn new_session_starts_idle_and_empty() {
135 let session = Session::new(
136 SessionId::ZERO,
137 AuthenticatedIdentity::Credentialless,
138 DatabaseId::new_random(),
139 1,
140 );
141 assert_eq!(session.transaction_state, TransactionState::Idle);
142 assert!(!session.transaction_state.is_active());
143 assert!(session.prepared_statements.is_empty());
144 assert!(session.settings.is_empty());
145 assert_eq!(session.read_your_writes_token, None);
146 assert_eq!(session.last_activity_unix_micros, 1);
147 }
148
149 #[test]
150 fn transaction_state_serde_round_trip() {
151 assert_serde_round_trip(&TransactionState::Idle);
152 assert_serde_round_trip(&TransactionState::Active {
153 transaction_id: TransactionId::new_random(),
154 isolation: IsolationLevel::Serializable,
155 });
156 }
157
158 #[test]
159 fn session_serde_round_trip() {
160 assert_serde_round_trip(&sample_session());
161 assert_serde_round_trip(&Session::new(
162 SessionId::from_bytes([0x44; 16]),
163 AuthenticatedIdentity::ServicePrincipal {
164 name: "cdc".to_owned(),
165 },
166 DatabaseId::new_random(),
167 42,
168 ));
169 assert_serde_round_trip(&Session::new(
170 SessionId::from_bytes([0x45; 16]),
171 AuthenticatedIdentity::ExternalPrincipal {
172 provider: "oidc".to_owned(),
173 subject: "alice".to_owned(),
174 username: "alice".to_owned(),
175 user_id: 7,
176 created_version: 8,
177 scopes: vec!["query".to_owned()],
178 },
179 DatabaseId::new_random(),
180 43,
181 ));
182 }
183}