You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cayenne.apache.org by an...@apache.org on 2009/06/17 16:52:12 UTC

svn commit: r785634 - in /cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src: main/java/org/apache/cayenne/access/trans/ main/java/org/apache/cayenne/dba/openbase/ main/java/org/apache/cayenne/dba/oracle/ main/java/org/apache/cayenne/map/ test...

Author: andrey
Date: Wed Jun 17 14:52:11 2009
New Revision: 785634

URL: http://svn.apache.org/viewvc?rev=785634&view=rev
Log:
CAY-1235 Implement qualifiers for DBEntities. Implementing expression-like qualifiers for DBEntities

Added:
    cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/trans/QualifierTranslator.java   (with props)
Modified:
    cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/trans/JoinStack.java
    cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/trans/JoinTreeNode.java
    cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/trans/QueryAssemblerHelper.java
    cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/trans/SelectTranslator.java
    cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/dba/openbase/OpenBaseJoinStack.java
    cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/dba/openbase/OpenBaseSelectTranslator.java
    cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/dba/oracle/Oracle8JoinStack.java
    cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/dba/oracle/Oracle8SelectTranslator.java
    cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/map/DbEntity.java
    cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/map/MapLoader.java
    cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/access/trans/SelectTranslatorTest.java

Modified: cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/trans/JoinStack.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/trans/JoinStack.java?rev=785634&r1=785633&r2=785634&view=diff
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/trans/JoinStack.java (original)
+++ cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/trans/JoinStack.java Wed Jun 17 14:52:11 2009
@@ -23,12 +23,17 @@
 
 import org.apache.cayenne.dba.DbAdapter;
 import org.apache.cayenne.dba.QuotingStrategy;
+import org.apache.cayenne.exp.Expression;
+import org.apache.cayenne.exp.parser.ASTDbPath;
+import org.apache.cayenne.exp.parser.ASTObjPath;
+import org.apache.cayenne.exp.parser.SimpleNode;
 import org.apache.cayenne.map.DataMap;
 import org.apache.cayenne.map.DbEntity;
 import org.apache.cayenne.map.DbJoin;
 import org.apache.cayenne.map.DbRelationship;
 import org.apache.cayenne.map.JoinType;
-import org.apache.cayenne.util.Util;
+import org.apache.cayenne.map.ObjEntity;
+import org.apache.commons.collections.Transformer;
 
 /**
  * Encapsulates join reuse/split logic used in SelectQuery processing. All expression
@@ -46,6 +51,11 @@
     private int aliasCounter;
     
     /**
+     * Helper class to process DbEntity qualifiers
+     */
+    private QualifierTranslator qualifierTranslator;
+    
+    /**
      * @deprecated since 3.0
      */   
     @Deprecated
@@ -56,7 +66,8 @@
         resetStack();
     }
     
-    protected JoinStack(DbAdapter dbAdapter, DataMap dataMap) {
+    protected JoinStack(DbAdapter dbAdapter, DataMap dataMap,
+            QueryAssembler assembler) {
         this.rootNode = new JoinTreeNode(this);
         this.rootNode.setTargetTableAlias(newAlias());
         boolean status;
@@ -66,7 +77,8 @@
             status = false;
         }
         strategy =  dbAdapter.getQuotingStrategy(status);
-  
+        qualifierTranslator = dbAdapter.getQualifierTranslator(assembler);
+        
         resetStack();
     }
     
@@ -159,6 +171,20 @@
             out.append('.');
             out.append(strategy.quoteString(join.getTargetName()));
         }
+        
+        /**
+         * Attaching root Db entity's qualifier
+         */
+        Expression dbQualifier = targetEntity.getQualifier();
+        if (dbQualifier != null) {
+            dbQualifier = dbQualifier.transform(new JoinedDbEntityQualifierTransformer(node));
+            
+            if (len > 0) {
+                out.append(" AND ");
+            }
+            qualifierTranslator.setOut(out);
+            qualifierTranslator.doAppendPart(dbQualifier);
+        }
 
         out.append(')');
 
