Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Next Next commit
feat: support any number/order of merge operations
  • Loading branch information
davidjgoss committed Jan 2, 2024
commit 6570d448b3b8d6ccfef4e05bc39001b337dee2d0
76 changes: 35 additions & 41 deletions src/main/java/net/sf/jsqlparser/statement/merge/Merge.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,7 @@ public class Merge implements Statement {
private OracleHint oracleHint = null;
private FromItem fromItem;
private Expression onCondition;
private MergeInsert mergeInsert;
private MergeUpdate mergeUpdate;
private boolean insertFirst = false;
private List<MergeOperation> operations;

private OutputClause outputClause;

Expand Down Expand Up @@ -129,33 +127,52 @@ public void setOnCondition(Expression onCondition) {
this.onCondition = onCondition;
}

public MergeInsert getMergeInsert() {
return mergeInsert;
public List<MergeOperation> getOperations() {
return operations;
}

public void setMergeInsert(MergeInsert insert) {
this.mergeInsert = insert;
public void setOperations(List<MergeOperation> operations) {
this.operations = operations;
}

public MergeUpdate getMergeUpdate() {
return mergeUpdate;
/**
* @deprecated use {@link #getOperations()} or consider a {@link MergeOperationVisitor} instead
*/
@Deprecated
public MergeInsert getMergeInsert() {
return operations.stream()
.filter(MergeInsert.class::isInstance)
.findFirst()
.map(MergeInsert.class::cast)
.orElse(null);
}

public void setMergeUpdate(MergeUpdate mergeUpdate) {
this.mergeUpdate = mergeUpdate;
/**
* @deprecated use {@link #getOperations()} or consider a {@link MergeOperationVisitor} instead
*/
@Deprecated
public MergeUpdate getMergeUpdate() {
return operations.stream()
.filter(MergeUpdate.class::isInstance)
.findFirst()
.map(MergeUpdate.class::cast)
.orElse(null);
}

@Override
public void accept(StatementVisitor statementVisitor) {
statementVisitor.visit(this);
}

/**
* @deprecated use {@link #getOperations()} or consider a {@link MergeOperationVisitor} instead
*/
@Deprecated
public boolean isInsertFirst() {
return insertFirst;
}

public void setInsertFirst(boolean insertFirst) {
this.insertFirst = insertFirst;
if (operations == null || operations.isEmpty()) {
return false;
}
return operations.get(0) instanceof MergeInsert;
}

public OutputClause getOutputClause() {
Expand Down Expand Up @@ -193,16 +210,8 @@ public String toString() {
b.append(" ON ");
b.append(onCondition);

if (insertFirst && mergeInsert != null) {
b.append(mergeInsert);
}

if (mergeUpdate != null) {
b.append(mergeUpdate);
}

if (!insertFirst && mergeInsert != null) {
b.append(mergeInsert);
if (operations != null && !operations.isEmpty()) {
operations.forEach(b::append);
}

if (outputClause != null) {
Expand Down Expand Up @@ -235,26 +244,11 @@ public Merge withOnCondition(Expression onCondition) {
return this;
}

public Merge withMergeUpdate(MergeUpdate mergeUpdate) {
this.setMergeUpdate(mergeUpdate);
return this;
}

public Merge withInsertFirst(boolean insertFirst) {
this.setInsertFirst(insertFirst);
return this;
}

public Merge withTable(Table table) {
this.setTable(table);
return this;
}

public Merge withMergeInsert(MergeInsert mergeInsert) {
this.setMergeInsert(mergeInsert);
return this;
}

public <E extends Expression> E getOnCondition(Class<E> type) {
return type.cast(getOnCondition());
}
Expand Down
47 changes: 47 additions & 0 deletions src/main/java/net/sf/jsqlparser/statement/merge/MergeDelete.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*-
* #%L
* JSQLParser library
* %%
* Copyright (C) 2004 - 2024 JSQLParser
* %%
* Dual licensed under GNU LGPL 2.1 or Apache License 2.0
* #L%
*/
package net.sf.jsqlparser.statement.merge;

import net.sf.jsqlparser.expression.Expression;

import java.io.Serializable;

public class MergeDelete implements Serializable, MergeOperation {
private Expression andPredicate;

public Expression getAndPredicate() {
return andPredicate;
}

public void setAndPredicate(Expression andPredicate) {
this.andPredicate = andPredicate;
}

public MergeDelete withAndPredicate(Expression andPredicate) {
this.setAndPredicate(andPredicate);
return this;
}

@Override
public void accept(MergeOperationVisitor mergeOperationVisitor) {
mergeOperationVisitor.visit(this);
}

@Override
public String toString() {
StringBuilder b = new StringBuilder();
b.append(" WHEN MATCHED");
if (andPredicate != null) {
b.append(" AND ").append(andPredicate.toString());
}
b.append(" THEN DELETE");
return b.toString();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
import java.util.Collection;
import java.util.Optional;

public class MergeInsert implements Serializable {
public class MergeInsert implements Serializable, MergeOperation {

private Expression andPredicate;
private ExpressionList<Column> columns;
Expand Down Expand Up @@ -57,6 +57,11 @@ public void setWhereCondition(Expression whereCondition) {
this.whereCondition = whereCondition;
}

@Override
public void accept(MergeOperationVisitor mergeOperationVisitor) {
mergeOperationVisitor.visit(this);
}

@Override
public String toString() {
StringBuilder b = new StringBuilder();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/*-
* #%L
* JSQLParser library
* %%
* Copyright (C) 2004 - 2024 JSQLParser
* %%
* Dual licensed under GNU LGPL 2.1 or Apache License 2.0
* #L%
*/
package net.sf.jsqlparser.statement.merge;

/**
* Marker interface to cover {@link MergeDelete}, {@link MergeUpdate} and {@link MergeInsert}
*/
public interface MergeOperation {
void accept(MergeOperationVisitor mergeOperationVisitor);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/*-
* #%L
* JSQLParser library
* %%
* Copyright (C) 2004 - 2024 JSQLParser
* %%
* Dual licensed under GNU LGPL 2.1 or Apache License 2.0
* #L%
*/
package net.sf.jsqlparser.statement.merge;

public interface MergeOperationVisitor {

void visit(MergeDelete mergeDelete);

void visit(MergeUpdate mergeUpdate);

void visit(MergeInsert mergeInsert);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*-
* #%L
* JSQLParser library
* %%
* Copyright (C) 2004 - 2024 JSQLParser
* %%
* Dual licensed under GNU LGPL 2.1 or Apache License 2.0
* #L%
*/
package net.sf.jsqlparser.statement.merge;

@SuppressWarnings({"PMD.UncommentedEmptyMethodBody"})
public class MergeOperationVisitorAdapter implements MergeOperationVisitor {
@Override
public void visit(MergeDelete mergeDelete) {

}

@Override
public void visit(MergeUpdate mergeUpdate) {

}

@Override
public void visit(MergeInsert mergeInsert) {

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
import java.io.Serializable;
import java.util.List;

public class MergeUpdate implements Serializable {
public class MergeUpdate implements Serializable, MergeOperation {

private List<UpdateSet> updateSets;
private Expression andPredicate;
Expand Down Expand Up @@ -61,6 +61,11 @@ public void setDeleteWhereCondition(Expression deleteWhereCondition) {
this.deleteWhereCondition = deleteWhereCondition;
}

@Override
public void accept(MergeOperationVisitor mergeOperationVisitor) {
mergeOperationVisitor.visit(this);
}

@Override
public String toString() {
StringBuilder b = new StringBuilder();
Expand Down
118 changes: 118 additions & 0 deletions src/main/java/net/sf/jsqlparser/util/deparser/MergeDeParser.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
/*-
* #%L
* JSQLParser library
* %%
* Copyright (C) 2004 - 2024 JSQLParser
* %%
* Dual licensed under GNU LGPL 2.1 or Apache License 2.0
* #L%
*/
package net.sf.jsqlparser.util.deparser;

import net.sf.jsqlparser.statement.merge.*;
import net.sf.jsqlparser.statement.select.WithItem;

import java.util.Iterator;
import java.util.List;

public class MergeDeParser extends AbstractDeParser<Merge> implements MergeOperationVisitor {
private final ExpressionDeParser expressionDeParser;

private final SelectDeParser selectDeParser;

public MergeDeParser(ExpressionDeParser expressionDeParser, SelectDeParser selectDeParser,
StringBuilder buffer) {
super(buffer);
this.expressionDeParser = expressionDeParser;
this.selectDeParser = selectDeParser;
}

@Override
void deParse(Merge merge) {
List<WithItem> withItemsList = merge.getWithItemsList();
if (withItemsList != null && !withItemsList.isEmpty()) {
buffer.append("WITH ");
for (Iterator<WithItem> iter = withItemsList.iterator(); iter.hasNext();) {
iter.next().accept(expressionDeParser);
if (iter.hasNext()) {
buffer.append(",");
}
buffer.append(" ");
}
}

buffer.append("MERGE ");
if (merge.getOracleHint() != null) {
buffer.append(merge.getOracleHint()).append(" ");
}
buffer.append("INTO ");
merge.getTable().accept(selectDeParser);

buffer.append(" USING ");
merge.getFromItem().accept(selectDeParser);

buffer.append(" ON ");
merge.getOnCondition().accept(expressionDeParser);

List<MergeOperation> operations = merge.getOperations();
if (operations != null && !operations.isEmpty()) {
operations.forEach(operation -> operation.accept(this));
}

if (merge.getOutputClause() != null) {
merge.getOutputClause().appendTo(buffer);
}
}

@Override
public void visit(MergeDelete mergeDelete) {
buffer.append(" WHEN MATCHED");
if (mergeDelete.getAndPredicate() != null) {
buffer.append(" AND ");
mergeDelete.getAndPredicate().accept(expressionDeParser);
}
buffer.append(" THEN DELETE");
}

@Override
public void visit(MergeUpdate mergeUpdate) {
buffer.append(" WHEN MATCHED");
if (mergeUpdate.getAndPredicate() != null) {
buffer.append(" AND ");
mergeUpdate.getAndPredicate().accept(expressionDeParser);
}
buffer.append(" THEN UPDATE SET ");
deparseUpdateSets(mergeUpdate.getUpdateSets(), buffer, expressionDeParser);

if (mergeUpdate.getWhereCondition() != null) {
buffer.append(" WHERE ");
mergeUpdate.getWhereCondition().accept(expressionDeParser);
}

if (mergeUpdate.getDeleteWhereCondition() != null) {
buffer.append(" DELETE WHERE ");
mergeUpdate.getDeleteWhereCondition().accept(expressionDeParser);
}
}

@Override
public void visit(MergeInsert mergeInsert) {
buffer.append(" WHEN NOT MATCHED");
if (mergeInsert.getAndPredicate() != null) {
buffer.append(" AND ");
mergeInsert.getAndPredicate().accept(expressionDeParser);
}
buffer.append(" THEN INSERT ");
if (mergeInsert.getColumns() != null) {
mergeInsert.getColumns().accept(expressionDeParser);
}
buffer.append(" VALUES ");
mergeInsert.getValues().accept(expressionDeParser);

if (mergeInsert.getWhereCondition() != null) {
buffer.append(" WHERE ");
mergeInsert.getWhereCondition().accept(expressionDeParser);
}
}

}
Loading