Skip to content

Commit 4909c84

Browse files
authored
Merge pull request #271 from utopia-php/index-length-validation
Index validation
2 parents 8e0d6f1 + e10a336 commit 4909c84

11 files changed

Lines changed: 589 additions & 66 deletions

File tree

composer.lock

Lines changed: 18 additions & 18 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Database/Adapter.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -636,4 +636,9 @@ public function escapeWildcards(string $value): string
636636
* @throws Exception
637637
*/
638638
abstract public function increaseDocumentAttribute(string $collection, string $id, string $attribute, int|float $value, int|float|null $min = null, int|float|null $max = null): bool;
639+
640+
/**
641+
* @return int
642+
*/
643+
abstract public function getMaxIndexLength(): int;
639644
}

src/Database/Adapter/MariaDB.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1278,7 +1278,7 @@ protected function getSQLType(string $type, int $size, bool $signed = true): str
12781278
return 'MEDIUMTEXT';
12791279
}
12801280

1281-
if ($size > 16383) {
1281+
if ($size > $this->getMaxVarcharLength()) {
12821282
return 'TEXT';
12831283
}
12841284

src/Database/Adapter/Mongo.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1650,4 +1650,12 @@ protected function processException(Exception $e): void
16501650

16511651
throw $e;
16521652
}
1653+
1654+
/**
1655+
* @return int
1656+
*/
1657+
public function getMaxIndexLength(): int
1658+
{
1659+
return 0;
1660+
}
16531661
}

src/Database/Adapter/MySQL.php

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
namespace Utopia\Database\Adapter;
44

5-
use Exception;
65
use PDOException;
76
use Utopia\Database\Database;
87
use Utopia\Database\Exception as DatabaseException;
@@ -19,9 +18,7 @@ class MySQL extends MariaDB
1918
* @param array<string> $attributes
2019
*
2120
* @return string
22-
* @throws Exception
23-
* @throws Exception
24-
* @throws Exception
21+
* @throws DatabaseException
2522
*/
2623
protected function getSQLIndex(string $collection, string $id, string $type, array $attributes): string
2724
{

src/Database/Adapter/Postgres.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1271,7 +1271,7 @@ protected function getSQLType(string $type, int $size, bool $signed = true): str
12711271
switch ($type) {
12721272
case Database::VAR_STRING:
12731273
// $size = $size * 4; // Convert utf8mb4 size to bytes
1274-
if ($size > 16383) {
1274+
if ($size > $this->getMaxVarcharLength()) {
12751275
return 'TEXT';
12761276
}
12771277

src/Database/Adapter/SQL.php

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -314,7 +314,7 @@ public function getAttributeWidth(Document $collection): int
314314
$total += 11;
315315
break;
316316

317-
case ($attribute['size'] > 16383):
317+
case ($attribute['size'] > $this->getMaxVarcharLength()):
318318
// 8 bytes length + 2 bytes for TEXT
319319
$total += 10;
320320
break;
@@ -865,4 +865,20 @@ public static function getPDOAttributes(): array
865865
PDO::ATTR_STRINGIFY_FETCHES => true // Returns all fetched data as Strings
866866
];
867867
}
868+
869+
/**
870+
* @return int
871+
*/
872+
public function getMaxVarcharLength(): int
873+
{
874+
return 16381; // Floor value for Postgres:16383 | MySQL:16381 | MariaDB:16382
875+
}
876+
877+
/**
878+
* @return int
879+
*/
880+
public function getMaxIndexLength(): int
881+
{
882+
return 768;
883+
}
868884
}

src/Database/Database.php

