From 1b58c93a437ed1691bb5b6a7edcd512da49adbe4 Mon Sep 17 00:00:00 2001 From: Carl Schwan Date: Fri, 23 Jan 2026 16:55:54 +0100 Subject: [PATCH] refactor(QueryBuilder): Properly type the query builder Signed-off-by: Carl Schwan --- build/psalm-baseline.xml | 19 - .../DB/QueryBuilder/ExtendedQueryBuilder.php | 175 ++-- .../FunctionBuilder/FunctionBuilder.php | 8 + .../Partitioned/PartitionedQueryBuilder.php | 69 +- lib/private/DB/QueryBuilder/QueryBuilder.php | 916 +++--------------- lib/private/DB/QueryBuilder/QuoteHelper.php | 18 +- .../Sharded/ShardedQueryBuilder.php | 123 ++- lib/private/Files/Cache/CacheQueryBuilder.php | 10 +- lib/private/Group/Database.php | 2 +- .../OpenMetrics/Exporters/ActiveUsers.php | 2 +- .../DB/QueryBuilder/IFunctionBuilder.php | 11 + lib/public/DB/QueryBuilder/IQueryBuilder.php | 174 ++-- 12 files changed, 463 insertions(+), 1064 deletions(-) diff --git a/build/psalm-baseline.xml b/build/psalm-baseline.xml index 371f575b9a25e..a752fbf87f72f 100644 --- a/build/psalm-baseline.xml +++ b/build/psalm-baseline.xml @@ -3520,25 +3520,6 @@ - - - - - - - - - - - - - - - - - - - l($type, $timestamp, [ diff --git a/lib/private/DB/QueryBuilder/ExtendedQueryBuilder.php b/lib/private/DB/QueryBuilder/ExtendedQueryBuilder.php index 9230c55c28c86..eba22583ff780 100644 --- a/lib/private/DB/QueryBuilder/ExtendedQueryBuilder.php +++ b/lib/private/DB/QueryBuilder/ExtendedQueryBuilder.php @@ -9,284 +9,349 @@ namespace OC\DB\QueryBuilder; use OCP\DB\IResult; +use OCP\DB\QueryBuilder\ICompositeExpression; +use OCP\DB\QueryBuilder\IExpressionBuilder; +use OCP\DB\QueryBuilder\IFunctionBuilder; +use OCP\DB\QueryBuilder\ILiteral; +use OCP\DB\QueryBuilder\IParameter; use OCP\DB\QueryBuilder\IQueryBuilder; +use OCP\DB\QueryBuilder\IQueryFunction; use OCP\IDBConnection; +use Override; /** * Base class for creating classes that extend the builtin query builder */ abstract class ExtendedQueryBuilder implements IQueryBuilder { public function __construct( - protected IQueryBuilder $builder, + protected readonly IQueryBuilder $builder, ) { } - public function automaticTablePrefix($enabled) { + #[Override] + public function automaticTablePrefix(bool $enabled): void { $this->builder->automaticTablePrefix($enabled); - return $this; } - public function expr() { + #[Override] + public function expr(): IExpressionBuilder { return $this->builder->expr(); } - public function func() { + #[Override] + public function func(): IFunctionBuilder { return $this->builder->func(); } - public function getType() { + #[Override] + public function getType(): int { return $this->builder->getType(); } - public function getConnection() { + #[Override] + public function getConnection(): IDBConnection { return $this->builder->getConnection(); } - public function getState() { + #[Override] + public function getState(): int { return $this->builder->getState(); } - public function getSQL() { + #[Override] + public function getSQL(): string { return $this->builder->getSQL(); } - public function setParameter($key, $value, $type = null) { + #[Override] + public function setParameter(string|int $key, mixed $value, string|null|int $type = null): self { $this->builder->setParameter($key, $value, $type); return $this; } - public function setParameters(array $params, array $types = []) { + #[Override] + public function setParameters(array $params, array $types = []): self { $this->builder->setParameters($params, $types); return $this; } - public function getParameters() { + #[Override] + public function getParameters(): array { return $this->builder->getParameters(); } - public function getParameter($key) { + #[Override] + public function getParameter(int|string $key): mixed { return $this->builder->getParameter($key); } - public function getParameterTypes() { + #[Override] + public function getParameterTypes(): array { return $this->builder->getParameterTypes(); } - public function getParameterType($key) { + #[Override] + public function getParameterType(int|string $key): mixed { return $this->builder->getParameterType($key); } - public function setFirstResult($firstResult) { + #[Override] + public function setFirstResult(int $firstResult): self { $this->builder->setFirstResult($firstResult); return $this; } - public function getFirstResult() { + #[Override] + public function getFirstResult(): int { return $this->builder->getFirstResult(); } - public function setMaxResults($maxResults) { + #[Override] + public function setMaxResults(?int $maxResults): self { $this->builder->setMaxResults($maxResults); return $this; } - public function getMaxResults() { + #[Override] + public function getMaxResults(): ?int { return $this->builder->getMaxResults(); } - public function select(...$selects) { + #[Override] + public function select(...$selects): self { $this->builder->select(...$selects); return $this; } - public function selectAlias($select, $alias) { + #[Override] + public function selectAlias(string|IQueryFunction|IParameter $select, string $alias): self { $this->builder->selectAlias($select, $alias); return $this; } - public function selectDistinct($select) { + #[Override] + public function selectDistinct(string|array $select): self { $this->builder->selectDistinct($select); return $this; } - public function addSelect(...$select) { - $this->builder->addSelect(...$select); + #[Override] + public function addSelect(...$selects): self { + $this->builder->addSelect(...$selects); return $this; } - public function delete($delete = null, $alias = null) { + #[Override] + public function delete(string $delete, ?string $alias = null): self { $this->builder->delete($delete, $alias); return $this; } - public function update($update = null, $alias = null) { + #[Override] + public function update(string $update, ?string $alias = null): self { $this->builder->update($update, $alias); return $this; } - public function insert($insert = null) { + #[Override] + public function insert(string $insert): self { $this->builder->insert($insert); return $this; } - public function from($from, $alias = null) { + #[Override] + public function from(string|IQueryFunction $from, ?string $alias = null): self { $this->builder->from($from, $alias); return $this; } - public function join($fromAlias, $join, $alias, $condition = null) { + #[Override] + public function join(string $fromAlias, string|IQueryFunction $join, string $alias, string|ICompositeExpression|null $condition = null): self { $this->builder->join($fromAlias, $join, $alias, $condition); return $this; } - public function innerJoin($fromAlias, $join, $alias, $condition = null) { + #[Override] + public function innerJoin($fromAlias, $join, $alias, $condition = null): self { $this->builder->innerJoin($fromAlias, $join, $alias, $condition); return $this; } - public function leftJoin($fromAlias, $join, $alias, $condition = null) { + #[Override] + public function leftJoin($fromAlias, $join, $alias, $condition = null): self { $this->builder->leftJoin($fromAlias, $join, $alias, $condition); return $this; } - public function rightJoin($fromAlias, $join, $alias, $condition = null) { + #[Override] + public function rightJoin($fromAlias, $join, $alias, $condition = null): self { $this->builder->rightJoin($fromAlias, $join, $alias, $condition); return $this; } - public function set($key, $value) { + #[Override] + public function set($key, $value): self { $this->builder->set($key, $value); return $this; } - public function where(...$predicates) { + #[Override] + public function where(...$predicates): self { $this->builder->where(...$predicates); return $this; } - public function andWhere(...$where) { + #[Override] + public function andWhere(...$where): self { $this->builder->andWhere(...$where); return $this; } - public function orWhere(...$where) { + #[Override] + public function orWhere(...$where): self { $this->builder->orWhere(...$where); return $this; } - public function groupBy(...$groupBys) { + #[Override] + public function groupBy(...$groupBys): self { $this->builder->groupBy(...$groupBys); return $this; } - public function addGroupBy(...$groupBy) { + #[Override] + public function addGroupBy(...$groupBy): self { $this->builder->addGroupBy(...$groupBy); return $this; } - public function setValue($column, $value) { + #[Override] + public function setValue(string $column, IParameter|IQueryFunction|string $value): self { $this->builder->setValue($column, $value); return $this; } - public function values(array $values) { + #[Override] + public function values(array $values): self { $this->builder->values($values); return $this; } - public function having(...$having) { + #[Override] + public function having(...$having): self { $this->builder->having(...$having); return $this; } - public function andHaving(...$having) { + #[Override] + public function andHaving(...$having): self { $this->builder->andHaving(...$having); return $this; } - public function orHaving(...$having) { + #[Override] + public function orHaving(...$having): self { $this->builder->orHaving(...$having); return $this; } - public function orderBy($sort, $order = null) { + #[Override] + public function orderBy(string|ILiteral|IParameter|IQueryFunction $sort, ?string $order = null): self { $this->builder->orderBy($sort, $order); return $this; } - public function addOrderBy($sort, $order = null) { + #[Override] + public function addOrderBy(string|ILiteral|IParameter|IQueryFunction $sort, ?string $order = null): self { $this->builder->addOrderBy($sort, $order); return $this; } - public function getQueryPart($queryPartName) { + #[Override] + public function getQueryPart(string $queryPartName): mixed { return $this->builder->getQueryPart($queryPartName); } - public function getQueryParts() { + #[Override] + public function getQueryParts(): array { return $this->builder->getQueryParts(); } - public function resetQueryParts($queryPartNames = null) { + #[Override] + public function resetQueryParts(?array $queryPartNames = null): self { $this->builder->resetQueryParts($queryPartNames); return $this; } - public function resetQueryPart($queryPartName) { + #[Override] + public function resetQueryPart(string $queryPartName): self { $this->builder->resetQueryPart($queryPartName); return $this; } - public function createNamedParameter($value, $type = self::PARAM_STR, $placeHolder = null) { + #[Override] + public function createNamedParameter(mixed $value, mixed $type = self::PARAM_STR, $placeHolder = null): IParameter { return $this->builder->createNamedParameter($value, $type, $placeHolder); } - public function createPositionalParameter($value, $type = self::PARAM_STR) { + #[Override] + public function createPositionalParameter(mixed $value, mixed $type = self::PARAM_STR): IParameter { return $this->builder->createPositionalParameter($value, $type); } - public function createParameter($name) { + #[Override] + public function createParameter(string $name): IParameter { return $this->builder->createParameter($name); } - public function createFunction($call) { + #[Override] + public function createFunction(string $call): IQueryFunction { return $this->builder->createFunction($call); } + #[Override] public function getLastInsertId(): int { return $this->builder->getLastInsertId(); } - public function getTableName($table) { + #[Override] + public function getTableName(string|IQueryFunction $table): string { return $this->builder->getTableName($table); } - public function getColumnName($column, $tableAlias = '') { + #[Override] + public function getColumnName(string $column, string $tableAlias = ''): string { return $this->builder->getColumnName($column, $tableAlias); } + #[Override] public function executeQuery(?IDBConnection $connection = null): IResult { return $this->builder->executeQuery($connection); } + #[Override] public function executeStatement(?IDBConnection $connection = null): int { return $this->builder->executeStatement($connection); } + #[Override] public function hintShardKey(string $column, mixed $value, bool $overwrite = false): self { $this->builder->hintShardKey($column, $value, $overwrite); return $this; } + #[Override] public function runAcrossAllShards(): self { $this->builder->runAcrossAllShards(); return $this; } + #[Override] public function getOutputColumns(): array { return $this->builder->getOutputColumns(); } + #[Override] public function prefixTableName(string $table): string { return $this->builder->prefixTableName($table); } diff --git a/lib/private/DB/QueryBuilder/FunctionBuilder/FunctionBuilder.php b/lib/private/DB/QueryBuilder/FunctionBuilder/FunctionBuilder.php index 48dc1da6330b8..ce4dff6a2adb5 100644 --- a/lib/private/DB/QueryBuilder/FunctionBuilder/FunctionBuilder.php +++ b/lib/private/DB/QueryBuilder/FunctionBuilder/FunctionBuilder.php @@ -10,6 +10,8 @@ use OC\DB\QueryBuilder\QueryFunction; use OC\DB\QueryBuilder\QuoteHelper; use OCP\DB\QueryBuilder\IFunctionBuilder; +use OCP\DB\QueryBuilder\ILiteral; +use OCP\DB\QueryBuilder\IParameter; use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\DB\QueryBuilder\IQueryFunction; use OCP\IDBConnection; @@ -78,6 +80,12 @@ public function count($count = '', $alias = ''): IQueryFunction { return new QueryFunction('COUNT(' . $quotedName . ')' . $alias); } + public function countDistinct(string|ILiteral|IParameter|IQueryFunction $count = '', string $alias = ''): IQueryFunction { + $alias = !empty($alias) ? (' AS ' . $this->helper->quoteColumnName($alias)) : ''; + $quotedName = $count === '' ? '*' : $this->helper->quoteColumnName($count); + return new QueryFunction('COUNT(DISTINCT ' . $quotedName . ')' . $alias); + } + public function octetLength($field, $alias = ''): IQueryFunction { $alias = $alias ? (' AS ' . $this->helper->quoteColumnName($alias)) : ''; $quotedName = $this->helper->quoteColumnName($field); diff --git a/lib/private/DB/QueryBuilder/Partitioned/PartitionedQueryBuilder.php b/lib/private/DB/QueryBuilder/Partitioned/PartitionedQueryBuilder.php index 7750ce5056a8d..d3936a9f9513c 100644 --- a/lib/private/DB/QueryBuilder/Partitioned/PartitionedQueryBuilder.php +++ b/lib/private/DB/QueryBuilder/Partitioned/PartitionedQueryBuilder.php @@ -14,6 +14,7 @@ use OC\DB\QueryBuilder\Sharded\ShardConnectionManager; use OC\DB\QueryBuilder\Sharded\ShardedQueryBuilder; use OCP\DB\IResult; +use OCP\DB\QueryBuilder\IParameter; use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\DB\QueryBuilder\IQueryFunction; use OCP\IDBConnection; @@ -41,7 +42,7 @@ class PartitionedQueryBuilder extends ShardedQueryBuilder { /** @var list */ private array $partitions = []; - /** @var array{'select': string|array, 'alias': ?string}[] */ + /** @var array{'select': string|IQueryFunction|IParameter, 'alias': ?string}[] */ private array $selects = []; private ?PartitionSplit $mainPartition = null; private bool $hasPositionalParameter = false; @@ -75,7 +76,7 @@ private function newQuery(): IQueryBuilder { } // we need to save selects until we know all the table aliases - public function select(...$selects) { + public function select(...$selects): self { if (count($selects) === 1 && is_array($selects[0])) { $selects = $selects[0]; } @@ -84,15 +85,13 @@ public function select(...$selects) { return $this; } - public function addSelect(...$select) { - $select = array_map(function ($select) { - return ['select' => $select, 'alias' => null]; - }, $select); - $this->selects = array_merge($this->selects, $select); + public function addSelect(...$selects): self { + $selects = array_map(static fn ($select) => ['select' => $select, 'alias' => null], $selects); + $this->selects = array_merge($this->selects, $selects); return $this; } - public function selectAlias($select, $alias) { + public function selectAlias(string|IQueryFunction|IParameter $select, string $alias): self { $this->selects[] = ['select' => $select, 'alias' => $alias]; return $this; } @@ -101,9 +100,6 @@ public function selectAlias($select, $alias) { * Ensure that a column is being selected by the query * * This is mainly used to ensure that the returned rows from both sides of a partition contains the columns of the join predicate - * - * @param string|IQueryFunction $column - * @return void */ private function ensureSelect(string|IQueryFunction $column, ?string $alias = null): void { $checkColumn = $alias ?: $column; @@ -190,14 +186,15 @@ private function getPartition(string $table): ?PartitionSplit { return null; } - public function from($from, $alias = null) { + public function from($from, $alias = null): self { if (is_string($from) && $partition = $this->getPartition($from)) { $this->mainPartition = $partition; if ($alias) { $this->mainPartition->addAlias($from, $alias); } } - return parent::from($from, $alias); + parent::from($from, $alias); + return $this; } public function innerJoin($fromAlias, $join, $alias, $condition = null): self { @@ -281,11 +278,14 @@ public function join($fromAlias, $join, $alias, $condition = null, $joinMode = P } else { // join within the main db or a partition if ($joinMode === PartitionQuery::JOIN_MODE_INNER) { - return parent::innerJoin($fromAlias, $join, $alias, $condition); + parent::innerJoin($fromAlias, $join, $alias, $condition); + return $this; } elseif ($joinMode === PartitionQuery::JOIN_MODE_LEFT) { - return parent::leftJoin($fromAlias, $join, $alias, $condition); + parent::leftJoin($fromAlias, $join, $alias, $condition); + return $this; } elseif ($joinMode === PartitionQuery::JOIN_MODE_RIGHT) { - return parent::rightJoin($fromAlias, $join, $alias, $condition); + parent::rightJoin($fromAlias, $join, $alias, $condition); + return $this; } else { throw new \InvalidArgumentException("Invalid join mode: $joinMode"); } @@ -333,11 +333,11 @@ private function splitPredicatesByParts(array $predicates): array { return $partitionPredicates; } - public function where(...$predicates) { + public function where(...$predicates): self { return $this->andWhere(...$predicates); } - public function andWhere(...$where) { + public function andWhere(...$where): self { if ($where) { foreach ($this->splitPredicatesByParts($where) as $alias => $predicates) { if (isset($this->splitQueries[$alias])) { @@ -380,30 +380,35 @@ private function getPartitionForPredicate(string $predicate): ?PartitionSplit { return null; } - public function update($update = null, $alias = null) { - return parent::update($update, $alias); + public function update(string $update, ?string $alias = null): self { + parent::update($update, $alias); + return $this; } - public function insert($insert = null) { - return parent::insert($insert); + public function insert(string $insert): self { + parent::insert($insert); + return $this; } - public function delete($delete = null, $alias = null) { - return parent::delete($delete, $alias); + public function delete(string $delete, ?string $alias = null): self { + parent::delete($delete, $alias); + return $this; } - public function setMaxResults($maxResults) { - if ($maxResults > 0) { - $this->limit = (int)$maxResults; + public function setMaxResults(?int $maxResults): self { + if ($maxResults !== null && $maxResults > 0) { + $this->limit = $maxResults; } - return parent::setMaxResults($maxResults); + parent::setMaxResults($maxResults); + return $this; } - public function setFirstResult($firstResult) { + public function setFirstResult(int $firstResult): self { if ($firstResult > 0) { - $this->offset = (int)$firstResult; + $this->offset = $firstResult; } - return parent::setFirstResult($firstResult); + parent::setFirstResult($firstResult); + return $this; } public function executeQuery(?IDBConnection $connection = null): IResult { @@ -444,7 +449,7 @@ public function executeStatement(?IDBConnection $connection = null): int { return parent::executeStatement($connection); } - public function getSQL() { + public function getSQL(): string { $this->applySelects(); return parent::getSQL(); } diff --git a/lib/private/DB/QueryBuilder/QueryBuilder.php b/lib/private/DB/QueryBuilder/QueryBuilder.php index 82fc6f84117c8..8e7307d5e4736 100644 --- a/lib/private/DB/QueryBuilder/QueryBuilder.php +++ b/lib/private/DB/QueryBuilder/QueryBuilder.php @@ -20,78 +20,46 @@ use OC\SystemConfig; use OCP\DB\IResult; use OCP\DB\QueryBuilder\ICompositeExpression; +use OCP\DB\QueryBuilder\IExpressionBuilder; +use OCP\DB\QueryBuilder\IFunctionBuilder; use OCP\DB\QueryBuilder\ILiteral; use OCP\DB\QueryBuilder\IParameter; use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\DB\QueryBuilder\IQueryFunction; use OCP\IDBConnection; +use Override; use Psr\Log\LoggerInterface; class QueryBuilder implements IQueryBuilder { - /** @var ConnectionAdapter */ - private $connection; + private \Doctrine\DBAL\Query\QueryBuilder $queryBuilder; + private QuoteHelper $helper; - /** @var SystemConfig */ - private $systemConfig; - - private LoggerInterface $logger; - - /** @var \Doctrine\DBAL\Query\QueryBuilder */ - private $queryBuilder; - - /** @var QuoteHelper */ - private $helper; - - /** @var bool */ - private $automaticTablePrefix = true; + private bool $automaticTablePrefix = true; private bool $nonEmptyWhere = false; - /** @var string */ - protected $lastInsertedTable; + protected ?string $lastInsertedTable = null; + /** @var string[] */ private array $selectedColumns = []; /** * Initializes a new QueryBuilder. - * - * @param ConnectionAdapter $connection - * @param SystemConfig $systemConfig */ - public function __construct(ConnectionAdapter $connection, SystemConfig $systemConfig, LoggerInterface $logger) { - $this->connection = $connection; - $this->systemConfig = $systemConfig; - $this->logger = $logger; + public function __construct( + private readonly ConnectionAdapter $connection, + private readonly SystemConfig $systemConfig, + private readonly LoggerInterface $logger, + ) { $this->queryBuilder = new \Doctrine\DBAL\Query\QueryBuilder($this->connection->getInner()); $this->helper = new QuoteHelper(); } - /** - * Enable/disable automatic prefixing of table names with the oc_ prefix - * - * @param bool $enabled If set to true table names will be prefixed with the - * owncloud database prefix automatically. - * @since 8.2.0 - */ - public function automaticTablePrefix($enabled) { - $this->automaticTablePrefix = (bool)$enabled; + #[Override] + public function automaticTablePrefix(bool $enabled): void { + $this->automaticTablePrefix = $enabled; } - /** - * Gets an ExpressionBuilder used for object-oriented construction of query expressions. - * This producer method is intended for convenient inline usage. Example: - * - * - * $qb = $conn->getQueryBuilder() - * ->select('u') - * ->from('users', 'u') - * ->where($qb->expr()->eq('u.id', 1)); - * - * - * For more complex expression construction, consider storing the expression - * builder object in a local variable. - * - * @return \OCP\DB\QueryBuilder\IExpressionBuilder - */ - public function expr() { + #[Override] + public function expr(): IExpressionBuilder { return match($this->connection->getDatabaseProvider()) { IDBConnection::PLATFORM_ORACLE => new OCIExpressionBuilder($this->connection, $this, $this->logger), IDBConnection::PLATFORM_POSTGRES => new PgSqlExpressionBuilder($this->connection, $this, $this->logger), @@ -101,23 +69,8 @@ public function expr() { }; } - /** - * Gets an FunctionBuilder used for object-oriented construction of query functions. - * This producer method is intended for convenient inline usage. Example: - * - * - * $qb = $conn->getQueryBuilder() - * ->select('u') - * ->from('users', 'u') - * ->where($qb->fun()->md5('u.id')); - * - * - * For more complex function construction, consider storing the function - * builder object in a local variable. - * - * @return \OCP\DB\QueryBuilder\IFunctionBuilder - */ - public function func() { + #[Override] + public function func(): IFunctionBuilder { return match($this->connection->getDatabaseProvider()) { IDBConnection::PLATFORM_ORACLE => new OCIFunctionBuilder($this->connection, $this, $this->helper), IDBConnection::PLATFORM_POSTGRES => new PgSqlFunctionBuilder($this->connection, $this, $this->helper), @@ -127,37 +80,23 @@ public function func() { }; } - /** - * Gets the type of the currently built query. - * - * @return integer - */ - public function getType() { + #[Override] + public function getType(): int { return $this->queryBuilder->getType(); } - /** - * Gets the associated DBAL Connection for this query builder. - * - * @return \OCP\IDBConnection - */ - public function getConnection() { + #[Override] + public function getConnection(): IDBConnection { return $this->connection; } - /** - * Gets the state of this query builder instance. - * - * @return int Always returns 0 which is former `QueryBuilder::STATE_DIRTY` - * @deprecated 30.0.0 This function is going to be removed with the next Doctrine/DBAL update - * and we can not fix this in our wrapper. - */ - public function getState() { + #[Override] + public function getState(): int { $this->logger->debug(IQueryBuilder::class . '::' . __FUNCTION__ . ' is deprecated and will be removed soon.', ['exception' => new \Exception('Deprecated call to ' . __METHOD__)]); return $this->queryBuilder->getState(); } - private function prepareForExecute() { + private function prepareForExecute(): void { if ($this->systemConfig->getValue('log_query', false)) { try { $params = []; @@ -253,6 +192,7 @@ private function prepareForExecute() { } } + #[Override] public function executeQuery(?IDBConnection $connection = null): IResult { if ($this->getType() !== \Doctrine\DBAL\Query\QueryBuilder::SELECT) { throw new \RuntimeException('Invalid query type, expected SELECT query'); @@ -270,6 +210,7 @@ public function executeQuery(?IDBConnection $connection = null): IResult { ); } + #[Override] public function executeStatement(?IDBConnection $connection = null): int { if ($this->getType() === \Doctrine\DBAL\Query\QueryBuilder::SELECT) { throw new \RuntimeException('Invalid query type, expected INSERT, DELETE or UPDATE statement'); @@ -287,181 +228,75 @@ public function executeStatement(?IDBConnection $connection = null): int { ); } - - /** - * Gets the complete SQL string formed by the current specifications of this QueryBuilder. - * - * - * $qb = $conn->getQueryBuilder() - * ->select('u') - * ->from('User', 'u') - * echo $qb->getSQL(); // SELECT u FROM User u - * - * - * @return string The SQL query string. - */ - public function getSQL() { + #[Override] + public function getSQL(): string { return $this->queryBuilder->getSQL(); } - /** - * Sets a query parameter for the query being constructed. - * - * - * $qb = $conn->getQueryBuilder() - * ->select('u') - * ->from('users', 'u') - * ->where('u.id = :user_id') - * ->setParameter(':user_id', 1); - * - * - * @param string|integer $key The parameter position or name. - * @param mixed $value The parameter value. - * @param string|null|int $type One of the IQueryBuilder::PARAM_* constants. - * - * @return $this This QueryBuilder instance. - */ - public function setParameter($key, $value, $type = null) { + #[Override] + public function setParameter(string|int $key, mixed $value, string|null|int $type = null): self { $this->queryBuilder->setParameter($key, $value, $type); return $this; } - /** - * Sets a collection of query parameters for the query being constructed. - * - * - * $qb = $conn->getQueryBuilder() - * ->select('u') - * ->from('users', 'u') - * ->where('u.id = :user_id1 OR u.id = :user_id2') - * ->setParameters(array( - * ':user_id1' => 1, - * ':user_id2' => 2 - * )); - * - * - * @param array $params The query parameters to set. - * @param array $types The query parameters types to set. - * - * @return $this This QueryBuilder instance. - */ - public function setParameters(array $params, array $types = []) { + #[Override] + public function setParameters(array $params, array $types = []): self { $this->queryBuilder->setParameters($params, $types); return $this; } - /** - * Gets all defined query parameters for the query being constructed indexed by parameter index or name. - * - * @return array The currently defined query parameters indexed by parameter index or name. - */ - public function getParameters() { + #[Override] + public function getParameters(): array { return $this->queryBuilder->getParameters(); } - /** - * Gets a (previously set) query parameter of the query being constructed. - * - * @param mixed $key The key (index or name) of the bound parameter. - * - * @return mixed The value of the bound parameter. - */ - public function getParameter($key) { + #[Override] + public function getParameter(int|string $key): mixed { return $this->queryBuilder->getParameter($key); } - /** - * Gets all defined query parameter types for the query being constructed indexed by parameter index or name. - * - * @return array The currently defined query parameter types indexed by parameter index or name. - */ - public function getParameterTypes() { + #[Override] + public function getParameterTypes(): array { return $this->queryBuilder->getParameterTypes(); } - /** - * Gets a (previously set) query parameter type of the query being constructed. - * - * @param mixed $key The key (index or name) of the bound parameter type. - * - * @return mixed The value of the bound parameter type. - */ - public function getParameterType($key) { + #[Override] + public function getParameterType(int|string $key): mixed { return $this->queryBuilder->getParameterType($key); } - /** - * Sets the position of the first result to retrieve (the "offset"). - * - * @param int $firstResult The first result to return. - * - * @return $this This QueryBuilder instance. - */ - public function setFirstResult($firstResult) { - $this->queryBuilder->setFirstResult((int)$firstResult); + #[Override] + public function setFirstResult(int $firstResult): self { + $this->queryBuilder->setFirstResult($firstResult); return $this; } - /** - * Gets the position of the first result the query object was set to retrieve (the "offset"). - * Returns 0 if {@link setFirstResult} was not applied to this QueryBuilder. - * - * @return int The position of the first result. - */ - public function getFirstResult() { + #[Override] + public function getFirstResult(): int { return $this->queryBuilder->getFirstResult(); } - /** - * Sets the maximum number of results to retrieve (the "limit"). - * - * NOTE: Setting max results to "0" will cause mixed behaviour. While most - * of the databases will just return an empty result set, Oracle will return - * all entries. - * - * @param int|null $maxResults The maximum number of results to retrieve. - * - * @return $this This QueryBuilder instance. - */ - public function setMaxResults($maxResults) { + #[Override] + public function setMaxResults(?int $maxResults): self { if ($maxResults === null) { $this->queryBuilder->setMaxResults($maxResults); } else { - $this->queryBuilder->setMaxResults((int)$maxResults); + $this->queryBuilder->setMaxResults($maxResults); } return $this; } - /** - * Gets the maximum number of results the query object was set to retrieve (the "limit"). - * Returns NULL if {@link setMaxResults} was not applied to this query builder. - * - * @return int|null The maximum number of results. - */ - public function getMaxResults() { + #[Override] + public function getMaxResults(): ?int { return $this->queryBuilder->getMaxResults(); } - /** - * Specifies an item that is to be returned in the query result. - * Replaces any previously specified selections, if any. - * - * - * $qb = $conn->getQueryBuilder() - * ->select('u.id', 'p.id') - * ->from('users', 'u') - * ->leftJoin('u', 'phonenumbers', 'p', 'u.id = p.user_id'); - * - * - * @param mixed ...$selects The selection expressions. - * - * '@return $this This QueryBuilder instance. - */ - public function select(...$selects) { + #[Override] + public function select(...$selects): self { if (count($selects) === 1 && is_array($selects[0])) { $selects = $selects[0]; } @@ -474,22 +309,8 @@ public function select(...$selects) { return $this; } - /** - * Specifies an item that is to be returned with a different name in the query result. - * - * - * $qb = $conn->getQueryBuilder() - * ->selectAlias('u.id', 'user_id') - * ->from('users', 'u') - * ->leftJoin('u', 'phonenumbers', 'p', 'u.id = p.user_id'); - * - * - * @param mixed $select The selection expressions. - * @param string $alias The column alias used in the constructed query. - * - * @return $this This QueryBuilder instance. - */ - public function selectAlias($select, $alias) { + #[Override] + public function selectAlias(string|IParameter|IQueryFunction $select, string $alias): self { $this->queryBuilder->addSelect( $this->helper->quoteColumnName($select) . ' AS ' . $this->helper->quoteColumnName($alias) ); @@ -498,20 +319,8 @@ public function selectAlias($select, $alias) { return $this; } - /** - * Specifies an item that is to be returned uniquely in the query result. - * - * - * $qb = $conn->getQueryBuilder() - * ->selectDistinct('type') - * ->from('users'); - * - * - * @param mixed $select The selection expressions. - * - * @return $this This QueryBuilder instance. - */ - public function selectDistinct($select) { + #[Override] + public function selectDistinct(string|array $select): self { if (!is_array($select)) { $select = [$select]; } @@ -526,22 +335,8 @@ public function selectDistinct($select) { return $this; } - /** - * Adds an item that is to be returned in the query result. - * - * - * $qb = $conn->getQueryBuilder() - * ->select('u.id') - * ->addSelect('p.id') - * ->from('users', 'u') - * ->leftJoin('u', 'phonenumbers', 'u.id = p.user_id'); - * - * - * @param mixed ...$selects The selection expression. - * - * @return $this This QueryBuilder instance. - */ - public function addSelect(...$selects) { + #[Override] + public function addSelect(...$selects): self { if (count($selects) === 1 && is_array($selects[0])) { $selects = $selects[0]; } @@ -570,28 +365,13 @@ private function addOutputColumns(array $columns): void { } } + #[Override] public function getOutputColumns(): array { return array_unique($this->selectedColumns); } - /** - * Turns the query being built into a bulk delete query that ranges over - * a certain table. - * - * - * $qb = $conn->getQueryBuilder() - * ->delete('users', 'u') - * ->where('u.id = :user_id'); - * ->setParameter(':user_id', 1); - * - * - * @param string $delete The table whose rows are subject to the deletion. - * @param string $alias The table alias used in the constructed query. - * - * @return $this This QueryBuilder instance. - * @since 30.0.0 Alias is deprecated and will no longer be used with the next Doctrine/DBAL update - */ - public function delete($delete = null, $alias = null) { + #[Override] + public function delete(string $delete, ?string $alias = null): self { if ($alias !== null) { $this->logger->debug('DELETE queries with alias are no longer supported and the provided alias is ignored', ['exception' => new \InvalidArgumentException('Table alias provided for DELETE query')]); } @@ -604,24 +384,8 @@ public function delete($delete = null, $alias = null) { return $this; } - /** - * Turns the query being built into a bulk update query that ranges over - * a certain table - * - * - * $qb = $conn->getQueryBuilder() - * ->update('users', 'u') - * ->set('u.password', md5('password')) - * ->where('u.id = ?'); - * - * - * @param string $update The table whose rows are subject to the update. - * @param string $alias The table alias used in the constructed query. - * - * @return $this This QueryBuilder instance. - * @since 30.0.0 Alias is deprecated and will no longer be used with the next Doctrine/DBAL update - */ - public function update($update = null, $alias = null) { + #[Override] + public function update(string $update, ?string $alias = null): self { if ($alias !== null) { $this->logger->debug('UPDATE queries with alias are no longer supported and the provided alias is ignored', ['exception' => new \InvalidArgumentException('Table alias provided for UPDATE query')]); } @@ -634,26 +398,8 @@ public function update($update = null, $alias = null) { return $this; } - /** - * Turns the query being built into an insert query that inserts into - * a certain table - * - * - * $qb = $conn->getQueryBuilder() - * ->insert('users') - * ->values( - * array( - * 'name' => '?', - * 'password' => '?' - * ) - * ); - * - * - * @param string $insert The table into which the rows should be inserted. - * - * @return $this This QueryBuilder instance. - */ - public function insert($insert = null) { + #[Override] + public function insert(string $insert): self { $this->queryBuilder->insert( $this->getTableName($insert) ); @@ -663,48 +409,18 @@ public function insert($insert = null) { return $this; } - /** - * Creates and adds a query root corresponding to the table identified by the - * given alias, forming a cartesian product with any existing query roots. - * - * - * $qb = $conn->getQueryBuilder() - * ->select('u.id') - * ->from('users', 'u') - * - * - * @param string|IQueryFunction $from The table. - * @param string|null $alias The alias of the table. - * - * @return $this This QueryBuilder instance. - */ - public function from($from, $alias = null) { + #[Override] + public function from(string|IQueryFunction $from, ?string $alias = null): self { $this->queryBuilder->from( $this->getTableName($from), - $this->quoteAlias($alias) + $alias ? $this->quoteAlias($alias) : null, ); return $this; } - /** - * Creates and adds a join to the query. - * - * - * $qb = $conn->getQueryBuilder() - * ->select('u.name') - * ->from('users', 'u') - * ->join('u', 'phonenumbers', 'p', 'p.is_primary = 1'); - * - * - * @param string $fromAlias The alias that points to a from clause. - * @param string $join The table name to join. - * @param string $alias The alias of the join table. - * @param string|ICompositeExpression|null $condition The condition for the join. - * - * @return $this This QueryBuilder instance. - */ - public function join($fromAlias, $join, $alias, $condition = null) { + #[Override] + public function join(string $fromAlias, string|IQueryFunction $join, string $alias, string|ICompositeExpression|null $condition = null): self { $this->queryBuilder->join( $this->quoteAlias($fromAlias), $this->getTableName($join), @@ -715,24 +431,8 @@ public function join($fromAlias, $join, $alias, $condition = null) { return $this; } - /** - * Creates and adds a join to the query. - * - * - * $qb = $conn->getQueryBuilder() - * ->select('u.name') - * ->from('users', 'u') - * ->innerJoin('u', 'phonenumbers', 'p', 'p.is_primary = 1'); - * - * - * @param string $fromAlias The alias that points to a from clause. - * @param string $join The table name to join. - * @param string $alias The alias of the join table. - * @param string|ICompositeExpression|null $condition The condition for the join. - * - * @return $this This QueryBuilder instance. - */ - public function innerJoin($fromAlias, $join, $alias, $condition = null) { + #[Override] + public function innerJoin(string $fromAlias, string|IQueryFunction $join, string $alias, string|ICompositeExpression|null $condition = null): self { $this->queryBuilder->innerJoin( $this->quoteAlias($fromAlias), $this->getTableName($join), @@ -743,24 +443,8 @@ public function innerJoin($fromAlias, $join, $alias, $condition = null) { return $this; } - /** - * Creates and adds a left join to the query. - * - * - * $qb = $conn->getQueryBuilder() - * ->select('u.name') - * ->from('users', 'u') - * ->leftJoin('u', 'phonenumbers', 'p', 'p.is_primary = 1'); - * - * - * @param string $fromAlias The alias that points to a from clause. - * @param string|IQueryFunction $join The table name or sub-query to join. - * @param string $alias The alias of the join table. - * @param string|ICompositeExpression|null $condition The condition for the join. - * - * @return $this This QueryBuilder instance. - */ - public function leftJoin($fromAlias, $join, $alias, $condition = null) { + #[Override] + public function leftJoin(string $fromAlias, string|IQueryFunction $join, string $alias, string|ICompositeExpression|null $condition = null): self { $this->queryBuilder->leftJoin( $this->quoteAlias($fromAlias), $this->getTableName($join), @@ -771,24 +455,8 @@ public function leftJoin($fromAlias, $join, $alias, $condition = null) { return $this; } - /** - * Creates and adds a right join to the query. - * - * - * $qb = $conn->getQueryBuilder() - * ->select('u.name') - * ->from('users', 'u') - * ->rightJoin('u', 'phonenumbers', 'p', 'p.is_primary = 1'); - * - * - * @param string $fromAlias The alias that points to a from clause. - * @param string $join The table name to join. - * @param string $alias The alias of the join table. - * @param string|ICompositeExpression|null $condition The condition for the join. - * - * @return $this This QueryBuilder instance. - */ - public function rightJoin($fromAlias, $join, $alias, $condition = null) { + #[Override] + public function rightJoin(string $fromAlias, string|IQueryFunction $join, string $alias, string|ICompositeExpression|null $condition = null): self { $this->queryBuilder->rightJoin( $this->quoteAlias($fromAlias), $this->getTableName($join), @@ -799,22 +467,8 @@ public function rightJoin($fromAlias, $join, $alias, $condition = null) { return $this; } - /** - * Sets a new value for a column in a bulk update query. - * - * - * $qb = $conn->getQueryBuilder() - * ->update('users', 'u') - * ->set('u.password', md5('password')) - * ->where('u.id = ?'); - * - * - * @param string $key The column to set. - * @param ILiteral|IParameter|IQueryFunction|string $value The value, expression, placeholder, etc. - * - * @return $this This QueryBuilder instance. - */ - public function set($key, $value) { + #[Override] + public function set(string $key, ILiteral|IParameter|IQueryFunction|string $value): self { $this->queryBuilder->set( $this->helper->quoteColumnName($key), $this->helper->quoteColumnName($value) @@ -823,34 +477,8 @@ public function set($key, $value) { return $this; } - /** - * Specifies one or more restrictions to the query result. - * Replaces any previously specified restrictions, if any. - * - * - * $qb = $conn->getQueryBuilder() - * ->select('u.name') - * ->from('users', 'u') - * ->where('u.id = ?'); - * - * // You can optionally programmatically build and/or expressions - * $qb = $conn->getQueryBuilder(); - * - * $or = $qb->expr()->orx( - * $qb->expr()->eq('u.id', 1), - * $qb->expr()->eq('u.id', 2), - * ); - * - * $qb->update('users', 'u') - * ->set('u.password', md5('password')) - * ->where($or); - * - * - * @param mixed ...$predicates The restriction predicates. - * - * @return $this This QueryBuilder instance. - */ - public function where(...$predicates) { + #[Override] + public function where(...$predicates): self { if ($this->nonEmptyWhere && $this->systemConfig->getValue('debug', false)) { // Only logging a warning, not throwing for now. $e = new QueryException('Using where() on non-empty WHERE part, please verify it is intentional to not call andWhere() or orWhere() instead. Otherwise consider creating a new query builder object or call resetQueryPart(\'where\') first.'); @@ -867,25 +495,8 @@ public function where(...$predicates) { return $this; } - /** - * Adds one or more restrictions to the query results, forming a logical - * conjunction with any previously specified restrictions. - * - * - * $qb = $conn->getQueryBuilder() - * ->select('u') - * ->from('users', 'u') - * ->where('u.username LIKE ?') - * ->andWhere('u.is_active = 1'); - * - * - * @param mixed ...$where The query restrictions. - * - * @return $this This QueryBuilder instance. - * - * @see where() - */ - public function andWhere(...$where) { + #[Override] + public function andWhere(...$where): self { $this->nonEmptyWhere = true; call_user_func_array( [$this->queryBuilder, 'andWhere'], @@ -895,25 +506,8 @@ public function andWhere(...$where) { return $this; } - /** - * Adds one or more restrictions to the query results, forming a logical - * disjunction with any previously specified restrictions. - * - * - * $qb = $conn->getQueryBuilder() - * ->select('u.name') - * ->from('users', 'u') - * ->where('u.id = 1') - * ->orWhere('u.id = 2'); - * - * - * @param mixed ...$where The WHERE statement. - * - * @return $this This QueryBuilder instance. - * - * @see where() - */ - public function orWhere(...$where) { + #[Override] + public function orWhere(...$where): self { $this->nonEmptyWhere = true; call_user_func_array( [$this->queryBuilder, 'orWhere'], @@ -923,22 +517,8 @@ public function orWhere(...$where) { return $this; } - /** - * Specifies a grouping over the results of the query. - * Replaces any previously specified groupings, if any. - * - * - * $qb = $conn->getQueryBuilder() - * ->select('u.name') - * ->from('users', 'u') - * ->groupBy('u.id'); - * - * - * @param mixed ...$groupBys The grouping expression. - * - * @return $this This QueryBuilder instance. - */ - public function groupBy(...$groupBys) { + #[Override] + public function groupBy(...$groupBys): self { if (count($groupBys) === 1 && is_array($groupBys[0])) { $groupBys = $groupBys[0]; } @@ -951,22 +531,8 @@ public function groupBy(...$groupBys) { return $this; } - /** - * Adds a grouping expression to the query. - * - * - * $qb = $conn->getQueryBuilder() - * ->select('u.name') - * ->from('users', 'u') - * ->groupBy('u.lastLogin'); - * ->addGroupBy('u.createdAt') - * - * - * @param mixed ...$groupBy The grouping expression. - * - * @return $this This QueryBuilder instance. - */ - public function addGroupBy(...$groupBy) { + #[Override] + public function addGroupBy(...$groupBy): self { call_user_func_array( [$this->queryBuilder, 'addGroupBy'], $this->helper->quoteColumnNames($groupBy) @@ -975,26 +541,8 @@ public function addGroupBy(...$groupBy) { return $this; } - /** - * Sets a value for a column in an insert query. - * - * - * $qb = $conn->getQueryBuilder() - * ->insert('users') - * ->values( - * array( - * 'name' => '?' - * ) - * ) - * ->setValue('password', '?'); - * - * - * @param string $column The column into which the value should be inserted. - * @param IParameter|string $value The value that should be inserted into the column. - * - * @return $this This QueryBuilder instance. - */ - public function setValue($column, $value) { + #[Override] + public function setValue(string $column, IParameter|IQueryFunction|string $value): self { $this->queryBuilder->setValue( $this->helper->quoteColumnName($column), (string)$value @@ -1003,26 +551,8 @@ public function setValue($column, $value) { return $this; } - /** - * Specifies values for an insert query indexed by column names. - * Replaces any previous values, if any. - * - * - * $qb = $conn->getQueryBuilder() - * ->insert('users') - * ->values( - * array( - * 'name' => '?', - * 'password' => '?' - * ) - * ); - * - * - * @param array $values The values to specify for the insert query indexed by column names. - * - * @return $this This QueryBuilder instance. - */ - public function values(array $values) { + #[Override] + public function values(array $values): self { $quotedValues = []; foreach ($values as $key => $value) { $quotedValues[$this->helper->quoteColumnName($key)] = $value; @@ -1033,15 +563,8 @@ public function values(array $values) { return $this; } - /** - * Specifies a restriction over the groups of the query. - * Replaces any previous having restrictions, if any. - * - * @param mixed ...$having The restriction over the groups. - * - * @return $this This QueryBuilder instance. - */ - public function having(...$having) { + #[Override] + public function having(...$having): self { call_user_func_array( [$this->queryBuilder, 'having'], $having @@ -1050,15 +573,8 @@ public function having(...$having) { return $this; } - /** - * Adds a restriction over the groups of the query, forming a logical - * conjunction with any existing having restrictions. - * - * @param mixed ...$having The restriction to append. - * - * @return $this This QueryBuilder instance. - */ - public function andHaving(...$having) { + #[Override] + public function andHaving(...$having): self { call_user_func_array( [$this->queryBuilder, 'andHaving'], $having @@ -1067,15 +583,8 @@ public function andHaving(...$having) { return $this; } - /** - * Adds a restriction over the groups of the query, forming a logical - * disjunction with any existing having restrictions. - * - * @param mixed ...$having The restriction to add. - * - * @return $this This QueryBuilder instance. - */ - public function orHaving(...$having) { + #[Override] + public function orHaving(...$having): self { call_user_func_array( [$this->queryBuilder, 'orHaving'], $having @@ -1084,17 +593,9 @@ public function orHaving(...$having) { return $this; } - /** - * Specifies an ordering for the query results. - * Replaces any previously specified orderings, if any. - * - * @param string|IQueryFunction|ILiteral|IParameter $sort The ordering expression. - * @param string $order The ordering direction. - * - * @return $this This QueryBuilder instance. - */ - public function orderBy($sort, $order = null) { - if ($order !== null && !in_array(strtoupper((string)$order), ['ASC', 'DESC'], true)) { + #[Override] + public function orderBy(string|ILiteral|IParameter|IQueryFunction $sort, ?string $order = null): self { + if ($order !== null && !in_array(strtoupper($order), ['ASC', 'DESC'], true)) { $order = null; } @@ -1106,16 +607,9 @@ public function orderBy($sort, $order = null) { return $this; } - /** - * Adds an ordering to the query results. - * - * @param string|ILiteral|IParameter|IQueryFunction $sort The ordering expression. - * @param string $order The ordering direction. - * - * @return $this This QueryBuilder instance. - */ - public function addOrderBy($sort, $order = null) { - if ($order !== null && !in_array(strtoupper((string)$order), ['ASC', 'DESC'], true)) { + #[Override] + public function addOrderBy(string|ILiteral|IParameter|IQueryFunction $sort, ?string $order = null): self { + if ($order !== null && !in_array(strtoupper($order), ['ASC', 'DESC'], true)) { $order = null; } @@ -1127,177 +621,57 @@ public function addOrderBy($sort, $order = null) { return $this; } - /** - * Gets a query part by its name. - * - * @param string $queryPartName - * - * @return mixed - * @deprecated 30.0.0 This function is going to be removed with the next Doctrine/DBAL update - * and we can not fix this in our wrapper. Please track the details you need, outside the object. - */ - public function getQueryPart($queryPartName) { + #[Override] + public function getQueryPart(string $queryPartName): mixed { $this->logger->debug(IQueryBuilder::class . '::' . __FUNCTION__ . ' is deprecated and will be removed soon.', ['exception' => new \Exception('Deprecated call to ' . __METHOD__)]); return $this->queryBuilder->getQueryPart($queryPartName); } - /** - * Gets all query parts. - * - * @return array - * @deprecated 30.0.0 This function is going to be removed with the next Doctrine/DBAL update - * and we can not fix this in our wrapper. Please track the details you need, outside the object. - */ - public function getQueryParts() { + #[Override] + public function getQueryParts(): array { $this->logger->debug(IQueryBuilder::class . '::' . __FUNCTION__ . ' is deprecated and will be removed soon.', ['exception' => new \Exception('Deprecated call to ' . __METHOD__)]); return $this->queryBuilder->getQueryParts(); } - /** - * Resets SQL parts. - * - * @param array|null $queryPartNames - * - * @return $this This QueryBuilder instance. - * @deprecated 30.0.0 This function is going to be removed with the next Doctrine/DBAL update - * and we can not fix this in our wrapper. Please create a new IQueryBuilder instead. - */ - public function resetQueryParts($queryPartNames = null) { + #[Override] + public function resetQueryParts(?array $queryPartNames = null): self { $this->logger->debug(IQueryBuilder::class . '::' . __FUNCTION__ . ' is deprecated and will be removed soon.', ['exception' => new \Exception('Deprecated call to ' . __METHOD__)]); $this->queryBuilder->resetQueryParts($queryPartNames); return $this; } - /** - * Resets a single SQL part. - * - * @param string $queryPartName - * - * @return $this This QueryBuilder instance. - * @deprecated 30.0.0 This function is going to be removed with the next Doctrine/DBAL update - * and we can not fix this in our wrapper. Please create a new IQueryBuilder instead. - */ - public function resetQueryPart($queryPartName) { + #[Override] + public function resetQueryPart(string $queryPartName): self { $this->logger->debug(IQueryBuilder::class . '::' . __FUNCTION__ . ' is deprecated and will be removed soon.', ['exception' => new \Exception('Deprecated call to ' . __METHOD__)]); $this->queryBuilder->resetQueryPart($queryPartName); return $this; } - /** - * Creates a new named parameter and bind the value $value to it. - * - * This method provides a shortcut for PDOStatement::bindValue - * when using prepared statements. - * - * The parameter $value specifies the value that you want to bind. If - * $placeholder is not provided bindValue() will automatically create a - * placeholder for you. An automatic placeholder will be of the name - * ':dcValue1', ':dcValue2' etc. - * - * For more information see {@link https://www.php.net/pdostatement-bindparam} - * - * Example: - * - * $value = 2; - * $q->eq( 'id', $q->bindValue( $value ) ); - * $stmt = $q->executeQuery(); // executed with 'id = 2' - * - * - * @license New BSD License - * @link http://www.zetacomponents.org - * - * @param mixed $value - * @param IQueryBuilder::PARAM_* $type - * @param string $placeHolder The name to bind with. The string must start with a colon ':'. - * - * @return IParameter the placeholder name used. - */ - public function createNamedParameter($value, $type = IQueryBuilder::PARAM_STR, $placeHolder = null) { + #[Override] + public function createNamedParameter(mixed $value, mixed $type = IQueryBuilder::PARAM_STR, ?string $placeHolder = null): IParameter { return new Parameter($this->queryBuilder->createNamedParameter($value, $type, $placeHolder)); } - /** - * Creates a new positional parameter and bind the given value to it. - * - * Attention: If you are using positional parameters with the query builder you have - * to be very careful to bind all parameters in the order they appear in the SQL - * statement , otherwise they get bound in the wrong order which can lead to serious - * bugs in your code. - * - * Example: - * - * $qb = $conn->getQueryBuilder(); - * $qb->select('u.*') - * ->from('users', 'u') - * ->where('u.username = ' . $qb->createPositionalParameter('Foo', IQueryBuilder::PARAM_STR)) - * ->orWhere('u.username = ' . $qb->createPositionalParameter('Bar', IQueryBuilder::PARAM_STR)) - * - * - * @param mixed $value - * @param IQueryBuilder::PARAM_* $type - * - * @return IParameter - */ - public function createPositionalParameter($value, $type = IQueryBuilder::PARAM_STR) { + #[Override] + public function createPositionalParameter(mixed $value, mixed $type = IQueryBuilder::PARAM_STR): IParameter { return new Parameter($this->queryBuilder->createPositionalParameter($value, $type)); } - /** - * Creates a new parameter - * - * Example: - * - * $qb = $conn->getQueryBuilder(); - * $qb->select('u.*') - * ->from('users', 'u') - * ->where('u.username = ' . $qb->createParameter('name')) - * ->setParameter('name', 'Bar', IQueryBuilder::PARAM_STR)) - * - * - * @param string $name - * - * @return IParameter - */ - public function createParameter($name) { + #[Override] + public function createParameter(string $name): IParameter { return new Parameter(':' . $name); } - /** - * Creates a new function - * - * Attention: Column names inside the call have to be quoted before hand - * - * Example: - * - * $qb = $conn->getQueryBuilder(); - * $qb->select($qb->createFunction('COUNT(*)')) - * ->from('users', 'u') - * echo $qb->getSQL(); // SELECT COUNT(*) FROM `users` u - * - * - * $qb = $conn->getQueryBuilder(); - * $qb->select($qb->createFunction('COUNT(`column`)')) - * ->from('users', 'u') - * echo $qb->getSQL(); // SELECT COUNT(`column`) FROM `users` u - * - * - * @param string $call - * - * @return IQueryFunction - */ - public function createFunction($call) { + #[Override] + public function createFunction(string $call): IQueryFunction { return new QueryFunction($call); } - /** - * Used to get the id of the last inserted element - * @return int - * @throws \BadMethodCallException When being called before an insert query has been run. - */ + #[Override] public function getLastInsertId(): int { - if ($this->getType() === \Doctrine\DBAL\Query\QueryBuilder::INSERT && $this->lastInsertedTable) { + if ($this->getType() === \Doctrine\DBAL\Query\QueryBuilder::INSERT && $this->lastInsertedTable !== null) { // lastInsertId() needs the prefix but no quotes $table = $this->prefixTableName($this->lastInsertedTable); return $this->connection->lastInsertId($table); @@ -1306,13 +680,8 @@ public function getLastInsertId(): int { throw new \BadMethodCallException('Invalid call to getLastInsertId without using insert() before.'); } - /** - * Returns the table name quoted and with database prefix as needed by the implementation - * - * @param string|IQueryFunction $table - * @return string - */ - public function getTableName($table) { + #[Override] + public function getTableName(string|IQueryFunction $table): string { if ($table instanceof IQueryFunction) { return (string)$table; } @@ -1321,14 +690,7 @@ public function getTableName($table) { return $this->helper->quoteColumnName($table); } - /** - * Returns the table name with database prefix as needed by the implementation - * - * Was protected until version 30. - * - * @param string $table - * @return string - */ + #[Override] public function prefixTableName(string $table): string { if ($this->automaticTablePrefix === false || str_starts_with($table, '*PREFIX*')) { return $table; @@ -1337,14 +699,8 @@ public function prefixTableName(string $table): string { return '*PREFIX*' . $table; } - /** - * Returns the column name quoted and with table alias prefix as needed by the implementation - * - * @param string $column - * @param string $tableAlias - * @return string - */ - public function getColumnName($column, $tableAlias = '') { + #[Override] + public function getColumnName(string $column, string $tableAlias = ''): string { if ($tableAlias !== '') { $tableAlias .= '.'; } @@ -1352,14 +708,8 @@ public function getColumnName($column, $tableAlias = '') { return $this->helper->quoteColumnName($tableAlias . $column); } - /** - * Returns the column name quoted and with table alias prefix as needed by the implementation - * - * @param string $alias - * @return string - */ - public function quoteAlias($alias) { - if ($alias === '' || $alias === null) { + public function quoteAlias(string $alias): string { + if ($alias === '') { return $alias; } @@ -1370,10 +720,12 @@ public function escapeLikeParameter(string $parameter): string { return $this->connection->escapeLikeParameter($parameter); } + #[Override] public function hintShardKey(string $column, mixed $value, bool $overwrite = false): self { return $this; } + #[Override] public function runAcrossAllShards(): self { // noop return $this; diff --git a/lib/private/DB/QueryBuilder/QuoteHelper.php b/lib/private/DB/QueryBuilder/QuoteHelper.php index 7ce4b359638d4..4ebbc53ed4e5c 100644 --- a/lib/private/DB/QueryBuilder/QuoteHelper.php +++ b/lib/private/DB/QueryBuilder/QuoteHelper.php @@ -12,11 +12,7 @@ use OCP\DB\QueryBuilder\IQueryFunction; class QuoteHelper { - /** - * @param array|string|ILiteral|IParameter|IQueryFunction $strings string, Literal or Parameter - * @return array|string - */ - public function quoteColumnNames($strings) { + public function quoteColumnNames(array|string|ILiteral|IParameter|IQueryFunction $strings): array|string { if (!is_array($strings)) { return $this->quoteColumnName($strings); } @@ -29,23 +25,15 @@ public function quoteColumnNames($strings) { return $return; } - /** - * @param string|ILiteral|IParameter|IQueryFunction $string string, Literal or Parameter - * @return string - */ - public function quoteColumnName($string) { + public function quoteColumnName(string|ILiteral|IParameter|IQueryFunction $string): string { if ($string instanceof IParameter || $string instanceof ILiteral || $string instanceof IQueryFunction) { return (string)$string; } - if ($string === null || $string === 'null' || $string === '*') { + if ($string === 'null' || $string === '*') { return $string; } - if (!is_string($string)) { - throw new \InvalidArgumentException('Only strings, Literals and Parameters are allowed'); - } - $string = str_replace(' AS ', ' as ', $string); if (substr_count($string, ' as ')) { return implode(' as ', array_map([$this, 'quoteColumnName'], explode(' as ', $string, 2))); diff --git a/lib/private/DB/QueryBuilder/Sharded/ShardedQueryBuilder.php b/lib/private/DB/QueryBuilder/Sharded/ShardedQueryBuilder.php index 2694a116af43f..36443d9bbab4e 100644 --- a/lib/private/DB/QueryBuilder/Sharded/ShardedQueryBuilder.php +++ b/lib/private/DB/QueryBuilder/Sharded/ShardedQueryBuilder.php @@ -12,8 +12,11 @@ use OC\DB\QueryBuilder\ExtendedQueryBuilder; use OC\DB\QueryBuilder\Parameter; use OCP\DB\IResult; +use OCP\DB\QueryBuilder\IParameter; use OCP\DB\QueryBuilder\IQueryBuilder; +use OCP\DB\QueryBuilder\IQueryFunction; use OCP\IDBConnection; +use Override; /** * A special query builder that automatically distributes queries over multiple database shards. @@ -83,11 +86,13 @@ private function getKeyValue($value): array { } } - public function where(...$predicates) { + #[Override] + public function where(...$predicates): self { return $this->andWhere(...$predicates); } - public function andWhere(...$where) { + #[Override] + public function andWhere(...$where): self { if ($where) { foreach ($where as $predicate) { $this->tryLoadShardKey($predicate); @@ -158,14 +163,18 @@ private function tryExtractShardKeys($predicate, string $column): array { return []; } - public function set($key, $value) { + #[Override] + public function set($key, $value): self { if ($this->shardDefinition && $key === $this->shardDefinition->shardKey) { + // TODO dead code? $updateShardKey = $value; } - return parent::set($key, $value); + parent::set($key, $value); + return $this; } - public function setValue($column, $value) { + #[Override] + public function setValue(string $column, IParameter|IQueryFunction|string $value): self { if ($this->shardDefinition) { if ($this->shardDefinition->isKey($column)) { $this->primaryKeys[] = $value; @@ -174,10 +183,12 @@ public function setValue($column, $value) { $this->shardKeys[] = $value; } } - return parent::setValue($column, $value); + parent::setValue($column, $value); + return $this; } - public function values(array $values) { + #[Override] + public function values(array $values): self { foreach ($values as $column => $value) { $this->setValue($column, $value); } @@ -193,33 +204,35 @@ private function actOnTable(string $table): void { } } - public function from($from, $alias = null) { - if (is_string($from) && $from) { + #[Override] + public function from(string|IQueryFunction $from, ?string $alias = null): self { + if (is_string($from)) { $this->actOnTable($from); } - return parent::from($from, $alias); + parent::from($from, $alias); + return $this; } - public function update($update = null, $alias = null) { - if (is_string($update) && $update) { - $this->actOnTable($update); - } - return parent::update($update, $alias); + #[Override] + public function update(string $update, ?string $alias = null): self { + $this->actOnTable($update); + parent::update($update, $alias); + return $this; } - public function insert($insert = null) { - if (is_string($insert) && $insert) { - $this->insertTable = $insert; - $this->actOnTable($insert); - } - return parent::insert($insert); + #[Override] + public function insert(string $insert): self { + $this->insertTable = $insert; + $this->actOnTable($insert); + parent::insert($insert); + return $this; } - public function delete($delete = null, $alias = null) { - if (is_string($delete) && $delete) { - $this->actOnTable($delete); - } - return parent::delete($delete, $alias); + #[Override] + public function delete(string $delete, ?string $alias = null): self { + $this->actOnTable($delete); + parent::delete($delete, $alias); + return $this; } private function checkJoin(string $table): void { @@ -235,67 +248,82 @@ private function checkJoin(string $table): void { } } - public function innerJoin($fromAlias, $join, $alias, $condition = null) { + #[Override] + public function innerJoin($fromAlias, $join, $alias, $condition = null): self { if (is_string($join)) { $this->checkJoin($join); } - return parent::innerJoin($fromAlias, $join, $alias, $condition); + parent::innerJoin($fromAlias, $join, $alias, $condition); + return $this; } - public function leftJoin($fromAlias, $join, $alias, $condition = null) { + #[Override] + public function leftJoin($fromAlias, $join, $alias, $condition = null): self { if (is_string($join)) { $this->checkJoin($join); } - return parent::leftJoin($fromAlias, $join, $alias, $condition); + parent::leftJoin($fromAlias, $join, $alias, $condition); + return $this; } - public function rightJoin($fromAlias, $join, $alias, $condition = null) { + #[Override] + public function rightJoin($fromAlias, $join, $alias, $condition = null): self { if ($this->shardDefinition) { throw new InvalidShardedQueryException("Sharded query on {$this->shardDefinition->table} isn't allowed to right join"); } - return parent::rightJoin($fromAlias, $join, $alias, $condition); + parent::rightJoin($fromAlias, $join, $alias, $condition); + return $this; } - public function join($fromAlias, $join, $alias, $condition = null) { + #[Override] + public function join($fromAlias, $join, $alias, $condition = null): self { return $this->innerJoin($fromAlias, $join, $alias, $condition); } - public function setMaxResults($maxResults) { - if ($maxResults > 0) { - $this->limit = (int)$maxResults; + #[Override] + public function setMaxResults(?int $maxResults): self { + if ($maxResults !== null && $maxResults > 0) { + $this->limit = $maxResults; } - return parent::setMaxResults($maxResults); + parent::setMaxResults($maxResults); + return $this; } - public function setFirstResult($firstResult) { + #[Override] + public function setFirstResult(int $firstResult): self { if ($firstResult > 0) { - $this->offset = (int)$firstResult; + $this->offset = $firstResult; } if ($this->shardDefinition && count($this->shardDefinition->shards) > 1) { // we have to emulate offset return $this; } else { - return parent::setFirstResult($firstResult); + parent::setFirstResult($firstResult); + return $this; } } - public function addOrderBy($sort, $order = null) { + #[Override] + public function addOrderBy($sort, $order = null): self { if ($order !== null && !in_array(strtoupper((string)$order), ['ASC', 'DESC'], true)) { $order = null; } $this->registerOrder((string)$sort, (string)($order ?? 'ASC')); - return parent::addOrderBy($sort, $order); + parent::addOrderBy($sort, $order); + return $this; } - public function orderBy($sort, $order = null) { + #[Override] + public function orderBy($sort, $order = null): self { if ($order !== null && !in_array(strtoupper((string)$order), ['ASC', 'DESC'], true)) { $order = null; } $this->sortList = []; $this->registerOrder((string)$sort, (string)($order ?? 'ASC')); - return parent::orderBy($sort, $order); + parent::orderBy($sort, $order); + return $this; } private function registerOrder(string $column, string $order): void { @@ -308,6 +336,7 @@ private function registerOrder(string $column, string $order): void { ]; } + #[Override] public function hintShardKey(string $column, mixed $value, bool $overwrite = false): self { if ($overwrite) { $this->primaryKeys = []; @@ -322,6 +351,7 @@ public function hintShardKey(string $column, mixed $value, bool $overwrite = fal return $this; } + #[Override] public function runAcrossAllShards(): self { $this->allShards = true; return $this; @@ -364,6 +394,7 @@ public function validate(): void { } } + #[Override] public function executeQuery(?IDBConnection $connection = null): IResult { $this->validate(); if ($this->shardDefinition) { @@ -373,6 +404,7 @@ public function executeQuery(?IDBConnection $connection = null): IResult { return parent::executeQuery($connection); } + #[Override] public function executeStatement(?IDBConnection $connection = null): int { $this->validate(); if ($this->shardDefinition) { @@ -403,6 +435,7 @@ public function executeStatement(?IDBConnection $connection = null): int { return parent::executeStatement($connection); } + #[Override] public function getLastInsertId(): int { if ($this->lastInsertId) { return $this->lastInsertId; @@ -414,6 +447,4 @@ public function getLastInsertId(): int { return parent::getLastInsertId(); } } - - } diff --git a/lib/private/Files/Cache/CacheQueryBuilder.php b/lib/private/Files/Cache/CacheQueryBuilder.php index 5492452273bba..03474261048cc 100644 --- a/lib/private/Files/Cache/CacheQueryBuilder.php +++ b/lib/private/Files/Cache/CacheQueryBuilder.php @@ -45,7 +45,7 @@ public function selectTagUsage(): self { return $this; } - public function selectFileCache(?string $alias = null, bool $joinExtendedCache = true) { + public function selectFileCache(?string $alias = null, bool $joinExtendedCache = true): self { $name = $alias ?: 'filecache'; $this->select("$name.fileid", 'storage', 'path', 'path_hash', "$name.parent", "$name.name", 'mimetype', 'mimepart', 'size', 'mtime', 'storage_mtime', 'encrypted', "$name.etag", "$name.permissions", 'checksum', 'unencrypted_size') @@ -61,13 +61,13 @@ public function selectFileCache(?string $alias = null, bool $joinExtendedCache = return $this; } - public function whereStorageId(int $storageId) { + public function whereStorageId(int $storageId): self { $this->andWhere($this->expr()->eq('storage', $this->createNamedParameter($storageId, IQueryBuilder::PARAM_INT))); return $this; } - public function whereFileId(int $fileId) { + public function whereFileId(int $fileId): self { $alias = $this->alias; if ($alias) { $alias .= '.'; @@ -86,7 +86,7 @@ public function wherePath(string $path) { return $this; } - public function whereParent(int $parent) { + public function whereParent(int $parent): self { $alias = $this->alias; if ($alias) { $alias .= '.'; @@ -99,7 +99,7 @@ public function whereParent(int $parent) { return $this; } - public function whereParentInParameter(string $parameter) { + public function whereParentInParameter(string $parameter): self { $alias = $this->alias; if ($alias) { $alias .= '.'; diff --git a/lib/private/Group/Database.php b/lib/private/Group/Database.php index 62a3990c31999..98d96016d4b2a 100644 --- a/lib/private/Group/Database.php +++ b/lib/private/Group/Database.php @@ -464,7 +464,7 @@ public function countDisabledInGroup(string $gid): int { $this->fixDI(); $query = $this->dbConn->getQueryBuilder(); - $query->select($query->createFunction('COUNT(DISTINCT ' . $query->getColumnName('uid') . ')')) + $query->select($query->func()->countDistinct('uid')) ->from('preferences', 'p') ->innerJoin('p', 'group_user', 'g', $query->expr()->eq('p.userid', 'g.uid')) ->where($query->expr()->eq('appid', $query->createNamedParameter('core'))) diff --git a/lib/private/OpenMetrics/Exporters/ActiveUsers.php b/lib/private/OpenMetrics/Exporters/ActiveUsers.php index 43425fd37e20f..2c568c89ec825 100644 --- a/lib/private/OpenMetrics/Exporters/ActiveUsers.php +++ b/lib/private/OpenMetrics/Exporters/ActiveUsers.php @@ -47,7 +47,7 @@ public function metrics(): Generator { ]; foreach ($timeFrames as $label => $time) { $qb = $this->connection->getQueryBuilder(); - $result = $qb->select($qb->createFunction('COUNT(DISTINCT ' . $qb->getColumnName('uid') . ')')) + $result = $qb->select($qb->func()->countDistinct('uid')) ->from('authtoken') ->where($qb->expr()->gte('last_activity', $qb->createNamedParameter($time))) ->executeQuery(); diff --git a/lib/public/DB/QueryBuilder/IFunctionBuilder.php b/lib/public/DB/QueryBuilder/IFunctionBuilder.php index 480ec1cb1ac8c..3b3be6e08596e 100644 --- a/lib/public/DB/QueryBuilder/IFunctionBuilder.php +++ b/lib/public/DB/QueryBuilder/IFunctionBuilder.php @@ -105,6 +105,17 @@ public function subtract($x, $y): IQueryFunction; */ public function count($count = '', $alias = ''): IQueryFunction; + /** + * Get the count of distinct entries. + * + * @param string|ILiteral|IParameter|IQueryFunction $count The input to be counted + * @param string $alias Alias for the counter + * + * @return IQueryFunction + * @since 34.0.0 + */ + public function countDistinct(string|ILiteral|IParameter|IQueryFunction $count = '', string $alias = ''): IQueryFunction; + /** * @param string|ILiteral|IParameter|IQueryFunction $field The input to be measured * @param string $alias Alias for the length diff --git a/lib/public/DB/QueryBuilder/IQueryBuilder.php b/lib/public/DB/QueryBuilder/IQueryBuilder.php index b1c483522ee44..846320b562ef0 100644 --- a/lib/public/DB/QueryBuilder/IQueryBuilder.php +++ b/lib/public/DB/QueryBuilder/IQueryBuilder.php @@ -11,6 +11,7 @@ use Doctrine\DBAL\Connection; use Doctrine\DBAL\ParameterType; use Doctrine\DBAL\Types\Types; +use OCP\AppFramework\Attribute\Consumable; use OCP\DB\Exception; use OCP\DB\IResult; use OCP\IDBConnection; @@ -21,6 +22,7 @@ * * @psalm-taint-specialize */ +#[Consumable(since: '8.2.0')] interface IQueryBuilder { /** * @since 9.0.0 @@ -126,7 +128,7 @@ interface IQueryBuilder { * owncloud database prefix automatically. * @since 8.2.0 */ - public function automaticTablePrefix($enabled); + public function automaticTablePrefix(bool $enabled): void; /** * Gets an ExpressionBuilder used for object-oriented construction of query expressions. @@ -142,10 +144,9 @@ public function automaticTablePrefix($enabled); * For more complex expression construction, consider storing the expression * builder object in a local variable. * - * @return \OCP\DB\QueryBuilder\IExpressionBuilder * @since 8.2.0 */ - public function expr(); + public function expr(): IExpressionBuilder; /** * Gets an FunctionBuilder used for object-oriented construction of query functions. @@ -161,26 +162,24 @@ public function expr(); * For more complex function construction, consider storing the function * builder object in a local variable. * - * @return \OCP\DB\QueryBuilder\IFunctionBuilder * @since 12.0.0 */ - public function func(); + public function func(): IFunctionBuilder; /** * Gets the type of the currently built query. * - * @return integer + * @deprecated Since 34.0.0, if necessary, track the type of the query being built outside of the builder. * @since 8.2.0 */ - public function getType(); + public function getType(): int; /** * Gets the associated DBAL Connection for this query builder. * - * @return \OCP\IDBConnection * @since 8.2.0 */ - public function getConnection(); + public function getConnection(): IDBConnection; /** * Gets the state of this query builder instance. @@ -190,7 +189,7 @@ public function getConnection(); * @deprecated 30.0.0 This function is going to be removed with the next Doctrine/DBAL update * and we can not fix this in our wrapper. */ - public function getState(); + public function getState(): int; /** * Execute for select statements @@ -229,7 +228,7 @@ public function executeStatement(?IDBConnection $connection = null): int; * @return string The SQL query string. * @since 8.2.0 */ - public function getSQL(); + public function getSQL(): string; /** * Sets a query parameter for the query being constructed. @@ -246,10 +245,9 @@ public function getSQL(); * @param mixed $value The parameter value. * @param string|null|int $type One of the IQueryBuilder::PARAM_* constants. * - * @return $this This QueryBuilder instance. * @since 8.2.0 */ - public function setParameter($key, $value, $type = null); + public function setParameter(string|int $key, mixed $value, string|null|int $type = null): self; /** * Sets a collection of query parameters for the query being constructed. @@ -268,10 +266,9 @@ public function setParameter($key, $value, $type = null); * @param array $params The query parameters to set. * @param array $types The query parameters types to set. * - * @return $this This QueryBuilder instance. * @since 8.2.0 */ - public function setParameters(array $params, array $types = []); + public function setParameters(array $params, array $types = []): self; /** * Gets all defined query parameters for the query being constructed indexed by parameter index or name. @@ -279,17 +276,17 @@ public function setParameters(array $params, array $types = []); * @return array The currently defined query parameters indexed by parameter index or name. * @since 8.2.0 */ - public function getParameters(); + public function getParameters(): array; /** * Gets a (previously set) query parameter of the query being constructed. * - * @param mixed $key The key (index or name) of the bound parameter. + * @param int|string $key The key (index or name) of the bound parameter. * * @return mixed The value of the bound parameter. * @since 8.2.0 */ - public function getParameter($key); + public function getParameter(int|string $key): mixed; /** * Gets all defined query parameter types for the query being constructed indexed by parameter index or name. @@ -297,17 +294,17 @@ public function getParameter($key); * @return array The currently defined query parameter types indexed by parameter index or name. * @since 8.2.0 */ - public function getParameterTypes(); + public function getParameterTypes(): array; /** * Gets a (previously set) query parameter type of the query being constructed. * - * @param mixed $key The key (index or name) of the bound parameter type. + * @param int|string $key The key (index or name) of the bound parameter type. * * @return mixed The value of the bound parameter type. * @since 8.2.0 */ - public function getParameterType($key); + public function getParameterType(int|string $key): mixed; /** * Sets the position of the first result to retrieve (the "offset"). @@ -317,7 +314,7 @@ public function getParameterType($key); * @return $this This QueryBuilder instance. * @since 8.2.0 */ - public function setFirstResult($firstResult); + public function setFirstResult(int $firstResult): self; /** * Gets the position of the first result the query object was set to retrieve (the "offset"). @@ -326,17 +323,16 @@ public function setFirstResult($firstResult); * @return int The position of the first result. * @since 8.2.0 */ - public function getFirstResult(); + public function getFirstResult(): int; /** * Sets the maximum number of results to retrieve (the "limit"). * * @param int|null $maxResults The maximum number of results to retrieve. * - * @return $this This QueryBuilder instance. * @since 8.2.0 */ - public function setMaxResults($maxResults); + public function setMaxResults(?int $maxResults): self; /** * Gets the maximum number of results the query object was set to retrieve (the "limit"). @@ -345,7 +341,7 @@ public function setMaxResults($maxResults); * @return int|null The maximum number of results. * @since 8.2.0 */ - public function getMaxResults(); + public function getMaxResults(): ?int; /** * Specifies an item that is to be returned in the query result. @@ -360,12 +356,11 @@ public function getMaxResults(); * * @param mixed ...$selects The selection expressions. * - * @return $this This QueryBuilder instance. * @since 8.2.0 * * @psalm-taint-sink sql $selects */ - public function select(...$selects); + public function select(...$selects): self; /** * Specifies an item that is to be returned with a different name in the query result. @@ -377,16 +372,15 @@ public function select(...$selects); * ->leftJoin('u', 'phonenumbers', 'p', 'u.id = p.user_id'); * * - * @param mixed $select The selection expressions. + * @param string|IParameter|IQueryFunction $select The selection expressions. * @param string $alias The column alias used in the constructed query. * - * @return $this This QueryBuilder instance. * @since 8.2.1 * * @psalm-taint-sink sql $select * @psalm-taint-sink sql $alias */ - public function selectAlias($select, $alias); + public function selectAlias(string|IParameter|IQueryFunction $select, string $alias): self; /** * Specifies an item that is to be returned uniquely in the query result. @@ -397,14 +391,13 @@ public function selectAlias($select, $alias); * ->from('users'); * * - * @param mixed $select The selection expressions. + * @param string|string[] $select The selection expressions. * - * @return $this This QueryBuilder instance. * @since 9.0.0 * * @psalm-taint-sink sql $select */ - public function selectDistinct($select); + public function selectDistinct(string|array $select): self; /** * Adds an item that is to be returned in the query result. @@ -419,12 +412,11 @@ public function selectDistinct($select); * * @param mixed ...$select The selection expression. * - * @return $this This QueryBuilder instance. * @since 8.2.0 * * @psalm-taint-sink sql $select */ - public function addSelect(...$select); + public function addSelect(...$selects): self; /** * Turns the query being built into a bulk delete query that ranges over @@ -438,15 +430,14 @@ public function addSelect(...$select); * * * @param string $delete The table whose rows are subject to the deletion. - * @param string $alias The table alias used in the constructed query. + * @param ?string $alias The table alias used in the constructed query. * - * @return $this This QueryBuilder instance. * @since 8.2.0 * @since 30.0.0 Alias is deprecated and will no longer be used with the next Doctrine/DBAL update * * @psalm-taint-sink sql $delete */ - public function delete($delete = null, $alias = null); + public function delete(string $delete, ?string $alias = null): self; /** * Turns the query being built into a bulk update query that ranges over @@ -461,15 +452,14 @@ public function delete($delete = null, $alias = null); * * * @param string $update The table whose rows are subject to the update. - * @param string $alias The table alias used in the constructed query. + * @param ?string $alias The table alias used in the constructed query. * - * @return $this This QueryBuilder instance. * @since 8.2.0 * @since 30.0.0 Alias is deprecated and will no longer be used with the next Doctrine/DBAL update * * @psalm-taint-sink sql $update */ - public function update($update = null, $alias = null); + public function update(string $update, ?string $alias = null): self; /** * Turns the query being built into an insert query that inserts into @@ -488,12 +478,11 @@ public function update($update = null, $alias = null); * * @param string $insert The table into which the rows should be inserted. * - * @return $this This QueryBuilder instance. * @since 8.2.0 * * @psalm-taint-sink sql $insert */ - public function insert($insert = null); + public function insert(string $insert): self; /** * Creates and adds a query root corresponding to the table identified by the @@ -508,12 +497,11 @@ public function insert($insert = null); * @param string|IQueryFunction $from The table. * @param string|null $alias The alias of the table. * - * @return $this This QueryBuilder instance. * @since 8.2.0 * * @psalm-taint-sink sql $from */ - public function from($from, $alias = null); + public function from(string|IQueryFunction $from, ?string $alias = null): self; /** * Creates and adds a join to the query. @@ -530,7 +518,6 @@ public function from($from, $alias = null); * @param string $alias The alias of the join table. * @param string|ICompositeExpression|null $condition The condition for the join. * - * @return $this This QueryBuilder instance. * @since 8.2.0 * * @psalm-taint-sink sql $fromAlias @@ -538,7 +525,7 @@ public function from($from, $alias = null); * @psalm-taint-sink sql $alias * @psalm-taint-sink sql $condition */ - public function join($fromAlias, $join, $alias, $condition = null); + public function join(string $fromAlias, string|IQueryFunction $join, string $alias, string|ICompositeExpression|null $condition = null): self; /** * Creates and adds a join to the query. @@ -555,7 +542,6 @@ public function join($fromAlias, $join, $alias, $condition = null); * @param string $alias The alias of the join table. * @param string|ICompositeExpression|null $condition The condition for the join. * - * @return $this This QueryBuilder instance. * @since 8.2.0 * * @psalm-taint-sink sql $fromAlias @@ -563,7 +549,7 @@ public function join($fromAlias, $join, $alias, $condition = null); * @psalm-taint-sink sql $alias * @psalm-taint-sink sql $condition */ - public function innerJoin($fromAlias, $join, $alias, $condition = null); + public function innerJoin(string $fromAlias, string|IQueryFunction $join, string $alias, string|ICompositeExpression|null $condition = null): self; /** * Creates and adds a left join to the query. @@ -580,7 +566,6 @@ public function innerJoin($fromAlias, $join, $alias, $condition = null); * @param string $alias The alias of the join table. * @param string|ICompositeExpression|null $condition The condition for the join. * - * @return $this This QueryBuilder instance. * @since 8.2.0 * @since 30.0.0 Allow passing IQueryFunction as parameter for `$join` to allow join with a sub-query. * @@ -589,7 +574,7 @@ public function innerJoin($fromAlias, $join, $alias, $condition = null); * @psalm-taint-sink sql $alias * @psalm-taint-sink sql $condition */ - public function leftJoin($fromAlias, $join, $alias, $condition = null); + public function leftJoin(string $fromAlias, string|IQueryFunction $join, string $alias, string|ICompositeExpression|null $condition = null): self; /** * Creates and adds a right join to the query. @@ -606,7 +591,6 @@ public function leftJoin($fromAlias, $join, $alias, $condition = null); * @param string $alias The alias of the join table. * @param string|ICompositeExpression|null $condition The condition for the join. * - * @return $this This QueryBuilder instance. * @since 8.2.0 * * @psalm-taint-sink sql $fromAlias @@ -614,7 +598,7 @@ public function leftJoin($fromAlias, $join, $alias, $condition = null); * @psalm-taint-sink sql $alias * @psalm-taint-sink sql $condition */ - public function rightJoin($fromAlias, $join, $alias, $condition = null); + public function rightJoin(string $fromAlias, string|IQueryFunction $join, string $alias, string|ICompositeExpression|null $condition = null): self; /** * Sets a new value for a column in a bulk update query. @@ -629,13 +613,12 @@ public function rightJoin($fromAlias, $join, $alias, $condition = null); * @param string $key The column to set. * @param ILiteral|IParameter|IQueryFunction|string $value The value, expression, placeholder, etc. * - * @return $this This QueryBuilder instance. * @since 8.2.0 * * @psalm-taint-sink sql $key * @psalm-taint-sink sql $value */ - public function set($key, $value); + public function set(string $key, ILiteral|IParameter|IQueryFunction|string $value): self; /** * Specifies one or more restrictions to the query result. @@ -662,12 +645,11 @@ public function set($key, $value); * * @param mixed $predicates The restriction predicates. * - * @return $this This QueryBuilder instance. * @since 8.2.0 * * @psalm-taint-sink sql $predicates */ - public function where(...$predicates); + public function where(...$predicates): self; /** * Adds one or more restrictions to the query results, forming a logical @@ -683,14 +665,12 @@ public function where(...$predicates); * * @param mixed ...$where The query restrictions. * - * @return $this This QueryBuilder instance. - * * @see where() * @since 8.2.0 * * @psalm-taint-sink sql $where */ - public function andWhere(...$where); + public function andWhere(...$where): self; /** * Adds one or more restrictions to the query results, forming a logical @@ -706,14 +686,12 @@ public function andWhere(...$where); * * @param mixed ...$where The WHERE statement. * - * @return $this This QueryBuilder instance. - * * @see where() * @since 8.2.0 * * @psalm-taint-sink sql $where */ - public function orWhere(...$where); + public function orWhere(...$where): self; /** * Specifies a grouping over the results of the query. @@ -728,12 +706,11 @@ public function orWhere(...$where); * * @param mixed ...$groupBys The grouping expression. * - * @return $this This QueryBuilder instance. * @since 8.2.0 * * @psalm-taint-sink sql $groupBys */ - public function groupBy(...$groupBys); + public function groupBy(...$groupBys): self; /** * Adds a grouping expression to the query. @@ -748,12 +725,11 @@ public function groupBy(...$groupBys); * * @param mixed ...$groupBy The grouping expression. * - * @return $this This QueryBuilder instance. * @since 8.2.0 * * @psalm-taint-sink sql $groupby */ - public function addGroupBy(...$groupBy); + public function addGroupBy(...$groupBy): self; /** * Sets a value for a column in an insert query. @@ -770,15 +746,14 @@ public function addGroupBy(...$groupBy); * * * @param string $column The column into which the value should be inserted. - * @param IParameter|string $value The value that should be inserted into the column. + * @param IParameter|IQueryFunction|string $value The value that should be inserted into the column. * - * @return $this This QueryBuilder instance. * @since 8.2.0 * * @psalm-taint-sink sql $column * @psalm-taint-sink sql $value */ - public function setValue($column, $value); + public function setValue(string $column, IParameter|IQueryFunction|string $value): self; /** * Specifies values for an insert query indexed by column names. @@ -795,14 +770,13 @@ public function setValue($column, $value); * ); * * - * @param array $values The values to specify for the insert query indexed by column names. + * @param array $values The values to specify for the insert query indexed by column names. * - * @return $this This QueryBuilder instance. * @since 8.2.0 * * @psalm-taint-sink sql $values */ - public function values(array $values); + public function values(array $values): self; /** * Specifies a restriction over the groups of the query. @@ -810,12 +784,11 @@ public function values(array $values); * * @param mixed ...$having The restriction over the groups. * - * @return $this This QueryBuilder instance. * @since 8.2.0 * * @psalm-taint-sink sql $having */ - public function having(...$having); + public function having(...$having): self; /** * Adds a restriction over the groups of the query, forming a logical @@ -823,12 +796,11 @@ public function having(...$having); * * @param mixed ...$having The restriction to append. * - * @return $this This QueryBuilder instance. * @since 8.2.0 * * @psalm-taint-sink sql $andHaving */ - public function andHaving(...$having); + public function andHaving(...$having): self; /** * Adds a restriction over the groups of the query, forming a logical @@ -836,19 +808,18 @@ public function andHaving(...$having); * * @param mixed ...$having The restriction to add. * - * @return $this This QueryBuilder instance. * @since 8.2.0 * * @psalm-taint-sink sql $having */ - public function orHaving(...$having); + public function orHaving(...$having): self; /** * Specifies an ordering for the query results. * Replaces any previously specified orderings, if any. * * @param string|IQueryFunction|ILiteral|IParameter $sort The ordering expression. - * @param string $order The ordering direction. + * @param 'ASC'|'DESC'|'asc'|'desc'|null $order The ordering direction. * * @return $this This QueryBuilder instance. * @since 8.2.0 @@ -856,13 +827,13 @@ public function orHaving(...$having); * @psalm-taint-sink sql $sort * @psalm-taint-sink sql $order */ - public function orderBy($sort, $order = null); + public function orderBy(string|IQueryFunction|ILiteral|IParameter $sort, ?string $order = null): self; /** * Adds an ordering to the query results. * * @param string|ILiteral|IParameter|IQueryFunction $sort The ordering expression. - * @param string $order The ordering direction. + * @param 'ASC'|'DESC'|'asc'|'desc'|null $order The ordering direction. * * @return $this This QueryBuilder instance. * @since 8.2.0 @@ -870,29 +841,25 @@ public function orderBy($sort, $order = null); * @psalm-taint-sink sql $sort * @psalm-taint-sink sql $order */ - public function addOrderBy($sort, $order = null); + public function addOrderBy(string|ILiteral|IParameter|IQueryFunction $sort, ?string $order = null): self; /** * Gets a query part by its name. * - * @param string $queryPartName - * - * @return mixed * @since 8.2.0 * @deprecated 30.0.0 This function is going to be removed with the next Doctrine/DBAL update * and we can not fix this in our wrapper. Please track the details you need, outside the object. */ - public function getQueryPart($queryPartName); + public function getQueryPart(string $queryPartName): mixed; /** * Gets all query parts. * - * @return array * @since 8.2.0 * @deprecated 30.0.0 This function is going to be removed with the next Doctrine/DBAL update * and we can not fix this in our wrapper. Please track the details you need, outside the object. */ - public function getQueryParts(); + public function getQueryParts(): array; /** * Resets SQL parts. @@ -904,7 +871,7 @@ public function getQueryParts(); * @deprecated 30.0.0 This function is going to be removed with the next Doctrine/DBAL update * and we can not fix this in our wrapper. Please create a new IQueryBuilder instead. */ - public function resetQueryParts($queryPartNames = null); + public function resetQueryParts(?array $queryPartNames = null): self; /** * Resets a single SQL part. @@ -916,7 +883,7 @@ public function resetQueryParts($queryPartNames = null); * @deprecated 30.0.0 This function is going to be removed with the next Doctrine/DBAL update * and we can not fix this in our wrapper. Please create a new IQueryBuilder instead. */ - public function resetQueryPart($queryPartName); + public function resetQueryPart(string $queryPartName): self; /** * Creates a new named parameter and bind the value $value to it. @@ -950,7 +917,7 @@ public function resetQueryPart($queryPartName); * * @psalm-taint-escape sql */ - public function createNamedParameter($value, $type = self::PARAM_STR, $placeHolder = null); + public function createNamedParameter(mixed $value, $type = self::PARAM_STR, ?string $placeHolder = null): IParameter; /** * Creates a new positional parameter and bind the given value to it. @@ -977,7 +944,7 @@ public function createNamedParameter($value, $type = self::PARAM_STR, $placeHold * * @psalm-taint-escape sql */ - public function createPositionalParameter($value, $type = self::PARAM_STR); + public function createPositionalParameter(mixed $value, $type = self::PARAM_STR): IParameter; /** * Creates a new parameter @@ -998,7 +965,7 @@ public function createPositionalParameter($value, $type = self::PARAM_STR); * * @psalm-taint-escape sql */ - public function createParameter($name); + public function createParameter(string $name): IParameter; /** * Creates a new function @@ -1026,7 +993,7 @@ public function createParameter($name); * * @psalm-taint-sink sql $call */ - public function createFunction($call); + public function createFunction(string $call): IQueryFunction; /** * Used to get the id of the last inserted element @@ -1046,13 +1013,11 @@ public function getLastInsertId(): int; * @since 9.0.0 * @since 24.0.0 accepts IQueryFunction as parameter */ - public function getTableName($table); + public function getTableName(string|IQueryFunction $table): string; /** * Returns the table name with database prefix as needed by the implementation * - * @param string $table - * @return string * @since 30.0.0 */ public function prefixTableName(string $table): string; @@ -1060,19 +1025,13 @@ public function prefixTableName(string $table): string; /** * Returns the column name quoted and with table alias prefix as needed by the implementation * - * @param string $column - * @param string $tableAlias - * @return string * @since 9.0.0 */ - public function getColumnName($column, $tableAlias = ''); + public function getColumnName(string $column, string $tableAlias = ''): string; /** * Provide a hint for the shard key for queries where this can't be detected otherwise * - * @param string $column - * @param mixed $value - * @return $this * @since 30.0.0 */ public function hintShardKey(string $column, mixed $value, bool $overwrite = false): self; @@ -1080,7 +1039,6 @@ public function hintShardKey(string $column, mixed $value, bool $overwrite = fal /** * Set the query to run across all shards if sharding is enabled. * - * @return $this * @since 30.0.0 */ public function runAcrossAllShards(): self; @@ -1088,7 +1046,7 @@ public function runAcrossAllShards(): self; /** * Get a list of column names that are expected in the query output * - * @return array + * @return string[] * @since 30.0.0 */ public function getOutputColumns(): array;