[ 
https://issues.apache.org/jira/browse/GROOVY-11919?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=18072821#comment-18072821
 ] 

ASF GitHub Bot commented on GROOVY-11919:
-----------------------------------------

Copilot commented on code in PR #2458:
URL: https://github.com/apache/groovy/pull/2458#discussion_r3067840300


##########
subprojects/groovy-ginq/src/main/groovy/org/apache/groovy/ginq/dsl/GinqAstBuilder.java:
##########
@@ -420,6 +433,27 @@ public void visitBinaryExpression(BinaryExpression 
expression) {
 
     @Override
     public void visitVariableExpression(VariableExpression expression) {
+        if (SET_OP_SET.contains(expression.getText())) {
+            if (latestGinqExpression == null) {
+                this.collectSyntaxError(new GinqSyntaxError(
+                        "`" + expression.getText() + "` must follow a complete 
GINQ expression (from...select)",
+                        expression.getLineNumber(), 
expression.getColumnNumber()));
+            } else {
+                if (setOperationLeft != null) {
+                    // chaining: wrap previous left + op + current right into 
a SetOperationExpression
+                    latestGinqExpression.putNodeMetaData(ROOT_GINQ_EXPRESSION, 
latestGinqExpression);
+                    SetOperationExpression prev = new 
SetOperationExpression(setOperationLeft, setOperationOp, latestGinqExpression);
+                    prev.setSourcePosition(setOperationLeft);
+                    setOperationLeft = prev;
+                } else {
+                    setOperationLeft = latestGinqExpression;
+                }
+                setOperationOp = expression.getText();
+                latestGinqExpression = null;
+            }
+            return;
+        }

Review Comment:
   Set operations are tracked by stashing `setOperationLeft`/`setOperationOp` 
and clearing `latestGinqExpression`, but the combined `SetOperationExpression` 
is only materialized later in `getGinqExpression(ASTNode)` at the end of 
`buildAST`. This means set operations won’t work correctly in nested-query 
contexts that consume `latestGinqExpression` during the AST walk (e.g. 
`(...).exists()`, `x in (...)`, casts/argument lists): those sites will see 
only the *rightmost* `from...select` and the prior `union/intersect/minus` will 
be lost. To fix this, construct and assign a combined `AbstractGinqExpression` 
as soon as the RHS `select` is completed (and store the latest nested 
expression as `AbstractGinqExpression`, not just `GinqExpression`), or 
otherwise scope pending set-op state per nesting level so nested replacements 
use the full set-op tree.



##########
subprojects/groovy-ginq/src/main/groovy/org/apache/groovy/ginq/dsl/GinqAstVisitor.java:
##########
@@ -51,6 +52,7 @@ public interface GinqAstVisitor<R> {
     R visitOrderExpression(OrderExpression orderExpression);
     R visitLimitExpression(LimitExpression limitExpression);
     R visitSelectExpression(SelectExpression selectExpression);
+    R visitSetOperationExpression(SetOperationExpression 
setOperationExpression);
     R visitShutdownExpression(ShutdownExpression shutdownExpression);

Review Comment:
   `SetOperationExpression` is imported/used here, but there is no 
`org.apache.groovy.ginq.dsl.expression.SetOperationExpression` class in the 
source tree (the `dsl/expression` package currently has no such file). This 
will break compilation; add the missing AST node class (with `left`, 
`operation`, `right`, and `accept(...)`/visitor dispatch) or adjust the 
imports/usages to reference the correct existing type.



##########
subprojects/groovy-ginq/src/main/groovy/org/apache/groovy/ginq/dsl/GinqAstBuilder.java:
##########
@@ -31,6 +31,7 @@
 import org.apache.groovy.ginq.dsl.expression.OnExpression;
 import org.apache.groovy.ginq.dsl.expression.OrderExpression;
 import org.apache.groovy.ginq.dsl.expression.SelectExpression;
+import org.apache.groovy.ginq.dsl.expression.SetOperationExpression;

Review Comment:
   `SetOperationExpression` is referenced but the corresponding class does not 
exist in `org.apache.groovy.ginq.dsl.expression` (no file in that package 
defines it). This will prevent `GinqAstBuilder` from compiling; ensure the new 
AST node type is added to `dsl/expression` and wired into the visitor pattern.
   



##########
subprojects/groovy-ginq/src/main/groovy/org/apache/groovy/ginq/dsl/GinqAstBuilder.java:
##########
@@ -110,30 +113,40 @@ public AbstractGinqExpression buildAST(ASTNode astNode) {
         return getGinqExpression(astNode);
     }
 
-    private GinqExpression getGinqExpression(ASTNode astNode) {
+    private AbstractGinqExpression getGinqExpression(ASTNode astNode) {
         if (null == latestGinqExpression) {
             ASTNode node = ginqExpressionStack.isEmpty() ? astNode : 
ginqExpressionStack.peek();
             this.collectSyntaxError(new GinqSyntaxError("`select` clause is 
missing",
                     node.getLineNumber(), node.getColumnNumber()));
         }
 
-        latestGinqExpression.visit(new GinqAstBaseVisitor() {
+        AbstractGinqExpression result;
+        if (setOperationLeft != null && latestGinqExpression != null) {
+            latestGinqExpression.putNodeMetaData(ROOT_GINQ_EXPRESSION, 
latestGinqExpression);
+            SetOperationExpression setOpExpr = new 
SetOperationExpression(setOperationLeft, setOperationOp, latestGinqExpression);
+            setOpExpr.setSourcePosition(setOperationLeft);
+            result = setOpExpr;
+        } else {
+            latestGinqExpression.putNodeMetaData(ROOT_GINQ_EXPRESSION, 
latestGinqExpression);
+            result = latestGinqExpression;
+        }

Review Comment:
   When building a `SetOperationExpression`, the `ROOT_GINQ_EXPRESSION` 
metadata is only set on `latestGinqExpression` (the right-hand query) and not 
on the left side (or the overall set-op node). `GinqAstWalker` uses this 
metadata to enable window-function support and `parallel` setup/teardown, so 
the left operand of a set operation will currently be treated as non-root and 
may break queries that use `over(...)` or `parallel` on the left side. Consider 
marking *all* operand `GinqExpression`s in the set-op tree as roots (or 
alternatively teaching the walker to treat the set-op root as the root 
context). Also note that returning `SetOperationExpression` as the root means 
`transformGinqCode` will skip `GinqAstOptimizer` (it only optimizes when the 
root is a `GinqExpression`), so set-op queries won’t be optimized unless the 
optimizer path is updated to traverse set-op nodes.



##########
subprojects/groovy-ginq/src/main/groovy/org/apache/groovy/ginq/provider/collection/GinqAstWalker.groovy:
##########
@@ -36,6 +36,7 @@ import org.apache.groovy.ginq.dsl.expression.LimitExpression
 import org.apache.groovy.ginq.dsl.expression.OnExpression
 import org.apache.groovy.ginq.dsl.expression.OrderExpression
 import org.apache.groovy.ginq.dsl.expression.SelectExpression
+import org.apache.groovy.ginq.dsl.expression.SetOperationExpression
 import org.apache.groovy.ginq.dsl.expression.ShutdownExpression
 import org.apache.groovy.ginq.dsl.expression.WhereExpression

Review Comment:
   `SetOperationExpression` is imported/used here, but the 
`org.apache.groovy.ginq.dsl.expression.SetOperationExpression` class is not 
present in the repository. This will cause compilation failures for the 
collection provider; add the missing AST node class under `dsl/expression` (and 
ensure it supports the visitor API).





> Provide set operators for GINQ, e.g. union, intersect
> -----------------------------------------------------
>
>                 Key: GROOVY-11919
>                 URL: https://issues.apache.org/jira/browse/GROOVY-11919
>             Project: Groovy
>          Issue Type: Improvement
>            Reporter: Paul King
>            Priority: Major
>




--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to