diff --git a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt index 52afbaddf..37c28ae23 100644 --- a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt +++ b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt @@ -150,6 +150,21 @@ public class CCJSqlParser extends AbstractJSqlParser { node.jjtSetValue(access); } + /** + * Links a node whose production takes its left operand as a parameter, starting the node's + * token range at that operand rather than at the operator. The range is left as JJTree set it + * when the operand carries no node of its own. + */ + private void linkAST(ASTNodeAccess access, Node node, Expression leftExpression) { + linkAST(access, node); + if (leftExpression instanceof ASTNodeAccess) { + Node leftNode = ((ASTNodeAccess) leftExpression).getASTNode(); + if (leftNode != null && leftNode.jjtGetFirstToken() != null) { + node.jjtSetFirstToken(leftNode.jjtGetFirstToken()); + } + } + } + public Node getASTRoot() { return jjtree.rootNode(); } @@ -8665,7 +8680,7 @@ Expression RegularConditionRHS(Expression leftExpression, int oracleJoinRight) # } { - linkAST(result, jjtThis); + linkAST(result, jjtThis, leftExpression); return result; } } @@ -8754,7 +8769,7 @@ Expression InExpression(Expression leftExpression) #InExpression : .withOldOracleJoinSyntax(oldOracleJoin) .withNot(usingNot) .setGlobal(usingGlobal); - linkAST(inExpression,jjtThis); + linkAST(inExpression, jjtThis, leftExpression); return inExpression; } } @@ -8769,7 +8784,7 @@ Expression IncludesExpression(Expression leftExpression) #IncludesExpression : { IncludesExpression includesExpression = new IncludesExpression(leftExpression, rightExpression); - linkAST(includesExpression,jjtThis); + linkAST(includesExpression, jjtThis, leftExpression); return includesExpression; } } @@ -8784,7 +8799,7 @@ Expression ExcludesExpression(Expression leftExpression) #ExcludesExpression : { ExcludesExpression excludesExpression = new ExcludesExpression(leftExpression, rightExpression); - linkAST(excludesExpression,jjtThis); + linkAST(excludesExpression, jjtThis, leftExpression); return excludesExpression; } } @@ -8895,7 +8910,7 @@ Expression LikeExpression(Expression leftExpression) #LikeExpression: { result.setLeftExpression(leftExpression); result.setRightExpression(rightExpression); - linkAST(result,jjtThis); + linkAST(result, jjtThis, leftExpression); return result; } } @@ -8922,7 +8937,7 @@ Expression SimilarToExpression(Expression leftExpression) #SimilarToExpression: { result.setLeftExpression(leftExpression); result.setRightExpression(rightExpression); - linkAST(result,jjtThis); + linkAST(result, jjtThis, leftExpression); return result; } } @@ -8938,7 +8953,7 @@ Expression IsDistinctExpression(Expression leftExpression) #IsDistinctExpression { result.setLeftExpression(leftExpression); result.setRightExpression(rightExpression); - linkAST(result,jjtThis); + linkAST(result, jjtThis, leftExpression); return result; } } diff --git a/src/test/java/net/sf/jsqlparser/statement/select/SelectASTTest.java b/src/test/java/net/sf/jsqlparser/statement/select/SelectASTTest.java index d45052d61..5fd6a7930 100644 --- a/src/test/java/net/sf/jsqlparser/statement/select/SelectASTTest.java +++ b/src/test/java/net/sf/jsqlparser/statement/select/SelectASTTest.java @@ -12,6 +12,9 @@ import java.util.ArrayList; import java.util.List; import net.sf.jsqlparser.JSQLParserException; +import net.sf.jsqlparser.expression.Expression; +import net.sf.jsqlparser.expression.operators.conditional.AndExpression; +import net.sf.jsqlparser.parser.ASTNodeAccess; import net.sf.jsqlparser.parser.CCJSqlParserDefaultVisitor; import net.sf.jsqlparser.parser.CCJSqlParserTreeConstants; import net.sf.jsqlparser.parser.CCJSqlParser; @@ -176,10 +179,30 @@ public Object visit(Node node, Object data) { assertNotNull(subSelectStart); assertNotNull(subSelectEnd); - assertEquals(32, subSelectStart.beginColumn); + // the node spans the whole IN expression, so it starts at the left operand + assertEquals(30, subSelectStart.beginColumn); assertEquals(49, subSelectEnd.endColumn); } + @Test + public void testBinaryConditionNodeStartsAtItsLeftOperand() throws JSQLParserException { + String[][] cases = { + {"SELECT * FROM t WHERE z = 0 AND a = 1", "a = 1"}, + {"SELECT * FROM t WHERE z = 0 AND a IN (1, 2)", "a IN (1, 2)"}, + {"SELECT * FROM t WHERE z = 0 AND a LIKE 'p'", "a LIKE 'p'"}, + {"SELECT * FROM t WHERE z = 0 AND a SIMILAR TO 'p'", "a SIMILAR TO 'p'"}, + {"SELECT * FROM t WHERE z = 0 AND a IS DISTINCT FROM 1", "a IS DISTINCT FROM 1"}}; + + for (String[] testCase : cases) { + String sql = testCase[0]; + Expression condition = ((AndExpression) ((PlainSelect) CCJSqlParserUtil.parse(sql)) + .getWhere()).getRightExpression(); + Node node = ((ASTNodeAccess) condition).getASTNode(); + assertEquals(testCase[1], sql.substring(node.jjtGetFirstToken().absoluteBegin - 1, + node.jjtGetLastToken().absoluteEnd - 1), sql); + } + } + @Test public void testSelectASTExtractWithCommentsIssue1580() throws JSQLParserException { String sql =