@@ -172,39 +198,10 @@
      */
     protected void appendQualifier(Appendable out, boolean firstQualifierElement)
             throws IOException {
-        // skip root, recursively append its children
-        for (JoinTreeNode child : rootNode.getChildren()) {
-            firstQualifierElement &= appendQualifier(out, child, firstQualifierElement);
-        }
+        // nothing as standard join is performed before "WHERE"
     }
     
     /**
-     * Append join tree node information to the qualifier - the part after "WHERE".
-     * @return whether qualifier was not yet appended (i.e. firstQualifierElement is still false)
-     */
-    private boolean appendQualifier(Appendable out, JoinTreeNode node, boolean firstQualifierElement) 
-        throws IOException {
-        DbRelationship relationship = node.getRelationship();
-
-        DbEntity targetEntity = (DbEntity) relationship.getTargetEntity();
-        if (!Util.isEmptyString(targetEntity.getQualifier())) {
-            if (!firstQualifierElement) {
-                out.append(" AND ");
-            }
-            else {
-                firstQualifierElement = false;
-            }
-            
-            out.append(targetEntity.getQualifier());
-        }
-        
-        for (JoinTreeNode child : node.getChildren()) {
-            firstQualifierElement &= appendQualifier(out, child, firstQualifierElement);
-        }
-        return firstQualifierElement;
-    }
-
-    /**
      * Pops the stack all the way to the root node.
      */
     void resetStack() {
@@ -223,4 +220,29 @@
         return "t" + aliasCounter++;
     }
     
+    /**
+     * Class to translate *joined* DB Entity qualifiers annotation to 
+     * *current* Obj-entity qualifiers annotation
+     * This is done by changing all Obj-paths to concatenated Db-paths to root entity
+     * and rejecting all original Db-paths
+     */
+    class JoinedDbEntityQualifierTransformer implements Transformer {
+        String pathToRoot;
+        
+        JoinedDbEntityQualifierTransformer(JoinTreeNode node) {
+            pathToRoot = "";
+            while (node != null && node.getRelationship() != null) {
+                pathToRoot += node.getRelationship().getName() + ObjEntity.PATH_SEPARATOR;
+                node = node.getParent();
+            }
+        }
+        
+        public Object transform(Object input) {
+            if (input instanceof ASTObjPath) {
+                return new ASTDbPath(pathToRoot + 
+                        ((SimpleNode) input).getOperand(0));
+            }
+            return input;
+        }
+    }
 }

Modified: cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/trans/JoinTreeNode.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/trans/JoinTreeNode.java?rev=785634&r1=785633&r2=785634&view=diff
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/trans/JoinTreeNode.java (original)
+++ cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/trans/JoinTreeNode.java Wed Jun 17 14:52:11 2009
@@ -38,17 +38,23 @@
     private JoinType joinType;
     private Collection<JoinTreeNode> children;
     private JoinStack joinProcessor;
+    
+    /**
+     * Parent join
+     */
+    private JoinTreeNode parent;
 
     JoinTreeNode(JoinStack joinProcessor) {
         this.joinProcessor = joinProcessor;
     }
 
     JoinTreeNode(JoinStack joinProcessor, DbRelationship relationship,
-            JoinType joinType, String alias) {
+            JoinType joinType, String alias, JoinTreeNode parent) {
         this(joinProcessor);
         this.relationship = relationship;
         this.alias = alias;
         this.joinType = joinType;
+        this.parent = parent;
     }
 
     int size() {
@@ -88,7 +94,8 @@
                 joinProcessor,
                 relationship,
                 joinType,
-                alias);
+                alias,
+                this);
         child.setSourceTableAlias(this.targetTableAlias);
         child.setTargetTableAlias(joinProcessor.newAlias());
         children.add(child);
@@ -128,4 +135,11 @@
     public JoinType getJoinType() {
         return joinType;
     }
+    
+    /**
+     * @return parent join
+     */
+    public JoinTreeNode getParent() {
+        return parent;
+    }
 }

