Skip to content

GROUP with duplicate (equal) BY expressions fails with AssertionError (v6 -> master regression) #1704

Description

@ichavchavadze

Problem

A GROUP property with duplicate (textually equal) BY expressions crashes the logics startup with an AssertionError (or an NPE if -ea is disabled). The same logics starts fine on v6, so this is a v6 -> master regression.

Minimal repro:

CLASS Order;
price = DATA NUMERIC[10,2] (Order);
quantity = DATA NUMERIC[10,2] (Order);

quantityOut(NUMERIC[10,2] p, NUMERIC[10,2] c) = GROUP SUM quantity(Order o) BY price(o), price(o);
java.lang.AssertionError
	at lsfusion.base.col.implementations.abs.AMap.toRevExclMap(...)
	at lsfusion.server.logics.LogicsModule.mapLGProp(...)
	at lsfusion.server.logics.LogicsModule.addSGProp(...)

Real-world case: a report property grouping by a price expression that is (legitimately) passed twice — once as the price output and once as the cost output:

quantityOut(Article a, Sort s, NUMERIC[7,2] p, NUMERIC[16,5] c) <-
    [GROUP SUM Sale.quantity(Sale.ShipmentDetail l) IF ... BY article(Sale.sku(l)), sort(Sale.sku(l)), Sale.price(l), Sale.price(l)](a, s, p, c);

Cause

Since b082332 (#1489) equal BY expressions produce equal (hashEquals) PropertyInterfaceImplements. The group property still creates a distinct interface per BY position (GroupProperty.Interface equality is identity-based), but its interface -> key mapping is no longer injective, so every toRevExclMap() over that mapping now fails its bijection assertion:

  • LogicsModule.mapLGProp — at construction of the resulting LP (the startup crash above);
  • AddGroupProperty.getGroupKeys — the value-pushing optimization on the SUM/MAX calc path;
  • OrderGroupProperty constructor (mapSelectTop, getMapRevInterfaces() is called unconditionally, even with an empty SELECT TOP) and OrderGroupProperty.getGroupKeys — the CONCAT/LAST paths.

On v6 the equal BY expressions were distinct objects, so all these reverse maps stayed bijective by accident.

Semantically the duplicate keys are valid and were supported on v6: GROUP BY price, priceGROUP BY price, with both outer interfaces reading (and being equated to) the same group column.

Fix

Make the group machinery tolerant to duplicate (equal, and therefore interchangeable) group keys instead of requiring bijection:

  • LogicsModule.mapLGProp matches the interfaces to the implement list positionally when duplicates are present (the plain bijective path is kept otherwise);
  • GroupProperty.getMergeMapRevInterfaces() — a merge-based (non-exclusive) reverse of getMapInterfaces, used by AddGroupProperty.getGroupKeys, OrderGroupProperty.getGroupKeys and mapSelectTop. Any of the equal interfaces works there: equal keys read equal group exprs, and both outer equality conditions are still applied by GroupExpr.create through joinImplement.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    Priority

    None yet

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions