Search in sources :

Example 1 with MapValueExpression

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

the class JoinManager method implicitJoin.

private JoinResult implicitJoin(JoinNode current, List<String> resultFields, PathExpression pathExpression, ClauseType fromClause, JoinType joinType, JoinNode currentJoinNode, Set<String> currentlyResolvingAliases, int start, int end, boolean allowParentAliases, boolean joinAllowed, boolean singularJoinAllowed, boolean idRemovable) {
    List<PathElementExpression> pathElements = pathExpression.getExpressions();
    PathElementExpression elementExpr;
    int singleValuedAssociationNameStartIndex = -1;
    int singleValuedAssociationNameEndIndex = -1;
    for (int i = start; i < end; i++) {
        AliasInfo aliasInfo;
        elementExpr = pathElements.get(i);
        if (elementExpr instanceof ArrayExpression) {
            ArrayExpression arrayExpr = (ArrayExpression) elementExpr;
            String joinRelationName;
            List<String> joinRelationAttributes;
            if (!resultFields.isEmpty()) {
                resultFields.add(arrayExpr.getBase().toString());
                joinRelationAttributes = resultFields;
                resultFields = new ArrayList<>();
                joinRelationName = StringUtils.join(".", joinRelationAttributes);
            } else {
                joinRelationName = arrayExpr.getBase().toString();
                joinRelationAttributes = Arrays.asList(joinRelationName);
            }
            current = current == null ? getRootNodeOrFail("Ambiguous join path [", joinRelationName, "] because of multiple root nodes!") : current;
            implicitJoinIndex(arrayExpr);
            // Find a node by a predicate match
            JoinNode matchingNode = findNode(current, joinRelationName, arrayExpr);
            if (matchingNode != null) {
                current = matchingNode;
            } else if (i == 0 && (aliasInfo = aliasManager.getAliasInfoForBottomLevel(joinRelationName)) != null) {
                // The first node is allowed to be a join alias
                if (aliasInfo instanceof SelectInfo) {
                    throw new IllegalArgumentException("Illegal reference to the select alias '" + joinRelationName + "'");
                }
                current = ((JoinAliasInfo) aliasInfo).getJoinNode();
                generateAndApplyOnPredicate(current, arrayExpr);
            } else {
                String joinAlias = getJoinAlias(arrayExpr);
                if (arrayExpr.getBase() instanceof PropertyExpression) {
                    final JoinResult result = createOrUpdateNode(current, joinRelationAttributes, null, joinAlias, joinType, currentJoinNode, true, false, joinAllowed, singularJoinAllowed);
                    current = result.baseNode;
                    resultFields = result.addToList(resultFields);
                } else {
                    joinAlias = aliasManager.generateJoinAlias(joinAlias);
                    Class<?> entityClass = ((EntityLiteral) arrayExpr.getBase()).getValue();
                    joinOn(null, current.getAlias(), entityClass, joinAlias, JoinType.LEFT, false).end();
                    current = ((JoinAliasInfo) aliasManager.getAliasInfo(joinAlias)).getJoinNode();
                }
                generateAndApplyOnPredicate(current, arrayExpr);
            }
        } else if (elementExpr instanceof TreatExpression) {
            if (i != 0 || current != null) {
                throw new IllegalArgumentException("A treat expression should be the first element in a path!");
            }
            TreatExpression treatExpression = (TreatExpression) elementExpr;
            boolean fromSubquery = false;
            boolean fromSelectAlias = false;
            boolean joinRequired = false;
            boolean fetch = false;
            current = implicitJoinTreatExpression((TreatExpression) elementExpr, joinAllowed, singularJoinAllowed, fromClause, joinType, currentJoinNode, currentlyResolvingAliases, fromSubquery, fromSelectAlias, true, false, fetch, false);
        } else if (elementExpr instanceof MapKeyExpression) {
            MapKeyExpression mapKeyExpression = (MapKeyExpression) elementExpr;
            boolean fromSubquery = false;
            boolean fromSelectAlias = false;
            boolean joinRequired = true;
            boolean fetch = false;
            current = joinMapKey(mapKeyExpression, null, fromClause, currentlyResolvingAliases, fromSubquery, fromSelectAlias, joinRequired, fetch, true, true);
        } else if (elementExpr instanceof MapValueExpression) {
            MapValueExpression mapValueExpression = (MapValueExpression) elementExpr;
            boolean fromSubquery = false;
            boolean fromSelectAlias = false;
            boolean joinRequired = true;
            boolean fetch = false;
            implicitJoin(mapValueExpression.getPath(), joinAllowed, singularJoinAllowed, true, null, fromClause, currentlyResolvingAliases, fromSubquery, fromSelectAlias, joinRequired, fetch);
            current = (JoinNode) mapValueExpression.getPath().getBaseNode();
        } else if (pathElements.size() == 1 && (aliasInfo = aliasManager.getAliasInfoForBottomLevel(elementExpr.toString())) != null) {
            if (aliasInfo instanceof SelectInfo) {
                throw new IllegalArgumentException("Can't dereference a select alias");
            } else {
                // Join alias usage like in "joinAlias.relationName"
                current = ((JoinAliasInfo) aliasInfo).getJoinNode();
            }
        } else {
            String elementExpressionString = elementExpr.toString();
            if (current == null) {
                // When no base is given, check if the attribute name is an alias
                aliasInfo = allowParentAliases ? aliasManager.getAliasInfo(elementExpressionString) : aliasManager.getAliasInfoForBottomLevel(elementExpressionString);
                if (aliasInfo instanceof JoinAliasInfo) {
                    current = ((JoinAliasInfo) aliasInfo).getJoinNode();
                    // We can only "consider" this path a single valued association id when we are about to "remove" the id part
                    if (idRemovable && current.getNodeType() instanceof ManagedType<?> && pathElements.size() == i + 2) {
                        ExtendedManagedType<?> managedType = metamodel.getManagedType(ExtendedManagedType.class, current.getManagedType());
                        if (contains(managedType.getIdAttributes(), pathElements.get(i + 1))) {
                            singleValuedAssociationNameStartIndex = 0;
                            singleValuedAssociationNameEndIndex = 0;
                            break;
                        }
                    }
                    continue;
                } else {
                    current = getRootNodeOrFail("Ambiguous join path [", elementExpressionString, "] because of multiple root nodes!");
                }
            }
            int pathElementsSize = pathElements.size();
            if (joinType != JoinType.INNER && (idRemovable || mainQuery.jpaProvider.supportsSingleValuedAssociationIdExpressions()) && (current.getManagedType().getPersistenceType() != Type.PersistenceType.EMBEDDABLE || current.getValuesLikeAttribute() != null) && i + 1 < pathElementsSize) {
                // If the current type is not an embeddable, we check if the path elements access a singular association id
                List<PathElementExpression> pathElementExpressions = new ArrayList<>(pathElementsSize - i);
                for (int j = i; j < pathElementsSize; j++) {
                    PathElementExpression pathElementExpression = pathElements.get(j);
                    if (!(pathElementExpression instanceof PropertyExpression)) {
                        break;
                    }
                    pathElementExpressions.add(pathElementExpression);
                }
                ExtendedManagedType<?> extendedManagedType = metamodel.getManagedType(ExtendedManagedType.class, current.getManagedType());
                // We collect and check if we have only property expressions
                if (pathElementExpressions.size() == pathElementsSize - i) {
                    // Only if all path elements are property expressions, we check if this is single valued association id
                    PathExpression pathRestExpression = new PathExpression(pathElementExpressions);
                    String pathRestString = pathRestExpression.toString();
                    int idx = 0;
                    ExtendedAttribute<?, ?> extendedAttribute;
                    if (current.getValuesLikeAttribute() == null) {
                        extendedAttribute = extendedManagedType.getOwnedSingularAttributes().get(pathRestString);
                    } else {
                        extendedAttribute = extendedManagedType.getAttributes().get(pathRestString);
                    }
                    if (extendedAttribute != null && !JpaMetamodelUtils.isAssociation(extendedAttribute.getAttribute())) {
                        ExtendedAttribute<?, ?> associationAttribute = null;
                        ExtendedAttribute<?, ?> attr;
                        singleValuedAssociationNameStartIndex = i;
                        List<String> newResultFields = new ArrayList<>();
                        for (int j = i; j < end; j++) {
                            idx = pathRestString.indexOf('.', idx + 1);
                            if (idx != -1 && JpaMetamodelUtils.isAssociation((attr = extendedManagedType.getAttribute(pathRestString.substring(0, idx))).getAttribute())) {
                                associationAttribute = attr;
                                singleValuedAssociationNameEndIndex = j;
                            }
                            newResultFields.add(pathElements.get(j).toString());
                        }
                        if (singleValuedAssociationNameEndIndex == -1) {
                            // The expression ends at an association, so this can't be a single valued association id expression
                            singleValuedAssociationNameStartIndex = -1;
                        } else if (current.getValueType() == null && mainQuery.jpaProvider.isForeignJoinColumn((EntityType<?>) current.getManagedType(), new PathExpression(pathElements.subList(singleValuedAssociationNameStartIndex, singleValuedAssociationNameEndIndex + 1)).toString()) || current.getValueType() != null && mainQuery.jpaProvider.isForeignJoinColumn(current.getValueType(), current.getValuesLikeAttribute() + "." + new PathExpression(pathElements.subList(singleValuedAssociationNameStartIndex, singleValuedAssociationNameEndIndex + 1)).toString())) {
                            // If the column is "foreign", we can't do any optimizations
                            singleValuedAssociationNameStartIndex = -1;
                        } else if (!mainQuery.jpaProvider.supportsSingleValuedAssociationNaturalIdExpressions() && !contains(metamodel.getManagedType(ExtendedManagedType.class, associationAttribute.getElementClass()), new PathExpression(pathElements.subList(singleValuedAssociationNameEndIndex + 1, pathElementsSize)))) {
                            // If the jpa provider doesn't support any optimizations, we are done
                            singleValuedAssociationNameStartIndex = -1;
                        } else {
                            resultFields.addAll(newResultFields);
                            break;
                        }
                    }
                }
            }
            if (resultFields.isEmpty()) {
                final JoinResult result = implicitJoinSingle(current, elementExpressionString, null, joinType, currentJoinNode, allowParentAliases, joinAllowed, singularJoinAllowed);
                if (current != result.baseNode) {
                    current = result.baseNode;
                }
                resultFields = result.addToList(resultFields);
            } else {
                resultFields.add(elementExpressionString);
                JoinResult currentResult = createOrUpdateNode(current, resultFields, null, null, joinType, currentJoinNode, true, true, joinAllowed, singularJoinAllowed);
                current = currentResult.baseNode;
                if (!currentResult.hasField()) {
                    resultFields.clear();
                }
            }
        }
    }
    if (resultFields.isEmpty()) {
        return new JoinResult(current, null, current == null ? null : current.getNodeType(), singleValuedAssociationNameStartIndex, singleValuedAssociationNameEndIndex);
    } else {
        List<PathElementExpression> pathElementExpressions = new ArrayList<>(resultFields.size());
        for (int i = 0; i < resultFields.size(); i++) {
            pathElementExpressions.add(new PropertyExpression(resultFields.get(i)));
        }
        Expression expression = new PathExpression(pathElementExpressions);
        Type<?> type = JpaUtils.getAttributeForJoining(metamodel, current.getNodeType(), expression, null).getAttributeType();
        return new JoinResult(current, resultFields, type, singleValuedAssociationNameStartIndex, singleValuedAssociationNameEndIndex);
    }
}
Also used : TreatExpression(com.blazebit.persistence.parser.expression.TreatExpression) MapValueExpression(com.blazebit.persistence.parser.expression.MapValueExpression) ArrayList(java.util.ArrayList) MapKeyExpression(com.blazebit.persistence.parser.expression.MapKeyExpression) PathExpression(com.blazebit.persistence.parser.expression.PathExpression) PropertyExpression(com.blazebit.persistence.parser.expression.PropertyExpression) ManagedType(javax.persistence.metamodel.ManagedType) ExtendedManagedType(com.blazebit.persistence.spi.ExtendedManagedType) ExtendedManagedType(com.blazebit.persistence.spi.ExtendedManagedType) EntityType(javax.persistence.metamodel.EntityType) PathElementExpression(com.blazebit.persistence.parser.expression.PathElementExpression) ListIndexExpression(com.blazebit.persistence.parser.expression.ListIndexExpression) Expression(com.blazebit.persistence.parser.expression.Expression) QualifiedExpression(com.blazebit.persistence.parser.expression.QualifiedExpression) TreatExpression(com.blazebit.persistence.parser.expression.TreatExpression) ParameterExpression(com.blazebit.persistence.parser.expression.ParameterExpression) PathExpression(com.blazebit.persistence.parser.expression.PathExpression) 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) MapEntryExpression(com.blazebit.persistence.parser.expression.MapEntryExpression) MapValueExpression(com.blazebit.persistence.parser.expression.MapValueExpression) MapKeyExpression(com.blazebit.persistence.parser.expression.MapKeyExpression) ArrayExpression(com.blazebit.persistence.parser.expression.ArrayExpression)