Added: cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/trans/QualifierTranslator.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/trans/QualifierTranslator.java?rev=785634&view=auto
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/trans/QualifierTranslator.java (added)
+++ cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/trans/QualifierTranslator.java Wed Jun 17 14:52:11 2009
@@ -0,0 +1,478 @@
+/*****************************************************************
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ ****************************************************************/
+
+package org.apache.cayenne.access.trans;
+
+import java.io.IOException;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.cayenne.CayenneRuntimeException;
+import org.apache.cayenne.ObjectId;
+import org.apache.cayenne.Persistent;
+import org.apache.cayenne.exp.Expression;
+import org.apache.cayenne.exp.TraversalHandler;
+import org.apache.cayenne.exp.parser.ASTDbPath;
+import org.apache.cayenne.exp.parser.ASTObjPath;
+import org.apache.cayenne.exp.parser.SimpleNode;
+import org.apache.cayenne.map.DbAttribute;
+import org.apache.cayenne.map.DbRelationship;
+import org.apache.cayenne.map.EntityInheritanceTree;
+import org.apache.cayenne.map.JoinType;
+import org.apache.cayenne.map.ObjEntity;
+import org.apache.cayenne.query.QualifiedQuery;
+import org.apache.cayenne.query.Query;
+import org.apache.commons.collections.IteratorUtils;
+import org.apache.commons.collections.Transformer;
+
+/**
+ * Translates query qualifier to SQL. Used as a helper class by query translators.
+ */
+public class QualifierTranslator extends QueryAssemblerHelper implements TraversalHandler {
+
+    protected DataObjectMatchTranslator objectMatchTranslator;
+    protected boolean matchingObject;
+
+    public QualifierTranslator(QueryAssembler queryAssembler) {
+        super(queryAssembler);
+    }
+
+    /**
+     * Translates query qualifier to SQL WHERE clause. Qualifier is obtained from the
+     * parent queryAssembler.
+     * 
+     * @since 3.0
+     */
+    @Override
+    protected void doAppendPart() throws IOException {
+        doAppendPart(extractQualifier());
+    }
+    
+    /**
+     * Translates query qualifier to SQL WHERE clause. Qualifier is a method parameter     * 
+     * @since 3.0
+     */
+    protected void doAppendPart(Expression rootNode) throws IOException {
+        if (rootNode == null) {
+            return;
+        }
+        rootNode.traverse(this);
+    }
+
+    protected Expression extractQualifier() {
+        Query q = queryAssembler.getQuery();
+
+        Expression qualifier = ((QualifiedQuery) q).getQualifier();
+
+        // append Entity qualifiers, taking inheritance into account
+        ObjEntity entity = getObjEntity();
+
+        if (entity != null) {
+            EntityInheritanceTree tree = queryAssembler
+                    .getEntityResolver()
+                    .lookupInheritanceTree(entity);
+            Expression entityQualifier = (tree != null) ? tree
+                    .qualifierForEntityAndSubclasses() : entity.getDeclaredQualifier();
+            if (entityQualifier != null) {
+                qualifier = (qualifier != null)
+                        ? qualifier.andExp(entityQualifier)
+                        : entityQualifier;
+            }
+        }
+        
+        /**
+         * Attaching root Db entity's qualifier
+         */
+        if (getDbEntity() != null) {
+            Expression dbQualifier = getDbEntity().getQualifier();
+            if (dbQualifier != null) {
+                dbQualifier = dbQualifier.transform(new DbEntityQualifierTransformer());
+                
+                qualifier = qualifier == null ? dbQualifier :
+                    qualifier.andExp(dbQualifier);
+            }
+        }
+
+        return qualifier;
+    }
+
+    /**
+     * Called before processing an expression to initialize objectMatchTranslator if
+     * needed.
+     */
+    protected void detectObjectMatch(Expression exp) {
+        // On demand initialization of
+        // objectMatchTranslator is not possible since there may be null
+        // object values that would not allow to detect the need for
+        // such translator in the right time (e.g.: null = dbpath)
+
+        matchingObject = false;
+
+        if (exp.getOperandCount() != 2) {
+            // only binary expressions are supported
+            return;
+        }
+
+        // check if there are DataObjects among direct children of the Expression
+        for (int i = 0; i < 2; i++) {
+            Object op = exp.getOperand(i);
+            if (op instanceof Persistent || op instanceof ObjectId) {
+                matchingObject = true;
+
+                if (objectMatchTranslator == null) {
+                    objectMatchTranslator = new DataObjectMatchTranslator();
+                }
+                else {
+                    objectMatchTranslator.reset();
+                }
+                break;
+            }
+        }
+    }
+
+    protected void appendObjectMatch() throws IOException {
+        if (!matchingObject || objectMatchTranslator == null) {
+            throw new IllegalStateException("An invalid attempt to append object match.");
+        }
+
+        // turn off special handling, so that all the methods behave as a superclass's
+        // impl.
+        matchingObject = false;
+
+        boolean first = true;
+
+        DbRelationship relationship = objectMatchTranslator.getRelationship();
+        if (!relationship.isToMany() && !relationship.isToPK()) {
+            queryAssembler.dbRelationshipAdded(
+                    relationship,
+                    JoinType.INNER,
+                    objectMatchTranslator.getJoinSplitAlias());
+        }
+
+        Iterator<String> it = objectMatchTranslator.keys();
+        while (it.hasNext()) {
+            if (first) {
+                first = false;
+            }
+            else {
+                out.append(" AND ");
+            }
+
+            String key = it.next();
+            DbAttribute attr = objectMatchTranslator.getAttribute(key);
+            Object val = objectMatchTranslator.getValue(key);
+
+            processColumn(attr);
+            out.append(objectMatchTranslator.getOperation());
+            appendLiteral(val, attr, objectMatchTranslator.getExpression());
+        }
+
+        objectMatchTranslator.reset();
+    }
+
+    public void finishedChild(Expression node, int childIndex, boolean hasMoreChildren) {
+
+        if (!hasMoreChildren) {
+            return;
+        }
+
+        Appendable out = (matchingObject) ? new StringBuilder() : this.out;
+
+        try {
+            switch (node.getType()) {
+                case Expression.AND:
+                    out.append(" AND ");
+                    break;
+                case Expression.OR:
+                    out.append(" OR ");
+                    break;
+                case Expression.EQUAL_TO:
+                    // translate NULL as IS NULL
+                    if (childIndex == 0
+                            && node.getOperandCount() == 2
+                            && node.getOperand(1) == null) {
+                        out.append(" IS ");
+                    }
+                    else {
+                        out.append(" = ");
+                    }
+                    break;
+                case Expression.NOT_EQUAL_TO:
+                    // translate NULL as IS NOT NULL
+                    if (childIndex == 0
+                            && node.getOperandCount() == 2
+                            && node.getOperand(1) == null) {
+                        out.append(" IS NOT ");
+                    }
+                    else {
+                        out.append(" <> ");
+                    }
+                    break;
+                case Expression.LESS_THAN:
+                    out.append(" < ");
+                    break;
+                case Expression.GREATER_THAN:
+                    out.append(" > ");
+                    break;
+                case Expression.LESS_THAN_EQUAL_TO:
+                    out.append(" <= ");
+                    break;
+                case Expression.GREATER_THAN_EQUAL_TO:
+                    out.append(" >= ");
+                    break;
+                case Expression.IN:
+                    out.append(" IN ");
+                    break;
+                case Expression.NOT_IN:
+                    out.append(" NOT IN ");
+                    break;
+                case Expression.LIKE:
+                    out.append(" LIKE ");
+                    break;
+                case Expression.NOT_LIKE:
+                    out.append(" NOT LIKE ");
+                    break;
+                case Expression.LIKE_IGNORE_CASE:
+                    out.append(") LIKE UPPER(");
+                    break;
+                case Expression.NOT_LIKE_IGNORE_CASE:
+                    out.append(") NOT LIKE UPPER(");
+                    break;
+                case Expression.ADD:
+                    out.append(" + ");
+                    break;
+                case Expression.SUBTRACT:
+                    out.append(" - ");
+                    break;
+                case Expression.MULTIPLY:
+                    out.append(" * ");
+                    break;
+                case Expression.DIVIDE:
+                    out.append(" / ");
+                    break;
+                case Expression.BETWEEN:
+                    if (childIndex == 0)
+                        out.append(" BETWEEN ");
+                    else if (childIndex == 1)
+                        out.append(" AND ");
+                    break;
+                case Expression.NOT_BETWEEN:
+                    if (childIndex == 0)
+                        out.append(" NOT BETWEEN ");
+                    else if (childIndex == 1)
+                        out.append(" AND ");
+                    break;
+            }
+        }
+        catch (IOException ioex) {
+            throw new CayenneRuntimeException("Error appending content", ioex);
+        }
+
+        if (matchingObject) {
+            objectMatchTranslator.setOperation(out.toString());
+            objectMatchTranslator.setExpression(node);
+        }
+    }
+
+    public void startNode(Expression node, Expression parentNode) {
+        int count = node.getOperandCount();
+
+        if (count == 2) {
+            // binary nodes are the only ones that currently require this
+            detectObjectMatch(node);
+        }
+
+        try {
+
+            if (parenthesisNeeded(node, parentNode)) {
+                out.append('(');
+            }
+
+            if (count == 0) {
+                // not all databases handle true/false
+                if (node.getType() == Expression.TRUE) {
+                    out.append("1 = 1");
+                }
+                if (node.getType() == Expression.FALSE) {
+                    out.append("1 = 0");
+                }
+            }
+
+            if (count == 1) {
+                if (node.getType() == Expression.NEGATIVE)
+                    out.append('-');
+                // ignore POSITIVE - it is a NOOP
+                // else if(node.getType() == Expression.POSITIVE)
+                // qualBuf.append('+');
+                else if (node.getType() == Expression.NOT)
+                    out.append("NOT ");
+            }
+            else if (node.getType() == Expression.LIKE_IGNORE_CASE
+                    || node.getType() == Expression.NOT_LIKE_IGNORE_CASE) {
+                out.append("UPPER(");
+            }
+        }
+        catch (IOException ioex) {
+            throw new CayenneRuntimeException("Error appending content", ioex);
+        }
+    }
+
+    /**
+     * @since 1.1
+     */
+    public void endNode(Expression node, Expression parentNode) {
+
+        try {
+            // check if we need to use objectMatchTranslator to finish building the
+            // expression
+            if (node.getOperandCount() == 2 && matchingObject) {
+                appendObjectMatch();
+            }
+
+            if (parenthesisNeeded(node, parentNode)) {
+                out.append(')');
+            }
+
+            if (node.getType() == Expression.LIKE_IGNORE_CASE
+                    || node.getType() == Expression.NOT_LIKE_IGNORE_CASE) {
+                out.append(')');
+            }
+        }
+        catch (IOException ioex) {
+            throw new CayenneRuntimeException("Error appending content", ioex);
+        }
+    }
+
+    public void objectNode(Object leaf, Expression parentNode) {
+
+        try {
+            if (parentNode.getType() == Expression.OBJ_PATH) {
+                appendObjPath(parentNode);
+            }
+            else if (parentNode.getType() == Expression.DB_PATH) {
+                appendDbPath(parentNode);
+            }
+            else if (parentNode.getType() == Expression.LIST) {
+                appendList(parentNode, paramsDbType(parentNode));
+            }
+            else {
+                appendLiteral(leaf, paramsDbType(parentNode), parentNode);
+            }
+        }
+        catch (IOException ioex) {
+            throw new CayenneRuntimeException("Error appending content", ioex);
+        }
+    }
+
+    protected boolean parenthesisNeeded(Expression node, Expression parentNode) {
+        if (parentNode == null)
+            return false;
+
+        // only unary expressions can go w/o parenthesis
+        if (node.getOperandCount() > 1)
+            return true;
+
+        if (node.getType() == Expression.OBJ_PATH)
+            return false;
+
+        if (node.getType() == Expression.DB_PATH)
+            return false;
+
+        return true;
+    }
+
+    private final void appendList(Expression listExpr, DbAttribute paramDesc)
+            throws IOException {
+        Iterator<?> it = null;
+        Object list = listExpr.getOperand(0);
+        if (list instanceof List) {
+            it = ((List<?>) list).iterator();
+        }
+        else if (list instanceof Object[]) {
+            it = IteratorUtils.arrayIterator((Object[]) list);
+        }
+        else {
+            String className = (list != null) ? list.getClass().getName() : "<null>";
+            throw new IllegalArgumentException(
+                    "Unsupported type for the list expressions: " + className);
+        }
+
+        // process first element outside the loop
+        // (unroll loop to avoid condition checking
+        if (it.hasNext())
+            appendLiteral(it.next(), paramDesc, listExpr);
+        else
+            return;
+
+        while (it.hasNext()) {
+            out.append(", ");
+            appendLiteral(it.next(), paramDesc, listExpr);
+        }
+    }
+
+    @Override
+    protected void appendLiteral(Object val, DbAttribute attr, Expression parentExpression)
+            throws IOException {
+
+        if (!matchingObject) {
+            super.appendLiteral(val, attr, parentExpression);
+        }
+        else if (val == null || (val instanceof Persistent)) {
+            objectMatchTranslator.setDataObject((Persistent) val);
+        }
+        else if (val instanceof ObjectId) {
+            objectMatchTranslator.setObjectId((ObjectId) val);
+        }
+        else {
+            throw new IllegalArgumentException(
+                    "Attempt to use literal other than DataObject during object match.");
+        }
+    }
+
+    @Override
+    protected void processRelTermination(
+            DbRelationship rel,
+            JoinType joinType,
+            String joinSplitAlias) throws IOException {
+
+        if (!matchingObject) {
+            super.processRelTermination(rel, joinType, joinSplitAlias);
+        }
+        else {
+            if (rel.isToMany()) {
+                // append joins
+                queryAssembler.dbRelationshipAdded(rel, joinType, joinSplitAlias);
+            }
+            objectMatchTranslator.setRelationship(rel, joinSplitAlias);
+        }
+    }
+    
+    /**
+     * Class to translate DB Entity qualifiers annotation to Obj-entity qualifiers annotation
+     * This is done by changing all Obj-paths to Db-paths and rejecting all original Db-paths
+     */
+    class DbEntityQualifierTransformer implements Transformer {
+        public Object transform(Object input) {
+            if (input instanceof ASTObjPath) {
+                return new ASTDbPath(((SimpleNode) input).getOperand(0));
+            }
+            return input;
+        }
+    }
+}

