fix(doris): qualify external catalog tables
This commit is contained in:
1 parent
c9c2fddfe2
commit
a9ca66a829
21 files changed
+222
-44
No files matched your search
@@ -72,25 +72,41 @@ pub fn qualified_table_name(database_type: Option<DatabaseType>, schema: Option<
|
||||
}
|
||||
|
||||
/// Like `qualified_table_name`, but prefixes a Doris/StarRocks external
|
||||
/// catalog (`<catalog>.<schema>.<table>`) when `catalog` is present, non-empty,
|
||||
/// and not the engine's `internal` catalog. The `internal` guard is defensive —
|
||||
/// built-in-catalog tables never carry a catalog in the first place (the
|
||||
/// sidebar routes them through the standard path), so this only ever prefixes
|
||||
/// genuine external catalogs. Other engines ignore `catalog` (they have no
|
||||
/// 3-part catalog naming).
|
||||
/// catalog (`<catalog>.<database>.<table>`) when `catalog` is present,
|
||||
/// non-empty, and not the engine's `internal` catalog. The middle segment is
|
||||
/// the database — Doris/StarRocks have no separate schema concept, so
|
||||
/// `schema` is only used when a caller passes it that way; otherwise `database`
|
||||
/// fills the middle slot. When neither is set the name degrades to the 2-part
|
||||
/// `<catalog>.<table>` form. The `internal` guard is defensive — built-in
|
||||
/// catalog tables never carry a catalog in the first place (the sidebar routes
|
||||
/// them through the standard path), so this only ever prefixes genuine
|
||||
/// external catalogs. Other engines ignore `catalog` (they have no 3-part
|
||||
/// catalog naming).
|
||||
pub fn qualified_table_name_with_catalog(
|
||||
database_type: Option<DatabaseType>,
|
||||
catalog: Option<&str>,
|
||||
schema: Option<&str>,
|
||||
database: Option<&str>,
|
||||
table_name: &str,
|
||||
) -> String {
|
||||
let table = qualified_table_name(database_type, schema, table_name);
|
||||
let catalog = catalog.map(str::trim).filter(|catalog| !catalog.is_empty() && *catalog != "internal");
|
||||
match (catalog, database_type) {
|
||||
(Some(catalog), Some(DatabaseType::Doris | DatabaseType::StarRocks)) => {
|
||||
let middle = schema
|
||||
.map(str::trim)
|
||||
.filter(|schema| !schema.is_empty())
|
||||
.or_else(|| database.map(str::trim).filter(|database| !database.is_empty()));
|
||||
let table = match middle {
|
||||
Some(middle) => format!(
|
||||
"{}.{}",
|
||||
quote_table_identifier(database_type, middle),
|
||||
quote_table_identifier(database_type, table_name)
|
||||
),
|
||||
None => quote_table_identifier(database_type, table_name),
|
||||
};
|
||||
format!("{}.{}", quote_table_identifier(database_type, catalog), table)
|
||||
}
|
||||
_ => table,
|
||||
_ => qualified_table_name(database_type, schema, table_name),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -33,6 +33,7 @@ pub fn build_table_data_select_sql(options: TableDataSelectSqlOptions) -> String
|
||||
database_type,
|
||||
options.catalog.as_deref(),
|
||||
options.schema.as_deref(),
|
||||
options.database.as_deref(),
|
||||
&options.table_name,
|
||||
)
|
||||
};
|
||||
@@ -458,13 +459,19 @@ pub(super) fn build_questdb_table_select_sql(
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
fn opts(database_type: DatabaseType, catalog: Option<&str>, table: &str) -> TableDataSelectSqlOptions {
|
||||
fn opts(
|
||||
database_type: DatabaseType,
|
||||
catalog: Option<&str>,
|
||||
database: Option<&str>,
|
||||
table: &str,
|
||||
) -> TableDataSelectSqlOptions {
|
||||
TableDataSelectSqlOptions {
|
||||
database_type: Some(database_type),
|
||||
identifier_quote: None,
|
||||
schema: None,
|
||||
table_name: table.to_string(),
|
||||
catalog: catalog.map(|c| c.to_string()),
|
||||
database: database.map(|d| d.to_string()),
|
||||
table_type: None,
|
||||
primary_keys: Vec::new(),
|
||||
columns: Vec::new(),
|
||||
@@ -479,32 +486,42 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn doris_external_catalog_prefixes_from_clause() {
|
||||
let sql = build_table_data_select_sql(opts(DatabaseType::Doris, Some("iceberg_catalog"), "orders"));
|
||||
assert!(sql.contains("FROM `iceberg_catalog`.`orders`"), "sql was: {sql}");
|
||||
let sql =
|
||||
build_table_data_select_sql(opts(DatabaseType::Doris, Some("iceberg_catalog"), Some("sales"), "orders"));
|
||||
assert!(sql.contains("FROM `iceberg_catalog`.`sales`.`orders`"), "sql was: {sql}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn starrocks_external_catalog_prefixes_from_clause() {
|
||||
let sql = build_table_data_select_sql(opts(DatabaseType::StarRocks, Some("hive_catalog"), "orders"));
|
||||
assert!(sql.contains("FROM `hive_catalog`.`orders`"), "sql was: {sql}");
|
||||
let sql =
|
||||
build_table_data_select_sql(opts(DatabaseType::StarRocks, Some("hive_catalog"), Some("sales"), "orders"));
|
||||
assert!(sql.contains("FROM `hive_catalog`.`sales`.`orders`"), "sql was: {sql}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn doris_external_catalog_without_database_degrades_to_two_part() {
|
||||
// When neither schema nor database is provided the name degrades to the
|
||||
// 2-part `catalog.table` form.
|
||||
let sql = build_table_data_select_sql(opts(DatabaseType::Doris, Some("iceberg_catalog"), None, "orders"));
|
||||
assert!(sql.contains("FROM `iceberg_catalog`.`orders`"), "sql was: {sql}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn doris_internal_catalog_is_not_prefixed() {
|
||||
let sql = build_table_data_select_sql(opts(DatabaseType::Doris, Some("internal"), "orders"));
|
||||
let sql = build_table_data_select_sql(opts(DatabaseType::Doris, Some("internal"), None, "orders"));
|
||||
assert!(!sql.contains("internal"), "sql was: {sql}");
|
||||
assert!(sql.contains("FROM `orders`"), "sql was: {sql}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn doris_empty_catalog_is_not_prefixed() {
|
||||
let sql = build_table_data_select_sql(opts(DatabaseType::Doris, Some(" "), "orders"));
|
||||
let sql = build_table_data_select_sql(opts(DatabaseType::Doris, Some(" "), None, "orders"));
|
||||
assert!(sql.contains("FROM `orders`"), "sql was: {sql}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn doris_no_catalog_is_not_prefixed() {
|
||||
let sql = build_table_data_select_sql(opts(DatabaseType::Doris, None, "orders"));
|
||||
let sql = build_table_data_select_sql(opts(DatabaseType::Doris, None, None, "orders"));
|
||||
assert!(sql.contains("FROM `orders`"), "sql was: {sql}");
|
||||
}
|
||||
|
||||
@@ -512,7 +529,8 @@ mod tests {
|
||||
fn external_catalog_is_ignored_for_non_doris_engines() {
|
||||
// Postgres does not support the 3-part catalog naming; the catalog
|
||||
// must be ignored to avoid emitting an invalid qualified name.
|
||||
let sql = build_table_data_select_sql(opts(DatabaseType::Postgres, Some("iceberg_catalog"), "orders"));
|
||||
let sql =
|
||||
build_table_data_select_sql(opts(DatabaseType::Postgres, Some("iceberg_catalog"), Some("sales"), "orders"));
|
||||
assert!(!sql.contains("iceberg_catalog"), "sql was: {sql}");
|
||||
assert!(sql.contains("orders"), "sql was: {sql}");
|
||||
}
|
||||
|
||||
@@ -28,9 +28,14 @@ pub struct TableDataSelectSqlOptions {
|
||||
pub table_name: String,
|
||||
/// Doris / StarRocks multi-catalog: when set to a non-`internal` catalog,
|
||||
/// the FROM clause is prefixed with the catalog
|
||||
/// (`<catalog>.<schema>.<table>`).
|
||||
/// (`<catalog>.<database>.<table>`).
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub catalog: Option<String>,
|
||||
/// Doris / StarRocks multi-catalog: the database under the external
|
||||
/// catalog, used as the middle segment of the 3-part qualified name when
|
||||
/// `schema` is absent (Doris/StarRocks have no separate schema concept).
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub database: Option<String>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub table_type: Option<String>,
|
||||
#[serde(default)]
|
||||
|
||||
Reference in new issue
Block a user