From 79e7eba8644250b3a5a03cdfb8daed34c88c14ab Mon Sep 17 00:00:00 2001 From: ArnabChatterjee20k Date: Tue, 9 Sep 2025 22:50:01 +0530 Subject: [PATCH 1/3] updated spatial update attribute --- src/Database/Adapter.php | 3 +- src/Database/Adapter/MariaDB.php | 6 +-- src/Database/Adapter/Pool.php | 2 +- src/Database/Adapter/Postgres.php | 5 ++- src/Database/Adapter/SQLite.php | 3 +- src/Database/Database.php | 6 ++- tests/e2e/Adapter/Scopes/SpatialTests.php | 47 +++++++++++++++++++++++ 7 files changed, 63 insertions(+), 9 deletions(-) diff --git a/src/Database/Adapter.php b/src/Database/Adapter.php index 5e416d036..db58e4fa4 100644 --- a/src/Database/Adapter.php +++ b/src/Database/Adapter.php @@ -578,10 +578,11 @@ abstract public function createAttributes(string $collection, array $attributes) * @param bool $signed * @param bool $array * @param string|null $newKey + * @param bool $required * * @return bool */ - abstract public function updateAttribute(string $collection, string $id, string $type, int $size, bool $signed = true, bool $array = false, ?string $newKey = null): bool; + abstract public function updateAttribute(string $collection, string $id, string $type, int $size, bool $signed = true, bool $array = false, ?string $newKey = null, bool $required = false): bool; /** * Delete Attribute diff --git a/src/Database/Adapter/MariaDB.php b/src/Database/Adapter/MariaDB.php index 4b4580afd..752a60eca 100644 --- a/src/Database/Adapter/MariaDB.php +++ b/src/Database/Adapter/MariaDB.php @@ -409,16 +409,16 @@ public function getSchemaAttributes(string $collection): array * @param bool $signed * @param bool $array * @param string|null $newKey + * @param bool $required * @return bool * @throws DatabaseException */ - public function updateAttribute(string $collection, string $id, string $type, int $size, bool $signed = true, bool $array = false, ?string $newKey = null): bool + public function updateAttribute(string $collection, string $id, string $type, int $size, bool $signed = true, bool $array = false, ?string $newKey = null, bool $required = false): bool { $name = $this->filter($collection); $id = $this->filter($id); $newKey = empty($newKey) ? null : $this->filter($newKey); - $type = $this->getSQLType($type, $size, $signed, $array, false); - + $type = $this->getSQLType($type, $size, $signed, $array, $required); if (!empty($newKey)) { $sql = "ALTER TABLE {$this->getSQLTable($name)} CHANGE COLUMN `{$id}` `{$newKey}` {$type};"; } else { diff --git a/src/Database/Adapter/Pool.php b/src/Database/Adapter/Pool.php index 636e37555..27dfd6bb8 100644 --- a/src/Database/Adapter/Pool.php +++ b/src/Database/Adapter/Pool.php @@ -175,7 +175,7 @@ public function createAttributes(string $collection, array $attributes): bool return $this->delegate(__FUNCTION__, \func_get_args()); } - public function updateAttribute(string $collection, string $id, string $type, int $size, bool $signed = true, bool $array = false, ?string $newKey = null): bool + public function updateAttribute(string $collection, string $id, string $type, int $size, bool $signed = true, bool $array = false, ?string $newKey = null, bool $required = false): bool { return $this->delegate(__FUNCTION__, \func_get_args()); } diff --git a/src/Database/Adapter/Postgres.php b/src/Database/Adapter/Postgres.php index b183c8c4e..1276cea57 100644 --- a/src/Database/Adapter/Postgres.php +++ b/src/Database/Adapter/Postgres.php @@ -535,16 +535,17 @@ public function renameAttribute(string $collection, string $old, string $new): b * @param bool $signed * @param bool $array * @param string|null $newKey + * @param bool $required * @return bool * @throws Exception * @throws PDOException */ - public function updateAttribute(string $collection, string $id, string $type, int $size, bool $signed = true, bool $array = false, ?string $newKey = null): bool + public function updateAttribute(string $collection, string $id, string $type, int $size, bool $signed = true, bool $array = false, ?string $newKey = null, bool $required = false): bool { $name = $this->filter($collection); $id = $this->filter($id); $newKey = empty($newKey) ? null : $this->filter($newKey); - $type = $this->getSQLType($type, $size, $signed, $array, false); + $type = $this->getSQLType($type, $size, $signed, $array, $required); if ($type == 'TIMESTAMP(3)') { $type = "TIMESTAMP(3) without time zone USING TO_TIMESTAMP(\"$id\", 'YYYY-MM-DD HH24:MI:SS.MS')"; diff --git a/src/Database/Adapter/SQLite.php b/src/Database/Adapter/SQLite.php index 4f9a2afdd..54ffb47dd 100644 --- a/src/Database/Adapter/SQLite.php +++ b/src/Database/Adapter/SQLite.php @@ -327,11 +327,12 @@ public function analyzeCollection(string $collection): bool * @param bool $signed * @param bool $array * @param string|null $newKey + * @param bool $required * @return bool * @throws Exception * @throws PDOException */ - public function updateAttribute(string $collection, string $id, string $type, int $size, bool $signed = true, bool $array = false, ?string $newKey = null): bool + public function updateAttribute(string $collection, string $id, string $type, int $size, bool $signed = true, bool $array = false, ?string $newKey = null, bool $required = false): bool { if (!empty($newKey) && $newKey !== $id) { return $this->renameAttribute($collection, $id, $newKey); diff --git a/src/Database/Database.php b/src/Database/Database.php index 9b7a60ff0..3f74ef10b 100644 --- a/src/Database/Database.php +++ b/src/Database/Database.php @@ -2161,6 +2161,10 @@ public function updateAttribute(string $collection, string $id, ?string $type = $default = null; } + if ($required === true && in_array($type, Database::SPATIAL_TYPES)) { + $altering = true; + } + switch ($type) { case self::VAR_STRING: if (empty($size)) { @@ -2322,7 +2326,7 @@ public function updateAttribute(string $collection, string $id, ?string $type = } } - $updated = $this->adapter->updateAttribute($collection, $id, $type, $size, $signed, $array, $newKey); + $updated = $this->adapter->updateAttribute($collection, $id, $type, $size, $signed, $array, $newKey, $required); if (!$updated) { throw new DatabaseException('Failed to update attribute'); diff --git a/tests/e2e/Adapter/Scopes/SpatialTests.php b/tests/e2e/Adapter/Scopes/SpatialTests.php index 6af26eedc..74f4055d4 100644 --- a/tests/e2e/Adapter/Scopes/SpatialTests.php +++ b/tests/e2e/Adapter/Scopes/SpatialTests.php @@ -840,6 +840,53 @@ public function testSpatialIndex(): void } finally { $database->deleteCollection($collNullIndex); } + + $collUpdateNull = 'spatial_idx_update_null_' . uniqid(); + try { + $database->createCollection($collUpdateNull); + + $database->createAttribute($collUpdateNull, 'loc', Database::VAR_POINT, 0, false); + if (!$nullSupported) { + try { + $database->createIndex($collUpdateNull, 'idx_loc', Database::INDEX_SPATIAL, ['loc']); + $this->fail('Expected exception when creating spatial index on NULL-able attribute'); + } catch (\Throwable $e) { + $this->assertInstanceOf(Exception::class, $e); + } + } else { + $this->assertTrue($database->createIndex($collUpdateNull, 'idx_loc', Database::INDEX_SPATIAL, ['loc'])); + } + + $database->updateAttribute($collUpdateNull, 'loc', required: true); + + $this->assertTrue($database->createIndex($collUpdateNull, 'idx_loc_req', Database::INDEX_SPATIAL, ['loc'])); + } finally { + $database->deleteCollection($collUpdateNull); + } + + + $collUpdateNull = 'spatial_idx_index_null_' . uniqid(); + try { + $database->createCollection($collUpdateNull); + + $database->createAttribute($collUpdateNull, 'loc', Database::VAR_POINT, 0, false); + if (!$nullSupported) { + try { + $database->createIndex($collUpdateNull, 'idx_loc', Database::INDEX_SPATIAL, ['loc']); + $this->fail('Expected exception when creating spatial index on NULL-able attribute'); + } catch (\Throwable $e) { + $this->assertInstanceOf(Exception::class, $e); + } + } else { + $this->assertTrue($database->createIndex($collUpdateNull, 'idx_loc', Database::INDEX_SPATIAL, ['loc'])); + } + + $database->updateAttribute($collUpdateNull, 'loc', required: true); + + $this->assertTrue($database->createIndex($collUpdateNull, 'idx_loc_req', Database::INDEX_SPATIAL, ['loc'])); + } finally { + $database->deleteCollection($collUpdateNull); + } } public function testComplexGeometricShapes(): void From 78ca0c70a546693690db6707ddd2fbbc891087f6 Mon Sep 17 00:00:00 2001 From: ArnabChatterjee20k Date: Tue, 9 Sep 2025 23:15:05 +0530 Subject: [PATCH 2/3] replaced database exception with query exception --- src/Database/Adapter/MariaDB.php | 3 ++- tests/e2e/Adapter/Scopes/SpatialTests.php | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Database/Adapter/MariaDB.php b/src/Database/Adapter/MariaDB.php index 752a60eca..cb4b27fed 100644 --- a/src/Database/Adapter/MariaDB.php +++ b/src/Database/Adapter/MariaDB.php @@ -9,6 +9,7 @@ use Utopia\Database\Exception as DatabaseException; use Utopia\Database\Exception\Duplicate as DuplicateException; use Utopia\Database\Exception\NotFound as NotFoundException; +use Utopia\Database\Exception\Query as QueryException; use Utopia\Database\Exception\Timeout as TimeoutException; use Utopia\Database\Exception\Truncate as TruncateException; use Utopia\Database\Helpers\ID; @@ -1393,7 +1394,7 @@ protected function handleDistanceSpatialQueries(Query $query, array &$binds, str $wktType = $this->getSpatialTypeFromWKT($wkt); $attrType = strtolower($type); if ($wktType != Database::VAR_POINT || $attrType != Database::VAR_POINT) { - throw new DatabaseException('Distance in meters is not supported between '.$attrType . ' and '. $wktType); + throw new QueryException('Distance in meters is not supported between '.$attrType . ' and '. $wktType); } return "ST_DISTANCE_SPHERE({$alias}.{$attribute}, ST_GeomFromText(:{$placeholder}_0), 6371000) {$operator} :{$placeholder}_1"; } diff --git a/tests/e2e/Adapter/Scopes/SpatialTests.php b/tests/e2e/Adapter/Scopes/SpatialTests.php index 74f4055d4..8053a6d18 100644 --- a/tests/e2e/Adapter/Scopes/SpatialTests.php +++ b/tests/e2e/Adapter/Scopes/SpatialTests.php @@ -5,6 +5,7 @@ use Utopia\Database\Database; use Utopia\Database\Document; use Utopia\Database\Exception; +use Utopia\Database\Exception\Query as QueryException; use Utopia\Database\Exception\Structure as StructureException; use Utopia\Database\Helpers\ID; use Utopia\Database\Helpers\Permission; @@ -2382,11 +2383,10 @@ public function testSpatialDistanceInMeterError(): void ]); $this->fail('Expected Exception not thrown for ' . implode(' vs ', $case['expected'])); } catch (\Exception $e) { - $this->assertInstanceOf(\Exception::class, $e); + $this->assertInstanceOf(QueryException::class, $e); // Validate exception message contains correct type names $msg = strtolower($e->getMessage()); - var_dump($msg); $this->assertStringContainsString($case['expected'][0], $msg, 'Attr type missing in exception'); $this->assertStringContainsString($case['expected'][1], $msg, 'Geom type missing in exception'); } From 2df73eadfcfa39ad7c9af86152291cef48e1e1b1 Mon Sep 17 00:00:00 2001 From: ArnabChatterjee20k Date: Tue, 9 Sep 2025 23:22:24 +0530 Subject: [PATCH 3/3] updated tests --- tests/e2e/Adapter/Scopes/SpatialTests.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/e2e/Adapter/Scopes/SpatialTests.php b/tests/e2e/Adapter/Scopes/SpatialTests.php index 8053a6d18..281edf070 100644 --- a/tests/e2e/Adapter/Scopes/SpatialTests.php +++ b/tests/e2e/Adapter/Scopes/SpatialTests.php @@ -842,14 +842,14 @@ public function testSpatialIndex(): void $database->deleteCollection($collNullIndex); } - $collUpdateNull = 'spatial_idx_update_null_' . uniqid(); + $collUpdateNull = 'spatial_idx_req'; try { $database->createCollection($collUpdateNull); $database->createAttribute($collUpdateNull, 'loc', Database::VAR_POINT, 0, false); if (!$nullSupported) { try { - $database->createIndex($collUpdateNull, 'idx_loc', Database::INDEX_SPATIAL, ['loc']); + $database->createIndex($collUpdateNull, 'idx_loc_required', Database::INDEX_SPATIAL, ['loc']); $this->fail('Expected exception when creating spatial index on NULL-able attribute'); } catch (\Throwable $e) { $this->assertInstanceOf(Exception::class, $e); @@ -866,7 +866,7 @@ public function testSpatialIndex(): void } - $collUpdateNull = 'spatial_idx_index_null_' . uniqid(); + $collUpdateNull = 'spatial_idx_index_null_required_true'; try { $database->createCollection($collUpdateNull); @@ -884,7 +884,7 @@ public function testSpatialIndex(): void $database->updateAttribute($collUpdateNull, 'loc', required: true); - $this->assertTrue($database->createIndex($collUpdateNull, 'idx_loc_req', Database::INDEX_SPATIAL, ['loc'])); + $this->assertTrue($database->createIndex($collUpdateNull, 'new index', Database::INDEX_SPATIAL, ['loc'])); } finally { $database->deleteCollection($collUpdateNull); }