[BUGFIX] DBAL: Fix retrieving the last insert id
[Packages/TYPO3.CMS.git] / typo3 / sysext / dbal / Classes / Database / DatabaseConnection.php
index b14aa32..4fb6db5 100644 (file)
@@ -18,10 +18,6 @@ use TYPO3\CMS\Core\Utility\GeneralUtility;
 
 /**
  * TYPO3 database abstraction layer
- *
- * @author Kasper Skårhøj <kasper@typo3.com>
- * @author Karsten Dambekalns <k.dambekalns@fishfarm.de>
- * @author Xavier Perseguers <xavier@typo3.org>
  */
 class DatabaseConnection extends \TYPO3\CMS\Core\Database\DatabaseConnection {
 
@@ -149,7 +145,7 @@ class DatabaseConnection extends \TYPO3\CMS\Core\Database\DatabaseConnection {
        /**
         * SQL parser
         *
-        * @var \TYPO3\CMS\Core\Database\SqlParser
+        * @var \TYPO3\CMS\Dbal\Database\SqlParser
         */
        public $SQLparser;
 
@@ -213,7 +209,7 @@ class DatabaseConnection extends \TYPO3\CMS\Core\Database\DatabaseConnection {
         */
        public function __construct() {
                // Set SQL parser object for internal use:
-               $this->SQLparser = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Database\SqlParser::class, $this);
+               $this->SQLparser = GeneralUtility::makeInstance(\TYPO3\CMS\Dbal\Database\SqlParser::class, $this);
                $this->installerSql = GeneralUtility::makeInstance(\TYPO3\CMS\Install\Service\SqlSchemaMigrationService::class);
                $this->queryCache = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Cache\CacheManager::class)->getCache('dbal');
                // Set internal variables with configuration:
@@ -233,21 +229,24 @@ class DatabaseConnection extends \TYPO3\CMS\Core\Database\DatabaseConnection {
                if (isset($this->conf['table2handlerKeys'])) {
                        $this->table2handlerKeys = $this->conf['table2handlerKeys'];
                }
+
+               $specificsClassName = Specifics\NullSpecifics::class;
                if (isset($this->conf['handlerCfg'])) {
                        $this->handlerCfg = $this->conf['handlerCfg'];
 
                        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);
                                        }
-                                       $this->dbmsSpecifics = GeneralUtility::makeInstance($className);
+                                       $specificsClassName = $className;
                                }
                        }
                }
+               $this->dbmsSpecifics = GeneralUtility::makeInstance($specificsClassName);
                $this->cacheFieldInfo();
                // Debugging settings:
                $this->printErrors = !empty($this->conf['debugOptions']['printErrors']);
