|
12 | 12 | import static net.sf.jsqlparser.test.TestUtils.assertSqlCanBeParsedAndDeparsed; |
13 | 13 | import static org.junit.jupiter.api.Assertions.*; |
14 | 14 |
|
| 15 | +import java.util.stream.Stream; |
15 | 16 | import net.sf.jsqlparser.JSQLParserException; |
16 | 17 | import net.sf.jsqlparser.parser.CCJSqlParserUtil; |
17 | 18 | import net.sf.jsqlparser.statement.Statement; |
18 | | -import org.junit.jupiter.api.Test; |
19 | 19 | import org.junit.jupiter.api.parallel.Execution; |
20 | 20 | import org.junit.jupiter.api.parallel.ExecutionMode; |
| 21 | +import org.junit.jupiter.params.ParameterizedTest; |
| 22 | +import org.junit.jupiter.params.provider.Arguments; |
| 23 | +import org.junit.jupiter.params.provider.MethodSource; |
| 24 | +import org.junit.jupiter.params.provider.ValueSource; |
21 | 25 |
|
22 | 26 | /** |
23 | 27 | * Regression tests for EXCEPT/MINUS ALL/DISTINCT modifier handling. |
24 | 28 | * <p> |
25 | | - * Verifies that the ALL and DISTINCT modifiers are correctly preserved during |
26 | | - * parse-toString round-trips for all set operation types: UNION, INTERSECT, |
27 | | - * EXCEPT, and MINUS. |
| 29 | + * Verifies that the ALL and DISTINCT modifiers are correctly preserved during parse-toString |
| 30 | + * round-trips for all set operation types: UNION, INTERSECT, EXCEPT, and MINUS. |
28 | 31 | * |
29 | | - * @see <a href="https://github.com/JSQLParser/JSqlParser/issues/2080">#2080</a> |
| 32 | + * @see <a href="https://github.com/JSQLParser/JSqlParser/issues/2419">#2419</a> |
30 | 33 | */ |
31 | 34 | @Execution(ExecutionMode.CONCURRENT) |
32 | 35 | public class SetOperationModifierTest { |
33 | 36 |
|
34 | | - // ── EXCEPT modifier tests ───────────────────────────────────── |
35 | | - |
36 | | - @Test |
37 | | - public void testExceptAllRoundTrip() throws JSQLParserException { |
38 | | - assertSqlCanBeParsedAndDeparsed("SELECT a FROM t1 EXCEPT ALL SELECT a FROM t2"); |
39 | | - } |
40 | | - |
41 | | - @Test |
42 | | - public void testExceptDistinctRoundTrip() throws JSQLParserException { |
43 | | - assertSqlCanBeParsedAndDeparsed("SELECT a FROM t1 EXCEPT DISTINCT SELECT a FROM t2"); |
44 | | - } |
45 | | - |
46 | | - @Test |
47 | | - public void testPlainExceptRoundTrip() throws JSQLParserException { |
48 | | - assertSqlCanBeParsedAndDeparsed("SELECT a FROM t1 EXCEPT SELECT a FROM t2"); |
49 | | - } |
50 | | - |
51 | | - // ── MINUS modifier tests ───────────────────────────────────── |
52 | | - |
53 | | - @Test |
54 | | - public void testMinusAllRoundTrip() throws JSQLParserException { |
55 | | - assertSqlCanBeParsedAndDeparsed("SELECT a FROM t1 MINUS ALL SELECT a FROM t2"); |
56 | | - } |
57 | | - |
58 | | - @Test |
59 | | - public void testMinusDistinctRoundTrip() throws JSQLParserException { |
60 | | - assertSqlCanBeParsedAndDeparsed("SELECT a FROM t1 MINUS DISTINCT SELECT a FROM t2"); |
61 | | - } |
62 | | - |
63 | | - @Test |
64 | | - public void testPlainMinusRoundTrip() throws JSQLParserException { |
65 | | - assertSqlCanBeParsedAndDeparsed("SELECT a FROM t1 MINUS SELECT a FROM t2"); |
66 | | - } |
67 | | - |
68 | | - // ── Cross-check: UNION and INTERSECT still work ────────────── |
69 | | - |
70 | | - @Test |
71 | | - public void testUnionAllRoundTrip() throws JSQLParserException { |
72 | | - assertSqlCanBeParsedAndDeparsed("SELECT a FROM t1 UNION ALL SELECT a FROM t2"); |
73 | | - } |
74 | | - |
75 | | - @Test |
76 | | - public void testIntersectAllRoundTrip() throws JSQLParserException { |
77 | | - assertSqlCanBeParsedAndDeparsed("SELECT a FROM t1 INTERSECT ALL SELECT a FROM t2"); |
| 37 | + @ParameterizedTest |
| 38 | + @ValueSource(strings = { |
| 39 | + "SELECT a FROM t1 EXCEPT ALL SELECT a FROM t2", |
| 40 | + "SELECT a FROM t1 EXCEPT DISTINCT SELECT a FROM t2", |
| 41 | + "SELECT a FROM t1 EXCEPT SELECT a FROM t2", |
| 42 | + "SELECT a FROM t1 MINUS ALL SELECT a FROM t2", |
| 43 | + "SELECT a FROM t1 MINUS DISTINCT SELECT a FROM t2", |
| 44 | + "SELECT a FROM t1 MINUS SELECT a FROM t2", |
| 45 | + "SELECT a FROM t1 UNION ALL SELECT a FROM t2", |
| 46 | + "SELECT a FROM t1 INTERSECT ALL SELECT a FROM t2", |
| 47 | + "SELECT a FROM t1 UNION ALL SELECT b FROM t2 EXCEPT DISTINCT SELECT c FROM t3" |
| 48 | + }) |
| 49 | + void testSetOperationModifierRoundTrip(String sql) throws JSQLParserException { |
| 50 | + assertSqlCanBeParsedAndDeparsed(sql); |
78 | 51 | } |
79 | 52 |
|
80 | | - // ── Modifier leak prevention: modifiers must not bleed across operators ── |
81 | | - |
82 | | - @Test |
83 | | - public void testModifierDoesNotLeakFromUnionAllToExcept() throws JSQLParserException { |
84 | | - String sql = "SELECT a FROM t1 UNION ALL SELECT b FROM t2 EXCEPT SELECT c FROM t3"; |
| 53 | + @ParameterizedTest |
| 54 | + @MethodSource("provideModifierLeakCases") |
| 55 | + void testModifierDoesNotLeakBetweenOperators(String sql, String forbidden) |
| 56 | + throws JSQLParserException { |
85 | 57 | Statement stmt = CCJSqlParserUtil.parse(sql); |
86 | | - String result = stmt.toString(); |
87 | | - // EXCEPT must NOT inherit ALL from the preceding UNION ALL |
88 | | - assertFalse(result.contains("EXCEPT ALL"), |
89 | | - "Modifier should not leak from UNION ALL to a subsequent plain EXCEPT: " + result); |
| 58 | + String deparsed = stmt.toString(); |
| 59 | + assertFalse(deparsed.contains(forbidden), |
| 60 | + "Modifier leaked: found '" + forbidden + "' in: " + deparsed); |
90 | 61 | } |
91 | 62 |
|
92 | | - @Test |
93 | | - public void testModifierDoesNotLeakFromIntersectAllToUnion() throws JSQLParserException { |
94 | | - String sql = "SELECT a FROM t1 INTERSECT ALL SELECT b FROM t2 UNION SELECT c FROM t3"; |
95 | | - Statement stmt = CCJSqlParserUtil.parse(sql); |
96 | | - String result = stmt.toString(); |
97 | | - assertFalse(result.contains("UNION ALL"), |
98 | | - "Modifier should not leak from INTERSECT ALL to a subsequent plain UNION: " + result); |
| 63 | + private static Stream<Arguments> provideModifierLeakCases() { |
| 64 | + return Stream.of( |
| 65 | + Arguments.of( |
| 66 | + "SELECT a FROM t1 UNION ALL SELECT b FROM t2 EXCEPT SELECT c FROM t3", |
| 67 | + "EXCEPT ALL"), |
| 68 | + Arguments.of( |
| 69 | + "SELECT a FROM t1 INTERSECT ALL SELECT b FROM t2 UNION SELECT c FROM t3", |
| 70 | + "UNION ALL")); |
99 | 71 | } |
100 | 72 |
|
101 | | - @Test |
102 | | - public void testMixedModifiersPreserved() throws JSQLParserException { |
103 | | - // UNION ALL followed by EXCEPT DISTINCT |
104 | | - assertSqlCanBeParsedAndDeparsed( |
105 | | - "SELECT a FROM t1 UNION ALL SELECT b FROM t2 EXCEPT DISTINCT SELECT c FROM t3"); |
| 73 | + @ParameterizedTest |
| 74 | + @MethodSource("provideSetOperationObjectCases") |
| 75 | + void testSetOperationObjectState(String sql, Class<?> expectedType, |
| 76 | + boolean expectedAll, boolean expectedDistinct) throws JSQLParserException { |
| 77 | + SetOperationList setOpList = (SetOperationList) CCJSqlParserUtil.parse(sql); |
| 78 | + SetOperation op = setOpList.getOperations().get(0); |
| 79 | + assertInstanceOf(expectedType, op); |
| 80 | + assertEquals(expectedAll, op.isAll(), |
| 81 | + "isAll() mismatch for: " + sql); |
| 82 | + assertEquals(expectedDistinct, op.isDistinct(), |
| 83 | + "isDistinct() mismatch for: " + sql); |
106 | 84 | } |
107 | 85 |
|
108 | | - // ── SetOperation object state verification ────────────────── |
109 | | - |
110 | | - @Test |
111 | | - public void testExceptAllSetOperationObject() throws JSQLParserException { |
112 | | - String sql = "SELECT a FROM t1 EXCEPT ALL SELECT a FROM t2"; |
113 | | - Statement stmt = CCJSqlParserUtil.parse(sql); |
114 | | - SetOperationList setOpList = (SetOperationList) stmt; |
115 | | - SetOperation op = setOpList.getOperation(0); |
116 | | - |
117 | | - assertInstanceOf(ExceptOp.class, op); |
118 | | - assertTrue(op.isAll(), "ExceptOp should report isAll() == true"); |
119 | | - assertFalse(op.isDistinct(), "ExceptOp should report isDistinct() == false"); |
120 | | - } |
121 | | - |
122 | | - @Test |
123 | | - public void testMinusAllSetOperationObject() throws JSQLParserException { |
124 | | - String sql = "SELECT a FROM t1 MINUS ALL SELECT a FROM t2"; |
125 | | - Statement stmt = CCJSqlParserUtil.parse(sql); |
126 | | - SetOperationList setOpList = (SetOperationList) stmt; |
127 | | - SetOperation op = setOpList.getOperation(0); |
128 | | - |
129 | | - assertInstanceOf(MinusOp.class, op); |
130 | | - assertTrue(op.isAll(), "MinusOp should report isAll() == true"); |
| 86 | + private static Stream<Arguments> provideSetOperationObjectCases() { |
| 87 | + return Stream.of( |
| 88 | + Arguments.of("SELECT a FROM t1 EXCEPT ALL SELECT a FROM t2", |
| 89 | + ExceptOp.class, true, false), |
| 90 | + Arguments.of("SELECT a FROM t1 MINUS ALL SELECT a FROM t2", |
| 91 | + MinusOp.class, true, false)); |
131 | 92 | } |
132 | 93 | } |
0 commit comments