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

oracle.toplink.essentials.internal.expressions.RelationExpression 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.expressions;

import java.util.*;
import oracle.toplink.essentials.exceptions.*;
import oracle.toplink.essentials.mappings.*;
import oracle.toplink.essentials.queryframework.*;
import oracle.toplink.essentials.internal.helper.*;
import oracle.toplink.essentials.expressions.*;
import oracle.toplink.essentials.internal.sessions.AbstractRecord;
import oracle.toplink.essentials.internal.sessions.AbstractSession;
import oracle.toplink.essentials.descriptors.ClassDescriptor;

/**
 * 

Purpose:Used for all relation operators except for between. */ public class RelationExpression extends CompoundExpression { /** * RelationExpression constructor comment. */ public RelationExpression() { super(); } /** * Test that both of our children are field nodes */ protected boolean allChildrenAreFields() { return (getFirstChild().getFields().size() == 1) && (getSecondChild().getFields().size() == 1); } /** * INTERNAL: * Modify this individual expression node to use outer joins wherever there are * equality operations between two field nodes. */ protected void convertNodeToUseOuterJoin() { if ((getOperator().getSelector() == ExpressionOperator.Equal) && allChildrenAreFields()) { setOperator(getOperator(ExpressionOperator.EqualOuterJoin)); } } /** * INTERNAL: * Used for debug printing. */ public String descriptionOfNodeType() { return "Relation"; } /** * INTERNAL: * Check if the object conforms to the expression in memory. * This is used for in-memory querying. * If the expression in not able to determine if the object conform throw a not supported exception. */ public boolean doesConform(Object object, AbstractSession session, AbstractRecord translationRow, InMemoryQueryIndirectionPolicy valueHolderPolicy, boolean isObjectUnregistered) { // Extract the value from the right side. //CR 3677 integration of valueHolderPolicy Object rightValue = getSecondChild().valueFromObject(object, session, translationRow, valueHolderPolicy, isObjectUnregistered); // Extract the value from the object. //CR 3677 integration of valueHolderPolicy Object leftValue = getFirstChild().valueFromObject(object, session, translationRow, valueHolderPolicy, isObjectUnregistered); // The right value may be a vector of values from an anyof, or an in. if (rightValue instanceof Vector) { //right vector means an anyof on right, so must check each value. for (Enumeration rightEnum = (Enumeration)((Vector)rightValue).elements(); rightEnum.hasMoreElements();) { Object tempRight = rightEnum.nextElement(); // Left may also be an anyof some must check each left with each right. if (leftValue instanceof Vector) { // If anyof the left match return true, otherwise keep checking. if (doesAnyOfLeftValuesConform((Vector)leftValue, tempRight, session)) { return true; } } if (doValuesConform(leftValue, tempRight, session)) { return true; } } // None of the value conform. return false; } // Otherwise the left may also be a vector of values from an anyof. if (leftValue instanceof Vector) { return doesAnyOfLeftValuesConform((Vector)leftValue, rightValue, session); } // Otherwise it is a simple value to value comparison, or simple object to object comparison. return doValuesConform(leftValue, rightValue, session); } /** * Conform in-memory the collection of left values with the right value for this expression. * This is used for anyOf support when the left side is a collection of values. */ protected boolean doesAnyOfLeftValuesConform(Vector leftValues, Object rightValue, AbstractSession session) { // Check each left value with the right value. for (int index = 0; index < leftValues.size(); index++) { Object leftValue = leftValues.get(index); if (doValuesConform(leftValue, rightValue, session)) { // Return true if any value matches. return true; } } // Return false only if none of the values match. return false; } /** * Conform in-memory the two values. */ protected boolean doValuesConform(Object leftValue, Object rightValue, AbstractSession session) { // Check for object comparison. if (isObjectComparison()) { return doesObjectConform(leftValue, rightValue, session); } else { return getOperator().doesRelationConform(leftValue, rightValue); } } /** * INTERNAL: * Check if the object conforms to the expression in memory. * This is used for in-memory querying across object relationships. */ public boolean doesObjectConform(Object leftValue, Object rightValue, AbstractSession session) { if ((leftValue == null) && (rightValue == null)) { return performSelector(true); } if ((leftValue == null) || (rightValue == null)) { //both are not null. return performSelector(false); } Class javaClass = leftValue.getClass(); Vector leftPrimaryKey; Vector rightPrimaryKey; ClassDescriptor descriptor; oracle.toplink.essentials.internal.identitymaps.CacheKey rightCacheKey; oracle.toplink.essentials.internal.identitymaps.CacheKey leftCacheKey; if (javaClass != rightValue.getClass()) { return performSelector(false); } descriptor = session.getDescriptor(javaClass); // Currently cannot conform aggregate comparisons in-memory. if (descriptor.isAggregateDescriptor()) { throw QueryException.cannotConformExpression(); } leftPrimaryKey = descriptor.getObjectBuilder().extractPrimaryKeyFromObject(leftValue, session); rightPrimaryKey = descriptor.getObjectBuilder().extractPrimaryKeyFromObject(rightValue, session); rightCacheKey = new oracle.toplink.essentials.internal.identitymaps.CacheKey(rightPrimaryKey); leftCacheKey = new oracle.toplink.essentials.internal.identitymaps.CacheKey(leftPrimaryKey); return performSelector(rightCacheKey.equals(leftCacheKey)); } /** * INTERNAL: * Extract the primary key from the expression into the row. * Ensure that the query is quering the exact primary key. * @param requireExactMatch refers to the primary key extracted gaurenteeing the result, * if not exact it is a hueristic and the cache hit will be conformed to the expression after the lookup * Return false if not on the primary key. */ public boolean extractPrimaryKeyValues(boolean requireExactMatch, ClassDescriptor descriptor, AbstractRecord primaryKeyRow, AbstractRecord translationRow) { // If an exact match is required then the operator must be equality. if (requireExactMatch && (!(getOperator().getSelector() == ExpressionOperator.Equal))) { return false; } // If not an exact match only =, <, <=, >=, >,... are allowed but not IN which has a different type if ((!requireExactMatch) && (getOperator().getSelector() == ExpressionOperator.In)) { return false; } DatabaseField field = null; Object value = null; if (getSecondChild().isConstantExpression()) { value = ((ConstantExpression)getSecondChild()).getValue(); } else if (getSecondChild().isParameterExpression()) { value = translationRow.get(((ParameterExpression)getSecondChild()).getField()); } else if (getFirstChild().isConstantExpression()) { value = ((ConstantExpression)getFirstChild()).getValue(); } else if (getFirstChild().isParameterExpression()) { value = translationRow.get(((ParameterExpression)getFirstChild()).getField()); } if (value == null) { return false; } // Ensure that the primary key is being queried on. if (getFirstChild().isFieldExpression()) { // Only get field for the source object. if (getFirstChild().getBuilder() != ((FieldExpression)getFirstChild()).getBaseExpression()) { return false; } field = ((FieldExpression)getFirstChild()).getField(); } else if (getFirstChild().isQueryKeyExpression()) { DatabaseMapping mapping = descriptor.getMappingForAttributeName(((QueryKeyExpression)getFirstChild()).getName()); if (getFirstChild().getBuilder() != ((QueryKeyExpression) getFirstChild()).getBaseExpression()){ return false; } // Only support referencing limited number of relationship types. if (mapping != null && (!mapping.isPrimaryKeyMapping()) ){ return false; } if (mapping != null && (mapping.isObjectReferenceMapping() || mapping.isAggregateObjectMapping())){ mapping.writeFromAttributeIntoRow(value, primaryKeyRow, getSession()); return true; } if (mapping != null && !mapping.isDirectToFieldMapping()){ return false; } // Only get field for the source object. field = descriptor.getObjectBuilder().getFieldForQueryKeyName(getFirstChild().getName()); } else if (getSecondChild().isFieldExpression()) {// For parameterized queries it may also be on the right side. // Only get field for the source object. if (getFirstChild().getBuilder() != ((FieldExpression)getSecondChild()).getBaseExpression()) { return false; } field = ((FieldExpression)getSecondChild()).getField(); } else if (getSecondChild().isQueryKeyExpression()) { DatabaseMapping mapping = descriptor.getMappingForAttributeName(((QueryKeyExpression)getSecondChild()).getName()); if (getSecondChild().getBuilder() != ((QueryKeyExpression) getSecondChild()).getBaseExpression() ){ return false; } // Only support referencing limited number of relationship types. if (mapping != null && (!mapping.isPrimaryKeyMapping()) ){ return false; } if (mapping != null && (mapping.isObjectReferenceMapping() || mapping.isAggregateObjectMapping())){ mapping.writeFromAttributeIntoRow(value, primaryKeyRow, getSession()); return true; } if (mapping != null && !mapping.isDirectToFieldMapping()){ return false; } field = descriptor.getObjectBuilder().getFieldForQueryKeyName(getSecondChild().getName()); } else { return false; } if ((field == null) || (!descriptor.getPrimaryKeyFields().contains(field))) { return false; } primaryKeyRow.put(field, value); return true; } /** * Check if the expression is an equal null expression, these must be handle in a special way in SQL. */ public boolean isEqualNull(ExpressionSQLPrinter printer) { if (isObjectComparison()) { return false; } if (getOperator().getSelector() != ExpressionOperator.Equal) { return false; } if (getSecondChild().isConstantExpression() && (((ConstantExpression)getSecondChild()).getValue() == null)) { return true; } if (getSecondChild().isParameterExpression() && (printer.getTranslationRow() != null) && (((ParameterExpression)getSecondChild()).getValue(printer.getTranslationRow(), printer.getSession()) == null)) { return true; } return false; } /** * Check if the expression is an equal null expression, these must be handle in a special way in SQL. */ public boolean isNotEqualNull(ExpressionSQLPrinter printer) { if (isObjectComparison()) { return false; } if (getOperator().getSelector() != ExpressionOperator.NotEqual) { return false; } if (getSecondChild().isConstantExpression() && (((ConstantExpression)getSecondChild()).getValue() == null)) { return true; } if (getSecondChild().isParameterExpression() && (printer.getTranslationRow() != null) && (((ParameterExpression)getSecondChild()).getValue(printer.getTranslationRow(), printer.getSession()) == null)) { return true; } return false; } /** * INTERNAL: * Return if the represents an object comparison. */ protected boolean isObjectComparison() { if ((!getFirstChild().isObjectExpression()) || ((ObjectExpression)getFirstChild()).isAttribute()) { return false; } DatabaseMapping mapping = ((ObjectExpression)getFirstChild()).getMapping(); if ((mapping != null) && (mapping.isDirectCollectionMapping())) { return false; } return getSecondChild().isObjectExpression() || (getSecondChild().isValueExpression() || (getSecondChild().isFunctionExpression() && ((FunctionExpression)getSecondChild()).getOperator().isAnyOrAll())); } /** * INTERNAL: */ public boolean isRelationExpression() { return true; } /** * INTERNAL: * Check for object comparison as this requires for the expression to be replaced by the object comparison. */ public Expression normalize(ExpressionNormalizer normalizer) { if (!isObjectComparison()) { return super.normalize(normalizer); } else { //bug # 2956674 //validation is moved into normalize to ensure that expressions are valid before we attempt to work with them // super.normalize will call validateNode as well. validateNode(); } if ((getOperator().getSelector() != ExpressionOperator.Equal) && (getOperator().getSelector() != ExpressionOperator.NotEqual)) { throw QueryException.invalidOperatorForObjectComparison(this); } if(getSecondChild().isFunctionExpression()) { FunctionExpression funcExp = (FunctionExpression)getSecondChild(); if(funcExp.getOperator().isAnyOrAll()) { SubSelectExpression subSelectExp = (SubSelectExpression)funcExp.getChildren().elementAt(1); ReportQuery subQuery = subSelectExp.getSubQuery(); // some db (derby) require that in EXIST(SELECT...) subquery returns a single column subQuery.getItems().clear(); subQuery.addItem("one", new ConstantExpression(new Integer(1), subQuery.getExpressionBuilder())); Expression subSelectCriteria = subQuery.getSelectionCriteria(); ExpressionBuilder subBuilder = subQuery.getExpressionBuilder(); ExpressionBuilder builder = getFirstChild().getBuilder(); Expression newExp; if(funcExp.getOperator().isAny()) { // Any or Some if(getOperator().getSelector() == ExpressionOperator.Equal) { subSelectCriteria = subBuilder.equal(getFirstChild()).and(subSelectCriteria); } else { subSelectCriteria = subBuilder.notEqual(getFirstChild()).and(subSelectCriteria); } subQuery.setSelectionCriteria(subSelectCriteria); newExp = builder.exists(subQuery); } else { // All if(getOperator().getSelector() == ExpressionOperator.Equal) { subSelectCriteria = subBuilder.notEqual(getFirstChild()).and(subSelectCriteria); } else { subSelectCriteria = subBuilder.equal(getFirstChild()).and(subSelectCriteria); } subQuery.setSelectionCriteria(subSelectCriteria); newExp = builder.notExists(subQuery); } return newExp.normalize(normalizer); } } // This can either be comparison to another object, null or another expression reference. // Object comparisons can be done on other object builders, 1:1 or 1:m m:m mappings, // 1:m/m:m must twist the primary key expression, // 1:1 must not join into the target but become the foreign key expression. // The value may be a constant or another expression. Expression foreignKeyJoin = null; ObjectExpression first = (ObjectExpression)getFirstChild(); // OPTIMIZATION 1: IDENTITY for CR#2456 / bug 2778339 // Most exists subqueries have something like projBuilder.equal(empBuilder.anyOf("projects")) // to correlate the subquery to the enclosing query. // TopLink can produce SQL with one less join by not mapping projBuilder and // anyOf("projects") to separate tables and equating them, but by treating // them as one and the same thing: as identical. // This trick can be pulled off by giving both the same TableAliasLookup, // but needs to be done very conservatively. // the equal() will be replaced directly with the mappingCriteria() of the anyOf("projects") // Example. emp.equal(emp.get("manager")) will now produce this SQL: // SELECT ... FROM EMPLOYEE t0 WHERE (t0.EMP_ID = t0.MANAGER_ID) not: // SELECT ... FROM EMPLOYEE t0, EMPLOYEE t1 WHERE ((t0.EMP_ID = t1.EMP_ID) // AND (t0.MANAGER_ID = t1.EMP_ID)) if (first.isExpressionBuilder() &&// If setting two query keys to equal the user probably intends a proper join. getSecondChild().isQueryKeyExpression() &&//.equal(anyOf() or get()) !((QueryKeyExpression)getSecondChild()).hasDerivedExpressions()) {//The right side is not used for anything else. first = (ExpressionBuilder)first.normalize(normalizer); // If FK joins go in the WHERE clause, want to get hold of it and // not put it in normalizer.additionalExpressions. Vector foreignKeyJoinPointer = new Vector(1); QueryKeyExpression second = (QueryKeyExpression)getSecondChild(); // If inside an OR the foreign key join must be on both sides. if (second.hasBeenNormalized()) { second.setHasBeenNormalized(false); } second = (QueryKeyExpression)second.normalize(normalizer, foreignKeyJoinPointer); if (!foreignKeyJoinPointer.isEmpty()) { foreignKeyJoin = (Expression)foreignKeyJoinPointer.firstElement(); // Will make left and right identical in the SQL. if (first.getTableAliases() == null) { TableAliasLookup tableAliases = new TableAliasLookup(); first.setTableAliases(tableAliases); second.setTableAliases(tableAliases); } else { second.setTableAliases(first.getTableAliases()); } } } // OPTIMIZATION 2: for 1-1 mappings and get(...).equal(null) // Imagine you had addr1 = emp.get("address"); then addr1.equal(addr2); // One could go (addr1.ADDRESS_ID = addr2.ADDRESS_ID) and (emp.ADDR_ID = addr1.ADDRESS_ID) (foreign key join). // The optimization is to drop addr1 and instead have: (emp.ADDR_ID = addr2.ADDRESS_ID). // Since emp can have only 1 address (OneToOne) the addr1.equal(addr2) is // implicit. This way if addr1 is used only for the comparison it can // be optimized out. // Also if addr2 were NULL there must be no join, just (emp.ADDR_ID = NULL) // For bug 3105559 handle AggregateObject case (emp.get("period").equal(period2) // which always falls into this case. else if (!first.isExpressionBuilder() && !((QueryKeyExpression)first).isNormalizationRequired()) { // Normalize firstChild's base only, as firstChild will be optimized out. if (first.getBaseExpression() != null) { first.setBaseExpression(first.getBaseExpression().normalize(normalizer)); } if (getSecondChild().isConstantExpression()) { Object targetObject = ((ConstantExpression)getSecondChild()).getValue(); foreignKeyJoin = first.getMapping().buildObjectJoinExpression(first, targetObject, getSession()); } else if (getSecondChild().isObjectExpression() || getSecondChild().isParameterExpression()) { foreignKeyJoin = first.getMapping().buildObjectJoinExpression(first, getSecondChild(), getSession()); } else { throw QueryException.invalidUseOfToManyQueryKeyInExpression(this); } } // DEFAULT: Left and right are separate entities, and the // equal() will be replaced with a comparison by primary key. if (foreignKeyJoin == null) { first = (ObjectExpression)first.normalize(normalizer); // A ConstantExpression stores a selection object. Compare the primary // keys of the first expression and the selection object. if (getSecondChild().isConstantExpression()) { Expression keyExpression = first.getDescriptor().getObjectBuilder().buildPrimaryKeyExpressionFromObject(((ConstantExpression)getSecondChild()).getValue(), getSession()); foreignKeyJoin = first.twist(keyExpression, first); // Each expression will represent a separate table, so compare the primary // keys of the first and second expressions. } else if (getSecondChild().isObjectExpression() || getSecondChild().isParameterExpression()) { foreignKeyJoin = first.twist(first.getDescriptor().getObjectBuilder().getPrimaryKeyExpression(), getSecondChild()); } else { throw QueryException.invalidUseOfToManyQueryKeyInExpression(this); } } if (getOperator().getSelector() == ExpressionOperator.NotEqual) { foreignKeyJoin = foreignKeyJoin.not(); } return foreignKeyJoin.normalize(normalizer); } /** * INTERNAL: * Check if the object conforms to the expression in memory. * This is used for in-memory querying across object relationships. */ public boolean performSelector(boolean areValuesEqual) { if (getOperator().getSelector() == getOperator().Equal) { return areValuesEqual; } if (getOperator().getSelector() == getOperator().NotEqual) { return !areValuesEqual; } throw QueryException.cannotConformExpression(); } /** * INTERNAL: * Print SQL */ public void printSQL(ExpressionSQLPrinter printer) { if (isEqualNull(printer)) { getFirstChild().isNull().printSQL(printer); } else if (isNotEqualNull(printer)) { getFirstChild().notNull().printSQL(printer); } else { super.printSQL(printer); } } /** * INTERNAL: * Print java for project class generation */ public void printJava(ExpressionJavaPrinter printer) { ExpressionOperator realOperator = getPlatformOperator(printer.getPlatform()); Expression tempFirstChild = getFirstChild(); Expression tempSecondChild = getSecondChild(); realOperator.printJavaDuo(tempFirstChild, tempSecondChild, printer); } /** * INTERNAL: * Check if it is a FunctionExpression and its Operator is ToUpperCase. */ /* public boolean isUpperCaseFunctionExpression(Expression expression, ExpressionJavaPrinter printer) { return expression.isFunctionExpression() && ((FunctionExpression)expression).getPlatformOperator(printer.getPlatform()).getSelector() == ExpressionOperator.ToUpperCase; }*/ /** * INTERNAL: * Check if it is a ConstantExpression and its value(String) is all upper case. */ /* public boolean isUpperCaseConstantExpression(Expression expression) { return expression.isConstantExpression() && ((ConstantExpression)expression).getValue() instanceof String && Helper.isUpperCaseString((String)((ConstantExpression)expression).getValue()); }*/ /** * INTERNAL: * Print SQL without adding parentheses (for DB2 outer joins). */ public void printSQLNoParens(ExpressionSQLPrinter printer) { ExpressionOperator realOperator = getPlatformOperator(printer.getPlatform()); realOperator.printDuo(getFirstChild(), getSecondChild(), printer); } /** * Do any required validation for this node. Throw an exception if it's incorrect. */ public void validateNode() { if (getFirstChild().isTableExpression()) { throw QueryException.cannotCompareTablesInExpression(((TableExpression)getFirstChild()).getTable()); } if (getSecondChild().isTableExpression()) { throw QueryException.cannotCompareTablesInExpression(((TableExpression)getSecondChild()).getTable()); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy