Skip to content

Commit 6277335

Browse files
committed
fix: span a binary condition's node from its left operand
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 c9645d7 commit 6277335

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
}
@@ -8665,7 +8680,7 @@ Expression RegularConditionRHS(Expression leftExpression, int oracleJoinRight) #
86658680
}
86668681

86678682
{
8668-
linkAST(result, jjtThis);
8683+
linkAST(result, jjtThis, leftExpression);
86698684
return result;
86708685
}
86718686
}
@@ -8754,7 +8769,7 @@ Expression InExpression(Expression leftExpression) #InExpression :
87548769
.withOldOracleJoinSyntax(oldOracleJoin)
87558770
.withNot(usingNot)
87568771
.setGlobal(usingGlobal);
8757-
linkAST(inExpression,jjtThis);
8772+
linkAST(inExpression, jjtThis, leftExpression);
87588773
return inExpression;
87598774
}
87608775
}
@@ -8769,7 +8784,7 @@ Expression IncludesExpression(Expression leftExpression) #IncludesExpression :
87698784
{
87708785
IncludesExpression includesExpression = new IncludesExpression(leftExpression, rightExpression);
87718786

8772-
linkAST(includesExpression,jjtThis);
8787+
linkAST(includesExpression, jjtThis, leftExpression);
87738788
return includesExpression;
87748789
}
87758790
}
@@ -8784,7 +8799,7 @@ Expression ExcludesExpression(Expression leftExpression) #ExcludesExpression :
87848799
{
87858800
ExcludesExpression excludesExpression = new ExcludesExpression(leftExpression, rightExpression);
87868801

8787-
linkAST(excludesExpression,jjtThis);
8802+
linkAST(excludesExpression, jjtThis, leftExpression);
87888803
return excludesExpression;
87898804
}
87908805
}
@@ -8895,7 +8910,7 @@ Expression LikeExpression(Expression leftExpression) #LikeExpression:
88958910
{
88968911
result.setLeftExpression(leftExpression);
88978912
result.setRightExpression(rightExpression);
8898-
linkAST(result,jjtThis);
8913+
linkAST(result, jjtThis, leftExpression);
88998914
return result;
89008915
}
89018916
}
@@ -8922,7 +8937,7 @@ Expression SimilarToExpression(Expression leftExpression) #SimilarToExpression:
89228937
{
89238938
result.setLeftExpression(leftExpression);
89248939
result.setRightExpression(rightExpression);
8925-
linkAST(result,jjtThis);
8940+
linkAST(result, jjtThis, leftExpression);
89268941
return result;
89278942
}
89288943
}
@@ -8938,7 +8953,7 @@ Expression IsDistinctExpression(Expression leftExpression) #IsDistinctExpression
89388953
{
89398954
result.setLeftExpression(leftExpression);
89408955
result.setRightExpression(rightExpression);
8941-
linkAST(result,jjtThis);
8956+
linkAST(result, jjtThis, leftExpression);
89428957
return result;
89438958
}
89448959
}

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)