use of org.hibernate.sql.ast.tree.select.QuerySpec in project hibernate-orm by hibernate.
the class MatchingIdSelectionHelper method generateMatchingIdSelectStatement.
/**
* @asciidoc
*
* Generates a query-spec for selecting all ids matching the restriction defined as part
* of the user's update/delete query. This query-spec is generally used:
*
* * to select all the matching ids via JDBC - see {@link MatchingIdSelectionHelper#selectMatchingIds}
* * as a sub-query restriction to insert rows into an "id table"
*/
public static SelectStatement generateMatchingIdSelectStatement(EntityMappingType targetEntityDescriptor, SqmDeleteOrUpdateStatement sqmStatement, boolean queryRoot, Predicate restriction, MultiTableSqmMutationConverter sqmConverter, DomainQueryExecutionContext executionContext, SessionFactoryImplementor sessionFactory) {
final EntityDomainType entityDomainType = sqmStatement.getTarget().getModel();
if (log.isTraceEnabled()) {
log.tracef("Starting generation of entity-id SQM selection - %s", entityDomainType.getHibernateEntityName());
}
final QuerySpec idSelectionQuery = new QuerySpec(queryRoot, 1);
idSelectionQuery.applyPredicate(restriction);
final TableGroup mutatingTableGroup = sqmConverter.getMutatingTableGroup();
idSelectionQuery.getFromClause().addRoot(mutatingTableGroup);
final List<DomainResult<?>> domainResults = new ArrayList<>();
sqmConverter.getProcessingStateStack().push(new SqlAstQueryPartProcessingStateImpl(idSelectionQuery, sqmConverter.getCurrentProcessingState(), sqmConverter.getSqlAstCreationState(), sqmConverter.getCurrentClauseStack()::getCurrent, false));
targetEntityDescriptor.getIdentifierMapping().applySqlSelections(mutatingTableGroup.getNavigablePath(), mutatingTableGroup, sqmConverter, (selection, jdbcMapping) -> {
domainResults.add(new BasicResult<>(selection.getValuesArrayPosition(), null, jdbcMapping.getJavaTypeDescriptor()));
});
sqmConverter.getProcessingStateStack().pop();
targetEntityDescriptor.getEntityPersister().applyBaseRestrictions(idSelectionQuery::applyPredicate, mutatingTableGroup, true, executionContext.getSession().getLoadQueryInfluencers().getEnabledFilters(), null, sqmConverter);
return new SelectStatement(idSelectionQuery, domainResults);
}
use of org.hibernate.sql.ast.tree.select.QuerySpec in project hibernate-orm by hibernate.
the class AbstractCteMutationHandler method createIdSubQuery.
protected QuerySpec createIdSubQuery(CteStatement idSelectCte, ModelPart fkModelPart, SessionFactoryImplementor factory) {
final NamedTableReference idSelectTableReference = new NamedTableReference(idSelectCte.getCteTable().getTableExpression(), CTE_TABLE_IDENTIFIER, false, factory);
final List<CteColumn> cteColumns = idSelectCte.getCteTable().getCteColumns();
final int size = cteColumns.size();
final QuerySpec subQuery = new QuerySpec(false, 1);
subQuery.getFromClause().addRoot(new CteTableGroup(idSelectTableReference));
final SelectClause subQuerySelectClause = subQuery.getSelectClause();
if (fkModelPart == null) {
for (int i = 0; i < size; i++) {
final CteColumn cteColumn = cteColumns.get(i);
subQuerySelectClause.addSqlSelection(new SqlSelectionImpl(i + 1, i, new ColumnReference(idSelectTableReference, cteColumn.getColumnExpression(), cteColumn.getJdbcMapping(), factory)));
}
} else {
fkModelPart.forEachSelectable((selectionIndex, selectableMapping) -> {
subQuerySelectClause.addSqlSelection(new SqlSelectionImpl(selectionIndex + 1, selectionIndex, new ColumnReference(idSelectTableReference, selectableMapping.getSelectionExpression(), selectableMapping.getJdbcMapping(), factory)));
});
}
return subQuery;
}
use of org.hibernate.sql.ast.tree.select.QuerySpec in project hibernate-orm by hibernate.
the class UpdateExecutionDelegate method updateTable.
private void updateTable(String tableExpression, Supplier<Consumer<SelectableConsumer>> tableKeyColumnVisitationSupplier, int expectedUpdateCount, QuerySpec idTableSubQuery, ExecutionContext executionContext) {
final TableReference updatingTableReference = updatingTableGroup.getTableReference(updatingTableGroup.getNavigablePath(), tableExpression, true, true);
final List<Assignment> assignments = assignmentsByTable.get(updatingTableReference);
if (assignments == null || assignments.isEmpty()) {
// no assignments for this table - skip it
return;
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// create the in-subquery predicate to restrict the updates to just
// matching ids
final TableKeyExpressionCollector keyColumnCollector = new TableKeyExpressionCollector(entityDescriptor);
tableKeyColumnVisitationSupplier.get().accept((columnIndex, selection) -> {
assert selection.getContainingTableExpression().equals(tableExpression);
keyColumnCollector.apply(new ColumnReference((String) null, selection, sessionFactory));
});
final Expression keyExpression = keyColumnCollector.buildKeyExpression();
final InSubQueryPredicate idTableSubQueryPredicate = new InSubQueryPredicate(keyExpression, idTableSubQuery, false);
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Create the SQL AST and convert it into a JdbcOperation
final NamedTableReference dmlTableReference = resolveUnionTableReference(updatingTableReference, tableExpression);
final UpdateStatement sqlAst = new UpdateStatement(dmlTableReference, assignments, idTableSubQueryPredicate);
final JdbcServices jdbcServices = sessionFactory.getJdbcServices();
final JdbcUpdate jdbcUpdate = jdbcServices.getJdbcEnvironment().getSqlAstTranslatorFactory().buildUpdateTranslator(sessionFactory, sqlAst).translate(jdbcParameterBindings, executionContext.getQueryOptions());
final int updateCount = jdbcServices.getJdbcMutationExecutor().execute(jdbcUpdate, jdbcParameterBindings, sql -> executionContext.getSession().getJdbcCoordinator().getStatementPreparer().prepareStatement(sql), (integer, preparedStatement) -> {
}, executionContext);
if (updateCount == expectedUpdateCount) {
// We are done when the update count matches
return;
}
// Otherwise we have to check if the table is nullable, and if so, insert into that table
final AbstractEntityPersister entityPersister = (AbstractEntityPersister) entityDescriptor.getEntityPersister();
boolean isNullable = false;
for (int i = 0; i < entityPersister.getTableSpan(); i++) {
if (tableExpression.equals(entityPersister.getTableName(i)) && entityPersister.isNullableTable(i)) {
isNullable = true;
break;
}
}
if (isNullable) {
// Copy the subquery contents into a root query
final QuerySpec querySpec = new QuerySpec(true);
for (TableGroup root : idTableSubQuery.getFromClause().getRoots()) {
querySpec.getFromClause().addRoot(root);
}
for (SqlSelection sqlSelection : idTableSubQuery.getSelectClause().getSqlSelections()) {
querySpec.getSelectClause().addSqlSelection(sqlSelection);
}
querySpec.applyPredicate(idTableSubQuery.getWhereClauseRestrictions());
// Prepare a not exists sub-query to avoid violating constraints
final QuerySpec existsQuerySpec = new QuerySpec(false);
existsQuerySpec.getSelectClause().addSqlSelection(new SqlSelectionImpl(-1, 0, new QueryLiteral<>(1, sessionFactory.getTypeConfiguration().getBasicTypeForJavaType(Integer.class))));
final NamedTableReference existsTableReference = new NamedTableReference(tableExpression, "dml_", false, sessionFactory);
existsQuerySpec.getFromClause().addRoot(new TableGroupImpl(null, null, existsTableReference, entityPersister));
final TableKeyExpressionCollector existsKeyColumnCollector = new TableKeyExpressionCollector(entityDescriptor);
tableKeyColumnVisitationSupplier.get().accept((columnIndex, selection) -> {
assert selection.getContainingTableExpression().equals(tableExpression);
existsKeyColumnCollector.apply(new ColumnReference(existsTableReference, selection, sessionFactory));
});
existsQuerySpec.applyPredicate(new ComparisonPredicate(existsKeyColumnCollector.buildKeyExpression(), ComparisonOperator.EQUAL, asExpression(idTableSubQuery.getSelectClause())));
querySpec.applyPredicate(new ExistsPredicate(existsQuerySpec, true, sessionFactory.getTypeConfiguration().getBasicTypeForJavaType(Boolean.class)));
// Collect the target column references from the key expressions
final List<ColumnReference> targetColumnReferences = new ArrayList<>();
if (keyExpression instanceof SqlTuple) {
// noinspection unchecked
targetColumnReferences.addAll((Collection<? extends ColumnReference>) ((SqlTuple) keyExpression).getExpressions());
} else {
targetColumnReferences.add((ColumnReference) keyExpression);
}
// And transform assignments to target column references and selections
for (Assignment assignment : assignments) {
targetColumnReferences.addAll(assignment.getAssignable().getColumnReferences());
querySpec.getSelectClause().addSqlSelection(new SqlSelectionImpl(0, -1, assignment.getAssignedValue()));
}
final InsertStatement insertSqlAst = new InsertStatement(dmlTableReference);
insertSqlAst.addTargetColumnReferences(targetColumnReferences.toArray(new ColumnReference[0]));
insertSqlAst.setSourceSelectStatement(querySpec);
final JdbcInsert jdbcInsert = jdbcServices.getJdbcEnvironment().getSqlAstTranslatorFactory().buildInsertTranslator(sessionFactory, insertSqlAst).translate(jdbcParameterBindings, executionContext.getQueryOptions());
final int insertCount = jdbcServices.getJdbcMutationExecutor().execute(jdbcInsert, jdbcParameterBindings, sql -> executionContext.getSession().getJdbcCoordinator().getStatementPreparer().prepareStatement(sql), (integer, preparedStatement) -> {
}, executionContext);
assert insertCount + updateCount == expectedUpdateCount;
}
}
use of org.hibernate.sql.ast.tree.select.QuerySpec in project hibernate-orm by hibernate.
the class UpdateExecutionDelegate method execute.
@Override
public int execute(ExecutionContext executionContext) {
ExecuteWithTemporaryTableHelper.performBeforeTemporaryTableUseActions(idTable, executionContext);
try {
final int rows = ExecuteWithTemporaryTableHelper.saveMatchingIdsIntoIdTable(sqmConverter, suppliedPredicate, idTable, sessionUidAccess, jdbcParameterBindings, executionContext);
final QuerySpec idTableSubQuery = ExecuteWithTemporaryTableHelper.createIdTableSelectQuerySpec(idTable, sessionUidAccess, entityDescriptor, executionContext);
entityDescriptor.visitConstraintOrderedTables((tableExpression, tableKeyColumnVisitationSupplier) -> updateTable(tableExpression, tableKeyColumnVisitationSupplier, rows, idTableSubQuery, executionContext));
return rows;
} finally {
ExecuteWithTemporaryTableHelper.performAfterTemporaryTableUseActions(idTable, sessionUidAccess, afterUseAction, executionContext);
}
}
use of org.hibernate.sql.ast.tree.select.QuerySpec in project hibernate-orm by hibernate.
the class InsertExecutionDelegate method insertTable.
private void insertTable(String tableExpression, String[] keyColumns, boolean nullableTable, ExecutionContext executionContext) {
final TableReference updatingTableReference = updatingTableGroup.getTableReference(updatingTableGroup.getNavigablePath(), tableExpression, true, true);
final List<Assignment> assignments = assignmentsByTable.get(updatingTableReference);
if (nullableTable && (assignments == null || assignments.isEmpty())) {
// no assignments for this table - skip it
return;
}
final NamedTableReference dmlTargetTableReference = resolveUnionTableReference(updatingTableReference, tableExpression);
final QuerySpec querySpec = new QuerySpec(true);
final TableGroupImpl temporaryTableGroup = new TableGroupImpl(updatingTableGroup.getNavigablePath(), null, new NamedTableReference(insertStatement.getTargetTable().getTableExpression(), updatingTableReference.getIdentificationVariable(), false, sessionFactory), entityDescriptor);
querySpec.getFromClause().addRoot(temporaryTableGroup);
final InsertStatement insertStatement = new InsertStatement(dmlTargetTableReference);
insertStatement.setSourceSelectStatement(querySpec);
if (assignments != null && !assignments.isEmpty()) {
for (Assignment assignment : assignments) {
insertStatement.addTargetColumnReferences(assignment.getAssignable().getColumnReferences());
for (ColumnReference columnReference : assignment.getAssignable().getColumnReferences()) {
querySpec.getSelectClause().addSqlSelection(new SqlSelectionImpl(1, 0, new ColumnReference(updatingTableReference.getIdentificationVariable(), columnReference.getColumnExpression(), false, null, null, columnReference.getJdbcMapping(), sessionFactory)));
}
}
}
final String targetKeyColumnName = keyColumns[0];
final AbstractEntityPersister entityPersister = (AbstractEntityPersister) entityDescriptor.getEntityPersister();
final IdentifierGenerator identifierGenerator = entityPersister.getIdentifierGenerator();
final boolean needsKeyInsert;
if (identifierGenerator instanceof PostInsertIdentifierGenerator) {
needsKeyInsert = true;
} else if (identifierGenerator instanceof OptimizableGenerator) {
final Optimizer optimizer = ((OptimizableGenerator) identifierGenerator).getOptimizer();
// If the generator uses an optimizer, we have to generate the identifiers for the new rows
needsKeyInsert = optimizer != null && optimizer.getIncrementSize() > 1;
} else {
needsKeyInsert = true;
}
if (needsKeyInsert && insertStatement.getTargetColumnReferences().stream().noneMatch(c -> targetKeyColumnName.equals(c.getColumnExpression()))) {
final BasicEntityIdentifierMapping identifierMapping = (BasicEntityIdentifierMapping) entityDescriptor.getIdentifierMapping();
insertStatement.addTargetColumnReferences(new ColumnReference(dmlTargetTableReference.getIdentificationVariable(), targetKeyColumnName, false, null, null, identifierMapping.getJdbcMapping(), sessionFactory));
querySpec.getSelectClause().addSqlSelection(new SqlSelectionImpl(1, 0, new ColumnReference(updatingTableReference.getIdentificationVariable(), identifierMapping, sessionFactory)));
}
final JdbcServices jdbcServices = sessionFactory.getJdbcServices();
final JdbcInsert jdbcInsert = jdbcServices.getJdbcEnvironment().getSqlAstTranslatorFactory().buildInsertTranslator(sessionFactory, insertStatement).translate(null, executionContext.getQueryOptions());
jdbcServices.getJdbcMutationExecutor().execute(jdbcInsert, JdbcParameterBindings.NO_BINDINGS, sql -> executionContext.getSession().getJdbcCoordinator().getStatementPreparer().prepareStatement(sql), (integer, preparedStatement) -> {
}, executionContext);
}
Aggregations