Raised DBAL version from 1.1.5 to 1.1.6
authorXavier Perseguers <typo3@perseguers.ch>
Fri, 28 May 2010 06:26:34 +0000 (06:26 +0000)
committerXavier Perseguers <typo3@perseguers.ch>
Fri, 28 May 2010 06:26:34 +0000 (06:26 +0000)
git-svn-id: https://svn.typo3.org/TYPO3v4/Core/trunk@7704 709f56b5-9817-0410-a4d7-c38de5d9e867

ChangeLog
typo3/sysext/dbal/ChangeLog
typo3/sysext/dbal/class.tx_dbal_autoloader.php
typo3/sysext/dbal/class.tx_dbal_installtool.php
typo3/sysext/dbal/class.ux_t3lib_db.php
typo3/sysext/dbal/class.ux_t3lib_sqlparser.php
typo3/sysext/dbal/ext_emconf.php
typo3/sysext/dbal/last_synched_target
typo3/sysext/dbal/tests/db_oracle_testcase.php
typo3/sysext/dbal/tests/fixtures/oci8.config.php
typo3/sysext/dbal/tests/sqlparser_general_testcase.php

index cd1b9e4..5adf950 100755 (executable)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,7 @@
+2010-05-28  Xavier Perseguers  <typo3@perseguers.ch>
+
+       * Raised DBAL version from 1.1.5 to 1.1.6
+
 2010-05-27  Jeff Segars  <jeff@webempoweredchurch.org>
 
        * Follow-up to bug #14486: Sprite icons not shown in Safari and IE6 (thanks to Steffen Ritter)
index a78711b..24a8ea9 100644 (file)
@@ -1,3 +1,18 @@
+2010-05-28  Xavier Perseguers  <typo3@perseguers.ch>
+
+       * Set version to 1.1.6 (TYPO3 4.4 beta3)
+
+2010-05-27  Xavier Perseguers  <typo3@perseguers.ch>
+
+       * Fixed bug #14496: SQL parser does not handle ALTER TABLE with character set operation
+       * Fixed bug #14456: SQL parser doesn't accept ALTER TABLE statement without CHANGE, DROP or ADD keyword
+       * Fixed bug #14457: 1-2-3 Install Process breaks with PostgreSQL
+
+2010-05-21  Xavier Perseguers  <typo3@perseguers.ch>
+
+       * Fixed bug #14372: Mapping does not work with table alias
+       * Follow-up of changeset 32847: DBAL cannot be loaded if typo3conf/ is not writable
+
 2010-05-03  Xavier Perseguers  <typo3@perseguers.ch>
 
        * Set version to 1.1.5 (TYPO3 4.4 beta2)
