FileDocCategorySizeDatePackage
SQLSelectStatement.javaAPI DocGlassfish v2 API80110Tue Jun 05 08:55:14 BST 2007oracle.toplink.essentials.internal.expressions

SQLSelectStatement

public class SQLSelectStatement extends SQLStatement

Purpose: Print SELECT statement.

Responsibilities:

  • Print SELECT statement.
author
Dorin Sandu
since
TOPLink/Java 1.0

Fields Summary
protected Vector
fields
Fields being selected (can include expressions).
protected Vector
nonSelectFields
Fields not being selected (can include expressions).
protected Vector
tables
Tables being selected from.
protected short
distinctState
Used for "Select Distinct" option.
protected Vector
orderByExpressions
Order by clause for read all queries.
protected Vector
groupByExpressions
Group by clause for report queries.
protected Expression
havingExpression
Having clause for report queries.
protected ForUpdateClause
forUpdateClause
Used for pessimistic locking ie. "For Update".
protected boolean
isAggregateSelect
Used for report query or counts so we know how to treat distincts.
protected Vector
outerJoinedExpressions
Used for DB2 style from clause outer joins.
protected Vector
outerJoinedMappingCriteria
protected Vector
outerJoinedAdditionalJoinCriteria
protected List
descriptorsForMultitableInheritanceOnly
protected Expression
startWithExpression
Used for Oracle Hierarchical Queries
protected Expression
connectByExpression
protected Vector
orderSiblingsByExpressions
protected boolean
requiresAliases
Variables used for aliasing and normalizing.
protected Hashtable
tableAliases
protected DatabaseTable
lastTable
protected DatabaseTable
currentAlias
protected int
currentAliasNumber
protected SQLSelectStatement
parentStatement
Used for subselects.
Constructors Summary
public SQLSelectStatement()

        this.fields = oracle.toplink.essentials.internal.helper.NonSynchronizedVector.newInstance(2);
        this.tables = oracle.toplink.essentials.internal.helper.NonSynchronizedVector.newInstance(4);
        this.requiresAliases = false;
        this.isAggregateSelect = false;
        this.distinctState = ObjectLevelReadQuery.UNCOMPUTED_DISTINCT;
        this.currentAliasNumber = 0;
    
Methods Summary
public voidaddField(oracle.toplink.essentials.internal.helper.DatabaseField field)

        getFields().addElement(field);
    
public voidaddField(oracle.toplink.essentials.expressions.Expression expression)
INTERNAL: adds an expression to the fields. set a flag if the expression is for and aggregate function.

        if (expression instanceof FunctionExpression) {
            if (((FunctionExpression)expression).getOperator().isAggregateOperator()) {
                setIsAggregateSelect(true);
            }
        }

        getFields().addElement(expression);
    
protected voidaddOrderByExpressionToSelectForDistinct()
When distinct is used with order by the ordered fields must be in the select clause.

        for (Enumeration orderExpressionsEnum = getOrderByExpressions().elements();
                 orderExpressionsEnum.hasMoreElements();) {
            Expression orderExpression = (Expression)orderExpressionsEnum.nextElement();
            Expression fieldExpression = null;

            if (orderExpression.isFunctionExpression() && (orderExpression.getOperator().isOrderOperator())) {
                fieldExpression = ((FunctionExpression)orderExpression).getBaseExpression();
            } else {
                fieldExpression = orderExpression;
            }

            // Changed to call a method to loop throught the fields vector and check each element
            // individually. Jon D. May 4, 2000 for pr 7811
            if ((fieldExpression.selectIfOrderedBy()) && !fieldsContainField(getFields(), fieldExpression)) {
                addField(fieldExpression);
            }
        }
    
public voidaddTable(oracle.toplink.essentials.internal.helper.DatabaseTable table)
Add a table to the statement. The table will be used in the FROM part of the SQL statement.

        if (!getTables().contains(table)) {
            getTables().addElement(table);
        }
    
public voidappendFromClauseForInformixOuterJoin(oracle.toplink.essentials.internal.expressions.ExpressionSQLPrinter printer, java.util.Vector outerJoinedAliases)
ADVANCED: If a platform is Informix, then the outer join must be in the FROM clause. This is used internally by TopLink for building Informix outer join syntax which differs from other platforms(Oracle,Sybase) that print the outer join in the WHERE clause and from DB2 which prints the. OuterJoinedAliases passed in to keep track of tables used for outer join so no normal join is given

        Writer writer = printer.getWriter();
        AbstractSession session = printer.getSession();

        // Print outer joins
        boolean firstTable = true;

        for (int index = 0; index < getOuterJoinExpressions().size(); index++) {
            QueryKeyExpression outerExpression = (QueryKeyExpression)getOuterJoinExpressions().elementAt(index);
            CompoundExpression relationExpression = (CompoundExpression)getOuterJoinedMappingCriteria().elementAt(index);// get expression for multiple table case

            // CR#3083929 direct collection/map mappings do not have reference descriptor.
            DatabaseTable targetTable = null;
            if (outerExpression.getMapping().isDirectCollectionMapping()) {
                targetTable = ((DirectCollectionMapping)outerExpression.getMapping()).getReferenceTable();
            } else {
                targetTable = (DatabaseTable)outerExpression.getMapping().getReferenceDescriptor().getTables().firstElement();
            }

            // Grab the source table from the mapping not just the first table 
            // from the descriptor. In an joined inheritance hierarchy, the
            // fk used in the outer join may be from a subclasses's table .
            DatabaseTable sourceTable;
            if (outerExpression.getMapping().isObjectReferenceMapping() && ((ObjectReferenceMapping) outerExpression.getMapping()).isForeignKeyRelationship()) {
                sourceTable = (DatabaseTable)((DatabaseField) outerExpression.getMapping().getFields().firstElement()).getTable();
            } else {
                sourceTable = (DatabaseTable)((ObjectExpression)outerExpression.getBaseExpression()).getDescriptor().getTables().firstElement();    
            }

            DatabaseTable sourceAlias = outerExpression.getBaseExpression().aliasForTable(sourceTable);
            DatabaseTable targetAlias = outerExpression.aliasForTable(targetTable);

            if (!(outerJoinedAliases.contains(sourceAlias) || outerJoinedAliases.contains(targetAlias))) {
                if (!firstTable) {
                    writer.write(", ");
                }

                firstTable = false;
                writer.write(sourceTable.getQualifiedName());
                outerJoinedAliases.addElement(sourceAlias);
                writer.write(" ");
                writer.write(sourceAlias.getQualifiedName());

                if (outerExpression.getMapping().isManyToManyMapping()) {// for many to many mappings, you need to do some funky stuff to get the relation table's alias
                    DatabaseTable newTarget = ((ManyToManyMapping)outerExpression.getMapping()).getRelationTable();
                    DatabaseTable newAlias = relationExpression.aliasForTable(newTarget);
                    writer.write(", OUTER ");// need to outer join only to relation table for many-to-many case in Informix
                    writer.write(newTarget.getQualifiedName());
                    writer.write(" ");
                    outerJoinedAliases.addElement(newAlias);
                    writer.write(newAlias.getQualifiedName());
                } else if (outerExpression.getMapping().isDirectCollectionMapping()) {// for many to many mappings, you need to do some funky stuff to get the relation table's alias
                    DatabaseTable newTarget = ((DirectCollectionMapping)outerExpression.getMapping()).getReferenceTable();
                    DatabaseTable newAlias = relationExpression.aliasForTable(newTarget);
                    writer.write(", OUTER ");
                    writer.write(newTarget.getQualifiedName());
                    writer.write(" ");
                    outerJoinedAliases.addElement(newAlias);
                    writer.write(newAlias.getQualifiedName());
                } else {// do normal outer stuff for Informix
                    for (Enumeration target = outerExpression.getMapping().getReferenceDescriptor().getTables().elements();
                             target.hasMoreElements();) {
                        DatabaseTable newTarget = (DatabaseTable)target.nextElement();
                        Expression onExpression = outerExpression;
                        DatabaseTable newAlias = outerExpression.aliasForTable(newTarget);
                        writer.write(", OUTER ");
                        writer.write(newTarget.getQualifiedName());
                        writer.write(" ");
                        outerJoinedAliases.addElement(newAlias);
                        writer.write(newAlias.getQualifiedName());
                    }
                }
            }
        }
    
