KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > versant > core > jdbc > ejbql > JdbcEJBQLCompiler


1
2 /*
3  * Copyright (c) 1998 - 2005 Versant Corporation
4  * All rights reserved. This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License v1.0
6  * which accompanies this distribution, and is available at
7  * http://www.eclipse.org/legal/epl-v10.html
8  *
9  * Contributors:
10  * Versant Corporation - initial API and implementation
11  */

12 package com.versant.core.jdbc.ejbql;
13
14 import com.versant.core.jdbc.JdbcStorageManager;
15 import com.versant.core.jdbc.metadata.JdbcField;
16 import com.versant.core.jdbc.fetch.FetchSpec;
17 import com.versant.core.jdbc.fetch.SqlBuffer;
18 import com.versant.core.jdbc.sql.exp.SqlExp;
19 import com.versant.core.jdbc.sql.exp.SelectExp;
20 import com.versant.core.jdbc.sql.exp.SqlParamUsage;
21 import com.versant.core.jdbc.sql.exp.ColumnExp;
22 import com.versant.core.jdbc.query.JdbcCompiledQuery;
23 import com.versant.core.metadata.ModelMetaData;
24 import com.versant.core.metadata.ClassMetaData;
25 import com.versant.core.jdo.QueryDetails;
26 import com.versant.core.ejb.query.*;
27 import com.versant.core.common.BindingSupportImpl;
28 import com.versant.core.common.Debug;
29 import com.versant.core.common.CmdBitSet;
30
31 import java.io.StringReader JavaDoc;
32
33 /**
34  * This will compile an EJBQL query.
35  */

