You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cayenne.apache.org by aa...@apache.org on 2007/03/26 11:30:40 UTC

svn commit: r522464 - in /cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src: main/java/org/apache/cayenne/access/jdbc/ main/java/org/apache/cayenne/ejbql/ main/java/org/apache/cayenne/ejbql/parser/ main/java/org/apache/cayenne/query/ test/jav...

Author: aadamchik
Date: Mon Mar 26 02:30:37 2007
New Revision: 522464

URL: http://svn.apache.org/viewvc?view=rev&rev=522464
Log:
CAY-452: EJB QL Cayenne Query
support for most conditions, paths; clearing space for joins support

Added:
    cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/main/java/org/apache/cayenne/query/EJBQLPathTranslator.java
      - copied, changed from r520869, cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/main/java/org/apache/cayenne/query/EJBQLPathVisitor.java
Removed:
    cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/main/java/org/apache/cayenne/query/EJBQLPathVisitor.java
Modified:
    cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/main/java/org/apache/cayenne/access/jdbc/SQLTemplateProcessor.java
    cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/main/java/org/apache/cayenne/ejbql/EJBQLBaseVisitor.java
    cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/main/java/org/apache/cayenne/ejbql/EJBQLDelegatingVisitor.java
    cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/main/java/org/apache/cayenne/ejbql/EJBQLExpression.java
    cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/main/java/org/apache/cayenne/ejbql/EJBQLExpressionVisitor.java
    cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/main/java/org/apache/cayenne/ejbql/parser/EJBQLDelete.java
    cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/main/java/org/apache/cayenne/ejbql/parser/EJBQLPath.java
    cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/main/java/org/apache/cayenne/ejbql/parser/EJBQLSelect.java
    cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/main/java/org/apache/cayenne/ejbql/parser/EJBQLUpdate.java
    cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/main/java/org/apache/cayenne/ejbql/parser/SimpleNode.java
    cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/main/java/org/apache/cayenne/query/EJBQLConditionTranslator.java
    cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/main/java/org/apache/cayenne/query/EJBQLFromTranslator.java
    cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/main/java/org/apache/cayenne/query/EJBQLQuery.java
    cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/main/java/org/apache/cayenne/query/EJBQLSelectTranslator.java
    cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/main/java/org/apache/cayenne/query/EJBQLTranslator.java
    cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/test/java/org/apache/cayenne/access/DataContextEJBQLQueryTest.java
    cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/test/java/org/apache/cayenne/query/EJBQLTranslatorTest.java
    cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/test/resources/dml/access.DataContextEJBQLQueryTest.xml

Modified: cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/main/java/org/apache/cayenne/access/jdbc/SQLTemplateProcessor.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/main/java/org/apache/cayenne/access/jdbc/SQLTemplateProcessor.java?view=diff&rev=522464&r1=522463&r2=522464
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/main/java/org/apache/cayenne/access/jdbc/SQLTemplateProcessor.java (original)
+++ cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/main/java/org/apache/cayenne/access/jdbc/SQLTemplateProcessor.java Mon Mar 26 02:30:37 2007
@@ -26,15 +26,14 @@
 import java.util.List;
 import java.util.Map;
 
+import org.apache.cayenne.CayenneRuntimeException;
 import org.apache.velocity.VelocityContext;
 import org.apache.velocity.context.InternalContextAdapterImpl;
-import org.apache.velocity.exception.ParseErrorException;
 import org.apache.velocity.runtime.RuntimeConstants;
 import org.apache.velocity.runtime.RuntimeInstance;
 import org.apache.velocity.runtime.log.NullLogSystem;
 import org.apache.velocity.runtime.parser.ParseException;
 import org.apache.velocity.runtime.parser.node.SimpleNode;
-import org.apache.cayenne.CayenneRuntimeException;
 
 /**
  * Processor for SQL velocity templates.
@@ -144,7 +143,10 @@
             nodeTree = velocityRuntime.parse(new StringReader(template), template);
         }
         catch (ParseException pex) {
-            throw new ParseErrorException(pex.getMessage());
+            throw new CayenneRuntimeException("Error parsing template '"
+                    + template
+                    + "' : "
+                    + pex.getMessage());
         }
 
         if (nodeTree == null) {

Modified: cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/main/java/org/apache/cayenne/ejbql/EJBQLBaseVisitor.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/main/java/org/apache/cayenne/ejbql/EJBQLBaseVisitor.java?view=diff&rev=522464&r1=522463&r2=522464
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/main/java/org/apache/cayenne/ejbql/EJBQLBaseVisitor.java (original)
+++ cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/main/java/org/apache/cayenne/ejbql/EJBQLBaseVisitor.java Mon Mar 26 02:30:37 2007
@@ -121,7 +121,7 @@
         return continueFlag;
     }
 
-    public boolean visitDelete(EJBQLExpression expression) {
+    public boolean visitDelete(EJBQLExpression expression, int finishedChildIndex) {
         return continueFlag;
     }
 
@@ -289,7 +289,7 @@
         return continueFlag;
     }
 
-    public boolean visitPath(EJBQLExpression expression) {
+    public boolean visitPath(EJBQLExpression expression, int finishedChildIndex) {
         return continueFlag;
     }
 
@@ -301,7 +301,7 @@
         return continueFlag;
     }
 
-    public boolean visitSelect(EJBQLExpression expression) {
+    public boolean visitSelect(EJBQLExpression expression, int finishedChildIndex) {
         return continueFlag;
     }
 
@@ -361,7 +361,7 @@
         return continueFlag;
     }
 
-    public boolean visitUpdate(EJBQLExpression expression) {
+    public boolean visitUpdate(EJBQLExpression expression, int finishedChildIndex) {
         return continueFlag;
     }
 

Modified: cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/main/java/org/apache/cayenne/ejbql/EJBQLDelegatingVisitor.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/main/java/org/apache/cayenne/ejbql/EJBQLDelegatingVisitor.java?view=diff&rev=522464&r1=522463&r2=522464
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/main/java/org/apache/cayenne/ejbql/EJBQLDelegatingVisitor.java (original)
+++ cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/main/java/org/apache/cayenne/ejbql/EJBQLDelegatingVisitor.java Mon Mar 26 02:30:37 2007
@@ -128,8 +128,10 @@
         return delegate != null ? delegate.visitDecimalLiteral(expression) : false;
     }
 
-    public boolean visitDelete(EJBQLExpression expression) {
-        return delegate != null ? delegate.visitDelete(expression) : false;
+    public boolean visitDelete(EJBQLExpression expression, int finishedChildIndex) {
+        return delegate != null
+                ? delegate.visitDelete(expression, finishedChildIndex)
+                : false;
     }
 
     public boolean visitDescending(EJBQLExpression expression) {
@@ -318,8 +320,10 @@
         return delegate != null ? delegate.visitOuterJoin(expression) : false;
     }
 
-    public boolean visitPath(EJBQLExpression expression) {
-        return delegate != null ? delegate.visitPath(expression) : false;
+    public boolean visitPath(EJBQLExpression expression, int finishedChildIndex) {
+        return delegate != null
+                ? delegate.visitPath(expression, finishedChildIndex)
+                : false;
     }
 
     public boolean visitPatternValue(EJBQLExpression expression) {
@@ -332,8 +336,10 @@
                 : false;
     }
 
-    public boolean visitSelect(EJBQLExpression expression) {
-        return delegate != null ? delegate.visitSelect(expression) : false;
+    public boolean visitSelect(EJBQLExpression expression, int finishedChildIndex) {
+        return delegate != null
+                ? delegate.visitSelect(expression, finishedChildIndex)
+                : false;
     }
 
     public boolean visitSelectExpression(EJBQLExpression expression) {
@@ -394,8 +400,10 @@
         return delegate != null ? delegate.visitTrimTrailing(expression) : false;
     }
 
-    public boolean visitUpdate(EJBQLExpression expression) {
-        return delegate != null ? delegate.visitUpdate(expression) : false;
+    public boolean visitUpdate(EJBQLExpression expression, int finishedChildIndex) {
+        return delegate != null
+                ? delegate.visitUpdate(expression, finishedChildIndex)
+                : false;
     }
 
     public boolean visitUpdateField(EJBQLExpression expression) {

Modified: cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/main/java/org/apache/cayenne/ejbql/EJBQLExpression.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/main/java/org/apache/cayenne/ejbql/EJBQLExpression.java?view=diff&rev=522464&r1=522463&r2=522464
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/main/java/org/apache/cayenne/ejbql/EJBQLExpression.java (original)
+++ cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/main/java/org/apache/cayenne/ejbql/EJBQLExpression.java Mon Mar 26 02:30:37 2007
@@ -47,4 +47,9 @@
      * Returns a text property of the node.
      */
     String getText();
