Search in sources :

Example 11 with QueryPart

use of org.hibernate.sql.ast.tree.select.QueryPart in project hibernate-orm by hibernate.

the class OracleSqlAstTranslator method emulateFetchOffsetWithWindowFunctions.

@Override
protected void emulateFetchOffsetWithWindowFunctions(QueryPart queryPart, Expression offsetExpression, Expression fetchExpression, FetchClauseType fetchClauseType, boolean emulateFetchClause) {
    if (queryPart instanceof QuerySpec && offsetExpression == null && fetchClauseType == FetchClauseType.ROWS_ONLY) {
        // Special case for Oracle to support locking along with simple max results paging
        final QuerySpec querySpec = (QuerySpec) queryPart;
        withRowNumbering(querySpec, // we need select aliases to avoid ORA-00918: column ambiguously defined
        true, () -> {
            final QueryPart currentQueryPart = getQueryPartStack().getCurrent();
            final boolean needsParenthesis;
            final boolean needsWrapper;
            if (currentQueryPart instanceof QueryGroup) {
                needsParenthesis = false;
                // visitQuerySpec will add the select wrapper
                needsWrapper = !currentQueryPart.hasOffsetOrFetchClause();
            } else {
                needsParenthesis = !querySpec.isRoot();
                needsWrapper = true;
            }
            if (needsWrapper) {
                if (needsParenthesis) {
                    appendSql('(');
                }
                appendSql("select * from ");
                if (!needsParenthesis) {
                    appendSql('(');
                }
            }
            super.visitQuerySpec(querySpec);
            if (needsWrapper) {
                if (!needsParenthesis) {
                    appendSql(')');
                }
            }
            appendSql(" where rownum<=");
            final Stack<Clause> clauseStack = getClauseStack();
            clauseStack.push(Clause.WHERE);
            try {
                fetchExpression.accept(this);
                // We render the FOR UPDATE clause in the outer query
                clauseStack.pop();
                clauseStack.push(Clause.FOR_UPDATE);
                visitForUpdateClause(querySpec);
            } finally {
                clauseStack.pop();
            }
            if (needsWrapper) {
                if (needsParenthesis) {
                    appendSql(')');
                }
            }
        });
    } else {
        super.emulateFetchOffsetWithWindowFunctions(queryPart, offsetExpression, fetchExpression, fetchClauseType, emulateFetchClause);
    }
}
Also used : QueryPart(org.hibernate.sql.ast.tree.select.QueryPart) Clause(org.hibernate.sql.ast.Clause) SelectClause(org.hibernate.sql.ast.tree.select.SelectClause) QuerySpec(org.hibernate.sql.ast.tree.select.QuerySpec) QueryGroup(org.hibernate.sql.ast.tree.select.QueryGroup)

Example 12 with QueryPart

use of org.hibernate.sql.ast.tree.select.QueryPart in project hibernate-orm by hibernate.

the class TableBasedInsertHandler method resolveDelegate.

private ExecutionDelegate resolveDelegate(DomainQueryExecutionContext executionContext) {
    final EntityPersister entityDescriptor = sessionFactory.getRuntimeMetamodels().getMappingMetamodel().getEntityDescriptor(getSqmInsertStatement().getTarget().getEntityName());
    final MultiTableSqmMutationConverter converterDelegate = new MultiTableSqmMutationConverter(entityDescriptor, getSqmInsertStatement(), getSqmInsertStatement().getTarget(), domainParameterXref, executionContext.getQueryOptions(), executionContext.getSession().getLoadQueryInfluencers(), executionContext.getQueryParameterBindings(), sessionFactory);
    final TableGroup insertingTableGroup = converterDelegate.getMutatingTableGroup();
    final Map<SqmParameter<?>, List<List<JdbcParameter>>> parameterResolutions;
    if (domainParameterXref.getSqmParameterCount() == 0) {
        parameterResolutions = Collections.emptyMap();
    } else {
        parameterResolutions = new IdentityHashMap<>();
    }
    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    // visit the insertion target using our special converter, collecting
    // information about the target paths
    final List<Assignment> targetPathColumns = new ArrayList<>();
    final Map<SqmParameter<?>, MappingModelExpressible<?>> paramTypeResolutions = new LinkedHashMap<>();
    final NamedTableReference entityTableReference = new NamedTableReference(entityTable.getTableExpression(), TemporaryTable.DEFAULT_ALIAS, true, sessionFactory);
    final InsertStatement insertStatement = new InsertStatement(entityTableReference);
    final BaseSqmToSqlAstConverter.AdditionalInsertValues additionalInsertValues = converterDelegate.visitInsertionTargetPaths((assigable, columnReferences) -> {
        insertStatement.addTargetColumnReferences(columnReferences);
        targetPathColumns.add(new Assignment(assigable, (Expression) assigable));
    }, sqmInsertStatement, entityDescriptor, insertingTableGroup, (sqmParameter, mappingType, jdbcParameters) -> {
        parameterResolutions.computeIfAbsent(sqmParameter, k -> new ArrayList<>(1)).add(jdbcParameters);
        paramTypeResolutions.put(sqmParameter, mappingType);
    });
    if (sqmInsertStatement instanceof SqmInsertSelectStatement) {
        final QueryPart queryPart = converterDelegate.visitQueryPart(((SqmInsertSelectStatement<?>) sqmInsertStatement).getSelectQueryPart());
        queryPart.visitQuerySpecs(querySpec -> {
            if (additionalInsertValues.applySelections(querySpec, sessionFactory)) {
                final TemporaryTableColumn rowNumberColumn = entityTable.getColumns().get(entityTable.getColumns().size() - 1);
                final ColumnReference columnReference = new ColumnReference((String) null, rowNumberColumn.getColumnName(), false, null, null, rowNumberColumn.getJdbcMapping(), sessionFactory);
                insertStatement.getTargetColumnReferences().set(insertStatement.getTargetColumnReferences().size() - 1, columnReference);
                targetPathColumns.set(targetPathColumns.size() - 1, new Assignment(columnReference, columnReference));
            } else if (entityDescriptor.getIdentifierGenerator() instanceof OptimizableGenerator) {
                final Optimizer optimizer = ((OptimizableGenerator) entityDescriptor.getIdentifierGenerator()).getOptimizer();
                if (optimizer != null && optimizer.getIncrementSize() > 1) {
                    if (!sessionFactory.getJdbcServices().getDialect().supportsWindowFunctions()) {
                        return;
                    }
                    final TemporaryTableColumn rowNumberColumn = entityTable.getColumns().get(entityTable.getColumns().size() - 1);
                    final ColumnReference columnReference = new ColumnReference((String) null, rowNumberColumn.getColumnName(), false, null, null, rowNumberColumn.getJdbcMapping(), sessionFactory);
                    insertStatement.getTargetColumnReferences().add(columnReference);
                    targetPathColumns.add(new Assignment(columnReference, columnReference));
                    final BasicType<Integer> rowNumberType = sessionFactory.getTypeConfiguration().getBasicTypeForJavaType(Integer.class);
                    querySpec.getSelectClause().addSqlSelection(new SqlSelectionImpl(1, 0, new Over<>(new SelfRenderingFunctionSqlAstExpression("row_number", (appender, args, walker) -> appender.appendSql("row_number()"), Collections.emptyList(), rowNumberType, rowNumberType), Collections.emptyList(), Collections.emptyList())));
                }
            }
        });
        insertStatement.setSourceSelectStatement(queryPart);
    } else {
        // Add the row number column if there is one
        final IdentifierGenerator generator = entityDescriptor.getIdentifierGenerator();
        final BasicType<?> rowNumberType;
        if (generator instanceof OptimizableGenerator) {
            final Optimizer optimizer = ((OptimizableGenerator) generator).getOptimizer();
            if (optimizer != null && optimizer.getIncrementSize() > 1) {
                final TemporaryTableColumn rowNumberColumn = entityTable.getColumns().get(entityTable.getColumns().size() - 1);
                rowNumberType = (BasicType<?>) rowNumberColumn.getJdbcMapping();
                final ColumnReference columnReference = new ColumnReference((String) null, rowNumberColumn.getColumnName(), false, null, null, rowNumberColumn.getJdbcMapping(), sessionFactory);
                insertStatement.getTargetColumnReferences().add(columnReference);
                targetPathColumns.add(new Assignment(columnReference, columnReference));
            } else {
                rowNumberType = null;
            }
        } else {
            rowNumberType = null;
        }
        final List<SqmValues> sqmValuesList = ((SqmInsertValuesStatement<?>) sqmInsertStatement).getValuesList();
        final List<Values> valuesList = new ArrayList<>(sqmValuesList.size());
        for (int i = 0; i < sqmValuesList.size(); i++) {
            final Values values = converterDelegate.visitValues(sqmValuesList.get(i));
            additionalInsertValues.applyValues(values);
            if (rowNumberType != null) {
                values.getExpressions().add(new QueryLiteral<>(i + 1, rowNumberType));
            }
            valuesList.add(values);
        }
        insertStatement.setValuesList(valuesList);
    }
    converterDelegate.pruneTableGroupJoins();
    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    // cross-reference the TableReference by alias.  The TableGroup already
    // cross-references it by name, but the ColumnReference only has the alias
    final Map<String, TableReference> tableReferenceByAlias = CollectionHelper.mapOfSize(insertingTableGroup.getTableReferenceJoins().size() + 1);
    collectTableReference(insertingTableGroup.getPrimaryTableReference(), tableReferenceByAlias::put);
    for (int i = 0; i < insertingTableGroup.getTableReferenceJoins().size(); i++) {
        collectTableReference(insertingTableGroup.getTableReferenceJoins().get(i), tableReferenceByAlias::put);
    }
    return new InsertExecutionDelegate(sqmInsertStatement, converterDelegate, entityTable, afterUseAction, sessionUidAccess, domainParameterXref, insertingTableGroup, tableReferenceByAlias, targetPathColumns, insertStatement, parameterResolutions, paramTypeResolutions, executionContext);
}
Also used : EntityPersister(org.hibernate.persister.entity.EntityPersister) CollectionHelper(org.hibernate.internal.util.collections.CollectionHelper) ExecutionContext(org.hibernate.sql.exec.spi.ExecutionContext) EntityPersister(org.hibernate.persister.entity.EntityPersister) BasicType(org.hibernate.type.BasicType) Logger(org.jboss.logging.Logger) ColumnReference(org.hibernate.sql.ast.tree.expression.ColumnReference) SqmInsertValuesStatement(org.hibernate.query.sqm.tree.insert.SqmInsertValuesStatement) OptimizableGenerator(org.hibernate.id.OptimizableGenerator) Function(java.util.function.Function) ArrayList(java.util.ArrayList) SqmJdbcExecutionContextAdapter(org.hibernate.query.sqm.internal.SqmJdbcExecutionContextAdapter) TableReference(org.hibernate.sql.ast.tree.from.TableReference) LinkedHashMap(java.util.LinkedHashMap) NamedTableReference(org.hibernate.sql.ast.tree.from.NamedTableReference) MultiTableSqmMutationConverter(org.hibernate.query.sqm.mutation.internal.MultiTableSqmMutationConverter) Map(java.util.Map) BiConsumer(java.util.function.BiConsumer) TableReferenceJoin(org.hibernate.sql.ast.tree.from.TableReferenceJoin) SessionFactoryImplementor(org.hibernate.engine.spi.SessionFactoryImplementor) SqmInsertSelectStatement(org.hibernate.query.sqm.tree.insert.SqmInsertSelectStatement) IdentifierGenerator(org.hibernate.id.IdentifierGenerator) Values(org.hibernate.sql.ast.tree.insert.Values) Assignment(org.hibernate.sql.ast.tree.update.Assignment) IdentityHashMap(java.util.IdentityHashMap) TemporaryTable(org.hibernate.dialect.temptable.TemporaryTable) SelfRenderingFunctionSqlAstExpression(org.hibernate.query.sqm.function.SelfRenderingFunctionSqlAstExpression) Optimizer(org.hibernate.id.enhanced.Optimizer) Expression(org.hibernate.sql.ast.tree.expression.Expression) QueryLiteral(org.hibernate.sql.ast.tree.expression.QueryLiteral) BaseSqmToSqlAstConverter(org.hibernate.query.sqm.sql.BaseSqmToSqlAstConverter) DomainQueryExecutionContext(org.hibernate.query.spi.DomainQueryExecutionContext) InsertHandler(org.hibernate.query.sqm.mutation.internal.InsertHandler) List(java.util.List) JdbcParameter(org.hibernate.sql.ast.tree.expression.JdbcParameter) SqmInsertStatement(org.hibernate.query.sqm.tree.insert.SqmInsertStatement) QueryPart(org.hibernate.sql.ast.tree.select.QueryPart) Over(org.hibernate.sql.ast.tree.expression.Over) SqlSelectionImpl(org.hibernate.sql.results.internal.SqlSelectionImpl) SqmParameter(org.hibernate.query.sqm.tree.expression.SqmParameter) InsertStatement(org.hibernate.sql.ast.tree.insert.InsertStatement) DomainParameterXref(org.hibernate.query.sqm.internal.DomainParameterXref) Collections(java.util.Collections) TemporaryTableColumn(org.hibernate.dialect.temptable.TemporaryTableColumn) MappingModelExpressible(org.hibernate.metamodel.mapping.MappingModelExpressible) TableGroup(org.hibernate.sql.ast.tree.from.TableGroup) SqmValues(org.hibernate.query.sqm.tree.insert.SqmValues) SharedSessionContractImplementor(org.hibernate.engine.spi.SharedSessionContractImplementor) BasicType(org.hibernate.type.BasicType) MappingModelExpressible(org.hibernate.metamodel.mapping.MappingModelExpressible) QueryPart(org.hibernate.sql.ast.tree.select.QueryPart) OptimizableGenerator(org.hibernate.id.OptimizableGenerator) Optimizer(org.hibernate.id.enhanced.Optimizer) ArrayList(java.util.ArrayList) Values(org.hibernate.sql.ast.tree.insert.Values) SqmValues(org.hibernate.query.sqm.tree.insert.SqmValues) TemporaryTableColumn(org.hibernate.dialect.temptable.TemporaryTableColumn) SqmInsertStatement(org.hibernate.query.sqm.tree.insert.SqmInsertStatement) InsertStatement(org.hibernate.sql.ast.tree.insert.InsertStatement) LinkedHashMap(java.util.LinkedHashMap) Assignment(org.hibernate.sql.ast.tree.update.Assignment) TableReference(org.hibernate.sql.ast.tree.from.TableReference) NamedTableReference(org.hibernate.sql.ast.tree.from.NamedTableReference) SqmValues(org.hibernate.query.sqm.tree.insert.SqmValues) ArrayList(java.util.ArrayList) List(java.util.List) SqmInsertSelectStatement(org.hibernate.query.sqm.tree.insert.SqmInsertSelectStatement) BaseSqmToSqlAstConverter(org.hibernate.query.sqm.sql.BaseSqmToSqlAstConverter) TableGroup(org.hibernate.sql.ast.tree.from.TableGroup) NamedTableReference(org.hibernate.sql.ast.tree.from.NamedTableReference) JdbcParameter(org.hibernate.sql.ast.tree.expression.JdbcParameter) SqmInsertValuesStatement(org.hibernate.query.sqm.tree.insert.SqmInsertValuesStatement) SelfRenderingFunctionSqlAstExpression(org.hibernate.query.sqm.function.SelfRenderingFunctionSqlAstExpression) Expression(org.hibernate.sql.ast.tree.expression.Expression) SelfRenderingFunctionSqlAstExpression(org.hibernate.query.sqm.function.SelfRenderingFunctionSqlAstExpression) SqlSelectionImpl(org.hibernate.sql.results.internal.SqlSelectionImpl) SqmParameter(org.hibernate.query.sqm.tree.expression.SqmParameter) MultiTableSqmMutationConverter(org.hibernate.query.sqm.mutation.internal.MultiTableSqmMutationConverter) ColumnReference(org.hibernate.sql.ast.tree.expression.ColumnReference) IdentifierGenerator(org.hibernate.id.IdentifierGenerator)

