Skip to content

Commit 47e1abe

Browse files
Merge branch 'master' of github.com:JSQLParser/JSqlParser
2 parents e2c2f81 + a2e0cec commit 47e1abe

226 files changed

Lines changed: 13666 additions & 1227 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎README.md‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,11 @@ and missing syntax gets added on demand — [open an issue](https://github.com/J
147147
| **DDL** | `CREATE …` · `ALTER …` · `DROP …` |
148148
| **Oracle vector types** | `VECTOR(*, *)` · `VECTOR(*, FLOAT32)` · `VECTOR(3, *)`, including dense and sparse storage formats |
149149
| **PostgreSQL ownership** | `DROP OWNED` · `REASSIGN OWNED` |
150-
| **MySQL table maintenance** | `ANALYZE` · `CHECK` · `OPTIMIZE` · `REPAIR TABLE` |
150+
| **Two-phase commit** | PostgreSQL `PREPARE TRANSACTION` · `COMMIT PREPARED` · `ROLLBACK PREPARED` · MySQL `XA START`/`END`/`PREPARE`/`COMMIT`/`ROLLBACK`/`RECOVER` |
151+
| **MySQL table maintenance** | `ANALYZE` · `CHECK` · `CHECKSUM` · `OPTIMIZE` · `REPAIR TABLE` |
152+
| **PostgreSQL maintenance** | `VACUUM` · `ANALYZE` · `CLUSTER` · `CHECKPOINT` |
153+
| **MySQL locks** | `LOCK TABLES` · `UNLOCK TABLES` · `LOCK INSTANCE FOR BACKUP` · `UNLOCK INSTANCE` |
154+
| **PostgreSQL session statements** | `DISCARD` · `LISTEN` · `UNLISTEN` · `SHOW`/`RESET TIME ZONE`/`TRANSACTION ISOLATION LEVEL`/`SESSION AUTHORIZATION` · `COPY` options from before 9.0 |
151155
| **MySQL account management** | `CREATE`/`ALTER`/`RENAME USER` · `CREATE ROLE` · `SET ROLE` · `SET DEFAULT ROLE` · `SET PASSWORD` |
152156
| **Tablespaces** | MySQL and PostgreSQL `CREATE TABLESPACE` · `ALTER TABLESPACE` |
153157
| **PostgreSQL RLS** | `CREATE POLICY` · `ALTER TABLE … ENABLE`/`DISABLE`/`FORCE`/`NO FORCE ROW LEVEL SECURITY` |

‎src/main/java/net/sf/jsqlparser/expression/Function.java‎

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import net.sf.jsqlparser.statement.select.Limit;
1818
import net.sf.jsqlparser.statement.select.OrderByElement;
1919

20+
import net.sf.jsqlparser.statement.create.table.ColDataType;
2021
import net.sf.jsqlparser.statement.create.table.ColumnDefinition;
2122
import net.sf.jsqlparser.statement.select.PlainSelect;
2223

@@ -59,6 +60,12 @@ public class Function extends ASTNodeAccessImpl implements Expression {
5960
*/
6061
private List<KeywordArgument> keywordArguments = null;
6162

63+
/**
64+
* The {@code AS type} cast of the argument, as in MySQL's {@code WEIGHT_STRING(str AS CHAR(3))}
65+
* or {@code WEIGHT_STRING(str AS BINARY(4))}.
66+
*/
67+
private ColDataType castType;
68+
6269
/** Column definitions supplied for a record-returning function inside ROWS FROM. */
6370
public List<ColumnDefinition> getResultColumnDefinitions() {
6471
return resultColumnDefinitions;
@@ -83,6 +90,31 @@ public Function(String name, Expression... parameters) {
8390
this.parameters = new ExpressionList<>(parameters);
8491
}
8592

93+
/**
94+
* Returns the {@code AS type} cast written after the argument, as in MySQL's
95+
* {@code WEIGHT_STRING(str AS CHAR(3))}, or {@code null}.
96+
*/
97+
public ColDataType getCastType() {
98+
return castType;
99+
}
100+
101+
public void setCastType(ColDataType castType) {
102+
this.castType = castType;
103+
}
104+
105+
public Function withCastType(ColDataType castType) {
106+
setCastType(castType);
107+
return this;
108+
}
109+
110+
/** Appends {@code " AS type"} when the argument has a {@link #getCastType() cast type}. */
111+
public StringBuilder appendCastTypeTo(StringBuilder builder) {
112+
if (castType != null) {
113+
builder.append(" AS ").append(castType);
114+
}
115+
return builder;
116+
}
117+
86118
@Override
87119
public <T, S> T accept(ExpressionVisitor<T> expressionVisitor, S context) {
88120
return expressionVisitor.visit(this, context);
@@ -385,6 +417,7 @@ public String toString() {
385417
}
386418

387419
b.append(parameters);
420+
appendCastTypeTo(b);
388421

389422
if (havingClause != null) {
390423
havingClause.appendTo(b);

‎src/main/java/net/sf/jsqlparser/expression/MySQLIndexHint.java‎

Lines changed: 60 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,35 @@
1212
import java.io.Serializable;
1313
import java.util.List;
1414

15+
/**
16+
* A MySQL index hint after a table reference:
17+
* {@code {USE|IGNORE|FORCE} {INDEX|KEY} [FOR {JOIN|ORDER BY|GROUP BY}] (index_list)}.
18+
* <p>
19+
* A table may carry several hints, see {@code Table.getIndexHints()}. {@code USE INDEX ()} has an
20+
* empty index list.
21+
*/
1522
public class MySQLIndexHint implements Serializable {
1623

1724
private final String action;
1825
private final String indexQualifier;
26+
private final Scope scope;
1927
private final List<String> indexNames;
2028

2129
public MySQLIndexHint(String action, String indexQualifier, List<String> indexNames) {
30+
this(action, indexQualifier, null, indexNames);
31+
}
32+
33+
/**
34+
* @param action {@code USE}, {@code IGNORE} or {@code FORCE}
35+
* @param indexQualifier {@code INDEX} or {@code KEY}
36+
* @param scope the {@code FOR JOIN|ORDER BY|GROUP BY} scope, or {@code null} for every use
37+
* @param indexNames the index names, empty for {@code USE INDEX ()}
38+
*/
39+
public MySQLIndexHint(String action, String indexQualifier, Scope scope,
40+
List<String> indexNames) {
2241
this.action = action;
2342
this.indexQualifier = indexQualifier;
43+
this.scope = scope;
2444
this.indexNames = indexNames;
2545
}
2646

@@ -32,22 +52,53 @@ public String getIndexQualifier() {
3252
return indexQualifier;
3353
}
3454

55+
/**
56+
* Returns the part of the query the hint applies to, or {@code null} when no {@code FOR} clause
57+
* was given.
58+
*/
59+
public Scope getScope() {
60+
return scope;
61+
}
62+
3563
public List<String> getIndexNames() {
3664
return indexNames;
3765
}
3866

39-
@Override
40-
public String toString() {
41-
// use|ignore|force key|index (index1,...,indexN)
42-
StringBuilder buffer = new StringBuilder();
43-
buffer.append(" ").append(action).append(" ").append(indexQualifier).append(" (");
67+
/** Appends {@code " USE INDEX [FOR scope] (i1,i2)"}, with a leading space. */
68+
public StringBuilder appendTo(StringBuilder builder) {
69+
// use|ignore|force key|index [for join|order by|group by] (index1,...,indexN)
70+
builder.append(" ").append(action).append(" ").append(indexQualifier);
71+
if (scope != null) {
72+
builder.append(" FOR ").append(scope.getKeywords());
73+
}
74+
builder.append(" (");
4475
for (int i = 0; i < indexNames.size(); i++) {
4576
if (i > 0) {
46-
buffer.append(",");
77+
builder.append(",");
4778
}
48-
buffer.append(indexNames.get(i));
79+
builder.append(indexNames.get(i));
80+
}
81+
return builder.append(")");
82+
}
83+
84+
@Override
85+
public String toString() {
86+
return appendTo(new StringBuilder()).toString();
87+
}
88+
89+
/** The {@code FOR} clause of an index hint. */
90+
public enum Scope {
91+
JOIN("JOIN"), ORDER_BY("ORDER BY"), GROUP_BY("GROUP BY");
92+
93+
private final String keywords;
94+
95+
Scope(String keywords) {
96+
this.keywords = keywords;
97+
}
98+
99+
/** Returns the keywords after {@code FOR}, for example {@code ORDER BY}. */
100+
public String getKeywords() {
101+
return keywords;
49102
}
50-
buffer.append(")");
51-
return buffer.toString();
52103
}
53104
}

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

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -71,22 +71,20 @@ public StringBuilder appendTo(StringBuilder builder) {
7171
return builder;
7272
}
7373

74+
/**
75+
* The nearest enclosing object in the tree, or null when there is none. A node that carries
76+
* only a token range, for an object no grammar rule opens a tree node for, has no parent.
77+
*/
7478
public ASTNodeAccess getParent() {
75-
Node parent = (Node) node.jjtGetParent();
76-
while (parent.jjtGetValue() == null) {
77-
parent = (Node) parent.jjtGetParent();
78-
}
79-
80-
return ASTNodeAccess.class.cast(parent.jjtGetValue());
79+
return getParent(ASTNodeAccess.class);
8180
}
8281

8382
public <T extends ASTNodeAccess> T getParent(Class<T> clazz) {
84-
Node parent = (Node) node.jjtGetParent();
85-
while (parent.jjtGetValue() == null || !clazz.isInstance(parent.jjtGetValue())) {
83+
Node parent = node == null ? null : (Node) node.jjtGetParent();
84+
while (parent != null && !clazz.isInstance(parent.jjtGetValue())) {
8685
parent = (Node) parent.jjtGetParent();
8786
}
88-
89-
return clazz.cast(parent.jjtGetValue());
87+
return parent == null ? null : clazz.cast(parent.jjtGetValue());
9088
}
9189

9290
@Override
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
/*-
2+
* #%L
3+
* JSQLParser library
4+
* %%
5+
* Copyright (C) 2004 - 2026 JSQLParser
6+
* %%
7+
* Dual licensed under GNU LGPL 2.1 or Apache License 2.0
8+
* #L%
9+
*/
10+
package net.sf.jsqlparser.parser;
11+
12+
/**
13+
* A node outside the parse tree that records the tokens an object spans. It serves an object no
14+
* grammar rule opens a tree node for, or one whose text runs past its tree node. In that second
15+
* case it stands in for the tree node, answering its parent, children and id, with the wider range.
16+
*/
17+
final class RangeNode extends Node {
18+
private final Node treeNode;
19+
20+
RangeNode(Token first, Token last, Node treeNode) {
21+
super(CCJSqlParserTreeConstants.JJTEXPRESSION);
22+
this.treeNode = treeNode;
23+
jjtSetFirstToken(first);
24+
jjtSetLastToken(last);
25+
}
26+
27+
@Override
28+
public Node jjtGetParent() {
29+
return treeNode == null ? null : treeNode.jjtGetParent();
30+
}
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+
}
71+
}

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

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,9 @@ 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 positionSpecialTokens()
52+
private int[] matchBegins = new int[24];
53+
private int matchBeginCount = 0;
5154
int bufsize;
5255
int available;
5356
int tokenBegin;
@@ -107,6 +110,38 @@ public final int getAbsoluteTokenBegin() {
107110
return absoluteTokenBegin;
108111
}
109112

113+
/**
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.
118+
*/
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;
125+
}
126+
if (i < 0) {
127+
break;
128+
}
129+
comment.absoluteBegin = matchBegins[i + 2];
130+
comment.absoluteEnd = comment.absoluteBegin + comment.image.length();
131+
i -= 3;
132+
}
133+
matchBeginCount = 0;
134+
}
135+
136+
private void recordMatchBegin() {
137+
if (matchBeginCount + 3 > matchBegins.length) {
138+
matchBegins = java.util.Arrays.copyOf(matchBegins, matchBegins.length * 2);
139+
}
140+
matchBegins[matchBeginCount++] = getBeginLine();
141+
matchBegins[matchBeginCount++] = getBeginColumn();
142+
matchBegins[matchBeginCount++] = absoluteTokenBegin;
143+
}
144+
110145
protected void ExpandBuff(boolean wrapAround) {
111146
final int newSize = bufsize * 2;
112147
char[] newbuffer = new char[newSize];
@@ -200,6 +235,7 @@ public final char BeginToken() throws java.io.IOException {
200235
tokenBegin = bufpos;
201236

202237
absoluteTokenBegin = totalCharsRead;
238+
recordMatchBegin();
203239

204240
if (c == '#' && featureConfiguration != null
205241
&& featureConfiguration.getAsBoolean(Feature.allowHashLineComments)) {
@@ -441,6 +477,7 @@ public final void backup(int amount) {
441477
* Reinitialise.
442478
*/
443479
public void ReInit(Provider dstream, int startline, int startcolumn, int buffersize) {
480+
matchBeginCount = 0;
444481
inputStream = dstream;
445482
line = startline;
446483
column = startcolumn - 1;

0 commit comments

Comments
 (0)