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


##########
grails-datamapping-core/src/main/groovy/org/grails/datastore/gorm/query/criteria/AbstractDetachedCriteria.groovy:
##########
@@ -1108,16 +1168,47 @@ abstract class AbstractDetachedCriteria<T> implements 
Criteria, Cloneable {
 
     protected abstract QueryableCriteria buildQueryableCriteria(Closure 
queryClosure)
 
+    /**
+     * Applies a pending {@code buildLazy} closure to this criteria, if any, 
and clears it. Used when this
+     * criteria is itself extended; a query reads a deferred criteria through 
{@link #materializeLazyCriteria()}.
+     */
     protected void applyLazyCriteria() {
         if (lazyQuery == null) {
             return
         }
 
         def criteria = lazyQuery
         lazyQuery = null
+        materializedTemplate = null

Review Comment:
   where { ne('name', 'Z') } after list() clears the template and runs the 
deferred closure again. Build the criteria with eq('name', wanted.name), run it 
while wanted.name is A, then set wanted.name to B and extend the criteria. The 
derived query returns B. The original query still returns A. Keep the 
restrictions from the first run and add the new one.



##########
grails-datamapping-core/src/main/groovy/org/grails/datastore/gorm/query/criteria/AbstractDetachedCriteria.groovy:
##########
@@ -1108,16 +1168,47 @@ abstract class AbstractDetachedCriteria<T> implements 
Criteria, Cloneable {
 
     protected abstract QueryableCriteria buildQueryableCriteria(Closure 
queryClosure)
 
+    /**
+     * Applies a pending {@code buildLazy} closure to this criteria, if any, 
and clears it. Used when this
+     * criteria is itself extended; a query reads a deferred criteria through 
{@link #materializeLazyCriteria()}.
+     */
     protected void applyLazyCriteria() {
         if (lazyQuery == null) {
             return
         }
 
         def criteria = lazyQuery
         lazyQuery = null
+        materializedTemplate = null
         this.with(criteria)
     }
 
+    /**
+     * Returns this criteria with its deferred query applied, without mutating 
it. The deferred closure runs
+     * once, on a private template that is never handed out; every call 
returns a fresh copy of that template, so
+     * a criteria shared between threads is never changed by a query that 
reads or extends the result.
+     * A criteria without a deferred query returns itself.
+     */
+    AbstractDetachedCriteria<T> materializeLazyCriteria() {
+        if (lazyQuery == null) {
+            return this
+        }
+        AbstractDetachedCriteria<T> template = materializedTemplate

Review Comment:
   The first list() stores a template, and a later list() returns a clone of 
that template. Add a restriction to an association that already exists, such as 
createAlias('organisation', 'o') and then eq('name', 'A') after the first 
execution. The second list() still returns the organisations that the first one 
returned. A projection, order, or alias set after the first execution is 
skipped the same way. Use the criteria as it is when the query runs. Do not run 
the deferred closure again.



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