Paul King created GROOVY-12113:
----------------------------------

             Summary:  GINQ roadmap: SQL backend (GEP-26), GEP-18 concurrency 
alignment, query continuation, and async sources
                 Key: GROOVY-12113
                 URL: https://issues.apache.org/jira/browse/GROOVY-12113
             Project: Groovy
          Issue Type: Umbrella
            Reporter: Paul King


h2. Overview

Umbrella tracking the remaining GINQ (Groovy-Integrated Query) roadmap. GINQ's 
SQL-like
surface and pluggable code-generation seam make several enhancements natural; 
this issue
collects the outstanding workstreams in one place. Detailed designs are linked 
where they
have their own proposals.

Already shipped (context):
* (/) [GROOVY-11915] -- {{groupby...into}} redesign with first-class 
{{GroupResult<K,T>}} group variables (the foundation piece for both the SQL 
backend and parallel improvements)
* (/) Set operations in the DSL -- {{union}}, {{unionall}}, {{intersect}}, 
{{minus}} with left-associative chaining
* (/) GEP-18 -- Integrated Concurrency and Parallel Processing: the bundled 
core runtime GINQ will align to ({{groovy.concurrent.Pool}} / 
{{ParallelScope}}, async/await, channels, {{ParallelCollectionExtensions}}, 
{{Cancellable}})

h2. Workstreams

h3. 1. SQL backend (native + JOOQ) -- specified in GEP-26

Execute an *unchanged* GINQ query against a relational database, pushing 
filtering, joining,
grouping, and aggregation down to the database. Delivered as two providers over 
one shared
front-end:
* *Native SQL provider* -- dependency-free, ANSI SQL via {{groovy.sql.Sql}}, 
fully ASL2
* *JOOQ provider* -- optional, dialect-aware, window functions, via JOOQ's 
plain-SQL DSL

Both consume a shared, provider-independent {{SqlQuery}} intermediate model 
produced once from
the GINQ AST. A new {{provider}} config key ({{'native-sql'}} / {{'jooq-sql'}} 
/ {{'sql'}} auto)
selects the backend, sugar over the existing {{astWalker}} hook. New optional 
{{groovy-ginq-sql}}
module. Estimated ~2,400-3,650 lines across five phases.

Full specification: *GEP-26 -- GINQ SQL Backend (Native and JOOQ Providers)*
(https://groovy.apache.org/wiki/GEP-26.html), target Groovy 7.0.

Phases:
# Shared walker + {{SqlQuery}} IR + native provider (single-table 
SELECT/FROM/WHERE/ORDER BY/LIMIT)
# Joins + GROUP BY/aggregates + set operations (native)
# JOOQ provider (reuses the shared IR -> dialects + window functions)
# Subqueries, IN-subquery, EXISTS
# Mixed-mode fallback, function mapping, polish

h3. 2. GINQ <-> GEP-18 concurrency alignment

GINQ's parallel support ({{@GQ(parallel: true)}}) predates GEP-18 and uses a 
private
{{QueryableHelper.ThreadPoolHolder}} ({{ginq-fj-thread}} {{ForkJoinPool}} +
{{-Dgroovy.ginq.parallelism}} + a separate virtual-thread executor). GEP-18 now 
bundles a
general {{Pool}} / {{ParallelScope}} runtime, so GINQ's parallelism should 
become a thin adapter
over it rather than a bespoke pool.

Scope:
* Replace {{ThreadPoolHolder}} with pool resolution: explicit {{poolSize:}} -> 
{{Pool.current()}} (an enclosing {{ParallelScope.withPool}}) -> 
{{ConcurrentConfig.getDefaultPool()}}
* Use the resolved pool's {{asForkJoinPool()}} for the parallel-stream work; 
surface {{poolSize:}} and {{ParallelScope.withPool}} composition
* Unify config on {{groovy.concurrent.poolsize}} (keep 
{{groovy.ginq.parallelism}} as a deprecated override one release)
* Drop bespoke pool shutdown for shared/borrowed pools; only close pools GINQ 
itself creates
* Wire cooperative cancellation via {{Cancellable}}; consider an 
ordered-parallel mode