Example 13 with QueryPart

use of org.hibernate.sql.ast.tree.select.QueryPart in project hibernate-orm by hibernate.

the class AbstractSqlAstTranslator method emulateQuantifiedTupleSubQueryPredicate.

/**
 * An optimized emulation for relational tuple sub-query comparisons.
 * The idea of this method is to use limit 1 to select the max or min tuple and only compare against that.
 */
protected void emulateQuantifiedTupleSubQueryPredicate(Predicate predicate, QueryPart queryPart, SqlTuple lhsTuple, ComparisonOperator tupleComparisonOperator) {
    final QuerySpec subQuery;
    if (queryPart instanceof QuerySpec && queryPart.getFetchClauseExpression() == null && queryPart.getOffsetClauseExpression() == null) {
        subQuery = (QuerySpec) queryPart;
        // We can only emulate the tuple sub query predicate comparing against the top element when there are no limit/offsets
        lhsTuple.accept(this);
        appendSql(tupleComparisonOperator.sqlText());
        final QueryPart queryPartForRowNumbering = this.queryPartForRowNumbering;
        final int queryPartForRowNumberingClauseDepth = this.queryPartForRowNumberingClauseDepth;
        final boolean needsSelectAliases = this.needsSelectAliases;
        try {
            this.queryPartForRowNumbering = null;
            this.queryPartForRowNumberingClauseDepth = -1;
            this.needsSelectAliases = false;
            queryPartStack.push(subQuery);
            appendSql(OPEN_PARENTHESIS);
            visitSelectClause(subQuery.getSelectClause());
            visitFromClause(subQuery.getFromClause());
            visitWhereClause(subQuery.getWhereClauseRestrictions());
            visitGroupByClause(subQuery, getDialect().getGroupBySelectItemReferenceStrategy());
            visitHavingClause(subQuery);
            appendSql(" order by ");
            final List<SqlSelection> sqlSelections = subQuery.getSelectClause().getSqlSelections();
            final String order;
            if (tupleComparisonOperator == ComparisonOperator.LESS_THAN || tupleComparisonOperator == ComparisonOperator.LESS_THAN_OR_EQUAL) {
                // Default order is asc so we don't need to specify the order explicitly
                order = "";
            } else {
                order = " desc";
            }
            appendSql('1');
            appendSql(order);
            for (int i = 1; i < sqlSelections.size(); i++) {
                appendSql(COMA_SEPARATOR_CHAR);
                appendSql(i + 1);
                appendSql(order);
            }
            renderFetch(new QueryLiteral<>(1, getIntegerType()), null, FetchClauseType.ROWS_ONLY);
            appendSql(CLOSE_PARENTHESIS);
        } finally {
            queryPartStack.pop();
            this.queryPartForRowNumbering = queryPartForRowNumbering;
            this.queryPartForRowNumberingClauseDepth = queryPartForRowNumberingClauseDepth;
            this.needsSelectAliases = needsSelectAliases;
        }
    } else {
        // TODO: We could use nested queries and use row numbers to emulate this
        throw new IllegalArgumentException("Can't emulate in predicate with tuples and limit/offset or set operations: " + predicate);
    }
}
Also used : QueryPart(org.hibernate.sql.ast.tree.select.QueryPart) QuerySpec(org.hibernate.sql.ast.tree.select.QuerySpec)

Example 14 with QueryPart

use of org.hibernate.sql.ast.tree.select.QueryPart in project hibernate-orm by hibernate.

the class AbstractSqlAstTranslator method determineLateralEmulationPredicate.

protected Predicate determineLateralEmulationPredicate(TableGroup tableGroup) {
    if (tableGroup.getPrimaryTableReference() instanceof QueryPartTableReference) {
        final QueryPartTableReference tableReference = (QueryPartTableReference) tableGroup.getPrimaryTableReference();
        final List<String> columnNames = tableReference.getColumnNames();
        final List<ColumnReference> columnReferences = new ArrayList<>(columnNames.size());
        final List<ColumnReference> subColumnReferences = new ArrayList<>(columnNames.size());
        final QueryPart queryPart = tableReference.getQueryPart();
        for (String columnName : columnNames) {
            columnReferences.add(new ColumnReference(tableReference, columnName, false, null, null, null, sessionFactory));
        }
        // The following optimization only makes sense if the necessary features are supported natively
        if ((columnReferences.size() == 1 || supportsRowValueConstructorSyntax()) && supportsDistinctFromPredicate()) {
            // ... x(c) on x.c is not distinct from (... fetch first 1 rows only)
            if (queryPart.getFetchClauseType() == FetchClauseType.ROWS_ONLY && queryPart.getFetchClauseExpression() instanceof QueryLiteral<?> && Integer.valueOf(1).equals(((QueryLiteral<?>) queryPart.getFetchClauseExpression()).getLiteralValue())) {
                return new ComparisonPredicate(new SqlTuple(columnReferences, tableGroup.getModelPart()), ComparisonOperator.NOT_DISTINCT_FROM, queryPart);
            }
        }
        // ... x(c) on exists(select x.c intersect ...)
        if (supportsIntersect()) {
            final QuerySpec lhsReferencesQuery = new QuerySpec(false);
            for (ColumnReference columnReference : columnReferences) {
                lhsReferencesQuery.getSelectClause().addSqlSelection(new SqlSelectionImpl(1, 0, columnReference));
            }
            final List<QueryPart> queryParts = new ArrayList<>(2);
            queryParts.add(lhsReferencesQuery);
            queryParts.add(queryPart);
            return new ExistsPredicate(new QueryGroup(false, SetOperator.INTERSECT, queryParts), false, getBooleanType());
        }
        // Double nested sub-query rendering if nothing else works
        // We try to avoid this as much as possible as it is not very efficient and some DBs don't like it
        // when a correlation happens in a sub-query that is not a direct child
        // ... x(c) on exists(select 1 from (...) synth_(c) where x.c = synth_.c)
        final QueryPartTableGroup subTableGroup = new QueryPartTableGroup(tableGroup.getNavigablePath(), (TableGroupProducer) tableGroup.getModelPart(), queryPart, "synth_", columnNames, false, true, sessionFactory);
        for (String columnName : columnNames) {
            subColumnReferences.add(new ColumnReference(subTableGroup.getPrimaryTableReference(), columnName, false, null, null, null, sessionFactory));
        }
        final QuerySpec existsQuery = new QuerySpec(false, 1);
        existsQuery.getSelectClause().addSqlSelection(new SqlSelectionImpl(1, 0, new QueryLiteral<>(1, getIntegerType())));
        existsQuery.getFromClause().addRoot(subTableGroup);
        existsQuery.applyPredicate(new ComparisonPredicate(new SqlTuple(columnReferences, tableGroup.getModelPart()), ComparisonOperator.NOT_DISTINCT_FROM, new SqlTuple(subColumnReferences, tableGroup.getModelPart())));
        return new ExistsPredicate(existsQuery, false, getBooleanType());
    }
    return null;
}
Also used : QueryPart(org.hibernate.sql.ast.tree.select.QueryPart) ExistsPredicate(org.hibernate.sql.ast.tree.predicate.ExistsPredicate) ArrayList(java.util.ArrayList) QueryPartTableGroup(org.hibernate.sql.ast.tree.from.QueryPartTableGroup) ComparisonPredicate(org.hibernate.sql.ast.tree.predicate.ComparisonPredicate) QueryGroup(org.hibernate.sql.ast.tree.select.QueryGroup) ConvertedQueryLiteral(org.hibernate.sql.ast.tree.expression.ConvertedQueryLiteral) QueryLiteral(org.hibernate.sql.ast.tree.expression.QueryLiteral) SqlTuple(org.hibernate.sql.ast.tree.expression.SqlTuple) SqlSelectionImpl(org.hibernate.sql.results.internal.SqlSelectionImpl) QuerySpec(org.hibernate.sql.ast.tree.select.QuerySpec) QueryPartTableReference(org.hibernate.sql.ast.tree.from.QueryPartTableReference) ColumnReference(org.hibernate.sql.ast.tree.expression.ColumnReference)

Example 15 with QueryPart

use of org.hibernate.sql.ast.tree.select.QueryPart in project hibernate-orm by hibernate.

the class AbstractSqlAstTranslator method emulateFetchOffsetWithWindowFunctions.

protected void emulateFetchOffsetWithWindowFunctions(QueryPart queryPart, Expression offsetExpression, Expression fetchExpression, FetchClauseType fetchClauseType, boolean emulateFetchClause) {
    final QueryPart queryPartForRowNumbering = this.queryPartForRowNumbering;
    final int queryPartForRowNumberingClauseDepth = this.queryPartForRowNumberingClauseDepth;
    final boolean needsSelectAliases = this.needsSelectAliases;
    try {
        this.queryPartForRowNumbering = queryPart;
        this.queryPartForRowNumberingClauseDepth = clauseStack.depth();
        this.needsSelectAliases = true;
        final String alias = "r_" + queryPartForRowNumberingAliasCounter + '_';
        queryPartForRowNumberingAliasCounter++;
        final boolean needsParenthesis;
        if (queryPart instanceof QueryGroup) {
            // We always need query wrapping if we are in a query group and the query part has a fetch clause
            needsParenthesis = queryPart.hasOffsetOrFetchClause();
        } else {
            needsParenthesis = !queryPart.isRoot();
        }
        if (needsParenthesis && !queryPart.isRoot()) {
            appendSql(OPEN_PARENTHESIS);
        }
        appendSql("select ");
        if (getClauseStack().isEmpty()) {
            appendSql('*');
        } else {
            final int size = queryPart.getFirstQuerySpec().getSelectClause().getSqlSelections().size();
            String separator = "";
            for (int i = 0; i < size; i++) {
                appendSql(separator);
                appendSql(alias);
                appendSql(".c");
                appendSql(i);
                separator = COMA_SEPARATOR;
            }
        }
        appendSql(" from ");
        if (!needsParenthesis || queryPart.isRoot()) {
            appendSql(OPEN_PARENTHESIS);
        }
        queryPart.accept(this);
        if (!needsParenthesis || queryPart.isRoot()) {
            appendSql(CLOSE_PARENTHESIS);
        }
        appendSql(WHITESPACE);
        appendSql(alias);
        appendSql(" where ");
        final Stack<Clause> clauseStack = getClauseStack();
        clauseStack.push(Clause.WHERE);
        try {
            if (emulateFetchClause && fetchExpression != null) {
                switch(fetchClauseType) {
                    case PERCENT_ONLY:
                        appendSql(alias);
                        appendSql(".rn<=");
                        if (offsetExpression != null) {
                            offsetExpression.accept(this);
                            appendSql('+');
                        }
                        appendSql("ceil(");
                        appendSql(alias);
                        appendSql(".cnt*");
                        fetchExpression.accept(this);
                        appendSql("/100)");
                        break;
                    case ROWS_ONLY:
                        appendSql(alias);
                        appendSql(".rn<=");
                        if (offsetExpression != null) {
                            offsetExpression.accept(this);
                            appendSql('+');
                        }
                        fetchExpression.accept(this);
                        break;
                    case PERCENT_WITH_TIES:
                        appendSql(alias);
                        appendSql(".rnk<=");
                        if (offsetExpression != null) {
                            offsetExpression.accept(this);
                            appendSql('+');
                        }
                        appendSql("ceil(");
                        appendSql(alias);
                        appendSql(".cnt*");
                        fetchExpression.accept(this);
                        appendSql("/100)");
                        break;
                    case ROWS_WITH_TIES:
                        appendSql(alias);
                        appendSql(".rnk<=");
                        if (offsetExpression != null) {
                            offsetExpression.accept(this);
                            appendSql('+');
                        }
                        fetchExpression.accept(this);
                        break;
                }
            }
            // todo: not sure if databases handle order by row number or the original ordering better..
            if (offsetExpression == null) {
                final Predicate additionalWherePredicate = this.additionalWherePredicate;
                if (additionalWherePredicate != null && !additionalWherePredicate.isEmpty()) {
                    this.additionalWherePredicate = null;
                    appendSql(" and ");
                    additionalWherePredicate.accept(this);
                }
                if (queryPart.isRoot()) {
                    switch(fetchClauseType) {
                        case PERCENT_ONLY:
                        case ROWS_ONLY:
                            appendSql(" order by ");
                            appendSql(alias);
                            appendSql(".rn");
                            break;
                        case PERCENT_WITH_TIES:
                        case ROWS_WITH_TIES:
                            appendSql(" order by ");
                            appendSql(alias);
                            appendSql(".rnk");
                            break;
                    }
                }
            } else {
                if (emulateFetchClause && fetchExpression != null) {
                    appendSql(" and ");
                }
                appendSql(alias);
                appendSql(".rn>");
                offsetExpression.accept(this);
                final Predicate additionalWherePredicate = this.additionalWherePredicate;
                if (additionalWherePredicate != null && !additionalWherePredicate.isEmpty()) {
                    this.additionalWherePredicate = null;
                    appendSql(" and ");
                    additionalWherePredicate.accept(this);
                }
                if (queryPart.isRoot()) {
                    appendSql(" order by ");
                    appendSql(alias);
                    appendSql(".rn");
                }
            }
            // We render the FOR UPDATE clause in the outer query
            if (queryPart instanceof QuerySpec) {
                clauseStack.pop();
                clauseStack.push(Clause.FOR_UPDATE);
                visitForUpdateClause((QuerySpec) queryPart);
            }
        } finally {
            clauseStack.pop();
        }
        if (needsParenthesis && !queryPart.isRoot()) {
            appendSql(CLOSE_PARENTHESIS);
        }
    } finally {
        this.queryPartForRowNumbering = queryPartForRowNumbering;
        this.queryPartForRowNumberingClauseDepth = queryPartForRowNumberingClauseDepth;
        this.needsSelectAliases = needsSelectAliases;
    }
}
Also used : QueryPart(org.hibernate.sql.ast.tree.select.QueryPart) Clause(org.hibernate.sql.ast.Clause) FromClause(org.hibernate.sql.ast.tree.from.FromClause) SelectClause(org.hibernate.sql.ast.tree.select.SelectClause) QuerySpec(org.hibernate.sql.ast.tree.select.QuerySpec) QueryGroup(org.hibernate.sql.ast.tree.select.QueryGroup) SqlFragmentPredicate(org.hibernate.persister.internal.SqlFragmentPredicate) InSubQueryPredicate(org.hibernate.sql.ast.tree.predicate.InSubQueryPredicate) SelfRenderingPredicate(org.hibernate.sql.ast.tree.predicate.SelfRenderingPredicate) FilterPredicate(org.hibernate.sql.ast.tree.predicate.FilterPredicate) NegatedPredicate(org.hibernate.sql.ast.tree.predicate.NegatedPredicate) LikePredicate(org.hibernate.sql.ast.tree.predicate.LikePredicate) ComparisonPredicate(org.hibernate.sql.ast.tree.predicate.ComparisonPredicate) BetweenPredicate(org.hibernate.sql.ast.tree.predicate.BetweenPredicate) NullnessPredicate(org.hibernate.sql.ast.tree.predicate.NullnessPredicate) GroupedPredicate(org.hibernate.sql.ast.tree.predicate.GroupedPredicate) BooleanExpressionPredicate(org.hibernate.sql.ast.tree.predicate.BooleanExpressionPredicate) ExistsPredicate(org.hibernate.sql.ast.tree.predicate.ExistsPredicate) InListPredicate(org.hibernate.sql.ast.tree.predicate.InListPredicate) Predicate(org.hibernate.sql.ast.tree.predicate.Predicate)

Aggregations

QueryPart (org.hibernate.sql.ast.tree.select.QueryPart)19 QueryGroup (org.hibernate.sql.ast.tree.select.QueryGroup)7 QuerySpec (org.hibernate.sql.ast.tree.select.QuerySpec)7 ComparisonPredicate (org.hibernate.sql.ast.tree.predicate.ComparisonPredicate)6 ArrayList (java.util.ArrayList)5 Expression (org.hibernate.sql.ast.tree.expression.Expression)5 NamedTableReference (org.hibernate.sql.ast.tree.from.NamedTableReference)5 ExistsPredicate (org.hibernate.sql.ast.tree.predicate.ExistsPredicate)5 InSubQueryPredicate (org.hibernate.sql.ast.tree.predicate.InSubQueryPredicate)5 SqmInsertSelectStatement (org.hibernate.query.sqm.tree.insert.SqmInsertSelectStatement)4 BinaryArithmeticExpression (org.hibernate.sql.ast.tree.expression.BinaryArithmeticExpression)4 ColumnReference (org.hibernate.sql.ast.tree.expression.ColumnReference)4 SelectClause (org.hibernate.sql.ast.tree.select.SelectClause)4 SqlSelectionImpl (org.hibernate.sql.results.internal.SqlSelectionImpl)4 Collections (java.util.Collections)3 IdentityHashMap (java.util.IdentityHashMap)3 LinkedHashMap (java.util.LinkedHashMap)3 List (java.util.List)3 Map (java.util.Map)3 BiConsumer (java.util.function.BiConsumer)3