Skip to content

Commit

Permalink
Fix in cases
Browse files Browse the repository at this point in the history
  • Loading branch information
abnegate committed Aug 19, 2024
1 parent ddf94c9 commit da8113e
Show file tree
Hide file tree
Showing 3 changed files with 93 additions and 60 deletions.
8 changes: 3 additions & 5 deletions src/Database/Adapter/MariaDB.php
Original file line number Diff line number Diff line change
Expand Up @@ -923,10 +923,8 @@ public function createDocuments(string $collection, array $documents, int $batch

try {
$this->getPDO()->beginTransaction();

$name = $this->filter($collection);
$batches = \array_chunk($documents, \max(1, $batchSize));

$internalIds = [];

foreach ($batches as $batch) {
Expand Down Expand Up @@ -1812,7 +1810,7 @@ public function find(string $collection, array $queries = [], ?int $limit = 25,
}

if ($this->sharedTables) {
$where[] = "table_main._tenant IN (:_tenant, NULL)";
$where[] = "(table_main._tenant = :_tenant OR table_main._tenant IS NULL)";
}

$sqlWhere = !empty($where) ? 'WHERE ' . implode(' AND ', $where) : '';
Expand Down Expand Up @@ -1938,7 +1936,7 @@ public function count(string $collection, array $queries = [], ?int $max = null)
}

if ($this->sharedTables) {
$where[] = "table_main._tenant IN (:_tenant, NULL)";
$where[] = "(table_main._tenant = :_tenant OR table_main._tenant IS NULL)";
}

$sqlWhere = !empty($where)
Expand Down Expand Up @@ -2008,7 +2006,7 @@ public function sum(string $collection, string $attribute, array $queries = [],
}

if ($this->sharedTables) {
$where[] = "table_main._tenant IN (:_tenant, NULL)";
$where[] = "(table_main._tenant = :_tenant OR table_main._tenant IS NULL)";
}

$sqlWhere = !empty($where)
Expand Down
137 changes: 86 additions & 51 deletions src/Database/Adapter/Postgres.php
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ public function delete(string $name): bool
* @param array<Document> $attributes
* @param array<Document> $indexes
* @return bool
* @throws DuplicateException
*/
public function createCollection(string $name, array $attributes = [], array $indexes = []): bool
{
Expand All @@ -85,8 +86,6 @@ public function createCollection(string $name, array $attributes = [], array $in
/** @var array<string> $attributeStrings */
$attributeStrings = [];

$this->getPDO()->beginTransaction();

/** @var array<string> $attributeStrings */
$attributeStrings = [];
foreach ($attributes as $attribute) {
Expand All @@ -99,13 +98,30 @@ public function createCollection(string $name, array $attributes = [], array $in
$attribute->getAttribute('array', false)
);

// Ignore relationships with virtual attributes
if ($attribute->getAttribute('type') === Database::VAR_RELATIONSHIP) {
$options = $attribute->getAttribute('options', []);
$relationType = $options['relationType'] ?? null;
$twoWay = $options['twoWay'] ?? false;
$side = $options['side'] ?? null;

if (
$relationType === Database::RELATION_MANY_TO_MANY
|| ($relationType === Database::RELATION_ONE_TO_ONE && !$twoWay && $side === Database::RELATION_SIDE_CHILD)
|| ($relationType === Database::RELATION_ONE_TO_MANY && $side === Database::RELATION_SIDE_PARENT)
|| ($relationType === Database::RELATION_MANY_TO_ONE && $side === Database::RELATION_SIDE_CHILD)
) {
continue;
}
}

$attributeStrings[] = "\"{$attrId}\" {$attrType}, ";
}

$sqlTenant = $this->sharedTables ? '_tenant INTEGER DEFAULT NULL,' : '';

$sql = "
CREATE TABLE IF NOT EXISTS {$this->getSQLTable($id)} (
$collection = "
CREATE TABLE {$this->getSQLTable($id)} (
_id SERIAL NOT NULL,
_uid VARCHAR(255) NOT NULL,
". $sqlTenant ."
Expand All @@ -116,59 +132,60 @@ public function createCollection(string $name, array $attributes = [], array $in
PRIMARY KEY (_id)
);
";

if ($this->sharedTables) {
$sql .= "
$collection .= "
CREATE UNIQUE INDEX \"{$namespace}_{$this->tenant}_{$id}_uid\" ON {$this->getSQLTable($id)} (LOWER(_uid), _tenant);
CREATE INDEX \"{$namespace}_{$this->tenant}_{$id}_created\" ON {$this->getSQLTable($id)} (_tenant, \"_createdAt\");
CREATE INDEX \"{$namespace}_{$this->tenant}_{$id}_updated\" ON {$this->getSQLTable($id)} (_tenant, \"_updatedAt\");
CREATE INDEX \"{$namespace}_{$this->tenant}_{$id}_tenant_id\" ON {$this->getSQLTable($id)} (_tenant, _id);
";
} else {
$sql .= "
$collection .= "
CREATE UNIQUE INDEX \"{$namespace}_{$id}_uid\" ON {$this->getSQLTable($id)} (LOWER(_uid));
CREATE INDEX \"{$namespace}_{$id}_created\" ON {$this->getSQLTable($id)} (\"_createdAt\");
CREATE INDEX \"{$namespace}_{$id}_updated\" ON {$this->getSQLTable($id)} (\"_updatedAt\");
";
}

$sql = $this->trigger(Database::EVENT_COLLECTION_CREATE, $sql);

$stmt = $this->getPDO()->prepare($sql);
$collection = $this->trigger(Database::EVENT_COLLECTION_CREATE, $collection);

try {
$stmt->execute();
$permissions = "
CREATE TABLE {$this->getSQLTable($id . '_perms')} (
_id SERIAL NOT NULL,
_tenant INTEGER DEFAULT NULL,
_type VARCHAR(12) NOT NULL,
_permission VARCHAR(255) NOT NULL,
_document VARCHAR(255) NOT NULL,
PRIMARY KEY (_id)
);
";

$sql = "
CREATE TABLE IF NOT EXISTS {$this->getSQLTable($id . '_perms')} (
_id SERIAL NOT NULL,
_tenant INTEGER DEFAULT NULL,
_type VARCHAR(12) NOT NULL,
_permission VARCHAR(255) NOT NULL,
_document VARCHAR(255) NOT NULL,
PRIMARY KEY (_id)
);
";
if ($this->sharedTables) {
$permissions .= "
CREATE UNIQUE INDEX \"{$namespace}_{$this->tenant}_{$id}_ukey\"
ON {$this->getSQLTable($id. '_perms')} USING btree (_tenant,_document,_type,_permission);
CREATE INDEX \"{$namespace}_{$this->tenant}_{$id}_permission\"
ON {$this->getSQLTable($id. '_perms')} USING btree (_tenant,_permission,_type);
";
} else {
$permissions .= "
CREATE UNIQUE INDEX \"{$namespace}_{$id}_ukey\"
ON {$this->getSQLTable($id. '_perms')} USING btree (_document,_type,_permission);
CREATE INDEX \"{$namespace}_{$id}_permission\"
ON {$this->getSQLTable($id. '_perms')} USING btree (_permission,_type);
";
}

if ($this->sharedTables) {
$sql .= "
CREATE UNIQUE INDEX \"{$namespace}_{$this->tenant}_{$id}_ukey\"
ON {$this->getSQLTable($id. '_perms')} USING btree (_tenant,_document,_type,_permission);
CREATE INDEX \"{$namespace}_{$this->tenant}_{$id}_permission\"
ON {$this->getSQLTable($id. '_perms')} USING btree (_tenant,_permission,_type);
";
} else {
$sql .= "
CREATE UNIQUE INDEX \"{$namespace}_{$id}_ukey\"
ON {$this->getSQLTable($id. '_perms')} USING btree (_document,_type,_permission);
CREATE INDEX \"{$namespace}_{$id}_permission\"
ON {$this->getSQLTable($id. '_perms')} USING btree (_permission,_type);
";
}
$permissions = $this->trigger(Database::EVENT_COLLECTION_CREATE, $permissions);

$sql = $this->trigger(Database::EVENT_COLLECTION_CREATE, $sql);
try {
$this->getPDO()
->prepare($collection)
->execute();

$this->getPDO()
->prepare($sql)
->prepare($permissions)
->execute();

foreach ($indexes as $index) {
Expand All @@ -186,13 +203,16 @@ public function createCollection(string $name, array $attributes = [], array $in
$indexOrders
);
}
} catch (Exception $e) {
$this->getPDO()->rollBack();
throw new DatabaseException('Failed to create collection: ' . $e->getMessage());
}
} catch (PDOException $e) {
$e = $this->processException($e);

if (!$this->getPDO()->commit()) {
throw new DatabaseException('Failed to commit transaction');
if (!($e instanceof DuplicateException)) {
$this->getPDO()
->prepare("DROP TABLE IF EXISTS {$this->getSQLTable($id)}, {$this->getSQLTable($id . '_perms')};")
->execute();
}

throw $e;
}

return true;
Expand Down Expand Up @@ -900,11 +920,12 @@ public function createDocuments(string $collection, array $documents, int $batch
return $documents;
}

$this->getPDO()->beginTransaction();

try {
$this->getPDO()->beginTransaction();
$name = $this->filter($collection);
$batches = \array_chunk($documents, max(1, $batchSize));
$internalIds = [];

foreach ($batches as $batch) {
$bindIndex = 0;
Expand All @@ -919,6 +940,11 @@ public function createDocuments(string $collection, array $documents, int $batch
$attributes['_updatedAt'] = $document->getUpdatedAt();
$attributes['_permissions'] = \json_encode($document->getPermissions());

if(!empty($document->getInternalId())) {
$internalIds[$document->getId()] = true;
$attributes['_id'] = $document->getInternalId();
}

if($this->sharedTables) {
$attributes['_tenant'] = $this->tenant;
}
Expand Down Expand Up @@ -978,9 +1004,6 @@ public function createDocuments(string $collection, array $documents, int $batch
if (!$this->getPDO()->commit()) {
throw new DatabaseException('Failed to commit transaction');
}

return $documents;

} catch (PDOException $e) {
$this->getPDO()->rollBack();

Expand All @@ -989,6 +1012,18 @@ public function createDocuments(string $collection, array $documents, int $batch
default => $e,
};
}

foreach ($documents as $document) {
if(!isset($internalIds[$document->getId()])) {
$document['$internalId'] = $this->getDocument(
$collection,
$document->getId(),
[Query::select(['$internalId'])]
)->getInternalId();
}
}

return $documents;
}

/**
Expand Down Expand Up @@ -1709,7 +1744,7 @@ public function find(string $collection, array $queries = [], ?int $limit = 25,
}

if ($this->sharedTables) {
$where[] = "table_main._tenant IN (:_tenant, NULL)";
$where[] = "(table_main._tenant = :_tenant OR table_main._tenant IS NULL)";
}

if (Authorization::$status) {
Expand Down Expand Up @@ -1835,7 +1870,7 @@ public function count(string $collection, array $queries = [], ?int $max = null)
}

if ($this->sharedTables) {
$where[] = "table_main._tenant IN (:_tenant, NULL)";
$where[] = "(table_main._tenant = :_tenant OR table_main._tenant IS NULL)";
}

if (Authorization::$status) {
Expand Down Expand Up @@ -1898,7 +1933,7 @@ public function sum(string $collection, string $attribute, array $queries = [],
}

if ($this->sharedTables) {
$where[] = "table_main._tenant IN (:_tenant, NULL)";
$where[] = "(table_main._tenant = :_tenant OR table_main._tenant IS NULL)";
}

if (Authorization::$status) {
Expand Down
8 changes: 4 additions & 4 deletions tests/e2e/Adapter/Base.php
Original file line number Diff line number Diff line change
Expand Up @@ -1001,18 +1001,18 @@ public function testQueryTimeout(): void
]));
}

$this->expectException(TimeoutException::class);

static::getDatabase()->setTimeout(1);

try {
static::getDatabase()->find('global-timeouts', [
Query::notEqual('longtext', 'appwrite'),
]);
} catch(TimeoutException $ex) {
$this->fail('Failed to throw exception');
} catch(\Exception $e) {
\var_dump($e);
$this->assertInstanceOf(TimeoutException::class, $e);
static::getDatabase()->clearTimeout();
static::getDatabase()->deleteCollection('global-timeouts');
throw $ex;
}
}

Expand Down

0 comments on commit da8113e

Please sign in to comment.