Skip to content

Commit

Permalink
[MONDRIAN-1599] Virt Cube MDX mishandled w/ non-joining dims
Browse files Browse the repository at this point in the history
Adding support for native evaluation of sets in the context of a
virtual cube in which no cubes "fully join".  That is, if there is no
cube or cubes which are applicable to all dimensions in the native
targets.  For example, with a crossjoin of Warehouse and Gender, no
single cube joins to both.  Formerly native eval logic depended on the
presence of one or more fully joining cubes in order to find tuples,
with the assumption that the absence of a fully joining cube implies
that the resulting set will be empty.  That assumption is incorrect
when ValidMeasure is use (or other MDX expressions which changes
dimensional context).

This commit takes the approach of "grouping" native targets based on
their applicable cubes.  So with the above example, Gender and
Warehouse are grouped and evaluated separately, then crossjoined after
the fact.  This is more expensive than the former logic, which
executed a single UNION'd SQL query with virtual cubes, so to avoid
unnecessary execution this first checks whether one or more
measures could shift context.  Otherwise, if there is no fully joining
cube, then it's safe to return an empty set.

http://jira.pentaho.com/browse/MONDRIAN-1599
http://jira.pentaho.com/browse/MONDRIAN-2280
  • Loading branch information
mkambol committed Jun 17, 2016
1 parent efc65d3 commit 5ee54f9
Show file tree
Hide file tree
Showing 7 changed files with 757 additions and 64 deletions.
7 changes: 7 additions & 0 deletions src/main/mondrian/olap/fun/CrossJoinFunDef.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import mondrian.olap.type.*;
import mondrian.resource.MondrianResource;
import mondrian.rolap.RolapEvaluator;
import mondrian.rolap.SqlConstraintUtils;
import mondrian.server.Execution;
import mondrian.server.Locus;
import mondrian.util.CancellationChecker;
Expand Down Expand Up @@ -776,6 +777,12 @@ protected TupleList nonEmptyList(
Formula[] formula = query.getFormulas();
if (formula != null) {
for (Formula f : formula) {
if (SqlConstraintUtils.containsValidMeasure(
f.getExpression()))
{
// short circuit if VM is present.
return list;
}
f.accept(measureVisitor);
}
}
Expand Down
13 changes: 7 additions & 6 deletions src/main/mondrian/rolap/HighCardSqlTupleReader.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,9 @@
// You must accept the terms of that agreement to use this software.
//
// Copyright (C) 2004-2005 TONBELLER AG
// Copyright (C) 2005-2013 Pentaho and others
// Copyright (C) 2005-2016 Pentaho and others
// All Rights Reserved.
*/

package mondrian.rolap;

import mondrian.calc.TupleList;
Expand Down Expand Up @@ -58,7 +57,8 @@ public void addLevelMembers(
protected void prepareTuples(
final DataSource dataSource,
final TupleList partialResult,
final List<List<RolapMember>> newPartialResult)
final List<List<RolapMember>> newPartialResult,
final List<TargetBase> targetGroup)
{
String message = "Populating member cache with members for " + targets;
SqlStatement stmt = null;
Expand All @@ -75,7 +75,7 @@ protected void prepareTuples(
}
}
final Pair<String, List<SqlStatement.Type>> pair =
makeLevelMembersSql(dataSource);
makeLevelMembersSql(dataSource, targetGroup);
String sql = pair.left;
List<SqlStatement.Type> types = pair.right;
stmt = RolapUtil.executeQuery(
Expand Down Expand Up @@ -136,7 +136,7 @@ public TupleList readMembers(
final TupleList partialResult,
final List<List<RolapMember>> newPartialResult)
{
prepareTuples(dataSource, partialResult, newPartialResult);
prepareTuples(dataSource, partialResult, newPartialResult, targets);

assert targets.size() == 1;

Expand All @@ -149,7 +149,8 @@ public TupleList readTuples(
final TupleList partialResult,
final List<List<RolapMember>> newPartialResult)
{
prepareTuples(jdbcConnection, partialResult, newPartialResult);
prepareTuples(
jdbcConnection, partialResult, newPartialResult, targets);

// List of tuples
final int n = targets.size();
Expand Down
33 changes: 26 additions & 7 deletions src/main/mondrian/rolap/SqlConstraintUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import mondrian.olap.fun.AggregateFunDef;
import mondrian.olap.fun.MemberExtractingVisitor;
import mondrian.olap.fun.ParenthesesFunDef;
import mondrian.olap.fun.ValidMeasureFunDef;
import mondrian.resource.MondrianResource;
import mondrian.rolap.RestrictedMemberReader.MultiCardinalityDefaultMember;
import mondrian.rolap.RolapHierarchy.LimitedRollupMember;
Expand Down Expand Up @@ -2231,7 +2232,18 @@ private static String generateSingleValueInExpr(
public static boolean measuresConflictWithMembers(
Set<Member> measures, Member[] members)
{
Set<Member> membersNestedInMeasures = new HashSet<Member>();
Set<Member> membersNestedInMeasures =
getMembersNestedInMeasures(measures);
for (Member memberInMeasure : membersNestedInMeasures) {
if (!anyMemberOverlaps(members, memberInMeasure)) {
return true;
}
}
return false;
}

public static Set<Member> getMembersNestedInMeasures(Set<Member> measures) {
Set<Member> membersNestedInMeasures = new HashSet<>();
for (Member m : measures) {
if (m.isCalculated()) {
Exp exp = m.getExpression();
Expand All @@ -2240,12 +2252,7 @@ public static boolean measuresConflictWithMembers(
membersNestedInMeasures, null, false));
}
}
for (Member memberInMeasure : membersNestedInMeasures) {
if (!anyMemberOverlaps(members, memberInMeasure)) {
return true;
}
}
return false;
return membersNestedInMeasures;
}

public static boolean measuresConflictWithMembers(
Expand All @@ -2255,6 +2262,18 @@ public static boolean measuresConflictWithMembers(
measuresMembers, getCJArgMembers(cjArgs));
}

public static boolean containsValidMeasure(Exp... expressions) {
for (Exp expression : expressions) {
if (expression instanceof ResolvedFunCall) {
ResolvedFunCall fun = ((ResolvedFunCall) expression);
return fun.getFunDef() instanceof ValidMeasureFunDef
|| containsValidMeasure(fun.getArgs());
}
}
return false;
}


/**
* Compares the array of members against memberInMeasure, returning
* true if any of the members are of the same hierarchy and
Expand Down
Loading

0 comments on commit 5ee54f9

Please sign in to comment.