public voidappendFromClauseForOuterJoin(oracle.toplink.essentials.internal.expressions.ExpressionSQLPrinter printer, java.util.Vector outerJoinedAliases)
ADVANCED: If a platform is DB2 or MySQL, then the outer join must be in the FROM clause. This is used internally by TopLink for building DB2 outer join syntax which differs from other platforms(Oracle,Sybase) that print the outer join in the WHERE clause. OuterJoinedAliases passed in to keep track of tables used for outer join so no normal join is given

        Writer writer = printer.getWriter();
        AbstractSession session = printer.getSession();

        // Print outer joins
        boolean firstTable = true;
        boolean requiresEscape = false;//checks if the jdbc closing escape syntax is needed

        OuterJoinExpressionHolders outerJoinExpressionHolders = new OuterJoinExpressionHolders();
        for (int index = 0; index < getOuterJoinExpressions().size(); index++) {
            QueryKeyExpression outerExpression = (QueryKeyExpression)getOuterJoinExpressions().elementAt(index);

            // CR#3083929 direct collection/map mappings do not have reference descriptor.
            DatabaseTable targetTable = null;
            DatabaseTable sourceTable = null;
            DatabaseTable sourceAlias = null;
            DatabaseTable targetAlias = null;
            if(outerExpression != null) {
                if (outerExpression.getMapping().isDirectCollectionMapping()) {
                    targetTable = ((DirectCollectionMapping)outerExpression.getMapping()).getReferenceTable();
                } else {
                    targetTable = (DatabaseTable)outerExpression.getMapping().getReferenceDescriptor().getTables().firstElement();
                }
                
                // Grab the source table from the mapping not just the first table 
                // from the descriptor. In an joined inheritance hierarchy, the
                // fk used in the outer join may be from a subclasses's table.
                if (outerExpression.getMapping().isObjectReferenceMapping() && ((ObjectReferenceMapping) outerExpression.getMapping()).isForeignKeyRelationship()) {
                    sourceTable = (DatabaseTable)((DatabaseField) outerExpression.getMapping().getFields().firstElement()).getTable();
                } else {
                    sourceTable = (DatabaseTable)((ObjectExpression)outerExpression.getBaseExpression()).getDescriptor().getTables().firstElement();    
                }
                
                sourceAlias = outerExpression.getBaseExpression().aliasForTable(sourceTable);
                targetAlias = outerExpression.aliasForTable(targetTable);
            } else {
                sourceTable = (DatabaseTable)((ClassDescriptor)getDescriptorsForMultitableInheritanceOnly().get(index)).getTables().firstElement();
                targetTable = (DatabaseTable)((ClassDescriptor)getDescriptorsForMultitableInheritanceOnly().get(index)).getInheritancePolicy().getChildrenTables().get(0);
                Expression exp = (Expression)((Map)getOuterJoinedAdditionalJoinCriteria().elementAt(index)).get(targetTable);
                sourceAlias = exp.aliasForTable(sourceTable);
                targetAlias = exp.aliasForTable(targetTable);
            }

            outerJoinExpressionHolders.add(
                new OuterJoinExpressionHolder(outerExpression, index, targetTable, 
                                              sourceTable, targetAlias, sourceAlias));
        }
        
        for (Iterator i = outerJoinExpressionHolders.linearize().iterator(); i.hasNext();) {
            OuterJoinExpressionHolder holder = (OuterJoinExpressionHolder)i.next();
            QueryKeyExpression outerExpression = holder.joinExpression;
            int index = holder.index;
            DatabaseTable targetTable = holder.targetTable;
            DatabaseTable sourceTable = holder.sourceTable;
            DatabaseTable sourceAlias = holder.sourceAlias;
            DatabaseTable targetAlias = holder.targetAlias;

            if (!outerJoinedAliases.contains(targetAlias)) {
                if (!outerJoinedAliases.contains(sourceAlias)) {
                    if (requiresEscape && session.getPlatform().shouldUseJDBCOuterJoinSyntax()) {
                        writer.write("}");
                    }

                    if (!firstTable) {
                        writer.write(",");
                    }

                    if (session.getPlatform().shouldUseJDBCOuterJoinSyntax()) {
                        writer.write(session.getPlatform().getJDBCOuterJoinString());
                    }

                    requiresEscape = true;
                    firstTable = false;
                    writer.write(sourceTable.getQualifiedName());
                    outerJoinedAliases.addElement(sourceAlias);
                    writer.write(" ");
                    writer.write(sourceAlias.getQualifiedName());
                }

                if(outerExpression == null) {
                    printAdditionalJoins(printer, outerJoinedAliases, (ClassDescriptor)getDescriptorsForMultitableInheritanceOnly().get(index), (Map)getOuterJoinedAdditionalJoinCriteria().elementAt(index));
                } else if (outerExpression.getMapping().isDirectCollectionMapping()) {
                    // Append the join clause,
                    // If this is a direct collection, join to direct table.
                    Expression onExpression = (Expression)getOuterJoinedMappingCriteria().elementAt(index);

                    DatabaseTable newAlias = onExpression.aliasForTable(targetTable);
                    writer.write(" LEFT OUTER JOIN ");
                    writer.write(targetTable.getQualifiedName());
                    writer.write(" ");
                    outerJoinedAliases.addElement(newAlias);
                    writer.write(newAlias.getQualifiedName());
                    writer.write(" ON ");

                    if (session.getPlatform() instanceof DB2MainframePlatform) {
                        ((RelationExpression)onExpression).printSQLNoParens(printer);
                    } else {
                        onExpression.printSQL(printer);
                    }

                    //Bug#4240751 Treat ManyToManyMapping separately for out join
                } else if (outerExpression.getMapping().isManyToManyMapping()) {
                    // Must outerjoin each of the targets tables.
                    // The first table is joined with the mapping join criteria,
                    // the rest of the tables are joined with the additional join criteria.
                    // 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)
                    DatabaseTable relationTable = ((ManyToManyMapping)outerExpression.getMapping()).getRelationTable();
                    DatabaseTable relationAlias = ((Expression)getOuterJoinedMappingCriteria().elementAt(index)).aliasForTable(relationTable);
                    writer.write(" LEFT OUTER JOIN (");
                    
                    writer.write(relationTable.getQualifiedName());
                    writer.write(" ");
                    outerJoinedAliases.addElement(relationAlias);
                    writer.write(relationAlias.getQualifiedName());
                    
                    Vector tablesInOrder = oracle.toplink.essentials.internal.helper.NonSynchronizedVector.newInstance(3);
                    // glassfish issue 2440: store aliases instead of tables
                    // in the tablesInOrder. This allows to distinguish source
                    // and target table in case of an self referencing relationship.
                    tablesInOrder.add(sourceAlias);
                    tablesInOrder.add(relationAlias);
                    tablesInOrder.add(targetAlias);
                    TreeMap indexToExpressionMap = new TreeMap();
                    mapTableIndexToExpression((Expression)getOuterJoinedMappingCriteria().elementAt(index), indexToExpressionMap, tablesInOrder);
                    Expression sourceToRelationJoin = (Expression)indexToExpressionMap.get(new Integer(1));
                    Expression relationToTargetJoin = (Expression)indexToExpressionMap.get(new Integer(2));
                    
                    writer.write(" JOIN ");
                    writer.write(targetTable.getQualifiedName());
                    writer.write(" ");
                    outerJoinedAliases.addElement(targetAlias);
                    writer.write(targetAlias.getQualifiedName());
                    writer.write(" ON ");
                    if (session.getPlatform() instanceof DB2MainframePlatform) {
                        ((RelationExpression)relationToTargetJoin).printSQLNoParens(printer);
                    } else {
                        relationToTargetJoin.printSQL(printer);
                    }
                    
                    Map tablesJoinExpression = (Map)getOuterJoinedAdditionalJoinCriteria().elementAt(index);
                    if(tablesJoinExpression != null && !tablesJoinExpression.isEmpty()) {
                        printAdditionalJoins(printer, outerJoinedAliases, outerExpression.getMapping().getReferenceDescriptor(), tablesJoinExpression);
                    }
                    writer.write(") ON ");
                    if (session.getPlatform() instanceof DB2MainframePlatform) {
                        ((RelationExpression)sourceToRelationJoin).printSQLNoParens(printer);
                    } else {
                        sourceToRelationJoin.printSQL(printer);
                    }
                } else {
                    // Must outerjoin each of the targets tables.
                    // The first table is joined with the mapping join criteria,
                    // the rest of the tables are joined with the additional join criteria.
                    writer.write(" LEFT OUTER JOIN ");
                    Map tablesJoinExpression = (Map)getOuterJoinedAdditionalJoinCriteria().elementAt(index);
                    boolean hasAdditionalJoinExpressions = tablesJoinExpression != null && !tablesJoinExpression.isEmpty();
                    if(hasAdditionalJoinExpressions) {
                        writer.write("(");
                    }
                    writer.write(targetTable.getQualifiedName());
                    writer.write(" ");
                    outerJoinedAliases.addElement(targetAlias);
                    writer.write(targetAlias.getQualifiedName());
                    if(hasAdditionalJoinExpressions) {
                        printAdditionalJoins(printer, outerJoinedAliases, outerExpression.getMapping().getReferenceDescriptor(), tablesJoinExpression);
                        writer.write(")");
                    }
                    writer.write(" ON ");
                    Expression sourceToTargetJoin = (Expression)getOuterJoinedMappingCriteria().elementAt(index);
                    if (session.getPlatform() instanceof DB2MainframePlatform) {
                        ((RelationExpression)sourceToTargetJoin).printSQLNoParens(printer);
                    } else {
                        sourceToTargetJoin.printSQL(printer);
                    }
                }
            }
        }

        if (requiresEscape && session.getPlatform().shouldUseJDBCOuterJoinSyntax()) {
            writer.write("}");
        }
    
