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