[FEATURE] dbal: DBMS specific conversion between Meta/MySQL field types 38/40138/6
authorMorton Jonuschat <m.jonuschat@mojocode.de>
Sat, 6 Jun 2015 16:43:57 +0000 (18:43 +0200)
committerAndreas Fernandez <typo3@scripting-base.de>
Fri, 19 Jun 2015 09:43:21 +0000 (11:43 +0200)
This patch changes the implementation of the methods that convert
between ADOdb meta field types and MySQL native field types to allow
overriding the conversion per data type.

The PostgresSpecifics provide better matching of BLOB, SERIAL, DOUBLE
and INTEGER types.

Resolves: #67290
Releases: master
Change-Id: Ieda2a2a26d411b031c2db96821d85e21b2ebb037
Reviewed-on: http://review.typo3.org/40138
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/dbal/Classes/Database/DatabaseConnection.php
typo3/sysext/dbal/Classes/Database/Specifics/AbstractSpecifics.php
typo3/sysext/dbal/Classes/Database/Specifics/Oci8.php [deleted file]
typo3/sysext/dbal/Classes/Database/Specifics/Oci8Specifics.php [new file with mode: 0644]
typo3/sysext/dbal/Classes/Database/Specifics/PostgresSpecifics.php [new file with mode: 0644]
typo3/sysext/dbal/Tests/Unit/Database/DatabaseConnectionOracleTest.php
typo3/sysext/dbal/Tests/Unit/Database/DatabaseSpecificsPostgresqlTest.php [new file with mode: 0644]

