Skip to content

Commit

Permalink
HHH-16138 Fix update statement join emulation for old DB2 versions
Browse files Browse the repository at this point in the history
  • Loading branch information
beikov committed Dec 22, 2023
1 parent 836263e commit 53dbc95
Show file tree
Hide file tree
Showing 3 changed files with 117 additions and 32 deletions.
Expand Up @@ -339,12 +339,27 @@ protected void visitDeleteStatementOnly(DeleteStatement statement) {
@Override
protected void visitUpdateStatementOnly(UpdateStatement statement) {
final boolean closeWrapper = renderReturningClause( statement );
super.visitUpdateStatementOnly( statement );
if ( supportsFromClauseInUpdate() || !hasNonTrivialFromClause( statement.getFromClause() ) ) {
super.visitUpdateStatementOnly( statement );
}
else {
if ( closeWrapper ) {
// Merge statements can't be used in the `from final table( ... )` syntax
visitUpdateStatementEmulateTupleSet( statement );
}
else {
visitUpdateStatementEmulateMerge( statement );
}
}
if ( closeWrapper ) {
appendSql( ')' );
}
}

protected boolean supportsFromClauseInUpdate() {
return getDB2Version().isSameOrAfter( 11 );
}

@Override
protected void visitInsertStatementOnly(InsertSelectStatement statement) {
final boolean closeWrapper = renderReturningClause( statement );
Expand Down
Expand Up @@ -342,12 +342,27 @@ protected void visitDeleteStatementOnly(DeleteStatement statement) {
@Override
protected void visitUpdateStatementOnly(UpdateStatement statement) {
final boolean closeWrapper = renderReturningClause( statement );
super.visitUpdateStatementOnly( statement );
if ( supportsFromClauseInUpdate() || !hasNonTrivialFromClause( statement.getFromClause() ) ) {
super.visitUpdateStatementOnly( statement );
}
else {
if ( closeWrapper ) {
// Merge statements can't be used in the `from final table( ... )` syntax
visitUpdateStatementEmulateTupleSet( statement );
}
else {
visitUpdateStatementEmulateMerge( statement );
}
}
if ( closeWrapper ) {
appendSql( ')' );
}
}

protected boolean supportsFromClauseInUpdate() {
return getDB2Version().isSameOrAfter( 11 );
}

@Override
protected void visitInsertStatementOnly(InsertSelectStatement statement) {
final boolean closeWrapper = renderReturningClause( statement );
Expand Down
Expand Up @@ -1421,7 +1421,7 @@ protected void visitUpdateStatementEmulateMerge(UpdateStatement statement) {
appendSql( " as t" );
clauseStack.pop();

final QueryPartTableReference inlineView = updateSourceAsSubquery( statement );
final QueryPartTableReference inlineView = updateSourceAsSubquery( statement, false );
appendSql( " using " );
clauseStack.push( Clause.FROM );
visitQueryPartTableReference( inlineView );
Expand Down Expand Up @@ -1456,8 +1456,8 @@ protected void visitUpdateStatementEmulateMerge(UpdateStatement statement) {
visitReturningColumns( statement.getReturningColumns() );
}

private QueryPartTableReference updateSourceAsSubquery(UpdateStatement statement) {
final QuerySpec inlineView = new QuerySpec( true );
private QueryPartTableReference updateSourceAsSubquery(UpdateStatement statement, boolean correlated) {
final QuerySpec inlineView = new QuerySpec( !correlated );
final SelectClause selectClause = inlineView.getSelectClause();
final List<Assignment> assignments = statement.getAssignments();
final List<String> columnNames = new ArrayList<>( assignments.size() );
Expand All @@ -1479,36 +1479,67 @@ else if ( assignedValue instanceof SqlTuple ) {
throw new IllegalQueryOperationException( "Unsupported tuple assignment in update query with joins." );
}
}
final String rowIdExpression = dialect.rowId( null );
if ( rowIdExpression == null ) {
final TableGroup dmlTargetTableGroup = statement.getFromClause().getRoots().get( 0 );
assert dmlTargetTableGroup.getPrimaryTableReference() == statement.getTargetTable();
final EntityIdentifierMapping identifierMapping = dmlTargetTableGroup.getModelPart()
.asEntityMappingType()
.getIdentifierMapping();
identifierMapping.forEachSelectable(
0,
(selectionIndex, selectableMapping) -> {
selectClause.addSqlSelection( new SqlSelectionImpl(
new ColumnReference( statement.getTargetTable(), selectableMapping )
) );
columnNames.add( selectableMapping.getSelectionExpression() );
if ( !correlated ) {
final String rowIdExpression = dialect.rowId( null );
if ( rowIdExpression == null ) {
final TableGroup dmlTargetTableGroup = statement.getFromClause().getRoots().get( 0 );
assert dmlTargetTableGroup.getPrimaryTableReference() == statement.getTargetTable();
final EntityIdentifierMapping identifierMapping = dmlTargetTableGroup.getModelPart()
.asEntityMappingType()
.getIdentifierMapping();
identifierMapping.forEachSelectable(
0,
(selectionIndex, selectableMapping) -> {
selectClause.addSqlSelection( new SqlSelectionImpl(
new ColumnReference( statement.getTargetTable(), selectableMapping )
) );
columnNames.add( selectableMapping.getSelectionExpression() );
}
);
}
else {
selectClause.addSqlSelection( new SqlSelectionImpl(
new ColumnReference(
statement.getTargetTable(),
rowIdExpression,
sessionFactory.getTypeConfiguration().getBasicTypeRegistry()
.resolve( Object.class, dialect.rowIdSqlType() )
)
) );
columnNames.add( "c" + columnNames.size() );
}
}

if ( correlated ) {
for ( TableGroup root : statement.getFromClause().getRoots() ) {
if ( statement.getTargetTable() == root.getPrimaryTableReference() ) {
final TableGroup dmlTargetTableGroup = new StandardTableGroup(
true,
new NavigablePath( "dual" ),
null,
null,
new NamedTableReference( getDual(), "d_" ),
null,
sessionFactory
);
inlineView.getFromClause().addRoot( dmlTargetTableGroup );
dmlTargetTableGroup.getTableReferenceJoins().addAll( root.getTableReferenceJoins() );
for ( TableGroupJoin tableGroupJoin : root.getTableGroupJoins() ) {
dmlTargetTableGroup.addTableGroupJoin( tableGroupJoin );
}
);
for ( TableGroupJoin tableGroupJoin : root.getNestedTableGroupJoins() ) {
dmlTargetTableGroup.addNestedTableGroupJoin( tableGroupJoin );
}
}
else {
inlineView.getFromClause().addRoot( root );
}
}
}
else {
selectClause.addSqlSelection( new SqlSelectionImpl(
new ColumnReference(
statement.getTargetTable(),
rowIdExpression,
sessionFactory.getTypeConfiguration().getBasicTypeRegistry()
.resolve( Object.class, dialect.rowIdSqlType() )
)
) );
columnNames.add( "c" + columnNames.size() );
}
for ( TableGroup root : statement.getFromClause().getRoots() ) {
inlineView.getFromClause().addRoot( root );
for ( TableGroup root : statement.getFromClause().getRoots() ) {
inlineView.getFromClause().addRoot( root );
}
}
inlineView.applyPredicate( statement.getRestriction() );

Expand Down Expand Up @@ -1545,6 +1576,30 @@ protected void visitUpdateStatementEmulateInlineView(UpdateStatement statement)
visitReturningColumns( statement.getReturningColumns() );
}

protected void visitUpdateStatementEmulateTupleSet(UpdateStatement statement) {
renderUpdateClause( statement );
appendSql( " set " );
char separator = '(';
try {
clauseStack.push( Clause.SET );
for ( Assignment assignment : statement.getAssignments() ) {
final List<ColumnReference> columnReferences = assignment.getAssignable().getColumnReferences();
for ( ColumnReference columnReference : columnReferences ) {
appendSql( separator );
separator = COMMA_SEPARATOR_CHAR;
columnReference.appendColumnForWrite( this, null );
}
}
appendSql( ")=" );
updateSourceAsSubquery( statement, true ).getStatement().accept( this );
}
finally {
clauseStack.pop();
}

visitWhereClause( determineWhereClauseRestrictionWithJoinEmulation( statement ) );
}

private QueryPartTableReference updateSourceAsInlineView(UpdateStatement statement) {
final QuerySpec inlineView = new QuerySpec( true );
final SelectClause selectClause = inlineView.getSelectClause();
Expand Down

0 comments on commit 53dbc95

Please sign in to comment.