diff --git a/src/Database/Adapter.php b/src/Database/Adapter.php index 861826c67..5f3bde320 100644 --- a/src/Database/Adapter.php +++ b/src/Database/Adapter.php @@ -1186,4 +1186,10 @@ abstract public function getSchemaAttributes(string $collection): array; * @return string */ abstract public function getTenantQuery(string $collection, string $alias = ''): string; + + /** + * @param mixed $stmt + * @return bool + */ + abstract protected function execute(mixed $stmt): bool; } diff --git a/src/Database/Adapter/MariaDB.php b/src/Database/Adapter/MariaDB.php index 31bdb1ad0..1d429ccd5 100644 --- a/src/Database/Adapter/MariaDB.php +++ b/src/Database/Adapter/MariaDB.php @@ -921,160 +921,6 @@ public function createDocument(string $collection, Document $document): Document return $document; } - /** - * Create Documents in batches - * - * @param string $collection - * @param array $documents - * - * @return array - * - * @throws DuplicateException - * @throws \Throwable - */ - public function createDocuments(string $collection, array $documents): array - { - if (empty($documents)) { - return $documents; - } - - try { - $name = $this->filter($collection); - - $attributeKeys = Database::INTERNAL_ATTRIBUTE_KEYS; - - $hasSequence = null; - foreach ($documents as $document) { - $attributes = $document->getAttributes(); - $attributeKeys = [...$attributeKeys, ...\array_keys($attributes)]; - - if ($hasSequence === null) { - $hasSequence = !empty($document->getSequence()); - } elseif ($hasSequence == empty($document->getSequence())) { - throw new DatabaseException('All documents must have an sequence if one is set'); - } - } - - $attributeKeys = array_unique($attributeKeys); - - if ($hasSequence) { - $attributeKeys[] = '_id'; - } - - if ($this->sharedTables) { - $attributeKeys[] = '_tenant'; - } - - $columns = []; - foreach ($attributeKeys as $key => $attribute) { - $columns[$key] = $this->quote($this->filter($attribute)); - } - - $columns = '(' . \implode(', ', $columns) . ')'; - - $bindIndex = 0; - $batchKeys = []; - $bindValues = []; - $permissions = []; - $documentIds = []; - $documentTenants = []; - - foreach ($documents as $index => $document) { - $attributes = $document->getAttributes(); - $attributes['_uid'] = $document->getId(); - $attributes['_createdAt'] = $document->getCreatedAt(); - $attributes['_updatedAt'] = $document->getUpdatedAt(); - $attributes['_permissions'] = \json_encode($document->getPermissions()); - - if (!empty($document->getSequence())) { - $attributes['_id'] = $document->getSequence(); - } else { - $documentIds[] = $document->getId(); - } - - if ($this->sharedTables) { - $attributes['_tenant'] = $document->getTenant(); - $documentTenants[] = $document->getTenant(); - } - - $bindKeys = []; - - foreach ($attributeKeys as $key) { - $value = $attributes[$key] ?? null; - if (\is_array($value)) { - $value = \json_encode($value); - } - $value = (\is_bool($value)) ? (int)$value : $value; - $bindKey = 'key_' . $bindIndex; - $bindKeys[] = ':' . $bindKey; - $bindValues[$bindKey] = $value; - $bindIndex++; - } - - $batchKeys[] = '(' . \implode(', ', $bindKeys) . ')'; - - foreach (Database::PERMISSIONS as $type) { - foreach ($document->getPermissionsByType($type) as $permission) { - $tenantBind = $this->sharedTables ? ", :_tenant_{$index}" : ''; - $permission = \str_replace('"', '', $permission); - $permission = "('{$type}', '{$permission}', :_uid_{$index} {$tenantBind})"; - $permissions[] = $permission; - } - } - } - - $batchKeys = \implode(', ', $batchKeys); - - $stmt = $this->getPDO()->prepare(" - INSERT INTO {$this->getSQLTable($name)} {$columns} - VALUES {$batchKeys} - "); - - foreach ($bindValues as $key => $value) { - $stmt->bindValue($key, $value, $this->getPDOType($value)); - } - - $stmt->execute(); - - if (!empty($permissions)) { - $tenantColumn = $this->sharedTables ? ', _tenant' : ''; - $permissions = \implode(', ', $permissions); - - $sqlPermissions = " - INSERT INTO {$this->getSQLTable($name . '_perms')} (_type, _permission, _document {$tenantColumn}) - VALUES {$permissions}; - "; - - $stmtPermissions = $this->getPDO()->prepare($sqlPermissions); - - foreach ($documents as $index => $document) { - $stmtPermissions->bindValue(":_uid_{$index}", $document->getId()); - if ($this->sharedTables) { - $stmtPermissions->bindValue(":_tenant_{$index}", $document->getTenant()); - } - } - - $stmtPermissions?->execute(); - } - - $sequences = $this->getSequences( - $collection, - $documentIds, - $documentTenants - ); - - foreach ($documents as $document) { - if (isset($sequences[$document->getId()])) { - $document['$sequence'] = $sequences[$document->getId()]; - } - } - } catch (PDOException $e) { - throw $this->processException($e); - } - - return $documents; - } - /** * Update Document * diff --git a/src/Database/Adapter/Pool.php b/src/Database/Adapter/Pool.php index e863e8d7e..b6bc54b08 100644 --- a/src/Database/Adapter/Pool.php +++ b/src/Database/Adapter/Pool.php @@ -484,4 +484,9 @@ public function getTenantQuery(string $collection, string $alias = ''): string { return $this->delegate(__FUNCTION__, \func_get_args()); } + + protected function execute(mixed $stmt): bool + { + return $this->delegate(__FUNCTION__, \func_get_args()); + } } diff --git a/src/Database/Adapter/Postgres.php b/src/Database/Adapter/Postgres.php index 9521d1143..37d2edeec 100644 --- a/src/Database/Adapter/Postgres.php +++ b/src/Database/Adapter/Postgres.php @@ -84,7 +84,7 @@ public function rollbackTransaction(): bool return $result; } - private function execute(mixed $stmt): bool + protected function execute(mixed $stmt): bool { $pdo = $this->getPDO(); @@ -1042,160 +1042,6 @@ public function createDocument(string $collection, Document $document): Document return $document; } - /** - * Create Documents in batches - * - * @param string $collection - * @param array $documents - * - * @return array - * - * @throws DuplicateException - * @throws \Throwable - */ - public function createDocuments(string $collection, array $documents): array - { - if (empty($documents)) { - return $documents; - } - - try { - $name = $this->filter($collection); - - $attributeKeys = Database::INTERNAL_ATTRIBUTE_KEYS; - - $hasSequence = null; - foreach ($documents as $document) { - $attributes = $document->getAttributes(); - $attributeKeys = [...$attributeKeys, ...\array_keys($attributes)]; - - if ($hasSequence === null) { - $hasSequence = !empty($document->getSequence()); - } elseif ($hasSequence == empty($document->getSequence())) { - throw new DatabaseException('All documents must have an sequence if one is set'); - } - } - - $attributeKeys = array_unique($attributeKeys); - - if ($hasSequence) { - $attributeKeys[] = '_id'; - } - - if ($this->sharedTables) { - $attributeKeys[] = '_tenant'; - } - - $columns = []; - foreach ($attributeKeys as $key => $attribute) { - $columns[$key] = $this->quote($this->filter($attribute)); - } - - $columns = '(' . \implode(', ', $columns) . ')'; - - $bindIndex = 0; - $batchKeys = []; - $bindValues = []; - $permissions = []; - $documentIds = []; - $documentTenants = []; - - foreach ($documents as $index => $document) { - $attributes = $document->getAttributes(); - $attributes['_uid'] = $document->getId(); - $attributes['_createdAt'] = $document->getCreatedAt(); - $attributes['_updatedAt'] = $document->getUpdatedAt(); - $attributes['_permissions'] = \json_encode($document->getPermissions()); - - if (!empty($document->getSequence())) { - $attributes['_id'] = $document->getSequence(); - } else { - $documentIds[] = $document->getId(); - } - - if ($this->sharedTables) { - $attributes['_tenant'] = $document->getTenant(); - $documentTenants[] = $document->getTenant(); - } - - $bindKeys = []; - - foreach ($attributeKeys as $key) { - $value = $attributes[$key] ?? null; - if (\is_array($value)) { - $value = \json_encode($value); - } - $value = (\is_bool($value)) ? (int)$value : $value; - $bindKey = 'key_' . $bindIndex; - $bindKeys[] = ':' . $bindKey; - $bindValues[$bindKey] = $value; - $bindIndex++; - } - - $batchKeys[] = '(' . \implode(', ', $bindKeys) . ')'; - - foreach (Database::PERMISSIONS as $type) { - foreach ($document->getPermissionsByType($type) as $permission) { - $tenantBind = $this->sharedTables ? ", :_tenant_{$index}" : ''; - $permission = \str_replace('"', '', $permission); - $permission = "('{$type}', '{$permission}', :_uid_{$index} {$tenantBind})"; - $permissions[] = $permission; - } - } - } - - $batchKeys = \implode(', ', $batchKeys); - - $stmt = $this->getPDO()->prepare(" - INSERT INTO {$this->getSQLTable($name)} {$columns} - VALUES {$batchKeys} - "); - - foreach ($bindValues as $key => $value) { - $stmt->bindValue($key, $value, $this->getPDOType($value)); - } - - $this->execute($stmt); - - if (!empty($permissions)) { - $tenantColumn = $this->sharedTables ? ', _tenant' : ''; - $permissions = \implode(', ', $permissions); - - $sqlPermissions = " - INSERT INTO {$this->getSQLTable($name . '_perms')} (_type, _permission, _document {$tenantColumn}) - VALUES {$permissions}; - "; - - $stmtPermissions = $this->getPDO()->prepare($sqlPermissions); - - foreach ($documents as $index => $document) { - $stmtPermissions->bindValue(":_uid_{$index}", $document->getId()); - if ($this->sharedTables) { - $stmtPermissions->bindValue(":_tenant_{$index}", $document->getTenant()); - } - } - - $this->execute($stmtPermissions); - } - - $sequences = $this->getSequences( - $collection, - $documentIds, - $documentTenants - ); - - foreach ($documents as $document) { - if (isset($sequences[$document->getId()])) { - $document['$sequence'] = $sequences[$document->getId()]; - } - } - } catch (PDOException $e) { - throw $this->processException($e); - } - - return $documents; - } - /** * Update Document * diff --git a/src/Database/Adapter/SQL.php b/src/Database/Adapter/SQL.php index 34dcba8a8..15f0b9d99 100644 --- a/src/Database/Adapter/SQL.php +++ b/src/Database/Adapter/SQL.php @@ -8,6 +8,7 @@ use Utopia\Database\Database; use Utopia\Database\Document; 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\Transaction as TransactionException; @@ -1749,4 +1750,167 @@ protected function processException(PDOException $e): \Exception { return $e; } + + /** + * @param mixed $stmt + * @return bool + */ + protected function execute(mixed $stmt): bool + { + return $stmt->execute(); + } + + /** + * Create Documents in batches + * + * @param string $collection + * @param array $documents + * + * @return array + * + * @throws DuplicateException + * @throws \Throwable + */ + public function createDocuments(string $collection, array $documents): array + { + if (empty($documents)) { + return $documents; + } + + try { + $name = $this->filter($collection); + + $attributeKeys = Database::INTERNAL_ATTRIBUTE_KEYS; + + $hasSequence = null; + foreach ($documents as $document) { + $attributes = $document->getAttributes(); + $attributeKeys = [...$attributeKeys, ...\array_keys($attributes)]; + + if ($hasSequence === null) { + $hasSequence = !empty($document->getSequence()); + } elseif ($hasSequence == empty($document->getSequence())) { + throw new DatabaseException('All documents must have an sequence if one is set'); + } + } + + $attributeKeys = array_unique($attributeKeys); + + if ($hasSequence) { + $attributeKeys[] = '_id'; + } + + if ($this->sharedTables) { + $attributeKeys[] = '_tenant'; + } + + $columns = []; + foreach ($attributeKeys as $key => $attribute) { + $columns[$key] = $this->quote($this->filter($attribute)); + } + + $columns = '(' . \implode(', ', $columns) . ')'; + + $bindIndex = 0; + $batchKeys = []; + $bindValues = []; + $permissions = []; + $documentIds = []; + $documentTenants = []; + + foreach ($documents as $index => $document) { + $attributes = $document->getAttributes(); + $attributes['_uid'] = $document->getId(); + $attributes['_createdAt'] = $document->getCreatedAt(); + $attributes['_updatedAt'] = $document->getUpdatedAt(); + $attributes['_permissions'] = \json_encode($document->getPermissions()); + + if (!empty($document->getSequence())) { + $attributes['_id'] = $document->getSequence(); + } else { + $documentIds[] = $document->getId(); + } + + if ($this->sharedTables) { + $attributes['_tenant'] = $document->getTenant(); + $documentTenants[] = $document->getTenant(); + } + + $bindKeys = []; + + foreach ($attributeKeys as $key) { + $value = $attributes[$key] ?? null; + if (\is_array($value)) { + $value = \json_encode($value); + } + $value = (\is_bool($value)) ? (int)$value : $value; + $bindKey = 'key_' . $bindIndex; + $bindKeys[] = ':' . $bindKey; + $bindValues[$bindKey] = $value; + $bindIndex++; + } + + $batchKeys[] = '(' . \implode(', ', $bindKeys) . ')'; + + foreach (Database::PERMISSIONS as $type) { + foreach ($document->getPermissionsByType($type) as $permission) { + $tenantBind = $this->sharedTables ? ", :_tenant_{$index}" : ''; + $permission = \str_replace('"', '', $permission); + $permission = "('{$type}', '{$permission}', :_uid_{$index} {$tenantBind})"; + $permissions[] = $permission; + } + } + } + + $batchKeys = \implode(', ', $batchKeys); + + $stmt = $this->getPDO()->prepare(" + INSERT INTO {$this->getSQLTable($name)} {$columns} + VALUES {$batchKeys} + "); + + foreach ($bindValues as $key => $value) { + $stmt->bindValue($key, $value, $this->getPDOType($value)); + } + + $this->execute($stmt); + + if (!empty($permissions)) { + $tenantColumn = $this->sharedTables ? ', _tenant' : ''; + $permissions = \implode(', ', $permissions); + + $sqlPermissions = " + INSERT INTO {$this->getSQLTable($name . '_perms')} (_type, _permission, _document {$tenantColumn}) + VALUES {$permissions}; + "; + + $stmtPermissions = $this->getPDO()->prepare($sqlPermissions); + + foreach ($documents as $index => $document) { + $stmtPermissions->bindValue(":_uid_{$index}", $document->getId()); + if ($this->sharedTables) { + $stmtPermissions->bindValue(":_tenant_{$index}", $document->getTenant()); + } + } + + $this->execute($stmtPermissions); + } + + $sequences = $this->getSequences( + $collection, + $documentIds, + $documentTenants + ); + + foreach ($documents as $document) { + if (isset($sequences[$document->getId()])) { + $document['$sequence'] = $sequences[$document->getId()]; + } + } + } catch (PDOException $e) { + throw $this->processException($e); + } + + return $documents; + } }