Compare commits
30 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3caa1467b1 | |||
| 2c2335b8b4 | |||
| d6eade0119 | |||
| 8d1cb50048 | |||
| b4b328cf61 | |||
| e47195a334 | |||
| 4d00f49757 | |||
| 83d795dd18 | |||
| a6b9483a5f | |||
| 636345bac8 | |||
| 79d4953e64 | |||
| ca8050b94e | |||
| 47b08a07d8 | |||
| 77c070bc93 | |||
| 09c9241b30 | |||
| 711bd2bc6d | |||
| 2a81cba978 | |||
| 6df490942c | |||
| 0b8e7bb4f0 | |||
| 7e264ba58e | |||
| 64f319ab4e | |||
| ec0ed788fa | |||
| 67d1fac6f6 | |||
| af98eed523 | |||
| bfac9e7023 | |||
| 237d5156b6 | |||
| 40c39270c0 | |||
| f5b18dd7fd | |||
| f885d7292f | |||
| e4244c5fc8 |
@@ -23,3 +23,7 @@
|
||||
- [ ] [Backports requested](https://github.com/nextcloud/backportbot/#usage) where applicable (ex: critical bugfixes)
|
||||
- [ ] [Labels added](https://github.com/nextcloud/server/labels) where applicable (ex: bug/enhancement, `3. to review`, feature component)
|
||||
- [ ] [Milestone added](https://github.com/nextcloud/server/milestones) for target branch/version (ex: 32.x for `stable32`)
|
||||
|
||||
## AI (if applicable)
|
||||
|
||||
- [ ] The content of this PR was partly or fully generated using AI
|
||||
|
||||
@@ -77,6 +77,7 @@ return array(
|
||||
'OCA\\DAV\\CalDAV\\Federation\\FederatedCalendarFactory' => $baseDir . '/../lib/CalDAV/Federation/FederatedCalendarFactory.php',
|
||||
'OCA\\DAV\\CalDAV\\Federation\\FederatedCalendarImpl' => $baseDir . '/../lib/CalDAV/Federation/FederatedCalendarImpl.php',
|
||||
'OCA\\DAV\\CalDAV\\Federation\\FederatedCalendarMapper' => $baseDir . '/../lib/CalDAV/Federation/FederatedCalendarMapper.php',
|
||||
'OCA\\DAV\\CalDAV\\Federation\\FederatedCalendarObject' => $baseDir . '/../lib/CalDAV/Federation/FederatedCalendarObject.php',
|
||||
'OCA\\DAV\\CalDAV\\Federation\\FederatedCalendarSyncService' => $baseDir . '/../lib/CalDAV/Federation/FederatedCalendarSyncService.php',
|
||||
'OCA\\DAV\\CalDAV\\Federation\\FederationSharingService' => $baseDir . '/../lib/CalDAV/Federation/FederationSharingService.php',
|
||||
'OCA\\DAV\\CalDAV\\Federation\\Protocol\\CalendarFederationProtocolV1' => $baseDir . '/../lib/CalDAV/Federation/Protocol/CalendarFederationProtocolV1.php',
|
||||
|
||||
@@ -92,6 +92,7 @@ class ComposerStaticInitDAV
|
||||
'OCA\\DAV\\CalDAV\\Federation\\FederatedCalendarFactory' => __DIR__ . '/..' . '/../lib/CalDAV/Federation/FederatedCalendarFactory.php',
|
||||
'OCA\\DAV\\CalDAV\\Federation\\FederatedCalendarImpl' => __DIR__ . '/..' . '/../lib/CalDAV/Federation/FederatedCalendarImpl.php',
|
||||
'OCA\\DAV\\CalDAV\\Federation\\FederatedCalendarMapper' => __DIR__ . '/..' . '/../lib/CalDAV/Federation/FederatedCalendarMapper.php',
|
||||
'OCA\\DAV\\CalDAV\\Federation\\FederatedCalendarObject' => __DIR__ . '/..' . '/../lib/CalDAV/Federation/FederatedCalendarObject.php',
|
||||
'OCA\\DAV\\CalDAV\\Federation\\FederatedCalendarSyncService' => __DIR__ . '/..' . '/../lib/CalDAV/Federation/FederatedCalendarSyncService.php',
|
||||
'OCA\\DAV\\CalDAV\\Federation\\FederationSharingService' => __DIR__ . '/..' . '/../lib/CalDAV/Federation/FederationSharingService.php',
|
||||
'OCA\\DAV\\CalDAV\\Federation\\Protocol\\CalendarFederationProtocolV1' => __DIR__ . '/..' . '/../lib/CalDAV/Federation/Protocol/CalendarFederationProtocolV1.php',
|
||||
|
||||
@@ -43,9 +43,9 @@ class CalendarProvider implements ICalendarProvider {
|
||||
});
|
||||
}
|
||||
|
||||
$additionalProperties = $this->getAdditionalPropertiesForCalendars($calendarInfos);
|
||||
$iCalendars = [];
|
||||
|
||||
$additionalProperties = $this->getAdditionalPropertiesForCalendars($calendarInfos);
|
||||
foreach ($calendarInfos as $calendarInfo) {
|
||||
$user = str_replace('principals/users/', '', $calendarInfo['principaluri']);
|
||||
$path = 'calendars/' . $user . '/' . $calendarInfo['uri'];
|
||||
@@ -60,9 +60,7 @@ class CalendarProvider implements ICalendarProvider {
|
||||
);
|
||||
}
|
||||
|
||||
$additionalFederatedProps = $this->getAdditionalPropertiesForCalendars(
|
||||
$federatedCalendarInfos,
|
||||
);
|
||||
$additionalFederatedProps = $this->getAdditionalPropertiesForCalendars($federatedCalendarInfos);
|
||||
foreach ($federatedCalendarInfos as $calendarInfo) {
|
||||
$user = str_replace('principals/users/', '', $calendarInfo['principaluri']);
|
||||
$path = 'calendars/' . $user . '/' . $calendarInfo['uri'];
|
||||
|
||||
@@ -104,9 +104,10 @@ class CalendarFederationProvider implements ICloudFederationProvider {
|
||||
);
|
||||
}
|
||||
|
||||
// TODO: implement read-write sharing
|
||||
// convert access to permissions
|
||||
$permissions = match ($access) {
|
||||
DavSharingBackend::ACCESS_READ => Constants::PERMISSION_READ,
|
||||
DavSharingBackend::ACCESS_READ_WRITE => Constants::PERMISSION_READ | Constants::PERMISSION_CREATE | Constants::PERMISSION_UPDATE | Constants::PERMISSION_DELETE,
|
||||
default => throw new ProviderCouldNotAddShareException(
|
||||
"Unsupported access value: $access",
|
||||
'',
|
||||
@@ -122,20 +123,27 @@ class CalendarFederationProvider implements ICloudFederationProvider {
|
||||
$sharedWithPrincipal = 'principals/users/' . $share->getShareWith();
|
||||
|
||||
// Delete existing incoming federated share first
|
||||
$this->federatedCalendarMapper->deleteByUri($sharedWithPrincipal, $calendarUri);
|
||||
$calendar = $this->federatedCalendarMapper->findByUri($sharedWithPrincipal, $calendarUri);
|
||||
|
||||
$calendar = new FederatedCalendarEntity();
|
||||
$calendar->setPrincipaluri($sharedWithPrincipal);
|
||||
$calendar->setUri($calendarUri);
|
||||
$calendar->setRemoteUrl($calendarUrl);
|
||||
$calendar->setDisplayName($displayName);
|
||||
$calendar->setColor($color);
|
||||
$calendar->setToken($share->getShareSecret());
|
||||
$calendar->setSharedBy($share->getSharedBy());
|
||||
$calendar->setSharedByDisplayName($share->getSharedByDisplayName());
|
||||
$calendar->setPermissions($permissions);
|
||||
$calendar->setComponents($components);
|
||||
$calendar = $this->federatedCalendarMapper->insert($calendar);
|
||||
if ($calendar === null) {
|
||||
$calendar = new FederatedCalendarEntity();
|
||||
$calendar->setPrincipaluri($sharedWithPrincipal);
|
||||
$calendar->setUri($calendarUri);
|
||||
$calendar->setRemoteUrl($calendarUrl);
|
||||
$calendar->setDisplayName($displayName);
|
||||
$calendar->setColor($color);
|
||||
$calendar->setToken($share->getShareSecret());
|
||||
$calendar->setSharedBy($share->getSharedBy());
|
||||
$calendar->setSharedByDisplayName($share->getSharedByDisplayName());
|
||||
$calendar->setPermissions($permissions);
|
||||
$calendar->setComponents($components);
|
||||
$calendar = $this->federatedCalendarMapper->insert($calendar);
|
||||
} else {
|
||||
$calendar->setToken($share->getShareSecret());
|
||||
$calendar->setPermissions($permissions);
|
||||
$calendar->setComponents($components);
|
||||
$this->federatedCalendarMapper->update($calendar);
|
||||
}
|
||||
|
||||
$this->jobList->add(FederatedCalendarSyncJob::class, [
|
||||
FederatedCalendarSyncJob::ARGUMENT_ID => $calendar->getId(),
|
||||
|
||||
@@ -10,29 +10,289 @@ declare(strict_types=1);
|
||||
namespace OCA\DAV\CalDAV\Federation;
|
||||
|
||||
use OCA\DAV\CalDAV\CalDavBackend;
|
||||
use OCA\DAV\CalDAV\Calendar;
|
||||
use OCP\IConfig;
|
||||
use OCP\IL10N;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Sabre\CalDAV\Backend;
|
||||
use OCP\Constants;
|
||||
use Sabre\CalDAV\ICalendar;
|
||||
use Sabre\CalDAV\Plugin;
|
||||
use Sabre\CalDAV\Xml\Property\SupportedCalendarComponentSet;
|
||||
use Sabre\DAV\Exception\MethodNotAllowed;
|
||||
use Sabre\DAV\Exception\NotFound;
|
||||
use Sabre\DAV\IMultiGet;
|
||||
use Sabre\DAV\INode;
|
||||
use Sabre\DAV\IProperties;
|
||||
use Sabre\DAV\PropPatch;
|
||||
|
||||
class FederatedCalendar implements ICalendar, IProperties, IMultiGet {
|
||||
|
||||
private const CALENDAR_TYPE = CalDavBackend::CALENDAR_TYPE_FEDERATED;
|
||||
private const DAV_PROPERTY_CALENDAR_LABEL = '{DAV:}displayname';
|
||||
private const DAV_PROPERTY_CALENDAR_COLOR = '{http://apple.com/ns/ical/}calendar-color';
|
||||
|
||||
private string $principalUri;
|
||||
private string $calendarUri;
|
||||
private ?array $calendarACL = null;
|
||||
private FederatedCalendarEntity $federationInfo;
|
||||
|
||||
class FederatedCalendar extends Calendar {
|
||||
public function __construct(
|
||||
Backend\BackendInterface $caldavBackend,
|
||||
$calendarInfo,
|
||||
IL10N $l10n,
|
||||
IConfig $config,
|
||||
LoggerInterface $logger,
|
||||
private readonly FederatedCalendarMapper $federatedCalendarMapper,
|
||||
private readonly FederatedCalendarSyncService $federatedCalendarService,
|
||||
private readonly CalDavBackend $caldavBackend,
|
||||
$calendarInfo,
|
||||
) {
|
||||
parent::__construct($caldavBackend, $calendarInfo, $l10n, $config, $logger);
|
||||
$this->principalUri = $calendarInfo['principaluri'];
|
||||
$this->calendarUri = $calendarInfo['uri'];
|
||||
$this->federationInfo = $federatedCalendarMapper->findByUri($this->principalUri, $this->calendarUri);
|
||||
}
|
||||
|
||||
public function delete() {
|
||||
$this->federatedCalendarMapper->deleteById($this->getResourceId());
|
||||
public function getResourceId(): int {
|
||||
return $this->federationInfo->getId();
|
||||
}
|
||||
|
||||
public function getName(): string {
|
||||
return $this->federationInfo->getUri();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name Name of the file
|
||||
*/
|
||||
public function setName($name): void {
|
||||
throw new MethodNotAllowed('Renaming federated calendars is not allowed');
|
||||
}
|
||||
|
||||
protected function getCalendarType(): int {
|
||||
return CalDavBackend::CALENDAR_TYPE_FEDERATED;
|
||||
return self::CALENDAR_TYPE;
|
||||
}
|
||||
|
||||
public function getPrincipalURI(): string {
|
||||
return $this->federationInfo->getPrincipaluri();
|
||||
}
|
||||
|
||||
public function getOwner(): ?string {
|
||||
return $this->federationInfo->getSharedByPrincipal();
|
||||
}
|
||||
|
||||
public function getGroup(): ?string {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<array-key, mixed>
|
||||
*/
|
||||
public function getACL(): array {
|
||||
if ($this->calendarACL !== null) {
|
||||
return $this->calendarACL;
|
||||
}
|
||||
|
||||
$permissions = $this->federationInfo->getPermissions();
|
||||
// default permission
|
||||
$acl = [
|
||||
// read object permission
|
||||
[
|
||||
'privilege' => '{DAV:}read',
|
||||
'principal' => $this->principalUri,
|
||||
'protected' => true,
|
||||
],
|
||||
// read acl permission
|
||||
[
|
||||
'privilege' => '{DAV:}read-acl',
|
||||
'principal' => $this->principalUri,
|
||||
'protected' => true,
|
||||
],
|
||||
// write properties permission (calendar name, color)
|
||||
[
|
||||
'privilege' => '{DAV:}write-properties',
|
||||
'principal' => $this->principalUri,
|
||||
'protected' => true,
|
||||
],
|
||||
];
|
||||
// create permission
|
||||
if ($permissions & Constants::PERMISSION_CREATE) {
|
||||
$acl[] = [
|
||||
'privilege' => '{DAV:}bind',
|
||||
'principal' => $this->principalUri,
|
||||
'protected' => true,
|
||||
];
|
||||
}
|
||||
// update permission
|
||||
if ($permissions & Constants::PERMISSION_UPDATE) {
|
||||
$acl[] = [
|
||||
'privilege' => '{DAV:}write-content',
|
||||
'principal' => $this->principalUri,
|
||||
'protected' => true,
|
||||
];
|
||||
}
|
||||
// delete permission
|
||||
if ($permissions & Constants::PERMISSION_DELETE) {
|
||||
$acl[] = [
|
||||
'privilege' => '{DAV:}unbind',
|
||||
'principal' => $this->principalUri,
|
||||
'protected' => true,
|
||||
];
|
||||
}
|
||||
|
||||
// cache the calculated ACL for later use
|
||||
$this->calendarACL = $acl;
|
||||
|
||||
return $acl;
|
||||
}
|
||||
|
||||
public function setACL(array $acl): void {
|
||||
throw new MethodNotAllowed('Changing ACLs on federated calendars is not allowed');
|
||||
}
|
||||
|
||||
public function getSupportedPrivilegeSet(): ?array {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, mixed> properties array, with property name as key
|
||||
*/
|
||||
public function getProperties($properties): array {
|
||||
return [
|
||||
self::DAV_PROPERTY_CALENDAR_LABEL => $this->federationInfo->getDisplayName(),
|
||||
self::DAV_PROPERTY_CALENDAR_COLOR => $this->federationInfo->getColor(),
|
||||
'{' . Plugin::NS_CALDAV . '}supported-calendar-component-set' => new SupportedCalendarComponentSet(explode(',', $this->federationInfo->getComponents())),
|
||||
];
|
||||
}
|
||||
|
||||
public function propPatch(PropPatch $propPatch): void {
|
||||
$mutations = $propPatch->getMutations();
|
||||
if (count($mutations) > 0) {
|
||||
// evaluate if name was changed
|
||||
if (isset($mutations[self::DAV_PROPERTY_CALENDAR_LABEL])) {
|
||||
$this->federationInfo->setDisplayName($mutations[self::DAV_PROPERTY_CALENDAR_LABEL]);
|
||||
$propPatch->setResultCode(self::DAV_PROPERTY_CALENDAR_LABEL, 200);
|
||||
}
|
||||
// evaluate if color was changed
|
||||
if (isset($mutations[self::DAV_PROPERTY_CALENDAR_COLOR])) {
|
||||
$this->federationInfo->setColor($mutations[self::DAV_PROPERTY_CALENDAR_COLOR]);
|
||||
$propPatch->setResultCode(self::DAV_PROPERTY_CALENDAR_COLOR, 200);
|
||||
}
|
||||
$this->federatedCalendarMapper->update($this->federationInfo);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function getChildACL(): array {
|
||||
return $this->getACL();
|
||||
}
|
||||
|
||||
public function getLastModified(): ?int {
|
||||
return $this->federationInfo->getLastSync();
|
||||
}
|
||||
|
||||
public function delete(): void {
|
||||
$this->federatedCalendarMapper->deleteById($this->getResourceId());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name Name of the file
|
||||
*/
|
||||
public function createDirectory($name): void {
|
||||
throw new MethodNotAllowed('Creating nested collection is not allowed');
|
||||
}
|
||||
|
||||
public function calendarQuery(array $filters): array {
|
||||
$uris = $this->caldavBackend->calendarQuery($this->federationInfo->getId(), $filters, $this->getCalendarType());
|
||||
return $uris;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name Name of the file
|
||||
*/
|
||||
public function getChild($name): INode {
|
||||
$obj = $this->caldavBackend->getCalendarObject($this->federationInfo->getId(), $name, $this->getCalendarType());
|
||||
|
||||
if ($obj === null) {
|
||||
throw new NotFound('Calendar object not found');
|
||||
}
|
||||
|
||||
return new FederatedCalendarObject($this, $obj);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<INode>
|
||||
*/
|
||||
public function getChildren(): array {
|
||||
$objs = $this->caldavBackend->getCalendarObjects($this->federationInfo->getId(), $this->getCalendarType());
|
||||
|
||||
$children = [];
|
||||
foreach ($objs as $obj) {
|
||||
$children[] = new FederatedCalendarObject($this, $obj);
|
||||
}
|
||||
|
||||
return $children;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string> $paths Names of the files
|
||||
*
|
||||
* @return array<INode>
|
||||
*/
|
||||
public function getMultipleChildren(array $paths): array {
|
||||
$objs = $this->caldavBackend->getMultipleCalendarObjects($this->federationInfo->getId(), $paths, $this->getCalendarType());
|
||||
|
||||
$children = [];
|
||||
foreach ($objs as $obj) {
|
||||
$children[] = new FederatedCalendarObject($this, $obj);
|
||||
}
|
||||
|
||||
return $children;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name Name of the file
|
||||
*/
|
||||
public function childExists($name): bool {
|
||||
$obj = $this->caldavBackend->getCalendarObject($this->federationInfo->getId(), $name, $this->getCalendarType());
|
||||
return $obj !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name Name of the file
|
||||
* @param resource|string $data Initial payload
|
||||
*/
|
||||
public function createFile($name, $data = null): string {
|
||||
if (is_resource($data)) {
|
||||
$data = stream_get_contents($data);
|
||||
}
|
||||
|
||||
// Create on remote server first
|
||||
$etag = $this->federatedCalendarService->createCalendarObject($this->federationInfo, $name, $data);
|
||||
|
||||
if (empty($etag)) {
|
||||
throw new \Exception('Failed to create calendar object on remote server');
|
||||
}
|
||||
|
||||
// Then store locally
|
||||
return $this->caldavBackend->createCalendarObject($this->federationInfo->getId(), $name, $data, $this->getCalendarType());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name Name of the file
|
||||
* @param resource|string $data Initial payload
|
||||
*/
|
||||
public function updateFile($name, $data): string {
|
||||
if (is_resource($data)) {
|
||||
$data = stream_get_contents($data);
|
||||
}
|
||||
|
||||
// Update remote calendar first
|
||||
$etag = $this->federatedCalendarService->updateCalendarObject($this->federationInfo, $name, $data);
|
||||
|
||||
if (empty($etag)) {
|
||||
throw new \Exception('Failed to update calendar object on remote server');
|
||||
}
|
||||
|
||||
// Then update locally
|
||||
return $this->caldavBackend->updateCalendarObject($this->federationInfo->getId(), $name, $data, $this->getCalendarType());
|
||||
}
|
||||
|
||||
public function deleteFile(string $name): void {
|
||||
// Delete from remote server first
|
||||
$this->federatedCalendarService->deleteCalendarObject($this->federationInfo, $name);
|
||||
|
||||
// Then delete locally
|
||||
$this->caldavBackend->deleteCalendarObject($this->federationInfo->getId(), $name, $this->getCalendarType());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -94,8 +94,8 @@ class FederatedCalendarEntity extends Entity {
|
||||
'{' . \Sabre\CalDAV\Plugin::NS_CALENDARSERVER . '}getctag' => $this->getSyncTokenForSabre(),
|
||||
'{' . \Sabre\CalDAV\Plugin::NS_CALDAV . '}supported-calendar-component-set' => $this->getSupportedCalendarComponentSet(),
|
||||
'{' . \OCA\DAV\DAV\Sharing\Plugin::NS_OWNCLOUD . '}owner-principal' => $this->getSharedByPrincipal(),
|
||||
// TODO: implement read-write sharing
|
||||
'{' . \OCA\DAV\DAV\Sharing\Plugin::NS_OWNCLOUD . '}read-only' => 1
|
||||
'{' . \OCA\DAV\DAV\Sharing\Plugin::NS_OWNCLOUD . '}read-only' => ($this->getPermissions() & \OCP\Constants::PERMISSION_UPDATE) === 0 ? 1 : 0,
|
||||
'{' . \OCA\DAV\DAV\Sharing\Plugin::NS_OWNCLOUD . '}permissions' => $this->getPermissions(),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,34 +9,23 @@ declare(strict_types=1);
|
||||
|
||||
namespace OCA\DAV\CalDAV\Federation;
|
||||
|
||||
use OCA\DAV\AppInfo\Application;
|
||||
use OCA\DAV\CalDAV\CalDavBackend;
|
||||
use OCP\IConfig;
|
||||
use OCP\IL10N;
|
||||
use OCP\L10N\IFactory as IL10NFactory;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
class FederatedCalendarFactory {
|
||||
private readonly IL10N $l10n;
|
||||
|
||||
public function __construct(
|
||||
private readonly CalDavBackend $caldavBackend,
|
||||
private readonly IConfig $config,
|
||||
private readonly LoggerInterface $logger,
|
||||
private readonly FederatedCalendarMapper $federatedCalendarMapper,
|
||||
IL10NFactory $l10nFactory,
|
||||
private readonly FederatedCalendarSyncService $federatedCalendarService,
|
||||
private readonly CalDavBackend $caldavBackend,
|
||||
) {
|
||||
$this->l10n = $l10nFactory->get(Application::APP_ID);
|
||||
}
|
||||
|
||||
public function createFederatedCalendar(array $calendarInfo): FederatedCalendar {
|
||||
return new FederatedCalendar(
|
||||
$this->federatedCalendarMapper,
|
||||
$this->federatedCalendarService,
|
||||
$this->caldavBackend,
|
||||
$calendarInfo,
|
||||
$this->l10n,
|
||||
$this->config,
|
||||
$this->logger,
|
||||
$this->federatedCalendarMapper,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,8 +51,7 @@ class FederatedCalendarImpl implements ICalendar, ICalendarIsShared, ICalendarIs
|
||||
}
|
||||
|
||||
public function getPermissions(): int {
|
||||
// TODO: implement read-write sharing
|
||||
return Constants::PERMISSION_READ;
|
||||
return $this->calendarInfo['{http://owncloud.org/ns}permissions'] ?? Constants::PERMISSION_READ;
|
||||
}
|
||||
|
||||
public function isDeleted(): bool {
|
||||
@@ -64,7 +63,8 @@ class FederatedCalendarImpl implements ICalendar, ICalendarIsShared, ICalendarIs
|
||||
}
|
||||
|
||||
public function isWritable(): bool {
|
||||
return false;
|
||||
$permissions = $this->getPermissions();
|
||||
return ($permissions & Constants::PERMISSION_UPDATE) !== 0;
|
||||
}
|
||||
|
||||
public function isEnabled(): bool {
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OCA\DAV\CalDAV\Federation;
|
||||
|
||||
use Sabre\CalDAV\ICalendarObject;
|
||||
use Sabre\DAV\Exception\MethodNotAllowed;
|
||||
use Sabre\DAVACL\IACL;
|
||||
|
||||
class FederatedCalendarObject implements ICalendarObject, IACL {
|
||||
|
||||
public function __construct(
|
||||
protected FederatedCalendar $calendarObject,
|
||||
protected $objectData,
|
||||
) {
|
||||
}
|
||||
|
||||
public function getName(): string {
|
||||
return $this->objectData['uri'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name Name of the file
|
||||
*/
|
||||
public function setName($name) {
|
||||
throw new \Exception('Not implemented');
|
||||
}
|
||||
|
||||
public function get(): string {
|
||||
return $this->objectData['calendardata'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param resource|string $data contents of the file
|
||||
*/
|
||||
public function put($data): string {
|
||||
|
||||
$etag = $this->calendarObject->updateFile($this->objectData['uri'], $data);
|
||||
$this->objectData['calendardata'] = $data;
|
||||
$this->objectData['etag'] = $etag;
|
||||
|
||||
return $etag;
|
||||
}
|
||||
|
||||
public function delete(): void {
|
||||
$this->calendarObject->deleteFile($this->objectData['uri']);
|
||||
}
|
||||
|
||||
public function getContentType(): ?string {
|
||||
$mime = 'text/calendar; charset=utf-8';
|
||||
if (isset($this->objectData['component']) && $this->objectData['component']) {
|
||||
$mime .= '; component=' . $this->objectData['component'];
|
||||
}
|
||||
|
||||
return $mime;
|
||||
}
|
||||
|
||||
public function getETag(): string {
|
||||
if (isset($this->objectData['etag'])) {
|
||||
return $this->objectData['etag'];
|
||||
} else {
|
||||
return '"' . md5($this->get()) . '"';
|
||||
}
|
||||
}
|
||||
|
||||
public function getLastModified(): int {
|
||||
return $this->objectData['lastmodified'];
|
||||
}
|
||||
|
||||
public function getSize(): int {
|
||||
if (isset($this->objectData['size'])) {
|
||||
return $this->objectData['size'];
|
||||
} else {
|
||||
return strlen($this->get());
|
||||
}
|
||||
}
|
||||
|
||||
public function getOwner(): ?string {
|
||||
return $this->calendarObject->getPrincipalURI();
|
||||
}
|
||||
|
||||
public function getGroup(): ?string {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<array-key, mixed>
|
||||
*/
|
||||
public function getACL(): array {
|
||||
return $this->calendarObject->getACL();
|
||||
}
|
||||
|
||||
public function setACL(array $acl): void {
|
||||
throw new MethodNotAllowed('Changing ACLs on federated events is not allowed');
|
||||
}
|
||||
|
||||
public function getSupportedPrivilegeSet(): ?array {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -9,20 +9,52 @@ declare(strict_types=1);
|
||||
|
||||
namespace OCA\DAV\CalDAV\Federation;
|
||||
|
||||
use OCA\DAV\CalDAV\SyncService as CalDavSyncService;
|
||||
use OCA\DAV\CalDAV\CalDavBackend;
|
||||
use OCA\DAV\Service\ASyncService;
|
||||
use OCP\AppFramework\Db\TTransactional;
|
||||
use OCP\AppFramework\Http;
|
||||
use OCP\Federation\ICloudIdManager;
|
||||
use OCP\Http\Client\IClientService;
|
||||
use OCP\IConfig;
|
||||
use OCP\IDBConnection;
|
||||
use Psr\Http\Client\ClientExceptionInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
class FederatedCalendarSyncService {
|
||||
class FederatedCalendarSyncService extends ASyncService {
|
||||
use TTransactional;
|
||||
|
||||
private const SYNC_TOKEN_PREFIX = 'http://sabre.io/ns/sync/';
|
||||
|
||||
public function __construct(
|
||||
IClientService $clientService,
|
||||
IConfig $config,
|
||||
private readonly FederatedCalendarMapper $federatedCalendarMapper,
|
||||
private readonly LoggerInterface $logger,
|
||||
private readonly CalDavSyncService $syncService,
|
||||
private readonly CalDavBackend $backend,
|
||||
private readonly IDBConnection $dbConnection,
|
||||
private readonly ICloudIdManager $cloudIdManager,
|
||||
) {
|
||||
parent::__construct($clientService, $config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract and encode credentials from a federated calendar entity.
|
||||
*
|
||||
* @return array{username: string, remoteUrl: string, token: string}
|
||||
*/
|
||||
private function getCredentials(FederatedCalendarEntity $calendar): array {
|
||||
[,, $sharedWith] = explode('/', $calendar->getPrincipaluri());
|
||||
$calDavUser = $this->cloudIdManager->getCloudId($sharedWith, null)->getId();
|
||||
|
||||
// Need to encode the cloud id as it might contain a colon which is not allowed in basic
|
||||
// auth according to RFC 7617
|
||||
$calDavUser = base64_encode($calDavUser);
|
||||
|
||||
return [
|
||||
'username' => $calDavUser,
|
||||
'remoteUrl' => $calendar->getRemoteUrl(),
|
||||
'token' => $calendar->getToken(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -31,29 +63,77 @@ class FederatedCalendarSyncService {
|
||||
* @throws ClientExceptionInterface If syncing the calendar fails.
|
||||
*/
|
||||
public function syncOne(FederatedCalendarEntity $calendar): int {
|
||||
[,, $sharedWith] = explode('/', $calendar->getPrincipaluri());
|
||||
$calDavUser = $this->cloudIdManager->getCloudId($sharedWith, null)->getId();
|
||||
$remoteUrl = $calendar->getRemoteUrl();
|
||||
$credentials = $this->getCredentials($calendar);
|
||||
$syncToken = $calendar->getSyncTokenForSabre();
|
||||
|
||||
// Need to encode the cloud id as it might contain a colon which is not allowed in basic
|
||||
// auth according to RFC 7617
|
||||
$calDavUser = base64_encode($calDavUser);
|
||||
try {
|
||||
$response = $this->requestSyncReport(
|
||||
$credentials['remoteUrl'],
|
||||
$credentials['username'],
|
||||
$credentials['token'],
|
||||
$syncToken,
|
||||
);
|
||||
} catch (ClientExceptionInterface $ex) {
|
||||
if ($ex->getCode() === Http::STATUS_UNAUTHORIZED) {
|
||||
// Remote server revoked access to the calendar => remove it
|
||||
$this->federatedCalendarMapper->delete($calendar);
|
||||
$this->logger->warning("Authorization failed, remove federated calendar: {$credentials['remoteUrl']}", [
|
||||
'app' => 'dav',
|
||||
]);
|
||||
return 0;
|
||||
}
|
||||
$this->logger->error('Client exception:', ['app' => 'dav', 'exception' => $ex]);
|
||||
throw $ex;
|
||||
}
|
||||
|
||||
$syncResponse = $this->syncService->syncRemoteCalendar(
|
||||
$remoteUrl,
|
||||
$calDavUser,
|
||||
$calendar->getToken(),
|
||||
$syncToken,
|
||||
$calendar,
|
||||
);
|
||||
// Process changes from remote
|
||||
$downloadedEvents = 0;
|
||||
foreach ($response['response'] as $resource => $status) {
|
||||
$objectUri = basename($resource);
|
||||
if (isset($status[200])) {
|
||||
// Object created or updated
|
||||
$absoluteUrl = $this->prepareUri($credentials['remoteUrl'], $resource);
|
||||
$calendarData = $this->download($absoluteUrl, $credentials['username'], $credentials['token']);
|
||||
$this->atomic(function () use ($calendar, $objectUri, $calendarData): void {
|
||||
$existingObject = $this->backend->getCalendarObject(
|
||||
$calendar->getId(),
|
||||
$objectUri,
|
||||
CalDavBackend::CALENDAR_TYPE_FEDERATED
|
||||
);
|
||||
if (!$existingObject) {
|
||||
$this->backend->createCalendarObject(
|
||||
$calendar->getId(),
|
||||
$objectUri,
|
||||
$calendarData,
|
||||
CalDavBackend::CALENDAR_TYPE_FEDERATED
|
||||
);
|
||||
} else {
|
||||
$this->backend->updateCalendarObject(
|
||||
$calendar->getId(),
|
||||
$objectUri,
|
||||
$calendarData,
|
||||
CalDavBackend::CALENDAR_TYPE_FEDERATED
|
||||
);
|
||||
}
|
||||
}, $this->dbConnection);
|
||||
$downloadedEvents++;
|
||||
} else {
|
||||
// Object deleted
|
||||
$this->backend->deleteCalendarObject(
|
||||
$calendar->getId(),
|
||||
$objectUri,
|
||||
CalDavBackend::CALENDAR_TYPE_FEDERATED,
|
||||
true
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$newSyncToken = $syncResponse->getSyncToken();
|
||||
$newSyncToken = $response['token'];
|
||||
|
||||
// Check sync token format and extract the actual sync token integer
|
||||
$matches = [];
|
||||
if (!preg_match('/^http:\/\/sabre\.io\/ns\/sync\/([0-9]+)$/', $newSyncToken, $matches)) {
|
||||
$this->logger->error("Failed to sync federated calendar at $remoteUrl: New sync token has unexpected format: $newSyncToken", [
|
||||
$this->logger->error("Failed to sync federated calendar at {$credentials['remoteUrl']}: New sync token has unexpected format: $newSyncToken", [
|
||||
'calendar' => $calendar->toCalendarInfo(),
|
||||
'newSyncToken' => $newSyncToken,
|
||||
]);
|
||||
@@ -67,10 +147,58 @@ class FederatedCalendarSyncService {
|
||||
$newSyncToken,
|
||||
);
|
||||
} else {
|
||||
$this->logger->debug("Sync Token for $remoteUrl unchanged from previous sync");
|
||||
$this->logger->debug("Sync Token for {$credentials['remoteUrl']} unchanged from previous sync");
|
||||
$this->federatedCalendarMapper->updateSyncTime($calendar->getId());
|
||||
}
|
||||
|
||||
return $syncResponse->getDownloadedEvents();
|
||||
return $downloadedEvents;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a calendar object on the remote server.
|
||||
*
|
||||
* @throws ClientExceptionInterface If the remote request fails.
|
||||
*/
|
||||
public function createCalendarObject(FederatedCalendarEntity $calendar, string $name, string $data): string {
|
||||
$credentials = $this->getCredentials($calendar);
|
||||
$objectUrl = $this->prepareUri($credentials['remoteUrl'], $name);
|
||||
|
||||
return $this->requestPut(
|
||||
$objectUrl,
|
||||
$credentials['username'],
|
||||
$credentials['token'],
|
||||
$data,
|
||||
'text/calendar; charset=utf-8'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a calendar object on the remote server.
|
||||
*
|
||||
* @throws ClientExceptionInterface If the remote request fails.
|
||||
*/
|
||||
public function updateCalendarObject(FederatedCalendarEntity $calendar, string $name, string $data): string {
|
||||
$credentials = $this->getCredentials($calendar);
|
||||
$objectUrl = $this->prepareUri($credentials['remoteUrl'], $name);
|
||||
|
||||
return $this->requestPut(
|
||||
$objectUrl,
|
||||
$credentials['username'],
|
||||
$credentials['token'],
|
||||
$data,
|
||||
'text/calendar; charset=utf-8'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a calendar object on the remote server.
|
||||
*
|
||||
* @throws ClientExceptionInterface If the remote request fails.
|
||||
*/
|
||||
public function deleteCalendarObject(FederatedCalendarEntity $calendar, string $name): void {
|
||||
$credentials = $this->getCredentials($calendar);
|
||||
$objectUrl = $this->prepareUri($credentials['remoteUrl'], $name);
|
||||
|
||||
$this->requestDelete($objectUrl, $credentials['username'], $credentials['token']);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ use OCA\DAV\CalDAV\Calendar;
|
||||
use OCA\DAV\CalDAV\CalendarHome;
|
||||
use OCA\DAV\CalDAV\CalendarObject;
|
||||
use OCA\DAV\CalDAV\DefaultCalendarValidator;
|
||||
use OCA\DAV\CalDAV\Federation\FederatedCalendar;
|
||||
use OCA\DAV\CalDAV\TipBroker;
|
||||
use OCP\IConfig;
|
||||
use Psr\Log\LoggerInterface;
|
||||
@@ -173,8 +174,15 @@ class Plugin extends \Sabre\CalDAV\Schedule\Plugin {
|
||||
return;
|
||||
}
|
||||
|
||||
/** @var Calendar $calendarNode */
|
||||
/** @var Calendar&ICalendar $calendarNode */
|
||||
$calendarNode = $this->server->tree->getNodeForPath($calendarPath);
|
||||
|
||||
// abort if calendar is federated
|
||||
if ($calendarNode instanceof FederatedCalendar) {
|
||||
$this->logger->debug('Not processing scheduling for federated calendar at path: ' . $calendarPath);
|
||||
return;
|
||||
}
|
||||
|
||||
// extract addresses for owner
|
||||
$addresses = $this->getAddressesForPrincipal($calendarNode->getOwner());
|
||||
// determine if request is from a sharee
|
||||
|
||||
@@ -1020,7 +1020,7 @@ class CardDavBackend implements BackendInterface, SyncSupport {
|
||||
'synctoken' => $query->createNamedParameter($syncToken),
|
||||
'addressbookid' => $query->createNamedParameter($addressBookId),
|
||||
'operation' => $query->createNamedParameter($operation),
|
||||
'created_at' => $query->createNamedParameter(time()),
|
||||
'created_at' => time(),
|
||||
])
|
||||
->executeStatement();
|
||||
|
||||
|
||||
@@ -164,6 +164,7 @@ class SharingMapper {
|
||||
->andWhere($query->expr()->eq(
|
||||
'type',
|
||||
$query->createNamedParameter($resourceType, IQueryBuilder::PARAM_STR)),
|
||||
IQueryBuilder::PARAM_STR,
|
||||
)
|
||||
->executeQuery();
|
||||
|
||||
|
||||
@@ -191,4 +191,70 @@ abstract class ASyncService {
|
||||
rtrim($responseUri, '/'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Push data to the remote server via HTTP PUT.
|
||||
* Used for creating or updating CalDAV/CardDAV objects.
|
||||
*
|
||||
* @param string $url The absolute URL to PUT to
|
||||
* @param string $username The username for authentication
|
||||
* @param string $token The authentication token/password
|
||||
* @param string $data The data to upload
|
||||
* @param string $contentType The Content-Type header (e.g., 'text/calendar' or 'text/vcard')
|
||||
*
|
||||
* @return string The ETag returned by the server
|
||||
*/
|
||||
protected function requestPut(
|
||||
string $url,
|
||||
string $username,
|
||||
string $token,
|
||||
string $data,
|
||||
string $contentType = 'text/calendar; charset=utf-8',
|
||||
): string {
|
||||
$client = $this->getClient();
|
||||
|
||||
$options = [
|
||||
'auth' => [$username, $token],
|
||||
'body' => $data,
|
||||
'headers' => [
|
||||
'Content-Type' => $contentType,
|
||||
],
|
||||
'verify' => !$this->config->getSystemValue(
|
||||
'sharing.federation.allowSelfSignedCertificates',
|
||||
false,
|
||||
),
|
||||
];
|
||||
|
||||
$response = $client->put($url, $options);
|
||||
|
||||
// Extract and return the ETag from the response
|
||||
$etag = $response->getHeader('ETag');
|
||||
return $etag;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a resource from the remote server via HTTP DELETE.
|
||||
* Used for deleting CalDAV/CardDAV objects.
|
||||
*
|
||||
* @param string $url The absolute URL to DELETE
|
||||
* @param string $username The username for authentication
|
||||
* @param string $token The authentication token/password
|
||||
*/
|
||||
protected function requestDelete(
|
||||
string $url,
|
||||
string $username,
|
||||
string $token,
|
||||
): void {
|
||||
$client = $this->getClient();
|
||||
|
||||
$options = [
|
||||
'auth' => [$username, $token],
|
||||
'verify' => !$this->config->getSystemValue(
|
||||
'sharing.federation.allowSelfSignedCertificates',
|
||||
false,
|
||||
),
|
||||
];
|
||||
|
||||
$client->delete($url, $options);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,6 @@ 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;
|
||||
@@ -47,12 +46,9 @@ 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, $parameter],
|
||||
[1337, \PDO::PARAM_STR, null, 'namedParameter1337']
|
||||
]);
|
||||
|
||||
$function = 'function1337';
|
||||
|
||||
@@ -92,11 +92,12 @@ class CalendarFederationProviderTest extends TestCase {
|
||||
->willReturn(true);
|
||||
|
||||
$this->federatedCalendarMapper->expects(self::once())
|
||||
->method('deleteByUri')
|
||||
->method('findByUri')
|
||||
->with(
|
||||
'principals/users/sharee1',
|
||||
'ae4b8ab904076fff2b955ea21b1a0d92',
|
||||
);
|
||||
)
|
||||
->willReturn(null);
|
||||
|
||||
$this->federatedCalendarMapper->expects(self::once())
|
||||
->method('insert')
|
||||
@@ -123,6 +124,68 @@ class CalendarFederationProviderTest extends TestCase {
|
||||
$this->assertEquals(10, $this->calendarFederationProvider->shareReceived($share));
|
||||
}
|
||||
|
||||
public function testShareReceivedWithExistingCalendar(): void {
|
||||
$share = $this->createMock(ICloudFederationShare::class);
|
||||
$share->method('getShareType')
|
||||
->willReturn('user');
|
||||
$share->method('getProtocol')
|
||||
->willReturn([
|
||||
'version' => 'v1',
|
||||
'url' => 'https://nextcloud.remote/remote.php/dav/remote-calendars/abcdef123/cal1_shared_by_user1',
|
||||
'displayName' => 'Calendar 1',
|
||||
'color' => '#ff0000',
|
||||
'access' => 3,
|
||||
'components' => 'VEVENT,VTODO',
|
||||
]);
|
||||
$share->method('getShareWith')
|
||||
->willReturn('sharee1');
|
||||
$share->method('getShareSecret')
|
||||
->willReturn('new-token');
|
||||
$share->method('getSharedBy')
|
||||
->willReturn('user1@nextcloud.remote');
|
||||
$share->method('getSharedByDisplayName')
|
||||
->willReturn('User 1');
|
||||
|
||||
$this->calendarFederationConfig->expects(self::once())
|
||||
->method('isFederationEnabled')
|
||||
->willReturn(true);
|
||||
|
||||
$existingCalendar = new FederatedCalendarEntity();
|
||||
$existingCalendar->setId(10);
|
||||
$existingCalendar->setPrincipaluri('principals/users/sharee1');
|
||||
$existingCalendar->setUri('ae4b8ab904076fff2b955ea21b1a0d92');
|
||||
$existingCalendar->setRemoteUrl('https://nextcloud.remote/remote.php/dav/remote-calendars/abcdef123/cal1_shared_by_user1');
|
||||
$existingCalendar->setToken('old-token');
|
||||
$existingCalendar->setPermissions(1);
|
||||
$existingCalendar->setComponents('VEVENT');
|
||||
|
||||
$this->federatedCalendarMapper->expects(self::once())
|
||||
->method('findByUri')
|
||||
->with(
|
||||
'principals/users/sharee1',
|
||||
'ae4b8ab904076fff2b955ea21b1a0d92',
|
||||
)
|
||||
->willReturn($existingCalendar);
|
||||
|
||||
$this->federatedCalendarMapper->expects(self::never())
|
||||
->method('insert');
|
||||
|
||||
$this->federatedCalendarMapper->expects(self::once())
|
||||
->method('update')
|
||||
->willReturnCallback(function (FederatedCalendarEntity $calendar) {
|
||||
$this->assertEquals('new-token', $calendar->getToken());
|
||||
$this->assertEquals(1, $calendar->getPermissions());
|
||||
$this->assertEquals('VEVENT,VTODO', $calendar->getComponents());
|
||||
return $calendar;
|
||||
});
|
||||
|
||||
$this->jobList->expects(self::once())
|
||||
->method('add')
|
||||
->with(FederatedCalendarSyncJob::class, ['id' => 10]);
|
||||
|
||||
$this->assertEquals(10, $this->calendarFederationProvider->shareReceived($share));
|
||||
}
|
||||
|
||||
public function testShareReceivedWithInvalidProtocolVersion(): void {
|
||||
$share = $this->createMock(ICloudFederationShare::class);
|
||||
$share->method('getShareType')
|
||||
@@ -270,7 +333,7 @@ class CalendarFederationProviderTest extends TestCase {
|
||||
$this->calendarFederationProvider->shareReceived($share);
|
||||
}
|
||||
|
||||
public function testShareReceivedWithUnsupportedAccess(): void {
|
||||
public function testShareReceivedWithReadWriteAccess(): void {
|
||||
$share = $this->createMock(ICloudFederationShare::class);
|
||||
$share->method('getShareType')
|
||||
->willReturn('user');
|
||||
@@ -296,6 +359,65 @@ class CalendarFederationProviderTest extends TestCase {
|
||||
->method('isFederationEnabled')
|
||||
->willReturn(true);
|
||||
|
||||
$this->federatedCalendarMapper->expects(self::once())
|
||||
->method('findByUri')
|
||||
->with(
|
||||
'principals/users/sharee1',
|
||||
'ae4b8ab904076fff2b955ea21b1a0d92',
|
||||
)
|
||||
->willReturn(null);
|
||||
|
||||
$this->federatedCalendarMapper->expects(self::once())
|
||||
->method('insert')
|
||||
->willReturnCallback(function (FederatedCalendarEntity $calendar) {
|
||||
$this->assertEquals('principals/users/sharee1', $calendar->getPrincipaluri());
|
||||
$this->assertEquals('ae4b8ab904076fff2b955ea21b1a0d92', $calendar->getUri());
|
||||
$this->assertEquals('https://nextcloud.remote/remote.php/dav/remote-calendars/abcdef123/cal1_shared_by_user1', $calendar->getRemoteUrl());
|
||||
$this->assertEquals('Calendar 1', $calendar->getDisplayName());
|
||||
$this->assertEquals('#ff0000', $calendar->getColor());
|
||||
$this->assertEquals('token', $calendar->getToken());
|
||||
$this->assertEquals('user1@nextcloud.remote', $calendar->getSharedBy());
|
||||
$this->assertEquals('User 1', $calendar->getSharedByDisplayName());
|
||||
$this->assertEquals(15, $calendar->getPermissions()); // READ | CREATE | UPDATE | DELETE
|
||||
$this->assertEquals('VEVENT,VTODO', $calendar->getComponents());
|
||||
|
||||
$calendar->setId(10);
|
||||
return $calendar;
|
||||
});
|
||||
|
||||
$this->jobList->expects(self::once())
|
||||
->method('add')
|
||||
->with(FederatedCalendarSyncJob::class, ['id' => 10]);
|
||||
|
||||
$this->assertEquals(10, $this->calendarFederationProvider->shareReceived($share));
|
||||
}
|
||||
|
||||
public function testShareReceivedWithUnsupportedAccess(): void {
|
||||
$share = $this->createMock(ICloudFederationShare::class);
|
||||
$share->method('getShareType')
|
||||
->willReturn('user');
|
||||
$share->method('getProtocol')
|
||||
->willReturn([
|
||||
'version' => 'v1',
|
||||
'url' => 'https://nextcloud.remote/remote.php/dav/remote-calendars/abcdef123/cal1_shared_by_user1',
|
||||
'displayName' => 'Calendar 1',
|
||||
'color' => '#ff0000',
|
||||
'access' => 999, // Invalid access value
|
||||
'components' => 'VEVENT,VTODO',
|
||||
]);
|
||||
$share->method('getShareWith')
|
||||
->willReturn('sharee1');
|
||||
$share->method('getShareSecret')
|
||||
->willReturn('token');
|
||||
$share->method('getSharedBy')
|
||||
->willReturn('user1@nextcloud.remote');
|
||||
$share->method('getSharedByDisplayName')
|
||||
->willReturn('User 1');
|
||||
|
||||
$this->calendarFederationConfig->expects(self::once())
|
||||
->method('isFederationEnabled')
|
||||
->willReturn(true);
|
||||
|
||||
$this->federatedCalendarMapper->expects(self::never())
|
||||
->method('insert');
|
||||
$this->jobList->expects(self::never())
|
||||
|
||||
@@ -9,13 +9,17 @@ declare(strict_types=1);
|
||||
|
||||
namespace OCA\DAV\Tests\unit\CalDAV\Federation;
|
||||
|
||||
use OCA\DAV\CalDAV\CalDavBackend;
|
||||
use OCA\DAV\CalDAV\Federation\FederatedCalendarEntity;
|
||||
use OCA\DAV\CalDAV\Federation\FederatedCalendarMapper;
|
||||
use OCA\DAV\CalDAV\Federation\FederatedCalendarSyncService;
|
||||
use OCA\DAV\CalDAV\SyncService as CalDavSyncService;
|
||||
use OCA\DAV\CalDAV\SyncServiceResult;
|
||||
use OCP\Federation\ICloudId;
|
||||
use OCP\Federation\ICloudIdManager;
|
||||
use OCP\Http\Client\IClient;
|
||||
use OCP\Http\Client\IClientService;
|
||||
use OCP\Http\Client\IResponse;
|
||||
use OCP\IConfig;
|
||||
use OCP\IDBConnection;
|
||||
use PHPUnit\Framework\Attributes\DataProvider;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use Psr\Log\LoggerInterface;
|
||||
@@ -26,21 +30,30 @@ class FederatedCalendarSyncServiceTest extends TestCase {
|
||||
|
||||
private FederatedCalendarMapper&MockObject $federatedCalendarMapper;
|
||||
private LoggerInterface&MockObject $logger;
|
||||
private CalDavSyncService&MockObject $calDavSyncService;
|
||||
private CalDavBackend&MockObject $backend;
|
||||
private IDBConnection&MockObject $dbConnection;
|
||||
private ICloudIdManager&MockObject $cloudIdManager;
|
||||
private IClientService&MockObject $clientService;
|
||||
private IConfig&MockObject $config;
|
||||
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
$this->federatedCalendarMapper = $this->createMock(FederatedCalendarMapper::class);
|
||||
$this->logger = $this->createMock(LoggerInterface::class);
|
||||
$this->calDavSyncService = $this->createMock(CalDavSyncService::class);
|
||||
$this->backend = $this->createMock(CalDavBackend::class);
|
||||
$this->dbConnection = $this->createMock(IDBConnection::class);
|
||||
$this->cloudIdManager = $this->createMock(ICloudIdManager::class);
|
||||
$this->clientService = $this->createMock(IClientService::class);
|
||||
$this->config = $this->createMock(IConfig::class);
|
||||
|
||||
$this->federatedCalendarSyncService = new FederatedCalendarSyncService(
|
||||
$this->clientService,
|
||||
$this->config,
|
||||
$this->federatedCalendarMapper,
|
||||
$this->logger,
|
||||
$this->calDavSyncService,
|
||||
$this->backend,
|
||||
$this->dbConnection,
|
||||
$this->cloudIdManager,
|
||||
);
|
||||
}
|
||||
@@ -61,16 +74,24 @@ class FederatedCalendarSyncServiceTest extends TestCase {
|
||||
->with('user1')
|
||||
->willReturn($cloudId);
|
||||
|
||||
$this->calDavSyncService->expects(self::once())
|
||||
->method('syncRemoteCalendar')
|
||||
->with(
|
||||
'https://remote.tld/remote.php/dav/remote-calendars/abcdef123/cal1_shared_by_user2',
|
||||
'dXNlcjFAbmV4dGNsb3VkLnRlc3Rpbmc=',
|
||||
'token',
|
||||
'http://sabre.io/ns/sync/100',
|
||||
$calendar,
|
||||
)
|
||||
->willReturn(new SyncServiceResult('http://sabre.io/ns/sync/101', 10));
|
||||
// Mock HTTP client for sync report
|
||||
$client = $this->createMock(IClient::class);
|
||||
$response = $this->createMock(IResponse::class);
|
||||
$response->method('getBody')
|
||||
->willReturn('<?xml version="1.0"?><d:multistatus xmlns:d="DAV:"><d:sync-token>http://sabre.io/ns/sync/101</d:sync-token></d:multistatus>');
|
||||
|
||||
$client->expects(self::once())
|
||||
->method('request')
|
||||
->with('REPORT', 'https://remote.tld/remote.php/dav/remote-calendars/abcdef123/cal1_shared_by_user2', self::anything())
|
||||
->willReturn($response);
|
||||
|
||||
$this->clientService->method('newClient')
|
||||
->willReturn($client);
|
||||
|
||||
$this->config->method('getSystemValueInt')
|
||||
->willReturn(30);
|
||||
$this->config->method('getSystemValue')
|
||||
->willReturn(false);
|
||||
|
||||
$this->federatedCalendarMapper->expects(self::once())
|
||||
->method('updateSyncTokenAndTime')
|
||||
@@ -78,7 +99,7 @@ class FederatedCalendarSyncServiceTest extends TestCase {
|
||||
$this->federatedCalendarMapper->expects(self::never())
|
||||
->method('updateSyncTime');
|
||||
|
||||
$this->assertEquals(10, $this->federatedCalendarSyncService->syncOne($calendar));
|
||||
$this->assertEquals(0, $this->federatedCalendarSyncService->syncOne($calendar));
|
||||
}
|
||||
|
||||
public function testSyncOneUnchanged(): void {
|
||||
@@ -97,16 +118,24 @@ class FederatedCalendarSyncServiceTest extends TestCase {
|
||||
->with('user1')
|
||||
->willReturn($cloudId);
|
||||
|
||||
$this->calDavSyncService->expects(self::once())
|
||||
->method('syncRemoteCalendar')
|
||||
->with(
|
||||
'https://remote.tld/remote.php/dav/remote-calendars/abcdef123/cal1_shared_by_user2',
|
||||
'dXNlcjFAbmV4dGNsb3VkLnRlc3Rpbmc=',
|
||||
'token',
|
||||
'http://sabre.io/ns/sync/100',
|
||||
$calendar,
|
||||
)
|
||||
->willReturn(new SyncServiceResult('http://sabre.io/ns/sync/100', 0));
|
||||
// Mock HTTP client for sync report
|
||||
$client = $this->createMock(IClient::class);
|
||||
$response = $this->createMock(IResponse::class);
|
||||
$response->method('getBody')
|
||||
->willReturn('<?xml version="1.0"?><d:multistatus xmlns:d="DAV:"><d:sync-token>http://sabre.io/ns/sync/100</d:sync-token></d:multistatus>');
|
||||
|
||||
$client->expects(self::once())
|
||||
->method('request')
|
||||
->with('REPORT', 'https://remote.tld/remote.php/dav/remote-calendars/abcdef123/cal1_shared_by_user2', self::anything())
|
||||
->willReturn($response);
|
||||
|
||||
$this->clientService->method('newClient')
|
||||
->willReturn($client);
|
||||
|
||||
$this->config->method('getSystemValueInt')
|
||||
->willReturn(30);
|
||||
$this->config->method('getSystemValue')
|
||||
->willReturn(false);
|
||||
|
||||
$this->federatedCalendarMapper->expects(self::never())
|
||||
->method('updateSyncTokenAndTime');
|
||||
@@ -143,16 +172,24 @@ class FederatedCalendarSyncServiceTest extends TestCase {
|
||||
->with('user1')
|
||||
->willReturn($cloudId);
|
||||
|
||||
$this->calDavSyncService->expects(self::once())
|
||||
->method('syncRemoteCalendar')
|
||||
->with(
|
||||
'https://remote.tld/remote.php/dav/remote-calendars/abcdef123/cal1_shared_by_user2',
|
||||
'dXNlcjFAbmV4dGNsb3VkLnRlc3Rpbmc=',
|
||||
'token',
|
||||
'http://sabre.io/ns/sync/100',
|
||||
$calendar,
|
||||
)
|
||||
->willReturn(new SyncServiceResult($syncToken, 10));
|
||||
// Mock HTTP client for sync report with unexpected token format
|
||||
$client = $this->createMock(IClient::class);
|
||||
$response = $this->createMock(IResponse::class);
|
||||
$response->method('getBody')
|
||||
->willReturn('<?xml version="1.0"?><d:multistatus xmlns:d="DAV:"><d:sync-token>' . $syncToken . '</d:sync-token></d:multistatus>');
|
||||
|
||||
$client->expects(self::once())
|
||||
->method('request')
|
||||
->with('REPORT', 'https://remote.tld/remote.php/dav/remote-calendars/abcdef123/cal1_shared_by_user2', self::anything())
|
||||
->willReturn($response);
|
||||
|
||||
$this->clientService->method('newClient')
|
||||
->willReturn($client);
|
||||
|
||||
$this->config->method('getSystemValueInt')
|
||||
->willReturn(30);
|
||||
$this->config->method('getSystemValue')
|
||||
->willReturn(false);
|
||||
|
||||
$this->federatedCalendarMapper->expects(self::never())
|
||||
->method('updateSyncTokenAndTime');
|
||||
|
||||
@@ -0,0 +1,404 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OCA\DAV\Tests\unit\CalDAV\Federation;
|
||||
|
||||
use OCA\DAV\CalDAV\CalDavBackend;
|
||||
use OCA\DAV\CalDAV\Federation\FederatedCalendar;
|
||||
use OCA\DAV\CalDAV\Federation\FederatedCalendarEntity;
|
||||
use OCA\DAV\CalDAV\Federation\FederatedCalendarMapper;
|
||||
use OCA\DAV\CalDAV\Federation\FederatedCalendarObject;
|
||||
use OCA\DAV\CalDAV\Federation\FederatedCalendarSyncService;
|
||||
use OCP\Constants;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use Sabre\DAV\Exception\MethodNotAllowed;
|
||||
use Sabre\DAV\Exception\NotFound;
|
||||
use Sabre\DAV\PropPatch;
|
||||
use Test\TestCase;
|
||||
|
||||
class FederatedCalendarTest extends TestCase {
|
||||
private FederatedCalendar $federatedCalendar;
|
||||
|
||||
private FederatedCalendarMapper&MockObject $federatedCalendarMapper;
|
||||
private FederatedCalendarSyncService&MockObject $federatedCalendarService;
|
||||
private CalDavBackend&MockObject $caldavBackend;
|
||||
private FederatedCalendarEntity $federationInfo;
|
||||
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
$this->federatedCalendarMapper = $this->createMock(FederatedCalendarMapper::class);
|
||||
$this->federatedCalendarService = $this->createMock(FederatedCalendarSyncService::class);
|
||||
$this->caldavBackend = $this->createMock(CalDavBackend::class);
|
||||
|
||||
$this->federationInfo = new FederatedCalendarEntity();
|
||||
$this->federationInfo->setId(10);
|
||||
$this->federationInfo->setPrincipaluri('principals/users/user1');
|
||||
$this->federationInfo->setUri('calendar-uri');
|
||||
$this->federationInfo->setDisplayName('Federated Calendar');
|
||||
$this->federationInfo->setColor('#ff0000');
|
||||
$this->federationInfo->setSharedBy('user2@nextcloud.remote');
|
||||
$this->federationInfo->setSharedByDisplayName('User 2');
|
||||
$this->federationInfo->setPermissions(Constants::PERMISSION_READ);
|
||||
$this->federationInfo->setLastSync(1234567890);
|
||||
|
||||
$this->federatedCalendarMapper->method('findByUri')
|
||||
->with('principals/users/user1', 'calendar-uri')
|
||||
->willReturn($this->federationInfo);
|
||||
|
||||
$calendarInfo = [
|
||||
'principaluri' => 'principals/users/user1',
|
||||
'id' => 10,
|
||||
'uri' => 'calendar-uri',
|
||||
];
|
||||
|
||||
$this->federatedCalendar = new FederatedCalendar(
|
||||
$this->federatedCalendarMapper,
|
||||
$this->federatedCalendarService,
|
||||
$this->caldavBackend,
|
||||
$calendarInfo,
|
||||
);
|
||||
}
|
||||
|
||||
public function testGetResourceId(): void {
|
||||
$this->assertEquals(10, $this->federatedCalendar->getResourceId());
|
||||
}
|
||||
|
||||
public function testGetName(): void {
|
||||
$this->assertEquals('calendar-uri', $this->federatedCalendar->getName());
|
||||
}
|
||||
|
||||
public function testSetName(): void {
|
||||
$this->expectException(MethodNotAllowed::class);
|
||||
$this->expectExceptionMessage('Renaming federated calendars is not allowed');
|
||||
$this->federatedCalendar->setName('new-name');
|
||||
}
|
||||
|
||||
public function testGetPrincipalURI(): void {
|
||||
$this->assertEquals('principals/users/user1', $this->federatedCalendar->getPrincipalURI());
|
||||
}
|
||||
|
||||
public function testGetOwner(): void {
|
||||
$expected = 'principals/remote-users/' . base64_encode('user2@nextcloud.remote');
|
||||
$this->assertEquals($expected, $this->federatedCalendar->getOwner());
|
||||
}
|
||||
|
||||
public function testGetGroup(): void {
|
||||
$this->assertNull($this->federatedCalendar->getGroup());
|
||||
}
|
||||
|
||||
public function testGetACLWithReadOnlyPermissions(): void {
|
||||
$this->federationInfo->setPermissions(Constants::PERMISSION_READ);
|
||||
|
||||
$acl = $this->federatedCalendar->getACL();
|
||||
|
||||
$this->assertCount(3, $acl);
|
||||
// Check basic read permissions
|
||||
$this->assertEquals('{DAV:}read', $acl[0]['privilege']);
|
||||
$this->assertTrue($acl[0]['protected']);
|
||||
$this->assertEquals('{DAV:}read-acl', $acl[1]['privilege']);
|
||||
$this->assertTrue($acl[1]['protected']);
|
||||
$this->assertEquals('{DAV:}write-properties', $acl[2]['privilege']);
|
||||
$this->assertTrue($acl[2]['protected']);
|
||||
}
|
||||
|
||||
public function testGetACLWithCreatePermission(): void {
|
||||
$this->federationInfo->setPermissions(Constants::PERMISSION_READ | Constants::PERMISSION_CREATE);
|
||||
|
||||
$acl = $this->federatedCalendar->getACL();
|
||||
|
||||
$this->assertCount(4, $acl);
|
||||
// Check that create permission is added
|
||||
$privileges = array_column($acl, 'privilege');
|
||||
$this->assertContains('{DAV:}bind', $privileges);
|
||||
}
|
||||
|
||||
public function testGetACLWithUpdatePermission(): void {
|
||||
$this->federationInfo->setPermissions(Constants::PERMISSION_READ | Constants::PERMISSION_UPDATE);
|
||||
|
||||
$acl = $this->federatedCalendar->getACL();
|
||||
|
||||
$this->assertCount(4, $acl);
|
||||
// Check that update permission is added (write-content, not write-properties which is already in base ACL)
|
||||
$privileges = array_column($acl, 'privilege');
|
||||
$this->assertContains('{DAV:}write-content', $privileges);
|
||||
}
|
||||
|
||||
public function testGetACLWithDeletePermission(): void {
|
||||
$this->federationInfo->setPermissions(Constants::PERMISSION_READ | Constants::PERMISSION_DELETE);
|
||||
|
||||
$acl = $this->federatedCalendar->getACL();
|
||||
|
||||
$this->assertCount(4, $acl);
|
||||
// Check that delete permission is added
|
||||
$privileges = array_column($acl, 'privilege');
|
||||
$this->assertContains('{DAV:}unbind', $privileges);
|
||||
}
|
||||
|
||||
public function testGetACLWithAllPermissions(): void {
|
||||
$this->federationInfo->setPermissions(
|
||||
Constants::PERMISSION_READ
|
||||
| Constants::PERMISSION_CREATE
|
||||
| Constants::PERMISSION_UPDATE
|
||||
| Constants::PERMISSION_DELETE
|
||||
);
|
||||
|
||||
$acl = $this->federatedCalendar->getACL();
|
||||
|
||||
$this->assertCount(6, $acl);
|
||||
$privileges = array_column($acl, 'privilege');
|
||||
$this->assertContains('{DAV:}read', $privileges);
|
||||
$this->assertContains('{DAV:}bind', $privileges);
|
||||
$this->assertContains('{DAV:}write-content', $privileges);
|
||||
$this->assertContains('{DAV:}write-properties', $privileges);
|
||||
$this->assertContains('{DAV:}unbind', $privileges);
|
||||
}
|
||||
|
||||
public function testSetACL(): void {
|
||||
$this->expectException(MethodNotAllowed::class);
|
||||
$this->expectExceptionMessage('Changing ACLs on federated calendars is not allowed');
|
||||
$this->federatedCalendar->setACL([]);
|
||||
}
|
||||
|
||||
public function testGetSupportedPrivilegeSet(): void {
|
||||
$this->assertNull($this->federatedCalendar->getSupportedPrivilegeSet());
|
||||
}
|
||||
|
||||
public function testGetProperties(): void {
|
||||
$properties = $this->federatedCalendar->getProperties([
|
||||
'{DAV:}displayname',
|
||||
'{http://apple.com/ns/ical/}calendar-color',
|
||||
]);
|
||||
|
||||
$this->assertEquals('Federated Calendar', $properties['{DAV:}displayname']);
|
||||
$this->assertEquals('#ff0000', $properties['{http://apple.com/ns/ical/}calendar-color']);
|
||||
}
|
||||
|
||||
public function testPropPatchWithDisplayName(): void {
|
||||
$propPatch = $this->createMock(PropPatch::class);
|
||||
$propPatch->method('getMutations')
|
||||
->willReturn([
|
||||
'{DAV:}displayname' => 'New Calendar Name',
|
||||
]);
|
||||
|
||||
$this->federatedCalendarMapper->expects(self::once())
|
||||
->method('update')
|
||||
->willReturnCallback(function (FederatedCalendarEntity $entity) {
|
||||
$this->assertEquals('New Calendar Name', $entity->getDisplayName());
|
||||
return $entity;
|
||||
});
|
||||
|
||||
$propPatch->expects(self::once())
|
||||
->method('setResultCode')
|
||||
->with('{DAV:}displayname', 200);
|
||||
|
||||
$this->federatedCalendar->propPatch($propPatch);
|
||||
}
|
||||
|
||||
public function testPropPatchWithColor(): void {
|
||||
$propPatch = $this->createMock(PropPatch::class);
|
||||
$propPatch->method('getMutations')
|
||||
->willReturn([
|
||||
'{http://apple.com/ns/ical/}calendar-color' => '#00ff00',
|
||||
]);
|
||||
|
||||
$this->federatedCalendarMapper->expects(self::once())
|
||||
->method('update')
|
||||
->willReturnCallback(function (FederatedCalendarEntity $entity) {
|
||||
$this->assertEquals('#00ff00', $entity->getColor());
|
||||
return $entity;
|
||||
});
|
||||
|
||||
$propPatch->expects(self::once())
|
||||
->method('setResultCode')
|
||||
->with('{http://apple.com/ns/ical/}calendar-color', 200);
|
||||
|
||||
$this->federatedCalendar->propPatch($propPatch);
|
||||
}
|
||||
|
||||
public function testPropPatchWithNoMutations(): void {
|
||||
$propPatch = $this->createMock(PropPatch::class);
|
||||
$propPatch->method('getMutations')
|
||||
->willReturn([]);
|
||||
|
||||
$this->federatedCalendarMapper->expects(self::never())
|
||||
->method('update');
|
||||
|
||||
$propPatch->expects(self::never())
|
||||
->method('handle');
|
||||
|
||||
$this->federatedCalendar->propPatch($propPatch);
|
||||
}
|
||||
|
||||
public function testGetChildACL(): void {
|
||||
$this->assertEquals($this->federatedCalendar->getACL(), $this->federatedCalendar->getChildACL());
|
||||
}
|
||||
|
||||
public function testGetLastModified(): void {
|
||||
$this->assertEquals(1234567890, $this->federatedCalendar->getLastModified());
|
||||
}
|
||||
|
||||
public function testDelete(): void {
|
||||
$this->federatedCalendarMapper->expects(self::once())
|
||||
->method('deleteById')
|
||||
->with(10);
|
||||
|
||||
$this->federatedCalendar->delete();
|
||||
}
|
||||
|
||||
public function testCreateDirectory(): void {
|
||||
$this->expectException(MethodNotAllowed::class);
|
||||
$this->expectExceptionMessage('Creating nested collection is not allowed');
|
||||
$this->federatedCalendar->createDirectory('test');
|
||||
}
|
||||
|
||||
public function testCalendarQuery(): void {
|
||||
$filters = ['comp-filter' => ['name' => 'VEVENT']];
|
||||
$expectedUris = ['event1.ics', 'event2.ics'];
|
||||
|
||||
$this->caldavBackend->expects(self::once())
|
||||
->method('calendarQuery')
|
||||
->with(10, $filters, 2) // 2 is CALENDAR_TYPE_FEDERATED
|
||||
->willReturn($expectedUris);
|
||||
|
||||
$result = $this->federatedCalendar->calendarQuery($filters);
|
||||
$this->assertEquals($expectedUris, $result);
|
||||
}
|
||||
|
||||
public function testGetChild(): void {
|
||||
$objectData = [
|
||||
'id' => 1,
|
||||
'uri' => 'event1.ics',
|
||||
'calendardata' => 'BEGIN:VCALENDAR...',
|
||||
];
|
||||
|
||||
$this->caldavBackend->expects(self::once())
|
||||
->method('getCalendarObject')
|
||||
->with(10, 'event1.ics', 2) // 2 is CALENDAR_TYPE_FEDERATED
|
||||
->willReturn($objectData);
|
||||
|
||||
$child = $this->federatedCalendar->getChild('event1.ics');
|
||||
$this->assertInstanceOf(FederatedCalendarObject::class, $child);
|
||||
}
|
||||
|
||||
public function testGetChildNotFound(): void {
|
||||
$this->caldavBackend->expects(self::once())
|
||||
->method('getCalendarObject')
|
||||
->with(10, 'nonexistent.ics', 2)
|
||||
->willReturn(null);
|
||||
|
||||
$this->expectException(NotFound::class);
|
||||
$this->federatedCalendar->getChild('nonexistent.ics');
|
||||
}
|
||||
|
||||
public function testGetChildren(): void {
|
||||
$objects = [
|
||||
['id' => 1, 'uri' => 'event1.ics', 'calendardata' => 'BEGIN:VCALENDAR...'],
|
||||
['id' => 2, 'uri' => 'event2.ics', 'calendardata' => 'BEGIN:VCALENDAR...'],
|
||||
];
|
||||
|
||||
$this->caldavBackend->expects(self::once())
|
||||
->method('getCalendarObjects')
|
||||
->with(10, 2) // 2 is CALENDAR_TYPE_FEDERATED
|
||||
->willReturn($objects);
|
||||
|
||||
$children = $this->federatedCalendar->getChildren();
|
||||
$this->assertCount(2, $children);
|
||||
$this->assertInstanceOf(FederatedCalendarObject::class, $children[0]);
|
||||
$this->assertInstanceOf(FederatedCalendarObject::class, $children[1]);
|
||||
}
|
||||
|
||||
public function testGetMultipleChildren(): void {
|
||||
$paths = ['event1.ics', 'event2.ics'];
|
||||
$objects = [
|
||||
['id' => 1, 'uri' => 'event1.ics', 'calendardata' => 'BEGIN:VCALENDAR...'],
|
||||
['id' => 2, 'uri' => 'event2.ics', 'calendardata' => 'BEGIN:VCALENDAR...'],
|
||||
];
|
||||
|
||||
$this->caldavBackend->expects(self::once())
|
||||
->method('getMultipleCalendarObjects')
|
||||
->with(10, $paths, 2) // 2 is CALENDAR_TYPE_FEDERATED
|
||||
->willReturn($objects);
|
||||
|
||||
$children = $this->federatedCalendar->getMultipleChildren($paths);
|
||||
$this->assertCount(2, $children);
|
||||
$this->assertInstanceOf(FederatedCalendarObject::class, $children[0]);
|
||||
$this->assertInstanceOf(FederatedCalendarObject::class, $children[1]);
|
||||
}
|
||||
|
||||
public function testChildExists(): void {
|
||||
$this->caldavBackend->expects(self::once())
|
||||
->method('getCalendarObject')
|
||||
->with(10, 'event1.ics', 2)
|
||||
->willReturn(['id' => 1, 'uri' => 'event1.ics']);
|
||||
|
||||
$result = $this->federatedCalendar->childExists('event1.ics');
|
||||
$this->assertTrue($result);
|
||||
}
|
||||
|
||||
public function testChildNotExists(): void {
|
||||
$this->caldavBackend->expects(self::once())
|
||||
->method('getCalendarObject')
|
||||
->with(10, 'nonexistent.ics', 2)
|
||||
->willReturn(null);
|
||||
|
||||
$result = $this->federatedCalendar->childExists('nonexistent.ics');
|
||||
$this->assertFalse($result);
|
||||
}
|
||||
|
||||
public function testCreateFile(): void {
|
||||
$calendarData = 'BEGIN:VCALENDAR...END:VCALENDAR';
|
||||
$remoteEtag = '"remote-etag-123"';
|
||||
$localEtag = '"local-etag-456"';
|
||||
|
||||
$this->federatedCalendarService->expects(self::once())
|
||||
->method('createCalendarObject')
|
||||
->with($this->federationInfo, 'event1.ics', $calendarData)
|
||||
->willReturn($remoteEtag);
|
||||
|
||||
$this->caldavBackend->expects(self::once())
|
||||
->method('createCalendarObject')
|
||||
->with(10, 'event1.ics', $calendarData, 2)
|
||||
->willReturn($localEtag);
|
||||
|
||||
$result = $this->federatedCalendar->createFile('event1.ics', $calendarData);
|
||||
$this->assertEquals($localEtag, $result);
|
||||
}
|
||||
|
||||
public function testUpdateFile(): void {
|
||||
$calendarData = 'BEGIN:VCALENDAR...UPDATED...END:VCALENDAR';
|
||||
$remoteEtag = '"remote-etag-updated"';
|
||||
$localEtag = '"local-etag-updated"';
|
||||
|
||||
$this->federatedCalendarService->expects(self::once())
|
||||
->method('updateCalendarObject')
|
||||
->with($this->federationInfo, 'event1.ics', $calendarData)
|
||||
->willReturn($remoteEtag);
|
||||
|
||||
$this->caldavBackend->expects(self::once())
|
||||
->method('updateCalendarObject')
|
||||
->with(10, 'event1.ics', $calendarData, 2)
|
||||
->willReturn($localEtag);
|
||||
|
||||
$result = $this->federatedCalendar->updateFile('event1.ics', $calendarData);
|
||||
$this->assertEquals($localEtag, $result);
|
||||
}
|
||||
|
||||
public function testDeleteFile(): void {
|
||||
$this->federatedCalendarService->expects(self::once())
|
||||
->method('deleteCalendarObject')
|
||||
->with($this->federationInfo, 'event1.ics');
|
||||
|
||||
$this->caldavBackend->expects(self::once())
|
||||
->method('deleteCalendarObject')
|
||||
->with(10, 'event1.ics', 2);
|
||||
|
||||
$this->federatedCalendar->deleteFile('event1.ics');
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,6 @@ 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;
|
||||
@@ -416,12 +415,9 @@ 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, $parameter]
|
||||
[$token, \PDO::PARAM_STR, null, 'namedParameterToken']
|
||||
]);
|
||||
|
||||
$stmt->expects($this->once())
|
||||
|
||||
@@ -545,7 +545,7 @@ class Crypt {
|
||||
$options,
|
||||
$iv);
|
||||
|
||||
if ($plainContent) {
|
||||
if ($plainContent !== false) {
|
||||
return $plainContent;
|
||||
} else {
|
||||
throw new DecryptionFailedException('Encryption library: Decryption (symmetric) of content failed: ' . openssl_error_string());
|
||||
|
||||
@@ -95,7 +95,7 @@ class Backends extends Base {
|
||||
*/
|
||||
private function formatConfiguration(array $parameters): array {
|
||||
$configuration = array_filter($parameters, function (DefinitionParameter $parameter) {
|
||||
return $parameter->isFlagSet(DefinitionParameter::FLAG_HIDDEN);
|
||||
return !$parameter->isFlagSet(DefinitionParameter::FLAG_HIDDEN);
|
||||
});
|
||||
return array_map(function (DefinitionParameter $parameter) {
|
||||
return $parameter->getTypeName();
|
||||
|
||||
@@ -270,8 +270,8 @@ class AmazonS3 extends Common {
|
||||
$connection->deleteObjects([
|
||||
'Bucket' => $this->bucket,
|
||||
'Delete' => [
|
||||
'Quiet' => true,
|
||||
'Objects' => array_map(fn (array $object) => [
|
||||
'ETag' => $object['ETag'],
|
||||
'Key' => $object['Key'],
|
||||
], $objects['Contents'])
|
||||
]
|
||||
|
||||
@@ -233,7 +233,7 @@ OC.L10N.register(
|
||||
"Create a new share link" : "Креирајте нов линк за споделување",
|
||||
"Quick share options, the current selected is \"{selectedOption}\"" : "Опции за брзо споделување за , тековната избрана е \"{selectedOption}\"",
|
||||
"View only" : "Само за гледање",
|
||||
"Can edit" : "Може да се уредува",
|
||||
"Can edit" : "Може да уредува",
|
||||
"Custom permissions" : "Прилагодени дозволи",
|
||||
"Resharing is not allowed" : "Повторно споделување не е дозволено",
|
||||
"Name or email …" : "Име или е-пошта …",
|
||||
|
||||
@@ -231,7 +231,7 @@
|
||||
"Create a new share link" : "Креирајте нов линк за споделување",
|
||||
"Quick share options, the current selected is \"{selectedOption}\"" : "Опции за брзо споделување за , тековната избрана е \"{selectedOption}\"",
|
||||
"View only" : "Само за гледање",
|
||||
"Can edit" : "Може да се уредува",
|
||||
"Can edit" : "Може да уредува",
|
||||
"Custom permissions" : "Прилагодени дозволи",
|
||||
"Resharing is not allowed" : "Повторно споделување не е дозволено",
|
||||
"Name or email …" : "Име или е-пошта …",
|
||||
|
||||
Generated
+21
-21
@@ -6730,9 +6730,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/asn1.js/node_modules/bn.js": {
|
||||
"version": "4.12.2",
|
||||
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.2.tgz",
|
||||
"integrity": "sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw==",
|
||||
"version": "4.12.3",
|
||||
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.3.tgz",
|
||||
"integrity": "sha512-fGTi3gxV/23FTYdAoUtLYp6qySe2KE3teyZitipKNRuVYcBkoP/bB3guXN/XVKUe9mxCHXnc9C4ocyz8OmgN0g==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/assert": {
|
||||
@@ -7084,9 +7084,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/bn.js": {
|
||||
"version": "5.2.2",
|
||||
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.2.tgz",
|
||||
"integrity": "sha512-v2YAxEmKaBLahNwE1mjp4WON6huMNeuDvagFZW+ASCuA/ku0bXR9hSMw0XpiqMoA3+rmnyck/tPRSFQkoC9Cuw==",
|
||||
"version": "5.2.3",
|
||||
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.3.tgz",
|
||||
"integrity": "sha512-EAcmnPkxpntVL+DS7bO1zhcZNvCkxqtkd0ZY53h06GNQ3DEkkGZ/gKgmDv6DdZQGj9BgfSPKtJJ7Dp1GPP8f7w==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/body-parser": {
|
||||
@@ -8114,9 +8114,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/create-ecdh/node_modules/bn.js": {
|
||||
"version": "4.12.2",
|
||||
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.2.tgz",
|
||||
"integrity": "sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw==",
|
||||
"version": "4.12.3",
|
||||
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.3.tgz",
|
||||
"integrity": "sha512-fGTi3gxV/23FTYdAoUtLYp6qySe2KE3teyZitipKNRuVYcBkoP/bB3guXN/XVKUe9mxCHXnc9C4ocyz8OmgN0g==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/create-hash": {
|
||||
@@ -8690,9 +8690,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/diffie-hellman/node_modules/bn.js": {
|
||||
"version": "4.12.2",
|
||||
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.2.tgz",
|
||||
"integrity": "sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw==",
|
||||
"version": "4.12.3",
|
||||
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.3.tgz",
|
||||
"integrity": "sha512-fGTi3gxV/23FTYdAoUtLYp6qySe2KE3teyZitipKNRuVYcBkoP/bB3guXN/XVKUe9mxCHXnc9C4ocyz8OmgN0g==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/dijkstrajs": {
|
||||
@@ -8889,9 +8889,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/elliptic/node_modules/bn.js": {
|
||||
"version": "4.12.2",
|
||||
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.2.tgz",
|
||||
"integrity": "sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw==",
|
||||
"version": "4.12.3",
|
||||
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.3.tgz",
|
||||
"integrity": "sha512-fGTi3gxV/23FTYdAoUtLYp6qySe2KE3teyZitipKNRuVYcBkoP/bB3guXN/XVKUe9mxCHXnc9C4ocyz8OmgN0g==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/emoji-mart-vue-fast": {
|
||||
@@ -13028,9 +13028,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/miller-rabin/node_modules/bn.js": {
|
||||
"version": "4.12.2",
|
||||
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.2.tgz",
|
||||
"integrity": "sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw==",
|
||||
"version": "4.12.3",
|
||||
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.3.tgz",
|
||||
"integrity": "sha512-fGTi3gxV/23FTYdAoUtLYp6qySe2KE3teyZitipKNRuVYcBkoP/bB3guXN/XVKUe9mxCHXnc9C4ocyz8OmgN0g==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/mime": {
|
||||
@@ -14750,9 +14750,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/public-encrypt/node_modules/bn.js": {
|
||||
"version": "4.12.2",
|
||||
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.2.tgz",
|
||||
"integrity": "sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw==",
|
||||
"version": "4.12.3",
|
||||
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.3.tgz",
|
||||
"integrity": "sha512-fGTi3gxV/23FTYdAoUtLYp6qySe2KE3teyZitipKNRuVYcBkoP/bB3guXN/XVKUe9mxCHXnc9C4ocyz8OmgN0g==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/punycode": {
|
||||
|
||||
@@ -3371,6 +3371,45 @@
|
||||
<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, [
|
||||
@@ -3970,4 +4009,13 @@
|
||||
<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>
|
||||
|
||||
@@ -21,6 +21,38 @@ $qb->selectColumnsDistinct('f');
|
||||
$qb->selectAlias('g', 'h');
|
||||
$qb->selectAlias($qb->func()->lower('i'), 'j');
|
||||
|
||||
$qb
|
||||
->setParameter('k', 'l')
|
||||
->setParameters([])
|
||||
->setFirstResult(0)
|
||||
->setMaxResults(0)
|
||||
->delete()
|
||||
->update()
|
||||
->insert()
|
||||
->from('m')
|
||||
->join('n', 'o', 'p')
|
||||
->innerJoin('q', 'r', 's')
|
||||
->leftJoin('t', 'u', 'v')
|
||||
->rightJoin('w', 'x', 'y')
|
||||
->set('z', '1')
|
||||
->where()
|
||||
->andWhere()
|
||||
->orWhere()
|
||||
->groupBy()
|
||||
->addGroupBy()
|
||||
->setValue('2', '3')
|
||||
->values([])
|
||||
->having()
|
||||
->andHaving()
|
||||
->orHaving()
|
||||
->orderBy('4')
|
||||
->addOrderBy('5')
|
||||
->resetQueryParts()
|
||||
->resetQueryPart('6')
|
||||
->hintShardKey('7', '8')
|
||||
->runAcrossAllShards()
|
||||
->forUpdate();
|
||||
|
||||
/** @psalm-check-type-exact $result = \OCP\DB\IResult<'a'|'b'|'c'|'d'|'e'|'f'|'h'|'j'> */
|
||||
$result = $qb->executeQuery();
|
||||
|
||||
|
||||
@@ -82,7 +82,7 @@ class StatusCommand extends Command implements CompletionAwareInterface {
|
||||
|
||||
$numExecutedUnavailableMigrations = count($executedUnavailableMigrations);
|
||||
$numNewMigrations = count(array_diff(array_keys($availableMigrations), $executedMigrations));
|
||||
$pending = $ms->describeMigrationStep('lastest');
|
||||
$pending = $ms->describeMigrationStep();
|
||||
|
||||
$infos = [
|
||||
'App' => $ms->getApp(),
|
||||
|
||||
@@ -43,6 +43,12 @@ class ResetPassword extends Base {
|
||||
InputOption::VALUE_NONE,
|
||||
'read password from environment variable NC_PASS/OC_PASS'
|
||||
)
|
||||
->addOption(
|
||||
'no-password',
|
||||
null,
|
||||
InputOption::VALUE_NONE,
|
||||
'Sets the password to blank'
|
||||
)
|
||||
;
|
||||
}
|
||||
|
||||
@@ -76,22 +82,32 @@ class ResetPassword extends Base {
|
||||
}
|
||||
}
|
||||
|
||||
$question = new Question('Enter a new password: ');
|
||||
$question->setHidden(true);
|
||||
$password = $helper->ask($input, $output, $question);
|
||||
if ($input->getOption('no-password')) {
|
||||
$question = new ConfirmationQuestion('Are you sure you want to clear the password for ' . $username . '?');
|
||||
|
||||
if ($password === null) {
|
||||
$output->writeln('<error>Password cannot be empty!</error>');
|
||||
return 1;
|
||||
}
|
||||
if (!$helper->ask($input, $output, $question)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
$question = new Question('Confirm the new password: ');
|
||||
$question->setHidden(true);
|
||||
$confirm = $helper->ask($input, $output, $question);
|
||||
$password = '';
|
||||
} else {
|
||||
$question = new Question('Enter a new password: ');
|
||||
$question->setHidden(true);
|
||||
$password = $helper->ask($input, $output, $question);
|
||||
|
||||
if ($password !== $confirm) {
|
||||
$output->writeln('<error>Passwords did not match!</error>');
|
||||
return 1;
|
||||
if ($password === null) {
|
||||
$output->writeln('<error>Password cannot be empty!</error>');
|
||||
return 1;
|
||||
}
|
||||
|
||||
$question = new Question('Confirm the new password: ');
|
||||
$question->setHidden(true);
|
||||
$confirm = $helper->ask($input, $output, $question);
|
||||
|
||||
if ($password !== $confirm) {
|
||||
$output->writeln('<error>Passwords did not match!</error>');
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$output->writeln('<error>Interactive input or --password-from-env is needed for entering a new password!</error>');
|
||||
|
||||
+4
-3
@@ -766,9 +766,10 @@ class OC {
|
||||
self::checkConfig();
|
||||
self::checkInstalled($systemConfig);
|
||||
|
||||
self::addSecurityHeaders();
|
||||
|
||||
self::performSameSiteCookieProtection($config);
|
||||
if (!self::$CLI) {
|
||||
self::addSecurityHeaders();
|
||||
self::performSameSiteCookieProtection($config);
|
||||
}
|
||||
|
||||
if (!defined('OC_CONSOLE')) {
|
||||
$eventLogger->start('check_server', 'Run a few configuration checks');
|
||||
|
||||
@@ -16,10 +16,6 @@ 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,9 +5,6 @@
|
||||
* 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;
|
||||
@@ -23,7 +20,6 @@ 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 {
|
||||
@@ -41,178 +37,385 @@ class ExpressionBuilder implements IExpressionBuilder {
|
||||
$this->functionBuilder = $queryBuilder->func();
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function andX(ICompositeExpression|string ...$x): ICompositeExpression {
|
||||
/**
|
||||
* 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 {
|
||||
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);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function orX(ICompositeExpression|string ...$x): ICompositeExpression {
|
||||
/**
|
||||
* 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 {
|
||||
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);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function comparison(string|ILiteral|IQueryFunction|IParameter $x, string $operator, $y, int|string|null $type = null): string {
|
||||
/**
|
||||
* 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 {
|
||||
$x = $this->prepareColumn($x, $type);
|
||||
$y = $this->prepareColumn($y, $type);
|
||||
return $this->expressionBuilder->comparison($x, $operator, $y);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function eq(string|ILiteral|IQueryFunction|IParameter $x, IQueryFunction|ILiteral|IParameter|string $y, int|string|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:
|
||||
*
|
||||
* [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 {
|
||||
$x = $this->prepareColumn($x, $type);
|
||||
$y = $this->prepareColumn($y, $type);
|
||||
return $this->expressionBuilder->eq($x, $y);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function neq(string|ILiteral|IQueryFunction|IParameter $x, string|ILiteral|IQueryFunction|IParameter $y, int|string|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:
|
||||
*
|
||||
* [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 {
|
||||
$x = $this->prepareColumn($x, $type);
|
||||
$y = $this->prepareColumn($y, $type);
|
||||
return $this->expressionBuilder->neq($x, $y);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function lt(string|ILiteral|IQueryFunction|IParameter $x, string|ILiteral|IQueryFunction|IParameter $y, int|string|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:
|
||||
*
|
||||
* [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 {
|
||||
$x = $this->prepareColumn($x, $type);
|
||||
$y = $this->prepareColumn($y, $type);
|
||||
return $this->expressionBuilder->lt($x, $y);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function lte(string|ILiteral|IQueryFunction|IParameter $x, string|ILiteral|IQueryFunction|IParameter $y, int|string|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:
|
||||
*
|
||||
* [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 {
|
||||
$x = $this->prepareColumn($x, $type);
|
||||
$y = $this->prepareColumn($y, $type);
|
||||
return $this->expressionBuilder->lte($x, $y);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function gt(string|ILiteral|IQueryFunction|IParameter $x, string|ILiteral|IQueryFunction|IParameter $y, int|string|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:
|
||||
*
|
||||
* [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 {
|
||||
$x = $this->prepareColumn($x, $type);
|
||||
$y = $this->prepareColumn($y, $type);
|
||||
return $this->expressionBuilder->gt($x, $y);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function gte(string|ILiteral|IQueryFunction|IParameter $x, string|ILiteral|IQueryFunction|IParameter $y, int|string|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:
|
||||
*
|
||||
* [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 {
|
||||
$x = $this->prepareColumn($x, $type);
|
||||
$y = $this->prepareColumn($y, $type);
|
||||
return $this->expressionBuilder->gte($x, $y);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function isNull(string|ILiteral|IParameter|IQueryFunction $x): string {
|
||||
/**
|
||||
* 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 {
|
||||
$x = $this->helper->quoteColumnName($x);
|
||||
return $this->expressionBuilder->isNull($x);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function isNotNull(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
|
||||
*/
|
||||
public function isNotNull($x): string {
|
||||
$x = $this->helper->quoteColumnName($x);
|
||||
return $this->expressionBuilder->isNotNull($x);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function like(
|
||||
string|IParameter|ILiteral|IQueryFunction $x,
|
||||
string|IParameter|ILiteral|IQueryFunction $y,
|
||||
int|string|null $type = null,
|
||||
): 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
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function like($x, $y, $type = null): string {
|
||||
$x = $this->helper->quoteColumnName($x);
|
||||
$y = $this->helper->quoteColumnName($y);
|
||||
return $this->expressionBuilder->like($x, $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 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 notLike(
|
||||
string|IParameter|ILiteral|IQueryFunction $x,
|
||||
string|IParameter|ILiteral|IQueryFunction $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
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function notLike($x, $y, $type = null): string {
|
||||
$x = $this->helper->quoteColumnName($x);
|
||||
$y = $this->helper->quoteColumnName($y);
|
||||
return $this->expressionBuilder->notLike($x, $y);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function in(
|
||||
ILiteral|IParameter|IQueryFunction|string $x,
|
||||
ILiteral|IParameter|IQueryFunction|string|array $y,
|
||||
int|string|null $type = null,
|
||||
): string {
|
||||
/**
|
||||
* 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 {
|
||||
$x = $this->helper->quoteColumnName($x);
|
||||
$y = $this->helper->quoteColumnNames($y);
|
||||
return $this->expressionBuilder->in($x, $y);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function notIn(
|
||||
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
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function notIn($x, $y, $type = null): string {
|
||||
$x = $this->helper->quoteColumnName($x);
|
||||
$y = $this->helper->quoteColumnNames($y);
|
||||
return $this->expressionBuilder->notIn($x, $y);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
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
|
||||
*/
|
||||
public function emptyString($x): string {
|
||||
return $this->eq($x, $this->literal('', IQueryBuilder::PARAM_STR));
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function nonEmptyString(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
|
||||
*/
|
||||
public function nonEmptyString($x): string {
|
||||
return $this->neq($x, $this->literal('', IQueryBuilder::PARAM_STR));
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function bitwiseAnd(string|ILiteral $x, int $y): IQueryFunction {
|
||||
/**
|
||||
* 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 {
|
||||
return new QueryFunction($this->connection->getDatabasePlatform()->getBitAndComparisonExpression(
|
||||
$this->helper->quoteColumnName($x),
|
||||
(string)$y
|
||||
$y
|
||||
));
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function bitwiseOr(string|ILiteral $x, int $y): IQueryFunction {
|
||||
/**
|
||||
* 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 {
|
||||
return new QueryFunction($this->connection->getDatabasePlatform()->getBitOrComparisonExpression(
|
||||
$this->helper->quoteColumnName($x),
|
||||
(string)$y
|
||||
$y
|
||||
));
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function literal($input, int|string $type = IQueryBuilder::PARAM_STR): ILiteral {
|
||||
/**
|
||||
* 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 {
|
||||
return new Literal($this->expressionBuilder->literal($input, $type));
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function castColumn(string|IQueryFunction|ILiteral|IParameter $column, int|string $type): IQueryFunction {
|
||||
/**
|
||||
* 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 {
|
||||
return new QueryFunction(
|
||||
$this->helper->quoteColumnName($column)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param IQueryBuilder::PARAM_* $type
|
||||
* @param mixed $column
|
||||
* @param mixed|null $type
|
||||
* @return array|IQueryFunction|string
|
||||
*/
|
||||
protected function prepareColumn(IQueryFunction|ILiteral|IParameter|string|array $column, int|string|null $type): array|string {
|
||||
protected function prepareColumn($column, $type) {
|
||||
return $this->helper->quoteColumnNames($column);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,18 +5,12 @@
|
||||
* 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 {
|
||||
@@ -25,25 +19,35 @@ 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');
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function iLike(string|IParameter|ILiteral|IQueryFunction $x, string|IParameter|ILiteral|IQueryFunction $y, int|string|null $type = null): string {
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function iLike($x, $y, $type = null): string {
|
||||
$x = $this->helper->quoteColumnName($x);
|
||||
$y = $this->helper->quoteColumnName($y);
|
||||
return $this->expressionBuilder->comparison($x, ' COLLATE ' . $this->collation . ' LIKE', $y);
|
||||
}
|
||||
|
||||
#[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),
|
||||
};
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,11 +12,14 @@ 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 {
|
||||
#[Override]
|
||||
protected function prepareColumn(IQueryFunction|ILiteral|IParameter|string|array $column, int|string|null $type): array|string {
|
||||
/**
|
||||
* @param mixed $column
|
||||
* @param mixed|null $type
|
||||
* @return array|IQueryFunction|string
|
||||
*/
|
||||
protected function prepareColumn($column, $type) {
|
||||
if ($type === IQueryBuilder::PARAM_STR && !is_array($column) && !($column instanceof IParameter) && !($column instanceof ILiteral)) {
|
||||
$column = $this->castColumn($column, $type);
|
||||
}
|
||||
@@ -24,8 +27,10 @@ class OCIExpressionBuilder extends ExpressionBuilder {
|
||||
return parent::prepareColumn($column, $type);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function eq(IQueryFunction|ILiteral|IParameter|string $x, IQueryFunction|ILiteral|IParameter|string $y, int|string|null $type = null): string {
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function eq($x, $y, $type = null): string {
|
||||
if ($type === IQueryBuilder::PARAM_JSON) {
|
||||
$x = $this->prepareColumn($x, $type);
|
||||
$y = $this->prepareColumn($y, $type);
|
||||
@@ -35,8 +40,10 @@ class OCIExpressionBuilder extends ExpressionBuilder {
|
||||
return parent::eq($x, $y, $type);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function neq(string|ILiteral|IQueryFunction|IParameter $x, string|ILiteral|IQueryFunction|IParameter $y, int|string|null $type = null): string {
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function neq($x, $y, $type = null): string {
|
||||
if ($type === IQueryBuilder::PARAM_JSON) {
|
||||
$x = $this->prepareColumn($x, $type);
|
||||
$y = $this->prepareColumn($y, $type);
|
||||
@@ -46,34 +53,57 @@ class OCIExpressionBuilder extends ExpressionBuilder {
|
||||
return parent::neq($x, $y, $type);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function in(ILiteral|IParameter|IQueryFunction|string $x, ILiteral|IParameter|IQueryFunction|string|array $y, int|string|null $type = null): string {
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function in($x, $y, $type = null): string {
|
||||
$x = $this->prepareColumn($x, $type);
|
||||
$y = $this->prepareColumn($y, $type);
|
||||
|
||||
return $this->expressionBuilder->in($x, $y);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function notIn(ILiteral|IParameter|IQueryFunction|string $x, ILiteral|IParameter|IQueryFunction|string|array $y, int|string|null $type = null): string {
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function notIn($x, $y, $type = null): string {
|
||||
$x = $this->prepareColumn($x, $type);
|
||||
$y = $this->prepareColumn($y, $type);
|
||||
|
||||
return $this->expressionBuilder->notIn($x, $y);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
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
|
||||
*/
|
||||
public function emptyString($x): string {
|
||||
return $this->isNull($x);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function nonEmptyString(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
|
||||
*/
|
||||
public function nonEmptyString($x): string {
|
||||
return $this->isNotNull($x);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function castColumn(string|IQueryFunction|ILiteral|IParameter $column, int|string|null $type): IQueryFunction {
|
||||
/**
|
||||
* 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 {
|
||||
if ($type === IQueryBuilder::PARAM_STR) {
|
||||
$column = $this->helper->quoteColumnName($column);
|
||||
return new QueryFunction('to_char(' . $column . ')');
|
||||
@@ -86,21 +116,17 @@ class OCIExpressionBuilder extends ExpressionBuilder {
|
||||
return parent::castColumn($column, $type);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function like(
|
||||
ILiteral|IParameter|IQueryFunction|string $x,
|
||||
ILiteral|IParameter|IQueryFunction|string $y,
|
||||
int|string|null $type = null,
|
||||
): string {
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function like($x, $y, $type = null): string {
|
||||
return parent::like($x, $y, $type) . " ESCAPE '\\'";
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function iLike(
|
||||
ILiteral|IParameter|IQueryFunction|string $x,
|
||||
ILiteral|IParameter|IQueryFunction|string $y,
|
||||
int|string|null $type = null,
|
||||
): string {
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function iLike($x, $y, $type = null): string {
|
||||
return $this->like($this->functionBuilder->lower($x), $this->functionBuilder->lower($y));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,20 +12,32 @@ 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 {
|
||||
#[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),
|
||||
};
|
||||
/**
|
||||
* 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]
|
||||
protected function prepareColumn(IQueryFunction|ILiteral|IParameter|string|array $column, int|string|null $type): string|array {
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
protected function prepareColumn($column, $type) {
|
||||
if ($type === IQueryBuilder::PARAM_JSON && !is_array($column) && !($column instanceof IParameter) && !($column instanceof ILiteral)) {
|
||||
$column = $this->castColumn($column, $type);
|
||||
}
|
||||
@@ -33,8 +45,10 @@ class PgSqlExpressionBuilder extends ExpressionBuilder {
|
||||
return parent::prepareColumn($column, $type);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function iLike(string|IParameter|ILiteral|IQueryFunction $x, mixed $y, mixed $type = null): string {
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function iLike($x, $y, $type = null): string {
|
||||
$x = $this->helper->quoteColumnName($x);
|
||||
$y = $this->helper->quoteColumnName($y);
|
||||
return $this->expressionBuilder->comparison($x, 'ILIKE', $y);
|
||||
|
||||
@@ -11,35 +11,44 @@ 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 {
|
||||
#[Override]
|
||||
public function like(ILiteral|IParameter|IQueryFunction|string $x, mixed $y, int|string|null $type = null): string {
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function like($x, $y, $type = null): string {
|
||||
return parent::like($x, $y, $type) . " ESCAPE '\\'";
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function iLike(string|IParameter|ILiteral|IQueryFunction $x, mixed $y, int|string|null $type = null): string {
|
||||
public function iLike($x, $y, $type = null): string {
|
||||
return $this->like($this->functionBuilder->lower($x), $this->functionBuilder->lower($y), $type);
|
||||
}
|
||||
|
||||
#[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)
|
||||
/**
|
||||
* @param mixed $column
|
||||
* @param mixed|null $type
|
||||
* @return array|IQueryFunction|string
|
||||
*/
|
||||
protected function prepareColumn($column, $type) {
|
||||
if ($type !== null
|
||||
&& !is_array($column)
|
||||
&& is_string($type)
|
||||
&& !($column instanceof IParameter)
|
||||
&& !($column instanceof ILiteral)
|
||||
&& (str_starts_with($type, 'date') || str_starts_with($type, 'time'))) {
|
||||
return (string)$this->castColumn($column, $type);
|
||||
return $this->castColumn($column, $type);
|
||||
}
|
||||
|
||||
return parent::prepareColumn($column, $type);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function castColumn(string|IQueryFunction|ILiteral|IParameter $column, string|int $type): IQueryFunction {
|
||||
/**
|
||||
* 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 {
|
||||
switch ($type) {
|
||||
case IQueryBuilder::PARAM_DATE_MUTABLE:
|
||||
case IQueryBuilder::PARAM_DATE_IMMUTABLE:
|
||||
|
||||
@@ -10,349 +10,284 @@ 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 readonly IQueryBuilder $builder,
|
||||
protected IQueryBuilder $builder,
|
||||
) {
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function automaticTablePrefix(bool $enabled): void {
|
||||
public function automaticTablePrefix($enabled) {
|
||||
$this->builder->automaticTablePrefix($enabled);
|
||||
return $this;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function expr(): IExpressionBuilder {
|
||||
public function expr() {
|
||||
return $this->builder->expr();
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function func(): IFunctionBuilder {
|
||||
public function func() {
|
||||
return $this->builder->func();
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function getType(): int {
|
||||
public function getType() {
|
||||
return $this->builder->getType();
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function getConnection(): IDBConnection {
|
||||
public function getConnection() {
|
||||
return $this->builder->getConnection();
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function getState(): int {
|
||||
public function getState() {
|
||||
return $this->builder->getState();
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function getSQL(): string {
|
||||
public function getSQL() {
|
||||
return $this->builder->getSQL();
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function setParameter(string|int $key, mixed $value, string|null|int $type = null): self {
|
||||
public function setParameter($key, $value, $type = null) {
|
||||
$this->builder->setParameter($key, $value, $type);
|
||||
return $this;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function setParameters(array $params, array $types = []): self {
|
||||
public function setParameters(array $params, array $types = []) {
|
||||
$this->builder->setParameters($params, $types);
|
||||
return $this;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function getParameters(): array {
|
||||
public function getParameters() {
|
||||
return $this->builder->getParameters();
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function getParameter(int|string $key): mixed {
|
||||
public function getParameter($key) {
|
||||
return $this->builder->getParameter($key);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function getParameterTypes(): array {
|
||||
public function getParameterTypes() {
|
||||
return $this->builder->getParameterTypes();
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function getParameterType(int|string $key): int|string {
|
||||
public function getParameterType($key) {
|
||||
return $this->builder->getParameterType($key);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function setFirstResult(int $firstResult): self {
|
||||
public function setFirstResult($firstResult) {
|
||||
$this->builder->setFirstResult($firstResult);
|
||||
return $this;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function getFirstResult(): int {
|
||||
public function getFirstResult() {
|
||||
return $this->builder->getFirstResult();
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function setMaxResults(?int $maxResults): self {
|
||||
public function setMaxResults($maxResults) {
|
||||
$this->builder->setMaxResults($maxResults);
|
||||
return $this;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function getMaxResults(): ?int {
|
||||
public function getMaxResults() {
|
||||
return $this->builder->getMaxResults();
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function select(...$selects): self {
|
||||
public function select(...$selects) {
|
||||
$this->builder->select(...$selects);
|
||||
return $this;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function selectAlias(string|IQueryFunction|IParameter|ILiteral $select, string $alias): self {
|
||||
public function selectAlias($select, $alias): self {
|
||||
$this->builder->selectAlias($select, $alias);
|
||||
return $this;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function selectDistinct(string|array $select): self {
|
||||
public function selectDistinct($select) {
|
||||
$this->builder->selectDistinct($select);
|
||||
return $this;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function addSelect(...$select): self {
|
||||
public function addSelect(...$select) {
|
||||
$this->builder->addSelect(...$select);
|
||||
return $this;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function delete(string $delete, ?string $alias = null): self {
|
||||
public function delete($delete = null, $alias = null) {
|
||||
$this->builder->delete($delete, $alias);
|
||||
return $this;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function update(string $update, ?string $alias = null): self {
|
||||
public function update($update = null, $alias = null) {
|
||||
$this->builder->update($update, $alias);
|
||||
return $this;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function insert(string $insert): self {
|
||||
public function insert($insert = null) {
|
||||
$this->builder->insert($insert);
|
||||
return $this;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function from(string|IQueryFunction $from, ?string $alias = null): self {
|
||||
public function from($from, $alias = null) {
|
||||
$this->builder->from($from, $alias);
|
||||
return $this;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function join(string $fromAlias, string|IQueryFunction $join, ?string $alias, string|ICompositeExpression|null $condition = null): self {
|
||||
public function join($fromAlias, $join, $alias, $condition = null) {
|
||||
$this->builder->join($fromAlias, $join, $alias, $condition);
|
||||
return $this;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function innerJoin(string $fromAlias, string|IQueryFunction $join, ?string $alias, string|ICompositeExpression|null $condition = null): self {
|
||||
public function innerJoin($fromAlias, $join, $alias, $condition = null) {
|
||||
$this->builder->innerJoin($fromAlias, $join, $alias, $condition);
|
||||
return $this;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function leftJoin(string $fromAlias, string|IQueryFunction $join, ?string $alias, string|ICompositeExpression|null $condition = null): self {
|
||||
public function leftJoin($fromAlias, $join, $alias, $condition = null) {
|
||||
$this->builder->leftJoin($fromAlias, $join, $alias, $condition);
|
||||
return $this;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function rightJoin(string $fromAlias, string|IQueryFunction $join, ?string $alias, string|ICompositeExpression|null $condition = null): self {
|
||||
public function rightJoin($fromAlias, $join, $alias, $condition = null) {
|
||||
$this->builder->rightJoin($fromAlias, $join, $alias, $condition);
|
||||
return $this;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function set(string $key, ILiteral|IParameter|IQueryFunction|string $value): self {
|
||||
public function set($key, $value) {
|
||||
$this->builder->set($key, $value);
|
||||
return $this;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function where(...$predicates): self {
|
||||
public function where(...$predicates) {
|
||||
$this->builder->where(...$predicates);
|
||||
return $this;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function andWhere(...$where): self {
|
||||
public function andWhere(...$where) {
|
||||
$this->builder->andWhere(...$where);
|
||||
return $this;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function orWhere(...$where): self {
|
||||
public function orWhere(...$where) {
|
||||
$this->builder->orWhere(...$where);
|
||||
return $this;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function groupBy(...$groupBys): self {
|
||||
public function groupBy(...$groupBys) {
|
||||
$this->builder->groupBy(...$groupBys);
|
||||
return $this;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function addGroupBy(...$groupBy): self {
|
||||
public function addGroupBy(...$groupBy) {
|
||||
$this->builder->addGroupBy(...$groupBy);
|
||||
return $this;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function setValue(string $column, ILiteral|IParameter|IQueryFunction|string $value): self {
|
||||
public function setValue($column, $value) {
|
||||
$this->builder->setValue($column, $value);
|
||||
return $this;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function values(array $values): self {
|
||||
public function values(array $values) {
|
||||
$this->builder->values($values);
|
||||
return $this;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function having(...$having): self {
|
||||
public function having(...$having) {
|
||||
$this->builder->having(...$having);
|
||||
return $this;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function andHaving(...$having): self {
|
||||
public function andHaving(...$having) {
|
||||
$this->builder->andHaving(...$having);
|
||||
return $this;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function orHaving(...$having): self {
|
||||
public function orHaving(...$having) {
|
||||
$this->builder->orHaving(...$having);
|
||||
return $this;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function orderBy(string|ILiteral|IParameter|IQueryFunction $sort, ?string $order = null): self {
|
||||
public function orderBy($sort, $order = null) {
|
||||
$this->builder->orderBy($sort, $order);
|
||||
return $this;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function addOrderBy(string|ILiteral|IParameter|IQueryFunction $sort, ?string $order = null): self {
|
||||
public function addOrderBy($sort, $order = null) {
|
||||
$this->builder->addOrderBy($sort, $order);
|
||||
return $this;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function getQueryPart(string $queryPartName): mixed {
|
||||
public function getQueryPart($queryPartName) {
|
||||
return $this->builder->getQueryPart($queryPartName);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function getQueryParts(): array {
|
||||
public function getQueryParts() {
|
||||
return $this->builder->getQueryParts();
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function resetQueryParts(?array $queryPartNames = null): self {
|
||||
public function resetQueryParts($queryPartNames = null) {
|
||||
$this->builder->resetQueryParts($queryPartNames);
|
||||
return $this;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function resetQueryPart(string $queryPartName): self {
|
||||
public function resetQueryPart($queryPartName) {
|
||||
$this->builder->resetQueryPart($queryPartName);
|
||||
return $this;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function createNamedParameter(mixed $value, mixed $type = self::PARAM_STR, $placeHolder = null): IParameter {
|
||||
public function createNamedParameter($value, $type = self::PARAM_STR, $placeHolder = null) {
|
||||
return $this->builder->createNamedParameter($value, $type, $placeHolder);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function createPositionalParameter(mixed $value, mixed $type = self::PARAM_STR): IParameter {
|
||||
public function createPositionalParameter($value, $type = self::PARAM_STR) {
|
||||
return $this->builder->createPositionalParameter($value, $type);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function createParameter(string $name): IParameter {
|
||||
public function createParameter($name) {
|
||||
return $this->builder->createParameter($name);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function createFunction(string $call): IQueryFunction {
|
||||
public function createFunction($call) {
|
||||
return $this->builder->createFunction($call);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function getLastInsertId(): int {
|
||||
return $this->builder->getLastInsertId();
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function getTableName(string|IQueryFunction $table): string {
|
||||
public function getTableName($table) {
|
||||
return $this->builder->getTableName($table);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function getColumnName(string $column, string $tableAlias = ''): string {
|
||||
public function getColumnName($column, $tableAlias = '') {
|
||||
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,31 +6,27 @@
|
||||
*/
|
||||
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 readonly ConnectionAdapter $connection,
|
||||
protected readonly IQueryBuilder $queryBuilder,
|
||||
protected readonly QuoteHelper $helper,
|
||||
protected IDBConnection $connection,
|
||||
protected IQueryBuilder $queryBuilder,
|
||||
protected QuoteHelper $helper,
|
||||
) {
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function md5(string|ILiteral|IParameter|IQueryFunction $input): IQueryFunction {
|
||||
public function md5($input): IQueryFunction {
|
||||
return new QueryFunction('MD5(' . $this->helper->quoteColumnName($input) . ')');
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function concat(string|ILiteral|IParameter|IQueryFunction $x, string|ILiteral|IParameter|IQueryFunction ...$expr): IQueryFunction {
|
||||
public function concat($x, ...$expr): IQueryFunction {
|
||||
$args = func_get_args();
|
||||
$list = [];
|
||||
foreach ($args as $item) {
|
||||
@@ -39,18 +35,12 @@ class FunctionBuilder implements IFunctionBuilder {
|
||||
return new QueryFunction(sprintf('CONCAT(%s)', implode(', ', $list)));
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function groupConcat(string|ILiteral|IParameter|IQueryFunction $expr, ?string $separator = ','): IQueryFunction {
|
||||
public function groupConcat($expr, ?string $separator = ','): IQueryFunction {
|
||||
$separator = $this->connection->quote($separator);
|
||||
return new QueryFunction('GROUP_CONCAT(' . $this->helper->quoteColumnName($expr) . ' SEPARATOR ' . $separator . ')');
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function substring(
|
||||
string|ILiteral|IParameter|IQueryFunction $input,
|
||||
string|ILiteral|IParameter|IQueryFunction $start,
|
||||
null|ILiteral|IParameter|IQueryFunction $length = null,
|
||||
): IQueryFunction {
|
||||
public function substring($input, $start, $length = null): IQueryFunction {
|
||||
if ($length) {
|
||||
return new QueryFunction('SUBSTR(' . $this->helper->quoteColumnName($input) . ', ' . $this->helper->quoteColumnName($start) . ', ' . $this->helper->quoteColumnName($length) . ')');
|
||||
} else {
|
||||
@@ -58,76 +48,53 @@ class FunctionBuilder implements IFunctionBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function sum(string|ILiteral|IParameter|IQueryFunction $field): IQueryFunction {
|
||||
public function sum($field): IQueryFunction {
|
||||
return new QueryFunction('SUM(' . $this->helper->quoteColumnName($field) . ')');
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function lower(string|ILiteral|IParameter|IQueryFunction $field): IQueryFunction {
|
||||
public function lower($field): IQueryFunction {
|
||||
return new QueryFunction('LOWER(' . $this->helper->quoteColumnName($field) . ')');
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function add(
|
||||
string|ILiteral|IParameter|IQueryFunction $x,
|
||||
string|ILiteral|IParameter|IQueryFunction $y,
|
||||
): IQueryFunction {
|
||||
public function add($x, $y): IQueryFunction {
|
||||
return new QueryFunction($this->helper->quoteColumnName($x) . ' + ' . $this->helper->quoteColumnName($y));
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function subtract(
|
||||
string|ILiteral|IParameter|IQueryFunction $x,
|
||||
string|ILiteral|IParameter|IQueryFunction $y,
|
||||
): IQueryFunction {
|
||||
public function subtract($x, $y): IQueryFunction {
|
||||
return new QueryFunction($this->helper->quoteColumnName($x) . ' - ' . $this->helper->quoteColumnName($y));
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function count(string|ILiteral|IParameter|IQueryFunction $count = '', string $alias = ''): IQueryFunction {
|
||||
public function count($count = '', $alias = ''): IQueryFunction {
|
||||
$alias = $alias ? (' AS ' . $this->helper->quoteColumnName($alias)) : '';
|
||||
$quotedName = $count === '' ? '*' : $this->helper->quoteColumnName($count);
|
||||
return new QueryFunction('COUNT(' . $quotedName . ')' . $alias);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function octetLength(string|ILiteral|IParameter|IQueryFunction $field, string $alias = ''): IQueryFunction {
|
||||
public function octetLength($field, $alias = ''): IQueryFunction {
|
||||
$alias = $alias ? (' AS ' . $this->helper->quoteColumnName($alias)) : '';
|
||||
$quotedName = $this->helper->quoteColumnName($field);
|
||||
return new QueryFunction('OCTET_LENGTH(' . $quotedName . ')' . $alias);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function charLength(string|ILiteral|IParameter|IQueryFunction $field, string $alias = ''): IQueryFunction {
|
||||
public function charLength($field, $alias = ''): IQueryFunction {
|
||||
$alias = $alias ? (' AS ' . $this->helper->quoteColumnName($alias)) : '';
|
||||
$quotedName = $this->helper->quoteColumnName($field);
|
||||
return new QueryFunction('CHAR_LENGTH(' . $quotedName . ')' . $alias);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function max(string|ILiteral|IParameter|IQueryFunction $field): IQueryFunction {
|
||||
public function max($field): IQueryFunction {
|
||||
return new QueryFunction('MAX(' . $this->helper->quoteColumnName($field) . ')');
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function min(string|ILiteral|IParameter|IQueryFunction $field): IQueryFunction {
|
||||
public function min($field): IQueryFunction {
|
||||
return new QueryFunction('MIN(' . $this->helper->quoteColumnName($field) . ')');
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function greatest(
|
||||
string|ILiteral|IParameter|IQueryFunction $x,
|
||||
string|ILiteral|IParameter|IQueryFunction $y,
|
||||
): IQueryFunction {
|
||||
public function greatest($x, $y): IQueryFunction {
|
||||
return new QueryFunction('GREATEST(' . $this->helper->quoteColumnName($x) . ', ' . $this->helper->quoteColumnName($y) . ')');
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function least(
|
||||
string|ILiteral|IParameter|IQueryFunction $x,
|
||||
string|ILiteral|IParameter|IQueryFunction $y,
|
||||
): IQueryFunction {
|
||||
public function least($x, $y): IQueryFunction {
|
||||
return new QueryFunction('LEAST(' . $this->helper->quoteColumnName($x) . ', ' . $this->helper->quoteColumnName($y) . ')');
|
||||
}
|
||||
|
||||
|
||||
@@ -6,15 +6,17 @@
|
||||
*/
|
||||
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(string|ILiteral|IParameter|IQueryFunction $input): IQueryFunction {
|
||||
if (version_compare($this->connection->getServerVersion(), '20', '>=')) {
|
||||
public function md5($input): IQueryFunction {
|
||||
/** @var ConnectionAdapter $co */
|
||||
$co = $this->connection;
|
||||
if (version_compare($co->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) . ')))');
|
||||
@@ -28,12 +30,11 @@ 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
|
||||
*/
|
||||
#[Override]
|
||||
public function greatest(
|
||||
string|ILiteral|IParameter|IQueryFunction $x,
|
||||
string|ILiteral|IParameter|IQueryFunction $y,
|
||||
): IQueryFunction {
|
||||
public function greatest($x, $y): IQueryFunction {
|
||||
if (is_string($y) || $y instanceof IQueryFunction) {
|
||||
return parent::greatest($y, $x);
|
||||
}
|
||||
@@ -48,12 +49,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
|
||||
*/
|
||||
#[Override]
|
||||
public function least(
|
||||
string|ILiteral|IParameter|IQueryFunction $x,
|
||||
string|ILiteral|IParameter|IQueryFunction $y,
|
||||
): IQueryFunction {
|
||||
public function least($x, $y): IQueryFunction {
|
||||
if (is_string($y) || $y instanceof IQueryFunction) {
|
||||
return parent::least($y, $x);
|
||||
}
|
||||
@@ -61,8 +62,7 @@ class OCIFunctionBuilder extends FunctionBuilder {
|
||||
return parent::least($x, $y);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function concat(string|ILiteral|IParameter|IQueryFunction $x, string|ILiteral|IParameter|IQueryFunction ...$expr): IQueryFunction {
|
||||
public function concat($x, ...$expr): IQueryFunction {
|
||||
$args = func_get_args();
|
||||
$list = [];
|
||||
foreach ($args as $item) {
|
||||
@@ -71,8 +71,7 @@ class OCIFunctionBuilder extends FunctionBuilder {
|
||||
return new QueryFunction(sprintf('(%s)', implode(' || ', $list)));
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function groupConcat(string|ILiteral|IParameter|IQueryFunction $expr, ?string $separator = ','): IQueryFunction {
|
||||
public function groupConcat($expr, ?string $separator = ','): IQueryFunction {
|
||||
$orderByClause = ' WITHIN GROUP(ORDER BY NULL)';
|
||||
if (is_null($separator)) {
|
||||
return new QueryFunction('LISTAGG(' . $this->helper->quoteColumnName($expr) . ')' . $orderByClause);
|
||||
@@ -82,15 +81,13 @@ class OCIFunctionBuilder extends FunctionBuilder {
|
||||
return new QueryFunction('LISTAGG(' . $this->helper->quoteColumnName($expr) . ', ' . $separator . ')' . $orderByClause);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function octetLength(string|ILiteral|IParameter|IQueryFunction $field, string $alias = ''): IQueryFunction {
|
||||
public function octetLength($field, $alias = ''): IQueryFunction {
|
||||
$alias = $alias ? (' AS ' . $this->helper->quoteColumnName($alias)) : '';
|
||||
$quotedName = $this->helper->quoteColumnName($field);
|
||||
return new QueryFunction('COALESCE(LENGTHB(' . $quotedName . '), 0)' . $alias);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function charLength(string|ILiteral|IParameter|IQueryFunction $field, string $alias = ''): IQueryFunction {
|
||||
public function charLength($field, $alias = ''): IQueryFunction {
|
||||
$alias = $alias ? (' AS ' . $this->helper->quoteColumnName($alias)) : '';
|
||||
$quotedName = $this->helper->quoteColumnName($field);
|
||||
return new QueryFunction('COALESCE(LENGTH(' . $quotedName . '), 0)' . $alias);
|
||||
|
||||
@@ -9,10 +9,8 @@ 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 = [];
|
||||
@@ -22,7 +20,6 @@ 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,10 +7,7 @@
|
||||
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 {
|
||||
@@ -27,19 +24,11 @@ class SqliteFunctionBuilder extends FunctionBuilder {
|
||||
return new QueryFunction('GROUP_CONCAT(' . $this->helper->quoteColumnName($expr) . ', ' . $separator . ')');
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function greatest(
|
||||
string|ILiteral|IParameter|IQueryFunction $x,
|
||||
string|ILiteral|IParameter|IQueryFunction $y,
|
||||
): IQueryFunction {
|
||||
public function greatest($x, $y): IQueryFunction {
|
||||
return new QueryFunction('MAX(' . $this->helper->quoteColumnName($x) . ', ' . $this->helper->quoteColumnName($y) . ')');
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function least(
|
||||
string|ILiteral|IParameter|IQueryFunction $x,
|
||||
string|ILiteral|IParameter|IQueryFunction $y,
|
||||
): IQueryFunction {
|
||||
public function least($x, $y): IQueryFunction {
|
||||
return new QueryFunction('MIN(' . $this->helper->quoteColumnName($x) . ', ' . $this->helper->quoteColumnName($y) . ')');
|
||||
}
|
||||
|
||||
|
||||
@@ -12,8 +12,11 @@ namespace OC\DB\QueryBuilder;
|
||||
use OCP\DB\QueryBuilder\ILiteral;
|
||||
|
||||
class Literal implements ILiteral {
|
||||
/**
|
||||
* @param mixed $literal
|
||||
*/
|
||||
public function __construct(
|
||||
protected readonly mixed $literal,
|
||||
protected $literal,
|
||||
) {
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,6 @@ 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;
|
||||
|
||||
/**
|
||||
@@ -67,9 +66,14 @@ class JoinCondition {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param null|string|CompositeExpression $condition
|
||||
* @param string $join
|
||||
* @param string $alias
|
||||
* @param string $fromAlias
|
||||
* @return JoinCondition
|
||||
* @throws InvalidPartitionedQueryException
|
||||
*/
|
||||
public static function parse(null|string|ICompositeExpression $condition, string $join, string $alias, string $fromAlias): JoinCondition {
|
||||
public static function parse($condition, string $join, string $alias, string $fromAlias): JoinCondition {
|
||||
if ($condition === null) {
|
||||
throw new InvalidPartitionedQueryException("Can't join on $join without a condition");
|
||||
}
|
||||
@@ -81,7 +85,7 @@ class JoinCondition {
|
||||
return $result;
|
||||
}
|
||||
|
||||
private static function parseSubCondition(string|ICompositeExpression $condition, string $join, string $alias, string $fromAlias): JoinCondition {
|
||||
private static function parseSubCondition($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,13 +14,9 @@ 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].
|
||||
@@ -45,7 +41,7 @@ class PartitionedQueryBuilder extends ShardedQueryBuilder {
|
||||
/** @var list<PartitionSplit> */
|
||||
private array $partitions = [];
|
||||
|
||||
/** @var array{'select': string|IQueryFunction|IParameter|ILiteral, 'alias': ?string}[] */
|
||||
/** @var array{'select': string|array, 'alias': ?string}[] */
|
||||
private array $selects = [];
|
||||
private ?PartitionSplit $mainPartition = null;
|
||||
private bool $hasPositionalParameter = false;
|
||||
@@ -79,7 +75,7 @@ class PartitionedQueryBuilder extends ShardedQueryBuilder {
|
||||
}
|
||||
|
||||
// we need to save selects until we know all the table aliases
|
||||
public function select(...$selects): self {
|
||||
public function select(...$selects) {
|
||||
if (count($selects) === 1 && is_array($selects[0])) {
|
||||
$selects = $selects[0];
|
||||
}
|
||||
@@ -88,13 +84,15 @@ class PartitionedQueryBuilder extends ShardedQueryBuilder {
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function addSelect(...$select): self {
|
||||
$select = array_map(static fn ($select) => ['select' => $select, 'alias' => null], $select);
|
||||
public function addSelect(...$select) {
|
||||
$select = array_map(function ($select) {
|
||||
return ['select' => $select, 'alias' => null];
|
||||
}, $select);
|
||||
$this->selects = array_merge($this->selects, $select);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function selectAlias(string|IQueryFunction|IParameter|ILiteral $select, string $alias): self {
|
||||
public function selectAlias($select, $alias): self {
|
||||
$this->selects[] = ['select' => $select, 'alias' => $alias];
|
||||
return $this;
|
||||
}
|
||||
@@ -103,6 +101,9 @@ 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;
|
||||
@@ -189,30 +190,25 @@ class PartitionedQueryBuilder extends ShardedQueryBuilder {
|
||||
return null;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function from($from, $alias = null): self {
|
||||
public function from($from, $alias = null) {
|
||||
if (is_string($from) && $partition = $this->getPartition($from)) {
|
||||
$this->mainPartition = $partition;
|
||||
if ($alias) {
|
||||
$this->mainPartition->addAlias($from, $alias);
|
||||
}
|
||||
}
|
||||
parent::from($from, $alias);
|
||||
return $this;
|
||||
return parent::from($from, $alias);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function innerJoin(string $fromAlias, string|IQueryFunction $join, ?string $alias, string|ICompositeExpression|null $condition = null): self {
|
||||
public function innerJoin($fromAlias, $join, $alias, $condition = null): self {
|
||||
return $this->join($fromAlias, $join, $alias, $condition);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function leftJoin(string $fromAlias, string|IQueryFunction $join, ?string $alias, string|ICompositeExpression|null $condition = null): self {
|
||||
public function leftJoin($fromAlias, $join, $alias, $condition = null): self {
|
||||
return $this->join($fromAlias, $join, $alias, $condition, PartitionQuery::JOIN_MODE_LEFT);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function join(string $fromAlias, string|IQueryFunction $join, ?string $alias, string|ICompositeExpression|null $condition = null, string $joinMode = PartitionQuery::JOIN_MODE_INNER): self {
|
||||
public function join($fromAlias, $join, $alias, $condition = null, $joinMode = PartitionQuery::JOIN_MODE_INNER): self {
|
||||
if ($join instanceof IQueryFunction) {
|
||||
$partition = null;
|
||||
$fromPartition = null;
|
||||
@@ -225,7 +221,7 @@ class PartitionedQueryBuilder extends ShardedQueryBuilder {
|
||||
/** @var string $join */
|
||||
// join from the main db to a partition
|
||||
|
||||
$joinCondition = JoinCondition::parse((string)$condition, $join, $alias, $fromAlias);
|
||||
$joinCondition = JoinCondition::parse($condition, $join, $alias, $fromAlias);
|
||||
$partition->addAlias($join, $alias);
|
||||
|
||||
if (!isset($this->splitQueries[$partition->name])) {
|
||||
@@ -252,7 +248,7 @@ class PartitionedQueryBuilder extends ShardedQueryBuilder {
|
||||
/** @var string $join */
|
||||
// join from partition, to the main db
|
||||
|
||||
$joinCondition = JoinCondition::parse((string)$condition, $join, $alias, $fromAlias);
|
||||
$joinCondition = JoinCondition::parse($condition, $join, $alias, $fromAlias);
|
||||
if (str_starts_with($fromPartition->name, 'from_')) {
|
||||
$partitionName = $fromPartition->name;
|
||||
} else {
|
||||
@@ -285,14 +281,11 @@ class PartitionedQueryBuilder extends ShardedQueryBuilder {
|
||||
} else {
|
||||
// join within the main db or a partition
|
||||
if ($joinMode === PartitionQuery::JOIN_MODE_INNER) {
|
||||
parent::innerJoin($fromAlias, $join, $alias, $condition);
|
||||
return $this;
|
||||
return parent::innerJoin($fromAlias, $join, $alias, $condition);
|
||||
} elseif ($joinMode === PartitionQuery::JOIN_MODE_LEFT) {
|
||||
parent::leftJoin($fromAlias, $join, $alias, $condition);
|
||||
return $this;
|
||||
return parent::leftJoin($fromAlias, $join, $alias, $condition);
|
||||
} elseif ($joinMode === PartitionQuery::JOIN_MODE_RIGHT) {
|
||||
parent::rightJoin($fromAlias, $join, $alias, $condition);
|
||||
return $this;
|
||||
return parent::rightJoin($fromAlias, $join, $alias, $condition);
|
||||
} else {
|
||||
throw new \InvalidArgumentException("Invalid join mode: $joinMode");
|
||||
}
|
||||
@@ -340,11 +333,11 @@ class PartitionedQueryBuilder extends ShardedQueryBuilder {
|
||||
return $partitionPredicates;
|
||||
}
|
||||
|
||||
public function where(...$predicates): self {
|
||||
public function where(...$predicates) {
|
||||
return $this->andWhere(...$predicates);
|
||||
}
|
||||
|
||||
public function andWhere(...$where): self {
|
||||
public function andWhere(...$where) {
|
||||
if ($where) {
|
||||
foreach ($this->splitPredicatesByParts($where) as $alias => $predicates) {
|
||||
if (isset($this->splitQueries[$alias])) {
|
||||
@@ -387,35 +380,30 @@ class PartitionedQueryBuilder extends ShardedQueryBuilder {
|
||||
return null;
|
||||
}
|
||||
|
||||
public function update(string $update, ?string $alias = null): self {
|
||||
parent::update($update, $alias);
|
||||
return $this;
|
||||
public function update($update = null, $alias = null) {
|
||||
return parent::update($update, $alias);
|
||||
}
|
||||
|
||||
public function insert(string $insert): self {
|
||||
parent::insert($insert);
|
||||
return $this;
|
||||
public function insert($insert = null) {
|
||||
return parent::insert($insert);
|
||||
}
|
||||
|
||||
public function delete(string $delete, ?string $alias = null): self {
|
||||
parent::delete($delete, $alias);
|
||||
return $this;
|
||||
public function delete($delete = null, $alias = null) {
|
||||
return parent::delete($delete, $alias);
|
||||
}
|
||||
|
||||
public function setMaxResults(?int $maxResults): self {
|
||||
if ($maxResults !== null && $maxResults > 0) {
|
||||
$this->limit = $maxResults;
|
||||
public function setMaxResults($maxResults) {
|
||||
if ($maxResults > 0) {
|
||||
$this->limit = (int)$maxResults;
|
||||
}
|
||||
parent::setMaxResults($maxResults);
|
||||
return $this;
|
||||
return parent::setMaxResults($maxResults);
|
||||
}
|
||||
|
||||
public function setFirstResult(int $firstResult): self {
|
||||
public function setFirstResult($firstResult) {
|
||||
if ($firstResult > 0) {
|
||||
$this->offset = $firstResult;
|
||||
$this->offset = (int)$firstResult;
|
||||
}
|
||||
parent::setFirstResult($firstResult);
|
||||
return $this;
|
||||
return parent::setFirstResult($firstResult);
|
||||
}
|
||||
|
||||
public function executeQuery(?IDBConnection $connection = null): IResult {
|
||||
@@ -456,7 +444,7 @@ class PartitionedQueryBuilder extends ShardedQueryBuilder {
|
||||
return parent::executeStatement($connection);
|
||||
}
|
||||
|
||||
public function getSQL(): string {
|
||||
public function getSQL() {
|
||||
$this->applySelects();
|
||||
return parent::getSQL();
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -12,7 +12,11 @@ use OCP\DB\QueryBuilder\IParameter;
|
||||
use OCP\DB\QueryBuilder\IQueryFunction;
|
||||
|
||||
class QuoteHelper {
|
||||
public function quoteColumnNames(array|string|ILiteral|IParameter|IQueryFunction $strings): array|string {
|
||||
/**
|
||||
* @param array|string|ILiteral|IParameter|IQueryFunction $strings string, Literal or Parameter
|
||||
* @return array|string
|
||||
*/
|
||||
public function quoteColumnNames($strings) {
|
||||
if (!is_array($strings)) {
|
||||
return $this->quoteColumnName($strings);
|
||||
}
|
||||
@@ -25,18 +29,26 @@ class QuoteHelper {
|
||||
return $return;
|
||||
}
|
||||
|
||||
public function quoteColumnName(string|ILiteral|IParameter|IQueryFunction $string): string {
|
||||
/**
|
||||
* @param string|ILiteral|IParameter|IQueryFunction $string string, Literal or Parameter
|
||||
* @return string
|
||||
*/
|
||||
public function quoteColumnName($string) {
|
||||
if ($string instanceof IParameter || $string instanceof ILiteral || $string instanceof IQueryFunction) {
|
||||
return (string)$string;
|
||||
}
|
||||
|
||||
if ($string === 'null' || $string === '*') {
|
||||
if ($string === null || $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,13 +12,8 @@ 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.
|
||||
@@ -88,13 +83,11 @@ class ShardedQueryBuilder extends ExtendedQueryBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function where(...$predicates): self {
|
||||
public function where(...$predicates) {
|
||||
return $this->andWhere(...$predicates);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function andWhere(...$where): self {
|
||||
public function andWhere(...$where) {
|
||||
if ($where) {
|
||||
foreach ($where as $predicate) {
|
||||
$this->tryLoadShardKey($predicate);
|
||||
@@ -165,18 +158,14 @@ class ShardedQueryBuilder extends ExtendedQueryBuilder {
|
||||
return [];
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function set($key, $value): self {
|
||||
public function set($key, $value) {
|
||||
if ($this->shardDefinition && $key === $this->shardDefinition->shardKey) {
|
||||
// TODO dead code?
|
||||
$updateShardKey = $value;
|
||||
}
|
||||
parent::set($key, $value);
|
||||
return $this;
|
||||
return parent::set($key, $value);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function setValue(string $column, ILiteral|IParameter|IQueryFunction|string $value): self {
|
||||
public function setValue($column, $value) {
|
||||
if ($this->shardDefinition) {
|
||||
if ($this->shardDefinition->isKey($column)) {
|
||||
$this->primaryKeys[] = $value;
|
||||
@@ -185,12 +174,10 @@ class ShardedQueryBuilder extends ExtendedQueryBuilder {
|
||||
$this->shardKeys[] = $value;
|
||||
}
|
||||
}
|
||||
parent::setValue($column, $value);
|
||||
return $this;
|
||||
return parent::setValue($column, $value);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function values(array $values): self {
|
||||
public function values(array $values) {
|
||||
foreach ($values as $column => $value) {
|
||||
$this->setValue($column, $value);
|
||||
}
|
||||
@@ -206,35 +193,33 @@ class ShardedQueryBuilder extends ExtendedQueryBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function from(string|IQueryFunction $from, ?string $alias = null): self {
|
||||
if (is_string($from)) {
|
||||
public function from($from, $alias = null) {
|
||||
if (is_string($from) && $from) {
|
||||
$this->actOnTable($from);
|
||||
}
|
||||
parent::from($from, $alias);
|
||||
return $this;
|
||||
return parent::from($from, $alias);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function update(string $update, ?string $alias = null): self {
|
||||
$this->actOnTable($update);
|
||||
parent::update($update, $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 insert(string $insert): self {
|
||||
$this->insertTable = $insert;
|
||||
$this->actOnTable($insert);
|
||||
parent::insert($insert);
|
||||
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 delete(string $delete, ?string $alias = null): self {
|
||||
$this->actOnTable($delete);
|
||||
parent::delete($delete, $alias);
|
||||
return $this;
|
||||
public function delete($delete = null, $alias = null) {
|
||||
if (is_string($delete) && $delete) {
|
||||
$this->actOnTable($delete);
|
||||
}
|
||||
return parent::delete($delete, $alias);
|
||||
}
|
||||
|
||||
private function checkJoin(string $table): void {
|
||||
@@ -250,83 +235,67 @@ class ShardedQueryBuilder extends ExtendedQueryBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function innerJoin(string $fromAlias, string|IQueryFunction $join, ?string $alias, string|ICompositeExpression|null $condition = null): self {
|
||||
public function innerJoin($fromAlias, $join, $alias, $condition = null) {
|
||||
if (is_string($join)) {
|
||||
$this->checkJoin($join);
|
||||
}
|
||||
parent::innerJoin($fromAlias, $join, $alias, $condition);
|
||||
return $this;
|
||||
return parent::innerJoin($fromAlias, $join, $alias, $condition);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function leftJoin(string $fromAlias, string|IQueryFunction $join, ?string $alias, string|ICompositeExpression|null $condition = null): self {
|
||||
public function leftJoin($fromAlias, $join, $alias, $condition = null) {
|
||||
if (is_string($join)) {
|
||||
$this->checkJoin($join);
|
||||
}
|
||||
parent::leftJoin($fromAlias, $join, $alias, $condition);
|
||||
return $this;
|
||||
return parent::leftJoin($fromAlias, $join, $alias, $condition);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function rightJoin(string $fromAlias, string|IQueryFunction $join, ?string $alias, string|ICompositeExpression|null $condition = null): self {
|
||||
public function rightJoin($fromAlias, $join, $alias, $condition = null) {
|
||||
if ($this->shardDefinition) {
|
||||
throw new InvalidShardedQueryException("Sharded query on {$this->shardDefinition->table} isn't allowed to right join");
|
||||
}
|
||||
parent::rightJoin($fromAlias, $join, $alias, $condition);
|
||||
return $this;
|
||||
return parent::rightJoin($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 join($fromAlias, $join, $alias, $condition = null) {
|
||||
return $this->innerJoin($fromAlias, $join, $alias, $condition);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function setMaxResults(?int $maxResults): self {
|
||||
if ($maxResults !== null && $maxResults > 0) {
|
||||
$this->limit = $maxResults;
|
||||
public function setMaxResults($maxResults) {
|
||||
if ($maxResults > 0) {
|
||||
$this->limit = (int)$maxResults;
|
||||
}
|
||||
parent::setMaxResults($maxResults);
|
||||
return $this;
|
||||
return parent::setMaxResults($maxResults);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function setFirstResult(int $firstResult): self {
|
||||
public function setFirstResult($firstResult) {
|
||||
if ($firstResult > 0) {
|
||||
$this->offset = $firstResult;
|
||||
$this->offset = (int)$firstResult;
|
||||
}
|
||||
if ($this->shardDefinition && count($this->shardDefinition->shards) > 1) {
|
||||
// we have to emulate offset
|
||||
return $this;
|
||||
} else {
|
||||
parent::setFirstResult($firstResult);
|
||||
return $this;
|
||||
return parent::setFirstResult($firstResult);
|
||||
}
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function addOrderBy($sort, $order = null): self {
|
||||
public function addOrderBy($sort, $order = null) {
|
||||
if ($order !== null && !in_array(strtoupper((string)$order), ['ASC', 'DESC'], true)) {
|
||||
$order = null;
|
||||
}
|
||||
|
||||
$this->registerOrder((string)$sort, (string)($order ?? 'ASC'));
|
||||
parent::addOrderBy($sort, $order);
|
||||
return $this;
|
||||
return parent::addOrderBy($sort, $order);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function orderBy($sort, $order = null): self {
|
||||
public function orderBy($sort, $order = null) {
|
||||
if ($order !== null && !in_array(strtoupper((string)$order), ['ASC', 'DESC'], true)) {
|
||||
$order = null;
|
||||
}
|
||||
|
||||
$this->sortList = [];
|
||||
$this->registerOrder((string)$sort, (string)($order ?? 'ASC'));
|
||||
parent::orderBy($sort, $order);
|
||||
return $this;
|
||||
return parent::orderBy($sort, $order);
|
||||
}
|
||||
|
||||
private function registerOrder(string $column, string $order): void {
|
||||
@@ -339,7 +308,6 @@ class ShardedQueryBuilder extends ExtendedQueryBuilder {
|
||||
];
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function hintShardKey(string $column, mixed $value, bool $overwrite = false): self {
|
||||
if ($overwrite) {
|
||||
$this->primaryKeys = [];
|
||||
@@ -354,7 +322,6 @@ class ShardedQueryBuilder extends ExtendedQueryBuilder {
|
||||
return $this;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function runAcrossAllShards(): self {
|
||||
$this->allShards = true;
|
||||
return $this;
|
||||
@@ -397,7 +364,6 @@ class ShardedQueryBuilder extends ExtendedQueryBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function executeQuery(?IDBConnection $connection = null): IResult {
|
||||
$this->validate();
|
||||
if ($this->shardDefinition) {
|
||||
@@ -407,7 +373,6 @@ class ShardedQueryBuilder extends ExtendedQueryBuilder {
|
||||
return parent::executeQuery($connection);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function executeStatement(?IDBConnection $connection = null): int {
|
||||
$this->validate();
|
||||
if ($this->shardDefinition) {
|
||||
@@ -438,7 +403,6 @@ class ShardedQueryBuilder extends ExtendedQueryBuilder {
|
||||
return parent::executeStatement($connection);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function getLastInsertId(): int {
|
||||
if ($this->lastInsertId) {
|
||||
return $this->lastInsertId;
|
||||
@@ -450,4 +414,6 @@ class ShardedQueryBuilder extends ExtendedQueryBuilder {
|
||||
return parent::getLastInsertId();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -27,9 +27,7 @@ abstract class TypedQueryBuilder implements ITypedQueryBuilder {
|
||||
$this->validateColumn($column);
|
||||
}
|
||||
|
||||
/** @psalm-suppress InternalMethod */
|
||||
$this->select(...$columns);
|
||||
return $this;
|
||||
return $this->select(...$columns);
|
||||
}
|
||||
|
||||
public function selectColumnsDistinct(string ...$columns): static {
|
||||
@@ -37,8 +35,6 @@ abstract class TypedQueryBuilder implements ITypedQueryBuilder {
|
||||
$this->validateColumn($column);
|
||||
}
|
||||
|
||||
/** @psalm-suppress InternalMethod */
|
||||
$this->selectDistinct($columns);
|
||||
return $this;
|
||||
return $this->selectDistinct($columns);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ class CacheQueryBuilder extends ExtendedQueryBuilder {
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function selectFileCache(?string $alias = null, bool $joinExtendedCache = true): self {
|
||||
public function selectFileCache(?string $alias = null, bool $joinExtendedCache = true) {
|
||||
$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): self {
|
||||
public function whereStorageId(int $storageId) {
|
||||
$this->andWhere($this->expr()->eq('storage', $this->createNamedParameter($storageId, IQueryBuilder::PARAM_INT)));
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function whereFileId(int $fileId): self {
|
||||
public function whereFileId(int $fileId) {
|
||||
$alias = $this->alias;
|
||||
if ($alias) {
|
||||
$alias .= '.';
|
||||
@@ -86,7 +86,7 @@ class CacheQueryBuilder extends ExtendedQueryBuilder {
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function whereParent(int $parent): self {
|
||||
public function whereParent(int $parent) {
|
||||
$alias = $this->alias;
|
||||
if ($alias) {
|
||||
$alias .= '.';
|
||||
@@ -99,7 +99,7 @@ class CacheQueryBuilder extends ExtendedQueryBuilder {
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function whereParentInParameter(string $parameter): self {
|
||||
public function whereParentInParameter(string $parameter) {
|
||||
$alias = $this->alias;
|
||||
if ($alias) {
|
||||
$alias .= '.';
|
||||
|
||||
@@ -800,14 +800,24 @@ class ObjectStoreStorage extends Common implements IChunkedFileWrite {
|
||||
$this->getCache()->update($stat['fileid'], $stat);
|
||||
}
|
||||
} catch (S3MultipartUploadException|S3Exception $e) {
|
||||
$this->objectStore->abortMultipartUpload($urn, $writeToken);
|
||||
$this->logger->error(
|
||||
'Could not complete multipart upload ' . $urn . ' with uploadId ' . $writeToken,
|
||||
'Unable to complete multipart upload for "' . $urn . '" (uploadId: "' . $writeToken . '")',
|
||||
[
|
||||
'app' => 'objectstore',
|
||||
'exception' => $e,
|
||||
]
|
||||
);
|
||||
try {
|
||||
$this->objectStore->abortMultipartUpload($urn, $writeToken);
|
||||
} catch (S3Exception $e) {
|
||||
$this->logger->error(
|
||||
'Unable to abort multipart upload for "' . $urn . '" (uploadId: "' . $writeToken . '") after completion error',
|
||||
[
|
||||
'app' => 'objectstore',
|
||||
'exception' => $e,
|
||||
]
|
||||
);
|
||||
}
|
||||
throw new GenericFileException('Could not write chunked file');
|
||||
}
|
||||
return $size;
|
||||
|
||||
@@ -145,7 +145,7 @@ class Factory implements IFactory {
|
||||
if ($lang === null) {
|
||||
return null;
|
||||
}
|
||||
$lang = preg_replace('/[^a-zA-Z0-9.;,=-]/', '', $lang);
|
||||
$lang = preg_replace('/[^a-zA-Z0-9.;,=_-]/', '', $lang);
|
||||
return str_replace('..', '', $lang);
|
||||
}
|
||||
|
||||
|
||||
@@ -199,14 +199,19 @@ class Repair implements IOutput {
|
||||
* @return list<IRepairStep>
|
||||
*/
|
||||
public static function getExpensiveRepairSteps(): array {
|
||||
return [
|
||||
$expensiveSteps = [
|
||||
Server::get(OldGroupMembershipShares::class),
|
||||
Server::get(RemoveBrokenProperties::class),
|
||||
Server::get(RepairMimeTypes::class),
|
||||
Server::get(DeleteSchedulingObjects::class),
|
||||
Server::get(RemoveObjectProperties::class),
|
||||
Server::get(CleanupShareTarget::class),
|
||||
];
|
||||
|
||||
if (class_exists(CleanupShareTarget::class)) {
|
||||
$expensiveSteps[] = Server::get(CleanupShareTarget::class);
|
||||
}
|
||||
|
||||
return $expensiveSteps;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -968,7 +968,7 @@ class DefaultShareProvider implements
|
||||
->setFirstResult(0);
|
||||
|
||||
if ($limit !== -1) {
|
||||
$qb->setMaxResults(max($limit - count($shares), 1));
|
||||
$qb->setMaxResults($limit - count($shares));
|
||||
}
|
||||
|
||||
// Filter by node if provided
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
@@ -12,28 +14,27 @@ use OCP\Files\NotFoundException;
|
||||
use OCP\Files\NotPermittedException;
|
||||
use OCP\Share\IManager;
|
||||
use OCP\Share\IShareHelper;
|
||||
use Override;
|
||||
|
||||
class ShareHelper implements IShareHelper {
|
||||
public function __construct(
|
||||
private IManager $shareManager,
|
||||
private readonly IManager $shareManager,
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Node $node
|
||||
* @return array [ users => [Mapping $uid => $pathForUser], remotes => [Mapping $cloudId => $pathToMountRoot]]
|
||||
*/
|
||||
public function getPathsForAccessList(Node $node) {
|
||||
#[Override]
|
||||
public function getPathsForAccessList(Node $node): array {
|
||||
$result = [
|
||||
'users' => [],
|
||||
'remotes' => [],
|
||||
];
|
||||
|
||||
$accessList = $this->shareManager->getAccessList($node, true, true);
|
||||
if (!empty($accessList['users'])) {
|
||||
if (isset($accessList['users']) && $accessList['users'] !== []) {
|
||||
$result['users'] = $this->getPathsForUsers($node, $accessList['users']);
|
||||
}
|
||||
if (!empty($accessList['remote'])) {
|
||||
|
||||
if (isset($accessList['remote']) && $accessList['remote'] !== []) {
|
||||
$result['remotes'] = $this->getPathsForRemotes($node, $accessList['remote']);
|
||||
}
|
||||
|
||||
@@ -60,20 +61,20 @@ class ShareHelper implements IShareHelper {
|
||||
* 'test3' => '/cat',
|
||||
* ],
|
||||
*
|
||||
* @param Node $node
|
||||
* @param array[] $users
|
||||
* @return array
|
||||
* @param non-empty-array<string, array{node_id: int, node_path: string}> $users
|
||||
* @return array<string, string>
|
||||
*/
|
||||
protected function getPathsForUsers(Node $node, array $users) {
|
||||
/** @var array[] $byId */
|
||||
protected function getPathsForUsers(Node $node, array $users): array {
|
||||
/** @var array<int, array<string, string>> $byId */
|
||||
$byId = [];
|
||||
/** @var array[] $results */
|
||||
/** @var array<string, string> $results */
|
||||
$results = [];
|
||||
|
||||
foreach ($users as $uid => $info) {
|
||||
if (!isset($byId[$info['node_id']])) {
|
||||
$byId[$info['node_id']] = [];
|
||||
}
|
||||
|
||||
$byId[$info['node_id']][$uid] = $info['node_path'];
|
||||
}
|
||||
|
||||
@@ -82,15 +83,14 @@ class ShareHelper implements IShareHelper {
|
||||
foreach ($byId[$node->getId()] as $uid => $path) {
|
||||
$results[$uid] = $path;
|
||||
}
|
||||
|
||||
unset($byId[$node->getId()]);
|
||||
}
|
||||
} catch (NotFoundException $e) {
|
||||
return $results;
|
||||
} catch (InvalidPathException $e) {
|
||||
} catch (NotFoundException|InvalidPathException) {
|
||||
return $results;
|
||||
}
|
||||
|
||||
if (empty($byId)) {
|
||||
if ($byId === []) {
|
||||
return $results;
|
||||
}
|
||||
|
||||
@@ -98,22 +98,18 @@ class ShareHelper implements IShareHelper {
|
||||
$appendix = '/' . $node->getName();
|
||||
while (!empty($byId)) {
|
||||
try {
|
||||
/** @var Node $item */
|
||||
$item = $item->getParent();
|
||||
|
||||
if (!empty($byId[$item->getId()])) {
|
||||
if ($byId[$item->getId()] !== []) {
|
||||
foreach ($byId[$item->getId()] as $uid => $path) {
|
||||
$results[$uid] = $path . $appendix;
|
||||
}
|
||||
|
||||
unset($byId[$item->getId()]);
|
||||
}
|
||||
|
||||
$appendix = '/' . $item->getName() . $appendix;
|
||||
} catch (NotFoundException $e) {
|
||||
return $results;
|
||||
} catch (InvalidPathException $e) {
|
||||
return $results;
|
||||
} catch (NotPermittedException $e) {
|
||||
} catch (NotFoundException|InvalidPathException|NotPermittedException) {
|
||||
return $results;
|
||||
}
|
||||
}
|
||||
@@ -141,27 +137,27 @@ class ShareHelper implements IShareHelper {
|
||||
* 'test3' => ['token' => 't3', 'node_path' => '/SixTeen/TwentyThree/FortyTwo'],
|
||||
* ],
|
||||
*
|
||||
* @param Node $node
|
||||
* @param array[] $remotes
|
||||
* @return array
|
||||
* @param non-empty-array<string, array{node_id: int, token: string}> $remotes
|
||||
* @return array<string, array{token: string, node_path: string}>
|
||||
*/
|
||||
protected function getPathsForRemotes(Node $node, array $remotes) {
|
||||
/** @var array[] $byId */
|
||||
protected function getPathsForRemotes(Node $node, array $remotes): array {
|
||||
/** @var array<int, array<string, string>> $byId */
|
||||
$byId = [];
|
||||
/** @var array[] $results */
|
||||
/** @var array<string, array{token: string, node_path: string}> $results */
|
||||
$results = [];
|
||||
|
||||
foreach ($remotes as $cloudId => $info) {
|
||||
if (!isset($byId[$info['node_id']])) {
|
||||
$byId[$info['node_id']] = [];
|
||||
}
|
||||
|
||||
$byId[$info['node_id']][$cloudId] = $info['token'];
|
||||
}
|
||||
|
||||
$item = $node;
|
||||
while (!empty($byId)) {
|
||||
try {
|
||||
if (!empty($byId[$item->getId()])) {
|
||||
if ($byId[$item->getId()] !== []) {
|
||||
$path = $this->getMountedPath($item);
|
||||
foreach ($byId[$item->getId()] as $uid => $token) {
|
||||
$results[$uid] = [
|
||||
@@ -169,16 +165,12 @@ class ShareHelper implements IShareHelper {
|
||||
'token' => $token,
|
||||
];
|
||||
}
|
||||
|
||||
unset($byId[$item->getId()]);
|
||||
}
|
||||
|
||||
/** @var Node $item */
|
||||
$item = $item->getParent();
|
||||
} catch (NotFoundException $e) {
|
||||
return $results;
|
||||
} catch (InvalidPathException $e) {
|
||||
return $results;
|
||||
} catch (NotPermittedException $e) {
|
||||
} catch (NotFoundException|InvalidPathException|NotPermittedException) {
|
||||
return $results;
|
||||
}
|
||||
}
|
||||
@@ -186,11 +178,7 @@ class ShareHelper implements IShareHelper {
|
||||
return $results;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Node $node
|
||||
* @return string
|
||||
*/
|
||||
protected function getMountedPath(Node $node) {
|
||||
protected function getMountedPath(Node $node): string {
|
||||
$path = $node->getPath();
|
||||
$sections = explode('/', $path, 4);
|
||||
return '/' . $sections[3];
|
||||
|
||||
@@ -285,7 +285,7 @@ class Database extends ABackend implements
|
||||
->orderBy($query->func()->lower('displayname'), 'ASC')
|
||||
->addOrderBy('uid_lower', 'ASC')
|
||||
->setMaxResults($limit)
|
||||
->setFirstResult($offset ?? 0);
|
||||
->setFirstResult($offset);
|
||||
|
||||
$result = $query->executeQuery();
|
||||
$displayNames = [];
|
||||
|
||||
@@ -36,7 +36,6 @@ use OCP\User\Events\BeforeUserCreatedEvent;
|
||||
use OCP\User\Events\UserCreatedEvent;
|
||||
use OCP\UserInterface;
|
||||
use OCP\Util;
|
||||
use Override;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
/**
|
||||
@@ -729,10 +728,16 @@ class Manager extends PublicEmitter implements IUserManager {
|
||||
}
|
||||
}
|
||||
|
||||
#[Override]
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
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,10 +47,4 @@ interface ICompositeExpression {
|
||||
* @since 8.2.0
|
||||
*/
|
||||
public function getType(): string;
|
||||
|
||||
/**
|
||||
* Case the composite expression to string.
|
||||
* @since 34.0.0
|
||||
*/
|
||||
public function __toString(): string;
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
namespace OCP\DB\QueryBuilder;
|
||||
|
||||
use Doctrine\DBAL\Query\Expression\ExpressionBuilder;
|
||||
use OCP\AppFramework\Attribute\Consumable;
|
||||
|
||||
/**
|
||||
* This class provides a wrapper around Doctrine's ExpressionBuilder
|
||||
@@ -16,7 +15,6 @@ use OCP\AppFramework\Attribute\Consumable;
|
||||
*
|
||||
* @psalm-taint-specialize
|
||||
*/
|
||||
#[Consumable(since: '8.2.0')]
|
||||
interface IExpressionBuilder {
|
||||
/**
|
||||
* @since 9.0.0
|
||||
@@ -52,15 +50,16 @@ interface IExpressionBuilder {
|
||||
* // (u.type = ?) AND (u.role = ?)
|
||||
* $expr->andX('u.type = ?', 'u.role = ?'));
|
||||
*
|
||||
* @param ICompositeExpression|string ...$x Optional clause. Defaults = null, but requires
|
||||
* at least one defined when converting to string.
|
||||
* @param mixed ...$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(ICompositeExpression|string ...$x): ICompositeExpression;
|
||||
public function andX(...$x): ICompositeExpression;
|
||||
|
||||
/**
|
||||
* Creates a disjunction of the given boolean expressions.
|
||||
@@ -71,24 +70,25 @@ interface IExpressionBuilder {
|
||||
* // (u.type = ?) OR (u.role = ?)
|
||||
* $qb->where($qb->expr()->orX('u.type = ?', 'u.role = ?'));
|
||||
*
|
||||
* @param ICompositeExpression|string ...$x Optional clause. Defaults = null, but requires
|
||||
* at least one defined when converting to string.
|
||||
* @param mixed ...$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(ICompositeExpression|string ...$x): ICompositeExpression;
|
||||
public function orX(...$x): ICompositeExpression;
|
||||
|
||||
/**
|
||||
* Creates a comparison expression.
|
||||
*
|
||||
* @param string|ILiteral|IQueryFunction|IParameter $x The left expression.
|
||||
* @param mixed $x The left expression.
|
||||
* @param string $operator One of the IExpressionBuilder::* constants.
|
||||
* @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
|
||||
* @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
|
||||
* @since 8.2.0 - Parameter $type was added in 9.0.0
|
||||
@@ -98,44 +98,45 @@ interface IExpressionBuilder {
|
||||
* @psalm-taint-sink sql $y
|
||||
* @psalm-taint-sink sql $type
|
||||
*/
|
||||
public function comparison(string|ILiteral|IQueryFunction|IParameter $x, string $operator, string|ILiteral|IQueryFunction|IParameter $y, string|int|null $type = null): string;
|
||||
public function comparison($x, string $operator, $y, $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 generate a <left expr> = <right expr>. Example:
|
||||
* When converted to string, it will generated a <left expr> = <right expr>. Example:
|
||||
*
|
||||
* [php]
|
||||
* // u.id = ?
|
||||
* $expr->eq('u.id', '?');
|
||||
*
|
||||
* @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
|
||||
* @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
|
||||
* @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(string|ILiteral|IQueryFunction|IParameter $x, string|ILiteral|IQueryFunction|IParameter $y, string|int|null $type = null): string;
|
||||
public function eq($x, $y, $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 generate a <left expr> <> <right expr>. Example:
|
||||
* 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 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
|
||||
* @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
|
||||
* @since 8.2.0 - Parameter $type was added in 9.0.0
|
||||
@@ -144,21 +145,21 @@ interface IExpressionBuilder {
|
||||
* @psalm-taint-sink sql $y
|
||||
* @psalm-taint-sink sql $type
|
||||
*/
|
||||
public function neq(string|ILiteral|IQueryFunction|IParameter $x, string|ILiteral|IQueryFunction|IParameter $y, string|int|null $type = null): string;
|
||||
public function neq($x, $y, $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 generate a <left expr> < <right expr>. Example:
|
||||
* When converted to string, it will generated a <left expr> < <right expr>. Example:
|
||||
*
|
||||
* [php]
|
||||
* // u.id < ?
|
||||
* $q->where($q->expr()->lt('u.id', '?'));
|
||||
*
|
||||
* @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
|
||||
* @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
|
||||
* @since 8.2.0 - Parameter $type was added in 9.0.0
|
||||
@@ -167,81 +168,76 @@ interface IExpressionBuilder {
|
||||
* @psalm-taint-sink sql $y
|
||||
* @psalm-taint-sink sql $type
|
||||
*/
|
||||
public function lt(string|ILiteral|IQueryFunction|IParameter $x, string|ILiteral|IQueryFunction|IParameter $y, string|int|null $type = null): string;
|
||||
public function lt($x, $y, $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 generate a <left expr> <= <right expr>. Example:
|
||||
* When converted to string, it will generated a <left expr> <= <right expr>. Example:
|
||||
*
|
||||
* [php]
|
||||
* // u.id <= ?
|
||||
* $q->where($q->expr()->lte('u.id', '?'));
|
||||
*
|
||||
* @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
|
||||
* @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
|
||||
* @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(string|ILiteral|IQueryFunction|IParameter $x, string|ILiteral|IQueryFunction|IParameter $y, string|int|null $type = null): string;
|
||||
public function lte($x, $y, $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 generate a <left expr> > <right expr>. Example:
|
||||
* When converted to string, it will generated a <left expr> > <right expr>. Example:
|
||||
*
|
||||
* [php]
|
||||
* // u.id > ?
|
||||
* $q->where($q->expr()->gt('u.id', '?'));
|
||||
*
|
||||
* @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
|
||||
* @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
|
||||
* @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(
|
||||
string|ILiteral|IQueryFunction|IParameter $x,
|
||||
string|ILiteral|IQueryFunction|IParameter $y,
|
||||
string|int|null $type = null,
|
||||
): string;
|
||||
public function gt($x, $y, $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 generate a <left expr> >= <right expr>. Example:
|
||||
* When converted to string, it will generated a <left expr> >= <right expr>. Example:
|
||||
*
|
||||
* [php]
|
||||
* // u.id >= ?
|
||||
* $q->where($q->expr()->gte('u.id', '?'));
|
||||
*
|
||||
* @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
|
||||
* @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
|
||||
* @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(
|
||||
string|ILiteral|IParameter|IQueryFunction $x,
|
||||
string|ILiteral|IParameter|IQueryFunction $y,
|
||||
int|string|null $type = null,
|
||||
): string;
|
||||
public function gte($x, $y, $type = null): string;
|
||||
|
||||
/**
|
||||
* Creates an IS NULL expression with the given arguments.
|
||||
@@ -253,46 +249,27 @@ interface IExpressionBuilder {
|
||||
*
|
||||
* @psalm-taint-sink sql $x
|
||||
*/
|
||||
public function isNull(string|ILiteral|IParameter|IQueryFunction $x): string;
|
||||
public function isNull($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(string|ILiteral|IParameter|IQueryFunction $x): string;
|
||||
public function isNotNull($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 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
|
||||
*
|
||||
* @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(
|
||||
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 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
|
||||
* @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
|
||||
* @since 8.2.0 - Parameter $type was added in 9.0.0
|
||||
@@ -301,19 +278,32 @@ interface IExpressionBuilder {
|
||||
* @psalm-taint-sink sql $y
|
||||
* @psalm-taint-sink sql $type
|
||||
*/
|
||||
public function notLike(
|
||||
ILiteral|IParameter|IQueryFunction|string $x,
|
||||
ILiteral|IParameter|IQueryFunction|string $y,
|
||||
int|string|null $type = null,
|
||||
): string;
|
||||
public function like($x, $y, $type = null): string;
|
||||
|
||||
/**
|
||||
* Creates an ILIKE() comparison expression with the given arguments.
|
||||
* Creates a NOT LIKE() comparison expression with the given arguments.
|
||||
*
|
||||
* @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
|
||||
* @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
|
||||
* @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 notLike($x, $y, $type = null): string;
|
||||
|
||||
/**
|
||||
* 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
|
||||
@@ -322,39 +312,15 @@ interface IExpressionBuilder {
|
||||
* @psalm-taint-sink sql $y
|
||||
* @psalm-taint-sink sql $type
|
||||
*/
|
||||
public function iLike(
|
||||
ILiteral|IParameter|IQueryFunction|string $x,
|
||||
ILiteral|IParameter|IQueryFunction|string $y,
|
||||
int|string|null $type = null,
|
||||
): string;
|
||||
public function iLike($x, $y, $type = null): string;
|
||||
|
||||
/**
|
||||
* Creates an IN () comparison expression with the given arguments.
|
||||
* 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 IQueryBuilder::PARAM_*|null $type one of the IQueryBuilder::PARAM_* constants
|
||||
* required when comparing text fields for oci compatibility
|
||||
*
|
||||
* @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(
|
||||
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 IQueryBuilder::PARAM_*|null $type one of the IQueryBuilder::PARAM_* constants
|
||||
* required when comparing text fields for oci compatibility
|
||||
* @param mixed|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
|
||||
@@ -363,31 +329,46 @@ interface IExpressionBuilder {
|
||||
* @psalm-taint-sink sql $y
|
||||
* @psalm-taint-sink sql $type
|
||||
*/
|
||||
public function notIn(
|
||||
ILiteral|IParameter|IQueryFunction|string $x,
|
||||
ILiteral|IParameter|IQueryFunction|string|array $y,
|
||||
int|string|null $type = null,
|
||||
): string;
|
||||
public function in($x, $y, $type = null): string;
|
||||
|
||||
/**
|
||||
* Creates a `$x = ''` statement, because Oracle needs a different check
|
||||
* 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
|
||||
* @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 notIn($x, $y, $type = null): 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 emptyString(string|ILiteral|IParameter|IQueryFunction $x): string;
|
||||
public function emptyString($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(string|ILiteral|IParameter|IQueryFunction $x): string;
|
||||
public function nonEmptyString($x): string;
|
||||
|
||||
|
||||
/**
|
||||
@@ -401,41 +382,46 @@ interface IExpressionBuilder {
|
||||
* @psalm-taint-sink sql $x
|
||||
* @psalm-taint-sink sql $y
|
||||
*/
|
||||
public function bitwiseAnd(string|ILiteral $x, int $y): IQueryFunction;
|
||||
public function bitwiseAnd($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(string|ILiteral $x, int $y): IQueryFunction;
|
||||
public function bitwiseOr($x, int $y): IQueryFunction;
|
||||
|
||||
/**
|
||||
* Quotes a given input parameter.
|
||||
*
|
||||
* @param mixed $input The parameter to be quoted.
|
||||
* @param IQueryBuilder::PARAM_* $type One of the IQueryBuilder::PARAM_* constants
|
||||
* @param int $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(mixed $input, int|string $type = IQueryBuilder::PARAM_STR): ILiteral;
|
||||
public function literal($input, $type = IQueryBuilder::PARAM_STR): ILiteral;
|
||||
|
||||
/**
|
||||
* Returns a IQueryFunction that casts the column to the given type
|
||||
*
|
||||
* @param IQueryBuilder::PARAM_* $type
|
||||
* @param string|IQueryFunction $column
|
||||
* @param mixed $type One of IQueryBuilder::PARAM_*
|
||||
* @psalm-param IQueryBuilder::PARAM_* $type
|
||||
* @return IQueryFunction
|
||||
* @since 9.0.0
|
||||
*
|
||||
* @psalm-taint-sink sql $column
|
||||
* @psalm-taint-sink sql $type
|
||||
*/
|
||||
public function castColumn(string|IQueryFunction|ILiteral|IParameter $column, int|string $type): IQueryFunction;
|
||||
public function castColumn($column, $type): IQueryFunction;
|
||||
}
|
||||
|
||||
@@ -6,14 +6,11 @@
|
||||
*/
|
||||
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
|
||||
@@ -23,7 +20,7 @@ interface IFunctionBuilder {
|
||||
* @return IQueryFunction
|
||||
* @since 12.0.0
|
||||
*/
|
||||
public function md5(string|ILiteral|IParameter|IQueryFunction $input): IQueryFunction;
|
||||
public function md5($input): IQueryFunction;
|
||||
|
||||
/**
|
||||
* Combines two input strings
|
||||
@@ -34,7 +31,7 @@ interface IFunctionBuilder {
|
||||
* @return IQueryFunction
|
||||
* @since 12.0.0
|
||||
*/
|
||||
public function concat(string|ILiteral|IParameter|IQueryFunction $x, string|ILiteral|IParameter|IQueryFunction ...$expr): IQueryFunction;
|
||||
public function concat($x, ...$expr): IQueryFunction;
|
||||
|
||||
/**
|
||||
* Returns a string which is the concatenation of all non-NULL values of X
|
||||
@@ -50,7 +47,7 @@ interface IFunctionBuilder {
|
||||
* @return IQueryFunction
|
||||
* @since 24.0.0
|
||||
*/
|
||||
public function groupConcat(string|IQueryFunction $expr, ?string $separator = ','): IQueryFunction;
|
||||
public function groupConcat($expr, ?string $separator = ','): IQueryFunction;
|
||||
|
||||
/**
|
||||
* Takes a substring from the input string
|
||||
@@ -59,29 +56,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(
|
||||
string|ILiteral|IParameter|IQueryFunction $input,
|
||||
string|ILiteral|IParameter|IQueryFunction $start,
|
||||
null|ILiteral|IParameter|IQueryFunction $length = null,
|
||||
): IQueryFunction;
|
||||
public function substring($input, $start, $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(string|ILiteral|IParameter|IQueryFunction $field): IQueryFunction;
|
||||
public function sum($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(string|ILiteral|IParameter|IQueryFunction $field): IQueryFunction;
|
||||
public function lower($field): IQueryFunction;
|
||||
|
||||
/**
|
||||
* @param string|ILiteral|IParameter|IQueryFunction $x The first input field or number
|
||||
@@ -89,10 +86,7 @@ interface IFunctionBuilder {
|
||||
* @return IQueryFunction
|
||||
* @since 14.0.0
|
||||
*/
|
||||
public function add(
|
||||
string|ILiteral|IParameter|IQueryFunction $x,
|
||||
string|ILiteral|IParameter|IQueryFunction $y,
|
||||
): IQueryFunction;
|
||||
public function add($x, $y): IQueryFunction;
|
||||
|
||||
/**
|
||||
* @param string|ILiteral|IParameter|IQueryFunction $x The first input field or number
|
||||
@@ -100,10 +94,7 @@ interface IFunctionBuilder {
|
||||
* @return IQueryFunction
|
||||
* @since 14.0.0
|
||||
*/
|
||||
public function subtract(
|
||||
string|ILiteral|IParameter|IQueryFunction $x,
|
||||
string|ILiteral|IParameter|IQueryFunction $y,
|
||||
): IQueryFunction;
|
||||
public function subtract($x, $y): IQueryFunction;
|
||||
|
||||
/**
|
||||
* @param string|ILiteral|IParameter|IQueryFunction $count The input to be counted
|
||||
@@ -112,7 +103,7 @@ interface IFunctionBuilder {
|
||||
* @return IQueryFunction
|
||||
* @since 14.0.0
|
||||
*/
|
||||
public function count(string|ILiteral|IParameter|IQueryFunction $count = '', string $alias = ''): IQueryFunction;
|
||||
public function count($count = '', $alias = ''): IQueryFunction;
|
||||
|
||||
/**
|
||||
* @param string|ILiteral|IParameter|IQueryFunction $field The input to be measured
|
||||
@@ -121,15 +112,16 @@ interface IFunctionBuilder {
|
||||
* @return IQueryFunction
|
||||
* @since 24.0.0
|
||||
*/
|
||||
public function octetLength(string|ILiteral|IParameter|IQueryFunction $field, string $alias = ''): IQueryFunction;
|
||||
public function octetLength($field, $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(string|ILiteral|IParameter|IQueryFunction $field, string $alias = ''): IQueryFunction;
|
||||
public function charLength($field, $alias = ''): IQueryFunction;
|
||||
|
||||
/**
|
||||
* Takes the maximum of all rows in a column
|
||||
@@ -138,9 +130,10 @@ interface IFunctionBuilder {
|
||||
*
|
||||
* @param string|ILiteral|IParameter|IQueryFunction $field the column to maximum
|
||||
*
|
||||
* @return IQueryFunction
|
||||
* @since 18.0.0
|
||||
*/
|
||||
public function max(string|ILiteral|IParameter|IQueryFunction $field): IQueryFunction;
|
||||
public function max($field): IQueryFunction;
|
||||
|
||||
/**
|
||||
* Takes the minimum of all rows in a column
|
||||
@@ -149,33 +142,34 @@ interface IFunctionBuilder {
|
||||
*
|
||||
* @param string|ILiteral|IParameter|IQueryFunction $field the column to minimum
|
||||
*
|
||||
* @return IQueryFunction
|
||||
* @since 18.0.0
|
||||
*/
|
||||
public function min(string|ILiteral|IParameter|IQueryFunction $field): IQueryFunction;
|
||||
public function min($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(
|
||||
string|ILiteral|IParameter|IQueryFunction $x,
|
||||
string|ILiteral|IParameter|IQueryFunction $y,
|
||||
): IQueryFunction;
|
||||
public function greatest($x, $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(
|
||||
string|ILiteral|IParameter|IQueryFunction $x,
|
||||
string|ILiteral|IParameter|IQueryFunction $y,
|
||||
): IQueryFunction;
|
||||
public function least($x, $y): IQueryFunction;
|
||||
|
||||
/**
|
||||
* Get the current date and time as a UNIX timestamp.
|
||||
|
||||
@@ -11,7 +11,6 @@ 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;
|
||||
@@ -22,7 +21,6 @@ use OCP\IDBConnection;
|
||||
*
|
||||
* @psalm-taint-specialize
|
||||
*/
|
||||
#[Consumable(since: '8.2.0')]
|
||||
interface IQueryBuilder {
|
||||
/**
|
||||
* @since 9.0.0
|
||||
@@ -128,7 +126,7 @@ interface IQueryBuilder {
|
||||
* owncloud database prefix automatically.
|
||||
* @since 8.2.0
|
||||
*/
|
||||
public function automaticTablePrefix(bool $enabled): void;
|
||||
public function automaticTablePrefix($enabled);
|
||||
|
||||
/**
|
||||
* Gets an ExpressionBuilder used for object-oriented construction of query expressions.
|
||||
@@ -144,9 +142,10 @@ 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(): IExpressionBuilder;
|
||||
public function expr();
|
||||
|
||||
/**
|
||||
* Gets an FunctionBuilder used for object-oriented construction of query functions.
|
||||
@@ -162,24 +161,26 @@ 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(): IFunctionBuilder;
|
||||
public function func();
|
||||
|
||||
/**
|
||||
* Gets the type of the currently built query.
|
||||
*
|
||||
* @deprecated 34.0.0 If necessary, track the type of the query being built outside of the builder.
|
||||
* @return integer
|
||||
* @since 8.2.0
|
||||
*/
|
||||
public function getType(): int;
|
||||
public function getType();
|
||||
|
||||
/**
|
||||
* Gets the associated DBAL Connection for this query builder.
|
||||
*
|
||||
* @return \OCP\IDBConnection
|
||||
* @since 8.2.0
|
||||
*/
|
||||
public function getConnection(): IDBConnection;
|
||||
public function getConnection();
|
||||
|
||||
/**
|
||||
* Gets the state of this query builder instance.
|
||||
@@ -189,7 +190,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(): int;
|
||||
public function getState();
|
||||
|
||||
/**
|
||||
* Execute for select statements
|
||||
@@ -228,7 +229,7 @@ interface IQueryBuilder {
|
||||
* @return string The SQL query string.
|
||||
* @since 8.2.0
|
||||
*/
|
||||
public function getSQL(): string;
|
||||
public function getSQL();
|
||||
|
||||
/**
|
||||
* Sets a query parameter for the query being constructed.
|
||||
@@ -244,11 +245,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.
|
||||
*
|
||||
* @return $this This QueryBuilder instance.
|
||||
* @since 8.2.0
|
||||
*/
|
||||
public function setParameter(string|int $key, mixed $value, string|null|int $type = null): self;
|
||||
public function setParameter($key, $value, $type = null);
|
||||
|
||||
/**
|
||||
* Sets a collection of query parameters for the query being constructed.
|
||||
@@ -264,13 +265,13 @@ interface IQueryBuilder {
|
||||
* ));
|
||||
* </code>
|
||||
*
|
||||
* @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.
|
||||
* @param array $params The query parameters to set.
|
||||
* @param array $types The query parameters types to set.
|
||||
*
|
||||
* @return $this This QueryBuilder instance.
|
||||
* @since 8.2.0
|
||||
*/
|
||||
public function setParameters(array $params, array $types = []): self;
|
||||
public function setParameters(array $params, array $types = []);
|
||||
|
||||
/**
|
||||
* Gets all defined query parameters for the query being constructed indexed by parameter index or name.
|
||||
@@ -278,73 +279,73 @@ interface IQueryBuilder {
|
||||
* @return array The currently defined query parameters indexed by parameter index or name.
|
||||
* @since 8.2.0
|
||||
*/
|
||||
public function getParameters(): array;
|
||||
public function getParameters();
|
||||
|
||||
/**
|
||||
* Gets a (previously set) query parameter of the query being constructed.
|
||||
*
|
||||
* @param int|string $key The key (index or name) of the bound parameter.
|
||||
* @param mixed $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(int|string $key): mixed;
|
||||
public function getParameter($key);
|
||||
|
||||
/**
|
||||
* Gets all defined query parameter types for the query being constructed indexed by parameter index or name.
|
||||
*
|
||||
* @return array<string|int, self::PARAM_*> The currently defined query parameter types indexed by parameter index or name.
|
||||
* @return array The currently defined query parameter types indexed by parameter index or name.
|
||||
* @since 8.2.0
|
||||
*/
|
||||
public function getParameterTypes(): array;
|
||||
public function getParameterTypes();
|
||||
|
||||
/**
|
||||
* Gets a (previously set) query parameter type of the query being constructed.
|
||||
*
|
||||
* @param int|string $key The key (index or name) of the bound parameter type.
|
||||
* @param mixed $key The key (index or name) of the bound parameter type.
|
||||
*
|
||||
* @return self::PARAM_* The value of the bound parameter type.
|
||||
* @return mixed The value of the bound parameter type.
|
||||
* @since 8.2.0
|
||||
*/
|
||||
public function getParameterType(int|string $key): int|string;
|
||||
public function getParameterType($key);
|
||||
|
||||
/**
|
||||
* Sets the position of the first result to retrieve (the "offset").
|
||||
*
|
||||
* @param non-negative-int $firstResult The first result to return.
|
||||
* @param int $firstResult The first result to return.
|
||||
*
|
||||
* @return $this This QueryBuilder instance.
|
||||
* @since 8.2.0
|
||||
*/
|
||||
public function setFirstResult(int $firstResult): self;
|
||||
public function setFirstResult($firstResult);
|
||||
|
||||
/**
|
||||
* 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 non-negative-int The position of the first result.
|
||||
* @return int The position of the first result.
|
||||
* @since 8.2.0
|
||||
*/
|
||||
public function getFirstResult(): int;
|
||||
public function getFirstResult();
|
||||
|
||||
/**
|
||||
* Sets the maximum number of results to retrieve (the "limit").
|
||||
*
|
||||
* @param positive-int|null $maxResults The maximum number of results to retrieve.
|
||||
* @return $this This QueryBuilder instance.
|
||||
* @param int|null $maxResults The maximum number of results to retrieve.
|
||||
*
|
||||
* @return $this This QueryBuilder instance.
|
||||
* @since 8.2.0
|
||||
*/
|
||||
public function setMaxResults(?int $maxResults): self;
|
||||
public function setMaxResults($maxResults);
|
||||
|
||||
/**
|
||||
* 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 positive-int|null The maximum number of results.
|
||||
* @return int|null The maximum number of results.
|
||||
* @since 8.2.0
|
||||
*/
|
||||
public function getMaxResults(): ?int;
|
||||
public function getMaxResults();
|
||||
|
||||
/**
|
||||
* Specifies an item that is to be returned in the query result.
|
||||
@@ -357,14 +358,14 @@ interface IQueryBuilder {
|
||||
* ->leftJoin('u', 'phonenumbers', 'p', 'u.id = p.user_id');
|
||||
* </code>
|
||||
*
|
||||
* @param string|list<string>|IQueryFunction|ILiteral ...$selects The selection expressions.
|
||||
* @return $this This QueryBuilder instance.
|
||||
* @param mixed ...$selects The selection expressions.
|
||||
*
|
||||
* @return $this This QueryBuilder instance.
|
||||
* @since 8.2.0
|
||||
*
|
||||
* @psalm-taint-sink sql $selects
|
||||
*/
|
||||
public function select(mixed...$selects): self;
|
||||
public function select(...$selects);
|
||||
|
||||
/**
|
||||
* Specifies an item that is to be returned with a different name in the query result.
|
||||
@@ -376,16 +377,16 @@ interface IQueryBuilder {
|
||||
* ->leftJoin('u', 'phonenumbers', 'p', 'u.id = p.user_id');
|
||||
* </code>
|
||||
*
|
||||
* @param string|IParameter|IQueryFunction $select The selection expressions.
|
||||
* @param mixed $select The selection expressions.
|
||||
* @param string $alias The column alias used in the constructed query.
|
||||
* @return $this This QueryBuilder instance.
|
||||
*
|
||||
* @return $this This QueryBuilder instance.
|
||||
* @since 8.2.1
|
||||
*
|
||||
* @psalm-taint-sink sql $select
|
||||
* @psalm-taint-sink sql $alias
|
||||
*/
|
||||
public function selectAlias(string|IParameter|IQueryFunction|ILiteral $select, string $alias): self;
|
||||
public function selectAlias($select, $alias): self;
|
||||
|
||||
/**
|
||||
* Specifies an item that is to be returned uniquely in the query result.
|
||||
@@ -396,14 +397,14 @@ interface IQueryBuilder {
|
||||
* ->from('users');
|
||||
* </code>
|
||||
*
|
||||
* @param string|string[] $select The selection expressions.
|
||||
* @return $this This QueryBuilder instance.
|
||||
* @param mixed $select The selection expressions.
|
||||
*
|
||||
* @return $this This QueryBuilder instance.
|
||||
* @since 9.0.0
|
||||
*
|
||||
* @psalm-taint-sink sql $select
|
||||
*/
|
||||
public function selectDistinct(string|array $select): self;
|
||||
public function selectDistinct($select);
|
||||
|
||||
/**
|
||||
* Adds an item that is to be returned in the query result.
|
||||
@@ -416,14 +417,14 @@ interface IQueryBuilder {
|
||||
* ->leftJoin('u', 'phonenumbers', 'u.id = p.user_id');
|
||||
* </code>
|
||||
*
|
||||
* @param string|IParameter|IQueryFunction|ILiteral|list<string> ...$select The selection expression.
|
||||
* @return $this This QueryBuilder instance.
|
||||
* @param mixed ...$select The selection expression.
|
||||
*
|
||||
* @return $this This QueryBuilder instance.
|
||||
* @since 8.2.0
|
||||
*
|
||||
* @psalm-taint-sink sql $select
|
||||
*/
|
||||
public function addSelect(mixed ...$select): self;
|
||||
public function addSelect(...$select);
|
||||
|
||||
/**
|
||||
* Turns the query being built into a bulk delete query that ranges over
|
||||
@@ -437,15 +438,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(string $delete, ?string $alias = null): self;
|
||||
public function delete($delete = null, $alias = null);
|
||||
|
||||
/**
|
||||
* Turns the query being built into a bulk update query that ranges over
|
||||
@@ -460,15 +461,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.
|
||||
* @return $this This QueryBuilder instance.
|
||||
* @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(string $update, ?string $alias = null): self;
|
||||
public function update($update = null, $alias = null);
|
||||
|
||||
/**
|
||||
* Turns the query being built into an insert query that inserts into
|
||||
@@ -486,13 +487,13 @@ interface IQueryBuilder {
|
||||
* </code>
|
||||
*
|
||||
* @param string $insert The table into which the rows should be inserted.
|
||||
* @return $this This QueryBuilder instance.
|
||||
*
|
||||
* @return $this This QueryBuilder instance.
|
||||
* @since 8.2.0
|
||||
*
|
||||
* @psalm-taint-sink sql $insert
|
||||
*/
|
||||
public function insert(string $insert): self;
|
||||
public function insert($insert = null);
|
||||
|
||||
/**
|
||||
* Creates and adds a query root corresponding to the table identified by the
|
||||
@@ -506,13 +507,13 @@ interface IQueryBuilder {
|
||||
*
|
||||
* @param string|IQueryFunction $from The table.
|
||||
* @param string|null $alias The alias of the table.
|
||||
* @return $this This QueryBuilder instance.
|
||||
*
|
||||
* @return $this This QueryBuilder instance.
|
||||
* @since 8.2.0
|
||||
*
|
||||
* @psalm-taint-sink sql $from
|
||||
*/
|
||||
public function from(string|IQueryFunction $from, ?string $alias = null): self;
|
||||
public function from($from, $alias = null);
|
||||
|
||||
/**
|
||||
* Creates and adds a join to the query.
|
||||
@@ -526,10 +527,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.
|
||||
*
|
||||
* @return $this This QueryBuilder instance.
|
||||
* @since 8.2.0
|
||||
*
|
||||
* @psalm-taint-sink sql $fromAlias
|
||||
@@ -537,7 +538,7 @@ interface IQueryBuilder {
|
||||
* @psalm-taint-sink sql $alias
|
||||
* @psalm-taint-sink sql $condition
|
||||
*/
|
||||
public function join(string $fromAlias, string|IQueryFunction $join, ?string $alias, string|ICompositeExpression|null $condition = null): self;
|
||||
public function join($fromAlias, $join, $alias, $condition = null);
|
||||
|
||||
/**
|
||||
* Creates and adds a join to the query.
|
||||
@@ -551,10 +552,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.
|
||||
*
|
||||
* @return $this This QueryBuilder instance.
|
||||
* @since 8.2.0
|
||||
*
|
||||
* @psalm-taint-sink sql $fromAlias
|
||||
@@ -562,7 +563,7 @@ interface IQueryBuilder {
|
||||
* @psalm-taint-sink sql $alias
|
||||
* @psalm-taint-sink sql $condition
|
||||
*/
|
||||
public function innerJoin(string $fromAlias, string|IQueryFunction $join, ?string $alias, string|ICompositeExpression|null $condition = null): self;
|
||||
public function innerJoin($fromAlias, $join, $alias, $condition = null);
|
||||
|
||||
/**
|
||||
* Creates and adds a left join to the query.
|
||||
@@ -576,10 +577,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.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
@@ -588,7 +589,7 @@ interface IQueryBuilder {
|
||||
* @psalm-taint-sink sql $alias
|
||||
* @psalm-taint-sink sql $condition
|
||||
*/
|
||||
public function leftJoin(string $fromAlias, string|IQueryFunction $join, ?string $alias, string|ICompositeExpression|null $condition = null): self;
|
||||
public function leftJoin($fromAlias, $join, $alias, $condition = null);
|
||||
|
||||
/**
|
||||
* Creates and adds a right join to the query.
|
||||
@@ -602,10 +603,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.
|
||||
*
|
||||
* @return $this This QueryBuilder instance.
|
||||
* @since 8.2.0
|
||||
*
|
||||
* @psalm-taint-sink sql $fromAlias
|
||||
@@ -613,7 +614,7 @@ interface IQueryBuilder {
|
||||
* @psalm-taint-sink sql $alias
|
||||
* @psalm-taint-sink sql $condition
|
||||
*/
|
||||
public function rightJoin(string $fromAlias, string|IQueryFunction $join, ?string $alias, string|ICompositeExpression|null $condition = null): self;
|
||||
public function rightJoin($fromAlias, $join, $alias, $condition = null);
|
||||
|
||||
/**
|
||||
* Sets a new value for a column in a bulk update query.
|
||||
@@ -627,14 +628,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.
|
||||
*
|
||||
* @return $this This QueryBuilder instance.
|
||||
* @since 8.2.0
|
||||
*
|
||||
* @psalm-taint-sink sql $key
|
||||
* @psalm-taint-sink sql $value
|
||||
*/
|
||||
public function set(string $key, ILiteral|IParameter|IQueryFunction|string $value): self;
|
||||
public function set($key, $value);
|
||||
|
||||
/**
|
||||
* Specifies one or more restrictions to the query result.
|
||||
@@ -659,14 +660,14 @@ interface IQueryBuilder {
|
||||
* ->where($or);
|
||||
* </code>
|
||||
*
|
||||
* @param string|IParameter|IQueryFunction|ILiteral|ICompositeExpression $predicates The restriction predicates.
|
||||
* @return $this This QueryBuilder instance.
|
||||
* @param mixed $predicates The restriction predicates.
|
||||
*
|
||||
* @return $this This QueryBuilder instance.
|
||||
* @since 8.2.0
|
||||
*
|
||||
* @psalm-taint-sink sql $predicates
|
||||
*/
|
||||
public function where(mixed ...$predicates): self;
|
||||
public function where(...$predicates);
|
||||
|
||||
/**
|
||||
* Adds one or more restrictions to the query results, forming a logical
|
||||
@@ -680,7 +681,8 @@ interface IQueryBuilder {
|
||||
* ->andWhere('u.is_active = 1');
|
||||
* </code>
|
||||
*
|
||||
* @param string|IParameter|IQueryFunction|ILiteral|ICompositeExpression ...$where The query restrictions.
|
||||
* @param mixed ...$where The query restrictions.
|
||||
*
|
||||
* @return $this This QueryBuilder instance.
|
||||
*
|
||||
* @see where()
|
||||
@@ -688,7 +690,7 @@ interface IQueryBuilder {
|
||||
*
|
||||
* @psalm-taint-sink sql $where
|
||||
*/
|
||||
public function andWhere(mixed ...$where): self;
|
||||
public function andWhere(...$where);
|
||||
|
||||
/**
|
||||
* Adds one or more restrictions to the query results, forming a logical
|
||||
@@ -702,7 +704,8 @@ interface IQueryBuilder {
|
||||
* ->orWhere('u.id = 2');
|
||||
* </code>
|
||||
*
|
||||
* @param string|IParameter|IQueryFunction|ILiteral|ICompositeExpression ...$where The WHERE statement.
|
||||
* @param mixed ...$where The WHERE statement.
|
||||
*
|
||||
* @return $this This QueryBuilder instance.
|
||||
*
|
||||
* @see where()
|
||||
@@ -710,7 +713,7 @@ interface IQueryBuilder {
|
||||
*
|
||||
* @psalm-taint-sink sql $where
|
||||
*/
|
||||
public function orWhere(mixed ...$where): self;
|
||||
public function orWhere(...$where);
|
||||
|
||||
/**
|
||||
* Specifies a grouping over the results of the query.
|
||||
@@ -723,14 +726,14 @@ interface IQueryBuilder {
|
||||
* ->groupBy('u.id');
|
||||
* </code>
|
||||
*
|
||||
* @param string|IParameter|IQueryFunction|ILiteral|list<string> ...$groupBys The grouping expression.
|
||||
* @return $this This QueryBuilder instance.
|
||||
* @param mixed ...$groupBys The grouping expression.
|
||||
*
|
||||
* @return $this This QueryBuilder instance.
|
||||
* @since 8.2.0
|
||||
*
|
||||
* @psalm-taint-sink sql $groupBys
|
||||
*/
|
||||
public function groupBy(mixed ...$groupBys): self;
|
||||
public function groupBy(...$groupBys);
|
||||
|
||||
/**
|
||||
* Adds a grouping expression to the query.
|
||||
@@ -743,14 +746,14 @@ interface IQueryBuilder {
|
||||
* ->addGroupBy('u.createdAt')
|
||||
* </code>
|
||||
*
|
||||
* @param string|IParameter|IQueryFunction|ILiteral|list<string> ...$groupBy The grouping expression.
|
||||
* @return $this This QueryBuilder instance.
|
||||
* @param mixed ...$groupBy The grouping expression.
|
||||
*
|
||||
* @return $this This QueryBuilder instance.
|
||||
* @since 8.2.0
|
||||
*
|
||||
* @psalm-taint-sink sql $groupby
|
||||
*/
|
||||
public function addGroupBy(mixed ...$groupBy): self;
|
||||
public function addGroupBy(...$groupBy);
|
||||
|
||||
/**
|
||||
* Sets a value for a column in an insert query.
|
||||
@@ -768,14 +771,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.
|
||||
*
|
||||
* @return $this This QueryBuilder instance.
|
||||
* @since 8.2.0
|
||||
*
|
||||
* @psalm-taint-sink sql $column
|
||||
* @psalm-taint-sink sql $value
|
||||
*/
|
||||
public function setValue(string $column, ILiteral|IParameter|IQueryFunction|string $value): self;
|
||||
public function setValue($column, $value);
|
||||
|
||||
/**
|
||||
* Specifies values for an insert query indexed by column names.
|
||||
@@ -792,60 +795,60 @@ interface IQueryBuilder {
|
||||
* );
|
||||
* </code>
|
||||
*
|
||||
* @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.
|
||||
* @param array $values The values to specify for the insert query indexed by column names.
|
||||
*
|
||||
* @return $this This QueryBuilder instance.
|
||||
* @since 8.2.0
|
||||
*
|
||||
* @psalm-taint-sink sql $values
|
||||
*/
|
||||
public function values(array $values): self;
|
||||
public function values(array $values);
|
||||
|
||||
/**
|
||||
* Specifies a restriction over the groups of the query.
|
||||
* Replaces any previous having restrictions, if any.
|
||||
*
|
||||
* @param string|IParameter|IQueryFunction|ILiteral ...$having The restriction over the groups.
|
||||
* @return $this This QueryBuilder instance.
|
||||
* @param mixed ...$having The restriction over the groups.
|
||||
*
|
||||
* @return $this This QueryBuilder instance.
|
||||
* @since 8.2.0
|
||||
*
|
||||
* @psalm-taint-sink sql $having
|
||||
*/
|
||||
public function having(mixed ...$having): self;
|
||||
public function having(...$having);
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @return $this This QueryBuilder instance.
|
||||
* @since 8.2.0
|
||||
*
|
||||
* @psalm-taint-sink sql $andHaving
|
||||
*/
|
||||
public function andHaving(...$having): self;
|
||||
public function andHaving(...$having);
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @return $this This QueryBuilder instance.
|
||||
* @since 8.2.0
|
||||
*
|
||||
* @psalm-taint-sink sql $having
|
||||
*/
|
||||
public function orHaving(...$having): self;
|
||||
public function orHaving(...$having);
|
||||
|
||||
/**
|
||||
* Specifies an ordering for the query results.
|
||||
* Replaces any previously specified orderings, if any.
|
||||
*
|
||||
* @param string|IQueryFunction|ILiteral|IParameter $sort The ordering expression.
|
||||
* @param 'ASC'|'DESC'|'asc'|'desc'|null $order The ordering direction.
|
||||
* @param string $order The ordering direction.
|
||||
*
|
||||
* @return $this This QueryBuilder instance.
|
||||
* @since 8.2.0
|
||||
@@ -853,13 +856,13 @@ interface IQueryBuilder {
|
||||
* @psalm-taint-sink sql $sort
|
||||
* @psalm-taint-sink sql $order
|
||||
*/
|
||||
public function orderBy(string|IQueryFunction|ILiteral|IParameter $sort, ?string $order = null): self;
|
||||
public function orderBy($sort, $order = null);
|
||||
|
||||
/**
|
||||
* Adds an ordering to the query results.
|
||||
*
|
||||
* @param string|ILiteral|IParameter|IQueryFunction $sort The ordering expression.
|
||||
* @param 'ASC'|'DESC'|'asc'|'desc'|null $order The ordering direction.
|
||||
* @param string $order The ordering direction.
|
||||
*
|
||||
* @return $this This QueryBuilder instance.
|
||||
* @since 8.2.0
|
||||
@@ -867,25 +870,29 @@ interface IQueryBuilder {
|
||||
* @psalm-taint-sink sql $sort
|
||||
* @psalm-taint-sink sql $order
|
||||
*/
|
||||
public function addOrderBy(string|ILiteral|IParameter|IQueryFunction $sort, ?string $order = null): self;
|
||||
public function addOrderBy($sort, $order = null);
|
||||
|
||||
/**
|
||||
* 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(string $queryPartName): mixed;
|
||||
public function getQueryPart($queryPartName);
|
||||
|
||||
/**
|
||||
* 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(): array;
|
||||
public function getQueryParts();
|
||||
|
||||
/**
|
||||
* Resets SQL parts.
|
||||
@@ -897,7 +904,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(?array $queryPartNames = null): self;
|
||||
public function resetQueryParts($queryPartNames = null);
|
||||
|
||||
/**
|
||||
* Resets a single SQL part.
|
||||
@@ -909,7 +916,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(string $queryPartName): self;
|
||||
public function resetQueryPart($queryPartName);
|
||||
|
||||
/**
|
||||
* Creates a new named parameter and bind the value $value to it.
|
||||
@@ -943,7 +950,7 @@ interface IQueryBuilder {
|
||||
*
|
||||
* @psalm-taint-escape sql
|
||||
*/
|
||||
public function createNamedParameter(mixed $value, $type = self::PARAM_STR, ?string $placeHolder = null): IParameter;
|
||||
public function createNamedParameter($value, $type = self::PARAM_STR, $placeHolder = null);
|
||||
|
||||
/**
|
||||
* Creates a new positional parameter and bind the given value to it.
|
||||
@@ -970,7 +977,7 @@ interface IQueryBuilder {
|
||||
*
|
||||
* @psalm-taint-escape sql
|
||||
*/
|
||||
public function createPositionalParameter(mixed $value, $type = self::PARAM_STR): IParameter;
|
||||
public function createPositionalParameter($value, $type = self::PARAM_STR);
|
||||
|
||||
/**
|
||||
* Creates a new parameter
|
||||
@@ -991,7 +998,7 @@ interface IQueryBuilder {
|
||||
*
|
||||
* @psalm-taint-escape sql
|
||||
*/
|
||||
public function createParameter(string $name): IParameter;
|
||||
public function createParameter($name);
|
||||
|
||||
/**
|
||||
* Creates a new function.
|
||||
@@ -1020,7 +1027,7 @@ interface IQueryBuilder {
|
||||
*
|
||||
* @psalm-taint-sink sql $call
|
||||
*/
|
||||
public function createFunction(string $call): IQueryFunction;
|
||||
public function createFunction($call);
|
||||
|
||||
/**
|
||||
* Used to get the id of the last inserted element
|
||||
@@ -1040,11 +1047,13 @@ interface IQueryBuilder {
|
||||
* @since 9.0.0
|
||||
* @since 24.0.0 accepts IQueryFunction as parameter
|
||||
*/
|
||||
public function getTableName(string|IQueryFunction $table): string;
|
||||
public function getTableName($table);
|
||||
|
||||
/**
|
||||
* 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;
|
||||
@@ -1052,14 +1061,19 @@ 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(string $column, string $tableAlias = ''): string;
|
||||
public function getColumnName($column, $tableAlias = '');
|
||||
|
||||
/**
|
||||
* Provide a hint for the shard key for queries where this can't be detected otherwise
|
||||
*
|
||||
* @return $this This QueryBuilder instance.
|
||||
* @param string $column
|
||||
* @param mixed $value
|
||||
* @return $this
|
||||
* @since 30.0.0
|
||||
*/
|
||||
public function hintShardKey(string $column, mixed $value, bool $overwrite = false): self;
|
||||
@@ -1067,7 +1081,7 @@ interface IQueryBuilder {
|
||||
/**
|
||||
* Set the query to run across all shards if sharding is enabled.
|
||||
*
|
||||
* @return $this This QueryBuilder instance.
|
||||
* @return $this
|
||||
* @since 30.0.0
|
||||
*/
|
||||
public function runAcrossAllShards(): self;
|
||||
@@ -1075,7 +1089,7 @@ interface IQueryBuilder {
|
||||
/**
|
||||
* Get a list of column names that are expected in the query output
|
||||
*
|
||||
* @return string[]
|
||||
* @return array
|
||||
* @since 30.0.0
|
||||
*/
|
||||
public function getOutputColumns(): array;
|
||||
|
||||
@@ -31,13 +31,15 @@ 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): self;
|
||||
public function select(...$selects);
|
||||
|
||||
/**
|
||||
* @template NewS of string
|
||||
* @param NewS ...$columns The columns to select. They are not allowed to contain table names or aliases, or asterisks. Use {@see self::selectAlias()} for that.
|
||||
* @psalm-this-out self<S|NewS>
|
||||
* @return $this
|
||||
* @since 34.0.0
|
||||
* @note Psalm has a bug that prevents inferring the correct type in chained calls: https://github.com/vimeo/psalm/issues/8803. Convert the chained calls to standalone calls or switch to PHPStan, which suffered the same bug in the past, but fixed it in 2.1.5: https://github.com/phpstan/phpstan/issues/8439
|
||||
*/
|
||||
public function selectColumns(string ...$columns): self;
|
||||
|
||||
@@ -46,13 +48,15 @@ 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(string|array $select): self;
|
||||
public function selectDistinct($select);
|
||||
|
||||
/**
|
||||
* @template NewS of string
|
||||
* @param NewS ...$columns The columns to select distinct. They are not allowed to contain table names or aliases, or asterisks. Use {@see self::selectAlias()} for that.
|
||||
* @psalm-this-out self<S|NewS>
|
||||
* @return $this
|
||||
* @since 34.0.0
|
||||
* @note Psalm has a bug that prevents inferring the correct type in chained calls: https://github.com/vimeo/psalm/issues/8803. Convert the chained calls to standalone calls or switch to PHPStan, which suffered the same bug in the past, but fixed it in 2.1.5: https://github.com/phpstan/phpstan/issues/8439
|
||||
*/
|
||||
public function selectColumnsDistinct(string ...$columns): self;
|
||||
|
||||
@@ -61,16 +65,252 @@ 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): self;
|
||||
public function addSelect(...$select);
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
* @param string|IParameter|IQueryFunction|ILiteral $select
|
||||
* @param mixed $select
|
||||
* @template NewS of string
|
||||
* @param NewS $alias
|
||||
* @psalm-this-out self<S|NewS>
|
||||
* @psalm-suppress LessSpecificImplementedReturnType
|
||||
* @return $this
|
||||
* @note Psalm has a bug that prevents inferring the correct type in chained calls: https://github.com/vimeo/psalm/issues/8803. Convert the chained calls to standalone calls or switch to PHPStan, which suffered the same bug in the past, but fixed it in 2.1.5: https://github.com/phpstan/phpstan/issues/8439
|
||||
*/
|
||||
#[Override]
|
||||
public function selectAlias(string|IParameter|IQueryFunction|ILiteral $select, string $alias): self;
|
||||
public function selectAlias($select, $alias): self;
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
* @return $this
|
||||
* @psalm-suppress MissingParamType
|
||||
*/
|
||||
#[Override]
|
||||
public function setParameter($key, $value, $type = null);
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
* @return $this
|
||||
*/
|
||||
#[Override]
|
||||
public function setParameters(array $params, array $types = []);
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
* @return $this
|
||||
* @psalm-suppress MissingParamType
|
||||
*/
|
||||
#[Override]
|
||||
public function setFirstResult($firstResult);
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
* @return $this
|
||||
* @psalm-suppress MissingParamType
|
||||
*/
|
||||
#[Override]
|
||||
public function setMaxResults($maxResults);
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
* @return $this
|
||||
* @psalm-suppress MissingParamType
|
||||
*/
|
||||
#[Override]
|
||||
public function delete($delete = null, $alias = null);
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
* @return $this
|
||||
* @psalm-suppress MissingParamType
|
||||
*/
|
||||
#[Override]
|
||||
public function update($update = null, $alias = null);
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
* @return $this
|
||||
* @psalm-suppress MissingParamType
|
||||
*/
|
||||
#[Override]
|
||||
public function insert($insert = null);
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
* @return $this
|
||||
* @psalm-suppress MissingParamType
|
||||
*/
|
||||
#[Override]
|
||||
public function from($from, $alias = null);
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
* @return $this
|
||||
* @psalm-suppress MissingParamType
|
||||
*/
|
||||
#[Override]
|
||||
public function join($fromAlias, $join, $alias, $condition = null);
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
* @return $this
|
||||
* @psalm-suppress MissingParamType
|
||||
*/
|
||||
#[Override]
|
||||
public function innerJoin($fromAlias, $join, $alias, $condition = null);
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
* @return $this
|
||||
* @psalm-suppress MissingParamType
|
||||
*/
|
||||
#[Override]
|
||||
public function leftJoin($fromAlias, $join, $alias, $condition = null);
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
* @return $this
|
||||
* @psalm-suppress MissingParamType
|
||||
*/
|
||||
#[Override]
|
||||
public function rightJoin($fromAlias, $join, $alias, $condition = null);
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
* @return $this
|
||||
* @psalm-suppress MissingParamType
|
||||
*/
|
||||
#[Override]
|
||||
public function set($key, $value);
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
* @return $this
|
||||
* @psalm-suppress MissingParamType
|
||||
*/
|
||||
#[Override]
|
||||
public function where(...$predicates);
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
* @return $this
|
||||
* @psalm-suppress MissingParamType
|
||||
*/
|
||||
#[Override]
|
||||
public function andWhere(...$where);
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
* @return $this
|
||||
* @psalm-suppress MissingParamType
|
||||
*/
|
||||
#[Override]
|
||||
public function orWhere(...$where);
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
* @return $this
|
||||
* @psalm-suppress MissingParamType
|
||||
*/
|
||||
#[Override]
|
||||
public function groupBy(...$groupBys);
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
* @return $this
|
||||
* @psalm-suppress MissingParamType
|
||||
*/
|
||||
#[Override]
|
||||
public function addGroupBy(...$groupBy);
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
* @return $this
|
||||
* @psalm-suppress MissingParamType
|
||||
*/
|
||||
#[Override]
|
||||
public function setValue($column, $value);
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
* @return $this
|
||||
*/
|
||||
#[Override]
|
||||
public function values(array $values);
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
* @return $this
|
||||
* @psalm-suppress MissingParamType
|
||||
*/
|
||||
#[Override]
|
||||
public function having(...$having);
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
* @return $this
|
||||
* @psalm-suppress MissingParamType
|
||||
*/
|
||||
#[Override]
|
||||
public function andHaving(...$having);
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
* @return $this
|
||||
* @psalm-suppress MissingParamType
|
||||
*/
|
||||
#[Override]
|
||||
public function orHaving(...$having);
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
* @return $this
|
||||
* @psalm-suppress MissingParamType
|
||||
*/
|
||||
#[Override]
|
||||
public function orderBy($sort, $order = null);
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
* @return $this
|
||||
* @psalm-suppress MissingParamType
|
||||
*/
|
||||
#[Override]
|
||||
public function addOrderBy($sort, $order = null);
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
* @return $this
|
||||
* @psalm-suppress MissingParamType
|
||||
*/
|
||||
#[Override]
|
||||
public function resetQueryParts($queryPartNames = null);
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
* @return $this
|
||||
* @psalm-suppress MissingParamType
|
||||
*/
|
||||
#[Override]
|
||||
public function resetQueryPart($queryPartName);
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
* @return $this
|
||||
*/
|
||||
#[Override]
|
||||
public function hintShardKey(string $column, mixed $value, bool $overwrite = false): self;
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
* @return $this
|
||||
*/
|
||||
#[Override]
|
||||
public function runAcrossAllShards(): self;
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
* @return $this
|
||||
*/
|
||||
#[Override]
|
||||
public function forUpdate(ConflictResolutionMode $conflictResolutionMode = ConflictResolutionMode::Ordinary): self;
|
||||
}
|
||||
|
||||
@@ -258,9 +258,9 @@ interface IManager {
|
||||
* @return ($currentAccess is true
|
||||
* ? array{
|
||||
* users?: array<string, array{node_id: int, node_path: string}>,
|
||||
* remote?: array<string, array{node_id: int, node_path: string}>,
|
||||
* remote?: array<string, array{node_id: int, token: string}>,
|
||||
* public?: bool,
|
||||
* mail?: array<string, array{node_id: int, node_path: string}>
|
||||
* mail?: array<string, array{node_id: int, token: string}>
|
||||
* }
|
||||
* : array{users?: list<string>, remote?: bool, public?: bool, mail?: list<string>})
|
||||
* @since 12.0.0
|
||||
|
||||
@@ -1,23 +1,26 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
namespace OCP\Share;
|
||||
|
||||
use OCP\AppFramework\Attribute\Consumable;
|
||||
use OCP\Files\Node;
|
||||
|
||||
/**
|
||||
* Interface IShareHelper
|
||||
*
|
||||
* @since 12
|
||||
* @since 12.0.0
|
||||
*/
|
||||
#[Consumable(since: '12.0.0')]
|
||||
interface IShareHelper {
|
||||
/**
|
||||
* @param Node $node
|
||||
* @return array [ users => [Mapping $uid => $pathForUser], remotes => [Mapping $cloudId => $pathToMountRoot]]
|
||||
* @return array{users: array<string, string>, remotes: array<string, array{token: string, node_path: string}>} [ users => [Mapping $uid => $pathForUser], remotes => [Mapping $cloudId => $pathToMountRoot]]
|
||||
* @since 12
|
||||
*/
|
||||
public function getPathsForAccessList(Node $node);
|
||||
public function getPathsForAccessList(Node $node): array;
|
||||
}
|
||||
|
||||
@@ -28,6 +28,8 @@
|
||||
<file name="build/psalm/ITypedQueryBuilderTest.php"/>
|
||||
<file name="lib/private/DB/QueryBuilder/TypedQueryBuilder.php"/>
|
||||
<file name="lib/public/DB/QueryBuilder/ITypedQueryBuilder.php"/>
|
||||
<file name="lib/private/Share20/ShareHelper.php"/>
|
||||
<file name="lib/public/Share/IShareHelper.php"/>
|
||||
<ignoreFiles>
|
||||
<directory name="apps/**/composer"/>
|
||||
<directory name="apps/**/tests"/>
|
||||
|
||||
@@ -55,7 +55,6 @@
|
||||
<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"/>
|
||||
@@ -173,61 +172,52 @@
|
||||
<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>
|
||||
|
||||
@@ -10,7 +10,6 @@ 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;
|
||||
@@ -29,8 +28,6 @@ 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;
|
||||
@@ -95,12 +92,15 @@ 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,19 +119,18 @@ class QBMapperTest extends \Test\TestCase {
|
||||
$datetimeParam = $this->qb->createNamedParameter('datetime_prop', IQueryBuilder::PARAM_DATETIME_IMMUTABLE);
|
||||
|
||||
$createNamedParameterCalls = [
|
||||
[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],
|
||||
[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],
|
||||
];
|
||||
$this->qb->expects($this->exactly(6))
|
||||
->method('createNamedParameter')
|
||||
->willReturnCallback(function () use (&$createNamedParameterCalls): IParameter {
|
||||
->willReturnCallback(function () use (&$createNamedParameterCalls): void {
|
||||
$expected = array_shift($createNamedParameterCalls);
|
||||
$this->assertEquals([$expected[0], $expected[1], $expected[2]], func_get_args());
|
||||
return $expected[3];
|
||||
$this->assertEquals($expected, func_get_args());
|
||||
});
|
||||
|
||||
$setValueCalls = [
|
||||
@@ -144,15 +143,15 @@ class QBMapperTest extends \Test\TestCase {
|
||||
];
|
||||
$this->qb->expects($this->exactly(6))
|
||||
->method('setValue')
|
||||
->willReturnCallback(function () use (&$setValueCalls): IQueryBuilder {
|
||||
->willReturnCallback(function () use (&$setValueCalls): void {
|
||||
$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();
|
||||
@@ -175,21 +174,20 @@ class QBMapperTest extends \Test\TestCase {
|
||||
$datetimeParam = $this->qb->createNamedParameter('datetime_prop', IQueryBuilder::PARAM_DATETIME_IMMUTABLE);
|
||||
|
||||
$createNamedParameterCalls = [
|
||||
[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],
|
||||
[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],
|
||||
];
|
||||
$this->qb->expects($this->exactly(8))
|
||||
->method('createNamedParameter')
|
||||
->willReturnCallback(function () use (&$createNamedParameterCalls): IParameter {
|
||||
->willReturnCallback(function () use (&$createNamedParameterCalls): void {
|
||||
$expected = array_shift($createNamedParameterCalls);
|
||||
$this->assertEquals([$expected[0], $expected[1], $expected[2]], func_get_args());
|
||||
return $expected[3];
|
||||
$this->assertEquals($expected, func_get_args());
|
||||
});
|
||||
|
||||
$setCalls = [
|
||||
@@ -203,16 +201,16 @@ class QBMapperTest extends \Test\TestCase {
|
||||
];
|
||||
$this->qb->expects($this->exactly(7))
|
||||
->method('set')
|
||||
->willReturnCallback(function () use (&$setCalls): IQueryBuilder {
|
||||
->willReturnCallback(function () use (&$setCalls): void {
|
||||
$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);
|
||||
}
|
||||
|
||||
|
||||
@@ -54,12 +54,7 @@ class ManagerTest extends TestCase {
|
||||
$this->connection->prepare($sql)->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* @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 {
|
||||
protected function addDatabaseEntry(?string $parentId, ?string $topmostParentId, ?\DateTimeInterface $creationDT = null, ?\DateTimeInterface $latestChildDT = null, $objectId = null, ?\DateTimeInterface $expireDate = null): string {
|
||||
$creationDT ??= new \DateTime();
|
||||
$latestChildDT ??= new \DateTime('yesterday');
|
||||
$objectId ??= 'file64';
|
||||
|
||||
@@ -14,14 +14,13 @@ 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;
|
||||
|
||||
#[Group(name: 'DB')]
|
||||
#[\PHPUnit\Framework\Attributes\Group('DB')]
|
||||
class ExpressionBuilderDBTest extends TestCase {
|
||||
protected IDBConnection $connection;
|
||||
protected bool $schemaSetup = false;
|
||||
/** @var \Doctrine\DBAL\Connection|IDBConnection */
|
||||
protected $connection;
|
||||
protected $schemaSetup = false;
|
||||
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
@@ -46,8 +45,14 @@ class ExpressionBuilderDBTest extends TestCase {
|
||||
];
|
||||
}
|
||||
|
||||
#[DataProvider(methodName: 'likeProvider')]
|
||||
public function testLike(string $param1, string $param2, bool $match): void {
|
||||
/**
|
||||
*
|
||||
* @param string $param1
|
||||
* @param string $param2
|
||||
* @param boolean $match
|
||||
*/
|
||||
#[\PHPUnit\Framework\Attributes\DataProvider('likeProvider')]
|
||||
public function testLike($param1, $param2, $match): void {
|
||||
$query = $this->connection->getQueryBuilder();
|
||||
|
||||
$query->select(new Literal('1'))
|
||||
@@ -77,8 +82,14 @@ class ExpressionBuilderDBTest extends TestCase {
|
||||
];
|
||||
}
|
||||
|
||||
#[DataProvider(methodName: 'ilikeProvider')]
|
||||
public function testILike(string $param1, string $param2, bool $match): void {
|
||||
/**
|
||||
*
|
||||
* @param string $param1
|
||||
* @param string $param2
|
||||
* @param boolean $match
|
||||
*/
|
||||
#[\PHPUnit\Framework\Attributes\DataProvider('ilikeProvider')]
|
||||
public function testILike($param1, $param2, $match): void {
|
||||
$query = $this->connection->getQueryBuilder();
|
||||
|
||||
$query->select(new Literal('1'))
|
||||
@@ -222,11 +233,7 @@ class ExpressionBuilderDBTest extends TestCase {
|
||||
self::assertCount(1, $entries);
|
||||
}
|
||||
|
||||
/**
|
||||
* @psalm-param '1'|'mykey' $key
|
||||
* @psalm-param '4'|'myvalue' $value
|
||||
*/
|
||||
protected function createConfig(string $appId, string $key, string $value) {
|
||||
protected function createConfig($appId, $key, $value) {
|
||||
$query = $this->connection->getQueryBuilder();
|
||||
$query->insert('appconfig')
|
||||
->values([
|
||||
|
||||
@@ -13,6 +13,7 @@ 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;
|
||||
@@ -23,7 +24,12 @@ use PHPUnit\Framework\MockObject\MockObject;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Test\TestCase;
|
||||
|
||||
#[Group(name: 'DB')]
|
||||
/**
|
||||
* Class ExpressionBuilderTest
|
||||
*
|
||||
* @package Test\DB\QueryBuilder
|
||||
*/
|
||||
#[Group('DB')]
|
||||
class ExpressionBuilderTest extends TestCase {
|
||||
protected ExpressionBuilder $expressionBuilder;
|
||||
protected DoctrineExpressionBuilder $doctrineExpressionBuilder;
|
||||
@@ -60,8 +66,8 @@ class ExpressionBuilderTest extends TestCase {
|
||||
return $testSets;
|
||||
}
|
||||
|
||||
#[DataProvider(methodName: 'dataComparison')]
|
||||
public function testComparison(string $comparison, mixed $input1, bool $isInput1Literal, mixed $input2, bool $isInput2Literal): void {
|
||||
#[DataProvider('dataComparison')]
|
||||
public function testComparison(string $comparison, string $input1, bool $isInput1Literal, string $input2, bool $isInput2Literal): void {
|
||||
[$doctrineInput1, $ocInput1] = $this->helpWithLiteral($input1, $isInput1Literal);
|
||||
[$doctrineInput2, $ocInput2] = $this->helpWithLiteral($input2, $isInput2Literal);
|
||||
|
||||
@@ -80,7 +86,7 @@ class ExpressionBuilderTest extends TestCase {
|
||||
];
|
||||
}
|
||||
|
||||
#[DataProvider(methodName: 'dataComparisons')]
|
||||
#[DataProvider('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);
|
||||
@@ -91,7 +97,7 @@ class ExpressionBuilderTest extends TestCase {
|
||||
);
|
||||
}
|
||||
|
||||
#[DataProvider(methodName: 'dataComparisons')]
|
||||
#[DataProvider('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);
|
||||
@@ -102,7 +108,7 @@ class ExpressionBuilderTest extends TestCase {
|
||||
);
|
||||
}
|
||||
|
||||
#[DataProvider(methodName: 'dataComparisons')]
|
||||
#[DataProvider('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);
|
||||
@@ -113,7 +119,7 @@ class ExpressionBuilderTest extends TestCase {
|
||||
);
|
||||
}
|
||||
|
||||
#[DataProvider(methodName: 'dataComparisons')]
|
||||
#[DataProvider('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);
|
||||
@@ -124,7 +130,7 @@ class ExpressionBuilderTest extends TestCase {
|
||||
);
|
||||
}
|
||||
|
||||
#[DataProvider(methodName: 'dataComparisons')]
|
||||
#[DataProvider('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);
|
||||
@@ -135,7 +141,7 @@ class ExpressionBuilderTest extends TestCase {
|
||||
);
|
||||
}
|
||||
|
||||
#[DataProvider(methodName: 'dataComparisons')]
|
||||
#[DataProvider('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);
|
||||
@@ -167,7 +173,7 @@ class ExpressionBuilderTest extends TestCase {
|
||||
];
|
||||
}
|
||||
|
||||
#[DataProvider(methodName: 'dataLike')]
|
||||
#[DataProvider('dataLike')]
|
||||
public function testLike(string $input, bool $isLiteral): void {
|
||||
[$doctrineInput, $ocInput] = $this->helpWithLiteral($input, $isLiteral);
|
||||
|
||||
@@ -177,7 +183,7 @@ class ExpressionBuilderTest extends TestCase {
|
||||
);
|
||||
}
|
||||
|
||||
#[DataProvider(methodName: 'dataLike')]
|
||||
#[DataProvider('dataLike')]
|
||||
public function testNotLike(string $input, bool $isLiteral): void {
|
||||
[$doctrineInput, $ocInput] = $this->helpWithLiteral($input, $isLiteral);
|
||||
|
||||
@@ -196,7 +202,7 @@ class ExpressionBuilderTest extends TestCase {
|
||||
];
|
||||
}
|
||||
|
||||
#[DataProvider(methodName: 'dataIn')]
|
||||
#[DataProvider('dataIn')]
|
||||
public function testIn(string|array $input, bool $isLiteral): void {
|
||||
[$doctrineInput, $ocInput] = $this->helpWithLiteral($input, $isLiteral);
|
||||
|
||||
@@ -206,7 +212,7 @@ class ExpressionBuilderTest extends TestCase {
|
||||
);
|
||||
}
|
||||
|
||||
#[DataProvider(methodName: 'dataIn')]
|
||||
#[DataProvider('dataIn')]
|
||||
public function testNotIn(string|array $input, bool $isLiteral): void {
|
||||
[$doctrineInput, $ocInput] = $this->helpWithLiteral($input, $isLiteral);
|
||||
|
||||
@@ -216,7 +222,7 @@ class ExpressionBuilderTest extends TestCase {
|
||||
);
|
||||
}
|
||||
|
||||
protected function helpWithLiteral(string|array $input, bool $isLiteral): array {
|
||||
protected function helpWithLiteral(string|array $input, bool $isLiteral) {
|
||||
if ($isLiteral) {
|
||||
if (is_array($input)) {
|
||||
$doctrineInput = array_map(function ($ident) {
|
||||
@@ -255,14 +261,12 @@ class ExpressionBuilderTest extends TestCase {
|
||||
];
|
||||
}
|
||||
|
||||
#[DataProvider(methodName: 'dataLiteral')]
|
||||
#[DataProvider('dataLiteral')]
|
||||
public function testLiteral(string|int $input, string|int|null $type): void {
|
||||
if ($type === null) {
|
||||
$actual = $this->expressionBuilder->literal($input);
|
||||
} else {
|
||||
$actual = $this->expressionBuilder->literal($input, $type);
|
||||
}
|
||||
/** @var Literal $actual */
|
||||
$actual = $this->expressionBuilder->literal($input, $type);
|
||||
|
||||
$this->assertInstanceOf('\OC\DB\QueryBuilder\Literal', $actual);
|
||||
$this->assertEquals(
|
||||
$this->doctrineExpressionBuilder->literal($input, $type),
|
||||
$actual->__toString()
|
||||
@@ -296,11 +300,8 @@ class ExpressionBuilderTest extends TestCase {
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param IQueryBuilder::PARAM_* $type
|
||||
*/
|
||||
#[DataProvider(methodName: 'dataClobComparisons')]
|
||||
public function testClobComparisons(string $function, string|array $value, mixed $type, bool $compareKeyToValue, int $expected): void {
|
||||
#[DataProvider('dataClobComparisons')]
|
||||
public function testClobComparisons(string $function, string|array $value, int $type, bool $compareKeyToValue, int $expected): void {
|
||||
$appId = $this->getUniqueID('testing');
|
||||
$this->createConfig($appId, 1, 4);
|
||||
$this->createConfig($appId, 2, 5);
|
||||
@@ -334,7 +335,8 @@ class ExpressionBuilderTest extends TestCase {
|
||||
->where($query->expr()->eq('appid', $query->createNamedParameter($appId)))
|
||||
->executeStatement();
|
||||
}
|
||||
protected function createConfig(string $appId, int $key, string|int $value): void {
|
||||
|
||||
protected function createConfig(string $appId, int $key, int|string $value) {
|
||||
$query = $this->connection->getQueryBuilder();
|
||||
$query->insert('appconfig')
|
||||
->values([
|
||||
|
||||
@@ -11,13 +11,18 @@ 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;
|
||||
|
||||
#[Group(name: 'DB')]
|
||||
/**
|
||||
* Class FunctionBuilderTest
|
||||
*
|
||||
*
|
||||
* @package Test\DB\QueryBuilder
|
||||
*/
|
||||
#[\PHPUnit\Framework\Attributes\Group('DB')]
|
||||
class FunctionBuilderTest extends TestCase {
|
||||
protected IDBConnection $connection;
|
||||
/** @var \Doctrine\DBAL\Connection|IDBConnection */
|
||||
protected $connection;
|
||||
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
@@ -25,8 +30,8 @@ class FunctionBuilderTest extends TestCase {
|
||||
$this->connection = Server::get(IDBConnection::class);
|
||||
}
|
||||
|
||||
#[DataProvider(methodName: 'providerTestConcatString')]
|
||||
public function testConcatString(callable $closure): void {
|
||||
#[\PHPUnit\Framework\Attributes\DataProvider('providerTestConcatString')]
|
||||
public function testConcatString($closure): void {
|
||||
$query = $this->connection->getQueryBuilder();
|
||||
[$real, $arguments, $return] = $closure($query);
|
||||
if ($real) {
|
||||
@@ -48,35 +53,35 @@ class FunctionBuilderTest extends TestCase {
|
||||
public static function providerTestConcatString(): array {
|
||||
return [
|
||||
'1 column: string param unicode'
|
||||
=> [function (IQueryBuilder $q) {
|
||||
=> [function ($q) {
|
||||
return [false, [$q->createNamedParameter('👍')], '👍'];
|
||||
}],
|
||||
'2 columns: string param and string param'
|
||||
=> [function (IQueryBuilder $q) {
|
||||
=> [function ($q) {
|
||||
return [false, [$q->createNamedParameter('foo'), $q->createNamedParameter('bar')], 'foobar'];
|
||||
}],
|
||||
'2 columns: string param and int literal'
|
||||
=> [function (IQueryBuilder $q) {
|
||||
=> [function ($q) {
|
||||
return [false, [$q->createNamedParameter('foo'), $q->expr()->literal(1)], 'foo1'];
|
||||
}],
|
||||
'2 columns: string param and string literal'
|
||||
=> [function (IQueryBuilder $q) {
|
||||
=> [function ($q) {
|
||||
return [false, [$q->createNamedParameter('foo'), $q->expr()->literal('bar')], 'foobar'];
|
||||
}],
|
||||
'2 columns: string real and int literal'
|
||||
=> [function (IQueryBuilder $q) {
|
||||
=> [function ($q) {
|
||||
return [true, ['configkey', $q->expr()->literal(2)], '12'];
|
||||
}],
|
||||
'4 columns: string literal'
|
||||
=> [function (IQueryBuilder $q) {
|
||||
=> [function ($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 (IQueryBuilder $q) {
|
||||
=> [function ($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 (IQueryBuilder $q) {
|
||||
=> [function ($q) {
|
||||
return [false, [$q->createNamedParameter('b'), $q->createNamedParameter("'"), $q->createNamedParameter('||'), $q->createNamedParameter(','), $q->createNamedParameter('a')], "b'||,a"];
|
||||
}],
|
||||
];
|
||||
@@ -328,7 +333,7 @@ class FunctionBuilderTest extends TestCase {
|
||||
];
|
||||
}
|
||||
|
||||
#[DataProvider(methodName: 'octetLengthProvider')]
|
||||
#[\PHPUnit\Framework\Attributes\DataProvider('octetLengthProvider')]
|
||||
public function testOctetLength(string $str, int $bytes): void {
|
||||
$query = $this->connection->getQueryBuilder();
|
||||
|
||||
@@ -351,7 +356,7 @@ class FunctionBuilderTest extends TestCase {
|
||||
];
|
||||
}
|
||||
|
||||
#[DataProvider(methodName: 'charLengthProvider')]
|
||||
#[\PHPUnit\Framework\Attributes\DataProvider('charLengthProvider')]
|
||||
public function testCharLength(string $str, int $bytes): void {
|
||||
$query = $this->connection->getQueryBuilder();
|
||||
|
||||
@@ -366,10 +371,7 @@ class FunctionBuilderTest extends TestCase {
|
||||
$this->assertEquals($bytes, $column);
|
||||
}
|
||||
|
||||
/**
|
||||
* @psalm-param 10|11|20 $value
|
||||
*/
|
||||
private function setUpMinMax(int $value) {
|
||||
private function setUpMinMax($value) {
|
||||
$query = $this->connection->getQueryBuilder();
|
||||
|
||||
$query->insert('appconfig')
|
||||
|
||||
@@ -14,7 +14,6 @@ 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;
|
||||
|
||||
@@ -42,7 +41,7 @@ class JoinConditionTest extends TestCase {
|
||||
);
|
||||
}
|
||||
|
||||
#[DataProvider(methodName: 'platformProvider')]
|
||||
#[\PHPUnit\Framework\Attributes\DataProvider('platformProvider')]
|
||||
public function testParseCondition(string $platform): void {
|
||||
$query = $this->getBuilder($platform);
|
||||
$param1 = $query->createNamedParameter('files');
|
||||
@@ -62,7 +61,7 @@ class JoinConditionTest extends TestCase {
|
||||
], $parsed->toConditions);
|
||||
}
|
||||
|
||||
#[DataProvider(methodName: 'platformProvider')]
|
||||
#[\PHPUnit\Framework\Attributes\DataProvider('platformProvider')]
|
||||
public function testParseCastCondition(string $platform): void {
|
||||
$query = $this->getBuilder($platform);
|
||||
|
||||
|
||||
@@ -16,10 +16,9 @@ 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;
|
||||
|
||||
#[Group(name: 'DB')]
|
||||
#[\PHPUnit\Framework\Attributes\Group('DB')]
|
||||
class PartitionedQueryBuilderTest extends TestCase {
|
||||
private IDBConnection $connection;
|
||||
private ShardConnectionManager $shardConnectionManager;
|
||||
@@ -28,6 +27,7 @@ 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);
|
||||
|
||||
@@ -10,7 +10,6 @@ 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;
|
||||
@@ -19,18 +18,20 @@ 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;
|
||||
|
||||
#[Group(name: 'DB')]
|
||||
/**
|
||||
* Class QueryBuilderTest
|
||||
*
|
||||
*
|
||||
* @package Test\DB\QueryBuilder
|
||||
*/
|
||||
#[\PHPUnit\Framework\Attributes\Group('DB')]
|
||||
class QueryBuilderTest extends \Test\TestCase {
|
||||
private SystemConfig&MockObject $config;
|
||||
private LoggerInterface&MockObject $logger;
|
||||
@@ -47,10 +48,7 @@ class QueryBuilderTest extends \Test\TestCase {
|
||||
$this->queryBuilder = new QueryBuilder($this->connection, $this->config, $this->logger);
|
||||
}
|
||||
|
||||
/**
|
||||
* @psalm-param 'testFirstResult'|'testFirstResult1'|'testFirstResult2' $appId
|
||||
*/
|
||||
protected function createTestingRows(string $appId = 'testFirstResult'): void {
|
||||
protected function createTestingRows($appId = 'testFirstResult') {
|
||||
$qB = $this->connection->getQueryBuilder();
|
||||
for ($i = 1; $i < 10; $i++) {
|
||||
$qB->insert('*PREFIX*appconfig')
|
||||
@@ -63,7 +61,7 @@ class QueryBuilderTest extends \Test\TestCase {
|
||||
}
|
||||
}
|
||||
|
||||
protected function getTestingRows(QueryBuilder $queryBuilder): array {
|
||||
protected function getTestingRows(QueryBuilder $queryBuilder) {
|
||||
$queryBuilder->select('configvalue')
|
||||
->from('*PREFIX*appconfig')
|
||||
->where($queryBuilder->expr()->eq(
|
||||
@@ -82,10 +80,7 @@ class QueryBuilderTest extends \Test\TestCase {
|
||||
return $rows;
|
||||
}
|
||||
|
||||
/**
|
||||
* @psalm-param 'testFirstResult'|'testFirstResult1'|'testFirstResult2' $appId
|
||||
*/
|
||||
protected function deleteTestingRows(string $appId = 'testFirstResult'): void {
|
||||
protected function deleteTestingRows($appId = 'testFirstResult') {
|
||||
$qB = $this->connection->getQueryBuilder();
|
||||
|
||||
$qB->delete('*PREFIX*appconfig')
|
||||
@@ -102,8 +97,13 @@ class QueryBuilderTest extends \Test\TestCase {
|
||||
];
|
||||
}
|
||||
|
||||
#[DataProvider(methodName: 'dataFirstResult')]
|
||||
public function testFirstResult(?int $firstResult, array $expectedSet): void {
|
||||
/**
|
||||
*
|
||||
* @param int|null $firstResult
|
||||
* @param array $expectedSet
|
||||
*/
|
||||
#[DataProvider('dataFirstResult')]
|
||||
public function testFirstResult($firstResult, $expectedSet): void {
|
||||
$this->deleteTestingRows();
|
||||
$this->createTestingRows();
|
||||
|
||||
@@ -134,8 +134,13 @@ class QueryBuilderTest extends \Test\TestCase {
|
||||
];
|
||||
}
|
||||
|
||||
#[DataProvider(methodName: 'dataMaxResults')]
|
||||
public function testMaxResults(?int $maxResult, array $expectedSet): void {
|
||||
/**
|
||||
*
|
||||
* @param int $maxResult
|
||||
* @param array $expectedSet
|
||||
*/
|
||||
#[DataProvider('dataMaxResults')]
|
||||
public function testMaxResults($maxResult, $expectedSet): void {
|
||||
$this->deleteTestingRows();
|
||||
$this->createTestingRows();
|
||||
|
||||
@@ -178,7 +183,7 @@ class QueryBuilderTest extends \Test\TestCase {
|
||||
];
|
||||
}
|
||||
|
||||
#[DataProvider(methodName: 'dataSelect')]
|
||||
#[DataProvider('dataSelect')]
|
||||
public function testSelect(array $selectArguments, array $expected, string $expectedLiteral = ''): void {
|
||||
$this->deleteTestingRows();
|
||||
$this->createTestingRows();
|
||||
@@ -186,7 +191,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));
|
||||
}
|
||||
},
|
||||
@@ -227,7 +232,7 @@ class QueryBuilderTest extends \Test\TestCase {
|
||||
];
|
||||
}
|
||||
|
||||
#[DataProvider(methodName: 'dataSelectAlias')]
|
||||
#[DataProvider('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));
|
||||
@@ -238,7 +243,7 @@ class QueryBuilderTest extends \Test\TestCase {
|
||||
|
||||
$this->queryBuilder->selectAlias($select, $alias);
|
||||
|
||||
$this->queryBuilder->from('appconfig')
|
||||
$this->queryBuilder->from('*PREFIX*appconfig')
|
||||
->where($this->queryBuilder->expr()->eq(
|
||||
'appid',
|
||||
$this->queryBuilder->expr()->literal('testFirstResult')
|
||||
@@ -346,7 +351,7 @@ class QueryBuilderTest extends \Test\TestCase {
|
||||
];
|
||||
}
|
||||
|
||||
#[DataProvider(methodName: 'dataAddSelect')]
|
||||
#[DataProvider('dataAddSelect')]
|
||||
public function testAddSelect(array $selectArguments, array $expected, string $expectedLiteral = ''): void {
|
||||
$this->deleteTestingRows();
|
||||
$this->createTestingRows();
|
||||
@@ -354,7 +359,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));
|
||||
}
|
||||
},
|
||||
@@ -401,8 +406,15 @@ class QueryBuilderTest extends \Test\TestCase {
|
||||
];
|
||||
}
|
||||
|
||||
#[DataProvider(methodName: 'dataDelete')]
|
||||
public function testDelete(string $tableName, ?string $tableAlias, array $expectedQueryPart, string $expectedQuery): void {
|
||||
/**
|
||||
*
|
||||
* @param string $tableName
|
||||
* @param string $tableAlias
|
||||
* @param array $expectedQueryPart
|
||||
* @param string $expectedQuery
|
||||
*/
|
||||
#[DataProvider('dataDelete')]
|
||||
public function testDelete($tableName, $tableAlias, $expectedQueryPart, $expectedQuery): void {
|
||||
$this->queryBuilder->delete($tableName, $tableAlias);
|
||||
|
||||
$this->assertSame(
|
||||
@@ -423,8 +435,15 @@ class QueryBuilderTest extends \Test\TestCase {
|
||||
];
|
||||
}
|
||||
|
||||
#[DataProvider(methodName: 'dataUpdate')]
|
||||
public function testUpdate(string $tableName, ?string $tableAlias, array $expectedQueryPart, string $expectedQuery): void {
|
||||
/**
|
||||
*
|
||||
* @param string $tableName
|
||||
* @param string $tableAlias
|
||||
* @param array $expectedQueryPart
|
||||
* @param string $expectedQuery
|
||||
*/
|
||||
#[DataProvider('dataUpdate')]
|
||||
public function testUpdate($tableName, $tableAlias, $expectedQueryPart, $expectedQuery): void {
|
||||
$this->queryBuilder->update($tableName, $tableAlias);
|
||||
|
||||
$this->assertSame(
|
||||
@@ -444,8 +463,14 @@ class QueryBuilderTest extends \Test\TestCase {
|
||||
];
|
||||
}
|
||||
|
||||
#[DataProvider(methodName: 'dataInsert')]
|
||||
public function testInsert(string $tableName, array $expectedQueryPart, string $expectedQuery): void {
|
||||
/**
|
||||
*
|
||||
* @param string $tableName
|
||||
* @param array $expectedQueryPart
|
||||
* @param string $expectedQuery
|
||||
*/
|
||||
#[DataProvider('dataInsert')]
|
||||
public function testInsert($tableName, $expectedQueryPart, $expectedQuery): void {
|
||||
$this->queryBuilder->insert($tableName);
|
||||
|
||||
$this->assertSame(
|
||||
@@ -477,7 +502,7 @@ class QueryBuilderTest extends \Test\TestCase {
|
||||
];
|
||||
}
|
||||
|
||||
#[DataProvider(methodName: 'dataFrom')]
|
||||
#[DataProvider('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);
|
||||
@@ -523,8 +548,17 @@ class QueryBuilderTest extends \Test\TestCase {
|
||||
];
|
||||
}
|
||||
|
||||
#[DataProvider(methodName: 'dataJoin')]
|
||||
public function testJoin(string $fromAlias, string $tableName, ?string $tableAlias, ?string $condition, array $expectedQueryPart, string $expectedQuery): void {
|
||||
/**
|
||||
*
|
||||
* @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 {
|
||||
$this->queryBuilder->from('data1', 'd1');
|
||||
$this->queryBuilder->join(
|
||||
$fromAlias,
|
||||
@@ -544,8 +578,17 @@ class QueryBuilderTest extends \Test\TestCase {
|
||||
);
|
||||
}
|
||||
|
||||
#[DataProvider(methodName: 'dataJoin')]
|
||||
public function testInnerJoin(string $fromAlias, string $tableName, ?string $tableAlias, ?string $condition, array $expectedQueryPart, string $expectedQuery): void {
|
||||
/**
|
||||
*
|
||||
* @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 {
|
||||
$this->queryBuilder->from('data1', 'd1');
|
||||
$this->queryBuilder->innerJoin(
|
||||
$fromAlias,
|
||||
@@ -585,8 +628,17 @@ class QueryBuilderTest extends \Test\TestCase {
|
||||
];
|
||||
}
|
||||
|
||||
#[DataProvider(methodName: 'dataLeftJoin')]
|
||||
public function testLeftJoin(string $fromAlias, string $tableName, ?string $tableAlias, ?string $condition, array $expectedQueryPart, string $expectedQuery): void {
|
||||
/**
|
||||
*
|
||||
* @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 {
|
||||
$this->queryBuilder->from('data1', 'd1');
|
||||
$this->queryBuilder->leftJoin(
|
||||
$fromAlias,
|
||||
@@ -626,8 +678,17 @@ class QueryBuilderTest extends \Test\TestCase {
|
||||
];
|
||||
}
|
||||
|
||||
#[DataProvider(methodName: 'dataRightJoin')]
|
||||
public function testRightJoin(string $fromAlias, string $tableName, ?string $tableAlias, ?string $condition, array $expectedQueryPart, string $expectedQuery): void {
|
||||
/**
|
||||
*
|
||||
* @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 {
|
||||
$this->queryBuilder->from('data1', 'd1');
|
||||
$this->queryBuilder->rightJoin(
|
||||
$fromAlias,
|
||||
@@ -656,8 +717,17 @@ class QueryBuilderTest extends \Test\TestCase {
|
||||
];
|
||||
}
|
||||
|
||||
#[DataProvider(methodName: 'dataSet')]
|
||||
public function testSet(string $partOne1, string|ILiteral|IParameter $partOne2, ?string $partTwo1, ?ILiteral $partTwo2, array $expectedQueryPart, string $expectedQuery): void {
|
||||
/**
|
||||
*
|
||||
* @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 {
|
||||
$this->queryBuilder->update('data');
|
||||
$this->queryBuilder->set($partOne1, $partOne2);
|
||||
if ($partTwo1 !== null) {
|
||||
@@ -682,8 +752,14 @@ class QueryBuilderTest extends \Test\TestCase {
|
||||
];
|
||||
}
|
||||
|
||||
#[DataProvider(methodName: 'dataWhere')]
|
||||
public function testWhere(array $whereArguments, CompositeExpression $expectedQueryPart, string $expectedQuery): void {
|
||||
/**
|
||||
*
|
||||
* @param array $whereArguments
|
||||
* @param array $expectedQueryPart
|
||||
* @param string $expectedQuery
|
||||
*/
|
||||
#[DataProvider('dataWhere')]
|
||||
public function testWhere($whereArguments, $expectedQueryPart, $expectedQuery): void {
|
||||
$this->queryBuilder->select('column');
|
||||
call_user_func_array(
|
||||
[$this->queryBuilder, 'where'],
|
||||
@@ -701,8 +777,14 @@ class QueryBuilderTest extends \Test\TestCase {
|
||||
);
|
||||
}
|
||||
|
||||
#[DataProvider(methodName: 'dataWhere')]
|
||||
public function testAndWhere(array $whereArguments, CompositeExpression $expectedQueryPart, string $expectedQuery): void {
|
||||
/**
|
||||
*
|
||||
* @param array $whereArguments
|
||||
* @param array $expectedQueryPart
|
||||
* @param string $expectedQuery
|
||||
*/
|
||||
#[DataProvider('dataWhere')]
|
||||
public function testAndWhere($whereArguments, $expectedQueryPart, $expectedQuery): void {
|
||||
$this->queryBuilder->select('column');
|
||||
call_user_func_array(
|
||||
[$this->queryBuilder, 'andWhere'],
|
||||
@@ -727,8 +809,14 @@ class QueryBuilderTest extends \Test\TestCase {
|
||||
];
|
||||
}
|
||||
|
||||
#[DataProvider(methodName: 'dataOrWhere')]
|
||||
public function testOrWhere(array $whereArguments, CompositeExpression $expectedQueryPart, string $expectedQuery): void {
|
||||
/**
|
||||
*
|
||||
* @param array $whereArguments
|
||||
* @param array $expectedQueryPart
|
||||
* @param string $expectedQuery
|
||||
*/
|
||||
#[DataProvider('dataOrWhere')]
|
||||
public function testOrWhere($whereArguments, $expectedQueryPart, $expectedQuery): void {
|
||||
$this->queryBuilder->select('column');
|
||||
call_user_func_array(
|
||||
[$this->queryBuilder, 'orWhere'],
|
||||
@@ -753,8 +841,14 @@ class QueryBuilderTest extends \Test\TestCase {
|
||||
];
|
||||
}
|
||||
|
||||
#[DataProvider(methodName: 'dataGroupBy')]
|
||||
public function testGroupBy(array $groupByArguments, array $expectedQueryPart, string $expectedQuery): void {
|
||||
/**
|
||||
*
|
||||
* @param array $groupByArguments
|
||||
* @param array $expectedQueryPart
|
||||
* @param string $expectedQuery
|
||||
*/
|
||||
#[DataProvider('dataGroupBy')]
|
||||
public function testGroupBy($groupByArguments, $expectedQueryPart, $expectedQuery): void {
|
||||
$this->queryBuilder->select('column');
|
||||
call_user_func_array(
|
||||
[$this->queryBuilder, 'groupBy'],
|
||||
@@ -779,8 +873,14 @@ class QueryBuilderTest extends \Test\TestCase {
|
||||
];
|
||||
}
|
||||
|
||||
#[DataProvider(methodName: 'dataAddGroupBy')]
|
||||
public function testAddGroupBy(array $groupByArguments, array $expectedQueryPart, string $expectedQuery): void {
|
||||
/**
|
||||
*
|
||||
* @param array $groupByArguments
|
||||
* @param array $expectedQueryPart
|
||||
* @param string $expectedQuery
|
||||
*/
|
||||
#[DataProvider('dataAddGroupBy')]
|
||||
public function testAddGroupBy($groupByArguments, $expectedQueryPart, $expectedQuery): void {
|
||||
$this->queryBuilder->select('column');
|
||||
$this->queryBuilder->groupBy('column1');
|
||||
call_user_func_array(
|
||||
@@ -805,8 +905,15 @@ class QueryBuilderTest extends \Test\TestCase {
|
||||
];
|
||||
}
|
||||
|
||||
#[DataProvider(methodName: 'dataSetValue')]
|
||||
public function testSetValue(string $column, string $value, array $expectedQueryPart, string $expectedQuery): void {
|
||||
/**
|
||||
*
|
||||
* @param string $column
|
||||
* @param string $value
|
||||
* @param array $expectedQueryPart
|
||||
* @param string $expectedQuery
|
||||
*/
|
||||
#[DataProvider('dataSetValue')]
|
||||
public function testSetValue($column, $value, $expectedQueryPart, $expectedQuery): void {
|
||||
$this->queryBuilder->insert('data');
|
||||
$this->queryBuilder->setValue($column, $value);
|
||||
|
||||
@@ -821,8 +928,15 @@ class QueryBuilderTest extends \Test\TestCase {
|
||||
);
|
||||
}
|
||||
|
||||
#[DataProvider(methodName: 'dataSetValue')]
|
||||
public function testValues(string $column, string $value, array $expectedQueryPart, string $expectedQuery): void {
|
||||
/**
|
||||
*
|
||||
* @param string $column
|
||||
* @param string $value
|
||||
* @param array $expectedQueryPart
|
||||
* @param string $expectedQuery
|
||||
*/
|
||||
#[DataProvider('dataSetValue')]
|
||||
public function testValues($column, $value, $expectedQueryPart, $expectedQuery): void {
|
||||
$this->queryBuilder->insert('data');
|
||||
$this->queryBuilder->values([
|
||||
$column => $value,
|
||||
@@ -856,8 +970,14 @@ class QueryBuilderTest extends \Test\TestCase {
|
||||
];
|
||||
}
|
||||
|
||||
#[DataProvider(methodName: 'dataHaving')]
|
||||
public function testHaving(array $havingArguments, CompositeExpression $expectedQueryPart, string $expectedQuery): void {
|
||||
/**
|
||||
*
|
||||
* @param array $havingArguments
|
||||
* @param array $expectedQueryPart
|
||||
* @param string $expectedQuery
|
||||
*/
|
||||
#[DataProvider('dataHaving')]
|
||||
public function testHaving($havingArguments, $expectedQueryPart, $expectedQuery): void {
|
||||
call_user_func_array(
|
||||
[$this->queryBuilder, 'having'],
|
||||
$havingArguments
|
||||
@@ -891,8 +1011,14 @@ class QueryBuilderTest extends \Test\TestCase {
|
||||
];
|
||||
}
|
||||
|
||||
#[DataProvider(methodName: 'dataAndHaving')]
|
||||
public function testAndHaving(array $havingArguments, CompositeExpression $expectedQueryPart, string $expectedQuery): void {
|
||||
/**
|
||||
*
|
||||
* @param array $havingArguments
|
||||
* @param array $expectedQueryPart
|
||||
* @param string $expectedQuery
|
||||
*/
|
||||
#[DataProvider('dataAndHaving')]
|
||||
public function testAndHaving($havingArguments, $expectedQueryPart, $expectedQuery): void {
|
||||
$this->queryBuilder->having('condition1');
|
||||
call_user_func_array(
|
||||
[$this->queryBuilder, 'andHaving'],
|
||||
@@ -927,8 +1053,14 @@ class QueryBuilderTest extends \Test\TestCase {
|
||||
];
|
||||
}
|
||||
|
||||
#[DataProvider(methodName: 'dataOrHaving')]
|
||||
public function testOrHaving(array $havingArguments, CompositeExpression $expectedQueryPart, string $expectedQuery): void {
|
||||
/**
|
||||
*
|
||||
* @param array $havingArguments
|
||||
* @param array $expectedQueryPart
|
||||
* @param string $expectedQuery
|
||||
*/
|
||||
#[DataProvider('dataOrHaving')]
|
||||
public function testOrHaving($havingArguments, $expectedQueryPart, $expectedQuery): void {
|
||||
$this->queryBuilder->having('condition1');
|
||||
call_user_func_array(
|
||||
[$this->queryBuilder, 'orHaving'],
|
||||
@@ -955,10 +1087,14 @@ class QueryBuilderTest extends \Test\TestCase {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|'ASC'|'DESC'|null $order
|
||||
*
|
||||
* @param string $sort
|
||||
* @param string $order
|
||||
* @param array $expectedQueryPart
|
||||
* @param string $expectedQuery
|
||||
*/
|
||||
#[DataProvider(methodName: 'dataOrderBy')]
|
||||
public function testOrderBy(string $sort, ?string $order, array $expectedQueryPart, string $expectedQuery): void {
|
||||
#[DataProvider('dataOrderBy')]
|
||||
public function testOrderBy($sort, $order, $expectedQueryPart, $expectedQuery): void {
|
||||
$this->queryBuilder->orderBy($sort, $order);
|
||||
|
||||
$this->assertEquals(
|
||||
@@ -987,11 +1123,15 @@ class QueryBuilderTest extends \Test\TestCase {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|'ASC'|'DESC'|null $order2
|
||||
* @param string|'ASC'|'DESC'|null $order1
|
||||
*
|
||||
* @param string $sort2
|
||||
* @param string $order2
|
||||
* @param string $order1
|
||||
* @param array $expectedQueryPart
|
||||
* @param string $expectedQuery
|
||||
*/
|
||||
#[DataProvider(methodName: 'dataAddOrderBy')]
|
||||
public function testAddOrderBy(string $sort2, ?string $order2, ?string $order1, array $expectedQueryPart, string $expectedQuery): void {
|
||||
#[DataProvider('dataAddOrderBy')]
|
||||
public function testAddOrderBy($sort2, $order2, $order1, $expectedQueryPart, $expectedQuery): void {
|
||||
$this->queryBuilder->orderBy('column1', $order1);
|
||||
$this->queryBuilder->addOrderBy($sort2, $order2);
|
||||
|
||||
@@ -1012,7 +1152,7 @@ class QueryBuilderTest extends \Test\TestCase {
|
||||
try {
|
||||
$qB->getLastInsertId();
|
||||
$this->fail('getLastInsertId() should throw an exception, when being called before insert()');
|
||||
} catch (BadMethodCallException) {
|
||||
} catch (\BadMethodCallException $e) {
|
||||
$this->addToAssertionCount(1);
|
||||
}
|
||||
|
||||
@@ -1027,6 +1167,8 @@ 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')
|
||||
@@ -1036,7 +1178,7 @@ class QueryBuilderTest extends \Test\TestCase {
|
||||
try {
|
||||
$qB->getLastInsertId();
|
||||
$this->fail('getLastInsertId() should throw an exception, when being called after delete()');
|
||||
} catch (BadMethodCallException) {
|
||||
} catch (\BadMethodCallException $e) {
|
||||
$this->addToAssertionCount(1);
|
||||
}
|
||||
}
|
||||
@@ -1057,7 +1199,7 @@ class QueryBuilderTest extends \Test\TestCase {
|
||||
];
|
||||
}
|
||||
|
||||
#[DataProvider(methodName: 'dataGetTableName')]
|
||||
#[DataProvider('dataGetTableName')]
|
||||
public function testGetTableName(string $tableName, ?bool $automatic, string $expected): void {
|
||||
if ($tableName === 'function') {
|
||||
$tableName = $this->queryBuilder->createFunction('(' . $this->queryBuilder->select('*')->from('table')->getSQL() . ')');
|
||||
@@ -1080,7 +1222,7 @@ class QueryBuilderTest extends \Test\TestCase {
|
||||
];
|
||||
}
|
||||
|
||||
#[DataProvider(methodName: 'dataGetColumnName')]
|
||||
#[DataProvider('dataGetColumnName')]
|
||||
public function testGetColumnName(string $column, string $prefix, string $expected): void {
|
||||
$this->assertSame(
|
||||
$expected,
|
||||
@@ -1088,7 +1230,7 @@ class QueryBuilderTest extends \Test\TestCase {
|
||||
);
|
||||
}
|
||||
|
||||
private function getConnection(): ConnectionAdapter&MockObject {
|
||||
private function getConnection(): MockObject&ConnectionAdapter {
|
||||
$connection = $this->createMock(ConnectionAdapter::class);
|
||||
$connection->method('executeStatement')
|
||||
->willReturn(3);
|
||||
@@ -1099,9 +1241,6 @@ 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('');
|
||||
@@ -1127,9 +1266,6 @@ 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')
|
||||
@@ -1171,9 +1307,6 @@ 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')
|
||||
@@ -1209,9 +1342,6 @@ 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')
|
||||
|
||||
@@ -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 {
|
||||
protected QuoteHelper $helper;
|
||||
/** @var QuoteHelper */
|
||||
protected $helper;
|
||||
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
@@ -37,8 +37,12 @@ class QuoteHelperTest extends \Test\TestCase {
|
||||
];
|
||||
}
|
||||
|
||||
#[DataProvider(methodName: 'dataQuoteColumnName')]
|
||||
public function testQuoteColumnName(string|Literal|Parameter $input, string $expected): void {
|
||||
/**
|
||||
* @param mixed $input
|
||||
* @param string $expected
|
||||
*/
|
||||
#[\PHPUnit\Framework\Attributes\DataProvider('dataQuoteColumnName')]
|
||||
public function testQuoteColumnName($input, $expected): void {
|
||||
$this->assertSame(
|
||||
$expected,
|
||||
$this->helper->quoteColumnName($input)
|
||||
@@ -68,15 +72,23 @@ class QuoteHelperTest extends \Test\TestCase {
|
||||
];
|
||||
}
|
||||
|
||||
#[DataProvider(methodName: 'dataQuoteColumnNames')]
|
||||
public function testQuoteColumnNames(string|Literal|Parameter|array $input, string|array $expected): void {
|
||||
/**
|
||||
* @param mixed $input
|
||||
* @param string $expected
|
||||
*/
|
||||
#[\PHPUnit\Framework\Attributes\DataProvider('dataQuoteColumnNames')]
|
||||
public function testQuoteColumnNames($input, $expected): void {
|
||||
$this->assertSame(
|
||||
$expected,
|
||||
$this->helper->quoteColumnNames($input)
|
||||
);
|
||||
}
|
||||
|
||||
public function quoteColumnNames(array|string|ILiteral|IParameter $strings): array|string {
|
||||
/**
|
||||
* @param array|string|ILiteral|IParameter $strings string, Literal or Parameter
|
||||
* @return array|string
|
||||
*/
|
||||
public function quoteColumnNames($strings) {
|
||||
if (!is_array($strings)) {
|
||||
return $this->quoteColumnName($strings);
|
||||
}
|
||||
@@ -89,9 +101,25 @@ class QuoteHelperTest extends \Test\TestCase {
|
||||
return $return;
|
||||
}
|
||||
|
||||
public function quoteColumnName(string|ILiteral|IParameter $string): string {
|
||||
if ($string instanceof ILiteral || $string instanceof IParameter) {
|
||||
return (string)$string;
|
||||
/**
|
||||
* @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');
|
||||
}
|
||||
|
||||
if (substr_count($string, '.')) {
|
||||
|
||||
@@ -17,10 +17,9 @@ 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;
|
||||
|
||||
#[Group(name: 'DB')]
|
||||
#[\PHPUnit\Framework\Attributes\Group('DB')]
|
||||
class SharedQueryBuilderTest extends TestCase {
|
||||
private IDBConnection $connection;
|
||||
private AutoIncrementHandler $autoIncrementHandler;
|
||||
|
||||
@@ -95,6 +95,7 @@ class FactoryTest extends TestCase {
|
||||
return [
|
||||
'null shortcut' => [null, null],
|
||||
'default language' => ['de', 'de'],
|
||||
'regional language' => ['de_DE', 'de_DE'],
|
||||
'malicious language' => ['de/../fr', 'defr'],
|
||||
'request language' => ['kab;q=0.8,ka;q=0.7,de;q=0.6', 'kab;q=0.8,ka;q=0.7,de;q=0.6'],
|
||||
];
|
||||
|
||||
Reference in New Issue
Block a user