FileDocCategorySizeDatePackage
SelectNode.javaAPI DocGlassfish v2 API16725Tue May 22 16:54:38 BST 2007oracle.toplink.essentials.internal.parsing

SelectNode

public class SelectNode extends QueryNode
INTERNAL

Purpose: Represent a SELECT

Responsibilities:

  • Hold the distinct status
  • Modify a query based on the contents The SELECT statement determines the return type of an EJBQL query. The SELECT may also determine the distinct state of a query A SELECT can be one of the following: 1. SELECT OBJECT(someObject)... This query will return a collection of objects 2. SELECT anObject.anAttribute ... This will return a collection of anAttribute 3. SELECT <aggregateFunction> ... This will return a single value The allowable aggregateFunctions are: AVG, COUNT, MAX, MIN, SUM SELECT AVG(emp.salary)... Returns the average of all the employees salaries SELECT COUNT(emp)... Returns a count of the employees SELECT COUNT(emp.firstName)... Returns a count of the employee's firstNames SELECT MAX(emp.salary)... Returns the maximum employee salary SELECT MIN(emp.salary)... Returns the minimum employee salary SELECT SUM(emp.salary)... Returns the sum of all the employees salaries
author
Jon Driscoll
since
TopLink 5.0

Fields Summary
private List
selectExpressions
private boolean
distinct
Constructors Summary
public SelectNode()

    
      
    
Methods Summary
private voidaddSelectExpression(java.lang.Object theNode)
INTERNAL Add an Order By Item to this node

        selectExpressions.add(theNode);
    
public voidapplyToQuery(oracle.toplink.essentials.queryframework.DatabaseQuery theQuery, oracle.toplink.essentials.internal.parsing.GenerationContext context)
INTERNAL Apply this node to the passed query

        ObjectLevelReadQuery readQuery = (ObjectLevelReadQuery)theQuery;
        if (selectExpressions.isEmpty()) {
            return;
        }

        //set the distinct state
        //BUG 3168673: Don't set distinct state if we're using Count
        if (!(isSingleSelectExpression() && getFirstSelectExpressionNode().isCountNode())) {
            // Set the distinct state for the query
            if (usesDistinct()) {
                getParseTree().setDistinctState(ObjectLevelReadQuery.USE_DISTINCT);
                readQuery.setDistinctState(ObjectLevelReadQuery.USE_DISTINCT);
            }
        }

        if (readQuery instanceof ReportQuery) {
            ReportQuery reportQuery = (ReportQuery)readQuery;
            reportQuery.returnWithoutReportQueryResult();
            if (isSingleSelectExpression() && 
                !getFirstSelectExpressionNode().isConstructorNode()) {
                reportQuery.returnSingleAttribute();
            }
        } 
        SelectGenerationContext selectContext = (SelectGenerationContext)context;
        for (Iterator i = selectExpressions.iterator(); i.hasNext();) {
            Node node = (Node)i.next();
            if (selectingRelationshipField(node, context)) {
                selectContext.useOuterJoins();
            }
            node.applyToQuery(readQuery, context); 
            selectContext.dontUseOuterJoins();
        }

        //indicate on the query if "return null if primary key null"
        //This means we want nulls returned if we expect an outer join
        if (this.hasOneToOneSelected(context)) {
            readQuery.setProperty("return null if primary key is null", Boolean.TRUE);
        } else {
            readQuery.removeProperty("return null if primary key is null");
        }

    
public oracle.toplink.essentials.queryframework.DatabaseQuerycreateDatabaseQuery(oracle.toplink.essentials.internal.parsing.ParseTreeContext context)
INTERNAL Returns a DatabaseQuery instance representing the owning ParseTree. This implementation returns a ReadAllQuery for simple SELECT queries and a ReportQuery otherwise.

        ObjectLevelReadQuery query;
        /* Disable the optimization for the time being. 
           The optimization exposes a problem with the ORDER BY clause in the
           generated SQL (see glassfish issue 2084).
           There is also a performance regression that needs to be
           investigated (see glassfish issue 2171).
        query = isReadAllQuery(context) ? 
            new ReadAllQuery() : new ReportQuery();
        */
        query = new ReportQuery();
        query.dontUseDistinct();//gf bug 1395- prevents using distinct unless user specified
        return query;
    
public oracle.toplink.essentials.expressions.ExpressiongenerateExpression(oracle.toplink.essentials.internal.parsing.GenerationContext context)
INTERNAL Return a TopLink expression generated using the left node

        return null;
    
