Skip to content

Commit

Permalink
MONDRIAN - Fixed changes in 8146 so native sql optimization is not used
Browse files Browse the repository at this point in the history
       if the members function is applied on a non-conforming dimension in
       the virtual cube

[git-p4: depot-paths = "//open/mondrian/": change = 8155]
  • Loading branch information
Zelaine Fong committed Nov 15, 2006
1 parent 1635a87 commit d3a4760
Show file tree
Hide file tree
Showing 5 changed files with 82 additions and 59 deletions.
61 changes: 16 additions & 45 deletions src/main/mondrian/rolap/RolapNativeCrossJoin.java
Expand Up @@ -69,64 +69,35 @@ NativeEvaluator createEvaluator(RolapEvaluator evaluator, FunDef fun, Exp[] args
if (!isEnabled())
return null;
RolapCube cube = (RolapCube) evaluator.getCube();
if (!NonEmptyCrossJoinConstraint.isValidContext(evaluator, false) ||
(cube.isVirtual() &&
!evaluator.getQuery().nativeCrossJoinVirtualCube()))
{
return null;
}

// join with fact table will always filter out those members
// that dont have a row in the fact table
if (!evaluator.isNonEmpty())
return null;


CrossJoinArg[] cargs = checkCrossJoin(fun, args);
if (cargs == null)
return null;
if (isPreferInterpreter(cargs))
return null;

if (cube.isVirtual() &&
!validCrossJoinLevels(evaluator.getQuery(), cargs))
RolapLevel [] levels = new RolapLevel[cargs.length];
for (int i = 0; i < args.length; i++) {
levels[i] = cargs[i].getLevel();
}
if ((cube.isVirtual() &&
!evaluator.getQuery().nativeCrossJoinVirtualCube()) ||
!NonEmptyCrossJoinConstraint.isValidContext(
evaluator,
false,
levels))
{
return null;
}

// join with fact table will always filter out those members
// that dont have a row in the fact table
if (!evaluator.isNonEmpty())
return null;

LOGGER.debug("using native crossjoin");

TupleConstraint constraint = new NonEmptyCrossJoinConstraint(cargs, evaluator);
SchemaReader schemaReader = evaluator.getSchemaReader();
return new SetEvaluator(cargs, schemaReader, constraint);
}

