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(