Browse Source

Fix some of the issues with buffered queries not buffering.

When a buffered query is added to a collection the IteratorIterator
constructor peels off the buffering decorator causing data to be lost
after the first collection operation.

Correctly buffering results requires another iterator that captures
results from the decorated statement.
Mark Story 7 years ago
parent
commit
5d96beb844

+ 18 - 0
src/Database/Statement/BufferedStatement.php

@@ -14,6 +14,9 @@
  */
 namespace Cake\Database\Statement;
 
+use CachingIterator;
+use IteratorIterator;
+
 /**
  * A statement decorator that implements buffered results.
  *
@@ -160,4 +163,19 @@ class BufferedStatement extends StatementDecorator
         $this->_records = [];
         $this->_allFetched = false;
     }
+
+    /**
+     * Get an iterator that buffers results.
+     *
+     * @return Traversable
+     */
+    public function getIterator()
+    {
+        $statement = parent::getIterator();
+        if (!$this->_iterator) {
+            $this->_iterator = new BufferedIterator(new IteratorIterator($statement));
+        }
+
+        return $this->_iterator;
+    }
 }

+ 24 - 0
tests/TestCase/Database/ConnectionTest.php

@@ -14,6 +14,7 @@
  */
 namespace Cake\Test\TestCase\Database;
 
+use Cake\Collection\Collection;
 use Cake\Database\Connection;
 use Cake\Database\Driver\Mysql;
 use Cake\Database\Exception\MissingConnectionException;
@@ -250,6 +251,29 @@ class ConnectionTest extends TestCase
     }
 
     /**
+     * test executing a buffered query interacts with Collection well.
+     *
+     * @return void
+     */
+    public function testBufferedStatementCollectionWrappingStatement()
+    {
+        $this->loadFixtures('Things');
+        $statement = $this->connection->query('SELECT * FROM things LIMIT 3');
+        $statement->bufferResults(true);
+
+        $collection = new Collection($statement);
+        $result = $collection->extract('id')->toArray();
+        $this->assertSame(['1', '2'], $result);
+
+        // Check iteration after extraction
+        $result = [];
+        foreach ($collection as $v) {
+            $result[] = $v['id'];
+        }
+        $this->assertSame(['1', '2'], $result);
+    }
+
+    /**
      * Tests that passing a unknown value to a query throws an exception
      *
      * @return void