From 8dc4f37917ac9a7685c6d10c19bf97b825270eb7 Mon Sep 17 00:00:00 2001
From: Jared Hancock <jared@osticket.com>
Date: Tue, 10 Jun 2014 16:26:18 -0500
Subject: [PATCH] orm: Fix issues surrounding MySQL commands OoS

Several places in the code initialize a list of objects from the database
and only fetch one item. In certain instances (which seem almost like a race
condition), MySQL will feel like there are more records available in the
database and will complain with "Commands out of sync, you can't run the
command now".

This patch addresses the issue by utilizing the ::one() method of the
QuerySet where only one record is expected. The ::one() method is further
designed to fetch all one results (which satisfies the MySQL client library)
and return the first item.
---
 include/class.dynamic_forms.php | 13 +++++++------
 include/class.orm.php           | 22 ++++++++++++++--------
 2 files changed, 21 insertions(+), 14 deletions(-)

diff --git a/include/class.dynamic_forms.php b/include/class.dynamic_forms.php
index 46c8460ef..8dfbb94c4 100644
--- a/include/class.dynamic_forms.php
+++ b/include/class.dynamic_forms.php
@@ -220,8 +220,7 @@ class UserForm extends DynamicForm {
 
     static function getUserForm() {
         if (!isset(static::$form)) {
-            $o = static::objects();
-            static::$form = $o[0];
+            static::$form = static::objects()->one();
         }
         return static::$form;
     }
@@ -233,8 +232,8 @@ class UserForm extends DynamicForm {
     }
 
     static function getNewInstance() {
-        $o = static::objects();
-        static::$instance = $o[0]->instanciate();
+        $o = static::objects()->one();
+        static::$instance = $o->instanciate();
         return static::$instance;
     }
 }
@@ -263,8 +262,8 @@ class TicketForm extends DynamicForm {
     }
 
     static function getNewInstance() {
-        $o = static::objects();
-        static::$instance = $o[0]->instanciate();
+        $o = static::objects()->one();
+        static::$instance = $o->instanciate();
         return static::$instance;
     }
 
@@ -1094,6 +1093,8 @@ class SelectionField extends FormField {
     }
 
     function to_php($value, $id=false) {
+        if ($value === null && $id === null)
+            return null;
         if ($id && is_int($id))
             $item = DynamicListItem::lookup($id);
         # Attempt item lookup by name too
diff --git a/include/class.orm.php b/include/class.orm.php
index b4f65e551..1d8810919 100644
--- a/include/class.orm.php
+++ b/include/class.orm.php
@@ -167,9 +167,7 @@ class VerySimpleModel {
         if (!is_array($criteria))
             // Model::lookup(1), where >1< is the pk value
             $criteria = array(static::$meta['pk'][0] => $criteria);
-        $list = static::objects()->filter($criteria)->limit(1);
-        // TODO: Throw error if more than one result from database
-        return $list[0];
+        return static::objects()->filter($criteria)->one();
     }
 
     function delete($pk=false) {
@@ -333,7 +331,8 @@ class QuerySet implements IteratorAggregate, ArrayAccess {
     }
 
     function one() {
-        $this->limit(1);
+        $list = $this->limit(1)->all();
+        // TODO: Throw error if more than one result from database
         return $this[0];
     }
 
@@ -987,7 +986,8 @@ class MysqlExecutor {
     function _prepare() {
         $this->execute();
         $this->_setup_output();
-        $this->stmt->store_result();
+        if (!$this->stmt->store_result())
+            throw new OrmException('Unable to process query: '.$this->stmt->error);
     }
 
     function execute() {
@@ -1024,9 +1024,11 @@ class MysqlExecutor {
     }
 
     function _setup_output() {
-        $meta = $this->stmt->result_metadata();
+        if (!($meta = $this->stmt->result_metadata()))
+            throw new OrmException('Unable to fetch statment metadata: ', $this->stmt->error);
         while ($f = $meta->fetch_field())
             $this->fields[] = $f;
+        $meta->free_result();
     }
 
     // Iterator interface
@@ -1057,7 +1059,9 @@ class MysqlExecutor {
         foreach ($this->fields as $f)
             $variables[] = &$output[$f->name]; // pass by reference
 
-        call_user_func_array(array($this->stmt, 'bind_result'), $variables);
+        if (!call_user_func_array(array($this->stmt, 'bind_result'), $variables))
+            throw new OrmException('Unable to bind result: ' . $this->stmt->error);
+
         if (!$this->next())
             return false;
         return $output;
@@ -1073,7 +1077,9 @@ class MysqlExecutor {
         foreach ($this->fields as $f)
             $variables[] = &$output[]; // pass by reference
 
-        call_user_func_array(array($this->stmt, 'bind_result'), $variables);
+        if (!call_user_func_array(array($this->stmt, 'bind_result'), $variables))
+            throw new OrmException('Unable to bind result: ' . $this->stmt->error);
+
         if (!$this->next())
             return false;
         return $output;
-- 
GitLab