All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.blazebit.persistence.impl.ResolvingQueryGenerator Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2014 Blazebit.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.blazebit.persistence.impl;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;

import com.blazebit.persistence.BaseFinalSetOperationBuilder;
import com.blazebit.persistence.impl.expression.*;
import com.blazebit.persistence.spi.JpaProvider;
import com.blazebit.persistence.spi.OrderByElement;

/**
 *
 * @author Moritz Becker
 * @author Christian Beikov
 * @since 1.0
 */
public class ResolvingQueryGenerator extends SimpleQueryGenerator {

    private boolean resolveSelectAliases = true;
    private final AliasManager aliasManager;
    private final JpaProvider jpaProvider;
    protected String aliasPrefix;
    private final Set registeredFunctions;

    public ResolvingQueryGenerator(AliasManager aliasManager, JpaProvider jpaProvider, Set registeredFunctions) {
        this.aliasManager = aliasManager;
        this.jpaProvider = jpaProvider;
        this.registeredFunctions = registeredFunctions;
    }

    @Override
    public void visit(NullExpression expression) {
        sb.append(jpaProvider.getNullExpression());
    }

    @Override
    public void visit(FunctionExpression expression) {
        if (com.blazebit.persistence.impl.util.ExpressionUtils.isOuterFunction(expression)) {
            expression.getExpressions().get(0).accept(this);
        } else if (ExpressionUtils.isFunctionFunctionExpression(expression)) {
            String functionName = ExpressionUtils.unwrapStringLiteral(expression.getExpressions().get(0).toString());
            renderFunctionFunction(functionName, expression.getExpressions());
        } else if (isCountStarFunction(expression)) {
            renderCountStar();
        } else if (com.blazebit.persistence.impl.util.ExpressionUtils.isValueFunction(expression)) {
            // NOTE: Hibernate uses the column from a join table if VALUE is used which is wrong, so drop the VALUE here
            String valueFunction = jpaProvider.getCollectionValueFunction();
            if (valueFunction != null) {
                sb.append(valueFunction);
                sb.append('(');
                expression.getExpressions().get(0).accept(this);
                sb.append(')');
            } else {
                expression.getExpressions().get(0).accept(this);
            }
        } else {
            super.visit(expression);
        }
    }

    @SuppressWarnings("unchecked")
    protected void renderCountStar() {
        if (jpaProvider.supportsCountStar()) {
            sb.append("COUNT(*)");
        } else {
            renderFunctionFunction("COUNT_STAR", (List) (List) Collections.emptyList());
        }
    }

    @Override
	public void visit(SubqueryExpression expression) {
        sb.append('(');

        if (expression.getSubquery() instanceof SubqueryInternalBuilder) {
            final SubqueryInternalBuilder subquery = (SubqueryInternalBuilder) expression.getSubquery();
            final boolean hasFirstResult = subquery.getFirstResult() != 0;
            final boolean hasMaxResults = subquery.getMaxResults() != Integer.MAX_VALUE;
            final boolean hasLimit = hasFirstResult || hasMaxResults;
            final boolean hasSetOperations = subquery instanceof BaseFinalSetOperationBuilder;
            final boolean isSimple = !hasLimit && !hasSetOperations;

            if (isSimple) {
                sb.append(subquery.getQueryString());
            } else if (hasSetOperations) {
                asExpression((AbstractCommonQueryBuilder) subquery).accept(this);
            } else {
                List arguments = new ArrayList(3);
                arguments.add(new StringLiteral("LIMIT"));
                arguments.add(asExpression((AbstractCommonQueryBuilder) subquery));

                if (!hasMaxResults) {
                    throw new IllegalArgumentException("First result without max results is not supported!");
                } else {
                    arguments.add(new NumericLiteral(Integer.toString(subquery.getMaxResults()), NumericType.INTEGER));
                }

                if (hasFirstResult) {
                    arguments.add(new NumericLiteral(Integer.toString(subquery.getFirstResult()), NumericType.INTEGER));
                }

                renderFunctionFunction("LIMIT", arguments);
            }
        } else {
            sb.append(expression.getSubquery().getQueryString());
        }
        
        sb.append(')');
	}
    
