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]
