Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature #72: Support aggregate and analytics functions #122

Merged
Merged
Show file tree
Hide file tree
Changes from 25 commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
5b4af25
Delete and ignore Eclipse config files
kaklakariada Sep 22, 2021
131553e
Upgrade dependencies
kaklakariada Sep 22, 2021
a66b59b
Configure sonarlint binding
kaklakariada Sep 22, 2021
3e51260
Fix sonar issues
kaklakariada Sep 22, 2021
8987fc5
Add dependabot and matrix build with java 16
kaklakariada Sep 22, 2021
da31304
Building with Java 16 causes many javadoc errors
kaklakariada Sep 22, 2021
bbeb158
Fix yml syntax
kaklakariada Sep 22, 2021
0014f2d
Upgrade project-keeper
kaklakariada Sep 22, 2021
89e4d9b
#102: Refactor ColumnsDefinitionRenderer to remove the leave() method
kaklakariada Sep 23, 2021
38e827c
Add tests with empty column list
kaklakariada Sep 23, 2021
f2473f8
Update changelog
kaklakariada Sep 23, 2021
2c5ac32
Add aggregate functions test
kaklakariada Sep 23, 2021
2767ea6
Increment minor version, add change notes
kaklakariada Sep 23, 2021
8609edb
Merge branch 'main' into feature/72-support-aggregate-and-analytics-f…
kaklakariada Sep 23, 2021
2170d9b
Update changelog version to 4.5.0
kaklakariada Sep 23, 2021
593a91f
Rename constant to match naming conventions
kaklakariada Sep 27, 2021
92de3b4
Merge branch 'main' into feature/72-support-aggregate-and-analytics-f…
kaklakariada Sep 27, 2021
1ca7984
Add AnalyticFunction with keyword support
kaklakariada Sep 27, 2021
35d55fb
Remove duplicate code
kaklakariada Sep 27, 2021
0b4a104
Document creating analytic functions
kaklakariada Sep 27, 2021
be43013
Initial support for OVER clause
kaklakariada Sep 28, 2021
19fb243
Add order by to over clause
kaklakariada Sep 28, 2021
cdae028
Merge remote-tracking branch 'origin/main' into feature/72-support-ag…
kaklakariada Sep 28, 2021
6dc4bf9
Move analytic & aggregate functions to new enum
kaklakariada Sep 29, 2021
c2ea5d0
Implement units and exclusion for over clause
kaklakariada Sep 29, 2021
04cc2c6
Apply suggestions from code review
kaklakariada Sep 29, 2021
d53bb6b
Remove WindowClause
kaklakariada Sep 29, 2021
a45ab76
Update dependencies in changelog
kaklakariada Sep 29, 2021
058e3bf
Add javadoc
kaklakariada Sep 29, 2021
563b73c
Merge branch 'feature/72-support-aggregate-and-analytics-functions' o…
kaklakariada Sep 29, 2021
6a04cda
Add unit tests
kaklakariada Sep 29, 2021
a5aab55
Update user guide
kaklakariada Sep 29, 2021
667870c
Update list of aggregate & analytic functions
kaklakariada Sep 29, 2021
78e14e8
Format function names and add links to the documentation
kaklakariada Sep 29, 2021
4fb839f
Update release date
kaklakariada Sep 29, 2021
8802904
Apply suggestions from code review
kaklakariada Sep 30, 2021
5ef02a3
Implement review findings
kaklakariada Sep 30, 2021
a876619
Merge branch 'feature/72-support-aggregate-and-analytics-functions' o…
kaklakariada Sep 30, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion doc/changes/changelog.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Changes

* [4.4.3](changes_4.4.3.md)
* [4.5.0](changes_4.5.0.md)
* [4.4.2](changes_4.4.2.md)
* [4.4.1](changes_4.4.1.md)
* [4.4.0](changes_4.4.0.md)
Expand Down
14 changes: 12 additions & 2 deletions doc/changes/changes_4.4.3.md → doc/changes/changes_4.5.0.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,16 @@
# Exasol SQL Statement Builder 4.4.3, released 2021-09-??
# Exasol SQL Statement Builder 4.5.0, released 2021-09-??

