fix: Chunk in-queries to 1000 items

Signed-off-by: Julius Härtl <jus@bitgrid.net>
This commit is contained in:
Julius Härtl
2023-02-17 09:15:15 +01:00
parent 46df19a3a6
commit b4de6a8f96
4 changed files with 37 additions and 13 deletions

View File

@@ -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);
}));
}
/**

View File

@@ -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<Assignment> */
class AssignmentMapper extends QBMapper implements IPermissionMapper {
/** @template-extends DeckMapper<Assignment> */
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);
}

View File

@@ -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);
}

View File

@@ -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;
}
}
}
}