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
7 changes: 7 additions & 0 deletions src/Database/Adapter.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
*
Expand Down
22 changes: 9 additions & 13 deletions src/Database/Adapter/MariaDB.php
Original file line number Diff line number Diff line change
Expand Up @@ -438,7 +438,7 @@ public function deleteAttribute(string $collection, string $id, bool $array = fa
return true;
}

throw $e;
throw $this->processException($e);
}
}

Expand All @@ -462,9 +462,13 @@ 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);
}
}

/**
Expand Down Expand Up @@ -766,7 +770,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))';
}
}
Expand Down Expand Up @@ -798,14 +802,6 @@ public function createIndex(string $collection, string $id, string $type, array
}
}

/**
* @return bool
*/
public function castIndexArray(): bool
{
return false;
}

/**
* Delete Index
*
Expand Down
5 changes: 5 additions & 0 deletions src/Database/Adapter/Mongo.php
Original file line number Diff line number Diff line change
Expand Up @@ -1797,6 +1797,11 @@ public function getSupportForSchemaAttributes(): bool
return false;
}

public function getSupportForCastIndexArray(): bool
{
return false;
}

/**
* Get current attribute count from collection document
*
Expand Down
11 changes: 7 additions & 4 deletions src/Database/Adapter/MySQL.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -74,10 +75,7 @@ public function getSizeOfCollectionOnDisk(string $collection): int
return $size;
}

/**
* @return bool
*/
public function castIndexArray(): bool
public function getSupportForCastIndexArray(): bool
{
return true;
}
Expand All @@ -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);
}
}
5 changes: 5 additions & 0 deletions src/Database/Adapter/SQL.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
40 changes: 23 additions & 17 deletions src/Database/Database.php
Original file line number Diff line number Diff line change
Expand Up @@ -2058,34 +2058,40 @@ 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<Document> $attributes
*/
$attributes = $collection->getAttribute('attributes', []);
$indexes = $collection->getAttribute('indexes', []);

$attribute = \in_array($old, \array_map(fn ($attribute) => $attribute['$id'], $attributes));
/**
* @var array<Document> $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);
$indexAttributes = \array_map(fn ($attr) => ($attr === $old) ? $new : $attr, $indexAttributes);

$index->setAttribute('attributes', $indexAttributes);
}
Expand All @@ -2099,7 +2105,7 @@ public function renameAttribute(string $collection, string $old, string $new): b
$this->silent(fn () => $this->updateDocument(self::METADATA, $collection->getId(), $collection));
}

$this->trigger(self::EVENT_ATTRIBUTE_UPDATE, $attributeNew);
$this->trigger(self::EVENT_ATTRIBUTE_UPDATE, $attribute);

return $renamed;
}
Expand Down
9 changes: 9 additions & 0 deletions src/Database/Exception/Dependency.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php

namespace Utopia\Database\Exception;

use Utopia\Database\Exception;

class Dependency extends Exception
{
}
36 changes: 36 additions & 0 deletions tests/e2e/Adapter/Base.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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',
Expand Down Expand Up @@ -2889,6 +2899,32 @@ 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'], [100]);

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->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_new'));
}

try {
$database->createIndex($collection, 'indx', Database::INDEX_FULLTEXT, ['names']);
$this->fail('Failed to throw exception');
Expand Down
Loading