Code name: Internal refactorings on "More Predicates"
Code name: Support more Aggregate and Analytic functions

## Summary

This release supports all aggregate and analytics functsions provided by Exasol, e.g. `GROUPING[_ID]`, `PERCENTILE_CONT`, `NTH_VALUE` and many more. See the [ticket](https://github.com/exasol/sql-statement-builder/issues/72) for a complete list.

We also addes support for the keywords `DISTINCT` and `ANY` as well as the [over_clause](https://docs.exasol.com/sql_references/functions/analyticfunctions.htm?Highlight=over_clause) for analytic functions.
kaklakariada marked this conversation as resolved.
Show resolved Hide resolved

## Features

* #72: Added support for more Aggregate and Analytic functions

## Refactoring

Expand Down
28 changes: 28 additions & 0 deletions doc/user_guide/statements/select.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ A udf takes a name of function and any number of [`ValueExpression`](../../../sr
selectWithEmits.from().table("people");
```

- To add special functions (e.g. analytic functions) to a statement you can use the `function()` method that takes a `Function` as argument. See the [section about creating functions](#creating-functions) for details.

- An `arithmetic expression` is a binary value expression using one of the following arithmetic operators: `+`, `-`, `*`, `/`.
Add an arithmetic expression using an `arithmeticExpression( ... )` method. You can also set a name for a derived field that contains an arithmetic expression.

Expand Down Expand Up @@ -165,3 +167,29 @@ select.from().table("t");
select.orderBy(column("t", "city"), column("t", "price"))
.nullsFirst().asc();
```

## Creating Functions

When you need to use special functions like analytic functions, you can add them to a statement like this:

```java
Function function = ... // create function
Select select = factory.select().function(function, "<column>");
```

### Analytic functions

#### Keywords `DISTINCT` and `ALL`

You create an analytic function with a keyword `DISTINCT` or `ALL` like this:

```java
Function function = AnalyticFunction.of(ExasolAnalyticFunction.ANY, Keyword.DISTINCT,
kaklakariada marked this conversation as resolved.
Show resolved Hide resolved
BooleanTerm.lt(column("age"), integerLiteral(30)));
```

This will render to the following SQL code:

```sql
SELECT ANY(DISTINCT(age < 30))
```
7 changes: 6 additions & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>com.exasol</groupId>
<artifactId>sql-statement-builder</artifactId>
<version>4.4.3</version>
<version>4.5.0</version>
<name>Exasol SQL Statement Builder</name>
<description>This module provides a Builder for SQL statements that helps creating the correct structure and
validates variable parts of the statements.
Expand Down Expand Up @@ -65,6 +65,11 @@
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>com.exasol</groupId>
<artifactId>error-reporting-java</artifactId>
<version>0.4.0</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
Expand Down
38 changes: 35 additions & 3 deletions src/main/java/com/exasol/sql/dql/select/GroupByClause.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,12 @@ public class GroupByClause extends AbstractFragment implements SelectFragment {
private final SqlStatement rootStatement;
private final List<ColumnReference> columnReferences;
private BooleanExpression booleanExpression;
private WindowClause windowClause;

/**
* Create a new instance of a {@link GroupByClause}.
*
* @param rootStatement SQL statement this {@code GROUP BY} clause belongs to
* @param rootStatement SQL statement this {@code GROUP BY} clause belongs to
* @param columnReferences column references for the {@code GROUP BY} clause
*/
public GroupByClause(final SqlStatement rootStatement, final ColumnReference... columnReferences) {
Expand Down Expand Up @@ -54,11 +55,42 @@ public Select having(final BooleanExpression booleanExpression) {
}

/**
* Get the boolean expression.
* Get the "having" boolean expression.
*
* @return boolean expression
* @return "having" boolean expression
*/
public BooleanExpression getHavingBooleanExpression() {
return this.booleanExpression;
}

/**
* Add a window clause to the having statement. Return the existing window clause if one was added before.
*
* @return the {@link WindowClause} for fluent programming
*/
public WindowClause window() {
if (this.windowClause == null) {
this.windowClause = WindowClause.of(this.root);
}
return this.windowClause;
}

/**
* Add the given window clause to the having statement.
*
* @return this {@link GroupByClause} for fluent programming
*/
public GroupByClause window(final WindowClause windowClause) {
this.windowClause = windowClause;
return this;
}

/**
* Get the window clause of the "having" statement.
*
* @return the window clause
*/
public WindowClause getWindowClause() {
return this.windowClause;
}
}
8 changes: 2 additions & 6 deletions src/main/java/com/exasol/sql/dql/select/Select.java
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,7 @@ public Select function(final FunctionName functionName, final ValueExpression...
public Select function(final FunctionName functionName, final String derivedColumnName,
final ValueExpression... valueExpressions) {
final Function function = ExpressionTerm.function(functionName, valueExpressions);
final DerivedColumn derivedColumn = new DerivedColumn(this, function, derivedColumnName);
this.derivedColumns.add(derivedColumn);
return this;
return this.function(function, derivedColumnName);
}

/**
Expand All @@ -99,8 +97,7 @@ public Select function(final Function function, final String derivedColumnName)
* @return {@code this} instance for fluent programming
*/
public Select function(final Function function) {
function(function, "");
return this;
return function(function, "");
}

/**
Expand All @@ -111,7 +108,6 @@ public Select function(final Function function) {
* @param valueExpressions zero or more value expressions
* @return {@code this} instance for fluent programming
*/

public Select udf(final String functionName, final ColumnsDefinition emitsColumnsDefinition,
final ValueExpression... valueExpressions) {
final Function udf = ExpressionTerm.udf(functionName, emitsColumnsDefinition, valueExpressions);
Expand Down
2 changes: 2 additions & 0 deletions src/main/java/com/exasol/sql/dql/select/SelectVisitor.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,6 @@ public interface SelectVisitor extends ValueTableVisitor {
public void visit(DerivedColumn derivedColumn);

public void visit(Table table);

public void visit(WindowClause windowClause);
}
23 changes: 23 additions & 0 deletions src/main/java/com/exasol/sql/dql/select/WindowClause.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.exasol.sql.dql.select;

import com.exasol.sql.AbstractFragment;
import com.exasol.sql.Fragment;

/**
* This class represents the {@code WINDOW} clause following the {@code HAVING} clause of an SQL statement.
*/
public class WindowClause extends AbstractFragment implements SelectFragment {

private WindowClause(final Fragment root) {
super(root);
}

public static WindowClause of(final Fragment root) {
return new WindowClause(root);
}

@Override
public void accept(final SelectVisitor visitor) {
visitor.visit(this);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,7 @@

import java.util.List;

import com.exasol.sql.DerivedColumn;
import com.exasol.sql.Table;
import com.exasol.sql.ValueTable;
import com.exasol.sql.ValueTableRow;
import com.exasol.sql.*;
import com.exasol.sql.dql.select.*;
import com.exasol.sql.expression.BooleanExpression;
import com.exasol.sql.rendering.AbstractFragmentRenderer;
Expand Down Expand Up @@ -154,7 +151,7 @@ public void leave(final ValueTable valueTable) {
final List<String> columnNameAliases = valueTable.getColumnNameAliases();
for (int i = 0; i < columnNameAliases.size(); i++) {
kaklakariada marked this conversation as resolved.
Show resolved Hide resolved
appendAutoQuoted(columnNameAliases.get(i));
if (i < columnNameAliases.size() - 1) {
if (i < (columnNameAliases.size() - 1)) {
append(", ");
}
}
Expand All @@ -177,6 +174,11 @@ public void leave(final ValueTableRow valueTableRow) {
setLastVisited(valueTableRow);
}

@Override
public void visit(final WindowClause windowClause) {
throw new UnsupportedOperationException("todo");
}

/**
* Create an {@link SelectRenderer} using the default renderer configuration
*
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package com.exasol.sql.expression.function;

import com.exasol.sql.expression.function.exasol.CastExasolFunction;
import com.exasol.sql.expression.function.exasol.ExasolFunction;
import com.exasol.sql.expression.function.exasol.ExasolUdf;
import com.exasol.sql.expression.function.exasol.*;

/**
* Visitor interface for {@link Function}.
Expand All @@ -13,4 +11,6 @@ public interface FunctionVisitor {
public void visit(ExasolUdf function);

public void visit(CastExasolFunction castFunction);

public void visit(AnalyticFunction analyticFunction);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
package com.exasol.sql.expression.function.exasol;

import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;

import com.exasol.sql.expression.ValueExpression;
import com.exasol.sql.expression.function.AbstractFunction;
import com.exasol.sql.expression.function.FunctionVisitor;

/**
* This class represents an analytic function in the Exasol database that supports keywords {@code DISTINCT} and
* {@code ALL} and the over clause.
*/
public class AnalyticFunction extends AbstractFunction {

public enum Keyword {
DISTINCT, ALL
}

private final Keyword keyword;
private OverClause overClause;

private AnalyticFunction(final ExasolAnalyticAggregateFunctions functionName, final Keyword keyword,
final List<ValueExpression> valueExpressions) {
super(functionName.toString(), valueExpressions);
this.keyword = keyword;
}

/**
* Create a new {@link AnalyticFunction} instance.
*
* @param functionName name of the function
* @param valueExpressions zero or more value expressions
* @return new {@link AnalyticFunction}
*/
public static AnalyticFunction of(final ExasolAnalyticAggregateFunctions functionName,
final ValueExpression... valueExpressions) {
return of(functionName, null, valueExpressions);
}

/**
* Create a new {@link AnalyticFunction} instance with a keyword.
*
* @param functionName name of the function
* @param keyword keyword used in the function
* @param valueExpressions zero or more value expressions
* @return new {@link AnalyticFunction}
*/
public static AnalyticFunction of(final ExasolAnalyticAggregateFunctions functionName, final Keyword keyword,
final ValueExpression... valueExpressions) {
return new AnalyticFunction(functionName, keyword, Arrays.asList(valueExpressions));
}

/**
* Get the keyword for the function call, may be {@code null}.
*
* @return keyword for the function call
*/
public Keyword getKeyword() {
return this.keyword;
}

/**
* Add the given over clause to the function call.
*
* @param overClause over clause to add
* @return this {@link AnalyticFunction} for fluent programming
*/
public AnalyticFunction over(final OverClause overClause) {
this.overClause = overClause;
return this;
}

public OverClause over(final String windowName) {
return over(over -> over.windowName(windowName)).overClause;
}

public AnalyticFunction over(final Consumer<OverClause> consumer) {
if (this.overClause == null) {
this.overClause = new OverClause();
}
consumer.accept(this.overClause);
return this;
}

/**
* Get the "over clause" appended to the function call, may be {@code null}.
*
* @return "over clause" appended to the function call
*/
public OverClause getOverClause() {
return this.overClause;
}

@Override
public boolean hasParenthesis() {
return true;
}

@Override
public void accept(final FunctionVisitor visitor) {
visitor.visit(this);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,9 @@
/**
* This class is a list of Aggregate Functions that the Exasol database supports.
*
* <p>
* Currently unsupported functions: GROUPING, PERCENTILE_CONT, PERCENTILE_DISC, OVER clause for all aggregate functions
* and keywords. See <a href="https://github.com/exasol/sql-statement-builder/issues/72"> github issue # 72</a>.
* </p>
* @deprecated Use enum {@link ExasolAnalyticAggregateFunctions}.
*/
@Deprecated
kaklakariada marked this conversation as resolved.
Show resolved Hide resolved
public enum ExasolAggregateFunction implements FunctionName {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does it stil, work?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It works with the normal ExasolFunction. The AnalyticFunction class requires ExasolAnalyticAggregateFunctions.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

APPROXIMATE_COUNT_DISTINCT, AVG, CORR, COUNT, COVAR_POP, COVAR_SAMP, FIRST_VALUE, GROUP_CONCAT, LAST_VALUE, MAX,
MEDIAN, MIN, REGR_SLOPE, REGR_INTERCEPT, REGR_COUNT, REGR_R2, REGR_AVGX, REGR_AVGY, REGR_SXX, REGR_SXY, REGR_SYY,
Expand Down
Loading