    protected Expression asExpression(AbstractCommonQueryBuilder queryBuilder) {
        if (queryBuilder instanceof BaseFinalSetOperationBuilderImpl) {
            BaseFinalSetOperationBuilderImpl operationBuilder = (BaseFinalSetOperationBuilderImpl) queryBuilder;
            SetOperationManager operationManager = operationBuilder.setOperationManager;
            
            if (operationManager.getOperator() == null || !operationManager.hasSetOperations()) {
                return asExpression(operationManager.getStartQueryBuilder());
            }
            
            List setOperationArgs = new ArrayList(operationManager.getSetOperations().size() + 2);
            StringBuilder nameSb = new StringBuilder();
            // Use prefix because hibernate uses UNION as keyword
            nameSb.append("SET_");
            nameSb.append(operationManager.getOperator().name());
            setOperationArgs.add(new StringLiteral(nameSb.toString()));
            setOperationArgs.add(asExpression(operationManager.getStartQueryBuilder()));

            List> setOperands = operationManager.getSetOperations();
            int operandsSize = setOperands.size();
            for (int i = 0; i < operandsSize; i++) {
                setOperationArgs.add(asExpression(setOperands.get(i)));
            }
            
            List orderByElements = operationBuilder.getOrderByElements();
            if (orderByElements.size() > 0) {
                setOperationArgs.add(new StringLiteral("ORDER_BY"));
                
                int orderByElementsSize = orderByElements.size();
                for (int i = 0; i < orderByElementsSize; i++) {
                    StringBuilder argSb = new StringBuilder(20);
                    argSb.append(orderByElements.get(i).toString());
                    setOperationArgs.add(new StringLiteral(argSb.toString()));
                }
            }
            
            if (operationBuilder.hasLimit()) {
                if (operationBuilder.maxResults != Integer.MAX_VALUE) {
                    setOperationArgs.add(new StringLiteral("LIMIT"));
                    setOperationArgs.add(new NumericLiteral(Integer.toString(operationBuilder.maxResults), NumericType.INTEGER));
                }
                if (operationBuilder.firstResult != 0) {
                    setOperationArgs.add(new StringLiteral("OFFSET"));
                    setOperationArgs.add(new NumericLiteral(Integer.toString(operationBuilder.firstResult), NumericType.INTEGER));
                }
            }
            
            Expression functionExpr = new FunctionExpression("FUNCTION", setOperationArgs);
            return functionExpr;
        }

        String queryString = queryBuilder.getQueryString();
        final StringBuilder subquerySb = new StringBuilder(queryString.length() + 2);
        subquerySb.append(queryString);
        return new SubqueryExpression(new Subquery() {
            @Override
            public String getQueryString() {
                return subquerySb.toString();
            }
        });
    }

	protected void renderFunctionFunction(String functionName, List arguments) {
        if (registeredFunctions.contains(functionName.toLowerCase())) {
            sb.append(jpaProvider.getCustomFunctionInvocation(functionName, arguments.size()));
            if (arguments.size() > 1) {
                arguments.get(1).accept(this);
                for (int i = 2; i < arguments.size(); i++) {
                    sb.append(",");
                    arguments.get(i).accept(this);
                }
            }
            sb.append(')');
        } else if (jpaProvider.supportsJpa21()) {
            // Add the JPA 2.1 Function style function
            sb.append("FUNCTION('");
            sb.append(functionName);
            sb.append('\'');

            for (int i = 1; i < arguments.size(); i++) {
                sb.append(',');
                arguments.get(i).accept(this);
            }

            sb.append(')');
        } else {
            throw new IllegalArgumentException("Unknown function [" + functionName + "] is used!");
        }
    }

    private boolean isCountStarFunction(FunctionExpression expression) {
        return expression instanceof AggregateExpression && expression.getExpressions().isEmpty()
            && "COUNT".equalsIgnoreCase(expression.getFunctionName());
    }

    @Override
    public void visit(TreatExpression expression) {
        if (jpaProvider.supportsRootTreat()) {
            super.visit(expression);
        } else if (jpaProvider.supportsSubtypePropertyResolving()) {
            // NOTE: this might be wrong when having multiple same named properties
            expression.getExpression().accept(this);
        } else {
            throw new IllegalArgumentException("Can not render treat expression[" + expression.toString() + "] as the JPA provider does not support it!");
        }
    }

