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);
}
}
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);
}
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);
}
}
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;
}
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;
}
}
Aggregations