You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ossie.apache.org by jb...@apache.org on 2026/09/23 12:18:54 UTC
(ossie) branch main updated: fix(orionbelt): read OSSIE_SQL_2026 metric expressions (#443)
This is an automated email from the ASF dual-hosted git repository.
jbonofre pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/ossie.git
The following commit(s) were added to refs/heads/main by this push:
new e6fe5208 fix(orionbelt): read OSSIE_SQL_2026 metric expressions (#443)
e6fe5208 is described below
commit e6fe52088e0e8940c219b1d00b529e0e8a3841f1
Author: Ralfo Becher <ra...@users.noreply.github.com>
AuthorDate: Wed Sep 23 14:18:48 2026 +0200
fix(orionbelt): read OSSIE_SQL_2026 metric expressions (#443)
* fix(orionbelt): read OSSIE_SQL_2026 metric expressions
A metric whose only expression was tagged OSSIE_SQL_2026 was preserved
as unconverted with a "no SQL-parseable dialect" LOSSY warning, because
_SQL_PARSEABLE_DIALECTS predates the dialect. It is the spec's portable,
ANSI-compatible expression language, so it is now read like ANSI_SQL and
preferred after it, before the SNOWFLAKE / DATABRICKS vendor dialects.
The warning text is built from the tuple so it cannot drift again.
Part of #442.
Co-Authored-By: Claude Opus 5 (1M context) <no...@anthropic.com>
* docs(orionbelt): list OSSIE_SQL_2026 among the dialects read on import
Co-Authored-By: Claude Opus 5 (1M context) <no...@anthropic.com>
---------
Co-authored-by: Claude Opus 5 (1M context) <no...@anthropic.com>
---
converters/orionbelt/README.md | 2 +-
.../orionbelt/ossie_obml_mapping_analysis.md | 2 +-
.../orionbelt/src/ossie_orionbelt/_common.py | 12 +++++----
.../orionbelt/src/ossie_orionbelt/ossie_to_obml.py | 17 +++++++------
.../tests/test_ossie_metric_no_silent_loss.py | 29 ++++++++++++++++++++++
5 files changed, 48 insertions(+), 14 deletions(-)
diff --git a/converters/orionbelt/README.md b/converters/orionbelt/README.md
index 15e590b8..fb947c95 100644
--- a/converters/orionbelt/README.md
+++ b/converters/orionbelt/README.md
@@ -129,7 +129,7 @@ OBML, but are not interpreted by other Ossie consumers:
(`obml_unconverted_metrics`) and re-emitted on OBML to Ossie, so the Ossie to OBML
to Ossie roundtrip stays lossless. A `LOSSY:` warning is raised for each such
metric because it is **not queryable through OBML**. SQL expressions in the
- `ANSI_SQL`, `SNOWFLAKE`, and `DATABRICKS` dialects are all read on import.
+ `ANSI_SQL`, `OSSIE_SQL_2026`, `SNOWFLAKE`, and `DATABRICKS` dialects are all read on import.
Ossie v0.1.x inputs are accepted on read via a legacy normalization shim; output
targets Ossie **v0.2.0.dev0**.
diff --git a/converters/orionbelt/ossie_obml_mapping_analysis.md b/converters/orionbelt/ossie_obml_mapping_analysis.md
index 08df96ce..4fd90d3c 100644
--- a/converters/orionbelt/ossie_obml_mapping_analysis.md
+++ b/converters/orionbelt/ossie_obml_mapping_analysis.md
@@ -149,7 +149,7 @@ These OBML features have no direct Ossie equivalent. Where possible, metadata is
- **`primary_key`** — natively represented: Ossie's dataset-level `primary_key` array maps to per-column `primaryKey: true` on OBML columns (`DataObjectColumn.primaryKey`), and back to the dataset array on export.
- **`unique_keys`** — no native OBML equivalent; round-trips via an `Ossie`-vendor `customExtension` (`obml_unique_keys`).
-- **Multi-dialect expressions** — on import the converter reads the first available SQL dialect in the order `ANSI_SQL`, `SNOWFLAKE`, `DATABRICKS`; non-SQL dialects (`MDX`, `TABLEAU`, `MAQL`) are not parsed. A metric with no SQL-parseable dialect, or an expression OBML cannot decompose, is preserved verbatim (`obml_unconverted_metrics`) with a `LOSSY:` warning rather than dropped. On export, OBML measures/metrics emit `ANSI_SQL`.
+- **Multi-dialect expressions** — on import the converter reads the first available SQL dialect in the order `ANSI_SQL`, `OSSIE_SQL_2026`, `SNOWFLAKE`, `DATABRICKS`; non-SQL dialects (`MDX`, `TABLEAU`, `MAQL`, `SIGMA`, `THOUGHTSPOT`, `DAX`) are not parsed. A metric with no SQL-parseable dialect, or an expression OBML cannot decompose, is preserved verbatim (`obml_unconverted_metrics`) with a `LOSSY:` warning rather than dropped. On export, OBML measures/metrics emit `ANSI_SQL`.
- **`ai_context`** — preserved losslessly via `customExtensions` (see Section 2.4).
- **`custom_extensions`** — mapped to OBML `customExtensions`.
diff --git a/converters/orionbelt/src/ossie_orionbelt/_common.py b/converters/orionbelt/src/ossie_orionbelt/_common.py
index 9d4a5412..bfe04922 100644
--- a/converters/orionbelt/src/ossie_orionbelt/_common.py
+++ b/converters/orionbelt/src/ossie_orionbelt/_common.py
@@ -34,11 +34,13 @@ import re
_OSSIE_VERSION = "0.2.0.dev0"
# SQL dialects (of the Ossie enum) whose aggregation expressions our regex-based
-# metric parser can read, in preference order. ANSI_SQL first; SNOWFLAKE and
-# DATABRICKS are SQL engines OrionBelt also targets, and their simple/expression
-# aggregations (``SUM(t.c)``, ``SUM(t.a * t.b)``) are syntactically identical to
-# ANSI. MDX / TABLEAU / MAQL are non-SQL languages and are never parsed as SQL.
-_SQL_PARSEABLE_DIALECTS = ("ANSI_SQL", "SNOWFLAKE", "DATABRICKS")
+# metric parser can read, in preference order. ANSI_SQL first, then
+# OSSIE_SQL_2026 (the spec's portable, ANSI-compatible expression language);
+# SNOWFLAKE and DATABRICKS are SQL engines OrionBelt also targets, and their
+# simple/expression aggregations (``SUM(t.c)``, ``SUM(t.a * t.b)``) are
+# syntactically identical to ANSI. Non-SQL languages (MDX, TABLEAU, MAQL, SIGMA,
+# THOUGHTSPOT, DAX) are never parsed as SQL.
+_SQL_PARSEABLE_DIALECTS = ("ANSI_SQL", "OSSIE_SQL_2026", "SNOWFLAKE", "DATABRICKS")
# Matches a ``dataset.column`` reference inside a SQL expression, where each
# side is a bare identifier or a quoted identifier (double quotes, backticks, or
diff --git a/converters/orionbelt/src/ossie_orionbelt/ossie_to_obml.py b/converters/orionbelt/src/ossie_orionbelt/ossie_to_obml.py
index 72bf28be..5f825792 100644
--- a/converters/orionbelt/src/ossie_orionbelt/ossie_to_obml.py
+++ b/converters/orionbelt/src/ossie_orionbelt/ossie_to_obml.py
@@ -724,14 +724,17 @@ class OssietoOBML:
measures[name] = delegated
continue
- # Prefer ANSI_SQL, but also read SNOWFLAKE / DATABRICKS expressions
- # (SQL engines OrionBelt targets) — their aggregations are
- # syntactically ANSI-compatible. Non-SQL dialects (MDX/TABLEAU/MAQL)
- # are not parsed as SQL.
+ # Prefer ANSI_SQL, but also read OSSIE_SQL_2026 and SNOWFLAKE /
+ # DATABRICKS expressions - their aggregations are syntactically
+ # ANSI-compatible. Non-SQL dialects (MDX/TABLEAU/MAQL/...) are not
+ # parsed as SQL.
expr_text, _expr_dialect = self._select_sql_expression(m.get("expression", {}))
if not expr_text:
self._preserve_unconverted_metric(
- m, "no SQL-parseable dialect (ANSI_SQL / SNOWFLAKE / DATABRICKS) expression"
+ m,
+ "no SQL-parseable dialect ("
+ + " / ".join(_SQL_PARSEABLE_DIALECTS)
+ + ") expression",
)
continue
@@ -996,8 +999,8 @@ class OssietoOBML:
"""Pick a SQL-parseable expression from an Ossie ``expression`` object.
Returns ``(expression, dialect)`` for the most preferred SQL dialect
- present (ANSI_SQL > SNOWFLAKE > DATABRICKS), or ``("", "")`` when the
- metric only carries non-SQL dialects (MDX / TABLEAU / MAQL) or no usable
+ present (ANSI_SQL > OSSIE_SQL_2026 > SNOWFLAKE > DATABRICKS), or
+ ``("", "")`` when the metric only carries non-SQL dialects or no usable
expression. Catching SNOWFLAKE / DATABRICKS lets third-party models
whose authors omitted ANSI_SQL still convert, since their aggregation
syntax is ANSI-compatible.
diff --git a/converters/orionbelt/tests/test_ossie_metric_no_silent_loss.py b/converters/orionbelt/tests/test_ossie_metric_no_silent_loss.py
index e46ad84c..a10e5f2f 100644
--- a/converters/orionbelt/tests/test_ossie_metric_no_silent_loss.py
+++ b/converters/orionbelt/tests/test_ossie_metric_no_silent_loss.py
@@ -93,6 +93,18 @@ class TestDialectCatching:
assert "Net" in obml.get("measures", {})
assert not any(w.startswith("LOSSY:") for w in converter.warnings)
+ def test_ossie_sql_2026_only_agg_becomes_measure(self) -> None:
+ # OSSIE_SQL_2026 is the spec's portable ANSI-compatible language; a
+ # metric expressed only in it must convert, not be preserved as lossy.
+ ossie = _ossie_model([_metric("Total Amount", "OSSIE_SQL_2026", "SUM(Orders.amount)")])
+ converter = conv.OssietoOBML(ossie)
+ obml = converter.convert()
+
+ measure = obml["measures"]["Total Amount"]
+ assert measure["aggregation"] == "sum"
+ assert measure["columns"][0] == {"dataObject": "Orders", "column": "amount"}
+ assert not any(w.startswith("LOSSY:") for w in converter.warnings)
+
def test_snowflake_uppercased_identifiers_resolve_to_canonical(self) -> None:
# Snowflake commonly upper-cases identifiers. They must resolve back to
# the real Ossie dataset/field names, not produce refs to ORDERS.AMOUNT.
@@ -173,6 +185,23 @@ class TestDialectCatching:
# ANSI_SQL wins regardless of ordering -> column is `id`, not `amount`.
assert obml["measures"]["Total Amount"]["columns"][0]["column"] == "id"
+ def test_ossie_sql_2026_preferred_over_vendor_dialects(self) -> None:
+ ossie = _ossie_model(
+ [
+ {
+ "name": "Total Amount",
+ "expression": {
+ "dialects": [
+ {"dialect": "SNOWFLAKE", "expression": "SUM(Orders.amount)"},
+ {"dialect": "OSSIE_SQL_2026", "expression": "SUM(Orders.id)"},
+ ]
+ },
+ }
+ ]
+ )
+ obml = conv.OssietoOBML(ossie).convert()
+ assert obml["measures"]["Total Amount"]["columns"][0]["column"] == "id"
+
class TestNoSilentLoss:
"""Non-convertible metrics are preserved + warned, never dropped."""