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/06/11 21:47:50 UTC

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

Author: aadamchik
Date: Mon Jun 11 12:47:49 2007
New Revision: 546236

URL: http://svn.apache.org/viewvc?view=rev&rev=546236
Log:
EJBQL joins processing

Modified:
    cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/main/java/org/apache/cayenne/access/jdbc/EJBQLFromTranslator.java
    cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/main/java/org/apache/cayenne/access/jdbc/EJBQLPathTranslator.java
    cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/main/java/org/apache/cayenne/access/jdbc/EJBQLSelectTranslator.java
    cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/main/java/org/apache/cayenne/access/jdbc/EJBQLTranslationContext.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/EJBQLExpressionVisitor.java
    cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/main/java/org/apache/cayenne/ejbql/parser/EJBQLFrom.java
    cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/main/java/org/apache/cayenne/ejbql/parser/SimpleNode.java

Modified: cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/main/java/org/apache/cayenne/access/jdbc/EJBQLFromTranslator.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/main/java/org/apache/cayenne/access/jdbc/EJBQLFromTranslator.java?view=diff&rev=546236&r1=546235&r2=546236
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/main/java/org/apache/cayenne/access/jdbc/EJBQLFromTranslator.java (original)
+++ cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/main/java/org/apache/cayenne/access/jdbc/EJBQLFromTranslator.java Mon Jun 11 12:47:49 2007
@@ -30,10 +30,10 @@
 import org.apache.cayenne.reflect.ClassDescriptor;
 
 public class EJBQLFromTranslator extends EJBQLBaseVisitor {
-    
-   
+
     private EJBQLTranslationContext context;
     private String lastTableAlias;
+    private String lastId;
 
     public EJBQLFromTranslator(EJBQLTranslationContext context) {
         super(true);
@@ -46,8 +46,10 @@
                 context.append(',');
             }
 
-            lastTableAlias = appendTable(expression.getId());
+            lastId = expression.getId();
+            lastTableAlias = appendTable(lastId);
         }
+
         return true;
     }
 
@@ -58,7 +60,7 @@
 
     public boolean visitInnerJoin(EJBQLJoin join, int finishedChildIndex) {
         if (finishedChildIndex < 0) {
-            appendJoin(join, "INNER JOIN");
+            appendJoin(join, "INNER JOIN", true);
         }
         return true;
     }
@@ -70,12 +72,16 @@
 
     public boolean visitOuterJoin(EJBQLJoin join, int finishedChildIndex) {
         if (finishedChildIndex < 0) {
-            appendJoin(join, "LEFT OUTER JOIN");
+            appendJoin(join, "LEFT OUTER JOIN", false);
         }
         return true;
     }
 
-    private void appendJoin(EJBQLJoin join, String semantics) {
+    void setLastTableAlias(String alias) {
+        this.lastTableAlias = alias;
+    }
+
+    private void appendJoin(EJBQLJoin join, String semantics, boolean reusable) {
 
         String id = join.getId();
 
@@ -126,7 +132,13 @@
         }
 
         context.append(")");
+
+        if (reusable) {
+            context.registerReusableJoin(lastId, incoming.getName(), id);
+        }
+
         this.lastTableAlias = targetAlias;
+        this.lastId = id;
     }
 
     private String appendTable(String id) {

Modified: cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/main/java/org/apache/cayenne/access/jdbc/EJBQLPathTranslator.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/main/java/org/apache/cayenne/access/jdbc/EJBQLPathTranslator.java?view=diff&rev=546236&r1=546235&r2=546236
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/main/java/org/apache/cayenne/access/jdbc/EJBQLPathTranslator.java (original)
+++ cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/main/java/org/apache/cayenne/access/jdbc/EJBQLPathTranslator.java Mon Jun 11 12:47:49 2007
@@ -21,6 +21,9 @@
 import org.apache.cayenne.ejbql.EJBQLBaseVisitor;
 import org.apache.cayenne.ejbql.EJBQLException;
 import org.apache.cayenne.ejbql.EJBQLExpression;
+import org.apache.cayenne.ejbql.parser.EJBQLIdentificationVariable;
+import org.apache.cayenne.ejbql.parser.EJBQLIdentifier;
+import org.apache.cayenne.ejbql.parser.EJBQLInnerJoin;
 import org.apache.cayenne.ejbql.parser.EJBQLPath;
 import org.apache.cayenne.map.DbEntity;
 import org.apache.cayenne.map.ObjAttribute;
@@ -32,8 +35,11 @@
 
     private EJBQLTranslationContext context;
     private ObjEntity currentEntity;
+    private ObjRelationship currentIncoming;
     private String lastPathComponent;
     private String idPath;
+    private String unresolvedPath;
+    private EJBQLFromTranslator joinAppender;
 
     EJBQLPathTranslator(EJBQLTranslationContext context) {
         super(true);
@@ -51,6 +57,7 @@
                 processLastPath();
             }
         }
