Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 8 additions & 5 deletions src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt
Original file line number Diff line number Diff line change
Expand Up @@ -681,7 +681,7 @@ public class CCJSqlParser extends AbstractJSqlParser<CCJSqlParser> {

/**
* Pratt arithmetic operator precedence loop.
* Handles: ^, ** (prec=7), *, /, %, DIV (prec=6) and +, -, ||, |, &, <<, >> (prec=5)
* Handles: ^, ** (prec=7), *, /, %, DIV (prec=6), +, -, || (prec=5) and |, &, #, <<, >> (prec=4)
* 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.
Expand Down Expand Up @@ -762,16 +762,19 @@ public class CCJSqlParser extends AbstractJSqlParser<CCJSqlParser> {
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;
else if (op == OP_CONCAT
|| (op == OP_PIPE && getToken(2).kind == OP_PIPE)) prec = 5;
// the bitwise operators bind looser than + and -, as in PostgreSQL, MySQL and SQLite
else if (op == S_HASH_OPERATOR || op == OP_PIPE
|| op == OP_LSHIFT || op == OP_RSHIFT) prec = 4;
else {
// Handle unnamed tokens by image
String img = t.image;
// DuckDB's LIMIT 10%: the % ends the row count instead of starting a modulo
if (t == limitPercentToken) break;
if ("*".equals(img) || "%".equals(img)) prec = 6;
else if ("+".equals(img) || "-".equals(img) || "&".equals(img)) prec = 5;
else if ("+".equals(img) || "-".equals(img)) prec = 5;
else if ("&".equals(img)) prec = 4;
else break; // not an arithmetic operator
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,25 @@
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.BitwiseLeftShift;
import net.sf.jsqlparser.expression.operators.arithmetic.BitwiseOr;
import net.sf.jsqlparser.expression.operators.arithmetic.BitwiseRightShift;
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.arithmetic.Subtraction;
import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
import net.sf.jsqlparser.expression.operators.relational.Between;
import net.sf.jsqlparser.expression.operators.relational.EqualsTo;
import net.sf.jsqlparser.expression.operators.relational.ExistsExpression;
import net.sf.jsqlparser.expression.operators.relational.InExpression;
import net.sf.jsqlparser.expression.operators.relational.Intersects;
import net.sf.jsqlparser.expression.operators.relational.IsNullExpression;
import net.sf.jsqlparser.expression.operators.relational.LikeExpression;
import net.sf.jsqlparser.expression.operators.relational.ParenthesedExpressionList;
import net.sf.jsqlparser.parser.AbstractJSqlParser.Dialect;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.schema.Column;
import net.sf.jsqlparser.test.TestUtils;
Expand All @@ -40,7 +46,15 @@ public class ExpressionPrecedenceTest {

@Test
public void testGetSign() throws JSQLParserException {
// || binds like +, so tighter than &
Expression expr = CCJSqlParserUtil.parseExpression("1&2||3");
Assertions.assertInstanceOf(BitwiseAnd.class, expr);
Assertions.assertInstanceOf(LongValue.class, ((BitwiseAnd) expr).getLeftExpression());
Assertions.assertInstanceOf(Concat.class, ((BitwiseAnd) expr).getRightExpression());

// PostgreSQL puts || and & on one level
expr = CCJSqlParserUtil.parseExpression("1&2||3", false,
parser -> parser.withDialect(Dialect.POSTGRESQL));
Assertions.assertInstanceOf(Concat.class, expr);
Assertions.assertInstanceOf(BitwiseAnd.class, ((Concat) expr).getLeftExpression());
Assertions.assertInstanceOf(LongValue.class, ((Concat) expr).getRightExpression());
Expand Down Expand Up @@ -252,4 +266,47 @@ public void testCaretIsLeftAssociative() throws JSQLParserException {
Assertions.assertInstanceOf(BitwiseXor.class, outer.getLeftExpression());
Assertions.assertInstanceOf(LongValue.class, outer.getRightExpression());
}

@Test
public void testBitwiseOperatorsBindLooserThanAddition() throws JSQLParserException {
// PostgreSQL, MySQL and SQLite all evaluate 1 & 7 - 2 as 1 & (7 - 2)
BitwiseAnd and = Assertions.assertInstanceOf(BitwiseAnd.class,
CCJSqlParserUtil.parseExpression("1 & 7 - 2"));
Assertions.assertInstanceOf(LongValue.class, and.getLeftExpression());
Assertions.assertInstanceOf(Subtraction.class, and.getRightExpression());

BitwiseOr or = Assertions.assertInstanceOf(BitwiseOr.class,
CCJSqlParserUtil.parseExpression("a | b + c"));
Assertions.assertInstanceOf(Addition.class, or.getRightExpression());

BitwiseLeftShift leftShift = Assertions.assertInstanceOf(BitwiseLeftShift.class,
CCJSqlParserUtil.parseExpression("1 << n - 1"));
Assertions.assertInstanceOf(Subtraction.class, leftShift.getRightExpression());

BitwiseRightShift rightShift = Assertions.assertInstanceOf(BitwiseRightShift.class,
CCJSqlParserUtil.parseExpression("a + b >> 2"));
Assertions.assertInstanceOf(Addition.class, rightShift.getLeftExpression());

Intersects xor = Assertions.assertInstanceOf(Intersects.class,
CCJSqlParserUtil.parseExpression("a # b + c", false,
parser -> parser.withDialect(Dialect.POSTGRESQL)));
Assertions.assertInstanceOf(Addition.class, xor.getRightExpression());

TestUtils.assertSqlCanBeParsedAndDeparsed("SELECT flags & 1 << bit - 1 FROM t");
}

@Test
public void testBitwiseOperatorsShareOneLeftAssociativeLevel() throws JSQLParserException {
// as in PostgreSQL and SQLite: 4 | 1 & 0 is (4 | 1) & 0
BitwiseAnd and = Assertions.assertInstanceOf(BitwiseAnd.class,
CCJSqlParserUtil.parseExpression("4 | 1 & 0"));
Assertions.assertInstanceOf(BitwiseOr.class, and.getLeftExpression());
}

@Test
public void testSpaceSeparatedConcatStillBindsLikeAddition() throws JSQLParserException {
BitwiseAnd and = Assertions.assertInstanceOf(BitwiseAnd.class,
CCJSqlParserUtil.parseExpression("a & b | | c"));
Assertions.assertInstanceOf(Concat.class, and.getRightExpression());
}
}
Loading