Skip to content

Commit ca7ca2c

Browse files
bert-wtaylorotwell
andauthored
[12.x] Query builder PDO fetch modes (#54443)
* fetchargs * add fetchArgs() function * fetchargs * add fetchArgs() function * add afterquery callbacks * fix typo * flip afterquerycallback * fix expectations * add expectations * add expectations * add expectations * add raw expression test * rename to fetchUsing * style * Remove test from PR #54396 * Revert most Query\Builder changes * remove DB::raw test * Revert QueryBuilder pluck tests * Add base tests for $query->fetchUsing() * style * formatting * Update Builder.php --------- Co-authored-by: Taylor Otwell <taylor@laravel.com>
1 parent 1567133 commit ca7ca2c

9 files changed

+179
-110
lines changed

src/Illuminate/Database/Connection.php

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -389,11 +389,12 @@ public function selectFromWriteConnection($query, $bindings = [])
389389
* @param string $query
390390
* @param array $bindings
391391
* @param bool $useReadPdo
392+
* @param array $fetchUsing
392393
* @return array
393394
*/
394-
public function select($query, $bindings = [], $useReadPdo = true)
395+
public function select($query, $bindings = [], $useReadPdo = true, array $fetchUsing = [])
395396
{
396-
return $this->run($query, $bindings, function ($query, $bindings) use ($useReadPdo) {
397+
return $this->run($query, $bindings, function ($query, $bindings) use ($useReadPdo, $fetchUsing) {
397398
if ($this->pretending()) {
398399
return [];
399400
}
@@ -409,7 +410,7 @@ public function select($query, $bindings = [], $useReadPdo = true)
409410

410411
$statement->execute();
411412

412-
return $statement->fetchAll();
413+
return $statement->fetchAll(...$fetchUsing);
413414
});
414415
}
415416

@@ -419,11 +420,12 @@ public function select($query, $bindings = [], $useReadPdo = true)
419420
* @param string $query
420421
* @param array $bindings
421422
* @param bool $useReadPdo
423+
* @param array $fetchUsing
422424
* @return array
423425
*/
424-
public function selectResultSets($query, $bindings = [], $useReadPdo = true)
426+
public function selectResultSets($query, $bindings = [], $useReadPdo = true, array $fetchUsing = [])
425427
{
426-
return $this->run($query, $bindings, function ($query, $bindings) use ($useReadPdo) {
428+
return $this->run($query, $bindings, function ($query, $bindings) use ($useReadPdo, $fetchUsing) {
427429
if ($this->pretending()) {
428430
return [];
429431
}
@@ -439,7 +441,7 @@ public function selectResultSets($query, $bindings = [], $useReadPdo = true)
439441
$sets = [];
440442

441443
do {
442-
$sets[] = $statement->fetchAll();
444+
$sets[] = $statement->fetchAll(...$fetchUsing);
443445
} while ($statement->nextRowset());
444446

445447
return $sets;
@@ -452,9 +454,10 @@ public function selectResultSets($query, $bindings = [], $useReadPdo = true)
452454
* @param string $query
453455
* @param array $bindings
454456
* @param bool $useReadPdo
457+
* @param array $fetchUsing
455458
* @return \Generator<int, \stdClass>
456459
*/
457-
public function cursor($query, $bindings = [], $useReadPdo = true)
460+
public function cursor($query, $bindings = [], $useReadPdo = true, array $fetchUsing = [])
458461
{
459462
$statement = $this->run($query, $bindings, function ($query, $bindings) use ($useReadPdo) {
460463
if ($this->pretending()) {
@@ -479,7 +482,7 @@ public function cursor($query, $bindings = [], $useReadPdo = true)
479482
return $statement;
480483
});
481484

482-
while ($record = $statement->fetch()) {
485+
while ($record = $statement->fetch(...$fetchUsing)) {
483486
yield $record;
484487
}
485488
}

src/Illuminate/Database/ConnectionInterface.php

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,19 +51,21 @@ public function scalar($query, $bindings = [], $useReadPdo = true);
5151
* @param string $query
5252
* @param array $bindings
5353
* @param bool $useReadPdo
54+
* @param array $fetchUsing
5455
* @return array
5556
*/
56-
public function select($query, $bindings = [], $useReadPdo = true);
57+
public function select($query, $bindings = [], $useReadPdo = true, array $fetchUsing = []);
5758

5859
/**
5960
* Run a select statement against the database and returns a generator.
6061
*
6162
* @param string $query
6263
* @param array $bindings
6364
* @param bool $useReadPdo
65+
* @param array $fetchUsing
6466
* @return \Generator
6567
*/
66-
public function cursor($query, $bindings = [], $useReadPdo = true);
68+
public function cursor($query, $bindings = [], $useReadPdo = true, array $fetchUsing = []);
6769

6870
/**
6971
* Run an insert statement against the database.

src/Illuminate/Database/Query/Builder.php

Lines changed: 38 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,13 @@ class Builder implements BuilderContract
249249
*/
250250
public $useWritePdo = false;
251251

252+
/**
253+
* The custom arguments for the PDOStatement::fetchAll / fetch functions.
254+
*
255+
* @var array
256+
*/
257+
public array $fetchUsing = [];
258+
252259
/**
253260
* Create a new query builder instance.
254261
*
@@ -3087,9 +3094,13 @@ public function soleValue($column)
30873094
*/
30883095
public function get($columns = ['*'])
30893096
{
3090-
$items = new Collection($this->onceWithColumns(Arr::wrap($columns), function () {
3091-
return $this->processor->processSelect($this, $this->runSelect());
3092-
}));
3097+
$original = $this->columns;
3098+
3099+
$this->columns ??= Arr::wrap($columns);
3100+
3101+
$items = new Collection($this->processor->processSelect($this, $this->runSelect()));
3102+
3103+
$this->columns = $original;
30933104

30943105
return $this->applyAfterQueryCallbacks(
30953106
isset($this->groupLimit) ? $this->withoutGroupLimitKeys($items) : $items
@@ -3104,7 +3115,7 @@ public function get($columns = ['*'])
31043115
protected function runSelect()
31053116
{
31063117
return $this->connection->select(
3107-
$this->toSql(), $this->getBindings(), ! $this->useWritePdo
3118+
$this->toSql(), $this->getBindings(), ! $this->useWritePdo, $this->fetchUsing
31083119
);
31093120
}
31103121

@@ -3322,7 +3333,7 @@ public function cursor()
33223333

33233334
return (new LazyCollection(function () {
33243335
yield from $this->connection->cursor(
3325-
$this->toSql(), $this->getBindings(), ! $this->useWritePdo
3336+
$this->toSql(), $this->getBindings(), ! $this->useWritePdo, $this->fetchUsing
33263337
);
33273338
}))->map(function ($item) {
33283339
return $this->applyAfterQueryCallbacks(new Collection([$item]))->first();
@@ -3352,17 +3363,18 @@ protected function enforceOrderBy()
33523363
*/
33533364
public function pluck($column, $key = null)
33543365
{
3366+
$original = $this->columns;
3367+
33553368
// First, we will need to select the results of the query accounting for the
33563369
// given columns / key. Once we have the results, we will be able to take
33573370
// the results and get the exact data that was requested for the query.
3358-
$queryResult = $this->onceWithColumns(
3359-
is_null($key) || $key === $column ? [$column] : [$column, $key],
3360-
function () {
3361-
return $this->processor->processSelect(
3362-
$this, $this->runSelect()
3363-
);
3364-
}
3365-
);
3371+
$this->columns = is_null($key) || $key === $column
3372+
? [$column]
3373+
: [$column, $key];
3374+
3375+
$queryResult = $this->processor->processSelect($this, $this->runSelect());
3376+
3377+
$this->columns = $original;
33663378

33673379
if (empty($queryResult)) {
33683380
return new Collection;
@@ -3656,30 +3668,6 @@ protected function setAggregate($function, $columns)
36563668
return $this;
36573669
}
36583670

3659-
/**
3660-
* Execute the given callback while selecting the given columns.
3661-
*
3662-
* After running the callback, the columns are reset to the original value.
3663-
*
3664-
* @param array $columns
3665-
* @param callable $callback
3666-
* @return mixed
3667-
*/
3668-
protected function onceWithColumns($columns, $callback)
3669-
{
3670-
$original = $this->columns;
3671-
3672-
if (is_null($original)) {
3673-
$this->columns = $columns;
3674-
}
3675-
3676-
$result = $callback();
3677-
3678-
$this->columns = $original;
3679-
3680-
return $result;
3681-
}
3682-
36833671
/**
36843672
* Insert new records into the database.
36853673
*
@@ -4293,6 +4281,19 @@ public function useWritePdo()
42934281
return $this;
42944282
}
42954283

4284+
/**
4285+
* Specify arguments for the PDOStatement::fetchAll / fetch functions.
4286+
*
4287+
* @param mixed ...$fetchUsing
4288+
* @return $this
4289+
*/
4290+
public function fetchUsing(...$fetchUsing)
4291+
{
4292+
$this->fetchUsing = $fetchUsing;
4293+
4294+
return $this;
4295+
}
4296+
42964297
/**
42974298
* Determine if the value is a query builder instance or a Closure.
42984299
*

tests/Database/DatabaseEloquentBelongsToManyCreateOrFirstTest.php

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ public function testCreateOrFirstMethodAssociatesExistingRelated(): void
8484

8585
$source->getConnection()
8686
->expects('select')
87-
->with('select * from "related_table" where ("attr" = ?) limit 1', ['foo'], true)
87+
->with('select * from "related_table" where ("attr" = ?) limit 1', ['foo'], true, [])
8888
->andReturn([[
8989
'id' => 456,
9090
'attr' => 'foo',
@@ -128,6 +128,7 @@ public function testFirstOrCreateMethodRetrievesExistingRelatedAlreadyAssociated
128128
'select "related_table".*, "pivot_table"."source_id" as "pivot_source_id", "pivot_table"."related_id" as "pivot_related_id" from "related_table" inner join "pivot_table" on "related_table"."id" = "pivot_table"."related_id" where "pivot_table"."source_id" = ? and ("attr" = ?) limit 1',
129129
[123, 'foo'],
130130
true,
131+
[],
131132
)
132133
->andReturn([[
133134
'id' => 456,
@@ -176,7 +177,7 @@ public function testCreateOrFirstMethodRetrievesExistingRelatedAssociatedJustNow
176177

177178
$source->getConnection()
178179
->expects('select')
179-
->with('select * from "related_table" where ("attr" = ?) limit 1', ['foo'], true)
180+
->with('select * from "related_table" where ("attr" = ?) limit 1', ['foo'], true, [])
180181
->andReturn([[
181182
'id' => 456,
182183
'attr' => 'foo',
@@ -199,6 +200,7 @@ public function testCreateOrFirstMethodRetrievesExistingRelatedAssociatedJustNow
199200
'select "related_table".*, "pivot_table"."source_id" as "pivot_source_id", "pivot_table"."related_id" as "pivot_related_id" from "related_table" inner join "pivot_table" on "related_table"."id" = "pivot_table"."related_id" where "pivot_table"."source_id" = ? and ("attr" = ?) limit 1',
200201
[123, 'foo'],
201202
false,
203+
[],
202204
)
203205
->andReturn([[
204206
'id' => 456,
@@ -243,6 +245,7 @@ public function testFirstOrCreateMethodRetrievesExistingRelatedAndAssociatesIt()
243245
'select "related_table".*, "pivot_table"."source_id" as "pivot_source_id", "pivot_table"."related_id" as "pivot_related_id" from "related_table" inner join "pivot_table" on "related_table"."id" = "pivot_table"."related_id" where "pivot_table"."source_id" = ? and ("attr" = ?) limit 1',
244246
[123, 'foo'],
245247
true,
248+
[],
246249
)
247250
->andReturn([]);
248251

@@ -252,6 +255,7 @@ public function testFirstOrCreateMethodRetrievesExistingRelatedAndAssociatesIt()
252255
'select * from "related_table" where ("attr" = ?) limit 1',
253256
['foo'],
254257
true,
258+
[],
255259
)
256260
->andReturn([[
257261
'id' => 456,
@@ -326,6 +330,7 @@ protected function newBelongsToMany(Builder $query, Model $parent, $table, $fore
326330
'select "related_table".*, "pivot_table"."source_id" as "pivot_source_id", "pivot_table"."related_id" as "pivot_related_id" from "related_table" inner join "pivot_table" on "related_table"."id" = "pivot_table"."related_id" where "pivot_table"."source_id" = ? and ("attr" = ?) limit 1',
327331
[123, 'foo'],
328332
true,
333+
[],
329334
)
330335
->andReturn([]);
331336

@@ -335,6 +340,7 @@ protected function newBelongsToMany(Builder $query, Model $parent, $table, $fore
335340
'select * from "related_table" where ("attr" = ?) limit 1',
336341
['foo'],
337342
true,
343+
[],
338344
)
339345
->andReturn([]);
340346

0 commit comments

Comments
 (0)