+
+    /**
+     * Returns an optional boolean flag that negates the value of the expression.
+     */
+    boolean isNegated();
 }

Modified: cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/main/java/org/apache/cayenne/ejbql/EJBQLExpressionVisitor.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/main/java/org/apache/cayenne/ejbql/EJBQLExpressionVisitor.java?view=diff&rev=522464&r1=522463&r2=522464
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/main/java/org/apache/cayenne/ejbql/EJBQLExpressionVisitor.java (original)
+++ cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/main/java/org/apache/cayenne/ejbql/EJBQLExpressionVisitor.java Mon Mar 26 02:30:37 2007
@@ -93,7 +93,16 @@
 
     boolean visitDecimalLiteral(EJBQLExpression expression);
 
-    boolean visitDelete(EJBQLExpression expression);
+    /**
+     * Called on visiting "delete" expression and also after visiting every expression
+     * child.
+     * 
+     * @param expression a "delete" node being visited.
+     * @param finishedChildIndex "-1" when the expression node is visited for the first
+     *            time, before its children; otherwise this is an index of a child just
+     *            visited.
+     */
+    boolean visitDelete(EJBQLExpression expression, int finishedChildIndex);
 
     boolean visitDescending(EJBQLExpression expression);
 
@@ -260,13 +269,30 @@
 
     boolean visitOuterJoin(EJBQLExpression expression);
 
-    boolean visitPath(EJBQLExpression expression);
+    /**
+     * Called on visiting "path" expression and also after visiting every expression
+     * child.
+     * 
+     * @param expression a "path" node being visited.
+     * @param finishedChildIndex "-1" when the expression node is visited for the first
+     *            time, before its children; otherwise this is an index of a child just
+     *            visited.
+     */
+    boolean visitPath(EJBQLExpression expression, int finishedChildIndex);
 
     boolean visitPatternValue(EJBQLExpression expression);
 
     boolean visitPositionalInputParameter(EJBQLExpression expression);
 
-    boolean visitSelect(EJBQLExpression expression);
+    /**
+     * Called on visiting "select" and also after visiting every expression child.
+     * 
+     * @param expression a "select" node being visited.
+     * @param finishedChildIndex "-1" when the expression node is visited for the first
+     *            time, before its children; otherwise this is an index of a child just
+     *            visited.
+     */
+    boolean visitSelect(EJBQLExpression expression, int finishedChildIndex);
 
     boolean visitSelectExpression(EJBQLExpression expression);
 
@@ -305,14 +331,23 @@
 
     boolean visitTrimTrailing(EJBQLExpression expression);
 
-    boolean visitUpdate(EJBQLExpression expression);
-    
+    /**
+     * Called on visiting "update" expression and also after visiting every expression
+     * child.
+     * 
+     * @param expression a "update" node being visited.
+     * @param finishedChildIndex "-1" when the expression node is visited for the first
+     *            time, before its children; otherwise this is an index of a child just
+     *            visited.
+     */
+    boolean visitUpdate(EJBQLExpression expression, int finishedChildIndex);
+
     boolean visitUpdateField(EJBQLExpression expression);
-    
+
     boolean visitUpdateItem(EJBQLExpression expression);
-    
+
     boolean visitUpdateValue(EJBQLExpression expression);
-    
+
     boolean visitUpper(EJBQLExpression expression);
 
     boolean visitWhere(EJBQLExpression expression);

Modified: cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/main/java/org/apache/cayenne/ejbql/parser/EJBQLDelete.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/main/java/org/apache/cayenne/ejbql/parser/EJBQLDelete.java?view=diff&rev=522464&r1=522463&r2=522464
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/main/java/org/apache/cayenne/ejbql/parser/EJBQLDelete.java (original)
+++ cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/main/java/org/apache/cayenne/ejbql/parser/EJBQLDelete.java Mon Mar 26 02:30:37 2007
@@ -25,15 +25,21 @@
  * @author Andrus Adamchik
  */
 public class EJBQLDelete extends SimpleNode {
-	public EJBQLDelete(int id) {
-		super(id);
-	}
+
+    public EJBQLDelete(int id) {
+        super(id);
+    }
 
     EJBQLDelete(AbstractParser parser, int id) {
         super(id);
     }
-    
+
     protected boolean visitNode(EJBQLExpressionVisitor visitor) {
-        return visitor.visitDelete(this);
+        return visitor.visitDelete(this, -1);
+    }
+
+    protected boolean visitChild(EJBQLExpressionVisitor visitor, int childIndex) {
+        return super.visitChild(visitor, childIndex)
+                && visitor.visitDelete(this, childIndex);
     }
 }

Modified: cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/main/java/org/apache/cayenne/ejbql/parser/EJBQLPath.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/main/java/org/apache/cayenne/ejbql/parser/EJBQLPath.java?view=diff&rev=522464&r1=522463&r2=522464
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/main/java/org/apache/cayenne/ejbql/parser/EJBQLPath.java (original)
+++ cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/main/java/org/apache/cayenne/ejbql/parser/EJBQLPath.java Mon Mar 26 02:30:37 2007
@@ -35,6 +35,11 @@
     }
 
     protected boolean visitNode(EJBQLExpressionVisitor visitor) {
-        return visitor.visitPath(this);
+        return visitor.visitPath(this, -1);
+    }
+
+    protected boolean visitChild(EJBQLExpressionVisitor visitor, int childIndex) {
+        return super.visitChild(visitor, childIndex)
+                && visitor.visitPath(this, childIndex);
     }
 }

Modified: cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/main/java/org/apache/cayenne/ejbql/parser/EJBQLSelect.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/main/java/org/apache/cayenne/ejbql/parser/EJBQLSelect.java?view=diff&rev=522464&r1=522463&r2=522464
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/main/java/org/apache/cayenne/ejbql/parser/EJBQLSelect.java (original)
+++ cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/main/java/org/apache/cayenne/ejbql/parser/EJBQLSelect.java Mon Mar 26 02:30:37 2007
@@ -29,12 +29,17 @@
     public EJBQLSelect(int id) {
         super(id);
     }
-    
+
     EJBQLSelect(AbstractParser parser, int id) {
         super(id);
     }
 
     protected boolean visitNode(EJBQLExpressionVisitor visitor) {
-        return visitor.visitSelect(this);
+        return visitor.visitSelect(this, -1);
+    }
+
+    protected boolean visitChild(EJBQLExpressionVisitor visitor, int childIndex) {
+        return super.visitChild(visitor, childIndex)
+                && visitor.visitSelect(this, childIndex);
     }
 }

