001    /*
002    // $Id: //open/mondrian/src/main/mondrian/rolap/agg/Aggregation.java#56 $
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-2008 Julian Hyde and others
007    // Copyright (C) 2001-2002 Kana Software, Inc.
008    // All Rights Reserved.
009    // You must accept the terms of that agreement to use this software.
010    //
011    // jhyde, 28 August, 2001
012     */
013    
014    package mondrian.rolap.agg;
015    
016    import mondrian.olap.*;
017    import mondrian.rolap.*;
018    
019    import java.io.PrintWriter;
020    import java.lang.ref.SoftReference;
021    import java.util.*;
022    import java.util.concurrent.CopyOnWriteArrayList;
023    
024    /**
025     * A <code>Aggregation</code> is a pre-computed aggregation over a set of
026     * columns.
027     *
028     * <p>Rollup operations:<ul>
029     * <li>drop an unrestricted column (e.g. state=*)</li>
030     * <li>tighten any restriction (e.g. year={1997,1998} becomes
031     * year={1997})</li>
032     * <li>restrict an unrestricted column (e.g. year=* becomes
033     * year={1997})</li>
034     * </ul>
035     *
036     * <p>Representation of aggregations. Sparse and dense representations are
037     * necessary for different data sets. Should adapt automatically. Use an
038     * interface to hold the data set, so the segment doesn't care.</p>
039     *
040     * Suppose we have a segment {year=1997, quarter={1,2,3},
041     * state={CA,WA}}. We want to roll up to a segment for {year=1997,
042     * state={CA,WA}}.  We need to know that we have all quarters.  We don't.
043     * Because year and quarter are independent, we know that we have all of
044     * the ...</p>
045     *
046     * <p>Suppose we have a segment specified by {region=West, state=*,
047     * year=*}, which materializes to ({West}, {CA,WA,OR}, {1997,1998}).
048     * Because state=*, we can rollup to {region=West, year=*} or {region=West,
049     * year=1997}.</p>
050     *
051     * <p>The space required for a segment depends upon the dimensionality (d),
052     * cell count (c) and the value count (v). We don't count the space
053     * required for the actual values, which is the same in any scheme.</p>
054     *
055     * @author jhyde
056     * @version $Id: //open/mondrian/src/main/mondrian/rolap/agg/Aggregation.java#56 $
057     * @since 28 August, 2001
058     */
059    public class Aggregation {
060    
061        /**
062         * The key that uniquely identify this Aggregation. It is also used
063         * to lookup Aggregation.
064         */
065        private AggregationKey aggregationKey;
066    
067        /**
068         * Setting for optimizing sql predicates.
069         */
070        private int maxConstraints;
071    
072        /**
073         * List of soft references to segments.
074         * This List implementation should be Thread safe on all mutative operations
075         * (add, set, and so on). Access to this list is not synchronized in the code.
076         * This is the only mutable field in the class.
077         */
078        private final List<SoftReference<Segment>> segmentRefs;
079    
080        /**
081         * Timestamp of when the aggregation was created. (We use
082         * {@link java.util.Date} rather than {@link java.sql.Timestamp} because it
083         * has less baggage.)
084         */
085        private final Date creationTimestamp;
086    
087        /**
088         * This is set in the load method and is used during
089         * the processing of a particular aggregate load.
090         */
091        private RolapStar.Column[] columns;
092    
093        /**
094         * Creates an Aggregation.
095         *
096         * @param aggregationKey the key specifying the axes, the context and
097         *                       the RolapStar for this Aggregation
098         */
099        public Aggregation(
100                AggregationKey aggregationKey) {
101            this.segmentRefs = getThreadSafeListImplementation();
102            this.maxConstraints =
103                    MondrianProperties.instance().MaxConstraints.get();
104            this.creationTimestamp = new Date();
105            this.aggregationKey = aggregationKey;
106    
107        }
108    
109        private CopyOnWriteArrayList<SoftReference<Segment>> getThreadSafeListImplementation() {
110            return new CopyOnWriteArrayList<SoftReference<Segment>>();
111        }
112    
113        /**
114         * @return Returns the timestamp when the aggregation was created
115         */
116        public Date getCreationTimestamp() {
117            return creationTimestamp;
118        }
119    
120        /**
121         * Loads a set of segments into this aggregation, one per measure,
122         * each constrained by the same set of column values, and each pinned
123         * once.
124         *
125         * <p>A Column and its constraints are accessed at the same level in their
126         * respective arrays.
127         *
128         * <p>For example,
129         * <blockquote><pre>
130         * measures = {unit_sales, store_sales},
131         * state = {CA, OR},
132         * gender = unconstrained</pre></blockquote>
133         */
134        public void load(
135            RolapStar.Column[] columns,
136            RolapStar.Measure[] measures,
137            StarColumnPredicate[] predicates,
138            RolapAggregationManager.PinSet pinnedSegments,
139            GroupingSetsCollector groupingSetsCollector)
140        {
141            // all constrained columns
142            if (this.columns == null) {
143                this.columns = columns;
144            }
145    
146            BitKey measureBitKey = getConstrainedColumnsBitKey().emptyCopy();
147            int axisCount = columns.length;
148            Util.assertTrue(predicates.length == axisCount);
149    
150            // This array of Aggregation.Axis is shared by all Segments for
151            // this set of measures and constraints
152            Aggregation.Axis[] axes = new Aggregation.Axis[axisCount];
153            for (int i = 0; i < axisCount; i++) {
154                axes[i] = new Aggregation.Axis(predicates[i]);
155            }
156            Segment[] segments =
157                addSegmentsToAggregation(
158                    measures, measureBitKey, axes, pinnedSegments);
159            // The constrained columns are simply the level and foreign columns
160            BitKey levelBitKey = getConstrainedColumnsBitKey();
161            GroupingSet groupingSet =
162                new GroupingSet(
163                    segments, levelBitKey, measureBitKey, axes, columns);
164            final List<StarPredicate> compoundPredicateList =
165                aggregationKey.getCompoundPredicateList();
166            if (groupingSetsCollector.useGroupingSets()) {
167                groupingSetsCollector.add(groupingSet);
168                // Segments are loaded using group by grouping sets
169                // by CompositeBatch.loadAggregation
170            } else {
171                new SegmentLoader().load(
172                    Collections.singletonList(groupingSet),
173                    pinnedSegments,
174                    compoundPredicateList);
175            }
176        }
177    
178        private Segment[] addSegmentsToAggregation(
179            RolapStar.Measure[] measures,
180            BitKey measureBitKey,
181            Axis[] axes,
182            RolapAggregationManager.PinSet pinnedSegments)
183        {
184            Segment[] segments = new Segment[measures.length];
185            for (int i = 0; i < measures.length; i++) {
186                RolapStar.Measure measure = measures[i];
187                measureBitKey.set(measure.getBitPosition());
188                Segment segment =
189                    new Segment(
190                        this, measure, axes,
191                        Collections.<Segment.Region>emptyList());
192                segments[i] = segment;
193                SoftReference<Segment> ref = new SoftReference<Segment>(segment);
194                segmentRefs.add(ref);
195                ((AggregationManager.PinSetImpl) pinnedSegments).add(segment);
196            }
197            return segments;
198        }
199    
200        /**
201         * Drops predicates, where the list of values is close to the values which
202         * would be returned anyway.
203         */
204        public StarColumnPredicate[] optimizePredicates(
205                RolapStar.Column[] columns,
206                StarColumnPredicate[] predicates) {
207            RolapStar star = getStar();
208            Util.assertTrue(predicates.length == columns.length);
209            StarColumnPredicate[] newPredicates = predicates.clone();
210            double[] bloats = new double[columns.length];
211    
212            // We want to handle the special case "drilldown" which occurs pretty
213            // often. Here, the parent is here as a constraint with a single member
214            // and the list of children as well.
215            List<Member> potentialParents = new ArrayList<Member>();
216            for (final StarColumnPredicate predicate : predicates) {
217                Member m;
218                if (predicate instanceof MemberColumnPredicate) {
219                    m = ((MemberColumnPredicate) predicate).getMember();
220                    potentialParents.add(m);
221                }
222            }
223    
224            for (int i = 0; i < newPredicates.length; i++) {
225                // A set of constraints with only one entry will not be optimized away
226                if (!(newPredicates[i] instanceof ListColumnPredicate)) {
227                    bloats[i] = 0.0;
228                    continue;
229                }
230    
231                final ListColumnPredicate newPredicate =
232                        (ListColumnPredicate) newPredicates[i];
233                final List<StarColumnPredicate> predicateList =
234                        newPredicate.getPredicates();
235                final int valueCount = predicateList.size();
236                if (valueCount < 2) {
237                    bloats[i] = 0.0;
238                    continue;
239                }
240    
241                if (valueCount > maxConstraints) {
242                    // Some databases can handle only a limited number of elements
243                    // in 'WHERE IN (...)'. This set is greater than this database
244                    // can handle, so we drop this constraint. Hopefully there are
245                    // other constraints that will limit the result.
246                    bloats[i] = 1.0; // will be optimized away
247                    continue;
248                }
249    
250                // more than one - check for children of same parent
251                double constraintLength = (double) valueCount;
252                Member parent = null;
253                Level level = null;
254                for (int j = 0; j < valueCount; j++) {
255                    Object value = predicateList.get(j);
256                    if (value instanceof MemberColumnPredicate) {
257                        MemberColumnPredicate memberColumnPredicate =
258                                (MemberColumnPredicate) value;
259                        Member m = memberColumnPredicate.getMember();
260                        if (j == 0) {
261                            parent = m.getParentMember();
262                            level = m.getLevel();
263                        } else {
264                            if (parent != null
265                                    && !parent.equals(m.getParentMember())) {
266                                parent = null; // no common parent
267                            }
268                            if (level != null
269                                    && !level.equals(m.getLevel())) {
270                                // should never occur, constraints are of same level
271                                level = null;
272                            }
273                        }
274                    } else {
275                        // Value constraint with no associated member.
276                        // Compute bloat by #constraints / column cardinality.
277                        parent = null;
278                        level = null;
279                        bloats[i] = constraintLength / columns[i].getCardinality();
280                        break;
281                    }
282                }
283                boolean done = false;
284                if (parent != null) {
285                    // common parent exists
286                    if (parent.isAll() || potentialParents.contains(parent)) {
287                        // common parent is there as constraint
288                        //  if the children are complete, this constraint set is
289                        //  unneccessary try to get the children directly from
290                        //  cache for the drilldown case, the children will be
291                        //  in the cache
292                        // - if not, forget this optimization.
293                        SchemaReader scr = star.getSchema().getSchemaReader();
294                        int childCount = scr.getChildrenCountFromCache(parent);
295                        if (childCount == -1) {
296                            // nothing gotten from cache
297                            if (!parent.isAll()) {
298                                // parent is in constraints
299                                // no information about children cardinality
300                                //  constraints must not be optimized away
301                                bloats[i] = 0.0;
302                                done = true;
303                            }
304                        } else {
305                            bloats[i] = constraintLength / childCount;
306                            done = true;
307                        }
308                    }
309                }
310    
311                if (!done && level != null) {
312                    // if the level members are cached, we do not need "count *"
313                    SchemaReader scr = star.getSchema().getSchemaReader();
314                    int memberCount = scr.getLevelCardinality(level, true, false);
315                    if (memberCount > 0) {
316                        bloats[i] = constraintLength / memberCount;
317                        done = true;
318                    }
319                }
320    
321                if (!done) {
322                    bloats[i] = constraintLength / columns[i].getCardinality();
323                }
324            }
325    
326            // build a list of constraints sorted by 'bloat factor'
327            ConstraintComparator comparator = new ConstraintComparator(bloats);
328            Integer[] indexes = new Integer[columns.length];
329            for (int i = 0; i < columns.length; i++) {
330                indexes[i] = i;
331            }
332    
333            // sort indexes by bloat descending
334            Arrays.sort(indexes, comparator);
335    
336            // Eliminate constraints one by one, until the constrained cell count
337            // became half of the unconstrained cell count. We can not have an
338            // absolute value here, because its
339            // very different if we fetch data for 2 years or 10 years (5 times
340            // more means 5 times slower). So a relative comparison is ok here
341            // but not an absolute one.
342    
343            double abloat = 1.0;
344            final double aBloatLimit = .5;
345    
346            for (Integer j : indexes) {
347                abloat = abloat * bloats[j];
348                if (abloat <= aBloatLimit) {
349                    break;
350                }
351                // eliminate this constraint
352                if (MondrianProperties.instance().OptimizePredicates.get()
353                        || bloats[j] == 1) {
354                    newPredicates[j] = new LiteralStarPredicate(columns[j], true);
355                }
356            }
357            return newPredicates;
358        }
359    
360        public String toString() {
361            java.io.StringWriter sw = new java.io.StringWriter(256);
362            PrintWriter pw = new PrintWriter(sw);
363            print(pw);
364            pw.flush();
365            return sw.toString();
366        }
367    
368        /**
369         * Prints the state of this <code>Aggregation</code> to a writer.
370         *
371         * @param pw Writer
372         */
373        public void print(PrintWriter pw) {
374            List<Segment> segmentList = new ArrayList<Segment>();
375            for (SoftReference<Segment> ref : segmentRefs) {
376                Segment segment = ref.get();
377                if (segment == null) {
378                    continue;
379                }
380                segmentList.add(segment);
381            }
382    
383            // Sort segments, to make order deterministic.
384            Collections.sort(
385                    segmentList,
386                    new Comparator<Segment>() {
387                        public int compare(Segment o1, Segment o2) {
388                            return o1.id - o2.id;
389                        }
390                    });
391    
392            for (Segment segment : segmentList) {
393                segment.print(pw);
394            }
395        }
396    
397        public void flush(
398                CacheControl cacheControl,
399                RolapCacheRegion cacheRegion) {
400            // Compare the bitmaps.
401            //
402            // Case 1: aggregate bitmap contains request bitmap.
403            // E.g. agg = (year={1997, 1998}, quarter=*, nation=USA),
404            //      request = (year=1997, nation={USA, Canada}).
405            // Assuming descendants (which we do, for now) flush the segment
406            // based on the {Year, Nation} values:
407            //      flush = (year=1997, quarter=*, nation=USA)
408            //
409            // Case 2: aggregate bitmap is strict subset of request bitmap
410            // E.g. agg = (year={1997, 1998}, nation=*)
411            //      request = (year={1997}, nation=*, gender="F")
412            // This segment isn't constrained on gender, therefore all cells could
413            // contain gender="F" values:
414            //      flush = (year=1997, nation=*)
415            //
416            // Case 3: no overlap
417            // E.g. agg = (product, gender),
418            //      request = (year=1997)
419            // This segment isn't constrained on year, therefore all cells could
420            // contain 1997 values. Flush the whole segment.
421            //
422            // The rule is:
423            //  - Column in flush request and in segment. Apply constraints.
424            //  - Column in flush request, not in segment. Ignore it.
425            //  - Column not in flush request, in segment. Ignore it.
426            final boolean bitmapsIntersect =
427                    cacheRegion.getConstrainedColumnsBitKey().intersects(
428                            getConstrainedColumnsBitKey());
429    
430            // New list of segments - will replace segmentRefs when we're done.
431            List<SoftReference<Segment>> newSegmentRefs =
432                    new ArrayList<SoftReference<Segment>>();
433            segmentLoop:
434            for (SoftReference<Segment> segmentRef : segmentRefs) {
435                Segment segment = segmentRef.get();
436                if (segment == null) {
437                    // Segment has been garbage collected. Flush it.
438                    cacheControl.trace("discarding garbage collected segment");
439                    continue;
440                }
441                if (!bitmapsIntersect) {
442                    // No intersection between the columns constraining the flush
443                    // and the columns defining the segment. Therefore, the segment
444                    // is definitely affected. Flush it.
445                    cacheControl.trace(
446                            "discard segment - it has no columns in common: " +
447                                    segment);
448                    continue;
449                }
450    
451                // For each axis, indicates which values will be retained when
452                // constraints have been applied.
453                BitSet[] axisKeepBitSets = new BitSet[columns.length];
454                for (int i = 0; i < columns.length; i++) {
455                    final Axis axis = segment.axes[i];
456                    int keyCount = axis.getKeys().length;
457                    final BitSet axisKeepBitSet
458                            = axisKeepBitSets[i]
459                            = new BitSet(keyCount);
460                    final StarColumnPredicate predicate = axis.predicate;
461                    assert predicate != null;
462    
463                    RolapStar.Column column = columns[i];
464                    if (!cacheRegion.getConstrainedColumnsBitKey().get(
465                            column.getBitPosition())) {
466                        axisKeepBitSet.set(0, keyCount);
467                        continue;
468                    }
469                    StarColumnPredicate flushPredicate =
470                            cacheRegion.getPredicate(column.getBitPosition());
471    
472                    // If the flush request is not constrained on this column, move
473                    // on to the next column.
474                    if (flushPredicate == null) {
475                        axisKeepBitSet.set(0, keyCount);
476                        continue;
477                    }
478    
479                    // If the segment is constrained on this column,
480                    // and the flush request is constrained on this column,
481                    // and the constraints do not intersect,
482                    // then this flush request does not affect this segment.
483                    // Keep it.
484                    if (!flushPredicate.mightIntersect(predicate)) {
485                        newSegmentRefs.add(segmentRef);
486                        continue segmentLoop;
487                    }
488    
489                    // The flush constraints overlap. We need to create a new
490                    // constraint which captures what is actually in this segment.
491                    //
492                    // After the flush, values explicitly flushed must be outside
493                    // the constraints of the axis. In particular, if the axis is
494                    // initially unconstrained, contains the values {X, Y, Z}, and
495                    // value Z is flushed, the new constraint of the axis will be
496                    // {X, Y}. This will force the reader to look to another segment
497                    // for the Z value, rather than assuming that it does not exist.
498                    //
499                    // Example #1. Column constraint is {A, B, C},
500                    // actual values are {A, B},
501                    // flush is {A, D}. New constraint could be
502                    // either {B, C} (constraint minus flush)
503                    // or {B} (actual minus flush).
504                    //
505                    // Example #2. Column constraint is * (unconstrained),
506                    // actual values are {A, B},
507                    // flush is {A, D}. New constraint must be
508                    // {B} (actual minus flush) because mondrian cannot model
509                    // negative constraints on segments.
510                    final Object[] axisKeys = axis.getKeys();
511                    for (int k = 0; k < axisKeys.length; k++) {
512                        Object key = axisKeys[k];
513                        if (!flushPredicate.evaluate(key)) {
514                            axisKeepBitSet.set(k);
515                        }
516                    }
517                }
518    
519                // Now go through the multi-column constraints, and eliminate any
520                // values which are always blocked by a given predicate.
521                for (StarPredicate predicate : cacheRegion.getPredicates()) {
522                    ValuePruner pruner =
523                            new ValuePruner(
524                                    predicate,
525                                    segment.axes,
526                                    segment.getData());
527                    pruner.go(axisKeepBitSets);
528                }
529    
530                // Figure out which of the axes retains most of its values.
531                float bestRetention = 0f;
532                int bestColumn = -1;
533                for (int i = 0; i < columns.length; i++) {
534                    // What proportion of the values on this axis survived the flush
535                    // constraint? 1.0 means they all survived. This means that none
536                    // of the cells in the segment will be discarded.
537                    // But we still need to tighten the constraints on the
538                    // segment, in case new axis values have appeared.
539                    RolapStar.Column column = columns[i];
540                    final int bitPosition = column.getBitPosition();
541                    if (!cacheRegion.getConstrainedColumnsBitKey().get(bitPosition)) {
542                        continue;
543                    }
544    
545                    final BitSet axisBitSet = axisKeepBitSets[i];
546                    final Axis axis = segment.axes[i];
547                    final Object[] axisKeys = axis.getKeys();
548    
549                    if (axisBitSet.cardinality() == 0) {
550                        // If one axis is empty, the entire segment is empty.
551                        // Discard it.
552                        continue segmentLoop;
553                    }
554    
555                    float retention =
556                            (float) axisBitSet.cardinality() /
557                                    (float) axisKeys.length;
558    
559                    if (bestColumn == -1 || retention > bestRetention) {
560                        // If there are multiple partially-satisfied
561                        // constraints ANDed together, keep the constraint
562                        // which is least selective.
563                        bestRetention = retention;
564                        bestColumn = i;
565                    }
566                }
567    
568                // Come up with an estimate of how many cells this region contains.
569                List<StarColumnPredicate> regionPredicates =
570                        new ArrayList<StarColumnPredicate>();
571                int cellCount = 1;
572                for (int i = 0; i < this.columns.length; i++) {
573                    RolapStar.Column column = this.columns[i];
574                    Axis axis = segment.axes[i];
575                    final int pos = column.getBitPosition();
576                    StarColumnPredicate flushPredicate =
577                            cacheRegion.getPredicate(pos);
578                    int keysMatched;
579                    if (flushPredicate == null) {
580                        flushPredicate = LiteralStarPredicate.TRUE;
581                        keysMatched = axis.getKeys().length;
582                    } else {
583                        keysMatched = axis.getMatchCount(flushPredicate);
584                    }
585                    cellCount *= keysMatched;
586                    regionPredicates.add(flushPredicate);
587                }
588    
589                // We don't know the selectivity of multi-column predicates
590                // (typically member predicates such as '>= [Time].[1997].[Q2]') so
591                // we guess 50% selectivity.
592                for (StarPredicate p : cacheRegion.getPredicates()) {
593                    cellCount *= .5;
594                }
595                Segment.Region region =
596                        new Segment.Region(
597                                regionPredicates,
598                                new ArrayList<StarPredicate>(cacheRegion.getPredicates()),
599                                cellCount);
600    
601                // How many cells left after we exclude this region? If there are
602                // none left, throw away the segment. It doesn't matter if we
603                // over-estimate how many cells are in the region, and therefore
604                // throw away a segment which has a few cells left.
605                int remainingCellCount = segment.getCellCount();
606                if (remainingCellCount - cellCount <= 0) {
607                    continue;
608                }
609    
610                // Add the flush region to the list of excluded regions.
611                //
612                // TODO: If the region has been fully accounted for in changes to
613                // the predicates on the axes, then don't add it to the exclusion
614                // list.
615                final List<Segment.Region> excludedRegions =
616                        new ArrayList<Segment.Region>(segment.getExcludedRegions());
617                if (!excludedRegions.contains(region)) {
618                    excludedRegions.add(region);
619                }
620    
621                StarColumnPredicate bestColumnPredicate;
622                if (bestColumn >= 0) {
623                    // Instantiate the axis with the best retention.
624                    RolapStar.Column column = columns[bestColumn];
625                    final int bitPosition = column.getBitPosition();
626                    StarColumnPredicate flushPredicate =
627                            cacheRegion.getPredicate(bitPosition);
628                    final Axis axis = segment.axes[bestColumn];
629                    bestColumnPredicate = axis.predicate;
630                    if (flushPredicate != null) {
631                        bestColumnPredicate =
632                                bestColumnPredicate.minus(flushPredicate);
633                    }
634                } else {
635                    bestColumnPredicate = null;
636                }
637    
638                final Segment newSegment =
639                        segment.createSubSegment(
640                                axisKeepBitSets,
641                                bestColumn,
642                                bestColumnPredicate,
643                                excludedRegions);
644    
645                newSegmentRefs.add(new SoftReference<Segment>(newSegment));
646            }
647    
648            // Replace list of segments.
649            // FIXME: Synchronize.
650            // TODO: Replace segmentRefs, don't copy.
651            segmentRefs.clear();
652            segmentRefs.addAll(newSegmentRefs);
653        }
654    
655        /**
656         * Retrieves the value identified by <code>keys</code>.
657         * If the requested cell is found in the loading segment, current Thread
658         * will be blocked until segment is loaded.
659         *
660         * <p>If <code>pinSet</code> is not null, pins the
661         * segment which holds it. <code>pinSet</code> ensures that a segment is
662         * only pinned once.
663         *
664         * <p>Returns <code>null</code> if no segment contains the cell.
665         *
666         * <p>Returns {@link Util#nullValue} if a segment contains the cell and the
667         * cell's value is null.
668         */
669        public Object getCellValue(
670                RolapStar.Measure measure,
671                Object[] keys,
672                RolapAggregationManager.PinSet pinSet) {
673            for (SoftReference<Segment> segmentref : segmentRefs) {
674                Segment segment = segmentref.get();
675                if (segment == null) {
676                    segmentRefs.remove(segmentref);
677                    continue; // it's being garbage-collected
678                }
679                if (segment.measure != measure) {
680                    continue;
681                }
682                if (segment.isReady()) {
683                    Object o = segment.getCellValue(keys);
684                    if (o != null) {
685                        if (pinSet != null) {
686                            ((AggregationManager.PinSetImpl) pinSet).add(segment);
687                        }
688                        return o;
689                    }
690                } else {
691                    // avoid to call wouldContain - its slow
692                    if (pinSet != null
693                            && !((AggregationManager.PinSetImpl) pinSet).contains(segment)
694                            && segment.wouldContain(keys)) {
695                        segment.waitUntilLoaded(); //Waiting on Segment state
696                        if (segment.isReady()) {
697                            ((AggregationManager.PinSetImpl) pinSet).add(segment);
698                            return segment.getCellValue(keys);
699                        }
700                    }
701                }
702            }
703            // No segment contains the requested cell.
704            return null;
705        }
706    
707        /**
708         * This is called during Sql generation.
709         */
710        public RolapStar.Column[] getColumns() {
711            return columns;
712        }
713    
714        /**
715         * This is called during Sql generation.
716         */
717        public RolapStar getStar() {
718            return aggregationKey.getStar();
719        }
720    
721        /**
722         * Returns the BitKey for ALL columns (Measures and Levels) involved in the
723         * query.
724         */
725        public BitKey getConstrainedColumnsBitKey() {
726            return aggregationKey.getConstrainedColumnsBitKey();
727        }
728    
729        // -- classes -------------------------------------------------------------
730    
731        static class Axis {
732    
733            /**
734             * Constraint on the keys in this Axis. Never null.
735             */
736            private final StarColumnPredicate predicate;
737    
738            /**
739             * Map holding the position of each key value.
740             *
741             * <p>TODO: Hold keys in a sorted array, then deduce ordinal by doing
742             * binary search.
743             */
744            private final Map<Comparable<?>, Integer> mapKeyToOffset =
745                    new HashMap<Comparable<?>, Integer>();
746    
747            /**
748             * Actual key values retrieved.
749             */
750            private Comparable<?>[] keys;
751            private boolean hasNull;
752    
753            /**
754             * Creates an empty Axis.
755             *
756             * @param predicate Predicate defining which keys should appear on
757             *                  axis. (If a key passes the predicate but is not in the list, every
758             *                  cell with that key is assumed to have a null value.)
759             */
760            Axis(StarColumnPredicate predicate) {
761                this.predicate = predicate;
762                assert predicate != null;
763            }
764    
765            /**
766             * Creates an axis populated with a set of keys.
767             *
768             * @param predicate Predicate defining which keys should appear on
769             *                  axis. (If a key passes the predicate but is not in the list, every
770             *                  cell with that key is assumed to have a null value.)
771             * @param keys      Keys
772             */
773            Axis(StarColumnPredicate predicate, Comparable<?>[] keys) {
774                this(predicate);
775                this.keys = keys;
776                for (int i = 0; i < keys.length; i++) {
777                    Comparable<?> key = keys[i];
778                    mapKeyToOffset.put(key, i);
779                    assert i == 0 ||
780                            ((Comparable) keys[i - 1]).compareTo(keys[i]) < 0;
781                }
782            }
783    
784            StarColumnPredicate getPredicate() {
785                return predicate;
786            }
787    
788            Comparable<?>[] getKeys() {
789                return this.keys;
790            }
791    
792            /**
793             * Loads keys into the axis.
794             *
795             * @param valueSet Set of distinct key values, sorted
796             * @param hasNull  Whether the axis contains the null value, in addition
797             *                 to the values in <code>valueSet</code>
798             * @return Number of keys on axis
799             */
800            int loadKeys(SortedSet<Comparable<?>> valueSet, boolean hasNull) {
801                this.hasNull = hasNull;
802                int size = valueSet.size();
803    
804                if (hasNull) {
805                    size++;
806                }
807                keys = new Comparable<?>[size];
808    
809                valueSet.toArray(keys);
810                if (hasNull) {
811                    keys[size - 1] = RolapUtil.sqlNullValue;
812                }
813    
814                for (int i = 0; i < size; i++) {
815                    mapKeyToOffset.put(keys[i], i);
816                }
817    
818                return size;
819            }
820    
821            static final Comparable wrap(Object o) {
822                if (Util.PreJdk15 && o instanceof Boolean) {
823                    return Integer.valueOf((Boolean) o ? 1 : 0);
824                } else {
825                    return (Comparable) o;
826                }
827            }
828    
829            final int getOffset(Object o) {
830                return getOffset(wrap(o));
831            }
832    
833            final int getOffset(Comparable key) {
834                Integer ordinal = mapKeyToOffset.get(key);
835                if (ordinal == null) {
836                    return -1;
837                }
838                return ordinal.intValue();
839            }
840    
841            /**
842             * Returns whether this axis contains a given key, or would contain it
843             * if it existed.
844             *
845             * <p>For example, if this axis is unconstrained, then this method
846             * returns <code>true</code> for any value.
847             *
848             * @param key Key
849             * @return Whether this axis would contain <code>key</code>
850             */
851            boolean contains(Object key) {
852                return predicate.evaluate(key);
853            }
854    
855            /**
856             * Returns how many of this Axis' keys match a given constraint.
857             *
858             * @param predicate Predicate
859             * @return How many keys match constraint
860             */
861            public int getMatchCount(StarColumnPredicate predicate) {
862                int matchCount = 0;
863                for (Object key : keys) {
864                    if (predicate.evaluate(key)) {
865                        ++matchCount;
866                    }
867                }
868                return matchCount;
869            }
870        }
871    
872        /**
873         * Helper class to figure out which axis values evaluate to true at least
874         * once by a given predicate.
875         *
876         * <p>Consider, for example, the flush predicate<blockquote><code>
877         *
878         * member between [Time].[1997].[Q3] and [Time].[1999].[Q1]
879         *
880         * </code></blockquote>applied to the segment <blockquote><code>
881         *
882         * year in (1996, 1997, 1998, 1999)<br/>
883         * quarter in (Q1, Q2, Q3, Q4)
884         *
885         * </code></blockquote> The predicate evaluates to true for the pairs
886         * <blockquote><code>
887         *
888         * {(1997, Q3), (1997, Q4),
889         * (1998, Q1), (1998, Q2), (1998, Q3), (1998, Q4), (1999, Q1)}
890         *
891         * </code></blockquote> and therefore we wish to eliminate these pairs from
892         * the segment. But we can eliminate a value only if <em>all</em> of its
893         * values are eliminated.
894         *
895         * <p>In this case, year=1998 is the only value which can be eliminated from
896         * the segment.
897         */
898        private static class ValuePruner {
899            /**
900             * Multi-column predicate. If the predicate evaluates to true, a cell
901             * will be removed from the segment. But we can only eliminate a value
902             * if all of its cells are eliminated.
903             */
904            private final StarPredicate flushPredicate;
905            /**
906             * Number of columns predicate depends on.
907             */
908            private final int arity;
909            /**
910             * For each column, the segment axis which the column corresponds to, or
911             * null.
912             */
913            private final Axis[] axes;
914            /**
915             * For each column, a bitmap of values for which the predicate is
916             * sometimes false. These values cannot be eliminated from the axis.
917             */
918            private final BitSet[] keepBitSets;
919            /**
920             * For each segment axis, the predicate column which depends on the
921             * axis, or -1.
922             */
923            private final int[] axisInverseOrdinals;
924            /**
925             * Workspace which contains the current key value for each column.
926             */
927            private final Object[] values;
928            /**
929             * View onto {@link #values} as a list.
930             */
931            private final List<Object> valueList;
932            /**
933             * Workspace which contains the ordinal of the current value of each
934             * column on its axis.
935             */
936            private final int[] ordinals;
937    
938            private final SegmentDataset data;
939    
940            private final CellKey cellKey;
941    
942            /**
943             * Creates a ValuePruner.
944             *
945             * @param flushPredicate Multi-column predicate to test
946             * @param segmentAxes    Axes of the segment. (The columns that the
947             *                       predicate may not be present, or may be in a different order.)
948             * @param data           Segment dataset, which allows pruner to determine whether
949             *                       a particular cell is currently empty
950             */
951            ValuePruner(
952                    StarPredicate flushPredicate,
953                    Axis[] segmentAxes,
954                    SegmentDataset data) {
955                this.flushPredicate = flushPredicate;
956                this.arity = flushPredicate.getConstrainedColumnList().size();
957                this.axes = new Axis[arity];
958                this.keepBitSets = new BitSet[arity];
959                this.axisInverseOrdinals = new int[segmentAxes.length];
960                Arrays.fill(axisInverseOrdinals, -1);
961                this.values = new Object[arity];
962                this.valueList = Arrays.asList(values);
963                this.ordinals = new int[arity];
964                assert data != null;
965                this.data = data;
966                this.cellKey = CellKey.Generator.newCellKey(segmentAxes.length);
967    
968                // Pair up constraint columns with axes. If one of the constraint's
969                // columns is not in this segment, it gets the null axis. The
970                // constraint will have to evaluate to true for all possible values
971                // of that column.
972                for (int i = 0; i < arity; i++) {
973                    RolapStar.Column column =
974                            flushPredicate.getConstrainedColumnList().get(i);
975                    int axisOrdinal =
976                            findAxis(segmentAxes, column.getBitPosition());
977                    if (axisOrdinal < 0) {
978                        this.axes[i] = null;
979                        values[i] = StarPredicate.WILDCARD;
980                        keepBitSets[i] = new BitSet(1); // dummy
981                    } else {
982                        axes[i] = segmentAxes[axisOrdinal];
983                        axisInverseOrdinals[axisOrdinal] = i;
984                        final int keyCount = axes[i].getKeys().length;
985                        keepBitSets[i] = new BitSet(keyCount);
986                    }
987                }
988            }
989    
990            private int findAxis(Axis[] axes, int bitPosition) {
991                for (int i = 0; i < axes.length; i++) {
992                    Axis axis = axes[i];
993                    if (axis.getPredicate().getConstrainedColumn().getBitPosition()
994                            == bitPosition) {
995                        return i;
996                    }
997                }
998                return -1;
999            }
1000    
1001            /**
1002             * Applies this ValuePruner's predicate and sets bits in axisBitSets
1003             * to indicate extra values which can be removed.
1004             *
1005             * @param axisKeepBitSets Array containing, for each axis, a bitset
1006             *                        of values to keep (not flush)
1007             */
1008            void go(BitSet[] axisKeepBitSets) {
1009                evaluatePredicate(0);
1010    
1011                // Clear bits in the axis bit sets (indicating that a value is never
1012                // used) if this predicate evaluates to true for every combination
1013                // of values which this axis value appears in.
1014                for (int i = 0; i < axisKeepBitSets.length; i++) {
1015                    if (axisInverseOrdinals[i] < 0) {
1016                        continue;
1017                    }
1018                    BitSet axisKeepBitSet = axisKeepBitSets[axisInverseOrdinals[i]];
1019                    final BitSet keepBitSet = keepBitSets[i];
1020                    axisKeepBitSet.and(keepBitSet);
1021                }
1022            }
1023    
1024            /**
1025             * Evaluates the predicate for axes <code>i</code> and higher, and marks
1026             * {@link #keepBitSets} if the predicate ever evaluates to false.
1027             * The result is that discardBitSets[i] will be false for column #i if
1028             * the predicate evaluates to true for all cells in the segment which
1029             * have that column value.
1030             *
1031             * @param axisOrdinal Axis ordinal
1032             */
1033            private void evaluatePredicate(int axisOrdinal) {
1034                if (axisOrdinal == arity) {
1035                    // If the flush predicate evaluates to false for this cell,
1036                    // and this cell currently has some data (*),
1037                    // then none of the values which are the coordinates of this
1038                    // cell can be discarded.
1039                    //
1040                    // * Important when there is sparsity. Consider the cell
1041                    // {year=1997, quarter=Q1, month=12}. This cell would never have
1042                    // data, so there's no point keeping it.
1043                    if (!flushPredicate.evaluate(valueList)) {
1044                        if (data.get(cellKey) != null) {
1045                            for (int k = 0; k < arity; k++) {
1046                                keepBitSets[k].set(ordinals[k]);
1047                            }
1048                        }
1049                    }
1050                } else {
1051                    final Axis axis = axes[axisOrdinal];
1052                    if (axis == null) {
1053                        evaluatePredicate(axisOrdinal + 1);
1054                    } else {
1055                        for (int keyOrdinal = 0; keyOrdinal < axis.keys.length; keyOrdinal++) {
1056                            Object key = axis.keys[keyOrdinal];
1057                            values[axisOrdinal] = key;
1058                            ordinals[axisOrdinal] = keyOrdinal;
1059                            cellKey.setAxis(
1060                                    axisInverseOrdinals[axisOrdinal],
1061                                    keyOrdinal);
1062                            evaluatePredicate(axisOrdinal + 1);
1063                        }
1064                    }
1065                }
1066            }
1067        }
1068    
1069        private static class ConstraintComparator implements Comparator<Integer> {
1070            private final double[] bloats;
1071    
1072            ConstraintComparator(double[] bloats) {
1073                this.bloats = bloats;
1074            }
1075    
1076            // implement Comparator
1077            // order by bloat descending
1078            public int compare(Integer o0, Integer o1) {
1079                double bloat0 = bloats[o0];
1080                double bloat1 = bloats[o1];
1081                return (bloat0 == bloat1)
1082                        ? 0
1083                        : (bloat0 < bloat1)
1084                        ? 1
1085                        : -1;
1086            }
1087        }
1088    
1089    
1090    }
1091    
1092    // End Aggregation.java