Search in sources :

Example 1 with AggregateExpression

use of com.blazebit.persistence.parser.expression.AggregateExpression in project blaze-persistence by Blazebit.

the class SizeTransformationVisitor method getSizeExpression.

private Expression getSizeExpression(ExpressionModifier parentModifier, PathExpression sizeArg) {
    JoinNode sizeArgJoin = (JoinNode) sizeArg.getBaseNode();
    String property = sizeArg.getPathReference().getField();
    final Type<?> nodeType = ((JoinNode) sizeArg.getBaseNode()).getNodeType();
    if (!(nodeType instanceof EntityType<?>)) {
        throw new IllegalArgumentException("Size on a collection owned by a non-entity type is not supported yet: " + sizeArg);
    }
    final EntityType<?> startType = (EntityType<?>) nodeType;
    AttributeHolder result = JpaUtils.getAttributeForJoining(metamodel, sizeArg);
    PluralAttribute<?, ?, ?> targetAttribute = (PluralAttribute<?, ?, ?>) result.getAttribute();
    if (targetAttribute == null) {
        throw new RuntimeException("Attribute [" + property + "] not found on class " + startType.getJavaType().getName());
    }
    final PluralAttribute.CollectionType collectionType = targetAttribute.getCollectionType();
    final boolean isElementCollection = jpaProvider.getJpaMetamodelAccessor().isElementCollection(targetAttribute);
    boolean subqueryRequired;
    if (isElementCollection) {
        subqueryRequired = false;
    } else {
        ManagedType<?> managedTargetType = (ManagedType<?>) result.getAttributeType();
        if (managedTargetType instanceof EntityType<?>) {
            // we could also generate counts for collections with embeddable id but we do not implement this for now
            subqueryRequired = ((EntityType<?>) managedTargetType).getIdType().getPersistenceType() == PersistenceType.EMBEDDABLE;
        } else {
            throw new RuntimeException("Path [" + sizeArg.toString() + "] does not refer to a collection");
        }
    }
    // build group by id clause
    List<PathExpression> groupByExprs = new ArrayList<>();
    for (SingularAttribute<?, ?> idAttribute : JpaMetamodelUtils.getIdAttributes(startType)) {
        List<PathElementExpression> pathElementExpr = new ArrayList<>(2);
        pathElementExpr.add(new PropertyExpression(sizeArgJoin.getAlias()));
        pathElementExpr.add(new PropertyExpression(idAttribute.getName()));
        PathExpression groupByExpr = new PathExpression(pathElementExpr);
        groupByExprs.add(groupByExpr);
    }
    subqueryRequired = subqueryRequired || // we could also generate counts for collections with IdClass attributes but we do not implement this for now
    !startType.hasSingleIdAttribute() || joinManager.getRoots().size() > 1 || clause == ClauseType.JOIN || !isCountTransformationEnabled() || // for now, we always generate a subquery when a bag is encountered
    jpaProvider.isBag((EntityType<?>) targetAttribute.getDeclaringType(), targetAttribute.getName()) || requiresBlacklistedNode(sizeArg) || aggregateFunctionContext;
    if (subqueryRequired) {
        return wrapSubqueryConditionally(generateSubquery(sizeArg), aggregateFunctionContext);
    } else {
        if (currentJoinNode != null && (!currentJoinNode.equals(sizeArgJoin))) {
            int currentJoinDepth = currentJoinNode.getJoinDepth();
            int sizeArgJoinDepth = sizeArgJoin.getJoinDepth();
            if (currentJoinDepth > sizeArgJoinDepth) {
                return wrapSubqueryConditionally(generateSubquery(sizeArg), aggregateFunctionContext);
            } else {
                // we have to change all transformed expressions to subqueries
                for (TransformedExpressionEntry transformedExpressionEntry : transformedExpressions) {
                    PathExpression originalSizeArg = transformedExpressionEntry.getOriginalSizeArg();
                    Expression subquery = wrapSubqueryConditionally(generateSubquery(originalSizeArg), transformedExpressionEntry.isAggregateFunctionContext());
                    transformedExpressionEntry.getParentModifier().set(subquery);
                }
                transformedExpressions.clear();
                requiredGroupBys.clear();
                lateJoins.clear();
                distinctRequired = false;
                if (currentJoinDepth == sizeArgJoinDepth) {
                    return wrapSubqueryConditionally(generateSubquery(sizeArg), aggregateFunctionContext);
                }
            }
        }
        for (PathExpression groupByExpr : groupByExprs) {
            joinManager.implicitJoin(groupByExpr, true, true, true, null, null, new HashSet<String>(), false, false, false, false);
        }
        PathExpression originalSizeArg = sizeArg.copy(ExpressionCopyContext.EMPTY);
        originalSizeArg.setPathReference(sizeArg.getPathReference());
        sizeArg.setUsedInCollectionFunction(false);
        List<Expression> countArguments = new ArrayList<>();
        String joinLookupKey = getJoinLookupKey(sizeArg);
        LateJoinEntry lateJoin = lateJoins.get(joinLookupKey);
        if (lateJoin == null) {
            lateJoin = new LateJoinEntry();
            lateJoins.put(joinLookupKey, lateJoin);
        }
        lateJoin.getExpressionsToJoin().add(sizeArg);
        lateJoin.getClauseDependencies().add(clause);
        if ((isElementCollection && collectionType != PluralAttribute.CollectionType.MAP) || collectionType == PluralAttribute.CollectionType.SET) {
            if (IDENTIFIABLE_PERSISTENCE_TYPES.contains(targetAttribute.getElementType().getPersistenceType()) && targetAttribute.isCollection()) {
                // append id attribute name of joinable size argument
                PluralAttribute<?, ?, ?> sizeArgTargetAttribute = (PluralAttribute<?, ?, ?>) JpaMetamodelUtils.getAttribute(startType, sizeArg.getPathReference().getField());
                for (Attribute<?, ?> idAttribute : JpaMetamodelUtils.getIdAttributes(((IdentifiableType<?>) sizeArgTargetAttribute.getElementType()))) {
                    List<PathElementExpression> pathElementExpressions = new ArrayList<>(sizeArg.getExpressions().size() + 1);
                    pathElementExpressions.addAll(sizeArg.getExpressions());
                    pathElementExpressions.add(new PropertyExpression(idAttribute.getName()));
                    PathExpression pathExpression = new PathExpression(pathElementExpressions);
                    countArguments.add(pathExpression);
                    lateJoin.getExpressionsToJoin().add(pathExpression);
                }
            } else {
                countArguments.add(sizeArg);
            }
        } else {
            sizeArg.setCollectionQualifiedPath(true);
            if (collectionType == PluralAttribute.CollectionType.LIST) {
                countArguments.add(new ListIndexExpression(sizeArg));
            } else {
                countArguments.add(new MapKeyExpression(sizeArg));
            }
        }
        AggregateExpression countExpr = createCountFunction(distinctRequired, countArguments);
        transformedExpressions.add(new TransformedExpressionEntry(countExpr, originalSizeArg, parentModifier, aggregateFunctionContext));
        currentJoinNode = (JoinNode) originalSizeArg.getBaseNode();
        if (!distinctRequired) {
            if (lateJoins.size() + joinManager.getCollectionJoins().size() > 1) {
                distinctRequired = true;
                /*
                     *  As soon as we encounter another collection join, set previously
                     *  performed transformations to distinct.
                     */
                for (TransformedExpressionEntry transformedExpressionEntry : transformedExpressions) {
                    AggregateExpression transformedExpr = transformedExpressionEntry.getTransformedExpression();
                    if (ExpressionUtils.isCustomFunctionInvocation(transformedExpr) && AbstractCountFunction.FUNCTION_NAME.equalsIgnoreCase(((StringLiteral) transformedExpr.getExpressions().get(0)).getValue())) {
                        Expression possibleDistinct = transformedExpr.getExpressions().get(1);
                        if (!(possibleDistinct instanceof StringLiteral) || !AbstractCountFunction.DISTINCT_QUALIFIER.equals(((StringLiteral) possibleDistinct).getValue())) {
                            transformedExpr.getExpressions().add(1, new StringLiteral(AbstractCountFunction.DISTINCT_QUALIFIER));
                        }
                    } else {
                        transformedExpr.setDistinct(true);
                    }
                }
            }
        }
        for (Expression groupByExpr : groupByExprs) {
            String groupByExprString = groupByExpr.toString();
            ResolvedExpression resolvedExpression = new ResolvedExpression(groupByExprString, groupByExpr);
            Set<ClauseType> clauseTypes = requiredGroupBys.get(resolvedExpression);
            if (clauseTypes == null) {
                requiredGroupBys.put(resolvedExpression, EnumSet.of(clause));
            } else {
                clauseTypes.add(clause);
            }
        }
        return countExpr;
    }
}
Also used : ArrayList(java.util.ArrayList) ResolvedExpression(com.blazebit.persistence.impl.ResolvedExpression) MapKeyExpression(com.blazebit.persistence.parser.expression.MapKeyExpression) PathExpression(com.blazebit.persistence.parser.expression.PathExpression) ListIndexExpression(com.blazebit.persistence.parser.expression.ListIndexExpression) PropertyExpression(com.blazebit.persistence.parser.expression.PropertyExpression) AttributeHolder(com.blazebit.persistence.impl.AttributeHolder) ManagedType(javax.persistence.metamodel.ManagedType) IdentifiableType(javax.persistence.metamodel.IdentifiableType) PluralAttribute(javax.persistence.metamodel.PluralAttribute) JoinNode(com.blazebit.persistence.impl.JoinNode) AggregateExpression(com.blazebit.persistence.parser.expression.AggregateExpression) EntityType(javax.persistence.metamodel.EntityType) PathElementExpression(com.blazebit.persistence.parser.expression.PathElementExpression) StringLiteral(com.blazebit.persistence.parser.expression.StringLiteral) ListIndexExpression(com.blazebit.persistence.parser.expression.ListIndexExpression) Expression(com.blazebit.persistence.parser.expression.Expression) PathExpression(com.blazebit.persistence.parser.expression.PathExpression) AggregateExpression(com.blazebit.persistence.parser.expression.AggregateExpression) PropertyExpression(com.blazebit.persistence.parser.expression.PropertyExpression) ResolvedExpression(com.blazebit.persistence.impl.ResolvedExpression) FunctionExpression(com.blazebit.persistence.parser.expression.FunctionExpression) SubqueryExpression(com.blazebit.persistence.parser.expression.SubqueryExpression) MapKeyExpression(com.blazebit.persistence.parser.expression.MapKeyExpression) PathElementExpression(com.blazebit.persistence.parser.expression.PathElementExpression) ClauseType(com.blazebit.persistence.impl.ClauseType)

