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