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