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

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

daniellansun commented on code in PR #3259:
URL: https://github.com/apache/groovy/pull/3259#discussion_r4550576103


##########
subprojects/groovy-ginq/src/main/groovy/org/apache/groovy/ginq/provider/collection/runtime/QueryableHelper.groovy:
##########
@@ -111,6 +111,36 @@ class QueryableHelper {
         return CompletableFuture.supplyAsync(() -> { function.apply(param) }, 
ThreadPoolHolder.THREAD_POOL)
     }
 
+    /**
+     * Runs one clause body on the shared pool. The declared type is {@code 
Object}
+     * so static inference never sees {@code CompletableFuture}: a parallel row
+     * was being typed as that future, and the generated bridge then cast the 
row.
+     * The value returned to the runtime is still the future {@code select} 
unwraps.
+     *
+     * @param supplier the clause body
+     * @return the asynchronous result
+     * @since 7.0.0
+     */
+    @GroovyABI(since = "7.0.0")
+    static Object supplyClause(Supplier<Object> supplier) {
+        return CompletableFuture.supplyAsync(supplier, 
ThreadPoolHolder.THREAD_POOL)

Review Comment:
   `supplyClause` stays `CompletableFuture.supplyAsync` on the GINQ pool. 
`async` uses the runtime async executor, and `executeAsync(supplier, pool)` 
returns an `Awaitable`, not a `CompletableFuture`. `select` unwraps with 
`CompletableFuture.get()`. The declared type remains `Object` so a parallel row 
is not inferred as the future.



##########
subprojects/groovy-ginq/src/test/groovy/org/apache/groovy/ginq/GinqCompileStaticTest.groovy:
##########
@@ -0,0 +1,237 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.groovy.ginq
+
+import groovy.ginq.transform.GQ
+import groovy.transform.CompileStatic
+import org.apache.groovy.ginq.provider.collection.runtime.Queryable
+import org.junit.jupiter.api.Test
+
+import static groovy.test.GroovyAssert.assertScript
+
+/**
+ * GINQ SQL-like queries compiled with {@code @CompileStatic}.
+ */
+@CompileStatic
+final class GinqCompileStaticTest {
+
+    private final int minimum = 18
+
+    @Test
+    void macroFiltersAndProjects() {
+        List<Integer> numbers = [1, 2, 3, 4]
+        assert [4, 6, 8] == GQ {
+            from n in numbers
+            where n > 1
+            select n * 2
+        }.toList()
+    }
+
+    @Test
+    void macroAsList() {
+        assert [2, 3] == GQL {
+            from n in [1, 2, 3]
+            where n > 1
+            select n
+        }
+    }
+
+    @Test
+    void readsFieldsAndLocals() {
+        int extra = 1
+        assert [18, 21] == GQ {
+            from n in [15, 18, 21]
+            where n >= minimum - extra
+            select n
+        }.toList()
+    }
+
+    @Test
+    void localIsReadWhenTheQueryIsBuilt() {
+        int extra = 1
+        Queryable query = GQ {
+            from n in [1, 2, 3, 4]
+            where n > extra
+            select n
+        }
+        extra = 3
+        // Locals are copied when the expression is evaluated. The copy is 
what runs later.
+        assert [2, 3, 4] == query.toList()
+    }
+
+    @Test
+    void joinsProjectsAndGroups() {
+        List<Item> items = [new Item(1, 'Orange'), new Item(2, 'Apple')]
+        List<Sale> sales = [new Sale(1), new Sale(1), new Sale(2)]
+        assert ['Orange', 'Orange', 'Apple'] == GQ {
+            from s in sales
+            join i in items on i.id == s.item
+            select i.name
+        }.toList()
+
+        assert [[0, 6.0], [1, 4.0]] == GQ {
+            from n in [1, 2, 3, 4]
+            groupby n % 2 as parity
+            orderby parity
+            select parity, sum(n)
+        }.toList()
+    }
+
+    @Test
+    void windowRowNumber() {
+        assert [[1, 0], [2, 1], [3, 2]] == GQ {
+            from n in [1, 2, 3]
+            select n, (rowNumber() over(orderby n))
+        }.toList()
+    }
+
+    @Test
+    void nestedQueryAndMapRows() {
+        assert [3] == GQ {
+            from n in [1, 2, 3]
+            where n > (from m in [1, 2] select max(m))
+            select n
+        }.toList()
+
+        List<Map<String, Integer>> rows = [[a: 1, b: 10], [a: 3, b: 30]]
+        assert [30] == GQ {
+            from r in rows
+            where r.a > 1
+            select r.b
+        }.toList()
+    }
+
+    @Test
+    void queryInsideClosure() {
+        List<Integer> numbers = [1, 2, 3]
+        List found = []
+        numbers.each { Integer n ->
+            if (n == 1) {
+                found.addAll(GQ {
+                    from m in numbers
+                    where m >= n
+                    select m
+                }.toList())
+            }
+        }
+        assert [1, 2, 3] == found
+    }
+
+    @Test
+    void orderByDescendingAndMembership() {
+        assert [3, 2, 1] == GQ {
+            from n in [1, 2, 3]
+            orderby n in desc
+            select n
+        }.toList()
+
+        assert [1, 3] == GQ {
+            from n in [1, 2, 3]
+            where n in [1, 3]
+            select n
+        }.toList()
+    }
+
+    @Test
+    void parallelClauseKeepsRowValues() {
+        assert [3, 2, 1] == GQ(parallel: true) {
+            from n in [1, 2, 3]
+            orderby n in desc
+            select n
+        }.toList()
+    }
+
+    @Test
+    void staticMethodAndAnnotation() {
+        assert [1, 2, 3] == staticNumbers()
+        assert [2, 3] == above([1, 2, 3], 2).toList()
+        assert [3, 4] == listed(2)
+    }
+
+    @Test
+    void compiledClassInAScript() {
+        assertScript '''
+            import groovy.transform.CompileStatic
+
+            @CompileStatic
+            class Shop {
+                List names(List<String> items) {
+                    GQ {
+                        from item in items
+                        where item.size() > 3
+                        select item.toUpperCase()
+                    }.toList()
+                }
+            }
+
+            assert ['PEAR'] == new Shop().names(['pear', 'fig'])
+        '''
+    }
+
+    @Test
+    void customAggregateFromTheDsl() {
+        assertScript '''
+            def result = GQ {
+                from n in [1, 2, 3]
+                groupby n % 2 as k
+                select total(n)
+            }.toList()
+            assert result.toSet() == [new BigDecimal('4'), new 
BigDecimal('2')].toSet()

Review Comment:
   `total()` sums with `BigDecimal`, including for integral inputs, so the 
values are BigDecimals. The assertion is now `[4.0, 2.0].toSet()`.





> GINQ: support static compilation and a fluent query API
> -------------------------------------------------------
>
>                 Key: GROOVY-12724
>                 URL: https://issues.apache.org/jira/browse/GROOVY-12724
>             Project: Groovy
>          Issue Type: Improvement
>          Components: GINQ
>            Reporter: Daniel Sun
>            Assignee: Daniel Sun
>            Priority: Major
>
> Under @CompileStatic / @TypeChecked, GQ, GQL, and @GQ currently rely on 
> dynamic lowering. This improvement makes those entry points usable from a 
> statically checked scope while keeping the SQL-like syntax lowered to the 
> in-memory collection runtime.
> h3. Goals
> * Allow GQ / GQL / @GQ from @CompileStatic and @TypeChecked without forcing 
> the lowered tree through static checking at the expression site.
> * Defer pipeline construction with LazyQueryable until a terminal read 
> (toList, count, etc.), copying locals at the expression site (same binding as 
> dynamic GQ).
> * Add Ginq.from(...) as a deferred, generic fluent chain on the same runtime, 
> with @ClosureParams so where/select/join/aggregate closures can infer element 
> types where inference is sound.
> * Preserve default lazy/deferred evaluation and dynamic GQ behavior for map 
> rows and other intentionally dynamic cases.
> h3. Non-goals / known limits
> * Stored untyped Closure variables still need a SAM target type for parameter 
> inference (same as Java/C#).
> * Raw Query parameters/fields cannot recover a safe element type.
> * Names bound by from inside GQ / GQL / @GQ stay dynamic by design; callers 
> that need checked element types use Ginq.from.
> h3. Related work
> GitHub PR: https://github.com/apache/groovy/pull/3259
> Stacked on GEP-26 SQL window work (PR #3167 / gep26-sql-window).



--
This message was sent by Atlassian Jira
(v10.3.26#10030026)

Reply via email to