Lines changed: 32 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ class Database
144144
* List of Internal Ids
145145
* @var array<array<string, mixed>>
146146
*/
147-
protected array $attributes = [
147+
protected static array $attributes = [
148148
[
149149
'$id' => '$id',
150150
'type' => self::VAR_STRING,
@@ -619,9 +619,11 @@ public function delete(string $name): bool
619619
* @param array<Document> $indexes
620620
* @param array<string> $permissions
621621
* @param bool $documentSecurity
622-
*
623622
* @return Document
623+
* @throws DatabaseException
624624
* @throws DuplicateException
625+
* @throws InvalidArgumentException
626+
* @throws LimitException
625627
*/
626628
public function createCollection(string $id, array $attributes = [], array $indexes = [], array $permissions = null, bool $documentSecurity = true): Document
627629
{
@@ -640,12 +642,6 @@ public function createCollection(string $id, array $attributes = [], array $inde
640642
throw new DuplicateException('Collection ' . $id . ' already exists');
641643
}
642644

643-
$this->adapter->createCollection($id, $attributes, $indexes);
644-
645-
if ($id === self::METADATA) {
646-
return new Document($this->collection);
647-
}
648-
649645
$collection = new Document([
650646
'$id' => ID::custom($id),
651647
'$permissions' => $permissions,
@@ -655,6 +651,17 @@ public function createCollection(string $id, array $attributes = [], array $inde
655651
'documentSecurity' => $documentSecurity
656652
]);
657653

654+
$validator = new IndexValidator($this->adapter->getMaxIndexLength());
655+
if (!$validator->isValid($collection)) {
656+
throw new DatabaseException($validator->getDescription());
657+
}
658+
659+
$this->adapter->createCollection($id, $attributes, $indexes);
660+
661+
if ($id === self::METADATA) {
662+
return new Document($this->collection);
663+
}
664+
658665
// Check index limits, if given
659666
if ($indexes && $this->adapter->getCountOfIndexes($collection) > $this->adapter->getLimitForIndexes()) {
660667
throw new LimitException('Index limit of ' . $this->adapter->getLimitForIndexes() . ' exceeded. Cannot create collection.');
@@ -692,7 +699,10 @@ public function createCollection(string $id, array $attributes = [], array $inde
692699
* @param bool $documentSecurity
693700
*
694701
* @return Document
695-
* @throws DuplicateException
702+
* @throws InvalidArgumentException
703+
* @throws ConflictException
704+
* @throws DatabaseException
705+
* @throws InvalidArgumentException
696706
*/
697707
public function updateCollection(string $id, array $permissions, bool $documentSecurity): Document
698708
{
@@ -1976,6 +1986,7 @@ public function renameIndex(string $collection, string $old, string $new): bool
19761986
* @throws DuplicateException
19771987
* @throws LimitException
19781988
* @throws StructureException
1989+
* @throws Exception
19791990
*/
19801991
public function createIndex(string $collection, string $id, string $type, array $attributes, array $lengths = [], array $orders = []): bool
19811992
{
@@ -1985,8 +1996,8 @@ public function createIndex(string $collection, string $id, string $type, array
19851996

19861997
$collection = $this->silent(fn () => $this->getCollection($collection));
19871998

1988-
$validator = new IndexValidator($collection);
1989-
if (!$validator->isValid(['type' => $type, 'attributes' => $attributes])) {
1999+
$validator = new IndexValidator($this->adapter->getMaxIndexLength());
2000+
if (!$validator->isValid($collection)) {
19902001
throw new DatabaseException($validator->getDescription());
19912002
}
19922003

@@ -2018,7 +2029,7 @@ public function createIndex(string $collection, string $id, string $type, array
20182029
break;
20192030

20202031
case self::INDEX_FULLTEXT:
2021-
if (!$this->adapter->getSupportForUniqueIndex()) {
2032+
if (!$this->adapter->getSupportForFulltextIndex()) {
20222033
throw new DatabaseException('Fulltext index is not supported');
20232034
}
20242035
break;
@@ -2027,8 +2038,6 @@ public function createIndex(string $collection, string $id, string $type, array
20272038
throw new DatabaseException('Unknown index type: ' . $type . '. Must be one of ' . Database::INDEX_KEY . ', ' . Database::INDEX_UNIQUE . ', ' . Database::INDEX_ARRAY . ', ' . Database::INDEX_FULLTEXT);
20282039
}
20292040

2030-
$index = $this->adapter->createIndex($collection->getId(), $id, $type, $attributes, $lengths, $orders);
2031-
20322041
$collection->setAttribute('indexes', new Document([
20332042
'$id' => ID::custom($id),
20342043
'key' => $id,
@@ -2038,6 +2047,13 @@ public function createIndex(string $collection, string $id, string $type, array
20382047
'orders' => $orders,
20392048
]), Document::SET_TYPE_APPEND);
20402049

2050+
$validator = new IndexValidator($this->adapter->getMaxIndexLength());
2051+
if (!$validator->isValid($collection)) {
2052+
throw new DatabaseException($validator->getDescription());
2053+
}
2054+
2055+
$index = $this->adapter->createIndex($collection->getId(), $id, $type, $attributes, $lengths, $orders);
2056+
20412057
if ($collection->getId() !== self::METADATA) {
20422058
$this->silent(fn () => $this->updateDocument(self::METADATA, $collection->getId(), $collection));
20432059
}
@@ -4167,10 +4183,10 @@ public static function addFilter(string $name, callable $encode, callable $decod
41674183
* @return array<Document>
41684184
* @throws DatabaseException
41694185
*/
4170-
public function getInternalAttributes(): array
4186+
public static function getInternalAttributes(): array
41714187
{
41724188
$attributes = [];
4173-
foreach ($this->attributes as $internal) {
4189+
foreach (self::$attributes as $internal) {
41744190
$attributes[] = new Document($internal);
41754191
}
41764192
return $attributes;

0 commit comments

Comments
 (0)