Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions src/Database/Adapter.php
Original file line number Diff line number Diff line change
Expand Up @@ -701,11 +701,12 @@ abstract public function deleteDocument(string $collection, string $id): bool;
* Delete Documents
*
* @param string $collection
* @param array<string> $ids
* @param array<string> $internalIds
* @param array<string> $permissionIds
*
* @return int
*/
abstract public function deleteDocuments(string $collection, array $ids): int;
abstract public function deleteDocuments(string $collection, array $internalIds, array $permissionIds): int;

/**
* Find Documents
Expand Down
326 changes: 5 additions & 321 deletions src/Database/Adapter/MariaDB.php
Original file line number Diff line number Diff line change
Expand Up @@ -1365,254 +1365,6 @@ public function updateDocument(string $collection, string $id, Document $documen
return $document;
}

/**
* Update documents
*
* Updates all documents which match the given query.
*
* @param string $collection
* @param Document $updates
* @param array<Document> $documents
*
* @return int
*
* @throws DatabaseException
*/
public function updateDocuments(string $collection, Document $updates, array $documents): int
{
$attributes = $updates->getAttributes();

if (!empty($updates->getUpdatedAt())) {
$attributes['_updatedAt'] = $updates->getUpdatedAt();
}

if (!empty($updates->getPermissions())) {
$attributes['_permissions'] = json_encode($updates->getPermissions());
}

if (empty($attributes)) {
return 0;
}

$name = $this->filter($collection);

$columns = '';

$where = [];

$ids = \array_map(fn ($document) => $document->getId(), $documents);
$where[] = "_uid IN (" . \implode(', ', \array_map(fn ($index) => ":_id_{$index}", \array_keys($ids))) . ")";

if ($this->sharedTables) {
$where[] = "_tenant = :_tenant";
}

$sqlWhere = 'WHERE ' . implode(' AND ', $where);

$bindIndex = 0;
foreach ($attributes as $attribute => $value) {
$column = $this->filter($attribute);
$bindKey = 'key_' . $bindIndex;
$columns .= "`{$column}`" . '=:' . $bindKey;

if ($attribute !== \array_key_last($attributes)) {
$columns .= ',';
}

$bindIndex++;
}

$sql = "
UPDATE {$this->getSQLTable($name)}
SET {$columns}
{$sqlWhere}
";

$sql = $this->trigger(Database::EVENT_DOCUMENTS_UPDATE, $sql);
$stmt = $this->getPDO()->prepare($sql);

if ($this->sharedTables) {
$stmt->bindValue(':_tenant', $this->tenant);
}

foreach ($ids as $id => $value) {
$stmt->bindValue(":_id_{$id}", $value);
}

$attributeIndex = 0;
foreach ($attributes as $attribute => $value) {
if (is_array($value)) {
$value = json_encode($value);
}

$bindKey = 'key_' . $attributeIndex;
$value = (is_bool($value)) ? (int)$value : $value;
$stmt->bindValue(':' . $bindKey, $value, $this->getPDOType($value));
$attributeIndex++;
}

$stmt->execute();
$affected = $stmt->rowCount();

// Permissions logic
if (!empty($updates->getPermissions())) {
$removeQueries = [];
$removeBindValues = [];

$addQuery = '';
$addBindValues = [];

/* @var $document Document */
foreach ($documents as $index => $document) {
// Permissions logic
$sql = "
SELECT _type, _permission
FROM {$this->getSQLTable($name . '_perms')}
WHERE _document = :_uid
{$this->getTenantQuery($collection)}
";

$sql = $this->trigger(Database::EVENT_PERMISSIONS_READ, $sql);

$permissionsStmt = $this->getPDO()->prepare($sql);
$permissionsStmt->bindValue(':_uid', $document->getId());

if ($this->sharedTables) {
$permissionsStmt->bindValue(':_tenant', $this->tenant);
}

$permissionsStmt->execute();
$permissions = $permissionsStmt->fetchAll();
$permissionsStmt->closeCursor();

$initial = [];
foreach (Database::PERMISSIONS as $type) {
$initial[$type] = [];
}

$permissions = \array_reduce($permissions, function (array $carry, array $item) {
$carry[$item['_type']][] = $item['_permission'];
return $carry;
}, $initial);

// Get removed Permissions
$removals = [];
foreach (Database::PERMISSIONS as $type) {
$diff = array_diff($permissions[$type], $updates->getPermissionsByType($type));
if (!empty($diff)) {
$removals[$type] = $diff;
}
}

// Build inner query to remove permissions
if (!empty($removals)) {
foreach ($removals as $type => $permissionsToRemove) {
$bindKey = 'uid_' . $index;
$removeBindKeys[] = ':uid_' . $index;
$removeBindValues[$bindKey] = $document->getId();

$removeQueries[] = "(
_document = :uid_{$index}
{$this->getTenantQuery($collection)}
AND _type = '{$type}'
AND _permission IN (" . \implode(', ', \array_map(function (string $i) use ($permissionsToRemove, $index, $type, &$removeBindKeys, &$removeBindValues) {
$bindKey = 'remove_' . $type . '_' . $index . '_' . $i;
$removeBindKeys[] = ':' . $bindKey;
$removeBindValues[$bindKey] = $permissionsToRemove[$i];

return ':' . $bindKey;
}, \array_keys($permissionsToRemove))) .
")
)";
}
}

