KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jboss > ejb > plugins > cmp > jdbc > JDBCEJBQLCompiler


1 /*
2 * JBoss, Home of Professional Open Source
3 * Copyright 2005, JBoss Inc., and individual contributors as indicated
4 * by the @authors tag. See the copyright.txt in the distribution for a
5 * full listing of individual contributors.
6 *
7 * This is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU Lesser General Public License as
9 * published by the Free Software Foundation; either version 2.1 of
10 * the License, or (at your option) any later version.
11 *
12 * This software is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this software; if not, write to the Free
19 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
20 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
21 */

22 package org.jboss.ejb.plugins.cmp.jdbc;
23
24 import java.io.StringReader JavaDoc;
25 import java.util.ArrayList JavaDoc;
26 import java.util.HashMap JavaDoc;
27 import java.util.HashSet JavaDoc;
28 import java.util.Iterator JavaDoc;
29 import java.util.List JavaDoc;
30 import java.util.Map JavaDoc;
31 import java.util.Set JavaDoc;
32
33 import org.jboss.ejb.plugins.cmp.ejbql.ASTAbs;
34 import org.jboss.ejb.plugins.cmp.ejbql.ASTAbstractSchema;
35 import org.jboss.ejb.plugins.cmp.ejbql.ASTBooleanLiteral;
36 import org.jboss.ejb.plugins.cmp.ejbql.ASTCollectionMemberDeclaration;
37 import org.jboss.ejb.plugins.cmp.ejbql.ASTConcat;
38 import org.jboss.ejb.plugins.cmp.ejbql.ASTEJBQL;
39 import org.jboss.ejb.plugins.cmp.ejbql.ASTEntityComparison;
40 import org.jboss.ejb.plugins.cmp.ejbql.ASTFrom;
41 import org.jboss.ejb.plugins.cmp.ejbql.ASTIdentifier;
42 import org.jboss.ejb.plugins.cmp.ejbql.ASTIsEmpty;
43 import org.jboss.ejb.plugins.cmp.ejbql.ASTLCase;
44 import org.jboss.ejb.plugins.cmp.ejbql.ASTLength;
45 import org.jboss.ejb.plugins.cmp.ejbql.ASTLocate;
46 import org.jboss.ejb.plugins.cmp.ejbql.ASTMemberOf;
47 import org.jboss.ejb.plugins.cmp.ejbql.ASTNullComparison;
48 import org.jboss.ejb.plugins.cmp.ejbql.ASTOrderBy;
49 import org.jboss.ejb.plugins.cmp.ejbql.ASTParameter;
50 import org.jboss.ejb.plugins.cmp.ejbql.ASTPath;
51 import org.jboss.ejb.plugins.cmp.ejbql.ASTRangeVariableDeclaration;
52 import org.jboss.ejb.plugins.cmp.ejbql.ASTSelect;
53 import org.jboss.ejb.plugins.cmp.ejbql.ASTSqrt;
54 import org.jboss.ejb.plugins.cmp.ejbql.ASTSubstring;
55 import org.jboss.ejb.plugins.cmp.ejbql.ASTUCase;
56 import org.jboss.ejb.plugins.cmp.ejbql.ASTValueClassComparison;
57 import org.jboss.ejb.plugins.cmp.ejbql.ASTWhere;
58 import org.jboss.ejb.plugins.cmp.ejbql.BasicVisitor;
59 import org.jboss.ejb.plugins.cmp.ejbql.Catalog;
60 import org.jboss.ejb.plugins.cmp.ejbql.EJBQLParser;
61 import org.jboss.ejb.plugins.cmp.ejbql.EJBQLTypes;
62 import org.jboss.ejb.plugins.cmp.ejbql.JBossQLParser;
63 import org.jboss.ejb.plugins.cmp.ejbql.Node;
64 import org.jboss.ejb.plugins.cmp.ejbql.SimpleNode;
65 import org.jboss.ejb.plugins.cmp.ejbql.ASTLimitOffset;
66 import org.jboss.ejb.plugins.cmp.ejbql.ASTCount;
67 import org.jboss.ejb.plugins.cmp.ejbql.SelectFunction;
68 import org.jboss.ejb.plugins.cmp.ejbql.ASTExactNumericLiteral;
69 import org.jboss.ejb.plugins.cmp.ejbql.ASTMax;
70 import org.jboss.ejb.plugins.cmp.ejbql.ASTMin;
71 import org.jboss.ejb.plugins.cmp.ejbql.ASTAvg;
72 import org.jboss.ejb.plugins.cmp.ejbql.ASTSum;
73 import org.jboss.ejb.plugins.cmp.ejbql.ASTWhereConditionalTerm;
74 import org.jboss.ejb.plugins.cmp.ejbql.ASTMod;
75 import org.jboss.ejb.plugins.cmp.jdbc.bridge.JDBCCMPFieldBridge;
76 import org.jboss.ejb.plugins.cmp.jdbc.bridge.JDBCCMRFieldBridge;
77 import org.jboss.ejb.plugins.cmp.jdbc.bridge.JDBCEntityBridge;
78 import org.jboss.ejb.plugins.cmp.jdbc.bridge.JDBCFieldBridge;
79 import org.jboss.ejb.plugins.cmp.jdbc.bridge.JDBCAbstractEntityBridge;
80 import org.jboss.ejb.plugins.cmp.jdbc.metadata.JDBCFunctionMappingMetaData;
81 import org.jboss.ejb.plugins.cmp.jdbc.metadata.JDBCReadAheadMetaData;
82 import org.jboss.ejb.plugins.cmp.jdbc.metadata.JDBCTypeMappingMetaData;
83 import org.jboss.ejb.plugins.cmp.jdbc.metadata.JDBCQueryMetaData;
84 import org.jboss.ejb.plugins.cmp.bridge.CMPFieldBridge;
85 import org.jboss.ejb.EntityPersistenceStore;
86 import org.jboss.deployment.DeploymentException;
87
88 /**
89  * Compiles EJB-QL and JBossQL into SQL.
90  *
91  * @author <a HREF="mailto:dain@daingroup.com">Dain Sundstrom</a>
92  * @author <a HREF="mailto:alex@jboss.org">Alex Loubyansky</a>
93  * @version $Revision: 58402 $
94  */

