[BUGFIX] Move Doctrine custom type init back to ConnectionPool 79/55379/6
authorAlexander Stehlik <alexander.stehlik@gmail.com>
Tue, 16 Jan 2018 15:11:34 +0000 (16:11 +0100)
committerChristian Kuhn <lolli@schwarzbu.ch>
Sun, 18 Mar 2018 12:27:54 +0000 (13:27 +0100)
To prevent race conditions etc. the custom type init which was moved
in patch https://review.typo3.org/#/c/54512/ gets reverted.

To fix the issue with the pageUnavailable_handling the method
TyposriptFrontendController::connectToDB handles the error correctly via
try catch block.

Change-Id: I4f410d82e54c182d7b032a8ab4486d5b3b859382
Releases: master, 8.7
Resolves: #83577
Related: #82795
Reviewed-on: https://review.typo3.org/55379
Tested-by: TYPO3com <no-reply@typo3.com>
Reviewed-by: Anja Leichsenring <aleichsenring@ab-softlab.de>
Tested-by: Anja Leichsenring <aleichsenring@ab-softlab.de>
Reviewed-by: Christian Kuhn <lolli@schwarzbu.ch>
Tested-by: Christian Kuhn <lolli@schwarzbu.ch>
typo3/sysext/core/Classes/Database/Connection.php
typo3/sysext/core/Classes/Database/ConnectionPool.php
typo3/sysext/core/Classes/Database/Schema/Parser/TableBuilder.php
typo3/sysext/frontend/Classes/Controller/TypoScriptFrontendController.php

index c96f272..f39d768 100644 (file)
@@ -19,18 +19,11 @@ use Doctrine\Common\EventManager;
 use Doctrine\DBAL\Configuration;
 use Doctrine\DBAL\Driver;
 use Doctrine\DBAL\Driver\Statement;
-use Doctrine\DBAL\Events;
 use Doctrine\DBAL\Platforms\PostgreSqlPlatform;
-use Doctrine\DBAL\Types\Type;
 use Psr\Log\LoggerAwareInterface;
 use Psr\Log\LoggerAwareTrait;
 use TYPO3\CMS\Core\Database\Query\Expression\ExpressionBuilder;
 use TYPO3\CMS\Core\Database\Query\QueryBuilder;
-use TYPO3\CMS\Core\Database\Schema\EventListener\SchemaAlterTableListener;
-use TYPO3\CMS\Core\Database\Schema\EventListener\SchemaColumnDefinitionListener;
-use TYPO3\CMS\Core\Database\Schema\EventListener\SchemaIndexDefinitionListener;
-use TYPO3\CMS\Core\Database\Schema\Types\EnumType;
-use TYPO3\CMS\Core\Database\Schema\Types\SetType;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
 
 class Connection extends \Doctrine\DBAL\Connection implements LoggerAwareInterface
@@ -74,28 +67,11 @@ class Connection extends \Doctrine\DBAL\Connection implements LoggerAwareInterfa
 
     /**
      * Prevents the duplicate registration of event handlers and types for this connection.
-     * If true the events will not be initialized any more in the connect() method.
+     * If true the events will not be initialized any more in the getDatabasePlatform() method.
      *
      * @var bool
      */