Example 2 with AggregateExpression

use of com.blazebit.persistence.parser.expression.AggregateExpression in project blaze-persistence by Blazebit.

the class SimpleQueryGenerator method visit.

@Override
public void visit(FunctionExpression expression) {
    if (expression.getRealArgument() != null) {
        expression.getRealArgument().accept(this);
        return;
    }
    BooleanLiteralRenderingContext oldBooleanLiteralRenderingContext = setBooleanLiteralRenderingContext(BooleanLiteralRenderingContext.PLAIN);
    ParameterRenderingMode oldParameterRenderingMode = setParameterRenderingMode(ParameterRenderingMode.PLACEHOLDER);
    List<Expression> expressions = expression.getExpressions();
    int size = expressions.size();
    boolean hasExpressions = size != 0;
    String functionName = expression.getFunctionName();
    WindowDefinition windowDefinition = expression.getWindowDefinition();
    if (expression instanceof AggregateExpression && windowDefinition != null && windowDefinition.isFilterOnly()) {
        sb.append(functionName, "window_".length(), functionName.length());
    } else {
        sb.append(functionName);
    }
    // @formatter:off
    if (!"CURRENT_TIME".equalsIgnoreCase(functionName) && !"CURRENT_DATE".equalsIgnoreCase(functionName) && !"CURRENT_TIMESTAMP".equalsIgnoreCase(functionName)) {
        // @formatter:on
        sb.append('(');
        if (expression instanceof AggregateExpression) {
            AggregateExpression aggregateExpression = (AggregateExpression) expression;
            if (aggregateExpression.isDistinct()) {
                sb.append("DISTINCT ");
            }
            if (!hasExpressions && "COUNT".equalsIgnoreCase(aggregateExpression.getFunctionName())) {
                sb.append('*');
            }
        }
        if (hasExpressions) {
            expressions.get(0).accept(this);
            for (int i = 1; i < size; i++) {
                sb.append(",");
                expressions.get(i).accept(this);
            }
        }
        sb.append(')');
        List<OrderByItem> withinGroup = expression.getWithinGroup();
        if (withinGroup != null && !withinGroup.isEmpty()) {
            sb.append(" WITHIN GROUP (");
            for (int i = 0; i < withinGroup.size(); i++) {
                visit(withinGroup.get(i));
                sb.append(", ");
            }
            sb.setLength(sb.length() - 1);
            sb.setCharAt(sb.length() - 1, ')');
        }
        visitWindowDefinition(windowDefinition);
    }
    setBooleanLiteralRenderingContext(oldBooleanLiteralRenderingContext);
    setParameterRenderingMode(oldParameterRenderingMode);
}
Also used : OrderByItem(com.blazebit.persistence.parser.expression.OrderByItem) ListIndexExpression(com.blazebit.persistence.parser.expression.ListIndexExpression) WhenClauseExpression(com.blazebit.persistence.parser.expression.WhenClauseExpression) Expression(com.blazebit.persistence.parser.expression.Expression) TreatExpression(com.blazebit.persistence.parser.expression.TreatExpression) ParameterExpression(com.blazebit.persistence.parser.expression.ParameterExpression) ArithmeticExpression(com.blazebit.persistence.parser.expression.ArithmeticExpression) PathExpression(com.blazebit.persistence.parser.expression.PathExpression) TypeFunctionExpression(com.blazebit.persistence.parser.expression.TypeFunctionExpression) AggregateExpression(com.blazebit.persistence.parser.expression.AggregateExpression) PropertyExpression(com.blazebit.persistence.parser.expression.PropertyExpression) ArrayExpression(com.blazebit.persistence.parser.expression.ArrayExpression) FunctionExpression(com.blazebit.persistence.parser.expression.FunctionExpression) SubqueryExpression(com.blazebit.persistence.parser.expression.SubqueryExpression) PathElementExpression(com.blazebit.persistence.parser.expression.PathElementExpression) GeneralCaseExpression(com.blazebit.persistence.parser.expression.GeneralCaseExpression) TrimExpression(com.blazebit.persistence.parser.expression.TrimExpression) MapEntryExpression(com.blazebit.persistence.parser.expression.MapEntryExpression) MapValueExpression(com.blazebit.persistence.parser.expression.MapValueExpression) NullExpression(com.blazebit.persistence.parser.expression.NullExpression) SimpleCaseExpression(com.blazebit.persistence.parser.expression.SimpleCaseExpression) MapKeyExpression(com.blazebit.persistence.parser.expression.MapKeyExpression) AggregateExpression(com.blazebit.persistence.parser.expression.AggregateExpression) WindowDefinition(com.blazebit.persistence.parser.expression.WindowDefinition)

