001/*
002// This software is subject to the terms of the Eclipse Public License v1.0
003// Agreement, available at the following URL:
004// http://www.eclipse.org/legal/epl-v10.html.
005// You must accept the terms of that agreement to use this software.
006//
007// Copyright (C) 2002-2005 Julian Hyde
008// Copyright (C) 2005-2012 Pentaho and others
009// All Rights Reserved.
010//
011// jhyde, 21 March, 2002
012*/
013package mondrian.rolap.agg;
014
015import mondrian.rolap.*;
016
017import java.util.*;
018
019/**
020 * A <code>CellRequest</code> contains the context necessary to get a cell
021 * value from a star.
022 *
023 * @author jhyde
024 * @since 21 March, 2002
025 */
026public class CellRequest {
027    private final RolapStar.Measure measure;
028    public final boolean extendedContext;
029    public final boolean drillThrough;
030
031    /*
032     * Sparsely populated array of column predicates.  Each predicate will
033     * be located according to the bitPosition of the column to which it
034     * corresponds.  This costs a little memory in terms of unused array
035     * slots, but avoids the need to explicitly sort the column predicates
036     * into a canonical order. There aren't usually a lot of predicates to
037     * sort, but that time adds up quickly.
038     */
039    private StarColumnPredicate[] sparseColumnPredicateList;
040
041    /**
042     * An array that contains the bit positions of each constrained
043     * column.
044     *
045     * <p>Used to allow us to convert from a numeric index (i.e., give me the
046     * third constrained column) to the actual column as referenced
047     * by bit position.
048     *
049     * For example, if the three constrained columns have bitPositions 3, 16,
050     * and 21, the contents of this array should be [3,16,21].
051     */
052    private int[] columnBitPositions;
053
054    /**
055     * Tracks the number of column constraints actually associated with this
056     * CellRequest. We could figure this out by iterating over
057     * sparseColumnPredicateList, but it's quicker to just track them
058     * as they are added.
059     */
060    private int numColumns;
061
062    /**
063     * Reference back to the CellRequest's star.  All CellRequests in a
064     * given query are associated with a single star. Keeping this
065     * reference allows us to maintain a list of columns by bit position
066     * in just one place (the star) rather than duplicate that
067     * information in each CellRequest.
068     */
069    private RolapStar star = null;
070
071    /*
072     * Array of column values;
073     * Not used to represent the compound members along one or more dimensions.
074     */
075    private Object[] singleValues;
076
077    /**
078     * After all of the columns are loaded, the columnsCache is created
079     * the first time the getColumns method (or any method that itself
080     * calls the check method) is called.
081     *
082     * <p>It is assumed that the call to all additional columns,
083     * {@link #addConstrainedColumn}, will not be called after the first call
084     * to the {@link #getConstrainedColumns()} method.
085     *
086     * <p>TODO: Since the expectation is that the columns do not change once
087     * set, it may be worth either caching these structures so that only one
088     * exists for every unique combination, or eliminating the cache and
089     * creating a wrapper that makes the bit key + star's column list look
090     * like a column array.
091     */
092    private RolapStar.Column[] columnsCache = null;
093
094    /**
095     * A bit is set for each column in the column list. Allows us to rapidly
096     * figure out whether two requests are for the same column set.
097     * These are all of the columns that are involved with a query, that is, all
098     * required to be present in an aggregate table for the table be used to
099     * fulfill the query.
100     */
101    private final BitKey constrainedColumnsBitKey;
102
103    /**
104     * Map from BitKey (representing a group of columns that forms a
105     * compound key) to StarPredicate (representing the predicate
106     * defining the compound member).
107     *
108     * <p>We use LinkedHashMap so that the entries occur in deterministic
109     * order; otherwise, successive runs generate different SQL queries.
110     * Another solution worth considering would be to use the inherent ordering
111     * of BitKeys and create a sorted map.
112     *
113     * <p>Creating CellRequests is one of the top hotspots in Mondrian.
114     * Therefore we initialize the map to null, and don't create a map until
115     * we add the first entry.
116     *
117     * <p>The map (when not null) is sorted by key, to allow more rapid
118     * comparison with maps of other requests and with existing segments.</p>
119     */
120    private SortedMap<BitKey, StarPredicate> compoundPredicateMap = null;
121
122    /**
123     * Whether the request is impossible to satisfy. This is set to 'true' if
124     * contradictory constraints are applied to the same column. For example,
125     * the levels [Customer].[City] and [Cities].[City] map to the same column
126     * via the same join-path, and one constraint sets city = 'Burbank' and
127     * another sets city = 'Los Angeles'.
128     */
129    private boolean unsatisfiable;
130
131    /**
132     * The columnPredicateList and columnsCache must be set after all
133     * constraints have been added. This is used by access methods to determine
134     * if both columnPredicateList and columnsCache need to be generated.
135     */
136    private boolean isDirty = true;
137
138    /**
139     * Creates a {@link CellRequest}.
140     *
141     * @param measure Measure the request is for
142     * @param extendedContext If a drill-through request, whether to join in
143     *   unconstrained levels so as to display extra columns
144     * @param drillThrough Whether this is a request for a drill-through set
145     */
146    public CellRequest(
147        RolapStar.Measure measure,
148        boolean extendedContext,
149        boolean drillThrough)
150    {
151        this.measure = measure;
152        this.extendedContext = extendedContext;
153        this.drillThrough = drillThrough;
154        this.constrainedColumnsBitKey =
155            BitKey.Factory.makeBitKey(measure.getStar().getColumnCount());
156        this.sparseColumnPredicateList =
157            new StarColumnPredicate[measure.getStar().getColumnCount()];
158    }
159
160    /**
161     * Adds a constraint to this request.
162     *
163     * @param column Column to constraint
164     * @param predicate Constraint to apply, or null to add column to the
165     *   output without applying constraint
166     */
167    public final void addConstrainedColumn(
168        RolapStar.Column column,
169        StarColumnPredicate predicate)
170    {
171        assert columnsCache == null;
172
173        // Sanity check; we should never be adding column constraints
174        // from more than one star
175        if (star == null) {
176            star = column.getStar();
177        } else {
178            assert (star == column.getStar());
179        }
180
181        final int bitPosition = column.getBitPosition();
182        if (this.constrainedColumnsBitKey.get(bitPosition)) {
183            // This column is already constrained. Unless the value is the
184            // same, or this value or the previous value is null (meaning
185            // unconstrained) the request will never return any results.
186            final StarColumnPredicate prevValue =
187                sparseColumnPredicateList[bitPosition];
188            if (prevValue == null) {
189                // Previous column was unconstrained. Constrain on new
190                // value.
191            } else if (predicate == null) {
192                // Previous column was constrained. Nothing to do.
193                return;
194            } else if (predicate.equalConstraint(prevValue)) {
195                // Same constraint again. Nothing to do.
196                return;
197            } else {
198                // Different constraint. Request is impossible to satisfy.
199                predicate = null;
200                unsatisfiable = true;
201            }
202        } else {
203            this.constrainedColumnsBitKey.set(bitPosition);
204            numColumns++;
205        }
206
207        // Note: it is possible and valid for predicate to be null here
208        this.sparseColumnPredicateList[bitPosition] = predicate;
209    }
210
211    /**
212     * Add compound member (formed via aggregate function) constraint to the
213     * Cell.
214     *
215     * @param compoundBitKey Compound bit key
216     * @param compoundPredicate Compound predicate
217     */
218    public void addAggregateList(
219        BitKey compoundBitKey,
220        StarPredicate compoundPredicate)
221    {
222        if (compoundPredicateMap == null) {
223            compoundPredicateMap = new TreeMap<BitKey, StarPredicate>();
224        }
225        compoundPredicateMap.put(compoundBitKey, compoundPredicate);
226    }
227
228    /**
229     * Returns the measure of this cell request.
230     *
231     * @return Measure
232     */
233    public RolapStar.Measure getMeasure() {
234        return measure;
235    }
236
237    public RolapStar.Column[] getConstrainedColumns() {
238        if (this.columnsCache == null) {
239            // This is called more than once so caching the value makes sense.
240            check();
241        }
242        return this.columnsCache;
243    }
244
245    /**
246     * Returns the BitKey for the list of columns.
247     *
248     * @return BitKey for the list of columns
249     */
250    public BitKey getConstrainedColumnsBitKey() {
251        return constrainedColumnsBitKey;
252    }
253
254    /**
255     * Returns the map of compound predicates, or null if empty.
256     *
257     * <p>NOTE: It is not generally considered good API design to return null
258     * to represent empty collections, but this collection is very often empty
259     * and the the implementation of Collections.emptyMap().keySet().iterator()
260     * is slow, so we optimize for the common case.
261     *
262     * @return predicate map, or null if empty
263     */
264    SortedMap<BitKey, StarPredicate> getCompoundPredicateMap() {
265        return compoundPredicateMap;
266    }
267
268    /**
269     * Builds the {@link #columnsCache} and {@link #columnBitPositions}
270     * based upon bit key position of the columns.
271     */
272    private void check() {
273        if (isDirty) {
274            columnsCache = new RolapStar.Column[numColumns];
275            columnBitPositions = new int[numColumns];
276            int i = 0;
277            for (int bitPos = constrainedColumnsBitKey.nextSetBit(0);
278                bitPos >= 0;
279                bitPos = constrainedColumnsBitKey.nextSetBit(bitPos + 1))
280            {
281                columnBitPositions[i] = bitPos;
282                columnsCache[i] = this.star.getColumn(bitPos);
283                i++;
284            }
285            isDirty = false;
286        }
287    }
288
289    /**
290     * Return the predicate value associated with the given index.  Note that
291     * index is different than bit position; if there are three constraints then
292     * the indices are 0, 1, and 2, while the bitPositions could span a larger
293     * range.
294     *
295     * <p> It is valid for the predicate at a given index to be null (there
296     * should always be a column at that index, but it may not have an
297     * associated predicate).
298     *
299     * @param index Index of the constraint we're looking up
300     * @return predicate value associated with the given index
301     */
302    public StarColumnPredicate getValueAt(int index) {
303        check();
304        return sparseColumnPredicateList[columnBitPositions[index]];
305    }
306
307    /**
308     * Return the number of column constraints associated with this CellRequest.
309     *
310     * @return number of columns in the CellRequest
311     */
312    public int getNumValues() {
313        check();
314        return numColumns;
315    }
316
317    /**
318     * Returns an array of the values for each column.
319     *
320     * <p>The caller must check whether this request is satisfiable before
321     * calling this method. May throw {@link NullPointerException} if request
322     * is not satisfiable.
323     *
324     * @pre !isUnsatisfiable()
325     * @return Array of values for each column
326     */
327    public Object[] getSingleValues() {
328        assert !unsatisfiable;
329        if (singleValues == null) {
330            check();
331            singleValues = new Object[numColumns];
332            int i = 0;
333            for (int bitPos : columnBitPositions) {
334                ValueColumnPredicate predicate =
335                    (ValueColumnPredicate) sparseColumnPredicateList[bitPos];
336                singleValues[i++] = predicate.getValue();
337            }
338        }
339        return singleValues;
340    }
341
342    /**
343     * Builds a map of column names to values, as specified
344     * by this cell request object.
345     */
346    public Map<String, Comparable> getMappedCellValues() {
347        final Map<String, Comparable> map =
348            new HashMap<String, Comparable>();
349        final RolapStar.Column[] columns =
350            this.getConstrainedColumns();
351        final Object[] values = this.getSingleValues();
352        for (int i = 0; i < columns.length; i++) {
353            RolapStar.Column column = columns[i];
354            final Object o = values[i];
355            map.put(
356                column.getExpression().getGenericExpression(),
357                (Comparable) o);
358        }
359        return map;
360    }
361
362    /**
363     * Returns whether this cell request is impossible to satisfy.
364     * This occurs when the same column has two or more inconsistent
365     * constraints.
366     *
367     * @return whether this cell request is impossible to satisfy
368     */
369    public boolean isUnsatisfiable() {
370        return unsatisfiable;
371    }
372}
373
374// End CellRequest.java