iceberg/catalog/
session.rs1use std::collections::HashMap;
21use std::fmt::Debug;
22
23use async_trait::async_trait;
24#[cfg(test)]
25use mockall::automock;
26use typed_builder::TypedBuilder;
27use uuid::Uuid;
28
29use crate::sensitive::SensitiveString;
30use crate::table::Table;
31use crate::{Namespace, NamespaceIdent, Result, TableCommit, TableCreation, TableIdent};
32
33#[derive(Debug, Clone, TypedBuilder)]
47pub struct SessionContext {
48 #[builder(default=Uuid::new_v4().to_string())]
54 session_id: String,
55
56 #[builder(default, setter(strip_option))]
58 identity: Option<String>,
59
60 #[builder(default)]
61 properties: HashMap<String, String>,
62
63 #[builder(default)]
64 credentials: HashMap<String, SensitiveString>,
65}
66
67impl SessionContext {
68 pub fn empty() -> Self {
70 Self::builder().build()
71 }
72
73 pub fn session_id(&self) -> &str {
77 &self.session_id
78 }
79
80 pub fn identity(&self) -> Option<&str> {
82 self.identity.as_deref()
83 }
84
85 pub fn properties(&self) -> &HashMap<String, String> {
87 &self.properties
88 }
89
90 pub fn credentials(&self) -> &HashMap<String, SensitiveString> {
92 &self.credentials
93 }
94}
95
96#[async_trait]
98#[cfg_attr(test, automock)]
99pub trait SessionCatalog: Debug + Send + Sync {
100 async fn list_namespaces(
102 &self,
103 context: &SessionContext,
104 parent: Option<&NamespaceIdent>,
105 ) -> Result<Vec<NamespaceIdent>>;
106
107 async fn create_namespace(
109 &self,
110 context: &SessionContext,
111 namespace: &NamespaceIdent,
112 properties: HashMap<String, String>,
113 ) -> Result<Namespace>;
114
115 async fn get_namespace(
117 &self,
118 context: &SessionContext,
119 namespace: &NamespaceIdent,
120 ) -> Result<Namespace>;
121
122 async fn namespace_exists(
124 &self,
125 context: &SessionContext,
126 namespace: &NamespaceIdent,
127 ) -> Result<bool>;
128
129 async fn update_namespace(
135 &self,
136 context: &SessionContext,
137 namespace: &NamespaceIdent,
138 properties: HashMap<String, String>,
139 ) -> Result<()>;
140
141 async fn drop_namespace(
143 &self,
144 context: &SessionContext,
145 namespace: &NamespaceIdent,
146 ) -> Result<()>;
147
148 async fn list_tables(
150 &self,
151 context: &SessionContext,
152 namespace: &NamespaceIdent,
153 ) -> Result<Vec<TableIdent>>;
154
155 async fn create_table(
157 &self,
158 context: &SessionContext,
159 namespace: &NamespaceIdent,
160 creation: TableCreation,
161 ) -> Result<Table>;
162
163 async fn load_table(&self, context: &SessionContext, table: &TableIdent) -> Result<Table>;
165
166 async fn drop_table(&self, context: &SessionContext, table: &TableIdent) -> Result<()>;
168
169 async fn purge_table(&self, context: &SessionContext, table: &TableIdent) -> Result<()>;
176
177 async fn table_exists(&self, context: &SessionContext, table: &TableIdent) -> Result<bool>;
179
180 async fn rename_table(
182 &self,
183 context: &SessionContext,
184 src: &TableIdent,
185 dest: &TableIdent,
186 ) -> Result<()>;
187
188 async fn register_table(
190 &self,
191 context: &SessionContext,
192 table: &TableIdent,
193 metadata_location: String,
194 ) -> Result<Table>;
195
196 async fn update_table(&self, context: &SessionContext, commit: TableCommit) -> Result<Table>;
198}
199
200#[cfg(test)]
201mod tests {
202 use std::collections::HashMap;
203
204 use uuid::Uuid;
205
206 use crate::sensitive::SensitiveString;
207 use crate::{SessionCatalog, SessionContext};
208
209 #[test]
210 fn test_empty_session_context_has_uuid_session_id() {
211 let session = SessionContext::empty();
212 let session_id = session.session_id();
213
214 assert!(Uuid::parse_str(session_id).is_ok());
215 }
216
217 #[test]
218 fn test_empty_sessions_get_unique_id() {
219 let session1 = SessionContext::empty();
220 let session2 = SessionContext::empty();
221
222 assert_ne!(session1.session_id(), session2.session_id())
223 }
224
225 #[test]
226 fn test_empty_sessions_get_unique_id_via_builder() {
227 let session1 = SessionContext::builder().build();
228 let session2 = SessionContext::builder().build();
229
230 assert_ne!(session1.session_id(), session2.session_id());
231 }
232
233 #[test]
234 fn test_session_with_credentials_does_not_display_them() {
235 let sensitive_value = "my-pw-123456";
236 let session = SessionContext::builder()
237 .credentials(HashMap::from([(
238 "key".to_string(),
239 SensitiveString::from(sensitive_value.to_string()),
240 )]))
241 .build();
242
243 let logged = format!("{:?}", session);
244 assert!(!logged.contains(sensitive_value))
245 }
246
247 #[test]
248 fn test_types_are_send_sync() {
249 assert_send_sync::<SessionContext>();
250 assert_send_sync::<dyn SessionCatalog>();
251
252 fn _dyn_compatible(_: &dyn SessionCatalog) {}
253 }
254
255 fn assert_send_sync<T: Send + Sync + ?Sized>() {}
256}