Skip to main content

iceberg/catalog/memory/
catalog.rs

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