Modified: cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/main/java/org/apache/cayenne/ejbql/parser/EJBQLUpdate.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/main/java/org/apache/cayenne/ejbql/parser/EJBQLUpdate.java?view=diff&rev=522464&r1=522463&r2=522464
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/main/java/org/apache/cayenne/ejbql/parser/EJBQLUpdate.java (original)
+++ cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/main/java/org/apache/cayenne/ejbql/parser/EJBQLUpdate.java Mon Mar 26 02:30:37 2007
@@ -25,15 +25,21 @@
  * @author Andrus Adamchik
  */
 public class EJBQLUpdate extends SimpleNode {
-	public EJBQLUpdate(int id) {
-		super(id);
-	}
-    
+
+    public EJBQLUpdate(int id) {
+        super(id);
+    }
+
     EJBQLUpdate(AbstractParser parser, int id) {
         super(id);
     }
-    
+
     protected boolean visitNode(EJBQLExpressionVisitor visitor) {
-        return visitor.visitUpdate(this);
+        return visitor.visitUpdate(this, -1);
+    }
+
+    protected boolean visitChild(EJBQLExpressionVisitor visitor, int childIndex) {
+        return super.visitChild(visitor, childIndex)
+                && visitor.visitUpdate(this, childIndex);
     }
 }

Modified: cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/main/java/org/apache/cayenne/ejbql/parser/SimpleNode.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/main/java/org/apache/cayenne/ejbql/parser/SimpleNode.java?view=diff&rev=522464&r1=522463&r2=522464
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/main/java/org/apache/cayenne/ejbql/parser/SimpleNode.java (original)
+++ cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/main/java/org/apache/cayenne/ejbql/parser/SimpleNode.java Mon Mar 26 02:30:37 2007
@@ -46,6 +46,10 @@
     public String getText() {
         return text;
     }
+    
+    public boolean isNegated() {
+        return not;
+    }
 
     /**
      * A recursive visit method that passes a visitor to this node and all its children,

Modified: cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/main/java/org/apache/cayenne/query/EJBQLConditionTranslator.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/main/java/org/apache/cayenne/query/EJBQLConditionTranslator.java?view=diff&rev=522464&r1=522463&r2=522464
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/main/java/org/apache/cayenne/query/EJBQLConditionTranslator.java (original)
+++ cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/main/java/org/apache/cayenne/query/EJBQLConditionTranslator.java Mon Mar 26 02:30:37 2007
@@ -18,7 +18,10 @@
  ****************************************************************/
 package org.apache.cayenne.query;
 
+import java.math.BigDecimal;
+
 import org.apache.cayenne.ejbql.EJBQLDelegatingVisitor;