public voidappendFromClauseToWriter(oracle.toplink.essentials.internal.expressions.ExpressionSQLPrinter printer)
Print the from clause. This includes outer joins, these must be printed before the normal join to ensure that the source tables are not joined again. Outer joins are not printed in the FROM clause on Oracle or Sybase.

        Writer writer = printer.getWriter();
        AbstractSession session = printer.getSession();
        writer.write(" FROM ");

        // Print outer joins
        boolean firstTable = true;
        Vector outerJoinedAliases = new Vector(1);// Must keep track of tables used for outer join so no normal join is given

        if (hasOuterJoinExpressions()) {
            if (session.getPlatform().isInformixOuterJoin()) {
                appendFromClauseForInformixOuterJoin(printer, outerJoinedAliases);
            } else if (!session.getPlatform().shouldPrintOuterJoinInWhereClause()) {
                appendFromClauseForOuterJoin(printer, outerJoinedAliases);
            }

            firstTable = false;
        }

        // If there are no table aliases it means the query was malformed,
        // most likely the wrong builder was used, or wrong builder on the left in a sub-query.
        if (getTableAliases().isEmpty()) {
            throw QueryException.invalidBuilderInQuery(null);// Query is set in execute.
        }

        // Print tables for normal join
        for (Enumeration aliasesEnum = getTableAliases().keys(); aliasesEnum.hasMoreElements();) {
            DatabaseTable alias = (DatabaseTable)aliasesEnum.nextElement();

            if (!outerJoinedAliases.contains(alias)) {
                DatabaseTable table = (DatabaseTable)getTableAliases().get(alias);

                if (requiresAliases()) {
                    if (!firstTable) {
                        writer.write(", ");
                    }

                    firstTable = false;
                    writer.write(table.getQualifiedName());
                    writer.write(" ");
                    writer.write(alias.getQualifiedName());
                } else {
                    writer.write(table.getQualifiedName());
                }
            }
        }
    
public voidappendGroupByClauseToWriter(oracle.toplink.essentials.internal.expressions.ExpressionSQLPrinter printer)
This method will append the group by clause to the end of the select statement.

        if (getGroupByExpressions().isEmpty()) {
            return;
        }

        printer.getWriter().write(" GROUP BY ");

        Vector newFields = new Vector();
        // to avoid printing a comma before the first field
        printer.setIsFirstElementPrinted(false);
        for (Enumeration expressionsEnum = getGroupByExpressions().elements();
                 expressionsEnum.hasMoreElements();) {
            Expression expression = (Expression)expressionsEnum.nextElement();            
            writeFieldsFromExpression(printer, expression, newFields);
        }
    
public voidappendHierarchicalQueryClauseToWriter(oracle.toplink.essentials.internal.expressions.ExpressionSQLPrinter printer)
This method will append the Hierarchical Query Clause to the end of the select statement

        Expression startWith = getStartWithExpression();
        Expression connectBy = getConnectByExpression();
        Vector orderSiblingsBy = getOrderSiblingsByExpressions();

        //Create the START WITH CLAUSE
        if (startWith != null) {
            printer.getWriter().write(" START WITH ");
            startWith.printSQL(printer);
        }

        if (connectBy != null) {
            if (!connectBy.isQueryKeyExpression()) {
                throw QueryException.illFormedExpression(connectBy);
            }

            printer.getWriter().write(" CONNECT BY ");

            DatabaseMapping mapping = ((QueryKeyExpression)connectBy).getMapping();
            ClassDescriptor descriptor = mapping.getDescriptor();

            //only works for these kinds of mappings. The data isn't hierarchical otherwise
            //Should also check that the source class and target class are the same.
            Map foreignKeys = null;

            if (mapping.isOneToManyMapping()) {
                OneToManyMapping otm = (OneToManyMapping)mapping;
                foreignKeys = otm.getTargetForeignKeyToSourceKeys();
            } else if (mapping.isOneToOneMapping()) {
                OneToOneMapping oto = (OneToOneMapping)mapping;
                foreignKeys = oto.getSourceToTargetKeyFields();
            } else if (mapping.isAggregateCollectionMapping()) {
                AggregateCollectionMapping acm = (AggregateCollectionMapping)mapping;
                foreignKeys = acm.getTargetForeignKeyToSourceKeys();
            } else {
                throw QueryException.invalidQueryKeyInExpression(connectBy);
            }

            DatabaseTable defaultTable = descriptor.getDefaultTable();
            String tableName = "";

            //determine which table name to use
            if (requiresAliases()) {
                tableName = getBuilder().aliasForTable(defaultTable).getName();
            } else {
                tableName = defaultTable.getName();
            }

            if ((foreignKeys != null) && !foreignKeys.isEmpty()) {
                //get the source and target fields. 
                Iterator sourceKeys = foreignKeys.keySet().iterator();

                //for each source field, get the target field and create the link. If there's
                //only one, use the simplest version without ugly bracets
                if (foreignKeys.size() > 1) {
                    printer.getWriter().write("((");
                }

                DatabaseField source = (DatabaseField)sourceKeys.next();
                DatabaseField target = (DatabaseField)foreignKeys.get(source);

                //OneToOneMappings -> Source in parent object goes to target in child object 
                if (mapping.isOneToOneMapping()) {
                    printer.getWriter().write("PRIOR " + tableName + "." + source.getName());
                    printer.getWriter().write(" = " + tableName + "." + target.getName());
                } else {//collections are the opposite way
                    printer.getWriter().write(tableName + "." + source.getName());
                    printer.getWriter().write(" = PRIOR " + tableName + "." + target.getName());
                }

                while (sourceKeys.hasNext()) {
                    printer.getWriter().write(") AND (");
                    source = (DatabaseField)sourceKeys.next();
                    target = (DatabaseField)foreignKeys.get(source);

                    if (mapping.isOneToOneMapping()) {
                        printer.getWriter().write("PRIOR " + tableName + "." + source.getName());
                        printer.getWriter().write(" = " + tableName + "." + target.getName());
                    } else {//collections are the opposite way
                        printer.getWriter().write(tableName + "." + source.getName());
                        printer.getWriter().write(" = PRIOR " + tableName + "." + target.getName());
                    }
                }

                if (foreignKeys.size() > 1) {
                    printer.getWriter().write("))");
                }
            }
        }

        if (orderSiblingsBy != null) {
            printer.getWriter().write(" ORDER SIBLINGS BY ");

            for (Enumeration expressionEnum = orderSiblingsBy.elements();
                     expressionEnum.hasMoreElements();) {
                Expression ex = (Expression)expressionEnum.nextElement();
                ex.printSQL(printer);

                if (expressionEnum.hasMoreElements()) {
                    printer.getWriter().write(", ");
                }
            }
        }
    
