[BUGFIX] Statement::rowCount not reliable for SELECT queries 83/50083/10
authorSusanne Moog <susanne.moog@typo3.org>
Thu, 31 May 2018 13:43:36 +0000 (15:43 +0200)
committerBenni Mack <benni@typo3.org>
Thu, 31 May 2018 17:27:38 +0000 (19:27 +0200)
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>
14 files changed:
typo3/sysext/backend/Classes/Form/Wizard/SuggestWizardDefaultReceiver.php
typo3/sysext/backend/Classes/History/RecordHistory.php
typo3/sysext/backend/Classes/View/PageLayoutView.php
typo3/sysext/core/Classes/Authentication/BackendUserAuthentication.php
typo3/sysext/core/Classes/Configuration/FlexForm/FlexFormTools.php
typo3/sysext/core/Tests/Functional/DataHandling/AbstractDataHandlerActionTestCase.php
typo3/sysext/core/Tests/Unit/Configuration/FlexForm/FlexFormToolsTest.php
typo3/sysext/indexed_search/Classes/Domain/Repository/AdministrationRepository.php
typo3/sysext/indexed_search/Classes/Hook/CrawlerHook.php
typo3/sysext/info/Classes/Controller/TranslationStatusController.php
typo3/sysext/lowlevel/Classes/Command/DeletedRecordsCommand.php
typo3/sysext/lowlevel/Classes/Command/LostFilesCommand.php
typo3/sysext/lowlevel/Classes/Command/OrphanRecordsCommand.php
typo3/sysext/recordlist/Classes/RecordList/DatabaseRecordList.php

index 0e8fedc..b9663a0 100644 (file)
@@ -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
index da43799..a5c50c1 100644 (file)
@@ -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) {
index 046cff3..80f446a 100644 (file)
@@ -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);
index 62a831e..9f17096 100644 (file)
@@ -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]);
             }
         }
     }
index d542170..d300a34 100644 (file)
@@ -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'
index bcbb03e..57409ec 100644 (file)
@@ -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;
index 520c714..8b3b503 100644 (file)
@@ -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);
index 7abfb89..abb641c 100644 (file)
@@ -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') {
index 484ddd8..fa6ee48 100644 (file)
@@ -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) . '-?',
index b6b2b27..5579edf 100644 (file)
@@ -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();
index 99c947e..01a2db5 100644 (file)
@@ -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;
             }
         }
index 6b90e79..2eb10da 100644 (file)
@@ -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;
             }
         }
index df8d732..ce924bd 100644 (file)
@@ -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'];
index e68f7ea..b1d5d64 100644 (file)
@@ -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: