diff --git a/Cargo.lock b/Cargo.lock index 350474c240..3d138b1ffe 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3611,7 +3611,7 @@ dependencies = [ "fs-err", "home", "iceberg", - "iceberg-catalog-rest", + "iceberg-catalog-loader", "iceberg-datafusion", "mimalloc", "stacker", diff --git a/Cargo.toml b/Cargo.toml index 793bb49d86..a56b2760e3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -85,6 +85,7 @@ http = "1.2" iceberg = { version = "0.9.0", path = "./crates/iceberg" } iceberg-catalog-glue = { version = "0.9.0", path = "./crates/catalog/glue" } iceberg-catalog-hms = { version = "0.9.0", path = "./crates/catalog/hms" } +iceberg-catalog-loader = { version = "0.9.0", path = "./crates/catalog/loader" } iceberg-catalog-rest = { version = "0.9.0", path = "./crates/catalog/rest" } iceberg-catalog-s3tables = { version = "0.9.0", path = "./crates/catalog/s3tables" } iceberg-catalog-sql = { version = "0.9.0", path = "./crates/catalog/sql" } diff --git a/crates/integrations/playground/Cargo.toml b/crates/integrations/playground/Cargo.toml index 3f6774be19..4a3f594874 100644 --- a/crates/integrations/playground/Cargo.toml +++ b/crates/integrations/playground/Cargo.toml @@ -35,7 +35,7 @@ dirs = { workspace = true } fs-err = { workspace = true } home = { workspace = true } iceberg = { workspace = true } -iceberg-catalog-rest = { workspace = true } +iceberg-catalog-loader = { workspace = true } iceberg-datafusion = { workspace = true } mimalloc = { workspace = true } stacker = { workspace = true } diff --git a/crates/integrations/playground/src/catalog.rs b/crates/integrations/playground/src/catalog.rs index 66742ab893..fb4cf8d49d 100644 --- a/crates/integrations/playground/src/catalog.rs +++ b/crates/integrations/playground/src/catalog.rs @@ -25,7 +25,6 @@ use datafusion::catalog::{CatalogProvider, CatalogProviderList}; use fs_err::read_to_string; use iceberg::CatalogBuilder; use iceberg::memory::MemoryCatalogBuilder; -use iceberg_catalog_rest::RestCatalogBuilder; use iceberg_datafusion::IcebergCatalogProvider; use toml::{Table as TomlTable, Value}; @@ -94,15 +93,15 @@ impl IcebergCatalogList { props.insert(key.to_string(), value_str.to_string()); } - // Create catalog based on type using the appropriate builder - let catalog: Arc = match r#type { - "rest" => Arc::new(RestCatalogBuilder::default().load(name, props).await?), - "memory" => Arc::new(MemoryCatalogBuilder::default().load(name, props).await?), - _ => { - return Err(anyhow::anyhow!( - "Unsupported catalog type: '{type}'. Supported types: rest, memory" - )); - } + // Create catalog based on type using the catalog loader. + // "memory" is special-cased because it lives in iceberg core, not in + // one of the catalog crates that the loader registry knows about. + let catalog: Arc = if r#type == "memory" { + Arc::new(MemoryCatalogBuilder::default().load(name, props).await?) + } else { + iceberg_catalog_loader::load(r#type)? + .load(name.to_string(), props) + .await? }; Ok(( @@ -179,7 +178,7 @@ mod tests { let err_msg = result.unwrap_err().to_string(); assert!(err_msg.contains("Unsupported catalog type")); assert!(err_msg.contains("hive")); - assert!(err_msg.contains("rest, memory")); + assert!(err_msg.contains("rest")); } #[tokio::test] @@ -206,4 +205,112 @@ mod tests { assert!(names.contains(&"catalog_one".to_string())); assert!(names.contains(&"catalog_two".to_string())); } + + #[tokio::test] + async fn test_parse_hms_catalog_config_error() { + let config = r#" + [[catalogs]] + name = "test_hms" + type = "hms" + [catalogs.config] + uri = "127.0.0.1:9083" + warehouse = "s3://warehouse/hive" + "#; + + let toml_table: TomlTable = toml::from_str(config).unwrap(); + let result = IcebergCatalogList::parse_table(&toml_table).await; + + assert!(result.is_err()); + } + + #[tokio::test] + async fn test_parse_hms_catalog_missing_uri() { + let config = r#" + [[catalogs]] + name = "test_hms" + type = "hms" + [catalogs.config] + warehouse = "s3://warehouse/hive" + "#; + + let toml_table: TomlTable = toml::from_str(config).unwrap(); + let result = IcebergCatalogList::parse_table(&toml_table).await; + + assert!(result.is_err()); + } + + #[tokio::test] + async fn test_parse_hms_catalog_missing_warehouse() { + let config = r#" + [[catalogs]] + name = "test_hms" + type = "hms" + [catalogs.config] + uri = "127.0.0.1:9083" + "#; + + let toml_table: TomlTable = toml::from_str(config).unwrap(); + let result = IcebergCatalogList::parse_table(&toml_table).await; + + assert!(result.is_err()); + } + + #[tokio::test] + async fn test_parse_catalog_missing_name() { + let config = r#" + [[catalogs]] + type = "memory" + [catalogs.config] + warehouse = "/tmp/warehouse" + "#; + + let toml_table: TomlTable = toml::from_str(config).unwrap(); + let result = IcebergCatalogList::parse_table(&toml_table).await; + + assert!(result.is_err()); + assert!(result.unwrap_err().to_string().contains("name not found")); + } + + #[tokio::test] + async fn test_parse_catalog_missing_type() { + let config = r#" + [[catalogs]] + name = "test_catalog" + [catalogs.config] + warehouse = "/tmp/warehouse" + "#; + + let toml_table: TomlTable = toml::from_str(config).unwrap(); + let result = IcebergCatalogList::parse_table(&toml_table).await; + + assert!(result.is_err()); + assert!(result.unwrap_err().to_string().contains("type not found")); + } + + #[tokio::test] + async fn test_parse_catalog_missing_config() { + let config = r#" + [[catalogs]] + name = "test_catalog" + type = "memory" + "#; + + let toml_table: TomlTable = toml::from_str(config).unwrap(); + let result = IcebergCatalogList::parse_table(&toml_table).await; + + assert!(result.is_err()); + assert!(result.unwrap_err().to_string().contains("config not found")); + } + + #[tokio::test] + async fn test_parse_empty_catalogs() { + let config = r#" + catalogs = [] + "#; + + let toml_table: TomlTable = toml::from_str(config).unwrap(); + let catalog_list = IcebergCatalogList::parse_table(&toml_table).await.unwrap(); + + assert!(catalog_list.catalog_names().is_empty()); + } }