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