001    /*
002    // $Id: //open/mondrian-release/3.0/src/main/mondrian/rolap/RolapResult.java#4 $
003    // This software is subject to the terms of the Common Public License
004    // Agreement, available at the following URL:
005    // http://www.opensource.org/licenses/cpl.html.
006    // Copyright (C) 2001-2002 Kana Software, Inc.
007    // Copyright (C) 2001-2007 Julian Hyde and others
008    // All Rights Reserved.
009    // You must accept the terms of that agreement to use this software.
010    //
011    // jhyde, 10 August, 2001
012    */
013    
014    package mondrian.rolap;
015    import mondrian.olap.*;
016    import mondrian.olap.fun.MondrianEvaluationException;
017    import mondrian.resource.MondrianResource;
018    import mondrian.rolap.agg.AggregationManager;
019    import mondrian.util.Format;
020    import mondrian.util.ObjectPool;
021    
022    import mondrian.olap.fun.FunUtil;
023    import mondrian.olap.type.ScalarType;
024    import mondrian.calc.*;
025    import mondrian.calc.impl.ValueCalc;
026    
027    import org.apache.log4j.Logger;
028    
029    import java.util.Collections;
030    import java.util.ArrayList;
031    import java.util.List;
032    import java.util.Iterator;
033    import java.util.ListIterator;
034    import java.util.HashMap;
035    import java.util.Map;
036    import java.util.Locale;
037    
038    /**
039     * A <code>RolapResult</code> is the result of running a query.
040     *
041     * @author jhyde
042     * @since 10 August, 2001
043     * @version $Id: //open/mondrian-release/3.0/src/main/mondrian/rolap/RolapResult.java#4 $
044     */
045    public
046    class RolapResult extends ResultBase {
047    
048        private static final Logger LOGGER = Logger.getLogger(ResultBase.class);
049    
050        private RolapEvaluator evaluator;
051        private final CellKey point;
052    
053        private CellInfoContainer cellInfos;
054        private FastBatchingCellReader batchingReader;
055        private final CellReader aggregatingReader =
056            AggregationManager.instance().getCacheCellReader();
057        private Modulos modulos = null;
058        private final int maxEvalDepth =
059                MondrianProperties.instance().MaxEvalDepth.get();
060    
061        RolapResult(Query query, boolean execute) {
062            super(query, new Axis[query.axes.length]);
063    
064            this.point = CellKey.Generator.newCellKey(query.axes.length);
065            final int expDeps = MondrianProperties.instance().TestExpDependencies.get();
066            if (expDeps > 0) {
067                this.evaluator = new RolapDependencyTestingEvaluator(this, expDeps);
068            } else {
069                final RolapEvaluator.RolapEvaluatorRoot root =
070                        new RolapResultEvaluatorRoot(this);
071                this.evaluator = new RolapEvaluator(root);
072            }
073            RolapCube rcube = (RolapCube) query.getCube();
074            this.batchingReader = new FastBatchingCellReader(rcube);
075    
076            this.cellInfos = (query.axes.length > 4)
077                ?  new CellInfoMap(point) : new CellInfoPool(query.axes.length);
078    
079    
080            if (!execute) {
081                return;
082            }
083    
084            boolean normalExecution = true;
085            try {
086                // This call to clear the cube's cache only has an
087                // effect if caching has been disabled, otherwise
088                // nothing happens.
089                // Clear the local cache before a query has run
090                rcube.clearCachedAggregations();
091                // Check if there are modifications to the global aggregate cache
092                rcube.checkAggregateModifications();
093    
094    
095                /////////////////////////////////////////////////////////////////
096                //
097                // Evaluation Algorithm
098                //
099                // There are three basic steps to the evaluation algorithm:
100                // 1) Determine all Members for each axis but do not save
101                // information (do not build the RolapAxis),
102                // 2) Save all Members for each axis (build RolapAxis).
103                // 3) Evaluate and store each Cell determined by the Members
104                // of the axes.
105                // Step 1 converges on the stable set of Members pre axis.
106                // Steps 1 and 2 make sure that the data has been loaded.
107                //
108                // More detail follows.
109                //
110                // Explicit and Implicit Members:
111                // A Member is said to be 'explicit' if it appears on one of
112                // the Axes (one of the RolapAxis Position List of Members).
113                // A Member is 'implicit' if it is in the query but does not
114                // end up on any Axes (its usage, for example, is in a function).
115                // When for a Dimension none of its Members are explicit in the
116                // query, then the default Member is used which is like putting
117                // the Member in the Slicer.
118                //
119                // Special Dimensions:
120                // There are 2 special dimensions.
121                // The first is the Time dimension. If in a schema there is
122                // no ALL Member, then Whatever happens to be the default
123                // Member is used if Time Members are not explicitly set
124                // in the query.
125                // The second is the Measures dimension. This dimension
126                // NEVER has an ALL Member. A cube's default Measure is set
127                // by convention - its simply the first Measure defined in the
128                // cube.
129                //
130                // First a RolapEvaluator is created. During its creation,
131                // it gets a Member from each Hierarchy. Each Member is the
132                // default Member of the Hierarchy. For most Hierarchies this
133                // Member is the ALL Member, but there are cases where 1)
134                // a Hierarchy does not have an ALL Member or 2) the Hierarchy
135                // has an ALL Member but that Member is not the default Member.
136                // In these cases, the default Member is still used, but its
137                // use can cause evaluation issues (seemingly strange evaluation
138                // results).
139                //
140                // Next, load all root Members for Hierarchies that have no ALL
141                // Member and load ALL Members that are not the default Member.
142                //
143                // Determine the Members of the Slicer axis (Step 1 above).  Any
144                // Members found are added to the AxisMember object. If one of these
145                // Members happens to be a Measure, then the Slicer is explicitly
146                // specifying the query's Measure and this should be put into the
147                // evaluator's context (replacing the default Measure which just
148                // happens to be the first Measure defined in the cube).  Other
149                // Members found in the AxisMember object are also placed into the
150                // evaluator's context since these also are explicitly specified.
151                // Also, any other Members in the AxisMember object which have the
152                // same Hierarchy as Members in the list of root Members for
153                // Hierarchies that have no ALL Member, replace those Members - they
154                // Slicer has explicitly determined which ones to use. The
155                // AxisMember object is now cleared.
156                // The Slicer does not depend upon the other Axes, but the other
157                // Axes depend upon both the Slicer and each other.
158                //
159                // The AxisMember object also checks if the number of Members
160                // exceeds the ResultLimit property throwing a
161                // TotalMembersLimitExceeded Exception if it does.
162                //
163                // For all non-Slicer axes, the Members are determined (Step 1
164                // above). If a Measure is found in the AxisMember, then an
165                // Axis is explicitly specifying a Measure.
166                // If any Members in the AxisMember object have the same Hierarchy
167                // as a Member in the set of root Members for Hierarchies that have
168                // no ALL Member, then replace those root Members with the Member
169                // from the AxisMember object. In this case, again, a Member
170                // was explicitly specified in an Axis. If this replacement
171                // occurs, then one must redo this step with the new Members.
172                //
173                // Now Step 3 above is done. First to the Slicer Axis and then
174                // to the other Axes. Here the Axes are actually generated.
175                // If a Member of an Axis is an Calculated Member (and the
176                // Calculated Member is not a Member of the Measure Hierarchy),
177                // then find the Dimension associated with the Calculated
178                // Member and remove Members with the same Dimension in the set of
179                // root Members for Hierarchies that have no ALL Member.
180                // This is done because via the Calculated Member the Member
181                // was implicitly specified in the query. If this removal occurs,
182                // then the Axes must be re-evaluated repeating Step 3.
183                //
184                /////////////////////////////////////////////////////////////////
185    
186    
187                // The AxisMember object is used to hold Members that are found
188                // during Step 1 when the Axes are determined.
189                final AxisMember axisMembers = new AxisMember();
190    
191    
192                // list of ALL Members that are not default Members
193                final List<Member> nonDefaultAllMembers = new ArrayList<Member>();
194    
195                // List of Members of Hierarchies that do not have an ALL Member
196                List<Member[]> nonAllMembers = new ArrayList<Member[]>();
197    
198                // List of Measures
199                final List<Member> measureMembers = new ArrayList<Member>();
200    
201                // load all root Members for Hierarchies that have no ALL
202                // Member and load ALL Members that are not the default Member.
203                // Also, all Measures are are gathered.
204                loadSpecialMembers(nonDefaultAllMembers,
205                                    nonAllMembers, measureMembers);
206    
207                // clear evaluation cache
208                query.clearEvalCache();
209    
210                // Save, may be needed by some Expression Calc's
211                query.putEvalCache("ALL_MEMBER_LIST", nonDefaultAllMembers);
212    
213    
214                final List<Member[]> emptyNonAllMembers = Collections.emptyList();
215    
216                /////////////////////////////////////////////////////////////////
217                //
218                // Determine Slicer
219                //
220                axisMembers.setSlicer(true);
221                loadMembers(emptyNonAllMembers, evaluator,
222                            query.slicerAxis, query.slicerCalc, axisMembers);
223                axisMembers.setSlicer(false);
224    
225                if (! axisMembers.isEmpty()) {
226                    for (Member m : axisMembers) {
227                        evaluator.setSlicerContext(m);
228                        if (m.isMeasure()) {
229                            // A Measure was explicitly declared in the
230                            // Slicer, don't need to worry about Measures
231                            // for this query.
232                            measureMembers.clear();
233                        }
234                    }
235                    replaceNonAllMembers(nonAllMembers, axisMembers);
236                    axisMembers.clearMembers();
237                }
238                //
239                /////////////////////////////////////////////////////////////////
240    
241    
242                /////////////////////////////////////////////////////////////////
243                //
244                // Determine Axes
245                //
246                boolean changed = false;
247    
248                // reset to total member count
249                axisMembers.clearTotalCellCount();
250    
251                for (int i = 0; i < axes.length; i++) {
252                    final QueryAxis axis = query.axes[i];
253                    final Calc calc = query.axisCalcs[i];
254                    loadMembers(emptyNonAllMembers, evaluator,
255                                axis, calc, axisMembers);
256                }
257    
258                if (! axisMembers.isEmpty()) {
259                    for (Member m : axisMembers) {
260                        if (m.isMeasure()) {
261                            // A Measure was explicitly declared on an
262                            // axis, don't need to worry about Measures
263                            // for this query.
264                            measureMembers.clear();
265                        }
266                    }
267                    changed = replaceNonAllMembers(nonAllMembers, axisMembers);
268                    axisMembers.clearMembers();
269                }
270    
271    /*
272                // This code allows replacing default Measure Member
273                // with one found in the query, an implicit Measure.
274                // Fixes some problems (RolapResultTest._testNullDefaultMeasure)
275                // but causes other ones (NamedSetTest.testNamedSetUsedInCrossJoin)
276                // so it can not be used.
277                Member previous = null;
278                if (! measureMembers.isEmpty()) {
279                     MeasureVisitor visitor = new MeasureVisitor();
280                    for (int i = 0; i < axes.length; i++) {
281                        query.axes[i].accept(visitor);
282                    }
283                    println("Measures on Axis:");
284                    for (Member m : visitor.measures) {
285                        println("   m=" +m.getUniqueName());
286                    }
287                    if (! visitor.measures.isEmpty()) {
288                        Member m = visitor.measures.get(0);
289                        previous = evaluator.setContext(m);
290                        changed |= ! m.equals(previous);
291                    }
292                }
293                // move this
294                // it should be set just before calling executeBody
295                // evaluator.setContext(previous);
296    */
297    
298    
299                if (changed) {
300                    // only count number of members, do not collect any
301                    axisMembers.countOnly(true);
302                    // reset to total member count
303                    axisMembers.clearTotalCellCount();
304    
305                    for (int i = 0; i < axes.length; i++) {
306                        final QueryAxis axis = query.axes[i];
307                        final Calc calc = query.axisCalcs[i];
308                        loadMembers(
309                            nonAllMembers,
310                            evaluator.push(),
311                            axis, calc, axisMembers);
312                    }
313                }
314    
315                // throws exception if number of members exceeds limit
316                axisMembers.checkLimit();
317    
318                //
319                /////////////////////////////////////////////////////////////////
320    
321                /////////////////////////////////////////////////////////////////
322                //
323                // Execute Slicer
324                //
325                this.slicerAxis =
326                    evalExecute(
327                        nonAllMembers,
328                        nonAllMembers.size() - 1,
329                        evaluator.push(),
330                        query.slicerAxis,
331                        query.slicerCalc);
332    
333                // Use the context created by the slicer for the other
334                // axes.  For example, "select filter([Customers], [Store
335                // Sales] > 100) on columns from Sales where
336                // ([Time].[1998])" should show customers whose 1998 (not
337                // total) purchases exceeded 100.
338    
339                // Getting the Position list's size and the Position
340                // at index == 0 will, in fact, cause an Iterable-base
341                // Axis Position List to become a List-base Axis
342                // Position List (and increae memory usage), but for
343                // the slicer axis, the number of Positions is very
344                // small, so who cares.
345                switch (this.slicerAxis.getPositions().size()) {
346                case 0:
347                    throw MondrianResource.instance().EmptySlicer.ex();
348                case 1:
349                    break;
350                default:
351                    throw MondrianResource.instance().CompoundSlicer.ex();
352                }
353                //
354                /////////////////////////////////////////////////////////////////
355    
356                /////////////////////////////////////////////////////////////////
357                //
358                // Execute Axes
359                //
360                boolean redo = true;
361                while (redo) {
362                    RolapEvaluator e = evaluator.push();
363                    redo = false;
364    
365                    for (int i = 0; i < axes.length; i++) {
366                        QueryAxis axis = query.axes[i];
367                        final Calc calc = query.axisCalcs[i];
368                        Axis axisResult = evalExecute(nonAllMembers,
369                                    nonAllMembers.size()-1, e, axis, calc);
370    
371                        if (! nonAllMembers.isEmpty()) {
372                            List<Position> pl = axisResult.getPositions();
373                            if (pl.size() > 0) {
374                                // Only need to process the first Position
375                                Position p = pl.get(0);
376                                for (Member m : p) {
377                                    if (m.isCalculated()) {
378                                        CalculatedMeasureVisitor visitor =
379                                            new CalculatedMeasureVisitor();
380                                        m.getExpression().accept(visitor);
381                                        Dimension dimension = visitor.dimension;
382                                        redo = removeDimension(dimension, nonAllMembers);
383                                    }
384                                }
385                            }
386                        }
387                        this.axes[i] = axisResult;
388                    }
389                }
390                //
391                /////////////////////////////////////////////////////////////////
392    
393                /////////////////////////////////////////////////////////////////
394                //
395                // Get value for each Cell
396                //
397                executeBody(query);
398                //
399                /////////////////////////////////////////////////////////////////
400    
401                // If you are very close to running out of memory due to
402                // the number of CellInfo's in cellInfos, then calling this
403                // may cause the out of memory one is trying to aviod.
404                // On the other hand, calling this can reduce the size of
405                // the ObjectPool's internal storage by half (but, of course,
406                // it will not reduce the size of the stored objects themselves).
407                // Only call this if there are lots of CellInfo.
408                if (this.cellInfos.size() > 10000) {
409                    this.cellInfos.trimToSize();
410                }
411    
412            } catch (ResultLimitExceededException ex) {
413                // If one gets a ResultLimitExceededException, then
414                // don't count on anything being worth caching.
415                normalExecution = false;
416    
417                // De-reference data structures that might be holding
418                // partial results but surely are taking up memory.
419                evaluator = null;
420                cellInfos = null;
421                batchingReader = null;
422                for (int i = 0; i < axes.length; i++) {
423                    axes[i] = null;
424                }
425                slicerAxis = null;
426    
427                query.clearEvalCache();
428    
429                throw ex;
430    
431            } finally {
432                if (normalExecution) {
433                    // Push all modifications to the aggregate cache to the global
434                    // cache so each thread can start using it
435                    rcube.pushAggregateModificationsToGlobalCache();
436    
437                    // Expression cache duration is for each query. It is time to
438                    // clear out the whole expression cache at the end of a query.
439                    evaluator.clearExpResultCache(true);
440                }
441                if (LOGGER.isDebugEnabled()) {
442                    LOGGER.debug("RolapResult<init>: " + Util.printMemory());
443                }
444            }
445        }
446        protected boolean removeDimension(Dimension dimension, List<Member[]> nonAllMembers) {
447            boolean changed = false;
448            for (ListIterator<Member[]> it = nonAllMembers.listIterator();
449                    it.hasNext();) {
450                Member[] ms = it.next();
451                Dimension d = ms[0].getHierarchy().getDimension();
452                if (d.equals(dimension)) {
453                    it.remove();
454                }
455            }
456            return changed;
457        }
458    
459        private static class CalculatedMeasureVisitor extends mondrian.mdx.MdxVisitorImpl {
460            Dimension dimension;
461            CalculatedMeasureVisitor() {
462            }
463            public Object visit(mondrian.olap.Formula formula) {
464                return null;
465            }
466            public Object visit(mondrian.mdx.ResolvedFunCall call) {
467                return null;
468            }
469            public Object visit(mondrian.olap.Id id) {
470                return null;
471            }
472            public Object visit(mondrian.mdx.ParameterExpr parameterExpr) {
473                return null;
474            }
475            public Object visit(mondrian.mdx.DimensionExpr dimensionExpr) {
476                dimension = dimensionExpr.getDimension();
477                return null;
478            }
479            public Object visit(mondrian.mdx.HierarchyExpr hierarchyExpr) {
480                Hierarchy hierarchy = hierarchyExpr.getHierarchy();
481                dimension = hierarchy.getDimension();
482                return null;
483            }
484            public Object visit(mondrian.mdx.LevelExpr levelExpr) {
485                return null;
486            }
487            public Object visit(mondrian.mdx.MemberExpr memberExpr)  {
488                Member member = memberExpr.getMember();
489                dimension = member.getHierarchy().getDimension();
490                return null;
491            }
492            public Object visit(mondrian.mdx.NamedSetExpr namedSetExpr) {
493                return null;
494            }
495            public Object visit(mondrian.olap.Literal literal) {
496                return null;
497            }
498        }
499    
500        protected boolean replaceNonAllMembers(List<Member[]> nonAllMembers,
501                                                AxisMember axisMembers) {
502    
503            boolean changed = false;
504            List<Member> mList = new ArrayList<Member>();
505            for (ListIterator<Member[]> it = nonAllMembers.listIterator();
506                    it.hasNext();) {
507                Member[] ms = it.next();
508                Hierarchy h = ms[0].getHierarchy();
509                mList.clear();
510                for (Member m : axisMembers) {
511                    if (m.getHierarchy().equals(h)) {
512                        mList.add(m);
513                    }
514                }
515                if (! mList.isEmpty()) {
516                    changed = true;
517                    Member[] ml = mList.toArray(new Member[mList.size()]);
518                    it.set(ml);
519                }
520            }
521            return changed;
522    
523        }
524    
525        protected void loadMembers(List<Member[]> nonAllMembers,
526                        RolapEvaluator evaluator, QueryAxis axis, Calc calc,
527                        AxisMember axisMembers) {
528            int attempt = 0;
529            evaluator.setCellReader(batchingReader);
530            while (true) {
531                axisMembers.clearAxisCount();
532                evalLoad(
533                    nonAllMembers, nonAllMembers.size() - 1,
534                    evaluator, axis, calc, axisMembers);
535    
536                if (!batchingReader.loadAggregations(query)) {
537                    break;
538                } else {
539                    // Clear invalid expression result so that the next evaluation
540                    // will pick up the newly loaded aggregates.
541                    evaluator.clearExpResultCache(false);
542                }
543                
544                if (attempt++ > maxEvalDepth) {
545                    throw Util.newInternal(
546                        "Failed to load all aggregations after " +
547                        maxEvalDepth +
548                        " passes; there's probably a cycle");
549                }
550            }
551        }
552    
553        void evalLoad(List<Member[]> nonAllMembers, int cnt,
554                Evaluator evaluator, QueryAxis axis, Calc calc,
555                AxisMember axisMembers) {
556            if (cnt < 0) {
557                executeAxis(evaluator.push(), axis, calc, false, axisMembers);
558            } else {
559                for (Member m : nonAllMembers.get(cnt)) {
560                    evaluator.setContext(m);
561                    evalLoad(nonAllMembers, cnt-1, evaluator, axis, calc, axisMembers);
562                }
563            }
564        }
565        Axis evalExecute(List<Member[]> nonAllMembers, int cnt,
566                RolapEvaluator evaluator, QueryAxis axis, Calc calc) {
567            Axis axisResult = null;
568            if (cnt < 0) {
569                evaluator.setCellReader(aggregatingReader);
570                axisResult =
571                        executeAxis(evaluator.push(), axis, calc, true, null);
572                // No need to clear expression cache here as no new aggregates are
573                // loaded(aggregatingReader reads from cache).
574            } else {
575                for (Member m : nonAllMembers.get(cnt)) {
576                    evaluator.setContext(m);
577                    Axis a = evalExecute(nonAllMembers, cnt-1, evaluator, axis, calc);
578                    boolean ordered = false;
579                    if (axis != null) {
580                        ordered = axis.isOrdered();
581                    }
582                    axisResult = mergeAxes(axisResult, a, evaluator, ordered);                
583                }
584            }
585            return axisResult;
586        }
587    
588        /**
589         * Finds all root Members 1) whose Hierarchy does not have an ALL
590         * Member, 2) whose default Member is not the ALL Member and 3)
591         * all Measures.
592         *
593         * @param nonDefaultAllMembers  List of all root Members for Hierarchies
594         * whose default Member is not the ALL Member.
595         * @param nonAllMembers List of root Members for Hierarchies that have no
596         * ALL Member.
597         * @param measureMembers  List all Measures
598         */
599        protected void loadSpecialMembers(
600            List<Member> nonDefaultAllMembers,
601            List<Member[]> nonAllMembers,
602            List<Member> measureMembers)
603        {
604            SchemaReader schemaReader = evaluator.getSchemaReader();
605            Member[] evalMembers = evaluator.getMembers();
606            for (Member em : evalMembers) {
607                if (em.isCalculated()) {
608                    continue;
609                }
610                Hierarchy h = em.getHierarchy();
611                Dimension d = h.getDimension();
612                if (d.getDimensionType() == DimensionType.TimeDimension) {
613                    continue;
614                }
615                if (!em.isAll()) {
616                    Member[] rootMembers = schemaReader.getHierarchyRootMembers(h);
617                    if (em.isMeasure()) {
618                        for (Member mm : rootMembers) {
619                            measureMembers.add(mm);
620                        }
621                    } else {
622                        if (h.hasAll()) {
623                            for (Member m : rootMembers) {
624                                if (m.isAll()) {
625                                    nonDefaultAllMembers.add(m);
626                                    break;
627                                }
628                            }
629                        } else {
630                            nonAllMembers.add(rootMembers);
631                        }
632                    }
633                }
634            }
635        }
636    
637        protected Logger getLogger() {
638            return LOGGER;
639        }
640    
641        public final RolapCube getCube() {
642            return evaluator.getCube();
643        }
644    
645        // implement Result
646        public Axis[] getAxes() {
647            return axes;
648        }
649    
650        /**
651         * Get the Cell for the given Cell position.
652         *
653         * @param pos Cell position.
654         * @return the Cell associated with the Cell position.
655         */
656        public Cell getCell(int[] pos) {
657            if (pos.length != point.size()) {
658                throw Util.newError(
659                        "coordinates should have dimension " + point.size());
660            }
661    
662            CellInfo ci = cellInfos.lookup(pos);
663            if (ci.value == null) {
664                for (int i = 0; i < pos.length; i++) {
665                    int po = pos[i];
666                    if (po < 0 || po >= axes[i].getPositions().size()) {
667                        throw Util.newError("coordinates out of range");
668                    }
669                }
670                ci.value = Util.nullValue;
671            }
672    
673            return new RolapCell(this, pos.clone(), ci);
674        }
675    
676        private Axis executeAxis(
677            Evaluator evaluator,
678            QueryAxis axis,
679            Calc axisCalc,
680            boolean construct,
681            AxisMember axisMembers)
682        {
683            Axis axisResult = null;
684            if (axis == null) {
685                // Create an axis containing one position with no members (not
686                // the same as an empty axis).
687                if (construct) {
688                    axisResult = new RolapAxis.SingleEmptyPosition();
689                }
690    
691            } else {
692                evaluator.setNonEmpty(axis.isNonEmpty());
693                evaluator.setEvalAxes(true);
694                Object value = axisCalc.evaluate(evaluator);
695                if (axisCalc.getClass().getName().indexOf("OrderFunDef") != -1) {
696                    axis.setOrdered(true);
697                }            
698                evaluator.setNonEmpty(false);
699                if (value != null) {
700                    // List or Iterable of Member or Member[]
701                    if (value instanceof List) {
702                        List<Object> list = (List) value;
703                        if (construct) {
704                            if (list.size() == 0) {
705                                // should be???
706                                axisResult = new RolapAxis.NoPosition();
707                            } else if (list.get(0) instanceof Member[]) {
708                                axisResult =
709                                    new RolapAxis.MemberArrayList((List<Member[]>)value);
710                            } else {
711                                axisResult =
712                                    new RolapAxis.MemberList((List<Member>)value);
713                            }
714                        } else if (axisMembers != null) {
715                            axisMembers.merge(value);
716                        }
717                    } else {
718                        // Iterable
719                        Iterable<Object> iter = (Iterable) value;
720                        Iterator it = iter.iterator();
721                        if (construct) {
722                            if (! it.hasNext()) {
723                                axisResult = new RolapAxis.NoPosition();
724                            } else if (it.next() instanceof Member[]) {
725                                axisResult = new RolapAxis.MemberArrayIterable(
726                                    (Iterable<Member[]>)value);
727                            } else {
728                                axisResult = new RolapAxis.MemberIterable(
729                                    (Iterable<Member>)value);
730                            }
731                        } else if (axisMembers != null) {
732                            axisMembers.merge(it);
733                        }
734                    }
735                }
736                evaluator.setEvalAxes(false);
737            }
738            return axisResult;
739        }
740    
741        private void executeBody(Query query) {
742            // Compute the cells several times. The first time, use a dummy
743            // evaluator which collects requests.
744            int count = 0;
745            while (true) {
746    
747                evaluator.setCellReader(batchingReader);
748                executeStripe(query.axes.length - 1, evaluator.push());
749    
750                // Retrieve the aggregations collected.
751                //
752                if (!batchingReader.loadAggregations(query)) {
753                    // We got all of the cells we needed, so the result must be
754                    // correct.
755                    return;
756                } else {
757                    // Clear invalid expression result so that the next evaluation
758                    // will pick up the newly loaded aggregates.
759                    evaluator.clearExpResultCache(false);
760                }
761    
762                if (count++ > maxEvalDepth) {
763                    if (evaluator instanceof RolapDependencyTestingEvaluator) {
764                        // The dependency testing evaluator can trigger new
765                        // requests every cycle. So let is run as normal for
766                        // the first N times, then run it disabled.
767                        ((RolapDependencyTestingEvaluator.DteRoot)
768                            evaluator.root).disabled = true;
769                        if (count > maxEvalDepth * 2) {
770                            throw Util.newInternal("Query required more than "
771                                + count + " iterations");
772                        }
773                    } else {
774                        throw Util.newInternal("Query required more than "
775                            + count + " iterations");
776                    }
777                }
778    
779                cellInfos.clear();
780            }
781        }
782    
783        boolean isDirty() {
784            return batchingReader.isDirty();
785        }
786    
787        private Object evaluateExp(Calc calc, RolapEvaluator evaluator) {
788            int attempt = 0;
789            boolean dirty = batchingReader.isDirty();
790            while (true) {
791                RolapEvaluator ev = evaluator.push();
792    
793                ev.setCellReader(batchingReader);
794                Object preliminaryValue = calc.evaluate(ev);
795                Util.discard(preliminaryValue);
796    
797                if (!batchingReader.loadAggregations(evaluator.getQuery())) {
798                    break;
799                } else {
800                    // Clear invalid expression result so that the next evaluation
801                    // will pick up the newly loaded aggregates.
802                    ev.clearExpResultCache(false);
803                }
804                
805                if (attempt++ > maxEvalDepth) {
806                    throw Util.newInternal(
807                            "Failed to load all aggregations after " +
808                            maxEvalDepth + "passes; there's probably a cycle");
809                }
810            }
811    
812            // If there were pending reads when we entered, some of the other
813            // expressions may have been evaluated incorrectly. Set the reader's
814            // 'dirty' flag so that the caller knows that it must re-evaluate them.
815            if (dirty) {
816                batchingReader.setDirty(true);
817            }
818            
819            RolapEvaluator ev = evaluator.push();
820            ev.setCellReader(aggregatingReader);
821            return calc.evaluate(ev);
822        }
823    
824        private void executeStripe(int axisOrdinal, RolapEvaluator revaluator) {
825            if (axisOrdinal < 0) {
826                Axis axis = slicerAxis;
827                List<Position> positions = axis.getPositions();
828                for (Position position: positions) {
829                    getQuery().checkCancelOrTimeout();
830                    revaluator.setContext(position);
831                    Object o;
832                    try {
833                        o = revaluator.evaluateCurrent();
834                    } catch (MondrianEvaluationException e) {
835                        LOGGER.warn("Mondrian: exception in executeStripe.", e);                    
836                        o = e;
837                    }
838    
839                    CellInfo ci = null;
840    
841                    // Get the Cell's format string and value formatting
842                    // Object.
843                    try {
844                        // This code is a combination of the code found in
845                        // the old RolapResult
846                        // <code>getCellNoDefaultFormatString</code> method and
847                        // the old RolapCell <code>getFormattedValue</code> method.
848    
849                        // Create a CellInfo object for the given position
850                        // integer array.
851                        ci = cellInfos.create(point.getOrdinals());
852    
853                        String cachedFormatString = null;
854                        ValueFormatter valueFormatter;
855    
856                        // Determine if there is a CellFormatter registered for
857                        // the current Cube's Measure's Dimension. If so,
858                        // then find or create a CellFormatterValueFormatter
859                        // for it. If not, then find or create a Locale based
860                        // FormatValueFormatter.
861                        final RolapCube cube = getCube();
862                        Dimension measuresDim =
863                            cube.getMeasuresHierarchy().getDimension();
864                        RolapMeasure m =
865                            (RolapMeasure) revaluator.getContext(measuresDim);
866                        CellFormatter cf = m.getFormatter();
867                        if (cf != null) {
868                            valueFormatter = cellFormatters.get(cf);
869                            if (valueFormatter == null) {
870                                valueFormatter = new CellFormatterValueFormatter(cf);
871                                cellFormatters.put(cf, valueFormatter);
872                            }
873                        } else {
874                            cachedFormatString = revaluator.getFormatString();
875                            Locale locale = query.getConnection().getLocale();
876                            valueFormatter = formatValueFormatters.get(locale);
877                            if (valueFormatter == null) {
878                                valueFormatter = new FormatValueFormatter(locale);
879                                formatValueFormatters.put(locale, valueFormatter);
880                            }
881                        }
882    
883                        ci.formatString = cachedFormatString;
884                        ci.valueFormatter = valueFormatter;
885    
886                    } catch (ResultLimitExceededException e) {
887                        // Do NOT ignore a ResultLimitExceededException!!!
888                        throw e;
889    
890                    } catch (MondrianEvaluationException e) {
891                        // ignore but warn
892                        LOGGER.warn("Mondrian: exception in executeStripe.", e);
893                     } catch (Error e) {
894                         // Errors indicate fatal JVM problems; do not discard
895                         throw e;
896                    } catch (Throwable e) {
897                        LOGGER.warn("Mondrian: exception in executeStripe.", e);
898                        Util.discard(e);
899                    }
900    
901                    if (o == RolapUtil.valueNotReadyException) {
902                        continue;
903                    }
904    
905                    ci.value = o;
906                }
907            } else {
908                Axis axis = axes[axisOrdinal];
909                List<Position> positions = axis.getPositions();
910                int i = 0;
911                for (Position position: positions) {
912                    point.setAxis(axisOrdinal, i);
913                    revaluator.setContext(position);
914                    getQuery().checkCancelOrTimeout();
915                    executeStripe(axisOrdinal - 1, revaluator);
916                    i++;
917                }
918            }
919        }
920    
921        /**
922         * Converts a cell ordinal to a set of cell coordinates. Converse of
923         * {@link #getCellOrdinal}. For example, if this result is 10 x 10 x 10,
924         * then cell ordinal 537 has coordinates (5, 3, 7).
925         * <p>
926         * This method is no longer used.
927         */
928        int[] getCellPos(int cellOrdinal) {
929            if (modulos == null) {
930                makeModulos();
931            }
932            return modulos.getCellPos(cellOrdinal);
933        }
934    
935        /**
936         * Converts a set of cell coordinates to a cell ordinal. Converse of
937         * {@link #getCellPos}.
938         * <p>
939         * This method can be expensive, because the ordinal is computed from the
940         * length of the axes, and therefore the axes need to be instantiated.
941         */
942        int getCellOrdinal(int[] pos) {
943            if (modulos == null) {
944                makeModulos();
945            }
946            return modulos.getCellOrdinal(pos);
947        }
948    
949        /*
950         * Instantiates the calculator to convert cell coordinates to a cell ordinal
951         * and vice versa.
952         *
953         * <p>To create the calculator, any axis that is based upon an Iterable is
954         * converted into a List - thus increasing memory usage.
955         */
956        protected void makeModulos() {
957            modulos = Modulos.Generator.create(axes);
958        }
959    
960        /**
961         * Called only by RolapCell.
962         *
963         * @param pos Coordinates of cell
964         * @return Evaluator whose context is the given cell
965         */
966        RolapEvaluator getCellEvaluator(int[] pos) {
967            final RolapEvaluator cellEvaluator = evaluator.push();
968            for (int i = 0; i < pos.length; i++) {
969                Position position = axes[i].getPositions().get(pos[i]);
970                cellEvaluator.setContext(position);
971            }
972            return cellEvaluator;
973        }
974    
975        /**
976         * Called only by RolapCell. Use this when creating an Evaluator
977         * (using method {@link #getCellEvaluator}) is not required.
978         *
979         * @param pos Coordinates of cell
980         * @return Members which form the context of the given cell
981         */
982        Member[] getCellMembers(int[] pos) {
983            Member[] members = evaluator.getMembers().clone();
984            final Cube cube = getCube();
985            for (int i = 0; i < pos.length; i++) {
986                Position position = axes[i].getPositions().get(pos[i]);
987                for (Member member: position) {
988                    RolapMember m = (RolapMember) member;
989                    int ordinal = m.getDimension().getOrdinal(cube);
990                    members[ordinal] = m;
991                }
992    
993            }
994            return members;
995        }
996    
997        Evaluator getRootEvaluator() {
998            return evaluator;
999        }
1000    
1001        Evaluator getEvaluator(int[] pos) {
1002            // Set up evaluator's context, so that context-dependent format
1003            // strings work properly.
1004            Evaluator cellEvaluator = evaluator.push();
1005            for (int i = -1; i < axes.length; i++) {
1006                Axis axis;
1007                int index;
1008                if (i < 0) {
1009                    axis = slicerAxis;
1010                    index = 0;
1011                } else {
1012                    axis = axes[i];
1013                    index = pos[i];
1014                }
1015                Position position = axis.getPositions().get(index);
1016                cellEvaluator.setContext(position);
1017            }
1018            return cellEvaluator;
1019        }
1020    
1021    
1022        /**
1023         * Counts and collects Members found of the axes.
1024         * This class does two things. First it collects all Members
1025         * found during the Member-Determination phase.
1026         * Secondly, it counts how many Members are on each axis and
1027         * forms the product, the totalCellCount which is checked against
1028         * the ResultLimit property value.
1029         */
1030        private static class AxisMember implements Iterable<Member> {
1031            private final List<Member> members;
1032            private final int limit;
1033            private boolean isSlicer;
1034            private int totalCellCount;
1035            private int axisCount;
1036            private boolean countOnly;
1037    
1038            AxisMember() {
1039                this.countOnly = false;
1040                this.members = new ArrayList<Member>();
1041                this.totalCellCount = 1;
1042                this.axisCount = 0;
1043                // Now that the axes are evaluated, make sure that the number of
1044                // cells does not exceed the result limit.
1045                this.limit = MondrianProperties.instance().ResultLimit.get();
1046            }
1047            public Iterator<Member> iterator() {
1048                return members.iterator();
1049            }
1050            void setSlicer(final boolean isSlicer) {
1051                this.isSlicer = isSlicer;
1052            }
1053            boolean isEmpty() {
1054                return this.members.isEmpty();
1055            }
1056            void countOnly(boolean countOnly) {
1057                this.countOnly = countOnly;
1058            }
1059            void checkLimit() {
1060                if (this.limit > 0) {
1061                    this.totalCellCount *= this.axisCount;
1062                    if (this.totalCellCount > this.limit) {
1063                        throw MondrianResource.instance().
1064                            TotalMembersLimitExceeded.ex(this.totalCellCount,
1065                                                            this.limit);
1066                    }
1067                    this.axisCount = 0;
1068                }
1069            }
1070            void clearAxisCount() {
1071                this.axisCount = 0;
1072            }
1073            void clearTotalCellCount() {
1074                this.totalCellCount = 1;
1075            }
1076            void clearMembers() {
1077                this.members.clear();
1078                this.axisCount = 0;
1079                this.totalCellCount = 1;
1080            }
1081            List<Member> members() {
1082                return this.members;
1083            }
1084            void merge(Object value) {
1085                List<Object> list = (List) value;
1086                if (list.size() != 0) {
1087                    if (list.get(0) instanceof Member[]) {
1088                        for (Member[] o : (List<Member[]>) value) {
1089                            merge(o);
1090                        }
1091                    } else {
1092                        for (Member o : (List<Member>) value) {
1093                            merge(o);
1094                        }
1095                    }
1096                }
1097            }
1098            void merge(Iterator it) {
1099                if (it.hasNext()) {
1100                    Object o = it.next();
1101                    if (o instanceof Member[]) {
1102                        merge((Member[]) o);
1103                        while (it.hasNext()) {
1104                            o = it.next();
1105                            merge((Member[]) o);
1106                        }
1107                    } else {
1108                        merge((Member) o);
1109                        while (it.hasNext()) {
1110                            o = it.next();
1111                            merge((Member) o);
1112                        }
1113                    }
1114                }
1115            }
1116            private Member getTopParent(final Member m) {
1117                Member parent = m.getParentMember();
1118                return (parent == null) ? m : getTopParent(parent);
1119            }
1120    
1121            private void merge(final Member[] members) {
1122                for (Member member : members) {
1123                    merge(member);
1124                }
1125            }
1126    
1127            private void merge(final Member member) {
1128                this.axisCount++;
1129                if (! countOnly) {
1130                    if (isSlicer) {
1131                        if (! members.contains(member)) {
1132                            members.add(member);
1133                        }
1134                    } else {
1135                        if (member.isNull()) {
1136                            return;
1137                        } else if (member.isMeasure()) {
1138                            return;
1139                        } else if (member.isCalculated()) {
1140                            return;
1141                        } else if (member.isAll()) {
1142                            return;
1143                        }
1144                        Member topParent = getTopParent(member);
1145                        if (! this.members.contains(topParent)) {
1146                            this.members.add(topParent);
1147                        }
1148                    }
1149                }
1150            }
1151        }
1152    
1153    
1154        /**
1155         * Extension to {@link RolapEvaluator.RolapEvaluatorRoot} which is capable
1156         * of evaluating named sets.<p/>
1157         *
1158         * A given set is only evaluated once each time a query is executed; the
1159         * result is added to the {@link #namedSetValues} cache on first execution
1160         * and re-used.<p/>
1161         *
1162         * Named sets are always evaluated in the context of the slicer.<p/>
1163         */
1164        protected static class RolapResultEvaluatorRoot
1165                extends RolapEvaluator.RolapEvaluatorRoot {
1166            /**
1167             * Maps the names of sets to their values. Populated on demand.
1168             */
1169            private final Map<String, Object> namedSetValues =
1170                new HashMap<String, Object>();
1171    
1172            /**
1173             * Evaluator containing context resulting from evaluating the slicer.
1174             */
1175            private RolapEvaluator slicerEvaluator;
1176            private final RolapResult result;
1177            private static final Object Sentinel = new Object();
1178    
1179            public RolapResultEvaluatorRoot(RolapResult result) {
1180                super(result.query);
1181                this.result = result;
1182            }
1183    
1184            protected void init(Evaluator evaluator) {
1185                slicerEvaluator = (RolapEvaluator) evaluator;
1186            }
1187    
1188            protected Object evaluateNamedSet(String name, Exp exp) {
1189                Object value = namedSetValues.get(name);
1190                if (value == null) {
1191                    final RolapEvaluator.RolapEvaluatorRoot root =
1192                        slicerEvaluator.root;
1193                    final Calc calc = root.getCompiled(exp, false, ResultStyle.LIST);
1194                    Object o = result.evaluateExp(calc, slicerEvaluator.push());
1195                    List list;
1196                    if (o instanceof List) {
1197                        list = (List) o;
1198                    } else {
1199                        // Iterable
1200    
1201                        // TODO:
1202                        // Here, we have to convert the Iterable into a List,
1203                        // materialize it, because in the class
1204                        // mondrian.mdx.NamedSetExpr the Calc returned by the
1205                        // 'accept' method is an AbstractListCalc (hence we must
1206                        // provide a list here). It would be better if the
1207                        // NamedSetExpr class knew if it needed a ListCalc or
1208                        // an IterCalc.
1209                        Iterable iter = (Iterable) o;
1210                        list = new ArrayList();
1211                        for (Object e : iter) {
1212                            list.add(e);
1213                        }
1214                    }
1215                    // Make immutable, just in case expressions are modifying the
1216                    // results we give them.
1217                    value = Collections.unmodifiableList(list);
1218                    namedSetValues.put(name, value);
1219                }
1220                return value;
1221            }
1222    
1223            public Object getParameterValue(ParameterSlot slot) {
1224                Object value = slot.getParameterValue();
1225                if (value != null) {
1226                    return value;
1227                }
1228    
1229                // Look in other places for the value. Which places we look depends
1230                // on the scope of the parameter.
1231                Parameter.Scope scope = slot.getParameter().getScope();
1232                switch (scope) {
1233                case System:
1234                    // TODO: implement system params
1235    
1236                    // fall through
1237                case Schema:
1238                    // TODO: implement schema params
1239    
1240                    // fall through
1241                case Connection:
1242                    // if it's set in the session, return that value
1243    
1244                    // fall through
1245                case Statement:
1246                    break;
1247    
1248                default:
1249                    throw Util.badValue(scope);
1250                }
1251    
1252                // Not set in any accessible scope. Evaluate the default value,
1253                // then cache it.
1254                value = slot.getCachedDefaultValue();
1255                if (value != null) {
1256                    if (value == Sentinel) {
1257                        throw MondrianResource.instance().
1258                            CycleDuringParameterEvaluation.ex(
1259                            slot.getParameter().getName());
1260                    }
1261                    return value;
1262                }
1263                // Set value to a sentinel, so we can detect cyclic evaluation.
1264                slot.setCachedDefaultValue(Sentinel);
1265                value = result.evaluateExp(
1266                    slot.getDefaultValueCalc(), slicerEvaluator.push());
1267                slot.setCachedDefaultValue(value);
1268                return value;
1269            }
1270        }
1271    
1272        /**
1273         * Formatter to convert values into formatted strings.
1274         *
1275         * <p>Every Cell has a value, a format string (or CellFormatter) and a
1276         * formatted value string.
1277         * There are a wide range of possible values (pick a Double, any
1278         * Double - its a value). Because there are lots of possible values,
1279         * there are also lots of possible formatted value strings. On the
1280         * other hand, there are only a very small number of format strings
1281         * and CellFormatter's. These formatters are to be cached
1282         * in a synchronized HashMaps in order to limit how many copies
1283         * need to be kept around.
1284         *
1285         * <p>
1286         * There are two implementations of the ValueFormatter interface:<ul>
1287         * <li>{@link CellFormatterValueFormatter}, which formats using a
1288         * user-registered {@link CellFormatter}; and
1289         * <li> {@link FormatValueFormatter}, which takes the {@link Locale} object.
1290         * </ul>
1291         */
1292        interface ValueFormatter {
1293            String format(Object value, String formatString);
1294        }
1295    
1296        /**
1297         * A CellFormatterValueFormatter uses a user-defined {@link CellFormatter}
1298         * to format values.
1299         */
1300        static class CellFormatterValueFormatter implements ValueFormatter {
1301            final CellFormatter cf;
1302    
1303            /**
1304             * Creates a CellFormatterValueFormatter
1305             *
1306             * @param cf Cell formatter
1307             */
1308            CellFormatterValueFormatter(CellFormatter cf) {
1309                this.cf = cf;
1310            }
1311            public String format(Object value, String formatString) {
1312                return cf.formatCell(value);
1313            }
1314        }
1315    
1316        /**
1317         * A FormatValueFormatter takes a {@link Locale}
1318         * as a parameter and uses it to get the {@link mondrian.util.Format}
1319         * to be used in formatting an Object value with a
1320         * given format string.
1321         */
1322        static class FormatValueFormatter implements ValueFormatter {
1323            final Locale locale;
1324    
1325            /**
1326             * Creates a FormatValueFormatter.
1327             *
1328             * @param locale Locale
1329             */
1330            FormatValueFormatter(Locale locale) {
1331                this.locale = locale;
1332            }
1333            public String format(Object value, String formatString) {
1334                if (value == Util.nullValue) {
1335                    Format format = getFormat(formatString);
1336                    return format.format(null);
1337                } else if (value instanceof Throwable) {
1338                    return "#ERR: " + value.toString();
1339                } else if (value instanceof String) {
1340                    return (String) value;
1341                } else {
1342                    Format format = getFormat(formatString);
1343                    return format.format(value);
1344                }
1345            }
1346            private Format getFormat(String formatString) {
1347                return Format.get(formatString, locale);
1348            }
1349        }
1350    
1351        /*
1352         * Generate a long ordinal based upon the values of the integers
1353         * stored in the cell position array. With this mechanism, the
1354         * Cell information can be stored using a long key (rather than
1355         * the array integer of positions) thus saving memory. The trick