KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > mondrian > rolap > SqlContextConstraint


1 /*
2 // This software is subject to the terms of the Common Public License
3 // Agreement, available at the following URL:
4 // http://www.opensource.org/licenses/cpl.html.
5 // Copyright (C) 2004-2005 TONBELLER AG
6 // All Rights Reserved.
7 // You must accept the terms of that agreement to use this software.
8 */

9 package mondrian.rolap;
10
11 import java.util.*;
12
13 import mondrian.mdx.MemberExpr;
14 import mondrian.mdx.ResolvedFunCall;
15 import mondrian.olap.*;
16 import mondrian.rolap.sql.MemberChildrenConstraint;
17 import mondrian.rolap.sql.SqlQuery;
18 import mondrian.rolap.sql.TupleConstraint;
19 import mondrian.rolap.aggmatcher.AggStar;
20
21 /**
22  * limits the result of a Member SQL query to the current evaluation context.
23  * All Members of the current context are joined against the fact table and only
24  * those rows are returned, that have an entry in the fact table.
25  * <p>
26  * For example, if you have two dimensions, "invoice" and "time", and the current
27  * context (e.g. the slicer) contains a day from the "time" dimension, then
28  * only the invoices of that day are found. Used to optimize NON EMPTY.
29  *
30  * <p> The {@link TupleConstraint} methods may silently ignore calculated
31  * members (depends on the <code>strict</code> c'tor argument), so these may
32  * return more members than the current context restricts to. The
33  * MemberChildren methods will never accept calculated members as parents,
34  * these will cause an exception.
35  *
36  * @author av
37  * @since Nov 2, 2005
38  */

