From b4de6a8f96fca20bea1bf679cd52763890ad6fb2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julius=20H=C3=A4rtl?= Date: Fri, 17 Feb 2023 09:15:15 +0100 Subject: [PATCH] fix: Chunk in-queries to 1000 items MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Julius Härtl --- lib/Db/AclMapper.php | 7 +++++-- lib/Db/AssignmentMapper.php | 19 +++++++++++-------- lib/Db/BoardMapper.php | 4 ++-- lib/Db/DeckMapper.php | 20 +++++++++++++++++++- 4 files changed, 37 insertions(+), 13 deletions(-) diff --git a/lib/Db/AclMapper.php b/lib/Db/AclMapper.php index 234bbae5a..a74244370 100644 --- a/lib/Db/AclMapper.php +++ b/lib/Db/AclMapper.php @@ -56,11 +56,14 @@ class AclMapper extends DeckMapper implements IPermissionMapper { $qb = $this->db->getQueryBuilder(); $qb->select('id', 'board_id', 'type', 'participant', 'permission_edit', 'permission_share', 'permission_manage') ->from('deck_board_acl') - ->where($qb->expr()->in('board_id', $qb->createNamedParameter($boardIds, IQueryBuilder::PARAM_INT_ARRAY))) + ->where($qb->expr()->in('board_id', $qb->createParameter('boardIds'))) ->setMaxResults($limit) ->setFirstResult($offset); - return $this->findEntities($qb); + return iterator_to_array($this->chunkQuery($boardIds, function (array $ids) use ($qb) { + $qb->setParameter('boardIds', $ids, IQueryBuilder::PARAM_INT_ARRAY); + return $this->findEntities($qb); + })); } /** diff --git a/lib/Db/AssignmentMapper.php b/lib/Db/AssignmentMapper.php index 44aa178df..f2731431f 100644 --- a/lib/Db/AssignmentMapper.php +++ b/lib/Db/AssignmentMapper.php @@ -28,15 +28,13 @@ namespace OCA\Deck\Db; use OCA\Deck\NotFoundException; use OCA\Deck\Service\CirclesService; use OCP\AppFramework\Db\Entity; -use OCP\AppFramework\Db\QBMapper; use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\IDBConnection; use OCP\IGroupManager; use OCP\IUserManager; -use PDO; -/** @template-extends QBMapper */ -class AssignmentMapper extends QBMapper implements IPermissionMapper { +/** @template-extends DeckMapper */ +class AssignmentMapper extends DeckMapper implements IPermissionMapper { /** @var CardMapper */ private $cardMapper; @@ -72,8 +70,13 @@ class AssignmentMapper extends QBMapper implements IPermissionMapper { $qb = $this->db->getQueryBuilder(); $qb->select('*') ->from('deck_assigned_users') - ->where($qb->expr()->in('card_id', $qb->createNamedParameter($cardIds, IQueryBuilder::PARAM_INT_ARRAY))); - $users = $this->findEntities($qb); + ->where($qb->expr()->in('card_id', $qb->createParameter('cardIds'))); + + $users = iterator_to_array($this->chunkQuery($cardIds, function (array $ids) use ($qb) { + $qb->setParameter('cardIds', $ids, IQueryBuilder::PARAM_INT_ARRAY); + return $this->findEntities($qb); + })); + foreach ($users as $user) { $this->mapParticipant($user); } @@ -84,8 +87,8 @@ class AssignmentMapper extends QBMapper implements IPermissionMapper { $qb = $this->db->getQueryBuilder(); $qb->select('*') ->from('deck_assigned_users') - ->where($qb->expr()->eq('participant', $qb->createNamedParameter($participant, PDO::PARAM_STR))) - ->andWhere($qb->expr()->eq('type', $qb->createNamedParameter($type, PDO::PARAM_INT))); + ->where($qb->expr()->eq('participant', $qb->createNamedParameter($participant, IQueryBuilder::PARAM_STR))) + ->andWhere($qb->expr()->eq('type', $qb->createNamedParameter($type, IQueryBuilder::PARAM_INT))); return $this->findEntities($qb); } diff --git a/lib/Db/BoardMapper.php b/lib/Db/BoardMapper.php index bd24c2e24..456152527 100644 --- a/lib/Db/BoardMapper.php +++ b/lib/Db/BoardMapper.php @@ -165,9 +165,9 @@ class BoardMapper extends QBMapper implements IPermissionMapper { /* @var Board $entry */ foreach ($allBoards as $entry) { - $boardAcls = array_filter($acls, function ($acl) use ($entry) { + $boardAcls = array_values(array_filter($acls, function ($acl) use ($entry) { return $acl->getBoardId() === $entry->getId(); - }); + })); $entry->setAcl($boardAcls); } diff --git a/lib/Db/DeckMapper.php b/lib/Db/DeckMapper.php index 887774409..884079447 100644 --- a/lib/Db/DeckMapper.php +++ b/lib/Db/DeckMapper.php @@ -23,6 +23,7 @@ namespace OCA\Deck\Db; +use Generator; use OCP\AppFramework\Db\Entity; use OCP\AppFramework\Db\QBMapper; use OCP\DB\QueryBuilder\IQueryBuilder; @@ -35,7 +36,7 @@ abstract class DeckMapper extends QBMapper { /** * @param $id - * @return \OCP\AppFramework\Db\Entity if not found + * @return T * @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException * @throws \OCP\AppFramework\Db\DoesNotExistException */ @@ -47,4 +48,21 @@ abstract class DeckMapper extends QBMapper { return $this->findEntity($qb); } + + /** + * Helper function to split passed array into chunks of 1000 elements and + * call a given callback for fetching query results + * + * Can be useful to limit to 1000 results per query for oracle compatiblity + * but still iterate over all results + */ + public function chunkQuery(array $ids, callable $callback): Generator { + $limit = 1000; + while (!empty($ids)) { + $slice = array_splice($ids, 0, $limit); + foreach ($callback($slice) as $item) { + yield $item; + } + } + } }