Example 2 with MapValueExpression

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

the class EqualityCheckingVisitor method visit.

@Override
public Boolean visit(MapValueExpression expression) {
    if (referenceExpression.getClass() != expression.getClass()) {
        return Boolean.TRUE;
    }
    MapValueExpression reference = (MapValueExpression) referenceExpression;
    referenceExpression = reference.getPath();
    return expression.getPath().accept(this);
}
Also used : MapValueExpression(com.blazebit.persistence.parser.expression.MapValueExpression)

Example 3 with MapValueExpression

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

the class SelectManager method collectFetchOwners.

public Set<JoinNode> collectFetchOwners() {
    Set<JoinNode> fetchOwners = new HashSet<>();
    List<SelectInfo> infos = selectInfos;
    int size = selectInfos.size();
    for (int i = 0; i < size; i++) {
        final SelectInfo selectInfo = infos.get(i);
        Expression expression = selectInfo.getExpression();
        // Map key and values are just qualified path expressions
        if (expression instanceof MapValueExpression) {
            expression = ((MapValueExpression) expression).getPath();
        } else if (expression instanceof MapKeyExpression) {
            expression = ((MapKeyExpression) expression).getPath();
        }
        // We only look for entity selects and those can only be path expressions
        if (expression instanceof PathExpression) {
            PathExpression pathExpression = (PathExpression) expression;
            JoinNode node = (JoinNode) pathExpression.getBaseNode();
            if (pathExpression.getField() == null) {
                fetchOwners.add(node);
            }
        }
    }
    if (size == 0) {
        fetchOwners.add(joinManager.getRootNodeOrFail("Empty select not allowed when having multiple roots!"));
    }
    return fetchOwners;
}
Also used : MapValueExpression(com.blazebit.persistence.parser.expression.MapValueExpression) MapKeyExpression(com.blazebit.persistence.parser.expression.MapKeyExpression) PathExpression(com.blazebit.persistence.parser.expression.PathExpression) Expression(com.blazebit.persistence.parser.expression.Expression) ParameterExpression(com.blazebit.persistence.parser.expression.ParameterExpression) PathExpression(com.blazebit.persistence.parser.expression.PathExpression) PropertyExpression(com.blazebit.persistence.parser.expression.PropertyExpression) FunctionExpression(com.blazebit.persistence.parser.expression.FunctionExpression) SubqueryExpression(com.blazebit.persistence.parser.expression.SubqueryExpression) PathElementExpression(com.blazebit.persistence.parser.expression.PathElementExpression) MapValueExpression(com.blazebit.persistence.parser.expression.MapValueExpression) MapKeyExpression(com.blazebit.persistence.parser.expression.MapKeyExpression) HashSet(java.util.HashSet) LinkedHashSet(java.util.LinkedHashSet)

