1use std::collections::HashMap;
21use std::str::FromStr;
22use std::sync::Arc;
23
24use async_trait::async_trait;
25use futures::lock::{Mutex, MutexGuard};
26use iceberg_property_macro::Properties;
27use itertools::Itertools;
28
29use super::namespace_state::NamespaceState;
30use crate::encryption::kms::{KeyManagementClient, KmsClientFactory};
31use crate::error::invalid_data;
32use crate::io::{FileIO, FileIOBuilder, MemoryStorageFactory, StorageFactory};
33use crate::runtime::Runtime;
34use crate::spec::{TableMetadata, TableMetadataBuilder};
35use crate::table::Table;
36use crate::{
37 Catalog, CatalogBuilder, MetadataLocation, Namespace, NamespaceIdent, Result, TableCommit,
38 TableCreation, TableIdent,
39};
40
41pub const MEMORY_CATALOG_WAREHOUSE: &str = "warehouse";
43
44const LOCATION: &str = "location";
46
47#[derive(Debug, Default)]
49pub struct MemoryCatalogBuilder {
50 storage_factory: Option<Arc<dyn StorageFactory>>,
51 kms_client_factory: Option<Arc<dyn KmsClientFactory>>,
52 runtime: Option<Runtime>,
53}
54
55impl CatalogBuilder for MemoryCatalogBuilder {
56 type C = MemoryCatalog;
57
58 fn with_storage_factory(mut self, storage_factory: Arc<dyn StorageFactory>) -> Self {
59 self.storage_factory = Some(storage_factory);
60 self
61 }
62
63 fn with_kms_client_factory(mut self, kms_client_factory: Arc<dyn KmsClientFactory>) -> Self {
64 self.kms_client_factory = Some(kms_client_factory);
65 self
66 }
67
68 fn with_runtime(mut self, runtime: Runtime) -> Self {
69 self.runtime = Some(runtime);
70 self
71 }
72
73 fn load(
74 self,
75 name: impl Into<String>,
76 props: HashMap<String, String>,
77 ) -> impl Future<Output = Result<Self::C>> + Send {
78 let name = name.into();
79
80 async move {
81 let catalog_properties = MemoryCatalogProperties::from_properties(&props)?;
82 if catalog_properties.warehouse.is_empty() {
83 return Err(invalid_data!("Catalog warehouse is required"));
84 }
85
86 let runtime = self.runtime.unwrap_or_else(Runtime::current);
87 let kms_client = match self.kms_client_factory {
88 Some(factory) => Some(factory.create_kms_client(&props).await?),
89 None => None,
90 };
91 MemoryCatalog::new(
92 name,
93 catalog_properties,
94 props,
95 self.storage_factory,
96 runtime,
97 kms_client,
98 )
99 }
100 }
101}
102
103#[derive(Debug, Properties)]
105pub(crate) struct MemoryCatalogProperties {
106 #[property(key = MEMORY_CATALOG_WAREHOUSE, default = "")]
107 warehouse: String,
108}
109
110pub struct MemoryCatalog {
112 name: String,
113 properties: MemoryCatalogProperties,
114 root_namespace_state: Mutex<NamespaceState>,
115 file_io: FileIO,
116 runtime: Runtime,
117 kms_client: Option<Arc<dyn KeyManagementClient>>,
118}
119
120impl std::fmt::Debug for MemoryCatalog {
121 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
122 f.debug_struct("MemoryCatalog")
123 .field("name", &self.name)
124 .field("properties", &self.properties)
125 .finish_non_exhaustive()
126 }
127}
128
129impl MemoryCatalog {
130 fn new(
132 name: String,
133 properties: MemoryCatalogProperties,
134 props: HashMap<String, String>,
135 storage_factory: Option<Arc<dyn StorageFactory>>,
136 runtime: Runtime,
137 kms_client: Option<Arc<dyn KeyManagementClient>>,
138 ) -> Result<Self> {
139 let factory = storage_factory.unwrap_or_else(|| Arc::new(MemoryStorageFactory));
141
142 Ok(Self {
143 name,
144 properties,
145 file_io: FileIOBuilder::new(factory).with_props(props).build(),
146 root_namespace_state: Mutex::new(NamespaceState::default()),
147 runtime,
148 kms_client,
149 })
150 }
151
152 async fn load_table_from_locked_state(
154 &self,
155 table_ident: &TableIdent,
156 root_namespace_state: &MutexGuard<'_, NamespaceState>,
157 ) -> Result<Table> {
158 let metadata_location = root_namespace_state.get_existing_table_location(table_ident)?;
159 let metadata = TableMetadata::read_from(&self.file_io, metadata_location).await?;
160
161 let mut builder = Table::builder()
162 .identifier(table_ident.clone())
163 .metadata(metadata)
164 .metadata_location(metadata_location.to_string())
165 .file_io(self.file_io.clone())
166 .runtime(self.runtime.clone());
167 if let Some(kms_client) = self.kms_client.clone() {
168 builder = builder.kms_client(kms_client);
169 }
170 builder.build()
171 }
172}
173
174#[async_trait]
175impl Catalog for MemoryCatalog {
176 async fn list_namespaces(
178 &self,
179 maybe_parent: Option<&NamespaceIdent>,
180 ) -> Result<Vec<NamespaceIdent>> {
181 let root_namespace_state = self.root_namespace_state.lock().await;
182
183 match maybe_parent {
184 None => {
185 let namespaces = root_namespace_state
186 .list_top_level_namespaces()
187 .into_iter()
188 .map(|str| NamespaceIdent::new(str.to_string()))
189 .collect_vec();
190
191 Ok(namespaces)
192 }
193 Some(parent_namespace_ident) => {
194 let namespaces = root_namespace_state
195 .list_namespaces_under(parent_namespace_ident)?
196 .into_iter()
197 .map(|name| {
198 let mut names = parent_namespace_ident.iter().cloned().collect::<Vec<_>>();
199 names.push(name.to_string());
200 NamespaceIdent::from_vec(names)
201 })
202 .collect::<Result<Vec<_>>>()?;
203
204 Ok(namespaces)
205 }
206 }
207 }
208
209 async fn create_namespace(
211 &self,
212 namespace_ident: &NamespaceIdent,
213 properties: HashMap<String, String>,
214 ) -> Result<Namespace> {
215 let mut root_namespace_state = self.root_namespace_state.lock().await;
216
217 root_namespace_state.insert_new_namespace(namespace_ident, properties.clone())?;
218 let namespace = Namespace::with_properties(namespace_ident.clone(), properties);
219
220 Ok(namespace)
221 }
222
223 async fn get_namespace(&self, namespace_ident: &NamespaceIdent) -> Result<Namespace> {
225 let root_namespace_state = self.root_namespace_state.lock().await;
226
227 let namespace = Namespace::with_properties(
228 namespace_ident.clone(),
229 root_namespace_state
230 .get_properties(namespace_ident)?
231 .clone(),
232 );
233
234 Ok(namespace)
235 }
236
237 async fn namespace_exists(&self, namespace_ident: &NamespaceIdent) -> Result<bool> {
239 let guarded_namespaces = self.root_namespace_state.lock().await;
240
241 Ok(guarded_namespaces.namespace_exists(namespace_ident))
242 }
243
244 async fn update_namespace(
250 &self,
251 namespace_ident: &NamespaceIdent,
252 properties: HashMap<String, String>,
253 ) -> Result<()> {
254 let mut root_namespace_state = self.root_namespace_state.lock().await;
255
256 root_namespace_state.replace_properties(namespace_ident, properties)
257 }
258
259 async fn drop_namespace(&self, namespace_ident: &NamespaceIdent) -> Result<()> {
261 let mut root_namespace_state = self.root_namespace_state.lock().await;
262
263 root_namespace_state.remove_existing_namespace(namespace_ident)
264 }
265
266 async fn list_tables(&self, namespace_ident: &NamespaceIdent) -> Result<Vec<TableIdent>> {
268 let root_namespace_state = self.root_namespace_state.lock().await;
269
270 let table_names = root_namespace_state.list_tables(namespace_ident)?;
271 let table_idents = table_names
272 .into_iter()
273 .map(|table_name| TableIdent::new(namespace_ident.clone(), table_name.clone()))
274 .collect_vec();
275
276 Ok(table_idents)
277 }
278
279 async fn create_table(
281 &self,
282 namespace_ident: &NamespaceIdent,
283 table_creation: TableCreation,
284 ) -> Result<Table> {
285 let mut root_namespace_state = self.root_namespace_state.lock().await;
286
287 let table_name = table_creation.name.clone();
288 let table_ident = TableIdent::new(namespace_ident.clone(), table_name);
289
290 let table_creation = if table_creation.location.is_some() {
291 table_creation
292 } else {
293 let namespace_properties = root_namespace_state.get_properties(namespace_ident)?;
294 let location_prefix = match namespace_properties.get(LOCATION) {
295 Some(namespace_location) => namespace_location.clone(),
296 None => format!(
297 "{}/{}",
298 self.properties.warehouse,
299 namespace_ident.join("/")
300 ),
301 };
302
303 let location = format!("{}/{}", location_prefix, table_ident.name());
304
305 TableCreation {
306 location: Some(location),
307 ..table_creation
308 }
309 };
310
311 let metadata = TableMetadataBuilder::from_table_creation(table_creation)?
312 .build()?
313 .metadata;
314 let metadata_location = MetadataLocation::try_new_with_metadata(&metadata)?;
315
316 metadata.write_to(&self.file_io, &metadata_location).await?;
317
318 root_namespace_state.insert_new_table(&table_ident, metadata_location.to_string())?;
319
320 let mut builder = Table::builder()
321 .file_io(self.file_io.clone())
322 .metadata_location(metadata_location.to_string())
323 .metadata(metadata)
324 .identifier(table_ident)
325 .runtime(self.runtime.clone());
326 if let Some(kms_client) = self.kms_client.clone() {
327 builder = builder.kms_client(kms_client);
328 }
329 builder.build()
330 }
331
332 async fn load_table(&self, table_ident: &TableIdent) -> Result<Table> {
334 let root_namespace_state = self.root_namespace_state.lock().await;
335
336 self.load_table_from_locked_state(table_ident, &root_namespace_state)
337 .await
338 }
339
340 async fn drop_table(&self, table_ident: &TableIdent) -> Result<()> {
342 let mut root_namespace_state = self.root_namespace_state.lock().await;
343
344 root_namespace_state.remove_existing_table(table_ident)?;
345 Ok(())
346 }
347
348 async fn purge_table(&self, table_ident: &TableIdent) -> Result<()> {
349 let table_info = self.load_table(table_ident).await?;
350 self.drop_table(table_ident).await?;
351 crate::catalog::utils::drop_table_data(&table_info).await
352 }
353
354 async fn table_exists(&self, table_ident: &TableIdent) -> Result<bool> {
356 let root_namespace_state = self.root_namespace_state.lock().await;
357
358 root_namespace_state.table_exists(table_ident)
359 }
360
361 async fn rename_table(
363 &self,
364 src_table_ident: &TableIdent,
365 dst_table_ident: &TableIdent,
366 ) -> Result<()> {
367 let mut root_namespace_state = self.root_namespace_state.lock().await;
368
369 let mut new_root_namespace_state = root_namespace_state.clone();
370 let metadata_location = new_root_namespace_state
371 .get_existing_table_location(src_table_ident)?
372 .clone();
373 new_root_namespace_state.remove_existing_table(src_table_ident)?;
374 new_root_namespace_state.insert_new_table(dst_table_ident, metadata_location)?;
375 *root_namespace_state = new_root_namespace_state;
376
377 Ok(())
378 }
379
380 async fn register_table(
381 &self,
382 table_ident: &TableIdent,
383 metadata_location: String,
384 ) -> Result<Table> {
385 let mut root_namespace_state = self.root_namespace_state.lock().await;
386 root_namespace_state.insert_new_table(&table_ident.clone(), metadata_location.clone())?;
387
388 let metadata = TableMetadata::read_from(&self.file_io, &metadata_location).await?;
389
390 let mut builder = Table::builder()
391 .file_io(self.file_io.clone())
392 .metadata_location(metadata_location)
393 .metadata(metadata)
394 .identifier(table_ident.clone())
395 .runtime(self.runtime.clone());
396 if let Some(kms_client) = self.kms_client.clone() {
397 builder = builder.kms_client(kms_client);
398 }
399 builder.build()
400 }
401
402 async fn update_table(&self, commit: TableCommit) -> Result<Table> {
404 let mut root_namespace_state = self.root_namespace_state.lock().await;
405
406 let current_table = self
407 .load_table_from_locked_state(commit.identifier(), &root_namespace_state)
408 .await?;
409
410 let staged_table = commit.apply(current_table)?;
412
413 let metadata_location =
415 MetadataLocation::from_str(staged_table.metadata_location_result()?)?;
416 staged_table
417 .metadata()
418 .write_to(staged_table.file_io(), &metadata_location)
419 .await?;
420
421 let updated_table = root_namespace_state.commit_table_update(staged_table)?;
423
424 Ok(updated_table)
425 }
426}
427
428#[cfg(test)]
429pub(crate) mod tests {
430 use std::collections::HashSet;
431 use std::hash::Hash;
432 use std::iter::FromIterator;
433 use std::vec;
434
435 use regex::Regex;
436 use tempfile::TempDir;
437
438 use super::*;
439 use crate::ErrorKind;
440 use crate::encryption::kms::MemoryKmsClientFactory;
441 use crate::io::{FileIO, LocalFsStorageFactory};
442 use crate::spec::{NestedField, PartitionSpec, PrimitiveType, Schema, SortOrder, Type};
443 use crate::test_utils::test_runtime;
444 use crate::transaction::{ApplyTransactionAction, Transaction};
445
446 fn temp_path() -> String {
447 let temp_dir = TempDir::new().unwrap();
448 temp_dir.path().to_str().unwrap().to_string()
449 }
450
451 #[test]
452 fn test_catalog_properties() {
453 let properties = MemoryCatalogProperties::from_properties(&HashMap::from([(
454 MEMORY_CATALOG_WAREHOUSE.to_string(),
455 "memory:///warehouse".to_string(),
456 )]))
457 .unwrap();
458
459 assert_eq!(properties.warehouse, "memory:///warehouse");
460 }
461
462 #[test]
463 fn test_catalog_properties_warehouse_defaults_to_empty() {
464 let missing = MemoryCatalogProperties::from_properties(&HashMap::new()).unwrap();
465 let explicitly_empty = MemoryCatalogProperties::from_properties(&HashMap::from([(
466 MEMORY_CATALOG_WAREHOUSE.to_string(),
467 String::new(),
468 )]))
469 .unwrap();
470
471 assert_eq!(missing.warehouse, "");
472 assert_eq!(explicitly_empty.warehouse, "");
473 }
474
475 #[tokio::test]
476 async fn test_catalog_forwards_properties_to_file_io() {
477 let catalog = MemoryCatalogBuilder::default()
478 .load(
479 "memory",
480 HashMap::from([
481 (
482 MEMORY_CATALOG_WAREHOUSE.to_string(),
483 "memory:///warehouse".to_string(),
484 ),
485 ("custom.property".to_string(), "value".to_string()),
486 ]),
487 )
488 .await
489 .unwrap();
490 let file_io_props = catalog.file_io.config().props();
491
492 assert_eq!(
493 file_io_props.get("custom.property"),
494 Some(&"value".to_string())
495 );
496 assert_eq!(
497 file_io_props.get(MEMORY_CATALOG_WAREHOUSE),
498 Some(&"memory:///warehouse".to_string())
499 );
500 }
501
502 pub(crate) async fn new_memory_catalog() -> impl Catalog {
503 let warehouse_location = temp_path();
504 MemoryCatalogBuilder::default()
505 .load(
506 "memory",
507 HashMap::from([(MEMORY_CATALOG_WAREHOUSE.to_string(), warehouse_location)]),
508 )
509 .await
510 .unwrap()
511 }
512
513 async fn create_namespace<C: Catalog>(catalog: &C, namespace_ident: &NamespaceIdent) {
514 let _ = catalog
515 .create_namespace(namespace_ident, HashMap::new())
516 .await
517 .unwrap();
518 }
519
520 async fn create_namespaces<C: Catalog>(catalog: &C, namespace_idents: &Vec<&NamespaceIdent>) {
521 for namespace_ident in namespace_idents {
522 let _ = create_namespace(catalog, namespace_ident).await;
523 }
524 }
525
526 fn to_set<T: Eq + Hash>(vec: Vec<T>) -> HashSet<T> {
527 HashSet::from_iter(vec)
528 }
529
530 fn simple_table_schema() -> Schema {
531 Schema::builder()
532 .with_fields(vec![
533 NestedField::required(1, "foo", Type::Primitive(PrimitiveType::Int)).into(),
534 ])
535 .build()
536 .unwrap()
537 }
538
539 async fn create_table<C: Catalog>(catalog: &C, table_ident: &TableIdent) -> Table {
540 catalog
541 .create_table(
542 &table_ident.namespace,
543 TableCreation::builder()
544 .name(table_ident.name().into())
545 .schema(simple_table_schema())
546 .build(),
547 )
548 .await
549 .unwrap()
550 }
551
552 async fn create_tables<C: Catalog>(catalog: &C, table_idents: Vec<&TableIdent>) {
553 for table_ident in table_idents {
554 create_table(catalog, table_ident).await;
555 }
556 }
557
558 async fn create_table_with_namespace<C: Catalog>(catalog: &C) -> Table {
559 let namespace_ident = NamespaceIdent::new("abc".into());
560 create_namespace(catalog, &namespace_ident).await;
561
562 let table_ident = TableIdent::new(namespace_ident, "test".to_string());
563 create_table(catalog, &table_ident).await
564 }
565
566 fn assert_table_eq(table: &Table, expected_table_ident: &TableIdent, expected_schema: &Schema) {
567 assert_eq!(table.identifier(), expected_table_ident);
568
569 let metadata = table.metadata();
570
571 assert_eq!(metadata.current_schema().as_ref(), expected_schema);
572
573 let expected_partition_spec = PartitionSpec::builder((*expected_schema).clone())
574 .with_spec_id(0)
575 .build()
576 .unwrap();
577
578 assert_eq!(
579 metadata
580 .partition_specs_iter()
581 .map(|p| p.as_ref())
582 .collect_vec(),
583 vec![&expected_partition_spec]
584 );
585
586 let expected_sorted_order = SortOrder::builder()
587 .with_order_id(0)
588 .with_fields(vec![])
589 .build(expected_schema)
590 .unwrap();
591
592 assert_eq!(
593 metadata
594 .sort_orders_iter()
595 .map(|s| s.as_ref())
596 .collect_vec(),
597 vec![&expected_sorted_order]
598 );
599
600 assert_eq!(metadata.properties(), &HashMap::new());
601
602 assert!(!table.readonly());
603 }
604
605 const UUID_REGEX_STR: &str = "[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}";
606
607 fn assert_table_metadata_location_matches(table: &Table, regex_str: &str) {
608 let actual = table.metadata_location().unwrap().to_string();
609 let regex = Regex::new(regex_str).unwrap();
610 assert!(
611 regex.is_match(&actual),
612 "Expected metadata location to match regex, but got location: {actual} and regex: {regex}"
613 )
614 }
615
616 #[tokio::test]
617 async fn test_list_namespaces_returns_empty_vector() {
618 let catalog = new_memory_catalog().await;
619
620 assert_eq!(catalog.list_namespaces(None).await.unwrap(), vec![]);
621 }
622
623 #[tokio::test]
624 async fn test_list_namespaces_returns_single_namespace() {
625 let catalog = new_memory_catalog().await;
626 let namespace_ident = NamespaceIdent::new("abc".into());
627 create_namespace(&catalog, &namespace_ident).await;
628
629 assert_eq!(catalog.list_namespaces(None).await.unwrap(), vec![
630 namespace_ident
631 ]);
632 }
633
634 #[tokio::test]
635 async fn test_list_namespaces_returns_multiple_namespaces() {
636 let catalog = new_memory_catalog().await;
637 let namespace_ident_1 = NamespaceIdent::new("a".into());
638 let namespace_ident_2 = NamespaceIdent::new("b".into());
639 create_namespaces(&catalog, &vec![&namespace_ident_1, &namespace_ident_2]).await;
640
641 assert_eq!(
642 to_set(catalog.list_namespaces(None).await.unwrap()),
643 to_set(vec![namespace_ident_1, namespace_ident_2])
644 );
645 }
646
647 #[tokio::test]
648 async fn test_list_namespaces_returns_only_top_level_namespaces() {
649 let catalog = new_memory_catalog().await;
650 let namespace_ident_1 = NamespaceIdent::new("a".into());
651 let namespace_ident_2 = NamespaceIdent::from_strs(vec!["a", "b"]).unwrap();
652 let namespace_ident_3 = NamespaceIdent::new("b".into());
653 create_namespaces(&catalog, &vec![
654 &namespace_ident_1,
655 &namespace_ident_2,
656 &namespace_ident_3,
657 ])
658 .await;
659
660 assert_eq!(
661 to_set(catalog.list_namespaces(None).await.unwrap()),
662 to_set(vec![namespace_ident_1, namespace_ident_3])
663 );
664 }
665
666 #[tokio::test]
667 async fn test_list_namespaces_returns_no_namespaces_under_parent() {
668 let catalog = new_memory_catalog().await;
669 let namespace_ident_1 = NamespaceIdent::new("a".into());
670 let namespace_ident_2 = NamespaceIdent::new("b".into());
671 create_namespaces(&catalog, &vec![&namespace_ident_1, &namespace_ident_2]).await;
672
673 assert_eq!(
674 catalog
675 .list_namespaces(Some(&namespace_ident_1))
676 .await
677 .unwrap(),
678 vec![]
679 );
680 }
681
682 #[tokio::test]
683 async fn test_list_namespaces_returns_namespace_under_parent() {
684 let catalog = new_memory_catalog().await;
685 let namespace_ident_1 = NamespaceIdent::new("a".into());
686 let namespace_ident_2 = NamespaceIdent::from_strs(vec!["a", "b"]).unwrap();
687 let namespace_ident_3 = NamespaceIdent::new("c".into());
688 create_namespaces(&catalog, &vec![
689 &namespace_ident_1,
690 &namespace_ident_2,
691 &namespace_ident_3,
692 ])
693 .await;
694
695 assert_eq!(
696 to_set(catalog.list_namespaces(None).await.unwrap()),
697 to_set(vec![namespace_ident_1.clone(), namespace_ident_3])
698 );
699
700 assert_eq!(
701 catalog
702 .list_namespaces(Some(&namespace_ident_1))
703 .await
704 .unwrap(),
705 vec![namespace_ident_2]
706 );
707 }
708
709 #[tokio::test]
710 async fn test_list_namespaces_returns_multiple_namespaces_under_parent() {
711 let catalog = new_memory_catalog().await;
712 let namespace_ident_1 = NamespaceIdent::new("a".to_string());
713 let namespace_ident_2 = NamespaceIdent::from_strs(vec!["a", "a"]).unwrap();
714 let namespace_ident_3 = NamespaceIdent::from_strs(vec!["a", "b"]).unwrap();
715 let namespace_ident_4 = NamespaceIdent::from_strs(vec!["a", "c"]).unwrap();
716 let namespace_ident_5 = NamespaceIdent::new("b".into());
717 create_namespaces(&catalog, &vec![
718 &namespace_ident_1,
719 &namespace_ident_2,
720 &namespace_ident_3,
721 &namespace_ident_4,
722 &namespace_ident_5,
723 ])
724 .await;
725
726 assert_eq!(
727 to_set(
728 catalog
729 .list_namespaces(Some(&namespace_ident_1))
730 .await
731 .unwrap()
732 ),
733 to_set(vec![
734 namespace_ident_2,
735 namespace_ident_3,
736 namespace_ident_4,
737 ])
738 );
739 }
740
741 #[tokio::test]
742 async fn test_namespace_exists_returns_false() {
743 let catalog = new_memory_catalog().await;
744 let namespace_ident = NamespaceIdent::new("a".into());
745 create_namespace(&catalog, &namespace_ident).await;
746
747 assert!(
748 !catalog
749 .namespace_exists(&NamespaceIdent::new("b".into()))
750 .await
751 .unwrap()
752 );
753 }
754
755 #[tokio::test]
756 async fn test_namespace_exists_returns_true() {
757 let catalog = new_memory_catalog().await;
758 let namespace_ident = NamespaceIdent::new("a".into());
759 create_namespace(&catalog, &namespace_ident).await;
760
761 assert!(catalog.namespace_exists(&namespace_ident).await.unwrap());
762 }
763
764 #[tokio::test]
765 async fn test_create_namespace_with_empty_properties() {
766 let catalog = new_memory_catalog().await;
767 let namespace_ident = NamespaceIdent::new("a".into());
768
769 assert_eq!(
770 catalog
771 .create_namespace(&namespace_ident, HashMap::new())
772 .await
773 .unwrap(),
774 Namespace::new(namespace_ident.clone())
775 );
776
777 assert_eq!(
778 catalog.get_namespace(&namespace_ident).await.unwrap(),
779 Namespace::with_properties(namespace_ident, HashMap::new())
780 );
781 }
782
783 #[tokio::test]
784 async fn test_create_namespace_with_properties() {
785 let catalog = new_memory_catalog().await;
786 let namespace_ident = NamespaceIdent::new("abc".into());
787
788 let mut properties: HashMap<String, String> = HashMap::new();
789 properties.insert("k".into(), "v".into());
790
791 assert_eq!(
792 catalog
793 .create_namespace(&namespace_ident, properties.clone())
794 .await
795 .unwrap(),
796 Namespace::with_properties(namespace_ident.clone(), properties.clone())
797 );
798
799 assert_eq!(
800 catalog.get_namespace(&namespace_ident).await.unwrap(),
801 Namespace::with_properties(namespace_ident, properties)
802 );
803 }
804
805 #[tokio::test]
806 async fn test_create_namespace_throws_error_if_namespace_already_exists() {
807 let catalog = new_memory_catalog().await;
808 let namespace_ident = NamespaceIdent::new("a".into());
809 create_namespace(&catalog, &namespace_ident).await;
810
811 assert_eq!(
812 catalog
813 .create_namespace(&namespace_ident, HashMap::new())
814 .await
815 .unwrap_err()
816 .to_string(),
817 format!(
818 "NamespaceAlreadyExists => Cannot create namespace {:?}. Namespace already exists.",
819 &namespace_ident
820 )
821 );
822
823 assert_eq!(
824 catalog.get_namespace(&namespace_ident).await.unwrap(),
825 Namespace::with_properties(namespace_ident, HashMap::new())
826 );
827 }
828
829 #[tokio::test]
830 async fn test_create_nested_namespace() {
831 let catalog = new_memory_catalog().await;
832 let parent_namespace_ident = NamespaceIdent::new("a".into());
833 create_namespace(&catalog, &parent_namespace_ident).await;
834
835 let child_namespace_ident = NamespaceIdent::from_strs(vec!["a", "b"]).unwrap();
836
837 assert_eq!(
838 catalog
839 .create_namespace(&child_namespace_ident, HashMap::new())
840 .await
841 .unwrap(),
842 Namespace::new(child_namespace_ident.clone())
843 );
844
845 assert_eq!(
846 catalog.get_namespace(&child_namespace_ident).await.unwrap(),
847 Namespace::with_properties(child_namespace_ident, HashMap::new())
848 );
849 }
850
851 #[tokio::test]
852 async fn test_create_deeply_nested_namespace() {
853 let catalog = new_memory_catalog().await;
854 let namespace_ident_a = NamespaceIdent::new("a".into());
855 let namespace_ident_a_b = NamespaceIdent::from_strs(vec!["a", "b"]).unwrap();
856 create_namespaces(&catalog, &vec![&namespace_ident_a, &namespace_ident_a_b]).await;
857
858 let namespace_ident_a_b_c = NamespaceIdent::from_strs(vec!["a", "b", "c"]).unwrap();
859
860 assert_eq!(
861 catalog
862 .create_namespace(&namespace_ident_a_b_c, HashMap::new())
863 .await
864 .unwrap(),
865 Namespace::new(namespace_ident_a_b_c.clone())
866 );
867
868 assert_eq!(
869 catalog.get_namespace(&namespace_ident_a_b_c).await.unwrap(),
870 Namespace::with_properties(namespace_ident_a_b_c, HashMap::new())
871 );
872 }
873
874 #[tokio::test]
875 async fn test_create_nested_namespace_throws_error_if_top_level_namespace_doesnt_exist() {
876 let catalog = new_memory_catalog().await;
877
878 let nested_namespace_ident = NamespaceIdent::from_strs(vec!["a", "b"]).unwrap();
879
880 assert_eq!(
881 catalog
882 .create_namespace(&nested_namespace_ident, HashMap::new())
883 .await
884 .unwrap_err()
885 .to_string(),
886 format!(
887 "NamespaceNotFound => No such namespace: {:?}",
888 NamespaceIdent::new("a".into())
889 )
890 );
891
892 assert_eq!(catalog.list_namespaces(None).await.unwrap(), vec![]);
893 }
894
895 #[tokio::test]
896 async fn test_create_deeply_nested_namespace_throws_error_if_intermediate_namespace_doesnt_exist()
897 {
898 let catalog = new_memory_catalog().await;
899
900 let namespace_ident_a = NamespaceIdent::new("a".into());
901 create_namespace(&catalog, &namespace_ident_a).await;
902
903 let namespace_ident_a_b_c = NamespaceIdent::from_strs(vec!["a", "b", "c"]).unwrap();
904
905 assert_eq!(
906 catalog
907 .create_namespace(&namespace_ident_a_b_c, HashMap::new())
908 .await
909 .unwrap_err()
910 .to_string(),
911 format!(
912 "NamespaceNotFound => No such namespace: {:?}",
913 NamespaceIdent::from_strs(vec!["a", "b"]).unwrap()
914 )
915 );
916
917 assert_eq!(catalog.list_namespaces(None).await.unwrap(), vec![
918 namespace_ident_a.clone()
919 ]);
920
921 assert_eq!(
922 catalog
923 .list_namespaces(Some(&namespace_ident_a))
924 .await
925 .unwrap(),
926 vec![]
927 );
928 }
929
930 #[tokio::test]
931 async fn test_get_namespace() {
932 let catalog = new_memory_catalog().await;
933 let namespace_ident = NamespaceIdent::new("abc".into());
934
935 let mut properties: HashMap<String, String> = HashMap::new();
936 properties.insert("k".into(), "v".into());
937 let _ = catalog
938 .create_namespace(&namespace_ident, properties.clone())
939 .await
940 .unwrap();
941
942 assert_eq!(
943 catalog.get_namespace(&namespace_ident).await.unwrap(),
944 Namespace::with_properties(namespace_ident, properties)
945 )
946 }
947
948 #[tokio::test]
949 async fn test_get_nested_namespace() {
950 let catalog = new_memory_catalog().await;
951 let namespace_ident_a = NamespaceIdent::new("a".into());
952 let namespace_ident_a_b = NamespaceIdent::from_strs(vec!["a", "b"]).unwrap();
953 create_namespaces(&catalog, &vec![&namespace_ident_a, &namespace_ident_a_b]).await;
954
955 assert_eq!(
956 catalog.get_namespace(&namespace_ident_a_b).await.unwrap(),
957 Namespace::with_properties(namespace_ident_a_b, HashMap::new())
958 );
959 }
960
961 #[tokio::test]
962 async fn test_get_deeply_nested_namespace() {
963 let catalog = new_memory_catalog().await;
964 let namespace_ident_a = NamespaceIdent::new("a".into());
965 let namespace_ident_a_b = NamespaceIdent::from_strs(vec!["a", "b"]).unwrap();
966 let namespace_ident_a_b_c = NamespaceIdent::from_strs(vec!["a", "b", "c"]).unwrap();
967 create_namespaces(&catalog, &vec![
968 &namespace_ident_a,
969 &namespace_ident_a_b,
970 &namespace_ident_a_b_c,
971 ])
972 .await;
973
974 assert_eq!(
975 catalog.get_namespace(&namespace_ident_a_b_c).await.unwrap(),
976 Namespace::with_properties(namespace_ident_a_b_c, HashMap::new())
977 );
978 }
979
980 #[tokio::test]
981 async fn test_get_namespace_throws_error_if_namespace_doesnt_exist() {
982 let catalog = new_memory_catalog().await;
983 create_namespace(&catalog, &NamespaceIdent::new("a".into())).await;
984
985 let non_existent_namespace_ident = NamespaceIdent::new("b".into());
986 assert_eq!(
987 catalog
988 .get_namespace(&non_existent_namespace_ident)
989 .await
990 .unwrap_err()
991 .to_string(),
992 format!("NamespaceNotFound => No such namespace: {non_existent_namespace_ident:?}")
993 )
994 }
995
996 #[tokio::test]
997 async fn test_update_namespace() {
998 let catalog = new_memory_catalog().await;
999 let namespace_ident = NamespaceIdent::new("abc".into());
1000 create_namespace(&catalog, &namespace_ident).await;
1001
1002 let mut new_properties: HashMap<String, String> = HashMap::new();
1003 new_properties.insert("k".into(), "v".into());
1004
1005 catalog
1006 .update_namespace(&namespace_ident, new_properties.clone())
1007 .await
1008 .unwrap();
1009
1010 assert_eq!(
1011 catalog.get_namespace(&namespace_ident).await.unwrap(),
1012 Namespace::with_properties(namespace_ident, new_properties)
1013 )
1014 }
1015
1016 #[tokio::test]
1017 async fn test_update_nested_namespace() {
1018 let catalog = new_memory_catalog().await;
1019 let namespace_ident_a = NamespaceIdent::new("a".into());
1020 let namespace_ident_a_b = NamespaceIdent::from_strs(vec!["a", "b"]).unwrap();
1021 create_namespaces(&catalog, &vec![&namespace_ident_a, &namespace_ident_a_b]).await;
1022
1023 let mut new_properties = HashMap::new();
1024 new_properties.insert("k".into(), "v".into());
1025
1026 catalog
1027 .update_namespace(&namespace_ident_a_b, new_properties.clone())
1028 .await
1029 .unwrap();
1030
1031 assert_eq!(
1032 catalog.get_namespace(&namespace_ident_a_b).await.unwrap(),
1033 Namespace::with_properties(namespace_ident_a_b, new_properties)
1034 );
1035 }
1036
1037 #[tokio::test]
1038 async fn test_update_deeply_nested_namespace() {
1039 let catalog = new_memory_catalog().await;
1040 let namespace_ident_a = NamespaceIdent::new("a".into());
1041 let namespace_ident_a_b = NamespaceIdent::from_strs(vec!["a", "b"]).unwrap();
1042 let namespace_ident_a_b_c = NamespaceIdent::from_strs(vec!["a", "b", "c"]).unwrap();
1043 create_namespaces(&catalog, &vec![
1044 &namespace_ident_a,
1045 &namespace_ident_a_b,
1046 &namespace_ident_a_b_c,
1047 ])
1048 .await;
1049
1050 let mut new_properties = HashMap::new();
1051 new_properties.insert("k".into(), "v".into());
1052
1053 catalog
1054 .update_namespace(&namespace_ident_a_b_c, new_properties.clone())
1055 .await
1056 .unwrap();
1057
1058 assert_eq!(
1059 catalog.get_namespace(&namespace_ident_a_b_c).await.unwrap(),
1060 Namespace::with_properties(namespace_ident_a_b_c, new_properties)
1061 );
1062 }
1063
1064 #[tokio::test]
1065 async fn test_update_namespace_throws_error_if_namespace_doesnt_exist() {
1066 let catalog = new_memory_catalog().await;
1067 create_namespace(&catalog, &NamespaceIdent::new("abc".into())).await;
1068
1069 let non_existent_namespace_ident = NamespaceIdent::new("def".into());
1070 assert_eq!(
1071 catalog
1072 .update_namespace(&non_existent_namespace_ident, HashMap::new())
1073 .await
1074 .unwrap_err()
1075 .to_string(),
1076 format!("NamespaceNotFound => No such namespace: {non_existent_namespace_ident:?}")
1077 )
1078 }
1079
1080 #[tokio::test]
1081 async fn test_drop_namespace() {
1082 let catalog = new_memory_catalog().await;
1083 let namespace_ident = NamespaceIdent::new("abc".into());
1084 create_namespace(&catalog, &namespace_ident).await;
1085
1086 catalog.drop_namespace(&namespace_ident).await.unwrap();
1087
1088 assert!(!catalog.namespace_exists(&namespace_ident).await.unwrap())
1089 }
1090
1091 #[tokio::test]
1092 async fn test_drop_nested_namespace() {
1093 let catalog = new_memory_catalog().await;
1094 let namespace_ident_a = NamespaceIdent::new("a".into());
1095 let namespace_ident_a_b = NamespaceIdent::from_strs(vec!["a", "b"]).unwrap();
1096 create_namespaces(&catalog, &vec![&namespace_ident_a, &namespace_ident_a_b]).await;
1097
1098 catalog.drop_namespace(&namespace_ident_a_b).await.unwrap();
1099
1100 assert!(
1101 !catalog
1102 .namespace_exists(&namespace_ident_a_b)
1103 .await
1104 .unwrap()
1105 );
1106
1107 assert!(catalog.namespace_exists(&namespace_ident_a).await.unwrap());
1108 }
1109
1110 #[tokio::test]
1111 async fn test_drop_deeply_nested_namespace() {
1112 let catalog = new_memory_catalog().await;
1113 let namespace_ident_a = NamespaceIdent::new("a".into());
1114 let namespace_ident_a_b = NamespaceIdent::from_strs(vec!["a", "b"]).unwrap();
1115 let namespace_ident_a_b_c = NamespaceIdent::from_strs(vec!["a", "b", "c"]).unwrap();
1116 create_namespaces(&catalog, &vec![
1117 &namespace_ident_a,
1118 &namespace_ident_a_b,
1119 &namespace_ident_a_b_c,
1120 ])
1121 .await;
1122
1123 catalog
1124 .drop_namespace(&namespace_ident_a_b_c)
1125 .await
1126 .unwrap();
1127
1128 assert!(
1129 !catalog
1130 .namespace_exists(&namespace_ident_a_b_c)
1131 .await
1132 .unwrap()
1133 );
1134
1135 assert!(
1136 catalog
1137 .namespace_exists(&namespace_ident_a_b)
1138 .await
1139 .unwrap()
1140 );
1141
1142 assert!(catalog.namespace_exists(&namespace_ident_a).await.unwrap());
1143 }
1144
1145 #[tokio::test]
1146 async fn test_drop_namespace_throws_error_if_namespace_doesnt_exist() {
1147 let catalog = new_memory_catalog().await;
1148
1149 let non_existent_namespace_ident = NamespaceIdent::new("abc".into());
1150 assert_eq!(
1151 catalog
1152 .drop_namespace(&non_existent_namespace_ident)
1153 .await
1154 .unwrap_err()
1155 .to_string(),
1156 format!("NamespaceNotFound => No such namespace: {non_existent_namespace_ident:?}")
1157 )
1158 }
1159
1160 #[tokio::test]
1161 async fn test_drop_namespace_throws_error_if_nested_namespace_doesnt_exist() {
1162 let catalog = new_memory_catalog().await;
1163 create_namespace(&catalog, &NamespaceIdent::new("a".into())).await;
1164
1165 let non_existent_namespace_ident =
1166 NamespaceIdent::from_vec(vec!["a".into(), "b".into()]).unwrap();
1167 assert_eq!(
1168 catalog
1169 .drop_namespace(&non_existent_namespace_ident)
1170 .await
1171 .unwrap_err()
1172 .to_string(),
1173 format!("NamespaceNotFound => No such namespace: {non_existent_namespace_ident:?}")
1174 )
1175 }
1176
1177 #[tokio::test]
1178 async fn test_dropping_a_namespace_also_drops_namespaces_nested_under_that_one() {
1179 let catalog = new_memory_catalog().await;
1180 let namespace_ident_a = NamespaceIdent::new("a".into());
1181 let namespace_ident_a_b = NamespaceIdent::from_strs(vec!["a", "b"]).unwrap();
1182 create_namespaces(&catalog, &vec![&namespace_ident_a, &namespace_ident_a_b]).await;
1183
1184 catalog.drop_namespace(&namespace_ident_a).await.unwrap();
1185
1186 assert!(!catalog.namespace_exists(&namespace_ident_a).await.unwrap());
1187
1188 assert!(
1189 !catalog
1190 .namespace_exists(&namespace_ident_a_b)
1191 .await
1192 .unwrap()
1193 );
1194 }
1195
1196 #[tokio::test]
1197 async fn test_create_table_with_location() {
1198 let tmp_dir = TempDir::new().unwrap();
1199 let catalog = new_memory_catalog().await;
1200 let namespace_ident = NamespaceIdent::new("a".into());
1201 create_namespace(&catalog, &namespace_ident).await;
1202
1203 let table_name = "abc";
1204 let location = tmp_dir.path().to_str().unwrap().to_string();
1205 let table_creation = TableCreation::builder()
1206 .name(table_name.into())
1207 .location(location.clone())
1208 .schema(simple_table_schema())
1209 .build();
1210
1211 let expected_table_ident = TableIdent::new(namespace_ident.clone(), table_name.into());
1212
1213 assert_table_eq(
1214 &catalog
1215 .create_table(&namespace_ident, table_creation)
1216 .await
1217 .unwrap(),
1218 &expected_table_ident,
1219 &simple_table_schema(),
1220 );
1221
1222 let table = catalog.load_table(&expected_table_ident).await.unwrap();
1223
1224 assert_table_eq(&table, &expected_table_ident, &simple_table_schema());
1225
1226 assert!(
1227 table
1228 .metadata_location()
1229 .unwrap()
1230 .to_string()
1231 .starts_with(&location)
1232 )
1233 }
1234
1235 #[tokio::test]
1236 async fn test_create_table_falls_back_to_namespace_location_if_table_location_is_missing() {
1237 let warehouse_location = temp_path();
1238 let catalog = MemoryCatalogBuilder::default()
1239 .load(
1240 "memory",
1241 HashMap::from([(
1242 MEMORY_CATALOG_WAREHOUSE.to_string(),
1243 warehouse_location.clone(),
1244 )]),
1245 )
1246 .await
1247 .unwrap();
1248
1249 let namespace_ident = NamespaceIdent::new("a".into());
1250 let mut namespace_properties = HashMap::new();
1251 let namespace_location = temp_path();
1252 namespace_properties.insert(LOCATION.to_string(), namespace_location.to_string());
1253 catalog
1254 .create_namespace(&namespace_ident, namespace_properties)
1255 .await
1256 .unwrap();
1257
1258 let table_name = "tbl1";
1259 let expected_table_ident = TableIdent::new(namespace_ident.clone(), table_name.into());
1260 let expected_table_metadata_location_regex =
1261 format!("^{namespace_location}/tbl1/metadata/00000-{UUID_REGEX_STR}.metadata.json$",);
1262
1263 let table = catalog
1264 .create_table(
1265 &namespace_ident,
1266 TableCreation::builder()
1267 .name(table_name.into())
1268 .schema(simple_table_schema())
1269 .build(),
1271 )
1272 .await
1273 .unwrap();
1274 assert_table_eq(&table, &expected_table_ident, &simple_table_schema());
1275 assert_table_metadata_location_matches(&table, &expected_table_metadata_location_regex);
1276
1277 let table = catalog.load_table(&expected_table_ident).await.unwrap();
1278 assert_table_eq(&table, &expected_table_ident, &simple_table_schema());
1279 assert_table_metadata_location_matches(&table, &expected_table_metadata_location_regex);
1280 }
1281
1282 #[tokio::test]
1283 async fn test_create_table_in_nested_namespace_falls_back_to_nested_namespace_location_if_table_location_is_missing()
1284 {
1285 let warehouse_location = temp_path();
1286 let catalog = MemoryCatalogBuilder::default()
1287 .load(
1288 "memory",
1289 HashMap::from([(
1290 MEMORY_CATALOG_WAREHOUSE.to_string(),
1291 warehouse_location.clone(),
1292 )]),
1293 )
1294 .await
1295 .unwrap();
1296
1297 let namespace_ident = NamespaceIdent::new("a".into());
1298 let mut namespace_properties = HashMap::new();
1299 let namespace_location = temp_path();
1300 namespace_properties.insert(LOCATION.to_string(), namespace_location.to_string());
1301 catalog
1302 .create_namespace(&namespace_ident, namespace_properties)
1303 .await
1304 .unwrap();
1305
1306 let nested_namespace_ident = NamespaceIdent::from_strs(vec!["a", "b"]).unwrap();
1307 let mut nested_namespace_properties = HashMap::new();
1308 let nested_namespace_location = temp_path();
1309 nested_namespace_properties
1310 .insert(LOCATION.to_string(), nested_namespace_location.to_string());
1311 catalog
1312 .create_namespace(&nested_namespace_ident, nested_namespace_properties)
1313 .await
1314 .unwrap();
1315
1316 let table_name = "tbl1";
1317 let expected_table_ident =
1318 TableIdent::new(nested_namespace_ident.clone(), table_name.into());
1319 let expected_table_metadata_location_regex = format!(
1320 "^{nested_namespace_location}/tbl1/metadata/00000-{UUID_REGEX_STR}.metadata.json$",
1321 );
1322
1323 let table = catalog
1324 .create_table(
1325 &nested_namespace_ident,
1326 TableCreation::builder()
1327 .name(table_name.into())
1328 .schema(simple_table_schema())
1329 .build(),
1331 )
1332 .await
1333 .unwrap();
1334 assert_table_eq(&table, &expected_table_ident, &simple_table_schema());
1335 assert_table_metadata_location_matches(&table, &expected_table_metadata_location_regex);
1336
1337 let table = catalog.load_table(&expected_table_ident).await.unwrap();
1338 assert_table_eq(&table, &expected_table_ident, &simple_table_schema());
1339 assert_table_metadata_location_matches(&table, &expected_table_metadata_location_regex);
1340 }
1341
1342 #[tokio::test]
1343 async fn test_create_table_falls_back_to_warehouse_location_if_both_table_location_and_namespace_location_are_missing()
1344 {
1345 let warehouse_location = temp_path();
1346 let catalog = MemoryCatalogBuilder::default()
1347 .load(
1348 "memory",
1349 HashMap::from([(
1350 MEMORY_CATALOG_WAREHOUSE.to_string(),
1351 warehouse_location.clone(),
1352 )]),
1353 )
1354 .await
1355 .unwrap();
1356
1357 let namespace_ident = NamespaceIdent::new("a".into());
1358 let namespace_properties = HashMap::new();
1360 catalog
1361 .create_namespace(&namespace_ident, namespace_properties)
1362 .await
1363 .unwrap();
1364
1365 let table_name = "tbl1";
1366 let expected_table_ident = TableIdent::new(namespace_ident.clone(), table_name.into());
1367 let expected_table_metadata_location_regex =
1368 format!("^{warehouse_location}/a/tbl1/metadata/00000-{UUID_REGEX_STR}.metadata.json$");
1369
1370 let table = catalog
1371 .create_table(
1372 &namespace_ident,
1373 TableCreation::builder()
1374 .name(table_name.into())
1375 .schema(simple_table_schema())
1376 .build(),
1378 )
1379 .await
1380 .unwrap();
1381 assert_table_eq(&table, &expected_table_ident, &simple_table_schema());
1382 assert_table_metadata_location_matches(&table, &expected_table_metadata_location_regex);
1383
1384 let table = catalog.load_table(&expected_table_ident).await.unwrap();
1385 assert_table_eq(&table, &expected_table_ident, &simple_table_schema());
1386 assert_table_metadata_location_matches(&table, &expected_table_metadata_location_regex);
1387 }
1388
1389 #[tokio::test]
1390 async fn test_create_table_in_nested_namespace_falls_back_to_warehouse_location_if_both_table_location_and_namespace_location_are_missing()
1391 {
1392 let warehouse_location = temp_path();
1393 let catalog = MemoryCatalogBuilder::default()
1394 .load(
1395 "memory",
1396 HashMap::from([(
1397 MEMORY_CATALOG_WAREHOUSE.to_string(),
1398 warehouse_location.clone(),
1399 )]),
1400 )
1401 .await
1402 .unwrap();
1403
1404 let namespace_ident = NamespaceIdent::new("a".into());
1405 catalog
1406 .create_namespace(&namespace_ident, HashMap::new())
1408 .await
1409 .unwrap();
1410
1411 let nested_namespace_ident = NamespaceIdent::from_strs(vec!["a", "b"]).unwrap();
1412 catalog
1413 .create_namespace(&nested_namespace_ident, HashMap::new())
1415 .await
1416 .unwrap();
1417
1418 let table_name = "tbl1";
1419 let expected_table_ident =
1420 TableIdent::new(nested_namespace_ident.clone(), table_name.into());
1421 let expected_table_metadata_location_regex = format!(
1422 "^{warehouse_location}/a/b/tbl1/metadata/00000-{UUID_REGEX_STR}.metadata.json$"
1423 );
1424
1425 let table = catalog
1426 .create_table(
1427 &nested_namespace_ident,
1428 TableCreation::builder()
1429 .name(table_name.into())
1430 .schema(simple_table_schema())
1431 .build(),
1433 )
1434 .await
1435 .unwrap();
1436 assert_table_eq(&table, &expected_table_ident, &simple_table_schema());
1437 assert_table_metadata_location_matches(&table, &expected_table_metadata_location_regex);
1438
1439 let table = catalog.load_table(&expected_table_ident).await.unwrap();
1440 assert_table_eq(&table, &expected_table_ident, &simple_table_schema());
1441 assert_table_metadata_location_matches(&table, &expected_table_metadata_location_regex);
1442 }
1443
1444 #[tokio::test]
1445 async fn test_load_throws_error_if_warehouse_is_missing() {
1446 let error = MemoryCatalogBuilder::default()
1447 .load("memory", HashMap::from([]))
1448 .await
1449 .unwrap_err();
1450
1451 assert_eq!(error.kind(), ErrorKind::DataInvalid);
1452 assert_eq!(error.message(), "Catalog warehouse is required");
1453 }
1454
1455 #[tokio::test]
1456 async fn test_load_throws_error_if_warehouse_is_empty() {
1457 let error = MemoryCatalogBuilder::default()
1458 .load(
1459 "memory",
1460 HashMap::from([(MEMORY_CATALOG_WAREHOUSE.to_string(), String::new())]),
1461 )
1462 .await
1463 .unwrap_err();
1464
1465 assert_eq!(error.kind(), ErrorKind::DataInvalid);
1466 assert_eq!(error.message(), "Catalog warehouse is required");
1467 }
1468
1469 #[tokio::test]
1470 async fn test_create_table_throws_error_if_table_with_same_name_already_exists() {
1471 let catalog = new_memory_catalog().await;
1472 let namespace_ident = NamespaceIdent::new("a".into());
1473 create_namespace(&catalog, &namespace_ident).await;
1474 let table_name = "tbl1";
1475 let table_ident = TableIdent::new(namespace_ident.clone(), table_name.into());
1476 create_table(&catalog, &table_ident).await;
1477
1478 let tmp_dir = TempDir::new().unwrap();
1479 let location = tmp_dir.path().to_str().unwrap().to_string();
1480
1481 assert_eq!(
1482 catalog
1483 .create_table(
1484 &namespace_ident,
1485 TableCreation::builder()
1486 .name(table_name.into())
1487 .schema(simple_table_schema())
1488 .location(location)
1489 .build()
1490 )
1491 .await
1492 .unwrap_err()
1493 .to_string(),
1494 format!(
1495 "TableAlreadyExists => Cannot create table {:?}. Table already exists.",
1496 &table_ident
1497 )
1498 );
1499 }
1500
1501 #[tokio::test]
1502 async fn test_list_tables_returns_empty_vector() {
1503 let catalog = new_memory_catalog().await;
1504 let namespace_ident = NamespaceIdent::new("a".into());
1505 create_namespace(&catalog, &namespace_ident).await;
1506
1507 assert_eq!(catalog.list_tables(&namespace_ident).await.unwrap(), vec![]);
1508 }
1509
1510 #[tokio::test]
1511 async fn test_list_tables_returns_a_single_table() {
1512 let catalog = new_memory_catalog().await;
1513 let namespace_ident = NamespaceIdent::new("n1".into());
1514 create_namespace(&catalog, &namespace_ident).await;
1515
1516 let table_ident = TableIdent::new(namespace_ident.clone(), "tbl1".into());
1517 create_table(&catalog, &table_ident).await;
1518
1519 assert_eq!(catalog.list_tables(&namespace_ident).await.unwrap(), vec![
1520 table_ident
1521 ]);
1522 }
1523
1524 #[tokio::test]
1525 async fn test_list_tables_returns_multiple_tables() {
1526 let catalog = new_memory_catalog().await;
1527 let namespace_ident = NamespaceIdent::new("n1".into());
1528 create_namespace(&catalog, &namespace_ident).await;
1529
1530 let table_ident_1 = TableIdent::new(namespace_ident.clone(), "tbl1".into());
1531 let table_ident_2 = TableIdent::new(namespace_ident.clone(), "tbl2".into());
1532 let _ = create_tables(&catalog, vec![&table_ident_1, &table_ident_2]).await;
1533
1534 assert_eq!(
1535 to_set(catalog.list_tables(&namespace_ident).await.unwrap()),
1536 to_set(vec![table_ident_1, table_ident_2])
1537 );
1538 }
1539
1540 #[tokio::test]
1541 async fn test_list_tables_returns_tables_from_correct_namespace() {
1542 let catalog = new_memory_catalog().await;
1543 let namespace_ident_1 = NamespaceIdent::new("n1".into());
1544 let namespace_ident_2 = NamespaceIdent::new("n2".into());
1545 create_namespaces(&catalog, &vec![&namespace_ident_1, &namespace_ident_2]).await;
1546
1547 let table_ident_1 = TableIdent::new(namespace_ident_1.clone(), "tbl1".into());
1548 let table_ident_2 = TableIdent::new(namespace_ident_1.clone(), "tbl2".into());
1549 let table_ident_3 = TableIdent::new(namespace_ident_2.clone(), "tbl1".into());
1550 let _ = create_tables(&catalog, vec![
1551 &table_ident_1,
1552 &table_ident_2,
1553 &table_ident_3,
1554 ])
1555 .await;
1556
1557 assert_eq!(
1558 to_set(catalog.list_tables(&namespace_ident_1).await.unwrap()),
1559 to_set(vec![table_ident_1, table_ident_2])
1560 );
1561
1562 assert_eq!(
1563 to_set(catalog.list_tables(&namespace_ident_2).await.unwrap()),
1564 to_set(vec![table_ident_3])
1565 );
1566 }
1567
1568 #[tokio::test]
1569 async fn test_list_tables_returns_table_under_nested_namespace() {
1570 let catalog = new_memory_catalog().await;
1571 let namespace_ident_a = NamespaceIdent::new("a".into());
1572 let namespace_ident_a_b = NamespaceIdent::from_strs(vec!["a", "b"]).unwrap();
1573 create_namespaces(&catalog, &vec![&namespace_ident_a, &namespace_ident_a_b]).await;
1574
1575 let table_ident = TableIdent::new(namespace_ident_a_b.clone(), "tbl1".into());
1576 create_table(&catalog, &table_ident).await;
1577
1578 assert_eq!(
1579 catalog.list_tables(&namespace_ident_a_b).await.unwrap(),
1580 vec![table_ident]
1581 );
1582 }
1583
1584 #[tokio::test]
1585 async fn test_list_tables_throws_error_if_namespace_doesnt_exist() {
1586 let catalog = new_memory_catalog().await;
1587
1588 let non_existent_namespace_ident = NamespaceIdent::new("n1".into());
1589
1590 assert_eq!(
1591 catalog
1592 .list_tables(&non_existent_namespace_ident)
1593 .await
1594 .unwrap_err()
1595 .to_string(),
1596 format!("NamespaceNotFound => No such namespace: {non_existent_namespace_ident:?}"),
1597 );
1598 }
1599
1600 #[tokio::test]
1601 async fn test_drop_table() {
1602 let catalog = new_memory_catalog().await;
1603 let namespace_ident = NamespaceIdent::new("n1".into());
1604 create_namespace(&catalog, &namespace_ident).await;
1605 let table_ident = TableIdent::new(namespace_ident.clone(), "tbl1".into());
1606 create_table(&catalog, &table_ident).await;
1607
1608 catalog.drop_table(&table_ident).await.unwrap();
1609 }
1610
1611 #[tokio::test]
1612 async fn test_drop_table_drops_table_under_nested_namespace() {
1613 let catalog = new_memory_catalog().await;
1614 let namespace_ident_a = NamespaceIdent::new("a".into());
1615 let namespace_ident_a_b = NamespaceIdent::from_strs(vec!["a", "b"]).unwrap();
1616 create_namespaces(&catalog, &vec![&namespace_ident_a, &namespace_ident_a_b]).await;
1617
1618 let table_ident = TableIdent::new(namespace_ident_a_b.clone(), "tbl1".into());
1619 create_table(&catalog, &table_ident).await;
1620
1621 catalog.drop_table(&table_ident).await.unwrap();
1622
1623 assert_eq!(
1624 catalog.list_tables(&namespace_ident_a_b).await.unwrap(),
1625 vec![]
1626 );
1627 }
1628
1629 #[tokio::test]
1630 async fn test_drop_table_throws_error_if_namespace_doesnt_exist() {
1631 let catalog = new_memory_catalog().await;
1632
1633 let non_existent_namespace_ident = NamespaceIdent::new("n1".into());
1634 let non_existent_table_ident =
1635 TableIdent::new(non_existent_namespace_ident.clone(), "tbl1".into());
1636
1637 assert_eq!(
1638 catalog
1639 .drop_table(&non_existent_table_ident)
1640 .await
1641 .unwrap_err()
1642 .to_string(),
1643 format!("NamespaceNotFound => No such namespace: {non_existent_namespace_ident:?}"),
1644 );
1645 }
1646
1647 #[tokio::test]
1648 async fn test_drop_table_throws_error_if_table_doesnt_exist() {
1649 let catalog = new_memory_catalog().await;
1650 let namespace_ident = NamespaceIdent::new("n1".into());
1651 create_namespace(&catalog, &namespace_ident).await;
1652
1653 let non_existent_table_ident = TableIdent::new(namespace_ident.clone(), "tbl1".into());
1654
1655 assert_eq!(
1656 catalog
1657 .drop_table(&non_existent_table_ident)
1658 .await
1659 .unwrap_err()
1660 .to_string(),
1661 format!("TableNotFound => No such table: {non_existent_table_ident:?}"),
1662 );
1663 }
1664
1665 #[tokio::test]
1666 async fn test_table_exists_returns_true() {
1667 let catalog = new_memory_catalog().await;
1668 let namespace_ident = NamespaceIdent::new("n1".into());
1669 create_namespace(&catalog, &namespace_ident).await;
1670 let table_ident = TableIdent::new(namespace_ident.clone(), "tbl1".into());
1671 create_table(&catalog, &table_ident).await;
1672
1673 assert!(catalog.table_exists(&table_ident).await.unwrap());
1674 }
1675
1676 #[tokio::test]
1677 async fn test_table_exists_returns_false() {
1678 let catalog = new_memory_catalog().await;
1679 let namespace_ident = NamespaceIdent::new("n1".into());
1680 create_namespace(&catalog, &namespace_ident).await;
1681 let non_existent_table_ident = TableIdent::new(namespace_ident.clone(), "tbl1".into());
1682
1683 assert!(
1684 !catalog
1685 .table_exists(&non_existent_table_ident)
1686 .await
1687 .unwrap()
1688 );
1689 }
1690
1691 #[tokio::test]
1692 async fn test_table_exists_under_nested_namespace() {
1693 let catalog = new_memory_catalog().await;
1694 let namespace_ident_a = NamespaceIdent::new("a".into());
1695 let namespace_ident_a_b = NamespaceIdent::from_strs(vec!["a", "b"]).unwrap();
1696 create_namespaces(&catalog, &vec![&namespace_ident_a, &namespace_ident_a_b]).await;
1697
1698 let table_ident = TableIdent::new(namespace_ident_a_b.clone(), "tbl1".into());
1699 create_table(&catalog, &table_ident).await;
1700
1701 assert!(catalog.table_exists(&table_ident).await.unwrap());
1702
1703 let non_existent_table_ident = TableIdent::new(namespace_ident_a_b.clone(), "tbl2".into());
1704 assert!(
1705 !catalog
1706 .table_exists(&non_existent_table_ident)
1707 .await
1708 .unwrap()
1709 );
1710 }
1711
1712 #[tokio::test]
1713 async fn test_table_exists_throws_error_if_namespace_doesnt_exist() {
1714 let catalog = new_memory_catalog().await;
1715
1716 let non_existent_namespace_ident = NamespaceIdent::new("n1".into());
1717 let non_existent_table_ident =
1718 TableIdent::new(non_existent_namespace_ident.clone(), "tbl1".into());
1719
1720 assert_eq!(
1721 catalog
1722 .table_exists(&non_existent_table_ident)
1723 .await
1724 .unwrap_err()
1725 .to_string(),
1726 format!("NamespaceNotFound => No such namespace: {non_existent_namespace_ident:?}"),
1727 );
1728 }
1729
1730 #[tokio::test]
1731 async fn test_rename_table_in_same_namespace() {
1732 let catalog = new_memory_catalog().await;
1733 let namespace_ident = NamespaceIdent::new("n1".into());
1734 create_namespace(&catalog, &namespace_ident).await;
1735 let src_table_ident = TableIdent::new(namespace_ident.clone(), "tbl1".into());
1736 let dst_table_ident = TableIdent::new(namespace_ident.clone(), "tbl2".into());
1737 create_table(&catalog, &src_table_ident).await;
1738
1739 catalog
1740 .rename_table(&src_table_ident, &dst_table_ident)
1741 .await
1742 .unwrap();
1743
1744 assert_eq!(catalog.list_tables(&namespace_ident).await.unwrap(), vec![
1745 dst_table_ident
1746 ],);
1747 }
1748
1749 #[tokio::test]
1750 async fn test_rename_table_across_namespaces() {
1751 let catalog = new_memory_catalog().await;
1752 let src_namespace_ident = NamespaceIdent::new("a".into());
1753 let dst_namespace_ident = NamespaceIdent::new("b".into());
1754 create_namespaces(&catalog, &vec![&src_namespace_ident, &dst_namespace_ident]).await;
1755 let src_table_ident = TableIdent::new(src_namespace_ident.clone(), "tbl1".into());
1756 let dst_table_ident = TableIdent::new(dst_namespace_ident.clone(), "tbl2".into());
1757 create_table(&catalog, &src_table_ident).await;
1758
1759 catalog
1760 .rename_table(&src_table_ident, &dst_table_ident)
1761 .await
1762 .unwrap();
1763
1764 assert_eq!(
1765 catalog.list_tables(&src_namespace_ident).await.unwrap(),
1766 vec![],
1767 );
1768
1769 assert_eq!(
1770 catalog.list_tables(&dst_namespace_ident).await.unwrap(),
1771 vec![dst_table_ident],
1772 );
1773 }
1774
1775 #[tokio::test]
1776 async fn test_rename_table_src_table_is_same_as_dst_table() {
1777 let catalog = new_memory_catalog().await;
1778 let namespace_ident = NamespaceIdent::new("n1".into());
1779 create_namespace(&catalog, &namespace_ident).await;
1780 let table_ident = TableIdent::new(namespace_ident.clone(), "tbl".into());
1781 create_table(&catalog, &table_ident).await;
1782
1783 catalog
1784 .rename_table(&table_ident, &table_ident)
1785 .await
1786 .unwrap();
1787
1788 assert_eq!(catalog.list_tables(&namespace_ident).await.unwrap(), vec![
1789 table_ident
1790 ],);
1791 }
1792
1793 #[tokio::test]
1794 async fn test_rename_table_across_nested_namespaces() {
1795 let catalog = new_memory_catalog().await;
1796 let namespace_ident_a = NamespaceIdent::new("a".into());
1797 let namespace_ident_a_b = NamespaceIdent::from_strs(vec!["a", "b"]).unwrap();
1798 let namespace_ident_a_b_c = NamespaceIdent::from_strs(vec!["a", "b", "c"]).unwrap();
1799 create_namespaces(&catalog, &vec![
1800 &namespace_ident_a,
1801 &namespace_ident_a_b,
1802 &namespace_ident_a_b_c,
1803 ])
1804 .await;
1805
1806 let src_table_ident = TableIdent::new(namespace_ident_a_b_c.clone(), "tbl1".into());
1807 create_tables(&catalog, vec![&src_table_ident]).await;
1808
1809 let dst_table_ident = TableIdent::new(namespace_ident_a_b.clone(), "tbl1".into());
1810 catalog
1811 .rename_table(&src_table_ident, &dst_table_ident)
1812 .await
1813 .unwrap();
1814
1815 assert!(!catalog.table_exists(&src_table_ident).await.unwrap());
1816
1817 assert!(catalog.table_exists(&dst_table_ident).await.unwrap());
1818 }
1819
1820 #[tokio::test]
1821 async fn test_rename_table_throws_error_if_src_namespace_doesnt_exist() {
1822 let catalog = new_memory_catalog().await;
1823
1824 let non_existent_src_namespace_ident = NamespaceIdent::new("n1".into());
1825 let src_table_ident =
1826 TableIdent::new(non_existent_src_namespace_ident.clone(), "tbl1".into());
1827
1828 let dst_namespace_ident = NamespaceIdent::new("n2".into());
1829 create_namespace(&catalog, &dst_namespace_ident).await;
1830 let dst_table_ident = TableIdent::new(dst_namespace_ident.clone(), "tbl1".into());
1831
1832 assert_eq!(
1833 catalog
1834 .rename_table(&src_table_ident, &dst_table_ident)
1835 .await
1836 .unwrap_err()
1837 .to_string(),
1838 format!("NamespaceNotFound => No such namespace: {non_existent_src_namespace_ident:?}"),
1839 );
1840 }
1841
1842 #[tokio::test]
1843 async fn test_rename_table_throws_error_if_dst_namespace_doesnt_exist() {
1844 let catalog = new_memory_catalog().await;
1845 let src_namespace_ident = NamespaceIdent::new("n1".into());
1846 let src_table_ident = TableIdent::new(src_namespace_ident.clone(), "tbl1".into());
1847 create_namespace(&catalog, &src_namespace_ident).await;
1848 create_table(&catalog, &src_table_ident).await;
1849
1850 let non_existent_dst_namespace_ident = NamespaceIdent::new("n2".into());
1851 let dst_table_ident =
1852 TableIdent::new(non_existent_dst_namespace_ident.clone(), "tbl1".into());
1853 assert_eq!(
1854 catalog
1855 .rename_table(&src_table_ident, &dst_table_ident)
1856 .await
1857 .unwrap_err()
1858 .to_string(),
1859 format!("NamespaceNotFound => No such namespace: {non_existent_dst_namespace_ident:?}"),
1860 );
1861 }
1862
1863 #[tokio::test]
1864 async fn test_rename_table_throws_error_if_src_table_doesnt_exist() {
1865 let catalog = new_memory_catalog().await;
1866 let namespace_ident = NamespaceIdent::new("n1".into());
1867 create_namespace(&catalog, &namespace_ident).await;
1868 let src_table_ident = TableIdent::new(namespace_ident.clone(), "tbl1".into());
1869 let dst_table_ident = TableIdent::new(namespace_ident.clone(), "tbl2".into());
1870
1871 assert_eq!(
1872 catalog
1873 .rename_table(&src_table_ident, &dst_table_ident)
1874 .await
1875 .unwrap_err()
1876 .to_string(),
1877 format!("TableNotFound => No such table: {src_table_ident:?}"),
1878 );
1879 }
1880
1881 #[tokio::test]
1882 async fn test_rename_table_throws_error_if_dst_table_already_exists() {
1883 let catalog = new_memory_catalog().await;
1884 let namespace_ident = NamespaceIdent::new("n1".into());
1885 create_namespace(&catalog, &namespace_ident).await;
1886 let src_table_ident = TableIdent::new(namespace_ident.clone(), "tbl1".into());
1887 let dst_table_ident = TableIdent::new(namespace_ident.clone(), "tbl2".into());
1888 create_tables(&catalog, vec![&src_table_ident, &dst_table_ident]).await;
1889
1890 assert_eq!(
1891 catalog
1892 .rename_table(&src_table_ident, &dst_table_ident)
1893 .await
1894 .unwrap_err()
1895 .to_string(),
1896 format!(
1897 "TableAlreadyExists => Cannot create table {:?}. Table already exists.",
1898 &dst_table_ident
1899 ),
1900 );
1901 }
1902
1903 #[tokio::test]
1904 async fn test_register_table() {
1905 let catalog = new_memory_catalog().await;
1907 let namespace_ident = NamespaceIdent::new("test_namespace".into());
1908 create_namespace(&catalog, &namespace_ident).await;
1909
1910 let source_table_ident = TableIdent::new(namespace_ident.clone(), "source_table".into());
1912 create_table(&catalog, &source_table_ident).await;
1913
1914 let source_table = catalog.load_table(&source_table_ident).await.unwrap();
1916 let metadata_location = source_table.metadata_location().unwrap().to_string();
1917
1918 let register_table_ident =
1920 TableIdent::new(namespace_ident.clone(), "register_table".into());
1921 let registered_table = catalog
1922 .register_table(®ister_table_ident, metadata_location.clone())
1923 .await
1924 .unwrap();
1925
1926 assert_eq!(registered_table.identifier(), ®ister_table_ident);
1928
1929 assert_eq!(
1931 registered_table.metadata_location().unwrap().to_string(),
1932 metadata_location
1933 );
1934
1935 assert!(catalog.table_exists(®ister_table_ident).await.unwrap());
1937
1938 let loaded_table = catalog.load_table(®ister_table_ident).await.unwrap();
1940 assert_eq!(loaded_table.identifier(), ®ister_table_ident);
1941 assert_eq!(
1942 loaded_table.metadata_location().unwrap().to_string(),
1943 metadata_location
1944 );
1945 }
1946
1947 #[tokio::test]
1948 async fn test_update_table() {
1949 let catalog = new_memory_catalog().await;
1950
1951 let table = create_table_with_namespace(&catalog).await;
1952
1953 assert!(!table.metadata().properties().contains_key("key"));
1955
1956 tokio::time::sleep(std::time::Duration::from_millis(2)).await;
1960
1961 let tx = Transaction::new(&table);
1963 let updated_table = tx
1964 .update_table_properties()
1965 .set("key".to_string(), "value".to_string())
1966 .apply(tx)
1967 .unwrap()
1968 .commit(&catalog)
1969 .await
1970 .unwrap();
1971
1972 assert_eq!(
1973 updated_table.metadata().properties().get("key").unwrap(),
1974 "value"
1975 );
1976
1977 assert_eq!(table.identifier(), updated_table.identifier());
1978 assert_eq!(table.metadata().uuid(), updated_table.metadata().uuid());
1979 assert_ne!(table.metadata_location(), updated_table.metadata_location());
1980
1981 assert!(
1982 table.metadata().metadata_log().len() < updated_table.metadata().metadata_log().len()
1983 );
1984 }
1985
1986 #[tokio::test]
1987 async fn test_update_table_fails_if_table_doesnt_exist() {
1988 let catalog = new_memory_catalog().await;
1989
1990 let namespace_ident = NamespaceIdent::new("a".into());
1991 create_namespace(&catalog, &namespace_ident).await;
1992
1993 let table_ident = TableIdent::new(namespace_ident, "test".to_string());
1995 let table = build_table(table_ident);
1996
1997 let tx = Transaction::new(&table);
1998 let err = tx
1999 .update_table_properties()
2000 .set("key".to_string(), "value".to_string())
2001 .apply(tx)
2002 .unwrap()
2003 .commit(&catalog)
2004 .await
2005 .unwrap_err();
2006 assert_eq!(err.kind(), ErrorKind::TableNotFound);
2007 }
2008
2009 const FIXTURE_MASTER_KEY_ID: &str = "master-1";
2012 const FIXTURE_MASTER_KEY_BYTES: [u8; 16] = [
2013 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
2014 0x0f,
2015 ];
2016
2017 fn fixture_kms_factory() -> MemoryKmsClientFactory {
2019 use crate::encryption::SensitiveBytes;
2020
2021 let factory = MemoryKmsClientFactory::new();
2022 factory
2023 .add_master_key_bytes(
2024 FIXTURE_MASTER_KEY_ID,
2025 SensitiveBytes::new(FIXTURE_MASTER_KEY_BYTES),
2026 )
2027 .unwrap();
2028 factory
2029 }
2030
2031 fn load_encrypted_fixture_metadata() -> TableMetadata {
2034 let manifest_dir = env!("CARGO_MANIFEST_DIR");
2035 let metadata_json = std::fs::read_to_string(format!(
2036 "{manifest_dir}/testdata/table_metadata/TableMetadataV3ValidEncryption.json"
2037 ))
2038 .unwrap();
2039 let mut metadata: TableMetadata = serde_json::from_str(&metadata_json).unwrap();
2040
2041 let manifest_list_path =
2042 format!("{manifest_dir}/testdata/manifests_lists/manifest-list-v3-encrypted.avro");
2043 let snapshot = metadata.snapshots.get_mut(&1).unwrap();
2044 let mut patched = snapshot.as_ref().clone();
2045 patched.manifest_list = manifest_list_path;
2046 *snapshot = Arc::new(patched);
2047
2048 metadata
2049 }
2050
2051 #[tokio::test]
2052 async fn catalog_kms_factory_client_reaches_table_encryption_manager() {
2053 let warehouse = temp_path();
2054 let catalog = MemoryCatalogBuilder::default()
2055 .with_storage_factory(Arc::new(LocalFsStorageFactory))
2056 .with_kms_client_factory(Arc::new(fixture_kms_factory()))
2057 .load(
2058 "memory",
2059 HashMap::from([(MEMORY_CATALOG_WAREHOUSE.to_string(), warehouse)]),
2060 )
2061 .await
2062 .unwrap();
2063
2064 let namespace_ident = NamespaceIdent::new("enc_ns".into());
2065 create_namespace(&catalog, &namespace_ident).await;
2066
2067 let metadata = load_encrypted_fixture_metadata();
2068 let metadata_dir = TempDir::new().unwrap();
2069 let metadata_location =
2070 format!("{}/v1.metadata.json", metadata_dir.path().to_str().unwrap());
2071 std::fs::write(&metadata_location, serde_json::to_vec(&metadata).unwrap()).unwrap();
2072
2073 let table_ident = TableIdent::new(namespace_ident, "enc".to_string());
2074 catalog
2075 .register_table(&table_ident, metadata_location)
2076 .await
2077 .unwrap();
2078
2079 let table = catalog.load_table(&table_ident).await.unwrap();
2080 assert!(
2081 table.encryption_manager().is_some(),
2082 "factory-built KMS client should have reached the table's EncryptionManager"
2083 );
2084
2085 let snapshot_ref = table.metadata().current_snapshot().unwrap();
2086 let manifest_list = table
2087 .object_cache()
2088 .get_manifest_list(snapshot_ref, &table.metadata_ref())
2089 .await
2090 .unwrap();
2091 assert_eq!(manifest_list.entries().len(), 0);
2092 }
2093
2094 fn build_table(ident: TableIdent) -> Table {
2095 let file_io = FileIO::new_with_fs();
2096
2097 let temp_dir = TempDir::new().unwrap();
2098 let location = temp_dir.path().to_str().unwrap().to_string();
2099
2100 let table_creation = TableCreation::builder()
2101 .name(ident.name().to_string())
2102 .schema(simple_table_schema())
2103 .location(location)
2104 .build();
2105 let metadata = TableMetadataBuilder::from_table_creation(table_creation)
2106 .unwrap()
2107 .build()
2108 .unwrap()
2109 .metadata;
2110
2111 Table::builder()
2112 .identifier(ident)
2113 .metadata(metadata)
2114 .file_io(file_io)
2115 .runtime(test_runtime())
2116 .build()
2117 .unwrap()
2118 }
2119}