From 502d73649d268295c40e9e01666b6bdf9051593e Mon Sep 17 00:00:00 2001 From: Chris Eubank <108756251+christianeu-db@users.noreply.github.com> Date: Fri, 18 Sep 2026 21:50:20 +0000 Subject: [PATCH 1/2] fix(databricks): match cascade-drop references case-insensitively Databricks SQL identifiers are case-insensitive, but the cascade-drop match in `_references_dropped` was case-sensitive. A metric such as `COUNT(DISTINCT REGION_NAME)` therefore failed to match a dropped `region_name` and survived as a dangling reference in the emitted Metric View. Add `re.IGNORECASE` to both patterns in `_references_dropped` (the `measure()` pattern and the bare-dimension token pattern). The word-boundary guards are unchanged, so qualified `alias.col` references are still not over-dropped. Adds the reported repro as a regression test that fails without the fix. Fixes #422 Co-authored-by: Isaac --- .../ossie_databricks/ossie_to_metric_view.py | 6 +++-- .../tests/test_ossie_to_metric_view.py | 25 +++++++++++++++++++ 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/converters/databricks/src/ossie_databricks/ossie_to_metric_view.py b/converters/databricks/src/ossie_databricks/ossie_to_metric_view.py index de828bef..4bd538e5 100644 --- a/converters/databricks/src/ossie_databricks/ossie_to_metric_view.py +++ b/converters/databricks/src/ossie_databricks/ossie_to_metric_view.py @@ -590,9 +590,11 @@ def _references_dropped(expr, self_name, dropped_dims, dropped_measures): that merely shares a dropped dimension's name is not over-dropped. The one ambiguity the regex can't resolve without a SQL parser is a bare, unqualified *source column* sharing a dropped dimension's name -- there it errs on dropping. + + Matching is case-insensitive, as Databricks SQL identifiers are case-insensitive. """ for m in dropped_measures: - if re.search(r"measure\(\s*" + re.escape(m) + r"\s*\)", expr): + if re.search(r"measure\(\s*" + re.escape(m) + r"\s*\)", expr, re.IGNORECASE): return m for d in dropped_dims: # Match only a bare, unqualified token: the negative look-behind/ahead for a @@ -600,7 +602,7 @@ def _references_dropped(expr, self_name, dropped_dims, dropped_measures): # qualified paths (`alias.name` / `name.col`), so a join alias or joined # column sharing a dropped name is not falsely cascade-dropped. if d != self_name and re.search( - r"(? Date: Sat, 19 Sep 2026 21:11:39 +0000 Subject: [PATCH 2/2] fix(databricks): exclude function calls from cascade-drop match Address review feedback on #428: - Exclude function-call tokens (`NAME(...)`) from the bare-dimension match, so a dropped dim named like a SQL function (e.g. `count`) does not falsely cascade-drop a surviving `COUNT(...)`. This also fixes the pre-existing exact-case collision. - Case-fold the self-reference guard on both the dimension and `measure()` branches, so a column is never dropped for referencing itself under a different case. - Add regression tests for both (each fails without its fix). Co-authored-by: Isaac --- .../ossie_databricks/ossie_to_metric_view.py | 34 +++++++++----- .../tests/test_ossie_to_metric_view.py | 47 +++++++++++++++++++ 2 files changed, 69 insertions(+), 12 deletions(-) diff --git a/converters/databricks/src/ossie_databricks/ossie_to_metric_view.py b/converters/databricks/src/ossie_databricks/ossie_to_metric_view.py index 4bd538e5..cd722765 100644 --- a/converters/databricks/src/ossie_databricks/ossie_to_metric_view.py +++ b/converters/databricks/src/ossie_databricks/ossie_to_metric_view.py @@ -587,22 +587,32 @@ def _references_dropped(expr, self_name, dropped_dims, dropped_measures): Measures are only referenceable via `measure()` (exact). Dimensions are referenced by their bare, *unqualified* name: a name that is part of a qualified path (`alias.name` or `name.col`) is ignored, so a join alias or joined column - that merely shares a dropped dimension's name is not over-dropped. The one - ambiguity the regex can't resolve without a SQL parser is a bare, unqualified - *source column* sharing a dropped dimension's name -- there it errs on dropping. - - Matching is case-insensitive, as Databricks SQL identifiers are case-insensitive. + that merely shares a dropped dimension's name is not over-dropped. A bare token + immediately followed by `(` is a function/keyword call (e.g. `COUNT(...)`), never + a dimension reference, so it is excluded too. + + Without a real SQL parser the bare-name match still can't tell an identifier from + a same-spelled keyword that is *not* a call (a type in `CAST(x AS DATE)`, a unit + in `EXTRACT(YEAR FROM d)` / `INTERVAL 1 DAY`, `CASE ... END`, `DISTINCT`) or from + text inside a string literal; a dropped dimension named like one of those errs on + dropping. This is the residual the paren-guard does not close. + + Matching is case-insensitive, as Databricks SQL identifiers are case-insensitive; + the self-reference guard is case-folded to match, so a measure or dimension is + never dropped for referencing itself under a different case. """ for m in dropped_measures: - if re.search(r"measure\(\s*" + re.escape(m) + r"\s*\)", expr, re.IGNORECASE): + if m.lower() != self_name.lower() and re.search( + r"measure\(\s*" + re.escape(m) + r"\s*\)", expr, re.IGNORECASE): return m for d in dropped_dims: - # Match only a bare, unqualified token: the negative look-behind/ahead for a - # word char or `.` excludes both substrings of a larger identifier and - # qualified paths (`alias.name` / `name.col`), so a join alias or joined - # column sharing a dropped name is not falsely cascade-dropped. - if d != self_name and re.search( - r"(?