index e15305c..4af9e2c 100644 (file)
@@ -125,7 +125,14 @@ class tx_dbal_autoloader {
                                // Get lines from localconf file
                        $lines = $instObj->writeToLocalconf_control();
                        $instObj->setValueInLocalconfFile($lines, '$TYPO3_CONF_VARS[\'EXT\'][\'extList\']', $newExtList);
-                       $instObj->writeToLocalconf_control($lines);
+                       $result = $instObj->writeToLocalconf_control($lines);
+                       if ($result === 'nochange') {
+                               $message = 'DBAL was not loaded.';
+                               if (!@is_writable(PATH_typo3conf)) {
+                                       $message .= ' ' . PATH_typo3conf . ' is not writable!';
+                               }
+                               throw new Exception($message);
+                       }
 
                        $GLOBALS['TYPO3_CONF_VARS']['EXT']['extList'] = $newExtList;
                                // Make sure to get cache file for backend, not frontend
index 3ce3bf8..798f444 100644 (file)
@@ -53,11 +53,22 @@ class tx_dbal_installtool {
        protected $availableDrivers;
 
        /**
+        * @var string
+        */
+       protected $driver;
+
+       /**
         * Default constructor.
         */
        public function __construct() {
                $this->supportedDrivers = $this->getSupportedDrivers();
                $this->availableDrivers = $this->getAvailableDrivers();
+
+               $configDriver =& $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['dbal']['handlerCfg']['_DEFAULT']['config']['driver'];
+               $this->driver = t3lib_div::_GET('driver');
+               if (!$this->driver && $configDriver) {
+                       $this->driver = $configDriver;
+               }
        }
 
        /**
@@ -71,7 +82,7 @@ class tx_dbal_installtool {
        public function executeStepOutput(array &$markers, $step, tx_install $instObj) {
                switch ($step) {
                        case 2:
-                               $this->createConnectionForm(t3lib_div::_GET('driver'), $markers, $instObj);
+                               $this->createConnectionForm($markers, $instObj);
                                break;
                        case 3:
                                $this->createDatabaseForm($markers, $instObj);
@@ -92,6 +103,10 @@ class tx_dbal_installtool {
                        case 3:
                        case 4:
                                $driver = $instObj->INSTALL['localconf.php']['typo_db_driver'];
+                               if (!$driver && $this->driver) {
+                                       // Driver was already configured
+                                       break;
+                               }
                                $driverConfig = '';
                                switch ($driver) {
                                        case 'oci8':
@@ -123,15 +138,14 @@ class tx_dbal_installtool {
        /**
         * Creates a specialized form to configure the DBMS connection.
         * 
-        * @param string $driver
         * @param array $markers
         * @param tx_install $instObj
         * @return void
         */
-       protected function createConnectionForm($driver, array &$markers, tx_install $instObj) {
+       protected function createConnectionForm(array &$markers, tx_install $instObj) {
                        // Normalize current driver
-               if (!$driver) {  
-                       $driver = $this->getDefaultDriver();
+               if (!$this->driver) {  
+                       $this->driver = $this->getDefaultDriver();
                }
 
                        // Get the template file
@@ -165,13 +179,13 @@ class tx_dbal_installtool {
                );
 
                        // Get the subpart related to selected database driver
-               if ($driver === '' || $driver === 'mysql') {
+               if ($this->driver === '' || $this->driver === 'mysql') {
                        $driverOptionsSubPart = t3lib_parsehtml::getSubpart(
                                $template, '###DRIVER_MYSQL###'
                        );
                } else {
                        $driverOptionsSubPart = t3lib_parsehtml::getSubpart(
-                               $template, '###DRIVER_' . t3lib_div::strtoupper($driver) . '###'
+                               $template, '###DRIVER_' . t3lib_div::strtoupper($this->driver) . '###'
                        );
                        if ($driverOptionsSubPart === '') {
                                $driverOptionsSubPart = t3lib_parsehtml::getSubpart(
@@ -182,7 +196,7 @@ class tx_dbal_installtool {
 
                        // Define driver-specific markers
                $driverMarkers = array();
-               switch ($driver) {
+               switch ($this->driver) {
                        case 'mssql':
                                $driverMarkers = array(
                                        'labelUsername' => 'Username',
@@ -224,6 +238,19 @@ class tx_dbal_installtool {
                                );
                                $nextStep = $instObj->step + 2;
                                break;
+                       case 'postgres':
+                               $driverMarkers = array(
+                                       'labelUsername' => 'Username',
+                                       'username' => TYPO3_db_username,
+                                       'labelPassword' => 'Password',
+                                       'password' => TYPO3_db_password,
+                                       'labelHost' => 'Host',
+                                       'host' => TYPO3_db_host ? TYPO3_db_host : 'localhost',
+                                       'labelDatabase' => 'Database',
+                                       'database' => TYPO3_db,
+                               );
+                               $nextStep = $instObj->step + 2;
+                               break;
                        default:
                                $driverMarkers = array(
                                        'labelUsername' => 'Username',
@@ -276,7 +303,6 @@ class tx_dbal_installtool {
 
                        // Create the drop-down list of available drivers
                $dropdown = '';
-               $activeDriver = t3lib_div::_GET('driver');
                foreach ($this->availableDrivers as $abstractionLayer => $drivers) {
                        $options = array();
                        foreach ($drivers as $driver => $label) {
@@ -286,7 +312,7 @@ class tx_dbal_installtool {
                                        'onclick'     => 'document.location=\'index.php?TYPO3_INSTALL[type]=config&mode=123&step=2&driver=' . $driver . '\';',
                                        'selected'    => '',
                                );
-                               if ($driver === $activeDriver) {
+                               if ($driver === $this->driver) {
                                        $markers['selected'] .= ' selected="selected"';
                                }
                                $options[] = t3lib_parsehtml::substituteMarkerArray(
index 4318d17..ce74a60 100644 (file)
@@ -1427,7 +1427,12 @@ class ux_t3lib_DB extends t3lib_DB {
                $this->lastHandlerKey = $this->handler_getFromTableList($table);
                switch ((string)$this->handlerCfg[$this->lastHandlerKey]['type']) {
                        case 'native':
-                               $str = mysql_real_escape_string($str, $this->handlerInstance[$this->lastHandlerKey]['link']);
+                               if ($this->handlerInstance[$this->lastHandlerKey]['link']) {
+                                       $str = mysql_real_escape_string($str, $this->handlerInstance[$this->lastHandlerKey]['link']);
+                               } else {
+                                               // link may be null when unit testing DBAL
+                                       $str = str_replace('\'', '\\\'', $str);
+                               }
                                break;
                        case 'adodb':
                                $str = substr($this->handlerInstance[$this->lastHandlerKey]->qstr($str), 1, -1);
@@ -2703,13 +2708,14 @@ class ux_t3lib_DB extends t3lib_DB {
        protected function map_assocArray($input, $tables, $rev = FALSE) {
                        // Traverse tables from query (hopefully only one table):
                foreach ($tables as $tableCfg) {
-                       if (is_array($this->mapping[$tableCfg['table']]['mapFieldNames'])) {
+                       $tableKey = $this->getMappingKey($tableCfg['table']);
+                       if (is_array($this->mapping[$tableKey]['mapFieldNames'])) {
 
                                        // Get the map (reversed if needed):
                                if ($rev) {
-                                       $theMap = array_flip($this->mapping[$tableCfg['table']]['mapFieldNames']);
+                                       $theMap = array_flip($this->mapping[$tableKey]['mapFieldNames']);
                                } else {
-                                       $theMap = $this->mapping[$tableCfg['table']]['mapFieldNames'];
+                                       $theMap = $this->mapping[$tableKey]['mapFieldNames'];
                                }
 
                                        // Traverse selected record, map fieldnames:
@@ -2749,37 +2755,61 @@ class ux_t3lib_DB extends t3lib_DB {
         * @see exec_SELECTquery()
         */
        protected function map_remapSELECTQueryParts(&$select_fields, &$from_table, &$where_clause, &$groupBy, &$orderBy) {
+                       // Backup current mapping as it may be altered if aliases on mapped tables are found
+               $backupMapping = $this->mapping;
+
                        // Tables:
                $tables = $this->SQLparser->parseFromTables($from_table);
                $defaultTable = $tables[0]['table'];
+                       // Prepare mapping for aliased tables. This will copy the definition of the original table name.
+                       // The alias is prefixed with a database-incompatible character to prevent naming clash with real table name
+                       // Further access to $this->mapping should be made through $this->getMappingKey() method
                foreach ($tables as $k => $v) {
-                       if ($this->mapping[$v['table']]['mapTableName']) {
-                               $tables[$k]['table'] = $this->mapping[$v['table']]['mapTableName'];
+                       if ($v['as'] && is_array($this->mapping[$v['table']]['mapFieldNames'])) {
+                               $mappingKey = $this->getFreeMappingKey($v['as']);
+                               $this->mapping[$mappingKey]['mapFieldNames'] =& $this->mapping[$v['table']]['mapFieldNames'];
+                       }
+                       if (is_array($v['JOIN'])) {
+                               foreach ($v['JOIN'] as $joinCnt => $join) {
+                                       if ($join['as'] && is_array($this->mapping[$join['withTable']]['mapFieldNames'])) {
+                                               $mappingKey = $this->getFreeMappingKey($join['as']);
+                                               $this->mapping[$mappingKey]['mapFieldNames'] =& $this->mapping[$join['withTable']]['mapFieldNames'];
+                                       }
+                               }
+                       }
+               }
+               foreach ($tables as $k => $v) {
+                       $tableKey = $this->getMappingKey($v['table']);
+                       if ($this->mapping[$tableKey]['mapTableName']) {
+                               $tables[$k]['table'] = $this->mapping[$tableKey]['mapTableName'];
                        }
                                // Mapping JOINS
                        if (is_array($v['JOIN'])) {
                                foreach($v['JOIN'] as $joinCnt => $join) {
                                                // Mapping withTable of the JOIN
-                                       if ($this->mapping[$join['withTable']]['mapTableName']) {
-                                               $tables[$k]['JOIN'][$joinCnt]['withTable'] = $this->mapping[$join['withTable']]['mapTableName'];                                        
+                                       $withTableKey = $this->getMappingKey($join['withTable']);
+                                       if ($this->mapping[$withTableKey]['mapTableName']) {
+                                               $tables[$k]['JOIN'][$joinCnt]['withTable'] = $this->mapping[$withTableKey]['mapTableName'];                                     
                                        }
                                        $onPartsArray = array();
                                                // Mapping ON parts of the JOIN
                                        if (is_array($tables[$k]['JOIN'][$joinCnt]['ON'])) {
                                                foreach ($tables[$k]['JOIN'][$joinCnt]['ON'] as &$condition) {
                                                                // Left side of the comparator
-                                                       if (isset($this->mapping[$condition['left']['table']]['mapFieldNames'][$condition['left']['field']])) {
-                                                               $condition['left']['field'] = $this->mapping[$condition['left']['table']]['mapFieldNames'][$condition['left']['field']];
+                                                       $leftTableKey = $this->getMappingKey($condition['left']['table']);
+                                                       if (isset($this->mapping[$leftTableKey]['mapFieldNames'][$condition['left']['field']])) {
+                                                               $condition['left']['field'] = $this->mapping[$leftTableKey]['mapFieldNames'][$condition['left']['field']];
                                                        }
-                                                       if (isset($this->mapping[$condition['left']['table']]['mapTableName'])) {
-                                                               $condition['left']['table'] = $this->mapping[$condition['left']['table']]['mapTableName'];
+                                                       if (isset($this->mapping[$leftTableKey]['mapTableName'])) {
+                                                               $condition['left']['table'] = $this->mapping[$leftTableKey]['mapTableName'];
                                                        }
                                                                // Right side of the comparator
-                                                       if (isset($this->mapping[$condition['right']['table']]['mapFieldNames'][$condition['right']['field']])) {
-                                                               $condition['right']['field'] = $this->mapping[$condition['right']['table']]['mapFieldNames'][$condition['right']['field']];
+                                                       $rightTableKey = $this->getMappingKey($condition['right']['table']);
+                                                       if (isset($this->mapping[$rightTableKey]['mapFieldNames'][$condition['right']['field']])) {
+                                                               $condition['right']['field'] = $this->mapping[$rightTableKey]['mapFieldNames'][$condition['right']['field']];
                                                        }
-                                                       if (isset($this->mapping[$condition['right']['table']]['mapTableName'])) {
-                                                               $condition['right']['table'] = $this->mapping[$condition['right']['table']]['mapTableName'];
+                                                       if (isset($this->mapping[$rightTableKey]['mapTableName'])) {
+                                                               $condition['right']['table'] = $this->mapping[$rightTableKey]['mapTableName'];
                                                        }
                                                }
                                        }
@@ -2807,6 +2837,37 @@ class ux_t3lib_DB extends t3lib_DB {
                $expFields = $this->SQLparser->parseFieldList($orderBy);
                $this->map_sqlParts($expFields,$defaultTable);
                $orderBy = $this->SQLparser->compileFieldList($expFields);
+
+                       // Restore the original mapping
+               $this->mapping = $backupMapping;
+       }
+
+       /**
+        * Returns the key to be used when retrieving information from $this->mapping. This ensures
+        * that mapping from aliased tables is properly retrieved.
+        *
+        * @param string $tableName
+        * @return string
+        */
+       protected function getMappingKey($tableName) {
+                       // Search deepest alias mapping
+               while (isset($this->mapping['*' . $tableName])) {
+                       $tableName = '*' . $tableName;
+               }
+               return $tableName;
+       }
+
+       /**
+        * Returns a free key to be used to store mapping information in $this->mapping.
+        *
+        * @param string $tableName
+        * @return string
+        */
+       protected function getFreeMappingKey($tableName) {
+               while (isset($this->mapping[$tableName])) {
+                       $tableName = '*' . $tableName;
+               }
+               return $tableName;
        }
 
        /**
@@ -2819,6 +2880,7 @@ class ux_t3lib_DB extends t3lib_DB {
         * @see map_remapSELECTQueryParts()
         */
        protected function map_sqlParts(&$sqlPartArray, $defaultTable) {
+               $defaultTableKey = $this->getMappingKey($defaultTable);
                        // Traverse sql Part array:
                if (is_array($sqlPartArray)) {
                        foreach ($sqlPartArray as $k => $v) {
@@ -2833,19 +2895,20 @@ class ux_t3lib_DB extends t3lib_DB {
                                                case 'CASE':
                                                        if (isset($sqlPartArray[$k]['case_field'])) {
                                                                $fieldArray = explode('.', $sqlPartArray[$k]['case_field']);
-                                                               if (count($fieldArray) == 1 && is_array($this->mapping[$defaultTable]['mapFieldNames']) && isset($this->mapping[$defaultTable]['mapFieldNames'][$fieldArray[0]])) {
-                                                                       $sqlPartArray[$k]['case_field'] = $this->mapping[$defaultTable]['mapFieldNames'][$fieldArray[0]];
+                                                               if (count($fieldArray) == 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) {
                                                                                // Map the external table
                                                                        $table = $fieldArray[0];
-                                                                       if (isset($this->mapping[$fieldArray[0]]['mapTableName'])) {
-                                                                               $table = $this->mapping[$fieldArray[0]]['mapTableName'];
+                                                                       $tableKey = $this->getMappingKey($table);
+                                                                       if (isset($this->mapping[$tableKey]['mapTableName'])) {
+                                                                               $table = $this->mapping[$tableKey]['mapTableName'];
                                                                        }
                                                                                // Map the field itself
                                                                        $field = $fieldArray[1];
-                                                                       if (is_array($this->mapping[$fieldArray[0]]['mapFieldNames']) && isset($this->mapping[$fieldArray[0]]['mapFieldNames'][$fieldArray[1]])) {
-                                                                               $field = $this->mapping[$fieldArray[0]]['mapFieldNames'][$fieldArray[1]];
+                                                                       if (is_array($this->mapping[$tableKey]['mapFieldNames']) && isset($this->mapping[$tableKey]['mapFieldNames'][$fieldArray[1]])) {
+                                                                               $field = $this->mapping[$tableKey]['mapFieldNames'][$fieldArray[1]];
                                                                        }
                                                                        $sqlPartArray[$k]['case_field'] = $table . '.' . $field;
                                                                }
@@ -2863,15 +2926,13 @@ class ux_t3lib_DB extends t3lib_DB {
                                } elseif (isset($sqlPartArray[$k]['func'])) {
                                        switch ($sqlPartArray[$k]['func']['type']) {
                                                case 'EXISTS':
-                                                       $subqueryDefaultTable = $sqlPartArray[$k]['func']['subquery']['FROM'][0]['table'];
-                                                       $this->map_sqlParts($sqlPartArray[$k]['func']['subquery']['SELECT'], $subqueryDefaultTable);
-                                                       $this->map_sqlParts($sqlPartArray[$k]['func']['subquery']['FROM'], $subqueryDefaultTable);
-                                                       $this->map_sqlParts($sqlPartArray[$k]['func']['subquery']['WHERE'], $subqueryDefaultTable);
+                                                       $this->map_subquery($sqlPartArray[$k]['func']['subquery']);
                                                        break;
                                                case 'IFNULL':
                                                case 'LOCATE':
                                                                // For the field, look for table mapping (generic):
                                                        $t = $sqlPartArray[$k]['func']['table'] ? $sqlPartArray[$k]['func']['table'] : $defaultTable;
+                                                       $t = $this->getMappingKey($t);
                                                        if (is_array($this->mapping[$t]['mapFieldNames']) && $this->mapping[$t]['mapFieldNames'][$sqlPartArray[$k]['func']['field']]) {
                                                                $sqlPartArray[$k]['func']['field'] = $this->mapping[$t]['mapFieldNames'][$sqlPartArray[$k]['func']['field']];
                                                        }
@@ -2883,6 +2944,7 @@ class ux_t3lib_DB extends t3lib_DB {
                                } else {
                                                // For the field, look for table mapping (generic):
                                        $t = $sqlPartArray[$k]['table'] ? $sqlPartArray[$k]['table'] : $defaultTable;
+                                       $t = $this->getMappingKey($t);
 
                                                // Mapping field name, if set:
                                        if (is_array($this->mapping[$t]['mapFieldNames']) && isset($this->mapping[$t]['mapFieldNames'][$sqlPartArray[$k]['field']])) {
@@ -2899,13 +2961,14 @@ class ux_t3lib_DB extends t3lib_DB {
                                                elseif (count($fieldArray) == 2) {
                                                                // Map the external table
                                                        $table = $fieldArray[0];
-                                                       if (isset($this->mapping[$fieldArray[0]]['mapTableName'])) {
-                                                               $table = $this->mapping[$fieldArray[0]]['mapTableName'];
+                                                       $tableKey = $this->getMappingKey($table);
+                                                       if (isset($this->mapping[$tableKey]['mapTableName'])) {
+                                                               $table = $this->mapping[$tableKey]['mapTableName'];
                                                        }
                                                                // Map the field itself
                                                        $field = $fieldArray[1];
-                                                       if (is_array($this->mapping[$fieldArray[0]]['mapFieldNames']) && isset($this->mapping[$fieldArray[0]]['mapFieldNames'][$fieldArray[1]])) {
-                                                               $field = $this->mapping[$fieldArray[0]]['mapFieldNames'][$fieldArray[1]];
+                                                       if (is_array($this->mapping[$tableKey]['mapFieldNames']) && isset($this->mapping[$tableKey]['mapFieldNames'][$fieldArray[1]])) {
+                                                               $field = $this->mapping[$tableKey]['mapFieldNames'][$fieldArray[1]];
                                                        }
                                                        $sqlPartArray[$k]['func_content.'][0]['func_content'] = $table . '.' . $field;
                                                        $sqlPartArray[$k]['func_content'] = $table . '.' . $field;
@@ -2924,21 +2987,19 @@ class ux_t3lib_DB extends t3lib_DB {
                                                // Do we have a function (e.g., CONCAT)
                                        if (isset($v['value']['operator'])) {
                                                foreach ($sqlPartArray[$k]['value']['args'] as $argK => $fieldDef) {
-                                                       if (isset($this->mapping[$fieldDef['table']]['mapTableName'])) {
-                                                               $sqlPartArray[$k]['value']['args'][$argK]['table'] = $this->mapping[$fieldDef['table']]['mapTableName'];
+                                                       $tableKey = $this->getMappingKey($fieldDef['table']);
+                                                       if (isset($this->mapping[$tableKey]['mapTableName'])) {
+                                                               $sqlPartArray[$k]['value']['args'][$argK]['table'] = $this->mapping[$tableKey]['mapTableName'];
                                                        }
-                                                       if (is_array($this->mapping[$fieldDef['table']]['mapFieldNames']) && isset($this->mapping[$fieldDef['table']]['mapFieldNames'][$fieldDef['field']])) {
-                                                               $sqlPartArray[$k]['value']['args'][$argK]['field'] = $this->mapping[$fieldDef['table']]['mapFieldNames'][$fieldDef['field']];   
+                                                       if (is_array($this->mapping[$tableKey]['mapFieldNames']) && isset($this->mapping[$tableKey]['mapFieldNames'][$fieldDef['field']])) {
+                                                               $sqlPartArray[$k]['value']['args'][$argK]['field'] = $this->mapping[$tableKey]['mapFieldNames'][$fieldDef['field']];    
                                                        }
                                                }
                                        }
 
                                                // Do we have a subquery (WHERE parts only)?
                                        if (isset($sqlPartArray[$k]['subquery'])) {
-                                               $subqueryDefaultTable = $sqlPartArray[$k]['subquery']['FROM'][0]['table'];
-                                               $this->map_sqlParts($sqlPartArray[$k]['subquery']['SELECT'], $subqueryDefaultTable);
-                                               $this->map_sqlParts($sqlPartArray[$k]['subquery']['FROM'], $subqueryDefaultTable);
-                                               $this->map_sqlParts($sqlPartArray[$k]['subquery']['WHERE'], $subqueryDefaultTable);
+                                               $this->map_subquery($sqlPartArray[$k]['subquery']);
                                        }
 
                                                // do we have a field name in the value?
@@ -2950,21 +3011,23 @@ class ux_t3lib_DB extends t3lib_DB {
                                                } elseif (count($fieldArray) == 2) {
                                                                // Map the external table
                                                        $table = $fieldArray[0];
-                                                       if (isset($this->mapping[$fieldArray[0]]['mapTableName'])) {
-                                                               $table = $this->mapping[$fieldArray[0]]['mapTableName'];
+                                                       $tableKey = $this->getMappingKey($table);
+                                                       if (isset($this->mapping[$tableKey]['mapTableName'])) {
+                                                               $table = $this->mapping[$tableKey]['mapTableName'];
                                                        }
                                                                // Map the field itself
                                                        $field = $fieldArray[1];
-                                                       if (is_array($this->mapping[$fieldArray[0]]['mapFieldNames']) && isset($this->mapping[$fieldArray[0]]['mapFieldNames'][$fieldArray[1]])) {
-                                                               $field = $this->mapping[$fieldArray[0]]['mapFieldNames'][$fieldArray[1]];
+                                                       if (is_array($this->mapping[$tableKey]['mapFieldNames']) && isset($this->mapping[$tableKey]['mapFieldNames'][$fieldArray[1]])) {
+                                                               $field = $this->mapping[$tableKey]['mapFieldNames'][$fieldArray[1]];
                                                        }
                                                        $sqlPartArray[$k]['value'][0] = $table . '.' . $field;
                                                }
                                        }
 
                                                // Map table?
-                                       if ($sqlPartArray[$k]['table'] && $this->mapping[$sqlPartArray[$k]['table']]['mapTableName']) {
-                                               $sqlPartArray[$k]['table'] = $this->mapping[$sqlPartArray[$k]['table']]['mapTableName'];
+                                       $tableKey = $this->getMappingKey($sqlPartArray[$k]['table']);
+                                       if ($sqlPartArray[$k]['table'] && $this->mapping[$tableKey]['mapTableName']) {
+                                               $sqlPartArray[$k]['table'] = $this->mapping[$tableKey]['mapTableName'];
                                        }
                                }
                        }
@@ -2972,6 +3035,44 @@ class ux_t3lib_DB extends t3lib_DB {
        }
 
        /**
+        * Maps table and field names in a subquery.
+        *
+        * @param array $parsedQuery
+        * @return void
+        */
+       protected function map_subquery(&$parsedQuery) {
+                       // Backup current mapping as it may be altered
+               $backupMapping = $this->mapping;
+
+               foreach ($parsedQuery['FROM'] as $k => $v) {
+                       $mappingKey = $v['table'];
+                       if ($v['as'] && is_array($this->mapping[$v['table']]['mapFieldNames'])) {
+                               $mappingKey = $this->getFreeMappingKey($v['as']);
+                       } else {
+                                       // Should ensure that no alias is defined in the external query
+                                       // which would correspond to a real table name in the subquery
+                               if ($this->getMappingKey($v['table']) !== $v['table']) {
+                                       $mappingKey = $this->getFreeMappingKey($v['table']);
+                                               // This is the only case when 'mapTableName' should be copied
+                                       $this->mapping[$mappingKey]['mapTableName'] =& $this->mapping[$v['table']]['mapTableName'];
+                               }
+                       }
+                       if ($mapping !== $v['table']) {
+                               $this->mapping[$mappingKey]['mapFieldNames'] =& $this->mapping[$v['table']]['mapFieldNames'];
+                       }
+               }
+
+                       // Perform subquery's remapping
+               $defaultTable = $parsedQuery['FROM'][0]['table'];
+               $this->map_sqlParts($parsedQuery['SELECT'], $defaultTable);
+               $this->map_sqlParts($parsedQuery['FROM'], $defaultTable);
+               $this->map_sqlParts($parsedQuery['WHERE'], $defaultTable);
+
+                       // Restore the mapping
+               $this->mapping = $backupMapping;
+       }
+
+       /**
         * Will do table/field mapping on a general tx_dbal_sqlengine-compliant SQL query
         * (May still not support all query types...)
         *
index d10f87c..e4f207c 100644 (file)
@@ -301,6 +301,10 @@ class ux_t3lib_sqlparser extends t3lib_sqlparser {
                                        case 'ADDUNIQUE':
                                                $query .= ' (' . implode(',', $components['fields']) . ')';
                                                break;
+                                       case 'DEFAULTCHARACTERSET':
+                                       case 'ENGINE':
+                                               // ??? todo!
+                                               break;
                                }
                                break;
                }
index da76923..fae8327 100644 (file)
@@ -3,7 +3,7 @@
 ########################################################################
 # Extension Manager/Repository config file for ext "dbal".
 #
-# Auto generated 03-05-2010 07:54
+# Auto generated 28-05-2010 08:20
 #
 # Manual updates:
 # Only the data in the array - everything else is removed by next
@@ -32,8 +32,8 @@ $EM_CONF[$_EXTKEY] = array(
        'author_company' => '',
        'CGLcompliance' => '',
        'CGLcompliance_note' => '',
-       'version' => '1.1.5',
-       '_md5_values_when_last_written' => 'a:42:{s:9:"ChangeLog";s:4:"015f";s:28:"class.tx_dbal_autoloader.php";s:4:"7e31";s:29:"class.tx_dbal_installtool.php";s:4:"cf25";s:26:"class.ux_db_list_extra.php";s:4:"60d9";s:21:"class.ux_t3lib_db.php";s:4:"1839";s:28:"class.ux_t3lib_sqlparser.php";s:4:"2c3f";s:16:"ext_autoload.php";s:4:"821a";s:21:"ext_conf_template.txt";s:4:"f5cf";s:12:"ext_icon.gif";s:4:"c9ba";s:17:"ext_localconf.php";s:4:"afdd";s:14:"ext_tables.php";s:4:"8414";s:14:"ext_tables.sql";s:4:"1f95";s:27:"doc/class.tslib_fe.php.diff";s:4:"0083";s:14:"doc/manual.sxw";s:4:"b022";s:45:"handlers/class.tx_dbal_handler_openoffice.php";s:4:"775f";s:43:"handlers/class.tx_dbal_handler_rawmysql.php";s:4:"2f1b";s:40:"handlers/class.tx_dbal_handler_xmldb.php";s:4:"e363";s:31:"lib/class.tx_dbal_sqlengine.php";s:4:"f1bb";s:33:"lib/class.tx_dbal_tsparserext.php";s:4:"862d";s:14:"mod1/clear.gif";s:4:"cc11";s:13:"mod1/conf.php";s:4:"6e63";s:14:"mod1/index.php";s:4:"4a5e";s:18:"mod1/locallang.xml";s:4:"0b57";s:22:"mod1/locallang_mod.xml";s:4:"86ef";s:19:"mod1/moduleicon.gif";s:4:"2b8f";s:10:"res/README";s:4:"be19";s:26:"res/Templates/install.html";s:4:"6a62";s:30:"res/oracle/indexed_search.diff";s:4:"ec81";s:23:"res/oracle/realurl.diff";s:4:"86da";s:25:"res/oracle/scheduler.diff";s:4:"7c06";s:27:"res/oracle/templavoila.diff";s:4:"1fd5";s:43:"res/postgresql/postgresql-compatibility.sql";s:4:"034c";s:22:"tests/BaseTestCase.php";s:4:"f736";s:26:"tests/FakeDbConnection.php";s:4:"7bab";s:29:"tests/db_general_testcase.php";s:4:"42f4";s:27:"tests/db_mssql_testcase.php";s:4:"5593";s:28:"tests/db_oracle_testcase.php";s:4:"4dfc";s:32:"tests/db_postgresql_testcase.php";s:4:"4851";s:36:"tests/sqlparser_general_testcase.php";s:4:"07a6";s:31:"tests/fixtures/mssql.config.php";s:4:"ff95";s:30:"tests/fixtures/oci8.config.php";s:4:"7179";s:36:"tests/fixtures/postgresql.config.php";s:4:"87a1";}',
+       'version' => '1.1.6',
+       '_md5_values_when_last_written' => 'a:42:{s:9:"ChangeLog";s:4:"68d3";s:28:"class.tx_dbal_autoloader.php";s:4:"4e1f";s:29:"class.tx_dbal_installtool.php";s:4:"9319";s:26:"class.ux_db_list_extra.php";s:4:"60d9";s:21:"class.ux_t3lib_db.php";s:4:"2947";s:28:"class.ux_t3lib_sqlparser.php";s:4:"1984";s:16:"ext_autoload.php";s:4:"821a";s:21:"ext_conf_template.txt";s:4:"f5cf";s:12:"ext_icon.gif";s:4:"c9ba";s:17:"ext_localconf.php";s:4:"afdd";s:14:"ext_tables.php";s:4:"8414";s:14:"ext_tables.sql";s:4:"1f95";s:27:"doc/class.tslib_fe.php.diff";s:4:"0083";s:14:"doc/manual.sxw";s:4:"b022";s:45:"handlers/class.tx_dbal_handler_openoffice.php";s:4:"775f";s:43:"handlers/class.tx_dbal_handler_rawmysql.php";s:4:"2f1b";s:40:"handlers/class.tx_dbal_handler_xmldb.php";s:4:"e363";s:31:"lib/class.tx_dbal_sqlengine.php";s:4:"f1bb";s:33:"lib/class.tx_dbal_tsparserext.php";s:4:"862d";s:14:"mod1/clear.gif";s:4:"cc11";s:13:"mod1/conf.php";s:4:"6e63";s:14:"mod1/index.php";s:4:"4a5e";s:18:"mod1/locallang.xml";s:4:"0b57";s:22:"mod1/locallang_mod.xml";s:4:"86ef";s:19:"mod1/moduleicon.gif";s:4:"2b8f";s:10:"res/README";s:4:"be19";s:26:"res/Templates/install.html";s:4:"6a62";s:30:"res/oracle/indexed_search.diff";s:4:"ec81";s:23:"res/oracle/realurl.diff";s:4:"86da";s:25:"res/oracle/scheduler.diff";s:4:"7c06";s:27:"res/oracle/templavoila.diff";s:4:"1fd5";s:43:"res/postgresql/postgresql-compatibility.sql";s:4:"034c";s:22:"tests/BaseTestCase.php";s:4:"f736";s:26:"tests/FakeDbConnection.php";s:4:"7bab";s:29:"tests/db_general_testcase.php";s:4:"42f4";s:27:"tests/db_mssql_testcase.php";s:4:"5593";s:28:"tests/db_oracle_testcase.php";s:4:"86f9";s:32:"tests/db_postgresql_testcase.php";s:4:"4851";s:36:"tests/sqlparser_general_testcase.php";s:4:"9783";s:31:"tests/fixtures/mssql.config.php";s:4:"ff95";s:30:"tests/fixtures/oci8.config.php";s:4:"819e";s:36:"tests/fixtures/postgresql.config.php";s:4:"87a1";}',
        'constraints' => array(
                'depends' => array(
                        'adodb' => '5.10.0-',
index 569eacf..e47848f 100644 (file)
@@ -1 +1 @@
-https://svn.typo3.org/TYPO3v4/Extensions/dbal/tags/1.1.5/
+https://svn.typo3.org/TYPO3v4/Extensions/dbal/tags/1.1.6/
index 0913a38..087b593 100644 (file)
@@ -445,8 +445,193 @@ class db_oracle_testcase extends BaseTestCase {
                $GLOBALS['TYPO3_DB']->_callRef('map_remapSELECTQueryParts', $selectFields, $fromTables, $whereClause, $groupBy, $orderBy);
                $query = $this->cleanSql($GLOBALS['TYPO3_DB']->SELECTquery($selectFields, $fromTables, $whereClause, $groupBy, $orderBy));
 
-               $expected = 'SELECT "cpg_categories"."uid", "cpg_categories"."name" FROM "cpg_categories", "pages" WHERE "pages"."uid" = "cpg_categories"."page_id"';
-               $expected .= ' AND "pages"."deleted" = 0 AND 1 = 1 ORDER BY "cpg_categories"."pos"';
+               $expected = 'SELECT "cpg_categories"."uid", "cpg_categories"."name" FROM "cpg_categories", "my_pages" WHERE "my_pages"."page_uid" = "cpg_categories"."page_id"';
+               $expected .= ' AND "my_pages"."deleted" = 0 AND 1 = 1 ORDER BY "cpg_categories"."pos"';
+               $this->assertEquals($expected, $query);
+       }
+
+       /**
+        * @test
+        * @see http://bugs.typo3.org/view.php?id=14372
+        */
+       public function fieldFromAliasIsRemapped() {
+               $selectFields = 'news.uid';
+               $fromTables   = 'tt_news AS news';
+               $whereClause  = 'news.uid = 1';
+               $groupBy      = '';
+               $orderBy      = '';
+
+               $GLOBALS['TYPO3_DB']->_callRef('map_remapSELECTQueryParts', $selectFields, $fromTables, $whereClause, $groupBy, $orderBy);
+               $query = $this->cleanSql($GLOBALS['TYPO3_DB']->SELECTquery($selectFields, $fromTables, $whereClause, $groupBy, $orderBy));
+
+               $expected = 'SELECT "news"."news_uid" FROM "ext_tt_news" AS "news" WHERE "news"."news_uid" = 1';
+               $this->assertEquals($expected, $query);
+       }
+
+       /**
+        * Trick here is that we already have a mapping for both table tt_news and table tt_news_cat
+        * (see tests/fixtures/oci8.config.php) which is used as alias name.
+        *
+        * @test
+        * @see http://bugs.typo3.org/view.php?id=14372
+        */
+       public function fieldFromAliasIsRemappedWithoutBeingTricked() {
+               $selectFields = 'tt_news_cat.uid';
+               $fromTables   = 'tt_news AS tt_news_cat';
+               $whereClause  = 'tt_news_cat.uid = 1';
+               $groupBy      = '';
+               $orderBy      = '';
+
+               $GLOBALS['TYPO3_DB']->_callRef('map_remapSELECTQueryParts', $selectFields, $fromTables, $whereClause, $groupBy, $orderBy);
+               $query = $this->cleanSql($GLOBALS['TYPO3_DB']->SELECTquery($selectFields, $fromTables, $whereClause, $groupBy, $orderBy));
+
+               $expected = 'SELECT "tt_news_cat"."news_uid" FROM "ext_tt_news" AS "tt_news_cat" WHERE "tt_news_cat"."news_uid" = 1';
+               $this->assertEquals($expected, $query);
+       }
+
+       /**
+        * @test
+        * @see http://bugs.typo3.org/view.php?id=14372
+        */
+       public function aliasRemappingDoesNotAlterFurtherQueries() {
+               $selectFields = 'foo.uid';
+               $fromTables   = 'tt_news AS foo';
+               $whereClause  = 'foo.uid = 1';
+               $groupBy      = '';
+               $orderBy      = '';
+
+                       // First call to possibly alter (in memory) the mapping from localconf.php 
+               $GLOBALS['TYPO3_DB']->_callRef('map_remapSELECTQueryParts', $selectFields, $fromTables, $whereClause, $groupBy, $orderBy);
+
+               $selectFields = 'uid';
+               $fromTables   = 'foo';
+               $whereClause  = 'uid = 1';
+               $groupBy      = '';
+               $orderBy      = '';
+
+               $GLOBALS['TYPO3_DB']->_callRef('map_remapSELECTQueryParts', $selectFields, $fromTables, $whereClause, $groupBy, $orderBy);
+               $query = $this->cleanSql($GLOBALS['TYPO3_DB']->SELECTquery($selectFields, $fromTables, $whereClause, $groupBy, $orderBy));
+
+               $expected = 'SELECT "uid" FROM "foo" WHERE "uid" = 1';
+               $this->assertEquals($expected, $query);
+       }
+
+       /**
+        * @test
+        * @see http://bugs.typo3.org/view.php?id=14372
+        */
+       public function fieldFromAliasInJoinIsRemapped() {
+               $selectFields = 'cat.uid, cat_mm.uid_local, news.uid';
+               $fromTables   = 'tt_news_cat AS cat' .
+                       ' INNER JOIN tt_news_cat_mm AS cat_mm ON cat.uid = cat_mm.uid_foreign' .
+                       ' INNER JOIN tt_news AS news ON news.uid = cat_mm.uid_local';
+               $whereClause  = '1=1';
+               $groupBy      = '';
+               $orderBy      = '';
+
+               $GLOBALS['TYPO3_DB']->_callRef('map_remapSELECTQueryParts', $selectFields, $fromTables, $whereClause, $groupBy, $orderBy);
+               $query = $this->cleanSql($GLOBALS['TYPO3_DB']->SELECTquery($selectFields, $fromTables, $whereClause, $groupBy, $orderBy));
+
+               $expected = 'SELECT "cat"."cat_uid", "cat_mm"."local_uid", "news"."news_uid"';
+               $expected .= ' FROM "ext_tt_news_cat" AS "cat"';
+               $expected .= ' INNER JOIN "ext_tt_news_cat_mm" AS "cat_mm" ON "cat"."cat_uid"="cat_mm"."uid_foreign"';
+               $expected .= ' INNER JOIN "ext_tt_news" AS "news" ON "news"."news_uid"="cat_mm"."local_uid"';
+               $expected .= ' WHERE 1 = 1';
+               $this->assertEquals($expected, $query);
+       }
+
+       /**
+        * @test
+        * @see http://bugs.typo3.org/view.php?id=14372
+        */
+       public function aliasRemappingWithInSubqueryDoesNotAffectMainQuery() {
+               $selectFields = 'foo.uid';
+               $fromTables   = 'tt_news AS foo INNER JOIN tt_news_cat_mm ON tt_news_cat_mm.uid_local = foo.uid';
+               $whereClause  = 'tt_news_cat_mm.uid_foreign IN (SELECT foo.uid FROM tt_news_cat AS foo WHERE foo.hidden = 0)';
+               $groupBy      = '';
+               $orderBy      = 'foo.uid';
+
+               $GLOBALS['TYPO3_DB']->_callRef('map_remapSELECTQueryParts', $selectFields, $fromTables, $whereClause, $groupBy, $orderBy);
+               $query = $this->cleanSql($GLOBALS['TYPO3_DB']->SELECTquery($selectFields, $fromTables, $whereClause, $groupBy, $orderBy));
+
+               $expected = 'SELECT "foo"."news_uid" FROM "ext_tt_news" AS "foo"';
+               $expected .= ' INNER JOIN "ext_tt_news_cat_mm" ON "ext_tt_news_cat_mm"."local_uid"="foo"."news_uid"';
+               $expected .= ' WHERE "ext_tt_news_cat_mm"."uid_foreign" IN (';
+               $expected .=    'SELECT "foo"."cat_uid" FROM "ext_tt_news_cat" AS "foo" WHERE "foo"."hidden" = 0';
+               $expected .= ')';
+               $expected .= ' ORDER BY "foo"."news_uid"';
+               $this->assertEquals($expected, $query);
+       }
+
+       /**
+        * @test
+        * @see http://bugs.typo3.org/view.php?id=14372
+        */
+       public function aliasRemappingWithExistsSubqueryDoesNotAffectMainQuery() {
+               $selectFields = 'foo.uid';
+               $fromTables   = 'tt_news AS foo INNER JOIN tt_news_cat_mm ON tt_news_cat_mm.uid_local = foo.uid';
+               $whereClause  = 'EXISTS (SELECT foo.uid FROM tt_news_cat AS foo WHERE foo.hidden = 0)';
+               $groupBy      = '';
+               $orderBy      = 'foo.uid';
+
+               $GLOBALS['TYPO3_DB']->_callRef('map_remapSELECTQueryParts', $selectFields, $fromTables, $whereClause, $groupBy, $orderBy);
+               $query = $this->cleanSql($GLOBALS['TYPO3_DB']->SELECTquery($selectFields, $fromTables, $whereClause, $groupBy, $orderBy));
+
+               $expected = 'SELECT "foo"."news_uid" FROM "ext_tt_news" AS "foo"';
+               $expected .= ' INNER JOIN "ext_tt_news_cat_mm" ON "ext_tt_news_cat_mm"."local_uid"="foo"."news_uid"';
+               $expected .= ' WHERE EXISTS (';
+               $expected .=    'SELECT "foo"."cat_uid" FROM "ext_tt_news_cat" AS "foo" WHERE "foo"."hidden" = 0';
+               $expected .= ')';
+               $expected .= ' ORDER BY "foo"."news_uid"';
+               $this->assertEquals($expected, $query);
+       }
+
+       /**
+        * @test
+        * @see http://bugs.typo3.org/view.php?id=14372
+        */
+       public function aliasRemappingSupportsNestedSubqueries() {
+               $selectFields = 'foo.uid';
+               $fromTables   = 'tt_news AS foo';
+               $whereClause  = 'uid IN (' .
+                       'SELECT foobar.uid_local FROM tt_news_cat_mm AS foobar WHERE uid_foreign IN (' .
+                               'SELECT uid FROM tt_news_cat WHERE deleted = 0' .
+                       '))';
+               $groupBy      = '';
+               $orderBy      = '';
+
+               $GLOBALS['TYPO3_DB']->_callRef('map_remapSELECTQueryParts', $selectFields, $fromTables, $whereClause, $groupBy, $orderBy);
+               $query = $this->cleanSql($GLOBALS['TYPO3_DB']->SELECTquery($selectFields, $fromTables, $whereClause, $groupBy, $orderBy));
+
+               $expected = 'SELECT "foo"."news_uid" FROM "ext_tt_news" AS "foo"';
+               $expected .= ' WHERE "news_uid" IN (';
+               $expected .=    'SELECT "foobar"."local_uid" FROM "ext_tt_news_cat_mm" AS "foobar" WHERE "uid_foreign" IN (';
+               $expected .=            'SELECT "cat_uid" FROM "ext_tt_news_cat" WHERE "deleted" = 0';
+               $expected .=    ')';
+               $expected .= ')';
+               $this->assertEquals($expected, $query);
+       }
+
+       /**
+        * @test
+        * @see http://bugs.typo3.org/view.php?id=14372
+        */
+       public function remappingDoesNotMixUpAliasesInSubquery() {
+               $selectFields = 'pages.uid';
+               $fromTables   = 'tt_news AS pages INNER JOIN tt_news_cat_mm AS cat_mm ON cat_mm.uid_local = pages.uid';
+               $whereClause  = 'pages.pid IN (SELECT uid FROM pages WHERE deleted = 0 AND cat_mm.uid_local != 100)';
+               $groupBy      = '';
+               $orderBy      = 'pages.uid';
+
+               $GLOBALS['TYPO3_DB']->_callRef('map_remapSELECTQueryParts', $selectFields, $fromTables, $whereClause, $groupBy, $orderBy);
+               $query = $this->cleanSql($GLOBALS['TYPO3_DB']->SELECTquery($selectFields, $fromTables, $whereClause, $groupBy, $orderBy));
+
+               $expected = 'SELECT "pages"."news_uid" FROM "ext_tt_news" AS "pages"';
+               $expected .= ' INNER JOIN "ext_tt_news_cat_mm" AS "cat_mm" ON "cat_mm"."local_uid"="pages"."news_uid"';
+               $expected .= ' WHERE "pages"."pid" IN (';
+               $expected .=    'SELECT "page_uid" FROM "my_pages" WHERE "deleted" = 0 AND "cat_mm"."local_uid" != 100';
+               $expected .= ')';
+               $expected .= ' ORDER BY "pages"."news_uid"';
                $this->assertEquals($expected, $query);
        }
 
index ab271d3..e4ad6cd 100644 (file)
@@ -35,6 +35,12 @@ $TYPO3_CONF_VARS['EXTCONF']['dbal']['mapping'] = array(
                        'pid' => 'page_id',
                ),
        ),
+       'pages' => array(
+               'mapTableName' => 'my_pages',
+               'mapFieldNames' => array(
+                       'uid' => 'page_uid',
+               ),
+       ),
        'tt_news' => array(
                'mapTableName' => 'ext_tt_news',
                'mapFieldNames' => array(
index 0201c64..f9e00c4 100644 (file)
@@ -337,6 +337,36 @@ class sqlparser_general_testcase extends BaseTestCase {
                $this->assertEquals($expected, $select);
        }
 
+       /**
+        * @test
+        * @see http://bugs.typo3.org/view.php?id=14456
+        */
+       public function canParseAlterEngineStatement() {
+               $parseString = 'ALTER TABLE tx_realurl_pathcache ENGINE=InnoDB';
+               $components = $this->fixture->_callRef('parseALTERTABLE', $parseString);
+
+               $this->assertTrue(is_array($components), $components);
+               $alterTable = $this->cleanSql($this->fixture->_callRef('compileALTERTABLE', $components));
+               $expected = 'ALTER TABLE tx_realurl_pathcache ENGINE = InnoDB';
+               $this->assertTrue(is_array($alterTable), $alterTable);
+               $this->assertEquals($expected, $alterTable[0]);
+       }
+
+       /**
+        * @test
+        * @see http://bugs.typo3.org/view.php?id=14496
+        */
+       public function canParseAlterCharacterSetStatement() {
+               $parseString = 'ALTER TABLE `index_phash` DEFAULT CHARACTER SET utf8';
+               $components = $this->fixture->_callRef('parseALTERTABLE', $parseString);
+
+               $this->assertTrue(is_array($components), $components);
+               $alterTable = $this->cleanSql($this->fixture->_callRef('compileALTERTABLE', $components));
+               $expected = 'ALTER TABLE index_phash DEFAULT CHARACTER SET utf8';
+               $this->assertTrue(is_array($alterTable), $alterTable);
+               $this->assertEquals($expected, $alterTable[0]);
+       }
+
        ///////////////////////////////////////
        // Tests concerning JOINs
        ///////////////////////////////////////