Skip to content

Commit 52a1d63

Browse files
Merge pull request #58373 from nextcloud/feat/db/typed-query-builder
2 parents 33f852a + 161c91e commit 52a1d63

18 files changed

Lines changed: 222 additions & 22 deletions

build/psalm-baseline.xml

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3395,9 +3395,6 @@
33953395
<NullableReturnStatement>
33963396
<code><![CDATA[$alias]]></code>
33973397
</NullableReturnStatement>
3398-
<ParamNameMismatch>
3399-
<code><![CDATA[$selects]]></code>
3400-
</ParamNameMismatch>
34013398
</file>
34023399
<file src="lib/private/DB/QueryBuilder/QuoteHelper.php">
34033400
<InvalidNullableReturnType>
@@ -3407,6 +3404,12 @@
34073404
<code><![CDATA[$string]]></code>
34083405
</NullableReturnStatement>
34093406
</file>
3407+
<file src="lib/private/DB/QueryBuilder/TypedQueryBuilder.php">
3408+
<InternalMethod>
3409+
<code><![CDATA[select]]></code>
3410+
<code><![CDATA[selectDistinct]]></code>
3411+
</InternalMethod>
3412+
</file>
34103413
<file src="lib/private/DateTimeFormatter.php">
34113414
<FalsableReturnStatement>
34123415
<code><![CDATA[$l->l($type, $timestamp, [
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
use OCP\IDBConnection;
6+
use OCP\Server;
7+
8+
/**
9+
* SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
10+
* SPDX-License-Identifier: AGPL-3.0-or-later
11+
*/
12+
13+
$qb = Server::get(IDBConnection::class)->getTypedQueryBuilder();
14+
15+
$qb->selectColumns('a', 'b');
16+
$qb->selectColumns('c');
17+
18+
$qb->selectColumnsDistinct('d', 'e');
19+
$qb->selectColumnsDistinct('f');
20+
21+
$qb->selectAlias('g', 'h');
22+
$qb->selectAlias($qb->func()->lower('i'), 'j');
23+
24+
/** @psalm-check-type-exact $result = \OCP\DB\IResult<'a'|'b'|'c'|'d'|'e'|'f'|'h'|'j'> */
25+
$result = $qb->executeQuery();
26+
27+
/** @psalm-check-type-exact $rows = array<'a'|'b'|'c'|'d'|'e'|'f'|'h'|'j', mixed>|false */
28+
$rows = $result->fetch(\PDO::FETCH_ASSOC);
29+
30+
/** @psalm-check-type-exact $rows = array<'a'|'b'|'c'|'d'|'e'|'f'|'h'|'j', mixed>|false */
31+
$rows = $result->fetchAssociative();
32+
33+
/** @psalm-check-type-exact $rows = list<array<'a'|'b'|'c'|'d'|'e'|'f'|'h'|'j', mixed>> */
34+
$rows = $result->fetchAll(\PDO::FETCH_ASSOC);
35+
36+
/** @psalm-check-type-exact $rows = list<array<'a'|'b'|'c'|'d'|'e'|'f'|'h'|'j', mixed>> */
37+
$rows = $result->fetchAllAssociative();

build/rector-strict.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@
1919
$nextcloudDir . '/apps/settings/lib/Service/AuthorizedGroupService.php',
2020
$nextcloudDir . '/lib/private/Files/Storage/Storage.php',
2121
$nextcloudDir . '/lib/private/Files/Storage/Wrapper/Wrapper.php',
22+
$nextcloudDir . '/build/psalm/ITypedQueryBuilderTest.php',
23+
$nextcloudDir . '/lib/private/DB/QueryBuilder/TypedQueryBuilder.php',
24+
$nextcloudDir . '/lib/public/DB/QueryBuilder/ITypedQueryBuilder.php',
2225
])
2326
->withPreparedSets(
2427
deadCode: true,

lib/composer/composer/autoload_classmap.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,7 @@
327327
'OCP\\DB\\QueryBuilder\\IParameter' => $baseDir . '/lib/public/DB/QueryBuilder/IParameter.php',
328328
'OCP\\DB\\QueryBuilder\\IQueryBuilder' => $baseDir . '/lib/public/DB/QueryBuilder/IQueryBuilder.php',
329329
'OCP\\DB\\QueryBuilder\\IQueryFunction' => $baseDir . '/lib/public/DB/QueryBuilder/IQueryFunction.php',
330+
'OCP\\DB\\QueryBuilder\\ITypedQueryBuilder' => $baseDir . '/lib/public/DB/QueryBuilder/ITypedQueryBuilder.php',
330331
'OCP\\DB\\QueryBuilder\\Sharded\\IShardMapper' => $baseDir . '/lib/public/DB/QueryBuilder/Sharded/IShardMapper.php',
331332
'OCP\\DB\\Types' => $baseDir . '/lib/public/DB/Types.php',
332333
'OCP\\Dashboard\\IAPIWidget' => $baseDir . '/lib/public/Dashboard/IAPIWidget.php',
@@ -1657,6 +1658,7 @@
16571658
'OC\\DB\\QueryBuilder\\Sharded\\ShardDefinition' => $baseDir . '/lib/private/DB/QueryBuilder/Sharded/ShardDefinition.php',
16581659
'OC\\DB\\QueryBuilder\\Sharded\\ShardQueryRunner' => $baseDir . '/lib/private/DB/QueryBuilder/Sharded/ShardQueryRunner.php',
16591660
'OC\\DB\\QueryBuilder\\Sharded\\ShardedQueryBuilder' => $baseDir . '/lib/private/DB/QueryBuilder/Sharded/ShardedQueryBuilder.php',
1661+
'OC\\DB\\QueryBuilder\\TypedQueryBuilder' => $baseDir . '/lib/private/DB/QueryBuilder/TypedQueryBuilder.php',
16601662
'OC\\DB\\ResultAdapter' => $baseDir . '/lib/private/DB/ResultAdapter.php',
16611663
'OC\\DB\\SQLiteMigrator' => $baseDir . '/lib/private/DB/SQLiteMigrator.php',
16621664
'OC\\DB\\SQLiteSessionInit' => $baseDir . '/lib/private/DB/SQLiteSessionInit.php',

lib/composer/composer/autoload_static.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
368368
'OCP\\DB\\QueryBuilder\\IParameter' => __DIR__ . '/../../..' . '/lib/public/DB/QueryBuilder/IParameter.php',
369369
'OCP\\DB\\QueryBuilder\\IQueryBuilder' => __DIR__ . '/../../..' . '/lib/public/DB/QueryBuilder/IQueryBuilder.php',
370370
'OCP\\DB\\QueryBuilder\\IQueryFunction' => __DIR__ . '/../../..' . '/lib/public/DB/QueryBuilder/IQueryFunction.php',
371+
'OCP\\DB\\QueryBuilder\\ITypedQueryBuilder' => __DIR__ . '/../../..' . '/lib/public/DB/QueryBuilder/ITypedQueryBuilder.php',
371372
'OCP\\DB\\QueryBuilder\\Sharded\\IShardMapper' => __DIR__ . '/../../..' . '/lib/public/DB/QueryBuilder/Sharded/IShardMapper.php',
372373
'OCP\\DB\\Types' => __DIR__ . '/../../..' . '/lib/public/DB/Types.php',
373374
'OCP\\Dashboard\\IAPIWidget' => __DIR__ . '/../../..' . '/lib/public/Dashboard/IAPIWidget.php',
@@ -1698,6 +1699,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
16981699
'OC\\DB\\QueryBuilder\\Sharded\\ShardDefinition' => __DIR__ . '/../../..' . '/lib/private/DB/QueryBuilder/Sharded/ShardDefinition.php',
16991700
'OC\\DB\\QueryBuilder\\Sharded\\ShardQueryRunner' => __DIR__ . '/../../..' . '/lib/private/DB/QueryBuilder/Sharded/ShardQueryRunner.php',
17001701
'OC\\DB\\QueryBuilder\\Sharded\\ShardedQueryBuilder' => __DIR__ . '/../../..' . '/lib/private/DB/QueryBuilder/Sharded/ShardedQueryBuilder.php',
1702+
'OC\\DB\\QueryBuilder\\TypedQueryBuilder' => __DIR__ . '/../../..' . '/lib/private/DB/QueryBuilder/TypedQueryBuilder.php',
17011703
'OC\\DB\\ResultAdapter' => __DIR__ . '/../../..' . '/lib/private/DB/ResultAdapter.php',
17021704
'OC\\DB\\SQLiteMigrator' => __DIR__ . '/../../..' . '/lib/private/DB/SQLiteMigrator.php',
17031705
'OC\\DB\\SQLiteSessionInit' => __DIR__ . '/../../..' . '/lib/private/DB/SQLiteSessionInit.php',

lib/private/DB/ArrayResult.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414

1515
/**
1616
* Wrap an array or rows into a result interface
17+
*
18+
* @template-implements IResult<string>
1719
*/
1820
class ArrayResult implements IResult {
1921
protected int $count;

lib/private/DB/Connection.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
use OC\DB\QueryBuilder\Sharded\ShardDefinition;
3535
use OC\SystemConfig;
3636
use OCP\DB\QueryBuilder\IQueryBuilder;
37+
use OCP\DB\QueryBuilder\ITypedQueryBuilder;
3738
use OCP\DB\QueryBuilder\Sharded\IShardMapper;
3839
use OCP\Diagnostics\IEventLogger;
3940
use OCP\EventDispatcher\IEventDispatcher;
@@ -247,6 +248,14 @@ public function getStats(): array {
247248
* Returns a QueryBuilder for the connection.
248249
*/
249250
public function getQueryBuilder(): IQueryBuilder {
251+
return $this->getInnerQueryBuilder();
252+
}
253+
254+
public function getTypedQueryBuilder(): ITypedQueryBuilder {
255+
return $this->getInnerQueryBuilder();
256+
}
257+
258+
private function getInnerQueryBuilder(): IQueryBuilder&ITypedQueryBuilder {
250259
$this->queriesBuilt++;
251260

252261
$builder = new QueryBuilder(

lib/private/DB/ConnectionAdapter.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
use OCP\DB\IPreparedStatement;
1818
use OCP\DB\IResult;
1919
use OCP\DB\QueryBuilder\IQueryBuilder;
20+
use OCP\DB\QueryBuilder\ITypedQueryBuilder;
2021
use OCP\IDBConnection;
2122

2223
/**
@@ -32,6 +33,10 @@ public function getQueryBuilder(): IQueryBuilder {
3233
return $this->inner->getQueryBuilder();
3334
}
3435

36+
public function getTypedQueryBuilder(): ITypedQueryBuilder {
37+
return $this->inner->getTypedQueryBuilder();
38+
}
39+
3540
public function prepare($sql, $limit = null, $offset = null): IPreparedStatement {
3641
try {
3742
return new PreparedStatement(

lib/private/DB/QueryBuilder/ExtendedQueryBuilder.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
/**
1717
* Base class for creating classes that extend the builtin query builder
1818
*/
19-
abstract class ExtendedQueryBuilder implements IQueryBuilder {
19+
abstract class ExtendedQueryBuilder extends TypedQueryBuilder {
2020
public function __construct(
2121
protected IQueryBuilder $builder,
2222
) {
@@ -100,7 +100,7 @@ public function select(...$selects) {
100100
return $this;
101101
}
102102

103-
public function selectAlias($select, $alias) {
103+
public function selectAlias($select, $alias): self {
104104
$this->builder->selectAlias($select, $alias);
105105
return $this;
106106
}

lib/private/DB/QueryBuilder/Partitioned/PartitionedQueryBuilder.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ public function addSelect(...$select) {
9292
return $this;
9393
}
9494

95-
public function selectAlias($select, $alias) {
95+
public function selectAlias($select, $alias): self {
9696
$this->selects[] = ['select' => $select, 'alias' => $alias];
9797
return $this;
9898
}

0 commit comments

Comments
 (0)