// Get added Permissions
$additions = [];
foreach (Database::PERMISSIONS as $type) {
$diff = \array_diff($updates->getPermissionsByType($type), $permissions[$type]);
if (!empty($diff)) {
$additions[$type] = $diff;
}
}

// Build inner query to add permissions
if (!empty($additions)) {
foreach ($additions as $type => $permissionsToAdd) {
foreach ($permissionsToAdd as $i => $permission) {
$bindKey = 'uid_' . $index;
$addBindValues[$bindKey] = $document->getId();

$bindKey = 'add_' . $type . '_' . $index . '_' . $i;
$addBindValues[$bindKey] = $permission;

$addQuery .= "(:uid_{$index}, '{$type}', :{$bindKey}";

if ($this->sharedTables) {
$addQuery .= ", :_tenant)";
} else {
$addQuery .= ")";
}

if ($i !== \array_key_last($permissionsToAdd) || $type !== \array_key_last($additions)) {
$addQuery .= ', ';
}
}
}
if ($index !== \array_key_last($documents)) {
$addQuery .= ', ';
}
}
}

if (!empty($removeQueries)) {
$removeQuery = \implode(' OR ', $removeQueries);

$stmtRemovePermissions = $this->getPDO()->prepare("
DELETE
FROM {$this->getSQLTable($name . '_perms')}
WHERE ({$removeQuery})
");

foreach ($removeBindValues as $key => $value) {
$stmtRemovePermissions->bindValue($key, $value, $this->getPDOType($value));
}

if ($this->sharedTables) {
$stmtRemovePermissions->bindValue(':_tenant', $this->tenant);
}
$stmtRemovePermissions->execute();
}

if (!empty($addQuery)) {
$sqlAddPermissions = "
INSERT INTO {$this->getSQLTable($name . '_perms')} (`_document`, `_type`, `_permission`
";

if ($this->sharedTables) {
$sqlAddPermissions .= ', `_tenant`)';
} else {
$sqlAddPermissions .= ')';
}

$sqlAddPermissions .= " VALUES {$addQuery}";

$stmtAddPermissions = $this->getPDO()->prepare($sqlAddPermissions);

foreach ($addBindValues as $key => $value) {
$stmtAddPermissions->bindValue($key, $value, $this->getPDOType($value));
}

if ($this->sharedTables) {
$stmtAddPermissions->bindValue(':_tenant', $this->tenant);
}

$stmtAddPermissions->execute();
}
}

return $affected;
}

/**
* @param string $collection
Expand Down Expand Up @@ -1943,79 +1695,6 @@ public function deleteDocument(string $collection, string $id): bool
return $deleted;
}

/**
* Delete Documents
*
* @param string $collection
* @param array<string> $ids
*
* @return int
*/
public function deleteDocuments(string $collection, array $ids): int
{
if (empty($ids)) {
return 0;
}

try {
$name = $this->filter($collection);
$where = [];

if ($this->sharedTables) {
$where[] = "_tenant = :_tenant";
}

$where[] = "_uid IN (" . \implode(', ', \array_map(fn ($index) => ":_id_{$index}", \array_keys($ids))) . ")";

$sql = "DELETE FROM {$this->getSQLTable($name)} WHERE " . \implode(' AND ', $where);

$sql = $this->trigger(Database::EVENT_DOCUMENTS_DELETE, $sql);

$stmt = $this->getPDO()->prepare($sql);

foreach ($ids as $id => $value) {
$stmt->bindValue(":_id_{$id}", $value);
}

if ($this->sharedTables) {
$stmt->bindValue(':_tenant', $this->tenant);
}

$sql = "
DELETE FROM {$this->getSQLTable($name . '_perms')}
WHERE _document IN (" . \implode(', ', \array_map(fn ($index) => ":_id_{$index}", \array_keys($ids))) . ")
";

if ($this->sharedTables) {
$sql .= ' AND _tenant = :_tenant';
}

$sql = $this->trigger(Database::EVENT_PERMISSIONS_DELETE, $sql);

$stmtPermissions = $this->getPDO()->prepare($sql);

foreach ($ids as $id => $value) {
$stmtPermissions->bindValue(":_id_{$id}", $value);
}

if ($this->sharedTables) {
$stmtPermissions->bindValue(':_tenant', $this->tenant);
}

if (!$stmt->execute()) {
throw new DatabaseException('Failed to delete documents');
}

if (!$stmtPermissions->execute()) {
throw new DatabaseException('Failed to delete permissions');
}
} catch (\Throwable $e) {
throw new DatabaseException($e->getMessage(), $e->getCode(), $e);
}

return $stmt->rowCount();
}

/**
* Find Documents
*
Expand Down Expand Up @@ -2751,4 +2430,9 @@ public function getSupportForSchemaAttributes(): bool
return true;
}

protected function quote(string $string): string
{
return "`{$string}`";
}

}
Loading