Example 3 with AggregateExpression

use of com.blazebit.persistence.parser.expression.AggregateExpression in project blaze-persistence by Blazebit.

the class GroupByExpressionGatheringVisitor method extractGroupByExpressions.

public Set<Expression> extractGroupByExpressions(Expression expression, ClauseType clauseType) {
    // Also, grouping by just a parameter does not make sense as the value is constant
    if (expression instanceof LiteralExpression || expression instanceof ParameterExpression) {
        return Collections.emptySet();
    }
    clear();
    try {
        // When having a predicate at the top level, we have to collect
        collect = expression instanceof Predicate;
        boolean expressionWasSplit = expression.accept(this);
        // In the HAVING clause we additionally add the path expressions to the expression set for DBMS like MySQL that's don't support partial expression structure matching
        if (clauseType == ClauseType.HAVING && !dbmsDialect.supportsGroupByExpressionInHavingMatching()) {
            expression.accept(new VisitorAdapter() {

                @Override
                public void visit(FunctionExpression expression) {
                    // Skip aggregate expressions
                    if (expression instanceof AggregateExpression || (treatSizeAsAggregate && com.blazebit.persistence.parser.util.ExpressionUtils.isSizeFunction(expression))) {
                        return;
                    }
                    super.visit(expression);
                }

                @Override
                public void visit(SubqueryExpression expression) {
                    GroupByExpressionGatheringVisitor.this.visit(expression);
                }

                @Override
                public void visit(PathExpression expression) {
                    if (expression.getBaseNode() == null) {
                        ((SelectInfo) aliasManager.getAliasInfo(expression.toString())).getExpression().accept(this);
                    } else {
                        expressions.add(expression);
                    }
                }

                @Override
                public void visit(TreatExpression expression) {
                    expressions.add(expression);
                }

                @Override
                public void visit(PropertyExpression expression) {
                    expressions.add(expression);
                }

                @Override
                public void visit(ListIndexExpression expression) {
                    expressions.add(expression);
                }

                @Override
                public void visit(MapEntryExpression expression) {
                    expressions.add(expression);
                }

                @Override
                public void visit(MapKeyExpression expression) {
                    expressions.add(expression);
                }

                @Override
                public void visit(MapValueExpression expression) {
                    expressions.add(expression);
                }
            });
            return expressions;
        }
        if (expressionWasSplit) {
            return expressions;
        }
    } catch (IllegalParameterException ex) {
        throw new IllegalArgumentException("Can't use the expression '" + expression + "' as an implicit group by clause, because the parameter '" + ex.parameterExpression + "' with the value '" + ex.value + "' can't be rendered as literal which is required!", ex);
    }
    return Collections.singleton(expression);
}
Also used : TreatExpression(com.blazebit.persistence.parser.expression.TreatExpression) MapValueExpression(com.blazebit.persistence.parser.expression.MapValueExpression) LiteralExpression(com.blazebit.persistence.parser.expression.LiteralExpression) AggregateExpression(com.blazebit.persistence.parser.expression.AggregateExpression) BetweenPredicate(com.blazebit.persistence.parser.predicate.BetweenPredicate) Predicate(com.blazebit.persistence.parser.predicate.Predicate) CompoundPredicate(com.blazebit.persistence.parser.predicate.CompoundPredicate) BinaryExpressionPredicate(com.blazebit.persistence.parser.predicate.BinaryExpressionPredicate) IsEmptyPredicate(com.blazebit.persistence.parser.predicate.IsEmptyPredicate) IsNullPredicate(com.blazebit.persistence.parser.predicate.IsNullPredicate) InPredicate(com.blazebit.persistence.parser.predicate.InPredicate) SubqueryExpression(com.blazebit.persistence.parser.expression.SubqueryExpression) MapEntryExpression(com.blazebit.persistence.parser.expression.MapEntryExpression) MapKeyExpression(com.blazebit.persistence.parser.expression.MapKeyExpression) FunctionExpression(com.blazebit.persistence.parser.expression.FunctionExpression) PathExpression(com.blazebit.persistence.parser.expression.PathExpression) ParameterExpression(com.blazebit.persistence.parser.expression.ParameterExpression) VisitorAdapter(com.blazebit.persistence.parser.expression.VisitorAdapter) AbortableVisitorAdapter(com.blazebit.persistence.parser.expression.AbortableVisitorAdapter) ListIndexExpression(com.blazebit.persistence.parser.expression.ListIndexExpression) PropertyExpression(com.blazebit.persistence.parser.expression.PropertyExpression)

