Skip to content

Commit b883661

Browse files
committed
Add support for sqlserver TOP syntax as custom SQL. fixes issue #18
git-svn-id: https://svn.code.sf.net/p/openhms/code/sqlbuilder/trunk@499 f9ba57bc-9804-47c6-9de9-2f2ff8aac994
1 parent b806cdf commit b883661

File tree

6 files changed

+116
-6
lines changed

6 files changed

+116
-6
lines changed

src/changes/changes.xml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,14 @@
44
<author email="jahlborn@users.sf.net">James Ahlborn</author>
55
</properties>
66
<body>
7+
<release version="2.1.3" date="TBD">
8+
<action dev="jahlborn" type="update" issue="18">
9+
Add support for sqlserver TOP syntax as custom SQL.
10+
</action>
11+
<action dev="jahlborn" type="update">
12+
Add support for SQL 2008 OFFSET and FETCH NEXT clauses in SELECT queries.
13+
</action>
14+
</release>
715
<release version="2.1.2" date="2016-01-30">
816
<action dev="jahlborn" type="update">
917
New site style!

src/main/java/com/healthmarketscience/sqlbuilder/CustomizableSqlObject.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,8 @@ protected void maybeAppendTo(AppendableExt app, HookAnchor anchor,
8181
}
8282

8383
/**
84-
* Appends any {@link HookType#BEFORE} customizations for the given anchor.
84+
* Appends any {@link HookType#BEFORE} or {@link HookType#AFTER}
85+
* customizations for the given anchor.
8586
*/
8687
protected AppendableExt customAppendTo(AppendableExt app, HookAnchor anchor)
8788
throws IOException
@@ -90,6 +91,7 @@ protected AppendableExt customAppendTo(AppendableExt app, HookAnchor anchor)
9091
if((_customizations != null) &&
9192
((custs = findCustomizations(anchor)) != null)) {
9293
appendCustomizations(app, custs, HookType.BEFORE);
94+
appendCustomizations(app, custs, HookType.AFTER);
9395
}
9496
return app;
9597
}
@@ -113,6 +115,7 @@ protected AppendableExt customAppendTo(AppendableExt app, HookAnchor anchor,
113115
app.append(str);
114116
}
115117
appendCustomizations(app, custs, HookType.SUFFIX);
118+
appendCustomizations(app, custs, HookType.AFTER);
116119
return app;
117120
}
118121

src/main/java/com/healthmarketscience/sqlbuilder/custom/HookType.java

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,10 @@
2626
* REPLACEMENT} type is used, the anchor text itself will <i>not</i> be
2727
* inserted.
2828
* <p/>
29-
* The {@code BEFORE} type, while similar in location to the {@code PREFIX}
30-
* type, is included regardless of whether or not the anchor clause itself is
31-
* included (it is more related to the surrounding clause than the anchor
32-
* clause itself).
29+
* The {@code BEFORE} and {@code AFTER} types, while similar in location to
30+
* the {@code PREFIX} and {@code SUFFIX} types (respectively), are included
31+
* regardless of whether or not the anchor clause itself is included (they are
32+
* more related to the surrounding clause than the anchor clause itself).
3333
* <p>
3434
* Note that customizable queries support multiple instances of each type for
3535
* a given anchor. Multiple customizations of the same type will be inserted
@@ -61,5 +61,12 @@ public enum HookType
6161
* Customization which is inserted after the anchor clause, but only if the
6262
* anchor clause itself is included in the query.
6363
*/
64-
SUFFIX;
64+
SUFFIX,
65+
/**
66+
* Customization which is inserted after the anchor clause. Unlike the
67+
* {@code SUFFIX} type, this type of customization will <i>always</i> be
68+
* inserted regardless of whether or not the related anchor clause itself is
69+
* included in the query.
70+
*/
71+
AFTER;
6572
}

