Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
import org.hibernate.exception.spi.ViolatedConstraintNameExtractor;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.internal.util.config.ConfigurationHelper;
import org.hibernate.persister.entity.mutation.EntityMutationTarget;
import org.hibernate.query.SemanticException;
import org.hibernate.query.common.TemporalUnit;
import org.hibernate.query.sqm.IntervalType;
Expand All @@ -62,6 +63,9 @@
import org.hibernate.sql.ast.spi.StandardSqlAstTranslatorFactory;
import org.hibernate.sql.ast.tree.Statement;
import org.hibernate.sql.exec.spi.JdbcOperation;
import org.hibernate.sql.model.MutationOperation;
import org.hibernate.sql.model.internal.OptionalTableUpdate;
import org.hibernate.sql.model.jdbc.OptionalTableUpdateWithUpsertOperation;
import org.hibernate.tool.schema.extract.internal.InformationExtractorPostgreSQLImpl;
import org.hibernate.tool.schema.extract.spi.ColumnTypeInformation;
import org.hibernate.tool.schema.extract.spi.ExtractionContext;
Expand Down Expand Up @@ -676,6 +680,14 @@ protected <T extends JdbcOperation> SqlAstTranslator<T> buildTranslator(
};
}

@Override
public MutationOperation createOptionalTableUpdateOperation(
EntityMutationTarget mutationTarget,
OptionalTableUpdate optionalTableUpdate,
SessionFactoryImplementor factory) {
return new OptionalTableUpdateWithUpsertOperation( mutationTarget, optionalTableUpdate, factory );
}

