You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by su...@apache.org on 2021/07/19 11:07:05 UTC
[groovy] branch master updated: Support `distinct` in GINQ
This is an automated email from the ASF dual-hosted git repository.
sunlan pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/groovy.git
The following commit(s) were added to refs/heads/master by this push:
new b8131c4 Support `distinct` in GINQ
b8131c4 is described below
commit b8131c4939d33b34ad145a9b227efa8706c979f4
Author: Daniel Sun <su...@apache.org>
AuthorDate: Mon Jul 19 19:06:51 2021 +0800
Support `distinct` in GINQ
---
.../org/apache/groovy/ginq/dsl/GinqAstBuilder.java | 36 ++++++++++++++++---
.../ginq/provider/collection/GinqAstWalker.groovy | 5 +++
.../groovy-ginq/src/spec/doc/ginq-userguide.adoc | 20 ++++++-----
.../test/org/apache/groovy/ginq/GinqTest.groovy | 40 ++++++++++++++++++++++
.../org/apache/groovy/ginq/GinqErrorTest.groovy | 11 ++++++
5 files changed, 100 insertions(+), 12 deletions(-)
diff --git a/subprojects/groovy-ginq/src/main/groovy/org/apache/groovy/ginq/dsl/GinqAstBuilder.java b/subprojects/groovy-ginq/src/main/groovy/org/apache/groovy/ginq/dsl/GinqAstBuilder.java
index 379472a..9c8b1b4 100644
--- a/subprojects/groovy-ginq/src/main/groovy/org/apache/groovy/ginq/dsl/GinqAstBuilder.java
+++ b/subprojects/groovy-ginq/src/main/groovy/org/apache/groovy/ginq/dsl/GinqAstBuilder.java
@@ -42,6 +42,7 @@ import org.codehaus.groovy.ast.expr.DeclarationExpression;
import org.codehaus.groovy.ast.expr.Expression;
import org.codehaus.groovy.ast.expr.MethodCallExpression;
import org.codehaus.groovy.ast.expr.PropertyExpression;
+import org.codehaus.groovy.ast.expr.TupleExpression;
import org.codehaus.groovy.ast.expr.VariableExpression;
import org.codehaus.groovy.ast.stmt.BlockStatement;
import org.codehaus.groovy.ast.stmt.ExpressionStatement;
@@ -64,6 +65,7 @@ import java.util.Set;
*/
public class GinqAstBuilder extends CodeVisitorSupport implements SyntaxErrorReportable {
public static final String ROOT_GINQ_EXPRESSION = "__ROOT_GINQ_EXPRESSION";
+ public static final String GINQ_SELECT_DISTINCT = "__GINQ_SELECT_DISTINCT";
private final Deque<GinqExpression> ginqExpressionStack = new ArrayDeque<>();
private GinqExpression latestGinqExpression;
private final SourceUnit sourceUnit;
@@ -151,11 +153,11 @@ public class GinqAstBuilder extends CodeVisitorSupport implements SyntaxErrorRep
@Override
public void visitMethodCallExpression(MethodCallExpression call) {
final String methodName = call.getMethodAsString();
- if ("over".equals(methodName)) {
+ if (KW_OVER.equals(methodName)) {
visitingOverClause = true;
}
super.visitMethodCallExpression(call);
- if ("over".equals(methodName)) {
+ if (KW_OVER.equals(methodName)) {
visitingOverClause = false;
}
@@ -329,7 +331,31 @@ public class GinqAstBuilder extends CodeVisitorSupport implements SyntaxErrorRep
}
if (KW_SELECT.equals(methodName)) {
- SelectExpression selectExpression = new SelectExpression(call.getArguments());
+ TupleExpression tupleExpression = (TupleExpression) call.getArguments();
+ if (1 == tupleExpression.getExpressions().size()) {
+ Expression firstExpression = tupleExpression.getExpressions().get(0);
+ if (firstExpression instanceof MethodCallExpression) {
+ MethodCallExpression mce = (MethodCallExpression) firstExpression;
+ if (KW_DISTINCT.equals(mce.getMethodAsString())) {
+ tupleExpression = (TupleExpression) mce.getArguments();
+ currentGinqExpression.putNodeMetaData(GINQ_SELECT_DISTINCT, true);
+ }
+ }
+ } else {
+ for (Expression expression : tupleExpression.getExpressions()) {
+ if (expression instanceof MethodCallExpression) {
+ MethodCallExpression mce = (MethodCallExpression) expression;
+ if (KW_DISTINCT.equals(mce.getMethodAsString())) {
+ this.collectSyntaxError(new GinqSyntaxError(
+ "Invalid usage of `distinct`",
+ mce.getLineNumber(), mce.getColumnNumber()
+ ));
+ }
+ }
+ }
+ }
+
+ SelectExpression selectExpression = new SelectExpression(tupleExpression);
selectExpression.setSourcePosition(call.getMethod());
currentGinqExpression.setSelectExpression(selectExpression);
@@ -450,9 +476,11 @@ public class GinqAstBuilder extends CodeVisitorSupport implements SyntaxErrorRep
private static final String KW_LIMIT = "limit";
private static final String KW_SELECT = "select";
private static final String KW_SHUTDOWN = "shutdown";
+ private static final String KW_DISTINCT = "distinct";
+ private static final String KW_OVER = "over";
private static final Set<String> KEYWORD_SET = new HashSet<>();
static {
- KEYWORD_SET.addAll(Arrays.asList(KW_FROM, KW_WHERE, KW_ON, KW_HAVING, KW_EXISTS, KW_GROUPBY, KW_ORDERBY, KW_LIMIT, KW_SELECT, KW_SHUTDOWN));
+ KEYWORD_SET.addAll(Arrays.asList(KW_FROM, KW_WHERE, KW_ON, KW_HAVING, KW_EXISTS, KW_GROUPBY, KW_ORDERBY, KW_LIMIT, KW_SELECT, KW_DISTINCT, KW_OVER, KW_SHUTDOWN));
KEYWORD_SET.addAll(JoinExpression.JOIN_NAME_LIST);
}
}
diff --git a/subprojects/groovy-ginq/src/main/groovy/org/apache/groovy/ginq/provider/collection/GinqAstWalker.groovy b/subprojects/groovy-ginq/src/main/groovy/org/apache/groovy/ginq/provider/collection/GinqAstWalker.groovy
index 819f979..3333790 100644
--- a/subprojects/groovy-ginq/src/main/groovy/org/apache/groovy/ginq/provider/collection/GinqAstWalker.groovy
+++ b/subprojects/groovy-ginq/src/main/groovy/org/apache/groovy/ginq/provider/collection/GinqAstWalker.groovy
@@ -202,6 +202,11 @@ class GinqAstWalker implements GinqAstVisitor<Expression>, SyntaxErrorReportable
}
final resultName = "__r${System.nanoTime()}"
+
+ Boolean distinct = ginqExpression.getNodeMetaData(GinqAstBuilder.GINQ_SELECT_DISTINCT)
+ if (distinct) {
+ selectMethodCallExpression = callX(selectMethodCallExpression, "distinct")
+ }
statementList << declS(localVarX(resultName).tap {it.modifiers |= Opcodes.ACC_FINAL}, selectMethodCallExpression)
if (parallelEnabled) {
diff --git a/subprojects/groovy-ginq/src/spec/doc/ginq-userguide.adoc b/subprojects/groovy-ginq/src/spec/doc/ginq-userguide.adoc
index 7c19105..cc5c708 100644
--- a/subprojects/groovy-ginq/src/spec/doc/ginq-userguide.adoc
+++ b/subprojects/groovy-ginq/src/spec/doc/ginq-userguide.adoc
@@ -141,6 +141,18 @@ Construct new objects as column values:
include::../test/org/apache/groovy/ginq/GinqTest.groovy[tags=ginq_projection_02,indent=0]
----
+===== Distinct
+`distinct` is equivalent to SQL's `DISTINCT`
+[source, groovy]
+----
+include::../test/org/apache/groovy/ginq/GinqTest.groovy[tags=ginq_distinct_1,indent=0]
+----
+
+[source, groovy]
+----
+include::../test/org/apache/groovy/ginq/GinqTest.groovy[tags=ginq_distinct_2,indent=0]
+----
+
==== Filtering
`where` is equivalent to SQL's `WHERE`
@@ -875,15 +887,7 @@ include::../test/org/apache/groovy/ginq/GinqTest.groovy[tags=ginq_winfunction_38
include::../test/org/apache/groovy/ginq/GinqTest.groovy[tags=ginq_tips_05,indent=0]
----
-==== Distinct
-GINQ does not support `distinct` of SQL for now, but we could invoke `distinct()` method to workaround:
-[source, groovy]
-----
-include::../test/org/apache/groovy/ginq/GinqTest.groovy[tags=ginq_tips_14,indent=0]
-----
-
==== List Comprehension
-
List comprehension is an elegant way to define and create lists based on existing lists:
[source, groovy]
----
diff --git a/subprojects/groovy-ginq/src/spec/test/org/apache/groovy/ginq/GinqTest.groovy b/subprojects/groovy-ginq/src/spec/test/org/apache/groovy/ginq/GinqTest.groovy
index 91c0d21..8220c90 100644
--- a/subprojects/groovy-ginq/src/spec/test/org/apache/groovy/ginq/GinqTest.groovy
+++ b/subprojects/groovy-ginq/src/spec/test/org/apache/groovy/ginq/GinqTest.groovy
@@ -235,6 +235,46 @@ class GinqTest {
}
@Test
+ void "testGinq - from select distinct - 1"() {
+ assertGinqScript '''
+// tag::ginq_distinct_1[]
+ def result = GQ {
+ from n in [1, 2, 2, 3, 3, 3]
+ select distinct(n)
+ }
+ assert [1, 2, 3] == result.toList()
+// end::ginq_distinct_1[]
+ '''
+ }
+
+ @Test
+ void "testGinq - from select distinct - 2"() {
+ assertGinqScript '''
+// tag::ginq_distinct_2[]
+ def result = GQ {
+ from n in [1, 2, 2, 3, 3, 3]
+ select distinct(n, n + 1)
+ }
+ assert [[1, 2], [2, 3], [3, 4]] == result.toList()
+// end::ginq_distinct_2[]
+ '''
+ }
+
+ @Test
+ void "testGinq - from select distinct - 3"() {
+ assertGinqScript '''
+ def result = GQ {
+ from v in (
+ from n in [1, 2, 2, 3, 3, 3]
+ select distinct(n)
+ )
+ select v
+ }
+ assert [1, 2, 3] == result.toList()
+ '''
+ }
+
+ @Test
void "testGinq - from where select - 1"() {
assertGinqScript '''
def numbers = [0, 1, 2, 3, 4, 5]
diff --git a/subprojects/groovy-ginq/src/test/groovy/org/apache/groovy/ginq/GinqErrorTest.groovy b/subprojects/groovy-ginq/src/test/groovy/org/apache/groovy/ginq/GinqErrorTest.groovy
index 0beefad..bea17c9 100644
--- a/subprojects/groovy-ginq/src/test/groovy/org/apache/groovy/ginq/GinqErrorTest.groovy
+++ b/subprojects/groovy-ginq/src/test/groovy/org/apache/groovy/ginq/GinqErrorTest.groovy
@@ -49,6 +49,17 @@ class GinqErrorTest {
}
@Test
+ void "testGinq - from select distinct - 1"() {
+ def err = shouldFail '''\
+ GQ {
+ from n in [1, 2, 2, 3, 3, 3]
+ select n, distinct(n + 1)
+ }
+ '''
+ assert err.toString().contains('Invalid usage of `distinct` @ line 3, column 27.')
+ }
+
+ @Test
void "testGinq - select from - 1"() {
def err = shouldFail '''\
GQ {