diff --git a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCStoreQuery.java b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCStoreQuery.java index c23c7317f..2cfcbdafe 100644 --- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCStoreQuery.java +++ b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCStoreQuery.java @@ -131,7 +131,7 @@ public class JDBCStoreQuery ClassMetaData base, ClassMetaData[] metas, boolean subclasses, ExpressionFactory[] facts, QueryExpressions[] exps, Object[] params, boolean lrs, long startIdx, long endIdx) { - if (metas.length > 1 && exps[0].aggregate) + if (metas.length > 1 && exps[0].isAggregate()) throw new UserException(Localizer.forPackage(JDBCStoreQuery.class) .get("mult-mapping-aggregate", Arrays.asList(metas))); diff --git a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/SelectConstructor.java b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/SelectConstructor.java index 189123d11..79245b7da 100644 --- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/SelectConstructor.java +++ b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/SelectConstructor.java @@ -177,8 +177,8 @@ class SelectConstructor { // get unique candidate values) and needed field values and // applies the where conditions; the outer select applies // ordering, grouping, etc - if (exps.aggregate || (exps.distinct & exps.DISTINCT_TRUE) == 0) - { + if (exps.isAggregate() + || (exps.distinct & exps.DISTINCT_TRUE) == 0) { DBDictionary dict = store.getDBDictionary(); dict.assertSupport(dict.supportsSubselect, "SupportsSubselect"); @@ -186,7 +186,7 @@ class SelectConstructor { Select inner = sel; sel = store.getSQLFactory().newSelect(); sel.setParent(parent, alias); - sel.setDistinct(exps.aggregate + sel.setDistinct(exps.isAggregate() && (exps.distinct & exps.DISTINCT_TRUE) != 0); sel.setFromSelect(inner); } diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/ExpressionStoreQuery.java b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/ExpressionStoreQuery.java index 278002122..038f93dcf 100644 --- a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/ExpressionStoreQuery.java +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/ExpressionStoreQuery.java @@ -349,7 +349,7 @@ public class ExpressionStoreQuery } public final boolean isAggregate(StoreQuery q) { - return assertQueryExpression().aggregate; + return assertQueryExpression().isAggregate(); } public final boolean hasGrouping(StoreQuery q) { diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/InMemoryExpressionFactory.java b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/InMemoryExpressionFactory.java index 8f0657bba..9c43d5dda 100644 --- a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/InMemoryExpressionFactory.java +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/InMemoryExpressionFactory.java @@ -221,7 +221,7 @@ public class InMemoryExpressionFactory return matches; // if an ungrouped aggregate, evaluate the whole matches list - if (exps.grouping.length == 0 && exps.aggregate) { + if (exps.grouping.length == 0 && exps.isAggregate()) { Object[] projection = project(matches, exps, true, ctx, params); return Arrays.asList(new Object[]{ projection }); } diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/QueryExpressions.java b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/QueryExpressions.java index 268b0401b..f9270e253 100644 --- a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/QueryExpressions.java +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/QueryExpressions.java @@ -37,7 +37,11 @@ public class QueryExpressions { public static final int DISTINCT_FALSE = 2 << 2; public static final Value[] EMPTY_VALUES = new Value[0]; - public boolean aggregate = false; + /** + * Map of {@link FieldMetaData},{@link Value} for update statements. + */ + public Map updates = null; + public int distinct = DISTINCT_AUTO; public String alias = null; public Value[] projections = EMPTY_VALUES; @@ -55,9 +59,51 @@ public class QueryExpressions { public int operation = QueryOperations.OP_SELECT; public ClassMetaData[] accessPath = StoreQuery.EMPTY_METAS; public String[] fetchPaths = StoreQuery.EMPTY_STRINGS; + private Boolean _aggregate = null; + + public boolean isAggregate() { + if (projections.length == 0) + return false; + if (_aggregate == null) + _aggregate = (AggregateExpressionVisitor.isAggregate(projections)) + ? Boolean.TRUE : Boolean.FALSE; + return _aggregate.booleanValue(); + } /** - * Map of {@link FieldMetaData},{@link Value} for update statements. + * Visitor to determine whether our projections are aggregates. */ - public Map updates = null; + private static class AggregateExpressionVisitor + extends AbstractExpressionVisitor { + + private Value _sub = null; + private boolean _agg = false; + + /** + * Return whether the given values include projections. + */ + public static boolean isAggregate(Value[] vals) { + if (vals.length == 0) + return false; + AggregateExpressionVisitor v = new AggregateExpressionVisitor(); + for (int i = 0; i < vals.length && !v._agg; i++) + vals[i].acceptVisit(v); + return v._agg; + } + + public void enter(Value val) { + if (_agg) + return; + if (_sub == null) { + if (val.isAggregate()) + _agg = true; + } else if (val instanceof Subquery) + _sub = val; + } + + public void exit(Value val) { + if (val == _sub) + _sub = null; + } + } } diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/jpql/JPQLExpressionBuilder.java b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/jpql/JPQLExpressionBuilder.java index d649a0d11..66a3b7429 100644 --- a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/jpql/JPQLExpressionBuilder.java +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/jpql/JPQLExpressionBuilder.java @@ -286,7 +286,6 @@ class JPQLExpressionBuilder JPQLNode parent = parametersNode.getChild(i); JPQLNode node = onlyChild(parent); Value proj = getValue(node); - exps.aggregate = exps.aggregate || node.id == JJTAGGREGATE; exps.projections[i] = proj; exps.projectionAliases[i] = nextAlias();