You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[BUG] EXCEPT ALL/DISTINCT and MINUS ALL/DISTINCT modifiers lost during parse-toString round-trip #2419
EXCEPT ALL, EXCEPT DISTINCT, MINUS ALL, and MINUS DISTINCT modifiers are silently dropped during parsing. The parser accepts the modifier syntax but does not pass the captured modifier string to the ExceptOp and MinusOp constructors.
Reproducer
Stringsql = "SELECT a FROM t1 EXCEPT ALL SELECT a FROM t2";
Statementstmt = CCJSqlParserUtil.parse(sql);
System.out.println(stmt.toString());
// Expected: SELECT a FROM t1 EXCEPT ALL SELECT a FROM t2// Actual: SELECT a FROM t1 EXCEPT SELECT a FROM t2
The same issue affects MINUS ALL:
Stringsql = "SELECT a FROM t1 MINUS ALL SELECT a FROM t2";
Statementstmt = CCJSqlParserUtil.parse(sql);
System.out.println(stmt.toString());
// Expected: SELECT a FROM t1 MINUS ALL SELECT a FROM t2// Actual: SELECT a FROM t1 MINUS SELECT a FROM t2
Root Cause
In JSqlParserCC.jjt, the SetOperationList rule captures the modifier via modifier=SetOperationModifier() but constructs MinusOp and ExceptOp with their no-arg constructors (which default to empty string), discarding the modifier:
<K_MINUS> [ modifier=SetOperationModifier() ] { MinusOp minus = new MinusOp(); ... }
<K_EXCEPT> [ modifier=SetOperationModifier() ] { ExceptOp except = new ExceptOp(); ... }
Compare with UnionOp and IntersectOp which correctly pass the modifier:
<K_UNION> [ modifier=SetOperationModifier() ] { UnionOp union = new UnionOp(modifier); ... }
<K_INTERSECT> [ modifier=SetOperationModifier() ] { IntersectOp intersect = new IntersectOp(modifier); ... }
Additionally, the modifier variable was not reset between iterations of the set-operation loop, causing modifiers to leak from one operator to the next (e.g., UNION ALL ... EXCEPT would incorrectly make the EXCEPT inherit ALL).
Impact
Semantic correctness: EXCEPT ALL preserves duplicates while EXCEPT removes them. Silently dropping the ALL modifier changes query semantics.
Round-trip fidelity: parse(sql).toString() does not reproduce the original SQL.
Affected versions
Current master (regression introduced in commit 5fe938bc which refactored SetOperationModifier handling for the CORRESPONDING feature).
Fix
Pass modifier to MinusOp and ExceptOp constructors
Reset modifier = null at the start of each set-operation loop iteration
A PR with the fix and regression tests will follow.
Activity
fix(parser): pass modifier to ExceptOp/MinusOp constructors and reset…
fix(parser): pass modifier to ExceptOp/MinusOp constructors (#2419) (#…