+import org.apache.cayenne.ejbql.EJBQLException;
 import org.apache.cayenne.ejbql.EJBQLExpression;
 
 /**
@@ -38,8 +41,23 @@
         return true;
     }
 
-    public boolean visitEquals(EJBQLExpression expression, int finishedChildIndex) {
-        afterChild(expression, " #", finishedChildIndex);
+    public boolean visitBetween(EJBQLExpression expression, int finishedChildIndex) {
+        setDelegate(null);
+        switch (finishedChildIndex) {
+            case 0:
+                if (expression.isNegated()) {
+                    parent.getParent().getBuffer().append(" NOT");
+                }
+                parent.getParent().getBuffer().append(" BETWEEN #bind(");
+                break;
+            case 1:
+                parent.getParent().getBuffer().append(") AND #bind(");
+                break;
+            case 2:
+                parent.getParent().getBuffer().append(")");
+                break;
+        }
+
         return true;
     }
 
@@ -48,6 +66,112 @@
         return true;
     }
 
+    public boolean visitEquals(EJBQLExpression expression, int finishedChildIndex) {
+        setDelegate(null);
+        switch (finishedChildIndex) {
+            case 0:
+                parent.getParent().getBuffer().append(" #bindEqual(");
+                break;
+            case 1:
+                parent.getParent().getBuffer().append(")");
+                break;
+        }
+
+        return true;
+    }
+
+    public boolean visitNot(EJBQLExpression expression) {
+        parent.getParent().getBuffer().append(" NOT");
+        return true;
+    }
+
+    public boolean visitNotEquals(EJBQLExpression expression, int finishedChildIndex) {
+        setDelegate(null);
+        switch (finishedChildIndex) {
+            case 0:
+                parent.getParent().getBuffer().append(" #bindNotEqual(");
+                break;
+            case 1:
+                parent.getParent().getBuffer().append(")");
+                break;
+        }
+
+        return true;
+    }
+
+    public boolean visitGreaterThan(EJBQLExpression expression, int finishedChildIndex) {
+        setDelegate(null);
+        switch (finishedChildIndex) {
+            case 0:
+                parent.getParent().getBuffer().append(" > #bind(");
+                break;
+            case 1:
+                parent.getParent().getBuffer().append(")");
+                break;
+        }
+
+        return true;
+    }
+
+    public boolean visitGreaterOrEqual(EJBQLExpression expression, int finishedChildIndex) {
+        setDelegate(null);
+        switch (finishedChildIndex) {
+            case 0:
+                parent.getParent().getBuffer().append(" >= #bind(");
+                break;
+            case 1:
+                parent.getParent().getBuffer().append(")");
+                break;
+        }
+
+        return true;
+    }
+
+    public boolean visitLessOrEqual(EJBQLExpression expression, int finishedChildIndex) {
+        setDelegate(null);
+        switch (finishedChildIndex) {
+            case 0:
+                parent.getParent().getBuffer().append(" <= #bind(");
+                break;
+            case 1:
+                parent.getParent().getBuffer().append(")");
+                break;
+        }
+
+        return true;
+    }
+
+    public boolean visitLessThan(EJBQLExpression expression, int finishedChildIndex) {
+        setDelegate(null);
+        switch (finishedChildIndex) {
+            case 0:
+                parent.getParent().getBuffer().append(" < #bind(");
+                break;
+            case 1:
+                parent.getParent().getBuffer().append(")");
+                break;
+        }
+
+        return true;
+    }
+
+    public boolean visitLike(EJBQLExpression expression, int finishedChildIndex) {
+        setDelegate(null);
+        switch (finishedChildIndex) {
+            case 0:
+                if (expression.isNegated()) {
+                    parent.getParent().getBuffer().append(" NOT");
+                }
+                parent.getParent().getBuffer().append(" LIKE #bind(");
+                break;
+            case 1:
+                parent.getParent().getBuffer().append(")");
+                break;
+        }
+
+        return true;
+    }
+
     protected void afterChild(EJBQLExpression e, String text, int childIndex) {
         if (childIndex >= 0) {
             if (childIndex + 1 < e.getChildrenCount()) {
@@ -59,13 +183,71 @@
         }
     }
 
-    public boolean visitPath(EJBQLExpression expression) {
-        setDelegate(new EJBQLPathVisitor());
+    public boolean visitPath(EJBQLExpression expression, int finishedChildIndex) {
+        if (finishedChildIndex < 0) {
+            setDelegate(new EJBQLPathTranslator(parent));
+            return true;
+        }
+        else {
+            return super.visitPath(expression, finishedChildIndex);
+        }
+    }
+
+    public boolean visitStringLiteral(EJBQLExpression expression) {
+        if (expression.getText() == null) {
+            parent.getParent().getBuffer().append("null");
+        }
+        else {
+            // note that String Literal text is already wrapped in single quotes, with
+            // quotes that are part of the string escaped.
+            parent.getParent().getBuffer().append(expression.getText()).append(
+                    " 'VARCHAR'");
+        }
+        return true;
+    }
+
+    public boolean visitIntegerLiteral(EJBQLExpression expression) {
+        if (expression.getText() == null) {
+            parent.getParent().getBuffer().append("null");
+        }
+        else {
+            Object value;
+
+            try {
+                value = new Integer(expression.getText());
+            }
+            catch (NumberFormatException nfex) {
+                throw new EJBQLException("Invalid integer: " + expression.getText());
+            }
+
+            String var = parent.getParent().bindParameter(value);
+            parent.getParent().getBuffer().append('$').append(var).append(" 'INTEGER'");
+        }
+        return true;
+    }
+
+    public boolean visitDecimalLiteral(EJBQLExpression expression) {
+        if (expression.getText() == null) {
+            parent.getParent().getBuffer().append("null");
+        }
+        else {
+            Object value;
+
+            try {
+                value = new BigDecimal(expression.getText());
+            }
+            catch (NumberFormatException nfex) {
+                throw new EJBQLException("Invalid decimal: " + expression.getText());
+            }
+
+            String var = parent.getParent().bindParameter(value);
+            parent.getParent().getBuffer().append('$').append(var).append(" 'DECIMAL'");
+        }
         return true;
     }
     
-    public boolean visitStringLiteral(EJBQLExpression expression) {
-        
+    public boolean visitPatternValue(EJBQLExpression expression) {
+        // TODO: andrus 3/25/2007 - implement me
         return true;
     }
 }

Modified: cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/main/java/org/apache/cayenne/query/EJBQLFromTranslator.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/main/java/org/apache/cayenne/query/EJBQLFromTranslator.java?view=diff&rev=522464&r1=522463&r2=522464
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/main/java/org/apache/cayenne/query/EJBQLFromTranslator.java (original)
+++ cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/main/java/org/apache/cayenne/query/EJBQLFromTranslator.java Mon Mar 26 02:30:37 2007
@@ -20,8 +20,6 @@
 
 import org.apache.cayenne.ejbql.EJBQLBaseVisitor;
 import org.apache.cayenne.ejbql.EJBQLExpression;
-import org.apache.cayenne.map.DbEntity;
-import org.apache.cayenne.reflect.ClassDescriptor;
 
 public class EJBQLFromTranslator extends EJBQLBaseVisitor {
 
@@ -40,19 +38,12 @@
         return true;
     }
 
-    public boolean visitIdentificationVariable(EJBQLExpression expression) {
+    public boolean visitIdentifier(EJBQLExpression expression) {
+        parent.appendRootIdentifier(expression.getText());
         return true;
     }
 
-    public boolean visitIdentifier(EJBQLExpression expression) {
-        String identifier = expression.getText();
-        ClassDescriptor descriptor = parent
-                .getParent()
-                .getCompiledExpression()
-                .getEntityDescriptor(identifier);
-
-        DbEntity table = descriptor.getEntity().getDbEntity();
-        parent.appendTable(identifier, table);
+    public boolean visitIdentificationVariable(EJBQLExpression expression) {
         return true;
     }
 }

Copied: cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/main/java/org/apache/cayenne/query/EJBQLPathTranslator.java (from r520869, cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/main/java/org/apache/cayenne/query/EJBQLPathVisitor.java)
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/main/java/org/apache/cayenne/query/EJBQLPathTranslator.java?view=diff&rev=522464&p1=cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/main/java/org/apache/cayenne/query/EJBQLPathVisitor.java&r1=520869&p2=cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/main/java/org/apache/cayenne/query/EJBQLPathTranslator.java&r2=522464
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/main/java/org/apache/cayenne/query/EJBQLPathVisitor.java (original)
+++ cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/main/java/org/apache/cayenne/query/EJBQLPathTranslator.java Mon Mar 26 02:30:37 2007
@@ -19,12 +19,89 @@
 package org.apache.cayenne.query;
 
 import org.apache.cayenne.ejbql.EJBQLBaseVisitor;
+import org.apache.cayenne.ejbql.EJBQLException;
+import org.apache.cayenne.ejbql.EJBQLExpression;
+import org.apache.cayenne.map.DbEntity;
+import org.apache.cayenne.map.ObjAttribute;
+import org.apache.cayenne.map.ObjEntity;
+import org.apache.cayenne.map.ObjRelationship;
+import org.apache.cayenne.reflect.ClassDescriptor;
 
-class EJBQLPathVisitor extends EJBQLBaseVisitor {
+class EJBQLPathTranslator extends EJBQLBaseVisitor {
 
-    EJBQLPathVisitor() {
+    private EJBQLSelectTranslator parent;
+    private ObjEntity currentEntity;
+    private String lastPathComponent;
+    private String idPath;
+
+    EJBQLPathTranslator(EJBQLSelectTranslator parent) {
         super(true);
+        this.parent = parent;
+    }
+
+    public boolean visitPath(EJBQLExpression expression, int finishedChildIndex) {
+
+        if (finishedChildIndex > 0) {
+
+            if (finishedChildIndex + 1 < expression.getChildrenCount()) {
+                processIntermediatePath();
+            }
+            else {
+                processLastPath();
+            }
+        }
+        return true;
+    }
+
+    public boolean visitIdentifier(EJBQLExpression expression) {
+        ClassDescriptor descriptor = parent
+                .getParent()
+                .getCompiledExpression()
+                .getEntityDescriptor(expression.getText());
+        if (descriptor == null) {
+            throw new EJBQLException("Invalid identification variable: "
+                    + expression.getText());
+        }
+
+        this.currentEntity = descriptor.getEntity();
+        this.idPath = expression.getText();
+        return true;
     }
 
-    
+    public boolean visitIdentificationVariable(EJBQLExpression expression) {
+        if (this.lastPathComponent != null) {
+            this.idPath += '.' + lastPathComponent;
+        }
+
+        this.lastPathComponent = expression.getText();
+        return true;
+    }
+
+    private void processIntermediatePath() {
+        ObjRelationship relationship = (ObjRelationship) currentEntity
+                .getRelationship(lastPathComponent);
+        if (relationship == null) {
+            throw new EJBQLException("Unknown relationship '"
+                    + lastPathComponent
+                    + "' for entity '"
+                    + currentEntity.getName()
+                    + "'");
+        }
+
+        parent.appendInnerJoin(idPath + '.' + lastPathComponent);
+        this.currentEntity = (ObjEntity) relationship.getTargetEntity();
+    }
+
+    private void processLastPath() {
+        // TODO: andrus 3/25/2007 - process terminal relationships
+        ObjAttribute attribute = (ObjAttribute) currentEntity
+                .getAttribute(lastPathComponent);
+
+        DbEntity table = currentEntity.getDbEntity();
+        String alias = parent.getParent().createAlias(
+                idPath,
+                table.getFullyQualifiedName());
+        parent.getParent().getBuffer().append(' ').append(alias).append('.').append(
+                attribute.getDbAttributeName());
+    }
 }

Modified: cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/main/java/org/apache/cayenne/query/EJBQLQuery.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/main/java/org/apache/cayenne/query/EJBQLQuery.java?view=diff&rev=522464&r1=522463&r2=522464
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/main/java/org/apache/cayenne/query/EJBQLQuery.java (original)
+++ cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/main/java/org/apache/cayenne/query/EJBQLQuery.java Mon Mar 26 02:30:37 2007
@@ -44,11 +44,7 @@
      */
     protected Query createReplacementQuery(EntityResolver resolver) {
         EJBQLCompiledExpression expression = getExpression(resolver);
-
-        EJBQLTranslator translator = new EJBQLTranslator(expression);
-        String sql = translator.translate();
-
-        return new SQLTemplate(expression.getRootDescriptor().getObjectClass(), sql);
+        return new EJBQLTranslator(expression).translate();
     }
 
     /**

Modified: cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/main/java/org/apache/cayenne/query/EJBQLSelectTranslator.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/main/java/org/apache/cayenne/query/EJBQLSelectTranslator.java?view=diff&rev=522464&r1=522463&r2=522464
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/main/java/org/apache/cayenne/query/EJBQLSelectTranslator.java (original)
+++ cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/main/java/org/apache/cayenne/query/EJBQLSelectTranslator.java Mon Mar 26 02:30:37 2007
@@ -19,6 +19,7 @@
 package org.apache.cayenne.query;
 
 import java.util.HashSet;
+import java.util.Iterator;
 import java.util.Set;
 
 import org.apache.cayenne.dba.TypesMapping;
@@ -26,6 +27,7 @@
 import org.apache.cayenne.ejbql.EJBQLExpression;
 import org.apache.cayenne.map.DbAttribute;
 import org.apache.cayenne.map.DbEntity;
+import org.apache.cayenne.reflect.ClassDescriptor;
 
 /**
  * A translator of EJBQL select statements into SQL.
@@ -37,15 +39,28 @@
 
     private EJBQLTranslator parent;
     private Set columns;
+    private StringBuffer fromClause;
+    private Set fromIds;
+    private Set innerJoins;
 
     EJBQLSelectTranslator(EJBQLTranslator parent) {
         this.parent = parent;
     }
 
-    void appendTable(String identifier, DbEntity table) {
-        String fqn = table.getFullyQualifiedName();
-        String alias = parent.createAlias(identifier, fqn);
-        parent.getBuffer().append(' ').append(fqn).append(' ').append(alias);
+    void appendRootIdentifier(String identifier) {
+        if (fromIds == null) {
+            fromIds = new HashSet();
+        }
+
+        fromIds.add(identifier);
+    }
+
+    void appendInnerJoin(String path) {
+        if (innerJoins == null) {
+            innerJoins = new HashSet();
+        }
+
+        innerJoins.add(path);
     }
 
     void appendColumn(String identifier, DbAttribute column) {
@@ -80,6 +95,25 @@
         }
     }
 
+    private void postprocess() {
+
+        if (fromIds != null && fromClause != null) {
+            Iterator it = fromIds.iterator();
+            while (it.hasNext()) {
+                String id = (String) it.next();
+                ClassDescriptor descriptor = parent
+                        .getCompiledExpression()
+                        .getEntityDescriptor(id);
+                DbEntity table = descriptor.getEntity().getDbEntity();
+                String fqn = table.getFullyQualifiedName();
+                String alias = parent.createAlias(id, fqn);
+                fromClause.append(' ').append(fqn).append(' ').append(alias);
+            }
+        }
+
+        // TODO: andrus, 3/26/2007 - inner joins
+    }
+
     EJBQLTranslator getParent() {
         return parent;
     }
@@ -90,7 +124,9 @@
     }
 
     public boolean visitFrom(EJBQLExpression expression) {
-        parent.getBuffer().append(" FROM");
+        this.fromClause = new StringBuffer(" FROM");
+        String fromId = parent.bindParameter(fromClause, "from");
+        parent.getBuffer().append(" $").append(fromId);
         setDelegate(new EJBQLFromTranslator(this));
         return true;
     }
@@ -101,9 +137,15 @@
         return true;
     }
 
-    public boolean visitSelect(EJBQLExpression expression) {
-        parent.getBuffer().append("SELECT");
-        setDelegate(new EJBQLSelectColumnsTranslator(this));
+    public boolean visitSelect(EJBQLExpression expression, int finishedChildIndex) {
+        if (finishedChildIndex < 0) {
+            parent.getBuffer().append("SELECT");
+            setDelegate(new EJBQLSelectColumnsTranslator(this));
+        }
+        else if (finishedChildIndex + 1 == expression.getChildrenCount()) {
+            postprocess();
+        }
+
         return true;
     }
 

Modified: cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/main/java/org/apache/cayenne/query/EJBQLTranslator.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/main/java/org/apache/cayenne/query/EJBQLTranslator.java?view=diff&rev=522464&r1=522463&r2=522464
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/main/java/org/apache/cayenne/query/EJBQLTranslator.java (original)
+++ cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/main/java/org/apache/cayenne/query/EJBQLTranslator.java Mon Mar 26 02:30:37 2007
@@ -34,6 +34,7 @@
 class EJBQLTranslator extends EJBQLBaseVisitor {
 
     private Map aliases;
+    private Map bindingVariables;
     private StringBuffer buffer;
     private EJBQLCompiledExpression compiledExpression;
 
@@ -42,23 +43,28 @@
         this.compiledExpression = compiledExpression;
     }
 
-    String translate() {
+    SQLTemplate translate() {
         this.buffer = new StringBuffer();
         compiledExpression.getExpression().visit(this);
-        return buffer.length() > 0 ? buffer.toString() : null;
+        String sql = buffer.length() > 0 ? buffer.toString() : null;
+        SQLTemplate query = new SQLTemplate(compiledExpression
+                .getRootDescriptor()
+                .getObjectClass(), sql);
+        query.setParameters(bindingVariables);
+        return query;
     }
 
-    public boolean visitSelect(EJBQLExpression expression) {
+    public boolean visitSelect(EJBQLExpression expression, int finishedChildIndex) {
         EJBQLSelectTranslator visitor = new EJBQLSelectTranslator(this);
         expression.visit(visitor);
         return false;
     }
 
-    public boolean visitDelete(EJBQLExpression expression) {
+    public boolean visitDelete(EJBQLExpression expression, int finishedChildIndex) {
         throw new UnsupportedOperationException("Not yet implemented");
     }
 
-    public boolean visitUpdate(EJBQLExpression expression) {
+    public boolean visitUpdate(EJBQLExpression expression, int finishedChildIndex) {
         throw new UnsupportedOperationException("Not yet implemented");
     }
 
@@ -71,11 +77,47 @@
     }
 
     /**
+     * Creates a new parameter variable, binding provided value to it.
+     */
+    String bindParameter(Object value) {
+        return bindParameter(value, "id");
+    }
+
+    /**
+     * Creates a new parameter variable with the specified prefix, binding provided value
+     * to it.
+     */
+    String bindParameter(Object value, String prefix) {
+        if (bindingVariables == null) {
+            bindingVariables = new HashMap();
+        }
+
+        String var = prefix + bindingVariables.size();
+        bindingVariables.put(var, value);
+        return var;
+    }
+
+    /**
      * Retrieves a SQL alias for the combination of EJBQL id variable and a table name. If
      * such alias hasn't been used, it is created on the fly.
      */