Example 4 with MapValueExpression

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

the class JoinManager method implicitJoin.

@SuppressWarnings("checkstyle:methodlength")
public void implicitJoin(Expression expression, boolean joinAllowed, boolean singularJoinAllowed, boolean objectLeafAllowed, String targetTypeName, ClauseType fromClause, JoinType joinType, JoinNode currentJoinNode, Set<String> currentlyResolvingAliases, boolean fromSubquery, boolean fromSelectAlias, boolean joinRequired, boolean idRemovable, boolean fetch, boolean reuseExisting) {
    PathExpression pathExpression;
    if (expression instanceof PathExpression) {
        pathExpression = (PathExpression) expression;
        List<PathElementExpression> pathElements = pathExpression.getExpressions();
        int pathElementSize = pathElements.size();
        PathElementExpression elementExpr = pathElements.get(pathElements.size() - 1);
        int singleValuedAssociationNameStartIndex = -1;
        int singleValuedAssociationNameEndIndex = -1;
        JoinNode current = null;
        List<String> resultFields = new ArrayList<>();
        JoinResult currentResult;
        JoinNode possibleRoot;
        int startIndex = 0;
        Expression aliasedExpression;
        String alias;
        // If joinable select alias, it is guaranteed to have only a single element
        if (pathExpression.getExpressions().size() == 1 && currentlyResolvingAliases != null && !currentlyResolvingAliases.contains(alias = pathExpression.toString()) && (aliasedExpression = getJoinableSelectAlias(pathExpression, fromClause == ClauseType.SELECT, fromSubquery)) != null) {
            // this check is necessary to prevent infinite recursion in the case of e.g. SELECT name AS name
            if (!fromSelectAlias) {
                try {
                    currentlyResolvingAliases.add(alias);
                    // we have to do this implicit join because we might have to adjust the selectOnly flag in the referenced join nodes
                    implicitJoin(aliasedExpression, joinAllowed, singularJoinAllowed, true, null, fromClause, currentlyResolvingAliases, fromSubquery, true, joinRequired, false);
                } finally {
                    currentlyResolvingAliases.remove(alias);
                }
            }
            return;
        } else if (isExternal(pathExpression)) {
            // try to correlate the path expression and use the correlation alias here instead
            String correlatedAlias = addRoot(null, pathExpression, null, false, true);
            if (correlatedAlias != null) {
                pathElements.clear();
                pathElements.addAll(expressionFactory.createPathExpression(correlatedAlias).getExpressions());
                pathElementSize = pathElements.size();
                elementExpr = pathExpression.getExpressions().get(pathExpression.getExpressions().size() - 1);
            }
            PathElementExpression firstElement = pathElements.get(0);
            if (firstElement instanceof PropertyExpression) {
                AliasInfo aliasInfo = aliasManager.getAliasInfo(((PropertyExpression) firstElement).getProperty());
                if (pathElements.size() == 1) {
                    JoinManager manager;
                    if (aliasInfo.getAliasOwner() == aliasManager) {
                        manager = this;
                    } else {
                        manager = parent;
                    }
                    manager.implicitJoin(pathExpression, true, true, true, targetTypeName, fromClause, currentlyResolvingAliases, true, fromSelectAlias, joinRequired, false);
                    return;
                } else {
                    current = ((JoinAliasInfo) aliasInfo).getJoinNode();
                    startIndex = 1;
                }
            } else if (firstElement instanceof TreatExpression) {
                current = implicitJoinTreatExpression((TreatExpression) firstElement, true, true, fromClause, JoinType.LEFT, null, currentlyResolvingAliases, fromSubquery, fromSelectAlias, true, false, false, true);
                startIndex = 1;
                if (pathElements.size() == 1) {
                    return;
                }
            } else {
                throw new IllegalArgumentException("Unsupported correlation with expression: " + pathExpression);
            }
        }
        // Skip root speculation if this is just a single element path
        if (current == null && pathElements.size() > 1 && (possibleRoot = getRootNode(pathElements.get(0))) != null) {
            startIndex = 1;
            current = possibleRoot;
        }
        if (pathElements.size() > startIndex + 1) {
            currentResult = implicitJoin(current, pathExpression, fromClause, joinType, currentJoinNode, currentlyResolvingAliases, startIndex, pathElements.size() - 1, false, joinAllowed, singularJoinAllowed, idRemovable);
            current = currentResult.baseNode;
            resultFields = currentResult.addToList(resultFields);
            // It can never be a single valued association id reference if the join type is INNER i.e. it is required
            singleValuedAssociationNameStartIndex = currentResult.singleValuedAssociationNameIndex;
            singleValuedAssociationNameEndIndex = currentResult.singleValuedAssociationNameEndIndex;
            if (singleValuedAssociationNameStartIndex != -1) {
                if (!mainQuery.jpaProvider.supportsSingleValuedAssociationIdExpressions()) {
                    if (idRemovable) {
                        // remove the id part only if we come from a predicate
                        elementExpr = null;
                        if (current == null) {
                            // This is the case when we use a join alias like "alias.id"
                            // We need to resolve the base since it might not be the root node
                            AliasInfo a = aliasManager.getAliasInfo(pathElements.get(currentResult.singleValuedAssociationNameIndex).toString());
                            // We know this can only be a join node alias
                            current = ((JoinAliasInfo) a).getJoinNode();
                            resultFields = Collections.emptyList();
                        }
                    } else {
                        // Need a normal join
                        elementExpr = null;
                        resultFields.clear();
                        currentResult = implicitJoin(current, resultFields, pathExpression, fromClause, joinType, currentJoinNode, currentlyResolvingAliases, currentResult.singleValuedAssociationNameIndex, pathElements.size(), false, joinAllowed, singularJoinAllowed, idRemovable);
                        current = currentResult.baseNode;
                        resultFields = currentResult.addToList(resultFields);
                        singleValuedAssociationNameStartIndex = -1;
                    }
                }
            }
        } else {
            // Single element expression like "alias", "relation", "property" or "alias.relation"
            currentResult = implicitJoin(current, pathExpression, fromClause, joinType, currentJoinNode, currentlyResolvingAliases, startIndex, pathElements.size() - 1, false, joinAllowed, singularJoinAllowed, idRemovable);
            current = currentResult.baseNode;
            resultFields = currentResult.addToList(resultFields);
            if (idRemovable) {
                if (current != null) {
                    // If there is a "base node" i.e. a current, the expression has 2 elements
                    if (isSingleValuedAssociationId(current.getNodeType(), elementExpr)) {
                        // We remove the "id" part
                        elementExpr = null;
                        // Treat it like a single valued association id expression
                        singleValuedAssociationNameStartIndex = singleValuedAssociationNameEndIndex = startIndex - 1;
                    }
                } else {
                    // There is no base node, this is a expression with 1 element
                    // Either relative or a direct alias
                    String elementExpressionString;
                    if (elementExpr instanceof ArrayExpression) {
                        elementExpressionString = ((ArrayExpression) elementExpr).getBase().toString();
                    } else {
                        elementExpressionString = elementExpr.toString();
                    }
                    AliasInfo a = aliasManager.getAliasInfo(elementExpressionString);
                    if (a == null) {
                        // If the element expression is an alias, there is nothing to replace
                        current = getRootNodeOrFail("Could not join path [", expression, "] because it did not use an absolute path but multiple root nodes are available!");
                        if (isSingleValuedAssociationId(current.getNodeType(), elementExpr)) {
                            // We replace the "id" part with the alias
                            elementExpr = new PropertyExpression(current.getAlias());
                        }
                    }
                }
            }
        }
        JoinResult result;
        AliasInfo aliasInfo;
        // The case of a simple join alias usage
        if (pathElements.size() == 1 && !fromSelectAlias && currentlyResolvingAliases != null && !currentlyResolvingAliases.contains(alias = elementExpr.toString()) && (aliasInfo = aliasManager.getAliasInfoForBottomLevel(alias)) != null) {
            // No need to assert the resultFields here since they can't appear anyways if we enter this branch
            if (aliasInfo instanceof SelectInfo) {
                if (targetTypeName != null) {
                    throw new IllegalArgumentException("The select alias '" + aliasInfo.getAlias() + "' can not be used for a treat expression!.");
                }
                // We actually allow usage of select aliases in expressions, but JPA doesn't, so we have to resolve them here
                Expression selectExpr = ((SelectInfo) aliasInfo).getExpression();
                if (!(selectExpr instanceof PathExpression)) {
                    throw new RuntimeException("The select expression '" + selectExpr.toString() + "' is not a simple path expression! No idea how to implicit join that.");
                }
                // join the expression behind a select alias once when it is encountered the first time
                if (((PathExpression) selectExpr).getBaseNode() == null) {
                    implicitJoin(selectExpr, joinAllowed, singularJoinAllowed, objectLeafAllowed, null, fromClause, currentlyResolvingAliases, fromSubquery, true, joinRequired, false);
                }
                PathExpression selectPathExpr = (PathExpression) selectExpr;
                PathReference reference = selectPathExpr.getPathReference();
                result = new JoinResult((JoinNode) selectPathExpr.getBaseNode(), Arrays.asList(selectPathExpr.getField()), reference.getType(), -1, -1);
            } else {
                JoinNode pathJoinNode = ((JoinAliasInfo) aliasInfo).getJoinNode();
                if (targetTypeName != null) {
                    // Treated root path
                    ManagedType<?> targetType = metamodel.managedType(targetTypeName);
                    result = new JoinResult(pathJoinNode);
                } else {
                    // Naked join alias usage like in "KEY(joinAlias)"
                    result = new JoinResult(pathJoinNode);
                }
            }
        } else if (pathElements.size() == 1 && elementExpr instanceof QualifiedExpression) {
            QualifiedExpression qualifiedExpression = (QualifiedExpression) elementExpr;
            JoinNode baseNode;
            if (elementExpr instanceof MapKeyExpression) {
                baseNode = joinMapKey((MapKeyExpression) elementExpr, null, fromClause, currentlyResolvingAliases, fromSubquery, fromSelectAlias, true, fetch, true, true);
            } else if (elementExpr instanceof ListIndexExpression) {
                baseNode = joinListIndex((ListIndexExpression) elementExpr, null, fromClause, currentlyResolvingAliases, fromSubquery, fromSelectAlias, true, fetch, true, true);
            } else if (elementExpr instanceof MapEntryExpression) {
                baseNode = joinMapEntry((MapEntryExpression) elementExpr, null, fromClause, currentlyResolvingAliases, fromSubquery, fromSelectAlias, true, fetch, true, true);
            } else if (elementExpr instanceof MapValueExpression) {
                implicitJoin(qualifiedExpression.getPath(), true, singularJoinAllowed, objectLeafAllowed, targetTypeName, fromClause, joinType, null, currentlyResolvingAliases, fromSubquery, fromSelectAlias, joinRequired, false, fetch, false);
                baseNode = (JoinNode) qualifiedExpression.getPath().getBaseNode();
            } else {
                throw new IllegalArgumentException("Unknown qualified expression type: " + elementExpr);
            }
            result = new JoinResult(baseNode);
        } else {
            if (singleValuedAssociationNameStartIndex != -1) {
                String associationName = new PathExpression(pathElements.subList(singleValuedAssociationNameStartIndex, singleValuedAssociationNameEndIndex + 1)).toString();
                AliasInfo singleValuedAssociationRootAliasInfo = null;
                JoinTreeNode treeNode;
                // } else
                if (pathElements.size() == 2) {
                    // If this path is composed of only two elements, the association name could represent an alias
                    singleValuedAssociationRootAliasInfo = aliasManager.getAliasInfoForBottomLevel(associationName);
                }
                if (singleValuedAssociationRootAliasInfo != null) {
                    JoinNode singleValuedAssociationRoot = ((JoinAliasInfo) singleValuedAssociationRootAliasInfo).getJoinNode();
                    if (elementExpr != null) {
                        AttributeHolder attributeHolder = JpaUtils.getAttributeForJoining(metamodel, singleValuedAssociationRoot.getNodeType(), elementExpr, null);
                        Type<?> type = attributeHolder.getAttributeType();
                        result = new JoinResult(singleValuedAssociationRoot, Arrays.asList(elementExpr.toString()), type, -1, -1);
                    } else {
                        result = new JoinResult(singleValuedAssociationRoot);
                    }
                } else {
                    if (current == null) {
                        current = getRootNodeOrFail("Could not join path [", expression, "] because it did not use an absolute path but multiple root nodes are available!");
                    }
                    treeNode = current.getNodes().get(associationName);
                    if (reuseExisting && treeNode != null && treeNode.getDefaultNode() != null) {
                        if (elementExpr != null) {
                            Expression restExpression = new PathExpression(pathElements.subList(singleValuedAssociationNameEndIndex + 1, pathElementSize));
                            String elementString = restExpression.toString();
                            AttributeHolder attributeHolder = JpaUtils.getAttributeForJoining(metamodel, treeNode.getDefaultNode().getNodeType(), restExpression, null);
                            Type<?> type = attributeHolder.getAttributeType();
                            result = new JoinResult(treeNode.getDefaultNode(), Arrays.asList(elementString), type, -1, -1);
                        } else {
                            result = new JoinResult(treeNode.getDefaultNode());
                        }
                    } else {
                        if (elementExpr != null) {
                            Expression restExpression = new PathExpression(pathElements.subList(singleValuedAssociationNameStartIndex, pathElementSize));
                            String elementString = restExpression.toString();
                            AttributeHolder attributeHolder = JpaUtils.getAttributeForJoining(metamodel, currentResult.baseNode.getNodeType(), restExpression, null);
                            Type<?> type = attributeHolder.getAttributeType();
                            result = new JoinResult(currentResult.baseNode, Arrays.asList(elementString), type, -1, -1);
                        } else if (metamodel.getManagedType(ExtendedManagedType.class, current.getManagedType()).getAttributes().get(associationName) != null) {
                            Expression resultExpr = new PathExpression(new PropertyExpression(associationName));
                            AttributeHolder attributeHolder = JpaUtils.getAttributeForJoining(metamodel, current.getNodeType(), resultExpr, null);
                            Type<?> type = attributeHolder.getAttributeType();
                            result = new JoinResult(current, Arrays.asList(associationName), type, -1, -1);
                        } else {
                            result = new JoinResult(current);
                        }
                    }
                }
            } else if (elementExpr instanceof ArrayExpression) {
                // Element collection case
                ArrayExpression arrayExpr = (ArrayExpression) elementExpr;
                if (arrayExpr.getBase() instanceof PropertyExpression) {
                    if (current == null) {
                        current = getRootNodeOrFail("Could not join path [", expression, "] because it did not use an absolute path but multiple root nodes are available!");
                    }
                }
                String joinRelationName = arrayExpr.getBase().toString();
                implicitJoinIndex(arrayExpr);
                // Find a node by a predicate match
                JoinNode matchingNode;
                if (pathElements.size() == 1 && (aliasInfo = aliasManager.getAliasInfoForBottomLevel(joinRelationName)) != null) {
                    // The first node is allowed to be a join alias
                    if (aliasInfo instanceof SelectInfo) {
                        throw new IllegalArgumentException("Illegal reference to the select alias '" + joinRelationName + "'");
                    }
                    current = ((JoinAliasInfo) aliasInfo).getJoinNode();
                    generateAndApplyOnPredicate(current, arrayExpr);
                } else if ((matchingNode = findNode(current, joinRelationName, arrayExpr)) != null) {
                    // We found a join node for the same join relation with the same array expression predicate
                    current = matchingNode;
                } else {
                    String joinAlias = getJoinAlias(arrayExpr);
                    if (arrayExpr.getBase() instanceof PropertyExpression) {
                        resultFields.add(joinRelationName);
                        currentResult = createOrUpdateNode(current, resultFields, targetTypeName, joinAlias, joinType, currentJoinNode, true, false, joinAllowed, singularJoinAllowed);
                    } else {
                        joinAlias = aliasManager.generateJoinAlias(joinAlias);
                        Class<?> entityClass = ((EntityLiteral) arrayExpr.getBase()).getValue();
                        joinOn(null, rootNodes.get(0).getAlias(), entityClass, joinAlias, JoinType.LEFT, false).end();
                        currentResult = new JoinResult(((JoinAliasInfo) aliasManager.getAliasInfo(joinAlias)).getJoinNode());
                    }
                    current = currentResult.baseNode;
                    // TODO: Not sure if necessary
                    if (currentResult.hasField()) {
                        throw new IllegalArgumentException("The join path [" + pathExpression + "] has a non joinable part [" + currentResult.joinFields() + "]");
                    }
                    generateAndApplyOnPredicate(current, arrayExpr);
                }
                result = new JoinResult(current);
            } else if (!pathExpression.isUsedInCollectionFunction()) {
                if (current == null) {
                    current = getRootNodeOrFail("Could not join path [", expression, "] because it did not use an absolute path but multiple root nodes are available!");
                }
                if (resultFields.isEmpty()) {
                    result = implicitJoinSingle(current, elementExpr.toString(), targetTypeName, joinType, currentJoinNode, objectLeafAllowed, joinRequired, joinAllowed, singularJoinAllowed);
                } else {
                    resultFields.add(elementExpr.toString());
                    String attributeName = StringUtils.join(".", resultFields);
                    // Validates and gets the path type
                    getPathType(current.getNodeType(), attributeName, pathExpression);
                    result = implicitJoinSingle(current, attributeName, targetTypeName, joinType, currentJoinNode, objectLeafAllowed, joinRequired, joinAllowed, singularJoinAllowed);
                }
            } else {
                if (current == null) {
                    current = getRootNodeOrFail("Could not join path [", expression, "] because it did not use an absolute path but multiple root nodes are available!");
                }
                if (resultFields.isEmpty()) {
                    String attributeName = elementExpr.toString();
                    Type<?> type = getPathType(current.getNodeType(), attributeName, pathExpression);
                    result = new JoinResult(current, Arrays.asList(attributeName), type, -1, -1);
                } else {
                    resultFields.add(elementExpr.toString());
                    String attributeName = StringUtils.join(".", resultFields);
                    Type<?> type = getPathType(current.getNodeType(), attributeName, pathExpression);
                    result = new JoinResult(current, resultFields, type, -1, -1);
                }
            }
        }
        if (fetch) {
            fetchPath(result.baseNode);
        }
        // Don't forget to update the clause dependencies, but only for normal attribute accesses, that way paginated queries can prevent joins in certain cases
        if (fromClause != null) {
            try {
                result.baseNode.updateClauseDependencies(fromClause, new LinkedHashSet<JoinNode>());
            } catch (IllegalStateException ex) {
                throw new IllegalArgumentException("Implicit join in expression '" + expression + "' introduces cyclic join dependency!", ex);
            }
        }
        if (result.isLazy()) {
            pathExpression.setPathReference(new LazyPathReference(result.baseNode, result.joinFields(), result.type, joinAllowed));
        } else {
            pathExpression.setPathReference(new SimplePathReference(result.baseNode, result.joinFields(), result.type));
        }
    } else if (expression instanceof FunctionExpression) {
        FunctionExpression functionExpression = (FunctionExpression) expression;
        List<Expression> expressions = functionExpression.getExpressions();
        int size = expressions.size();
        for (int i = 0; i < size; i++) {
            implicitJoin(expressions.get(i), joinAllowed, singularJoinAllowed, objectLeafAllowed, null, fromClause, currentlyResolvingAliases, fromSubquery, fromSelectAlias, joinRequired, false);
        }
        List<OrderByItem> withinGroup = functionExpression.getWithinGroup();
        if (withinGroup != null) {
            size = withinGroup.size();
            for (int i = 0; i < size; i++) {
                implicitJoin(withinGroup.get(i).getExpression(), joinAllowed, singularJoinAllowed, objectLeafAllowed, null, fromClause, currentlyResolvingAliases, fromSubquery, fromSelectAlias, joinRequired, false);
            }
        }
    } else if (expression instanceof MapKeyExpression) {
        MapKeyExpression mapKeyExpression = (MapKeyExpression) expression;
        joinMapKey(mapKeyExpression, null, fromClause, currentlyResolvingAliases, fromSubquery, fromSelectAlias, joinRequired, fetch, true, true);
    } else if (expression instanceof QualifiedExpression) {
        implicitJoin(((QualifiedExpression) expression).getPath(), joinAllowed, singularJoinAllowed, objectLeafAllowed, null, fromClause, currentlyResolvingAliases, fromSubquery, fromSelectAlias, joinRequired, false);
    } else if (expression instanceof ArrayExpression || expression instanceof GeneralCaseExpression || expression instanceof TreatExpression) {
        // NOTE: I haven't found a use case for this yet, so I'd like to throw an exception instead of silently not supporting this
        throw new IllegalArgumentException("Unsupported expression for implicit joining found: " + expression);
    } else {
    // Other expressions don't need handling
    }
}
Also used : TreatExpression(com.blazebit.persistence.parser.expression.TreatExpression) PathReference(com.blazebit.persistence.parser.expression.PathReference) MapValueExpression(com.blazebit.persistence.parser.expression.MapValueExpression) ArrayList(java.util.ArrayList) MapKeyExpression(com.blazebit.persistence.parser.expression.MapKeyExpression) MapEntryExpression(com.blazebit.persistence.parser.expression.MapEntryExpression) FunctionExpression(com.blazebit.persistence.parser.expression.FunctionExpression) PathExpression(com.blazebit.persistence.parser.expression.PathExpression) ListIndexExpression(com.blazebit.persistence.parser.expression.ListIndexExpression) PropertyExpression(com.blazebit.persistence.parser.expression.PropertyExpression) List(java.util.List) ArrayList(java.util.ArrayList) GeneralCaseExpression(com.blazebit.persistence.parser.expression.GeneralCaseExpression) QualifiedExpression(com.blazebit.persistence.parser.expression.QualifiedExpression) ExtendedManagedType(com.blazebit.persistence.spi.ExtendedManagedType) PathElementExpression(com.blazebit.persistence.parser.expression.PathElementExpression) IdentifiableType(javax.persistence.metamodel.IdentifiableType) JoinType(com.blazebit.persistence.JoinType) ManagedType(javax.persistence.metamodel.ManagedType) EntityType(javax.persistence.metamodel.EntityType) Type(javax.persistence.metamodel.Type) ExtendedManagedType(com.blazebit.persistence.spi.ExtendedManagedType) EmbeddableType(javax.persistence.metamodel.EmbeddableType) ListIndexExpression(com.blazebit.persistence.parser.expression.ListIndexExpression) Expression(com.blazebit.persistence.parser.expression.Expression) QualifiedExpression(com.blazebit.persistence.parser.expression.QualifiedExpression) TreatExpression(com.blazebit.persistence.parser.expression.TreatExpression) ParameterExpression(com.blazebit.persistence.parser.expression.ParameterExpression) PathExpression(com.blazebit.persistence.parser.expression.PathExpression) 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) MapEntryExpression(com.blazebit.persistence.parser.expression.MapEntryExpression) MapValueExpression(com.blazebit.persistence.parser.expression.MapValueExpression) MapKeyExpression(com.blazebit.persistence.parser.expression.MapKeyExpression) ArrayExpression(com.blazebit.persistence.parser.expression.ArrayExpression)

