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).



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to