The three subtleties that are the real work (not the wiring):
# *Virtual pools can't back parallel streams* -- {{asForkJoinPool()}} throws on 
a virtual pool; GINQ must resolve a FJP-backed pool ({{cpu()}}/{{fixed}}) for 
stream/groupby work and use a virtual/IO pool only for the {{supplyAsync}} 
transform path. (Decision: what to do if a user binds a virtual pool.)
# *Scoped binding vs InheritableThreadLocal* -- {{Pool.current()}} is 
scope-bound while GINQ's {{isParallel()}} flag is an 
{{InheritableThreadLocal}}; capture the pool before {{submit}} and re-establish 
it on the worker via {{Pool.withCurrent(...)}}.
# *Nested-parallelism deadlock* -- {{QueryableCollection}} already 
force-materializes a hash table to avoid nested-parallel deadlock; verify the 
guard still holds when a single bounded pool is shared across nested queries.

Files: {{QueryableHelper.groovy}}, {{GinqAstWalker.groovy}}, 
{{QueryableCollection.java}},
{{GinqGroovyMethods.groovy}}, optionally {{GQ.java}} (additive {{poolSize}} 
member).
Effort: Medium, ~300-600 lines + tests. Best tracked as a minor-GEP integration 
aligned to GEP-18.

h3. 3. Query continuation

Allow {{where}}/{{orderby}}/etc. *after* {{groupby...into g}} (operating on 
{{g}} as a new data
source), matching C# LINQ's {{into}} continuation. Currently {{where}} after 
{{groupby...into}}
is rejected ("reserved for future use"); users must use {{having}}. Requires a 
chainable/nested
{{GinqExpression}} model (a {{GinqExpression}} containing a 
sub-{{GinqExpression}}). Could
piggy-back on a general language continuations feature. Effort: High; 
aspirational LINQ parity.

h3. 4. Async streaming data sources (GEP-18 addendum)

Awaitable GINQ results already work today (wrap {{GQL{...}}} in {{async { 
}}}}). The future piece
is querying over asynchronous streams -- {{from e in someAsyncChannel}} with a 
lazy
{{AsyncQueryable}} pulling from {{groovy.concurrent.AsyncChannel}} (with 
backpressure) consumed via
{{for await}}. Now buildable against the *merged* GEP-18 primitives, so best 
handled as a small
GEP-18 addendum rather than a standalone GEP. Effort: High.

h2. Suggested sequencing

|| # || Item || Effort || Depends on ||
| 1 | SQL backend Phase 1 (GEP-26) -- shared walker + native, single-table | 
Medium | -- |
| 2 | SQL backend Phase 2 (GEP-26) -- joins + GROUP BY + set ops, native | 
Medium | Phase 1 |
| 3 | SQL backend Phase 3 (GEP-26) -- JOOQ provider | Medium | Phase 1 |
| 4 | GINQ <-> GEP-18 alignment -- pool resolution, poolSize, withPool | Medium 
| GEP-18 (done) |
| 5 | Cooperative cancellation + ordered parallel | Medium | #4 |
| 6 | SQL backend Phases 4-5 (GEP-26) -- subqueries, mixed-mode, functions | 
Medium-High | Phase 2 |
| 7 | Query continuation | High | continuations (beneficial, not required) |
| 8 | Async streaming data sources (GEP-18 addendum) | High | for await over 
AsyncChannel |

SQL phases 2 and 3 depend only on Phase 1 (not each other) and can proceed in 
parallel. The
GEP-18 alignment (#4) is independent of the SQL work and can proceed at any 
time.

h2. External dependencies

|| Feature || Dependency || License || Required? ||
| Native SQL provider (GEP-26) | none (uses groovy-sql) | ASL2 | built-in |
| JOOQ SQL provider (GEP-26) | JOOQ OSS edition | ASL2 (OSS DBs), commercial 
(proprietary DBs) | optional, user-supplied |
| Parallel / async runtime | groovy.concurrent (GEP-18) | ASL2 | bundled in 
core |
| Async data sources | GEP-18 AsyncChannel / for await | ASL2 | bundled in core 
|
| SQL backend tests | H2 Database | EPL/MPL | test-only |

h2. References

* GEP-26: GINQ SQL Backend (Native and JOOQ Providers) -- 
https://groovy.apache.org/wiki/GEP-26.html
* GEP-18: Integrated Concurrency and Parallel Processing -- 
https://groovy.apache.org/wiki/GEP-18.html
* [GROOVY-11915] -- groupby...into redesign
* [GROOVY-8258] -- original GINQ proposal
* [GROOVY-12042] -- graduate groovy-ginq from incubating to stable (the 
parallel/provider config keys should stay open to additive growth)




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

Reply via email to