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 2010/02/02 08:58:31 UTC

svn commit: r905540 - in /openjpa/trunk: openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/ openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/ openjpa-jdbc/src/main/resources/org/apache/openjpa/jdbc/kernel/exps/ openjpa-persistence-jd...

Author: faywang
Date: Tue Feb  2 07:58:31 2010
New Revision: 905540

URL: http://svn.apache.org/viewvc?rev=905540&view=rev
Log:
OPENJPA-1483: support count distinct compound key 

Modified:
    openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/Count.java
    openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/Distinct.java
    openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/PCPath.java
    openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/SelectConstructor.java
    openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/UnaryOp.java
    openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/SelectImpl.java
    openjpa/trunk/openjpa-jdbc/src/main/resources/org/apache/openjpa/jdbc/kernel/exps/localizer.properties
    openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/enhance/identity/TestMappedById.java

Modified: openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/Count.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/Count.java?rev=905540&r1=905539&r2=905540&view=diff
==============================================================================
--- openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/Count.java (original)
+++ openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/Count.java Tue Feb  2 07:58:31 2010
@@ -18,6 +18,7 @@
  */
 package org.apache.openjpa.jdbc.kernel.exps;
 
+import org.apache.openjpa.jdbc.schema.Column;
 import org.apache.openjpa.jdbc.sql.SQLBuffer;
 import org.apache.openjpa.jdbc.sql.Select;
 
