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/27 09:57:55 UTC
(ossie) branch main updated: fix(sigma): record dropped keys when a relationship's column arrays have unequal length (#406)
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 744b4055 fix(sigma): record dropped keys when a relationship's column arrays have unequal length (#406)
744b4055 is described below
commit 744b4055149d80dece8d4524883427ce354a47d4
Author: Amir Fathi <am...@gmail.com>
AuthorDate: Sun Sep 27 03:57:50 2026 -0600
fix(sigma): record dropped keys when a relationship's column arrays have unequal length (#406)
* fix(sigma): record dropped keys when a relationship's column arrays have unequal length
from_columns and to_columns are only constrained independently in the OSI JSON
schema (each just needs at least one entry), so a compound-key relationship with
unequal-length arrays is legal input. _build_relationship paired them with a bare
zip(), which stops at the shorter array and drops the extra key column(s) with no
warning at all.
Thread the issues list into _build_relationship and record a
RELATIONSHIP_COLUMN_ARITY_MISMATCH issue when the lengths differ, mirroring the
RELATIONSHIP_COLUMN_UNRESOLVED pattern already used for the reverse direction. The
converter still degrades to the shorter pairing (Sigma's own keys array has no way
to represent an unequal-length join), but the loss is now visible in the returned
ConverterResult.issues instead of silent.
* fix(sigma): scope arity-mismatch element_name by owning dataset
Relationship identity is already scoped by (dataset_name, rel.name);
the arity-mismatch ConverterIssue used the bare relationship name,
so two same-named relationships on different table pairs became
indistinguishable when both hit the mismatch.
* fix(sigma): update arity-mismatch tests for the flat OssieDocument shape
Rebased onto main after #396 (flat semantic model documents). The two new
tests from the previous commits still built OssieDocument via the retired
semantic_model=[OssieSemanticModel(...)] wrapper, which now raises a
ValidationError, so both were failing with NameError: name
'OssieSemanticModel' is not defined. Rewritten to the flat
OssieDocument(name=..., datasets=..., relationships=...) shape used
elsewhere in this file.
---
.../sigma/src/ossie_sigma/converter_issues.py | 1 +
converters/sigma/src/ossie_sigma/ossie_to_sigma.py | 15 ++++-
converters/sigma/tests/test_ossie_to_sigma.py | 76 ++++++++++++++++++++++
3 files changed, 91 insertions(+), 1 deletion(-)
diff --git a/converters/sigma/src/ossie_sigma/converter_issues.py b/converters/sigma/src/ossie_sigma/converter_issues.py
index 7ac34d49..46fdfb2c 100644
--- a/converters/sigma/src/ossie_sigma/converter_issues.py
+++ b/converters/sigma/src/ossie_sigma/converter_issues.py
@@ -26,6 +26,7 @@ class ConverterIssueType(Enum):
UNSUPPORTED_ELEMENT_KIND = "UNSUPPORTED_ELEMENT_KIND"
EXPRESSION_NOT_TRANSLATABLE = "EXPRESSION_NOT_TRANSLATABLE"
RELATIONSHIP_COLUMN_UNRESOLVED = "RELATIONSHIP_COLUMN_UNRESOLVED"
+ RELATIONSHIP_COLUMN_ARITY_MISMATCH = "RELATIONSHIP_COLUMN_ARITY_MISMATCH"
UNIQUE_KEY_COLUMN_UNRESOLVED = "UNIQUE_KEY_COLUMN_UNRESOLVED"
DERIVED_ELEMENT_NOT_MODELED = "DERIVED_ELEMENT_NOT_MODELED"
FILTER_NOT_MODELED = "FILTER_NOT_MODELED"
diff --git a/converters/sigma/src/ossie_sigma/ossie_to_sigma.py b/converters/sigma/src/ossie_sigma/ossie_to_sigma.py
index 3753aa70..453cd648 100644
--- a/converters/sigma/src/ossie_sigma/ossie_to_sigma.py
+++ b/converters/sigma/src/ossie_sigma/ossie_to_sigma.py
@@ -281,7 +281,7 @@ class OssieToSigmaConverter:
relationships = relationships_by_element.get(element_id, [])
if relationships:
element["relationships"] = [
- self._build_relationship(r, dataset.name, dataset_element_id, field_ids) for r in relationships
+ self._build_relationship(r, dataset.name, dataset_element_id, field_ids, issues) for r in relationships
]
return element
@@ -348,6 +348,7 @@ class OssieToSigmaConverter:
dataset_name: str,
dataset_element_id: dict[str, str],
field_ids: dict[str, str],
+ issues: list[ConverterIssue],
) -> dict[str, Any]:
ext = _sigma_ext(rel) or {}
target_element_id = dataset_element_id.get(rel.to, rel.to)
@@ -367,6 +368,18 @@ class OssieToSigmaConverter:
if raw_keys is not None:
result["keys"] = raw_keys
else:
+ if len(rel.from_columns) != len(rel.to_columns):
+ # zip() below stops at the shorter array; record what it drops.
+ issues.append(
+ ConverterIssue(
+ ConverterIssueType.RELATIONSHIP_COLUMN_ARITY_MISMATCH,
+ f"{dataset_name}.{rel.name}",
+ f"from_columns ({len(rel.from_columns)}) and to_columns "
+ f"({len(rel.to_columns)}) have different lengths; the "
+ f"{abs(len(rel.from_columns) - len(rel.to_columns))} extra "
+ "key column(s) were dropped from the Sigma relationship.",
+ )
+ )
result["keys"] = [
{
"sourceColumnId": field_ids.get(from_col, from_col),
diff --git a/converters/sigma/tests/test_ossie_to_sigma.py b/converters/sigma/tests/test_ossie_to_sigma.py
index da6ff383..3e1495f0 100644
--- a/converters/sigma/tests/test_ossie_to_sigma.py
+++ b/converters/sigma/tests/test_ossie_to_sigma.py
@@ -140,6 +140,82 @@ def test_legacy_semantic_model_wrapper_is_rejected():
OssieDocument(semantic_model=[])
+def test_relationship_column_arity_mismatch_is_recorded_not_silently_truncated():
+ """from_columns/to_columns are independently constrained in the OSI schema (each
+ only needs to be non-empty), so a compound-key relationship with unequal lengths
+ is legal input. zip() truncates to the shorter array; that must be a recorded
+ issue, not a silent drop of the extra key column(s)."""
+ document = OssieDocument(
+ name="m",
+ datasets=[
+ OssieDataset(name="orders", source="db.public.orders"),
+ OssieDataset(name="regions", source="db.public.regions"),
+ ],
+ relationships=[
+ OssieRelationship(
+ name="OrderRegion",
+ **{"from": "orders"},
+ to="regions",
+ from_columns=["region_id", "sub_id"],
+ to_columns=["region_id"],
+ ),
+ ],
+ )
+
+ result = OssieToSigmaConverter().convert(document)
+
+ issue_types = {i.issue_type for i in result.issues}
+ assert ConverterIssueType.RELATIONSHIP_COLUMN_ARITY_MISMATCH in issue_types
+
+ rel = next(
+ r for p in result.output["pages"] for e in p["elements"] for r in e.get("relationships", [])
+ )
+ # The mismatch is still recorded rather than crashing the conversion, but only
+ # one key pair can be formed from a 2-vs-1 mismatch.
+ assert len(rel["keys"]) == 1
+
+
+def test_relationship_arity_mismatch_element_names_are_scoped_by_owning_dataset():
+ """Relationship identity is already scoped by (dataset_name, rel.name) (see
+ test_relationship_ids_are_scoped_by_owning_dataset); an arity-mismatch issue's
+ element_name must be scoped the same way, or two unrelated relationships sharing a
+ name on different table pairs become indistinguishable in the issue list."""
+ document = OssieDocument(
+ name="m",
+ datasets=[
+ OssieDataset(name="orders", source="db.public.orders"),
+ OssieDataset(name="customers", source="db.public.customers"),
+ OssieDataset(name="shipments", source="db.public.shipments"),
+ OssieDataset(name="carriers", source="db.public.carriers"),
+ ],
+ relationships=[
+ OssieRelationship(
+ name="Parent",
+ **{"from": "orders"},
+ to="customers",
+ from_columns=["region_id", "sub_id"],
+ to_columns=["region_id"],
+ ),
+ OssieRelationship(
+ name="Parent",
+ **{"from": "shipments"},
+ to="carriers",
+ from_columns=["region_id", "sub_id"],
+ to_columns=["region_id"],
+ ),
+ ],
+ )
+
+ result = OssieToSigmaConverter().convert(document)
+
+ arity_issues = [
+ i for i in result.issues if i.issue_type == ConverterIssueType.RELATIONSHIP_COLUMN_ARITY_MISMATCH
+ ]
+ assert len(arity_issues) == 2
+ element_names = {i.element_name for i in arity_issues}
+ assert len(element_names) == 2, "arity-mismatch issues for same-named relationships must not collide"
+
+
def test_model_level_metadata_round_trips_through_ossie_and_back():
spec = load_fixture("fixtureA_sigma.json")
spec.update(