public voidappendOrderClauseToWriter(oracle.toplink.essentials.internal.expressions.ExpressionSQLPrinter printer)
This method will append the order clause to the end of the select statement.

        if (!hasOrderByExpressions()) {
            return;
        }

        printer.getWriter().write(" ORDER BY ");

        for (Enumeration expressionsEnum = getOrderByExpressions().elements();
                 expressionsEnum.hasMoreElements();) {
            Expression expression = (Expression)expressionsEnum.nextElement();
            expression.printSQL(printer);

            if (expressionsEnum.hasMoreElements()) {
                printer.getWriter().write(", ");
            }
        }
    
public voidassignAliases(java.util.Vector allExpressions)
INTERNAL: Alias the tables in all of our nodes.

        // For sub-selects all statements must share aliasing information.
        // For  CR#2627019
        currentAliasNumber = getCurrentAliasNumber();

        ExpressionIterator iterator = new ExpressionIterator() {
            public void iterate(Expression each) {
                currentAliasNumber = each.assignTableAliasesStartingAt(currentAliasNumber);
            }
        };

        if (allExpressions.isEmpty()) {
            // bug 3878553 - ensure aliases are always assigned for when required .
            if ((getBuilder() != null) && requiresAliases()) {
                getBuilder().assignTableAliasesStartingAt(currentAliasNumber);
            }
        } else {
            for (Enumeration expressionEnum = allExpressions.elements();
                     expressionEnum.hasMoreElements();) {
                Expression expression = (Expression)expressionEnum.nextElement();
                iterator.iterateOn(expression);
            }
        }

        // For sub-selects update aliasing information of all statements.
        // For  CR#2627019
        setCurrentAliasNumber(currentAliasNumber);
    
public oracle.toplink.essentials.internal.databaseaccess.DatabaseCallbuildCall(oracle.toplink.essentials.internal.sessions.AbstractSession session)
Print the SQL representation of the statement on a stream.

        SQLCall call = new SQLCall();
        call.returnManyRows();

        Writer writer = new CharArrayWriter(200);

        ExpressionSQLPrinter printer = new ExpressionSQLPrinter(session, getTranslationRow(), call, requiresAliases());
        printer.setWriter(writer);

        call.setFields(printSQL(printer));
        call.setSQLString(writer.toString());

        return call;
    
public voidcomputeDistinct()
INTERNAL: This is used by cursored stream to determine if an expression used distinct as the size must account for this.

        ExpressionIterator iterator = new ExpressionIterator() {
            public void iterate(Expression expression) {
                if (expression.isQueryKeyExpression() && ((QueryKeyExpression)expression).shouldQueryToManyRelationship()) {
                    // Aggregate should only use distinct as specified by the user.
                    if (!isDistinctComputed()) {
                        useDistinct();
                    }
                }
            }
        };

        if (getWhereClause() != null) {
            iterator.iterateOn(getWhereClause());
        }
    
public voidcomputeTables()
INTERNAL: Computes all aliases which will appear in the FROM clause.

        // Compute tables should never defer to computeTablesFromTables
        // This iterator will pull all the table aliases out of an expression, and
        // put them in a hashtable.
        ExpressionIterator iterator = new ExpressionIterator() {
            public void iterate(Expression each) {
                TableAliasLookup aliases = each.getTableAliases();

                if (aliases != null) {
                    // Insure that an aliased table is only added to a single
                    // FROM clause.
                    if (!aliases.haveBeenAddedToStatement()) {
                        aliases.addToHashtable((Hashtable)getResult());
                        aliases.setHaveBeenAddedToStatement(true);
                    }
                }
            }
        };

        iterator.setResult(new Hashtable(5));

        if (getWhereClause() != null) {
            iterator.iterateOn(getWhereClause());
        } else if (hasOuterJoinExpressions()) {
            Expression outerJoinCriteria = (Expression)getOuterJoinedMappingCriteria().firstElement();
            if (outerJoinCriteria != null){
                iterator.iterateOn(outerJoinCriteria);
            }
        }

        //Iterate on fields as well in that rare case where the select is not in the where clause
        for (Iterator fields = getFields().iterator(); fields.hasNext();) {
            Object field = fields.next();
            if (field instanceof Expression) {
                iterator.iterateOn((Expression)field);
            }
        }

        // Always iterator on the builder, as the where clause may not contain the builder, i.e. value=value.
        iterator.iterateOn(getBuilder());

        Hashtable allTables = (Hashtable)iterator.getResult();
        setTableAliases(allTables);

        for (Enumeration e = allTables.elements(); e.hasMoreElements();) {
            addTable((DatabaseTable)e.nextElement());
        }
    
public voidcomputeTablesFromTables()
If there is no where clause, alias the tables from the tables list directly. Assume there's no ambiguity

        Hashtable allTables = new Hashtable();

        for (int index = 0; index < getTables().size(); index++) {
            DatabaseTable next = (DatabaseTable)getTables().elementAt(index);
            DatabaseTable alias = new DatabaseTable("t" + (index));
            allTables.put(alias, next);
        }

        setTableAliases(allTables);
    
public voiddontUseDistinct()
ADVANCED: If a distinct has been set the DISTINCT clause will be printed. This is used internally by TopLink for batch reading but may also be used directly for advanced queries or report queries.

        setDistinctState(ObjectLevelReadQuery.DONT_USE_DISTINCT);
    
protected booleanfieldsContainField(java.util.Vector fields, oracle.toplink.essentials.expressions.Expression expression)
Check if the field from the field expression is already contained in the select clause of the statement. This is used on order by expression when the field being ordered by must be in the select, but cannot be in the select twice.

        DatabaseField orderByField;

        if (expression instanceof DataExpression) {
            orderByField = ((DataExpression)expression).getField();
        } else {
            return false;
        }

        //check all fields for a match
        for (Enumeration fieldsEnum = fields.elements(); fieldsEnum.hasMoreElements();) {
            Object fieldOrExpression = fieldsEnum.nextElement();

            if (fieldOrExpression instanceof DatabaseField) {
                DatabaseField field = (DatabaseField)fieldOrExpression;
                DataExpression exp = (DataExpression)expression;

                if (field.equals(orderByField) && (exp.getBaseExpression() == getBuilder())) {
                    // found a match
                    return true;
                }
            }
            // For CR#2589.  This method was not getting the fields in the same way that
            // printSQL does (i.e. using getFields() instead of getField()).
            // The problem was that getField() on an expression builder led to a null pointer
            // exception.
            else {
                Expression exp = (Expression)fieldOrExpression;
                DatabaseTable table = orderByField.getTable();

                if (exp.getFields().contains(orderByField) && (expression.aliasForTable(table).equals(exp.aliasForTable(table)))) {
                    //found a match
                    return true;
                }
            }
        }

        // no matches
        return false;
    
