diff options
Diffstat (limited to 'lib/private')
44 files changed, 611 insertions, 475 deletions
diff --git a/lib/private/AppConfig.php b/lib/private/AppConfig.php index 84305e60a12..6a0dc2fab2b 100644 --- a/lib/private/AppConfig.php +++ b/lib/private/AppConfig.php @@ -33,10 +33,10 @@ namespace OC; +use OC\DB\Connection; use OC\DB\OracleConnection; use OCP\IAppConfig; use OCP\IConfig; -use OCP\IDBConnection; /** * This class provides an easy way for apps to store config values in the @@ -68,7 +68,7 @@ class AppConfig implements IAppConfig { ], ]; - /** @var \OCP\IDBConnection */ + /** @var Connection */ protected $conn; /** @var array[] */ @@ -78,11 +78,10 @@ class AppConfig implements IAppConfig { private $configLoaded = false; /** - * @param IDBConnection $conn + * @param Connection $conn */ - public function __construct(IDBConnection $conn) { + public function __construct(Connection $conn) { $this->conn = $conn; - $this->configLoaded = false; } /** diff --git a/lib/private/AppFramework/Http/Dispatcher.php b/lib/private/AppFramework/Http/Dispatcher.php index 47e6650c999..828864f1e02 100644 --- a/lib/private/AppFramework/Http/Dispatcher.php +++ b/lib/private/AppFramework/Http/Dispatcher.php @@ -36,12 +36,11 @@ namespace OC\AppFramework\Http; use OC\AppFramework\Http; use OC\AppFramework\Middleware\MiddlewareDispatcher; use OC\AppFramework\Utility\ControllerMethodReflector; -use OC\DB\Connection; +use OC\DB\ConnectionAdapter; use OCP\AppFramework\Controller; use OCP\AppFramework\Http\DataResponse; use OCP\AppFramework\Http\Response; use OCP\IConfig; -use OCP\IDBConnection; use OCP\IRequest; use Psr\Log\LoggerInterface; @@ -65,7 +64,7 @@ class Dispatcher { /** @var IConfig */ private $config; - /** @var IDBConnection|Connection */ + /** @var ConnectionAdapter */ private $connection; /** @var LoggerInterface */ @@ -79,7 +78,7 @@ class Dispatcher { * the arguments for the controller * @param IRequest $request the incoming request * @param IConfig $config - * @param IDBConnection $connection + * @param ConnectionAdapter $connection * @param LoggerInterface $logger */ public function __construct(Http $protocol, @@ -87,7 +86,7 @@ class Dispatcher { ControllerMethodReflector $reflector, IRequest $request, IConfig $config, - IDBConnection $connection, + ConnectionAdapter $connection, LoggerInterface $logger) { $this->protocol = $protocol; $this->middlewareDispatcher = $middlewareDispatcher; @@ -122,13 +121,13 @@ class Dispatcher { $databaseStatsBefore = []; if ($this->config->getSystemValueBool('debug', false)) { - $databaseStatsBefore = $this->connection->getStats(); + $databaseStatsBefore = $this->connection->getInner()->getStats(); } $response = $this->executeController($controller, $methodName); if (!empty($databaseStatsBefore)) { - $databaseStatsAfter = $this->connection->getStats(); + $databaseStatsAfter = $this->connection->getInner()->getStats(); $numBuilt = $databaseStatsAfter['built'] - $databaseStatsBefore['built']; $numExecuted = $databaseStatsAfter['executed'] - $databaseStatsBefore['executed']; diff --git a/lib/private/DB/Adapter.php b/lib/private/DB/Adapter.php index 26fd018dec1..49b831301be 100644 --- a/lib/private/DB/Adapter.php +++ b/lib/private/DB/Adapter.php @@ -52,7 +52,7 @@ class Adapter { * @return int id of last insert statement */ public function lastInsertId($table) { - return $this->conn->realLastInsertId($table); + return (int) $this->conn->realLastInsertId($table); } /** @@ -94,7 +94,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\DBALException + * @throws \Doctrine\DBAL\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) { diff --git a/lib/private/DB/AdapterPgSql.php b/lib/private/DB/AdapterPgSql.php index 77f0b6b7722..0d8794d59ac 100644 --- a/lib/private/DB/AdapterPgSql.php +++ b/lib/private/DB/AdapterPgSql.php @@ -31,7 +31,10 @@ class AdapterPgSql extends Adapter { protected $compatModePre9_5 = null; public function lastInsertId($table) { - return $this->conn->fetchColumn('SELECT lastval()'); + $result = $this->conn->executeQuery('SELECT lastval()'); + $val = $result->fetchOne(); + $result->free(); + return (int)$val; } public const UNIX_TIMESTAMP_REPLACEMENT = 'cast(extract(epoch from current_timestamp) as integer)'; @@ -62,7 +65,9 @@ class AdapterPgSql extends Adapter { return $this->compatModePre9_5; } - $version = $this->conn->fetchColumn('SHOW SERVER_VERSION'); + $result = $this->conn->executeQuery('SHOW SERVER_VERSION'); + $version = $result->fetchOne(); + $result->free(); $this->compatModePre9_5 = version_compare($version, '9.5', '<'); return $this->compatModePre9_5; diff --git a/lib/private/DB/AdapterSqlite.php b/lib/private/DB/AdapterSqlite.php index 5731ee1721a..a62960bae1a 100644 --- a/lib/private/DB/AdapterSqlite.php +++ b/lib/private/DB/AdapterSqlite.php @@ -62,7 +62,7 @@ class AdapterSqlite extends 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\DBALException + * @throws \Doctrine\DBAL\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) { diff --git a/lib/private/DB/Connection.php b/lib/private/DB/Connection.php index b024989ac04..c67c6df0826 100644 --- a/lib/private/DB/Connection.php +++ b/lib/private/DB/Connection.php @@ -1,4 +1,7 @@ <?php + +declare(strict_types=1); + /** * @copyright Copyright (c) 2016, ownCloud, Inc. * @@ -36,20 +39,21 @@ namespace OC\DB; use Doctrine\Common\EventManager; use Doctrine\DBAL\Cache\QueryCacheProfile; use Doctrine\DBAL\Configuration; -use Doctrine\DBAL\DBALException; use Doctrine\DBAL\Driver; +use Doctrine\DBAL\Exception; use Doctrine\DBAL\Exception\ConstraintViolationException; use Doctrine\DBAL\Exception\NotNullConstraintViolationException; -use Doctrine\DBAL\Platforms\MySqlPlatform; +use Doctrine\DBAL\Platforms\MySQLPlatform; +use Doctrine\DBAL\Result; use Doctrine\DBAL\Schema\Schema; +use Doctrine\DBAL\Statement; use OC\DB\QueryBuilder\QueryBuilder; use OC\SystemConfig; use OCP\DB\QueryBuilder\IQueryBuilder; -use OCP\IDBConnection; use OCP\ILogger; use OCP\PreConditionNotMetException; -class Connection extends ReconnectWrapper implements IDBConnection { +class Connection extends ReconnectWrapper { /** @var string */ protected $tablePrefix; @@ -73,9 +77,9 @@ class Connection extends ReconnectWrapper implements IDBConnection { public function connect() { try { return parent::connect(); - } catch (DBALException $e) { + } catch (Exception $e) { // throw a new exception to prevent leaking info from the stacktrace - throw new DBALException('Failed to connect to the database: ' . $e->getMessage(), $e->getCode()); + throw new Exception('Failed to connect to the database: ' . $e->getMessage(), $e->getCode()); } } @@ -88,13 +92,11 @@ class Connection extends ReconnectWrapper implements IDBConnection { /** * Returns a QueryBuilder for the connection. - * - * @return \OCP\DB\QueryBuilder\IQueryBuilder */ - public function getQueryBuilder() { + public function getQueryBuilder(): IQueryBuilder { $this->queriesBuilt++; return new QueryBuilder( - $this, + new ConnectionAdapter($this), $this->systemConfig, $this->logger ); @@ -181,9 +183,9 @@ class Connection extends ReconnectWrapper implements IDBConnection { * @param string $statement The SQL statement to prepare. * @param int $limit * @param int $offset - * @return \Doctrine\DBAL\Driver\Statement The prepared statement. + * @return Statement The prepared statement. */ - public function prepare($statement, $limit = null, $offset = null) { + public function prepare($statement, $limit = null, $offset = null): Statement { if ($limit === -1) { $limit = null; } @@ -208,18 +210,18 @@ class Connection extends ReconnectWrapper implements IDBConnection { * @param array $types The types the previous parameters are in. * @param \Doctrine\DBAL\Cache\QueryCacheProfile|null $qcp The query cache profile, optional. * - * @return \Doctrine\DBAL\Driver\Statement The executed statement. + * @return Result The executed statement. * - * @throws \Doctrine\DBAL\DBALException + * @throws \Doctrine\DBAL\Exception */ - public function executeQuery($sql, array $params = [], $types = [], QueryCacheProfile $qcp = null) { + public function executeQuery(string $sql, array $params = [], $types = [], QueryCacheProfile $qcp = null): Result { $sql = $this->replaceTablePrefix($sql); $sql = $this->adapter->fixupStatement($sql); $this->queriesExecuted++; return parent::executeQuery($sql, $params, $types, $qcp); } - public function executeUpdate($sql, array $params = [], array $types = []) { + public function executeUpdate(string $sql, array $params = [], array $types = []): int { $sql = $this->replaceTablePrefix($sql); $sql = $this->adapter->fixupStatement($sql); $this->queriesExecuted++; @@ -236,11 +238,11 @@ class Connection extends ReconnectWrapper implements IDBConnection { * @param array $params The query parameters. * @param array $types The parameter types. * - * @return integer The number of affected rows. + * @return int The number of affected rows. * - * @throws \Doctrine\DBAL\DBALException + * @throws \Doctrine\DBAL\Exception */ - public function executeStatement($sql, array $params = [], array $types = []) { + public function executeStatement($sql, array $params = [], array $types = []): int { $sql = $this->replaceTablePrefix($sql); $sql = $this->adapter->fixupStatement($sql); $this->queriesExecuted++; @@ -256,7 +258,7 @@ class Connection extends ReconnectWrapper implements IDBConnection { * columns or sequences. * * @param string $seqName Name of the sequence object from which the ID should be returned. - * @return string A string representation of the last inserted ID. + * @return string the last inserted ID. */ public function lastInsertId($seqName = null) { if ($seqName) { @@ -281,7 +283,7 @@ class Connection extends ReconnectWrapper implements 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 \Doctrine\DBAL\DBALException + * @throws \Doctrine\DBAL\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) { @@ -310,7 +312,7 @@ class Connection extends ReconnectWrapper implements IDBConnection { * @param array $values (column name => value) * @param array $updatePreconditionValues ensure values match preconditions (column name => value) * @return int number of new rows - * @throws \Doctrine\DBAL\DBALException + * @throws \Doctrine\DBAL\Exception * @throws PreConditionNotMetException */ public function setValues($table, array $keys, array $values, array $updatePreconditionValues = []) { @@ -393,7 +395,7 @@ class Connection extends ReconnectWrapper implements IDBConnection { public function getError() { $msg = $this->errorCode() . ': '; $errorInfo = $this->errorInfo(); - if (is_array($errorInfo)) { + if (!empty($errorInfo)) { $msg .= 'SQLSTATE = '.$errorInfo[0] . ', '; $msg .= 'Driver Code = '.$errorInfo[1] . ', '; $msg .= 'Driver Message = '.$errorInfo[2]; @@ -401,6 +403,14 @@ class Connection extends ReconnectWrapper implements IDBConnection { return $msg; } + public function errorCode() { + return -1; + } + + public function errorInfo() { + return []; + } + /** * Drop a table from the database if it exists * @@ -462,7 +472,7 @@ class Connection extends ReconnectWrapper implements IDBConnection { * @since 11.0.0 */ public function supports4ByteText() { - if (!$this->getDatabasePlatform() instanceof MySqlPlatform) { + if (!$this->getDatabasePlatform() instanceof MySQLPlatform) { return true; } return $this->getParams()['charset'] === 'utf8mb4'; diff --git a/lib/private/DB/ConnectionAdapter.php b/lib/private/DB/ConnectionAdapter.php new file mode 100644 index 00000000000..97a0b60044d --- /dev/null +++ b/lib/private/DB/ConnectionAdapter.php @@ -0,0 +1,172 @@ +<?php + +declare(strict_types=1); + +/* + * @copyright 2020 Christoph Wurst <christoph@winzerhof-wurst.at> + * + * @author 2020 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; + +use Doctrine\DBAL\Platforms\AbstractPlatform; +use Doctrine\DBAL\Schema\Schema; +use OCP\DB\IPreparedStatement; +use OCP\DB\IResult; +use OCP\DB\QueryBuilder\IQueryBuilder; +use OCP\IDBConnection; + +/** + * Adapts the public API to our internal DBAL connection wrapper + */ +class ConnectionAdapter implements IDBConnection { + + /** @var Connection */ + private $inner; + + public function __construct(Connection $inner) { + $this->inner = $inner; + } + + public function getQueryBuilder(): IQueryBuilder { + return $this->inner->getQueryBuilder(); + } + + public function prepare($sql, $limit = null, $offset = null): IPreparedStatement { + return new PreparedStatement( + $this->inner->prepare($sql, $limit, $offset) + ); + } + + public function executeQuery(string $sql, array $params = [], $types = []): IResult { + return new ResultAdapter( + $this->inner->executeQuery($sql, $params, $types) + ); + } + + public function executeUpdate(string $sql, array $params = [], array $types = []): int { + return $this->inner->executeUpdate($sql, $params, $types); + } + + public function executeStatement($sql, array $params = [], array $types = []): int { + return $this->inner->executeStatement($sql, $params, $types); + } + + public function lastInsertId(string $table): int { + return (int) $this->inner->lastInsertId($table); + } + + public function insertIfNotExist(string $table, array $input, array $compare = null) { + return $this->inner->insertIfNotExist($table, $input, $compare); + } + + public function insertIgnoreConflict(string $table, array $values): int { + return $this->inner->insertIgnoreConflict($table, $values); + } + + public function setValues($table, array $keys, array $values, array $updatePreconditionValues = []): int { + return $this->inner->setValues($table, $keys, $values, $updatePreconditionValues); + } + + public function lockTable($tableName): void { + $this->inner->lockTable($tableName); + } + + public function unlockTable(): void { + $this->inner->unlockTable(); + } + + public function beginTransaction(): void { + $this->inner->beginTransaction(); + } + + public function inTransaction(): bool { + return $this->inner->inTransaction(); + } + + public function commit(): void { + $this->inner->commit(); + } + + public function rollBack(): void { + $this->inner->rollBack(); + } + + public function getError(): string { + return $this->inner->getError(); + } + + public function errorCode() { + return $this->inner->errorCode(); + } + + public function errorInfo() { + return $this->inner->errorInfo(); + } + + public function connect(): bool { + return $this->inner->connect(); + } + + public function close(): void { + $this->inner->close(); + } + + public function quote($input, $type = IQueryBuilder::PARAM_STR) { + return $this->inner->quote($input, $type); + } + + /** + * @todo we are leaking a 3rdparty type here + */ + public function getDatabasePlatform(): AbstractPlatform { + return $this->inner->getDatabasePlatform(); + } + + public function dropTable(string $table): void { + $this->inner->dropTable($table); + } + + public function tableExists(string $table): bool { + return $this->inner->tableExists($table); + } + + public function escapeLikeParameter(string $param): string { + return $this->inner->escapeLikeParameter($param); + } + + public function supports4ByteText(): bool { + return $this->inner->supports4ByteText(); + } + + /** + * @todo leaks a 3rdparty type + */ + public function createSchema(): Schema { + return $this->inner->createSchema(); + } + + public function migrateToSchema(Schema $toSchema): void { + $this->inner->migrateToSchema($toSchema); + } + + public function getInner(): Connection { + return $this->inner; + } +} diff --git a/lib/private/DB/MDB2SchemaManager.php b/lib/private/DB/MDB2SchemaManager.php index 44ea3986214..18549eb1fa2 100644 --- a/lib/private/DB/MDB2SchemaManager.php +++ b/lib/private/DB/MDB2SchemaManager.php @@ -31,36 +31,24 @@ namespace OC\DB; -use Doctrine\DBAL\Platforms\MySqlPlatform; +use Doctrine\DBAL\Platforms\MySQLPlatform; use Doctrine\DBAL\Platforms\OraclePlatform; -use Doctrine\DBAL\Platforms\PostgreSqlPlatform; +use Doctrine\DBAL\Platforms\PostgreSQL94Platform; use Doctrine\DBAL\Platforms\SqlitePlatform; use Doctrine\DBAL\Schema\Schema; -use OCP\IDBConnection; class MDB2SchemaManager { - /** @var \OC\DB\Connection $conn */ + /** @var Connection $conn */ protected $conn; /** - * @param IDBConnection $conn + * @param Connection $conn */ public function __construct($conn) { $this->conn = $conn; } /** - * saves database scheme to xml file - * @param string $file name of file - * @return bool - * - * TODO: write more documentation - */ - public function getDbStructure($file) { - return \OC\DB\MDB2SchemaWriter::saveSchemaToFile($file, $this->conn); - } - - /** * Creates tables from XML file * @param string $file file to read structure from * @return bool @@ -86,9 +74,9 @@ class MDB2SchemaManager { return new SQLiteMigrator($this->conn, $random, $config, $dispatcher); } elseif ($platform instanceof OraclePlatform) { return new OracleMigrator($this->conn, $random, $config, $dispatcher); - } elseif ($platform instanceof MySqlPlatform) { + } elseif ($platform instanceof MySQLPlatform) { return new MySQLMigrator($this->conn, $random, $config, $dispatcher); - } elseif ($platform instanceof PostgreSqlPlatform) { + } elseif ($platform instanceof PostgreSQL94Platform) { return new PostgreSqlMigrator($this->conn, $random, $config, $dispatcher); } else { return new Migrator($this->conn, $random, $config, $dispatcher); diff --git a/lib/private/DB/MDB2SchemaWriter.php b/lib/private/DB/MDB2SchemaWriter.php deleted file mode 100644 index 2c1f6a5e545..00000000000 --- a/lib/private/DB/MDB2SchemaWriter.php +++ /dev/null @@ -1,181 +0,0 @@ -<?php -/** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Bart Visscher <bartv@thisnet.nl> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Jörn Friedrich Dreyer <jfd@butonic.de> - * @author Morris Jobke <hey@morrisjobke.de> - * @author tbelau666 <thomas.belau@gmx.de> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * 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, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * - */ - -namespace OC\DB; - -use Doctrine\DBAL\Schema\Column; -use Doctrine\DBAL\Schema\Index; - -class MDB2SchemaWriter { - - /** - * @param string $file - * @param \OC\DB\Connection $conn - * @return bool - */ - public static function saveSchemaToFile($file, \OC\DB\Connection $conn) { - $config = \OC::$server->getConfig(); - - $xml = new \SimpleXMLElement('<database/>'); - $xml->addChild('name', $config->getSystemValue('dbname', 'owncloud')); - $xml->addChild('create', 'true'); - $xml->addChild('overwrite', 'false'); - if ($config->getSystemValue('dbtype', 'sqlite') === 'mysql' && $config->getSystemValue('mysql.utf8mb4', false)) { - $xml->addChild('charset', 'utf8mb4'); - } else { - $xml->addChild('charset', 'utf8'); - } - - // FIX ME: bloody work around - if ($config->getSystemValue('dbtype', 'sqlite') === 'oci') { - $filterExpression = '/^"' . preg_quote($conn->getPrefix()) . '/'; - } else { - $filterExpression = '/^' . preg_quote($conn->getPrefix()) . '/'; - } - $conn->getConfiguration()->setFilterSchemaAssetsExpression($filterExpression); - - foreach ($conn->getSchemaManager()->listTables() as $table) { - self::saveTable($table, $xml->addChild('table')); - } - file_put_contents($file, $xml->asXML()); - return true; - } - - /** - * @param \Doctrine\DBAL\Schema\Table $table - * @param \SimpleXMLElement $xml - */ - private static function saveTable($table, $xml) { - $xml->addChild('name', $table->getName()); - $declaration = $xml->addChild('declaration'); - foreach ($table->getColumns() as $column) { - self::saveColumn($column, $declaration->addChild('field')); - } - foreach ($table->getIndexes() as $index) { - if ($index->getName() == 'PRIMARY') { - $autoincrement = false; - foreach ($index->getColumns() as $column) { - if ($table->getColumn($column)->getAutoincrement()) { - $autoincrement = true; - } - } - if ($autoincrement) { - continue; - } - } - self::saveIndex($index, $declaration->addChild('index')); - } - } - - /** - * @param Column $column - * @param \SimpleXMLElement $xml - */ - private static function saveColumn($column, $xml) { - $xml->addChild('name', $column->getName()); - switch ($column->getType()) { - case 'SmallInt': - case 'Integer': - case 'BigInt': - $xml->addChild('type', 'integer'); - $default = $column->getDefault(); - if (is_null($default) && $column->getAutoincrement()) { - $default = '0'; - } - $xml->addChild('default', $default); - $xml->addChild('notnull', self::toBool($column->getNotnull())); - if ($column->getAutoincrement()) { - $xml->addChild('autoincrement', '1'); - } - if ($column->getUnsigned()) { - $xml->addChild('unsigned', 'true'); - } - $length = '4'; - if ($column->getType() == 'SmallInt') { - $length = '2'; - } elseif ($column->getType() == 'BigInt') { - $length = '8'; - } - $xml->addChild('length', $length); - break; - case 'String': - $xml->addChild('type', 'text'); - $default = trim($column->getDefault()); - if ($default === '') { - $default = false; - } - $xml->addChild('default', $default); - $xml->addChild('notnull', self::toBool($column->getNotnull())); - $xml->addChild('length', $column->getLength()); - break; - case 'Text': - $xml->addChild('type', 'clob'); - $xml->addChild('notnull', self::toBool($column->getNotnull())); - break; - case 'Decimal': - $xml->addChild('type', 'decimal'); - $xml->addChild('default', $column->getDefault()); - $xml->addChild('notnull', self::toBool($column->getNotnull())); - $xml->addChild('length', '15'); - break; - case 'Boolean': - $xml->addChild('type', 'integer'); - $xml->addChild('default', $column->getDefault()); - $xml->addChild('notnull', self::toBool($column->getNotnull())); - $xml->addChild('length', '1'); - break; - case 'DateTime': - $xml->addChild('type', 'timestamp'); - $xml->addChild('default', $column->getDefault()); - $xml->addChild('notnull', self::toBool($column->getNotnull())); - break; - - } - } - - /** - * @param Index $index - * @param \SimpleXMLElement $xml - */ - private static function saveIndex($index, $xml) { - $xml->addChild('name', $index->getName()); - if ($index->isPrimary()) { - $xml->addChild('primary', 'true'); - } elseif ($index->isUnique()) { - $xml->addChild('unique', 'true'); - } - foreach ($index->getColumns() as $column) { - $field = $xml->addChild('field'); - $field->addChild('name', $column); - $field->addChild('sorting', 'ascending'); - } - } - - private static function toBool($bool) { - return $bool ? 'true' : 'false'; - } -} diff --git a/lib/private/DB/MigrationService.php b/lib/private/DB/MigrationService.php index 6a1cb4be016..44707911092 100644 --- a/lib/private/DB/MigrationService.php +++ b/lib/private/DB/MigrationService.php @@ -30,7 +30,7 @@ namespace OC\DB; use Doctrine\DBAL\Exception\DriverException; use Doctrine\DBAL\Platforms\OraclePlatform; -use Doctrine\DBAL\Platforms\PostgreSqlPlatform; +use Doctrine\DBAL\Platforms\PostgreSQL94Platform; use Doctrine\DBAL\Schema\Index; use Doctrine\DBAL\Schema\Schema; use Doctrine\DBAL\Schema\SchemaException; @@ -42,7 +42,6 @@ use OC\IntegrityCheck\Helpers\AppLocator; use OC\Migration\SimpleOutput; use OCP\AppFramework\App; use OCP\AppFramework\QueryException; -use OCP\IDBConnection; use OCP\Migration\IMigrationStep; use OCP\Migration\IOutput; @@ -65,12 +64,12 @@ class MigrationService { * MigrationService constructor. * * @param $appName - * @param IDBConnection $connection + * @param Connection $connection * @param AppLocator $appLocator * @param IOutput|null $output * @throws \Exception */ - public function __construct($appName, IDBConnection $connection, IOutput $output = null, AppLocator $appLocator = null) { + public function __construct($appName, Connection $connection, IOutput $output = null, AppLocator $appLocator = null) { $this->appName = $appName; $this->connection = $connection; $this->output = $output; @@ -591,7 +590,7 @@ class MigrationService { $indexName = strtolower($primaryKey->getName()); $isUsingDefaultName = $indexName === 'primary'; - if ($this->connection->getDatabasePlatform() instanceof PostgreSqlPlatform) { + if ($this->connection->getDatabasePlatform() instanceof PostgreSQL94Platform) { $defaultName = $table->getName() . '_pkey'; $isUsingDefaultName = strtolower($defaultName) === $indexName; diff --git a/lib/private/DB/Migrator.php b/lib/private/DB/Migrator.php index 569b1a6c282..f62735ea6b2 100644 --- a/lib/private/DB/Migrator.php +++ b/lib/private/DB/Migrator.php @@ -31,7 +31,8 @@ namespace OC\DB; -use Doctrine\DBAL\DBALException; +use Doctrine\DBAL\Exception; +use Doctrine\DBAL\Schema\AbstractAsset; use Doctrine\DBAL\Schema\Comparator; use Doctrine\DBAL\Schema\Index; use Doctrine\DBAL\Schema\Schema; @@ -43,6 +44,7 @@ use OCP\IConfig; use OCP\Security\ISecureRandom; use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\EventDispatcher\GenericEvent; +use function preg_match; class Migrator { @@ -102,34 +104,6 @@ class Migrator { } /** - * @param Schema $targetSchema - * @throws \OC\DB\MigrationException - */ - public function checkMigrate(Schema $targetSchema) { - $this->noEmit = true; - /**@var \Doctrine\DBAL\Schema\Table[] $tables */ - $tables = $targetSchema->getTables(); - $filterExpression = $this->getFilterExpression(); - $this->connection->getConfiguration()-> - setFilterSchemaAssetsExpression($filterExpression); - $existingTables = $this->connection->getSchemaManager()->listTableNames(); - - $step = 0; - foreach ($tables as $table) { - if (strpos($table->getName(), '.')) { - list(, $tableName) = explode('.', $table->getName()); - } else { - $tableName = $table->getName(); - } - $this->emitCheckStep($tableName, $step++, count($tables)); - // don't need to check for new tables - if (array_search($tableName, $existingTables) !== false) { - $this->checkTableMigrate($table); - } - } - } - - /** * Create a unique name for the temporary table * * @param string $name @@ -160,7 +134,7 @@ class Migrator { try { $this->applySchema($schema); $this->dropTable($tmpName); - } catch (DBALException $e) { + } catch (Exception $e) { // pgsql needs to commit it's failed transaction before doing anything else if ($this->connection->isTransactionActive()) { $this->connection->commit(); @@ -193,12 +167,18 @@ class Migrator { } // foreign keys are not supported so we just set it to an empty array - return new Table($newName, $table->getColumns(), $newIndexes, [], 0, $table->getOptions()); + return new Table($newName, $table->getColumns(), $newIndexes, [], [], $table->getOptions()); } public function createSchema() { - $filterExpression = $this->getFilterExpression(); - $this->connection->getConfiguration()->setFilterSchemaAssetsExpression($filterExpression); + $this->connection->getConfiguration()->setSchemaAssetsFilter(function ($asset) { + /** @var string|AbstractAsset $asset */ + $filterExpression = $this->getFilterExpression(); + if ($asset instanceof AbstractAsset) { + return preg_match($filterExpression, $asset->getName()) !== false; + } + return preg_match($filterExpression, $asset) !== false; + }); return $this->connection->getSchemaManager()->createSchema(); } @@ -206,7 +186,6 @@ class Migrator { * @param Schema $targetSchema * @param \Doctrine\DBAL\Connection $connection * @return \Doctrine\DBAL\Schema\SchemaDiff - * @throws DBALException */ protected function getDiff(Schema $targetSchema, \Doctrine\DBAL\Connection $connection) { // adjust varchar columns with a length higher then getVarcharMaxLength to clob @@ -221,8 +200,14 @@ class Migrator { } } - $filterExpression = $this->getFilterExpression(); - $this->connection->getConfiguration()->setFilterSchemaAssetsExpression($filterExpression); + $this->connection->getConfiguration()->setSchemaAssetsFilter(function ($asset) { + /** @var string|AbstractAsset $asset */ + $filterExpression = $this->getFilterExpression(); + if ($asset instanceof AbstractAsset) { + return preg_match($filterExpression, $asset->getName()) !== false; + } + return preg_match($filterExpression, $asset) !== false; + }); $sourceSchema = $connection->getSchemaManager()->createSchema(); // remove tables we don't know about diff --git a/lib/private/DB/MySqlTools.php b/lib/private/DB/MySqlTools.php index 007388d5615..e738a2bd94a 100644 --- a/lib/private/DB/MySqlTools.php +++ b/lib/private/DB/MySqlTools.php @@ -32,7 +32,7 @@ use OCP\IDBConnection; class MySqlTools { /** - * @param Connection $connection + * @param IDBConnection $connection * @return bool */ public function supports4ByteCharset(IDBConnection $connection) { diff --git a/lib/private/DB/OracleMigrator.php b/lib/private/DB/OracleMigrator.php index cad5390f092..7a327a42797 100644 --- a/lib/private/DB/OracleMigrator.php +++ b/lib/private/DB/OracleMigrator.php @@ -30,7 +30,7 @@ namespace OC\DB; -use Doctrine\DBAL\DBALException; +use Doctrine\DBAL\Exception; use Doctrine\DBAL\Schema\Column; use Doctrine\DBAL\Schema\ColumnDiff; use Doctrine\DBAL\Schema\ForeignKeyConstraint; @@ -113,7 +113,7 @@ class OracleMigrator extends Migrator { * @param Schema $targetSchema * @param \Doctrine\DBAL\Connection $connection * @return \Doctrine\DBAL\Schema\SchemaDiff - * @throws DBALException + * @throws Exception */ protected function getDiff(Schema $targetSchema, \Doctrine\DBAL\Connection $connection) { $schemaDiff = parent::getDiff($targetSchema, $connection); @@ -128,10 +128,10 @@ class OracleMigrator extends Migrator { array_map(function (Index $index) { return $this->quoteIndex($index); }, $table->getIndexes()), + [], array_map(function (ForeignKeyConstraint $fck) { return $this->quoteForeignKeyConstraint($fck); }, $table->getForeignKeys()), - 0, $table->getOptions() ); }, $schemaDiff->newTables); @@ -141,8 +141,8 @@ class OracleMigrator extends Migrator { $this->connection->quoteIdentifier($table->getName()), $table->getColumns(), $table->getIndexes(), + [], $table->getForeignKeys(), - 0, $table->getOptions() ); }, $schemaDiff->removedTables); diff --git a/lib/private/DB/PgSqlTools.php b/lib/private/DB/PgSqlTools.php index 724344ac2a1..d555bfb391b 100644 --- a/lib/private/DB/PgSqlTools.php +++ b/lib/private/DB/PgSqlTools.php @@ -26,7 +26,10 @@ namespace OC\DB; +use Doctrine\DBAL\Schema\AbstractAsset; use OCP\IConfig; +use function preg_match; +use function preg_quote; /** * Various PostgreSQL specific helper functions. @@ -50,20 +53,30 @@ class PgSqlTools { * @return null */ public function resynchronizeDatabaseSequences(Connection $conn) { - $filterExpression = '/^' . preg_quote($this->config->getSystemValue('dbtableprefix', 'oc_')) . '/'; $databaseName = $conn->getDatabase(); - $conn->getConfiguration()->setFilterSchemaAssetsExpression($filterExpression); + $conn->getConfiguration()->setSchemaAssetsFilter(function ($asset) { + /** @var string|AbstractAsset $asset */ + $filterExpression = '/^' . preg_quote($this->config->getSystemValue('dbtableprefix', 'oc_')) . '/'; + if ($asset instanceof AbstractAsset) { + return preg_match($filterExpression, $asset->getName()) !== false; + } + return preg_match($filterExpression, $asset) !== false; + }); foreach ($conn->getSchemaManager()->listSequences() as $sequence) { $sequenceName = $sequence->getName(); $sqlInfo = 'SELECT table_schema, table_name, column_name FROM information_schema.columns WHERE column_default = ? AND table_catalog = ?'; - $sequenceInfo = $conn->fetchAssoc($sqlInfo, [ + $result = $conn->executeQuery($sqlInfo, [ "nextval('$sequenceName'::regclass)", $databaseName ]); + $sequenceInfo = $result->fetchAssociative(); + $result->free(); + /** @var string $tableName */ $tableName = $sequenceInfo['table_name']; + /** @var string $columnName */ $columnName = $sequenceInfo['column_name']; $sqlMaxId = "SELECT MAX($columnName) FROM $tableName"; $sqlSetval = "SELECT setval('$sequenceName', ($sqlMaxId))"; diff --git a/lib/private/DB/PreparedStatement.php b/lib/private/DB/PreparedStatement.php new file mode 100644 index 00000000000..f679576a428 --- /dev/null +++ b/lib/private/DB/PreparedStatement.php @@ -0,0 +1,101 @@ +<?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; + +use Doctrine\DBAL\Exception; +use Doctrine\DBAL\ParameterType; +use Doctrine\DBAL\Statement; +use OCP\DB\IPreparedStatement; +use OCP\DB\IResult; +use PDO; + +/** + * Adapts our public API to what doctrine/dbal exposed with 2.6 + * + * The old dbal statement had stateful methods e.g. to fetch data from an executed + * prepared statement. To provide backwards compatibility to apps we need to make + * this class stateful. As soon as those now deprecated exposed methods are gone, + * we can limit the API of this adapter to the methods that map to the direct dbal + * methods without much magic. + */ +class PreparedStatement implements IPreparedStatement { + + /** @var Statement */ + private $statement; + + /** @var IResult|null */ + private $result; + + public function __construct(Statement $statement) { + $this->statement = $statement; + } + + public function closeCursor(): bool { + $this->getResult()->closeCursor(); + + return true; + } + + public function fetch(int $fetchMode = PDO::FETCH_ASSOC) { + return $this->getResult()->fetch($fetchMode); + } + + public function fetchAll(int $fetchMode = PDO::FETCH_ASSOC): array { + return $this->getResult()->fetchAll($fetchMode); + } + + public function fetchColumn() { + return $this->getResult()->fetchOne(); + } + + public function fetchOne() { + return $this->getResult()->fetchOne(); + } + + public function bindValue($param, $value, $type = ParameterType::STRING): bool { + return $this->statement->bindValue($param, $value, $type); + } + + public function bindParam($param, &$variable, $type = ParameterType::STRING, $length = null): bool { + return $this->statement->bindParam($param, $variable, $type, $length); + } + + public function execute($params = null): IResult { + return ($this->result = new ResultAdapter($this->statement->execute($params))); + } + + public function rowCount(): int { + return $this->getResult()->rowCount(); + } + + private function getResult(): IResult { + if ($this->result !== null) { + return $this->result; + } + + throw new Exception("You have to execute the prepared statement before accessing the results"); + } +} diff --git a/lib/private/DB/QueryBuilder/ExpressionBuilder/ExpressionBuilder.php b/lib/private/DB/QueryBuilder/ExpressionBuilder/ExpressionBuilder.php index 61eea80640e..d4c1a9db881 100644 --- a/lib/private/DB/QueryBuilder/ExpressionBuilder/ExpressionBuilder.php +++ b/lib/private/DB/QueryBuilder/ExpressionBuilder/ExpressionBuilder.php @@ -27,6 +27,7 @@ namespace OC\DB\QueryBuilder\ExpressionBuilder; use Doctrine\DBAL\Query\Expression\ExpressionBuilder as DoctrineExpressionBuilder; +use OC\DB\ConnectionAdapter; use OC\DB\QueryBuilder\CompositeExpression; use OC\DB\QueryBuilder\FunctionBuilder\FunctionBuilder; use OC\DB\QueryBuilder\Literal; @@ -55,13 +56,13 @@ class ExpressionBuilder implements IExpressionBuilder { /** * Initializes a new <tt>ExpressionBuilder</tt>. * - * @param IDBConnection $connection + * @param ConnectionAdapter $connection * @param IQueryBuilder $queryBuilder */ - public function __construct(IDBConnection $connection, IQueryBuilder $queryBuilder) { + public function __construct(ConnectionAdapter $connection, IQueryBuilder $queryBuilder) { $this->connection = $connection; $this->helper = new QuoteHelper(); - $this->expressionBuilder = new DoctrineExpressionBuilder($connection); + $this->expressionBuilder = new DoctrineExpressionBuilder($connection->getInner()); $this->functionBuilder = $queryBuilder->func(); } diff --git a/lib/private/DB/QueryBuilder/ExpressionBuilder/MySqlExpressionBuilder.php b/lib/private/DB/QueryBuilder/ExpressionBuilder/MySqlExpressionBuilder.php index 899f9277439..3e4119bb11c 100644 --- a/lib/private/DB/QueryBuilder/ExpressionBuilder/MySqlExpressionBuilder.php +++ b/lib/private/DB/QueryBuilder/ExpressionBuilder/MySqlExpressionBuilder.php @@ -24,9 +24,8 @@ namespace OC\DB\QueryBuilder\ExpressionBuilder; -use OC\DB\Connection; +use OC\DB\ConnectionAdapter; use OCP\DB\QueryBuilder\IQueryBuilder; -use OCP\IDBConnection; class MySqlExpressionBuilder extends ExpressionBuilder { @@ -34,13 +33,13 @@ class MySqlExpressionBuilder extends ExpressionBuilder { protected $charset; /** - * @param \OCP\IDBConnection|Connection $connection + * @param ConnectionAdapter $connection * @param IQueryBuilder $queryBuilder */ - public function __construct(IDBConnection $connection, IQueryBuilder $queryBuilder) { + public function __construct(ConnectionAdapter $connection, IQueryBuilder $queryBuilder) { parent::__construct($connection, $queryBuilder); - $params = $connection->getParams(); + $params = $connection->getInner()->getParams(); $this->charset = isset($params['charset']) ? $params['charset'] : 'utf8'; } diff --git a/lib/private/DB/QueryBuilder/QueryBuilder.php b/lib/private/DB/QueryBuilder/QueryBuilder.php index 2d5fe6ce575..657e52e54bc 100644 --- a/lib/private/DB/QueryBuilder/QueryBuilder.php +++ b/lib/private/DB/QueryBuilder/QueryBuilder.php @@ -30,11 +30,12 @@ namespace OC\DB\QueryBuilder; -use Doctrine\DBAL\Platforms\MySqlPlatform; -use Doctrine\DBAL\Platforms\PostgreSqlPlatform; +use Doctrine\DBAL\Platforms\MySQLPlatform; +use Doctrine\DBAL\Platforms\OraclePlatform; +use Doctrine\DBAL\Platforms\PostgreSQL94Platform; use Doctrine\DBAL\Platforms\SqlitePlatform; use Doctrine\DBAL\Query\QueryException; -use OC\DB\OracleConnection; +use OC\DB\ConnectionAdapter; use OC\DB\QueryBuilder\ExpressionBuilder\ExpressionBuilder; use OC\DB\QueryBuilder\ExpressionBuilder\MySqlExpressionBuilder; use OC\DB\QueryBuilder\ExpressionBuilder\OCIExpressionBuilder; @@ -44,17 +45,18 @@ use OC\DB\QueryBuilder\FunctionBuilder\FunctionBuilder; use OC\DB\QueryBuilder\FunctionBuilder\OCIFunctionBuilder; use OC\DB\QueryBuilder\FunctionBuilder\PgSqlFunctionBuilder; use OC\DB\QueryBuilder\FunctionBuilder\SqliteFunctionBuilder; +use OC\DB\ResultAdapter; use OC\SystemConfig; +use OCP\DB\IResult; use OCP\DB\QueryBuilder\ILiteral; use OCP\DB\QueryBuilder\IParameter; use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\DB\QueryBuilder\IQueryFunction; -use OCP\IDBConnection; use OCP\ILogger; class QueryBuilder implements IQueryBuilder { - /** @var \OCP\IDBConnection */ + /** @var ConnectionAdapter */ private $connection; /** @var SystemConfig */ @@ -78,15 +80,15 @@ class QueryBuilder implements IQueryBuilder { /** * Initializes a new QueryBuilder. * - * @param IDBConnection $connection + * @param ConnectionAdapter $connection * @param SystemConfig $systemConfig * @param ILogger $logger */ - public function __construct(IDBConnection $connection, SystemConfig $systemConfig, ILogger $logger) { + public function __construct(ConnectionAdapter $connection, SystemConfig $systemConfig, ILogger $logger) { $this->connection = $connection; $this->systemConfig = $systemConfig; $this->logger = $logger; - $this->queryBuilder = new \Doctrine\DBAL\Query\QueryBuilder($this->connection); + $this->queryBuilder = new \Doctrine\DBAL\Query\QueryBuilder($this->connection->getInner()); $this->helper = new QuoteHelper(); } @@ -118,17 +120,20 @@ class QueryBuilder implements IQueryBuilder { * @return \OCP\DB\QueryBuilder\IExpressionBuilder */ public function expr() { - if ($this->connection instanceof OracleConnection) { + if ($this->connection->getDatabasePlatform() instanceof OraclePlatform) { return new OCIExpressionBuilder($this->connection, $this); - } elseif ($this->connection->getDatabasePlatform() instanceof PostgreSqlPlatform) { + } + if ($this->connection->getDatabasePlatform() instanceof PostgreSQL94Platform) { return new PgSqlExpressionBuilder($this->connection, $this); - } elseif ($this->connection->getDatabasePlatform() instanceof MySqlPlatform) { + } + if ($this->connection->getDatabasePlatform() instanceof MySQLPlatform) { return new MySqlExpressionBuilder($this->connection, $this); - } elseif ($this->connection->getDatabasePlatform() instanceof SqlitePlatform) { + } + if ($this->connection->getDatabasePlatform() instanceof SqlitePlatform) { return new SqliteExpressionBuilder($this->connection, $this); - } else { - return new ExpressionBuilder($this->connection, $this); } + + return new ExpressionBuilder($this->connection, $this); } /** @@ -148,15 +153,17 @@ class QueryBuilder implements IQueryBuilder { * @return \OCP\DB\QueryBuilder\IFunctionBuilder */ public function func() { - if ($this->connection instanceof OracleConnection) { + if ($this->connection->getDatabasePlatform() instanceof OraclePlatform) { return new OCIFunctionBuilder($this->helper); - } elseif ($this->connection->getDatabasePlatform() instanceof SqlitePlatform) { + } + if ($this->connection->getDatabasePlatform() instanceof SqlitePlatform) { return new SqliteFunctionBuilder($this->helper); - } elseif ($this->connection->getDatabasePlatform() instanceof PostgreSqlPlatform) { + } + if ($this->connection->getDatabasePlatform() instanceof PostgreSQL94Platform) { return new PgSqlFunctionBuilder($this->helper); - } else { - return new FunctionBuilder($this->helper); } + + return new FunctionBuilder($this->helper); } /** @@ -192,7 +199,7 @@ class QueryBuilder implements IQueryBuilder { * Uses {@see Connection::executeQuery} for select statements and {@see Connection::executeUpdate} * for insert, update and delete statements. * - * @return \Doctrine\DBAL\Driver\Statement|int + * @return IResult|int */ public function execute() { if ($this->systemConfig->getValue('log_query', false)) { @@ -246,7 +253,11 @@ class QueryBuilder implements IQueryBuilder { } } - return $this->queryBuilder->execute(); + $result = $this->queryBuilder->execute(); + if (is_int($result)) { + return $result; + } + return new ResultAdapter($result); } /** diff --git a/lib/private/DB/ReconnectWrapper.php b/lib/private/DB/ReconnectWrapper.php index 9599d6b0fe6..a0170152862 100644 --- a/lib/private/DB/ReconnectWrapper.php +++ b/lib/private/DB/ReconnectWrapper.php @@ -44,12 +44,12 @@ class ReconnectWrapper extends \Doctrine\DBAL\Connection { if ($this->lastConnectionCheck > $checkTime || $this->isTransactionActive()) { return parent::connect(); - } else { - $this->lastConnectionCheck = $now; - if (!$this->ping()) { - $this->close(); - } - return parent::connect(); } + + $this->lastConnectionCheck = $now; + if (!$this->isConnected()) { + $this->close(); + } + return parent::connect(); } } diff --git a/lib/private/DB/ResultAdapter.php b/lib/private/DB/ResultAdapter.php new file mode 100644 index 00000000000..176de5b45ea --- /dev/null +++ b/lib/private/DB/ResultAdapter.php @@ -0,0 +1,71 @@ +<?php +/* + * @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/>. + */ + +declare(strict_types=1); + +namespace OC\DB; + +use Doctrine\DBAL\Result; +use OCP\DB\IResult; +use PDO; + +/** + * Adapts DBAL 2.6 API for DBAL 3.x for backwards compatibility of a leaked type + */ +class ResultAdapter implements IResult { + + /** @var Result */ + private $inner; + + public function __construct(Result $inner) { + $this->inner = $inner; + } + + public function closeCursor(): bool { + $this->inner->free(); + + return true; + } + + public function fetch(int $fetchMode = PDO::FETCH_ASSOC) { + return $this->inner->fetch($fetchMode); + } + + public function fetchAll(int $fetchMode = PDO::FETCH_ASSOC): array { + if ($fetchMode !== PDO::FETCH_ASSOC && $fetchMode !== PDO::FETCH_NUM && $fetchMode !== PDO::FETCH_COLUMN) { + throw new \Exception('Fetch mode needs to be assoc, num or column.'); + } + return $this->inner->fetchAll($fetchMode); + } + + public function fetchColumn($columnIndex = 0) { + return $this->inner->fetchOne(); + } + + public function fetchOne() { + return $this->inner->fetchOne(); + } + + public function rowCount(): int { + return $this->inner->rowCount(); + } +} diff --git a/lib/private/DB/SQLiteMigrator.php b/lib/private/DB/SQLiteMigrator.php index 16f18be135e..24b6c02b31c 100644 --- a/lib/private/DB/SQLiteMigrator.php +++ b/lib/private/DB/SQLiteMigrator.php @@ -26,7 +26,6 @@ namespace OC\DB; -use Doctrine\DBAL\DBALException; use Doctrine\DBAL\Schema\Schema; use Doctrine\DBAL\Types\BigIntType; use Doctrine\DBAL\Types\Type; @@ -34,42 +33,6 @@ use Doctrine\DBAL\Types\Type; class SQLiteMigrator extends Migrator { /** - * @param \Doctrine\DBAL\Schema\Schema $targetSchema - * @throws \OC\DB\MigrationException - * - * For sqlite we simple make a copy of the entire database, and test the migration on that - */ - public function checkMigrate(\Doctrine\DBAL\Schema\Schema $targetSchema) { - $dbFile = $this->connection->getDatabase(); - $tmpFile = $this->buildTempDatabase(); - copy($dbFile, $tmpFile); - - $connectionParams = [ - 'path' => $tmpFile, - 'driver' => 'pdo_sqlite', - ]; - $conn = \Doctrine\DBAL\DriverManager::getConnection($connectionParams); - try { - $this->applySchema($targetSchema, $conn); - $conn->close(); - unlink($tmpFile); - } catch (DBALException $e) { - $conn->close(); - unlink($tmpFile); - throw new MigrationException('', $e->getMessage()); - } - } - - /** - * @return string - */ - private function buildTempDatabase() { - $dataDir = $this->config->getSystemValue("datadirectory", \OC::$SERVERROOT . '/data'); - $tmpFile = uniqid("oc_"); - return "$dataDir/$tmpFile.db"; - } - - /** * @param Schema $targetSchema * @param \Doctrine\DBAL\Connection $connection * @return \Doctrine\DBAL\Schema\SchemaDiff diff --git a/lib/private/DB/SQLiteSessionInit.php b/lib/private/DB/SQLiteSessionInit.php index 0c53a0587f0..924a3b2758c 100644 --- a/lib/private/DB/SQLiteSessionInit.php +++ b/lib/private/DB/SQLiteSessionInit.php @@ -60,8 +60,9 @@ class SQLiteSessionInit implements EventSubscriber { $sensitive = $this->caseSensitiveLike ? 'true' : 'false'; $args->getConnection()->executeUpdate('PRAGMA case_sensitive_like = ' . $sensitive); $args->getConnection()->executeUpdate('PRAGMA journal_mode = ' . $this->journalMode); - /** @var \PDO $pdo */ - $pdo = $args->getConnection()->getWrappedConnection(); + /** @var \Doctrine\DBAL\Driver\PDO\Connection $connection */ + $connection = $args->getConnection()->getWrappedConnection(); + $pdo = $connection->getWrappedConnection(); $pdo->sqliteCreateFunction('md5', 'md5', 1); } diff --git a/lib/private/DB/SchemaWrapper.php b/lib/private/DB/SchemaWrapper.php index 440008d35b3..20ae5b6faa6 100644 --- a/lib/private/DB/SchemaWrapper.php +++ b/lib/private/DB/SchemaWrapper.php @@ -26,11 +26,10 @@ namespace OC\DB; use Doctrine\DBAL\Schema\Schema; use OCP\DB\ISchemaWrapper; -use OCP\IDBConnection; class SchemaWrapper implements ISchemaWrapper { - /** @var IDBConnection|Connection */ + /** @var Connection */ protected $connection; /** @var Schema */ @@ -39,10 +38,7 @@ class SchemaWrapper implements ISchemaWrapper { /** @var array */ protected $tablesToDelete = []; - /** - * @param IDBConnection $connection - */ - public function __construct(IDBConnection $connection) { + public function __construct(Connection $connection) { $this->connection = $connection; $this->schema = $this->connection->createSchema(); } diff --git a/lib/private/Files/Cache/Cache.php b/lib/private/Files/Cache/Cache.php index dbe0dd9d778..2513abd525f 100644 --- a/lib/private/Files/Cache/Cache.php +++ b/lib/private/Files/Cache/Cache.php @@ -39,8 +39,8 @@ namespace OC\Files\Cache; -use Doctrine\DBAL\Driver\Statement; use Doctrine\DBAL\Exception\UniqueConstraintViolationException; +use OCP\DB\IResult; use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\EventDispatcher\IEventDispatcher; use OCP\Files\Cache\CacheEntryInsertedEvent; @@ -486,7 +486,7 @@ class Cache implements ICache { ->wherePath($file); $result = $query->execute(); - $id = $result->fetchColumn(); + $id = $result->fetchOne(); $result->closeCursor(); return $id === false ? -1 : (int)$id; @@ -746,7 +746,7 @@ class Cache implements ICache { ->wherePath($file); $result = $query->execute(); - $size = $result->fetchColumn(); + $size = $result->fetchOne(); $result->closeCursor(); if ($size !== false) { @@ -793,10 +793,10 @@ class Cache implements ICache { } /** - * @param Statement $result + * @param IResult $result * @return CacheEntry[] */ - private function searchResultToCacheEntries(Statement $result) { + private function searchResultToCacheEntries(IResult $result): array { $files = $result->fetchAll(); return array_map(function (array $data) { @@ -870,7 +870,9 @@ class Cache implements ICache { } $result = $query->execute(); - return $this->searchResultToCacheEntries($result); + $cacheEntries = $this->searchResultToCacheEntries($result); + $result->closeCursor(); + return $cacheEntries; } /** @@ -912,7 +914,7 @@ class Cache implements ICache { ->andWhere($query->expr()->lt('size', $query->createNamedParameter(0, IQueryBuilder::PARAM_INT))); $result = $query->execute(); - $size = (int)$result->fetchColumn(); + $size = (int)$result->fetchOne(); $result->closeCursor(); return $size; @@ -1002,7 +1004,7 @@ class Cache implements ICache { ->setMaxResults(1); $result = $query->execute(); - $path = $result->fetchColumn(); + $path = $result->fetchOne(); $result->closeCursor(); return $path; @@ -1022,7 +1024,7 @@ class Cache implements ICache { ->whereFileId($id); $result = $query->execute(); - $path = $result->fetchColumn(); + $path = $result->fetchOne(); $result->closeCursor(); if ($path === false) { diff --git a/lib/private/Files/Cache/Scanner.php b/lib/private/Files/Cache/Scanner.php index 26264321a9a..0dbc34fae2f 100644 --- a/lib/private/Files/Cache/Scanner.php +++ b/lib/private/Files/Cache/Scanner.php @@ -36,6 +36,7 @@ namespace OC\Files\Cache; +use Doctrine\DBAL\Exception; use OC\Files\Filesystem; use OC\Hooks\BasicEmitter; use OCP\Files\Cache\IScanner; @@ -437,7 +438,7 @@ class Scanner extends BasicEmitter implements IScanner { $size += $data['size']; } } - } catch (\Doctrine\DBAL\DBALException $ex) { + } catch (Exception $ex) { // might happen if inserting duplicate while a scanning // process is running in parallel // log and ignore diff --git a/lib/private/Group/Database.php b/lib/private/Group/Database.php index 97094c67728..c49f3bce596 100644 --- a/lib/private/Group/Database.php +++ b/lib/private/Group/Database.php @@ -410,7 +410,7 @@ class Database extends ABackend implements } $result = $query->execute(); - $count = $result->fetchColumn(); + $count = $result->fetchOne(); $result->closeCursor(); if ($count !== false) { @@ -442,7 +442,7 @@ class Database extends ABackend implements ->andWhere($query->expr()->eq('gid', $query->createNamedParameter($gid), IQueryBuilder::PARAM_STR)); $result = $query->execute(); - $count = $result->fetchColumn(); + $count = $result->fetchOne(); $result->closeCursor(); if ($count !== false) { @@ -467,7 +467,7 @@ class Database extends ABackend implements ->where($query->expr()->eq('gid', $query->createNamedParameter($gid))); $result = $query->execute(); - $displayName = $result->fetchColumn(); + $displayName = $result->fetchOne(); $result->closeCursor(); return (string) $displayName; diff --git a/lib/private/Installer.php b/lib/private/Installer.php index 0b020aed569..e0eb8e9de18 100644 --- a/lib/private/Installer.php +++ b/lib/private/Installer.php @@ -45,6 +45,7 @@ use OC\App\AppStore\Bundles\Bundle; use OC\App\AppStore\Fetcher\AppFetcher; use OC\AppFramework\Bootstrap\Coordinator; use OC\Archive\TAR; +use OC\DB\Connection; use OC_App; use OC_DB; use OC_Helper; @@ -158,7 +159,7 @@ class Installer { OC_DB::updateDbFromStructure($basedir.'/appinfo/database.xml'); } } else { - $ms = new \OC\DB\MigrationService($info['id'], \OC::$server->getDatabaseConnection()); + $ms = new \OC\DB\MigrationService($info['id'], \OC::$server->get(Connection::class)); $ms->migrate('latest', true); } if ($previousVersion) { @@ -593,7 +594,7 @@ class Installer { ); } } else { - $ms = new \OC\DB\MigrationService($app, \OC::$server->getDatabaseConnection()); + $ms = new \OC\DB\MigrationService($app, \OC::$server->get(Connection::class)); $ms->migrate('latest', true); } diff --git a/lib/private/Lock/DBLockingProvider.php b/lib/private/Lock/DBLockingProvider.php index c6b9cf63780..30566c7b253 100644 --- a/lib/private/Lock/DBLockingProvider.php +++ b/lib/private/Lock/DBLockingProvider.php @@ -159,7 +159,7 @@ class DBLockingProvider extends AbstractLockingProvider { } $query = $this->connection->prepare('SELECT `lock` from `*PREFIX*file_locks` WHERE `key` = ?'); $query->execute([$path]); - $lockValue = (int)$query->fetchColumn(); + $lockValue = (int)$query->fetchOne(); if ($type === self::LOCK_SHARED) { if ($this->isLocallyLocked($path)) { // if we have a shared lock we kept open locally but it's released we always have at least 1 shared lock in the db diff --git a/lib/private/Repair.php b/lib/private/Repair.php index 80a8a8a87c1..e4f75b43fdc 100644 --- a/lib/private/Repair.php +++ b/lib/private/Repair.php @@ -36,6 +36,8 @@ namespace OC; use OC\App\AppStore\Bundles\BundleFetcher; use OC\Avatar\AvatarManager; +use OC\DB\Connection; +use OC\DB\ConnectionAdapter; use OC\Repair\AddBruteForceCleanupJob; use OC\Repair\AddCleanupUpdaterBackupsJob; use OC\Repair\CleanTags; @@ -210,13 +212,16 @@ class Repair implements IOutput { * @return IRepairStep[] */ public static function getBeforeUpgradeRepairSteps() { - $connection = \OC::$server->getDatabaseConnection(); + /** @var Connection $connection */ + $connection = \OC::$server->get(Connection::class); + /** @var ConnectionAdapter $connectionAdapter */ + $connectionAdapter = \OC::$server->get(ConnectionAdapter::class); $config = \OC::$server->getConfig(); $steps = [ - new Collation(\OC::$server->getConfig(), \OC::$server->getLogger(), $connection, true), + new Collation(\OC::$server->getConfig(), \OC::$server->getLogger(), $connectionAdapter, true), new SqliteAutoincrement($connection), - new SaveAccountsTableData($connection, $config), - new DropAccountTermsTable($connection) + new SaveAccountsTableData($connectionAdapter, $config), + new DropAccountTermsTable($connectionAdapter) ]; return $steps; diff --git a/lib/private/Repair/Collation.php b/lib/private/Repair/Collation.php index fb0e0192693..d2974c1680a 100644 --- a/lib/private/Repair/Collation.php +++ b/lib/private/Repair/Collation.php @@ -28,7 +28,7 @@ namespace OC\Repair; use Doctrine\DBAL\Exception\DriverException; -use Doctrine\DBAL\Platforms\MySqlPlatform; +use Doctrine\DBAL\Platforms\MySQLPlatform; use OCP\IConfig; use OCP\IDBConnection; use OCP\ILogger; @@ -69,7 +69,7 @@ class Collation implements IRepairStep { * Fix mime types */ public function run(IOutput $output) { - if (!$this->connection->getDatabasePlatform() instanceof MySqlPlatform) { + if (!$this->connection->getDatabasePlatform() instanceof MySQLPlatform) { $output->info('Not a mysql database -> nothing to do'); return; } diff --git a/lib/private/Repair/RemoveLinkShares.php b/lib/private/Repair/RemoveLinkShares.php index 3a0dd6f2884..32cbd1f73f8 100644 --- a/lib/private/Repair/RemoveLinkShares.php +++ b/lib/private/Repair/RemoveLinkShares.php @@ -30,8 +30,8 @@ declare(strict_types=1); namespace OC\Repair; -use Doctrine\DBAL\Driver\Statement; use OCP\AppFramework\Utility\ITimeFactory; +use OCP\DB\IResult; use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\IConfig; use OCP\IDBConnection; @@ -138,10 +138,8 @@ class RemoveLinkShares implements IRepairStep { /** * Get the cursor to fetch all the shares - * - * @return \Doctrine\DBAL\Driver\Statement */ - private function getShares(): Statement { + private function getShares(): IResult { $subQuery = $this->connection->getQueryBuilder(); $subQuery->select('*') ->from('share') @@ -160,7 +158,9 @@ class RemoveLinkShares implements IRepairStep { $query->expr()->eq('s2.share_type', $query->expr()->literal(2, IQueryBuilder::PARAM_INT)) )) ->andWhere($query->expr()->eq('s1.item_source', 's2.item_source')); - return $query->execute(); + /** @var IResult $result */ + $result = $query->execute(); + return $result; } /** @@ -210,13 +210,13 @@ class RemoveLinkShares implements IRepairStep { private function repair(IOutput $output, int $total): void { $output->startProgress($total); - $shareCursor = $this->getShares(); - while ($data = $shareCursor->fetch()) { + $shareResult = $this->getShares(); + while ($data = $shareResult->fetch()) { $this->processShare($data); $output->advance(); } $output->finishProgress(); - $shareCursor->closeCursor(); + $shareResult->closeCursor(); // Notifiy all admins $adminGroup = $this->groupManager->get('admin'); diff --git a/lib/private/Repair/RepairMimeTypes.php b/lib/private/Repair/RepairMimeTypes.php index 3e6fa51b196..d0e3bbff707 100644 --- a/lib/private/Repair/RepairMimeTypes.php +++ b/lib/private/Repair/RepairMimeTypes.php @@ -71,7 +71,7 @@ class RepairMimeTypes implements IRepairStep { if (empty($this->folderMimeTypeId)) { $query->setParameter('mimetype', 'httpd/unix-directory'); $result = $query->execute(); - $this->folderMimeTypeId = (int)$result->fetchColumn(); + $this->folderMimeTypeId = (int)$result->fetchOne(); $result->closeCursor(); } @@ -88,7 +88,7 @@ class RepairMimeTypes implements IRepairStep { // get target mimetype id $query->setParameter('mimetype', $mimetype); $result = $query->execute(); - $mimetypeId = (int)$result->fetchColumn(); + $mimetypeId = (int)$result->fetchOne(); $result->closeCursor(); if (!$mimetypeId) { @@ -242,7 +242,7 @@ class RepairMimeTypes implements IRepairStep { if (version_compare($ocVersionFromBeforeUpdate, '20.0.0.5', '<') && $this->introduceOpenDocumentTemplates()) { $out->info('Fixed OpenDocument template mime types'); } - + if (version_compare($ocVersionFromBeforeUpdate, '21.0.0.7', '<') && $this->introduceOrgModeType()) { $out->info('Fixed orgmode mime types'); } diff --git a/lib/private/Server.php b/lib/private/Server.php index 687eba68e73..680eea3beca 100644 --- a/lib/private/Server.php +++ b/lib/private/Server.php @@ -77,6 +77,8 @@ use OC\Comments\ManagerFactory as CommentsManagerFactory; use OC\Contacts\ContactsMenu\ActionFactory; use OC\Contacts\ContactsMenu\ContactsStore; use OC\Dashboard\DashboardManager; +use OC\DB\Connection; +use OC\DB\ConnectionAdapter; use OC\Diagnostics\EventLogger; use OC\Diagnostics\QueryLogger; use OC\EventDispatcher\SymfonyAdapter; @@ -792,7 +794,8 @@ class Server extends ServerContainer implements IServerContainer { /** @deprecated 19.0.0 */ $this->registerDeprecatedAlias('CredentialsManager', ICredentialsManager::class); - $this->registerService(IDBConnection::class, function (Server $c) { + $this->registerAlias(IDBConnection::class, ConnectionAdapter::class); + $this->registerService(Connection::class, function (Server $c) { $systemConfig = $c->get(SystemConfig::class); $factory = new \OC\DB\ConnectionFactory($systemConfig); $type = $systemConfig->getValue('dbtype', 'sqlite'); diff --git a/lib/private/Setup/AbstractDatabase.php b/lib/private/Setup/AbstractDatabase.php index 8a9aed09f1b..e0761db3070 100644 --- a/lib/private/Setup/AbstractDatabase.php +++ b/lib/private/Setup/AbstractDatabase.php @@ -29,6 +29,7 @@ namespace OC\Setup; +use OC\DB\Connection; use OC\DB\ConnectionFactory; use OC\DB\MigrationService; use OC\SystemConfig; @@ -108,7 +109,7 @@ abstract class AbstractDatabase { * @param array $configOverwrite * @return \OC\DB\Connection */ - protected function connect(array $configOverwrite = []) { + protected function connect(array $configOverwrite = []): Connection { $connectionParams = [ 'host' => $this->dbHost, 'user' => $this->dbUser, @@ -149,7 +150,7 @@ abstract class AbstractDatabase { if (!is_dir(\OC::$SERVERROOT."/core/Migrations")) { return; } - $ms = new MigrationService('core', \OC::$server->getDatabaseConnection()); + $ms = new MigrationService('core', \OC::$server->get(Connection::class)); $ms->migrate('latest', true); } } diff --git a/lib/private/Setup/MySQL.php b/lib/private/Setup/MySQL.php index 966c97edf55..21339dc46d0 100644 --- a/lib/private/Setup/MySQL.php +++ b/lib/private/Setup/MySQL.php @@ -31,6 +31,7 @@ namespace OC\Setup; +use OC\DB\ConnectionAdapter; use OC\DB\MySqlTools; use OCP\IDBConnection; use OCP\ILogger; @@ -45,12 +46,12 @@ class MySQL extends AbstractDatabase { // detect mb4 $tools = new MySqlTools(); - if ($tools->supports4ByteCharset($connection)) { + if ($tools->supports4ByteCharset(new ConnectionAdapter($connection))) { $this->config->setValue('mysql.utf8mb4', true); $connection = $this->connect(['dbname' => null]); } - $this->createSpecificUser($username, $connection); + $this->createSpecificUser($username, new ConnectionAdapter($connection)); //create the database $this->createDatabase($connection); @@ -156,27 +157,24 @@ class MySQL extends AbstractDatabase { $result = $connection->executeQuery($query, [$adminUser]); //current dbuser has admin rights - if ($result) { - $data = $result->fetchAll(); - //new dbuser does not exist - if (count($data) === 0) { - //use the admin login data for the new database user - $this->dbUser = $adminUser; - - //create a random password so we don't need to store the admin password in the config file - $this->dbPassword = $this->random->generate(30); - - $this->createDBUser($connection); - - break; - } else { - //repeat with different username - $length = strlen((string)$i); - $adminUser = substr('oc_' . $username, 0, 16 - $length) . $i; - $i++; - } - } else { + $data = $result->fetchAll(); + $result->closeCursor(); + //new dbuser does not exist + if (count($data) === 0) { + //use the admin login data for the new database user + $this->dbUser = $adminUser; + + //create a random password so we don't need to store the admin password in the config file + $this->dbPassword = $this->random->generate(30); + + $this->createDBUser($connection); + break; + } else { + //repeat with different username + $length = strlen((string)$i); + $adminUser = substr('oc_' . $username, 0, 16 - $length) . $i; + $i++; } } } diff --git a/lib/private/Setup/PostgreSQL.php b/lib/private/Setup/PostgreSQL.php index 1f0b7b8f894..7e59bf297f1 100644 --- a/lib/private/Setup/PostgreSQL.php +++ b/lib/private/Setup/PostgreSQL.php @@ -30,8 +30,8 @@ namespace OC\Setup; use OC\DatabaseException; +use OC\DB\Connection; use OC\DB\QueryBuilder\Literal; -use OCP\IDBConnection; class PostgreSQL extends AbstractDatabase { public $dbprettyname = 'PostgreSQL'; @@ -103,7 +103,7 @@ class PostgreSQL extends AbstractDatabase { } } - private function createDatabase(IDBConnection $connection) { + private function createDatabase(Connection $connection) { if (!$this->databaseExists($connection)) { //The database does not exists... let's create it $query = $connection->prepare("CREATE DATABASE " . addslashes($this->dbName) . " OWNER " . addslashes($this->dbUser)); @@ -124,7 +124,7 @@ class PostgreSQL extends AbstractDatabase { } } - private function userExists(IDBConnection $connection) { + private function userExists(Connection $connection) { $builder = $connection->getQueryBuilder(); $builder->automaticTablePrefix(false); $query = $builder->select('*') @@ -134,7 +134,7 @@ class PostgreSQL extends AbstractDatabase { return $result->rowCount() > 0; } - private function databaseExists(IDBConnection $connection) { + private function databaseExists(Connection $connection) { $builder = $connection->getQueryBuilder(); $builder->automaticTablePrefix(false); $query = $builder->select('datname') @@ -144,7 +144,7 @@ class PostgreSQL extends AbstractDatabase { return $result->rowCount() > 0; } - private function createDBUser(IDBConnection $connection) { + private function createDBUser(Connection $connection) { $dbUser = $this->dbUser; try { $i = 1; diff --git a/lib/private/Tags.php b/lib/private/Tags.php index 720e9dd5d7d..8e32f3925e3 100644 --- a/lib/private/Tags.php +++ b/lib/private/Tags.php @@ -230,10 +230,6 @@ class Tags implements ITags { } $entries[$objId][] = $row['category']; } - if ($result === null) { - \OCP\Util::writeLog('core', __METHOD__. 'DB error: ' . \OC::$server->getDatabaseConnection()->getError(), ILogger::ERROR); - return false; - } } } catch (\Exception $e) { \OC::$server->getLogger()->logException($e, [ diff --git a/lib/private/Updater.php b/lib/private/Updater.php index ec0a50cc6ca..09aa955283c 100644 --- a/lib/private/Updater.php +++ b/lib/private/Updater.php @@ -37,6 +37,7 @@ namespace OC; +use OC\DB\Connection; use OC\DB\MigrationService; use OC\Hooks\BasicEmitter; use OC\IntegrityCheck\Checker; @@ -298,7 +299,7 @@ class Updater extends BasicEmitter { $this->emit('\OC\Updater', 'dbUpgradeBefore'); // execute core migrations - $ms = new MigrationService('core', \OC::$server->getDatabaseConnection()); + $ms = new MigrationService('core', \OC::$server->get(Connection::class)); $ms->migrate(); $this->emit('\OC\Updater', 'dbUpgrade'); diff --git a/lib/private/User/Database.php b/lib/private/User/Database.php index 7c936acd0bd..85e22d196e4 100644 --- a/lib/private/User/Database.php +++ b/lib/private/User/Database.php @@ -436,7 +436,7 @@ class Database extends ABackend implements ->from($this->table); $result = $query->execute(); - return $result->fetchColumn(); + return $result->fetchOne(); } /** diff --git a/lib/private/User/Manager.php b/lib/private/User/Manager.php index 1d58c68268c..39e3a37254f 100644 --- a/lib/private/User/Manager.php +++ b/lib/private/User/Manager.php @@ -491,7 +491,7 @@ class Manager extends PublicEmitter implements IUserManager { $result = $queryBuilder->execute(); - $count = $result->fetchColumn(); + $count = $result->fetchOne(); $result->closeCursor(); if ($count !== false) { @@ -521,7 +521,7 @@ class Manager extends PublicEmitter implements IUserManager { ->andWhere($queryBuilder->expr()->in('gid', $queryBuilder->createNamedParameter($groups, IQueryBuilder::PARAM_STR_ARRAY))); $result = $queryBuilder->execute(); - $count = $result->fetchColumn(); + $count = $result->fetchOne(); $result->closeCursor(); if ($count !== false) { @@ -549,7 +549,7 @@ class Manager extends PublicEmitter implements IUserManager { $query = $queryBuilder->execute(); - $result = (int)$query->fetchColumn(); + $result = (int)$query->fetchOne(); $query->closeCursor(); return $result; diff --git a/lib/private/legacy/OC_App.php b/lib/private/legacy/OC_App.php index 32d2569d0a0..a64b13f1e2c 100644 --- a/lib/private/legacy/OC_App.php +++ b/lib/private/legacy/OC_App.php @@ -989,7 +989,7 @@ class OC_App { if (file_exists($appPath . '/appinfo/database.xml')) { OC_DB::updateDbFromStructure($appPath . '/appinfo/database.xml'); } else { - $ms = new MigrationService($appId, \OC::$server->getDatabaseConnection()); + $ms = new MigrationService($appId, \OC::$server->get(\OC\DB\Connection::class)); $ms->migrate(); } diff --git a/lib/private/legacy/OC_DB.php b/lib/private/legacy/OC_DB.php index 11cd1f4cae0..b5b6c737046 100644 --- a/lib/private/legacy/OC_DB.php +++ b/lib/private/legacy/OC_DB.php @@ -44,7 +44,7 @@ class OC_DB { * @return \OC\DB\MDB2SchemaManager */ private static function getMDB2SchemaManager() { - return new \OC\DB\MDB2SchemaManager(\OC::$server->getDatabaseConnection()); + return new \OC\DB\MDB2SchemaManager(\OC::$server->get(\OC\DB\Connection::class)); } /** @@ -70,7 +70,7 @@ class OC_DB { // return the result try { $result = $connection->prepare($query, $limit, $offset); - } catch (\Doctrine\DBAL\DBALException $e) { + } catch (\Doctrine\DBAL\Exception $e) { throw new \OC\DatabaseException($e->getMessage()); } // differentiate between query and manipulation @@ -160,18 +160,6 @@ class OC_DB { } /** - * saves database schema to xml file - * @param string $file name of file - * @return bool - * - * TODO: write more documentation - */ - public static function getDbStructure($file) { - $schemaManager = self::getMDB2SchemaManager(); - return $schemaManager->getDbStructure($file); - } - - /** * Creates tables from XML file * @param string $file file to read structure from * @return bool @@ -211,7 +199,7 @@ class OC_DB { } /** - * check if a result is an error and throws an exception, works with \Doctrine\DBAL\DBALException + * check if a result is an error and throws an exception, works with \Doctrine\DBAL\Exception * @param mixed $result * @param string $message * @return void diff --git a/lib/private/legacy/OC_DB_StatementWrapper.php b/lib/private/legacy/OC_DB_StatementWrapper.php index 667c2de9678..cc2320015a3 100644 --- a/lib/private/legacy/OC_DB_StatementWrapper.php +++ b/lib/private/legacy/OC_DB_StatementWrapper.php @@ -28,6 +28,8 @@ * */ +use OCP\DB\IPreparedStatement; + /** * small wrapper around \Doctrine\DBAL\Driver\Statement to make it behave, more like an MDB2 Statement * @@ -38,17 +40,20 @@ * @method array fetchAll(integer $fetchMode = null); */ class OC_DB_StatementWrapper { - /** - * @var \Doctrine\DBAL\Driver\Statement - */ + /** @var IPreparedStatement */ private $statement = null; + + /** @var bool */ private $isManipulation = false; + + /** @var array */ private $lastArguments = []; /** + * @param IPreparedStatement $statement * @param boolean $isManipulation */ - public function __construct($statement, $isManipulation) { + public function __construct(IPreparedStatement $statement, $isManipulation) { $this->statement = $statement; $this->isManipulation = $isManipulation; } @@ -63,31 +68,34 @@ class OC_DB_StatementWrapper { /** * make execute return the result instead of a bool * - * @param array $input + * @param mixed[] $input * @return \OC_DB_StatementWrapper|int|bool + * @deprecated */ public function execute($input = []) { $this->lastArguments = $input; - if (count($input) > 0) { - $result = $this->statement->execute($input); - } else { - $result = $this->statement->execute(); - } - - if ($result === false) { + try { + if (count($input) > 0) { + $result = $this->statement->execute($input); + } else { + $result = $this->statement->execute(); + } + } catch (\Doctrine\DBAL\Exception $e) { return false; } + if ($this->isManipulation) { return $this->statement->rowCount(); - } else { - return $this; } + + return $this; } /** * provide an alias for fetch * * @return mixed + * @deprecated */ public function fetchRow() { return $this->statement->fetch(); @@ -97,11 +105,11 @@ class OC_DB_StatementWrapper { * Provide a simple fetchOne. * * fetch single column from the next row - * @param int $column the column number to fetch * @return string + * @deprecated */ - public function fetchOne($column = 0) { - return $this->statement->fetchColumn($column); + public function fetchOne() { + return $this->statement->fetchOne(); } /** diff --git a/lib/private/legacy/OC_Util.php b/lib/private/legacy/OC_Util.php index 4176dfbb229..d5805719ea2 100644 --- a/lib/private/legacy/OC_Util.php +++ b/lib/private/legacy/OC_Util.php @@ -993,7 +993,7 @@ class OC_Util { ]; } } - } catch (\Doctrine\DBAL\DBALException $e) { + } catch (\Doctrine\DBAL\Exception $e) { $logger = \OC::$server->getLogger(); $logger->warning('Error occurred while checking PostgreSQL version, assuming >= 9'); $logger->logException($e); |