Compare commits

...

2 Commits

Author SHA1 Message Date
Carl Schwan 5c85223379 fix(QueryBuilder): Make types even more strict
Signed-off-by: Carl Schwan <carlschwan@kde.org>
2026-02-25 11:58:37 +01:00
Carl Schwan 29b81b5449 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>
2026-02-25 11:55:55 +01:00
43 changed files with 1161 additions and 2054 deletions
+1 -1
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();
@@ -164,7 +164,6 @@ class SharingMapper {
->andWhere($query->expr()->eq(
'type',
$query->createNamedParameter($resourceType, IQueryBuilder::PARAM_STR)),
IQueryBuilder::PARAM_STR,
)
->executeQuery();
@@ -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';
@@ -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())
-48
View File
@@ -3371,45 +3371,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>
</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/DB/QueryBuilder/TypedQueryBuilder.php">
<InternalMethod>
<code><![CDATA[select]]></code>
<code><![CDATA[selectDistinct]]></code>
</InternalMethod>
</file>
<file src="lib/private/DateTimeFormatter.php">
<FalsableReturnStatement>
<code><![CDATA[$l->l($type, $timestamp, [
@@ -4009,13 +3970,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>
@@ -16,6 +16,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 = [],
@@ -5,6 +5,9 @@
* 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;
@@ -20,6 +23,7 @@ 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 ExpressionBuilder implements IExpressionBuilder {
@@ -37,385 +41,178 @@ class ExpressionBuilder implements IExpressionBuilder {
$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 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 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);
}
}
@@ -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),
};
}
}
@@ -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));
}
}
@@ -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);
@@ -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:
@@ -10,284 +10,349 @@ namespace OC\DB\QueryBuilder;
use OCP\DB\IResult;
use OCP\DB\QueryBuilder\ConflictResolutionMode;
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 extends TypedQueryBuilder {
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): self {
#[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) {
#[Override]
public function addSelect(...$select): self {
$this->builder->addSelect(...$select);
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);
}
@@ -6,27 +6,31 @@
*/
namespace OC\DB\QueryBuilder\FunctionBuilder;
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 {
public function __construct(
protected IDBConnection $connection,
protected IQueryBuilder $queryBuilder,
protected QuoteHelper $helper,
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) {
@@ -35,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 {
@@ -48,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) . ')');
}
@@ -6,17 +6,15 @@
*/
namespace OC\DB\QueryBuilder\FunctionBuilder;
use OC\DB\ConnectionAdapter;
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 {
/** @var ConnectionAdapter $co */
$co = $this->connection;
if (version_compare($co->getServerVersion(), '20', '>=')) {
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'))");
}
return new QueryFunction('LOWER(DBMS_OBFUSCATION_TOOLKIT.md5 (input => UTL_RAW.cast_to_raw(' . $this->helper->quoteColumnName($input) . ')))');
@@ -30,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);
}
@@ -49,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);
}
@@ -62,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) {
@@ -71,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);
@@ -81,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);
@@ -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);
@@ -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) . ')');
}
+1 -4
View File
@@ -12,11 +12,8 @@ namespace OC\DB\QueryBuilder;
use OCP\DB\QueryBuilder\ILiteral;
class Literal implements ILiteral {
/**
* @param mixed $literal
*/
public function __construct(
protected $literal,
protected readonly mixed $literal,
) {
}
@@ -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");
@@ -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|ILiteral, '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);
public function addSelect(...$select): self {
$select = array_map(static fn ($select) => ['select' => $select, 'alias' => null], $select);
$this->selects = array_merge($this->selects, $select);
return $this;
}
public function selectAlias($select, $alias): self {
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
+4 -16
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, '.')) {
@@ -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();
}
}
}
@@ -27,7 +27,9 @@ abstract class TypedQueryBuilder implements ITypedQueryBuilder {
$this->validateColumn($column);
}
return $this->select(...$columns);
/** @psalm-suppress InternalMethod */
$this->select(...$columns);
return $this;
}
public function selectColumnsDistinct(string ...$columns): static {
@@ -35,6 +37,8 @@ abstract class TypedQueryBuilder implements ITypedQueryBuilder {
$this->validateColumn($column);
}
return $this->selectDistinct($columns);
/** @psalm-suppress InternalMethod */
$this->selectDistinct($columns);
return $this;
}
}
@@ -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 .= '.';
+1 -1
View File
@@ -968,7 +968,7 @@ class DefaultShareProvider implements
->setFirstResult(0);
if ($limit !== -1) {
$qb->setMaxResults($limit - count($shares));
$qb->setMaxResults(max($limit - count($shares), 1));
}
// Filter by node if provided
+1 -1
View File
@@ -285,7 +285,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 = [];
+3 -8
View File
@@ -36,6 +36,7 @@ use OCP\User\Events\BeforeUserCreatedEvent;
use OCP\User\Events\UserCreatedEvent;
use OCP\UserInterface;
use OCP\Util;
use Override;
use Psr\Log\LoggerInterface;
/**
@@ -728,16 +729,10 @@ class Manager extends PublicEmitter implements IUserManager {
}
}
/**
* Gets the list of user ids sorted by lastLogin, from most recent to least recent
*
* @param int|null $limit how many users to fetch (default: 25, max: 100)
* @param int $offset from which offset to fetch
* @param string $search search users based on search params
* @return list<string> list of user IDs
*/
#[Override]
public function getLastLoggedInUsers(?int $limit = null, int $offset = 0, string $search = ''): array {
// We can't load all users who already logged in
/** @var int<1, 100> */
$limit = min(100, $limit ?: 25);
$connection = Server::get(IDBConnection::class);
@@ -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;
}
+109 -95
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;
}
+34 -28
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.
+111 -125
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.
@@ -265,13 +264,13 @@ interface IQueryBuilder {
* ));
* </code>
*
* @param array $params The query parameters to set.
* @param array $types The query parameters types to set.
*
* @param array<string|int, mixed> $params The query parameters to set.
* @param array<string|int, self::PARAM_*> $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,73 +278,73 @@ 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 array<string|int, 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").
*
* @param int $firstResult The first result to return.
* @param non-negative-int $firstResult The first result to return.
*
* @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").
* Returns 0 if {@link setFirstResult} was not applied to this QueryBuilder.
*
* @return int The position of the first result.
* @return non-negative-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.
*
* @param positive-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").
* Returns NULL if {@link setMaxResults} was not applied to this query builder.
*
* @return int|null The maximum number of results.
* @return positive-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.
@@ -358,14 +357,14 @@ interface IQueryBuilder {
* ->leftJoin('u', 'phonenumbers', 'p', 'u.id = p.user_id');
* </code>
*
* @param mixed ...$selects The selection expressions.
*
* @param string|list<string>|IQueryFunction|ILiteral ...$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(mixed...$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): self;
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.
@@ -417,14 +416,14 @@ interface IQueryBuilder {
* ->leftJoin('u', 'phonenumbers', 'u.id = p.user_id');
* </code>
*
* @param mixed ...$select The selection expression.
*
* @param string|IParameter|IQueryFunction|ILiteral|list<string> ...$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(mixed ...$select): 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.
@@ -660,14 +659,14 @@ interface IQueryBuilder {
* ->where($or);
* </code>
*
* @param mixed $predicates The restriction predicates.
*
* @param string|IParameter|IQueryFunction|ILiteral|ICompositeExpression $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(mixed ...$predicates): self;
/**
* Adds one or more restrictions to the query results, forming a logical
@@ -681,8 +680,7 @@ interface IQueryBuilder {
* ->andWhere('u.is_active = 1');
* </code>
*
* @param mixed ...$where The query restrictions.
*
* @param string|IParameter|IQueryFunction|ILiteral|ICompositeExpression ...$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(mixed ...$where): self;
/**
* Adds one or more restrictions to the query results, forming a logical
@@ -704,8 +702,7 @@ interface IQueryBuilder {
* ->orWhere('u.id = 2');
* </code>
*
* @param mixed ...$where The WHERE statement.
*
* @param string|IParameter|IQueryFunction|ILiteral|ICompositeExpression ...$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(mixed ...$where): self;
/**
* Specifies a grouping over the results of the query.
@@ -726,14 +723,14 @@ interface IQueryBuilder {
* ->groupBy('u.id');
* </code>
*
* @param mixed ...$groupBys The grouping expression.
*
* @param string|IParameter|IQueryFunction|ILiteral|list<string> ...$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(mixed ...$groupBys): self;
/**
* Adds a grouping expression to the query.
@@ -746,14 +743,14 @@ interface IQueryBuilder {
* ->addGroupBy('u.createdAt')
* </code>
*
* @param mixed ...$groupBy The grouping expression.
*
* @param string|IParameter|IQueryFunction|ILiteral|list<string> ...$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(mixed ...$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.
*
* @param string|IParameter|IQueryFunction|ILiteral ...$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(mixed ...$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;
@@ -31,7 +31,7 @@ interface ITypedQueryBuilder extends IQueryBuilder {
* @internal This method does not work with {@see self}. Use {@see self::selectColumns()} or {@see self::selectAlias()} instead.
*/
#[Override]
public function select(...$selects);
public function select(...$selects): self;
/**
* @template NewS of string
@@ -46,7 +46,7 @@ interface ITypedQueryBuilder extends IQueryBuilder {
* @internal This method does not work with {@see self}. Use {@see self::selectColumnDistinct()} or {@see self::selectAlias()} instead.
*/
#[Override]
public function selectDistinct($select);
public function selectDistinct(string|array $select): self;
/**
* @template NewS of string
@@ -61,16 +61,16 @@ interface ITypedQueryBuilder extends IQueryBuilder {
* @internal This method does not work with {@see self}. Use {@see self::selectColumns()} or {@see self::selectAlias()} instead.
*/
#[Override]
public function addSelect(...$select);
public function addSelect(...$select): self;
/**
* @inheritDoc
* @param mixed $select
* @param string|IParameter|IQueryFunction|ILiteral $select
* @template NewS of string
* @param NewS $alias
* @psalm-this-out self<S|NewS>
* @psalm-suppress LessSpecificImplementedReturnType
*/
#[Override]
public function selectAlias($select, $alias): self;
public function selectAlias(string|IParameter|IQueryFunction|ILiteral $select, string $alias): self;
}
+10
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"/>
@@ -172,52 +173,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>
+27 -25
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);
}
+6 -1
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';
@@ -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([
@@ -13,7 +13,6 @@ namespace Test\DB\QueryBuilder;
use Doctrine\DBAL\Query\Expression\ExpressionBuilder as DoctrineExpressionBuilder;
use OC\DB\Connection;
use OC\DB\QueryBuilder\ExpressionBuilder\ExpressionBuilder;
use OC\DB\QueryBuilder\Literal;
use OCP\DB\QueryBuilder\IFunctionBuilder;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\IDBConnection;
@@ -24,12 +23,7 @@ use PHPUnit\Framework\MockObject\MockObject;
use Psr\Log\LoggerInterface;
use Test\TestCase;
/**
* Class ExpressionBuilderTest
*
* @package Test\DB\QueryBuilder
*/
#[Group('DB')]
#[Group(name: 'DB')]
class ExpressionBuilderTest extends TestCase {
protected ExpressionBuilder $expressionBuilder;
protected DoctrineExpressionBuilder $doctrineExpressionBuilder;
@@ -66,8 +60,8 @@ class ExpressionBuilderTest extends TestCase {
return $testSets;
}
#[DataProvider('dataComparison')]
public function testComparison(string $comparison, string $input1, bool $isInput1Literal, string $input2, bool $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);
@@ -86,7 +80,7 @@ class ExpressionBuilderTest extends TestCase {
];
}
#[DataProvider('dataComparisons')]
#[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);
@@ -97,7 +91,7 @@ class ExpressionBuilderTest extends TestCase {
);
}
#[DataProvider('dataComparisons')]
#[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);
@@ -108,7 +102,7 @@ class ExpressionBuilderTest extends TestCase {
);
}
#[DataProvider('dataComparisons')]
#[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);
@@ -119,7 +113,7 @@ class ExpressionBuilderTest extends TestCase {
);
}
#[DataProvider('dataComparisons')]
#[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);
@@ -130,7 +124,7 @@ class ExpressionBuilderTest extends TestCase {
);
}
#[DataProvider('dataComparisons')]
#[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);
@@ -141,7 +135,7 @@ class ExpressionBuilderTest extends TestCase {
);
}
#[DataProvider('dataComparisons')]
#[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);
@@ -173,7 +167,7 @@ class ExpressionBuilderTest extends TestCase {
];
}
#[DataProvider('dataLike')]
#[DataProvider(methodName: 'dataLike')]
public function testLike(string $input, bool $isLiteral): void {
[$doctrineInput, $ocInput] = $this->helpWithLiteral($input, $isLiteral);
@@ -183,7 +177,7 @@ class ExpressionBuilderTest extends TestCase {
);
}
#[DataProvider('dataLike')]
#[DataProvider(methodName: 'dataLike')]
public function testNotLike(string $input, bool $isLiteral): void {
[$doctrineInput, $ocInput] = $this->helpWithLiteral($input, $isLiteral);
@@ -202,7 +196,7 @@ class ExpressionBuilderTest extends TestCase {
];
}
#[DataProvider('dataIn')]
#[DataProvider(methodName: 'dataIn')]
public function testIn(string|array $input, bool $isLiteral): void {
[$doctrineInput, $ocInput] = $this->helpWithLiteral($input, $isLiteral);
@@ -212,7 +206,7 @@ class ExpressionBuilderTest extends TestCase {
);
}
#[DataProvider('dataIn')]
#[DataProvider(methodName: 'dataIn')]
public function testNotIn(string|array $input, bool $isLiteral): void {
[$doctrineInput, $ocInput] = $this->helpWithLiteral($input, $isLiteral);
@@ -222,7 +216,7 @@ class ExpressionBuilderTest extends TestCase {
);
}
protected function helpWithLiteral(string|array $input, bool $isLiteral) {
protected function helpWithLiteral(string|array $input, bool $isLiteral): array {
if ($isLiteral) {
if (is_array($input)) {
$doctrineInput = array_map(function ($ident) {
@@ -261,12 +255,14 @@ class ExpressionBuilderTest extends TestCase {
];
}
#[DataProvider('dataLiteral')]
#[DataProvider(methodName: 'dataLiteral')]
public function testLiteral(string|int $input, string|int|null $type): void {
/** @var Literal $actual */
$actual = $this->expressionBuilder->literal($input, $type);
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()
@@ -300,8 +296,11 @@ class ExpressionBuilderTest extends TestCase {
];
}
#[DataProvider('dataClobComparisons')]
public function testClobComparisons(string $function, string|array $value, int $type, bool $compareKeyToValue, int $expected): void {
/**
* @param IQueryBuilder::PARAM_* $type
*/
#[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);
@@ -335,8 +334,7 @@ class ExpressionBuilderTest extends TestCase {
->where($query->expr()->eq('appid', $query->createNamedParameter($appId)))
->executeStatement();
}
protected function createConfig(string $appId, int $key, int|string $value) {
protected function createConfig(string $appId, int $key, string|int $value): void {
$query = $this->connection->getQueryBuilder();
$query->insert('appconfig')
->values([
@@ -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')
@@ -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);
@@ -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);
+86 -216
View File
@@ -10,6 +10,7 @@ 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;
@@ -18,20 +19,18 @@ 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;
@@ -48,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')
@@ -61,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(
@@ -80,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')
@@ -97,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();
@@ -134,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();
@@ -183,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();
@@ -191,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));
}
},
@@ -232,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));
@@ -243,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')
@@ -351,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();
@@ -359,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));
}
},
@@ -406,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(
@@ -435,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(
@@ -463,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(
@@ -502,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);
@@ -548,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,
@@ -578,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,
@@ -628,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,
@@ -678,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,
@@ -717,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) {
@@ -752,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'],
@@ -777,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'],
@@ -809,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'],
@@ -841,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'],
@@ -873,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(
@@ -905,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);
@@ -928,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,
@@ -970,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
@@ -1011,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'],
@@ -1053,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'],
@@ -1087,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(
@@ -1123,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);
@@ -1152,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);
}
@@ -1167,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')
@@ -1178,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);
}
}
@@ -1199,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() . ')');
@@ -1222,7 +1080,7 @@ class QueryBuilderTest extends \Test\TestCase {
];
}
#[DataProvider('dataGetColumnName')]
#[DataProvider(methodName: 'dataGetColumnName')]
public function testGetColumnName(string $column, string $prefix, string $expected): void {
$this->assertSame(
$expected,
@@ -1230,7 +1088,7 @@ class QueryBuilderTest extends \Test\TestCase {
);
}
private function getConnection(): MockObject&ConnectionAdapter {
private function getConnection(): ConnectionAdapter&MockObject {
$connection = $this->createMock(ConnectionAdapter::class);
$connection->method('executeStatement')
->willReturn(3);
@@ -1241,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('');
@@ -1266,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')
@@ -1307,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')
@@ -1342,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')
+10 -38
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, '.')) {
@@ -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;