From ee02e3246dc18a6390cd9fbfd7b66f441e71ccc1 Mon Sep 17 00:00:00 2001 From: Ferdinand Thiessen Date: Mon, 19 Aug 2024 16:43:17 +0200 Subject: [PATCH 1/4] feat(AppFramework): Add full support for date / time / datetime columns MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This adds support for all Doctrine supported types, for the column types only the immutable variants needed to be added. But especially those types are the important ones, as our **Entity** class works by detecting changes through setters. Meaning if it is mutable, changes like `$entity->date->modfiy()` can not be detected, so the immutable types make more sense here. Similar the parameter types needed to be added. `Enity` and `QBMapper` needed to be adjusted so they support (auto map) those types, required when insert or update an entity. Also added more tests, especially to make sure the mapper really serializes the values correctly. Co-authored-by: Ferdinand Thiessen Co-authored-by: Côme Chilliet <91878298+come-nc@users.noreply.github.com> Signed-off-by: Ferdinand Thiessen --- .../lib/FederatedShareProvider.php | 4 +- .../files_reminders/lib/Db/ReminderMapper.php | 2 +- apps/sharebymail/lib/ShareByMailProvider.php | 8 +- lib/private/Comments/Manager.php | 20 +-- lib/public/AppFramework/Db/Entity.php | 49 +++--- lib/public/AppFramework/Db/QBMapper.php | 28 ++- lib/public/DB/QueryBuilder/IQueryBuilder.php | 46 ++++- lib/public/DB/Types.php | 77 +++++++++ .../Migration/Attributes/ColumnType.php | 48 ++++-- tests/lib/AppFramework/Db/EntityTest.php | 44 ++++- tests/lib/AppFramework/Db/QBMapperDBTest.php | 159 ++++++++++++++++++ tests/lib/AppFramework/Db/QBMapperTest.php | 67 ++++---- tests/lib/Comments/ManagerTest.php | 7 +- .../QueryBuilder/ExpressionBuilderDBTest.php | 4 +- 14 files changed, 458 insertions(+), 105 deletions(-) create mode 100644 tests/lib/AppFramework/Db/QBMapperDBTest.php diff --git a/apps/federatedfilesharing/lib/FederatedShareProvider.php b/apps/federatedfilesharing/lib/FederatedShareProvider.php index da55b9a03d75c..45ac8ba15def5 100644 --- a/apps/federatedfilesharing/lib/FederatedShareProvider.php +++ b/apps/federatedfilesharing/lib/FederatedShareProvider.php @@ -303,7 +303,7 @@ private function addShareToDB($itemSource, $itemType, $shareWith, $sharedBy, $ui ->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)) ->setValue('token', $qb->createNamedParameter($token)) ->setValue('stime', $qb->createNamedParameter(time())); @@ -333,7 +333,7 @@ public function update(IShare $share) { ->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)) ->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 16859585bdfe9..35421656da2b4 100644 --- a/apps/files_reminders/lib/Db/ReminderMapper.php +++ b/apps/files_reminders/lib/Db/ReminderMapper.php @@ -135,7 +135,7 @@ public function findNotified(DateTime $buffer, ?int $limit = null) { $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))) ->orderBy('due_date', 'ASC') ->setMaxResults($limit); diff --git a/apps/sharebymail/lib/ShareByMailProvider.php b/apps/sharebymail/lib/ShareByMailProvider.php index 3ec13dd3a08e3..77ff33cdbeb3c 100644 --- a/apps/sharebymail/lib/ShareByMailProvider.php +++ b/apps/sharebymail/lib/ShareByMailProvider.php @@ -699,7 +699,7 @@ protected function addShareToDB( ->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)) ->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 @@ protected function addShareToDB( $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)); } $qb->executeStatement(); @@ -752,10 +752,10 @@ public function update(IShare $share, ?string $plainTextPassword = null): IShare ->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)) ->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)) ->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/lib/private/Comments/Manager.php b/lib/private/Comments/Manager.php index 41e0c6622128f..860d3034ce8f8 100644 --- a/lib/private/Comments/Manager.php +++ b/lib/private/Comments/Manager.php @@ -440,14 +440,14 @@ public function getCommentsWithVerbForObjectSinceComment( $query->expr()->orX( $query->expr()->lt( 'creation_timestamp', - $query->createNamedParameter($lastKnownCommentDateTime, IQueryBuilder::PARAM_DATE), - IQueryBuilder::PARAM_DATE + $query->createNamedParameter($lastKnownCommentDateTime, IQueryBuilder::PARAM_DATETIME), + IQueryBuilder::PARAM_DATETIME ), $query->expr()->andX( $query->expr()->eq( 'creation_timestamp', - $query->createNamedParameter($lastKnownCommentDateTime, IQueryBuilder::PARAM_DATE), - IQueryBuilder::PARAM_DATE + $query->createNamedParameter($lastKnownCommentDateTime, IQueryBuilder::PARAM_DATETIME), + IQueryBuilder::PARAM_DATETIME ), $idComparison ) @@ -463,14 +463,14 @@ public function getCommentsWithVerbForObjectSinceComment( $query->expr()->orX( $query->expr()->gt( 'creation_timestamp', - $query->createNamedParameter($lastKnownCommentDateTime, IQueryBuilder::PARAM_DATE), - IQueryBuilder::PARAM_DATE + $query->createNamedParameter($lastKnownCommentDateTime, IQueryBuilder::PARAM_DATETIME), + IQueryBuilder::PARAM_DATETIME ), $query->expr()->andX( $query->expr()->eq( 'creation_timestamp', - $query->createNamedParameter($lastKnownCommentDateTime, IQueryBuilder::PARAM_DATE), - IQueryBuilder::PARAM_DATE + $query->createNamedParameter($lastKnownCommentDateTime, IQueryBuilder::PARAM_DATETIME), + IQueryBuilder::PARAM_DATETIME ), $idComparison ) @@ -740,7 +740,7 @@ public function getLastCommentBeforeDate(string $objectType, string $objectId, \ ->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))) ->orderBy('creation_timestamp', 'desc'); if ($verb !== '') { @@ -1551,7 +1551,7 @@ public function deleteCommentsExpiredAtObject(string $objectType, string $object $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))) ->andWhere($qb->expr()->eq('object_type', $qb->createNamedParameter($objectType))); if ($objectId !== '') { diff --git a/lib/public/AppFramework/Db/Entity.php b/lib/public/AppFramework/Db/Entity.php index f37107ac1288f..882902a212e4d 100644 --- a/lib/public/AppFramework/Db/Entity.php +++ b/lib/public/AppFramework/Db/Entity.php @@ -7,6 +7,8 @@ */ namespace OCP\AppFramework\Db; +use OCP\DB\Types; + use function lcfirst; use function substr; @@ -102,33 +104,38 @@ protected function setter(string $name, array $args): void { // 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]; diff --git a/lib/public/AppFramework/Db/QBMapper.php b/lib/public/AppFramework/Db/QBMapper.php index ef4516221e6b8..c0667d2711183 100644 --- a/lib/public/AppFramework/Db/QBMapper.php +++ b/lib/public/AppFramework/Db/QBMapper.php @@ -10,6 +10,7 @@ use Generator; use OCP\DB\Exception; use OCP\DB\QueryBuilder\IQueryBuilder; +use OCP\DB\Types; use OCP\IDBConnection; /** @@ -218,18 +219,33 @@ protected function getParameterTypeForProperty(Entity $entity, string $property) 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': + case Types::DATE: return IQueryBuilder::PARAM_DATE; - case 'json': + case Types::DATETIME: + return IQueryBuilder::PARAM_DATETIME; + case Types::DATETIME_TZ: + return IQueryBuilder::PARAM_DATETIME_TZ; + 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; + 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 72b2ccbecffe0..1ff2d4959c544 100644 --- a/lib/public/DB/QueryBuilder/IQueryBuilder.php +++ b/lib/public/DB/QueryBuilder/IQueryBuilder.php @@ -42,10 +42,54 @@ interface IQueryBuilder { * @since 9.0.0 */ public const PARAM_LOB = ParameterType::LARGE_OBJECT; + + /** + * For passing a \DateTime instance when only interested in the time part (without timezone support) + * @since 31.0.0 + */ + public const PARAM_TIME = 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 = Types::DATE_MUTABLE; + + /** + * For passing a \DateTime instance (without timezone support) + * @since 31.0.0 + */ + public const PARAM_DATETIME = Types::DATETIME_MUTABLE; + + /** + * For passing a \DateTime instance with timezone support + * @since 31.0.0 + */ + public const PARAM_DATETIME_TZ = 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_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 414d81a24c8b2..969ec5e661129 100644 --- a/lib/public/DB/Types.php +++ b/lib/public/DB/Types.php @@ -41,17 +41,76 @@ 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,11 +148,29 @@ 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 23445e822b659..57bea9207634f 100644 --- a/lib/public/Migration/Attributes/ColumnType.php +++ b/lib/public/Migration/Attributes/ColumnType.php @@ -8,6 +8,8 @@ */ namespace OCP\Migration\Attributes; +use OCP\DB\Types; + /** * enum ColumnType based on OCP\DB\Types * @@ -16,31 +18,45 @@ */ enum ColumnType : string { /** @since 30.0.0 */ - case BIGINT = 'bigint'; - /** @since 30.0.0 */ - case BINARY = 'binary'; - /** @since 30.0.0 */ - case BLOB = 'blob'; + case BIGINT = Types::BIGINT; /** @since 30.0.0 */ - case BOOLEAN = 'boolean'; + case BINARY = Types::BINARY; /** @since 30.0.0 */ - case DATE = 'date'; + case BLOB = Types::BLOB; /** @since 30.0.0 */ - case DATETIME = 'datetime'; + case BOOLEAN = Types::BOOLEAN; + /** + * 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 = Types::DATE; + /** + * 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 = Types::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 = Types::DATETIME_TZ; /** @since 30.0.0 */ - case DECIMAL = 'decimal'; + case DECIMAL = Types::DECIMAL; /** @since 30.0.0 */ - case FLOAT = 'float'; + case FLOAT = Types::FLOAT; /** @since 30.0.0 */ - case INTEGER = 'integer'; + case INTEGER = Types::INTEGER; /** @since 30.0.0 */ - case SMALLINT = 'smallint'; + case SMALLINT = Types::SMALLINT; /** @since 30.0.0 */ - case STRING = 'string'; + case STRING = Types::STRING; /** @since 30.0.0 */ - case TEXT = 'text'; + case TEXT = Types::TEXT; /** @since 30.0.0 */ - case TIME = 'time'; + case TIME = Types::TIME; /** @since 30.0.0 */ - case JSON = 'json'; + case JSON = Types::JSON; } diff --git a/tests/lib/AppFramework/Db/EntityTest.php b/tests/lib/AppFramework/Db/EntityTest.php index 8e6e6dcd6452f..5b953a25c1a2e 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 @@ * @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 @@ public function testSetterConvertsResourcesToStringProperly(): void { $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 @@ public function testGetFieldTypes(): void { 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 0000000000000..72bc2d956d6f0 --- /dev/null +++ b/tests/lib/AppFramework/Db/QBMapperDBTest.php @@ -0,0 +1,159 @@ +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 f08beca05cad4..5fb46bfd668da 100644 --- a/tests/lib/AppFramework/Db/QBMapperTest.php +++ b/tests/lib/AppFramework/Db/QBMapperTest.php @@ -10,7 +10,9 @@ 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 @@ * @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 @@ public function getParameterTypeForPropertyForTest(Entity $entity, string $prope * @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 @@ protected function setUp(): void { 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 @@ public function testInsertEntityParameterTypeMapping(): void { public function testUpdateEntityParameterTypeMapping(): void { + $datetime = new \DateTimeImmutable(); $entity = new QBTestEntity(); $entity->setId(789); $entity->setIntProp(123); @@ -153,7 +151,8 @@ public function testUpdateEntityParameterTypeMapping(): void { $entity->setStringProp('string'); $entity->setIntegerProp(456); $entity->setBooleanProp(false); - $entity->setJsonProp(['hello' => 'world']); + $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 @@ public function testUpdateEntityParameterTypeMapping(): void { $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)], @@ -171,11 +171,12 @@ public function testUpdateEntityParameterTypeMapping(): void { [$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(['hello' => 'world']), $this->equalTo(IQueryBuilder::PARAM_JSON)], + [$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 @@ public function testUpdateEntityParameterTypeMapping(): void { [$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 @@ public function testGetParameterTypeForProperty(): void { $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 9a36182d39306..44cb7c36f3194 100644 --- a/tests/lib/Comments/ManagerTest.php +++ b/tests/lib/Comments/ManagerTest.php @@ -14,6 +14,7 @@ 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 @@ protected function addDatabaseEntry($parentId, $topmostParentId, $creationDT = n '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), + 'latest_child_timestamp' => $qb->createNamedParameter($latestChildDT, IQueryBuilder::PARAM_DATETIME), 'object_type' => $qb->createNamedParameter('files'), 'object_id' => $qb->createNamedParameter($objectId), - 'expire_date' => $qb->createNamedParameter($expireDate, 'datetime'), + 'expire_date' => $qb->createNamedParameter($expireDate, IQueryBuilder::PARAM_DATETIME), '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 ed6e328881be0..536e9f38405e5 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; @@ -223,7 +223,7 @@ protected function prepareTestingTable(): void { 'notnull' => true, ]); - $table->addColumn('datetime', Types::DATETIME_MUTABLE, [ + $table->addColumn('datetime', Types::DATETIME, [ 'notnull' => false, ]); From e314d521184514aca4d36c264995d728b04b2f49 Mon Sep 17 00:00:00 2001 From: Ferdinand Thiessen Date: Tue, 20 Aug 2024 11:55:14 +0200 Subject: [PATCH 2/4] fix: Adjust parameter type usage and add SQLite support Signed-off-by: Ferdinand Thiessen --- .../SqliteExpressionBuilder.php | 24 +++++++++++--- .../RateLimiting/Backend/DatabaseBackend.php | 4 +-- lib/private/Share20/DefaultShareProvider.php | 8 ++--- lib/private/TextToImage/Db/TaskMapper.php | 4 +-- .../Migration/Attributes/ColumnType.php | 32 +++++++++---------- 5 files changed, 43 insertions(+), 29 deletions(-) diff --git a/lib/private/DB/QueryBuilder/ExpressionBuilder/SqliteExpressionBuilder.php b/lib/private/DB/QueryBuilder/ExpressionBuilder/SqliteExpressionBuilder.php index ac4698cdc76b4..d1ca65b0bdac9 100644 --- a/lib/private/DB/QueryBuilder/ExpressionBuilder/SqliteExpressionBuilder.php +++ b/lib/private/DB/QueryBuilder/ExpressionBuilder/SqliteExpressionBuilder.php @@ -29,7 +29,11 @@ public function iLike($x, $y, $type = null): string { * @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 @@ protected function prepareColumn($column, $type) { * @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: + case IQueryBuilder::PARAM_DATE_IMMUTABLE: + $column = $this->helper->quoteColumnName($column); + return new QueryFunction('DATE(' . $column . ')'); + case IQueryBuilder::PARAM_DATETIME: + case IQueryBuilder::PARAM_DATETIME_TZ: + case IQueryBuilder::PARAM_DATETIME_IMMUTABLE: + case IQueryBuilder::PARAM_DATETIME_TZ_IMMUTABLE: + $column = $this->helper->quoteColumnName($column); + return new QueryFunction('DATETIME(' . $column . ')'); + case IQueryBuilder::PARAM_TIME: + 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 8adcd8e168ccf..b03f5f3d29513 100644 --- a/lib/private/Security/RateLimiting/Backend/DatabaseBackend.php +++ b/lib/private/Security/RateLimiting/Backend/DatabaseBackend.php @@ -42,7 +42,7 @@ private function getExistingAttemptCount( $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)) ) ->executeStatement(); @@ -87,7 +87,7 @@ public function registerAttempt( $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), ]); if (!$this->config->getSystemValueBool('ratelimit.protection.enabled', true)) { diff --git a/lib/private/Share20/DefaultShareProvider.php b/lib/private/Share20/DefaultShareProvider.php index af993b7f314c8..9c36860cb4577 100644 --- a/lib/private/Share20/DefaultShareProvider.php +++ b/lib/private/Share20/DefaultShareProvider.php @@ -222,7 +222,7 @@ public function update(\OCP\Share\IShare $share) { ->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)) ->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 @@ public function update(\OCP\Share\IShare $share) { ->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)) ->set('note', $qb->createNamedParameter($share->getNote())) ->execute(); @@ -252,7 +252,7 @@ public function update(\OCP\Share\IShare $share) { ->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)) ->set('note', $qb->createNamedParameter($share->getNote())) ->execute(); @@ -279,7 +279,7 @@ public function update(\OCP\Share\IShare $share) { ->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)) ->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 b34c749eb663a..3d1e2c249a4ee 100644 --- a/lib/private/TextToImage/Db/TaskMapper.php +++ b/lib/private/TextToImage/Db/TaskMapper.php @@ -95,11 +95,11 @@ public function deleteOlderThan(int $timeout): array { $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))); $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))); $qb->executeStatement(); return $deletedTasks; } diff --git a/lib/public/Migration/Attributes/ColumnType.php b/lib/public/Migration/Attributes/ColumnType.php index 57bea9207634f..ab6044e3ae2c1 100644 --- a/lib/public/Migration/Attributes/ColumnType.php +++ b/lib/public/Migration/Attributes/ColumnType.php @@ -8,8 +8,6 @@ */ namespace OCP\Migration\Attributes; -use OCP\DB\Types; - /** * enum ColumnType based on OCP\DB\Types * @@ -18,45 +16,45 @@ */ enum ColumnType : string { /** @since 30.0.0 */ - case BIGINT = Types::BIGINT; + case BIGINT = 'bigint'; /** @since 30.0.0 */ - case BINARY = Types::BINARY; + case BINARY = 'binary'; /** @since 30.0.0 */ - case BLOB = Types::BLOB; + case BLOB = 'blob'; /** @since 30.0.0 */ - case BOOLEAN = Types::BOOLEAN; + case BOOLEAN = 'boolean'; /** * 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 = Types::DATE; + case DATE = 'date'; /** * 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 = Types::DATETIME; + 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 = Types::DATETIME_TZ; + case DATETIME_TZ = 'datetimetz'; /** @since 30.0.0 */ - case DECIMAL = Types::DECIMAL; + case DECIMAL = 'decimal'; /** @since 30.0.0 */ - case FLOAT = Types::FLOAT; + case FLOAT = 'float'; /** @since 30.0.0 */ - case INTEGER = Types::INTEGER; + case INTEGER = 'integer'; /** @since 30.0.0 */ - case SMALLINT = Types::SMALLINT; + case SMALLINT = 'smallint'; /** @since 30.0.0 */ - case STRING = Types::STRING; + case STRING = 'string'; /** @since 30.0.0 */ - case TEXT = Types::TEXT; + case TEXT = 'text'; /** @since 30.0.0 */ - case TIME = Types::TIME; + case TIME = 'time'; /** @since 30.0.0 */ - case JSON = Types::JSON; + case JSON = 'json'; } From db94e10af0928f35b74c22f9370df1cb3ea1160f Mon Sep 17 00:00:00 2001 From: Ferdinand Thiessen Date: Wed, 21 Aug 2024 11:42:56 +0200 Subject: [PATCH 3/4] fix: Prevent breaking change in IQueryBuilder Signed-off-by: Ferdinand Thiessen --- .../lib/FederatedShareProvider.php | 4 ++-- .../files_reminders/lib/Db/ReminderMapper.php | 2 +- apps/sharebymail/lib/ShareByMailProvider.php | 8 ++++---- lib/private/Comments/Manager.php | 20 +++++++++---------- .../SqliteExpressionBuilder.php | 8 ++++---- .../RateLimiting/Backend/DatabaseBackend.php | 4 ++-- lib/private/Share20/DefaultShareProvider.php | 8 ++++---- lib/private/TextToImage/Db/TaskMapper.php | 4 ++-- lib/public/AppFramework/Db/QBMapper.php | 8 ++++---- lib/public/DB/QueryBuilder/IQueryBuilder.php | 16 ++++++++++----- tests/lib/AppFramework/Db/QBMapperTest.php | 4 ++-- tests/lib/Comments/ManagerTest.php | 6 +++--- .../QueryBuilder/ExpressionBuilderDBTest.php | 12 +++++------ .../lib/Share20/DefaultShareProviderTest.php | 2 +- tests/lib/Share20/ShareByMailProviderTest.php | 2 +- 15 files changed, 57 insertions(+), 51 deletions(-) diff --git a/apps/federatedfilesharing/lib/FederatedShareProvider.php b/apps/federatedfilesharing/lib/FederatedShareProvider.php index 45ac8ba15def5..c115c61333857 100644 --- a/apps/federatedfilesharing/lib/FederatedShareProvider.php +++ b/apps/federatedfilesharing/lib/FederatedShareProvider.php @@ -303,7 +303,7 @@ private function addShareToDB($itemSource, $itemType, $shareWith, $sharedBy, $ui ->setValue('uid_owner', $qb->createNamedParameter($uidOwner)) ->setValue('uid_initiator', $qb->createNamedParameter($sharedBy)) ->setValue('permissions', $qb->createNamedParameter($permissions)) - ->setValue('expiration', $qb->createNamedParameter($expirationDate, IQueryBuilder::PARAM_DATETIME)) + ->setValue('expiration', $qb->createNamedParameter($expirationDate, IQueryBuilder::PARAM_DATETIME_MUTABLE)) ->setValue('token', $qb->createNamedParameter($token)) ->setValue('stime', $qb->createNamedParameter(time())); @@ -333,7 +333,7 @@ public function update(IShare $share) { ->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_DATETIME)) + ->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 35421656da2b4..e747c8af397f9 100644 --- a/apps/files_reminders/lib/Db/ReminderMapper.php +++ b/apps/files_reminders/lib/Db/ReminderMapper.php @@ -135,7 +135,7 @@ public function findNotified(DateTime $buffer, ?int $limit = null) { $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_DATETIME))) + ->andWhere($qb->expr()->lt('due_date', $qb->createNamedParameter($buffer, IQueryBuilder::PARAM_DATETIME_MUTABLE))) ->orderBy('due_date', 'ASC') ->setMaxResults($limit); diff --git a/apps/sharebymail/lib/ShareByMailProvider.php b/apps/sharebymail/lib/ShareByMailProvider.php index 77ff33cdbeb3c..9efc0ec2513a8 100644 --- a/apps/sharebymail/lib/ShareByMailProvider.php +++ b/apps/sharebymail/lib/ShareByMailProvider.php @@ -699,7 +699,7 @@ protected function addShareToDB( ->setValue('permissions', $qb->createNamedParameter($permissions)) ->setValue('token', $qb->createNamedParameter($token)) ->setValue('password', $qb->createNamedParameter($password)) - ->setValue('password_expiration_time', $qb->createNamedParameter($passwordExpirationTime, IQueryBuilder::PARAM_DATETIME)) + ->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 @@ protected function addShareToDB( $qb->setValue('attributes', $qb->createNamedParameter($shareAttributes)); if ($expirationTime !== null) { - $qb->setValue('expiration', $qb->createNamedParameter($expirationTime, IQueryBuilder::PARAM_DATETIME)); + $qb->setValue('expiration', $qb->createNamedParameter($expirationTime, IQueryBuilder::PARAM_DATETIME_MUTABLE)); } $qb->executeStatement(); @@ -752,10 +752,10 @@ public function update(IShare $share, ?string $plainTextPassword = null): IShare ->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_DATETIME)) + ->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_DATETIME)) + ->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/lib/private/Comments/Manager.php b/lib/private/Comments/Manager.php index 860d3034ce8f8..d959ebcb0a31c 100644 --- a/lib/private/Comments/Manager.php +++ b/lib/private/Comments/Manager.php @@ -440,14 +440,14 @@ public function getCommentsWithVerbForObjectSinceComment( $query->expr()->orX( $query->expr()->lt( 'creation_timestamp', - $query->createNamedParameter($lastKnownCommentDateTime, IQueryBuilder::PARAM_DATETIME), - IQueryBuilder::PARAM_DATETIME + $query->createNamedParameter($lastKnownCommentDateTime, IQueryBuilder::PARAM_DATETIME_MUTABLE), + IQueryBuilder::PARAM_DATETIME_MUTABLE ), $query->expr()->andX( $query->expr()->eq( 'creation_timestamp', - $query->createNamedParameter($lastKnownCommentDateTime, IQueryBuilder::PARAM_DATETIME), - IQueryBuilder::PARAM_DATETIME + $query->createNamedParameter($lastKnownCommentDateTime, IQueryBuilder::PARAM_DATETIME_MUTABLE), + IQueryBuilder::PARAM_DATETIME_MUTABLE ), $idComparison ) @@ -463,14 +463,14 @@ public function getCommentsWithVerbForObjectSinceComment( $query->expr()->orX( $query->expr()->gt( 'creation_timestamp', - $query->createNamedParameter($lastKnownCommentDateTime, IQueryBuilder::PARAM_DATETIME), - IQueryBuilder::PARAM_DATETIME + $query->createNamedParameter($lastKnownCommentDateTime, IQueryBuilder::PARAM_DATETIME_MUTABLE), + IQueryBuilder::PARAM_DATETIME_MUTABLE ), $query->expr()->andX( $query->expr()->eq( 'creation_timestamp', - $query->createNamedParameter($lastKnownCommentDateTime, IQueryBuilder::PARAM_DATETIME), - IQueryBuilder::PARAM_DATETIME + $query->createNamedParameter($lastKnownCommentDateTime, IQueryBuilder::PARAM_DATETIME_MUTABLE), + IQueryBuilder::PARAM_DATETIME_MUTABLE ), $idComparison ) @@ -740,7 +740,7 @@ public function getLastCommentBeforeDate(string $objectType, string $objectId, \ ->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_DATETIME))) + ->andWhere($query->expr()->lt('creation_timestamp', $query->createNamedParameter($beforeDate, IQueryBuilder::PARAM_DATETIME_MUTABLE))) ->orderBy('creation_timestamp', 'desc'); if ($verb !== '') { @@ -1551,7 +1551,7 @@ public function deleteCommentsExpiredAtObject(string $objectType, string $object $qb = $this->dbConn->getQueryBuilder(); $qb->delete('comments') ->where($qb->expr()->lte('expire_date', - $qb->createNamedParameter($this->timeFactory->getDateTime(), IQueryBuilder::PARAM_DATETIME))) + $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 d1ca65b0bdac9..559c29df20876 100644 --- a/lib/private/DB/QueryBuilder/ExpressionBuilder/SqliteExpressionBuilder.php +++ b/lib/private/DB/QueryBuilder/ExpressionBuilder/SqliteExpressionBuilder.php @@ -49,17 +49,17 @@ protected function prepareColumn($column, $type) { */ public function castColumn($column, $type): IQueryFunction { switch ($type) { - case IQueryBuilder::PARAM_DATE: + case IQueryBuilder::PARAM_DATE_MUTABLE: case IQueryBuilder::PARAM_DATE_IMMUTABLE: $column = $this->helper->quoteColumnName($column); return new QueryFunction('DATE(' . $column . ')'); - case IQueryBuilder::PARAM_DATETIME: - case IQueryBuilder::PARAM_DATETIME_TZ: + 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: + case IQueryBuilder::PARAM_TIME_MUTABLE: case IQueryBuilder::PARAM_TIME_IMMUTABLE: $column = $this->helper->quoteColumnName($column); return new QueryFunction('TIME(' . $column . ')'); diff --git a/lib/private/Security/RateLimiting/Backend/DatabaseBackend.php b/lib/private/Security/RateLimiting/Backend/DatabaseBackend.php index b03f5f3d29513..9fb237f2f720a 100644 --- a/lib/private/Security/RateLimiting/Backend/DatabaseBackend.php +++ b/lib/private/Security/RateLimiting/Backend/DatabaseBackend.php @@ -42,7 +42,7 @@ private function getExistingAttemptCount( $qb = $this->dbConnection->getQueryBuilder(); $qb->delete(self::TABLE_NAME) ->where( - $qb->expr()->lte('delete_after', $qb->createNamedParameter($currentTime, IQueryBuilder::PARAM_DATETIME)) + $qb->expr()->lte('delete_after', $qb->createNamedParameter($currentTime, IQueryBuilder::PARAM_DATETIME_MUTABLE)) ) ->executeStatement(); @@ -87,7 +87,7 @@ public function registerAttempt( $qb->insert(self::TABLE_NAME) ->values([ 'hash' => $qb->createNamedParameter($identifier, IQueryBuilder::PARAM_STR), - 'delete_after' => $qb->createNamedParameter($deleteAfter, IQueryBuilder::PARAM_DATETIME), + '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 9c36860cb4577..77251fd0bc168 100644 --- a/lib/private/Share20/DefaultShareProvider.php +++ b/lib/private/Share20/DefaultShareProvider.php @@ -222,7 +222,7 @@ public function update(\OCP\Share\IShare $share) { ->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_DATETIME)) + ->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 @@ public function update(\OCP\Share\IShare $share) { ->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_DATETIME)) + ->set('expiration', $qb->createNamedParameter($expirationDate, IQueryBuilder::PARAM_DATETIME_MUTABLE)) ->set('note', $qb->createNamedParameter($share->getNote())) ->execute(); @@ -252,7 +252,7 @@ public function update(\OCP\Share\IShare $share) { ->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_DATETIME)) + ->set('expiration', $qb->createNamedParameter($expirationDate, IQueryBuilder::PARAM_DATETIME_MUTABLE)) ->set('note', $qb->createNamedParameter($share->getNote())) ->execute(); @@ -279,7 +279,7 @@ public function update(\OCP\Share\IShare $share) { ->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_DATETIME)) + ->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 3d1e2c249a4ee..37f492e14cf3b 100644 --- a/lib/private/TextToImage/Db/TaskMapper.php +++ b/lib/private/TextToImage/Db/TaskMapper.php @@ -95,11 +95,11 @@ public function deleteOlderThan(int $timeout): array { $qb = $this->db->getQueryBuilder(); $qb->select('*') ->from($this->tableName) - ->where($qb->expr()->lt('last_updated', $qb->createPositionalParameter($datetime, IQueryBuilder::PARAM_DATETIME))); + ->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_DATETIME))); + ->where($qb->expr()->lt('last_updated', $qb->createPositionalParameter($datetime, IQueryBuilder::PARAM_DATETIME_MUTABLE))); $qb->executeStatement(); return $deletedTasks; } diff --git a/lib/public/AppFramework/Db/QBMapper.php b/lib/public/AppFramework/Db/QBMapper.php index c0667d2711183..243310fe93301 100644 --- a/lib/public/AppFramework/Db/QBMapper.php +++ b/lib/public/AppFramework/Db/QBMapper.php @@ -230,11 +230,11 @@ protected function getParameterTypeForProperty(Entity $entity, string $property) case Types::BLOB: return IQueryBuilder::PARAM_LOB; case Types::DATE: - return IQueryBuilder::PARAM_DATE; + return IQueryBuilder::PARAM_DATETIME_MUTABLE; case Types::DATETIME: - return IQueryBuilder::PARAM_DATETIME; + return IQueryBuilder::PARAM_DATETIME_MUTABLE; case Types::DATETIME_TZ: - return IQueryBuilder::PARAM_DATETIME_TZ; + return IQueryBuilder::PARAM_DATETIME_TZ_MUTABLE; case Types::DATE_IMMUTABLE: return IQueryBuilder::PARAM_DATE_IMMUTABLE; case Types::DATETIME_IMMUTABLE: @@ -242,7 +242,7 @@ protected function getParameterTypeForProperty(Entity $entity, string $property) case Types::DATETIME_TZ_IMMUTABLE: return IQueryBuilder::PARAM_DATETIME_TZ_IMMUTABLE; case Types::TIME: - return IQueryBuilder::PARAM_TIME; + return IQueryBuilder::PARAM_TIME_MUTABLE; case Types::TIME_IMMUTABLE: return IQueryBuilder::PARAM_TIME_IMMUTABLE; case Types::JSON: diff --git a/lib/public/DB/QueryBuilder/IQueryBuilder.php b/lib/public/DB/QueryBuilder/IQueryBuilder.php index 1ff2d4959c544..4794e7e887775 100644 --- a/lib/public/DB/QueryBuilder/IQueryBuilder.php +++ b/lib/public/DB/QueryBuilder/IQueryBuilder.php @@ -43,29 +43,35 @@ interface IQueryBuilder { */ 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 = Types::TIME_MUTABLE; + public const PARAM_TIME_MUTABLE = Types::TIME_MUTABLE; /** * For passing a \DateTime instance when only interested in the date part (without timezone support) - * @since 9.0.0 + * @since 31.0.0 */ - public const PARAM_DATE = Types::DATE_MUTABLE; + public const PARAM_DATE_MUTABLE = Types::DATE_MUTABLE; /** * For passing a \DateTime instance (without timezone support) * @since 31.0.0 */ - public const PARAM_DATETIME = Types::DATETIME_MUTABLE; + 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 = Types::DATETIMETZ_MUTABLE; + public const PARAM_DATETIME_TZ_MUTABLE = Types::DATETIMETZ_MUTABLE; /** * For passing a \DateTimeImmutable instance when only interested in the time part (without timezone support) diff --git a/tests/lib/AppFramework/Db/QBMapperTest.php b/tests/lib/AppFramework/Db/QBMapperTest.php index 5fb46bfd668da..3cf32e56f125c 100644 --- a/tests/lib/AppFramework/Db/QBMapperTest.php +++ b/tests/lib/AppFramework/Db/QBMapperTest.php @@ -151,7 +151,7 @@ public function testUpdateEntityParameterTypeMapping(): void { $entity->setStringProp('string'); $entity->setIntegerProp(456); $entity->setBooleanProp(false); - $entity->setJsonProp(["hello" => "world"]); + $entity->setJsonProp(['hello' => 'world']); $entity->setDatetimeProp($datetime); $idParam = $this->qb->createNamedParameter('id', IQueryBuilder::PARAM_INT); @@ -171,7 +171,7 @@ public function testUpdateEntityParameterTypeMapping(): void { [$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(["hello" => "world"]), $this->equalTo(IQueryBuilder::PARAM_JSON)], + [$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)], ); diff --git a/tests/lib/Comments/ManagerTest.php b/tests/lib/Comments/ManagerTest.php index 44cb7c36f3194..15961b029f520 100644 --- a/tests/lib/Comments/ManagerTest.php +++ b/tests/lib/Comments/ManagerTest.php @@ -71,11 +71,11 @@ protected function addDatabaseEntry($parentId, $topmostParentId, $creationDT = n 'actor_id' => $qb->createNamedParameter('alice'), 'message' => $qb->createNamedParameter('nice one'), 'verb' => $qb->createNamedParameter('comment'), - 'creation_timestamp' => $qb->createNamedParameter($creationDT, IQueryBuilder::PARAM_DATETIME), - 'latest_child_timestamp' => $qb->createNamedParameter($latestChildDT, IQueryBuilder::PARAM_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, IQueryBuilder::PARAM_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 536e9f38405e5..392fe9ff9374a 100644 --- a/tests/lib/DB/QueryBuilder/ExpressionBuilderDBTest.php +++ b/tests/lib/DB/QueryBuilder/ExpressionBuilderDBTest.php @@ -145,13 +145,13 @@ public function testDateTimeEquals(): void { $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 @@ public function testDateTimeLess(): void { $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 @@ public function testDateTimeGreater(): void { $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(); diff --git a/tests/lib/Share20/DefaultShareProviderTest.php b/tests/lib/Share20/DefaultShareProviderTest.php index 5effaea8cf91a..0351504e1a7cb 100644 --- a/tests/lib/Share20/DefaultShareProviderTest.php +++ b/tests/lib/Share20/DefaultShareProviderTest.php @@ -162,7 +162,7 @@ private function addShareToDB($shareType, $sharedWith, $sharedBy, $shareOwner, $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 01029e421e090..bc8e9e53df091 100644 --- a/tests/lib/Share20/ShareByMailProviderTest.php +++ b/tests/lib/Share20/ShareByMailProviderTest.php @@ -179,7 +179,7 @@ private function addShareToDB($shareType, $sharedWith, $sharedBy, $shareOwner, $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)); From 0e54c2bd43853891deac92f4ef9842c40ca64feb Mon Sep 17 00:00:00 2001 From: Ferdinand Thiessen Date: Thu, 19 Sep 2024 15:46:20 +0200 Subject: [PATCH 4/4] fix: Adjust Entity types Signed-off-by: Ferdinand Thiessen --- apps/contactsinteraction/lib/Db/RecentContact.php | 13 +++++++------ apps/dav/lib/CalDAV/Proxy/Proxy.php | 7 ++++--- apps/dav/lib/Db/Direct.php | 9 +++++---- apps/oauth2/lib/Db/AccessToken.php | 11 ++++++----- apps/oauth2/lib/Db/Client.php | 3 ++- apps/user_status/lib/Db/UserStatus.php | 7 ++++--- core/Db/LoginFlowV2.php | 5 +++-- .../Authentication/Token/PublicKeyToken.php | 15 ++++++++------- lib/private/Updater/Changes.php | 3 ++- lib/public/AppFramework/Db/Entity.php | 7 +++---- 10 files changed, 44 insertions(+), 36 deletions(-) diff --git a/apps/contactsinteraction/lib/Db/RecentContact.php b/apps/contactsinteraction/lib/Db/RecentContact.php index 581b4b292ca0b..fc6ec3cc24981 100644 --- a/apps/contactsinteraction/lib/Db/RecentContact.php +++ b/apps/contactsinteraction/lib/Db/RecentContact.php @@ -9,6 +9,7 @@ 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 a10f82e4b29d3..ef1ad8c634f88 100644 --- a/apps/dav/lib/CalDAV/Proxy/Proxy.php +++ b/apps/dav/lib/CalDAV/Proxy/Proxy.php @@ -9,6 +9,7 @@ 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 377a120c43649..4e4a12d225f54 100644 --- a/apps/dav/lib/Db/Direct.php +++ b/apps/dav/lib/Db/Direct.php @@ -9,6 +9,7 @@ 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/oauth2/lib/Db/AccessToken.php b/apps/oauth2/lib/Db/AccessToken.php index 543d512e0ce35..f9c0e6de51e6f 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 c48ca4edfc0dc..458d85b24ab25 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/user_status/lib/Db/UserStatus.php b/apps/user_status/lib/Db/UserStatus.php index 1be358308532d..b2da4a9e07a91 100644 --- a/apps/user_status/lib/Db/UserStatus.php +++ b/apps/user_status/lib/Db/UserStatus.php @@ -9,6 +9,7 @@ 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 90b6f41a626ab..1671e5990e04f 100644 --- a/core/Db/LoginFlowV2.php +++ b/core/Db/LoginFlowV2.php @@ -9,6 +9,7 @@ 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 961b7191d84ae..be427ab48392a 100644 --- a/lib/private/Authentication/Token/PublicKeyToken.php +++ b/lib/private/Authentication/Token/PublicKeyToken.php @@ -10,6 +10,7 @@ use OCP\AppFramework\Db\Entity; use OCP\Authentication\Token\IToken; +use OCP\DB\Types; /** * @method void setId(int $id) @@ -88,16 +89,16 @@ public function __construct() { $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/Updater/Changes.php b/lib/private/Updater/Changes.php index bdf799a01006a..c941dfb3fa568 100644 --- a/lib/private/Updater/Changes.php +++ b/lib/private/Updater/Changes.php @@ -9,6 +9,7 @@ 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 882902a212e4d..cd15df134f1bd 100644 --- a/lib/public/AppFramework/Db/Entity.php +++ b/lib/public/AppFramework/Db/Entity.php @@ -15,7 +15,6 @@ /** * @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 */ @@ -26,7 +25,7 @@ abstract class Entity { public $id; private array $_updatedFields = []; - /** @var array */ + /** @var array */ private array $_fieldTypes = ['id' => 'integer']; /** @@ -67,7 +66,7 @@ public static function fromRow(array $row): static { /** - * @return array with attribute and type + * @return array with attribute and type * @since 7.0.0 */ public function getFieldTypes(): array { @@ -260,7 +259,7 @@ public function getUpdatedFields(): array { * 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 {