index c1b865a..936d61a 100644 (file)
@@ -241,7 +241,7 @@ class DatabaseConnection extends \TYPO3\CMS\Core\Database\DatabaseConnection {
                        if (isset($this->handlerCfg['_DEFAULT']['config']['driver'])) {
                                // load DBMS specifics
                                $driver = $this->handlerCfg['_DEFAULT']['config']['driver'];
-                               $className = 'TYPO3\\CMS\\Dbal\\Database\\Specifics\\' . ucfirst(strtolower($driver));
+                               $className = 'TYPO3\\CMS\\Dbal\\Database\\Specifics\\' . ucfirst(strtolower($driver)) . 'Specifics';
                                if (class_exists($className)) {
                                        if (!is_subclass_of($className, Specifics\AbstractSpecifics::class)) {
                                                throw new \InvalidArgumentException($className . ' must inherit from ' . Specifics\AbstractSpecifics::class, 1416919866);
index f719279..288f0fc 100644 (file)
@@ -35,6 +35,85 @@ abstract class AbstractSpecifics {
        protected $specificProperties = array();
 
        /**
+        * Contains the DBMS specific mapping information for native MySQL to ADOdb meta field types
+        *
+        * @var array
+        */
+       protected $nativeToMetaFieldTypeMap = array(
+               'STRING' => 'C',
+               'CHAR' => 'C',
+               'VARCHAR' => 'C',
+               'TINYBLOB' => 'C',
+               'TINYTEXT' => 'C',
+               'ENUM' => 'C',
+               'SET' => 'C',
+               'TEXT' => 'XL',
+               'LONGTEXT' => 'XL',
+               'MEDIUMTEXT' => 'XL',
+               'IMAGE' => 'B',
+               'LONGBLOB' => 'B',
+               'BLOB' => 'B',
+               'MEDIUMBLOB' => 'B',
+               'YEAR' => 'D',
+               'DATE' => 'D',
+               'TIME' => 'T',
+               'DATETIME' => 'T',
+               'TIMESTAMP' => 'T',
+               'FLOAT' => 'F',
+               'DOUBLE' => 'F',
+               'INT' => 'I8',
+               'INTEGER' => 'I8',
+               'TINYINT' => 'I8',
+               'SMALLINT' => 'I8',
+               'MEDIUMINT' => 'I8',
+               'BIGINT' => 'I8',
+       );
+
+       /**
+        * Contains the DBMS specific mapping overrides for native MySQL to ADOdb meta field types
+        */
+       protected $nativeToMetaFieldTypeOverrides = array();
+
+       /**
+        * Contains the default mapping information for ADOdb meta to MySQL native field types
+        *
+        * @var array
+        */
+       protected $metaToNativeFieldTypeMap = array(
+               'C' => 'VARCHAR',
+               'C2' => 'VARCHAR',
+               'X' => 'LONGTEXT',
+               'XL' => 'LONGTEXT',
+               'X2' => 'LONGTEXT',
+               'B' => 'LONGBLOB',
+               'D' => 'DATE',
+               'T' => 'DATETIME',
+               'L' => 'TINYINT',
+               'I' => 'BIGINT',
+               'I1' => 'BIGINT',
+               'I2' => 'BIGINT',
+               'I4' => 'BIGINT',
+               'I8' => 'BIGINT',
+               'F' => 'DOUBLE',
+               'N' => 'NUMERIC'
+       );
+
+       /**
+        * Contains the DBMS specific mapping information for ADOdb meta field types to MySQL native field types
+        *
+        * @var array
+        */
+       protected $metaToNativeFieldTypeOverrides = array();
+
+       /**
+        * Constructor
+        */
+       public function __construct() {
+               $this->nativeToMetaFieldTypeMap = array_merge($this->nativeToMetaFieldTypeMap, $this->nativeToMetaFieldTypeOverrides);
+               $this->metaToNativeFieldTypeMap = array_merge($this->metaToNativeFieldTypeMap, $this->metaToNativeFieldTypeOverrides);
+       }
+
+       /**
         * Checks if a specific is defined for the used DBMS.
         *
         * @param string $specific
@@ -98,42 +177,8 @@ abstract class AbstractSpecifics {
         * @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;
-               }
+               $metaType = strtoupper($metaType);
+               return empty($this->metaToNativeFieldTypeMap[$metaType]) ? $metaType : $this->metaToNativeFieldTypeMap[$metaType];
        }
 
        /**
@@ -143,64 +188,8 @@ abstract class AbstractSpecifics {
         * @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';
-               }
+               $nativeType = strtoupper($nativeType);
+               return empty($this->nativeToMetaFieldTypeMap[$nativeType]) ? 'N' : $this->nativeToMetaFieldTypeMap[$nativeType];
        }
 
        /**
diff --git a/typo3/sysext/dbal/Classes/Database/Specifics/Oci8.php b/typo3/sysext/dbal/Classes/Database/Specifics/Oci8.php
deleted file mode 100644 (file)
index 97f2ff3..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-<?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 the specifics for Oracle DBMS.
- * Any logic is in AbstractSpecifics.
- */
-class Oci8 extends AbstractSpecifics {
-       /**
-        * Contains the specifics that need to be taken care of for Oracle DBMS.
-        *
-        * @var array
-        */
-       protected $specificProperties = array(
-               self::TABLE_MAXLENGTH => 30,
-               self::FIELD_MAXLENGTH => 30,
-               self::LIST_MAXEXPRESSIONS => 1000
-       );
-}
\ No newline at end of file
diff --git a/typo3/sysext/dbal/Classes/Database/Specifics/Oci8Specifics.php b/typo3/sysext/dbal/Classes/Database/Specifics/Oci8Specifics.php
new file mode 100644 (file)
index 0000000..b5bafb7
--- /dev/null
@@ -0,0 +1,32 @@
+<?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 the specifics for Oracle DBMS.
+ * Any logic is in AbstractSpecifics.
+ */
+class Oci8Specifics extends AbstractSpecifics {
+       /**
+        * Contains the specifics that need to be taken care of for Oracle DBMS.
+        *
+        * @var array
+        */
+       protected $specificProperties = array(
+               self::TABLE_MAXLENGTH => 30,
+               self::FIELD_MAXLENGTH => 30,
+               self::LIST_MAXEXPRESSIONS => 1000
+       );
+}
diff --git a/typo3/sysext/dbal/Classes/Database/Specifics/PostgresSpecifics.php b/typo3/sysext/dbal/Classes/Database/Specifics/PostgresSpecifics.php
new file mode 100644 (file)
index 0000000..fd620fc
--- /dev/null
@@ -0,0 +1,75 @@
+<?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 the specifics for PostgreSQL DBMS.
+ * Any logic is in AbstractSpecifics.
+ */
+class PostgresSpecifics extends AbstractSpecifics {
+       /**
+        * Contains the DBMS specific mapping overrides for native MySQL to ADOdb meta field types
+        */
+       protected $nativeToMetaFieldTypeOverrides = array(
+               'TINYBLOB' => 'B',
+               'INT' => 'I4',
+               'INTEGER' => 'I4',
+               'TINYINT' => 'I2',
+               'SMALLINT' => 'I2',
+               'MEDIUMINT' => 'I4'
+       );
+
+       /**
+        * Contains the DBMS specific mapping information for ADOdb meta field types to MySQL native field types
+        *
+        * @var array
+        */
+       protected $metaToNativeFieldTypeOverrides = array(
+               'R' => 'INT',
+               'I' => 'INT',
+               'I1' => 'SMALLINT',
+               'I2' => 'SMALLINT',
+               'I4' => 'INT',
+       );
+
+       /**
+        * 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 'DOUBLE':
+                               return '';
+                       case 'TINYINT':
+                               return '(4)';
+                       case 'SMALLINT':
+                               return '(6)';
+                       case 'MEDIUMINT':
+                               return '(9)';
+                       case 'INT':
+                               return '(11)';
+                       case 'BIGINT':
+                               return '(20)';
+                       default:
+                               return '(' . $maxLength . ')';
+               }
+       }
+}
index d04174a..085e894 100644 (file)
@@ -916,7 +916,7 @@ class DatabaseConnectionOracleTest extends AbstractTestCase {
        public function expressionListWithNotInIsConcatenatedWithAnd() {
                $listMaxExpressions = 1000;
 
-               $mockSpecificsOci8 = $this->getAccessibleMock(\TYPO3\CMS\Dbal\Database\Specifics\Oci8::class, array(), array(), '', FALSE);
+               $mockSpecificsOci8 = $this->getAccessibleMock(\TYPO3\CMS\Dbal\Database\Specifics\Oci8Specifics::class, array(), array(), '', FALSE);
                $mockSpecificsOci8->expects($this->any())->method('getSpecific')->will($this->returnValue($listMaxExpressions));
 
                $items = range(0, 1250);
@@ -947,7 +947,7 @@ class DatabaseConnectionOracleTest extends AbstractTestCase {
        public function expressionListWithInIsConcatenatedWithOr() {
                $listMaxExpressions = 1000;
 
-               $mockSpecificsOci8 = $this->getAccessibleMock(\TYPO3\CMS\Dbal\Database\Specifics\Oci8::class, array(), array(), '', FALSE);
+               $mockSpecificsOci8 = $this->getAccessibleMock(\TYPO3\CMS\Dbal\Database\Specifics\Oci8Specifics::class, array(), array(), '', FALSE);
                $mockSpecificsOci8->expects($this->any())->method('getSpecific')->will($this->returnValue($listMaxExpressions));
 
                $items = range(0, 1250);
@@ -978,7 +978,7 @@ class DatabaseConnectionOracleTest extends AbstractTestCase {
        public function expressionListIsUnchanged() {
                $listMaxExpressions = 1000;
 
-               $mockSpecificsOci8 = $this->getAccessibleMock(\TYPO3\CMS\Dbal\Database\Specifics\Oci8::class, array(), array(), '', FALSE);
+               $mockSpecificsOci8 = $this->getAccessibleMock(\TYPO3\CMS\Dbal\Database\Specifics\Oci8Specifics::class, array(), array(), '', FALSE);
                $mockSpecificsOci8->expects($this->any())->method('getSpecific')->will($this->returnValue($listMaxExpressions));
 
                $result = $this->subject->SELECTquery('*', 'tt_content', 'uid IN (0,1,2,3,4,5,6,7,8,9,10)');
@@ -993,7 +993,7 @@ class DatabaseConnectionOracleTest extends AbstractTestCase {
        public function expressionListBracesAreSetCorrectly() {
                $listMaxExpressions = 1000;
 
-               $mockSpecificsOci8 = $this->getAccessibleMock(\TYPO3\CMS\Dbal\Database\Specifics\Oci8::class, array(), array(), '', FALSE);
+               $mockSpecificsOci8 = $this->getAccessibleMock(\TYPO3\CMS\Dbal\Database\Specifics\Oci8Specifics::class, array(), array(), '', FALSE);
                $mockSpecificsOci8->expects($this->any())->method('getSpecific')->will($this->returnValue($listMaxExpressions));
 
                $items = range(0, 1250);
@@ -1024,7 +1024,7 @@ class DatabaseConnectionOracleTest extends AbstractTestCase {
        public function multipleExpressiosInWhereClauseAreBracedCorrectly() {
                $listMaxExpressions = 1000;
 
-               $mockSpecificsOci8 = $this->getAccessibleMock(\TYPO3\CMS\Dbal\Database\Specifics\Oci8::class, array(), array(), '', FALSE);
+               $mockSpecificsOci8 = $this->getAccessibleMock(\TYPO3\CMS\Dbal\Database\Specifics\Oci8Specifics::class, array(), array(), '', FALSE);
                $mockSpecificsOci8->expects($this->any())->method('getSpecific')->will($this->returnValue($listMaxExpressions));
 
                $INitems = range(0, 1250);
diff --git a/typo3/sysext/dbal/Tests/Unit/Database/DatabaseSpecificsPostgresqlTest.php b/typo3/sysext/dbal/Tests/Unit/Database/DatabaseSpecificsPostgresqlTest.php
new file mode 100644 (file)
index 0000000..2c1b8b3
--- /dev/null
@@ -0,0 +1,108 @@
+<?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;
+
+/**
+ * Test case
+ */
+class DatabaseSpecificsPostgresqlTest extends DatabaseSpecificsTest {
+       /**
+        * Set up
+        */
+       protected function setUp() {
+               $GLOBALS['TYPO3_LOADED_EXT'] = array();
+
+               /** @var \TYPO3\CMS\Dbal\Database\Specifics\AbstractSpecifics|\PHPUnit_Framework_MockObject_MockObject|\TYPO3\CMS\Core\Tests\AccessibleObjectInterface $subject */
+               $this->subject = GeneralUtility::makeInstance(\TYPO3\CMS\Dbal\Database\Specifics\PostgresSpecifics::class);
+       }
+
+       /**
+        * @return array
+        */
+       public function determineMetaTypeProvider() {
+               return array(
+                       array('INT', 'I4'),
+                       array('INTEGER', 'I4'),
+                       array('TINYINT', 'I2'),
+                       array('SMALLINT', 'I2'),
+                       array('MEDIUMINT', 'I4'),
+                       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', 'B'),
+                       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', 'INT'),
+                       array('I1', 'SMALLINT'),
+                       array('I2', 'SMALLINT'),
+                       array('I4', 'INT'),
+                       array('I8', 'BIGINT'),
+                       array('R', 'INT'),
+                       array('F', 'DOUBLE'),
+                       array('N', 'NUMERIC'),
+                       array('U', 'U')
+               );
+       }
+
+       /**
+        * @return array
+        */
+       public function determineNativeFieldLengthProvider() {
+               return array(
+                       array('SMALLINT', '2', '(6)'),
+                       array('INT', '4', '(11)'),
+                       array('BIGINT', '8', '(20)'),
+                       array('VARCHAR', -1, ''),
+                       array('VARCHAR', 30, '(30)'),
+                       array('DOUBLE', 8, '')
+               );
+       }
+}