-    String createAlias(String idVariable, String tableName) {
-        String key = idVariable + ":" + tableName;
+    String createAlias(String idPath, String tableName) {
+
+        StringBuffer keyBuffer = new StringBuffer();
+
+        // per JPA spec, 4.4.2, "Identification variables are case insensitive.", while
+        // relationship path is case-sensitive
+
+        int dot = idPath.indexOf('.');
+        if (dot > 0) {
+            keyBuffer.append(idPath.substring(0, dot).toLowerCase()).append(
+                    idPath.substring(dot));
+        }
+        else {
+            keyBuffer.append(idPath.toLowerCase());
+        }
+
+        String key = keyBuffer.append(':').append(tableName).toString();
 
         String alias;
 

Modified: cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/test/java/org/apache/cayenne/access/DataContextEJBQLQueryTest.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/test/java/org/apache/cayenne/access/DataContextEJBQLQueryTest.java?view=diff&rev=522464&r1=522463&r2=522464
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/test/java/org/apache/cayenne/access/DataContextEJBQLQueryTest.java (original)
+++ cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/test/java/org/apache/cayenne/access/DataContextEJBQLQueryTest.java Mon Mar 26 02:30:37 2007
@@ -18,9 +18,14 @@
  ****************************************************************/
 package org.apache.cayenne.access;
 
+import java.math.BigDecimal;
+import java.util.HashSet;
+import java.util.Iterator;
 import java.util.List;
+import java.util.Set;
 
 import org.apache.art.Artist;
+import org.apache.art.Painting;
 import org.apache.cayenne.PersistenceState;
 import org.apache.cayenne.query.EJBQLQuery;
 import org.apache.cayenne.unit.CayenneCase;
@@ -41,5 +46,138 @@
         assertEquals(4, artists.size());
         assertTrue(artists.get(0) instanceof Artist);
         assertTrue(((Artist) artists.get(0)).getPersistenceState() == PersistenceState.COMMITTED);
+    }
+
+    public void testSelectFromWhereEqual() throws Exception {
+        createTestData("prepare");
+
+        String ejbql = "select a from Artist a where a.artistName = 'AA2'";
+        EJBQLQuery query = new EJBQLQuery(ejbql);
+
+        List artists = createDataContext().performQuery(query);
+        assertEquals(1, artists.size());
+        assertEquals("AA2", ((Artist) artists.get(0)).getArtistName());
+    }
+
+    public void testSelectFromWhereNot() throws Exception {
+        createTestData("prepare");
+
+        String ejbql = "select a from Artist a where not a.artistName = 'AA2'";
+        EJBQLQuery query = new EJBQLQuery(ejbql);
+
+        List artists = createDataContext().performQuery(query);
+        assertEquals(3, artists.size());
+        Iterator it = artists.iterator();
+        while (it.hasNext()) {
+            Artist a = (Artist) it.next();
+            assertFalse("AA2".equals(a.getArtistName()));
+        }
+    }
+    
+    public void testSelectFromWhereNotEquals() throws Exception {
+        createTestData("prepare");
+
+        String ejbql = "select a from Artist a where a.artistName <> 'AA2'";
+        EJBQLQuery query = new EJBQLQuery(ejbql);
+
+        List artists = createDataContext().performQuery(query);
+        assertEquals(3, artists.size());
+        Iterator it = artists.iterator();
+        while (it.hasNext()) {
+            Artist a = (Artist) it.next();
+            assertFalse("AA2".equals(a.getArtistName()));
+        }
+    }
+
+    public void testSelectFromWhereOrEqual() throws Exception {
+        createTestData("prepare");
+
+        String ejbql = "select a from Artist a where a.artistName = 'AA2' or a.artistName = 'BB1'";
+        EJBQLQuery query = new EJBQLQuery(ejbql);
+
+        List artists = createDataContext().performQuery(query);
+        assertEquals(2, artists.size());
+
+        Set names = new HashSet();
+        Iterator it = artists.iterator();
+        while (it.hasNext()) {
+            names.add(((Artist) it.next()).getArtistName());
+        }
+
+        assertTrue(names.contains("AA2"));
+        assertTrue(names.contains("BB1"));
+    }
+
+    public void testSelectFromWhereAndEqual() throws Exception {
+        createTestData("prepare");
+
+        String ejbql = "select P from Painting P where P.paintingTitle = 'P1' "
+                + "AND p.estimatedPrice = 3000";
+        EJBQLQuery query = new EJBQLQuery(ejbql);
+
+        List ps = createDataContext().performQuery(query);
+        assertEquals(1, ps.size());
+
+        Painting p = (Painting) ps.get(0);
+        assertEquals("P1", p.getPaintingTitle());
+        assertEquals(new BigDecimal(3000), p.getEstimatedPrice());
+    }
+
+    public void testSelectFromWhereGreater() throws Exception {
+        createTestData("prepare");
+
+        String ejbql = "select P from Painting P WHERE p.estimatedPrice > 3000";
+        EJBQLQuery query = new EJBQLQuery(ejbql);
+
+        List ps = createDataContext().performQuery(query);
+        assertEquals(1, ps.size());
+
+        Painting p = (Painting) ps.get(0);
+        assertEquals("P2", p.getPaintingTitle());
+        assertEquals(new BigDecimal(5000), p.getEstimatedPrice());
+    }
+
+    public void testSelectFromWhereGreaterOrEqual() throws Exception {
+        createTestData("prepare");
+
+        String ejbql = "select P from Painting P WHERE p.estimatedPrice >= 3000";
+        EJBQLQuery query = new EJBQLQuery(ejbql);
+
+        List ps = createDataContext().performQuery(query);
+        assertEquals(2, ps.size());
+    }
+
+    public void testSelectFromWhereLess() throws Exception {
+        createTestData("prepare");
+
+        String ejbql = "select P from Painting P WHERE p.estimatedPrice < 5000";
+        EJBQLQuery query = new EJBQLQuery(ejbql);
+
+        List ps = createDataContext().performQuery(query);
+        assertEquals(1, ps.size());
+
+        Painting p = (Painting) ps.get(0);
+        assertEquals("P1", p.getPaintingTitle());
+        assertEquals(new BigDecimal(3000), p.getEstimatedPrice());
+    }
+
+    public void testSelectFromWhereLessOrEqual() throws Exception {
+        createTestData("prepare");
+
+        String ejbql = "select P from Painting P WHERE p.estimatedPrice <= 5000";
+        EJBQLQuery query = new EJBQLQuery(ejbql);
+
+        List ps = createDataContext().performQuery(query);
+        assertEquals(2, ps.size());
+    }
+
+    public void testSelectFromWhereDecimalNumber() throws Exception {
+        createTestData("prepare");
+
+        String ejbql = "select P from Painting P WHERE p.estimatedPrice <= 5000.00";
+        EJBQLQuery query = new EJBQLQuery(ejbql);
+
+        List ps = createDataContext().performQuery(query);
+        assertEquals(2, ps.size());
     }
 }