Propchange: cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/trans/QualifierTranslator.java
------------------------------------------------------------------------------
    svn:mergeinfo = 

Modified: cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/trans/QueryAssemblerHelper.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/trans/QueryAssemblerHelper.java?rev=785634&r1=785633&r2=785634&view=diff
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/trans/QueryAssemblerHelper.java (original)
+++ cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/trans/QueryAssemblerHelper.java Wed Jun 17 14:52:11 2009
@@ -82,6 +82,20 @@
         doAppendPart();
         return out;
     }
+    
+    /**
+     * Sets ouput buffer
+     */
+    void setOut(Appendable out) {
+        this.out = out;
+    }
+    
+    /**
+     * @return output buffer
+     */
+    Appendable getOut() {
+        return out;
+    }
 
     /**
      * @since 3.0

Modified: cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/trans/SelectTranslator.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/trans/SelectTranslator.java?rev=785634&r1=785633&r2=785634&view=diff
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/trans/SelectTranslator.java (original)
+++ cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/trans/SelectTranslator.java Wed Jun 17 14:52:11 2009
@@ -97,7 +97,7 @@
     boolean forcingDistinct;
 
     protected JoinStack createJoinStack() {
-        return new JoinStack(getAdapter(), queryMetadata.getDataMap());
+        return new JoinStack(getAdapter(), queryMetadata.getDataMap(), this);
     }
 
     /**
@@ -124,7 +124,8 @@
         this.resultColumns = buildResultColumns();
 
         // build qualifier
-        StringBuilder qualifierBuffer = adapter.getQualifierTranslator(this).appendPart(
+        QualifierTranslator qualifierTranslator = adapter.getQualifierTranslator(this);
+        StringBuilder qualifierBuffer = qualifierTranslator.appendPart(
                 new StringBuilder());
 
         // build ORDER BY
@@ -545,6 +546,7 @@
      * @deprecated since 3.0. Will likely be removed after 3.0M6. Can be replaced with
      *             EJBQL.
      */
