From 0f9cab068c3e527e43a801de65ad023e004e7718 Mon Sep 17 00:00:00 2001
From: Jared Hancock <gravydish@gmail.com>
Date: Wed, 18 Apr 2018 20:12:37 -0500
Subject: [PATCH] queryset: Fix circular reference error

This fixes an error where the ModelInstanceManager maintained a reference to
the QuerySet instance, and the QuerySet instance managed a reference to the
ModelInstanceManager instance (if it's the iterator for the query). Because
of the circular reference, if the iterator is not exhausted, then the
resource is not closed and the query remains open. This wastes memory and
prevents some other queries from running after such a situation happens.

This addresses the issue by removing the circular reference between the
QuerySet and the ModelInstanceManager.
---
 include/class.orm.php | 19 +++++++++++--------
 1 file changed, 11 insertions(+), 8 deletions(-)

diff --git a/include/class.orm.php b/include/class.orm.php
index 7539c1445..74ecc322a 100644
--- a/include/class.orm.php
+++ b/include/class.orm.php
@@ -1606,15 +1606,22 @@ extends CachedResultSet {
 
 class ModelInstanceManager
 implements IteratorAggregate {
-    var $queryset;
     var $model;
     var $map;
+    var $resource;
+    var $annnotations;
+    var $defer;
 
     static $objectCache = array();
 
     function __construct(QuerySet $queryset) {
-        $this->queryset = $queryset;
         $this->model = $queryset->model;
+        $this->resource = $queryset->getQuery();
+        $cache = !$queryset->hasOption(QuerySet::OPT_NOCACHE);
+        $this->resource->setBuffered($cache);
+        $this->map = $this->resource->getMap();
+        $this->annotations = $queryset->annotations;
+        $this->defer = $queryset->defer;
     }
 
     function cache($model) {
@@ -1669,7 +1676,7 @@ implements IteratorAggregate {
                 return null;
             }
         }
-        $annotations = $this->queryset->annotations;
+        $annotations = $this->annotations;
         $extras = array();
         // For annotations, drop them from the $fields list and add them to
         // an $extras list. The fields passed to the root model should only
@@ -1688,7 +1695,7 @@ implements IteratorAggregate {
             // Construct and cache the object
             $m = $modelClass::$meta->newInstance($fields);
             // XXX: defer may refer to fields not in this model
-            $m->__deferred__ = $this->queryset->defer;
+            $m->__deferred__ = $this->defer;
             $m->__onload();
             if ($cache)
                 $this->cache($m);
@@ -1758,10 +1765,6 @@ implements IteratorAggregate {
     }
 
     function getIterator() {
-        $this->resource = $this->queryset->getQuery();
-        $this->map = $this->resource->getMap();
-        $cache = !$this->queryset->hasOption(QuerySet::OPT_NOCACHE);
-        $this->resource->setBuffered($cache);
         $func = ($this->map) ? 'getRow' : 'getArray';
         $func = array($this->resource, $func);
 
-- 
GitLab