You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@openjpa.apache.org by fa...@apache.org on 2009/07/20 19:49:37 UTC

svn commit: r795934 [2/2] - in /openjpa/trunk: openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/ openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/ openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/ openjpa-jdbc/src/main/jav...

Modified: openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/jpql/JPQLExpressionBuilder.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/jpql/JPQLExpressionBuilder.java?rev=795934&r1=795933&r2=795934&view=diff
==============================================================================
--- openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/jpql/JPQLExpressionBuilder.java (original)
+++ openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/jpql/JPQLExpressionBuilder.java Mon Jul 20 17:49:36 2009
@@ -46,6 +46,7 @@
 import org.apache.openjpa.kernel.exps.Resolver;
 import org.apache.openjpa.kernel.exps.Subquery;
 import org.apache.openjpa.kernel.exps.Value;
+import org.apache.openjpa.kernel.exps.Context;
 import org.apache.openjpa.lib.util.Localizer;
 import org.apache.openjpa.lib.util.Localizer.Message;
 import org.apache.openjpa.lib.log.Log;
@@ -99,7 +100,7 @@
             ? (ParsedJPQL) parsedQuery
             : parsedQuery instanceof String
             ? getParsedQuery((String) parsedQuery)
-            : null, null));
+            : null, null, null));
 
         if (ctx().parsed == null)
             throw new InternalException(parsedQuery + "");
@@ -271,11 +272,15 @@
 
     QueryExpressions getQueryExpressions() {
         QueryExpressions exps = new QueryExpressions();
+        exps.setContexts(contexts);
 
         evalQueryOperation(exps);
 
         Expression filter = null;
-        filter = and(evalFromClause(root().id == JJTSELECT), filter);
+        Expression from = ctx().from;
+        if (from == null)
+            from = evalFromClause(root().id == JJTSELECT);
+        filter = and(from, filter);
         filter = and(evalWhereClause(), filter);
         filter = and(evalSelectClause(exps), filter);
 
@@ -413,7 +418,7 @@
                 exps.ascending[i] = node.getChildCount() <= 1 ||
                     lastChild(node).id == JJTASCENDING ? true : false;
             }