@@ -29,16 +30,30 @@
 class Count
     extends UnaryOp {
 
+    private boolean isCountMultiColumns = false;
+    private boolean isCountDistinct = false;
+    
     /**
      * Constructor. Provide the value to operate on.
      */
     public Count(Val val) {
         super(val);
+        if (val instanceof Distinct)
+            isCountDistinct = true;
     }
 
     public ExpState initialize(Select sel, ExpContext ctx, int flags) {
         // join into related object if present
-        return initializeValue(sel, ctx, JOIN_REL);
+        ExpState expState = initializeValue(sel, ctx, JOIN_REL);
+        Val val = isCountDistinct ? ((Distinct)getValue()).getValue() : getValue();
+        if (val instanceof PCPath) {
+            Column[] cols = ((PCPath)val).getColumns(expState);
+            if (cols.length > 1) {
+                isCountMultiColumns = true;
+            }
+        }
+            
+        return expState;
     }
 
     protected Class getType(Class c) {
@@ -52,15 +67,24 @@
     public boolean isAggregate() {
         return true;
     }
-    
+
+    public boolean isCountDistinctMultiCols() {
+        return isCountDistinct && isCountMultiColumns;
+    }
+
     /**
      * Overrides SQL formation by replacing COUNT(column) by COUNT(*) when specific conditions are met and
      * DBDictionary configuration <code>useWildCardForCount</code> is set.
      */
     @Override
     public void appendTo(Select sel, ExpContext ctx, ExpState state, SQLBuffer sql, int index) {
-        super.appendTo(sel, ctx, state, sql, index);
-        if (ctx.store.getDBDictionary().useWildCardForCount && state.joins.isEmpty()) {
+        if (isCountDistinctMultiCols()) {
+            getValue().appendTo(sel, ctx, state, sql, 0);
+            sql.addCastForParam(getOperator(), getValue());
+        } else
+            super.appendTo(sel, ctx, state, sql, index);
+        if ((ctx.store.getDBDictionary().useWildCardForCount && state.joins.isEmpty()) ||
+            !isCountDistinct && isCountMultiColumns){
             String s = sql.getSQL();
             if (s.startsWith("COUNT(") && s.endsWith(")")) {
                 sql.replaceSqlString("COUNT(".length(), s.length()-1, "*");

Modified: openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/Distinct.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/Distinct.java?rev=905540&r1=905539&r2=905540&view=diff
==============================================================================
--- openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/Distinct.java (original)
+++ openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/Distinct.java Tue Feb  2 07:58:31 2010
@@ -41,4 +41,22 @@
     protected String getOperator() {
         return "DISTINCT";
     }
+
+    @Override
+    public void appendTo(Select sel, ExpContext ctx, ExpState state, 
+        SQLBuffer sql, int index) {
+        Val val = getValue();
+        if (val instanceof PCPath) {
+            boolean noParen = getNoParen();
+            sql.append(getOperator());
+            sql.append(noParen ? " " : "(");
+            ((PCPath)val).appendTo(sel, ctx, state, sql); 
+            sql.addCastForParam(getOperator(), val);
+            if (!noParen)
+                sql.append(")");
+            
+        } else
+            super.appendTo(sel, ctx, state, sql, index);
+    }
+    
 }

Modified: openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/PCPath.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/PCPath.java?rev=905540&r1=905539&r2=905540&view=diff
==============================================================================
--- openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/PCPath.java (original)
+++ openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/PCPath.java Tue Feb  2 07:58:31 2010
@@ -971,8 +971,22 @@
     }
 
     public void appendTo(Select sel, ExpContext ctx, ExpState state, 
+        SQLBuffer sql) {
+        Column[] cols = getColumns(state);
+        for (int i = 0; i < cols.length; i++) {
+            appendTo(sel, state, sql, cols[i]);
+            if (i < cols.length -1)
+            sql.append(", ");
+        }
+    }
+    
+    public void appendTo(Select sel, ExpContext ctx, ExpState state, 
         SQLBuffer sql, int index) {
         Column col = getColumns(state)[index];
+        appendTo(sel, state, sql, col);
+    }
+    
+    public void appendTo(Select sel, ExpState state, SQLBuffer sql, Column col) {
         if (sel != null)
             sel.setSchemaAlias(_schemaAlias);
 

Modified: openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/SelectConstructor.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/SelectConstructor.java?rev=905540&r1=905539&r2=905540&view=diff
==============================================================================
--- openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/SelectConstructor.java (original)
+++ openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/SelectConstructor.java Tue Feb  2 07:58:31 2010
@@ -36,6 +36,8 @@
 import org.apache.openjpa.kernel.exps.QueryExpressions;
 import org.apache.openjpa.kernel.exps.Subquery;
 import org.apache.openjpa.kernel.exps.Value;
+import org.apache.openjpa.lib.util.Localizer;
+import org.apache.openjpa.util.UnsupportedException;
 
 /**
  * Turns parsed queries into selects.
@@ -48,6 +50,7 @@
 
     private boolean _extent = false;
     private Select _subselect = null;
+    private static final Localizer _loc = Localizer.forPackage(SelectConstructor.class);
 
     /**
      * Return true if we know the select to have on criteria; to be an extent.
@@ -115,6 +118,18 @@
         }
         for (int i = 0; i < exps.grouping.length; i++)
             ((Val) exps.grouping[i]).groupBy(sel, ctx, state.grouping[i]);
+        
+        if (exps.projections.length == 1) {
+            Val val = (Val) exps.projections[0];
+            if (val instanceof Count && ((Count)val).isCountDistinctMultiCols()) {
+                Select newSel = ctx.store.getSQLFactory().newSelect();
+                newSel.select("COUNT(*)", val);
+                newSel.setExpectedResultCount(1, true);
+                newSel.setFromSelect(sel);
+                sel.setExpectedResultCount(0, true);
+                sel = newSel;
+            }
+        }        
         return sel;
     }
 
@@ -235,6 +250,10 @@
                 // projections; this ensures that we have all our joins cached
                 state.projections[i] = resultVal.initialize(sel, ctx, 
                     Val.JOIN_REL | Val.FORCE_OUTER);
+                if (exps.projections.length > 1 && resultVal instanceof Count) {
+                    if (((Count)resultVal).isCountDistinctMultiCols())
+                        throw new UnsupportedException(_loc.get("count-distinct-multi-col-only"));
+                }
                 joins = sel.and(joins, state.projections[i].joins);
             }
         }
@@ -290,6 +309,8 @@
         Select inner = sel.getFromSelect();
         Val val;
         Joins joins = null;
+        boolean isCountDistinctMultiCols = false;
+
         if (sel.getSubselectPath() != null)
             joins = sel.newJoins().setSubselect(sel.getSubselectPath());
 
@@ -307,9 +328,18 @@
             // subselect for objects; we really just need the primary key values
             sel.select(mapping.getPrimaryKeyColumns(), joins);
         } else {
+            if (exps.projections.length == 1) {
+                val = (Val) exps.projections[0];
+                if (val instanceof Count && ((Count)val).isCountDistinctMultiCols()) {
+                    isCountDistinctMultiCols = true;
+                    if (sel.getParent() != null)
+                        throw new UnsupportedException(_loc.get("count-distinct-multi-col-subselect-unsupported"));
+                }
+            }            
+
             // if we have an inner select, we need to select the candidate
             // class' pk columns to guarantee unique instances
-            if (inner != null)
+            if (inner != null && !isCountDistinctMultiCols)
                 inner.select(mapping.getPrimaryKeyColumns(), joins);
 
             // select each result value; no need to pass on the eager mode since
@@ -317,9 +347,13 @@
             boolean pks = sel.getParent() != null;
             for (int i = 0; i < exps.projections.length; i++) {
                 val = (Val) exps.projections[i];
-                if (inner != null)
-                    val.selectColumns(inner, ctx, state.projections[i], pks);
-                val.select(sel, ctx, state.projections[i], pks);
+                if (inner != null) {
+                    if (!isCountDistinctMultiCols)
+                        val.selectColumns(inner, ctx, state.projections[i], pks);
+                    else
+                        val.select(inner, ctx, state.projections[i], pks);
+                } else
+                    val.select(sel, ctx, state.projections[i], pks);
             }
 
             // make sure having columns are selected since it is required by 

Modified: openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/UnaryOp.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/UnaryOp.java?rev=905540&r1=905539&r2=905540&view=diff
==============================================================================
--- openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/UnaryOp.java (original)
+++ openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/UnaryOp.java Tue Feb  2 07:58:31 2010
@@ -74,6 +74,10 @@
     public void setImplicitType(Class type) {
         _cast = type;
     }
+    
+    public boolean getNoParen() {
+        return _noParen;
+    }
 
     public ExpState initialize(Select sel, ExpContext ctx, int flags) {
         return initializeValue(sel, ctx, flags);

Modified: openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/SelectImpl.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/SelectImpl.java?rev=905540&r1=905539&r2=905540&view=diff
==============================================================================
--- openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/SelectImpl.java (original)
+++ openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/SelectImpl.java Tue Feb  2 07:58:31 2010
@@ -3111,9 +3111,10 @@
                         alias = alias + _dict.getStringVal;
                         
                     String as = null;
-                    if (inner)
-                        as = ((String) alias).replace('.', '_');
-                    else if (_selectAs != null)
+                    if (inner) {
+                        if (alias instanceof String)
+                            as = ((String) alias).replace('.', '_');
+                    } else if (_selectAs != null)
                         as = (String) _selectAs.get(id);
                     else if (id instanceof Value)
                         as = ((Value) id).getAlias();

Modified: openjpa/trunk/openjpa-jdbc/src/main/resources/org/apache/openjpa/jdbc/kernel/exps/localizer.properties
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/main/resources/org/apache/openjpa/jdbc/kernel/exps/localizer.properties?rev=905540&r1=905539&r2=905540&view=diff
==============================================================================
--- openjpa/trunk/openjpa-jdbc/src/main/resources/org/apache/openjpa/jdbc/kernel/exps/localizer.properties (original)
+++ openjpa/trunk/openjpa-jdbc/src/main/resources/org/apache/openjpa/jdbc/kernel/exps/localizer.properties Tue Feb  2 07:58:31 2010
@@ -30,4 +30,8 @@
 no-order-column: Field "{0}" does not have order column defined".
 not-collection-parm: Invalid input parameter "{0}", a collection-valued \
     input parameter is expected.
-empty-collection-parm: Input parameter "{0}" is empty.
\ No newline at end of file
+empty-collection-parm: Input parameter "{0}" is empty.
+count-distinct-multi-col-only: Count distinct compound primary key is not \
+    supported when there are other projection items.
+count-distinct-multi-col-subselect-unsupported: Count distinct multiple columns \
+	in the subselect is not supported.
\ No newline at end of file

Modified: openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/enhance/identity/TestMappedById.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/enhance/identity/TestMappedById.java?rev=905540&r1=905539&r2=905540&view=diff
==============================================================================
--- openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/enhance/identity/TestMappedById.java (original)
+++ openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/enhance/identity/TestMappedById.java Tue Feb  2 07:58:31 2010
@@ -206,6 +206,55 @@
         assertNotNull(newDep);
     }
     
+    public void testCountDistinctMultiCols() {
+        EntityManager em = emf.createEntityManager(); 
+
+        Employee2 emp1 = new Employee2();
+        EmployeeId2 empId1 = new EmployeeId2();
+        empId1.setFirstName("James");
+        empId1.setLastName("Bond");
+        emp1.setEmpId(empId1);
+        
+        Employee2 emp2 = new Employee2();
+        EmployeeId2 empId2 = new EmployeeId2();
+        empId2.setFirstName("James");
+        empId2.setLastName("Obama");
+        emp2.setEmpId(empId2);
+        
+        Dependent2 dep1 = new Dependent2();
+        DependentId2 depId1 = new DependentId2();
+        depId1.setEmpPK(empId1);
+        depId1.setName("Alan");
+        dep1.setId(depId1);
+        
+        Dependent2 dep2 = new Dependent2();
+        DependentId2 depId2 = new DependentId2();
+        depId2.setEmpPK(empId2);
+        depId2.setName("Darren");
+        dep2.setId(depId2);
+        
+        em.persist(emp1);
+        em.persist(emp2);
+        em.persist(dep1);
+        em.persist(dep2);
+        
+        em.getTransaction().begin();
+        em.flush();        
+        em.getTransaction().commit();
+        
+        String[] jpqls = {
+            "SELECT COUNT (DISTINCT d2.emp) FROM Dependent2 d2",
+            "select count (DISTINCT d2) from Dependent2 d2",
+        };
+        
+        for (int i = 0; i < jpqls.length; i++) {
+            Query q = em.createQuery(jpqls[i]) ;
+            Long o = (Long)q.getSingleResult();
+            int count = (int)o.longValue();
+            assertEquals(2, count);
+        }
+    }
+
     public void createObj1() {
         EntityManager em = emf.createEntityManager();
         EntityTransaction tran = em.getTransaction();