fix: reject illegal MERGE WHEN NOT MATCHED clause pairings (#2480) - #2481
Open
fudianchn wants to merge 1 commit into
Open
fix: reject illegal MERGE WHEN NOT MATCHED clause pairings (#2480)#2481fudianchn wants to merge 1 commit into
fudianchn wants to merge 1 commit into
Conversation
…r#2480) WHEN NOT MATCHED accepted any clause regardless of the BY qualifier, and the AST collapsed the illegal pairings into the opposite semantics on deparse: WHEN NOT MATCHED [BY TARGET] THEN UPDATE/DELETE printed as WHEN MATCHED, WHEN NOT MATCHED BY SOURCE THEN INSERT printed as WHEN NOT MATCHED. SQL Server and BigQuery reject these pairings at parse time. Validate the pairing via MergeSide.validatePairing, mirroring the existing MergeSide.fromImage validation. Signed-off-by: 付典 <fudianchn@gmail.com>
Contributor
Author
|
The only failing check is Gradle Check (macos-latest), which failed in its Set up job step while downloading the actions/setup-java action (429 Too Many Requests from codeload.github.com): a transient GitHub infrastructure issue unrelated to this change. Gradle Check on ubuntu/windows and Codacy pass for the same commit; Maven Verify was skipped because of the failed sibling job. I cannot re-run the failed job on a fork PR, please re-run it when convenient. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Rejects the five illegal
WHEN NOT MATCHEDside / clause pairings at parse time instead of silently rewriting them:WHEN NOT MATCHED [BY TARGET] THEN UPDATE/DELETEandWHEN NOT MATCHED BY SOURCE THEN INSERTnow throw, matching what SQL Server and BigQuery do. Fixes #2480.Why
On master these pairings are accepted and the deparse collapses them into the opposite semantics:
WHEN NOT MATCHED [BY TARGET] THEN UPDATE/DELETEprints asWHEN MATCHED(condition inverted) andWHEN NOT MATCHED BY SOURCE THEN INSERTprints asWHEN NOT MATCHED(BY SOURCEdropped, a different row set). A silent semantic rewrite is the worst failure mode for tools that parse SQL to inspect it. This closes the gap left by #2453, which calibrated the six legal combinations but did not guard the illegal ones.How
MergeWhenNotMatchedends withMergeSide.validatePairing(side, operation), which enforces the pairing rule already documented onMergeSide:BY TARGET(the default) only allowsINSERT,BY SOURCEonly allowsUPDATE/DELETE. The validation lives inMergeSidenext to the existingfromImagevalidation and keeps the grammar action a single statement.WHEN MATCHED THEN INSERTis already rejected by the grammar and is pinned by a test as well.Alternative considered (not implemented): accept the illegal pairings and render them faithfully. This would require extending
MergeUpdate/MergeDelete/MergeInsertwith combinations no dialect accepts (MergeUpdatecurrently has noNOT MATCHEDdimension), adding AST surface for SQL that no database executes. Happy to switch to that direction or to move the check into the statement validator instead, if preferred.Root cause
MergeWhenNotMatchedaccepted any clause for any side, while the AST can only represent the legal pairings (MergeUpdate:82/MergeDelete:55renderWHEN MATCHEDfor any side other thanSOURCE,MergeInsert:77always rendersWHEN NOT MATCHED), so the deparse silently changed the meaning.Testing
MergeTest#testMergeRejectsInvalidWhenNotMatchedClausePairings: on master the five illegal pairings parse (test fails), with this change they throwJSQLParserExceptionwith the pairing message;./gradlew test --tests net.sf.jsqlparser.statement.merge.MergeTest-> 26 tests, 0 failures.#2421tests, untouched).Verification of the original issue
f41c0b8WHEN NOT MATCHED [BY TARGET] THEN UPDATE/DELETEWHEN MATCHEDWHEN NOT MATCHED [BY TARGET] cannot take an UPDATE or DELETE clauseWHEN NOT MATCHED BY SOURCE THEN INSERTWHEN NOT MATCHEDWHEN NOT MATCHED BY SOURCE cannot take an INSERT clauseGrammar tokens, productions and LOOKAHEADs are untouched (javacc warnings stay at 13) and the benchmark input
performance.sqlcontains noMERGEstatement, so the changed path never executes ingradle jmh; no performance impact is possible.Programmatic AST construction (building an illegal
MergeUpdateby hand and printing it) is not guarded; the check protects the parse entry point.Fixes #2480