diff --git a/pkg-r/R/catalog-snowflake.R b/pkg-r/R/catalog-snowflake.R index a52b89f0..a2f25221 100644 --- a/pkg-r/R/catalog-snowflake.R +++ b/pkg-r/R/catalog-snowflake.R @@ -680,9 +680,11 @@ snowflake_semantic_dependencies <- function(id, specification) { complete <- TRUE for (table in semantic_spec_entries(specification$tables)) { base <- table$base_table %||% list() - table_name <- base$table - catalog <- base$database %||% base$catalog %||% model[["catalog"]] - schema <- base$schema %||% model[["schema"]] + # If base_table has no database or schema, use the view's as-is. + table_name <- snowflake_yaml_identifier(base$table) + catalog <- snowflake_yaml_identifier(base$database %||% base$catalog) %||% + model[["catalog"]] + schema <- snowflake_yaml_identifier(base$schema) %||% model[["schema"]] if ( !rlang::is_string(catalog) || !rlang::is_string(schema) || @@ -706,6 +708,17 @@ snowflake_semantic_dependencies <- function(id, specification) { list(ids = ids[!duplicated(keys)], complete = complete && length(ids) > 0L) } +# Strip quotes from a quoted name ("" is one quote); upper-case others (see #446). +snowflake_yaml_identifier <- function(name) { + if (!rlang::is_string(name)) { + return(NULL) + } + if (grepl('^".*"$', name)) { + return(gsub('""', '"', substr(name, 2L, nchar(name) - 1L), fixed = TRUE)) + } + toupper(name) +} + snowflake_semantic_context <- function( specification, dimensions, diff --git a/pkg-r/tests/testthat/test-catalog-snowflake.R b/pkg-r/tests/testthat/test-catalog-snowflake.R index cdb8456f..4f3778d0 100644 --- a/pkg-r/tests/testthat/test-catalog-snowflake.R +++ b/pkg-r/tests/testthat/test-catalog-snowflake.R @@ -93,6 +93,35 @@ test_that("Snowflake semantic YAML imports only public dimensions and metrics", expect_equal(model$metrics[[1]]$parent, "orders") }) +test_that("Snowflake semantic dependencies read base tables as SQL identifiers", { + # The YAML gives each name as written in SQL, quotes and all. + specification <- list(tables = list( + list(base_table = list( + database = '"Commons"', + schema = "live", + table = '"order ""lines"""' + )) + )) + id <- DBI::Id(catalog = "COMMONS", schema = "LIVE", table = "MODEL") + + model <- snowflake_semantic_model_from_spec(id, specification) + + expect_identical( + model$dependencies[[1]], + DBI::Id(catalog = "Commons", schema = "LIVE", table = 'order "lines"') + ) + + # A namespace the YAML leaves out is the view's own, which is exact. + inherited <- snowflake_semantic_model_from_spec( + DBI::Id(catalog = "MyDb", schema = "MySchema", table = "MODEL"), + list(tables = list(list(base_table = list(table = "orders")))) + ) + expect_identical( + inherited$dependencies[[1]], + DBI::Id(catalog = "MyDb", schema = "MySchema", table = "ORDERS") + ) +}) + test_that("Snowflake semantic scope retains dependencies and public context", { specification <- list( custom_instructions = "Use booked revenue for finance questions.",