diff --git a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt index 973004a6e..d86e227d7 100644 --- a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt +++ b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt @@ -280,7 +280,9 @@ public class CCJSqlParser extends AbstractJSqlParser { * in operand position instead (directly after an operator, e.g. `x = :name` * or directly after the leading "?") starts a JDBC named parameter. * DATA_TYPE tokens end expressions as well, because a cast's target type - * (`b :: int`) or a bare type keyword closes the then-branch. + * (`b :: int`) or a bare type keyword closes the then-branch. The bare "?" + * positional parameter has no token-kind constant (anonymous token), so it + * is recognized by its image, like the closing brackets. */ private boolean canEndExpression(Token t) { if (t == null) { @@ -294,7 +296,8 @@ public class CCJSqlParser extends AbstractJSqlParser { return true; default: return (t.kind >= MIN_NON_RESERVED_WORD && t.kind <= MAX_NON_RESERVED_WORD) - || ")".equals(t.image) || "]".equals(t.image); + || ")".equals(t.image) || "]".equals(t.image) + || "?".equals(t.image); } } diff --git a/src/test/java/net/sf/jsqlparser/expression/TernaryExpressionTest.java b/src/test/java/net/sf/jsqlparser/expression/TernaryExpressionTest.java index 03f535db3..38f20111d 100644 --- a/src/test/java/net/sf/jsqlparser/expression/TernaryExpressionTest.java +++ b/src/test/java/net/sf/jsqlparser/expression/TernaryExpressionTest.java @@ -10,6 +10,7 @@ package net.sf.jsqlparser.expression; import net.sf.jsqlparser.JSQLParserException; +import net.sf.jsqlparser.expression.operators.arithmetic.Addition; import net.sf.jsqlparser.parser.CCJSqlParserUtil; import net.sf.jsqlparser.expression.operators.conditional.AndExpression; import net.sf.jsqlparser.expression.operators.conditional.OrExpression; @@ -65,6 +66,8 @@ void testIssue2436() throws JSQLParserException { "SELECT a IS NULL ? 'x' : y FROM t", // jdbc parameters as branches "SELECT a ? ? : c FROM t", + "SELECT a ? b + ? : c FROM t", + "SELECT a ? ? : ? FROM t", "SELECT a ? b : ? FROM t", "SELECT * FROM t WHERE x = ? AND y ? z : w", // case expression inside a branch @@ -156,6 +159,23 @@ void testTernaryWithCastThenBranch() throws JSQLParserException { Assertions.assertTrue(ternary.getThenExpression() instanceof CastExpression); } + @Test + void testTernaryWithPositionalParameterThenBranch() throws JSQLParserException { + // regression guard: a bare "?" positional parameter is a complete + // expression, so the ":" after it closes the ternary instead of being + // skipped in favor of the jsonb reading + Select select = (Select) CCJSqlParserUtil.parse("SELECT a ? ? : c FROM t"); + TernaryExpression ternary = (TernaryExpression) ((PlainSelect) select).getSelectItem(0) + .getExpression(); + Assertions.assertTrue(ternary.getThenExpression() instanceof JdbcParameter); + Assertions.assertTrue(ternary.getElseExpression() instanceof Column); + + // a positional parameter as the LAST token of the then-branch, too + select = (Select) CCJSqlParserUtil.parse("SELECT a ? b + ? : c FROM t"); + ternary = (TernaryExpression) ((PlainSelect) select).getSelectItem(0).getExpression(); + Assertions.assertTrue(ternary.getThenExpression() instanceof Addition); + } + @ParameterizedTest @ValueSource(strings = { // PostgreSQL jsonb operator combined with JDBC named parameters