Skip to content

Commit df0a0fd

Browse files
committed
Add support for window functions (ideas from Andrey Karepin). fixes patch #3
git-svn-id: https://svn.code.sf.net/p/openhms/code/sqlbuilder/trunk@548 f9ba57bc-9804-47c6-9de9-2f2ff8aac994
1 parent a4d2be8 commit df0a0fd

File tree

7 files changed

+448
-9
lines changed

7 files changed

+448
-9
lines changed

src/changes/changes.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@
88
<action dev="jahlborn" type="add" system="SourceForge2Patches" issue="4">
99
Add the EXTRACT expression (thanks to Andrey Karepin).
1010
</action>
11+
<action dev="jahlborn" type="add" system="SourceForge2Patches" issue="3">
12+
Add support for window functions (ideas from Andrey Karepin).
13+
</action>
1114
</release>
1215
<release version="2.1.6" date="2016-12-18">
1316
<action dev="jahlborn" type="update" system="SourceForge2Features" issue="19">

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

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ public class FunctionCall extends Expression
3333
private boolean _isDistinct;
3434
private SqlObject _functionName;
3535
private SqlObjectList<SqlObject> _params = SqlObjectList.create();
36+
private SqlObject _window;
3637

3738
public FunctionCall(Function function) {
3839
this((Object)function);
@@ -82,11 +83,31 @@ public FunctionCall addColumnParams(Column... columns) {
8283
public FunctionCall addNumericValueParam(Object obj) {
8384
return addCustomParams(obj);
8485
}
86+
87+
/**
88+
* Sets the window clause for this function call, like
89+
* <code>"OVER (&lt;windowClause&gt;)"</code>.
90+
* @see WindowDefinitionClause
91+
*/
92+
public FunctionCall setWindow(Object window) {
93+
_window = Converter.toCustomColumnSqlObject(window);
94+
return this;
95+
}
96+
97+
/**
98+
* Sets the window clause for this function call to a reference to the named
99+
* window definition, like <code>"OVER &lt;windowClauseName&gt;"</code>.
100+
*/
101+
public FunctionCall setWindowByName(String windowName) {
102+
_window = new CustomSql(windowName);
103+
return this;
104+
}
85105

86106
@Override
87107
protected void collectSchemaObjects(ValidationContext vContext) {
88108
_functionName.collectSchemaObjects(vContext);
89109
_params.collectSchemaObjects(vContext);
110+
collectSchemaObjects(_window, vContext);
90111
}
91112

92113
@Override
@@ -96,6 +117,9 @@ public void appendTo(AppendableExt app) throws IOException {
96117
app.append("DISTINCT ");
97118
}
98119
app.append(_params).append(")");
120+
if(_window != null) {
121+
app.append(" OVER ").append(_window);
122+
}
99123
}
100124

101125
/**
@@ -147,4 +171,34 @@ public static FunctionCall countAll() {
147171
.addCustomParams(ALL_SYMBOL);
148172
}
149173

174+
/**
175+
* Convenience method for generating a FunctionCall using the ROW_NUMBER
176+
* aggregate function.
177+
*
178+
* @see "SQL 2003"
179+
*/
180+
public static FunctionCall rowNumber() {
181+
return (new FunctionCall(new CustomSql("ROW_NUMBER")));
182+
}
183+
184+
/**
185+
* Convenience method for generating a FunctionCall using the RANK
186+
* aggregate function.
187+
*
188+
* @see "SQL 2003"
189+
*/
190+
public static FunctionCall rank() {
191+
return (new FunctionCall(new CustomSql("RANK")));
192+
}
193+
194+
/**
195+
* Convenience method for generating a FunctionCall using the DENSE_RANK
196+
* aggregate function.
197+
*
198+
* @see "SQL 2003"
199+
*/
200+
public static FunctionCall denseRank() {
201+
return (new FunctionCall(new CustomSql("DENSE_RANK")));
202+
}
203+
150204
}

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

Lines changed: 51 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,8 @@ public enum Hook implements HookAnchor
9999
/** Anchor for the " HAVING " sub-clause (only possible if there is a
100100
GROUP BY clause) */
101101
HAVING,
102+
/** Anchor for the " WINDOW " clause */
103+
WINDOW,
102104
/** Anchor for the " ORDER BY " clause */
103105
ORDER_BY,
104106
/** Anchor for the " FOR UPDATE " clause */
@@ -117,6 +119,7 @@ public enum Hook implements HookAnchor
117119
private SqlObjectList<SqlObject> _grouping = SqlObjectList.create();
118120
private SqlObjectList<SqlObject> _ordering = SqlObjectList.create();
119121
private ComboCondition _having = ComboCondition.and();
122+
private SqlObjectList<SqlObject> _windows = SqlObjectList.create();
120123
private SqlObject _offset;
121124
private SqlObject _fetchCount;
122125