public oracle.toplink.essentials.expressions.ExpressiongetConnectByExpression()
INTERNAL: Return the CONNECT BY expression

        return connectByExpression;
    
public intgetCurrentAliasNumber()
Gets a unique id that will be used to alias the next table. For sub-selects all must use this same aliasing information, maintained in the root enclosing statement. For CR#2627019

        if (getParentStatement() != null) {
            return getParentStatement().getCurrentAliasNumber();
        } else {
            return currentAliasNumber;
        }
    
public java.util.ListgetDescriptorsForMultitableInheritanceOnly()

        if (descriptorsForMultitableInheritanceOnly == null) {
            descriptorsForMultitableInheritanceOnly = new ArrayList(3);
        }

        return descriptorsForMultitableInheritanceOnly;
    
public java.util.VectorgetFields()
INTERNAL: Return all the fields

        return fields;
    
protected oracle.toplink.essentials.internal.expressions.ForUpdateClausegetForUpdateClause()

        return forUpdateClause;
    
public java.util.VectorgetGroupByExpressions()
INTERNAL: Return the group bys.

        if (groupByExpressions == null) {
            groupByExpressions = oracle.toplink.essentials.internal.helper.NonSynchronizedVector.newInstance(3);
        }

        return groupByExpressions;
    
public oracle.toplink.essentials.expressions.ExpressiongetHavingExpression()
INTERNAL: Return the having expression.

        return havingExpression;
    
public java.util.VectorgetNonSelectFields()
Return the fields we don't want to select but want to join on.

        return nonSelectFields;
    
public java.util.VectorgetOrderByExpressions()
INTERNAL: Return the order expressions for the query.

        if (orderByExpressions == null) {
            orderByExpressions = oracle.toplink.essentials.internal.helper.NonSynchronizedVector.newInstance(3);
        }

        return orderByExpressions;
    
public java.util.VectorgetOrderSiblingsByExpressions()
INTERNAL: Return the ORDER SIBLINGS BY expression

        return orderSiblingsByExpressions;
    
public java.util.VectorgetOuterJoinExpressions()

        if (outerJoinedExpressions == null) {
            outerJoinedExpressions = oracle.toplink.essentials.internal.helper.NonSynchronizedVector.newInstance(3);
        }

        return outerJoinedExpressions;
    
public java.util.VectorgetOuterJoinedAdditionalJoinCriteria()
INTERNAL: Each Vector's element is a map of tables join expressions keyed by the tables

        if (outerJoinedAdditionalJoinCriteria == null) {
            outerJoinedAdditionalJoinCriteria = oracle.toplink.essentials.internal.helper.NonSynchronizedVector.newInstance(3);
        }

        return outerJoinedAdditionalJoinCriteria;
    
public java.util.VectorgetOuterJoinedMappingCriteria()

        if (outerJoinedMappingCriteria == null) {
            outerJoinedMappingCriteria = oracle.toplink.essentials.internal.helper.NonSynchronizedVector.newInstance(3);
        }

        return outerJoinedMappingCriteria;
    
public oracle.toplink.essentials.internal.expressions.SQLSelectStatementgetParentStatement()
Return the parent statement if using subselects. This is used to normalize correctly with subselects.

        return parentStatement;
    
public oracle.toplink.essentials.expressions.ExpressiongetStartWithExpression()
INTERNAL: Return the StartWith expression

        return startWithExpression;
    
public java.util.HashtablegetTableAliases()
INTERNAL: Return the aliases used.

        return tableAliases;
    
public java.util.VectorgetTables()
INTERNAL: Return all the tables.

        return tables;
    
protected booleanhasAliasForTable(oracle.toplink.essentials.internal.helper.DatabaseTable table)

        if (tableAliases != null) {
            return getTableAliases().containsKey(table);
        }

        return false;
    
public booleanhasConnectByExpression()

        return connectByExpression != null;
    
public booleanhasGroupByExpressions()

        return (groupByExpressions != null) && (!groupByExpressions.isEmpty());
    
public booleanhasHavingExpression()

        return (havingExpression != null);
    
public booleanhasHierarchicalQueryExpressions()

        return ((startWithExpression != null) || (connectByExpression != null) || (orderSiblingsByExpressions != null));
    
public booleanhasNonSelectFields()

        return (nonSelectFields != null) && (!nonSelectFields.isEmpty());
    
public booleanhasOrderByExpressions()

        return (orderByExpressions != null) && (!orderByExpressions.isEmpty());
    
public booleanhasOrderSiblingsByExpressions()

        return orderSiblingsByExpressions != null;
    
public booleanhasOuterJoinExpressions()

        return (outerJoinedExpressions != null) && (!outerJoinedExpressions.isEmpty());
    
public booleanhasOuterJoinedAdditionalJoinCriteria()

        return (outerJoinedAdditionalJoinCriteria != null) && (!outerJoinedAdditionalJoinCriteria.isEmpty());
    
public booleanhasStartWithExpression()

        return startWithExpression != null;
    
public booleanisAggregateSelect()
INTERNAL:

        return isAggregateSelect;
    
public booleanisDistinctComputed()
INTERNAL: return true if this query has computed its distinct value already

        return distinctState != ObjectLevelReadQuery.UNCOMPUTED_DISTINCT;
    
public booleanisSubSelect()

        return (getParentStatement() != null);
    
protected static java.util.SortedSetmapTableIndexToExpression(oracle.toplink.essentials.expressions.Expression expression, java.util.SortedMap map, java.util.Vector tablesInOrder)
INTERNAL: The method searches for expressions that join two tables each in a given expression. Given expression and tablesInOrder and an empty SortedMap (TreeMap with no Comparator), this method populates the map with expressions corresponding to two tables keyed by an index (in tablesInOrder) of the table with the highest (of two) index; returns all the participating in at least one of the expressions. Example: expression (joining Employee to Project through m-m mapping "projects"): (employee.emp_id = proj_emp.emp_id) and (proj_emp.proj_id = project.proj_id) tablesInOrder: employee, proj_emp, project results: map: 1 -> (employee.emp_id = proj_emp.emp_id) 2 -> (proj_emp.proj_id = project.proj_id) returned SortedSet: {0, 1, 2}. Note that tablesInOrder must contain all tables used by expression

        // glassfish issue 2440: 
        // - Use DataExpression.getAliasedField instead of getField. This
        // allows to distinguish source and target tables in case of a self
        // referencing relationship.
        // - Removed the block handling ParameterExpressions, because it is
        // not possible to get into that method with a ParameterExpression.
        TreeSet tables = new TreeSet();
        if(expression instanceof DataExpression) {
            DataExpression de = (DataExpression)expression;
            if(de.getAliasedField() != null) {
                tables.add(new Integer(tablesInOrder.indexOf(de.getAliasedField().getTable())));
            }
        } else if(expression instanceof CompoundExpression) {
            CompoundExpression ce = (CompoundExpression)expression;
            tables.addAll(mapTableIndexToExpression(ce.getFirstChild(), map, tablesInOrder));
            tables.addAll(mapTableIndexToExpression(ce.getSecondChild(), map, tablesInOrder));
        } else if(expression instanceof FunctionExpression) {
            FunctionExpression fe = (FunctionExpression)expression;
            Iterator it = fe.getChildren().iterator();
            while(it.hasNext()) {
                tables.addAll(mapTableIndexToExpression((Expression)it.next(), map, tablesInOrder));
            }
        }
        
        if(tables.size() == 2) {
            map.put(tables.last(), expression);
        }
        
        return tables;
    
