Merge pull request #3446 from nextcloud/backport/3365/stable23

[stable23] Switch to QBMapper in BoardMapper
This commit is contained in:
Julien Veyssier
2021-11-24 10:53:51 +01:00
committed by GitHub
2 changed files with 187 additions and 73 deletions

View File

@@ -25,12 +25,14 @@ namespace OCA\Deck\Db;
use OC\Cache\CappedMemoryCache; use OC\Cache\CappedMemoryCache;
use OCP\AppFramework\Db\DoesNotExistException; use OCP\AppFramework\Db\DoesNotExistException;
use OCP\AppFramework\Db\QBMapper;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\IDBConnection; use OCP\IDBConnection;
use OCP\IUserManager; use OCP\IUserManager;
use OCP\IGroupManager; use OCP\IGroupManager;
use Psr\Log\LoggerInterface; use Psr\Log\LoggerInterface;
class BoardMapper extends DeckMapper implements IPermissionMapper { class BoardMapper extends QBMapper implements IPermissionMapper {
private $labelMapper; private $labelMapper;
private $aclMapper; private $aclMapper;
private $stackMapper; private $stackMapper;
@@ -70,14 +72,20 @@ class BoardMapper extends DeckMapper implements IPermissionMapper {
* @param $id * @param $id
* @param bool $withLabels * @param bool $withLabels
* @param bool $withAcl * @param bool $withAcl
* @return \OCP\AppFramework\Db\Entity * @return Board
* @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException * @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
* @throws DoesNotExistException * @throws DoesNotExistException
*/ */
public function find($id, $withLabels = false, $withAcl = false) { public function find($id, $withLabels = false, $withAcl = false): Board {
$sql = 'SELECT id, title, owner, color, archived, deleted_at, last_modified FROM `*PREFIX*deck_boards` ' . $qb = $this->db->getQueryBuilder();
'WHERE `id` = ?'; $qb->select('*')
$board = $this->findEntity($sql, [$id]); ->from('deck_boards')
->where($qb->expr()->eq('id', $qb->createNamedParameter($id, IQueryBuilder::PARAM_INT)))
->orderBy('id');
$board = $this->findEntity($qb);
// FIXME is this necessary? it was NOT done with the old mapper
// $this->mapOwner($board);
// Add labels // Add labels
if ($withLabels) { if ($withLabels) {
@@ -123,44 +131,89 @@ class BoardMapper extends DeckMapper implements IPermissionMapper {
*/ */
public function findAllByUser(string $userId, ?int $limit = null, ?int $offset = null, ?int $since = null, public function findAllByUser(string $userId, ?int $limit = null, ?int $offset = null, ?int $since = null,
bool $includeArchived = true, ?int $before = null, ?string $term = null) { bool $includeArchived = true, ?int $before = null, ?string $term = null) {
// FIXME: One moving to QBMapper we should allow filtering the boards probably by method chaining for additional where clauses // FIXME this used to be a UNION to get boards owned by $userId and the user shares in one single query
$sql = 'SELECT id, title, owner, color, archived, deleted_at, 0 as shared, last_modified FROM `*PREFIX*deck_boards` WHERE owner = ?'; // Is it possible with the query builder?
$params = [$userId]; $qb = $this->db->getQueryBuilder();
$qb->select('id', 'title', 'owner', 'color', 'archived', 'deleted_at', 'last_modified')
// this does not work in MySQL/PostgreSQL
//->selectAlias('0', 'shared')
->from('deck_boards', 'b')
->where($qb->expr()->eq('owner', $qb->createNamedParameter($userId, IQueryBuilder::PARAM_STR)));
if (!$includeArchived) { if (!$includeArchived) {
$sql .= ' AND NOT archived AND deleted_at = 0'; $qb->andWhere($qb->expr()->eq('archived', $qb->createNamedParameter(false, IQueryBuilder::PARAM_BOOL)))
->andWhere($qb->expr()->eq('deleted_at', $qb->createNamedParameter(0, IQueryBuilder::PARAM_INT)));
} }
if ($since !== null) { if ($since !== null) {
$sql .= ' AND last_modified > ?'; $qb->andWhere($qb->expr()->gt('last_modified', $qb->createNamedParameter($since, IQueryBuilder::PARAM_INT)));
$params[] = $since;
} }
if ($before !== null) { if ($before !== null) {
$sql .= ' AND last_modified < ?'; $qb->andWhere($qb->expr()->lt('last_modified', $qb->createNamedParameter($before, IQueryBuilder::PARAM_INT)));
$params[] = $before;
} }
if ($term !== null) { if ($term !== null) {
$sql .= ' AND lower(title) LIKE ?'; $qb->andWhere(
$params[] = '%' . $term . '%'; $qb->expr()->iLike(
'title',
$qb->createNamedParameter(
'%' . $this->db->escapeLikeParameter($term) . '%',
IQueryBuilder::PARAM_STR
)
)
);
} }
$sql .= ' UNION ' . $qb->orderBy('b.id');
'SELECT boards.id, title, owner, color, archived, deleted_at, 1 as shared, last_modified FROM `*PREFIX*deck_boards` as boards ' . if ($limit !== null) {
'JOIN `*PREFIX*deck_board_acl` as acl ON boards.id=acl.board_id WHERE acl.participant=? AND acl.type=? AND boards.owner != ?'; $qb->setMaxResults($limit);
array_push($params, $userId, Acl::PERMISSION_TYPE_USER, $userId); }
if ($offset !== null) {
$qb->setFirstResult($offset);
}
$entries = $this->findEntities($qb);
foreach ($entries as $entry) {
$entry->setShared(0);
}
// shared with user
$qb->resetQueryParts();
$qb->select('b.id', 'title', 'owner', 'color', 'archived', 'deleted_at', 'last_modified')
//->selectAlias('1', 'shared')
->from('deck_boards', 'b')
->innerJoin('b', 'deck_board_acl', 'acl', $qb->expr()->eq('b.id', 'acl.board_id'))
->where($qb->expr()->eq('acl.participant', $qb->createNamedParameter($userId, IQueryBuilder::PARAM_STR)))
->andWhere($qb->expr()->eq('acl.type', $qb->createNamedParameter(Acl::PERMISSION_TYPE_USER, IQueryBuilder::PARAM_INT)))
->andWhere($qb->expr()->neq('b.owner', $qb->createNamedParameter($userId, IQueryBuilder::PARAM_STR)));
if (!$includeArchived) { if (!$includeArchived) {
$sql .= ' AND NOT archived AND deleted_at = 0'; $qb->andWhere($qb->expr()->eq('archived', $qb->createNamedParameter(false, IQueryBuilder::PARAM_BOOL)))
->andWhere($qb->expr()->eq('deleted_at', $qb->createNamedParameter(0, IQueryBuilder::PARAM_INT)));
} }
if ($since !== null) { if ($since !== null) {
$sql .= ' AND last_modified > ?'; $qb->andWhere($qb->expr()->gt('last_modified', $qb->createNamedParameter($since, IQueryBuilder::PARAM_INT)));
$params[] = $since;
} }
if ($before !== null) { if ($before !== null) {
$sql .= ' AND last_modified < ?'; $qb->andWhere($qb->expr()->lt('last_modified', $qb->createNamedParameter($before, IQueryBuilder::PARAM_INT)));
$params[] = $before;
} }
if ($term !== null) { if ($term !== null) {
$sql .= ' AND lower(title) LIKE ?'; $qb->andWhere(
$params[] = '%' . $term . '%'; $qb->expr()->iLike(
'title',
$qb->createNamedParameter(
'%' . $this->db->escapeLikeParameter($term) . '%',
IQueryBuilder::PARAM_STR
)
)
);
} }
$entries = $this->findEntities($sql, $params, $limit, $offset); $qb->orderBy('b.id');
if ($limit !== null) {
$qb->setMaxResults($limit);
}
if ($offset !== null) {
$qb->setFirstResult($offset);
}
$sharedEntries = $this->findEntities($qb);
foreach ($sharedEntries as $entry) {
$entry->setShared(1);
}
$entries = array_merge($entries, $sharedEntries);
/* @var Board $entry */ /* @var Board $entry */
foreach ($entries as $entry) { foreach ($entries as $entry) {
$acl = $this->aclMapper->findAll($entry->id); $acl = $this->aclMapper->findAll($entry->id);
@@ -169,9 +222,19 @@ class BoardMapper extends DeckMapper implements IPermissionMapper {
return $entries; return $entries;
} }
public function findAllByOwner(string $userId, int $limit = null, int $offset = null) { public function findAllByOwner(string $userId, ?int $limit = null, ?int $offset = null) {
$sql = 'SELECT * FROM `*PREFIX*deck_boards` WHERE owner = ?'; $qb = $this->db->getQueryBuilder();
return $this->findEntities($sql, [$userId], $limit, $offset); $qb->select('*')
->from('deck_boards')
->where($qb->expr()->eq('owner', $qb->createNamedParameter($userId, IQueryBuilder::PARAM_STR)))
->orderBy('id');
if ($limit !== null) {
$qb->setMaxResults($limit);
}
if ($offset !== null) {
$qb->setFirstResult($offset);
}
return $this->findEntities($qb);
} }
/** /**
@@ -188,33 +251,52 @@ class BoardMapper extends DeckMapper implements IPermissionMapper {
if (count($groups) <= 0) { if (count($groups) <= 0) {
return []; return [];
} }
$sql = 'SELECT boards.id, title, owner, color, archived, deleted_at, 2 as shared, last_modified FROM `*PREFIX*deck_boards` as boards ' . $qb = $this->db->getQueryBuilder();
'INNER JOIN `*PREFIX*deck_board_acl` as acl ON boards.id=acl.board_id WHERE owner != ? AND type=? AND ('; $qb->select('b.id', 'title', 'owner', 'color', 'archived', 'deleted_at', 'last_modified')
$params = [$userId, Acl::PERMISSION_TYPE_GROUP]; //->selectAlias('2', 'shared')
->from('deck_boards', 'b')
->innerJoin('b', 'deck_board_acl', 'acl', $qb->expr()->eq('b.id', 'acl.board_id'))
->where($qb->expr()->eq('acl.type', $qb->createNamedParameter(Acl::PERMISSION_TYPE_GROUP, IQueryBuilder::PARAM_INT)))
->andWhere($qb->expr()->neq('b.owner', $qb->createNamedParameter($userId, IQueryBuilder::PARAM_STR)));
$or = $qb->expr()->orx();
for ($i = 0, $iMax = count($groups); $i < $iMax; $i++) { for ($i = 0, $iMax = count($groups); $i < $iMax; $i++) {
$sql .= 'acl.participant = ? '; $or->add(
if (count($groups) > 1 && $i < count($groups) - 1) { $qb->expr()->eq('acl.participant', $qb->createNamedParameter($groups[$i], IQueryBuilder::PARAM_STR))
$sql .= ' OR '; );
} }
} $qb->andWhere($or);
$sql .= ')';
array_push($params, ...$groups);
if (!$includeArchived) { if (!$includeArchived) {
$sql .= ' AND NOT archived AND deleted_at = 0'; $qb->andWhere($qb->expr()->eq('archived', $qb->createNamedParameter(false, IQueryBuilder::PARAM_BOOL)))
->andWhere($qb->expr()->eq('deleted_at', $qb->createNamedParameter(0, IQueryBuilder::PARAM_INT)));
} }
if ($since !== null) { if ($since !== null) {
$sql .= ' AND last_modified > ?'; $qb->andWhere($qb->expr()->gt('last_modified', $qb->createNamedParameter($since, IQueryBuilder::PARAM_INT)));
$params[] = $since;
} }
if ($before !== null) { if ($before !== null) {
$sql .= ' AND last_modified < ?'; $qb->andWhere($qb->expr()->lt('last_modified', $qb->createNamedParameter($before, IQueryBuilder::PARAM_INT)));
$params[] = $before;
} }
if ($term !== null) { if ($term !== null) {
$sql .= ' AND lower(title) LIKE ?'; $qb->andWhere(
$params[] = '%' . $term . '%'; $qb->expr()->iLike(
'title',
$qb->createNamedParameter(
'%' . $this->db->escapeLikeParameter($term) . '%',
IQueryBuilder::PARAM_STR
)
)
);
}
$qb->orderBy('b.id');
if ($limit !== null) {
$qb->setMaxResults($limit);
}
if ($offset !== null) {
$qb->setFirstResult($offset);
}
$entries = $this->findEntities($qb);
foreach ($entries as $entry) {
$entry->setShared(2);
} }
$entries = $this->findEntities($sql, $params, $limit, $offset);
/* @var Board $entry */ /* @var Board $entry */
foreach ($entries as $entry) { foreach ($entries as $entry) {
$acl = $this->aclMapper->findAll($entry->id); $acl = $this->aclMapper->findAll($entry->id);
@@ -235,33 +317,52 @@ class BoardMapper extends DeckMapper implements IPermissionMapper {
return []; return [];
} }
$sql = 'SELECT boards.id, title, owner, color, archived, deleted_at, 2 as shared, last_modified FROM `*PREFIX*deck_boards` as boards ' . $qb = $this->db->getQueryBuilder();
'INNER JOIN `*PREFIX*deck_board_acl` as acl ON boards.id=acl.board_id WHERE owner != ? AND type=? AND ('; $qb->select('b.id', 'title', 'owner', 'color', 'archived', 'deleted_at', 'last_modified')
$params = [$userId, Acl::PERMISSION_TYPE_CIRCLE]; //->selectAlias('2', 'shared')
->from('deck_boards', 'b')
->innerJoin('b', 'deck_board_acl', 'acl', $qb->expr()->eq('b.id', 'acl.board_id'))
->where($qb->expr()->eq('acl.type', $qb->createNamedParameter(Acl::PERMISSION_TYPE_CIRCLE, IQueryBuilder::PARAM_INT)))
->andWhere($qb->expr()->neq('b.owner', $qb->createNamedParameter($userId, IQueryBuilder::PARAM_STR)));
$or = $qb->expr()->orx();
for ($i = 0, $iMax = count($circles); $i < $iMax; $i++) { for ($i = 0, $iMax = count($circles); $i < $iMax; $i++) {
$sql .= 'acl.participant = ? '; $or->add(
if (count($circles) > 1 && $i < count($circles) - 1) { $qb->expr()->eq('acl.participant', $qb->createNamedParameter($circles[$i], IQueryBuilder::PARAM_STR))
$sql .= ' OR '; );
} }
} $qb->andWhere($or);
$sql .= ')';
array_push($params, ...$circles);
if (!$includeArchived) { if (!$includeArchived) {
$sql .= ' AND NOT archived AND deleted_at = 0'; $qb->andWhere($qb->expr()->eq('archived', $qb->createNamedParameter(false, IQueryBuilder::PARAM_BOOL)))
->andWhere($qb->expr()->eq('deleted_at', $qb->createNamedParameter(0, IQueryBuilder::PARAM_INT)));
} }
if ($since !== null) { if ($since !== null) {
$sql .= ' AND last_modified > ?'; $qb->andWhere($qb->expr()->gt('last_modified', $qb->createNamedParameter($since, IQueryBuilder::PARAM_INT)));
$params[] = $since;
} }
if ($before !== null) { if ($before !== null) {
$sql .= ' AND last_modified < ?'; $qb->andWhere($qb->expr()->lt('last_modified', $qb->createNamedParameter($before, IQueryBuilder::PARAM_INT)));
$params[] = $before;
} }
if ($term !== null) { if ($term !== null) {
$sql .= ' AND lower(title) LIKE ?'; $qb->andWhere(
$params[] = '%' . $term . '%'; $qb->expr()->iLike(
'title',
$qb->createNamedParameter(
'%' . $this->db->escapeLikeParameter($term) . '%',
IQueryBuilder::PARAM_STR
)
)
);
}
$qb->orderBy('b.id');
if ($limit !== null) {
$qb->setMaxResults($limit);
}
if ($offset !== null) {
$qb->setFirstResult($offset);
}
$entries = $this->findEntities($qb);
foreach ($entries as $entry) {
$entry->setShared(2);
} }
$entries = $this->findEntities($sql, $params, $limit, $offset);
/* @var Board $entry */ /* @var Board $entry */
foreach ($entries as $entry) { foreach ($entries as $entry) {
$acl = $this->aclMapper->findAll($entry->id); $acl = $this->aclMapper->findAll($entry->id);
@@ -270,21 +371,26 @@ class BoardMapper extends DeckMapper implements IPermissionMapper {
return $entries; return $entries;
} }
public function findAll() { public function findAll(): array {
$sql = 'SELECT id from *PREFIX*deck_boards;'; $qb = $this->db->getQueryBuilder();
return $this->findEntities($sql); $qb->select('id')
->from('deck_boards');
return $this->findEntities($qb);
} }
public function findToDelete() { public function findToDelete() {
// add buffer of 5 min // add buffer of 5 min
$timeLimit = time() - (60 * 5); $timeLimit = time() - (60 * 5);
$sql = 'SELECT id, title, owner, color, archived, deleted_at, last_modified FROM `*PREFIX*deck_boards` ' . $qb = $this->db->getQueryBuilder();
'WHERE `deleted_at` > 0 AND `deleted_at` < ?'; $qb->select('id', 'title', 'owner', 'color', 'archived', 'deleted_at', 'last_modified')
return $this->findEntities($sql, [$timeLimit]); ->from('deck_boards')
->where($qb->expr()->gt('deleted_at', $qb->createNamedParameter(0, IQueryBuilder::PARAM_INT)))
->andWhere($qb->expr()->lt('deleted_at', $qb->createNamedParameter($timeLimit, IQueryBuilder::PARAM_INT)));
return $this->findEntities($qb);
} }
public function delete(/** @noinspection PhpUnnecessaryFullyQualifiedNameInspection */ public function delete(/** @noinspection PhpUnnecessaryFullyQualifiedNameInspection */
\OCP\AppFramework\Db\Entity $entity) { \OCP\AppFramework\Db\Entity $entity): \OCP\AppFramework\Db\Entity {
// delete acl // delete acl
$acl = $this->aclMapper->findAll($entity->getId()); $acl = $this->aclMapper->findAll($entity->getId());
foreach ($acl as $item) { foreach ($acl as $item) {

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<files psalm-version="4.9.3@4c262932602b9bbab5020863d1eb22d49de0dbf4"> <files psalm-version="4.11.2@6fba5eb554f9507b72932f9c75533d8af593688d">
<file src="lib/Activity/ActivityManager.php"> <file src="lib/Activity/ActivityManager.php">
<TypeDoesNotContainType occurrences="1"> <TypeDoesNotContainType occurrences="1">
<code>$message !== null</code> <code>$message !== null</code>
@@ -116,6 +116,14 @@
<ParamNameMismatch occurrences="1"> <ParamNameMismatch occurrences="1">
<code>$boardId</code> <code>$boardId</code>
</ParamNameMismatch> </ParamNameMismatch>
<TypeDoesNotContainType occurrences="6">
<code>$limit !== null</code>
<code>$limit !== null</code>
<code>$limit !== null</code>
<code>$offset !== null</code>
<code>$offset !== null</code>
<code>$offset !== null</code>
</TypeDoesNotContainType>
<UndefinedClass occurrences="2"> <UndefinedClass occurrences="2">
<code>\OCA\Circles\Api\v1\Circles</code> <code>\OCA\Circles\Api\v1\Circles</code>
<code>\OCA\Circles\Api\v1\Circles</code> <code>\OCA\Circles\Api\v1\Circles</code>