+    @Deprecated
     List<ColumnDescriptor> appendCustomColumns(
             List<ColumnDescriptor> columns,
             SelectQuery query) {

Modified: cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/dba/openbase/OpenBaseJoinStack.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/dba/openbase/OpenBaseJoinStack.java?rev=785634&r1=785633&r2=785634&view=diff
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/dba/openbase/OpenBaseJoinStack.java (original)
+++ cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/dba/openbase/OpenBaseJoinStack.java Wed Jun 17 14:52:11 2009
@@ -23,6 +23,7 @@
 
 import org.apache.cayenne.access.trans.JoinStack;
 import org.apache.cayenne.access.trans.JoinTreeNode;
+import org.apache.cayenne.access.trans.QueryAssembler;
 import org.apache.cayenne.dba.DbAdapter;
 import org.apache.cayenne.map.DataMap;
 import org.apache.cayenne.map.DbEntity;
@@ -39,8 +40,8 @@
  */
 class OpenBaseJoinStack extends JoinStack {
 
-    protected OpenBaseJoinStack(DbAdapter dbAdapter, DataMap dataMap) {
-        super(dbAdapter, dataMap);
+    protected OpenBaseJoinStack(DbAdapter dbAdapter, DataMap dataMap, QueryAssembler assembler) {
+        super(dbAdapter, dataMap, assembler);
     }
 
     @Override

Modified: cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/dba/openbase/OpenBaseSelectTranslator.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/dba/openbase/OpenBaseSelectTranslator.java?rev=785634&r1=785633&r2=785634&view=diff
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/dba/openbase/OpenBaseSelectTranslator.java (original)
+++ cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/dba/openbase/OpenBaseSelectTranslator.java Wed Jun 17 14:52:11 2009
@@ -29,7 +29,7 @@
 
     @Override
     protected JoinStack createJoinStack() {
-        return new OpenBaseJoinStack(getAdapter(), queryMetadata.getDataMap());
+        return new OpenBaseJoinStack(getAdapter(), queryMetadata.getDataMap(), this);
     }
 
     @Override

Modified: cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/dba/oracle/Oracle8JoinStack.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/dba/oracle/Oracle8JoinStack.java?rev=785634&r1=785633&r2=785634&view=diff
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/dba/oracle/Oracle8JoinStack.java (original)
+++ cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/dba/oracle/Oracle8JoinStack.java Wed Jun 17 14:52:11 2009
@@ -23,6 +23,7 @@
 
 import org.apache.cayenne.access.trans.JoinStack;
 import org.apache.cayenne.access.trans.JoinTreeNode;
+import org.apache.cayenne.access.trans.QueryAssembler;
 import org.apache.cayenne.dba.DbAdapter;
 import org.apache.cayenne.map.DataMap;
 import org.apache.cayenne.map.DbEntity;
@@ -35,8 +36,8 @@
 // cloned from OpenBaseJoin stack... need better strategies of reuse...
 class Oracle8JoinStack extends JoinStack {
 
-    Oracle8JoinStack(DbAdapter dbAdapter, DataMap dataMap) {
-        super(dbAdapter, dataMap);
+    Oracle8JoinStack(DbAdapter dbAdapter, DataMap dataMap, QueryAssembler assembler) {
+        super(dbAdapter, dataMap, assembler);
     }
 
     @Override

Modified: cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/dba/oracle/Oracle8SelectTranslator.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/dba/oracle/Oracle8SelectTranslator.java?rev=785634&r1=785633&r2=785634&view=diff
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/dba/oracle/Oracle8SelectTranslator.java (original)
+++ cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/dba/oracle/Oracle8SelectTranslator.java Wed Jun 17 14:52:11 2009
@@ -31,7 +31,7 @@
      */
     @Override
     protected JoinStack createJoinStack() {
-        return new Oracle8JoinStack(getAdapter(), queryMetadata.getDataMap());
+        return new Oracle8JoinStack(getAdapter(), queryMetadata.getDataMap(), this);
     }
 
 }

Modified: cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/map/DbEntity.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/map/DbEntity.java?rev=785634&r1=785633&r2=785634&view=diff
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/map/DbEntity.java (original)
+++ cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/map/DbEntity.java Wed Jun 17 14:52:11 2009
@@ -65,7 +65,7 @@
     /**
      * Qualifier, that will be applied to all select queries and joins with this DbEntity
      */
-    protected String qualifier;
+    protected Expression qualifier;
 
     /**
      * Creates an unnamed DbEntity.
@@ -116,9 +116,9 @@
             getPrimaryKeyGenerator().encodeAsXML(encoder);
         }
         
-        if (getQualifier() != null && getQualifier().trim().length() > 0) {
+        if (getQualifier() != null) {
             encoder.print("<qualifier><![CDATA[");
-            encoder.print(getQualifier());
+            getQualifier().encodeAsXML(encoder);
             encoder.println("]]></qualifier>");
         }
 
@@ -559,14 +559,14 @@
     /**
      * @return qualifier that will be ANDed to all select queries with this entity
      */
-    public String getQualifier() {
+    public Expression getQualifier() {
         return qualifier;
     }
     
     /**
      * Sets qualifier for this entity
      */
-    public void setQualifier(String qualifier) {
+    public void setQualifier(Expression qualifier) {
         this.qualifier = qualifier;
     }
 

Modified: cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/map/MapLoader.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/map/MapLoader.java?rev=785634&r1=785633&r2=785634&view=diff
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/map/MapLoader.java (original)
+++ cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/map/MapLoader.java Wed Jun 17 14:52:11 2009
@@ -1241,7 +1241,7 @@
             objEntity.setDeclaredQualifier(Expression.fromString(qualifier));
         }
         else if (dbEntity != null) {
-            dbEntity.setQualifier(qualifier);
+            dbEntity.setQualifier(Expression.fromString(qualifier));
         }
         else {
             queryBuilder.setQualifier(qualifier);

Modified: cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/access/trans/SelectTranslatorTest.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/access/trans/SelectTranslatorTest.java?rev=785634&r1=785633&r2=785634&view=diff
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/access/trans/SelectTranslatorTest.java (original)
+++ cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/access/trans/SelectTranslatorTest.java Wed Jun 17 14:52:11 2009
@@ -80,8 +80,8 @@
         SelectQuery q = new SelectQuery(Artist.class);
         final DbEntity entity = getNode().getEntityResolver().getDbEntity("ARTIST");
         final DbEntity middleEntity = getNode().getEntityResolver().getDbEntity("ARTIST_GROUP");
-        entity.setQualifier("ARTIST_NAME = \"123\"");
-        middleEntity.setQualifier("ARTIST_GROUP_ID = 1987");
+        entity.setQualifier(Expression.fromString("ARTIST_NAME = \"123\""));
+        middleEntity.setQualifier(Expression.fromString("GROUP_ID = 1987"));
 
         try {
             Template test = new Template() {
@@ -93,14 +93,15 @@
                     assertNotNull(generatedSql);
                     assertTrue(generatedSql.startsWith("SELECT "));
                     assertTrue(generatedSql.indexOf(" FROM ") > 0);
-                    assertTrue(generatedSql.indexOf(entity.getQualifier()) > 0);
+                    assertTrue(generatedSql.indexOf("ARTIST_NAME = ") > 0);
                 }
             };
     
             test.test(q);
             
             //testing quering from related table 
-            q = new SelectQuery(Painting.class, ExpressionFactory.matchExp("toArtist.artistName", "foo"));
+            q = new SelectQuery(Painting.class, 
+                    ExpressionFactory.matchExp("toArtist.artistName", "foo"));
             test.test(q);
             
             //testing flattened rels
@@ -108,7 +109,7 @@
             new Template() {
                 @Override
                 void test(SelectTranslator transl) throws Exception {
-                    assertTrue(transl.createSqlString().indexOf(middleEntity.getQualifier()) > 0);
+                    assertTrue(transl.createSqlString().indexOf("GROUP_ID = ") > 0);
                 }
             }.test(q);
         }