public static java.util.MapmapTableToExpression(oracle.toplink.essentials.expressions.Expression expression, java.util.Vector tablesInOrder)
INTERNAL: The method searches for expressions that join two tables each in a given expression. Given expression and tablesInOrder, this method returns the map with expressions corresponding to two tables keyed by tables (from tablesInOrder) with the highest (of two) index; Example: expression (joining Employee to Project through m-m mapping "projects"): (employee.emp_id = proj_emp.emp_id) and (proj_emp.proj_id = project.proj_id) tablesInOrder: employee, proj_emp, project results: returned map: proj_emp -> (employee.emp_id = proj_emp.emp_id) project -> (proj_emp.proj_id = project.proj_id) Note that tablesInOrder must contain all tables used by expression

        TreeMap indexToExpressionMap = new TreeMap();
        mapTableIndexToExpression(expression, indexToExpressionMap, tablesInOrder);
        HashMap map = new HashMap(indexToExpressionMap.size());
        Iterator it = indexToExpressionMap.entrySet().iterator();
        while(it.hasNext()) {
            Map.Entry entry = (Map.Entry)it.next();
            int index = ((Integer)entry.getKey()).intValue();
            map.put(tablesInOrder.elementAt(index), entry.getValue());
        }
        return map;
    
public final voidnormalize(oracle.toplink.essentials.internal.sessions.AbstractSession session, oracle.toplink.essentials.descriptors.ClassDescriptor descriptor)
INTERNAL: Normalize an expression into a printable structure. i.e. merge the expression with the join expressions. Also replace table names with corresponding aliases.

param
clonedExpressions

        // 2612538 - the default size of IdentityHashtable (32) is appropriate
        normalize(session, descriptor, new IdentityHashtable());
    
public voidnormalize(oracle.toplink.essentials.internal.sessions.AbstractSession session, oracle.toplink.essentials.descriptors.ClassDescriptor descriptor, java.util.Dictionary clonedExpressions)
INTERNAL: Normalize an expression into a printable structure. i.e. merge the expression with the join expressions. Also replace table names with corresponding aliases.

param
clonedExpressions With 2612185 allows additional expressions from multiple bases to be rebuilt on the correct cloned base.

        // Initialize the builder.
        if (getBuilder() == null) {
            if (getWhereClause() == null) {
                setBuilder(new ExpressionBuilder());
            } else {
                setBuilder(getWhereClause().getBuilder());
            }
        }

        ExpressionBuilder builder = getBuilder();

        // For flashback: The builder is increasingly important.  It can store
        // an AsOfClause, needs to be normalized for history, and aliased for
        // pessimistic locking to work.  Hence everything that would have
        // been applied to the where clause will be applied to the builder if
        // the former is null.  In the past though if there was no where
        // clause just threw away the builder (to get to this point we had to
        // pass it in via the vacated where clause), and neither normalized nor
        // aliased it directly.
        if (getWhereClause() == builder) {
            setWhereClause(null);
        }

        builder.setSession(session.getRootSession(null));

        // Some queries are not on objects but for data, thus no descriptor.
        if (!builder.doesNotRepresentAnObjectInTheQuery()) {
            if (descriptor != null) {
                Class queryClass = builder.getQueryClass();
                // GF 2333 Only change the descriptor class if it is not set, or if this is an inheritance query
                if ((queryClass == null) || descriptor.isChildDescriptor()) {
                    builder.setQueryClass(descriptor.getJavaClass());
                }
            }
        }

        // Compute all other expressions used other than the where clause, i.e. select, order by, group by.
        // Also must ensure that all expression use a unique builder.
        Vector allExpressions = new Vector();

        // Process select expressions.
        rebuildAndAddExpressions(getFields(), allExpressions, builder, clonedExpressions);

        // Process non-select expressions
        if (hasNonSelectFields()) {
            rebuildAndAddExpressions(getNonSelectFields(), allExpressions, builder, clonedExpressions);
        }
        
        // Process group by expressions.
        if (hasGroupByExpressions()) {
            rebuildAndAddExpressions(getGroupByExpressions(), allExpressions, builder, clonedExpressions);
        }

        if (hasHavingExpression()) {
            //rebuildAndAddExpressions(getHavingExpression(), allExpressions, builder, clonedExpressions);
            Expression expression = getHavingExpression();
            ExpressionBuilder originalBuilder = expression.getBuilder();
            if (originalBuilder != builder) {
                // For bug 2612185 avoid rebuildOn if possible as it rebuilds all on a single base.
                // i.e. Report query items could be from parallel expressions.
                if (clonedExpressions.get(originalBuilder) != null) {
                    expression = expression.copiedVersionFrom(clonedExpressions);
                } else {
                    // Possibly the expression was built with the wrong builder.
                    expression = expression.rebuildOn(builder);
                }

                setHavingExpression(expression);
            }

            allExpressions.addElement(expression);
        }

        // Process order by expressions.
        if (hasOrderByExpressions()) {
            rebuildAndAddExpressions(getOrderByExpressions(), allExpressions, builder, clonedExpressions);
        }

        // Process outer join by expressions.
        if (hasOuterJoinExpressions()) {
            rebuildAndAddExpressions(getOuterJoinedMappingCriteria(), allExpressions, builder, clonedExpressions);
            for (Iterator criterias = getOuterJoinedAdditionalJoinCriteria().iterator();
                     criterias.hasNext();) {
                rebuildAndAddExpressions((Map)criterias.next(), allExpressions, builder, clonedExpressions);
            }
        }

        //Process hierarchical query expressions.
        if (hasStartWithExpression()) {
            startWithExpression = getStartWithExpression().rebuildOn(builder);
            allExpressions.addElement(startWithExpression);
        }

        if (hasConnectByExpression()) {
            connectByExpression = getConnectByExpression().rebuildOn(builder);
        }

        if (hasOrderSiblingsByExpressions()) {
            rebuildAndAddExpressions(getOrderSiblingsByExpressions(), allExpressions, builder, clonedExpressions);
        }

        // We have to handle the cases where the where
        // clause is initially empty but might have clauses forced into it because the class
        // has multiple tables, order by forces a join, etc.  So we have to create a builder
        // and add expressions for it and the extras, but throw it away if they didn't force anything
        Expression oldRoot = getWhereClause();
        ExpressionNormalizer normalizer = new ExpressionNormalizer(this);
        normalizer.setSession(session);

        Expression newRoot = null;

        if (oldRoot != null) {
            newRoot = oldRoot.normalize(normalizer);
        }

        // CR#3166542 always ensure that the builder has been normalized,
        // there may be an expression that does not refer to the builder, i.e. value=value.
        if (descriptor != null) {
            builder.normalize(normalizer);
        }

        for (int index = 0; index < allExpressions.size(); index++) {
            Expression expression = (Expression)allExpressions.elementAt(index);
            expression.normalize(normalizer);
        }

        // Sets the where clause and AND's it with the additional Expression
        // setNormalizedWhereClause must be called to avoid the builder side-effects
        if (newRoot == null) {
            setNormalizedWhereClause(normalizer.getAdditionalExpression());
        } else {
            setNormalizedWhereClause(newRoot.and(normalizer.getAdditionalExpression()));
        }

        if (getWhereClause() != null) {
            allExpressions.addElement(getWhereClause());
        }

        // CR#3166542 always ensure that the builder has been normalized,
        // there may be an expression that does not refer to the builder, i.e. value=value.
        if (descriptor != null) {
            allExpressions.addElement(builder);
        }

        // Must also assign aliases to outer joined mapping criterias.
        if (hasOuterJoinExpressions()) {
            // Check for null on criterias.
            for (Iterator criterias = getOuterJoinedMappingCriteria().iterator();
                     criterias.hasNext();) {
                Expression criteria = (Expression)criterias.next();
                if (criteria != null) {
                    allExpressions.add(criteria);
                }
            }

            // Check for null on criterias.
            for (Iterator criterias = getOuterJoinedAdditionalJoinCriteria().iterator();
                     criterias.hasNext();) {
                Map map = (Map)criterias.next();
                if (map != null) {
                    Iterator it = map.values().iterator();
                    while(it.hasNext()) {
                        Expression criteria = (Expression)it.next();
                        if(criteria != null) {
                            allExpressions.add(criteria);
                        }
                    }
                }
            }
        }

        // Bug 2956674 Remove validate call as validation will be completed as the expression was normalized
        // Assign all table aliases.
        assignAliases(allExpressions);

        // If this is data level then the tables must be set manually.
        if (descriptor == null) {
            computeTablesFromTables();
        } else {
            computeTables();
        }

        // Now that the parent statement has been normalized, aliased, etc.,
        // normalize the subselect expressions.  For CR#4223.
        if (normalizer.encounteredSubSelectExpressions()) {
            normalizer.normalizeSubSelects(clonedExpressions);
        }

        // CR2114; If this is data level then we don't have a descriptor.
        // We don't have a target class so we must use the root platform. PWK
        // We are not fixing the informix.
        Class aClass = null;

        if (descriptor != null) {
            aClass = descriptor.getJavaClass();
        }

        // When a distinct is used the order bys must be in the select clause, so this forces them into the select.
        if ((session.getPlatform(aClass).isInformix()) || (shouldDistinctBeUsed() && hasOrderByExpressions())) {
            addOrderByExpressionToSelectForDistinct();
        }
    