@@ -424,8 +427,7 @@ public ComboCondition getHavingClause() {
424427
/**
425428
* Adds a condition to the HAVING clause for the select query (AND'd with
426429
* any other HAVING conditions). Note that the HAVING clause will only be
427-
* generated if some conditions have
428-
been added.
430+
* generated if some conditions have been added.
429431
* <p>
430432
* For convenience purposes, the SelectQuery generates it's own
431433
* ComboCondition allowing multiple HAVING conditions to be AND'd together.
@@ -437,12 +439,26 @@ public SelectQuery addHaving(Condition newCondition) {
437439
return this;
438440
}
439441

442+
/**
443+
* Adds a named window definition to the select query's WINDOW definitions
444+
* <p>
445+
* {@code Object} -&gt; {@code SqlObject} conversions handled by
446+
* {@link Converter#toCustomSqlObject}.
447+
* @see "SQL 2003"
448+
*/
449+
public SelectQuery addWindowDefinition(String name, Object window) {
450+
_windows.addObject(new NamedWindowDefinition(
451+
name, Converter.toCustomSqlObject(window)));
452+
return this;
453+
}
454+
440455
/**
441456
* Sets the value for the "OFFSET" clause. Note that this clause is defined
442457
* in "SQL 2008".
443458
* <p>
444459
* {@code Object} -&gt; {@code SqlObject} conversions handled by
445460
* {@link Converter#toValueSqlObject}.
461+
* @see "SQL 2008"
446462
*/
447463
public SelectQuery setOffset(Object offset) {
448464
_offset = Converter.toValueSqlObject(offset);
@@ -455,6 +471,7 @@ public SelectQuery setOffset(Object offset) {
455471
* <p>
456472
* {@code Object} -&gt; {@code SqlObject} conversions handled by
457473
* {@link Converter#toValueSqlObject}.
474+
* @see "SQL 2008"
458475
*/
459476
public SelectQuery setFetchNext(Object fetchCount) {
460477
_fetchCount = Converter.toValueSqlObject(fetchCount);
@@ -489,14 +506,15 @@ public SelectQuery addCustomization(CustomSyntax obj) {
489506
}
490507

491508
@Override
492-
protected void collectSchemaObjects(ValidationContext vContext) {
509+
protected void collectSchemaObjects(ValidationContext vContext) {
493510
super.collectSchemaObjects(vContext);
494511
_joins.collectSchemaObjects(vContext);
495512
_columns.collectSchemaObjects(vContext);
496513
_condition.collectSchemaObjects(vContext);
497514
_grouping.collectSchemaObjects(vContext);
498515
_ordering.collectSchemaObjects(vContext);
499516
_having.collectSchemaObjects(vContext);
517+
_windows.collectSchemaObjects(vContext);
500518
if(_offset != null) {
501519
_offset.collectSchemaObjects(vContext);
502520
}
@@ -506,7 +524,7 @@ protected void collectSchemaObjects(ValidationContext vContext) {
506524
}
507525

508526
@Override
509-
public void validate(ValidationContext vContext)
527+
public void validate(ValidationContext vContext)
510528
throws ValidationException
511529
{
512530
// if we have joins, check the tables, otherwise, the join tables will
@@ -624,7 +642,7 @@ private static void validateValue(SqlObject valueObj, String type, int minVal) {
624642
}
625643

626644
@Override
627-
protected void appendTo(AppendableExt app, SqlContext newContext)
645+
protected void appendTo(AppendableExt app, SqlContext newContext)
628646
throws IOException
629647
{
630648
newContext.setUseTableAliases(true);
@@ -658,6 +676,9 @@ protected void appendTo(AppendableExt app, SqlContext newContext)
658676
// BY clause)
659677
maybeAppendTo(app, Hook.HAVING, " HAVING ", _having, !_having.isEmpty());
660678
}
679+
680+
// append window definition clauses
681+
maybeAppendTo(app, Hook.WINDOW, " WINDOW ", _windows, !_windows.isEmpty());
661682

662683
// append ordering clause
663684
maybeAppendTo(app, Hook.ORDER_BY, " ORDER BY ", _ordering,
@@ -742,7 +763,7 @@ static boolean hasAllColumns(SqlObjectList<? extends SqlObject> columns) {
742763
* Outputs the right side of a join clause
743764
* <code>"&lt;joinType&gt; &lt;toTable&gt; ON &lt;joinCondition&gt;"</code>.
744765
*/
745-
private static class JoinTo extends SqlObject
766+
private static final class JoinTo extends SqlObject
746767
{
747768
private SqlObject _toTable;
748769
private JoinType _joinType;
@@ -793,7 +814,30 @@ public void appendTo(AppendableExt app) throws IOException {
793814
app.append(", ").append(_toTable);
794815
}
795816
}
796-
797817
}
798818

819+
/**
820+
* Outputs a named window definition clause like
821+
* <code>"&lt;name&gt; AS &lt;windowDefinition&gt;"</code>.
822+
*/
823+
private static final class NamedWindowDefinition extends SqlObject
824+
{
825+
private final String _name;
826+
private final SqlObject _definition;
827+
828+
private NamedWindowDefinition(String name, SqlObject definition) {
829+
_name = name;
830+
_definition = definition;
831+
}
832+
833+
@Override
834+
protected void collectSchemaObjects(ValidationContext vContext) {
835+
_definition.collectSchemaObjects(vContext);
836+
}
837+
838+
@Override
839+
public void appendTo(AppendableExt app) throws IOException {
840+
app.append(_name).append(" AS ").append(_definition);
841+
}
842+
}
799843
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
/**
2626
* Base query for queries which generate a series of SELECT queries joined by
2727
* one or more "set operations", such as UNION [ALL], EXCEPT [ALL], and
28-
* INSERSECT [ALL].
28+
* INTERSECT [ALL].
2929
*
3030
* @author James Ahlborn
3131
*/

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,10 +104,14 @@ protected void doValidate() throws ValidationException
104104
/**
105105
* Utility method for classes which may not be in this package to invoke
106106
* {@link #collectSchemaObjects(ValidationContext)} on a SqlObject.
107+
* @param obj relevant sql object, may be {@code null}
108+
* @param vContext current ValidationContext
107109
*/
108110
public static void collectSchemaObjects(
109111
SqlObject obj, ValidationContext vContext) {
110-
obj.collectSchemaObjects(vContext);
112+
if(obj != null) {
113+
obj.collectSchemaObjects(vContext);
114+
}
111115
}
112116

113117
/**

0 commit comments

Comments
 (0)