    @Override
    public void visit(PathExpression expression) {
        if (resolveSelectAliases) {
            // if path expression should not be replaced by select aliases we
            // check for select aliases that have to be replaced with the corresponding
            // path expressions
            if (expression.getBaseNode() == null) {
                AliasInfo aliasInfo;
                if ((aliasInfo = aliasManager.getAliasInfo(expression.toString())) != null) {
                    if (aliasInfo instanceof SelectInfo) {
                        SelectInfo selectAliasInfo = (SelectInfo) aliasInfo;
                        if (((SelectInfo) aliasInfo).getExpression() instanceof PathExpression) {
                            selectAliasInfo.getExpression().accept(this);
                            return;
                        }
                    }
                }
            }
        }
        if (expression.getBaseNode() == null) {
            super.visit(expression);
        } else if (expression.getField() == null) {
            boolean valueFunction = needsValueFunction(expression) && !expression.isUsedInCollectionFunction()
                && jpaProvider.getCollectionValueFunction() != null;

            if (valueFunction) {
                sb.append(jpaProvider.getCollectionValueFunction());
                sb.append('(');
            }

            if (aliasPrefix != null) {
                sb.append(aliasPrefix);
            }

            JoinNode baseNode = (JoinNode) expression.getBaseNode();
            baseNode.appendAlias(sb, null);

            if (valueFunction) {
                sb.append(')');
            }
        } else {
            // Thats e.g. TREAT(TREAT(alias).property)
            if (expression.hasTreatedSubpath()) {
                // Actually we know that the treated subpath must be the first part of the path
                expression.getExpressions().get(0).accept(this);
                sb.append(".").append(expression.getField());
            } else {
                // Dereferencing after a value function does not seem to work for datanucleus?
                //            boolean valueFunction = false;
                boolean valueFunction = needsValueFunction(expression) && jpaProvider.getCollectionValueFunction() != null;
                JoinNode baseNode = (JoinNode) expression.getBaseNode();

                if (valueFunction) {
                    sb.append(jpaProvider.getCollectionValueFunction());
                    sb.append('(');

                    if (aliasPrefix != null) {
                        sb.append(aliasPrefix);
                    }

                    baseNode.appendAlias(sb, null);
                    sb.append(')');
                    sb.append(".").append(expression.getField());
                } else {
                    if (aliasPrefix != null) {
                        sb.append(aliasPrefix);
                    }

                    baseNode.appendAlias(sb, expression.getField());
                }
            }

        }
    }

    private boolean needsValueFunction(PathExpression expression) {
        JoinNode baseNode = (JoinNode) expression.getBaseNode();
        return !expression.isCollectionKeyPath() && baseNode.getParentTreeNode() != null && baseNode.getParentTreeNode().isMap();
    }

    @Override
    protected String getBooleanConditionalExpression(boolean value) {
        return jpaProvider.getBooleanConditionalExpression(value);
    }

    @Override
    protected String getBooleanExpression(boolean value) {
        return jpaProvider.getBooleanExpression(value);
    }

    @Override
    protected String escapeCharacter(char character) {
        return jpaProvider.escapeCharacter(character);
    }

    @Override
    public void visit(ParameterExpression expression) {
        String paramName;
        if (expression.getName() == null) {
            throw new IllegalStateException("Unsatisfied parameter " + expression.getName());
        } else {
            paramName = expression.getName();
        }
        // Workaround for hibernate
        // TODO: Remove when HHH-7407 is fixed
        boolean needsBrackets = jpaProvider.needsBracketsForListParamter() && expression.isCollectionValued();

        if (needsBrackets) {
            sb.append('(');
        }

        sb.append(":");
        sb.append(paramName);

        if (needsBrackets) {
            sb.append(')');
        }
    }

    public boolean isResolveSelectAliases() {
        return resolveSelectAliases;
    }

    public void setResolveSelectAliases(boolean replaceSelectAliases) {
        this.resolveSelectAliases = replaceSelectAliases;
    }

    public String getAliasPrefix() {
        return aliasPrefix;
    }

    public void setAliasPrefix(String aliasPrefix) {
        this.aliasPrefix = aliasPrefix;
    }

    @Override
    public void visit(ArrayExpression expression) {
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy