From a3985963f0df4fffa1a6d272c777f781ebd86d50 Mon Sep 17 00:00:00 2001
From: Aleksander Machniak <alec@alec.pl>
Date: Fri, 07 Dec 2012 06:38:08 -0500
Subject: [PATCH] Fix big memory consumption of DB layer (#1488856)

---
 CHANGELOG                          |    1 
 program/lib/Roundcube/rcube_db.php |  100 +++++++++++++++----------------------------------
 2 files changed, 32 insertions(+), 69 deletions(-)

diff --git a/CHANGELOG b/CHANGELOG
index 5a1b1ac..79f19b9 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,6 +1,7 @@
 CHANGELOG Roundcube Webmail
 ===========================
 
+- Fix big memory consumption of DB layer (#1488856)
 - Add workaround for IE<=8 bug where Content-Disposition:inline was ignored (#1488844)
 - Fix XSS vulnerability in vbscript: and data:text links handling (#1488850)
 - Fix broken message/part bodies when FETCH response contains more untagged lines (#1488836)
diff --git a/program/lib/Roundcube/rcube_db.php b/program/lib/Roundcube/rcube_db.php
index 5d8c4a5..2c471e7 100644
--- a/program/lib/Roundcube/rcube_db.php
+++ b/program/lib/Roundcube/rcube_db.php
@@ -37,12 +37,11 @@
     protected $db_mode;               // Connection mode
     protected $dbh;                   // Connection handle
 
-    protected $db_error        = false;
-    protected $db_error_msg    = '';
-    protected $conn_failure    = false;
-    protected $a_query_results = array('dummy');
-    protected $last_res_id     = 0;
-    protected $db_index        = 0;
+    protected $db_error     = false;
+    protected $db_error_msg = '';
+    protected $conn_failure = false;
+    protected $db_index     = 0;
+    protected $last_result;
     protected $tables;
     protected $variables;
 
@@ -267,14 +266,14 @@
     /**
      * Getter for error state
      *
-     * @param int $res_id Optional query result identifier
+     * @param mixed $result Optional query result
      *
      * @return string Error message
      */
-    public function is_error($res_id = null)
+    public function is_error($result = null)
     {
-        if ($res_id !== null) {
-            return $this->_get_result($res_id) === false ? $this->db_error_msg : null;
+        if ($result !== null) {
+            return $result === false ? $this->db_error_msg : null;
         }
 
         return $this->db_error ? $this->db_error_msg : null;
@@ -343,7 +342,7 @@
      * @param int    Number of rows for LIMIT statement
      * @param mixed  Values to be inserted in query
      *
-     * @return int Query handle identifier
+     * @return PDOStatement|bool Query handle or False on error
      */
     public function limitquery()
     {
@@ -363,7 +362,7 @@
      * @param int    $numrows Number of rows for LIMIT statement
      * @param array  $params  Values to be inserted in query
      *
-     * @return int Query handle identifier
+     * @return PDOStatement|bool Query handle or False on error
      */
     protected function _query($query, $offset, $numrows, $params)
     {
@@ -374,7 +373,7 @@
 
         // check connection before proceeding
         if (!$this->is_connected()) {
-            return null;
+            return $this->last_result = false;
         }
 
         if ($numrows || $offset) {
@@ -417,20 +416,21 @@
                 'message' => $this->db_error_msg), true, false);
         }
 
-        // add result, even if it's an error
-        return $this->_add_result($query);
+        $this->last_result = $query;
+
+        return $query;
     }
 
     /**
      * Get number of affected rows for the last query
      *
-     * @param  number $res_id Optional query handle identifier
+     * @param mixed $result Optional query handle
      *
      * @return int Number of rows or false on failure
      */
-    public function affected_rows($res_id = null)
+    public function affected_rows($result = null)
     {
-        if ($result = $this->_get_result($res_id)) {
+        if ($result || ($result === null && ($result = $this->last_result))) {
             return $result->rowCount();
         }
 
@@ -464,13 +464,12 @@
      * Get an associative array for one row
      * If no query handle is specified, the last query will be taken as reference
      *
-     * @param int $res_id Optional query handle identifier
+     * @param mixed $result Optional query handle
      *
      * @return mixed Array with col values or false on failure
      */
-    public function fetch_assoc($res_id = null)
+    public function fetch_assoc($result = null)
     {
-        $result = $this->_get_result($res_id);
         return $this->_fetch_row($result, PDO::FETCH_ASSOC);
     }
 
@@ -478,31 +477,30 @@
      * Get an index array for one row
      * If no query handle is specified, the last query will be taken as reference
      *
-     * @param int $res_id Optional query handle identifier
+     * @param mixed $result Optional query handle
      *
      * @return mixed Array with col values or false on failure
      */
-    public function fetch_array($res_id = null)
+    public function fetch_array($result = null)
     {
-        $result = $this->_get_result($res_id);
         return $this->_fetch_row($result, PDO::FETCH_NUM);
     }
 
     /**
      * Get col values for a result row
      *
-     * @param PDOStatement $result Result handle
-     * @param int          $mode   Fetch mode identifier
+     * @param mixed $result Optional query handle
+     * @param int   $mode   Fetch mode identifier
      *
      * @return mixed Array with col values or false on failure
      */
     protected function _fetch_row($result, $mode)
     {
-        if (!is_object($result) || !$this->is_connected()) {
-            return false;
+        if ($result || ($result === null && ($result = $this->last_result))) {
+            return $result->fetch($mode);
         }
 
-        return $result->fetch($mode);
+        return false;
     }
 
     /**
@@ -538,8 +536,8 @@
         if ($this->tables === null) {
             $q = $this->query('SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES ORDER BY TABLE_NAME');
 
-            if ($res = $this->_get_result($q)) {
-                $this->tables = $res->fetchAll(PDO::FETCH_COLUMN, 0);
+            if ($q) {
+                $this->tables = $q->fetchAll(PDO::FETCH_COLUMN, 0);
             }
             else {
                 $this->tables = array();
@@ -561,8 +559,8 @@
         $q = $this->query('SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = ?',
             array($table));
 
-        if ($res = $this->_get_result($q)) {
-            return $res->fetchAll(PDO::FETCH_COLUMN, 0);
+        if ($q) {
+            return $q->fetchAll(PDO::FETCH_COLUMN, 0);
         }
 
         return array();
@@ -774,42 +772,6 @@
         }
 
         return utf8_decode($input);
-    }
-
-    /**
-     * Adds a query result and returns a handle ID
-     *
-     * @param object $res Query handle
-     *
-     * @return int Handle ID
-     */
-    protected function _add_result($res)
-    {
-        $this->last_res_id = sizeof($this->a_query_results);
-        $this->a_query_results[$this->last_res_id] = $res;
-
-        return $this->last_res_id;
-    }
-
-    /**
-     * Resolves a given handle ID and returns the according query handle
-     * If no ID is specified, the last resource handle will be returned
-     *
-     * @param int $res_id Handle ID
-     *
-     * @return mixed Resource handle or false on failure
-     */
-    protected function _get_result($res_id = null)
-    {
-        if ($res_id == null) {
-            $res_id = $this->last_res_id;
-        }
-
-        if (!empty($this->a_query_results[$res_id])) {
-            return $this->a_query_results[$res_id];
-        }
-
-        return false;
     }
 
     /**

--
Gitblit v1.9.1