From 0367dbda85deb5009671ea8bf0484458a920a7a2 Mon Sep 17 00:00:00 2001 From: Susanne Moog <susanne.moog@typo3.org> Date: Thu, 31 May 2018 15:43:36 +0200 Subject: [PATCH] [BUGFIX] Statement::rowCount not reliable for SELECT queries Concerning the usage of Statement::rowCount() the Doctrine DBAL documentation states the following: Returns the number of rows affected by the last DELETE, INSERT, or UPDATE statement executed by the corresponding object. For default configurations using MySQL as platform, rowCount() is returning the expected value for SELECT statements as well. However, on other DBMS this value will be zero as mentioned in the documentation - for instance SQLite is a candidate for that. For retrieving the row count for given criteria, the count() expression has to be used explicitly. Resolves: #78129 Releases: master Change-Id: Id28d72a1a21e8615ec45b63d4aecc100d03a39a3 Reviewed-on: https://review.typo3.org/50083 Reviewed-by: Oliver Hader <oliver.hader@typo3.org> Tested-by: Oliver Hader <oliver.hader@typo3.org> Tested-by: TYPO3com <no-reply@typo3.com> Reviewed-by: Benni Mack <benni@typo3.org> Tested-by: Benni Mack <benni@typo3.org> --- .../Wizard/SuggestWizardDefaultReceiver.php | 6 +++++- .../backend/Classes/History/RecordHistory.php | 3 ++- .../backend/Classes/View/PageLayoutView.php | 4 ++-- .../BackendUserAuthentication.php | 10 +++++++--- .../Configuration/FlexForm/FlexFormTools.php | 6 +++++- .../AbstractDataHandlerActionTestCase.php | 7 +++++-- .../FlexForm/FlexFormToolsTest.php | 20 ++++++++++++------- .../Repository/AdministrationRepository.php | 15 +++++++++++--- .../Classes/Hook/CrawlerHook.php | 8 +++++--- .../TranslationStatusController.php | 2 +- .../Classes/Command/DeletedRecordsCommand.php | 7 +++++-- .../Classes/Command/LostFilesCommand.php | 3 ++- .../Classes/Command/OrphanRecordsCommand.php | 3 ++- .../Classes/RecordList/DatabaseRecordList.php | 5 ++++- 14 files changed, 70 insertions(+), 29 deletions(-) diff --git a/typo3/sysext/backend/Classes/Form/Wizard/SuggestWizardDefaultReceiver.php b/typo3/sysext/backend/Classes/Form/Wizard/SuggestWizardDefaultReceiver.php index 0e8fedca7743..b9663a09af0b 100644 --- a/typo3/sysext/backend/Classes/Form/Wizard/SuggestWizardDefaultReceiver.php +++ b/typo3/sysext/backend/Classes/Form/Wizard/SuggestWizardDefaultReceiver.php @@ -157,7 +157,11 @@ class SuggestWizardDefaultReceiver ->setFirstResult($start) ->setMaxResults(50) ->execute(); - $allRowsCount = $result->rowCount(); + $allRowsCount = GeneralUtility::makeInstance(ConnectionPool::class) + ->getQueryBuilderForTable($this->table) + ->count('uid') + ->execute() + ->fetchColumn(0); if ($allRowsCount) { while ($row = $result->fetch()) { // check if we already have collected the maximum number of records diff --git a/typo3/sysext/backend/Classes/History/RecordHistory.php b/typo3/sysext/backend/Classes/History/RecordHistory.php index da43799239fe..a5c50c1e0aee 100644 --- a/typo3/sysext/backend/Classes/History/RecordHistory.php +++ b/typo3/sysext/backend/Classes/History/RecordHistory.php @@ -349,7 +349,8 @@ class RecordHistory ) ) ->execute(); - if ($rows->rowCount() === 0) { + $rowCount = (int)$queryBuilder->count('uid')->execute()->fetchColumn(0); + if ($rowCount === 0) { continue; } foreach ($rows as $row) { diff --git a/typo3/sysext/backend/Classes/View/PageLayoutView.php b/typo3/sysext/backend/Classes/View/PageLayoutView.php index 046cff3d12bf..80f446af9ab3 100644 --- a/typo3/sysext/backend/Classes/View/PageLayoutView.php +++ b/typo3/sysext/backend/Classes/View/PageLayoutView.php @@ -1472,7 +1472,7 @@ class PageLayoutView implements LoggerAwareInterface if ($this->totalItems) { $result = $queryBuilder->execute(); // Will return FALSE, if $result is invalid - $dbCount = $result->rowCount(); + $dbCount = $queryBuilder->count('uid')->execute()->fetchColumn(0); } // If records were found, render the list if (!$dbCount) { @@ -1723,7 +1723,7 @@ class PageLayoutView implements LoggerAwareInterface if ($depth >= 0) { $result = $queryBuilder->execute(); - $rowCount = $result->rowCount(); + $rowCount = $queryBuilder->count('uid')->execute()->fetchColumn(0); $count = 0; while ($row = $result->fetch()) { BackendUtility::workspaceOL('pages', $row); diff --git a/typo3/sysext/core/Classes/Authentication/BackendUserAuthentication.php b/typo3/sysext/core/Classes/Authentication/BackendUserAuthentication.php index 62a831e71cfe..9f17096203c7 100644 --- a/typo3/sysext/core/Classes/Authentication/BackendUserAuthentication.php +++ b/typo3/sysext/core/Classes/Authentication/BackendUserAuthentication.php @@ -2446,11 +2446,15 @@ class BackendUserAuthentication extends AbstractUserAuthentication ->orderBy('tstamp') ->execute(); + $rowCount = $queryBuilder + ->count('uid') + ->execute() + ->fetchColumn(0); // Check for more than $max number of error failures with the last period. - if ($result->rowCount() > $max) { + if ($rowCount > $max) { // OK, so there were more than the max allowed number of login failures - so we will send an email then. $subject = 'TYPO3 Login Failure Warning (at ' . $GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename'] . ')'; - $email_body = 'There have been some attempts (' . $result->rowCount() . ') to login at the TYPO3 + $email_body = 'There have been some attempts (' . $rowCount . ') to login at the TYPO3 site "' . $GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename'] . '" (' . GeneralUtility::getIndpEnv('HTTP_HOST') . '). This is a dump of the failures: @@ -2469,7 +2473,7 @@ This is a dump of the failures: $mail->setTo($email)->setSubject($subject)->setBody($email_body); $mail->send(); // Logout written to log - $this->writelog(255, 4, 0, 3, 'Failure warning (%s failures within %s seconds) sent by email to %s', [$result->rowCount(), $secondsBack, $email]); + $this->writelog(255, 4, 0, 3, 'Failure warning (%s failures within %s seconds) sent by email to %s', [$rowCount, $secondsBack, $email]); } } } diff --git a/typo3/sysext/core/Classes/Configuration/FlexForm/FlexFormTools.php b/typo3/sysext/core/Classes/Configuration/FlexForm/FlexFormTools.php index d542170237b7..d300a342d741 100644 --- a/typo3/sysext/core/Classes/Configuration/FlexForm/FlexFormTools.php +++ b/typo3/sysext/core/Classes/Configuration/FlexForm/FlexFormTools.php @@ -301,7 +301,11 @@ class FlexFormTools ) ) ->execute(); - if ($queryStatement->rowCount() !== 1) { + $rowCount = $queryBuilder + ->count('uid') + ->execute() + ->fetchColumn(0); + if ($rowCount !== 1) { throw new InvalidParentRowException( 'The data structure for field "' . $fieldName . '" in table "' . $tableName . '" has to be looked up' . ' in field "' . $pointerFieldName . '". That field had no valid value, so a lookup in parent record' diff --git a/typo3/sysext/core/Tests/Functional/DataHandling/AbstractDataHandlerActionTestCase.php b/typo3/sysext/core/Tests/Functional/DataHandling/AbstractDataHandlerActionTestCase.php index bcbb03e48760..57409ec83d92 100644 --- a/typo3/sysext/core/Tests/Functional/DataHandling/AbstractDataHandlerActionTestCase.php +++ b/typo3/sysext/core/Tests/Functional/DataHandling/AbstractDataHandlerActionTestCase.php @@ -148,8 +148,11 @@ abstract class AbstractDataHandlerActionTestCase extends FunctionalTestCase ) ->execute(); - $actualErrorLogEntries = $statement->rowCount(); - if ($actualErrorLogEntries === $this->expectedErrorLogEntries) { + $actualErrorLogEntries = (int)$queryBuilder + ->count('uid') + ->execute() + ->fetchColumn(0); + if ($actualErrorLogEntries === (int)$this->expectedErrorLogEntries) { $this->assertSame($this->expectedErrorLogEntries, $actualErrorLogEntries); } else { $failureMessage = 'Expected ' . $this->expectedErrorLogEntries . ' entries in sys_log, but got ' . $actualErrorLogEntries . LF; diff --git a/typo3/sysext/core/Tests/Unit/Configuration/FlexForm/FlexFormToolsTest.php b/typo3/sysext/core/Tests/Unit/Configuration/FlexForm/FlexFormToolsTest.php index 520c714daf7f..8b3b5039eafb 100644 --- a/typo3/sysext/core/Tests/Unit/Configuration/FlexForm/FlexFormToolsTest.php +++ b/typo3/sysext/core/Tests/Unit/Configuration/FlexForm/FlexFormToolsTest.php @@ -566,7 +566,7 @@ class FlexFormToolsTest extends \TYPO3\TestingFramework\Core\Unit\UnitTestCase $queryBuilderProphecy->execute()->shouldBeCalled()->willReturn($statementProphecy->reveal()); // Error case that is tested here: Do not return a valid parent row from db -> exception should be thrown - $statementProphecy->rowCount()->shouldBeCalled()->willReturn(0); + $queryBuilderProphecy->count('uid')->shouldBeCalled()->willReturn($queryBuilderProphecy); $this->expectException(InvalidParentRowException::class); $this->expectExceptionCode(1463833794); (new FlexFormTools())->getDataStructureIdentifier($fieldTca, 'aTableName', 'aFieldName', $row); @@ -628,9 +628,10 @@ class FlexFormToolsTest extends \TYPO3\TestingFramework\Core\Unit\UnitTestCase $queryBuilderProphecy->where('uid = 2')->shouldBeCalled()->willReturn($queryBuilderRevelation); $queryBuilderProphecy->where('uid = 1')->shouldBeCalled()->willReturn($queryBuilderRevelation); $queryBuilderProphecy->execute()->shouldBeCalled()->willReturn($statementProphecy->reveal()); - $statementProphecy->rowCount()->shouldBeCalled()->willReturn(1); + $queryBuilderProphecy->count('uid')->shouldBeCalled()->willReturn($queryBuilderProphecy); // First db call returns $secondRow, second returns $thirdRow, which points back to $initialRow -> exception + $statementProphecy->fetchColumn(0)->willReturn(1); $statementProphecy->fetch()->willReturn($secondRow, $thirdRow); $this->expectException(InvalidParentRowLoopException::class); @@ -694,7 +695,8 @@ class FlexFormToolsTest extends \TYPO3\TestingFramework\Core\Unit\UnitTestCase $queryBuilderProphecy->where('uid = 2')->shouldBeCalled()->willReturn($queryBuilderRevelation); $queryBuilderProphecy->where('uid = 1')->shouldBeCalled()->willReturn($queryBuilderRevelation); $queryBuilderProphecy->execute()->shouldBeCalled()->willReturn($statementProphecy->reveal()); - $statementProphecy->rowCount()->shouldBeCalled()->willReturn(1); + $queryBuilderProphecy->count('uid')->shouldBeCalled()->willReturn($queryBuilderRevelation); + $statementProphecy->fetchColumn(0)->shouldBeCalled()->willReturn(1); // First db call returns $secondRow, second returns $thirdRow. $thirdRow has pid 0 and still no ds -> exception $statementProphecy->fetch()->willReturn($secondRow, $thirdRow); @@ -833,7 +835,8 @@ class FlexFormToolsTest extends \TYPO3\TestingFramework\Core\Unit\UnitTestCase $queryBuilderProphecy->where('uid = 2')->shouldBeCalled()->willReturn($queryBuilderRevelation); $queryBuilderProphecy->where('uid = 1')->shouldBeCalled()->willReturn($queryBuilderRevelation); $queryBuilderProphecy->execute()->shouldBeCalled()->willReturn($statementProphecy->reveal()); - $statementProphecy->rowCount()->shouldBeCalled()->willReturn(1); + $queryBuilderProphecy->count('uid')->shouldBeCalled()->willReturn($queryBuilderRevelation); + $statementProphecy->fetchColumn(0)->shouldBeCalled()->willReturn(1); // First db call returns $secondRow, second returns $thirdRow. $thirdRow resolves ds $statementProphecy->fetch()->willReturn($secondRow, $thirdRow); @@ -888,8 +891,9 @@ class FlexFormToolsTest extends \TYPO3\TestingFramework\Core\Unit\UnitTestCase $queryBuilderProphecy->createNamedParameter(2, 1)->willReturn(2); $expressionBuilderProphecy->eq('uid', 2)->shouldBeCalled()->willReturn('uid = 2'); $queryBuilderProphecy->where('uid = 2')->shouldBeCalled()->willReturn($queryBuilderRevelation); + $queryBuilderProphecy->count('uid')->shouldBeCalled()->willReturn($queryBuilderRevelation); $queryBuilderProphecy->execute()->shouldBeCalled()->willReturn($statementProphecy->reveal()); - $statementProphecy->rowCount()->shouldBeCalled()->willReturn(1); + $statementProphecy->fetchColumn(0)->shouldBeCalled()->willReturn(1); // First db call returns $secondRow. $secendRow resolves DS and does not look further up $statementProphecy->fetch()->willReturn($secondRow); @@ -948,8 +952,9 @@ class FlexFormToolsTest extends \TYPO3\TestingFramework\Core\Unit\UnitTestCase $queryBuilderProphecy->createNamedParameter(2, 1)->willReturn(2); $expressionBuilderProphecy->eq('uid', 2)->shouldBeCalled()->willReturn('uid = 2'); $queryBuilderProphecy->where('uid = 2')->shouldBeCalled()->willReturn($queryBuilderRevelation); + $queryBuilderProphecy->count('uid')->shouldBeCalled()->willReturn($queryBuilderRevelation); $queryBuilderProphecy->execute()->shouldBeCalled()->willReturn($statementProphecy->reveal()); - $statementProphecy->rowCount()->shouldBeCalled()->willReturn(1); + $statementProphecy->fetchColumn(0)->shouldBeCalled()->willReturn(1); // First db call returns $secondRow. $secendRow resolves DS and does not look further up $statementProphecy->fetch()->willReturn($secondRow); @@ -1030,7 +1035,8 @@ class FlexFormToolsTest extends \TYPO3\TestingFramework\Core\Unit\UnitTestCase $expressionBuilderProphecy->eq('uid', 2)->shouldBeCalled()->willReturn('uid = 2'); $queryBuilderProphecy->where('uid = 2')->shouldBeCalled()->willReturn($queryBuilderRevelation); $queryBuilderProphecy->execute()->shouldBeCalled()->willReturn($statementProphecy->reveal()); - $statementProphecy->rowCount()->shouldBeCalled()->willReturn(1); + $queryBuilderProphecy->count('uid')->shouldBeCalled()->willReturn($queryBuilderRevelation); + $statementProphecy->fetchColumn(0)->shouldBeCalled()->willReturn(1); // First db call returns $secondRow. $secendRow resolves DS and does not look further up $statementProphecy->fetch()->willReturn($secondRow); diff --git a/typo3/sysext/indexed_search/Classes/Domain/Repository/AdministrationRepository.php b/typo3/sysext/indexed_search/Classes/Domain/Repository/AdministrationRepository.php index 7abfb8988dc2..abb641c46849 100644 --- a/typo3/sysext/indexed_search/Classes/Domain/Repository/AdministrationRepository.php +++ b/typo3/sysext/indexed_search/Classes/Domain/Repository/AdministrationRepository.php @@ -70,7 +70,10 @@ class AdministrationRepository ) ) ->execute(); - $numberOfRows = $result->rowCount(); + $numberOfRows = $queryBuilder + ->count('uniqid') + ->execute() + ->fetchColumn(0); $allRows = []; while ($row = $result->fetch()) { $row['pcount'] = $numberOfRows; @@ -412,7 +415,10 @@ class AdministrationRepository } $result = $queryBuilder->execute(); - $count = (int)$result->rowCount(); + $count = (int)$queryBuilder + ->count('uid') + ->execute() + ->fetchColumn(0); $result->closeCursor(); // exist several statistics for this page? @@ -587,7 +593,10 @@ class AdministrationRepository ->groupBy('index_words.baseword') ->execute(); - $row['wordCount'] = $wordCountResult->rowCount(); + $row['wordCount'] = $queryBuilder + ->count('uid') + ->execute() + ->fetchColumn(0); $wordCountResult->closeCursor(); if ($mode === 'content') { diff --git a/typo3/sysext/indexed_search/Classes/Hook/CrawlerHook.php b/typo3/sysext/indexed_search/Classes/Hook/CrawlerHook.php index 484ddd88092e..fa6ee48e4da9 100644 --- a/typo3/sysext/indexed_search/Classes/Hook/CrawlerHook.php +++ b/typo3/sysext/indexed_search/Classes/Hook/CrawlerHook.php @@ -279,7 +279,7 @@ class CrawlerHook $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class) ->getQueryBuilderForTable($cfgRec['table2index']); - $result = $queryBuilder->select('*') + $baseQueryBuilder = $queryBuilder->select('*') ->from($cfgRec['table2index']) ->where( $queryBuilder->expr()->eq( @@ -290,7 +290,8 @@ class CrawlerHook 'uid', $queryBuilder->createNamedParameter($session_data['uid'], \PDO::PARAM_INT) ) - ) + ); + $result = $baseQueryBuilder ->setMaxResults($numberOfRecords) ->orderBy('uid') ->execute(); @@ -303,8 +304,9 @@ class CrawlerHook $session_data['uid'] = $row['uid']; } + $rowCount = $baseQueryBuilder->count('uid')->execute()->fetchColumn(0); // Finally, set entry for next indexing of batch of records: - if ($result->rowCount()) { + if ($rowCount) { $nparams = [ 'indexConfigUid' => $cfgRec['uid'], 'url' => 'Records from UID#' . ($session_data['uid'] + 1) . '-?', diff --git a/typo3/sysext/info/Classes/Controller/TranslationStatusController.php b/typo3/sysext/info/Classes/Controller/TranslationStatusController.php index b6b2b274218c..5579edfc309f 100644 --- a/typo3/sysext/info/Classes/Controller/TranslationStatusController.php +++ b/typo3/sysext/info/Classes/Controller/TranslationStatusController.php @@ -416,7 +416,7 @@ class TranslationStatusController extends \TYPO3\CMS\Backend\Module\AbstractFunc $row = $result->fetch(); BackendUtility::workspaceOL('pages', $row); if (is_array($row)) { - $row['_COUNT'] = $result->rowCount(); + $row['_COUNT'] = $queryBuilder->count('uid')->execute()->fetchColumn(0); $row['_HIDDEN'] = $row['hidden'] || (int)$row['endtime'] > 0 && (int)$row['endtime'] < $GLOBALS['EXEC_TIME'] || $GLOBALS['EXEC_TIME'] < (int)$row['starttime']; } $result->closeCursor(); diff --git a/typo3/sysext/lowlevel/Classes/Command/DeletedRecordsCommand.php b/typo3/sysext/lowlevel/Classes/Command/DeletedRecordsCommand.php index 99c947e2e3ba..01a2db56467f 100644 --- a/typo3/sysext/lowlevel/Classes/Command/DeletedRecordsCommand.php +++ b/typo3/sysext/lowlevel/Classes/Command/DeletedRecordsCommand.php @@ -150,9 +150,12 @@ class DeletedRecordsCommand extends Command ) ) ->execute(); - + $rowCount = $queryBuilderForPages + ->count('uid') + ->execute() + ->fetchColumn(0); // Register if page itself is deleted - if ($pageRecordIsDeleted->rowCount() > 0) { + if ($rowCount > 0) { $deletedRecords['pages'][$pageId] = $pageId; } } diff --git a/typo3/sysext/lowlevel/Classes/Command/LostFilesCommand.php b/typo3/sysext/lowlevel/Classes/Command/LostFilesCommand.php index 6b90e7951a57..2eb10da4d007 100644 --- a/typo3/sysext/lowlevel/Classes/Command/LostFilesCommand.php +++ b/typo3/sysext/lowlevel/Classes/Command/LostFilesCommand.php @@ -241,8 +241,9 @@ If you want to get more detailed information, use the --verbose option.') ->orderBy('sorting', 'DESC') ->execute(); + $rowCount = $queryBuilder->count('hash')->execute()->fetchColumn(0); // We conclude that the file is lost - if ($result->rowCount() === 0) { + if ($rowCount === 0) { $lostFiles[] = $value; } } diff --git a/typo3/sysext/lowlevel/Classes/Command/OrphanRecordsCommand.php b/typo3/sysext/lowlevel/Classes/Command/OrphanRecordsCommand.php index df8d732829df..ce924bd08023 100644 --- a/typo3/sysext/lowlevel/Classes/Command/OrphanRecordsCommand.php +++ b/typo3/sysext/lowlevel/Classes/Command/OrphanRecordsCommand.php @@ -109,7 +109,8 @@ Manual repair suggestions: ->execute(); $totalOrphans = 0; - if ($result->rowCount()) { + $rowCount = $queryBuilder->count('uid')->execute()->fetchColumn(0); + if ($rowCount) { $orphans[$tableName] = []; while ($orphanRecord = $result->fetch()) { $orphans[$tableName][$orphanRecord['uid']] = $orphanRecord['uid']; diff --git a/typo3/sysext/recordlist/Classes/RecordList/DatabaseRecordList.php b/typo3/sysext/recordlist/Classes/RecordList/DatabaseRecordList.php index e68f7ea7acb8..b1d5d64abcd0 100644 --- a/typo3/sysext/recordlist/Classes/RecordList/DatabaseRecordList.php +++ b/typo3/sysext/recordlist/Classes/RecordList/DatabaseRecordList.php @@ -1107,7 +1107,10 @@ class DatabaseRecordList $this->showLimit = $this->totalItems; $this->iLimit = $this->totalItems; } - $dbCount = $queryResult->rowCount(); + $dbCount = $queryBuilder + ->count('uid') + ->execute() + ->fetchColumn(0); } } // If any records was selected, render the list: -- GitLab