@@ -309,7 +308,8 @@ class DatabaseConnection extends \TYPO3\CMS\Core\Database\DatabaseConnection {
         * @return void
         */
        protected function analyzeCachingTables() {
-               $this->parseAndAnalyzeSql(\TYPO3\CMS\Core\Cache\Cache::getDatabaseTableDefinitions());
+               $schemaService = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Cache\DatabaseSchemaService::class);
+               $this->parseAndAnalyzeSql($schemaService->getCachingFrameworkRequiredDatabaseSchema());
        }
 
        /**
@@ -383,7 +383,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];
@@ -509,7 +509,9 @@ class DatabaseConnection extends \TYPO3\CMS\Core\Database\DatabaseConnection {
                                                if ($table != 'tx_dbal_debuglog') {
                                                        $this->handlerInstance[$this->lastHandlerKey]->last_insert_id = $new_id;
                                                }
-                                       } else {
+                                       } elseif (!$this->handlerInstance[$this->lastHandlerKey]->hasInsertID) {
+                                               // The table does not support auto-incremented fields, fall back to
+                                               // using a sequence table to simulate the auto-increment
                                                $new_id = $this->handlerInstance[$this->lastHandlerKey]->GenID($table . '_' . $this->cache_autoIncFields[$table], $this->handlerInstance[$this->lastHandlerKey]->sequenceStart);
                                                $fields_values[$this->cache_autoIncFields[$table]] = $new_id;
                                                if ($table != 'tx_dbal_debuglog') {
@@ -520,10 +522,24 @@ class DatabaseConnection extends \TYPO3\CMS\Core\Database\DatabaseConnection {
                                $this->lastQuery = $this->INSERTquery($table, $fields_values, $no_quote_fields);
                                if (is_string($this->lastQuery)) {
                                        $sqlResult = $this->handlerInstance[$this->lastHandlerKey]->_query($this->lastQuery, FALSE);
+                                       if ($this->handlerInstance[$this->lastHandlerKey]->hasInsertID && !empty($this->cache_autoIncFields[$table])) {
+                                               // The table is able to retrieve the ID of the last insert, use it to update the blob below
+                                               $new_id = $this->handlerInstance[$this->lastHandlerKey]->Insert_ID($table, $this->cache_autoIncFields[$table]);
+                                               if ($table !== 'tx_dbal_debuglog') {
+                                                       $this->handlerInstance[$this->lastHandlerKey]->last_insert_id = $new_id;
+                                               }
+                                       }
                                } else {
                                        $this->handlerInstance[$this->lastHandlerKey]->StartTrans();
-                                       if ($this->lastQuery[0] !== '') {
+                                       if ((string)$this->lastQuery[0] !== '') {
                                                $sqlResult = $this->handlerInstance[$this->lastHandlerKey]->_query($this->lastQuery[0], FALSE);
+                                               if ($this->handlerInstance[$this->lastHandlerKey]->hasInsertID && !empty($this->cache_autoIncFields[$table])) {
+                                                       // The table is able to retrieve the ID of the last insert, use it to update the blob below
+                                                       $new_id = $this->handlerInstance[$this->lastHandlerKey]->Insert_ID($table, $this->cache_autoIncFields[$table]);
+                                                       if ($table !== 'tx_dbal_debuglog') {
+                                                               $this->handlerInstance[$this->lastHandlerKey]->last_insert_id = $new_id;
+                                                       }
+                                               }
                                        }
                                        if (is_array($this->lastQuery[1])) {
                                                foreach ($this->lastQuery[1] as $field => $content) {
@@ -675,7 +691,7 @@ class DatabaseConnection extends \TYPO3\CMS\Core\Database\DatabaseConnection {
                                        $sqlResult = $this->handlerInstance[$this->lastHandlerKey]->_query($this->lastQuery, FALSE);
                                } else {
                                        $this->handlerInstance[$this->lastHandlerKey]->StartTrans();
-                                       if ($this->lastQuery[0] !== '') {
+                                       if ((string)$this->lastQuery[0] !== '') {
                                                $sqlResult = $this->handlerInstance[$this->lastHandlerKey]->_query($this->lastQuery[0], FALSE);
                                        }
                                        if (is_array($this->lastQuery[1])) {
@@ -792,7 +808,7 @@ class DatabaseConnection extends \TYPO3\CMS\Core\Database\DatabaseConnection {
                        $remappedParameters = $this->map_remapSELECTQueryParts($select_fields, $from, $where_clause, $groupBy, $orderBy);
                }
                // Get handler key and select API:
-               if (count($remappedParameters) > 0) {
+               if (!empty($remappedParameters)) {
                        $mappedQueryParts = $this->compileSelectParameters($remappedParameters);
                        $fromTable = $mappedQueryParts[1];
                } else {
@@ -803,7 +819,7 @@ class DatabaseConnection extends \TYPO3\CMS\Core\Database\DatabaseConnection {
                $sqlResult = NULL;
                switch ($hType) {
                        case 'native':
-                               if (count($remappedParameters) > 0) {
+                               if (!empty($remappedParameters)) {
                                        list($select_fields, $from_table, $where_clause, $groupBy, $orderBy) = $this->compileSelectParameters($remappedParameters);
                                }
                                $this->lastQuery = $this->SELECTquery($select_fields, $from_table, $where_clause, $groupBy, $orderBy, $limit);
@@ -822,14 +838,14 @@ class DatabaseConnection extends \TYPO3\CMS\Core\Database\DatabaseConnection {
                                                $numrows = $splitLimit[0];
                                                $offset = 0;
                                        }
-                                       if (count($remappedParameters) > 0) {
+                                       if (!empty($remappedParameters)) {
                                                $sqlResult = $this->handlerInstance[$this->lastHandlerKey]->SelectLimit($this->SELECTqueryFromArray($remappedParameters), $numrows, $offset);
                                        } else {
                                                $sqlResult = $this->handlerInstance[$this->lastHandlerKey]->SelectLimit($this->SELECTquery($select_fields, $from_table, $where_clause, $groupBy, $orderBy), $numrows, $offset);
                                        }
                                        $this->lastQuery = $sqlResult->sql;
                                } else {
-                                       if (count($remappedParameters) > 0) {
+                                       if (!empty($remappedParameters)) {
                                                $this->lastQuery = $this->SELECTqueryFromArray($remappedParameters);
                                        } else {
                                                $this->lastQuery = $this->SELECTquery($select_fields, $from_table, $where_clause, $groupBy, $orderBy);
@@ -844,7 +860,7 @@ class DatabaseConnection extends \TYPO3\CMS\Core\Database\DatabaseConnection {
                                $sqlResult->TYPO3_DBAL_tableList = $ORIG_tableName;
                                break;
                        case 'userdefined':
-                               if (count($remappedParameters) > 0) {
+                               if (!empty($remappedParameters)) {
                                        list($select_fields, $from_table, $where_clause, $groupBy, $orderBy) = $this->compileSelectParameters($remappedParameters);
                                }
                                $sqlResult = $this->handlerInstance[$this->lastHandlerKey]->exec_SELECTquery($select_fields, $from_table, $where_clause, $groupBy, $orderBy, $limit);
@@ -927,7 +943,7 @@ class DatabaseConnection extends \TYPO3\CMS\Core\Database\DatabaseConnection {
         * Executes a query.
         * EXPERIMENTAL since TYPO3 4.4.
         *
-        * @param array $queryParts SQL parsed by method parseSQL() of \TYPO3\CMS\Core\Database\SqlParser
+        * @param array $queryParts SQL parsed by method parseSQL() of \TYPO3\CMS\Dbal\Database\SqlParser
         * @return \mysqli_result|object MySQLi result object / DBAL object
         * @see self::sql_query()
         */
@@ -1005,7 +1021,7 @@ class DatabaseConnection extends \TYPO3\CMS\Core\Database\DatabaseConnection {
         */
        public function INSERTquery($table, $fields_values, $no_quote_fields = FALSE) {
                // Table and fieldnames should be "SQL-injection-safe" when supplied to this function (contrary to values in the arrays which may be insecure).
-               if (!is_array($fields_values) || count($fields_values) === 0) {
+               if (!is_array($fields_values) || empty($fields_values)) {
                        return '';
                }
                foreach ($this->preProcessHookObjects as $hookObject) {
@@ -1039,9 +1055,9 @@ class DatabaseConnection extends \TYPO3\CMS\Core\Database\DatabaseConnection {
                                $nArr[$this->quoteFieldNames($k)] = !in_array($k, $no_quote_fields) ? $this->fullQuoteStr($v, $table, TRUE) : $v;
                        }
                }
-               if (count($blobFields) || count($clobFields)) {
+               if (!empty($blobFields) || !empty($clobFields)) {
                        $query = array();
-                       if (count($nArr)) {
+                       if (!empty($nArr)) {
                                $query[0] = 'INSERT INTO ' . $this->quoteFromTables($table) . '
                                (
                                        ' . implode(',
@@ -1051,10 +1067,10 @@ class DatabaseConnection extends \TYPO3\CMS\Core\Database\DatabaseConnection {
                                        ', $nArr) . '
                                )';
                        }
-                       if (count($blobFields)) {
+                       if (!empty($blobFields)) {
                                $query[1] = $blobFields;
                        }
-                       if (count($clobFields)) {
+                       if (!empty($clobFields)) {
                                $query[2] = $clobFields;
                        }
                        if (isset($query[0]) && ($this->debugOutput || $this->store_lastBuiltQuery)) {
@@ -1124,7 +1140,7 @@ class DatabaseConnection extends \TYPO3\CMS\Core\Database\DatabaseConnection {
                        }
                        $blobFields = $clobFields = array();
                        $nArr = array();
-                       if (is_array($fields_values) && count($fields_values)) {
+                       if (is_array($fields_values) && !empty($fields_values)) {
                                if (is_string($no_quote_fields)) {
                                        $no_quote_fields = explode(',', $no_quote_fields);
                                } elseif (!is_array($no_quote_fields)) {
@@ -1152,9 +1168,9 @@ class DatabaseConnection extends \TYPO3\CMS\Core\Database\DatabaseConnection {
                                        }
                                }
                        }
-                       if (count($blobFields) || count($clobFields)) {
+                       if (!empty($blobFields) || !empty($clobFields)) {
                                $query = array();
-                               if (count($nArr)) {
+                               if (!empty($nArr)) {
                                        $query[0] = 'UPDATE ' . $this->quoteFromTables($table) . '
                                                SET
                                                        ' . implode(',
@@ -1162,10 +1178,10 @@ class DatabaseConnection extends \TYPO3\CMS\Core\Database\DatabaseConnection {
                                                WHERE
                                                        ' . $this->quoteWhereClause($where) : '');
                                }
-                               if (count($blobFields)) {
+                               if (!empty($blobFields)) {
                                        $query[1] = $blobFields;
                                }
-                               if (count($clobFields)) {
+                               if (!empty($clobFields)) {
                                        $query[2] = $clobFields;
                                }
                                if (isset($query[0]) && ($this->debugOutput || $this->store_lastBuiltQuery)) {
@@ -1243,6 +1259,7 @@ class DatabaseConnection extends \TYPO3\CMS\Core\Database\DatabaseConnection {
                $where_clause = $this->quoteWhereClause($where_clause);
                $groupBy = $this->quoteGroupBy($groupBy);
                $orderBy = $this->quoteOrderBy($orderBy);
+               $this->dbmsSpecifics->transformQueryParts($select_fields, $from_table, $where_clause, $groupBy, $orderBy, $limit);
                // Call parent method to build actual query
                $query = parent::SELECTquery($select_fields, $from_table, $where_clause, $groupBy, $orderBy, $limit);
                if ($this->debugOutput || $this->store_lastBuiltQuery) {
@@ -1263,19 +1280,20 @@ class DatabaseConnection extends \TYPO3\CMS\Core\Database\DatabaseConnection {
                // $from_table
                $params[1] = $this->_quoteFromTables($params[1]);
                // $where_clause
-               if (count($params[2]) > 0) {
+               if (!empty($params[2])) {
                        $params[2] = $this->_quoteWhereClause($params[2]);
                }
                // $group_by
-               if (count($params[3]) > 0) {
+               if (!empty($params[3])) {
                        $params[3] = $this->_quoteGroupBy($params[3]);
                }
                // $order_by
-               if (count($params[4]) > 0) {
+               if (!empty($params[4])) {
                        $params[4] = $this->_quoteOrderBy($params[4]);
                }
                // Compile the SELECT parameters
                list($select_fields, $from_table, $where_clause, $groupBy, $orderBy) = $this->compileSelectParameters($params);
+               $this->dbmsSpecifics->transformQueryParts($select_fields, $from_table, $where_clause, $groupBy, $orderBy);
                // Call parent method to build actual query
                $query = parent::SELECTquery($select_fields, $from_table, $where_clause, $groupBy, $orderBy);
                if ($this->debugOutput || $this->store_lastBuiltQuery) {
@@ -1294,9 +1312,9 @@ class DatabaseConnection extends \TYPO3\CMS\Core\Database\DatabaseConnection {
        protected function compileSelectParameters(array $params) {
                $select_fields = $this->SQLparser->compileFieldList($params[0]);
                $from_table = $this->SQLparser->compileFromTables($params[1]);
-               $where_clause = count($params[2]) > 0 ? $this->SQLparser->compileWhereClause($params[2]) : '';
-               $groupBy = count($params[3]) > 0 ? $this->SQLparser->compileFieldList($params[3]) : '';
-               $orderBy = count($params[4]) > 0 ? $this->SQLparser->compileFieldList($params[4]) : '';
+               $where_clause = !empty($params[2]) ? $this->SQLparser->compileWhereClause($params[2]) : '';
+               $groupBy = !empty($params[3]) ? $this->SQLparser->compileFieldList($params[3]) : '';
+               $orderBy = !empty($params[4]) ? $this->SQLparser->compileFieldList($params[4]) : '';
                return array($select_fields, $from_table, $where_clause, $groupBy, $orderBy);
        }
 
@@ -1360,7 +1378,7 @@ class DatabaseConnection extends \TYPO3\CMS\Core\Database\DatabaseConnection {
                        }
                }
                $ORIG_tableName = '';
-               if (count($precompiledParts) == 0) {
+               if (empty($precompiledParts)) {
                        // Map table / field names if needed:
                        $ORIG_tableName = $from_table;
                        // Saving table names in $ORIG_from_table since $from_table is transformed beneath:
@@ -1500,6 +1518,7 @@ class DatabaseConnection extends \TYPO3\CMS\Core\Database\DatabaseConnection {
                                $precompiledParts['queryParts'] = explode($parameterWrap, $query);
                                break;
                        case 'adodb':
+                               $this->dbmsSpecifics->transformQueryParts($select_fields, $from_table, $where_clause, $groupBy, $orderBy, $limit);
                                $query = parent::SELECTquery($select_fields, $from_table, $where_clause, $groupBy, $orderBy);
                                $precompiledParts['queryParts'] = explode($parameterWrap, $query);
                                $precompiledParts['LIMIT'] = $limit;
@@ -1741,7 +1760,17 @@ class DatabaseConnection extends \TYPO3\CMS\Core\Database\DatabaseConnection {
                                                // but it's not overridden from \TYPO3\CMS\Core\Database\DatabaseConnection at the moment...
                                                $patternForLike = $this->escapeStrForLike($pattern, $where_clause[$k]['func']['table']);
                                                $where_clause[$k]['func']['str_like'] = $patternForLike;
-                                               // Intentional fallthrough
+                                               if ($where_clause[$k]['func']['table'] !== '') {
+                                                       $where_clause[$k]['func']['table'] = $this->quoteName($v['func']['table']);
+                                               }
+                                               if ($where_clause[$k]['func']['field'] !== '') {
+                                                       if ($this->dbmsSpecifics->getSpecific(Specifics\AbstractSpecifics::CAST_FIND_IN_SET)) {
+                                                               $where_clause[$k]['func']['field'] = 'CAST(' . $this->quoteName($v['func']['field']) . ' AS CHAR)';
+                                                       } else {
+                                                               $where_clause[$k]['func']['field'] = $this->quoteName($v['func']['field']);
+                                                       }
+                                               }
+                                               break;
                                        case 'IFNULL':
                                                // Intentional fallthrough
                                        case 'LOCATE':
@@ -1777,9 +1806,7 @@ class DatabaseConnection extends \TYPO3\CMS\Core\Database\DatabaseConnection {
                                        }
                                } else {
                                        // Detecting value type; list or plain:
-                                       if (GeneralUtility::inList('NOTIN,IN', strtoupper(str_replace(array(' ', '
-', '
-', '   '), '', $where_clause[$k]['comparator'])))) {
+                                       if (GeneralUtility::inList('NOTIN,IN', strtoupper(str_replace(array(' ', LF, CR, TAB), '', $where_clause[$k]['comparator'])))) {
                                                if (isset($v['subquery'])) {
                                                        $where_clause[$k]['subquery'] = $this->quoteSELECTsubquery($v['subquery']);
                                                }
@@ -1789,6 +1816,8 @@ class DatabaseConnection extends \TYPO3\CMS\Core\Database\DatabaseConnection {
                                                        && is_string($where_clause[$k]['value'][0]) && strstr($where_clause[$k]['value'][0], '.')
                                                ) {
                                                        $where_clause[$k]['value'][0] = $this->quoteFieldNames($where_clause[$k]['value'][0]);
+                                               } elseif ($this->runningADOdbDriver('mssql')) {
+                                                       $where_clause[$k]['value'][0] = substr($this->handlerInstance[$this->lastHandlerKey]->qstr($where_clause[$k]['value'][0]), 1, -1);
                                                }
                                        }
                                }
@@ -1964,8 +1993,24 @@ class DatabaseConnection extends \TYPO3\CMS\Core\Database\DatabaseConnection {
         * @param int $maxLength
         * @throws \RuntimeException
         * @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, use getMetadata() instead
         */
        public function MetaType($type, $table, $maxLength = -1) {
+               GeneralUtility::logDeprecatedFunction();
+               return $this->getMetadata($type, $table, 'dummyFieldToBypassCache', $maxLength);
+       }
+
+       /**
+        * Return Metadata for native field type (ADOdb only!)
+        *
+        * @param string $type  Native type as reported by admin_get_fields()
+        * @param string $table Table name for which the type is queried. Important for detection of DBMS handler of the query!
+        * @param string $field Field name for which the type is queried. Important for accessing the field information cache.
+        * @param int    $maxLength
+        * @throws \RuntimeException
+        * @return string Meta type (currently ADOdb syntax only, http://phplens.com/lens/adodb/docs-adodb.htm#metatype)
+        */
+       public function getMetadata($type, $table, $field, $maxLength = -1) {
                $this->lastHandlerKey = $this->handler_getFromTableList($table);
                $str = '';
                switch ((string)$this->handlerCfg[$this->lastHandlerKey]['type']) {
@@ -1973,7 +2018,9 @@ class DatabaseConnection extends \TYPO3\CMS\Core\Database\DatabaseConnection {
                                $str = $type;
                                break;
                        case 'adodb':
-                               if (in_array($table, $this->cache_fieldType)) {
+                               if (!empty($this->cache_fieldType[$table][$field])) {
+                                       $str = $this->cache_fieldType[$table][$field]['metaType'];
+                               } else {
                                        $rs = $this->handlerInstance[$this->lastHandlerKey]->SelectLimit('SELECT * FROM ' . $this->quoteFromTables($table), 1);
                                        $str = $rs->MetaType($type, $maxLength);
                                }
@@ -1992,66 +2039,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);
        }
 
        /**
@@ -2059,44 +2051,78 @@ 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);
+       }
+
+       /*********************************************
+        *
+        * SqlSchemaMigrationService helper functions
+        *
+        *********************************************/
+       /**
+        * Remove the index prefix length information from columns in an index definition.
+        * Partial indexes based on a prefix are not supported by all databases.
+        *
+        * @param string $indexSQL
+        * @return string
+        */
+       public function getEquivalentIndexDefinition($indexSQL) {
+               if ($this->dbmsSpecifics->specificExists(Specifics\AbstractSpecifics::PARTIAL_STRING_INDEX) && (bool)$this->dbmsSpecifics->getSpecific(Specifics\AbstractSpecifics::PARTIAL_STRING_INDEX)) {
+                       return $indexSQL;
                }
+
+               $strippedIndexSQL = preg_replace_callback(
+                       '/\A([^(]+)\((.*)\)\Z/',
+                       function($matches) {
+                               return $matches[1] . '(' . preg_replace('/\((\d+)\)/', '', $matches[2]) . ')';
+                       },
+                       $indexSQL
+               );
+
+               return $strippedIndexSQL === NULL ? $indexSQL : $strippedIndexSQL;
+       }
+
+       /**
+        * Convert the native MySQL Field type to the closest matching equivalent field type supported by the DBMS.
+        * INTEGER and TINYTEXT colums need to be further processed due to MySQL limitations / non-standard features.
+        *
+        * @param string $fieldSQL
+        * @return string
+        */
+       public function getEquivalentFieldDefinition($fieldSQL) {
+               if (!preg_match('/^([a-z0-9]+)(\(([^\)]+)\))?(.*)/', $fieldSQL, $components)) {
+                       return $fieldSQL;
+               }
+
+               $metaType = $this->dbmsSpecifics->getMetaFieldType($components[1]);
+               $replacementType = $this->dbmsSpecifics->getNativeFieldType($metaType);
+               $replacementLength = $components[2];
+               $replacementExtra = '';
+
+               // MySQL INT types support a display length that has no effect on the
+               // actual range of values that can be stored, normalize to the default
+               // display length returned by DBAL.
+               if (substr($metaType, 0, 1) === 'I') {
+                       $replacementLength = $this->dbmsSpecifics->getNativeFieldLength($replacementType, $components[3]);
+               }
+
+               // MySQL TINYTEXT is equivalent to VARCHAR(255) DEFAULT NULL. MySQL TEXT
+               // columns can not have a default value in contrast to VARCHAR, so the
+               // `default NULL` gets appended to avoid false-positive schema changes.
+               if ($components[1] === 'tinytext') {
+                       $replacementLength = '(255)';
+                       if (FALSE !== stripos($components[0], ' NOT NULL')) {
+                               $replacementExtra = ' default \'\'';
+                       } else {
+                               $replacementExtra = ' default NULL';
+                       }
+               }
+
+               return str_replace($components[1] . $components[2], strtolower($replacementType) . $replacementLength, $components[0]) . $replacementExtra;
        }
 
        /**************************************
@@ -2193,7 +2219,7 @@ class DatabaseConnection extends \TYPO3\CMS\Core\Database\DatabaseConnection {
                                break;
                        case 'adodb':
                                // Check if method exists for the current $res object.
-                               // If a table exists in TCA but not in the db, a error
+                               // If a table exists in TCA but not in the db, an error
                                // occurred because $res is not a valid object.
                                if (method_exists($res, 'FetchRow')) {
                                        $output = $res->FetchRow();
@@ -2261,7 +2287,7 @@ class DatabaseConnection extends \TYPO3\CMS\Core\Database\DatabaseConnection {
                                break;
                        case 'adodb':
                                // Check if method exists for the current $res object.
-                               // If a table exists in TCA but not in the db, a error
+                               // If a table exists in TCA but not in the db, an error
                                // occurred because $res is not a valid object.
                                if (method_exists($res, 'FetchRow')) {
                                        $output = $res->FetchRow();
@@ -2555,7 +2581,7 @@ class DatabaseConnection extends \TYPO3\CMS\Core\Database\DatabaseConnection {
                if (!$ret) {
                        GeneralUtility::sysLog(
                                'Could not select MySQL database ' . $databaseName . ': ' . $this->sql_error(),
-                               'Core',
+                               'core',
                                GeneralUtility::SYSLOG_SEVERITY_FATAL
                        );
                }
@@ -2643,7 +2669,7 @@ class DatabaseConnection extends \TYPO3\CMS\Core\Database\DatabaseConnection {
                                                        // Skip tables from the Oracle 10 Recycle Bin
                                                        continue;
                                                }
-                                               $whichTables[$theTable] = $theTable;
+                                               $whichTables[$theTable] = array('Name' => $theTable);
                                        }
                                }
                                break;
@@ -2652,7 +2678,7 @@ class DatabaseConnection extends \TYPO3\CMS\Core\Database\DatabaseConnection {
                                break;
                }
                // Check mapping:
-               if (is_array($this->mapping) && count($this->mapping)) {
+               if (is_array($this->mapping) && !empty($this->mapping)) {
                        // Mapping table names in reverse, first getting list of real table names:
                        $tMap = array();
                        foreach ($this->mapping as $tN => $tMapInfo) {
@@ -2665,6 +2691,7 @@ class DatabaseConnection extends \TYPO3\CMS\Core\Database\DatabaseConnection {
                        foreach ($whichTables as $tN => $tDefinition) {
                                if (isset($tMap[$tN])) {
                                        $tN = $tMap[$tN];
+                                       $tDefinition = array('Name' => $tN);
                                }
                                $newList[$tN] = $tDefinition;
                        }
@@ -2673,7 +2700,7 @@ class DatabaseConnection extends \TYPO3\CMS\Core\Database\DatabaseConnection {
                // Adding tables configured to reside in other DBMS (handler by other handlers than the default):
                if (is_array($this->table2handlerKeys)) {
                        foreach ($this->table2handlerKeys as $key => $handlerKey) {
-                               $whichTables[$key] = $key;
+                               $whichTables[$key] = array('Name' => $key);
                        }
                }
                return $whichTables;
@@ -2716,15 +2743,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->getMetadata($fieldRow['type'], $tableName, $fieldRow['name']);
+                                               $output[$fieldRow['name']] = $this->dbmsSpecifics->transformFieldRowToMySQL($fieldRow, $metaType);
                                        }
                                }
                                break;
@@ -2782,7 +2802,7 @@ class DatabaseConnection extends \TYPO3\CMS\Core\Database\DatabaseConnection {
                                        foreach ($keyRows as $k => $theKey) {
                                                $theKey['Table'] = $tableName;
                                                $theKey['Non_unique'] = (int)(!$theKey['unique']);
-                                               $theKey['Key_name'] = str_replace($tableName . '_', '', $k);
+                                               $theKey['Key_name'] = str_replace(hash('crc32b', $tableName) . '_', '', $k);
                                                // the following are probably not needed anyway...
                                                $theKey['Collation'] = '';
                                                $theKey['Cardinality'] = '';
@@ -2888,9 +2908,9 @@ class DatabaseConnection extends \TYPO3\CMS\Core\Database\DatabaseConnection {
                // Process query based on type:
                switch ($parsedQuery['type']) {
                        case 'CREATETABLE':
-
                        case 'ALTERTABLE':
-
+                               $this->createMappingsIfRequired($parsedQuery);
+                               // Fall-through next instruction
                        case 'DROPTABLE':
                                $this->clearCachedFieldInfo();
                                $this->map_genericQueryParsed($parsedQuery);
@@ -2965,7 +2985,7 @@ class DatabaseConnection extends \TYPO3\CMS\Core\Database\DatabaseConnection {
                        $_tableList = $tableList;
                        $tableArray = $this->SQLparser->parseFromTables($_tableList);
                        // If success, traverse the tables:
-                       if (is_array($tableArray) && count($tableArray)) {
+                       if (is_array($tableArray) && !empty($tableArray)) {
                                $outputHandlerKey = '';
                                foreach ($tableArray as $vArray) {
                                        // Find handler key, select "_DEFAULT" if none is specifically configured:
@@ -3037,7 +3057,7 @@ class DatabaseConnection extends \TYPO3\CMS\Core\Database\DatabaseConnection {
                                        if ($link->set_charset($this->connectionCharset) === FALSE) {
                                                GeneralUtility::sysLog(
                                                        'Error setting connection charset to "' . $this->connectionCharset . '"',
-                                                       'Core',
+                                                       'core',
                                                        GeneralUtility::SYSLOG_SEVERITY_ERROR
                                                );
                                        }
@@ -3051,7 +3071,7 @@ class DatabaseConnection extends \TYPO3\CMS\Core\Database\DatabaseConnection {
                                                        if ($this->query($command) === FALSE) {
                                                                GeneralUtility::sysLog(
                                                                        'Could not initialize DB connection with query "' . $command . '": ' . $this->sql_error(),
-                                                                       'Core',
+                                                                       'core',
                                                                        GeneralUtility::SYSLOG_SEVERITY_ERROR
                                                                );
                                                        }
@@ -3062,7 +3082,7 @@ class DatabaseConnection extends \TYPO3\CMS\Core\Database\DatabaseConnection {
 
                                        $output = TRUE;
                                } else {
-                                       GeneralUtility::sysLog('Could not connect to MySQL server ' . $cfgArray['config']['host'] . ' with user ' . $cfgArray['config']['username'] . '.', 'Core', 4);
+                                       GeneralUtility::sysLog('Could not connect to MySQL server ' . $cfgArray['config']['host'] . ' with user ' . $cfgArray['config']['username'] . '.', 'core', GeneralUtility::SYSLOG_SEVERITY_FATAL);
                                }
                                break;
                        case 'adodb':
@@ -3092,7 +3112,7 @@ class DatabaseConnection extends \TYPO3\CMS\Core\Database\DatabaseConnection {
                                }
                                if (!$this->handlerInstance[$handlerKey]->isConnected()) {
                                        $dsn = $cfgArray['config']['driver'] . '://' . $cfgArray['config']['username'] . ((string)$cfgArray['config']['password'] !== '' ? ':XXXX@' : '') . $cfgArray['config']['host'] . (isset($cfgArray['config']['port']) ? ':' . $cfgArray['config']['port'] : '') . '/' . $cfgArray['config']['database'] . ($GLOBALS['TYPO3_CONF_VARS']['SYS']['no_pconnect'] ? '' : '?persistent=1');
-                                       GeneralUtility::sysLog('Could not connect to DB server using ADOdb on ' . $cfgArray['config']['host'] . ' with user ' . $cfgArray['config']['username'] . '.', 'Core', 4);
+                                       GeneralUtility::sysLog('Could not connect to DB server using ADOdb on ' . $cfgArray['config']['host'] . ' with user ' . $cfgArray['config']['username'] . '.', 'core', GeneralUtility::SYSLOG_SEVERITY_FATAL);
                                        error_log('DBAL error: Connection to ' . $dsn . ' failed. Maybe PHP doesn\'t support the database?');
                                        $output = FALSE;
                                } else {
@@ -3106,12 +3126,16 @@ class DatabaseConnection extends \TYPO3\CMS\Core\Database\DatabaseConnection {
                                }
                                break;
                        case 'userdefined':
-                               // Find class file:
-                               $fileName = GeneralUtility::getFileAbsFileName($cfgArray['config']['classFile']);
-                               if (@is_file($fileName)) {
-                                       require_once $fileName;
-                               } else {
-                                       throw new \RuntimeException('DBAL error: "' . $fileName . '" was not a file to include.', 1310027975);
+                               // if not set class may also be loaded by autoload on demand
+                               if (isset($cfgArray['config']['classFile'])) {
+                                       GeneralUtility::deprecationLog('The DBAL handler option "config.classFile" is deprecated since TYPO3 CMS 7, and will be removed with CMS 8. Make use of autoloading instead.');
+                                       // Find class file:
+                                       $fileName = GeneralUtility::getFileAbsFileName($cfgArray['config']['classFile']);
+                                       if (@is_file($fileName)) {
+                                               require_once $fileName;
+                                       } else {
+                                               throw new \RuntimeException('DBAL error: "' . $fileName . '" was not a file to include.', 1310027975);
+                                       }
                                }
                                // Initialize:
                                $this->handlerInstance[$handlerKey] = GeneralUtility::makeInstance($cfgArray['config']['class']);
@@ -3400,7 +3424,7 @@ class DatabaseConnection extends \TYPO3\CMS\Core\Database\DatabaseConnection {
        }
 
        /**
-        * Generic mapping of table/field names arrays (as parsed by \TYPO3\CMS\Core\Database\SqlParser)
+        * Generic mapping of table/field names arrays (as parsed by \TYPO3\CMS\Dbal\Database\SqlParser)
         *
         * @param array $sqlPartArray Array with parsed SQL parts; Takes both fields, tables, where-parts, group and order-by. Passed by reference.
         * @param string $defaultTable Default table name to assume if no table is found in $sqlPartArray
@@ -3423,9 +3447,10 @@ class DatabaseConnection extends \TYPO3\CMS\Core\Database\DatabaseConnection {
                                                case 'CASE':
                                                        if (isset($sqlPartArray[$k]['case_field'])) {
                                                                $fieldArray = explode('.', $sqlPartArray[$k]['case_field']);
-                                                               if (count($fieldArray) == 1 && is_array($this->mapping[$defaultTableKey]['mapFieldNames']) && isset($this->mapping[$defaultTableKey]['mapFieldNames'][$fieldArray[0]])) {
+                                                               $fieldArrayCount = count($fieldArray);
+                                                               if ($fieldArrayCount === 1 && is_array($this->mapping[$defaultTableKey]['mapFieldNames']) && isset($this->mapping[$defaultTableKey]['mapFieldNames'][$fieldArray[0]])) {
                                                                        $sqlPartArray[$k]['case_field'] = $this->mapping[$defaultTableKey]['mapFieldNames'][$fieldArray[0]];
-                                                               } elseif (count($fieldArray) == 2) {
+                                                               } elseif ($fieldArrayCount === 2) {
                                                                        // Map the external table
                                                                        $table = $fieldArray[0];
                                                                        $tableKey = $this->getMappingKey($table);
@@ -3481,10 +3506,11 @@ class DatabaseConnection extends \TYPO3\CMS\Core\Database\DatabaseConnection {
                                        // Mapping field name in SQL-functions like MIN(), MAX() or SUM()
                                        if ($this->mapping[$t]['mapFieldNames']) {
                                                $fieldArray = explode('.', $sqlPartArray[$k]['func_content']);
-                                               if (count($fieldArray) == 1 && is_array($this->mapping[$t]['mapFieldNames']) && isset($this->mapping[$t]['mapFieldNames'][$fieldArray[0]])) {
+                                               $fieldArrayCount = count($fieldArray);
+                                               if ($fieldArrayCount === 1 && is_array($this->mapping[$t]['mapFieldNames']) && isset($this->mapping[$t]['mapFieldNames'][$fieldArray[0]])) {
                                                        $sqlPartArray[$k]['func_content.'][0]['func_content'] = $this->mapping[$t]['mapFieldNames'][$fieldArray[0]];
                                                        $sqlPartArray[$k]['func_content'] = $this->mapping[$t]['mapFieldNames'][$fieldArray[0]];
-                                               } elseif (count($fieldArray) == 2) {
+                                               } elseif ($fieldArrayCount === 2) {
                                                        // Map the external table
                                                        $table = $fieldArray[0];
                                                        $tableKey = $this->getMappingKey($table);
@@ -3529,9 +3555,10 @@ class DatabaseConnection extends \TYPO3\CMS\Core\Database\DatabaseConnection {
                                        // this is a very simplistic check, beware
                                        if (!is_numeric($sqlPartArray[$k]['value'][0]) && !isset($sqlPartArray[$k]['value'][1])) {
                                                $fieldArray = explode('.', $sqlPartArray[$k]['value'][0]);
-                                               if (count($fieldArray) == 1 && is_array($this->mapping[$t]['mapFieldNames']) && isset($this->mapping[$t]['mapFieldNames'][$fieldArray[0]])) {
+                                               $fieldArrayCount = count($fieldArray);
+                                               if ($fieldArrayCount === 1 && is_array($this->mapping[$t]['mapFieldNames']) && isset($this->mapping[$t]['mapFieldNames'][$fieldArray[0]])) {
                                                        $sqlPartArray[$k]['value'][0] = $this->mapping[$t]['mapFieldNames'][$fieldArray[0]];
-                                               } elseif (count($fieldArray) == 2) {
+                                               } elseif ($fieldArrayCount === 2) {
                                                        // Map the external table
                                                        $table = $fieldArray[0];
                                                        $tableKey = $this->getMappingKey($table);
@@ -3592,10 +3619,10 @@ class DatabaseConnection extends \TYPO3\CMS\Core\Database\DatabaseConnection {
        }
 
        /**
-        * Will do table/field mapping on a general \TYPO3\CMS\Core\Database\SqlParser-compliant SQL query
+        * Will do table/field mapping on a general \TYPO3\CMS\Dbal\Database\SqlParser-compliant SQL query
         * (May still not support all query types...)
         *
-        * @param array $parsedQuery Parsed QUERY as from \TYPO3\CMS\Core\Database\SqlParser::parseSQL(). NOTICE: Passed by reference!
+        * @param array $parsedQuery Parsed QUERY as from \TYPO3\CMS\Dbal\Database\SqlParser::parseSQL(). NOTICE: Passed by reference!
         * @throws \InvalidArgumentException
         * @return void
         * @see \TYPO3\CMS\Core\Database\SqlParser::parseSQL()
@@ -3669,6 +3696,51 @@ class DatabaseConnection extends \TYPO3\CMS\Core\Database\DatabaseConnection {
                }
        }
 
+       /**
+        * Create a mapping for each table and field if required.
+        *
+        * @param array $parsedQuery The parsed query
+        * @return void
+        */
+       protected function createMappingsIfRequired($parsedQuery) {
+               if (
+                       !$this->dbmsSpecifics->specificExists(Specifics\AbstractSpecifics::TABLE_MAXLENGTH)
+                       && !$this->dbmsSpecifics->specificExists(Specifics\AbstractSpecifics::FIELD_MAXLENGTH)
+               ) {
+                       return;
+               }
+
+               $mappingConfiguration = array();
+               $table = $parsedQuery['TABLE'];
+               if (!isset($this->mapping[$table])) {
+                       $truncatedTable = $this->dbmsSpecifics->truncateIdentifier($table, Specifics\AbstractSpecifics::TABLE_MAXLENGTH);
+                       if ($table !== $truncatedTable) {
+                               $mappingConfiguration['mapTableName'] = $truncatedTable;
+                       }
+               }
+               foreach ($parsedQuery['FIELDS'] as $field => $_) {
+                       if (!isset($this->mapping[$table]['mapFieldNames'][$field])) {
+                               $truncatedField = $this->dbmsSpecifics->truncateIdentifier($field, Specifics\AbstractSpecifics::FIELD_MAXLENGTH);
+                               if ($field !== $truncatedField) {
+                                       $mappingConfiguration['mapFieldNames'][$field] = $truncatedField;
+                               }
+                       }
+               }
+               if (!empty($mappingConfiguration)) {
+                       /** @var \TYPO3\CMS\Extbase\Object\ObjectManager $objectManager */
+                       $objectManager = GeneralUtility::makeInstance(\TYPO3\CMS\Extbase\Object\ObjectManager::class);
+                       /** @var \TYPO3\CMS\Core\Configuration\ConfigurationManager $configurationManager */
+                       $configurationManager = $objectManager->get(\TYPO3\CMS\Core\Configuration\ConfigurationManager::class);
+                       $configurationManager->setLocalConfigurationValueByPath(
+                               'EXTCONF/dbal/mapping/' . $table,
+                               $mappingConfiguration
+                       );
+
+                       // renew mapping information
+                       $this->mapping = array_merge($this->mapping, array($table => $mappingConfiguration));
+               }
+       }
+
        /**************************************
         *
         * Debugging
@@ -3748,7 +3820,7 @@ class DatabaseConnection extends \TYPO3\CMS\Core\Database\DatabaseConnection {
                                                                unset($parseResults[$k]);
                                                        }
                                                }
-                                               if (count($parseResults)) {
+                                               if (!empty($parseResults)) {
                                                        $data['parseError'] = $parseResults;
                                                        $errorFlag |= 2;
                                                }
@@ -3802,10 +3874,10 @@ class DatabaseConnection extends \TYPO3\CMS\Core\Database\DatabaseConnection {
        public function debug_log($query, $ms, $data, $join, $errorFlag, $script = '') {
                if (is_array($query)) {
                        $queryToLog = $query[0] . ' --  ';
-                       if (count($query[1])) {
+                       if (!empty($query[1])) {
                                $queryToLog .= count($query[1]) . ' BLOB FIELDS: ' . implode(', ', array_keys($query[1]));
                        }
-                       if (count($query[2])) {
+                       if (!empty($query[2])) {
                                $queryToLog .= count($query[2]) . ' CLOB FIELDS: ' . implode(', ', array_keys($query[2]));
                        }
                } else {