fix: do not treat a named parameter ':' as the ternary else separator (#2475) - #2476
Conversation
Signed-off-by: 付典 <fudianchn@gmail.com>
|
While regression-testing the current snapshot build I found one edge case where this change silently altered behavior. A ternary whose then-branch ends with a plain positional parameter no longer parses as a
Cause: The round-trip case The fix is one line: add I can open a follow-up PR with the one-liner plus an AST-shape regression test if that works. |
Sure, and lesson learned: we should not chase too many too exotic features. There is a good reason for SQL Standard providing |
|
Understood. Going forward I will focus on standard SQL alignment. Follow-up PR: #2477. |
…hen-branch (#2477) A bare '?' (anonymous token, no kind constant) was missing from canEndExpression(), so a ternary whose then-branch ends with a positional parameter was silently misread as a jsonb operator: SELECT a ? ? : c FROM t parsed as JsonOperator(a, JsonExpression(?:c)) instead of TernaryExpression(a, JdbcParameter, c). Recognize it by image, like the closing brackets, and pin the AST shape with a regression test. Follow-up to #2476. Signed-off-by: 付典 <fudianchn@gmail.com>
What
The PostgreSQL jsonb operator
?keeps working when the same condition also contains JDBC named parameters:Why / Root cause
isTernaryAhead()(introduced with the ClickHouse ternary in #2466) committed to the ternary reading as soon as its token scan found ANY standalone:at bracket depth 0 before a clause boundary. A JDBC named parameter starts with the very same:token, so the statements above were misread as a ternary and then failed withExpected ':' closing the ternary conditional operator(both parsed before #2466: regression).How
The scan now requires the
:to be preceded by a token that can legally END an expression (canEndExpression()): identifier, literal, type keyword (a cast target, e.g.b :: int), JDBC parameter, non-reserved keyword, or a balanced closing bracket. The then-branch of a ternary must be a complete expression, so its closing:can never follow an operator directly. A:in operand position (after=,AND, the leading?, ...) starts a named parameter instead: the scan skips it and keeps looking for the real separator.Consequences:
j ? :key/j ? 'k' AND x = :pparse again as jsonb operator + named parameter (sameJsonOperatorAST as before fix: support ClickHouse C-style ternary operator (? :) (#2436) #2466).TernaryExpressionTestcases stay green (a ? b : c,a ? ? : cwith a positional parameter as then-branch, parentheses,a ? b OR c : d,CASE ... END : e, right-associative nesting).a ? b :: int : cstill parses as aTernaryExpressionwith aCastExpressionthen-branch (the type keyword ends the expression, so the:after it closes the ternary).a ? :t : :enow has a defined reading (named parameter then- and else-branches).Scope
Only the ternary-vs-JSON disambiguation predicate changes; the ternary grammar productions, the
TernaryExpressionAST, and the JSON operator handling are untouched.Testing
TernaryExpressionTest.testTernaryAndJsonbWithJdbcNamedParameters(6 round-trip cases) andtestJsonbOperatorWithNamedParameter(AST shape) reproduce the issue: 5 of these executions fail onmasterwithJSQLParserException.testTernaryWithCastThenBranchguards the cast-then-branch reading. All 42 tests ofTernaryExpressionTestpass with this change.Performance
gradle jmh,JSQLParserBenchmark.parseSQLStatementsonperformance.sql,version=latest, 10 forks x 10 iterations (100 samples per run, two interleaved runs per build) on a 32-core host:master(7b64825)The two builds interleave across the repeated runs (pair 1:
+0.24%, pair 2:-0.37%); every delta lies within the 99.9% confidence intervals: no regression.Verification of the original issue
Both statements from the issue fail on
masterand parse + round-trip with this change.Fixes #2475