39 public class SqlContextConstraint implements MemberChildrenConstraint,
40         TupleConstraint {
41     List<Object JavaDoc> cacheKey;
42     private Evaluator evaluator;
43     private boolean strict;
44
45     /**
46      * @return false if this contstraint will not work for the current context
47      */

48     public static boolean isValidContext(Evaluator context) {
49         return isValidContext(context, true, null);
50     }
51
52     /**
53      * @param context evaluation context
54      * @param disallowVirtualCube if true, check for virtual cubes
55      * @param levels levels being referenced in the current context
56      *
57      * @return false if constraint will not work for current context
58      */

59     public static boolean isValidContext(
60         Evaluator context,
61         boolean disallowVirtualCube,
62         Level [] levels)
63     {
64         if (context == null) {
65             return false;
66         }
67         RolapCube cube = (RolapCube) context.getCube();
68         if (disallowVirtualCube) {
69             if (cube.isVirtual()) {
70                 return false;
71             }
72         }
73         if (cube.isVirtual()) {
74             Query query = context.getQuery();
75             Set<Map<RolapLevel, RolapStar.Column>> baseCubesLevelToColumnMaps =
76                 new HashSet<Map<RolapLevel, RolapStar.Column>>();
77             Map<Map<RolapLevel, RolapStar.Column>, RolapMember> measureMap =
78                 new HashMap<Map<RolapLevel, RolapStar.Column>, RolapMember>();
79             if (!findVirtualCubeJoinLevels(
80                 query,
81                 baseCubesLevelToColumnMaps,
82                 measureMap))
83             {
84                 return false;
85             }
86             assert(levels != null);
87             // we need to make sure all the levels join with each fact table;
88
// otherwise, it doesn't make sense to do the processing
89
// natively, as you'll end up with cartesian product joins!
90
for (Map<RolapLevel, RolapStar.Column> map : baseCubesLevelToColumnMaps) {
91                 for (Level level : levels) {
92                     if (map.get((RolapLevel) level) == null) {
93                         return false;
94                     }
95                 }
96             }
97             query.setVirtualCubeBaseCubeMaps(baseCubesLevelToColumnMaps);
98             query.setLevelMapToMeasureMap(measureMap);
99         }
100         return true;
101     }
102
103     /**
104      * Locates joins required with the underlying fact tables that make up a
105      * virtual cube by validating the measures referenced in the query.
106      *
107      * @param query query referencing the virtual cube
108      * @param baseCubesLevelToColumnMaps level to column maps corresponding
109      * to the base cubes referenced from the virtual cube
110      * @param measureMap mapping between a measure and the level to column
111      * maps
112      *
113      * @return true if valid measures exist
114      */

115     private static boolean findVirtualCubeJoinLevels(
116         Query query,
117         Set<Map<RolapLevel, RolapStar.Column>> baseCubesLevelToColumnMaps,
118         Map<Map<RolapLevel, RolapStar.Column>, RolapMember> measureMap)
119     {
120         // Gather the unique set of level-to-column maps corresponding
121
// to the underlying star/cube where the measure column
122
// originates from.
123
Set<Member> measureMembers = query.getMeasuresMembers();
124         // if no measures are explicitly referenced, just use the default
125
// measure
126
if (measureMembers.isEmpty()) {
127             Cube cube = query.getCube();
128             Dimension dimension = cube.getDimensions()[0];
129             query.addMeasuresMembers(
130                 dimension.getHierarchy().getDefaultMember());
131         }
132         for (Member member : query.getMeasuresMembers()) {
133             if (member instanceof RolapStoredMeasure) {
134                 addMeasure(
135                     (RolapStoredMeasure) member,
136                     baseCubesLevelToColumnMaps,
137                     measureMap);
138             } else if (member instanceof RolapCalculatedMember) {
139                 findMeasures(
140                     member.getExpression(),
141                     baseCubesLevelToColumnMaps,
142                     measureMap);
143             }
144         }
145         if (measureMap.isEmpty()) {
146             return false;
147         }
148
149         return true;
150     }
151
152     /**
153      * Adds information regarding a stored measure to maps
154      *
155      * @param measure the stored measure
156      * @param baseCubesLevelToColumnMaps level to column maps for the
157      * underlying cubes that make up the virtual cube referenced in a query
158      * @param measureMap maps a level-to-column map to a measure
159      */

160     private static void addMeasure(
161         RolapStoredMeasure measure,
162         Set<Map<RolapLevel, RolapStar.Column>> baseCubesLevelToColumnMaps,
163         Map<Map<RolapLevel, RolapStar.Column>, RolapMember> measureMap)
164     {
165         RolapStar.Measure starMeasure =
166             (RolapStar.Measure) measure.getStarMeasure();
167         RolapStar star = starMeasure.getStar();
168         RolapCube baseCube = measure.getCube();
169         Map<RolapLevel, RolapStar.Column> levelToColumnMap =
170             star.getLevelToColumnMap(baseCube);
171         if (baseCubesLevelToColumnMaps.add(levelToColumnMap)) {
172             measureMap.put(levelToColumnMap, (RolapMember) measure);
173         }
174     }
175
176     /**
177      * Extracts the stored measures referenced in an expression
178      *
179      * @param exp expression
180      * @param baseCubesLevelToColumnMaps
181      * @param baseCubesLevelToColumnMaps level to column maps for the
182      * underlying cubes that make up the virtual cube referenced in a query
183      * @param measureMap maps a level-to-column map to a measure
184      */

185     private static void findMeasures(
186         Exp exp,
187         Set<Map<RolapLevel, RolapStar.Column>> baseCubesLevelToColumnMaps,
188         Map<Map<RolapLevel, RolapStar.Column>, RolapMember> measureMap)
189     {
190         if (exp instanceof MemberExpr) {
191             MemberExpr memberExpr = (MemberExpr) exp;
192             Member member = memberExpr.getMember();
193             if (member instanceof RolapStoredMeasure) {
194                 addMeasure(
195                     (RolapStoredMeasure) member,
196                     baseCubesLevelToColumnMaps,
197                     measureMap);
198             } else if (member instanceof RolapCalculatedMember) {
199                 findMeasures(
200                     member.getExpression(),
201                     baseCubesLevelToColumnMaps,
202                     measureMap);
203             }
204         } else if (exp instanceof ResolvedFunCall) {
205             ResolvedFunCall funCall = (ResolvedFunCall) exp;
206             Exp [] args = funCall.getArgs();
207             for (Exp arg : args) {
208                 findMeasures(
209                     arg,
210                     baseCubesLevelToColumnMaps,
211                     measureMap);
212             }
213         }
214     }
215
216    /**
217     * @param strict defines the behaviour if the evaluator context
218     * contains calculated members. If true, an exception is thrown,
219     * otherwise calculated members are silently ignored. The
220     * methods {@link mondrian.rolap.sql.MemberChildrenConstraint#addMemberConstraint(mondrian.rolap.sql.SqlQuery,mondrian.rolap.aggmatcher.AggStar,java.util.List)} and
221     * {@link mondrian.rolap.sql.MemberChildrenConstraint#addMemberConstraint(mondrian.rolap.sql.SqlQuery,mondrian.rolap.aggmatcher.AggStar,RolapMember)} will
222     * never accept a calculated member as parent.
223     */

224     SqlContextConstraint(RolapEvaluator evaluator, boolean strict) {
225         this.evaluator = evaluator;
226         this.strict = strict;
227         cacheKey = new ArrayList<Object JavaDoc>();
228         cacheKey.add(getClass());
229         cacheKey.add(strict);
230         cacheKey.addAll(Arrays.asList(evaluator.getMembers()));
231     }
232
233     /**
234      * Called from MemberChildren: adds <code>parent</code> to the current
235      * context and restricts the SQL resultset to that new context.
236      */

237     public void addMemberConstraint(
238         SqlQuery sqlQuery,
239         Map<RolapLevel, RolapStar.Column> levelToColumnMap,
240         AggStar aggStar,
241         RolapMember parent)
242     {
243         if (parent.isCalculated()) {
244             throw Util.newInternal("cannot restrict SQL to calculated member");
245         }
246         Evaluator e = evaluator.push(parent);
247         SqlConstraintUtils.addContextConstraint(sqlQuery, aggStar, e, strict);
248         SqlConstraintUtils.addMemberConstraint(
249             sqlQuery, levelToColumnMap, aggStar, parent, true);
250     }
251
252     public void addMemberConstraint(
253         SqlQuery sqlQuery,
254         Map<RolapLevel, RolapStar.Column> levelToColumnMap,
255         AggStar aggStar,
256         List<RolapMember> parents)
257     {
258         SqlConstraintUtils.addContextConstraint(
259             sqlQuery, aggStar, evaluator, strict);
260         SqlConstraintUtils.addMemberConstraint(
261             sqlQuery, levelToColumnMap, aggStar, parents, true, false);
262     }
263
264     /**
265      * Called from LevelMembers: restricts the SQL resultset to the current
266      * context.
267      */

268     public void addConstraint(
269         SqlQuery sqlQuery, Map<RolapLevel, RolapStar.Column> levelToColumnMap) {
270         SqlConstraintUtils.addContextConstraint(
271             sqlQuery, null, evaluator, strict);
272     }
273
274     /**
275      * Returns whether a join with the fact table is required. A join is
276      * required if the context contains members from dimensions other than
277      * level. If we are interested in the members of a level or a members
278      * children then it does not make sense to join only one dimension (the one
279      * that contains the requested members) with the fact table for NON EMPTY
280      * optimization.
281      */

282     protected boolean isJoinRequired() {
283         Member[] members = evaluator.getMembers();
284         // members[0] is the Measure, so loop starts at 1
285
for (int i = 1; i < members.length; i++) {
286             if (!members[i].isAll()) {
287                 return true;
288             }
289         }
290         return false;
291     }
292
293     public void addLevelConstraint(
294         SqlQuery sqlQuery,
295         AggStar aggStar,
296         RolapLevel level,
297         Map<RolapLevel, RolapStar.Column> levelToColumnMap) {
298         if (!isJoinRequired()) {
299             return;
300         }
301         SqlConstraintUtils.joinLevelTableToFactTable(
302             sqlQuery, aggStar, evaluator, level, levelToColumnMap);
303     }
304
305     public MemberChildrenConstraint getMemberChildrenConstraint(RolapMember parent) {
306         return this;
307     }
308
309     public Object JavaDoc getCacheKey() {
310         return cacheKey;
311     }
312
313     public Evaluator getEvaluator() {
314         return evaluator;
315     }
316 }
317
318 // End SqlContextConstraint.java
319
Popular Tags