95 public final class JDBCEJBQLCompiler extends BasicVisitor implements QLCompiler
96 {
97    // input objects
98
private final Catalog catalog;
99    private Class JavaDoc returnType;
100    private Class JavaDoc[] parameterTypes;
101    private JDBCReadAheadMetaData readAhead;
102    private boolean lazyResultSetLoading;
103
104    // alias info
105
private AliasManager aliasManager;
106
107    // join info
108
private Set JavaDoc declaredPaths = new HashSet JavaDoc();
109    private Set JavaDoc ctermJoinPaths = new HashSet JavaDoc();
110    private Set JavaDoc allJoinPaths = new HashSet JavaDoc();
111    private Map JavaDoc ctermCollectionMemberJoinPaths = new HashMap JavaDoc();
112    private Map JavaDoc allCollectionMemberJoinPaths = new HashMap JavaDoc();
113    private Map JavaDoc ctermLeftJoinPaths = new HashMap JavaDoc();
114    private Map JavaDoc allLeftJoinPaths = new HashMap JavaDoc();
115
116    // mapping metadata
117
private JDBCTypeMappingMetaData typeMapping;
118    private JDBCTypeFactory typeFactory;
119    private boolean subquerySupported;
120
121    // output objects
122
private boolean forceDistinct;
123    private String JavaDoc sql;
124    private int offsetParam;
125    private int offsetValue;
126    private int limitParam;
127    private int limitValue;
128    private JDBCStoreManager selectManager;
129    private Object JavaDoc selectObject;
130    private List JavaDoc inputParameters = new ArrayList JavaDoc();
131
132    /**
133     * deep read ahead for cmrs
134     */

135    private List JavaDoc leftJoinCMRList = new ArrayList JavaDoc();
136    private StringBuffer JavaDoc onFindCMRJoin;
137
138    private boolean countCompositePk;
139    private String JavaDoc selectAlias;
140    private boolean selectDistinct;
141
142    public JDBCEJBQLCompiler(Catalog catalog)
143    {
144       this.catalog = catalog;
145    }
146
147    public void compileEJBQL(String JavaDoc ejbql,
148                             Class JavaDoc returnType,
149                             Class JavaDoc[] parameterTypes,
150                             JDBCQueryMetaData metadata) throws Exception JavaDoc
151    {
152       // reset all state variables
153
reset();
154
155       // set input arguemts
156
this.returnType = returnType;
157       this.parameterTypes = parameterTypes;
158       this.readAhead = metadata.getReadAhead();
159       this.lazyResultSetLoading = metadata.isLazyResultSetLoading();
160
161       // get the parser
162
EJBQLParser parser = new EJBQLParser(new StringReader JavaDoc(SQLUtil.EMPTY_STRING));
163
164       try
165       {
166          // parse the ejbql into an abstract sytax tree
167
ASTEJBQL ejbqlNode;
168          ejbqlNode = parser.parse(catalog, parameterTypes, ejbql);
169
170          // translate to sql
171
sql = ejbqlNode.jjtAccept(this, new StringBuffer JavaDoc()).toString();
172       }
173       catch(Exception JavaDoc e)
174       {
175          // if there is a problem reset the state before exiting
176
reset();
177          throw e;
178       }
179       catch(Error JavaDoc e)
180       {
181          // lame javacc lexer throws Errors
182
reset();
183          throw e;
184       }
185    }
186
187    public void compileJBossQL(String JavaDoc ejbql,
188                               Class JavaDoc returnType,
189                               Class JavaDoc[] parameterTypes,
190                               JDBCQueryMetaData metadata)
191       throws Exception JavaDoc
192    {
193       // reset all state variables
194
reset();
195
196       // set input arguemts
197
this.returnType = returnType;
198       this.parameterTypes = parameterTypes;
199       this.readAhead = metadata.getReadAhead();
200       this.lazyResultSetLoading = metadata.isLazyResultSetLoading();
201
202       // get the parser
203
JBossQLParser parser = new JBossQLParser(new StringReader JavaDoc(SQLUtil.EMPTY_STRING));
204
205       try
206       {
207          // parse the ejbql into an abstract sytax tree
208
ASTEJBQL ejbqlNode;
209          ejbqlNode = parser.parse(catalog, parameterTypes, ejbql);
210
211          // translate to sql
212
sql = ejbqlNode.jjtAccept(this, new StringBuffer JavaDoc()).toString();
213       }
214       catch(Exception JavaDoc e)
215       {
216          // if there is a problem reset the state before exiting
217
reset();
218          throw e;
219       }
220       catch(Error JavaDoc e)
221       {
222          // lame javacc lexer throws Errors
223
reset();
224          throw e;
225       }
226    }
227
228    private void reset()
229    {
230       returnType = null;
231       parameterTypes = null;
232       readAhead = null;
233       inputParameters.clear();
234       declaredPaths.clear();
235       clearPerTermJoinPaths();
236       allJoinPaths.clear();
237       allCollectionMemberJoinPaths.clear();
238       allLeftJoinPaths.clear();
239       selectObject = null;
240       selectManager = null;
241       typeFactory = null;
242       typeMapping = null;
243       aliasManager = null;
244       subquerySupported = true;
245       forceDistinct = false;
246       limitParam = 0;
247       limitValue = 0;
248       offsetParam = 0;
249       offsetValue = 0;
250       leftJoinCMRList.clear();
251       onFindCMRJoin = null;
252       countCompositePk = false;
253       selectAlias = null;
254       selectDistinct = false;
255    }
256
257    public String JavaDoc getSQL()
258    {
259       return sql;
260    }
261
262    public int getOffsetValue()
263    {
264       return offsetValue;
265    }
266
267    public int getOffsetParam()
268    {
269       return offsetParam;
270    }
271
272    public int getLimitValue()
273    {
274       return limitValue;
275    }
276
277    public int getLimitParam()
278    {
279       return limitParam;
280    }
281
282    public boolean isSelectEntity()
283    {
284       return selectObject instanceof JDBCEntityBridge;
285    }
286
287    public JDBCAbstractEntityBridge getSelectEntity()
288    {
289       return (JDBCAbstractEntityBridge) selectObject;
290    }
291
292    public boolean isSelectField()
293    {
294       return selectObject instanceof JDBCCMPFieldBridge;
295    }
296
297    public JDBCFieldBridge getSelectField()
298    {
299       return (JDBCCMPFieldBridge) selectObject;
300    }
301
302    public SelectFunction getSelectFunction()
303    {
304       return (SelectFunction) selectObject;
305    }
306
307    public EntityPersistenceStore getStoreManager()
308    {
309       return selectManager;
310    }
311
312    public List JavaDoc getInputParameters()
313    {
314       return inputParameters;
315    }
316
317    public List JavaDoc getLeftJoinCMRList()
318    {
319       return leftJoinCMRList;
320    }
321
322    public boolean isSelectDistinct()
323    {
324       return selectDistinct;
325    }
326
327    public Object JavaDoc visit(SimpleNode node, Object JavaDoc data)
328    {
329       throw new RuntimeException JavaDoc("Internal error: Found unknown node type in " +
330          "EJB-QL abstract syntax tree: node=" + node);
331    }
332
333    private void setTypeFactory(JDBCTypeFactory typeFactory)
334    {
335       this.typeFactory = typeFactory;
336       this.typeMapping = typeFactory.getTypeMapping();
337       aliasManager = new AliasManager(typeMapping.getAliasHeaderPrefix(),
338          typeMapping.getAliasHeaderSuffix(),
339          typeMapping.getAliasMaxLength());
340       subquerySupported = typeMapping.isSubquerySupported();
341    }
342
343    private Class JavaDoc getParameterType(int index)
344    {
345       int zeroBasedIndex = index - 1;
346       Class JavaDoc[] params = parameterTypes;
347       if(zeroBasedIndex < params.length)
348       {
349          return params[zeroBasedIndex];
350       }
351       return null;
352    }
353
354    // verify that parameter is the same type as the entity
355
private void verifyParameterEntityType(int number,
356                                           JDBCEntityBridge entity)
357    {
358       Class JavaDoc parameterType = getParameterType(number);
359       Class JavaDoc remoteClass = entity.getMetaData().getRemoteClass();
360       Class JavaDoc localClass = entity.getMetaData().getLocalClass();
361       if((
362          localClass == null ||
363          !localClass.isAssignableFrom(parameterType)
364          ) &&
365          (
366          remoteClass == null ||
367          !remoteClass.isAssignableFrom(parameterType)
368          ))
369       {
370
371          throw new IllegalStateException JavaDoc("Only like types can be " +
372             "compared: from entity=" +
373             entity.getEntityName() +
374             " to parameter type=" + parameterType);
375       }
376    }
377
378    private void compareEntity(boolean not,
379                               Node fromNode,
380                               Node toNode,
381                               StringBuffer JavaDoc buf)
382    {
383       buf.append('(');
384       if(not)
385       {
386          buf.append(SQLUtil.NOT).append('(');
387       }
388
389       String JavaDoc fromAlias;
390       JDBCEntityBridge fromEntity;
391       ASTPath fromPath = (ASTPath) fromNode;
392       addJoinPath(fromPath);
393       fromAlias = aliasManager.getAlias(fromPath.getPath());
394       fromEntity = (JDBCEntityBridge) fromPath.getEntity();
395
396       if(toNode instanceof ASTParameter)
397       {
398          ASTParameter toParam = (ASTParameter) toNode;
399
400          // can only compare like kind entities
401
verifyParameterEntityType(toParam.number, fromEntity);
402
403          inputParameters.addAll(QueryParameter.createParameters(toParam.number - 1, fromEntity));
404
405          SQLUtil.getWhereClause(fromEntity.getPrimaryKeyFields(), fromAlias, buf);
406       }
407       else
408       {
409          String JavaDoc toAlias;
410          JDBCEntityBridge toEntity;
411          ASTPath toPath = (ASTPath) toNode;
412          addJoinPath(toPath);
413          toAlias = aliasManager.getAlias(toPath.getPath());
414          toEntity = (JDBCEntityBridge) toPath.getEntity();
415
416          // can only compare like kind entities
417
if(!fromEntity.equals(toEntity))
418          {
419             throw new IllegalStateException JavaDoc("Only like types can be " +
420                "compared: from entity=" +
421                fromEntity.getEntityName() +
422                " to entity=" + toEntity.getEntityName());
423          }
424
425          SQLUtil.getSelfCompareWhereClause(fromEntity.getPrimaryKeyFields(), fromAlias, toAlias, buf);
426       }
427
428       if(not)
429       {
430          buf.append(')');
431       }
432       buf.append(')');
433    }
434
435    private void existsClause(ASTPath path, StringBuffer JavaDoc buf, boolean not)
436    {
437       if(!path.isCMRField())
438       {
439          throw new IllegalArgumentException JavaDoc("path must be a cmr field");
440       }
441
442       JDBCCMRFieldBridge cmrField = (JDBCCMRFieldBridge) path.getCMRField();
443       String JavaDoc pathStr = path.getPath(path.size() - 2);
444       String JavaDoc parentAlias = aliasManager.getAlias(pathStr);
445
446       // if exists is not supported we use a left join and is null
447
if(!subquerySupported)
448       {
449          // add the path to the list of paths to left join
450
addLeftJoinPath(pathStr, path);
451          forceDistinct = true;
452
453          addJoinPath(path);
454
455          if(cmrField.getRelationMetaData().isForeignKeyMappingStyle())
456          {
457             JDBCEntityBridge childEntity = (JDBCEntityBridge) cmrField.getRelatedEntity();
458             String JavaDoc childAlias = aliasManager.getAlias(path.getPath());
459             SQLUtil.getIsNullClause(!not, childEntity.getPrimaryKeyFields(), childAlias, buf);
460          }
461          else
462          {
463             String JavaDoc relationTableAlias = aliasManager.getRelationTableAlias(path.getPath());
464             SQLUtil.getIsNullClause(!not, cmrField.getTableKeyFields(), relationTableAlias, buf);
465          }
466          return;
467       }
468
469       if(not)
470       {
471          buf.append(SQLUtil.NOT);
472       }
473       buf.append(SQLUtil.EXISTS).append('(');
474
475       if(cmrField.getRelationMetaData().isForeignKeyMappingStyle())
476       {
477          JDBCEntityBridge childEntity = (JDBCEntityBridge) cmrField.getRelatedEntity();
478          String JavaDoc childAlias = aliasManager.getAlias(path.getPath());
479
480          buf.append(SQLUtil.SELECT);
481
482          SQLUtil.getColumnNamesClause(childEntity.getPrimaryKeyFields(), childAlias, buf)
483             .append(SQLUtil.FROM)
484             .append(childEntity.getQualifiedTableName()).append(' ').append(childAlias)
485             .append(SQLUtil.WHERE);
486          SQLUtil.getJoinClause(cmrField, parentAlias, childAlias, buf);
487       }
488       else
489       {
490          String JavaDoc relationTableAlias = aliasManager.getRelationTableAlias(path.getPath());
491          buf.append(SQLUtil.SELECT);
492          SQLUtil.getColumnNamesClause(cmrField.getTableKeyFields(), relationTableAlias, buf)
493             .append(SQLUtil.FROM)
494             .append(cmrField.getQualifiedTableName())
495             .append(' ')
496             .append(relationTableAlias)
497             .append(SQLUtil.WHERE);
498          SQLUtil.getRelationTableJoinClause(cmrField, parentAlias, relationTableAlias, buf);
499       }
500
501       buf.append(')');
502    }
503
504    public Object JavaDoc visit(ASTEJBQL node, Object JavaDoc data)
505    {
506       Node selectNode = node.jjtGetChild(0);
507       Node fromNode = node.jjtGetChild(1);
508       Node whereNode = null;
509       Node orderByNode = null;
510       Node limitNode = null;
511
512       for(int childNode = 2; childNode < node.jjtGetNumChildren(); childNode++)
513       {
514          Node temp = node.jjtGetChild(childNode);
515          if(temp instanceof ASTWhere)
516          {
517             whereNode = temp;
518          }
519          else if(temp instanceof ASTOrderBy)
520          {
521             orderByNode = temp;
522          }
523          else if(temp instanceof ASTLimitOffset)
524          {
525             limitNode = temp;
526          }
527       }
528
529       // translate select and add it to the buffer
530
StringBuffer JavaDoc select = new StringBuffer JavaDoc();
531       selectNode.jjtAccept(this, select);
532
533       // conditional term paths from the where clause are treated separately from those
534
// in select, from and order by.
535
// TODO come up with a nicer treatment implementation.
536
Set JavaDoc selectJoinPaths = new HashSet JavaDoc(ctermJoinPaths);
537       Map JavaDoc selectCollectionMemberJoinPaths = new HashMap JavaDoc(ctermCollectionMemberJoinPaths);
538       Map JavaDoc selectLeftJoinPaths = new HashMap JavaDoc(ctermLeftJoinPaths);
539
540       // translate where and save results to append later
541
StringBuffer JavaDoc where = new StringBuffer JavaDoc();
542       if(whereNode != null)
543       {
544          whereNode.jjtAccept(this, where);
545       }
546
547       // reassign conditional term paths and add paths from order by and from to select paths
548
ctermJoinPaths = selectJoinPaths;
549       ctermCollectionMemberJoinPaths = selectCollectionMemberJoinPaths;
550       ctermLeftJoinPaths = selectLeftJoinPaths;
551
552       // translate order by and save results to append later
553
StringBuffer JavaDoc orderBy = new StringBuffer JavaDoc();
554       if(orderByNode != null)
555       {
556          orderByNode.jjtAccept(this, orderBy);
557
558          // hack alert - this should use the visitor approach
559
for(int i = 0; i < orderByNode.jjtGetNumChildren(); i++)
560          {
561             Node orderByPath = orderByNode.jjtGetChild(i);
562             ASTPath path = (ASTPath) orderByPath.jjtGetChild(0);
563             if(!isSelected(path))
564             {
565                select.append(SQLUtil.COMMA);
566                path.jjtAccept(this, select);
567             }
568          }
569       }
570
571       if(limitNode != null)
572       {
573          limitNode.jjtAccept(this, null);
574       }
575
576       StringBuffer JavaDoc from = new StringBuffer JavaDoc(50);
577       fromNode.jjtAccept(this, from);
578
579       StringBuffer JavaDoc fromThetaJoin = new StringBuffer JavaDoc();
580       createThetaJoin(fromThetaJoin);
581
582       if(where.length() != 0 && fromThetaJoin.length() != 0)
583       {
584          where.insert(0, '(')
585             .append(')')
586             .append(SQLUtil.AND)
587             .append(fromThetaJoin);
588       }
589       else if(fromThetaJoin.length() != 0)
590       {
591          where.append(fromThetaJoin.toString());
592       }
593
594       selectDistinct = isDistinct(selectNode);
595
596       // select size
597
if(lazyResultSetLoading)
598       {
599          StringBuffer JavaDoc buf = new StringBuffer JavaDoc(200);
600          if(isSelectEntity())
601          {
602             final JDBCFieldBridge[] pkFields = getSelectEntity().getPrimaryKeyFields();
603             if(pkFields.length == 1)
604             {
605                buf.append('(').append(SQLUtil.SELECT).append("count(");
606                if(selectDistinct)
607                {
608                   buf.append(SQLUtil.DISTINCT);
609                }
610                SQLUtil.getColumnNamesClause(pkFields, selectAlias, buf);
611                buf.append(')').append(SQLUtil.FROM);
612                buf.append(from);
613                if(where.length() > 0)
614                {
615                   buf.append(SQLUtil.WHERE).append(where);
616                }
617                buf.append("), ");
618                select.insert(0, buf);
619             }
620             else
621             {
622                buf.append('(').append(SQLUtil.SELECT).append("count(*)").append(SQLUtil.FROM)
623                   .append('(')
624                   .append(SQLUtil.SELECT);
625
626                if(selectDistinct)
627                {
628                   buf.append(SQLUtil.DISTINCT);
629                }
630
631                SQLUtil.getColumnNamesClause(pkFields, selectAlias, buf);
632                buf.append(SQLUtil.FROM).append(from);
633
634                if(where.length() > 0)
635                {
636                   buf.append(SQLUtil.WHERE).append(where);
637                }
638                buf.append(") t_count), ");
639                select.insert(0, buf);
640             }
641          }
642          else if(isSelectField())
643          {
644             buf.append('(').append(SQLUtil.SELECT).append("count(");
645             if(selectDistinct)
646             {
647                buf.append(SQLUtil.DISTINCT);
648             }
649             buf.append(select).append(')').append(SQLUtil.FROM);
650             buf.append(from);
651             if(where.length() > 0)
652             {
653                buf.append(SQLUtil.WHERE).append(where);
654             }
655             buf.append("), ");
656             select.insert(0, buf);
657          }
658       }
659
660       // distinct
661
if(selectDistinct)
662       {
663          select.insert(0, SQLUtil.DISTINCT);
664       }
665
666       StringBuffer JavaDoc buf = (StringBuffer JavaDoc) data;
667       if(selectManager.getMetaData().hasRowLocking())
668       {
669          JDBCFunctionMappingMetaData rowLockingTemplate = typeMapping.getRowLockingTemplate();
670          Object JavaDoc args[] = new Object JavaDoc[]{
671             select,
672             from,
673             where.length() == 0 ? null : where,
674             orderBy.length() == 0 ? null : orderBy
675          };
676          rowLockingTemplate.getFunctionSql(args, buf);
677       }
678       else
679       {
680          buf.append(SQLUtil.SELECT)
681             .append(select)
682             .append(SQLUtil.FROM)
683             .append(from);
684
685          if(where.length() > 0)
686          {
687             buf.append(SQLUtil.WHERE)
688                .append(where);
689          }
690
691          if(orderBy.length() != 0)
692          {
693             buf.append(SQLUtil.ORDERBY)
694                .append(orderBy);
695          }
696       }
697
698       // todo: ...
699
if(countCompositePk)
700       {
701          buf.insert(0, "SELECT COUNT(*) FROM (").append(") t_count");
702       }
703
704       return buf;
705    }
706
707    public Object JavaDoc visit(ASTFrom node, Object JavaDoc data)
708    {
709       StringBuffer JavaDoc buf = (StringBuffer JavaDoc) data;
710
711       node.jjtGetChild(0).jjtAccept(this, buf);
712       for(int i = 1; i < node.jjtGetNumChildren(); i++)
713       {
714          buf.append(SQLUtil.COMMA);
715          node.jjtGetChild(i).jjtAccept(this, buf);
716       }
717
718       // add all the additional path tables
719
if(!allJoinPaths.isEmpty())
720       {
721          for(Iterator JavaDoc iter = allJoinPaths.iterator(); iter.hasNext();)
722          {
723             ASTPath path = (ASTPath) iter.next();
724             for(int i = 0; i < path.size(); i++)
725             {
726                declareTables(path, i, buf);
727             }
728          }
729       }
730
731       // add all parent paths for collection member join paths
732
if(!allCollectionMemberJoinPaths.isEmpty())
733       {
734          for(Iterator JavaDoc iter = allCollectionMemberJoinPaths.values().iterator(); iter.hasNext();)
735          {
736             ASTPath path = (ASTPath) iter.next();
737             // don't declare the last one as the first path was left joined
738
for(int i = 0; i < path.size() - 1; i++)
739             {
740                declareTables(path, i, buf);
741             }
742          }
743       }
744
745       // get all the left joined paths
746
if(!allLeftJoinPaths.isEmpty())
747       {
748          Set JavaDoc allLeftJoins = new HashSet JavaDoc();
749          for(Iterator JavaDoc iter = allLeftJoinPaths.values().iterator(); iter.hasNext();)
750          {
751             allLeftJoins.addAll((Set JavaDoc) iter.next());
752          }
753
754          // add all parent paths for left joins
755
for(Iterator JavaDoc iter = allLeftJoins.iterator(); iter.hasNext();)
756          {
757             ASTPath path = (ASTPath) iter.next();
758             // don't declare the last one as the first path was left joined
759
for(int i = 0; i < path.size() - 1; i++)
760             {
761                declareTables(path, i, buf);
762             }
763          }
764       }
765
766       return buf;
767    }
768
769    private void declareTables(ASTPath path, int i, StringBuffer JavaDoc buf)
770    {
771       if(!path.isCMRField(i) || declaredPaths.contains(path.getPath(i)))
772       {
773          return;
774       }
775
776       JDBCCMRFieldBridge cmrField = (JDBCCMRFieldBridge) path.getCMRField(i);
777       JDBCEntityBridge entity = (JDBCEntityBridge) path.getEntity(i);
778
779       buf.append(SQLUtil.COMMA)
780          .append(entity.getQualifiedTableName())
781          .append(' ')
782          .append(aliasManager.getAlias(path.getPath(i)));
783       leftJoins(path.getPath(i), buf);
784
785       if(cmrField.getRelationMetaData().isTableMappingStyle())
786       {
787          String JavaDoc relationTableAlias = aliasManager.getRelationTableAlias(path.getPath(i));
788          buf.append(SQLUtil.COMMA)
789             .append(cmrField.getQualifiedTableName())
790             .append(' ')
791             .append(relationTableAlias);
792       }
793
794       declaredPaths.add(path.getPath(i));
795    }
796
797    private void leftJoins(String JavaDoc parentPath, StringBuffer JavaDoc buf)
798    {
799       Set JavaDoc paths = (Set JavaDoc) ctermLeftJoinPaths.get(parentPath);
800       if(subquerySupported || paths == null)
801       {
802          return;
803       }
804
805       for(Iterator JavaDoc iter = paths.iterator(); iter.hasNext();)
806       {
807          ASTPath path = (ASTPath) iter.next();
808
809          JDBCCMRFieldBridge cmrField = (JDBCCMRFieldBridge) path.getCMRField();
810          String JavaDoc parentAlias = aliasManager.getAlias(parentPath);
811
812          if(cmrField.getRelationMetaData().isForeignKeyMappingStyle())
813          {
814             JDBCEntityBridge childEntity = (JDBCEntityBridge) cmrField.getRelatedEntity();
815             String JavaDoc childAlias = aliasManager.getAlias(path.getPath());
816
817             buf.append(SQLUtil.LEFT_JOIN)
818                .append(childEntity.getQualifiedTableName())
819                .append(' ')
820                .append(childAlias)
821                .append(SQLUtil.ON);
822             SQLUtil.getJoinClause(cmrField, parentAlias, childAlias, buf);
823          }
824          else
825          {
826             String JavaDoc relationTableAlias = aliasManager.getRelationTableAlias(path.getPath());
827             buf.append(SQLUtil.LEFT_JOIN)
828                .append(cmrField.getQualifiedTableName())
829                .append(' ')
830                .append(relationTableAlias)
831                .append(SQLUtil.ON);
832             SQLUtil.getRelationTableJoinClause(cmrField, parentAlias, relationTableAlias, buf);
833          }
834       }
835    }
836
837    private void createThetaJoin(StringBuffer JavaDoc buf)
838    {
839       Set JavaDoc joinedAliases = new HashSet JavaDoc();
840       // add all the additional path tables
841
if(!ctermJoinPaths.isEmpty())
842       {
843          for(Iterator JavaDoc iter = ctermJoinPaths.iterator(); iter.hasNext();)
844          {
845             ASTPath path = (ASTPath) iter.next();
846             for(int i = 0; i < path.size(); i++)
847             {
848                createThetaJoin(path, i, joinedAliases, buf);
849             }
850          }
851       }
852
853       // add all the collection member path tables
854
if(!ctermCollectionMemberJoinPaths.isEmpty())
855       {
856          for(Iterator JavaDoc iter = ctermCollectionMemberJoinPaths.entrySet().iterator(); iter.hasNext();)
857          {
858             Map.Entry JavaDoc entry = (Map.Entry JavaDoc) iter.next();
859             String JavaDoc childAlias = (String JavaDoc) entry.getKey();
860             ASTPath path = (ASTPath) entry.getValue();
861
862             // join the memeber path
863
createThetaJoin(path, path.size() - 1, joinedAliases, childAlias, buf);
864
865             // join the memeber path parents
866
for(int i = 0; i < path.size() - 1; i++)
867             {
868                createThetaJoin(path, i, joinedAliases, buf);
869             }
870          }
871       }
872
873       // get all the left joined paths
874
if(!ctermLeftJoinPaths.isEmpty())
875       {
876          Set JavaDoc allLeftJoins = new HashSet JavaDoc();
877          for(Iterator JavaDoc iter = ctermLeftJoinPaths.values().iterator(); iter.hasNext();)
878          {
879             allLeftJoins.addAll((Set JavaDoc) iter.next());
880          }
881
882          // add all parent paths for left joins
883
for(Iterator JavaDoc iter = allLeftJoins.iterator(); iter.hasNext();)
884          {
885             ASTPath path = (ASTPath) iter.next();
886             // don't declare the last one as the first path was left joined
887
for(int i = 0; i < path.size() - 1; i++)
888             {
889                createThetaJoin(path, i, joinedAliases, buf);
890             }
891          }
892       }
893    }
894
895    private void createThetaJoin(ASTPath path,
896                                 int i,
897                                 Set JavaDoc joinedAliases,
898                                 StringBuffer JavaDoc buf)
899    {
900       String JavaDoc childAlias = aliasManager.getAlias(path.getPath(i));
901       createThetaJoin(path, i, joinedAliases, childAlias, buf);
902    }
903
904    private void createThetaJoin(ASTPath path,
905                                 int i,
906                                 Set JavaDoc joinedAliases,
907                                 String JavaDoc childAlias,
908                                 StringBuffer JavaDoc buf)
909    {
910       if(!path.isCMRField(i) || joinedAliases.contains(childAlias))
911       {
912          return;
913       }
914
915       JDBCCMRFieldBridge cmrField = (JDBCCMRFieldBridge) path.getCMRField(i);
916       String JavaDoc parentAlias = aliasManager.getAlias(path.getPath(i - 1));
917
918       if(joinedAliases.size() > 0)
919       {
920          buf.append(SQLUtil.AND);
921       }
922
923       if(cmrField.getRelationMetaData().isForeignKeyMappingStyle())
924       {
925          SQLUtil.getJoinClause(cmrField, parentAlias, childAlias, buf);
926       }
927       else
928       {
929          String JavaDoc relationTableAlias = aliasManager.getRelationTableAlias(path.getPath(i));
930
931          // parent to relation table
932
SQLUtil.getRelationTableJoinClause(cmrField, parentAlias, relationTableAlias, buf)
933             .append(SQLUtil.AND);
934          // child to relation table
935
SQLUtil.getRelationTableJoinClause(cmrField.getRelatedCMRField(), childAlias, relationTableAlias, buf);
936       }
937
938       joinedAliases.add(childAlias);
939    }
940
941
942    public Object JavaDoc visit(ASTCollectionMemberDeclaration node, Object JavaDoc data)
943    {
944       StringBuffer JavaDoc buf = (StringBuffer JavaDoc) data;
945
946       // first arg is a collection valued path
947
ASTPath path = (ASTPath) node.jjtGetChild(0);
948
949       // add this path to the list of declared paths
950
declaredPaths.add(path.getPath());
951
952       // get the entity at the end of this path
953
JDBCEntityBridge entity = (JDBCEntityBridge) path.getEntity();
954
955       // second arg is the identifier
956
ASTIdentifier id = (ASTIdentifier) node.jjtGetChild(1);
957
958       // get the alias
959
String JavaDoc alias = aliasManager.getAlias(id.identifier);
960
961       // add this path to the list of join paths so parent paths will be joined
962
addCollectionMemberJoinPath(alias, path);
963
964       // declare the alias mapping
965
aliasManager.addAlias(path.getPath(), alias);
966
967       buf.append(entity.getQualifiedTableName());
968       buf.append(' ');
969       buf.append(alias);
970       leftJoins(path.getPath(), buf);
971
972       if(onFindCMRJoin != null && alias.equals(selectAlias))
973       {
974          buf.append(onFindCMRJoin);
975          onFindCMRJoin = null;
976       }
977
978       // add the relation-table
979
JDBCCMRFieldBridge cmrField = (JDBCCMRFieldBridge) path.getCMRField();
980       if(cmrField.getRelationMetaData().isTableMappingStyle())
981       {
982          String JavaDoc relationTableAlias = aliasManager.getRelationTableAlias(path.getPath());
983          buf.append(SQLUtil.COMMA)
984             .append(cmrField.getQualifiedTableName())
985             .append(' ')
986             .append(relationTableAlias);
987       }
988
989       return buf;
990    }
991
992    public Object JavaDoc visit(ASTRangeVariableDeclaration node, Object JavaDoc data)
993    {
994       StringBuffer JavaDoc buf = (StringBuffer JavaDoc) data;
995
996       ASTAbstractSchema schema = (ASTAbstractSchema) node.jjtGetChild(0);
997       JDBCEntityBridge entity = (JDBCEntityBridge) schema.entity;
998       ASTIdentifier id = (ASTIdentifier) node.jjtGetChild(1);
999
1000      String JavaDoc alias = aliasManager.getAlias(id.identifier);
1001      buf.append(entity.getQualifiedTableName())
1002         .append(' ')
1003         .append(alias);
1004      leftJoins(id.identifier, buf);
1005
1006      if(onFindCMRJoin != null && alias.equals(selectAlias))
1007      {
1008         buf.append(onFindCMRJoin);
1009         onFindCMRJoin = null;
1010      }
1011
1012      return buf;
1013   }
1014
1015   public Object JavaDoc visit(ASTSelect node, Object JavaDoc data)
1016   {
1017      StringBuffer JavaDoc buf = (StringBuffer JavaDoc) data;
1018
1019      Node child0 = node.jjtGetChild(0);
1020      ASTPath path;
1021      if(child0 instanceof ASTPath)
1022      {
1023         path = (ASTPath) child0;
1024
1025         if(path.isCMPField())
1026         {
1027            // set the select object
1028
JDBCCMPFieldBridge selectField = (JDBCCMPFieldBridge) path.getCMPField();
1029            selectManager = (JDBCStoreManager) selectField.getManager();
1030            selectObject = selectField;
1031            setTypeFactory(selectManager.getJDBCTypeFactory());
1032
1033            addJoinPath(path);
1034            selectAlias = aliasManager.getAlias(path.getPath(path.size() - 2));
1035            SQLUtil.getColumnNamesClause(selectField, selectAlias, buf);
1036         }
1037         else
1038         {
1039            // set the select object
1040
JDBCEntityBridge selectEntity = (JDBCEntityBridge) path.getEntity();
1041            selectManager = (JDBCStoreManager) selectEntity.getManager();
1042            selectObject = selectEntity;
1043            setTypeFactory(selectManager.getJDBCTypeFactory());
1044            selectEntity(path, buf);
1045         }
1046      }
1047      else
1048      {
1049         // the function should take a path expresion as a parameter
1050
path = getPathFromChildren(child0);
1051
1052         if(path == null)
1053         {
1054            throw new IllegalStateException JavaDoc("The function in SELECT clause does not contain a path expression.");
1055         }
1056
1057         if(path.isCMPField())
1058         {
1059            JDBCCMPFieldBridge selectField = (JDBCCMPFieldBridge) path.getCMPField();
1060            selectManager = (JDBCStoreManager) selectField.getManager();
1061         }
1062         else if(path.isCMRField())
1063         {
1064            JDBCCMRFieldBridge cmrField = (JDBCCMRFieldBridge) path.getCMRField();
1065            selectManager = (JDBCStoreManager) cmrField.getEntity().getManager();
1066            addJoinPath(path);
1067         }
1068         else
1069         {
1070            final JDBCEntityBridge entity = (JDBCEntityBridge) path.getEntity();
1071            selectManager = (JDBCStoreManager) entity.getManager();
1072            addJoinPath(path);
1073         }
1074
1075         setTypeFactory(selectManager.getJDBCTypeFactory());
1076         selectObject = child0;
1077         child0.jjtAccept(this, buf);
1078      }
1079
1080      return buf;
1081   }
1082
1083   /**
1084    * Generates where clause without the "WHERE" keyword.
1085    */

1086   public Object JavaDoc visit(ASTWhere node, Object JavaDoc data)
1087   {
1088      node.jjtGetChild(0).jjtAccept(this, data);
1089      return data;
1090   }
1091
1092   public Object JavaDoc visit(ASTNullComparison node, Object JavaDoc data)
1093   {
1094      StringBuffer JavaDoc buf = (StringBuffer JavaDoc) data;
1095
1096      final Node child0 = node.jjtGetChild(0);
1097      if(child0 instanceof ASTPath)
1098      {
1099         ASTPath path = (ASTPath) child0;
1100
1101         if(path.isCMRField())
1102         {
1103            JDBCCMRFieldBridge cmrField = (JDBCCMRFieldBridge) path.getCMRField();
1104            if(cmrField.getRelationMetaData().isTableMappingStyle())
1105            {
1106               existsClause(path, buf, !node.not);
1107               return buf;
1108            }
1109         }
1110
1111         String JavaDoc alias = aliasManager.getAlias(path.getPath(path.size() - 2));
1112         JDBCFieldBridge field = (JDBCFieldBridge) path.getField();
1113
1114         // if jdbc type is null then it should be a cmr field in
1115
// a one-to-one mapping that isn't a foreign key.
1116
// handle it the way the IS EMPTY on the one side of one-to-many
1117
// relationship is handled
1118
if(field.getJDBCType() == null)
1119         {
1120            existsClause(path, buf, !node.not);
1121            return buf;
1122         }
1123
1124         // check the path for cmr fields and add them to join paths
1125
if(path.fieldList.size() > 2)
1126         {
1127            for(int i = 0; i < path.fieldList.size(); ++i)
1128            {
1129               Object JavaDoc pathEl = path.fieldList.get(i);
1130               if(pathEl instanceof JDBCCMRFieldBridge)
1131               {
1132                  addJoinPath(path);
1133                  break;
1134               }
1135            }
1136         }
1137
1138         buf = SQLUtil.getIsNullClause(node.not, field, alias, buf);
1139      }
1140      else if(child0 instanceof ASTParameter)
1141      {
1142         ASTParameter param = (ASTParameter) child0;
1143         Class JavaDoc type = getParameterType(param.number);
1144
1145         QueryParameter queryParam = new QueryParameter(param.number - 1, typeFactory.getJDBCType(type));
1146         inputParameters.add(queryParam);
1147
1148         buf.append("? IS ");
1149         if(node.not)
1150         {
1151            buf.append(SQLUtil.NOT);
1152         }
1153         buf.append(SQLUtil.NULL);
1154      }
1155      else
1156      {
1157         throw new IllegalStateException JavaDoc("Unexpected node in IS NULL clause: " + node);
1158      }
1159
1160      return buf;
1161   }
1162
1163   public Object JavaDoc visit(ASTIsEmpty node, Object JavaDoc data)
1164   {
1165      StringBuffer JavaDoc buf = (StringBuffer JavaDoc) data;
1166      ASTPath path = (ASTPath) node.jjtGetChild(0);
1167
1168      existsClause(path, buf, !node.not);
1169      return buf;
1170   }
1171
1172   /**
1173    * Compare entity
1174    */

1175   public Object JavaDoc visit(ASTMemberOf node, Object JavaDoc data)
1176   {
1177      StringBuffer JavaDoc buf = (StringBuffer JavaDoc) data;
1178
1179      // setup compare to vars first, so we can compre types in from vars
1180
ASTPath toPath = (ASTPath) node.jjtGetChild(1);
1181
1182      JDBCCMRFieldBridge toCMRField = (JDBCCMRFieldBridge) toPath.getCMRField();
1183
1184      JDBCEntityBridge toChildEntity = (JDBCEntityBridge) toPath.getEntity();
1185
1186      String JavaDoc pathStr = toPath.getPath(toPath.size() - 2);
1187      String JavaDoc toParentAlias = aliasManager.getAlias(pathStr);
1188      String JavaDoc toChildAlias = aliasManager.getAlias(toPath.getPath());
1189      String JavaDoc relationTableAlias = null;
1190      if(toCMRField.getRelationMetaData().isTableMappingStyle())
1191      {
1192         relationTableAlias = aliasManager.getRelationTableAlias(toPath.getPath());
1193      }
1194
1195      // setup from variables
1196
String JavaDoc fromAlias = null;
1197      int fromParamNumber = -1;
1198      if(node.jjtGetChild(0) instanceof ASTParameter)
1199      {
1200         ASTParameter fromParam = (ASTParameter) node.jjtGetChild(0);
1201
1202         // can only compare like kind entities
1203
verifyParameterEntityType(fromParam.number, toChildEntity);
1204
1205         fromParamNumber = fromParam.number;
1206      }
1207      else
1208      {
1209         ASTPath fromPath = (ASTPath) node.jjtGetChild(0);
1210         addJoinPath(fromPath);
1211
1212         JDBCEntityBridge fromEntity = (JDBCEntityBridge) fromPath.getEntity();
1213         fromAlias = aliasManager.getAlias(fromPath.getPath());
1214
1215         // can only compare like kind entities
1216
if(!fromEntity.equals(toChildEntity))
1217         {
1218            throw new IllegalStateException JavaDoc("Only like types can be " +
1219               "compared: from entity=" +
1220               fromEntity.getEntityName() +
1221               " to entity=" + toChildEntity.getEntityName());
1222         }
1223      }
1224
1225      // add the path to the list of paths to left join
1226
addLeftJoinPath(pathStr, toPath);
1227
1228      // first part makes toChild not in toParent.child
1229
if(!subquerySupported)
1230      {
1231         addJoinPath(toPath);
1232
1233         // subquery not supported; use a left join and is not null
1234
if(node.not)
1235         {
1236            buf.append(SQLUtil.NOT);
1237         }
1238         buf.append('(');
1239
1240         if(relationTableAlias == null)
1241         {
1242            SQLUtil.getIsNullClause(true, toChildEntity.getPrimaryKeyFields(), toChildAlias, buf);
1243         }
1244         else
1245         {
1246            SQLUtil.getIsNullClause(true, toCMRField.getTableKeyFields(), relationTableAlias, buf);
1247         }
1248      }
1249      else
1250      {
1251         // subquery supported; use exists subquery
1252
if(node.not)
1253         {
1254            buf.append(SQLUtil.NOT);
1255         }
1256
1257         buf.append(SQLUtil.EXISTS).append('(');
1258
1259         if(relationTableAlias == null)
1260         {
1261            buf.append(SQLUtil.SELECT);
1262            SQLUtil.getColumnNamesClause(toChildEntity.getPrimaryKeyFields(), toChildAlias, buf)
1263               .append(SQLUtil.FROM)
1264               .append(toChildEntity.getQualifiedTableName())
1265               .append(' ')
1266               .append(toChildAlias)
1267               .append(SQLUtil.WHERE);
1268            SQLUtil.getJoinClause(toCMRField, toParentAlias, toChildAlias, buf);
1269         }
1270         else
1271         {
1272            buf.append(SQLUtil.SELECT);
1273            SQLUtil.getColumnNamesClause(toCMRField.getRelatedCMRField().getTableKeyFields(), relationTableAlias, buf)
1274               .append(SQLUtil.FROM)
1275               .append(toCMRField.getQualifiedTableName())
1276               .append(' ')
1277               .append(relationTableAlias)
1278               .append(SQLUtil.WHERE);
1279            SQLUtil.getRelationTableJoinClause(toCMRField, toParentAlias, relationTableAlias, buf);
1280         }
1281      }
1282
1283      buf.append(SQLUtil.AND);
1284
1285      // second part makes fromNode equal toChild
1286
if(fromAlias != null)
1287      {
1288         // compre pk to pk
1289
if(relationTableAlias == null)
1290         {
1291            SQLUtil.getSelfCompareWhereClause(toChildEntity.getPrimaryKeyFields(),
1292               toChildAlias,
1293               fromAlias,
1294               buf);
1295         }
1296         else
1297         {
1298            SQLUtil.getRelationTableJoinClause(toCMRField.getRelatedCMRField(),
1299               fromAlias,
1300               relationTableAlias,
1301               buf);
1302         }
1303      }
1304      else
1305      {
1306         // add the parameters
1307
inputParameters.addAll(QueryParameter.createParameters(fromParamNumber - 1,
1308            toChildEntity));
1309
1310         // compare pk to parameter
1311
if(relationTableAlias == null)
1312         {
1313            SQLUtil.getWhereClause(toChildEntity.getPrimaryKeyFields(), toChildAlias, buf);
1314         }
1315         else
1316         {
1317            SQLUtil.getWhereClause(toCMRField.getRelatedCMRField().getTableKeyFields(),
1318               relationTableAlias,
1319               buf);
1320         }
1321      }
1322
1323      buf.append(')');
1324
1325      return buf;
1326   }
1327
1328   public Object JavaDoc visit(ASTValueClassComparison node, Object JavaDoc data)
1329   {
1330      StringBuffer JavaDoc buf = (StringBuffer JavaDoc) data;
1331
1332      boolean not = (node.opp.equals(SQLUtil.NOT_EQUAL));
1333      String JavaDoc comparison = node.opp;
1334      buf.append('(');
1335      if(not)
1336      {
1337         buf.append(SQLUtil.NOT).append('(');
1338         comparison = "=";
1339      }
1340
1341      // setup the from path
1342
ASTPath fromPath = (ASTPath) node.jjtGetChild(0);
1343      addJoinPath(fromPath);
1344      String JavaDoc fromAlias = aliasManager.getAlias(fromPath.getPath(fromPath.size() - 2));
1345      JDBCCMPFieldBridge fromCMPField = (JDBCCMPFieldBridge) fromPath.getCMPField();
1346
1347      Node toNode = node.jjtGetChild(1);
1348      if(toNode instanceof ASTParameter)
1349      {
1350         ASTParameter toParam = (ASTParameter) toNode;
1351
1352         // can only compare like kind entities
1353
Class JavaDoc parameterType = getParameterType(toParam.number);
1354         if(!(fromCMPField.getFieldType().equals(parameterType)))
1355         {
1356            throw new IllegalStateException JavaDoc("Only like types can be " +
1357               "compared: from CMP field=" +
1358               fromCMPField.getFieldType() +
1359               " to parameter=" + parameterType);
1360         }
1361
1362         inputParameters.addAll(QueryParameter.createParameters(toParam.number - 1, fromCMPField));
1363         SQLUtil.getWhereClause(fromCMPField.getJDBCType(), fromAlias, comparison, buf);
1364      }
1365      else
1366      {
1367         ASTPath toPath = (ASTPath) toNode;
1368         addJoinPath(toPath);
1369         String JavaDoc toAlias = aliasManager.getAlias(toPath.getPath(toPath.size() - 2));
1370         JDBCCMPFieldBridge toCMPField = (JDBCCMPFieldBridge) toPath.getCMPField();
1371
1372         // can only compare like kind entities
1373
if(!(fromCMPField.getFieldType().equals(toCMPField.getFieldType())))
1374         {
1375            throw new IllegalStateException JavaDoc("Only like types can be " +
1376               "compared: from CMP field=" +
1377               fromCMPField.getFieldType() +
1378               " to CMP field=" + toCMPField.getFieldType());
1379         }
1380
1381         SQLUtil.getSelfCompareWhereClause(fromCMPField, toCMPField, fromAlias, toAlias, comparison, buf);
1382      }
1383
1384      return (not ? buf.append(')') : buf).append(')');
1385   }
1386
1387   /**
1388    * compreEntity(arg0, arg1)
1389    */

1390   public Object JavaDoc visit(ASTEntityComparison node, Object JavaDoc data)
1391   {
1392      StringBuffer JavaDoc buf = (StringBuffer JavaDoc) data;
1393      Node arg0 = node.jjtGetChild(0);
1394      Node arg1 = node.jjtGetChild(1);
1395      if(node.opp.equals(SQLUtil.NOT_EQUAL))
1396      {
1397         compareEntity(true, arg0, arg1, buf);
1398      }
1399      else
1400      {
1401         compareEntity(false, arg0, arg1, buf);
1402      }
1403      return buf;
1404   }
1405
1406   /**
1407    * Type-mapping function translation
1408    */

1409   public Object JavaDoc visit(ASTConcat node, Object JavaDoc data)
1410   {
1411      StringBuffer JavaDoc buf = (StringBuffer JavaDoc) data;
1412      JDBCFunctionMappingMetaData function = typeMapping.getFunctionMapping(JDBCTypeMappingMetaData.CONCAT);
1413      Object JavaDoc[] args = new Object JavaDoc[]{
1414         new NodeStringWrapper(node.jjtGetChild(0)),
1415         new NodeStringWrapper(node.jjtGetChild(1)),
1416      };
1417      function.getFunctionSql(args, buf);
1418      return buf;
1419   }
1420
1421   /**
1422    * Type-mapping function translation
1423    */

1424   public Object JavaDoc visit(ASTSubstring node, Object JavaDoc data)
1425   {
1426      StringBuffer JavaDoc buf = (StringBuffer JavaDoc) data;
1427      JDBCFunctionMappingMetaData function = typeMapping.getFunctionMapping(JDBCTypeMappingMetaData.SUBSTRING);
1428      Object JavaDoc[] args = new Object JavaDoc[]{
1429         new NodeStringWrapper(node.jjtGetChild(0)),
1430         new NodeStringWrapper(node.jjtGetChild(1)),
1431         new NodeStringWrapper(node.jjtGetChild(2)),
1432      };
1433      function.getFunctionSql(args, buf);
1434      return buf;
1435   }
1436
1437   /**
1438    * Type-mapping function translation
1439    */

1440   public Object JavaDoc visit(ASTLCase node, Object JavaDoc data)
1441   {
1442      StringBuffer JavaDoc buf = (StringBuffer JavaDoc) data;
1443      JDBCFunctionMappingMetaData function = typeMapping.getFunctionMapping(JDBCTypeMappingMetaData.LCASE);
1444      Object JavaDoc[] args = new Object JavaDoc[]{
1445         new NodeStringWrapper(node.jjtGetChild(0)),
1446      };
1447      function.getFunctionSql(args, buf);
1448      return buf;
1449   }
1450
1451   /**
1452    * Type-mapping function translation
1453    */

1454   public Object JavaDoc visit(ASTUCase node, Object JavaDoc data)
1455   {
1456      StringBuffer JavaDoc buf = (StringBuffer JavaDoc) data;
1457      JDBCFunctionMappingMetaData function = typeMapping.getFunctionMapping(JDBCTypeMappingMetaData.UCASE);
1458      Object JavaDoc[] args = new Object JavaDoc[]{
1459         new NodeStringWrapper(node.jjtGetChild(0)),
1460      };
1461      function.getFunctionSql(args, buf);
1462      return buf;
1463   }
1464
1465   /**
1466    * Type-mapping function translation
1467    */

1468   public Object JavaDoc visit(ASTLength node, Object JavaDoc data)
1469   {
1470      StringBuffer JavaDoc buf = (StringBuffer JavaDoc) data;
1471      JDBCFunctionMappingMetaData function = typeMapping.getFunctionMapping(JDBCTypeMappingMetaData.LENGTH);
1472      Object JavaDoc[] args = new Object JavaDoc[]{
1473         new NodeStringWrapper(node.jjtGetChild(0)),
1474      };
1475      function.getFunctionSql(args, buf);
1476      return buf;
1477   }
1478
1479   /**
1480    * Type-mapping function translation
1481    */

1482   public Object JavaDoc visit(ASTLocate node, Object JavaDoc data)
1483   {
1484      StringBuffer JavaDoc buf = (StringBuffer JavaDoc) data;
1485
1486      JDBCFunctionMappingMetaData function = typeMapping.getFunctionMapping(JDBCTypeMappingMetaData.LOCATE);
1487      Object JavaDoc[] args = new Object JavaDoc[3];
1488      args[0] = new NodeStringWrapper(node.jjtGetChild(0));
1489      args[1] = new NodeStringWrapper(node.jjtGetChild(1));
1490      if(node.jjtGetNumChildren() == 3)
1491      {
1492         args[2] = new NodeStringWrapper(node.jjtGetChild(2));
1493      }
1494      else
1495      {
1496         args[2] = "1";
1497      }
1498
1499      // add the sql to the current buffer
1500
function.getFunctionSql(args, buf);
1501      return buf;
1502   }
1503
1504   /**
1505    * Type-mapping function translation
1506    */

1507   public Object JavaDoc visit(ASTAbs node, Object JavaDoc data)
1508   {
1509      StringBuffer JavaDoc buf = (StringBuffer JavaDoc) data;
1510      JDBCFunctionMappingMetaData function = typeMapping.getFunctionMapping(JDBCTypeMappingMetaData.ABS);
1511      Object JavaDoc[] args = new Object JavaDoc[]{
1512         new NodeStringWrapper(node.jjtGetChild(0)),
1513      };
1514      function.getFunctionSql(args, buf);
1515      return buf;
1516   }
1517
1518   /**
1519    * Type-mapping function translation
1520    */

1521   public Object JavaDoc visit(ASTMod node, Object JavaDoc data)
1522   {
1523      StringBuffer JavaDoc buf = (StringBuffer JavaDoc) data;
1524      //JDBCFunctionMappingMetaData function = typeMapping.getFunctionMapping(JDBCTypeMappingMetaData.MOD);
1525
JDBCFunctionMappingMetaData function = JDBCTypeMappingMetaData.MOD_FUNC;
1526      Object JavaDoc[] args = new Object JavaDoc[]{
1527         new NodeStringWrapper(node.jjtGetChild(0)),
1528         new NodeStringWrapper(node.jjtGetChild(1)),
1529      };
1530      function.getFunctionSql(args, buf);
1531      return buf;
1532   }
1533
1534   /**
1535    * Type-mapping function translation
1536    */

1537   public Object JavaDoc visit(ASTSqrt node, Object JavaDoc data)
1538   {
1539      StringBuffer JavaDoc buf = (StringBuffer JavaDoc) data;
1540      JDBCFunctionMappingMetaData function = typeMapping.getFunctionMapping(JDBCTypeMappingMetaData.SQRT);
1541      Object JavaDoc[] args = new Object JavaDoc[]{
1542         new NodeStringWrapper(node.jjtGetChild(0))
1543      };
1544      function.getFunctionSql(args, buf);
1545      return buf;
1546   }
1547
1548   public Object JavaDoc visit(ASTCount node, Object JavaDoc data)
1549   {
1550      StringBuffer JavaDoc buf = (StringBuffer JavaDoc) data;
1551      node.setResultType(returnType);
1552
1553      Object JavaDoc args[];
1554      final ASTPath cntPath = (ASTPath) node.jjtGetChild(0);
1555      if(cntPath.isCMPField())
1556      {
1557         args = new Object JavaDoc[]{node.distinct, new NodeStringWrapper(cntPath)};
1558      }
1559      else
1560      {
1561         JDBCEntityBridge entity = (JDBCEntityBridge) cntPath.getEntity();
1562         final JDBCFieldBridge[] pkFields = entity.getPrimaryKeyFields();
1563         if(pkFields.length > 1)
1564         {
1565            countCompositePk = true;
1566            forceDistinct = node.distinct.length() > 0;
1567            selectEntity(cntPath, buf);
1568            return buf;
1569         }
1570         else
1571         {
1572
1573            final String JavaDoc alias = aliasManager.getAlias(cntPath.getPath());
1574            StringBuffer JavaDoc keyColumn = new StringBuffer JavaDoc(20);
1575            SQLUtil.getColumnNamesClause(pkFields[0], alias, keyColumn);
1576            args = new Object JavaDoc[]{node.distinct, keyColumn.toString()};
1577         }
1578      }
1579
1580      return JDBCTypeMappingMetaData.COUNT_FUNC.getFunctionSql(args, buf);
1581   }
1582
1583   public Object JavaDoc visit(ASTMax node, Object JavaDoc data)
1584   {
1585      node.setResultType(returnType);
1586      StringBuffer JavaDoc buf = (StringBuffer JavaDoc) data;
1587      Object JavaDoc[] args = new Object JavaDoc[]{
1588         node.distinct,
1589         new NodeStringWrapper(node.jjtGetChild(0))
1590      };
1591      return JDBCTypeMappingMetaData.MAX_FUNC.getFunctionSql(args, buf);
1592   }
1593
1594   public Object JavaDoc visit(ASTMin node, Object JavaDoc data)
1595   {
1596      node.setResultType(returnType);
1597      StringBuffer JavaDoc buf = (StringBuffer JavaDoc) data;
1598      Object JavaDoc[] args = new Object JavaDoc[]{
1599         node.distinct,
1600         new NodeStringWrapper(node.jjtGetChild(0))
1601      };
1602      return JDBCTypeMappingMetaData.MIN_FUNC.getFunctionSql(args, buf);
1603   }
1604
1605   public Object JavaDoc visit(ASTAvg node, Object JavaDoc data)
1606   {
1607      node.setResultType(returnType);
1608      StringBuffer JavaDoc buf = (StringBuffer JavaDoc) data;
1609      Object JavaDoc[] args = new Object JavaDoc[]{
1610         node.distinct,
1611         new NodeStringWrapper(node.jjtGetChild(0)),
1612      };
1613      return JDBCTypeMappingMetaData.AVG_FUNC.getFunctionSql(args, buf);
1614   }
1615
1616   public Object JavaDoc visit(ASTSum node, Object JavaDoc data)
1617   {
1618      node.setResultType(returnType);
1619      StringBuffer JavaDoc buf = (StringBuffer JavaDoc) data;
1620      Object JavaDoc[] args = new Object JavaDoc[]{
1621         node.distinct,
1622         new NodeStringWrapper(node.jjtGetChild(0))
1623      };
1624      return JDBCTypeMappingMetaData.SUM_FUNC.getFunctionSql(args, buf);
1625   }
1626
1627   /**
1628    * tableAlias.columnName
1629    */

1630   public Object JavaDoc visit(ASTPath node, Object JavaDoc data)
1631   {
1632      StringBuffer JavaDoc buf = (StringBuffer JavaDoc) data;
1633      if(!node.isCMPField())
1634      {
1635         throw new IllegalStateException JavaDoc("Can only visit cmp valued path " +
1636            "node. Should have been handled at a higher level.");
1637      }
1638
1639      JDBCCMPFieldBridge cmpField = (JDBCCMPFieldBridge) node.getCMPField();
1640
1641      // make sure this is mapped to a single column
1642
switch(node.type)
1643      {
1644         case EJBQLTypes.ENTITY_TYPE:
1645         case EJBQLTypes.VALUE_CLASS_TYPE:
1646            if(cmpField.getJDBCType().hasMapper())
1647            {
1648               break;
1649            }
1650         case EJBQLTypes.UNKNOWN_TYPE:
1651            throw new IllegalStateException JavaDoc("Can not visit multi-column path " +
1652               "node. Should have been handled at a higher level.");
1653      }
1654
1655      addJoinPath(node);
1656      String JavaDoc alias = aliasManager.getAlias(node.getPath(node.size() - 2));
1657      SQLUtil.getColumnNamesClause(cmpField, alias, buf);
1658      return buf;
1659   }
1660
1661   public Object JavaDoc visit(ASTAbstractSchema node, Object JavaDoc data)
1662   {
1663      throw new IllegalStateException JavaDoc("Can not visit abstract schema node. " +
1664         "Should have been handled at a higher level.");
1665   }
1666
1667   /**
1668    * ?
1669    */

1670   public Object JavaDoc visit(ASTParameter node, Object JavaDoc data)
1671   {
1672      StringBuffer JavaDoc buf = (StringBuffer JavaDoc) data;
1673      Class JavaDoc type = getParameterType(node.number);
1674
1675      // make sure this is mapped to a single column
1676
int ejbqlType = EJBQLTypes.getEJBQLType(type);
1677      if(ejbqlType == EJBQLTypes.ENTITY_TYPE ||
1678         ejbqlType == EJBQLTypes.VALUE_CLASS_TYPE ||
1679         ejbqlType == EJBQLTypes.UNKNOWN_TYPE)
1680      {
1681         throw new IllegalStateException JavaDoc("Can not visit multi-column " +
1682            "parameter node. Should have been handled at a higher level.");
1683      }
1684
1685      QueryParameter param = new QueryParameter(node.number - 1, typeFactory.getJDBCType(type));
1686      inputParameters.add(param);
1687      buf.append('?');
1688      return buf;
1689   }
1690
1691   /**
1692    * typeMapping.get<True/False>Mapping()
1693    */

1694   public Object JavaDoc visit(ASTBooleanLiteral node, Object JavaDoc data)
1695   {
1696      StringBuffer JavaDoc buf = (StringBuffer JavaDoc) data;
1697      if(node.value)
1698      {
1699         buf.append(typeMapping.getTrueMapping());
1700      }
1701      else
1702      {
1703         buf.append(typeMapping.getFalseMapping());
1704      }
1705      return data;
1706   }
1707
1708   public Object JavaDoc visit(ASTLimitOffset node, Object JavaDoc data)
1709   {
1710      int child = 0;
1711      if(node.hasOffset)
1712      {
1713         Node offsetNode = node.jjtGetChild(child++);
1714         if(offsetNode instanceof ASTParameter)
1715         {
1716            ASTParameter param = (ASTParameter) offsetNode;
1717            Class JavaDoc parameterType = getParameterType(param.number);
1718            if(int.class != parameterType && Integer JavaDoc.class != parameterType)
1719            {
1720               throw new UnsupportedOperationException JavaDoc("OFFSET parameter must be an int");
1721            }
1722            offsetParam = param.number;
1723         }
1724         else
1725         {
1726            ASTExactNumericLiteral param = (ASTExactNumericLiteral) offsetNode;
1727            offsetValue = (int) param.value;
1728         }
1729      }
1730      if(node.hasLimit)
1731      {
1732         Node limitNode = node.jjtGetChild(child);
1733         if(limitNode instanceof ASTParameter)
1734         {
1735            ASTParameter param = (ASTParameter) limitNode;
1736            Class JavaDoc parameterType = getParameterType(param.number);
1737            if(int.class != parameterType && Integer JavaDoc.class != parameterType)
1738            {
1739               throw new UnsupportedOperationException JavaDoc("LIMIT parameter must be an int");
1740            }
1741            limitParam = param.number;
1742         }
1743         else
1744         {
1745            ASTExactNumericLiteral param = (ASTExactNumericLiteral) limitNode;
1746            limitValue = (int) param.value;
1747         }
1748      }
1749      return data;
1750   }
1751
1752   public Object JavaDoc visit(ASTWhereConditionalTerm node, Object JavaDoc data)
1753   {
1754      // clear per term paths
1755
clearPerTermJoinPaths();
1756
1757      StringBuffer JavaDoc buf = (StringBuffer JavaDoc) data;
1758      buf.append('(');
1759      for(int i = 0; i < node.jjtGetNumChildren(); ++i)
1760      {
1761         node.jjtGetChild(i).jjtAccept(this, data);
1762      }
1763
1764      StringBuffer JavaDoc thetaJoin = new StringBuffer JavaDoc();
1765      createThetaJoin(thetaJoin);
1766
1767      if(thetaJoin.length() > 0)
1768      {
1769         buf.append(SQLUtil.AND).append(thetaJoin.toString());
1770      }
1771
1772      buf.append(')');
1773      return data;
1774   }
1775
1776   /**
1777    * Wrap a node with a class that when ever toString is called visits the
1778    * node. This is used by the function implmentations, for parameters.
1779    * <p/>
1780    * Be careful with this class because it visits the node for each call of
1781    * toString, which could have undesireable result if called multiple times.
1782    */

1783   private final class NodeStringWrapper
1784   {
1785      final Node node;
1786
1787      public NodeStringWrapper(Node node)
1788      {
1789         this.node = node;
1790      }
1791
1792      public String JavaDoc toString()
1793      {
1794         return node.jjtAccept(JDBCEJBQLCompiler.this, new StringBuffer JavaDoc()).toString();
1795      }
1796   }
1797
1798   /**
1799    * Recursively searches for ASTPath among children.
1800    *
1801    * @param selectFunction a node implements SelectFunction
1802    * @return ASTPath child or null if there was no child of type ASTPath
1803    */

1804   private ASTPath getPathFromChildren(Node selectFunction)
1805   {
1806      for(int childInd = 0; childInd < selectFunction.jjtGetNumChildren(); ++childInd)
1807      {
1808         Node child = selectFunction.jjtGetChild(childInd);
1809         if(child instanceof ASTPath)
1810         {
1811            return (ASTPath) child;
1812         }
1813         else if(child instanceof SelectFunction)
1814         {
1815            Node path = getPathFromChildren(child);
1816            if(path != null)
1817            {
1818               return (ASTPath) path;
1819            }
1820         }
1821      }
1822      return null;
1823   }
1824
1825   /**
1826    * Checks whether the path passed in is already in the SELECT clause.
1827    *
1828    * @param path the path to check.
1829    * @return true if the path is already in the SELECT clause.
1830    */

1831   private boolean isSelected(ASTPath path)
1832   {
1833      boolean selected = false;
1834
1835      CMPFieldBridge cmpField = path.getCMPField();
1836      if(selectObject instanceof JDBCCMPFieldBridge && cmpField == selectObject)
1837      {
1838         selected = true;
1839      }
1840      else if(selectObject instanceof JDBCEntityBridge)
1841      {
1842         JDBCEntityBridge entity = (JDBCEntityBridge) selectObject;
1843         JDBCFieldBridge[] pkFields = entity.getPrimaryKeyFields();
1844         for(int pkInd = 0; pkInd < pkFields.length; ++pkInd)
1845         {
1846            if(pkFields[pkInd] == cmpField)
1847            {
1848               selected = true;
1849               break;
1850            }
1851         }
1852      }
1853      else if(selectObject instanceof SelectFunction)
1854      {
1855         Node funcNode = (Node) selectObject;
1856         ASTPath fieldPath = getPathFromChildren(funcNode);
1857         if(fieldPath.getCMPField() == cmpField)
1858         {
1859            selected = true;
1860         }
1861      }
1862
1863      return selected;
1864   }
1865
1866   private void selectEntity(ASTPath path, StringBuffer JavaDoc buf)
1867   {
1868      JDBCEntityBridge selectEntity = (JDBCEntityBridge) path.getEntity();
1869
1870      StringBuffer JavaDoc columnNamesClause = new StringBuffer JavaDoc(200);
1871      addJoinPath(path);
1872      selectAlias = aliasManager.getAlias(path.getPath());
1873
1874      // get a list of all fields to be loaded
1875
// get the identifier for this field
1876
SQLUtil.getColumnNamesClause(selectEntity.getPrimaryKeyFields(), selectAlias, columnNamesClause);
1877
1878      if(readAhead.isOnFind())
1879      {
1880         String JavaDoc eagerLoadGroupName = readAhead.getEagerLoadGroup();
1881         boolean[] loadGroupMask = selectEntity.getLoadGroupMask(eagerLoadGroupName);
1882         SQLUtil.appendColumnNamesClause(selectEntity.getTableFields(), loadGroupMask, selectAlias, columnNamesClause);
1883
1884         try
1885         {
1886            leftJoinCMRList = JDBCAbstractQueryCommand.getLeftJoinCMRNodes(
1887               selectEntity, path.getPath(), readAhead.getLeftJoins(), declaredPaths);
1888         }
1889         catch(DeploymentException e)
1890         {
1891            throw new IllegalStateException JavaDoc(e.getMessage());
1892         }
1893
1894         if(!leftJoinCMRList.isEmpty())
1895         {
1896            onFindCMRJoin = new StringBuffer JavaDoc(100);
1897            JDBCAbstractQueryCommand.leftJoinCMRNodes(selectAlias, leftJoinCMRList, aliasManager, onFindCMRJoin);
1898            JDBCAbstractQueryCommand.appendLeftJoinCMRColumnNames(leftJoinCMRList, aliasManager, columnNamesClause);
1899         }
1900      }
1901      buf.append(columnNamesClause);
1902   }
1903
1904   private void addJoinPath(ASTPath path)
1905   {
1906      ctermJoinPaths.add(path);
1907      allJoinPaths.add(path);
1908   }
1909
1910   private void addCollectionMemberJoinPath(String JavaDoc alias, ASTPath path)
1911   {
1912      ctermCollectionMemberJoinPaths.put(alias, path);
1913      allCollectionMemberJoinPaths.put(alias, path);
1914   }
1915
1916   private void addLeftJoinPath(String JavaDoc pathStr, ASTPath path)
1917   {
1918      Set JavaDoc set = (Set JavaDoc) ctermLeftJoinPaths.get(pathStr);
1919      if(set == null)
1920      {
1921         set = new HashSet JavaDoc();
1922         ctermLeftJoinPaths.put(pathStr, set);
1923      }
1924      set.add(path);
1925
1926      set = (Set JavaDoc) allLeftJoinPaths.get(pathStr);
1927      if(set == null)
1928      {
1929         set = new HashSet JavaDoc();
1930         allLeftJoinPaths.put(pathStr, set);
1931      }
1932      set.add(path);
1933   }
1934
1935   private void clearPerTermJoinPaths()
1936   {
1937      ctermJoinPaths.clear();
1938      ctermCollectionMemberJoinPaths.clear();
1939      ctermLeftJoinPaths.clear();
1940   }
1941
1942   private boolean isDistinct(Node selectNode)
1943   {
1944      return ((ASTSelect) selectNode).distinct || returnType.equals(Set JavaDoc.class) || forceDistinct;
1945   }
1946}
1947
Popular Tags