Modified: cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/test/java/org/apache/cayenne/query/EJBQLTranslatorTest.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/test/java/org/apache/cayenne/query/EJBQLTranslatorTest.java?view=diff&rev=522464&r1=522463&r2=522464
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/test/java/org/apache/cayenne/query/EJBQLTranslatorTest.java (original)
+++ cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/test/java/org/apache/cayenne/query/EJBQLTranslatorTest.java Mon Mar 26 02:30:37 2007
@@ -32,17 +32,16 @@
                 getDomain().getEntityResolver());
 
         EJBQLTranslator tr = new EJBQLTranslator(select);
-        String sql = tr.translate();
-
-        // System.out.println("Expression: " + select.getExpression());
-        // System.out.println("SQL: " + sql);
+        SQLTemplate query = tr.translate();
+        String sql = query.getDefaultTemplate();
 
         // column order is unpredictable, just need to ensure that they are all there
         assertTrue(sql, sql.startsWith("SELECT "));
         assertTrue(sql, sql.indexOf("t0.ARTIST_ID") > 0);
         assertTrue(sql, sql.indexOf("t0.ARTIST_NAME") > 0);
         assertTrue(sql, sql.indexOf("t0.DATE_OF_BIRTH") > 0);
-        assertTrue(sql, sql.endsWith(" FROM ARTIST t0"));
+        assertTrue(sql, sql.endsWith(" $from0"));
+        assertEquals(" FROM ARTIST t0", query.getParameters().get("from0").toString());
     }
 
     public void testSelectDistinct() {
@@ -52,13 +51,12 @@
                 getDomain().getEntityResolver());
 
         EJBQLTranslator tr = new EJBQLTranslator(select);
-        String sql = tr.translate();
-
-        // System.out.println("Expression: " + select.getExpression());
-        // System.out.println("SQL: " + sql);
+        SQLTemplate query = tr.translate();
+        String sql = query.getDefaultTemplate();
 
         assertTrue(sql, sql.startsWith("SELECT DISTINCT "));
-        assertTrue(sql, sql.endsWith(" FROM ARTIST t0"));
+        assertTrue(sql, sql.endsWith(" $from0"));
+        assertEquals(" FROM ARTIST t0", query.getParameters().get("from0").toString());
     }
 
     public void testSelectFromWhereEqual() {
@@ -68,44 +66,261 @@
                 getDomain().getEntityResolver());
 
         EJBQLTranslator tr = new EJBQLTranslator(select);
