Search in sources :

Example 1 with RolapEvaluator

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);
    }
}
Also used : SchemaReader(mondrian.olap.SchemaReader) Set(java.util.Set) HashSet(java.util.HashSet) Query(mondrian.olap.Query) HashMap(java.util.HashMap) TupleCursor(mondrian.calc.TupleCursor) AbstractTupleCursor(mondrian.calc.impl.AbstractTupleCursor) ArrayList(java.util.ArrayList) RolapEvaluator(mondrian.rolap.RolapEvaluator) Formula(mondrian.olap.Formula) Hierarchy(mondrian.olap.Hierarchy) Execution(mondrian.server.Execution) Member(mondrian.olap.Member) ListTupleList(mondrian.calc.impl.ListTupleList) DelegatingTupleList(mondrian.calc.impl.DelegatingTupleList) TupleList(mondrian.calc.TupleList) DummyExp(mondrian.calc.DummyExp) Exp(mondrian.olap.Exp)

Example 2 with RolapEvaluator

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;
        }
    };
}
Also used : AbstractListCalc(mondrian.calc.impl.AbstractListCalc) AbstractListCalc(mondrian.calc.impl.AbstractListCalc) RolapEvaluator(mondrian.rolap.RolapEvaluator) RolapEvaluator(mondrian.rolap.RolapEvaluator)

Example 3 with RolapEvaluator

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;
}
Also used : Evaluator(mondrian.olap.Evaluator) RolapEvaluator(mondrian.rolap.RolapEvaluator) RolapHierarchy(mondrian.rolap.RolapHierarchy) Member(mondrian.olap.Member)

Example 4 with RolapEvaluator

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());
            }
        }
    }
}
Also used : RolapHierarchy(mondrian.rolap.RolapHierarchy) Set(java.util.Set) RolapEvaluator(mondrian.rolap.RolapEvaluator) StringProperty(org.eigenbase.util.property.StringProperty)

Aggregations

RolapEvaluator (mondrian.rolap.RolapEvaluator)4 Set (java.util.Set)2 Member (mondrian.olap.Member)2 RolapHierarchy (mondrian.rolap.RolapHierarchy)2 ArrayList (java.util.ArrayList)1 HashMap (java.util.HashMap)1 HashSet (java.util.HashSet)1 DummyExp (mondrian.calc.DummyExp)1 TupleCursor (mondrian.calc.TupleCursor)1 TupleList (mondrian.calc.TupleList)1 AbstractListCalc (mondrian.calc.impl.AbstractListCalc)1 AbstractTupleCursor (mondrian.calc.impl.AbstractTupleCursor)1 DelegatingTupleList (mondrian.calc.impl.DelegatingTupleList)1 ListTupleList (mondrian.calc.impl.ListTupleList)1 Evaluator (mondrian.olap.Evaluator)1 Exp (mondrian.olap.Exp)1 Formula (mondrian.olap.Formula)1 Hierarchy (mondrian.olap.Hierarchy)1 Query (mondrian.olap.Query)1 SchemaReader (mondrian.olap.SchemaReader)1