[BUGFIX] Move Doctrine custom type init back to ConnectionPool 55/56355/2
authorAlexander Stehlik <alexander.stehlik@gmail.com>
Tue, 16 Jan 2018 15:11:34 +0000 (16:11 +0100)
committerChristian Kuhn <lolli@schwarzbu.ch>
Mon, 19 Mar 2018 12:17:40 +0000 (13:17 +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/56355
Tested-by: TYPO3com <no-reply@typo3.com>
Reviewed-by: Stefan Neufeind <typo3.neufeind@speedpartner.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 f6d99a4..783740a 100644 (file)
@@ -19,16 +19,9 @@ 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 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
@@ -70,28 +63,11 @@ class Connection extends \Doctrine\DBAL\Connection
 
     /**
      * 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.
@@ -110,49 +86,17 @@ class Connection extends \Doctrine\DBAL\Connection
     }
 
     /**
-     * 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) {
                 GeneralUtility::sysLog(
@@ -163,6 +107,8 @@ class Connection extends \Doctrine\DBAL\Connection
             }
         }
 
+        $this->customConnectSetupExecuted = true;
+
         return true;
     }
 
index 191afb8..83b0be1 100644 (file)
@@ -17,7 +17,15 @@ namespace TYPO3\CMS\Core\Database;
 
 use Doctrine\DBAL\Configuration;
 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.
@@ -42,6 +50,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
@@ -141,6 +157,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;
     }
 
@@ -176,4 +224,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 64ae6bf..5c7540a 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 5f6326c..cc19fbb 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 TYPO3\CMS\Backend\FrontendBackendUserAuthentication;
 use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
 use TYPO3\CMS\Core\Cache\CacheManager;
@@ -916,12 +916,15 @@ class TypoScriptFrontendController
      */
     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()
+            );
             if ($this->checkPageUnavailableHandler()) {
                 $this->pageUnavailableAndExit($message);
             } else {