Skip to content

Commit 59e44ff

Browse files
authored
Fix batch reading + Disable batching by default (#114)
1 parent 264a406 commit 59e44ff

File tree

3 files changed

+66
-56
lines changed

3 files changed

+66
-56
lines changed

src/QueryDataReader.php

Lines changed: 46 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,6 @@
5151
*/
5252
class QueryDataReader implements QueryDataReaderInterface
5353
{
54-
/** @psalm-suppress MissingClassConstType */
55-
final public const DEFAULT_BATCH_SIZE = 100;
56-
5754
private FilterHandler $filterHandler;
5855
private FieldMapperInterface $fieldMapper;
5956

@@ -66,7 +63,7 @@ class QueryDataReader implements QueryDataReaderInterface
6663
* @var array[]|object[]|null
6764
* @psalm-var array<TKey, TValue>|null
6865
*/
69-
private ?array $data = null;
66+
private array|null $cache = null;
7067

7168
/**
7269
* @psalm-param non-negative-int|null $limit
@@ -81,7 +78,7 @@ public function __construct(
8178
private ?string $countParam = null,
8279
private FilterInterface $filter = new All(),
8380
private FilterInterface $having = new All(),
84-
private ?int $batchSize = self::DEFAULT_BATCH_SIZE,
81+
private int|null $batchSize = null,
8582
array|null $filterHandlers = null,
8683
array|FieldMapperInterface $fieldMapper = [],
8784
) {
@@ -93,26 +90,55 @@ public function __construct(
9390

9491
/**
9592
* @psalm-return Generator<TKey, TValue, mixed, void>
96-
* @psalm-suppress InvalidReturnType
9793
*/
9894
final public function getIterator(): Generator
9995
{
100-
if (is_array($this->data)) {
101-
yield from $this->data;
102-
} elseif ($this->batchSize === null) {
103-
yield from $this->read();
104-
} else {
105-
yield from $this->getPreparedQuery()->batch($this->batchSize);
96+
if ($this->batchSize !== null) {
97+
foreach ($this->getPreparedQuery()->batch($this->batchSize) as $data) {
98+
/** @psalm-var array<TKey, TValue> $data */
99+
yield from $data;
100+
}
101+
return;
106102
}
103+
104+
if (is_array($this->cache)) {
105+
yield from $this->cache;
106+
return;
107+
}
108+
109+
/** @psalm-var array<TKey, TValue> */
110+
$this->cache = $this->getPreparedQuery()->all();
111+
yield from $this->cache;
112+
}
113+
114+
/**
115+
* @psalm-return Generator<TKey, TValue, mixed, void>
116+
*/
117+
final public function read(): Generator
118+
{
119+
return $this->getIterator();
120+
}
121+
122+
/**
123+
* @psalm-return TValue|null
124+
*/
125+
final public function readOne(): array|object|null
126+
{
127+
if (is_array($this->cache)) {
128+
$key = array_key_first($this->cache);
129+
return $key === null ? null : $this->cache[$key];
130+
}
131+
132+
return $this->withLimit(1)->getIterator()->current();
107133
}
108134

109135
final public function count(): int
110136
{
111137
if ($this->count === null) {
112138
$q = $this->countParam ?? '*';
113139

114-
if ($q === '*' && is_array($this->data) && !$this->limit && !$this->offset) {
115-
$this->count = count($this->data);
140+
if ($q === '*' && is_array($this->cache) && !$this->limit && !$this->offset) {
141+
$this->count = count($this->cache);
116142
} else {
117143
$query = $this->getPreparedQuery();
118144
$query->offset(null);
@@ -172,7 +198,7 @@ final public function getPreparedQuery(): QueryInterface
172198
final public function withOffset(int $offset): static
173199
{
174200
$new = clone $this;
175-
$new->data = null;
201+
$new->cache = null;
176202
$new->offset = $offset;
177203
return $new;
178204
}
@@ -190,7 +216,7 @@ final public function withLimit(?int $limit): static
190216
}
191217

192218
$new = clone $this;
193-
$new->data = null;
219+
$new->cache = null;
194220
$new->limit = $limit;
195221
return $new;
196222
}
@@ -222,7 +248,7 @@ final public function withCountParam(?string $countParam): static
222248
final public function withSort(?Sort $sort): static
223249
{
224250
$new = clone $this;
225-
$new->data = null;
251+
$new->cache = null;
226252
$new->sort = $sort;
227253
return $new;
228254
}
@@ -237,7 +263,7 @@ final public function withFilter(FilterInterface $filter): static
237263
{
238264
$new = clone $this;
239265
$new->filter = $filter;
240-
$new->count = $new->data = null;
266+
$new->count = $new->cache = null;
241267
return $new;
242268
}
243269

@@ -251,7 +277,7 @@ final public function withHaving(FilterInterface $having): static
251277
{
252278
$new = clone $this;
253279
$new->having = $having;
254-
$new->count = $new->data = null;
280+
$new->count = $new->cache = null;
255281
return $new;
256282
}
257283

@@ -292,7 +318,7 @@ final public function withAddedFilterHandlers(FilterHandlerInterface ...$filterH
292318
/** @var QueryFilterHandlerInterface[] $filterHandlers */
293319

294320
$new = clone $this;
295-
$new->count = $new->data = null;
321+
$new->count = $new->cache = null;
296322
$new->filterHandler = $this->filterHandler->withAddedFilterHandlers(...$filterHandlers);
297323
return $new;
298324
}
@@ -302,33 +328,6 @@ final public function getSort(): ?Sort
302328
return $this->sort;
303329
}
304330

305-
/**
306-
* @psalm-return array<TKey, TValue>
307-
*/
308-
final public function read(): array
309-
{
310-
if ($this->data === null) {
311-
/** @psalm-var array<TKey, TValue> */
312-
$this->data = $this->getPreparedQuery()->all();
313-
}
314-
315-
return $this->data;
316-
}
317-
318-
/**
319-
* @psalm-return TValue|null
320-
*/
321-
final public function readOne(): array|object|null
322-
{
323-
if (is_array($this->data)) {
324-
$key = array_key_first($this->data);
325-
326-
return $key === null ? null : $this->data[$key];
327-
}
328-
329-
return $this->withLimit(1)->getIterator()->current();
330-
}
331-
332331
final public function getFilter(): FilterInterface
333332
{
334333
return $this->filter;

tests/Base/DataTrait.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
namespace Yiisoft\Data\Db\Tests\Base;
66

77
use DateTimeImmutable;
8+
use Traversable;
89
use Yiisoft\Data\Db\QueryDataReader;
910
use Yiisoft\Data\Reader\DataReaderInterface;
1011
use Yiisoft\Data\Tests\Common\FixtureTrait;
@@ -55,8 +56,10 @@ protected function getReader(): DataReaderInterface
5556
return new QueryDataReader((new Query($db))->from('user'));
5657
}
5758

58-
protected function assertFixtures(array $expectedFixtureIndexes, array $actualFixtures): void
59+
protected function assertFixtures(array $expectedFixtureIndexes, iterable $actualFixtures): void
5960
{
61+
$actualFixtures = $actualFixtures instanceof Traversable ? iterator_to_array($actualFixtures) : $actualFixtures;
62+
6063
$processedActualFixtures = [];
6164
foreach ($actualFixtures as $fixture) {
6265
if (is_object($fixture)) {

tests/Sqlite/QueryDataReaderTest.php

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ public function testWithAddedFilterHandlers(): void
4747
$dataReaderWithUnsupportedFilter = $dataReaderWithAdded->withFilter(new Equals('id', 'test'));
4848
$this->expectException(LogicException::class);
4949
$this->expectExceptionMessage('Operator "' . Equals::class . '" is not supported.');
50-
$dataReaderWithUnsupportedFilter->read();
50+
iterator_to_array($dataReaderWithUnsupportedFilter->read());
5151
}
5252

5353
public function testWithAddedFilterHandlersWithIncorrectHandler(): void
@@ -77,8 +77,8 @@ public function testGetIteratorAfterRead(): void
7777

7878
$dataReader = new QueryDataReader($db->createQuery()->from('test'));
7979

80-
$readResult = $dataReader->read();
81-
$read2Result = $dataReader->read();
80+
$readResult = iterator_to_array($dataReader->read());
81+
$read2Result = iterator_to_array($dataReader->read());
8282
$getIteratorResult = iterator_to_array($dataReader->getIterator());
8383

8484
$this->assertCount(1, $logger->getMessages()); // Only one query should be logged
@@ -140,10 +140,18 @@ public function testBatchReading(): void
140140
batchSize: 1,
141141
);
142142

143-
$results = iterator_to_array($dataReader->getIterator());
144-
$result = array_merge(...$results);
145-
146-
$this->assertSame($data, $result);
143+
$this->assertSame(
144+
$data,
145+
iterator_to_array($dataReader->read()),
146+
);
147+
$this->assertSame(
148+
$data[0],
149+
$dataReader->readOne(),
150+
);
151+
$this->assertSame(
152+
$data,
153+
iterator_to_array($dataReader->getIterator()),
154+
);
147155
}
148156

149157
public function testCountInCommonCaseAfterRead(): void
@@ -160,7 +168,7 @@ public function testCountInCommonCaseAfterRead(): void
160168

161169
$dataReader = new QueryDataReader($db->createQuery()->from('test'));
162170

163-
$result = $dataReader->read();
171+
$result = iterator_to_array($dataReader->read());
164172
$count = $dataReader->count();
165173

166174
$this->assertCount(1, $logger->getMessages()); // Only one query should be logged

0 commit comments

Comments
 (0)