Skip to content

Commit

Permalink
Fix data loss on multiple Result->rewind()
Browse files Browse the repository at this point in the history
Signed-off-by: Grundik <grundik@ololo.cc>
  • Loading branch information
Grundik committed Dec 17, 2022
1 parent f382589 commit 7c15d90
Show file tree
Hide file tree
Showing 3 changed files with 122 additions and 3 deletions.
8 changes: 5 additions & 3 deletions src/Adapter/Driver/Pdo/Result.php
Original file line number Diff line number Diff line change
Expand Up @@ -201,9 +201,11 @@ public function rewind()
'This result is a forward only result set, calling rewind() after moving forward is not supported'
);
}
$this->currentData = $this->resource->fetch($this->fetchMode);
$this->currentComplete = true;
$this->position = 0;
if (! $this->currentComplete) {
$this->currentData = $this->resource->fetch($this->fetchMode);
$this->currentComplete = true;
}
$this->position = 0;
}

/**
Expand Down
64 changes: 64 additions & 0 deletions test/unit/Adapter/Driver/Pdo/ResultTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@
use Laminas\Db\Adapter\Driver\Pdo\Result;
use Laminas\Db\Adapter\Exception\InvalidArgumentException;
use PDO;
use PDOStatement;
use PHPUnit\Framework\TestCase;
use stdClass;

use function assert;
use function uniqid;

/**
Expand Down Expand Up @@ -80,4 +82,66 @@ public function testFetchModeRange()
self::assertEquals(11, $result->getFetchMode());
self::assertInstanceOf('stdClass', $result->current());
}

public function testMultipleRewind()
{
$data = [
['test' => 1],
['test' => 2],
];
$position = 0;

$stub = $this->getMockBuilder('PDOStatement')->getMock();
assert($stub instanceof PDOStatement); // to suppress IDE type warnings
$stub->expects($this->any())
->method('fetch')
->will($this->returnCallback(function () use ($data, &$position) {
return $data[$position++];
}));
$result = new Result();
$result->initialize($stub, null);

$result->rewind();
$result->rewind();

$this->assertEquals(0, $result->key());
$this->assertEquals(1, $position);
$this->assertEquals($data[0], $result->current());

$result->next();
$this->assertEquals(1, $result->key());
$this->assertEquals(2, $position);
$this->assertEquals($data[1], $result->current());
}

public function testMultipleRewindBuffered()
{
$data = [
['test' => 1],
['test' => 2],
];
$position = 0;

$stub = $this->getMockBuilder(PDOStatement::class)->getMock();
assert($stub instanceof PDOStatement); // to suppress IDE type warnings
$stub->expects($this->any())
->method('fetch')
->will($this->returnCallback(function () use ($data, &$position) {
return $data[$position++];
}));
$result = new Result();
$result->initialize($stub, null);

$result->rewind();
$result->rewind();

$this->assertEquals(0, $result->key());
$this->assertEquals(1, $position);
$this->assertEquals($data[0], $result->current());

$result->next();
$this->assertEquals(1, $result->key());
$this->assertEquals(2, $position);
$this->assertEquals($data[1], $result->current());
}
}
53 changes: 53 additions & 0 deletions test/unit/ResultSet/AbstractResultSetTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,17 @@
namespace LaminasTest\Db\ResultSet;

use ArrayIterator;
use Laminas\Db\Adapter\Driver\Pdo\Result;
use Laminas\Db\Adapter\Driver\ResultInterface;
use Laminas\Db\ResultSet\AbstractResultSet;
use Laminas\Db\ResultSet\Exception\InvalidArgumentException;
use Laminas\Db\ResultSet\Exception\RuntimeException;
use PDOStatement;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;

use function assert;

class AbstractResultSetTest extends TestCase
{
/** @var MockObject */
Expand Down Expand Up @@ -266,4 +270,53 @@ public function testBufferIterations()
$data = $resultSet->current();
self::assertEquals(3, $data['id']);
}

/**
* Test multiple iterations with buffer with multiple rewind() calls
*
* @group issue-6845
*/
public function testMultipleRewindBufferIterations()
{
$resultSet = $this->getMockForAbstractClass(AbstractResultSet::class);
$result = new Result();
$stub = $this->getMockBuilder(PDOStatement::class)->getMock();
$data = new ArrayIterator([
['id' => 1, 'name' => 'one'],
['id' => 2, 'name' => 'two'],
['id' => 3, 'name' => 'three'],
]);
assert($stub instanceof PDOStatement); // to suppress IDE type warnings
$stub->expects($this->any())
->method('fetch')
->will($this->returnCallback(function () use ($data) {
$r = $data->current();
$data->next();
return $r;
}));
$result->initialize($stub, null);
$result->rewind();
$result->rewind();
$resultSet->initialize($result);
$resultSet->buffer();
$resultSet->rewind();
$resultSet->rewind();

$data = $resultSet->current();
self::assertEquals(1, $data['id']);
$resultSet->next();
$data = $resultSet->current();
self::assertEquals(2, $data['id']);

$resultSet->rewind();
$resultSet->rewind();
$data = $resultSet->current();
self::assertEquals(1, $data['id']);
$resultSet->next();
$data = $resultSet->current();
self::assertEquals(2, $data['id']);
$resultSet->next();
$data = $resultSet->current();
self::assertEquals(3, $data['id']);
}
}

0 comments on commit 7c15d90

Please sign in to comment.