use of mondrian.rolap.RolapEvaluator in project mondrian by pentaho.
the class CrossJoinFunDef method nonEmptyList.
/**
* This is the entry point to the crossjoin non-empty optimizer code.
*
* <p>
* What one wants to determine is for each individual Member of the input parameter list, a 'List-Member', whether
* across a slice there is any data.
*
* <p>
* But what data?
*
* <p>
* For Members other than those in the list, the 'non-List-Members', one wants to consider all data across the scope
* of these other Members. For instance, if Time is not a List-Member, then one wants to consider data across All
* Time. Or, if Customer is not a List-Member, then look at data across All Customers. The theory here, is if there is
* no data for a particular Member of the list where all other Members not part of the list are span their complete
* hierarchy, then there is certainly no data for Members of that Hierarchy at a more specific Level (more on this
* below).
*
* <p>
* When a Member that is a non-List-Member is part of a Hierarchy that has an All Member (hasAll="true"), then its
* very easy to make sure that the All Member is used during the optimization. If a non-List-Member is part of a
* Hierarchy that does not have an All Member, then one must, in fact, iterate over all top-level Members of the
* Hierarchy!!! - otherwise a List-Member might be excluded because the optimization code was not looking everywhere.
*
* <p>
* Concerning default Members for those Hierarchies for the non-List-Members, ignore them. What is wanted is either
* the All Member or one must iterate across all top-level Members, what happens to be the default Member of the
* Hierarchy is of no relevant.
*
* <p>
* The Measures Hierarchy has special considerations. First, there is no All Measure. But, certainly one need only
* involve Measures that are actually in the query... yes and no. For Calculated Measures one must also get all of the
* non-Calculated Measures that make up each Calculated Measure. Thus, one ends up iterating across all Calculated and
* non-Calculated Measures that are explicitly mentioned in the query as well as all Calculated and non-Calculated
* Measures that are used to define the Calculated Measures in the query. Why all of these? because this represents
* the total scope of possible Measures that might yield a non-null value for the List-Members and that is what we
* what to find. It might be a super set, but thats ok; we just do not want to miss anything.
*
* <p>
* For other Members, the default Member is used, but for Measures one should look for that data for all Measures
* associated with the query, not just one Measure. For a dense dataset this may not be a problem or even apparent,
* but for a sparse dataset, the first Measure may, in fact, have not data but other Measures associated with the
* query might. Hence, the solution here is to identify all Measures associated with the query and then for each
* Member of the list, determine if there is any data iterating across all Measures until non-null data is found or
* the end of the Measures is reached.
*
* <p>
* This is a non-optimistic implementation. This means that an element of the input parameter List is only not
* included in the returned result List if for no combination of Measures, non-All Members (for Hierarchies that have
* no All Members) and evaluator default Members did the element evaluate to non-null.
*
* @param evaluator
* Evaluator
* @param list
* List of members or tuples
* @param call
* Calling ResolvedFunCall used to determine what Measures to use
* @return List of elements from the input parameter list that have evaluated to non-null.
*/
protected TupleList nonEmptyList(Evaluator evaluator, TupleList list, ResolvedFunCall call) {
if (list.isEmpty()) {
return list;
}
TupleList result = TupleCollections.createList(list.getArity(), (list.size() + 2) >> 1);
// Get all of the Measures
final Query query = evaluator.getQuery();
final String measureSetKey = "MEASURE_SET-" + ctag;
Set<Member> measureSet = Util.cast((Set) query.getEvalCache(measureSetKey));
final String memberSetKey = "MEMBER_SET-" + ctag;
Set<Member> memberSet = Util.cast((Set) query.getEvalCache(memberSetKey));
// sense to create and cache it.
if (measureSet == null || memberSet == null) {
measureSet = new HashSet<Member>();
memberSet = new HashSet<Member>();
Set<Member> queryMeasureSet = query.getMeasuresMembers();
MeasureVisitor measureVisitor = new MeasureVisitor(measureSet, call);
// MemberExtractingVisitor will collect the dimension members
// referenced within the measures in the query.
// One or more measures may conflict with the members in the tuple,
// overriding the context of the tuple member when determining
// non-emptiness.
MemberExtractingVisitor memVisitor = new MemberExtractingVisitor(memberSet, call, false);
for (Member m : queryMeasureSet) {
if (m.isCalculated()) {
Exp exp = m.getExpression();
exp.accept(measureVisitor);
exp.accept(memVisitor);
} else {
measureSet.add(m);
}
}
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);
}
}
query.putEvalCache(measureSetKey, measureSet);
query.putEvalCache(memberSetKey, memberSet);
}
final String allMemberListKey = "ALL_MEMBER_LIST-" + ctag;
List<Member> allMemberList = Util.cast((List) query.getEvalCache(allMemberListKey));
final String nonAllMembersKey = "NON_ALL_MEMBERS-" + ctag;
Member[][] nonAllMembers = (Member[][]) query.getEvalCache(nonAllMembersKey);
if (nonAllMembers == null) {
//
// Get all of the All Members and those Hierarchies that
// do not have All Members.
//
Member[] evalMembers = evaluator.getMembers().clone();
List<Member> listMembers = list.get(0);
// Remove listMembers from evalMembers and independentSlicerMembers
for (Member lm : listMembers) {
Hierarchy h = lm.getHierarchy();
for (int i = 0; i < evalMembers.length; i++) {
Member em = evalMembers[i];
if ((em != null) && h.equals(em.getHierarchy())) {
evalMembers[i] = null;
}
}
}
Map<Hierarchy, Set<Member>> mapOfSlicerMembers = new HashMap<Hierarchy, Set<Member>>();
if (evaluator instanceof RolapEvaluator) {
RolapEvaluator rev = (RolapEvaluator) evaluator;
mapOfSlicerMembers = rev.getSlicerMembersByHierarchy();
}
// Now we have the non-List-Members, but some of them may not be
// All Members (default Member need not be the All Member) and
// for some Hierarchies there may not be an All Member.
// So we create an array of Objects some elements of which are
// All Members and others elements will be an array of all top-level
// Members when there is not an All Member.
SchemaReader schemaReader = evaluator.getSchemaReader();
allMemberList = new ArrayList<Member>();
List<Member[]> nonAllMemberList = new ArrayList<Member[]>();
Member em;
boolean isSlicerMember;
for (Member evalMember : evalMembers) {
em = evalMember;
if (em == null) {
// to null. These are the CrossJoin axes.
continue;
}
if (em.isMeasure()) {
continue;
}
isSlicerMember = false;
if (mapOfSlicerMembers != null) {
Set<Member> members = mapOfSlicerMembers.get(em.getHierarchy());
if (members != null) {
isSlicerMember = members.contains(em);
}
}
//
if ((isSlicerMember && !em.isCalculated()) || (!isSlicerMember && em.isCalculated())) {
// hierarchy, add them to nonAllMemberList
if (isSlicerMember) {
Set<Member> hierarchySlicerMembers = mapOfSlicerMembers.get(em.getHierarchy());
if (hierarchySlicerMembers.size() > 1) {
nonAllMemberList.add(hierarchySlicerMembers.toArray(new Member[hierarchySlicerMembers.size()]));
}
}
continue;
}
// replace with the "all" member.
if (isSlicerMember || !em.isAll()) {
Hierarchy h = em.getHierarchy();
final List<Member> rootMemberList = schemaReader.getHierarchyRootMembers(h);
if (h.hasAll()) {
// The Hierarchy has an All member
boolean found = false;
for (Member m : rootMemberList) {
if (m.isAll()) {
allMemberList.add(m);
found = true;
break;
}
}
if (!found) {
LOGGER.warn("CrossJoinFunDef.nonEmptyListNEW: ERROR");
}
} else {
// The Hierarchy does NOT have an All member
Member[] rootMembers = rootMemberList.toArray(new Member[rootMemberList.size()]);
nonAllMemberList.add(rootMembers);
}
}
}
nonAllMembers = nonAllMemberList.toArray(new Member[nonAllMemberList.size()][]);
query.putEvalCache(allMemberListKey, allMemberList);
query.putEvalCache(nonAllMembersKey, nonAllMembers);
}
//
// Determine if there is any data.
//
// Put all of the All Members into Evaluator
final int savepoint = evaluator.savepoint();
try {
evaluator.setContext(allMemberList);
// Iterate over elements of the input list. If for any
// combination of
// Measure and non-All Members evaluation is non-null, then
// add it to the result List.
final TupleCursor cursor = list.tupleCursor();
int currentIteration = 0;
Execution execution = query.getStatement().getCurrentExecution();
while (cursor.forward()) {
cursor.setContext(evaluator);
for (Member member : memberSet) {
// memberSet contains members referenced within measures.
// Make sure that we don't incorrectly assume a context
// that will be changed by the measure, so conservatively
// push context to [All] for each of the associated
// hierarchies.
evaluator.setContext(member.getHierarchy().getAllMember());
}
// Check if the MDX query was canceled.
// Throws an exception in case of timeout is exceeded
// see MONDRIAN-2425
CancellationChecker.checkCancelOrTimeout(currentIteration++, execution);
if (tupleContainsCalcs(cursor.current()) || checkData(nonAllMembers, nonAllMembers.length - 1, measureSet, evaluator)) {
result.addCurrent(cursor);
}
}
return result;
} finally {
evaluator.restore(savepoint);
}
}
use of mondrian.rolap.RolapEvaluator in project mondrian by pentaho.
the class NonEmptyCrossJoinFunDef method compileCall.
public Calc compileCall(final ResolvedFunCall call, ExpCompiler compiler) {
final ListCalc listCalc1 = compiler.compileList(call.getArg(0));
final ListCalc listCalc2 = compiler.compileList(call.getArg(1));
return new AbstractListCalc(call, new Calc[] { listCalc1, listCalc2 }, false) {
public TupleList evaluateList(Evaluator evaluator) {
SchemaReader schemaReader = evaluator.getSchemaReader();
// Evaluate the arguments in non empty mode, but remove from
// the slicer any members that will be overridden by args to
// the NonEmptyCrossjoin function. For example, in
//
// SELECT NonEmptyCrossJoin(
// [Store].[USA].Children,
// [Product].[Beer].Children)
// FROM [Sales]
// WHERE [Store].[Mexico]
//
// we want all beers, not just those sold in Mexico.
final int savepoint = evaluator.savepoint();
try {
evaluator.setNonEmpty(true);
for (Member member : ((RolapEvaluator) evaluator).getSlicerMembers()) {
if (getType().getElementType().usesHierarchy(member.getHierarchy(), true)) {
evaluator.setContext(member.getHierarchy().getAllMember());
}
}
NativeEvaluator nativeEvaluator = schemaReader.getNativeSetEvaluator(call.getFunDef(), call.getArgs(), evaluator, this);
if (nativeEvaluator != null) {
evaluator.restore(savepoint);
return (TupleList) nativeEvaluator.execute(ResultStyle.LIST);
}
final TupleList list1 = listCalc1.evaluateList(evaluator);
if (list1.isEmpty()) {
evaluator.restore(savepoint);
return list1;
}
final TupleList list2 = listCalc2.evaluateList(evaluator);
TupleList result = mutableCrossJoin(list1, list2);
// remove any remaining empty crossings from the result
result = nonEmptyList(evaluator, result, call);
return result;
} finally {
evaluator.restore(savepoint);
}
}
public boolean dependsOn(Hierarchy hierarchy) {
if (super.dependsOn(hierarchy)) {
return true;
}
// expression from the inherited context.
if (listCalc1.getType().usesHierarchy(hierarchy, true)) {
return false;
}
if (listCalc2.getType().usesHierarchy(hierarchy, true)) {
return false;
}
// whether a given tuple is empty, depends upon all dimensions.
return true;
}
};
}
use of mondrian.rolap.RolapEvaluator in project mondrian by pentaho.
the class AbstractCalc method simplifyEvaluator.
/**
* Returns a simplified evalator whose context is the same for every dimension which an expression depends on, and the
* default member for every dimension which it does not depend on.
*
* <p>
* The default member is often the 'all' member, so this evaluator is usually the most efficient context in which to
* evaluate the expression.
*
* @param calc
* @param evaluator
*/
public static Evaluator simplifyEvaluator(Calc calc, Evaluator evaluator) {
if (evaluator.isNonEmpty()) {
// 'NonEmptyCrossJoin'.
return evaluator;
}
int changeCount = 0;
Evaluator ev = evaluator;
final List<RolapHierarchy> hierarchies = ((RolapEvaluator) evaluator).getCube().getHierarchies();
for (RolapHierarchy hierarchy : hierarchies) {
final Member member = ev.getContext(hierarchy);
if (member.isAll()) {
continue;
}
if (calc.dependsOn(hierarchy)) {
continue;
}
final Member unconstrainedMember = member.getHierarchy().getDefaultMember();
if (member == unconstrainedMember) {
// is already the default member.
continue;
}
if (changeCount++ == 0) {
ev = evaluator.push();
}
ev.setContext(unconstrainedMember);
}
return ev;
}
use of mondrian.rolap.RolapEvaluator in project mondrian by pentaho.
the class HierarchyCurrentMemberFunDef method validateSlicerMembers.
private static void validateSlicerMembers(Hierarchy hierarchy, Evaluator evaluator) {
if (evaluator instanceof RolapEvaluator) {
StringProperty alertProperty = MondrianProperties.instance().CurrentMemberWithCompoundSlicerAlert;
String alertValue = alertProperty.get();
if (alertValue.equalsIgnoreCase(org.apache.logging.log4j.Level.OFF.toString())) {
// No validation
return;
}
RolapEvaluator rev = (RolapEvaluator) evaluator;
Map<Hierarchy, Set<Member>> map = rev.getSlicerMembersByHierarchy();
Set<Member> members = map.get(hierarchy);
if (members != null && members.size() > 1) {
MondrianException exception = MondrianResource.instance().CurrentMemberWithCompoundSlicer.ex(hierarchy.getUniqueName());
if (alertValue.equalsIgnoreCase(org.apache.logging.log4j.Level.WARN.toString())) {
LOGGER.warn(exception.getMessage());
} else if (alertValue.equalsIgnoreCase(org.apache.logging.log4j.Level.ERROR.toString())) {
throw MondrianResource.instance().CurrentMemberWithCompoundSlicer.ex(hierarchy.getUniqueName());
}
}
}
}
Aggregations