From 2391a45a9f254235d9c0107a6bfb51644ee3de75 Mon Sep 17 00:00:00 2001
From: Jared Hancock <jared@osticket.com>
Date: Sat, 21 May 2016 06:58:21 -0500
Subject: [PATCH] export: Fix crash on export, fix select_related

This originates from an ORM issue, where the select_related in a queryset
was not honored by the compiler. Therefore, the query would not include all
the fields requested and would require extra trips to the database for some
queries.

This is especially important for unbuffered queries, such as those used in
the export process, which be fetched in parallel with other queries.
---
 include/class.orm.php    | 19 +++++++++++++++++--
 include/class.ticket.php | 20 +++++++++++---------
 include/mysqli.php       |  8 --------
 3 files changed, 28 insertions(+), 19 deletions(-)

diff --git a/include/class.orm.php b/include/class.orm.php
index 7756fec73..ae82a7911 100644
--- a/include/class.orm.php
+++ b/include/class.orm.php
@@ -1237,8 +1237,23 @@ class QuerySet implements IteratorAggregate, ArrayAccess, Serializable, Countabl
         return $this->limit || $this->offset || (count($this->values) + count($this->annotations) + @count($this->extra['select'])) > 1;
     }
 
+    /**
+     * Fetch related fields with the query. This will result in better
+     * performance as related items are fetched with the root model with
+     * only one trip to the database.
+     *
+     * Either an array of fields can be sent as one argument, or the list of
+     * fields can be sent as the arguments to the function.
+     *
+     * Example:
+     * >>> $q = User::objects()->select_related('role');
+     */
     function select_related() {
-        $this->related = array_merge($this->related, func_get_args());
+        $args = func_get_args();
+        if (is_array($args[0]))
+            $args = $args[0];
+
+        $this->related = array_merge($this->related, $args);
         return $this;
     }
 
@@ -1557,7 +1572,7 @@ class QuerySet implements IteratorAggregate, ArrayAccess, Serializable, Countabl
             $query->ordering = array();
         elseif (!$query->ordering && $meta['ordering'])
             $query->ordering = $meta['ordering'];
-        if (false !== $query->related && !$query->values && $meta['select_related'])
+        if (false !== $query->related && !$query->related && !$query->values && $meta['select_related'])
             $query->related = $meta['select_related'];
         if (!$query->defer && $meta['defer'])
             $query->defer = $meta['defer'];
diff --git a/include/class.ticket.php b/include/class.ticket.php
index e08fab62d..570b1eeaf 100644
--- a/include/class.ticket.php
+++ b/include/class.ticket.php
@@ -167,10 +167,6 @@ implements RestrictedAccess, Threadable, Searchable {
     var $recipients;
     var $lastrespondent;
 
-    function __onload() {
-        $this->loadDynamicData();
-    }
-
     function loadDynamicData($force=false) {
         if (!isset($this->_answers) || $force) {
             $this->_answers = array();
@@ -188,6 +184,12 @@ implements RestrictedAccess, Threadable, Searchable {
         return $this->_answers;
     }
 
+    function getAnswer($field, $form=null) {
+        // TODO: Prefer CDATA ORM relationship if already loaded
+        $this->loadDynamicData();
+        return $this->_answers[$field];
+    }
+
     function getId() {
         return $this->ticket_id;
     }
@@ -363,7 +365,7 @@ implements RestrictedAccess, Threadable, Searchable {
     }
 
     function getSubject() {
-        return (string) $this->_answers['subject'];
+        return (string) $this->getAnswer('subject');
     }
 
     /* Help topic title  - NOT object -> $topic */
@@ -464,7 +466,7 @@ implements RestrictedAccess, Threadable, Searchable {
     function getPriorityId() {
         global $cfg;
 
-        if (($a = $this->_answers['priority'])
+        if (($a = $this->getAnswer('priority'))
             && ($b = $a->getValue())
         ) {
             return $b->getId();
@@ -473,7 +475,7 @@ implements RestrictedAccess, Threadable, Searchable {
     }
 
     function getPriority() {
-        if (($a = $this->_answers['priority']) && ($b = $a->getValue()))
+        if (($a = $this->getAnswer('priority')) && ($b = $a->getValue()))
             return $b->getDesc();
         return '';
     }
@@ -1790,11 +1792,11 @@ implements RestrictedAccess, Threadable, Searchable {
         case 'user':
             return $this->getOwner();
         default:
-            if (isset($this->_answers[$tag]))
+            if ($a = $this->getAnswer($tag))
                 // The answer object is retrieved here which will
                 // automatically invoke the toString() method when the
                 // answer is coerced into text
-                return $this->_answers[$tag];
+                return $a;
         }
     }
 
diff --git a/include/mysqli.php b/include/mysqli.php
index 41e7f1fe0..998d28659 100644
--- a/include/mysqli.php
+++ b/include/mysqli.php
@@ -181,11 +181,6 @@ function db_create_database($database, $charset='utf8',
 function db_query($query, $logError=true, $buffered=true) {
     global $ost, $__db;
 
-    if ($__db->unbuffered_result) {
-        $__db->unbuffered_result->free();
-        $__db->unbuffered_result = false;
-    }
-
     $tries = 3;
     do {
         $res = $__db->query($query,
@@ -205,9 +200,6 @@ function db_query($query, $logError=true, $buffered=true) {
         //echo $msg; #uncomment during debuging or dev.
     }
 
-    if (is_object($res) && !$buffered)
-        $__db->unbuffered_result = $res;
-
     return $res;
 }
 
-- 
GitLab