diff --git a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt index e2a4c900d..902ece9a7 100644 --- a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt +++ b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt @@ -681,7 +681,7 @@ public class CCJSqlParser extends AbstractJSqlParser { /** * Pratt arithmetic operator precedence loop. - * Handles: *, /, %, ^, DIV (prec=6) and +, -, ||, |, &, <<, >> (prec=5) + * Handles: ^, ** (prec=7), *, /, %, DIV (prec=6) and +, -, ||, |, &, <<, >> (prec=5) * Called only in real-parse mode (not syntactic-LOOKAHEAD mode) so action * blocks execute correctly. Safe now that no syntactic production LOOKAHEADs * scan through arithmetic expressions. @@ -759,8 +759,9 @@ public class CCJSqlParser extends AbstractJSqlParser { if (jsonAccess || op == OP_JSON_PATH_DELETE || op == OP_JSON_PATH_EXISTS || (op == OP_CONCAT && Dialect.POSTGRESQL.name() .equals(getAsString(Feature.dialect)))) prec = 4; - else if (op == OP_SLASH || op == OP_CARET || op == K_DIV) prec = 6; - else if (op == OP_POWER) prec = 7; + else if (op == OP_SLASH || op == K_DIV) prec = 6; + // ^ binds tighter than * / %, both as PostgreSQL's power and as MySQL's XOR + else if (op == OP_CARET || op == OP_POWER) prec = 7; else if (op == S_HASH_OPERATOR) prec = 5; else if (op == OP_CONCAT || op == OP_PIPE || op == OP_LSHIFT || op == OP_RSHIFT) prec = 5; diff --git a/src/test/java/net/sf/jsqlparser/expression/ExpressionPrecedenceTest.java b/src/test/java/net/sf/jsqlparser/expression/ExpressionPrecedenceTest.java index a0a497371..12e15e877 100644 --- a/src/test/java/net/sf/jsqlparser/expression/ExpressionPrecedenceTest.java +++ b/src/test/java/net/sf/jsqlparser/expression/ExpressionPrecedenceTest.java @@ -12,7 +12,11 @@ import net.sf.jsqlparser.JSQLParserException; import net.sf.jsqlparser.expression.operators.arithmetic.Addition; import net.sf.jsqlparser.expression.operators.arithmetic.BitwiseAnd; +import net.sf.jsqlparser.expression.operators.arithmetic.BitwiseXor; import net.sf.jsqlparser.expression.operators.arithmetic.Concat; +import net.sf.jsqlparser.expression.operators.arithmetic.Division; +import net.sf.jsqlparser.expression.operators.arithmetic.Modulo; +import net.sf.jsqlparser.expression.operators.arithmetic.Multiplication; import net.sf.jsqlparser.expression.operators.conditional.AndExpression; import net.sf.jsqlparser.expression.operators.relational.Between; import net.sf.jsqlparser.expression.operators.relational.EqualsTo; @@ -217,4 +221,35 @@ public void testValuePositionNotStaysOnOperand() throws JSQLParserException { Addition add = Assertions.assertInstanceOf(Addition.class, addition); Assertions.assertInstanceOf(NotExpression.class, add.getRightExpression()); } + + @Test + public void testCaretBindsTighterThanMultiplication() throws JSQLParserException { + // PostgreSQL's power and MySQL's XOR both evaluate 2 * 3 ^ 2 as 2 * (3 ^ 2) + Multiplication multiplication = Assertions.assertInstanceOf(Multiplication.class, + CCJSqlParserUtil.parseExpression("2 * 3 ^ 2")); + Assertions.assertInstanceOf(LongValue.class, multiplication.getLeftExpression()); + Assertions.assertInstanceOf(BitwiseXor.class, multiplication.getRightExpression()); + + Division division = Assertions.assertInstanceOf(Division.class, + CCJSqlParserUtil.parseExpression("10 / 2 ^ 2")); + Assertions.assertInstanceOf(BitwiseXor.class, division.getRightExpression()); + + Modulo modulo = Assertions.assertInstanceOf(Modulo.class, + CCJSqlParserUtil.parseExpression("18 % 4 ^ 2")); + Assertions.assertInstanceOf(BitwiseXor.class, modulo.getRightExpression()); + + multiplication = Assertions.assertInstanceOf(Multiplication.class, + CCJSqlParserUtil.parseExpression("2 ^ 3 * 2")); + Assertions.assertInstanceOf(BitwiseXor.class, multiplication.getLeftExpression()); + + TestUtils.assertSqlCanBeParsedAndDeparsed("SELECT pi() * r ^ 2 FROM circles"); + } + + @Test + public void testCaretIsLeftAssociative() throws JSQLParserException { + BitwiseXor outer = Assertions.assertInstanceOf(BitwiseXor.class, + CCJSqlParserUtil.parseExpression("2 ^ 3 ^ 2")); + Assertions.assertInstanceOf(BitwiseXor.class, outer.getLeftExpression()); + Assertions.assertInstanceOf(LongValue.class, outer.getRightExpression()); + } }