From d543705a543c8c7f75a459228e68d29207cb5bae Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Wed, 15 Feb 2023 20:35:24 +1300 Subject: [PATCH 01/10] Migration for database update --- app/tasks/migrate.php | 1 + src/Appwrite/Migration/Migration.php | 33 +++++++ src/Appwrite/Migration/Version/V15.php | 5 - src/Appwrite/Migration/Version/V18.php | 123 +++++++++++++++++++++++++ 4 files changed, 157 insertions(+), 5 deletions(-) create mode 100644 src/Appwrite/Migration/Version/V18.php diff --git a/app/tasks/migrate.php b/app/tasks/migrate.php index 1d155c210d..ded157e54b 100644 --- a/app/tasks/migrate.php +++ b/app/tasks/migrate.php @@ -85,6 +85,7 @@ $cli try { $migration ->setProject($project, $projectDB, $consoleDB) + ->setPDO($register->get('db')) ->execute(); } catch (\Throwable $th) { throw $th; diff --git a/src/Appwrite/Migration/Migration.php b/src/Appwrite/Migration/Migration.php index 6f8c51201e..6081f4cc5f 100644 --- a/src/Appwrite/Migration/Migration.php +++ b/src/Appwrite/Migration/Migration.php @@ -37,6 +37,8 @@ abstract class Migration */ protected Database $consoleDB; + protected \PDO $pdo; + /** * @var array */ @@ -49,6 +51,7 @@ abstract class Migration '1.1.1' => 'V16', '1.1.2' => 'V16', '1.2.0' => 'V17', + '1.3.0' => 'V18', ]; /** @@ -97,6 +100,13 @@ abstract class Migration return $this; } + public function setPDO(\PDO $pdo): self + { + $this->pdo = $pdo; + + return $this; + } + /** * Iterates through every document. * @@ -330,6 +340,29 @@ abstract class Migration ); } + /** + * Change a collection attribute's internal type + * + * @param string $collection + * @param string $attribute + * @param string $type + * @return void + */ + protected function changeAttributeInternalType(string $collection, string $attribute, string $type): void + { + $stmt = $this->pdo->prepare('ALTER TABLE :table MODIFY :attribute :type;'); + + $stmt->bindValue(':table', "{$this->projectDB->getDefaultDatabase()}`.`_{$this->project->getInternalId()}_{$collection}`"); + $stmt->bindValue(':attribute', $attribute); + $stmt->bindValue(':type', $type); + + try { + $stmt->execute(); + } catch (\Exception $e) { + Console::warning($e->getMessage()); + } + } + /** * Executes migration for set project. */ diff --git a/src/Appwrite/Migration/Version/V15.php b/src/Appwrite/Migration/Version/V15.php index ac948d01d2..60f5fa20ab 100644 --- a/src/Appwrite/Migration/Version/V15.php +++ b/src/Appwrite/Migration/Version/V15.php @@ -17,11 +17,6 @@ use Utopia\Database\Helpers\Role; class V15 extends Migration { - /** - * @var \PDO $pdo - */ - private $pdo; - /** * @var array */ diff --git a/src/Appwrite/Migration/Version/V18.php b/src/Appwrite/Migration/Version/V18.php new file mode 100644 index 0000000000..c2345cad1d --- /dev/null +++ b/src/Appwrite/Migration/Version/V18.php @@ -0,0 +1,123 @@ +redis = $register->get('cache'); + + /** + * Disable SubQueries for Performance. + */ + foreach (['subQueryIndexes', 'subQueryPlatforms', 'subQueryDomains', 'subQueryKeys', 'subQueryWebhooks', 'subQuerySessions', 'subQueryTokens', 'subQueryMemberships', 'subQueryVariables'] as $name) { + Database::addFilter( + $name, + fn () => null, + fn () => [] + ); + } + + Console::log('Migrating Project: ' . $this->project->getAttribute('name') . ' (' . $this->project->getId() . ')'); + $this->projectDB->setNamespace("_{$this->project->getInternalId()}"); + + Console::info('Migrating Databases'); + $this->migrateDatabases(); + + Console::info('Migrating Collections'); + $this->migrateCollections(); + + Console::info('Migrating Documents'); + $this->forEachDocument([$this, 'migrateDocument']); + + Console::info('Migrating Cache'); + $this->forEachDocument([$this, 'migrateCache']); + } + + /** + * Migrate all Databases. + * + * @return void + * @throws \Exception + */ + private function migrateDatabases(): void + { + foreach ($this->documentsIterator('databases') as $database) { + $databaseTable = "database_{$database->getInternalId()}"; + + Console::info("Migrating Collections of {$database->getId()} ({$database->getAttribute('name')})"); + foreach ($this->documentsIterator($databaseTable) as $collection) { + $collectionTable = "{$databaseTable}_collection_{$collection->getInternalId()}"; + + $floats = \array_filter($collection->getAttributes(), function ($attribute) { + return $attribute->getAttribute('type') === Database::VAR_FLOAT; + }); + + foreach ($floats as $attribute) { + $this->changeAttributeInternalType($collectionTable, $attribute->getId(), 'DOUBLE'); + } + } + } + } + + /** + * Migrate all Collections. + * + * @return void + */ + private function migrateCollections(): void + { + foreach ($this->collections as $collection) { + $id = $collection['$id']; + + Console::log("Migrating Collection \"{$id}\""); + $floats = \array_filter($collection, function ($attribute) { + return $attribute['type'] === Database::VAR_FLOAT; + }); + + foreach ($floats as $attribute) { + $this->changeAttributeInternalType($id, $attribute->getId(), 'DOUBLE'); + } + } + } + + /** + * Fix run on each document + * + * @param Document $document + * @return Document + */ + private function migrateDocument(Document $document): Document + { + switch ($document->getCollection()) { + case 'projects': + $document->setAttribute('version', '1.3.0'); + break; + } + + return $document; + } + + private function migrateCache(Document $document) { + $key = "cache-_{$this->project->getInternalId()}:_{$document->getCollection()}:{$document->getId()}"; + $value = $this->redis->get($key); + + if ($value) { + $this->redis->del($key); + $this->redis->set( $key. ':*', $value); + } + } +} From 28a9a373b11fdec0eb78fb9050296636e714a962 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Tue, 11 Apr 2023 22:27:29 +1200 Subject: [PATCH 02/10] Fix binding --- src/Appwrite/Migration/Migration.php | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/Appwrite/Migration/Migration.php b/src/Appwrite/Migration/Migration.php index 767fc0c4ec..9b17b30c2d 100644 --- a/src/Appwrite/Migration/Migration.php +++ b/src/Appwrite/Migration/Migration.php @@ -351,11 +351,7 @@ abstract class Migration */ protected function changeAttributeInternalType(string $collection, string $attribute, string $type): void { - $stmt = $this->pdo->prepare('ALTER TABLE :table MODIFY :attribute :type;'); - - $stmt->bindValue(':table', "{$this->projectDB->getDefaultDatabase()}`.`_{$this->project->getInternalId()}_{$collection}`"); - $stmt->bindValue(':attribute', $attribute); - $stmt->bindValue(':type', $type); + $stmt = $this->pdo->prepare("ALTER TABLE `{$this->projectDB->getDefaultDatabase()}`.`_{$this->project->getInternalId()}_{$collection}` MODIFY $attribute $type;"); try { $stmt->execute(); From c60d7764e41312d859296d22f9beba4d78006732 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Tue, 11 Apr 2023 22:27:49 +1200 Subject: [PATCH 03/10] Fix cache key --- src/Appwrite/Migration/Version/V18.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Appwrite/Migration/Version/V18.php b/src/Appwrite/Migration/Version/V18.php index 3b1a4fde85..7ae982da5c 100644 --- a/src/Appwrite/Migration/Version/V18.php +++ b/src/Appwrite/Migration/Version/V18.php @@ -149,7 +149,7 @@ class V18 extends Migration private function migrateCache(Document $document) { - $key = "cache-_{$this->project->getInternalId()}:_{$document->getCollection()}:{$document->getId()}"; + $key = "cache-_{$this->project->getInternalId()}:{$document->getCollection()}:{$document->getId()}"; $value = $this->redis->get($key); if ($value) { From 0f1c1fda9e30b9a5bdbb690abad3c52e3d120751 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Tue, 11 Apr 2023 22:30:45 +1200 Subject: [PATCH 04/10] Quote attribute name --- src/Appwrite/Migration/Migration.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Appwrite/Migration/Migration.php b/src/Appwrite/Migration/Migration.php index 9b17b30c2d..62c4dfba63 100644 --- a/src/Appwrite/Migration/Migration.php +++ b/src/Appwrite/Migration/Migration.php @@ -351,7 +351,7 @@ abstract class Migration */ protected function changeAttributeInternalType(string $collection, string $attribute, string $type): void { - $stmt = $this->pdo->prepare("ALTER TABLE `{$this->projectDB->getDefaultDatabase()}`.`_{$this->project->getInternalId()}_{$collection}` MODIFY $attribute $type;"); + $stmt = $this->pdo->prepare("ALTER TABLE `{$this->projectDB->getDefaultDatabase()}`.`_{$this->project->getInternalId()}_{$collection}` MODIFY `$attribute` $type;"); try { $stmt->execute(); From a2cb3c135fb15af0993b6ffd869882025da6760d Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Tue, 11 Apr 2023 23:42:06 +1200 Subject: [PATCH 05/10] Fix migration of floats --- app/init.php | 2 +- app/tasks/migrate.php | 2 +- src/Appwrite/Migration/Version/V18.php | 29 ++++++++------------------ 3 files changed, 11 insertions(+), 22 deletions(-) diff --git a/app/init.php b/app/init.php index 6620985cdc..980040da68 100644 --- a/app/init.php +++ b/app/init.php @@ -101,7 +101,7 @@ const APP_LIMIT_LIST_DEFAULT = 25; // Default maximum number of items to return const APP_KEY_ACCCESS = 24 * 60 * 60; // 24 hours const APP_CACHE_UPDATE = 24 * 60 * 60; // 24 hours const APP_CACHE_BUSTER = 501; -const APP_VERSION_STABLE = '1.2.1'; +const APP_VERSION_STABLE = '1.3.0'; const APP_DATABASE_ATTRIBUTE_EMAIL = 'email'; const APP_DATABASE_ATTRIBUTE_ENUM = 'enum'; const APP_DATABASE_ATTRIBUTE_IP = 'ip'; diff --git a/app/tasks/migrate.php b/app/tasks/migrate.php index ded157e54b..7bb52f8970 100644 --- a/app/tasks/migrate.php +++ b/app/tasks/migrate.php @@ -88,8 +88,8 @@ $cli ->setPDO($register->get('db')) ->execute(); } catch (\Throwable $th) { - throw $th; Console::error('Failed to update project ("' . $project->getId() . '") version with error: ' . $th->getMessage()); + throw $th; } clearProjectsCache($redis, $project); diff --git a/src/Appwrite/Migration/Version/V18.php b/src/Appwrite/Migration/Version/V18.php index 7ae982da5c..7951168568 100644 --- a/src/Appwrite/Migration/Version/V18.php +++ b/src/Appwrite/Migration/Version/V18.php @@ -41,10 +41,9 @@ class V18 extends Migration $this->migrateCollections(); Console::info('Migrating Documents'); - $this->forEachDocument([$this, 'migrateDocument']); - - Console::info('Migrating Cache'); - $this->forEachDocument([$this, 'migrateCache']); + $this->forEachDocument(function (Document $document) { + $this->migrateDocument($document); + }); } /** @@ -59,15 +58,16 @@ class V18 extends Migration $databaseTable = "database_{$database->getInternalId()}"; Console::info("Migrating Collections of {$database->getId()} ({$database->getAttribute('name')})"); + foreach ($this->documentsIterator($databaseTable) as $collection) { $collectionTable = "{$databaseTable}_collection_{$collection->getInternalId()}"; - $floats = \array_filter($collection->getAttributes(), function ($attribute) { - return $attribute->getAttribute('type') === Database::VAR_FLOAT; + $floats = \array_filter($collection['attributes'] ?? [], function ($attribute) { + return $attribute['type'] === Database::VAR_FLOAT; }); foreach ($floats as $attribute) { - $this->changeAttributeInternalType($collectionTable, $attribute->getId(), 'DOUBLE'); + $this->changeAttributeInternalType($collectionTable, $attribute['key'], 'DOUBLE'); } } } @@ -85,12 +85,12 @@ class V18 extends Migration Console::log("Migrating Collection \"{$id}\""); - $floats = \array_filter($collection, function ($attribute) { + $floats = \array_filter($collection['attributes'] ?? [], function ($attribute) { return $attribute['type'] === Database::VAR_FLOAT; }); foreach ($floats as $attribute) { - $this->changeAttributeInternalType($id, $attribute->getId(), 'DOUBLE'); + $this->changeAttributeInternalType($id, $attribute['$id'], 'DOUBLE'); } switch ($id) { @@ -146,15 +146,4 @@ class V18 extends Migration return $document; } - - private function migrateCache(Document $document) - { - $key = "cache-_{$this->project->getInternalId()}:{$document->getCollection()}:{$document->getId()}"; - $value = $this->redis->get($key); - - if ($value) { - $this->redis->del($key); - $this->redis->set($key . ':*', $value); - } - } } From d46f3092e5a1eef3b3285f1cec8a7ed231c6ecc6 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Wed, 12 Apr 2023 00:04:53 +1200 Subject: [PATCH 06/10] Remove redis and unused imports --- src/Appwrite/Migration/Version/V18.php | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/Appwrite/Migration/Version/V18.php b/src/Appwrite/Migration/Version/V18.php index 7951168568..adc7c4eb1d 100644 --- a/src/Appwrite/Migration/Version/V18.php +++ b/src/Appwrite/Migration/Version/V18.php @@ -2,23 +2,15 @@ namespace Appwrite\Migration\Version; -use Appwrite\Auth\Auth; use Appwrite\Migration\Migration; -use Appwrite\Query; -use PDO; use Utopia\CLI\Console; use Utopia\Database\Database; use Utopia\Database\Document; class V18 extends Migration { - private \Redis $redis; - public function execute(): void { - global $register; - - $this->redis = $register->get('cache'); /** * Disable SubQueries for Performance. From b0a6190394dc93e6d818cd3ab944d32e4d15f051 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Wed, 12 Apr 2023 00:05:19 +1200 Subject: [PATCH 07/10] Only iterate attributes once --- src/Appwrite/Migration/Version/V18.php | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/src/Appwrite/Migration/Version/V18.php b/src/Appwrite/Migration/Version/V18.php index adc7c4eb1d..aa7ee165a8 100644 --- a/src/Appwrite/Migration/Version/V18.php +++ b/src/Appwrite/Migration/Version/V18.php @@ -54,12 +54,11 @@ class V18 extends Migration foreach ($this->documentsIterator($databaseTable) as $collection) { $collectionTable = "{$databaseTable}_collection_{$collection->getInternalId()}"; - $floats = \array_filter($collection['attributes'] ?? [], function ($attribute) { - return $attribute['type'] === Database::VAR_FLOAT; - }); - - foreach ($floats as $attribute) { - $this->changeAttributeInternalType($collectionTable, $attribute['key'], 'DOUBLE'); + foreach ($collection['attributes'] ?? [] as $attribute) { + if ($attribute['type'] !== Database::VAR_FLOAT) { + continue; + } + $this->changeAttributeInternalType($collectionTable, $attribute['$id'], 'DOUBLE'); } } } @@ -77,11 +76,10 @@ class V18 extends Migration Console::log("Migrating Collection \"{$id}\""); - $floats = \array_filter($collection['attributes'] ?? [], function ($attribute) { - return $attribute['type'] === Database::VAR_FLOAT; - }); - - foreach ($floats as $attribute) { + foreach ($collection['attributes'] ?? [] as $attribute) { + if ($attribute['type'] !== Database::VAR_FLOAT) { + continue; + } $this->changeAttributeInternalType($id, $attribute['$id'], 'DOUBLE'); } From 43946cade51120cec57d0274c669c7138c137bff Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Wed, 12 Apr 2023 00:33:29 +1200 Subject: [PATCH 08/10] Add teams migration --- src/Appwrite/Migration/Version/V18.php | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/Appwrite/Migration/Version/V18.php b/src/Appwrite/Migration/Version/V18.php index aa7ee165a8..8f11e37a22 100644 --- a/src/Appwrite/Migration/Version/V18.php +++ b/src/Appwrite/Migration/Version/V18.php @@ -58,7 +58,7 @@ class V18 extends Migration if ($attribute['type'] !== Database::VAR_FLOAT) { continue; } - $this->changeAttributeInternalType($collectionTable, $attribute['$id'], 'DOUBLE'); + $this->changeAttributeInternalType($collectionTable, $attribute['key'], 'DOUBLE'); } } } @@ -95,6 +95,17 @@ class V18 extends Migration Console::warning("'passwordHistory' from {$id}: {$th->getMessage()}"); } break; + case 'teams': + try { + /** + * Create 'prefs' attribute + */ + $this->createAttributeFromCollection($this->projectDB, $id, 'prefs'); + $this->projectDB->deleteCachedCollection($id); + } catch (\Throwable $th) { + Console::warning("'prefs' from {$id}: {$th->getMessage()}"); + } + break; default: break; } @@ -132,6 +143,12 @@ class V18 extends Migration */ $document->setAttribute('passwordHistory', []); break; + case 'teams': + /** + * Default prefs + */ + $document->setAttribute('prefs', new \stdClass()); + break; } return $document; From de2ea95099602fa03e3f3b8c3bd7556fd00c81a8 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Wed, 12 Apr 2023 00:34:29 +1200 Subject: [PATCH 09/10] Update changelog --- CHANGES.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index b5059a8f4f..7d35be465d 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,13 +1,20 @@ -# Version TBD +# Version 1.3.0 ## Features - Password dictionary setting allows to compare user's password against command password database [4906](https://github.com/appwrite/appwrite/pull/4906) - Password history setting allows to save user's last used password so that it may not be used again. Maximum number of history saved is 20, which can be configured. Minimum is 0 which means disabled. [#4866](https://github.com/appwrite/appwrite/pull/4866) - Update APIs to check X-Appwrite-Timestamp header [#5024](https://github.com/appwrite/appwrite/pull/5024) +- Database relationships [#5238](https://github.com/appwrite/appwrite/pull/5238) +- New query operators [#5238](https://github.com/appwrite/appwrite/pull/5238) +- Team preferences [#5196](https://github.com/appwrite/appwrite/pull/5196) +- Update attribute metadata [#5164](https://github.com/appwrite/appwrite/pull/5164) ## Bugs - Fix not storing function's response on response codes 5xx [#4610](https://github.com/appwrite/appwrite/pull/4610) - Fix expire to formatTz in create account session [#4985](https://github.com/appwrite/appwrite/pull/4985) +- Fix creating documents with attributes with special characters [#246](https://github.com/utopia-php/database/pull/246) +- Fix deleting attribute not deleting metadata index [#246](https://github.com/utopia-php/database/pull/246) +- Fix create attribute event payload [#246](https://github.com/utopia-php/database/pull/246) # Version 1.2.1 ## Changes From bf73b1a55f0e887cde8280f74d058100139ea992 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Wed, 12 Apr 2023 00:38:07 +1200 Subject: [PATCH 10/10] Update changelog --- CHANGES.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 7d35be465d..e5ab933e04 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -12,6 +12,8 @@ ## Bugs - Fix not storing function's response on response codes 5xx [#4610](https://github.com/appwrite/appwrite/pull/4610) - Fix expire to formatTz in create account session [#4985](https://github.com/appwrite/appwrite/pull/4985) +- Fix deleting collections from a project [#4983](https://github.com/appwrite/appwrite/pull/4983) +- Fix cleaning up project databases [#4984](https://github.com/appwrite/appwrite/pull/4984) - Fix creating documents with attributes with special characters [#246](https://github.com/utopia-php/database/pull/246) - Fix deleting attribute not deleting metadata index [#246](https://github.com/utopia-php/database/pull/246) - Fix create attribute event payload [#246](https://github.com/utopia-php/database/pull/246)