use of org.hibernate.sql.ast.tree.expression.SqlSelectionExpression in project hibernate-orm by hibernate.
the class AbstractSqlAstTranslator method getSelectItemsToInline.
private BitSet getSelectItemsToInline() {
final QuerySpec querySpec = (QuerySpec) getQueryPartStack().getCurrent();
final List<SqlSelection> sqlSelections = querySpec.getSelectClause().getSqlSelections();
final BitSet bitSet = new BitSet(sqlSelections.size());
for (Expression groupByClauseExpression : querySpec.getGroupByClauseExpressions()) {
final SqlSelectionExpression selectItemReference = getSelectItemReference(groupByClauseExpression);
if (selectItemReference != null) {
bitSet.set(sqlSelections.indexOf(selectItemReference.getSelection()));
}
}
return bitSet;
}
use of org.hibernate.sql.ast.tree.expression.SqlSelectionExpression in project hibernate-orm by hibernate.
the class AggregateWindowEmulationQueryTransformer method transform.
@Override
public QuerySpec transform(CteContainer cteContainer, QuerySpec querySpec, SqmToSqlAstConverter converter) {
final SessionFactoryImplementor factory = converter.getCreationContext().getSessionFactory();
final QuerySpec outerQuerySpec = new QuerySpec(querySpec.isRoot());
final String identifierVariable = "hhh_";
final NavigablePath navigablePath = new NavigablePath(identifierVariable, identifierVariable);
final SelectClause selectClause = outerQuerySpec.getSelectClause();
final QuerySpec subQuerySpec = querySpec.asSubQuery();
final SelectClause subSelectClause = subQuerySpec.getSelectClause();
final List<SqlSelection> subSelections = subSelectClause.getSqlSelections();
final List<String> columnNames = new ArrayList<>(subSelections.size());
// A map to find the select item position for an expression
// which is needed to decide if we need to introduce synthetic select items
// for group by items, since these group by items are migrated to the outer query
final Map<Expression, Integer> selectionMapping = new HashMap<>(subSelections.size());
// for the QueryPartTableGroup within which the sub query spec is embedded
for (int i = 0; i < subSelections.size(); i++) {
final BasicValuedMapping mapping = (BasicValuedMapping) subSelections.get(i).getExpressionType();
final String columnName = "col" + i;
final ColumnReference columnReference = new ColumnReference(identifierVariable, columnName, false, null, null, mapping.getJdbcMapping(), factory);
final Expression expression = subSelections.get(i).getExpression();
final Expression finalExpression;
if (expression == windowFunction) {
finalExpression = new SelfRenderingAggregateFunctionSqlAstExpression("min", (sqlAppender, sqlAstArguments, walker1) -> {
sqlAppender.appendSql("min(");
sqlAstArguments.get(0).accept(walker1);
sqlAppender.append(')');
}, Collections.singletonList(columnReference), null, (ReturnableType<?>) mapping.getMappedType(), expression.getExpressionType());
} else {
finalExpression = columnReference;
selectionMapping.put(expression, i);
}
columnNames.add(columnName);
selectClause.addSqlSelection(new ResolvedSqlSelection(i + 1, i, finalExpression, (BasicType<Object>) mapping.getJdbcMapping()));
}
// Migrate the group by clause to the outer query
// and push group by expressions into the partition by clause of the window function
final List<Expression> groupByExpressions = new ArrayList<>(subQuerySpec.getGroupByClauseExpressions().size());
for (Expression groupByClauseExpression : subQuerySpec.getGroupByClauseExpressions()) {
final Expression realExpression;
final Expression outerGroupByExpression;
if (groupByClauseExpression instanceof SqlSelectionExpression) {
final SqlSelection selection = ((SqlSelectionExpression) groupByClauseExpression).getSelection();
outerGroupByExpression = new SqlSelectionExpression(selectClause.getSqlSelections().get(selection.getValuesArrayPosition()));
realExpression = selection.getExpression();
} else {
if (groupByClauseExpression instanceof SqmPathInterpretation<?>) {
realExpression = ((SqmPathInterpretation<?>) groupByClauseExpression).getSqlExpression();
} else {
realExpression = groupByClauseExpression;
}
final Integer position = selectionMapping.get(realExpression);
if (position == null) {
// Group by something that has no corresponding selection item,
// so we need to introduce an intermediate selection item
final int valuesPosition = selectClause.getSqlSelections().size();
final String columnName = "col" + valuesPosition;
final JdbcMapping jdbcMapping = realExpression.getExpressionType().getJdbcMappings().get(0);
final ColumnReference columnReference = new ColumnReference(identifierVariable, columnName, false, null, null, jdbcMapping, factory);
final int subValuesPosition = subSelectClause.getSqlSelections().size();
final SqlSelection subSelection = new ResolvedSqlSelection(subValuesPosition + 1, subValuesPosition, realExpression, (BasicType<Object>) jdbcMapping);
columnNames.add(columnName);
subSelectClause.addSqlSelection(subSelection);
outerGroupByExpression = columnReference;
selectionMapping.put(realExpression, subValuesPosition);
} else {
outerGroupByExpression = new SqlSelectionExpression(selectClause.getSqlSelections().get(position));
}
}
windowFunction.getPartitions().add(realExpression);
groupByExpressions.add(outerGroupByExpression);
}
outerQuerySpec.setGroupByClauseExpressions(groupByExpressions);
subQuerySpec.setGroupByClauseExpressions(null);
// Migrate the having clause to the outer query
if (subQuerySpec.getHavingClauseRestrictions() != null) {
final Predicate predicate = new ExpressionReplacementWalker() {
@Override
protected <X extends SqlAstNode> X replaceExpression(X expression) {
if (expression instanceof Literal || expression instanceof JdbcParameter) {
return expression;
}
final Expression outerExpression;
if (expression instanceof SqlSelectionExpression) {
final SqlSelection selection = ((SqlSelectionExpression) expression).getSelection();
outerExpression = selectClause.getSqlSelections().get(selection.getValuesArrayPosition()).getExpression();
} else {
final Expression realExpression;
if (expression instanceof SqmPathInterpretation<?>) {
realExpression = ((SqmPathInterpretation<?>) expression).getSqlExpression();
} else {
realExpression = (Expression) expression;
}
final Integer position = selectionMapping.get(realExpression);
if (position == null) {
// An expression that has no corresponding selection item,
// so we need to introduce an intermediate selection item
final int valuesPosition = selectClause.getSqlSelections().size();
final String columnName = "col" + valuesPosition;
final JdbcMapping jdbcMapping = realExpression.getExpressionType().getJdbcMappings().get(0);
final ColumnReference columnReference = new ColumnReference(identifierVariable, columnName, false, null, null, jdbcMapping, factory);
final int subValuesPosition = subSelectClause.getSqlSelections().size();
final SqlSelection subSelection = new ResolvedSqlSelection(subValuesPosition + 1, subValuesPosition, realExpression, (BasicType<Object>) jdbcMapping);
columnNames.add(columnName);
subSelectClause.addSqlSelection(subSelection);
outerExpression = columnReference;
selectionMapping.put(realExpression, subValuesPosition);
} else {
outerExpression = selectClause.getSqlSelections().get(position).getExpression();
}
}
return (X) outerExpression;
}
}.replaceExpressions(subQuerySpec.getHavingClauseRestrictions());
outerQuerySpec.setHavingClauseRestrictions(predicate);
subQuerySpec.setHavingClauseRestrictions(null);
}
// Migrate the order by clause to the outer query
if (subQuerySpec.hasSortSpecifications()) {
for (SortSpecification sortSpecification : subQuerySpec.getSortSpecifications()) {
final Expression sortExpression = sortSpecification.getSortExpression();
final Expression outerSortExpression;
if (sortExpression instanceof SqlSelectionExpression) {
final SqlSelection selection = ((SqlSelectionExpression) sortExpression).getSelection();
outerSortExpression = new SqlSelectionExpression(selectClause.getSqlSelections().get(selection.getValuesArrayPosition()));
} else {
final Expression realExpression;
if (sortExpression instanceof SqmPathInterpretation<?>) {
realExpression = ((SqmPathInterpretation<?>) sortExpression).getSqlExpression();
} else {
realExpression = sortExpression;
}
final Integer position = selectionMapping.get(realExpression);
if (position == null) {
// Group by something that has no corresponding selection item,
// so we need to introduce an intermediate selection item
final int valuesPosition = selectClause.getSqlSelections().size();
final String columnName = "col" + valuesPosition;
final JdbcMapping jdbcMapping = realExpression.getExpressionType().getJdbcMappings().get(0);
final ColumnReference columnReference = new ColumnReference(identifierVariable, columnName, false, null, null, jdbcMapping, factory);
final int subValuesPosition = subSelectClause.getSqlSelections().size();
final SqlSelection subSelection = new ResolvedSqlSelection(subValuesPosition + 1, subValuesPosition, realExpression, (BasicType<Object>) jdbcMapping);
columnNames.add(columnName);
subSelectClause.addSqlSelection(subSelection);
outerSortExpression = columnReference;
selectionMapping.put(realExpression, subValuesPosition);
} else {
outerSortExpression = new SqlSelectionExpression(selectClause.getSqlSelections().get(position));
}
}
outerQuerySpec.addSortSpecification(new SortSpecification(outerSortExpression, sortSpecification.getSortOrder(), sortSpecification.getNullPrecedence()));
}
subQuerySpec.getSortSpecifications().clear();
}
// We need to add selection items for the expressions we order by to the sub query spec.
final int selectionOffset = columnNames.size();
// Collect the sorting column references so we can apply the filter later
final List<ColumnReference> sortingColumns = new ArrayList<>(withinGroup.size());
for (int i = 0; i < withinGroup.size(); i++) {
final int valueIndex = selectionOffset + i;
final Expression sortExpression = withinGroup.get(i).getSortExpression();
final BasicValuedMapping mapping = (BasicValuedMapping) sortExpression.getExpressionType();
final String columnName = "col" + valueIndex;
final int oldValueIndex = subSelectClause.getSqlSelections().size();
columnNames.add(columnName);
subSelectClause.addSqlSelection(new ResolvedSqlSelection(oldValueIndex + 1, oldValueIndex, sortExpression, (BasicType<Object>) mapping.getJdbcMapping()));
sortingColumns.add(new ColumnReference(identifierVariable, columnName, false, null, null, mapping.getJdbcMapping(), factory));
}
if (arguments != null) {
// So we add a filter to the outer query so we can extract the rank
switch(arguments.size()) {
case 0:
break;
case 1:
outerQuerySpec.applyPredicate(new ComparisonPredicate(sortingColumns.get(0), ComparisonOperator.EQUAL, (Expression) arguments.get(0)));
break;
default:
outerQuerySpec.applyPredicate(new ComparisonPredicate(new SqlTuple(sortingColumns, null), ComparisonOperator.EQUAL, new SqlTuple((List<? extends Expression>) (List<?>) arguments, null)));
}
}
final QueryPartTableGroup queryPartTableGroup = new QueryPartTableGroup(navigablePath, null, subQuerySpec, identifierVariable, columnNames, false, true, factory);
outerQuerySpec.getFromClause().addRoot(queryPartTableGroup);
// Migrate the offset/fetch clause
outerQuerySpec.setOffsetClauseExpression(subQuerySpec.getOffsetClauseExpression());
outerQuerySpec.setFetchClauseExpression(subQuerySpec.getFetchClauseExpression(), subQuerySpec.getFetchClauseType());
subQuerySpec.setOffsetClauseExpression(null);
subQuerySpec.setFetchClauseExpression(null, null);
return outerQuerySpec;
}
use of org.hibernate.sql.ast.tree.expression.SqlSelectionExpression in project hibernate-orm by hibernate.
the class BaseSqmToSqlAstConverter method visitTableGroup.
private Expression visitTableGroup(TableGroup tableGroup, SqmFrom<?, ?> path) {
final ModelPartContainer modelPart;
final MappingModelExpressible<?> inferredValueMapping = getInferredValueMapping();
// For plain SqmFrom node uses, prefer the mapping type from the context if possible
if (!(inferredValueMapping instanceof ModelPartContainer)) {
modelPart = tableGroup.getModelPart();
} else {
modelPart = (ModelPartContainer) inferredValueMapping;
}
final ModelPart resultModelPart;
final ModelPart interpretationModelPart;
final TableGroup parentGroupToUse;
if (modelPart instanceof ToOneAttributeMapping) {
final ToOneAttributeMapping toOneAttributeMapping = (ToOneAttributeMapping) modelPart;
final ModelPart targetPart = toOneAttributeMapping.getForeignKeyDescriptor().getPart(toOneAttributeMapping.getSideNature().inverse());
if (tableGroup.getModelPart().getPartMappingType() == modelPart.getPartMappingType()) {
resultModelPart = targetPart;
} else {
// If the table group is for a different mapping type i.e. an inheritance subtype,
// lookup the target part on that mapping type
resultModelPart = tableGroup.getModelPart().findSubPart(targetPart.getPartName(), null);
}
interpretationModelPart = modelPart;
parentGroupToUse = null;
} else if (modelPart instanceof PluralAttributeMapping) {
final PluralAttributeMapping pluralAttributeMapping = (PluralAttributeMapping) modelPart;
final CollectionPart elementDescriptor = pluralAttributeMapping.getElementDescriptor();
if (elementDescriptor instanceof EntityCollectionPart) {
// Usually, we need to resolve to the PK for visitTableGroup
final EntityCollectionPart collectionPart = (EntityCollectionPart) elementDescriptor;
final ModelPart collectionTargetPart = collectionPart.getForeignKeyDescriptor().getPart(collectionPart.getSideNature().inverse());
final EntityIdentifierMapping identifierMapping = collectionPart.getEntityMappingType().getIdentifierMapping();
// If the FK points to the PK, we can use the FK part though, if this is not a root
if (collectionTargetPart == identifierMapping && !(path instanceof SqmRoot<?>)) {
resultModelPart = collectionPart.getForeignKeyDescriptor().getPart(collectionPart.getSideNature());
} else {
resultModelPart = identifierMapping;
}
} else {
resultModelPart = elementDescriptor;
}
interpretationModelPart = elementDescriptor;
parentGroupToUse = null;
} else if (modelPart instanceof EntityCollectionPart) {
// Usually, we need to resolve to the PK for visitTableGroup
final EntityCollectionPart collectionPart = (EntityCollectionPart) modelPart;
final ModelPart collectionTargetPart = collectionPart.getForeignKeyDescriptor().getPart(collectionPart.getSideNature().inverse());
final EntityIdentifierMapping identifierMapping = collectionPart.getEntityMappingType().getIdentifierMapping();
// If the FK points to the PK, we can use the FK part though, if this is not a root
if (collectionTargetPart == identifierMapping && !(path instanceof SqmRoot<?>)) {
resultModelPart = collectionPart.getForeignKeyDescriptor().getPart(collectionPart.getSideNature());
} else {
resultModelPart = identifierMapping;
}
interpretationModelPart = modelPart;
parentGroupToUse = findTableGroup(tableGroup.getNavigablePath().getParent());
} else if (modelPart instanceof EntityMappingType) {
resultModelPart = ((EntityMappingType) modelPart).getIdentifierMapping();
interpretationModelPart = modelPart;
// todo: I think this will always be null anyways because EntityMappingType will only be the model part
// of a TableGroup if that is a root TableGroup, so check if we can just switch to null
parentGroupToUse = findTableGroup(tableGroup.getNavigablePath().getParent());
} else {
resultModelPart = modelPart;
interpretationModelPart = modelPart;
parentGroupToUse = null;
}
final NavigablePath navigablePath;
if (interpretationModelPart == modelPart) {
navigablePath = tableGroup.getNavigablePath();
} else {
navigablePath = tableGroup.getNavigablePath().append(interpretationModelPart.getPartName());
}
final Expression result;
if (interpretationModelPart instanceof EntityValuedModelPart) {
final boolean expandToAllColumns;
if (currentClauseStack.getCurrent() == Clause.GROUP) {
// When the table group is known to be fetched i.e. a fetch join
// but also when the from clause is part of the select clause
// we need to expand to all columns, as we also expand this to all columns in the select clause
expandToAllColumns = tableGroup.isFetched() || selectClauseContains(path);
} else {
expandToAllColumns = false;
}
final EntityValuedModelPart mapping = (EntityValuedModelPart) interpretationModelPart;
EntityMappingType mappingType;
if (path instanceof SqmTreatedPath) {
mappingType = creationContext.getSessionFactory().getRuntimeMetamodels().getMappingMetamodel().findEntityDescriptor(((SqmTreatedPath<?, ?>) path).getTreatTarget().getHibernateEntityName());
} else {
mappingType = mapping.getEntityMappingType();
}
result = EntityValuedPathInterpretation.from(navigablePath, parentGroupToUse == null ? tableGroup : parentGroupToUse, expandToAllColumns ? null : resultModelPart, (EntityValuedModelPart) interpretationModelPart, mappingType, this);
} else if (interpretationModelPart instanceof EmbeddableValuedModelPart) {
final EmbeddableValuedModelPart mapping = (EmbeddableValuedModelPart) resultModelPart;
result = new EmbeddableValuedPathInterpretation<>(mapping.toSqlExpression(tableGroup, currentClauseStack.getCurrent(), this, getSqlAstCreationState()), navigablePath, (EmbeddableValuedModelPart) interpretationModelPart, tableGroup);
} else {
assert interpretationModelPart instanceof BasicValuedModelPart;
final BasicValuedModelPart mapping = (BasicValuedModelPart) resultModelPart;
final TableReference tableReference = tableGroup.resolveTableReference(navigablePath.append(resultModelPart.getPartName()), mapping.getContainingTableExpression());
final Expression expression = getSqlExpressionResolver().resolveSqlExpression(SqlExpressionResolver.createColumnReferenceKey(tableReference, mapping.getSelectionExpression()), sacs -> new ColumnReference(tableReference.getIdentificationVariable(), mapping, getCreationContext().getSessionFactory()));
final ColumnReference columnReference;
if (expression instanceof ColumnReference) {
columnReference = (ColumnReference) expression;
} else if (expression instanceof SqlSelectionExpression) {
final Expression selectedExpression = ((SqlSelectionExpression) expression).getSelection().getExpression();
assert selectedExpression instanceof ColumnReference;
columnReference = (ColumnReference) selectedExpression;
} else {
throw new UnsupportedOperationException("Unsupported basic-valued path expression : " + expression);
}
result = new BasicValuedPathInterpretation<>(columnReference, navigablePath, (BasicValuedModelPart) interpretationModelPart, tableGroup);
}
return withTreatRestriction(result, path);
}
use of org.hibernate.sql.ast.tree.expression.SqlSelectionExpression in project hibernate-orm by hibernate.
the class BaseSqmToSqlAstConverter method resolveGroupOrOrderByExpression.
protected Expression resolveGroupOrOrderByExpression(SqmExpression<?> groupByClauseExpression) {
final int sqmPosition;
if (groupByClauseExpression instanceof SqmAliasedNodeRef) {
final int aliasedNodeOrdinal = ((SqmAliasedNodeRef) groupByClauseExpression).getPosition();
sqmPosition = aliasedNodeOrdinal - 1;
} else if (statement.getQuerySource() == SqmQuerySource.CRITERIA) {
// In JPA Criteria we could be using the same expression object for the group/order by and select item
// We try to find the select item position for this expression here which is not necessarily just an optimization.
// This is vital to enable the support for parameters in these expressions.
// Databases usually don't know if a parameter marker will have the same value as another parameter marker
// and due to that, a database usually complains when seeing something like
// `select ?, count(*) from dual group by ?` saying that there is a missing group by for the first `?`
// To avoid this issue, we determine the position and let the SqlAstTranslator handle the rest.
// Usually it will render `select ?, count(*) from dual group by 1` if supported
// or force rendering the parameter as literal instead so that the database can see the grouping is fine
final SqmQuerySpec<?> querySpec = currentSqmQueryPart.getFirstQuerySpec();
sqmPosition = indexOfExpression(querySpec.getSelectClause().getSelections(), groupByClauseExpression);
} else {
sqmPosition = -1;
}
if (sqmPosition != -1) {
final List<SqlSelection> selections = currentSqlSelectionCollector().getSelections(sqmPosition);
assert selections != null : String.format(Locale.ROOT, "No SqlSelections for SQM position `%s`", sqmPosition);
final List<Expression> expressions = new ArrayList<>(selections.size());
OUTER: for (int i = 0; i < selections.size(); i++) {
final SqlSelection selection = selections.get(i);
// which is, just like the identifier itself, also registered as selection
for (int j = 0; j < i; j++) {
if (selections.get(j) == selection) {
continue OUTER;
}
}
if (currentSqmQueryPart instanceof SqmQueryGroup<?>) {
// Reusing the SqlSelection for query groups would be wrong because the aliases do no exist
// So we have to use a literal expression in a new SqlSelection instance to refer to the position
expressions.add(new SqlSelectionExpression(new SqlSelectionImpl(selection.getJdbcResultSetIndex(), selection.getValuesArrayPosition(), new QueryLiteral<>(selection.getValuesArrayPosition(), basicType(Integer.class)))));
} else {
expressions.add(new SqlSelectionExpression(selection));
}
}
if (expressions.size() == 1) {
return expressions.get(0);
}
return new SqlTuple(expressions, null);
}
return (Expression) groupByClauseExpression.accept(this);
}
use of org.hibernate.sql.ast.tree.expression.SqlSelectionExpression in project hibernate-orm by hibernate.
the class BasicValuedPathInterpretation method from.
/**
* Static factory
*/
public static <T> BasicValuedPathInterpretation<T> from(SqmBasicValuedSimplePath<T> sqmPath, SqlAstCreationState sqlAstCreationState, SemanticQueryWalker sqmWalker, boolean jpaQueryComplianceEnabled) {
final FromClauseAccess fromClauseAccess = sqlAstCreationState.getFromClauseAccess();
final TableGroup tableGroup = fromClauseAccess.getTableGroup(sqmPath.getNavigablePath().getParent());
EntityMappingType treatTarget = null;
if (jpaQueryComplianceEnabled) {
if (sqmPath.getLhs() instanceof SqmTreatedPath) {
final EntityDomainType treatTargetDomainType = ((SqmTreatedPath) sqmPath.getLhs()).getTreatTarget();
final MappingMetamodel mappingMetamodel = sqlAstCreationState.getCreationContext().getSessionFactory().getRuntimeMetamodels().getMappingMetamodel();
treatTarget = mappingMetamodel.findEntityDescriptor(treatTargetDomainType.getHibernateEntityName());
} else if (sqmPath.getLhs().getNodeType() instanceof EntityDomainType) {
final EntityDomainType entityDomainType = (EntityDomainType) sqmPath.getLhs().getNodeType();
final MappingMetamodel mappingMetamodel = sqlAstCreationState.getCreationContext().getSessionFactory().getRuntimeMetamodels().getMappingMetamodel();
treatTarget = mappingMetamodel.findEntityDescriptor(entityDomainType.getHibernateEntityName());
}
}
final BasicValuedModelPart mapping = (BasicValuedModelPart) tableGroup.getModelPart().findSubPart(sqmPath.getReferencedPathSource().getPathName(), treatTarget);
if (mapping == null) {
if (jpaQueryComplianceEnabled) {
// to get the better error, see if we got nothing because of treat handling
final ModelPart subPart = tableGroup.getModelPart().findSubPart(sqmPath.getReferencedPathSource().getPathName(), null);
if (subPart != null) {
throw new StrictJpaComplianceViolation(StrictJpaComplianceViolation.Type.IMPLICIT_TREAT);
}
}
throw new SemanticException("`" + sqmPath.getNavigablePath().getFullPath() + "` did not reference a known model part");
}
final TableReference tableReference = tableGroup.resolveTableReference(sqmPath.getNavigablePath(), mapping.getContainingTableExpression());
final Expression expression = sqlAstCreationState.getSqlExpressionResolver().resolveSqlExpression(SqlExpressionResolver.createColumnReferenceKey(tableReference, mapping.getSelectionExpression()), sacs -> new ColumnReference(tableReference.getIdentificationVariable(), mapping, sqlAstCreationState.getCreationContext().getSessionFactory()));
final ColumnReference columnReference;
if (expression instanceof ColumnReference) {
columnReference = ((ColumnReference) expression);
} else if (expression instanceof SqlSelectionExpression) {
final Expression selectedExpression = ((SqlSelectionExpression) expression).getSelection().getExpression();
assert selectedExpression instanceof ColumnReference;
columnReference = (ColumnReference) selectedExpression;
} else {
throw new UnsupportedOperationException("Unsupported basic-valued path expression : " + expression);
}
return new BasicValuedPathInterpretation<>(columnReference, sqmPath.getNavigablePath(), mapping, tableGroup);
}
Aggregations