Skip to content

Commit 0ecf096

Browse files
fix: span a binary condition's node from its left operand (#2615)
Seven productions take their left operand as a parameter, so the caller has already consumed those tokens when JJTree opens the node and the range starts at the operator. A node for `a IN (1, 2)` covered only `IN (1, 2)`, and one for `a = 1` only `= 1`, leaving anyone reading `jjtGetFirstToken()` a range that excludes the operand the condition is about. Comparisons are a regression from 1b51f05, which moved the left operand out of RegularCondition and into the caller as part of the Pratt refactor. IN, LIKE, SIMILAR TO and IS DISTINCT have taken their operand as a parameter for far longer and were already reporting the narrow range, so before that refactor the two halves of the family disagreed with each other. Link those nodes through an overload that starts the range at the left operand, which fixes the regression and makes the family consistent. testDetectInExpressions asserted the narrow range and now expects the wider one. Between, IsNullExpression, IsBooleanExpression, IsUnknownExpression, MemberOfExpression and OverlapsCondition also take a left operand but build no node at all, so they keep returning null and are left alone here.
1 parent e4cf316 commit 0ecf096

2 files changed

Lines changed: 46 additions & 8 deletions

File tree

src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,21 @@ public class CCJSqlParser extends AbstractJSqlParser<CCJSqlParser> {
150150
node.jjtSetValue(access);
151151
}
152152

153+
/**
154+
* Links a node whose production takes its left operand as a parameter, starting the node's
155+
* token range at that operand rather than at the operator. The range is left as JJTree set it
156+
* when the operand carries no node of its own.
157+
*/
158+
private void linkAST(ASTNodeAccess access, Node node, Expression leftExpression) {
159+
linkAST(access, node);
160+
if (leftExpression instanceof ASTNodeAccess) {
161+
Node leftNode = ((ASTNodeAccess) leftExpression).getASTNode();
162+
if (leftNode != null && leftNode.jjtGetFirstToken() != null) {
163+
node.jjtSetFirstToken(leftNode.jjtGetFirstToken());
164+
}
165+
}
166+
}
167+
153168
public Node getASTRoot() {
154169
return jjtree.rootNode();
155170
}
@@ -8704,7 +8719,7 @@ Expression RegularConditionRHS(Expression leftExpression, int oracleJoinRight) #
87048719
}
87058720

87068721
{
8707-
linkAST(result, jjtThis);
8722+
linkAST(result, jjtThis, leftExpression);
87088723
return result;
87098724
}
87108725
}
@@ -8793,7 +8808,7 @@ Expression InExpression(Expression leftExpression) #InExpression :
87938808
.withOldOracleJoinSyntax(oldOracleJoin)
87948809
.withNot(usingNot)
87958810
.setGlobal(usingGlobal);
8796-
linkAST(inExpression,jjtThis);
8811+
linkAST(inExpression, jjtThis, leftExpression);
87978812
return inExpression;
87988813
}
87998814
}
@@ -8808,7 +8823,7 @@ Expression IncludesExpression(Expression leftExpression) #IncludesExpression :
88088823
{
88098824
IncludesExpression includesExpression = new IncludesExpression(leftExpression, rightExpression);
88108825

8811-
linkAST(includesExpression,jjtThis);
8826+
linkAST(includesExpression, jjtThis, leftExpression);
88128827
return includesExpression;
88138828
}
88148829
}
@@ -8823,7 +8838,7 @@ Expression ExcludesExpression(Expression leftExpression) #ExcludesExpression :
88238838
{
88248839
ExcludesExpression excludesExpression = new ExcludesExpression(leftExpression, rightExpression);
88258840

8826-
linkAST(excludesExpression,jjtThis);
8841+
linkAST(excludesExpression, jjtThis, leftExpression);
88278842
return excludesExpression;
88288843
}
88298844
}
@@ -8934,7 +8949,7 @@ Expression LikeExpression(Expression leftExpression) #LikeExpression:
89348949
{
89358950
result.setLeftExpression(leftExpression);
89368951
result.setRightExpression(rightExpression);
8937-
linkAST(result,jjtThis);
8952+
linkAST(result, jjtThis, leftExpression);
89388953
return result;
89398954
}
89408955
}
@@ -8961,7 +8976,7 @@ Expression SimilarToExpression(Expression leftExpression) #SimilarToExpression:
89618976
{
89628977
result.setLeftExpression(leftExpression);
89638978
result.setRightExpression(rightExpression);
8964-
linkAST(result,jjtThis);
8979+
linkAST(result, jjtThis, leftExpression);
89658980
return result;
89668981
}
89678982
}
@@ -8977,7 +8992,7 @@ Expression IsDistinctExpression(Expression leftExpression) #IsDistinctExpression
89778992
{
89788993
result.setLeftExpression(leftExpression);
89798994
result.setRightExpression(rightExpression);
8980-
linkAST(result,jjtThis);
8995+
linkAST(result, jjtThis, leftExpression);
89818996
return result;
89828997
}
89838998
}

src/test/java/net/sf/jsqlparser/statement/select/SelectASTTest.java

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@
1212
import java.util.ArrayList;
1313
import java.util.List;
1414
import net.sf.jsqlparser.JSQLParserException;
15+
import net.sf.jsqlparser.expression.Expression;
16+
import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
17+
import net.sf.jsqlparser.parser.ASTNodeAccess;
1518
import net.sf.jsqlparser.parser.CCJSqlParserDefaultVisitor;
1619
import net.sf.jsqlparser.parser.CCJSqlParserTreeConstants;
1720
import net.sf.jsqlparser.parser.CCJSqlParser;
@@ -176,10 +179,30 @@ public Object visit(Node node, Object data) {
176179

177180
assertNotNull(subSelectStart);
178181
assertNotNull(subSelectEnd);
179-
assertEquals(32, subSelectStart.beginColumn);
182+
// the node spans the whole IN expression, so it starts at the left operand
183+
assertEquals(30, subSelectStart.beginColumn);
180184
assertEquals(49, subSelectEnd.endColumn);
181185
}
182186

187+
@Test
188+
public void testBinaryConditionNodeStartsAtItsLeftOperand() throws JSQLParserException {
189+
String[][] cases = {
190+
{"SELECT * FROM t WHERE z = 0 AND a = 1", "a = 1"},
191+
{"SELECT * FROM t WHERE z = 0 AND a IN (1, 2)", "a IN (1, 2)"},
192+
{"SELECT * FROM t WHERE z = 0 AND a LIKE 'p'", "a LIKE 'p'"},
193+
{"SELECT * FROM t WHERE z = 0 AND a SIMILAR TO 'p'", "a SIMILAR TO 'p'"},
194+
{"SELECT * FROM t WHERE z = 0 AND a IS DISTINCT FROM 1", "a IS DISTINCT FROM 1"}};
195+
196+
for (String[] testCase : cases) {
197+
String sql = testCase[0];
198+
Expression condition = ((AndExpression) ((PlainSelect) CCJSqlParserUtil.parse(sql))
199+
.getWhere()).getRightExpression();
200+
Node node = ((ASTNodeAccess) condition).getASTNode();
201+
assertEquals(testCase[1], sql.substring(node.jjtGetFirstToken().absoluteBegin - 1,
202+
node.jjtGetLastToken().absoluteEnd - 1), sql);
203+
}
204+
}
205+
183206
@Test
184207
public void testSelectASTExtractWithCommentsIssue1580() throws JSQLParserException {
185208
String sql =

0 commit comments

Comments
 (0)