@Override
public NationalizationSupport getNationalizationSupport() {
// TEXT / STRING inherently support nationalized data
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@
import org.hibernate.sql.exec.spi.JdbcOperation;
import org.hibernate.sql.model.MutationOperation;
import org.hibernate.sql.model.internal.OptionalTableUpdate;
import org.hibernate.sql.model.jdbc.OptionalTableUpdateWithUpsertOperation;
import org.hibernate.tool.schema.extract.internal.InformationExtractorPostgreSQLImpl;
import org.hibernate.tool.schema.extract.spi.ColumnTypeInformation;
import org.hibernate.tool.schema.extract.spi.ExtractionContext;
Expand Down Expand Up @@ -1587,7 +1588,7 @@ public MutationOperation createOptionalTableUpdateOperation(
.createMergeOperation( optionalTableUpdate );
}
else {
return super.createOptionalTableUpdateOperation( mutationTarget, optionalTableUpdate, factory );
return new OptionalTableUpdateWithUpsertOperation( mutationTarget, optionalTableUpdate, factory );
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@
import org.hibernate.sql.exec.internal.JdbcOperationQueryInsertImpl;
import org.hibernate.sql.exec.spi.JdbcOperation;
import org.hibernate.sql.exec.spi.JdbcOperationQueryInsert;
import org.hibernate.sql.model.internal.OptionalTableInsert;
import org.hibernate.sql.model.internal.TableInsertStandard;

/**
* A SQL AST translator for Cockroach.
Expand All @@ -47,6 +49,39 @@ public void visitBinaryArithmeticExpression(BinaryArithmeticExpression arithmeti
super.visitBinaryArithmeticExpression(arithmeticExpression);
}

@Override
public void visitStandardTableInsert(TableInsertStandard tableInsert) {
getCurrentClauseStack().push( Clause.INSERT );
try {
renderInsertInto( tableInsert );
if ( tableInsert instanceof OptionalTableInsert optionalTableInsert ) {
appendSql( " on conflict " );
final String constraintName = optionalTableInsert.getConstraintName();
if ( constraintName != null ) {
appendSql( " on constraint " );
appendSql( constraintName );
}
else {
char separator = '(';
for ( String constraintColumnName : optionalTableInsert.getConstraintColumnNames() ) {
appendSql( separator );
appendSql( constraintColumnName );
separator = ',';
}
appendSql( ')' );
}
appendSql( " do nothing" );
}

if ( tableInsert.getNumberOfReturningColumns() > 0 ) {
visitReturningColumns( tableInsert::getReturningColumns );
}
}
finally {
getCurrentClauseStack().pop();
}
}

@Override
protected JdbcOperationQueryInsert translateInsert(InsertSelectStatement sqlAst) {
visitInsertStatement( sqlAst );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import org.hibernate.sql.exec.internal.JdbcOperationQueryInsertImpl;
import org.hibernate.sql.exec.spi.JdbcOperation;
import org.hibernate.sql.exec.spi.JdbcOperationQueryInsert;
import org.hibernate.sql.model.internal.OptionalTableInsert;
import org.hibernate.sql.model.internal.TableInsertStandard;
import org.hibernate.type.SqlTypes;

Expand Down Expand Up @@ -59,6 +60,39 @@ protected String getArrayContainsFunction() {
return super.getArrayContainsFunction();
}

@Override
public void visitStandardTableInsert(TableInsertStandard tableInsert) {
getCurrentClauseStack().push( Clause.INSERT );
try {
renderInsertInto( tableInsert );
if ( tableInsert instanceof OptionalTableInsert optionalTableInsert ) {
appendSql( " on conflict " );
final String constraintName = optionalTableInsert.getConstraintName();
if ( constraintName != null ) {
appendSql( " on constraint " );
appendSql( constraintName );
}
else {
char separator = '(';
for ( String constraintColumnName : optionalTableInsert.getConstraintColumnNames() ) {
appendSql( separator );
appendSql( constraintColumnName );
separator = ',';
}
appendSql( ')' );
}
appendSql( " do nothing" );
}

if ( tableInsert.getNumberOfReturningColumns() > 0 ) {
visitReturningColumns( tableInsert::getReturningColumns );
}
}
finally {
getCurrentClauseStack().pop();
}
}

@Override
protected void renderInsertIntoNoColumns(TableInsertStandard tableInsert) {
renderIntoIntoAndTable( tableInsert );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,7 @@
import org.hibernate.sql.model.ast.ColumnWriteFragment;
import org.hibernate.sql.model.ast.RestrictedTableMutation;
import org.hibernate.sql.model.ast.TableMutation;
import org.hibernate.sql.model.internal.OptionalTableInsert;
import org.hibernate.sql.model.internal.OptionalTableUpdate;
import org.hibernate.sql.model.internal.TableDeleteCustomSql;
import org.hibernate.sql.model.internal.TableDeleteStandard;
Expand Down Expand Up @@ -8508,6 +8509,9 @@ private T translateTableMutation(TableMutation<?> mutation) {

@Override
public void visitStandardTableInsert(TableInsertStandard tableInsert) {
if ( tableInsert instanceof OptionalTableInsert ) {
throw new IllegalQueryOperationException( "Optional table insert is not supported" );
}
getCurrentClauseStack().push( Clause.INSERT );
try {
renderInsertInto( tableInsert );
Expand All @@ -8521,7 +8525,7 @@ public void visitStandardTableInsert(TableInsertStandard tableInsert) {
}
}

private void renderInsertInto(TableInsertStandard tableInsert) {
protected void renderInsertInto(TableInsertStandard tableInsert) {
applySqlComment( tableInsert.getMutationComment() );

if ( tableInsert.getNumberOfValueBindings() == 0 ) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* SPDX-License-Identifier: Apache-2.0
* Copyright Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.sql.model.internal;

import org.checkerframework.checker.nullness.qual.Nullable;
import org.hibernate.sql.ast.tree.expression.ColumnReference;
import org.hibernate.sql.model.MutationTarget;
import org.hibernate.sql.model.ast.ColumnValueBinding;
import org.hibernate.sql.model.ast.ColumnValueParameter;
import org.hibernate.sql.model.ast.MutatingTableReference;

import java.util.List;

public class OptionalTableInsert extends TableInsertStandard {

private final @Nullable String constraintName;
private final List<String> constraintColumnNames;

public OptionalTableInsert(
MutatingTableReference mutatingTable,
MutationTarget<?> mutationTarget,
List<ColumnValueBinding> valueBindings,
List<ColumnReference> returningColumns,
List<ColumnValueParameter> parameters,
@Nullable String constraintName,
List<String> constraintColumnNames) {
super( mutatingTable, mutationTarget, valueBindings, returningColumns, parameters );
this.constraintName = constraintName;
this.constraintColumnNames = constraintColumnNames;
}

public @Nullable String getConstraintName() {
return constraintName;
}

public List<String> getConstraintColumnNames() {
return constraintColumnNames;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,22 @@ public TableMapping getTableDetails() {
return tableMapping;
}

public List<ColumnValueBinding> getValueBindings() {
return valueBindings;
}

public List<ColumnValueBinding> getKeyBindings() {
return keyBindings;
}

public List<ColumnValueBinding> getOptimisticLockBindings() {
return optimisticLockBindings;
}

public List<ColumnValueParameter> getParameters() {
return parameters;
}

@Override
public JdbcValueDescriptor findValueDescriptor(String columnName, ParameterUsage usage) {
for ( int i = 0; i < jdbcValueDescriptors.size(); i++ ) {
Expand Down Expand Up @@ -375,7 +391,7 @@ protected JdbcMutationOperation createJdbcUpdate(SharedSessionContractImplemento
}

private void performInsert(JdbcValueBindings jdbcValueBindings, SharedSessionContractImplementor session) {
final JdbcInsertMutation jdbcInsert = createJdbcInsert( session );
final JdbcMutationOperation jdbcInsert = createJdbcOptionalInsert( session );
final JdbcServices jdbcServices = session.getJdbcServices();
final JdbcCoordinator jdbcCoordinator = session.getJdbcCoordinator();
final PreparedStatement insertStatement = createStatementDetails( jdbcInsert, jdbcCoordinator );
Expand Down Expand Up @@ -413,6 +429,13 @@ private void performInsert(JdbcValueBindings jdbcValueBindings, SharedSessionCon
}
}

/*
* Used by Hibernate Reactive
*/
protected JdbcMutationOperation createJdbcOptionalInsert(SharedSessionContractImplementor session) {
return createJdbcInsert( session );
}

/*
* Used by Hibernate Reactive
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*
* SPDX-License-Identifier: Apache-2.0
* Copyright Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.sql.model.jdbc;

import org.hibernate.engine.jdbc.mutation.internal.MutationQueryOptions;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.internal.util.collections.CollectionHelper;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.persister.entity.mutation.EntityMutationTarget;
import org.hibernate.sql.model.ast.MutatingTableReference;
import org.hibernate.sql.model.ast.TableMutation;
import org.hibernate.sql.model.internal.OptionalTableInsert;
import org.hibernate.sql.model.internal.OptionalTableUpdate;

import java.util.Arrays;
import java.util.Collections;

/**
* Uses {@link org.hibernate.sql.model.internal.OptionalTableInsert} for the insert operation,
* to avoid primary key constraint violations when inserting only primary key columns.
*/
public class OptionalTableUpdateWithUpsertOperation extends OptionalTableUpdateOperation {

public OptionalTableUpdateWithUpsertOperation(
EntityMutationTarget mutationTarget,
OptionalTableUpdate upsert,
@SuppressWarnings("unused") SessionFactoryImplementor factory) {
super( mutationTarget, upsert, factory );
}

@Override
protected JdbcMutationOperation createJdbcOptionalInsert(SharedSessionContractImplementor session) {
if ( getTableDetails().getInsertDetails() != null
&& getTableDetails().getInsertDetails().getCustomSql() != null
|| !getValueBindings().isEmpty() ) {
return super.createJdbcOptionalInsert( session );
}
else {
// Ignore a primary key violation on insert when inserting just the primary key columns
final TableMutation<? extends JdbcMutationOperation> tableInsert = new OptionalTableInsert(
new MutatingTableReference( getTableDetails() ),
getMutationTarget(),
CollectionHelper.combine( getValueBindings(), getKeyBindings() ),
Collections.emptyList(),
getParameters(),
null,
Arrays.asList( ((EntityPersister) getMutationTarget()).getIdentifierColumnNames() )
);

final SessionFactoryImplementor factory = session.getSessionFactory();
return factory.getJdbcServices().getJdbcEnvironment().getSqlAstTranslatorFactory()
.buildModelMutationTranslator( tableInsert, factory )
.translate( null, MutationQueryOptions.INSTANCE );
}
}

@Override
public String toString() {
return "OptionalTableUpdateWithUpsertOperation(" + getTableDetails() + ")";
}
}