jamesfredley commented on code in PR #16820:
URL: https://github.com/apache/grails-core/pull/16820#discussion_r4482774478


##########
grails-datamapping-core/src/main/groovy/org/grails/datastore/gorm/query/criteria/AbstractDetachedCriteria.groovy:
##########
@@ -871,21 +922,91 @@ abstract class AbstractDetachedCriteria<T> implements 
Criteria, Cloneable {
     @Override
     @CompileStatic
     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.@junctions = new ArrayList(this.junctions)
-        criteria.@connectionName = this.connectionName
-        criteria.@lazyQuery = this.lazyQuery
-        criteria.@associationCriteriaMap = new 
LinkedHashMap<>(this.associationCriteriaMap)
-        return criteria
+        // a copy carries the result of the deferred query, never the query: 
it runs once, here or earlier, and
+        // waits for a run on another thread, so that a copy is never half 
built
+        applyLazyCriteria()
+        synchronized (this) {
+            AbstractDetachedCriteria criteria = newInstance()
+            Map<DetachedAssociationCriteria, DetachedAssociationCriteria> 
associationCopies = new IdentityHashMap<>()
+            List<Query.Criterion> clonedCriteria = new 
ArrayList<>(this.criteria.size())
+            for (Query.Criterion criterion : this.criteria) {
+                clonedCriteria.add(copyCriterion(criterion, associationCopies))
+            }
+            criteria.@criteria = clonedCriteria
+            criteria.@alias = this.alias
+            final projections = new ArrayList(this.projections)
+            criteria.@projections = projections
+            criteria.projectionList = criteria.newProjectionList(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.@junctions = new ArrayList(this.junctions)
+            criteria.@connectionName = this.connectionName
+            criteria.@lazyQuery = this.lazyQuery

Review Comment:
   buildLazy { sort('name') } clones the criteria while the deferred closure is 
still running. The clone copies that closure and has no guard, so sort reads 
the orders and runs the closure again, until the query throws 
StackOverflowError. A clone made during that run must not carry the running 
closure.



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