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
130 changes: 40 additions & 90 deletions src/Database/Database.php
Original file line number Diff line number Diff line change
Expand Up @@ -1462,17 +1462,8 @@ public function createAttribute(string $collection, string $id, string $type, in
throw new DatabaseException("Attribute of type: $type requires the following filters: " . implode(",", $requiredFilters));
}

if (
$this->adapter->getLimitForAttributes() > 0 &&
$this->adapter->getCountOfAttributes($collection) >= $this->adapter->getLimitForAttributes()
) {
throw new LimitException('Column limit reached. Cannot create new attribute.');
}

if ($format) {
if (!Structure::hasFormat($format, $type)) {
throw new DatabaseException('Format ("' . $format . '") not available for this attribute type ("' . $type . '")');
}
if ($format && !Structure::hasFormat($format, $type)) {
throw new DatabaseException('Format ("' . $format . '") not available for this attribute type ("' . $type . '")');
}

$attribute = new Document([
Expand All @@ -1489,22 +1480,20 @@ public function createAttribute(string $collection, string $id, string $type, in
'filters' => $filters,
]);

$collection->setAttribute('attributes', $attribute, Document::SET_TYPE_APPEND);
$this->checkAttribute($collection, $attribute);

if (
$this->adapter->getDocumentSizeLimit() > 0 &&
$this->adapter->getAttributeWidth($collection) >= $this->adapter->getDocumentSizeLimit()
) {
throw new LimitException('Row width limit reached. Cannot create new attribute.');
}
$collection->setAttribute(
'attributes',
$attribute,
Document::SET_TYPE_APPEND
);

switch ($type) {
case self::VAR_STRING:
if ($size > $this->adapter->getLimitForString()) {
throw new DatabaseException('Max size allowed for string is: ' . number_format($this->adapter->getLimitForString()));
}
break;

case self::VAR_INTEGER:
$limit = ($signed) ? $this->adapter->getLimitForInt() / 2 : $this->adapter->getLimitForInt();
if ($size > $limit) {
Expand Down Expand Up @@ -2259,8 +2248,24 @@ public function createRelationship(
}

$this->silent(function () use ($collection, $relatedCollection, $type, $twoWay, $id, $twoWayKey) {
$this->updateDocument(self::METADATA, $collection->getId(), $collection);
$this->updateDocument(self::METADATA, $relatedCollection->getId(), $relatedCollection);
try {
$this->withTransaction(function () use ($collection, $relatedCollection) {
$this->updateDocument(self::METADATA, $collection->getId(), $collection);
$this->updateDocument(self::METADATA, $relatedCollection->getId(), $relatedCollection);
});
} catch (\Throwable $e) {
$this->adapter->deleteRelationship(
$collection->getId(),
$relatedCollection->getId(),
$type,
$twoWay,
$id,
$twoWayKey,
Database::RELATION_SIDE_PARENT
);

throw new DatabaseException('Failed to create relationship: ' . $e->getMessage());
}

$indexKey = '_index_' . $id;
$twoWayIndexKey = '_index_' . $twoWayKey;
Expand Down Expand Up @@ -2328,13 +2333,13 @@ public function updateRelationship(
!\is_null($newKey)
&& \in_array($newKey, \array_map(fn ($attribute) => $attribute['key'], $attributes))
) {
throw new DuplicateException('Attribute already exists');
throw new DuplicateException('Relationship already exists');
}

$attributeIndex = array_search($id, array_map(fn ($attribute) => $attribute['$id'], $attributes));

if ($attributeIndex === false) {
throw new NotFoundException('Attribute not found');
throw new NotFoundException('Relationship not found');
}

$attribute = $attributes[$attributeIndex];
Expand Down Expand Up @@ -2521,7 +2526,7 @@ public function deleteRelationship(string $collection, string $id): bool
}

if (\is_null($relationship)) {
throw new NotFoundException('Attribute not found');
throw new NotFoundException('Relationship not found');
}

$collection->setAttribute('attributes', \array_values($attributes));
Expand All @@ -2545,8 +2550,14 @@ public function deleteRelationship(string $collection, string $id): bool
$relatedCollection->setAttribute('attributes', \array_values($relatedAttributes));

$this->silent(function () use ($collection, $relatedCollection, $type, $twoWay, $id, $twoWayKey, $side) {
$this->updateDocument(self::METADATA, $collection->getId(), $collection);
$this->updateDocument(self::METADATA, $relatedCollection->getId(), $relatedCollection);
try {
$this->withTransaction(function () use ($collection, $relatedCollection) {
$this->updateDocument(self::METADATA, $collection->getId(), $collection);
$this->updateDocument(self::METADATA, $relatedCollection->getId(), $relatedCollection);
});
} catch (\Throwable $e) {
throw new DatabaseException('Failed to delete relationship: ' . $e->getMessage());
}

$indexKey = '_index_' . $id;
$twoWayIndexKey = '_index_' . $twoWayKey;
Expand Down Expand Up @@ -2992,35 +3003,9 @@ public function getDocument(string $collection, string $id, array $queries = [],
$attribute['type'] === Database::VAR_RELATIONSHIP
);

$hasTwoWayRelationship = false;
foreach ($relationships as $relationship) {
if ($relationship['options']['twoWay']) {
$hasTwoWayRelationship = true;
break;
}
}

/**
* Bug with function purity in PHPStan means it thinks $this->map is always empty
* @phpstan-ignore-next-line
*/
foreach ($this->map as $key => $value) {
[$k, $v] = \explode('=>', $key);
$ck = $this->cacheName . '-cache-' . $this->getNamespace() . ':' . $this->adapter->getTenant() . ':map:' . $k;
$cache = $this->cache->load($ck, self::TTL, $ck);
if (empty($cache)) {
$cache = [];
}
if (!\in_array($v, $cache)) {
$cache[] = $v;
$this->cache->save($ck, $cache, $ck);
}
}

// Don't save to cache if it's part of a relationship
if (!$hasTwoWayRelationship && empty($relationships)) {
if (empty($relationships)) {
$this->cache->save($documentCacheKey, $document->getArrayCopy(), $documentCacheHash);
// Add document reference to the collection key
$this->cache->save($collectionCacheKey, 'empty', $documentCacheKey);
}

Expand Down Expand Up @@ -3948,7 +3933,6 @@ public function updateDocument(string $collection, string $id, Document $documen

$document = $this->decode($collection, $document);

$this->purgeRelatedDocuments($collection, $id);
$this->purgeCachedDocument($collection->getId(), $id);
$this->trigger(self::EVENT_DOCUMENT_UPDATE, $document);

Expand Down Expand Up @@ -4103,7 +4087,6 @@ public function updateDocuments(string $collection, Document $updates, array $qu
}

foreach ($documents as $document) {
$this->purgeRelatedDocuments($collection, $document->getId());
$this->purgeCachedDocument($collection->getId(), $document->getId());
}

Expand Down Expand Up @@ -4752,7 +4735,6 @@ public function deleteDocument(string $collection, string $id): bool
return $this->adapter->deleteDocument($collection->getId(), $id);
});

$this->purgeRelatedDocuments($collection, $id);
$this->purgeCachedDocument($collection->getId(), $id);

$this->trigger(self::EVENT_DOCUMENT_DELETE, $document);
Expand Down Expand Up @@ -5246,7 +5228,6 @@ public function deleteDocuments(string $collection, array $queries = [], int $ba
throw new ConflictException('Document was updated after the request timestamp');
}

$this->purgeRelatedDocuments($collection, $document->getId());
$this->purgeCachedDocument($collection->getId(), $document->getId());
}

Expand Down Expand Up @@ -5292,6 +5273,8 @@ public function purgeCachedCollection(string $collectionId): bool
$this->cache->purge($documentKey);
}

$this->cache->purge($collectionKey);

return true;
}

Expand Down Expand Up @@ -5993,39 +5976,6 @@ public static function convertQueries(Document $collection, array $queries): arr
return $queries;
}

/**
* @param Document $collection
* @param string $id
* @return void
* @throws DatabaseException
*/
private function purgeRelatedDocuments(Document $collection, string $id): void
{
if ($collection->getId() === self::METADATA) {
return;
}

$relationships = \array_filter(
$collection->getAttribute('attributes', []),
fn ($attribute) =>
$attribute['type'] === Database::VAR_RELATIONSHIP
);

if (empty($relationships)) {
return;
}

$key = $this->cacheName . '-cache-' . $this->getNamespace() . ':map:' . $collection->getId() . ':' . $id;
$cache = $this->cache->load($key, self::TTL, $key);
if (!empty($cache)) {
foreach ($cache as $v) {
list($collectionId, $documentId) = explode(':', $v);
$this->purgeCachedDocument($collectionId, $documentId);
}
$this->cache->purge($key);
}
}

/**
* @return array<array<string, mixed>>
*/
Expand Down
14 changes: 8 additions & 6 deletions tests/e2e/Adapter/Base.php
Original file line number Diff line number Diff line change
Expand Up @@ -7897,7 +7897,7 @@ public function testIdenticalTwoWayKeyRelationship(): void
);
$this->fail('Failed to throw Exception');
} catch (Exception $e) {
$this->assertEquals('Attribute already exists', $e->getMessage());
$this->assertEquals('Relationship already exists', $e->getMessage());
}

try {
Expand Down Expand Up @@ -12955,10 +12955,12 @@ public function testDeleteMissingRelationship(): void
return;
}

$this->expectException(Exception::class);
$this->expectExceptionMessage('Attribute not found');

static::getDatabase()->deleteRelationship('test', 'test2');
try {
static::getDatabase()->deleteRelationship('test', 'test2');
$this->fail('Failed to throw exception');
} catch (\Throwable $e) {
$this->assertEquals('Relationship not found', $e->getMessage());
}
}

public function testCreateInvalidIntValueRelationship(): void
Expand Down Expand Up @@ -13321,7 +13323,7 @@ public function testUpdateRelationshipToExistingKey(): void
static::getDatabase()->updateRelationship('ovens', 'cakes', newKey: 'owner');
$this->fail('Failed to throw exception');
} catch (DuplicateException $e) {
$this->assertEquals('Attribute already exists', $e->getMessage());
$this->assertEquals('Relationship already exists', $e->getMessage());
}

try {
Expand Down
Loading