FileDocCategorySizeDatePackage
RelationExpression.javaAPI DocGlassfish v2 API27961Tue May 22 16:54:32 BST 2007oracle.toplink.essentials.internal.expressions

RelationExpression

public class RelationExpression extends CompoundExpression

Purpose:Used for all relation operators except for between.

Fields Summary
Constructors Summary
public RelationExpression()
RelationExpression constructor comment.

        super();
    
Methods Summary
protected booleanallChildrenAreFields()
Test that both of our children are field nodes

        return (getFirstChild().getFields().size() == 1) && (getSecondChild().getFields().size() == 1);

    
protected voidconvertNodeToUseOuterJoin()
INTERNAL: Modify this individual expression node to use outer joins wherever there are equality operations between two field nodes.

        if ((getOperator().getSelector() == ExpressionOperator.Equal) && allChildrenAreFields()) {
            setOperator(getOperator(ExpressionOperator.EqualOuterJoin));
        }
    
public java.lang.StringdescriptionOfNodeType()
INTERNAL: Used for debug printing.

        return "Relation";
    
protected booleandoValuesConform(java.lang.Object leftValue, java.lang.Object rightValue, oracle.toplink.essentials.internal.sessions.AbstractSession session)
Conform in-memory the two values.

        // Check for object comparison.
        if (isObjectComparison()) {
            return doesObjectConform(leftValue, rightValue, session);
        } else {
            return getOperator().doesRelationConform(leftValue, rightValue);
        }
    
protected booleandoesAnyOfLeftValuesConform(java.util.Vector leftValues, java.lang.Object rightValue, oracle.toplink.essentials.internal.sessions.AbstractSession 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.

        // 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;
    
public booleandoesConform(java.lang.Object object, oracle.toplink.essentials.internal.sessions.AbstractSession session, oracle.toplink.essentials.internal.sessions.AbstractRecord translationRow, oracle.toplink.essentials.queryframework.InMemoryQueryIndirectionPolicy valueHolderPolicy, boolean isObjectUnregistered)
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.

        // 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);
    
public booleandoesObjectConform(java.lang.Object leftValue, java.lang.Object rightValue, oracle.toplink.essentials.internal.sessions.AbstractSession session)
INTERNAL: Check if the object conforms to the expression in memory. This is used for in-memory querying across object relationships.

        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));

    
public booleanextractPrimaryKeyValues(boolean requireExactMatch, oracle.toplink.essentials.descriptors.ClassDescriptor descriptor, oracle.toplink.essentials.internal.sessions.AbstractRecord primaryKeyRow, oracle.toplink.essentials.internal.sessions.AbstractRecord translationRow)
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.

        // 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;
    
public booleanisEqualNull(oracle.toplink.essentials.internal.expressions.ExpressionSQLPrinter printer)
Check if the expression is an equal null expression, these must be handle in a special way in SQL.

        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;
    
public booleanisNotEqualNull(oracle.toplink.essentials.internal.expressions.ExpressionSQLPrinter printer)
Check if the expression is an equal null expression, these must be handle in a special way in SQL.

        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;
    
protected booleanisObjectComparison()
INTERNAL: Return if the represents an object comparison.

        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()));
    
public booleanisRelationExpression()
INTERNAL:

        return true;
    
public oracle.toplink.essentials.expressions.Expressionnormalize(oracle.toplink.essentials.internal.expressions.ExpressionNormalizer normalizer)
INTERNAL: Check for object comparison as this requires for the expression to be replaced by the object comparison.

        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);
    
public booleanperformSelector(boolean areValuesEqual)
INTERNAL: Check if the object conforms to the expression in memory. This is used for in-memory querying across object relationships.

        if (getOperator().getSelector() == getOperator().Equal) {
            return areValuesEqual;
        }
        if (getOperator().getSelector() == getOperator().NotEqual) {
            return !areValuesEqual;
        }

        throw QueryException.cannotConformExpression();

    
public voidprintJava(oracle.toplink.essentials.internal.expressions.ExpressionJavaPrinter printer)
INTERNAL: Print java for project class generation

        ExpressionOperator realOperator = getPlatformOperator(printer.getPlatform());
        Expression tempFirstChild = getFirstChild();
        Expression tempSecondChild = getSecondChild();
        realOperator.printJavaDuo(tempFirstChild, tempSecondChild, printer);
    
public voidprintSQL(oracle.toplink.essentials.internal.expressions.ExpressionSQLPrinter printer)
INTERNAL: Print SQL

        if (isEqualNull(printer)) {
            getFirstChild().isNull().printSQL(printer);
        } else if (isNotEqualNull(printer)) {
            getFirstChild().notNull().printSQL(printer);
        } else {
            super.printSQL(printer);
        }
    
public voidprintSQLNoParens(oracle.toplink.essentials.internal.expressions.ExpressionSQLPrinter printer)
INTERNAL: Print SQL without adding parentheses (for DB2 outer joins).

        ExpressionOperator realOperator = getPlatformOperator(printer.getPlatform());
        realOperator.printDuo(getFirstChild(), getSecondChild(), printer);
    
public voidvalidateNode()
Do any required validation for this node. Throw an exception if it's incorrect.

        if (getFirstChild().isTableExpression()) {
            throw QueryException.cannotCompareTablesInExpression(((TableExpression)getFirstChild()).getTable());
        }
        if (getSecondChild().isTableExpression()) {
            throw QueryException.cannotCompareTablesInExpression(((TableExpression)getSecondChild()).getTable());
        }