Example 5 with MapValueExpression

use of com.blazebit.persistence.parser.expression.MapValueExpression 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

MapValueExpression (com.blazebit.persistence.parser.expression.MapValueExpression)5 FunctionExpression (com.blazebit.persistence.parser.expression.FunctionExpression)4 MapKeyExpression (com.blazebit.persistence.parser.expression.MapKeyExpression)4 ParameterExpression (com.blazebit.persistence.parser.expression.ParameterExpression)4 PathExpression (com.blazebit.persistence.parser.expression.PathExpression)4 PropertyExpression (com.blazebit.persistence.parser.expression.PropertyExpression)4 SubqueryExpression (com.blazebit.persistence.parser.expression.SubqueryExpression)4 Expression (com.blazebit.persistence.parser.expression.Expression)3 ListIndexExpression (com.blazebit.persistence.parser.expression.ListIndexExpression)3 MapEntryExpression (com.blazebit.persistence.parser.expression.MapEntryExpression)3 PathElementExpression (com.blazebit.persistence.parser.expression.PathElementExpression)3 TreatExpression (com.blazebit.persistence.parser.expression.TreatExpression)3 ArrayExpression (com.blazebit.persistence.parser.expression.ArrayExpression)2 GeneralCaseExpression (com.blazebit.persistence.parser.expression.GeneralCaseExpression)2 QualifiedExpression (com.blazebit.persistence.parser.expression.QualifiedExpression)2 ExtendedManagedType (com.blazebit.persistence.spi.ExtendedManagedType)2 ArrayList (java.util.ArrayList)2 EntityType (javax.persistence.metamodel.EntityType)2 ManagedType (javax.persistence.metamodel.ManagedType)2 JoinType (com.blazebit.persistence.JoinType)1