Skip to content

Commit dc215bb

Browse files
author
LionelNirva
committed
Add support for new SQL Server 2012 and Oracle 12c versions of LIMIT
(equivalent to MySql and PostgreSQL LIMIT ... OFFSET ... clauses) for parsing and deparsing.
1 parent c3ec64d commit dc215bb

File tree

4 files changed

+201
-24
lines changed

4 files changed

+201
-24
lines changed

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

Lines changed: 71 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
/**
2525
* A limit clause in the form [LIMIT {[offset,] row_count) | (row_count | ALL)
2626
* OFFSET offset}]
27+
* or in the form [OFFSET offset (ROW | ROWS) [FETCH (FIRST | NEXT) row_count (ROW | ROWS) ONLY]]
28+
* or in the form FETCH (FIRST | NEXT) row_count (ROW | ROWS) ONLY
2729
*/
2830
public class Limit {
2931

@@ -33,6 +35,12 @@ public class Limit {
3335
private boolean offsetJdbcParameter = false;
3436
private boolean limitAll;
3537
private boolean limitNull = false;
38+
private boolean oracleSqlServerVersion = false;
39+
private boolean hasOffset = false;
40+
private boolean isOffsetParamRows = false;
41+
private boolean hasFetch = false;
42+
private boolean isFetchParamRows = false;
43+
private boolean isFetchParamFirst = false;
3644

3745
public long getOffset() {
3846
return offset;
@@ -58,6 +66,29 @@ public boolean isRowCountJdbcParameter() {
5866
return rowCountJdbcParameter;
5967
}
6068

69+
public boolean isOracleSqlServerVersion() {
70+
return oracleSqlServerVersion;
71+
}
72+
73+
public boolean isHasOffset() {
74+
return hasOffset;
75+
}
76+
77+
public boolean isHasFetch() {
78+
return hasFetch;
79+
}
80+
public boolean isOffsetParamRows() {
81+
return isOffsetParamRows;
82+
}
83+
84+
public boolean isFetchParamRows() {
85+
return isFetchParamRows;
86+
}
87+
88+
public boolean isFetchParamFirst() {
89+
return isFetchParamFirst;
90+
}
91+
6192
public void setOffsetJdbcParameter(boolean b) {
6293
offsetJdbcParameter = b;
6394
}
@@ -66,6 +97,30 @@ public void setRowCountJdbcParameter(boolean b) {
6697
rowCountJdbcParameter = b;
6798
}
6899

100+
public void setOracleSqlServerVersion(boolean b) {
101+
oracleSqlServerVersion = b;
102+
}
103+
104+
public void setHasOffset(boolean b) {
105+
hasOffset = b;
106+
}
107+
108+
public void setHasFetch(boolean b) {
109+
hasFetch = b;
110+
}
111+
112+
public void setOffsetParamRows(boolean isOffsetParamRows) {
113+
this.isOffsetParamRows = isOffsetParamRows;
114+
}
115+
116+
public void setFetchParamRows(boolean isFetchParamRows) {
117+
this.isFetchParamRows = isFetchParamRows;
118+
}
119+
120+
public void setFetchParamFirst(boolean isFetchParamFirst) {
121+
this.isFetchParamFirst = isFetchParamFirst;
122+
}
123+
69124
/**
70125
* @return true if the limit is "LIMIT ALL [OFFSET ...])
71126
*/
@@ -87,13 +142,22 @@ public void setLimitAll(boolean b) {
87142
@Override
88143
public String toString() {
89144
String retVal = "";
90-
if (limitNull) {
91-
retVal += " LIMIT NULL";
92-
} else if (rowCount >= 0 || rowCountJdbcParameter) {
93-
retVal += " LIMIT " + (rowCountJdbcParameter ? "?" : rowCount + "");
94-
}
95-
if (offset > 0 || offsetJdbcParameter) {
96-
retVal += " OFFSET " + (offsetJdbcParameter ? "?" : offset + "");
145+
if (oracleSqlServerVersion) {
146+
if (hasOffset) {
147+
retVal = " OFFSET " + offset + " "+(isOffsetParamRows ? "ROWS" : "ROW");
148+
}
149+
if (hasFetch) {
150+
retVal += " FETCH "+(isFetchParamFirst ? "FIRST" : "NEXT")+" " + rowCount + " "+(isFetchParamRows ? "ROWS" : "ROW")+" ONLY";
151+
}
152+
} else {
153+
if (limitNull) {
154+
retVal += " LIMIT NULL";
155+
} else if (rowCount >= 0 || rowCountJdbcParameter) {
156+
retVal += " LIMIT " + (rowCountJdbcParameter ? "?" : rowCount + "");
157+
}
158+
if (offset > 0 || offsetJdbcParameter) {
159+
retVal += " OFFSET " + (offsetJdbcParameter ? "?" : offset + "");
160+
}
97161
}
98162
return retVal;
99163
}

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

Lines changed: 46 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -248,20 +248,53 @@ public void deparseOrderBy(boolean oracleSiblings, List<OrderByElement> orderByE
248248

249249
public void deparseLimit(Limit limit) {
250250
// LIMIT n OFFSET skip
251-
if (limit.isRowCountJdbcParameter()) {
252-
buffer.append(" LIMIT ");
253-
buffer.append("?");
254-
} else if (limit.getRowCount() >= 0) {
255-
buffer.append(" LIMIT ");
256-
buffer.append(limit.getRowCount());
257-
} else if (limit.isLimitNull()) {
258-
buffer.append(" LIMIT NULL");
259-
}
251+
// or
252+
// OFFSET offset (ROW | ROWS) [FETCH (FIRST | NEXT) row_count (ROW | ROWS) ONLY]
253+
// or
254+
// FETCH (FIRST | NEXT) row_count (ROW | ROWS) ONLY
255+
if (limit.isOracleSqlServerVersion()) {
256+
if (limit.isHasOffset()) {
257+
buffer.append(" OFFSET ");
258+
buffer.append(limit.getOffset());
259+
buffer.append(" ");
260+
if (limit.isOffsetParamRows()) {
261+
buffer.append("ROWS");
262+
} else {
263+
buffer.append("ROW");
264+
}
265+
}
266+
if (limit.isHasFetch()) {
267+
buffer.append(" FETCH ");
268+
if (limit.isFetchParamFirst()) {
269+
buffer.append("FIRST ");
270+
} else {
271+
buffer.append("NEXT ");
272+
}
273+
buffer.append(limit.getRowCount());
274+
buffer.append(" ");
275+
if (limit.isFetchParamRows()) {
276+
buffer.append("ROWS");
277+
} else {
278+
buffer.append("ROW");
279+
}
280+
buffer.append(" ONLY");
281+
}
282+
} else {
283+
if (limit.isRowCountJdbcParameter()) {
284+
buffer.append(" LIMIT ");
285+
buffer.append("?");
286+
} else if (limit.getRowCount() >= 0) {
287+
buffer.append(" LIMIT ");
288+
buffer.append(limit.getRowCount());
289+
} else if (limit.isLimitNull()) {
290+
buffer.append(" LIMIT NULL");
291+
}
260292

261-
if (limit.isOffsetJdbcParameter()) {
262-
buffer.append(" OFFSET ?");
263-
} else if (limit.getOffset() != 0) {
264-
buffer.append(" OFFSET ").append(limit.getOffset());
293+
if (limit.isOffsetJdbcParameter()) {
294+
buffer.append(" OFFSET ?");
295+
} else if (limit.getOffset() != 0) {
296+
buffer.append(" OFFSET ").append(limit.getOffset());
297+
}
265298
}
266299

267300
}

src/main/javacc/net/sf/jsqlparser/parser/JSqlParserCC.jj

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,9 @@ TOKEN: /* SQL Keywords. prefixed with K_ to avoid name clashes */
201201
| <K_UNLOGGED: "UNLOGGED">
202202
| <K_EXEC: "EXEC">
203203
| <K_EXECUTE: "EXECUTE">
204+
| <K_FETCH:"FETCH">
205+
| <K_NEXT:"NEXT">
206+
| <K_ONLY:"ONLY">
204207
}
205208

206209
TOKEN : /* Numeric Constants */
@@ -1162,10 +1165,6 @@ Limit Limit():
11621165
(
11631166
token=<S_LONG> { limit.setRowCount(Long.parseLong(token.image)); } | "?" { limit.setRowCountJdbcParameter(true);}
11641167
)
1165-
|
1166-
// postgresql-> OFFSET offset
1167-
<K_OFFSET>
1168-
(token=<S_LONG> { limit.setOffset(Long.parseLong(token.image)); } | "?" { limit.setOffsetJdbcParameter(true);} )
11691168
|
11701169
// mysql-postgresql-> LIMIT (row_count | ALL | NULL) [OFFSET offset]
11711170
<K_LIMIT>
@@ -1181,6 +1180,25 @@ Limit Limit():
11811180

11821181
[LOOKAHEAD(2) <K_OFFSET>
11831182
(token=<S_LONG> { limit.setOffset(Long.parseLong(token.image)); } | "?" { limit.setOffsetJdbcParameter(true);} ) ]
1183+
|
1184+
// postgresql-> OFFSET offset
1185+
// sqlserver-oracle-> OFFSET offset (ROW | ROWS) [FETCH (FIRST | NEXT) row_count (ROW | ROWS) ONLY]
1186+
<K_OFFSET>
1187+
(token=<S_LONG> { limit.setOffset(Long.parseLong(token.image)); }
1188+
[(<K_ROWS> { limit.setOffsetParamRows(true); } | <K_ROW>) {limit.setOracleSqlServerVersion(true); limit.setHasOffset(true); }]
1189+
| "?" { limit.setOffsetJdbcParameter(true);} )
1190+
[LOOKAHEAD(5) <K_FETCH>
1191+
(<K_FIRST> { limit.setFetchParamFirst(true); } | <K_NEXT>)
1192+
(token=<S_LONG> { limit.setOracleSqlServerVersion(true); limit.setHasFetch(true); limit.setRowCount(Long.parseLong(token.image)); })
1193+
(<K_ROWS> { limit.setFetchParamRows(true); } | <K_ROW>)
1194+
<K_ONLY> ]
1195+
|
1196+
// oracle-> FETCH (FIRST | NEXT) row_count (ROW | ROWS) ONLY
1197+
<K_FETCH>
1198+
(<K_FIRST> { limit.setFetchParamFirst(true); } | <K_NEXT>)
1199+
(token=<S_LONG> { limit.setOracleSqlServerVersion(true); limit.setHasFetch(true); limit.setRowCount(Long.parseLong(token.image)); })
1200+
(<K_ROWS> { limit.setFetchParamRows(true); } | <K_ROW>)
1201+
<K_ONLY>
11841202

11851203
)
11861204
{

src/test/java/net/sf/jsqlparser/test/select/SelectTest.java

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,68 @@ public void testLimit2() throws JSQLParserException {
312312
assertSqlCanBeParsedAndDeparsed(statement);
313313
}
314314

315+
public void testLimitSqlServer1() throws JSQLParserException {
316+
String statement = "SELECT * FROM mytable WHERE mytable.col = 9 ORDER BY mytable.id OFFSET 3 ROWS FETCH NEXT 5 ROWS ONLY";
317+
318+
Select select = (Select) parserManager.parse(new StringReader(statement));
319+
320+
assertTrue(((PlainSelect) select.getSelectBody()).getLimit().isOracleSqlServerVersion());
321+
assertTrue(((PlainSelect) select.getSelectBody()).getLimit().isHasOffset());
322+
assertTrue(((PlainSelect) select.getSelectBody()).getLimit().isHasFetch());
323+
assertTrue(((PlainSelect) select.getSelectBody()).getLimit().isOffsetParamRows());
324+
assertTrue(((PlainSelect) select.getSelectBody()).getLimit().isFetchParamRows());
325+
assertFalse(((PlainSelect) select.getSelectBody()).getLimit().isFetchParamFirst());
326+
assertEquals(3, ((PlainSelect) select.getSelectBody()).getLimit().getOffset());
327+
assertEquals(5, ((PlainSelect) select.getSelectBody()).getLimit().getRowCount());
328+
assertStatementCanBeDeparsedAs(select, statement);
329+
}
330+
331+
public void testLimitSqlServer2() throws JSQLParserException {
332+
// Alternative with the other keywords
333+
String statement = "SELECT * FROM mytable WHERE mytable.col = 9 ORDER BY mytable.id OFFSET 3 ROW FETCH FIRST 5 ROW ONLY";
334+
335+
Select select = (Select) parserManager.parse(new StringReader(statement));
336+
337+
assertTrue(((PlainSelect) select.getSelectBody()).getLimit().isOracleSqlServerVersion());
338+
assertTrue(((PlainSelect) select.getSelectBody()).getLimit().isHasOffset());
339+
assertTrue(((PlainSelect) select.getSelectBody()).getLimit().isHasFetch());
340+
assertFalse(((PlainSelect) select.getSelectBody()).getLimit().isOffsetParamRows());
341+
assertFalse(((PlainSelect) select.getSelectBody()).getLimit().isFetchParamRows());
342+
assertTrue(((PlainSelect) select.getSelectBody()).getLimit().isFetchParamFirst());
343+
assertEquals(3, ((PlainSelect) select.getSelectBody()).getLimit().getOffset());
344+
assertEquals(5, ((PlainSelect) select.getSelectBody()).getLimit().getRowCount());
345+
assertStatementCanBeDeparsedAs(select, statement);
346+
}
347+
348+
public void testLimitSqlServer3() throws JSQLParserException {
349+
// Query with no Fetch
350+
String statement = "SELECT * FROM mytable WHERE mytable.col = 9 ORDER BY mytable.id OFFSET 3 ROWS";
351+
352+
Select select = (Select) parserManager.parse(new StringReader(statement));
353+
354+
assertTrue(((PlainSelect) select.getSelectBody()).getLimit().isOracleSqlServerVersion());
355+
assertTrue(((PlainSelect) select.getSelectBody()).getLimit().isHasOffset());
356+
assertFalse(((PlainSelect) select.getSelectBody()).getLimit().isHasFetch());
357+
assertTrue(((PlainSelect) select.getSelectBody()).getLimit().isOffsetParamRows());
358+
assertEquals(3, ((PlainSelect) select.getSelectBody()).getLimit().getOffset());
359+
assertStatementCanBeDeparsedAs(select, statement);
360+
}
361+
362+
public void testLimitSqlServer4() throws JSQLParserException {
363+
// For Oracle syntax, query with no offset
364+
String statement = "SELECT * FROM mytable WHERE mytable.col = 9 ORDER BY mytable.id FETCH NEXT 5 ROWS ONLY";
365+
366+
Select select = (Select) parserManager.parse(new StringReader(statement));
367+
368+
assertTrue(((PlainSelect) select.getSelectBody()).getLimit().isOracleSqlServerVersion());
369+
assertFalse(((PlainSelect) select.getSelectBody()).getLimit().isHasOffset());
370+
assertTrue(((PlainSelect) select.getSelectBody()).getLimit().isHasFetch());
371+
assertTrue(((PlainSelect) select.getSelectBody()).getLimit().isFetchParamRows());
372+
assertFalse(((PlainSelect) select.getSelectBody()).getLimit().isFetchParamFirst());
373+
assertEquals(5, ((PlainSelect) select.getSelectBody()).getLimit().getRowCount());
374+
assertStatementCanBeDeparsedAs(select, statement);
375+
}
376+
315377
public void testTop() throws JSQLParserException {
316378
String statement = "SELECT TOP 3 * FROM mytable WHERE mytable.col = 9";
317379

0 commit comments

Comments
 (0)