use of org.hibernate.query.sqm.tree.from.SqmFrom in project hibernate-orm by hibernate.
the class SemanticQueryBuilder method resolveOrderByOrGroupByExpression.
private SqmExpression<?> resolveOrderByOrGroupByExpression(ParseTree child, boolean definedCollate) {
if (child instanceof TerminalNode) {
if (definedCollate) {
// This is syntactically disallowed
throw new ParsingException("COLLATE is not allowed for position based order-by or group-by items");
}
final int position = Integer.parseInt(child.getText());
// make sure this selection exists
final SqmAliasedNode<?> nodeByPosition = getCurrentProcessingState().getPathRegistry().findAliasedNodeByPosition(position);
if (nodeByPosition == null) {
throw new ParsingException("Numeric literal '" + position + "' used in group-by does not match a registered select-item");
}
return new SqmAliasedNodeRef(position, integerDomainType, creationContext.getNodeBuilder());
} else if (child instanceof HqlParser.IdentifierContext) {
final String identifierText = visitIdentifier((HqlParser.IdentifierContext) child);
final Integer correspondingPosition = getCurrentProcessingState().getPathRegistry().findAliasedNodePosition(identifierText);
if (correspondingPosition != null) {
if (definedCollate) {
// This is syntactically disallowed
throw new ParsingException("COLLATE is not allowed for alias based order-by or group-by items");
}
return new SqmAliasedNodeRef(correspondingPosition, integerDomainType, creationContext.getNodeBuilder());
}
final SqmFrom<?, ?> sqmFrom = getCurrentProcessingState().getPathRegistry().findFromByAlias(identifierText, true);
if (sqmFrom != null) {
if (definedCollate) {
// This is syntactically disallowed
throw new ParsingException("COLLATE is not allowed for alias based order-by or group-by items");
}
// this will group-by all of the sub-parts in the from-element's model part
return sqmFrom;
}
final DotIdentifierConsumer dotIdentifierConsumer = dotIdentifierConsumerStack.getCurrent();
dotIdentifierConsumer.consumeIdentifier(identifierText, true, true);
return (SqmExpression<?>) dotIdentifierConsumer.getConsumedPart();
}
return (SqmExpression<?>) child.accept(this);
}
use of org.hibernate.query.sqm.tree.from.SqmFrom 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.query.sqm.tree.from.SqmFrom in project hibernate-orm by hibernate.
the class QualifiedJoinPredicatePathConsumer method createBasePart.
@Override
protected SemanticPathPart createBasePart() {
return new BaseLocalSequencePart() {
@Override
protected void validateAsRoot(SqmFrom<?, ?> pathRoot) {
final SqmRoot<?> root = pathRoot.findRoot();
final SqmRoot<?> joinRoot = sqmJoin.findRoot();
if (root != joinRoot) {
// The root of a path within a join condition doesn't have the same root as the current join we are processing.
// The aim of this check is to prevent uses of different "spaces" i.e. `from A a, B b join b.id = a.id` would be illegal
SqmCreationProcessingState processingState = getCreationState().getCurrentProcessingState();
// First, we need to find out if the current join is part of current processing query
final SqmQuery<?> currentProcessingQuery = processingState.getProcessingQuery();
if (currentProcessingQuery instanceof SqmSelectQuery<?>) {
final SqmQuerySpec<?> querySpec = ((SqmSelectQuery<?>) currentProcessingQuery).getQuerySpec();
final SqmFromClause fromClause = querySpec.getFromClause();
// then the root of the processing path must be a root of one of the parent queries
if (fromClause != null && fromClause.getRoots().contains(joinRoot)) {
// It is allowed to use correlations from the same query
if (!(root instanceof SqmCorrelation<?, ?>) || !fromClause.getRoots().contains(root)) {
validateAsRootOnParentQueryClosure(pathRoot, root, processingState.getParentProcessingState());
}
return;
}
}
// in which case the path root is allowed to occur in the current processing query as root
if (currentProcessingQuery instanceof SqmSubQuery<?>) {
validateAsRootOnParentQueryClosure(pathRoot, root, processingState);
return;
}
throw new SemanticException(String.format(Locale.ROOT, "SqmQualifiedJoin predicate referred to SqmRoot [`%s`] other than the join's root [`%s`]", pathRoot.getNavigablePath().getFullPath(), sqmJoin.getNavigablePath().getFullPath()));
}
super.validateAsRoot(pathRoot);
}
private void validateAsRootOnParentQueryClosure(SqmFrom<?, ?> pathRoot, SqmRoot<?> root, SqmCreationProcessingState processingState) {
while (processingState != null) {
final SqmQuery<?> processingQuery = processingState.getProcessingQuery();
if (processingQuery instanceof SqmSelectQuery<?>) {
final SqmQuerySpec<?> querySpec = ((SqmSelectQuery<?>) processingQuery).getQuerySpec();
final SqmFromClause fromClause = querySpec.getFromClause();
// i.e. `from A a, B b join b.id = a.id` would be illegal
if (fromClause != null && fromClause.getRoots().contains(root)) {
super.validateAsRoot(pathRoot);
return;
}
}
processingState = processingState.getParentProcessingState();
}
throw new SemanticException(String.format(Locale.ROOT, "SqmQualifiedJoin predicate referred to SqmRoot [`%s`] other than the join's root [`%s`]", pathRoot.getNavigablePath().getFullPath(), sqmJoin.getNavigablePath().getFullPath()));
}
};
}
use of org.hibernate.query.sqm.tree.from.SqmFrom in project hibernate-orm by hibernate.
the class BaseSqmToSqlAstConverter method prepareForSelection.
private void prepareForSelection(SqmPath<?> selectionPath) {
final SqmPath<?> path;
// through a cardinality preserving mechanism in visitIndexAggregateFunction/visitElementAggregateFunction
if (selectionPath instanceof AbstractSqmSpecificPluralPartPath<?>) {
path = selectionPath.getLhs().getLhs();
} else {
path = selectionPath;
}
final FromClauseIndex fromClauseIndex = getFromClauseIndex();
final TableGroup tableGroup = fromClauseIndex.findTableGroup(path.getNavigablePath());
if (tableGroup == null) {
prepareReusablePath(path, () -> null);
final NavigablePath navigablePath;
if (CollectionPart.Nature.fromNameExact(path.getNavigablePath().getUnaliasedLocalName()) != null) {
navigablePath = path.getLhs().getLhs().getNavigablePath();
} else if (path instanceof SqmTreatedRoot<?, ?>) {
navigablePath = ((SqmTreatedRoot<?, ?>) path).getWrappedPath().getNavigablePath();
} else {
navigablePath = path.getLhs().getNavigablePath();
}
final TableGroup createdTableGroup = createTableGroup(fromClauseIndex.getTableGroup(navigablePath), path);
if (createdTableGroup != null) {
if (path instanceof SqmTreatedPath<?, ?>) {
fromClauseIndex.register(path, createdTableGroup);
}
if (path instanceof SqmFrom<?, ?>) {
registerTreatUsage((SqmFrom<?, ?>) path, createdTableGroup);
}
}
} else if (path instanceof SqmFrom<?, ?>) {
registerTreatUsage((SqmFrom<?, ?>) path, tableGroup);
}
}
use of org.hibernate.query.sqm.tree.from.SqmFrom in project hibernate-orm by hibernate.
the class BaseSqmToSqlAstConverter method consumeFromClauseCorrelatedRoot.
protected void consumeFromClauseCorrelatedRoot(SqmRoot<?> sqmRoot) {
log.tracef("Resolving SqmRoot [%s] to TableGroup", sqmRoot);
final FromClauseIndex fromClauseIndex = getFromClauseIndex();
if (fromClauseIndex.isResolved(sqmRoot)) {
log.tracef("Already resolved SqmRoot [%s] to TableGroup", sqmRoot);
}
final QuerySpec currentQuerySpec = currentQuerySpec();
final TableGroup tableGroup;
if (!sqmRoot.isCorrelated()) {
return;
}
final SessionFactoryImplementor sessionFactory = creationContext.getSessionFactory();
if (sqmRoot.containsOnlyInnerJoins()) {
// If we have just inner joins against a correlated root, we can render the joins as references
final SqmFrom<?, ?> from;
// It will always contain just a single correlated join though, which is what is actually correlated
if (sqmRoot instanceof SqmCorrelatedRootJoin<?>) {
assert sqmRoot.getSqmJoins().size() == 1;
assert sqmRoot.getSqmJoins().get(0).isCorrelated();
from = sqmRoot.getSqmJoins().get(0);
} else {
from = sqmRoot;
}
final TableGroup parentTableGroup = fromClauseIndex.findTableGroupOnParents(from.getCorrelationParent().getNavigablePath());
final SqlAliasBase sqlAliasBase = sqlAliasBaseManager.createSqlAliasBase(parentTableGroup.getGroupAlias());
if (parentTableGroup instanceof PluralTableGroup) {
final PluralTableGroup pluralTableGroup = (PluralTableGroup) parentTableGroup;
final CorrelatedPluralTableGroup correlatedPluralTableGroup = new CorrelatedPluralTableGroup(parentTableGroup, sqlAliasBase, currentQuerySpec, predicate -> additionalRestrictions = SqlAstTreeHelper.combinePredicates(additionalRestrictions, predicate), sessionFactory);
final TableGroup elementTableGroup = pluralTableGroup.getElementTableGroup();
if (elementTableGroup != null) {
final TableGroup correlatedElementTableGroup = new CorrelatedTableGroup(elementTableGroup, sqlAliasBase, currentQuerySpec, predicate -> additionalRestrictions = SqlAstTreeHelper.combinePredicates(additionalRestrictions, predicate), sessionFactory);
final TableGroupJoin tableGroupJoin = new TableGroupJoin(elementTableGroup.getNavigablePath(), SqlAstJoinType.INNER, correlatedElementTableGroup);
correlatedPluralTableGroup.registerElementTableGroup(tableGroupJoin);
}
final TableGroup indexTableGroup = pluralTableGroup.getIndexTableGroup();
if (indexTableGroup != null) {
final TableGroup correlatedIndexTableGroup = new CorrelatedTableGroup(indexTableGroup, sqlAliasBase, currentQuerySpec, predicate -> additionalRestrictions = SqlAstTreeHelper.combinePredicates(additionalRestrictions, predicate), sessionFactory);
final TableGroupJoin tableGroupJoin = new TableGroupJoin(indexTableGroup.getNavigablePath(), SqlAstJoinType.INNER, correlatedIndexTableGroup);
correlatedPluralTableGroup.registerIndexTableGroup(tableGroupJoin);
}
tableGroup = correlatedPluralTableGroup;
} else {
tableGroup = new CorrelatedTableGroup(parentTableGroup, sqlAliasBase, currentQuerySpec, predicate -> additionalRestrictions = SqlAstTreeHelper.combinePredicates(additionalRestrictions, predicate), sessionFactory);
}
fromClauseIndex.register(from, tableGroup);
registerPluralTableGroupParts(tableGroup);
log.tracef("Resolved SqmRoot [%s] to correlated TableGroup [%s]", sqmRoot, tableGroup);
consumeExplicitJoins(from, tableGroup);
return;
} else {
final EntityPersister entityDescriptor = resolveEntityPersister(sqmRoot.getReferencedPathSource());
final TableGroup parentTableGroup = fromClauseIndex.findTableGroupOnParents(sqmRoot.getCorrelationParent().getNavigablePath());
// If we have non-inner joins against a correlated root, we must render the root with a correlation predicate
tableGroup = entityDescriptor.createRootTableGroup(true, sqmRoot.getNavigablePath(), sqmRoot.getExplicitAlias(), () -> predicate -> {
}, this, creationContext);
final EntityIdentifierMapping identifierMapping = entityDescriptor.getIdentifierMapping();
final NavigablePath navigablePath = sqmRoot.getNavigablePath().append(identifierMapping.getNavigableRole().getNavigableName());
final int jdbcTypeCount = identifierMapping.getJdbcTypeCount();
if (jdbcTypeCount == 1) {
identifierMapping.forEachSelectable((index, selectable) -> additionalRestrictions = SqlAstTreeHelper.combinePredicates(additionalRestrictions, new ComparisonPredicate(new ColumnReference(parentTableGroup.resolveTableReference(navigablePath, selectable.getContainingTableExpression()), selectable, sessionFactory), ComparisonOperator.EQUAL, new ColumnReference(tableGroup.resolveTableReference(navigablePath, selectable.getContainingTableExpression()), selectable, sessionFactory))));
} else {
final List<Expression> lhs = new ArrayList<>(jdbcTypeCount);
final List<Expression> rhs = new ArrayList<>(jdbcTypeCount);
identifierMapping.forEachSelectable((index, selectable) -> {
lhs.add(new ColumnReference(parentTableGroup.resolveTableReference(navigablePath, selectable.getContainingTableExpression()), selectable, sessionFactory));
rhs.add(new ColumnReference(tableGroup.resolveTableReference(navigablePath, selectable.getContainingTableExpression()), selectable, sessionFactory));
});
additionalRestrictions = SqlAstTreeHelper.combinePredicates(additionalRestrictions, new ComparisonPredicate(new SqlTuple(lhs, identifierMapping), ComparisonOperator.EQUAL, new SqlTuple(rhs, identifierMapping)));
}
}
log.tracef("Resolved SqmRoot [%s] to new TableGroup [%s]", sqmRoot, tableGroup);
fromClauseIndex.register(sqmRoot, tableGroup);
currentQuerySpec.getFromClause().addRoot(tableGroup);
consumeJoins(sqmRoot, fromClauseIndex, tableGroup);
}
Aggregations