From c2394958c05f13b2f4b928e9e3760839ecb21c3e Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Fri, 28 Jun 2024 14:55:16 +0200 Subject: [PATCH 01/51] chore(deps): Bump doctrine/dbal from 3.8.3 to 4.0.4 Signed-off-by: Joas Schilling --- 3rdparty | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/3rdparty b/3rdparty index 45d8fc9e09fbb..b5c63bed7b21e 160000 --- a/3rdparty +++ b/3rdparty @@ -1 +1 @@ -Subproject commit 45d8fc9e09fbb4d3c4de5819afd9a6f72ce00f56 +Subproject commit b5c63bed7b21e25b05de3e0125073d40e4a9e72f From 015a3e85fe71b7677ef2afa93de0ae80c8e8b9c1 Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Fri, 28 Jun 2024 15:49:18 +0200 Subject: [PATCH 02/51] fix(db)!: Fix casing of doctrine's SQLitePlatform Signed-off-by: Joas Schilling --- apps/settings/lib/SetupChecks/SupportedDatabase.php | 4 ++-- apps/settings/tests/SetupChecks/SupportedDatabaseTest.php | 4 ++-- apps/user_ldap/lib/Mapping/AbstractMapping.php | 4 ++-- core/Command/Db/ConvertFilecacheBigInt.php | 4 ++-- lib/private/DB/Connection.php | 4 ++-- lib/private/DB/ConnectionAdapter.php | 4 ++-- lib/private/DB/OCSqlitePlatform.php | 2 +- lib/private/DB/QueryBuilder/QueryBuilder.php | 6 +++--- tests/lib/DB/MigratorTest.php | 4 ++-- 9 files changed, 18 insertions(+), 18 deletions(-) diff --git a/apps/settings/lib/SetupChecks/SupportedDatabase.php b/apps/settings/lib/SetupChecks/SupportedDatabase.php index 89f789483059b..fa42516a87a8a 100644 --- a/apps/settings/lib/SetupChecks/SupportedDatabase.php +++ b/apps/settings/lib/SetupChecks/SupportedDatabase.php @@ -11,7 +11,7 @@ use Doctrine\DBAL\Platforms\MySQLPlatform; use Doctrine\DBAL\Platforms\OraclePlatform; use Doctrine\DBAL\Platforms\PostgreSQLPlatform; -use Doctrine\DBAL\Platforms\SqlitePlatform; +use Doctrine\DBAL\Platforms\SQLitePlatform; use OCP\IDBConnection; use OCP\IL10N; use OCP\IURLGenerator; @@ -103,7 +103,7 @@ public function run(): SetupResult { } } elseif ($databasePlatform instanceof OraclePlatform) { $version = 'Oracle'; - } elseif ($databasePlatform instanceof SqlitePlatform) { + } elseif ($databasePlatform instanceof SQLitePlatform) { return SetupResult::warning( $this->l10n->t('SQLite is currently being used as the backend database. For larger installations we recommend that you switch to a different database backend. This is particularly recommended when using the desktop client for file synchronisation. To migrate to another database use the command line tool: "occ db:convert-type".'), $this->urlGenerator->linkToDocs('admin-db-conversion') diff --git a/apps/settings/tests/SetupChecks/SupportedDatabaseTest.php b/apps/settings/tests/SetupChecks/SupportedDatabaseTest.php index 2492379b557ca..123bb9b1e881a 100644 --- a/apps/settings/tests/SetupChecks/SupportedDatabaseTest.php +++ b/apps/settings/tests/SetupChecks/SupportedDatabaseTest.php @@ -8,7 +8,7 @@ */ namespace OCA\Settings\Tests; -use Doctrine\DBAL\Platforms\SqlitePlatform; +use Doctrine\DBAL\Platforms\SQLitePlatform; use OCA\Settings\SetupChecks\SupportedDatabase; use OCP\IDBConnection; use OCP\IL10N; @@ -42,7 +42,7 @@ protected function setUp(): void { public function testPass(): void { $platform = $this->connection->getDatabasePlatform(); - if ($platform instanceof SqlitePlatform) { + if ($platform instanceof SQLitePlatform) { /** SQlite always gets a warning */ $this->assertEquals(SetupResult::WARNING, $this->check->run()->getSeverity()); } else { diff --git a/apps/user_ldap/lib/Mapping/AbstractMapping.php b/apps/user_ldap/lib/Mapping/AbstractMapping.php index c243731eaf7a3..db3a47dfd943a 100644 --- a/apps/user_ldap/lib/Mapping/AbstractMapping.php +++ b/apps/user_ldap/lib/Mapping/AbstractMapping.php @@ -8,7 +8,7 @@ namespace OCA\User_LDAP\Mapping; use Doctrine\DBAL\Exception; -use Doctrine\DBAL\Platforms\SqlitePlatform; +use Doctrine\DBAL\Platforms\SQLitePlatform; use OCP\DB\IPreparedStatement; use OCP\DB\QueryBuilder\IQueryBuilder; use Psr\Log\LoggerInterface; @@ -216,7 +216,7 @@ protected function collectResultsFromListOfIdsQuery(IQueryBuilder $qb, array &$r public function getListOfIdsByDn(array $fdns): array { $totalDBParamLimit = 65000; $sliceSize = 1000; - $maxSlices = $this->dbc->getDatabasePlatform() instanceof SqlitePlatform ? 9 : $totalDBParamLimit / $sliceSize; + $maxSlices = $this->dbc->getDatabasePlatform() instanceof SQLitePlatform ? 9 : $totalDBParamLimit / $sliceSize; $results = []; $slice = 1; diff --git a/core/Command/Db/ConvertFilecacheBigInt.php b/core/Command/Db/ConvertFilecacheBigInt.php index 5f3e790e9ceb8..3f70cef3d79aa 100644 --- a/core/Command/Db/ConvertFilecacheBigInt.php +++ b/core/Command/Db/ConvertFilecacheBigInt.php @@ -5,7 +5,7 @@ */ namespace OC\Core\Command\Db; -use Doctrine\DBAL\Platforms\SqlitePlatform; +use Doctrine\DBAL\Platforms\SQLitePlatform; use Doctrine\DBAL\Types\Type; use OC\DB\Connection; use OC\DB\SchemaWrapper; @@ -53,7 +53,7 @@ public static function getColumnsByTable(): array { protected function execute(InputInterface $input, OutputInterface $output): int { $schema = new SchemaWrapper($this->connection); - $isSqlite = $this->connection->getDatabasePlatform() instanceof SqlitePlatform; + $isSqlite = $this->connection->getDatabasePlatform() instanceof SQLitePlatform; $updates = []; $tables = static::getColumnsByTable(); diff --git a/lib/private/DB/Connection.php b/lib/private/DB/Connection.php index c886cb202353f..e6a0eb9e7f970 100644 --- a/lib/private/DB/Connection.php +++ b/lib/private/DB/Connection.php @@ -17,7 +17,7 @@ use Doctrine\DBAL\Exception\ConnectionLost; use Doctrine\DBAL\Platforms\MySQLPlatform; use Doctrine\DBAL\Platforms\OraclePlatform; -use Doctrine\DBAL\Platforms\SqlitePlatform; +use Doctrine\DBAL\Platforms\SQLitePlatform; use Doctrine\DBAL\Result; use Doctrine\DBAL\Schema\Schema; use Doctrine\DBAL\Statement; @@ -671,7 +671,7 @@ private function getMigrator() { $platform = $this->getDatabasePlatform(); $config = \OC::$server->getConfig(); $dispatcher = Server::get(\OCP\EventDispatcher\IEventDispatcher::class); - if ($platform instanceof SqlitePlatform) { + if ($platform instanceof SQLitePlatform) { return new SQLiteMigrator($this, $config, $dispatcher); } elseif ($platform instanceof OraclePlatform) { return new OracleMigrator($this, $config, $dispatcher); diff --git a/lib/private/DB/ConnectionAdapter.php b/lib/private/DB/ConnectionAdapter.php index 86a901a7de33d..d2808bb0295ce 100644 --- a/lib/private/DB/ConnectionAdapter.php +++ b/lib/private/DB/ConnectionAdapter.php @@ -13,7 +13,7 @@ use Doctrine\DBAL\Platforms\MySQLPlatform; use Doctrine\DBAL\Platforms\OraclePlatform; use Doctrine\DBAL\Platforms\PostgreSQLPlatform; -use Doctrine\DBAL\Platforms\SqlitePlatform; +use Doctrine\DBAL\Platforms\SQLitePlatform; use Doctrine\DBAL\Schema\Schema; use OC\DB\Exceptions\DbalException; use OCP\DB\IPreparedStatement; @@ -238,7 +238,7 @@ public function getDatabaseProvider(): string { return IDBConnection::PLATFORM_ORACLE; } elseif ($platform instanceof PostgreSQLPlatform) { return IDBConnection::PLATFORM_POSTGRES; - } elseif ($platform instanceof SqlitePlatform) { + } elseif ($platform instanceof SQLitePlatform) { return IDBConnection::PLATFORM_SQLITE; } else { throw new \Exception('Database ' . $platform::class . ' not supported'); diff --git a/lib/private/DB/OCSqlitePlatform.php b/lib/private/DB/OCSqlitePlatform.php index 3e2b10e6e4073..b8bd8df5eb11e 100644 --- a/lib/private/DB/OCSqlitePlatform.php +++ b/lib/private/DB/OCSqlitePlatform.php @@ -7,5 +7,5 @@ */ namespace OC\DB; -class OCSqlitePlatform extends \Doctrine\DBAL\Platforms\SqlitePlatform { +class OCSqlitePlatform extends \Doctrine\DBAL\Platforms\SQLitePlatform { } diff --git a/lib/private/DB/QueryBuilder/QueryBuilder.php b/lib/private/DB/QueryBuilder/QueryBuilder.php index 0e7d8d2ff3eca..4f82a97daa645 100644 --- a/lib/private/DB/QueryBuilder/QueryBuilder.php +++ b/lib/private/DB/QueryBuilder/QueryBuilder.php @@ -10,7 +10,7 @@ use Doctrine\DBAL\Platforms\MySQLPlatform; use Doctrine\DBAL\Platforms\OraclePlatform; use Doctrine\DBAL\Platforms\PostgreSQL94Platform; -use Doctrine\DBAL\Platforms\SqlitePlatform; +use Doctrine\DBAL\Platforms\SQLitePlatform; use Doctrine\DBAL\Query\QueryException; use OC\DB\ConnectionAdapter; use OC\DB\QueryBuilder\ExpressionBuilder\ExpressionBuilder; @@ -104,7 +104,7 @@ public function expr() { if ($this->connection->getDatabasePlatform() instanceof MySQLPlatform) { return new MySqlExpressionBuilder($this->connection, $this); } - if ($this->connection->getDatabasePlatform() instanceof SqlitePlatform) { + if ($this->connection->getDatabasePlatform() instanceof SQLitePlatform) { return new SqliteExpressionBuilder($this->connection, $this); } @@ -131,7 +131,7 @@ public function func() { if ($this->connection->getDatabasePlatform() instanceof OraclePlatform) { return new OCIFunctionBuilder($this->connection, $this, $this->helper); } - if ($this->connection->getDatabasePlatform() instanceof SqlitePlatform) { + if ($this->connection->getDatabasePlatform() instanceof SQLitePlatform) { return new SqliteFunctionBuilder($this->connection, $this, $this->helper); } if ($this->connection->getDatabasePlatform() instanceof PostgreSQL94Platform) { diff --git a/tests/lib/DB/MigratorTest.php b/tests/lib/DB/MigratorTest.php index eaa6540b93bd6..0ca698bbbe9db 100644 --- a/tests/lib/DB/MigratorTest.php +++ b/tests/lib/DB/MigratorTest.php @@ -11,7 +11,7 @@ use Doctrine\DBAL\Exception; use Doctrine\DBAL\ParameterType; use Doctrine\DBAL\Platforms\OraclePlatform; -use Doctrine\DBAL\Platforms\SqlitePlatform; +use Doctrine\DBAL\Platforms\SQLitePlatform; use Doctrine\DBAL\Schema\Schema; use Doctrine\DBAL\Schema\SchemaConfig; use OC\DB\Migrator; @@ -59,7 +59,7 @@ private function getMigrator(): Migrator { $platform = $this->connection->getDatabasePlatform(); $random = \OC::$server->get(ISecureRandom::class); $dispatcher = \OC::$server->get(\OCP\EventDispatcher\IEventDispatcher::class); - if ($platform instanceof SqlitePlatform) { + if ($platform instanceof SQLitePlatform) { return new SQLiteMigrator($this->connection, $this->config, $dispatcher); } elseif ($platform instanceof OraclePlatform) { return new OracleMigrator($this->connection, $this->config, $dispatcher); From a5432e3c6946233954c18500de7a9495c118f833 Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Fri, 28 Jun 2024 15:51:24 +0200 Subject: [PATCH 03/51] fix(db): Adjust Connection signature to updated doctrine one Signed-off-by: Joas Schilling --- lib/private/DB/Connection.php | 33 +++++++++------------------------ 1 file changed, 9 insertions(+), 24 deletions(-) diff --git a/lib/private/DB/Connection.php b/lib/private/DB/Connection.php index e6a0eb9e7f970..2a96384c5cf2a 100644 --- a/lib/private/DB/Connection.php +++ b/lib/private/DB/Connection.php @@ -116,7 +116,7 @@ public function __construct( /** * @throws Exception */ - public function connect($connectionName = null) { + public function connect(?string $connectionName = null): Driver\Connection { try { if ($this->_conn) { $this->reconnectIfNeeded(); @@ -140,7 +140,7 @@ public function connect($connectionName = null) { } } - protected function performConnect(?string $connectionName = null): bool { + protected function performConnect(?string $connectionName = null): Driver\Connection { if (($connectionName ?? 'replica') === 'replica' && count($this->params['replica']) === 1 && $this->params['primary'] === $this->params['replica'][0]) { @@ -174,26 +174,13 @@ public function getQueryBuilder(): IQueryBuilder { * @return \Doctrine\DBAL\Query\QueryBuilder * @deprecated please use $this->getQueryBuilder() instead */ - public function createQueryBuilder() { + public function createQueryBuilder(): \Doctrine\DBAL\Query\QueryBuilder { $backtrace = $this->getCallerBacktrace(); $this->logger->debug('Doctrine QueryBuilder retrieved in {backtrace}', ['app' => 'core', 'backtrace' => $backtrace]); $this->queriesBuilt++; return parent::createQueryBuilder(); } - /** - * Gets the ExpressionBuilder for the connection. - * - * @return \Doctrine\DBAL\Query\Expression\ExpressionBuilder - * @deprecated please use $this->getQueryBuilder()->expr() instead - */ - public function getExpressionBuilder() { - $backtrace = $this->getCallerBacktrace(); - $this->logger->debug('Doctrine ExpressionBuilder retrieved in {backtrace}', ['app' => 'core', 'backtrace' => $backtrace]); - $this->queriesBuilt++; - return parent::getExpressionBuilder(); - } - /** * Get the file and line that called the method where `getCallerBacktrace()` was used * @@ -680,15 +667,15 @@ private function getMigrator() { } } - public function beginTransaction() { + public function beginTransaction(): void { if (!$this->inTransaction()) { $this->transactionActiveSince = microtime(true); } - return parent::beginTransaction(); + parent::beginTransaction(); } - public function commit() { - $result = parent::commit(); + public function commit(): void { + parent::commit(); if ($this->getTransactionNestingLevel() === 0) { $timeTook = microtime(true) - $this->transactionActiveSince; $this->transactionActiveSince = null; @@ -696,11 +683,10 @@ public function commit() { $this->logger->debug('Transaction took ' . $timeTook . 's', ['exception' => new \Exception('Transaction took ' . $timeTook . 's')]); } } - return $result; } - public function rollBack() { - $result = parent::rollBack(); + public function rollBack(): void { + parent::rollBack(); if ($this->getTransactionNestingLevel() === 0) { $timeTook = microtime(true) - $this->transactionActiveSince; $this->transactionActiveSince = null; @@ -708,7 +694,6 @@ public function rollBack() { $this->logger->debug('Transaction rollback took longer than 1s: ' . $timeTook, ['exception' => new \Exception('Long running transaction rollback')]); } } - return $result; } private function reconnectIfNeeded(): void { From de0141d83cb883d23d163f641b87729efdb9cf1a Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Fri, 28 Jun 2024 15:53:39 +0200 Subject: [PATCH 04/51] fix(db)!: Empty and adjust query-part related methods Signed-off-by: Joas Schilling --- lib/private/DB/QueryBuilder/QueryBuilder.php | 64 +++++++++++++------- lib/public/DB/QueryBuilder/IQueryBuilder.php | 4 ++ 2 files changed, 46 insertions(+), 22 deletions(-) diff --git a/lib/private/DB/QueryBuilder/QueryBuilder.php b/lib/private/DB/QueryBuilder/QueryBuilder.php index 4f82a97daa645..9a2e6402451fc 100644 --- a/lib/private/DB/QueryBuilder/QueryBuilder.php +++ b/lib/private/DB/QueryBuilder/QueryBuilder.php @@ -207,24 +207,24 @@ public function execute() { } } - if (!empty($this->getQueryPart('select'))) { - $select = $this->getQueryPart('select'); - $hasSelectAll = array_filter($select, static function ($s) { - return $s === '*'; - }); - $hasSelectSpecific = array_filter($select, static function ($s) { - return $s !== '*'; - }); - - if (empty($hasSelectAll) === empty($hasSelectSpecific)) { - $exception = new QueryException('Query is selecting * and specific values in the same query. This is not supported in Oracle.'); - $this->logger->error($exception->getMessage(), [ - 'query' => $this->getSQL(), - 'app' => 'core', - 'exception' => $exception, - ]); - } - } + //if (!empty($this->getQueryPart('select'))) { + // $select = $this->getQueryPart('select'); + // $hasSelectAll = array_filter($select, static function ($s) { + // return $s === '*'; + // }); + // $hasSelectSpecific = array_filter($select, static function ($s) { + // return $s !== '*'; + // }); + + // if (empty($hasSelectAll) === empty($hasSelectSpecific)) { + // $exception = new QueryException('Query is selecting * and specific values in the same query. This is not supported in Oracle.'); + // $this->logger->error($exception->getMessage(), [ + // 'query' => $this->getSQL(), + // 'app' => 'core', + // 'exception' => $exception, + // ]); + // } + //} $numberOfParameters = 0; $hasTooLargeArrayParameter = false; @@ -1096,18 +1096,20 @@ public function addOrderBy($sort, $order = null) { * @param string $queryPartName * * @return mixed + * @deprecated 30.0.0 The function always throws an exception */ public function getQueryPart($queryPartName) { - return $this->queryBuilder->getQueryPart($queryPartName); + throw new \Exception('Getting query parts is no longer supported as they are implementation details.'); } /** * Gets all query parts. * * @return array + * @deprecated 30.0.0 The function always throws an exception */ public function getQueryParts() { - return $this->queryBuilder->getQueryParts(); + throw new \Exception('Getting query parts is no longer supported as they are implementation details.'); } /** @@ -1116,9 +1118,20 @@ public function getQueryParts() { * @param array|null $queryPartNames * * @return $this This QueryBuilder instance. + * @since 30.0.0 Only null and a list of 'where'|'having'|'groupBy'|'orderBy' is supported. Everything else will throw. */ public function resetQueryParts($queryPartNames = null) { - $this->queryBuilder->resetQueryParts($queryPartNames); + if ($queryPartNames === null) { + $this->queryBuilder->resetWhere(); + $this->queryBuilder->resetHaving(); + $this->queryBuilder->resetGroupBy(); + $this->queryBuilder->resetOrderBy(); + return $this; + } + + foreach ($queryPartNames as $queryPartName) { + $this->resetQueryPart($queryPartName); + } return $this; } @@ -1129,9 +1142,16 @@ public function resetQueryParts($queryPartNames = null) { * @param string $queryPartName * * @return $this This QueryBuilder instance. + * @since 30.0.0 Only 'where'|'having'|'groupBy'|'orderBy' are supported. Everything else will throw. */ public function resetQueryPart($queryPartName) { - $this->queryBuilder->resetQueryPart($queryPartName); + match ($queryPartName) { + 'where' => $this->queryBuilder->resetWhere(), + 'having' => $this->queryBuilder->resetHaving(), + 'groupBy' => $this->queryBuilder->resetGroupBy(), + 'orderBy' => $this->queryBuilder->resetOrderBy(), + default => throw new \Exception('Resetting query part "' . $queryPartName. '" is no longer supported. Please create a new QueryBuilder instead.'), + }; return $this; } diff --git a/lib/public/DB/QueryBuilder/IQueryBuilder.php b/lib/public/DB/QueryBuilder/IQueryBuilder.php index 94ab796adf4c0..271771b5aa276 100644 --- a/lib/public/DB/QueryBuilder/IQueryBuilder.php +++ b/lib/public/DB/QueryBuilder/IQueryBuilder.php @@ -833,6 +833,7 @@ public function addOrderBy($sort, $order = null); * * @return mixed * @since 8.2.0 + * @deprecated 30.0.0 The function always throws an exception */ public function getQueryPart($queryPartName); @@ -841,6 +842,7 @@ public function getQueryPart($queryPartName); * * @return array * @since 8.2.0 + * @deprecated 30.0.0 The function always throws an exception */ public function getQueryParts(); @@ -851,6 +853,7 @@ public function getQueryParts(); * * @return $this This QueryBuilder instance. * @since 8.2.0 + * @since 30.0.0 Only null and a list of 'where'|'having'|'groupBy'|'orderBy' is supported. Everything else will throw. */ public function resetQueryParts($queryPartNames = null); @@ -861,6 +864,7 @@ public function resetQueryParts($queryPartNames = null); * * @return $this This QueryBuilder instance. * @since 8.2.0 + * @since 30.0.0 Only 'where'|'having'|'groupBy'|'orderBy' are supported. Everything else will throw. */ public function resetQueryPart($queryPartName); From 72886c0bfbf820c5b09d7624a88fc68dd5dc003a Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Fri, 28 Jun 2024 15:54:18 +0200 Subject: [PATCH 05/51] fix(db): Make sure setComment() is only called with strings Signed-off-by: Joas Schilling --- lib/private/DB/SQLiteMigrator.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/private/DB/SQLiteMigrator.php b/lib/private/DB/SQLiteMigrator.php index 6be17625476a3..a8b288323005d 100644 --- a/lib/private/DB/SQLiteMigrator.php +++ b/lib/private/DB/SQLiteMigrator.php @@ -19,8 +19,8 @@ protected function getDiff(Schema $targetSchema, \Doctrine\DBAL\Connection $conn foreach ($targetSchema->getTables() as $table) { foreach ($table->getColumns() as $column) { // column comments are not supported on SQLite - if ($column->getComment() !== null) { - $column->setComment(null); + if ($column->getComment() !== '') { + $column->setComment(''); } } } From 6d0e8e46067f49a4a3b4f1fe5ade20797c7587d4 Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Fri, 28 Jun 2024 15:55:02 +0200 Subject: [PATCH 06/51] fix(db): Use new function name to get the schema Signed-off-by: Joas Schilling --- lib/private/DB/Connection.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/private/DB/Connection.php b/lib/private/DB/Connection.php index 2a96384c5cf2a..92ed44e606251 100644 --- a/lib/private/DB/Connection.php +++ b/lib/private/DB/Connection.php @@ -564,7 +564,7 @@ public function dropTable($table) { */ public function tableExists($table) { $table = $this->tablePrefix . trim($table); - $schema = $this->getSchemaManager(); + $schema = $this->createSchemaManager(); return $schema->tablesExist([$table]); } From 76ef7dd71e88e88f45affa2bca8d25a36cccf5c2 Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Fri, 28 Jun 2024 15:55:36 +0200 Subject: [PATCH 07/51] fix(db): Remove usage of dropped doctrine event manager Signed-off-by: Joas Schilling --- lib/private/DB/ConnectionFactory.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/private/DB/ConnectionFactory.php b/lib/private/DB/ConnectionFactory.php index 5e50d01538acc..2429bfdf7f8b5 100644 --- a/lib/private/DB/ConnectionFactory.php +++ b/lib/private/DB/ConnectionFactory.php @@ -103,8 +103,8 @@ public function getDefaultConnectionParams($type) { */ public function getConnection($type, $additionalConnectionParams) { $normalizedType = $this->normalizeType($type); - $eventManager = new EventManager(); - $eventManager->addEventSubscriber(new SetTransactionIsolationLevel()); + // FIXME $eventManager = new EventManager(); + // FIXME $eventManager->addEventSubscriber(new SetTransactionIsolationLevel()); $additionalConnectionParams = array_merge($this->createConnectionParams(), $additionalConnectionParams); switch ($normalizedType) { case 'pgsql': @@ -117,7 +117,7 @@ public function getConnection($type, $additionalConnectionParams) { break; case 'oci': - $eventManager->addEventSubscriber(new OracleSessionInit); + // FIXME $eventManager->addEventSubscriber(new OracleSessionInit); // the driverOptions are unused in dbal and need to be mapped to the parameters if (isset($additionalConnectionParams['driverOptions'])) { $additionalConnectionParams = array_merge($additionalConnectionParams, $additionalConnectionParams['driverOptions']); @@ -138,14 +138,14 @@ public function getConnection($type, $additionalConnectionParams) { case 'sqlite3': $journalMode = $additionalConnectionParams['sqlite.journal_mode']; $additionalConnectionParams['platform'] = new OCSqlitePlatform(); - $eventManager->addEventSubscriber(new SQLiteSessionInit(true, $journalMode)); + // FIXME $eventManager->addEventSubscriber(new SQLiteSessionInit(true, $journalMode)); break; } /** @var Connection $connection */ $connection = DriverManager::getConnection( $additionalConnectionParams, new Configuration(), - $eventManager + // FIXME $eventManager ); return $connection; } From 648b20c588a6a62c825fdd416cc87633fcbb995f Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Fri, 28 Jun 2024 15:57:05 +0200 Subject: [PATCH 08/51] fix(db)!: Manually track the query type and whether WHERE is empty Signed-off-by: Joas Schilling --- lib/private/DB/QueryBuilder/QueryBuilder.php | 45 ++++++++++++++++---- 1 file changed, 37 insertions(+), 8 deletions(-) diff --git a/lib/private/DB/QueryBuilder/QueryBuilder.php b/lib/private/DB/QueryBuilder/QueryBuilder.php index 9a2e6402451fc..8410be7c9d15f 100644 --- a/lib/private/DB/QueryBuilder/QueryBuilder.php +++ b/lib/private/DB/QueryBuilder/QueryBuilder.php @@ -33,6 +33,18 @@ use Psr\Log\LoggerInterface; class QueryBuilder implements IQueryBuilder { + /** @internal */ + protected const SELECT = 0; + + /** @internal */ + protected const DELETE = 1; + + /** @internal */ + protected const UPDATE = 2; + + /** @internal */ + protected const INSERT = 3; + /** @var ConnectionAdapter */ private $connection; @@ -49,6 +61,8 @@ class QueryBuilder implements IQueryBuilder { /** @var bool */ private $automaticTablePrefix = true; + private bool $nonEmptyWhere = false; + private int $type = self::SELECT; /** @var string */ protected $lastInsertedTable; @@ -147,7 +161,7 @@ public function func() { * @return integer */ public function getType() { - return $this->queryBuilder->getType(); + return $this->type; } /** @@ -254,7 +268,11 @@ public function execute() { ]); } - $result = $this->queryBuilder->execute(); + if ($this->getType() !== self::SELECT) { + $result = $this->queryBuilder->executeStatement(); + } else { + $result = $this->queryBuilder->executeQuery(); + } if (is_int($result)) { return $result; } @@ -262,7 +280,7 @@ public function execute() { } public function executeQuery(): IResult { - if ($this->getType() !== \Doctrine\DBAL\Query\QueryBuilder::SELECT) { + if ($this->getType() !== self::SELECT) { throw new \RuntimeException('Invalid query type, expected SELECT query'); } @@ -280,7 +298,7 @@ public function executeQuery(): IResult { } public function executeStatement(): int { - if ($this->getType() === \Doctrine\DBAL\Query\QueryBuilder::SELECT) { + if ($this->getType() === self::SELECT) { throw new \RuntimeException('Invalid query type, expected INSERT, DELETE or UPDATE statement'); } @@ -472,12 +490,13 @@ public function getMaxResults() { * '@return $this This QueryBuilder instance. */ public function select(...$selects) { + $this->type = self::SELECT; if (count($selects) === 1 && is_array($selects[0])) { $selects = $selects[0]; } $this->queryBuilder->select( - $this->helper->quoteColumnNames($selects) + ...$this->helper->quoteColumnNames($selects) ); return $this; @@ -499,6 +518,7 @@ public function select(...$selects) { * @return $this This QueryBuilder instance. */ public function selectAlias($select, $alias) { + $this->type = self::SELECT; $this->queryBuilder->addSelect( $this->helper->quoteColumnName($select) . ' AS ' . $this->helper->quoteColumnName($alias) ); @@ -520,6 +540,7 @@ public function selectAlias($select, $alias) { * @return $this This QueryBuilder instance. */ public function selectDistinct($select) { + $this->type = self::SELECT; if (!is_array($select)) { $select = [$select]; } @@ -549,12 +570,13 @@ public function selectDistinct($select) { * @return $this This QueryBuilder instance. */ public function addSelect(...$selects) { + $this->type = self::SELECT; if (count($selects) === 1 && is_array($selects[0])) { $selects = $selects[0]; } $this->queryBuilder->addSelect( - $this->helper->quoteColumnNames($selects) + ...$this->helper->quoteColumnNames($selects) ); return $this; @@ -577,6 +599,7 @@ public function addSelect(...$selects) { * @return $this This QueryBuilder instance. */ public function delete($delete = null, $alias = null) { + $this->type = self::DELETE; $this->queryBuilder->delete( $this->getTableName($delete), $alias @@ -602,6 +625,7 @@ public function delete($delete = null, $alias = null) { * @return $this This QueryBuilder instance. */ public function update($update = null, $alias = null) { + $this->type = self::UPDATE; $this->queryBuilder->update( $this->getTableName($update), $alias @@ -630,6 +654,7 @@ public function update($update = null, $alias = null) { * @return $this This QueryBuilder instance. */ public function insert($insert = null) { + $this->type = self::INSERT; $this->queryBuilder->insert( $this->getTableName($insert) ); @@ -826,12 +851,14 @@ public function set($key, $value) { * @return $this This QueryBuilder instance. */ public function where(...$predicates) { - if ($this->getQueryPart('where') !== null && $this->systemConfig->getValue('debug', false)) { + if ($this->nonEmptyWhere && $this->systemConfig->getValue('debug', false)) { // Only logging a warning, not throwing for now. $e = new QueryException('Using where() on non-empty WHERE part, please verify it is intentional to not call andWhere() or orWhere() instead. Otherwise consider creating a new query builder object or call resetQueryPart(\'where\') first.'); $this->logger->warning($e->getMessage(), ['exception' => $e]); } + $this->nonEmptyWhere = true; + call_user_func_array( [$this->queryBuilder, 'where'], $predicates @@ -859,6 +886,7 @@ public function where(...$predicates) { * @see where() */ public function andWhere(...$where) { + $this->nonEmptyWhere = true; call_user_func_array( [$this->queryBuilder, 'andWhere'], $where @@ -886,6 +914,7 @@ public function andWhere(...$where) { * @see where() */ public function orWhere(...$where) { + $this->nonEmptyWhere = true; call_user_func_array( [$this->queryBuilder, 'orWhere'], $where @@ -1268,7 +1297,7 @@ public function createFunction($call) { * @throws \BadMethodCallException When being called before an insert query has been run. */ public function getLastInsertId(): int { - if ($this->getType() === \Doctrine\DBAL\Query\QueryBuilder::INSERT && $this->lastInsertedTable) { + if ($this->getType() === self::INSERT && $this->lastInsertedTable) { // lastInsertId() needs the prefix but no quotes $table = $this->prefixTableName($this->lastInsertedTable); return $this->connection->lastInsertId($table); From e6e1bd8b9352f86305325b5c42044b58eba0b8a0 Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Fri, 28 Jun 2024 15:57:35 +0200 Subject: [PATCH 09/51] fix(db): Fix internal calls to doctrine's fetch functions Signed-off-by: Joas Schilling --- lib/private/DB/ResultAdapter.php | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/lib/private/DB/ResultAdapter.php b/lib/private/DB/ResultAdapter.php index 8b004d471ec8e..95a7620e0ffb8 100644 --- a/lib/private/DB/ResultAdapter.php +++ b/lib/private/DB/ResultAdapter.php @@ -30,14 +30,21 @@ public function closeCursor(): bool { } public function fetch(int $fetchMode = PDO::FETCH_ASSOC) { - return $this->inner->fetch($fetchMode); + return match ($fetchMode) { + PDO::FETCH_ASSOC => $this->inner->fetchAssociative(), + PDO::FETCH_NUM => $this->inner->fetchNumeric(), + PDO::FETCH_COLUMN => $this->inner->fetchOne(), + default => throw new \Exception('Fetch mode needs to be assoc, num or column.'), + }; } 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); + return match ($fetchMode) { + PDO::FETCH_ASSOC => $this->inner->fetchAllAssociative(), + PDO::FETCH_NUM => $this->inner->fetchAllNumeric(), + PDO::FETCH_COLUMN => $this->inner->fetchFirstColumn(), + default => throw new \Exception('Fetch mode needs to be assoc, num or column.'), + }; } public function fetchColumn($columnIndex = 0) { From f6f53a8ca8418946b8e44626906cf17f239b2795 Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Mon, 1 Jul 2024 11:00:24 +0200 Subject: [PATCH 10/51] fix(db)!: Doctrine\DBAL\Types\Type::getName() was removed Signed-off-by: Joas Schilling --- .../Version1011Date20201120125158.php | 2 +- .../Version11300Date20201120141438.php | 2 +- .../Version1002Date20170919123342.php | 4 +-- core/Command/Db/ConvertFilecacheBigInt.php | 2 +- core/Command/Db/ConvertType.php | 2 +- lib/private/DB/Exceptions/DbalException.php | 4 +++ lib/private/DB/MigrationService.php | 8 ++--- lib/public/DB/Exception.php | 7 ++++ lib/public/DB/Types.php | 36 +++++++++++++++++++ lib/public/Migration/BigIntMigration.php | 5 +-- 10 files changed, 60 insertions(+), 12 deletions(-) diff --git a/apps/federatedfilesharing/lib/Migration/Version1011Date20201120125158.php b/apps/federatedfilesharing/lib/Migration/Version1011Date20201120125158.php index 78517e3a29731..bd12722801a66 100644 --- a/apps/federatedfilesharing/lib/Migration/Version1011Date20201120125158.php +++ b/apps/federatedfilesharing/lib/Migration/Version1011Date20201120125158.php @@ -32,7 +32,7 @@ public function changeSchema(IOutput $output, Closure $schemaClosure, array $opt if ($schema->hasTable('federated_reshares')) { $table = $schema->getTable('federated_reshares'); $remoteIdColumn = $table->getColumn('remote_id'); - if ($remoteIdColumn && $remoteIdColumn->getType()->getName() !== Types::STRING) { + if ($remoteIdColumn && Types::getType($remoteIdColumn->getType()) !== Types::STRING) { $remoteIdColumn->setNotnull(false); $remoteIdColumn->setType(Type::getType(Types::STRING)); $remoteIdColumn->setOptions(['length' => 255]); diff --git a/apps/files_sharing/lib/Migration/Version11300Date20201120141438.php b/apps/files_sharing/lib/Migration/Version11300Date20201120141438.php index 21dbb64fef066..4a0448a7d4fb0 100644 --- a/apps/files_sharing/lib/Migration/Version11300Date20201120141438.php +++ b/apps/files_sharing/lib/Migration/Version11300Date20201120141438.php @@ -90,7 +90,7 @@ public function changeSchema(IOutput $output, Closure $schemaClosure, array $opt } else { $table = $schema->getTable('share_external'); $remoteIdColumn = $table->getColumn('remote_id'); - if ($remoteIdColumn && $remoteIdColumn->getType()->getName() !== Types::STRING) { + if ($remoteIdColumn && Types::getType($remoteIdColumn->getType()) !== Types::STRING) { $remoteIdColumn->setNotnull(false); $remoteIdColumn->setType(Type::getType(Types::STRING)); $remoteIdColumn->setOptions(['length' => 255]); diff --git a/apps/twofactor_backupcodes/lib/Migration/Version1002Date20170919123342.php b/apps/twofactor_backupcodes/lib/Migration/Version1002Date20170919123342.php index 2ca390e6eddb6..8c0fb692bbf5c 100644 --- a/apps/twofactor_backupcodes/lib/Migration/Version1002Date20170919123342.php +++ b/apps/twofactor_backupcodes/lib/Migration/Version1002Date20170919123342.php @@ -9,8 +9,8 @@ namespace OCA\TwoFactorBackupCodes\Migration; use Doctrine\DBAL\Types\Type; -use Doctrine\DBAL\Types\Types; use OCP\DB\ISchemaWrapper; +use OCP\DB\Types; use OCP\Migration\IOutput; use OCP\Migration\SimpleMigrationStep; @@ -32,7 +32,7 @@ public function changeSchema(IOutput $output, \Closure $schemaClosure, array $op $column->setDefault(''); $column = $table->getColumn('used'); - if ($column->getType()->getName() !== Types::SMALLINT) { + if (Types::getType($column->getType()) !== Types::SMALLINT) { $column->setType(Type::getType(Types::SMALLINT)); $column->setOptions(['length' => 6]); } diff --git a/core/Command/Db/ConvertFilecacheBigInt.php b/core/Command/Db/ConvertFilecacheBigInt.php index 3f70cef3d79aa..7e47e3fa753be 100644 --- a/core/Command/Db/ConvertFilecacheBigInt.php +++ b/core/Command/Db/ConvertFilecacheBigInt.php @@ -68,7 +68,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int $column = $table->getColumn($columnName); $isAutoIncrement = $column->getAutoincrement(); $isAutoIncrementOnSqlite = $isSqlite && $isAutoIncrement; - if ($column->getType()->getName() !== Types::BIGINT && !$isAutoIncrementOnSqlite) { + if (Types::getType($column->getType()) !== Types::BIGINT && !$isAutoIncrementOnSqlite) { $column->setType(Type::getType(Types::BIGINT)); $column->setOptions(['length' => 20]); diff --git a/core/Command/Db/ConvertType.php b/core/Command/Db/ConvertType.php index 333f29625f62f..bba20220a3dab 100644 --- a/core/Command/Db/ConvertType.php +++ b/core/Command/Db/ConvertType.php @@ -362,7 +362,7 @@ protected function getColumnType(Table $table, $columnName) { return $this->columnTypes[$tableName][$columnName]; } - $type = $table->getColumn($columnName)->getType()->getName(); + $type = Types::getType($table->getColumn($columnName)->getType()); switch ($type) { case Types::BLOB: diff --git a/lib/private/DB/Exceptions/DbalException.php b/lib/private/DB/Exceptions/DbalException.php index 05ea9e22a5d42..341493ba54472 100644 --- a/lib/private/DB/Exceptions/DbalException.php +++ b/lib/private/DB/Exceptions/DbalException.php @@ -24,6 +24,7 @@ use Doctrine\DBAL\Exception\ServerException; use Doctrine\DBAL\Exception\SyntaxErrorException; use Doctrine\DBAL\Exception\UniqueConstraintViolationException; +use Doctrine\DBAL\Types\Exception\TypeNotRegistered; use OCP\DB\Exception; /** @@ -83,6 +84,9 @@ public function getReason(): ?int { /** * Other server errors */ + if ($this->original instanceof TypeNotRegistered) { + return parent::REASON_TYPE_UNKNOWN; + } if ($this->original instanceof LockWaitTimeoutException) { return parent::REASON_LOCK_WAIT_TIMEOUT; } diff --git a/lib/private/DB/MigrationService.php b/lib/private/DB/MigrationService.php index 19d1b2407369b..ca326abb5bbfa 100644 --- a/lib/private/DB/MigrationService.php +++ b/lib/private/DB/MigrationService.php @@ -13,13 +13,13 @@ use Doctrine\DBAL\Schema\SchemaException; use Doctrine\DBAL\Schema\Sequence; use Doctrine\DBAL\Schema\Table; -use Doctrine\DBAL\Types\Types; use OC\App\InfoParser; use OC\IntegrityCheck\Helpers\AppLocator; use OC\Migration\SimpleOutput; use OCP\AppFramework\App; use OCP\AppFramework\QueryException; use OCP\DB\ISchemaWrapper; +use OCP\DB\Types; use OCP\Migration\IMigrationStep; use OCP\Migration\IOutput; use OCP\Server; @@ -565,7 +565,7 @@ public function ensureOracleConstraints(Schema $sourceSchema, Schema $targetSche throw new \InvalidArgumentException('Column "' . $table->getName() . '"."' . $thing->getName() . '" is NotNull, but has empty string or null as default.'); } - if ($thing->getNotnull() && $thing->getType()->getName() === Types::BOOLEAN) { + if ($thing->getNotnull() && Types::getType($thing->getType()) === Types::BOOLEAN) { throw new \InvalidArgumentException('Column "' . $table->getName() . '"."' . $thing->getName() . '" is type Bool and also NotNull, so it can not store "false".'); } @@ -576,8 +576,8 @@ public function ensureOracleConstraints(Schema $sourceSchema, Schema $targetSche // If the column was just created OR the length changed OR the type changed // we will NOT detect invalid length if the column is not modified - if (($sourceColumn === null || $sourceColumn->getLength() !== $thing->getLength() || $sourceColumn->getType()->getName() !== Types::STRING) - && $thing->getLength() > 4000 && $thing->getType()->getName() === Types::STRING) { + if (($sourceColumn === null || $sourceColumn->getLength() !== $thing->getLength() || Types::getType($sourceColumn->getType()) !== Types::STRING) + && $thing->getLength() > 4000 && Types::getType($thing->getType()) === Types::STRING) { throw new \InvalidArgumentException('Column "' . $table->getName() . '"."' . $thing->getName() . '" is type String, but exceeding the 4.000 length limit.'); } } diff --git a/lib/public/DB/Exception.php b/lib/public/DB/Exception.php index 6b908382aeab6..b77dca9f0f177 100644 --- a/lib/public/DB/Exception.php +++ b/lib/public/DB/Exception.php @@ -127,6 +127,13 @@ class Exception extends BaseException { */ public const REASON_LOCK_WAIT_TIMEOUT = 15; + /** + * The lock wait timeout was exceeded + * + * @since 30.0.0 + */ + public const REASON_TYPE_UNKNOWN = 16; + /** * @return int|null * @psalm-return Exception::REASON_* diff --git a/lib/public/DB/Types.php b/lib/public/DB/Types.php index 414d81a24c8b2..5a496c132d3a6 100644 --- a/lib/public/DB/Types.php +++ b/lib/public/DB/Types.php @@ -8,6 +8,10 @@ */ namespace OCP\DB; +use Doctrine\DBAL\Types\Exception\TypeNotRegistered; +use Doctrine\DBAL\Types\Type; +use OC\DB\Exceptions\DbalException; + /** * Database types supported by Nextcloud's DBs * @@ -99,4 +103,36 @@ final class Types { * @since 24.0.0 */ public const JSON = 'json'; + + /** + * @param Type $type + * @return string + * @throws Exception + * @since 30.0.0 + */ + public static function getType(Type $type): string { + try { + $doctrineType = $type->getTypeRegistry()->lookupName($type); + } catch (\Doctrine\DBAL\Exception $e) { + throw DbalException::wrap($e); + } + + return match ($doctrineType) { + self::BIGINT, + self::BINARY, + self::BLOB, + self::BOOLEAN, + self::DATE, + self::DATETIME, + self::DECIMAL, + self::FLOAT, + self::INTEGER, + self::SMALLINT, + self::STRING, + self::TEXT, + self::TIME, + self::JSON => $doctrineType, + default => throw DbalException::wrap(new TypeNotRegistered(sprintf('Type of the class %s is not registered.', $doctrineType))), + }; + } } diff --git a/lib/public/Migration/BigIntMigration.php b/lib/public/Migration/BigIntMigration.php index db50147b28ec8..f16deb0590ff4 100644 --- a/lib/public/Migration/BigIntMigration.php +++ b/lib/public/Migration/BigIntMigration.php @@ -1,5 +1,6 @@ getColumn($columnName); - if ($column->getType()->getName() !== Types::BIGINT) { + if (Types::getType($column->getType()) !== Types::BIGINT) { $column->setType(Type::getType(Types::BIGINT)); $column->setOptions(['length' => 20]); } From 27c49e7a9aec89ea6f6bb1d713a4ac66661ff4ac Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Mon, 1 Jul 2024 11:05:59 +0200 Subject: [PATCH 11/51] fix(db)!: Table::changeColumn() was renamed to Table::modifyColumn() Signed-off-by: Joas Schilling --- .../lib/Migration/Version1004Date20170825134824.php | 4 ++-- .../lib/Migration/Version1011Date20200630192246.php | 2 +- .../lib/Migration/Version2200Date20210805101925.php | 2 +- core/Migrations/Version21000Date20201120141228.php | 10 +++++----- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/apps/dav/lib/Migration/Version1004Date20170825134824.php b/apps/dav/lib/Migration/Version1004Date20170825134824.php index 54c4c1947784c..3810c613c8dc3 100644 --- a/apps/dav/lib/Migration/Version1004Date20170825134824.php +++ b/apps/dav/lib/Migration/Version1004Date20170825134824.php @@ -233,7 +233,7 @@ public function changeSchema(IOutput $output, \Closure $schemaClosure, array $op $table->addUniqueIndex(['principaluri', 'uri'], 'calendars_index'); } else { $table = $schema->getTable('calendars'); - $table->changeColumn('components', [ + $table->modifyColumn('components', [ 'notnull' => false, 'length' => 64, ]); @@ -323,7 +323,7 @@ public function changeSchema(IOutput $output, \Closure $schemaClosure, array $op $table->addUniqueIndex(['principaluri', 'uri'], 'calsub_index'); } else { $table = $schema->getTable('calendarsubscriptions'); - $table->changeColumn('lastmodified', [ + $table->modifyColumn('lastmodified', [ 'notnull' => false, 'unsigned' => true, ]); diff --git a/apps/files_external/lib/Migration/Version1011Date20200630192246.php b/apps/files_external/lib/Migration/Version1011Date20200630192246.php index c87b1cfbc8be9..dffbd70aa5868 100644 --- a/apps/files_external/lib/Migration/Version1011Date20200630192246.php +++ b/apps/files_external/lib/Migration/Version1011Date20200630192246.php @@ -103,7 +103,7 @@ public function changeSchema(IOutput $output, Closure $schemaClosure, array $opt $table->addUniqueIndex(['mount_id', 'key'], 'config_mount_key'); } else { $table = $schema->getTable('external_config'); - $table->changeColumn('value', [ + $table->modifyColumn('value', [ 'notnull' => false, 'length' => 4000, ]); diff --git a/apps/workflowengine/lib/Migration/Version2200Date20210805101925.php b/apps/workflowengine/lib/Migration/Version2200Date20210805101925.php index 841277acfcebc..f924b7ae8ef02 100644 --- a/apps/workflowengine/lib/Migration/Version2200Date20210805101925.php +++ b/apps/workflowengine/lib/Migration/Version2200Date20210805101925.php @@ -27,7 +27,7 @@ public function changeSchema(IOutput $output, Closure $schemaClosure, array $opt if ($schema->hasTable('flow_operations')) { $table = $schema->getTable('flow_operations'); - $table->changeColumn('name', [ + $table->modifyColumn('name', [ 'notnull' => false, ]); } diff --git a/core/Migrations/Version21000Date20201120141228.php b/core/Migrations/Version21000Date20201120141228.php index ac20cbfd577af..feba7c469bb4a 100644 --- a/core/Migrations/Version21000Date20201120141228.php +++ b/core/Migrations/Version21000Date20201120141228.php @@ -24,16 +24,16 @@ public function changeSchema(IOutput $output, Closure $schemaClosure, array $opt if ($loginNameColumn->getLength() !== 255) { $loginNameColumn->setLength(255); } - $table->changeColumn('type', [ + $table->modifyColumn('type', [ 'notnull' => false, ]); - $table->changeColumn('remember', [ + $table->modifyColumn('remember', [ 'notnull' => false, ]); - $table->changeColumn('last_activity', [ + $table->modifyColumn('last_activity', [ 'notnull' => false, ]); - $table->changeColumn('last_check', [ + $table->modifyColumn('last_check', [ 'notnull' => false, ]); } @@ -51,7 +51,7 @@ public function changeSchema(IOutput $output, Closure $schemaClosure, array $opt if ($schema->hasTable('jobs')) { $table = $schema->getTable('jobs'); - $table->changeColumn('execution_duration', [ + $table->modifyColumn('execution_duration', [ 'notnull' => false, 'default' => 0, ]); From 466f455e9305de6e3f4d747e8fe08bf349482e36 Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Mon, 1 Jul 2024 11:09:24 +0200 Subject: [PATCH 12/51] fix(db)!: `Doctrine\DBAL\Schema\Table::hasPrimaryKey() was removed, use `Table::getPrimaryKey` instead. Signed-off-by: Joas Schilling --- core/Command/Db/AddMissingPrimaryKeys.php | 2 +- lib/private/DB/MigrationService.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/core/Command/Db/AddMissingPrimaryKeys.php b/core/Command/Db/AddMissingPrimaryKeys.php index 073ce7538ccc0..539720cc639f0 100644 --- a/core/Command/Db/AddMissingPrimaryKeys.php +++ b/core/Command/Db/AddMissingPrimaryKeys.php @@ -55,7 +55,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int foreach ($missingKeys as $missingKey) { if ($schema->hasTable($missingKey['tableName'])) { $table = $schema->getTable($missingKey['tableName']); - if (!$table->hasPrimaryKey()) { + if (!$table->getPrimaryKey()) { $output->writeln('Adding primary key to the ' . $missingKey['tableName'] . ' table, this can take some time...'); $table->setPrimaryKey($missingKey['columns'], $missingKey['primaryKeyName']); diff --git a/lib/private/DB/MigrationService.php b/lib/private/DB/MigrationService.php index ca326abb5bbfa..54257025de313 100644 --- a/lib/private/DB/MigrationService.php +++ b/lib/private/DB/MigrationService.php @@ -595,7 +595,7 @@ public function ensureOracleConstraints(Schema $sourceSchema, Schema $targetSche } $primaryKey = $table->getPrimaryKey(); - if ($primaryKey instanceof Index && (!$sourceTable instanceof Table || !$sourceTable->hasPrimaryKey())) { + if ($primaryKey instanceof Index && (!$sourceTable instanceof Table || !$sourceTable->getPrimaryKey())) { $indexName = strtolower($primaryKey->getName()); $isUsingDefaultName = $indexName === 'primary'; From 287d5271e0eed4327f8ef189bea016d7bf092781 Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Mon, 1 Jul 2024 11:17:38 +0200 Subject: [PATCH 13/51] fix(db)!: `Doctrine\DBAL\Connection::getSchemaManager` was removed, use `Connection::createSchemaManager` instead Signed-off-by: Joas Schilling --- core/Command/Db/ConvertType.php | 4 ++-- lib/private/DB/Connection.php | 2 +- lib/private/DB/OracleConnection.php | 4 ++-- lib/private/DB/PgSqlTools.php | 2 +- tests/lib/Repair/RepairCollationTest.php | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/core/Command/Db/ConvertType.php b/core/Command/Db/ConvertType.php index bba20220a3dab..961f294e40ee0 100644 --- a/core/Command/Db/ConvertType.php +++ b/core/Command/Db/ConvertType.php @@ -245,7 +245,7 @@ protected function clearSchema(Connection $db, InputInterface $input, OutputInte $output->writeln('Clearing schema in new database'); } foreach ($toTables as $table) { - $db->getSchemaManager()->dropTable($table); + $db->createSchemaManager()->dropTable($table); } } @@ -258,7 +258,7 @@ protected function getTables(Connection $db) { } return preg_match($filterExpression, $asset) !== false; }); - return $db->getSchemaManager()->listTableNames(); + return $db->createSchemaManager()->listTableNames(); } /** diff --git a/lib/private/DB/Connection.php b/lib/private/DB/Connection.php index 92ed44e606251..365ef75dc3d3c 100644 --- a/lib/private/DB/Connection.php +++ b/lib/private/DB/Connection.php @@ -548,7 +548,7 @@ public function errorInfo() { */ public function dropTable($table) { $table = $this->tablePrefix . trim($table); - $schema = $this->getSchemaManager(); + $schema = $this->createSchemaManager(); if ($schema->tablesExist([$table])) { $schema->dropTable($table); } diff --git a/lib/private/DB/OracleConnection.php b/lib/private/DB/OracleConnection.php index abfb69f129b9f..5ffb65d801dbe 100644 --- a/lib/private/DB/OracleConnection.php +++ b/lib/private/DB/OracleConnection.php @@ -68,7 +68,7 @@ public function delete($table, array $criteria, array $types = []) { public function dropTable($table) { $table = $this->tablePrefix . trim($table); $table = $this->quoteIdentifier($table); - $schema = $this->getSchemaManager(); + $schema = $this->createSchemaManager(); if ($schema->tablesExist([$table])) { $schema->dropTable($table); } @@ -83,7 +83,7 @@ public function dropTable($table) { public function tableExists($table) { $table = $this->tablePrefix . trim($table); $table = $this->quoteIdentifier($table); - $schema = $this->getSchemaManager(); + $schema = $this->createSchemaManager(); return $schema->tablesExist([$table]); } } diff --git a/lib/private/DB/PgSqlTools.php b/lib/private/DB/PgSqlTools.php index 35e8016191c74..d529cb26b09d5 100644 --- a/lib/private/DB/PgSqlTools.php +++ b/lib/private/DB/PgSqlTools.php @@ -43,7 +43,7 @@ public function resynchronizeDatabaseSequences(Connection $conn) { return preg_match($filterExpression, $asset) !== false; }); - foreach ($conn->getSchemaManager()->listSequences() as $sequence) { + foreach ($conn->createSchemaManager()->listSequences() as $sequence) { $sequenceName = $sequence->getName(); $sqlInfo = 'SELECT table_schema, table_name, column_name FROM information_schema.columns diff --git a/tests/lib/Repair/RepairCollationTest.php b/tests/lib/Repair/RepairCollationTest.php index 55bda8337c978..5f5e0737f77bd 100644 --- a/tests/lib/Repair/RepairCollationTest.php +++ b/tests/lib/Repair/RepairCollationTest.php @@ -73,7 +73,7 @@ protected function setUp(): void { } protected function tearDown(): void { - $this->connection->getInner()->getSchemaManager()->dropTable($this->tableName); + $this->connection->getInner()->createSchemaManager()->dropTable($this->tableName); parent::tearDown(); } From 3c5acbcb1aa220e44873bd8a8f9cebc66e9ae25c Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Mon, 1 Jul 2024 11:23:02 +0200 Subject: [PATCH 14/51] fix(db): `Doctrine\DBAL\Connection::executeUpdate()` was removed Signed-off-by: Joas Schilling --- lib/private/DB/Connection.php | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/lib/private/DB/Connection.php b/lib/private/DB/Connection.php index 365ef75dc3d3c..0b65745e473c2 100644 --- a/lib/private/DB/Connection.php +++ b/lib/private/DB/Connection.php @@ -305,10 +305,7 @@ private function getQueriedTables(string $sql): array { * @throws Exception */ public function executeUpdate(string $sql, array $params = [], array $types = []): int { - $sql = $this->finishQuery($sql); - $this->queriesExecuted++; - $this->logQueryToFile($sql); - return parent::executeUpdate($sql, $params, $types); + return $this->executeStatement($sql, $params, $types); } /** From f45ac5bb6862be84e4b22730ee55616b9e8e9930 Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Mon, 1 Jul 2024 11:24:06 +0200 Subject: [PATCH 15/51] fix(db): `Doctrine\DBAL\Query\Expression\ExpressionBuilder::andX()` and `::orX()` were removed Signed-off-by: Joas Schilling --- .../DB/QueryBuilder/ExpressionBuilder/ExpressionBuilder.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/private/DB/QueryBuilder/ExpressionBuilder/ExpressionBuilder.php b/lib/private/DB/QueryBuilder/ExpressionBuilder/ExpressionBuilder.php index a7af00b9f9777..f2451bfac7403 100644 --- a/lib/private/DB/QueryBuilder/ExpressionBuilder/ExpressionBuilder.php +++ b/lib/private/DB/QueryBuilder/ExpressionBuilder/ExpressionBuilder.php @@ -63,7 +63,7 @@ public function __construct(ConnectionAdapter $connection, IQueryBuilder $queryB * @return \OCP\DB\QueryBuilder\ICompositeExpression */ public function andX(...$x): ICompositeExpression { - $compositeExpression = call_user_func_array([$this->expressionBuilder, 'andX'], $x); + $compositeExpression = call_user_func_array([$this->expressionBuilder, 'and'], $x); return new CompositeExpression($compositeExpression); } @@ -82,7 +82,7 @@ public function andX(...$x): ICompositeExpression { * @return \OCP\DB\QueryBuilder\ICompositeExpression */ public function orX(...$x): ICompositeExpression { - $compositeExpression = call_user_func_array([$this->expressionBuilder, 'orX'], $x); + $compositeExpression = call_user_func_array([$this->expressionBuilder, 'or'], $x); return new CompositeExpression($compositeExpression); } From 72151b550c2218f53a817bdd353306a4356b0258 Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Mon, 1 Jul 2024 12:33:37 +0200 Subject: [PATCH 16/51] fix(db): `Table::getPrimaryKeyColumns()` was removed Signed-off-by: Joas Schilling --- core/Command/Db/ConvertType.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/core/Command/Db/ConvertType.php b/core/Command/Db/ConvertType.php index 961f294e40ee0..2d4cf4f9af620 100644 --- a/core/Command/Db/ConvertType.php +++ b/core/Command/Db/ConvertType.php @@ -9,6 +9,7 @@ use Doctrine\DBAL\Exception; use Doctrine\DBAL\Schema\AbstractAsset; +use Doctrine\DBAL\Schema\Index; use Doctrine\DBAL\Schema\Table; use OC\DB\Connection; use OC\DB\ConnectionFactory; @@ -302,7 +303,12 @@ protected function copyTable(Connection $fromDB, Connection $toDB, Table $table, ->setMaxResults($chunkSize); try { - $orderColumns = $table->getPrimaryKeyColumns(); + $key = $table->getPrimaryKey(); + if ($key instanceof Index) { + $orderColumns = $key->getColumns(); + } else { + $orderColumns = $table->getColumns(); + } } catch (Exception $e) { $orderColumns = $table->getColumns(); } From a20110bdc4da96cf4416005a7a74a30398a13a65 Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Mon, 1 Jul 2024 12:39:02 +0200 Subject: [PATCH 17/51] fix(db): UndefinedConstant: Constant Doctrine\DBAL\Connection::PARAM_STR_ARRAY is not defined Signed-off-by: Joas Schilling --- apps/dav/lib/DAV/CustomPropertiesBackend.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/dav/lib/DAV/CustomPropertiesBackend.php b/apps/dav/lib/DAV/CustomPropertiesBackend.php index ab62ae36c2cc4..f4dd9b2d03857 100644 --- a/apps/dav/lib/DAV/CustomPropertiesBackend.php +++ b/apps/dav/lib/DAV/CustomPropertiesBackend.php @@ -411,7 +411,7 @@ private function getUserProperties(string $path, array $requestedProperties) { // request only a subset $sql .= ' AND `propertyname` in (?)'; $whereValues[] = $requestedProperties; - $whereTypes[] = \Doctrine\DBAL\Connection::PARAM_STR_ARRAY; + $whereTypes[] = IQueryBuilder::PARAM_STR_ARRAY; } $result = $this->connection->executeQuery( From c0d2f7ee214023f7aea713b459388342c23883f9 Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Mon, 1 Jul 2024 12:41:55 +0200 Subject: [PATCH 18/51] fix(CI): Operand of type Doctrine\DBAL\Schema\Column is always truthy Signed-off-by: Joas Schilling --- .../lib/Migration/Version1011Date20201120125158.php | 2 +- .../lib/Migration/Version11300Date20201120141438.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/federatedfilesharing/lib/Migration/Version1011Date20201120125158.php b/apps/federatedfilesharing/lib/Migration/Version1011Date20201120125158.php index bd12722801a66..8e9c6dbb2bf28 100644 --- a/apps/federatedfilesharing/lib/Migration/Version1011Date20201120125158.php +++ b/apps/federatedfilesharing/lib/Migration/Version1011Date20201120125158.php @@ -32,7 +32,7 @@ public function changeSchema(IOutput $output, Closure $schemaClosure, array $opt if ($schema->hasTable('federated_reshares')) { $table = $schema->getTable('federated_reshares'); $remoteIdColumn = $table->getColumn('remote_id'); - if ($remoteIdColumn && Types::getType($remoteIdColumn->getType()) !== Types::STRING) { + if (Types::getType($remoteIdColumn->getType()) !== Types::STRING) { $remoteIdColumn->setNotnull(false); $remoteIdColumn->setType(Type::getType(Types::STRING)); $remoteIdColumn->setOptions(['length' => 255]); diff --git a/apps/files_sharing/lib/Migration/Version11300Date20201120141438.php b/apps/files_sharing/lib/Migration/Version11300Date20201120141438.php index 4a0448a7d4fb0..6c7f323a8f75a 100644 --- a/apps/files_sharing/lib/Migration/Version11300Date20201120141438.php +++ b/apps/files_sharing/lib/Migration/Version11300Date20201120141438.php @@ -90,7 +90,7 @@ public function changeSchema(IOutput $output, Closure $schemaClosure, array $opt } else { $table = $schema->getTable('share_external'); $remoteIdColumn = $table->getColumn('remote_id'); - if ($remoteIdColumn && Types::getType($remoteIdColumn->getType()) !== Types::STRING) { + if (Types::getType($remoteIdColumn->getType()) !== Types::STRING) { $remoteIdColumn->setNotnull(false); $remoteIdColumn->setType(Type::getType(Types::STRING)); $remoteIdColumn->setOptions(['length' => 255]); From fb3d4d579f4f9aa92e398fc2a04bb311b3d9cd05 Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Mon, 1 Jul 2024 12:46:54 +0200 Subject: [PATCH 19/51] fix(db): Don't use removed/deprecated methods in setup Signed-off-by: Joas Schilling --- lib/private/Setup/MySQL.php | 12 ++++++------ lib/private/Setup/PostgreSQL.php | 14 +++++++------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/lib/private/Setup/MySQL.php b/lib/private/Setup/MySQL.php index 93aee667d18da..8c0240356cef7 100644 --- a/lib/private/Setup/MySQL.php +++ b/lib/private/Setup/MySQL.php @@ -66,7 +66,7 @@ private function createDatabase($connection) { //we can't use OC_DB functions here because we need to connect as the administrative user. $characterSet = $this->config->getValue('mysql.utf8mb4', false) ? 'utf8mb4' : 'utf8'; $query = "CREATE DATABASE IF NOT EXISTS `$name` CHARACTER SET $characterSet COLLATE {$characterSet}_bin;"; - $connection->executeUpdate($query); + $connection->executeStatement($query); } catch (\Exception $ex) { $this->logger->error('Database creation failed.', [ 'exception' => $ex, @@ -78,7 +78,7 @@ private function createDatabase($connection) { try { //this query will fail if there aren't the right permissions, ignore the error $query = "GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, REFERENCES, INDEX, ALTER, CREATE TEMPORARY TABLES, LOCK TABLES, EXECUTE, CREATE VIEW, SHOW VIEW, CREATE ROUTINE, ALTER ROUTINE, EVENT, TRIGGER ON `$name` . * TO '$user'"; - $connection->executeUpdate($query); + $connection->executeStatement($query); } catch (\Exception $ex) { $this->logger->debug('Could not automatically grant privileges, this can be ignored if database user already had privileges.', [ 'exception' => $ex, @@ -100,14 +100,14 @@ private function createDBUser($connection) { if ($connection->getDatabasePlatform() instanceof Mysql80Platform) { $query = "CREATE USER '$name'@'localhost' IDENTIFIED WITH mysql_native_password BY '$password'"; - $connection->executeUpdate($query); + $connection->executeStatement($query); $query = "CREATE USER '$name'@'%' IDENTIFIED WITH mysql_native_password BY '$password'"; - $connection->executeUpdate($query); + $connection->executeStatement($query); } else { $query = "CREATE USER '$name'@'localhost' IDENTIFIED BY '$password'"; - $connection->executeUpdate($query); + $connection->executeStatement($query); $query = "CREATE USER '$name'@'%' IDENTIFIED BY '$password'"; - $connection->executeUpdate($query); + $connection->executeStatement($query); } } catch (\Exception $ex) { $this->logger->error('Database user creation failed.', [ diff --git a/lib/private/Setup/PostgreSQL.php b/lib/private/Setup/PostgreSQL.php index 4ece8957ce63a..6f979c7ae672c 100644 --- a/lib/private/Setup/PostgreSQL.php +++ b/lib/private/Setup/PostgreSQL.php @@ -35,7 +35,7 @@ public function setupDatabase($username) { ->andWhere($builder->expr()->eq('rolname', $builder->createNamedParameter($this->dbUser))); try { - $result = $query->execute(); + $result = $query->executeQuery(); $canCreateRoles = $result->rowCount() > 0; } catch (DatabaseException $e) { $canCreateRoles = false; @@ -103,7 +103,7 @@ private function createDatabase(Connection $connection) { //The database does not exists... let's create it $query = $connection->prepare("CREATE DATABASE " . addslashes($this->dbName) . " OWNER \"" . addslashes($this->dbUser) . '"'); try { - $query->execute(); + $query->executeStatement(); } catch (DatabaseException $e) { $this->logger->error('Error while trying to create database', [ 'exception' => $e, @@ -112,7 +112,7 @@ private function createDatabase(Connection $connection) { } else { $query = $connection->prepare("REVOKE ALL PRIVILEGES ON DATABASE " . addslashes($this->dbName) . " FROM PUBLIC"); try { - $query->execute(); + $query->executeStatement(); } catch (DatabaseException $e) { $this->logger->error('Error while trying to restrict database permissions', [ 'exception' => $e, @@ -127,7 +127,7 @@ private function userExists(Connection $connection) { $query = $builder->select('*') ->from('pg_roles') ->where($builder->expr()->eq('rolname', $builder->createNamedParameter($this->dbUser))); - $result = $query->execute(); + $result = $query->executeQuery(); return $result->rowCount() > 0; } @@ -137,7 +137,7 @@ private function databaseExists(Connection $connection) { $query = $builder->select('datname') ->from('pg_database') ->where($builder->expr()->eq('datname', $builder->createNamedParameter($this->dbName))); - $result = $query->execute(); + $result = $query->executeQuery(); return $result->rowCount() > 0; } @@ -152,10 +152,10 @@ private function createDBUser(Connection $connection) { // create the user $query = $connection->prepare("CREATE USER \"" . addslashes($this->dbUser) . "\" CREATEDB PASSWORD '" . addslashes($this->dbPassword) . "'"); - $query->execute(); + $query->executeStatement(); if ($this->databaseExists($connection)) { $query = $connection->prepare('GRANT CONNECT ON DATABASE ' . addslashes($this->dbName) . ' TO "' . addslashes($this->dbUser) . '"'); - $query->execute(); + $query->executeStatement(); } } catch (DatabaseException $e) { $this->logger->error('Error while trying to create database user', [ From 2d27a328702bd2ce3c9260ced84af6a538759dec Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Mon, 1 Jul 2024 12:54:47 +0200 Subject: [PATCH 20/51] fix(db): InaccessibleMethod: Cannot access private method Doctrine\DBAL\Statement::execute from context OC\DB\PreparedStatement Signed-off-by: Joas Schilling --- lib/private/DB/PreparedStatement.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/private/DB/PreparedStatement.php b/lib/private/DB/PreparedStatement.php index 5fdfa2b03e894..9ff02c105e33d 100644 --- a/lib/private/DB/PreparedStatement.php +++ b/lib/private/DB/PreparedStatement.php @@ -66,7 +66,7 @@ public function bindParam($param, &$variable, $type = ParameterType::STRING, $le } public function execute($params = null): IResult { - return ($this->result = new ResultAdapter($this->statement->execute($params))); + return ($this->result = new ResultAdapter($this->statement->executeQuery($params))); } public function rowCount(): int { @@ -78,6 +78,6 @@ private function getResult(): IResult { return $this->result; } - throw new Exception("You have to execute the prepared statement before accessing the results"); + throw new \Exception("You have to execute the prepared statement before accessing the results"); } } From d9e69e14f3aaa30e1b487a51eb1e1cd594173293 Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Mon, 1 Jul 2024 16:49:42 +0200 Subject: [PATCH 21/51] fix(db)!: Table alias for DELETE and UPDATE no longer supported Signed-off-by: Joas Schilling --- lib/private/DB/QueryBuilder/QueryBuilder.php | 12 ++++++++++-- lib/public/DB/QueryBuilder/IQueryBuilder.php | 13 ++++++++----- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/lib/private/DB/QueryBuilder/QueryBuilder.php b/lib/private/DB/QueryBuilder/QueryBuilder.php index 8410be7c9d15f..66ed88f03c8c2 100644 --- a/lib/private/DB/QueryBuilder/QueryBuilder.php +++ b/lib/private/DB/QueryBuilder/QueryBuilder.php @@ -597,12 +597,16 @@ public function addSelect(...$selects) { * @param string $alias The table alias used in the constructed query. * * @return $this This QueryBuilder instance. + * @since 30.0.0 Alias is no longer supported */ public function delete($delete = null, $alias = null) { + if ($alias !== null) { + $this->logger->debug('DELETE queries with alias are no longer supported and the provided alias is ignored', ['exception' => new \InvalidArgumentException('Table alias provided for DELETE query')]); + } + $this->type = self::DELETE; $this->queryBuilder->delete( $this->getTableName($delete), - $alias ); return $this; @@ -623,12 +627,16 @@ public function delete($delete = null, $alias = null) { * @param string $alias The table alias used in the constructed query. * * @return $this This QueryBuilder instance. + * @since 30.0.0 Alias is no longer supported */ public function update($update = null, $alias = null) { + if ($alias !== null) { + $this->logger->debug('UPDATE queries with alias are no longer supported and the provided alias is ignored', ['exception' => new \InvalidArgumentException('Table alias provided for UPDATE query')]); + } + $this->type = self::UPDATE; $this->queryBuilder->update( $this->getTableName($update), - $alias ); return $this; diff --git a/lib/public/DB/QueryBuilder/IQueryBuilder.php b/lib/public/DB/QueryBuilder/IQueryBuilder.php index 271771b5aa276..7d9d56a1f905a 100644 --- a/lib/public/DB/QueryBuilder/IQueryBuilder.php +++ b/lib/public/DB/QueryBuilder/IQueryBuilder.php @@ -391,8 +391,8 @@ public function addSelect(...$select); * * * $qb = $conn->getQueryBuilder() - * ->delete('users', 'u') - * ->where('u.id = :user_id'); + * ->delete('users') + * ->where('id = :user_id'); * ->setParameter(':user_id', 1); * * @@ -401,6 +401,7 @@ public function addSelect(...$select); * * @return $this This QueryBuilder instance. * @since 8.2.0 + * @since 30.0.0 Alias is no longer supported * * @psalm-taint-sink sql $delete */ @@ -412,9 +413,10 @@ public function delete($delete = null, $alias = null); * * * $qb = $conn->getQueryBuilder() - * ->update('users', 'u') - * ->set('u.password', md5('password')) - * ->where('u.id = ?'); + * ->update('users') + * ->set('email', ':email') + * ->where('id = :user_id'); + * ->setParameter(':user_id', 1); * * * @param string $update The table whose rows are subject to the update. @@ -422,6 +424,7 @@ public function delete($delete = null, $alias = null); * * @return $this This QueryBuilder instance. * @since 8.2.0 + * @since 30.0.0 Alias is no longer supported * * @psalm-taint-sink sql $update */ From f6ac69a2a0f692d8d5351e0e323361c77a0a6e82 Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Mon, 1 Jul 2024 16:59:47 +0200 Subject: [PATCH 22/51] fix(db)!: `Doctrine\DBAL\Platforms\PostgreSQL94Platform` was removed Signed-off-by: Joas Schilling --- core/Migrations/Version13000Date20170718121200.php | 3 +-- lib/private/DB/MigrationService.php | 4 ++-- lib/private/DB/QueryBuilder/QueryBuilder.php | 6 +++--- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/core/Migrations/Version13000Date20170718121200.php b/core/Migrations/Version13000Date20170718121200.php index c485e025c3709..3985bdaeb57e6 100644 --- a/core/Migrations/Version13000Date20170718121200.php +++ b/core/Migrations/Version13000Date20170718121200.php @@ -5,7 +5,6 @@ */ namespace OC\Core\Migrations; -use Doctrine\DBAL\Platforms\PostgreSQL94Platform; use OCP\DB\ISchemaWrapper; use OCP\DB\Types; use OCP\IDBConnection; @@ -238,7 +237,7 @@ public function changeSchema(IOutput $output, \Closure $schemaClosure, array $op $table->addIndex(['name'], 'fs_name_hash'); $table->addIndex(['mtime'], 'fs_mtime'); $table->addIndex(['size'], 'fs_size'); - if (!$schema->getDatabasePlatform() instanceof PostgreSQL94Platform) { + if ($this->connection->getDatabaseProvider() !== IDBConnection::PLATFORM_POSTGRES) { $table->addIndex(['storage', 'path'], 'fs_storage_path_prefix', [], ['lengths' => [null, 64]]); } } diff --git a/lib/private/DB/MigrationService.php b/lib/private/DB/MigrationService.php index 54257025de313..7bf074fb51da1 100644 --- a/lib/private/DB/MigrationService.php +++ b/lib/private/DB/MigrationService.php @@ -7,7 +7,7 @@ namespace OC\DB; use Doctrine\DBAL\Platforms\OraclePlatform; -use Doctrine\DBAL\Platforms\PostgreSQL94Platform; +use Doctrine\DBAL\Platforms\PostgreSQLPlatform; use Doctrine\DBAL\Schema\Index; use Doctrine\DBAL\Schema\Schema; use Doctrine\DBAL\Schema\SchemaException; @@ -599,7 +599,7 @@ public function ensureOracleConstraints(Schema $sourceSchema, Schema $targetSche $indexName = strtolower($primaryKey->getName()); $isUsingDefaultName = $indexName === 'primary'; - if ($this->connection->getDatabasePlatform() instanceof PostgreSQL94Platform) { + if ($this->connection->getDatabasePlatform() instanceof PostgreSQLPlatform) { $defaultName = $table->getName() . '_pkey'; $isUsingDefaultName = strtolower($defaultName) === $indexName; diff --git a/lib/private/DB/QueryBuilder/QueryBuilder.php b/lib/private/DB/QueryBuilder/QueryBuilder.php index 66ed88f03c8c2..48ed7b6871893 100644 --- a/lib/private/DB/QueryBuilder/QueryBuilder.php +++ b/lib/private/DB/QueryBuilder/QueryBuilder.php @@ -9,7 +9,6 @@ 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\ConnectionAdapter; @@ -30,6 +29,7 @@ use OCP\DB\QueryBuilder\IParameter; use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\DB\QueryBuilder\IQueryFunction; +use OCP\IDBConnection; use Psr\Log\LoggerInterface; class QueryBuilder implements IQueryBuilder { @@ -112,7 +112,7 @@ public function expr() { if ($this->connection->getDatabasePlatform() instanceof OraclePlatform) { return new OCIExpressionBuilder($this->connection, $this); } - if ($this->connection->getDatabasePlatform() instanceof PostgreSQL94Platform) { + if ($this->connection->getDatabaseProvider() !== IDBConnection::PLATFORM_POSTGRES) { return new PgSqlExpressionBuilder($this->connection, $this); } if ($this->connection->getDatabasePlatform() instanceof MySQLPlatform) { @@ -148,7 +148,7 @@ public function func() { if ($this->connection->getDatabasePlatform() instanceof SQLitePlatform) { return new SqliteFunctionBuilder($this->connection, $this, $this->helper); } - if ($this->connection->getDatabasePlatform() instanceof PostgreSQL94Platform) { + if ($this->connection->getDatabaseProvider() === IDBConnection::PLATFORM_POSTGRES) { return new PgSqlFunctionBuilder($this->connection, $this, $this->helper); } From c6cd20ea8ae37703fcaa0354e3e2af063896d63f Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Mon, 1 Jul 2024 17:00:21 +0200 Subject: [PATCH 23/51] fix(db)!: `Doctrine\DBAL\FetchMode` was removed Signed-off-by: Joas Schilling --- apps/user_ldap/lib/Mapping/AbstractMapping.php | 2 +- lib/private/DirectEditing/Manager.php | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/apps/user_ldap/lib/Mapping/AbstractMapping.php b/apps/user_ldap/lib/Mapping/AbstractMapping.php index db3a47dfd943a..7dced1ca7c39c 100644 --- a/apps/user_ldap/lib/Mapping/AbstractMapping.php +++ b/apps/user_ldap/lib/Mapping/AbstractMapping.php @@ -202,7 +202,7 @@ protected function prepareListOfIdsQuery(array $hashList): IQueryBuilder { protected function collectResultsFromListOfIdsQuery(IQueryBuilder $qb, array &$results): void { $stmt = $qb->executeQuery(); - while ($entry = $stmt->fetch(\Doctrine\DBAL\FetchMode::ASSOCIATIVE)) { + while ($entry = $stmt->fetch()) { $results[$entry['ldap_dn']] = $entry['owncloud_name']; $this->cache[$entry['ldap_dn']] = $entry['owncloud_name']; } diff --git a/lib/private/DirectEditing/Manager.php b/lib/private/DirectEditing/Manager.php index 4b8c6af31e231..47d7dc4efa789 100644 --- a/lib/private/DirectEditing/Manager.php +++ b/lib/private/DirectEditing/Manager.php @@ -5,7 +5,6 @@ */ namespace OC\DirectEditing; -use Doctrine\DBAL\FetchMode; use OCP\AppFramework\Http\NotFoundResponse; use OCP\AppFramework\Http\Response; use OCP\AppFramework\Http\TemplateResponse; @@ -210,7 +209,7 @@ public function getToken(string $token): IToken { $query->select('*')->from(self::TABLE_TOKENS) ->where($query->expr()->eq('token', $query->createNamedParameter($token, IQueryBuilder::PARAM_STR))); $result = $query->execute(); - if ($tokenRow = $result->fetch(FetchMode::ASSOCIATIVE)) { + if ($tokenRow = $result->fetch()) { return new Token($this, $tokenRow); } throw new \RuntimeException('Failed to validate the token'); From 165443046acebbd610fc087669b2dfbbe4d12170 Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Mon, 1 Jul 2024 17:15:23 +0200 Subject: [PATCH 24/51] fix(db): Interface `Doctrine\DBAL\Exception` cannot be instantiated Signed-off-by: Joas Schilling --- lib/private/DB/Connection.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/private/DB/Connection.php b/lib/private/DB/Connection.php index 0b65745e473c2..9f0fa172f136f 100644 --- a/lib/private/DB/Connection.php +++ b/lib/private/DB/Connection.php @@ -11,6 +11,7 @@ use Doctrine\Common\EventManager; use Doctrine\DBAL\Cache\QueryCacheProfile; use Doctrine\DBAL\Configuration; +use Doctrine\DBAL\ConnectionException; use Doctrine\DBAL\Connections\PrimaryReadReplicaConnection; use Doctrine\DBAL\Driver; use Doctrine\DBAL\Exception; @@ -136,7 +137,7 @@ public function connect(?string $connectionName = null): Driver\Connection { return $status; } catch (Exception $e) { // throw a new exception to prevent leaking info from the stacktrace - throw new Exception('Failed to connect to the database: ' . $e->getMessage(), $e->getCode()); + throw new ConnectionException('Failed to connect to the database: ' . $e->getMessage(), $e->getCode()); } } From 4c1003122d6c08a809e01b3a6ce49271533fd15b Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Mon, 1 Jul 2024 17:19:33 +0200 Subject: [PATCH 25/51] fix(db): `Doctrine\DBAL\Schema\Schema::getTableNames()` was removed Signed-off-by: Joas Schilling --- lib/private/DB/SchemaWrapper.php | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/private/DB/SchemaWrapper.php b/lib/private/DB/SchemaWrapper.php index 5720e10fbdbeb..59cc9e74312e5 100644 --- a/lib/private/DB/SchemaWrapper.php +++ b/lib/private/DB/SchemaWrapper.php @@ -8,6 +8,7 @@ use Doctrine\DBAL\Exception; use Doctrine\DBAL\Platforms\AbstractPlatform; use Doctrine\DBAL\Schema\Schema; +use Doctrine\DBAL\Schema\Table; use OCP\DB\ISchemaWrapper; class SchemaWrapper implements ISchemaWrapper { @@ -43,26 +44,25 @@ public function performDropTableCalls() { /** * Gets all table names * - * @return array + * @return list */ - public function getTableNamesWithoutPrefix() { - $tableNames = $this->schema->getTableNames(); - return array_map(function ($tableName) { + public function getTableNamesWithoutPrefix(): array { + return array_map(function (string $tableName) { if (str_starts_with($tableName, $this->connection->getPrefix())) { return substr($tableName, strlen($this->connection->getPrefix())); } return $tableName; - }, $tableNames); + }, $this->getTableNames()); } // Overwritten methods /** - * @return array + * @return list */ - public function getTableNames() { - return $this->schema->getTableNames(); + public function getTableNames(): array { + return array_map(static fn (Table $table) => $table->getName(), $this->schema->getTables()); } /** From ec1a3d6cd3bbb90ed09defbcfdba20cacd734c71 Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Mon, 1 Jul 2024 17:35:26 +0200 Subject: [PATCH 26/51] fix(db): `Doctrine\DBAL\Query\QueryBuilder::getState` does not exist Signed-off-by: Joas Schilling --- lib/private/DB/QueryBuilder/QueryBuilder.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/private/DB/QueryBuilder/QueryBuilder.php b/lib/private/DB/QueryBuilder/QueryBuilder.php index 48ed7b6871893..f47de8005aa15 100644 --- a/lib/private/DB/QueryBuilder/QueryBuilder.php +++ b/lib/private/DB/QueryBuilder/QueryBuilder.php @@ -176,10 +176,12 @@ public function getConnection() { /** * Gets the state of this query builder instance. * - * @return integer Either QueryBuilder::STATE_DIRTY or QueryBuilder::STATE_CLEAN. + * @return int Always returns 0 which is former `QueryBuilder::STATE_DIRTY` + * @deprecated 30.0.0 Function is no-op because it's removed upstream */ public function getState() { - return $this->queryBuilder->getState(); + $this->logger->debug('Relying on the query builder state is deprecated as it is an internal concern.', ['exception' => new \Exception('Table alias provided for UPDATE query')]); + return 0; } /** From 6e90ddf41a536cefcabe40cf04d3fb89af876a38 Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Mon, 1 Jul 2024 17:36:02 +0200 Subject: [PATCH 27/51] fix(db): `Doctrine\DBAL\Query\Expression\CompositeExpression::add*` does not exist Signed-off-by: Joas Schilling --- lib/private/DB/QueryBuilder/CompositeExpression.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/private/DB/QueryBuilder/CompositeExpression.php b/lib/private/DB/QueryBuilder/CompositeExpression.php index 493d804d54df5..1d5cc2c768fa8 100644 --- a/lib/private/DB/QueryBuilder/CompositeExpression.php +++ b/lib/private/DB/QueryBuilder/CompositeExpression.php @@ -30,7 +30,9 @@ public function __construct(\Doctrine\DBAL\Query\Expression\CompositeExpression * @return \OCP\DB\QueryBuilder\ICompositeExpression */ public function addMultiple(array $parts = []): ICompositeExpression { - $this->compositeExpression->addMultiple($parts); + foreach ($parts as $part) { + $this->add($part); + } return $this; } @@ -43,8 +45,7 @@ public function addMultiple(array $parts = []): ICompositeExpression { * @return \OCP\DB\QueryBuilder\ICompositeExpression */ public function add($part): ICompositeExpression { - $this->compositeExpression->add($part); - + $this->compositeExpression = $this->compositeExpression->with($part); return $this; } From 4bbaae5798017192621b66cb28671bb5c939336c Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Mon, 1 Jul 2024 17:43:16 +0200 Subject: [PATCH 28/51] fix(db): `ExpressionBuilder::like` expects string, but `OCP\DB\QueryBuilder\IQueryFunction` provided Signed-off-by: Joas Schilling --- .../DB/QueryBuilder/ExpressionBuilder/ExpressionBuilder.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/private/DB/QueryBuilder/ExpressionBuilder/ExpressionBuilder.php b/lib/private/DB/QueryBuilder/ExpressionBuilder/ExpressionBuilder.php index f2451bfac7403..db7abdb1444c4 100644 --- a/lib/private/DB/QueryBuilder/ExpressionBuilder/ExpressionBuilder.php +++ b/lib/private/DB/QueryBuilder/ExpressionBuilder/ExpressionBuilder.php @@ -288,7 +288,7 @@ public function like($x, $y, $type = null): string { * @since 9.0.0 */ public function iLike($x, $y, $type = null): string { - return $this->expressionBuilder->like($this->functionBuilder->lower($x), $this->functionBuilder->lower($y)); + return $this->expressionBuilder->like((string) $this->functionBuilder->lower($x), (string) $this->functionBuilder->lower($y)); } /** From 2b83016a2be63db8659d5094fea705484aa28c11 Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Mon, 1 Jul 2024 17:50:12 +0200 Subject: [PATCH 29/51] fix(db): TooManyArguments: Too many arguments for method `Doctrine\DBAL\Statement::executeQuery()` Signed-off-by: Joas Schilling --- lib/private/DB/PreparedStatement.php | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/lib/private/DB/PreparedStatement.php b/lib/private/DB/PreparedStatement.php index 9ff02c105e33d..00a32a629f7e6 100644 --- a/lib/private/DB/PreparedStatement.php +++ b/lib/private/DB/PreparedStatement.php @@ -66,7 +66,17 @@ public function bindParam($param, &$variable, $type = ParameterType::STRING, $le } public function execute($params = null): IResult { - return ($this->result = new ResultAdapter($this->statement->executeQuery($params))); + if ($params !== null) { + foreach ($params as $key => $param) { + if (is_int($key)) { + // Parameter count starts with 1 + $this->bindValue($key + 1, $param); + } else { + $this->bindValue($key, $param); + } + } + } + return ($this->result = new ResultAdapter($this->statement->executeQuery())); } public function rowCount(): int { From 55a8f338731ec0d27512604db9c08747bc12c448 Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Mon, 1 Jul 2024 18:17:15 +0200 Subject: [PATCH 30/51] fix(db): Doctrine Event Manager is removed Signed-off-by: Joas Schilling --- lib/composer/composer/autoload_classmap.php | 6 +-- lib/composer/composer/autoload_static.php | 6 +-- lib/private/DB/Connection.php | 6 +-- lib/private/DB/ConnectionFactory.php | 33 ++++++++---- .../Middlewares/SQLiteCaseSensitiveLike.php | 32 +++++++++++ .../DB/Middlewares/SQLiteJournalMode.php | 34 ++++++++++++ .../SetTransactionIsolationLevel.php | 39 ++++++++++++++ lib/private/DB/OCSqlitePlatform.php | 11 ---- lib/private/DB/SQLiteSessionInit.php | 53 ------------------- .../DB/SetTransactionIsolationLevel.php | 36 ------------- 10 files changed, 137 insertions(+), 119 deletions(-) create mode 100644 lib/private/DB/Middlewares/SQLiteCaseSensitiveLike.php create mode 100644 lib/private/DB/Middlewares/SQLiteJournalMode.php create mode 100644 lib/private/DB/Middlewares/SetTransactionIsolationLevel.php delete mode 100644 lib/private/DB/OCSqlitePlatform.php delete mode 100644 lib/private/DB/SQLiteSessionInit.php delete mode 100644 lib/private/DB/SetTransactionIsolationLevel.php diff --git a/lib/composer/composer/autoload_classmap.php b/lib/composer/composer/autoload_classmap.php index 958223bf2fc0f..01c8671f8ad49 100644 --- a/lib/composer/composer/autoload_classmap.php +++ b/lib/composer/composer/autoload_classmap.php @@ -1327,6 +1327,9 @@ 'OC\\DB\\ConnectionFactory' => $baseDir . '/lib/private/DB/ConnectionFactory.php', 'OC\\DB\\DbDataCollector' => $baseDir . '/lib/private/DB/DbDataCollector.php', 'OC\\DB\\Exceptions\\DbalException' => $baseDir . '/lib/private/DB/Exceptions/DbalException.php', + 'OC\\DB\\Middlewares\\SQLiteCaseSensitiveLike' => $baseDir . '/lib/private/DB/Middlewares/SQLiteCaseSensitiveLike.php', + 'OC\\DB\\Middlewares\\SQLiteJournalMode' => $baseDir . '/lib/private/DB/Middlewares/SQLiteJournalMode.php', + 'OC\\DB\\Middlewares\\SetTransactionIsolationLevel' => $baseDir . '/lib/private/DB/Middlewares/SetTransactionIsolationLevel.php', 'OC\\DB\\MigrationException' => $baseDir . '/lib/private/DB/MigrationException.php', 'OC\\DB\\MigrationService' => $baseDir . '/lib/private/DB/MigrationService.php', 'OC\\DB\\Migrator' => $baseDir . '/lib/private/DB/Migrator.php', @@ -1335,7 +1338,6 @@ 'OC\\DB\\MissingIndexInformation' => $baseDir . '/lib/private/DB/MissingIndexInformation.php', 'OC\\DB\\MissingPrimaryKeyInformation' => $baseDir . '/lib/private/DB/MissingPrimaryKeyInformation.php', 'OC\\DB\\MySqlTools' => $baseDir . '/lib/private/DB/MySqlTools.php', - 'OC\\DB\\OCSqlitePlatform' => $baseDir . '/lib/private/DB/OCSqlitePlatform.php', 'OC\\DB\\ObjectParameter' => $baseDir . '/lib/private/DB/ObjectParameter.php', 'OC\\DB\\OracleConnection' => $baseDir . '/lib/private/DB/OracleConnection.php', 'OC\\DB\\OracleMigrator' => $baseDir . '/lib/private/DB/OracleMigrator.php', @@ -1358,9 +1360,7 @@ 'OC\\DB\\QueryBuilder\\QuoteHelper' => $baseDir . '/lib/private/DB/QueryBuilder/QuoteHelper.php', 'OC\\DB\\ResultAdapter' => $baseDir . '/lib/private/DB/ResultAdapter.php', 'OC\\DB\\SQLiteMigrator' => $baseDir . '/lib/private/DB/SQLiteMigrator.php', - 'OC\\DB\\SQLiteSessionInit' => $baseDir . '/lib/private/DB/SQLiteSessionInit.php', 'OC\\DB\\SchemaWrapper' => $baseDir . '/lib/private/DB/SchemaWrapper.php', - 'OC\\DB\\SetTransactionIsolationLevel' => $baseDir . '/lib/private/DB/SetTransactionIsolationLevel.php', 'OC\\Dashboard\\Manager' => $baseDir . '/lib/private/Dashboard/Manager.php', 'OC\\DatabaseException' => $baseDir . '/lib/private/DatabaseException.php', 'OC\\DatabaseSetupException' => $baseDir . '/lib/private/DatabaseSetupException.php', diff --git a/lib/composer/composer/autoload_static.php b/lib/composer/composer/autoload_static.php index df24fac37657e..fc271f585d700 100644 --- a/lib/composer/composer/autoload_static.php +++ b/lib/composer/composer/autoload_static.php @@ -1360,6 +1360,9 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2 'OC\\DB\\ConnectionFactory' => __DIR__ . '/../../..' . '/lib/private/DB/ConnectionFactory.php', 'OC\\DB\\DbDataCollector' => __DIR__ . '/../../..' . '/lib/private/DB/DbDataCollector.php', 'OC\\DB\\Exceptions\\DbalException' => __DIR__ . '/../../..' . '/lib/private/DB/Exceptions/DbalException.php', + 'OC\\DB\\Middlewares\\SQLiteCaseSensitiveLike' => __DIR__ . '/../../..' . '/lib/private/DB/Middlewares/SQLiteCaseSensitiveLike.php', + 'OC\\DB\\Middlewares\\SQLiteJournalMode' => __DIR__ . '/../../..' . '/lib/private/DB/Middlewares/SQLiteJournalMode.php', + 'OC\\DB\\Middlewares\\SetTransactionIsolationLevel' => __DIR__ . '/../../..' . '/lib/private/DB/Middlewares/SetTransactionIsolationLevel.php', 'OC\\DB\\MigrationException' => __DIR__ . '/../../..' . '/lib/private/DB/MigrationException.php', 'OC\\DB\\MigrationService' => __DIR__ . '/../../..' . '/lib/private/DB/MigrationService.php', 'OC\\DB\\Migrator' => __DIR__ . '/../../..' . '/lib/private/DB/Migrator.php', @@ -1368,7 +1371,6 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2 'OC\\DB\\MissingIndexInformation' => __DIR__ . '/../../..' . '/lib/private/DB/MissingIndexInformation.php', 'OC\\DB\\MissingPrimaryKeyInformation' => __DIR__ . '/../../..' . '/lib/private/DB/MissingPrimaryKeyInformation.php', 'OC\\DB\\MySqlTools' => __DIR__ . '/../../..' . '/lib/private/DB/MySqlTools.php', - 'OC\\DB\\OCSqlitePlatform' => __DIR__ . '/../../..' . '/lib/private/DB/OCSqlitePlatform.php', 'OC\\DB\\ObjectParameter' => __DIR__ . '/../../..' . '/lib/private/DB/ObjectParameter.php', 'OC\\DB\\OracleConnection' => __DIR__ . '/../../..' . '/lib/private/DB/OracleConnection.php', 'OC\\DB\\OracleMigrator' => __DIR__ . '/../../..' . '/lib/private/DB/OracleMigrator.php', @@ -1391,9 +1393,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2 'OC\\DB\\QueryBuilder\\QuoteHelper' => __DIR__ . '/../../..' . '/lib/private/DB/QueryBuilder/QuoteHelper.php', 'OC\\DB\\ResultAdapter' => __DIR__ . '/../../..' . '/lib/private/DB/ResultAdapter.php', 'OC\\DB\\SQLiteMigrator' => __DIR__ . '/../../..' . '/lib/private/DB/SQLiteMigrator.php', - 'OC\\DB\\SQLiteSessionInit' => __DIR__ . '/../../..' . '/lib/private/DB/SQLiteSessionInit.php', 'OC\\DB\\SchemaWrapper' => __DIR__ . '/../../..' . '/lib/private/DB/SchemaWrapper.php', - 'OC\\DB\\SetTransactionIsolationLevel' => __DIR__ . '/../../..' . '/lib/private/DB/SetTransactionIsolationLevel.php', 'OC\\Dashboard\\Manager' => __DIR__ . '/../../..' . '/lib/private/Dashboard/Manager.php', 'OC\\DatabaseException' => __DIR__ . '/../../..' . '/lib/private/DatabaseException.php', 'OC\\DatabaseSetupException' => __DIR__ . '/../../..' . '/lib/private/DatabaseSetupException.php', diff --git a/lib/private/DB/Connection.php b/lib/private/DB/Connection.php index 9f0fa172f136f..ccbd4633155ec 100644 --- a/lib/private/DB/Connection.php +++ b/lib/private/DB/Connection.php @@ -8,7 +8,6 @@ */ namespace OC\DB; -use Doctrine\Common\EventManager; use Doctrine\DBAL\Cache\QueryCacheProfile; use Doctrine\DBAL\Configuration; use Doctrine\DBAL\ConnectionException; @@ -79,7 +78,6 @@ public function __construct( private array $params, Driver $driver, ?Configuration $config = null, - ?EventManager $eventManager = null ) { if (!isset($params['adapter'])) { throw new \Exception('adapter not set'); @@ -90,7 +88,7 @@ public function __construct( /** * @psalm-suppress InternalMethod */ - parent::__construct($params, $driver, $config, $eventManager); + parent::__construct($params, $driver, $config); $this->adapter = new $params['adapter']($this); $this->tablePrefix = $params['tablePrefix']; @@ -108,7 +106,7 @@ public function __construct( $profiler->add($this->dbDataCollector); $debugStack = new BacktraceDebugStack(); $this->dbDataCollector->setDebugStack($debugStack); - $this->_config->setSQLLogger($debugStack); + // FIXME $this->_config->setSQLLogger($debugStack); } $this->setNestTransactionsWithSavepoints(true); diff --git a/lib/private/DB/ConnectionFactory.php b/lib/private/DB/ConnectionFactory.php index 2429bfdf7f8b5..7bada317f3db6 100644 --- a/lib/private/DB/ConnectionFactory.php +++ b/lib/private/DB/ConnectionFactory.php @@ -7,10 +7,11 @@ */ namespace OC\DB; -use Doctrine\Common\EventManager; use Doctrine\DBAL\Configuration; use Doctrine\DBAL\DriverManager; -use Doctrine\DBAL\Event\Listeners\OracleSessionInit; +use OC\DB\Middlewares\SetTransactionIsolationLevel; +use OC\DB\Middlewares\SQLiteCaseSensitiveLike; +use OC\DB\Middlewares\SQLiteJournalMode; use OC\SystemConfig; /** @@ -103,9 +104,12 @@ public function getDefaultConnectionParams($type) { */ public function getConnection($type, $additionalConnectionParams) { $normalizedType = $this->normalizeType($type); - // FIXME $eventManager = new EventManager(); - // FIXME $eventManager->addEventSubscriber(new SetTransactionIsolationLevel()); $additionalConnectionParams = array_merge($this->createConnectionParams(), $additionalConnectionParams); + + $doctrineConfiguration = new Configuration(); + $doctrineMiddlewares = $doctrineConfiguration->getMiddlewares(); + $doctrineMiddlewares[] = new SetTransactionIsolationLevel(); + switch ($normalizedType) { case 'pgsql': // pg_connect used by Doctrine DBAL does not support URI notation (enclosed in brackets) @@ -117,7 +121,8 @@ public function getConnection($type, $additionalConnectionParams) { break; case 'oci': - // FIXME $eventManager->addEventSubscriber(new OracleSessionInit); + $doctrineMiddlewares[] = new \Doctrine\DBAL\Driver\OCI8\Middleware\InitializeSession(); + // the driverOptions are unused in dbal and need to be mapped to the parameters if (isset($additionalConnectionParams['driverOptions'])) { $additionalConnectionParams = array_merge($additionalConnectionParams, $additionalConnectionParams['driverOptions']); @@ -137,16 +142,26 @@ public function getConnection($type, $additionalConnectionParams) { case 'sqlite3': $journalMode = $additionalConnectionParams['sqlite.journal_mode']; - $additionalConnectionParams['platform'] = new OCSqlitePlatform(); - // FIXME $eventManager->addEventSubscriber(new SQLiteSessionInit(true, $journalMode)); + $doctrineMiddlewares[] = new \Doctrine\DBAL\Driver\AbstractSQLiteDriver\Middleware\EnableForeignKeys(); + $doctrineMiddlewares[] = new SQLiteCaseSensitiveLike(); + SQLiteJournalMode::$journalMode = $additionalConnectionParams['sqlite.journal_mode']; + $doctrineMiddlewares[] = new SQLiteJournalMode(); break; } + + $doctrineConfiguration->setMiddlewares($doctrineMiddlewares); + /** @var Connection $connection */ $connection = DriverManager::getConnection( $additionalConnectionParams, - new Configuration(), - // FIXME $eventManager + $doctrineConfiguration, ); + + if ($normalizedType === 'sqlite3') { + $pdo = $connection->getNativeConnection(); + $pdo->sqliteCreateFunction('md5', 'md5', 1); + } + return $connection; } diff --git a/lib/private/DB/Middlewares/SQLiteCaseSensitiveLike.php b/lib/private/DB/Middlewares/SQLiteCaseSensitiveLike.php new file mode 100644 index 0000000000000..b360523002182 --- /dev/null +++ b/lib/private/DB/Middlewares/SQLiteCaseSensitiveLike.php @@ -0,0 +1,32 @@ +exec('PRAGMA case_sensitive_like = true'); + return $connection; + } + }; + } +} diff --git a/lib/private/DB/Middlewares/SQLiteJournalMode.php b/lib/private/DB/Middlewares/SQLiteJournalMode.php new file mode 100644 index 0000000000000..51a0a6fb99279 --- /dev/null +++ b/lib/private/DB/Middlewares/SQLiteJournalMode.php @@ -0,0 +1,34 @@ +exec('PRAGMA journal_mode = ' . SQLiteJournalMode::$journalMode); + return $connection; + } + }; + } +} diff --git a/lib/private/DB/Middlewares/SetTransactionIsolationLevel.php b/lib/private/DB/Middlewares/SetTransactionIsolationLevel.php new file mode 100644 index 0000000000000..10987ef0b91b8 --- /dev/null +++ b/lib/private/DB/Middlewares/SetTransactionIsolationLevel.php @@ -0,0 +1,39 @@ +isConnectedToPrimary()) { + $connection->setTransactionIsolation(\Doctrine\DBAL\TransactionIsolationLevel::READ_COMMITTED); + if ($connection->getDatabasePlatform() instanceof MySQLPlatform) { + $connection->executeStatement('SET SESSION AUTOCOMMIT=1'); + } + } + return $connection; + } + }; + } +} diff --git a/lib/private/DB/OCSqlitePlatform.php b/lib/private/DB/OCSqlitePlatform.php deleted file mode 100644 index b8bd8df5eb11e..0000000000000 --- a/lib/private/DB/OCSqlitePlatform.php +++ /dev/null @@ -1,11 +0,0 @@ -caseSensitiveLike = $caseSensitiveLike; - $this->journalMode = $journalMode; - } - - /** - * @param ConnectionEventArgs $args - * @return void - */ - public function postConnect(ConnectionEventArgs $args) { - $sensitive = $this->caseSensitiveLike ? 'true' : 'false'; - $args->getConnection()->executeUpdate('PRAGMA case_sensitive_like = ' . $sensitive); - $args->getConnection()->executeUpdate('PRAGMA journal_mode = ' . $this->journalMode); - /** @var \Doctrine\DBAL\Driver\PDO\Connection $connection */ - $connection = $args->getConnection()->getWrappedConnection(); - $pdo = $connection->getWrappedConnection(); - $pdo->sqliteCreateFunction('md5', 'md5', 1); - } - - public function getSubscribedEvents() { - return [Events::postConnect]; - } -} diff --git a/lib/private/DB/SetTransactionIsolationLevel.php b/lib/private/DB/SetTransactionIsolationLevel.php deleted file mode 100644 index cd18c4255e339..0000000000000 --- a/lib/private/DB/SetTransactionIsolationLevel.php +++ /dev/null @@ -1,36 +0,0 @@ -getConnection(); - if ($connection instanceof PrimaryReadReplicaConnection && $connection->isConnectedToPrimary()) { - $connection->setTransactionIsolation(TransactionIsolationLevel::READ_COMMITTED); - if ($connection->getDatabasePlatform() instanceof MySQLPlatform) { - $connection->executeStatement('SET SESSION AUTOCOMMIT=1'); - } - } - } - - public function getSubscribedEvents() { - return [Events::postConnect]; - } -} From 5823d58d30f4cad04540774692659a52e60a8a84 Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Tue, 2 Jul 2024 10:50:13 +0200 Subject: [PATCH 31/51] fix(db)!: `Doctrine\DBAL\Platforms\MySQL80Platform` requires the length of a VARCHAR column to be specified Signed-off-by: Joas Schilling --- lib/private/DB/MigrationService.php | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/lib/private/DB/MigrationService.php b/lib/private/DB/MigrationService.php index 7bf074fb51da1..feee88fa81d84 100644 --- a/lib/private/DB/MigrationService.php +++ b/lib/private/DB/MigrationService.php @@ -419,6 +419,7 @@ public function migrateSchemaOnly(string $to = 'latest'): void { if ($toSchema instanceof SchemaWrapper) { $this->output->debug('- Checking target database schema'); $targetSchema = $toSchema->getWrappedSchema(); + $this->ensureVarcharLength($targetSchema); $this->ensureUniqueNamesConstraints($targetSchema, true); if ($this->checkOracle) { $beforeSchema = $this->connection->createSchema(); @@ -500,6 +501,7 @@ public function executeStep($version, $schemaOnly = false) { if ($toSchema instanceof SchemaWrapper) { $targetSchema = $toSchema->getWrappedSchema(); + $this->ensureVarcharLength($targetSchema); $this->ensureUniqueNamesConstraints($targetSchema, $schemaOnly); if ($this->checkOracle) { $sourceSchema = $this->connection->createSchema(); @@ -712,6 +714,23 @@ public function ensureUniqueNamesConstraints(Schema $targetSchema, bool $isInsta } } + /** + * Ensure VARCHAR columns have a default length (required since Doctrine/DBAL 4.0) + * + * @param Schema $targetSchema + */ + public function ensureVarcharLength(Schema $targetSchema): void { + foreach ($targetSchema->getTables() as $table) { + foreach ($table->getColumns() as $column) { + if ($column->getLength() === null) { + if ($column->getType()->getTypeRegistry()->lookupName($column->getType()) === 'string') { + $column->setLength(255); + } + } + } + } + } + protected function logErrorOrWarning(string $log): void { if ($this->output instanceof SimpleOutput) { $this->output->warning($log); From e8ef23d052072016b020d075c5aedcd189f26fd9 Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Tue, 2 Jul 2024 10:58:05 +0200 Subject: [PATCH 32/51] fix(db): Too few arguments for `Doctrine\DBAL\Driver::getDatabasePlatform` - expecting versionProvider to be passed Signed-off-by: Joas Schilling --- lib/private/DB/Connection.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/private/DB/Connection.php b/lib/private/DB/Connection.php index ccbd4633155ec..ebde26d396b0a 100644 --- a/lib/private/DB/Connection.php +++ b/lib/private/DB/Connection.php @@ -702,7 +702,7 @@ private function reconnectIfNeeded(): void { } try { - $this->_conn->query($this->getDriver()->getDatabasePlatform()->getDummySelectSQL()); + $this->_conn->query($this->getDatabasePlatform()->getDummySelectSQL()); $this->lastConnectionCheck[$this->getConnectionName()] = time(); } catch (ConnectionLost|\Exception $e) { $this->logger->warning('Exception during connectivity check, closing and reconnecting', ['exception' => $e]); From df7d088653faefb12854eaffd3bcf992079d382a Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Tue, 2 Jul 2024 11:10:12 +0200 Subject: [PATCH 33/51] fix(db)!: Deprecate getDatabasePlatform which leaks 3rdparty Signed-off-by: Joas Schilling --- lib/private/DB/ConnectionAdapter.php | 1 + lib/public/IDBConnection.php | 1 + 2 files changed, 2 insertions(+) diff --git a/lib/private/DB/ConnectionAdapter.php b/lib/private/DB/ConnectionAdapter.php index d2808bb0295ce..8c1a2c79d6b55 100644 --- a/lib/private/DB/ConnectionAdapter.php +++ b/lib/private/DB/ConnectionAdapter.php @@ -178,6 +178,7 @@ public function quote($input, $type = IQueryBuilder::PARAM_STR) { /** * @todo we are leaking a 3rdparty type here + * @deprecated 30.0.0 Use {@see getDatabaseProvider()} instead */ public function getDatabasePlatform(): AbstractPlatform { return $this->inner->getDatabasePlatform(); diff --git a/lib/public/IDBConnection.php b/lib/public/IDBConnection.php index a5df85896e2a1..1c1e0a0e86916 100644 --- a/lib/public/IDBConnection.php +++ b/lib/public/IDBConnection.php @@ -278,6 +278,7 @@ public function quote($input, $type = IQueryBuilder::PARAM_STR); * * @return \Doctrine\DBAL\Platforms\AbstractPlatform The database platform. * @since 8.0.0 + * @deprecated 30.0.0 Use {@see getDatabaseProvider()} instead */ public function getDatabasePlatform(); From 60217aac39436b5a10f6633dec837325fc470c91 Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Tue, 2 Jul 2024 11:10:52 +0200 Subject: [PATCH 34/51] fix(db): No longer return result of `Connection::connect()` Signed-off-by: Joas Schilling --- lib/private/DB/ConnectionAdapter.php | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/lib/private/DB/ConnectionAdapter.php b/lib/private/DB/ConnectionAdapter.php index 8c1a2c79d6b55..d28a0afa0cb46 100644 --- a/lib/private/DB/ConnectionAdapter.php +++ b/lib/private/DB/ConnectionAdapter.php @@ -25,11 +25,9 @@ * 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 __construct( + protected Connection $inner, + ) { } public function getQueryBuilder(): IQueryBuilder { @@ -162,7 +160,8 @@ public function errorInfo() { public function connect(): bool { try { - return $this->inner->connect(); + $this->inner->connect(); + return true; } catch (Exception $e) { throw DbalException::wrap($e); } From 926f2ab887b3ab5869bd9973d660e1061e50b4a9 Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Tue, 2 Jul 2024 11:11:45 +0200 Subject: [PATCH 35/51] fix(db): Too many arguments for method `\Doctrine\DBAL\Connection::quote()` - saw 2 Signed-off-by: Joas Schilling --- lib/private/DB/ConnectionAdapter.php | 10 +++++++++- .../ExpressionBuilder/ExpressionBuilder.php | 5 ++++- lib/public/DB/QueryBuilder/IExpressionBuilder.php | 1 + lib/public/IDBConnection.php | 1 + 4 files changed, 15 insertions(+), 2 deletions(-) diff --git a/lib/private/DB/ConnectionAdapter.php b/lib/private/DB/ConnectionAdapter.php index d28a0afa0cb46..61bb37766d256 100644 --- a/lib/private/DB/ConnectionAdapter.php +++ b/lib/private/DB/ConnectionAdapter.php @@ -171,8 +171,16 @@ public function close(): void { $this->inner->close(); } + /** + * @param mixed $input + * @param int $type + * @deprecated 30.0.0 Only strings are supported as database type in the end and the $type parameter is ignored going forward + */ public function quote($input, $type = IQueryBuilder::PARAM_STR) { - return $this->inner->quote($input, $type); + if ($type !== IQueryBuilder::PARAM_STR) { + \OC::$server->getLogger()->debug('Parameter $type is no longer supported and the function only handles resulting database type string', ['exception' => new \InvalidArgumentException('$type parameter is no longer supported')]); + } + return $this->inner->getDatabasePlatform()->quoteStringLiteral($input); } /** diff --git a/lib/private/DB/QueryBuilder/ExpressionBuilder/ExpressionBuilder.php b/lib/private/DB/QueryBuilder/ExpressionBuilder/ExpressionBuilder.php index db7abdb1444c4..5eb7b31f2d1eb 100644 --- a/lib/private/DB/QueryBuilder/ExpressionBuilder/ExpressionBuilder.php +++ b/lib/private/DB/QueryBuilder/ExpressionBuilder/ExpressionBuilder.php @@ -400,7 +400,10 @@ public function bitwiseOr($x, int $y): IQueryFunction { * @return ILiteral */ public function literal($input, $type = IQueryBuilder::PARAM_STR): ILiteral { - return new Literal($this->expressionBuilder->literal($input, $type)); + if ($type !== IQueryBuilder::PARAM_STR) { + \OC::$server->getLogger()->debug('Parameter $type is no longer supported and the function only handles resulting database type string', ['exception' => new \InvalidArgumentException('$type parameter is no longer supported')]); + } + return new Literal($this->connection->getDatabasePlatform()->quoteStringLiteral((string) $input)); } /** diff --git a/lib/public/DB/QueryBuilder/IExpressionBuilder.php b/lib/public/DB/QueryBuilder/IExpressionBuilder.php index 6df9949cb75c2..d0f89b5580024 100644 --- a/lib/public/DB/QueryBuilder/IExpressionBuilder.php +++ b/lib/public/DB/QueryBuilder/IExpressionBuilder.php @@ -403,6 +403,7 @@ public function bitwiseOr($x, int $y): IQueryFunction; * * @return ILiteral * @since 8.2.0 + * @deprecated 30.0.0 Only strings are supported as database type in the end and the $type parameter is ignored going forward * * @psalm-taint-sink sql $input * @psalm-taint-sink sql $type diff --git a/lib/public/IDBConnection.php b/lib/public/IDBConnection.php index 1c1e0a0e86916..75440b7428a28 100644 --- a/lib/public/IDBConnection.php +++ b/lib/public/IDBConnection.php @@ -269,6 +269,7 @@ public function close(): void; * @param int $type Type of the parameter. * @return mixed The quoted parameter. * @since 8.0.0 + * @deprecated 30.0.0 Only strings are supported as database type in the end and the $type parameter is ignored going forward */ public function quote($input, $type = IQueryBuilder::PARAM_STR); From bae31bfa7b52d80668802c7078767b13e5ce30de Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Tue, 2 Jul 2024 11:48:59 +0200 Subject: [PATCH 36/51] fix(db): Method `\Doctrine\DBAL\Platforms\AbstractPlatform::getIdentifierQuoteCharacter()` does not exist Signed-off-by: Joas Schilling --- lib/private/DB/OracleConnection.php | 31 ++++++++++------------------- 1 file changed, 10 insertions(+), 21 deletions(-) diff --git a/lib/private/DB/OracleConnection.php b/lib/private/DB/OracleConnection.php index 5ffb65d801dbe..182622a5f309b 100644 --- a/lib/private/DB/OracleConnection.php +++ b/lib/private/DB/OracleConnection.php @@ -13,15 +13,10 @@ class OracleConnection extends Connection { * @param array $data * @return array */ - private function quoteKeys(array $data) { + private function quoteKeys(array $data): array { $return = []; - $c = $this->getDatabasePlatform()->getIdentifierQuoteCharacter(); foreach ($data as $key => $value) { - if ($key[0] !== $c) { - $return[$this->quoteIdentifier($key)] = $value; - } else { - $return[$key] = $value; - } + $return[$this->quoteIdentifier($key)] = $value; } return $return; } @@ -29,10 +24,8 @@ private function quoteKeys(array $data) { /** * {@inheritDoc} */ - public function insert($table, array $data, array $types = []) { - if ($table[0] !== $this->getDatabasePlatform()->getIdentifierQuoteCharacter()) { - $table = $this->quoteIdentifier($table); - } + public function insert(string $table, array $data, array $types = []): int|string { + $table = $this->quoteIdentifier($table); $data = $this->quoteKeys($data); return parent::insert($table, $data, $types); } @@ -40,10 +33,8 @@ public function insert($table, array $data, array $types = []) { /** * {@inheritDoc} */ - public function update($table, array $data, array $criteria, array $types = []) { - if ($table[0] !== $this->getDatabasePlatform()->getIdentifierQuoteCharacter()) { - $table = $this->quoteIdentifier($table); - } + public function update(string $table, array $data, array $criteria = [], array $types = []): int|string { + $table = $this->quoteIdentifier($table); $data = $this->quoteKeys($data); $criteria = $this->quoteKeys($criteria); return parent::update($table, $data, $criteria, $types); @@ -52,10 +43,8 @@ public function update($table, array $data, array $criteria, array $types = []) /** * {@inheritDoc} */ - public function delete($table, array $criteria, array $types = []) { - if ($table[0] !== $this->getDatabasePlatform()->getIdentifierQuoteCharacter()) { - $table = $this->quoteIdentifier($table); - } + public function delete(string $table, array $criteria = [], array $types = []): int|string { + $table = $this->quoteIdentifier($table); $criteria = $this->quoteKeys($criteria); return parent::delete($table, $criteria); } @@ -65,7 +54,7 @@ public function delete($table, array $criteria, array $types = []) { * * @param string $table table name without the prefix */ - public function dropTable($table) { + public function dropTable($table): void { $table = $this->tablePrefix . trim($table); $table = $this->quoteIdentifier($table); $schema = $this->createSchemaManager(); @@ -80,7 +69,7 @@ public function dropTable($table) { * @param string $table table name without the prefix * @return bool */ - public function tableExists($table) { + public function tableExists($table): bool { $table = $this->tablePrefix . trim($table); $table = $this->quoteIdentifier($table); $schema = $this->createSchemaManager(); From 715656927c139f09a781aa7d3194fb866ac28074 Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Tue, 2 Jul 2024 12:03:48 +0200 Subject: [PATCH 37/51] fix(db): Argument 2 of `\Doctrine\DBAL\Platforms\AbstractPlatform::getBitAndComparisonExpression` expects string Signed-off-by: Joas Schilling --- .../DB/QueryBuilder/ExpressionBuilder/ExpressionBuilder.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/private/DB/QueryBuilder/ExpressionBuilder/ExpressionBuilder.php b/lib/private/DB/QueryBuilder/ExpressionBuilder/ExpressionBuilder.php index 5eb7b31f2d1eb..9c1ca8c7c8f37 100644 --- a/lib/private/DB/QueryBuilder/ExpressionBuilder/ExpressionBuilder.php +++ b/lib/private/DB/QueryBuilder/ExpressionBuilder/ExpressionBuilder.php @@ -372,7 +372,7 @@ public function nonEmptyString($x): string { public function bitwiseAnd($x, int $y): IQueryFunction { return new QueryFunction($this->connection->getDatabasePlatform()->getBitAndComparisonExpression( $this->helper->quoteColumnName($x), - $y + (string) $y )); } @@ -387,7 +387,7 @@ public function bitwiseAnd($x, int $y): IQueryFunction { public function bitwiseOr($x, int $y): IQueryFunction { return new QueryFunction($this->connection->getDatabasePlatform()->getBitOrComparisonExpression( $this->helper->quoteColumnName($x), - $y + (string) $y )); } From 66aac18bad2d857e1df0b33058c2cc286ba859a5 Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Tue, 2 Jul 2024 12:13:14 +0200 Subject: [PATCH 38/51] fix(db)!: `OCP\DB\IPreparedStatement::bindParam()` is deprecated and calls `bindValue()` internally Signed-off-by: Joas Schilling --- lib/private/DB/PreparedStatement.php | 10 ++++++++-- lib/public/DB/IPreparedStatement.php | 1 + 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/lib/private/DB/PreparedStatement.php b/lib/private/DB/PreparedStatement.php index 00a32a629f7e6..9f6872f1d2bc3 100644 --- a/lib/private/DB/PreparedStatement.php +++ b/lib/private/DB/PreparedStatement.php @@ -13,6 +13,7 @@ use Doctrine\DBAL\Statement; use OCP\DB\IPreparedStatement; use OCP\DB\IResult; +use OCP\DB\QueryBuilder\IQueryBuilder; use PDO; /** @@ -58,11 +59,16 @@ public function fetchOne() { } public function bindValue($param, $value, $type = ParameterType::STRING): bool { - return $this->statement->bindValue($param, $value, $type); + $this->statement->bindValue($param, $value, $type); + return true; } public function bindParam($param, &$variable, $type = ParameterType::STRING, $length = null): bool { - return $this->statement->bindParam($param, $variable, $type, $length); + if ($type !== IQueryBuilder::PARAM_STR) { + \OC::$server->getLogger()->warning('PreparedStatement::bindParam() is no longer supported. Use bindValue() instead.', ['exception' => new \BadMethodCallException('bindParam() is no longer supported')]); + } + $this->bindValue($param, $variable, $type); + return true; } public function execute($params = null): IResult { diff --git a/lib/public/DB/IPreparedStatement.php b/lib/public/DB/IPreparedStatement.php index 887451a1caf87..d77f677b719e1 100644 --- a/lib/public/DB/IPreparedStatement.php +++ b/lib/public/DB/IPreparedStatement.php @@ -93,6 +93,7 @@ public function bindValue($param, $value, $type = ParameterType::STRING): bool; * @throws Exception * * @since 21.0.0 + * @deprecated 30.0.0 Use {@see self::bindValue()} instead */ public function bindParam($param, &$variable, $type = ParameterType::STRING, $length = null): bool; From 98b6c7c48a2484300311d51604f0d14c9d85c8fc Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Tue, 2 Jul 2024 12:26:56 +0200 Subject: [PATCH 39/51] fix(db): Add a hint that we return PHP_INT_MAX Signed-off-by: Joas Schilling --- lib/private/DB/Connection.php | 2 +- lib/private/DB/QueryBuilder/QueryBuilder.php | 10 ++++------ lib/private/DB/ResultAdapter.php | 2 +- lib/public/DB/IResult.php | 3 ++- 4 files changed, 8 insertions(+), 9 deletions(-) diff --git a/lib/private/DB/Connection.php b/lib/private/DB/Connection.php index ebde26d396b0a..e0f4a1ddc56d2 100644 --- a/lib/private/DB/Connection.php +++ b/lib/private/DB/Connection.php @@ -317,7 +317,7 @@ public function executeUpdate(string $sql, array $params = [], array $types = [] * @param array $params The query parameters. * @param array $types The parameter types. * - * @return int The number of affected rows. + * @return int The number of affected rows, if the result is bigger than PHP_INT_MAX, PHP_INT_MAX is returned * * @throws \Doctrine\DBAL\Exception */ diff --git a/lib/private/DB/QueryBuilder/QueryBuilder.php b/lib/private/DB/QueryBuilder/QueryBuilder.php index f47de8005aa15..97f80baa24493 100644 --- a/lib/private/DB/QueryBuilder/QueryBuilder.php +++ b/lib/private/DB/QueryBuilder/QueryBuilder.php @@ -187,7 +187,7 @@ public function getState() { /** * Executes this query using the bound parameters and their types. * - * Uses {@see Connection::executeQuery} for select statements and {@see Connection::executeUpdate} + * Uses {@see Connection::executeQuery} for select statements and {@see Connection::executeStatement} * for insert, update and delete statements. * * @return IResult|int @@ -272,12 +272,10 @@ public function execute() { if ($this->getType() !== self::SELECT) { $result = $this->queryBuilder->executeStatement(); - } else { - $result = $this->queryBuilder->executeQuery(); - } - if (is_int($result)) { - return $result; + return (int) $result; } + + $result = $this->queryBuilder->executeQuery(); return new ResultAdapter($result); } diff --git a/lib/private/DB/ResultAdapter.php b/lib/private/DB/ResultAdapter.php index 95a7620e0ffb8..158d802125aac 100644 --- a/lib/private/DB/ResultAdapter.php +++ b/lib/private/DB/ResultAdapter.php @@ -56,6 +56,6 @@ public function fetchOne() { } public function rowCount(): int { - return $this->inner->rowCount(); + return (int) $this->inner->rowCount(); } } diff --git a/lib/public/DB/IResult.php b/lib/public/DB/IResult.php index 347919ab3366b..ccff8c0cd185c 100644 --- a/lib/public/DB/IResult.php +++ b/lib/public/DB/IResult.php @@ -69,9 +69,10 @@ public function fetchColumn(); public function fetchOne(); /** - * @return int + * @return int If the result is bigger than PHP_INT_MAX, PHP_INT_MAX is returned * * @since 21.0.0 + * @since 30.0.0 If the result is bigger than PHP_INT_MAX, PHP_INT_MAX is returned */ public function rowCount(): int; } From 6b2f1367cd60d0f6dd140a6ba0da722635baed8a Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Tue, 2 Jul 2024 14:50:22 +0200 Subject: [PATCH 40/51] fix(db): Map parameter types to doctrine's enums Signed-off-by: Joas Schilling --- lib/composer/composer/autoload_classmap.php | 1 + lib/composer/composer/autoload_static.php | 1 + lib/private/DB/PreparedStatement.php | 10 +++---- lib/private/DB/QueryBuilder/QueryBuilder.php | 10 +++++-- lib/private/DB/TDoctrineParameterTypeMap.php | 31 ++++++++++++++++++++ lib/public/DB/IPreparedStatement.php | 6 ++-- lib/public/DB/QueryBuilder/IQueryBuilder.php | 14 ++++----- 7 files changed, 55 insertions(+), 18 deletions(-) create mode 100644 lib/private/DB/TDoctrineParameterTypeMap.php diff --git a/lib/composer/composer/autoload_classmap.php b/lib/composer/composer/autoload_classmap.php index 01c8671f8ad49..6feb5778063e8 100644 --- a/lib/composer/composer/autoload_classmap.php +++ b/lib/composer/composer/autoload_classmap.php @@ -1361,6 +1361,7 @@ 'OC\\DB\\ResultAdapter' => $baseDir . '/lib/private/DB/ResultAdapter.php', 'OC\\DB\\SQLiteMigrator' => $baseDir . '/lib/private/DB/SQLiteMigrator.php', 'OC\\DB\\SchemaWrapper' => $baseDir . '/lib/private/DB/SchemaWrapper.php', + 'OC\\DB\\TDoctrineParameterTypeMap' => $baseDir . '/lib/private/DB/TDoctrineParameterTypeMap.php', 'OC\\Dashboard\\Manager' => $baseDir . '/lib/private/Dashboard/Manager.php', 'OC\\DatabaseException' => $baseDir . '/lib/private/DatabaseException.php', 'OC\\DatabaseSetupException' => $baseDir . '/lib/private/DatabaseSetupException.php', diff --git a/lib/composer/composer/autoload_static.php b/lib/composer/composer/autoload_static.php index fc271f585d700..f717c664d2e1c 100644 --- a/lib/composer/composer/autoload_static.php +++ b/lib/composer/composer/autoload_static.php @@ -1394,6 +1394,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2 'OC\\DB\\ResultAdapter' => __DIR__ . '/../../..' . '/lib/private/DB/ResultAdapter.php', 'OC\\DB\\SQLiteMigrator' => __DIR__ . '/../../..' . '/lib/private/DB/SQLiteMigrator.php', 'OC\\DB\\SchemaWrapper' => __DIR__ . '/../../..' . '/lib/private/DB/SchemaWrapper.php', + 'OC\\DB\\TDoctrineParameterTypeMap' => __DIR__ . '/../../..' . '/lib/private/DB/TDoctrineParameterTypeMap.php', 'OC\\Dashboard\\Manager' => __DIR__ . '/../../..' . '/lib/private/Dashboard/Manager.php', 'OC\\DatabaseException' => __DIR__ . '/../../..' . '/lib/private/DatabaseException.php', 'OC\\DatabaseSetupException' => __DIR__ . '/../../..' . '/lib/private/DatabaseSetupException.php', diff --git a/lib/private/DB/PreparedStatement.php b/lib/private/DB/PreparedStatement.php index 9f6872f1d2bc3..84669cc992f1b 100644 --- a/lib/private/DB/PreparedStatement.php +++ b/lib/private/DB/PreparedStatement.php @@ -8,8 +8,6 @@ */ namespace OC\DB; -use Doctrine\DBAL\Exception; -use Doctrine\DBAL\ParameterType; use Doctrine\DBAL\Statement; use OCP\DB\IPreparedStatement; use OCP\DB\IResult; @@ -26,6 +24,8 @@ * methods without much magic. */ class PreparedStatement implements IPreparedStatement { + use TDoctrineParameterTypeMap; + /** @var Statement */ private $statement; @@ -58,12 +58,12 @@ public function fetchOne() { return $this->getResult()->fetchOne(); } - public function bindValue($param, $value, $type = ParameterType::STRING): bool { - $this->statement->bindValue($param, $value, $type); + public function bindValue($param, $value, $type = IQueryBuilder::PARAM_STR): bool { + $this->statement->bindValue($param, $value, $this->convertParameterTypeToDoctrine($type)); return true; } - public function bindParam($param, &$variable, $type = ParameterType::STRING, $length = null): bool { + public function bindParam($param, &$variable, $type = IQueryBuilder::PARAM_STR, $length = null): bool { if ($type !== IQueryBuilder::PARAM_STR) { \OC::$server->getLogger()->warning('PreparedStatement::bindParam() is no longer supported. Use bindValue() instead.', ['exception' => new \BadMethodCallException('bindParam() is no longer supported')]); } diff --git a/lib/private/DB/QueryBuilder/QueryBuilder.php b/lib/private/DB/QueryBuilder/QueryBuilder.php index 97f80baa24493..e9b9fa467e163 100644 --- a/lib/private/DB/QueryBuilder/QueryBuilder.php +++ b/lib/private/DB/QueryBuilder/QueryBuilder.php @@ -22,6 +22,7 @@ use OC\DB\QueryBuilder\FunctionBuilder\PgSqlFunctionBuilder; use OC\DB\QueryBuilder\FunctionBuilder\SqliteFunctionBuilder; use OC\DB\ResultAdapter; +use OC\DB\TDoctrineParameterTypeMap; use OC\SystemConfig; use OCP\DB\IResult; use OCP\DB\QueryBuilder\ICompositeExpression; @@ -33,6 +34,8 @@ use Psr\Log\LoggerInterface; class QueryBuilder implements IQueryBuilder { + use TDoctrineParameterTypeMap; + /** @internal */ protected const SELECT = 0; @@ -350,7 +353,7 @@ public function getSQL() { * @return $this This QueryBuilder instance. */ public function setParameter($key, $value, $type = null) { - $this->queryBuilder->setParameter($key, $value, $type); + $this->queryBuilder->setParameter($key, $value, $this->convertParameterTypeToDoctrine($type)); return $this; } @@ -375,6 +378,7 @@ public function setParameter($key, $value, $type = null) { * @return $this This QueryBuilder instance. */ public function setParameters(array $params, array $types = []) { + $types = array_map($this->convertParameterTypeToDoctrine(...), $types); $this->queryBuilder->setParameters($params, $types); return $this; @@ -1223,7 +1227,7 @@ public function resetQueryPart($queryPartName) { * @return IParameter the placeholder name used. */ public function createNamedParameter($value, $type = IQueryBuilder::PARAM_STR, $placeHolder = null) { - return new Parameter($this->queryBuilder->createNamedParameter($value, $type, $placeHolder)); + return new Parameter($this->queryBuilder->createNamedParameter($value, $this->convertParameterTypeToDoctrine($type), $placeHolder)); } /** @@ -1249,7 +1253,7 @@ public function createNamedParameter($value, $type = IQueryBuilder::PARAM_STR, $ * @return IParameter */ public function createPositionalParameter($value, $type = IQueryBuilder::PARAM_STR) { - return new Parameter($this->queryBuilder->createPositionalParameter($value, $type)); + return new Parameter($this->queryBuilder->createPositionalParameter($value, $this->convertParameterTypeToDoctrine($type))); } /** diff --git a/lib/private/DB/TDoctrineParameterTypeMap.php b/lib/private/DB/TDoctrineParameterTypeMap.php new file mode 100644 index 0000000000000..f4e06fd9a0c90 --- /dev/null +++ b/lib/private/DB/TDoctrineParameterTypeMap.php @@ -0,0 +1,31 @@ + $type, + IQueryBuilder::PARAM_NULL => ParameterType::NULL, + IQueryBuilder::PARAM_BOOL => ParameterType::BOOLEAN, + IQueryBuilder::PARAM_INT => ParameterType::INTEGER, + null, + IQueryBuilder::PARAM_STR => ParameterType::STRING, + IQueryBuilder::PARAM_LOB => ParameterType::LARGE_OBJECT, + IQueryBuilder::PARAM_INT_ARRAY => ArrayParameterType::INTEGER, + IQueryBuilder::PARAM_STR_ARRAY => ArrayParameterType::STRING, + }; + } + +} diff --git a/lib/public/DB/IPreparedStatement.php b/lib/public/DB/IPreparedStatement.php index d77f677b719e1..e2a72bd4a27ed 100644 --- a/lib/public/DB/IPreparedStatement.php +++ b/lib/public/DB/IPreparedStatement.php @@ -9,7 +9,7 @@ namespace OCP\DB; use Doctrine\DBAL\Exception; -use Doctrine\DBAL\ParameterType; +use OCP\DB\QueryBuilder\IQueryBuilder; use PDO; /** @@ -80,7 +80,7 @@ public function fetchOne(); * * @since 21.0.0 */ - public function bindValue($param, $value, $type = ParameterType::STRING): bool; + public function bindValue($param, $value, $type = IQueryBuilder::PARAM_STR): bool; /** * @param int|string $param @@ -95,7 +95,7 @@ public function bindValue($param, $value, $type = ParameterType::STRING): bool; * @since 21.0.0 * @deprecated 30.0.0 Use {@see self::bindValue()} instead */ - public function bindParam($param, &$variable, $type = ParameterType::STRING, $length = null): bool; + public function bindParam($param, &$variable, $type = IQueryBuilder::PARAM_STR, $length = null): bool; /** * @param mixed[]|null $params diff --git a/lib/public/DB/QueryBuilder/IQueryBuilder.php b/lib/public/DB/QueryBuilder/IQueryBuilder.php index 7d9d56a1f905a..4d6ab1bd59299 100644 --- a/lib/public/DB/QueryBuilder/IQueryBuilder.php +++ b/lib/public/DB/QueryBuilder/IQueryBuilder.php @@ -23,23 +23,23 @@ interface IQueryBuilder { /** * @since 9.0.0 */ - public const PARAM_NULL = ParameterType::NULL; + public const PARAM_NULL = 0; // Translates to ParameterType::NULL; /** * @since 9.0.0 */ - public const PARAM_BOOL = ParameterType::BOOLEAN; + public const PARAM_BOOL = 4; // Translates to ParameterType::BOOLEAN; /** * @since 9.0.0 */ - public const PARAM_INT = ParameterType::INTEGER; + public const PARAM_INT = 1; // Translates to ParameterType::INTEGER; /** * @since 9.0.0 */ - public const PARAM_STR = ParameterType::STRING; + public const PARAM_STR = 2; // Translates to ParameterType::STRING; /** * @since 9.0.0 */ - public const PARAM_LOB = ParameterType::LARGE_OBJECT; + public const PARAM_LOB = 3; // Translates to ParameterType::LARGE_OBJECT; /** * @since 9.0.0 */ @@ -53,11 +53,11 @@ interface IQueryBuilder { /** * @since 9.0.0 */ - public const PARAM_INT_ARRAY = ArrayParameterType::INTEGER; + public const PARAM_INT_ARRAY = 100; // Translates to ArrayParameterType::INTEGER; /** * @since 9.0.0 */ - public const PARAM_STR_ARRAY = ArrayParameterType::STRING; + public const PARAM_STR_ARRAY = 101; // Translates to ArrayParameterType::STRING; /** * @since 24.0.0 Indicates how many rows can be deleted at once with MySQL From 1370693133a274115f8d5c117b8212b0d5c3d0ac Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Tue, 2 Jul 2024 17:27:01 +0200 Subject: [PATCH 41/51] fix(db): Remove usage of Platforms Signed-off-by: Joas Schilling --- lib/private/DB/QueryBuilder/QueryBuilder.php | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/lib/private/DB/QueryBuilder/QueryBuilder.php b/lib/private/DB/QueryBuilder/QueryBuilder.php index e9b9fa467e163..743df5206fe48 100644 --- a/lib/private/DB/QueryBuilder/QueryBuilder.php +++ b/lib/private/DB/QueryBuilder/QueryBuilder.php @@ -7,9 +7,6 @@ */ namespace OC\DB\QueryBuilder; -use Doctrine\DBAL\Platforms\MySQLPlatform; -use Doctrine\DBAL\Platforms\OraclePlatform; -use Doctrine\DBAL\Platforms\SQLitePlatform; use Doctrine\DBAL\Query\QueryException; use OC\DB\ConnectionAdapter; use OC\DB\QueryBuilder\ExpressionBuilder\ExpressionBuilder; @@ -112,16 +109,16 @@ public function automaticTablePrefix($enabled) { * @return \OCP\DB\QueryBuilder\IExpressionBuilder */ public function expr() { - if ($this->connection->getDatabasePlatform() instanceof OraclePlatform) { + if ($this->connection->getDatabaseProvider() === IDBConnection::PLATFORM_ORACLE) { return new OCIExpressionBuilder($this->connection, $this); } - if ($this->connection->getDatabaseProvider() !== IDBConnection::PLATFORM_POSTGRES) { + if ($this->connection->getDatabaseProvider() === IDBConnection::PLATFORM_POSTGRES) { return new PgSqlExpressionBuilder($this->connection, $this); } - if ($this->connection->getDatabasePlatform() instanceof MySQLPlatform) { + if ($this->connection->getDatabaseProvider() === IDBConnection::PLATFORM_MYSQL) { return new MySqlExpressionBuilder($this->connection, $this); } - if ($this->connection->getDatabasePlatform() instanceof SQLitePlatform) { + if ($this->connection->getDatabaseProvider() === IDBConnection::PLATFORM_SQLITE) { return new SqliteExpressionBuilder($this->connection, $this); } @@ -145,10 +142,10 @@ public function expr() { * @return \OCP\DB\QueryBuilder\IFunctionBuilder */ public function func() { - if ($this->connection->getDatabasePlatform() instanceof OraclePlatform) { + if ($this->connection->getDatabaseProvider() === IDBConnection::PLATFORM_ORACLE) { return new OCIFunctionBuilder($this->connection, $this, $this->helper); } - if ($this->connection->getDatabasePlatform() instanceof SQLitePlatform) { + if ($this->connection->getDatabaseProvider() === IDBConnection::PLATFORM_SQLITE) { return new SqliteFunctionBuilder($this->connection, $this, $this->helper); } if ($this->connection->getDatabaseProvider() === IDBConnection::PLATFORM_POSTGRES) { From de5a7c3c73e5f2f5dda5ec07333c8d3dd89f4540 Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Wed, 3 Jul 2024 10:39:06 +0200 Subject: [PATCH 42/51] fix(db)!: Reimplement the query logger with a doctrine middleware Signed-off-by: Joas Schilling --- lib/composer/composer/autoload_classmap.php | 5 +- lib/composer/composer/autoload_static.php | 5 +- lib/private/DB/BacktraceDebugStack.php | 20 ------ lib/private/DB/Connection.php | 8 +-- lib/private/DB/ConnectionFactory.php | 9 +++ lib/private/DB/DbDataCollector.php | 73 ++++++-------------- lib/private/DB/Logging/Connection.php | 49 +++++++++++++ lib/private/DB/Logging/Driver.php | 34 +++++++++ lib/private/DB/Logging/Middleware.php | 28 ++++++++ lib/private/DB/Logging/Statement.php | 50 ++++++++++++++ lib/private/DB/TDoctrineParameterTypeMap.php | 14 ++++ lib/private/Diagnostics/Query.php | 60 ++++++---------- lib/private/Diagnostics/QueryLogger.php | 46 ++++-------- lib/public/Diagnostics/IQuery.php | 29 ++++---- lib/public/Diagnostics/IQueryLogger.php | 18 ++--- 15 files changed, 278 insertions(+), 170 deletions(-) delete mode 100644 lib/private/DB/BacktraceDebugStack.php create mode 100644 lib/private/DB/Logging/Connection.php create mode 100644 lib/private/DB/Logging/Driver.php create mode 100644 lib/private/DB/Logging/Middleware.php create mode 100644 lib/private/DB/Logging/Statement.php diff --git a/lib/composer/composer/autoload_classmap.php b/lib/composer/composer/autoload_classmap.php index 6feb5778063e8..93357dbc8e3da 100644 --- a/lib/composer/composer/autoload_classmap.php +++ b/lib/composer/composer/autoload_classmap.php @@ -1321,12 +1321,15 @@ 'OC\\DB\\AdapterOCI8' => $baseDir . '/lib/private/DB/AdapterOCI8.php', 'OC\\DB\\AdapterPgSql' => $baseDir . '/lib/private/DB/AdapterPgSql.php', 'OC\\DB\\AdapterSqlite' => $baseDir . '/lib/private/DB/AdapterSqlite.php', - 'OC\\DB\\BacktraceDebugStack' => $baseDir . '/lib/private/DB/BacktraceDebugStack.php', '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\\DbDataCollector' => $baseDir . '/lib/private/DB/DbDataCollector.php', 'OC\\DB\\Exceptions\\DbalException' => $baseDir . '/lib/private/DB/Exceptions/DbalException.php', + 'OC\\DB\\Logging\\Connection' => $baseDir . '/lib/private/DB/Logging/Connection.php', + 'OC\\DB\\Logging\\Driver' => $baseDir . '/lib/private/DB/Logging/Driver.php', + 'OC\\DB\\Logging\\Middleware' => $baseDir . '/lib/private/DB/Logging/Middleware.php', + 'OC\\DB\\Logging\\Statement' => $baseDir . '/lib/private/DB/Logging/Statement.php', 'OC\\DB\\Middlewares\\SQLiteCaseSensitiveLike' => $baseDir . '/lib/private/DB/Middlewares/SQLiteCaseSensitiveLike.php', 'OC\\DB\\Middlewares\\SQLiteJournalMode' => $baseDir . '/lib/private/DB/Middlewares/SQLiteJournalMode.php', 'OC\\DB\\Middlewares\\SetTransactionIsolationLevel' => $baseDir . '/lib/private/DB/Middlewares/SetTransactionIsolationLevel.php', diff --git a/lib/composer/composer/autoload_static.php b/lib/composer/composer/autoload_static.php index f717c664d2e1c..7461a567cfb9b 100644 --- a/lib/composer/composer/autoload_static.php +++ b/lib/composer/composer/autoload_static.php @@ -1354,12 +1354,15 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2 'OC\\DB\\AdapterOCI8' => __DIR__ . '/../../..' . '/lib/private/DB/AdapterOCI8.php', 'OC\\DB\\AdapterPgSql' => __DIR__ . '/../../..' . '/lib/private/DB/AdapterPgSql.php', 'OC\\DB\\AdapterSqlite' => __DIR__ . '/../../..' . '/lib/private/DB/AdapterSqlite.php', - 'OC\\DB\\BacktraceDebugStack' => __DIR__ . '/../../..' . '/lib/private/DB/BacktraceDebugStack.php', '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\\DbDataCollector' => __DIR__ . '/../../..' . '/lib/private/DB/DbDataCollector.php', 'OC\\DB\\Exceptions\\DbalException' => __DIR__ . '/../../..' . '/lib/private/DB/Exceptions/DbalException.php', + 'OC\\DB\\Logging\\Connection' => __DIR__ . '/../../..' . '/lib/private/DB/Logging/Connection.php', + 'OC\\DB\\Logging\\Driver' => __DIR__ . '/../../..' . '/lib/private/DB/Logging/Driver.php', + 'OC\\DB\\Logging\\Middleware' => __DIR__ . '/../../..' . '/lib/private/DB/Logging/Middleware.php', + 'OC\\DB\\Logging\\Statement' => __DIR__ . '/../../..' . '/lib/private/DB/Logging/Statement.php', 'OC\\DB\\Middlewares\\SQLiteCaseSensitiveLike' => __DIR__ . '/../../..' . '/lib/private/DB/Middlewares/SQLiteCaseSensitiveLike.php', 'OC\\DB\\Middlewares\\SQLiteJournalMode' => __DIR__ . '/../../..' . '/lib/private/DB/Middlewares/SQLiteJournalMode.php', 'OC\\DB\\Middlewares\\SetTransactionIsolationLevel' => __DIR__ . '/../../..' . '/lib/private/DB/Middlewares/SetTransactionIsolationLevel.php', diff --git a/lib/private/DB/BacktraceDebugStack.php b/lib/private/DB/BacktraceDebugStack.php deleted file mode 100644 index 4afd3ce6a1342..0000000000000 --- a/lib/private/DB/BacktraceDebugStack.php +++ /dev/null @@ -1,20 +0,0 @@ -queries[$this->currentQuery]['backtrace'] = $backtrace; - $this->queries[$this->currentQuery]['start'] = $this->start; - } -} diff --git a/lib/private/DB/Connection.php b/lib/private/DB/Connection.php index e0f4a1ddc56d2..d1fb255eb1b14 100644 --- a/lib/private/DB/Connection.php +++ b/lib/private/DB/Connection.php @@ -25,6 +25,7 @@ use OC\SystemConfig; use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\Diagnostics\IEventLogger; +use OCP\Diagnostics\IQueryLogger; use OCP\IRequestId; use OCP\PreConditionNotMetException; use OCP\Profiler\IProfiler; @@ -99,14 +100,11 @@ public function __construct( $this->logRequestId = $this->systemConfig->getValue('db.log_request_id', false); $this->requestId = Server::get(IRequestId::class)->getId(); - /** @var \OCP\Profiler\IProfiler */ + /** @var IProfiler */ $profiler = Server::get(IProfiler::class); if ($profiler->isEnabled()) { - $this->dbDataCollector = new DbDataCollector($this); + $this->dbDataCollector = new DbDataCollector(Server::get(IQueryLogger::class)); $profiler->add($this->dbDataCollector); - $debugStack = new BacktraceDebugStack(); - $this->dbDataCollector->setDebugStack($debugStack); - // FIXME $this->_config->setSQLLogger($debugStack); } $this->setNestTransactionsWithSavepoints(true); diff --git a/lib/private/DB/ConnectionFactory.php b/lib/private/DB/ConnectionFactory.php index 7bada317f3db6..88dbfc59c52b9 100644 --- a/lib/private/DB/ConnectionFactory.php +++ b/lib/private/DB/ConnectionFactory.php @@ -9,10 +9,14 @@ use Doctrine\DBAL\Configuration; use Doctrine\DBAL\DriverManager; +use OC\DB\Logging\Middleware; use OC\DB\Middlewares\SetTransactionIsolationLevel; use OC\DB\Middlewares\SQLiteCaseSensitiveLike; use OC\DB\Middlewares\SQLiteJournalMode; use OC\SystemConfig; +use OCP\Diagnostics\IQueryLogger; +use OCP\Profiler\IProfiler; +use OCP\Server; /** * Takes care of creating and configuring Doctrine connections. @@ -108,6 +112,11 @@ public function getConnection($type, $additionalConnectionParams) { $doctrineConfiguration = new Configuration(); $doctrineMiddlewares = $doctrineConfiguration->getMiddlewares(); + /** @var IProfiler */ + $profiler = Server::get(IProfiler::class); + if ($profiler->isEnabled()) { + $doctrineMiddlewares[] = new Middleware(Server::get(IQueryLogger::class)); + } $doctrineMiddlewares[] = new SetTransactionIsolationLevel(); switch ($normalizedType) { diff --git a/lib/private/DB/DbDataCollector.php b/lib/private/DB/DbDataCollector.php index fcaa74daeab7c..e30877fa86983 100644 --- a/lib/private/DB/DbDataCollector.php +++ b/lib/private/DB/DbDataCollector.php @@ -8,31 +8,22 @@ namespace OC\DB; -use Doctrine\DBAL\Types\ConversionException; -use Doctrine\DBAL\Types\Type; use OC\AppFramework\Http\Request; +use OC\Diagnostics\Query; use OCP\AppFramework\Http\Response; +use OCP\Diagnostics\IQueryLogger; class DbDataCollector extends \OCP\DataCollector\AbstractDataCollector { - protected ?BacktraceDebugStack $debugStack = null; - private Connection $connection; - - /** - * DbDataCollector constructor. - */ - public function __construct(Connection $connection) { - $this->connection = $connection; - } - - public function setDebugStack(BacktraceDebugStack $debugStack, $name = 'default'): void { - $this->debugStack = $debugStack; + public function __construct( + private readonly IQueryLogger $queryLogger, + ) { } /** * @inheritDoc */ public function collect(Request $request, Response $response, ?\Throwable $exception = null): void { - $queries = $this->sanitizeQueries($this->debugStack->queries); + $queries = $this->sanitizeQueries($this->queryLogger->getQueries()); $this->data = [ 'queries' => $queries, @@ -55,37 +46,19 @@ private function sanitizeQueries(array $queries): array { return $queries; } - private function sanitizeQuery(array $query): array { - $query['explainable'] = true; - $query['runnable'] = true; - if ($query['params'] === null) { - $query['params'] = []; - } - if (!\is_array($query['params'])) { - $query['params'] = [$query['params']]; - } - if (!\is_array($query['types'])) { - $query['types'] = []; - } - foreach ($query['params'] as $j => $param) { - $e = null; - if (isset($query['types'][$j])) { - // Transform the param according to the type - $type = $query['types'][$j]; - if (\is_string($type)) { - $type = Type::getType($type); - } - if ($type instanceof Type) { - $query['types'][$j] = $type->getBindingType(); - try { - $param = $type->convertToDatabaseValue($param, $this->connection->getDatabasePlatform()); - } catch (\TypeError $e) { - } catch (ConversionException $e) { - } - } - } + private function sanitizeQuery(Query $queryObject): array { + $query = [ + 'sql' => $queryObject->getSql(), + 'params' => $queryObject->getParams() ?? [], + 'types' => $queryObject->getTypes() ?? [], + 'executionMS' => $queryObject->getDuration(), + 'backtrace' => $queryObject->getStacktrace(), + 'explainable' => true, + 'runnable' => true, + ]; - [$query['params'][$j], $explainable, $runnable] = $this->sanitizeParam($param, $e); + foreach ($query['params'] as $j => $param) { + [$query['params'][$j], $explainable, $runnable] = $this->sanitizeParam($param); if (!$explainable) { $query['explainable'] = false; } @@ -105,20 +78,16 @@ private function sanitizeQuery(array $query): array { * indicating if the original value was kept (allowing to use the sanitized * value to explain the query). */ - private function sanitizeParam($var, ?\Throwable $error): array { + private function sanitizeParam($var): array { if (\is_object($var)) { - return [$o = new ObjectParameter($var, $error), false, $o->isStringable() && !$error]; - } - - if ($error) { - return ['âš  '.$error->getMessage(), false, false]; + return [$o = new ObjectParameter($var, null), false, $o->isStringable()]; } if (\is_array($var)) { $a = []; $explainable = $runnable = true; foreach ($var as $k => $v) { - [$value, $e, $r] = $this->sanitizeParam($v, null); + [$value, $e, $r] = $this->sanitizeParam($v); $explainable = $explainable && $e; $runnable = $runnable && $r; $a[$k] = $value; diff --git a/lib/private/DB/Logging/Connection.php b/lib/private/DB/Logging/Connection.php new file mode 100644 index 0000000000000..30f1e7444c57c --- /dev/null +++ b/lib/private/DB/Logging/Connection.php @@ -0,0 +1,49 @@ +queryLogger, + $sql, + ); + } + + public function query(string $sql): Result { + $this->queryLogger->startQuery($sql); + $result = parent::query($sql); + $this->queryLogger->stopQuery(); + + return $result; + } + + public function exec(string $sql): int|string { + $this->queryLogger->startQuery($sql); + $result = parent::exec($sql); + $this->queryLogger->stopQuery(); + + return $result; + } +} diff --git a/lib/private/DB/Logging/Driver.php b/lib/private/DB/Logging/Driver.php new file mode 100644 index 0000000000000..f787ef66320b5 --- /dev/null +++ b/lib/private/DB/Logging/Driver.php @@ -0,0 +1,34 @@ +queryLogger, + ); + } +} diff --git a/lib/private/DB/Logging/Middleware.php b/lib/private/DB/Logging/Middleware.php new file mode 100644 index 0000000000000..dbfbd49fa6a1c --- /dev/null +++ b/lib/private/DB/Logging/Middleware.php @@ -0,0 +1,28 @@ +queryLogger, + ); + } +} diff --git a/lib/private/DB/Logging/Statement.php b/lib/private/DB/Logging/Statement.php new file mode 100644 index 0000000000000..affb0cf61baf2 --- /dev/null +++ b/lib/private/DB/Logging/Statement.php @@ -0,0 +1,50 @@ +|array */ + private array $params = []; + + /** @var array|array */ + private array $types = []; + + public function __construct( + StatementInterface $statement, + private readonly IQueryLogger $queryLogger, + private readonly string $sql, + ) { + parent::__construct($statement); + } + + public function bindValue(int|string $param, mixed $value, ParameterType $type): void { + $this->params[$param] = $value; + $this->types[$param] = $this->convertParameterTypeToJsonSerializable($type); + + parent::bindValue($param, $value, $type); + } + + public function execute(): ResultInterface { + $this->queryLogger->startQuery($this->sql, $this->params, $this->types); + $result = parent::execute(); + $this->queryLogger->stopQuery(); + + return $result; + } +} diff --git a/lib/private/DB/TDoctrineParameterTypeMap.php b/lib/private/DB/TDoctrineParameterTypeMap.php index f4e06fd9a0c90..3e9c53219a0c9 100644 --- a/lib/private/DB/TDoctrineParameterTypeMap.php +++ b/lib/private/DB/TDoctrineParameterTypeMap.php @@ -28,4 +28,18 @@ protected function convertParameterTypeToDoctrine(string|int|null $type): ArrayP }; } + protected function convertParameterTypeToJsonSerializable(ArrayParameterType|ParameterType|string $type): string { + return match($type) { + ParameterType::NULL => 'null', + ParameterType::BOOLEAN => 'boolean', + ParameterType::INTEGER => 'integer', + ArrayParameterType::INTEGER => 'integer[]', + ParameterType::STRING => 'string', + ArrayParameterType::STRING => 'string[]', + ParameterType::LARGE_OBJECT => 'lob', + IQueryBuilder::PARAM_DATE, + IQueryBuilder::PARAM_JSON => $type, + default => 'unsupported-type', + }; + } } diff --git a/lib/private/Diagnostics/Query.php b/lib/private/Diagnostics/Query.php index 8161007470908..6624d13acab3a 100644 --- a/lib/private/Diagnostics/Query.php +++ b/lib/private/Diagnostics/Query.php @@ -1,5 +1,6 @@ sql = $sql; - $this->params = $params; - $this->start = $start; - $this->stack = $stack; + public function __construct( + private readonly string $sql, + private readonly ?array $params, + private readonly ?array $types, + private readonly float $start, + private readonly array $stack, + ) { } - public function end($time) { + public function end(float $time): void { $this->end = $time; } - /** - * @return array - */ - public function getParams() { + public function getParams(): ?array { return $this->params; } - /** - * @return string - */ - public function getSql() { + public function getTypes(): ?array { + return $this->types; + } + + public function getSql(): string { return $this->sql; } - /** - * @return float - */ - public function getStart() { + public function getStart(): float { return $this->start; } - - /** - * @return float - */ - public function getDuration() { + + public function getDuration(): float { return $this->end - $this->start; } - public function getStartTime() { + public function getStartTime(): float { return $this->start; } - public function getStacktrace() { + public function getStacktrace(): array { return $this->stack; } } diff --git a/lib/private/Diagnostics/QueryLogger.php b/lib/private/Diagnostics/QueryLogger.php index 5efe99d1a747b..8c80207a1a8e1 100644 --- a/lib/private/Diagnostics/QueryLogger.php +++ b/lib/private/Diagnostics/QueryLogger.php @@ -1,5 +1,6 @@ */ protected CappedMemoryCache $queries; - /** - * QueryLogger constructor. - */ public function __construct() { $this->queries = new CappedMemoryCache(1024); } - /** - * @var bool - Module needs to be activated by some app - */ - private $activated = false; - - /** - * @inheritdoc - */ - public function startQuery($sql, ?array $params = null, ?array $types = null) { + public function startQuery(string $sql, ?array $params = null, ?array $types = null): void { if ($this->activated) { - $this->activeQuery = new Query($sql, $params, microtime(true), $this->getStack()); + $this->activeQuery = new Query($sql, $params, $types, microtime(true), $this->getStack()); } } - private function getStack() { - $stack = debug_backtrace(); - array_shift($stack); - array_shift($stack); - array_shift($stack); - return $stack; + private function getStack(): array { + return debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); } - /** - * @inheritdoc - */ - public function stopQuery() { + public function stopQuery(): void { if ($this->activated && $this->activeQuery) { $this->activeQuery->end(microtime(true)); $this->queries[(string)$this->index] = $this->activeQuery; @@ -58,17 +48,11 @@ public function stopQuery() { } } - /** - * @inheritdoc - */ - public function getQueries() { + public function getQueries(): array { return $this->queries->getData(); } - /** - * @inheritdoc - */ - public function activate() { + public function activate(): void { $this->activated = true; } } diff --git a/lib/public/Diagnostics/IQuery.php b/lib/public/Diagnostics/IQuery.php index 3fe39be27bd99..0c717f547d0ac 100644 --- a/lib/public/Diagnostics/IQuery.php +++ b/lib/public/Diagnostics/IQuery.php @@ -1,5 +1,6 @@ Date: Wed, 3 Jul 2024 13:48:28 +0200 Subject: [PATCH 43/51] fix(db): Don't set null as type Signed-off-by: Joas Schilling --- lib/private/DB/Connection.php | 4 ++++ lib/private/DB/TDoctrineParameterTypeMap.php | 7 ++++--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/lib/private/DB/Connection.php b/lib/private/DB/Connection.php index d1fb255eb1b14..6b2618271250a 100644 --- a/lib/private/DB/Connection.php +++ b/lib/private/DB/Connection.php @@ -15,6 +15,7 @@ use Doctrine\DBAL\Driver; use Doctrine\DBAL\Exception; use Doctrine\DBAL\Exception\ConnectionLost; +use Doctrine\DBAL\ParameterType; use Doctrine\DBAL\Platforms\MySQLPlatform; use Doctrine\DBAL\Platforms\OraclePlatform; use Doctrine\DBAL\Platforms\SQLitePlatform; @@ -37,6 +38,8 @@ use function in_array; class Connection extends PrimaryReadReplicaConnection { + use TDoctrineParameterTypeMap; + /** @var string */ protected $tablePrefix; @@ -285,6 +288,7 @@ public function executeQuery(string $sql, array $params = [], $types = [], ?Quer $sql = $this->finishQuery($sql); $this->queriesExecuted++; $this->logQueryToFile($sql); + $types = array_map($this->convertParameterTypeToDoctrine(...), $types); return parent::executeQuery($sql, $params, $types, $qcp); } diff --git a/lib/private/DB/TDoctrineParameterTypeMap.php b/lib/private/DB/TDoctrineParameterTypeMap.php index 3e9c53219a0c9..2ab7f29e4b0b5 100644 --- a/lib/private/DB/TDoctrineParameterTypeMap.php +++ b/lib/private/DB/TDoctrineParameterTypeMap.php @@ -13,10 +13,8 @@ use OCP\DB\QueryBuilder\IQueryBuilder; trait TDoctrineParameterTypeMap { - protected function convertParameterTypeToDoctrine(string|int|null $type): ArrayParameterType|ParameterType|string { + protected function convertParameterTypeToDoctrine(ArrayParameterType|ParameterType|string|int|null $type): ArrayParameterType|ParameterType|string { return match($type) { - IQueryBuilder::PARAM_DATE, - IQueryBuilder::PARAM_JSON => $type, IQueryBuilder::PARAM_NULL => ParameterType::NULL, IQueryBuilder::PARAM_BOOL => ParameterType::BOOLEAN, IQueryBuilder::PARAM_INT => ParameterType::INTEGER, @@ -25,6 +23,9 @@ protected function convertParameterTypeToDoctrine(string|int|null $type): ArrayP IQueryBuilder::PARAM_LOB => ParameterType::LARGE_OBJECT, IQueryBuilder::PARAM_INT_ARRAY => ArrayParameterType::INTEGER, IQueryBuilder::PARAM_STR_ARRAY => ArrayParameterType::STRING, + // IQueryBuilder::PARAM_DATE, + // IQueryBuilder::PARAM_JSON, + default => $type, }; } From fc383a323fa1e4d8bfa4519b8d9a26cfa255b1c4 Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Wed, 3 Jul 2024 13:48:58 +0200 Subject: [PATCH 44/51] fix(tests): Adjust the migrator test Signed-off-by: Joas Schilling --- tests/lib/DB/MigratorTest.php | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/tests/lib/DB/MigratorTest.php b/tests/lib/DB/MigratorTest.php index 0ca698bbbe9db..a486a39ae3d2e 100644 --- a/tests/lib/DB/MigratorTest.php +++ b/tests/lib/DB/MigratorTest.php @@ -17,6 +17,8 @@ use OC\DB\Migrator; use OC\DB\OracleMigrator; use OC\DB\SQLiteMigrator; +use OC\DB\TDoctrineParameterTypeMap; +use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\DB\Types; use OCP\IConfig; use OCP\Security\ISecureRandom; @@ -29,6 +31,8 @@ * @package Test\DB */ class MigratorTest extends \Test\TestCase { + use TDoctrineParameterTypeMap; + /** * @var \Doctrine\DBAL\Connection $connection */ @@ -74,12 +78,12 @@ private function getUniqueTableName() { protected function tearDown(): void { // Try to delete if exists (IF EXISTS NOT SUPPORTED IN ORACLE) try { - $this->connection->exec('DROP TABLE ' . $this->connection->quoteIdentifier($this->tableNameTmp)); + $this->connection->executeStatement('DROP TABLE ' . $this->connection->quoteIdentifier($this->tableNameTmp)); } catch (Exception $e) { } try { - $this->connection->exec('DROP TABLE ' . $this->connection->quoteIdentifier($this->tableName)); + $this->connection->executeStatement('DROP TABLE ' . $this->connection->quoteIdentifier($this->tableName)); } catch (Exception $e) { } parent::tearDown(); @@ -267,28 +271,28 @@ public function testAddingForeignKey() { public function dataNotNullEmptyValuesFailOracle(): array { return [ - [ParameterType::BOOLEAN, true, Types::BOOLEAN, false], - [ParameterType::BOOLEAN, false, Types::BOOLEAN, true], + [IQueryBuilder::PARAM_BOOL, true, Types::BOOLEAN, false], + [IQueryBuilder::PARAM_BOOL, false, Types::BOOLEAN, true], - [ParameterType::STRING, 'foo', Types::STRING, false], - [ParameterType::STRING, '', Types::STRING, true], + [IQueryBuilder::PARAM_STR, 'foo', Types::STRING, false], + [IQueryBuilder::PARAM_STR, '', Types::STRING, true], - [ParameterType::INTEGER, 1234, Types::INTEGER, false], - [ParameterType::INTEGER, 0, Types::INTEGER, false], // Integer 0 is not stored as Null and therefor works + [IQueryBuilder::PARAM_INT, 1234, Types::INTEGER, false], + [IQueryBuilder::PARAM_INT, 0, Types::INTEGER, false], // Integer 0 is not stored as Null and therefor works - [ParameterType::STRING, '{"a": 2}', Types::JSON, false], + [IQueryBuilder::PARAM_JSON, '{"a": 2}', Types::JSON, false], ]; } /** * @dataProvider dataNotNullEmptyValuesFailOracle * - * @param int $parameterType + * @param int|string $parameterType * @param bool|int|string $value * @param string $columnType * @param bool $oracleThrows */ - public function testNotNullEmptyValuesFailOracle(int $parameterType, $value, string $columnType, bool $oracleThrows): void { + public function testNotNullEmptyValuesFailOracle(int|string $parameterType, bool|int|string $value, string $columnType, bool $oracleThrows): void { $startSchema = new Schema([], [], $this->getSchemaConfig()); $table = $startSchema->createTable($this->tableName); $table->addColumn('id', Types::BIGINT); @@ -308,7 +312,7 @@ public function testNotNullEmptyValuesFailOracle(int $parameterType, $value, str $this->connection->insert( $this->tableName, ['id' => 1, 'will_it_blend' => $value], - ['id' => ParameterType::INTEGER, 'will_it_blend' => $parameterType], + ['id' => ParameterType::INTEGER, 'will_it_blend' => $this->convertParameterTypeToDoctrine($parameterType)], ); $this->addToAssertionCount(1); From 4c80fdd2bdfa3cce34c3d4d5f8cc88da9151288c Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Wed, 3 Jul 2024 14:52:48 +0200 Subject: [PATCH 45/51] fix(db): Adjust unit tests of query builder Signed-off-by: Joas Schilling --- lib/private/DB/QueryBuilder/QueryBuilder.php | 4 +- .../lib/DB/QueryBuilder/QueryBuilderTest.php | 487 +++++------------- 2 files changed, 130 insertions(+), 361 deletions(-) diff --git a/lib/private/DB/QueryBuilder/QueryBuilder.php b/lib/private/DB/QueryBuilder/QueryBuilder.php index 743df5206fe48..d071c942bc85d 100644 --- a/lib/private/DB/QueryBuilder/QueryBuilder.php +++ b/lib/private/DB/QueryBuilder/QueryBuilder.php @@ -1105,7 +1105,7 @@ public function orHaving(...$having) { public function orderBy($sort, $order = null) { $this->queryBuilder->orderBy( $this->helper->quoteColumnName($sort), - $order + $order ?? 'ASC' ); return $this; @@ -1122,7 +1122,7 @@ public function orderBy($sort, $order = null) { public function addOrderBy($sort, $order = null) { $this->queryBuilder->addOrderBy( $this->helper->quoteColumnName($sort), - $order + $order ?? 'ASC' ); return $this; diff --git a/tests/lib/DB/QueryBuilder/QueryBuilderTest.php b/tests/lib/DB/QueryBuilder/QueryBuilderTest.php index 96cde8ba1f9e8..55ec87f915674 100644 --- a/tests/lib/DB/QueryBuilder/QueryBuilderTest.php +++ b/tests/lib/DB/QueryBuilder/QueryBuilderTest.php @@ -407,154 +407,99 @@ public function testAddSelect($selectArguments, $expected, $expectedLiteral = '' $this->deleteTestingRows(); } - public function dataDelete() { + public function dataDelete(): array { return [ - ['data', null, ['table' => '`*PREFIX*data`', 'alias' => null], '`*PREFIX*data`'], - ['data', 't', ['table' => '`*PREFIX*data`', 'alias' => 't'], '`*PREFIX*data` t'], + ['data', null, '`*PREFIX*data`'], + ['data', 't', '`*PREFIX*data`'], ]; } /** * @dataProvider dataDelete - * - * @param string $tableName - * @param string $tableAlias - * @param array $expectedQueryPart - * @param string $expectedQuery */ - public function testDelete($tableName, $tableAlias, $expectedQueryPart, $expectedQuery) { + public function testDelete(string $tableName, ?string $tableAlias, string $expectedQuery): void { $this->queryBuilder->delete($tableName, $tableAlias); - $this->assertSame( - $expectedQueryPart, - $this->queryBuilder->getQueryPart('from') - ); - $this->assertSame( 'DELETE FROM ' . $expectedQuery, $this->queryBuilder->getSQL() ); } - public function dataUpdate() { + public function dataUpdate(): array { return [ - ['data', null, ['table' => '`*PREFIX*data`', 'alias' => null], '`*PREFIX*data`'], - ['data', 't', ['table' => '`*PREFIX*data`', 'alias' => 't'], '`*PREFIX*data` t'], + ['data', null, '`*PREFIX*data`'], + ['data', 't', '`*PREFIX*data`'], ]; } /** * @dataProvider dataUpdate - * - * @param string $tableName - * @param string $tableAlias - * @param array $expectedQueryPart - * @param string $expectedQuery */ - public function testUpdate($tableName, $tableAlias, $expectedQueryPart, $expectedQuery) { + public function testUpdate(string $tableName, ?string $tableAlias, string $expectedQuery): void { $this->queryBuilder->update($tableName, $tableAlias); - $this->assertSame( - $expectedQueryPart, - $this->queryBuilder->getQueryPart('from') - ); - $this->assertSame( 'UPDATE ' . $expectedQuery . ' SET ', $this->queryBuilder->getSQL() ); } - public function dataInsert() { + public function dataInsert(): array { return [ - ['data', ['table' => '`*PREFIX*data`'], '`*PREFIX*data`'], + ['data', '`*PREFIX*data`'], ]; } /** * @dataProvider dataInsert - * - * @param string $tableName - * @param array $expectedQueryPart - * @param string $expectedQuery */ - public function testInsert($tableName, $expectedQueryPart, $expectedQuery) { + public function testInsert(string $tableName, string $expectedQuery): void { $this->queryBuilder->insert($tableName); - $this->assertSame( - $expectedQueryPart, - $this->queryBuilder->getQueryPart('from') - ); - $this->assertSame( 'INSERT INTO ' . $expectedQuery . ' () VALUES()', $this->queryBuilder->getSQL() ); } - public function dataFrom() { + public function dataFrom(): array { $config = $this->createMock(SystemConfig::class); $logger = $this->createMock(LoggerInterface::class); $qb = new QueryBuilder(\OC::$server->getDatabaseConnection(), $config, $logger); return [ - [$qb->createFunction('(' . $qb->select('*')->from('test')->getSQL() . ')'), 'q', null, null, [ - ['table' => '(SELECT * FROM `*PREFIX*test`)', 'alias' => '`q`'] - ], '(SELECT * FROM `*PREFIX*test`) `q`'], - ['data', null, null, null, [['table' => '`*PREFIX*data`', 'alias' => null]], '`*PREFIX*data`'], - ['data', 't', null, null, [['table' => '`*PREFIX*data`', 'alias' => '`t`']], '`*PREFIX*data` `t`'], - ['data1', null, 'data2', null, [ - ['table' => '`*PREFIX*data1`', 'alias' => null], - ['table' => '`*PREFIX*data2`', 'alias' => null] - ], '`*PREFIX*data1`, `*PREFIX*data2`'], - ['data', 't1', 'data', 't2', [ - ['table' => '`*PREFIX*data`', 'alias' => '`t1`'], - ['table' => '`*PREFIX*data`', 'alias' => '`t2`'] - ], '`*PREFIX*data` `t1`, `*PREFIX*data` `t2`'], + [$qb->createFunction('(' . $qb->select('*')->from('test')->getSQL() . ')'), 'q', null, null, '(SELECT * FROM `*PREFIX*test`) `q`'], + ['data', null, null, null, '`*PREFIX*data`'], + ['data', 't', null, null, '`*PREFIX*data` `t`'], + ['data1', null, 'data2', null, '`*PREFIX*data1`, `*PREFIX*data2`'], + ['data', 't1', 'data', 't2', '`*PREFIX*data` `t1`, `*PREFIX*data` `t2`'], ]; } /** * @dataProvider dataFrom - * - * @param string|IQueryFunction $table1Name - * @param string $table1Alias - * @param string|IQueryFunction $table2Name - * @param string $table2Alias - * @param array $expectedQueryPart - * @param string $expectedQuery */ - public function testFrom($table1Name, $table1Alias, $table2Name, $table2Alias, $expectedQueryPart, $expectedQuery) { + public function testFrom(string|IQueryFunction $table1Name, ?string $table1Alias, null|string|IQueryFunction $table2Name, ?string $table2Alias, string $expectedQuery): void { + $this->queryBuilder->select('*'); $this->queryBuilder->from($table1Name, $table1Alias); if ($table2Name !== null) { $this->queryBuilder->from($table2Name, $table2Alias); } $this->assertSame( - $expectedQueryPart, - $this->queryBuilder->getQueryPart('from') - ); - - $this->assertSame( - 'SELECT FROM ' . $expectedQuery, + 'SELECT * FROM ' . $expectedQuery, $this->queryBuilder->getSQL() ); } - public function dataJoin() { + public function dataJoin(): array { return [ - [ - 'd1', 'data2', null, null, - ['`d1`' => [['joinType' => 'inner', 'joinTable' => '`*PREFIX*data2`', 'joinAlias' => null, 'joinCondition' => null]]], - '`*PREFIX*data1` `d1` INNER JOIN `*PREFIX*data2` ' - ], [ 'd1', 'data2', 'd2', null, - ['`d1`' => [['joinType' => 'inner', 'joinTable' => '`*PREFIX*data2`', 'joinAlias' => '`d2`', 'joinCondition' => null]]], '`*PREFIX*data1` `d1` INNER JOIN `*PREFIX*data2` `d2`' ], [ 'd1', 'data2', 'd2', '`d1`.`field1` = `d2`.`field2`', - ['`d1`' => [['joinType' => 'inner', 'joinTable' => '`*PREFIX*data2`', 'joinAlias' => '`d2`', 'joinCondition' => '`d1`.`field1` = `d2`.`field2`']]], '`*PREFIX*data1` `d1` INNER JOIN `*PREFIX*data2` `d2` ON `d1`.`field1` = `d2`.`field2`' ], @@ -563,15 +508,9 @@ public function dataJoin() { /** * @dataProvider dataJoin - * - * @param string $fromAlias - * @param string $tableName - * @param string $tableAlias - * @param string $condition - * @param array $expectedQueryPart - * @param string $expectedQuery */ - public function testJoin($fromAlias, $tableName, $tableAlias, $condition, $expectedQueryPart, $expectedQuery) { + public function testJoin(string $fromAlias, string $tableName, string $tableAlias, ?string $condition, string $expectedQuery): void { + $this->queryBuilder->select('*'); $this->queryBuilder->from('data1', 'd1'); $this->queryBuilder->join( $fromAlias, @@ -580,28 +519,18 @@ public function testJoin($fromAlias, $tableName, $tableAlias, $condition, $expec $condition ); - $this->assertSame( - $expectedQueryPart, - $this->queryBuilder->getQueryPart('join') - ); $this->assertSame( - 'SELECT FROM ' . $expectedQuery, + 'SELECT * FROM ' . $expectedQuery, $this->queryBuilder->getSQL() ); } /** * @dataProvider dataJoin - * - * @param string $fromAlias - * @param string $tableName - * @param string $tableAlias - * @param string $condition - * @param array $expectedQueryPart - * @param string $expectedQuery */ - public function testInnerJoin($fromAlias, $tableName, $tableAlias, $condition, $expectedQueryPart, $expectedQuery) { + public function testInnerJoin(string $fromAlias, string $tableName, string $tableAlias, ?string $condition, string $expectedQuery): void { + $this->queryBuilder->select('*'); $this->queryBuilder->from('data1', 'd1'); $this->queryBuilder->innerJoin( $fromAlias, @@ -611,31 +540,19 @@ public function testInnerJoin($fromAlias, $tableName, $tableAlias, $condition, $ ); $this->assertSame( - $expectedQueryPart, - $this->queryBuilder->getQueryPart('join') - ); - - $this->assertSame( - 'SELECT FROM ' . $expectedQuery, + 'SELECT * FROM ' . $expectedQuery, $this->queryBuilder->getSQL() ); } - public function dataLeftJoin() { + public function dataLeftJoin(): array { return [ - [ - 'd1', 'data2', null, null, - ['`d1`' => [['joinType' => 'left', 'joinTable' => '`*PREFIX*data2`', 'joinAlias' => null, 'joinCondition' => null]]], - '`*PREFIX*data1` `d1` LEFT JOIN `*PREFIX*data2` ' - ], [ 'd1', 'data2', 'd2', null, - ['`d1`' => [['joinType' => 'left', 'joinTable' => '`*PREFIX*data2`', 'joinAlias' => '`d2`', 'joinCondition' => null]]], '`*PREFIX*data1` `d1` LEFT JOIN `*PREFIX*data2` `d2`' ], [ 'd1', 'data2', 'd2', '`d1`.`field1` = `d2`.`field2`', - ['`d1`' => [['joinType' => 'left', 'joinTable' => '`*PREFIX*data2`', 'joinAlias' => '`d2`', 'joinCondition' => '`d1`.`field1` = `d2`.`field2`']]], '`*PREFIX*data1` `d1` LEFT JOIN `*PREFIX*data2` `d2` ON `d1`.`field1` = `d2`.`field2`' ], ]; @@ -643,15 +560,9 @@ public function dataLeftJoin() { /** * @dataProvider dataLeftJoin - * - * @param string $fromAlias - * @param string $tableName - * @param string $tableAlias - * @param string $condition - * @param array $expectedQueryPart - * @param string $expectedQuery */ - public function testLeftJoin($fromAlias, $tableName, $tableAlias, $condition, $expectedQueryPart, $expectedQuery) { + public function testLeftJoin(string $fromAlias, string $tableName, string $tableAlias, ?string $condition, string $expectedQuery): void { + $this->queryBuilder->select('*'); $this->queryBuilder->from('data1', 'd1'); $this->queryBuilder->leftJoin( $fromAlias, @@ -661,31 +572,19 @@ public function testLeftJoin($fromAlias, $tableName, $tableAlias, $condition, $e ); $this->assertSame( - $expectedQueryPart, - $this->queryBuilder->getQueryPart('join') - ); - - $this->assertSame( - 'SELECT FROM ' . $expectedQuery, + 'SELECT * FROM ' . $expectedQuery, $this->queryBuilder->getSQL() ); } - public function dataRightJoin() { + public function dataRightJoin(): array { return [ - [ - 'd1', 'data2', null, null, - ['`d1`' => [['joinType' => 'right', 'joinTable' => '`*PREFIX*data2`', 'joinAlias' => null, 'joinCondition' => null]]], - '`*PREFIX*data1` `d1` RIGHT JOIN `*PREFIX*data2` ' - ], [ 'd1', 'data2', 'd2', null, - ['`d1`' => [['joinType' => 'right', 'joinTable' => '`*PREFIX*data2`', 'joinAlias' => '`d2`', 'joinCondition' => null]]], '`*PREFIX*data1` `d1` RIGHT JOIN `*PREFIX*data2` `d2`' ], [ 'd1', 'data2', 'd2', '`d1`.`field1` = `d2`.`field2`', - ['`d1`' => [['joinType' => 'right', 'joinTable' => '`*PREFIX*data2`', 'joinAlias' => '`d2`', 'joinCondition' => '`d1`.`field1` = `d2`.`field2`']]], '`*PREFIX*data1` `d1` RIGHT JOIN `*PREFIX*data2` `d2` ON `d1`.`field1` = `d2`.`field2`' ], ]; @@ -693,15 +592,9 @@ public function dataRightJoin() { /** * @dataProvider dataRightJoin - * - * @param string $fromAlias - * @param string $tableName - * @param string $tableAlias - * @param string $condition - * @param array $expectedQueryPart - * @param string $expectedQuery */ - public function testRightJoin($fromAlias, $tableName, $tableAlias, $condition, $expectedQueryPart, $expectedQuery) { + public function testRightJoin(string $fromAlias, string $tableName, string $tableAlias, ?string $condition, string $expectedQuery): void { + $this->queryBuilder->select('*'); $this->queryBuilder->from('data1', 'd1'); $this->queryBuilder->rightJoin( $fromAlias, @@ -711,79 +604,53 @@ public function testRightJoin($fromAlias, $tableName, $tableAlias, $condition, $ ); $this->assertSame( - $expectedQueryPart, - $this->queryBuilder->getQueryPart('join') - ); - - $this->assertSame( - 'SELECT FROM ' . $expectedQuery, + 'SELECT * FROM ' . $expectedQuery, $this->queryBuilder->getSQL() ); } - public function dataSet() { + public function dataSet(): array { return [ - ['column1', new Literal('value'), null, null, ['`column1` = value'], '`column1` = value'], - ['column1', new Parameter(':param'), null, null, ['`column1` = :param'], '`column1` = :param'], - ['column1', 'column2', null, null, ['`column1` = `column2`'], '`column1` = `column2`'], - ['column1', 'column2', 'column3', new Literal('value'), ['`column1` = `column2`', '`column3` = value'], '`column1` = `column2`, `column3` = value'], + ['column1', new Literal('value'), null, null, '`column1` = value'], + ['column1', new Parameter(':param'), null, null, '`column1` = :param'], + ['column1', 'column2', null, null, '`column1` = `column2`'], + ['column1', 'column2', 'column3', new Literal('value'), '`column1` = `column2`, `column3` = value'], ]; } /** * @dataProvider dataSet - * - * @param string $partOne1 - * @param string $partOne2 - * @param string $partTwo1 - * @param string $partTwo2 - * @param array $expectedQueryPart - * @param string $expectedQuery */ - public function testSet($partOne1, $partOne2, $partTwo1, $partTwo2, $expectedQueryPart, $expectedQuery) { + public function testSet(string $partOne1, string|Literal|Parameter $partOne2, ?string $partTwo1, null|string|Literal|Parameter $partTwo2, string $expectedQuery): void { $this->queryBuilder->update('data'); $this->queryBuilder->set($partOne1, $partOne2); if ($partTwo1 !== null) { $this->queryBuilder->set($partTwo1, $partTwo2); } - $this->assertSame( - $expectedQueryPart, - $this->queryBuilder->getQueryPart('set') - ); - $this->assertSame( 'UPDATE `*PREFIX*data` SET ' . $expectedQuery, $this->queryBuilder->getSQL() ); } - public function dataWhere() { + public function dataWhere(): array { return [ - [['where1'], new CompositeExpression('AND', ['where1']), 'where1'], - [['where1', 'where2'], new CompositeExpression('AND', ['where1', 'where2']), '(where1) AND (where2)'], + [['where1'], 'where1'], + [['where1', 'where2'], '(where1) AND (where2)'], ]; } /** * @dataProvider dataWhere - * - * @param array $whereArguments - * @param array $expectedQueryPart - * @param string $expectedQuery */ - public function testWhere($whereArguments, $expectedQueryPart, $expectedQuery) { + public function testWhere(array $whereArguments, string $expectedQuery): void { $this->queryBuilder->select('column'); call_user_func_array( [$this->queryBuilder, 'where'], $whereArguments ); - $this->assertEquals( - $expectedQueryPart, - $this->queryBuilder->getQueryPart('where') - ); - $this->assertSame( 'SELECT `column` WHERE ' . $expectedQuery, $this->queryBuilder->getSQL() @@ -797,103 +664,75 @@ public function testWhere($whereArguments, $expectedQueryPart, $expectedQuery) { * @param array $expectedQueryPart * @param string $expectedQuery */ - public function testAndWhere($whereArguments, $expectedQueryPart, $expectedQuery) { + public function testAndWhere(array $whereArguments, string $expectedQuery): void { $this->queryBuilder->select('column'); call_user_func_array( [$this->queryBuilder, 'andWhere'], $whereArguments ); - $this->assertEquals( - $expectedQueryPart, - $this->queryBuilder->getQueryPart('where') - ); - $this->assertSame( 'SELECT `column` WHERE ' . $expectedQuery, $this->queryBuilder->getSQL() ); } - public function dataOrWhere() { + public function dataOrWhere(): array { return [ - [['where1'], new CompositeExpression('OR', ['where1']), 'where1'], - [['where1', 'where2'], new CompositeExpression('OR', ['where1', 'where2']), '(where1) OR (where2)'], + [['where1'], 'where1'], + [['where1', 'where2'], '(where1) OR (where2)'], ]; } /** * @dataProvider dataOrWhere - * - * @param array $whereArguments - * @param array $expectedQueryPart - * @param string $expectedQuery */ - public function testOrWhere($whereArguments, $expectedQueryPart, $expectedQuery) { + public function testOrWhere(array $whereArguments, string $expectedQuery): void { $this->queryBuilder->select('column'); call_user_func_array( [$this->queryBuilder, 'orWhere'], $whereArguments ); - $this->assertEquals( - $expectedQueryPart, - $this->queryBuilder->getQueryPart('where') - ); - $this->assertSame( 'SELECT `column` WHERE ' . $expectedQuery, $this->queryBuilder->getSQL() ); } - public function dataGroupBy() { + public function dataGroupBy(): array { return [ - [['column1'], ['`column1`'], '`column1`'], - [['column1', 'column2'], ['`column1`', '`column2`'], '`column1`, `column2`'], + [['column1'], '`column1`'], + [['column1', 'column2'], '`column1`, `column2`'], ]; } /** * @dataProvider dataGroupBy - * - * @param array $groupByArguments - * @param array $expectedQueryPart - * @param string $expectedQuery */ - public function testGroupBy($groupByArguments, $expectedQueryPart, $expectedQuery) { + public function testGroupBy(array $groupByArguments, string $expectedQuery): void { $this->queryBuilder->select('column'); call_user_func_array( [$this->queryBuilder, 'groupBy'], $groupByArguments ); - - $this->assertEquals( - $expectedQueryPart, - $this->queryBuilder->getQueryPart('groupBy') - ); - $this->assertSame( 'SELECT `column` GROUP BY ' . $expectedQuery, $this->queryBuilder->getSQL() ); } - public function dataAddGroupBy() { + public function dataAddGroupBy(): array { return [ - [['column2'], ['`column1`', '`column2`'], '`column1`, `column2`'], - [['column2', 'column3'], ['`column1`', '`column2`', '`column3`'], '`column1`, `column2`, `column3`'], + [['column2'], '`column1`, `column2`'], + [['column2', 'column3'], '`column1`, `column2`, `column3`'], ]; } /** * @dataProvider dataAddGroupBy - * - * @param array $groupByArguments - * @param array $expectedQueryPart - * @param string $expectedQuery */ - public function testAddGroupBy($groupByArguments, $expectedQueryPart, $expectedQuery) { + public function testAddGroupBy(array $groupByArguments, string $expectedQuery): void { $this->queryBuilder->select('column'); $this->queryBuilder->groupBy('column1'); call_user_func_array( @@ -901,40 +740,25 @@ public function testAddGroupBy($groupByArguments, $expectedQueryPart, $expectedQ $groupByArguments ); - $this->assertEquals( - $expectedQueryPart, - $this->queryBuilder->getQueryPart('groupBy') - ); - $this->assertSame( 'SELECT `column` GROUP BY ' . $expectedQuery, $this->queryBuilder->getSQL() ); } - public function dataSetValue() { + public function dataSetValue(): array { return [ - ['column', 'value', ['`column`' => 'value'], '(`column`) VALUES(value)'], + ['column', 'value', '(`column`) VALUES(value)'], ]; } /** * @dataProvider dataSetValue - * - * @param string $column - * @param string $value - * @param array $expectedQueryPart - * @param string $expectedQuery */ - public function testSetValue($column, $value, $expectedQueryPart, $expectedQuery) { + public function testSetValue(string $column, string $value, string $expectedQuery): void { $this->queryBuilder->insert('data'); $this->queryBuilder->setValue($column, $value); - $this->assertEquals( - $expectedQueryPart, - $this->queryBuilder->getQueryPart('values') - ); - $this->assertSame( 'INSERT INTO `*PREFIX*data` ' . $expectedQuery, $this->queryBuilder->getSQL() @@ -943,41 +767,29 @@ public function testSetValue($column, $value, $expectedQueryPart, $expectedQuery /** * @dataProvider dataSetValue - * - * @param string $column - * @param string $value - * @param array $expectedQueryPart - * @param string $expectedQuery */ - public function testValues($column, $value, $expectedQueryPart, $expectedQuery) { + public function testValues(string $column, string $value, string $expectedQuery): void { $this->queryBuilder->insert('data'); $this->queryBuilder->values([ $column => $value, ]); - $this->assertEquals( - $expectedQueryPart, - $this->queryBuilder->getQueryPart('values') - ); - $this->assertSame( 'INSERT INTO `*PREFIX*data` ' . $expectedQuery, $this->queryBuilder->getSQL() ); } - public function dataHaving() { + public function dataHaving(): array { return [ - [['condition1'], new CompositeExpression('AND', ['condition1']), 'HAVING condition1'], - [['condition1', 'condition2'], new CompositeExpression('AND', ['condition1', 'condition2']), 'HAVING (condition1) AND (condition2)'], + [['condition1'], 'HAVING condition1'], + [['condition1', 'condition2'], 'HAVING (condition1) AND (condition2)'], [ - [new CompositeExpression('OR', ['condition1', 'condition2'])], - new CompositeExpression('OR', ['condition1', 'condition2']), + [new CompositeExpression('OR', 'condition1', 'condition2')], 'HAVING (condition1) OR (condition2)' ], [ - [new CompositeExpression('AND', ['condition1', 'condition2'])], - new CompositeExpression('AND', ['condition1', 'condition2']), + [new CompositeExpression('AND', 'condition1', 'condition2')], 'HAVING (condition1) AND (condition2)' ], ]; @@ -985,40 +797,30 @@ public function dataHaving() { /** * @dataProvider dataHaving - * - * @param array $havingArguments - * @param array $expectedQueryPart - * @param string $expectedQuery */ - public function testHaving($havingArguments, $expectedQueryPart, $expectedQuery) { + public function testHaving(array $havingArguments, string $expectedQuery): void { + $this->queryBuilder->select('*'); call_user_func_array( [$this->queryBuilder, 'having'], $havingArguments ); - $this->assertEquals( - $expectedQueryPart, - $this->queryBuilder->getQueryPart('having') - ); - $this->assertSame( - 'SELECT ' . $expectedQuery, + 'SELECT * ' . $expectedQuery, $this->queryBuilder->getSQL() ); } - public function dataAndHaving() { + public function dataAndHaving(): array { return [ - [['condition2'], new CompositeExpression('AND', ['condition1', 'condition2']), 'HAVING (condition1) AND (condition2)'], - [['condition2', 'condition3'], new CompositeExpression('AND', ['condition1', 'condition2', 'condition3']), 'HAVING (condition1) AND (condition2) AND (condition3)'], + [['condition2'], 'HAVING (condition1) AND (condition2)'], + [['condition2', 'condition3'], 'HAVING (condition1) AND (condition2) AND (condition3)'], [ - [new CompositeExpression('OR', ['condition2', 'condition3'])], - new CompositeExpression('AND', ['condition1', new CompositeExpression('OR', ['condition2', 'condition3'])]), + [new CompositeExpression('OR', 'condition2', 'condition3')], 'HAVING (condition1) AND ((condition2) OR (condition3))' ], [ - [new CompositeExpression('AND', ['condition2', 'condition3'])], - new CompositeExpression('AND', ['condition1', new CompositeExpression('AND', ['condition2', 'condition3'])]), + [new CompositeExpression('AND', 'condition2', 'condition3')], 'HAVING (condition1) AND ((condition2) AND (condition3))' ], ]; @@ -1026,41 +828,31 @@ public function dataAndHaving() { /** * @dataProvider dataAndHaving - * - * @param array $havingArguments - * @param array $expectedQueryPart - * @param string $expectedQuery */ - public function testAndHaving($havingArguments, $expectedQueryPart, $expectedQuery) { + public function testAndHaving(array $havingArguments, string $expectedQuery): void { + $this->queryBuilder->select('*'); $this->queryBuilder->having('condition1'); call_user_func_array( [$this->queryBuilder, 'andHaving'], $havingArguments ); - $this->assertEquals( - $expectedQueryPart, - $this->queryBuilder->getQueryPart('having') - ); - $this->assertSame( - 'SELECT ' . $expectedQuery, + 'SELECT * ' . $expectedQuery, $this->queryBuilder->getSQL() ); } - public function dataOrHaving() { + public function dataOrHaving(): array { return [ - [['condition2'], new CompositeExpression('OR', ['condition1', 'condition2']), 'HAVING (condition1) OR (condition2)'], - [['condition2', 'condition3'], new CompositeExpression('OR', ['condition1', 'condition2', 'condition3']), 'HAVING (condition1) OR (condition2) OR (condition3)'], + [['condition2'], 'HAVING (condition1) OR (condition2)'], + [['condition2', 'condition3'], 'HAVING (condition1) OR (condition2) OR (condition3)'], [ - [new CompositeExpression('OR', ['condition2', 'condition3'])], - new CompositeExpression('OR', ['condition1', new CompositeExpression('OR', ['condition2', 'condition3'])]), + [new CompositeExpression('OR', 'condition2', 'condition3')], 'HAVING (condition1) OR ((condition2) OR (condition3))' ], [ - [new CompositeExpression('AND', ['condition2', 'condition3'])], - new CompositeExpression('OR', ['condition1', new CompositeExpression('AND', ['condition2', 'condition3'])]), + [new CompositeExpression('AND', 'condition2', 'condition3')], 'HAVING (condition1) OR ((condition2) AND (condition3))' ], ]; @@ -1068,93 +860,66 @@ public function dataOrHaving() { /** * @dataProvider dataOrHaving - * - * @param array $havingArguments - * @param array $expectedQueryPart - * @param string $expectedQuery */ - public function testOrHaving($havingArguments, $expectedQueryPart, $expectedQuery) { + public function testOrHaving(array $havingArguments, string $expectedQuery): void { + $this->queryBuilder->select('*'); $this->queryBuilder->having('condition1'); call_user_func_array( [$this->queryBuilder, 'orHaving'], $havingArguments ); - $this->assertEquals( - $expectedQueryPart, - $this->queryBuilder->getQueryPart('having') - ); - $this->assertSame( - 'SELECT ' . $expectedQuery, + 'SELECT * ' . $expectedQuery, $this->queryBuilder->getSQL() ); } - public function dataOrderBy() { + public function dataOrderBy(): array { return [ - ['column', null, ['`column` ASC'], 'ORDER BY `column` ASC'], - ['column', 'ASC', ['`column` ASC'], 'ORDER BY `column` ASC'], - ['column', 'DESC', ['`column` DESC'], 'ORDER BY `column` DESC'], + ['column', null, 'ORDER BY `column` ASC'], + ['column', 'ASC', 'ORDER BY `column` ASC'], + ['column', 'DESC', 'ORDER BY `column` DESC'], ]; } /** * @dataProvider dataOrderBy - * - * @param string $sort - * @param string $order - * @param array $expectedQueryPart - * @param string $expectedQuery */ - public function testOrderBy($sort, $order, $expectedQueryPart, $expectedQuery) { + public function testOrderBy(string $sort, ?string $order, string $expectedQuery): void { + $this->queryBuilder->select('*'); $this->queryBuilder->orderBy($sort, $order); - $this->assertEquals( - $expectedQueryPart, - $this->queryBuilder->getQueryPart('orderBy') - ); - $this->assertSame( - 'SELECT ' . $expectedQuery, + 'SELECT * ' . $expectedQuery, $this->queryBuilder->getSQL() ); } - public function dataAddOrderBy() { + public function dataAddOrderBy(): array { return [ - ['column2', null, null, ['`column1` ASC', '`column2` ASC'], 'ORDER BY `column1` ASC, `column2` ASC'], - ['column2', null, 'ASC', ['`column1` ASC', '`column2` ASC'], 'ORDER BY `column1` ASC, `column2` ASC'], - ['column2', null, 'DESC', ['`column1` DESC', '`column2` ASC'], 'ORDER BY `column1` DESC, `column2` ASC'], - ['column2', 'ASC', null, ['`column1` ASC', '`column2` ASC'], 'ORDER BY `column1` ASC, `column2` ASC'], - ['column2', 'ASC', 'ASC', ['`column1` ASC', '`column2` ASC'], 'ORDER BY `column1` ASC, `column2` ASC'], - ['column2', 'ASC', 'DESC', ['`column1` DESC', '`column2` ASC'], 'ORDER BY `column1` DESC, `column2` ASC'], - ['column2', 'DESC', null, ['`column1` ASC', '`column2` DESC'], 'ORDER BY `column1` ASC, `column2` DESC'], - ['column2', 'DESC', 'ASC', ['`column1` ASC', '`column2` DESC'], 'ORDER BY `column1` ASC, `column2` DESC'], - ['column2', 'DESC', 'DESC', ['`column1` DESC', '`column2` DESC'], 'ORDER BY `column1` DESC, `column2` DESC'], + ['column2', null, null, 'ORDER BY `column1` ASC, `column2` ASC'], + ['column2', null, 'ASC', 'ORDER BY `column1` ASC, `column2` ASC'], + ['column2', null, 'DESC', 'ORDER BY `column1` DESC, `column2` ASC'], + ['column2', 'ASC', null, 'ORDER BY `column1` ASC, `column2` ASC'], + ['column2', 'ASC', 'ASC', 'ORDER BY `column1` ASC, `column2` ASC'], + ['column2', 'ASC', 'DESC', 'ORDER BY `column1` DESC, `column2` ASC'], + ['column2', 'DESC', null, 'ORDER BY `column1` ASC, `column2` DESC'], + ['column2', 'DESC', 'ASC', 'ORDER BY `column1` ASC, `column2` DESC'], + ['column2', 'DESC', 'DESC', 'ORDER BY `column1` DESC, `column2` DESC'], ]; } /** * @dataProvider dataAddOrderBy - * - * @param string $sort2 - * @param string $order2 - * @param string $order1 - * @param array $expectedQueryPart - * @param string $expectedQuery */ - public function testAddOrderBy($sort2, $order2, $order1, $expectedQueryPart, $expectedQuery) { + public function testAddOrderBy(string $sort2, ?string $order2, ?string $order1, string $expectedQuery): void { + $this->queryBuilder->select('*'); $this->queryBuilder->orderBy('column1', $order1); $this->queryBuilder->addOrderBy($sort2, $order2); - $this->assertEquals( - $expectedQueryPart, - $this->queryBuilder->getQueryPart('orderBy') - ); - $this->assertSame( - 'SELECT ' . $expectedQuery, + 'SELECT * ' . $expectedQuery, $this->queryBuilder->getSQL() ); } @@ -1253,11 +1018,11 @@ public function testGetColumnName($column, $prefix, $expected) { ); } - public function testExecuteWithoutLogger() { + public function testExecuteWithoutLogger(): void { $queryBuilder = $this->createMock(\Doctrine\DBAL\Query\QueryBuilder::class); $queryBuilder ->expects($this->once()) - ->method('execute') + ->method('executeStatement') ->willReturn(3); $queryBuilder ->expects($this->any()) @@ -1272,11 +1037,12 @@ public function testExecuteWithoutLogger() { ->with('log_query', false) ->willReturn(false); - $this->invokePrivate($this->queryBuilder, 'queryBuilder', [$queryBuilder]); + $this->queryBuilder->insert('migrations'); + self::invokePrivate($this->queryBuilder, 'queryBuilder', [$queryBuilder]); $this->assertEquals(3, $this->queryBuilder->execute()); } - public function testExecuteWithLoggerAndNamedArray() { + public function testExecuteWithLoggerAndNamedArray(): void { $queryBuilder = $this->createMock(\Doctrine\DBAL\Query\QueryBuilder::class); $queryBuilder ->expects($this->any()) @@ -1291,7 +1057,7 @@ public function testExecuteWithLoggerAndNamedArray() { ->willReturn('SELECT * FROM FOO WHERE BAR = ?'); $queryBuilder ->expects($this->once()) - ->method('execute') + ->method('executeStatement') ->willReturn(3); $this->logger ->expects($this->once()) @@ -1310,11 +1076,12 @@ public function testExecuteWithLoggerAndNamedArray() { ->with('log_query', false) ->willReturn(true); - $this->invokePrivate($this->queryBuilder, 'queryBuilder', [$queryBuilder]); + $this->queryBuilder->insert('migrations'); + self::invokePrivate($this->queryBuilder, 'queryBuilder', [$queryBuilder]); $this->assertEquals(3, $this->queryBuilder->execute()); } - public function testExecuteWithLoggerAndUnnamedArray() { + public function testExecuteWithLoggerAndUnnamedArray(): void { $queryBuilder = $this->createMock(\Doctrine\DBAL\Query\QueryBuilder::class); $queryBuilder ->expects($this->any()) @@ -1326,7 +1093,7 @@ public function testExecuteWithLoggerAndUnnamedArray() { ->willReturn('SELECT * FROM FOO WHERE BAR = ?'); $queryBuilder ->expects($this->once()) - ->method('execute') + ->method('executeStatement') ->willReturn(3); $this->logger ->expects($this->once()) @@ -1345,11 +1112,12 @@ public function testExecuteWithLoggerAndUnnamedArray() { ->with('log_query', false) ->willReturn(true); - $this->invokePrivate($this->queryBuilder, 'queryBuilder', [$queryBuilder]); + $this->queryBuilder->insert('migrations'); + self::invokePrivate($this->queryBuilder, 'queryBuilder', [$queryBuilder]); $this->assertEquals(3, $this->queryBuilder->execute()); } - public function testExecuteWithLoggerAndNoParams() { + public function testExecuteWithLoggerAndNoParams(): void { $queryBuilder = $this->createMock(\Doctrine\DBAL\Query\QueryBuilder::class); $queryBuilder ->expects($this->any()) @@ -1361,7 +1129,7 @@ public function testExecuteWithLoggerAndNoParams() { ->willReturn('SELECT * FROM FOO WHERE BAR = ?'); $queryBuilder ->expects($this->once()) - ->method('execute') + ->method('executeStatement') ->willReturn(3); $this->logger ->expects($this->once()) @@ -1379,11 +1147,12 @@ public function testExecuteWithLoggerAndNoParams() { ->with('log_query', false) ->willReturn(true); - $this->invokePrivate($this->queryBuilder, 'queryBuilder', [$queryBuilder]); + $this->queryBuilder->insert('migrations'); + self::invokePrivate($this->queryBuilder, 'queryBuilder', [$queryBuilder]); $this->assertEquals(3, $this->queryBuilder->execute()); } - public function testExecuteWithParameterTooLarge() { + public function testExecuteWithParameterTooLarge(): void { $queryBuilder = $this->createMock(\Doctrine\DBAL\Query\QueryBuilder::class); $p = array_fill(0, 1001, 'foo'); $queryBuilder @@ -1396,7 +1165,7 @@ public function testExecuteWithParameterTooLarge() { ->willReturn('SELECT * FROM FOO WHERE BAR IN (?)'); $queryBuilder ->expects($this->once()) - ->method('execute') + ->method('executeQuery') ->willReturn($this->createMock(Result::class)); $this->logger ->expects($this->once()) @@ -1414,11 +1183,11 @@ public function testExecuteWithParameterTooLarge() { ->with('log_query', false) ->willReturn(false); - $this->invokePrivate($this->queryBuilder, 'queryBuilder', [$queryBuilder]); + self::invokePrivate($this->queryBuilder, 'queryBuilder', [$queryBuilder]); $this->queryBuilder->execute(); } - public function testExecuteWithParametersTooMany() { + public function testExecuteWithParametersTooMany(): void { $queryBuilder = $this->createMock(\Doctrine\DBAL\Query\QueryBuilder::class); $p = array_fill(0, 999, 'foo'); $queryBuilder @@ -1431,7 +1200,7 @@ public function testExecuteWithParametersTooMany() { ->willReturn('SELECT * FROM FOO WHERE BAR IN (?) OR BAR IN (?)'); $queryBuilder ->expects($this->once()) - ->method('execute') + ->method('executeQuery') ->willReturn($this->createMock(Result::class)); $this->logger ->expects($this->once()) @@ -1449,7 +1218,7 @@ public function testExecuteWithParametersTooMany() { ->with('log_query', false) ->willReturn(false); - $this->invokePrivate($this->queryBuilder, 'queryBuilder', [$queryBuilder]); + self::invokePrivate($this->queryBuilder, 'queryBuilder', [$queryBuilder]); $this->queryBuilder->execute(); } } From 5718e8fff50d4d6487f21686c99a1243a79aa25a Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Wed, 3 Jul 2024 15:50:01 +0200 Subject: [PATCH 46/51] fix(tests): Fix changes in doctrine's Exception handling Signed-off-by: Joas Schilling --- tests/lib/DB/Exception/DbalExceptionTest.php | 8 +++--- tests/lib/DB/MigrationsTest.php | 30 ++++++++++---------- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/tests/lib/DB/Exception/DbalExceptionTest.php b/tests/lib/DB/Exception/DbalExceptionTest.php index 470beff9080b9..ec9a1f9716cde 100644 --- a/tests/lib/DB/Exception/DbalExceptionTest.php +++ b/tests/lib/DB/Exception/DbalExceptionTest.php @@ -7,7 +7,6 @@ */ namespace Test\DB\Exception; -use Doctrine\DBAL\ConnectionException; use Doctrine\DBAL\Driver\Exception as TheDriverException; use Doctrine\DBAL\Exception\ConstraintViolationException; use Doctrine\DBAL\Exception\DatabaseObjectExistsException; @@ -15,9 +14,10 @@ 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\InvalidWrapperClass; use Doctrine\DBAL\Exception\LockWaitTimeoutException; +use Doctrine\DBAL\Exception\NoActiveTransaction; use Doctrine\DBAL\Exception\NonUniqueFieldNameException; use Doctrine\DBAL\Exception\NotNullConstraintViolationException; use Doctrine\DBAL\Exception\ServerException; @@ -63,12 +63,12 @@ public function dataDriverException(): array { } public function testConnectionException(): void { - $result = DbalException::wrap(ConnectionException::noActiveTransaction()); + $result = DbalException::wrap(NoActiveTransaction::new()); $this->assertSame(DbalException::REASON_CONNECTION_LOST, $result->getReason()); } public function testInvalidArgumentException(): void { - $result = DbalException::wrap(InvalidArgumentException::fromEmptyCriteria()); + $result = DbalException::wrap(InvalidWrapperClass::new('A')); $this->assertSame(DbalException::REASON_INVALID_ARGUMENT, $result->getReason()); } } diff --git a/tests/lib/DB/MigrationsTest.php b/tests/lib/DB/MigrationsTest.php index 6cd489258bffe..05dbc13e372c8 100644 --- a/tests/lib/DB/MigrationsTest.php +++ b/tests/lib/DB/MigrationsTest.php @@ -11,10 +11,10 @@ use Doctrine\DBAL\Platforms\OraclePlatform; use Doctrine\DBAL\Platforms\PostgreSqlPlatform; use Doctrine\DBAL\Schema\Column; +use Doctrine\DBAL\Schema\Exception\TableDoesNotExist; use Doctrine\DBAL\Schema\ForeignKeyConstraint; use Doctrine\DBAL\Schema\Index; use Doctrine\DBAL\Schema\Schema; -use Doctrine\DBAL\Schema\SchemaException; use Doctrine\DBAL\Schema\Sequence; use Doctrine\DBAL\Schema\Table; use Doctrine\DBAL\Types\Type; @@ -100,7 +100,7 @@ public function testExecuteStepWithSchemaChange() { ->method('migrateToSchema'); $wrappedSchema = $this->createMock(Schema::class); - $wrappedSchema->expects($this->exactly(2)) + $wrappedSchema->expects($this->any()) ->method('getTables') ->willReturn([]); $wrappedSchema->expects($this->exactly(2)) @@ -273,7 +273,7 @@ public function testEnsureOracleConstraintsValid() { $sourceSchema = $this->createMock(Schema::class); $sourceSchema->expects($this->any()) ->method('getTable') - ->willThrowException(new SchemaException()); + ->willThrowException(TableDoesNotExist::new('a')); $sourceSchema->expects($this->any()) ->method('hasSequence') ->willReturn(false); @@ -316,7 +316,7 @@ public function testEnsureOracleConstraintsValidWithPrimaryKey() { $sourceSchema = $this->createMock(Schema::class); $sourceSchema->expects($this->any()) ->method('getTable') - ->willThrowException(new SchemaException()); + ->willThrowException(TableDoesNotExist::new('a')); $sourceSchema->expects($this->any()) ->method('hasSequence') ->willReturn(false); @@ -369,7 +369,7 @@ public function testEnsureOracleConstraintsValidWithPrimaryKeyDefault() { $sourceSchema = $this->createMock(Schema::class); $sourceSchema->expects($this->any()) ->method('getTable') - ->willThrowException(new SchemaException()); + ->willThrowException(TableDoesNotExist::new('a')); $sourceSchema->expects($this->any()) ->method('hasSequence') ->willReturn(false); @@ -394,7 +394,7 @@ public function testEnsureOracleConstraintsTooLongTableName() { $sourceSchema = $this->createMock(Schema::class); $sourceSchema->expects($this->any()) ->method('getTable') - ->willThrowException(new SchemaException()); + ->willThrowException(TableDoesNotExist::new('a')); $sourceSchema->expects($this->any()) ->method('hasSequence') ->willReturn(false); @@ -447,7 +447,7 @@ public function testEnsureOracleConstraintsTooLongPrimaryWithDefault() { $sourceSchema = $this->createMock(Schema::class); $sourceSchema->expects($this->any()) ->method('getTable') - ->willThrowException(new SchemaException()); + ->willThrowException(TableDoesNotExist::new('a')); $sourceSchema->expects($this->any()) ->method('hasSequence') ->willReturn(false); @@ -490,7 +490,7 @@ public function testEnsureOracleConstraintsTooLongPrimaryWithName() { $sourceSchema = $this->createMock(Schema::class); $sourceSchema->expects($this->any()) ->method('getTable') - ->willThrowException(new SchemaException()); + ->willThrowException(TableDoesNotExist::new('a')); $sourceSchema->expects($this->any()) ->method('hasSequence') ->willReturn(false); @@ -524,7 +524,7 @@ public function testEnsureOracleConstraintsTooLongColumnName() { $sourceSchema = $this->createMock(Schema::class); $sourceSchema->expects($this->any()) ->method('getTable') - ->willThrowException(new SchemaException()); + ->willThrowException(TableDoesNotExist::new('a')); $sourceSchema->expects($this->any()) ->method('hasSequence') ->willReturn(false); @@ -561,7 +561,7 @@ public function testEnsureOracleConstraintsTooLongIndexName() { $sourceSchema = $this->createMock(Schema::class); $sourceSchema->expects($this->any()) ->method('getTable') - ->willThrowException(new SchemaException()); + ->willThrowException(TableDoesNotExist::new('a')); $sourceSchema->expects($this->any()) ->method('hasSequence') ->willReturn(false); @@ -601,7 +601,7 @@ public function testEnsureOracleConstraintsTooLongForeignKeyName() { $sourceSchema = $this->createMock(Schema::class); $sourceSchema->expects($this->any()) ->method('getTable') - ->willThrowException(new SchemaException()); + ->willThrowException(TableDoesNotExist::new('a')); $sourceSchema->expects($this->any()) ->method('hasSequence') ->willReturn(false); @@ -642,7 +642,7 @@ public function testEnsureOracleConstraintsNoPrimaryKey() { $sourceSchema = $this->createMock(Schema::class); $sourceSchema->expects($this->any()) ->method('getTable') - ->willThrowException(new SchemaException()); + ->willThrowException(TableDoesNotExist::new('a')); $sourceSchema->expects($this->any()) ->method('hasSequence') ->willReturn(false); @@ -670,7 +670,7 @@ public function testEnsureOracleConstraintsTooLongSequenceName() { $sourceSchema = $this->createMock(Schema::class); $sourceSchema->expects($this->any()) ->method('getTable') - ->willThrowException(new SchemaException()); + ->willThrowException(TableDoesNotExist::new('a')); $sourceSchema->expects($this->any()) ->method('hasSequence') ->willReturn(false); @@ -710,7 +710,7 @@ public function testEnsureOracleConstraintsBooleanNotNull() { $sourceSchema = $this->createMock(Schema::class); $sourceSchema->expects($this->any()) ->method('getTable') - ->willThrowException(new SchemaException()); + ->willThrowException(TableDoesNotExist::new('a')); $sourceSchema->expects($this->any()) ->method('hasSequence') ->willReturn(false); @@ -750,7 +750,7 @@ public function testEnsureOracleConstraintsStringLength4000() { $sourceSchema = $this->createMock(Schema::class); $sourceSchema->expects($this->any()) ->method('getTable') - ->willThrowException(new SchemaException()); + ->willThrowException(TableDoesNotExist::new('a')); $sourceSchema->expects($this->any()) ->method('hasSequence') ->willReturn(false); From f0348be4c80bdadc8ace8ff20eaaae3af43ec15b Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Thu, 4 Jul 2024 08:55:05 +0200 Subject: [PATCH 47/51] fix(db): Replace more Platform usages Signed-off-by: Joas Schilling --- .../lib/Migration/CalDAVRemoveEmptyValue.php | 3 +-- .../SetupChecks/SupportedDatabaseTest.php | 4 +--- .../user_ldap/lib/Mapping/AbstractMapping.php | 4 ++-- core/Command/Db/ConvertFilecacheBigInt.php | 4 ++-- core/Command/Db/ConvertMysqlToMB4.php | 3 +-- lib/private/AllConfig.php | 5 ++--- lib/private/BackgroundJob/JobList.php | 3 +-- lib/private/DB/Connection.php | 17 ++++++++++++++++ lib/private/DB/ConnectionAdapter.php | 16 ++++----------- lib/private/DB/MigrationService.php | 7 +++---- lib/private/DB/QueryBuilder/QueryBuilder.php | 20 ++++++------------- lib/private/Repair/Collation.php | 3 +-- lib/public/IDBConnection.php | 2 +- tests/lib/DB/MigrationsTest.php | 10 ++++------ tests/lib/DB/MigratorTest.php | 12 ++++------- tests/lib/DB/OCPostgreSqlPlatformTest.php | 4 ++-- tests/lib/Files/Cache/CacheTest.php | 4 ++-- tests/lib/Repair/RepairCollationTest.php | 3 +-- 18 files changed, 55 insertions(+), 69 deletions(-) diff --git a/apps/dav/lib/Migration/CalDAVRemoveEmptyValue.php b/apps/dav/lib/Migration/CalDAVRemoveEmptyValue.php index cbce847a298c2..c7f57dcb11718 100644 --- a/apps/dav/lib/Migration/CalDAVRemoveEmptyValue.php +++ b/apps/dav/lib/Migration/CalDAVRemoveEmptyValue.php @@ -5,7 +5,6 @@ */ namespace OCA\DAV\Migration; -use Doctrine\DBAL\Platforms\OraclePlatform; use OCA\DAV\CalDAV\CalDavBackend; use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\IDBConnection; @@ -75,7 +74,7 @@ public function run(IOutput $output) { } protected function getInvalidObjects($pattern) { - if ($this->db->getDatabasePlatform() instanceof OraclePlatform) { + if ($this->db->getDatabaseProvider() === IDBConnection::PLATFORM_ORACLE) { $rows = []; $chunkSize = 500; $query = $this->db->getQueryBuilder(); diff --git a/apps/settings/tests/SetupChecks/SupportedDatabaseTest.php b/apps/settings/tests/SetupChecks/SupportedDatabaseTest.php index 123bb9b1e881a..0ba1621c5febb 100644 --- a/apps/settings/tests/SetupChecks/SupportedDatabaseTest.php +++ b/apps/settings/tests/SetupChecks/SupportedDatabaseTest.php @@ -8,7 +8,6 @@ */ namespace OCA\Settings\Tests; -use Doctrine\DBAL\Platforms\SQLitePlatform; use OCA\Settings\SetupChecks\SupportedDatabase; use OCP\IDBConnection; use OCP\IL10N; @@ -41,8 +40,7 @@ protected function setUp(): void { } public function testPass(): void { - $platform = $this->connection->getDatabasePlatform(); - if ($platform instanceof SQLitePlatform) { + if ($this->connection->getDatabaseProvider() === IDBConnection::PLATFORM_SQLITE) { /** SQlite always gets a warning */ $this->assertEquals(SetupResult::WARNING, $this->check->run()->getSeverity()); } else { diff --git a/apps/user_ldap/lib/Mapping/AbstractMapping.php b/apps/user_ldap/lib/Mapping/AbstractMapping.php index 7dced1ca7c39c..9b92960d3546c 100644 --- a/apps/user_ldap/lib/Mapping/AbstractMapping.php +++ b/apps/user_ldap/lib/Mapping/AbstractMapping.php @@ -8,9 +8,9 @@ namespace OCA\User_LDAP\Mapping; use Doctrine\DBAL\Exception; -use Doctrine\DBAL\Platforms\SQLitePlatform; use OCP\DB\IPreparedStatement; use OCP\DB\QueryBuilder\IQueryBuilder; +use OCP\IDBConnection; use Psr\Log\LoggerInterface; /** @@ -216,7 +216,7 @@ protected function collectResultsFromListOfIdsQuery(IQueryBuilder $qb, array &$r public function getListOfIdsByDn(array $fdns): array { $totalDBParamLimit = 65000; $sliceSize = 1000; - $maxSlices = $this->dbc->getDatabasePlatform() instanceof SQLitePlatform ? 9 : $totalDBParamLimit / $sliceSize; + $maxSlices = $this->dbc->getDatabaseProvider() === IDBConnection::PLATFORM_SQLITE ? 9 : $totalDBParamLimit / $sliceSize; $results = []; $slice = 1; diff --git a/core/Command/Db/ConvertFilecacheBigInt.php b/core/Command/Db/ConvertFilecacheBigInt.php index 7e47e3fa753be..0375823c36c16 100644 --- a/core/Command/Db/ConvertFilecacheBigInt.php +++ b/core/Command/Db/ConvertFilecacheBigInt.php @@ -5,11 +5,11 @@ */ namespace OC\Core\Command\Db; -use Doctrine\DBAL\Platforms\SQLitePlatform; use Doctrine\DBAL\Types\Type; use OC\DB\Connection; use OC\DB\SchemaWrapper; use OCP\DB\Types; +use OCP\IDBConnection; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; @@ -53,7 +53,7 @@ public static function getColumnsByTable(): array { protected function execute(InputInterface $input, OutputInterface $output): int { $schema = new SchemaWrapper($this->connection); - $isSqlite = $this->connection->getDatabasePlatform() instanceof SQLitePlatform; + $isSqlite = $this->connection->getDatabaseProvider() === IDBConnection::PLATFORM_SQLITE; $updates = []; $tables = static::getColumnsByTable(); diff --git a/core/Command/Db/ConvertMysqlToMB4.php b/core/Command/Db/ConvertMysqlToMB4.php index 679cdd5f61611..618e15de3f45a 100644 --- a/core/Command/Db/ConvertMysqlToMB4.php +++ b/core/Command/Db/ConvertMysqlToMB4.php @@ -5,7 +5,6 @@ */ namespace OC\Core\Command\Db; -use Doctrine\DBAL\Platforms\MySQLPlatform; use OC\DB\MySqlTools; use OC\Migration\ConsoleOutput; use OC\Repair\Collation; @@ -34,7 +33,7 @@ protected function configure() { } protected function execute(InputInterface $input, OutputInterface $output): int { - if (!$this->connection->getDatabasePlatform() instanceof MySQLPlatform) { + if ($this->connection->getDatabaseProvider() !== IDBConnection::PLATFORM_MYSQL) { $output->writeln("This command is only valid for MySQL/MariaDB databases."); return 1; } diff --git a/lib/private/AllConfig.php b/lib/private/AllConfig.php index d05fe440202dd..58eee772fbf91 100644 --- a/lib/private/AllConfig.php +++ b/lib/private/AllConfig.php @@ -6,7 +6,6 @@ */ namespace OC; -use Doctrine\DBAL\Platforms\OraclePlatform; use OCP\Cache\CappedMemoryCache; use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\IConfig; @@ -470,7 +469,7 @@ public function getUsersForUserValue($appName, $key, $value) { $this->fixDIInit(); $qb = $this->connection->getQueryBuilder(); - $configValueColumn = ($this->connection->getDatabasePlatform() instanceof OraclePlatform) + $configValueColumn = ($this->connection->getDatabaseProvider() === IDBConnection::PLATFORM_ORACLE) ? $qb->expr()->castColumn('configvalue', IQueryBuilder::PARAM_STR) : 'configvalue'; $result = $qb->select('userid') @@ -509,7 +508,7 @@ public function getUsersForUserValueCaseInsensitive($appName, $key, $value) { } $qb = $this->connection->getQueryBuilder(); - $configValueColumn = ($this->connection->getDatabasePlatform() instanceof OraclePlatform) + $configValueColumn = ($this->connection->getDatabaseProvider() === IDBConnection::PLATFORM_ORACLE) ? $qb->expr()->castColumn('configvalue', IQueryBuilder::PARAM_STR) : 'configvalue'; diff --git a/lib/private/BackgroundJob/JobList.php b/lib/private/BackgroundJob/JobList.php index 61c48b0eab218..cec487fbe7bae 100644 --- a/lib/private/BackgroundJob/JobList.php +++ b/lib/private/BackgroundJob/JobList.php @@ -7,7 +7,6 @@ */ namespace OC\BackgroundJob; -use Doctrine\DBAL\Platforms\MySQLPlatform; use OCP\AppFramework\QueryException; use OCP\AppFramework\Utility\ITimeFactory; use OCP\AutoloadNotAllowedException; @@ -98,7 +97,7 @@ public function remove($job, $argument = null): void { // Add galera safe delete chunking if using mysql // Stops us hitting wsrep_max_ws_rows when large row counts are deleted - if ($this->connection->getDatabasePlatform() instanceof MySQLPlatform) { + if ($this->connection->getDatabaseProvider() === IDBConnection::PLATFORM_MYSQL) { // Then use chunked delete $max = IQueryBuilder::MAX_ROW_DELETION; diff --git a/lib/private/DB/Connection.php b/lib/private/DB/Connection.php index 6b2618271250a..425d034fd07c6 100644 --- a/lib/private/DB/Connection.php +++ b/lib/private/DB/Connection.php @@ -18,6 +18,7 @@ use Doctrine\DBAL\ParameterType; use Doctrine\DBAL\Platforms\MySQLPlatform; use Doctrine\DBAL\Platforms\OraclePlatform; +use Doctrine\DBAL\Platforms\PostgreSQLPlatform; use Doctrine\DBAL\Platforms\SQLitePlatform; use Doctrine\DBAL\Result; use Doctrine\DBAL\Schema\Schema; @@ -27,6 +28,7 @@ use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\Diagnostics\IEventLogger; use OCP\Diagnostics\IQueryLogger; +use OCP\IDBConnection; use OCP\IRequestId; use OCP\PreConditionNotMetException; use OCP\Profiler\IProfiler; @@ -715,4 +717,19 @@ private function reconnectIfNeeded(): void { private function getConnectionName(): string { return $this->isConnectedToPrimary() ? 'primary' : 'replica'; } + + public function getDatabaseProvider(): string { + $platform = $this->getDatabasePlatform(); + if ($platform instanceof MySQLPlatform) { + return IDBConnection::PLATFORM_MYSQL; + } elseif ($platform instanceof OraclePlatform) { + return IDBConnection::PLATFORM_ORACLE; + } elseif ($platform instanceof PostgreSQLPlatform) { + return IDBConnection::PLATFORM_POSTGRES; + } elseif ($platform instanceof SQLitePlatform) { + return IDBConnection::PLATFORM_SQLITE; + } else { + throw new \Exception('Database ' . $platform::class . ' not supported'); + } + } } diff --git a/lib/private/DB/ConnectionAdapter.php b/lib/private/DB/ConnectionAdapter.php index 61bb37766d256..52e2ab3261e19 100644 --- a/lib/private/DB/ConnectionAdapter.php +++ b/lib/private/DB/ConnectionAdapter.php @@ -238,18 +238,10 @@ public function getInner(): Connection { return $this->inner; } + /** + * @return self::PLATFORM_MYSQL|self::PLATFORM_ORACLE|self::PLATFORM_POSTGRES|self::PLATFORM_SQLITE + */ public function getDatabaseProvider(): string { - $platform = $this->inner->getDatabasePlatform(); - if ($platform instanceof MySQLPlatform) { - return IDBConnection::PLATFORM_MYSQL; - } elseif ($platform instanceof OraclePlatform) { - return IDBConnection::PLATFORM_ORACLE; - } elseif ($platform instanceof PostgreSQLPlatform) { - return IDBConnection::PLATFORM_POSTGRES; - } elseif ($platform instanceof SQLitePlatform) { - return IDBConnection::PLATFORM_SQLITE; - } else { - throw new \Exception('Database ' . $platform::class . ' not supported'); - } + return $this->inner->getDatabaseProvider(); } } diff --git a/lib/private/DB/MigrationService.php b/lib/private/DB/MigrationService.php index feee88fa81d84..acf060f348d43 100644 --- a/lib/private/DB/MigrationService.php +++ b/lib/private/DB/MigrationService.php @@ -6,8 +6,6 @@ */ namespace OC\DB; -use Doctrine\DBAL\Platforms\OraclePlatform; -use Doctrine\DBAL\Platforms\PostgreSQLPlatform; use Doctrine\DBAL\Schema\Index; use Doctrine\DBAL\Schema\Schema; use Doctrine\DBAL\Schema\SchemaException; @@ -20,6 +18,7 @@ use OCP\AppFramework\QueryException; use OCP\DB\ISchemaWrapper; use OCP\DB\Types; +use OCP\IDBConnection; use OCP\Migration\IMigrationStep; use OCP\Migration\IOutput; use OCP\Server; @@ -601,7 +600,7 @@ public function ensureOracleConstraints(Schema $sourceSchema, Schema $targetSche $indexName = strtolower($primaryKey->getName()); $isUsingDefaultName = $indexName === 'primary'; - if ($this->connection->getDatabasePlatform() instanceof PostgreSQLPlatform) { + if ($this->connection->getDatabaseProvider() === IDBConnection::PLATFORM_POSTGRES) { $defaultName = $table->getName() . '_pkey'; $isUsingDefaultName = strtolower($defaultName) === $indexName; @@ -611,7 +610,7 @@ public function ensureOracleConstraints(Schema $sourceSchema, Schema $targetSche return $sequence->getName() !== $sequenceName; }); } - } elseif ($this->connection->getDatabasePlatform() instanceof OraclePlatform) { + } elseif ($this->connection->getDatabaseProvider() === IDBConnection::PLATFORM_ORACLE) { $defaultName = $table->getName() . '_seq'; $isUsingDefaultName = strtolower($defaultName) === $indexName; } diff --git a/lib/private/DB/QueryBuilder/QueryBuilder.php b/lib/private/DB/QueryBuilder/QueryBuilder.php index d071c942bc85d..93a2120ec9a49 100644 --- a/lib/private/DB/QueryBuilder/QueryBuilder.php +++ b/lib/private/DB/QueryBuilder/QueryBuilder.php @@ -109,20 +109,12 @@ public function automaticTablePrefix($enabled) { * @return \OCP\DB\QueryBuilder\IExpressionBuilder */ public function expr() { - if ($this->connection->getDatabaseProvider() === IDBConnection::PLATFORM_ORACLE) { - return new OCIExpressionBuilder($this->connection, $this); - } - if ($this->connection->getDatabaseProvider() === IDBConnection::PLATFORM_POSTGRES) { - return new PgSqlExpressionBuilder($this->connection, $this); - } - if ($this->connection->getDatabaseProvider() === IDBConnection::PLATFORM_MYSQL) { - return new MySqlExpressionBuilder($this->connection, $this); - } - if ($this->connection->getDatabaseProvider() === IDBConnection::PLATFORM_SQLITE) { - return new SqliteExpressionBuilder($this->connection, $this); - } - - return new ExpressionBuilder($this->connection, $this); + return match($this->connection->getDatabaseProvider()) { + IDBConnection::PLATFORM_ORACLE => new OCIExpressionBuilder($this->connection, $this), + IDBConnection::PLATFORM_POSTGRES => new PgSqlExpressionBuilder($this->connection, $this), + IDBConnection::PLATFORM_MYSQL => new MySqlExpressionBuilder($this->connection, $this), + IDBConnection::PLATFORM_SQLITE => new SqliteExpressionBuilder($this->connection, $this), + }; } /** diff --git a/lib/private/Repair/Collation.php b/lib/private/Repair/Collation.php index 0affb3b1ca980..a01a684151b08 100644 --- a/lib/private/Repair/Collation.php +++ b/lib/private/Repair/Collation.php @@ -8,7 +8,6 @@ namespace OC\Repair; use Doctrine\DBAL\Exception\DriverException; -use Doctrine\DBAL\Platforms\MySQLPlatform; use OCP\IConfig; use OCP\IDBConnection; use OCP\Migration\IOutput; @@ -50,7 +49,7 @@ public function getName() { * Fix mime types */ public function run(IOutput $output) { - if (!$this->connection->getDatabasePlatform() instanceof MySQLPlatform) { + if ($this->connection->getDatabaseProvider() !== IDBConnection::PLATFORM_MYSQL) { $output->info('Not a mysql database -> nothing to do'); return; } diff --git a/lib/public/IDBConnection.php b/lib/public/IDBConnection.php index 75440b7428a28..25823bc33e7a9 100644 --- a/lib/public/IDBConnection.php +++ b/lib/public/IDBConnection.php @@ -343,7 +343,7 @@ public function migrateToSchema(Schema $toSchema): void; * Returns the database provider name * @link https://github.com/nextcloud/server/issues/30877 * @since 28.0.0 - * @return IDBConnection::PLATFORM_* + * @return self::PLATFORM_MYSQL|self::PLATFORM_ORACLE|self::PLATFORM_POSTGRES|self::PLATFORM_SQLITE */ public function getDatabaseProvider(): string; } diff --git a/tests/lib/DB/MigrationsTest.php b/tests/lib/DB/MigrationsTest.php index 05dbc13e372c8..e1432e59964d5 100644 --- a/tests/lib/DB/MigrationsTest.php +++ b/tests/lib/DB/MigrationsTest.php @@ -8,8 +8,6 @@ namespace Test\DB; -use Doctrine\DBAL\Platforms\OraclePlatform; -use Doctrine\DBAL\Platforms\PostgreSqlPlatform; use Doctrine\DBAL\Schema\Column; use Doctrine\DBAL\Schema\Exception\TableDoesNotExist; use Doctrine\DBAL\Schema\ForeignKeyConstraint; @@ -326,9 +324,9 @@ public function testEnsureOracleConstraintsValidWithPrimaryKey() { public function testEnsureOracleConstraintsValidWithPrimaryKeyDefault() { $defaultName = 'PRIMARY'; - if ($this->db->getDatabasePlatform() instanceof PostgreSqlPlatform) { + if ($this->db->getDatabaseProvider() === IDBConnection::PLATFORM_POSTGRES) { $defaultName = \str_repeat('a', 26) . '_' . \str_repeat('b', 30) . '_seq'; - } elseif ($this->db->getDatabasePlatform() instanceof OraclePlatform) { + } elseif ($this->db->getDatabaseProvider() === IDBConnection::PLATFORM_ORACLE) { $defaultName = \str_repeat('a', 26) . '_seq'; } @@ -407,9 +405,9 @@ public function testEnsureOracleConstraintsTooLongPrimaryWithDefault() { $this->expectException(\InvalidArgumentException::class); $defaultName = 'PRIMARY'; - if ($this->db->getDatabasePlatform() instanceof PostgreSqlPlatform) { + if ($this->db->getDatabaseProvider() === IDBConnection::PLATFORM_POSTGRES) { $defaultName = \str_repeat('a', 27) . '_' . \str_repeat('b', 30) . '_seq'; - } elseif ($this->db->getDatabasePlatform() instanceof OraclePlatform) { + } elseif ($this->db->getDatabaseProvider() === IDBConnection::PLATFORM_ORACLE) { $defaultName = \str_repeat('a', 27) . '_seq'; } diff --git a/tests/lib/DB/MigratorTest.php b/tests/lib/DB/MigratorTest.php index a486a39ae3d2e..36343e3fc4767 100644 --- a/tests/lib/DB/MigratorTest.php +++ b/tests/lib/DB/MigratorTest.php @@ -10,8 +10,6 @@ use Doctrine\DBAL\Exception; use Doctrine\DBAL\ParameterType; -use Doctrine\DBAL\Platforms\OraclePlatform; -use Doctrine\DBAL\Platforms\SQLitePlatform; use Doctrine\DBAL\Schema\Schema; use Doctrine\DBAL\Schema\SchemaConfig; use OC\DB\Migrator; @@ -21,7 +19,7 @@ use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\DB\Types; use OCP\IConfig; -use OCP\Security\ISecureRandom; +use OCP\IDBConnection; /** * Class MigratorTest @@ -60,12 +58,10 @@ protected function setUp(): void { } private function getMigrator(): Migrator { - $platform = $this->connection->getDatabasePlatform(); - $random = \OC::$server->get(ISecureRandom::class); $dispatcher = \OC::$server->get(\OCP\EventDispatcher\IEventDispatcher::class); - if ($platform instanceof SQLitePlatform) { + if ($this->connection->getDatabaseProvider() === IDBConnection::PLATFORM_SQLITE) { return new SQLiteMigrator($this->connection, $this->config, $dispatcher); - } elseif ($platform instanceof OraclePlatform) { + } elseif ($this->connection->getDatabaseProvider() === IDBConnection::PLATFORM_ORACLE) { return new OracleMigrator($this->connection, $this->config, $dispatcher); } return new Migrator($this->connection, $this->config, $dispatcher); @@ -304,7 +300,7 @@ public function testNotNullEmptyValuesFailOracle(int|string $parameterType, bool $migrator = $this->getMigrator(); $migrator->migrate($startSchema); - if ($oracleThrows && $this->connection->getDatabasePlatform() instanceof OraclePlatform) { + if ($oracleThrows && $this->connection->getDatabaseProvider() === IDBConnection::PLATFORM_ORACLE) { // Oracle can not store false|empty string in notnull columns $this->expectException(\Doctrine\DBAL\Exception\NotNullConstraintViolationException::class); } diff --git a/tests/lib/DB/OCPostgreSqlPlatformTest.php b/tests/lib/DB/OCPostgreSqlPlatformTest.php index 4f83e866a7c51..3ed420df501db 100644 --- a/tests/lib/DB/OCPostgreSqlPlatformTest.php +++ b/tests/lib/DB/OCPostgreSqlPlatformTest.php @@ -7,7 +7,7 @@ namespace Test\DB; -use Doctrine\DBAL\Platforms\PostgreSQL100Platform; +use Doctrine\DBAL\Platforms\PostgreSQLPlatform; use Doctrine\DBAL\Schema\Comparator; use Doctrine\DBAL\Schema\Schema; use Doctrine\DBAL\Types\Types; @@ -24,7 +24,7 @@ */ class OCPostgreSqlPlatformTest extends \Test\TestCase { public function testAlterBigint() { - $platform = new PostgreSQL100Platform(); + $platform = new PostgreSQLPlatform(); $sourceSchema = new Schema(); $targetSchema = new Schema(); diff --git a/tests/lib/Files/Cache/CacheTest.php b/tests/lib/Files/Cache/CacheTest.php index faecbf54491c0..a36607eb9659e 100644 --- a/tests/lib/Files/Cache/CacheTest.php +++ b/tests/lib/Files/Cache/CacheTest.php @@ -7,12 +7,12 @@ namespace Test\Files\Cache; -use Doctrine\DBAL\Platforms\MySqlPlatform; use OC\Files\Cache\Cache; use OC\Files\Search\SearchComparison; use OC\Files\Search\SearchQuery; use OCP\EventDispatcher\IEventDispatcher; use OCP\Files\Search\ISearchComparison; +use OCP\IDBConnection; use OCP\IUser; class LongId extends \OC\Files\Storage\Temporary { @@ -142,7 +142,7 @@ public function testFolder($folder) { if (strpos($folder, 'F09F9890')) { // 4 byte UTF doesn't work on mysql $params = \OC::$server->get(\OC\DB\Connection::class)->getParams(); - if (\OC::$server->getDatabaseConnection()->getDatabasePlatform() instanceof MySqlPlatform && $params['charset'] !== 'utf8mb4') { + if (\OC::$server->getDatabaseConnection()->getDatabaseProvider() === IDBConnection::PLATFORM_MYSQL && $params['charset'] !== 'utf8mb4') { $this->markTestSkipped('MySQL doesn\'t support 4 byte UTF-8'); } } diff --git a/tests/lib/Repair/RepairCollationTest.php b/tests/lib/Repair/RepairCollationTest.php index 5f5e0737f77bd..6d3946b2a85b8 100644 --- a/tests/lib/Repair/RepairCollationTest.php +++ b/tests/lib/Repair/RepairCollationTest.php @@ -7,7 +7,6 @@ namespace Test\Repair; -use Doctrine\DBAL\Platforms\MySqlPlatform; use OC\Repair\Collation; use OCP\IDBConnection; use OCP\Migration\IOutput; @@ -61,7 +60,7 @@ protected function setUp(): void { $this->connection = \OC::$server->get(IDBConnection::class); $this->logger = $this->createMock(LoggerInterface::class); $this->config = \OC::$server->getConfig(); - if (!$this->connection->getDatabasePlatform() instanceof MySqlPlatform) { + if ($this->connection->getDatabaseProvider() !== IDBConnection::PLATFORM_MYSQL) { $this->markTestSkipped("Test only relevant on MySql"); } From b2cf45cdaab0411c2a30fe096ce4b0620f1c9a80 Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Thu, 4 Jul 2024 10:24:47 +0200 Subject: [PATCH 48/51] fix(tests): Add length to test columns that bypass the migration service Signed-off-by: Joas Schilling --- tests/lib/DB/MigratorTest.php | 73 +++++++++++++++++++---------------- 1 file changed, 39 insertions(+), 34 deletions(-) diff --git a/tests/lib/DB/MigratorTest.php b/tests/lib/DB/MigratorTest.php index 36343e3fc4767..2174ec543b8d1 100644 --- a/tests/lib/DB/MigratorTest.php +++ b/tests/lib/DB/MigratorTest.php @@ -1,5 +1,6 @@ connection, $this->config, $dispatcher); } - private function getUniqueTableName() { + private function getUniqueTableName(): string { return strtolower($this->getUniqueID($this->config->getSystemValueString('dbtableprefix', 'oc_') . 'test_')); } @@ -88,17 +89,17 @@ protected function tearDown(): void { /** * @return \Doctrine\DBAL\Schema\Schema[] */ - private function getDuplicateKeySchemas() { + private function getDuplicateKeySchemas(): array { $startSchema = new Schema([], [], $this->getSchemaConfig()); $table = $startSchema->createTable($this->tableName); - $table->addColumn('id', 'integer'); - $table->addColumn('name', 'string'); + $table->addColumn('id', Types::INTEGER); + $table->addColumn('name', Types::STRING, ['length' => 255]); $table->addIndex(['id'], $this->tableName . '_id'); $endSchema = new Schema([], [], $this->getSchemaConfig()); $table = $endSchema->createTable($this->tableName); - $table->addColumn('id', 'integer'); - $table->addColumn('name', 'string'); + $table->addColumn('id', Types::INTEGER); + $table->addColumn('name', Types::STRING, ['length' => 255]); $table->addUniqueIndex(['id'], $this->tableName . '_id'); return [$startSchema, $endSchema]; @@ -107,30 +108,30 @@ private function getDuplicateKeySchemas() { /** * @return \Doctrine\DBAL\Schema\Schema[] */ - private function getChangedTypeSchema($from, $to) { + private function getChangedTypeSchema(string $from, string $to): array { $startSchema = new Schema([], [], $this->getSchemaConfig()); $table = $startSchema->createTable($this->tableName); $table->addColumn('id', $from); - $table->addColumn('name', 'string'); + $table->addColumn('name', Types::STRING, ['length' => 255]); $table->addIndex(['id'], $this->tableName . '_id'); $endSchema = new Schema([], [], $this->getSchemaConfig()); $table = $endSchema->createTable($this->tableName); $table->addColumn('id', $to); - $table->addColumn('name', 'string'); + $table->addColumn('name', Types::STRING, ['length' => 255]); $table->addIndex(['id'], $this->tableName . '_id'); return [$startSchema, $endSchema]; } - private function getSchemaConfig() { + private function getSchemaConfig(): SchemaConfig { $config = new SchemaConfig(); $config->setName($this->connection->getDatabase()); return $config; } - public function testUpgrade() { + public function testUpgrade(): void { [$startSchema, $endSchema] = $this->getDuplicateKeySchemas(); $migrator = $this->getMigrator(); $migrator->migrate($startSchema); @@ -143,7 +144,7 @@ public function testUpgrade() { $this->addToAssertionCount(1); } - public function testUpgradeDifferentPrefix() { + public function testUpgradeDifferentPrefix(): void { $oldTablePrefix = $this->config->getSystemValueString('dbtableprefix', 'oc_'); $this->config->setSystemValue('dbtableprefix', 'ownc_'); @@ -163,7 +164,7 @@ public function testUpgradeDifferentPrefix() { $this->config->setSystemValue('dbtableprefix', $oldTablePrefix); } - public function testInsertAfterUpgrade() { + public function testInsertAfterUpgrade(): void { [$startSchema, $endSchema] = $this->getDuplicateKeySchemas(); $migrator = $this->getMigrator(); $migrator->migrate($startSchema); @@ -180,16 +181,16 @@ public function testInsertAfterUpgrade() { } } - public function testAddingPrimaryKeyWithAutoIncrement() { + public function testAddingPrimaryKeyWithAutoIncrement(): void { $startSchema = new Schema([], [], $this->getSchemaConfig()); $table = $startSchema->createTable($this->tableName); - $table->addColumn('id', 'integer'); - $table->addColumn('name', 'string'); + $table->addColumn('id', Types::INTEGER); + $table->addColumn('name', Types::STRING, ['length' => 255]); $endSchema = new Schema([], [], $this->getSchemaConfig()); $table = $endSchema->createTable($this->tableName); - $table->addColumn('id', 'integer', ['autoincrement' => true]); - $table->addColumn('name', 'string'); + $table->addColumn('id', Types::INTEGER, ['autoincrement' => true]); + $table->addColumn('name', Types::STRING, ['length' => 255]); $table->setPrimaryKey(['id']); $migrator = $this->getMigrator(); @@ -200,17 +201,17 @@ public function testAddingPrimaryKeyWithAutoIncrement() { $this->addToAssertionCount(1); } - public function testReservedKeywords() { + public function testReservedKeywords(): void { $startSchema = new Schema([], [], $this->getSchemaConfig()); $table = $startSchema->createTable($this->tableName); - $table->addColumn('id', 'integer', ['autoincrement' => true]); - $table->addColumn('user', 'string', ['length' => 255]); + $table->addColumn('id', Types::INTEGER, ['autoincrement' => true]); + $table->addColumn('user', Types::STRING, ['length' => 255]); $table->setPrimaryKey(['id']); $endSchema = new Schema([], [], $this->getSchemaConfig()); $table = $endSchema->createTable($this->tableName); - $table->addColumn('id', 'integer', ['autoincrement' => true]); - $table->addColumn('user', 'string', ['length' => 64]); + $table->addColumn('id', Types::INTEGER, ['autoincrement' => true]); + $table->addColumn('user', Types::STRING, ['length' => 64]); $table->setPrimaryKey(['id']); $migrator = $this->getMigrator(); @@ -224,17 +225,17 @@ public function testReservedKeywords() { /** * Test for nextcloud/server#36803 */ - public function testColumnCommentsInUpdate() { + public function testColumnCommentsInUpdate(): void { $startSchema = new Schema([], [], $this->getSchemaConfig()); $table = $startSchema->createTable($this->tableName); - $table->addColumn('id', 'integer', ['autoincrement' => true, 'comment' => 'foo']); + $table->addColumn('id', Types::INTEGER, ['autoincrement' => true, 'comment' => 'foo']); $table->setPrimaryKey(['id']); $endSchema = new Schema([], [], $this->getSchemaConfig()); $table = $endSchema->createTable($this->tableName); - $table->addColumn('id', 'integer', ['autoincrement' => true, 'comment' => 'foo']); + $table->addColumn('id', Types::INTEGER, ['autoincrement' => true, 'comment' => 'foo']); // Assert adding comments on existing tables work (or at least does not throw) - $table->addColumn('time', 'integer', ['comment' => 'unix-timestamp', 'notnull' => false]); + $table->addColumn('time', Types::INTEGER, ['comment' => 'unix-timestamp', 'notnull' => false]); $table->setPrimaryKey(['id']); $migrator = $this->getMigrator(); @@ -245,17 +246,17 @@ public function testColumnCommentsInUpdate() { $this->addToAssertionCount(1); } - public function testAddingForeignKey() { + public function testAddingForeignKey(): void { $startSchema = new Schema([], [], $this->getSchemaConfig()); $table = $startSchema->createTable($this->tableName); - $table->addColumn('id', 'integer', ['autoincrement' => true]); - $table->addColumn('name', 'string'); + $table->addColumn('id', Types::INTEGER, ['autoincrement' => true]); + $table->addColumn('name', Types::STRING, ['length' => 255]); $table->setPrimaryKey(['id']); $fkName = "fkc"; $tableFk = $startSchema->createTable($this->tableNameTmp); - $tableFk->addColumn('fk_id', 'integer'); - $tableFk->addColumn('name', 'string'); + $tableFk->addColumn('fk_id', Types::INTEGER); + $tableFk->addColumn('name', Types::STRING, ['length' => 255]); $tableFk->addForeignKeyConstraint($this->tableName, ['fk_id'], ['id'], [], $fkName); $migrator = $this->getMigrator(); @@ -292,9 +293,13 @@ public function testNotNullEmptyValuesFailOracle(int|string $parameterType, bool $startSchema = new Schema([], [], $this->getSchemaConfig()); $table = $startSchema->createTable($this->tableName); $table->addColumn('id', Types::BIGINT); - $table->addColumn('will_it_blend', $columnType, [ + $options = $columnType === Types::STRING ? [ + 'length' => 255, 'notnull' => true, - ]); + ] : [ + 'notnull' => true, + ]; + $table->addColumn('will_it_blend', $columnType, $options); $table->addIndex(['id'], $this->tableName . '_id'); $migrator = $this->getMigrator(); From 72c8c3af3a6c4db45a1fec1d76ad40de7ca2948f Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Thu, 4 Jul 2024 10:26:11 +0200 Subject: [PATCH 49/51] fix(tests): Adjust postgres schema diffing Signed-off-by: Joas Schilling --- tests/lib/DB/OCPostgreSqlPlatformTest.php | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/tests/lib/DB/OCPostgreSqlPlatformTest.php b/tests/lib/DB/OCPostgreSqlPlatformTest.php index 3ed420df501db..73e7cb4ace038 100644 --- a/tests/lib/DB/OCPostgreSqlPlatformTest.php +++ b/tests/lib/DB/OCPostgreSqlPlatformTest.php @@ -1,4 +1,6 @@ createTableAndColumn($sourceSchema, Types::INTEGER); $this->createTableAndColumn($targetSchema, Types::BIGINT); - $comparator = new Comparator(); - $diff = $comparator->compare($sourceSchema, $targetSchema); - $sqlStatements = $diff->toSql($platform); + $comparator = new Comparator($platform); + $diff = $comparator->compareSchemas($sourceSchema, $targetSchema); + $sqlStatements = $platform->getAlterSchemaSQL($diff); $this->assertContains( 'ALTER TABLE poor_yorick ALTER id TYPE BIGINT', - $sqlStatements, - true + $sqlStatements ); $this->assertNotContains( 'ALTER TABLE poor_yorick ALTER id DROP DEFAULT', - $sqlStatements, - true + $sqlStatements ); } - protected function createTableAndColumn($schema, $type) { + protected function createTableAndColumn(Schema $schema, string $type): void { $table = $schema->createTable("poor_yorick"); $table->addColumn('id', $type, [ 'autoincrement' => true, From 8b7f8e14d6e7806ac3086646036e4cae5057ec83 Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Thu, 4 Jul 2024 10:50:37 +0200 Subject: [PATCH 50/51] fix(tests): Also handle the case where the schema is cached and the DELETE fails Signed-off-by: Joas Schilling --- tests/lib/DB/QueryBuilder/ExpressionBuilderDBTest.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tests/lib/DB/QueryBuilder/ExpressionBuilderDBTest.php b/tests/lib/DB/QueryBuilder/ExpressionBuilderDBTest.php index 671bcfc209ee2..efc9be6f80044 100644 --- a/tests/lib/DB/QueryBuilder/ExpressionBuilderDBTest.php +++ b/tests/lib/DB/QueryBuilder/ExpressionBuilderDBTest.php @@ -9,6 +9,7 @@ use Doctrine\DBAL\Schema\SchemaException; use Doctrine\DBAL\Types\Types; use OC\DB\QueryBuilder\Literal; +use OCP\DB\Exception; use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\IConfig; use OCP\Server; @@ -215,7 +216,11 @@ protected function prepareTestingTable(): void { try { $schema->getTable($prefix . 'testing'); $this->connection->getQueryBuilder()->delete('testing')->executeStatement(); - } catch (SchemaException $e) { + } catch (SchemaException|Exception $e) { + if ($e instanceof Exception && $e->getReason() !== Exception::REASON_DATABASE_OBJECT_NOT_FOUND) { + throw $e; + } + $this->schemaSetup = true; $table = $schema->createTable($prefix . 'testing'); $table->addColumn('id', Types::BIGINT, [ From 5fdc5d50114e130d157a142bd72bce3f8d0ac373 Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Thu, 4 Jul 2024 11:26:17 +0200 Subject: [PATCH 51/51] fix(db)!: Too few arguments to function `Doctrine\DBAL\Query\Expression\ExpressionBuilder::or()`, 0 passed and atleast 1 expected Signed-off-by: Joas Schilling --- .../lib/Db/CardSearchDao.php | 20 +++--- .../lib/Db/RecentContactMapper.php | 14 ++-- apps/dav/lib/CalDAV/CalDavBackend.php | 64 ++++++++++--------- apps/dav/lib/CardDAV/CardDavBackend.php | 12 ++-- lib/private/BackgroundJob/JobList.php | 8 +-- lib/private/DB/Connection.php | 12 ++-- lib/private/DB/QueryBuilder/QueryBuilder.php | 7 +- lib/private/TaskProcessing/Db/TaskMapper.php | 10 +-- lib/public/DB/QueryBuilder/IQueryBuilder.php | 7 +- 9 files changed, 83 insertions(+), 71 deletions(-) diff --git a/apps/contactsinteraction/lib/Db/CardSearchDao.php b/apps/contactsinteraction/lib/Db/CardSearchDao.php index 962f9ff768bcf..0929cb7efa098 100644 --- a/apps/contactsinteraction/lib/Db/CardSearchDao.php +++ b/apps/contactsinteraction/lib/Db/CardSearchDao.php @@ -29,24 +29,24 @@ public function findExisting(IUser $user, $cardQuery = $this->db->getQueryBuilder(); $propQuery = $this->db->getQueryBuilder(); - $propOr = $propQuery->expr()->orX(); + $additionalWheres = []; if ($uid !== null) { - $propOr->add($propQuery->expr()->andX( + $additionalWheres[] = $propQuery->expr()->andX( $propQuery->expr()->eq('name', $cardQuery->createNamedParameter('UID')), $propQuery->expr()->eq('value', $cardQuery->createNamedParameter($uid)) - )); + ); } if ($email !== null) { - $propOr->add($propQuery->expr()->andX( + $additionalWheres[] = $propQuery->expr()->andX( $propQuery->expr()->eq('name', $cardQuery->createNamedParameter('EMAIL')), $propQuery->expr()->eq('value', $cardQuery->createNamedParameter($email)) - )); + ); } if ($cloudId !== null) { - $propOr->add($propQuery->expr()->andX( + $additionalWheres[] = $propQuery->expr()->andX( $propQuery->expr()->eq('name', $cardQuery->createNamedParameter('CLOUD')), $propQuery->expr()->eq('value', $cardQuery->createNamedParameter($cloudId)) - )); + ); } $addressbooksQuery->selectDistinct('id') ->from('addressbooks') @@ -54,8 +54,12 @@ public function findExisting(IUser $user, $propQuery->selectDistinct('cardid') ->from('cards_properties') ->where($propQuery->expr()->in('addressbookid', $propQuery->createFunction($addressbooksQuery->getSQL()), IQueryBuilder::PARAM_INT_ARRAY)) - ->andWhere($propOr) ->groupBy('cardid'); + + if (!empty($additionalWheres)) { + $propQuery->andWhere($propQuery->expr()->orX(...$additionalWheres)); + } + $cardQuery->select('carddata') ->from('cards') ->where($cardQuery->expr()->in('id', $cardQuery->createFunction($propQuery->getSQL()), IQueryBuilder::PARAM_INT_ARRAY)) diff --git a/apps/contactsinteraction/lib/Db/RecentContactMapper.php b/apps/contactsinteraction/lib/Db/RecentContactMapper.php index e0b2ac723fbea..c835b5287c87f 100644 --- a/apps/contactsinteraction/lib/Db/RecentContactMapper.php +++ b/apps/contactsinteraction/lib/Db/RecentContactMapper.php @@ -61,23 +61,25 @@ public function findMatch(IUser $user, ?string $cloudId): array { $qb = $this->db->getQueryBuilder(); - $or = $qb->expr()->orX(); + $additionalWheres = []; if ($uid !== null) { - $or->add($qb->expr()->eq('uid', $qb->createNamedParameter($uid))); + $additionalWheres[] = $qb->expr()->eq('uid', $qb->createNamedParameter($uid)); } if ($email !== null) { - $or->add($qb->expr()->eq('email', $qb->createNamedParameter($email))); + $additionalWheres[] = $qb->expr()->eq('email', $qb->createNamedParameter($email)); } if ($cloudId !== null) { - $or->add($qb->expr()->eq('federated_cloud_id', $qb->createNamedParameter($cloudId))); + $additionalWheres[] = $qb->expr()->eq('federated_cloud_id', $qb->createNamedParameter($cloudId)); } $select = $qb ->select('*') ->from($this->getTableName()) - ->where($or) - ->andWhere($qb->expr()->eq('actor_uid', $qb->createNamedParameter($user->getUID()))); + ->where($qb->expr()->eq('actor_uid', $qb->createNamedParameter($user->getUID()))); + if (!empty($additionalWheres)) { + $select->andWhere($select->expr()->orX(...$additionalWheres)); + } return $this->findEntities($select); } diff --git a/apps/dav/lib/CalDAV/CalDavBackend.php b/apps/dav/lib/CalDAV/CalDavBackend.php index cc6c4344c3c37..df4ed3c0405f5 100644 --- a/apps/dav/lib/CalDAV/CalDavBackend.php +++ b/apps/dav/lib/CalDAV/CalDavBackend.php @@ -1874,12 +1874,12 @@ public function search( } if (!empty($searchProperties)) { - $or = $innerQuery->expr()->orX(); + $or = []; foreach ($searchProperties as $searchProperty) { - $or->add($innerQuery->expr()->eq('op.name', - $outerQuery->createNamedParameter($searchProperty))); + $or[] = $innerQuery->expr()->eq('op.name', + $outerQuery->createNamedParameter($searchProperty)); } - $innerQuery->andWhere($or); + $innerQuery->andWhere($innerQuery->expr()->orX(...$or)); } if ($pattern !== '') { @@ -1923,12 +1923,12 @@ public function search( } if (!empty($options['types'])) { - $or = $outerQuery->expr()->orX(); + $or = []; foreach ($options['types'] as $type) { - $or->add($outerQuery->expr()->eq('componenttype', - $outerQuery->createNamedParameter($type))); + $or[] = $outerQuery->expr()->eq('componenttype', + $outerQuery->createNamedParameter($type)); } - $outerQuery->andWhere($or); + $outerQuery->andWhere($outerQuery->expr()->orX(...$or)); } $outerQuery->andWhere($outerQuery->expr()->in('c.id', $outerQuery->createFunction($innerQuery->getSQL()))); @@ -2149,16 +2149,17 @@ public function searchPrincipalUri(string $principalUri, $escapePattern = !\array_key_exists('escape_like_param', $options) || $options['escape_like_param'] !== false; $calendarObjectIdQuery = $this->db->getQueryBuilder(); - $calendarOr = $calendarObjectIdQuery->expr()->orX(); - $searchOr = $calendarObjectIdQuery->expr()->orX(); + $calendarOr = []; + $searchOr = []; // Fetch calendars and subscription $calendars = $this->getCalendarsForUser($principalUri); $subscriptions = $this->getSubscriptionsForUser($principalUri); foreach ($calendars as $calendar) { - $calendarAnd = $calendarObjectIdQuery->expr()->andX(); - $calendarAnd->add($calendarObjectIdQuery->expr()->eq('cob.calendarid', $calendarObjectIdQuery->createNamedParameter((int)$calendar['id']))); - $calendarAnd->add($calendarObjectIdQuery->expr()->eq('cob.calendartype', $calendarObjectIdQuery->createNamedParameter(self::CALENDAR_TYPE_CALENDAR))); + $calendarAnd = $calendarObjectIdQuery->expr()->andX( + $calendarObjectIdQuery->expr()->eq('cob.calendarid', $calendarObjectIdQuery->createNamedParameter((int)$calendar['id'])), + $calendarObjectIdQuery->expr()->eq('cob.calendartype', $calendarObjectIdQuery->createNamedParameter(self::CALENDAR_TYPE_CALENDAR)), + ); // If it's shared, limit search to public events if (isset($calendar['{http://owncloud.org/ns}owner-principal']) @@ -2166,12 +2167,13 @@ public function searchPrincipalUri(string $principalUri, $calendarAnd->add($calendarObjectIdQuery->expr()->eq('co.classification', $calendarObjectIdQuery->createNamedParameter(self::CLASSIFICATION_PUBLIC))); } - $calendarOr->add($calendarAnd); + $calendarOr[] = $calendarAnd; } foreach ($subscriptions as $subscription) { - $subscriptionAnd = $calendarObjectIdQuery->expr()->andX(); - $subscriptionAnd->add($calendarObjectIdQuery->expr()->eq('cob.calendarid', $calendarObjectIdQuery->createNamedParameter((int)$subscription['id']))); - $subscriptionAnd->add($calendarObjectIdQuery->expr()->eq('cob.calendartype', $calendarObjectIdQuery->createNamedParameter(self::CALENDAR_TYPE_SUBSCRIPTION))); + $subscriptionAnd = $calendarObjectIdQuery->expr()->andX( + $calendarObjectIdQuery->expr()->eq('cob.calendarid', $calendarObjectIdQuery->createNamedParameter((int)$subscription['id'])), + $calendarObjectIdQuery->expr()->eq('cob.calendartype', $calendarObjectIdQuery->createNamedParameter(self::CALENDAR_TYPE_SUBSCRIPTION)), + ); // If it's shared, limit search to public events if (isset($subscription['{http://owncloud.org/ns}owner-principal']) @@ -2179,28 +2181,30 @@ public function searchPrincipalUri(string $principalUri, $subscriptionAnd->add($calendarObjectIdQuery->expr()->eq('co.classification', $calendarObjectIdQuery->createNamedParameter(self::CLASSIFICATION_PUBLIC))); } - $calendarOr->add($subscriptionAnd); + $calendarOr[] = $subscriptionAnd; } foreach ($searchProperties as $property) { - $propertyAnd = $calendarObjectIdQuery->expr()->andX(); - $propertyAnd->add($calendarObjectIdQuery->expr()->eq('cob.name', $calendarObjectIdQuery->createNamedParameter($property, IQueryBuilder::PARAM_STR))); - $propertyAnd->add($calendarObjectIdQuery->expr()->isNull('cob.parameter')); + $propertyAnd = $calendarObjectIdQuery->expr()->andX( + $calendarObjectIdQuery->expr()->eq('cob.name', $calendarObjectIdQuery->createNamedParameter($property, IQueryBuilder::PARAM_STR)), + $calendarObjectIdQuery->expr()->isNull('cob.parameter'), + ); - $searchOr->add($propertyAnd); + $searchOr[] = $propertyAnd; } foreach ($searchParameters as $property => $parameter) { - $parameterAnd = $calendarObjectIdQuery->expr()->andX(); - $parameterAnd->add($calendarObjectIdQuery->expr()->eq('cob.name', $calendarObjectIdQuery->createNamedParameter($property, IQueryBuilder::PARAM_STR))); - $parameterAnd->add($calendarObjectIdQuery->expr()->eq('cob.parameter', $calendarObjectIdQuery->createNamedParameter($parameter, IQueryBuilder::PARAM_STR_ARRAY))); + $parameterAnd = $calendarObjectIdQuery->expr()->andX( + $calendarObjectIdQuery->expr()->eq('cob.name', $calendarObjectIdQuery->createNamedParameter($property, IQueryBuilder::PARAM_STR)), + $calendarObjectIdQuery->expr()->eq('cob.parameter', $calendarObjectIdQuery->createNamedParameter($parameter, IQueryBuilder::PARAM_STR_ARRAY)), + ); - $searchOr->add($parameterAnd); + $searchOr[] = $parameterAnd; } - if ($calendarOr->count() === 0) { + if (empty($calendarOr)) { return []; } - if ($searchOr->count() === 0) { + if (empty($searchOr)) { return []; } @@ -2208,8 +2212,8 @@ public function searchPrincipalUri(string $principalUri, ->from($this->dbObjectPropertiesTable, 'cob') ->leftJoin('cob', 'calendarobjects', 'co', $calendarObjectIdQuery->expr()->eq('co.id', 'cob.objectid')) ->andWhere($calendarObjectIdQuery->expr()->in('co.componenttype', $calendarObjectIdQuery->createNamedParameter($componentTypes, IQueryBuilder::PARAM_STR_ARRAY))) - ->andWhere($calendarOr) - ->andWhere($searchOr) + ->andWhere($calendarObjectIdQuery->expr()->orX(...$calendarOr)) + ->andWhere($calendarObjectIdQuery->expr()->orX(...$searchOr)) ->andWhere($calendarObjectIdQuery->expr()->isNull('deleted_at')); if ($pattern !== '') { diff --git a/apps/dav/lib/CardDAV/CardDavBackend.php b/apps/dav/lib/CardDAV/CardDavBackend.php index cdbbc228047f8..9d787c917d34f 100644 --- a/apps/dav/lib/CardDAV/CardDavBackend.php +++ b/apps/dav/lib/CardDAV/CardDavBackend.php @@ -1148,20 +1148,20 @@ private function searchByAddressBookIds(array $addressBookIds, /** * FIXME Find a way to match only 4 last digits * BDAY can be --1018 without year or 20001019 with it - * $bDayOr = $query2->expr()->orX(); + * $bDayOr = []; * if ($options['since'] instanceof DateTimeFilter) { - * $bDayOr->add( + * $bDayOr[] = * $query2->expr()->gte('SUBSTR(cp_bday.value, -4)', - * $query2->createNamedParameter($options['since']->get()->format('md'))) + * $query2->createNamedParameter($options['since']->get()->format('md')) * ); * } * if ($options['until'] instanceof DateTimeFilter) { - * $bDayOr->add( + * $bDayOr[] = * $query2->expr()->lte('SUBSTR(cp_bday.value, -4)', - * $query2->createNamedParameter($options['until']->get()->format('md'))) + * $query2->createNamedParameter($options['until']->get()->format('md')) * ); * } - * $query2->andWhere($bDayOr); + * $query2->andWhere($query2->expr()->orX(...$bDayOr)); */ } diff --git a/lib/private/BackgroundJob/JobList.php b/lib/private/BackgroundJob/JobList.php index cec487fbe7bae..cefd1e6a8927d 100644 --- a/lib/private/BackgroundJob/JobList.php +++ b/lib/private/BackgroundJob/JobList.php @@ -203,12 +203,12 @@ public function getNext(bool $onlyTimeSensitive = false, ?array $jobClasses = nu $query->andWhere($query->expr()->eq('time_sensitive', $query->createNamedParameter(IJob::TIME_SENSITIVE, IQueryBuilder::PARAM_INT))); } - if ($jobClasses !== null && count($jobClasses) > 0) { - $orClasses = $query->expr()->orx(); + if (!empty($jobClasses)) { + $orClasses = []; foreach ($jobClasses as $jobClass) { - $orClasses->add($query->expr()->eq('class', $query->createNamedParameter($jobClass, IQueryBuilder::PARAM_STR))); + $orClasses[] = $query->expr()->eq('class', $query->createNamedParameter($jobClass, IQueryBuilder::PARAM_STR)); } - $query->andWhere($orClasses); + $query->andWhere($query->expr()->orX(...$orClasses)); } $result = $query->executeQuery(); diff --git a/lib/private/DB/Connection.php b/lib/private/DB/Connection.php index 425d034fd07c6..271560a1f1452 100644 --- a/lib/private/DB/Connection.php +++ b/lib/private/DB/Connection.php @@ -459,22 +459,22 @@ public function setValues(string $table, array $keys, array $values, array $upda foreach ($values as $name => $value) { $updateQb->set($name, $updateQb->createNamedParameter($value, $this->getType($value))); } - $where = $updateQb->expr()->andX(); + $where = []; $whereValues = array_merge($keys, $updatePreconditionValues); foreach ($whereValues as $name => $value) { if ($value === '') { - $where->add($updateQb->expr()->emptyString( + $where[] = $updateQb->expr()->emptyString( $name - )); + ); } else { - $where->add($updateQb->expr()->eq( + $where[] = $updateQb->expr()->eq( $name, $updateQb->createNamedParameter($value, $this->getType($value)), $this->getType($value) - )); + ); } } - $updateQb->where($where); + $updateQb->where($updateQb->expr()->andX(...$where)); $affected = $updateQb->executeStatement(); if ($affected === 0 && !empty($updatePreconditionValues)) { diff --git a/lib/private/DB/QueryBuilder/QueryBuilder.php b/lib/private/DB/QueryBuilder/QueryBuilder.php index 93a2120ec9a49..d163b1e32dc4a 100644 --- a/lib/private/DB/QueryBuilder/QueryBuilder.php +++ b/lib/private/DB/QueryBuilder/QueryBuilder.php @@ -838,9 +838,10 @@ public function set($key, $value) { * // You can optionally programmatically build and/or expressions * $qb = $conn->getQueryBuilder(); * - * $or = $qb->expr()->orx(); - * $or->add($qb->expr()->eq('u.id', 1)); - * $or->add($qb->expr()->eq('u.id', 2)); + * $or = $qb->expr()->orx( + * $qb->expr()->eq('u.id', 1), + * $qb->expr()->eq('u.id', 2), + * ); * * $qb->update('users', 'u') * ->set('u.password', md5('password')) diff --git a/lib/private/TaskProcessing/Db/TaskMapper.php b/lib/private/TaskProcessing/Db/TaskMapper.php index 86b2a2fcc590a..da3910dcb3d89 100644 --- a/lib/private/TaskProcessing/Db/TaskMapper.php +++ b/lib/private/TaskProcessing/Db/TaskMapper.php @@ -59,16 +59,16 @@ public function findOldestScheduledByType(array $taskTypes, array $taskIdsToIgno ->setMaxResults(1) ->orderBy('last_updated', 'ASC'); - if (count($taskTypes) > 0) { - $filter = $qb->expr()->orX(); + if (!empty($taskTypes)) { + $filter = []; foreach ($taskTypes as $taskType) { - $filter->add($qb->expr()->eq('type', $qb->createPositionalParameter($taskType))); + $filter[] = $qb->expr()->eq('type', $qb->createPositionalParameter($taskType)); } - $qb->andWhere($filter); + $qb->andWhere($qb->expr()->orX(...$filter)); } - if (count($taskIdsToIgnore) > 0) { + if (!empty($taskIdsToIgnore)) { $qb->andWhere($qb->expr()->notIn('id', $qb->createNamedParameter($taskIdsToIgnore, IQueryBuilder::PARAM_INT_ARRAY))); } diff --git a/lib/public/DB/QueryBuilder/IQueryBuilder.php b/lib/public/DB/QueryBuilder/IQueryBuilder.php index 4d6ab1bd59299..ee992493738c8 100644 --- a/lib/public/DB/QueryBuilder/IQueryBuilder.php +++ b/lib/public/DB/QueryBuilder/IQueryBuilder.php @@ -608,9 +608,10 @@ public function set($key, $value); * // You can optionally programmatically build and/or expressions * $qb = $conn->getQueryBuilder(); * - * $or = $qb->expr()->orx(); - * $or->add($qb->expr()->eq('u.id', 1)); - * $or->add($qb->expr()->eq('u.id', 2)); + * $or = $qb->expr()->orx( + * $qb->expr()->eq('u.id', 1), + * $qb->expr()->eq('u.id', 2), + * ); * * $qb->update('users', 'u') * ->set('u.password', md5('password'))