-        String sql = tr.translate();
-
-        // System.out.println("Expression: " + select.getExpression());
-        // System.out.println("SQL: " + sql);
+        SQLTemplate query = tr.translate();
+        String sql = query.getDefaultTemplate();
 
         assertTrue(sql, sql.startsWith("SELECT "));
-        // assertTrue(sql, sql.endsWith(" FROM ARTIST t0 WHERE t0.ARTIST_NAME = ?"));
+        assertTrue(sql, sql.endsWith(" $from0 WHERE t0.ARTIST_NAME "
+                + "#bindEqual('Dali' 'VARCHAR')"));
+        assertEquals(" FROM ARTIST t0", query.getParameters().get("from0").toString());
     }
 
     public void testSelectFromWhereOrEqual() {
         EJBQLParser parser = EJBQLParserFactory.getParser();
-        EJBQLCompiledExpression select = parser
-                .compile(
-                        "select a from Artist a where a.artistName = 'Dali' or a.artistName = 'Malevich'",
-                        getDomain().getEntityResolver());
+        EJBQLCompiledExpression select = parser.compile(
+                "select a from Artist a where a.artistName = 'Dali' "
+                        + "or a.artistName = 'Malevich'",
+                getDomain().getEntityResolver());
 
         EJBQLTranslator tr = new EJBQLTranslator(select);
-        String sql = tr.translate();
-
-        // System.out.println("Expression: " + select.getExpression());
-        // System.out.println("SQL: " + sql);
+        SQLTemplate query = tr.translate();
+        String sql = query.getDefaultTemplate();
 
         EJBQLCompiledExpression select1 = parser.compile(
-                "select a from Artist a where a.artistName = 'Dali' "
+                "select a from Artist a where a.artistName = 'Picasso' "
                         + "or a.artistName = 'Malevich' "
-                        + "or a.artistName = 'Picasso'",
+                        + "or a.artistName = 'Dali'",
+                getDomain().getEntityResolver());
+
+        EJBQLTranslator tr1 = new EJBQLTranslator(select1);
+        SQLTemplate query1 = tr1.translate();
+        String sql1 = query1.getDefaultTemplate();
+
+        assertTrue(sql, sql.startsWith("SELECT "));
+        assertTrue(sql, sql.indexOf(" $from0 WHERE ") > 0);
+        assertEquals(" FROM ARTIST t0", query.getParameters().get("from0").toString());
+        assertEquals(1, countDelimiters(sql, " OR ", sql.indexOf("WHERE ")));
+
+        assertTrue(sql1, sql1.startsWith("SELECT "));
+        assertTrue(sql1, sql.indexOf(" $from0 WHERE ") > 0);
+        assertEquals(" FROM ARTIST t0", query1.getParameters().get("from0").toString());
+        assertEquals(2, countDelimiters(sql1, " OR ", sql.indexOf("WHERE ")));
+    }
+
+    public void testSelectFromWhereAndEqual() {
+        EJBQLParser parser = EJBQLParserFactory.getParser();
+        EJBQLCompiledExpression select = parser.compile(
+                "select a from Artist a where a.artistName = 'Dali' "
+                        + "and a.artistName = 'Malevich'",
                 getDomain().getEntityResolver());
 
-        EJBQLTranslator tr1 = new EJBQLTranslator(select);
-        String sql1 = tr1.translate();
+        EJBQLTranslator tr = new EJBQLTranslator(select);
+        SQLTemplate query = tr.translate();
+        String sql = query.getDefaultTemplate();
+
+        EJBQLCompiledExpression select1 = parser.compile(
+                "select a from Artist a where a.artistName = 'Picasso' "
+                        + "and a.artistName = 'Malevich' "
+                        + "and a.artistName = 'Dali'",
+                getDomain().getEntityResolver());
 
-        System.out.println("Expression: " + select1.getExpression());
-        System.out.println("SQL: " + sql1);
+        EJBQLTranslator tr1 = new EJBQLTranslator(select1);
+        SQLTemplate query1 = tr1.translate();
+        String sql1 = query1.getDefaultTemplate();
 
         assertTrue(sql, sql.startsWith("SELECT "));
-        assertTrue(sql, sql.indexOf(" FROM ARTIST t0 WHERE ") > 0);
+        assertTrue(sql, sql.indexOf(" $from0 WHERE ") > 0);
+        assertEquals(1, countDelimiters(sql, " AND ", sql.indexOf("WHERE ")));
 
         assertTrue(sql1, sql1.startsWith("SELECT "));
-        assertTrue(sql1, sql1.indexOf(" FROM ARTIST t0 WHERE ") > 0);
+        assertTrue(sql1, sql1.indexOf(" $from0 WHERE ") > 0);
+        assertEquals(2, countDelimiters(sql1, " AND ", sql1.indexOf("WHERE ")));
+    }
+
+    public void testSelectFromWhereNot() {
+        EJBQLParser parser = EJBQLParserFactory.getParser();
+        EJBQLCompiledExpression select = parser.compile(
+                "select a from Artist a where not (a.artistName = 'Dali')",
+                getDomain().getEntityResolver());
+
+        EJBQLTranslator tr = new EJBQLTranslator(select);
+        SQLTemplate query = tr.translate();
+        String sql = query.getDefaultTemplate();
+
+        assertTrue(sql, sql.startsWith("SELECT "));
+        assertTrue(sql, sql.endsWith(" $from0 WHERE NOT "
+                + "t0.ARTIST_NAME #bindEqual('Dali' 'VARCHAR')"));
+    }
+
+    public void testSelectFromWhereGreater() {
+        EJBQLParser parser = EJBQLParserFactory.getParser();
+        EJBQLCompiledExpression select = parser.compile(
+                "select p from Painting p where p.estimatedPrice > 1.0",
+                getDomain().getEntityResolver());
+
+        EJBQLTranslator tr = new EJBQLTranslator(select);
+        SQLTemplate query = tr.translate();
+        String sql = query.getDefaultTemplate();
+
+        assertTrue(sql, sql.startsWith("SELECT "));
+        assertTrue(sql, sql.endsWith(" $from0 "
+                + "WHERE t0.ESTIMATED_PRICE > #bind($id1 'DECIMAL')"));
+    }
+
+    public void testSelectFromWhereGreaterOrEqual() {
+        EJBQLParser parser = EJBQLParserFactory.getParser();
+        EJBQLCompiledExpression select = parser.compile(
+                "select p from Painting p where p.estimatedPrice >= 2",
+                getDomain().getEntityResolver());
+
+        EJBQLTranslator tr = new EJBQLTranslator(select);
+        SQLTemplate query = tr.translate();
+        String sql = query.getDefaultTemplate();
+
+        assertTrue(sql, sql.startsWith("SELECT "));
+        assertTrue(sql, sql.endsWith(" $from0 "
+                + "WHERE t0.ESTIMATED_PRICE >= #bind($id1 'INTEGER')"));
+    }
+
+    public void testSelectFromWhereLess() {
+        EJBQLParser parser = EJBQLParserFactory.getParser();
+        EJBQLCompiledExpression select = parser.compile(
+                "select p from Painting p where p.estimatedPrice < 1.0",
+                getDomain().getEntityResolver());
+
+        EJBQLTranslator tr = new EJBQLTranslator(select);
+        SQLTemplate query = tr.translate();
+        String sql = query.getDefaultTemplate();
+
+        assertTrue(sql, sql.startsWith("SELECT "));
+        assertTrue(sql, sql.endsWith(" $from0 "
+                + "WHERE t0.ESTIMATED_PRICE < #bind($id1 'DECIMAL')"));
+    }
+
+    public void testSelectFromWhereLessOrEqual() {
+        EJBQLParser parser = EJBQLParserFactory.getParser();
+        EJBQLCompiledExpression select = parser.compile(
+                "select p from Painting p where p.estimatedPrice <= 1.0",
+                getDomain().getEntityResolver());
+
+        EJBQLTranslator tr = new EJBQLTranslator(select);
+        SQLTemplate query = tr.translate();
+        String sql = query.getDefaultTemplate();
+
+        assertTrue(sql, sql.startsWith("SELECT "));
+        assertTrue(sql, sql.endsWith(" $from0 "
+                + "WHERE t0.ESTIMATED_PRICE <= #bind($id1 'DECIMAL')"));
+    }
+
+    public void testSelectFromWhereNotEqual() {
+        EJBQLParser parser = EJBQLParserFactory.getParser();
+        EJBQLCompiledExpression select = parser.compile(
+                "select a from Artist a where a.artistName <> 'Dali'",
+                getDomain().getEntityResolver());
+
+        EJBQLTranslator tr = new EJBQLTranslator(select);
+        SQLTemplate query = tr.translate();
+        String sql = query.getDefaultTemplate();
+
+        assertTrue(sql, sql.startsWith("SELECT "));
+        assertTrue(sql, sql.endsWith(" $from0 "
+                + "WHERE t0.ARTIST_NAME #bindNotEqual('Dali' 'VARCHAR')"));
+    }
+
+    public void testSelectFromWhereBetween() {
+        EJBQLParser parser = EJBQLParserFactory.getParser();
+        EJBQLCompiledExpression select = parser.compile(
+                "select p from Painting p where p.estimatedPrice between 3 and 5",
+                getDomain().getEntityResolver());
+
+        EJBQLTranslator tr = new EJBQLTranslator(select);
+        SQLTemplate query = tr.translate();
+        String sql = query.getDefaultTemplate();
+
+        assertTrue(sql, sql.startsWith("SELECT "));
+        assertTrue(sql, sql.endsWith(" $from0 "
+                + "WHERE t0.ESTIMATED_PRICE "
+                + "BETWEEN #bind($id1 'INTEGER') AND #bind($id2 'INTEGER')"));
+    }
+
+    public void testSelectFromWhereNotBetween() {
+        EJBQLParser parser = EJBQLParserFactory.getParser();
+        EJBQLCompiledExpression select = parser.compile(
+                "select p from Painting p where p.estimatedPrice not between 3 and 5",
+                getDomain().getEntityResolver());
+
+        EJBQLTranslator tr = new EJBQLTranslator(select);
+        SQLTemplate query = tr.translate();
+        String sql = query.getDefaultTemplate();
+
+        assertTrue(sql, sql.startsWith("SELECT "));
+        assertTrue(sql, sql.endsWith(" $from0 "
+                + "WHERE t0.ESTIMATED_PRICE "
+                + "NOT BETWEEN #bind($id1 'INTEGER') AND #bind($id2 'INTEGER')"));
+    }
+
+    public void testSelectFromWhereLike() {
+        EJBQLParser parser = EJBQLParserFactory.getParser();
+        EJBQLCompiledExpression select = parser.compile(
+                "select p from Painting p where p.paintingTitle like 'Stuff'",
+                getDomain().getEntityResolver());
+
+        System.out.println(select.getExpression().toString());
+        EJBQLTranslator tr = new EJBQLTranslator(select);
+        SQLTemplate query = tr.translate();
+        String sql = query.getDefaultTemplate();
+
+        assertTrue(sql, sql.startsWith("SELECT "));
+        assertTrue(sql, sql.endsWith(" $from0 "
+                + "WHERE t0.PAINTING_TITLE "
+                + "LIKE #bind('Stuff' 'VARCHAR')"));
+    }
+
+    public void testSelectFromWhereNotLike() {
+        EJBQLParser parser = EJBQLParserFactory.getParser();
+        EJBQLCompiledExpression select = parser.compile(
+                "select p from Painting p where p.paintingTitle NOT like 'Stuff'",
+                getDomain().getEntityResolver());
+
+        EJBQLTranslator tr = new EJBQLTranslator(select);
+        SQLTemplate query = tr.translate();
+        String sql = query.getDefaultTemplate();
+
+        assertTrue(sql, sql.startsWith("SELECT "));
+        assertTrue(sql, sql.endsWith(" $from0 "
+                + "WHERE t0.PAINTING_TITLE "
+                + "NOT LIKE #bind('Stuff' 'VARCHAR')"));
+    }
+
+    public void testSelectFromWhereRelationshipPropertyPath() {
+        EJBQLParser parser = EJBQLParserFactory.getParser();
+        EJBQLCompiledExpression select = parser.compile(
+                "select p from Painting p where p.toArtist.artistName = 'AA2'",
+                getDomain().getEntityResolver());
+
+        System.out.println("Expression: " + select.getExpression());
+
+        EJBQLTranslator tr = new EJBQLTranslator(select);
+        SQLTemplate query = tr.translate();
+        String sql = query.getDefaultTemplate();
+
+        System.out.println("SQL: " + sql);
+
+        assertTrue(sql, sql.startsWith("SELECT "));
+        assertTrue(sql, sql.endsWith(" $from0 "
+                + "WHERE t1.ARTIST_NAME #bindEqual('AA2' 'VARCHAR')"));
+        // TODO: andrus, 3/25/2007 - implement joins support
+        // assertEquals(" FROM PAINTING t0 JOIN ARTIST t1 ON (t0.ARTIST_ID =
+        // t1.ARTIST_ID)", query.getParameters().get("from0"));
+    }
+
+    private int countDelimiters(String string, String delim, int fromIndex) {
+        int i = 0;
+        while ((fromIndex = string.indexOf(delim, fromIndex)) >= 0) {
+            fromIndex += delim.length();
+            i++;
+        }
+
+        return i;
     }
 }