Aggregations

AggregateExpression (com.blazebit.persistence.parser.expression.AggregateExpression)3 FunctionExpression (com.blazebit.persistence.parser.expression.FunctionExpression)3 ListIndexExpression (com.blazebit.persistence.parser.expression.ListIndexExpression)3 MapKeyExpression (com.blazebit.persistence.parser.expression.MapKeyExpression)3 PathExpression (com.blazebit.persistence.parser.expression.PathExpression)3 PropertyExpression (com.blazebit.persistence.parser.expression.PropertyExpression)3 SubqueryExpression (com.blazebit.persistence.parser.expression.SubqueryExpression)3 Expression (com.blazebit.persistence.parser.expression.Expression)2 MapEntryExpression (com.blazebit.persistence.parser.expression.MapEntryExpression)2 MapValueExpression (com.blazebit.persistence.parser.expression.MapValueExpression)2 ParameterExpression (com.blazebit.persistence.parser.expression.ParameterExpression)2 PathElementExpression (com.blazebit.persistence.parser.expression.PathElementExpression)2 TreatExpression (com.blazebit.persistence.parser.expression.TreatExpression)2 AttributeHolder (com.blazebit.persistence.impl.AttributeHolder)1 ClauseType (com.blazebit.persistence.impl.ClauseType)1 JoinNode (com.blazebit.persistence.impl.JoinNode)1 ResolvedExpression (com.blazebit.persistence.impl.ResolvedExpression)1 AbortableVisitorAdapter (com.blazebit.persistence.parser.expression.AbortableVisitorAdapter)1 ArithmeticExpression (com.blazebit.persistence.parser.expression.ArithmeticExpression)1 ArrayExpression (com.blazebit.persistence.parser.expression.ArrayExpression)1