[TASK] dbal: Move MySQL / DBMS field type translation into DBMS Specifics 54/39954/6
authorMorton Jonuschat <m.jonuschat@mojocode.de>
Thu, 4 Jun 2015 18:32:12 +0000 (20:32 +0200)
committerAndreas Fernandez <typo3@scripting-base.de>
Mon, 8 Jun 2015 07:10:52 +0000 (09:10 +0200)
The function used by the DBAL provide a very coarse and generic translations
between the ADdb meta types and the MySQL native types. This results in
ambiguities that can't properly be resolved and leads to changes in the
install tool schema migration that can never be resolved.

The default DBMS specifics provided resemble the current output of the
functions and don't result in any changes to the DBMS schema.

Resolves: #67297
Related: #67290
Related: #67288
Releases: master
Change-Id: Id26a897c7f43520edd188e0bd2ddb158507c8049
Reviewed-on: http://review.typo3.org/39954
Reviewed-by: Markus Klein <markus.klein@typo3.org>
Tested-by: Markus Klein <markus.klein@typo3.org>
Reviewed-by: Andreas Fernandez <typo3@scripting-base.de>
Tested-by: Andreas Fernandez <typo3@scripting-base.de>
typo3/sysext/core/Documentation/Changelog/master/Deprecation-67297-DbalFieldTypeConversion.rst [new file with mode: 0644]
typo3/sysext/dbal/Classes/Database/DatabaseConnection.php
typo3/sysext/dbal/Classes/Database/Specifics/AbstractSpecifics.php
typo3/sysext/dbal/Classes/Database/Specifics/Null.php [new file with mode: 0644]
typo3/sysext/dbal/Classes/Database/SqlParser.php
typo3/sysext/dbal/Tests/Unit/Database/DatabaseSpecificsTest.php [new file with mode: 0644]

diff --git a/typo3/sysext/core/Documentation/Changelog/master/Deprecation-67297-DbalFieldTypeConversion.rst b/typo3/sysext/core/Documentation/Changelog/master/Deprecation-67297-DbalFieldTypeConversion.rst
new file mode 100644 (file)
index 0000000..32d61b8
--- /dev/null
@@ -0,0 +1,22 @@
+========================================================
+Deprecation: #67297 - MySQL / DBMS field type conversion
+========================================================
+
+Description
+===========
+
+The Dbal\DatabaseConnection provides generic functions that translate between native MySQL field types
+and ADOdb meta field types. The generic functions ``MySQLActualType()`` and ``MySQLMetaType`` are
+deprecated and should not be used any longer.
+
+
+Impact
+======
+
+Although these are public functions the use was probably limited to the DBAL Extension.
+
+
+Migration
+=========
+
+Use the functions ``getNativeFieldType()`` and ``getMetaFieldType()`` provided by the DBMS specifics class.
index 6e8f8d2..fde289e 100644 (file)
@@ -244,8 +244,10 @@ class DatabaseConnection extends \TYPO3\CMS\Core\Database\DatabaseConnection {
                                        if (!is_subclass_of($className, Specifics\AbstractSpecifics::class)) {
                                                throw new \InvalidArgumentException($className . ' must inherit from ' . Specifics\AbstractSpecifics::class, 1416919866);
                                        }
-                                       $this->dbmsSpecifics = GeneralUtility::makeInstance($className);
+                               } else {
+                                       $className = Specifics\Null::class;
                                }
+                               $this->dbmsSpecifics = GeneralUtility::makeInstance($className);
                        }
                }
                $this->cacheFieldInfo();
