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) 2008-2011 Pentaho and others
008// All Rights Reserved.
009*/
010package mondrian.rolap;
011
012import mondrian.calc.*;
013import mondrian.olap.*;
014import mondrian.server.Execution;
015import mondrian.server.Statement;
016import mondrian.spi.Dialect;
017import mondrian.spi.DialectManager;
018
019import java.util.*;
020
021/**
022 * Context at the root of a tree of evaluators.
023 *
024 * <p>Contains the context that does not change as evaluation context is
025 * pushed/popped.
026 *
027 * @author jhyde
028 * @since Nov 11, 2008
029 */
030class RolapEvaluatorRoot {
031    final Map<Object, Object> expResultCache = new HashMap<Object, Object>();
032    final Map<Object, Object> tmpExpResultCache =
033        new HashMap<Object, Object>();
034    final RolapCube cube;
035    final RolapConnection connection;
036    final SchemaReader schemaReader;
037    final Map<CompiledExpKey, Calc> compiledExps =
038        new HashMap<CompiledExpKey, Calc>();
039    final Statement statement;
040    final Query query;
041    private final Date queryStartTime;
042    final Dialect currentDialect;
043
044    /**
045     * Default members of each hierarchy, from the schema reader's
046     * perspective. Finding the default member is moderately expensive, but
047     * happens very often.
048     */
049    final RolapMember[] defaultMembers;
050    final int[] nonAllPositions;
051    int nonAllPositionCount;
052
053    final SolveOrderMode solveOrderMode =
054        Util.lookup(
055            SolveOrderMode.class,
056            MondrianProperties.instance().SolveOrderMode.get().toUpperCase(),
057            SolveOrderMode.ABSOLUTE);
058
059    final Set<Exp> activeNativeExpansions = new HashSet<Exp>();
060
061    /**
062     * The size of the command stack at which we will next check for recursion.
063     */
064    int recursionCheckCommandCount;
065    public final Execution execution;
066
067    /**
068     * Creates a RolapEvaluatorRoot.
069     *
070     * @param statement statement
071     * @deprecated
072     */
073    public RolapEvaluatorRoot(Statement statement) {
074        this(statement, null);
075    }
076
077    public RolapEvaluatorRoot(Execution execution) {
078        this(execution.getMondrianStatement(), execution);
079    }
080
081    private RolapEvaluatorRoot(Statement statement, Execution execution) {
082        this.execution = execution;
083        this.statement = statement;
084        this.query = statement.getQuery();
085        this.cube = (RolapCube) query.getCube();
086        this.connection = statement.getMondrianConnection();
087        this.schemaReader = query.getSchemaReader(true);
088        this.queryStartTime = new Date();
089        List<RolapMember> list = new ArrayList<RolapMember>();
090        nonAllPositions = new int[cube.getHierarchies().size()];
091        nonAllPositionCount = 0;
092        for (RolapHierarchy hierarchy : cube.getHierarchies()) {
093            RolapMember defaultMember =
094                (RolapMember) schemaReader.getHierarchyDefaultMember(hierarchy);
095            assert defaultMember != null;
096
097            if (ScenarioImpl.isScenario(hierarchy)
098                && connection.getScenario() != null)
099            {
100                defaultMember =
101                    ((ScenarioImpl) connection.getScenario()).getMember();
102            }
103
104            // This fragment is a concurrency bottleneck, so use a cache of
105            // hierarchy usages.
106            final HierarchyUsage hierarchyUsage = cube.getFirstUsage(hierarchy);
107            if (hierarchyUsage != null) {
108                if (defaultMember instanceof RolapMemberBase) {
109                ((RolapMemberBase) defaultMember).makeUniqueName(
110                    hierarchyUsage);
111                }
112            }
113
114            list.add(defaultMember);
115            if (!defaultMember.isAll()) {
116                nonAllPositions[nonAllPositionCount] =
117                    hierarchy.getOrdinalInCube();
118                nonAllPositionCount++;
119            }
120        }
121        this.defaultMembers = list.toArray(new RolapMember[list.size()]);
122        this.currentDialect =
123            DialectManager.createDialect(schemaReader.getDataSource(), null);
124
125        this.recursionCheckCommandCount = (defaultMembers.length << 4);
126    }
127
128    /**
129     * Implements a cheap-and-cheerful mapping from expressions to compiled
130     * expressions.
131     *
132     * <p>TODO: Save compiled expressions somewhere better.
133     *
134     * @param exp Expression
135     * @param scalar Whether expression is scalar
136     * @param resultStyle Preferred result style; if null, use query's default
137     *     result style; ignored if expression is scalar
138     * @return compiled expression
139     */
140    final Calc getCompiled(
141        Exp exp,
142        boolean scalar,
143        ResultStyle resultStyle)
144    {
145        CompiledExpKey key = new CompiledExpKey(exp, scalar, resultStyle);
146        Calc calc = compiledExps.get(key);
147        if (calc == null) {
148            calc =
149                statement.getQuery().compileExpression(
150                    exp, scalar, resultStyle);
151            compiledExps.put(key, calc);
152        }
153        return calc;
154    }
155
156    /**
157     * Just a simple key of Exp/scalar/resultStyle, used for keeping
158     * compiled expressions.  Previous to the introduction of this
159     * class, the key was a list constructed as Arrays.asList(exp, scalar,
160     * resultStyle) and having poorer performance on equals, hashCode,
161     * and construction.
162     */
163    private static class CompiledExpKey {
164        private final Exp exp;
165        private final boolean scalar;
166        private final ResultStyle resultStyle;
167        private int hashCode = Integer.MIN_VALUE;
168
169        private CompiledExpKey(
170            Exp exp,
171            boolean scalar,
172            ResultStyle resultStyle)
173        {
174            this.exp = exp;
175            this.scalar = scalar;
176            this.resultStyle = resultStyle;
177        }
178
179        public boolean equals(Object other) {
180            if (this == other) {
181                return true;
182            }
183            if (!(other instanceof CompiledExpKey)) {
184                return false;
185            }
186            CompiledExpKey otherKey = (CompiledExpKey)other;
187            return this.scalar == otherKey.scalar
188                   && this.resultStyle == otherKey.resultStyle
189                   && this.exp.equals(otherKey.exp);
190        }
191
192        public int hashCode() {
193            if (hashCode != Integer.MIN_VALUE) {
194                return hashCode;
195            } else {
196                int hash = 0;
197                hash = Util.hash(hash, scalar);
198                hash = Util.hash(hash, resultStyle);
199                this.hashCode = Util.hash(hash, exp);
200            }
201            return this.hashCode;
202        }
203    }
204
205    /**
206     * Evaluates a named set.
207     *
208     * <p>The default implementation throws
209     * {@link UnsupportedOperationException}.
210     *
211     * @param namedSet Named set
212     * @param create Whether to create named set evaluator if not found
213     */
214    protected Evaluator.NamedSetEvaluator evaluateNamedSet(
215        NamedSet namedSet,
216        boolean create)
217    {
218        throw new UnsupportedOperationException();
219    }
220
221
222    /**
223     * Evaluates a named set represented by an expression.
224     *
225     * <p>The default implementation throws
226     * {@link UnsupportedOperationException}.
227     *
228     * @param exp Expression
229     * @param create Whether to create named set evaluator if not found
230     */
231    protected Evaluator.SetEvaluator evaluateSet(
232        Exp exp,
233        boolean create)
234    {
235        throw new UnsupportedOperationException();
236    }
237
238
239    /**
240     * Returns the value of a parameter, evaluating its default expression
241     * if necessary.
242     *
243     * <p>The default implementation throws
244     * {@link UnsupportedOperationException}.
245     */
246    public Object getParameterValue(ParameterSlot slot) {
247        throw new UnsupportedOperationException();
248    }
249
250    /**
251     * Puts result in cache.
252     *
253     * @param key key
254     * @param result value to be cached
255     * @param isValidResult indicate if this result is valid
256     */
257    public final void putCacheResult(
258        Object key,
259        Object result,
260        boolean isValidResult)
261    {
262        if (isValidResult) {
263            expResultCache.put(key, result);
264        } else {
265            tmpExpResultCache.put(key, result);
266        }
267    }
268
269    /**
270     * Gets result from cache.
271     *
272     * @param key cache key
273     * @return cached expression
274     */
275    public final Object getCacheResult(Object key) {
276        Object result = expResultCache.get(key);
277        if (result == null) {
278            result = tmpExpResultCache.get(key);
279        }
280        return result;
281    }
282
283    /**
284     * Clears the expression result cache.
285     *
286     * @param clearValidResult whether to clear valid expression results
287     */
288    public final void clearResultCache(boolean clearValidResult) {
289        if (clearValidResult) {
290            expResultCache.clear();
291        }
292        tmpExpResultCache.clear();
293    }
294
295    /**
296     * Get query start time.
297     *
298     * @return the query start time
299     */
300    public Date getQueryStartTime() {
301        return queryStartTime;
302    }
303}
304
305// End RolapEvaluatorRoot.java