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