private java.lang.ClassgetClassOfFirstVariable(oracle.toplink.essentials.internal.parsing.GenerationContext context)

        Class clazz = null;
        String variable = getParseTree().getFromNode().getFirstVariable();
        ParseTreeContext parseTreeContext = context.getParseTreeContext();
        if (parseTreeContext.isRangeVariable(variable)) {
            String schema = parseTreeContext.schemaForVariable(variable);
            // variables is defines in a range variable declaration, so there
            // is a schema name for this variable
            clazz = parseTreeContext.classForSchemaName(schema, context);
        } else {
            // variable is defined in a JOIN clause, so there is a a defining
            // node for the variable
            Node path = parseTreeContext.pathForVariable(variable);
            clazz = path.resolveClass(context);
        }
        return clazz;
    
private oracle.toplink.essentials.internal.parsing.NodegetFirstSelectExpressionNode()
Returns the first select expression node.

        return selectExpressions.size() > 0 ? 
            (Node)selectExpressions.get(0) : null;
    
public java.lang.ClassgetReferenceClass(oracle.toplink.essentials.internal.parsing.GenerationContext context)
Compute the Reference class for this query

param
context
return
the class this query is querying for

        return getClassOfFirstVariable(context);
    
public java.util.ListgetSelectExpressions()

        return selectExpressions;
    
public booleanhasOneToOneSelected(oracle.toplink.essentials.internal.parsing.GenerationContext context)
INTERNAL

        // Iterate the select expression and return true if one of it has a
        // oneToOne selected.
        for (Iterator i = selectExpressions.iterator(); i.hasNext();) {
            Node node = (Node)i.next();
            if (hasOneToOneSelected(node, context)) {
                return true;
            }
        }
        return false;
    
private booleanhasOneToOneSelected(oracle.toplink.essentials.internal.parsing.Node node, oracle.toplink.essentials.internal.parsing.GenerationContext context)
INTERNAL Answer true if there is a one-to-one relationship selected. This includes a chain of relationships. True: SELECT employee.address FROM ..... //Simple 1:1 True: SELECT a.b.c.d FROM ..... //where a->b, b->c and c->d are all 1:1. False: SELECT OBJECT(employee) FROM ..... //simple SELECT False: SELECT phoneNumber.areaCode FROM ..... //direct-to-field

        //BUG 3240484: Not SELECTing 1:1 if it's in a COUNT
        if (node.isCountNode()) {
            return false;
        }

        if (node.isAggregateNode()) {
            // delegate to aggregate expression
            return hasOneToOneSelected(node.getLeft(), context);
        }
         
        if (node.isVariableNode()){
            return !nodeRefersToObject(node, context);
        }

        if (node.isConstructorNode()) {
            List args = ((ConstructorNode)node).getConstructorItems();
            for (Iterator i = args.iterator(); i.hasNext();) {
                Node arg = (Node)i.next();
                if (hasOneToOneSelected(arg, context)) {
                    return true;
                }
            }
            return false;
        }
      
        // check whether it is a direct-to-field mapping
        return !selectingDirectToField(node, context);
    
private booleanisReadAllQuery(oracle.toplink.essentials.internal.parsing.ParseTreeContext context)
INTERNAL Returns true if the SELECT clause consists of a single expression returning the base identification variable of the query and if the base variable is defined as a range variable w/o FETCH JOINs.

        if (!isSingleSelectExpression()) {
            // multiple expressions in the select clause => ReportQuery
            return false;
        }
        
        Node node = getFirstSelectExpressionNode();
        if (!node.isVariableNode()) {
            // Does not select an identification variable (e.g. projection or
            // aggregate function) =>  ReportQuery
            return false;
        }
        String variable = ((VariableNode)node).getCanonicalVariableName();
        
        // Note, the base variable in ParseTreeContext is not yet set =>
        // calculate it
        String baseVariable = getParseTree().getFromNode().getFirstVariable();
        if (!context.isRangeVariable(baseVariable) || 
            (context.getFetchJoins(baseVariable) != null)) {
            // Query's base variable is not a range variable or the base
            // variable has FETCH JOINs => ReportQuery
            return false;
        }
        
        // Use ReadAllQuery if the variable of the SELECT clause expression is
        // the base variable
        return baseVariable.equals(variable);
    
public booleanisSelectNode()

        return true;
    
