use of org.hibernate.query.sqm.sql.internal.SqlAstQueryPartProcessingStateImpl in project hibernate-orm by hibernate.
the class CteDeleteHandler method addDmlCtes.
@Override
protected void addDmlCtes(CteContainer statement, CteStatement idSelectCte, MultiTableSqmMutationConverter sqmConverter, Map<SqmParameter, List<JdbcParameter>> parameterResolutions, SessionFactoryImplementor factory) {
final TableGroup updatingTableGroup = sqmConverter.getMutatingTableGroup();
final SelectStatement idSelectStatement = (SelectStatement) idSelectCte.getCteDefinition();
sqmConverter.getProcessingStateStack().push(new SqlAstQueryPartProcessingStateImpl(idSelectStatement.getQuerySpec(), sqmConverter.getCurrentProcessingState(), sqmConverter.getSqlAstCreationState(), sqmConverter.getCurrentClauseStack()::getCurrent, false));
getEntityDescriptor().visitSubTypeAttributeMappings(attribute -> {
if (attribute instanceof PluralAttributeMapping) {
final PluralAttributeMapping pluralAttribute = (PluralAttributeMapping) attribute;
if (pluralAttribute.getSeparateCollectionTable() != null) {
// Ensure that the FK target columns are available
final boolean useFkTarget = !(pluralAttribute.getKeyDescriptor().getTargetPart() instanceof EntityIdentifierMapping);
if (useFkTarget) {
final TableGroup mutatingTableGroup = sqmConverter.getMutatingTableGroup();
pluralAttribute.getKeyDescriptor().getTargetPart().applySqlSelections(mutatingTableGroup.getNavigablePath(), mutatingTableGroup, sqmConverter, (selection, jdbcMapping) -> {
idSelectStatement.getDomainResultDescriptors().add(new BasicResult<>(selection.getValuesArrayPosition(), null, jdbcMapping.getJavaTypeDescriptor()));
});
}
// this collection has a separate collection table, meaning it is one of:
// 1) element-collection
// 2) many-to-many
// 3) one-to many using a dedicated join-table
//
// in all of these cases, we should clean up the matching rows in the
// collection table
final String tableExpression = pluralAttribute.getSeparateCollectionTable();
final CteTable dmlResultCte = new CteTable(getCteTableName(pluralAttribute), idSelectCte.getCteTable().getCteColumns(), factory);
final NamedTableReference dmlTableReference = new NamedTableReference(tableExpression, DeleteStatement.DEFAULT_ALIAS, true, factory);
final List<ColumnReference> columnReferences = new ArrayList<>(idSelectCte.getCteTable().getCteColumns().size());
pluralAttribute.getKeyDescriptor().visitKeySelectables((index, selectable) -> columnReferences.add(new ColumnReference(dmlTableReference, selectable, factory)));
final MutationStatement dmlStatement = new DeleteStatement(dmlTableReference, createIdSubQueryPredicate(columnReferences, idSelectCte, useFkTarget ? pluralAttribute.getKeyDescriptor().getTargetPart() : null, factory), columnReferences);
statement.addCteStatement(new CteStatement(dmlResultCte, dmlStatement));
}
}
});
sqmConverter.getProcessingStateStack().pop();
getEntityDescriptor().visitConstraintOrderedTables((tableExpression, tableColumnsVisitationSupplier) -> {
final CteTable dmlResultCte = new CteTable(getCteTableName(tableExpression), idSelectCte.getCteTable().getCteColumns(), factory);
final TableReference updatingTableReference = updatingTableGroup.getTableReference(updatingTableGroup.getNavigablePath(), tableExpression, true, true);
final NamedTableReference dmlTableReference = resolveUnionTableReference(updatingTableReference, tableExpression);
final List<ColumnReference> columnReferences = new ArrayList<>(idSelectCte.getCteTable().getCteColumns().size());
tableColumnsVisitationSupplier.get().accept((index, selectable) -> columnReferences.add(new ColumnReference(dmlTableReference, selectable, factory)));
final MutationStatement dmlStatement = new DeleteStatement(dmlTableReference, createIdSubQueryPredicate(columnReferences, idSelectCte, factory), columnReferences);
statement.addCteStatement(new CteStatement(dmlResultCte, dmlStatement));
});
}
use of org.hibernate.query.sqm.sql.internal.SqlAstQueryPartProcessingStateImpl in project hibernate-orm by hibernate.
the class MatchingIdSelectionHelper method selectMatchingIds.
/**
* Centralized selection of ids matching the restriction of the DELETE
* or UPDATE SQM query
*/
public static List<Object> selectMatchingIds(SqmDeleteOrUpdateStatement sqmMutationStatement, DomainParameterXref domainParameterXref, DomainQueryExecutionContext executionContext) {
final SessionFactoryImplementor factory = executionContext.getSession().getFactory();
final EntityMappingType entityDescriptor = factory.getRuntimeMetamodels().getEntityMappingType(sqmMutationStatement.getTarget().getModel().getHibernateEntityName());
final MultiTableSqmMutationConverter sqmConverter = new MultiTableSqmMutationConverter(entityDescriptor, sqmMutationStatement, sqmMutationStatement.getTarget(), domainParameterXref, executionContext.getQueryOptions(), executionContext.getSession().getLoadQueryInfluencers(), executionContext.getQueryParameterBindings(), factory);
final Map<SqmParameter, List<JdbcParameter>> parameterResolutions;
if (domainParameterXref.getSqmParameterCount() == 0) {
parameterResolutions = Collections.emptyMap();
} else {
parameterResolutions = new IdentityHashMap<>();
}
final Predicate restriction = sqmConverter.visitWhereClause(sqmMutationStatement.getWhereClause(), columnReference -> {
}, (sqmParam, mappingType, jdbcParameters) -> parameterResolutions.put(sqmParam, jdbcParameters));
final SelectStatement matchingIdSelection = generateMatchingIdSelectStatement(entityDescriptor, sqmMutationStatement, true, restriction, sqmConverter, executionContext, factory);
sqmConverter.getProcessingStateStack().push(new SqlAstQueryPartProcessingStateImpl(matchingIdSelection.getQuerySpec(), sqmConverter.getCurrentProcessingState(), sqmConverter.getSqlAstCreationState(), sqmConverter.getCurrentClauseStack()::getCurrent, true));
entityDescriptor.visitSubTypeAttributeMappings(attribute -> {
if (attribute instanceof PluralAttributeMapping) {
final PluralAttributeMapping pluralAttribute = (PluralAttributeMapping) attribute;
if (pluralAttribute.getSeparateCollectionTable() != null) {
// Ensure that the FK target columns are available
final boolean useFkTarget = !(pluralAttribute.getKeyDescriptor().getTargetPart() instanceof EntityIdentifierMapping);
if (useFkTarget) {
final TableGroup mutatingTableGroup = sqmConverter.getMutatingTableGroup();
pluralAttribute.getKeyDescriptor().getTargetPart().applySqlSelections(mutatingTableGroup.getNavigablePath(), mutatingTableGroup, sqmConverter, (selection, jdbcMapping) -> {
matchingIdSelection.getDomainResultDescriptors().add(new BasicResult<>(selection.getValuesArrayPosition(), null, jdbcMapping.getJavaTypeDescriptor()));
});
}
}
}
});
sqmConverter.getProcessingStateStack().pop();
final JdbcServices jdbcServices = factory.getJdbcServices();
final JdbcEnvironment jdbcEnvironment = jdbcServices.getJdbcEnvironment();
final SqlAstTranslator<JdbcSelect> sqlAstSelectTranslator = jdbcEnvironment.getSqlAstTranslatorFactory().buildSelectTranslator(factory, matchingIdSelection);
final JdbcParameterBindings jdbcParameterBindings = SqmUtil.createJdbcParameterBindings(executionContext.getQueryParameterBindings(), domainParameterXref, SqmUtil.generateJdbcParamsXref(domainParameterXref, sqmConverter), factory.getRuntimeMetamodels().getMappingMetamodel(), navigablePath -> sqmConverter.getMutatingTableGroup(), new SqmParameterMappingModelResolutionAccess() {
@Override
@SuppressWarnings("unchecked")
public <T> MappingModelExpressible<T> getResolvedMappingModelType(SqmParameter<T> parameter) {
return (MappingModelExpressible<T>) sqmConverter.getSqmParameterMappingModelExpressibleResolutions().get(parameter);
}
}, executionContext.getSession());
final LockOptions lockOptions = executionContext.getQueryOptions().getLockOptions();
final LockMode lockMode = lockOptions.getLockMode();
// Acquire a WRITE lock for the rows that are about to be modified
lockOptions.setLockMode(LockMode.WRITE);
// Visit the table joins and reset the lock mode if we encounter OUTER joins that are not supported
if (!jdbcEnvironment.getDialect().supportsOuterJoinForUpdate()) {
matchingIdSelection.getQuerySpec().getFromClause().visitTableJoins(tableJoin -> {
if (tableJoin.getJoinType() != SqlAstJoinType.INNER) {
lockOptions.setLockMode(lockMode);
}
});
}
final JdbcSelect idSelectJdbcOperation = sqlAstSelectTranslator.translate(jdbcParameterBindings, executionContext.getQueryOptions());
lockOptions.setLockMode(lockMode);
return jdbcServices.getJdbcSelectExecutor().list(idSelectJdbcOperation, jdbcParameterBindings, SqmJdbcExecutionContextAdapter.omittingLockingAndPaging(executionContext), row -> row[0], ListResultsConsumer.UniqueSemantic.FILTER);
}
use of org.hibernate.query.sqm.sql.internal.SqlAstQueryPartProcessingStateImpl in project hibernate-orm by hibernate.
the class BaseSqmToSqlAstConverter method createLateralJoinExpression.
protected Expression createLateralJoinExpression(AbstractSqmSpecificPluralPartPath<?> pluralPartPath, boolean index, String functionName) {
prepareReusablePath(pluralPartPath.getLhs(), () -> null);
final PluralAttributeMapping pluralAttributeMapping = (PluralAttributeMapping) determineValueMapping(pluralPartPath.getPluralDomainPath());
final FromClauseAccess parentFromClauseAccess = getFromClauseAccess();
final TableGroup parentTableGroup = parentFromClauseAccess.findTableGroup(pluralPartPath.getNavigablePath().getParent());
final CollectionPart collectionPart = index ? pluralAttributeMapping.getIndexDescriptor() : pluralAttributeMapping.getElementDescriptor();
final ModelPart modelPart;
if (collectionPart instanceof EntityAssociationMapping) {
modelPart = ((EntityAssociationMapping) collectionPart).getKeyTargetMatchPart();
} else {
modelPart = collectionPart;
}
final int jdbcTypeCount = modelPart.getJdbcTypeCount();
final String pathName = functionName + (index ? "_index" : "_element");
final String identifierVariable = parentTableGroup.getPrimaryTableReference().getIdentificationVariable() + "_" + pathName;
final NavigablePath queryPath = new NavigablePath(parentTableGroup.getNavigablePath(), pathName, identifierVariable);
TableGroup lateralTableGroup = parentFromClauseAccess.findTableGroup(queryPath);
if (lateralTableGroup == null) {
final QuerySpec subQuerySpec = new QuerySpec(false);
pushProcessingState(new SqlAstQueryPartProcessingStateImpl(subQuerySpec, getCurrentProcessingState(), this, currentClauseStack::getCurrent, false));
try {
final TableGroup tableGroup = pluralAttributeMapping.createRootTableGroup(true, pluralPartPath.getNavigablePath(), null, () -> subQuerySpec::applyPredicate, this, creationContext);
pluralAttributeMapping.applyBaseRestrictions(subQuerySpec::applyPredicate, tableGroup, true, getLoadQueryInfluencers().getEnabledFilters(), null, this);
getFromClauseAccess().registerTableGroup(pluralPartPath.getNavigablePath(), tableGroup);
registerPluralTableGroupParts(tableGroup);
subQuerySpec.getFromClause().addRoot(tableGroup);
final List<String> columnNames = new ArrayList<>(jdbcTypeCount);
final List<ColumnReference> resultColumnReferences = new ArrayList<>(jdbcTypeCount);
final NavigablePath navigablePath = pluralPartPath.getNavigablePath();
final Boolean max = functionName.equalsIgnoreCase("max") ? Boolean.TRUE : (functionName.equalsIgnoreCase("min") ? Boolean.FALSE : null);
final AbstractSqmSelfRenderingFunctionDescriptor functionDescriptor = (AbstractSqmSelfRenderingFunctionDescriptor) creationContext.getSessionFactory().getQueryEngine().getSqmFunctionRegistry().findFunctionDescriptor(functionName);
final List<ColumnReference> subQueryColumns = new ArrayList<>(jdbcTypeCount);
modelPart.forEachSelectable((selectionIndex, selectionMapping) -> {
final ColumnReference columnReference = new ColumnReference(tableGroup.resolveTableReference(navigablePath, selectionMapping.getContainingTableExpression()), selectionMapping, creationContext.getSessionFactory());
final String columnName;
if (selectionMapping.isFormula()) {
columnName = "col" + columnNames.size();
} else {
columnName = selectionMapping.getSelectionExpression();
}
columnNames.add(columnName);
subQueryColumns.add(columnReference);
if (max != null) {
subQuerySpec.addSortSpecification(new SortSpecification(columnReference, max ? SortOrder.DESCENDING : SortOrder.ASCENDING));
}
});
if (max != null) {
for (int i = 0; i < subQueryColumns.size(); i++) {
subQuerySpec.getSelectClause().addSqlSelection(new SqlSelectionImpl(i + 1, i, subQueryColumns.get(i)));
resultColumnReferences.add(new ColumnReference(identifierVariable, columnNames.get(i), false, null, null, subQueryColumns.get(i).getJdbcMapping(), creationContext.getSessionFactory()));
}
subQuerySpec.setFetchClauseExpression(new QueryLiteral<>(1, basicType(Integer.class)), FetchClauseType.ROWS_ONLY);
} else {
final List<? extends SqlAstNode> arguments;
if (jdbcTypeCount == 1) {
arguments = subQueryColumns;
} else {
arguments = Collections.singletonList(new SqlTuple(subQueryColumns, modelPart));
}
final Expression expression = new SelfRenderingAggregateFunctionSqlAstExpression(functionDescriptor.getName(), functionDescriptor, arguments, null, (ReturnableType<?>) functionDescriptor.getReturnTypeResolver().resolveFunctionReturnType(() -> null, arguments).getJdbcMapping(), modelPart);
subQuerySpec.getSelectClause().addSqlSelection(new SqlSelectionImpl(1, 0, expression));
resultColumnReferences.add(new ColumnReference(identifierVariable, columnNames.get(0), false, null, null, expression.getExpressionType().getJdbcMappings().get(0), creationContext.getSessionFactory()));
}
subQuerySpec.applyPredicate(pluralAttributeMapping.getKeyDescriptor().generateJoinPredicate(parentFromClauseAccess.findTableGroup(pluralPartPath.getPluralDomainPath().getNavigablePath().getParent()), tableGroup, getSqlExpressionResolver(), creationContext));
final String compatibleTableExpression;
if (modelPart instanceof BasicValuedModelPart) {
compatibleTableExpression = ((BasicValuedModelPart) modelPart).getContainingTableExpression();
} else if (modelPart instanceof EmbeddableValuedModelPart) {
compatibleTableExpression = ((EmbeddableValuedModelPart) modelPart).getContainingTableExpression();
} else {
compatibleTableExpression = null;
}
lateralTableGroup = new QueryPartTableGroup(queryPath, null, subQuerySpec, identifierVariable, columnNames, compatibleTableExpression, true, false, creationContext.getSessionFactory());
if (currentlyProcessingJoin == null) {
parentTableGroup.addTableGroupJoin(new TableGroupJoin(lateralTableGroup.getNavigablePath(), SqlAstJoinType.LEFT, lateralTableGroup));
} else {
// In case this is used in the ON condition, we must prepend this lateral join
final TableGroup targetTableGroup;
if (currentlyProcessingJoin.getLhs() == null) {
targetTableGroup = parentFromClauseAccess.getTableGroup(currentlyProcessingJoin.findRoot().getNavigablePath());
} else {
targetTableGroup = parentFromClauseAccess.getTableGroup(currentlyProcessingJoin.getLhs().getNavigablePath());
}
// Many databases would support modelling this as nested table group join,
// but at least SQL Server doesn't like that, saying that the correlated columns can't be "bound"
// Since there is no dependency on the currentlyProcessingJoin, we can safely prepend this join
targetTableGroup.prependTableGroupJoin(currentlyProcessingJoin.getNavigablePath(), new TableGroupJoin(lateralTableGroup.getNavigablePath(), SqlAstJoinType.LEFT, lateralTableGroup));
}
parentFromClauseAccess.registerTableGroup(lateralTableGroup.getNavigablePath(), lateralTableGroup);
if (jdbcTypeCount == 1) {
return new SelfRenderingFunctionSqlAstExpression(pathName, (sqlAppender, sqlAstArguments, walker) -> {
sqlAstArguments.get(0).accept(walker);
}, resultColumnReferences, (ReturnableType<?>) resultColumnReferences.get(0).getJdbcMapping(), resultColumnReferences.get(0).getJdbcMapping());
} else {
return new SqlTuple(resultColumnReferences, modelPart);
}
} finally {
popProcessingStateStack();
}
}
final QueryPartTableReference tableReference = (QueryPartTableReference) lateralTableGroup.getPrimaryTableReference();
if (jdbcTypeCount == 1) {
final List<SqlSelection> sqlSelections = tableReference.getQueryPart().getFirstQuerySpec().getSelectClause().getSqlSelections();
return new SelfRenderingFunctionSqlAstExpression(pathName, (sqlAppender, sqlAstArguments, walker) -> {
sqlAstArguments.get(0).accept(walker);
}, Collections.singletonList(new ColumnReference(identifierVariable, tableReference.getColumnNames().get(0), false, null, null, sqlSelections.get(0).getExpressionType().getJdbcMappings().get(0), creationContext.getSessionFactory())), (ReturnableType<?>) sqlSelections.get(0).getExpressionType().getJdbcMappings().get(0), sqlSelections.get(0).getExpressionType());
} else {
final List<ColumnReference> resultColumnReferences = new ArrayList<>(jdbcTypeCount);
modelPart.forEachSelectable((selectionIndex, selectionMapping) -> resultColumnReferences.add(new ColumnReference(identifierVariable, tableReference.getColumnNames().get(selectionIndex), false, null, null, selectionMapping.getJdbcMapping(), creationContext.getSessionFactory())));
return new SqlTuple(resultColumnReferences, modelPart);
}
}
use of org.hibernate.query.sqm.sql.internal.SqlAstQueryPartProcessingStateImpl 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.query.sqm.sql.internal.SqlAstQueryPartProcessingStateImpl in project hibernate-orm by hibernate.
the class BaseSqmToSqlAstConverter method visitQuerySpec.
@Override
public QuerySpec visitQuerySpec(SqmQuerySpec<?> sqmQuerySpec) {
final boolean topLevel = getProcessingStateStack().isEmpty();
final QuerySpec sqlQuerySpec = new QuerySpec(topLevel, sqmQuerySpec.getFromClause().getNumberOfRoots());
final SqmSelectClause selectClause = sqmQuerySpec.getSelectClause();
final Predicate originalAdditionalRestrictions = additionalRestrictions;
additionalRestrictions = null;
final boolean trackAliasedNodePositions;
if (trackSelectionsForGroup) {
trackAliasedNodePositions = true;
} else if (sqmQuerySpec.getOrderByClause() != null && sqmQuerySpec.getOrderByClause().hasPositionalSortItem()) {
trackAliasedNodePositions = true;
} else if (sqmQuerySpec.hasPositionalGroupItem()) {
trackAliasedNodePositions = true;
} else {
// Since JPA Criteria queries can use the same expression object in order or group by items,
// we need to track the positions to be able to replace the expression in the items with alias references
// Also see #resolveGroupOrOrderByExpression for more details
trackAliasedNodePositions = statement.getQuerySource() == SqmQuerySource.CRITERIA && (sqmQuerySpec.getOrderByClause() != null || !sqmQuerySpec.getGroupByClauseExpressions().isEmpty());
}
final SqlAstProcessingState processingState;
if (trackAliasedNodePositions) {
processingState = new SqlAstQueryPartProcessingStateImpl(sqlQuerySpec, getCurrentProcessingState(), this, r -> new SqmAliasedNodePositionTracker(r, selectClause.getSelections()), currentClauseStack::getCurrent, deduplicateSelectionItems);
} else {
processingState = new SqlAstQueryPartProcessingStateImpl(sqlQuerySpec, getCurrentProcessingState(), this, currentClauseStack::getCurrent, deduplicateSelectionItems);
}
final SqmQueryPart<?> sqmQueryPart = currentSqmQueryPart;
final boolean originalDeduplicateSelectionItems = deduplicateSelectionItems;
currentSqmQueryPart = sqmQuerySpec;
// In sub-queries, we can never deduplicate the selection items as that might change semantics
deduplicateSelectionItems = false;
pushProcessingState(processingState);
queryTransformers.push(new ArrayList<>());
try {
// we want to visit the from-clause first
visitFromClause(sqmQuerySpec.getFromClause());
visitSelectClause(selectClause);
final SqmWhereClause whereClause = sqmQuerySpec.getWhereClause();
if (whereClause != null) {
sqlQuerySpec.applyPredicate(visitWhereClause(whereClause.getPredicate()));
}
sqlQuerySpec.setGroupByClauseExpressions(visitGroupByClause(sqmQuerySpec.getGroupByClauseExpressions()));
if (sqmQuerySpec.getHavingClausePredicate() != null) {
sqlQuerySpec.setHavingClauseRestrictions(visitHavingClause(sqmQuerySpec.getHavingClausePredicate()));
}
visitOrderByOffsetAndFetch(sqmQuerySpec, sqlQuerySpec);
if (topLevel && statement instanceof SqmSelectStatement<?>) {
if (orderByFragments != null) {
orderByFragments.forEach(entry -> entry.getKey().apply(sqlQuerySpec, entry.getValue(), this));
orderByFragments = null;
}
applyCollectionFilterPredicates(sqlQuerySpec);
}
QuerySpec finalQuerySpec = sqlQuerySpec;
for (QueryTransformer transformer : queryTransformers.getCurrent()) {
finalQuerySpec = transformer.transform(cteContainer, finalQuerySpec, this);
}
return finalQuerySpec;
} finally {
if (additionalRestrictions != null) {
sqlQuerySpec.applyPredicate(additionalRestrictions);
}
additionalRestrictions = originalAdditionalRestrictions;
popProcessingStateStack();
queryTransformers.pop();
currentSqmQueryPart = sqmQueryPart;
deduplicateSelectionItems = originalDeduplicateSelectionItems;
}
}
Aggregations