/**
* Determines if the levels referenced in the cross join all join with the
* underlying fact tables that make up a virtual cube.
*
* @param query query containing the cross join
* @param cargs arguments to the cross join
*
* @return true if all levels join with the fact tables
*/
private boolean validCrossJoinLevels(Query query, CrossJoinArg[] cargs)
{
Set baseCubesLevelToColumnMaps = query.getVirtualCubeBaseCubeMaps();

// we need to make sure all the levels join with each fact table;
// otherwise, it doesn't make sense to do the processing
// natively, as you'll end up with cartesian product joins!
for (Iterator it = baseCubesLevelToColumnMaps.iterator();
it.hasNext(); )
{
Map map = (Map) it.next();
for (int i = 0; i < cargs.length; i++) {
RolapLevel level = cargs[i].getLevel();
if (map.get(level) == null) {
return false;
}
}
}
return true;
}
}
4 changes: 3 additions & 1 deletion src/main/mondrian/rolap/RolapSchemaReader.java
Expand Up @@ -348,7 +348,9 @@ public Member[] getLevelMembers(Level level, boolean includeCalculated) {

public Member[] getLevelMembers(Level level, Evaluator context) {
TupleConstraint constraint =
sqlConstraintFactory.getLevelMembersConstraint(context);
sqlConstraintFactory.getLevelMembersConstraint(
context,
new Level [] { level });
final MemberReader memberReader = getMemberReader(level.getHierarchy());
final List membersInLevel = memberReader.getMembersInLevel(
(RolapLevel) level, 0, Integer.MAX_VALUE, constraint);
Expand Down
12 changes: 11 additions & 1 deletion src/main/mondrian/rolap/SqlConstraintFactory.java
Expand Up @@ -11,6 +11,7 @@
import java.util.List;

import mondrian.olap.Evaluator;
import mondrian.olap.Level;
import mondrian.olap.MondrianProperties;
import mondrian.rolap.sql.MemberChildrenConstraint;
import mondrian.rolap.sql.TupleConstraint;
Expand Down Expand Up @@ -42,8 +43,17 @@ public MemberChildrenConstraint getMemberChildrenConstraint(Evaluator context) {
}

public TupleConstraint getLevelMembersConstraint(Evaluator context) {
if (!enabled || !SqlContextConstraint.isValidContext(context, false))
return getLevelMembersConstraint(context, null);
}

public TupleConstraint getLevelMembersConstraint(
Evaluator context,
Level [] levels) {
if (!enabled ||
!SqlContextConstraint.isValidContext(context, false, levels))
{
return DefaultTupleConstraint.instance();
}
return new SqlContextConstraint((RolapEvaluator) context, false);
}

Expand Down
53 changes: 41 additions & 12 deletions src/main/mondrian/rolap/SqlContextConstraint.java
Expand Up @@ -46,18 +46,21 @@ public class SqlContextConstraint implements MemberChildrenConstraint,
* @return false if this contstraint will not work for the current context
*/
public static boolean isValidContext(Evaluator context) {
return isValidContext(context, true);
return isValidContext(context, true, null);
}

/**
* @param context evaluation context
* @param disallowVirtualCube if true, check for virtual cubes
* @param levels levels being referenced in the current context
*
* @return false if constraint will not work for current context
*/
public static boolean isValidContext(
Evaluator context, boolean disallowVirtualCube) {

Evaluator context,
boolean disallowVirtualCube,
Level [] levels)
{
if (context == null) {
return false;
}
Expand All @@ -67,10 +70,33 @@ public static boolean isValidContext(
return false;
}
}
if (cube.isVirtual() &&
!findVirtualCubeJoinLevels(context.getQuery()))
{
return false;
if (cube.isVirtual()) {
Query query = context.getQuery();
Set baseCubesLevelToColumnMaps = new HashSet();
Map measureMap = new HashMap();
if (!findVirtualCubeJoinLevels(
query,
baseCubesLevelToColumnMaps,
measureMap))
{
return false;
}
assert(levels != null);
// we need to make sure all the levels join with each fact table;
// otherwise, it doesn't make sense to do the processing
// natively, as you'll end up with cartesian product joins!
for (Iterator it = baseCubesLevelToColumnMaps.iterator();
it.hasNext(); )
{
Map map = (Map) it.next();
for (int i = 0; i < levels.length; i++) {
if (map.get(levels[i]) == null) {
return false;
}
}
}
query.setVirtualCubeBaseCubeMaps(baseCubesLevelToColumnMaps);
query.setLevelMapToMeasureMap(measureMap);
}
return true;
}
Expand All @@ -80,16 +106,21 @@ public static boolean isValidContext(
* virtual cube by validating the measures referenced in the query.
*
* @param query query referencing the virtual cube
* @param baseCubesLevelToColumnMaps level to column maps corresponding
* to the base cubes referenced from the virtual cube
* @param measureMap mapping between a measure and the level to column
* maps
*
* @return true if valid measures exist
*/
private static boolean findVirtualCubeJoinLevels(Query query)
private static boolean findVirtualCubeJoinLevels(
Query query,
Set baseCubesLevelToColumnMaps,
Map measureMap)
{
// Gather the unique set of level-to-column maps corresponding
// to the underlying star/cube where the measure column
// originates from.
Set baseCubesLevelToColumnMaps = new HashSet();
Map measureMap = new HashMap();
Set measureMembers = query.getMeasuresMembers();
// if no measures are explicitly referenced, just use the default
// measure
Expand Down Expand Up @@ -119,8 +150,6 @@ private static boolean findVirtualCubeJoinLevels(Query query)
return false;
}

query.setVirtualCubeBaseCubeMaps(baseCubesLevelToColumnMaps);
query.setLevelMapToMeasureMap(measureMap);
return true;
}

Expand Down
11 changes: 11 additions & 0 deletions testsrc/main/mondrian/rolap/NonEmptyTest.java
Expand Up @@ -183,6 +183,17 @@ public void testVirtualCubeMembers() throws Exception {
"from [Warehouse and Sales]");
c.run();
}

public void testVirtualCubeMembersNonConformingDim() throws Exception {
// native sql optimization should not be used when you have a
// non-conforming dimensions because it will result in a cartesian
// product join
checkNotNative(
1,
"select non empty {[Customers].[Country].members} on columns, " +
"{[Measures].[Units Ordered]} on rows from " +
"[Warehouse and Sales]");
}

public void testNativeFilter() {
if (!MondrianProperties.instance().EnableNativeFilter.get()) {
Expand Down

0 comments on commit d3a4760

Please sign in to comment.