All Downloads are FREE. Search and download functionalities are using the official Maven repository.

oracle.toplink.essentials.internal.parsing.SelectNode Maven / Gradle / Ivy

There is a newer version: 2.1-60f
Show newest version
/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 * 
 * // Copyright (c) 1998, 2007, Oracle. All rights reserved.
 * 
 *
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common Development
 * and Distribution License("CDDL") (collectively, the "License").  You
 * may not use this file except in compliance with the License. You can obtain
 * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
 * or glassfish/bootstrap/legal/LICENSE.txt.  See the License for the specific
 * language governing permissions and limitations under the License.
 * 
 * When distributing the software, include this License Header Notice in each
 * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
 * Sun designates this particular file as subject to the "Classpath" exception
 * as provided by Sun in the GPL Version 2 section of the License file that
 * accompanied this code.  If applicable, add the following below the License
 * Header, with the fields enclosed by brackets [] replaced by your own
 * identifying information: "Portions Copyrighted [year]
 * [name of copyright owner]"
 * 
 * Contributor(s):
 * 
 * If you wish your version of this file to be governed by only the CDDL or
 * only the GPL Version 2, indicate your decision by adding "[Contributor]
 * elects to include this software in this distribution under the [CDDL or GPL
 * Version 2] license."  If you don't indicate a single choice of license, a
 * recipient has the option to distribute your version of this file under
 * either the CDDL, the GPL Version 2 or to extend the choice of license to
 * its licensees as provided above.  However, if you add GPL Version 2 code
 * and therefore, elected the GPL Version 2 license, then the option applies
 * only if the new code is made subject to such option by the copyright
 * holder.
 */
package oracle.toplink.essentials.internal.parsing;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import oracle.toplink.essentials.queryframework.*;
import oracle.toplink.essentials.expressions.Expression;
import oracle.toplink.essentials.expressions.ExpressionBuilder;
import oracle.toplink.essentials.descriptors.ClassDescriptor;

/**
 * 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 */ public class SelectNode extends QueryNode { private List selectExpressions = new ArrayList(); private boolean distinct =false; public SelectNode() { } /** * INTERNAL * Add an Order By Item to this node */ private void addSelectExpression(Object theNode) { selectExpressions.add(theNode); } /** */ public List getSelectExpressions() { return selectExpressions; } /** */ public void setSelectExpressions(List exprs) { selectExpressions = exprs; } /** */ public boolean usesDistinct() { return distinct; } /** */ public void setDistinct(boolean distinct) { this.distinct = distinct; } /** * INTERNAL * Returns a DatabaseQuery instance representing the owning * ParseTree. This implementation returns a ReadAllQuery for simple SELECT * queries and a ReportQuery otherwise. */ public DatabaseQuery createDatabaseQuery(ParseTreeContext context) { 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; } /** * 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. */ private boolean isReadAllQuery(ParseTreeContext context) { 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); } /** * INTERNAL * Apply this node to the passed query */ public void applyToQuery(DatabaseQuery theQuery, GenerationContext context) { 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"); } } /** * INTERNAL **/ public boolean hasOneToOneSelected(GenerationContext context) { // 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; } /** * 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 **/ private boolean hasOneToOneSelected(Node node, GenerationContext context) { //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); } /** * 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 ... */ public void verifySelectedAlias(GenerationContext context) { 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); } } /** * Answer true if the variable name given as argument is SELECTed. * * True: "SELECT OBJECT(emp) ...." & variableName = "emp" * False: "SELECT OBJECT(somethingElse) ..." & variableName = "emp" */ public boolean isSelected(String variableName) { 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; } public boolean isSelectNode() { return true; } /** * 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. */ public Node qualifyAttributeAccess(ParseTreeContext context) { for (int i = 0; i < selectExpressions.size(); i++) { Node item = (Node)selectExpressions.get(i); selectExpressions.set(i, item.qualifyAttributeAccess(context)); } return this; } /** * INTERNAL * Validate node. */ public void validate(ParseTreeContext context) { for (Iterator i = selectExpressions.iterator(); i.hasNext(); ) { Node item = (Node)i.next(); item.validate(context); } } /** * resolveClass: Answer the class associated with my left node. */ public Class resolveClass(GenerationContext context) { return getReferenceClass(context); } /** * INTERNAL * Return a TopLink expression generated using the left node */ public Expression generateExpression(GenerationContext context) { return null; } /** * Compute the Reference class for this query * @param context * @return the class this query is querying for */ public Class getReferenceClass(GenerationContext context) { return getClassOfFirstVariable(context); } /** */ private Class getClassOfFirstVariable(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; } /** * INTERNAL * Answer true if a variable in the IN clause is SELECTed */ public boolean isVariableInINClauseSelected(GenerationContext context) { 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; } /** * 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 **/ public boolean nodeRefersToObject(Node node, GenerationContext context) { 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; } /** * INTERNAL */ private boolean selectingRelationshipField(Node node, GenerationContext context) { 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()); } /** * INTERNAL * Answer true if the SELECT ends in a direct-to-field. * true: SELECT phone.areaCode * false: SELECT employee.address */ private boolean selectingDirectToField(Node node, GenerationContext context) { if ((node == null) || !node.isDotNode()) { return false; } return ((DotNode)node).endsWithDirectToField(context); } /** * Returns the first select expression node. */ private Node getFirstSelectExpressionNode() { return selectExpressions.size() > 0 ? (Node)selectExpressions.get(0) : null; } /** */ private boolean isSingleSelectExpression() { return selectExpressions.size() == 1; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy