use of org.hibernate.sql.ast.tree.select.QueryGroup in project hibernate-orm by hibernate.
the class AbstractSqlAstTranslator method visitQuerySpec.
@Override
public void visitQuerySpec(QuerySpec querySpec) {
final QueryPart queryPartForRowNumbering = this.queryPartForRowNumbering;
final int queryPartForRowNumberingClauseDepth = this.queryPartForRowNumberingClauseDepth;
final boolean needsSelectAliases = this.needsSelectAliases;
final Predicate additionalWherePredicate = this.additionalWherePredicate;
final ForUpdateClause forUpdate = this.forUpdate;
try {
this.additionalWherePredicate = null;
this.forUpdate = null;
// See the field documentation of queryPartForRowNumbering etc. for an explanation about this
// In addition, we also reset the row numbering if the currently row numbered query part is a query group
// which means this query spec is a part of that query group.
// We want the row numbering to happen on the query group level, not on the query spec level, so we reset
final QueryPart currentQueryPart = queryPartStack.getCurrent();
if (currentQueryPart != null && (queryPartForRowNumbering instanceof QueryGroup || queryPartForRowNumberingClauseDepth != clauseStack.depth())) {
this.queryPartForRowNumbering = null;
this.queryPartForRowNumberingClauseDepth = -1;
}
String queryGroupAlias = "";
final boolean needsParenthesis;
if (currentQueryPart instanceof QueryGroup) {
// We always need query wrapping if we are in a query group and this query spec has a fetch clause
// because of order by precedence in SQL
needsParenthesis = querySpec.hasOffsetOrFetchClause();
if (needsParenthesis) {
// or if the database does not support simple query grouping, we must use a select wrapper
if (!supportsSimpleQueryGrouping() || currentQueryPart.hasOffsetOrFetchClause()) {
queryGroupAlias = " grp_" + queryGroupAliasCounter + '_';
queryGroupAliasCounter++;
appendSql("select");
appendSql(queryGroupAlias);
appendSql(".* from ");
}
}
} else {
needsParenthesis = !querySpec.isRoot();
}
queryPartStack.push(querySpec);
if (needsParenthesis) {
appendSql(OPEN_PARENTHESIS);
}
visitSelectClause(querySpec.getSelectClause());
visitFromClause(querySpec.getFromClause());
visitWhereClause(querySpec.getWhereClauseRestrictions());
visitGroupByClause(querySpec, getDialect().getGroupBySelectItemReferenceStrategy());
visitHavingClause(querySpec);
visitOrderBy(querySpec.getSortSpecifications());
visitOffsetFetchClause(querySpec);
// We render the FOR UPDATE clause in the parent query
if (queryPartForRowNumbering == null) {
visitForUpdateClause(querySpec);
}
if (needsParenthesis) {
appendSql(CLOSE_PARENTHESIS);
appendSql(queryGroupAlias);
}
} finally {
this.queryPartStack.pop();
this.queryPartForRowNumbering = queryPartForRowNumbering;
this.queryPartForRowNumberingClauseDepth = queryPartForRowNumberingClauseDepth;
this.needsSelectAliases = needsSelectAliases;
this.additionalWherePredicate = additionalWherePredicate;
if (queryPartForRowNumbering == null) {
this.forUpdate = forUpdate;
}
}
}
use of org.hibernate.sql.ast.tree.select.QueryGroup in project hibernate-orm by hibernate.
the class AbstractSqlAstTranslator method visitRelationalPredicate.
@Override
public void visitRelationalPredicate(ComparisonPredicate comparisonPredicate) {
// todo (6.0) : do we want to allow multi-valued parameters in a relational predicate?
// yes means we'd have to support dynamically converting this predicate into
// an IN predicate or an OR predicate
//
// NOTE: JPA does not define support for multi-valued parameters here.
//
// If we decide to support that ^^ we should validate that *both* sides of the
// predicate are multi-valued parameters. because...
// well... its stupid :)
final SqlTuple lhsTuple;
final SqlTuple rhsTuple;
if ((lhsTuple = SqlTupleContainer.getSqlTuple(comparisonPredicate.getLeftHandExpression())) != null) {
final Expression rhsExpression = comparisonPredicate.getRightHandExpression();
final boolean all;
final QueryPart subquery;
// Handle emulation of quantified comparison
if (rhsExpression instanceof QueryPart) {
subquery = (QueryPart) rhsExpression;
all = true;
} else if (rhsExpression instanceof Every) {
subquery = ((Every) rhsExpression).getSubquery();
all = true;
} else if (rhsExpression instanceof Any) {
subquery = ((Any) rhsExpression).getSubquery();
all = false;
} else {
subquery = null;
all = false;
}
final ComparisonOperator operator = comparisonPredicate.getOperator();
if (lhsTuple.getExpressions().size() == 1) {
// Special case for tuples with arity 1 as any DBMS supports scalar IN predicates
if (subquery == null) {
renderComparison(lhsTuple.getExpressions().get(0), operator, SqlTupleContainer.getSqlTuple(comparisonPredicate.getRightHandExpression()).getExpressions().get(0));
} else {
renderComparison(lhsTuple.getExpressions().get(0), operator, rhsExpression);
}
} else if (subquery != null && !supportsRowValueConstructorSyntaxInQuantifiedPredicates()) {
// For quantified relational comparisons, we can do an optimized emulation
if (supportsRowValueConstructorSyntax() && all) {
switch(operator) {
case LESS_THAN:
case LESS_THAN_OR_EQUAL:
case GREATER_THAN:
case GREATER_THAN_OR_EQUAL:
{
emulateQuantifiedTupleSubQueryPredicate(comparisonPredicate, subquery, lhsTuple, operator);
return;
}
}
}
emulateSubQueryRelationalRestrictionPredicate(comparisonPredicate, all, subquery, lhsTuple, this::renderSelectTupleComparison, all ? operator.negated() : operator);
} else if (!supportsRowValueConstructorSyntax()) {
rhsTuple = SqlTupleContainer.getSqlTuple(rhsExpression);
assert rhsTuple != null;
// Some DBs like Oracle support tuples only for the IN subquery predicate
if ((operator == ComparisonOperator.EQUAL || operator == ComparisonOperator.NOT_EQUAL) && supportsRowValueConstructorSyntaxInInSubQuery()) {
comparisonPredicate.getLeftHandExpression().accept(this);
if (operator == ComparisonOperator.NOT_EQUAL) {
appendSql(" not");
}
appendSql(" in(");
renderExpressionsAsSubquery(rhsTuple.getExpressions());
appendSql(CLOSE_PARENTHESIS);
} else {
emulateTupleComparison(lhsTuple.getExpressions(), rhsTuple.getExpressions(), operator, true);
}
} else {
renderComparison(comparisonPredicate.getLeftHandExpression(), operator, rhsExpression);
}
} else if ((rhsTuple = SqlTupleContainer.getSqlTuple(comparisonPredicate.getRightHandExpression())) != null) {
final Expression lhsExpression = comparisonPredicate.getLeftHandExpression();
if (lhsExpression instanceof QueryGroup) {
if (rhsTuple.getExpressions().size() == 1) {
// Special case for tuples with arity 1 as any DBMS supports scalar IN predicates
renderComparison(lhsExpression, comparisonPredicate.getOperator(), rhsTuple.getExpressions().get(0));
} else if (supportsRowValueConstructorSyntax()) {
renderComparison(lhsExpression, comparisonPredicate.getOperator(), comparisonPredicate.getRightHandExpression());
} else {
emulateSubQueryRelationalRestrictionPredicate(comparisonPredicate, false, (QueryGroup) lhsExpression, rhsTuple, this::renderSelectTupleComparison, // Since we switch the order of operands, we have to invert the operator
comparisonPredicate.getOperator().invert());
}
} else {
throw new IllegalStateException("Unsupported tuple comparison combination. LHS is neither a tuple nor a tuple subquery but RHS is a tuple: " + comparisonPredicate);
}
} else {
renderComparison(comparisonPredicate.getLeftHandExpression(), comparisonPredicate.getOperator(), comparisonPredicate.getRightHandExpression());
}
}
use of org.hibernate.sql.ast.tree.select.QueryGroup in project hibernate-orm by hibernate.
the class BaseSqmToSqlAstConverter method visitQueryGroup.
@Override
public QueryGroup visitQueryGroup(SqmQueryGroup<?> queryGroup) {
final List<? extends SqmQueryPart<?>> queryParts = queryGroup.getQueryParts();
final int size = queryParts.size();
final List<QueryPart> newQueryParts = new ArrayList<>(size);
final QueryGroup group = new QueryGroup(getProcessingStateStack().isEmpty(), queryGroup.getSetOperator(), newQueryParts);
if (queryGroup.getOrderByClause() != null && queryGroup.getOrderByClause().hasPositionalSortItem()) {
trackSelectionsForGroup = true;
}
final SqlAstQueryPartProcessingStateImpl processingState = new SqlAstQueryPartProcessingStateImpl(group, getCurrentProcessingState(), this, DelegatingSqmAliasedNodeCollector::new, currentClauseStack::getCurrent, deduplicateSelectionItems);
final DelegatingSqmAliasedNodeCollector collector = (DelegatingSqmAliasedNodeCollector) processingState.getSqlExpressionResolver();
final SqmQueryPart<?> sqmQueryPart = currentSqmQueryPart;
currentSqmQueryPart = queryGroup;
pushProcessingState(processingState);
try {
newQueryParts.add(visitQueryPart(queryParts.get(0)));
collector.setSqmAliasedNodeCollector((SqmAliasedNodeCollector) lastPoppedProcessingState.getSqlExpressionResolver());
visitOrderByOffsetAndFetch(queryGroup, group);
trackSelectionsForGroup = false;
for (int i = 1; i < size; i++) {
newQueryParts.add(visitQueryPart(queryParts.get(i)));
}
return group;
} finally {
popProcessingStateStack();
currentSqmQueryPart = sqmQueryPart;
}
}
use of org.hibernate.sql.ast.tree.select.QueryGroup 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);
}
}
use of org.hibernate.sql.ast.tree.select.QueryGroup 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;
}
Aggregations