refactor(QueryBuilder): Properly type the query builder

And make sure the related unit tests are also typed and checked by
psalm.

Signed-off-by: Carl Schwan <carlschwan@kde.org>
This commit is contained in:
Carl Schwan 2026-01-23 16:55:54 +01:00
parent 2437046e25
commit 4020f91c75
No known key found for this signature in database
GPG key ID: 02325448204E452A
38 changed files with 1172 additions and 2180 deletions

View file

@ -1020,7 +1020,7 @@ class CardDavBackend implements BackendInterface, SyncSupport {
'synctoken' => $query->createNamedParameter($syncToken),
'addressbookid' => $query->createNamedParameter($addressBookId),
'operation' => $query->createNamedParameter($operation),
'created_at' => time(),
'created_at' => $query->createNamedParameter(time()),
])
->executeStatement();

View file

@ -11,6 +11,7 @@ namespace OCA\DAV\Tests\unit\BackgroundJob;
use OCA\DAV\BackgroundJob\CleanupInvitationTokenJob;
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\DB\QueryBuilder\IExpressionBuilder;
use OCP\DB\QueryBuilder\IParameter;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\IDBConnection;
use PHPUnit\Framework\MockObject\MockObject;
@ -46,9 +47,12 @@ class CleanupInvitationTokenJobTest extends TestCase {
->willReturn($queryBuilder);
$queryBuilder->method('expr')
->willReturn($expr);
$parameter = $this->createMock(IParameter::class);
$parameter->method('__toString')
->willReturn('namedParameter1337');
$queryBuilder->method('createNamedParameter')
->willReturnMap([
[1337, \PDO::PARAM_STR, null, 'namedParameter1337']
[1337, \PDO::PARAM_STR, null, $parameter],
]);
$function = 'function1337';

View file

@ -15,6 +15,7 @@ use OCP\AppFramework\Http\TemplateResponse;
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\DB\IResult;
use OCP\DB\QueryBuilder\IExpressionBuilder;
use OCP\DB\QueryBuilder\IParameter;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\IDBConnection;
use OCP\IRequest;
@ -415,9 +416,12 @@ EOF;
->willReturn($queryBuilder);
$queryBuilder->method('expr')
->willReturn($expr);
$parameter = $this->createMock(IParameter::class);
$parameter->method('__toString')
->willReturn('namedParameterToken');
$queryBuilder->method('createNamedParameter')
->willReturnMap([
[$token, \PDO::PARAM_STR, null, 'namedParameterToken']
[$token, \PDO::PARAM_STR, null, $parameter]
]);
$stmt->expects($this->once())

View file

@ -3503,42 +3503,6 @@
<code><![CDATA[0]]></code>
</TypeDoesNotContainType>
</file>
<file src="lib/private/DB/QueryBuilder/ExpressionBuilder/ExpressionBuilder.php">
<ImplicitToStringCast>
<code><![CDATA[$this->functionBuilder->lower($x)]]></code>
</ImplicitToStringCast>
<InvalidArgument>
<code><![CDATA[$y]]></code>
<code><![CDATA[$y]]></code>
</InvalidArgument>
</file>
<file src="lib/private/DB/QueryBuilder/ExpressionBuilder/MySqlExpressionBuilder.php">
<InternalMethod>
<code><![CDATA[getParams]]></code>
</InternalMethod>
<InvalidArrayOffset>
<code><![CDATA[$params['collation']]]></code>
</InvalidArrayOffset>
</file>
<file src="lib/private/DB/QueryBuilder/QueryBuilder.php">
<InvalidNullableReturnType>
<code><![CDATA[string]]></code>
</InvalidNullableReturnType>
<NullableReturnStatement>
<code><![CDATA[$alias]]></code>
</NullableReturnStatement>
<ParamNameMismatch>
<code><![CDATA[$selects]]></code>
</ParamNameMismatch>
</file>
<file src="lib/private/DB/QueryBuilder/QuoteHelper.php">
<InvalidNullableReturnType>
<code><![CDATA[string]]></code>
</InvalidNullableReturnType>
<NullableReturnStatement>
<code><![CDATA[$string]]></code>
</NullableReturnStatement>
</file>
<file src="lib/private/DateTimeFormatter.php">
<FalsableReturnStatement>
<code><![CDATA[$l->l($type, $timestamp, [
@ -4256,13 +4220,4 @@
<code><![CDATA[getAppValue]]></code>
</DeprecatedMethod>
</file>
<file src="tests/lib/TestCase.php">
<DeprecatedMethod>
<code><![CDATA[$container]]></code>
</DeprecatedMethod>
<InternalMethod>
<code><![CDATA[lockFile]]></code>
<code><![CDATA[unlockFile]]></code>
</InternalMethod>
</file>
</files>

View file

@ -14,6 +14,10 @@ class CompositeExpression implements ICompositeExpression, \Countable {
public const TYPE_AND = 'AND';
public const TYPE_OR = 'OR';
/**
* @param self::TYPE_* $type
* @param array<ICompositeExpression|string> $parts
*/
public function __construct(
private string $type,
private array $parts = [],

View file

@ -5,427 +5,214 @@
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
* SPDX-License-Identifier: AGPL-3.0-only
*/
declare(strict_types=1);
namespace OC\DB\QueryBuilder\ExpressionBuilder;
use Doctrine\DBAL\Query\Expression\ExpressionBuilder as DoctrineExpressionBuilder;
use OC\DB\ConnectionAdapter;
use OC\DB\QueryBuilder\CompositeExpression;
use OC\DB\QueryBuilder\FunctionBuilder\FunctionBuilder;
use OC\DB\QueryBuilder\Literal;
use OC\DB\QueryBuilder\QueryFunction;
use OC\DB\QueryBuilder\QuoteHelper;
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 ExpressionBuilder implements IExpressionBuilder {
/** @var \Doctrine\DBAL\Query\Expression\ExpressionBuilder */
protected $expressionBuilder;
protected DoctrineExpressionBuilder $expressionBuilder;
protected QuoteHelper $helper;
protected IFunctionBuilder $functionBuilder;
/** @var QuoteHelper */
protected $helper;
/** @var IDBConnection */
protected $connection;
/** @var LoggerInterface */
protected $logger;
/** @var FunctionBuilder */
protected $functionBuilder;
public function __construct(ConnectionAdapter $connection, IQueryBuilder $queryBuilder, LoggerInterface $logger) {
$this->connection = $connection;
$this->logger = $logger;
public function __construct(
protected readonly ConnectionAdapter $connection,
IQueryBuilder $queryBuilder,
protected readonly LoggerInterface $logger,
) {
$this->helper = new QuoteHelper();
$this->expressionBuilder = new DoctrineExpressionBuilder($connection->getInner());
$this->functionBuilder = $queryBuilder->func();
}
/**
* Creates a conjunction of the given boolean expressions.
*
* Example:
*
* [php]
* // (u.type = ?) AND (u.role = ?)
* $expr->andX('u.type = ?', 'u.role = ?'));
*
* @param mixed ...$x Optional clause. Defaults = null, but requires
* at least one defined when converting to string.
*
* @return \OCP\DB\QueryBuilder\ICompositeExpression
*/
public function andX(...$x): ICompositeExpression {
#[Override]
public function andX(ICompositeExpression|string ...$x): ICompositeExpression {
if (empty($x)) {
$this->logger->debug('Calling ' . IQueryBuilder::class . '::' . __FUNCTION__ . ' without parameters is deprecated and will throw soon.', ['exception' => new \Exception('No parameters in call to ' . __METHOD__)]);
}
return new CompositeExpression(CompositeExpression::TYPE_AND, $x);
}
/**
* Creates a disjunction of the given boolean expressions.
*
* Example:
*
* [php]
* // (u.type = ?) OR (u.role = ?)
* $qb->where($qb->expr()->orX('u.type = ?', 'u.role = ?'));
*
* @param mixed ...$x Optional clause. Defaults = null, but requires
* at least one defined when converting to string.
*
* @return \OCP\DB\QueryBuilder\ICompositeExpression
*/
public function orX(...$x): ICompositeExpression {
#[Override]
public function orX(ICompositeExpression|string ...$x): ICompositeExpression {
if (empty($x)) {
$this->logger->debug('Calling ' . IQueryBuilder::class . '::' . __FUNCTION__ . ' without parameters is deprecated and will throw soon.', ['exception' => new \Exception('No parameters in call to ' . __METHOD__)]);
}
return new CompositeExpression(CompositeExpression::TYPE_OR, $x);
}
/**
* Creates a comparison expression.
*
* @param mixed $x The left expression.
* @param string $operator One of the IExpressionBuilder::* constants.
* @param mixed $y The right expression.
* @param mixed|null $type one of the IQueryBuilder::PARAM_* constants
* required when comparing text fields for oci compatibility
*
* @return string
*/
public function comparison($x, string $operator, $y, $type = null): string {
#[Override]
public function comparison(string|ILiteral|IQueryFunction|IParameter $x, string $operator, $y, int|string|null $type = null): string {
$x = $this->prepareColumn($x, $type);
$y = $this->prepareColumn($y, $type);
return $this->expressionBuilder->comparison($x, $operator, $y);
}
/**
* Creates an equality comparison expression with the given arguments.
*
* First argument is considered the left expression and the second is the right expression.
* When converted to string, it will generated a <left expr> = <right expr>. Example:
*
* [php]
* // u.id = ?
* $expr->eq('u.id', '?');
*
* @param mixed $x The left expression.
* @param mixed $y The right expression.
* @param mixed|null $type one of the IQueryBuilder::PARAM_* constants
* required when comparing text fields for oci compatibility
*
* @return string
*/
public function eq($x, $y, $type = null): string {
#[Override]
public function eq(string|ILiteral|IQueryFunction|IParameter $x, IQueryFunction|ILiteral|IParameter|string $y, int|string|null $type = null): string {
$x = $this->prepareColumn($x, $type);
$y = $this->prepareColumn($y, $type);
return $this->expressionBuilder->eq($x, $y);
}
/**
* Creates a non equality comparison expression with the given arguments.
* First argument is considered the left expression and the second is the right expression.
* When converted to string, it will generated a <left expr> <> <right expr>. Example:
*
* [php]
* // u.id <> 1
* $q->where($q->expr()->neq('u.id', '1'));
*
* @param mixed $x The left expression.
* @param mixed $y The right expression.
* @param mixed|null $type one of the IQueryBuilder::PARAM_* constants
* required when comparing text fields for oci compatibility
*
* @return string
*/
public function neq($x, $y, $type = null): string {
#[Override]
public function neq(string|ILiteral|IQueryFunction|IParameter $x, string|ILiteral|IQueryFunction|IParameter $y, int|string|null $type = null): string {
$x = $this->prepareColumn($x, $type);
$y = $this->prepareColumn($y, $type);
return $this->expressionBuilder->neq($x, $y);
}
/**
* Creates a lower-than comparison expression with the given arguments.
* First argument is considered the left expression and the second is the right expression.
* When converted to string, it will generated a <left expr> < <right expr>. Example:
*
* [php]
* // u.id < ?
* $q->where($q->expr()->lt('u.id', '?'));
*
* @param mixed $x The left expression.
* @param mixed $y The right expression.
* @param mixed|null $type one of the IQueryBuilder::PARAM_* constants
* required when comparing text fields for oci compatibility
*
* @return string
*/
public function lt($x, $y, $type = null): string {
#[Override]
public function lt(string|ILiteral|IQueryFunction|IParameter $x, string|ILiteral|IQueryFunction|IParameter $y, int|string|null $type = null): string {
$x = $this->prepareColumn($x, $type);
$y = $this->prepareColumn($y, $type);
return $this->expressionBuilder->lt($x, $y);
}
/**
* Creates a lower-than-equal comparison expression with the given arguments.
* First argument is considered the left expression and the second is the right expression.
* When converted to string, it will generated a <left expr> <= <right expr>. Example:
*
* [php]
* // u.id <= ?
* $q->where($q->expr()->lte('u.id', '?'));
*
* @param mixed $x The left expression.
* @param mixed $y The right expression.
* @param mixed|null $type one of the IQueryBuilder::PARAM_* constants
* required when comparing text fields for oci compatibility
*
* @return string
*/
public function lte($x, $y, $type = null): string {
#[Override]
public function lte(string|ILiteral|IQueryFunction|IParameter $x, string|ILiteral|IQueryFunction|IParameter $y, int|string|null $type = null): string {
$x = $this->prepareColumn($x, $type);
$y = $this->prepareColumn($y, $type);
return $this->expressionBuilder->lte($x, $y);
}
/**
* Creates a greater-than comparison expression with the given arguments.
* First argument is considered the left expression and the second is the right expression.
* When converted to string, it will generated a <left expr> > <right expr>. Example:
*
* [php]
* // u.id > ?
* $q->where($q->expr()->gt('u.id', '?'));
*
* @param mixed $x The left expression.
* @param mixed $y The right expression.
* @param mixed|null $type one of the IQueryBuilder::PARAM_* constants
* required when comparing text fields for oci compatibility
*
* @return string
*/
public function gt($x, $y, $type = null): string {
#[Override]
public function gt(string|ILiteral|IQueryFunction|IParameter $x, string|ILiteral|IQueryFunction|IParameter $y, int|string|null $type = null): string {
$x = $this->prepareColumn($x, $type);
$y = $this->prepareColumn($y, $type);
return $this->expressionBuilder->gt($x, $y);
}
/**
* Creates a greater-than-equal comparison expression with the given arguments.
* First argument is considered the left expression and the second is the right expression.
* When converted to string, it will generated a <left expr> >= <right expr>. Example:
*
* [php]
* // u.id >= ?
* $q->where($q->expr()->gte('u.id', '?'));
*
* @param mixed $x The left expression.
* @param mixed $y The right expression.
* @param mixed|null $type one of the IQueryBuilder::PARAM_* constants
* required when comparing text fields for oci compatibility
*
* @return string
*/
public function gte($x, $y, $type = null): string {
#[Override]
public function gte(string|ILiteral|IQueryFunction|IParameter $x, string|ILiteral|IQueryFunction|IParameter $y, int|string|null $type = null): string {
$x = $this->prepareColumn($x, $type);
$y = $this->prepareColumn($y, $type);
return $this->expressionBuilder->gte($x, $y);
}
/**
* Creates an IS NULL expression with the given arguments.
*
* @param string|ILiteral|IParameter|IQueryFunction $x The field in string format to be restricted by IS NULL.
*
* @return string
*/
public function isNull($x): string {
#[Override]
public function isNull(string|ILiteral|IParameter|IQueryFunction $x): string {
$x = $this->helper->quoteColumnName($x);
return $this->expressionBuilder->isNull($x);
}
/**
* Creates an IS NOT NULL expression with the given arguments.
*
* @param string|ILiteral|IParameter|IQueryFunction $x The field in string format to be restricted by IS NOT NULL.
*
* @return string
*/
public function isNotNull($x): string {
#[Override]
public function isNotNull(string|ILiteral|IParameter|IQueryFunction $x): string {
$x = $this->helper->quoteColumnName($x);
return $this->expressionBuilder->isNotNull($x);
}
/**
* Creates a LIKE() comparison expression with the given arguments.
*
* @param ILiteral|IParameter|IQueryFunction|string $x Field in string format to be inspected by LIKE() comparison.
* @param mixed $y Argument to be used in LIKE() comparison.
* @param mixed|null $type one of the IQueryBuilder::PARAM_* constants
* required when comparing text fields for oci compatibility
*
* @return string
*/
public function like($x, $y, $type = null): string {
#[Override]
public function like(
string|IParameter|ILiteral|IQueryFunction $x,
string|IParameter|ILiteral|IQueryFunction $y,
int|string|null $type = null,
): string {
$x = $this->helper->quoteColumnName($x);
$y = $this->helper->quoteColumnName($y);
return $this->expressionBuilder->like($x, $y);
}
/**
* Creates a ILIKE() comparison expression with the given arguments.
*
* @param string $x Field in string format to be inspected by ILIKE() comparison.
* @param mixed $y Argument to be used in ILIKE() comparison.
* @param mixed|null $type one of the IQueryBuilder::PARAM_* constants
* required when comparing text fields for oci compatibility
*
* @return string
* @since 9.0.0
*/
public function iLike($x, $y, $type = null): string {
return $this->expressionBuilder->like($this->functionBuilder->lower($x), $this->functionBuilder->lower($y));
#[Override]
public function iLike(
string|IParameter|ILiteral|IQueryFunction $x,
string|IParameter|ILiteral|IQueryFunction $y,
int|string|null $type = null,
): string {
return $this->expressionBuilder->like((string)$this->functionBuilder->lower($x), (string)$this->functionBuilder->lower($y));
}
/**
* Creates a NOT LIKE() comparison expression with the given arguments.
*
* @param ILiteral|IParameter|IQueryFunction|string $x Field in string format to be inspected by NOT LIKE() comparison.
* @param mixed $y Argument to be used in NOT LIKE() comparison.
* @param mixed|null $type one of the IQueryBuilder::PARAM_* constants
* required when comparing text fields for oci compatibility
*
* @return string
*/
public function notLike($x, $y, $type = null): string {
#[Override]
public function notLike(
string|IParameter|ILiteral|IQueryFunction $x,
string|IParameter|ILiteral|IQueryFunction $y,
int|string|null $type = null,
): string {
$x = $this->helper->quoteColumnName($x);
$y = $this->helper->quoteColumnName($y);
return $this->expressionBuilder->notLike($x, $y);
}
/**
* Creates a IN () comparison expression with the given arguments.
*
* @param ILiteral|IParameter|IQueryFunction|string $x The field in string format to be inspected by IN() comparison.
* @param ILiteral|IParameter|IQueryFunction|string|array $y The placeholder or the array of values to be used by IN() comparison.
* @param mixed|null $type one of the IQueryBuilder::PARAM_* constants
* required when comparing text fields for oci compatibility
*
* @return string
*/
public function in($x, $y, $type = null): string {
#[Override]
public function in(
ILiteral|IParameter|IQueryFunction|string $x,
ILiteral|IParameter|IQueryFunction|string|array $y,
int|string|null $type = null,
): string {
$x = $this->helper->quoteColumnName($x);
$y = $this->helper->quoteColumnNames($y);
return $this->expressionBuilder->in($x, $y);
}
/**
* Creates a NOT IN () comparison expression with the given arguments.
*
* @param ILiteral|IParameter|IQueryFunction|string $x The field in string format to be inspected by NOT IN() comparison.
* @param ILiteral|IParameter|IQueryFunction|string|array $y The placeholder or the array of values to be used by NOT IN() comparison.
* @param mixed|null $type one of the IQueryBuilder::PARAM_* constants
* required when comparing text fields for oci compatibility
*
* @return string
*/
public function notIn($x, $y, $type = null): string {
#[Override]
public function notIn(
ILiteral|IParameter|IQueryFunction|string $x,
ILiteral|IParameter|IQueryFunction|string|array $y,
int|string|null $type = null,
): string {
$x = $this->helper->quoteColumnName($x);
$y = $this->helper->quoteColumnNames($y);
return $this->expressionBuilder->notIn($x, $y);
}
/**
* Creates a $x = '' statement, because Oracle needs a different check
*
* @param string|ILiteral|IParameter|IQueryFunction $x The field in string format to be inspected by the comparison.
* @return string
* @since 13.0.0
*/
public function emptyString($x): string {
#[Override]
public function emptyString(string|ILiteral|IParameter|IQueryFunction $x): string {
return $this->eq($x, $this->literal('', IQueryBuilder::PARAM_STR));
}
/**
* Creates a `$x <> ''` statement, because Oracle needs a different check
*
* @param string|ILiteral|IParameter|IQueryFunction $x The field in string format to be inspected by the comparison.
* @return string
* @since 13.0.0
*/
public function nonEmptyString($x): string {
#[Override]
public function nonEmptyString(string|ILiteral|IParameter|IQueryFunction $x): string {
return $this->neq($x, $this->literal('', IQueryBuilder::PARAM_STR));
}
/**
* Binary AND Operator copies a bit to the result if it exists in both operands.
*
* @param string|ILiteral $x The field or value to check
* @param int $y Bitmap that must be set
* @return IQueryFunction
* @since 12.0.0
*/
public function bitwiseAnd($x, int $y): IQueryFunction {
#[Override]
public function bitwiseAnd(string|ILiteral $x, int $y): IQueryFunction {
return new QueryFunction($this->connection->getDatabasePlatform()->getBitAndComparisonExpression(
$this->helper->quoteColumnName($x),
$y
(string)$y
));
}
/**
* Binary OR Operator copies a bit if it exists in either operand.
*
* @param string|ILiteral $x The field or value to check
* @param int $y Bitmap that must be set
* @return IQueryFunction
* @since 12.0.0
*/
public function bitwiseOr($x, int $y): IQueryFunction {
#[Override]
public function bitwiseOr(string|ILiteral $x, int $y): IQueryFunction {
return new QueryFunction($this->connection->getDatabasePlatform()->getBitOrComparisonExpression(
$this->helper->quoteColumnName($x),
$y
(string)$y
));
}
/**
* Quotes a given input parameter.
*
* @param mixed $input The parameter to be quoted.
* @param int $type One of the IQueryBuilder::PARAM_* constants
*
* @return ILiteral
*/
public function literal($input, $type = IQueryBuilder::PARAM_STR): ILiteral {
#[Override]
public function literal($input, int|string $type = IQueryBuilder::PARAM_STR): ILiteral {
return new Literal($this->expressionBuilder->literal($input, $type));
}
/**
* Returns a IQueryFunction that casts the column to the given type
*
* @param string|IQueryFunction $column
* @param mixed $type One of IQueryBuilder::PARAM_*
* @psalm-param IQueryBuilder::PARAM_* $type
* @return IQueryFunction
*/
public function castColumn($column, $type): IQueryFunction {
#[Override]
public function castColumn(string|IQueryFunction|ILiteral|IParameter $column, int|string $type): IQueryFunction {
return new QueryFunction(
$this->helper->quoteColumnName($column)
);
}
/**
* @param mixed $column
* @param mixed|null $type
* @return array|IQueryFunction|string
* @param IQueryBuilder::PARAM_* $type
*/
protected function prepareColumn($column, $type) {
protected function prepareColumn(IQueryFunction|ILiteral|IParameter|string|array $column, int|string|null $type): array|string {
return $this->helper->quoteColumnNames($column);
}
}

View file

@ -5,12 +5,18 @@
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
* SPDX-License-Identifier: AGPL-3.0-only
*/
declare(strict_types=1);
namespace OC\DB\QueryBuilder\ExpressionBuilder;
use OC\DB\ConnectionAdapter;
use OC\DB\QueryBuilder\QueryFunction;
use OCP\DB\QueryBuilder\ILiteral;
use OCP\DB\QueryBuilder\IParameter;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\DB\QueryBuilder\IQueryFunction;
use Override;
use Psr\Log\LoggerInterface;
class MySqlExpressionBuilder extends ExpressionBuilder {
@ -19,35 +25,25 @@ class MySqlExpressionBuilder extends ExpressionBuilder {
public function __construct(ConnectionAdapter $connection, IQueryBuilder $queryBuilder, LoggerInterface $logger) {
parent::__construct($connection, $queryBuilder, $logger);
/** @psalm-suppress InternalMethod */
$params = $connection->getInner()->getParams();
/** @psalm-suppress InvalidArrayOffset collation is sometime defined */
$this->collation = $params['collation'] ?? (($params['charset'] ?? 'utf8') . '_general_ci');
}
/**
* @inheritdoc
*/
public function iLike($x, $y, $type = null): string {
#[Override]
public function iLike(string|IParameter|ILiteral|IQueryFunction $x, string|IParameter|ILiteral|IQueryFunction $y, int|string|null $type = null): string {
$x = $this->helper->quoteColumnName($x);
$y = $this->helper->quoteColumnName($y);
return $this->expressionBuilder->comparison($x, ' COLLATE ' . $this->collation . ' LIKE', $y);
}
/**
* Returns a IQueryFunction that casts the column to the given type
*
* @param string|IQueryFunction $column
* @param mixed $type One of IQueryBuilder::PARAM_*
* @psalm-param IQueryBuilder::PARAM_* $type
* @return IQueryFunction
*/
public function castColumn($column, $type): IQueryFunction {
switch ($type) {
case IQueryBuilder::PARAM_STR:
return new QueryFunction('CAST(' . $this->helper->quoteColumnName($column) . ' AS CHAR)');
case IQueryBuilder::PARAM_JSON:
return new QueryFunction('CAST(' . $this->helper->quoteColumnName($column) . ' AS JSON)');
default:
return parent::castColumn($column, $type);
}
#[Override]
public function castColumn(string|IParameter|ILiteral|IQueryFunction $column, int|string $type): IQueryFunction {
return match ($type) {
IQueryBuilder::PARAM_STR => new QueryFunction('CAST(' . $this->helper->quoteColumnName($column) . ' AS CHAR)'),
IQueryBuilder::PARAM_JSON => new QueryFunction('CAST(' . $this->helper->quoteColumnName($column) . ' AS JSON)'),
default => parent::castColumn($column, $type),
};
}
}

View file

@ -12,14 +12,11 @@ use OCP\DB\QueryBuilder\ILiteral;
use OCP\DB\QueryBuilder\IParameter;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\DB\QueryBuilder\IQueryFunction;
use Override;
class OCIExpressionBuilder extends ExpressionBuilder {
/**
* @param mixed $column
* @param mixed|null $type
* @return array|IQueryFunction|string
*/
protected function prepareColumn($column, $type) {
#[Override]
protected function prepareColumn(IQueryFunction|ILiteral|IParameter|string|array $column, int|string|null $type): array|string {
if ($type === IQueryBuilder::PARAM_STR && !is_array($column) && !($column instanceof IParameter) && !($column instanceof ILiteral)) {
$column = $this->castColumn($column, $type);
}
@ -27,10 +24,8 @@ class OCIExpressionBuilder extends ExpressionBuilder {
return parent::prepareColumn($column, $type);
}
/**
* @inheritdoc
*/
public function eq($x, $y, $type = null): string {
#[Override]
public function eq(IQueryFunction|ILiteral|IParameter|string $x, IQueryFunction|ILiteral|IParameter|string $y, int|string|null $type = null): string {
if ($type === IQueryBuilder::PARAM_JSON) {
$x = $this->prepareColumn($x, $type);
$y = $this->prepareColumn($y, $type);
@ -40,10 +35,8 @@ class OCIExpressionBuilder extends ExpressionBuilder {
return parent::eq($x, $y, $type);
}
/**
* @inheritdoc
*/
public function neq($x, $y, $type = null): string {
#[Override]
public function neq(string|ILiteral|IQueryFunction|IParameter $x, string|ILiteral|IQueryFunction|IParameter $y, int|string|null $type = null): string {
if ($type === IQueryBuilder::PARAM_JSON) {
$x = $this->prepareColumn($x, $type);
$y = $this->prepareColumn($y, $type);
@ -53,57 +46,34 @@ class OCIExpressionBuilder extends ExpressionBuilder {
return parent::neq($x, $y, $type);
}
/**
* @inheritdoc
*/
public function in($x, $y, $type = null): string {
#[Override]
public function in(ILiteral|IParameter|IQueryFunction|string $x, ILiteral|IParameter|IQueryFunction|string|array $y, int|string|null $type = null): string {
$x = $this->prepareColumn($x, $type);
$y = $this->prepareColumn($y, $type);
return $this->expressionBuilder->in($x, $y);
}
/**
* @inheritdoc
*/
public function notIn($x, $y, $type = null): string {
#[Override]
public function notIn(ILiteral|IParameter|IQueryFunction|string $x, ILiteral|IParameter|IQueryFunction|string|array $y, int|string|null $type = null): string {
$x = $this->prepareColumn($x, $type);
$y = $this->prepareColumn($y, $type);
return $this->expressionBuilder->notIn($x, $y);
}
/**
* Creates a $x = '' statement, because Oracle needs a different check
*
* @param string|ILiteral|IParameter|IQueryFunction $x The field in string format to be inspected by the comparison.
* @return string
* @since 13.0.0
*/
public function emptyString($x): string {
#[Override]
public function emptyString(string|ILiteral|IParameter|IQueryFunction $x): string {
return $this->isNull($x);
}
/**
* Creates a `$x <> ''` statement, because Oracle needs a different check
*
* @param string|ILiteral|IParameter|IQueryFunction $x The field in string format to be inspected by the comparison.
* @return string
* @since 13.0.0
*/
public function nonEmptyString($x): string {
#[Override]
public function nonEmptyString(string|ILiteral|IParameter|IQueryFunction $x): string {
return $this->isNotNull($x);
}
/**
* Returns a IQueryFunction that casts the column to the given type
*
* @param string|IQueryFunction $column
* @param mixed $type One of IQueryBuilder::PARAM_*
* @psalm-param IQueryBuilder::PARAM_* $type
* @return IQueryFunction
*/
public function castColumn($column, $type): IQueryFunction {
#[Override]
public function castColumn(string|IQueryFunction|ILiteral|IParameter $column, int|string|null $type): IQueryFunction {
if ($type === IQueryBuilder::PARAM_STR) {
$column = $this->helper->quoteColumnName($column);
return new QueryFunction('to_char(' . $column . ')');
@ -116,17 +86,21 @@ class OCIExpressionBuilder extends ExpressionBuilder {
return parent::castColumn($column, $type);
}
/**
* @inheritdoc
*/
public function like($x, $y, $type = null): string {
#[Override]
public function like(
ILiteral|IParameter|IQueryFunction|string $x,
ILiteral|IParameter|IQueryFunction|string $y,
int|string|null $type = null,
): string {
return parent::like($x, $y, $type) . " ESCAPE '\\'";
}
/**
* @inheritdoc
*/
public function iLike($x, $y, $type = null): string {
#[Override]
public function iLike(
ILiteral|IParameter|IQueryFunction|string $x,
ILiteral|IParameter|IQueryFunction|string $y,
int|string|null $type = null,
): string {
return $this->like($this->functionBuilder->lower($x), $this->functionBuilder->lower($y));
}
}

View file

@ -12,32 +12,20 @@ use OCP\DB\QueryBuilder\ILiteral;
use OCP\DB\QueryBuilder\IParameter;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\DB\QueryBuilder\IQueryFunction;
use Override;
class PgSqlExpressionBuilder extends ExpressionBuilder {
/**
* Returns a IQueryFunction that casts the column to the given type
*
* @param string|IQueryFunction $column
* @param mixed $type One of IQueryBuilder::PARAM_*
* @psalm-param IQueryBuilder::PARAM_* $type
* @return IQueryFunction
*/
public function castColumn($column, $type): IQueryFunction {
switch ($type) {
case IQueryBuilder::PARAM_INT:
return new QueryFunction('CAST(' . $this->helper->quoteColumnName($column) . ' AS BIGINT)');
case IQueryBuilder::PARAM_STR:
case IQueryBuilder::PARAM_JSON:
return new QueryFunction('CAST(' . $this->helper->quoteColumnName($column) . ' AS TEXT)');
default:
return parent::castColumn($column, $type);
}
#[Override]
public function castColumn(string|IQueryFunction|ILiteral|IParameter $column, string|int $type): IQueryFunction {
return match ($type) {
IQueryBuilder::PARAM_INT => new QueryFunction('CAST(' . $this->helper->quoteColumnName($column) . ' AS BIGINT)'),
IQueryBuilder::PARAM_STR, IQueryBuilder::PARAM_JSON => new QueryFunction('CAST(' . $this->helper->quoteColumnName($column) . ' AS TEXT)'),
default => parent::castColumn($column, $type),
};
}
/**
* @inheritdoc
*/
protected function prepareColumn($column, $type) {
#[Override]
protected function prepareColumn(IQueryFunction|ILiteral|IParameter|string|array $column, int|string|null $type): string|array {
if ($type === IQueryBuilder::PARAM_JSON && !is_array($column) && !($column instanceof IParameter) && !($column instanceof ILiteral)) {
$column = $this->castColumn($column, $type);
}
@ -45,10 +33,8 @@ class PgSqlExpressionBuilder extends ExpressionBuilder {
return parent::prepareColumn($column, $type);
}
/**
* @inheritdoc
*/
public function iLike($x, $y, $type = null): string {
#[Override]
public function iLike(string|IParameter|ILiteral|IQueryFunction $x, mixed $y, mixed $type = null): string {
$x = $this->helper->quoteColumnName($x);
$y = $this->helper->quoteColumnName($y);
return $this->expressionBuilder->comparison($x, 'ILIKE', $y);

View file

@ -11,44 +11,35 @@ use OCP\DB\QueryBuilder\ILiteral;
use OCP\DB\QueryBuilder\IParameter;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\DB\QueryBuilder\IQueryFunction;
use Override;
class SqliteExpressionBuilder extends ExpressionBuilder {
/**
* @inheritdoc
*/
public function like($x, $y, $type = null): string {
#[Override]
public function like(ILiteral|IParameter|IQueryFunction|string $x, mixed $y, int|string|null $type = null): string {
return parent::like($x, $y, $type) . " ESCAPE '\\'";
}
public function iLike($x, $y, $type = null): string {
#[Override]
public function iLike(string|IParameter|ILiteral|IQueryFunction $x, mixed $y, int|string|null $type = null): string {
return $this->like($this->functionBuilder->lower($x), $this->functionBuilder->lower($y), $type);
}
/**
* @param mixed $column
* @param mixed|null $type
* @return array|IQueryFunction|string
*/
protected function prepareColumn($column, $type) {
if ($type !== null
&& !is_array($column)
&& !($column instanceof IParameter)
#[Override]
protected function prepareColumn(IQueryFunction|ILiteral|IParameter|string|array $column, int|string|null $type): string|array {
if (!($column instanceof IParameter)
&& !($column instanceof IQueryFunction)
&& !($column instanceof ILiteral)
&& !is_array($column)
&& is_string($type)
&& (str_starts_with($type, 'date') || str_starts_with($type, 'time'))) {
return $this->castColumn($column, $type);
return (string)$this->castColumn($column, $type);
}
return parent::prepareColumn($column, $type);
}
/**
* Returns a IQueryFunction that casts the column to the given type
*
* @param string $column
* @param mixed $type One of IQueryBuilder::PARAM_*
* @return IQueryFunction
*/
public function castColumn($column, $type): IQueryFunction {
#[Override]
public function castColumn(string|IQueryFunction|ILiteral|IParameter $column, string|int $type): IQueryFunction {
switch ($type) {
case IQueryBuilder::PARAM_DATE_MUTABLE:
case IQueryBuilder::PARAM_DATE_IMMUTABLE:

View file

@ -9,284 +9,349 @@ declare(strict_types=1);
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): int|string {
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|ILiteral $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(string $fromAlias, string|IQueryFunction $join, ?string $alias, string|ICompositeExpression|null $condition = null): self {
$this->builder->innerJoin($fromAlias, $join, $alias, $condition);
return $this;
}
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->builder->leftJoin($fromAlias, $join, $alias, $condition);
return $this;
}
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->builder->rightJoin($fromAlias, $join, $alias, $condition);
return $this;
}
public function set($key, $value) {
#[Override]
public function set(string $key, ILiteral|IParameter|IQueryFunction|string $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, ILiteral|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);
}

View file

@ -6,36 +6,31 @@
*/
namespace OC\DB\QueryBuilder\FunctionBuilder;
use OC\DB\Connection;
use OC\DB\ConnectionAdapter;
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;
use Override;
class FunctionBuilder implements IFunctionBuilder {
/** @var IDBConnection|Connection */
protected $connection;
/** @var IQueryBuilder */
protected $queryBuilder;
/** @var QuoteHelper */
protected $helper;
public function __construct(IDBConnection $connection, IQueryBuilder $queryBuilder, QuoteHelper $helper) {
$this->connection = $connection;
$this->queryBuilder = $queryBuilder;
$this->helper = $helper;
public function __construct(
protected readonly ConnectionAdapter $connection,
protected readonly IQueryBuilder $queryBuilder,
protected readonly QuoteHelper $helper,
) {
}
public function md5($input): IQueryFunction {
#[Override]
public function md5(string|ILiteral|IParameter|IQueryFunction $input): IQueryFunction {
return new QueryFunction('MD5(' . $this->helper->quoteColumnName($input) . ')');
}
public function concat($x, ...$expr): IQueryFunction {
#[Override]
public function concat(string|ILiteral|IParameter|IQueryFunction $x, string|ILiteral|IParameter|IQueryFunction ...$expr): IQueryFunction {
$args = func_get_args();
$list = [];
foreach ($args as $item) {
@ -44,12 +39,18 @@ class FunctionBuilder implements IFunctionBuilder {
return new QueryFunction(sprintf('CONCAT(%s)', implode(', ', $list)));
}
public function groupConcat($expr, ?string $separator = ','): IQueryFunction {
#[Override]
public function groupConcat(string|ILiteral|IParameter|IQueryFunction $expr, ?string $separator = ','): IQueryFunction {
$separator = $this->connection->quote($separator);
return new QueryFunction('GROUP_CONCAT(' . $this->helper->quoteColumnName($expr) . ' SEPARATOR ' . $separator . ')');
}
public function substring($input, $start, $length = null): IQueryFunction {
#[Override]
public function substring(
string|ILiteral|IParameter|IQueryFunction $input,
string|ILiteral|IParameter|IQueryFunction $start,
null|ILiteral|IParameter|IQueryFunction $length = null,
): IQueryFunction {
if ($length) {
return new QueryFunction('SUBSTR(' . $this->helper->quoteColumnName($input) . ', ' . $this->helper->quoteColumnName($start) . ', ' . $this->helper->quoteColumnName($length) . ')');
} else {
@ -57,53 +58,76 @@ class FunctionBuilder implements IFunctionBuilder {
}
}
public function sum($field): IQueryFunction {
#[Override]
public function sum(string|ILiteral|IParameter|IQueryFunction $field): IQueryFunction {
return new QueryFunction('SUM(' . $this->helper->quoteColumnName($field) . ')');
}
public function lower($field): IQueryFunction {
#[Override]
public function lower(string|ILiteral|IParameter|IQueryFunction $field): IQueryFunction {
return new QueryFunction('LOWER(' . $this->helper->quoteColumnName($field) . ')');
}
public function add($x, $y): IQueryFunction {
#[Override]
public function add(
string|ILiteral|IParameter|IQueryFunction $x,
string|ILiteral|IParameter|IQueryFunction $y,
): IQueryFunction {
return new QueryFunction($this->helper->quoteColumnName($x) . ' + ' . $this->helper->quoteColumnName($y));
}
public function subtract($x, $y): IQueryFunction {
#[Override]
public function subtract(
string|ILiteral|IParameter|IQueryFunction $x,
string|ILiteral|IParameter|IQueryFunction $y,
): IQueryFunction {
return new QueryFunction($this->helper->quoteColumnName($x) . ' - ' . $this->helper->quoteColumnName($y));
}
public function count($count = '', $alias = ''): IQueryFunction {
#[Override]
public function count(string|ILiteral|IParameter|IQueryFunction $count = '', string $alias = ''): IQueryFunction {
$alias = $alias ? (' AS ' . $this->helper->quoteColumnName($alias)) : '';
$quotedName = $count === '' ? '*' : $this->helper->quoteColumnName($count);
return new QueryFunction('COUNT(' . $quotedName . ')' . $alias);
}
public function octetLength($field, $alias = ''): IQueryFunction {
#[Override]
public function octetLength(string|ILiteral|IParameter|IQueryFunction $field, string $alias = ''): IQueryFunction {
$alias = $alias ? (' AS ' . $this->helper->quoteColumnName($alias)) : '';
$quotedName = $this->helper->quoteColumnName($field);
return new QueryFunction('OCTET_LENGTH(' . $quotedName . ')' . $alias);
}
public function charLength($field, $alias = ''): IQueryFunction {
#[Override]
public function charLength(string|ILiteral|IParameter|IQueryFunction $field, string $alias = ''): IQueryFunction {
$alias = $alias ? (' AS ' . $this->helper->quoteColumnName($alias)) : '';
$quotedName = $this->helper->quoteColumnName($field);
return new QueryFunction('CHAR_LENGTH(' . $quotedName . ')' . $alias);
}
public function max($field): IQueryFunction {
#[Override]
public function max(string|ILiteral|IParameter|IQueryFunction $field): IQueryFunction {
return new QueryFunction('MAX(' . $this->helper->quoteColumnName($field) . ')');
}
public function min($field): IQueryFunction {
#[Override]
public function min(string|ILiteral|IParameter|IQueryFunction $field): IQueryFunction {
return new QueryFunction('MIN(' . $this->helper->quoteColumnName($field) . ')');
}
public function greatest($x, $y): IQueryFunction {
#[Override]
public function greatest(
string|ILiteral|IParameter|IQueryFunction $x,
string|ILiteral|IParameter|IQueryFunction $y,
): IQueryFunction {
return new QueryFunction('GREATEST(' . $this->helper->quoteColumnName($x) . ', ' . $this->helper->quoteColumnName($y) . ')');
}
public function least($x, $y): IQueryFunction {
#[Override]
public function least(
string|ILiteral|IParameter|IQueryFunction $x,
string|ILiteral|IParameter|IQueryFunction $y,
): IQueryFunction {
return new QueryFunction('LEAST(' . $this->helper->quoteColumnName($x) . ', ' . $this->helper->quoteColumnName($y) . ')');
}

View file

@ -10,9 +10,10 @@ use OC\DB\QueryBuilder\QueryFunction;
use OCP\DB\QueryBuilder\ILiteral;
use OCP\DB\QueryBuilder\IParameter;
use OCP\DB\QueryBuilder\IQueryFunction;
use Override;
class OCIFunctionBuilder extends FunctionBuilder {
public function md5($input): IQueryFunction {
public function md5(string|ILiteral|IParameter|IQueryFunction $input): IQueryFunction {
if (version_compare($this->connection->getServerVersion(), '20', '>=')) {
return new QueryFunction('LOWER(STANDARD_HASH(' . $this->helper->quoteColumnName($input) . ", 'MD5'))");
}
@ -27,11 +28,12 @@ class OCIFunctionBuilder extends FunctionBuilder {
* the second parameter is a function or column, we have to put that as
* first parameter.
*
* @param string|ILiteral|IParameter|IQueryFunction $x
* @param string|ILiteral|IParameter|IQueryFunction $y
* @return IQueryFunction
*/
public function greatest($x, $y): IQueryFunction {
#[Override]
public function greatest(
string|ILiteral|IParameter|IQueryFunction $x,
string|ILiteral|IParameter|IQueryFunction $y,
): IQueryFunction {
if (is_string($y) || $y instanceof IQueryFunction) {
return parent::greatest($y, $x);
}
@ -46,12 +48,12 @@ class OCIFunctionBuilder extends FunctionBuilder {
* math, it will cast the expression to int and continue with a "0". So when
* the second parameter is a function or column, we have to put that as
* first parameter.
*
* @param string|ILiteral|IParameter|IQueryFunction $x
* @param string|ILiteral|IParameter|IQueryFunction $y
* @return IQueryFunction
*/
public function least($x, $y): IQueryFunction {
#[Override]
public function least(
string|ILiteral|IParameter|IQueryFunction $x,
string|ILiteral|IParameter|IQueryFunction $y,
): IQueryFunction {
if (is_string($y) || $y instanceof IQueryFunction) {
return parent::least($y, $x);
}
@ -59,7 +61,8 @@ class OCIFunctionBuilder extends FunctionBuilder {
return parent::least($x, $y);
}
public function concat($x, ...$expr): IQueryFunction {
#[Override]
public function concat(string|ILiteral|IParameter|IQueryFunction $x, string|ILiteral|IParameter|IQueryFunction ...$expr): IQueryFunction {
$args = func_get_args();
$list = [];
foreach ($args as $item) {
@ -68,7 +71,8 @@ class OCIFunctionBuilder extends FunctionBuilder {
return new QueryFunction(sprintf('(%s)', implode(' || ', $list)));
}
public function groupConcat($expr, ?string $separator = ','): IQueryFunction {
#[Override]
public function groupConcat(string|ILiteral|IParameter|IQueryFunction $expr, ?string $separator = ','): IQueryFunction {
$orderByClause = ' WITHIN GROUP(ORDER BY NULL)';
if (is_null($separator)) {
return new QueryFunction('LISTAGG(' . $this->helper->quoteColumnName($expr) . ')' . $orderByClause);
@ -78,13 +82,15 @@ class OCIFunctionBuilder extends FunctionBuilder {
return new QueryFunction('LISTAGG(' . $this->helper->quoteColumnName($expr) . ', ' . $separator . ')' . $orderByClause);
}
public function octetLength($field, $alias = ''): IQueryFunction {
#[Override]
public function octetLength(string|ILiteral|IParameter|IQueryFunction $field, string $alias = ''): IQueryFunction {
$alias = $alias ? (' AS ' . $this->helper->quoteColumnName($alias)) : '';
$quotedName = $this->helper->quoteColumnName($field);
return new QueryFunction('COALESCE(LENGTHB(' . $quotedName . '), 0)' . $alias);
}
public function charLength($field, $alias = ''): IQueryFunction {
#[Override]
public function charLength(string|ILiteral|IParameter|IQueryFunction $field, string $alias = ''): IQueryFunction {
$alias = $alias ? (' AS ' . $this->helper->quoteColumnName($alias)) : '';
$quotedName = $this->helper->quoteColumnName($field);
return new QueryFunction('COALESCE(LENGTH(' . $quotedName . '), 0)' . $alias);

View file

@ -9,8 +9,10 @@ namespace OC\DB\QueryBuilder\FunctionBuilder;
use OC\DB\QueryBuilder\QueryFunction;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\DB\QueryBuilder\IQueryFunction;
use Override;
class PgSqlFunctionBuilder extends FunctionBuilder {
#[Override]
public function concat($x, ...$expr): IQueryFunction {
$args = func_get_args();
$list = [];
@ -20,6 +22,7 @@ class PgSqlFunctionBuilder extends FunctionBuilder {
return new QueryFunction(sprintf('(%s)', implode(' || ', $list)));
}
#[Override]
public function groupConcat($expr, ?string $separator = ','): IQueryFunction {
$castedExpression = $this->queryBuilder->expr()->castColumn($expr, IQueryBuilder::PARAM_STR);

View file

@ -7,7 +7,10 @@
namespace OC\DB\QueryBuilder\FunctionBuilder;
use OC\DB\QueryBuilder\QueryFunction;
use OCP\DB\QueryBuilder\ILiteral;
use OCP\DB\QueryBuilder\IParameter;
use OCP\DB\QueryBuilder\IQueryFunction;
use Override;
class SqliteFunctionBuilder extends FunctionBuilder {
public function concat($x, ...$expr): IQueryFunction {
@ -24,11 +27,19 @@ class SqliteFunctionBuilder extends FunctionBuilder {
return new QueryFunction('GROUP_CONCAT(' . $this->helper->quoteColumnName($expr) . ', ' . $separator . ')');
}
public function greatest($x, $y): IQueryFunction {
#[Override]
public function greatest(
string|ILiteral|IParameter|IQueryFunction $x,
string|ILiteral|IParameter|IQueryFunction $y,
): IQueryFunction {
return new QueryFunction('MAX(' . $this->helper->quoteColumnName($x) . ', ' . $this->helper->quoteColumnName($y) . ')');
}
public function least($x, $y): IQueryFunction {
#[Override]
public function least(
string|ILiteral|IParameter|IQueryFunction $x,
string|ILiteral|IParameter|IQueryFunction $y,
): IQueryFunction {
return new QueryFunction('MIN(' . $this->helper->quoteColumnName($x) . ', ' . $this->helper->quoteColumnName($y) . ')');
}

View file

@ -10,11 +10,9 @@ namespace OC\DB\QueryBuilder;
use OCP\DB\QueryBuilder\ILiteral;
class Literal implements ILiteral {
/** @var mixed */
protected $literal;
public function __construct($literal) {
$this->literal = $literal;
public function __construct(
protected readonly mixed $literal,
) {
}
public function __toString(): string {

View file

@ -10,6 +10,7 @@ namespace OC\DB\QueryBuilder\Partitioned;
use OC\DB\QueryBuilder\CompositeExpression;
use OC\DB\QueryBuilder\QueryFunction;
use OCP\DB\QueryBuilder\ICompositeExpression;
use OCP\DB\QueryBuilder\IQueryFunction;
/**
@ -66,14 +67,9 @@ class JoinCondition {
}
/**
* @param null|string|CompositeExpression $condition
* @param string $join
* @param string $alias
* @param string $fromAlias
* @return JoinCondition
* @throws InvalidPartitionedQueryException
*/
public static function parse($condition, string $join, string $alias, string $fromAlias): JoinCondition {
public static function parse(null|string|ICompositeExpression $condition, string $join, string $alias, string $fromAlias): JoinCondition {
if ($condition === null) {
throw new InvalidPartitionedQueryException("Can't join on $join without a condition");
}
@ -85,7 +81,7 @@ class JoinCondition {
return $result;
}
private static function parseSubCondition($condition, string $join, string $alias, string $fromAlias): JoinCondition {
private static function parseSubCondition(string|ICompositeExpression $condition, string $join, string $alias, string $fromAlias): JoinCondition {
if ($condition instanceof CompositeExpression) {
if ($condition->getType() === CompositeExpression::TYPE_OR) {
throw new InvalidPartitionedQueryException("Cannot join on $join with an OR expression");

View file

@ -14,9 +14,13 @@ use OC\DB\QueryBuilder\Sharded\AutoIncrementHandler;
use OC\DB\QueryBuilder\Sharded\ShardConnectionManager;
use OC\DB\QueryBuilder\Sharded\ShardedQueryBuilder;
use OCP\DB\IResult;
use OCP\DB\QueryBuilder\ICompositeExpression;
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;
/**
* A special query builder that automatically splits queries that span across multiple database partitions[1].
@ -41,7 +45,7 @@ class PartitionedQueryBuilder extends ShardedQueryBuilder {
/** @var list<PartitionSplit> */
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 +79,7 @@ class PartitionedQueryBuilder extends ShardedQueryBuilder {
}
// 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 +88,13 @@ class PartitionedQueryBuilder extends ShardedQueryBuilder {
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|ILiteral $select, string $alias): self {
$this->selects[] = ['select' => $select, 'alias' => $alias];
return $this;
}
@ -101,9 +103,6 @@ class PartitionedQueryBuilder extends ShardedQueryBuilder {
* 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,25 +189,30 @@ class PartitionedQueryBuilder extends ShardedQueryBuilder {
return null;
}
public function from($from, $alias = null) {
#[Override]
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 {
#[Override]
public function innerJoin(string $fromAlias, string|IQueryFunction $join, ?string $alias, string|ICompositeExpression|null $condition = null): self {
return $this->join($fromAlias, $join, $alias, $condition);
}
public function leftJoin($fromAlias, $join, $alias, $condition = null): self {
#[Override]
public function leftJoin(string $fromAlias, string|IQueryFunction $join, ?string $alias, string|ICompositeExpression|null $condition = null): self {
return $this->join($fromAlias, $join, $alias, $condition, PartitionQuery::JOIN_MODE_LEFT);
}
public function join($fromAlias, $join, $alias, $condition = null, $joinMode = PartitionQuery::JOIN_MODE_INNER): self {
#[Override]
public function join(string $fromAlias, string|IQueryFunction $join, ?string $alias, string|ICompositeExpression|null $condition = null, string $joinMode = PartitionQuery::JOIN_MODE_INNER): self {
if ($join instanceof IQueryFunction) {
$partition = null;
$fromPartition = null;
@ -221,7 +225,7 @@ class PartitionedQueryBuilder extends ShardedQueryBuilder {
/** @var string $join */
// join from the main db to a partition
$joinCondition = JoinCondition::parse($condition, $join, $alias, $fromAlias);
$joinCondition = JoinCondition::parse((string)$condition, $join, $alias, $fromAlias);
$partition->addAlias($join, $alias);
if (!isset($this->splitQueries[$partition->name])) {
@ -248,7 +252,7 @@ class PartitionedQueryBuilder extends ShardedQueryBuilder {
/** @var string $join */
// join from partition, to the main db
$joinCondition = JoinCondition::parse($condition, $join, $alias, $fromAlias);
$joinCondition = JoinCondition::parse((string)$condition, $join, $alias, $fromAlias);
if (str_starts_with($fromPartition->name, 'from_')) {
$partitionName = $fromPartition->name;
} else {
@ -281,11 +285,14 @@ class PartitionedQueryBuilder extends ShardedQueryBuilder {
} 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 +340,11 @@ class PartitionedQueryBuilder extends ShardedQueryBuilder {
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 +387,35 @@ class PartitionedQueryBuilder extends ShardedQueryBuilder {
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 +456,7 @@ class PartitionedQueryBuilder extends ShardedQueryBuilder {
return parent::executeStatement($connection);
}
public function getSQL() {
public function getSQL(): string {
$this->applySelects();
return parent::getSQL();
}

File diff suppressed because it is too large Load diff

View file

@ -12,11 +12,7 @@ use OCP\DB\QueryBuilder\IParameter;
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,26 +25,18 @@ class QuoteHelper {
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)));
return implode(' as ', array_map($this->quoteColumnName(...), explode(' as ', $string, 2)));
}
if (substr_count($string, '.')) {

View file

@ -12,8 +12,13 @@ use OC\DB\QueryBuilder\CompositeExpression;
use OC\DB\QueryBuilder\ExtendedQueryBuilder;
use OC\DB\QueryBuilder\Parameter;
use OCP\DB\IResult;
use OCP\DB\QueryBuilder\ICompositeExpression;
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;
/**
* A special query builder that automatically distributes queries over multiple database shards.
@ -83,11 +88,13 @@ class ShardedQueryBuilder extends ExtendedQueryBuilder {
}
}
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 +165,18 @@ class ShardedQueryBuilder extends ExtendedQueryBuilder {
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, ILiteral|IParameter|IQueryFunction|string $value): self {
if ($this->shardDefinition) {
if ($this->shardDefinition->isKey($column)) {
$this->primaryKeys[] = $value;
@ -174,10 +185,12 @@ class ShardedQueryBuilder extends ExtendedQueryBuilder {
$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 +206,35 @@ class ShardedQueryBuilder extends ExtendedQueryBuilder {
}
}
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 +250,83 @@ class ShardedQueryBuilder extends ExtendedQueryBuilder {
}
}
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 {
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(string $fromAlias, string|IQueryFunction $join, ?string $alias, string|ICompositeExpression|null $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(string $fromAlias, string|IQueryFunction $join, ?string $alias, string|ICompositeExpression|null $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) {
return $this->innerJoin($fromAlias, $join, $alias, $condition);
#[Override]
public function join(string $fromAlias, string|IQueryFunction $join, ?string $alias, string|ICompositeExpression|null $condition = null): self {
$this->innerJoin($fromAlias, $join, $alias, $condition);
return $this;
}
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 +339,7 @@ class ShardedQueryBuilder extends ExtendedQueryBuilder {
];
}
#[Override]
public function hintShardKey(string $column, mixed $value, bool $overwrite = false): self {
if ($overwrite) {
$this->primaryKeys = [];
@ -322,6 +354,7 @@ class ShardedQueryBuilder extends ExtendedQueryBuilder {
return $this;
}
#[Override]
public function runAcrossAllShards(): self {
$this->allShards = true;
return $this;
@ -364,6 +397,7 @@ class ShardedQueryBuilder extends ExtendedQueryBuilder {
}
}
#[Override]
public function executeQuery(?IDBConnection $connection = null): IResult {
$this->validate();
if ($this->shardDefinition) {
@ -373,6 +407,7 @@ class ShardedQueryBuilder extends ExtendedQueryBuilder {
return parent::executeQuery($connection);
}
#[Override]
public function executeStatement(?IDBConnection $connection = null): int {
$this->validate();
if ($this->shardDefinition) {
@ -403,6 +438,7 @@ class ShardedQueryBuilder extends ExtendedQueryBuilder {
return parent::executeStatement($connection);
}
#[Override]
public function getLastInsertId(): int {
if ($this->lastInsertId) {
return $this->lastInsertId;
@ -414,6 +450,4 @@ class ShardedQueryBuilder extends ExtendedQueryBuilder {
return parent::getLastInsertId();
}
}
}

View file

@ -45,7 +45,7 @@ class CacheQueryBuilder extends ExtendedQueryBuilder {
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 @@ class CacheQueryBuilder extends ExtendedQueryBuilder {
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 @@ class CacheQueryBuilder extends ExtendedQueryBuilder {
return $this;
}
public function whereParent(int $parent) {
public function whereParent(int $parent): self {
$alias = $this->alias;
if ($alias) {
$alias .= '.';
@ -99,7 +99,7 @@ class CacheQueryBuilder extends ExtendedQueryBuilder {
return $this;
}
public function whereParentInParameter(string $parameter) {
public function whereParentInParameter(string $parameter): self {
$alias = $this->alias;
if ($alias) {
$alias .= '.';

View file

@ -289,7 +289,7 @@ class Database extends ABackend implements
->orderBy($query->func()->lower('displayname'), 'ASC')
->addOrderBy('uid_lower', 'ASC')
->setMaxResults($limit)
->setFirstResult($offset);
->setFirstResult($offset ?? 0);
$result = $query->executeQuery();
$displayNames = [];

View file

@ -47,4 +47,10 @@ interface ICompositeExpression {
* @since 8.2.0
*/
public function getType(): string;
/**
* Case the composite expression to string.
* @since 34.0.0
*/
public function __toString(): string;
}

View file

@ -8,6 +8,7 @@
namespace OCP\DB\QueryBuilder;
use Doctrine\DBAL\Query\Expression\ExpressionBuilder;
use OCP\AppFramework\Attribute\Consumable;
/**
* This class provides a wrapper around Doctrine's ExpressionBuilder
@ -15,6 +16,7 @@ use Doctrine\DBAL\Query\Expression\ExpressionBuilder;
*
* @psalm-taint-specialize
*/
#[Consumable(since: '8.2.0')]
interface IExpressionBuilder {
/**
* @since 9.0.0
@ -50,16 +52,15 @@ interface IExpressionBuilder {
* // (u.type = ?) AND (u.role = ?)
* $expr->andX('u.type = ?', 'u.role = ?'));
*
* @param mixed ...$x Optional clause. Defaults = null, but requires
* at least one defined when converting to string.
* @param ICompositeExpression|string ...$x Optional clause. Defaults = null, but requires
* at least one defined when converting to string.
*
* @return \OCP\DB\QueryBuilder\ICompositeExpression
* @since 8.2.0
* @since 30.0.0 Calling the method without any arguments is deprecated and will throw with the next Doctrine/DBAL update
*
* @psalm-taint-sink sql $x
*/
public function andX(...$x): ICompositeExpression;
public function andX(ICompositeExpression|string ...$x): ICompositeExpression;
/**
* Creates a disjunction of the given boolean expressions.
@ -70,25 +71,24 @@ interface IExpressionBuilder {
* // (u.type = ?) OR (u.role = ?)
* $qb->where($qb->expr()->orX('u.type = ?', 'u.role = ?'));
*
* @param mixed ...$x Optional clause. Defaults = null, but requires
* at least one defined when converting to string.
* @param ICompositeExpression|string ...$x Optional clause. Defaults = null, but requires
* at least one defined when converting to string.
*
* @return \OCP\DB\QueryBuilder\ICompositeExpression
* @since 8.2.0
* @since 30.0.0 Calling the method without any arguments is deprecated and will throw with the next Doctrine/DBAL update
*
* @psalm-taint-sink sql $x
*/
public function orX(...$x): ICompositeExpression;
public function orX(ICompositeExpression|string ...$x): ICompositeExpression;
/**
* Creates a comparison expression.
*
* @param mixed $x The left expression.
* @param string|ILiteral|IQueryFunction|IParameter $x The left expression.
* @param string $operator One of the IExpressionBuilder::* constants.
* @param mixed $y The right expression.
* @param mixed|null $type one of the IQueryBuilder::PARAM_* constants
* required when comparing text fields for oci compatibility
* @param string|ILiteral|IQueryFunction|IParameter $y The right expression.
* @param IQueryBuilder::PARAM_*|null $type one of the IQueryBuilder::PARAM_* constants
* required when comparing text fields for oci compatibility
*
* @return string
* @since 8.2.0 - Parameter $type was added in 9.0.0
@ -98,45 +98,44 @@ interface IExpressionBuilder {
* @psalm-taint-sink sql $y
* @psalm-taint-sink sql $type
*/
public function comparison($x, string $operator, $y, $type = null): string;
public function comparison(string|ILiteral|IQueryFunction|IParameter $x, string $operator, string|ILiteral|IQueryFunction|IParameter $y, string|int|null $type = null): string;
/**
* Creates an equality comparison expression with the given arguments.
*
* First argument is considered the left expression and the second is the right expression.
* When converted to string, it will generated a <left expr> = <right expr>. Example:
* When converted to string, it will generate a <left expr> = <right expr>. Example:
*
* [php]
* // u.id = ?
* $expr->eq('u.id', '?');
*
* @param mixed $x The left expression.
* @param mixed $y The right expression.
* @param mixed|null $type one of the IQueryBuilder::PARAM_* constants
* required when comparing text fields for oci compatibility
* @param string|ILiteral|IQueryFunction|IParameter $x The left expression.
* @param string|ILiteral|IQueryFunction|IParameter $y The right expression.
* @param IQueryBuilder::PARAM_*|null $type one of the IQueryBuilder::PARAM_* constants
* required when comparing text fields for oci compatibility
*
* @return string
* @since 8.2.0 - Parameter $type was added in 9.0.0
*
* @psalm-taint-sink sql $x
* @psalm-taint-sink sql $y
* @psalm-taint-sink sql $type
*/
public function eq($x, $y, $type = null): string;
public function eq(string|ILiteral|IQueryFunction|IParameter $x, string|ILiteral|IQueryFunction|IParameter $y, string|int|null $type = null): string;
/**
* Creates a non equality comparison expression with the given arguments.
* First argument is considered the left expression and the second is the right expression.
* When converted to string, it will generated a <left expr> <> <right expr>. Example:
* When converted to string, it will generate a <left expr> <> <right expr>. Example:
*
* [php]
* // u.id <> 1
* $q->where($q->expr()->neq('u.id', '1'));
*
* @param mixed $x The left expression.
* @param mixed $y The right expression.
* @param mixed|null $type one of the IQueryBuilder::PARAM_* constants
* required when comparing text fields for oci compatibility
* @param string|ILiteral|IQueryFunction|IParameter $x The left expression.
* @param string|ILiteral|IQueryFunction|IParameter $y The right expression.
* @param IQueryBuilder::PARAM_*|null $type one of the IQueryBuilder::PARAM_* constants
* required when comparing text fields for oci compatibility
*
* @return string
* @since 8.2.0 - Parameter $type was added in 9.0.0
@ -145,21 +144,21 @@ interface IExpressionBuilder {
* @psalm-taint-sink sql $y
* @psalm-taint-sink sql $type
*/
public function neq($x, $y, $type = null): string;
public function neq(string|ILiteral|IQueryFunction|IParameter $x, string|ILiteral|IQueryFunction|IParameter $y, string|int|null $type = null): string;
/**
* Creates a lower-than comparison expression with the given arguments.
* First argument is considered the left expression and the second is the right expression.
* When converted to string, it will generated a <left expr> < <right expr>. Example:
* When converted to string, it will generate a <left expr> < <right expr>. Example:
*
* [php]
* // u.id < ?
* $q->where($q->expr()->lt('u.id', '?'));
*
* @param mixed $x The left expression.
* @param mixed $y The right expression.
* @param mixed|null $type one of the IQueryBuilder::PARAM_* constants
* required when comparing text fields for oci compatibility
* @param string|ILiteral|IQueryFunction|IParameter $x The left expression.
* @param string|ILiteral|IQueryFunction|IParameter $y The right expression.
* @param IQueryBuilder::PARAM_*|null $type one of the IQueryBuilder::PARAM_* constants
* required when comparing text fields for oci compatibility
*
* @return string
* @since 8.2.0 - Parameter $type was added in 9.0.0
@ -168,76 +167,81 @@ interface IExpressionBuilder {
* @psalm-taint-sink sql $y
* @psalm-taint-sink sql $type
*/
public function lt($x, $y, $type = null): string;
public function lt(string|ILiteral|IQueryFunction|IParameter $x, string|ILiteral|IQueryFunction|IParameter $y, string|int|null $type = null): string;
/**
* Creates a lower-than-equal comparison expression with the given arguments.
* First argument is considered the left expression and the second is the right expression.
* When converted to string, it will generated a <left expr> <= <right expr>. Example:
* When converted to string, it will generate a <left expr> <= <right expr>. Example:
*
* [php]
* // u.id <= ?
* $q->where($q->expr()->lte('u.id', '?'));
*
* @param mixed $x The left expression.
* @param mixed $y The right expression.
* @param mixed|null $type one of the IQueryBuilder::PARAM_* constants
* required when comparing text fields for oci compatibility
* @param string|ILiteral|IQueryFunction|IParameter $x The left expression.
* @param string|ILiteral|IQueryFunction|IParameter $y The right expression.
* @param IQueryBuilder::PARAM_*|null $type one of the IQueryBuilder::PARAM_* constants
* required when comparing text fields for oci compatibility
*
* @return string
* @since 8.2.0 - Parameter $type was added in 9.0.0
*
* @psalm-taint-sink sql $x
* @psalm-taint-sink sql $y
* @psalm-taint-sink sql $type
*/
public function lte($x, $y, $type = null): string;
public function lte(string|ILiteral|IQueryFunction|IParameter $x, string|ILiteral|IQueryFunction|IParameter $y, string|int|null $type = null): string;
/**
* Creates a greater-than comparison expression with the given arguments.
* First argument is considered the left expression and the second is the right expression.
* When converted to string, it will generated a <left expr> > <right expr>. Example:
* When converted to string, it will generate a <left expr> > <right expr>. Example:
*
* [php]
* // u.id > ?
* $q->where($q->expr()->gt('u.id', '?'));
*
* @param mixed $x The left expression.
* @param mixed $y The right expression.
* @param mixed|null $type one of the IQueryBuilder::PARAM_* constants
* required when comparing text fields for oci compatibility
* @param string|ILiteral|IQueryFunction|IParameter $x The left expression.
* @param string|ILiteral|IQueryFunction|IParameter $y The right expression.
* @param IQueryBuilder::PARAM_*|null $type one of the IQueryBuilder::PARAM_* constants
* required when comparing text fields for oci compatibility
*
* @return string
* @since 8.2.0 - Parameter $type was added in 9.0.0
*
* @psalm-taint-sink sql $x
* @psalm-taint-sink sql $y
* @psalm-taint-sink sql $type
*/
public function gt($x, $y, $type = null): string;
public function gt(
string|ILiteral|IQueryFunction|IParameter $x,
string|ILiteral|IQueryFunction|IParameter $y,
string|int|null $type = null,
): string;
/**
* Creates a greater-than-equal comparison expression with the given arguments.
* First argument is considered the left expression and the second is the right expression.
* When converted to string, it will generated a <left expr> >= <right expr>. Example:
* When converted to string, it will generate a <left expr> >= <right expr>. Example:
*
* [php]
* // u.id >= ?
* $q->where($q->expr()->gte('u.id', '?'));
*
* @param mixed $x The left expression.
* @param mixed $y The right expression.
* @param mixed|null $type one of the IQueryBuilder::PARAM_* constants
* required when comparing text fields for oci compatibility
* @param string|ILiteral|IQueryFunction|IParameter $x The left expression.
* @param string|ILiteral|IQueryFunction|IParameter $y The right expression.
* @param IQueryBuilder::PARAM_*|null $type one of the IQueryBuilder::PARAM_* constants
* required when comparing text fields for oci compatibility
*
* @return string
* @since 8.2.0 - Parameter $type was added in 9.0.0
*
* @psalm-taint-sink sql $x
* @psalm-taint-sink sql $y
* @psalm-taint-sink sql $type
*/
public function gte($x, $y, $type = null): string;
public function gte(
string|ILiteral|IParameter|IQueryFunction $x,
string|ILiteral|IParameter|IQueryFunction $y,
int|string|null $type = null,
): string;
/**
* Creates an IS NULL expression with the given arguments.
@ -249,44 +253,46 @@ interface IExpressionBuilder {
*
* @psalm-taint-sink sql $x
*/
public function isNull($x): string;
public function isNull(string|ILiteral|IParameter|IQueryFunction $x): string;
/**
* Creates an IS NOT NULL expression with the given arguments.
*
* @param string|ILiteral|IParameter|IQueryFunction $x The field in string format to be restricted by IS NOT NULL.
*
* @return string
* @since 8.2.0
*
* @psalm-taint-sink sql $x
*/
public function isNotNull($x): string;
public function isNotNull(string|ILiteral|IParameter|IQueryFunction $x): string;
/**
* Creates a LIKE() comparison expression with the given arguments.
*
* @param ILiteral|IParameter|IQueryFunction|string $x Field in string format to be inspected by LIKE() comparison.
* @param mixed $y Argument to be used in LIKE() comparison.
* @param mixed|null $type one of the IQueryBuilder::PARAM_* constants
* required when comparing text fields for oci compatibility
* @param ILiteral|IParameter|IQueryFunction|string $y Argument to be used in LIKE() comparison.
* @param IQueryBuilder::PARAM_*|null $type one of the IQueryBuilder::PARAM_* constants
* required when comparing text fields for oci compatibility
*
* @return string
* @since 8.2.0 - Parameter $type was added in 9.0.0
*
* @psalm-taint-sink sql $x
* @psalm-taint-sink sql $y
* @psalm-taint-sink sql $type
*/
public function like($x, $y, $type = null): string;
public function like(
ILiteral|IParameter|IQueryFunction|string $x,
ILiteral|IParameter|IQueryFunction|string $y,
int|string|null $type = null,
): string;
/**
* Creates a NOT LIKE() comparison expression with the given arguments.
*
* @param ILiteral|IParameter|IQueryFunction|string $x Field in string format to be inspected by NOT LIKE() comparison.
* @param mixed $y Argument to be used in NOT LIKE() comparison.
* @param mixed|null $type one of the IQueryBuilder::PARAM_* constants
* required when comparing text fields for oci compatibility
* @param ILiteral|IParameter|IQueryFunction|string $y Argument to be used in NOT LIKE() comparison.
* @param IQueryBuilder::PARAM_*|null $type one of the IQueryBuilder::PARAM_* constants
* required when comparing text fields for oci compatibility
*
* @return string
* @since 8.2.0 - Parameter $type was added in 9.0.0
@ -295,15 +301,19 @@ interface IExpressionBuilder {
* @psalm-taint-sink sql $y
* @psalm-taint-sink sql $type
*/
public function notLike($x, $y, $type = null): string;
public function notLike(
ILiteral|IParameter|IQueryFunction|string $x,
ILiteral|IParameter|IQueryFunction|string $y,
int|string|null $type = null,
): string;
/**
* Creates a ILIKE() comparison expression with the given arguments.
* Creates an ILIKE() comparison expression with the given arguments.
*
* @param string $x Field in string format to be inspected by ILIKE() comparison.
* @param mixed $y Argument to be used in ILIKE() comparison.
* @param mixed|null $type one of the IQueryBuilder::PARAM_* constants
* required when comparing text fields for oci compatibility
* @param ILiteral|IParameter|IQueryFunction|string $x Field in string format to be inspected by ILIKE() comparison.
* @param ILiteral|IParameter|IQueryFunction|string $y Argument to be used in ILIKE() comparison.
* @param IQueryBuilder::PARAM_*|null $type one of the IQueryBuilder::PARAM_* constants
* required when comparing text fields for oci compatibility
*
* @return string
* @since 9.0.0
@ -312,32 +322,39 @@ interface IExpressionBuilder {
* @psalm-taint-sink sql $y
* @psalm-taint-sink sql $type
*/
public function iLike($x, $y, $type = null): string;
public function iLike(
ILiteral|IParameter|IQueryFunction|string $x,
ILiteral|IParameter|IQueryFunction|string $y,
int|string|null $type = null,
): string;
/**
* Creates a IN () comparison expression with the given arguments.
* Creates an IN () comparison expression with the given arguments.
*
* @param ILiteral|IParameter|IQueryFunction|string $x The field in string format to be inspected by IN() comparison.
* @param ILiteral|IParameter|IQueryFunction|string|array $y The placeholder or the array of values to be used by IN() comparison.
* @param mixed|null $type one of the IQueryBuilder::PARAM_* constants
* required when comparing text fields for oci compatibility
* @param IQueryBuilder::PARAM_*|null $type one of the IQueryBuilder::PARAM_* constants
* required when comparing text fields for oci compatibility
*
* @return string
* @since 8.2.0 - Parameter $type was added in 9.0.0
*
* @psalm-taint-sink sql $x
* @psalm-taint-sink sql $y
* @psalm-taint-sink sql $type
*/
public function in($x, $y, $type = null): string;
public function in(
ILiteral|IParameter|IQueryFunction|string $x,
ILiteral|IParameter|IQueryFunction|string|array $y,
int|string|null $type = null,
): string;
/**
* Creates a NOT IN () comparison expression with the given arguments.
*
* @param ILiteral|IParameter|IQueryFunction|string $x The field in string format to be inspected by NOT IN() comparison.
* @param ILiteral|IParameter|IQueryFunction|string|array $y The placeholder or the array of values to be used by NOT IN() comparison.
* @param mixed|null $type one of the IQueryBuilder::PARAM_* constants
* required when comparing text fields for oci compatibility
* @param IQueryBuilder::PARAM_*|null $type one of the IQueryBuilder::PARAM_* constants
* required when comparing text fields for oci compatibility
*
* @return string
* @since 8.2.0 - Parameter $type was added in 9.0.0
@ -346,29 +363,31 @@ interface IExpressionBuilder {
* @psalm-taint-sink sql $y
* @psalm-taint-sink sql $type
*/
public function notIn($x, $y, $type = null): string;
public function notIn(
ILiteral|IParameter|IQueryFunction|string $x,
ILiteral|IParameter|IQueryFunction|string|array $y,
int|string|null $type = null,
): string;
/**
* Creates a $x = '' statement, because Oracle needs a different check
* Creates a `$x = ''` statement, because Oracle needs a different check
*
* @param string|ILiteral|IParameter|IQueryFunction $x The field in string format to be inspected by the comparison.
* @return string
* @since 13.0.0
*
* @psalm-taint-sink sql $x
*/
public function emptyString($x): string;
public function emptyString(string|ILiteral|IParameter|IQueryFunction $x): string;
/**
* Creates a `$x <> ''` statement, because Oracle needs a different check
*
* @param string|ILiteral|IParameter|IQueryFunction $x The field in string format to be inspected by the comparison.
* @return string
* @since 13.0.0
*
* @psalm-taint-sink sql $x
*/
public function nonEmptyString($x): string;
public function nonEmptyString(string|ILiteral|IParameter|IQueryFunction $x): string;
/**
@ -382,46 +401,41 @@ interface IExpressionBuilder {
* @psalm-taint-sink sql $x
* @psalm-taint-sink sql $y
*/
public function bitwiseAnd($x, int $y): IQueryFunction;
public function bitwiseAnd(string|ILiteral $x, int $y): IQueryFunction;
/**
* Creates a bitwise OR comparison
*
* @param string|ILiteral $x The field or value to check
* @param int $y Bitmap that must be set
* @return IQueryFunction
* @since 12.0.0
*
* @psalm-taint-sink sql $x
* @psalm-taint-sink sql $y
*/
public function bitwiseOr($x, int $y): IQueryFunction;
public function bitwiseOr(string|ILiteral $x, int $y): IQueryFunction;
/**
* Quotes a given input parameter.
*
* @param mixed $input The parameter to be quoted.
* @param int $type One of the IQueryBuilder::PARAM_* constants
* @param IQueryBuilder::PARAM_* $type One of the IQueryBuilder::PARAM_* constants
*
* @return ILiteral
* @since 8.2.0
*
* @psalm-taint-sink sql $input
* @psalm-taint-sink sql $type
*/
public function literal($input, $type = IQueryBuilder::PARAM_STR): ILiteral;
public function literal(mixed $input, int|string $type = IQueryBuilder::PARAM_STR): ILiteral;
/**
* Returns a IQueryFunction that casts the column to the given type
*
* @param string|IQueryFunction $column
* @param mixed $type One of IQueryBuilder::PARAM_*
* @psalm-param IQueryBuilder::PARAM_* $type
* @return IQueryFunction
* @param IQueryBuilder::PARAM_* $type
* @since 9.0.0
*
* @psalm-taint-sink sql $column
* @psalm-taint-sink sql $type
*/
public function castColumn($column, $type): IQueryFunction;
public function castColumn(string|IQueryFunction|ILiteral|IParameter $column, int|string $type): IQueryFunction;
}

View file

@ -6,11 +6,14 @@
*/
namespace OCP\DB\QueryBuilder;
use OCP\AppFramework\Attribute\Consumable;
/**
* This class provides a builder for sql some functions
*
* @since 12.0.0
*/
#[Consumable(since: '12.0.0')]
interface IFunctionBuilder {
/**
* Calculates the MD5 hash of a given input
@ -20,7 +23,7 @@ interface IFunctionBuilder {
* @return IQueryFunction
* @since 12.0.0
*/
public function md5($input): IQueryFunction;
public function md5(string|ILiteral|IParameter|IQueryFunction $input): IQueryFunction;
/**
* Combines two input strings
@ -31,7 +34,7 @@ interface IFunctionBuilder {
* @return IQueryFunction
* @since 12.0.0
*/
public function concat($x, ...$expr): IQueryFunction;
public function concat(string|ILiteral|IParameter|IQueryFunction $x, string|ILiteral|IParameter|IQueryFunction ...$expr): IQueryFunction;
/**
* Returns a string which is the concatenation of all non-NULL values of X
@ -47,7 +50,7 @@ interface IFunctionBuilder {
* @return IQueryFunction
* @since 24.0.0
*/
public function groupConcat($expr, ?string $separator = ','): IQueryFunction;
public function groupConcat(string|IQueryFunction $expr, ?string $separator = ','): IQueryFunction;
/**
* Takes a substring from the input string
@ -56,29 +59,29 @@ interface IFunctionBuilder {
* @param string|ILiteral|IParameter|IQueryFunction $start The start of the substring, note that counting starts at 1
* @param null|ILiteral|IParameter|IQueryFunction $length The length of the substring
*
* @return IQueryFunction
* @since 12.0.0
*/
public function substring($input, $start, $length = null): IQueryFunction;
public function substring(
string|ILiteral|IParameter|IQueryFunction $input,
string|ILiteral|IParameter|IQueryFunction $start,
null|ILiteral|IParameter|IQueryFunction $length = null,
): IQueryFunction;
/**
* Takes the sum of all rows in a column
*
* @param string|ILiteral|IParameter|IQueryFunction $field the column to sum
*
* @return IQueryFunction
* @since 12.0.0
*/
public function sum($field): IQueryFunction;
public function sum(string|ILiteral|IParameter|IQueryFunction $field): IQueryFunction;
/**
* Transforms a string field or value to lower case
*
* @param string|ILiteral|IParameter|IQueryFunction $field
* @return IQueryFunction
* @since 14.0.0
*/
public function lower($field): IQueryFunction;
public function lower(string|ILiteral|IParameter|IQueryFunction $field): IQueryFunction;
/**
* @param string|ILiteral|IParameter|IQueryFunction $x The first input field or number
@ -86,7 +89,10 @@ interface IFunctionBuilder {
* @return IQueryFunction
* @since 14.0.0
*/
public function add($x, $y): IQueryFunction;
public function add(
string|ILiteral|IParameter|IQueryFunction $x,
string|ILiteral|IParameter|IQueryFunction $y,
): IQueryFunction;
/**
* @param string|ILiteral|IParameter|IQueryFunction $x The first input field or number
@ -94,7 +100,10 @@ interface IFunctionBuilder {
* @return IQueryFunction
* @since 14.0.0
*/
public function subtract($x, $y): IQueryFunction;
public function subtract(
string|ILiteral|IParameter|IQueryFunction $x,
string|ILiteral|IParameter|IQueryFunction $y,
): IQueryFunction;
/**
* @param string|ILiteral|IParameter|IQueryFunction $count The input to be counted
@ -103,7 +112,7 @@ interface IFunctionBuilder {
* @return IQueryFunction
* @since 14.0.0
*/
public function count($count = '', $alias = ''): IQueryFunction;
public function count(string|ILiteral|IParameter|IQueryFunction $count = '', string $alias = ''): IQueryFunction;
/**
* @param string|ILiteral|IParameter|IQueryFunction $field The input to be measured
@ -112,16 +121,15 @@ interface IFunctionBuilder {
* @return IQueryFunction
* @since 24.0.0
*/
public function octetLength($field, $alias = ''): IQueryFunction;
public function octetLength(string|ILiteral|IParameter|IQueryFunction $field, string $alias = ''): IQueryFunction;
/**
* @param string|ILiteral|IParameter|IQueryFunction $field The input to be measured
* @param string $alias Alias for the length
*
* @return IQueryFunction
* @since 24.0.0
*/
public function charLength($field, $alias = ''): IQueryFunction;
public function charLength(string|ILiteral|IParameter|IQueryFunction $field, string $alias = ''): IQueryFunction;
/**
* Takes the maximum of all rows in a column
@ -130,10 +138,9 @@ interface IFunctionBuilder {
*
* @param string|ILiteral|IParameter|IQueryFunction $field the column to maximum
*
* @return IQueryFunction
* @since 18.0.0
*/
public function max($field): IQueryFunction;
public function max(string|ILiteral|IParameter|IQueryFunction $field): IQueryFunction;
/**
* Takes the minimum of all rows in a column
@ -142,34 +149,33 @@ interface IFunctionBuilder {
*
* @param string|ILiteral|IParameter|IQueryFunction $field the column to minimum
*
* @return IQueryFunction
* @since 18.0.0
*/
public function min($field): IQueryFunction;
public function min(string|ILiteral|IParameter|IQueryFunction $field): IQueryFunction;
/**
* Takes the maximum of multiple values
*
* If you want to get the maximum value of all rows in a column, use `max` instead
*
* @param string|ILiteral|IParameter|IQueryFunction $x
* @param string|ILiteral|IParameter|IQueryFunction $y
* @return IQueryFunction
* @since 18.0.0
*/
public function greatest($x, $y): IQueryFunction;
public function greatest(
string|ILiteral|IParameter|IQueryFunction $x,
string|ILiteral|IParameter|IQueryFunction $y,
): IQueryFunction;
/**
* Takes the minimum of multiple values
*
* If you want to get the minimum value of all rows in a column, use `min` instead
*
* @param string|ILiteral|IParameter|IQueryFunction $x
* @param string|ILiteral|IParameter|IQueryFunction $y
* @return IQueryFunction
* @since 18.0.0
*/
public function least($x, $y): IQueryFunction;
public function least(
string|ILiteral|IParameter|IQueryFunction $x,
string|ILiteral|IParameter|IQueryFunction $y,
): IQueryFunction;
/**
* Get the current date and time as a UNIX timestamp.

View file

@ -11,6 +11,7 @@ use Doctrine\DBAL\ArrayParameterType;
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 @@ use OCP\IDBConnection;
*
* @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 @@ interface IQueryBuilder {
* 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 @@ interface IQueryBuilder {
* 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 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 @@ interface IQueryBuilder {
* @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 @@ interface IQueryBuilder {
* @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.
@ -245,11 +244,11 @@ interface IQueryBuilder {
* @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.
*
* @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.
@ -267,11 +266,11 @@ interface IQueryBuilder {
*
* @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,35 +278,35 @@ interface IQueryBuilder {
* @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.
*
* @return array The currently defined query parameter types indexed by parameter index or name.
* @return list<self::PARAM_*> 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.
* @return self::PARAM_* The value of the bound parameter type.
* @since 8.2.0
*/
public function getParameterType($key);
public function getParameterType(int|string $key): int|string;
/**
* Sets the position of the first result to retrieve (the "offset").
@ -317,7 +316,7 @@ interface IQueryBuilder {
* @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 +325,17 @@ interface IQueryBuilder {
* @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 +344,7 @@ interface IQueryBuilder {
* @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.
@ -359,13 +358,13 @@ interface IQueryBuilder {
* </code>
*
* @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 +376,16 @@ interface IQueryBuilder {
* ->leftJoin('u', 'phonenumbers', 'p', 'u.id = p.user_id');
* </code>
*
* @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|ILiteral $select, string $alias): self;
/**
* Specifies an item that is to be returned uniquely in the query result.
@ -397,14 +396,14 @@ interface IQueryBuilder {
* ->from('users');
* </code>
*
* @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.
@ -418,13 +417,13 @@ interface IQueryBuilder {
* </code>
*
* @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 +437,15 @@ interface IQueryBuilder {
* </code>
*
* @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
* @return $this This QueryBuilder instance.
*
* @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 +460,15 @@ interface IQueryBuilder {
* </code>
*
* @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
@ -487,13 +486,13 @@ interface IQueryBuilder {
* </code>
*
* @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
@ -507,13 +506,13 @@ interface IQueryBuilder {
*
* @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.
@ -527,10 +526,10 @@ interface IQueryBuilder {
*
* @param string $fromAlias The alias that points to a from clause.
* @param string|IQueryFunction $join The table name to join.
* @param string $alias The alias of the join table.
* @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 +537,7 @@ interface IQueryBuilder {
* @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.
@ -552,10 +551,10 @@ interface IQueryBuilder {
*
* @param string $fromAlias The alias that points to a from clause.
* @param string|IQueryFunction $join The table name to join.
* @param string $alias The alias of the join table.
* @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 +562,7 @@ interface IQueryBuilder {
* @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.
@ -577,10 +576,10 @@ interface IQueryBuilder {
*
* @param string $fromAlias The alias that points to a from clause.
* @param string|IQueryFunction $join The table name to join.
* @param string $alias The alias of the join table.
* @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 +588,7 @@ interface IQueryBuilder {
* @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.
@ -603,10 +602,10 @@ interface IQueryBuilder {
*
* @param string $fromAlias The alias that points to a from clause.
* @param string|IQueryFunction $join The table name to join.
* @param string $alias The alias of the join table.
* @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 +613,7 @@ interface IQueryBuilder {
* @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.
@ -628,14 +627,14 @@ interface IQueryBuilder {
*
* @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.
@ -661,13 +660,13 @@ interface IQueryBuilder {
* </code>
*
* @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
@ -682,7 +681,6 @@ interface IQueryBuilder {
* </code>
*
* @param mixed ...$where The query restrictions.
*
* @return $this This QueryBuilder instance.
*
* @see where()
@ -690,7 +688,7 @@ interface IQueryBuilder {
*
* @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
@ -705,7 +703,6 @@ interface IQueryBuilder {
* </code>
*
* @param mixed ...$where The WHERE statement.
*
* @return $this This QueryBuilder instance.
*
* @see where()
@ -713,7 +710,7 @@ interface IQueryBuilder {
*
* @psalm-taint-sink sql $where
*/
public function orWhere(...$where);
public function orWhere(...$where): self;
/**
* Specifies a grouping over the results of the query.
@ -727,13 +724,13 @@ interface IQueryBuilder {
* </code>
*
* @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.
@ -747,13 +744,13 @@ interface IQueryBuilder {
* </code>
*
* @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.
@ -771,14 +768,14 @@ interface IQueryBuilder {
*
* @param string $column The column into which the value should be inserted.
* @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, ILiteral|IParameter|IQueryFunction|string $value): self;
/**
* Specifies values for an insert query indexed by column names.
@ -795,60 +792,60 @@ interface IQueryBuilder {
* );
* </code>
*
* @param array $values The values to specify for the insert query indexed by column names.
*
* @param array<string, IParameter|ILiteral|IFunctionBuilder|string|int> $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.
* Replaces any previous having restrictions, if any.
*
* @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
* conjunction with any existing having restrictions.
*
* @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
* disjunction with any existing having restrictions.
*
* @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 +853,13 @@ interface IQueryBuilder {
* @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 +867,25 @@ interface IQueryBuilder {
* @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 +897,7 @@ interface IQueryBuilder {
* @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 +909,7 @@ interface IQueryBuilder {
* @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 +943,7 @@ interface IQueryBuilder {
*
* @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 +970,7 @@ interface IQueryBuilder {
*
* @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 +991,7 @@ interface IQueryBuilder {
*
* @psalm-taint-escape sql
*/
public function createParameter($name);
public function createParameter(string $name): IParameter;
/**
* Creates a new function.
@ -1027,7 +1020,7 @@ interface IQueryBuilder {
*
* @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
@ -1047,13 +1040,11 @@ interface IQueryBuilder {
* @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;
@ -1061,19 +1052,14 @@ interface IQueryBuilder {
/**
* 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
* @return $this This QueryBuilder instance.
* @since 30.0.0
*/
public function hintShardKey(string $column, mixed $value, bool $overwrite = false): self;
@ -1081,7 +1067,7 @@ interface IQueryBuilder {
/**
* Set the query to run across all shards if sharding is enabled.
*
* @return $this
* @return $this This QueryBuilder instance.
* @since 30.0.0
*/
public function runAcrossAllShards(): self;
@ -1089,7 +1075,7 @@ interface IQueryBuilder {
/**
* 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;

View file

@ -55,6 +55,7 @@
<directory name="lib"/>
<directory name="ocs"/>
<directory name="tests/lib/Comments"/>
<directory name="tests/lib/DB/QueryBuilder"/>
<directory name="ocs-provider"/>
<file name="cron.php"/>
<file name="index.php"/>
@ -171,52 +172,61 @@
<errorLevel type="suppress">
<directory name="lib" />
<directory name="apps/*/tests" />
<directory name="tests" />
</errorLevel>
</DeprecatedClass>
<DeprecatedConstant>
<errorLevel type="suppress">
<directory name="lib" />
<directory name="apps/*/tests" />
<directory name="tests" />
</errorLevel>
</DeprecatedConstant>
<DeprecatedFunction>
<errorLevel type="suppress">
<directory name="lib" />
<directory name="apps/*/tests" />
<directory name="tests" />
</errorLevel>
</DeprecatedFunction>
<DeprecatedInterface>
<errorLevel type="suppress">
<directory name="lib" />
<directory name="apps/*/tests" />
<directory name="tests" />
</errorLevel>
</DeprecatedInterface>
<DeprecatedMethod>
<errorLevel type="suppress">
<directory name="lib" />
<directory name="apps/*/tests" />
<directory name="tests" />
</errorLevel>
</DeprecatedMethod>
<DeprecatedProperty>
<errorLevel type="suppress">
<directory name="lib" />
<directory name="apps/*/tests" />
<directory name="tests" />
</errorLevel>
</DeprecatedProperty>
<DeprecatedTrait>
<errorLevel type="suppress">
<directory name="lib" />
<directory name="apps/*/tests" />
<directory name="tests" />
</errorLevel>
</DeprecatedTrait>
<InternalMethod>
<errorLevel type="suppress">
<directory name="apps/*/tests" />
<directory name="tests" />
</errorLevel>
</InternalMethod>
<InternalClass>
<errorLevel type="suppress">
<directory name="apps/*/tests" />
<directory name="tests" />
</errorLevel>
</InternalClass>
</issueHandlers>

View file

@ -10,6 +10,7 @@ namespace Test\AppFramework\Db;
use OCP\AppFramework\Db\Entity;
use OCP\AppFramework\Db\QBMapper;
use OCP\DB\QueryBuilder\IExpressionBuilder;
use OCP\DB\QueryBuilder\IParameter;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\DB\Types;
use OCP\IDBConnection;
@ -28,6 +29,8 @@ use PHPUnit\Framework\MockObject\MockObject;
* @method void setIntegerProp(integer $integerProp)
* @method ?\DateTimeImmutable getDatetimeProp()
* @method void setDatetimeProp(?\DateTimeImmutable $datetime)
* @method array getJsonProp()
* @method void setJsonProp(array $jsonProp)
*/
class QBTestEntity extends Entity {
protected $intProp;
@ -92,15 +95,12 @@ class QBMapperTest extends \Test\TestCase {
->disableOriginalConstructor()
->getMock();
$this->qb->method('expr')->willReturn($this->expr);
$this->db->method('getQueryBuilder')->willReturn($this->qb);
$this->mapper = new QBTestMapper($this->db);
}
public function testInsertEntityParameterTypeMapping(): void {
$datetime = new \DateTimeImmutable();
$entity = new QBTestEntity();
@ -119,18 +119,19 @@ class QBMapperTest extends \Test\TestCase {
$datetimeParam = $this->qb->createNamedParameter('datetime_prop', IQueryBuilder::PARAM_DATETIME_IMMUTABLE);
$createNamedParameterCalls = [
[123, IQueryBuilder::PARAM_INT, null],
[true, IQueryBuilder::PARAM_BOOL, null],
['string', IQueryBuilder::PARAM_STR, null],
[456, IQueryBuilder::PARAM_INT, null],
[false, IQueryBuilder::PARAM_BOOL, null],
[$datetime, IQueryBuilder::PARAM_DATETIME_IMMUTABLE, null],
[123, IQueryBuilder::PARAM_INT, null, $intParam],
[true, IQueryBuilder::PARAM_BOOL, null, $boolParam],
['string', IQueryBuilder::PARAM_STR, null, $stringParam],
[456, IQueryBuilder::PARAM_INT, null, $integerParam],
[false, IQueryBuilder::PARAM_BOOL, null, $booleanParam],
[$datetime, IQueryBuilder::PARAM_DATETIME_IMMUTABLE, null, $datetimeParam],
];
$this->qb->expects($this->exactly(6))
->method('createNamedParameter')
->willReturnCallback(function () use (&$createNamedParameterCalls): void {
->willReturnCallback(function () use (&$createNamedParameterCalls): IParameter {
$expected = array_shift($createNamedParameterCalls);
$this->assertEquals($expected, func_get_args());
$this->assertEquals([$expected[0], $expected[1], $expected[2]], func_get_args());
return $expected[3];
});
$setValueCalls = [
@ -143,15 +144,15 @@ class QBMapperTest extends \Test\TestCase {
];
$this->qb->expects($this->exactly(6))
->method('setValue')
->willReturnCallback(function () use (&$setValueCalls): void {
->willReturnCallback(function () use (&$setValueCalls): IQueryBuilder {
$expected = array_shift($setValueCalls);
$this->assertEquals($expected, func_get_args());
return $this->qb;
});
$this->mapper->insert($entity);
}
public function testUpdateEntityParameterTypeMapping(): void {
$datetime = new \DateTimeImmutable();
$entity = new QBTestEntity();
@ -174,20 +175,21 @@ class QBMapperTest extends \Test\TestCase {
$datetimeParam = $this->qb->createNamedParameter('datetime_prop', IQueryBuilder::PARAM_DATETIME_IMMUTABLE);
$createNamedParameterCalls = [
[123, IQueryBuilder::PARAM_INT, null],
[true, IQueryBuilder::PARAM_BOOL, null],
['string', IQueryBuilder::PARAM_STR, null],
[456, IQueryBuilder::PARAM_INT, null],
[false, IQueryBuilder::PARAM_BOOL, null],
[['hello' => 'world'], IQueryBuilder::PARAM_JSON, null],
[$datetime, IQueryBuilder::PARAM_DATETIME_IMMUTABLE, null],
[789, IQueryBuilder::PARAM_INT, null],
[123, IQueryBuilder::PARAM_INT, null, $intParam],
[true, IQueryBuilder::PARAM_BOOL, null, $boolParam],
['string', IQueryBuilder::PARAM_STR, null, $stringParam],
[456, IQueryBuilder::PARAM_INT, null, $integerParam],
[false, IQueryBuilder::PARAM_BOOL, null, $booleanParam],
[['hello' => 'world'], IQueryBuilder::PARAM_JSON, null, $jsonParam],
[$datetime, IQueryBuilder::PARAM_DATETIME_IMMUTABLE, null, $datetimeParam],
[789, IQueryBuilder::PARAM_INT, null, $idParam],
];
$this->qb->expects($this->exactly(8))
->method('createNamedParameter')
->willReturnCallback(function () use (&$createNamedParameterCalls): void {
->willReturnCallback(function () use (&$createNamedParameterCalls): IParameter {
$expected = array_shift($createNamedParameterCalls);
$this->assertEquals($expected, func_get_args());
$this->assertEquals([$expected[0], $expected[1], $expected[2]], func_get_args());
return $expected[3];
});
$setCalls = [
@ -201,16 +203,16 @@ class QBMapperTest extends \Test\TestCase {
];
$this->qb->expects($this->exactly(7))
->method('set')
->willReturnCallback(function () use (&$setCalls): void {
->willReturnCallback(function () use (&$setCalls): IQueryBuilder {
$expected = array_shift($setCalls);
$this->assertEquals($expected, func_get_args());
return $this->qb;
});
$this->expr->expects($this->once())
->method('eq')
->with($this->equalTo('id'), $this->equalTo($idParam));
$this->mapper->update($entity);
}

View file

@ -54,7 +54,12 @@ class ManagerTest extends TestCase {
$this->connection->prepare($sql)->execute();
}
protected function addDatabaseEntry(?string $parentId, ?string $topmostParentId, ?\DateTimeInterface $creationDT = null, ?\DateTimeInterface $latestChildDT = null, $objectId = null, ?\DateTimeInterface $expireDate = null): string {
/**
* @param int|null|string $objectId
*
* @psalm-param 'file1'|'file2'|'file3'|int|null $objectId
*/
protected function addDatabaseEntry(?string $parentId, ?string $topmostParentId, ?\DateTimeInterface $creationDT = null, ?\DateTimeInterface $latestChildDT = null, string|int|null $objectId = null, ?\DateTimeInterface $expireDate = null): string {
$creationDT ??= new \DateTime();
$latestChildDT ??= new \DateTime('yesterday');
$objectId ??= 'file64';

View file

@ -14,13 +14,14 @@ use OCP\DB\Types;
use OCP\IConfig;
use OCP\IDBConnection;
use OCP\Server;
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\Attributes\Group;
use Test\TestCase;
#[\PHPUnit\Framework\Attributes\Group('DB')]
#[Group(name: 'DB')]
class ExpressionBuilderDBTest extends TestCase {
/** @var \Doctrine\DBAL\Connection|IDBConnection */
protected $connection;
protected $schemaSetup = false;
protected IDBConnection $connection;
protected bool $schemaSetup = false;
protected function setUp(): void {
parent::setUp();
@ -45,14 +46,8 @@ class ExpressionBuilderDBTest extends TestCase {
];
}
/**
*
* @param string $param1
* @param string $param2
* @param boolean $match
*/
#[\PHPUnit\Framework\Attributes\DataProvider('likeProvider')]
public function testLike($param1, $param2, $match): void {
#[DataProvider(methodName: 'likeProvider')]
public function testLike(string $param1, string $param2, bool $match): void {
$query = $this->connection->getQueryBuilder();
$query->select(new Literal('1'))
@ -82,14 +77,8 @@ class ExpressionBuilderDBTest extends TestCase {
];
}
/**
*
* @param string $param1
* @param string $param2
* @param boolean $match
*/
#[\PHPUnit\Framework\Attributes\DataProvider('ilikeProvider')]
public function testILike($param1, $param2, $match): void {
#[DataProvider(methodName: 'ilikeProvider')]
public function testILike(string $param1, string $param2, bool $match): void {
$query = $this->connection->getQueryBuilder();
$query->select(new Literal('1'))
@ -233,7 +222,11 @@ class ExpressionBuilderDBTest extends TestCase {
self::assertCount(1, $entries);
}
protected function createConfig($appId, $key, $value) {
/**
* @psalm-param '1'|'mykey' $key
* @psalm-param '4'|'myvalue' $value
*/
protected function createConfig(string $appId, string $key, string $value) {
$query = $this->connection->getQueryBuilder();
$query->insert('appconfig')
->values([

View file

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
@ -13,31 +15,19 @@ use OC\DB\QueryBuilder\ExpressionBuilder\ExpressionBuilder;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\IDBConnection;
use OCP\Server;
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\Attributes\Group;
use PHPUnit\Framework\MockObject\MockObject;
use Psr\Log\LoggerInterface;
use Test\TestCase;
/**
* Class ExpressionBuilderTest
*
*
* @package Test\DB\QueryBuilder
*/
#[\PHPUnit\Framework\Attributes\Group('DB')]
#[Group(name: 'DB')]
class ExpressionBuilderTest extends TestCase {
/** @var ExpressionBuilder */
protected $expressionBuilder;
/** @var DoctrineExpressionBuilder */
protected $doctrineExpressionBuilder;
/** @var IDBConnection */
protected $connection;
/** @var \Doctrine\DBAL\Connection */
protected $internalConnection;
/** @var LoggerInterface */
protected $logger;
protected ExpressionBuilder $expressionBuilder;
protected DoctrineExpressionBuilder $doctrineExpressionBuilder;
protected IDBConnection $connection;
protected \OC\DB\Connection $internalConnection;
protected LoggerInterface&MockObject $logger;
protected function setUp(): void {
parent::setUp();
@ -66,16 +56,8 @@ class ExpressionBuilderTest extends TestCase {
return $testSets;
}
/**
*
* @param string $comparison
* @param mixed $input1
* @param bool $isInput1Literal
* @param mixed $input2
* @param bool $isInput2Literal
*/
#[\PHPUnit\Framework\Attributes\DataProvider('dataComparison')]
public function testComparison($comparison, $input1, $isInput1Literal, $input2, $isInput2Literal): void {
#[DataProvider(methodName: 'dataComparison')]
public function testComparison(string $comparison, mixed $input1, bool $isInput1Literal, mixed $input2, bool $isInput2Literal): void {
[$doctrineInput1, $ocInput1] = $this->helpWithLiteral($input1, $isInput1Literal);
[$doctrineInput2, $ocInput2] = $this->helpWithLiteral($input2, $isInput2Literal);
@ -94,15 +76,8 @@ class ExpressionBuilderTest extends TestCase {
];
}
/**
*
* @param mixed $input1
* @param bool $isInput1Literal
* @param mixed $input2
* @param bool $isInput2Literal
*/
#[\PHPUnit\Framework\Attributes\DataProvider('dataComparisons')]
public function testEquals($input1, $isInput1Literal, $input2, $isInput2Literal): void {
#[DataProvider(methodName: 'dataComparisons')]
public function testEquals(string $input1, bool $isInput1Literal, string $input2, bool $isInput2Literal): void {
[$doctrineInput1, $ocInput1] = $this->helpWithLiteral($input1, $isInput1Literal);
[$doctrineInput2, $ocInput2] = $this->helpWithLiteral($input2, $isInput2Literal);
@ -112,15 +87,8 @@ class ExpressionBuilderTest extends TestCase {
);
}
/**
*
* @param mixed $input1
* @param bool $isInput1Literal
* @param mixed $input2
* @param bool $isInput2Literal
*/
#[\PHPUnit\Framework\Attributes\DataProvider('dataComparisons')]
public function testNotEquals($input1, $isInput1Literal, $input2, $isInput2Literal): void {
#[DataProvider(methodName: 'dataComparisons')]
public function testNotEquals(string $input1, bool $isInput1Literal, string $input2, bool $isInput2Literal): void {
[$doctrineInput1, $ocInput1] = $this->helpWithLiteral($input1, $isInput1Literal);
[$doctrineInput2, $ocInput2] = $this->helpWithLiteral($input2, $isInput2Literal);
@ -130,15 +98,8 @@ class ExpressionBuilderTest extends TestCase {
);
}
/**
*
* @param mixed $input1
* @param bool $isInput1Literal
* @param mixed $input2
* @param bool $isInput2Literal
*/
#[\PHPUnit\Framework\Attributes\DataProvider('dataComparisons')]
public function testLowerThan($input1, $isInput1Literal, $input2, $isInput2Literal): void {
#[DataProvider(methodName: 'dataComparisons')]
public function testLowerThan(string $input1, bool $isInput1Literal, string $input2, bool $isInput2Literal): void {
[$doctrineInput1, $ocInput1] = $this->helpWithLiteral($input1, $isInput1Literal);
[$doctrineInput2, $ocInput2] = $this->helpWithLiteral($input2, $isInput2Literal);
@ -148,15 +109,8 @@ class ExpressionBuilderTest extends TestCase {
);
}
/**
*
* @param mixed $input1
* @param bool $isInput1Literal
* @param mixed $input2
* @param bool $isInput2Literal
*/
#[\PHPUnit\Framework\Attributes\DataProvider('dataComparisons')]
public function testLowerThanEquals($input1, $isInput1Literal, $input2, $isInput2Literal): void {
#[DataProvider(methodName: 'dataComparisons')]
public function testLowerThanEquals(string $input1, bool $isInput1Literal, string $input2, bool $isInput2Literal): void {
[$doctrineInput1, $ocInput1] = $this->helpWithLiteral($input1, $isInput1Literal);
[$doctrineInput2, $ocInput2] = $this->helpWithLiteral($input2, $isInput2Literal);
@ -166,15 +120,8 @@ class ExpressionBuilderTest extends TestCase {
);
}
/**
*
* @param mixed $input1
* @param bool $isInput1Literal
* @param mixed $input2
* @param bool $isInput2Literal
*/
#[\PHPUnit\Framework\Attributes\DataProvider('dataComparisons')]
public function testGreaterThan($input1, $isInput1Literal, $input2, $isInput2Literal): void {
#[DataProvider(methodName: 'dataComparisons')]
public function testGreaterThan(string $input1, bool $isInput1Literal, string $input2, bool $isInput2Literal): void {
[$doctrineInput1, $ocInput1] = $this->helpWithLiteral($input1, $isInput1Literal);
[$doctrineInput2, $ocInput2] = $this->helpWithLiteral($input2, $isInput2Literal);
@ -184,15 +131,8 @@ class ExpressionBuilderTest extends TestCase {
);
}
/**
*
* @param mixed $input1
* @param bool $isInput1Literal
* @param mixed $input2
* @param bool $isInput2Literal
*/
#[\PHPUnit\Framework\Attributes\DataProvider('dataComparisons')]
public function testGreaterThanEquals($input1, $isInput1Literal, $input2, $isInput2Literal): void {
#[DataProvider(methodName: 'dataComparisons')]
public function testGreaterThanEquals(string $input1, bool $isInput1Literal, string $input2, bool $isInput2Literal): void {
[$doctrineInput1, $ocInput1] = $this->helpWithLiteral($input1, $isInput1Literal);
[$doctrineInput2, $ocInput2] = $this->helpWithLiteral($input2, $isInput2Literal);
@ -223,13 +163,8 @@ class ExpressionBuilderTest extends TestCase {
];
}
/**
*
* @param mixed $input
* @param bool $isLiteral
*/
#[\PHPUnit\Framework\Attributes\DataProvider('dataLike')]
public function testLike($input, $isLiteral): void {
#[DataProvider(methodName: 'dataLike')]
public function testLike(string $input, bool $isLiteral): void {
[$doctrineInput, $ocInput] = $this->helpWithLiteral($input, $isLiteral);
$this->assertEquals(
@ -238,13 +173,8 @@ class ExpressionBuilderTest extends TestCase {
);
}
/**
*
* @param mixed $input
* @param bool $isLiteral
*/
#[\PHPUnit\Framework\Attributes\DataProvider('dataLike')]
public function testNotLike($input, $isLiteral): void {
#[DataProvider(methodName: 'dataLike')]
public function testNotLike(string $input, bool $isLiteral): void {
[$doctrineInput, $ocInput] = $this->helpWithLiteral($input, $isLiteral);
$this->assertEquals(
@ -262,13 +192,8 @@ class ExpressionBuilderTest extends TestCase {
];
}
/**
*
* @param mixed $input
* @param bool $isLiteral
*/
#[\PHPUnit\Framework\Attributes\DataProvider('dataIn')]
public function testIn($input, $isLiteral): void {
#[DataProvider(methodName: 'dataIn')]
public function testIn(string|array $input, bool $isLiteral): void {
[$doctrineInput, $ocInput] = $this->helpWithLiteral($input, $isLiteral);
$this->assertEquals(
@ -277,13 +202,8 @@ class ExpressionBuilderTest extends TestCase {
);
}
/**
*
* @param mixed $input
* @param bool $isLiteral
*/
#[\PHPUnit\Framework\Attributes\DataProvider('dataIn')]
public function testNotIn($input, $isLiteral): void {
#[DataProvider(methodName: 'dataIn')]
public function testNotIn(string|array $input, bool $isLiteral): void {
[$doctrineInput, $ocInput] = $this->helpWithLiteral($input, $isLiteral);
$this->assertEquals(
@ -292,7 +212,7 @@ class ExpressionBuilderTest extends TestCase {
);
}
protected function helpWithLiteral($input, $isLiteral) {
protected function helpWithLiteral(mixed $input, bool $isLiteral): array {
if ($isLiteral) {
if (is_array($input)) {
$doctrineInput = array_map(function ($ident) {
@ -331,17 +251,14 @@ class ExpressionBuilderTest extends TestCase {
];
}
/**
*
* @param mixed $input
* @param string|null $type
*/
#[\PHPUnit\Framework\Attributes\DataProvider('dataLiteral')]
public function testLiteral($input, $type): void {
/** @var \OC\DB\QueryBuilder\Literal $actual */
$actual = $this->expressionBuilder->literal($input, $type);
#[DataProvider(methodName: 'dataLiteral')]
public function testLiteral(string|int $input, string|int|null $type): void {
if ($type === null) {
$actual = $this->expressionBuilder->literal($input);
} else {
$actual = $this->expressionBuilder->literal($input, $type);
}
$this->assertInstanceOf('\OC\DB\QueryBuilder\Literal', $actual);
$this->assertEquals(
$this->doctrineExpressionBuilder->literal($input, $type),
$actual->__toString()
@ -376,14 +293,10 @@ class ExpressionBuilderTest extends TestCase {
}
/**
* @param string $function
* @param mixed $value
* @param mixed $type
* @param bool $compareKeyToValue
* @param int $expected
* @param IQueryBuilder::PARAM_* $type
*/
#[\PHPUnit\Framework\Attributes\DataProvider('dataClobComparisons')]
public function testClobComparisons($function, $value, $type, $compareKeyToValue, $expected): void {
#[DataProvider(methodName: 'dataClobComparisons')]
public function testClobComparisons(string $function, string|array $value, mixed $type, bool $compareKeyToValue, int $expected): void {
$appId = $this->getUniqueID('testing');
$this->createConfig($appId, 1, 4);
$this->createConfig($appId, 2, 5);
@ -418,7 +331,7 @@ class ExpressionBuilderTest extends TestCase {
->executeStatement();
}
protected function createConfig($appId, $key, $value) {
protected function createConfig(string $appId, int $key, string|int $value): void {
$query = $this->connection->getQueryBuilder();
$query->insert('appconfig')
->values([

View file

@ -11,18 +11,13 @@ use OC\DB\QueryBuilder\Literal;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\IDBConnection;
use OCP\Server;
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\Attributes\Group;
use Test\TestCase;
/**
* Class FunctionBuilderTest
*
*
* @package Test\DB\QueryBuilder
*/
#[\PHPUnit\Framework\Attributes\Group('DB')]
#[Group(name: 'DB')]
class FunctionBuilderTest extends TestCase {
/** @var \Doctrine\DBAL\Connection|IDBConnection */
protected $connection;
protected IDBConnection $connection;
protected function setUp(): void {
parent::setUp();
@ -30,8 +25,8 @@ class FunctionBuilderTest extends TestCase {
$this->connection = Server::get(IDBConnection::class);
}
#[\PHPUnit\Framework\Attributes\DataProvider('providerTestConcatString')]
public function testConcatString($closure): void {
#[DataProvider(methodName: 'providerTestConcatString')]
public function testConcatString(callable $closure): void {
$query = $this->connection->getQueryBuilder();
[$real, $arguments, $return] = $closure($query);
if ($real) {
@ -53,35 +48,35 @@ class FunctionBuilderTest extends TestCase {
public static function providerTestConcatString(): array {
return [
'1 column: string param unicode'
=> [function ($q) {
=> [function (IQueryBuilder $q) {
return [false, [$q->createNamedParameter('👍')], '👍'];
}],
'2 columns: string param and string param'
=> [function ($q) {
=> [function (IQueryBuilder $q) {
return [false, [$q->createNamedParameter('foo'), $q->createNamedParameter('bar')], 'foobar'];
}],
'2 columns: string param and int literal'
=> [function ($q) {
=> [function (IQueryBuilder $q) {
return [false, [$q->createNamedParameter('foo'), $q->expr()->literal(1)], 'foo1'];
}],
'2 columns: string param and string literal'
=> [function ($q) {
=> [function (IQueryBuilder $q) {
return [false, [$q->createNamedParameter('foo'), $q->expr()->literal('bar')], 'foobar'];
}],
'2 columns: string real and int literal'
=> [function ($q) {
=> [function (IQueryBuilder $q) {
return [true, ['configkey', $q->expr()->literal(2)], '12'];
}],
'4 columns: string literal'
=> [function ($q) {
=> [function (IQueryBuilder $q) {
return [false, [$q->expr()->literal('foo'), $q->expr()->literal('bar'), $q->expr()->literal('foo'), $q->expr()->literal('bar')], 'foobarfoobar'];
}],
'4 columns: int literal'
=> [function ($q) {
=> [function (IQueryBuilder $q) {
return [false, [$q->expr()->literal(1), $q->expr()->literal(2), $q->expr()->literal(3), $q->expr()->literal(4)], '1234'];
}],
'5 columns: string param with special chars used in the function'
=> [function ($q) {
=> [function (IQueryBuilder $q) {
return [false, [$q->createNamedParameter('b'), $q->createNamedParameter("'"), $q->createNamedParameter('||'), $q->createNamedParameter(','), $q->createNamedParameter('a')], "b'||,a"];
}],
];
@ -333,7 +328,7 @@ class FunctionBuilderTest extends TestCase {
];
}
#[\PHPUnit\Framework\Attributes\DataProvider('octetLengthProvider')]
#[DataProvider(methodName: 'octetLengthProvider')]
public function testOctetLength(string $str, int $bytes): void {
$query = $this->connection->getQueryBuilder();
@ -356,7 +351,7 @@ class FunctionBuilderTest extends TestCase {
];
}
#[\PHPUnit\Framework\Attributes\DataProvider('charLengthProvider')]
#[DataProvider(methodName: 'charLengthProvider')]
public function testCharLength(string $str, int $bytes): void {
$query = $this->connection->getQueryBuilder();
@ -371,7 +366,10 @@ class FunctionBuilderTest extends TestCase {
$this->assertEquals($bytes, $column);
}
private function setUpMinMax($value) {
/**
* @psalm-param 10|11|20 $value
*/
private function setUpMinMax(int $value) {
$query = $this->connection->getQueryBuilder();
$query->insert('appconfig')

View file

@ -14,6 +14,7 @@ use OC\DB\QueryBuilder\QueryBuilder;
use OC\SystemConfig;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\IDBConnection;
use PHPUnit\Framework\Attributes\DataProvider;
use Psr\Log\LoggerInterface;
use Test\TestCase;
@ -41,7 +42,7 @@ class JoinConditionTest extends TestCase {
);
}
#[\PHPUnit\Framework\Attributes\DataProvider('platformProvider')]
#[DataProvider(methodName: 'platformProvider')]
public function testParseCondition(string $platform): void {
$query = $this->getBuilder($platform);
$param1 = $query->createNamedParameter('files');
@ -61,7 +62,7 @@ class JoinConditionTest extends TestCase {
], $parsed->toConditions);
}
#[\PHPUnit\Framework\Attributes\DataProvider('platformProvider')]
#[DataProvider(methodName: 'platformProvider')]
public function testParseCastCondition(string $platform): void {
$query = $this->getBuilder($platform);

View file

@ -16,9 +16,10 @@ use OC\DB\QueryBuilder\Sharded\ShardConnectionManager;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\IDBConnection;
use OCP\Server;
use PHPUnit\Framework\Attributes\Group;
use Test\TestCase;
#[\PHPUnit\Framework\Attributes\Group('DB')]
#[Group(name: 'DB')]
class PartitionedQueryBuilderTest extends TestCase {
private IDBConnection $connection;
private ShardConnectionManager $shardConnectionManager;
@ -27,7 +28,6 @@ class PartitionedQueryBuilderTest extends TestCase {
protected function setUp(): void {
if (PHP_INT_SIZE < 8) {
$this->markTestSkipped('Test requires 64bit');
return;
}
$this->connection = Server::get(IDBConnection::class);
$this->shardConnectionManager = Server::get(ShardConnectionManager::class);

View file

@ -10,27 +10,27 @@ declare(strict_types=1);
namespace Test\DB\QueryBuilder;
use BadMethodCallException;
use Doctrine\DBAL\Query\Expression\CompositeExpression;
use Doctrine\DBAL\Query\QueryException;
use OC\DB\ConnectionAdapter;
use OC\DB\QueryBuilder\Literal;
use OC\DB\QueryBuilder\Parameter;
use OC\DB\QueryBuilder\QueryBuilder;
use OC\SystemConfig;
use OCP\DB\IResult;
use OCP\DB\QueryBuilder\ILiteral;
use OCP\DB\QueryBuilder\IParameter;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\IDBConnection;
use OCP\Server;
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\Attributes\Group;
use PHPUnit\Framework\MockObject\MockObject;
use Psr\Log\LoggerInterface;
use function str_starts_with;
/**
* Class QueryBuilderTest
*
*
* @package Test\DB\QueryBuilder
*/
#[\PHPUnit\Framework\Attributes\Group('DB')]
#[Group(name: 'DB')]
class QueryBuilderTest extends \Test\TestCase {
private SystemConfig&MockObject $config;
private LoggerInterface&MockObject $logger;
@ -47,7 +47,10 @@ class QueryBuilderTest extends \Test\TestCase {
$this->queryBuilder = new QueryBuilder($this->connection, $this->config, $this->logger);
}
protected function createTestingRows($appId = 'testFirstResult') {
/**
* @psalm-param 'testFirstResult'|'testFirstResult1'|'testFirstResult2' $appId
*/
protected function createTestingRows(string $appId = 'testFirstResult'): void {
$qB = $this->connection->getQueryBuilder();
for ($i = 1; $i < 10; $i++) {
$qB->insert('*PREFIX*appconfig')
@ -60,7 +63,7 @@ class QueryBuilderTest extends \Test\TestCase {
}
}
protected function getTestingRows(QueryBuilder $queryBuilder) {
protected function getTestingRows(QueryBuilder $queryBuilder): array {
$queryBuilder->select('configvalue')
->from('*PREFIX*appconfig')
->where($queryBuilder->expr()->eq(
@ -79,7 +82,10 @@ class QueryBuilderTest extends \Test\TestCase {
return $rows;
}
protected function deleteTestingRows($appId = 'testFirstResult') {
/**
* @psalm-param 'testFirstResult'|'testFirstResult1'|'testFirstResult2' $appId
*/
protected function deleteTestingRows(string $appId = 'testFirstResult'): void {
$qB = $this->connection->getQueryBuilder();
$qB->delete('*PREFIX*appconfig')
@ -96,13 +102,8 @@ class QueryBuilderTest extends \Test\TestCase {
];
}
/**
*
* @param int|null $firstResult
* @param array $expectedSet
*/
#[DataProvider('dataFirstResult')]
public function testFirstResult($firstResult, $expectedSet): void {
#[DataProvider(methodName: 'dataFirstResult')]
public function testFirstResult(?int $firstResult, array $expectedSet): void {
$this->deleteTestingRows();
$this->createTestingRows();
@ -133,13 +134,8 @@ class QueryBuilderTest extends \Test\TestCase {
];
}
/**
*
* @param int $maxResult
* @param array $expectedSet
*/
#[DataProvider('dataMaxResults')]
public function testMaxResults($maxResult, $expectedSet): void {
#[DataProvider(methodName: 'dataMaxResults')]
public function testMaxResults(?int $maxResult, array $expectedSet): void {
$this->deleteTestingRows();
$this->createTestingRows();
@ -182,7 +178,7 @@ class QueryBuilderTest extends \Test\TestCase {
];
}
#[DataProvider('dataSelect')]
#[DataProvider(methodName: 'dataSelect')]
public function testSelect(array $selectArguments, array $expected, string $expectedLiteral = ''): void {
$this->deleteTestingRows();
$this->createTestingRows();
@ -190,7 +186,7 @@ class QueryBuilderTest extends \Test\TestCase {
array_walk_recursive(
$selectArguments,
function (string &$arg): void {
if (\str_starts_with($arg, 'l::')) {
if (str_starts_with($arg, 'l::')) {
$arg = $this->queryBuilder->expr()->literal(substr($arg, 3));
}
},
@ -231,7 +227,7 @@ class QueryBuilderTest extends \Test\TestCase {
];
}
#[DataProvider('dataSelectAlias')]
#[DataProvider(methodName: 'dataSelectAlias')]
public function testSelectAlias(string $select, string $alias, array $expected): void {
if (str_starts_with($select, 'l::')) {
$select = $this->queryBuilder->expr()->literal(substr($select, 3));
@ -242,7 +238,7 @@ class QueryBuilderTest extends \Test\TestCase {
$this->queryBuilder->selectAlias($select, $alias);
$this->queryBuilder->from('*PREFIX*appconfig')
$this->queryBuilder->from('appconfig')
->where($this->queryBuilder->expr()->eq(
'appid',
$this->queryBuilder->expr()->literal('testFirstResult')
@ -350,7 +346,7 @@ class QueryBuilderTest extends \Test\TestCase {
];
}
#[DataProvider('dataAddSelect')]
#[DataProvider(methodName: 'dataAddSelect')]
public function testAddSelect(array $selectArguments, array $expected, string $expectedLiteral = ''): void {
$this->deleteTestingRows();
$this->createTestingRows();
@ -358,7 +354,7 @@ class QueryBuilderTest extends \Test\TestCase {
array_walk_recursive(
$selectArguments,
function (string &$arg): void {
if (\str_starts_with($arg, 'l::')) {
if (str_starts_with($arg, 'l::')) {
$arg = $this->queryBuilder->expr()->literal(substr($arg, 3));
}
},
@ -405,15 +401,8 @@ class QueryBuilderTest extends \Test\TestCase {
];
}
/**
*
* @param string $tableName
* @param string $tableAlias
* @param array $expectedQueryPart
* @param string $expectedQuery
*/
#[DataProvider('dataDelete')]
public function testDelete($tableName, $tableAlias, $expectedQueryPart, $expectedQuery): void {
#[DataProvider(methodName: 'dataDelete')]
public function testDelete(string $tableName, ?string $tableAlias, array $expectedQueryPart, string $expectedQuery): void {
$this->queryBuilder->delete($tableName, $tableAlias);
$this->assertSame(
@ -434,15 +423,8 @@ class QueryBuilderTest extends \Test\TestCase {
];
}
/**
*
* @param string $tableName
* @param string $tableAlias
* @param array $expectedQueryPart
* @param string $expectedQuery
*/
#[DataProvider('dataUpdate')]
public function testUpdate($tableName, $tableAlias, $expectedQueryPart, $expectedQuery): void {
#[DataProvider(methodName: 'dataUpdate')]
public function testUpdate(string $tableName, ?string $tableAlias, array $expectedQueryPart, string $expectedQuery): void {
$this->queryBuilder->update($tableName, $tableAlias);
$this->assertSame(
@ -462,14 +444,8 @@ class QueryBuilderTest extends \Test\TestCase {
];
}
/**
*
* @param string $tableName
* @param array $expectedQueryPart
* @param string $expectedQuery
*/
#[DataProvider('dataInsert')]
public function testInsert($tableName, $expectedQueryPart, $expectedQuery): void {
#[DataProvider(methodName: 'dataInsert')]
public function testInsert(string $tableName, array $expectedQueryPart, string $expectedQuery): void {
$this->queryBuilder->insert($tableName);
$this->assertSame(
@ -501,7 +477,7 @@ class QueryBuilderTest extends \Test\TestCase {
];
}
#[DataProvider('dataFrom')]
#[DataProvider(methodName: 'dataFrom')]
public function testFrom(string $table1Name, ?string $table1Alias, ?string $table2Name, ?string $table2Alias, array $expectedQueryPart, string $expectedQuery): void {
$config = $this->createMock(SystemConfig::class);
$logger = $this->createMock(LoggerInterface::class);
@ -547,17 +523,8 @@ class QueryBuilderTest extends \Test\TestCase {
];
}
/**
*
* @param string $fromAlias
* @param string $tableName
* @param string $tableAlias
* @param string $condition
* @param array $expectedQueryPart
* @param string $expectedQuery
*/
#[DataProvider('dataJoin')]
public function testJoin($fromAlias, $tableName, $tableAlias, $condition, $expectedQueryPart, $expectedQuery): void {
#[DataProvider(methodName: 'dataJoin')]
public function testJoin(string $fromAlias, string $tableName, ?string $tableAlias, ?string $condition, array $expectedQueryPart, string $expectedQuery): void {
$this->queryBuilder->from('data1', 'd1');
$this->queryBuilder->join(
$fromAlias,
@ -577,17 +544,8 @@ class QueryBuilderTest extends \Test\TestCase {
);
}
/**
*
* @param string $fromAlias
* @param string $tableName
* @param string $tableAlias
* @param string $condition
* @param array $expectedQueryPart
* @param string $expectedQuery
*/
#[DataProvider('dataJoin')]
public function testInnerJoin($fromAlias, $tableName, $tableAlias, $condition, $expectedQueryPart, $expectedQuery): void {
#[DataProvider(methodName: 'dataJoin')]
public function testInnerJoin(string $fromAlias, string $tableName, ?string $tableAlias, ?string $condition, array $expectedQueryPart, string $expectedQuery): void {
$this->queryBuilder->from('data1', 'd1');
$this->queryBuilder->innerJoin(
$fromAlias,
@ -627,17 +585,8 @@ class QueryBuilderTest extends \Test\TestCase {
];
}
/**
*
* @param string $fromAlias
* @param string $tableName
* @param string $tableAlias
* @param string $condition
* @param array $expectedQueryPart
* @param string $expectedQuery
*/
#[DataProvider('dataLeftJoin')]
public function testLeftJoin($fromAlias, $tableName, $tableAlias, $condition, $expectedQueryPart, $expectedQuery): void {
#[DataProvider(methodName: 'dataLeftJoin')]
public function testLeftJoin(string $fromAlias, string $tableName, ?string $tableAlias, ?string $condition, array $expectedQueryPart, string $expectedQuery): void {
$this->queryBuilder->from('data1', 'd1');
$this->queryBuilder->leftJoin(
$fromAlias,
@ -677,17 +626,8 @@ class QueryBuilderTest extends \Test\TestCase {
];
}
/**
*
* @param string $fromAlias
* @param string $tableName
* @param string $tableAlias
* @param string $condition
* @param array $expectedQueryPart
* @param string $expectedQuery
*/
#[DataProvider('dataRightJoin')]
public function testRightJoin($fromAlias, $tableName, $tableAlias, $condition, $expectedQueryPart, $expectedQuery): void {
#[DataProvider(methodName: 'dataRightJoin')]
public function testRightJoin(string $fromAlias, string $tableName, ?string $tableAlias, ?string $condition, array $expectedQueryPart, string $expectedQuery): void {
$this->queryBuilder->from('data1', 'd1');
$this->queryBuilder->rightJoin(
$fromAlias,
@ -716,17 +656,8 @@ class QueryBuilderTest extends \Test\TestCase {
];
}
/**
*
* @param string $partOne1
* @param string $partOne2
* @param string $partTwo1
* @param string $partTwo2
* @param array $expectedQueryPart
* @param string $expectedQuery
*/
#[DataProvider('dataSet')]
public function testSet($partOne1, $partOne2, $partTwo1, $partTwo2, $expectedQueryPart, $expectedQuery): void {
#[DataProvider(methodName: 'dataSet')]
public function testSet(string $partOne1, string|ILiteral|IParameter $partOne2, ?string $partTwo1, ?ILiteral $partTwo2, array $expectedQueryPart, string $expectedQuery): void {
$this->queryBuilder->update('data');
$this->queryBuilder->set($partOne1, $partOne2);
if ($partTwo1 !== null) {
@ -751,14 +682,8 @@ class QueryBuilderTest extends \Test\TestCase {
];
}
/**
*
* @param array $whereArguments
* @param array $expectedQueryPart
* @param string $expectedQuery
*/
#[DataProvider('dataWhere')]
public function testWhere($whereArguments, $expectedQueryPart, $expectedQuery): void {
#[DataProvider(methodName: 'dataWhere')]
public function testWhere(array $whereArguments, CompositeExpression $expectedQueryPart, string $expectedQuery): void {
$this->queryBuilder->select('column');
call_user_func_array(
[$this->queryBuilder, 'where'],
@ -776,14 +701,8 @@ class QueryBuilderTest extends \Test\TestCase {
);
}
/**
*
* @param array $whereArguments
* @param array $expectedQueryPart
* @param string $expectedQuery
*/
#[DataProvider('dataWhere')]
public function testAndWhere($whereArguments, $expectedQueryPart, $expectedQuery): void {
#[DataProvider(methodName: 'dataWhere')]
public function testAndWhere(array $whereArguments, CompositeExpression $expectedQueryPart, string $expectedQuery): void {
$this->queryBuilder->select('column');
call_user_func_array(
[$this->queryBuilder, 'andWhere'],
@ -808,14 +727,8 @@ class QueryBuilderTest extends \Test\TestCase {
];
}
/**
*
* @param array $whereArguments
* @param array $expectedQueryPart
* @param string $expectedQuery
*/
#[DataProvider('dataOrWhere')]
public function testOrWhere($whereArguments, $expectedQueryPart, $expectedQuery): void {
#[DataProvider(methodName: 'dataOrWhere')]
public function testOrWhere(array $whereArguments, CompositeExpression $expectedQueryPart, string $expectedQuery): void {
$this->queryBuilder->select('column');
call_user_func_array(
[$this->queryBuilder, 'orWhere'],
@ -840,14 +753,8 @@ class QueryBuilderTest extends \Test\TestCase {
];
}
/**
*
* @param array $groupByArguments
* @param array $expectedQueryPart
* @param string $expectedQuery
*/
#[DataProvider('dataGroupBy')]
public function testGroupBy($groupByArguments, $expectedQueryPart, $expectedQuery): void {
#[DataProvider(methodName: 'dataGroupBy')]
public function testGroupBy(array $groupByArguments, array $expectedQueryPart, string $expectedQuery): void {
$this->queryBuilder->select('column');
call_user_func_array(
[$this->queryBuilder, 'groupBy'],
@ -872,14 +779,8 @@ class QueryBuilderTest extends \Test\TestCase {
];
}
/**
*
* @param array $groupByArguments
* @param array $expectedQueryPart
* @param string $expectedQuery
*/
#[DataProvider('dataAddGroupBy')]
public function testAddGroupBy($groupByArguments, $expectedQueryPart, $expectedQuery): void {
#[DataProvider(methodName: 'dataAddGroupBy')]
public function testAddGroupBy(array $groupByArguments, array $expectedQueryPart, string $expectedQuery): void {
$this->queryBuilder->select('column');
$this->queryBuilder->groupBy('column1');
call_user_func_array(
@ -904,15 +805,8 @@ class QueryBuilderTest extends \Test\TestCase {
];
}
/**
*
* @param string $column
* @param string $value
* @param array $expectedQueryPart
* @param string $expectedQuery
*/
#[DataProvider('dataSetValue')]
public function testSetValue($column, $value, $expectedQueryPart, $expectedQuery): void {
#[DataProvider(methodName: 'dataSetValue')]
public function testSetValue(string $column, string $value, array $expectedQueryPart, string $expectedQuery): void {
$this->queryBuilder->insert('data');
$this->queryBuilder->setValue($column, $value);
@ -927,15 +821,8 @@ class QueryBuilderTest extends \Test\TestCase {
);
}
/**
*
* @param string $column
* @param string $value
* @param array $expectedQueryPart
* @param string $expectedQuery
*/
#[DataProvider('dataSetValue')]
public function testValues($column, $value, $expectedQueryPart, $expectedQuery): void {
#[DataProvider(methodName: 'dataSetValue')]
public function testValues(string $column, string $value, array $expectedQueryPart, string $expectedQuery): void {
$this->queryBuilder->insert('data');
$this->queryBuilder->values([
$column => $value,
@ -969,14 +856,8 @@ class QueryBuilderTest extends \Test\TestCase {
];
}
/**
*
* @param array $havingArguments
* @param array $expectedQueryPart
* @param string $expectedQuery
*/
#[DataProvider('dataHaving')]
public function testHaving($havingArguments, $expectedQueryPart, $expectedQuery): void {
#[DataProvider(methodName: 'dataHaving')]
public function testHaving(array $havingArguments, CompositeExpression $expectedQueryPart, string $expectedQuery): void {
call_user_func_array(
[$this->queryBuilder, 'having'],
$havingArguments
@ -1010,14 +891,8 @@ class QueryBuilderTest extends \Test\TestCase {
];
}
/**
*
* @param array $havingArguments
* @param array $expectedQueryPart
* @param string $expectedQuery
*/
#[DataProvider('dataAndHaving')]
public function testAndHaving($havingArguments, $expectedQueryPart, $expectedQuery): void {
#[DataProvider(methodName: 'dataAndHaving')]
public function testAndHaving(array $havingArguments, CompositeExpression $expectedQueryPart, string $expectedQuery): void {
$this->queryBuilder->having('condition1');
call_user_func_array(
[$this->queryBuilder, 'andHaving'],
@ -1052,14 +927,8 @@ class QueryBuilderTest extends \Test\TestCase {
];
}
/**
*
* @param array $havingArguments
* @param array $expectedQueryPart
* @param string $expectedQuery
*/
#[DataProvider('dataOrHaving')]
public function testOrHaving($havingArguments, $expectedQueryPart, $expectedQuery): void {
#[DataProvider(methodName: 'dataOrHaving')]
public function testOrHaving(array $havingArguments, CompositeExpression $expectedQueryPart, string $expectedQuery): void {
$this->queryBuilder->having('condition1');
call_user_func_array(
[$this->queryBuilder, 'orHaving'],
@ -1086,14 +955,10 @@ class QueryBuilderTest extends \Test\TestCase {
}
/**
*
* @param string $sort
* @param string $order
* @param array $expectedQueryPart
* @param string $expectedQuery
* @param string|'ASC'|'DESC'|null $order
*/
#[DataProvider('dataOrderBy')]
public function testOrderBy($sort, $order, $expectedQueryPart, $expectedQuery): void {
#[DataProvider(methodName: 'dataOrderBy')]
public function testOrderBy(string $sort, ?string $order, array $expectedQueryPart, string $expectedQuery): void {
$this->queryBuilder->orderBy($sort, $order);
$this->assertEquals(
@ -1122,15 +987,11 @@ class QueryBuilderTest extends \Test\TestCase {
}
/**
*
* @param string $sort2
* @param string $order2
* @param string $order1
* @param array $expectedQueryPart
* @param string $expectedQuery
* @param string|'ASC'|'DESC'|null $order2
* @param string|'ASC'|'DESC'|null $order1
*/
#[DataProvider('dataAddOrderBy')]
public function testAddOrderBy($sort2, $order2, $order1, $expectedQueryPart, $expectedQuery): void {
#[DataProvider(methodName: 'dataAddOrderBy')]
public function testAddOrderBy(string $sort2, ?string $order2, ?string $order1, array $expectedQueryPart, string $expectedQuery): void {
$this->queryBuilder->orderBy('column1', $order1);
$this->queryBuilder->addOrderBy($sort2, $order2);
@ -1151,7 +1012,7 @@ class QueryBuilderTest extends \Test\TestCase {
try {
$qB->getLastInsertId();
$this->fail('getLastInsertId() should throw an exception, when being called before insert()');
} catch (\BadMethodCallException $e) {
} catch (BadMethodCallException) {
$this->addToAssertionCount(1);
}
@ -1166,8 +1027,6 @@ class QueryBuilderTest extends \Test\TestCase {
$actual = $qB->getLastInsertId();
$this->assertNotNull($actual);
$this->assertIsInt($actual);
$this->assertEquals($this->connection->lastInsertId('*PREFIX*properties'), $actual);
$qB->delete('properties')
@ -1177,7 +1036,7 @@ class QueryBuilderTest extends \Test\TestCase {
try {
$qB->getLastInsertId();
$this->fail('getLastInsertId() should throw an exception, when being called after delete()');
} catch (\BadMethodCallException $e) {
} catch (BadMethodCallException) {
$this->addToAssertionCount(1);
}
}
@ -1198,7 +1057,7 @@ class QueryBuilderTest extends \Test\TestCase {
];
}
#[DataProvider('dataGetTableName')]
#[DataProvider(methodName: 'dataGetTableName')]
public function testGetTableName(string $tableName, ?bool $automatic, string $expected): void {
if ($tableName === 'function') {
$tableName = $this->queryBuilder->createFunction('(' . $this->queryBuilder->select('*')->from('table')->getSQL() . ')');
@ -1221,21 +1080,16 @@ class QueryBuilderTest extends \Test\TestCase {
];
}
/**
* @param string $column
* @param string $prefix
* @param string $expected
*/
#[DataProvider('dataGetColumnName')]
public function testGetColumnName($column, $prefix, $expected): void {
#[DataProvider(methodName: 'dataGetColumnName')]
public function testGetColumnName(string $column, string $prefix, string $expected): void {
$this->assertSame(
$expected,
$this->queryBuilder->getColumnName($column, $prefix)
);
}
private function getConnection(): IDBConnection {
$connection = $this->createMock(IDBConnection::class);
private function getConnection(): ConnectionAdapter&MockObject {
$connection = $this->createMock(ConnectionAdapter::class);
$connection->method('executeStatement')
->willReturn(3);
$connection->method('executeQuery')
@ -1245,6 +1099,9 @@ class QueryBuilderTest extends \Test\TestCase {
public function testExecuteWithoutLogger(): void {
$queryBuilder = $this->createMock(\Doctrine\DBAL\Query\QueryBuilder::class);
$queryBuilder
->method('getType')
->willReturn(\Doctrine\DBAL\Query\QueryBuilder::INSERT);
$queryBuilder
->method('getSQL')
->willReturn('');
@ -1270,6 +1127,9 @@ class QueryBuilderTest extends \Test\TestCase {
public function testExecuteWithLoggerAndNamedArray(): void {
$queryBuilder = $this->createMock(\Doctrine\DBAL\Query\QueryBuilder::class);
$queryBuilder
->method('getType')
->willReturn(\Doctrine\DBAL\Query\QueryBuilder::INSERT);
$queryBuilder
->expects($this->any())
->method('getParameters')
@ -1311,6 +1171,9 @@ class QueryBuilderTest extends \Test\TestCase {
public function testExecuteWithLoggerAndUnnamedArray(): void {
$queryBuilder = $this->createMock(\Doctrine\DBAL\Query\QueryBuilder::class);
$queryBuilder
->method('getType')
->willReturn(\Doctrine\DBAL\Query\QueryBuilder::INSERT);
$queryBuilder
->expects($this->any())
->method('getParameters')
@ -1346,6 +1209,9 @@ class QueryBuilderTest extends \Test\TestCase {
public function testExecuteWithLoggerAndNoParams(): void {
$queryBuilder = $this->createMock(\Doctrine\DBAL\Query\QueryBuilder::class);
$queryBuilder
->method('getType')
->willReturn(\Doctrine\DBAL\Query\QueryBuilder::INSERT);
$queryBuilder
->expects($this->any())
->method('getParameters')

View file

@ -13,10 +13,10 @@ use OC\DB\QueryBuilder\Parameter;
use OC\DB\QueryBuilder\QuoteHelper;
use OCP\DB\QueryBuilder\ILiteral;
use OCP\DB\QueryBuilder\IParameter;
use PHPUnit\Framework\Attributes\DataProvider;
class QuoteHelperTest extends \Test\TestCase {
/** @var QuoteHelper */
protected $helper;
protected QuoteHelper $helper;
protected function setUp(): void {
parent::setUp();
@ -37,12 +37,8 @@ class QuoteHelperTest extends \Test\TestCase {
];
}
/**
* @param mixed $input
* @param string $expected
*/
#[\PHPUnit\Framework\Attributes\DataProvider('dataQuoteColumnName')]
public function testQuoteColumnName($input, $expected): void {
#[DataProvider(methodName: 'dataQuoteColumnName')]
public function testQuoteColumnName(string|Literal|Parameter $input, string $expected): void {
$this->assertSame(
$expected,
$this->helper->quoteColumnName($input)
@ -72,23 +68,15 @@ class QuoteHelperTest extends \Test\TestCase {
];
}
/**
* @param mixed $input
* @param string $expected
*/
#[\PHPUnit\Framework\Attributes\DataProvider('dataQuoteColumnNames')]
public function testQuoteColumnNames($input, $expected): void {
#[DataProvider(methodName: 'dataQuoteColumnNames')]
public function testQuoteColumnNames(string|Literal|Parameter|array $input, string|array $expected): void {
$this->assertSame(
$expected,
$this->helper->quoteColumnNames($input)
);
}
/**
* @param array|string|ILiteral|IParameter $strings string, Literal or Parameter
* @return array|string
*/
public function quoteColumnNames($strings) {
public function quoteColumnNames(array|string|ILiteral|IParameter $strings): array|string {
if (!is_array($strings)) {
return $this->quoteColumnName($strings);
}
@ -101,25 +89,9 @@ class QuoteHelperTest extends \Test\TestCase {
return $return;
}
/**
* @param string|ILiteral|IParameter $string string, Literal or Parameter
* @return string
*/
public function quoteColumnName($string) {
if ($string instanceof IParameter) {
return $string->getName();
}
if ($string instanceof ILiteral) {
return $string->getLiteral();
}
if ($string === null) {
return $string;
}
if (!is_string($string)) {
throw new \InvalidArgumentException('Only strings, Literals and Parameters are allowed');
public function quoteColumnName(string|ILiteral|IParameter $string): string {
if ($string instanceof ILiteral || $string instanceof IParameter) {
return (string)$string;
}
if (substr_count($string, '.')) {

View file

@ -17,9 +17,10 @@ use OC\DB\QueryBuilder\Sharded\ShardedQueryBuilder;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\IDBConnection;
use OCP\Server;
use PHPUnit\Framework\Attributes\Group;
use Test\TestCase;
#[\PHPUnit\Framework\Attributes\Group('DB')]
#[Group(name: 'DB')]
class SharedQueryBuilderTest extends TestCase {
private IDBConnection $connection;
private AutoIncrementHandler $autoIncrementHandler;