src/main/java/com/healthmarketscience/sqlbuilder/custom/package-info.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@
6868
* <li>Oracle {@link com.healthmarketscience.sqlbuilder.custom.oracle}</li>
6969
* <li>MySQL {@link com.healthmarketscience.sqlbuilder.custom.mysql}</li>
7070
* <li>PostreSQL {@link com.healthmarketscience.sqlbuilder.custom.postgresql}</li>
71+
* <li>SQLServer {@link com.healthmarketscience.sqlbuilder.custom.sqlserver}</li>
7172
* </ul>
7273
* <p/>
7374
* <h3>Customizable Queries</h3>
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
/*
2+
Copyright (c) 2016 James Ahlborn
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package com.healthmarketscience.sqlbuilder.custom.sqlserver;
18+
19+
import java.io.IOException;
20+
21+
import com.healthmarketscience.common.util.AppendableExt;
22+
import com.healthmarketscience.sqlbuilder.Converter;
23+
import com.healthmarketscience.sqlbuilder.SelectQuery;
24+
import com.healthmarketscience.sqlbuilder.SqlObject;
25+
import com.healthmarketscience.sqlbuilder.ValidationContext;
26+
import com.healthmarketscience.sqlbuilder.custom.CustomSyntax;
27+
import com.healthmarketscience.sqlbuilder.custom.HookType;
28+
29+
/**
30+
* Appends a SQLServer TOP clause like {@code " TOP <count> [PERCENT]"} for
31+
* use in {@link SelectQuery}s.
32+
*
33+
* @see SelectQuery#addCustomization(CustomSyntax)
34+
*
35+
* @author James Ahlborn
36+
*/
37+
public class MssTopClause extends CustomSyntax
38+
{
39+
private SqlObject _count;
40+
private boolean _isPercent;
41+
42+
public MssTopClause(Object count) {
43+
this(count, false);
44+
}
45+
46+
public MssTopClause(Object count, boolean isPercent) {
47+
_count = Converter.toValueSqlObject(count);
48+
_isPercent = isPercent;
49+
}
50+
51+
public MssTopClause setIsPercent(boolean isPercent) {
52+
_isPercent = isPercent;
53+
return this;
54+
}
55+
56+
@Override
57+
public void apply(SelectQuery query) {
58+
query.addCustomization(SelectQuery.Hook.DISTINCT, HookType.AFTER, this);
59+
}
60+
61+
@Override
62+
public void appendTo(AppendableExt app) throws IOException {
63+
app.append("TOP ").append(_count).append(" ");
64+
if(_isPercent) {
65+
app.append("PERCENT ");
66+
}
67+
}
68+
69+
@Override
70+
protected void collectSchemaObjects(ValidationContext vContext) {
71+
collectSchemaObjects(_count, vContext);
72+
}
73+
}

src/test/java/com/healthmarketscience/sqlbuilder/custom/CustomSyntaxTest.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import com.healthmarketscience.sqlbuilder.custom.postgresql.PgLimitClause;
3030
import com.healthmarketscience.sqlbuilder.custom.postgresql.PgOffsetClause;
3131
import com.healthmarketscience.sqlbuilder.custom.postgresql.PgObjects;
32+
import com.healthmarketscience.sqlbuilder.custom.sqlserver.MssTopClause;
3233
import com.healthmarketscience.sqlbuilder.dbspec.basic.DbIndex;
3334

3435
/**
@@ -205,4 +206,21 @@ public void testOracleLimitClause()
205206
"SELECT t0.col1 FROM Schema1.Table1 t0 WHERE (ROWNUM < 100)");
206207
}
207208

209+
public void testSQLServerTopClause()
210+
{
211+
String selectQuery1 = new SelectQuery()
212+
.addColumns(_table1_col1)
213+
.addCustomization(new MssTopClause(10))
214+
.validate().toString();
215+
checkResult(selectQuery1,
216+
"SELECT TOP 10 t0.col1 FROM Schema1.Table1 t0");
217+
218+
String selectQuery2 = new SelectQuery()
219+
.addColumns(_table1_col1)
220+
.setIsDistinct(true)
221+
.addCustomization(new MssTopClause(30, true))
222+
.validate().toString();
223+
checkResult(selectQuery2,
224+
"SELECT DISTINCT TOP 30 PERCENT t0.col1 FROM Schema1.Table1 t0");
225+
}
208226
}

0 commit comments

Comments
 (0)