36 public class JdbcEJBQLCompiler {
37
38     private final JdbcStorageManager sm;
39     private final ModelMetaData jmd;
40
41     public JdbcEJBQLCompiler(JdbcStorageManager sm) {
42         this.sm = sm;
43         this.jmd = sm.getJmd();
44     }
45
46     /**
47      * Compile a QueryDetails into a JdbcCompiledQuery ready to run.
48      */

49     public JdbcCompiledQuery compile(QueryDetails q) {
50         Node tree = parse(q);
51         if (Debug.DEBUG) {
52             System.out.println("\n%%% parsed:\n" + tree + "\n");
53         }
54
55         ResolveContext rc = new ResolveContext(jmd);
56         tree.resolve(rc);
57         if (Debug.DEBUG) {
58             System.out.println("%%% resolved:\n" + tree + "\n");
59             rc.dump(System.out);
60             System.out.println();
61         }
62
63         EJBQLNodeToSqlExp converter = new EJBQLNodeToSqlExp(rc,
64                 sm.getSqlDriver());
65         SqlExp sqlExp = converter.toSqlExp(tree, null);
66         if (!(sqlExp instanceof SelectExp)) {
67             throw BindingSupportImpl.getInstance().internal("not supported");
68         }
69         SelectExp root = (SelectExp)sqlExp;
70         FetchSpec spec = root.fetchSpec;
71         compileParams(rc, spec);
72         if (Debug.DEBUG) {
73             System.out.println("%%% root:");
74             root.dump(" ");
75             System.out.println("\n%%% root.fetchSpec");
76             spec.printPlan(System.out, " ");
77         }
78
79         ClassMetaData candidateCMD = rc.getRoot(0).getNavClassMetaData();
80         JdbcCompiledQueryEJBQL cq = new JdbcCompiledQueryEJBQL(candidateCMD,
81                 q, spec);
82
83         CmdBitSet bits = new CmdBitSet(jmd);
84         for (int i = rc.getRootCount() - 1; i >= 0; i--) {
85             rc.getRoot(i).addInvolvedClasses(bits);
86         }
87         int[] a = q.getExtraEvictClasses();
88         if (a != null) {
89             for (int i = a.length - 1; i >= 0; i--) {
90                 bits.add(jmd.classes[a[i]]);
91             }
92         }
93         cq.setFilterClsIndexs(bits.toArray());
94         cq.setEvictionClassBits(bits.getBits());
95         cq.setEvictionClassIndexes(bits.getIndexes());
96
97         return cq;
98     }
99
100     private Node parse(QueryDetails q) {
101         String JavaDoc filter = q.getFilter();
102         if (filter == null) {
103             throw BindingSupportImpl.getInstance().invalidOperation(
104                     "EJBQL query string (filter) is null");
105         }
106         StringReader JavaDoc r = new StringReader JavaDoc(filter);
107         EJBQLParser p = new EJBQLParser(r);
108         try {
109             return p.ejbqlQuery();
110         } catch (ParseException e) {
111             throw BindingSupportImpl.getInstance().invalidOperation(
112                     e.getMessage());
113         }
114     }
115
116     /**
117      * This nasty code has to find the bits of SQL occupied by parameter
118      * expressions that could be null. They may need to be converted into
119      * 'is null' or 'is not null' or removed completely (for shared columns)
120      * if the corresponding parameter is null. This is not required by the
121      * JSR 220 spec but we already have it for JDOQL so might as well have
122      * it for EJBQL as well.
123      *
124      * The parameter code is way too complex and needs to be refactored. A
125      * better solution would be to create a new 'fake parameter' for each time
126      * a given input parameter is used in the query. That would simplify
127      * things a lot. The real parameters to the query can be easily expanded
128      * to match the list of 'fake parameters'. This would simplify things by
129      * getting rid of the first layer of usage.
130      */

131     private void compileParams(ResolveContext rc, FetchSpec spec) {
132         ResolveContext.ParamUsage[] params = rc.getParameters();
133         if (params == null) {
134             return;
135         }
136         SqlBuffer.Param list = null;
137         SqlBuffer.Param pos = null;
138         int np = params.length;
139         for (int i = 0; i < np; i++) {
140             for (ResolveContext.ParamUsage rcUsage = params[i];
141                     rcUsage != null; rcUsage = rcUsage.getNext()) {
142                 SqlParamUsage usage = (SqlParamUsage)rcUsage.storeObject;
143
144                 // create new param and add it to the list
145
SqlBuffer.Param param = new SqlBuffer.Param(
146                         rcUsage.getParamNode().getName());
147                 if (pos == null) {
148                     pos = list = param;
149                 } else {
150                     pos = pos.next = param;
151                 }
152
153                 // fill in the param
154
param.declaredParamIndex = rcUsage.getIndex();
155                 JdbcField jdbcField = usage.jdbcField;
156                 if (jdbcField == null) {
157                     param.classIndex = usage.classIndex;
158                     param.fieldNo = -1;
159                     param.javaTypeCode = usage.javaTypeCode;
160                     // todo param.javaTypeCode == 0: get type from value?
161
param.jdbcType = usage.jdbcType;
162                     param.col = usage.col;
163                 } else {
164                     param.classIndex = jdbcField.fmd.classMetaData.index;
165                     param.fieldNo = jdbcField.stateFieldNo;
166                     param.col = usage.col;
167                 }
168                 param.mod = usage.mod;
169
170                 // make a CharSpan for each ? in the SQL
171
if (usage.expCount > 0) {
172                     SqlBuffer.CharSpan cspos = null;
173                     SqlBuffer.CharSpan[] a
174                             = new SqlBuffer.CharSpan[usage.expCount];
175                     boolean multicol = usage.expCount > 1;
176                     int j = 0;
177                     int removeCount = 0;
178                     for (SqlExp e = usage.expList; j < a.length; j++) {
179                         SqlBuffer.CharSpan cs = a[j] =
180                                 new SqlBuffer.CharSpan();
181                         if (multicol && mustBeRemovedIfNull(e)) {
182                             if (++removeCount == a.length) {
183                                 // all expressions are to be removed so restart
184
// the loop making them all 'is null' instead
185
multicol = false;
186                                 e = usage.expList;
187                                 j = -1;
188                                 cspos = null;
189                                 continue;
190                             }
191                             cs.firstCharIndex = e.getPreFirstCharIndex();
192                             if (e.next == null) { // last span
193
cs.lastCharIndex = e.getLastCharIndex();
194                                 // work back and remove trailing 'and' if any
195
for (int k = j - 1; k >= 0; k--) {
196                                     if (a[k].type != SqlBuffer.CharSpan.TYPE_REMOVE) {
197                                         a[k + 1].firstCharIndex -= 4; // 'and '
198
break;
199                                     }
200                                 }
201                             } else { // first or middle span
202
cs.lastCharIndex = e.next.getPreFirstCharIndex();
203                             }
204                             cs.type = SqlBuffer.CharSpan.TYPE_REMOVE;
205                         } else {
206                             cs.firstCharIndex = e.getFirstCharIndex();
207                             cs.lastCharIndex = e.getLastCharIndex();
208                             cs.type = e.isNegative()
209                                     ? SqlBuffer.CharSpan.TYPE_NOT_NULL
210                                     : SqlBuffer.CharSpan.TYPE_NULL;
211                         }
212
213                         if (cspos == null) {
214                             cspos = param.charSpanList = cs;
215                             param.firstCharIndex = cs.firstCharIndex;
216                         } else {
217                             cspos = cspos.next = cs;
218                         }
219
220                         e = e.next;
221                     }
222                 } else {
223                     param.firstCharIndex = usage.expList.getFirstCharIndex();
224                 }
225             }
226         }
227         if (list != null) {
228             spec.setParamList(sortParams(list));
229         }
230     }
231
232     /**
233      * Columns that are not updated (i.e. are shared) must be removed
234      * completely if the matching parameter is null.
235      */

236     private static boolean mustBeRemovedIfNull(SqlExp e) {
237         if (!e.isNegative() && e.childList instanceof ColumnExp) {
238             ColumnExp ce = (ColumnExp)e.childList;
239             return !ce.col.isForUpdate();
240         }
241         return false;
242     }
243
244     /**
245      * Sort the params in the order that they appear in the query.
246      */

247     private static SqlBuffer.Param sortParams(SqlBuffer.Param list) {
248         if (list.next == null) return list;
249         // stone sort the list (bubble sort except elements sink to the bottom)
250
for (; ;) {
251             boolean changed = false;
252             SqlBuffer.Param p0 = null;
253             for (SqlBuffer.Param p1 = list; ;) {
254                 SqlBuffer.Param p2 = p1.next;
255                 if (p2 == null) break;
256                 if (p1.firstCharIndex > p2.firstCharIndex) {
257                     // exchange p and p2
258
p1.next = p2.next;
259                     p2.next = p1;
260                     if (p0 == null) {
261                         list = p2;
262                     } else {
263                         p0.next = p2;
264                     }
265                     p0 = p2;
266                     changed = true;
267                 } else {
268                     p0 = p1;
269                     p1 = p2;
270                 }
271             }
272             if (!changed) return list;
273         }
274     }
275
276     /*
277     private void dumpParams(JdbcCompiledQuery cq, SqlBuffer.Param p) {
278         for (; p != null; p = p.next) {
279             if (Debug.DEBUG) {
280                 Debug.OUT.println("Param " + p.declaredParamIndex +
281                         " firstCharIndex " + p.firstCharIndex);
282             }
283             for (SqlBuffer.CharSpan s = p.charSpanList; s != null; s = s.next) {
284                 if (Debug.DEBUG) {
285                     String ts;
286                     switch (s.type) {
287                         case SqlBuffer.CharSpan.TYPE_NULL:
288                             ts = "NULL";
289                             break;
290                         case SqlBuffer.CharSpan.TYPE_NOT_NULL:
291                             ts = "NOT_NULL";
292                             break;
293                         case SqlBuffer.CharSpan.TYPE_REMOVE:
294                             ts = "REMOVE";
295                             break;
296                         default:
297                             ts = "Unknown(" + s.type + ")";
298                     }
299                     Debug.OUT.println(" CharSpan " + s.firstCharIndex + " to " +
300                             s.lastCharIndex + " " + ts + " = '" +
301                             cq.getSqlbuf().toString(s.firstCharIndex,
302                                     s.lastCharIndex - s.firstCharIndex) + "'");
303                 }
304             }
305         }
306     }
307     */

308
309 }
310
311
Popular Tags