jamesfredley opened a new issue, #15422:
URL: https://github.com/apache/grails-core/issues/15422

   ## Description
   
   `AbstractDetachedCriteria.clone()` copies 8 fields (criteria, projections, 
projectionList, orders, defaultMax, defaultOffset, fetchStrategies, joinTypes) 
but omits 4 others: `connectionName`, `alias`, `lazyQuery`, and 
`associationCriteriaMap`.
   
   This causes `withConnection()` settings to be silently lost whenever a 
chained method calls `clone()` internally, because the cloned instance reverts 
`connectionName` to `ConnectionSource.DEFAULT`.
   
   ## Affected Methods
   
   Any method that calls `clone()` internally loses the connection setting:
   - `where()` / `whereLazy()` (line 812-825)
   - `max()` (line 895-898)
   - `offset()` (line 907-910)
   - `sort()` (line 919+)
   
   ## Steps to Reproduce
   
   **Reproducer app:** 
https://github.com/jamesfredley/grails-detachedcriteria-clone-connection
   
   ```bash
   git clone 
https://github.com/jamesfredley/grails-detachedcriteria-clone-connection.git
   cd grails-detachedcriteria-clone-connection
   ./gradlew integrationTest
   ```
   
   ### Minimal code example
   
   ```groovy
   // withConnection('secondary') sets connectionName on the returned criteria
   DetachedCriteria<Product> criteria = new DetachedCriteria<>(Product)
       .withConnection('secondary')   // clone() + set connectionName = 
'secondary'
       .where { price > 100.0 }       // clone() again -- connectionName lost!
   
   // Query runs against DEFAULT datasource instead of secondary
   criteria.list()
   ```
   
   ### Expected Behavior
   
   After `withConnection('secondary').where { ... }`, the resulting criteria 
should still have `connectionName == 'secondary'` and queries should route to 
the secondary datasource.
   
   ### Actual Behavior
   
   `where()` internally calls `clone()` which creates a new instance without 
copying `connectionName`. The resulting criteria has `connectionName == 
'DEFAULT'`, so queries silently route to the wrong datasource.
   
   ## Root Cause
   
   In [`AbstractDetachedCriteria.groovy` lines 
873-885](https://github.com/apache/grails-core/blob/7.0.x/grails-datamapping-core/src/main/groovy/org/grails/datastore/gorm/query/criteria/AbstractDetachedCriteria.groovy#L873-L885):
   
   ```groovy
   protected AbstractDetachedCriteria<T> clone() {
       AbstractDetachedCriteria criteria = newInstance()
       criteria.@criteria = new ArrayList(this.criteria)
       final projections = new ArrayList(this.projections)
       criteria.@projections = projections
       criteria.projectionList = new DetachedProjections(projections)
       criteria.@orders = new ArrayList(this.orders)
       criteria.defaultMax = defaultMax
       criteria.defaultOffset = defaultOffset
       criteria.@fetchStrategies = new HashMap<>(this.fetchStrategies)
       criteria.@joinTypes = new HashMap<>(this.joinTypes)
       return criteria
       // Missing: connectionName, alias, lazyQuery, associationCriteriaMap
   }
   ```
   
   The `withConnection()` method (line 865-869) works correctly in isolation - 
it calls `clone()` then sets `connectionName`. But any subsequent chained 
method that also calls `clone()` creates a fresh copy without the connection 
setting.
   
   ## Suggested Fix
   
   Add the missing field copies to `clone()`:
   
   ```groovy
   protected AbstractDetachedCriteria<T> clone() {
       AbstractDetachedCriteria criteria = newInstance()
       criteria.@criteria = new ArrayList(this.criteria)
       final projections = new ArrayList(this.projections)
       criteria.@projections = projections
       criteria.projectionList = new DetachedProjections(projections)
       criteria.@orders = new ArrayList(this.orders)
       criteria.defaultMax = defaultMax
       criteria.defaultOffset = defaultOffset
       criteria.@fetchStrategies = new HashMap<>(this.fetchStrategies)
       criteria.@joinTypes = new HashMap<>(this.joinTypes)
       criteria.connectionName = this.connectionName
       criteria.alias = this.alias
       criteria.lazyQuery = this.lazyQuery
       criteria.@associationCriteriaMap = new 
HashMap<>(this.associationCriteriaMap)
       return criteria
   }
   ```
   
   ## Environment
   
   - Grails: 7.0.7
   - GORM Hibernate: via grails-data-hibernate5
   - JDK: 17
   
   ## Related
   
   - #15416 - `@Where` connection routing fix (separate issue, same area of 
code)
   - PR #15418 - Fix for #15416


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