KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > mondrian > rolap > RolapNativeSql


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.ArrayList JavaDoc;
12 import java.util.List JavaDoc;
13
14 import mondrian.olap.*;
15 import mondrian.rolap.sql.SqlQuery;
16 import mondrian.mdx.MemberExpr;
17
18 /**
19  * Creates SQL from parse tree nodes. Currently it creates the SQL that
20  * accesses a measure for the ORDER BY that is generated for a TopCount.<p/>
21  *
22  * @author av
23  * @since Nov 17, 2005
24  * @version $Id: //open/mondrian/src/main/mondrian/rolap/RolapNativeSql.java#12 $
25  */

26 public class RolapNativeSql {
27
28     private SqlQuery sqlQuery;
29     private SqlQuery.Dialect dialect;
30
31     CompositeSqlCompiler numericCompiler;
32     CompositeSqlCompiler booleanCompiler;
33
34     RolapStoredMeasure storedMeasure;
35
36     /**
37      * We remember one of the measures so we can generate
38      * the constraints from RolapAggregationManager. Also
39      * make sure all measures live in the same star.
40      *
41      * @see RolapAggregationManager#makeRequest(Member[], boolean, boolean)
42      */

43     private boolean saveStoredMeasure(RolapStoredMeasure m) {
44         if (storedMeasure != null) {
45             RolapStar star1 = getStar(storedMeasure);
46             RolapStar star2 = getStar(m);
47             if (star1 != star2)
48                 return false;
49         }
50         this.storedMeasure = m;
51         return true;
52     }
53
54     private RolapStar getStar(RolapStoredMeasure m) {
55         return ((RolapStar.Measure )m.getStarMeasure()).getStar();
56     }
57
58     /**
59      * Translates an expression into SQL
60      *
61      * @author av
62      * @since Nov 23, 2005
63      */

64     interface SqlCompiler {
65         /**
66          * Returns SQL. If <code>exp</code> can not be compiled into SQL,
67          * returns null.
68          *
69          * @param exp Expression
70          * @return SQL, or null if cannot be converted into SQL
71          */

72         String JavaDoc compile(Exp exp);
73     }
74
75     /**
76      * uses chain of responsibility to find a matching sql compiler
77      * @author av
78      * @since Nov 23, 2005
79      */

80     class CompositeSqlCompiler implements SqlCompiler {
81         List JavaDoc<SqlCompiler> compilers = new ArrayList JavaDoc<SqlCompiler>();
82
83         public void add(SqlCompiler compiler) {
84             compilers.add(compiler);
85         }
86
87         public String JavaDoc compile(Exp exp) {
88             for (SqlCompiler compiler : compilers) {
89                 String JavaDoc s = compiler.compile(exp);
90                 if (s != null) {
91                     return s;
92                 }
93             }
94             return null;
95         }
96
97         public String JavaDoc toString() {
98             return compilers.toString();
99         }
100
101     }
102
103     /**
104      * compiles a numeric literal to SQL
105      * @author av
106      * @since Nov 23, 2005
107      */

108     class NumberSqlCompiler implements SqlCompiler {
109         public String JavaDoc compile(Exp exp) {
110             if (!(exp instanceof Literal)) {
111                 return null;
112             }
113             if ((exp.getCategory() & Category.Numeric) == 0) {
114                 return null;
115             }
116             Literal literal = (Literal) exp;
117             String JavaDoc expr = String.valueOf(literal.getValue());
118             if (dialect.isDB2()) {
119                 expr = "FLOAT(" + expr + ")";
120             }
121             return expr;
122         }
123
124         public String JavaDoc toString() {
125             return "NumberSqlCompiler";
126         }
127     }
128
129     /**
130      * base class to remove MemberScalarExp
131      * @author av
132      * @since Nov 23, 2005
133      */

134     abstract class MemberSqlCompiler implements SqlCompiler {
135         protected Exp unwind(Exp exp) {
136             return exp;
137         }
138     }
139
140     /**
141      * compiles a measure into SQL, the measure will be aggregated like <code>sum(measure)</code>
142      *
143      * @author av
144      * @since Nov 23, 2005
145      */

146     class StoredMeasureSqlCompiler extends MemberSqlCompiler {
147
148         public String JavaDoc compile(Exp exp) {
149             exp = unwind(exp);
150             if (!(exp instanceof MemberExpr)) {
151                 return null;
152             }
153             final Member member = ((MemberExpr) exp).getMember();
154             if (!(member instanceof RolapStoredMeasure)) {
155                 return null;
156             }
157             RolapStoredMeasure measure = (RolapStoredMeasure) member;
158             if (measure.isCalculated()) {
159                 return null; // ??
160
}
161             if (!saveStoredMeasure(measure))
162                 return null;
163             String JavaDoc exprInner = measure.getMondrianDefExpression().getExpression(sqlQuery);
164             String JavaDoc expr = measure.getAggregator().getExpression(exprInner);
165             if (dialect.isDB2()) {
166                 expr = "FLOAT(" + expr + ")";
167             }
168             return expr;
169         }
170
171         public String JavaDoc toString() {
172             return "StoredMeasureSqlCompiler";
173         }
174     }
175
176     /**
177      * compiles the underlying expression of a calculated member
178      *
179      * @author av
180      * @since Nov 23, 2005
181      */

182     class CalculatedMemberSqlCompiler extends MemberSqlCompiler {
183         SqlCompiler compiler;
184
185         CalculatedMemberSqlCompiler(SqlCompiler argumentCompiler) {
186             this.compiler = argumentCompiler;
187         }
188
189         public String JavaDoc compile(Exp exp) {
190             exp = unwind(exp);
191             if (!(exp instanceof MemberExpr)) {
192                 return null;
193             }
194             final Member member = ((MemberExpr) exp).getMember();
195             if (!(member instanceof RolapCalculatedMember)) {
196                 return null;
197             }
198             exp = member.getExpression();
199             if (exp == null) {
200                 return null;
201             }
202             return compiler.compile(exp);
203         }
204
205         public String JavaDoc toString() {
206             return "CalculatedMemberSqlCompiler";
207         }
208     }
209
210     /**
211      * contains utility methods to compile FunCall expressions into SQL.
212      *
213      * @author av
214      * @since Nov 23, 2005
215      */

216     abstract class FunCallSqlCompilerBase implements SqlCompiler {
217         int category;
218         String JavaDoc mdx;
219         int argCount;
220
221         FunCallSqlCompilerBase(int category, String JavaDoc mdx, int argCount) {
222             this.category = category;
223             this.mdx = mdx;
224             this.argCount = argCount;
225         }
226
227         /**
228          * @return true if exp is a matching FunCall
229          */

230         protected boolean match(Exp exp) {
231             if ((exp.getCategory() & category) == 0) {
232                 return false;
233             }
234             if (!(exp instanceof FunCall)) {
235                 return false;
236             }
237             FunCall fc = (FunCall) exp;
238             if (!mdx.equalsIgnoreCase(fc.getFunName())) {
239                 return false;
240             }
241             Exp[] args = fc.getArgs();
242             if (args.length != argCount) {
243                 return false;
244             }
245             return true;
246         }
247
248         /**
249          * compiles the arguments of a FunCall
250          *
251          * @return array of expressions or null if either exp does not match or
252          * any argument could not be compiled.
253          */

254         protected String JavaDoc[] compileArgs(Exp exp, SqlCompiler compiler) {
255             if (!match(exp)) {
256                 return null;
257             }
258             Exp[] args = ((FunCall) exp).getArgs();
259             String JavaDoc[] sqls = new String JavaDoc[args.length];
260             for (int i = 0; i < args.length; i++) {
261                 sqls[i] = compiler.compile(args[i]);
262                 if (sqls[i] == null) {
263                     return null;
264                 }
265             }
266             return sqls;
267         }
268     }
269
270     /**
271      * compiles a funcall, e.g. foo(a, b, c)
272      * @author av
273      * @since Nov 23, 2005
274      */

275     class FunCallSqlCompiler extends FunCallSqlCompilerBase {
276         SqlCompiler compiler;
277         String JavaDoc sql;
278
279         protected FunCallSqlCompiler(int category, String JavaDoc mdx, String JavaDoc sql,
280                 int argCount, SqlCompiler argumentCompiler) {
281             super(category, mdx, argCount);
282             this.sql = sql;
283             this.compiler = argumentCompiler;
284         }
285
286         public String JavaDoc compile(Exp exp) {
287             String JavaDoc[] args = compileArgs(exp, compiler);
288             if (args == null) {
289                 return null;
290             }
291             StringBuilder JavaDoc buf = new StringBuilder JavaDoc();
292             buf.append(sql);
293             buf.append("(");
294             for (int i = 0; i < args.length; i++) {
295                 if (i > 0) {
296                     buf.append(", ");
297                 }
298                 buf.append(args[i]);
299             }
300             buf.append(") ");
301             return buf.toString();
302         }
303
304         public String JavaDoc toString() {
305             return "FunCallSqlCompiler[" + mdx + "]";
306         }
307     }
308
309     /**
310      * shortcut for an unary operator like NOT(a)
311      * @author av
312      * @since Nov 23, 2005
313      */

314     class UnaryOpSqlCompiler extends FunCallSqlCompiler {
315         protected UnaryOpSqlCompiler(int category, String JavaDoc mdx, String JavaDoc sql, SqlCompiler argumentCompiler) {
316             super(category, mdx, sql, 1, argumentCompiler);
317         }
318     }
319
320     /**
321      * shortcut for ()
322      * @author av
323      * @since Nov 23, 2005
324      */

325     class ParenthesisSqlCompiler extends FunCallSqlCompiler {
326         protected ParenthesisSqlCompiler(int category, SqlCompiler argumentCompiler) {
327             super(category, "()", "", 1, argumentCompiler);
328         }
329
330         public String JavaDoc toString() {
331             return "ParenthesisSqlCompiler";
332         }
333     }
334
335     /**
336      * compiles an infix operator like addition into SQL like <code>(a + b)</code>
337      *
338      * @author av
339      * @since Nov 23, 2005
340      */

341     class InfixOpSqlCompiler extends FunCallSqlCompilerBase {
342         private final String JavaDoc sql;
343         private final SqlCompiler compiler;
344
345         protected InfixOpSqlCompiler(int category, String JavaDoc mdx, String JavaDoc sql,
346                 SqlCompiler argumentCompiler) {
347             super(category, mdx, 2);
348             this.sql = sql;
349             this.compiler = argumentCompiler;
350         }
351
352         public String JavaDoc compile(Exp exp) {
353             String JavaDoc[] args = compileArgs(exp, compiler);
354             if (args == null) {
355                 return null;
356             }
357             return "(" + args[0] + " " + sql + " " + args[1] + ")";
358         }
359
360         public String JavaDoc toString() {
361             return "InfixSqlCompiler[" + mdx + "]";
362         }
363     }
364
365     /**
366      * compiles an <code>IIF(cond, val1, val2)</code>
367      * expression into SQL <code>CASE WHEN cond THEN val1 ELSE val2 END</code>
368      *
369      * @author av
370      * @since Nov 23, 2005
371      */

372     class IifSqlCompiler extends FunCallSqlCompilerBase {
373
374         SqlCompiler valueCompiler;
375
376         IifSqlCompiler(int category, SqlCompiler valueCompiler) {
377             super(category, "iif", 3);
378             this.valueCompiler = valueCompiler;
379         }
380
381         public String JavaDoc compile(Exp exp) {
382             if (!match(exp)) {
383                 return null;
384             }
385             Exp[] args = ((FunCall) exp).getArgs();
386             String JavaDoc cond = booleanCompiler.compile(args[0]);
387             String JavaDoc val1 = valueCompiler.compile(args[1]);
388             String JavaDoc val2 = valueCompiler.compile(args[2]);
389             if (cond == null || val1 == null || val2 == null) {
390                 return null;
391             }
392             return sqlQuery.getDialect().caseWhenElse(cond, val1, val2);
393         }
394
395     }
396
397     /**
398      * creates a new instance
399      * @param sqlQuery the query which is needed for differen SQL dialects - its not modified.
400      */

401     RolapNativeSql(SqlQuery sqlQuery) {
402         this.sqlQuery = sqlQuery;
403         this.dialect = sqlQuery.getDialect();
404
405         numericCompiler = new CompositeSqlCompiler();
406         booleanCompiler = new CompositeSqlCompiler();
407
408         numericCompiler.add(new NumberSqlCompiler());
409         numericCompiler.add(new StoredMeasureSqlCompiler());
410         numericCompiler.add(new CalculatedMemberSqlCompiler(numericCompiler));
411         numericCompiler.add(new ParenthesisSqlCompiler(Category.Numeric, numericCompiler));
412         numericCompiler.add(new InfixOpSqlCompiler(Category.Numeric, "+", "+", numericCompiler));
413         numericCompiler.add(new InfixOpSqlCompiler(Category.Numeric, "-", "-", numericCompiler));
414         numericCompiler.add(new InfixOpSqlCompiler(Category.Numeric, "/", "/", numericCompiler));
415         numericCompiler.add(new InfixOpSqlCompiler(Category.Numeric, "*", "*", numericCompiler));
416         numericCompiler.add(new IifSqlCompiler(Category.Numeric, numericCompiler));
417
418         booleanCompiler.add(new InfixOpSqlCompiler(Category.Logical, "<", "<", numericCompiler));
419         booleanCompiler.add(new InfixOpSqlCompiler(Category.Logical, "<=", "<=", numericCompiler));
420         booleanCompiler.add(new InfixOpSqlCompiler(Category.Logical, ">", ">", numericCompiler));
421         booleanCompiler.add(new InfixOpSqlCompiler(Category.Logical, ">=", ">=", numericCompiler));
422         booleanCompiler.add(new InfixOpSqlCompiler(Category.Logical, "=", "=", numericCompiler));
423         booleanCompiler.add(new InfixOpSqlCompiler(Category.Logical, "<>", "<>", numericCompiler));
424
425         booleanCompiler.add(new InfixOpSqlCompiler(Category.Logical, "and", "AND", booleanCompiler));
426         booleanCompiler.add(new InfixOpSqlCompiler(Category.Logical, "or", "OR", booleanCompiler));
427         booleanCompiler.add(new UnaryOpSqlCompiler(Category.Logical, "not", "NOT", booleanCompiler));
428         booleanCompiler.add(new ParenthesisSqlCompiler(Category.Logical, booleanCompiler));
429         booleanCompiler.add(new IifSqlCompiler(Category.Logical, booleanCompiler));
430     }
431
432     /**
433      * generates an aggregate of a measure, e.g. "sum(Store_Sales)" for TopCount. The
434      * returned expr will be added to the select list and to the order by clause.
435      */

436     public String JavaDoc generateTopCountOrderBy(Exp exp) {
437         return numericCompiler.compile(exp);
438     }
439
440     public String JavaDoc generateFilterCondition(Exp exp) {
441         return booleanCompiler.compile(exp);
442     }
443
444     public RolapStoredMeasure getStoredMeasure() {
445         return storedMeasure;
446     }
447
448 }
449
450 // End RolapNativeSql.java
451
Popular Tags