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, price ≡ GROUP 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.
Problem
A
GROUPproperty with duplicate (textually equal)BYexpressions crashes the logics startup with anAssertionError(or an NPE if-eais disabled). The same logics starts fine on v6, so this is a v6 -> master regression.Minimal repro:
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:
Cause
Since b082332 (#1489) equal
BYexpressions produce equal (hashEquals)PropertyInterfaceImplements. The group property still creates a distinct interface perBYposition (GroupProperty.Interfaceequality is identity-based), but its interface -> key mapping is no longer injective, so everytoRevExclMap()over that mapping now fails its bijection assertion:LogicsModule.mapLGProp— at construction of the resultingLP(the startup crash above);AddGroupProperty.getGroupKeys— the value-pushing optimization on the SUM/MAX calc path;OrderGroupPropertyconstructor (mapSelectTop,getMapRevInterfaces()is called unconditionally, even with an emptySELECT TOP) andOrderGroupProperty.getGroupKeys— the CONCAT/LAST paths.On v6 the equal
BYexpressions 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, price≡GROUP 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.mapLGPropmatches 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 ofgetMapInterfaces, used byAddGroupProperty.getGroupKeys,OrderGroupProperty.getGroupKeysandmapSelectTop. Any of the equal interfaces works there: equal keys read equal group exprs, and both outer equality conditions are still applied byGroupExpr.createthroughjoinImplement.