aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--apps/contactsinteraction/lib/Db/RecentContact.php13
-rw-r--r--apps/dav/lib/CalDAV/Proxy/Proxy.php7
-rw-r--r--apps/dav/lib/Db/Direct.php9
-rw-r--r--apps/federatedfilesharing/lib/FederatedShareProvider.php4
-rw-r--r--apps/files_reminders/lib/Db/ReminderMapper.php2
-rw-r--r--apps/oauth2/lib/Db/AccessToken.php11
-rw-r--r--apps/oauth2/lib/Db/Client.php3
-rw-r--r--apps/sharebymail/lib/ShareByMailProvider.php8
-rw-r--r--apps/user_status/lib/Db/UserStatus.php7
-rw-r--r--core/Db/LoginFlowV2.php5
-rw-r--r--lib/private/Authentication/Token/PublicKeyToken.php15
-rw-r--r--lib/private/Comments/Manager.php20
-rw-r--r--lib/private/DB/QueryBuilder/ExpressionBuilder/SqliteExpressionBuilder.php24
-rw-r--r--lib/private/Security/RateLimiting/Backend/DatabaseBackend.php4
-rw-r--r--lib/private/Share20/DefaultShareProvider.php8
-rw-r--r--lib/private/TextToImage/Db/TaskMapper.php4
-rw-r--r--lib/private/Updater/Changes.php3
-rw-r--r--lib/public/AppFramework/Db/Entity.php56
-rw-r--r--lib/public/AppFramework/Db/QBMapper.php30
-rw-r--r--lib/public/DB/QueryBuilder/IQueryBuilder.php52
-rw-r--r--lib/public/DB/Types.php77
-rw-r--r--lib/public/Migration/Attributes/ColumnType.php18
-rw-r--r--tests/lib/AppFramework/Db/EntityTest.php44
-rw-r--r--tests/lib/AppFramework/Db/QBMapperDBTest.php159
-rw-r--r--tests/lib/AppFramework/Db/QBMapperTest.php63
-rw-r--r--tests/lib/Comments/ManagerTest.php7
-rw-r--r--tests/lib/DB/QueryBuilder/ExpressionBuilderDBTest.php16
-rw-r--r--tests/lib/Share20/DefaultShareProviderTest.php2
-rw-r--r--tests/lib/Share20/ShareByMailProviderTest.php2
29 files changed, 527 insertions, 146 deletions
diff --git a/apps/contactsinteraction/lib/Db/RecentContact.php b/apps/contactsinteraction/lib/Db/RecentContact.php
index 581b4b292ca..fc6ec3cc249 100644
--- a/apps/contactsinteraction/lib/Db/RecentContact.php
+++ b/apps/contactsinteraction/lib/Db/RecentContact.php
@@ -9,6 +9,7 @@ declare(strict_types=1);
namespace OCA\ContactsInteraction\Db;
use OCP\AppFramework\Db\Entity;
+use OCP\DB\Types;
/**
* @method void setActorUid(string $uid)
@@ -33,11 +34,11 @@ class RecentContact extends Entity {
protected int $lastContact = -1;
public function __construct() {
- $this->addType('actorUid', 'string');
- $this->addType('uid', 'string');
- $this->addType('email', 'string');
- $this->addType('federatedCloudId', 'string');
- $this->addType('card', 'blob');
- $this->addType('lastContact', 'int');
+ $this->addType('actorUid', Types::STRING);
+ $this->addType('uid', Types::STRING);
+ $this->addType('email', Types::STRING);
+ $this->addType('federatedCloudId', Types::STRING);
+ $this->addType('card', Types::BLOB);
+ $this->addType('lastContact', Types::INTEGER);
}
}
diff --git a/apps/dav/lib/CalDAV/Proxy/Proxy.php b/apps/dav/lib/CalDAV/Proxy/Proxy.php
index a10f82e4b29..ef1ad8c634f 100644
--- a/apps/dav/lib/CalDAV/Proxy/Proxy.php
+++ b/apps/dav/lib/CalDAV/Proxy/Proxy.php
@@ -9,6 +9,7 @@ declare(strict_types=1);
namespace OCA\DAV\CalDAV\Proxy;
use OCP\AppFramework\Db\Entity;
+use OCP\DB\Types;
/**
* @method string getOwnerId()
@@ -28,8 +29,8 @@ class Proxy extends Entity {
protected $permissions;
public function __construct() {
- $this->addType('ownerId', 'string');
- $this->addType('proxyId', 'string');
- $this->addType('permissions', 'int');
+ $this->addType('ownerId', Types::STRING);
+ $this->addType('proxyId', Types::STRING);
+ $this->addType('permissions', Types::INTEGER);
}
}
diff --git a/apps/dav/lib/Db/Direct.php b/apps/dav/lib/Db/Direct.php
index 377a120c436..4e4a12d225f 100644
--- a/apps/dav/lib/Db/Direct.php
+++ b/apps/dav/lib/Db/Direct.php
@@ -9,6 +9,7 @@ declare(strict_types=1);
namespace OCA\DAV\Db;
use OCP\AppFramework\Db\Entity;
+use OCP\DB\Types;
/**
* @method string getUserId()
@@ -34,9 +35,9 @@ class Direct extends Entity {
protected $expiration;
public function __construct() {
- $this->addType('userId', 'string');
- $this->addType('fileId', 'int');
- $this->addType('token', 'string');
- $this->addType('expiration', 'int');
+ $this->addType('userId', Types::STRING);
+ $this->addType('fileId', Types::INTEGER);
+ $this->addType('token', Types::STRING);
+ $this->addType('expiration', Types::INTEGER);
}
}
diff --git a/apps/federatedfilesharing/lib/FederatedShareProvider.php b/apps/federatedfilesharing/lib/FederatedShareProvider.php
index da55b9a03d7..c115c613338 100644
--- a/apps/federatedfilesharing/lib/FederatedShareProvider.php
+++ b/apps/federatedfilesharing/lib/FederatedShareProvider.php
@@ -303,7 +303,7 @@ class FederatedShareProvider implements IShareProvider {
->setValue('uid_owner', $qb->createNamedParameter($uidOwner))
->setValue('uid_initiator', $qb->createNamedParameter($sharedBy))
->setValue('permissions', $qb->createNamedParameter($permissions))
- ->setValue('expiration', $qb->createNamedParameter($expirationDate, IQueryBuilder::PARAM_DATE))
+ ->setValue('expiration', $qb->createNamedParameter($expirationDate, IQueryBuilder::PARAM_DATETIME_MUTABLE))
->setValue('token', $qb->createNamedParameter($token))
->setValue('stime', $qb->createNamedParameter(time()));
@@ -333,7 +333,7 @@ class FederatedShareProvider implements IShareProvider {
->set('permissions', $qb->createNamedParameter($share->getPermissions()))
->set('uid_owner', $qb->createNamedParameter($share->getShareOwner()))
->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy()))
- ->set('expiration', $qb->createNamedParameter($share->getExpirationDate(), IQueryBuilder::PARAM_DATE))
+ ->set('expiration', $qb->createNamedParameter($share->getExpirationDate(), IQueryBuilder::PARAM_DATETIME_MUTABLE))
->executeStatement();
// send the updated permission to the owner/initiator, if they are not the same
diff --git a/apps/files_reminders/lib/Db/ReminderMapper.php b/apps/files_reminders/lib/Db/ReminderMapper.php
index 16859585bdf..e747c8af397 100644
--- a/apps/files_reminders/lib/Db/ReminderMapper.php
+++ b/apps/files_reminders/lib/Db/ReminderMapper.php
@@ -135,7 +135,7 @@ class ReminderMapper extends QBMapper {
$qb->select('id', 'user_id', 'file_id', 'due_date', 'updated_at', 'created_at', 'notified')
->from($this->getTableName())
->where($qb->expr()->eq('notified', $qb->createNamedParameter(true, IQueryBuilder::PARAM_BOOL)))
- ->andWhere($qb->expr()->lt('due_date', $qb->createNamedParameter($buffer, IQueryBuilder::PARAM_DATE)))
+ ->andWhere($qb->expr()->lt('due_date', $qb->createNamedParameter($buffer, IQueryBuilder::PARAM_DATETIME_MUTABLE)))
->orderBy('due_date', 'ASC')
->setMaxResults($limit);
diff --git a/apps/oauth2/lib/Db/AccessToken.php b/apps/oauth2/lib/Db/AccessToken.php
index 543d512e0ce..f9c0e6de51e 100644
--- a/apps/oauth2/lib/Db/AccessToken.php
+++ b/apps/oauth2/lib/Db/AccessToken.php
@@ -6,6 +6,7 @@
namespace OCA\OAuth2\Db;
use OCP\AppFramework\Db\Entity;
+use OCP\DB\Types;
/**
* @method int getTokenId()
@@ -36,12 +37,12 @@ class AccessToken extends Entity {
protected $tokenCount;
public function __construct() {
- $this->addType('id', 'int');
- $this->addType('tokenId', 'int');
- $this->addType('clientId', 'int');
+ $this->addType('id', Types::INTEGER);
+ $this->addType('tokenId', Types::INTEGER);
+ $this->addType('clientId', Types::INTEGER);
$this->addType('hashedCode', 'string');
$this->addType('encryptedToken', 'string');
- $this->addType('codeCreatedAt', 'int');
- $this->addType('tokenCount', 'int');
+ $this->addType('codeCreatedAt', Types::INTEGER);
+ $this->addType('tokenCount', Types::INTEGER);
}
}
diff --git a/apps/oauth2/lib/Db/Client.php b/apps/oauth2/lib/Db/Client.php
index c48ca4edfc0..458d85b24ab 100644
--- a/apps/oauth2/lib/Db/Client.php
+++ b/apps/oauth2/lib/Db/Client.php
@@ -6,6 +6,7 @@
namespace OCA\OAuth2\Db;
use OCP\AppFramework\Db\Entity;
+use OCP\DB\Types;
/**
* @method string getClientIdentifier()
@@ -28,7 +29,7 @@ class Client extends Entity {
protected $secret;
public function __construct() {
- $this->addType('id', 'int');
+ $this->addType('id', Types::INTEGER);
$this->addType('name', 'string');
$this->addType('redirectUri', 'string');
$this->addType('clientIdentifier', 'string');
diff --git a/apps/sharebymail/lib/ShareByMailProvider.php b/apps/sharebymail/lib/ShareByMailProvider.php
index 3ec13dd3a08..9efc0ec2513 100644
--- a/apps/sharebymail/lib/ShareByMailProvider.php
+++ b/apps/sharebymail/lib/ShareByMailProvider.php
@@ -699,7 +699,7 @@ class ShareByMailProvider extends DefaultShareProvider implements IShareProvider
->setValue('permissions', $qb->createNamedParameter($permissions))
->setValue('token', $qb->createNamedParameter($token))
->setValue('password', $qb->createNamedParameter($password))
- ->setValue('password_expiration_time', $qb->createNamedParameter($passwordExpirationTime, IQueryBuilder::PARAM_DATE))
+ ->setValue('password_expiration_time', $qb->createNamedParameter($passwordExpirationTime, IQueryBuilder::PARAM_DATETIME_MUTABLE))
->setValue('password_by_talk', $qb->createNamedParameter($sendPasswordByTalk, IQueryBuilder::PARAM_BOOL))
->setValue('stime', $qb->createNamedParameter(time()))
->setValue('hide_download', $qb->createNamedParameter((int)$hideDownload, IQueryBuilder::PARAM_INT))
@@ -712,7 +712,7 @@ class ShareByMailProvider extends DefaultShareProvider implements IShareProvider
$qb->setValue('attributes', $qb->createNamedParameter($shareAttributes));
if ($expirationTime !== null) {
- $qb->setValue('expiration', $qb->createNamedParameter($expirationTime, IQueryBuilder::PARAM_DATE));
+ $qb->setValue('expiration', $qb->createNamedParameter($expirationTime, IQueryBuilder::PARAM_DATETIME_MUTABLE));
}
$qb->executeStatement();
@@ -752,10 +752,10 @@ class ShareByMailProvider extends DefaultShareProvider implements IShareProvider
->set('uid_owner', $qb->createNamedParameter($share->getShareOwner()))
->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy()))
->set('password', $qb->createNamedParameter($share->getPassword()))
- ->set('password_expiration_time', $qb->createNamedParameter($share->getPasswordExpirationTime(), IQueryBuilder::PARAM_DATE))
+ ->set('password_expiration_time', $qb->createNamedParameter($share->getPasswordExpirationTime(), IQueryBuilder::PARAM_DATETIME_MUTABLE))
->set('label', $qb->createNamedParameter($share->getLabel()))
->set('password_by_talk', $qb->createNamedParameter($share->getSendPasswordByTalk(), IQueryBuilder::PARAM_BOOL))
- ->set('expiration', $qb->createNamedParameter($share->getExpirationDate(), IQueryBuilder::PARAM_DATE))
+ ->set('expiration', $qb->createNamedParameter($share->getExpirationDate(), IQueryBuilder::PARAM_DATETIME_MUTABLE))
->set('note', $qb->createNamedParameter($share->getNote()))
->set('hide_download', $qb->createNamedParameter((int)$share->getHideDownload(), IQueryBuilder::PARAM_INT))
->set('attributes', $qb->createNamedParameter($shareAttributes))
diff --git a/apps/user_status/lib/Db/UserStatus.php b/apps/user_status/lib/Db/UserStatus.php
index 1be35830853..b2da4a9e07a 100644
--- a/apps/user_status/lib/Db/UserStatus.php
+++ b/apps/user_status/lib/Db/UserStatus.php
@@ -9,6 +9,7 @@ declare(strict_types=1);
namespace OCA\UserStatus\Db;
use OCP\AppFramework\Db\Entity;
+use OCP\DB\Types;
/**
* Class UserStatus
@@ -73,13 +74,13 @@ class UserStatus extends Entity {
public function __construct() {
$this->addType('userId', 'string');
$this->addType('status', 'string');
- $this->addType('statusTimestamp', 'int');
+ $this->addType('statusTimestamp', Types::INTEGER);
$this->addType('isUserDefined', 'boolean');
$this->addType('messageId', 'string');
$this->addType('customIcon', 'string');
$this->addType('customMessage', 'string');
- $this->addType('clearAt', 'int');
+ $this->addType('clearAt', Types::INTEGER);
$this->addType('isBackup', 'boolean');
- $this->addType('statusMessageTimestamp', 'int');
+ $this->addType('statusMessageTimestamp', Types::INTEGER);
}
}
diff --git a/core/Db/LoginFlowV2.php b/core/Db/LoginFlowV2.php
index 90b6f41a626..1671e5990e0 100644
--- a/core/Db/LoginFlowV2.php
+++ b/core/Db/LoginFlowV2.php
@@ -9,6 +9,7 @@ declare(strict_types=1);
namespace OC\Core\Db;
use OCP\AppFramework\Db\Entity;
+use OCP\DB\Types;
/**
* @method int getTimestamp()
@@ -55,8 +56,8 @@ class LoginFlowV2 extends Entity {
protected $appPassword;
public function __construct() {
- $this->addType('timestamp', 'int');
- $this->addType('started', 'int');
+ $this->addType('timestamp', Types::INTEGER);
+ $this->addType('started', Types::INTEGER);
$this->addType('pollToken', 'string');
$this->addType('loginToken', 'string');
$this->addType('publicKey', 'string');
diff --git a/lib/private/Authentication/Token/PublicKeyToken.php b/lib/private/Authentication/Token/PublicKeyToken.php
index 961b7191d84..be427ab4839 100644
--- a/lib/private/Authentication/Token/PublicKeyToken.php
+++ b/lib/private/Authentication/Token/PublicKeyToken.php
@@ -10,6 +10,7 @@ namespace OC\Authentication\Token;
use OCP\AppFramework\Db\Entity;
use OCP\Authentication\Token\IToken;
+use OCP\DB\Types;
/**
* @method void setId(int $id)
@@ -88,16 +89,16 @@ class PublicKeyToken extends Entity implements INamedToken, IWipeableToken {
$this->addType('passwordHash', 'string');
$this->addType('name', 'string');
$this->addType('token', 'string');
- $this->addType('type', 'int');
- $this->addType('remember', 'int');
- $this->addType('lastActivity', 'int');
- $this->addType('lastCheck', 'int');
+ $this->addType('type', Types::INTEGER);
+ $this->addType('remember', Types::INTEGER);
+ $this->addType('lastActivity', Types::INTEGER);
+ $this->addType('lastCheck', Types::INTEGER);
$this->addType('scope', 'string');
- $this->addType('expires', 'int');
+ $this->addType('expires', Types::INTEGER);
$this->addType('publicKey', 'string');
$this->addType('privateKey', 'string');
- $this->addType('version', 'int');
- $this->addType('passwordInvalid', 'bool');
+ $this->addType('version', Types::INTEGER);
+ $this->addType('passwordInvalid', Types::BOOLEAN);
}
public function getId(): int {
diff --git a/lib/private/Comments/Manager.php b/lib/private/Comments/Manager.php
index 41e0c662212..d959ebcb0a3 100644
--- a/lib/private/Comments/Manager.php
+++ b/lib/private/Comments/Manager.php
@@ -440,14 +440,14 @@ class Manager implements ICommentsManager {
$query->expr()->orX(
$query->expr()->lt(
'creation_timestamp',
- $query->createNamedParameter($lastKnownCommentDateTime, IQueryBuilder::PARAM_DATE),
- IQueryBuilder::PARAM_DATE
+ $query->createNamedParameter($lastKnownCommentDateTime, IQueryBuilder::PARAM_DATETIME_MUTABLE),
+ IQueryBuilder::PARAM_DATETIME_MUTABLE
),
$query->expr()->andX(
$query->expr()->eq(
'creation_timestamp',
- $query->createNamedParameter($lastKnownCommentDateTime, IQueryBuilder::PARAM_DATE),
- IQueryBuilder::PARAM_DATE
+ $query->createNamedParameter($lastKnownCommentDateTime, IQueryBuilder::PARAM_DATETIME_MUTABLE),
+ IQueryBuilder::PARAM_DATETIME_MUTABLE
),
$idComparison
)
@@ -463,14 +463,14 @@ class Manager implements ICommentsManager {
$query->expr()->orX(
$query->expr()->gt(
'creation_timestamp',
- $query->createNamedParameter($lastKnownCommentDateTime, IQueryBuilder::PARAM_DATE),
- IQueryBuilder::PARAM_DATE
+ $query->createNamedParameter($lastKnownCommentDateTime, IQueryBuilder::PARAM_DATETIME_MUTABLE),
+ IQueryBuilder::PARAM_DATETIME_MUTABLE
),
$query->expr()->andX(
$query->expr()->eq(
'creation_timestamp',
- $query->createNamedParameter($lastKnownCommentDateTime, IQueryBuilder::PARAM_DATE),
- IQueryBuilder::PARAM_DATE
+ $query->createNamedParameter($lastKnownCommentDateTime, IQueryBuilder::PARAM_DATETIME_MUTABLE),
+ IQueryBuilder::PARAM_DATETIME_MUTABLE
),
$idComparison
)
@@ -740,7 +740,7 @@ class Manager implements ICommentsManager {
->from('comments')
->where($query->expr()->eq('object_type', $query->createNamedParameter($objectType)))
->andWhere($query->expr()->eq('object_id', $query->createNamedParameter($objectId)))
- ->andWhere($query->expr()->lt('creation_timestamp', $query->createNamedParameter($beforeDate, IQueryBuilder::PARAM_DATE)))
+ ->andWhere($query->expr()->lt('creation_timestamp', $query->createNamedParameter($beforeDate, IQueryBuilder::PARAM_DATETIME_MUTABLE)))
->orderBy('creation_timestamp', 'desc');
if ($verb !== '') {
@@ -1551,7 +1551,7 @@ class Manager implements ICommentsManager {
$qb = $this->dbConn->getQueryBuilder();
$qb->delete('comments')
->where($qb->expr()->lte('expire_date',
- $qb->createNamedParameter($this->timeFactory->getDateTime(), IQueryBuilder::PARAM_DATE)))
+ $qb->createNamedParameter($this->timeFactory->getDateTime(), IQueryBuilder::PARAM_DATETIME_MUTABLE)))
->andWhere($qb->expr()->eq('object_type', $qb->createNamedParameter($objectType)));
if ($objectId !== '') {
diff --git a/lib/private/DB/QueryBuilder/ExpressionBuilder/SqliteExpressionBuilder.php b/lib/private/DB/QueryBuilder/ExpressionBuilder/SqliteExpressionBuilder.php
index ac4698cdc76..559c29df208 100644
--- a/lib/private/DB/QueryBuilder/ExpressionBuilder/SqliteExpressionBuilder.php
+++ b/lib/private/DB/QueryBuilder/ExpressionBuilder/SqliteExpressionBuilder.php
@@ -29,7 +29,11 @@ class SqliteExpressionBuilder extends ExpressionBuilder {
* @return array|IQueryFunction|string
*/
protected function prepareColumn($column, $type) {
- if ($type === IQueryBuilder::PARAM_DATE && !is_array($column) && !($column instanceof IParameter) && !($column instanceof ILiteral)) {
+ if ($type !== null
+ && !is_array($column)
+ && !($column instanceof IParameter)
+ && !($column instanceof ILiteral)
+ && (str_starts_with($type, 'date') || str_starts_with($type, 'time'))) {
return $this->castColumn($column, $type);
}
@@ -44,9 +48,21 @@ class SqliteExpressionBuilder extends ExpressionBuilder {
* @return IQueryFunction
*/
public function castColumn($column, $type): IQueryFunction {
- if ($type === IQueryBuilder::PARAM_DATE) {
- $column = $this->helper->quoteColumnName($column);
- return new QueryFunction('DATETIME(' . $column . ')');
+ switch ($type) {
+ case IQueryBuilder::PARAM_DATE_MUTABLE:
+ case IQueryBuilder::PARAM_DATE_IMMUTABLE:
+ $column = $this->helper->quoteColumnName($column);
+ return new QueryFunction('DATE(' . $column . ')');
+ case IQueryBuilder::PARAM_DATETIME_MUTABLE:
+ case IQueryBuilder::PARAM_DATETIME_IMMUTABLE:
+ case IQueryBuilder::PARAM_DATETIME_TZ_MUTABLE:
+ case IQueryBuilder::PARAM_DATETIME_TZ_IMMUTABLE:
+ $column = $this->helper->quoteColumnName($column);
+ return new QueryFunction('DATETIME(' . $column . ')');
+ case IQueryBuilder::PARAM_TIME_MUTABLE:
+ case IQueryBuilder::PARAM_TIME_IMMUTABLE:
+ $column = $this->helper->quoteColumnName($column);
+ return new QueryFunction('TIME(' . $column . ')');
}
return parent::castColumn($column, $type);
diff --git a/lib/private/Security/RateLimiting/Backend/DatabaseBackend.php b/lib/private/Security/RateLimiting/Backend/DatabaseBackend.php
index 8adcd8e168c..9fb237f2f72 100644
--- a/lib/private/Security/RateLimiting/Backend/DatabaseBackend.php
+++ b/lib/private/Security/RateLimiting/Backend/DatabaseBackend.php
@@ -42,7 +42,7 @@ class DatabaseBackend implements IBackend {
$qb = $this->dbConnection->getQueryBuilder();
$qb->delete(self::TABLE_NAME)
->where(
- $qb->expr()->lte('delete_after', $qb->createNamedParameter($currentTime, IQueryBuilder::PARAM_DATE))
+ $qb->expr()->lte('delete_after', $qb->createNamedParameter($currentTime, IQueryBuilder::PARAM_DATETIME_MUTABLE))
)
->executeStatement();
@@ -87,7 +87,7 @@ class DatabaseBackend implements IBackend {
$qb->insert(self::TABLE_NAME)
->values([
'hash' => $qb->createNamedParameter($identifier, IQueryBuilder::PARAM_STR),
- 'delete_after' => $qb->createNamedParameter($deleteAfter, IQueryBuilder::PARAM_DATE),
+ 'delete_after' => $qb->createNamedParameter($deleteAfter, IQueryBuilder::PARAM_DATETIME_MUTABLE),
]);
if (!$this->config->getSystemValueBool('ratelimit.protection.enabled', true)) {
diff --git a/lib/private/Share20/DefaultShareProvider.php b/lib/private/Share20/DefaultShareProvider.php
index 2ad926fbaa7..db2734190e4 100644
--- a/lib/private/Share20/DefaultShareProvider.php
+++ b/lib/private/Share20/DefaultShareProvider.php
@@ -222,7 +222,7 @@ class DefaultShareProvider implements IShareProviderWithNotification, IShareProv
->set('attributes', $qb->createNamedParameter($shareAttributes))
->set('item_source', $qb->createNamedParameter($share->getNode()->getId()))
->set('file_source', $qb->createNamedParameter($share->getNode()->getId()))
- ->set('expiration', $qb->createNamedParameter($expirationDate, IQueryBuilder::PARAM_DATE))
+ ->set('expiration', $qb->createNamedParameter($expirationDate, IQueryBuilder::PARAM_DATETIME_MUTABLE))
->set('note', $qb->createNamedParameter($share->getNote()))
->set('accepted', $qb->createNamedParameter($share->getStatus()))
->set('reminder_sent', $qb->createNamedParameter($share->getReminderSent(), IQueryBuilder::PARAM_BOOL))
@@ -237,7 +237,7 @@ class DefaultShareProvider implements IShareProviderWithNotification, IShareProv
->set('attributes', $qb->createNamedParameter($shareAttributes))
->set('item_source', $qb->createNamedParameter($share->getNode()->getId()))
->set('file_source', $qb->createNamedParameter($share->getNode()->getId()))
- ->set('expiration', $qb->createNamedParameter($expirationDate, IQueryBuilder::PARAM_DATE))
+ ->set('expiration', $qb->createNamedParameter($expirationDate, IQueryBuilder::PARAM_DATETIME_MUTABLE))
->set('note', $qb->createNamedParameter($share->getNote()))
->executeStatement();
@@ -252,7 +252,7 @@ class DefaultShareProvider implements IShareProviderWithNotification, IShareProv
->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy()))
->set('item_source', $qb->createNamedParameter($share->getNode()->getId()))
->set('file_source', $qb->createNamedParameter($share->getNode()->getId()))
- ->set('expiration', $qb->createNamedParameter($expirationDate, IQueryBuilder::PARAM_DATE))
+ ->set('expiration', $qb->createNamedParameter($expirationDate, IQueryBuilder::PARAM_DATETIME_MUTABLE))
->set('note', $qb->createNamedParameter($share->getNote()))
->executeStatement();
@@ -279,7 +279,7 @@ class DefaultShareProvider implements IShareProviderWithNotification, IShareProv
->set('item_source', $qb->createNamedParameter($share->getNode()->getId()))
->set('file_source', $qb->createNamedParameter($share->getNode()->getId()))
->set('token', $qb->createNamedParameter($share->getToken()))
- ->set('expiration', $qb->createNamedParameter($expirationDate, IQueryBuilder::PARAM_DATE))
+ ->set('expiration', $qb->createNamedParameter($expirationDate, IQueryBuilder::PARAM_DATETIME_MUTABLE))
->set('note', $qb->createNamedParameter($share->getNote()))
->set('label', $qb->createNamedParameter($share->getLabel()))
->set('hide_download', $qb->createNamedParameter($share->getHideDownload() ? 1 : 0), IQueryBuilder::PARAM_INT)
diff --git a/lib/private/TextToImage/Db/TaskMapper.php b/lib/private/TextToImage/Db/TaskMapper.php
index b34c749eb66..37f492e14cf 100644
--- a/lib/private/TextToImage/Db/TaskMapper.php
+++ b/lib/private/TextToImage/Db/TaskMapper.php
@@ -95,11 +95,11 @@ class TaskMapper extends QBMapper {
$qb = $this->db->getQueryBuilder();
$qb->select('*')
->from($this->tableName)
- ->where($qb->expr()->lt('last_updated', $qb->createPositionalParameter($datetime, IQueryBuilder::PARAM_DATE)));
+ ->where($qb->expr()->lt('last_updated', $qb->createPositionalParameter($datetime, IQueryBuilder::PARAM_DATETIME_MUTABLE)));
$deletedTasks = $this->findEntities($qb);
$qb = $this->db->getQueryBuilder();
$qb->delete($this->tableName)
- ->where($qb->expr()->lt('last_updated', $qb->createPositionalParameter($datetime, IQueryBuilder::PARAM_DATE)));
+ ->where($qb->expr()->lt('last_updated', $qb->createPositionalParameter($datetime, IQueryBuilder::PARAM_DATETIME_MUTABLE)));
$qb->executeStatement();
return $deletedTasks;
}
diff --git a/lib/private/Updater/Changes.php b/lib/private/Updater/Changes.php
index bdf799a0100..c941dfb3fa5 100644
--- a/lib/private/Updater/Changes.php
+++ b/lib/private/Updater/Changes.php
@@ -9,6 +9,7 @@ declare(strict_types=1);
namespace OC\Updater;
use OCP\AppFramework\Db\Entity;
+use OCP\DB\Types;
/**
* Class Changes
@@ -39,7 +40,7 @@ class Changes extends Entity {
public function __construct() {
$this->addType('version', 'string');
$this->addType('etag', 'string');
- $this->addType('lastCheck', 'int');
+ $this->addType('lastCheck', Types::INTEGER);
$this->addType('data', 'string');
}
}
diff --git a/lib/public/AppFramework/Db/Entity.php b/lib/public/AppFramework/Db/Entity.php
index f37107ac128..cd15df134f1 100644
--- a/lib/public/AppFramework/Db/Entity.php
+++ b/lib/public/AppFramework/Db/Entity.php
@@ -7,13 +7,14 @@
*/
namespace OCP\AppFramework\Db;
+use OCP\DB\Types;
+
use function lcfirst;
use function substr;
/**
* @method int getId()
* @method void setId(int $id)
- * @psalm-type AllowedTypes = 'json'|'blob'|'datetime'|'string'|'int'|'integer'|'bool'|'boolean'|'float'|'double'|'array'|'object'
* @since 7.0.0
* @psalm-consistent-constructor
*/
@@ -24,7 +25,7 @@ abstract class Entity {
public $id;
private array $_updatedFields = [];
- /** @var array<string, AllowedTypes> */
+ /** @var array<string, \OCP\DB\Types::*> */
private array $_fieldTypes = ['id' => 'integer'];
/**
@@ -65,7 +66,7 @@ abstract class Entity {
/**
- * @return array<string, AllowedTypes> with attribute and type
+ * @return array<string, \OCP\DB\Types::*> with attribute and type
* @since 7.0.0
*/
public function getFieldTypes(): array {
@@ -102,33 +103,38 @@ abstract class Entity {
// if type definition exists, cast to correct type
if ($args[0] !== null && array_key_exists($name, $this->_fieldTypes)) {
$type = $this->_fieldTypes[$name];
- if ($type === 'blob') {
+ if ($type === Types::BLOB) {
// (B)LOB is treated as string when we read from the DB
if (is_resource($args[0])) {
$args[0] = stream_get_contents($args[0]);
}
- $type = 'string';
+ $type = Types::STRING;
}
- if ($type === 'datetime') {
- if (!$args[0] instanceof \DateTime) {
- $args[0] = new \DateTime($args[0]);
- }
- } elseif ($type === 'json') {
- if (!is_array($args[0])) {
- $args[0] = json_decode($args[0], true);
- }
- } else {
- $args[0] = match($type) {
- 'string' => (string)$args[0],
- 'bool', 'boolean', => (bool)$args[0],
- 'int', 'integer', => (int)$args[0],
- 'float' => (float)$args[0],
- 'double' => (float)$args[0],
- 'array' => (array)$args[0],
- 'object' => (object)$args[0],
- default => new \InvalidArgumentException()
- };
+ switch ($type) {
+ case Types::TIME:
+ case Types::DATE:
+ case Types::DATETIME:
+ case Types::DATETIME_TZ:
+ if (!$args[0] instanceof \DateTime) {
+ $args[0] = new \DateTime($args[0]);
+ }
+ break;
+ case Types::TIME_IMMUTABLE:
+ case Types::DATE_IMMUTABLE:
+ case Types::DATETIME_IMMUTABLE:
+ case Types::DATETIME_TZ_IMMUTABLE:
+ if (!$args[0] instanceof \DateTimeImmutable) {
+ $args[0] = new \DateTimeImmutable($args[0]);
+ }
+ break;
+ case Types::JSON:
+ if (!is_array($args[0])) {
+ $args[0] = json_decode($args[0], true);
+ }
+ break;
+ default:
+ settype($args[0], $type);
}
}
$this->$name = $args[0];
@@ -253,7 +259,7 @@ abstract class Entity {
* that value once its being returned from the database
*
* @param string $fieldName the name of the attribute
- * @param AllowedTypes $type the type which will be used to match a cast
+ * @param \OCP\DB\Types::* $type the type which will be used to match a cast
* @since 7.0.0
*/
protected function addType(string $fieldName, string $type): void {
diff --git a/lib/public/AppFramework/Db/QBMapper.php b/lib/public/AppFramework/Db/QBMapper.php
index ef4516221e6..243310fe933 100644
--- a/lib/public/AppFramework/Db/QBMapper.php
+++ b/lib/public/AppFramework/Db/QBMapper.php
@@ -10,6 +10,7 @@ namespace OCP\AppFramework\Db;
use Generator;
use OCP\DB\Exception;
use OCP\DB\QueryBuilder\IQueryBuilder;
+use OCP\DB\Types;
use OCP\IDBConnection;
/**
@@ -218,18 +219,33 @@ abstract class QBMapper {
switch ($types[ $property ]) {
case 'int':
- case 'integer':
+ case Types::INTEGER:
+ case Types::SMALLINT:
return IQueryBuilder::PARAM_INT;
- case 'string':
+ case Types::STRING:
return IQueryBuilder::PARAM_STR;
case 'bool':
- case 'boolean':
+ case Types::BOOLEAN:
return IQueryBuilder::PARAM_BOOL;
- case 'blob':
+ case Types::BLOB:
return IQueryBuilder::PARAM_LOB;
- case 'datetime':
- return IQueryBuilder::PARAM_DATE;
- case 'json':
+ case Types::DATE:
+ return IQueryBuilder::PARAM_DATETIME_MUTABLE;
+ case Types::DATETIME:
+ return IQueryBuilder::PARAM_DATETIME_MUTABLE;
+ case Types::DATETIME_TZ:
+ return IQueryBuilder::PARAM_DATETIME_TZ_MUTABLE;
+ case Types::DATE_IMMUTABLE:
+ return IQueryBuilder::PARAM_DATE_IMMUTABLE;
+ case Types::DATETIME_IMMUTABLE:
+ return IQueryBuilder::PARAM_DATETIME_IMMUTABLE;
+ case Types::DATETIME_TZ_IMMUTABLE:
+ return IQueryBuilder::PARAM_DATETIME_TZ_IMMUTABLE;
+ case Types::TIME:
+ return IQueryBuilder::PARAM_TIME_MUTABLE;
+ case Types::TIME_IMMUTABLE:
+ return IQueryBuilder::PARAM_TIME_IMMUTABLE;
+ case Types::JSON:
return IQueryBuilder::PARAM_JSON;
}
diff --git a/lib/public/DB/QueryBuilder/IQueryBuilder.php b/lib/public/DB/QueryBuilder/IQueryBuilder.php
index 72b2ccbecff..4794e7e8877 100644
--- a/lib/public/DB/QueryBuilder/IQueryBuilder.php
+++ b/lib/public/DB/QueryBuilder/IQueryBuilder.php
@@ -42,10 +42,60 @@ interface IQueryBuilder {
* @since 9.0.0
*/
public const PARAM_LOB = ParameterType::LARGE_OBJECT;
+
+ /**
+ * @since 9.0.0
+ * @deprecated 31.0.0 - use PARAM_DATETIME_MUTABLE instead
+ */
+ public const PARAM_DATE = Types::DATETIME_MUTABLE;
+
+ /**
+ * For passing a \DateTime instance when only interested in the time part (without timezone support)
+ * @since 31.0.0
+ */
+ public const PARAM_TIME_MUTABLE = Types::TIME_MUTABLE;
+
+ /**
+ * For passing a \DateTime instance when only interested in the date part (without timezone support)
+ * @since 31.0.0
+ */
+ public const PARAM_DATE_MUTABLE = Types::DATE_MUTABLE;
+
+ /**
+ * For passing a \DateTime instance (without timezone support)
+ * @since 31.0.0
+ */
+ public const PARAM_DATETIME_MUTABLE = Types::DATETIME_MUTABLE;
+
+ /**
+ * For passing a \DateTime instance with timezone support
+ * @since 31.0.0
+ */
+ public const PARAM_DATETIME_TZ_MUTABLE = Types::DATETIMETZ_MUTABLE;
+
+ /**
+ * For passing a \DateTimeImmutable instance when only interested in the time part (without timezone support)
+ * @since 31.0.0
+ */
+ public const PARAM_TIME_IMMUTABLE = Types::TIME_MUTABLE;
+
/**
+ * For passing a \DateTime instance when only interested in the date part (without timezone support)
* @since 9.0.0
*/
- public const PARAM_DATE = 'datetime';
+ public const PARAM_DATE_IMMUTABLE = Types::DATE_IMMUTABLE;
+
+ /**
+ * For passing a \DateTime instance (without timezone support)
+ * @since 31.0.0
+ */
+ public const PARAM_DATETIME_IMMUTABLE = Types::DATETIME_IMMUTABLE;
+
+ /**
+ * For passing a \DateTime instance with timezone support
+ * @since 31.0.0
+ */
+ public const PARAM_DATETIME_TZ_IMMUTABLE = Types::DATETIMETZ_IMMUTABLE;
/**
* @since 24.0.0
diff --git a/lib/public/DB/Types.php b/lib/public/DB/Types.php
index 414d81a24c8..969ec5e6611 100644
--- a/lib/public/DB/Types.php
+++ b/lib/public/DB/Types.php
@@ -41,18 +41,77 @@ final class Types {
public const BOOLEAN = 'boolean';
/**
+ * A datetime instance with only the date set.
+ * This will be (de)serialized into a \DateTime instance,
+ * it is recommended to instead use the `DATE_IMMUTABLE` instead.
+ *
+ * Warning: When deserialized the timezone will be set to UTC.
* @var string
* @since 21.0.0
*/
public const DATE = 'date';
/**
+ * An immutable datetime instance with only the date set.
+ * This will be (de)serialized into a \DateTimeImmutable instance,
+ * It is recommended to use this over the `DATE` type because
+ * out `Entity` class works detecting changes through the setter,
+ * changes on mutable objects can not be detected.
+ *
+ * Warning: When deserialized the timezone will be set to UTC.
+ * @var string
+ * @since 31.0.0
+ */
+ public const DATE_IMMUTABLE = 'date_immutable';
+
+ /**
+ * A datetime instance with date and time support.
+ * This will be (de)serialized into a \DateTime instance,
+ * it is recommended to instead use the `DATETIME_IMMUTABLE` instead.
+ *
+ * Warning: When deserialized the timezone will be set to UTC.
* @var string
* @since 21.0.0
*/
public const DATETIME = 'datetime';
/**
+ * An immutable datetime instance with date and time set.
+ * This will be (de)serialized into a \DateTimeImmutable instance,
+ * It is recommended to use this over the `DATETIME` type because
+ * out `Entity` class works detecting changes through the setter,
+ * changes on mutable objects can not be detected.
+ *
+ * Warning: When deserialized the timezone will be set to UTC.
+ * @var string
+ * @since 31.0.0
+ */
+ public const DATETIME_IMMUTABLE = 'datetime_immutable';
+
+
+ /**
+ * A datetime instance with timezone support
+ * This will be (de)serialized into a \DateTime instance,
+ * it is recommended to instead use the `DATETIME_TZ_IMMUTABLE` instead.
+ *
+ * @var string
+ * @since 31.0.0
+ */
+ public const DATETIME_TZ = 'datetimetz';
+
+ /**
+ * An immutable timezone aware datetime instance with date and time set.
+ * This will be (de)serialized into a \DateTimeImmutable instance,
+ * It is recommended to use this over the `DATETIME_TZ` type because
+ * out `Entity` class works detecting changes through the setter,
+ * changes on mutable objects can not be detected.
+ *
+ * @var string
+ * @since 31.0.0
+ */
+ public const DATETIME_TZ_IMMUTABLE = 'datetimetz_immutable';
+
+ /**
* @var string
* @since 21.0.0
*/
@@ -89,12 +148,30 @@ final class Types {
public const TEXT = 'text';
/**
+ * A datetime instance with only the time set.
+ * This will be (de)serialized into a \DateTime instance,
+ * it is recommended to instead use the `TIME_IMMUTABLE` instead.
+ *
+ * Warning: When deserialized the timezone will be set to UTC.
* @var string
* @since 21.0.0
*/
public const TIME = 'time';
/**
+ * A datetime instance with only the time set.
+ * This will be (de)serialized into a \DateTime instance.
+ *
+ * It is recommended to use this over the `DATETIME_TZ` type because
+ * out `Entity` class works detecting changes through the setter,
+ * changes on mutable objects can not be detected.
+ *
+ * @var string
+ * @since 31.0.0
+ */
+ public const TIME_IMMUTABLE = 'time_immutable';
+
+ /**
* @var string
* @since 24.0.0
*/
diff --git a/lib/public/Migration/Attributes/ColumnType.php b/lib/public/Migration/Attributes/ColumnType.php
index 23445e822b6..ab6044e3ae2 100644
--- a/lib/public/Migration/Attributes/ColumnType.php
+++ b/lib/public/Migration/Attributes/ColumnType.php
@@ -23,10 +23,24 @@ enum ColumnType : string {
case BLOB = 'blob';
/** @since 30.0.0 */
case BOOLEAN = 'boolean';
- /** @since 30.0.0 */
+ /**
+ * A column created with `DATE` can be used for both `DATE` and `DATE_IMMUTABLE`
+ * on the `\OCP\AppFramework\Db\Entity`.
+ * @since 30.0.0
+ */
case DATE = 'date';
- /** @since 30.0.0 */
+ /**
+ * A column created with `DATETIME` can be used for both `DATETIME` and `DATETIME_IMMUTABLE`
+ * on the `\OCP\AppFramework\Db\Entity`.
+ * @since 30.0.0
+ */
case DATETIME = 'datetime';
+ /**
+ * A column created with `DATETIME_TZ` can be used for both `DATETIME_TZ` and `DATETIME_TZ_IMMUTABLE`
+ * on the `\OCP\AppFramework\Db\Entity`.
+ * @since 31.0.0
+ */
+ case DATETIME_TZ = 'datetimetz';
/** @since 30.0.0 */
case DECIMAL = 'decimal';
/** @since 30.0.0 */
diff --git a/tests/lib/AppFramework/Db/EntityTest.php b/tests/lib/AppFramework/Db/EntityTest.php
index 8e6e6dcd645..5b953a25c1a 100644
--- a/tests/lib/AppFramework/Db/EntityTest.php
+++ b/tests/lib/AppFramework/Db/EntityTest.php
@@ -9,6 +9,7 @@
namespace Test\AppFramework\Db;
use OCP\AppFramework\Db\Entity;
+use OCP\DB\Types;
use PHPUnit\Framework\Constraint\IsType;
/**
@@ -29,6 +30,10 @@ use PHPUnit\Framework\Constraint\IsType;
* @method bool isAnotherBool()
* @method string getLongText()
* @method void setLongText(string $longText)
+ * @method \DateTime getTime()
+ * @method void setTime(\DateTime $time)
+ * @method \DateTimeImmutable getDatetime()
+ * @method void setDatetime(\DateTimeImmutable $datetime)
*/
class TestEntity extends Entity {
protected $name;
@@ -38,12 +43,16 @@ class TestEntity extends Entity {
protected $trueOrFalse;
protected $anotherBool;
protected $longText;
+ protected $time;
+ protected $datetime;
public function __construct($name = null) {
- $this->addType('testId', 'integer');
+ $this->addType('testId', Types::INTEGER);
$this->addType('trueOrFalse', 'bool');
- $this->addType('anotherBool', 'boolean');
- $this->addType('longText', 'blob');
+ $this->addType('anotherBool', Types::BOOLEAN);
+ $this->addType('longText', Types::BLOB);
+ $this->addType('time', Types::TIME);
+ $this->addType('datetime', Types::DATETIME_IMMUTABLE);
$this->name = $name;
}
@@ -216,15 +225,34 @@ class EntityTest extends \Test\TestCase {
$this->assertSame($string, $entity->getLongText());
}
+ public function testSetterConvertsDatetime() {
+ $entity = new TestEntity();
+ $entity->setDatetime('2024-08-19 15:26:00');
+ $this->assertEquals(new \DateTimeImmutable('2024-08-19 15:26:00'), $entity->getDatetime());
+ }
+
+ public function testSetterDoesNotConvertNullOnDatetime() {
+ $entity = new TestEntity();
+ $entity->setDatetime(null);
+ $this->assertNull($entity->getDatetime());
+ }
+
+ public function testSetterConvertsTime() {
+ $entity = new TestEntity();
+ $entity->setTime('15:26:00');
+ $this->assertEquals(new \DateTime('15:26:00'), $entity->getTime());
+ }
public function testGetFieldTypes(): void {
$entity = new TestEntity();
$this->assertEquals([
- 'id' => 'integer',
- 'testId' => 'integer',
+ 'id' => Types::INTEGER,
+ 'testId' => Types::INTEGER,
'trueOrFalse' => 'bool',
- 'anotherBool' => 'boolean',
- 'longText' => 'blob',
+ 'anotherBool' => Types::BOOLEAN,
+ 'longText' => Types::BLOB,
+ 'time' => Types::TIME,
+ 'datetime' => Types::DATETIME_IMMUTABLE,
], $entity->getFieldTypes());
}
@@ -232,7 +260,7 @@ class EntityTest extends \Test\TestCase {
public function testGetItInt(): void {
$entity = new TestEntity();
$entity->setId(3);
- $this->assertEquals('integer', gettype($entity->getId()));
+ $this->assertEquals(Types::INTEGER, gettype($entity->getId()));
}
diff --git a/tests/lib/AppFramework/Db/QBMapperDBTest.php b/tests/lib/AppFramework/Db/QBMapperDBTest.php
new file mode 100644
index 00000000000..72bc2d956d6
--- /dev/null
+++ b/tests/lib/AppFramework/Db/QBMapperDBTest.php
@@ -0,0 +1,159 @@
+<?php
+/**
+ * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace Test\AppFramework\Db;
+
+use Doctrine\DBAL\Schema\SchemaException;
+use OCP\AppFramework\Db\Entity;
+use OCP\AppFramework\Db\QBMapper;
+use OCP\DB\QueryBuilder\IQueryBuilder;
+use OCP\DB\Types;
+use OCP\IConfig;
+use OCP\IDBConnection;
+use OCP\Server;
+use Test\TestCase;
+
+/**
+ * @method void setTime(?\DateTime $time)
+ * @method ?\DateTime getTime()
+ * @method void setDatetime(?\DateTimeImmutable $datetime)
+ * @method ?\DateTimeImmutable getDatetime()
+ */
+class QBDBTestEntity extends Entity {
+ protected ?\DateTime $time = null;
+ protected ?\DateTimeImmutable $datetime = null;
+
+ public function __construct() {
+ $this->addType('time', Types::TIME);
+ $this->addType('datetime', Types::DATETIME_IMMUTABLE);
+ }
+}
+
+/**
+ * Class QBDBTestMapper
+ *
+ * @package Test\AppFramework\Db
+ */
+class QBDBTestMapper extends QBMapper {
+ public function __construct(IDBConnection $db) {
+ parent::__construct($db, 'testing', QBDBTestEntity::class);
+ }
+
+ public function getParameterTypeForPropertyForTest(Entity $entity, string $property) {
+ return parent::getParameterTypeForProperty($entity, $property);
+ }
+
+ public function getById(int $id): QBDBTestEntity {
+ $qb = $this->db->getQueryBuilder();
+ $query = $qb
+ ->select('*')
+ ->from($this->tableName)
+ ->where(
+ $qb->expr()->eq('id', $qb->createPositionalParameter($id, IQueryBuilder::PARAM_INT)),
+ );
+ return $this->findEntity($query);
+ }
+}
+
+/**
+ * Test real database handling (serialization)
+ * @group DB
+ */
+class QBMapperDBTest extends TestCase {
+ /** @var \Doctrine\DBAL\Connection|\OCP\IDBConnection */
+ protected $connection;
+ protected $schemaSetup = false;
+
+ protected function setUp(): void {
+ parent::setUp();
+
+ $this->connection = \OCP\Server::get(IDBConnection::class);
+ $this->prepareTestingTable();
+ }
+
+ public function testInsertDateTime(): void {
+ $mapper = new QBDBTestMapper($this->connection);
+ $entity = new QBDBTestEntity();
+ $entity->setTime(new \DateTime('2003-01-01 12:34:00'));
+ $entity->setDatetime(new \DateTimeImmutable('2000-01-01 23:45:00'));
+
+ $result = $mapper->insert($entity);
+ $this->assertNotNull($result->getId());
+ }
+
+ public function testRetrieveDateTime(): void {
+ $time = new \DateTime('2000-01-01 01:01:00');
+ $datetime = new \DateTimeImmutable('2000-01-01 02:02:00');
+
+ $mapper = new QBDBTestMapper($this->connection);
+ $entity = new QBDBTestEntity();
+ $entity->setTime($time);
+ $entity->setDatetime($datetime);
+
+ $result = $mapper->insert($entity);
+ $this->assertNotNull($result->getId());
+
+ $dbEntity = $mapper->getById($result->getId());
+ $this->assertEquals($time->format('H:i:s'), $dbEntity->getTime()->format('H:i:s'));
+ $this->assertEquals($datetime->format('Y-m-d H:i:s'), $dbEntity->getDatetime()->format('Y-m-d H:i:s'));
+ // The date is not saved for "time"
+ $this->assertNotEquals($time->format('Y'), $dbEntity->getTime()->format('Y'));
+ }
+
+ public function testUpdateDateTime(): void {
+ $time = new \DateTime('2000-01-01 01:01:00');
+ $datetime = new \DateTimeImmutable('2000-01-01 02:02:00');
+
+ $mapper = new QBDBTestMapper($this->connection);
+ $entity = new QBDBTestEntity();
+ $entity->setTime('now');
+ $entity->setDatetime('now');
+
+ /** @var QBDBTestEntity */
+ $entity = $mapper->insert($entity);
+ $this->assertNotNull($entity->getId());
+
+ // Update the values
+ $entity->setTime($time);
+ $entity->setDatetime($datetime);
+ $mapper->update($entity);
+
+ $dbEntity = $mapper->getById($entity->getId());
+ $this->assertEquals($time->format('H:i:s'), $dbEntity->getTime()->format('H:i:s'));
+ $this->assertEquals($datetime->format('Y-m-d H:i:s'), $dbEntity->getDatetime()->format('Y-m-d H:i:s'));
+ }
+
+ protected function prepareTestingTable(): void {
+ if ($this->schemaSetup) {
+ $this->connection->getQueryBuilder()->delete('testing')->executeStatement();
+ }
+
+ $prefix = Server::get(IConfig::class)->getSystemValueString('dbtableprefix', 'oc_');
+ $schema = $this->connection->createSchema();
+ try {
+ $schema->getTable($prefix . 'testing');
+ $this->connection->getQueryBuilder()->delete('testing')->executeStatement();
+ } catch (SchemaException $e) {
+ $this->schemaSetup = true;
+ $table = $schema->createTable($prefix . 'testing');
+ $table->addColumn('id', Types::BIGINT, [
+ 'autoincrement' => true,
+ 'notnull' => true,
+ ]);
+
+ $table->addColumn('time', Types::TIME, [
+ 'notnull' => false,
+ ]);
+
+ $table->addColumn('datetime', Types::DATETIME_IMMUTABLE, [
+ 'notnull' => false,
+ ]);
+
+ $table->setPrimaryKey(['id']);
+ $this->connection->migrateToSchema($schema);
+ }
+ }
+}
diff --git a/tests/lib/AppFramework/Db/QBMapperTest.php b/tests/lib/AppFramework/Db/QBMapperTest.php
index f08beca05ca..3cf32e56f12 100644
--- a/tests/lib/AppFramework/Db/QBMapperTest.php
+++ b/tests/lib/AppFramework/Db/QBMapperTest.php
@@ -10,7 +10,9 @@ use OCP\AppFramework\Db\Entity;
use OCP\AppFramework\Db\QBMapper;
use OCP\DB\QueryBuilder\IExpressionBuilder;
use OCP\DB\QueryBuilder\IQueryBuilder;
+use OCP\DB\Types;
use OCP\IDBConnection;
+use PHPUnit\Framework\MockObject\MockObject;
/**
* @method bool getBoolProp()
@@ -23,6 +25,8 @@ use OCP\IDBConnection;
* @method void setBooleanProp(bool $booleanProp)
* @method integer getIntegerProp()
* @method void setIntegerProp(integer $integerProp)
+ * @method ?\DateTimeImmutable getDatetimeProp()
+ * @method void setDatetimeProp(?\DateTimeImmutable $datetime)
*/
class QBTestEntity extends Entity {
protected $intProp;
@@ -31,14 +35,16 @@ class QBTestEntity extends Entity {
protected $integerProp;
protected $booleanProp;
protected $jsonProp;
+ protected $datetimeProp;
public function __construct() {
$this->addType('intProp', 'int');
$this->addType('boolProp', 'bool');
- $this->addType('stringProp', 'string');
- $this->addType('integerProp', 'integer');
- $this->addType('booleanProp', 'boolean');
- $this->addType('jsonProp', 'json');
+ $this->addType('stringProp', Types::STRING);
+ $this->addType('integerProp', Types::INTEGER);
+ $this->addType('booleanProp', Types::BOOLEAN);
+ $this->addType('jsonProp', Types::JSON);
+ $this->addType('datetimeProp', Types::DATETIME_IMMUTABLE);
}
}
@@ -63,25 +69,11 @@ class QBTestMapper extends QBMapper {
* @package Test\AppFramework\Db
*/
class QBMapperTest extends \Test\TestCase {
- /**
- * @var \PHPUnit\Framework\MockObject\MockObject|IDBConnection
- */
- protected $db;
-
- /**
- * @var \PHPUnit\Framework\MockObject\MockObject|IQueryBuilder
- */
- protected $qb;
- /**
- * @var \PHPUnit\Framework\MockObject\MockObject|IExpressionBuilder
- */
- protected $expr;
-
- /**
- * @var \Test\AppFramework\Db\QBTestMapper
- */
- protected $mapper;
+ protected IDBConnection&MockObject $db;
+ protected IQueryBuilder&MockObject $qb;
+ protected IExpressionBuilder&MockObject $expr;
+ protected QBTestMapper $mapper;
/**
* @throws \ReflectionException
@@ -109,36 +101,41 @@ class QBMapperTest extends \Test\TestCase {
public function testInsertEntityParameterTypeMapping(): void {
+ $datetime = new \DateTimeImmutable();
$entity = new QBTestEntity();
$entity->setIntProp(123);
$entity->setBoolProp(true);
$entity->setStringProp('string');
$entity->setIntegerProp(456);
$entity->setBooleanProp(false);
+ $entity->setDatetimeProp($datetime);
$intParam = $this->qb->createNamedParameter('int_prop', IQueryBuilder::PARAM_INT);
$boolParam = $this->qb->createNamedParameter('bool_prop', IQueryBuilder::PARAM_BOOL);
$stringParam = $this->qb->createNamedParameter('string_prop', IQueryBuilder::PARAM_STR);
$integerParam = $this->qb->createNamedParameter('integer_prop', IQueryBuilder::PARAM_INT);
$booleanParam = $this->qb->createNamedParameter('boolean_prop', IQueryBuilder::PARAM_BOOL);
+ $datetimeParam = $this->qb->createNamedParameter('datetime_prop', IQueryBuilder::PARAM_DATETIME_IMMUTABLE);
- $this->qb->expects($this->exactly(5))
+ $this->qb->expects($this->exactly(6))
->method('createNamedParameter')
->withConsecutive(
[$this->equalTo(123), $this->equalTo(IQueryBuilder::PARAM_INT)],
[$this->equalTo(true), $this->equalTo(IQueryBuilder::PARAM_BOOL)],
[$this->equalTo('string'), $this->equalTo(IQueryBuilder::PARAM_STR)],
[$this->equalTo(456), $this->equalTo(IQueryBuilder::PARAM_INT)],
- [$this->equalTo(false), $this->equalTo(IQueryBuilder::PARAM_BOOL)]
+ [$this->equalTo(false), $this->equalTo(IQueryBuilder::PARAM_BOOL)],
+ [$this->equalTo($datetime), $this->equalTo(IQueryBuilder::PARAM_DATETIME_IMMUTABLE)],
);
- $this->qb->expects($this->exactly(5))
+ $this->qb->expects($this->exactly(6))
->method('setValue')
->withConsecutive(
[$this->equalTo('int_prop'), $this->equalTo($intParam)],
[$this->equalTo('bool_prop'), $this->equalTo($boolParam)],
[$this->equalTo('string_prop'), $this->equalTo($stringParam)],
[$this->equalTo('integer_prop'), $this->equalTo($integerParam)],
- [$this->equalTo('boolean_prop'), $this->equalTo($booleanParam)]
+ [$this->equalTo('boolean_prop'), $this->equalTo($booleanParam)],
+ [$this->equalTo('datetime_prop'), $this->equalTo($datetimeParam)],
);
$this->mapper->insert($entity);
@@ -146,6 +143,7 @@ class QBMapperTest extends \Test\TestCase {
public function testUpdateEntityParameterTypeMapping(): void {
+ $datetime = new \DateTimeImmutable();
$entity = new QBTestEntity();
$entity->setId(789);
$entity->setIntProp(123);
@@ -154,6 +152,7 @@ class QBMapperTest extends \Test\TestCase {
$entity->setIntegerProp(456);
$entity->setBooleanProp(false);
$entity->setJsonProp(['hello' => 'world']);
+ $entity->setDatetimeProp($datetime);
$idParam = $this->qb->createNamedParameter('id', IQueryBuilder::PARAM_INT);
$intParam = $this->qb->createNamedParameter('int_prop', IQueryBuilder::PARAM_INT);
@@ -162,8 +161,9 @@ class QBMapperTest extends \Test\TestCase {
$integerParam = $this->qb->createNamedParameter('integer_prop', IQueryBuilder::PARAM_INT);
$booleanParam = $this->qb->createNamedParameter('boolean_prop', IQueryBuilder::PARAM_BOOL);
$jsonParam = $this->qb->createNamedParameter('json_prop', IQueryBuilder::PARAM_JSON);
+ $datetimeParam = $this->qb->createNamedParameter('datetime_prop', IQueryBuilder::PARAM_DATETIME_IMMUTABLE);
- $this->qb->expects($this->exactly(7))
+ $this->qb->expects($this->exactly(8))
->method('createNamedParameter')
->withConsecutive(
[$this->equalTo(123), $this->equalTo(IQueryBuilder::PARAM_INT)],
@@ -172,10 +172,11 @@ class QBMapperTest extends \Test\TestCase {
[$this->equalTo(456), $this->equalTo(IQueryBuilder::PARAM_INT)],
[$this->equalTo(false), $this->equalTo(IQueryBuilder::PARAM_BOOL)],
[$this->equalTo(['hello' => 'world']), $this->equalTo(IQueryBuilder::PARAM_JSON)],
+ [$this->equalTo($datetime), $this->equalTo(IQueryBuilder::PARAM_DATETIME_IMMUTABLE)],
[$this->equalTo(789), $this->equalTo(IQueryBuilder::PARAM_INT)],
);
- $this->qb->expects($this->exactly(6))
+ $this->qb->expects($this->exactly(7))
->method('set')
->withConsecutive(
[$this->equalTo('int_prop'), $this->equalTo($intParam)],
@@ -183,7 +184,8 @@ class QBMapperTest extends \Test\TestCase {
[$this->equalTo('string_prop'), $this->equalTo($stringParam)],
[$this->equalTo('integer_prop'), $this->equalTo($integerParam)],
[$this->equalTo('boolean_prop'), $this->equalTo($booleanParam)],
- [$this->equalTo('json_prop'), $this->equalTo($jsonParam)]
+ [$this->equalTo('json_prop'), $this->equalTo($jsonParam)],
+ [$this->equalTo('datetime_prop'), $this->equalTo($datetimeParam)],
);
$this->expr->expects($this->once())
@@ -216,6 +218,9 @@ class QBMapperTest extends \Test\TestCase {
$jsonType = $this->mapper->getParameterTypeForPropertyForTest($entity, 'jsonProp');
$this->assertEquals(IQueryBuilder::PARAM_JSON, $jsonType, 'JSON type property mapping incorrect');
+ $datetimeType = $this->mapper->getParameterTypeForPropertyForTest($entity, 'datetimeProp');
+ $this->assertEquals(IQueryBuilder::PARAM_DATETIME_IMMUTABLE, $datetimeType, 'DateTimeImmutable type property mapping incorrect');
+
$unknownType = $this->mapper->getParameterTypeForPropertyForTest($entity, 'someProp');
$this->assertEquals(IQueryBuilder::PARAM_STR, $unknownType, 'Unknown type property mapping incorrect');
}
diff --git a/tests/lib/Comments/ManagerTest.php b/tests/lib/Comments/ManagerTest.php
index 9a36182d393..15961b029f5 100644
--- a/tests/lib/Comments/ManagerTest.php
+++ b/tests/lib/Comments/ManagerTest.php
@@ -14,6 +14,7 @@ use OCP\Comments\IComment;
use OCP\Comments\ICommentsEventHandler;
use OCP\Comments\ICommentsManager;
use OCP\Comments\NotFoundException;
+use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\Files\Folder;
use OCP\Files\IRootFolder;
@@ -70,11 +71,11 @@ class ManagerTest extends TestCase {
'actor_id' => $qb->createNamedParameter('alice'),
'message' => $qb->createNamedParameter('nice one'),
'verb' => $qb->createNamedParameter('comment'),
- 'creation_timestamp' => $qb->createNamedParameter($creationDT, 'datetime'),
- 'latest_child_timestamp' => $qb->createNamedParameter($latestChildDT, 'datetime'),
+ 'creation_timestamp' => $qb->createNamedParameter($creationDT, IQueryBuilder::PARAM_DATETIME_MUTABLE),
+ 'latest_child_timestamp' => $qb->createNamedParameter($latestChildDT, IQueryBuilder::PARAM_DATETIME_MUTABLE),
'object_type' => $qb->createNamedParameter('files'),
'object_id' => $qb->createNamedParameter($objectId),
- 'expire_date' => $qb->createNamedParameter($expireDate, 'datetime'),
+ 'expire_date' => $qb->createNamedParameter($expireDate, IQueryBuilder::PARAM_DATETIME_MUTABLE),
'reference_id' => $qb->createNamedParameter('referenceId'),
'meta_data' => $qb->createNamedParameter(json_encode(['last_edit_actor_id' => 'admin'])),
])
diff --git a/tests/lib/DB/QueryBuilder/ExpressionBuilderDBTest.php b/tests/lib/DB/QueryBuilder/ExpressionBuilderDBTest.php
index ed6e328881b..392fe9ff937 100644
--- a/tests/lib/DB/QueryBuilder/ExpressionBuilderDBTest.php
+++ b/tests/lib/DB/QueryBuilder/ExpressionBuilderDBTest.php
@@ -7,9 +7,9 @@
namespace Test\DB\QueryBuilder;
use Doctrine\DBAL\Schema\SchemaException;
-use Doctrine\DBAL\Types\Types;
use OC\DB\QueryBuilder\Literal;
use OCP\DB\QueryBuilder\IQueryBuilder;
+use OCP\DB\Types;
use OCP\IConfig;
use OCP\Server;
use Test\TestCase;
@@ -145,13 +145,13 @@ class ExpressionBuilderDBTest extends TestCase {
$dateTime = new \DateTime('2023-01-01');
$insert = $this->connection->getQueryBuilder();
$insert->insert('testing')
- ->values(['datetime' => $insert->createNamedParameter($dateTime, IQueryBuilder::PARAM_DATE)])
+ ->values(['datetime' => $insert->createNamedParameter($dateTime, IQueryBuilder::PARAM_DATETIME_MUTABLE)])
->executeStatement();
$query = $this->connection->getQueryBuilder();
$result = $query->select('*')
->from('testing')
- ->where($query->expr()->eq('datetime', $query->createNamedParameter($dateTime, IQueryBuilder::PARAM_DATE)))
+ ->where($query->expr()->eq('datetime', $query->createNamedParameter($dateTime, IQueryBuilder::PARAM_DATETIME_MUTABLE)))
->executeQuery();
$entries = $result->fetchAll();
$result->closeCursor();
@@ -163,13 +163,13 @@ class ExpressionBuilderDBTest extends TestCase {
$dateTimeCompare = new \DateTime('2022-01-02');
$insert = $this->connection->getQueryBuilder();
$insert->insert('testing')
- ->values(['datetime' => $insert->createNamedParameter($dateTime, IQueryBuilder::PARAM_DATE)])
+ ->values(['datetime' => $insert->createNamedParameter($dateTime, IQueryBuilder::PARAM_DATETIME_MUTABLE)])
->executeStatement();
$query = $this->connection->getQueryBuilder();
$result = $query->select('*')
->from('testing')
- ->where($query->expr()->lt('datetime', $query->createNamedParameter($dateTimeCompare, IQueryBuilder::PARAM_DATE)))
+ ->where($query->expr()->lt('datetime', $query->createNamedParameter($dateTimeCompare, IQueryBuilder::PARAM_DATETIME_MUTABLE)))
->executeQuery();
$entries = $result->fetchAll();
$result->closeCursor();
@@ -181,13 +181,13 @@ class ExpressionBuilderDBTest extends TestCase {
$dateTimeCompare = new \DateTime('2023-01-01');
$insert = $this->connection->getQueryBuilder();
$insert->insert('testing')
- ->values(['datetime' => $insert->createNamedParameter($dateTime, IQueryBuilder::PARAM_DATE)])
+ ->values(['datetime' => $insert->createNamedParameter($dateTime, IQueryBuilder::PARAM_DATETIME_MUTABLE)])
->executeStatement();
$query = $this->connection->getQueryBuilder();
$result = $query->select('*')
->from('testing')
- ->where($query->expr()->gt('datetime', $query->createNamedParameter($dateTimeCompare, IQueryBuilder::PARAM_DATE)))
+ ->where($query->expr()->gt('datetime', $query->createNamedParameter($dateTimeCompare, IQueryBuilder::PARAM_DATETIME_MUTABLE)))
->executeQuery();
$entries = $result->fetchAll();
$result->closeCursor();
@@ -223,7 +223,7 @@ class ExpressionBuilderDBTest extends TestCase {
'notnull' => true,
]);
- $table->addColumn('datetime', Types::DATETIME_MUTABLE, [
+ $table->addColumn('datetime', Types::DATETIME, [
'notnull' => false,
]);
diff --git a/tests/lib/Share20/DefaultShareProviderTest.php b/tests/lib/Share20/DefaultShareProviderTest.php
index 5effaea8cf9..0351504e1a7 100644
--- a/tests/lib/Share20/DefaultShareProviderTest.php
+++ b/tests/lib/Share20/DefaultShareProviderTest.php
@@ -162,7 +162,7 @@ class DefaultShareProviderTest extends \Test\TestCase {
$qb->setValue('token', $qb->expr()->literal($token));
}
if ($expiration) {
- $qb->setValue('expiration', $qb->createNamedParameter($expiration, IQueryBuilder::PARAM_DATE));
+ $qb->setValue('expiration', $qb->createNamedParameter($expiration, IQueryBuilder::PARAM_DATETIME_MUTABLE));
}
if ($parent) {
$qb->setValue('parent', $qb->expr()->literal($parent));
diff --git a/tests/lib/Share20/ShareByMailProviderTest.php b/tests/lib/Share20/ShareByMailProviderTest.php
index 01029e421e0..bc8e9e53df0 100644
--- a/tests/lib/Share20/ShareByMailProviderTest.php
+++ b/tests/lib/Share20/ShareByMailProviderTest.php
@@ -179,7 +179,7 @@ class ShareByMailProviderTest extends TestCase {
$qb->setValue('token', $qb->expr()->literal($token));
}
if ($expiration) {
- $qb->setValue('expiration', $qb->createNamedParameter($expiration, IQueryBuilder::PARAM_DATE));
+ $qb->setValue('expiration', $qb->createNamedParameter($expiration, IQueryBuilder::PARAM_DATETIME_MUTABLE));
}
if ($parent) {
$qb->setValue('parent', $qb->expr()->literal($parent));