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