-    private $customTypesAndEventsInitialized = false;
-
-    /**
-     * Returns the list of custom Doctrine data types implemented by TYPO3.
-     * This method is needed by the Schema parser to register the types as it
-     * does not require a database connection and thus the types don't get
-     * registered automatically.
-     *
-     * @internal
-     * @return array
-     */
-    public static function getCustomDoctrineTypes(): array
-    {
-        return [
-            EnumType::TYPE => EnumType::class,
-            SetType::TYPE => SetType::class,
-        ];
-    }
+    private $customConnectSetupExecuted = false;
 
     /**
      * Initializes a new instance of the Connection class.
@@ -114,55 +90,25 @@ class Connection extends \Doctrine\DBAL\Connection implements LoggerAwareInterfa
     }
 
     /**
-     * Establishes the connection with the database.
+     * Gets the DatabasePlatform for the connection and initializes custom types and event listeners.
      *
-     * @return bool true if the connection was successfully established, false if the connection is already open.
+     * @return \Doctrine\DBAL\Platforms\AbstractPlatform
      */
     public function connect(): bool
     {
         // Early return if the connection is already open and custom setup has been done.
-        if (!parent::connect() || $this->customTypesAndEventsInitialized) {
+        if ($this->customConnectSetupExecuted || !parent::connect()) {
             return false;
         }
 
-        // Register custom data types
-        foreach (static::getCustomDoctrineTypes() as $type => $className) {
-            if (!Type::hasType($type)) {
-                Type::addType($type, $className);
-            }
-        }
-
-        // Register all custom data types in the type mapping
-        foreach (static::getCustomDoctrineTypes() as $type => $className) {
-            $this->getDatabasePlatform()->registerDoctrineTypeMapping($type, $type);
-        }
-
-        // Handler for building custom data type column definitions in the SchemaManager
-        $this->getDatabasePlatform()->getEventManager()->addEventListener(
-            Events::onSchemaColumnDefinition,
-            GeneralUtility::makeInstance(SchemaColumnDefinitionListener::class)
-        );
-
-        // Handler for enhanced index definitions in the SchemaManager
-        $this->getDatabasePlatform()->getEventManager()->addEventListener(
-            Events::onSchemaIndexDefinition,
-            GeneralUtility::makeInstance(SchemaIndexDefinitionListener::class)
-        );
-
-        // Handler for adding custom database platform options to ALTER TABLE requests in the SchemaManager
-        $this->getDatabasePlatform()->getEventManager()->addEventListener(
-            Events::onSchemaAlterTable,
-            GeneralUtility::makeInstance(SchemaAlterTableListener::class)
-        );
-
-        $this->customTypesAndEventsInitialized = true;
-
         foreach ($this->prepareConnectionCommands as $command) {
             if ($this->executeUpdate($command) === false) {
                 $this->logger->critical('Could not initialize DB connection with query "' . $command . '": ' . $this->errorInfo());
             }
         }
 
+        $this->customConnectSetupExecuted = true;
+
         return true;
     }
 
index bfc42a3..47e5ced 100644 (file)
@@ -16,7 +16,15 @@ namespace TYPO3\CMS\Core\Database;
  */
 
 use Doctrine\DBAL\DriverManager;
+use Doctrine\DBAL\Events;
+use Doctrine\DBAL\Types\Type;
 use TYPO3\CMS\Core\Database\Query\QueryBuilder;
+use TYPO3\CMS\Core\Database\Schema\EventListener\SchemaAlterTableListener;
+use TYPO3\CMS\Core\Database\Schema\EventListener\SchemaColumnDefinitionListener;
+use TYPO3\CMS\Core\Database\Schema\EventListener\SchemaIndexDefinitionListener;
+use TYPO3\CMS\Core\Database\Schema\Types\EnumType;
+use TYPO3\CMS\Core\Database\Schema\Types\SetType;
+use TYPO3\CMS\Core\Utility\GeneralUtility;
 
 /**
  * Manager that handles opening/retrieving database connections.
@@ -41,6 +49,14 @@ class ConnectionPool
     protected static $connections = [];
 
     /**
+     * @var array
+     */
+    protected $customDoctrineTypes = [
+        EnumType::TYPE => EnumType::class,
+        SetType::TYPE => SetType::class,
+    ];
+
+    /**
      * Creates a connection object based on the specified table name.
      *
      * This is the official entry point to get a database connection to ensure
@@ -138,6 +154,38 @@ class ConnectionPool
         $conn->setFetchMode(\PDO::FETCH_ASSOC);
         $conn->prepareConnection($connectionParams['initCommands'] ?? '');
 
+        // Register custom data types
+        foreach ($this->customDoctrineTypes as $type => $className) {
+            if (!Type::hasType($type)) {
+                Type::addType($type, $className);
+            }
+        }
+
+        // Register all custom data types in the type mapping
+        foreach ($this->customDoctrineTypes as $type => $className) {
+            $conn->getDatabasePlatform()->registerDoctrineTypeMapping($type, $type);
+        }
+
+        // Handler for building custom data type column definitions
+        // in the SchemaManager
+        $conn->getDatabasePlatform()->getEventManager()->addEventListener(
+            Events::onSchemaColumnDefinition,
+            GeneralUtility::makeInstance(SchemaColumnDefinitionListener::class)
+        );
+
+        // Handler for enhanced index definitions in the SchemaManager
+        $conn->getDatabasePlatform()->getEventManager()->addEventListener(
+            Events::onSchemaIndexDefinition,
+            GeneralUtility::makeInstance(SchemaIndexDefinitionListener::class)
+        );
+
+        // Handler for adding custom database platform options to ALTER TABLE
+        // requests in the SchemaManager
+        $conn->getDatabasePlatform()->getEventManager()->addEventListener(
+            Events::onSchemaAlterTable,
+            GeneralUtility::makeInstance(SchemaAlterTableListener::class)
+        );
+
         return $conn;
     }
 
@@ -173,4 +221,18 @@ class ConnectionPool
     {
         return array_keys($GLOBALS['TYPO3_CONF_VARS']['DB']['Connections']);
     }
+
+    /**
+     * Returns the list of custom Doctrine data types implemented by TYPO3.
+     * This method is needed by the Schema parser to register the types as it
+     * does not require a database connection and thus the types don't get
+     * registered automatically.
+     *
+     * @internal
+     * @return array
+     */
+    public function getCustomDoctrineTypes(): array
+    {
+        return $this->customDoctrineTypes;
+    }
 }
