KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > oracle > toplink > essentials > internal > expressions > SQLSelectStatement


1 /*
2  * The contents of this file are subject to the terms
3  * of the Common Development and Distribution License
4  * (the "License"). You may not use this file except
5  * in compliance with the License.
6  *
7  * You can obtain a copy of the license at
8  * glassfish/bootstrap/legal/CDDLv1.0.txt or
9  * https://glassfish.dev.java.net/public/CDDLv1.0.html.
10  * See the License for the specific language governing
11  * permissions and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL
14  * HEADER in each file and include the License file at
15  * glassfish/bootstrap/legal/CDDLv1.0.txt. If applicable,
16  * add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your
18  * own identifying information: Portions Copyright [yyyy]
19  * [name of copyright owner]
20  */

21
22 // Copyright (c) 1998, 2006, Oracle. All rights reserved.
23
package oracle.toplink.essentials.internal.expressions;
24
25 import java.io.*;
26 import java.util.*;
27 import oracle.toplink.essentials.exceptions.*;
28 import oracle.toplink.essentials.expressions.*;
29 import oracle.toplink.essentials.internal.databaseaccess.*;
30 import oracle.toplink.essentials.internal.helper.*;
31 import oracle.toplink.essentials.mappings.*;
32 import oracle.toplink.essentials.queryframework.*;
33 import oracle.toplink.essentials.platform.database.DB2MainframePlatform;
34 import oracle.toplink.essentials.internal.sessions.AbstractSession;
35 import oracle.toplink.essentials.descriptors.ClassDescriptor;
36
37 /**
38  * <p><b>Purpose</b>: Print SELECT statement.
39  * <p><b>Responsibilities</b>:<ul>
40  * <li> Print SELECT statement.
41  * </ul>
42  * @author Dorin Sandu
43  * @since TOPLink/Java 1.0
44  */

