Skip to content

Commit a52767c

Browse files
committed
Fix join export SQL format and rank measure self-reference
Export join SQL using ${CUBE}.col syntax (not ${model_name}.col) so the importer's FK extraction regex can re-parse it on roundtrip. Remove self-referential SQL fallback for rank measures: store rank metadata only without fabricating a derived type that points to a nonexistent column.
1 parent 17332bd commit a52767c

2 files changed

Lines changed: 11 additions & 10 deletions

File tree

sidemantic/adapters/cube.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -383,8 +383,8 @@ def _parse_measure(self, measure_def: dict, cube_name: str) -> Metric | None:
383383
# Handle unknown measure types explicitly
384384
if agg_type is None and measure_type not in ("number",):
385385
if measure_type == "rank":
386-
# Rank measures: store as derived with rank metadata
387-
metric_type = "derived"
386+
# Rank measures: store metadata only, no agg or derived type
387+
# (rank is a window function concept with no simple SQL mapping)
388388
meta = meta.copy() if meta else {}
389389
meta["cube_type"] = "rank"
390390
meta["order_by"] = measure_def.get("order_by")
@@ -492,10 +492,6 @@ def replace_measure_ref(match):
492492

493493
measure_sql = re.sub(r"\$\{(\w+)\}", replace_measure_ref, measure_sql)
494494

495-
# For rank measures, ensure they have sql to satisfy derived validation
496-
if metric_type == "derived" and not measure_sql:
497-
measure_sql = name
498-
499495
return Metric(
500496
name=name,
501497
type=metric_type,
@@ -931,15 +927,19 @@ def _export_cube(self, model: Model, resolved_models: dict[str, Model]) -> dict:
931927
# Find target model from resolved models (inheritance-applied)
932928
target_model = resolved_models.get(relationship.name)
933929
if target_model:
930+
# Use ${CUBE} for local side and ${target.col} for remote side
931+
# so _extract_fk_from_join_sql can re-parse the exported SQL
934932
if relationship.type in ("many_to_one", "one_to_one"):
935933
local_key = relationship.sql_expr or relationship.foreign_key
936934
remote_key = relationship.primary_key or target_model.primary_key
937-
join_sql = f"${{{model.name}}}.{local_key} = ${{{relationship.name}}}.{remote_key}"
935+
if isinstance(remote_key, list):
936+
remote_key = remote_key[0]
937+
join_sql = f"${{CUBE}}.{local_key} = ${{{relationship.name}}}.{remote_key}"
938938
else:
939-
# one_to_many: swap direction
939+
# one_to_many: ${CUBE}.pk = ${target.fk}
940940
local_key = model.primary_key if isinstance(model.primary_key, str) else model.primary_key[0]
941941
remote_key = relationship.sql_expr or relationship.foreign_key
942-
join_sql = f"${{{model.name}}}.{local_key} = ${{{relationship.name}}}.{remote_key}"
942+
join_sql = f"${{CUBE}}.{local_key} = ${{{relationship.name}}}.{remote_key}"
943943

944944
join_def = {
945945
"name": relationship.name,

tests/adapters/cube/test_fixtures.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -456,9 +456,10 @@ def test_rank_not_count(self, graph):
456456
product_rank = rank_model.get_metric("product_rank")
457457
assert product_rank is not None
458458
assert product_rank.agg != "count"
459-
assert product_rank.type == "derived"
460459
assert product_rank.meta is not None
461460
assert product_rank.meta.get("cube_type") == "rank"
461+
assert product_rank.meta.get("order_by") is not None
462+
assert product_rank.meta.get("reduce_by") is not None
462463

463464
def test_ranking_measures(self, graph):
464465
rank_model = graph.get_model("ranking")

0 commit comments

Comments
 (0)