public voidnormalizeForView(oracle.toplink.essentials.internal.sessions.AbstractSession theSession, oracle.toplink.essentials.descriptors.ClassDescriptor theDescriptor, java.util.Dictionary clonedExpressions)
INTERNAL: Normalize an expression mapping all of the descriptor's tables to the view. This is used to allow a descriptor to read from a view, but write to tables. This is used in the multiple table and subclasses read so all of the descriptor's possible tables must be mapped to the view.

        ExpressionBuilder builder;

        // bug 3878553 - alias all view selects. 
        setRequiresAliases(true);

        if (getWhereClause() != null) {
            builder = getWhereClause().getBuilder();
        } else {
            builder = new ExpressionBuilder();
            setBuilder(builder);
        }

        builder.setViewTable((DatabaseTable)getTables().firstElement());

        normalize(theSession, theDescriptor, clonedExpressions);
    
protected voidprintAdditionalJoins(oracle.toplink.essentials.internal.expressions.ExpressionSQLPrinter printer, java.util.Vector outerJoinedAliases, oracle.toplink.essentials.descriptors.ClassDescriptor desc, java.util.Map tablesJoinExpressions)

        Writer writer = printer.getWriter();
        AbstractSession session = printer.getSession();
        Vector descriptorTables = desc.getTables();
        int nDescriptorTables = descriptorTables.size();
        Vector tables;
        if(desc.hasInheritance()) {
            tables = desc.getInheritancePolicy().getAllTables();
        } else {
            tables = descriptorTables;
        }

        // skip main table - start with i=1
        int tablesSize = tables.size();
        for(int i=1; i < tablesSize; i++) {
            DatabaseTable table = (DatabaseTable)tables.elementAt(i);
            Expression onExpression = (Expression)tablesJoinExpressions.get(table);
            if (onExpression != null) {
                if(i < nDescriptorTables) {
                    // it's descriptor's own table
                    writer.write(" JOIN ");
                } else {
                    // it's child's table
                    writer.write(" LEFT OUTER JOIN ");
                }
                writer.write(table.getQualifiedName());
                writer.write(" ");
                DatabaseTable alias = onExpression.aliasForTable(table);
                outerJoinedAliases.addElement(alias);
                writer.write(alias.getQualifiedName());
                writer.write(" ON ");
                if (session.getPlatform() instanceof DB2MainframePlatform) {
                    ((RelationExpression)onExpression).printSQLNoParens(printer);
                } else {
                    onExpression.printSQL(printer);
                }
            }
        }
    
public java.util.VectorprintSQL(oracle.toplink.essentials.internal.expressions.ExpressionSQLPrinter printer)
Print the SQL representation of the statement on a stream.

        try {
            Vector selectFields = null;
            printer.setRequiresDistinct(shouldDistinctBeUsed());
            printer.printString("SELECT ");

            if (shouldDistinctBeUsed()) {
                printer.printString("DISTINCT ");
            }

            selectFields = writeFieldsIn(printer);

            appendFromClauseToWriter(printer);

            if (!(getWhereClause() == null)) {
                printer.printString(" WHERE ");
                printer.printExpression(getWhereClause());
            }

            if (hasHierarchicalQueryExpressions()) {
                appendHierarchicalQueryClauseToWriter(printer);
            }

            if (hasGroupByExpressions()) {
                appendGroupByClauseToWriter(printer);
            }
            if (hasHavingExpression()) {
                //appendHavingClauseToWriter(printer);
                printer.printString(" HAVING ");
                printer.printExpression(getHavingExpression());
            }

            if (hasOrderByExpressions()) {
                appendOrderClauseToWriter(printer);
            }

            // For pessimistic locking.
            if (getForUpdateClause() != null) {
                getForUpdateClause().printSQL(printer, this);
            }

            return selectFields;
        } catch (IOException exception) {
            throw ValidationException.fileError(exception);
        }
    
public voidrebuildAndAddExpressions(java.util.Vector expressions, java.util.Vector allExpressions, oracle.toplink.essentials.expressions.ExpressionBuilder primaryBuilder, java.util.Dictionary clonedExpressions)
Rebuild the expressions with the correct expression builder if using a different one.

        for (int index = 0; index < expressions.size(); index++) {
            Object fieldOrExpression = expressions.elementAt(index);

            if (fieldOrExpression instanceof Expression) {
                Expression expression = (Expression)fieldOrExpression;
                ExpressionBuilder originalBuilder = expression.getBuilder();

                if (originalBuilder != primaryBuilder) {
                    // For bug 2612185 avoid rebuildOn if possible as it rebuilds all on a single base.
                    // i.e. Report query items could be from parallel expressions.
                    if (clonedExpressions.get(originalBuilder) != null) {
                        expression = expression.copiedVersionFrom(clonedExpressions);
                        //if there is no builder or it is a copy of the base builder then rebuild otherwise it is a parallel expression not joined
                    } 
                    if (originalBuilder.wasQueryClassSetInternally()) {
                        // Possibly the expression was built with the wrong builder.
                        expression = expression.rebuildOn(primaryBuilder);
                    }
                    expressions.setElementAt(expression, index);
                }

                allExpressions.addElement(expression);
            }
        }
    
public voidrebuildAndAddExpressions(java.util.Map expressions, java.util.Vector allExpressions, oracle.toplink.essentials.expressions.ExpressionBuilder primaryBuilder, java.util.Dictionary clonedExpressions)
Rebuild the expressions with the correct expression builder if using a different one. Exact copy of the another rebuildAndAddExpressions adopted to a Map with Expression values as the first parameter (instead of Vector in the original method)

        Iterator it = expressions.entrySet().iterator();
        while (it.hasNext()) {
            Map.Entry entry = (Map.Entry)it.next();
            Object fieldOrExpression = entry.getValue();

            if (fieldOrExpression instanceof Expression) {
                Expression expression = (Expression)fieldOrExpression;
                ExpressionBuilder originalBuilder = expression.getBuilder();

                if (originalBuilder != primaryBuilder) {
                    // For bug 2612185 avoid rebuildOn if possible as it rebuilds all on a single base.
                    // i.e. Report query items could be from parallel expressions.
                    if (clonedExpressions.get(originalBuilder) != null) {
                        expression = expression.copiedVersionFrom(clonedExpressions);
                        //if there is no builder or it is a copy of the base builder then rebuild otherwise it is a parallel expression not joined
                    } 
                    if (originalBuilder.wasQueryClassSetInternally()) {
                        // Possibly the expression was built with the wrong builder.
                        expression = expression.rebuildOn(primaryBuilder);
                    }

                    entry.setValue(expression);
                }

                allExpressions.addElement(expression);
            }
        }
    