45 public class SQLSelectStatement extends SQLStatement {
46
47     /** Fields being selected (can include expressions). */
48     protected Vector fields;
49     
50     /** Fields not being selected (can include expressions). */
51     protected Vector nonSelectFields;
52
53     /** Tables being selected from. */
54     protected Vector tables;
55
56     /** Used for "Select Distinct" option. */
57     protected short distinctState;
58
59     /** Order by clause for read all queries. */
60     protected Vector orderByExpressions;
61
62     /** Group by clause for report queries. */
63     protected Vector groupByExpressions;
64
65     /** Having clause for report queries. */
66     protected Expression havingExpression;
67
68     /** Used for pessimistic locking ie. "For Update". */
69     protected ForUpdateClause forUpdateClause;
70
71     /** Used for report query or counts so we know how to treat distincts. */
72     protected boolean isAggregateSelect;
73
74     /** Used for DB2 style from clause outer joins. */
75     protected Vector outerJoinedExpressions;
76     protected Vector outerJoinedMappingCriteria;
77     protected Vector outerJoinedAdditionalJoinCriteria;
78     // used in case no corresponding outerJoinExpression is provided -
79
// only multitable inheritance should be outer joined
80
protected List descriptorsForMultitableInheritanceOnly;
81
82     /** Used for Oracle Hierarchical Queries */
83     protected Expression startWithExpression;
84     protected Expression connectByExpression;
85     protected Vector orderSiblingsByExpressions;
86
87     /** Variables used for aliasing and normalizing. */
88     protected boolean requiresAliases;
89     protected Hashtable tableAliases;
90     protected DatabaseTable lastTable;
91     protected DatabaseTable currentAlias;
92     protected int currentAliasNumber;
93
94     /** Used for subselects. */
95     protected SQLSelectStatement parentStatement;
96
97     public SQLSelectStatement() {
98         this.fields = oracle.toplink.essentials.internal.helper.NonSynchronizedVector.newInstance(2);
99         this.tables = oracle.toplink.essentials.internal.helper.NonSynchronizedVector.newInstance(4);
100         this.requiresAliases = false;
101         this.isAggregateSelect = false;
102         this.distinctState = ObjectLevelReadQuery.UNCOMPUTED_DISTINCT;
103         this.currentAliasNumber = 0;
104     }
105
106     public void addField(DatabaseField field) {
107         getFields().addElement(field);
108     }
109
110     /**
111      * INTERNAL: adds an expression to the fields. set a flag if the expression
112      * is for and aggregate function.
113      */

114     public void addField(Expression expression) {
115         if (expression instanceof FunctionExpression) {
116             if (((FunctionExpression)expression).getOperator().isAggregateOperator()) {
117                 setIsAggregateSelect(true);
118             }
119         }
120
121         getFields().addElement(expression);
122     }
123
124     /**
125      * When distinct is used with order by the ordered fields must be in the select clause.
126      */

127     protected void addOrderByExpressionToSelectForDistinct() {
128         for (Enumeration orderExpressionsEnum = getOrderByExpressions().elements();
129                  orderExpressionsEnum.hasMoreElements();) {
130             Expression orderExpression = (Expression)orderExpressionsEnum.nextElement();
131             Expression fieldExpression = null;
132
133             if (orderExpression.isFunctionExpression() && (orderExpression.getOperator().isOrderOperator())) {
134                 fieldExpression = ((FunctionExpression)orderExpression).getBaseExpression();
135             } else {
136                 fieldExpression = orderExpression;
137             }
138
139             // Changed to call a method to loop throught the fields vector and check each element
140
// individually. Jon D. May 4, 2000 for pr 7811
141
if ((fieldExpression.selectIfOrderedBy()) && !fieldsContainField(getFields(), fieldExpression)) {
142                 addField(fieldExpression);
143             }
144         }
145     }
146
147     /**
148      * Add a table to the statement. The table will
149      * be used in the FROM part of the SQL statement.
150      */

151     public void addTable(DatabaseTable table) {
152         if (!getTables().contains(table)) {
153             getTables().addElement(table);
154         }
155     }
156
157     /**
158      * ADVANCED:
159      * If a platform is Informix, then the outer join must be in the FROM clause.
160      * This is used internally by TopLink for building Informix outer join syntax which differs from
161      * other platforms(Oracle,Sybase) that print the outer join in the WHERE clause and from DB2 which prints
162      * the.
163      * OuterJoinedAliases passed in to keep track of tables used for outer join so no normal join is given
164      */

165     public void appendFromClauseForInformixOuterJoin(ExpressionSQLPrinter printer, Vector outerJoinedAliases) throws IOException {
166         Writer writer = printer.getWriter();
167         AbstractSession session = printer.getSession();
168
169         // Print outer joins
170
boolean firstTable = true;
171
172         for (int index = 0; index < getOuterJoinExpressions().size(); index++) {
173             QueryKeyExpression outerExpression = (QueryKeyExpression)getOuterJoinExpressions().elementAt(index);
174             CompoundExpression relationExpression = (CompoundExpression)getOuterJoinedMappingCriteria().elementAt(index);// get expression for multiple table case
175

176             // CR#3083929 direct collection/map mappings do not have reference descriptor.
177
DatabaseTable targetTable = null;
178             if (outerExpression.getMapping().isDirectCollectionMapping()) {
179                 targetTable = ((DirectCollectionMapping)outerExpression.getMapping()).getReferenceTable();
180             } else {
181                 targetTable = (DatabaseTable)outerExpression.getMapping().getReferenceDescriptor().getTables().firstElement();
182             }
183             DatabaseTable sourceTable = (DatabaseTable)((ObjectExpression)outerExpression.getBaseExpression()).getDescriptor().getTables().firstElement();
184             DatabaseTable sourceAlias = outerExpression.getBaseExpression().aliasForTable(sourceTable);
185             DatabaseTable targetAlias = outerExpression.aliasForTable(targetTable);
186
187             if (!(outerJoinedAliases.contains(sourceAlias) || outerJoinedAliases.contains(targetAlias))) {
188                 if (!firstTable) {
189                     writer.write(", ");
190                 }
191
192                 firstTable = false;
193                 writer.write(sourceTable.getQualifiedName());
194                 outerJoinedAliases.addElement(sourceAlias);
195                 writer.write(" ");
196                 writer.write(sourceAlias.getQualifiedName());
197
198                 if (outerExpression.getMapping().isManyToManyMapping()) {// for many to many mappings, you need to do some funky stuff to get the relation table's alias
199
DatabaseTable newTarget = ((ManyToManyMapping)outerExpression.getMapping()).getRelationTable();
200                     DatabaseTable newAlias = relationExpression.aliasForTable(newTarget);
201                     writer.write(", OUTER ");// need to outer join only to relation table for many-to-many case in Informix
202
writer.write(newTarget.getQualifiedName());
203                     writer.write(" ");
204                     outerJoinedAliases.addElement(newAlias);
205                     writer.write(newAlias.getQualifiedName());
206                 } else if (outerExpression.getMapping().isDirectCollectionMapping()) {// for many to many mappings, you need to do some funky stuff to get the relation table's alias
207
DatabaseTable newTarget = ((DirectCollectionMapping)outerExpression.getMapping()).getReferenceTable();
208                     DatabaseTable newAlias = relationExpression.aliasForTable(newTarget);
209                     writer.write(", OUTER ");
210                     writer.write(newTarget.getQualifiedName());
211                     writer.write(" ");
212                     outerJoinedAliases.addElement(newAlias);
213                     writer.write(newAlias.getQualifiedName());
214                 } else {// do normal outer stuff for Informix
215
for (Enumeration target = outerExpression.getMapping().getReferenceDescriptor().getTables().elements();
216                              target.hasMoreElements();) {
217                         DatabaseTable newTarget = (DatabaseTable)target.nextElement();
218                         Expression onExpression = outerExpression;
219                         DatabaseTable newAlias = outerExpression.aliasForTable(newTarget);
220                         writer.write(", OUTER ");
221                         writer.write(newTarget.getQualifiedName());
222                         writer.write(" ");
223                         outerJoinedAliases.addElement(newAlias);
224                         writer.write(newAlias.getQualifiedName());
225                     }
226                 }
227             }
228         }
229     }
230
231     /**
232      * ADVANCED:
233      * If a platform is DB2, then the outer join must be in the FROM clause.
234      * This is used internally by TopLink for building DB2 outer join syntax which differs from
235      * other platforms(Oracle,Sybase) that print the outer join in the WHERE clause.
236      * OuterJoinedAliases passed in to keep track of tables used for outer join so no normal join is given
237      */

238     public void appendFromClauseForOuterJoin(ExpressionSQLPrinter printer, Vector outerJoinedAliases) throws IOException {
239         Writer writer = printer.getWriter();
240         AbstractSession session = printer.getSession();
241
242         // Print outer joins
243
boolean firstTable = true;
244         boolean requiresEscape = false;//checks if the jdbc closing escape syntax is needed
245

246         for (int index = 0; index < getOuterJoinExpressions().size(); index++) {
247             QueryKeyExpression outerExpression = (QueryKeyExpression)getOuterJoinExpressions().elementAt(index);
248
249             // CR#3083929 direct collection/map mappings do not have reference descriptor.
250
DatabaseTable targetTable = null;
251             DatabaseTable sourceTable = null;
252             DatabaseTable sourceAlias = null;
253             DatabaseTable targetAlias = null;
254             if(outerExpression != null) {
255                 if (outerExpression.getMapping().isDirectCollectionMapping()) {
256                     targetTable = ((DirectCollectionMapping)outerExpression.getMapping()).getReferenceTable();
257                 } else {
258                     targetTable = (DatabaseTable)outerExpression.getMapping().getReferenceDescriptor().getTables().firstElement();
259                 }
260                 sourceTable = (DatabaseTable)((ObjectExpression)outerExpression.getBaseExpression()).getDescriptor().getTables().firstElement();
261                 sourceAlias = outerExpression.getBaseExpression().aliasForTable(sourceTable);
262                 targetAlias = outerExpression.aliasForTable(targetTable);
263             } else {
264                 sourceTable = (DatabaseTable)((ClassDescriptor)getDescriptorsForMultitableInheritanceOnly().get(index)).getTables().firstElement();
265                 targetTable = (DatabaseTable)((ClassDescriptor)getDescriptorsForMultitableInheritanceOnly().get(index)).getInheritancePolicy().getChildrenTables().get(0);
266                 Expression exp = (Expression)((Map)getOuterJoinedAdditionalJoinCriteria().elementAt(index)).get(targetTable);
267                 sourceAlias = exp.aliasForTable(sourceTable);
268                 targetAlias = exp.aliasForTable(targetTable);
269             }
270
271             if (!outerJoinedAliases.contains(targetAlias)) {
272                 if (!outerJoinedAliases.contains(sourceAlias)) {
273                     if (requiresEscape && session.getPlatform().shouldUseJDBCOuterJoinSyntax()) {
274                         writer.write("}");
275                     }
276
277                     if (!firstTable) {
278                         writer.write(",");
279                     }
280
281                     if (session.getPlatform().shouldUseJDBCOuterJoinSyntax()) {
282                         writer.write(session.getPlatform().getJDBCOuterJoinString());
283                     }
284
285                     requiresEscape = true;
286                     firstTable = false;
287                     writer.write(sourceTable.getQualifiedName());
288                     outerJoinedAliases.addElement(sourceAlias);
289                     writer.write(" ");
290                     writer.write(sourceAlias.getQualifiedName());
291                 }
292
293                 if(outerExpression == null) {
294                     printAdditionalJoins(printer, outerJoinedAliases, (ClassDescriptor)getDescriptorsForMultitableInheritanceOnly().get(index), (Map)getOuterJoinedAdditionalJoinCriteria().elementAt(index));
295                 } else if (outerExpression.getMapping().isDirectCollectionMapping()) {
296                     // Append the join clause,
297
// If this is a direct collection, join to direct table.
298
Expression onExpression = (Expression)getOuterJoinedMappingCriteria().elementAt(index);
299
300                     DatabaseTable newAlias = onExpression.aliasForTable(targetTable);
301                     writer.write(" LEFT OUTER JOIN ");
302                     writer.write(targetTable.getQualifiedName());
303                     writer.write(" ");
304                     outerJoinedAliases.addElement(newAlias);
305                     writer.write(newAlias.getQualifiedName());
306                     writer.write(" ON ");
307
308                     if (session.getPlatform() instanceof DB2MainframePlatform) {
309                         ((RelationExpression)onExpression).printSQLNoParens(printer);
310                     } else {
311                         onExpression.printSQL(printer);
312                     }
313
314                     //Bug#4240751 Treat ManyToManyMapping separately for out join
315
} else if (outerExpression.getMapping().isManyToManyMapping()) {
316                     // Must outerjoin each of the targets tables.
317
// The first table is joined with the mapping join criteria,
318
// the rest of the tables are joined with the additional join criteria.
319
// For example: EMPLOYEE t1 LEFT OUTER JOIN (PROJ_EMP t3 LEFT OUTER JOIN PROJECT t0 ON (t0.PROJ_ID = t3.PROJ_ID)) ON (t3.EMP_ID = t1.EMP_ID)
320
DatabaseTable relationTable = ((ManyToManyMapping)outerExpression.getMapping()).getRelationTable();
321                     DatabaseTable relationAlias = ((Expression)getOuterJoinedMappingCriteria().elementAt(index)).aliasForTable(relationTable);
322                     writer.write(" LEFT OUTER JOIN (");
323                     
324                     writer.write(relationTable.getQualifiedName());
325                     writer.write(" ");
326                     outerJoinedAliases.addElement(relationAlias);
327                     writer.write(relationAlias.getQualifiedName());
328                     
329                     Vector tablesInOrder = oracle.toplink.essentials.internal.helper.NonSynchronizedVector.newInstance(3);
330                     tablesInOrder.add(sourceTable);
331                     tablesInOrder.add(relationTable);
332                     tablesInOrder.add(targetTable);
333                     TreeMap indexToExpressionMap = new TreeMap();
334                     mapTableIndexToExpression((Expression)getOuterJoinedMappingCriteria().elementAt(index), indexToExpressionMap, tablesInOrder);
335                     Expression sourceToRelationJoin = (Expression)indexToExpressionMap.get(new Integer JavaDoc(1));
336                     Expression relationToTargetJoin = (Expression)indexToExpressionMap.get(new Integer JavaDoc(2));
337                     
338                     writer.write(" JOIN ");
339                     writer.write(targetTable.getQualifiedName());
340                     writer.write(" ");
341                     outerJoinedAliases.addElement(targetAlias);
342                     writer.write(targetAlias.getQualifiedName());
343                     writer.write(" ON ");
344                     if (session.getPlatform() instanceof DB2MainframePlatform) {
345                         ((RelationExpression)relationToTargetJoin).printSQLNoParens(printer);
346                     } else {
347                         relationToTargetJoin.printSQL(printer);
348                     }
349                     
350                     Map tablesJoinExpression = (Map)getOuterJoinedAdditionalJoinCriteria().elementAt(index);
351                     if(tablesJoinExpression != null && !tablesJoinExpression.isEmpty()) {
352                         printAdditionalJoins(printer, outerJoinedAliases, outerExpression.getMapping().getReferenceDescriptor(), tablesJoinExpression);
353                     }
354                     writer.write(") ON ");
355                     if (session.getPlatform() instanceof DB2MainframePlatform) {
356                         ((RelationExpression)sourceToRelationJoin).printSQLNoParens(printer);
357                     } else {
358                         sourceToRelationJoin.printSQL(printer);
359                     }
360                 } else {
361                     // Must outerjoin each of the targets tables.
362
// The first table is joined with the mapping join criteria,
363
// the rest of the tables are joined with the additional join criteria.
364
writer.write(" LEFT OUTER JOIN ");
365                     Map tablesJoinExpression = (Map)getOuterJoinedAdditionalJoinCriteria().elementAt(index);
366                     boolean hasAdditionalJoinExpressions = tablesJoinExpression != null && !tablesJoinExpression.isEmpty();
367                     if(hasAdditionalJoinExpressions) {
368                         writer.write("(");
369                     }
370                     writer.write(targetTable.getQualifiedName());
371                     writer.write(" ");
372                     outerJoinedAliases.addElement(targetAlias);
373                     writer.write(targetAlias.getQualifiedName());
374                     if(hasAdditionalJoinExpressions) {
375                         printAdditionalJoins(printer, outerJoinedAliases, outerExpression.getMapping().getReferenceDescriptor(), tablesJoinExpression);
376                         writer.write(")");
377                     }
378                     writer.write(" ON ");
379                     Expression sourceToTargetJoin = (Expression)getOuterJoinedMappingCriteria().elementAt(index);
380                     if (session.getPlatform() instanceof DB2MainframePlatform) {
381                         ((RelationExpression)sourceToTargetJoin).printSQLNoParens(printer);
382                     } else {
383                         sourceToTargetJoin.printSQL(printer);
384                     }
385                 }
386             }
387         }
388
389         if (requiresEscape && session.getPlatform().shouldUseJDBCOuterJoinSyntax()) {
390             writer.write("}");
391         }
392     }
393
394     protected void printAdditionalJoins(ExpressionSQLPrinter printer, Vector outerJoinedAliases, ClassDescriptor desc, Map tablesJoinExpressions) throws IOException {
395         Writer writer = printer.getWriter();
396         AbstractSession session = printer.getSession();
397         Vector descriptorTables = desc.getTables();
398         int nDescriptorTables = descriptorTables.size();
399         Vector tables;
400         if(desc.hasInheritance()) {
401             tables = desc.getInheritancePolicy().getAllTables();
402         } else {
403             tables = descriptorTables;
404         }
405
406         // skip main table - start with i=1
407
int tablesSize = tables.size();
408         for(int i=1; i < tablesSize; i++) {
409             DatabaseTable table = (DatabaseTable)tables.elementAt(i);
410             Expression onExpression = (Expression)tablesJoinExpressions.get(table);
411             if (onExpression != null) {
412                 if(i < nDescriptorTables) {
413                     // it's descriptor's own table
414
writer.write(" JOIN ");
415                 } else {
416                     // it's child's table
417
writer.write(" LEFT OUTER JOIN ");
418                 }
419                 writer.write(table.getQualifiedName());
420                 writer.write(" ");
421                 DatabaseTable alias = onExpression.aliasForTable(table);
422                 outerJoinedAliases.addElement(alias);
423                 writer.write(alias.getQualifiedName());
424                 writer.write(" ON ");
425                 if (session.getPlatform() instanceof DB2MainframePlatform) {
426                     ((RelationExpression)onExpression).printSQLNoParens(printer);
427                 } else {
428                     onExpression.printSQL(printer);
429                 }
430             }
431         }
432     }
433
434     /**
435      * Print the from clause.
436      * This includes outer joins, these must be printed before the normal join to ensure that the source tables are not joined again.
437      * Outer joins are not printed in the FROM clause on Oracle or Sybase.
438      */

439     public void appendFromClauseToWriter(ExpressionSQLPrinter printer) throws IOException {
440         Writer writer = printer.getWriter();
441         AbstractSession session = printer.getSession();
442         writer.write(" FROM ");
443
444         // Print outer joins
445
boolean firstTable = true;
446         Vector outerJoinedAliases = new Vector(1);// Must keep track of tables used for outer join so no normal join is given
447

448         if (hasOuterJoinExpressions()) {
449             if (session.getPlatform().isInformixOuterJoin()) {
450                 appendFromClauseForInformixOuterJoin(printer, outerJoinedAliases);
451             } else if (!session.getPlatform().shouldPrintOuterJoinInWhereClause()) {
452                 appendFromClauseForOuterJoin(printer, outerJoinedAliases);
453             }
454
455             firstTable = false;
456         }
457
458         // If there are no table aliases it means the query was malformed,
459
// most likely the wrong builder was used, or wrong builder on the left in a sub-query.
460
if (getTableAliases().isEmpty()) {
461             throw QueryException.invalidBuilderInQuery(null);// Query is set in execute.
462
}
463
464         // Print tables for normal join
465
for (Enumeration aliasesEnum = getTableAliases().keys(); aliasesEnum.hasMoreElements();) {
466             DatabaseTable alias = (DatabaseTable)aliasesEnum.nextElement();
467
468             if (!outerJoinedAliases.contains(alias)) {
469                 DatabaseTable table = (DatabaseTable)getTableAliases().get(alias);
470
471                 if (requiresAliases()) {
472                     if (!firstTable) {
473                         writer.write(", ");
474                     }
475
476                     firstTable = false;
477                     writer.write(table.getQualifiedName());
478                     writer.write(" ");
479                     writer.write(alias.getQualifiedName());
480                 } else {
481                     writer.write(table.getQualifiedName());
482                 }
483             }
484         }
485     }
486
487     /**
488      * This method will append the group by clause to the end of the
489      * select statement.
490      */

491     public void appendGroupByClauseToWriter(ExpressionSQLPrinter printer) throws IOException {
492         if (getGroupByExpressions().isEmpty()) {
493             return;
494         }
495
496         printer.getWriter().write(" GROUP BY ");
497
498         Vector newFields = new Vector();
499         // to avoid printing a comma before the first field
500
printer.setIsFirstElementPrinted(false);
501         for (Enumeration expressionsEnum = getGroupByExpressions().elements();
502                  expressionsEnum.hasMoreElements();) {
503             Expression expression = (Expression)expressionsEnum.nextElement();
504             writeFieldsFromExpression(printer, expression, newFields);
505         }
506     }
507
508     /**
509      * This method will append the Hierarchical Query Clause to the end of the
510      * select statement
511      */

512     public void appendHierarchicalQueryClauseToWriter(ExpressionSQLPrinter printer) throws IOException {
513         Expression startWith = getStartWithExpression();
514         Expression connectBy = getConnectByExpression();
515         Vector orderSiblingsBy = getOrderSiblingsByExpressions();
516
517         //Create the START WITH CLAUSE
518
if (startWith != null) {
519             printer.getWriter().write(" START WITH ");
520             startWith.printSQL(printer);
521         }
522
523         if (connectBy != null) {
524             if (!connectBy.isQueryKeyExpression()) {
525                 throw QueryException.illFormedExpression(connectBy);
526             }
527
528             printer.getWriter().write(" CONNECT BY ");
529
530             DatabaseMapping mapping = ((QueryKeyExpression)connectBy).getMapping();
531             ClassDescriptor descriptor = mapping.getDescriptor();
532
533             //only works for these kinds of mappings. The data isn't hierarchical otherwise
534
//Should also check that the source class and target class are the same.
535
Map foreignKeys = null;
536
537             if (mapping.isOneToManyMapping()) {
538                 OneToManyMapping otm = (OneToManyMapping)mapping;
539                 foreignKeys = otm.getTargetForeignKeyToSourceKeys();
540             } else if (mapping.isOneToOneMapping()) {
541                 OneToOneMapping oto = (OneToOneMapping)mapping;
542                 foreignKeys = oto.getSourceToTargetKeyFields();
543             } else if (mapping.isAggregateCollectionMapping()) {
544                 AggregateCollectionMapping acm = (AggregateCollectionMapping)mapping;
545                 foreignKeys = acm.getTargetForeignKeyToSourceKeys();
546             } else {
547                 throw QueryException.invalidQueryKeyInExpression(connectBy);
548             }
549
550             DatabaseTable defaultTable = descriptor.getDefaultTable();
551             String JavaDoc tableName = "";
552
553             //determine which table name to use
554
if (requiresAliases()) {
555                 tableName = getBuilder().aliasForTable(defaultTable).getName();
556             } else {
557                 tableName = defaultTable.getName();
558             }
559
560             if ((foreignKeys != null) && !foreignKeys.isEmpty()) {
561                 //get the source and target fields.
562
Iterator sourceKeys = foreignKeys.keySet().iterator();
563
564                 //for each source field, get the target field and create the link. If there's
565
//only one, use the simplest version without ugly bracets
566
if (foreignKeys.size() > 1) {
567                     printer.getWriter().write("((");
568                 }
569
570                 DatabaseField source = (DatabaseField)sourceKeys.next();
571                 DatabaseField target = (DatabaseField)foreignKeys.get(source);
572
573                 //OneToOneMappings -> Source in parent object goes to target in child object
574
if (mapping.isOneToOneMapping()) {
575                     printer.getWriter().write("PRIOR " + tableName + "." + source.getName());
576                     printer.getWriter().write(" = " + tableName + "." + target.getName());
577                 } else {//collections are the opposite way
578
printer.getWriter().write(tableName + "." + source.getName());
579                     printer.getWriter().write(" = PRIOR " + tableName + "." + target.getName());
580                 }
581
582                 while (sourceKeys.hasNext()) {
583                     printer.getWriter().write(") AND (");
584                     source = (DatabaseField)sourceKeys.next();
585                     target = (DatabaseField)foreignKeys.get(source);
586
587                     if (mapping.isOneToOneMapping()) {
588                         printer.getWriter().write("PRIOR " + tableName + "." + source.getName());
589                         printer.getWriter().write(" = " + tableName + "." + target.getName());
590                     } else {//collections are the opposite way
591
printer.getWriter().write(tableName + "." + source.getName());
592                         printer.getWriter().write(" = PRIOR " + tableName + "." + target.getName());
593                     }
594                 }
595
596                 if (foreignKeys.size() > 1) {
597                     printer.getWriter().write("))");
598                 }
599             }
600         }
601
602         if (orderSiblingsBy != null) {
603             printer.getWriter().write(" ORDER SIBLINGS BY ");
604
605             for (Enumeration expressionEnum = orderSiblingsBy.elements();
606                      expressionEnum.hasMoreElements();) {
607                 Expression ex = (Expression)expressionEnum.nextElement();
608                 ex.printSQL(printer);
609
610                 if (expressionEnum.hasMoreElements()) {
611                     printer.getWriter().write(", ");
612                 }
613             }
614         }
615     }
616
617     /**
618      * This method will append the order clause to the end of the
619      * select statement.
620      */

621     public void appendOrderClauseToWriter(ExpressionSQLPrinter printer) throws IOException {
622         if (!hasOrderByExpressions()) {
623             return;
624         }
625
626         printer.getWriter().write(" ORDER BY ");
627
628         for (Enumeration expressionsEnum = getOrderByExpressions().elements();
629                  expressionsEnum.hasMoreElements();) {
630             Expression expression = (Expression)expressionsEnum.nextElement();
631             expression.printSQL(printer);
632
633             if (expressionsEnum.hasMoreElements()) {
634                 printer.getWriter().write(", ");
635             }
636         }
637     }
638
639     /**
640      * INTERNAL: Alias the tables in all of our nodes.
641      */

642     public void assignAliases(Vector allExpressions) {
643         // For sub-selects all statements must share aliasing information.
644
// For CR#2627019
645
currentAliasNumber = getCurrentAliasNumber();
646
647         ExpressionIterator iterator = new ExpressionIterator() {
648             public void iterate(Expression each) {
649                 currentAliasNumber = each.assignTableAliasesStartingAt(currentAliasNumber);
650             }
651         };
652
653         if (allExpressions.isEmpty()) {
654             // bug 3878553 - ensure aliases are always assigned for when required .
655
if ((getBuilder() != null) && requiresAliases()) {
656                 getBuilder().assignTableAliasesStartingAt(currentAliasNumber);
657             }
658         } else {
659             for (Enumeration expressionEnum = allExpressions.elements();
660                      expressionEnum.hasMoreElements();) {
661                 Expression expression = (Expression)expressionEnum.nextElement();
662                 iterator.iterateOn(expression);
663             }
664         }
665
666         // For sub-selects update aliasing information of all statements.
667
// For CR#2627019
668
setCurrentAliasNumber(currentAliasNumber);
669     }
670
671     /**
672      * Print the SQL representation of the statement on a stream.
673      */

674     public DatabaseCall buildCall(AbstractSession session) {
675         SQLCall call = new SQLCall();
676         call.returnManyRows();
677
678         Writer writer = new CharArrayWriter(200);
679
680         ExpressionSQLPrinter printer = new ExpressionSQLPrinter(session, getTranslationRow(), call, requiresAliases());
681         printer.setWriter(writer);
682
683         call.setFields(printSQL(printer));
684         call.setSQLString(writer.toString());
685
686         return call;
687     }
688
689     /**
690      * INTERNAL:
691      * This is used by cursored stream to determine if an expression used distinct as the size must account for this.
692      */

693     public void computeDistinct() {
694         ExpressionIterator iterator = new ExpressionIterator() {
695             public void iterate(Expression expression) {
696                 if (expression.isQueryKeyExpression() && ((QueryKeyExpression)expression).shouldQueryToManyRelationship()) {
697                     // Aggregate should only use distinct as specified by the user.
698
if (!isDistinctComputed()) {
699                         useDistinct();
700                     }
701                 }
702             }
703         };
704
705         if (getWhereClause() != null) {
706             iterator.iterateOn(getWhereClause());
707         }
708     }
709
710     public boolean isSubSelect() {
711         return (getParentStatement() != null);
712     }
713
714     /**
715      * INTERNAL:
716      * Computes all aliases which will appear in the FROM clause.
717      */

718     public void computeTables() {
719         // Compute tables should never defer to computeTablesFromTables
720
// This iterator will pull all the table aliases out of an expression, and
721
// put them in a hashtable.
722
ExpressionIterator iterator = new ExpressionIterator() {
723             public void iterate(Expression each) {
724                 TableAliasLookup aliases = each.getTableAliases();
725
726                 if (aliases != null) {
727                     // Insure that an aliased table is only added to a single
728
// FROM clause.
729
if (!aliases.haveBeenAddedToStatement()) {
730                         aliases.addToHashtable((Hashtable)getResult());
731                         aliases.setHaveBeenAddedToStatement(true);
732                     }
733                 }
734             }
735         };
736
737         iterator.setResult(new Hashtable(5));
738
739         if (getWhereClause() != null) {
740             iterator.iterateOn(getWhereClause());
741         } else if (hasOuterJoinExpressions()) {
742             Expression outerJoinCriteria = (Expression)getOuterJoinedMappingCriteria().firstElement();
743             if (outerJoinCriteria != null){
744                 iterator.iterateOn(outerJoinCriteria);
745             }
746         }
747
748         //Iterate on fields as well in that rare case where the select is not in the where clause
749
for (Iterator fields = getFields().iterator(); fields.hasNext();) {
750             Object JavaDoc field = fields.next();
751             if (field instanceof Expression) {
752                 iterator.iterateOn((Expression)field);
753             }
754         }
755
756         // Always iterator on the builder, as the where clause may not contain the builder, i.e. value=value.
757
iterator.iterateOn(getBuilder());
758
759         Hashtable allTables = (Hashtable)iterator.getResult();
760         setTableAliases(allTables);
761
762         for (Enumeration e = allTables.elements(); e.hasMoreElements();) {
763             addTable((DatabaseTable)e.nextElement());
764         }
765     }
766
767     /**
768      * If there is no where clause, alias the tables from the tables list directly. Assume there's
769      * no ambiguity
770      */

771     public void computeTablesFromTables() {
772         Hashtable allTables = new Hashtable();
773
774         for (int index = 0; index < getTables().size(); index++) {
775             DatabaseTable next = (DatabaseTable)getTables().elementAt(index);
776             DatabaseTable alias = new DatabaseTable("t" + (index));
777             allTables.put(alias, next);
778         }
779
780         setTableAliases(allTables);
781     }
782
783     /**
784      * ADVANCED:
785      * If a distinct has been set the DISTINCT clause will be printed.
786      * This is used internally by TopLink for batch reading but may also be
787      * used directly for advanced queries or report queries.
788      */

789     public void dontUseDistinct() {
790         setDistinctState(ObjectLevelReadQuery.DONT_USE_DISTINCT);
791     }
792
793     /**
794      * Check if the field from the field expression is already contained in the select clause of the statement.
795      * This is used on order by expression when the field being ordered by must be in the select,
796      * but cannot be in the select twice.
797      */

798     protected boolean fieldsContainField(Vector fields, Expression expression) {
799         DatabaseField orderByField;
800
801         if (expression instanceof DataExpression) {
802             orderByField = ((DataExpression)expression).getField();
803         } else {
804             return false;
805         }
806
807         //check all fields for a match
808
for (Enumeration fieldsEnum = fields.elements(); fieldsEnum.hasMoreElements();) {
809             Object JavaDoc fieldOrExpression = fieldsEnum.nextElement();
810
811             if (fieldOrExpression instanceof DatabaseField) {
812                 DatabaseField field = (DatabaseField)fieldOrExpression;
813                 DataExpression exp = (DataExpression)expression;
814
815                 if (field.equals(orderByField) && (exp.getBaseExpression() == getBuilder())) {
816                     // found a match
817
return true;
818                 }
819             }
820             // For CR#2589. This method was not getting the fields in the same way that
821
// printSQL does (i.e. using getFields() instead of getField()).
822
// The problem was that getField() on an expression builder led to a null pointer
823
// exception.
824
else {
825                 Expression exp = (Expression)fieldOrExpression;
826                 DatabaseTable table = orderByField.getTable();
827
828                 if (exp.getFields().contains(orderByField) && (expression.aliasForTable(table).equals(exp.aliasForTable(table)))) {
829                     //found a match
830
return true;
831                 }
832             }
833         }
834
835         // no matches
836
return false;
837     }
838
839     /**
840      * Gets a unique id that will be used to alias the next table.
841      * For sub-selects all must use this same aliasing information, maintained
842      * in the root enclosing statement. For CR#2627019
843      */

844     public int getCurrentAliasNumber() {
845         if (getParentStatement() != null) {
846             return getParentStatement().getCurrentAliasNumber();
847         } else {
848             return currentAliasNumber;
849         }
850     }
851
852     /**
853      * INTERNAL:
854      * Return all the fields
855      */

856     public Vector getFields() {
857         return fields;
858     }
859
860     protected ForUpdateClause getForUpdateClause() {
861         return forUpdateClause;
862     }
863
864     /**
865      * INTERNAL:
866      * Return the group bys.
867      */

868     public Vector getGroupByExpressions() {
869         if (groupByExpressions == null) {
870             groupByExpressions = oracle.toplink.essentials.internal.helper.NonSynchronizedVector.newInstance(3);
871         }
872
873         return groupByExpressions;
874     }
875
876     /**
877      * INTERNAL:
878      * Return the having expression.
879      */

880     public Expression getHavingExpression() {
881         return havingExpression;
882     }
883
884     /**
885      * INTERNAL:
886      * Return the StartWith expression
887      */

888     public Expression getStartWithExpression() {
889         return startWithExpression;
890     }
891
892     /**
893      * INTERNAL:
894      * Return the CONNECT BY expression
895      */

896     public Expression getConnectByExpression() {
897         return connectByExpression;
898     }
899
900     /**
901      * INTERNAL:
902      * Return the ORDER SIBLINGS BY expression
903      */

904     public Vector getOrderSiblingsByExpressions() {
905         return orderSiblingsByExpressions;
906     }
907
908     /**
909      * Return the fields we don't want to select but want to join on.
910      */

911     public Vector getNonSelectFields() {
912         return nonSelectFields;
913     }
914     
915     /**
916      * INTERNAL:
917      * Return the order expressions for the query.
918      */

919     public Vector getOrderByExpressions() {
920         if (orderByExpressions == null) {
921             orderByExpressions = oracle.toplink.essentials.internal.helper.NonSynchronizedVector.newInstance(3);
922         }
923
924         return orderByExpressions;
925     }
926
927     /**
928      * INTERNAL:
929      * Each Vector's element is a map of tables join expressions keyed by the tables
930      */

931     public Vector getOuterJoinedAdditionalJoinCriteria() {
932         if (outerJoinedAdditionalJoinCriteria == null) {
933             outerJoinedAdditionalJoinCriteria = oracle.toplink.essentials.internal.helper.NonSynchronizedVector.newInstance(3);
934         }
935
936         return outerJoinedAdditionalJoinCriteria;
937     }
938
939     public Vector getOuterJoinedMappingCriteria() {
940         if (outerJoinedMappingCriteria == null) {
941             outerJoinedMappingCriteria = oracle.toplink.essentials.internal.helper.NonSynchronizedVector.newInstance(3);
942         }
943
944         return outerJoinedMappingCriteria;
945     }
946
947     public Vector getOuterJoinExpressions() {
948         if (outerJoinedExpressions == null) {
949             outerJoinedExpressions = oracle.toplink.essentials.internal.helper.NonSynchronizedVector.newInstance(3);
950         }
951
952         return outerJoinedExpressions;
953     }
954
955     public List getDescriptorsForMultitableInheritanceOnly() {
956         if (descriptorsForMultitableInheritanceOnly == null) {
957             descriptorsForMultitableInheritanceOnly = new ArrayList(3);
958         }
959
960         return descriptorsForMultitableInheritanceOnly;
961     }
962
963     /**
964      * Return the parent statement if using subselects.
965      * This is used to normalize correctly with subselects.
966      */

967     public SQLSelectStatement getParentStatement() {
968         return parentStatement;
969     }
970
971     /**
972      * INTERNAL:
973      * Return the aliases used.
974      */

975     public Hashtable getTableAliases() {
976         return tableAliases;
977     }
978
979     /**
980      * INTERNAL:
981      * Return all the tables.
982      */

983     public Vector getTables() {
984         return tables;
985     }
986
987     protected boolean hasAliasForTable(DatabaseTable table) {
988         if (tableAliases != null) {
989             return getTableAliases().containsKey(table);
990         }
991
992         return false;
993     }
994
995     public boolean hasGroupByExpressions() {
996         return (groupByExpressions != null) && (!groupByExpressions.isEmpty());
997     }
998
999     public boolean hasHavingExpression() {
1000        return (havingExpression != null);
1001    }
1002
1003    public boolean hasStartWithExpression() {
1004        return startWithExpression != null;
1005    }
1006
1007    public boolean hasConnectByExpression() {
1008        return connectByExpression != null;
1009    }
1010
1011    public boolean hasOrderSiblingsByExpressions() {
1012        return orderSiblingsByExpressions != null;
1013    }
1014
1015    public boolean hasHierarchicalQueryExpressions() {
1016        return ((startWithExpression != null) || (connectByExpression != null) || (orderSiblingsByExpressions != null));
1017    }
1018
1019    public boolean hasOrderByExpressions() {
1020        return (orderByExpressions != null) && (!orderByExpressions.isEmpty());
1021    }
1022    
1023    public boolean hasNonSelectFields() {
1024        return (nonSelectFields != null) && (!nonSelectFields.isEmpty());
1025    }
1026
1027    public boolean hasOuterJoinedAdditionalJoinCriteria() {
1028        return (outerJoinedAdditionalJoinCriteria != null) && (!outerJoinedAdditionalJoinCriteria.isEmpty());
1029    }
1030
1031    public boolean hasOuterJoinExpressions() {
1032        return (outerJoinedExpressions != null) && (!outerJoinedExpressions.isEmpty());
1033    }
1034
1035    /**
1036     * INTERNAL:
1037     */

1038    public boolean isAggregateSelect() {
1039        return isAggregateSelect;
1040    }
1041
1042    /**
1043     * INTERNAL:
1044     * return true if this query has computed its distinct value already
1045     */

1046    public boolean isDistinctComputed() {
1047        return distinctState != ObjectLevelReadQuery.UNCOMPUTED_DISTINCT;
1048    }
1049
1050    /**
1051     * INTERNAL:
1052     * Normalize an expression into a printable structure.
1053     * i.e. merge the expression with the join expressions.
1054     * Also replace table names with corresponding aliases.
1055     * @param clonedExpressions
1056     */

1057    public final void normalize(AbstractSession session, ClassDescriptor descriptor) {
1058        // 2612538 - the default size of IdentityHashtable (32) is appropriate
1059
normalize(session, descriptor, new IdentityHashtable());
1060    }
1061
1062    /**
1063     * INTERNAL:
1064     * Normalize an expression into a printable structure.
1065     * i.e. merge the expression with the join expressions.
1066     * Also replace table names with corresponding aliases.
1067     * @param clonedExpressions With 2612185 allows additional expressions
1068     * from multiple bases to be rebuilt on the correct cloned base.
1069     */

1070    public void normalize(AbstractSession session, ClassDescriptor descriptor, Dictionary clonedExpressions) {
1071        // Initialize the builder.
1072
if (getBuilder() == null) {
1073            if (getWhereClause() == null) {
1074                setBuilder(new ExpressionBuilder());
1075            } else {
1076                setBuilder(getWhereClause().getBuilder());
1077            }
1078        }
1079
1080        ExpressionBuilder builder = getBuilder();
1081
1082        // For flashback: The builder is increasingly important. It can store
1083
// an AsOfClause, needs to be normalized for history, and aliased for
1084
// pessimistic locking to work. Hence everything that would have
1085
// been applied to the where clause will be applied to the builder if
1086
// the former is null. In the past though if there was no where
1087
// clause just threw away the builder (to get to this point we had to
1088
// pass it in via the vacated where clause), and neither normalized nor
1089
// aliased it directly.
1090
if (getWhereClause() == builder) {
1091            setWhereClause(null);
1092        }
1093
1094        builder.setSession(session);
1095
1096        // Some queries are not on objects but for data, thus no descriptor.
1097
if (!builder.doesNotRepresentAnObjectInTheQuery()) {
1098            if (descriptor != null) {
1099                builder.setQueryClass(descriptor.getJavaClass());
1100            }
1101        }
1102
1103        // Compute all other expressions used other than the where clause, i.e. select, order by, group by.
1104
// Also must ensure that all expression use a unique builder.
1105
Vector allExpressions = new Vector();
1106
1107        // Process select expressions.
1108
rebuildAndAddExpressions(getFields(), allExpressions, builder, clonedExpressions);
1109
1110        // Process non-select expressions
1111
if (hasNonSelectFields()) {
1112            rebuildAndAddExpressions(getNonSelectFields(), allExpressions, builder, clonedExpressions);
1113        }
1114        
1115        // Process group by expressions.
1116
if (hasGroupByExpressions()) {
1117            rebuildAndAddExpressions(getGroupByExpressions(), allExpressions, builder, clonedExpressions);
1118        }
1119
1120        if (hasHavingExpression()) {
1121            //rebuildAndAddExpressions(getHavingExpression(), allExpressions, builder, clonedExpressions);
1122
Expression expression = getHavingExpression();
1123            ExpressionBuilder originalBuilder = expression.getBuilder();
1124            if (originalBuilder != builder) {
1125                // For bug 2612185 avoid rebuildOn if possible as it rebuilds all on a single base.
1126
// i.e. Report query items could be from parallel expressions.
1127
if (clonedExpressions.get(originalBuilder) != null) {
1128                    expression = expression.copiedVersionFrom(clonedExpressions);
1129                } else {
1130                    // Possibly the expression was built with the wrong builder.
1131
expression = expression.rebuildOn(builder);
1132                }
1133
1134                setHavingExpression(expression);
1135            }
1136
1137            allExpressions.addElement(expression);
1138        }
1139
1140        // Process order by expressions.
1141
if (hasOrderByExpressions()) {
1142            rebuildAndAddExpressions(getOrderByExpressions(), allExpressions, builder, clonedExpressions);
1143        }
1144
1145        // Process outer join by expressions.
1146
if (hasOuterJoinExpressions()) {
1147            rebuildAndAddExpressions(getOuterJoinedMappingCriteria(), allExpressions, builder, clonedExpressions);
1148            for (Iterator criterias = getOuterJoinedAdditionalJoinCriteria().iterator();
1149                     criterias.hasNext();) {
1150                rebuildAndAddExpressions((Map)criterias.next(), allExpressions, builder, clonedExpressions);
1151            }
1152        }
1153
1154        //Process hierarchical query expressions.
1155
if (hasStartWithExpression()) {
1156            startWithExpression = getStartWithExpression().rebuildOn(builder);
1157            allExpressions.addElement(startWithExpression);
1158        }
1159
1160        if (hasConnectByExpression()) {
1161            connectByExpression = getConnectByExpression().rebuildOn(builder);
1162        }
1163
1164        if (hasOrderSiblingsByExpressions()) {
1165            rebuildAndAddExpressions(getOrderSiblingsByExpressions(), allExpressions, builder, clonedExpressions);
1166        }
1167
1168        // We have to handle the cases where the where
1169
// clause is initially empty but might have clauses forced into it because the class
1170
// has multiple tables, order by forces a join, etc. So we have to create a builder
1171
// and add expressions for it and the extras, but throw it away if they didn't force anything
1172
Expression oldRoot = getWhereClause();
1173        ExpressionNormalizer normalizer = new ExpressionNormalizer(this);
1174        normalizer.setSession(session);
1175
1176        Expression newRoot = null;
1177
1178        if (oldRoot != null) {
1179            newRoot = oldRoot.normalize(normalizer);
1180        }
1181
1182        // CR#3166542 always ensure that the builder has been normalized,
1183
// there may be an expression that does not refer to the builder, i.e. value=value.
1184
if (descriptor != null) {
1185            builder.normalize(normalizer);
1186        }
1187
1188        for (int index = 0; index < allExpressions.size(); index++) {
1189            Expression expression = (Expression)allExpressions.elementAt(index);
1190            expression.normalize(normalizer);
1191        }
1192
1193        // Sets the where clause and AND's it with the additional Expression
1194
// setNormalizedWhereClause must be called to avoid the builder side-effects
1195
if (newRoot == null) {
1196            setNormalizedWhereClause(normalizer.getAdditionalExpression());
1197        } else {
1198            setNormalizedWhereClause(newRoot.and(normalizer.getAdditionalExpression()));
1199        }
1200
1201        if (getWhereClause() != null) {
1202            allExpressions.addElement(getWhereClause());
1203        }
1204
1205        // CR#3166542 always ensure that the builder has been normalized,
1206
// there may be an expression that does not refer to the builder, i.e. value=value.
1207
if (descriptor != null) {
1208            allExpressions.addElement(builder);
1209        }
1210
1211        // Must also assign aliases to outer joined mapping criterias.
1212
if (hasOuterJoinExpressions()) {
1213            // Check for null on criterias.
1214
for (Iterator criterias = getOuterJoinedMappingCriteria().iterator();
1215                     criterias.hasNext();) {
1216                Expression criteria = (Expression)criterias.next();
1217                if (criteria != null) {
1218                    allExpressions.add(criteria);
1219                }
1220            }
1221
1222            // Check for null on criterias.
1223
for (Iterator criterias = getOuterJoinedAdditionalJoinCriteria().iterator();
1224                     criterias.hasNext();) {
1225                Map map = (Map)criterias.next();
1226                if (map != null) {
1227                    Iterator it = map.values().iterator();
1228                    while(it.hasNext()) {
1229                        Expression criteria = (Expression)it.next();
1230                        if(criteria != null) {
1231                            allExpressions.add(criteria);
1232                        }
1233                    }
1234                }
1235            }
1236        }
1237
1238        // Bug 2956674 Remove validate call as validation will be completed as the expression was normalized
1239
// Assign all table aliases.
1240
assignAliases(allExpressions);
1241
1242        // If this is data level then the tables must be set manually.
1243
if (descriptor == null) {
1244            computeTablesFromTables();
1245        } else {
1246            computeTables();
1247        }
1248
1249        // Now that the parent statement has been normalized, aliased, etc.,
1250
// normalize the subselect expressions. For CR#4223.
1251
if (normalizer.encounteredSubSelectExpressions()) {
1252            normalizer.normalizeSubSelects(clonedExpressions);
1253        }
1254
1255        // CR2114; If this is data level then we don't have a descriptor.
1256
// We don't have a target class so we must use the root platform. PWK
1257
// We are not fixing the informix.
1258
Class JavaDoc aClass = null;
1259
1260        if (descriptor != null) {
1261            aClass = descriptor.getJavaClass();
1262        }
1263
1264        // When a distinct is used the order bys must be in the select clause, so this forces them into the select.
1265
if ((session.getPlatform(aClass).isInformix()) || (shouldDistinctBeUsed() && hasOrderByExpressions())) {
1266            addOrderByExpressionToSelectForDistinct();
1267        }
1268    }
1269
1270    /**
1271     * INTERNAL:
1272     * Normalize an expression mapping all of the descriptor's tables to the view.
1273     * This is used to allow a descriptor to read from a view, but write to tables.
1274     * This is used in the multiple table and subclasses read so all of the descriptor's
1275     * possible tables must be mapped to the view.
1276     */

1277    public void normalizeForView(AbstractSession theSession, ClassDescriptor theDescriptor, Dictionary clonedExpressions) {
1278        ExpressionBuilder builder;
1279
1280        // bug 3878553 - alias all view selects.
1281
setRequiresAliases(true);
1282
1283        if (getWhereClause() != null) {
1284            builder = getWhereClause().getBuilder();
1285        } else {
1286            builder = new ExpressionBuilder();
1287            setBuilder(builder);
1288        }
1289
1290        builder.setViewTable((DatabaseTable)getTables().firstElement());
1291
1292        normalize(theSession, theDescriptor, clonedExpressions);
1293    }
1294
1295    /**
1296     * Print the SQL representation of the statement on a stream.
1297     */

1298    public Vector printSQL(ExpressionSQLPrinter printer) {
1299        try {
1300            Vector selectFields = null;
1301            printer.setRequiresDistinct(shouldDistinctBeUsed());
1302            printer.printString("SELECT ");
1303
1304            if (shouldDistinctBeUsed()) {
1305                printer.printString("DISTINCT ");
1306            }
1307
1308            selectFields = writeFieldsIn(printer);
1309
1310            appendFromClauseToWriter(printer);
1311
1312            if (!(getWhereClause() == null)) {
1313                printer.printString(" WHERE ");
1314                printer.printExpression(getWhereClause());
1315            }
1316
1317            if (hasHierarchicalQueryExpressions()) {
1318                appendHierarchicalQueryClauseToWriter(printer);
1319            }
1320
1321            if (hasGroupByExpressions()) {
1322                appendGroupByClauseToWriter(printer);
1323            }
1324            if (hasHavingExpression()) {
1325                //appendHavingClauseToWriter(printer);
1326
printer.printString(" HAVING ");
1327                printer.printExpression(getHavingExpression());
1328            }
1329
1330            if (hasOrderByExpressions()) {
1331                appendOrderClauseToWriter(printer);
1332            }
1333
1334            // For pessimistic locking.
1335
if (getForUpdateClause() != null) {
1336                getForUpdateClause().printSQL(printer, this);
1337            }
1338
1339            return selectFields;
1340        } catch (IOException exception) {
1341            throw ValidationException.fileError(exception);
1342        }
1343    }
1344
1345    /**
1346     * Rebuild the expressions with the correct expression builder if using a different one.
1347     */

1348    public void rebuildAndAddExpressions(Vector expressions, Vector allExpressions, ExpressionBuilder primaryBuilder, Dictionary clonedExpressions) {
1349        for (int index = 0; index < expressions.size(); index++) {
1350            Object JavaDoc fieldOrExpression = expressions.elementAt(index);
1351
1352            if (fieldOrExpression instanceof Expression) {
1353                Expression expression = (Expression)fieldOrExpression;
1354                ExpressionBuilder originalBuilder = expression.getBuilder();
1355
1356                if (originalBuilder != primaryBuilder) {
1357                    // For bug 2612185 avoid rebuildOn if possible as it rebuilds all on a single base.
1358
// i.e. Report query items could be from parallel expressions.
1359
if (clonedExpressions.get(originalBuilder) != null) {
1360                        expression = expression.copiedVersionFrom(clonedExpressions);
1361                        //if there is no builder or it is a copy of the base builder then rebuild otherwise it is a parallel expression not joined
1362
}
1363                    if (originalBuilder.wasQueryClassSetInternally()) {
1364                        // Possibly the expression was built with the wrong builder.
1365
expression = expression.rebuildOn(primaryBuilder);
1366                    }
1367                    expressions.setElementAt(expression, index);
1368                }
1369
1370                allExpressions.addElement(expression);
1371            }
1372        }
1373    }
1374
1375    /**
1376     * Rebuild the expressions with the correct expression builder if using a different one.
1377     * Exact copy of the another rebuildAndAddExpressions adopted to a Map with Expression values
1378     * as the first parameter (instead of Vector in the original method)
1379     */

1380    public void rebuildAndAddExpressions(Map expressions, Vector allExpressions, ExpressionBuilder primaryBuilder, Dictionary clonedExpressions) {
1381        Iterator it = expressions.entrySet().iterator();
1382        while (it.hasNext()) {
1383            Map.Entry entry = (Map.Entry)it.next();
1384            Object JavaDoc fieldOrExpression = entry.getValue();
1385
1386            if (fieldOrExpression instanceof Expression) {
1387                Expression expression = (Expression)fieldOrExpression;
1388                ExpressionBuilder originalBuilder = expression.getBuilder();
1389
1390                if (originalBuilder != primaryBuilder) {
1391                    // For bug 2612185 avoid rebuildOn if possible as it rebuilds all on a single base.
1392
// i.e. Report query items could be from parallel expressions.
1393
if (clonedExpressions.get(originalBuilder) != null) {
1394                        expression = expression.copiedVersionFrom(clonedExpressions);
1395                        //if there is no builder or it is a copy of the base builder then rebuild otherwise it is a parallel expression not joined
1396
}
1397                    if (originalBuilder.wasQueryClassSetInternally()) {
1398                        // Possibly the expression was built with the wrong builder.
1399
expression = expression.rebuildOn(primaryBuilder);
1400                    }
1401
1402                    entry.setValue(expression);
1403                }
1404
1405                allExpressions.addElement(expression);
1406            }
1407        }
1408    }
1409
1410    /**
1411     * INTERNAL:
1412     */

1413    public void removeField(DatabaseField field) {
1414        getFields().removeElement(field);
1415    }
1416
1417    /**
1418     * Remove a table from the statement. The table will
1419     * be dropped from the FROM part of the SQL statement.
1420     */

1421    public void removeTable(DatabaseTable table) {
1422        getTables().removeElement(table);
1423    }
1424
1425    /**
1426     * INTERNAL: Returns true if aliases are required, false otherwise.
1427     * If requiresAliases is set then force aliasing, this is required for object-rel.
1428     */

1429    public boolean requiresAliases() {
1430        if (requiresAliases || hasOuterJoinExpressions()) {
1431            return true;
1432        }
1433
1434        if (tableAliases != null) {
1435            return getTableAliases().size() > 1;
1436        }
1437
1438        // tableAliases is null
1439
return false;
1440    }
1441
1442    /**
1443     * ADVANCED:
1444     * If a distinct has been set the DISTINCT clause will be printed.
1445     * This is used internally by TopLink for batch reading but may also be
1446     * used directly for advanced queries or report queries.
1447     */

1448    public void resetDistinct() {
1449        setDistinctState(ObjectLevelReadQuery.UNCOMPUTED_DISTINCT);
1450    }
1451
1452    /**
1453     * Sets a unique id that will be used to alias the next table.
1454     * For sub-selects all must use this same aliasing information, maintained
1455     * in the root enclosing statement. For CR#2627019
1456     */

1457    public void setCurrentAliasNumber(int currentAliasNumber) {
1458        if (getParentStatement() != null) {
1459            getParentStatement().setCurrentAliasNumber(currentAliasNumber);
1460        } else {
1461            this.currentAliasNumber = currentAliasNumber;
1462        }
1463    }
1464
1465    /**
1466     * Set the non select fields. The fields are used only on joining.
1467     */

1468    public void setNonSelectFields(Vector nonSelectFields) {
1469        this.nonSelectFields = nonSelectFields;
1470    }
1471
1472    /**
1473     * Set the where clause expression.
1474     * This must be used during normalization as the normal setWhereClause has the side effect
1475     * of setting the builder, which must not occur during normalize.
1476     */

1477    public void setNormalizedWhereClause(Expression whereClause) {
1478        this.whereClause = whereClause;
1479    }
1480
1481    /**
1482     * ADVANCED:
1483     * If a distinct has been set the DISTINCT clause will be printed.
1484     * This is used internally by TopLink for batch reading but may also be
1485     * used directly for advanced queries or report queries.
1486     */

1487    public void setDistinctState(short distinctState) {
1488        this.distinctState = distinctState;
1489    }
1490
1491    /**
1492     * INTERNAL:
1493     * Set the fields, if any are aggregate selects then record this so that the distinct is not printed through anyOfs.
1494     */

1495    public void setFields(Vector fields) {
1496        for (Enumeration fieldsEnum = fields.elements(); fieldsEnum.hasMoreElements();) {
1497            Object JavaDoc fieldOrExpression = fieldsEnum.nextElement();
1498
1499            if (fieldOrExpression instanceof FunctionExpression) {
1500                if (((FunctionExpression)fieldOrExpression).getOperator().isAggregateOperator()) {
1501                    setIsAggregateSelect(true);
1502
1503                    break;
1504                }
1505            }
1506        }
1507
1508        this.fields = fields;
1509    }
1510
1511    public void setGroupByExpressions(Vector expressions) {
1512        this.groupByExpressions = expressions;
1513    }
1514
1515    public void setHavingExpression(Expression expressions) {
1516        this.havingExpression = expressions;
1517    }
1518
1519    /**
1520     * INTERNAL:
1521     * takes the hierarchical query expression which have been set on the query and sets them here
1522     * used to generate the Hierarchical Query Clause in the SQL
1523     */

1524    public void setHierarchicalQueryExpressions(Expression startWith, Expression connectBy, Vector orderSiblingsExpressions) {
1525        this.startWithExpression = startWith;
1526        this.connectByExpression = connectBy;
1527        this.orderSiblingsByExpressions = orderSiblingsExpressions;
1528    }
1529
1530    public void setIsAggregateSelect(boolean isAggregateSelect) {
1531        this.isAggregateSelect = isAggregateSelect;
1532    }
1533
1534    protected void setForUpdateClause(ForUpdateClause clause) {
1535        this.forUpdateClause = clause;
1536    }
1537
1538    public void setLockingClause(ForUpdateClause lockingClause) {
1539        this.forUpdateClause = lockingClause;
1540    }
1541
1542    public void setOrderByExpressions(Vector orderByExpressions) {
1543        this.orderByExpressions = orderByExpressions;
1544    }
1545
1546    public void setOuterJoinedAdditionalJoinCriteria(Vector outerJoinedAdditionalJoinCriteria) {
1547        this.outerJoinedAdditionalJoinCriteria = outerJoinedAdditionalJoinCriteria;
1548    }
1549
1550    public void setOuterJoinedMappingCriteria(Vector outerJoinedMappingCriteria) {
1551        this.outerJoinedMappingCriteria = outerJoinedMappingCriteria;
1552    }
1553
1554    public void setOuterJoinExpressions(Vector outerJoinedExpressions) {
1555        this.outerJoinedExpressions = outerJoinedExpressions;
1556    }
1557
1558    /**
1559     * Set the parent statement if using subselects.
1560     * This is used to normalize correctly with subselects.
1561     */

1562    public void setParentStatement(SQLSelectStatement parentStatement) {
1563        this.parentStatement = parentStatement;
1564    }
1565
1566    public void setRequiresAliases(boolean requiresAliases) {
1567        this.requiresAliases = requiresAliases;
1568    }
1569
1570    protected void setTableAliases(Hashtable theTableAliases) {
1571        tableAliases = theTableAliases;
1572    }
1573
1574    public void setTables(Vector theTables) {
1575        tables = theTables;
1576    }
1577
1578    /**
1579     * INTERNAL:
1580     * If a distinct has been set the DISTINCT clause will be printed.
1581     * This is required for batch reading.
1582     */

1583    public boolean shouldDistinctBeUsed() {
1584        return distinctState == ObjectLevelReadQuery.USE_DISTINCT;
1585    }
1586
1587    /**
1588     * ADVANCED:
1589     * If a distinct has been set the DISTINCT clause will be printed.
1590     * This is used internally by TopLink for batch reading but may also be
1591     * used directly for advanced queries or report queries.
1592     */

1593    public void useDistinct() {
1594        setDistinctState(ObjectLevelReadQuery.USE_DISTINCT);
1595    }
1596
1597    /**
1598     * INTERNAL:
1599     */

1600    protected void writeField(ExpressionSQLPrinter printer, DatabaseField field) {
1601        //print ", " before each selected field except the first one
1602
if (printer.isFirstElementPrinted()) {
1603            printer.printString(", ");
1604        } else {
1605            printer.setIsFirstElementPrinted(true);
1606        }
1607
1608        if (printer.shouldPrintQualifiedNames()) {
1609            if (field.getTable() != lastTable) {
1610                lastTable = field.getTable();
1611                currentAlias = getBuilder().aliasForTable(lastTable);
1612
1613                // This is really for the special case where things were pre-aliased
1614
if (currentAlias == null) {
1615                    currentAlias = lastTable;
1616                }
1617            }
1618
1619            printer.printString(currentAlias.getQualifiedName());
1620            printer.printString(".");
1621            printer.printString(field.getName());
1622        } else {
1623            printer.printString(field.getName());
1624        }
1625    }
1626
1627    /**
1628     * INTERNAL:
1629     */

1630    protected void writeFieldsFromExpression(ExpressionSQLPrinter printer, Expression expression, Vector newFields) {
1631        expression.writeFields(printer, newFields, this);
1632    }
1633
1634    /**
1635     * INTERNAL:
1636     */

1637    protected Vector writeFieldsIn(ExpressionSQLPrinter printer) {
1638        this.lastTable = null;
1639
1640        Vector newFields = oracle.toplink.essentials.internal.helper.NonSynchronizedVector.newInstance();
1641
1642        for (Enumeration fieldsEnum = getFields().elements(); fieldsEnum.hasMoreElements();) {
1643            Object JavaDoc next = fieldsEnum.nextElement();
1644
1645            if (next instanceof Expression) {
1646                writeFieldsFromExpression(printer, (Expression)next, newFields);
1647            } else {
1648                writeField(printer, (DatabaseField)next);
1649                newFields.addElement(next);
1650            }
1651        }
1652
1653        return newFields;
1654    }
1655
1656    /**
1657     * INTERNAL:
1658     * The method searches for expressions that join two tables each in a given expression.
1659     * Given expression and tablesInOrder and an empty SortedMap (TreeMap with no Comparator), this method
1660     * populates the map with expressions corresponding to two tables
1661     * keyed by an index (in tablesInOrder) of the table with the highest (of two) index;
1662     * returns all the participating in at least one of the expressions.
1663     * Example:
1664     * expression (joining Employee to Project through m-m mapping "projects"):
1665     * (employee.emp_id = proj_emp.emp_id) and (proj_emp.proj_id = project.proj_id)
1666     * tablesInOrder:
1667     * employee, proj_emp, project
1668     *
1669     * results:
1670     * map:
1671     * 1 -> (employee.emp_id = proj_emp.emp_id)
1672     * 2 -> (proj_emp.proj_id = project.proj_id)
1673     * returned SortedSet: {0, 1, 2}.
1674     *
1675     * Note that tablesInOrder must contain all tables used by expression
1676     */

1677    protected static SortedSet mapTableIndexToExpression(Expression expression, SortedMap map, Vector tablesInOrder) {
1678        TreeSet tables = new TreeSet();
1679        if(expression instanceof DataExpression) {
1680            DataExpression de = (DataExpression)expression;
1681            if(de.getField() != null) {
1682                tables.add(new Integer JavaDoc(tablesInOrder.indexOf(de.getField().getTable())));
1683            }
1684        } else if(expression instanceof ParameterExpression) {
1685            ParameterExpression pe = (ParameterExpression)expression;
1686            if(pe.getField() != null) {
1687                tables.add(new Integer JavaDoc(tablesInOrder.indexOf(pe.getField().getTable())));
1688            }
1689        } else if(expression instanceof CompoundExpression) {
1690            CompoundExpression ce = (CompoundExpression)expression;
1691            tables.addAll(mapTableIndexToExpression(ce.getFirstChild(), map, tablesInOrder));
1692            tables.addAll(mapTableIndexToExpression(ce.getSecondChild(), map, tablesInOrder));
1693        } else if(expression instanceof FunctionExpression) {
1694            FunctionExpression fe = (FunctionExpression)expression;
1695            Iterator it = fe.getChildren().iterator();
1696            while(it.hasNext()) {
1697                tables.addAll(mapTableIndexToExpression((Expression)it.next(), map, tablesInOrder));
1698            }
1699        }
1700        
1701        if(tables.size() == 2) {
1702            map.put(tables.last(), expression);
1703        }
1704        
1705        return tables;
1706    }
1707
1708    /**
1709     * INTERNAL:
1710     * The method searches for expressions that join two tables each in a given expression.
1711     * Given expression and tablesInOrder, this method
1712     * returns the map with expressions corresponding to two tables
1713     * keyed by tables (from tablesInOrder) with the highest (of two) index;
1714     * Example:
1715     * expression (joining Employee to Project through m-m mapping "projects"):
1716     * (employee.emp_id = proj_emp.emp_id) and (proj_emp.proj_id = project.proj_id)
1717     * tablesInOrder:
1718     * employee, proj_emp, project
1719     *
1720     * results:
1721     * returned map:
1722     * proj_emp -> (employee.emp_id = proj_emp.emp_id)
1723     * project -> (proj_emp.proj_id = project.proj_id)
1724     *
1725     * Note that tablesInOrder must contain all tables used by expression
1726     */

1727    public static Map mapTableToExpression(Expression expression, Vector tablesInOrder) {
1728        TreeMap indexToExpressionMap = new TreeMap();
1729        mapTableIndexToExpression(expression, indexToExpressionMap, tablesInOrder);
1730        HashMap map = new HashMap(indexToExpressionMap.size());
1731        Iterator it = indexToExpressionMap.entrySet().iterator();
1732        while(it.hasNext()) {
1733            Map.Entry entry = (Map.Entry)it.next();
1734            int index = ((Integer JavaDoc)entry.getKey()).intValue();
1735            map.put(tablesInOrder.elementAt(index), entry.getValue());
1736        }
1737        return map;
1738    }
1739}
1740
Popular Tags