index 7815934..3ce0019 100644 (file)
@@ -22,7 +22,7 @@ use Doctrine\DBAL\Schema\Column;
 use Doctrine\DBAL\Schema\Index;
 use Doctrine\DBAL\Schema\Table;
 use Doctrine\DBAL\Types\Type;
-use TYPO3\CMS\Core\Database\Connection;
+use TYPO3\CMS\Core\Database\ConnectionPool;
 use TYPO3\CMS\Core\Database\Schema\Parser\AST\CreateColumnDefinitionItem;
 use TYPO3\CMS\Core\Database\Schema\Parser\AST\CreateForeignKeyDefinitionItem;
 use TYPO3\CMS\Core\Database\Schema\Parser\AST\CreateIndexDefinitionItem;
@@ -59,7 +59,12 @@ class TableBuilder
      */
     public function __construct(AbstractPlatform $platform = null)
     {
-        foreach (Connection::getCustomDoctrineTypes() as $type => $className) {
+        // Register custom data types as no connection might have
+        // been established yet so the types would not be available
+        // when building tables/columns.
+        $connectionPool = GeneralUtility::makeInstance(ConnectionPool::class);
+
+        foreach ($connectionPool->getCustomDoctrineTypes() as $type => $className) {
             if (!Type::hasType($type)) {
                 Type::addType($type, $className);
             }
index 0dbfdf7..03644ee 100644 (file)
@@ -14,7 +14,7 @@ namespace TYPO3\CMS\Frontend\Controller;
  * The TYPO3 project - inspiring people to share!
  */
 
-use Doctrine\DBAL\Exception\ConnectionException;
+use Doctrine\DBAL\DBALException;
 use Psr\Http\Message\ResponseInterface;
 use Psr\Log\LoggerAwareInterface;
 use Psr\Log\LoggerAwareTrait;
@@ -841,12 +841,15 @@ class TypoScriptFrontendController implements LoggerAwareInterface
      */
     public function connectToDB()
     {
-        $connection = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable('pages');
         try {
+            $connection = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable('pages');
             $connection->connect();
-        } catch (ConnectionException $exception) {
+        } catch (DBALException $exception) {
             // Cannot connect to current database
-            $message = 'Cannot connect to the configured database "' . $connection->getDatabase() . '"';
+            $message = sprintf(
+                'Cannot connect to the configured database. Connection failed with: "%s"',
+                $exception->getMessage()
+            );
             $this->logger->emergency($message, ['exception' => $exception]);
             try {
                 $response = GeneralUtility::makeInstance(ErrorController::class)->unavailableAction($message);