public booleanisSelected(java.lang.String variableName)
Answer true if the variable name given as argument is SELECTed. True: "SELECT OBJECT(emp) ...." & variableName = "emp" False: "SELECT OBJECT(somethingElse) ..." & variableName = "emp"

        for (Iterator i = selectExpressions.iterator(); i.hasNext();) {
            Node node = (Node)i.next();
            //Make sure we've SELECted a VariableNode
            if (node.isVariableNode() && 
                ((VariableNode)node).getCanonicalVariableName().equals(variableName)) {
                return true;
            }
        }
        return false;
    
private booleanisSingleSelectExpression()

        return selectExpressions.size() == 1;
    
public booleanisVariableInINClauseSelected(oracle.toplink.essentials.internal.parsing.GenerationContext context)
INTERNAL Answer true if a variable in the IN clause is SELECTed

        for (Iterator i = selectExpressions.iterator(); i.hasNext();) {
            Node node = (Node)i.next();
        
            if (node.isVariableNode()) {
                String variableNameForLeft = ((VariableNode)node).getCanonicalVariableName();
                if (!context.getParseTreeContext().isRangeVariable(variableNameForLeft)) {
                    return true;
                }
            }
        }
        return false;
    
public booleannodeRefersToObject(oracle.toplink.essentials.internal.parsing.Node node, oracle.toplink.essentials.internal.parsing.GenerationContext context)
INTERNAL Answer true if this node refers to an object described later in the EJBQL True: SELECT p FROM Project p False: SELECT p.id FROM Project p

        if (!node.isVariableNode()){
            return false;
        }
        String name = ((VariableNode)node).getCanonicalVariableName();
        String alias = context.getParseTreeContext().schemaForVariable(name);
        if (alias != null){
            ClassDescriptor descriptor = context.getSession().getDescriptorForAlias(alias);
            if (descriptor != null){
                return true;
            }
        }
        return false;
    
public oracle.toplink.essentials.internal.parsing.NodequalifyAttributeAccess(oracle.toplink.essentials.internal.parsing.ParseTreeContext context)
INTERNAL Check the select expression nodes for a path expression starting with a unqualified field access and if so, replace it by a qualified field access.

        for (int i = 0; i < selectExpressions.size(); i++) {
            Node item = (Node)selectExpressions.get(i);
            selectExpressions.set(i, item.qualifyAttributeAccess(context));
        }
        return this;
    
public java.lang.ClassresolveClass(oracle.toplink.essentials.internal.parsing.GenerationContext context)
resolveClass: Answer the class associated with my left node.

        return getReferenceClass(context);
    
private booleanselectingDirectToField(oracle.toplink.essentials.internal.parsing.Node node, oracle.toplink.essentials.internal.parsing.GenerationContext context)
INTERNAL Answer true if the SELECT ends in a direct-to-field. true: SELECT phone.areaCode false: SELECT employee.address


        if ((node == null) || !node.isDotNode()) {     
            return false;
        }
        return ((DotNode)node).endsWithDirectToField(context);
    
private booleanselectingRelationshipField(oracle.toplink.essentials.internal.parsing.Node node, oracle.toplink.essentials.internal.parsing.GenerationContext context)
INTERNAL

        if ((node == null) || !node.isDotNode()) {
            return false;
        }
        TypeHelper typeHelper = context.getParseTreeContext().getTypeHelper();
        Node path = node.getLeft();
        AttributeNode attribute = (AttributeNode)node.getRight();
        return typeHelper.isRelationship(path.getType(), 
                                         attribute.getAttributeName());
    
public voidsetDistinct(boolean distinct)

        this.distinct = distinct;
    
public voidsetSelectExpressions(java.util.List exprs)

        selectExpressions = exprs;
    
public booleanusesDistinct()

        return distinct;
    
public voidvalidate(oracle.toplink.essentials.internal.parsing.ParseTreeContext context)
INTERNAL Validate node.

        for (Iterator i = selectExpressions.iterator(); i.hasNext(); ) {
            Node item = (Node)i.next();
            item.validate(context);
        }
    
public voidverifySelectedAlias(oracle.toplink.essentials.internal.parsing.GenerationContext context)
Verify that the selected alias is a valid alias. If it's not valid, an Exception will be thrown, likely EJBQLException.aliasResolutionException. Valid: SELECT OBJECT(emp) FROM Employee emp WHERE ... Invalid: SELECT OBJECT(badAlias) FROM Employee emp WHERE ...

        for (Iterator i = selectExpressions.iterator(); i.hasNext();) {
            Node node = (Node)i.next();
            //if the node is a DotNode, there is no selected alias
            if (node.isDotNode()) {
                return;
            }
            node.resolveClass(context);
        }