Skip to content

Commit 4d8a512

Browse files
feat: SQL:2016 TABLESAMPLE clause
- supports SQL:2016 - supports Oracle dialect - fixes #1826 - fixes #1593 Signed-off-by: Andreas Reichel <[email protected]>
1 parent 52df32d commit 4d8a512

File tree

19 files changed

+346
-83
lines changed

19 files changed

+346
-83
lines changed

src/main/java/net/sf/jsqlparser/parser/CCJSqlParserUtil.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -461,7 +461,7 @@ public static int getUnbalancedPosition(String text) {
461461
return i; // Return position of unbalanced closing bracket
462462
}
463463
char top = stack.pop();
464-
if ((c == ')' && top != '(') || (c == ']' && top != '[') || (c == '}' && top != '{')) {
464+
if (c == ')' && top != '(' || c == ']' && top != '[' || c == '}' && top != '{') {
465465
return i; // Return position of unbalanced closing bracket
466466
}
467467
}

src/main/java/net/sf/jsqlparser/parser/ParserKeywordsUtils.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,8 @@ public class ParserKeywordsUtils {
7777
{"PIVOT", RESTRICTED_JSQLPARSER}, {"PROCEDURE", RESTRICTED_ALIAS},
7878
{"PUBLIC", RESTRICTED_ALIAS}, {"RECURSIVE", RESTRICTED_SQL2016},
7979
{"REGEXP", RESTRICTED_SQL2016}, {"RETURNING", RESTRICTED_JSQLPARSER},
80-
{"RIGHT", RESTRICTED_SQL2016}, {"SEL", RESTRICTED_ALIAS}, {"SELECT", RESTRICTED_ALIAS},
80+
{"RIGHT", RESTRICTED_SQL2016}, {"SAMPLE", RESTRICTED_ALIAS}, {"SEL", RESTRICTED_ALIAS},
81+
{"SELECT", RESTRICTED_ALIAS},
8182
{"SEMI", RESTRICTED_JSQLPARSER}, {"SET", RESTRICTED_JSQLPARSER},
8283
{"SOME", RESTRICTED_JSQLPARSER}, {"START", RESTRICTED_JSQLPARSER},
8384
{"TABLES", RESTRICTED_ALIAS}, {"TOP", RESTRICTED_SQL2016},
@@ -86,7 +87,8 @@ public class ParserKeywordsUtils {
8687
{"UNPIVOT", RESTRICTED_JSQLPARSER}, {"USE", RESTRICTED_JSQLPARSER},
8788
{"USING", RESTRICTED_SQL2016}, {"SQL_CACHE", RESTRICTED_JSQLPARSER},
8889
{"SQL_CALC_FOUND_ROWS", RESTRICTED_JSQLPARSER}, {"SQL_NO_CACHE", RESTRICTED_JSQLPARSER},
89-
{"STRAIGHT_JOIN", RESTRICTED_JSQLPARSER}, {"VALUE", RESTRICTED_JSQLPARSER},
90+
{"STRAIGHT_JOIN", RESTRICTED_JSQLPARSER}, {"TABLESAMPLE", RESTRICTED_ALIAS},
91+
{"VALUE", RESTRICTED_JSQLPARSER},
9092
{"VALUES", RESTRICTED_SQL2016}, {"VARYING", RESTRICTED_JSQLPARSER},
9193
{"WHEN", RESTRICTED_SQL2016}, {"WHERE", RESTRICTED_SQL2016},
9294
{"WINDOW", RESTRICTED_SQL2016}, {"WITH", RESTRICTED_SQL2016},

src/main/java/net/sf/jsqlparser/parser/feature/Feature.java

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -292,7 +292,7 @@ public enum Feature {
292292
/**
293293
* "RETURNING expr(, expr)*"
294294
*
295-
* @see SelectExpressionItem
295+
* @see net.sf.jsqlparser.expression.operators.relational.ExpressionList
296296
*/
297297
insertReturningExpressionList,
298298

@@ -326,7 +326,7 @@ public enum Feature {
326326
/**
327327
* "RETURNING expr(, expr)*"
328328
*
329-
* @see SelectExpressionItem
329+
* @see net.sf.jsqlparser.statement.select.SelectItem
330330
*/
331331
updateReturning,
332332
/**
@@ -354,7 +354,7 @@ public enum Feature {
354354
/**
355355
* "RETURNING expr(, expr)*"
356356
*
357-
* @see SelectExpressionItem
357+
* @see net.sf.jsqlparser.statement.select.SelectItem
358358
*/
359359
deleteReturningExpressionList,
360360

@@ -436,7 +436,6 @@ public enum Feature {
436436
/**
437437
* SQL "REPLACE" statement is allowed
438438
*
439-
* @see Replace
440439
*/
441440
@Deprecated
442441
replace,
@@ -644,7 +643,7 @@ public enum Feature {
644643

645644
lateralSubSelect,
646645
/**
647-
* @see ValuesList
646+
* @see net.sf.jsqlparser.statement.select.Values
648647
*/
649648
valuesList,
650649
/**

src/main/java/net/sf/jsqlparser/schema/Table.java

Lines changed: 48 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import net.sf.jsqlparser.statement.select.FromItemVisitor;
2121
import net.sf.jsqlparser.statement.select.IntoTableVisitor;
2222
import net.sf.jsqlparser.statement.select.Pivot;
23+
import net.sf.jsqlparser.statement.select.SampleClause;
2324
import net.sf.jsqlparser.statement.select.UnPivot;
2425

2526
/**
@@ -42,6 +43,8 @@ public class Table extends ASTNodeAccessImpl implements FromItem, MultiPartName
4243

4344
private Alias alias;
4445

46+
private SampleClause sampleClause;
47+
4548
private Pivot pivot;
4649

4750
private UnPivot unpivot;
@@ -50,8 +53,7 @@ public class Table extends ASTNodeAccessImpl implements FromItem, MultiPartName
5053

5154
private SQLServerHints sqlServerHints;
5255

53-
public Table() {
54-
}
56+
public Table() {}
5557

5658
public Table(String name) {
5759
setName(name);
@@ -104,21 +106,21 @@ public void setSchemaName(String schemaName) {
104106

105107
public String getName() {
106108
String name = getIndex(NAME_IDX);
107-
if (name!=null && name.contains("@")) {
109+
if (name != null && name.contains("@")) {
108110
int pos = name.lastIndexOf('@');
109-
if (pos>0) {
110-
name = name.substring(0, pos );
111+
if (pos > 0) {
112+
name = name.substring(0, pos);
111113
}
112114
}
113115
return name;
114116
}
115117

116118
public String getDBLinkName() {
117119
String name = getIndex(NAME_IDX);
118-
if (name!=null && name.contains("@")) {
120+
if (name != null && name.contains("@")) {
119121
int pos = name.lastIndexOf('@');
120-
if (pos>0 && name.length()>1) {
121-
name = name.substring(pos+1);
122+
if (pos > 0 && name.length() > 1) {
123+
name = name.substring(pos + 1);
122124
}
123125
}
124126
return name;
@@ -232,12 +234,46 @@ public void setSqlServerHints(SQLServerHints sqlServerHints) {
232234
this.sqlServerHints = sqlServerHints;
233235
}
234236

237+
public SampleClause getSampleClause() {
238+
return sampleClause;
239+
}
240+
241+
public Table setSampleClause(SampleClause sampleClause) {
242+
this.sampleClause = sampleClause;
243+
return this;
244+
}
245+
246+
public StringBuilder appendTo(StringBuilder builder) {
247+
builder.append(getFullyQualifiedName());
248+
if (alias != null) {
249+
builder.append(alias);
250+
}
251+
252+
if (sampleClause != null) {
253+
sampleClause.appendTo(builder);
254+
}
255+
256+
if (pivot != null) {
257+
builder.append(" ").append(pivot);
258+
}
259+
260+
if (unpivot != null) {
261+
builder.append(" ").append(unpivot);
262+
}
263+
264+
if (mysqlHints != null) {
265+
builder.append(mysqlHints);
266+
}
267+
268+
if (sqlServerHints != null) {
269+
builder.append(sqlServerHints);
270+
}
271+
return builder;
272+
}
273+
235274
@Override
236275
public String toString() {
237-
return getFullyQualifiedName() + ((alias != null) ? alias.toString() : "")
238-
+ ((pivot != null) ? " " + pivot : "") + ((unpivot != null) ? " " + unpivot : "")
239-
+ ((mysqlHints != null) ? mysqlHints.toString() : "")
240-
+ ((sqlServerHints != null) ? sqlServerHints.toString() : "");
276+
return appendTo(new StringBuilder()).toString();
241277
}
242278

243279
@Override

src/main/java/net/sf/jsqlparser/statement/ReturningClause.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@
1515
import java.util.List;
1616

1717
/**
18-
* RETURNING clause according to
19-
* {@see https://docs.oracle.com/en/database/oracle/oracle-database/19/sqlrf/DELETE.html#GUID-156845A5-B626-412B-9F95-8869B988ABD7
20-
* } Part of UPDATE, INSERT, DELETE statements
18+
* RETURNING clause according to <a href=
19+
* "https://docs.oracle.com/en/database/oracle/oracle-database/19/sqlrf/DELETE.html#GUID-156845A5-B626-412B-9F95-8869B988ABD7"
20+
* /> Part of UPDATE, INSERT, DELETE statements
2121
*/
2222

2323
public class ReturningClause extends ArrayList<SelectItem<?>> {

src/main/java/net/sf/jsqlparser/statement/select/FromItem.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,4 +43,5 @@ default FromItem withUnPivot(UnPivot unpivot) {
4343

4444
void setUnPivot(UnPivot unpivot);
4545

46+
4647
}

src/main/java/net/sf/jsqlparser/statement/select/Join.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ public Join withInner(boolean b) {
8686

8787
/**
8888
*
89-
* @return Sets the INNER keyword and switches off any contradicting qualifiers automatically.
89+
* Sets the INNER keyword and switches off any contradicting qualifiers automatically.
9090
*/
9191
public void setInner(boolean b) {
9292
if (b) {
@@ -128,7 +128,7 @@ public Join withOuter(boolean b) {
128128

129129
/**
130130
*
131-
* @return Sets the OUTER keyword and switches off any contradicting qualifiers automatically.
131+
* Sets the OUTER keyword and switches off any contradicting qualifiers automatically.
132132
*/
133133
public void setOuter(boolean b) {
134134
if (b) {
@@ -184,7 +184,7 @@ public Join withLeft(boolean b) {
184184

185185
/**
186186
*
187-
* @return Sets the LEFT keyword and switches off any contradicting qualifiers automatically.
187+
* Sets the LEFT keyword and switches off any contradicting qualifiers automatically.
188188
*/
189189
public void setLeft(boolean b) {
190190
if (b) {
@@ -210,7 +210,7 @@ public Join withRight(boolean b) {
210210

211211
/**
212212
*
213-
* @return Sets the RIGHT keyword and switches off any contradicting qualifiers automatically.
213+
* Sets the RIGHT keyword and switches off any contradicting qualifiers automatically.
214214
*/
215215
public void setRight(boolean b) {
216216
if (b) {
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
package net.sf.jsqlparser.statement.select;
2+
3+
public class SampleClause {
4+
public enum SampleKeyword {
5+
SAMPLE, TABLESAMPLE;
6+
7+
public static SampleKeyword from(String sampleKeyword) {
8+
return Enum.valueOf(SampleKeyword.class, sampleKeyword.toUpperCase());
9+
}
10+
}
11+
12+
public enum SampleMethod {
13+
BERNOULLI, SYSTEM, BLOCK;
14+
15+
public static SampleMethod from(String sampleMethod) {
16+
return Enum.valueOf(SampleMethod.class, sampleMethod.toUpperCase());
17+
}
18+
}
19+
20+
private SampleKeyword keyword;
21+
private SampleMethod method;
22+
private Number percentageArgument;
23+
private Number repeatArgument;
24+
25+
// Oracle Specific
26+
private Number seedArgument;
27+
28+
public SampleClause(String keyword, String method, Number percentageArgument,
29+
Number repeatArgument, Number seedArgument) {
30+
this.keyword = SampleKeyword.from(keyword);
31+
this.method = method == null || method.length() == 0 ? null : SampleMethod.from(method);
32+
this.percentageArgument = percentageArgument;
33+
this.repeatArgument = repeatArgument;
34+
this.seedArgument = seedArgument;
35+
}
36+
37+
public SampleClause() {
38+
this(SampleKeyword.TABLESAMPLE.toString(), null, null, null, null);
39+
}
40+
41+
public SampleClause(String keyword) {
42+
this(keyword, null, null, null, null);
43+
}
44+
45+
public SampleKeyword getKeyword() {
46+
return keyword;
47+
}
48+
49+
public SampleClause setKeyword(SampleKeyword keyword) {
50+
this.keyword = keyword;
51+
return this;
52+
}
53+
54+
public Number getPercentageArgument() {
55+
return percentageArgument;
56+
}
57+
58+
public SampleClause setPercentageArgument(Number percentageArgument) {
59+
this.percentageArgument = percentageArgument;
60+
return this;
61+
}
62+
63+
public Number getRepeatArgument() {
64+
return repeatArgument;
65+
}
66+
67+
public SampleClause setRepeatArgument(Number repeatArgument) {
68+
this.repeatArgument = repeatArgument;
69+
return this;
70+
}
71+
72+
public Number getSeedArgument() {
73+
return seedArgument;
74+
}
75+
76+
public SampleClause setSeedArgument(Number seedArgument) {
77+
this.seedArgument = seedArgument;
78+
return this;
79+
}
80+
81+
public SampleMethod getMethod() {
82+
return method;
83+
}
84+
85+
public SampleClause setMethod(SampleMethod method) {
86+
this.method = method;
87+
return this;
88+
}
89+
90+
public SampleClause setMethod(String method) {
91+
this.method = method == null || method.length() == 0 ? null : SampleMethod.from(method);
92+
return this;
93+
}
94+
95+
public StringBuilder appendTo(StringBuilder builder) {
96+
97+
builder.append(" ").append(keyword);
98+
if (method != null) {
99+
builder.append(" ").append(method);
100+
}
101+
102+
if (percentageArgument != null) {
103+
builder.append(" (").append(percentageArgument).append(")");
104+
}
105+
106+
if (repeatArgument != null) {
107+
builder.append(" REPEATABLE (").append(repeatArgument).append(")");
108+
}
109+
110+
if (seedArgument != null) {
111+
builder.append(" SEED (").append(seedArgument).append(")");
112+
}
113+
114+
return builder;
115+
}
116+
117+
public String toString() {
118+
return appendTo(new StringBuilder()).toString();
119+
}
120+
}

src/main/java/net/sf/jsqlparser/util/deparser/SelectDeParser.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,13 @@ public void visit(PlainSelect plainSelect) {
211211
if (plainSelect.getFromItem() != null) {
212212
buffer.append(" FROM ");
213213
plainSelect.getFromItem().accept(this);
214+
215+
if (plainSelect.getFromItem() instanceof Table) {
216+
Table table = (Table) plainSelect.getFromItem();
217+
if (table.getSampleClause() != null) {
218+
table.getSampleClause().appendTo(buffer);
219+
}
220+
}
214221
}
215222

216223
if (plainSelect.getLateralViews() != null) {

0 commit comments

Comments
 (0)