+
         return true;
     }
 
@@ -64,18 +71,69 @@
 
         this.currentEntity = descriptor.getEntity();
         this.idPath = expression.getText();
+        this.unresolvedPath = idPath;
         return true;
     }
 
     public boolean visitIdentificationVariable(EJBQLExpression expression) {
-        if (this.lastPathComponent != null) {
-            this.idPath += '.' + lastPathComponent;
+
+        // TODO: andrus 6/11/2007 - if the path ends with relationship, the last join will
+        // get lost...
+        if (lastPathComponent != null) {
+            resolveJoin();
         }
 
         this.lastPathComponent = expression.getText();
         return true;
     }
 
+    private void resolveJoin() {
+
+        String newPath = idPath + '.' + lastPathComponent;
+        String oldPath = context.registerReusableJoin(idPath, lastPathComponent, newPath);
+        
+        this.unresolvedPath = unresolvedPath + '.' + lastPathComponent;
+
+        if (oldPath != null) {
+            this.idPath = oldPath;
+        }
+        else {
+            // register join
+            EJBQLIdentifier id = new EJBQLIdentifier(-1);
+            id.setText(idPath);
+
+            EJBQLIdentificationVariable idVar = new EJBQLIdentificationVariable(-1);
+            idVar.setText(lastPathComponent);
+
+            EJBQLPath path = new EJBQLPath(-1);
+            path.jjtAddChild(id, 0);
+            path.jjtAddChild(idVar, 1);
+
+            EJBQLIdentifier joinId = new EJBQLIdentifier(-1);
+            joinId.setText(unresolvedPath);
+
+            EJBQLInnerJoin join = new EJBQLInnerJoin(-1);
+            join.jjtAddChild(path, 0);
+            join.jjtAddChild(joinId, 1);
+
+            if (joinAppender == null) {
+                joinAppender = new EJBQLFromTranslator(context);
+            }
+
+            ObjEntity sourceEntity = (ObjEntity) currentIncoming.getSourceEntity();
+
+            context.switchToMarker(EJBQLTranslationContext.FROM_TAIL_MARKER);
+            joinAppender.setLastTableAlias(context.getAlias(idPath, sourceEntity
+                    .getDbEntityName()));
+            joinAppender.visitInnerJoin(join, -1);
+            context.switchToMainBuffer();
+
+            this.idPath = newPath;
+        }
+
+        
+    }
+
     private void processIntermediatePath() {
         ObjRelationship relationship = (ObjRelationship) currentEntity
                 .getRelationship(lastPathComponent);
@@ -87,6 +145,7 @@
                     + "'");
         }
 
+        this.currentIncoming = relationship;
         this.currentEntity = (ObjEntity) relationship.getTargetEntity();
     }
 

Modified: cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/main/java/org/apache/cayenne/access/jdbc/EJBQLSelectTranslator.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/main/java/org/apache/cayenne/access/jdbc/EJBQLSelectTranslator.java?view=diff&rev=546236&r1=546235&r2=546236
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/main/java/org/apache/cayenne/access/jdbc/EJBQLSelectTranslator.java (original)
+++ cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/main/java/org/apache/cayenne/access/jdbc/EJBQLSelectTranslator.java Mon Jun 11 12:47:49 2007
@@ -44,9 +44,15 @@
         return true;
     }
 
-    public boolean visitFrom(EJBQLExpression expression) {
-        context.append(" FROM");
-        setDelegate(new EJBQLFromTranslator(context));
+    public boolean visitFrom(EJBQLExpression expression, int finishedChildIndex) {
+        if (finishedChildIndex < 0) {
+            context.append(" FROM");
+            setDelegate(new EJBQLFromTranslator(context));
+        }
+        else if (finishedChildIndex + 1 == expression.getChildrenCount()) {
+            context.markCurrentPosition(EJBQLTranslationContext.FROM_TAIL_MARKER);
+        }
+        
         return true;
     }
 

Modified: cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/main/java/org/apache/cayenne/access/jdbc/EJBQLTranslationContext.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/main/java/org/apache/cayenne/access/jdbc/EJBQLTranslationContext.java?view=diff&rev=546236&r1=546235&r2=546236
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/main/java/org/apache/cayenne/access/jdbc/EJBQLTranslationContext.java (original)
+++ cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/main/java/org/apache/cayenne/access/jdbc/EJBQLTranslationContext.java Mon Jun 11 12:47:49 2007
@@ -32,19 +32,24 @@
  */
 class EJBQLTranslationContext {
 
+    static final String FROM_TAIL_MARKER = "FROM_TAIL_MARKER";
+
     private Map aliases;
     private Map bindingVariables;
-    private StringBuffer buffer;
+    private StringBuffer mainBuffer;
+    private StringBuffer currentBuffer;
     private EJBQLCompiledExpression compiledExpression;
     private Map attributes;
+    private Map reusableJoins;
 
     EJBQLTranslationContext(EJBQLCompiledExpression compiledExpression) {
         this.compiledExpression = compiledExpression;
-        this.buffer = new StringBuffer();
+        this.mainBuffer = new StringBuffer();
+        this.currentBuffer = mainBuffer;
     }
 
     SQLTemplate getQuery() {
-        String sql = buffer.length() > 0 ? buffer.toString() : null;
+        String sql = mainBuffer.length() > 0 ? mainBuffer.toString() : null;
         SQLTemplate query = new SQLTemplate(compiledExpression
                 .getRootDescriptor()
                 .getObjectClass(), sql);
@@ -53,6 +58,46 @@
     }
 
     /**
+     * Inserts a marker in the SQL, mapped to a StringBuffer that can be later filled with
+     * content.
+     */
+    void markCurrentPosition(String marker) {
+        // make sure we mark the main buffer
+        StringBuffer current = this.currentBuffer;
+
+        try {
+            switchToMainBuffer();
+            String internalMarker = bindParameter(new StringBuffer(), "marker");
+            append("${").append(internalMarker).append("}");
+
+            // register mapping of internal to external marker
+            setAttribute(marker, internalMarker);
+        }
+        finally {
+            this.currentBuffer = current;
+        }
+    }
+
+    void switchToMarker(String marker) {
+        String internalMarker = (String) getAttribute(marker);
+        if (internalMarker == null) {
+            throw new IllegalArgumentException("Invalid marker: " + marker);
+        }
+
+        Object object = bindingVariables.get(internalMarker);
+        if (!(object instanceof StringBuffer)) {
+            throw new IllegalArgumentException("Invalid or missing buffer for marker: "
+                    + marker);
+        }
+
+        this.currentBuffer = (StringBuffer) object;
+    }
+
+    void switchToMainBuffer() {
+        this.currentBuffer = this.mainBuffer;
+    }
+
+    /**
      * Returns a context "attribute" stored for the given name. Attributes is a state
      * preservation mechanism used by translators and have the same scope as the context.
      */
@@ -76,7 +121,7 @@
      * Appends a piece of SQL to the internal buffer.
      */
     EJBQLTranslationContext append(String chunk) {
-        buffer.append(chunk);
+        currentBuffer.append(chunk);
         return this;
     }
 
@@ -84,7 +129,7 @@
      * Appends a piece of SQL to the internal buffer.
      */
     EJBQLTranslationContext append(char chunk) {
-        buffer.append(chunk);
+        currentBuffer.append(chunk);
         return this;
     }
 
@@ -111,6 +156,28 @@
         String var = prefix + bindingVariables.size();
         bindingVariables.put(var, value);
         return var;
+    }
+
+    /**
+     * Registers a "reusable" join, returning a preexisting ID if the join is already
+     * registered. Reusable normally means an inner join that can be duplicated implicitly
+     * in the path expressions.
+     */
+    String registerReusableJoin(String sourceIdPath, String relationship, String targetId) {
+        if (reusableJoins == null) {
+            reusableJoins = new HashMap();
+        }
+
+        String key = sourceIdPath + ":" + relationship;
+
+        String oldId = (String) reusableJoins.put(key, targetId);
+        if (oldId != null) {
+            // revert back to old id
+            reusableJoins.put(key, oldId);
+            return oldId;
+        }
+
+        return 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=546236&r1=546235&r2=546236
==============================================================================
--- 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 Jun 11 12:47:49 2007
@@ -157,7 +157,7 @@
         return continueFlag;
     }
 
-    public boolean visitFrom(EJBQLExpression expression) {
+    public boolean visitFrom(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=546236&r1=546235&r2=546236
==============================================================================
--- 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 Jun 11 12:47:49 2007
@@ -190,8 +190,10 @@
         return delegate != null ? delegate.visitExists(expression) : continueFlag;
     }
 
-    public boolean visitFrom(EJBQLExpression expression) {
-        return delegate != null ? delegate.visitFrom(expression) : continueFlag;
+    public boolean visitFrom(EJBQLExpression expression, int finishedChildIndex) {
+        return delegate != null
+                ? delegate.visitFrom(expression, finishedChildIndex)
+                : continueFlag;
     }
 
     public boolean visitFromItem(EJBQLFromItem expression, int finishedChildIndex) {
@@ -235,9 +237,9 @@
     }
 
     public boolean visitInnerFetchJoin(EJBQLJoin join, int finishedChildIndex) {
-        return delegate != null ? delegate.visitInnerFetchJoin(
-                join,
-                finishedChildIndex) : continueFlag;
+        return delegate != null
+                ? delegate.visitInnerFetchJoin(join, finishedChildIndex)
+                : continueFlag;
     }
 
     public boolean visitInnerJoin(EJBQLJoin join, int finishedChildIndex) {
@@ -345,9 +347,9 @@
     }
 
     public boolean visitOuterFetchJoin(EJBQLJoin join, int finishedChildIndex) {
-        return delegate != null ? delegate.visitOuterFetchJoin(
-                join,
-                finishedChildIndex) : continueFlag;
+        return delegate != null
+                ? delegate.visitOuterFetchJoin(join, finishedChildIndex)
+                : continueFlag;
     }
 
     public boolean visitOuterJoin(EJBQLJoin join, int finishedChildIndex) {

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=546236&r1=546235&r2=546236
==============================================================================
--- 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 Jun 11 12:47:49 2007
@@ -140,7 +140,7 @@
 
     boolean visitExists(EJBQLExpression expression);
 
-    boolean visitFrom(EJBQLExpression expression);
+    boolean visitFrom(EJBQLExpression expression, int finishedChildIndex);
 
     boolean visitFromItem(EJBQLFromItem expression, int finishedChildIndex);
 

Modified: cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/main/java/org/apache/cayenne/ejbql/parser/EJBQLFrom.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/main/java/org/apache/cayenne/ejbql/parser/EJBQLFrom.java?view=diff&rev=546236&r1=546235&r2=546236
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/main/java/org/apache/cayenne/ejbql/parser/EJBQLFrom.java (original)
+++ cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/main/java/org/apache/cayenne/ejbql/parser/EJBQLFrom.java Mon Jun 11 12:47:49 2007
@@ -31,6 +31,11 @@
     }
 
     protected boolean visitNode(EJBQLExpressionVisitor visitor) {
-        return visitor.visitFrom(this);
+        return visitor.visitFrom(this, -1);
+    }
+
+    protected boolean visitChild(EJBQLExpressionVisitor visitor, int childIndex) {
+        return super.visitChild(visitor, childIndex)
+                && visitor.visitFrom(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=546236&r1=546235&r2=546236
==============================================================================
--- 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 Jun 11 12:47:49 2007
@@ -136,7 +136,7 @@
         return (children == null) ? 0 : children.length;
     }
 
-    void setText(String text) {
+    public void setText(String text) {
         this.text = text;
     }