Skip to content

Commit 927c724

Browse files
committed
fix: link statements nested in others, and position every comment
Statements that never pass through SingleStatement get their node where they are built: data-modifying CTEs, statements inside an Oracle block, CREATE SCHEMA elements, CREATE TABLE ... AS EXECUTE and the GRANT or REVOKE of ALTER DEFAULT PRIVILEGES. An IF statement's range takes in the semicolon it prints. A RangeNode that widens a tree node answers its children, id and dump too, so a WITH statement's node still shows the tree beneath it. Comments are positioned in one backwards pass over the recorded match begins, and comments before the end of input are positioned once parsing ends, since the end-of-file token gets no token action. Values in a primary expression, string literals, expression lists and row patterns get a RangeNode only once something wraps them, so a production's own tree node is the only node most of them get. On a 20,000-row INSERT ... VALUES this cuts the added allocation from 12.9% to 3.2%. The coverage test parses the Oracle tests in the Oracle dialect, checks that getParent() encloses each range, and runs its checks over the statements above, which the test corpus does not contain.
1 parent 4702549 commit 927c724

4 files changed

Lines changed: 214 additions & 83 deletions

File tree

‎src/main/java/net/sf/jsqlparser/parser/RangeNode.java‎

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
/**
1313
* A node outside the parse tree that records the tokens an object spans. It serves an object no
1414
* grammar rule opens a tree node for, or one whose text runs past its tree node. In that second
15-
* case it answers the tree node's parent, which the object's {@code getParent()} then reports.
15+
* case it stands in for the tree node, answering its parent, children and id, with the wider range.
1616
*/
1717
public final class RangeNode extends Node {
1818
private final Node treeNode;
@@ -28,4 +28,44 @@ public RangeNode(Token first, Token last, Node treeNode) {
2828
public Node jjtGetParent() {
2929
return treeNode == null ? null : treeNode.jjtGetParent();
3030
}
31+
32+
@Override
33+
public Node[] jjtGetChildren() {
34+
return treeNode == null ? super.jjtGetChildren() : treeNode.jjtGetChildren();
35+
}
36+
37+
@Override
38+
public Node jjtGetChild(int i) {
39+
return treeNode == null ? super.jjtGetChild(i) : treeNode.jjtGetChild(i);
40+
}
41+
42+
@Override
43+
public int jjtGetNumChildren() {
44+
return treeNode == null ? super.jjtGetNumChildren() : treeNode.jjtGetNumChildren();
45+
}
46+
47+
@Override
48+
public Object childrenAccept(CCJSqlParserVisitor visitor, Object data) {
49+
return treeNode == null ? super.childrenAccept(visitor, data)
50+
: treeNode.childrenAccept(visitor, data);
51+
}
52+
53+
@Override
54+
public int getId() {
55+
return treeNode == null ? super.getId() : treeNode.getId();
56+
}
57+
58+
@Override
59+
public String toString() {
60+
return treeNode == null ? super.toString() : treeNode.toString();
61+
}
62+
63+
@Override
64+
public void dump(String prefix) {
65+
if (treeNode == null) {
66+
super.dump(prefix);
67+
} else {
68+
treeNode.dump(prefix);
69+
}
70+
}
3171
}

‎src/main/java/net/sf/jsqlparser/parser/SimpleCharStream.java‎

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ public class SimpleCharStream {
4848
protected boolean trackLineColumn = true;
4949
protected int totalCharsRead = 0;
5050
protected int absoluteTokenBegin = 0;
51-
// line, column and absolute position of each match begun since the last forgetMatchBegins()
51+
// line, column and absolute position of each match begun since the last positionSpecialTokens()
5252
private int[] matchBegins = new int[24];
5353
private int matchBeginCount = 0;
5454
int bufsize;
@@ -111,20 +111,25 @@ public final int getAbsoluteTokenBegin() {
111111
}
112112

113113
/**
114-
* The absolute position of the match that began at the given line and column since the last
115-
* {@link #forgetMatchBegins()}, or 0 when none did. Lets a comment, which no token action sees,
116-
* take its position from where its match began.
114+
* Gives the comments in front of {@code token} the positions where their matches began, from
115+
* the begins recorded since the last call, and forgets those. Comments come through no token
116+
* action, so this is how they get a position. The comments chain backwards from the token while
117+
* the begins are recorded forwards, so one backwards pass pairs them.
117118
*/
118-
public final int getAbsoluteBegin(int line, int column) {
119-
for (int i = 0; i < matchBeginCount; i += 3) {
120-
if (matchBegins[i] == line && matchBegins[i + 1] == column) {
121-
return matchBegins[i + 2];
119+
public final void positionSpecialTokens(Token token) {
120+
int i = matchBeginCount - 3;
121+
for (Token comment = token.specialToken; comment != null; comment = comment.specialToken) {
122+
while (i >= 0 && (matchBegins[i] != comment.beginLine
123+
|| matchBegins[i + 1] != comment.beginColumn)) {
124+
i -= 3;
122125
}
126+
if (i < 0) {
127+
break;
128+
}
129+
comment.absoluteBegin = matchBegins[i + 2];
130+
comment.absoluteEnd = comment.absoluteBegin + comment.image.length();
131+
i -= 3;
123132
}
124-
return 0;
125-
}
126-
127-
public final void forgetMatchBegins() {
128133
matchBeginCount = 0;
129134
}
130135

0 commit comments

Comments
 (0)