@@ -383,7 +385,7 @@ class DatabaseConnection extends \TYPO3\CMS\Core\Database\DatabaseConnection {
                                        $fdef = $this->SQLparser->parseFieldDef($fdefString);
                                        $fieldType = isset($fdef['fieldType']) ? $fdef['fieldType'] : '';
                                        $this->cache_fieldType[$table][$field]['type'] = $fieldType;
-                                       $this->cache_fieldType[$table][$field]['metaType'] = $this->MySQLMetaType($fieldType);
+                                       $this->cache_fieldType[$table][$field]['metaType'] = $this->dbmsSpecifics->getMetaFieldType($fieldType);
                                        $this->cache_fieldType[$table][$field]['notnull'] = isset($fdef['featureIndex']['NOTNULL']) && !$this->SQLparser->checkEmptyDefaultValue($fdef['featureIndex']) ? 1 : 0;
                                        if (isset($fdef['featureIndex']['DEFAULT'])) {
                                                $default = $fdef['featureIndex']['DEFAULT']['value'][0];
@@ -2001,66 +2003,11 @@ class DatabaseConnection extends \TYPO3\CMS\Core\Database\DatabaseConnection {
         *
         * @param string $t native type as reported as in mysqldump files
         * @return string Meta type (currently ADOdb syntax only, http://phplens.com/lens/adodb/docs-adodb.htm#metatype)
+        * @deprecated since TYPO3 CMS 7, will be removed in TYPO3 CMS 8
         */
        public function MySQLMetaType($t) {
-               switch (strtoupper($t)) {
-                       case 'STRING':
-
-                       case 'CHAR':
-
-                       case 'VARCHAR':
-
-                       case 'TINYBLOB':
-
-                       case 'TINYTEXT':
-
-                       case 'ENUM':
-
-                       case 'SET':
-                               return 'C';
-                       case 'TEXT':
-
-                       case 'LONGTEXT':
-
-                       case 'MEDIUMTEXT':
-                               return 'XL';
-                       case 'IMAGE':
-
-                       case 'LONGBLOB':
-
-                       case 'BLOB':
-
-                       case 'MEDIUMBLOB':
-                               return 'B';
-                       case 'YEAR':
-
-                       case 'DATE':
-                               return 'D';
-                       case 'TIME':
-
-                       case 'DATETIME':
-
-                       case 'TIMESTAMP':
-                               return 'T';
-                       case 'FLOAT':
-
-                       case 'DOUBLE':
-                               return 'F';
-                       case 'INT':
-
-                       case 'INTEGER':
-
-                       case 'TINYINT':
-
-                       case 'SMALLINT':
-
-                       case 'MEDIUMINT':
-
-                       case 'BIGINT':
-                               return 'I8';
-                       default:
-                               return 'N';
-               }
+               GeneralUtility::logDeprecatedFunction();
+               return $this->dbmsSpecifics->getMetaFieldType($t);
        }
 
        /**
@@ -2068,44 +2015,11 @@ class DatabaseConnection extends \TYPO3\CMS\Core\Database\DatabaseConnection {
         *
         * @param string $meta Meta type (currenly ADOdb syntax only, http://phplens.com/lens/adodb/docs-adodb.htm#metatype)
         * @return string Native type as reported as in mysqldump files, uppercase
+        * @deprecated since TYPO3 CMS 7, will be removed in TYPO3 CMS 8
         */
        public function MySQLActualType($meta) {
-               switch (strtoupper($meta)) {
-                       case 'C':
-                               return 'VARCHAR';
-                       case 'XL':
-
-                       case 'X':
-                               return 'LONGTEXT';
-                       case 'C2':
-                               return 'VARCHAR';
-                       case 'X2':
-                               return 'LONGTEXT';
-                       case 'B':
-                               return 'LONGBLOB';
-                       case 'D':
-                               return 'DATE';
-                       case 'T':
-                               return 'DATETIME';
-                       case 'L':
-                               return 'TINYINT';
-                       case 'I':
-
-                       case 'I1':
-
-                       case 'I2':
-
-                       case 'I4':
-
-                       case 'I8':
-                               return 'BIGINT';
-                       case 'F':
-                               return 'DOUBLE';
-                       case 'N':
-                               return 'NUMERIC';
-                       default:
-                               return $meta;
-               }
+               GeneralUtility::logDeprecatedFunction();
+               return $this->dbmsSpecifics->getNativeFieldType($meta);
        }
 
        /**************************************
@@ -2725,15 +2639,8 @@ class DatabaseConnection extends \TYPO3\CMS\Core\Database\DatabaseConnection {
                                if (is_array($fieldRows)) {
                                        foreach ($fieldRows as $k => $fieldRow) {
                                                settype($fieldRow, 'array');
-                                               $fieldRow['Field'] = $fieldRow['name'];
-                                               $ntype = $this->MySQLActualType($this->MetaType($fieldRow['type'], $tableName));
-                                               $ntype .= $fieldRow['max_length'] != -1 ? ($ntype == 'INT' ? '(11)' : '(' . $fieldRow['max_length'] . ')') : '';
-                                               $fieldRow['Type'] = strtolower($ntype);
-                                               $fieldRow['Null'] = '';
-                                               $fieldRow['Key'] = '';
-                                               $fieldRow['Default'] = $fieldRow['default_value'];
-                                               $fieldRow['Extra'] = '';
-                                               $output[$fieldRow['name']] = $fieldRow;
+                                               $metaType = $this->MetaType($fieldRow['type'], $tableName, $fieldRow['name']);
+                                               $output[$fieldRow['name']] = $this->dbmsSpecifics->transformFieldRowToMySQL($fieldRow, $metaType);
                                        }
                                }
                                break;
index 717d73b..aac5f4a 100644 (file)
@@ -69,4 +69,156 @@ abstract class AbstractSpecifics {
                return array_chunk($expressionList, $this->getSpecific(self::LIST_MAXEXPRESSIONS), $preserveArrayKeys);
        }
 
-}
\ No newline at end of file
+       /**
+        * Transforms a database specific representation of field information and translates it
+        * as close as possible to the MySQL standard.
+        *
+        * @param array $fieldRow
+        * @param string $metaType
+        * @return array
+        */
+       public function transformFieldRowToMySQL($fieldRow, $metaType) {
+               $mysqlType = $this->getNativeFieldType($metaType);
+               $mysqlType .= $this->getNativeFieldLength($mysqlType, $fieldRow['max_length']);
+
+               $fieldRow['Field'] = $fieldRow['name'];
+               $fieldRow['Type'] = strtolower($mysqlType);
+               $fieldRow['Null'] = '';
+               $fieldRow['Key'] = '';
+               $fieldRow['Default'] = $fieldRow['default_value'];
+               $fieldRow['Extra'] = '';
+
+               return $fieldRow;
+       }
+
+       /**
+        * Return actual MySQL type for meta field type
+        *
+        * @param string $metaType Meta type (currenly ADOdb syntax only, http://phplens.com/lens/adodb/docs-adodb.htm#metatype)
+        * @return string Native type as reported as in mysqldump files, uppercase
+        */
+       public function getNativeFieldType($metaType) {
+               switch (strtoupper($metaType)) {
+                       case 'C':
+                               return 'VARCHAR';
+                       case 'XL':
+
+                       case 'X':
+                               return 'LONGTEXT';
+                       case 'C2':
+                               return 'VARCHAR';
+                       case 'X2':
+                               return 'LONGTEXT';
+                       case 'B':
+                               return 'LONGBLOB';
+                       case 'D':
+                               return 'DATE';
+                       case 'T':
+                               return 'DATETIME';
+                       case 'L':
+                               return 'TINYINT';
+                       case 'I':
+
+                       case 'I1':
+
+                       case 'I2':
+
+                       case 'I4':
+
+                       case 'I8':
+                               return 'BIGINT';
+                       case 'F':
+                               return 'DOUBLE';
+                       case 'N':
+                               return 'NUMERIC';
+                       default:
+                               return $metaType;
+               }
+       }
+
+       /**
+        * Return MetaType for native MySQL field type
+        *
+        * @param string $nativeType native type as reported as in mysqldump files
+        * @return string Meta type (currently ADOdb syntax only, http://phplens.com/lens/adodb/docs-adodb.htm#metatype)
+        */
+       public function getMetaFieldType($nativeType) {
+               switch (strtoupper($nativeType)) {
+                       case 'STRING':
+
+                       case 'CHAR':
+
+                       case 'VARCHAR':
+
+                       case 'TINYBLOB':
+
+                       case 'TINYTEXT':
+
+                       case 'ENUM':
+
+                       case 'SET':
+                               return 'C';
+                       case 'TEXT':
+
+                       case 'LONGTEXT':
+
+                       case 'MEDIUMTEXT':
+                               return 'XL';
+                       case 'IMAGE':
+
+                       case 'LONGBLOB':
+
+                       case 'BLOB':
+
+                       case 'MEDIUMBLOB':
+                               return 'B';
+                       case 'YEAR':
+
+                       case 'DATE':
+                               return 'D';
+                       case 'TIME':
+
+                       case 'DATETIME':
+
+                       case 'TIMESTAMP':
+                               return 'T';
+                       case 'FLOAT':
+
+                       case 'DOUBLE':
+                               return 'F';
+                       case 'INT':
+
+                       case 'INTEGER':
+
+                       case 'TINYINT':
+
+                       case 'SMALLINT':
+
+                       case 'MEDIUMINT':
+
+                       case 'BIGINT':
+                               return 'I8';
+                       default:
+                               return 'N';
+               }
+       }
+
+       /**
+        * Determine the native field length information for a table field.
+        *
+        * @param string  $mysqlType
+        * @param integer $maxLength
+        * @return string
+        */
+       public function getNativeFieldLength($mysqlType, $maxLength) {
+               if ($maxLength === -1) {
+                       return '';
+               }
+               switch ($mysqlType) {
+                       case 'INT':
+                               return '(11)';
+                       default:
+                               return '(' . $maxLength . ')';
+               }
+       }
+}
diff --git a/typo3/sysext/dbal/Classes/Database/Specifics/Null.php b/typo3/sysext/dbal/Classes/Database/Specifics/Null.php
new file mode 100644 (file)
index 0000000..88728c7
--- /dev/null
@@ -0,0 +1,23 @@
+<?php
+namespace TYPO3\CMS\Dbal\Database\Specifics;
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+/**
+ * This class contains a null driver for specifics used by any
+ * DBMS that does not have its own requirements.
+ * Any logic is in AbstractSpecifics.
+ */
+class Null extends AbstractSpecifics {
+}
index bafa6e2..912cda1 100644 (file)
@@ -364,7 +364,7 @@ class SqlParser extends \TYPO3\CMS\Core\Database\SqlParser {
                                break;
                        case 'adodb':
                                // Set type:
-                               $type = $this->databaseConnection->MySQLMetaType($fieldCfg['fieldType']);
+                               $type = $this->databaseConnection->getSpecifics()->getMetaFieldType($fieldCfg['fieldType']);
                                $cfg = $type;
                                // Add value, if any:
                                if ((string)$fieldCfg['value'] !== '' && in_array($type, array('C', 'C2'))) {
diff --git a/typo3/sysext/dbal/Tests/Unit/Database/DatabaseSpecificsTest.php b/typo3/sysext/dbal/Tests/Unit/Database/DatabaseSpecificsTest.php
new file mode 100644 (file)
index 0000000..dd7bed3
--- /dev/null
@@ -0,0 +1,145 @@
+<?php
+namespace TYPO3\CMS\Dbal\Tests\Unit\Database;
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+use TYPO3\CMS\Core\Utility\GeneralUtility;
+use TYPO3\CMS\Dbal\Database\Specifics\AbstractSpecifics;
+
+/**
+ * Test case
+ */
+class DatabaseSpecificsTest extends AbstractTestCase {
+
+       /**
+        * @var AbstractSpecifics|\PHPUnit_Framework_MockObject_MockObject|\TYPO3\CMS\Core\Tests\AccessibleObjectInterface
+        */
+       protected $subject;
+
+       /**
+        * Set up
+        */
+       protected function setUp() {
+               $GLOBALS['TYPO3_LOADED_EXT'] = array();
+
+               /** @var AbstractSpecifics|\PHPUnit_Framework_MockObject_MockObject|\TYPO3\CMS\Core\Tests\AccessibleObjectInterface $subject */
+               $this->subject = GeneralUtility::makeInstance(\TYPO3\CMS\Dbal\Database\Specifics\Null::class);
+       }
+
+       /**
+        * @test
+        * @param string $nativeType
+        * @param string $expected
+        * @dataProvider determineMetaTypeProvider
+        */
+       public function determineMetaTypeFromNativeType($nativeType, $expected) {
+               $result = $this->subject->getMetaFieldType($nativeType);
+               $this->assertSame($expected, $result);
+       }
+
+       /**
+        * @test
+        * @param string $metaType
+        * @param string $expected
+        * @dataProvider determineNativeTypeProvider
+        */
+       public function determineNativeTypeFromMetaType($metaType, $expected) {
+               $result = $this->subject->getNativeFieldType($metaType);
+               $this->assertSame($expected, $result);
+       }
+
+       /**
+        * @test
+        * @param string $fieldType
+        * @param int $maxLength
+        * @param string $expected
+        * @dataProvider determineNativeFieldLengthProvider
+        */
+       public function determineNativeFieldLength($fieldType, $maxLength, $expected) {
+               $result = $this->subject->getNativeFieldLength($fieldType, $maxLength);
+               $this->assertSame($expected, $result);
+       }
+
+       /**
+        * @return array
+        */
+       public function determineMetaTypeProvider() {
+               return array(
+                       array('INT', 'I8'),
+                       array('INTEGER', 'I8'),
+                       array('TINYINT', 'I8'),
+                       array('SMALLINT', 'I8'),
+                       array('MEDIUMINT', 'I8'),
+                       array('BIGINT', 'I8'),
+                       array('DOUBLE', 'F'),
+                       array('FLOAT', 'F'),
+                       array('TIME', 'T'),
+                       array('TIMESTAMP', 'T'),
+                       array('DATETIME', 'T'),
+                       array('DATE', 'D'),
+                       array('YEAR', 'D'),
+                       array('IMAGE', 'B'),
+                       array('BLOB', 'B'),
+                       array('MEDIUMBLOB', 'B'),
+                       array('LONGBLOB', 'B'),
+                       array('IMAGE', 'B'),
+                       array('TEXT', 'XL'),
+                       array('MEDIUMTEXT', 'XL'),
+                       array('LONGTEXT', 'XL'),
+                       array('STRING', 'C'),
+                       array('CHAR', 'C'),
+                       array('VARCHAR', 'C'),
+                       array('TINYBLOB', 'C'),
+                       array('TINYTEXT', 'C'),
+                       array('ENUM', 'C'),
+                       array('SET', 'C')
+               );
+       }
+
+       /**
+        * @return array
+        */
+       public function determineNativeTypeProvider() {
+               return array(
+                       array('C', 'VARCHAR'),
+                       array('C2', 'VARCHAR'),
+                       array('X', 'LONGTEXT'),
+                       array('X2', 'LONGTEXT'),
+                       array('XL', 'LONGTEXT'),
+                       array('B', 'LONGBLOB'),
+                       array('D', 'DATE'),
+                       array('T', 'DATETIME'),
+                       array('L', 'TINYINT'),
+                       array('I', 'BIGINT'),
+                       array('I1', 'BIGINT'),
+                       array('I2', 'BIGINT'),
+                       array('I4', 'BIGINT'),
+                       array('I8', 'BIGINT'),
+                       array('F', 'DOUBLE'),
+                       array('N', 'NUMERIC'),
+                       array('U', 'U')
+               );
+       }
+
+       /**
+        * @return array
+        */
+       public function determineNativeFieldLengthProvider() {
+               return array(
+                       array('INT', '4', '(11)'),
+                       array('VARCHAR', -1, ''),
+                       array('VARCHAR', 30, '(30)')
+               );
+       }
+}