fix: support ClickHouse C-style ternary operator (? :) (#2436) - #2466
Merged
manticore-projects merged 1 commit intoAug 14, 2026
Merged
Conversation
…JSQLParser#2436) Parse cond ? then : else as a TernaryExpression at the boolean-operator level in prattExpressionRest, keeping C-style precedence (binds looser than AND/OR) and right-associativity. A pending ? is disambiguated between this ternary and the PostgreSQL JSON operator via isTernaryAhead, and inside a then-branch a top-level : closes the ternary instead of starting a JSON path, so both dialects keep working. Signed-off-by: 付典 <fudianchn@gmail.com>
fudianchn
force-pushed
the
fix/clickhouse-ternary-operator-2436
branch
from
August 14, 2026 12:38
10b8465 to
9d0e057
Compare
Contributor
|
Excellent work! Thank your for your contribution! |
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
Support the ClickHouse C-style ternary conditional operator
cond ? then : else(an alias forif(cond, then, else)), fixing #2436:JSQLParser currently rejects these with
ParseException: Encountered: "?".Why / Root cause
?is already double-booked in the grammar: as a JDBC parameter atom (JdbcParameter) and as the PostgreSQL JSON existence operator (RegularConditionRHSconsumesexpr ? 'key'). The ternary needs the third reading:?after a complete boolean expression, closed by a:. Neither existing path can express that, sox > 0 ? 'y' : 'n'fails at the?.How
The ternary is integrated into the existing Pratt boolean loop
prattExpressionRest, at the same precedence level asOR(C semantics: binds looser thanAND/OR, right-associative):?is routed to the ternary via a newisTernaryAhead()scan (same pattern asisUnparenthesizedSelectAhead): it is a ternary when a standalone:closes the then-branch at the same nesting depth before any expression boundary (,,;,), EOF, clause keyword). Otherwise the?keeps its current meaning as the JSON operator —isConditionSuffixAhead()/isComparisonOperatorAhead()decline it only when it is a ternary.:separator collided with the JSON path operator (b : pathinPrimaryExpression/JsonExpression); inside a then-branch a top-level:now closes the ternary instead (guarded by aternaryThenBranchDepthcounter,LOOKAHEAD(2, { ... })).AST: new
TernaryExpression(condition, thenExpression, elseExpression)wired intoExpressionVisitor,ExpressionVisitorAdapter,TablesNamesFinder,ExpressionDeParserandExpressionValidator, following the pattern ofRangeExpression.Scope
Works in every expression context (select items,
WHERE,GROUP BY/HAVING,ORDER BY, function arguments, joinON,UPDATE SET,INSERT VALUES, subqueries, nested ternaries in both branches, JDBC parameters as branches). The PostgreSQL JSON operators (?,?|,?&,->,:paths) and JDBC parameters (?,?5,LIMIT ?) keep parsing unchanged; a JSON?followed later by a top-level:at depth 0 is inherently ambiguous across dialects and stays a JSON operator only when no:closes it first.Testing
TernaryExpressionTest(32 tests): the issue case with AST shape assertions, 16 context round-trips (parse + deparse), precedence/associativity AST assertions (a OR b ? c : dgroups as(a OR b) ? c : d,a ? b : c ? d : enests right), and 11 unaffected-syntax guards for JSON operators, JSON paths, JDBC parameters and array ranges.Local:
spotlessApply,checkstyleMain/Test,spotbugsMain,pmdMainclean; full test suite green (4737 tests, 0 failures).Performance
gradle jmh,JSQLParserBenchmark.parseSQLStatementsonperformance.sql,version=latest, 10 forks × 10 iterations (100 samples) on a 32-core host:3.714 ± 0.0213.730 ± 0.023The
+0.43%delta lies within the 99.9% confidence intervals (the CIs overlap from3.707to3.735): no regression. When no?is present the only added cost is one string comparison perprattExpressionRestiteration.Verification of the original issue
SELECT x > 0 ? 'y' : 'n' FROM tnow parses and round-trips: