[BUGFIX] DB Check causes PHP warnings and errors 13/51813/9
authorThomas Hohn <thomas@hohn.dk>
Fri, 24 Feb 2017 07:26:56 +0000 (08:26 +0100)
committerChristian Kuhn <lolli@schwarzbu.ch>
Sun, 26 Feb 2017 19:47:15 +0000 (20:47 +0100)
In DatabaseIntegrityCheck the method selectNonEmptyRecordsWithFkeys
also needs to take the BLOB into account or else it will fail with
a PHP warning. Furthermore the returned tableColumns list may contain
quoted fields names if the field name can be interpreted as a keyword
for the used connection platform. This patch ensures the correct
column information is received before further processing is started.

Additionally the "Record Statics" module is fixed by checking for an
empty TCA table field which invokes an exception.

Resolves: #79347
Resolves: #79991
Resolves: #79992
Releases: master
Change-Id: Ie455abd2da90d7dabc773ec345b5c8a87b0b5836
Reviewed-on: https://review.typo3.org/51813
Reviewed-by: Christer V <cvi@systime.dk>
Tested-by: Christer V <cvi@systime.dk>
Tested-by: TYPO3com <no-reply@typo3.com>
Reviewed-by: Claus Due <claus@phpmind.net>
Reviewed-by: Nicole Cordes <typo3@cordes.co>
Tested-by: Nicole Cordes <typo3@cordes.co>
Reviewed-by: Markus Klein <markus.klein@typo3.org>
Reviewed-by: Anders Kostending <aha@systime.dk>
Reviewed-by: Christian Kuhn <lolli@schwarzbu.ch>
Tested-by: Christian Kuhn <lolli@schwarzbu.ch>
typo3/sysext/core/Classes/Database/Query/Expression/ExpressionBuilder.php
typo3/sysext/core/Classes/Integrity/DatabaseIntegrityCheck.php
typo3/sysext/core/Tests/Unit/Database/Query/Expression/ExpressionBuilderTest.php

index ad5cf5b..692dd25 100644 (file)
@@ -438,6 +438,18 @@ class ExpressionBuilder
     }
 
     /**
+     * Creates a LENGTH expression for the given field/alias.
+     *
+     * @param string $fieldName
+     * @param string|null $alias
+     * @return string
+     */
+    public function length(string $fieldName, string $alias = null): string
+    {
+        return $this->calculation('LENGTH', $fieldName, $alias);
+    }
+
+    /**
      * Create a SQL aggregate function.
      *
      * @param string $aggregateName
index 82be6f7..34eb270 100644 (file)
@@ -18,6 +18,7 @@ use Doctrine\DBAL\Types\Type;
 use TYPO3\CMS\Backend\Utility\BackendUtility;
 use TYPO3\CMS\Core\Database\Connection;
 use TYPO3\CMS\Core\Database\ConnectionPool;
+use TYPO3\CMS\Core\Database\Query\Expression\ExpressionBuilder;
 use TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction;
 use TYPO3\CMS\Core\Database\RelationHandler;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
@@ -227,7 +228,11 @@ class DatabaseIntegrityCheck
                 }
                 $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($table);
                 $queryBuilder->getRestrictions()->removeAll();
-                $queryResult = $queryBuilder->select('uid', 'pid', $GLOBALS['TCA'][$table]['ctrl']['label'])
+                $selectFields = ['uid', 'pid'];
+                if (!empty($GLOBALS['TCA'][$table]['ctrl']['label'])) {
+                    $selectFields[] = $GLOBALS['TCA'][$table]['ctrl']['label'];
+                }
+                $queryResult = $queryBuilder->select(...$selectFields)
                     ->from($table)
                     ->where(
                         $queryBuilder->expr()->notIn(
@@ -422,7 +427,8 @@ class DatabaseIntegrityCheck
             $connectionPool = GeneralUtility::makeInstance(ConnectionPool::class);
             foreach ($fkey_arrays as $table => $field_list) {
                 if ($GLOBALS['TCA'][$table] && trim($field_list)) {
-                    $schemaManager = $connectionPool->getConnectionForTable($table)->getSchemaManager();
+                    $connection = $connectionPool->getConnectionForTable($table);
+                    $schemaManager = $connection->getSchemaManager();
                     $tableColumns = $schemaManager->listTableColumns($table);
 
                     $queryBuilder = $connectionPool->getQueryBuilderForTable($table);
@@ -433,9 +439,13 @@ class DatabaseIntegrityCheck
                     $queryBuilder->select('uid')
                         ->from($table);
                     $whereClause = [];
+
                     foreach ($fields as $fieldName) {
                         // The array index of $tableColumns is the lowercased column name!
-                        $fieldType = $tableColumns[strtolower($fieldName)]->getType()->getName();
+                        // It is quoted for keywords
+                        $column = $tableColumns[strtolower($fieldName)]
+                            ?? $tableColumns[$connection->quoteIdentifier(strtolower($fieldName))];
+                        $fieldType = $column->getType()->getName();
                         if (in_array(
                             $fieldType,
                             [Type::BIGINT, Type::INTEGER, Type::SMALLINT, Type::DECIMAL, Type::FLOAT],
@@ -456,6 +466,16 @@ class DatabaseIntegrityCheck
                                     $queryBuilder->createNamedParameter('', \PDO::PARAM_STR)
                                 )
                             );
+                        } elseif (in_array($fieldType, [Type::BLOB], true)) {
+                            $whereClause[] = $queryBuilder->expr()->andX(
+                                $queryBuilder->expr()->isNotNull($fieldName),
+                                $queryBuilder->expr()
+                                    ->comparison(
+                                        $queryBuilder->expr()->length($fieldName),
+                                        ExpressionBuilder::GT,
+                                        $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT)
+                                    )
+                            );
                         }
                     }
                     $queryResult = $queryBuilder->orWhere(...$whereClause)->execute();
index 30798ba..4926d90 100644 (file)
@@ -491,6 +491,23 @@ class ExpressionBuilderTest extends \TYPO3\TestingFramework\Core\Unit\UnitTestCa
     /**
      * @test
      */
+    public function lengthQuotesIdentifier()
+    {
+        $this->connectionProphet->quoteIdentifier(Argument::cetera())->will(function ($args) {
+            $platform = new MockPlatform();
+            return $platform->quoteIdentifier($args[0]);
+        });
+
+        $this->assertSame('LENGTH("tableName"."fieldName")', $this->subject->length('tableName.fieldName'));
+        $this->assertSame(
+            'LENGTH("tableName"."fieldName") AS "anAlias"',
+            $this->subject->length('tableName.fieldName', 'anAlias')
+        );
+    }
+
+    /**
+     * @test
+     */
     public function trimQuotesIdentifierWithDefaultValues()
     {
         $platform = new MockPlatform();