Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

[ZF2-281] && [ZF2-262] Cannot Count rows in a MySQLi Result Set /

Zend\Db\Adapter\Driver\Mysqli\Result Iterator implementation mangled

Cleaned both Result.php and ResultSet.php and made them compliant with
the Iterator interface

1) fixed the count() issue
2) fixed the 2+ next() issue (2 or more next() did not advance mysqli
pointer)
3) fixed the issue with rewind() and mysqli losing fetched data, by
buffering mysqli results
4) simplified Result.php by eliminating the currentComplete and
nextComplete, both aggregated into currentData = null | false | array
5) removed caching of count() in ResultSet.php (row count is
already stored in Result.php)
6) fixed a issue with the logic of pointer (pointer can be changed only
by next() and rewind(), and should not be touched by current())



Change-Id: I5bf38cd6adb5330deff0400428db3d6ded651efc
  • Loading branch information...
commit d8027ac9958eaf67d9cf056e0987d3c9f91a54f8 1 parent e9f287f
authored May 12, 2012
169  library/Zend/Db/Adapter/Driver/Mysqli/Result.php
@@ -54,6 +54,11 @@ class Result implements \Iterator, ResultInterface
54 54
      */
55 55
     protected $resource = null;
56 56
 
  57
+    /**
  58
+     * @var boolean
  59
+     */
  60
+    protected $isResultStored = false;
  61
+    
57 62
     /**
58 63
      * Cursor position
59 64
      * @var int
@@ -64,24 +69,25 @@ class Result implements \Iterator, ResultInterface
64 69
      * Number of known rows
65 70
      * @var int
66 71
      */
67  
-    protected $numberOfRows = -1;
68  
-
69  
-    /**
70  
-     * Is the current() operation already complete for this pointer position?
71  
-     * @var bool
72  
-     */
73  
-    protected $currentComplete = false;
74  
-
75  
-    /**
76  
-     *
77  
-     * @var bool
78  
-     */
79  
-    protected $nextComplete = false;
  72
+    protected $numberOfRows = 0;
  73
+
  74
+    /**
  75
+     * Dataset has been looped, or store_result() has been called
  76
+     * @var boolean
  77
+     */
  78
+    protected $isNumberOfRowsFinal = false;
  79
+    
80 80
     /**
81 81
      *
82  
-     * @var bool
  82
+     * @var null|false|array
83 83
      */
84  
-    protected $currentData = false;
  84
+    protected $currentData = null;
  85
+    
  86
+    /**
  87
+     * Stores data not available anumore on mysqli
  88
+     * @var array
  89
+     */
  90
+    protected $dataBuffer = array();
85 91
     
86 92
     /**
87 93
      *
@@ -156,6 +162,7 @@ public function getAffectedRows()
156 162
             return $this->resource->num_rows;
157 163
         }
158 164
     }
  165
+    
159 166
     /**
160 167
      * Current
161 168
      * 
@@ -163,17 +170,16 @@ public function getAffectedRows()
163 170
      */
164 171
     public function current()
165 172
     {
166  
-        if ($this->currentComplete) {
167  
-            return $this->currentData;
  173
+        if (is_null($this->currentData)) {
  174
+            //Load current row
  175
+            if ($this->mode == self::MODE_STATEMENT) {
  176
+                $this->loadDataFromMysqliStatement();
  177
+            } else {
  178
+                $this->loadFromMysqliResult();
  179
+            }
168 180
         }
169 181
         
170  
-        if ($this->mode == self::MODE_STATEMENT) {
171  
-            $this->loadDataFromMysqliStatement();
172  
-            return $this->currentData;
173  
-        } else {
174  
-            $this->loadFromMysqliResult();
175  
-            return $this->currentData;
176  
-        }
  182
+        return $this->currentData;
177 183
     }
178 184
     
179 185
     /**
@@ -188,7 +194,12 @@ public function current()
188 194
      */
189 195
     protected function loadDataFromMysqliStatement()
190 196
     {
191  
-        $data = null;
  197
+
  198
+        if (isset($this->dataBuffer[$this->position])) {
  199
+            $this->currentData  =  $this->dataBuffer[$this->position];
  200
+            return $this->dataBuffer[$this->position];
  201
+        }
  202
+        
192 203
         // build the default reference based bind strutcure, if it does not already exist
193 204
         if ($this->statementBindValues['keys'] === null) {
194 205
             $this->statementBindValues['keys'] = array();
@@ -205,7 +216,12 @@ protected function loadDataFromMysqliStatement()
205 216
         }
206 217
         
207 218
         if (($r = $this->resource->fetch()) === null) {
  219
+            
  220
+            $this->isNumberOfRowsFinal           = true;
  221
+            $this->currentData                   = false;
  222
+            $this->dataBuffer[$this->position]   = false; 
208 223
             return false;
  224
+            
209 225
         } elseif ($r === false) {
210 226
             throw new \RuntimeException($this->resource->error);
211 227
         }
@@ -214,11 +230,17 @@ protected function loadDataFromMysqliStatement()
214 230
         for ($i = 0; $i < count($this->statementBindValues['keys']); $i++) {
215 231
             $this->currentData[$this->statementBindValues['keys'][$i]] = $this->statementBindValues['values'][$i];
216 232
         }
217  
-        $this->currentComplete = true;
218  
-        $this->nextComplete = true;
219  
-        $this->position++;
  233
+
  234
+        $this->dataBuffer[$this->position] = $this->currentData; 
  235
+        
  236
+        //Data buffering prevents from double counting
  237
+        if (!$this->isNumberOfRowsFinal) {
  238
+            $this->numberOfRows++;
  239
+        }
  240
+        
220 241
         return true;
221 242
     }
  243
+    
222 244
     /**
223 245
      * Load from mysqli result
224 246
      * 
@@ -226,32 +248,47 @@ protected function loadDataFromMysqliStatement()
226 248
      */
227 249
     protected function loadFromMysqliResult()
228 250
     {
229  
-        $this->currentData = null;
  251
+
  252
+        if (isset($this->dataBuffer[$this->position])) {
  253
+            $this->currentData  =  $this->dataBuffer[$this->position];
  254
+            return $this->dataBuffer[$this->position];
  255
+        }
230 256
         
231 257
         if (($data = $this->resource->fetch_assoc()) === null) {
  258
+            $this->isNumberOfRowsFinal = true;
  259
+            $this->currentData = false;
232 260
             return false;
233 261
         }
234 262
         
235  
-        $this->position++;
236  
-        $this->currentData = $data;
237  
-        $this->currentComplete = true;
238  
-        $this->nextComplete = true;
239  
-        $this->position++;
  263
+        $this->currentData                 = $data;
  264
+        $this->dataBuffer[$this->position] = $this->currentData;
  265
+        
  266
+        //Data buffering prevents from double counting
  267
+        if (!$this->isNumberOfRowsFinal) {
  268
+            $this->numberOfRows++;
  269
+        }
  270
+        
240 271
         return true;
241 272
     }
  273
+    
242 274
     /**
243 275
      * Next
244 276
      */
245 277
     public function next()
246 278
     {
247  
-        $this->currentComplete = false;
248  
-        
249  
-        if ($this->nextComplete == false) {
250  
-            $this->position++;
  279
+        if (is_null($this->currentData)) {
  280
+            //Called first, or called 2+ next() in sequence. Need to advance the pointer of mysqli
  281
+            $this->current();
251 282
         }
252 283
         
253  
-        $this->nextComplete = false;
  284
+        //If false do nothing, rows are finished and it cannot advance
  285
+        if (!is_null($this->currentData) && $this->currentData !== false) {
  286
+            $this->currentData = null;
  287
+            $this->position ++;
  288
+        }
  289
+ 
254 290
     }
  291
+    
255 292
     /**
256 293
      * Key
257 294
      * 
@@ -261,20 +298,32 @@ public function key()
261 298
     {
262 299
         return $this->position;
263 300
     }
  301
+    
264 302
     /**
265 303
      * Rewind
266 304
      * 
267 305
      */
268 306
     public function rewind()
269 307
     {
270  
-        $this->currentComplete = false;
  308
+        if ($this->position == 0)
  309
+            return;
  310
+        
  311
+        /* 
  312
+        //Buffering makes this useless, and it didn't work unless results are stored as first step!
  313
+        if ($this->resource instanceof \mysqli_stmt && !$this->isResultStored) {
  314
+            $this->resource->store_result(); //Required for data_seek to work
  315
+            $this->isResultStored = true;
  316
+        }    
  317
+        
  318
+        //Works with \mysqli_result only if the results are buffered
  319
+        $this->resource->data_seek(0);
  320
+
  321
+        */
  322
+        
271 323
         $this->position = 0;
272  
-        if ($this->resource instanceof \mysqli_stmt) {
273  
-            //$this->resource->reset();
274  
-        } else {
275  
-            $this->resource->data_seek(0); // works for both mysqli_result & mysqli_stmt
276  
-        }
  324
+        $this->currentData = null;
277 325
     }
  326
+    
278 327
     /**
279 328
      * Valid
280 329
      * 
@@ -282,16 +331,13 @@ public function rewind()
282 331
      */
283 332
     public function valid()
284 333
     {
285  
-        if ($this->currentComplete) {
286  
-            return true;
287  
-        }
  334
+        if (is_null($this->currentData))
  335
+            $this->current();
  336
+        
  337
+        return !empty($this->currentData);
288 338
         
289  
-        if ($this->mode == self::MODE_STATEMENT) {
290  
-            return $this->loadDataFromMysqliStatement();
291  
-        } else {
292  
-            return $this->loadFromMysqliResult();
293  
-        }
294 339
     }
  340
+    
295 341
     /**
296 342
      * Count
297 343
      * 
@@ -299,9 +345,24 @@ public function valid()
299 345
      */
300 346
     public function count()
301 347
     {
302  
-        return $this->resource->num_rows;
  348
+        //If results are not stored, number of rows is zero
  349
+        if (!$this->isNumberOfRowsFinal) {
  350
+            if ($this->resource instanceof \mysqli_stmt && !$this->isResultStored) {
  351
+                $this->resource->store_result();
  352
+                $this->isResultStored = true;
  353
+            }
  354
+            $this->numberOfRows += $this->resource->num_rows;
  355
+            $this->isNumberOfRowsFinal = true;
  356
+        }
  357
+        
  358
+        return $this->numberOfRows;
303 359
     }
304 360
 
  361
+    /**
  362
+     * Generated Value
  363
+     *
  364
+     * @return mixed
  365
+     */
305 366
     public function getGeneratedValue()
306 367
     {
307 368
         return $this->generatedValue;
11  library/Zend/Db/ResultSet/ResultSet.php
@@ -62,11 +62,6 @@ class ResultSet implements Countable, Iterator /*, ResultSetInterface */
62 62
     protected $returnType = self::TYPE_OBJECT;
63 63
 
64 64
     /**
65  
-     * @var null|int
66  
-     */
67  
-    protected $count;
68  
-
69  
-    /**
70 65
      * @var Iterator|IteratorAggregate
71 66
      */
72 67
     protected $dataSource = null;
@@ -268,11 +263,7 @@ public function rewind()
268 263
      */
269 264
     public function count()
270 265
     {
271  
-        if ($this->count !== null) {
272  
-            return $this->count;
273  
-        }
274  
-        $this->count = count($this->dataSource);
275  
-        return $this->count;
  266
+        return $this->dataSource->count();
276 267
     }
277 268
 
278 269
     /**

0 notes on commit d8027ac

Please sign in to comment.
Something went wrong with that request. Please try again.