Skip to content
Merged
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
23 changes: 23 additions & 0 deletions src/main/java/net/sf/jsqlparser/statement/merge/MergeSide.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,29 @@ public enum MergeSide {
* @throws IllegalArgumentException if {@code image} is neither {@code TARGET} nor
* {@code SOURCE}
*/
/**
* Validates the clause pairing of a {@code WHEN NOT MATCHED} branch: {@code BY TARGET} (the
* default) only allows an {@code INSERT} clause, {@code BY SOURCE} only allows {@code UPDATE}
* or {@code DELETE}.
*
* @param side the parsed {@code BY TARGET}/{@code BY SOURCE} qualifier, null when absent
* @param operation the parsed clause
* @return the passed {@code operation}
* @throws IllegalArgumentException when the pairing is not legal in any dialect
*/
public static MergeOperation validatePairing(MergeSide side, MergeOperation operation) {
if (side == MergeSide.SOURCE) {
if (operation instanceof MergeInsert) {
throw new IllegalArgumentException(
"WHEN NOT MATCHED BY SOURCE cannot take an INSERT clause");
}
} else if (!(operation instanceof MergeInsert)) {
throw new IllegalArgumentException(
"WHEN NOT MATCHED [BY TARGET] cannot take an UPDATE or DELETE clause");
}
return operation;
}

public static MergeSide fromImage(String image) {
for (MergeSide value : values()) {
if (value.name().equalsIgnoreCase(image)) {
Expand Down
2 changes: 1 addition & 1 deletion src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt
Original file line number Diff line number Diff line change
Expand Up @@ -4489,7 +4489,7 @@ MergeOperation MergeWhenNotMatched() : {
|
operation = MergeDeleteClause(predicate) { ((MergeDelete) operation).setSide(side); }
)
{ return operation; }
{ return MergeSide.validatePairing(side, operation); }
}

// table names seem to allow ":" delimiters, e.g. for Informix see #1134
Expand Down
33 changes: 33 additions & 0 deletions src/test/java/net/sf/jsqlparser/statement/merge/MergeTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import static net.sf.jsqlparser.test.TestUtils.assertOracleHintExists;
import static net.sf.jsqlparser.test.TestUtils.assertSqlCanBeParsedAndDeparsed;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.Mockito.mock;

/**
Expand Down Expand Up @@ -363,4 +364,36 @@ private static Stream<Arguments> deriveStandardClausesFromOperationsCases() {
Arguments.of(Arrays.asList(insert1, insert2, update1, update2, delete1), update1,
insert1, true));
}

@Test
void testMergeRejectsInvalidWhenNotMatchedClausePairings() {
// WHEN MATCHED rows exist in the target: INSERT is not legal
assertThrows(JSQLParserException.class, () -> CCJSqlParserUtil.parse(
"MERGE INTO t USING s ON t.id = s.id WHEN MATCHED THEN INSERT (a) VALUES (1)"));

// WHEN NOT MATCHED [BY TARGET] rows do not exist in the target: only INSERT is legal
assertInvalidMergePairing(
"MERGE INTO t USING s ON t.id = s.id WHEN NOT MATCHED THEN UPDATE SET a = 1");
assertInvalidMergePairing(
"MERGE INTO t USING s ON t.id = s.id WHEN NOT MATCHED THEN DELETE");
assertInvalidMergePairing(
"MERGE INTO t USING s ON t.id = s.id WHEN NOT MATCHED BY TARGET THEN UPDATE SET a = 1");
assertInvalidMergePairing(
"MERGE INTO t USING s ON t.id = s.id WHEN NOT MATCHED BY TARGET THEN DELETE");

// WHEN NOT MATCHED BY SOURCE rows exist in the target: INSERT is not legal
assertInvalidMergePairing(
"MERGE INTO t USING s ON t.id = s.id WHEN NOT MATCHED BY SOURCE THEN INSERT (a) VALUES (1)");
}

private static void assertInvalidMergePairing(String sql) {
JSQLParserException exception = assertThrows(JSQLParserException.class,
() -> CCJSqlParserUtil.parse(sql));
Throwable cause = exception;
while (cause.getCause() != null) {
cause = cause.getCause();
}
assertThat(cause).isInstanceOf(IllegalArgumentException.class);
assertThat(cause.getMessage()).contains("WHEN NOT MATCHED");
}
}
Loading