-            // check if order by selec item alias
+            // check if order by select item result alias
             for (int i = 0; i < ordercount; i++) {
                 if (exps.orderingClauses[i] != null && 
                     !exps.orderingClauses[i].equals(""))
@@ -543,12 +548,15 @@
     }
 
     private Expression evalFromClause(boolean needsAlias) {
-        Expression exp = null;
-
         // build up the alias map in the FROM clause
         JPQLNode from = root().findChildByID(JJTFROM, false);
         if (from == null)
             throw parseException(EX_USER, "no-from-clause", null, null);
+        return evalFromClause(from, needsAlias);
+    }
+
+    private Expression evalFromClause(JPQLNode from, boolean needsAlias) {
+        Expression exp = null;
 
         for (int i = 0; i < from.children.length; i++) {
             JPQLNode node = from.children[i];
@@ -573,7 +581,7 @@
 
     private Expression bindVariableForKeyPath(Path path, String alias,
         Expression exp) {
-        if (alias != null && !isSeendVariable(alias)) {
+        if (alias != null && ctx().findVariable(alias) == null) {
             // subquery may have KEY range over a variable 
             // that is not defined.
             JPQLNode key = root().findChildByID(JJTKEY, true);
@@ -584,6 +592,29 @@
         }
         return exp;
     }
+
+    private Expression getSubquery(String alias, Path path, Expression exp) {
+        FieldMetaData fmd = path.last();
+        ClassMetaData candidate = getFieldType(fmd);
+        if (candidate == null && fmd.isElementCollection())
+            candidate = fmd.getDefiningMetaData();
+
+        setCandidate(candidate, alias);
+
+        Context subContext = ctx();
+        Subquery subquery = ctx().getSubquery();
+        if (subquery == null){
+            subquery = factory.newSubquery(candidate, true, alias);
+            subContext.setSubquery(subquery);
+        }
+        Path subpath = factory.newPath(subquery);
+        subpath.setMetaData(candidate);
+        subquery.setMetaData(candidate);
+        exp = bindVariableForKeyPath(path, alias, exp);
+        exp =  and(exp, factory.equal(path, subpath));
+        return exp;
+    }
+
     /**
      * Adds a join condition to the given expression.
      *
@@ -604,14 +635,8 @@
         JPQLNode alias = node.getChildCount() >= 2 ? right(node) : null;
         // OPENJPA-15 support subquery's from clause do not start with 
         // identification_variable_declaration()
-        if (inner && ctx().subquery != null && ctx().schemaAlias == null) {
-            setCandidate(getFieldType(path.last()), alias.text);
-
-            Path subpath = factory.newPath(ctx().subquery);
-            subpath.setMetaData(ctx().subquery.getMetaData());
-            exp = bindVariableForKeyPath(path, alias.text, exp);
-            exp =  and(exp, factory.equal(path, subpath));
-            return exp;
+        if (inner && ctx().getParent() != null && ctx().schemaAlias == null) {
+            return getSubquery(alias.text, path, exp);
         }
 
         return addJoin(path, alias, exp);
@@ -666,23 +691,13 @@
         } else {
             alias = right(node).text;
             JPQLNode left = left(node);
+            addSchemaToContext(alias, cmd);
 
             // check to see if the we are referring to a path in the from
             // clause, since we might be in a subquery against a collection
             if (isPath(left)) {
                 Path path = getPath(left);
-                FieldMetaData fmd = path.last();
-                ClassMetaData candidate = getFieldType(fmd);
-
-                if (candidate == null && fmd.isElementCollection())
-                    candidate = fmd.getDefiningMetaData();
-
-                setCandidate(candidate, alias);
-
-                Path subpath = factory.newPath(ctx().subquery);
-                subpath.setMetaData(ctx().subquery.getMetaData());
-                exp = bindVariableForKeyPath(path, alias, exp);
-                return and(exp, factory.equal(path, subpath));
+                return getSubquery(alias, path, exp);
             } else {
                 // we have an alias: bind it as a variable
                 Value var = getVariable(alias, true);
@@ -764,8 +779,15 @@
         return super.getVariable(id.toLowerCase(), bind);
     }
 
-    protected boolean isSeendVariable(String id) {
-        return id != null && super.isSeenVariable(id.toLowerCase());
+    protected Value getDefinedVariable(String id) {
+        return ctx().getVariable(id);
+    }
+
+    protected boolean isSeenVariable(String var) {
+        Context c = ctx().findContext(var);
+        if (c != null)
+            return true;
+        return false;
     }
 
     /**
@@ -1181,7 +1203,11 @@
                 return eval(onlyChild(node));
 
             case JJTCOUNT:
-                return factory.count(getValue(lastChild(node)));
+                JPQLNode c = lastChild(node);
+                if (c.id == JJTIDENTIFIER)
+                    // count(e)
+                    return factory.count(getPath(node, false, true));
+                return factory.count(getValue(c));
 
             case JJTMAX:
                 return factory.max(getNumberValue(onlyChild(node)));
@@ -1392,12 +1418,22 @@
 
         // parse the subquery
         ParsedJPQL parsed = new ParsedJPQL(node.parser.jpql, node);
+        Context parent = ctx();
+        Context subContext = new Context(parsed, null, ctx());
+        contexts.push(subContext);
+        subContext.setParent(parent);
 
         ClassMetaData candidate = getCandidateMetaData(node);
-        Subquery subq = factory.newSubquery(candidate, subclasses, alias);
+        Subquery subq = subContext.getSubquery();
+        if (subq == null) {
+            subq = factory.newSubquery(candidate, subclasses, alias);
+            subContext.setSubquery(subq);
+        }
         subq.setMetaData(candidate);
-
-        contexts.push(new Context(parsed, subq));
+        
+        // evaluate from clause for resolving variables defined in subquery
+        JPQLNode from = node.getChild(1);
+        subContext.from = evalFromClause(from, true);
 
         try {
             QueryExpressions subexp = getQueryExpressions();
@@ -1506,7 +1542,14 @@
         if (cmd != null) {
             // handle the case where the class name is the alias
             // for the candidate (we don't use variables for this)
-            Value thiz = factory.getThis();
+            Value thiz = null;
+            if (ctx().subquery == null || 
+                ctx().getSchema(name.toLowerCase()) == null) {
+                thiz = factory.getThis();
+            } else {
+                thiz = factory.newPath(ctx().subquery);
+            }
+            ((Path)thiz).setSchemaAlias(name);
             thiz.setMetaData(cmd);
             return thiz;
         } else if (val instanceof Path) {
@@ -1734,6 +1777,7 @@
             if (ctx().subquery != null) {
                 path = factory.newPath(ctx().subquery);
                 path.setMetaData(ctx().subquery.getMetaData());
+                factory.bindVariable(val, path);
             } else {
                 path = factory.newPath();
                 path.setMetaData(ctx().meta);
@@ -1748,6 +1792,8 @@
             throw parseException(EX_USER, "path-invalid",
                 new Object[]{ assemble(node), name }, null);
 
+        path.setSchemaAlias(name);
+
         // walk through the children and assemble the path
         boolean allowNull = !inner;
         for (int i = 1; i < node.children.length; i++) {
@@ -1758,7 +1804,10 @@
             }
             path = (Path) traversePath(path, node.children[i].text, pcOnly,
                 allowNull);
-
+            if (ctx().getParent() != null && ctx().getVariable(path.getSchemaAlias()) == null) {
+                path.setSubqueryContext(ctx());
+            }
+        
             // all traversals but the first one will always be inner joins
             allowNull = false;
         }
@@ -1902,17 +1951,23 @@
         return null;
     }
 
-    private class Context {
+    protected void addSchemaToContext(String id, ClassMetaData meta) {
+        ctx().addSchema(id.toLowerCase(), meta);    
+    }
 
-        private final ParsedJPQL parsed;
-        private ClassMetaData meta;
-        private String schemaAlias;
-        private Subquery subquery;
-
-        Context(ParsedJPQL parsed, Subquery subquery) {
-            this.parsed = parsed;
-            this.subquery = subquery;
-        }
+    protected void addVariableToContext(String id, Value var) {
+        ctx().addVariable(id, var);
+    }
+
+    protected Value getSeenVariable(String var) {
+        Context c = ctx();
+        Value v = c.getVariable(var);
+        if (v != null)
+            return v;
+        if (c.getParent() != null)
+            return c.getParent().findVariable(var);
+
+        return null;
     }
 
     ////////////////////////////

Modified: openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/query/TestSubquery.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/query/TestSubquery.java?rev=795934&r1=795933&r2=795934&view=diff
==============================================================================
--- openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/query/TestSubquery.java (original)
+++ openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/query/TestSubquery.java Mon Jul 20 17:49:36 2009
@@ -24,6 +24,7 @@
 import javax.persistence.EntityManager;
 import javax.persistence.Query;
 
+import org.apache.openjpa.persistence.query.Customer.CreditRating;
 import org.apache.openjpa.persistence.test.SingleEMFTestCase;
 
 /**
@@ -68,7 +69,7 @@
             " (select o from c.orders o where o.oid = 1) or exists" +
             " (select o from c.orders o where o.oid = 2)",
         "select c.name from Customer c, in(c.orders) o where o.amount " +
-        "between" +
+            "between" +
             " (select max(o.amount) from Order o) and" +
             " (select avg(o.amount) from Order o) ",
         "select o.oid from Order o where o.amount >" +
@@ -90,12 +91,12 @@
             "(SELECT MAX(m3.datePublished) "+
             "FROM Magazine m3 "+
             "WHERE m3.idPublisher.id = p.id)) ", 
-    // outstanding problem subqueries:
-    // "select o from Order o where o.amount > (select count(o) from Order o)",
-    // "select o from Order o where o.amount > (select count(o2) from
-    // Order o2)",
-    // "select c from Customer c left join c.orders o where not exists"
-    //   + " (select o2 from c.orders o2 where o2 = o)",
+        "select o from Order o where o.amount > " +
+            " (select count(o) from Order o)",
+        "select o from Order o where o.amount > " +
+            "(select count(o2) from Order o2)",
+        "select c from Customer c left join c.orders o where not exists"
+         + " (select o2 from c.orders o2 where o2 = o)",
     };
 
     static String[]  querys_jpa20 = new String[] {        
@@ -206,4 +207,16 @@
         q.getResultList();
         em.close();
     }
+
+    public void testUpdateWithCorrelatedSubquery() {
+        String update = "update Customer c set c.creditRating = ?1 where EXISTS" +
+           " (select o from  in(c.orders)  o)";
+        EntityManager em = emf.createEntityManager();
+        em.getTransaction().begin();
+        CreditRating creditRating = CreditRating.GOOD;
+        int updateCount = em.createQuery(update).
+            setParameter(1, creditRating).executeUpdate();
+        em.getTransaction().rollback();
+        em.close();
+    }
 }