KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > versant > core > jdbc > query > JdbcJDOQLCompiler


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.query;
13
14 import com.versant.core.common.Debug;
15 import com.versant.core.jdo.QueryDetails;
16 import com.versant.core.common.CmdBitSet;
17 import com.versant.core.metadata.*;
18 import com.versant.core.jdo.query.*;
19 import com.versant.core.jdbc.FgDs;
20 import com.versant.core.jdbc.JdbcStorageManager;
21 import com.versant.core.jdbc.ProjectionQueryDecoder;
22 import com.versant.core.jdbc.metadata.JdbcClass;
23 import com.versant.core.jdbc.metadata.JdbcColumn;
24 import com.versant.core.jdbc.metadata.JdbcField;
25 import com.versant.core.jdbc.metadata.JdbcFetchGroup;
26 import com.versant.core.jdbc.sql.SqlDriver;
27 import com.versant.core.jdbc.sql.exp.*;
28
29 import com.versant.core.common.BindingSupportImpl;
30
31 /**
32  * This will compile a JDOQL query into a JdbcCompiledQuery.
33  *
34  * @see JdbcCompiledQuery
35  * @see #reinit
36  */

37 public final class JdbcJDOQLCompiler {
38
39     private final JdbcStorageManager sm;
40     private final ModelMetaData jmd;
41     private final JDOQLNodeToSqlExp visitor;
42
43     // these fields must be set to null in reinit
44
private ClassMetaData cmd;
45     private ParamNode[] params;
46     private OrderNode[] orders;
47     private UnaryNode filter;
48     private ResultNode resultNode;
49     private GroupingNode groupingNode;
50     private QueryParser qParser;
51     private SelectExp candidateSelectExp;
52
53     public JdbcJDOQLCompiler(JdbcStorageManager sm) {
54         this.sm = sm;
55         this.jmd = sm.getJmd();
56         visitor = new JDOQLNodeToSqlExp(this);
57     }
58
59     /**
60      * Get this compiler ready to compile more queries. This is called before
61      * it is returned to the pool.
62      */

63     public void reinit() {
64         cmd = null;
65         params = null;
66         orders = null;
67         filter = null;
68         qParser = null;
69         resultNode = null;
70         groupingNode = null;
71         candidateSelectExp = null;
72     }
73
74     public QueryParser getQParser() {
75         return qParser;
76     }
77
78     public JDOQLNodeToSqlExp getVisitor() {
79         return visitor;
80     }
81
82     /**
83      * Compile a QueryDetails into a JdbcCompiledQuery ready to run.
84      */

85     public JdbcCompiledQuery compile(QueryDetails q) {
86
87         cmd = jmd.getClassMetaData(q.getCandidateClass());
88         if (cmd == null) {
89             throw BindingSupportImpl.getInstance().invalidOperation(
90                     "Class " +
91                     q.getCandidateClass().getName() + " not found in meta data");
92         }
93
94         return compileImp(q);
95     }
96
97     /**
98      * Create a filter exp for a collection query.
99      */

100     public SqlExp compileParallelFetch(QueryDetails q) {
101         cmd = jmd.getClassMetaData(q.getCandidateClass());
102         if (cmd == null) {
103             throw BindingSupportImpl.getInstance().invalidOperation(
104                     "Class " +
105                     q.getCandidateClass().getName() + " not found in meta data");
106         }
107         return compileParallelFetchImp(q);
108     }
109
110     private JdbcCompiledQuery compileImp(QueryDetails q) {
111         JdbcCompiledQuery cq = new JdbcCompiledQuery(cmd, q);
112
113         qParser = new QueryParser(jmd);
114
115         try {
116             qParser.parse(q);
117
118             resolveVarNodes(qParser);
119             params = qParser.getParams();
120             orders = qParser.getOrders();
121             filter = qParser.getFilter();
122             resultNode = qParser.getResultNode();
123             groupingNode = qParser.getGroupingNode();
124         } catch (Exception JavaDoc e) {
125             if (BindingSupportImpl.getInstance().isOwnException(e)) {
126                 throw (RuntimeException JavaDoc)e;
127             } else {
128                 throw BindingSupportImpl.getInstance().invalidOperation(e.getMessage(), e);
129             }
130         } catch (TokenMgrError e) {
131             throw BindingSupportImpl.getInstance().invalidOperation(e.getMessage(), e);
132         }
133
134         // build the SQL query tree
135
candidateSelectExp = new SelectExp();
136         JdbcClass jdbcClass = (JdbcClass)cmd.storeClass;
137         candidateSelectExp.table = jdbcClass.table;
138
139         FetchGroup fg = cmd.fetchGroups[q.getFetchGroupIndex()];
140
141         //do inner joins to basetable
142
for (FetchGroup supg = fg.superFetchGroup; supg != null;
143              supg = supg.superFetchGroup) {
144             JdbcClass sc = (JdbcClass)supg.classMetaData.storeClass;
145
146             if (sc.table != candidateSelectExp.table) {
147                 // different table so do an inner join
148
SelectExp se = candidateSelectExp.findTable(sc.table);
149                 if (se == null) {
150                     se = new SelectExp();
151                     se.table = sc.table;
152                     candidateSelectExp.addJoin(candidateSelectExp.table.pk,
153                             se.table.pk, se);
154                 }
155             }
156         }
157
158         if (filter != null) {
159             candidateSelectExp.whereExp = visitor.toSqlExp(filter,
160                     candidateSelectExp, null, 0, null);
161 // candidateSelectExp.whereExp = filter.toSqlExp(this,
162
// candidateSelectExp, null, 0, null);
163
}
164
165         // include only correct subclass(es) if cmd is in a heirachy
166
if (cmd.isInHeirachy()) addSubclassFilter(candidateSelectExp, cq);
167
168         if (Debug.DEBUG) {
169             Debug.OUT.println("\n* SQL tree:");
170             candidateSelectExp.dump("");
171         }
172
173         SqlDriver driver = sm.getSqlDriver();
174
175
176         FgDs fgDs = cq.fgDs = ((JdbcFetchGroup)fg.storeFetchGroup).getFgDs(cq.isIncludeSubclasses(), false);
177         SqlExp orderByFromCrossJoin = null;
178         if (resultNode == null) {
179             candidateSelectExp.normalize(driver, null, driver.isConvertExistsToDistinctJoin());
180             if (Debug.DEBUG) {
181                 Debug.OUT.println("\n* Normalized SQL tree:");
182                 candidateSelectExp.dump("");
183             }
184
185             cq.process();
186             // add the fetch group to the query and make sure the pk is also there
187
sm.addSelectFetchGroup(candidateSelectExp,
188                     fg, cq.isIncludeSubclasses(), fgDs, cq.isCrossJoinAllowed());
189
190             addPrimaryKey(candidateSelectExp);
191
192             cq.setSelectColumnCount(
193                     candidateSelectExp.getSelectListColumnCount());
194             orderByFromCrossJoin = candidateSelectExp.orderByList;
195             candidateSelectExp.orderByList = null;
196         } else {
197             processNode(resultNode, "Result");
198             processNode(groupingNode, "Grouping");
199             if (groupingNode != null && groupingNode.havingNode != null) {
200                 processNode(groupingNode.havingNode, "Having");
201             }
202             ProjectionQueryDecoder decoder = new ProjectionQueryDecoder(resultNode, driver);
203
204             cq.setProjectionDecoder(decoder);
205             cq.setGroupingNode(groupingNode);
206             cq.process();
207
208             /**
209              * remove distinct if all the existExp that was removed was for the
210              * variable that is in the result string.
211              *
212              * Check to see if the candidateSelectExp constains joins that require
213              * a distinct
214              */

215
216             boolean convertExistBecauseOfVarInProjection = resultNode.processForVarNodes();
217             candidateSelectExp.normalize(driver, null, convertExistBecauseOfVarInProjection
218                      || driver.isConvertExistsToDistinctJoin());
219
220             if (resultNode.isDistinct()) {
221                 candidateSelectExp.distinct = true;
222             }
223
224             if (Debug.DEBUG) {
225                 Debug.OUT.println("\n* Normalized SQL tree:");
226                 candidateSelectExp.dump("");
227             }
228
229             if (decoder.containsThis()) {
230                 SqlExp tail = sm.addSelectFetchGroup(candidateSelectExp,
231                         fg, cq.isIncludeSubclasses(), fgDs, false);
232                 addPrimaryKey(candidateSelectExp);
233                 cq.setSelectColumnCount(candidateSelectExp.selectListCountBeforeAggregate
234                         = candidateSelectExp.getSelectListColumnCount());
235                 tail.next = visitor.toSqlExp(resultNode, candidateSelectExp, null,
236                         0, null);
237             } else {
238                 candidateSelectExp.selectList = visitor.toSqlExp(resultNode,
239                         candidateSelectExp, null, 0, null);
240             }
241
242         }
243
244         candidateSelectExp.groupByList = getGroupingExp();
245         if (groupingNode != null) {
246             if (groupingNode.havingNode == null) {
247                 candidateSelectExp.havingExp = null;
248             } else {
249                 candidateSelectExp.havingExp = new HavingExp(visitor.toSqlExp(
250                         groupingNode.havingNode, candidateSelectExp, null, 0,
251                         null));
252             }
253         }
254
255         reOrderJoinExp(cmd.fetchGroups[q.getFetchGroupIndex()], candidateSelectExp);
256
257         // add an orderby if required
258
if (orders != null) {
259             addOrderBy(candidateSelectExp);
260         }
261
262         if (Debug.DEBUG) {
263             Debug.OUT.println("\n* Finished SQL tree:");
264             candidateSelectExp.dump("");
265         }
266
267         /**
268          * determine if we should add a orderBy pk. This is only needed for
269          * parColFetching. If this is a aggregate only query then this is not needed.
270          */

271         if (cq.isContainsThis()) {
272             if (cq.isParColFetchEnabled() || cq.isCrossJoinAllowed()) {
273                 candidateSelectExp.appendOrderByForColumns(((JdbcClass)cmd.storeClass).table.pk);
274             }
275         }
276
277         /**
278          * Add the orderby as provided by the crossjoin
279          */

280         if (orderByFromCrossJoin != null) {
281             candidateSelectExp.appendOrderByExp(orderByFromCrossJoin);
282         }
283
284         //find all equivalent joins
285
SelectExp.mergeJoinList(candidateSelectExp.joinList);
286
287         doFinalSql(cq.getSqlStruct(), candidateSelectExp, driver);
288
289         // build params from ParamNode usages
290
if (params != null) compileParams(qParser, cq.getSqlStruct());
291
292         if (Debug.DEBUG) {
293             Debug.OUT.println("\nParams:");
294             dumpParams(cq, cq.getParamList());
295         }
296
297         final CmdBitSet bits = qParser.getCmds();
298         int[] a = q.getExtraEvictClasses();
299         if (a != null) {
300             for (int i = a.length - 1; i >= 0; i--) {
301                 bits.add(jmd.classes[a[i]]);
302             }
303         }
304         cq.setFilterClsIndexs(bits.toArray());
305         cq.setCacheable(bits.isCacheble() && !q.isRandomAccess());
306         cq.setEvictionClassBits(bits.getBits());
307         cq.setEvictionClassIndexes(bits.getIndexes());
308         return cq;
309     }
310
311     /**
312      * Fill in the storeExtent of all variables.
313      */

314     private void resolveVarNodes(QueryParser qParser) {
315         VarNode[] vars = qParser.getVars();
316         if (vars == null || vars.length == 0) {
317             return;
318         }
319         for (int i = 0; i < vars.length; i++) {
320             VarNode var = vars[i];
321             ClassMetaData vcmd = var.getCmd();
322             if (vcmd == null) {
323                 continue;
324             }
325             SelectExp se = new SelectExp();
326             se.table = ((JdbcClass)vcmd.storeClass).table;
327             se.var = var;
328             if (vcmd.pcSuperMetaData != null) {
329                 // subclass so add test for correct class-id in jdo_class
330
// column
331
se.whereExp =
332                         ((JdbcClass)vcmd.storeClass).getCheckClassIdExp(se);
333             }
334             var.setStoreExtent(se);
335         }
336     }
337
338     public static void reOrderJoinExp(FetchGroup fg, SelectExp se) {
339         Join current = se.joinList;
340         FetchGroupField[] fgfs = fg.fields;
341         for (int i = 0; i < fgfs.length; i++) {
342             FetchGroupField fgf = fgfs[i];
343
344             if (fgf.fmd.category != MDStatics.CATEGORY_REF) continue;
345             Join join = se.findJoin((JdbcField)fgf.fmd.storeField);
346             if (join != null) {
347                 if (join == current) {
348                     current = current.next;
349                 } else {
350                     Join beforeToMove = findJoinBefore(join,
351                             se.joinList);
352                     Join beforeCurrent = findJoinBefore(current,
353                             se.joinList);
354
355                     if (beforeToMove.next != join) {
356                         throw new RuntimeException JavaDoc("before.next != join");
357                     }
358
359                     beforeToMove.next = join.next;
360                     join.next = current;
361                     if (beforeCurrent != null) beforeCurrent.next = join;
362                     if (current == se.joinList) {
363                         se.joinList = join;
364                     }
365                 }
366             }
367         }
368     }
369
370     private SqlExp getGroupingExp() {
371         SqlExp gpExp = null;
372         if (groupingNode != null) {
373             gpExp = visitor.toSqlExp(groupingNode, candidateSelectExp, null, 0,
374                     null);
375         }
376         return gpExp;
377     }
378
379     private void processNode(Node node, String JavaDoc info) {
380         if (node == null) return;
381         if (Debug.DEBUG) {
382             Debug.OUT.println("\n* " + info + ": " + node);
383             Debug.OUT.println("\n* Parsed tree:");
384             node.dump("");
385         }
386
387         node.normalize();
388         if (Debug.DEBUG) {
389             Debug.OUT.println("\n* Normalized tree:");
390             node.dump("");
391         }
392
393         node.resolve(qParser, cmd, false);
394         if (Debug.DEBUG) {
395             Debug.OUT.println("\n* Resolved tree:");
396             node.dump("");
397         }
398
399         node.normalize();
400         if (Debug.DEBUG) {
401             Debug.OUT.println("\n* Second normalized tree:");
402             node.dump("");
403         }
404     }
405
406     private SqlExp compileParallelFetchImp(QueryDetails q) {
407         JdbcCompiledQuery cq = new JdbcCompiledQuery(cmd, q);
408         qParser = new QueryParser(jmd);
409
410         try {
411             qParser.parse(q);
412             resolveVarNodes(qParser);
413             params = qParser.getParams();
414             orders = qParser.getOrders();
415             filter = qParser.getFilter();
416         } catch (Exception JavaDoc e) {
417             if( BindingSupportImpl.getInstance().isOwnException(e) )
418             {
419                 throw (RuntimeException JavaDoc) e;
420             }
421             else
422             {
423                 throw BindingSupportImpl.getInstance().invalidOperation(e.getMessage(), e);
424             }
425         } catch (TokenMgrError e) {
426             throw BindingSupportImpl.getInstance().invalidOperation(e.getMessage(), e);
427         }
428
429         if (filter != null) {
430             filter.normalize();
431             if (Debug.DEBUG) {
432                 Debug.OUT.println("\n* Normalized tree:");
433                 filter.dump("");
434             }
435         }
436
437         // build the SQL query tree
438
candidateSelectExp = new SelectExp();
439         JdbcClass jdbcClass = (JdbcClass)cmd.storeClass;
440         candidateSelectExp.table = jdbcClass.table;
441
442         FetchGroup fg = cmd.fetchGroups[q.getFetchGroupIndex()];
443         //do inner joins to basetable
444
for (FetchGroup supg = fg.superFetchGroup; supg != null;
445              supg = supg.superFetchGroup) {
446             JdbcClass sc = (JdbcClass)supg.classMetaData.storeClass;
447
448             if (sc.table != candidateSelectExp.table) {
449                 // different table so do an inner join
450
SelectExp se = candidateSelectExp.findTable(sc.table);
451                 if (se == null) {
452                     se = new SelectExp();
453                     se.table = sc.table;
454                     candidateSelectExp.addJoin(candidateSelectExp.table.pk,
455                             se.table.pk, se);
456                 }
457             }
458         }
459
460         if (filter != null) {
461             candidateSelectExp.whereExp = visitor.toSqlExp(filter,
462                     candidateSelectExp, null, 0, null);
463         }
464
465         // include only correct subclass(es) if cmd is in a heirachy
466
if (cmd.isInHeirachy()) addSubclassFilter(candidateSelectExp, cq);
467
468         if (Debug.DEBUG) {
469             Debug.OUT.println("\n* SQL tree:");
470             candidateSelectExp.dump("");
471         }
472
473         SqlDriver driver = sm.getSqlDriver();
474         candidateSelectExp.normalize(driver, null, driver.isConvertExistsToDistinctJoin());
475         if (Debug.DEBUG) {
476             Debug.OUT.println("\n* Normalized SQL tree:");
477             candidateSelectExp.dump("");
478         }
479
480         reOrderJoinExp(cmd.fetchGroups[q.getFetchGroupIndex()], candidateSelectExp);
481
482         // add an order by if required
483
if (orders != null) {
484             addOrderBy(candidateSelectExp);
485         }
486
487         if (Debug.DEBUG) {
488             Debug.OUT.println("\n* Finished SQL tree:");
489             candidateSelectExp.dump("");
490         }
491
492         candidateSelectExp.appendOrderByForColumns(((JdbcClass)cmd.storeClass).table.pk);
493
494         if (Debug.DEBUG) {
495             Debug.OUT.println("\nSQL:\n" + cq.getSqlbuf());
496         }
497         return candidateSelectExp;
498     }
499
500     public static void doFinalSql(SqlStruct sqlStruct, SelectExp root,
501             SqlDriver driver) {
502         // get the final SQL
503
int aliasCount = root.createAlias(0);
504         if (aliasCount == 1) {
505             root.alias = null;
506             sqlStruct.setFirstTableOrAlias(root.table.name);
507         } else {
508             sqlStruct.setFirstTableOrAlias(root.alias);
509         }
510
511         root.appendSQL(driver, sqlStruct.getSqlbuf(), null);
512         sqlStruct.setSelectListRange(root.distinct, root.selectListStartIndex,
513                 root.selectListFirstColEndIndex, root.selectListEndIndex);
514         sqlStruct.setOrderByRange(root.orderByStartIndex, root.orderByEndIndex);
515
516         // work around bug with replace in CharBuffer class
517
sqlStruct.getSqlbuf().append(' ');
518
519         if (Debug.DEBUG) {
520             System.out.println("\nSQL:\n" + sqlStruct.getSqlbuf());
521         }
522     }
523
524     public static Join findJoinBefore(Join aJoin, Join root) {
525         for (Join join = root; join != null; join = join.next) {
526             if (join.next == aJoin) return join;
527         }
528         return null;
529     }
530
531     private void addSubclassFilter(SelectExp root, JdbcCompiledQuery cq) {
532         // no filter needed if we want the whole heirachy
533
if (((JdbcClass)cmd.storeClass).classIdCol != null) {
534             if (cq.isIncludeSubclasses() && cmd.pcSuperMetaData == null) return;
535             if (cq.isIncludeSubclasses()
536                     && ((JdbcClass)cmd.storeClass).inheritance == JdbcClass.INHERITANCE_VERTICAL) {
537                 return;
538             }
539             addClassIdFilter(root, cq);
540         } else {
541             if (!cq.isIncludeSubclasses()) {
542                 if (cmd.pcSubclasses == null) return;
543                 //must add joins to immediate sub tables and where id col is null
544
for (int i = 0; i < cmd.pcSubclasses.length; i++) {
545                     ClassMetaData pcSubclass = cmd.pcSubclasses[i];
546
547                     SelectExp se = root.findTable(((JdbcClass)pcSubclass.storeClass).table);
548                     if (se == null) {
549                         se = new SelectExp();
550                         se.outer = true;
551                         se.table = ((JdbcClass)pcSubclass.storeClass).table;
552
553                         SqlExp colExp = ((JdbcClass)pcSubclass.storeClass).table.pk[0].toSqlExp(se);
554
555                         root.appendToWhereExp(new IsNullExp(colExp));
556                         root.addJoin(root.table.pk, se.table.pk, se);
557                     }
558                 }
559             }
560         }
561
562     }
563
564     private void addClassIdFilter(SelectExp root, JdbcCompiledQuery cq) {
565         // create expression for the class ID column
566
JdbcColumn cidcol = ((JdbcClass)cmd.storeClass).classIdCol;
567         SelectExp se = root.findTable(cidcol.table);
568         if (se == null) {
569             throw BindingSupportImpl.getInstance().invalidOperation(
570                     "Table for classId column not in SelectExp");
571         }
572         SqlExp cidexp = cidcol.toSqlExp(se);
573         if (cidexp.next != null) {
574             throw BindingSupportImpl.getInstance().invalidOperation(
575                     "Compound classId columns not implemented");
576         }
577
578         SqlExp fe;
579         if (!cq.isIncludeSubclasses() || cmd.pcSubclasses == null) {
580             // classid = literal expression
581
fe = new BinaryOpExp(cidexp, BinaryOpExp.EQUAL,
582                     cidcol.createClassIdLiteralExp(((JdbcClass)cmd.storeClass).jdbcClassId));
583         } else {
584             // in expression with a literal for each class id
585
cidexp.next = createClassIdLiteralExp(cmd);
586             fe = new InExp(cidexp);
587         }
588
589         // add this to the where clause
590
if (root.whereExp == null) {
591             root.whereExp = fe;
592         } else if (root.whereExp instanceof AndExp) {
593             root.whereExp.append(fe);
594         } else {
595             root.whereExp.next = fe;
596             root.whereExp = new AndExp(root.whereExp);
597         }
598     }
599
600     /**
601      * Create a list of LiteralExp's for the class ID's of cmd and all of
602      * its subclasses.
603      */

604     private SqlExp createClassIdLiteralExp(ClassMetaData cmd) {
605         SqlExp e = ((JdbcClass)cmd.storeClass).classIdCol.createClassIdLiteralExp(
606                 ((JdbcClass)cmd.storeClass).jdbcClassId);
607         ClassMetaData[] a = cmd.pcSubclasses;
608         if (a != null) {
609             SqlExp p = e;
610             for (int i = a.length - 1; i >= 0; i--) {
611                 SqlExp q = createClassIdLiteralExp(a[i]);
612                 for (; p.next != null; p = p.next) ;
613                 p = p.next = q;
614             }
615         }
616         return e;
617     }
618
619     private SqlExp addOrderBy(SelectExp root) {
620         int len = orders.length;
621         for (int i = 0; i < len; i++) orders[i].resolve(qParser, cmd, true);
622         return root.addOrderBy(orders, false, visitor);
623     }
624
625     private void addPrimaryKey(SelectExp root) {
626         JdbcColumn[] pk = root.table.pkSimpleCols;
627         ColumnExp list = new ColumnExp(pk[0], root, null);
628         SqlExp pos = list;
629         int n = pk.length;
630         for (int i = 1; i < n; i++) {
631             pos = pos.next = new ColumnExp(pk[i], root,
632                     null);
633         }
634         pos.next = root.selectList;
635         root.selectList = list;
636     }
637
638     /**
639      * This nasty code has to find the bits of SQL occupied by parameter
640      * expressions that could be null. They may need to be converted into
641      * 'is null' or 'is not null' or removed completely (for shared columns)
642      * if the corresponding parameter is null.
643      */

644     public static void compileParams(QueryParser qParser, SqlStruct sqlStruct) {
645         if (qParser.getParams() == null) return;
646         SqlStruct.Param list = null;
647         SqlStruct.Param pos = null;
648         ParamNode[] params = qParser.getParams();
649         int np = params.length;
650         for (int i = 0; i < np; i++) {
651             ParamNode p = params[i];
652             SqlParamUsage usage = (SqlParamUsage)p.usageList;
653             if (usage == null) continue;
654
655             for (; usage != null; usage = usage.next) {
656
657                 // create new param and add it to the list
658
SqlStruct.Param param = new SqlStruct.Param(p.getIdentifier());
659                 if (pos == null) {
660                     pos = list = param;
661                 } else {
662                     pos = pos.next = param;
663                 }
664
665                 // fill in the param
666
param.declaredParamIndex = i;
667                 JdbcField jdbcField = usage.jdbcField;
668                 if (jdbcField == null) {
669                     param.classIndex = usage.classIndex;
670                     param.fieldNo = -1;
671                     param.javaTypeCode = usage.javaTypeCode;
672                     if (param.javaTypeCode == 0) {
673                         p.resolve(qParser, null, false);
674                         param.javaTypeCode = MDStaticUtils.toTypeCode(
675                                 p.getCls());
676                     }
677                     param.jdbcType = usage.jdbcType;
678                     param.col = usage.col;
679                 } else {
680                     param.classIndex = jdbcField.fmd.classMetaData.index;
681                     param.fieldNo = jdbcField.stateFieldNo;
682                     param.col = usage.col;
683                 }
684                 param.mod = usage.mod;
685
686                 // make a CharSpan for each usage
687
if (usage.expCount > 0) {
688                     SqlStruct.CharSpan cspos = null;
689                     SqlStruct.CharSpan[] a
690                             = new SqlStruct.CharSpan[usage.expCount];
691                     boolean multicol = usage.expCount > 1;
692                     int j = 0;
693                     int removeCount = 0;
694                     for (SqlExp e = usage.expList; j < a.length; j++) {
695                         SqlStruct.CharSpan cs = a[j] =
696                                 new SqlStruct.CharSpan();
697                         if (multicol && mustBeRemovedIfNull(e)) {
698                             if (++removeCount == a.length) {
699                                 // all expressions are to be removed so restart
700
// the loop making them all 'is null' instead
701
multicol = false;
702                                 e = usage.expList;
703                                 j = -1;
704                                 cspos = null;
705                                 continue;
706                             }
707                             cs.firstCharIndex = e.getPreFirstCharIndex();
708                             if (e.next == null) { // last span
709
cs.lastCharIndex = e.getLastCharIndex();
710                                 // work back and remove trailing 'and' if any
711
for (int k = j - 1; k >= 0; k--) {
712                                     if (a[k].type != SqlStruct.CharSpan.TYPE_REMOVE) {
713                                         a[k + 1].firstCharIndex -= 4; // 'and '
714
break;
715                                     }
716                                 }
717                             } else { // first or middle span
718
cs.lastCharIndex = e.next.getPreFirstCharIndex();
719                             }
720                             cs.type = SqlStruct.CharSpan.TYPE_REMOVE;
721                         } else {
722                             cs.firstCharIndex = e.getFirstCharIndex();
723                             cs.lastCharIndex = e.getLastCharIndex();
724                             cs.type = e.isNegative()
725                                     ? SqlStruct.CharSpan.TYPE_NOT_NULL
726                                     : SqlStruct.CharSpan.TYPE_NULL;
727                         }
728
729                         if (cspos == null) {
730                             cspos = param.charSpanList = cs;
731                             param.firstCharIndex = cs.firstCharIndex;
732                         } else {
733                             cspos = cspos.next = cs;
734                         }
735
736                         e = e.next;
737                     }
738                 } else {
739                     param.firstCharIndex = usage.expList.getFirstCharIndex();
740                 }
741             }
742         }
743         if (list != null) sqlStruct.setParamList(sortParams(list));
744     }
745
746     /**
747      * Columns that are not updated (i.e. are shared) must be removed
748      * completely if the matching parameter is null.
749      */

750     private static boolean mustBeRemovedIfNull(SqlExp e) {
751         if (!e.isNegative() && e.childList instanceof ColumnExp) {
752             ColumnExp ce = (ColumnExp)e.childList;
753             return !ce.col.isForUpdate();
754         }
755         return false;
756     }
757
758     /**
759      * Sort the params in the order that they appear in the query.
760      */

761     private static SqlStruct.Param sortParams(SqlStruct.Param list) {
762         if (list.next == null) return list;
763         // stone sort the list (bubble sort except elements sink to the bottom)
764
for (; ;) {
765             boolean changed = false;
766             SqlStruct.Param p0 = null;
767             for (SqlStruct.Param p1 = list; ;) {
768                 SqlStruct.Param p2 = p1.next;
769                 if (p2 == null) break;
770                 if (p1.firstCharIndex > p2.firstCharIndex) {
771                     // exchange p and p2
772
p1.next = p2.next;
773                     p2.next = p1;
774                     if (p0 == null) {
775                         list = p2;
776                     } else {
777                         p0.next = p2;
778                     }
779                     p0 = p2;
780                     changed = true;
781                 } else {
782                     p0 = p1;
783                     p1 = p2;
784                 }
785             }
786             if (!changed) return list;
787         }
788     }
789
790     private void dumpParams(JdbcCompiledQuery cq, SqlStruct.Param p) {
791         for (; p != null; p = p.next) {
792             if (Debug.DEBUG) {
793                 Debug.OUT.println("Param " + p.declaredParamIndex +
794                         " firstCharIndex " + p.firstCharIndex);
795             }
796             for (SqlStruct.CharSpan s = p.charSpanList; s != null; s = s.next) {
797                 if (Debug.DEBUG) {
798                     String JavaDoc ts;
799                     switch (s.type) {
800                         case SqlStruct.CharSpan.TYPE_NULL:
801                             ts = "NULL";
802                             break;
803                         case SqlStruct.CharSpan.TYPE_NOT_NULL:
804                             ts = "NOT_NULL";
805                             break;
806                         case SqlStruct.CharSpan.TYPE_REMOVE:
807                             ts = "REMOVE";
808                             break;
809                         default:
810                             ts = "Unknown(" + s.type + ")";
811                     }
812                     Debug.OUT.println(" CharSpan " + s.firstCharIndex + " to " +
813                             s.lastCharIndex + " " + ts + " = '" +
814                             cq.getSqlbuf().toString(s.firstCharIndex,
815                                     s.lastCharIndex - s.firstCharIndex) + "'");
816                 }
817             }
818         }
819     }
820
821     public ModelMetaData getJmd() {
822         return jmd;
823     }
824
825     /**
826      * Get the select for the candidate class. All other SqlExp's for the
827      * query are reachable from this.
828      */

829     public SelectExp getCandidateSelectExp() {
830         return candidateSelectExp;
831     }
832 }
833
Popular Tags