Modified: cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/test/resources/dml/access.DataContextEJBQLQueryTest.xml
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/test/resources/dml/access.DataContextEJBQLQueryTest.xml?view=diff&rev=522464&r1=522463&r2=522464
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/test/resources/dml/access.DataContextEJBQLQueryTest.xml (original)
+++ cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/test/resources/dml/access.DataContextEJBQLQueryTest.xml Mon Mar 26 02:30:37 2007
@@ -35,6 +35,19 @@
 		</value></constructor-arg>
 	</bean>
 	
+	<bean id="P11" class="org.apache.cayenne.unit.util.UpdatingSQLTemplate">
+		<constructor-arg type="java.lang.Class"><value>org.apache.art.Painting</value></constructor-arg>
+		<constructor-arg><value>
+		INSERT INTO PAINTING (PAINTING_ID, PAINTING_TITLE, ARTIST_ID, ESTIMATED_PRICE) VALUES (33001, 'P1', 33001, 3000)
+		</value></constructor-arg>
+	</bean>
+	
+	<bean id="P12" class="org.apache.cayenne.unit.util.UpdatingSQLTemplate">
+		<constructor-arg type="java.lang.Class"><value>org.apache.art.Painting</value></constructor-arg>
+		<constructor-arg><value>
+		INSERT INTO PAINTING (PAINTING_ID, PAINTING_TITLE, ARTIST_ID, ESTIMATED_PRICE) VALUES (33002, 'P2', 33002, 5000)
+		</value></constructor-arg>
+	</bean>
 	
 
 	<!-- ======================================= -->
@@ -48,6 +61,8 @@
 				<ref bean="A2"/>
 				<ref bean="A3"/>
 				<ref bean="A4"/>
+				<ref bean="P11"/>
+				<ref bean="P12"/>
 			</list>
 		</constructor-arg>
 	</bean>