fix(translate): normalize backend dialect detection with explicit mapping

- Replace fragile substring check (any(kw in backend for kw in ('clickhouse', 'ch')))
  with a proper _backend_normalize mapping + exact comparison.
  Fixes misdetection of Greenplum (postgresql) and other backends.
- Add 'clickhousedb' to CLICKHOUSE_DIALECTS so SQL generator uses
  backtick quoting and correct INSERT strategy for ClickHouse.
- Normalize _extract_dialect to map 'clickhousedb' -> 'clickhouse'
  and 'greenplum' -> 'postgresql'.
This commit is contained in:
2026-05-29 14:32:00 +03:00
parent 745c9e7ef2
commit 6743b67605
3 changed files with 40 additions and 5 deletions

View File

@@ -241,8 +241,31 @@ def validate_target_table_schema(
safe_schema = (req.target_schema or "public").replace("'", "''")
safe_table = req.target_table.replace("'", "''")
# Нормализуем backend через маппинг (как в get_dialect_from_database)
# чтобы правильно обработать "clickhousedb" → "clickhouse",
# "greenplum" → "postgresql" и другие варианты
_backend_normalize = {
"clickhouse": "clickhouse",
"clickhousedb": "clickhouse",
"postgresql": "postgresql",
"greenplum": "postgresql",
"mysql": "mysql",
"mssql": "mssql",
"sqlite": "sqlite",
"oracle": "oracle",
"snowflake": "snowflake",
"bigquery": "bigquery",
"redshift": "redshift",
"presto": "presto",
"trino": "trino",
"druid": "druid",
"hive": "hive",
"spark": "spark",
"databricks": "databricks",
}
normalized_backend = _backend_normalize.get(backend.lower().strip(), backend.lower().strip())
# ClickHouse использует system.columns, всё остальное — information_schema.columns
is_clickhouse = any(kw in backend.lower() for kw in ("clickhouse", "ch"))
is_clickhouse = normalized_backend == "clickhouse"
if is_clickhouse:
sql = (
f"SELECT name, type, default_expression AS data_default "

View File

@@ -7,15 +7,27 @@ from ...schemas.translate import TranslateJobResponse
# #region _extract_dialect [TYPE Function]
# @BRIEF Extract database dialect from Superset backend URI.
# @BRIEF Extract database dialect from Superset backend URI or engine name.
def _extract_dialect(backend: str) -> str:
"""Extract dialect name from a Superset database backend URI."""
"""Extract dialect name from a Superset database backend URI or engine name.
Handles both URI formats (e.g. 'postgresql://...', 'clickhousedb://...')
and plain engine names (e.g. 'postgresql', 'clickhousedb') returned by Superset.
Normalises known variants like 'clickhousedb''clickhouse'.
"""
if not backend:
return "unknown"
try:
# Extract scheme from URI or use plain name
scheme = backend.split("://")[0]
dialect = scheme.split("+")[0]
return dialect.lower()
raw = dialect.lower()
# Normalise known Superset backend variants
dialect_map = {
"clickhousedb": "clickhouse",
"greenplum": "postgresql",
}
return dialect_map.get(raw, raw)
except Exception:
return "unknown"
# #endregion _extract_dialect

View File

@@ -18,7 +18,7 @@ from ...core.logger import belief_scope, logger
# PostgreSQL/Greenplum dialects that support ON CONFLICT
POSTGRESQL_DIALECTS = {"postgresql", "redshift", "greenplum"}
# Dialects that use backtick or no quoting
CLICKHOUSE_DIALECTS = {"clickhouse"}
CLICKHOUSE_DIALECTS = {"clickhouse", "clickhousedb"}
# #region _normalize_timestamp_value [C:2] [TYPE Function] [SEMANTICS translate,sql,timestamp]