GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — fix: support ClickHouse C-style ternary operator (? :) (#2436) by fudianchn · Pull Request #2466 · JSQLParser/JSqlParser

Conversation

@fudianchn

@fudianchn fudianchn commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

What

Support the ClickHouse C-style ternary conditional operator cond ? then : else (an alias for if(cond, then, else)), fixing #2436:

SELECT x > 0 ? 'y' : 'n' FROM t
SELECT * FROM t WHERE a OR b ? c : d
SELECT abs(x > 0 ? 1 : 2) FROM t

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 (RegularConditionRHS consumes expr ? 'key'). The ternary needs the third reading: ? after a complete boolean expression, closed by a :. Neither existing path can express that, so x > 0 ? 'y' : 'n' fails at the ?.

How

The ternary is integrated into the existing Pratt boolean loop prattExpressionRest, at the same precedence level as OR (C semantics: binds looser than AND/OR, right-associative):

  • a pending ? is routed to the ternary via a new isTernaryAhead() scan (same pattern as isUnparenthesizedSelectAhead): 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.
  • the : separator collided with the JSON path operator (b : path in PrimaryExpression/JsonExpression); inside a then-branch a top-level : now closes the ternary instead (guarded by a ternaryThenBranchDepth counter, LOOKAHEAD(2, { ... })).

AST: new TernaryExpression(condition, thenExpression, elseExpression) wired into ExpressionVisitor, ExpressionVisitorAdapter, TablesNamesFinder, ExpressionDeParser and ExpressionValidator, following the pattern of RangeExpression.

Scope

Works in every expression context (select items, WHERE, GROUP BY/HAVING, ORDER BY, function arguments, join ON, 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 : d groups as (a OR b) ? c : d, a ? b : c ? d : e nests right), and 11 unaffected-syntax guards for JSON operators, JSON paths, JDBC parameters and array ranges.

Local: spotlessApply, checkstyleMain/Test, spotbugsMain, pmdMain clean; full test suite green (4737 tests, 0 failures).

Performance

gradle jmh, JSQLParserBenchmark.parseSQLStatements on performance.sql, version=latest, 10 forks × 10 iterations (100 samples) on a 32-core host:

build ms/op
master 3.714 ± 0.021
this change 3.730 ± 0.023

The +0.43% delta lies within the 99.9% confidence intervals (the CIs overlap from 3.707 to 3.735): no regression. When no ? is present the only added cost is one string comparison per prattExpressionRest iteration.

Verification of the original issue

SELECT x > 0 ? 'y' : 'n' FROM t now parses and round-trips:

SELECT x > 0 ? 'y' : 'n' FROM t
condition      : x > 0 (GreaterThan)
thenExpression : 'y'
elseExpression : 'n'

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@fudianchn
fix: support ClickHouse C-style ternary operator (? :) in expressions (…
9d0e057
…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: 付典 <[email protected]>
@fudianchn
fudianchn force-pushed the fix/clickhouse-ternary-operator-2436 branch from 10b8465 to 9d0e057 Compare August 14, 2026 12:38
@manticore-projects

manticore-projects commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

Excellent work! Thank your for your contribution!

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@manticore-projects
manticore-projects merged commit 60b0875 into JSQLParser:master Aug 14, 2026
7 checks passed

Uh oh!

There was an error while loading. Please reload this page.

This was referenced Aug 16, 2026
[BUG] JSQLParser 5.4-SNAPSHOT : PostgreSQL : jsonb operator "?" fails together with JDBC named parameters (regression via #2466) #2475
Closed
fix: do not treat a named parameter ':' as the ternary else separator (#2475) #2476
Merged
@fudianchn
fudianchn deleted the fix/clickhouse-ternary-operator-2436 branch August 21, 2026 00:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Reviewers

No reviews

Uh oh!

There was an error while loading. Please reload this page.

Assignees

No one assigned

Labels

None yet

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

Uh oh!

There was an error while loading. Please reload this page.

2 participants

@fudianchn @manticore-projects
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.
You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.