Skip to content

Commit

Permalink
MONDRIAN: MemberReader pushes down filters to the RDBMS if there is c…
Browse files Browse the repository at this point in the history
…ontext due to a NON EMPTY clause. (Code changes are by Andreas; I am just the messenger and occasionally spell-checker.)

[git-p4: depot-paths = "//open/mondrian/": change = 4442]
  • Loading branch information
julianhyde committed Nov 21, 2005
1 parent ec6e649 commit d88f133
Show file tree
Hide file tree
Showing 41 changed files with 2,769 additions and 487 deletions.
37 changes: 33 additions & 4 deletions src/main/mondrian/olap/DelegatingSchemaReader.java
Expand Up @@ -4,6 +4,7 @@
// Agreement, available at the following URL:
// http://www.opensource.org/licenses/cpl.html.
// (C) Copyright 2003-2005 Julian Hyde
// (C) Copyright 2004-2005 TONBELLER AG
// All Rights Reserved.
// You must accept the terms of that agreement to use this software.
//
Expand All @@ -24,7 +25,7 @@
* @since Feb 26, 2003
* @version $Id$
**/
public class DelegatingSchemaReader implements SchemaReader {
public abstract class DelegatingSchemaReader implements SchemaReader {
protected final SchemaReader schemaReader;

DelegatingSchemaReader(SchemaReader schemaReader) {
Expand Down Expand Up @@ -52,10 +53,16 @@ public Member[] getMemberChildren(Member[] members) {
}

public void getMemberDescendants(
Member member, List result, Level level,
boolean before, boolean self, boolean after, boolean leaves) {
Member member,
List result,
Level level,
boolean before,
boolean self,
boolean after,
boolean leaves,
Evaluator context) {
schemaReader.getMemberDescendants(
member, result, level, before, self, after, leaves);
member, result, level, before, self, after, leaves, context);
}

public int getMemberDepth(Member member) {
Expand Down Expand Up @@ -147,6 +154,28 @@ public int getChildrenCountFromCache(Member member) {
public int getLevelCardinalityFromCache(Level level) {
return schemaReader.getLevelCardinalityFromCache(level);
}

public Member[] getLevelMembers(Level level, Evaluator context) {
return schemaReader.getLevelMembers(level, context);
}

public Member[] getMemberChildren(Member member, Evaluator context) {
return schemaReader.getMemberChildren(member, context);
}

public Member[] getMemberChildren(Member[] members, Evaluator context) {
return schemaReader.getMemberChildren(members, context);
}

public Member lookupMemberChildByName(Member member, String memberName) {
return schemaReader.lookupMemberChildByName(member, memberName);
}

public NativeEvaluator getNativeSetEvaluator(FunDef fun, Evaluator evaluator, Exp[] args) {
return schemaReader.getNativeSetEvaluator(fun, evaluator, args);
}


}

// End DelegatingSchemaReader.java
2 changes: 1 addition & 1 deletion src/main/mondrian/olap/MemberBase.java
Expand Up @@ -115,7 +115,7 @@ public boolean isNull() {
}

public OlapElement lookupChild(SchemaReader schemaReader, String s) {
return Util.lookupMemberChildByName(schemaReader, this, s);
return schemaReader.lookupMemberChildByName(this, s);
}

// implement Member
Expand Down
13 changes: 13 additions & 0 deletions src/main/mondrian/olap/NativeEvaluator.java
@@ -0,0 +1,13 @@
package mondrian.olap;


/**
* allows expressions to be evaluated native, e.g. in SQL.
*
* @author av
* @since Nov 11, 2005
*/

public interface NativeEvaluator {
Object execute();
}
43 changes: 41 additions & 2 deletions src/main/mondrian/olap/SchemaReader.java
Expand Up @@ -60,12 +60,28 @@ public interface SchemaReader {
**/
Member[] getMemberChildren(Member member);

/**
* Returns direct children of <code>member</code>, optimized
* for NON EMPTY.
* <p>
* If <code>context == null</code> then
* there is no context and all members are returned - then
* its identical to {@link #getMemberChildren(Member)}.
* If <code>context</code> is not null, the resulting members
* <em>may</em> be restricted to those members that have a
* non empty row in the fact table for <code>context</code>.
* Wether or not optimization is possible depends
* on the SchemaReader implementation.
*/
Member[] getMemberChildren(Member member, Evaluator context);

/**
* Returns direct children of each element of <code>members</code>.
* @pre members != null
* @post return != null
**/
Member[] getMemberChildren(Member[] members);
Member[] getMemberChildren(Member[] members, Evaluator context);

/**
* Returns the parent of <code>member</code>.
Expand All @@ -86,10 +102,18 @@ public interface SchemaReader {
* @param self Whether to output members at <code>level</code>
* @param after Whether to output members below <code>level</code>
* @param leaves Whether to output members which are leaves
* @param context Evaluation context; determines criteria by which the
* result set should be filtered
*/
void getMemberDescendants(
Member member, List result, Level level,
boolean before, boolean self, boolean after, boolean leaves);
Member member,
List result,
Level level,
boolean before,
boolean self,
boolean after,
boolean leaves,
Evaluator context);

/**
* Returns the depth of a member.
Expand Down Expand Up @@ -198,6 +222,7 @@ OlapElement lookupCompound(
* Returns the members of <code>level</code>.
*/
Member[] getLevelMembers(Level level);
Member[] getLevelMembers(Level level, Evaluator context);

/**
* Returns the accessible levels of a hierarchy.
Expand Down Expand Up @@ -242,6 +267,20 @@ OlapElement lookupCompound(
* Returns the list of calculated members.
*/
List getCalculatedMembers();

/**
* Finds a child of a member with a given name.
*/
Member lookupMemberChildByName(Member parent, String childName);

/**
* returns the instance that can perform native crossjoin
* @param args
* @param evaluator
* @param fun
*/
NativeEvaluator getNativeSetEvaluator(FunDef fun, Evaluator evaluator, Exp[] args);

}

// End SchemaReader.java
17 changes: 1 addition & 16 deletions src/main/mondrian/olap/Util.java
Expand Up @@ -593,7 +593,7 @@ public static Member lookupHierarchyRootMember(SchemaReader reader,
// example, they could say '[USA]' instead of '[(All
// Customers)].[USA]'.
return (rootMembers.length == 1 && rootMembers[0].isAll())
? lookupMemberChildByName(reader, rootMembers[0], memberName)
? reader.lookupMemberChildByName(rootMembers[0], memberName)
: null;
}

Expand All @@ -612,21 +612,6 @@ public static Level lookupHierarchyLevel(Hierarchy hierarchy, String s) {
}


/**
* Finds a child of a member with a given name.
*/
public static Member lookupMemberChildByName(
SchemaReader reader, Member member, String memberName) {
Member[] children = reader.getMemberChildren(member);
// TODO: Linear search may be a performance problem.
for (int i = 0; i < children.length; i++){
final Member child = children[i];
if (Util.equals(child.getName(), memberName)) {
return child;
}
}
return null;
}

/**
* Finds the zero based ordinal of a Member among its siblings.
Expand Down
31 changes: 29 additions & 2 deletions src/main/mondrian/olap/fun/BuiltinFunTable.java
Expand Up @@ -1331,7 +1331,8 @@ public Object evaluate(Evaluator evaluator, Exp[] args) {
"pxm") {
public Object evaluate(Evaluator evaluator, Exp[] args) {
Member member = getMemberArg(evaluator, args, 0, true);
Member[] children = evaluator.getSchemaReader().getMemberChildren(member);
//Member[] children = evaluator.getSchemaReader().getMemberChildren(member);
Member[] children = getNonEmptyMemberChildren(evaluator, member);
return Arrays.asList(children);
}
});
Expand Down Expand Up @@ -1541,6 +1542,11 @@ public Object evaluate(Evaluator evaluator, Exp[] args) {
"Returns the set resulting from filtering a set based on a search condition.",
"fxxb") {
public Object evaluate(Evaluator evaluator, Exp[] args) {
SchemaReader schemaReader = evaluator.getSchemaReader();
NativeEvaluator nativeEvaluator = schemaReader.getNativeSetEvaluator(this, evaluator, args);
if (nativeEvaluator != null)
return nativeEvaluator.execute();

List members = (List) getArg(evaluator, args, 0);
Exp exp = args[1];
List result = new ArrayList();
Expand Down Expand Up @@ -1744,7 +1750,8 @@ public Object evaluate(Evaluator evaluator, Exp[] args) {
"pxl") {
public Object evaluate(Evaluator evaluator, Exp[] args) {
Level level = (Level) getArg(evaluator, args, 0);
Member[] members = evaluator.getSchemaReader().getLevelMembers(level);
//Member[] members = evaluator.getSchemaReader().getLevelMembers(level);
Member[] members = getNonEmptyLevelMembers(evaluator, level);
ArrayList memberList = new ArrayList(Arrays.asList(members));
if (memberList != null) {
removeCalculatedMembers(memberList);
Expand Down Expand Up @@ -2091,6 +2098,11 @@ public String[] getReservedWords() {
protected FunDef createFunDef(Exp[] args, FunDef dummyFunDef) {
return new FunDefBase(dummyFunDef) {
public Object evaluate(Evaluator evaluator, Exp[] args) {
SchemaReader schemaReader = evaluator.getSchemaReader();
NativeEvaluator nativeEvaluator = schemaReader.getNativeSetEvaluator(this, evaluator, args);
if (nativeEvaluator != null)
return nativeEvaluator.execute();

List list = (List) getArg(evaluator, args, 0);
int n = getIntArg(evaluator, args, 1);
ExpBase exp = (ExpBase) getArgNoEval(args, 2, null);
Expand Down Expand Up @@ -3311,6 +3323,21 @@ public static BuiltinFunTable instance() {
return instance;
}

protected Member[] getNonEmptyMemberChildren(Evaluator evaluator, Member member) {
SchemaReader sr = evaluator.getSchemaReader();
if (evaluator.isNonEmpty()) {
return sr.getMemberChildren(member, evaluator);
}
return sr.getMemberChildren(member);
}

protected Member[] getNonEmptyLevelMembers(Evaluator evaluator, Level level) {
SchemaReader sr = evaluator.getSchemaReader();
if (evaluator.isNonEmpty()) {
return sr.getLevelMembers(level, evaluator);
}
return sr.getLevelMembers(level);
}
}

// End BuiltinFunTable.java
40 changes: 33 additions & 7 deletions src/main/mondrian/olap/fun/CrossJoinFunDef.java
Expand Up @@ -66,14 +66,27 @@ private static void addTypes(final Type type, ArrayList list) {
}

public Object evaluate(Evaluator evaluator, Exp[] args) {

SchemaReader schemaReader = evaluator.getSchemaReader();
NativeEvaluator nativeEvaluator = schemaReader.getNativeSetEvaluator(this, evaluator, args);
if (nativeEvaluator != null)
return nativeEvaluator.execute();

int resultLimit = MondrianProperties.instance().ResultLimit.get();

List set0 = getArgAsList(evaluator, args, 0);
List set1 = getArgAsList(evaluator, args, 1);
if (set0.isEmpty())
return Collections.EMPTY_LIST;

// optimize nonempty(crossjoin(a,b)) ==
// nonempty(crossjoin(nonempty(a),nonempty(b))
List set1 = getNonEmptyArgAsList(set0, evaluator, args, 1);
if (set1.isEmpty())
return Collections.EMPTY_LIST;

long size = (long)set0.size() * (long)set1.size();
if (size > 1000 && evaluator.isNonEmpty()) {
if (resultLimit > 0 && size > resultLimit && evaluator.isNonEmpty()) {
// instead of overflow exception try to further
// optimize nonempty(crossjoin(a,b)) ==
// nonempty(crossjoin(nonempty(a),nonempty(b))
set0 = nonEmptyList(evaluator, set0);
set1 = nonEmptyList(evaluator, set1);
size = (long)set0.size() * (long)set1.size();
Expand All @@ -84,11 +97,10 @@ public Object evaluate(Evaluator evaluator, Exp[] args) {
}

// throw an exeption, if the crossjoin gets too large
int limit = MondrianProperties.instance().ResultLimit.get();
if (limit > 0 && limit < size) {
if (resultLimit > 0 && resultLimit < size) {
// result limit exceeded, throw an exception
throw MondrianResource.instance().LimitExceededDuringCrossjoin.ex(
new Long(size), new Long(limit));
new Long(size), new Long(resultLimit));
}

boolean neitherSideIsTuple = true;
Expand Down Expand Up @@ -147,6 +159,20 @@ public Object evaluate(Evaluator evaluator, Exp[] args) {
return result;
}

/**
* if the first argument of crossjoin is a single member and we are in NON EMPTY mode,
* then we push the single member into the evaluator context. So the second argument
* is evaluated in the new context and possibly will return less members.
*/
private List getNonEmptyArgAsList(List left, Evaluator evaluator, Exp[] args, int index) {
if (left.size() == 1 && evaluator.isNonEmpty()) {
Object obj = left.get(0);
if (obj instanceof Member) // could be Member[]
evaluator = evaluator.push((Member)obj);
}
return getArgAsList(evaluator, args, index);
}

private static List getArgAsList(Evaluator evaluator, Exp[] args,
int index) {
final Object arg = getArg(evaluator, args, index);
Expand Down

0 comments on commit d88f133

Please sign in to comment.