From aa8f7cb348bca739af3b06895adedd551ce85889 Mon Sep 17 00:00:00 2001 From: fogelito Date: Mon, 23 Dec 2024 10:30:37 +0200 Subject: [PATCH 1/9] Dependency Exception --- composer.lock | 173 +++++++++++++------------- src/Database/Database.php | 53 +++++--- src/Database/Exception/Dependency.php | 7 ++ tests/e2e/Adapter/Base.php | 21 ++++ 4 files changed, 149 insertions(+), 105 deletions(-) create mode 100644 src/Database/Exception/Dependency.php diff --git a/composer.lock b/composer.lock index d08a958c7..a73ee5ada 100644 --- a/composer.lock +++ b/composer.lock @@ -149,16 +149,16 @@ }, { "name": "google/protobuf", - "version": "v4.28.3", + "version": "v4.29.2", "source": { "type": "git", "url": "https://github.com/protocolbuffers/protobuf-php.git", - "reference": "c5c311e0f3d89928251ac5a2f0e3db283612c100" + "reference": "79aa5014efeeec3d137df5cdb0ae2fc163953945" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/protocolbuffers/protobuf-php/zipball/c5c311e0f3d89928251ac5a2f0e3db283612c100", - "reference": "c5c311e0f3d89928251ac5a2f0e3db283612c100", + "url": "https://api.github.com/repos/protocolbuffers/protobuf-php/zipball/79aa5014efeeec3d137df5cdb0ae2fc163953945", + "reference": "79aa5014efeeec3d137df5cdb0ae2fc163953945", "shasum": "" }, "require": { @@ -187,9 +187,9 @@ "proto" ], "support": { - "source": "https://github.com/protocolbuffers/protobuf-php/tree/v4.28.3" + "source": "https://github.com/protocolbuffers/protobuf-php/tree/v4.29.2" }, - "time": "2024-10-22T22:27:17+00:00" + "time": "2024-12-18T14:11:12+00:00" }, { "name": "jean85/pretty-package-versions", @@ -465,16 +465,16 @@ }, { "name": "open-telemetry/api", - "version": "1.1.1", + "version": "1.1.2", "source": { "type": "git", "url": "https://github.com/opentelemetry-php/api.git", - "reference": "542064815d38a6df55af7957cd6f1d7d967c99c6" + "reference": "04c85a1e41a3d59fa9bdc801a5de1df6624b95ed" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/opentelemetry-php/api/zipball/542064815d38a6df55af7957cd6f1d7d967c99c6", - "reference": "542064815d38a6df55af7957cd6f1d7d967c99c6", + "url": "https://api.github.com/repos/opentelemetry-php/api/zipball/04c85a1e41a3d59fa9bdc801a5de1df6624b95ed", + "reference": "04c85a1e41a3d59fa9bdc801a5de1df6624b95ed", "shasum": "" }, "require": { @@ -488,13 +488,13 @@ }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "1.1.x-dev" - }, "spi": { "OpenTelemetry\\API\\Instrumentation\\AutoInstrumentation\\HookManagerInterface": [ "OpenTelemetry\\API\\Instrumentation\\AutoInstrumentation\\ExtensionHookManager" ] + }, + "branch-alias": { + "dev-main": "1.1.x-dev" } }, "autoload": { @@ -531,7 +531,7 @@ "issues": "https://github.com/open-telemetry/opentelemetry-php/issues", "source": "https://github.com/open-telemetry/opentelemetry-php" }, - "time": "2024-10-15T22:42:37+00:00" + "time": "2024-11-16T04:32:30+00:00" }, { "name": "open-telemetry/context", @@ -758,13 +758,13 @@ }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "1.0.x-dev" - }, "spi": { "OpenTelemetry\\API\\Instrumentation\\AutoInstrumentation\\HookManagerInterface": [ "OpenTelemetry\\API\\Instrumentation\\AutoInstrumentation\\ExtensionHookManager" ] + }, + "branch-alias": { + "dev-main": "1.0.x-dev" } }, "autoload": { @@ -1391,16 +1391,16 @@ }, { "name": "symfony/deprecation-contracts", - "version": "v3.5.0", + "version": "v3.5.1", "source": { "type": "git", "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1" + "reference": "74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1", - "reference": "0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6", + "reference": "74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6", "shasum": "" }, "require": { @@ -1438,7 +1438,7 @@ "description": "A generic function and convention to trigger deprecation notices", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/deprecation-contracts/tree/v3.5.0" + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.5.1" }, "funding": [ { @@ -1454,30 +1454,31 @@ "type": "tidelift" } ], - "time": "2024-04-18T09:32:20+00:00" + "time": "2024-09-25T14:20:29+00:00" }, { "name": "symfony/http-client", - "version": "v7.1.8", + "version": "v7.2.1", "source": { "type": "git", "url": "https://github.com/symfony/http-client.git", - "reference": "c30d91a1deac0dc3ed5e604683cf2e1dfc635b8a" + "reference": "ff4df2b68d1c67abb9fef146e6540ea16b58d99e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-client/zipball/c30d91a1deac0dc3ed5e604683cf2e1dfc635b8a", - "reference": "c30d91a1deac0dc3ed5e604683cf2e1dfc635b8a", + "url": "https://api.github.com/repos/symfony/http-client/zipball/ff4df2b68d1c67abb9fef146e6540ea16b58d99e", + "reference": "ff4df2b68d1c67abb9fef146e6540ea16b58d99e", "shasum": "" }, "require": { "php": ">=8.2", "psr/log": "^1|^2|^3", "symfony/deprecation-contracts": "^2.5|^3", - "symfony/http-client-contracts": "^3.4.1", + "symfony/http-client-contracts": "~3.4.4|^3.5.2", "symfony/service-contracts": "^2.5|^3" }, "conflict": { + "amphp/amp": "<2.5", "php-http/discovery": "<1.15", "symfony/http-foundation": "<6.4" }, @@ -1488,14 +1489,14 @@ "symfony/http-client-implementation": "3.0" }, "require-dev": { - "amphp/amp": "^2.5", - "amphp/http-client": "^4.2.1", - "amphp/http-tunnel": "^1.0", + "amphp/http-client": "^4.2.1|^5.0", + "amphp/http-tunnel": "^1.0|^2.0", "amphp/socket": "^1.1", "guzzlehttp/promises": "^1.4|^2.0", "nyholm/psr7": "^1.0", "php-http/httplug": "^1.0|^2.0", "psr/http-client": "^1.0", + "symfony/amphp-http-client-meta": "^1.0|^2.0", "symfony/dependency-injection": "^6.4|^7.0", "symfony/http-kernel": "^6.4|^7.0", "symfony/messenger": "^6.4|^7.0", @@ -1532,7 +1533,7 @@ "http" ], "support": { - "source": "https://github.com/symfony/http-client/tree/v7.1.8" + "source": "https://github.com/symfony/http-client/tree/v7.2.1" }, "funding": [ { @@ -1548,20 +1549,20 @@ "type": "tidelift" } ], - "time": "2024-11-13T13:40:27+00:00" + "time": "2024-12-07T08:50:44+00:00" }, { "name": "symfony/http-client-contracts", - "version": "v3.5.0", + "version": "v3.5.2", "source": { "type": "git", "url": "https://github.com/symfony/http-client-contracts.git", - "reference": "20414d96f391677bf80078aa55baece78b82647d" + "reference": "ee8d807ab20fcb51267fdace50fbe3494c31e645" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-client-contracts/zipball/20414d96f391677bf80078aa55baece78b82647d", - "reference": "20414d96f391677bf80078aa55baece78b82647d", + "url": "https://api.github.com/repos/symfony/http-client-contracts/zipball/ee8d807ab20fcb51267fdace50fbe3494c31e645", + "reference": "ee8d807ab20fcb51267fdace50fbe3494c31e645", "shasum": "" }, "require": { @@ -1569,12 +1570,12 @@ }, "type": "library", "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, "branch-alias": { "dev-main": "3.5-dev" - }, - "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" } }, "autoload": { @@ -1610,7 +1611,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/http-client-contracts/tree/v3.5.0" + "source": "https://github.com/symfony/http-client-contracts/tree/v3.5.2" }, "funding": [ { @@ -1626,7 +1627,7 @@ "type": "tidelift" } ], - "time": "2024-04-18T09:32:20+00:00" + "time": "2024-12-07T08:49:48+00:00" }, { "name": "symfony/polyfill-mbstring", @@ -1654,8 +1655,8 @@ "type": "library", "extra": { "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { @@ -1728,8 +1729,8 @@ "type": "library", "extra": { "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { @@ -1808,8 +1809,8 @@ "type": "library", "extra": { "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { @@ -1866,16 +1867,16 @@ }, { "name": "symfony/service-contracts", - "version": "v3.5.0", + "version": "v3.5.1", "source": { "type": "git", "url": "https://github.com/symfony/service-contracts.git", - "reference": "bd1d9e59a81d8fa4acdcea3f617c581f7475a80f" + "reference": "e53260aabf78fb3d63f8d79d69ece59f80d5eda0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/bd1d9e59a81d8fa4acdcea3f617c581f7475a80f", - "reference": "bd1d9e59a81d8fa4acdcea3f617c581f7475a80f", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/e53260aabf78fb3d63f8d79d69ece59f80d5eda0", + "reference": "e53260aabf78fb3d63f8d79d69ece59f80d5eda0", "shasum": "" }, "require": { @@ -1929,7 +1930,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/service-contracts/tree/v3.5.0" + "source": "https://github.com/symfony/service-contracts/tree/v3.5.1" }, "funding": [ { @@ -1945,7 +1946,7 @@ "type": "tidelift" } ], - "time": "2024-04-18T09:32:20+00:00" + "time": "2024-09-25T14:20:29+00:00" }, { "name": "tbachert/spi", @@ -1974,10 +1975,10 @@ }, "type": "composer-plugin", "extra": { + "class": "Nevay\\SPI\\Composer\\Plugin", "branch-alias": { "dev-main": "0.2.x-dev" }, - "class": "Nevay\\SPI\\Composer\\Plugin", "plugin-optional": true }, "autoload": { @@ -2097,16 +2098,16 @@ }, { "name": "utopia-php/framework", - "version": "0.33.14", + "version": "0.33.15", "source": { "type": "git", "url": "https://github.com/utopia-php/http.git", - "reference": "45a5a2db3602fa054096f378482c7da9936f5850" + "reference": "83b0628900c2c53e8c3efbf069f3e13050295edc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/http/zipball/45a5a2db3602fa054096f378482c7da9936f5850", - "reference": "45a5a2db3602fa054096f378482c7da9936f5850", + "url": "https://api.github.com/repos/utopia-php/http/zipball/83b0628900c2c53e8c3efbf069f3e13050295edc", + "reference": "83b0628900c2c53e8c3efbf069f3e13050295edc", "shasum": "" }, "require": { @@ -2138,9 +2139,9 @@ ], "support": { "issues": "https://github.com/utopia-php/http/issues", - "source": "https://github.com/utopia-php/http/tree/0.33.14" + "source": "https://github.com/utopia-php/http/tree/0.33.15" }, - "time": "2024-11-20T12:39:10+00:00" + "time": "2024-12-10T13:07:04+00:00" }, { "name": "utopia-php/mongo", @@ -2389,16 +2390,16 @@ }, { "name": "laravel/pint", - "version": "v1.18.2", + "version": "v1.18.3", "source": { "type": "git", "url": "https://github.com/laravel/pint.git", - "reference": "f55daaf7eb6c2f49ddf6702fb42e3091c64d8a64" + "reference": "cef51821608239040ab841ad6e1c6ae502ae3026" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/pint/zipball/f55daaf7eb6c2f49ddf6702fb42e3091c64d8a64", - "reference": "f55daaf7eb6c2f49ddf6702fb42e3091c64d8a64", + "url": "https://api.github.com/repos/laravel/pint/zipball/cef51821608239040ab841ad6e1c6ae502ae3026", + "reference": "cef51821608239040ab841ad6e1c6ae502ae3026", "shasum": "" }, "require": { @@ -2409,13 +2410,13 @@ "php": "^8.1.0" }, "require-dev": { - "friendsofphp/php-cs-fixer": "^3.64.0", - "illuminate/view": "^10.48.20", - "larastan/larastan": "^2.9.8", + "friendsofphp/php-cs-fixer": "^3.65.0", + "illuminate/view": "^10.48.24", + "larastan/larastan": "^2.9.11", "laravel-zero/framework": "^10.4.0", "mockery/mockery": "^1.6.12", - "nunomaduro/termwind": "^1.15.1", - "pestphp/pest": "^2.35.1" + "nunomaduro/termwind": "^1.17.0", + "pestphp/pest": "^2.36.0" }, "bin": [ "builds/pint" @@ -2451,7 +2452,7 @@ "issues": "https://github.com/laravel/pint/issues", "source": "https://github.com/laravel/pint" }, - "time": "2024-11-20T09:33:46+00:00" + "time": "2024-11-26T15:34:00+00:00" }, { "name": "myclabs/deep-copy", @@ -2723,16 +2724,16 @@ }, { "name": "phpstan/phpstan", - "version": "1.12.11", + "version": "1.12.13", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "0d1fc20a962a91be578bcfe7cf939e6e1a2ff733" + "reference": "9b469068840cfa031e1deaf2fa1886d00e20680f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/0d1fc20a962a91be578bcfe7cf939e6e1a2ff733", - "reference": "0d1fc20a962a91be578bcfe7cf939e6e1a2ff733", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/9b469068840cfa031e1deaf2fa1886d00e20680f", + "reference": "9b469068840cfa031e1deaf2fa1886d00e20680f", "shasum": "" }, "require": { @@ -2777,7 +2778,7 @@ "type": "github" } ], - "time": "2024-11-17T14:08:01+00:00" + "time": "2024-12-17T17:00:20+00:00" }, { "name": "phpunit/php-code-coverage", @@ -3100,16 +3101,16 @@ }, { "name": "phpunit/phpunit", - "version": "9.6.21", + "version": "9.6.22", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "de6abf3b6f8dd955fac3caad3af7a9504e8c2ffa" + "reference": "f80235cb4d3caa59ae09be3adf1ded27521d1a9c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/de6abf3b6f8dd955fac3caad3af7a9504e8c2ffa", - "reference": "de6abf3b6f8dd955fac3caad3af7a9504e8c2ffa", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/f80235cb4d3caa59ae09be3adf1ded27521d1a9c", + "reference": "f80235cb4d3caa59ae09be3adf1ded27521d1a9c", "shasum": "" }, "require": { @@ -3120,7 +3121,7 @@ "ext-mbstring": "*", "ext-xml": "*", "ext-xmlwriter": "*", - "myclabs/deep-copy": "^1.12.0", + "myclabs/deep-copy": "^1.12.1", "phar-io/manifest": "^2.0.4", "phar-io/version": "^3.2.1", "php": ">=7.3", @@ -3183,7 +3184,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.21" + "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.22" }, "funding": [ { @@ -3199,7 +3200,7 @@ "type": "tidelift" } ], - "time": "2024-09-19T10:50:18+00:00" + "time": "2024-12-05T13:48:26+00:00" }, { "name": "rregeer/phpunit-coverage-check", @@ -4348,7 +4349,7 @@ ], "aliases": [], "minimum-stability": "stable", - "stability-flags": {}, + "stability-flags": [], "prefer-stable": false, "prefer-lowest": false, "platform": { @@ -4356,6 +4357,6 @@ "ext-mbstring": "*", "php": ">=8.3" }, - "platform-dev": {}, + "platform-dev": [], "plugin-api-version": "2.6.0" } diff --git a/src/Database/Database.php b/src/Database/Database.php index 730c37581..7c7c9d0d1 100644 --- a/src/Database/Database.php +++ b/src/Database/Database.php @@ -15,6 +15,7 @@ use Utopia\Database\Exception\Restricted as RestrictedException; use Utopia\Database\Exception\Structure as StructureException; use Utopia\Database\Exception\Timeout as TimeoutException; +use Utopia\Database\Exception\Dependency as DependencyException; use Utopia\Database\Helpers\ID; use Utopia\Database\Helpers\Permission; use Utopia\Database\Helpers\Role; @@ -2063,38 +2064,54 @@ public function deleteAttribute(string $collection, string $id): bool public function renameAttribute(string $collection, string $old, string $new): bool { $collection = $this->silent(fn () => $this->getCollection($collection)); + + /** + * @var array $attributes + */ $attributes = $collection->getAttribute('attributes', []); - $indexes = $collection->getAttribute('indexes', []); - $attribute = \in_array($old, \array_map(fn ($attribute) => $attribute['$id'], $attributes)); + /** + * @var array $indexes + */ + $indexes = $collection->getAttribute('indexes', []); - if ($attribute === false) { - throw new NotFoundException('Attribute not found'); - } + $attribute = new Document(); - $attributeNew = \in_array($new, \array_map(fn ($attribute) => $attribute['$id'], $attributes)); + foreach ($attributes as $value) { + if($value->getId() === $old){ + $attribute = $value; + } - if ($attributeNew !== false) { - throw new DuplicateException('Attribute name already used'); + if($value->getId() === $new){ + throw new DuplicateException('Attribute name already used'); + } } - foreach ($attributes as $key => $value) { - if (isset($value['$id']) && $value['$id'] === $old) { - $attributes[$key]['key'] = $new; - $attributes[$key]['$id'] = $new; - $attributeNew = $attributes[$key]; - break; - } + if ($attribute->isEmpty()) { + throw new NotFoundException('Attribute not found'); } + $attribute->setAttribute('$id', $new); + $attribute->setAttribute('key', $new); + foreach ($indexes as $index) { $indexAttributes = $index->getAttribute('attributes', []); - $indexAttributes = \array_map(fn ($attribute) => ($attribute === $old) ? $new : $attribute, $indexAttributes); + foreach ($indexAttributes as $key => $indexAttribute) { + if($indexAttribute === $old){ + if($attribute->getAttribute('array', false)){ + throw new DependencyException("Can't rename attribute because of functional index dependency must drop index first."); + } + + $indexAttributes[$key] = $new; + } + } $index->setAttribute('attributes', $indexAttributes); } + $renamed = $this->adapter->renameAttribute($collection->getId(), $old, $new); + $collection->setAttribute('attributes', $attributes); $collection->setAttribute('indexes', $indexes); @@ -2102,9 +2119,7 @@ public function renameAttribute(string $collection, string $old, string $new): b $this->silent(fn () => $this->updateDocument(self::METADATA, $collection->getId(), $collection)); } - $renamed = $this->adapter->renameAttribute($collection->getId(), $old, $new); - - $this->trigger(self::EVENT_ATTRIBUTE_UPDATE, $attributeNew); + $this->trigger(self::EVENT_ATTRIBUTE_UPDATE, $attribute); return $renamed; } diff --git a/src/Database/Exception/Dependency.php b/src/Database/Exception/Dependency.php new file mode 100644 index 000000000..c090f4748 --- /dev/null +++ b/src/Database/Exception/Dependency.php @@ -0,0 +1,7 @@ +assertEquals(true, $database->createAttribute( $collection, 'names', @@ -2766,6 +2767,15 @@ public function testArrayAttribute(): void array: true )); + $this->assertEquals(true, $database->createAttribute( + $collection, + 'cards', + Database::VAR_STRING, + size: 5000, + required: false, + array: true + )); + $this->assertEquals(true, $database->createAttribute( $collection, 'numbers', @@ -2889,6 +2899,17 @@ public function testArrayAttribute(): void $this->assertEquals('Antony', $document->getAttribute('names')[1]); $this->assertEquals(100, $document->getAttribute('numbers')[1]); + /** + * functional index dependency cannot be dropped or rename + */ + $database->createIndex($collection, 'idx_cards', Database::INDEX_KEY, ['cards']); + try { + $database->renameAttribute($collection, 'cards', 'cards_new'); + $this->fail('Failed to throw exception'); + } catch (Throwable $e) { + $this->assertEquals("Can't rename attribute because of functional index dependency must drop index first.", $e->getMessage()); + } + try { $database->createIndex($collection, 'indx', Database::INDEX_FULLTEXT, ['names']); $this->fail('Failed to throw exception'); From 8282694b5a55def06a52c513006aa77ab2ab2afd Mon Sep 17 00:00:00 2001 From: fogelito Date: Mon, 23 Dec 2024 10:52:20 +0200 Subject: [PATCH 2/9] deleteAttribute functional index dependency --- tests/e2e/Adapter/Base.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/e2e/Adapter/Base.php b/tests/e2e/Adapter/Base.php index 8b38ee43d..5699102d0 100644 --- a/tests/e2e/Adapter/Base.php +++ b/tests/e2e/Adapter/Base.php @@ -2903,6 +2903,9 @@ public function testArrayAttribute(): void * functional index dependency cannot be dropped or rename */ $database->createIndex($collection, 'idx_cards', Database::INDEX_KEY, ['cards']); + + $database->deleteAttribute($collection, 'cards'); + try { $database->renameAttribute($collection, 'cards', 'cards_new'); $this->fail('Failed to throw exception'); From 4a9cd839907068a28f884c501c3add9faa35e410 Mon Sep 17 00:00:00 2001 From: fogelito Date: Mon, 23 Dec 2024 12:41:44 +0200 Subject: [PATCH 3/9] getSupportForCastIndexArray --- src/Database/Adapter.php | 7 +++++++ src/Database/Adapter/MariaDB.php | 24 +++++++++++------------- src/Database/Adapter/Mongo.php | 5 +++++ src/Database/Adapter/MySQL.php | 11 +++++++---- src/Database/Adapter/SQL.php | 5 +++++ src/Database/Database.php | 6 +++--- tests/e2e/Adapter/Base.php | 26 ++++++++++++++++++++------ 7 files changed, 58 insertions(+), 26 deletions(-) diff --git a/src/Database/Adapter.php b/src/Database/Adapter.php index 6e7cc9f9e..4ad975706 100644 --- a/src/Database/Adapter.php +++ b/src/Database/Adapter.php @@ -868,6 +868,13 @@ abstract public function getSupportForAttributeResizing(): bool; */ abstract public function getSupportForGetConnectionId(): bool; + /** + * Is cast index as array supported? + * + * @return bool + */ + abstract public function getSupportForCastIndexArray(): bool; + /** * Get current attribute count from collection document * diff --git a/src/Database/Adapter/MariaDB.php b/src/Database/Adapter/MariaDB.php index b5d2956f1..104d2aab8 100644 --- a/src/Database/Adapter/MariaDB.php +++ b/src/Database/Adapter/MariaDB.php @@ -433,12 +433,13 @@ public function deleteAttribute(string $collection, string $id, bool $array = fa return $this->getPDO() ->prepare($sql) ->execute(); + } catch (PDOException $e) { if ($e->getCode() === "42000" && $e->errorInfo[1] === 1091) { return true; } - throw $e; + throw $this->processException($e); } } @@ -462,9 +463,14 @@ public function renameAttribute(string $collection, string $old, string $new): b $sql = $this->trigger(Database::EVENT_ATTRIBUTE_UPDATE, $sql); - return $this->getPDO() - ->prepare($sql) - ->execute(); + try { + return $this->getPDO() + ->prepare($sql) + ->execute(); + + } catch (PDOException $e) { + throw $this->processException($e); + } } /** @@ -766,7 +772,7 @@ public function createIndex(string $collection, string $id, string $type, array $attributes[$i] = "`{$attr}`{$length} {$order}"; - if (!empty($collectionAttribute['array']) && $this->castIndexArray()) { + if (!empty($collectionAttribute['array']) && $this->getSupportForCastIndexArray()) { $attributes[$i] = '(CAST(' . $attr . ' AS char(' . Database::ARRAY_INDEX_LENGTH . ') ARRAY))'; } } @@ -798,14 +804,6 @@ public function createIndex(string $collection, string $id, string $type, array } } - /** - * @return bool - */ - public function castIndexArray(): bool - { - return false; - } - /** * Delete Index * diff --git a/src/Database/Adapter/Mongo.php b/src/Database/Adapter/Mongo.php index 2e35a74e0..bae6ca9db 100644 --- a/src/Database/Adapter/Mongo.php +++ b/src/Database/Adapter/Mongo.php @@ -1797,6 +1797,11 @@ public function getSupportForSchemaAttributes(): bool return false; } + public function getSupportForCastIndexArray(): bool + { + return false; + } + /** * Get current attribute count from collection document * diff --git a/src/Database/Adapter/MySQL.php b/src/Database/Adapter/MySQL.php index 7564d5a51..fb1694cd0 100644 --- a/src/Database/Adapter/MySQL.php +++ b/src/Database/Adapter/MySQL.php @@ -5,6 +5,7 @@ use PDOException; use Utopia\Database\Database; use Utopia\Database\Exception as DatabaseException; +use Utopia\Database\Exception\Dependency as DependencyException; use Utopia\Database\Exception\Timeout as TimeoutException; class MySQL extends MariaDB @@ -74,10 +75,7 @@ public function getSizeOfCollectionOnDisk(string $collection): int return $size; } - /** - * @return bool - */ - public function castIndexArray(): bool + public function getSupportForCastIndexArray(): bool { return true; } @@ -89,6 +87,11 @@ protected function processException(PDOException $e): \Exception return new TimeoutException($e->getMessage(), $e->getCode(), $e); } + // Functional index dependency + if ($e->getCode() === 'HY000' && isset($e->errorInfo[1]) && $e->errorInfo[1] === 3837) { + return new DependencyException($e->errorInfo[2], $e->getCode(), $e); + } + return parent::processException($e); } } diff --git a/src/Database/Adapter/SQL.php b/src/Database/Adapter/SQL.php index ee69d5919..725a91a50 100644 --- a/src/Database/Adapter/SQL.php +++ b/src/Database/Adapter/SQL.php @@ -846,6 +846,11 @@ public function getSupportForQueryContains(): bool */ abstract public function getSupportForJSONOverlaps(): bool; + public function getSupportForCastIndexArray(): bool + { + return false; + } + public function getSupportForRelationships(): bool { return true; diff --git a/src/Database/Database.php b/src/Database/Database.php index 7c7c9d0d1..8ec8ebe9e 100644 --- a/src/Database/Database.php +++ b/src/Database/Database.php @@ -2099,9 +2099,9 @@ public function renameAttribute(string $collection, string $old, string $new): b foreach ($indexAttributes as $key => $indexAttribute) { if($indexAttribute === $old){ - if($attribute->getAttribute('array', false)){ - throw new DependencyException("Can't rename attribute because of functional index dependency must drop index first."); - } +// if($attribute->getAttribute('array', false)){ +// throw new DependencyException("Can't rename attribute because of functional index dependency must drop index first."); +// } $indexAttributes[$key] = $new; } diff --git a/tests/e2e/Adapter/Base.php b/tests/e2e/Adapter/Base.php index 5699102d0..b1c1a66c5 100644 --- a/tests/e2e/Adapter/Base.php +++ b/tests/e2e/Adapter/Base.php @@ -20,6 +20,7 @@ use Utopia\Database\Exception\Structure as StructureException; use Utopia\Database\Exception\Timeout as TimeoutException; use Utopia\Database\Exception\Truncate as TruncateException; +use Utopia\Database\Exception\Dependency as DependencyException; use Utopia\Database\Helpers\ID; use Utopia\Database\Helpers\Permission; use Utopia\Database\Helpers\Role; @@ -2904,13 +2905,26 @@ public function testArrayAttribute(): void */ $database->createIndex($collection, 'idx_cards', Database::INDEX_KEY, ['cards']); - $database->deleteAttribute($collection, 'cards'); + if ($this->getDatabase()->getAdapter()->getSupportForCastIndexArray()) { + try { + $database->deleteAttribute($collection, 'cards'); + $this->fail('Failed to throw exception'); + } catch (Throwable $e) { + $this->assertInstanceOf(DependencyException::class, $e); + $this->assertEquals("Column 'cards' has a functional index dependency and cannot be dropped or renamed.", $e->getMessage()); + } - try { - $database->renameAttribute($collection, 'cards', 'cards_new'); - $this->fail('Failed to throw exception'); - } catch (Throwable $e) { - $this->assertEquals("Can't rename attribute because of functional index dependency must drop index first.", $e->getMessage()); + try { + $database->renameAttribute($collection, 'cards', 'cards_new'); + $this->fail('Failed to throw exception'); + } catch (Throwable $e) { + $this->assertInstanceOf(DependencyException::class, $e); + $this->assertEquals("Column 'cards' has a functional index dependency and cannot be dropped or renamed.", $e->getMessage()); + } + } + else { + $this->assertTrue($database->renameAttribute($collection, 'cards', 'cards_new')); + $this->assertTrue($database->deleteAttribute($collection, 'cards')); } try { From 93355a9039a73c19be45355e480682b1805d6bba Mon Sep 17 00:00:00 2001 From: fogelito Date: Mon, 23 Dec 2024 13:03:42 +0200 Subject: [PATCH 4/9] formatting --- src/Database/Database.php | 14 +++++++------- src/Database/Exception/Dependency.php | 4 +++- tests/e2e/Adapter/Base.php | 7 +++---- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/src/Database/Database.php b/src/Database/Database.php index 8ec8ebe9e..6d089c4fc 100644 --- a/src/Database/Database.php +++ b/src/Database/Database.php @@ -7,6 +7,7 @@ use Utopia\Database\Exception as DatabaseException; use Utopia\Database\Exception\Authorization as AuthorizationException; use Utopia\Database\Exception\Conflict as ConflictException; +use Utopia\Database\Exception\Dependency as DependencyException; use Utopia\Database\Exception\Duplicate as DuplicateException; use Utopia\Database\Exception\Limit as LimitException; use Utopia\Database\Exception\NotFound as NotFoundException; @@ -15,7 +16,6 @@ use Utopia\Database\Exception\Restricted as RestrictedException; use Utopia\Database\Exception\Structure as StructureException; use Utopia\Database\Exception\Timeout as TimeoutException; -use Utopia\Database\Exception\Dependency as DependencyException; use Utopia\Database\Helpers\ID; use Utopia\Database\Helpers\Permission; use Utopia\Database\Helpers\Role; @@ -2078,11 +2078,11 @@ public function renameAttribute(string $collection, string $old, string $new): b $attribute = new Document(); foreach ($attributes as $value) { - if($value->getId() === $old){ + if ($value->getId() === $old) { $attribute = $value; } - if($value->getId() === $new){ + if ($value->getId() === $new) { throw new DuplicateException('Attribute name already used'); } } @@ -2098,10 +2098,10 @@ public function renameAttribute(string $collection, string $old, string $new): b $indexAttributes = $index->getAttribute('attributes', []); foreach ($indexAttributes as $key => $indexAttribute) { - if($indexAttribute === $old){ -// if($attribute->getAttribute('array', false)){ -// throw new DependencyException("Can't rename attribute because of functional index dependency must drop index first."); -// } + if ($indexAttribute === $old) { + // if($attribute->getAttribute('array', false)){ + // throw new DependencyException("Can't rename attribute because of functional index dependency must drop index first."); + // } $indexAttributes[$key] = $new; } diff --git a/src/Database/Exception/Dependency.php b/src/Database/Exception/Dependency.php index c090f4748..5c58ef63c 100644 --- a/src/Database/Exception/Dependency.php +++ b/src/Database/Exception/Dependency.php @@ -4,4 +4,6 @@ use Utopia\Database\Exception; -class Dependency extends Exception {} +class Dependency extends Exception +{ +} diff --git a/tests/e2e/Adapter/Base.php b/tests/e2e/Adapter/Base.php index b1c1a66c5..38ec0c41c 100644 --- a/tests/e2e/Adapter/Base.php +++ b/tests/e2e/Adapter/Base.php @@ -12,6 +12,7 @@ use Utopia\Database\Exception as DatabaseException; use Utopia\Database\Exception\Authorization as AuthorizationException; use Utopia\Database\Exception\Conflict as ConflictException; +use Utopia\Database\Exception\Dependency as DependencyException; use Utopia\Database\Exception\Duplicate as DuplicateException; use Utopia\Database\Exception\Limit as LimitException; use Utopia\Database\Exception\Query as QueryException; @@ -20,7 +21,6 @@ use Utopia\Database\Exception\Structure as StructureException; use Utopia\Database\Exception\Timeout as TimeoutException; use Utopia\Database\Exception\Truncate as TruncateException; -use Utopia\Database\Exception\Dependency as DependencyException; use Utopia\Database\Helpers\ID; use Utopia\Database\Helpers\Permission; use Utopia\Database\Helpers\Role; @@ -2921,10 +2921,9 @@ public function testArrayAttribute(): void $this->assertInstanceOf(DependencyException::class, $e); $this->assertEquals("Column 'cards' has a functional index dependency and cannot be dropped or renamed.", $e->getMessage()); } - } - else { + } else { $this->assertTrue($database->renameAttribute($collection, 'cards', 'cards_new')); - $this->assertTrue($database->deleteAttribute($collection, 'cards')); + $this->assertTrue($database->deleteAttribute($collection, 'cards_new')); } try { From 894f1ec966a5a6ef528f40cd70fc376c562eb0c6 Mon Sep 17 00:00:00 2001 From: fogelito Date: Mon, 23 Dec 2024 14:57:38 +0200 Subject: [PATCH 5/9] Add length --- tests/e2e/Adapter/Base.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/e2e/Adapter/Base.php b/tests/e2e/Adapter/Base.php index 38ec0c41c..d16f9630d 100644 --- a/tests/e2e/Adapter/Base.php +++ b/tests/e2e/Adapter/Base.php @@ -2903,7 +2903,7 @@ public function testArrayAttribute(): void /** * functional index dependency cannot be dropped or rename */ - $database->createIndex($collection, 'idx_cards', Database::INDEX_KEY, ['cards']); + $database->createIndex($collection, 'idx_cards', Database::INDEX_KEY, ['cards'], [100]); if ($this->getDatabase()->getAdapter()->getSupportForCastIndexArray()) { try { From 57793e0089e6d6c2141ef33b33f04aea8085173e Mon Sep 17 00:00:00 2001 From: fogelito Date: Mon, 23 Dec 2024 18:33:58 +0200 Subject: [PATCH 6/9] Remove comment --- src/Database/Adapter/MariaDB.php | 1 - src/Database/Database.php | 18 ++++-------------- 2 files changed, 4 insertions(+), 15 deletions(-) diff --git a/src/Database/Adapter/MariaDB.php b/src/Database/Adapter/MariaDB.php index 104d2aab8..89f02c0b1 100644 --- a/src/Database/Adapter/MariaDB.php +++ b/src/Database/Adapter/MariaDB.php @@ -467,7 +467,6 @@ public function renameAttribute(string $collection, string $old, string $new): b return $this->getPDO() ->prepare($sql) ->execute(); - } catch (PDOException $e) { throw $this->processException($e); } diff --git a/src/Database/Database.php b/src/Database/Database.php index 6d089c4fc..f29d47953 100644 --- a/src/Database/Database.php +++ b/src/Database/Database.php @@ -7,7 +7,6 @@ use Utopia\Database\Exception as DatabaseException; use Utopia\Database\Exception\Authorization as AuthorizationException; use Utopia\Database\Exception\Conflict as ConflictException; -use Utopia\Database\Exception\Dependency as DependencyException; use Utopia\Database\Exception\Duplicate as DuplicateException; use Utopia\Database\Exception\Limit as LimitException; use Utopia\Database\Exception\NotFound as NotFoundException; @@ -2049,17 +2048,16 @@ public function deleteAttribute(string $collection, string $id): bool } /** - * Rename Attribute - * * @param string $collection - * @param string $old Current attribute ID + * @param string $old * @param string $new * @return bool * @throws AuthorizationException * @throws ConflictException - * @throws DatabaseException * @throws DuplicateException + * @throws NotFoundException * @throws StructureException + * @throws DatabaseException */ public function renameAttribute(string $collection, string $old, string $new): bool { @@ -2097,15 +2095,7 @@ public function renameAttribute(string $collection, string $old, string $new): b foreach ($indexes as $index) { $indexAttributes = $index->getAttribute('attributes', []); - foreach ($indexAttributes as $key => $indexAttribute) { - if ($indexAttribute === $old) { - // if($attribute->getAttribute('array', false)){ - // throw new DependencyException("Can't rename attribute because of functional index dependency must drop index first."); - // } - - $indexAttributes[$key] = $new; - } - } + $indexAttributes = \array_map(fn ($attr) => ($attr === $old) ? $new : $attr, $indexAttributes); $index->setAttribute('attributes', $indexAttributes); } From 10809152c77870a79e76f609a1a53400b5ac015e Mon Sep 17 00:00:00 2001 From: fogelito Date: Mon, 23 Dec 2024 18:36:02 +0200 Subject: [PATCH 7/9] Extra line --- src/Database/Adapter/MariaDB.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Database/Adapter/MariaDB.php b/src/Database/Adapter/MariaDB.php index 89f02c0b1..868a0918d 100644 --- a/src/Database/Adapter/MariaDB.php +++ b/src/Database/Adapter/MariaDB.php @@ -433,7 +433,6 @@ public function deleteAttribute(string $collection, string $id, bool $array = fa return $this->getPDO() ->prepare($sql) ->execute(); - } catch (PDOException $e) { if ($e->getCode() === "42000" && $e->errorInfo[1] === 1091) { return true; From 9ffece83e9fd16f3dd3d5234d01185f81939ac41 Mon Sep 17 00:00:00 2001 From: fogelito Date: Mon, 23 Dec 2024 18:37:57 +0200 Subject: [PATCH 8/9] hints --- src/Database/Database.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Database/Database.php b/src/Database/Database.php index f29d47953..c7635cd33 100644 --- a/src/Database/Database.php +++ b/src/Database/Database.php @@ -2048,16 +2048,17 @@ public function deleteAttribute(string $collection, string $id): bool } /** + * Rename Attribute + * * @param string $collection - * @param string $old + * @param string $old Current attribute ID * @param string $new * @return bool * @throws AuthorizationException * @throws ConflictException + * @throws DatabaseException * @throws DuplicateException - * @throws NotFoundException * @throws StructureException - * @throws DatabaseException */ public function renameAttribute(string $collection, string $old, string $new): bool { From 4cb6368dd2cb1591a00ed7eb067f8521ff04f185 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Wed, 8 Jan 2025 14:26:57 +1300 Subject: [PATCH 9/9] Update tests/e2e/Adapter/Base.php --- tests/e2e/Adapter/Base.php | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/e2e/Adapter/Base.php b/tests/e2e/Adapter/Base.php index d16f9630d..7aead1761 100644 --- a/tests/e2e/Adapter/Base.php +++ b/tests/e2e/Adapter/Base.php @@ -2758,7 +2758,6 @@ public function testArrayAttribute(): void array: true )); - $this->assertEquals(true, $database->createAttribute( $collection, 'names',