public voidremoveField(oracle.toplink.essentials.internal.helper.DatabaseField field)
INTERNAL:

        getFields().removeElement(field);
    
public voidremoveTable(oracle.toplink.essentials.internal.helper.DatabaseTable table)
Remove a table from the statement. The table will be dropped from the FROM part of the SQL statement.

        getTables().removeElement(table);
    
public booleanrequiresAliases()
INTERNAL: Returns true if aliases are required, false otherwise. If requiresAliases is set then force aliasing, this is required for object-rel.

        if (requiresAliases || hasOuterJoinExpressions()) {
            return true;
        }

        if (tableAliases != null) {
            return getTableAliases().size() > 1;
        }

        // tableAliases is null
        return false;
    
public voidresetDistinct()
ADVANCED: If a distinct has been set the DISTINCT clause will be printed. This is used internally by TopLink for batch reading but may also be used directly for advanced queries or report queries.

        setDistinctState(ObjectLevelReadQuery.UNCOMPUTED_DISTINCT);
    
public voidsetCurrentAliasNumber(int currentAliasNumber)
Sets a unique id that will be used to alias the next table. For sub-selects all must use this same aliasing information, maintained in the root enclosing statement. For CR#2627019

        if (getParentStatement() != null) {
            getParentStatement().setCurrentAliasNumber(currentAliasNumber);
        } else {
            this.currentAliasNumber = currentAliasNumber;
        }
    
public voidsetDistinctState(short distinctState)
ADVANCED: If a distinct has been set the DISTINCT clause will be printed. This is used internally by TopLink for batch reading but may also be used directly for advanced queries or report queries.

        this.distinctState = distinctState;
    
public voidsetFields(java.util.Vector fields)
INTERNAL: Set the fields, if any are aggregate selects then record this so that the distinct is not printed through anyOfs.

        for (Enumeration fieldsEnum = fields.elements(); fieldsEnum.hasMoreElements();) {
            Object fieldOrExpression = fieldsEnum.nextElement();

            if (fieldOrExpression instanceof FunctionExpression) {
                if (((FunctionExpression)fieldOrExpression).getOperator().isAggregateOperator()) {
                    setIsAggregateSelect(true);

                    break;
                }
            }
        }

        this.fields = fields;
    
protected voidsetForUpdateClause(oracle.toplink.essentials.internal.expressions.ForUpdateClause clause)

        this.forUpdateClause = clause;
    
public voidsetGroupByExpressions(java.util.Vector expressions)

        this.groupByExpressions = expressions;
    
public voidsetHavingExpression(oracle.toplink.essentials.expressions.Expression expressions)

        this.havingExpression = expressions;
    
public voidsetHierarchicalQueryExpressions(oracle.toplink.essentials.expressions.Expression startWith, oracle.toplink.essentials.expressions.Expression connectBy, java.util.Vector orderSiblingsExpressions)
INTERNAL: takes the hierarchical query expression which have been set on the query and sets them here used to generate the Hierarchical Query Clause in the SQL

        this.startWithExpression = startWith;
        this.connectByExpression = connectBy;
        this.orderSiblingsByExpressions = orderSiblingsExpressions;
    
public voidsetIsAggregateSelect(boolean isAggregateSelect)

        this.isAggregateSelect = isAggregateSelect;
    
public voidsetLockingClause(oracle.toplink.essentials.internal.expressions.ForUpdateClause lockingClause)

        this.forUpdateClause = lockingClause;
    
public voidsetNonSelectFields(java.util.Vector nonSelectFields)
Set the non select fields. The fields are used only on joining.

        this.nonSelectFields = nonSelectFields;
    
public voidsetNormalizedWhereClause(oracle.toplink.essentials.expressions.Expression whereClause)
Set the where clause expression. This must be used during normalization as the normal setWhereClause has the side effect of setting the builder, which must not occur during normalize.

        this.whereClause = whereClause;
    
public voidsetOrderByExpressions(java.util.Vector orderByExpressions)

        this.orderByExpressions = orderByExpressions;
    
public voidsetOuterJoinExpressions(java.util.Vector outerJoinedExpressions)

        this.outerJoinedExpressions = outerJoinedExpressions;
    
public voidsetOuterJoinedAdditionalJoinCriteria(java.util.Vector outerJoinedAdditionalJoinCriteria)

        this.outerJoinedAdditionalJoinCriteria = outerJoinedAdditionalJoinCriteria;
    
public voidsetOuterJoinedMappingCriteria(java.util.Vector outerJoinedMappingCriteria)

        this.outerJoinedMappingCriteria = outerJoinedMappingCriteria;
    
public voidsetParentStatement(oracle.toplink.essentials.internal.expressions.SQLSelectStatement parentStatement)
Set the parent statement if using subselects. This is used to normalize correctly with subselects.

        this.parentStatement = parentStatement;
    
public voidsetRequiresAliases(boolean requiresAliases)

        this.requiresAliases = requiresAliases;
    
protected voidsetTableAliases(java.util.Hashtable theTableAliases)

        tableAliases = theTableAliases;
    
public voidsetTables(java.util.Vector theTables)

        tables = theTables;
    
public booleanshouldDistinctBeUsed()
INTERNAL: If a distinct has been set the DISTINCT clause will be printed. This is required for batch reading.

        return distinctState == ObjectLevelReadQuery.USE_DISTINCT;
    
public voiduseDistinct()
ADVANCED: If a distinct has been set the DISTINCT clause will be printed. This is used internally by TopLink for batch reading but may also be used directly for advanced queries or report queries.

        setDistinctState(ObjectLevelReadQuery.USE_DISTINCT);
    
protected voidwriteField(oracle.toplink.essentials.internal.expressions.ExpressionSQLPrinter printer, oracle.toplink.essentials.internal.helper.DatabaseField field)
INTERNAL:

        //print ", " before each selected field except the first one
        if (printer.isFirstElementPrinted()) {
            printer.printString(", ");
        } else {
            printer.setIsFirstElementPrinted(true);
        }

        if (printer.shouldPrintQualifiedNames()) {
            if (field.getTable() != lastTable) {
                lastTable = field.getTable();
                currentAlias = getBuilder().aliasForTable(lastTable);

                // This is really for the special case where things were pre-aliased
                if (currentAlias == null) {
                    currentAlias = lastTable;
                }
            }

            printer.printString(currentAlias.getQualifiedName());
            printer.printString(".");
            printer.printString(field.getName());
        } else {
            printer.printString(field.getName());
        }
    
protected voidwriteFieldsFromExpression(oracle.toplink.essentials.internal.expressions.ExpressionSQLPrinter printer, oracle.toplink.essentials.expressions.Expression expression, java.util.Vector newFields)
INTERNAL:

        expression.writeFields(printer, newFields, this);
    
protected java.util.VectorwriteFieldsIn(oracle.toplink.essentials.internal.expressions.ExpressionSQLPrinter printer)
INTERNAL:

        this.lastTable = null;

        Vector newFields = oracle.toplink.essentials.internal.helper.NonSynchronizedVector.newInstance();

        for (Enumeration fieldsEnum = getFields().elements(); fieldsEnum.hasMoreElements();) {
            Object next = fieldsEnum.nextElement();

            if (next instanceof Expression) {
                writeFieldsFromExpression(printer, (Expression)next, newFields);
            } else {
                writeField(printer, (DatabaseField)next);
                newFields.addElement(next);
            }
        }

        return newFields;