diff options
author | Christoph Wurst <ChristophWurst@users.noreply.github.com> | 2021-01-14 12:27:23 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-01-14 12:27:23 +0100 |
commit | b9287f9780c6743f0f5e2db84fe32c470cf70b22 (patch) | |
tree | c34823626526580de9a9f3b0e1b77e28600b042f /lib | |
parent | 5a141bd1622c3cb3573740e81ce573453121800c (diff) | |
parent | 2c9cdc1cdbf900a8578c80075f5ea2da479c4f80 (diff) | |
download | nextcloud-server-b9287f9780c6743f0f5e2db84fe32c470cf70b22.tar.gz nextcloud-server-b9287f9780c6743f0f5e2db84fe32c470cf70b22.zip |
Merge pull request #25091 from nextcloud/enhancement/ocp-db-exception-abstraction
Add our own DB exception abstraction
Diffstat (limited to 'lib')
-rw-r--r-- | lib/composer/composer/autoload_classmap.php | 2 | ||||
-rw-r--r-- | lib/composer/composer/autoload_static.php | 2 | ||||
-rw-r--r-- | lib/private/DB/Adapter.php | 10 | ||||
-rw-r--r-- | lib/private/DB/Connection.php | 25 | ||||
-rw-r--r-- | lib/private/DB/ConnectionAdapter.php | 118 | ||||
-rw-r--r-- | lib/private/DB/Exceptions/DbalException.php | 136 | ||||
-rw-r--r-- | lib/private/DB/Migrator.php | 7 | ||||
-rw-r--r-- | lib/public/DB/Exception.php | 149 | ||||
-rw-r--r-- | lib/public/DB/QueryBuilder/IQueryBuilder.php | 2 | ||||
-rw-r--r-- | lib/public/IDBConnection.php | 15 |
10 files changed, 439 insertions, 27 deletions
diff --git a/lib/composer/composer/autoload_classmap.php b/lib/composer/composer/autoload_classmap.php index dc5f9dc9e8e..b1068d1a4b2 100644 --- a/lib/composer/composer/autoload_classmap.php +++ b/lib/composer/composer/autoload_classmap.php @@ -161,6 +161,7 @@ return array( 'OCP\\Contacts\\ContactsMenu\\IProvider' => $baseDir . '/lib/public/Contacts/ContactsMenu/IProvider.php', 'OCP\\Contacts\\Events\\ContactInteractedWithEvent' => $baseDir . '/lib/public/Contacts/Events/ContactInteractedWithEvent.php', 'OCP\\Contacts\\IManager' => $baseDir . '/lib/public/Contacts/IManager.php', + 'OCP\\DB\\Exception' => $baseDir . '/lib/public/DB/Exception.php', 'OCP\\DB\\IPreparedStatement' => $baseDir . '/lib/public/DB/IPreparedStatement.php', 'OCP\\DB\\IResult' => $baseDir . '/lib/public/DB/IResult.php', 'OCP\\DB\\ISchemaWrapper' => $baseDir . '/lib/public/DB/ISchemaWrapper.php', @@ -953,6 +954,7 @@ return array( 'OC\\DB\\Connection' => $baseDir . '/lib/private/DB/Connection.php', 'OC\\DB\\ConnectionAdapter' => $baseDir . '/lib/private/DB/ConnectionAdapter.php', 'OC\\DB\\ConnectionFactory' => $baseDir . '/lib/private/DB/ConnectionFactory.php', + 'OC\\DB\\Exceptions\\DbalException' => $baseDir . '/lib/private/DB/Exceptions/DbalException.php', 'OC\\DB\\MDB2SchemaManager' => $baseDir . '/lib/private/DB/MDB2SchemaManager.php', 'OC\\DB\\MDB2SchemaReader' => $baseDir . '/lib/private/DB/MDB2SchemaReader.php', 'OC\\DB\\MigrationException' => $baseDir . '/lib/private/DB/MigrationException.php', diff --git a/lib/composer/composer/autoload_static.php b/lib/composer/composer/autoload_static.php index 455fb01b18e..d1e11bbb09a 100644 --- a/lib/composer/composer/autoload_static.php +++ b/lib/composer/composer/autoload_static.php @@ -190,6 +190,7 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c 'OCP\\Contacts\\ContactsMenu\\IProvider' => __DIR__ . '/../../..' . '/lib/public/Contacts/ContactsMenu/IProvider.php', 'OCP\\Contacts\\Events\\ContactInteractedWithEvent' => __DIR__ . '/../../..' . '/lib/public/Contacts/Events/ContactInteractedWithEvent.php', 'OCP\\Contacts\\IManager' => __DIR__ . '/../../..' . '/lib/public/Contacts/IManager.php', + 'OCP\\DB\\Exception' => __DIR__ . '/../../..' . '/lib/public/DB/Exception.php', 'OCP\\DB\\IPreparedStatement' => __DIR__ . '/../../..' . '/lib/public/DB/IPreparedStatement.php', 'OCP\\DB\\IResult' => __DIR__ . '/../../..' . '/lib/public/DB/IResult.php', 'OCP\\DB\\ISchemaWrapper' => __DIR__ . '/../../..' . '/lib/public/DB/ISchemaWrapper.php', @@ -982,6 +983,7 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c 'OC\\DB\\Connection' => __DIR__ . '/../../..' . '/lib/private/DB/Connection.php', 'OC\\DB\\ConnectionAdapter' => __DIR__ . '/../../..' . '/lib/private/DB/ConnectionAdapter.php', 'OC\\DB\\ConnectionFactory' => __DIR__ . '/../../..' . '/lib/private/DB/ConnectionFactory.php', + 'OC\\DB\\Exceptions\\DbalException' => __DIR__ . '/../../..' . '/lib/private/DB/Exceptions/DbalException.php', 'OC\\DB\\MDB2SchemaManager' => __DIR__ . '/../../..' . '/lib/private/DB/MDB2SchemaManager.php', 'OC\\DB\\MDB2SchemaReader' => __DIR__ . '/../../..' . '/lib/private/DB/MDB2SchemaReader.php', 'OC\\DB\\MigrationException' => __DIR__ . '/../../..' . '/lib/private/DB/MigrationException.php', diff --git a/lib/private/DB/Adapter.php b/lib/private/DB/Adapter.php index 49b831301be..62514e7d83a 100644 --- a/lib/private/DB/Adapter.php +++ b/lib/private/DB/Adapter.php @@ -30,6 +30,7 @@ namespace OC\DB; +use Doctrine\DBAL\Exception; use Doctrine\DBAL\Exception\UniqueConstraintViolationException; /** @@ -49,7 +50,9 @@ class Adapter { /** * @param string $table name + * * @return int id of last insert statement + * @throws Exception */ public function lastInsertId($table) { return (int) $this->conn->realLastInsertId($table); @@ -67,6 +70,7 @@ class Adapter { * Create an exclusive read+write lock on a table * * @param string $tableName + * @throws Exception * @since 9.1.0 */ public function lockTable($tableName) { @@ -77,6 +81,7 @@ class Adapter { /** * Release a previous acquired lock again * + * @throws Exception * @since 9.1.0 */ public function unlockTable() { @@ -94,7 +99,7 @@ class Adapter { * If this is null or an empty array, all keys of $input will be compared * Please note: text fields (clob) must not be used in the compare array * @return int number of inserted rows - * @throws \Doctrine\DBAL\Exception + * @throws Exception * @deprecated 15.0.0 - use unique index and "try { $db->insert() } catch (UniqueConstraintViolationException $e) {}" instead, because it is more reliable and does not have the risk for deadlocks - see https://github.com/nextcloud/server/pull/12371 */ public function insertIfNotExist($table, $input, array $compare = null) { @@ -130,6 +135,9 @@ class Adapter { } } + /** + * @throws \OCP\DB\Exception + */ public function insertIgnoreConflict(string $table,array $values) : int { try { $builder = $this->conn->getQueryBuilder(); diff --git a/lib/private/DB/Connection.php b/lib/private/DB/Connection.php index c67c6df0826..cb7af4d51e2 100644 --- a/lib/private/DB/Connection.php +++ b/lib/private/DB/Connection.php @@ -74,6 +74,9 @@ class Connection extends ReconnectWrapper { /** @var int */ protected $queriesExecuted = 0; + /** + * @throws Exception + */ public function connect() { try { return parent::connect(); @@ -183,7 +186,9 @@ class Connection extends ReconnectWrapper { * @param string $statement The SQL statement to prepare. * @param int $limit * @param int $offset + * * @return Statement The prepared statement. + * @throws Exception */ public function prepare($statement, $limit = null, $offset = null): Statement { if ($limit === -1) { @@ -221,6 +226,9 @@ class Connection extends ReconnectWrapper { return parent::executeQuery($sql, $params, $types, $qcp); } + /** + * @throws Exception + */ public function executeUpdate(string $sql, array $params = [], array $types = []): int { $sql = $this->replaceTablePrefix($sql); $sql = $this->adapter->fixupStatement($sql); @@ -258,7 +266,9 @@ class Connection extends ReconnectWrapper { * columns or sequences. * * @param string $seqName Name of the sequence object from which the ID should be returned. + * * @return string the last inserted ID. + * @throws Exception */ public function lastInsertId($seqName = null) { if ($seqName) { @@ -267,7 +277,10 @@ class Connection extends ReconnectWrapper { return $this->adapter->lastInsertId($seqName); } - // internal use + /** + * @internal + * @throws Exception + */ public function realLastInsertId($seqName = null) { return parent::lastInsertId($seqName); } @@ -364,7 +377,9 @@ class Connection extends ReconnectWrapper { * Create an exclusive read+write lock on a table * * @param string $tableName + * * @throws \BadMethodCallException When trying to acquire a second lock + * @throws Exception * @since 9.1.0 */ public function lockTable($tableName) { @@ -380,6 +395,7 @@ class Connection extends ReconnectWrapper { /** * Release a previous acquired lock again * + * @throws Exception * @since 9.1.0 */ public function unlockTable() { @@ -415,6 +431,8 @@ class Connection extends ReconnectWrapper { * Drop a table from the database if it exists * * @param string $table table name without the prefix + * + * @throws Exception */ public function dropTable($table) { $table = $this->tablePrefix . trim($table); @@ -428,7 +446,9 @@ class Connection extends ReconnectWrapper { * Check if a table exists * * @param string $table table name without the prefix + * * @return bool + * @throws Exception */ public function tableExists($table) { $table = $this->tablePrefix . trim($table); @@ -483,6 +503,7 @@ class Connection extends ReconnectWrapper { * Create the schema of the connected database * * @return Schema + * @throws Exception */ public function createSchema() { $schemaManager = new MDB2SchemaManager($this); @@ -494,6 +515,8 @@ class Connection extends ReconnectWrapper { * Migrate the database to the given schema * * @param Schema $toSchema + * + * @throws Exception */ public function migrateToSchema(Schema $toSchema) { $schemaManager = new MDB2SchemaManager($this); diff --git a/lib/private/DB/ConnectionAdapter.php b/lib/private/DB/ConnectionAdapter.php index 97a0b60044d..62c013e4dcf 100644 --- a/lib/private/DB/ConnectionAdapter.php +++ b/lib/private/DB/ConnectionAdapter.php @@ -25,8 +25,10 @@ declare(strict_types=1); namespace OC\DB; +use Doctrine\DBAL\Exception; use Doctrine\DBAL\Platforms\AbstractPlatform; use Doctrine\DBAL\Schema\Schema; +use OC\DB\Exceptions\DbalException; use OCP\DB\IPreparedStatement; use OCP\DB\IResult; use OCP\DB\QueryBuilder\IQueryBuilder; @@ -49,51 +51,95 @@ class ConnectionAdapter implements IDBConnection { } public function prepare($sql, $limit = null, $offset = null): IPreparedStatement { - return new PreparedStatement( - $this->inner->prepare($sql, $limit, $offset) - ); + try { + return new PreparedStatement( + $this->inner->prepare($sql, $limit, $offset) + ); + } catch (Exception $e) { + throw DbalException::wrap($e); + } } public function executeQuery(string $sql, array $params = [], $types = []): IResult { - return new ResultAdapter( - $this->inner->executeQuery($sql, $params, $types) - ); + try { + return new ResultAdapter( + $this->inner->executeQuery($sql, $params, $types) + ); + } catch (Exception $e) { + throw DbalException::wrap($e); + } } public function executeUpdate(string $sql, array $params = [], array $types = []): int { - return $this->inner->executeUpdate($sql, $params, $types); + try { + return $this->inner->executeUpdate($sql, $params, $types); + } catch (Exception $e) { + throw DbalException::wrap($e); + } } public function executeStatement($sql, array $params = [], array $types = []): int { - return $this->inner->executeStatement($sql, $params, $types); + try { + return $this->inner->executeStatement($sql, $params, $types); + } catch (Exception $e) { + throw DbalException::wrap($e); + } } public function lastInsertId(string $table): int { - return (int) $this->inner->lastInsertId($table); + try { + return (int)$this->inner->lastInsertId($table); + } catch (Exception $e) { + throw DbalException::wrap($e); + } } public function insertIfNotExist(string $table, array $input, array $compare = null) { - return $this->inner->insertIfNotExist($table, $input, $compare); + try { + return $this->inner->insertIfNotExist($table, $input, $compare); + } catch (Exception $e) { + throw DbalException::wrap($e); + } } public function insertIgnoreConflict(string $table, array $values): int { - return $this->inner->insertIgnoreConflict($table, $values); + try { + return $this->inner->insertIgnoreConflict($table, $values); + } catch (Exception $e) { + throw DbalException::wrap($e); + } } public function setValues($table, array $keys, array $values, array $updatePreconditionValues = []): int { - return $this->inner->setValues($table, $keys, $values, $updatePreconditionValues); + try { + return $this->inner->setValues($table, $keys, $values, $updatePreconditionValues); + } catch (Exception $e) { + throw DbalException::wrap($e); + } } public function lockTable($tableName): void { - $this->inner->lockTable($tableName); + try { + $this->inner->lockTable($tableName); + } catch (Exception $e) { + throw DbalException::wrap($e); + } } public function unlockTable(): void { - $this->inner->unlockTable(); + try { + $this->inner->unlockTable(); + } catch (Exception $e) { + throw DbalException::wrap($e); + } } public function beginTransaction(): void { - $this->inner->beginTransaction(); + try { + $this->inner->beginTransaction(); + } catch (Exception $e) { + throw DbalException::wrap($e); + } } public function inTransaction(): bool { @@ -101,11 +147,19 @@ class ConnectionAdapter implements IDBConnection { } public function commit(): void { - $this->inner->commit(); + try { + $this->inner->commit(); + } catch (Exception $e) { + throw DbalException::wrap($e); + } } public function rollBack(): void { - $this->inner->rollBack(); + try { + $this->inner->rollBack(); + } catch (Exception $e) { + throw DbalException::wrap($e); + } } public function getError(): string { @@ -121,7 +175,11 @@ class ConnectionAdapter implements IDBConnection { } public function connect(): bool { - return $this->inner->connect(); + try { + return $this->inner->connect(); + } catch (Exception $e) { + throw DbalException::wrap($e); + } } public function close(): void { @@ -140,11 +198,19 @@ class ConnectionAdapter implements IDBConnection { } public function dropTable(string $table): void { - $this->inner->dropTable($table); + try { + $this->inner->dropTable($table); + } catch (Exception $e) { + throw DbalException::wrap($e); + } } public function tableExists(string $table): bool { - return $this->inner->tableExists($table); + try { + return $this->inner->tableExists($table); + } catch (Exception $e) { + throw DbalException::wrap($e); + } } public function escapeLikeParameter(string $param): string { @@ -159,11 +225,19 @@ class ConnectionAdapter implements IDBConnection { * @todo leaks a 3rdparty type */ public function createSchema(): Schema { - return $this->inner->createSchema(); + try { + return $this->inner->createSchema(); + } catch (Exception $e) { + throw DbalException::wrap($e); + } } public function migrateToSchema(Schema $toSchema): void { - $this->inner->migrateToSchema($toSchema); + try { + $this->inner->migrateToSchema($toSchema); + } catch (Exception $e) { + throw DbalException::wrap($e); + } } public function getInner(): Connection { diff --git a/lib/private/DB/Exceptions/DbalException.php b/lib/private/DB/Exceptions/DbalException.php new file mode 100644 index 00000000000..4e0e1517048 --- /dev/null +++ b/lib/private/DB/Exceptions/DbalException.php @@ -0,0 +1,136 @@ +<?php + +declare(strict_types=1); + +/* + * @copyright 2021 Christoph Wurst <christoph@winzerhof-wurst.at> + * + * @author 2021 Christoph Wurst <christoph@winzerhof-wurst.at> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +namespace OC\DB\Exceptions; + +use Doctrine\DBAL\ConnectionException; +use Doctrine\DBAL\Exception\ConstraintViolationException; +use Doctrine\DBAL\Exception\DatabaseObjectExistsException; +use Doctrine\DBAL\Exception\DatabaseObjectNotFoundException; +use Doctrine\DBAL\Exception\DeadlockException; +use Doctrine\DBAL\Exception\DriverException; +use Doctrine\DBAL\Exception\ForeignKeyConstraintViolationException; +use Doctrine\DBAL\Exception\InvalidArgumentException; +use Doctrine\DBAL\Exception\InvalidFieldNameException; +use Doctrine\DBAL\Exception\NonUniqueFieldNameException; +use Doctrine\DBAL\Exception\NotNullConstraintViolationException; +use Doctrine\DBAL\Exception\ServerException; +use Doctrine\DBAL\Exception\SyntaxErrorException; +use Doctrine\DBAL\Exception\UniqueConstraintViolationException; +use OCP\DB\Exception; + +/** + * Wrapper around the raw dbal exception, so we can pass it to apps that catch + * our OCP db exception + * + * @psalm-immutable + */ +class DbalException extends Exception { + + /** @var \Doctrine\DBAL\Exception */ + private $original; + + /** + * @param \Doctrine\DBAL\Exception $original + * @param int $code + * @param string $message + */ + private function __construct(\Doctrine\DBAL\Exception $original, int $code, string $message) { + parent::__construct( + $message, + $code, + $original + ); + $this->original = $original; + } + + public static function wrap(\Doctrine\DBAL\Exception $original, string $message = ''): self { + return new self( + $original, + is_int($original->getCode()) ? $original->getCode() : 0, + empty($message) ? $original->getMessage() : $message + ); + } + + public function getReason(): ?int { + /** + * Generic errors + */ + if ($this->original instanceof ConnectionException) { + return parent::REASON_CONNECTION_LOST; + } + if ($this->original instanceof DriverException) { + return parent::REASON_DRIVER; + } + if ($this->original instanceof InvalidArgumentException) { + return parent::REASON_INVALID_ARGUMENT; + } + + /** + * Constraint errors + */ + if ($this->original instanceof ForeignKeyConstraintViolationException) { + return parent::REASON_FOREIGN_KEY_VIOLATION; + } + if ($this->original instanceof NotNullConstraintViolationException) { + return parent::REASON_NOT_NULL_CONSTRAINT_VIOLATION; + } + if ($this->original instanceof UniqueConstraintViolationException) { + return parent::REASON_UNIQUE_CONSTRAINT_VIOLATION; + } + // The base exception comes last + if ($this->original instanceof ConstraintViolationException) { + return parent::REASON_CONSTRAINT_VIOLATION; + } + + /** + * Other server errors + */ + if ($this->original instanceof DatabaseObjectExistsException) { + return parent::REASON_DATABASE_OBJECT_EXISTS; + } + if ($this->original instanceof DatabaseObjectNotFoundException) { + return parent::REASON_DATABASE_OBJECT_NOT_FOUND; + } + if ($this->original instanceof DeadlockException) { + return parent::REASON_DEADLOCK; + } + if ($this->original instanceof InvalidFieldNameException) { + return parent::REASON_INVALID_FIELD_NAME; + } + if ($this->original instanceof NonUniqueFieldNameException) { + return parent::REASON_NON_UNIQUE_FIELD_NAME; + } + if ($this->original instanceof SyntaxErrorException) { + return parent::REASON_SYNTAX_ERROR; + } + // The base server exception class comes last + if ($this->original instanceof ServerException) { + return parent::REASON_SERVER; + } + + return null; + } +} diff --git a/lib/private/DB/Migrator.php b/lib/private/DB/Migrator.php index e50927f620b..dcf0db89f72 100644 --- a/lib/private/DB/Migrator.php +++ b/lib/private/DB/Migrator.php @@ -82,6 +82,8 @@ class Migrator { /** * @param \Doctrine\DBAL\Schema\Schema $targetSchema + * + * @throws Exception */ public function migrate(Schema $targetSchema) { $this->noEmit = true; @@ -171,6 +173,9 @@ class Migrator { return new Table($newName, $table->getColumns(), $newIndexes, [], [], $table->getOptions()); } + /** + * @throws Exception + */ public function createSchema() { $this->connection->getConfiguration()->setSchemaAssetsFilter(function ($asset) { /** @var string|AbstractAsset $asset */ @@ -231,6 +236,8 @@ class Migrator { /** * @param \Doctrine\DBAL\Schema\Schema $targetSchema * @param \Doctrine\DBAL\Connection $connection + * + * @throws Exception */ protected function applySchema(Schema $targetSchema, \Doctrine\DBAL\Connection $connection = null) { if (is_null($connection)) { diff --git a/lib/public/DB/Exception.php b/lib/public/DB/Exception.php new file mode 100644 index 00000000000..1154530e85d --- /dev/null +++ b/lib/public/DB/Exception.php @@ -0,0 +1,149 @@ +<?php + +declare(strict_types=1); + +/* + * @copyright 2021 Christoph Wurst <christoph@winzerhof-wurst.at> + * + * @author 2021 Christoph Wurst <christoph@winzerhof-wurst.at> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +namespace OCP\DB; + +use Exception as BaseException; + +/** + * Database exception + * + * Thrown by Nextcloud's database abstraction layer. This is the base class that + * any specific exception will extend. Use this class in your try-catch to catch + * *any* error related to the database. Use any of the subclasses in the same + * namespace if you are only interested in specific errors. + * + * @psalm-immutable + * @since 21.0.0 + */ +class Exception extends BaseException { + + /** + * Nextcloud lost connection to the database + * + * @since 21.0.0 + */ + public const REASON_CONNECTION_LOST = 1; + + /** + * A database constraint was violated + * + * @since 21.0.0 + */ + public const REASON_CONSTRAINT_VIOLATION = 2; + + /** + * A database object (table, column, index) already exists + * + * @since 21.0.0 + */ + public const REASON_DATABASE_OBJECT_EXISTS = 3; + + /** + * A database object (table, column, index) can't be found + * + * @since 21.0.0 + */ + public const REASON_DATABASE_OBJECT_NOT_FOUND = 4; + + /** + * The database ran into a deadlock + * + * @since 21.0.0 + */ + public const REASON_DEADLOCK = 5; + + /** + * The database driver encountered an issue + * + * @since 21.0.0 + */ + public const REASON_DRIVER = 6; + + /** + * A foreign key constraint was violated + * + * @since 21.0.0 + */ + public const REASON_FOREIGN_KEY_VIOLATION = 7; + + /** + * An invalid argument was passed to the database abstraction + * + * @since 21.0.0 + */ + public const REASON_INVALID_ARGUMENT = 8; + + /** + * A field name was invalid + * + * @since 21.0.0 + */ + public const REASON_INVALID_FIELD_NAME = 9; + + /** + * A name in the query was ambiguous + * + * @since 21.0.0 + */ + public const REASON_NON_UNIQUE_FIELD_NAME = 10; + + /** + * A not null contraint was violated + * + * @since 21.0.0 + */ + public const REASON_NOT_NULL_CONSTRAINT_VIOLATION = 11; + + /** + * A generic server error was encountered + * + * @since 21.0.0 + */ + public const REASON_SERVER = 12; + + /** + * A syntax error was reported by the server + * + * @since 21.0.0 + */ + public const REASON_SYNTAX_ERROR = 13; + + /** + * A unique constraint was violated + * + * @since 21.0.0 + */ + public const REASON_UNIQUE_CONSTRAINT_VIOLATION = 14; + + /** + * @return int|null + * @psalm-return Exception::REASON_* + * @since 21.0.0 + */ + public function getReason(): ?int { + return null; + } +} diff --git a/lib/public/DB/QueryBuilder/IQueryBuilder.php b/lib/public/DB/QueryBuilder/IQueryBuilder.php index 8fcbd6c3276..24de7b4ce60 100644 --- a/lib/public/DB/QueryBuilder/IQueryBuilder.php +++ b/lib/public/DB/QueryBuilder/IQueryBuilder.php @@ -29,6 +29,7 @@ namespace OCP\DB\QueryBuilder; use Doctrine\DBAL\Connection; +use OCP\DB\Exception; use OCP\DB\IResult; /** @@ -154,6 +155,7 @@ interface IQueryBuilder { * to bridge old code to the new API * * @return IResult|int + * @throws Exception since 21.0.0 * @since 8.2.0 */ public function execute(); diff --git a/lib/public/IDBConnection.php b/lib/public/IDBConnection.php index 16a5f998fde..5618e3ec40b 100644 --- a/lib/public/IDBConnection.php +++ b/lib/public/IDBConnection.php @@ -39,8 +39,8 @@ namespace OCP; -use Doctrine\DBAL\Exception; use Doctrine\DBAL\Schema\Schema; +use OCP\DB\Exception; use OCP\DB\IPreparedStatement; use OCP\DB\IResult; use OCP\DB\QueryBuilder\IQueryBuilder; @@ -103,6 +103,7 @@ interface IDBConnection { * @param array $types The parameter types. * @return int The number of affected rows. * @since 8.0.0 + * @throws Exception since 21.0.0 * * @deprecated 21.0.0 use executeStatement */ @@ -119,6 +120,7 @@ interface IDBConnection { * @param array $types The parameter types. * @return int The number of affected rows. * @since 21.0.0 + * @throws Exception since 21.0.0 */ public function executeStatement($sql, array $params = [], array $types = []): int; @@ -143,7 +145,7 @@ interface IDBConnection { * If this is null or an empty array, all keys of $input will be compared * Please note: text fields (clob) must not be used in the compare array * @return int number of inserted rows - * @throws Exception + * @throws Exception used to be the removed dbal exception, since 21.0.0 it's \OCP\DB\Exception * @since 6.0.0 - parameter $compare was added in 8.1.0, return type changed from boolean in 8.1.0 * @deprecated 15.0.0 - use unique index and "try { $db->insert() } catch (UniqueConstraintViolationException $e) {}" instead, because it is more reliable and does not have the risk for deadlocks - see https://github.com/nextcloud/server/pull/12371 */ @@ -171,7 +173,7 @@ interface IDBConnection { * @param array $values (column name => value) * @param array $updatePreconditionValues ensure values match preconditions (column name => value) * @return int number of new rows - * @throws Exception + * @throws Exception used to be the removed dbal exception, since 21.0.0 it's \OCP\DB\Exception * @throws PreconditionNotMetException * @since 9.0.0 */ @@ -185,6 +187,7 @@ interface IDBConnection { * transaction while holding a lock. * * @param string $tableName + * @throws Exception since 21.0.0 * @since 9.1.0 */ public function lockTable($tableName): void; @@ -192,6 +195,7 @@ interface IDBConnection { /** * Release a previous acquired lock again * + * @throws Exception since 21.0.0 * @since 9.1.0 */ public function unlockTable(): void; @@ -255,6 +259,7 @@ interface IDBConnection { * Establishes the connection with the database. * * @return bool + * @throws Exception since 21.0.0 * @since 8.0.0 */ public function connect(): bool; @@ -288,6 +293,7 @@ interface IDBConnection { * Drop a table from the database if it exists * * @param string $table table name without the prefix + * @throws Exception since 21.0.0 * @since 8.0.0 */ public function dropTable(string $table): void; @@ -297,6 +303,7 @@ interface IDBConnection { * * @param string $table table name without the prefix * @return bool + * @throws Exception since 21.0.0 * @since 8.0.0 */ public function tableExists(string $table): bool; @@ -322,6 +329,7 @@ interface IDBConnection { * Create the schema of the connected database * * @return Schema + * @throws Exception since 21.0.0 * @since 13.0.0 */ public function createSchema(): Schema; @@ -330,6 +338,7 @@ interface IDBConnection { * Migrate the database to the given schema * * @param Schema $toSchema + * @throws Exception since 21.0.0 * @since 13.0.0 */ public function migrateToSchema(Schema $toSchema): void; |