From 280432a2e1d7bc4de272d83d8f26af785ca70c04 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Sat, 14 Mar 2026 21:32:20 +1300 Subject: [PATCH 1/2] fix: skip Sequence validation for $tenant attribute Tenant can be int or string depending on the adapter, so it should not go through Sequence validation which is string-only. Tenant is a framework-controlled internal attribute set via setTenant(). Co-Authored-By: Claude Opus 4.6 --- src/Database/Validator/Structure.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Database/Validator/Structure.php b/src/Database/Validator/Structure.php index a65734dbd..9644ac1d5 100644 --- a/src/Database/Validator/Structure.php +++ b/src/Database/Validator/Structure.php @@ -340,6 +340,9 @@ protected function checkForInvalidAttributeValues(array $structure, array $keys) switch ($type) { case Database::VAR_ID: + if ($attribute['$id'] === '$tenant') { + break; + } $validators[] = new Sequence($this->idAttributeType, $attribute['$id'] === '$sequence'); break; From 372e62253d96d5ad1fa2447bcf2ce2942f40437c Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Sat, 14 Mar 2026 21:52:50 +1300 Subject: [PATCH 2/2] fix: use loose comparison for tenant checks to handle int/string mismatch The casting() method converts VAR_ID to string on read, but setTenant() accepts int|string. Strict !== comparison between document tenant (string after casting) and adapter tenant (int) caused "Collection not found" errors in shared tables mode. Co-Authored-By: Claude Opus 4.6 --- src/Database/Database.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Database/Database.php b/src/Database/Database.php index 32682716a..cf91fd310 100644 --- a/src/Database/Database.php +++ b/src/Database/Database.php @@ -1856,7 +1856,7 @@ public function updateCollection(string $id, array $permissions, bool $documentS if ( $this->adapter->getSharedTables() - && $collection->getTenant() !== $this->adapter->getTenant() + && $collection->getTenant() != $this->adapter->getTenant() ) { throw new NotFoundException('Collection not found'); } @@ -1892,7 +1892,7 @@ public function getCollection(string $id): Document $id !== self::METADATA && $this->adapter->getSharedTables() && $collection->getTenant() !== null - && $collection->getTenant() !== $this->adapter->getTenant() + && $collection->getTenant() != $this->adapter->getTenant() ) { return new Document(); } @@ -1947,7 +1947,7 @@ public function getSizeOfCollection(string $collection): int throw new NotFoundException('Collection not found'); } - if ($this->adapter->getSharedTables() && $collection->getTenant() !== $this->adapter->getTenant()) { + if ($this->adapter->getSharedTables() && $collection->getTenant() != $this->adapter->getTenant()) { throw new NotFoundException('Collection not found'); } @@ -1973,7 +1973,7 @@ public function getSizeOfCollectionOnDisk(string $collection): int throw new NotFoundException('Collection not found'); } - if ($this->adapter->getSharedTables() && $collection->getTenant() !== $this->adapter->getTenant()) { + if ($this->adapter->getSharedTables() && $collection->getTenant() != $this->adapter->getTenant()) { throw new NotFoundException('Collection not found'); } @@ -2007,7 +2007,7 @@ public function deleteCollection(string $id): bool throw new NotFoundException('Collection not found'); } - if ($this->adapter->getSharedTables() && $collection->getTenant() !== $this->adapter->getTenant()) { + if ($this->adapter->getSharedTables() && $collection->getTenant() != $this->adapter->getTenant()) { throw new NotFoundException('Collection not found'); } @@ -7201,7 +7201,7 @@ public function upsertDocumentsWithIncrease( if ($document->getTenant() === null) { throw new DatabaseException('Missing tenant. Tenant must be set when tenant per document is enabled.'); } - if (!$old->isEmpty() && $old->getTenant() !== $document->getTenant()) { + if (!$old->isEmpty() && $old->getTenant() != $document->getTenant()) { throw new DatabaseException('Tenant cannot be changed.'); } } else {