HHH-16110 - MERGE for optional table update PostgreSQL
This commit is contained in:
parent
a6995b50a9
commit
3281f4522e
|
@ -297,6 +297,7 @@ public class H2SqlAstTranslator<T extends JdbcOperation> extends SqlAstTranslato
|
|||
return " from dual";
|
||||
}
|
||||
|
||||
|
||||
private boolean supportsOffsetFetchClause() {
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -28,8 +28,8 @@ import org.hibernate.boot.model.TypeContributions;
|
|||
import org.hibernate.dialect.aggregate.AggregateSupport;
|
||||
import org.hibernate.dialect.aggregate.PostgreSQLAggregateSupport;
|
||||
import org.hibernate.dialect.function.CommonFunctionFactory;
|
||||
import org.hibernate.dialect.function.PostgreSQLTruncRoundFunction;
|
||||
import org.hibernate.dialect.function.PostgreSQLMinMaxFunction;
|
||||
import org.hibernate.dialect.function.PostgreSQLTruncRoundFunction;
|
||||
import org.hibernate.dialect.identity.IdentityColumnSupport;
|
||||
import org.hibernate.dialect.identity.PostgreSQLIdentityColumnSupport;
|
||||
import org.hibernate.dialect.pagination.LimitHandler;
|
||||
|
@ -51,6 +51,7 @@ import org.hibernate.exception.spi.ViolatedConstraintNameExtractor;
|
|||
import org.hibernate.internal.util.JdbcExceptionHelper;
|
||||
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||
import org.hibernate.metamodel.spi.RuntimeModelCreationContext;
|
||||
import org.hibernate.persister.entity.mutation.EntityMutationTarget;
|
||||
import org.hibernate.procedure.internal.PostgreSQLCallableStatementSupport;
|
||||
import org.hibernate.procedure.spi.CallableStatementSupport;
|
||||
import org.hibernate.query.SemanticException;
|
||||
|
@ -68,6 +69,8 @@ import org.hibernate.sql.ast.spi.SqlAppender;
|
|||
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.type.JavaObjectType;
|
||||
import org.hibernate.type.descriptor.java.PrimitiveByteArrayJavaType;
|
||||
import org.hibernate.type.descriptor.jdbc.AggregateJdbcType;
|
||||
|
@ -1366,23 +1369,12 @@ public class PostgreSQLDialect extends Dialect {
|
|||
return OTHER;
|
||||
}
|
||||
|
||||
// @Override
|
||||
// public String getDisableConstraintsStatement() {
|
||||
// return "set constraints all deferred";
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public String getEnableConstraintsStatement() {
|
||||
// return "set constraints all immediate";
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public String getDisableConstraintStatement(String tableName, String name) {
|
||||
// return "alter table " + tableName + " alter constraint " + name + " deferrable";
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public String getEnableConstraintStatement(String tableName, String name) {
|
||||
// return "alter table " + tableName + " alter constraint " + name + " deferrable";
|
||||
// }
|
||||
@Override
|
||||
public MutationOperation createOptionalTableUpdateOperation(
|
||||
EntityMutationTarget mutationTarget,
|
||||
OptionalTableUpdate optionalTableUpdate,
|
||||
SessionFactoryImplementor factory) {
|
||||
final PostgreSQLSqlAstTranslator<?> translator = new PostgreSQLSqlAstTranslator<>( factory, optionalTableUpdate );
|
||||
return translator.createMergeOperation( optionalTableUpdate );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,11 +6,10 @@
|
|||
*/
|
||||
package org.hibernate.dialect;
|
||||
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.metamodel.mapping.JdbcMappingContainer;
|
||||
import org.hibernate.query.sqm.ComparisonOperator;
|
||||
import org.hibernate.query.sqm.FetchClauseType;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator;
|
||||
import org.hibernate.sql.ast.tree.Statement;
|
||||
import org.hibernate.sql.ast.tree.cte.CteMaterialization;
|
||||
import org.hibernate.sql.ast.tree.cte.CteStatement;
|
||||
|
@ -33,7 +32,7 @@ import org.hibernate.type.SqlTypes;
|
|||
*
|
||||
* @author Christian Beikov
|
||||
*/
|
||||
public class PostgreSQLSqlAstTranslator<T extends JdbcOperation> extends AbstractSqlAstTranslator<T> {
|
||||
public class PostgreSQLSqlAstTranslator<T extends JdbcOperation> extends SqlAstTranslatorWithMerge<T> {
|
||||
|
||||
public PostgreSQLSqlAstTranslator(SessionFactoryImplementor sessionFactory, Statement statement) {
|
||||
super( sessionFactory, statement );
|
||||
|
@ -280,5 +279,4 @@ public class PostgreSQLSqlAstTranslator<T extends JdbcOperation> extends Abstrac
|
|||
arithmeticExpression.getRightHandOperand().accept( this );
|
||||
appendSql( CLOSE_PARENTHESIS );
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -11,14 +11,18 @@ import java.sql.ResultSet;
|
|||
import java.sql.SQLException;
|
||||
import java.sql.Types;
|
||||
|
||||
import jakarta.persistence.TemporalType;
|
||||
|
||||
import org.hibernate.boot.model.FunctionContributions;
|
||||
import org.hibernate.dialect.function.CommonFunctionFactory;
|
||||
import org.hibernate.engine.jdbc.dialect.spi.DialectResolutionInfo;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.persister.entity.mutation.EntityMutationTarget;
|
||||
import org.hibernate.query.sqm.CastType;
|
||||
import org.hibernate.query.sqm.TemporalUnit;
|
||||
import org.hibernate.sql.model.MutationOperation;
|
||||
import org.hibernate.sql.model.internal.OptionalTableUpdate;
|
||||
import org.hibernate.sql.model.jdbc.OptionalTableUpdateOperation;
|
||||
|
||||
import jakarta.persistence.TemporalType;
|
||||
|
||||
import static org.hibernate.query.sqm.TemporalUnit.DAY;
|
||||
|
||||
|
@ -112,4 +116,13 @@ public class PostgresPlusDialect extends PostgreSQLDialect {
|
|||
return "select uuid_generate_v1";
|
||||
}
|
||||
|
||||
@Override
|
||||
public MutationOperation createOptionalTableUpdateOperation(
|
||||
EntityMutationTarget mutationTarget,
|
||||
OptionalTableUpdate optionalTableUpdate,
|
||||
SessionFactoryImplementor factory) {
|
||||
// Postgres Plus does not support full merge semantics -
|
||||
// https://www.enterprisedb.com/docs/migrating/oracle/oracle_epas_comparison/notable_differences/
|
||||
return new OptionalTableUpdateOperation( mutationTarget, optionalTableUpdate, factory );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,16 +9,22 @@ package org.hibernate.dialect;
|
|||
import java.util.List;
|
||||
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.internal.util.StringHelper;
|
||||
import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator;
|
||||
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.ast.ColumnValueBinding;
|
||||
import org.hibernate.sql.model.internal.OptionalTableUpdate;
|
||||
import org.hibernate.sql.model.jdbc.MergeOperation;
|
||||
|
||||
/**
|
||||
* Base for translators which support a full insert-or-update-or-delete (MERGE) command
|
||||
* Base for translators which support a full insert-or-update-or-delete (MERGE) command.
|
||||
* <p/>
|
||||
* Use {@link #createMergeOperation(OptionalTableUpdate)} to translate an
|
||||
* {@linkplain OptionalTableUpdate} into an executable {@linkplain MergeOperation}
|
||||
* operation.
|
||||
* <p/>
|
||||
*
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
|
@ -28,9 +34,12 @@ public abstract class SqlAstTranslatorWithMerge<T extends JdbcOperation> extends
|
|||
}
|
||||
|
||||
/**
|
||||
* Create the MutationOperation for performing a MERGE
|
||||
* Create the MutationOperation for performing a MERGE.
|
||||
*
|
||||
* The OptionalTableUpdate is {@linkplain #renderMergeStatement translated}
|
||||
* and wrapped as a MutationOperation
|
||||
*/
|
||||
public MutationOperation createMergeOperation(OptionalTableUpdate optionalTableUpdate) {
|
||||
public MergeOperation createMergeOperation(OptionalTableUpdate optionalTableUpdate) {
|
||||
renderMergeStatement( optionalTableUpdate );
|
||||
|
||||
return new MergeOperation(
|
||||
|
@ -41,97 +50,110 @@ public abstract class SqlAstTranslatorWithMerge<T extends JdbcOperation> extends
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the OptionalTableUpdate as a MERGE query.
|
||||
*
|
||||
*/
|
||||
protected void renderMergeStatement(OptionalTableUpdate optionalTableUpdate) {
|
||||
// template:
|
||||
//
|
||||
// merge into [table] as t
|
||||
// using values([bindings]) as s ([column-names])
|
||||
// on t.[key] = s.[key]
|
||||
// merge into <target-table> as t
|
||||
// using (select col_1, col_2, ... from dual) as s
|
||||
// on (t.key = s.key)
|
||||
// when not matched
|
||||
// then insert ...
|
||||
// then insert ...
|
||||
// when matched
|
||||
// and s.[columns] is null
|
||||
// then delete
|
||||
// and s.col_1 is null
|
||||
// and s.col_2 is null
|
||||
// and ...
|
||||
// then delete
|
||||
// when matched
|
||||
// then update ...
|
||||
// then update ...
|
||||
|
||||
// `merge into <target-table> [as] t`
|
||||
renderMergeInto( optionalTableUpdate );
|
||||
appendSql( " " );
|
||||
|
||||
// using (select col_1, col_2, ... from dual) as s
|
||||
renderMergeUsing( optionalTableUpdate );
|
||||
appendSql( " " );
|
||||
|
||||
// on (t.key = s.key)
|
||||
renderMergeOn( optionalTableUpdate );
|
||||
appendSql( " " );
|
||||
|
||||
// when not matched
|
||||
// then insert ...
|
||||
renderMergeInsert( optionalTableUpdate );
|
||||
appendSql( " " );
|
||||
|
||||
// when matched
|
||||
// and s.col_1 is null
|
||||
// and s.col_2 is null
|
||||
// and ...
|
||||
// then delete
|
||||
renderMergeDelete( optionalTableUpdate );
|
||||
appendSql( " " );
|
||||
|
||||
// when matched
|
||||
// then update ...
|
||||
renderMergeUpdate( optionalTableUpdate );
|
||||
}
|
||||
|
||||
protected void renderMergeInto(OptionalTableUpdate optionalTableUpdate) {
|
||||
appendSql( "merge into " );
|
||||
renderMergeTarget( optionalTableUpdate );
|
||||
}
|
||||
|
||||
private void renderMergeTarget(OptionalTableUpdate optionalTableUpdate) {
|
||||
appendSql( optionalTableUpdate.getMutatingTable().getTableName() );
|
||||
appendSql( " " );
|
||||
renderMergeTargetAlias();
|
||||
}
|
||||
|
||||
protected void renderMergeTargetAlias() {
|
||||
appendSql( " as t" );
|
||||
appendSql( "as t" );
|
||||
}
|
||||
|
||||
protected void renderMergeUsing(OptionalTableUpdate optionalTableUpdate) {
|
||||
appendSql( "using " );
|
||||
|
||||
renderMergeSource( optionalTableUpdate );
|
||||
}
|
||||
|
||||
protected boolean wrapMergeSourceExpression() {
|
||||
return true;
|
||||
}
|
||||
|
||||
private void renderMergeSource(OptionalTableUpdate optionalTableUpdate) {
|
||||
if ( wrapMergeSourceExpression() ) {
|
||||
appendSql( " (" );
|
||||
}
|
||||
|
||||
final List<ColumnValueBinding> valueBindings = optionalTableUpdate.getValueBindings();
|
||||
final List<ColumnValueBinding> keyBindings = optionalTableUpdate.getKeyBindings();
|
||||
|
||||
final StringBuilder columnList = new StringBuilder();
|
||||
|
||||
appendSql( " values (" );
|
||||
|
||||
for ( int i = 0; i < keyBindings.size(); i++ ) {
|
||||
final ColumnValueBinding keyBinding = keyBindings.get( i );
|
||||
if ( i > 0 ) {
|
||||
appendSql( ", " );
|
||||
columnList.append( ", " );
|
||||
}
|
||||
columnList.append( keyBinding.getColumnReference().getColumnExpression() );
|
||||
renderCasted( keyBinding.getValueExpression() );
|
||||
}
|
||||
for ( int i = 0; i < valueBindings.size(); i++ ) {
|
||||
appendSql( ", " );
|
||||
columnList.append( ", " );
|
||||
final ColumnValueBinding valueBinding = valueBindings.get( i );
|
||||
columnList.append( valueBinding.getColumnReference().getColumnExpression() );
|
||||
renderCasted( valueBinding.getValueExpression() );
|
||||
}
|
||||
|
||||
appendSql( "using (" );
|
||||
renderMergeUsingQuery( optionalTableUpdate );
|
||||
appendSql( ") " );
|
||||
|
||||
if ( wrapMergeSourceExpression() ) {
|
||||
appendSql( ") " );
|
||||
}
|
||||
|
||||
renderMergeSourceAlias();
|
||||
|
||||
appendSql( "(" );
|
||||
appendSql( columnList.toString() );
|
||||
appendSql( ")" );
|
||||
}
|
||||
|
||||
protected void renderMergeSourceAlias() {
|
||||
appendSql( " as s" );
|
||||
appendSql( "as s" );
|
||||
}
|
||||
|
||||
private void renderMergeUsingQuery(OptionalTableUpdate optionalTableUpdate) {
|
||||
final List<ColumnValueBinding> valueBindings = optionalTableUpdate.getValueBindings();
|
||||
final List<ColumnValueBinding> keyBindings = optionalTableUpdate.getKeyBindings();
|
||||
|
||||
appendSql( "select " );
|
||||
|
||||
for ( int i = 0; i < keyBindings.size(); i++ ) {
|
||||
if ( i > 0 ) {
|
||||
appendSql( ", " );
|
||||
}
|
||||
renderMergeUsingQuerySelection( keyBindings.get( i ) );
|
||||
}
|
||||
for ( int i = 0; i < valueBindings.size(); i++ ) {
|
||||
appendSql( ", " );
|
||||
renderMergeUsingQuerySelection( valueBindings.get( i ) );
|
||||
}
|
||||
|
||||
final String selectionTable = StringHelper.nullIfEmpty( getFromDualForSelectOnly() );
|
||||
if ( selectionTable != null ) {
|
||||
appendSql( " " );
|
||||
appendSql( selectionTable );
|
||||
}
|
||||
}
|
||||
|
||||
protected void renderMergeUsingQuerySelection(ColumnValueBinding selectionBinding) {
|
||||
renderCasted( selectionBinding.getValueExpression() );
|
||||
appendSql( " " );
|
||||
appendSql( selectionBinding.getColumnReference().getColumnExpression() );
|
||||
}
|
||||
|
||||
protected void renderMergeOn(OptionalTableUpdate optionalTableUpdate) {
|
||||
|
@ -203,7 +225,7 @@ public abstract class SqlAstTranslatorWithMerge<T extends JdbcOperation> extends
|
|||
if ( i > 0 ) {
|
||||
appendSql( ", " );
|
||||
}
|
||||
binding.getColumnReference().appendColumnForWrite( this, "t" );
|
||||
binding.getColumnReference().appendColumnForWrite( this, null );
|
||||
appendSql( "=" );
|
||||
binding.getColumnReference().appendColumnForWrite( this, "s" );
|
||||
}
|
||||
|
|
|
@ -74,6 +74,10 @@ public class SqlAstTranslatorWithUpsert<T extends JdbcOperation> extends Abstrac
|
|||
|
||||
protected void renderMergeInto(OptionalTableUpdate optionalTableUpdate) {
|
||||
appendSql( "merge into " );
|
||||
renderMergeTarget( optionalTableUpdate );
|
||||
}
|
||||
|
||||
private void renderMergeTarget(OptionalTableUpdate optionalTableUpdate) {
|
||||
appendSql( optionalTableUpdate.getMutatingTable().getTableName() );
|
||||
renderMergeTargetAlias();
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue